/* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcwl.h" #include #include #ifdef DEBUG #define PCWL_DBG_BASIC 0x1 #define PCWL_DBG_INFO 0x2 #define PCWL_DBG_SEND 0x4 #define PCWL_DBG_RCV 0x8 #define PCWL_DBG_LINKINFO 0x10 uint32_t pcwl_debug = 0; #define PCWLDBG(x) \ if (pcwl_debug & PCWL_DBG_BASIC) cmn_err x #else #define PCWLDBG(x) #endif /* for pci card */ static ddi_device_acc_attr_t accattr = { DDI_DEVICE_ATTR_V0, DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC, DDI_DEFAULT_ACC }; void *pcwl_soft_state_p = NULL; static int pcwl_device_type; static int pcwl_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf); static int pcwl_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf); static void pcwl_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wlpd_pr_num, mac_prop_info_handle_t mph); static void pcwl_delay(pcwl_maci_t *, clock_t); mac_callbacks_t pcwl_m_callbacks = { MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO, pcwl_gstat, pcwl_start, pcwl_stop, pcwl_prom, pcwl_sdmulti, pcwl_saddr, pcwl_tx, NULL, pcwl_ioctl, NULL, NULL, NULL, pcwl_m_setprop, pcwl_m_getprop, pcwl_m_propinfo }; static char *pcwl_name_str = "pcwl"; #ifdef __sparc #define pcwl_quiesce ddi_quiesce_not_supported #else static int pcwl_quiesce(dev_info_t *); #endif DDI_DEFINE_STREAM_OPS(pcwl_dev_ops, nulldev, pcwl_probe, pcwl_attach, pcwl_detach, nodev, NULL, D_MP, NULL, pcwl_quiesce); extern struct mod_ops mod_driverops; static struct modldrv modldrv = { &mod_driverops, "Lucent/PRISM-II 802.11b driver", &pcwl_dev_ops }; static struct modlinkage modlinkage = { MODREV_1, (void *)&modldrv, NULL }; int _init(void) { int stat; /* Allocate soft state */ if ((stat = ddi_soft_state_init(&pcwl_soft_state_p, sizeof (pcwl_maci_t), N_PCWL)) != DDI_SUCCESS) return (stat); mac_init_ops(&pcwl_dev_ops, "pcwl"); wl_frame_default.wl_dat[0] = htons(WL_SNAP_WORD0); wl_frame_default.wl_dat[1] = htons(WL_SNAP_WORD1); stat = mod_install(&modlinkage); if (stat != DDI_SUCCESS) { mac_fini_ops(&pcwl_dev_ops); ddi_soft_state_fini(&pcwl_soft_state_p); } return (stat); } int _fini(void) { int stat; if ((stat = mod_remove(&modlinkage)) != DDI_SUCCESS) return (stat); mac_fini_ops(&pcwl_dev_ops); ddi_soft_state_fini(&pcwl_soft_state_p); return (stat); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } static int pcwl_probe(dev_info_t *dip) { int len, ret; char *buf; dev_info_t *pdip = ddi_get_parent(dip); ret = ddi_getlongprop(DDI_DEV_T_ANY, pdip, 0, "device_type", (caddr_t)&buf, &len); if (ret != DDI_SUCCESS) return (DDI_PROBE_FAILURE); PCWLDBG((CE_NOTE, "pcwl probe: device_type %s\n", buf)); if ((strcmp(buf, "pccard") == 0) || (strcmp(buf, "pcmcia") == 0)) { pcwl_device_type = PCWL_DEVICE_PCCARD; ret = DDI_PROBE_SUCCESS; } else if (strcmp(buf, "pci") == 0) { pcwl_device_type = PCWL_DEVICE_PCI; ret = DDI_PROBE_SUCCESS; } else { ret = DDI_PROBE_FAILURE; } kmem_free(buf, len); return (ret); } static int pcwl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int ret, i; int instance; uint16_t stat; uint32_t err; pcwl_maci_t *pcwl_p; wifi_data_t wd = { 0 }; mac_register_t *macp; modify_config_t cfgmod; char strbuf[256]; PCWLDBG((CE_NOTE, "pcwl attach: dip=0x%p cmd=%x\n", (void *)dip, cmd)); if (cmd != DDI_ATTACH) goto attach_fail1; /* * Allocate soft state associated with this instance. */ if (ddi_soft_state_zalloc(pcwl_soft_state_p, ddi_get_instance(dip)) != DDI_SUCCESS) { cmn_err(CE_CONT, "pcwl attach: alloc softstate failed\n"); goto attach_fail1; } pcwl_p = (pcwl_maci_t *)ddi_get_soft_state(pcwl_soft_state_p, ddi_get_instance(dip)); pcwl_p->pcwl_device_type = pcwl_device_type; if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) { if (ddi_regs_map_setup(dip, 0, (caddr_t *)&pcwl_p->pcwl_cfg_base, 0, 0, &accattr, &pcwl_p->pcwl_cfg_handle) != DDI_SUCCESS) { cmn_err(CE_WARN, "pcwl(pci) attach: pci_regs_map_setup" " failed\n"); goto attach_fail2; } stat = ddi_get16(pcwl_p->pcwl_cfg_handle, (uint16_t *)(pcwl_p->pcwl_cfg_base + PCI_CONF_COMM)); stat |= (PCI_COMM_IO | PCI_COMM_MAE); ddi_put16(pcwl_p->pcwl_cfg_handle, (uint16_t *)(pcwl_p->pcwl_cfg_base + PCI_CONF_COMM), stat); stat = ddi_get16(pcwl_p->pcwl_cfg_handle, (uint16_t *)(pcwl_p->pcwl_cfg_base + PCI_CONF_COMM)); if ((stat & (PCI_COMM_IO | PCI_COMM_MAE)) != (PCI_COMM_IO | PCI_COMM_MAE)) { cmn_err(CE_WARN, "pcwl(pci) attach: pci command" " reg enable failed\n"); goto attach_fail2a; } if (ddi_regs_map_setup(dip, 1, (caddr_t *)&pcwl_p->pcwl_bar, 0, 0, &accattr, (ddi_acc_handle_t *)&pcwl_p->pcwl_handle) != DDI_SUCCESS) { cmn_err(CE_WARN, "pcwl(pci) attach: pci_regs_map_setup" " failed\n"); goto attach_fail2a; } PCWLDBG((CE_NOTE, "pcwl(pci): regs_map_setup,bar=%p\n", (void *)pcwl_p->pcwl_bar)); /* * tricky! copy from freebsd code. */ PCWL_WRITE(pcwl_p, 0x26, 0x80); drv_usecwait(500000); PCWL_WRITE(pcwl_p, 0x26, 0x0); drv_usecwait(500000); for (i = 0; i < WL_TIMEOUT; i++) { PCWL_READ(pcwl_p, 0x0, stat); if (stat & WL_CMD_BUSY) drv_usecwait(10); else break; } if (i == WL_TIMEOUT) { cmn_err(CE_WARN, "pcwl(pci) attach: hardware init" " failed\n"); goto attach_fail3; } /* * magic number verification. * tricky! copy from freebsd code. */ PCWL_WRITE(pcwl_p, 0x28, 0x4a2d); PCWL_READ(pcwl_p, 0x28, stat); PCWLDBG((CE_NOTE, "pcwl(pci):magic number = %x\n", stat)); if (stat != 0x4a2d) { cmn_err(CE_WARN, "pcwl(pci) attach: magic verify" " failed\n"); goto attach_fail3; } } pcwl_p->pcwl_dip = dip; pcwl_p->pcwl_flag = 0; pcwl_p->pcwl_socket = ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS, "socket", -1); pcwl_p->pcwl_reschedule_need = B_FALSE; if (ddi_get_iblock_cookie(dip, 0, &pcwl_p->pcwl_ib_cookie) != DDI_SUCCESS) { cmn_err(CE_WARN, "pcwl attach: get_iblk_cookie failed\n"); goto attach_fail3; } mutex_init(&pcwl_p->pcwl_glock, NULL, MUTEX_DRIVER, pcwl_p->pcwl_ib_cookie); mutex_init(&pcwl_p->pcwl_scanlist_lock, NULL, MUTEX_DRIVER, pcwl_p->pcwl_ib_cookie); mutex_init(&pcwl_p->pcwl_txring.wl_tx_lock, NULL, MUTEX_DRIVER, pcwl_p->pcwl_ib_cookie); if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) { if (ret = ddi_add_intr(dip, 0, NULL, NULL, pcwl_intr, (caddr_t)pcwl_p)) { cmn_err(CE_NOTE, "pcwl(pci) attach: add intr failed\n"); goto attach_fail3a; } } else if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) { if (ret = pcwl_register_cs(dip, pcwl_p)) { cmn_err(CE_WARN, "pcwl attach(pccard): " "register_cs err %x\n", ret); goto attach_fail3a; } } else { cmn_err(CE_WARN, "pcwl attach: unsupported device type\n"); goto attach_fail3a; } mutex_enter(&pcwl_p->pcwl_glock); if (ret = pcwl_reset_backend(pcwl_p)) { cmn_err(CE_WARN, "pcwl attach: reset_backend failed %x\n", ret); mutex_exit(&pcwl_p->pcwl_glock); goto attach_fail4; } if (ret = pcwl_get_cap(pcwl_p)) { /* sets macaddr for mac_register */ cmn_err(CE_WARN, "pcwl attach: get_cap failed %x\n", ret); mutex_exit(&pcwl_p->pcwl_glock); goto attach_fail4; } mutex_exit(&pcwl_p->pcwl_glock); /* * Provide initial settings for the WiFi plugin; whenever this * information changes, we need to call mac_pdata_update() */ wd.wd_secalloc = WIFI_SEC_NONE; wd.wd_opmode = IEEE80211_M_STA; macp = mac_alloc(MAC_VERSION); if (macp == NULL) { PCWLDBG((CE_NOTE, "pcwl attach: " "MAC version mismatch\n")); goto attach_fail4; } macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; macp->m_driver = pcwl_p; macp->m_dip = dip; macp->m_src_addr = pcwl_p->pcwl_mac_addr; macp->m_callbacks = &pcwl_m_callbacks; macp->m_min_sdu = 0; macp->m_max_sdu = IEEE80211_MTU; macp->m_pdata = &wd; macp->m_pdata_size = sizeof (wd); err = mac_register(macp, &pcwl_p->pcwl_mh); mac_free(macp); if (err != 0) { PCWLDBG((CE_NOTE, "pcwl attach: " "mac_register err\n")); goto attach_fail4; } mutex_enter(&pcwl_p->pcwl_glock); if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) { /* * turn on CS interrupt */ cfgmod.Attributes = CONF_ENABLE_IRQ_STEERING | CONF_IRQ_CHANGE_VALID; cfgmod.Vpp1 = 0; cfgmod.Vpp2 = 0; (void) csx_ModifyConfiguration(pcwl_p->pcwl_chdl, &cfgmod); } if (ret = pcwl_init_nicmem(pcwl_p)) { cmn_err(CE_WARN, "pcwl(pccard) attach: pcwl_init_nicmem" " failed %x\n", ret); mutex_exit(&pcwl_p->pcwl_glock); goto attach_fail5; } pcwl_chip_type(pcwl_p); if (ret = pcwl_loaddef_rf(pcwl_p)) { cmn_err(CE_WARN, "pcwl attach: config_rf failed%x\n", ret); mutex_exit(&pcwl_p->pcwl_glock); goto attach_fail5; } (void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0); pcwl_stop_locked(pcwl_p); /* leaves interface down */ list_create(&pcwl_p->pcwl_scan_list, sizeof (wl_scan_list_t), offsetof(wl_scan_list_t, wl_scan_node)); pcwl_p->pcwl_scan_num = 0; mutex_exit(&pcwl_p->pcwl_glock); pcwl_p->pcwl_scanlist_timeout_id = timeout(pcwl_scanlist_timeout, pcwl_p, drv_usectohz(1000000)); instance = ddi_get_instance(dip); (void) snprintf(strbuf, sizeof (strbuf), "pcwl%d", instance); if (ddi_create_minor_node(dip, strbuf, S_IFCHR, instance + 1, DDI_NT_NET_WIFI, 0) != DDI_SUCCESS) { goto attach_fail6; } pcwl_p->pcwl_flag |= PCWL_ATTACHED; if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) { pcwl_p->pcwl_flag |= PCWL_CARD_READY; } return (DDI_SUCCESS); attach_fail6: if (pcwl_p->pcwl_scanlist_timeout_id != 0) { (void) untimeout(pcwl_p->pcwl_scanlist_timeout_id); pcwl_p->pcwl_scanlist_timeout_id = 0; } list_destroy(&pcwl_p->pcwl_scan_list); attach_fail5: (void) mac_unregister(pcwl_p->pcwl_mh); attach_fail4: if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) { ddi_remove_intr(dip, 0, pcwl_p->pcwl_ib_cookie); } else if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) { pcwl_unregister_cs(pcwl_p); } attach_fail3a: pcwl_destroy_locks(pcwl_p); attach_fail3: if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) ddi_regs_map_free(&pcwl_p->pcwl_handle); attach_fail2a: if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) ddi_regs_map_free(&pcwl_p->pcwl_cfg_handle); attach_fail2: ddi_soft_state_free(pcwl_soft_state_p, ddi_get_instance(dip)); attach_fail1: return (DDI_FAILURE); } static int pcwl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { pcwl_maci_t *pcwl_p; wl_scan_list_t *scan_item0; int ret; pcwl_p = ddi_get_soft_state(pcwl_soft_state_p, ddi_get_instance(dip)); PCWLDBG((CE_NOTE, "pcwl detach: dip=0x%p cmd=%x\n", (void *)dip, cmd)); if (cmd != DDI_DETACH) return (DDI_FAILURE); if (!(pcwl_p->pcwl_flag & PCWL_ATTACHED)) return (DDI_FAILURE); ret = mac_disable(pcwl_p->pcwl_mh); if (ret != 0) return (DDI_FAILURE); if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) { mutex_enter(&pcwl_p->pcwl_glock); (void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0); PCWL_DISABLE_INTR(pcwl_p); mutex_exit(&pcwl_p->pcwl_glock); } if (pcwl_p->pcwl_scanlist_timeout_id != 0) { (void) untimeout(pcwl_p->pcwl_scanlist_timeout_id); pcwl_p->pcwl_scanlist_timeout_id = 0; } if (pcwl_p->pcwl_connect_timeout_id != 0) { (void) untimeout(pcwl_p->pcwl_connect_timeout_id); pcwl_p->pcwl_connect_timeout_id = 0; } mutex_enter(&pcwl_p->pcwl_scanlist_lock); scan_item0 = list_head(&pcwl_p->pcwl_scan_list); while (scan_item0) { pcwl_delete_scan_item(pcwl_p, scan_item0); scan_item0 = list_head(&pcwl_p->pcwl_scan_list); } list_destroy(&pcwl_p->pcwl_scan_list); mutex_exit(&pcwl_p->pcwl_scanlist_lock); (void) mac_unregister(pcwl_p->pcwl_mh); if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) { mutex_enter(&pcwl_p->pcwl_glock); ddi_remove_intr(dip, 0, pcwl_p->pcwl_ib_cookie); ddi_regs_map_free(&pcwl_p->pcwl_handle); ddi_regs_map_free(&pcwl_p->pcwl_cfg_handle); mutex_exit(&pcwl_p->pcwl_glock); } else if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) { pcwl_unregister_cs(pcwl_p); } pcwl_destroy_locks(pcwl_p); ddi_remove_minor_node(dip, NULL); ddi_soft_state_free(pcwl_soft_state_p, ddi_get_instance(dip)); return (DDI_SUCCESS); } /* * card services and event handlers */ static int pcwl_register_cs(dev_info_t *dip, pcwl_maci_t *pcwl_p) { int ret; client_reg_t cr; client_handle_t chdl; /* uint encoding of socket, function, client */ get_status_t card_status; request_socket_mask_t sock_req; bzero(&cr, sizeof (cr)); cr.Attributes = INFO_IO_CLIENT|INFO_CARD_EXCL|INFO_CARD_SHARE; cr.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_REGISTRATION_COMPLETE | CS_EVENT_CARD_REMOVAL_LOWP | CS_EVENT_CARD_READY | CS_EVENT_PM_RESUME | CS_EVENT_PM_SUSPEND | CS_EVENT_CLIENT_INFO; cr.event_callback_args.client_data = pcwl_p; cr.Version = CS_VERSION; cr.event_handler = (csfunction_t *)pcwl_ev_hdlr; cr.dip = dip; (void) strcpy(cr.driver_name, pcwl_name_str); if (ret = csx_RegisterClient(&chdl, &cr)) { cmn_err(CE_WARN, "pcwl: RegisterClient failed %x\n", ret); goto regcs_ret; } pcwl_p->pcwl_chdl = chdl; bzero(&card_status, sizeof (card_status)); (void) csx_GetStatus(chdl, &card_status); PCWLDBG((CE_NOTE, "pcwl: register_cs: Sock=%x CState=%x SState=%x rState=%x\n", card_status.Socket, card_status.CardState, card_status.SocketState, card_status.raw_CardState)); if (!(card_status.CardState & CS_STATUS_CARD_INSERTED)) { /* card is not present, why are we attaching ? */ ret = CS_NO_CARD; goto regcs_unreg; } cv_init(&pcwl_p->pcwl_cscv, NULL, CV_DRIVER, NULL); mutex_init(&pcwl_p->pcwl_cslock, NULL, MUTEX_DRIVER, *cr.iblk_cookie); mutex_enter(&pcwl_p->pcwl_cslock); if (ret = csx_MapLogSocket(chdl, &pcwl_p->pcwl_log_sock)) { cmn_err(CE_WARN, "pcwl: MapLogSocket failed %x\n", ret); goto regcs_fail; } PCWLDBG((CE_NOTE, "pcwl: register_cs: LogSock=%x PhyAdapter=%x PhySock=%x\n", pcwl_p->pcwl_log_sock.LogSocket, pcwl_p->pcwl_log_sock.PhyAdapter, pcwl_p->pcwl_log_sock.PhySocket)); /* turn on initialization events */ sock_req.Socket = 0; sock_req.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_REGISTRATION_COMPLETE; if (ret = csx_RequestSocketMask(chdl, &sock_req)) { cmn_err(CE_WARN, "pcwl: RequestSocketMask failed %x\n", ret); goto regcs_fail; } /* wait for and process card insertion events */ while (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) cv_wait(&pcwl_p->pcwl_cscv, &pcwl_p->pcwl_cslock); mutex_exit(&pcwl_p->pcwl_cslock); pcwl_p->pcwl_flag |= PCWL_CS_REGISTERED; return (PCWL_SUCCESS); regcs_fail: mutex_destroy(&pcwl_p->pcwl_cslock); cv_destroy(&pcwl_p->pcwl_cscv); regcs_unreg: (void) csx_DeregisterClient(chdl); regcs_ret: pcwl_p->pcwl_flag &= ~PCWL_CS_REGISTERED; return (ret); } static void pcwl_unregister_cs(pcwl_maci_t *pcwl_p) { int ret; release_socket_mask_t mask; mask.Socket = pcwl_p->pcwl_socket; /* * The card service not registered means register_cs function * doesnot return TRUE. Then all the lelated resource has been * released in register_cs. */ if (!(pcwl_p->pcwl_flag | PCWL_CS_REGISTERED)) return; if (ret = csx_ReleaseSocketMask(pcwl_p->pcwl_chdl, &mask)) cmn_err(CE_WARN, "pcwl: ReleaseSocket mask failed %x\n", ret); if (pcwl_p->pcwl_flag & PCWL_CARD_READY) { pcwl_card_remove(pcwl_p); pcwl_p->pcwl_flag &= ~PCWL_CARD_READY; } mutex_destroy(&pcwl_p->pcwl_cslock); cv_destroy(&pcwl_p->pcwl_cscv); if (ret = csx_DeregisterClient(pcwl_p->pcwl_chdl)) cmn_err(CE_WARN, "pcwl: Deregister failed %x\n", ret); } static void pcwl_destroy_locks(pcwl_maci_t *pcwl_p) { mutex_destroy(&pcwl_p->pcwl_txring.wl_tx_lock); mutex_destroy(&pcwl_p->pcwl_scanlist_lock); mutex_destroy(&pcwl_p->pcwl_glock); } static void pcwl_do_suspend(pcwl_maci_t *pcwl_p); static int pcwl_ev_hdlr(event_t event, int priority, event_callback_args_t *arg) { int ret = CS_SUCCESS; pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg->client_data; client_info_t *ci_p = (client_info_t *)&arg->client_info; mutex_enter(&pcwl_p->pcwl_cslock); switch (event) { case CS_EVENT_CARD_INSERTION: ret = pcwl_card_insert(pcwl_p); cv_broadcast(&pcwl_p->pcwl_cscv); break; case CS_EVENT_REGISTRATION_COMPLETE: cv_broadcast(&pcwl_p->pcwl_cscv); break; case CS_EVENT_CARD_REMOVAL: if (priority & CS_EVENT_PRI_HIGH) break; pcwl_card_remove(pcwl_p); cv_broadcast(&pcwl_p->pcwl_cscv); break; case CS_EVENT_CLIENT_INFO: if (GET_CLIENT_INFO_SUBSVC(ci_p->Attributes) != CS_CLIENT_INFO_SUBSVC_CS) break; ci_p->Revision = 0x0101; ci_p->CSLevel = CS_VERSION; ci_p->RevDate = CS_CLIENT_INFO_MAKE_DATE(9, 12, 14); (void) strcpy(ci_p->ClientName, PCWL_IDENT_STRING); (void) strcpy(ci_p->VendorName, CS_SUN_VENDOR_DESCRIPTION); ci_p->Attributes |= CS_CLIENT_INFO_VALID; break; case CS_EVENT_PM_SUSPEND: pcwl_do_suspend(pcwl_p); break; default: ret = CS_UNSUPPORTED_EVENT; break; } mutex_exit(&pcwl_p->pcwl_cslock); return (ret); } /* * assume card is already removed, don't touch the hardware */ static void pcwl_do_suspend(pcwl_maci_t *pcwl_p) { int ret; if (pcwl_p->pcwl_flag & PCWL_CARD_LINKUP) { if (pcwl_p->pcwl_connect_timeout_id != 0) { (void) untimeout(pcwl_p->pcwl_connect_timeout_id); pcwl_p->pcwl_connect_timeout_id = 0; } mutex_enter(&pcwl_p->pcwl_glock); pcwl_p->pcwl_flag &= ~PCWL_CARD_LINKUP; (void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0); /* * A workaround here: If the card is in ad-hoc mode, the * following scan will not work correctly, so any * 'dladm connect-wifi' which need a scan first will not * succeed. software reset the card here as a workround. */ if ((pcwl_p->pcwl_rf.rf_porttype == WL_BSS_IBSS) && (pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT)) { (void) pcwl_reset_backend(pcwl_p); (void) pcwl_init_nicmem(pcwl_p); pcwl_start_locked(pcwl_p); } if (ret = pcwl_loaddef_rf(pcwl_p)) { PCWLDBG((CE_WARN, "cfg_loaddef_err %d\n", ret)); } if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) { PCWLDBG((CE_WARN, "set enable cmd err\n")); } pcwl_delay(pcwl_p, 1000000); if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) { PCWLDBG((CE_WARN, "set disable cmd err\n")); } mac_link_update(GLD3(pcwl_p), LINK_STATE_DOWN); mutex_exit(&pcwl_p->pcwl_glock); } pcwl_p->pcwl_flag |= PCWL_CARD_SUSPEND; PCWLDBG((CE_WARN, "pcwl: do suspend\n")); } static int pcwl_card_insert(pcwl_maci_t *pcwl_p) { int ret, hi, lo; tuple_t tuple; cisparse_t cisparse; io_req_t io; irq_req_t irq; config_req_t cfg; cistpl_config_t config; cistpl_cftable_entry_t *tbl_p; register client_handle_t chdl = pcwl_p->pcwl_chdl; modify_config_t cfgmod; bzero(&tuple, sizeof (tuple)); tuple.DesiredTuple = CISTPL_MANFID; if (ret = csx_GetFirstTuple(chdl, &tuple)) { cmn_err(CE_WARN, "pcwl: get manufacture id failed %x\n", ret); goto insert_ret; } bzero(&cisparse, sizeof (cisparse)); if (ret = csx_Parse_CISTPL_MANFID(chdl, &tuple, &cisparse.manfid)) { cmn_err(CE_WARN, "pcwl: parse manufacture id failed %x\n", ret); goto insert_ret; } /* * verify manufacture ID */ PCWLDBG((CE_NOTE, "pcwl insert: manufacturer_id=%x card=%x\n", cisparse.manfid.manf, cisparse.manfid.card)); bzero(&tuple, sizeof (tuple)); tuple.DesiredTuple = CISTPL_FUNCID; if (ret = csx_GetFirstTuple(chdl, &tuple)) { cmn_err(CE_WARN, "pcwl: get function id failed %x\n", ret); goto insert_ret; } bzero(&cisparse, sizeof (cisparse)); if (ret = csx_Parse_CISTPL_FUNCID(chdl, &tuple, &cisparse.funcid)) { cmn_err(CE_WARN, "pcwl: parse function id failed %x\n", ret); goto insert_ret; } /* * verify function ID */ PCWLDBG((CE_NOTE, "insert:fun_id=%x\n", cisparse.funcid.function)); bzero(&tuple, sizeof (tuple)); tuple.DesiredTuple = CISTPL_CONFIG; if (ret = csx_GetFirstTuple(chdl, &tuple)) { cmn_err(CE_WARN, "pcwl: get config failed %x\n", ret); goto insert_ret; } bzero(&config, sizeof (config)); if (ret = csx_Parse_CISTPL_CONFIG(chdl, &tuple, &config)) { cmn_err(CE_WARN, "pcwl: parse config failed %x\n", ret); goto insert_ret; } PCWLDBG((CE_NOTE, "pcwl: config present=%x nr=%x hr=%x regs[0]=%x base=%x last=%x\n", config.present, config.nr, config.hr, config.regs[0], config.base, config.last)); hi = 0; lo = (int)-1; /* really big number */ tbl_p = &cisparse.cftable; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; for (tbl_p->index = 0; tbl_p->index <= config.hr; ) { PCWLDBG((CE_NOTE, "pcwl insert:tuple idx=%x:\n", tbl_p->index)); if (ret = csx_GetNextTuple(chdl, &tuple)) { cmn_err(CE_WARN, "pcwl: get cftable failed %x\n", ret); break; } bzero((caddr_t)&cisparse, sizeof (cisparse)); if (ret = csx_Parse_CISTPL_CFTABLE_ENTRY(chdl, &tuple, tbl_p)) { cmn_err(CE_WARN, "pcwl: parse cftable failed %x\n", ret); break; } if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_PWR && tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC) { if (tbl_p->pd.pd_vcc.avgI > hi) { hi = tbl_p->pd.pd_vcc.avgI; pcwl_p->pcwl_config_hi = tbl_p->index; } if (tbl_p->pd.pd_vcc.avgI < lo) { lo = tbl_p->pd.pd_vcc.avgI; pcwl_p->pcwl_config = tbl_p->index; } } if (tbl_p->flags & CISTPL_CFTABLE_TPCE_DEFAULT) { if (tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC) pcwl_p->pcwl_vcc = tbl_p->pd.pd_vcc.nomV; if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_IO) pcwl_p->pcwl_iodecode = tbl_p->io.addr_lines; } } PCWLDBG((CE_NOTE, "pcwl: insert:cfg_hi=%x cfg=%x vcc=%x iodecode=%x\n", pcwl_p->pcwl_config_hi, pcwl_p->pcwl_config, pcwl_p->pcwl_vcc, pcwl_p->pcwl_iodecode)); bzero(&io, sizeof (io)); io.BasePort1.base = 0; io.NumPorts1 = 1 << pcwl_p->pcwl_iodecode; io.Attributes1 = IO_DATA_PATH_WIDTH_16; io.IOAddrLines = pcwl_p->pcwl_iodecode; if (ret = csx_RequestIO(chdl, &io)) { cmn_err(CE_WARN, "pcwl: RequestIO failed %x\n", ret); goto insert_ret; } pcwl_p->pcwl_port = io.BasePort1.handle; if (ret = ddi_add_softintr(DIP(pcwl_p), DDI_SOFTINT_HIGH, &pcwl_p->pcwl_softint_id, &pcwl_p->pcwl_ib_cookie, NULL, pcwl_intr, (caddr_t)pcwl_p)) { cmn_err(CE_NOTE, "pcwl(pccard): add softintr failed\n"); goto insert_ret; } irq.Attributes = IRQ_TYPE_EXCLUSIVE; irq.irq_handler = ddi_intr_hilevel(DIP(pcwl_p), 0) ? (csfunction_t *)pcwl_intr_hi : (csfunction_t *)pcwl_intr; irq.irq_handler_arg = pcwl_p; if (ret = csx_RequestIRQ(pcwl_p->pcwl_chdl, &irq)) { cmn_err(CE_WARN, "pcwl: RequestIRQ failed %x\n", ret); goto un_io; } bzero(&cfg, sizeof (cfg)); cfg.Attributes = 0; /* not ready for CONF_ENABLE_IRQ_STEERING yet */ cfg.Vcc = 50; cfg.IntType = SOCKET_INTERFACE_MEMORY_AND_IO; cfg.ConfigBase = config.base; cfg.ConfigIndex = pcwl_p->pcwl_config; cfg.Status = CCSR_IO_IS_8; cfg.Present = config.present; pcwl_p->pcwl_flag |= PCWL_CARD_READY; if (ret = csx_RequestConfiguration(chdl, &cfg)) { cmn_err(CE_WARN, "pcwl: RequestConfiguration failed %x\n", ret); goto un_irq; } if (pcwl_p->pcwl_flag & PCWL_CARD_SUSPEND) { mutex_enter(&pcwl_p->pcwl_glock); (void) pcwl_reset_backend(pcwl_p); /* turn on CS interrupt */ cfgmod.Attributes = CONF_ENABLE_IRQ_STEERING | CONF_IRQ_CHANGE_VALID; cfgmod.Vpp1 = 50; cfgmod.Vpp2 = 50; (void) csx_ModifyConfiguration(pcwl_p->pcwl_chdl, &cfgmod); (void) pcwl_init_nicmem(pcwl_p); pcwl_chip_type(pcwl_p); (void) pcwl_loaddef_rf(pcwl_p); (void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0); pcwl_stop_locked(pcwl_p); /* leaves interface down */ pcwl_p->pcwl_flag &= ~PCWL_CARD_SUSPEND; mutex_exit(&pcwl_p->pcwl_glock); } if (pcwl_p->pcwl_flag & PCWL_CARD_PLUMBED) { (void) pcwl_start(pcwl_p); pcwl_p->pcwl_flag &= ~PCWL_CARD_PLUMBED; } return (CS_SUCCESS); un_irq: (void) csx_ReleaseIRQ(chdl, &irq); un_io: ddi_remove_softintr(pcwl_p->pcwl_softint_id); (void) csx_ReleaseIO(chdl, &io); pcwl_p->pcwl_port = 0; insert_ret: pcwl_p->pcwl_flag &= ~PCWL_CARD_READY; return (ret); } /* * assume card is already removed, don't touch the hardware */ static void pcwl_card_remove(pcwl_maci_t *pcwl_p) { int ret; io_req_t io; irq_req_t irq; /* * The card not ready means Insert function doesnot return TRUE. * then the IO and IRQ has been released in Insert */ if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) return; if (pcwl_p->pcwl_connect_timeout_id != 0) { (void) untimeout(pcwl_p->pcwl_connect_timeout_id); pcwl_p->pcwl_connect_timeout_id = 0; } if (pcwl_p->pcwl_flag & PCWL_CARD_LINKUP) { pcwl_p->pcwl_flag &= ~PCWL_CARD_LINKUP; mac_link_update(GLD3(pcwl_p), LINK_STATE_DOWN); } mutex_enter(&pcwl_p->pcwl_glock); if (pcwl_p->pcwl_flag & PCWL_CARD_INTREN) { pcwl_stop_locked(pcwl_p); pcwl_p->pcwl_flag |= PCWL_CARD_PLUMBED; } pcwl_p->pcwl_flag &= ~PCWL_CARD_READY; mutex_exit(&pcwl_p->pcwl_glock); if (ret = csx_ReleaseConfiguration(pcwl_p->pcwl_chdl, NULL)) cmn_err(CE_WARN, "pcwl: ReleaseConfiguration failed %x\n", ret); bzero(&irq, sizeof (irq)); if (ret = csx_ReleaseIRQ(pcwl_p->pcwl_chdl, &irq)) cmn_err(CE_WARN, "pcwl: ReleaseIRQ failed %x\n", ret); ddi_remove_softintr(pcwl_p->pcwl_softint_id); bzero(&io, sizeof (io)); io.BasePort1.handle = pcwl_p->pcwl_port; io.NumPorts1 = 16; if (ret = csx_ReleaseIO(pcwl_p->pcwl_chdl, &io)) cmn_err(CE_WARN, "pcwl: ReleaseIO failed %x\n", ret); pcwl_p->pcwl_port = 0; } /* * mac operation interface routines */ static int pcwl_start(void *arg) { pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; mutex_enter(&pcwl_p->pcwl_glock); if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) { mutex_exit(&pcwl_p->pcwl_glock); return (PCWL_FAIL); } pcwl_start_locked(pcwl_p); mutex_exit(&pcwl_p->pcwl_glock); return (PCWL_SUCCESS); } static void pcwl_stop(void *arg) { pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; PCWLDBG((CE_NOTE, "pcwl_stop called\n")); mutex_enter(&pcwl_p->pcwl_glock); if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) { mutex_exit(&pcwl_p->pcwl_glock); return; } pcwl_stop_locked(pcwl_p); mutex_exit(&pcwl_p->pcwl_glock); if (pcwl_p->pcwl_connect_timeout_id != 0) { (void) untimeout(pcwl_p->pcwl_connect_timeout_id); pcwl_p->pcwl_connect_timeout_id = 0; } } static int pcwl_saddr(void *arg, const uint8_t *macaddr) { int ret = PCWL_SUCCESS; pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; mutex_enter(&pcwl_p->pcwl_glock); if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) { ret = PCWL_FAIL; goto done; } ether_copy(macaddr, pcwl_p->pcwl_mac_addr); if (pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) { ret = PCWL_FAIL; goto done; } if (pcwl_saddr_locked(pcwl_p)) { ret = PCWL_FAIL; goto done; } if (pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) { ret = PCWL_FAIL; } done: if (ret) cmn_err(CE_WARN, "pcwl set_mac_addr: failed\n"); mutex_exit(&pcwl_p->pcwl_glock); return (ret); } static int pcwl_send(pcwl_maci_t *pcwl_p, mblk_t *mblk_p) { int i = 0; char *buf, *buf_p; wl_frame_t *frm_p; uint16_t pkt_len, ret; uint16_t xmt_id, ring_idx; struct ieee80211_frame *wh; struct ieee80211_llc *llc; mutex_enter(&pcwl_p->pcwl_glock); if ((pcwl_p->pcwl_flag & (PCWL_CARD_READY | PCWL_CARD_LINKUP)) != (PCWL_CARD_READY | PCWL_CARD_LINKUP)) { mutex_exit(&pcwl_p->pcwl_glock); freemsg(mblk_p); return (PCWL_SUCCESS); /* drop packet */ } mutex_exit(&pcwl_p->pcwl_glock); if (pullupmsg(mblk_p, -1) == 0) { freemsg(mblk_p); return (PCWL_SUCCESS); /* drop packet */ } wh = (struct ieee80211_frame *)mblk_p->b_rptr; llc = (struct ieee80211_llc *)&wh[1]; mutex_enter(&pcwl_p->pcwl_txring.wl_tx_lock); ring_idx = pcwl_p->pcwl_txring.wl_tx_prod; pcwl_p->pcwl_txring.wl_tx_prod = (ring_idx + 1) & (WL_XMT_BUF_NUM - 1); /* * check whether there is a xmt buffer available */ while ((i < WL_XMT_BUF_NUM) && (pcwl_p->pcwl_txring.wl_tx_ring[ring_idx])) { ring_idx = pcwl_p->pcwl_txring.wl_tx_prod; pcwl_p->pcwl_txring.wl_tx_prod = (ring_idx + 1) & (WL_XMT_BUF_NUM - 1); i++; } if (i == WL_XMT_BUF_NUM) { mutex_exit(&pcwl_p->pcwl_txring.wl_tx_lock); mutex_enter(&pcwl_p->pcwl_glock); pcwl_p->pcwl_reschedule_need = B_TRUE; mutex_exit(&pcwl_p->pcwl_glock); pcwl_p->pcwl_noxmtbuf++; return (PCWL_FAIL); } xmt_id = pcwl_p->pcwl_txring.wl_tx_fids[ring_idx]; pcwl_p->pcwl_txring.wl_tx_ring[ring_idx] = xmt_id; mutex_exit(&pcwl_p->pcwl_txring.wl_tx_lock); buf = kmem_zalloc(PCWL_NICMEM_SZ, KM_SLEEP); buf_p = (ulong_t)buf & 1 ? buf + 1 : buf; frm_p = (wl_frame_t *)buf_p; #ifdef DEBUG if (pcwl_debug & PCWL_DBG_SEND) { cmn_err(CE_NOTE, "pcwl send: packet"); for (i = 0; i < MBLKL(mblk_p); i++) cmn_err(CE_NOTE, "%x: %x\n", i, *((unsigned char *)mblk_p->b_rptr + i)); } #endif pkt_len = msgdsize(mblk_p); if (pkt_len > (PCWL_NICMEM_SZ - sizeof (wl_frame_t))) { cmn_err(CE_WARN, "pcwl: send mblk is too long"); kmem_free(buf, PCWL_NICMEM_SZ); freemsg(mblk_p); return (PCWL_SUCCESS); /* drop packet */ } if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_TODS) { kmem_free(buf, PCWL_NICMEM_SZ); freemsg(mblk_p); return (PCWL_SUCCESS); /* drop packet */ } bzero(frm_p, WL_802_11_HDRLEN); frm_p->wl_tx_ctl = WL_TXCNTL_SET; bcopy(wh->i_addr3, frm_p->wl_dst_addr, ETHERADDRL); /* dst macaddr */ bcopy(wh->i_addr2, frm_p->wl_src_addr, ETHERADDRL); /* src macaddr */ frm_p->wl_len = htons(pkt_len - sizeof (*wh)); bcopy(llc, &frm_p->wl_dat[0], pkt_len - sizeof (*wh)); pkt_len = pkt_len - (sizeof (*wh) + sizeof (*llc)) + WL_802_11_HDRLEN; PCWLDBG((CE_NOTE, "send: DIX frmsz=%x pkt_len=%x\n", WL_802_11_HDRLEN, pkt_len)); if (pkt_len & 1) /* round up to 16-bit boundary and pad 0 */ buf_p[pkt_len++] = 0; ASSERT(pkt_len <= PCWL_NICMEM_SZ); #ifdef DEBUG if (pcwl_debug & PCWL_DBG_SEND) { cmn_err(CE_NOTE, "pkt_len = %x\n", pkt_len); for (i = 0; i < pkt_len; i++) cmn_err(CE_NOTE, "%x: %x\n", i, *((unsigned char *)buf + i)); } #endif mutex_enter(&pcwl_p->pcwl_glock); ret = (WRCH1(pcwl_p, xmt_id, 0, (uint16_t *)buf_p, 0x2e) || WRPKT(pcwl_p, xmt_id, 0x2e, (uint16_t *)(buf_p + 0x2e), pkt_len - 0x2e)); if (ret) { goto done; } PCWLDBG((CE_NOTE, "send: xmt_id=%x send=%x\n", xmt_id, pkt_len)); (void) pcwl_set_cmd(pcwl_p, WL_CMD_TX | WL_RECLAIM, xmt_id); done: mutex_exit(&pcwl_p->pcwl_glock); kmem_free(buf, PCWL_NICMEM_SZ); freemsg(mblk_p); return (PCWL_SUCCESS); } static mblk_t * pcwl_tx(void *arg, mblk_t *mp) { pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; mblk_t *next; ASSERT(mp != NULL); mutex_enter(&pcwl_p->pcwl_glock); if ((pcwl_p->pcwl_flag & (PCWL_CARD_LINKUP | PCWL_CARD_READY)) != (PCWL_CARD_LINKUP | PCWL_CARD_READY)) { mutex_exit(&pcwl_p->pcwl_glock); freemsgchain(mp); return (NULL); } mutex_exit(&pcwl_p->pcwl_glock); while (mp != NULL) { next = mp->b_next; mp->b_next = NULL; if (pcwl_send(pcwl_p, mp)) { mp->b_next = next; break; } mp = next; } return (mp); } static int pcwl_prom(void *arg, boolean_t on) { int ret = PCWL_SUCCESS; pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; mutex_enter(&pcwl_p->pcwl_glock); if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) { ret = PCWL_FAIL; goto done; } PCWLDBG((CE_NOTE, "pcwl_prom called %x\n", on)); if (on) pcwl_p->pcwl_rf.rf_promiscuous = 1; else pcwl_p->pcwl_rf.rf_promiscuous = 0; if (ret = pcwl_fil_ltv(pcwl_p, 2, WL_RID_PROMISC, pcwl_p->pcwl_rf.rf_promiscuous)) { ret = PCWL_FAIL; } done: if (ret) cmn_err(CE_WARN, "pcwl promisc: failed\n"); mutex_exit(&pcwl_p->pcwl_glock); return (ret); } static int pcwl_gstat(void *arg, uint_t statitem, uint64_t *val) { pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; int ret = PCWL_SUCCESS; uint64_t *cntr_p = pcwl_p->pcwl_cntrs_s; uint16_t rate = 0; uint64_t speed; PCWLDBG((CE_NOTE, "pcwl_gstat called\n")); mutex_enter(&pcwl_p->pcwl_glock); if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) { ret = PCWL_FAIL; goto done; } if (pcwl_get_ltv(pcwl_p, 2, WL_RID_CUR_TX_RATE, &rate)) { cmn_err(CE_WARN, "pcwl kstat: get speed failed\n"); ret = PCWL_FAIL; goto done; } switch (pcwl_p->pcwl_chip_type) { case PCWL_CHIP_PRISMII: switch (rate) { case WL_SPEED_1Mbps_P2: rate = 2; break; case WL_SPEED_2Mbps_P2: rate = 4; break; case WL_SPEED_55Mbps_P2: rate = 11; break; case WL_SPEED_11Mbps_P2: rate = 22; break; default: rate = 0; break; } speed = rate * 500000; break; case PCWL_CHIP_LUCENT: default: speed = rate * 1000000; if (rate == 6) speed = 5500000; break; } switch (statitem) { case MAC_STAT_IFSPEED: *val = speed; break; case MAC_STAT_NOXMTBUF: *val = pcwl_p->pcwl_noxmtbuf; break; case MAC_STAT_NORCVBUF: *val = cntr_p[WLC_RX_DISCARDS_NOBUF]; break; case MAC_STAT_IERRORS: *val = 0; break; case MAC_STAT_OERRORS: *val = cntr_p[WLC_TX_DISCARDS] + cntr_p[WLC_TX_DISCARDS_WRONG_SA]; break; case MAC_STAT_RBYTES: *val = cntr_p[WLC_RX_UNICAST_OCTETS]; break; case MAC_STAT_IPACKETS: *val = cntr_p[WLC_RX_UNICAST_FRAMES]; break; case MAC_STAT_OBYTES: *val = cntr_p[WLC_TX_UNICAST_OCTETS]; break; case MAC_STAT_OPACKETS: *val = cntr_p[WLC_TX_UNICAST_FRAMES]; break; case WIFI_STAT_TX_FAILED: *val = cntr_p[WLC_TX_RETRY_LIMIT] + cntr_p[WLC_TX_DEFERRED_XMITS]; break; case WIFI_STAT_TX_RETRANS: *val = cntr_p[WLC_TX_SINGLE_RETRIES] + cntr_p[WLC_TX_MULTI_RETRIES]; break; case WIFI_STAT_FCS_ERRORS: *val = cntr_p[WLC_RX_FCS_ERRORS]; break; case WIFI_STAT_WEP_ERRORS: *val = cntr_p[WLC_RX_WEP_CANT_DECRYPT]; break; case WIFI_STAT_MCAST_TX: *val = cntr_p[WLC_TX_MULTICAST_FRAMES]; break; case WIFI_STAT_MCAST_RX: *val = cntr_p[WLC_RX_MULTICAST_FRAMES]; break; case WIFI_STAT_TX_FRAGS: *val = cntr_p[WLC_TX_FRAGMENTS]; break; case WIFI_STAT_RX_FRAGS: *val = cntr_p[WLC_RX_FRAGMENTS] + cntr_p[WLC_RX_MSG_IN_MSG_FRAGS] + cntr_p[WLC_RX_MSG_IN_BAD_MSG_FRAGS]; break; case WIFI_STAT_RTS_SUCCESS: case WIFI_STAT_RTS_FAILURE: case WIFI_STAT_ACK_FAILURE: case WIFI_STAT_RX_DUPS: *val = 0; break; default: ret = ENOTSUP; } done: mutex_exit(&pcwl_p->pcwl_glock); return (ret); } static int pcwl_sdmulti(void *arg, boolean_t add, const uint8_t *eth_p) { int ret = PCWL_SUCCESS; uint16_t i; pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; uint16_t *mc_p = pcwl_p->pcwl_mcast; mutex_enter(&pcwl_p->pcwl_glock); if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) { ret = PCWL_FAIL; goto done; } if (add) { /* enable multicast on eth_p, search for available entries */ for (i = 0; i < 16; i++, mc_p += (ETHERADDRL >> 1)) { if (!ether_cmp(eth_p, mc_p)) break; } if (i < 16) /* already part of the filter */ goto done; mc_p = pcwl_p->pcwl_mcast; /* reset mc_p for 2nd scan */ for (i = 0; i < 16; i++, mc_p += (ETHERADDRL >> 1)) { PCWLDBG((CE_NOTE, "smulti: mc[%x]=%s\n", i, ether_sprintf((struct ether_addr *)mc_p))); if (mc_p[0] == 0 && mc_p[1] == 0 && mc_p[2] == 0) break; } if (i >= 16) /* can't find a vacant entry */ goto done; ether_copy(eth_p, mc_p); } else { /* disable multicast, locate the entry and clear it */ for (i = 0; i < 16; i++, mc_p += (ETHERADDRL >> 1)) { if (!ether_cmp(eth_p, mc_p)) break; } if (i >= 16) goto done; mc_p[0] = 0; mc_p[1] = 0; mc_p[2] = 0; } /* * re-blow the entire 16 entries buffer */ if (i = pcwl_put_ltv(pcwl_p, ETHERADDRL << 4, WL_RID_MCAST, pcwl_p->pcwl_mcast)) { ret = PCWL_FAIL; } done: if (ret) cmn_err(CE_WARN, "pcwl set multi addr: failed\n"); mutex_exit(&pcwl_p->pcwl_glock); return (ret); } static uint_t pcwl_intr(caddr_t arg) { uint16_t stat; pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; mutex_enter(&pcwl_p->pcwl_glock); if ((pcwl_p->pcwl_flag & (PCWL_CARD_READY | PCWL_CARD_INTREN)) != (PCWL_CARD_READY | PCWL_CARD_INTREN)) { mutex_exit(&pcwl_p->pcwl_glock); return (DDI_INTR_UNCLAIMED); } PCWL_READ(pcwl_p, WL_EVENT_STAT, stat); if (!(stat & WL_INTRS) || stat == WL_EV_ALL) { mutex_exit(&pcwl_p->pcwl_glock); return (DDI_INTR_UNCLAIMED); } PCWL_WRITE(pcwl_p, WL_INT_EN, 0); if (stat & WL_EV_RX) { pcwl_rcv(pcwl_p); PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_RX); PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_RX); } if (stat & WL_EV_TX) { if (pcwl_txdone(pcwl_p) == PCWL_SUCCESS) { if (pcwl_p->pcwl_reschedule_need == B_TRUE) { mutex_exit(&pcwl_p->pcwl_glock); mac_tx_update(GLD3(pcwl_p)); mutex_enter(&pcwl_p->pcwl_glock); pcwl_p->pcwl_reschedule_need = B_FALSE; } } PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX); PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX); } if (stat & WL_EV_ALLOC) { PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_ALLOC | 0x1000); PCWL_WRITE(pcwl_p, WL_EVENT_ACK, 0x1000); } if (stat & WL_EV_INFO) { pcwl_infodone(pcwl_p); PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO); PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO); } if (stat & WL_EV_TX_EXC) { if (pcwl_txdone(pcwl_p) == PCWL_SUCCESS) { if (pcwl_p->pcwl_reschedule_need == B_TRUE) { mutex_exit(&pcwl_p->pcwl_glock); mac_tx_update(GLD3(pcwl_p)); mutex_enter(&pcwl_p->pcwl_glock); pcwl_p->pcwl_reschedule_need = B_FALSE; } } PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX_EXC); PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX_EXC); } if (stat & WL_EV_INFO_DROP) { PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO_DROP); PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO_DROP); } PCWL_ENABLE_INTR(pcwl_p); mutex_exit(&pcwl_p->pcwl_glock); return (DDI_INTR_CLAIMED); } static uint_t pcwl_intr_hi(caddr_t arg) { uint16_t stat; pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; mutex_enter(&pcwl_p->pcwl_glock); if ((pcwl_p->pcwl_flag & (PCWL_CARD_READY | PCWL_CARD_INTREN)) != (PCWL_CARD_READY | PCWL_CARD_INTREN)) { mutex_exit(&pcwl_p->pcwl_glock); return (DDI_INTR_UNCLAIMED); } PCWL_READ(pcwl_p, WL_EVENT_STAT, stat); if (!(stat & WL_INTRS) || stat == WL_EV_ALL) { mutex_exit(&pcwl_p->pcwl_glock); return (DDI_INTR_UNCLAIMED); } PCWL_WRITE(pcwl_p, WL_INT_EN, 0); /* disable interrupt without ack */ mutex_exit(&pcwl_p->pcwl_glock); ddi_trigger_softintr(pcwl_p->pcwl_softint_id); return (DDI_INTR_CLAIMED); } /* * called at interrupt context to retrieve data from card */ static void pcwl_rcv(pcwl_maci_t *pcwl_p) { uint16_t id, len, off, ret, frm_ctl; wl_frame_t frm; mblk_t *mp = allocb(PCWL_NICMEM_SZ, BPRI_MED); if (!mp) return; ASSERT(mp->b_rptr == mp->b_wptr); PCWL_READ(pcwl_p, WL_RX_FID, id); PCWL_WRITE(pcwl_p, WL_RX_FID, 0); if (id == WL_INVALID_FID) { PCWLDBG((CE_NOTE, "pcwl rcv: get rx_fid failed\n")); ret = PCWL_FAIL; goto done; } if (ret = RDCH0(pcwl_p, id, 0, (uint16_t *)&frm, sizeof (frm))) { PCWLDBG((CE_NOTE, "pcwl rcv: read frm failed %x\n", ret)); goto done; } if (frm.wl_status & WL_STAT_ERRSTAT) { PCWLDBG((CE_NOTE, "pcwl rcv: errstat %x\n", frm.wl_status)); ret = frm.wl_status; goto done; } PCWLDBG((CE_NOTE, "pcwl rcv: frame type %x\n", frm.wl_status)); #ifdef DEBUG if (pcwl_debug & PCWL_DBG_RCV) { int i; cmn_err(CE_NOTE, "pcwl rcv: frm header\n"); for (i = 0; i < WL_802_11_HDRLEN; i++) cmn_err(CE_NOTE, "%x: %x\n", i, *((uint8_t *)&frm + i)); } #endif len = frm.wl_dat_len; /* * this driver deal with WEP by itself. so plugin always thinks no wep. */ frm.wl_frame_ctl &= ~(IEEE80211_FC1_WEP << 8); frm_ctl = frm.wl_frame_ctl; switch (frm.wl_status) { case WL_STAT_1042: case WL_STAT_TUNNEL: case WL_STAT_WMP_MSG: PCWL_SWAP16((uint16_t *)&frm.wl_frame_ctl, sizeof (struct ieee80211_frame)); /* * discard those frames which are not from the AP we connect or * without 'ap->sta' direction */ if ((pcwl_p->pcwl_rf.rf_porttype == WL_BSS_BSS) && ((((frm_ctl >> 8) & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_FROMDS) || bcmp(pcwl_p->pcwl_bssid, frm.wl_addr2, 6) != 0)) { ret = PCWL_FAIL; goto done; } bcopy(&frm.wl_frame_ctl, mp->b_wptr, sizeof (struct ieee80211_frame)); mp->b_wptr += sizeof (struct ieee80211_frame); PCWL_SWAP16((uint16_t *)&frm.wl_dat[0], sizeof (struct ieee80211_llc)); bcopy(&frm.wl_dat[0], mp->b_wptr, sizeof (struct ieee80211_llc)); mp->b_wptr += sizeof (struct ieee80211_llc); len -= (2 + WL_SNAPHDR_LEN); off = WL_802_11_HDRLEN; break; default: PCWLDBG((CE_NOTE, "pcwl rcv: incorrect pkt\n")); break; } if (len > MBLKSIZE(mp)) { PCWLDBG((CE_NOTE, "pcwl rcv: oversz pkt %x\n", len)); ret = PCWL_FAIL; goto done; } if (len & 1) len++; ret = RDPKT(pcwl_p, id, off, (uint16_t *)mp->b_wptr, len); done: if (ret) { PCWLDBG((CE_NOTE, "pcwl rcv: rd data %x\n", ret)); freemsg(mp); return; } mp->b_wptr = mp->b_wptr + len; #ifdef DEBUG if (pcwl_debug & PCWL_DBG_RCV) { int i; cmn_err(CE_NOTE, "pcwl rcv: len=0x%x\n", len); for (i = 0; i < len+14; i++) cmn_err(CE_NOTE, "%x: %x\n", i, *((uint8_t *)mp->b_rptr + i)); } #endif mutex_exit(&pcwl_p->pcwl_glock); mac_rx(GLD3(pcwl_p), NULL, mp); mutex_enter(&pcwl_p->pcwl_glock); } static uint32_t pcwl_txdone(pcwl_maci_t *pcwl_p) { uint16_t fid, i; PCWL_READ(pcwl_p, WL_ALLOC_FID, fid); PCWL_WRITE(pcwl_p, WL_ALLOC_FID, 0); mutex_enter(&pcwl_p->pcwl_txring.wl_tx_lock); for (i = 0; i < WL_XMT_BUF_NUM; i++) { if (fid == pcwl_p->pcwl_txring.wl_tx_ring[i]) { pcwl_p->pcwl_txring.wl_tx_ring[i] = 0; break; } } pcwl_p->pcwl_txring.wl_tx_cons = (pcwl_p->pcwl_txring.wl_tx_cons + 1) & (WL_XMT_BUF_NUM - 1); mutex_exit(&pcwl_p->pcwl_txring.wl_tx_lock); if (i == WL_XMT_BUF_NUM) return (PCWL_FAIL); return (PCWL_SUCCESS); } static void pcwl_infodone(pcwl_maci_t *pcwl_p) { uint16_t id, ret, i; uint16_t linkStatus[2]; uint16_t linkStat; wifi_data_t wd = { 0 }; PCWL_READ(pcwl_p, WL_INFO_FID, id); if (id == WL_INVALID_FID) { cmn_err(CE_WARN, "pcwl infodone: read info_fid failed\n"); return; } if (ret = RDCH0(pcwl_p, id, 0, linkStatus, sizeof (linkStatus))) { PCWLDBG((CE_WARN, "pcwl infodone read infoFrm failed %x\n", ret)); return; } PCWLDBG((CE_NOTE, "pcwl infodone: Frame length= %x, Frame Type = %x\n", linkStatus[0], linkStatus[1])); switch (linkStatus[1]) { case WL_INFO_LINK_STAT: (void) RDCH0(pcwl_p, id, sizeof (linkStatus), &linkStat, sizeof (linkStat)); PCWLDBG((CE_NOTE, "pcwl infodone: link status=%x\n", linkStat)); if (!(pcwl_p->pcwl_flag & PCWL_CARD_LINKUP) && linkStat == WL_LINK_CONNECT) { #ifdef DEBUG if (pcwl_debug & PCWL_DBG_LINKINFO) cmn_err(CE_NOTE, "pcwl: Link up \n"); #endif pcwl_p->pcwl_flag |= PCWL_CARD_LINKUP; mutex_exit(&pcwl_p->pcwl_glock); if (pcwl_p->pcwl_connect_timeout_id != 0) { (void) untimeout(pcwl_p-> pcwl_connect_timeout_id); pcwl_p->pcwl_connect_timeout_id = 0; } mutex_enter(&pcwl_p->pcwl_glock); mac_link_update(GLD3(pcwl_p), LINK_STATE_UP); (void) pcwl_get_ltv(pcwl_p, 6, WL_RID_BSSID, (uint16_t *)pcwl_p->pcwl_bssid); PCWL_SWAP16((uint16_t *)pcwl_p->pcwl_bssid, 6); pcwl_get_rssi(pcwl_p); bcopy(pcwl_p->pcwl_bssid, wd.wd_bssid, 6); wd.wd_secalloc = WIFI_SEC_NONE; wd.wd_opmode = IEEE80211_M_STA; (void) mac_pdata_update(pcwl_p->pcwl_mh, &wd, sizeof (wd)); } if ((pcwl_p->pcwl_flag & PCWL_CARD_LINKUP) && ((linkStat == WL_LINK_DISCONNECT) || (linkStat == WL_LINK_AP_OOR))) { #ifdef DEBUG if (pcwl_debug & PCWL_DBG_LINKINFO) cmn_err(CE_NOTE, "pcwl: Link down \n"); #endif PCWLDBG((CE_NOTE, "pcwl infodone: link status = %d\n", linkStat)); pcwl_p->pcwl_flag &= ~PCWL_CARD_LINKUP; if (linkStat == WL_LINK_AP_OOR) pcwl_p->pcwl_connect_timeout_id = timeout(pcwl_connect_timeout, pcwl_p, drv_usectohz(1000)); mutex_exit(&pcwl_p->pcwl_glock); mac_link_update(GLD3(pcwl_p), LINK_STATE_DOWN); mutex_enter(&pcwl_p->pcwl_glock); } break; case WL_INFO_SCAN_RESULTS: case WL_INFO_HSCAN_RESULTS: pcwl_ssid_scan(pcwl_p, id, linkStatus[0], linkStatus[1]); break; case WL_INFO_COUNTERS: linkStatus[0]--; if (linkStatus[0] > WLC_STAT_CNT) { linkStatus[0] = MIN(linkStatus[0], WLC_STAT_CNT); } (void) RDCH0(pcwl_p, id, sizeof (linkStatus), pcwl_p->pcwl_cntrs_t, linkStatus[0]<<1); /* * accumulate all the statistics items for kstat use. */ for (i = 0; i < WLC_STAT_CNT; i++) pcwl_p->pcwl_cntrs_s[i] += pcwl_p->pcwl_cntrs_t[i]; break; default: break; } } static uint16_t pcwl_set_cmd(pcwl_maci_t *pcwl_p, uint16_t cmd, uint16_t param) { int i; uint16_t stat; if (((cmd == WL_CMD_ENABLE) && ((pcwl_p->pcwl_flag & PCWL_ENABLED) != 0)) || ((cmd == WL_CMD_DISABLE) && ((pcwl_p->pcwl_flag & PCWL_ENABLED) == 0))) return (PCWL_SUCCESS); for (i = 0; i < WL_TIMEOUT; i++) { PCWL_READ(pcwl_p, WL_COMMAND, stat); if (stat & WL_CMD_BUSY) { drv_usecwait(1); } else { break; } } if (i == WL_TIMEOUT) { cmn_err(CE_WARN, "pcwl: setcmd %x, %x timeout %x due to " "busy bit\n", cmd, param, stat); return (PCWL_TIMEDOUT_CMD); } PCWL_WRITE(pcwl_p, WL_PARAM0, param); PCWL_WRITE(pcwl_p, WL_PARAM1, 0); PCWL_WRITE(pcwl_p, WL_PARAM2, 0); PCWL_WRITE(pcwl_p, WL_COMMAND, cmd); if (cmd == WL_CMD_INI) drv_usecwait(100000); /* wait .1 sec */ for (i = 0; i < WL_TIMEOUT; i++) { PCWL_READ(pcwl_p, WL_EVENT_STAT, stat); if (!(stat & WL_EV_CMD)) { drv_usecwait(1); } else { break; } } if (i == WL_TIMEOUT) { cmn_err(CE_WARN, "pcwl: setcmd %x,%x timeout %x\n", cmd, param, stat); if (stat & (WL_EV_ALLOC | WL_EV_RX)) PCWL_WRITE(pcwl_p, WL_EVENT_ACK, stat); return (PCWL_TIMEDOUT_CMD); } PCWL_READ(pcwl_p, WL_STATUS, stat); PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_CMD); if (stat & WL_STAT_CMD_RESULT) { /* err in feedback status */ cmn_err(CE_WARN, "pcwl: set_cmd %x,%x failed %x\n", cmd, param, stat); return (PCWL_FAILURE_CMD); } if (cmd == WL_CMD_ENABLE) pcwl_p->pcwl_flag |= PCWL_ENABLED; if (cmd == WL_CMD_DISABLE) pcwl_p->pcwl_flag &= (~PCWL_ENABLED); return (PCWL_SUCCESS); } static uint16_t pcwl_set_ch(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off, uint16_t channel) { int i; uint16_t stat, select, offset; if (channel) { select = WL_SEL1; offset = WL_OFF1; } else { select = WL_SEL0; offset = WL_OFF0; } PCWL_WRITE(pcwl_p, select, type); PCWL_WRITE(pcwl_p, offset, off); for (stat = 0, i = 0; i < WL_TIMEOUT; i++) { PCWL_READ(pcwl_p, offset, stat); if (!(stat & (WL_OFF_BUSY|WL_OFF_ERR))) break; else { drv_usecwait(1); } } if (i == WL_TIMEOUT) { cmn_err(CE_WARN, "set_ch%d %x,%x failed %x\n", channel, type, off, stat); return (PCWL_TIMEDOUT_TARGET); } return (PCWL_SUCCESS); } static uint16_t pcwl_get_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type, uint16_t *val_p) { uint16_t stat; ASSERT(!(len & 1)); len >>= 1; /* convert bytes to 16-bit words */ /* * 1. select read mode */ if (stat = pcwl_set_cmd(pcwl_p, WL_CMD_ACCESS | WL_ACCESS_READ, type)) return (stat); /* * 2. select Buffer Access Path (channel) 1 for PIO */ if (stat = pcwl_set_ch(pcwl_p, type, 0, 1)) return (stat); /* * 3. read length */ PCWL_READ(pcwl_p, WL_DATA1, stat); if (stat != (len + 1)) { PCWLDBG((CE_NOTE, "get_ltv 0x%x expected 0x%x+1, got 0x%x\n", type, (len + 1) << 1, stat)); stat = (stat >> 1) - 1; len = MIN(stat, len); } /* * 4. read type */ PCWL_READ(pcwl_p, WL_DATA1, stat); if (stat != type) return (PCWL_BADTYPE); /* * 5. read value */ for (stat = 0; stat < len; stat++, val_p++) { PCWL_READ_P(pcwl_p, WL_DATA1, val_p, 1); } return (PCWL_SUCCESS); } static uint16_t pcwl_fil_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type, uint16_t val) { uint16_t stat; ASSERT(!(len & 1)); /* * 1. select Buffer Access Path (channel) 1 for PIO */ if (stat = pcwl_set_ch(pcwl_p, type, 0, 1)) return (stat); /* * 2. write length */ len >>= 1; /* convert bytes to 16-bit words */ stat = len + 1; /* 1 extra word */ PCWL_WRITE(pcwl_p, WL_DATA1, stat); /* * 3. write type */ PCWL_WRITE(pcwl_p, WL_DATA1, type); /* * 4. fill value */ for (stat = 0; stat < len; stat++) { PCWL_WRITE(pcwl_p, WL_DATA1, val); } /* * 5. select write mode */ return (pcwl_set_cmd(pcwl_p, WL_CMD_ACCESS|WL_ACCESS_WRITE, type)); } static uint16_t pcwl_put_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type, uint16_t *val_p) { uint16_t stat; ASSERT(!(len & 1)); /* * 1. select Buffer Access Path (channel) 1 for PIO */ if (stat = pcwl_set_ch(pcwl_p, type, 0, 1)) return (stat); /* * 2. write length */ len >>= 1; /* convert bytes to 16-bit words */ stat = len + 1; /* 1 extra word */ PCWL_WRITE(pcwl_p, WL_DATA1, stat); /* * 3. write type */ PCWL_WRITE(pcwl_p, WL_DATA1, type); /* * 4. write value */ for (stat = 0; stat < len; stat++, val_p++) { PCWL_WRITE_P(pcwl_p, WL_DATA1, val_p, 1); } /* * 5. select write mode */ return (pcwl_set_cmd(pcwl_p, WL_CMD_ACCESS|WL_ACCESS_WRITE, type)); } #define PCWL_COMPSTR_LEN 34 static uint16_t pcwl_put_str(pcwl_maci_t *pcwl_p, uint16_t type, char *str_p) { uint16_t buf[PCWL_COMPSTR_LEN / 2]; uint8_t str_len = strlen(str_p); bzero(buf, PCWL_COMPSTR_LEN); buf[0] = str_len; bcopy(str_p, (caddr_t)(buf + 1), str_len); PCWLDBG((CE_NOTE, "put_str: buf[0]=%x buf=%s\n", buf[0], (caddr_t)(buf + 1))); PCWL_SWAP16(buf + 1, PCWL_COMPSTR_LEN - 2); return (pcwl_put_ltv(pcwl_p, PCWL_COMPSTR_LEN, type, buf)); } /*ARGSUSED*/ static uint16_t pcwl_rdch0(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off, uint16_t *buf_p, int len, int order) { uint16_t o; ASSERT(!(len & 1)); /* * It seems that for PrismII chip, frequently overlap use of path0 * and path1 may hang the hardware. So for PrismII chip, just use * path1. Test proves this workaround is OK. */ if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) { if (type = pcwl_set_ch(pcwl_p, type, off, 1)) return (type); o = WL_DATA1; } else { if (type = pcwl_set_ch(pcwl_p, type, off, 0)) return (type); o = WL_DATA0; } len >>= 1; for (off = 0; off < len; off++, buf_p++) { PCWL_READ_P(pcwl_p, o, buf_p, order); } return (PCWL_SUCCESS); } /*ARGSUSED*/ static uint16_t pcwl_wrch1(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off, uint16_t *buf_p, int len, int order) { ASSERT(!(len & 1)); if (type = pcwl_set_ch(pcwl_p, type, off, 1)) return (type); len >>= 1; for (off = 0; off < len; off++, buf_p++) { PCWL_WRITE_P(pcwl_p, WL_DATA1, buf_p, order); } return (PCWL_SUCCESS); } static uint16_t pcwl_alloc_nicmem(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t *id_p) { int i; uint16_t stat; len = ((len + 1) >> 1) << 1; /* round up to 16-bit boundary */ if (stat = pcwl_set_cmd(pcwl_p, WL_CMD_ALLOC_MEM, len)) return (stat); for (stat = 0, i = 0; i < WL_TIMEOUT; i++) { PCWL_READ(pcwl_p, WL_EVENT_STAT, stat); if (stat & WL_EV_ALLOC) break; else drv_usecwait(1); } if (i == WL_TIMEOUT) return (PCWL_TIMEDOUT_ALLOC); PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_ALLOC); PCWL_READ(pcwl_p, WL_ALLOC_FID, stat); *id_p = stat; /* * zero fill the allocated NIC mem - sort of pcwl_fill_ch */ (void) pcwl_set_ch(pcwl_p, stat, 0, 1); for (len >>= 1, stat = 0; stat < len; stat++) { PCWL_WRITE(pcwl_p, WL_DATA1, 0); } return (PCWL_SUCCESS); } static int pcwl_add_scan_item(pcwl_maci_t *pcwl_p, wl_scan_result_t s) { wl_scan_list_t *scan_item; scan_item = kmem_zalloc(sizeof (wl_scan_list_t), KM_SLEEP); if (scan_item == NULL) { cmn_err(CE_WARN, "pcwl add_scan_item: zalloc failed\n"); return (PCWL_FAIL); } scan_item->wl_val = s; scan_item->wl_timeout = WL_SCAN_TIMEOUT_MAX; list_insert_tail(&pcwl_p->pcwl_scan_list, scan_item); pcwl_p->pcwl_scan_num++; return (PCWL_SUCCESS); } static void pcwl_delete_scan_item(pcwl_maci_t *pcwl_p, wl_scan_list_t *s) { list_remove(&pcwl_p->pcwl_scan_list, s); kmem_free(s, sizeof (*s)); pcwl_p->pcwl_scan_num--; } static void pcwl_scanlist_timeout(void *arg) { pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; wl_scan_list_t *scan_item0, *scan_item1; mutex_enter(&pcwl_p->pcwl_scanlist_lock); scan_item0 = list_head(&pcwl_p->pcwl_scan_list); for (; scan_item0; ) { PCWLDBG((CE_NOTE, "ssid = %s\n", scan_item0->wl_val.wl_srt_ssid)); PCWLDBG((CE_NOTE, "timeout left: %ds", scan_item0->wl_timeout)); scan_item1 = list_next(&pcwl_p->pcwl_scan_list, scan_item0); if (scan_item0->wl_timeout == 0) { pcwl_delete_scan_item(pcwl_p, scan_item0); } else { scan_item0->wl_timeout--; } scan_item0 = scan_item1; } mutex_exit(&pcwl_p->pcwl_scanlist_lock); pcwl_p->pcwl_scanlist_timeout_id = timeout(pcwl_scanlist_timeout, pcwl_p, drv_usectohz(1000000)); } static void pcwl_get_rssi(pcwl_maci_t *pcwl_p) { wl_scan_list_t *scan_item0; uint16_t cq[3]; bzero(cq, sizeof (cq)); mutex_enter(&pcwl_p->pcwl_scanlist_lock); scan_item0 = list_head(&pcwl_p->pcwl_scan_list); for (; scan_item0; ) { if (bcmp(scan_item0->wl_val.wl_srt_bssid, pcwl_p->pcwl_bssid, 6) == 0) { pcwl_p->pcwl_rssi = scan_item0->wl_val.wl_srt_sl; } scan_item0 = list_next(&pcwl_p->pcwl_scan_list, scan_item0); } mutex_exit(&pcwl_p->pcwl_scanlist_lock); if (!pcwl_p->pcwl_rssi) { (void) pcwl_get_ltv(pcwl_p, 6, WL_RID_COMMQUAL, cq); pcwl_p->pcwl_rssi = cq[1]; } } /* * Note: * PrismII chipset has 2 extra space for the reason why scan is initiated */ static void pcwl_ssid_scan(pcwl_maci_t *pcwl_p, uint16_t fid, uint16_t flen, uint16_t stype) { uint16_t stat; uint16_t ssidNum, i; uint16_t off, szbuf; uint16_t tmp[2]; wl_scan_list_t *scan_item0; uint32_t check_num; uint8_t bssid_t[6]; wl_scan_result_t sctbl; off = sizeof (uint16_t) * 2; switch (pcwl_p->pcwl_chip_type) { case PCWL_CHIP_PRISMII: (void) RDCH0(pcwl_p, fid, off, tmp, 4); off += 4; szbuf = (stype == WL_INFO_SCAN_RESULTS ? 31 : 32); PCWLDBG((CE_NOTE, "pcwl ssid_scan: PRISM chip\n")); break; case PCWL_CHIP_LUCENT: PCWLDBG((CE_NOTE, "pcwl ssid_scan LUCENT chip\n")); default: szbuf = 25; } flen = flen + 1 - (off >> 1); ssidNum = flen/szbuf; ssidNum = min(WL_SRT_MAX_NUM, ssidNum); PCWLDBG((CE_NOTE, "pcwl: ssid_scan frame length = %d\n", flen)); PCWLDBG((CE_NOTE, "pcwl ssid_scan: %d ssid(s) available", ssidNum)); bzero(bssid_t, sizeof (bssid_t)); for (i = 0; i < ssidNum; i++) { (void) RDCH0(pcwl_p, fid, off, (uint16_t *)&sctbl, 2*szbuf); #ifdef DEBUG if (pcwl_debug & PCWL_DBG_INFO) { int j; for (j = 0; j < sizeof (sctbl); j++) cmn_err(CE_NOTE, "%d: %x\n", j, *((uint8_t *)&sctbl + j)); } #endif off += (szbuf << 1); stat = min(sctbl.wl_srt_ssidlen, 31); PCWL_SWAP16((uint16_t *)(sctbl.wl_srt_bssid), 6); PCWL_SWAP16((uint16_t *)(sctbl.wl_srt_ssid), stat); sctbl.wl_srt_ssid[stat] = '\0'; sctbl.wl_srt_sl &= 0x7f; /* * sometimes, those empty items are recorded by hardware, * this is wrong, just ignore those items here. */ if (bcmp(sctbl.wl_srt_bssid, bssid_t, 6) == 0) { continue; } if (bcmp(sctbl.wl_srt_bssid, pcwl_p->pcwl_bssid, 6) == 0) { pcwl_p->pcwl_rssi = sctbl.wl_srt_sl; } /* * save/update the scan item in scanlist */ mutex_enter(&pcwl_p->pcwl_scanlist_lock); check_num = 0; scan_item0 = list_head(&pcwl_p->pcwl_scan_list); if (scan_item0 == NULL) { if (pcwl_add_scan_item(pcwl_p, sctbl) != 0) { mutex_exit(&pcwl_p->pcwl_scanlist_lock); return; } } for (; scan_item0; ) { if (bcmp(sctbl.wl_srt_bssid, scan_item0->wl_val.wl_srt_bssid, 6) == 0) { scan_item0->wl_val = sctbl; scan_item0->wl_timeout = WL_SCAN_TIMEOUT_MAX; break; } else { check_num++; } scan_item0 = list_next(&pcwl_p->pcwl_scan_list, scan_item0); } if (check_num == pcwl_p->pcwl_scan_num) { if (pcwl_add_scan_item(pcwl_p, sctbl) != 0) { mutex_exit(&pcwl_p->pcwl_scanlist_lock); return; } } mutex_exit(&pcwl_p->pcwl_scanlist_lock); PCWLDBG((CE_NOTE, "pcwl ssid_scan: ssid%d = %s\n", i+1, sctbl.wl_srt_ssid)); PCWLDBG((CE_NOTE, "pcwl ssid_scan: channel = %d\n", sctbl.wl_srt_chid)); PCWLDBG((CE_NOTE, "pcwl ssid_scan: signal level= %d\n", sctbl.wl_srt_sl)); PCWLDBG((CE_NOTE, "pcwl ssid_scan: noise level = %d\n", sctbl.wl_srt_anl)); PCWLDBG((CE_NOTE, "pcwl ssid_scan: bssid%d =" " %x %x %x %x %x %x\n\n", i+1, sctbl.wl_srt_bssid[0], sctbl.wl_srt_bssid[1], sctbl.wl_srt_bssid[2], sctbl.wl_srt_bssid[3], sctbl.wl_srt_bssid[4], sctbl.wl_srt_bssid[5])); } } /* * delay in which the mutex is not hold. * assuming the mutex has already been hold. */ static void pcwl_delay(pcwl_maci_t *pcwl_p, clock_t microsecs) { ASSERT(mutex_owned(&pcwl_p->pcwl_glock)); mutex_exit(&pcwl_p->pcwl_glock); delay(drv_usectohz(microsecs)); mutex_enter(&pcwl_p->pcwl_glock); } static int pcwl_reset_backend(pcwl_maci_t *pcwl_p) { uint16_t ret = 0; if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_INI, 0)) { return ((int)ret); } pcwl_delay(pcwl_p, 100000); /* wait .1 sec */ if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_INI, 0)) { return ((int)ret); } pcwl_delay(pcwl_p, 100000); /* wait .1 sec */ PCWL_DISABLE_INTR(pcwl_p); return (PCWL_SUCCESS); } /* * get card capability (WEP, default channel), setup broadcast, mac addresses */ static int pcwl_get_cap(pcwl_maci_t *pcwl_p) { uint16_t stat, ch_no; uint16_t buf[ETHERADDRL >> 1]; bzero(buf, ETHERADDRL); if (stat = pcwl_get_ltv(pcwl_p, 2, WL_RID_OWN_CHNL, &ch_no)) { cmn_err(CE_CONT, "pcwl get_cap: get def channel failed" " %x\n", stat); return ((int)stat); } if (stat = pcwl_get_ltv(pcwl_p, 2, WL_RID_WEP_AVAIL, &pcwl_p->pcwl_has_wep)) { cmn_err(CE_CONT, "pcwl get_cap: get WEP capability failed" " %x\n", stat); return ((int)stat); } if (stat = pcwl_get_ltv(pcwl_p, ETHERADDRL, WL_RID_MAC_NODE, buf)) { cmn_err(CE_CONT, "pcwl get_cap: get macaddr failed" " %x\n", stat); return ((int)stat); } /* * don't assume m_xxx members are 16-bit aligned */ PCWL_SWAP16(buf, ETHERADDRL); ether_copy(buf, pcwl_p->pcwl_mac_addr); return (PCWL_SUCCESS); } static int pcwl_init_nicmem(pcwl_maci_t *pcwl_p) { uint16_t ret, i; uint16_t rc; for (i = 0; i < WL_XMT_BUF_NUM; i++) { ret = pcwl_alloc_nicmem(pcwl_p, PCWL_NICMEM_SZ, &rc); if (ret) { cmn_err(CE_WARN, "pcwl: alloc NIC Tx buf failed %x\n", ret); return (PCWL_FAIL); } pcwl_p->pcwl_txring.wl_tx_fids[i] = rc; pcwl_p->pcwl_txring.wl_tx_ring[i] = 0; PCWLDBG((CE_NOTE, "pcwl: alloc_nicmem_id[%d]=%x\n", i, rc)); } pcwl_p->pcwl_txring.wl_tx_prod = pcwl_p->pcwl_txring.wl_tx_cons = 0; ret = pcwl_alloc_nicmem(pcwl_p, PCWL_NICMEM_SZ, &pcwl_p->pcwl_mgmt_id); if (ret) { cmn_err(CE_WARN, "pcwl: alloc NIC Mgmt buf failed %x\n", ret); return (PCWL_FAIL); } PCWLDBG((CE_NOTE, "pcwl: alloc_nicmem mgmt_id=%x\n", pcwl_p->pcwl_mgmt_id)); return (PCWL_SUCCESS); } static int pcwl_loaddef_rf(pcwl_maci_t *pcwl_p) { pcwl_p->pcwl_rf.rf_max_datalen = WL_DEFAULT_DATALEN; pcwl_p->pcwl_rf.rf_create_ibss = WL_DEFAULT_CREATE_IBSS; pcwl_p->pcwl_rf.rf_porttype = WL_BSS_BSS; pcwl_p->pcwl_rf.rf_rts_thresh = WL_DEFAULT_RTS_THRESH; pcwl_p->pcwl_rf.rf_tx_rate = WL_TX_RATE_FIX_11M(pcwl_p); pcwl_p->pcwl_rf.rf_pm_enabled = WL_DEFAULT_PM_ENABLED; pcwl_p->pcwl_rf.rf_own_chnl = WL_DEFAULT_CHAN; (void) strcpy(pcwl_p->pcwl_rf.rf_own_ssid, ""); (void) strcpy(pcwl_p->pcwl_rf.rf_desired_ssid, ""); (void) strcpy(pcwl_p->pcwl_rf.rf_nodename, ""); pcwl_p->pcwl_rf.rf_encryption = WL_NOENCRYPTION; pcwl_p->pcwl_rf.rf_authtype = WL_OPENSYSTEM; pcwl_p->pcwl_rf.rf_tx_crypt_key = WL_DEFAULT_TX_CRYPT_KEY; bzero((pcwl_p->pcwl_rf.rf_ckeys), sizeof (rf_ckey_t) * 4); pcwl_p->pcwl_rf.rf_promiscuous = 0; return (pcwl_config_rf(pcwl_p)); } static int pcwl_config_rf(pcwl_maci_t *pcwl_p) { pcwl_rf_t *rf_p = &pcwl_p->pcwl_rf; uint16_t create_ibss, porttype; /* * Lucent card: * 0 Join ESS or IBSS; 1 Join ESS or join/create IBSS * PrismII card: * 3 Join ESS or IBSS(do not create IBSS); * 1 Join ESS or join/create IBSS */ create_ibss = rf_p->rf_create_ibss; if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) { if (rf_p->rf_create_ibss == 0) create_ibss = 3; } /* * Lucent card: * 1 BSS; 3 pseudo IBSS(only for test,not the 802.11 IBSS) * so porttype register should always be set to 1 * PrismII card: * 0 IBSS; 1 BSS; 2 WDS; 3 pseudo IBSS; 6 hostAP */ switch (pcwl_p->pcwl_chip_type) { case PCWL_CHIP_PRISMII: if (rf_p->rf_porttype == WL_BSS_BSS) porttype = 1; else if (rf_p->rf_porttype == WL_BSS_IBSS) porttype = 0; else porttype = 0; break; case PCWL_CHIP_LUCENT: default: porttype = 1; } FIL_LTV(pcwl_p, PCWL_MCBUF_LEN, WL_RID_MCAST, 0); FIL_LTV(pcwl_p, 2, WL_RID_PROMISC, 0); FIL_LTV(pcwl_p, 2, WL_RID_TICK_TIME, 0); FIL_LTV(pcwl_p, 2, WL_RID_MAX_DATALEN, rf_p->rf_max_datalen); FIL_LTV(pcwl_p, 2, WL_RID_CREATE_IBSS, create_ibss); FIL_LTV(pcwl_p, 2, WL_RID_PORTTYPE, porttype); FIL_LTV(pcwl_p, 2, WL_RID_RTS_THRESH, rf_p->rf_rts_thresh); FIL_LTV(pcwl_p, 2, WL_RID_TX_RATE, rf_p->rf_tx_rate); FIL_LTV(pcwl_p, 2, WL_RID_SYSTEM_SCALE, rf_p->rf_system_scale); FIL_LTV(pcwl_p, 2, WL_RID_PM_ENABLED, rf_p->rf_pm_enabled); FIL_LTV(pcwl_p, 2, WL_RID_MAX_SLEEP, rf_p->rf_max_sleep); FIL_LTV(pcwl_p, 2, WL_RID_OWN_CHNL, rf_p->rf_own_chnl); PUT_STR(pcwl_p, WL_RID_OWN_SSID, rf_p->rf_own_ssid); PUT_STR(pcwl_p, WL_RID_DESIRED_SSID, rf_p->rf_desired_ssid); PUT_STR(pcwl_p, WL_RID_NODENAME, rf_p->rf_nodename); if (!pcwl_p->pcwl_has_wep) goto done; switch (pcwl_p->pcwl_chip_type) { case PCWL_CHIP_PRISMII: { int i; for (i = 0; i < 4; i++) { int k_len = strlen((char *)rf_p->rf_ckeys[i].ckey_dat); if (k_len == 0) continue; k_len = k_len > 5 ? 14 : 6; PUT_LTV(pcwl_p, k_len, WL_RID_CRYPT_KEY0_P2 + i, (uint16_t *)&rf_p->rf_ckeys[i].ckey_dat); } FIL_LTV(pcwl_p, 2, WL_RID_TX_CRYPT_KEY_P2, rf_p->rf_tx_crypt_key); FIL_LTV(pcwl_p, 2, WL_RID_AUTHTYPE_P2, rf_p->rf_authtype); FIL_LTV(pcwl_p, 2, WL_RID_ENCRYPTION_P2, rf_p->rf_encryption); if (pcwl_p->pcwl_rf.rf_promiscuous) FIL_LTV(pcwl_p, 2, WL_RID_PROMISC, 1); } break; case PCWL_CHIP_LUCENT: default: FIL_LTV(pcwl_p, 2, WL_RID_ENCRYPTION, rf_p->rf_encryption); FIL_LTV(pcwl_p, 2, WL_RID_AUTHTYPE_L, rf_p->rf_authtype); FIL_LTV(pcwl_p, 2, WL_RID_TX_CRYPT_KEY, rf_p->rf_tx_crypt_key); PUT_LTV(pcwl_p, sizeof (rf_p->rf_ckeys), WL_RID_DEFLT_CRYPT_KEYS, (uint16_t *)rf_p->rf_ckeys); break; } done: return (PCWL_SUCCESS); } static void pcwl_start_locked(pcwl_maci_t *pcwl_p) { pcwl_p->pcwl_flag |= PCWL_CARD_INTREN; PCWL_ENABLE_INTR(pcwl_p); } static void pcwl_stop_locked(pcwl_maci_t *pcwl_p) { PCWL_DISABLE_INTR(pcwl_p); pcwl_p->pcwl_flag &= (~PCWL_CARD_INTREN); PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX|WL_EV_RX|WL_EV_TX_EXC| WL_EV_ALLOC|WL_EV_INFO|WL_EV_INFO_DROP); PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX|WL_EV_RX|WL_EV_TX_EXC| WL_EV_ALLOC| WL_EV_INFO|WL_EV_INFO_DROP); } /*ARGSUSED*/ static int pcwl_saddr_locked(pcwl_maci_t *pcwl_p) { int ret; uint16_t buf[ETHERADDRL >> 1]; ether_copy(pcwl_p->pcwl_mac_addr, buf); PCWL_SWAP16(buf, ETHERADDRL); ret = pcwl_put_ltv(pcwl_p, ETHERADDRL, WL_RID_MAC_NODE, buf); if (ret) { cmn_err(CE_WARN, "pcwl set_mac_addr: failed %x\n", ret); return (PCWL_FAIL); } return (PCWL_SUCCESS); } static void pcwl_chip_type(pcwl_maci_t *pcwl_p) { pcwl_ltv_ver_t ver; pcwl_ltv_fwver_t f; bzero(&ver, sizeof (ver)); (void) pcwl_get_ltv(pcwl_p, sizeof (ver), WL_RID_CARD_ID, (uint16_t *)&ver); PCWLDBG((CE_NOTE, "card id:%04x-%04x-%04x-%04x\n", ver.wl_compid, ver.wl_variant, ver.wl_major, ver.wl_minor)); if ((ver.wl_compid & 0xf000) != 0x8000) return; /* lucent */ pcwl_p->pcwl_chip_type = PCWL_CHIP_PRISMII; (void) pcwl_get_ltv(pcwl_p, sizeof (ver), WL_RID_COMP_IDENT, (uint16_t *)&ver); PCWLDBG((CE_NOTE, "PRISM-II ver:%04x-%04x-%04x-%04x\n", ver.wl_compid, ver.wl_variant, ver.wl_major, ver.wl_minor)); bzero(&f, sizeof (f)); (void) pcwl_get_ltv(pcwl_p, sizeof (f), WL_RID_FWVER, (uint16_t *)&f); PCWL_SWAP16((uint16_t *)&f, sizeof (f)); PCWLDBG((CE_NOTE, "Firmware Pri:%s 2,3:%s\n", (char *)f.pri, (char *)f.st)); } /* * Brussels support */ /* * MAC_PROP_WL_ESSID */ static int pcwl_set_essid(pcwl_maci_t *pcwl_p, const void *wldp_buf) { char *value; pcwl_rf_t *rf_p; wl_essid_t *iw_essid = (wl_essid_t *)wldp_buf; rf_p = &pcwl_p->pcwl_rf; value = iw_essid->wl_essid_essid; (void) strncpy(rf_p->rf_desired_ssid, value, MIN(32, strlen(value))); rf_p->rf_desired_ssid[strlen(value)] = '\0'; (void) strncpy(rf_p->rf_own_ssid, value, MIN(32, strlen(value))); rf_p->rf_own_ssid[strlen(value)] = '\0'; PCWLDBG((CE_CONT, "pcwl: set: desired essid=%s\n", rf_p->rf_desired_ssid)); return (ENETRESET); } static int pcwl_get_essid(pcwl_maci_t *pcwl_p, void *wldp_buf) { char ssid[36]; uint16_t ret; uint16_t val; int len; int err = 0; wl_essid_t ow_essid; pcwl_rf_t *rf_p; rf_p = &pcwl_p->pcwl_rf; bzero(&ow_essid, sizeof (wl_essid_t)); bzero(ssid, sizeof (ssid)); ret = pcwl_get_ltv(pcwl_p, 2, WL_RID_PORTSTATUS, &val); if (ret) { err = EIO; return (err); } PCWLDBG((CE_NOTE, "PortStatus = %d\n", val)); switch (val) { case WL_PORT_DISABLED: case WL_PORT_INITIAL: len = mi_strlen(rf_p->rf_desired_ssid); ow_essid.wl_essid_length = len; bcopy(rf_p->rf_desired_ssid, ow_essid.wl_essid_essid, len); break; case WL_PORT_TO_IBSS: case WL_PORT_TO_BSS: case WL_PORT_OOR: (void) pcwl_get_ltv((pcwl_p), 34, WL_RID_SSID, (uint16_t *)ssid); PCWL_SWAP16((uint16_t *)(ssid+2), *(uint16_t *)ssid); ssid[*(uint16_t *)ssid + 2] = '\0'; len = mi_strlen(ssid+2); ow_essid.wl_essid_length = len; bcopy(ssid + 2, ow_essid.wl_essid_essid, len); break; default: err = EINVAL; break; } bcopy(&ow_essid, wldp_buf, sizeof (wl_essid_t)); return (err); } /* * MAC_PROP_WL_BSSID */ static int pcwl_get_bssid(pcwl_maci_t *pcwl_p, void *wldp_buf) { uint16_t ret; uint16_t retval; uint8_t bssid[6]; int err = 0; if (ret = pcwl_get_ltv(pcwl_p, 2, WL_RID_PORTSTATUS, &retval)) { err = EIO; return (err); } PCWLDBG((CE_NOTE, "PortStatus = %d\n", ret)); if (retval == WL_PORT_DISABLED || retval == WL_PORT_INITIAL) { bzero(wldp_buf, sizeof (wl_bssid_t)); } else if (retval == WL_PORT_TO_IBSS || retval == WL_PORT_TO_BSS || retval == WL_PORT_OOR) { (void) pcwl_get_ltv(pcwl_p, 6, WL_RID_BSSID, (uint16_t *)bssid); PCWL_SWAP16((uint16_t *)bssid, 6); bcopy(bssid, wldp_buf, sizeof (wl_bssid_t)); } PCWLDBG((CE_CONT, "pcwl_get_bssid: bssid=%x %x %x %x %x %x\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5])); return (err); } /* * MAC_PROP_WL_LINKSTATUS */ static int pcwl_get_linkstatus(pcwl_maci_t *pcwl_p, void *wldp_buf) { uint16_t ret; uint16_t retval; int err = 0; ret = pcwl_get_ltv(pcwl_p, 2, WL_RID_PORTSTATUS, &retval); if (ret) { err = EIO; PCWLDBG((CE_WARN, "cfg_linkstatus_get_error\n")); return (err); } PCWLDBG((CE_NOTE, "PortStatus = %d\n", retval)); switch (retval) { case WL_PORT_DISABLED: case WL_PORT_INITIAL: *(wl_linkstatus_t *)wldp_buf = WL_NOTCONNECTED; break; case WL_PORT_TO_IBSS: case WL_PORT_TO_BSS: case WL_PORT_OOR: *(wl_linkstatus_t *)wldp_buf = WL_CONNECTED; break; default: err = EINVAL; break; } return (err); } /* * MAC_PROP_WL_BSSTYP */ static int pcwl_set_bsstype(pcwl_maci_t *pcwl_p, const void *wldp_buf) { uint16_t ret; pcwl_rf_t *rf_p; int err = ENETRESET; rf_p = &pcwl_p->pcwl_rf; ret = (uint16_t)(*(wl_bss_type_t *)wldp_buf); if ((ret != WL_BSS_BSS) && (ret != WL_BSS_IBSS) && (ret != WL_BSS_ANY)) { err = ENOTSUP; return (err); } rf_p->rf_porttype = ret; return (err); } static void pcwl_get_bsstype(pcwl_maci_t *pcwl_p, void *wldp_buf) { pcwl_rf_t *rf_p; rf_p = &pcwl_p->pcwl_rf; *(wl_bss_type_t *)wldp_buf = rf_p->rf_porttype; PCWLDBG((CE_CONT, "pcwl_get_bsstype: porttype=%d\n", rf_p->rf_porttype)); } /* * MAC_PROP_WL_PHY_CONFIG */ static int pcwl_set_phy(pcwl_maci_t *pcwl_p, const void *wldp_buf) { uint16_t ret; pcwl_rf_t *rf_p; int err = ENETRESET; wl_phy_conf_t *phy = (wl_phy_conf_t *)wldp_buf; rf_p = &pcwl_p->pcwl_rf; ret = (uint16_t)(phy->wl_phy_dsss_conf.wl_dsss_channel); if (ret < 1 || ret > 14) { err = ENOTSUP; return (err); } rf_p->rf_own_chnl = ret; PCWLDBG((CE_CONT, "pcwl: set channel=%d\n", rf_p->rf_own_chnl)); return (err); } static int pcwl_get_phy(pcwl_maci_t *pcwl_p, void *wldp_buf) { uint16_t retval; wl_dsss_t *dsss = (wl_dsss_t *)wldp_buf; int err = 0; if (pcwl_get_ltv(pcwl_p, 2, WL_RID_CURRENT_CHNL, &retval)) { err = EIO; return (err); } dsss->wl_dsss_channel = retval; PCWLDBG((CE_CONT, "pcwl_get_phy: channel=%d\n", retval)); dsss->wl_dsss_subtype = WL_DSSS; return (err); } /* * MAC_PROP_WL_DESIRED_RATESa */ static int pcwl_set_desrates(pcwl_maci_t *pcwl_p, const void *wldp_buf) { int err = ENETRESET; char rates[4]; char maxrate; uint16_t i; pcwl_rf_t *rf_p; wl_rates_t *iw_rates = (wl_rates_t *)wldp_buf; rf_p = &pcwl_p->pcwl_rf; bzero(rates, sizeof (rates)); for (i = 0; i < 4; i++) { rates[i] = iw_rates->wl_rates_rates[i]; PCWLDBG((CE_CONT, "pcwl: set tx_rate[%d]=%d\n", i, rates[i])); } PCWLDBG((CE_CONT, "pcwl: set rate_num=%d\n", iw_rates->wl_rates_num)); switch (iw_rates->wl_rates_num) { case 1: switch (rates[0]) { case WL_RATE_1M: rf_p->rf_tx_rate = WL_TX_RATE_FIX_1M(pcwl_p); break; case WL_RATE_2M: rf_p->rf_tx_rate = WL_TX_RATE_FIX_2M(pcwl_p); break; case WL_RATE_11M: rf_p->rf_tx_rate = WL_TX_RATE_FIX_11M(pcwl_p); break; case WL_RATE_5_5M: rf_p->rf_tx_rate = WL_TX_RATE_FIX_5M(pcwl_p); break; default: err = EINVAL; break; } break; case 2: maxrate = (rates[0] > rates[1] ? rates[0] : rates[1]); switch (maxrate) { case WL_RATE_2M: rf_p->rf_tx_rate = WL_TX_RATE_AUTO_L(pcwl_p); break; case WL_RATE_11M: rf_p->rf_tx_rate = WL_TX_RATE_AUTO_H(pcwl_p); break; case WL_RATE_5_5M: rf_p->rf_tx_rate = WL_TX_RATE_AUTO_M(pcwl_p); break; default: err = EINVAL; break; } break; case 3: maxrate = (rates[0] > rates[1] ? rates[0] : rates[1]); maxrate = (rates[2] > maxrate ? rates[2] : maxrate); switch (maxrate) { case WL_RATE_11M: rf_p->rf_tx_rate = WL_TX_RATE_AUTO_H(pcwl_p); break; case WL_RATE_5_5M: rf_p->rf_tx_rate = WL_TX_RATE_AUTO_M(pcwl_p); break; default: err = EINVAL; break; } break; case 4: rf_p->rf_tx_rate = WL_TX_RATE_AUTO_H(pcwl_p); break; default: err = ENOTSUP; break; } PCWLDBG((CE_CONT, "pcwl: set tx_rate=%d\n", rf_p->rf_tx_rate)); return (err); } static int pcwl_get_desrates(pcwl_maci_t *pcwl_p, void *wldp_buf) { uint16_t rate; int err = 0; if (pcwl_get_ltv(pcwl_p, 2, WL_RID_TX_RATE, &rate)) { err = EIO; return (err); } if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) { ((wl_rates_t *)wldp_buf)->wl_rates_num = 1; switch (rate) { case WL_SPEED_1Mbps_P2: (((wl_rates_t *)wldp_buf)->wl_rates_rates)[0] = WL_RATE_1M; break; case WL_SPEED_2Mbps_P2: (((wl_rates_t *)wldp_buf)->wl_rates_rates)[0] = WL_RATE_2M; break; case WL_SPEED_55Mbps_P2: (((wl_rates_t *)wldp_buf)->wl_rates_rates)[0] = WL_RATE_5_5M; break; case WL_SPEED_11Mbps_P2: (((wl_rates_t *)wldp_buf)->wl_rates_rates)[0] = WL_RATE_11M; break; default: err = EINVAL; break; } } else { switch (rate) { case WL_L_TX_RATE_FIX_1M: ((wl_rates_t *)wldp_buf)->wl_rates_num = 1; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[0] = WL_RATE_1M; break; case WL_L_TX_RATE_FIX_2M: ((wl_rates_t *)wldp_buf)->wl_rates_num = 1; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[0] = WL_RATE_2M; break; case WL_L_TX_RATE_AUTO_H: ((wl_rates_t *)wldp_buf)->wl_rates_num = 4; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[0] = WL_RATE_1M; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[1] = WL_RATE_2M; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[2] = WL_RATE_5_5M; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[3] = WL_RATE_11M; break; case WL_L_TX_RATE_FIX_5M: ((wl_rates_t *)wldp_buf)->wl_rates_num = 1; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[0] = WL_RATE_5_5M; break; case WL_L_TX_RATE_FIX_11M: ((wl_rates_t *)wldp_buf)->wl_rates_num = 1; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[0] = WL_RATE_11M; break; case WL_L_TX_RATE_AUTO_L: ((wl_rates_t *)wldp_buf)->wl_rates_num = 2; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[0] = WL_RATE_1M; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[1] = WL_RATE_2M; break; case WL_L_TX_RATE_AUTO_M: ((wl_rates_t *)wldp_buf)->wl_rates_num = 3; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[0] = WL_RATE_1M; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[1] = WL_RATE_2M; (((wl_rates_t *)wldp_buf)->wl_rates_rates)[2] = WL_RATE_5_5M; break; default: err = EINVAL; break; } } PCWLDBG((CE_CONT, "pcwl: get rate=%d\n", rate)); return (err); } /* * MAC_PROP_WL_SUP_RATE */ static void pcwl_get_suprates(void *wldp_buf) { wl_rates_t *wl_rates = (wl_rates_t *)wldp_buf; wl_rates->wl_rates_num = 4; wl_rates->wl_rates_rates[0] = WL_RATE_1M; wl_rates->wl_rates_rates[1] = WL_RATE_2M; wl_rates->wl_rates_rates[2] = WL_RATE_5_5M; wl_rates->wl_rates_rates[3] = WL_RATE_11M; } /* * MAC_PROP_WL_POWER_MODE */ static int pcwl_set_powermode(pcwl_maci_t *pcwl_p, const void *wldp_buf) { uint16_t ret; pcwl_rf_t *rf_p; int err = 0; rf_p = &pcwl_p->pcwl_rf; ret = (uint16_t)(((wl_ps_mode_t *)wldp_buf)->wl_ps_mode); if (ret != WL_PM_AM && ret != WL_PM_MPS && ret != WL_PM_FAST) { err = ENOTSUP; return (err); } rf_p->rf_pm_enabled = ret; return (err); } static void pcwl_get_powermode(pcwl_maci_t *pcwl_p, void *wldp_buf) { pcwl_rf_t *rf_p; rf_p = &pcwl_p->pcwl_rf; ((wl_ps_mode_t *)wldp_buf)->wl_ps_mode = rf_p->rf_pm_enabled; } /* * MAC_PROP_AUTH_MODE */ static int pcwl_set_authmode(pcwl_maci_t *pcwl_p, const void *wldp_buf) { uint16_t ret; pcwl_rf_t *rf_p; int err = ENETRESET; rf_p = &pcwl_p->pcwl_rf; ret = (uint16_t)(*(wl_authmode_t *)wldp_buf); if (ret != WL_OPENSYSTEM && ret != WL_SHAREDKEY) { err = ENOTSUP; return (err); } rf_p->rf_authtype = ret; return (err); } static void pcwl_get_authmode(pcwl_maci_t *pcwl_p, void *wldp_buf) { pcwl_rf_t *rf_p; rf_p = &pcwl_p->pcwl_rf; *(wl_authmode_t *)wldp_buf = rf_p->rf_authtype; } /* * MAC_PROP_WL_ENCRYPTION */ static int pcwl_set_encrypt(pcwl_maci_t *pcwl_p, const void *wldp_buf) { uint16_t ret; pcwl_rf_t *rf_p; int err = ENETRESET; rf_p = &pcwl_p->pcwl_rf; ret = (uint16_t)(*(wl_encryption_t *)wldp_buf); PCWLDBG((CE_NOTE, "pcwl_set_encrypt: %d\n", ret)); if (ret != WL_NOENCRYPTION && ret != WL_ENC_WEP) { err = ENOTSUP; return (err); } rf_p->rf_encryption = ret; return (err); } static void pcwl_get_encrypt(pcwl_maci_t *pcwl_p, void *wldp_buf) { pcwl_rf_t *rf_p; rf_p = &pcwl_p->pcwl_rf; *(wl_encryption_t *)wldp_buf = rf_p->rf_encryption; } /* * MAC_PROP_WL_CREATE_IBSS */ static int pcwl_set_ibss(pcwl_maci_t *pcwl_p, const void *wldp_buf) { uint16_t ret; pcwl_rf_t *rf_p; int err = ENETRESET; rf_p = &pcwl_p->pcwl_rf; ret = (uint16_t)(*(wl_create_ibss_t *)wldp_buf); if (ret != 0 && ret != 1) { err = ENOTSUP; return (err); } rf_p->rf_create_ibss = ret; return (err); } static void pcwl_get_ibss(pcwl_maci_t *pcwl_p, void *wldp_buf) { pcwl_rf_t *rf_p; rf_p = &pcwl_p->pcwl_rf; *(wl_create_ibss_t *)wldp_buf = rf_p->rf_create_ibss; } /* * MAC_PROP_WL_RSSI */ static void pcwl_get_param_rssi(pcwl_maci_t *pcwl_p, void *wldp_buf) { if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) { *(wl_rssi_t *)wldp_buf = min((pcwl_p->pcwl_rssi * 15 / 85 + 1), 15); } else { /* * According to the description of the * datasheet(Lucent card), the signal level * value is between 27 -- 154. * we reflect these value to 1-15 as rssi. */ if (pcwl_p->pcwl_rssi <= 27) *(wl_rssi_t *)wldp_buf = 1; else if (pcwl_p->pcwl_rssi > 154) *(wl_rssi_t *)wldp_buf = 15; else *(wl_rssi_t *)wldp_buf = min(15, ((pcwl_p->pcwl_rssi - 27) * 15 / 127)); } } /* * MAC_PROP_WL_KEY_TAB */ static int pcwl_set_wepkey(pcwl_maci_t *pcwl_p, const void *wldp_buf) { uint16_t i; pcwl_rf_t *rf_p; wl_wep_key_t *p_wepkey_tab; rf_p = &pcwl_p->pcwl_rf; bzero((rf_p->rf_ckeys), sizeof (rf_ckey_t) * MAX_NWEPKEYS); p_wepkey_tab = (wl_wep_key_t *)wldp_buf; for (i = 0; i < MAX_NWEPKEYS; i++) { if (p_wepkey_tab[i].wl_wep_operation == WL_ADD) { rf_p->rf_ckeys[i].ckey_len = p_wepkey_tab[i].wl_wep_length; bcopy(p_wepkey_tab[i].wl_wep_key, rf_p->rf_ckeys[i].ckey_dat, p_wepkey_tab[i].wl_wep_length); PCWL_SWAP16((uint16_t *) &rf_p->rf_ckeys[i].ckey_dat, rf_p->rf_ckeys[i].ckey_len + 1); PCWLDBG((CE_CONT, "%s, %d\n", rf_p->rf_ckeys[i].ckey_dat, i)); } PCWLDBG((CE_CONT, "pcwl: rf_ckeys[%d]=%s\n", i, (char *)(rf_p->rf_ckeys[i].ckey_dat))); } return (ENETRESET); } /* * MAC_PROP_WL_RADIO */ static void pcwl_get_radio(void *wldp_buf) { wl_radio_t *radio = (wl_radio_t *)wldp_buf; *radio = B_TRUE; } /* * MAC_PROP_WL_ESSLIST */ static void pcwl_get_esslist(pcwl_maci_t *pcwl_p, void *wldp_buf) { uint16_t i; wl_ess_conf_t *p_ess_conf; wl_scan_list_t *scan_item; mutex_enter(&pcwl_p->pcwl_scanlist_lock); ((wl_ess_list_t *)wldp_buf)->wl_ess_list_num = pcwl_p->pcwl_scan_num; scan_item = list_head(&pcwl_p->pcwl_scan_list); for (i = 0; i < pcwl_p->pcwl_scan_num; i++) { if (!scan_item) break; p_ess_conf = (wl_ess_conf_t *)((char *)wldp_buf + offsetof(wl_ess_list_t, wl_ess_list_ess) + i * sizeof (wl_ess_conf_t)); bcopy(scan_item->wl_val.wl_srt_ssid, p_ess_conf->wl_ess_conf_essid.wl_essid_essid, mi_strlen(scan_item->wl_val.wl_srt_ssid)); bcopy(scan_item->wl_val.wl_srt_bssid, p_ess_conf->wl_ess_conf_bssid, 6); (p_ess_conf->wl_phy_conf).wl_phy_dsss_conf.wl_dsss_subtype = WL_DSSS; p_ess_conf->wl_ess_conf_wepenabled = (scan_item->wl_val.wl_srt_cap & 0x10 ? WL_ENC_WEP : WL_NOENCRYPTION); p_ess_conf->wl_ess_conf_bsstype = (scan_item->wl_val.wl_srt_cap & 0x1 ? WL_BSS_BSS : WL_BSS_IBSS); p_ess_conf->wl_phy_conf.wl_phy_dsss_conf.wl_dsss_channel = scan_item->wl_val.wl_srt_chid; if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) { p_ess_conf->wl_ess_conf_sl = min(scan_item->wl_val.wl_srt_sl * 15 / 85 + 1, 15); } else { if (scan_item->wl_val.wl_srt_sl <= 27) p_ess_conf->wl_ess_conf_sl = 1; else if (scan_item->wl_val.wl_srt_sl > 154) p_ess_conf->wl_ess_conf_sl = 15; else p_ess_conf->wl_ess_conf_sl = min(15, ((scan_item->wl_val.wl_srt_sl - 27) * 15 / 127)); } p_ess_conf->wl_supported_rates[0] = WL_RATE_1M; p_ess_conf->wl_supported_rates[0] = WL_RATE_2M; p_ess_conf->wl_supported_rates[0] = WL_RATE_5_5M; p_ess_conf->wl_supported_rates[0] = WL_RATE_11M; scan_item = list_next(&pcwl_p->pcwl_scan_list, scan_item); } mutex_exit(&pcwl_p->pcwl_scanlist_lock); } /* * for wificonfig and dladm ioctl */ static int pcwl_cfg_essid(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { char ssid[36]; uint16_t i; uint16_t val; pcwl_rf_t *rf_p; wldp_t *infp; wldp_t *outfp; char *buf; int iret; int err = 0; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); infp = (wldp_t *)mp->b_rptr; rf_p = &pcwl_p->pcwl_rf; bzero(ssid, sizeof (ssid)); if (cmd == WLAN_GET_PARAM) { err = pcwl_get_essid(pcwl_p, outfp->wldp_buf); if (err == EIO) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_HW_ERROR; goto done; } (void) pcwl_get_ltv(pcwl_p, 2, WL_RID_PORTSTATUS, &val); if (val == WL_PORT_DISABLED || val == WL_PORT_INITIAL) { outfp->wldp_length = WIFI_BUF_OFFSET + offsetof(wl_essid_t, wl_essid_essid) + mi_strlen(rf_p->rf_desired_ssid); } else if (val == WL_PORT_TO_IBSS || val == WL_PORT_TO_BSS || val == WL_PORT_OOR) { outfp->wldp_length = WIFI_BUF_OFFSET + offsetof(wl_essid_t, wl_essid_essid) + mi_strlen(ssid+2); } else { outfp->wldp_length = WIFI_BUF_OFFSET; } outfp->wldp_result = WL_SUCCESS; PCWLDBG((CE_CONT, "outfp->length=%d\n", outfp->wldp_length)); PCWLDBG((CE_CONT, "pcwl: get desired essid=%s\n", rf_p->rf_desired_ssid)); } else if (cmd == WLAN_SET_PARAM) { (void) pcwl_set_essid(pcwl_p, infp->wldp_buf); outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_SUCCESS; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } static int pcwl_cfg_bssid(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t i; int iret; wldp_t *outfp; char *buf; int err = 0; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bssid_t); if (cmd == WLAN_GET_PARAM) { err = pcwl_get_bssid(pcwl_p, outfp->wldp_buf); if (err == EIO) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_HW_ERROR; goto done; } outfp->wldp_result = WL_SUCCESS; } else if (cmd == WLAN_SET_PARAM) { outfp->wldp_result = WL_READONLY; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } /*ARGSUSED*/ static int pcwl_cmd_scan(pcwl_maci_t *pcwl_p) { uint16_t vall[18], ret = WL_SUCCESS; pcwl_rf_t *rf_p; uint32_t enable, i; size_t len; rf_p = &pcwl_p->pcwl_rf; /* * The logic of this funtion is really tricky. * Firstly, the chip can only scan in BSS mode, so necessary * backup and restore is required before and after the scan * command. * Secondly, for Lucent chip, Alrealy associated with an AP * can only scan the APes on the fixed channel, so we must * set the desired_ssid as "" before scan and restore after. * Thirdly, scan cmd is effective only when the card is enabled * and any 'set' operation(such as set bsstype, ssid)must disable * the card first and then enable the card after the 'set' */ enable = pcwl_p->pcwl_flag & PCWL_ENABLED; len = strlen(rf_p->rf_desired_ssid); if (pcwl_p->pcwl_rf.rf_porttype != WL_BSS_BSS) { if ((enable) && (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0))) { ret = (int)WL_HW_ERROR; goto done; } FIL_LTV(pcwl_p, 2, WL_RID_PORTTYPE, WL_BSS_BSS); } if ((pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT) && (len != 0)) { if ((enable) && (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0))) { ret = (int)WL_HW_ERROR; goto done; } PUT_STR(pcwl_p, WL_RID_DESIRED_SSID, ""); } if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) { ret = (int)WL_HW_ERROR; goto done; } pcwl_delay(pcwl_p, 1000000); switch (pcwl_p->pcwl_chip_type) { case PCWL_CHIP_PRISMII: bzero(vall, sizeof (vall)); vall[0] = 0x3fff; /* channel mask */ vall[1] = 0x1; /* tx rate */ for (i = 0; i < WL_MAX_SCAN_TIMES; i++) { PUT_LTV(pcwl_p, sizeof (vall), WL_RID_HSCAN_REQUEST, vall); pcwl_delay(pcwl_p, 1000000); if (pcwl_p->pcwl_scan_num >= WL_SCAN_AGAIN_THRESHOLD) break; } PCWLDBG((CE_NOTE, "PRISM chip\n")); break; case PCWL_CHIP_LUCENT: PCWLDBG((CE_NOTE, "LUCENT chip\n")); default: for (i = 0; i < WL_MAX_SCAN_TIMES; i++) { if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_INQUIRE, WL_INFO_SCAN_RESULTS)) { ret = (int)WL_HW_ERROR; goto done; } pcwl_delay(pcwl_p, 500000); if (pcwl_p->pcwl_scan_num >= WL_SCAN_AGAIN_THRESHOLD) break; } break; } if ((pcwl_p->pcwl_rf.rf_porttype != WL_BSS_BSS) || ((pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT) && (len != 0))) { if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) { ret = (int)WL_HW_ERROR; goto done; } if (ret = pcwl_config_rf(pcwl_p)) { ret = (int)WL_HW_ERROR; goto done; } if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) { ret = (int)WL_HW_ERROR; goto done; } pcwl_delay(pcwl_p, 1000000); } if ((!enable) && (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0))) { ret = (int)WL_HW_ERROR; } done: if (ret) cmn_err(CE_WARN, "pcwl: scan failed due to hardware error"); return (ret); } /*ARGSUSED*/ static int pcwl_cfg_scan(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { wldp_t *outfp; char *buf; uint16_t i; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); pcwl_get_esslist(pcwl_p, outfp->wldp_buf); outfp->wldp_length = WIFI_BUF_OFFSET + offsetof(wl_ess_list_t, wl_ess_list_ess) + pcwl_p->pcwl_scan_num * sizeof (wl_ess_conf_t); outfp->wldp_result = WL_SUCCESS; for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); kmem_free(buf, MAX_BUF_LEN); return (WL_SUCCESS); } /*ARGSUSED*/ static int pcwl_cfg_linkstatus(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { wldp_t *outfp; char *buf; uint16_t i, val; int iret; int err = 0; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); err = pcwl_get_linkstatus(pcwl_p, outfp->wldp_buf); if (err == EIO) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_HW_ERROR; goto done; } (void) pcwl_get_ltv(pcwl_p, 2, WL_RID_PORTSTATUS, &val); if (val == WL_PORT_DISABLED || val == WL_PORT_INITIAL) { outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t); } else if (val == WL_PORT_TO_IBSS || val == WL_PORT_TO_BSS || val == WL_PORT_OOR) { outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t); } else { outfp->wldp_length = WIFI_BUF_OFFSET; } outfp->wldp_result = WL_SUCCESS; done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } static int pcwl_cfg_bsstype(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t i; wldp_t *infp; wldp_t *outfp; char *buf; int iret; int err = 0; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); infp = (wldp_t *)mp->b_rptr; outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bss_type_t); if (cmd == WLAN_GET_PARAM) { pcwl_get_bsstype(pcwl_p, outfp->wldp_buf); outfp->wldp_result = WL_SUCCESS; } else if (cmd == WLAN_SET_PARAM) { err = pcwl_set_bsstype(pcwl_p, infp->wldp_buf); if (err == ENOTSUP) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_NOTSUPPORTED; goto done; } outfp->wldp_result = WL_SUCCESS; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } static int pcwl_cfg_phy(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t i; wldp_t *infp; wldp_t *outfp; char *buf; int iret; int err = 0; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); infp = (wldp_t *)mp->b_rptr; outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_dsss_t); if (cmd == WLAN_GET_PARAM) { err = pcwl_get_phy(pcwl_p, outfp->wldp_buf); if (err == EIO) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_HW_ERROR; goto done; } outfp->wldp_result = WL_SUCCESS; } else if (cmd == WLAN_SET_PARAM) { err = pcwl_set_phy(pcwl_p, infp->wldp_buf); if (err == ENOTSUP) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_NOTSUPPORTED; goto done; } outfp->wldp_result = WL_SUCCESS; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } static int pcwl_cfg_desiredrates(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t rate; uint16_t i; wldp_t *infp; wldp_t *outfp; char *buf; int iret; int err = 0; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); infp = (wldp_t *)mp->b_rptr; if (cmd == WLAN_GET_PARAM) { err = pcwl_get_desrates(pcwl_p, outfp->wldp_buf); if (err == EIO || err == EINVAL) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_NOTSUPPORTED; goto done; } if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) { outfp->wldp_length = WIFI_BUF_OFFSET + offsetof(wl_rates_t, wl_rates_rates) + 1 * sizeof (char); } else { (void) pcwl_get_ltv(pcwl_p, 2, WL_RID_TX_RATE, &rate); switch (rate) { case WL_L_TX_RATE_FIX_1M: outfp->wldp_length = WIFI_BUF_OFFSET + offsetof(wl_rates_t, wl_rates_rates) + 1 * sizeof (char); break; case WL_L_TX_RATE_FIX_2M: outfp->wldp_length = WIFI_BUF_OFFSET + offsetof(wl_rates_t, wl_rates_rates) + 1 * sizeof (char); break; case WL_L_TX_RATE_AUTO_H: outfp->wldp_length = WIFI_BUF_OFFSET + offsetof(wl_rates_t, wl_rates_rates) + 4 * sizeof (char); break; case WL_L_TX_RATE_FIX_5M: outfp->wldp_length = WIFI_BUF_OFFSET + offsetof(wl_rates_t, wl_rates_rates) + 1 * sizeof (char); break; case WL_L_TX_RATE_FIX_11M: outfp->wldp_length = WIFI_BUF_OFFSET + offsetof(wl_rates_t, wl_rates_rates) + 1 * sizeof (char); break; case WL_L_TX_RATE_AUTO_L: outfp->wldp_length = WIFI_BUF_OFFSET + offsetof(wl_rates_t, wl_rates_rates) + 2 * sizeof (char); break; case WL_L_TX_RATE_AUTO_M: outfp->wldp_length = WIFI_BUF_OFFSET + offsetof(wl_rates_t, wl_rates_rates) + 3 * sizeof (char); break; default: break; } } outfp->wldp_result = WL_SUCCESS; } else if (cmd == WLAN_SET_PARAM) { err = pcwl_set_desrates(pcwl_p, infp->wldp_buf); if (err == EINVAL) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_NOTSUPPORTED; goto done; } if (err == ENOTSUP) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_LACK_FEATURE; goto done; } outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_SUCCESS; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } /*ARGSUSED*/ static int pcwl_cfg_supportrates(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t i; wldp_t *outfp; char *buf; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); if (cmd == WLAN_GET_PARAM) { pcwl_get_suprates(outfp->wldp_buf); outfp->wldp_length = WIFI_BUF_OFFSET + offsetof(wl_rates_t, wl_rates_rates) + 4 * sizeof (char); outfp->wldp_result = WL_SUCCESS; for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); kmem_free(buf, MAX_BUF_LEN); return (WL_SUCCESS); } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } } static int pcwl_cfg_powermode(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t i; wldp_t *infp; wldp_t *outfp; char *buf; int iret; int err = 0; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); infp = (wldp_t *)mp->b_rptr; outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_ps_mode_t); if (cmd == WLAN_GET_PARAM) { pcwl_get_powermode(pcwl_p, outfp->wldp_buf); outfp->wldp_result = WL_SUCCESS; } else if (cmd == WLAN_SET_PARAM) { err = pcwl_set_powermode(pcwl_p, infp->wldp_buf); if (err == ENOTSUP) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_NOTSUPPORTED; goto done; } outfp->wldp_result = WL_SUCCESS; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } static int pcwl_cfg_authmode(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t i; wldp_t *infp; wldp_t *outfp; char *buf; int iret; int err = 0; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); infp = (wldp_t *)mp->b_rptr; outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_authmode_t); if (cmd == WLAN_GET_PARAM) { pcwl_get_authmode(pcwl_p, outfp->wldp_buf); outfp->wldp_result = WL_SUCCESS; } else if (cmd == WLAN_SET_PARAM) { err = pcwl_set_authmode(pcwl_p, infp->wldp_buf); if (err == ENOTSUP) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_NOTSUPPORTED; goto done; } outfp->wldp_result = WL_SUCCESS; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } static int pcwl_cfg_encryption(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t i; wldp_t *infp; wldp_t *outfp; char *buf; int iret; int err = 0; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); infp = (wldp_t *)mp->b_rptr; outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_encryption_t); if (cmd == WLAN_GET_PARAM) { pcwl_get_encrypt(pcwl_p, outfp->wldp_buf); outfp->wldp_result = WL_SUCCESS; } else if (cmd == WLAN_SET_PARAM) { err = pcwl_set_encrypt(pcwl_p, infp->wldp_buf); if (err == ENOTSUP) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_NOTSUPPORTED; goto done; } outfp->wldp_result = WL_SUCCESS; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } static int pcwl_cfg_wepkeyid(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t i, ret; pcwl_rf_t *rf_p; wldp_t *infp; wldp_t *outfp; char *buf; int iret; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); infp = (wldp_t *)mp->b_rptr; rf_p = &pcwl_p->pcwl_rf; outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_wep_key_id_t); if (cmd == WLAN_GET_PARAM) { *(wl_wep_key_id_t *)(outfp->wldp_buf) = rf_p->rf_tx_crypt_key; outfp->wldp_result = WL_SUCCESS; } else if (cmd == WLAN_SET_PARAM) { ret = (uint16_t)(*(wl_wep_key_id_t *)(infp->wldp_buf)); if (ret >= MAX_NWEPKEYS) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_NOTSUPPORTED; goto done; } rf_p->rf_tx_crypt_key = ret; outfp->wldp_result = WL_SUCCESS; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } static int pcwl_cfg_createibss(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t i; wldp_t *infp; wldp_t *outfp; char *buf; int iret; int err = 0; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); infp = (wldp_t *)mp->b_rptr; outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_create_ibss_t); if (cmd == WLAN_GET_PARAM) { pcwl_get_ibss(pcwl_p, outfp->wldp_buf); outfp->wldp_result = WL_SUCCESS; } else if (cmd == WLAN_SET_PARAM) { err = pcwl_set_ibss(pcwl_p, infp->wldp_buf); if (err == ENOTSUP) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_NOTSUPPORTED; goto done; } outfp->wldp_result = WL_SUCCESS; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } static int pcwl_cfg_rssi(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t i; int iret; wldp_t *outfp; char *buf; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_rssi_t); if (cmd == WLAN_GET_PARAM) { pcwl_get_param_rssi(pcwl_p, outfp->wldp_buf); outfp->wldp_result = WL_SUCCESS; } else if (cmd == WLAN_SET_PARAM) { outfp->wldp_result = WL_READONLY; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } /*ARGSUSED*/ static int pcwl_cfg_radio(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t i; int iret; wldp_t *outfp; char *buf; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); if (cmd == WLAN_GET_PARAM) { *(wl_radio_t *)(outfp->wldp_buf) = B_TRUE; outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_radio_t); outfp->wldp_result = WL_SUCCESS; } else if (cmd == WLAN_SET_PARAM) { outfp->wldp_length = WIFI_BUF_OFFSET; outfp->wldp_result = WL_LACK_FEATURE; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } static int pcwl_cfg_wepkey(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { uint16_t i; wldp_t *infp; wldp_t *outfp; char *buf; int iret; buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP); if (buf == NULL) { PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n", MAX_BUF_LEN)); return (ENOMEM); } outfp = (wldp_t *)buf; bcopy(mp->b_rptr, buf, sizeof (wldp_t)); infp = (wldp_t *)mp->b_rptr; outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_wep_key_tab_t); if (cmd == WLAN_GET_PARAM) { outfp->wldp_result = WL_WRITEONLY; } else if (cmd == WLAN_SET_PARAM) { (void) pcwl_set_wepkey(pcwl_p, infp->wldp_buf); outfp->wldp_result = WL_SUCCESS; } else { kmem_free(buf, MAX_BUF_LEN); return (EINVAL); } done: for (i = 0; i < (outfp->wldp_length); i++) (void) mi_mpprintf_putc((char *)mp, buf[i]); iret = (int)(outfp->wldp_result); kmem_free(buf, MAX_BUF_LEN); return (iret); } static void pcwl_connect_timeout(void *arg) { pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; uint16_t ret = 0; mutex_enter(&pcwl_p->pcwl_glock); PCWL_DISABLE_INTR(pcwl_p); if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) { goto done; } if (ret = pcwl_config_rf(pcwl_p)) { goto done; } if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) { goto done; } PCWL_ENABLE_INTR(pcwl_p); done: if (ret) cmn_err(CE_WARN, "pcwl: connect failed due to hardware error"); mutex_exit(&pcwl_p->pcwl_glock); pcwl_p->pcwl_connect_timeout_id = 0; } static int pcwl_getset(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd) { int ret = WL_SUCCESS; int connect = 0; mutex_enter(&pcwl_p->pcwl_glock); if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) { mutex_exit(&pcwl_p->pcwl_glock); return (PCWL_FAIL); } switch (((wldp_t *)mp->b_rptr)->wldp_id) { case WL_ESSID: ret = pcwl_cfg_essid(mp, pcwl_p, cmd); connect = 1; PCWLDBG((CE_NOTE, "cfg_essid\n")); break; case WL_BSSID: ret = pcwl_cfg_bssid(mp, pcwl_p, cmd); connect = 1; PCWLDBG((CE_NOTE, "cfg_bssid\n")); break; case WL_ESS_LIST: ret = pcwl_cfg_scan(mp, pcwl_p, cmd); PCWLDBG((CE_NOTE, "cfg_scan\n")); break; case WL_LINKSTATUS: ret = pcwl_cfg_linkstatus(mp, pcwl_p, cmd); PCWLDBG((CE_NOTE, "cfg_linkstatus\n")); break; case WL_BSS_TYPE: ret = pcwl_cfg_bsstype(mp, pcwl_p, cmd); connect = 1; PCWLDBG((CE_NOTE, "cfg_bsstype\n")); break; case WL_PHY_CONFIG: ret = pcwl_cfg_phy(mp, pcwl_p, cmd); connect = 1; PCWLDBG((CE_NOTE, "cfg_phy\n")); break; case WL_DESIRED_RATES: ret = pcwl_cfg_desiredrates(mp, pcwl_p, cmd); connect = 1; PCWLDBG((CE_NOTE, "cfg_disred-rates\n")); break; case WL_SUPPORTED_RATES: ret = pcwl_cfg_supportrates(mp, pcwl_p, cmd); PCWLDBG((CE_NOTE, "cfg_supported-rates\n")); break; case WL_POWER_MODE: ret = pcwl_cfg_powermode(mp, pcwl_p, cmd); PCWLDBG((CE_NOTE, "cfg_powermode\n")); break; case WL_AUTH_MODE: ret = pcwl_cfg_authmode(mp, pcwl_p, cmd); connect = 1; PCWLDBG((CE_NOTE, "cfg_authmode\n")); break; case WL_ENCRYPTION: ret = pcwl_cfg_encryption(mp, pcwl_p, cmd); connect = 1; PCWLDBG((CE_NOTE, "cfg_encryption\n")); break; case WL_WEP_KEY_ID: ret = pcwl_cfg_wepkeyid(mp, pcwl_p, cmd); connect = 1; PCWLDBG((CE_NOTE, "cfg_wepkeyid\n")); break; case WL_CREATE_IBSS: ret = pcwl_cfg_createibss(mp, pcwl_p, cmd); connect = 1; PCWLDBG((CE_NOTE, "cfg_create-ibss\n")); break; case WL_RSSI: ret = pcwl_cfg_rssi(mp, pcwl_p, cmd); PCWLDBG((CE_NOTE, "cfg_rssi\n")); break; case WL_RADIO: ret = pcwl_cfg_radio(mp, pcwl_p, cmd); PCWLDBG((CE_NOTE, "cfg_radio\n")); break; case WL_WEP_KEY_TAB: ret = pcwl_cfg_wepkey(mp, pcwl_p, cmd); connect = 1; PCWLDBG((CE_NOTE, "cfg_wepkey\n")); break; case WL_SCAN: mutex_exit(&pcwl_p->pcwl_glock); if (pcwl_p->pcwl_connect_timeout_id != 0) { (void) untimeout(pcwl_p->pcwl_connect_timeout_id); pcwl_p->pcwl_connect_timeout_id = 0; } mutex_enter(&pcwl_p->pcwl_glock); ret = pcwl_cmd_scan(pcwl_p); break; case WL_LOAD_DEFAULTS: mutex_exit(&pcwl_p->pcwl_glock); if (pcwl_p->pcwl_connect_timeout_id != 0) { (void) untimeout(pcwl_p->pcwl_connect_timeout_id); pcwl_p->pcwl_connect_timeout_id = 0; } mutex_enter(&pcwl_p->pcwl_glock); if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) { ret = (int)WL_HW_ERROR; break; } if (ret = pcwl_loaddef_rf(pcwl_p)) { ret = (int)WL_HW_ERROR; PCWLDBG((CE_WARN, "cfg_loaddef_err\n")); break; } if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) { ret = (int)WL_HW_ERROR; break; } pcwl_delay(pcwl_p, 1000000); if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) { ret = (int)WL_HW_ERROR; break; } PCWLDBG((CE_NOTE, "loaddef\n")); break; case WL_DISASSOCIATE: mutex_exit(&pcwl_p->pcwl_glock); if (pcwl_p->pcwl_connect_timeout_id != 0) { (void) untimeout(pcwl_p->pcwl_connect_timeout_id); pcwl_p->pcwl_connect_timeout_id = 0; } mutex_enter(&pcwl_p->pcwl_glock); if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) { ret = (int)WL_HW_ERROR; break; } /* * A workaround here: If the card is in ad-hoc mode, the * following scan will not work correctly, so any * 'dladm connect-wifi' which need a scan first will not * succeed. software reset the card here as a workround. */ if ((pcwl_p->pcwl_rf.rf_porttype == WL_BSS_IBSS) && (pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT)) { if (ret = pcwl_reset_backend(pcwl_p)) { ret = (int)WL_HW_ERROR; break; } if (ret = pcwl_init_nicmem(pcwl_p)) { ret = (int)WL_HW_ERROR; break; } pcwl_start_locked(pcwl_p); } if (ret = pcwl_loaddef_rf(pcwl_p)) { ret = (int)WL_HW_ERROR; PCWLDBG((CE_WARN, "cfg_loaddef_err\n")); break; } if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) { ret = (int)WL_HW_ERROR; break; } pcwl_delay(pcwl_p, 1000000); if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) { ret = (int)WL_HW_ERROR; break; } PCWLDBG((CE_NOTE, "disassociate\n")); break; case WL_REASSOCIATE: case WL_ASSOCIAT: mutex_exit(&pcwl_p->pcwl_glock); if (pcwl_p->pcwl_connect_timeout_id != 0) { (void) untimeout(pcwl_p->pcwl_connect_timeout_id); pcwl_p->pcwl_connect_timeout_id = 0; } mutex_enter(&pcwl_p->pcwl_glock); if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) { ret = (int)WL_HW_ERROR; break; } if (ret = pcwl_config_rf(pcwl_p)) { ret = (int)WL_HW_ERROR; break; } if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) { ret = (int)WL_HW_ERROR; break; } PCWLDBG((CE_NOTE, "associate")); break; default: break; } mutex_exit(&pcwl_p->pcwl_glock); if ((cmd == WLAN_SET_PARAM) && (connect)) { (void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0); if (pcwl_p->pcwl_connect_timeout_id != 0) { (void) untimeout(pcwl_p->pcwl_connect_timeout_id); pcwl_p->pcwl_connect_timeout_id = 0; } pcwl_p->pcwl_connect_timeout_id = timeout(pcwl_connect_timeout, pcwl_p, 2 * drv_usectohz(1000000)); } return (ret); } static void pcwl_wlan_ioctl(pcwl_maci_t *pcwl_p, queue_t *wq, mblk_t *mp, uint32_t cmd) { struct iocblk *iocp = (struct iocblk *)mp->b_rptr; wldp_t *infp; uint32_t len, ret; mblk_t *mp1; /* * sanity check */ if (iocp->ioc_count == 0 || !(mp1 = mp->b_cont)) { miocnak(wq, mp, 0, EINVAL); return; } /* * assuming single data block */ if (mp1->b_cont) { freemsg(mp1->b_cont); mp1->b_cont = NULL; } /* * we will overwrite everything */ mp1->b_wptr = mp1->b_rptr; infp = (wldp_t *)mp1->b_rptr; PCWLDBG((CE_NOTE, "pcwl: wldp->length=0x%x\n", infp->wldp_length)); PCWLDBG((CE_NOTE, "pcwl: wldp->type =:%s\n", infp->wldp_type == NET_802_11 ? "NET_802_11" : "Unknown")); PCWLDBG((CE_NOTE, "pcwl: wldp->id=0x%x\n", infp->wldp_id)); PCWLDBG((CE_NOTE, "pcwl: wldp->result=0x%x\n", infp->wldp_result)); ret = pcwl_getset(mp1, pcwl_p, cmd); len = msgdsize(mp1); PCWLDBG((CE_CONT, "pcwl: ioctl message length = %d\n", len)); miocack(wq, mp, len, ret); } static void pcwl_ioctl(void *arg, queue_t *wq, mblk_t *mp) { struct iocblk *iocp; uint32_t cmd, ret; pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; boolean_t need_privilege = B_TRUE; /* * Validate the command before bothering with the mutexen ... */ iocp = (struct iocblk *)mp->b_rptr; iocp->ioc_error = 0; cmd = iocp->ioc_cmd; switch (cmd) { default: PCWLDBG((CE_CONT, "pcwl_ioctl: unknown cmd 0x%x", cmd)); miocnak(wq, mp, 0, EINVAL); return; case WLAN_GET_PARAM: need_privilege = B_FALSE; break; case WLAN_SET_PARAM: case WLAN_COMMAND: break; } if (need_privilege && (ret = secpolicy_dl_config(iocp->ioc_cr)) != 0) miocnak(wq, mp, 0, ret); else pcwl_wlan_ioctl(pcwl_p, wq, mp, cmd); } /* * brussels */ /* ARGSUSED */ static int pcwl_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf) { int err = 0; pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; mutex_enter(&pcwl_p->pcwl_glock); if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) { mutex_exit(&pcwl_p->pcwl_glock); err = EINVAL; return (err); } switch (wldp_pr_num) { /* mac_prop_id */ case MAC_PROP_WL_ESSID: err = pcwl_set_essid(pcwl_p, wldp_buf); break; case MAC_PROP_WL_PHY_CONFIG: err = pcwl_set_phy(pcwl_p, wldp_buf); break; case MAC_PROP_WL_KEY_TAB: err = pcwl_set_wepkey(pcwl_p, wldp_buf); break; case MAC_PROP_WL_AUTH_MODE: err = pcwl_set_authmode(pcwl_p, wldp_buf); break; case MAC_PROP_WL_ENCRYPTION: err = pcwl_set_encrypt(pcwl_p, wldp_buf); break; case MAC_PROP_WL_BSSTYPE: err = pcwl_set_bsstype(pcwl_p, wldp_buf); break; case MAC_PROP_WL_DESIRED_RATES: err = pcwl_set_desrates(pcwl_p, wldp_buf); break; case MAC_PROP_WL_POWER_MODE: err = pcwl_set_powermode(pcwl_p, wldp_buf); break; case MAC_PROP_WL_CREATE_IBSS: err = pcwl_set_ibss(pcwl_p, wldp_buf); break; case MAC_PROP_WL_BSSID: case MAC_PROP_WL_RADIO: case MAC_PROP_WL_WPA: case MAC_PROP_WL_KEY: case MAC_PROP_WL_DELKEY: case MAC_PROP_WL_SETOPTIE: case MAC_PROP_WL_MLME: case MAC_PROP_WL_LINKSTATUS: case MAC_PROP_WL_ESS_LIST: case MAC_PROP_WL_SUPPORTED_RATES: case MAC_PROP_WL_RSSI: case MAC_PROP_WL_CAPABILITY: case MAC_PROP_WL_SCANRESULTS: cmn_err(CE_WARN, "pcwl_setprop:" "opmode not support\n"); err = ENOTSUP; break; default: cmn_err(CE_WARN, "pcwl_setprop:" "opmode err\n"); err = EINVAL; break; } mutex_exit(&pcwl_p->pcwl_glock); if (err == ENETRESET) { (void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0); if (pcwl_p->pcwl_connect_timeout_id != 0) { (void) untimeout(pcwl_p->pcwl_connect_timeout_id); pcwl_p->pcwl_connect_timeout_id = 0; } pcwl_p->pcwl_connect_timeout_id = timeout(pcwl_connect_timeout, pcwl_p, 2 * drv_usectohz(1000000)); err = 0; } return (err); } /* ARGSUSED */ /* ARGSUSED */ static int pcwl_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf) { int err = 0; pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg; mutex_enter(&pcwl_p->pcwl_glock); if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) { mutex_exit(&pcwl_p->pcwl_glock); err = EINVAL; return (err); } switch (wldp_pr_num) { /* mac_prop_id */ case MAC_PROP_WL_ESSID: err = pcwl_get_essid(pcwl_p, wldp_buf); break; case MAC_PROP_WL_BSSID: err = pcwl_get_bssid(pcwl_p, wldp_buf); break; case MAC_PROP_WL_PHY_CONFIG: err = pcwl_get_phy(pcwl_p, wldp_buf); break; case MAC_PROP_WL_AUTH_MODE: pcwl_get_authmode(pcwl_p, wldp_buf); break; case MAC_PROP_WL_ENCRYPTION: pcwl_get_encrypt(pcwl_p, wldp_buf); break; case MAC_PROP_WL_BSSTYPE: pcwl_get_bsstype(pcwl_p, wldp_buf); break; case MAC_PROP_WL_LINKSTATUS: err = pcwl_get_linkstatus(pcwl_p, wldp_buf); break; case MAC_PROP_WL_ESS_LIST: pcwl_get_esslist(pcwl_p, wldp_buf); break; case MAC_PROP_WL_SUPPORTED_RATES: pcwl_get_suprates(wldp_buf); break; case MAC_PROP_WL_RSSI: pcwl_get_param_rssi(pcwl_p, wldp_buf); break; case MAC_PROP_WL_RADIO: pcwl_get_radio(wldp_buf); break; case MAC_PROP_WL_POWER_MODE: pcwl_get_powermode(pcwl_p, wldp_buf); break; case MAC_PROP_WL_CREATE_IBSS: pcwl_get_ibss(pcwl_p, wldp_buf); break; case MAC_PROP_WL_DESIRED_RATES: err = pcwl_get_desrates(pcwl_p, wldp_buf); break; case MAC_PROP_WL_CAPABILITY: case MAC_PROP_WL_WPA: case MAC_PROP_WL_SCANRESULTS: case MAC_PROP_WL_KEY_TAB: case MAC_PROP_WL_KEY: case MAC_PROP_WL_DELKEY: case MAC_PROP_WL_SETOPTIE: case MAC_PROP_WL_MLME: cmn_err(CE_WARN, "pcwl_getprop:" "opmode not support\n"); err = ENOTSUP; break; default: cmn_err(CE_WARN, "pcwl_getprop:" "opmode err\n"); err = EINVAL; break; } mutex_exit(&pcwl_p->pcwl_glock); return (err); } static void pcwl_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wlpd_pr_num, mac_prop_info_handle_t prh) { _NOTE(ARGUNUSED(arg, pr_name)); switch (wlpd_pr_num) { case MAC_PROP_WL_LINKSTATUS: case MAC_PROP_WL_ESS_LIST: case MAC_PROP_WL_SUPPORTED_RATES: case MAC_PROP_WL_RSSI: mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); } } /* * quiesce(9E) entry point. * * This function is called when the system is single-threaded at high * PIL with preemption disabled. Therefore, this function must not be * blocked. * * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. * DDI_FAILURE indicates an error condition and should almost never happen. */ #ifndef __sparc static int pcwl_quiesce(dev_info_t *dip) { pcwl_maci_t *pcwl_p; pcwl_p = ddi_get_soft_state(pcwl_soft_state_p, ddi_get_instance(dip)); if (pcwl_p == NULL) return (DDI_FAILURE); if (pcwl_p->pcwl_flag & PCWL_CARD_READY) pcwl_stop_locked(pcwl_p); return (DDI_SUCCESS); } #endif