Index: user/thompsa/vaptq/sys/dev/if_ndis/if_ndis.c =================================================================== --- user/thompsa/vaptq/sys/dev/if_ndis/if_ndis.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/if_ndis/if_ndis.c (revision 185733) @@ -1,3450 +1,3422 @@ /*- * Copyright (c) 2003 * 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. * * WPA support originally contributed by Arvind Srinivasan * then hacked upon mercilessly by my. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NDIS_DEBUG #ifdef NDIS_DEBUG #define DPRINTF(x) do { if (ndis_debug > 0) printf x; } while (0) int ndis_debug = 0; SYSCTL_INT(_debug, OID_AUTO, ndis, CTLFLAG_RW, &ndis_debug, 0, "if_ndis debug level"); #else #define DPRINTF(x) #endif MODULE_DEPEND(ndis, ether, 1, 1, 1); MODULE_DEPEND(ndis, wlan, 1, 1, 1); MODULE_DEPEND(ndis, ndisapi, 1, 1, 1); MODULE_VERSION(ndis, 1); int ndis_attach (device_t); int ndis_detach (device_t); int ndis_suspend (device_t); int ndis_resume (device_t); void ndis_shutdown (device_t); int ndisdrv_modevent (module_t, int, void *); static void ndis_txeof (ndis_handle, ndis_packet *, ndis_status); static void ndis_rxeof (ndis_handle, ndis_packet **, uint32_t); static void ndis_rxeof_eth (ndis_handle, ndis_handle, char *, void *, uint32_t, void *, uint32_t, uint32_t); static void ndis_rxeof_done (ndis_handle); static void ndis_rxeof_xfr (kdpc *, ndis_handle, void *, void *); static void ndis_rxeof_xfr_done (ndis_handle, ndis_packet *, uint32_t, uint32_t); static void ndis_linksts (ndis_handle, ndis_status, void *, uint32_t); static void ndis_linksts_done (ndis_handle); /* We need to wrap these functions for amd64. */ static funcptr ndis_txeof_wrap; static funcptr ndis_rxeof_wrap; static funcptr ndis_rxeof_eth_wrap; static funcptr ndis_rxeof_done_wrap; static funcptr ndis_rxeof_xfr_wrap; static funcptr ndis_rxeof_xfr_done_wrap; static funcptr ndis_linksts_wrap; static funcptr ndis_linksts_done_wrap; static funcptr ndis_ticktask_wrap; static funcptr ndis_starttask_wrap; static funcptr ndis_resettask_wrap; static funcptr ndis_inputtask_wrap; static struct ieee80211vap *ndis_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void ndis_vap_delete (struct ieee80211vap *); static void ndis_tick (void *); static void ndis_ticktask (device_object *, void *); static int ndis_raw_xmit (struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void ndis_update_mcast (struct ifnet *ifp); static void ndis_update_promisc (struct ifnet *ifp); static void ndis_start (struct ifnet *); static void ndis_starttask (device_object *, void *); static void ndis_resettask (device_object *, void *); static void ndis_inputtask (device_object *, void *); static int ndis_ioctl (struct ifnet *, u_long, caddr_t); static int ndis_ioctl_80211 (struct ifnet *, u_long, caddr_t); static int ndis_newstate (struct ieee80211vap *, enum ieee80211_state, int); static int ndis_nettype_chan (uint32_t); static int ndis_nettype_mode (uint32_t); static void ndis_scan (void *, int); static void ndis_scan_results (struct ndis_softc *); static void ndis_scan_start (struct ieee80211com *); static void ndis_scan_end (struct ieee80211com *); static void ndis_set_channel (struct ieee80211com *); static void ndis_scan_curchan (struct ieee80211_scan_state *, unsigned long); static void ndis_scan_mindwell (struct ieee80211_scan_state *); static void ndis_init (void *); static void ndis_stop (struct ndis_softc *); static int ndis_ifmedia_upd (struct ifnet *); static void ndis_ifmedia_sts (struct ifnet *, struct ifmediareq *); -static void ndis_auth (void *, int); -static void ndis_assoc (void *, int); static int ndis_get_assoc (struct ndis_softc *, ndis_wlan_bssid_ex **); static int ndis_probe_offload (struct ndis_softc *); static int ndis_set_offload (struct ndis_softc *); static void ndis_getstate_80211 (struct ndis_softc *); static void ndis_setstate_80211 (struct ndis_softc *); static void ndis_auth_and_assoc (struct ndis_softc *, struct ieee80211vap *); static int ndis_set_cipher (struct ndis_softc *, int); static int ndis_set_wpa (struct ndis_softc *, void *, int); static int ndis_add_key (struct ieee80211vap *, const struct ieee80211_key *, const u_int8_t []); static int ndis_del_key (struct ieee80211vap *, const struct ieee80211_key *); static void ndis_setmulti (struct ndis_softc *); static void ndis_map_sclist (void *, bus_dma_segment_t *, int, bus_size_t, int); static int ndisdrv_loaded = 0; /* * This routine should call windrv_load() once for each driver * image. This will do the relocation and dynalinking for the * image, and create a Windows driver object which will be * saved in our driver database. */ int ndisdrv_modevent(mod, cmd, arg) module_t mod; int cmd; void *arg; { int error = 0; switch (cmd) { case MOD_LOAD: ndisdrv_loaded++; if (ndisdrv_loaded > 1) break; windrv_wrap((funcptr)ndis_rxeof, &ndis_rxeof_wrap, 3, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_rxeof_eth, &ndis_rxeof_eth_wrap, 8, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_rxeof_done, &ndis_rxeof_done_wrap, 1, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_rxeof_xfr, &ndis_rxeof_xfr_wrap, 4, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_rxeof_xfr_done, &ndis_rxeof_xfr_done_wrap, 4, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_txeof, &ndis_txeof_wrap, 3, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_linksts, &ndis_linksts_wrap, 4, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_linksts_done, &ndis_linksts_done_wrap, 1, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_ticktask, &ndis_ticktask_wrap, 2, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_starttask, &ndis_starttask_wrap, 2, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_resettask, &ndis_resettask_wrap, 2, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_inputtask, &ndis_inputtask_wrap, 2, WINDRV_WRAP_STDCALL); break; case MOD_UNLOAD: ndisdrv_loaded--; if (ndisdrv_loaded > 0) break; /* fallthrough */ case MOD_SHUTDOWN: windrv_unwrap(ndis_rxeof_wrap); windrv_unwrap(ndis_rxeof_eth_wrap); windrv_unwrap(ndis_rxeof_done_wrap); windrv_unwrap(ndis_rxeof_xfr_wrap); windrv_unwrap(ndis_rxeof_xfr_done_wrap); windrv_unwrap(ndis_txeof_wrap); windrv_unwrap(ndis_linksts_wrap); windrv_unwrap(ndis_linksts_done_wrap); windrv_unwrap(ndis_ticktask_wrap); windrv_unwrap(ndis_starttask_wrap); windrv_unwrap(ndis_resettask_wrap); windrv_unwrap(ndis_inputtask_wrap); break; default: error = EINVAL; break; } return (error); } /* * Program the 64-bit multicast hash filter. */ static void ndis_setmulti(sc) struct ndis_softc *sc; { struct ifnet *ifp; struct ifmultiaddr *ifma; int len, mclistsz, error; uint8_t *mclist; ifp = sc->ifp; if (!NDIS_INITIALIZED(sc)) return; if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; len = sizeof(sc->ndis_filter); error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, &sc->ndis_filter, &len); if (error) device_printf (sc->ndis_dev, "set allmulti failed: %d\n", error); return; } if (TAILQ_EMPTY(&ifp->if_multiaddrs)) return; len = sizeof(mclistsz); ndis_get_info(sc, OID_802_3_MAXIMUM_LIST_SIZE, &mclistsz, &len); mclist = malloc(ETHER_ADDR_LEN * mclistsz, M_TEMP, M_NOWAIT|M_ZERO); if (mclist == NULL) { sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; goto out; } sc->ndis_filter |= NDIS_PACKET_TYPE_MULTICAST; len = 0; IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), mclist + (ETHER_ADDR_LEN * len), ETHER_ADDR_LEN); len++; if (len > mclistsz) { IF_ADDR_UNLOCK(ifp); sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; sc->ndis_filter &= ~NDIS_PACKET_TYPE_MULTICAST; goto out; } } IF_ADDR_UNLOCK(ifp); len = len * ETHER_ADDR_LEN; error = ndis_set_info(sc, OID_802_3_MULTICAST_LIST, mclist, &len); if (error) { device_printf (sc->ndis_dev, "set mclist failed: %d\n", error); sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; sc->ndis_filter &= ~NDIS_PACKET_TYPE_MULTICAST; } out: free(mclist, M_TEMP); len = sizeof(sc->ndis_filter); error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, &sc->ndis_filter, &len); if (error) device_printf (sc->ndis_dev, "set multi failed: %d\n", error); return; } static int ndis_set_offload(sc) struct ndis_softc *sc; { ndis_task_offload *nto; ndis_task_offload_hdr *ntoh; ndis_task_tcpip_csum *nttc; struct ifnet *ifp; int len, error; ifp = sc->ifp; if (!NDIS_INITIALIZED(sc)) return(EINVAL); /* See if there's anything to set. */ error = ndis_probe_offload(sc); if (error) return(error); if (sc->ndis_hwassist == 0 && ifp->if_capabilities == 0) return(0); len = sizeof(ndis_task_offload_hdr) + sizeof(ndis_task_offload) + sizeof(ndis_task_tcpip_csum); ntoh = malloc(len, M_TEMP, M_NOWAIT|M_ZERO); if (ntoh == NULL) return(ENOMEM); ntoh->ntoh_vers = NDIS_TASK_OFFLOAD_VERSION; ntoh->ntoh_len = sizeof(ndis_task_offload_hdr); ntoh->ntoh_offset_firsttask = sizeof(ndis_task_offload_hdr); ntoh->ntoh_encapfmt.nef_encaphdrlen = sizeof(struct ether_header); ntoh->ntoh_encapfmt.nef_encap = NDIS_ENCAP_IEEE802_3; ntoh->ntoh_encapfmt.nef_flags = NDIS_ENCAPFLAG_FIXEDHDRLEN; nto = (ndis_task_offload *)((char *)ntoh + ntoh->ntoh_offset_firsttask); nto->nto_vers = NDIS_TASK_OFFLOAD_VERSION; nto->nto_len = sizeof(ndis_task_offload); nto->nto_task = NDIS_TASK_TCPIP_CSUM; nto->nto_offset_nexttask = 0; nto->nto_taskbuflen = sizeof(ndis_task_tcpip_csum); nttc = (ndis_task_tcpip_csum *)nto->nto_taskbuf; if (ifp->if_capenable & IFCAP_TXCSUM) nttc->nttc_v4tx = sc->ndis_v4tx; if (ifp->if_capenable & IFCAP_RXCSUM) nttc->nttc_v4rx = sc->ndis_v4rx; error = ndis_set_info(sc, OID_TCP_TASK_OFFLOAD, ntoh, &len); free(ntoh, M_TEMP); return(error); } static int ndis_probe_offload(sc) struct ndis_softc *sc; { ndis_task_offload *nto; ndis_task_offload_hdr *ntoh; ndis_task_tcpip_csum *nttc = NULL; struct ifnet *ifp; int len, error, dummy; ifp = sc->ifp; len = sizeof(dummy); error = ndis_get_info(sc, OID_TCP_TASK_OFFLOAD, &dummy, &len); if (error != ENOSPC) return(error); ntoh = malloc(len, M_TEMP, M_NOWAIT|M_ZERO); if (ntoh == NULL) return(ENOMEM); ntoh->ntoh_vers = NDIS_TASK_OFFLOAD_VERSION; ntoh->ntoh_len = sizeof(ndis_task_offload_hdr); ntoh->ntoh_encapfmt.nef_encaphdrlen = sizeof(struct ether_header); ntoh->ntoh_encapfmt.nef_encap = NDIS_ENCAP_IEEE802_3; ntoh->ntoh_encapfmt.nef_flags = NDIS_ENCAPFLAG_FIXEDHDRLEN; error = ndis_get_info(sc, OID_TCP_TASK_OFFLOAD, ntoh, &len); if (error) { free(ntoh, M_TEMP); return(error); } if (ntoh->ntoh_vers != NDIS_TASK_OFFLOAD_VERSION) { free(ntoh, M_TEMP); return(EINVAL); } nto = (ndis_task_offload *)((char *)ntoh + ntoh->ntoh_offset_firsttask); while (1) { switch (nto->nto_task) { case NDIS_TASK_TCPIP_CSUM: nttc = (ndis_task_tcpip_csum *)nto->nto_taskbuf; break; /* Don't handle these yet. */ case NDIS_TASK_IPSEC: case NDIS_TASK_TCP_LARGESEND: default: break; } if (nto->nto_offset_nexttask == 0) break; nto = (ndis_task_offload *)((char *)nto + nto->nto_offset_nexttask); } if (nttc == NULL) { free(ntoh, M_TEMP); return(ENOENT); } sc->ndis_v4tx = nttc->nttc_v4tx; sc->ndis_v4rx = nttc->nttc_v4rx; if (nttc->nttc_v4tx & NDIS_TCPSUM_FLAGS_IP_CSUM) sc->ndis_hwassist |= CSUM_IP; if (nttc->nttc_v4tx & NDIS_TCPSUM_FLAGS_TCP_CSUM) sc->ndis_hwassist |= CSUM_TCP; if (nttc->nttc_v4tx & NDIS_TCPSUM_FLAGS_UDP_CSUM) sc->ndis_hwassist |= CSUM_UDP; if (sc->ndis_hwassist) ifp->if_capabilities |= IFCAP_TXCSUM; if (nttc->nttc_v4rx & NDIS_TCPSUM_FLAGS_IP_CSUM) ifp->if_capabilities |= IFCAP_RXCSUM; if (nttc->nttc_v4rx & NDIS_TCPSUM_FLAGS_TCP_CSUM) ifp->if_capabilities |= IFCAP_RXCSUM; if (nttc->nttc_v4rx & NDIS_TCPSUM_FLAGS_UDP_CSUM) ifp->if_capabilities |= IFCAP_RXCSUM; free(ntoh, M_TEMP); return(0); } static int ndis_nettype_chan(uint32_t type) { switch (type) { case NDIS_80211_NETTYPE_11FH: return (IEEE80211_CHAN_FHSS); case NDIS_80211_NETTYPE_11DS: return (IEEE80211_CHAN_B); case NDIS_80211_NETTYPE_11OFDM5: return (IEEE80211_CHAN_A); case NDIS_80211_NETTYPE_11OFDM24: return (IEEE80211_CHAN_G); } DPRINTF(("unknown channel nettype %d\n", type)); return (IEEE80211_CHAN_B); /* Default to 11B chan */ } static int ndis_nettype_mode(uint32_t type) { switch (type) { case NDIS_80211_NETTYPE_11FH: return (IEEE80211_MODE_FH); case NDIS_80211_NETTYPE_11DS: return (IEEE80211_MODE_11B); case NDIS_80211_NETTYPE_11OFDM5: return (IEEE80211_MODE_11A); case NDIS_80211_NETTYPE_11OFDM24: return (IEEE80211_MODE_11G); } DPRINTF(("unknown mode nettype %d\n", type)); return (IEEE80211_MODE_AUTO); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ int ndis_attach(dev) device_t dev; { u_char eaddr[ETHER_ADDR_LEN]; struct ndis_softc *sc; driver_object *pdrv; device_object *pdo; struct ifnet *ifp = NULL; int error = 0, len, mode; uint8_t bands = 0; int i; sc = device_get_softc(dev); mtx_init(&sc->ndis_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); KeInitializeSpinLock(&sc->ndis_rxlock); InitializeListHead(&sc->ndis_shlist); callout_init(&sc->ndis_stat_callout, CALLOUT_MPSAFE); if (sc->ndis_iftype == PCMCIABus) { error = ndis_alloc_amem(sc); if (error) { device_printf(dev, "failed to allocate " "attribute memory\n"); goto fail; } } /* Create sysctl registry nodes */ ndis_create_sysctls(sc); /* Find the PDO for this device instance. */ if (sc->ndis_iftype == PCIBus) pdrv = windrv_lookup(0, "PCI Bus"); else if (sc->ndis_iftype == PCMCIABus) pdrv = windrv_lookup(0, "PCCARD Bus"); else pdrv = windrv_lookup(0, "USB Bus"); pdo = windrv_find_pdo(pdrv, dev); /* * Create a new functional device object for this * device. This is what creates the miniport block * for this device instance. */ if (NdisAddDevice(sc->ndis_dobj, pdo) != STATUS_SUCCESS) { device_printf(dev, "failed to create FDO!\n"); error = ENXIO; goto fail; } /* Tell the user what version of the API the driver is using. */ device_printf(dev, "NDIS API version: %d.%d\n", sc->ndis_chars->nmc_version_major, sc->ndis_chars->nmc_version_minor); /* Do resource conversion. */ if (sc->ndis_iftype == PCMCIABus || sc->ndis_iftype == PCIBus) ndis_convert_res(sc); else sc->ndis_block->nmb_rlist = NULL; /* Install our RX and TX interrupt handlers. */ sc->ndis_block->nmb_senddone_func = ndis_txeof_wrap; sc->ndis_block->nmb_pktind_func = ndis_rxeof_wrap; sc->ndis_block->nmb_ethrxindicate_func = ndis_rxeof_eth_wrap; sc->ndis_block->nmb_ethrxdone_func = ndis_rxeof_done_wrap; sc->ndis_block->nmb_tdcond_func = ndis_rxeof_xfr_done_wrap; /* Override the status handler so we can detect link changes. */ sc->ndis_block->nmb_status_func = ndis_linksts_wrap; sc->ndis_block->nmb_statusdone_func = ndis_linksts_done_wrap; /* Set up work item handlers. */ sc->ndis_tickitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); sc->ndis_startitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); sc->ndis_resetitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); sc->ndis_inputitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); KeInitializeDpc(&sc->ndis_rxdpc, ndis_rxeof_xfr_wrap, sc->ndis_block); /* Call driver's init routine. */ if (ndis_init_nic(sc)) { device_printf (dev, "init handler failed\n"); error = ENXIO; goto fail; } /* * Get station address from the driver. */ len = sizeof(eaddr); ndis_get_info(sc, OID_802_3_CURRENT_ADDRESS, &eaddr, &len); /* * Figure out how big to make the TX buffer pool. */ len = sizeof(sc->ndis_maxpkts); if (ndis_get_info(sc, OID_GEN_MAXIMUM_SEND_PACKETS, &sc->ndis_maxpkts, &len)) { device_printf (dev, "failed to get max TX packets\n"); error = ENXIO; goto fail; } /* * If this is a deserialized miniport, we don't have * to honor the OID_GEN_MAXIMUM_SEND_PACKETS result. */ if (!NDIS_SERIALIZED(sc->ndis_block)) sc->ndis_maxpkts = NDIS_TXPKTS; /* Enforce some sanity, just in case. */ if (sc->ndis_maxpkts == 0) sc->ndis_maxpkts = 10; sc->ndis_txarray = malloc(sizeof(ndis_packet *) * sc->ndis_maxpkts, M_DEVBUF, M_NOWAIT|M_ZERO); /* Allocate a pool of ndis_packets for TX encapsulation. */ NdisAllocatePacketPool(&i, &sc->ndis_txpool, sc->ndis_maxpkts, PROTOCOL_RESERVED_SIZE_IN_PACKET); if (i != NDIS_STATUS_SUCCESS) { sc->ndis_txpool = NULL; device_printf(dev, "failed to allocate TX packet pool"); error = ENOMEM; goto fail; } sc->ndis_txpending = sc->ndis_maxpkts; sc->ndis_oidcnt = 0; /* Get supported oid list. */ ndis_get_supported_oids(sc, &sc->ndis_oids, &sc->ndis_oidcnt); /* If the NDIS module requested scatter/gather, init maps. */ if (sc->ndis_sc) ndis_init_dma(sc); /* * See if the OID_802_11_CONFIGURATION OID is * supported by this driver. If it is, then this an 802.11 * wireless driver, and we should set up media for wireless. */ for (i = 0; i < sc->ndis_oidcnt; i++) { if (sc->ndis_oids[i] == OID_802_11_CONFIGURATION) { sc->ndis_80211++; break; } } if (sc->ndis_80211) ifp = if_alloc(IFT_IEEE80211); else ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { error = ENOSPC; goto fail; } sc->ifp = ifp; ifp->if_softc = sc; /* Check for task offload support. */ ndis_probe_offload(sc); if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = ndis_ioctl; ifp->if_start = ndis_start; ifp->if_init = ndis_init; ifp->if_baudrate = 10000000; IFQ_SET_MAXLEN(&ifp->if_snd, 50); ifp->if_snd.ifq_drv_maxlen = 25; IFQ_SET_READY(&ifp->if_snd); ifp->if_capenable = ifp->if_capabilities; ifp->if_hwassist = sc->ndis_hwassist; /* Do media setup */ if (sc->ndis_80211) { struct ieee80211com *ic = ifp->if_l2com; ndis_80211_rates_ex rates; struct ndis_80211_nettype_list *ntl; uint32_t arg; int r; sc->ndis_tq = taskqueue_create("nids_taskq", M_NOWAIT | M_ZERO, taskqueue_thread_enqueue, &sc->ndis_tq); taskqueue_start_threads(&sc->ndis_tq, 1, PI_NET, "%s taskq", device_get_nameunit(dev)); TASK_INIT(&sc->ndis_scantask, 0, ndis_scan, sc); - TASK_INIT(&sc->ndis_authtask, 0, ndis_auth, sc); - TASK_INIT(&sc->ndis_assoctask, 0, ndis_assoc, sc); ifp->if_ioctl = ndis_ioctl_80211; ic->ic_ifp = ifp; ic->ic_opmode = IEEE80211_M_STA; ic->ic_phytype = IEEE80211_T_DS; ic->ic_caps = IEEE80211_C_STA | IEEE80211_C_IBSS; setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); len = 0; r = ndis_get_info(sc, OID_802_11_NETWORK_TYPES_SUPPORTED, NULL, &len); if (r != ENOSPC) goto nonettypes; ntl = malloc(len, M_DEVBUF, M_NOWAIT|M_ZERO); r = ndis_get_info(sc, OID_802_11_NETWORK_TYPES_SUPPORTED, ntl, &len); if (r != 0) { free(ntl, M_DEVBUF); goto nonettypes; } for (i = 0; i < ntl->ntl_items; i++) { mode = ndis_nettype_mode(ntl->ntl_type[i]); if (mode) { setbit(ic->ic_modecaps, mode); setbit(&bands, mode); } else device_printf(dev, "Unknown nettype %d\n", ntl->ntl_type[i]); } free(ntl, M_DEVBUF); nonettypes: /* Default to 11b channels if the card did not supply any */ if (bands == 0) { setbit(ic->ic_modecaps, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11B); } len = sizeof(rates); bzero((char *)&rates, len); r = ndis_get_info(sc, OID_802_11_SUPPORTED_RATES, (void *)rates, &len); if (r) device_printf (dev, "get rates failed: 0x%x\n", r); /* * Since the supported rates only up to 8 can be supported, * if this is not 802.11b we're just going to be faking it * all up to heck. */ #define TESTSETRATE(x, y) \ do { \ int i; \ for (i = 0; i < ic->ic_sup_rates[x].rs_nrates; i++) { \ if (ic->ic_sup_rates[x].rs_rates[i] == (y)) \ break; \ } \ if (i == ic->ic_sup_rates[x].rs_nrates) { \ ic->ic_sup_rates[x].rs_rates[i] = (y); \ ic->ic_sup_rates[x].rs_nrates++; \ } \ } while (0) #define SETRATE(x, y) \ ic->ic_sup_rates[x].rs_rates[ic->ic_sup_rates[x].rs_nrates] = (y) #define INCRATE(x) \ ic->ic_sup_rates[x].rs_nrates++ ic->ic_curmode = IEEE80211_MODE_AUTO; if (isset(ic->ic_modecaps, IEEE80211_MODE_11A)) ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates = 0; if (isset(ic->ic_modecaps, IEEE80211_MODE_11B)) ic->ic_sup_rates[IEEE80211_MODE_11B].rs_nrates = 0; if (isset(ic->ic_modecaps, IEEE80211_MODE_11G)) ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates = 0; for (i = 0; i < len; i++) { switch (rates[i] & IEEE80211_RATE_VAL) { case 2: case 4: case 11: case 10: case 22: if (isclr(ic->ic_modecaps, IEEE80211_MODE_11B)) { /* Lazy-init 802.11b. */ setbit(ic->ic_modecaps, IEEE80211_MODE_11B); ic->ic_sup_rates[IEEE80211_MODE_11B]. rs_nrates = 0; } SETRATE(IEEE80211_MODE_11B, rates[i]); INCRATE(IEEE80211_MODE_11B); break; default: if (isset(ic->ic_modecaps, IEEE80211_MODE_11A)) { SETRATE(IEEE80211_MODE_11A, rates[i]); INCRATE(IEEE80211_MODE_11A); } if (isset(ic->ic_modecaps, IEEE80211_MODE_11G)) { SETRATE(IEEE80211_MODE_11G, rates[i]); INCRATE(IEEE80211_MODE_11G); } break; } } /* * If the hardware supports 802.11g, it most * likely supports 802.11b and all of the * 802.11b and 802.11g speeds, so maybe we can * just cheat here. Just how in the heck do * we detect turbo modes, though? */ if (isset(ic->ic_modecaps, IEEE80211_MODE_11B)) { TESTSETRATE(IEEE80211_MODE_11B, IEEE80211_RATE_BASIC|2); TESTSETRATE(IEEE80211_MODE_11B, IEEE80211_RATE_BASIC|4); TESTSETRATE(IEEE80211_MODE_11B, IEEE80211_RATE_BASIC|11); TESTSETRATE(IEEE80211_MODE_11B, IEEE80211_RATE_BASIC|22); } if (isset(ic->ic_modecaps, IEEE80211_MODE_11G)) { TESTSETRATE(IEEE80211_MODE_11G, 47); TESTSETRATE(IEEE80211_MODE_11G, 72); TESTSETRATE(IEEE80211_MODE_11G, 96); TESTSETRATE(IEEE80211_MODE_11G, 108); } if (isset(ic->ic_modecaps, IEEE80211_MODE_11A)) { TESTSETRATE(IEEE80211_MODE_11A, 47); TESTSETRATE(IEEE80211_MODE_11A, 72); TESTSETRATE(IEEE80211_MODE_11A, 96); TESTSETRATE(IEEE80211_MODE_11A, 108); } #undef SETRATE #undef INCRATE ieee80211_init_channels(ic, NULL, &bands); /* * To test for WPA support, we need to see if we can * set AUTHENTICATION_MODE to WPA and read it back * successfully. */ i = sizeof(arg); arg = NDIS_80211_AUTHMODE_WPA; r = ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &i); if (r == 0) { r = ndis_get_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &i); if (r == 0 && arg == NDIS_80211_AUTHMODE_WPA) ic->ic_caps |= IEEE80211_C_WPA; } /* * To test for supported ciphers, we set each * available encryption type in descending order. * If ENC3 works, then we have WEP, TKIP and AES. * If only ENC2 works, then we have WEP and TKIP. * If only ENC1 works, then we have just WEP. */ i = sizeof(arg); arg = NDIS_80211_WEPSTAT_ENC3ENABLED; r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); if (r == 0) { ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_TKIP | IEEE80211_CRYPTO_AES_CCM; goto got_crypto; } arg = NDIS_80211_WEPSTAT_ENC2ENABLED; r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); if (r == 0) { ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_TKIP; goto got_crypto; } arg = NDIS_80211_WEPSTAT_ENC1ENABLED; r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); if (r == 0) ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP; got_crypto: i = sizeof(arg); r = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &i); if (r == 0) ic->ic_caps |= IEEE80211_C_PMGT; bcopy(eaddr, &ic->ic_myaddr, sizeof(eaddr)); ieee80211_ifattach(ic); ic->ic_raw_xmit = ndis_raw_xmit; ic->ic_scan_start = ndis_scan_start; ic->ic_scan_end = ndis_scan_end; ic->ic_set_channel = ndis_set_channel; ic->ic_scan_curchan = ndis_scan_curchan; ic->ic_scan_mindwell = ndis_scan_mindwell; ic->ic_bsschan = IEEE80211_CHAN_ANYC; //ic->ic_bss->ni_chan = ic->ic_bsschan; ic->ic_vap_create = ndis_vap_create; ic->ic_vap_delete = ndis_vap_delete; ic->ic_update_mcast = ndis_update_mcast; ic->ic_update_promisc = ndis_update_promisc; } else { ifmedia_init(&sc->ifmedia, IFM_IMASK, ndis_ifmedia_upd, ndis_ifmedia_sts); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_AUTO); ether_ifattach(ifp, eaddr); } fail: if (error) ndis_detach(dev); else /* We're done talking to the NIC for now; halt it. */ ndis_halt_nic(sc); return(error); } static struct ieee80211vap * ndis_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ndis_vap *nvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; nvp = (struct ndis_vap *) malloc(sizeof(struct ndis_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (nvp == NULL) return NULL; vap = &nvp->vap; ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); /* override with driver methods */ nvp->newstate = vap->iv_newstate; vap->iv_newstate = ndis_newstate; /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; /* install key handing routines */ vap->iv_key_set = ndis_add_key; vap->iv_key_delete = ndis_del_key; return vap; } static void ndis_vap_delete(struct ieee80211vap *vap) { struct ndis_vap *nvp = NDIS_VAP(vap); ieee80211_vap_detach(vap); free(nvp, M_80211_VAP); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ int ndis_detach(dev) device_t dev; { struct ndis_softc *sc; struct ifnet *ifp; driver_object *drv; sc = device_get_softc(dev); NDIS_LOCK(sc); ifp = sc->ifp; if (ifp != NULL) ifp->if_flags &= ~IFF_UP; if (device_is_attached(dev)) { NDIS_UNLOCK(sc); ndis_stop(sc); if (ifp != NULL) { if (sc->ndis_80211) ieee80211_ifdetach(ifp->if_l2com); else ether_ifdetach(ifp); } } else NDIS_UNLOCK(sc); if (sc->ndis_80211) { taskqueue_drain(sc->ndis_tq, &sc->ndis_scantask); - taskqueue_drain(sc->ndis_tq, &sc->ndis_authtask); - taskqueue_drain(sc->ndis_tq, &sc->ndis_assoctask); } if (sc->ndis_tickitem != NULL) IoFreeWorkItem(sc->ndis_tickitem); if (sc->ndis_startitem != NULL) IoFreeWorkItem(sc->ndis_startitem); if (sc->ndis_resetitem != NULL) IoFreeWorkItem(sc->ndis_resetitem); if (sc->ndis_inputitem != NULL) IoFreeWorkItem(sc->ndis_inputitem); bus_generic_detach(dev); ndis_unload_driver(sc); if (sc->ndis_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ndis_irq); if (sc->ndis_res_io) bus_release_resource(dev, SYS_RES_IOPORT, sc->ndis_io_rid, sc->ndis_res_io); if (sc->ndis_res_mem) bus_release_resource(dev, SYS_RES_MEMORY, sc->ndis_mem_rid, sc->ndis_res_mem); if (sc->ndis_res_altmem) bus_release_resource(dev, SYS_RES_MEMORY, sc->ndis_altmem_rid, sc->ndis_res_altmem); if (ifp != NULL) if_free(ifp); if (sc->ndis_iftype == PCMCIABus) ndis_free_amem(sc); if (sc->ndis_sc) ndis_destroy_dma(sc); if (sc->ndis_txarray) free(sc->ndis_txarray, M_DEVBUF); if (!sc->ndis_80211) ifmedia_removeall(&sc->ifmedia); if (sc->ndis_txpool != NULL) NdisFreePacketPool(sc->ndis_txpool); /* Destroy the PDO for this device. */ if (sc->ndis_iftype == PCIBus) drv = windrv_lookup(0, "PCI Bus"); else if (sc->ndis_iftype == PCMCIABus) drv = windrv_lookup(0, "PCCARD Bus"); else drv = windrv_lookup(0, "USB Bus"); if (drv == NULL) panic("couldn't find driver object"); windrv_destroy_pdo(drv, dev); if (sc->ndis_iftype == PCIBus) bus_dma_tag_destroy(sc->ndis_parent_tag); if (sc->ndis_80211) taskqueue_free(sc->ndis_tq); return(0); } int ndis_suspend(dev) device_t dev; { struct ndis_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); ifp = sc->ifp; #ifdef notdef if (NDIS_INITIALIZED(sc)) ndis_stop(sc); #endif return(0); } int ndis_resume(dev) device_t dev; { struct ndis_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); ifp = sc->ifp; if (NDIS_INITIALIZED(sc)) ndis_init(sc); return(0); } /* * The following bunch of routines are here to support drivers that * use the NdisMEthIndicateReceive()/MiniportTransferData() mechanism. * The NdisMEthIndicateReceive() handler runs at DISPATCH_LEVEL for * serialized miniports, or IRQL <= DISPATCH_LEVEL for deserialized * miniports. */ static void ndis_rxeof_eth(adapter, ctx, addr, hdr, hdrlen, lookahead, lookaheadlen, pktlen) ndis_handle adapter; ndis_handle ctx; char *addr; void *hdr; uint32_t hdrlen; void *lookahead; uint32_t lookaheadlen; uint32_t pktlen; { ndis_miniport_block *block; uint8_t irql = 0; uint32_t status; ndis_buffer *b; ndis_packet *p; struct mbuf *m; ndis_ethpriv *priv; block = adapter; m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { NdisFreePacket(p); return; } /* Save the data provided to us so far. */ m->m_len = lookaheadlen + hdrlen; m->m_pkthdr.len = pktlen + hdrlen; m->m_next = NULL; m_copyback(m, 0, hdrlen, hdr); m_copyback(m, hdrlen, lookaheadlen, lookahead); /* Now create a fake NDIS_PACKET to hold the data */ NdisAllocatePacket(&status, &p, block->nmb_rxpool); if (status != NDIS_STATUS_SUCCESS) { m_freem(m); return; } p->np_m0 = m; b = IoAllocateMdl(m->m_data, m->m_pkthdr.len, FALSE, FALSE, NULL); if (b == NULL) { NdisFreePacket(p); m_freem(m); return; } p->np_private.npp_head = p->np_private.npp_tail = b; p->np_private.npp_totlen = m->m_pkthdr.len; /* Save the packet RX context somewhere. */ priv = (ndis_ethpriv *)&p->np_protocolreserved; priv->nep_ctx = ctx; if (!NDIS_SERIALIZED(block)) KeAcquireSpinLock(&block->nmb_lock, &irql); InsertTailList((&block->nmb_packetlist), (&p->np_list)); if (!NDIS_SERIALIZED(block)) KeReleaseSpinLock(&block->nmb_lock, irql); return; } /* * NdisMEthIndicateReceiveComplete() handler, runs at DISPATCH_LEVEL * for serialized miniports, or IRQL <= DISPATCH_LEVEL for deserialized * miniports. */ static void ndis_rxeof_done(adapter) ndis_handle adapter; { struct ndis_softc *sc; ndis_miniport_block *block; block = adapter; /* Schedule transfer/RX of queued packets. */ sc = device_get_softc(block->nmb_physdeviceobj->do_devext); KeInsertQueueDpc(&sc->ndis_rxdpc, NULL, NULL); return; } /* * MiniportTransferData() handler, runs at DISPATCH_LEVEL. */ static void ndis_rxeof_xfr(dpc, adapter, sysarg1, sysarg2) kdpc *dpc; ndis_handle adapter; void *sysarg1; void *sysarg2; { ndis_miniport_block *block; struct ndis_softc *sc; ndis_packet *p; list_entry *l; uint32_t status; ndis_ethpriv *priv; struct ifnet *ifp; struct mbuf *m; block = adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); ifp = sc->ifp; KeAcquireSpinLockAtDpcLevel(&block->nmb_lock); l = block->nmb_packetlist.nle_flink; while(!IsListEmpty(&block->nmb_packetlist)) { l = RemoveHeadList((&block->nmb_packetlist)); p = CONTAINING_RECORD(l, ndis_packet, np_list); InitializeListHead((&p->np_list)); priv = (ndis_ethpriv *)&p->np_protocolreserved; m = p->np_m0; p->np_softc = sc; p->np_m0 = NULL; KeReleaseSpinLockFromDpcLevel(&block->nmb_lock); status = MSCALL6(sc->ndis_chars->nmc_transferdata_func, p, &p->np_private.npp_totlen, block, priv->nep_ctx, m->m_len, m->m_pkthdr.len - m->m_len); KeAcquireSpinLockAtDpcLevel(&block->nmb_lock); /* * If status is NDIS_STATUS_PENDING, do nothing and * wait for a callback to the ndis_rxeof_xfr_done() * handler. */ m->m_len = m->m_pkthdr.len; m->m_pkthdr.rcvif = ifp; if (status == NDIS_STATUS_SUCCESS) { IoFreeMdl(p->np_private.npp_head); NdisFreePacket(p); KeAcquireSpinLockAtDpcLevel(&sc->ndis_rxlock); _IF_ENQUEUE(&sc->ndis_rxqueue, m); KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); IoQueueWorkItem(sc->ndis_inputitem, (io_workitem_func)ndis_inputtask_wrap, WORKQUEUE_CRITICAL, ifp); } if (status == NDIS_STATUS_FAILURE) m_freem(m); /* Advance to next packet */ l = block->nmb_packetlist.nle_flink; } KeReleaseSpinLockFromDpcLevel(&block->nmb_lock); return; } /* * NdisMTransferDataComplete() handler, runs at DISPATCH_LEVEL. */ static void ndis_rxeof_xfr_done(adapter, packet, status, len) ndis_handle adapter; ndis_packet *packet; uint32_t status; uint32_t len; { ndis_miniport_block *block; struct ndis_softc *sc; struct ifnet *ifp; struct mbuf *m; block = adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); ifp = sc->ifp; m = packet->np_m0; IoFreeMdl(packet->np_private.npp_head); NdisFreePacket(packet); if (status != NDIS_STATUS_SUCCESS) { m_freem(m); return; } m->m_len = m->m_pkthdr.len; m->m_pkthdr.rcvif = ifp; KeAcquireSpinLockAtDpcLevel(&sc->ndis_rxlock); _IF_ENQUEUE(&sc->ndis_rxqueue, m); KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); IoQueueWorkItem(sc->ndis_inputitem, (io_workitem_func)ndis_inputtask_wrap, WORKQUEUE_CRITICAL, ifp); return; } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. * * When handling received NDIS packets, the 'status' field in the * out-of-band portion of the ndis_packet has special meaning. In the * most common case, the underlying NDIS driver will set this field * to NDIS_STATUS_SUCCESS, which indicates that it's ok for us to * take posession of it. We then change the status field to * NDIS_STATUS_PENDING to tell the driver that we now own the packet, * and that we will return it at some point in the future via the * return packet handler. * * If the driver hands us a packet with a status of NDIS_STATUS_RESOURCES, * this means the driver is running out of packet/buffer resources and * wants to maintain ownership of the packet. In this case, we have to * copy the packet data into local storage and let the driver keep the * packet. */ static void ndis_rxeof(adapter, packets, pktcnt) ndis_handle adapter; ndis_packet **packets; uint32_t pktcnt; { struct ndis_softc *sc; ndis_miniport_block *block; ndis_packet *p; uint32_t s; ndis_tcpip_csum *csum; struct ifnet *ifp; struct mbuf *m0, *m; int i; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); ifp = sc->ifp; /* * There's a slim chance the driver may indicate some packets * before we're completely ready to handle them. If we detect this, * we need to return them to the miniport and ignore them. */ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { for (i = 0; i < pktcnt; i++) { p = packets[i]; if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS) { p->np_refcnt++; ndis_return_packet(p, block); } } return; } for (i = 0; i < pktcnt; i++) { p = packets[i]; /* Stash the softc here so ptom can use it. */ p->np_softc = sc; if (ndis_ptom(&m0, p)) { device_printf (sc->ndis_dev, "ptom failed\n"); if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS) ndis_return_packet(p, block); } else { #ifdef notdef if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) { m = m_dup(m0, M_DONTWAIT); /* * NOTE: we want to destroy the mbuf here, but * we don't actually want to return it to the * driver via the return packet handler. By * bumping np_refcnt, we can prevent the * ndis_return_packet() routine from actually * doing anything. */ p->np_refcnt++; m_freem(m0); if (m == NULL) ifp->if_ierrors++; else m0 = m; } else p->np_oob.npo_status = NDIS_STATUS_PENDING; #endif m = m_dup(m0, M_DONTWAIT); if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) p->np_refcnt++; else p->np_oob.npo_status = NDIS_STATUS_PENDING; m_freem(m0); if (m == NULL) { ifp->if_ierrors++; continue; } m0 = m; m0->m_pkthdr.rcvif = ifp; /* Deal with checksum offload. */ if (ifp->if_capenable & IFCAP_RXCSUM && p->np_ext.npe_info[ndis_tcpipcsum_info] != NULL) { s = (uintptr_t) p->np_ext.npe_info[ndis_tcpipcsum_info]; csum = (ndis_tcpip_csum *)&s; if (csum->u.ntc_rxflags & NDIS_RXCSUM_IP_PASSED) m0->m_pkthdr.csum_flags |= CSUM_IP_CHECKED|CSUM_IP_VALID; if (csum->u.ntc_rxflags & (NDIS_RXCSUM_TCP_PASSED | NDIS_RXCSUM_UDP_PASSED)) { m0->m_pkthdr.csum_flags |= CSUM_DATA_VALID|CSUM_PSEUDO_HDR; m0->m_pkthdr.csum_data = 0xFFFF; } } KeAcquireSpinLockAtDpcLevel(&sc->ndis_rxlock); _IF_ENQUEUE(&sc->ndis_rxqueue, m0); KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); IoQueueWorkItem(sc->ndis_inputitem, (io_workitem_func)ndis_inputtask_wrap, WORKQUEUE_CRITICAL, ifp); } } return; } /* * This routine is run at PASSIVE_LEVEL. We use this routine to pass * packets into the stack in order to avoid calling (*ifp->if_input)() * with any locks held (at DISPATCH_LEVEL, we'll be holding the * 'dispatch level' per-cpu sleep lock). */ static void ndis_inputtask(dobj, arg) device_object *dobj; void *arg; { ndis_miniport_block *block; struct ifnet *ifp; struct ndis_softc *sc; struct mbuf *m; struct ieee80211com *ic; struct ieee80211vap *vap; uint8_t irql; ifp = arg; sc = ifp->if_softc; ic = ifp->if_l2com; vap = TAILQ_FIRST(&ic->ic_vaps); block = dobj->do_devext; KeAcquireSpinLock(&sc->ndis_rxlock, &irql); while(1) { _IF_DEQUEUE(&sc->ndis_rxqueue, m); if (m == NULL) break; KeReleaseSpinLock(&sc->ndis_rxlock, irql); if (sc->ndis_80211) vap->iv_deliver_data(vap, vap->iv_bss, m); else (*ifp->if_input)(ifp, m); KeAcquireSpinLock(&sc->ndis_rxlock, &irql); } KeReleaseSpinLock(&sc->ndis_rxlock, irql); return; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void ndis_txeof(adapter, packet, status) ndis_handle adapter; ndis_packet *packet; ndis_status status; { struct ndis_softc *sc; ndis_miniport_block *block; struct ifnet *ifp; int idx; struct mbuf *m; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); ifp = sc->ifp; m = packet->np_m0; idx = packet->np_txidx; if (sc->ndis_sc) bus_dmamap_unload(sc->ndis_ttag, sc->ndis_tmaps[idx]); ndis_free_packet(packet); m_freem(m); NDIS_LOCK(sc); sc->ndis_txarray[idx] = NULL; sc->ndis_txpending++; if (status == NDIS_STATUS_SUCCESS) ifp->if_opackets++; else ifp->if_oerrors++; sc->ndis_tx_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; NDIS_UNLOCK(sc); IoQueueWorkItem(sc->ndis_startitem, (io_workitem_func)ndis_starttask_wrap, WORKQUEUE_CRITICAL, ifp); return; } static void ndis_linksts(adapter, status, sbuf, slen) ndis_handle adapter; ndis_status status; void *sbuf; uint32_t slen; { ndis_miniport_block *block; struct ndis_softc *sc; block = adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); sc->ndis_sts = status; /* Event list is all full up, drop this one. */ NDIS_LOCK(sc); if (sc->ndis_evt[sc->ndis_evtpidx].ne_sts) { NDIS_UNLOCK(sc); return; } /* Cache the event. */ if (slen) { sc->ndis_evt[sc->ndis_evtpidx].ne_buf = malloc(slen, M_TEMP, M_NOWAIT); if (sc->ndis_evt[sc->ndis_evtpidx].ne_buf == NULL) { NDIS_UNLOCK(sc); return; } bcopy((char *)sbuf, sc->ndis_evt[sc->ndis_evtpidx].ne_buf, slen); } sc->ndis_evt[sc->ndis_evtpidx].ne_sts = status; sc->ndis_evt[sc->ndis_evtpidx].ne_len = slen; NDIS_EVTINC(sc->ndis_evtpidx); NDIS_UNLOCK(sc); return; } static void ndis_linksts_done(adapter) ndis_handle adapter; { ndis_miniport_block *block; struct ndis_softc *sc; struct ifnet *ifp; block = adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); ifp = sc->ifp; if (!NDIS_INITIALIZED(sc)) return; switch (sc->ndis_sts) { case NDIS_STATUS_MEDIA_CONNECT: IoQueueWorkItem(sc->ndis_tickitem, (io_workitem_func)ndis_ticktask_wrap, WORKQUEUE_CRITICAL, sc); IoQueueWorkItem(sc->ndis_startitem, (io_workitem_func)ndis_starttask_wrap, WORKQUEUE_CRITICAL, ifp); break; case NDIS_STATUS_MEDIA_DISCONNECT: if (sc->ndis_link) IoQueueWorkItem(sc->ndis_tickitem, (io_workitem_func)ndis_ticktask_wrap, WORKQUEUE_CRITICAL, sc); break; default: break; } /* Notify possible listners of interface change. */ rt_ifmsg(ifp); return; } static void ndis_tick(xsc) void *xsc; { struct ndis_softc *sc; sc = xsc; if (sc->ndis_hang_timer && --sc->ndis_hang_timer == 0) { IoQueueWorkItem(sc->ndis_tickitem, (io_workitem_func)ndis_ticktask_wrap, WORKQUEUE_CRITICAL, sc); sc->ndis_hang_timer = sc->ndis_block->nmb_checkforhangsecs; } if (sc->ndis_tx_timer && --sc->ndis_tx_timer == 0) { sc->ifp->if_oerrors++; device_printf(sc->ndis_dev, "watchdog timeout\n"); IoQueueWorkItem(sc->ndis_resetitem, (io_workitem_func)ndis_resettask_wrap, WORKQUEUE_CRITICAL, sc); IoQueueWorkItem(sc->ndis_startitem, (io_workitem_func)ndis_starttask_wrap, WORKQUEUE_CRITICAL, sc->ifp); } callout_reset(&sc->ndis_stat_callout, hz, ndis_tick, sc); } static void ndis_ticktask(d, xsc) device_object *d; void *xsc; { struct ndis_softc *sc; struct ieee80211com *ic; struct ieee80211vap *vap; ndis_checkforhang_handler hangfunc; uint8_t rval; sc = xsc; ic = sc->ifp->if_l2com; vap = TAILQ_FIRST(&ic->ic_vaps); NDIS_LOCK(sc); if (!NDIS_INITIALIZED(sc)) { NDIS_UNLOCK(sc); return; } NDIS_UNLOCK(sc); hangfunc = sc->ndis_chars->nmc_checkhang_func; if (hangfunc != NULL) { rval = MSCALL1(hangfunc, sc->ndis_block->nmb_miniportadapterctx); if (rval == TRUE) { ndis_reset_nic(sc); return; } } NDIS_LOCK(sc); if (sc->ndis_link == 0 && sc->ndis_sts == NDIS_STATUS_MEDIA_CONNECT) { sc->ndis_link = 1; NDIS_UNLOCK(sc); if (sc->ndis_80211) { ndis_getstate_80211(sc); ieee80211_new_state(vap, IEEE80211_S_RUN, -1); } NDIS_LOCK(sc); if_link_state_change(sc->ifp, LINK_STATE_UP); } if (sc->ndis_link == 1 && sc->ndis_sts == NDIS_STATUS_MEDIA_DISCONNECT) { sc->ndis_link = 0; NDIS_UNLOCK(sc); if (sc->ndis_80211) ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); NDIS_LOCK(sc); if_link_state_change(sc->ifp, LINK_STATE_DOWN); } NDIS_UNLOCK(sc); return; } static void ndis_map_sclist(arg, segs, nseg, mapsize, error) void *arg; bus_dma_segment_t *segs; int nseg; bus_size_t mapsize; int error; { struct ndis_sc_list *sclist; int i; if (error || arg == NULL) return; sclist = arg; sclist->nsl_frags = nseg; for (i = 0; i < nseg; i++) { sclist->nsl_elements[i].nse_addr.np_quad = segs[i].ds_addr; sclist->nsl_elements[i].nse_len = segs[i].ds_len; } return; } static int ndis_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { /* no support; just discard */ m_freem(m); ieee80211_free_node(ni); return 0; } static void ndis_update_mcast(struct ifnet *ifp) { struct ndis_softc *sc = ifp->if_softc; ndis_setmulti(sc); } static void ndis_update_promisc(struct ifnet *ifp) { /* not supported */ } static void ndis_starttask(d, arg) device_object *d; void *arg; { struct ifnet *ifp; ifp = arg; if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) ndis_start(ifp); return; } /* * Main transmit routine. To make NDIS drivers happy, we need to * transform mbuf chains into NDIS packets and feed them to the * send packet routines. Most drivers allow you to send several * packets at once (up to the maxpkts limit). Unfortunately, rather * that accepting them in the form of a linked list, they expect * a contiguous array of pointers to packets. * * For those drivers which use the NDIS scatter/gather DMA mechanism, * we need to perform busdma work here. Those that use map registers * will do the mapping themselves on a buffer by buffer basis. */ static void ndis_start(ifp) struct ifnet *ifp; { struct ndis_softc *sc; struct mbuf *m = NULL; ndis_packet **p0 = NULL, *p = NULL; ndis_tcpip_csum *csum; int pcnt = 0, status; sc = ifp->if_softc; NDIS_LOCK(sc); if (!sc->ndis_link || ifp->if_drv_flags & IFF_DRV_OACTIVE) { NDIS_UNLOCK(sc); return; } p0 = &sc->ndis_txarray[sc->ndis_txidx]; while(sc->ndis_txpending) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; NdisAllocatePacket(&status, &sc->ndis_txarray[sc->ndis_txidx], sc->ndis_txpool); if (status != NDIS_STATUS_SUCCESS) break; if (ndis_mtop(m, &sc->ndis_txarray[sc->ndis_txidx])) { IFQ_DRV_PREPEND(&ifp->if_snd, m); NDIS_UNLOCK(sc); return; } /* * Save pointer to original mbuf * so we can free it later. */ p = sc->ndis_txarray[sc->ndis_txidx]; p->np_txidx = sc->ndis_txidx; p->np_m0 = m; p->np_oob.npo_status = NDIS_STATUS_PENDING; /* * Do scatter/gather processing, if driver requested it. */ if (sc->ndis_sc) { bus_dmamap_load_mbuf(sc->ndis_ttag, sc->ndis_tmaps[sc->ndis_txidx], m, ndis_map_sclist, &p->np_sclist, BUS_DMA_NOWAIT); bus_dmamap_sync(sc->ndis_ttag, sc->ndis_tmaps[sc->ndis_txidx], BUS_DMASYNC_PREREAD); p->np_ext.npe_info[ndis_sclist_info] = &p->np_sclist; } /* Handle checksum offload. */ if (ifp->if_capenable & IFCAP_TXCSUM && m->m_pkthdr.csum_flags) { csum = (ndis_tcpip_csum *) &p->np_ext.npe_info[ndis_tcpipcsum_info]; csum->u.ntc_txflags = NDIS_TXCSUM_DO_IPV4; if (m->m_pkthdr.csum_flags & CSUM_IP) csum->u.ntc_txflags |= NDIS_TXCSUM_DO_IP; if (m->m_pkthdr.csum_flags & CSUM_TCP) csum->u.ntc_txflags |= NDIS_TXCSUM_DO_TCP; if (m->m_pkthdr.csum_flags & CSUM_UDP) csum->u.ntc_txflags |= NDIS_TXCSUM_DO_UDP; p->np_private.npp_flags = NDIS_PROTOCOL_ID_TCP_IP; } NDIS_INC(sc); sc->ndis_txpending--; pcnt++; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ if (!sc->ndis_80211) /* XXX handle 80211 */ BPF_MTAP(ifp, m); /* * The array that p0 points to must appear contiguous, * so we must not wrap past the end of sc->ndis_txarray[]. * If it looks like we're about to wrap, break out here * so the this batch of packets can be transmitted, then * wait for txeof to ask us to send the rest. */ if (sc->ndis_txidx == 0) break; } if (pcnt == 0) { NDIS_UNLOCK(sc); return; } if (sc->ndis_txpending == 0) ifp->if_drv_flags |= IFF_DRV_OACTIVE; /* * Set a timeout in case the chip goes out to lunch. */ sc->ndis_tx_timer = 5; NDIS_UNLOCK(sc); /* * According to NDIS documentation, if a driver exports * a MiniportSendPackets() routine, we prefer that over * a MiniportSend() routine (which sends just a single * packet). */ if (sc->ndis_chars->nmc_sendmulti_func != NULL) ndis_send_packets(sc, p0, pcnt); else ndis_send_packet(sc, p); return; } static void ndis_init(xsc) void *xsc; { struct ndis_softc *sc = xsc; struct ifnet *ifp = sc->ifp; struct ieee80211com *ic = ifp->if_l2com; int i, len, error; /* * Avoid reintializing the link unnecessarily. * This should be dealt with in a better way by * fixing the upper layer modules so they don't * call ifp->if_init() quite as often. */ if (sc->ndis_link) return; /* * Cancel pending I/O and free all RX/TX buffers. */ ndis_stop(sc); if (ndis_init_nic(sc)) return; /* Init our MAC address */ /* Program the packet filter */ sc->ndis_filter = NDIS_PACKET_TYPE_DIRECTED; if (ifp->if_flags & IFF_BROADCAST) sc->ndis_filter |= NDIS_PACKET_TYPE_BROADCAST; if (ifp->if_flags & IFF_PROMISC) sc->ndis_filter |= NDIS_PACKET_TYPE_PROMISCUOUS; len = sizeof(sc->ndis_filter); error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, &sc->ndis_filter, &len); if (error) device_printf (sc->ndis_dev, "set filter failed: %d\n", error); /* * Set lookahead. */ i = ifp->if_mtu; len = sizeof(i); ndis_set_info(sc, OID_GEN_CURRENT_LOOKAHEAD, &i, &len); /* * Program the multicast filter, if necessary. */ ndis_setmulti(sc); /* Setup task offload. */ ndis_set_offload(sc); if (sc->ndis_80211) ndis_setstate_80211(sc); NDIS_LOCK(sc); sc->ndis_txidx = 0; sc->ndis_txpending = sc->ndis_maxpkts; sc->ndis_link = 0; if_link_state_change(sc->ifp, LINK_STATE_UNKNOWN); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->ndis_tx_timer = 0; /* * Some drivers don't set this value. The NDIS spec says * the default checkforhang timeout is "approximately 2 * seconds." We use 3 seconds, because it seems for some * drivers, exactly 2 seconds is too fast. */ if (sc->ndis_block->nmb_checkforhangsecs == 0) sc->ndis_block->nmb_checkforhangsecs = 3; sc->ndis_hang_timer = sc->ndis_block->nmb_checkforhangsecs; callout_reset(&sc->ndis_stat_callout, hz, ndis_tick, sc); NDIS_UNLOCK(sc); /* XXX force handling */ ieee80211_start_all(ic); /* start all vap's */ } /* * Set media options. */ static int ndis_ifmedia_upd(ifp) struct ifnet *ifp; { struct ndis_softc *sc; sc = ifp->if_softc; if (NDIS_INITIALIZED(sc)) ndis_init(sc); return(0); } /* * Report current media status. */ static void ndis_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct ndis_softc *sc; uint32_t media_info; ndis_media_state linkstate; int error, len; ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; sc = ifp->if_softc; if (!NDIS_INITIALIZED(sc)) return; len = sizeof(linkstate); error = ndis_get_info(sc, OID_GEN_MEDIA_CONNECT_STATUS, (void *)&linkstate, &len); len = sizeof(media_info); error = ndis_get_info(sc, OID_GEN_LINK_SPEED, (void *)&media_info, &len); if (linkstate == nmc_connected) ifmr->ifm_status |= IFM_ACTIVE; switch(media_info) { case 100000: ifmr->ifm_active |= IFM_10_T; break; case 1000000: ifmr->ifm_active |= IFM_100_TX; break; case 10000000: ifmr->ifm_active |= IFM_1000_T; break; default: device_printf(sc->ndis_dev, "unknown speed: %d\n", media_info); break; } return; } static int ndis_set_cipher(sc, cipher) struct ndis_softc *sc; int cipher; { struct ieee80211com *ic; int rval = 0, len; uint32_t arg, save; ic = sc->ifp->if_l2com; len = sizeof(arg); if (cipher == WPA_CSE_WEP40 || WPA_CSE_WEP104) { if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP)) return(ENOTSUP); arg = NDIS_80211_WEPSTAT_ENC1ENABLED; } if (cipher == WPA_CSE_TKIP) { if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP)) return(ENOTSUP); arg = NDIS_80211_WEPSTAT_ENC2ENABLED; } if (cipher == WPA_CSE_CCMP) { if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_AES_CCM)) return(ENOTSUP); arg = NDIS_80211_WEPSTAT_ENC3ENABLED; } DPRINTF(("Setting cipher to %d\n", arg)); save = arg; rval = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); if (rval) return(rval); /* Check that the cipher was set correctly. */ len = sizeof(save); rval = ndis_get_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); if (rval != 0 || arg != save) return(ENODEV); return(0); } /* * WPA is hairy to set up. Do the work in a separate routine * so we don't clutter the setstate function too much. * Important yet undocumented fact: first we have to set the * authentication mode, _then_ we enable the ciphers. If one * of the WPA authentication modes isn't enabled, the driver * might not permit the TKIP or AES ciphers to be selected. */ static int ndis_set_wpa(sc, ie, ielen) struct ndis_softc *sc; void *ie; int ielen; { struct ieee80211_ie_wpa *w; struct ndis_ie *n; char *pos; uint32_t arg; int i; /* * Apparently, the only way for us to know what ciphers * and key management/authentication mode to use is for * us to inspect the optional information element (IE) * stored in the 802.11 state machine. This IE should be * supplied by the WPA supplicant. */ w = (struct ieee80211_ie_wpa *)ie; /* Check for the right kind of IE. */ if (w->wpa_id != IEEE80211_ELEMID_VENDOR) { DPRINTF(("Incorrect IE type %d\n", w->wpa_id)); return(EINVAL); } /* Skip over the ucast cipher OIDs. */ pos = (char *)&w->wpa_uciphers[0]; pos += w->wpa_uciphercnt * sizeof(struct ndis_ie); /* Skip over the authmode count. */ pos += sizeof(u_int16_t); /* * Check for the authentication modes. I'm * pretty sure there's only supposed to be one. */ n = (struct ndis_ie *)pos; if (n->ni_val == WPA_ASE_NONE) arg = NDIS_80211_AUTHMODE_WPANONE; if (n->ni_val == WPA_ASE_8021X_UNSPEC) arg = NDIS_80211_AUTHMODE_WPA; if (n->ni_val == WPA_ASE_8021X_PSK) arg = NDIS_80211_AUTHMODE_WPAPSK; DPRINTF(("Setting WPA auth mode to %d\n", arg)); i = sizeof(arg); if (ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &i)) return(ENOTSUP); i = sizeof(arg); ndis_get_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &i); /* Now configure the desired ciphers. */ /* First, set up the multicast group cipher. */ n = (struct ndis_ie *)&w->wpa_mcipher[0]; if (ndis_set_cipher(sc, n->ni_val)) return(ENOTSUP); /* Now start looking around for the unicast ciphers. */ pos = (char *)&w->wpa_uciphers[0]; n = (struct ndis_ie *)pos; for (i = 0; i < w->wpa_uciphercnt; i++) { if (ndis_set_cipher(sc, n->ni_val)) return(ENOTSUP); n++; } return(0); } static void ndis_setstate_80211(sc) struct ndis_softc *sc; { struct ieee80211com *ic; ndis_80211_macaddr bssid; ndis_80211_config config; int rval = 0, len; uint32_t arg; struct ifnet *ifp; ifp = sc->ifp; ic = ifp->if_l2com; if (!NDIS_INITIALIZED(sc)) { DPRINTF(("%s: NDIS not initialized\n", __func__)); return; } /* Disassociate and turn off radio. */ len = sizeof(arg); arg = 1; ndis_set_info(sc, OID_802_11_DISASSOCIATE, &arg, &len); /* Set network infrastructure mode. */ len = sizeof(arg); if (ic->ic_opmode == IEEE80211_M_IBSS) arg = NDIS_80211_NET_INFRA_IBSS; else arg = NDIS_80211_NET_INFRA_BSS; rval = ndis_set_info(sc, OID_802_11_INFRASTRUCTURE_MODE, &arg, &len); if (rval) device_printf (sc->ndis_dev, "set infra failed: %d\n", rval); /* Set power management */ len = sizeof(arg); if (ic->ic_flags & IEEE80211_F_PMGTON) arg = NDIS_80211_POWERMODE_FAST_PSP; else arg = NDIS_80211_POWERMODE_CAM; ndis_set_info(sc, OID_802_11_POWER_MODE, &arg, &len); /* * Default encryption mode to off, authentication * to open and privacy to 'accept everything.' */ len = sizeof(arg); arg = NDIS_80211_WEPSTAT_DISABLED; ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); len = sizeof(arg); arg = NDIS_80211_AUTHMODE_OPEN; ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len); /* * Note that OID_80211_PRIVACY_FILTER is optional: * not all drivers implement it. */ len = sizeof(arg); arg = NDIS_80211_PRIVFILT_8021XWEP; ndis_set_info(sc, OID_802_11_PRIVACY_FILTER, &arg, &len); len = sizeof(config); bzero((char *)&config, len); config.nc_length = len; config.nc_fhconfig.ncf_length = sizeof(ndis_80211_config_fh); rval = ndis_get_info(sc, OID_802_11_CONFIGURATION, &config, &len); /* * Some drivers expect us to initialize these values, so * provide some defaults. */ if (config.nc_beaconperiod == 0) config.nc_beaconperiod = 100; if (config.nc_atimwin == 0) config.nc_atimwin = 100; if (config.nc_fhconfig.ncf_dwelltime == 0) config.nc_fhconfig.ncf_dwelltime = 200; if (rval == 0 && ic->ic_bsschan != IEEE80211_CHAN_ANYC) { int chan, chanflag; chan = ieee80211_chan2ieee(ic, ic->ic_bsschan); chanflag = config.nc_dsconfig > 2500000 ? IEEE80211_CHAN_2GHZ : IEEE80211_CHAN_5GHZ; if (chan != ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0)) { config.nc_dsconfig = ic->ic_bsschan->ic_freq * 1000; len = sizeof(config); config.nc_length = len; config.nc_fhconfig.ncf_length = sizeof(ndis_80211_config_fh); DPRINTF(("Setting channel to %ukHz\n", config.nc_dsconfig)); rval = ndis_set_info(sc, OID_802_11_CONFIGURATION, &config, &len); if (rval) device_printf(sc->ndis_dev, "couldn't change " "DS config to %ukHz: %d\n", config.nc_dsconfig, rval); } } else if (rval) device_printf(sc->ndis_dev, "couldn't retrieve " "channel info: %d\n", rval); /* Set the BSSID to our value so the driver doesn't associate */ len = IEEE80211_ADDR_LEN; bcopy(ic->ic_myaddr, bssid, len); DPRINTF(("Setting BSSID to %6D\n", (uint8_t *)&bssid, ":")); rval = ndis_set_info(sc, OID_802_11_BSSID, &bssid, &len); if (rval) device_printf(sc->ndis_dev, "setting BSSID failed: %d\n", rval); } static void -ndis_auth(void *arg, int npending) -{ - struct ndis_softc *sc = arg; - struct ifnet *ifp = sc->ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - vap->iv_state = IEEE80211_S_AUTH; - ndis_auth_and_assoc(sc, vap); -} - -static void -ndis_assoc(void *arg, int npending) -{ - struct ndis_softc *sc = arg; - struct ifnet *ifp = sc->ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - vap->iv_state = IEEE80211_S_ASSOC; - ndis_auth_and_assoc(sc, vap); -} - -static void ndis_auth_and_assoc(sc, vap) struct ndis_softc *sc; struct ieee80211vap *vap; { struct ieee80211com *ic; struct ieee80211_node *ni; ndis_80211_ssid ssid; ndis_80211_macaddr bssid; ndis_80211_wep wep; int i, rval = 0, len, error; uint32_t arg; struct ifnet *ifp; ifp = sc->ifp; ic = ifp->if_l2com; ni = vap->iv_bss; if (!NDIS_INITIALIZED(sc)) { DPRINTF(("%s: NDIS not initialized\n", __func__)); return; } /* Initial setup */ ndis_setstate_80211(sc); /* Set network infrastructure mode. */ len = sizeof(arg); if (vap->iv_opmode == IEEE80211_M_IBSS) arg = NDIS_80211_NET_INFRA_IBSS; else arg = NDIS_80211_NET_INFRA_BSS; rval = ndis_set_info(sc, OID_802_11_INFRASTRUCTURE_MODE, &arg, &len); if (rval) device_printf (sc->ndis_dev, "set infra failed: %d\n", rval); /* Set RTS threshold */ len = sizeof(arg); arg = vap->iv_rtsthreshold; ndis_set_info(sc, OID_802_11_RTS_THRESHOLD, &arg, &len); /* Set fragmentation threshold */ len = sizeof(arg); arg = vap->iv_fragthreshold; ndis_set_info(sc, OID_802_11_FRAGMENTATION_THRESHOLD, &arg, &len); /* Set WEP */ if (vap->iv_flags & IEEE80211_F_PRIVACY && !(vap->iv_flags & IEEE80211_F_WPA)) { int keys_set = 0; if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { len = sizeof(arg); arg = NDIS_80211_AUTHMODE_SHARED; DPRINTF(("Setting shared auth\n")); ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len); } for (i = 0; i < IEEE80211_WEP_NKID; i++) { if (vap->iv_nw_keys[i].wk_keylen) { if (vap->iv_nw_keys[i].wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP) continue; bzero((char *)&wep, sizeof(wep)); wep.nw_keylen = vap->iv_nw_keys[i].wk_keylen; /* * 5, 13 and 16 are the only valid * only valid key lengths. Anything * in between will be zero padded out to * the next highest boundary. */ if (vap->iv_nw_keys[i].wk_keylen < 5) wep.nw_keylen = 5; else if (vap->iv_nw_keys[i].wk_keylen > 5 && vap->iv_nw_keys[i].wk_keylen < 13) wep.nw_keylen = 13; else if (vap->iv_nw_keys[i].wk_keylen > 13 && vap->iv_nw_keys[i].wk_keylen < 16) wep.nw_keylen = 16; wep.nw_keyidx = i; wep.nw_length = (sizeof(uint32_t) * 3) + wep.nw_keylen; if (i == vap->iv_def_txkey) wep.nw_keyidx |= NDIS_80211_WEPKEY_TX; bcopy(vap->iv_nw_keys[i].wk_key, wep.nw_keydata, wep.nw_length); len = sizeof(wep); DPRINTF(("Setting WEP key %d\n", i)); rval = ndis_set_info(sc, OID_802_11_ADD_WEP, &wep, &len); if (rval) device_printf(sc->ndis_dev, "set wepkey failed: %d\n", rval); keys_set++; } } if (keys_set) { DPRINTF(("Setting WEP on\n")); arg = NDIS_80211_WEPSTAT_ENABLED; len = sizeof(arg); rval = ndis_set_info(sc, OID_802_11_WEP_STATUS, &arg, &len); if (rval) device_printf(sc->ndis_dev, "enable WEP failed: %d\n", rval); if (vap->iv_flags & IEEE80211_F_DROPUNENC) arg = NDIS_80211_PRIVFILT_8021XWEP; else arg = NDIS_80211_PRIVFILT_ACCEPTALL; len = sizeof(arg); ndis_set_info(sc, OID_802_11_PRIVACY_FILTER, &arg, &len); } } /* Set up WPA. */ if ((vap->iv_flags & IEEE80211_F_WPA) && vap->iv_appie_assocreq != NULL) { struct ieee80211_appie *ie = vap->iv_appie_assocreq; error = ndis_set_wpa(sc, ie->ie_data, ie->ie_len); if (error != 0) device_printf(sc->ndis_dev, "WPA setup failed\n"); } #ifdef notyet /* Set network type. */ arg = 0; switch (vap->iv_curmode) { case IEEE80211_MODE_11A: arg = NDIS_80211_NETTYPE_11OFDM5; break; case IEEE80211_MODE_11B: arg = NDIS_80211_NETTYPE_11DS; break; case IEEE80211_MODE_11G: arg = NDIS_80211_NETTYPE_11OFDM24; break; default: device_printf(sc->ndis_dev, "unknown mode: %d\n", vap->iv_curmode); } if (arg) { DPRINTF(("Setting network type to %d\n", arg)); len = sizeof(arg); rval = ndis_set_info(sc, OID_802_11_NETWORK_TYPE_IN_USE, &arg, &len); if (rval) device_printf (sc->ndis_dev, "set nettype failed: %d\n", rval); } #endif /* * If the user selected a specific BSSID, try * to use that one. This is useful in the case where * there are several APs in range with the same network * name. To delete the BSSID, we use the broadcast * address as the BSSID. * Note that some drivers seem to allow setting a BSSID * in ad-hoc mode, which has the effect of forcing the * NIC to create an ad-hoc cell with a specific BSSID, * instead of a randomly chosen one. However, the net80211 * code makes the assumtion that the BSSID setting is invalid * when you're in ad-hoc mode, so we don't allow that here. */ len = IEEE80211_ADDR_LEN; if (vap->iv_flags & IEEE80211_F_DESBSSID && vap->iv_opmode != IEEE80211_M_IBSS) bcopy(ni->ni_bssid, bssid, len); else bcopy(ifp->if_broadcastaddr, bssid, len); DPRINTF(("Setting BSSID to %6D\n", (uint8_t *)&bssid, ":")); rval = ndis_set_info(sc, OID_802_11_BSSID, &bssid, &len); if (rval) device_printf(sc->ndis_dev, "setting BSSID failed: %d\n", rval); /* Set SSID -- always do this last. */ #ifdef NDIS_DEBUG if (ndis_debug > 0) { printf("Setting ESSID to "); ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); printf("\n"); } #endif len = sizeof(ssid); bzero((char *)&ssid, len); ssid.ns_ssidlen = ni->ni_esslen; if (ssid.ns_ssidlen == 0) { ssid.ns_ssidlen = 1; } else bcopy(ni->ni_essid, ssid.ns_ssid, ssid.ns_ssidlen); rval = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len); if (rval) device_printf (sc->ndis_dev, "set ssid failed: %d\n", rval); - if (vap->iv_state == IEEE80211_S_AUTH) - ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); - return; } static int ndis_get_assoc(sc, assoc) struct ndis_softc *sc; ndis_wlan_bssid_ex **assoc; { struct ifnet *ifp = sc->ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap; struct ieee80211_node *ni; ndis_80211_bssid_list_ex *bl; ndis_wlan_bssid_ex *bs; ndis_80211_macaddr bssid; int i, len, error; if (!sc->ndis_link) return(ENOENT); len = sizeof(bssid); error = ndis_get_info(sc, OID_802_11_BSSID, &bssid, &len); if (error) { device_printf(sc->ndis_dev, "failed to get bssid\n"); return(ENOENT); } vap = TAILQ_FIRST(&ic->ic_vaps); ni = vap->iv_bss; len = sizeof(uint32_t) + (sizeof(ndis_wlan_bssid_ex) * 16); bl = malloc(len, M_TEMP, M_NOWAIT | M_ZERO); if (bl == NULL) return (ENOMEM); error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len); if (error == ENOSPC) { free(bl, M_TEMP); bl = malloc(len, M_TEMP, M_NOWAIT | M_ZERO); if (bl == NULL) return (ENOMEM); error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len); } if (error) { free(bl, M_TEMP); device_printf(sc->ndis_dev, "bssid_list failed\n"); return (error); } bs = (ndis_wlan_bssid_ex *)&bl->nblx_bssid[0]; for (i = 0; i < bl->nblx_items; i++) { if (bcmp(bs->nwbx_macaddr, bssid, sizeof(bssid)) == 0) { *assoc = malloc(bs->nwbx_len, M_TEMP, M_NOWAIT); if (*assoc == NULL) { free(bl, M_TEMP); return(ENOMEM); } bcopy((char *)bs, (char *)*assoc, bs->nwbx_len); free(bl, M_TEMP); if (ic->ic_opmode == IEEE80211_M_STA) ni->ni_associd = 1 | 0xc000; /* fake associd */ return(0); } bs = (ndis_wlan_bssid_ex *)((char *)bs + bs->nwbx_len); } free(bl, M_TEMP); return(ENOENT); } static void ndis_getstate_80211(sc) struct ndis_softc *sc; { struct ieee80211com *ic; struct ieee80211vap *vap; struct ieee80211_node *ni; ndis_wlan_bssid_ex *bs; int rval, len, i = 0; int chanflag; uint32_t arg; struct ifnet *ifp; ifp = sc->ifp; ic = ifp->if_l2com; vap = TAILQ_FIRST(&ic->ic_vaps); ni = vap->iv_bss; if (!NDIS_INITIALIZED(sc)) return; if ((rval = ndis_get_assoc(sc, &bs)) != 0) return; /* We're associated, retrieve info on the current bssid. */ ic->ic_curmode = ndis_nettype_mode(bs->nwbx_nettype); chanflag = ndis_nettype_chan(bs->nwbx_nettype); IEEE80211_ADDR_COPY(ni->ni_bssid, bs->nwbx_macaddr); /* Get SSID from current association info. */ bcopy(bs->nwbx_ssid.ns_ssid, ni->ni_essid, bs->nwbx_ssid.ns_ssidlen); ni->ni_esslen = bs->nwbx_ssid.ns_ssidlen; len = sizeof(arg); rval = ndis_get_info(sc, OID_GEN_LINK_SPEED, &arg, &len); if (rval) device_printf (sc->ndis_dev, "get link speed failed: %d\n", rval); if (isset(ic->ic_modecaps, IEEE80211_MODE_11B)) { ni->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B]; for (i = 0; i < ni->ni_rates.rs_nrates; i++) { if ((ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) == arg / 5000) break; } } if (i == ni->ni_rates.rs_nrates && isset(ic->ic_modecaps, IEEE80211_MODE_11G)) { ni->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11G]; for (i = 0; i < ni->ni_rates.rs_nrates; i++) { if ((ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) == arg / 5000) break; } } if (i == ni->ni_rates.rs_nrates) device_printf(sc->ndis_dev, "no matching rate for: %d\n", arg / 5000); else ni->ni_txrate = i; if (ic->ic_caps & IEEE80211_C_PMGT) { len = sizeof(arg); rval = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &len); if (rval) device_printf(sc->ndis_dev, "get power mode failed: %d\n", rval); if (arg == NDIS_80211_POWERMODE_CAM) ic->ic_flags &= ~IEEE80211_F_PMGTON; else ic->ic_flags |= IEEE80211_F_PMGTON; } /* * Use the current association information to reflect * what channel we're on. */ ic->ic_curchan = ieee80211_find_channel(ic, bs->nwbx_config.nc_dsconfig / 1000, chanflag); if (ic->ic_curchan == NULL) ic->ic_curchan = &ic->ic_channels[0]; ni->ni_chan = ic->ic_curchan; ic->ic_bsschan = ic->ic_curchan; free(bs, M_TEMP); /* * Determine current authentication mode. Note: authmode * reporting isn't supported prior to FreeBSD 6.x. */ len = sizeof(arg); rval = ndis_get_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len); if (rval) device_printf (sc->ndis_dev, "get authmode status failed: %d\n", rval); else { ic->ic_flags &= ~IEEE80211_F_WPA; switch(arg) { case NDIS_80211_AUTHMODE_OPEN: ni->ni_authmode = IEEE80211_AUTH_OPEN; break; case NDIS_80211_AUTHMODE_SHARED: ni->ni_authmode = IEEE80211_AUTH_SHARED; break; case NDIS_80211_AUTHMODE_AUTO: ni->ni_authmode = IEEE80211_AUTH_AUTO; break; case NDIS_80211_AUTHMODE_WPA: case NDIS_80211_AUTHMODE_WPAPSK: case NDIS_80211_AUTHMODE_WPANONE: ni->ni_authmode = IEEE80211_AUTH_WPA; ic->ic_flags |= IEEE80211_F_WPA1; break; case NDIS_80211_AUTHMODE_WPA2: case NDIS_80211_AUTHMODE_WPA2PSK: ni->ni_authmode = IEEE80211_AUTH_WPA; ic->ic_flags |= IEEE80211_F_WPA2; break; default: ni->ni_authmode = IEEE80211_AUTH_NONE; break; } } len = sizeof(arg); rval = ndis_get_info(sc, OID_802_11_WEP_STATUS, &arg, &len); if (rval) device_printf (sc->ndis_dev, "get wep status failed: %d\n", rval); if (arg == NDIS_80211_WEPSTAT_ENABLED) ic->ic_flags |= IEEE80211_F_PRIVACY|IEEE80211_F_DROPUNENC; else ic->ic_flags &= ~(IEEE80211_F_PRIVACY|IEEE80211_F_DROPUNENC); return; } static int ndis_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct ndis_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; int i, error = 0; /*NDIS_LOCK(sc);*/ switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING && ifp->if_flags & IFF_PROMISC && !(sc->ndis_if_flags & IFF_PROMISC)) { sc->ndis_filter |= NDIS_PACKET_TYPE_PROMISCUOUS; i = sizeof(sc->ndis_filter); error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, &sc->ndis_filter, &i); } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && !(ifp->if_flags & IFF_PROMISC) && sc->ndis_if_flags & IFF_PROMISC) { sc->ndis_filter &= ~NDIS_PACKET_TYPE_PROMISCUOUS; i = sizeof(sc->ndis_filter); error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, &sc->ndis_filter, &i); } else ndis_init(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) ndis_stop(sc); } sc->ndis_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: ndis_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); break; case SIOCSIFCAP: ifp->if_capenable = ifr->ifr_reqcap; if (ifp->if_capenable & IFCAP_TXCSUM) ifp->if_hwassist = sc->ndis_hwassist; else ifp->if_hwassist = 0; ndis_set_offload(sc); break; default: error = ether_ioctl(ifp, command, data); break; } /*NDIS_UNLOCK(sc);*/ return(error); } static int ndis_ioctl_80211(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct ndis_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; struct ndis_oid_data oid; struct ndis_evt evt; void *oidbuf; int error = 0; switch(command) { case SIOCSIFFLAGS: /*NDIS_LOCK(sc);*/ if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) ndis_init(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) ndis_stop(sc); } sc->ndis_if_flags = ifp->if_flags; error = 0; /*NDIS_UNLOCK(sc);*/ break; case SIOCGDRVSPEC: if ((error = priv_check(curthread, PRIV_DRIVER))) break; error = copyin(ifr->ifr_data, &oid, sizeof(oid)); if (error) break; oidbuf = malloc(oid.len, M_TEMP, M_NOWAIT|M_ZERO); if (oidbuf == NULL) { error = ENOMEM; break; } error = copyin(ifr->ifr_data + sizeof(oid), oidbuf, oid.len); if (error) { free(oidbuf, M_TEMP); break; } error = ndis_get_info(sc, oid.oid, oidbuf, &oid.len); if (error) { free(oidbuf, M_TEMP); break; } error = copyout(&oid, ifr->ifr_data, sizeof(oid)); if (error) { free(oidbuf, M_TEMP); break; } error = copyout(oidbuf, ifr->ifr_data + sizeof(oid), oid.len); free(oidbuf, M_TEMP); break; case SIOCSDRVSPEC: if ((error = priv_check(curthread, PRIV_DRIVER))) break; error = copyin(ifr->ifr_data, &oid, sizeof(oid)); if (error) break; oidbuf = malloc(oid.len, M_TEMP, M_NOWAIT|M_ZERO); if (oidbuf == NULL) { error = ENOMEM; break; } error = copyin(ifr->ifr_data + sizeof(oid), oidbuf, oid.len); if (error) { free(oidbuf, M_TEMP); break; } error = ndis_set_info(sc, oid.oid, oidbuf, &oid.len); if (error) { free(oidbuf, M_TEMP); break; } error = copyout(&oid, ifr->ifr_data, sizeof(oid)); if (error) { free(oidbuf, M_TEMP); break; } error = copyout(oidbuf, ifr->ifr_data + sizeof(oid), oid.len); free(oidbuf, M_TEMP); break; case SIOCGPRIVATE_0: if ((error = priv_check(curthread, PRIV_DRIVER))) break; NDIS_LOCK(sc); if (sc->ndis_evt[sc->ndis_evtcidx].ne_sts == 0) { error = ENOENT; NDIS_UNLOCK(sc); break; } error = copyin(ifr->ifr_data, &evt, sizeof(evt)); if (error) { NDIS_UNLOCK(sc); break; } if (evt.ne_len < sc->ndis_evt[sc->ndis_evtcidx].ne_len) { error = ENOSPC; NDIS_UNLOCK(sc); break; } error = copyout(&sc->ndis_evt[sc->ndis_evtcidx], ifr->ifr_data, sizeof(uint32_t) * 2); if (error) { NDIS_UNLOCK(sc); break; } if (sc->ndis_evt[sc->ndis_evtcidx].ne_len) { error = copyout(sc->ndis_evt[sc->ndis_evtcidx].ne_buf, ifr->ifr_data + (sizeof(uint32_t) * 2), sc->ndis_evt[sc->ndis_evtcidx].ne_len); if (error) { NDIS_UNLOCK(sc); break; } free(sc->ndis_evt[sc->ndis_evtcidx].ne_buf, M_TEMP); sc->ndis_evt[sc->ndis_evtcidx].ne_buf = NULL; } sc->ndis_evt[sc->ndis_evtcidx].ne_len = 0; sc->ndis_evt[sc->ndis_evtcidx].ne_sts = 0; NDIS_EVTINC(sc->ndis_evtcidx); NDIS_UNLOCK(sc); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, command); break; case SIOCGIFADDR: error = ether_ioctl(ifp, command, data); break; default: error = EINVAL; break; } return(error); } int ndis_del_key(vap, key) struct ieee80211vap *vap; const struct ieee80211_key *key; { struct ndis_softc *sc; ndis_80211_key rkey; int len, error = 0; sc = vap->iv_ic->ic_ifp->if_softc; bzero((char *)&rkey, sizeof(rkey)); len = sizeof(rkey); rkey.nk_len = len; rkey.nk_keyidx = key->wk_keyix; bcopy(vap->iv_ifp->if_broadcastaddr, rkey.nk_bssid, IEEE80211_ADDR_LEN); error = ndis_set_info(sc, OID_802_11_REMOVE_KEY, &rkey, &len); if (error) return(0); return(1); } /* * In theory this could be called for any key, but we'll * only use it for WPA TKIP or AES keys. These need to be * set after initial authentication with the AP. */ static int ndis_add_key(vap, key, mac) struct ieee80211vap *vap; const struct ieee80211_key *key; const uint8_t mac[IEEE80211_ADDR_LEN]; { struct ndis_softc *sc; struct ifnet *ifp; ndis_80211_key rkey; int len, error = 0; ifp = vap->iv_ic->ic_ifp; sc = ifp->if_softc; switch (key->wk_cipher->ic_cipher) { case IEEE80211_CIPHER_TKIP: len = sizeof(ndis_80211_key); bzero((char *)&rkey, sizeof(rkey)); rkey.nk_len = len; rkey.nk_keylen = key->wk_keylen; if (key->wk_flags & IEEE80211_KEY_SWMIC) rkey.nk_keylen += 16; /* key index - gets weird in NDIS */ if (key->wk_keyix != IEEE80211_KEYIX_NONE) rkey.nk_keyidx = key->wk_keyix; else rkey.nk_keyidx = 0; if (key->wk_flags & IEEE80211_KEY_XMIT) rkey.nk_keyidx |= 1 << 31; if (key->wk_flags & IEEE80211_KEY_GROUP) { bcopy(ifp->if_broadcastaddr, rkey.nk_bssid, IEEE80211_ADDR_LEN); } else { bcopy(vap->iv_bss->ni_bssid, rkey.nk_bssid, IEEE80211_ADDR_LEN); /* pairwise key */ rkey.nk_keyidx |= 1 << 30; } /* need to set bit 29 based on keyrsc */ rkey.nk_keyrsc = key->wk_keyrsc[0]; /* XXX need tid */ if (rkey.nk_keyrsc) rkey.nk_keyidx |= 1 << 29; if (key->wk_flags & IEEE80211_KEY_SWMIC) { bcopy(key->wk_key, rkey.nk_keydata, 16); bcopy(key->wk_key + 24, rkey.nk_keydata + 16, 8); bcopy(key->wk_key + 16, rkey.nk_keydata + 24, 8); } else bcopy(key->wk_key, rkey.nk_keydata, key->wk_keylen); error = ndis_set_info(sc, OID_802_11_ADD_KEY, &rkey, &len); break; case IEEE80211_CIPHER_WEP: error = 0; break; /* * I don't know how to set up keys for the AES * cipher yet. Is it the same as TKIP? */ case IEEE80211_CIPHER_AES_CCM: default: error = ENOTTY; break; } /* We need to return 1 for success, 0 for failure. */ if (error) return(0); return (1); } static void ndis_resettask(d, arg) device_object *d; void *arg; { struct ndis_softc *sc; sc = arg; ndis_reset_nic(sc); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void ndis_stop(sc) struct ndis_softc *sc; { struct ifnet *ifp; int i; ifp = sc->ifp; callout_drain(&sc->ndis_stat_callout); NDIS_LOCK(sc); sc->ndis_tx_timer = 0; sc->ndis_link = 0; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); NDIS_UNLOCK(sc); ndis_halt_nic(sc); NDIS_LOCK(sc); for (i = 0; i < NDIS_EVENTS; i++) { if (sc->ndis_evt[i].ne_sts && sc->ndis_evt[i].ne_buf != NULL) free(sc->ndis_evt[i].ne_buf, M_TEMP); sc->ndis_evt[i].ne_sts = 0; sc->ndis_evt[i].ne_len = 0; } sc->ndis_evtcidx = 0; sc->ndis_evtpidx = 0; NDIS_UNLOCK(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ void ndis_shutdown(dev) device_t dev; { struct ndis_softc *sc; sc = device_get_softc(dev); ndis_stop(sc); return; } static int ndis_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ndis_vap *nvp = NDIS_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct ndis_softc *sc = ifp->if_softc; enum ieee80211_state ostate; DPRINTF(("%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate])); ostate = vap->iv_state; vap->iv_state = nstate; switch (nstate) { /* pass on to net80211 */ case IEEE80211_S_INIT: case IEEE80211_S_SCAN: return nvp->newstate(vap, nstate, arg); case IEEE80211_S_ASSOC: if (ostate != IEEE80211_S_AUTH) { - taskqueue_enqueue(sc->ndis_tq, &sc->ndis_assoctask); - return EINPROGRESS; + IEEE80211_UNLOCK(ic); + ndis_auth_and_assoc(sc, vap); + IEEE80211_LOCK(ic); } break; case IEEE80211_S_AUTH: - taskqueue_enqueue(sc->ndis_tq, &sc->ndis_authtask); - return EINPROGRESS; + IEEE80211_UNLOCK(ic); + ndis_auth_and_assoc(sc, vap); + if (vap->iv_state == IEEE80211_S_AUTH) /* XXX */ + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); + IEEE80211_LOCK(ic); + break; default: break; } return (0); } static void ndis_scan(void *arg, int npending) { struct ndis_softc *sc = arg; struct ieee80211com *ic; struct ieee80211vap *vap; struct ieee80211_scan_state *ss; ndis_80211_ssid ssid; int error, len; ic = sc->ifp->if_l2com; ss = ic->ic_scan; vap = TAILQ_FIRST(&ic->ic_vaps); if (!NDIS_INITIALIZED(sc)) { DPRINTF(("%s: scan aborted\n", __func__)); ieee80211_cancel_scan(vap); return; } len = sizeof(ssid); bzero((char *)&ssid, len); if (ss->ss_nssid == 0) ssid.ns_ssidlen = 1; else { /* Perform a directed scan */ ssid.ns_ssidlen = ss->ss_ssid[0].len; bcopy(ss->ss_ssid[0].ssid, ssid.ns_ssid, ssid.ns_ssidlen); } error = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len); if (error) DPRINTF(("%s: set ESSID failed\n", __func__)); len = 0; error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN, NULL, &len); if (error) { DPRINTF(("%s: scan command failed\n", __func__)); ieee80211_cancel_scan(vap); return; } pause("ssidscan", hz * 3); if (!NDIS_INITIALIZED(sc)) /* The interface was downed while we were sleeping */ return; ndis_scan_results(sc); ieee80211_scan_done(vap); } static void ndis_scan_results(struct ndis_softc *sc) { struct ieee80211com *ic; struct ieee80211vap *vap; ndis_80211_bssid_list_ex *bl; ndis_wlan_bssid_ex *wb; struct ieee80211_scanparams sp; struct ieee80211_frame wh; struct ieee80211_channel *saved_chan; int i, j; int error, len, rssi, noise, freq, chanflag; static long rstamp; uint8_t ssid[2+IEEE80211_NWID_LEN]; uint8_t rates[2+IEEE80211_RATE_MAXSIZE]; uint8_t *frm, *efrm; ic = sc->ifp->if_l2com; vap = TAILQ_FIRST(&ic->ic_vaps); saved_chan = ic->ic_curchan; noise = -96; len = sizeof(uint32_t) + (sizeof(ndis_wlan_bssid_ex) * 16); bl = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO); if (bl == NULL) return; error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len); if (error == ENOSPC) { free(bl, M_DEVBUF); bl = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO); if (bl == NULL) return; error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len); } if (error) { DPRINTF(("%s: failed to read\n", __func__)); free(bl, M_DEVBUF); return;; } DPRINTF(("%s: %d results\n", __func__, bl->nblx_items)); rstamp++; wb = &bl->nblx_bssid[0]; for (i = 0; i < bl->nblx_items; i++) { memset(&sp, 0, sizeof(sp)); memcpy(wh.i_addr2, wb->nwbx_macaddr, sizeof(wh.i_addr2)); memcpy(wh.i_addr3, wb->nwbx_macaddr, sizeof(wh.i_addr3)); rssi = 100 * (wb->nwbx_rssi - noise) / (-32 - noise); rssi = max(0, min(rssi, 100)); /* limit 0 <= rssi <= 100 */ if (wb->nwbx_privacy) sp.capinfo |= IEEE80211_CAPINFO_PRIVACY; sp.bintval = wb->nwbx_config.nc_beaconperiod; switch (wb->nwbx_netinfra) { case NDIS_80211_NET_INFRA_IBSS: sp.capinfo |= IEEE80211_CAPINFO_IBSS; break; case NDIS_80211_NET_INFRA_BSS: sp.capinfo |= IEEE80211_CAPINFO_ESS; break; } sp.rates = &rates[0]; for (j = 0; j < IEEE80211_RATE_MAXSIZE; j++) { /* XXX - check units */ if (wb->nwbx_supportedrates[j] == 0) break; rates[2 + j] = wb->nwbx_supportedrates[j] & 0x7f; } rates[1] = j; sp.ssid = (uint8_t *)&ssid[0]; memcpy(sp.ssid + 2, &wb->nwbx_ssid.ns_ssid, wb->nwbx_ssid.ns_ssidlen); sp.ssid[1] = wb->nwbx_ssid.ns_ssidlen; chanflag = ndis_nettype_chan(wb->nwbx_nettype); freq = wb->nwbx_config.nc_dsconfig / 1000; sp.chan = sp.bchan = ieee80211_mhz2ieee(freq, chanflag); /* Hack ic->ic_curchan to be in sync with the scan result */ ic->ic_curchan = ieee80211_find_channel(ic, freq, chanflag); if (ic->ic_curchan == NULL) ic->ic_curchan = &ic->ic_channels[0]; /* Process extended info from AP */ if (wb->nwbx_len > sizeof(ndis_wlan_bssid)) { frm = (uint8_t *)&wb->nwbx_ies; efrm = frm + wb->nwbx_ielen; if (efrm - frm < 12) goto done; sp.tstamp = frm; frm += 8; sp.bintval = le16toh(*(uint16_t *)frm); frm += 2; sp.capinfo = le16toh(*(uint16_t *)frm); frm += 2; /* Grab variable length ies */ while (efrm - frm > 1) { if (efrm - frm < frm[1] + 2) break; switch (*frm) { case IEEE80211_ELEMID_RSN: sp.rsn = frm; break; } frm += frm[1] + 2; } } done: DPRINTF(("scan: bssid %s chan %dMHz (%d/%d) rssi %d\n", ether_sprintf(wb->nwbx_macaddr), freq, sp.bchan, chanflag, rssi)); ieee80211_add_scan(vap, &sp, &wh, 0, rssi, noise, rstamp); wb = (ndis_wlan_bssid_ex *)((char *)wb + wb->nwbx_len); } free(bl, M_DEVBUF); /* Restore the channel after messing with it */ ic->ic_curchan = saved_chan; } static void ndis_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct ndis_softc *sc = ifp->if_softc; taskqueue_enqueue(sc->ndis_tq, &sc->ndis_scantask); } static void ndis_set_channel(struct ieee80211com *ic) { /* ignore */ } static void ndis_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { /* ignore */ } static void ndis_scan_mindwell(struct ieee80211_scan_state *ss) { /* NB: don't try to abort scan; wait for firmware to finish */ } static void ndis_scan_end(struct ieee80211com *ic) { /* ignore */ } Index: user/thompsa/vaptq/sys/dev/if_ndis/if_ndisvar.h =================================================================== --- user/thompsa/vaptq/sys/dev/if_ndis/if_ndisvar.h (revision 185732) +++ user/thompsa/vaptq/sys/dev/if_ndis/if_ndisvar.h (revision 185733) @@ -1,189 +1,187 @@ /*- * Copyright (c) 2003 * 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. * * $FreeBSD$ */ #define NDIS_DEFAULT_NODENAME "FreeBSD NDIS node" #define NDIS_NODENAME_LEN 32 /* For setting/getting OIDs from userspace. */ struct ndis_oid_data { uint32_t oid; uint32_t len; #ifdef notdef uint8_t data[1]; #endif }; struct ndis_pci_type { uint16_t ndis_vid; uint16_t ndis_did; uint32_t ndis_subsys; char *ndis_name; }; struct ndis_pccard_type { const char *ndis_vid; const char *ndis_did; char *ndis_name; }; struct ndis_shmem { list_entry ndis_list; bus_dma_tag_t ndis_stag; bus_dmamap_t ndis_smap; void *ndis_saddr; ndis_physaddr ndis_paddr; }; struct ndis_cfglist { ndis_cfg ndis_cfg; struct sysctl_oid *ndis_oid; TAILQ_ENTRY(ndis_cfglist) link; }; /* * Helper struct to make parsing information * elements easier. */ struct ndis_ie { uint8_t ni_oui[3]; uint8_t ni_val; }; TAILQ_HEAD(nch, ndis_cfglist); #define NDIS_INITIALIZED(sc) (sc->ndis_block->nmb_devicectx != NULL) #define NDIS_TXPKTS 64 #define NDIS_INC(x) \ (x)->ndis_txidx = ((x)->ndis_txidx + 1) % (x)->ndis_maxpkts #define NDIS_EVENTS 4 #define NDIS_EVTINC(x) (x) = ((x) + 1) % NDIS_EVENTS struct ndis_evt { uint32_t ne_sts; uint32_t ne_len; char *ne_buf; }; struct ndis_vap { struct ieee80211vap vap; int (*newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define NDIS_VAP(vap) ((struct ndis_vap *)(vap)) struct ndis_softc { struct ifnet *ifp; struct ifmedia ifmedia; /* media info */ u_long ndis_hwassist; uint32_t ndis_v4tx; uint32_t ndis_v4rx; bus_space_handle_t ndis_bhandle; bus_space_tag_t ndis_btag; void *ndis_intrhand; struct resource *ndis_irq; struct resource *ndis_res; struct resource *ndis_res_io; int ndis_io_rid; struct resource *ndis_res_mem; int ndis_mem_rid; struct resource *ndis_res_altmem; int ndis_altmem_rid; struct resource *ndis_res_am; /* attribute mem (pccard) */ int ndis_am_rid; struct resource *ndis_res_cm; /* common mem (pccard) */ struct resource_list ndis_rl; int ndis_rescnt; struct mtx ndis_mtx; uint8_t ndis_irql; device_t ndis_dev; int ndis_unit; ndis_miniport_block *ndis_block; ndis_miniport_characteristics *ndis_chars; interface_type ndis_type; struct callout ndis_stat_callout; int ndis_maxpkts; ndis_oid *ndis_oids; int ndis_oidcnt; int ndis_txidx; int ndis_txpending; ndis_packet **ndis_txarray; ndis_handle ndis_txpool; int ndis_sc; ndis_cfg *ndis_regvals; struct nch ndis_cfglist_head; int ndis_80211; int ndis_link; uint32_t ndis_sts; uint32_t ndis_filter; int ndis_if_flags; int ndis_skip; int ndis_devidx; interface_type ndis_iftype; driver_object *ndis_dobj; io_workitem *ndis_tickitem; io_workitem *ndis_startitem; io_workitem *ndis_resetitem; io_workitem *ndis_inputitem; kdpc ndis_rxdpc; bus_dma_tag_t ndis_parent_tag; list_entry ndis_shlist; bus_dma_tag_t ndis_mtag; bus_dma_tag_t ndis_ttag; bus_dmamap_t *ndis_mmaps; bus_dmamap_t *ndis_tmaps; int ndis_mmapcnt; struct ndis_evt ndis_evt[NDIS_EVENTS]; int ndis_evtpidx; int ndis_evtcidx; struct ifqueue ndis_rxqueue; kspin_lock ndis_rxlock; struct taskqueue *ndis_tq; /* private task queue */ struct task ndis_scantask; - struct task ndis_authtask; - struct task ndis_assoctask; int (*ndis_newstate)(struct ieee80211com *, enum ieee80211_state, int); int ndis_tx_timer; int ndis_hang_timer; }; #define NDIS_LOCK(_sc) mtx_lock(&(_sc)->ndis_mtx) #define NDIS_UNLOCK(_sc) mtx_unlock(&(_sc)->ndis_mtx) Index: user/thompsa/vaptq/sys/dev/ipw/if_ipw.c =================================================================== --- user/thompsa/vaptq/sys/dev/ipw/if_ipw.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/ipw/if_ipw.c (revision 185733) @@ -1,2780 +1,2773 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2004-2006 * Damien Bergamini . All rights reserved. * Copyright (c) 2006 Sam Leffler, Errno Consulting * Copyright (c) 2007 Andrew Thompson * * 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 unmodified, 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); /*- * Intel(R) PRO/Wireless 2100 MiniPCI driver * http://www.intel.com/network/connectivity/products/wireless/prowireless_mobile.htm */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IPW_DEBUG #ifdef IPW_DEBUG #define DPRINTF(x) do { if (ipw_debug > 0) printf x; } while (0) #define DPRINTFN(n, x) do { if (ipw_debug >= (n)) printf x; } while (0) int ipw_debug = 0; SYSCTL_INT(_debug, OID_AUTO, ipw, CTLFLAG_RW, &ipw_debug, 0, "ipw debug level"); #else #define DPRINTF(x) #define DPRINTFN(n, x) #endif MODULE_DEPEND(ipw, pci, 1, 1, 1); MODULE_DEPEND(ipw, wlan, 1, 1, 1); MODULE_DEPEND(ipw, firmware, 1, 1, 1); struct ipw_ident { uint16_t vendor; uint16_t device; const char *name; }; static const struct ipw_ident ipw_ident_table[] = { { 0x8086, 0x1043, "Intel(R) PRO/Wireless 2100 MiniPCI" }, { 0, 0, NULL } }; static struct ieee80211vap *ipw_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void ipw_vap_delete(struct ieee80211vap *); static int ipw_dma_alloc(struct ipw_softc *); static void ipw_release(struct ipw_softc *); static void ipw_media_status(struct ifnet *, struct ifmediareq *); static int ipw_newstate(struct ieee80211vap *, enum ieee80211_state, int); static uint16_t ipw_read_prom_word(struct ipw_softc *, uint8_t); static void ipw_rx_cmd_intr(struct ipw_softc *, struct ipw_soft_buf *); static void ipw_assocsuccess(void *, int); static void ipw_assocfailed(void *, int); static void ipw_scandone(void *, int); static void ipw_bmiss(void *, int); static void ipw_rx_newstate_intr(struct ipw_softc *, struct ipw_soft_buf *); static void ipw_rx_data_intr(struct ipw_softc *, struct ipw_status *, struct ipw_soft_bd *, struct ipw_soft_buf *); static void ipw_rx_intr(struct ipw_softc *); static void ipw_release_sbd(struct ipw_softc *, struct ipw_soft_bd *); static void ipw_tx_intr(struct ipw_softc *); static void ipw_intr(void *); static void ipw_dma_map_addr(void *, bus_dma_segment_t *, int, int); static const char * ipw_cmdname(int); static int ipw_cmd(struct ipw_softc *, uint32_t, void *, uint32_t); static int ipw_tx_start(struct ifnet *, struct mbuf *, struct ieee80211_node *); static int ipw_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void ipw_start(struct ifnet *); static void ipw_start_locked(struct ifnet *); static void ipw_watchdog(void *); static int ipw_ioctl(struct ifnet *, u_long, caddr_t); static void ipw_stop_master(struct ipw_softc *); static int ipw_enable(struct ipw_softc *); static int ipw_disable(struct ipw_softc *); static int ipw_reset(struct ipw_softc *); static int ipw_load_ucode(struct ipw_softc *, const char *, int); static int ipw_load_firmware(struct ipw_softc *, const char *, int); static int ipw_config(struct ipw_softc *); -static void ipw_assoc_task(void *, int); -static void ipw_disassoc_task(void *, int); +static void ipw_assoc(struct ieee80211com *, struct ieee80211vap *); +static void ipw_disassoc(struct ieee80211com *, struct ieee80211vap *); static void ipw_init_task(void *, int); static void ipw_init(void *); static void ipw_init_locked(struct ipw_softc *); static void ipw_stop(void *); static void ipw_stop_locked(struct ipw_softc *); static int ipw_sysctl_stats(SYSCTL_HANDLER_ARGS); static int ipw_sysctl_radio(SYSCTL_HANDLER_ARGS); static uint32_t ipw_read_table1(struct ipw_softc *, uint32_t); static void ipw_write_table1(struct ipw_softc *, uint32_t, uint32_t); #if 0 static int ipw_read_table2(struct ipw_softc *, uint32_t, void *, uint32_t *); static void ipw_read_mem_1(struct ipw_softc *, bus_size_t, uint8_t *, bus_size_t); #endif static void ipw_write_mem_1(struct ipw_softc *, bus_size_t, const uint8_t *, bus_size_t); static void ipw_scan_task(void *, int); static int ipw_scan(struct ipw_softc *); static void ipw_scan_start(struct ieee80211com *); static void ipw_scan_end(struct ieee80211com *); static void ipw_set_channel(struct ieee80211com *); static void ipw_scan_curchan(struct ieee80211_scan_state *, unsigned long maxdwell); static void ipw_scan_mindwell(struct ieee80211_scan_state *); static int ipw_probe(device_t); static int ipw_attach(device_t); static int ipw_detach(device_t); static int ipw_shutdown(device_t); static int ipw_suspend(device_t); static int ipw_resume(device_t); static device_method_t ipw_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ipw_probe), DEVMETHOD(device_attach, ipw_attach), DEVMETHOD(device_detach, ipw_detach), DEVMETHOD(device_shutdown, ipw_shutdown), DEVMETHOD(device_suspend, ipw_suspend), DEVMETHOD(device_resume, ipw_resume), { 0, 0 } }; static driver_t ipw_driver = { "ipw", ipw_methods, sizeof (struct ipw_softc) }; static devclass_t ipw_devclass; DRIVER_MODULE(ipw, pci, ipw_driver, ipw_devclass, 0, 0); DRIVER_MODULE(ipw, cardbus, ipw_driver, ipw_devclass, 0, 0); static int ipw_probe(device_t dev) { const struct ipw_ident *ident; for (ident = ipw_ident_table; ident->name != NULL; ident++) { if (pci_get_vendor(dev) == ident->vendor && pci_get_device(dev) == ident->device) { device_set_desc(dev, ident->name); return 0; } } return ENXIO; } /* Base Address Register */ #define IPW_PCI_BAR0 0x10 static int ipw_attach(device_t dev) { struct ipw_softc *sc = device_get_softc(dev); struct ifnet *ifp; struct ieee80211com *ic; struct ieee80211_channel *c; uint16_t val; int error, i; sc->sc_dev = dev; mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); TASK_INIT(&sc->sc_init_task, 0, ipw_init_task, sc); TASK_INIT(&sc->sc_scan_task, 0, ipw_scan_task, sc); TASK_INIT(&sc->sc_bmiss_task, 0, ipw_bmiss, sc); callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0); if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { device_printf(dev, "chip is in D%d power mode " "-- setting to D0\n", pci_get_powerstate(dev)); pci_set_powerstate(dev, PCI_POWERSTATE_D0); } pci_write_config(dev, 0x41, 0, 1); /* enable bus-mastering */ pci_enable_busmaster(dev); sc->mem_rid = IPW_PCI_BAR0; sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem == NULL) { device_printf(dev, "could not allocate memory resource\n"); goto fail; } sc->sc_st = rman_get_bustag(sc->mem); sc->sc_sh = rman_get_bushandle(sc->mem); sc->irq_rid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->irq == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); goto fail1; } if (ipw_reset(sc) != 0) { device_printf(dev, "could not reset adapter\n"); goto fail2; } if (ipw_dma_alloc(sc) != 0) { device_printf(dev, "could not allocate DMA resources\n"); goto fail2; } ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); goto fail3; } ic = ifp->if_l2com; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = ipw_init; ifp->if_ioctl = ipw_ioctl; ifp->if_start = ipw_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ic->ic_ifp = ifp; ic->ic_opmode = IEEE80211_M_STA; ic->ic_phytype = IEEE80211_T_DS; /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_PMGT /* power save supported */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_WPA /* 802.11i supported */ ; /* read MAC address from EEPROM */ val = ipw_read_prom_word(sc, IPW_EEPROM_MAC + 0); ic->ic_myaddr[0] = val >> 8; ic->ic_myaddr[1] = val & 0xff; val = ipw_read_prom_word(sc, IPW_EEPROM_MAC + 1); ic->ic_myaddr[2] = val >> 8; ic->ic_myaddr[3] = val & 0xff; val = ipw_read_prom_word(sc, IPW_EEPROM_MAC + 2); ic->ic_myaddr[4] = val >> 8; ic->ic_myaddr[5] = val & 0xff; /* set supported .11b channels (read from EEPROM) */ if ((val = ipw_read_prom_word(sc, IPW_EEPROM_CHANNEL_LIST)) == 0) val = 0x7ff; /* default to channels 1-11 */ val <<= 1; for (i = 1; i < 16; i++) { if (val & (1 << i)) { c = &ic->ic_channels[ic->ic_nchans++]; c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); c->ic_flags = IEEE80211_CHAN_B; c->ic_ieee = i; } } /* check support for radio transmitter switch in EEPROM */ if (!(ipw_read_prom_word(sc, IPW_EEPROM_RADIO) & 8)) sc->flags |= IPW_FLAG_HAS_RADIO_SWITCH; ieee80211_ifattach(ic); ic->ic_scan_start = ipw_scan_start; ic->ic_scan_end = ipw_scan_end; ic->ic_set_channel = ipw_set_channel; ic->ic_scan_curchan = ipw_scan_curchan; ic->ic_scan_mindwell = ipw_scan_mindwell; ic->ic_raw_xmit = ipw_raw_xmit; ic->ic_vap_create = ipw_vap_create; ic->ic_vap_delete = ipw_vap_delete; bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap)); sc->sc_rxtap_len = sizeof sc->sc_rxtap; sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(IPW_RX_RADIOTAP_PRESENT); sc->sc_txtap_len = sizeof sc->sc_txtap; sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(IPW_TX_RADIOTAP_PRESENT); /* * Add a few sysctl knobs. */ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "radio", CTLTYPE_INT | CTLFLAG_RD, sc, 0, ipw_sysctl_radio, "I", "radio transmitter switch state (0=off, 1=on)"); SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "stats", CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, ipw_sysctl_stats, "S", "statistics"); /* * Hook our interrupt after all initialization is complete. */ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, ipw_intr, sc, &sc->sc_ih); if (error != 0) { device_printf(dev, "could not set up interrupt\n"); goto fail4; } if (bootverbose) ieee80211_announce(ic); return 0; fail4: if_free(ifp); fail3: ipw_release(sc); fail2: bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); fail1: bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); fail: mtx_destroy(&sc->sc_mtx); return ENXIO; } static int ipw_detach(device_t dev) { struct ipw_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; ipw_stop(sc); bpfdetach(ifp); ieee80211_ifdetach(ic); callout_drain(&sc->sc_wdtimer); taskqueue_drain(taskqueue_fast, &sc->sc_init_task); taskqueue_drain(taskqueue_fast, &sc->sc_scan_task); taskqueue_drain(taskqueue_fast, &sc->sc_bmiss_task); ipw_release(sc); bus_teardown_intr(dev, sc->irq, sc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); if_free(ifp); if (sc->sc_firmware != NULL) { firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); sc->sc_firmware = NULL; } mtx_destroy(&sc->sc_mtx); return 0; } static struct ieee80211vap * ipw_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ifnet *ifp = ic->ic_ifp; struct ipw_softc *sc = ifp->if_softc; struct ipw_vap *ivp; struct ieee80211vap *vap; const struct firmware *fp; const struct ipw_firmware_hdr *hdr; const char *imagename; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; switch (opmode) { case IEEE80211_M_STA: imagename = "ipw_bss"; break; case IEEE80211_M_IBSS: imagename = "ipw_ibss"; break; case IEEE80211_M_MONITOR: imagename = "ipw_monitor"; break; default: return NULL; } /* * Load firmware image using the firmware(9) subsystem. Doing * this unlocked is ok since we're single-threaded by the * 802.11 layer. */ if (sc->sc_firmware == NULL || strcmp(sc->sc_firmware->name, imagename) != 0) { if (sc->sc_firmware != NULL) firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); sc->sc_firmware = firmware_get(imagename); } if (sc->sc_firmware == NULL) { device_printf(sc->sc_dev, "could not load firmware image '%s'\n", imagename); return NULL; } fp = sc->sc_firmware; if (fp->datasize < sizeof *hdr) { device_printf(sc->sc_dev, "firmware image too short %zu\n", fp->datasize); firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); sc->sc_firmware = NULL; return NULL; } hdr = (const struct ipw_firmware_hdr *)fp->data; if (fp->datasize < sizeof *hdr + le32toh(hdr->mainsz) + le32toh(hdr->ucodesz)) { device_printf(sc->sc_dev, "firmware image too short %zu\n", fp->datasize); firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); sc->sc_firmware = NULL; return NULL; } ivp = (struct ipw_vap *) malloc(sizeof(struct ipw_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (ivp == NULL) return NULL; vap = &ivp->vap; - TASK_INIT(&ivp->assoc_task, 0, ipw_assoc_task, vap); - TASK_INIT(&ivp->disassoc_task, 0, ipw_disassoc_task, vap); TASK_INIT(&ivp->assoc_success_task, 0, ipw_assocsuccess, vap); TASK_INIT(&ivp->assoc_failed_task, 0, ipw_assocfailed, vap); TASK_INIT(&ivp->scandone_task, 0, ipw_scandone, vap); ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); /* override with driver methods */ ivp->newstate = vap->iv_newstate; vap->iv_newstate = ipw_newstate; /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ipw_media_status); ic->ic_opmode = opmode; return vap; } static void ipw_vap_delete(struct ieee80211vap *vap) { struct ipw_vap *ivp = IPW_VAP(vap); ieee80211_vap_detach(vap); free(ivp, M_80211_VAP); } static int ipw_dma_alloc(struct ipw_softc *sc) { struct ipw_soft_bd *sbd; struct ipw_soft_hdr *shdr; struct ipw_soft_buf *sbuf; bus_addr_t physaddr; int error, i; /* * Allocate and map tx ring. */ error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, IPW_TBD_SZ, 1, IPW_TBD_SZ, 0, NULL, NULL, &sc->tbd_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create tx ring DMA tag\n"); goto fail; } error = bus_dmamem_alloc(sc->tbd_dmat, (void **)&sc->tbd_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->tbd_map); if (error != 0) { device_printf(sc->sc_dev, "could not allocate tx ring DMA memory\n"); goto fail; } error = bus_dmamap_load(sc->tbd_dmat, sc->tbd_map, sc->tbd_list, IPW_TBD_SZ, ipw_dma_map_addr, &sc->tbd_phys, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map tx ring DMA memory\n"); goto fail; } /* * Allocate and map rx ring. */ error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, IPW_RBD_SZ, 1, IPW_RBD_SZ, 0, NULL, NULL, &sc->rbd_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create rx ring DMA tag\n"); goto fail; } error = bus_dmamem_alloc(sc->rbd_dmat, (void **)&sc->rbd_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->rbd_map); if (error != 0) { device_printf(sc->sc_dev, "could not allocate rx ring DMA memory\n"); goto fail; } error = bus_dmamap_load(sc->rbd_dmat, sc->rbd_map, sc->rbd_list, IPW_RBD_SZ, ipw_dma_map_addr, &sc->rbd_phys, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map rx ring DMA memory\n"); goto fail; } /* * Allocate and map status ring. */ error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, IPW_STATUS_SZ, 1, IPW_STATUS_SZ, 0, NULL, NULL, &sc->status_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create status ring DMA tag\n"); goto fail; } error = bus_dmamem_alloc(sc->status_dmat, (void **)&sc->status_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->status_map); if (error != 0) { device_printf(sc->sc_dev, "could not allocate status ring DMA memory\n"); goto fail; } error = bus_dmamap_load(sc->status_dmat, sc->status_map, sc->status_list, IPW_STATUS_SZ, ipw_dma_map_addr, &sc->status_phys, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map status ring DMA memory\n"); goto fail; } /* * Allocate command DMA map. */ error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, sizeof (struct ipw_cmd), 1, sizeof (struct ipw_cmd), 0, NULL, NULL, &sc->cmd_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create command DMA tag\n"); goto fail; } error = bus_dmamap_create(sc->cmd_dmat, 0, &sc->cmd_map); if (error != 0) { device_printf(sc->sc_dev, "could not create command DMA map\n"); goto fail; } /* * Allocate headers DMA maps. */ error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, sizeof (struct ipw_hdr), 1, sizeof (struct ipw_hdr), 0, NULL, NULL, &sc->hdr_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create header DMA tag\n"); goto fail; } SLIST_INIT(&sc->free_shdr); for (i = 0; i < IPW_NDATA; i++) { shdr = &sc->shdr_list[i]; error = bus_dmamap_create(sc->hdr_dmat, 0, &shdr->map); if (error != 0) { device_printf(sc->sc_dev, "could not create header DMA map\n"); goto fail; } SLIST_INSERT_HEAD(&sc->free_shdr, shdr, next); } /* * Allocate tx buffers DMA maps. */ error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, IPW_MAX_NSEG, MCLBYTES, 0, NULL, NULL, &sc->txbuf_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create tx DMA tag\n"); goto fail; } SLIST_INIT(&sc->free_sbuf); for (i = 0; i < IPW_NDATA; i++) { sbuf = &sc->tx_sbuf_list[i]; error = bus_dmamap_create(sc->txbuf_dmat, 0, &sbuf->map); if (error != 0) { device_printf(sc->sc_dev, "could not create tx DMA map\n"); goto fail; } SLIST_INSERT_HEAD(&sc->free_sbuf, sbuf, next); } /* * Initialize tx ring. */ for (i = 0; i < IPW_NTBD; i++) { sbd = &sc->stbd_list[i]; sbd->bd = &sc->tbd_list[i]; sbd->type = IPW_SBD_TYPE_NOASSOC; } /* * Pre-allocate rx buffers and DMA maps. */ error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL, NULL, &sc->rxbuf_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create rx DMA tag\n"); goto fail; } for (i = 0; i < IPW_NRBD; i++) { sbd = &sc->srbd_list[i]; sbuf = &sc->rx_sbuf_list[i]; sbd->bd = &sc->rbd_list[i]; sbuf->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (sbuf->m == NULL) { device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); error = ENOMEM; goto fail; } error = bus_dmamap_create(sc->rxbuf_dmat, 0, &sbuf->map); if (error != 0) { device_printf(sc->sc_dev, "could not create rx DMA map\n"); goto fail; } error = bus_dmamap_load(sc->rxbuf_dmat, sbuf->map, mtod(sbuf->m, void *), MCLBYTES, ipw_dma_map_addr, &physaddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map rx DMA memory\n"); goto fail; } sbd->type = IPW_SBD_TYPE_DATA; sbd->priv = sbuf; sbd->bd->physaddr = htole32(physaddr); sbd->bd->len = htole32(MCLBYTES); } bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE); return 0; fail: ipw_release(sc); return error; } static void ipw_release(struct ipw_softc *sc) { struct ipw_soft_buf *sbuf; int i; if (sc->tbd_dmat != NULL) { if (sc->stbd_list != NULL) { bus_dmamap_unload(sc->tbd_dmat, sc->tbd_map); bus_dmamem_free(sc->tbd_dmat, sc->tbd_list, sc->tbd_map); } bus_dma_tag_destroy(sc->tbd_dmat); } if (sc->rbd_dmat != NULL) { if (sc->rbd_list != NULL) { bus_dmamap_unload(sc->rbd_dmat, sc->rbd_map); bus_dmamem_free(sc->rbd_dmat, sc->rbd_list, sc->rbd_map); } bus_dma_tag_destroy(sc->rbd_dmat); } if (sc->status_dmat != NULL) { if (sc->status_list != NULL) { bus_dmamap_unload(sc->status_dmat, sc->status_map); bus_dmamem_free(sc->status_dmat, sc->status_list, sc->status_map); } bus_dma_tag_destroy(sc->status_dmat); } for (i = 0; i < IPW_NTBD; i++) ipw_release_sbd(sc, &sc->stbd_list[i]); if (sc->cmd_dmat != NULL) { bus_dmamap_destroy(sc->cmd_dmat, sc->cmd_map); bus_dma_tag_destroy(sc->cmd_dmat); } if (sc->hdr_dmat != NULL) { for (i = 0; i < IPW_NDATA; i++) bus_dmamap_destroy(sc->hdr_dmat, sc->shdr_list[i].map); bus_dma_tag_destroy(sc->hdr_dmat); } if (sc->txbuf_dmat != NULL) { for (i = 0; i < IPW_NDATA; i++) { bus_dmamap_destroy(sc->txbuf_dmat, sc->tx_sbuf_list[i].map); } bus_dma_tag_destroy(sc->txbuf_dmat); } if (sc->rxbuf_dmat != NULL) { for (i = 0; i < IPW_NRBD; i++) { sbuf = &sc->rx_sbuf_list[i]; if (sbuf->m != NULL) { bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rxbuf_dmat, sbuf->map); m_freem(sbuf->m); } bus_dmamap_destroy(sc->rxbuf_dmat, sbuf->map); } bus_dma_tag_destroy(sc->rxbuf_dmat); } } static int ipw_shutdown(device_t dev) { struct ipw_softc *sc = device_get_softc(dev); ipw_stop(sc); return 0; } static int ipw_suspend(device_t dev) { struct ipw_softc *sc = device_get_softc(dev); ipw_stop(sc); return 0; } static int ipw_resume(device_t dev) { struct ipw_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; pci_write_config(dev, 0x41, 0, 1); if (ifp->if_flags & IFF_UP) ipw_init(sc); return 0; } static int ipw_cvtrate(int ipwrate) { switch (ipwrate) { case IPW_RATE_DS1: return 2; case IPW_RATE_DS2: return 4; case IPW_RATE_DS5: return 11; case IPW_RATE_DS11: return 22; } return 0; } /* * The firmware automatically adapts the transmit speed. We report its current * value here. */ static void ipw_media_status(struct ifnet *ifp, struct ifmediareq *imr) { struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; struct ipw_softc *sc = ic->ic_ifp->if_softc; /* read current transmission rate from adapter */ vap->iv_bss->ni_txrate = ipw_cvtrate( ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf); ieee80211_media_status(ifp, imr); } static int ipw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ipw_vap *ivp = IPW_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct ipw_softc *sc = ifp->if_softc; + enum ieee80211_state ostate; DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate], sc->flags)); + ostate = vap->iv_state; + IEEE80211_UNLOCK(ic); + switch (nstate) { case IEEE80211_S_RUN: if (ic->ic_opmode == IEEE80211_M_IBSS) { /* * XXX when joining an ibss network we are called * with a SCAN -> RUN transition on scan complete. * Use that to call ipw_auth_and_assoc. On completing * the join we are then called again with an * AUTH -> RUN transition and we want to do nothing. * This is all totally bogus and needs to be redone. */ - if (vap->iv_state == IEEE80211_S_SCAN) { - taskqueue_enqueue(taskqueue_swi, - &IPW_VAP(vap)->assoc_task); - return EINPROGRESS; - } + if (ostate == IEEE80211_S_SCAN) + ipw_assoc(ic, vap); } break; case IEEE80211_S_INIT: if (sc->flags & IPW_FLAG_ASSOCIATED) - taskqueue_enqueue(taskqueue_swi, - &IPW_VAP(vap)->disassoc_task); + ipw_disassoc(ic, vap); break; case IEEE80211_S_AUTH: - taskqueue_enqueue(taskqueue_swi, &IPW_VAP(vap)->assoc_task); - return EINPROGRESS; + ipw_assoc(ic, vap); + break; case IEEE80211_S_ASSOC: /* * If we are not transitioning from AUTH the resend the * association request. */ - if (vap->iv_state != IEEE80211_S_AUTH) { - taskqueue_enqueue(taskqueue_swi, - &IPW_VAP(vap)->assoc_task); - return EINPROGRESS; - } + if (ostate != IEEE80211_S_AUTH) + ipw_assoc(ic, vap); break; default: break; } + IEEE80211_LOCK(ic); return ivp->newstate(vap, nstate, arg); } /* * Read 16 bits at address 'addr' from the serial EEPROM. */ static uint16_t ipw_read_prom_word(struct ipw_softc *sc, uint8_t addr) { uint32_t tmp; uint16_t val; int n; /* clock C once before the first command */ IPW_EEPROM_CTL(sc, 0); IPW_EEPROM_CTL(sc, IPW_EEPROM_S); IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_C); IPW_EEPROM_CTL(sc, IPW_EEPROM_S); /* write start bit (1) */ IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D); IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D | IPW_EEPROM_C); /* write READ opcode (10) */ IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D); IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D | IPW_EEPROM_C); IPW_EEPROM_CTL(sc, IPW_EEPROM_S); IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_C); /* write address A7-A0 */ for (n = 7; n >= 0; n--) { IPW_EEPROM_CTL(sc, IPW_EEPROM_S | (((addr >> n) & 1) << IPW_EEPROM_SHIFT_D)); IPW_EEPROM_CTL(sc, IPW_EEPROM_S | (((addr >> n) & 1) << IPW_EEPROM_SHIFT_D) | IPW_EEPROM_C); } IPW_EEPROM_CTL(sc, IPW_EEPROM_S); /* read data Q15-Q0 */ val = 0; for (n = 15; n >= 0; n--) { IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_C); IPW_EEPROM_CTL(sc, IPW_EEPROM_S); tmp = MEM_READ_4(sc, IPW_MEM_EEPROM_CTL); val |= ((tmp & IPW_EEPROM_Q) >> IPW_EEPROM_SHIFT_Q) << n; } IPW_EEPROM_CTL(sc, 0); /* clear Chip Select and clock C */ IPW_EEPROM_CTL(sc, IPW_EEPROM_S); IPW_EEPROM_CTL(sc, 0); IPW_EEPROM_CTL(sc, IPW_EEPROM_C); return le16toh(val); } static void ipw_rx_cmd_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) { struct ipw_cmd *cmd; bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD); cmd = mtod(sbuf->m, struct ipw_cmd *); DPRINTFN(9, ("cmd ack'ed %s(%u, %u, %u, %u, %u)\n", ipw_cmdname(le32toh(cmd->type)), le32toh(cmd->type), le32toh(cmd->subtype), le32toh(cmd->seq), le32toh(cmd->len), le32toh(cmd->status))); sc->flags &= ~IPW_FLAG_BUSY; wakeup(sc); } static void ipw_assocsuccess(void *arg, int npending) { struct ieee80211vap *vap = arg; ieee80211_new_state(vap, IEEE80211_S_RUN, -1); } static void ipw_assocfailed(void *arg, int npending) { struct ieee80211vap *vap = arg; ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); } static void ipw_scandone(void *arg, int npending) { struct ieee80211vap *vap = arg; ieee80211_scan_done(vap); } static void ipw_bmiss(void *arg, int npending) { struct ieee80211com *ic = arg; ieee80211_beacon_miss(ic); } static void ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) { #define IEEESTATE(vap) ieee80211_state_name[vap->iv_state] struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t state; bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD); state = le32toh(*mtod(sbuf->m, uint32_t *)); switch (state) { case IPW_STATE_ASSOCIATED: DPRINTFN(2, ("Association succeeded (%s flags 0x%x)\n", IEEESTATE(vap), sc->flags)); /* XXX suppress state change in case the fw auto-associates */ if ((sc->flags & IPW_FLAG_ASSOCIATING) == 0) { DPRINTF(("Unexpected association (%s, flags 0x%x)\n", IEEESTATE(vap), sc->flags)); break; } sc->flags &= ~IPW_FLAG_ASSOCIATING; sc->flags |= IPW_FLAG_ASSOCIATED; taskqueue_enqueue(taskqueue_swi, &IPW_VAP(vap)->assoc_success_task); break; case IPW_STATE_SCANNING: DPRINTFN(3, ("Scanning (%s flags 0x%x)\n", IEEESTATE(vap), sc->flags)); /* * NB: Check driver state for association on assoc * loss as the firmware will immediately start to * scan and we would treat it as a beacon miss if * we checked the 802.11 layer state. */ if (sc->flags & IPW_FLAG_ASSOCIATED) { /* XXX probably need to issue disassoc to fw */ taskqueue_enqueue(taskqueue_swi, &sc->sc_bmiss_task); } break; case IPW_STATE_SCAN_COMPLETE: /* * XXX For some reason scan requests generate scan * started + scan done events before any traffic is * received (e.g. probe response frames). We work * around this by marking the HACK flag and skipping * the first scan complete event. */ DPRINTFN(3, ("Scan complete (%s flags 0x%x)\n", IEEESTATE(vap), sc->flags)); if (sc->flags & IPW_FLAG_HACK) { sc->flags &= ~IPW_FLAG_HACK; break; } if (sc->flags & IPW_FLAG_SCANNING) { taskqueue_enqueue(taskqueue_swi, &IPW_VAP(vap)->scandone_task); sc->flags &= ~IPW_FLAG_SCANNING; sc->sc_scan_timer = 0; } break; case IPW_STATE_ASSOCIATION_LOST: DPRINTFN(2, ("Association lost (%s flags 0x%x)\n", IEEESTATE(vap), sc->flags)); sc->flags &= ~(IPW_FLAG_ASSOCIATING | IPW_FLAG_ASSOCIATED); if (vap->iv_state == IEEE80211_S_RUN) taskqueue_enqueue(taskqueue_swi, &IPW_VAP(vap)->assoc_failed_task); break; case IPW_STATE_DISABLED: /* XXX? is this right? */ sc->flags &= ~(IPW_FLAG_HACK | IPW_FLAG_SCANNING | IPW_FLAG_ASSOCIATING | IPW_FLAG_ASSOCIATED); DPRINTFN(2, ("Firmware disabled (%s flags 0x%x)\n", IEEESTATE(vap), sc->flags)); break; case IPW_STATE_RADIO_DISABLED: device_printf(sc->sc_dev, "radio turned off\n"); ieee80211_notify_radio(ic, 0); ipw_stop_locked(sc); /* XXX start polling thread to detect radio on */ break; default: DPRINTFN(2, ("%s: unhandled state %u %s flags 0x%x\n", __func__, state, IEEESTATE(vap), sc->flags)); break; } #undef IEEESTATE } /* * Set driver state for current channel. */ static void ipw_setcurchan(struct ipw_softc *sc, struct ieee80211_channel *chan) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; ic->ic_curchan = chan; sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq = htole16(ic->ic_curchan->ic_freq); sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags = htole16(ic->ic_curchan->ic_flags); } /* * XXX: Hack to set the current channel to the value advertised in beacons or * probe responses. Only used during AP detection. */ static void ipw_fix_channel(struct ipw_softc *sc, struct mbuf *m) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_channel *c; struct ieee80211_frame *wh; uint8_t subtype; uint8_t *frm, *efrm; wh = mtod(m, struct ieee80211_frame *); if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) return; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) return; /* XXX use ieee80211_parse_beacon */ frm = (uint8_t *)(wh + 1); efrm = mtod(m, uint8_t *) + m->m_len; frm += 12; /* skip tstamp, bintval and capinfo fields */ while (frm < efrm) { if (*frm == IEEE80211_ELEMID_DSPARMS) #if IEEE80211_CHAN_MAX < 255 if (frm[2] <= IEEE80211_CHAN_MAX) #endif { DPRINTF(("Fixing channel to %d\n", frm[2])); c = ieee80211_find_channel(ic, ieee80211_ieee2mhz(frm[2], 0), IEEE80211_CHAN_B); if (c == NULL) c = &ic->ic_channels[0]; ipw_setcurchan(sc, c); } frm += frm[1] + 2; } } static void ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status, struct ipw_soft_bd *sbd, struct ipw_soft_buf *sbuf) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct mbuf *mnew, *m; struct ieee80211_node *ni; bus_addr_t physaddr; int error; IPW_LOCK_DECL; DPRINTFN(5, ("received frame len=%u, rssi=%u\n", le32toh(status->len), status->rssi)); if (le32toh(status->len) < sizeof (struct ieee80211_frame_min) || le32toh(status->len) > MCLBYTES) return; /* * Try to allocate a new mbuf for this ring element and load it before * processing the current mbuf. If the ring element cannot be loaded, * drop the received packet and reuse the old mbuf. In the unlikely * case that the old mbuf can't be reloaded either, explicitly panic. */ mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) { ifp->if_ierrors++; return; } bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rxbuf_dmat, sbuf->map); error = bus_dmamap_load(sc->rxbuf_dmat, sbuf->map, mtod(mnew, void *), MCLBYTES, ipw_dma_map_addr, &physaddr, 0); if (error != 0) { m_freem(mnew); /* try to reload the old mbuf */ error = bus_dmamap_load(sc->rxbuf_dmat, sbuf->map, mtod(sbuf->m, void *), MCLBYTES, ipw_dma_map_addr, &physaddr, 0); if (error != 0) { /* very unlikely that it will fail... */ panic("%s: could not load old rx mbuf", device_get_name(sc->sc_dev)); } ifp->if_ierrors++; return; } /* * New mbuf successfully loaded, update Rx ring and continue * processing. */ m = sbuf->m; sbuf->m = mnew; sbd->bd->physaddr = htole32(physaddr); /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = le32toh(status->len); if (bpf_peers_present(ifp->if_bpf)) { struct ipw_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; tap->wr_antsignal = status->rssi + IPW_RSSI_TO_DBM; tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } if (sc->flags & IPW_FLAG_SCANNING) ipw_fix_channel(sc, m); IPW_UNLOCK(sc); ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { (void) ieee80211_input(ni, m, status->rssi, -95, 0); ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, status->rssi, -95, 0); IPW_LOCK(sc); bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE); } static void ipw_rx_intr(struct ipw_softc *sc) { struct ipw_status *status; struct ipw_soft_bd *sbd; struct ipw_soft_buf *sbuf; uint32_t r, i; if (!(sc->flags & IPW_FLAG_FW_INITED)) return; r = CSR_READ_4(sc, IPW_CSR_RX_READ); bus_dmamap_sync(sc->status_dmat, sc->status_map, BUS_DMASYNC_POSTREAD); for (i = (sc->rxcur + 1) % IPW_NRBD; i != r; i = (i + 1) % IPW_NRBD) { status = &sc->status_list[i]; sbd = &sc->srbd_list[i]; sbuf = sbd->priv; switch (le16toh(status->code) & 0xf) { case IPW_STATUS_CODE_COMMAND: ipw_rx_cmd_intr(sc, sbuf); break; case IPW_STATUS_CODE_NEWSTATE: ipw_rx_newstate_intr(sc, sbuf); break; case IPW_STATUS_CODE_DATA_802_3: case IPW_STATUS_CODE_DATA_802_11: ipw_rx_data_intr(sc, status, sbd, sbuf); break; case IPW_STATUS_CODE_NOTIFICATION: DPRINTFN(2, ("notification status, len %u flags 0x%x\n", le32toh(status->len), status->flags)); /* XXX maybe drive state machine AUTH->ASSOC? */ break; default: device_printf(sc->sc_dev, "unexpected status code %u\n", le16toh(status->code)); } /* firmware was killed, stop processing received frames */ if (!(sc->flags & IPW_FLAG_FW_INITED)) return; sbd->bd->flags = 0; } bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE); /* kick the firmware */ sc->rxcur = (r == 0) ? IPW_NRBD - 1 : r - 1; CSR_WRITE_4(sc, IPW_CSR_RX_WRITE, sc->rxcur); } static void ipw_release_sbd(struct ipw_softc *sc, struct ipw_soft_bd *sbd) { struct ipw_soft_hdr *shdr; struct ipw_soft_buf *sbuf; switch (sbd->type) { case IPW_SBD_TYPE_COMMAND: bus_dmamap_sync(sc->cmd_dmat, sc->cmd_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->cmd_dmat, sc->cmd_map); break; case IPW_SBD_TYPE_HEADER: shdr = sbd->priv; bus_dmamap_sync(sc->hdr_dmat, shdr->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->hdr_dmat, shdr->map); SLIST_INSERT_HEAD(&sc->free_shdr, shdr, next); break; case IPW_SBD_TYPE_DATA: sbuf = sbd->priv; bus_dmamap_sync(sc->txbuf_dmat, sbuf->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->txbuf_dmat, sbuf->map); SLIST_INSERT_HEAD(&sc->free_sbuf, sbuf, next); if (sbuf->m->m_flags & M_TXCB) ieee80211_process_callback(sbuf->ni, sbuf->m, 0/*XXX*/); m_freem(sbuf->m); ieee80211_free_node(sbuf->ni); sc->sc_tx_timer = 0; break; } sbd->type = IPW_SBD_TYPE_NOASSOC; } static void ipw_tx_intr(struct ipw_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ipw_soft_bd *sbd; uint32_t r, i; if (!(sc->flags & IPW_FLAG_FW_INITED)) return; r = CSR_READ_4(sc, IPW_CSR_TX_READ); for (i = (sc->txold + 1) % IPW_NTBD; i != r; i = (i + 1) % IPW_NTBD) { sbd = &sc->stbd_list[i]; if (sbd->type == IPW_SBD_TYPE_DATA) ifp->if_opackets++; ipw_release_sbd(sc, sbd); sc->txfree++; } /* remember what the firmware has processed */ sc->txold = (r == 0) ? IPW_NTBD - 1 : r - 1; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ipw_start_locked(ifp); } static void ipw_intr(void *arg) { struct ipw_softc *sc = arg; uint32_t r; IPW_LOCK_DECL; IPW_LOCK(sc); if ((r = CSR_READ_4(sc, IPW_CSR_INTR)) == 0 || r == 0xffffffff) { IPW_UNLOCK(sc); return; } /* disable interrupts */ CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, 0); /* acknowledge all interrupts */ CSR_WRITE_4(sc, IPW_CSR_INTR, r); if (r & (IPW_INTR_FATAL_ERROR | IPW_INTR_PARITY_ERROR)) { device_printf(sc->sc_dev, "firmware error\n"); taskqueue_enqueue(taskqueue_swi, &sc->sc_init_task); r = 0; /* don't process more interrupts */ } if (r & IPW_INTR_FW_INIT_DONE) wakeup(sc); if (r & IPW_INTR_RX_TRANSFER) ipw_rx_intr(sc); if (r & IPW_INTR_TX_TRANSFER) ipw_tx_intr(sc); /* re-enable interrupts */ CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, IPW_INTR_MASK); IPW_UNLOCK(sc); } static void ipw_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) { if (error != 0) return; KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); *(bus_addr_t *)arg = segs[0].ds_addr; } static const char * ipw_cmdname(int cmd) { #define N(a) (sizeof(a) / sizeof(a[0])) static const struct { int cmd; const char *name; } cmds[] = { { IPW_CMD_ADD_MULTICAST, "ADD_MULTICAST" }, { IPW_CMD_BROADCAST_SCAN, "BROADCAST_SCAN" }, { IPW_CMD_DISABLE, "DISABLE" }, { IPW_CMD_DISABLE_PHY, "DISABLE_PHY" }, { IPW_CMD_ENABLE, "ENABLE" }, { IPW_CMD_PREPARE_POWER_DOWN, "PREPARE_POWER_DOWN" }, { IPW_CMD_SET_BASIC_TX_RATES, "SET_BASIC_TX_RATES" }, { IPW_CMD_SET_BEACON_INTERVAL, "SET_BEACON_INTERVAL" }, { IPW_CMD_SET_CHANNEL, "SET_CHANNEL" }, { IPW_CMD_SET_CONFIGURATION, "SET_CONFIGURATION" }, { IPW_CMD_SET_DESIRED_BSSID, "SET_DESIRED_BSSID" }, { IPW_CMD_SET_ESSID, "SET_ESSID" }, { IPW_CMD_SET_FRAG_THRESHOLD, "SET_FRAG_THRESHOLD" }, { IPW_CMD_SET_MAC_ADDRESS, "SET_MAC_ADDRESS" }, { IPW_CMD_SET_MANDATORY_BSSID, "SET_MANDATORY_BSSID" }, { IPW_CMD_SET_MODE, "SET_MODE" }, { IPW_CMD_SET_MSDU_TX_RATES, "SET_MSDU_TX_RATES" }, { IPW_CMD_SET_POWER_MODE, "SET_POWER_MODE" }, { IPW_CMD_SET_RTS_THRESHOLD, "SET_RTS_THRESHOLD" }, { IPW_CMD_SET_SCAN_OPTIONS, "SET_SCAN_OPTIONS" }, { IPW_CMD_SET_SECURITY_INFO, "SET_SECURITY_INFO" }, { IPW_CMD_SET_TX_POWER_INDEX, "SET_TX_POWER_INDEX" }, { IPW_CMD_SET_TX_RATES, "SET_TX_RATES" }, { IPW_CMD_SET_WEP_FLAGS, "SET_WEP_FLAGS" }, { IPW_CMD_SET_WEP_KEY, "SET_WEP_KEY" }, { IPW_CMD_SET_WEP_KEY_INDEX, "SET_WEP_KEY_INDEX" }, { IPW_CMD_SET_WPA_IE, "SET_WPA_IE" }, }; static char buf[12]; int i; for (i = 0; i < N(cmds); i++) if (cmds[i].cmd == cmd) return cmds[i].name; snprintf(buf, sizeof(buf), "%u", cmd); return buf; #undef N } /* * Send a command to the firmware and wait for the acknowledgement. */ static int ipw_cmd(struct ipw_softc *sc, uint32_t type, void *data, uint32_t len) { struct ipw_soft_bd *sbd; bus_addr_t physaddr; int error; IPW_LOCK_ASSERT(sc); if (sc->flags & IPW_FLAG_BUSY) { device_printf(sc->sc_dev, "%s: %s not sent, busy\n", __func__, ipw_cmdname(type)); return EAGAIN; } sc->flags |= IPW_FLAG_BUSY; sbd = &sc->stbd_list[sc->txcur]; error = bus_dmamap_load(sc->cmd_dmat, sc->cmd_map, &sc->cmd, sizeof (struct ipw_cmd), ipw_dma_map_addr, &physaddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map command DMA memory\n"); sc->flags &= ~IPW_FLAG_BUSY; return error; } sc->cmd.type = htole32(type); sc->cmd.subtype = 0; sc->cmd.len = htole32(len); sc->cmd.seq = 0; memcpy(sc->cmd.data, data, len); sbd->type = IPW_SBD_TYPE_COMMAND; sbd->bd->physaddr = htole32(physaddr); sbd->bd->len = htole32(sizeof (struct ipw_cmd)); sbd->bd->nfrag = 1; sbd->bd->flags = IPW_BD_FLAG_TX_FRAME_COMMAND | IPW_BD_FLAG_TX_LAST_FRAGMENT; bus_dmamap_sync(sc->cmd_dmat, sc->cmd_map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->tbd_dmat, sc->tbd_map, BUS_DMASYNC_PREWRITE); #ifdef IPW_DEBUG if (ipw_debug >= 4) { printf("sending %s(%u, %u, %u, %u)", ipw_cmdname(type), type, 0, 0, len); /* Print the data buffer in the higher debug level */ if (ipw_debug >= 9 && len > 0) { printf(" data: 0x"); for (int i = 1; i <= len; i++) printf("%1D", (u_char *)data + len - i, ""); } printf("\n"); } #endif /* kick firmware */ sc->txfree--; sc->txcur = (sc->txcur + 1) % IPW_NTBD; CSR_WRITE_4(sc, IPW_CSR_TX_WRITE, sc->txcur); /* wait at most one second for command to complete */ error = msleep(sc, &sc->sc_mtx, 0, "ipwcmd", hz); if (error != 0) { device_printf(sc->sc_dev, "%s: %s failed, timeout (error %u)\n", __func__, ipw_cmdname(type), error); sc->flags &= ~IPW_FLAG_BUSY; return (error); } return (0); } static int ipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) { struct ipw_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_frame *wh; struct ipw_soft_bd *sbd; struct ipw_soft_hdr *shdr; struct ipw_soft_buf *sbuf; struct ieee80211_key *k; struct mbuf *mnew; bus_dma_segment_t segs[IPW_MAX_NSEG]; bus_addr_t physaddr; int nsegs, error, i; wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } if (bpf_peers_present(ifp->if_bpf)) { struct ipw_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } shdr = SLIST_FIRST(&sc->free_shdr); sbuf = SLIST_FIRST(&sc->free_sbuf); KASSERT(shdr != NULL && sbuf != NULL, ("empty sw hdr/buf pool")); shdr->hdr.type = htole32(IPW_HDR_TYPE_SEND); shdr->hdr.subtype = 0; shdr->hdr.encrypted = (wh->i_fc[1] & IEEE80211_FC1_WEP) ? 1 : 0; shdr->hdr.encrypt = 0; shdr->hdr.keyidx = 0; shdr->hdr.keysz = 0; shdr->hdr.fragmentsz = 0; IEEE80211_ADDR_COPY(shdr->hdr.src_addr, wh->i_addr2); if (ic->ic_opmode == IEEE80211_M_STA) IEEE80211_ADDR_COPY(shdr->hdr.dst_addr, wh->i_addr3); else IEEE80211_ADDR_COPY(shdr->hdr.dst_addr, wh->i_addr1); /* trim IEEE802.11 header */ m_adj(m0, sizeof (struct ieee80211_frame)); error = bus_dmamap_load_mbuf_sg(sc->txbuf_dmat, sbuf->map, m0, segs, &nsegs, 0); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); m_freem(m0); return error; } if (error != 0) { mnew = m_defrag(m0, M_DONTWAIT); if (mnew == NULL) { device_printf(sc->sc_dev, "could not defragment mbuf\n"); m_freem(m0); return ENOBUFS; } m0 = mnew; error = bus_dmamap_load_mbuf_sg(sc->txbuf_dmat, sbuf->map, m0, segs, &nsegs, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); m_freem(m0); return error; } } error = bus_dmamap_load(sc->hdr_dmat, shdr->map, &shdr->hdr, sizeof (struct ipw_hdr), ipw_dma_map_addr, &physaddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map header DMA memory\n"); bus_dmamap_unload(sc->txbuf_dmat, sbuf->map); m_freem(m0); return error; } SLIST_REMOVE_HEAD(&sc->free_sbuf, next); SLIST_REMOVE_HEAD(&sc->free_shdr, next); sbd = &sc->stbd_list[sc->txcur]; sbd->type = IPW_SBD_TYPE_HEADER; sbd->priv = shdr; sbd->bd->physaddr = htole32(physaddr); sbd->bd->len = htole32(sizeof (struct ipw_hdr)); sbd->bd->nfrag = 1 + nsegs; sbd->bd->flags = IPW_BD_FLAG_TX_FRAME_802_3 | IPW_BD_FLAG_TX_NOT_LAST_FRAGMENT; DPRINTFN(5, ("sending tx hdr (%u, %u, %u, %u, %6D, %6D)\n", shdr->hdr.type, shdr->hdr.subtype, shdr->hdr.encrypted, shdr->hdr.encrypt, shdr->hdr.src_addr, ":", shdr->hdr.dst_addr, ":")); sc->txfree--; sc->txcur = (sc->txcur + 1) % IPW_NTBD; sbuf->m = m0; sbuf->ni = ni; for (i = 0; i < nsegs; i++) { sbd = &sc->stbd_list[sc->txcur]; sbd->bd->physaddr = htole32(segs[i].ds_addr); sbd->bd->len = htole32(segs[i].ds_len); sbd->bd->nfrag = 0; sbd->bd->flags = IPW_BD_FLAG_TX_FRAME_802_3; if (i == nsegs - 1) { sbd->type = IPW_SBD_TYPE_DATA; sbd->priv = sbuf; sbd->bd->flags |= IPW_BD_FLAG_TX_LAST_FRAGMENT; } else { sbd->type = IPW_SBD_TYPE_NOASSOC; sbd->bd->flags |= IPW_BD_FLAG_TX_NOT_LAST_FRAGMENT; } DPRINTFN(5, ("sending fragment (%d)\n", i)); sc->txfree--; sc->txcur = (sc->txcur + 1) % IPW_NTBD; } bus_dmamap_sync(sc->hdr_dmat, shdr->map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->txbuf_dmat, sbuf->map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->tbd_dmat, sc->tbd_map, BUS_DMASYNC_PREWRITE); /* kick firmware */ CSR_WRITE_4(sc, IPW_CSR_TX_WRITE, sc->txcur); return 0; } static int ipw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { /* no support; just discard */ m_freem(m); ieee80211_free_node(ni); return 0; } static void ipw_start(struct ifnet *ifp) { struct ipw_softc *sc = ifp->if_softc; IPW_LOCK_DECL; IPW_LOCK(sc); ipw_start_locked(ifp); IPW_UNLOCK(sc); } static void ipw_start_locked(struct ifnet *ifp) { struct ipw_softc *sc = ifp->if_softc; struct ieee80211_node *ni; struct mbuf *m; IPW_LOCK_ASSERT(sc); for (;;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; if (sc->txfree < 1 + IPW_MAX_NSEG) { IFQ_DRV_PREPEND(&ifp->if_snd, m); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); continue; } if (ipw_tx_start(ifp, m, ni) != 0) { ieee80211_free_node(ni); ifp->if_oerrors++; break; } /* start watchdog timer */ sc->sc_tx_timer = 5; } } static void ipw_watchdog(void *arg) { struct ipw_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; IPW_LOCK_ASSERT(sc); if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { if_printf(ifp, "device timeout\n"); ifp->if_oerrors++; taskqueue_enqueue(taskqueue_swi, &sc->sc_init_task); } } if (sc->sc_scan_timer > 0) { if (--sc->sc_scan_timer == 0) { DPRINTFN(3, ("Scan timeout\n")); /* End the scan */ if (sc->flags & IPW_FLAG_SCANNING) { ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps)); sc->flags &= ~IPW_FLAG_SCANNING; } } } if (ifp->if_drv_flags & IFF_DRV_RUNNING) callout_reset(&sc->sc_wdtimer, hz, ipw_watchdog, sc); } static int ipw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ipw_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; IPW_LOCK_DECL; switch (cmd) { case SIOCSIFFLAGS: IPW_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { ipw_init_locked(sc); startall = 1; } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) ipw_stop_locked(sc); } IPW_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return error; } static void ipw_stop_master(struct ipw_softc *sc) { uint32_t tmp; int ntries; /* disable interrupts */ CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, 0); CSR_WRITE_4(sc, IPW_CSR_RST, IPW_RST_STOP_MASTER); for (ntries = 0; ntries < 50; ntries++) { if (CSR_READ_4(sc, IPW_CSR_RST) & IPW_RST_MASTER_DISABLED) break; DELAY(10); } if (ntries == 50) device_printf(sc->sc_dev, "timeout waiting for master\n"); tmp = CSR_READ_4(sc, IPW_CSR_RST); CSR_WRITE_4(sc, IPW_CSR_RST, tmp | IPW_RST_PRINCETON_RESET); /* Clear all flags except the following */ sc->flags &= IPW_FLAG_HAS_RADIO_SWITCH; } static int ipw_reset(struct ipw_softc *sc) { uint32_t tmp; int ntries; ipw_stop_master(sc); /* move adapter to D0 state */ tmp = CSR_READ_4(sc, IPW_CSR_CTL); CSR_WRITE_4(sc, IPW_CSR_CTL, tmp | IPW_CTL_INIT); /* wait for clock stabilization */ for (ntries = 0; ntries < 1000; ntries++) { if (CSR_READ_4(sc, IPW_CSR_CTL) & IPW_CTL_CLOCK_READY) break; DELAY(200); } if (ntries == 1000) return EIO; tmp = CSR_READ_4(sc, IPW_CSR_RST); CSR_WRITE_4(sc, IPW_CSR_RST, tmp | IPW_RST_SW_RESET); DELAY(10); tmp = CSR_READ_4(sc, IPW_CSR_CTL); CSR_WRITE_4(sc, IPW_CSR_CTL, tmp | IPW_CTL_INIT); return 0; } static int ipw_waitfordisable(struct ipw_softc *sc, int waitfor) { int ms = hz < 1000 ? 1 : hz/10; int i, error; for (i = 0; i < 100; i++) { if (ipw_read_table1(sc, IPW_INFO_CARD_DISABLED) == waitfor) return 0; error = msleep(sc, &sc->sc_mtx, PCATCH, __func__, ms); if (error == 0 || error != EWOULDBLOCK) return 0; } DPRINTF(("%s: timeout waiting for %s\n", __func__, waitfor ? "disable" : "enable")); return ETIMEDOUT; } static int ipw_enable(struct ipw_softc *sc) { int error; if ((sc->flags & IPW_FLAG_ENABLED) == 0) { DPRINTF(("Enable adapter\n")); error = ipw_cmd(sc, IPW_CMD_ENABLE, NULL, 0); if (error != 0) return error; error = ipw_waitfordisable(sc, 0); if (error != 0) return error; sc->flags |= IPW_FLAG_ENABLED; } return 0; } static int ipw_disable(struct ipw_softc *sc) { int error; if (sc->flags & IPW_FLAG_ENABLED) { DPRINTF(("Disable adapter\n")); error = ipw_cmd(sc, IPW_CMD_DISABLE, NULL, 0); if (error != 0) return error; error = ipw_waitfordisable(sc, 1); if (error != 0) return error; sc->flags &= ~IPW_FLAG_ENABLED; } return 0; } /* * Upload the microcode to the device. */ static int ipw_load_ucode(struct ipw_softc *sc, const char *uc, int size) { int ntries; MEM_WRITE_4(sc, 0x3000e0, 0x80000000); CSR_WRITE_4(sc, IPW_CSR_RST, 0); MEM_WRITE_2(sc, 0x220000, 0x0703); MEM_WRITE_2(sc, 0x220000, 0x0707); MEM_WRITE_1(sc, 0x210014, 0x72); MEM_WRITE_1(sc, 0x210014, 0x72); MEM_WRITE_1(sc, 0x210000, 0x40); MEM_WRITE_1(sc, 0x210000, 0x00); MEM_WRITE_1(sc, 0x210000, 0x40); MEM_WRITE_MULTI_1(sc, 0x210010, uc, size); MEM_WRITE_1(sc, 0x210000, 0x00); MEM_WRITE_1(sc, 0x210000, 0x00); MEM_WRITE_1(sc, 0x210000, 0x80); MEM_WRITE_2(sc, 0x220000, 0x0703); MEM_WRITE_2(sc, 0x220000, 0x0707); MEM_WRITE_1(sc, 0x210014, 0x72); MEM_WRITE_1(sc, 0x210014, 0x72); MEM_WRITE_1(sc, 0x210000, 0x00); MEM_WRITE_1(sc, 0x210000, 0x80); for (ntries = 0; ntries < 10; ntries++) { if (MEM_READ_1(sc, 0x210000) & 1) break; DELAY(10); } if (ntries == 10) { device_printf(sc->sc_dev, "timeout waiting for ucode to initialize\n"); return EIO; } MEM_WRITE_4(sc, 0x3000e0, 0); return 0; } /* set of macros to handle unaligned little endian data in firmware image */ #define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) #define GETLE16(p) ((p)[0] | (p)[1] << 8) static int ipw_load_firmware(struct ipw_softc *sc, const char *fw, int size) { const uint8_t *p, *end; uint32_t tmp, dst; uint16_t len; int error; p = fw; end = fw + size; while (p < end) { dst = GETLE32(p); p += 4; len = GETLE16(p); p += 2; ipw_write_mem_1(sc, dst, p, len); p += len; } CSR_WRITE_4(sc, IPW_CSR_IO, IPW_IO_GPIO1_ENABLE | IPW_IO_GPIO3_MASK | IPW_IO_LED_OFF); /* enable interrupts */ CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, IPW_INTR_MASK); /* kick the firmware */ CSR_WRITE_4(sc, IPW_CSR_RST, 0); tmp = CSR_READ_4(sc, IPW_CSR_CTL); CSR_WRITE_4(sc, IPW_CSR_CTL, tmp | IPW_CTL_ALLOW_STANDBY); /* wait at most one second for firmware initialization to complete */ if ((error = msleep(sc, &sc->sc_mtx, 0, "ipwinit", hz)) != 0) { device_printf(sc->sc_dev, "timeout waiting for firmware " "initialization to complete\n"); return error; } tmp = CSR_READ_4(sc, IPW_CSR_IO); CSR_WRITE_4(sc, IPW_CSR_IO, tmp | IPW_IO_GPIO1_MASK | IPW_IO_GPIO3_MASK); return 0; } static int ipw_setwepkeys(struct ipw_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ipw_wep_key wepkey; struct ieee80211_key *wk; int error, i; for (i = 0; i < IEEE80211_WEP_NKID; i++) { wk = &vap->iv_nw_keys[i]; if (wk->wk_cipher == NULL || wk->wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP) continue; wepkey.idx = i; wepkey.len = wk->wk_keylen; memset(wepkey.key, 0, sizeof wepkey.key); memcpy(wepkey.key, wk->wk_key, wk->wk_keylen); DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx, wepkey.len)); error = ipw_cmd(sc, IPW_CMD_SET_WEP_KEY, &wepkey, sizeof wepkey); if (error != 0) return error; } return 0; } static int ipw_setwpaie(struct ipw_softc *sc, const void *ie, int ielen) { struct ipw_wpa_ie wpaie; memset(&wpaie, 0, sizeof(wpaie)); wpaie.len = htole32(ielen); /* XXX verify length */ memcpy(&wpaie.ie, ie, ielen); DPRINTF(("Setting WPA IE\n")); return ipw_cmd(sc, IPW_CMD_SET_WPA_IE, &wpaie, sizeof(wpaie)); } static int ipw_setbssid(struct ipw_softc *sc, uint8_t *bssid) { static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; if (bssid == NULL || bcmp(bssid, zerobssid, IEEE80211_ADDR_LEN) == 0) { DPRINTF(("Setting mandatory BSSID to null\n")); return ipw_cmd(sc, IPW_CMD_SET_MANDATORY_BSSID, NULL, 0); } else { DPRINTF(("Setting mandatory BSSID to %6D\n", bssid, ":")); return ipw_cmd(sc, IPW_CMD_SET_MANDATORY_BSSID, bssid, IEEE80211_ADDR_LEN); } } static int ipw_setssid(struct ipw_softc *sc, void *ssid, size_t ssidlen) { if (ssidlen == 0) { /* * A bug in the firmware breaks the ``don't associate'' * bit in the scan options command. To compensate for * this install a bogus ssid when no ssid is specified * so the firmware won't try to associate. */ DPRINTF(("Setting bogus ESSID to WAR firmware bug\n")); return ipw_cmd(sc, IPW_CMD_SET_ESSID, "\x18\x19\x20\x21\x22\x23\x24\x25\x26\x27" "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31" "\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b" "\x3c\x3d", IEEE80211_NWID_LEN); } else { #ifdef IPW_DEBUG if (ipw_debug > 0) { printf("Setting ESSID to "); ieee80211_print_essid(ssid, ssidlen); printf("\n"); } #endif return ipw_cmd(sc, IPW_CMD_SET_ESSID, ssid, ssidlen); } } static int ipw_setscanopts(struct ipw_softc *sc, uint32_t chanmask, uint32_t flags) { struct ipw_scan_options opts; DPRINTF(("Scan options: mask 0x%x flags 0x%x\n", chanmask, flags)); opts.channels = htole32(chanmask); opts.flags = htole32(flags); return ipw_cmd(sc, IPW_CMD_SET_SCAN_OPTIONS, &opts, sizeof(opts)); } /* * Handler for sc_scan_task. This is a simple wrapper around ipw_scan(). */ static void ipw_scan_task(void *context, int pending) { struct ipw_softc *sc = context; IPW_LOCK_DECL; IPW_LOCK(sc); ipw_scan(sc); IPW_UNLOCK(sc); } static int ipw_scan(struct ipw_softc *sc) { uint32_t params; int error; DPRINTF(("%s: flags 0x%x\n", __func__, sc->flags)); if (sc->flags & IPW_FLAG_SCANNING) return (EBUSY); sc->flags |= IPW_FLAG_SCANNING | IPW_FLAG_HACK; /* NB: IPW_SCAN_DO_NOT_ASSOCIATE does not work (we set it anyway) */ error = ipw_setscanopts(sc, 0x3fff, IPW_SCAN_DO_NOT_ASSOCIATE); if (error != 0) goto done; /* * Setup null/bogus ssid so firmware doesn't use any previous * ssid to try and associate. This is because the ``don't * associate'' option bit is broken (sigh). */ error = ipw_setssid(sc, NULL, 0); if (error != 0) goto done; /* * NB: the adapter may be disabled on association lost; * if so just re-enable it to kick off scanning. */ DPRINTF(("Starting scan\n")); sc->sc_scan_timer = 3; if (sc->flags & IPW_FLAG_ENABLED) { params = 0; /* XXX? */ error = ipw_cmd(sc, IPW_CMD_BROADCAST_SCAN, ¶ms, sizeof(params)); } else error = ipw_enable(sc); done: if (error != 0) { DPRINTF(("Scan failed\n")); sc->flags &= ~(IPW_FLAG_SCANNING | IPW_FLAG_HACK); } return (error); } static int ipw_setchannel(struct ipw_softc *sc, struct ieee80211_channel *chan) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t data; int error; data = htole32(ieee80211_chan2ieee(ic, chan)); DPRINTF(("Setting channel to %u\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_CHANNEL, &data, sizeof data); if (error == 0) ipw_setcurchan(sc, chan); return error; } static void -ipw_assoc_task(void *context, int pending) +ipw_assoc(struct ieee80211com *ic, struct ieee80211vap *vap) { - struct ieee80211vap *vap = context; struct ifnet *ifp = vap->iv_ic->ic_ifp; - struct ieee80211com *ic = ifp->if_l2com; struct ipw_softc *sc = ifp->if_softc; struct ieee80211_node *ni = vap->iv_bss; struct ipw_security security; uint32_t data; int error; IPW_LOCK_DECL; IPW_LOCK(sc); error = ipw_disable(sc); if (error != 0) goto done; memset(&security, 0, sizeof security); security.authmode = (ni->ni_authmode == IEEE80211_AUTH_SHARED) ? IPW_AUTH_SHARED : IPW_AUTH_OPEN; security.ciphers = htole32(IPW_CIPHER_NONE); DPRINTF(("Setting authmode to %u\n", security.authmode)); error = ipw_cmd(sc, IPW_CMD_SET_SECURITY_INFO, &security, sizeof security); if (error != 0) goto done; data = htole32(vap->iv_rtsthreshold); DPRINTF(("Setting RTS threshold to %u\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_RTS_THRESHOLD, &data, sizeof data); if (error != 0) goto done; data = htole32(vap->iv_fragthreshold); DPRINTF(("Setting frag threshold to %u\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_FRAG_THRESHOLD, &data, sizeof data); if (error != 0) goto done; if (vap->iv_flags & IEEE80211_F_PRIVACY) { error = ipw_setwepkeys(sc); if (error != 0) goto done; if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) { data = htole32(vap->iv_def_txkey); DPRINTF(("Setting wep tx key index to %u\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_WEP_KEY_INDEX, &data, sizeof data); if (error != 0) goto done; } } data = htole32((vap->iv_flags & IEEE80211_F_PRIVACY) ? IPW_WEPON : 0); DPRINTF(("Setting wep flags to 0x%x\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_WEP_FLAGS, &data, sizeof data); if (error != 0) goto done; error = ipw_setssid(sc, ni->ni_essid, ni->ni_esslen); if (error != 0) goto done; error = ipw_setbssid(sc, ni->ni_bssid); if (error != 0) goto done; if (vap->iv_appie_assocreq != NULL) { struct ieee80211_appie *ie = vap->iv_appie_assocreq; error = ipw_setwpaie(sc, ie->ie_data, ie->ie_len); if (error != 0) goto done; } if (ic->ic_opmode == IEEE80211_M_IBSS) { error = ipw_setchannel(sc, ni->ni_chan); if (error != 0) goto done; } /* lock scan to ap's channel and enable associate */ error = ipw_setscanopts(sc, 1<<(ieee80211_chan2ieee(ic, ni->ni_chan)-1), 0); if (error != 0) goto done; error = ipw_enable(sc); /* finally, enable adapter */ if (error == 0) sc->flags |= IPW_FLAG_ASSOCIATING; done: IPW_UNLOCK(sc); } static void -ipw_disassoc_task(void *context, int pending) +ipw_disassoc(struct ieee80211com *ic, struct ieee80211vap *vap) { - struct ieee80211vap *vap = context; struct ifnet *ifp = vap->iv_ic->ic_ifp; struct ieee80211_node *ni = vap->iv_bss; struct ipw_softc *sc = ifp->if_softc; IPW_LOCK_DECL; IPW_LOCK(sc); DPRINTF(("Disassociate from %6D\n", ni->ni_bssid, ":")); /* * NB: don't try to do this if ipw_stop_master has * shutdown the firmware and disabled interrupts. */ if (sc->flags & IPW_FLAG_FW_INITED) { sc->flags &= ~IPW_FLAG_ASSOCIATED; /* * NB: firmware currently ignores bssid parameter, but * supply it in case this changes (follow linux driver). */ (void) ipw_cmd(sc, IPW_CMD_DISASSOCIATE, ni->ni_bssid, IEEE80211_ADDR_LEN); } IPW_UNLOCK(sc); } /* * Handler for sc_init_task. This is a simple wrapper around ipw_init(). * It is called on firmware panics or on watchdog timeouts. */ static void ipw_init_task(void *context, int pending) { ipw_init(context); } static void ipw_init(void *priv) { struct ipw_softc *sc = priv; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; IPW_LOCK_DECL; IPW_LOCK(sc); ipw_init_locked(sc); IPW_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); /* start all vap's */ } static void ipw_init_locked(struct ipw_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); const struct firmware *fp; const struct ipw_firmware_hdr *hdr; const char *fw; IPW_LOCK_ASSERT(sc); DPRINTF(("%s: state %s flags 0x%x\n", __func__, ieee80211_state_name[vap->iv_state], sc->flags)); /* * Avoid re-entrant calls. We need to release the mutex in ipw_init() * when loading the firmware and we don't want to be called during this * operation. */ if (sc->flags & IPW_FLAG_INIT_LOCKED) return; sc->flags |= IPW_FLAG_INIT_LOCKED; ipw_stop_locked(sc); if (ipw_reset(sc) != 0) { device_printf(sc->sc_dev, "could not reset adapter\n"); goto fail; } if (sc->sc_firmware == NULL) { device_printf(sc->sc_dev, "no firmware\n"); goto fail; } /* NB: consistency already checked on load */ fp = sc->sc_firmware; hdr = (const struct ipw_firmware_hdr *)fp->data; DPRINTF(("Loading firmware image '%s'\n", fp->name)); fw = (const char *)fp->data + sizeof *hdr + le32toh(hdr->mainsz); if (ipw_load_ucode(sc, fw, le32toh(hdr->ucodesz)) != 0) { device_printf(sc->sc_dev, "could not load microcode\n"); goto fail; } ipw_stop_master(sc); /* * Setup tx, rx and status rings. */ sc->txold = IPW_NTBD - 1; sc->txcur = 0; sc->txfree = IPW_NTBD - 2; sc->rxcur = IPW_NRBD - 1; CSR_WRITE_4(sc, IPW_CSR_TX_BASE, sc->tbd_phys); CSR_WRITE_4(sc, IPW_CSR_TX_SIZE, IPW_NTBD); CSR_WRITE_4(sc, IPW_CSR_TX_READ, 0); CSR_WRITE_4(sc, IPW_CSR_TX_WRITE, sc->txcur); CSR_WRITE_4(sc, IPW_CSR_RX_BASE, sc->rbd_phys); CSR_WRITE_4(sc, IPW_CSR_RX_SIZE, IPW_NRBD); CSR_WRITE_4(sc, IPW_CSR_RX_READ, 0); CSR_WRITE_4(sc, IPW_CSR_RX_WRITE, sc->rxcur); CSR_WRITE_4(sc, IPW_CSR_STATUS_BASE, sc->status_phys); fw = (const char *)fp->data + sizeof *hdr; if (ipw_load_firmware(sc, fw, le32toh(hdr->mainsz)) != 0) { device_printf(sc->sc_dev, "could not load firmware\n"); goto fail; } sc->flags |= IPW_FLAG_FW_INITED; /* retrieve information tables base addresses */ sc->table1_base = CSR_READ_4(sc, IPW_CSR_TABLE1_BASE); sc->table2_base = CSR_READ_4(sc, IPW_CSR_TABLE2_BASE); ipw_write_table1(sc, IPW_INFO_LOCK, 0); if (ipw_config(sc) != 0) { device_printf(sc->sc_dev, "device configuration failed\n"); goto fail; } callout_reset(&sc->sc_wdtimer, hz, ipw_watchdog, sc); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->flags &=~ IPW_FLAG_INIT_LOCKED; return; fail: ipw_stop_locked(sc); sc->flags &=~ IPW_FLAG_INIT_LOCKED; } static int ipw_config(struct ipw_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ipw_configuration config; uint32_t data; int error; error = ipw_disable(sc); if (error != 0) return error; switch (ic->ic_opmode) { case IEEE80211_M_STA: case IEEE80211_M_HOSTAP: case IEEE80211_M_WDS: /* XXX */ data = htole32(IPW_MODE_BSS); break; case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: data = htole32(IPW_MODE_IBSS); break; case IEEE80211_M_MONITOR: data = htole32(IPW_MODE_MONITOR); break; } DPRINTF(("Setting mode to %u\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_MODE, &data, sizeof data); if (error != 0) return error; if (ic->ic_opmode == IEEE80211_M_IBSS || ic->ic_opmode == IEEE80211_M_MONITOR) { error = ipw_setchannel(sc, ic->ic_curchan); if (error != 0) return error; } if (ic->ic_opmode == IEEE80211_M_MONITOR) return ipw_enable(sc); config.flags = htole32(IPW_CFG_BSS_MASK | IPW_CFG_IBSS_MASK | IPW_CFG_PREAMBLE_AUTO | IPW_CFG_802_1x_ENABLE); if (ic->ic_opmode == IEEE80211_M_IBSS) config.flags |= htole32(IPW_CFG_IBSS_AUTO_START); if (ifp->if_flags & IFF_PROMISC) config.flags |= htole32(IPW_CFG_PROMISCUOUS); config.bss_chan = htole32(0x3fff); /* channels 1-14 */ config.ibss_chan = htole32(0x7ff); /* channels 1-11 */ DPRINTF(("Setting configuration to 0x%x\n", le32toh(config.flags))); error = ipw_cmd(sc, IPW_CMD_SET_CONFIGURATION, &config, sizeof config); if (error != 0) return error; data = htole32(0x3); /* 1, 2 */ DPRINTF(("Setting basic tx rates to 0x%x\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_BASIC_TX_RATES, &data, sizeof data); if (error != 0) return error; /* NB: use the same rate set */ DPRINTF(("Setting msdu tx rates to 0x%x\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_MSDU_TX_RATES, &data, sizeof data); if (error != 0) return error; data = htole32(0xf); /* 1, 2, 5.5, 11 */ DPRINTF(("Setting tx rates to 0x%x\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_TX_RATES, &data, sizeof data); if (error != 0) return error; data = htole32(IPW_POWER_MODE_CAM); DPRINTF(("Setting power mode to %u\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_POWER_MODE, &data, sizeof data); if (error != 0) return error; if (ic->ic_opmode == IEEE80211_M_IBSS) { data = htole32(32); /* default value */ DPRINTF(("Setting tx power index to %u\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_TX_POWER_INDEX, &data, sizeof data); if (error != 0) return error; } return 0; } static void ipw_stop(void *priv) { struct ipw_softc *sc = priv; IPW_LOCK_DECL; IPW_LOCK(sc); ipw_stop_locked(sc); IPW_UNLOCK(sc); } static void ipw_stop_locked(struct ipw_softc *sc) { struct ifnet *ifp = sc->sc_ifp; int i; IPW_LOCK_ASSERT(sc); callout_stop(&sc->sc_wdtimer); ipw_stop_master(sc); CSR_WRITE_4(sc, IPW_CSR_RST, IPW_RST_SW_RESET); /* * Release tx buffers. */ for (i = 0; i < IPW_NTBD; i++) ipw_release_sbd(sc, &sc->stbd_list[i]); sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } static int ipw_sysctl_stats(SYSCTL_HANDLER_ARGS) { struct ipw_softc *sc = arg1; uint32_t i, size, buf[256]; memset(buf, 0, sizeof buf); if (!(sc->flags & IPW_FLAG_FW_INITED)) return SYSCTL_OUT(req, buf, sizeof buf); CSR_WRITE_4(sc, IPW_CSR_AUTOINC_ADDR, sc->table1_base); size = min(CSR_READ_4(sc, IPW_CSR_AUTOINC_DATA), 256); for (i = 1; i < size; i++) buf[i] = MEM_READ_4(sc, CSR_READ_4(sc, IPW_CSR_AUTOINC_DATA)); return SYSCTL_OUT(req, buf, size); } static int ipw_sysctl_radio(SYSCTL_HANDLER_ARGS) { struct ipw_softc *sc = arg1; int val; val = !((sc->flags & IPW_FLAG_HAS_RADIO_SWITCH) && (CSR_READ_4(sc, IPW_CSR_IO) & IPW_IO_RADIO_DISABLED)); return SYSCTL_OUT(req, &val, sizeof val); } static uint32_t ipw_read_table1(struct ipw_softc *sc, uint32_t off) { return MEM_READ_4(sc, MEM_READ_4(sc, sc->table1_base + off)); } static void ipw_write_table1(struct ipw_softc *sc, uint32_t off, uint32_t info) { MEM_WRITE_4(sc, MEM_READ_4(sc, sc->table1_base + off), info); } #if 0 static int ipw_read_table2(struct ipw_softc *sc, uint32_t off, void *buf, uint32_t *len) { uint32_t addr, info; uint16_t count, size; uint32_t total; /* addr[4] + count[2] + size[2] */ addr = MEM_READ_4(sc, sc->table2_base + off); info = MEM_READ_4(sc, sc->table2_base + off + 4); count = info >> 16; size = info & 0xffff; total = count * size; if (total > *len) { *len = total; return EINVAL; } *len = total; ipw_read_mem_1(sc, addr, buf, total); return 0; } static void ipw_read_mem_1(struct ipw_softc *sc, bus_size_t offset, uint8_t *datap, bus_size_t count) { for (; count > 0; offset++, datap++, count--) { CSR_WRITE_4(sc, IPW_CSR_INDIRECT_ADDR, offset & ~3); *datap = CSR_READ_1(sc, IPW_CSR_INDIRECT_DATA + (offset & 3)); } } #endif static void ipw_write_mem_1(struct ipw_softc *sc, bus_size_t offset, const uint8_t *datap, bus_size_t count) { for (; count > 0; offset++, datap++, count--) { CSR_WRITE_4(sc, IPW_CSR_INDIRECT_ADDR, offset & ~3); CSR_WRITE_1(sc, IPW_CSR_INDIRECT_DATA + (offset & 3), *datap); } } static void ipw_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct ipw_softc *sc = ifp->if_softc; IPW_LOCK_DECL; IPW_LOCK(sc); if (!(sc->flags & IPW_FLAG_SCANNING)) taskqueue_enqueue(taskqueue_swi, &sc->sc_scan_task); IPW_UNLOCK(sc); } static void ipw_set_channel(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct ipw_softc *sc = ifp->if_softc; IPW_LOCK_DECL; IPW_LOCK(sc); if (ic->ic_opmode == IEEE80211_M_MONITOR) { ipw_disable(sc); ipw_setchannel(sc, ic->ic_curchan); ipw_enable(sc); } IPW_UNLOCK(sc); } static void ipw_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { /* NB: all channels are scanned at once */ } static void ipw_scan_mindwell(struct ieee80211_scan_state *ss) { /* NB: don't try to abort scan; wait for firmware to finish */ } static void ipw_scan_end(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct ipw_softc *sc = ifp->if_softc; IPW_LOCK_DECL; IPW_LOCK(sc); sc->flags &= ~IPW_FLAG_SCANNING; IPW_UNLOCK(sc); } Index: user/thompsa/vaptq/sys/dev/ipw/if_ipwvar.h =================================================================== --- user/thompsa/vaptq/sys/dev/ipw/if_ipwvar.h (revision 185732) +++ user/thompsa/vaptq/sys/dev/ipw/if_ipwvar.h (revision 185733) @@ -1,185 +1,183 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2004-2006 * Damien Bergamini . 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 unmodified, 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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. */ #define IPW_MAX_NSEG 1 struct ipw_soft_bd { struct ipw_bd *bd; int type; #define IPW_SBD_TYPE_NOASSOC 0 #define IPW_SBD_TYPE_COMMAND 1 #define IPW_SBD_TYPE_HEADER 2 #define IPW_SBD_TYPE_DATA 3 void *priv; }; struct ipw_soft_hdr { struct ipw_hdr hdr; bus_dmamap_t map; SLIST_ENTRY(ipw_soft_hdr) next; }; struct ipw_soft_buf { struct mbuf *m; struct ieee80211_node *ni; bus_dmamap_t map; SLIST_ENTRY(ipw_soft_buf) next; }; struct ipw_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint8_t wr_flags; uint16_t wr_chan_freq; uint16_t wr_chan_flags; uint8_t wr_antsignal; }; #define IPW_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) struct ipw_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; uint16_t wt_chan_freq; uint16_t wt_chan_flags; }; #define IPW_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_CHANNEL)) struct ipw_vap { struct ieee80211vap vap; - struct task assoc_task; - struct task disassoc_task; struct task assoc_success_task; struct task assoc_failed_task; struct task scandone_task; int (*newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define IPW_VAP(vap) ((struct ipw_vap *)(vap)) struct ipw_softc { struct ifnet *sc_ifp; device_t sc_dev; struct mtx sc_mtx; struct task sc_init_task; struct task sc_scan_task; struct task sc_chan_task; struct task sc_bmiss_task; struct callout sc_wdtimer; /* watchdog timer */ uint32_t flags; #define IPW_FLAG_FW_INITED 0x0001 #define IPW_FLAG_INIT_LOCKED 0x0002 #define IPW_FLAG_HAS_RADIO_SWITCH 0x0004 #define IPW_FLAG_HACK 0x0008 #define IPW_FLAG_SCANNING 0x0010 #define IPW_FLAG_ENABLED 0x0020 #define IPW_FLAG_BUSY 0x0040 #define IPW_FLAG_ASSOCIATING 0x0080 #define IPW_FLAG_ASSOCIATED 0x0100 int irq_rid; int mem_rid; struct resource *irq; struct resource *mem; bus_space_tag_t sc_st; bus_space_handle_t sc_sh; void *sc_ih; const struct firmware *sc_firmware; int sc_tx_timer; int sc_scan_timer; bus_dma_tag_t tbd_dmat; bus_dma_tag_t rbd_dmat; bus_dma_tag_t status_dmat; bus_dma_tag_t cmd_dmat; bus_dma_tag_t hdr_dmat; bus_dma_tag_t txbuf_dmat; bus_dma_tag_t rxbuf_dmat; bus_dmamap_t tbd_map; bus_dmamap_t rbd_map; bus_dmamap_t status_map; bus_dmamap_t cmd_map; bus_addr_t tbd_phys; bus_addr_t rbd_phys; bus_addr_t status_phys; struct ipw_bd *tbd_list; struct ipw_bd *rbd_list; struct ipw_status *status_list; struct ipw_cmd cmd; struct ipw_soft_bd stbd_list[IPW_NTBD]; struct ipw_soft_buf tx_sbuf_list[IPW_NDATA]; struct ipw_soft_hdr shdr_list[IPW_NDATA]; struct ipw_soft_bd srbd_list[IPW_NRBD]; struct ipw_soft_buf rx_sbuf_list[IPW_NRBD]; SLIST_HEAD(, ipw_soft_hdr) free_shdr; SLIST_HEAD(, ipw_soft_buf) free_sbuf; uint32_t table1_base; uint32_t table2_base; uint32_t txcur; uint32_t txold; uint32_t rxcur; int txfree; struct ipw_rx_radiotap_header sc_rxtap; int sc_rxtap_len; struct ipw_tx_radiotap_header sc_txtap; int sc_txtap_len; }; /* * NB.: This models the only instance of async locking in ipw_init_locked * and must be kept in sync. */ #define IPW_LOCK_DECL int __waslocked = 0 #define IPW_LOCK(sc) do { \ if (!(__waslocked = mtx_owned(&(sc)->sc_mtx))) \ mtx_lock(&sc->sc_mtx); \ } while (0) #define IPW_UNLOCK(sc) do { \ if (!__waslocked) \ mtx_unlock(&sc->sc_mtx); \ } while (0) #define IPW_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) Index: user/thompsa/vaptq/sys/dev/iwi/if_iwi.c =================================================================== --- user/thompsa/vaptq/sys/dev/iwi/if_iwi.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/iwi/if_iwi.c (revision 185733) @@ -1,3696 +1,3686 @@ /*- * Copyright (c) 2004, 2005 * Damien Bergamini . All rights reserved. * Copyright (c) 2005-2006 Sam Leffler, Errno Consulting * Copyright (c) 2007 Andrew Thompson * * 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 unmodified, 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); /*- * Intel(R) PRO/Wireless 2200BG/2225BG/2915ABG driver * http://www.intel.com/network/connectivity/products/wireless/prowireless_mobile.htm */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IWI_DEBUG #ifdef IWI_DEBUG #define DPRINTF(x) do { if (iwi_debug > 0) printf x; } while (0) #define DPRINTFN(n, x) do { if (iwi_debug >= (n)) printf x; } while (0) int iwi_debug = 0; SYSCTL_INT(_debug, OID_AUTO, iwi, CTLFLAG_RW, &iwi_debug, 0, "iwi debug level"); static const char *iwi_fw_states[] = { "IDLE", /* IWI_FW_IDLE */ "LOADING", /* IWI_FW_LOADING */ "ASSOCIATING", /* IWI_FW_ASSOCIATING */ "DISASSOCIATING", /* IWI_FW_DISASSOCIATING */ "SCANNING", /* IWI_FW_SCANNING */ }; #else #define DPRINTF(x) #define DPRINTFN(n, x) #endif MODULE_DEPEND(iwi, pci, 1, 1, 1); MODULE_DEPEND(iwi, wlan, 1, 1, 1); MODULE_DEPEND(iwi, firmware, 1, 1, 1); enum { IWI_LED_TX, IWI_LED_RX, IWI_LED_POLL, }; struct iwi_ident { uint16_t vendor; uint16_t device; const char *name; }; static const struct iwi_ident iwi_ident_table[] = { { 0x8086, 0x4220, "Intel(R) PRO/Wireless 2200BG" }, { 0x8086, 0x4221, "Intel(R) PRO/Wireless 2225BG" }, { 0x8086, 0x4223, "Intel(R) PRO/Wireless 2915ABG" }, { 0x8086, 0x4224, "Intel(R) PRO/Wireless 2915ABG" }, { 0, 0, NULL } }; static struct ieee80211vap *iwi_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void iwi_vap_delete(struct ieee80211vap *); static void iwi_dma_map_addr(void *, bus_dma_segment_t *, int, int); static int iwi_alloc_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *, int); static void iwi_reset_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *); static void iwi_free_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *); static int iwi_alloc_tx_ring(struct iwi_softc *, struct iwi_tx_ring *, int, bus_addr_t, bus_addr_t); static void iwi_reset_tx_ring(struct iwi_softc *, struct iwi_tx_ring *); static void iwi_free_tx_ring(struct iwi_softc *, struct iwi_tx_ring *); static int iwi_alloc_rx_ring(struct iwi_softc *, struct iwi_rx_ring *, int); static void iwi_reset_rx_ring(struct iwi_softc *, struct iwi_rx_ring *); static void iwi_free_rx_ring(struct iwi_softc *, struct iwi_rx_ring *); static struct ieee80211_node *iwi_node_alloc(struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); static void iwi_node_free(struct ieee80211_node *); static void iwi_media_status(struct ifnet *, struct ifmediareq *); static int iwi_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void iwi_wme_init(struct iwi_softc *); static int iwi_wme_setparams(struct iwi_softc *, struct ieee80211com *); static int iwi_wme_update(struct ieee80211com *); static uint16_t iwi_read_prom_word(struct iwi_softc *, uint8_t); static void iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int, struct iwi_frame *); static void iwi_authsuccess(void *, int); static void iwi_assocsuccess(void *, int); static void iwi_assocfailed(void *, int); static void iwi_notification_intr(struct iwi_softc *, struct iwi_notif *); static void iwi_rx_intr(struct iwi_softc *); static void iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *); static void iwi_intr(void *); static int iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t); static void iwi_write_ibssnode(struct iwi_softc *, const u_int8_t [], int); static int iwi_tx_start(struct ifnet *, struct mbuf *, struct ieee80211_node *, int); static int iwi_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void iwi_start_locked(struct ifnet *); static void iwi_start(struct ifnet *); static void iwi_watchdog(void *); static int iwi_ioctl(struct ifnet *, u_long, caddr_t); static void iwi_stop_master(struct iwi_softc *); static int iwi_reset(struct iwi_softc *); static int iwi_load_ucode(struct iwi_softc *, const struct iwi_fw *); static int iwi_load_firmware(struct iwi_softc *, const struct iwi_fw *); static void iwi_release_fw_dma(struct iwi_softc *sc); static int iwi_config(struct iwi_softc *); static int iwi_get_firmware(struct iwi_softc *, enum ieee80211_opmode); static void iwi_put_firmware(struct iwi_softc *); static int iwi_scanchan(struct iwi_softc *, unsigned long, int); static void iwi_scan_start(struct ieee80211com *); static void iwi_scan_end(struct ieee80211com *); static void iwi_scanabort(void *, int); static void iwi_set_channel(struct ieee80211com *); static void iwi_scan_curchan(struct ieee80211_scan_state *, unsigned long maxdwell); #if 0 static void iwi_scan_allchan(struct ieee80211com *, unsigned long maxdwell); #endif static void iwi_scan_mindwell(struct ieee80211_scan_state *); static void iwi_ops(void *, int); static int iwi_queue_cmd(struct iwi_softc *, int, unsigned long); static int iwi_auth_and_assoc(struct iwi_softc *, struct ieee80211vap *); static int iwi_disassociate(struct iwi_softc *, int quiet); static void iwi_init_locked(struct iwi_softc *); static void iwi_init(void *); static int iwi_init_fw_dma(struct iwi_softc *, int); static void iwi_stop_locked(void *); static void iwi_stop(struct iwi_softc *); static void iwi_restart(void *, int); static int iwi_getrfkill(struct iwi_softc *); static void iwi_radio_on(void *, int); static void iwi_radio_off(void *, int); static void iwi_sysctlattach(struct iwi_softc *); static void iwi_led_event(struct iwi_softc *, int); static void iwi_ledattach(struct iwi_softc *); static int iwi_probe(device_t); static int iwi_attach(device_t); static int iwi_detach(device_t); static int iwi_shutdown(device_t); static int iwi_suspend(device_t); static int iwi_resume(device_t); static device_method_t iwi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, iwi_probe), DEVMETHOD(device_attach, iwi_attach), DEVMETHOD(device_detach, iwi_detach), DEVMETHOD(device_shutdown, iwi_shutdown), DEVMETHOD(device_suspend, iwi_suspend), DEVMETHOD(device_resume, iwi_resume), { 0, 0 } }; static driver_t iwi_driver = { "iwi", iwi_methods, sizeof (struct iwi_softc) }; static devclass_t iwi_devclass; DRIVER_MODULE(iwi, pci, iwi_driver, iwi_devclass, 0, 0); static __inline uint8_t MEM_READ_1(struct iwi_softc *sc, uint32_t addr) { CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); return CSR_READ_1(sc, IWI_CSR_INDIRECT_DATA); } static __inline uint32_t MEM_READ_4(struct iwi_softc *sc, uint32_t addr) { CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); return CSR_READ_4(sc, IWI_CSR_INDIRECT_DATA); } static int iwi_probe(device_t dev) { const struct iwi_ident *ident; for (ident = iwi_ident_table; ident->name != NULL; ident++) { if (pci_get_vendor(dev) == ident->vendor && pci_get_device(dev) == ident->device) { device_set_desc(dev, ident->name); return 0; } } return ENXIO; } /* Base Address Register */ #define IWI_PCI_BAR0 0x10 static int iwi_attach(device_t dev) { struct iwi_softc *sc = device_get_softc(dev); struct ifnet *ifp; struct ieee80211com *ic; uint16_t val; int i, error; uint8_t bands; sc->sc_dev = dev; ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); return ENXIO; } ic = ifp->if_l2com; IWI_LOCK_INIT(sc); IWI_CMD_LOCK_INIT(sc); sc->sc_unr = new_unrhdr(1, IWI_MAX_IBSSNODE-1, &sc->sc_mtx); sc->sc_tq = taskqueue_create("iwi_taskq", M_NOWAIT | M_ZERO, taskqueue_thread_enqueue, &sc->sc_tq); taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(dev)); sc->sc_tq2 = taskqueue_create("iwi_taskq2", M_NOWAIT | M_ZERO, taskqueue_thread_enqueue, &sc->sc_tq2); taskqueue_start_threads(&sc->sc_tq2, 1, PI_NET, "%s taskq2", device_get_nameunit(dev)); TASK_INIT(&sc->sc_radiontask, 0, iwi_radio_on, sc); TASK_INIT(&sc->sc_radiofftask, 0, iwi_radio_off, sc); TASK_INIT(&sc->sc_restarttask, 0, iwi_restart, sc); TASK_INIT(&sc->sc_opstask, 0, iwi_ops, sc); TASK_INIT(&sc->sc_scanaborttask, 0, iwi_scanabort, sc); callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0); callout_init_mtx(&sc->sc_rftimer, &sc->sc_mtx, 0); if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { device_printf(dev, "chip is in D%d power mode " "-- setting to D0\n", pci_get_powerstate(dev)); pci_set_powerstate(dev, PCI_POWERSTATE_D0); } pci_write_config(dev, 0x41, 0, 1); /* enable bus-mastering */ pci_enable_busmaster(dev); sc->mem_rid = IWI_PCI_BAR0; sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem == NULL) { device_printf(dev, "could not allocate memory resource\n"); goto fail; } sc->sc_st = rman_get_bustag(sc->mem); sc->sc_sh = rman_get_bushandle(sc->mem); sc->irq_rid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->irq == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); goto fail; } if (iwi_reset(sc) != 0) { device_printf(dev, "could not reset adapter\n"); goto fail; } /* * Allocate rings. */ if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) { device_printf(dev, "could not allocate Cmd ring\n"); goto fail; } for (i = 0; i < 4; i++) { error = iwi_alloc_tx_ring(sc, &sc->txq[i], IWI_TX_RING_COUNT, IWI_CSR_TX1_RIDX + i * 4, IWI_CSR_TX1_WIDX + i * 4); if (error != 0) { device_printf(dev, "could not allocate Tx ring %d\n", i+i); goto fail; } } if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) { device_printf(dev, "could not allocate Rx ring\n"); goto fail; } iwi_wme_init(sc); ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = iwi_init; ifp->if_ioctl = iwi_ioctl; ifp->if_start = iwi_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ic->ic_ifp = ifp; ic->ic_opmode = IEEE80211_M_STA; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_PMGT /* power save supported */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_WPA /* 802.11i */ | IEEE80211_C_WME /* 802.11e */ #if 0 | IEEE80211_C_BGSCAN /* capable of bg scanning */ #endif ; /* read MAC address from EEPROM */ val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 0); ic->ic_myaddr[0] = val & 0xff; ic->ic_myaddr[1] = val >> 8; val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 1); ic->ic_myaddr[2] = val & 0xff; ic->ic_myaddr[3] = val >> 8; val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2); ic->ic_myaddr[4] = val & 0xff; ic->ic_myaddr[5] = val >> 8; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); if (pci_get_device(dev) >= 0x4223) setbit(&bands, IEEE80211_MODE_11A); ieee80211_init_channels(ic, NULL, &bands); ieee80211_ifattach(ic); /* override default methods */ ic->ic_node_alloc = iwi_node_alloc; sc->sc_node_free = ic->ic_node_free; ic->ic_node_free = iwi_node_free; ic->ic_raw_xmit = iwi_raw_xmit; ic->ic_scan_start = iwi_scan_start; ic->ic_scan_end = iwi_scan_end; ic->ic_set_channel = iwi_set_channel; ic->ic_scan_curchan = iwi_scan_curchan; ic->ic_scan_mindwell = iwi_scan_mindwell; ic->ic_wme.wme_update = iwi_wme_update; ic->ic_vap_create = iwi_vap_create; ic->ic_vap_delete = iwi_vap_delete; bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap)); sc->sc_rxtap_len = sizeof sc->sc_rxtap; sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(IWI_RX_RADIOTAP_PRESENT); sc->sc_txtap_len = sizeof sc->sc_txtap; sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(IWI_TX_RADIOTAP_PRESENT); iwi_sysctlattach(sc); iwi_ledattach(sc); /* * Hook our interrupt after all initialization is complete. */ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, iwi_intr, sc, &sc->sc_ih); if (error != 0) { device_printf(dev, "could not set up interrupt\n"); goto fail; } if (bootverbose) ieee80211_announce(ic); return 0; fail: /* XXX fix */ iwi_detach(dev); return ENXIO; } static int iwi_detach(device_t dev) { struct iwi_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; iwi_stop(sc); bpfdetach(ifp); ieee80211_ifdetach(ic); /* NB: do early to drain any pending tasks */ taskqueue_free(sc->sc_tq); taskqueue_free(sc->sc_tq2); iwi_put_firmware(sc); iwi_release_fw_dma(sc); iwi_free_cmd_ring(sc, &sc->cmdq); iwi_free_tx_ring(sc, &sc->txq[0]); iwi_free_tx_ring(sc, &sc->txq[1]); iwi_free_tx_ring(sc, &sc->txq[2]); iwi_free_tx_ring(sc, &sc->txq[3]); iwi_free_rx_ring(sc, &sc->rxq); bus_teardown_intr(dev, sc->irq, sc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); delete_unrhdr(sc->sc_unr); IWI_LOCK_DESTROY(sc); IWI_CMD_LOCK_DESTROY(sc); if_free(ifp); return 0; } static struct ieee80211vap * iwi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ifnet *ifp = ic->ic_ifp; struct iwi_softc *sc = ifp->if_softc; struct iwi_vap *ivp; struct ieee80211vap *vap; int i; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; /* * Get firmware image (and possibly dma memory) on mode change. */ if (iwi_get_firmware(sc, opmode)) return NULL; /* allocate DMA memory for mapping firmware image */ i = sc->fw_fw.size; if (sc->fw_boot.size > i) i = sc->fw_boot.size; /* XXX do we dma the ucode as well ? */ if (sc->fw_uc.size > i) i = sc->fw_uc.size; if (iwi_init_fw_dma(sc, i)) return NULL; ivp = (struct iwi_vap *) malloc(sizeof(struct iwi_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (ivp == NULL) return NULL; vap = &ivp->iwi_vap; ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); /* override the default, the setting comes from the linux driver */ vap->iv_bmissthreshold = 24; /* override with driver methods */ ivp->iwi_newstate = vap->iv_newstate; vap->iv_newstate = iwi_newstate; TASK_INIT(&ivp->iwi_authsuccess_task, 0, iwi_authsuccess, vap); TASK_INIT(&ivp->iwi_assocsuccess_task, 0, iwi_assocsuccess, vap); TASK_INIT(&ivp->iwi_assocfailed_task, 0, iwi_assocfailed, vap); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, iwi_media_status); ic->ic_opmode = opmode; return vap; } static void iwi_vap_delete(struct ieee80211vap *vap) { struct iwi_vap *ivp = IWI_VAP(vap); ieee80211_vap_detach(vap); free(ivp, M_80211_VAP); } static void iwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) { if (error != 0) return; KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); *(bus_addr_t *)arg = segs[0].ds_addr; } static int iwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring, int count) { int error; ring->count = count; ring->queued = 0; ring->cur = ring->next = 0; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, count * IWI_CMD_DESC_SIZE, 1, count * IWI_CMD_DESC_SIZE, 0, NULL, NULL, &ring->desc_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create desc DMA tag\n"); goto fail; } error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); if (error != 0) { device_printf(sc->sc_dev, "could not allocate DMA memory\n"); goto fail; } error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, count * IWI_CMD_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not load desc DMA map\n"); goto fail; } return 0; fail: iwi_free_cmd_ring(sc, ring); return error; } static void iwi_reset_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) { ring->queued = 0; ring->cur = ring->next = 0; } static void iwi_free_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) { if (ring->desc != NULL) { bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->desc_dmat, ring->desc_map); bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); } if (ring->desc_dmat != NULL) bus_dma_tag_destroy(ring->desc_dmat); } static int iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring, int count, bus_addr_t csr_ridx, bus_addr_t csr_widx) { int i, error; ring->count = count; ring->queued = 0; ring->cur = ring->next = 0; ring->csr_ridx = csr_ridx; ring->csr_widx = csr_widx; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, count * IWI_TX_DESC_SIZE, 1, count * IWI_TX_DESC_SIZE, 0, NULL, NULL, &ring->desc_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create desc DMA tag\n"); goto fail; } error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); if (error != 0) { device_printf(sc->sc_dev, "could not allocate DMA memory\n"); goto fail; } error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, count * IWI_TX_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not load desc DMA map\n"); goto fail; } ring->data = malloc(count * sizeof (struct iwi_tx_data), M_DEVBUF, M_NOWAIT | M_ZERO); if (ring->data == NULL) { device_printf(sc->sc_dev, "could not allocate soft data\n"); error = ENOMEM; goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, IWI_MAX_NSEG, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create data DMA tag\n"); goto fail; } for (i = 0; i < count; i++) { error = bus_dmamap_create(ring->data_dmat, 0, &ring->data[i].map); if (error != 0) { device_printf(sc->sc_dev, "could not create DMA map\n"); goto fail; } } return 0; fail: iwi_free_tx_ring(sc, ring); return error; } static void iwi_reset_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring) { struct iwi_tx_data *data; int i; for (i = 0; i < ring->count; i++) { data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } } ring->queued = 0; ring->cur = ring->next = 0; } static void iwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring) { struct iwi_tx_data *data; int i; if (ring->desc != NULL) { bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->desc_dmat, ring->desc_map); bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); } if (ring->desc_dmat != NULL) bus_dma_tag_destroy(ring->desc_dmat); if (ring->data != NULL) { for (i = 0; i < ring->count; i++) { data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); } if (data->ni != NULL) ieee80211_free_node(data->ni); if (data->map != NULL) bus_dmamap_destroy(ring->data_dmat, data->map); } free(ring->data, M_DEVBUF); } if (ring->data_dmat != NULL) bus_dma_tag_destroy(ring->data_dmat); } static int iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count) { struct iwi_rx_data *data; int i, error; ring->count = count; ring->cur = 0; ring->data = malloc(count * sizeof (struct iwi_rx_data), M_DEVBUF, M_NOWAIT | M_ZERO); if (ring->data == NULL) { device_printf(sc->sc_dev, "could not allocate soft data\n"); error = ENOMEM; goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create data DMA tag\n"); goto fail; } for (i = 0; i < count; i++) { data = &ring->data[i]; error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "could not create DMA map\n"); goto fail; } data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (data->m == NULL) { device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); error = ENOMEM; goto fail; } error = bus_dmamap_load(ring->data_dmat, data->map, mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not load rx buf DMA map"); goto fail; } data->reg = IWI_CSR_RX_BASE + i * 4; } return 0; fail: iwi_free_rx_ring(sc, ring); return error; } static void iwi_reset_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring) { ring->cur = 0; } static void iwi_free_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring) { struct iwi_rx_data *data; int i; if (ring->data != NULL) { for (i = 0; i < ring->count; i++) { data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); } if (data->map != NULL) bus_dmamap_destroy(ring->data_dmat, data->map); } free(ring->data, M_DEVBUF); } if (ring->data_dmat != NULL) bus_dma_tag_destroy(ring->data_dmat); } static int iwi_shutdown(device_t dev) { struct iwi_softc *sc = device_get_softc(dev); iwi_stop(sc); iwi_put_firmware(sc); /* ??? XXX */ return 0; } static int iwi_suspend(device_t dev) { struct iwi_softc *sc = device_get_softc(dev); iwi_stop(sc); return 0; } static int iwi_resume(device_t dev) { struct iwi_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; pci_write_config(dev, 0x41, 0, 1); if (ifp->if_flags & IFF_UP) iwi_init(sc); return 0; } static struct ieee80211_node * iwi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct iwi_node *in; in = malloc(sizeof (struct iwi_node), M_80211_NODE, M_NOWAIT | M_ZERO); if (in == NULL) return NULL; /* XXX assign sta table entry for adhoc */ in->in_station = -1; return &in->in_node; } static void iwi_node_free(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct iwi_softc *sc = ic->ic_ifp->if_softc; struct iwi_node *in = (struct iwi_node *)ni; if (in->in_station != -1) { DPRINTF(("%s mac %6D station %u\n", __func__, ni->ni_macaddr, ":", in->in_station)); free_unr(sc->sc_unr, in->in_station); } sc->sc_node_free(ni); } /* * Convert h/w rate code to IEEE rate code. */ static int iwi_cvtrate(int iwirate) { switch (iwirate) { case IWI_RATE_DS1: return 2; case IWI_RATE_DS2: return 4; case IWI_RATE_DS5: return 11; case IWI_RATE_DS11: return 22; case IWI_RATE_OFDM6: return 12; case IWI_RATE_OFDM9: return 18; case IWI_RATE_OFDM12: return 24; case IWI_RATE_OFDM18: return 36; case IWI_RATE_OFDM24: return 48; case IWI_RATE_OFDM36: return 72; case IWI_RATE_OFDM48: return 96; case IWI_RATE_OFDM54: return 108; } return 0; } /* * The firmware automatically adapts the transmit speed. We report its current * value here. */ static void iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr) { struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; struct iwi_softc *sc = ic->ic_ifp->if_softc; /* read current transmission rate from adapter */ vap->iv_bss->ni_txrate = iwi_cvtrate(CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE)); ieee80211_media_status(ifp, imr); } static int iwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct iwi_vap *ivp = IWI_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct iwi_softc *sc = ifp->if_softc; IWI_LOCK_DECL; DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate], sc->flags)); + IEEE80211_UNLOCK(ic); + IWI_LOCK(sc); switch (nstate) { case IEEE80211_S_INIT: - IWI_LOCK(sc); /* * NB: don't try to do this if iwi_stop_master has * shutdown the firmware and disabled interrupts. */ if (vap->iv_state == IEEE80211_S_RUN && (sc->flags & IWI_FLAG_FW_INITED)) - iwi_queue_cmd(sc, IWI_DISASSOC, 1); - IWI_UNLOCK(sc); + iwi_disassociate(sc, 0); break; case IEEE80211_S_AUTH: - iwi_queue_cmd(sc, IWI_AUTH, arg); - return EINPROGRESS; + iwi_auth_and_assoc(sc, vap); + break; case IEEE80211_S_RUN: if (vap->iv_opmode == IEEE80211_M_IBSS && vap->iv_state == IEEE80211_S_SCAN) { /* * XXX when joining an ibss network we are called * with a SCAN -> RUN transition on scan complete. * Use that to call iwi_auth_and_assoc. On completing * the join we are then called again with an * AUTH -> RUN transition and we want to do nothing. * This is all totally bogus and needs to be redone. */ - iwi_queue_cmd(sc, IWI_ASSOC, 0); - return EINPROGRESS; + iwi_auth_and_assoc(sc, vap); } break; case IEEE80211_S_ASSOC: /* * If we are transitioning from AUTH then just wait * for the ASSOC status to come back from the firmware. * Otherwise we need to issue the association request. */ if (vap->iv_state == IEEE80211_S_AUTH) break; - iwi_queue_cmd(sc, IWI_ASSOC, arg); - return EINPROGRESS; + iwi_auth_and_assoc(sc, vap); + break; default: break; } + IWI_UNLOCK(sc); + IEEE80211_LOCK(ic); return ivp->iwi_newstate(vap, nstate, arg); } /* * WME parameters coming from IEEE 802.11e specification. These values are * already declared in ieee80211_proto.c, but they are static so they can't * be reused here. */ static const struct wmeParams iwi_wme_cck_params[WME_NUM_AC] = { { 0, 3, 5, 7, 0 }, /* WME_AC_BE */ { 0, 3, 5, 10, 0 }, /* WME_AC_BK */ { 0, 2, 4, 5, 188 }, /* WME_AC_VI */ { 0, 2, 3, 4, 102 } /* WME_AC_VO */ }; static const struct wmeParams iwi_wme_ofdm_params[WME_NUM_AC] = { { 0, 3, 4, 6, 0 }, /* WME_AC_BE */ { 0, 3, 4, 10, 0 }, /* WME_AC_BK */ { 0, 2, 3, 4, 94 }, /* WME_AC_VI */ { 0, 2, 2, 3, 47 } /* WME_AC_VO */ }; #define IWI_EXP2(v) htole16((1 << (v)) - 1) #define IWI_USEC(v) htole16(IEEE80211_TXOP_TO_US(v)) static void iwi_wme_init(struct iwi_softc *sc) { const struct wmeParams *wmep; int ac; memset(sc->wme, 0, sizeof sc->wme); for (ac = 0; ac < WME_NUM_AC; ac++) { /* set WME values for CCK modulation */ wmep = &iwi_wme_cck_params[ac]; sc->wme[1].aifsn[ac] = wmep->wmep_aifsn; sc->wme[1].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); sc->wme[1].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); sc->wme[1].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); sc->wme[1].acm[ac] = wmep->wmep_acm; /* set WME values for OFDM modulation */ wmep = &iwi_wme_ofdm_params[ac]; sc->wme[2].aifsn[ac] = wmep->wmep_aifsn; sc->wme[2].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); sc->wme[2].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); sc->wme[2].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); sc->wme[2].acm[ac] = wmep->wmep_acm; } } static int iwi_wme_setparams(struct iwi_softc *sc, struct ieee80211com *ic) { const struct wmeParams *wmep; int ac; for (ac = 0; ac < WME_NUM_AC; ac++) { /* set WME values for current operating mode */ wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; sc->wme[0].aifsn[ac] = wmep->wmep_aifsn; sc->wme[0].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); sc->wme[0].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); sc->wme[0].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); sc->wme[0].acm[ac] = wmep->wmep_acm; } DPRINTF(("Setting WME parameters\n")); return iwi_cmd(sc, IWI_CMD_SET_WME_PARAMS, sc->wme, sizeof sc->wme); } #undef IWI_USEC #undef IWI_EXP2 static int iwi_wme_update(struct ieee80211com *ic) { struct iwi_softc *sc = ic->ic_ifp->if_softc; /* * We may be called to update the WME parameters in * the adapter at various places. If we're already * associated then initiate the request immediately * (via the taskqueue); otherwise we assume the params * will get sent down to the adapter as part of the * work iwi_auth_and_assoc does. */ return iwi_queue_cmd(sc, IWI_SET_WME, 0); } static int iwi_wme_setie(struct iwi_softc *sc) { struct ieee80211_wme_info wme; memset(&wme, 0, sizeof wme); wme.wme_id = IEEE80211_ELEMID_VENDOR; wme.wme_len = sizeof (struct ieee80211_wme_info) - 2; wme.wme_oui[0] = 0x00; wme.wme_oui[1] = 0x50; wme.wme_oui[2] = 0xf2; wme.wme_type = WME_OUI_TYPE; wme.wme_subtype = WME_INFO_OUI_SUBTYPE; wme.wme_version = WME_VERSION; wme.wme_info = 0; DPRINTF(("Setting WME IE (len=%u)\n", wme.wme_len)); return iwi_cmd(sc, IWI_CMD_SET_WMEIE, &wme, sizeof wme); } /* * Read 16 bits at address 'addr' from the serial EEPROM. */ static uint16_t iwi_read_prom_word(struct iwi_softc *sc, uint8_t addr) { uint32_t tmp; uint16_t val; int n; /* clock C once before the first command */ IWI_EEPROM_CTL(sc, 0); IWI_EEPROM_CTL(sc, IWI_EEPROM_S); IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); IWI_EEPROM_CTL(sc, IWI_EEPROM_S); /* write start bit (1) */ IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D); IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C); /* write READ opcode (10) */ IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D); IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C); IWI_EEPROM_CTL(sc, IWI_EEPROM_S); IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); /* write address A7-A0 */ for (n = 7; n >= 0; n--) { IWI_EEPROM_CTL(sc, IWI_EEPROM_S | (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D)); IWI_EEPROM_CTL(sc, IWI_EEPROM_S | (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D) | IWI_EEPROM_C); } IWI_EEPROM_CTL(sc, IWI_EEPROM_S); /* read data Q15-Q0 */ val = 0; for (n = 15; n >= 0; n--) { IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); IWI_EEPROM_CTL(sc, IWI_EEPROM_S); tmp = MEM_READ_4(sc, IWI_MEM_EEPROM_CTL); val |= ((tmp & IWI_EEPROM_Q) >> IWI_EEPROM_SHIFT_Q) << n; } IWI_EEPROM_CTL(sc, 0); /* clear Chip Select and clock C */ IWI_EEPROM_CTL(sc, IWI_EEPROM_S); IWI_EEPROM_CTL(sc, 0); IWI_EEPROM_CTL(sc, IWI_EEPROM_C); return val; } static void iwi_setcurchan(struct iwi_softc *sc, int chan) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; sc->curchan = chan; sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq = htole16(ic->ic_curchan->ic_freq); sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags = htole16(ic->ic_curchan->ic_flags); } static void iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i, struct iwi_frame *frame) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct mbuf *mnew, *m; struct ieee80211_node *ni; int type, error, framelen; IWI_LOCK_DECL; framelen = le16toh(frame->len); if (framelen < IEEE80211_MIN_LEN || framelen > MCLBYTES) { /* * XXX >MCLBYTES is bogus as it means the h/w dma'd * out of bounds; need to figure out how to limit * frame size in the firmware */ /* XXX stat */ DPRINTFN(1, ("drop rx frame len=%u chan=%u rssi=%u rssi_dbm=%u\n", le16toh(frame->len), frame->chan, frame->rssi, frame->rssi_dbm)); return; } DPRINTFN(5, ("received frame len=%u chan=%u rssi=%u rssi_dbm=%u\n", le16toh(frame->len), frame->chan, frame->rssi, frame->rssi_dbm)); if (frame->chan != sc->curchan) iwi_setcurchan(sc, frame->chan); /* * Try to allocate a new mbuf for this ring element and load it before * processing the current mbuf. If the ring element cannot be loaded, * drop the received packet and reuse the old mbuf. In the unlikely * case that the old mbuf can't be reloaded either, explicitly panic. */ mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) { ifp->if_ierrors++; return; } bus_dmamap_unload(sc->rxq.data_dmat, data->map); error = bus_dmamap_load(sc->rxq.data_dmat, data->map, mtod(mnew, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr, 0); if (error != 0) { m_freem(mnew); /* try to reload the old mbuf */ error = bus_dmamap_load(sc->rxq.data_dmat, data->map, mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr, 0); if (error != 0) { /* very unlikely that it will fail... */ panic("%s: could not load old rx mbuf", device_get_name(sc->sc_dev)); } ifp->if_ierrors++; return; } /* * New mbuf successfully loaded, update Rx ring and continue * processing. */ m = data->m; data->m = mnew; CSR_WRITE_4(sc, data->reg, data->physaddr); /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = sizeof (struct iwi_hdr) + sizeof (struct iwi_frame) + framelen; m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame)); if (bpf_peers_present(ifp->if_bpf)) { struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; tap->wr_rate = iwi_cvtrate(frame->rate); tap->wr_antsignal = frame->signal; tap->wr_antenna = frame->antenna; bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } IWI_UNLOCK(sc); ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { type = ieee80211_input(ni, m, frame->rssi_dbm, 0, 0); ieee80211_free_node(ni); } else type = ieee80211_input_all(ic, m, frame->rssi_dbm, 0, 0); IWI_LOCK(sc); if (sc->sc_softled) { /* * Blink for any data frame. Otherwise do a * heartbeat-style blink when idle. The latter * is mainly for station mode where we depend on * periodic beacon frames to trigger the poll event. */ if (type == IEEE80211_FC0_TYPE_DATA) { sc->sc_rxrate = frame->rate; iwi_led_event(sc, IWI_LED_RX); } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) iwi_led_event(sc, IWI_LED_POLL); } } /* * Check for an association response frame to see if QoS * has been negotiated. We parse just enough to figure * out if we're supposed to use QoS. The proper solution * is to pass the frame up so ieee80211_input can do the * work but that's made hard by how things currently are * done in the driver. */ static void iwi_checkforqos(struct ieee80211vap *vap, const struct ieee80211_frame *wh, int len) { #define SUBTYPE(wh) ((wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) const uint8_t *frm, *efrm, *wme; struct ieee80211_node *ni; uint16_t capinfo, status, associd; /* NB: +8 for capinfo, status, associd, and first ie */ if (!(sizeof(*wh)+8 < len && len < IEEE80211_MAX_LEN) || SUBTYPE(wh) != IEEE80211_FC0_SUBTYPE_ASSOC_RESP) return; /* * asresp frame format * [2] capability information * [2] status * [2] association ID * [tlv] supported rates * [tlv] extended supported rates * [tlv] WME */ frm = (const uint8_t *)&wh[1]; efrm = ((const uint8_t *) wh) + len; capinfo = le16toh(*(const uint16_t *)frm); frm += 2; status = le16toh(*(const uint16_t *)frm); frm += 2; associd = le16toh(*(const uint16_t *)frm); frm += 2; wme = NULL; while (frm < efrm) { IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1], return); switch (*frm) { case IEEE80211_ELEMID_VENDOR: if (iswmeoui(frm)) wme = frm; break; } frm += frm[1] + 2; } ni = vap->iv_bss; ni->ni_capinfo = capinfo; ni->ni_associd = associd; if (wme != NULL) ni->ni_flags |= IEEE80211_NODE_QOS; else ni->ni_flags &= ~IEEE80211_NODE_QOS; #undef SUBTYPE } /* * Task queue callbacks for iwi_notification_intr used to avoid LOR's. */ static void iwi_authsuccess(void *arg, int npending) { struct ieee80211vap *vap = arg; ieee80211_new_state(vap, IEEE80211_S_ASSOC, -1); } static void iwi_assocsuccess(void *arg, int npending) { struct ieee80211vap *vap = arg; ieee80211_new_state(vap, IEEE80211_S_RUN, -1); } static void iwi_assocfailed(void *arg, int npending) { struct ieee80211vap *vap = arg; ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); } static void iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct iwi_notif_scan_channel *chan; struct iwi_notif_scan_complete *scan; struct iwi_notif_authentication *auth; struct iwi_notif_association *assoc; struct iwi_notif_beacon_state *beacon; switch (notif->type) { case IWI_NOTIF_TYPE_SCAN_CHANNEL: chan = (struct iwi_notif_scan_channel *)(notif + 1); DPRINTFN(3, ("Scan of channel %u complete (%u)\n", ieee80211_ieee2mhz(chan->nchan, 0), chan->nchan)); /* Reset the timer, the scan is still going */ sc->sc_state_timer = 3; break; case IWI_NOTIF_TYPE_SCAN_COMPLETE: scan = (struct iwi_notif_scan_complete *)(notif + 1); DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan, scan->status)); IWI_STATE_END(sc, IWI_FW_SCANNING); if (scan->status == IWI_SCAN_COMPLETED) { /* NB: don't need to defer, net80211 does it for us */ ieee80211_scan_next(vap); } break; case IWI_NOTIF_TYPE_AUTHENTICATION: auth = (struct iwi_notif_authentication *)(notif + 1); switch (auth->state) { case IWI_AUTH_SUCCESS: DPRINTFN(2, ("Authentication succeeeded\n")); taskqueue_enqueue(taskqueue_swi, &IWI_VAP(vap)->iwi_authsuccess_task); break; case IWI_AUTH_FAIL: /* * These are delivered as an unsolicited deauth * (e.g. due to inactivity) or in response to an * associate request. */ sc->flags &= ~IWI_FLAG_ASSOCIATED; if (vap->iv_state != IEEE80211_S_RUN) { DPRINTFN(2, ("Authentication failed\n")); vap->iv_stats.is_rx_auth_fail++; IWI_STATE_END(sc, IWI_FW_ASSOCIATING); } else { DPRINTFN(2, ("Deauthenticated\n")); vap->iv_stats.is_rx_deauth++; } taskqueue_enqueue(taskqueue_swi, &IWI_VAP(vap)->iwi_assocfailed_task); break; case IWI_AUTH_SENT_1: case IWI_AUTH_RECV_2: case IWI_AUTH_SEQ1_PASS: break; case IWI_AUTH_SEQ1_FAIL: DPRINTFN(2, ("Initial authentication handshake failed; " "you probably need shared key\n")); vap->iv_stats.is_rx_auth_fail++; IWI_STATE_END(sc, IWI_FW_ASSOCIATING); /* XXX retry shared key when in auto */ break; default: device_printf(sc->sc_dev, "unknown authentication state %u\n", auth->state); break; } break; case IWI_NOTIF_TYPE_ASSOCIATION: assoc = (struct iwi_notif_association *)(notif + 1); switch (assoc->state) { case IWI_AUTH_SUCCESS: /* re-association, do nothing */ break; case IWI_ASSOC_SUCCESS: DPRINTFN(2, ("Association succeeded\n")); sc->flags |= IWI_FLAG_ASSOCIATED; IWI_STATE_END(sc, IWI_FW_ASSOCIATING); iwi_checkforqos(vap, (const struct ieee80211_frame *)(assoc+1), le16toh(notif->len) - sizeof(*assoc)); taskqueue_enqueue(taskqueue_swi, &IWI_VAP(vap)->iwi_assocsuccess_task); break; case IWI_ASSOC_INIT: sc->flags &= ~IWI_FLAG_ASSOCIATED; switch (sc->fw_state) { case IWI_FW_ASSOCIATING: DPRINTFN(2, ("Association failed\n")); IWI_STATE_END(sc, IWI_FW_ASSOCIATING); taskqueue_enqueue(taskqueue_swi, &IWI_VAP(vap)->iwi_assocfailed_task); break; case IWI_FW_DISASSOCIATING: DPRINTFN(2, ("Dissassociated\n")); IWI_STATE_END(sc, IWI_FW_DISASSOCIATING); vap->iv_stats.is_rx_disassoc++; taskqueue_enqueue(taskqueue_swi, &IWI_VAP(vap)->iwi_assocfailed_task); break; } break; default: device_printf(sc->sc_dev, "unknown association state %u\n", assoc->state); break; } break; case IWI_NOTIF_TYPE_BEACON: /* XXX check struct length */ beacon = (struct iwi_notif_beacon_state *)(notif + 1); DPRINTFN(5, ("Beacon state (%u, %u)\n", beacon->state, le32toh(beacon->number))); if (beacon->state == IWI_BEACON_MISS) { /* * The firmware notifies us of every beacon miss * so we need to track the count against the * configured threshold before notifying the * 802.11 layer. * XXX try to roam, drop assoc only on much higher count */ if (le32toh(beacon->number) >= vap->iv_bmissthreshold) { DPRINTF(("Beacon miss: %u >= %u\n", le32toh(beacon->number), vap->iv_bmissthreshold)); vap->iv_stats.is_beacon_miss++; /* * It's pointless to notify the 802.11 layer * as it'll try to send a probe request (which * we'll discard) and then timeout and drop us * into scan state. Instead tell the firmware * to disassociate and then on completion we'll * kick the state machine to scan. */ iwi_queue_cmd(sc, IWI_DISASSOC, 1); } } break; case IWI_NOTIF_TYPE_CALIBRATION: case IWI_NOTIF_TYPE_NOISE: case IWI_NOTIF_TYPE_LINK_QUALITY: DPRINTFN(5, ("Notification (%u)\n", notif->type)); break; default: DPRINTF(("unknown notification type %u flags 0x%x len %u\n", notif->type, notif->flags, le16toh(notif->len))); break; } } static void iwi_rx_intr(struct iwi_softc *sc) { struct iwi_rx_data *data; struct iwi_hdr *hdr; uint32_t hw; hw = CSR_READ_4(sc, IWI_CSR_RX_RIDX); for (; sc->rxq.cur != hw;) { data = &sc->rxq.data[sc->rxq.cur]; bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); hdr = mtod(data->m, struct iwi_hdr *); switch (hdr->type) { case IWI_HDR_TYPE_FRAME: iwi_frame_intr(sc, data, sc->rxq.cur, (struct iwi_frame *)(hdr + 1)); break; case IWI_HDR_TYPE_NOTIF: iwi_notification_intr(sc, (struct iwi_notif *)(hdr + 1)); break; default: device_printf(sc->sc_dev, "unknown hdr type %u\n", hdr->type); } DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur)); sc->rxq.cur = (sc->rxq.cur + 1) % IWI_RX_RING_COUNT; } /* tell the firmware what we have processed */ hw = (hw == 0) ? IWI_RX_RING_COUNT - 1 : hw - 1; CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, hw); } static void iwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq) { struct ifnet *ifp = sc->sc_ifp; struct iwi_tx_data *data; uint32_t hw; hw = CSR_READ_4(sc, txq->csr_ridx); for (; txq->next != hw;) { data = &txq->data[txq->next]; bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(txq->data_dmat, data->map); if (data->m->m_flags & M_TXCB) ieee80211_process_callback(data->ni, data->m, 0/*XXX*/); m_freem(data->m); data->m = NULL; ieee80211_free_node(data->ni); data->ni = NULL; DPRINTFN(15, ("tx done idx=%u\n", txq->next)); ifp->if_opackets++; txq->queued--; txq->next = (txq->next + 1) % IWI_TX_RING_COUNT; } sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (sc->sc_softled) iwi_led_event(sc, IWI_LED_TX); iwi_start_locked(ifp); } static void iwi_intr(void *arg) { struct iwi_softc *sc = arg; uint32_t r; IWI_LOCK_DECL; IWI_LOCK(sc); if ((r = CSR_READ_4(sc, IWI_CSR_INTR)) == 0 || r == 0xffffffff) { IWI_UNLOCK(sc); return; } /* acknowledge interrupts */ CSR_WRITE_4(sc, IWI_CSR_INTR, r); if (r & IWI_INTR_FATAL_ERROR) { device_printf(sc->sc_dev, "firmware error\n"); taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask); sc->flags &= ~IWI_FLAG_BUSY; sc->sc_busy_timer = 0; wakeup(sc); } if (r & IWI_INTR_FW_INITED) { if (!(r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR))) wakeup(sc); } if (r & IWI_INTR_RADIO_OFF) taskqueue_enqueue(sc->sc_tq, &sc->sc_radiofftask); if (r & IWI_INTR_CMD_DONE) { sc->flags &= ~IWI_FLAG_BUSY; sc->sc_busy_timer = 0; wakeup(sc); } if (r & IWI_INTR_TX1_DONE) iwi_tx_intr(sc, &sc->txq[0]); if (r & IWI_INTR_TX2_DONE) iwi_tx_intr(sc, &sc->txq[1]); if (r & IWI_INTR_TX3_DONE) iwi_tx_intr(sc, &sc->txq[2]); if (r & IWI_INTR_TX4_DONE) iwi_tx_intr(sc, &sc->txq[3]); if (r & IWI_INTR_RX_DONE) iwi_rx_intr(sc); if (r & IWI_INTR_PARITY_ERROR) { /* XXX rate-limit */ device_printf(sc->sc_dev, "parity error\n"); } IWI_UNLOCK(sc); } static int iwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len) { struct iwi_cmd_desc *desc; IWI_LOCK_ASSERT(sc); if (sc->flags & IWI_FLAG_BUSY) { device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n", __func__, type); return EAGAIN; } sc->flags |= IWI_FLAG_BUSY; sc->sc_busy_timer = 2; desc = &sc->cmdq.desc[sc->cmdq.cur]; desc->hdr.type = IWI_HDR_TYPE_COMMAND; desc->hdr.flags = IWI_HDR_FLAG_IRQ; desc->type = type; desc->len = len; memcpy(desc->data, data, len); bus_dmamap_sync(sc->cmdq.desc_dmat, sc->cmdq.desc_map, BUS_DMASYNC_PREWRITE); DPRINTFN(2, ("sending command idx=%u type=%u len=%u\n", sc->cmdq.cur, type, len)); sc->cmdq.cur = (sc->cmdq.cur + 1) % IWI_CMD_RING_COUNT; CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur); return msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz); } static void iwi_write_ibssnode(struct iwi_softc *sc, const u_int8_t addr[IEEE80211_ADDR_LEN], int entry) { struct iwi_ibssnode node; /* write node information into NIC memory */ memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.bssid, addr); DPRINTF(("%s mac %6D station %u\n", __func__, node.bssid, ":", entry)); CSR_WRITE_REGION_1(sc, IWI_CSR_NODE_BASE + entry * sizeof node, (uint8_t *)&node, sizeof node); } static int iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni, int ac) { struct iwi_softc *sc = ifp->if_softc; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct iwi_node *in = (struct iwi_node *)ni; const struct ieee80211_frame *wh; struct ieee80211_key *k; const struct chanAccParams *cap; struct iwi_tx_ring *txq = &sc->txq[ac]; struct iwi_tx_data *data; struct iwi_tx_desc *desc; struct mbuf *mnew; bus_dma_segment_t segs[IWI_MAX_NSEG]; int error, nsegs, hdrlen, i; int ismcast, flags, xflags, staid; IWI_LOCK_ASSERT(sc); wh = mtod(m0, const struct ieee80211_frame *); /* NB: only data frames use this path */ hdrlen = ieee80211_hdrsize(wh); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); flags = xflags = 0; if (!ismcast) flags |= IWI_DATA_FLAG_NEED_ACK; if (vap->iv_flags & IEEE80211_F_SHPREAMBLE) flags |= IWI_DATA_FLAG_SHPREAMBLE; if (IEEE80211_QOS_HAS_SEQ(wh)) { xflags |= IWI_DATA_XFLAG_QOS; cap = &ic->ic_wme.wme_chanParams; if (!cap->cap_wmeParams[ac].wmep_noackPolicy) flags &= ~IWI_DATA_FLAG_NEED_ACK; } /* * This is only used in IBSS mode where the firmware expect an index * in a h/w table instead of a destination address. */ if (vap->iv_opmode == IEEE80211_M_IBSS) { if (!ismcast) { if (in->in_station == -1) { in->in_station = alloc_unr(sc->sc_unr); if (in->in_station == -1) { /* h/w table is full */ m_freem(m0); ieee80211_free_node(ni); ifp->if_oerrors++; return 0; } iwi_write_ibssnode(sc, ni->ni_macaddr, in->in_station); } staid = in->in_station; } else { /* * Multicast addresses have no associated node * so there will be no station entry. We reserve * entry 0 for one mcast address and use that. * If there are many being used this will be * expensive and we'll need to do a better job * but for now this handles the broadcast case. */ if (!IEEE80211_ADDR_EQ(wh->i_addr1, sc->sc_mcast)) { IEEE80211_ADDR_COPY(sc->sc_mcast, wh->i_addr1); iwi_write_ibssnode(sc, sc->sc_mcast, 0); } staid = 0; } } else staid = 0; if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } if (bpf_peers_present(ifp->if_bpf)) { struct iwi_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } data = &txq->data[txq->cur]; desc = &txq->desc[txq->cur]; /* save and trim IEEE802.11 header */ m_copydata(m0, 0, hdrlen, (caddr_t)&desc->wh); m_adj(m0, hdrlen); error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, m0, segs, &nsegs, 0); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); m_freem(m0); return error; } if (error != 0) { mnew = m_defrag(m0, M_DONTWAIT); if (mnew == NULL) { device_printf(sc->sc_dev, "could not defragment mbuf\n"); m_freem(m0); return ENOBUFS; } m0 = mnew; error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, m0, segs, &nsegs, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); m_freem(m0); return error; } } data->m = m0; data->ni = ni; desc->hdr.type = IWI_HDR_TYPE_DATA; desc->hdr.flags = IWI_HDR_FLAG_IRQ; desc->station = staid; desc->cmd = IWI_DATA_CMD_TX; desc->len = htole16(m0->m_pkthdr.len); desc->flags = flags; desc->xflags = xflags; #if 0 if (vap->iv_flags & IEEE80211_F_PRIVACY) desc->wep_txkey = vap->iv_def_txkey; else #endif desc->flags |= IWI_DATA_FLAG_NO_WEP; desc->nseg = htole32(nsegs); for (i = 0; i < nsegs; i++) { desc->seg_addr[i] = htole32(segs[i].ds_addr); desc->seg_len[i] = htole16(segs[i].ds_len); } bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE); DPRINTFN(5, ("sending data frame txq=%u idx=%u len=%u nseg=%u\n", ac, txq->cur, le16toh(desc->len), nsegs)); txq->queued++; txq->cur = (txq->cur + 1) % IWI_TX_RING_COUNT; CSR_WRITE_4(sc, txq->csr_widx, txq->cur); return 0; } static int iwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { /* no support; just discard */ m_freem(m); ieee80211_free_node(ni); return 0; } static void iwi_start_locked(struct ifnet *ifp) { struct iwi_softc *sc = ifp->if_softc; struct mbuf *m; struct ieee80211_node *ni; int ac; IWI_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; for (;;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; ac = M_WME_GETAC(m); if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) { /* there is no place left in this ring; tail drop */ /* XXX tail drop */ IFQ_DRV_PREPEND(&ifp->if_snd, m); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } BPF_MTAP(ifp, m); ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); ifp->if_oerrors++; continue; } if (iwi_tx_start(ifp, m, ni, ac) != 0) { ieee80211_free_node(ni); ifp->if_oerrors++; break; } sc->sc_tx_timer = 5; } } static void iwi_start(struct ifnet *ifp) { struct iwi_softc *sc = ifp->if_softc; IWI_LOCK_DECL; IWI_LOCK(sc); iwi_start_locked(ifp); IWI_UNLOCK(sc); } static void iwi_watchdog(void *arg) { struct iwi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; IWI_LOCK_ASSERT(sc); if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { if_printf(ifp, "device timeout\n"); ifp->if_oerrors++; taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask); } } if (sc->sc_state_timer > 0) { if (--sc->sc_state_timer == 0) { if_printf(ifp, "firmware stuck in state %d, resetting\n", sc->fw_state); taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask); if (sc->fw_state == IWI_FW_SCANNING) { struct ieee80211com *ic = ifp->if_l2com; ieee80211_cancel_scan(TAILQ_FIRST(&ic->ic_vaps)); } sc->sc_state_timer = 3; } } if (sc->sc_busy_timer > 0) { if (--sc->sc_busy_timer == 0) { if_printf(ifp, "firmware command timeout, resetting\n"); taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask); } } callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc); } static int iwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct iwi_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; IWI_LOCK_DECL; switch (cmd) { case SIOCSIFFLAGS: IWI_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { iwi_init_locked(sc); startall = 1; } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) iwi_stop_locked(sc); } IWI_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return error; } static void iwi_stop_master(struct iwi_softc *sc) { uint32_t tmp; int ntries; /* disable interrupts */ CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0); CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_STOP_MASTER); for (ntries = 0; ntries < 5; ntries++) { if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED) break; DELAY(10); } if (ntries == 5) device_printf(sc->sc_dev, "timeout waiting for master\n"); tmp = CSR_READ_4(sc, IWI_CSR_RST); CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_PRINCETON_RESET); sc->flags &= ~IWI_FLAG_FW_INITED; } static int iwi_reset(struct iwi_softc *sc) { uint32_t tmp; int i, ntries; iwi_stop_master(sc); tmp = CSR_READ_4(sc, IWI_CSR_CTL); CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT); CSR_WRITE_4(sc, IWI_CSR_READ_INT, IWI_READ_INT_INIT_HOST); /* wait for clock stabilization */ for (ntries = 0; ntries < 1000; ntries++) { if (CSR_READ_4(sc, IWI_CSR_CTL) & IWI_CTL_CLOCK_READY) break; DELAY(200); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for clock stabilization\n"); return EIO; } tmp = CSR_READ_4(sc, IWI_CSR_RST); CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_SOFT_RESET); DELAY(10); tmp = CSR_READ_4(sc, IWI_CSR_CTL); CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT); /* clear NIC memory */ CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0); for (i = 0; i < 0xc000; i++) CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); return 0; } static const struct iwi_firmware_ohdr * iwi_setup_ofw(struct iwi_softc *sc, struct iwi_fw *fw) { const struct firmware *fp = fw->fp; const struct iwi_firmware_ohdr *hdr; if (fp->datasize < sizeof (struct iwi_firmware_ohdr)) { device_printf(sc->sc_dev, "image '%s' too small\n", fp->name); return NULL; } hdr = (const struct iwi_firmware_ohdr *)fp->data; if ((IWI_FW_GET_MAJOR(le32toh(hdr->version)) != IWI_FW_REQ_MAJOR) || (IWI_FW_GET_MINOR(le32toh(hdr->version)) != IWI_FW_REQ_MINOR)) { device_printf(sc->sc_dev, "version for '%s' %d.%d != %d.%d\n", fp->name, IWI_FW_GET_MAJOR(le32toh(hdr->version)), IWI_FW_GET_MINOR(le32toh(hdr->version)), IWI_FW_REQ_MAJOR, IWI_FW_REQ_MINOR); return NULL; } fw->data = ((const char *) fp->data) + sizeof(struct iwi_firmware_ohdr); fw->size = fp->datasize - sizeof(struct iwi_firmware_ohdr); fw->name = fp->name; return hdr; } static const struct iwi_firmware_ohdr * iwi_setup_oucode(struct iwi_softc *sc, struct iwi_fw *fw) { const struct iwi_firmware_ohdr *hdr; hdr = iwi_setup_ofw(sc, fw); if (hdr != NULL && le32toh(hdr->mode) != IWI_FW_MODE_UCODE) { device_printf(sc->sc_dev, "%s is not a ucode image\n", fw->name); hdr = NULL; } return hdr; } static void iwi_getfw(struct iwi_fw *fw, const char *fwname, struct iwi_fw *uc, const char *ucname) { if (fw->fp == NULL) fw->fp = firmware_get(fwname); /* NB: pre-3.0 ucode is packaged separately */ if (uc->fp == NULL && fw->fp != NULL && fw->fp->version < 300) uc->fp = firmware_get(ucname); } /* * Get the required firmware images if not already loaded. * Note that we hold firmware images so long as the device * is marked up in case we need to reload them on device init. * This is necessary because we re-init the device sometimes * from a context where we cannot read from the filesystem * (e.g. from the taskqueue thread when rfkill is re-enabled). * XXX return 0 on success, 1 on error. * * NB: the order of get'ing and put'ing images here is * intentional to support handling firmware images bundled * by operating mode and/or all together in one file with * the boot firmware as "master". */ static int iwi_get_firmware(struct iwi_softc *sc, enum ieee80211_opmode opmode) { const struct iwi_firmware_hdr *hdr; const struct firmware *fp; /* invalidate cached firmware on mode change */ if (sc->fw_mode != opmode) iwi_put_firmware(sc); switch (opmode) { case IEEE80211_M_STA: iwi_getfw(&sc->fw_fw, "iwi_bss", &sc->fw_uc, "iwi_ucode_bss"); break; case IEEE80211_M_IBSS: iwi_getfw(&sc->fw_fw, "iwi_ibss", &sc->fw_uc, "iwi_ucode_ibss"); break; case IEEE80211_M_MONITOR: iwi_getfw(&sc->fw_fw, "iwi_monitor", &sc->fw_uc, "iwi_ucode_monitor"); break; default: break; } fp = sc->fw_fw.fp; if (fp == NULL) { device_printf(sc->sc_dev, "could not load firmware\n"); goto bad; } if (fp->version < 300) { /* * Firmware prior to 3.0 was packaged as separate * boot, firmware, and ucode images. Verify the * ucode image was read in, retrieve the boot image * if needed, and check version stamps for consistency. * The version stamps in the data are also checked * above; this is a bit paranoid but is a cheap * safeguard against mis-packaging. */ if (sc->fw_uc.fp == NULL) { device_printf(sc->sc_dev, "could not load ucode\n"); goto bad; } if (sc->fw_boot.fp == NULL) { sc->fw_boot.fp = firmware_get("iwi_boot"); if (sc->fw_boot.fp == NULL) { device_printf(sc->sc_dev, "could not load boot firmware\n"); goto bad; } } if (sc->fw_boot.fp->version != sc->fw_fw.fp->version || sc->fw_boot.fp->version != sc->fw_uc.fp->version) { device_printf(sc->sc_dev, "firmware version mismatch: " "'%s' is %d, '%s' is %d, '%s' is %d\n", sc->fw_boot.fp->name, sc->fw_boot.fp->version, sc->fw_uc.fp->name, sc->fw_uc.fp->version, sc->fw_fw.fp->name, sc->fw_fw.fp->version ); goto bad; } /* * Check and setup each image. */ if (iwi_setup_oucode(sc, &sc->fw_uc) == NULL || iwi_setup_ofw(sc, &sc->fw_boot) == NULL || iwi_setup_ofw(sc, &sc->fw_fw) == NULL) goto bad; } else { /* * Check and setup combined image. */ if (fp->datasize < sizeof(struct iwi_firmware_hdr)) { device_printf(sc->sc_dev, "image '%s' too small\n", fp->name); goto bad; } hdr = (const struct iwi_firmware_hdr *)fp->data; if (fp->datasize < sizeof(*hdr) + le32toh(hdr->bsize) + le32toh(hdr->usize) + le32toh(hdr->fsize)) { device_printf(sc->sc_dev, "image '%s' too small (2)\n", fp->name); goto bad; } sc->fw_boot.data = ((const char *) fp->data) + sizeof(*hdr); sc->fw_boot.size = le32toh(hdr->bsize); sc->fw_boot.name = fp->name; sc->fw_uc.data = sc->fw_boot.data + sc->fw_boot.size; sc->fw_uc.size = le32toh(hdr->usize); sc->fw_uc.name = fp->name; sc->fw_fw.data = sc->fw_uc.data + sc->fw_uc.size; sc->fw_fw.size = le32toh(hdr->fsize); sc->fw_fw.name = fp->name; } #if 0 device_printf(sc->sc_dev, "boot %d ucode %d fw %d bytes\n", sc->fw_boot.size, sc->fw_uc.size, sc->fw_fw.size); #endif sc->fw_mode = opmode; return 0; bad: iwi_put_firmware(sc); return 1; } static void iwi_put_fw(struct iwi_fw *fw) { if (fw->fp != NULL) { firmware_put(fw->fp, FIRMWARE_UNLOAD); fw->fp = NULL; } fw->data = NULL; fw->size = 0; fw->name = NULL; } /* * Release any cached firmware images. */ static void iwi_put_firmware(struct iwi_softc *sc) { iwi_put_fw(&sc->fw_uc); iwi_put_fw(&sc->fw_fw); iwi_put_fw(&sc->fw_boot); } static int iwi_load_ucode(struct iwi_softc *sc, const struct iwi_fw *fw) { uint32_t tmp; const uint16_t *w; const char *uc = fw->data; size_t size = fw->size; int i, ntries, error; IWI_LOCK_ASSERT(sc); error = 0; CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) | IWI_RST_STOP_MASTER); for (ntries = 0; ntries < 5; ntries++) { if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED) break; DELAY(10); } if (ntries == 5) { device_printf(sc->sc_dev, "timeout waiting for master\n"); error = EIO; goto fail; } MEM_WRITE_4(sc, 0x3000e0, 0x80000000); DELAY(5000); tmp = CSR_READ_4(sc, IWI_CSR_RST); tmp &= ~IWI_RST_PRINCETON_RESET; CSR_WRITE_4(sc, IWI_CSR_RST, tmp); DELAY(5000); MEM_WRITE_4(sc, 0x3000e0, 0); DELAY(1000); MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 1); DELAY(1000); MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 0); DELAY(1000); MEM_WRITE_1(sc, 0x200000, 0x00); MEM_WRITE_1(sc, 0x200000, 0x40); DELAY(1000); /* write microcode into adapter memory */ for (w = (const uint16_t *)uc; size > 0; w++, size -= 2) MEM_WRITE_2(sc, 0x200010, htole16(*w)); MEM_WRITE_1(sc, 0x200000, 0x00); MEM_WRITE_1(sc, 0x200000, 0x80); /* wait until we get an answer */ for (ntries = 0; ntries < 100; ntries++) { if (MEM_READ_1(sc, 0x200000) & 1) break; DELAY(100); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for ucode to initialize\n"); error = EIO; goto fail; } /* read the answer or the firmware will not initialize properly */ for (i = 0; i < 7; i++) MEM_READ_4(sc, 0x200004); MEM_WRITE_1(sc, 0x200000, 0x00); fail: return error; } /* macro to handle unaligned little endian data in firmware image */ #define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) static int iwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw) { u_char *p, *end; uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp; int ntries, error; IWI_LOCK_ASSERT(sc); /* copy firmware image to DMA memory */ memcpy(sc->fw_virtaddr, fw->data, fw->size); /* make sure the adapter will get up-to-date values */ bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_PREWRITE); /* tell the adapter where the command blocks are stored */ MEM_WRITE_4(sc, 0x3000a0, 0x27000); /* * Store command blocks into adapter's internal memory using register * indirections. The adapter will read the firmware image through DMA * using information stored in command blocks. */ src = sc->fw_physaddr; p = sc->fw_virtaddr; end = p + fw->size; CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0x27000); while (p < end) { dst = GETLE32(p); p += 4; src += 4; len = GETLE32(p); p += 4; src += 4; p += len; while (len > 0) { mlen = min(len, IWI_CB_MAXDATALEN); ctl = IWI_CB_DEFAULT_CTL | mlen; sum = ctl ^ src ^ dst; /* write a command block */ CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, ctl); CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, src); CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, dst); CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, sum); src += mlen; dst += mlen; len -= mlen; } } /* write a fictive final command block (sentinel) */ sentinel = CSR_READ_4(sc, IWI_CSR_AUTOINC_ADDR); CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); tmp = CSR_READ_4(sc, IWI_CSR_RST); tmp &= ~(IWI_RST_MASTER_DISABLED | IWI_RST_STOP_MASTER); CSR_WRITE_4(sc, IWI_CSR_RST, tmp); /* tell the adapter to start processing command blocks */ MEM_WRITE_4(sc, 0x3000a4, 0x540100); /* wait until the adapter reaches the sentinel */ for (ntries = 0; ntries < 400; ntries++) { if (MEM_READ_4(sc, 0x3000d0) >= sentinel) break; DELAY(100); } /* sync dma, just in case */ bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_POSTWRITE); if (ntries == 400) { device_printf(sc->sc_dev, "timeout processing command blocks for %s firmware\n", fw->name); return EIO; } /* we're done with command blocks processing */ MEM_WRITE_4(sc, 0x3000a4, 0x540c00); /* allow interrupts so we know when the firmware is ready */ CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK); /* tell the adapter to initialize the firmware */ CSR_WRITE_4(sc, IWI_CSR_RST, 0); tmp = CSR_READ_4(sc, IWI_CSR_CTL); CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_ALLOW_STANDBY); /* wait at most one second for firmware initialization to complete */ if ((error = msleep(sc, &sc->sc_mtx, 0, "iwiinit", hz)) != 0) { device_printf(sc->sc_dev, "timeout waiting for %s firmware " "initialization to complete\n", fw->name); } return error; } static int iwi_setpowermode(struct iwi_softc *sc, struct ieee80211vap *vap) { uint32_t data; if (vap->iv_flags & IEEE80211_F_PMGTON) { /* XXX set more fine-grained operation */ data = htole32(IWI_POWER_MODE_MAX); } else data = htole32(IWI_POWER_MODE_CAM); DPRINTF(("Setting power mode to %u\n", le32toh(data))); return iwi_cmd(sc, IWI_CMD_SET_POWER_MODE, &data, sizeof data); } static int iwi_setwepkeys(struct iwi_softc *sc, struct ieee80211vap *vap) { struct iwi_wep_key wepkey; struct ieee80211_key *wk; int error, i; for (i = 0; i < IEEE80211_WEP_NKID; i++) { wk = &vap->iv_nw_keys[i]; wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY; wepkey.idx = i; wepkey.len = wk->wk_keylen; memset(wepkey.key, 0, sizeof wepkey.key); memcpy(wepkey.key, wk->wk_key, wk->wk_keylen); DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx, wepkey.len)); error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey, sizeof wepkey); if (error != 0) return error; } return 0; } static int iwi_config(struct iwi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwi_configuration config; struct iwi_rateset rs; struct iwi_txpower power; uint32_t data; int error, i; IWI_LOCK_ASSERT(sc); IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); DPRINTF(("Setting MAC address to %6D\n", ic->ic_myaddr, ":")); error = iwi_cmd(sc, IWI_CMD_SET_MAC_ADDRESS, ic->ic_myaddr, IEEE80211_ADDR_LEN); if (error != 0) return error; memset(&config, 0, sizeof config); config.bluetooth_coexistence = sc->bluetooth; config.silence_threshold = 0x1e; config.antenna = sc->antenna; config.multicast_enabled = 1; config.answer_pbreq = (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0; config.disable_unicast_decryption = 1; config.disable_multicast_decryption = 1; DPRINTF(("Configuring adapter\n")); error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config); if (error != 0) return error; if (ic->ic_opmode == IEEE80211_M_IBSS) { power.mode = IWI_MODE_11B; power.nchan = 11; for (i = 0; i < 11; i++) { power.chan[i].chan = i + 1; power.chan[i].power = IWI_TXPOWER_MAX; } DPRINTF(("Setting .11b channels tx power\n")); error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power); if (error != 0) return error; power.mode = IWI_MODE_11G; DPRINTF(("Setting .11g channels tx power\n")); error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power); if (error != 0) return error; } memset(&rs, 0, sizeof rs); rs.mode = IWI_MODE_11G; rs.type = IWI_RATESET_TYPE_SUPPORTED; rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates; memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates, rs.nrates); DPRINTF(("Setting .11bg supported rates (%u)\n", rs.nrates)); error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); if (error != 0) return error; memset(&rs, 0, sizeof rs); rs.mode = IWI_MODE_11A; rs.type = IWI_RATESET_TYPE_SUPPORTED; rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates; memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates, rs.nrates); DPRINTF(("Setting .11a supported rates (%u)\n", rs.nrates)); error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); if (error != 0) return error; data = htole32(arc4random()); DPRINTF(("Setting initialization vector to %u\n", le32toh(data))); error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data); if (error != 0) return error; /* enable adapter */ DPRINTF(("Enabling adapter\n")); return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0); } static __inline void set_scan_type(struct iwi_scan_ext *scan, int ix, int scan_type) { uint8_t *st = &scan->scan_type[ix / 2]; if (ix % 2) *st = (*st & 0xf0) | ((scan_type & 0xf) << 0); else *st = (*st & 0x0f) | ((scan_type & 0xf) << 4); } static int scan_type(const struct ieee80211_scan_state *ss, const struct ieee80211_channel *chan) { /* We can only set one essid for a directed scan */ if (ss->ss_nssid != 0) return IWI_SCAN_TYPE_BDIRECTED; if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) && (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) return IWI_SCAN_TYPE_BROADCAST; return IWI_SCAN_TYPE_PASSIVE; } static __inline int scan_band(const struct ieee80211_channel *c) { return IEEE80211_IS_CHAN_5GHZ(c) ? IWI_CHAN_5GHZ : IWI_CHAN_2GHZ; } /* * Start a scan on the current channel or all channels. */ static int iwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int mode) { struct ieee80211com *ic; struct ieee80211_channel *chan; struct ieee80211_scan_state *ss; struct iwi_scan_ext scan; int error = 0; IWI_LOCK_ASSERT(sc); if (sc->fw_state == IWI_FW_SCANNING) { /* * This should not happen as we only trigger scan_next after * completion */ DPRINTF(("%s: called too early - still scanning\n", __func__)); return (EBUSY); } IWI_STATE_BEGIN(sc, IWI_FW_SCANNING); ic = sc->sc_ifp->if_l2com; ss = ic->ic_scan; memset(&scan, 0, sizeof scan); scan.full_scan_index = htole32(++sc->sc_scangen); scan.dwell_time[IWI_SCAN_TYPE_PASSIVE] = htole16(maxdwell); if (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) { /* * Use very short dwell times for when we send probe request * frames. Without this bg scans hang. Ideally this should * be handled with early-termination as done by net80211 but * that's not feasible (aborting a scan is problematic). */ scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(30); scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(30); } else { scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(maxdwell); scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(maxdwell); } /* We can only set one essid for a directed scan */ if (ss->ss_nssid != 0) { error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); if (error) return (error); } if (mode == IWI_SCAN_ALLCHAN) { int i, next, band, b, bstart; /* * Convert scan list to run-length encoded channel list * the firmware requires (preserving the order setup by * net80211). The first entry in each run specifies the * band and the count of items in the run. */ next = 0; /* next open slot */ bstart = 0; /* NB: not needed, silence compiler */ band = -1; /* NB: impossible value */ KASSERT(ss->ss_last > 0, ("no channels")); for (i = 0; i < ss->ss_last; i++) { chan = ss->ss_chans[i]; b = scan_band(chan); if (b != band) { if (band != -1) scan.channels[bstart] = (next - bstart) | band; /* NB: this allocates a slot for the run-len */ band = b, bstart = next++; } if (next >= IWI_SCAN_CHANNELS) { DPRINTF(("truncating scan list\n")); break; } scan.channels[next] = ieee80211_chan2ieee(ic, chan); set_scan_type(&scan, next, scan_type(ss, chan)); next++; } scan.channels[bstart] = (next - bstart) | band; } else { /* Scan the current channel only */ chan = ic->ic_curchan; scan.channels[0] = 1 | scan_band(chan); scan.channels[1] = ieee80211_chan2ieee(ic, chan); set_scan_type(&scan, 1, scan_type(ss, chan)); } #ifdef IWI_DEBUG if (iwi_debug > 0) { static const char *scantype[8] = { "PSTOP", "PASV", "DIR", "BCAST", "BDIR", "5", "6", "7" }; int i; printf("Scan request: index %u dwell %d/%d/%d\n" , le32toh(scan.full_scan_index) , le16toh(scan.dwell_time[IWI_SCAN_TYPE_PASSIVE]) , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BROADCAST]) , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED]) ); i = 0; do { int run = scan.channels[i]; if (run == 0) break; printf("Scan %d %s channels:", run & 0x3f, run & IWI_CHAN_2GHZ ? "2.4GHz" : "5GHz"); for (run &= 0x3f, i++; run > 0; run--, i++) { uint8_t type = scan.scan_type[i/2]; printf(" %u/%s", scan.channels[i], scantype[(i & 1 ? type : type>>4) & 7]); } printf("\n"); } while (i < IWI_SCAN_CHANNELS); } #endif return (iwi_cmd(sc, IWI_CMD_SCAN_EXT, &scan, sizeof scan)); } static void iwi_scanabort(void *arg, int npending) { struct iwi_softc *sc = arg; IWI_LOCK_DECL; IWI_LOCK(sc); sc->flags &= ~IWI_FLAG_CHANNEL_SCAN; /* NB: make sure we're still scanning */ if (sc->fw_state == IWI_FW_SCANNING) iwi_cmd(sc, IWI_CMD_ABORT_SCAN, NULL, 0); IWI_UNLOCK(sc); } static int iwi_set_sensitivity(struct iwi_softc *sc, int8_t rssi_dbm) { struct iwi_sensitivity sens; DPRINTF(("Setting sensitivity to %d\n", rssi_dbm)); memset(&sens, 0, sizeof sens); sens.rssi = htole16(rssi_dbm); return iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &sens, sizeof sens); } static int iwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = vap->iv_ifp; struct ieee80211_node *ni = vap->iv_bss; struct iwi_configuration config; struct iwi_associate *assoc = &sc->assoc; struct iwi_rateset rs; uint16_t capinfo; uint32_t data; int error, mode; IWI_LOCK_ASSERT(sc); if (sc->flags & IWI_FLAG_ASSOCIATED) { DPRINTF(("Already associated\n")); return (-1); } IWI_STATE_BEGIN(sc, IWI_FW_ASSOCIATING); error = 0; mode = 0; if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) mode = IWI_MODE_11A; else if (IEEE80211_IS_CHAN_G(ic->ic_curchan)) mode = IWI_MODE_11G; if (IEEE80211_IS_CHAN_B(ic->ic_curchan)) mode = IWI_MODE_11B; if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { memset(&config, 0, sizeof config); config.bluetooth_coexistence = sc->bluetooth; config.antenna = sc->antenna; config.multicast_enabled = 1; if (mode == IWI_MODE_11G) config.use_protection = 1; config.answer_pbreq = (vap->iv_opmode == IEEE80211_M_IBSS) ? 1 : 0; config.disable_unicast_decryption = 1; config.disable_multicast_decryption = 1; DPRINTF(("Configuring adapter\n")); error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config); if (error != 0) goto done; } #ifdef IWI_DEBUG if (iwi_debug > 0) { printf("Setting ESSID to "); ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); printf("\n"); } #endif error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen); if (error != 0) goto done; error = iwi_setpowermode(sc, vap); if (error != 0) goto done; data = htole32(vap->iv_rtsthreshold); DPRINTF(("Setting RTS threshold to %u\n", le32toh(data))); error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data); if (error != 0) goto done; data = htole32(vap->iv_fragthreshold); DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data))); error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data); if (error != 0) goto done; /* the rate set has already been "negotiated" */ memset(&rs, 0, sizeof rs); rs.mode = mode; rs.type = IWI_RATESET_TYPE_NEGOTIATED; rs.nrates = ni->ni_rates.rs_nrates; if (rs.nrates > IWI_RATESET_SIZE) { DPRINTF(("Truncating negotiated rate set from %u\n", rs.nrates)); rs.nrates = IWI_RATESET_SIZE; } memcpy(rs.rates, ni->ni_rates.rs_rates, rs.nrates); DPRINTF(("Setting negotiated rates (%u)\n", rs.nrates)); error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); if (error != 0) goto done; memset(assoc, 0, sizeof *assoc); if ((vap->iv_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) { /* NB: don't treat WME setup as failure */ if (iwi_wme_setparams(sc, ic) == 0 && iwi_wme_setie(sc) == 0) assoc->policy |= htole16(IWI_POLICY_WME); /* XXX complain on failure? */ } if (vap->iv_appie_wpa != NULL) { struct ieee80211_appie *ie = vap->iv_appie_wpa; DPRINTF(("Setting optional IE (len=%u)\n", ie->ie_len)); error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ie->ie_data, ie->ie_len); if (error != 0) goto done; } error = iwi_set_sensitivity(sc, ic->ic_node_getrssi(ni)); if (error != 0) goto done; assoc->mode = mode; assoc->chan = ic->ic_curchan->ic_ieee; /* * NB: do not arrange for shared key auth w/o privacy * (i.e. a wep key); it causes a firmware error. */ if ((vap->iv_flags & IEEE80211_F_PRIVACY) && ni->ni_authmode == IEEE80211_AUTH_SHARED) { assoc->auth = IWI_AUTH_SHARED; /* * It's possible to have privacy marked but no default * key setup. This typically is due to a user app bug * but if we blindly grab the key the firmware will * barf so avoid it for now. */ if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) assoc->auth |= vap->iv_def_txkey << 4; error = iwi_setwepkeys(sc, vap); if (error != 0) goto done; } if (vap->iv_flags & IEEE80211_F_WPA) assoc->policy |= htole16(IWI_POLICY_WPA); if (vap->iv_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf == 0) assoc->type = IWI_HC_IBSS_START; else assoc->type = IWI_HC_ASSOC; memcpy(assoc->tstamp, ni->ni_tstamp.data, 8); if (vap->iv_opmode == IEEE80211_M_IBSS) capinfo = IEEE80211_CAPINFO_IBSS; else capinfo = IEEE80211_CAPINFO_ESS; if (vap->iv_flags & IEEE80211_F_PRIVACY) capinfo |= IEEE80211_CAPINFO_PRIVACY; if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; if (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; assoc->capinfo = htole16(capinfo); assoc->lintval = htole16(ic->ic_lintval); assoc->intval = htole16(ni->ni_intval); IEEE80211_ADDR_COPY(assoc->bssid, ni->ni_bssid); if (vap->iv_opmode == IEEE80211_M_IBSS) IEEE80211_ADDR_COPY(assoc->dst, ifp->if_broadcastaddr); else IEEE80211_ADDR_COPY(assoc->dst, ni->ni_bssid); DPRINTF(("%s bssid %6D dst %6D channel %u policy 0x%x " "auth %u capinfo 0x%x lintval %u bintval %u\n", assoc->type == IWI_HC_IBSS_START ? "Start" : "Join", assoc->bssid, ":", assoc->dst, ":", assoc->chan, le16toh(assoc->policy), assoc->auth, le16toh(assoc->capinfo), le16toh(assoc->lintval), le16toh(assoc->intval))); error = iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc); done: if (error) IWI_STATE_END(sc, IWI_FW_ASSOCIATING); return (error); } static int iwi_disassociate(struct iwi_softc *sc, int quiet) { struct iwi_associate *assoc = &sc->assoc; if ((sc->flags & IWI_FLAG_ASSOCIATED) == 0) { DPRINTF(("Not associated\n")); return (-1); } IWI_STATE_BEGIN(sc, IWI_FW_DISASSOCIATING); if (quiet) assoc->type = IWI_HC_DISASSOC_QUIET; else assoc->type = IWI_HC_DISASSOC; DPRINTF(("Trying to disassociate from %6D channel %u\n", assoc->bssid, ":", assoc->chan)); return iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc); } /* * release dma resources for the firmware */ static void iwi_release_fw_dma(struct iwi_softc *sc) { if (sc->fw_flags & IWI_FW_HAVE_PHY) bus_dmamap_unload(sc->fw_dmat, sc->fw_map); if (sc->fw_flags & IWI_FW_HAVE_MAP) bus_dmamem_free(sc->fw_dmat, sc->fw_virtaddr, sc->fw_map); if (sc->fw_flags & IWI_FW_HAVE_DMAT) bus_dma_tag_destroy(sc->fw_dmat); sc->fw_flags = 0; sc->fw_dma_size = 0; sc->fw_dmat = NULL; sc->fw_map = NULL; sc->fw_physaddr = 0; sc->fw_virtaddr = NULL; } /* * allocate the dma descriptor for the firmware. * Return 0 on success, 1 on error. * Must be called unlocked, protected by IWI_FLAG_FW_LOADING. */ static int iwi_init_fw_dma(struct iwi_softc *sc, int size) { if (sc->fw_dma_size >= size) return 0; if (bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, 0, NULL, NULL, &sc->fw_dmat) != 0) { device_printf(sc->sc_dev, "could not create firmware DMA tag\n"); goto error; } sc->fw_flags |= IWI_FW_HAVE_DMAT; if (bus_dmamem_alloc(sc->fw_dmat, &sc->fw_virtaddr, 0, &sc->fw_map) != 0) { device_printf(sc->sc_dev, "could not allocate firmware DMA memory\n"); goto error; } sc->fw_flags |= IWI_FW_HAVE_MAP; if (bus_dmamap_load(sc->fw_dmat, sc->fw_map, sc->fw_virtaddr, size, iwi_dma_map_addr, &sc->fw_physaddr, 0) != 0) { device_printf(sc->sc_dev, "could not load firmware DMA map\n"); goto error; } sc->fw_flags |= IWI_FW_HAVE_PHY; sc->fw_dma_size = size; return 0; error: iwi_release_fw_dma(sc); return 1; } static void iwi_init_locked(struct iwi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct iwi_rx_data *data; int i; IWI_LOCK_ASSERT(sc); if (sc->fw_state == IWI_FW_LOADING) { device_printf(sc->sc_dev, "%s: already loading\n", __func__); return; /* XXX: condvar? */ } iwi_stop_locked(sc); IWI_STATE_BEGIN(sc, IWI_FW_LOADING); taskqueue_unblock(sc->sc_tq); taskqueue_unblock(sc->sc_tq2); if (iwi_reset(sc) != 0) { device_printf(sc->sc_dev, "could not reset adapter\n"); goto fail; } if (iwi_load_firmware(sc, &sc->fw_boot) != 0) { device_printf(sc->sc_dev, "could not load boot firmware %s\n", sc->fw_boot.name); goto fail; } if (iwi_load_ucode(sc, &sc->fw_uc) != 0) { device_printf(sc->sc_dev, "could not load microcode %s\n", sc->fw_uc.name); goto fail; } iwi_stop_master(sc); CSR_WRITE_4(sc, IWI_CSR_CMD_BASE, sc->cmdq.physaddr); CSR_WRITE_4(sc, IWI_CSR_CMD_SIZE, sc->cmdq.count); CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur); CSR_WRITE_4(sc, IWI_CSR_TX1_BASE, sc->txq[0].physaddr); CSR_WRITE_4(sc, IWI_CSR_TX1_SIZE, sc->txq[0].count); CSR_WRITE_4(sc, IWI_CSR_TX1_WIDX, sc->txq[0].cur); CSR_WRITE_4(sc, IWI_CSR_TX2_BASE, sc->txq[1].physaddr); CSR_WRITE_4(sc, IWI_CSR_TX2_SIZE, sc->txq[1].count); CSR_WRITE_4(sc, IWI_CSR_TX2_WIDX, sc->txq[1].cur); CSR_WRITE_4(sc, IWI_CSR_TX3_BASE, sc->txq[2].physaddr); CSR_WRITE_4(sc, IWI_CSR_TX3_SIZE, sc->txq[2].count); CSR_WRITE_4(sc, IWI_CSR_TX3_WIDX, sc->txq[2].cur); CSR_WRITE_4(sc, IWI_CSR_TX4_BASE, sc->txq[3].physaddr); CSR_WRITE_4(sc, IWI_CSR_TX4_SIZE, sc->txq[3].count); CSR_WRITE_4(sc, IWI_CSR_TX4_WIDX, sc->txq[3].cur); for (i = 0; i < sc->rxq.count; i++) { data = &sc->rxq.data[i]; CSR_WRITE_4(sc, data->reg, data->physaddr); } CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, sc->rxq.count - 1); if (iwi_load_firmware(sc, &sc->fw_fw) != 0) { device_printf(sc->sc_dev, "could not load main firmware %s\n", sc->fw_fw.name); goto fail; } sc->flags |= IWI_FLAG_FW_INITED; IWI_STATE_END(sc, IWI_FW_LOADING); if (iwi_config(sc) != 0) { device_printf(sc->sc_dev, "unable to enable adapter\n"); goto fail2; } callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; return; fail: IWI_STATE_END(sc, IWI_FW_LOADING); fail2: iwi_stop_locked(sc); } static void iwi_init(void *priv) { struct iwi_softc *sc = priv; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; IWI_LOCK_DECL; IWI_LOCK(sc); iwi_init_locked(sc); IWI_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); } static void iwi_stop_locked(void *priv) { struct iwi_softc *sc = priv; struct ifnet *ifp = sc->sc_ifp; IWI_LOCK_ASSERT(sc); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); taskqueue_block(sc->sc_tq); taskqueue_block(sc->sc_tq2); if (sc->sc_softled) { callout_stop(&sc->sc_ledtimer); sc->sc_blinking = 0; } callout_stop(&sc->sc_wdtimer); callout_stop(&sc->sc_rftimer); iwi_stop_master(sc); CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SOFT_RESET); /* reset rings */ iwi_reset_cmd_ring(sc, &sc->cmdq); iwi_reset_tx_ring(sc, &sc->txq[0]); iwi_reset_tx_ring(sc, &sc->txq[1]); iwi_reset_tx_ring(sc, &sc->txq[2]); iwi_reset_tx_ring(sc, &sc->txq[3]); iwi_reset_rx_ring(sc, &sc->rxq); memset(sc->sc_cmd, 0, sizeof(sc->sc_cmd)); sc->sc_tx_timer = 0; sc->sc_state_timer = 0; sc->sc_busy_timer = 0; sc->flags &= ~(IWI_FLAG_BUSY | IWI_FLAG_ASSOCIATED); sc->fw_state = IWI_FW_IDLE; wakeup(sc); } static void iwi_stop(struct iwi_softc *sc) { IWI_LOCK_DECL; IWI_LOCK(sc); iwi_stop_locked(sc); IWI_UNLOCK(sc); } static void iwi_restart(void *arg, int npending) { struct iwi_softc *sc = arg; iwi_init(sc); } /* * Return whether or not the radio is enabled in hardware * (i.e. the rfkill switch is "off"). */ static int iwi_getrfkill(struct iwi_softc *sc) { return (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) == 0; } static void iwi_radio_on(void *arg, int pending) { struct iwi_softc *sc = arg; struct ieee80211com *ic = sc->sc_ifp->if_l2com; device_printf(sc->sc_dev, "radio turned on\n"); iwi_init(sc); ieee80211_notify_radio(ic, 1); } static void iwi_rfkill_poll(void *arg) { struct iwi_softc *sc = arg; IWI_LOCK_ASSERT(sc); /* * Check for a change in rfkill state. We get an * interrupt when a radio is disabled but not when * it is enabled so we must poll for the latter. */ if (!iwi_getrfkill(sc)) { taskqueue_unblock(sc->sc_tq); taskqueue_enqueue(sc->sc_tq, &sc->sc_radiontask); return; } callout_reset(&sc->sc_rftimer, 2*hz, iwi_rfkill_poll, sc); } static void iwi_radio_off(void *arg, int pending) { struct iwi_softc *sc = arg; struct ieee80211com *ic = sc->sc_ifp->if_l2com; IWI_LOCK_DECL; device_printf(sc->sc_dev, "radio turned off\n"); ieee80211_notify_radio(ic, 0); IWI_LOCK(sc); iwi_stop_locked(sc); iwi_rfkill_poll(sc); IWI_UNLOCK(sc); } static int iwi_sysctl_stats(SYSCTL_HANDLER_ARGS) { struct iwi_softc *sc = arg1; uint32_t size, buf[128]; memset(buf, 0, sizeof buf); if (!(sc->flags & IWI_FLAG_FW_INITED)) return SYSCTL_OUT(req, buf, sizeof buf); size = min(CSR_READ_4(sc, IWI_CSR_TABLE0_SIZE), 128 - 1); CSR_READ_REGION_4(sc, IWI_CSR_TABLE0_BASE, &buf[1], size); return SYSCTL_OUT(req, buf, size); } static int iwi_sysctl_radio(SYSCTL_HANDLER_ARGS) { struct iwi_softc *sc = arg1; int val = !iwi_getrfkill(sc); return SYSCTL_OUT(req, &val, sizeof val); } /* * Add sysctl knobs. */ static void iwi_sysctlattach(struct iwi_softc *sc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "radio", CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I", "radio transmitter switch state (0=off, 1=on)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "stats", CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, "S", "statistics"); sc->bluetooth = 0; SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "bluetooth", CTLFLAG_RW, &sc->bluetooth, 0, "bluetooth coexistence"); sc->antenna = IWI_ANTENNA_AUTO; SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "antenna", CTLFLAG_RW, &sc->antenna, 0, "antenna (0=auto)"); } /* * LED support. * * Different cards have different capabilities. Some have three * led's while others have only one. The linux ipw driver defines * led's for link state (associated or not), band (11a, 11g, 11b), * and for link activity. We use one led and vary the blink rate * according to the tx/rx traffic a la the ath driver. */ static __inline uint32_t iwi_toggle_event(uint32_t r) { return r &~ (IWI_RST_STANDBY | IWI_RST_GATE_ODMA | IWI_RST_GATE_IDMA | IWI_RST_GATE_ADMA); } static uint32_t iwi_read_event(struct iwi_softc *sc) { return MEM_READ_4(sc, IWI_MEM_EEPROM_EVENT); } static void iwi_write_event(struct iwi_softc *sc, uint32_t v) { MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, v); } static void iwi_led_done(void *arg) { struct iwi_softc *sc = arg; sc->sc_blinking = 0; } /* * Turn the activity LED off: flip the pin and then set a timer so no * update will happen for the specified duration. */ static void iwi_led_off(void *arg) { struct iwi_softc *sc = arg; uint32_t v; v = iwi_read_event(sc); v &= ~sc->sc_ledpin; iwi_write_event(sc, iwi_toggle_event(v)); callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, iwi_led_done, sc); } /* * Blink the LED according to the specified on/off times. */ static void iwi_led_blink(struct iwi_softc *sc, int on, int off) { uint32_t v; v = iwi_read_event(sc); v |= sc->sc_ledpin; iwi_write_event(sc, iwi_toggle_event(v)); sc->sc_blinking = 1; sc->sc_ledoff = off; callout_reset(&sc->sc_ledtimer, on, iwi_led_off, sc); } static void iwi_led_event(struct iwi_softc *sc, int event) { #define N(a) (sizeof(a)/sizeof(a[0])) /* NB: on/off times from the Atheros NDIS driver, w/ permission */ static const struct { u_int rate; /* tx/rx iwi rate */ u_int16_t timeOn; /* LED on time (ms) */ u_int16_t timeOff; /* LED off time (ms) */ } blinkrates[] = { { IWI_RATE_OFDM54, 40, 10 }, { IWI_RATE_OFDM48, 44, 11 }, { IWI_RATE_OFDM36, 50, 13 }, { IWI_RATE_OFDM24, 57, 14 }, { IWI_RATE_OFDM18, 67, 16 }, { IWI_RATE_OFDM12, 80, 20 }, { IWI_RATE_DS11, 100, 25 }, { IWI_RATE_OFDM9, 133, 34 }, { IWI_RATE_OFDM6, 160, 40 }, { IWI_RATE_DS5, 200, 50 }, { 6, 240, 58 }, /* XXX 3Mb/s if it existed */ { IWI_RATE_DS2, 267, 66 }, { IWI_RATE_DS1, 400, 100 }, { 0, 500, 130 }, /* unknown rate/polling */ }; uint32_t txrate; int j = 0; /* XXX silence compiler */ sc->sc_ledevent = ticks; /* time of last event */ if (sc->sc_blinking) /* don't interrupt active blink */ return; switch (event) { case IWI_LED_POLL: j = N(blinkrates)-1; break; case IWI_LED_TX: /* read current transmission rate from adapter */ txrate = CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE); if (blinkrates[sc->sc_txrix].rate != txrate) { for (j = 0; j < N(blinkrates)-1; j++) if (blinkrates[j].rate == txrate) break; sc->sc_txrix = j; } else j = sc->sc_txrix; break; case IWI_LED_RX: if (blinkrates[sc->sc_rxrix].rate != sc->sc_rxrate) { for (j = 0; j < N(blinkrates)-1; j++) if (blinkrates[j].rate == sc->sc_rxrate) break; sc->sc_rxrix = j; } else j = sc->sc_rxrix; break; } /* XXX beware of overflow */ iwi_led_blink(sc, (blinkrates[j].timeOn * hz) / 1000, (blinkrates[j].timeOff * hz) / 1000); #undef N } static int iwi_sysctl_softled(SYSCTL_HANDLER_ARGS) { struct iwi_softc *sc = arg1; int softled = sc->sc_softled; int error; error = sysctl_handle_int(oidp, &softled, 0, req); if (error || !req->newptr) return error; softled = (softled != 0); if (softled != sc->sc_softled) { if (softled) { uint32_t v = iwi_read_event(sc); v &= ~sc->sc_ledpin; iwi_write_event(sc, iwi_toggle_event(v)); } sc->sc_softled = softled; } return 0; } static void iwi_ledattach(struct iwi_softc *sc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); sc->sc_blinking = 0; sc->sc_ledstate = 1; sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */ callout_init_mtx(&sc->sc_ledtimer, &sc->sc_mtx, 0); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0, iwi_sysctl_softled, "I", "enable/disable software LED support"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0, "pin setting to turn activity LED on"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0, "idle time for inactivity LED (ticks)"); /* XXX for debugging */ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "nictype", CTLFLAG_RD, &sc->sc_nictype, 0, "NIC type from EEPROM"); sc->sc_ledpin = IWI_RST_LED_ACTIVITY; sc->sc_softled = 1; sc->sc_nictype = (iwi_read_prom_word(sc, IWI_EEPROM_NIC) >> 8) & 0xff; if (sc->sc_nictype == 1) { /* * NB: led's are reversed. */ sc->sc_ledpin = IWI_RST_LED_ASSOCIATED; } } static void iwi_ops(void *arg0, int npending) { static const char *opnames[] = { [IWI_CMD_FREE] = "FREE", [IWI_SCAN_START] = "SCAN_START", [IWI_SET_CHANNEL] = "SET_CHANNEL", - [IWI_AUTH] = "AUTH", - [IWI_ASSOC] = "ASSOC", [IWI_DISASSOC] = "DISASSOC", [IWI_SCAN_CURCHAN] = "SCAN_CURCHAN", [IWI_SCAN_ALLCHAN] = "SCAN_ALLCHAN", [IWI_SET_WME] = "SET_WME", }; struct iwi_softc *sc = arg0; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); IWI_LOCK_DECL; int cmd; unsigned long arg; again: IWI_CMD_LOCK(sc); cmd = sc->sc_cmd[sc->sc_cmd_cur]; if (cmd == IWI_CMD_FREE) { /* No more commands to process */ IWI_CMD_UNLOCK(sc); return; } arg = sc->sc_arg[sc->sc_cmd_cur]; sc->sc_cmd[sc->sc_cmd_cur] = IWI_CMD_FREE; /* free the slot */ sc->sc_cmd_cur = (sc->sc_cmd_cur + 1) % IWI_CMD_MAXOPS; IWI_CMD_UNLOCK(sc); IWI_LOCK(sc); while (sc->fw_state != IWI_FW_IDLE || (sc->flags & IWI_FLAG_BUSY)) { msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz/10); } if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { IWI_UNLOCK(sc); return; } DPRINTF(("%s: %s arg %lu\n", __func__, opnames[cmd], arg)); switch (cmd) { - case IWI_AUTH: - case IWI_ASSOC: - if (cmd == IWI_AUTH) - vap->iv_state = IEEE80211_S_AUTH; - else - vap->iv_state = IEEE80211_S_ASSOC; - iwi_auth_and_assoc(sc, vap); - /* NB: completion done in iwi_notification_intr */ - break; case IWI_DISASSOC: iwi_disassociate(sc, 0); break; case IWI_SET_WME: if (vap->iv_state == IEEE80211_S_RUN) (void) iwi_wme_setparams(sc, ic); break; case IWI_SCAN_START: sc->flags |= IWI_FLAG_CHANNEL_SCAN; break; case IWI_SCAN_CURCHAN: case IWI_SCAN_ALLCHAN: if (!(sc->flags & IWI_FLAG_CHANNEL_SCAN)) { DPRINTF(("%s: ic_scan_curchan while not scanning\n", __func__)); goto done; } if (iwi_scanchan(sc, arg, cmd)) ieee80211_cancel_scan(vap); break; } done: IWI_UNLOCK(sc); /* Take another pass */ goto again; } static int iwi_queue_cmd(struct iwi_softc *sc, int cmd, unsigned long arg) { IWI_CMD_LOCK(sc); if (sc->sc_cmd[sc->sc_cmd_next] != 0) { IWI_CMD_UNLOCK(sc); DPRINTF(("%s: command %d dropped\n", __func__, cmd)); return (EBUSY); } sc->sc_cmd[sc->sc_cmd_next] = cmd; sc->sc_arg[sc->sc_cmd_next] = arg; sc->sc_cmd_next = (sc->sc_cmd_next + 1) % IWI_CMD_MAXOPS; taskqueue_enqueue(sc->sc_tq, &sc->sc_opstask); IWI_CMD_UNLOCK(sc); return (0); } static void iwi_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct iwi_softc *sc = ifp->if_softc; iwi_queue_cmd(sc, IWI_SCAN_START, 0); } static void iwi_set_channel(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct iwi_softc *sc = ifp->if_softc; if (sc->fw_state == IWI_FW_IDLE) iwi_setcurchan(sc, ic->ic_curchan->ic_ieee); } static void iwi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { struct ieee80211vap *vap = ss->ss_vap; struct ifnet *ifp = vap->iv_ic->ic_ifp; struct iwi_softc *sc = ifp->if_softc; iwi_queue_cmd(sc, IWI_SCAN_CURCHAN, maxdwell); } #if 0 static void iwi_scan_allchan(struct ieee80211com *ic, unsigned long maxdwell) { struct ifnet *ifp = ic->ic_ifp; struct iwi_softc *sc = ifp->if_softc; iwi_queue_cmd(sc, IWI_SCAN_ALLCHAN, maxdwell); } #endif static void iwi_scan_mindwell(struct ieee80211_scan_state *ss) { /* NB: don't try to abort scan; wait for firmware to finish */ } static void iwi_scan_end(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct iwi_softc *sc = ifp->if_softc; taskqueue_enqueue(sc->sc_tq2, &sc->sc_scanaborttask); } Index: user/thompsa/vaptq/sys/dev/iwi/if_iwivar.h =================================================================== --- user/thompsa/vaptq/sys/dev/iwi/if_iwivar.h (revision 185732) +++ user/thompsa/vaptq/sys/dev/iwi/if_iwivar.h (revision 185733) @@ -1,287 +1,285 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2004, 2005 * Damien Bergamini . 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 unmodified, 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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. */ struct iwi_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint8_t wr_flags; uint8_t wr_rate; uint16_t wr_chan_freq; uint16_t wr_chan_flags; uint8_t wr_antsignal; uint8_t wr_antenna; }; #define IWI_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) | \ (1 << IEEE80211_RADIOTAP_ANTENNA)) struct iwi_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; uint16_t wt_chan_freq; uint16_t wt_chan_flags; }; #define IWI_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_CHANNEL)) struct iwi_cmd_ring { bus_dma_tag_t desc_dmat; bus_dmamap_t desc_map; bus_addr_t physaddr; struct iwi_cmd_desc *desc; int count; int queued; int cur; int next; }; struct iwi_tx_data { bus_dmamap_t map; struct mbuf *m; struct ieee80211_node *ni; }; struct iwi_tx_ring { bus_dma_tag_t desc_dmat; bus_dma_tag_t data_dmat; bus_dmamap_t desc_map; bus_addr_t physaddr; bus_addr_t csr_ridx; bus_addr_t csr_widx; struct iwi_tx_desc *desc; struct iwi_tx_data *data; int count; int queued; int cur; int next; }; struct iwi_rx_data { bus_dmamap_t map; bus_addr_t physaddr; uint32_t reg; struct mbuf *m; }; struct iwi_rx_ring { bus_dma_tag_t data_dmat; struct iwi_rx_data *data; int count; int cur; }; struct iwi_node { struct ieee80211_node in_node; int in_station; #define IWI_MAX_IBSSNODE 32 }; struct iwi_fw { const struct firmware *fp; /* image handle */ const char *data; /* firmware image data */ size_t size; /* firmware image size */ const char *name; /* associated image name */ }; struct iwi_vap { struct ieee80211vap iwi_vap; struct task iwi_authsuccess_task; struct task iwi_assocsuccess_task; struct task iwi_assocfailed_task; int (*iwi_newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define IWI_VAP(vap) ((struct iwi_vap *)(vap)) struct iwi_softc { struct ifnet *sc_ifp; void (*sc_node_free)(struct ieee80211_node *); device_t sc_dev; struct mtx sc_mtx; struct mtx sc_cmdlock; char sc_cmdname[12]; /* e.g. "iwi0_cmd" */ uint8_t sc_mcast[IEEE80211_ADDR_LEN]; struct unrhdr *sc_unr; struct taskqueue *sc_tq; /* private task queue */ struct taskqueue *sc_tq2; /* reset task queue */ uint32_t flags; #define IWI_FLAG_FW_INITED (1 << 0) #define IWI_FLAG_BUSY (1 << 3) /* busy sending a command */ #define IWI_FLAG_ASSOCIATED (1 << 4) /* currently associated */ #define IWI_FLAG_CHANNEL_SCAN (1 << 5) uint32_t fw_state; #define IWI_FW_IDLE 0 #define IWI_FW_LOADING 1 #define IWI_FW_ASSOCIATING 2 #define IWI_FW_DISASSOCIATING 3 #define IWI_FW_SCANNING 4 struct iwi_cmd_ring cmdq; struct iwi_tx_ring txq[WME_NUM_AC]; struct iwi_rx_ring rxq; struct resource *irq; struct resource *mem; bus_space_tag_t sc_st; bus_space_handle_t sc_sh; void *sc_ih; int mem_rid; int irq_rid; /* * The card needs external firmware images to work, which is made of a * bootloader, microcode and firmware proper. In version 3.00 and * above, all pieces are contained in a single image, preceded by a * struct iwi_firmware_hdr indicating the size of the 3 pieces. * Old firmware < 3.0 has separate boot and ucode, so we need to * load all of them explicitly. * To avoid issues related to fragmentation, we keep the block of * dma-ble memory around until detach time, and reallocate it when * it becomes too small. fw_dma_size is the size currently allocated. */ int fw_dma_size; uint32_t fw_flags; /* allocation status */ #define IWI_FW_HAVE_DMAT 0x01 #define IWI_FW_HAVE_MAP 0x02 #define IWI_FW_HAVE_PHY 0x04 bus_dma_tag_t fw_dmat; bus_dmamap_t fw_map; bus_addr_t fw_physaddr; void *fw_virtaddr; enum ieee80211_opmode fw_mode; /* mode of current firmware */ struct iwi_fw fw_boot; /* boot firmware */ struct iwi_fw fw_uc; /* microcode */ struct iwi_fw fw_fw; /* operating mode support */ int curchan; /* current h/w channel # */ int antenna; int bluetooth; struct iwi_associate assoc; struct iwi_wme_params wme[3]; u_int sc_scangen; struct task sc_radiontask; /* radio on processing */ struct task sc_radiofftask; /* radio off processing */ struct task sc_scanaborttask; /* cancel active scan */ struct task sc_restarttask; /* restart adapter processing */ struct task sc_opstask; /* scan / auth processing */ unsigned int sc_softled : 1, /* enable LED gpio status */ sc_ledstate: 1, /* LED on/off state */ sc_blinking: 1; /* LED blink operation active */ u_int sc_nictype; /* NIC type from EEPROM */ u_int sc_ledpin; /* mask for activity LED */ u_int sc_ledidle; /* idle polling interval */ int sc_ledevent; /* time of last LED event */ u_int8_t sc_rxrate; /* current rx rate for LED */ u_int8_t sc_rxrix; u_int8_t sc_txrate; /* current tx rate for LED */ u_int8_t sc_txrix; u_int16_t sc_ledoff; /* off time for current blink */ struct callout sc_ledtimer; /* led off timer */ struct callout sc_wdtimer; /* watchdog timer */ struct callout sc_rftimer; /* rfkill timer */ int sc_tx_timer; int sc_state_timer; /* firmware state timer */ int sc_busy_timer; /* firmware cmd timer */ #define IWI_CMD_MAXOPS 10 int sc_cmd[IWI_CMD_MAXOPS]; unsigned long sc_arg[IWI_CMD_MAXOPS]; int sc_cmd_cur; /* current queued scan task */ int sc_cmd_next; /* last queued scan task */ #define IWI_CMD_FREE 0 /* for marking slots unused */ #define IWI_SCAN_START 1 #define IWI_SET_CHANNEL 2 -#define IWI_AUTH 3 -#define IWI_ASSOC 4 -#define IWI_DISASSOC 5 -#define IWI_SCAN_CURCHAN 6 -#define IWI_SCAN_ALLCHAN 7 -#define IWI_SET_WME 8 +#define IWI_DISASSOC 3 +#define IWI_SCAN_CURCHAN 4 +#define IWI_SCAN_ALLCHAN 5 +#define IWI_SET_WME 6 struct iwi_rx_radiotap_header sc_rxtap; int sc_rxtap_len; struct iwi_tx_radiotap_header sc_txtap; int sc_txtap_len; }; #define IWI_STATE_BEGIN(_sc, _state) do { \ KASSERT(_sc->fw_state == IWI_FW_IDLE, \ ("iwi firmware not idle")); \ _sc->fw_state = _state; \ _sc->sc_state_timer = 5; \ DPRINTF(("enter %s state\n", iwi_fw_states[_state])); \ } while (0) #define IWI_STATE_END(_sc, _state) do { \ if (_sc->fw_state == _state) \ DPRINTF(("exit %s state\n", iwi_fw_states[_state])); \ else \ DPRINTF(("expected %s state, got %s\n", \ iwi_fw_states[_state], iwi_fw_states[_sc->fw_state])); \ _sc->fw_state = IWI_FW_IDLE; \ wakeup(_sc); \ _sc->sc_state_timer = 0; \ } while (0) /* * NB.: This models the only instance of async locking in iwi_init_locked * and must be kept in sync. */ #define IWI_LOCK_INIT(sc) \ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->sc_dev), \ MTX_NETWORK_LOCK, MTX_DEF) #define IWI_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) #define IWI_LOCK_DECL int __waslocked = 0 #define IWI_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) #define IWI_LOCK(sc) do { \ if (!(__waslocked = mtx_owned(&(sc)->sc_mtx))) \ mtx_lock(&(sc)->sc_mtx); \ } while (0) #define IWI_UNLOCK(sc) do { \ if (!__waslocked) \ mtx_unlock(&(sc)->sc_mtx); \ } while (0) #define IWI_CMD_LOCK_INIT(sc) do { \ snprintf((sc)->sc_cmdname, sizeof((sc)->sc_cmdname), "%s_cmd", \ device_get_nameunit((sc)->sc_dev)); \ mtx_init(&(sc)->sc_cmdlock, (sc)->sc_cmdname, NULL, MTX_DEF); \ } while (0) #define IWI_CMD_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_cmdlock) #define IWI_CMD_LOCK(sc) mtx_lock(&(sc)->sc_cmdlock) #define IWI_CMD_UNLOCK(sc) mtx_unlock(&(sc)->sc_cmdlock) Index: user/thompsa/vaptq/sys/dev/iwn/if_iwn.c =================================================================== --- user/thompsa/vaptq/sys/dev/iwn/if_iwn.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/iwn/if_iwn.c (revision 185733) @@ -1,4631 +1,4596 @@ /*- * Copyright (c) 2007 * Damien Bergamini * Copyright (c) 2008 * Benjamin Close * Copyright (c) 2008 Sam Leffler, Errno Consulting * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Driver for Intel Wireless WiFi Link 4965AGN 802.11 network adapters. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int iwn_probe(device_t); static int iwn_attach(device_t); static int iwn_detach(device_t); static int iwn_cleanup(device_t); static struct ieee80211vap *iwn_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void iwn_vap_delete(struct ieee80211vap *); static int iwn_shutdown(device_t); static int iwn_suspend(device_t); static int iwn_resume(device_t); static int iwn_dma_contig_alloc(struct iwn_softc *, struct iwn_dma_info *, void **, bus_size_t, bus_size_t, int); static void iwn_dma_contig_free(struct iwn_dma_info *); int iwn_alloc_shared(struct iwn_softc *); void iwn_free_shared(struct iwn_softc *); int iwn_alloc_kw(struct iwn_softc *); void iwn_free_kw(struct iwn_softc *); int iwn_alloc_fwmem(struct iwn_softc *); void iwn_free_fwmem(struct iwn_softc *); struct iwn_rbuf *iwn_alloc_rbuf(struct iwn_softc *); void iwn_free_rbuf(void *, void *); int iwn_alloc_rpool(struct iwn_softc *); void iwn_free_rpool(struct iwn_softc *); int iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); void iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); void iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); int iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *, int); void iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); void iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); static struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); void iwn_newassoc(struct ieee80211_node *, int); int iwn_media_change(struct ifnet *); int iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); void iwn_mem_lock(struct iwn_softc *); void iwn_mem_unlock(struct iwn_softc *); uint32_t iwn_mem_read(struct iwn_softc *, uint32_t); void iwn_mem_write(struct iwn_softc *, uint32_t, uint32_t); void iwn_mem_write_region_4(struct iwn_softc *, uint32_t, const uint32_t *, int); int iwn_eeprom_lock(struct iwn_softc *); void iwn_eeprom_unlock(struct iwn_softc *); int iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int); int iwn_transfer_microcode(struct iwn_softc *, const uint8_t *, int); int iwn_transfer_firmware(struct iwn_softc *); int iwn_load_firmware(struct iwn_softc *); void iwn_unload_firmware(struct iwn_softc *); static void iwn_timer_timeout(void *); static void iwn_calib_reset(struct iwn_softc *); void iwn_ampdu_rx_start(struct iwn_softc *, struct iwn_rx_desc *); void iwn_rx_intr(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *); void iwn_tx_intr(struct iwn_softc *, struct iwn_rx_desc *); void iwn_cmd_intr(struct iwn_softc *, struct iwn_rx_desc *); static void iwn_bmiss(void *, int); void iwn_notif_intr(struct iwn_softc *); void iwn_intr(void *); void iwn_read_eeprom(struct iwn_softc *); static void iwn_read_eeprom_channels(struct iwn_softc *); void iwn_print_power_group(struct iwn_softc *, int); uint8_t iwn_plcp_signal(int); int iwn_tx_data(struct iwn_softc *, struct mbuf *, struct ieee80211_node *, struct iwn_tx_ring *); void iwn_start(struct ifnet *); void iwn_start_locked(struct ifnet *); static int iwn_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void iwn_watchdog(struct iwn_softc *); int iwn_ioctl(struct ifnet *, u_long, caddr_t); int iwn_cmd(struct iwn_softc *, int, const void *, int, int); int iwn_set_link_quality(struct iwn_softc *, uint8_t, const struct ieee80211_channel *, int); int iwn_set_key(struct ieee80211com *, struct ieee80211_node *, const struct ieee80211_key *); int iwn_wme_update(struct ieee80211com *); void iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t); int iwn_set_critical_temp(struct iwn_softc *); void iwn_enable_tsf(struct iwn_softc *, struct ieee80211_node *); void iwn_power_calibration(struct iwn_softc *, int); int iwn_set_txpower(struct iwn_softc *, struct ieee80211_channel *, int); int8_t iwn_get_rssi(struct iwn_softc *, const struct iwn_rx_stat *); int iwn_get_noise(const struct iwn_rx_general_stats *); int iwn_get_temperature(struct iwn_softc *); int iwn_init_sensitivity(struct iwn_softc *); void iwn_compute_differential_gain(struct iwn_softc *, const struct iwn_rx_general_stats *); void iwn_tune_sensitivity(struct iwn_softc *, const struct iwn_rx_stats *); int iwn_send_sensitivity(struct iwn_softc *); -int iwn_auth(struct iwn_softc *); -int iwn_run(struct iwn_softc *); +int iwn_auth(struct iwn_softc *, struct ieee80211vap *); +int iwn_run(struct iwn_softc *, struct ieee80211vap *); int iwn_scan(struct iwn_softc *); int iwn_config(struct iwn_softc *); void iwn_post_alive(struct iwn_softc *); void iwn_stop_master(struct iwn_softc *); int iwn_reset(struct iwn_softc *); void iwn_hw_config(struct iwn_softc *); void iwn_init_locked(struct iwn_softc *); void iwn_init(void *); void iwn_stop_locked(struct iwn_softc *); void iwn_stop(struct iwn_softc *); static void iwn_scan_start(struct ieee80211com *); static void iwn_scan_end(struct ieee80211com *); static void iwn_set_channel(struct ieee80211com *); static void iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long); static void iwn_scan_mindwell(struct ieee80211_scan_state *); static void iwn_ops(void *, int); static int iwn_queue_cmd( struct iwn_softc *, int, int, int); static void iwn_bpfattach(struct iwn_softc *); static void iwn_sysctlattach(struct iwn_softc *); #define IWN_DEBUG #ifdef IWN_DEBUG enum { IWN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ IWN_DEBUG_RECV = 0x00000002, /* basic recv operation */ IWN_DEBUG_STATE = 0x00000004, /* 802.11 state transitions */ IWN_DEBUG_TXPOW = 0x00000008, /* tx power processing */ IWN_DEBUG_RESET = 0x00000010, /* reset processing */ IWN_DEBUG_OPS = 0x00000020, /* iwn_ops processing */ IWN_DEBUG_BEACON = 0x00000040, /* beacon handling */ IWN_DEBUG_WATCHDOG = 0x00000080, /* watchdog timeout */ IWN_DEBUG_INTR = 0x00000100, /* ISR */ IWN_DEBUG_CALIBRATE = 0x00000200, /* periodic calibration */ IWN_DEBUG_NODE = 0x00000400, /* node management */ IWN_DEBUG_LED = 0x00000800, /* led management */ IWN_DEBUG_CMD = 0x00001000, /* cmd submission */ IWN_DEBUG_FATAL = 0x80000000, /* fatal errors */ IWN_DEBUG_ANY = 0xffffffff }; #define DPRINTF(sc, m, fmt, ...) do { \ if (sc->sc_debug & (m)) \ printf(fmt, __VA_ARGS__); \ } while (0) static const char *iwn_ops_str(int); static const char *iwn_intr_str(uint8_t); #else #define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0) #endif struct iwn_ident { uint16_t vendor; uint16_t device; const char *name; }; static const struct iwn_ident iwn_ident_table [] = { { 0x8086, 0x4229, "Intel(R) PRO/Wireless 4965BGN" }, { 0x8086, 0x422D, "Intel(R) PRO/Wireless 4965BGN" }, { 0x8086, 0x4230, "Intel(R) PRO/Wireless 4965BGN" }, { 0x8086, 0x4233, "Intel(R) PRO/Wireless 4965BGN" }, { 0, 0, NULL } }; static int iwn_probe(device_t dev) { const struct iwn_ident *ident; for (ident = iwn_ident_table; ident->name != NULL; ident++) { if (pci_get_vendor(dev) == ident->vendor && pci_get_device(dev) == ident->device) { device_set_desc(dev, ident->name); return 0; } } return ENXIO; } static int iwn_attach(device_t dev) { struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; int i, error, result; sc->sc_dev = dev; /* XXX */ if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { device_printf(dev, "chip is in D%d power mode " "-- setting to D0\n", pci_get_powerstate(dev)); pci_set_powerstate(dev, PCI_POWERSTATE_D0); } /* clear device specific PCI configuration register 0x41 */ pci_write_config(dev, 0x41, 0, 1); /* enable bus-mastering */ pci_enable_busmaster(dev); sc->mem_rid= PCIR_BAR(0); sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem == NULL ) { device_printf(dev, "could not allocate memory resources\n"); error = ENOMEM; return error; } sc->sc_st = rman_get_bustag(sc->mem); sc->sc_sh = rman_get_bushandle(sc->mem); sc->irq_rid = 0; if ((result = pci_msi_count(dev)) == 1 && pci_alloc_msi(dev, &result) == 0) sc->irq_rid = 1; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->irq == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); error = ENOMEM; return error; } IWN_LOCK_INIT(sc); IWN_CMD_LOCK_INIT(sc); callout_init_mtx(&sc->sc_timer_to, &sc->sc_mtx, 0); /* * Create the taskqueues used by the driver. Primarily * sc_tq handles most the task */ sc->sc_tq = taskqueue_create("iwn_taskq", M_NOWAIT | M_ZERO, taskqueue_thread_enqueue, &sc->sc_tq); taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(dev)); TASK_INIT(&sc->sc_ops_task, 0, iwn_ops, sc ); TASK_INIT(&sc->sc_bmiss_task, 0, iwn_bmiss, sc); /* * Put adapter into a known state. */ error = iwn_reset(sc); if (error != 0) { device_printf(dev, "could not reset adapter, error %d\n", error); goto fail; } /* * Allocate DMA memory for firmware transfers. */ error = iwn_alloc_fwmem(sc); if (error != 0) { device_printf(dev, "could not allocate firmware memory, error %d\n", error); goto fail; } /* * Allocate a "keep warm" page. */ error = iwn_alloc_kw(sc); if (error != 0) { device_printf(dev, "could not allocate keep-warm page, error %d\n", error); goto fail; } /* * Allocate shared area (communication area). */ error = iwn_alloc_shared(sc); if (error != 0) { device_printf(dev, "could not allocate shared area, error %d\n", error); goto fail; } /* * Allocate Tx rings. */ for (i = 0; i < IWN_NTXQUEUES; i++) { error = iwn_alloc_tx_ring(sc, &sc->txq[i], i); if (error != 0) { device_printf(dev, "could not allocate Tx ring %d, error %d\n", i, error); goto fail; } } error = iwn_alloc_rx_ring(sc, &sc->rxq); if (error != 0 ){ device_printf(dev, "could not allocate Rx ring, error %d\n", error); goto fail; } ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(dev, "can not allocate ifnet structure\n"); goto fail; } ic = ifp->if_l2com; ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_WPA | IEEE80211_C_SHPREAMBLE /* short preamble supported */ #if 0 | IEEE80211_C_BGSCAN /* background scanning */ | IEEE80211_C_IBSS /* ibss/adhoc mode */ #endif | IEEE80211_C_WME /* WME */ ; #if 0 /* XXX disable until HT channel setup works */ ic->ic_htcaps = IEEE80211_HTCAP_SMPS_ENA /* SM PS mode enabled */ | IEEE80211_HTCAP_CHWIDTH40 /* 40MHz channel width */ | IEEE80211_HTCAP_SHORTGI20 /* short GI in 20MHz */ | IEEE80211_HTCAP_SHORTGI40 /* short GI in 40MHz */ | IEEE80211_HTCAP_RXSTBC_2STREAM/* 1-2 spatial streams */ | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */ /* s/w capabilities */ | IEEE80211_HTC_HT /* HT operation */ | IEEE80211_HTC_AMPDU /* tx A-MPDU */ | IEEE80211_HTC_AMSDU /* tx A-MSDU */ ; #endif /* read supported channels and MAC address from EEPROM */ iwn_read_eeprom(sc); if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = iwn_init; ifp->if_ioctl = iwn_ioctl; ifp->if_start = iwn_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ieee80211_ifattach(ic); ic->ic_vap_create = iwn_vap_create; ic->ic_vap_delete = iwn_vap_delete; ic->ic_raw_xmit = iwn_raw_xmit; ic->ic_node_alloc = iwn_node_alloc; ic->ic_newassoc = iwn_newassoc; ic->ic_wme.wme_update = iwn_wme_update; ic->ic_scan_start = iwn_scan_start; ic->ic_scan_end = iwn_scan_end; ic->ic_set_channel = iwn_set_channel; ic->ic_scan_curchan = iwn_scan_curchan; ic->ic_scan_mindwell = iwn_scan_mindwell; iwn_bpfattach(sc); iwn_sysctlattach(sc); /* * Hook our interrupt after all initialization is complete. */ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, iwn_intr, sc, &sc->sc_ih); if (error != 0) { device_printf(dev, "could not set up interrupt, error %d\n", error); goto fail; } ieee80211_announce(ic); return 0; fail: iwn_cleanup(dev); return error; } static int iwn_detach(device_t dev) { iwn_cleanup(dev); return 0; } /* * Cleanup any device resources that were allocated */ int iwn_cleanup(device_t dev) { struct iwn_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int i; if (ifp != NULL) { iwn_stop(sc); callout_drain(&sc->sc_timer_to); bpfdetach(ifp); ieee80211_ifdetach(ic); } iwn_unload_firmware(sc); iwn_free_rx_ring(sc, &sc->rxq); for (i = 0; i < IWN_NTXQUEUES; i++) iwn_free_tx_ring(sc, &sc->txq[i]); iwn_free_kw(sc); iwn_free_fwmem(sc); if (sc->irq != NULL) { bus_teardown_intr(dev, sc->irq, sc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); if (sc->irq_rid == 1) pci_release_msi(dev); } if (sc->mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); if (ifp != NULL) if_free(ifp); taskqueue_free(sc->sc_tq); IWN_CMD_LOCK_DESTROY(sc); IWN_LOCK_DESTROY(sc); return 0; } static struct ieee80211vap * iwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct iwn_vap *ivp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; ivp = (struct iwn_vap *) malloc(sizeof(struct iwn_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (ivp == NULL) return NULL; vap = &ivp->iv_vap; ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); vap->iv_bmissthreshold = 10; /* override default */ /* override with driver methods */ ivp->iv_newstate = vap->iv_newstate; vap->iv_newstate = iwn_newstate; ieee80211_amrr_init(&ivp->iv_amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 500 /*ms*/); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return vap; } static void iwn_vap_delete(struct ieee80211vap *vap) { struct iwn_vap *ivp = IWN_VAP(vap); ieee80211_amrr_cleanup(&ivp->iv_amrr); ieee80211_vap_detach(vap); free(ivp, M_80211_VAP); } static int iwn_shutdown(device_t dev) { struct iwn_softc *sc = device_get_softc(dev); iwn_stop(sc); return 0; } static int iwn_suspend(device_t dev) { struct iwn_softc *sc = device_get_softc(dev); iwn_stop(sc); return 0; } static int iwn_resume(device_t dev) { struct iwn_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; pci_write_config(dev, 0x41, 0, 1); if (ifp->if_flags & IFF_UP) iwn_init(sc); return 0; } static void iwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); *(bus_addr_t *)arg = segs[0].ds_addr; } static int iwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma, void **kvap, bus_size_t size, bus_size_t alignment, int flags) { int error, lalignment, i; /* * FreeBSD can't guarrenty 16k alignment at the moment (11/2007) so * we allocate an extra 12k with 4k alignement and walk through * it trying to find where the alignment is. It's a nasty fix for * a bigger problem. */ DPRINTF(sc, IWN_DEBUG_RESET, "Size: %zd - alignment %zd\n", size, alignment); if (alignment == 0x4000) { size += 12*1024; lalignment = 4096; DPRINTF(sc, IWN_DEBUG_RESET, "%s\n", "Attempting to find a 16k boundary"); } else lalignment = alignment; dma->size = size; dma->tag = NULL; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), lalignment, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, flags, NULL, NULL, &dma->tag); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dma_tag_create failed, error %d\n", __func__, error); goto fail; } error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, flags | BUS_DMA_ZERO, &dma->map); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dmamem_alloc failed, error %d\n", __func__, error); goto fail; } if (alignment == 0x4000) { for (i = 0; i < 3 && (((uintptr_t)dma->vaddr) & 0x3fff); i++) { DPRINTF(sc, IWN_DEBUG_RESET, "%s\n", "Memory Unaligned, shifting pointer by 4k"); dma->vaddr += 4096; size -= 4096; } if ((((uintptr_t)dma->vaddr ) & (alignment-1))) { DPRINTF(sc, IWN_DEBUG_ANY, "%s: failed to align memory, vaddr %p, align %zd\n", __func__, dma->vaddr, alignment); error = ENOMEM; goto fail; } } error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, iwn_dma_map_addr, &dma->paddr, flags); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dmamap_load failed, error %d\n", __func__, error); goto fail; } if (kvap != NULL) *kvap = dma->vaddr; return 0; fail: iwn_dma_contig_free(dma); return error; } static void iwn_dma_contig_free(struct iwn_dma_info *dma) { if (dma->tag != NULL) { if (dma->map != NULL) { if (dma->paddr == 0) { bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(dma->tag, dma->map); } bus_dmamem_free(dma->tag, &dma->vaddr, dma->map); } bus_dma_tag_destroy(dma->tag); } } int iwn_alloc_shared(struct iwn_softc *sc) { /* must be aligned on a 1KB boundary */ return iwn_dma_contig_alloc(sc, &sc->shared_dma, (void **)&sc->shared, sizeof (struct iwn_shared), 1024, BUS_DMA_NOWAIT); } void iwn_free_shared(struct iwn_softc *sc) { iwn_dma_contig_free(&sc->shared_dma); } int iwn_alloc_kw(struct iwn_softc *sc) { /* must be aligned on a 4k boundary */ return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, PAGE_SIZE, PAGE_SIZE, BUS_DMA_NOWAIT); } void iwn_free_kw(struct iwn_softc *sc) { iwn_dma_contig_free(&sc->kw_dma); } int iwn_alloc_fwmem(struct iwn_softc *sc) { /* allocate enough contiguous space to store text and data */ return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, IWN_FW_MAIN_TEXT_MAXSZ + IWN_FW_MAIN_DATA_MAXSZ, 16, BUS_DMA_NOWAIT); } void iwn_free_fwmem(struct iwn_softc *sc) { iwn_dma_contig_free(&sc->fw_dma); } int iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) { int i, error; ring->cur = 0; error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, IWN_RX_RING_COUNT * sizeof (uint32_t), IWN_RING_DMA_ALIGN, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate rx ring DMA memory, error %d\n", __func__, error); goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dma_tag_create_failed, error %d\n", __func__, error); goto fail; } /* * Setup Rx buffers. */ for (i = 0; i < IWN_RX_RING_COUNT; i++) { struct iwn_rx_data *data = &ring->data[i]; struct mbuf *m; bus_addr_t paddr; error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dmamap_create failed, error %d\n", __func__, error); goto fail; } m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (m == NULL) { device_printf(sc->sc_dev, "%s: could not allocate rx mbuf\n", __func__); error = ENOMEM; goto fail; } /* map page */ error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m, caddr_t), MJUMPAGESIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "%s: bus_dmamap_load failed, error %d\n", __func__, error); m_freem(m); error = ENOMEM; /* XXX unique code */ goto fail; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); data->m = m; /* Rx buffers are aligned on a 256-byte boundary */ ring->desc[i] = htole32(paddr >> 8); } bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); return 0; fail: iwn_free_rx_ring(sc, ring); return error; } void iwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) { int ntries; iwn_mem_lock(sc); IWN_WRITE(sc, IWN_RX_CONFIG, 0); for (ntries = 0; ntries < 100; ntries++) { if (IWN_READ(sc, IWN_RX_STATUS) & IWN_RX_IDLE) break; DELAY(10); } #ifdef IWN_DEBUG if (ntries == 100) DPRINTF(sc, IWN_DEBUG_ANY, "%s\n", "timeout resetting Rx ring"); #endif iwn_mem_unlock(sc); ring->cur = 0; } void iwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) { int i; iwn_dma_contig_free(&ring->desc_dma); for (i = 0; i < IWN_RX_RING_COUNT; i++) if (ring->data[i].m != NULL) m_freem(ring->data[i].m); } int iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) { bus_size_t size; int i, error; ring->qid = qid; ring->queued = 0; ring->cur = 0; size = IWN_TX_RING_COUNT * sizeof(struct iwn_tx_desc); error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, size, IWN_RING_DMA_ALIGN, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate tx ring DMA memory, error %d\n", __func__, error); goto fail; } size = IWN_TX_RING_COUNT * sizeof(struct iwn_tx_cmd); error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, size, 4, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate tx cmd DMA memory, error %d\n", __func__, error); goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, IWN_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dma_tag_create_failed, error %d\n", __func__, error); goto fail; } for (i = 0; i < IWN_TX_RING_COUNT; i++) { struct iwn_tx_data *data = &ring->data[i]; error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dmamap_create failed, error %d\n", __func__, error); goto fail; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); } return 0; fail: iwn_free_tx_ring(sc, ring); return error; } void iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) { uint32_t tmp; int i, ntries; iwn_mem_lock(sc); IWN_WRITE(sc, IWN_TX_CONFIG(ring->qid), 0); for (ntries = 0; ntries < 20; ntries++) { tmp = IWN_READ(sc, IWN_TX_STATUS); if ((tmp & IWN_TX_IDLE(ring->qid)) == IWN_TX_IDLE(ring->qid)) break; DELAY(10); } #ifdef IWN_DEBUG if (ntries == 20) DPRINTF(sc, IWN_DEBUG_RESET, "%s: timeout resetting Tx ring %d\n", __func__, ring->qid); #endif iwn_mem_unlock(sc); for (i = 0; i < IWN_TX_RING_COUNT; i++) { struct iwn_tx_data *data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } } ring->queued = 0; ring->cur = 0; } void iwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) { int i; iwn_dma_contig_free(&ring->desc_dma); iwn_dma_contig_free(&ring->cmd_dma); if (ring->data != NULL) { for (i = 0; i < IWN_TX_RING_COUNT; i++) { struct iwn_tx_data *data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); } } } } struct ieee80211_node * iwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { return malloc(sizeof (struct iwn_node), M_80211_NODE,M_NOWAIT | M_ZERO); } void iwn_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&IWN_VAP(vap)->iv_amrr, &IWN_NODE(ni)->amn, ni); } int iwn_media_change(struct ifnet *ifp) { int error = ieee80211_media_change(ifp); /* NB: only the fixed rate can change and that doesn't need a reset */ return (error == ENETRESET ? 0 : error); } int iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct iwn_vap *ivp = IWN_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct iwn_softc *sc = ic->ic_ifp->if_softc; int error; DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); + IEEE80211_UNLOCK(ic); IWN_LOCK(sc); callout_stop(&sc->sc_timer_to); - IWN_UNLOCK(sc); - /* - * Some state transitions require issuing a configure request - * to the adapter. This must be done in a blocking context - * so we toss control to the task q thread where the state - * change will be finished after the command completes. - */ if (nstate == IEEE80211_S_AUTH && vap->iv_state != IEEE80211_S_AUTH) { /* !AUTH -> AUTH requires adapter config */ - error = iwn_queue_cmd(sc, IWN_AUTH, arg, IWN_QUEUE_NORMAL); - return (error != 0 ? error : EINPROGRESS); + error = iwn_auth(sc, vap); } if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) { /* * !RUN -> RUN requires setting the association id * which is done with a firmware cmd. We also defer * starting the timers until that work is done. */ - error = iwn_queue_cmd(sc, IWN_RUN, arg, IWN_QUEUE_NORMAL); - return (error != 0 ? error : EINPROGRESS); + error = iwn_run(sc, vap); } if (nstate == IEEE80211_S_RUN) { /* * RUN -> RUN transition; just restart the timers. */ iwn_calib_reset(sc); } + IWN_UNLOCK(sc); + IEEE80211_LOCK(ic); return ivp->iv_newstate(vap, nstate, arg); } /* * Grab exclusive access to NIC memory. */ void iwn_mem_lock(struct iwn_softc *sc) { uint32_t tmp; int ntries; tmp = IWN_READ(sc, IWN_GPIO_CTL); IWN_WRITE(sc, IWN_GPIO_CTL, tmp | IWN_GPIO_MAC); /* spin until we actually get the lock */ for (ntries = 0; ntries < 1000; ntries++) { if ((IWN_READ(sc, IWN_GPIO_CTL) & (IWN_GPIO_CLOCK | IWN_GPIO_SLEEP)) == IWN_GPIO_CLOCK) break; DELAY(10); } if (ntries == 1000) device_printf(sc->sc_dev, "%s: could not lock memory\n", __func__); } /* * Release lock on NIC memory. */ void iwn_mem_unlock(struct iwn_softc *sc) { uint32_t tmp = IWN_READ(sc, IWN_GPIO_CTL); IWN_WRITE(sc, IWN_GPIO_CTL, tmp & ~IWN_GPIO_MAC); } uint32_t iwn_mem_read(struct iwn_softc *sc, uint32_t addr) { IWN_WRITE(sc, IWN_READ_MEM_ADDR, IWN_MEM_4 | addr); return IWN_READ(sc, IWN_READ_MEM_DATA); } void iwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) { IWN_WRITE(sc, IWN_WRITE_MEM_ADDR, IWN_MEM_4 | addr); IWN_WRITE(sc, IWN_WRITE_MEM_DATA, data); } void iwn_mem_write_region_4(struct iwn_softc *sc, uint32_t addr, const uint32_t *data, int wlen) { for (; wlen > 0; wlen--, data++, addr += 4) iwn_mem_write(sc, addr, *data); } int iwn_eeprom_lock(struct iwn_softc *sc) { uint32_t tmp; int ntries; tmp = IWN_READ(sc, IWN_HWCONFIG); IWN_WRITE(sc, IWN_HWCONFIG, tmp | IWN_HW_EEPROM_LOCKED); /* spin until we actually get the lock */ for (ntries = 0; ntries < 100; ntries++) { if (IWN_READ(sc, IWN_HWCONFIG) & IWN_HW_EEPROM_LOCKED) return 0; DELAY(10); } return ETIMEDOUT; } void iwn_eeprom_unlock(struct iwn_softc *sc) { uint32_t tmp = IWN_READ(sc, IWN_HWCONFIG); IWN_WRITE(sc, IWN_HWCONFIG, tmp & ~IWN_HW_EEPROM_LOCKED); } /* * Read `len' bytes from the EEPROM. We access the EEPROM through the MAC * instead of using the traditional bit-bang method. */ int iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int len) { uint8_t *out = data; uint32_t val; int ntries, tmp; iwn_mem_lock(sc); for (; len > 0; len -= 2, addr++) { IWN_WRITE(sc, IWN_EEPROM_CTL, addr << 2); tmp = IWN_READ(sc, IWN_EEPROM_CTL); IWN_WRITE(sc, IWN_EEPROM_CTL, tmp & ~IWN_EEPROM_MSK ); for (ntries = 0; ntries < 10; ntries++) { if ((val = IWN_READ(sc, IWN_EEPROM_CTL)) & IWN_EEPROM_READY) break; DELAY(5); } if (ntries == 10) { device_printf(sc->sc_dev,"could not read EEPROM\n"); return ETIMEDOUT; } *out++ = val >> 16; if (len > 1) *out++ = val >> 24; } iwn_mem_unlock(sc); return 0; } /* * The firmware boot code is small and is intended to be copied directly into * the NIC internal memory. */ int iwn_transfer_microcode(struct iwn_softc *sc, const uint8_t *ucode, int size) { int ntries; size /= sizeof (uint32_t); iwn_mem_lock(sc); /* copy microcode image into NIC memory */ iwn_mem_write_region_4(sc, IWN_MEM_UCODE_BASE, (const uint32_t *)ucode, size); iwn_mem_write(sc, IWN_MEM_UCODE_SRC, 0); iwn_mem_write(sc, IWN_MEM_UCODE_DST, IWN_FW_TEXT); iwn_mem_write(sc, IWN_MEM_UCODE_SIZE, size); /* run microcode */ iwn_mem_write(sc, IWN_MEM_UCODE_CTL, IWN_UC_RUN); /* wait for transfer to complete */ for (ntries = 0; ntries < 1000; ntries++) { if (!(iwn_mem_read(sc, IWN_MEM_UCODE_CTL) & IWN_UC_RUN)) break; DELAY(10); } if (ntries == 1000) { iwn_mem_unlock(sc); device_printf(sc->sc_dev, "%s: could not load boot firmware\n", __func__); return ETIMEDOUT; } iwn_mem_write(sc, IWN_MEM_UCODE_CTL, IWN_UC_ENABLE); iwn_mem_unlock(sc); return 0; } int iwn_load_firmware(struct iwn_softc *sc) { int error; KASSERT(sc->fw_fp == NULL, ("firmware already loaded")); IWN_UNLOCK(sc); /* load firmware image from disk */ sc->fw_fp = firmware_get("iwnfw"); if (sc->fw_fp == NULL) { device_printf(sc->sc_dev, "%s: could not load firmare image \"iwnfw\"\n", __func__); error = EINVAL; } else error = 0; IWN_LOCK(sc); return error; } int iwn_transfer_firmware(struct iwn_softc *sc) { struct iwn_dma_info *dma = &sc->fw_dma; const struct iwn_firmware_hdr *hdr; const uint8_t *init_text, *init_data, *main_text, *main_data; const uint8_t *boot_text; uint32_t init_textsz, init_datasz, main_textsz, main_datasz; uint32_t boot_textsz; int error = 0; const struct firmware *fp = sc->fw_fp; /* extract firmware header information */ if (fp->datasize < sizeof (struct iwn_firmware_hdr)) { device_printf(sc->sc_dev, "%s: truncated firmware header: %zu bytes, expecting %zu\n", __func__, fp->datasize, sizeof (struct iwn_firmware_hdr)); error = EINVAL; goto fail; } hdr = (const struct iwn_firmware_hdr *)fp->data; main_textsz = le32toh(hdr->main_textsz); main_datasz = le32toh(hdr->main_datasz); init_textsz = le32toh(hdr->init_textsz); init_datasz = le32toh(hdr->init_datasz); boot_textsz = le32toh(hdr->boot_textsz); /* sanity-check firmware segments sizes */ if (main_textsz > IWN_FW_MAIN_TEXT_MAXSZ || main_datasz > IWN_FW_MAIN_DATA_MAXSZ || init_textsz > IWN_FW_INIT_TEXT_MAXSZ || init_datasz > IWN_FW_INIT_DATA_MAXSZ || boot_textsz > IWN_FW_BOOT_TEXT_MAXSZ || (boot_textsz & 3) != 0) { device_printf(sc->sc_dev, "%s: invalid firmware header, main [%d,%d], init [%d,%d] " "boot %d\n", __func__, main_textsz, main_datasz, init_textsz, init_datasz, boot_textsz); error = EINVAL; goto fail; } /* check that all firmware segments are present */ if (fp->datasize < sizeof (struct iwn_firmware_hdr) + main_textsz + main_datasz + init_textsz + init_datasz + boot_textsz) { device_printf(sc->sc_dev, "%s: firmware file too short: " "%zu bytes, main [%d, %d], init [%d,%d] boot %d\n", __func__, fp->datasize, main_textsz, main_datasz, init_textsz, init_datasz, boot_textsz); error = EINVAL; goto fail; } /* get pointers to firmware segments */ main_text = (const uint8_t *)(hdr + 1); main_data = main_text + main_textsz; init_text = main_data + main_datasz; init_data = init_text + init_textsz; boot_text = init_data + init_datasz; /* copy initialization images into pre-allocated DMA-safe memory */ memcpy(dma->vaddr, init_data, init_datasz); memcpy(dma->vaddr + IWN_FW_INIT_DATA_MAXSZ, init_text, init_textsz); /* tell adapter where to find initialization images */ iwn_mem_lock(sc); iwn_mem_write(sc, IWN_MEM_DATA_BASE, dma->paddr >> 4); iwn_mem_write(sc, IWN_MEM_DATA_SIZE, init_datasz); iwn_mem_write(sc, IWN_MEM_TEXT_BASE, (dma->paddr + IWN_FW_INIT_DATA_MAXSZ) >> 4); iwn_mem_write(sc, IWN_MEM_TEXT_SIZE, init_textsz); iwn_mem_unlock(sc); /* load firmware boot code */ error = iwn_transfer_microcode(sc, boot_text, boot_textsz); if (error != 0) { device_printf(sc->sc_dev, "%s: could not load boot firmware, error %d\n", __func__, error); goto fail; } /* now press "execute" ;-) */ IWN_WRITE(sc, IWN_RESET, 0); /* wait at most one second for first alive notification */ error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz); if (error != 0) { /* this isn't what was supposed to happen.. */ device_printf(sc->sc_dev, "%s: timeout waiting for first alive notice, error %d\n", __func__, error); goto fail; } /* copy runtime images into pre-allocated DMA-safe memory */ memcpy(dma->vaddr, main_data, main_datasz); memcpy(dma->vaddr + IWN_FW_MAIN_DATA_MAXSZ, main_text, main_textsz); /* tell adapter where to find runtime images */ iwn_mem_lock(sc); iwn_mem_write(sc, IWN_MEM_DATA_BASE, dma->paddr >> 4); iwn_mem_write(sc, IWN_MEM_DATA_SIZE, main_datasz); iwn_mem_write(sc, IWN_MEM_TEXT_BASE, (dma->paddr + IWN_FW_MAIN_DATA_MAXSZ) >> 4); iwn_mem_write(sc, IWN_MEM_TEXT_SIZE, IWN_FW_UPDATED | main_textsz); iwn_mem_unlock(sc); /* wait at most one second for second alive notification */ error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz); if (error != 0) { /* this isn't what was supposed to happen.. */ device_printf(sc->sc_dev, "%s: timeout waiting for second alive notice, error %d\n", __func__, error); goto fail; } return 0; fail: return error; } void iwn_unload_firmware(struct iwn_softc *sc) { if (sc->fw_fp != NULL) { firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); sc->fw_fp = NULL; } } static void iwn_timer_timeout(void *arg) { struct iwn_softc *sc = arg; IWN_LOCK_ASSERT(sc); if (sc->calib_cnt && --sc->calib_cnt == 0) { DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n", "send statistics request"); (void) iwn_cmd(sc, IWN_CMD_GET_STATISTICS, NULL, 0, 1); sc->calib_cnt = 60; /* do calibration every 60s */ } iwn_watchdog(sc); /* NB: piggyback tx watchdog */ callout_reset(&sc->sc_timer_to, hz, iwn_timer_timeout, sc); } static void iwn_calib_reset(struct iwn_softc *sc) { callout_reset(&sc->sc_timer_to, hz, iwn_timer_timeout, sc); sc->calib_cnt = 60; /* do calibration every 60s */ } void iwn_ampdu_rx_start(struct iwn_softc *sc, struct iwn_rx_desc *desc) { struct iwn_rx_stat *stat; DPRINTF(sc, IWN_DEBUG_RECV, "%s\n", "received AMPDU stats"); /* save Rx statistics, they will be used on IWN_AMPDU_RX_DONE */ stat = (struct iwn_rx_stat *)(desc + 1); memcpy(&sc->last_rx_stat, stat, sizeof (*stat)); sc->last_rx_valid = 1; } static __inline int maprate(int iwnrate) { switch (iwnrate) { /* CCK rates */ case 10: return 2; case 20: return 4; case 55: return 11; case 110: return 22; /* OFDM rates */ case 0xd: return 12; case 0xf: return 18; case 0x5: return 24; case 0x7: return 36; case 0x9: return 48; case 0xb: return 72; case 0x1: return 96; case 0x3: return 108; /* XXX MCS */ } /* unknown rate: should not happen */ return 0; } void iwn_rx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc, struct iwn_rx_data *data) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_rx_ring *ring = &sc->rxq; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct mbuf *m, *mnew; struct iwn_rx_stat *stat; caddr_t head; uint32_t *tail; int8_t rssi, nf; int len, error; bus_addr_t paddr; if (desc->type == IWN_AMPDU_RX_DONE) { /* check for prior AMPDU_RX_START */ if (!sc->last_rx_valid) { DPRINTF(sc, IWN_DEBUG_ANY, "%s: missing AMPDU_RX_START\n", __func__); ifp->if_ierrors++; return; } sc->last_rx_valid = 0; stat = &sc->last_rx_stat; } else stat = (struct iwn_rx_stat *)(desc + 1); if (stat->cfg_phy_len > IWN_STAT_MAXLEN) { device_printf(sc->sc_dev, "%s: invalid rx statistic header, len %d\n", __func__, stat->cfg_phy_len); ifp->if_ierrors++; return; } if (desc->type == IWN_AMPDU_RX_DONE) { struct iwn_rx_ampdu *ampdu = (struct iwn_rx_ampdu *)(desc + 1); head = (caddr_t)(ampdu + 1); len = le16toh(ampdu->len); } else { head = (caddr_t)(stat + 1) + stat->cfg_phy_len; len = le16toh(stat->len); } /* discard Rx frames with bad CRC early */ tail = (uint32_t *)(head + len); if ((le32toh(*tail) & IWN_RX_NOERROR) != IWN_RX_NOERROR) { DPRINTF(sc, IWN_DEBUG_RECV, "%s: rx flags error %x\n", __func__, le32toh(*tail)); ifp->if_ierrors++; return; } if (len < sizeof (struct ieee80211_frame)) { DPRINTF(sc, IWN_DEBUG_RECV, "%s: frame too short: %d\n", __func__, len); ic->ic_stats.is_rx_tooshort++; ifp->if_ierrors++; return; } /* XXX don't need mbuf, just dma buffer */ mnew = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (mnew == NULL) { DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n", __func__); ic->ic_stats.is_rx_nobuf++; ifp->if_ierrors++; return; } error = bus_dmamap_load(ring->data_dmat, data->map, mtod(mnew, caddr_t), MJUMPAGESIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "%s: bus_dmamap_load failed, error %d\n", __func__, error); m_freem(mnew); ic->ic_stats.is_rx_nobuf++; /* XXX need stat */ ifp->if_ierrors++; return; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); /* finalize mbuf and swap in new one */ m = data->m; m->m_pkthdr.rcvif = ifp; m->m_data = head; m->m_pkthdr.len = m->m_len = len; data->m = mnew; /* update Rx descriptor */ ring->desc[ring->cur] = htole32(paddr >> 8); rssi = iwn_get_rssi(sc, stat); /* grab a reference to the source node */ wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN && (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95; if (bpf_peers_present(ifp->if_bpf)) { struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; tap->wr_dbm_antsignal = rssi; tap->wr_dbm_antnoise = nf; tap->wr_rate = maprate(stat->rate); tap->wr_tsft = htole64(stat->tstamp); if (stat->flags & htole16(IWN_CONFIG_SHPREAMBLE)) tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } IWN_UNLOCK(sc); /* send the frame to the 802.11 layer */ if (ni != NULL) { (void) ieee80211_input(ni, m, rssi - nf, nf, 0); ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, rssi - nf, nf, 0); IWN_LOCK(sc); } void iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct iwn_calib_state *calib = &sc->calib; struct iwn_stats *stats = (struct iwn_stats *)(desc + 1); /* beacon stats are meaningful only when associated and not scanning */ if (vap->iv_state != IEEE80211_S_RUN || (ic->ic_flags & IEEE80211_F_SCAN)) return; DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: cmd %d\n", __func__, desc->type); iwn_calib_reset(sc); /* test if temperature has changed */ if (stats->general.temp != sc->rawtemp) { int temp; sc->rawtemp = stats->general.temp; temp = iwn_get_temperature(sc); DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d\n", __func__, temp); /* update Tx power if need be */ iwn_power_calibration(sc, temp); } if (desc->type != IWN_BEACON_STATISTICS) return; /* reply to a statistics request */ sc->noise = iwn_get_noise(&stats->rx.general); DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: noise %d\n", __func__, sc->noise); /* test that RSSI and noise are present in stats report */ if (stats->rx.general.flags != htole32(1)) { DPRINTF(sc, IWN_DEBUG_ANY, "%s\n", "received statistics without RSSI"); return; } if (calib->state == IWN_CALIB_STATE_ASSOC) iwn_compute_differential_gain(sc, &stats->rx.general); else if (calib->state == IWN_CALIB_STATE_RUN) iwn_tune_sensitivity(sc, &stats->rx); } void iwn_tx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc) { struct ifnet *ifp = sc->sc_ifp; struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; struct iwn_tx_data *data = &ring->data[desc->idx]; struct iwn_tx_stat *stat = (struct iwn_tx_stat *)(desc + 1); struct iwn_node *wn = IWN_NODE(data->ni); struct mbuf *m; struct ieee80211_node *ni; uint32_t status; KASSERT(data->ni != NULL, ("no node")); DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n", __func__, desc->qid, desc->idx, stat->ntries, stat->nkill, stat->rate, le16toh(stat->duration), le32toh(stat->status)); /* * Update rate control statistics for the node. */ status = le32toh(stat->status) & 0xff; if (status & 0x80) { DPRINTF(sc, IWN_DEBUG_ANY, "%s: status 0x%x\n", __func__, le32toh(stat->status)); ifp->if_oerrors++; ieee80211_amrr_tx_complete(&wn->amn, IEEE80211_AMRR_FAILURE, stat->ntries); } else { ieee80211_amrr_tx_complete(&wn->amn, IEEE80211_AMRR_SUCCESS, stat->ntries); } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m = data->m, data->m = NULL; ni = data->ni, data->ni = NULL; if (m->m_flags & M_TXCB) { /* * Channels marked for "radar" require traffic to be received * to unlock before we can transmit. Until traffic is seen * any attempt to transmit is returned immediately with status * set to IWN_TX_FAIL_TX_LOCKED. Unfortunately this can easily * happen on first authenticate after scanning. To workaround * this we ignore a failure of this sort in AUTH state so the * 802.11 layer will fall back to using a timeout to wait for * the AUTH reply. This allows the firmware time to see * traffic so a subsequent retry of AUTH succeeds. It's * unclear why the firmware does not maintain state for * channels recently visited as this would allow immediate * use of the channel after a scan (where we see traffic). */ if (status == IWN_TX_FAIL_TX_LOCKED && ni->ni_vap->iv_state == IEEE80211_S_AUTH) ieee80211_process_callback(ni, m, 0); else ieee80211_process_callback(ni, m, (status & IWN_TX_FAIL) != 0); } m_freem(m); ieee80211_free_node(ni); ring->queued--; sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; iwn_start_locked(ifp); } void iwn_cmd_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc) { struct iwn_tx_ring *ring = &sc->txq[4]; struct iwn_tx_data *data; if ((desc->qid & 0xf) != 4) return; /* not a command ack */ data = &ring->data[desc->idx]; /* if the command was mapped in a mbuf, free it */ if (data->m != NULL) { bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } wakeup(&ring->cmd[desc->idx]); } static void iwn_bmiss(void *arg, int npending) { struct iwn_softc *sc = arg; struct ieee80211com *ic = sc->sc_ifp->if_l2com; ieee80211_beacon_miss(ic); } void iwn_notif_intr(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint16_t hw; hw = le16toh(sc->shared->closed_count) & 0xfff; while (sc->rxq.cur != hw) { struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur]; struct iwn_rx_desc *desc = (void *)data->m->m_ext.ext_buf; DPRINTF(sc, IWN_DEBUG_RECV, "%s: qid %x idx %d flags %x type %d(%s) len %d\n", __func__, desc->qid, desc->idx, desc->flags, desc->type, iwn_intr_str(desc->type), le16toh(desc->len)); if (!(desc->qid & 0x80)) /* reply to a command */ iwn_cmd_intr(sc, desc); switch (desc->type) { case IWN_RX_DONE: case IWN_AMPDU_RX_DONE: iwn_rx_intr(sc, desc, data); break; case IWN_AMPDU_RX_START: iwn_ampdu_rx_start(sc, desc); break; case IWN_TX_DONE: /* a 802.11 frame has been transmitted */ iwn_tx_intr(sc, desc); break; case IWN_RX_STATISTICS: case IWN_BEACON_STATISTICS: iwn_rx_statistics(sc, desc); break; case IWN_BEACON_MISSED: { struct iwn_beacon_missed *miss = (struct iwn_beacon_missed *)(desc + 1); int misses = le32toh(miss->consecutive); /* XXX not sure why we're notified w/ zero */ if (misses == 0) break; DPRINTF(sc, IWN_DEBUG_STATE, "%s: beacons missed %d/%d\n", __func__, misses, le32toh(miss->total)); /* * If more than 5 consecutive beacons are missed, * reinitialize the sensitivity state machine. */ if (vap->iv_state == IEEE80211_S_RUN && misses > 5) (void) iwn_init_sensitivity(sc); if (misses >= vap->iv_bmissthreshold) taskqueue_enqueue(taskqueue_swi, &sc->sc_bmiss_task); break; } case IWN_UC_READY: { struct iwn_ucode_info *uc = (struct iwn_ucode_info *)(desc + 1); /* the microcontroller is ready */ DPRINTF(sc, IWN_DEBUG_RESET, "microcode alive notification version=%d.%d " "subtype=%x alive=%x\n", uc->major, uc->minor, uc->subtype, le32toh(uc->valid)); if (le32toh(uc->valid) != 1) { device_printf(sc->sc_dev, "microcontroller initialization failed"); break; } if (uc->subtype == IWN_UCODE_INIT) { /* save microcontroller's report */ memcpy(&sc->ucode_info, uc, sizeof (*uc)); } break; } case IWN_STATE_CHANGED: { uint32_t *status = (uint32_t *)(desc + 1); /* * State change allows hardware switch change to be * noted. However, we handle this in iwn_intr as we * get both the enable/disble intr. */ DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n", le32toh(*status)); break; } case IWN_START_SCAN: { struct iwn_start_scan *scan = (struct iwn_start_scan *)(desc + 1); DPRINTF(sc, IWN_DEBUG_ANY, "%s: scanning channel %d status %x\n", __func__, scan->chan, le32toh(scan->status)); break; } case IWN_STOP_SCAN: { struct iwn_stop_scan *scan = (struct iwn_stop_scan *)(desc + 1); DPRINTF(sc, IWN_DEBUG_STATE, "scan finished nchan=%d status=%d chan=%d\n", scan->nchan, scan->status, scan->chan); iwn_queue_cmd(sc, IWN_SCAN_NEXT, 0, IWN_QUEUE_NORMAL); break; } } sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT; } /* tell the firmware what we have processed */ hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1; IWN_WRITE(sc, IWN_RX_WIDX, hw & ~7); } void iwn_intr(void *arg) { struct iwn_softc *sc = arg; uint32_t r1, r2; IWN_LOCK(sc); /* disable interrupts */ IWN_WRITE(sc, IWN_MASK, 0); r1 = IWN_READ(sc, IWN_INTR); r2 = IWN_READ(sc, IWN_INTR_STATUS); if (r1 == 0 && r2 == 0) { IWN_WRITE(sc, IWN_MASK, IWN_INTR_MASK); goto done; /* not for us */ } if (r1 == 0xffffffff) goto done; /* hardware gone */ /* ack interrupts */ IWN_WRITE(sc, IWN_INTR, r1); IWN_WRITE(sc, IWN_INTR_STATUS, r2); DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=%x reg2=%x\n", r1, r2); if (r1 & IWN_RF_TOGGLED) { uint32_t tmp = IWN_READ(sc, IWN_GPIO_CTL); device_printf(sc->sc_dev, "RF switch: radio %s\n", (tmp & IWN_GPIO_RF_ENABLED) ? "enabled" : "disabled"); if (tmp & IWN_GPIO_RF_ENABLED) iwn_queue_cmd(sc, IWN_RADIO_ENABLE, 0, IWN_QUEUE_CLEAR); else iwn_queue_cmd(sc, IWN_RADIO_DISABLE, 0, IWN_QUEUE_CLEAR); } if (r1 & IWN_CT_REACHED) device_printf(sc->sc_dev, "critical temperature reached!\n"); if (r1 & (IWN_SW_ERROR | IWN_HW_ERROR)) { device_printf(sc->sc_dev, "error, INTR=%b STATUS=0x%x\n", r1, IWN_INTR_BITS, r2); iwn_queue_cmd(sc, IWN_REINIT, 0, IWN_QUEUE_CLEAR); goto done; } if ((r1 & (IWN_RX_INTR | IWN_SW_RX_INTR)) || (r2 & IWN_RX_STATUS_INTR)) iwn_notif_intr(sc); if (r1 & IWN_ALIVE_INTR) wakeup(sc); /* re-enable interrupts */ IWN_WRITE(sc, IWN_MASK, IWN_INTR_MASK); done: IWN_UNLOCK(sc); } uint8_t iwn_plcp_signal(int rate) { switch (rate) { /* CCK rates (returned values are device-dependent) */ case 2: return 10; case 4: return 20; case 11: return 55; case 22: return 110; /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ /* R1-R4, (u)ral is R4-R1 */ case 12: return 0xd; case 18: return 0xf; case 24: return 0x5; case 36: return 0x7; case 48: return 0x9; case 72: return 0xb; case 96: return 0x1; case 108: return 0x3; case 120: return 0x3; } /* unknown rate (should not get there) */ return 0; } /* determine if a given rate is CCK or OFDM */ #define IWN_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) int iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, struct iwn_tx_ring *ring) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = sc->sc_ifp; const struct ieee80211_txparam *tp; struct iwn_tx_desc *desc; struct iwn_tx_data *data; struct iwn_tx_cmd *cmd; struct iwn_cmd_data *tx; struct ieee80211_frame *wh; struct ieee80211_key *k; bus_addr_t paddr; uint32_t flags; uint16_t timeout; uint8_t type; u_int hdrlen; struct mbuf *mnew; int rate, error, pad, nsegs, i, ismcast, id; bus_dma_segment_t segs[IWN_MAX_SCATTER]; IWN_LOCK_ASSERT(sc); wh = mtod(m0, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); hdrlen = ieee80211_anyhdrsize(wh); /* pick a tx rate */ /* XXX ni_chan */ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (type == IEEE80211_FC0_TYPE_MGT) rate = tp->mgmtrate; else if (ismcast) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else { (void) ieee80211_amrr_choose(ni, &IWN_NODE(ni)->amn); rate = ni->ni_txrate; } if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } else k = NULL; if (bpf_peers_present(ifp->if_bpf)) { struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; if (k != NULL) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } flags = IWN_TX_AUTO_SEQ; /* XXX honor ACM */ if (!ismcast) flags |= IWN_TX_NEED_ACK; if (ismcast || type != IEEE80211_FC0_TYPE_DATA) id = IWN_ID_BROADCAST; else id = IWN_ID_BSS; /* check if RTS/CTS or CTS-to-self protection must be used */ if (!ismcast) { /* multicast frames are not sent at OFDM rates in 802.11b/g */ if (m0->m_pkthdr.len+IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && IWN_RATE_IS_OFDM(rate)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; } } if (type == IEEE80211_FC0_TYPE_MGT) { uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; /* tell h/w to set timestamp in probe responses */ if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= IWN_TX_INSERT_TSTAMP; if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) timeout = htole16(3); else timeout = htole16(2); } else timeout = htole16(0); if (hdrlen & 3) { /* first segment's length must be a multiple of 4 */ flags |= IWN_TX_NEED_PADDING; pad = 4 - (hdrlen & 3); } else pad = 0; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; cmd = &ring->cmd[ring->cur]; cmd->code = IWN_CMD_TX_DATA; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; tx = (struct iwn_cmd_data *)cmd->data; /* NB: no need to bzero tx, all fields are reinitialized here */ tx->id = id; tx->flags = htole32(flags); tx->len = htole16(m0->m_pkthdr.len); tx->rate = iwn_plcp_signal(rate); tx->rts_ntries = 60; /* XXX? */ tx->data_ntries = 15; /* XXX? */ tx->lifetime = htole32(IWN_LIFETIME_INFINITE); tx->timeout = timeout; if (k != NULL) { /* XXX fill in */; } else tx->security = 0; /* XXX alternate between Ant A and Ant B ? */ tx->rflags = IWN_RFLAG_ANT_B; if (tx->id == IWN_ID_BROADCAST) { tx->ridx = IWN_MAX_TX_RETRIES - 1; if (!IWN_RATE_IS_OFDM(rate)) tx->rflags |= IWN_RFLAG_CCK; } else { tx->ridx = 0; /* tell adapter to ignore rflags */ tx->flags |= htole32(IWN_TX_USE_NODE_RATE); } /* copy and trim IEEE802.11 header */ memcpy((uint8_t *)(tx + 1), wh, hdrlen); m_adj(m0, hdrlen); error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { if (error == EFBIG) { /* too many fragments, linearize */ mnew = m_collapse(m0, M_DONTWAIT, IWN_MAX_SCATTER); if (mnew == NULL) { IWN_UNLOCK(sc); device_printf(sc->sc_dev, "%s: could not defrag mbuf\n", __func__); m_freem(m0); return ENOBUFS; } m0 = mnew; error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); } if (error != 0) { IWN_UNLOCK(sc); device_printf(sc->sc_dev, "%s: bus_dmamap_load_mbuf_sg failed, error %d\n", __func__, error); m_freem(m0); return error; } } data->m = m0; data->ni = ni; DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", __func__, ring->qid, ring->cur, m0->m_pkthdr.len, nsegs); paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); tx->loaddr = htole32(paddr + 4 + offsetof(struct iwn_cmd_data, ntries)); tx->hiaddr = 0; /* limit to 32-bit physical addresses */ /* first scatter/gather segment is used by the tx data command */ IWN_SET_DESC_NSEGS(desc, 1 + nsegs); IWN_SET_DESC_SEG(desc, 0, paddr, 4 + sizeof (*tx) + hdrlen + pad); for (i = 1; i <= nsegs; i++) { IWN_SET_DESC_SEG(desc, i, segs[i - 1].ds_addr, segs[i - 1].ds_len); } sc->shared->len[ring->qid][ring->cur] = htole16(hdrlen + m0->m_pkthdr.len + 8); if (ring->cur < IWN_TX_WINDOW) sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = htole16(hdrlen + m0->m_pkthdr.len + 8); ring->queued++; /* kick Tx ring */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); ifp->if_opackets++; sc->sc_tx_timer = 5; return 0; } void iwn_start(struct ifnet *ifp) { struct iwn_softc *sc = ifp->if_softc; IWN_LOCK(sc); iwn_start_locked(ifp); IWN_UNLOCK(sc); } void iwn_start_locked(struct ifnet *ifp) { struct iwn_softc *sc = ifp->if_softc; struct ieee80211_node *ni; struct iwn_tx_ring *txq; struct mbuf *m; int pri; IWN_LOCK_ASSERT(sc); for (;;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; pri = M_WME_GETAC(m); txq = &sc->txq[pri]; m = ieee80211_encap(ni, m); if (m == NULL) { ifp->if_oerrors++; ieee80211_free_node(ni); continue; } if (txq->queued >= IWN_TX_RING_COUNT - 8) { /* XXX not right */ /* ring is nearly full, stop flow */ ifp->if_drv_flags |= IFF_DRV_OACTIVE; } if (iwn_tx_data(sc, m, ni, txq) != 0) { ifp->if_oerrors++; ieee80211_free_node(ni); IWN_UNLOCK(sc); break; } } } static int iwn_tx_handoff(struct iwn_softc *sc, struct iwn_tx_ring *ring, struct iwn_tx_cmd *cmd, struct iwn_cmd_data *tx, struct ieee80211_node *ni, struct mbuf *m0, u_int hdrlen, int pad) { struct ifnet *ifp = sc->sc_ifp; struct iwn_tx_desc *desc; struct iwn_tx_data *data; bus_addr_t paddr; struct mbuf *mnew; int error, nsegs, i; bus_dma_segment_t segs[IWN_MAX_SCATTER]; /* copy and trim IEEE802.11 header */ memcpy((uint8_t *)(tx + 1), mtod(m0, uint8_t *), hdrlen); m_adj(m0, hdrlen); desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { if (error == EFBIG) { /* too many fragments, linearize */ mnew = m_collapse(m0, M_DONTWAIT, IWN_MAX_SCATTER); if (mnew == NULL) { IWN_UNLOCK(sc); device_printf(sc->sc_dev, "%s: could not defrag mbuf\n", __func__); m_freem(m0); return ENOBUFS; } m0 = mnew; error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); } if (error != 0) { IWN_UNLOCK(sc); device_printf(sc->sc_dev, "%s: bus_dmamap_load_mbuf_sg failed, error %d\n", __func__, error); m_freem(m0); return error; } } data->m = m0; data->ni = ni; DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", __func__, ring->qid, ring->cur, m0->m_pkthdr.len, nsegs); paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); tx->loaddr = htole32(paddr + 4 + offsetof(struct iwn_cmd_data, ntries)); tx->hiaddr = 0; /* limit to 32-bit physical addresses */ /* first scatter/gather segment is used by the tx data command */ IWN_SET_DESC_NSEGS(desc, 1 + nsegs); IWN_SET_DESC_SEG(desc, 0, paddr, 4 + sizeof (*tx) + hdrlen + pad); for (i = 1; i <= nsegs; i++) { IWN_SET_DESC_SEG(desc, i, segs[i - 1].ds_addr, segs[i - 1].ds_len); } sc->shared->len[ring->qid][ring->cur] = htole16(hdrlen + m0->m_pkthdr.len + 8); if (ring->cur < IWN_TX_WINDOW) sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = htole16(hdrlen + m0->m_pkthdr.len + 8); ring->queued++; /* kick Tx ring */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); ifp->if_opackets++; sc->sc_tx_timer = 5; return 0; } static int iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, struct iwn_tx_ring *ring, const struct ieee80211_bpf_params *params) { struct ifnet *ifp = sc->sc_ifp; struct iwn_tx_cmd *cmd; struct iwn_cmd_data *tx; struct ieee80211_frame *wh; uint32_t flags; uint8_t type, subtype; u_int hdrlen; int rate, pad; IWN_LOCK_ASSERT(sc); wh = mtod(m0, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; hdrlen = ieee80211_anyhdrsize(wh); flags = IWN_TX_AUTO_SEQ; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= IWN_TX_NEED_ACK; if (params->ibp_flags & IEEE80211_BPF_RTS) flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; if (params->ibp_flags & IEEE80211_BPF_CTS) flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP; if (type == IEEE80211_FC0_TYPE_MGT && subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) { /* tell h/w to set timestamp in probe responses */ flags |= IWN_TX_INSERT_TSTAMP; } if (hdrlen & 3) { /* first segment's length must be a multiple of 4 */ flags |= IWN_TX_NEED_PADDING; pad = 4 - (hdrlen & 3); } else pad = 0; /* pick a tx rate */ rate = params->ibp_rate0; if (bpf_peers_present(ifp->if_bpf)) { struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } cmd = &ring->cmd[ring->cur]; cmd->code = IWN_CMD_TX_DATA; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; tx = (struct iwn_cmd_data *)cmd->data; /* NB: no need to bzero tx, all fields are reinitialized here */ tx->id = IWN_ID_BROADCAST; tx->flags = htole32(flags); tx->len = htole16(m0->m_pkthdr.len); tx->rate = iwn_plcp_signal(rate); tx->rts_ntries = params->ibp_try1; /* XXX? */ tx->data_ntries = params->ibp_try0; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); /* XXX use try count? */ if (type == IEEE80211_FC0_TYPE_MGT) { if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) tx->timeout = htole16(3); else tx->timeout = htole16(2); } else tx->timeout = htole16(0); tx->security = 0; /* XXX alternate between Ant A and Ant B ? */ tx->rflags = IWN_RFLAG_ANT_B; /* XXX params->ibp_pri >> 2 */ tx->ridx = IWN_MAX_TX_RETRIES - 1; if (!IWN_RATE_IS_OFDM(rate)) tx->rflags |= IWN_RFLAG_CCK; return iwn_tx_handoff(sc, ring, cmd, tx, ni, m0, hdrlen, pad); } static int iwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; struct iwn_tx_ring *txq; int error; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { ieee80211_free_node(ni); m_freem(m); return ENETDOWN; } IWN_LOCK(sc); if (params == NULL) txq = &sc->txq[M_WME_GETAC(m)]; else txq = &sc->txq[params->ibp_pri & 3]; if (txq->queued >= IWN_TX_RING_COUNT - 8) { /* XXX not right */ /* ring is nearly full, stop flow */ ifp->if_drv_flags |= IFF_DRV_OACTIVE; } if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ error = iwn_tx_data(sc, m, ni, txq); } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ error = iwn_tx_data_raw(sc, m, ni, txq, params); } if (error != 0) { /* NB: m is reclaimed on tx failure */ ieee80211_free_node(ni); ifp->if_oerrors++; } IWN_UNLOCK(sc); return error; } static void iwn_watchdog(struct iwn_softc *sc) { if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) { struct ifnet *ifp = sc->sc_ifp; if_printf(ifp, "device timeout\n"); iwn_queue_cmd(sc, IWN_REINIT, 0, IWN_QUEUE_CLEAR); } } int iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct iwn_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; switch (cmd) { case SIOCSIFFLAGS: IWN_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { iwn_init_locked(sc); startall = 1; } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) iwn_stop_locked(sc); } IWN_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return error; } void iwn_read_eeprom(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; char domain[4]; uint16_t val; int i, error; if ((error = iwn_eeprom_lock(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not lock EEPROM, error %d\n", __func__, error); return; } /* read and print regulatory domain */ iwn_read_prom_data(sc, IWN_EEPROM_DOMAIN, domain, 4); device_printf(sc->sc_dev,"Reg Domain: %.4s", domain); /* read and print MAC address */ iwn_read_prom_data(sc, IWN_EEPROM_MAC, ic->ic_myaddr, 6); printf(", address %s\n", ether_sprintf(ic->ic_myaddr)); /* read the list of authorized channels */ iwn_read_eeprom_channels(sc); /* read maximum allowed Tx power for 2GHz and 5GHz bands */ iwn_read_prom_data(sc, IWN_EEPROM_MAXPOW, &val, 2); sc->maxpwr2GHz = val & 0xff; sc->maxpwr5GHz = val >> 8; /* check that EEPROM values are correct */ if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50) sc->maxpwr5GHz = 38; if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50) sc->maxpwr2GHz = 38; DPRINTF(sc, IWN_DEBUG_RESET, "maxpwr 2GHz=%d 5GHz=%d\n", sc->maxpwr2GHz, sc->maxpwr5GHz); /* read voltage at which samples were taken */ iwn_read_prom_data(sc, IWN_EEPROM_VOLTAGE, &val, 2); sc->eeprom_voltage = (int16_t)le16toh(val); DPRINTF(sc, IWN_DEBUG_RESET, "voltage=%d (in 0.3V)\n", sc->eeprom_voltage); /* read power groups */ iwn_read_prom_data(sc, IWN_EEPROM_BANDS, sc->bands, sizeof sc->bands); #ifdef IWN_DEBUG if (sc->sc_debug & IWN_DEBUG_ANY) { for (i = 0; i < IWN_NBANDS; i++) iwn_print_power_group(sc, i); } #endif iwn_eeprom_unlock(sc); } struct iwn_chan_band { uint32_t addr; /* offset in EEPROM */ uint32_t flags; /* net80211 flags */ uint8_t nchan; #define IWN_MAX_CHAN_PER_BAND 14 uint8_t chan[IWN_MAX_CHAN_PER_BAND]; }; static void iwn_read_eeprom_band(struct iwn_softc *sc, const struct iwn_chan_band *band) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_eeprom_chan channels[IWN_MAX_CHAN_PER_BAND]; struct ieee80211_channel *c; int i, chan, flags; iwn_read_prom_data(sc, band->addr, channels, band->nchan * sizeof (struct iwn_eeprom_chan)); for (i = 0; i < band->nchan; i++) { if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) { DPRINTF(sc, IWN_DEBUG_RESET, "skip chan %d flags 0x%x maxpwr %d\n", band->chan[i], channels[i].flags, channels[i].maxpwr); continue; } chan = band->chan[i]; /* translate EEPROM flags to net80211 */ flags = 0; if ((channels[i].flags & IWN_EEPROM_CHAN_ACTIVE) == 0) flags |= IEEE80211_CHAN_PASSIVE; if ((channels[i].flags & IWN_EEPROM_CHAN_IBSS) == 0) flags |= IEEE80211_CHAN_NOADHOC; if (channels[i].flags & IWN_EEPROM_CHAN_RADAR) { flags |= IEEE80211_CHAN_DFS; /* XXX apparently IBSS may still be marked */ flags |= IEEE80211_CHAN_NOADHOC; } DPRINTF(sc, IWN_DEBUG_RESET, "add chan %d flags 0x%x maxpwr %d\n", chan, channels[i].flags, channels[i].maxpwr); c = &ic->ic_channels[ic->ic_nchans++]; c->ic_ieee = chan; c->ic_freq = ieee80211_ieee2mhz(chan, band->flags); c->ic_maxregpower = channels[i].maxpwr; c->ic_maxpower = 2*c->ic_maxregpower; if (band->flags & IEEE80211_CHAN_2GHZ) { /* G =>'s B is supported */ c->ic_flags = IEEE80211_CHAN_B | flags; c = &ic->ic_channels[ic->ic_nchans++]; c[0] = c[-1]; c->ic_flags = IEEE80211_CHAN_G | flags; } else { /* 5GHz band */ c->ic_flags = IEEE80211_CHAN_A | flags; } /* XXX no constraints on using HT20 */ /* add HT20, HT40 added separately */ c = &ic->ic_channels[ic->ic_nchans++]; c[0] = c[-1]; c->ic_flags |= IEEE80211_CHAN_HT20; /* XXX NARROW =>'s 1/2 and 1/4 width? */ } } static void iwn_read_eeprom_ht40(struct iwn_softc *sc, const struct iwn_chan_band *band) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_eeprom_chan channels[IWN_MAX_CHAN_PER_BAND]; struct ieee80211_channel *c, *cent, *extc; int i; iwn_read_prom_data(sc, band->addr, channels, band->nchan * sizeof (struct iwn_eeprom_chan)); for (i = 0; i < band->nchan; i++) { if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID) || !(channels[i].flags & IWN_EEPROM_CHAN_WIDE)) { DPRINTF(sc, IWN_DEBUG_RESET, "skip chan %d flags 0x%x maxpwr %d\n", band->chan[i], channels[i].flags, channels[i].maxpwr); continue; } /* * Each entry defines an HT40 channel pair; find the * center channel, then the extension channel above. */ cent = ieee80211_find_channel_byieee(ic, band->chan[i], band->flags & ~IEEE80211_CHAN_HT); if (cent == NULL) { /* XXX shouldn't happen */ device_printf(sc->sc_dev, "%s: no entry for channel %d\n", __func__, band->chan[i]); continue; } extc = ieee80211_find_channel(ic, cent->ic_freq+20, band->flags & ~IEEE80211_CHAN_HT); if (extc == NULL) { DPRINTF(sc, IWN_DEBUG_RESET, "skip chan %d, extension channel not found\n", band->chan[i]); continue; } DPRINTF(sc, IWN_DEBUG_RESET, "add ht40 chan %d flags 0x%x maxpwr %d\n", band->chan[i], channels[i].flags, channels[i].maxpwr); c = &ic->ic_channels[ic->ic_nchans++]; c[0] = cent[0]; c->ic_extieee = extc->ic_ieee; c->ic_flags &= ~IEEE80211_CHAN_HT; c->ic_flags |= IEEE80211_CHAN_HT40U; c = &ic->ic_channels[ic->ic_nchans++]; c[0] = extc[0]; c->ic_extieee = cent->ic_ieee; c->ic_flags &= ~IEEE80211_CHAN_HT; c->ic_flags |= IEEE80211_CHAN_HT40D; } } static void iwn_read_eeprom_channels(struct iwn_softc *sc) { #define N(a) (sizeof(a)/sizeof(a[0])) static const struct iwn_chan_band iwn_bands[] = { { IWN_EEPROM_BAND1, IEEE80211_CHAN_G, 14, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 } }, { IWN_EEPROM_BAND2, IEEE80211_CHAN_A, 13, { 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 } }, { IWN_EEPROM_BAND3, IEEE80211_CHAN_A, 12, { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 } }, { IWN_EEPROM_BAND4, IEEE80211_CHAN_A, 11, { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 } }, { IWN_EEPROM_BAND5, IEEE80211_CHAN_A, 6, { 145, 149, 153, 157, 161, 165 } }, { IWN_EEPROM_BAND6, IEEE80211_CHAN_G | IEEE80211_CHAN_HT40, 7, { 1, 2, 3, 4, 5, 6, 7 } }, { IWN_EEPROM_BAND7, IEEE80211_CHAN_A | IEEE80211_CHAN_HT40, 11, { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 } } }; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int i; /* read the list of authorized channels */ for (i = 0; i < N(iwn_bands)-2; i++) iwn_read_eeprom_band(sc, &iwn_bands[i]); for (; i < N(iwn_bands); i++) iwn_read_eeprom_ht40(sc, &iwn_bands[i]); ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); #undef N } #ifdef IWN_DEBUG void iwn_print_power_group(struct iwn_softc *sc, int i) { struct iwn_eeprom_band *band = &sc->bands[i]; struct iwn_eeprom_chan_samples *chans = band->chans; int j, c; printf("===band %d===\n", i); printf("chan lo=%d, chan hi=%d\n", band->lo, band->hi); printf("chan1 num=%d\n", chans[0].num); for (c = 0; c < IWN_NTXCHAINS; c++) { for (j = 0; j < IWN_NSAMPLES; j++) { printf("chain %d, sample %d: temp=%d gain=%d " "power=%d pa_det=%d\n", c, j, chans[0].samples[c][j].temp, chans[0].samples[c][j].gain, chans[0].samples[c][j].power, chans[0].samples[c][j].pa_det); } } printf("chan2 num=%d\n", chans[1].num); for (c = 0; c < IWN_NTXCHAINS; c++) { for (j = 0; j < IWN_NSAMPLES; j++) { printf("chain %d, sample %d: temp=%d gain=%d " "power=%d pa_det=%d\n", c, j, chans[1].samples[c][j].temp, chans[1].samples[c][j].gain, chans[1].samples[c][j].power, chans[1].samples[c][j].pa_det); } } } #endif /* * Send a command to the firmware. */ int iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) { struct iwn_tx_ring *ring = &sc->txq[4]; struct iwn_tx_desc *desc; struct iwn_tx_cmd *cmd; bus_addr_t paddr; IWN_LOCK_ASSERT(sc); KASSERT(size <= sizeof cmd->data, ("Command too big")); desc = &ring->desc[ring->cur]; cmd = &ring->cmd[ring->cur]; cmd->code = code; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; memcpy(cmd->data, buf, size); paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); IWN_SET_DESC_NSEGS(desc, 1); IWN_SET_DESC_SEG(desc, 0, paddr, 4 + size); sc->shared->len[ring->qid][ring->cur] = htole16(8); if (ring->cur < IWN_TX_WINDOW) { sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = htole16(8); } DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n", __func__, iwn_intr_str(cmd->code), cmd->code, cmd->flags, cmd->qid, cmd->idx); /* kick cmd ring */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); return async ? 0 : msleep(cmd, &sc->sc_mtx, PCATCH, "iwncmd", hz); } static const uint8_t iwn_ridx_to_plcp[] = { 10, 20, 55, 110, /* CCK */ 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, 0x3 /* OFDM R1-R4 */ }; static const uint8_t iwn_siso_mcs_to_plcp[] = { 0, 0, 0, 0, /* CCK */ 0, 0, 1, 2, 3, 4, 5, 6, 7 /* HT */ }; static const uint8_t iwn_mimo_mcs_to_plcp[] = { 0, 0, 0, 0, /* CCK */ 8, 8, 9, 10, 11, 12, 13, 14, 15 /* HT */ }; static const uint8_t iwn_prev_ridx[] = { /* NB: allow fallback from CCK11 to OFDM9 and from OFDM6 to CCK5 */ 0, 0, 1, 5, /* CCK */ 2, 4, 3, 6, 7, 8, 9, 10, 10 /* OFDM */ }; /* * Configure hardware link parameters for the specified * node operating on the specified channel. */ int iwn_set_link_quality(struct iwn_softc *sc, uint8_t id, const struct ieee80211_channel *c, int async) { struct iwn_cmd_link_quality lq; int i, ridx; memset(&lq, 0, sizeof(lq)); lq.id = id; if (IEEE80211_IS_CHAN_HT(c)) { lq.mimo = 1; lq.ssmask = 0x1; } else lq.ssmask = 0x2; if (id == IWN_ID_BSS) ridx = IWN_RATE_OFDM54; else if (IEEE80211_IS_CHAN_A(c)) ridx = IWN_RATE_OFDM6; else ridx = IWN_RATE_CCK1; for (i = 0; i < IWN_MAX_TX_RETRIES; i++) { /* XXX toggle antenna for retry patterns */ if (IEEE80211_IS_CHAN_HT40(c)) { lq.table[i].rate = iwn_mimo_mcs_to_plcp[ridx] | IWN_RATE_MCS; lq.table[i].rflags = IWN_RFLAG_HT | IWN_RFLAG_HT40 | IWN_RFLAG_ANT_A; /* XXX shortGI */ } else if (IEEE80211_IS_CHAN_HT(c)) { lq.table[i].rate = iwn_siso_mcs_to_plcp[ridx] | IWN_RATE_MCS; lq.table[i].rflags = IWN_RFLAG_HT | IWN_RFLAG_ANT_A; /* XXX shortGI */ } else { lq.table[i].rate = iwn_ridx_to_plcp[ridx]; if (ridx <= IWN_RATE_CCK11) lq.table[i].rflags = IWN_RFLAG_CCK; lq.table[i].rflags |= IWN_RFLAG_ANT_B; } ridx = iwn_prev_ridx[ridx]; } lq.dsmask = 0x3; lq.ampdu_disable = 3; lq.ampdu_limit = htole16(4000); #ifdef IWN_DEBUG if (sc->sc_debug & IWN_DEBUG_STATE) { printf("%s: set link quality for node %d, mimo %d ssmask %d\n", __func__, id, lq.mimo, lq.ssmask); printf("%s:", __func__); for (i = 0; i < IWN_MAX_TX_RETRIES; i++) printf(" %d:%x", lq.table[i].rate, lq.table[i].rflags); printf("\n"); } #endif return iwn_cmd(sc, IWN_CMD_TX_LINK_QUALITY, &lq, sizeof(lq), async); } #if 0 /* * Install a pairwise key into the hardware. */ int iwn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, const struct ieee80211_key *k) { struct iwn_softc *sc = ic->ic_softc; struct iwn_node_info node; if (k->k_flags & IEEE80211_KEY_GROUP) return 0; memset(&node, 0, sizeof node); switch (k->k_cipher) { case IEEE80211_CIPHER_CCMP: node.security = htole16(IWN_CIPHER_CCMP); memcpy(node.key, k->k_key, k->k_len); break; default: return 0; } node.id = IWN_ID_BSS; IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); node.control = IWN_NODE_UPDATE; node.flags = IWN_FLAG_SET_KEY; return iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 1); } #endif int iwn_wme_update(struct ieee80211com *ic) { #define IWN_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ #define IWN_TXOP_TO_US(v) (v<<5) struct iwn_softc *sc = ic->ic_ifp->if_softc; struct iwn_edca_params cmd; int i; memset(&cmd, 0, sizeof cmd); cmd.flags = htole32(IWN_EDCA_UPDATE); for (i = 0; i < WME_NUM_AC; i++) { const struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[i]; cmd.ac[i].aifsn = wmep->wmep_aifsn; cmd.ac[i].cwmin = htole16(IWN_EXP2(wmep->wmep_logcwmin)); cmd.ac[i].cwmax = htole16(IWN_EXP2(wmep->wmep_logcwmax)); cmd.ac[i].txoplimit = htole16(IWN_TXOP_TO_US(wmep->wmep_txopLimit)); } IWN_LOCK(sc); (void) iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1 /*async*/); IWN_UNLOCK(sc); return 0; #undef IWN_TXOP_TO_US #undef IWN_EXP2 } void iwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on) { struct iwn_cmd_led led; led.which = which; led.unit = htole32(100000); /* on/off in unit of 100ms */ led.off = off; led.on = on; (void) iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1); } /* * Set the critical temperature at which the firmware will automatically stop * the radio transmitter. */ int iwn_set_critical_temp(struct iwn_softc *sc) { struct iwn_ucode_info *uc = &sc->ucode_info; struct iwn_critical_temp crit; uint32_t r1, r2, r3, temp; r1 = le32toh(uc->temp[0].chan20MHz); r2 = le32toh(uc->temp[1].chan20MHz); r3 = le32toh(uc->temp[2].chan20MHz); /* inverse function of iwn_get_temperature() */ temp = r2 + (IWN_CTOK(110) * (r3 - r1)) / 259; IWN_WRITE(sc, IWN_UCODE_CLR, IWN_CTEMP_STOP_RF); memset(&crit, 0, sizeof crit); crit.tempR = htole32(temp); DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %u\n", temp); return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0); } void iwn_enable_tsf(struct iwn_softc *sc, struct ieee80211_node *ni) { struct iwn_cmd_tsf tsf; uint64_t val, mod; memset(&tsf, 0, sizeof tsf); memcpy(&tsf.tstamp, ni->ni_tstamp.data, sizeof (uint64_t)); tsf.bintval = htole16(ni->ni_intval); tsf.lintval = htole16(10); /* XXX all wrong */ /* compute remaining time until next beacon */ val = (uint64_t)ni->ni_intval * 1024; /* msecs -> usecs */ DPRINTF(sc, IWN_DEBUG_ANY, "%s: val = %ju %s\n", __func__, val, val == 0 ? "correcting" : ""); if (val == 0) val = 1; mod = le64toh(tsf.tstamp) % val; tsf.binitval = htole32((uint32_t)(val - mod)); DPRINTF(sc, IWN_DEBUG_RESET, "TSF bintval=%u tstamp=%ju, init=%u\n", ni->ni_intval, le64toh(tsf.tstamp), (uint32_t)(val - mod)); if (iwn_cmd(sc, IWN_CMD_TSF, &tsf, sizeof tsf, 1) != 0) device_printf(sc->sc_dev, "%s: could not enable TSF\n", __func__); } void iwn_power_calibration(struct iwn_softc *sc, int temp) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; #if 0 KASSERT(ic->ic_state == IEEE80211_S_RUN, ("not running")); #endif DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n", __func__, sc->temp, temp); /* adjust Tx power if need be (delta >= 3°C) */ if (abs(temp - sc->temp) < 3) return; sc->temp = temp; DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: set Tx power for channel %d\n", __func__, ieee80211_chan2ieee(ic, ic->ic_bsschan)); if (iwn_set_txpower(sc, ic->ic_bsschan, 1) != 0) { /* just warn, too bad for the automatic calibration... */ device_printf(sc->sc_dev, "%s: could not adjust Tx power\n", __func__); } } /* * Set Tx power for a given channel (each rate has its own power settings). * This function takes into account the regulatory information from EEPROM, * the current temperature and the current voltage. */ int iwn_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, int async) { /* fixed-point arithmetic division using a n-bit fractional part */ #define fdivround(a, b, n) \ ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) /* linear interpolation */ #define interpolate(x, x1, y1, x2, y2, n) \ ((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 }; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_ucode_info *uc = &sc->ucode_info; struct iwn_cmd_txpower cmd; struct iwn_eeprom_chan_samples *chans; const uint8_t *rf_gain, *dsp_gain; int32_t vdiff, tdiff; int i, c, grp, maxpwr; u_int chan; /* get channel number */ chan = ieee80211_chan2ieee(ic, ch); memset(&cmd, 0, sizeof cmd); cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1; cmd.chan = chan; if (IEEE80211_IS_CHAN_5GHZ(ch)) { maxpwr = sc->maxpwr5GHz; rf_gain = iwn_rf_gain_5ghz; dsp_gain = iwn_dsp_gain_5ghz; } else { maxpwr = sc->maxpwr2GHz; rf_gain = iwn_rf_gain_2ghz; dsp_gain = iwn_dsp_gain_2ghz; } /* compute voltage compensation */ vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7; if (vdiff > 0) vdiff *= 2; if (abs(vdiff) > 2) vdiff = 0; DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n", __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage); /* get channel's attenuation group */ if (chan <= 20) /* 1-20 */ grp = 4; else if (chan <= 43) /* 34-43 */ grp = 0; else if (chan <= 70) /* 44-70 */ grp = 1; else if (chan <= 124) /* 71-124 */ grp = 2; else /* 125-200 */ grp = 3; DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: chan %d, attenuation group=%d\n", __func__, chan, grp); /* get channel's sub-band */ for (i = 0; i < IWN_NBANDS; i++) if (sc->bands[i].lo != 0 && sc->bands[i].lo <= chan && chan <= sc->bands[i].hi) break; chans = sc->bands[i].chans; DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: chan %d sub-band=%d\n", __func__, chan, i); for (c = 0; c < IWN_NTXCHAINS; c++) { uint8_t power, gain, temp; int maxchpwr, pwr, ridx, idx; power = interpolate(chan, chans[0].num, chans[0].samples[c][1].power, chans[1].num, chans[1].samples[c][1].power, 1); gain = interpolate(chan, chans[0].num, chans[0].samples[c][1].gain, chans[1].num, chans[1].samples[c][1].gain, 1); temp = interpolate(chan, chans[0].num, chans[0].samples[c][1].temp, chans[1].num, chans[1].samples[c][1].temp, 1); DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: Tx chain %d: power=%d gain=%d temp=%d\n", __func__, c, power, gain, temp); /* compute temperature compensation */ tdiff = ((sc->temp - temp) * 2) / tdiv[grp]; DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n", __func__, tdiff, sc->temp, temp); for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) { maxchpwr = ch->ic_maxpower; if ((ridx / 8) & 1) { /* MIMO: decrease Tx power (-3dB) */ maxchpwr -= 6; } pwr = maxpwr - 10; /* decrease power for highest OFDM rates */ if ((ridx % 8) == 5) /* 48Mbit/s */ pwr -= 5; else if ((ridx % 8) == 6) /* 54Mbit/s */ pwr -= 7; else if ((ridx % 8) == 7) /* 60Mbit/s */ pwr -= 10; if (pwr > maxchpwr) pwr = maxchpwr; idx = gain - (pwr - power) - tdiff - vdiff; if ((ridx / 8) & 1) /* MIMO */ idx += (int32_t)le32toh(uc->atten[grp][c]); if (cmd.band == 0) idx += 9; /* 5GHz */ if (ridx == IWN_RIDX_MAX) idx += 5; /* CCK */ /* make sure idx stays in a valid range */ if (idx < 0) idx = 0; else if (idx > IWN_MAX_PWR_INDEX) idx = IWN_MAX_PWR_INDEX; DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: Tx chain %d, rate idx %d: power=%d\n", __func__, c, ridx, idx); cmd.power[ridx].rf_gain[c] = rf_gain[idx]; cmd.power[ridx].dsp_gain[c] = dsp_gain[idx]; } } DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: set tx power for chan %d\n", __func__, chan); return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async); #undef interpolate #undef fdivround } /* * Get the best (maximum) RSSI among the * connected antennas and convert to dBm. */ int8_t iwn_get_rssi(struct iwn_softc *sc, const struct iwn_rx_stat *stat) { int mask, agc, rssi; mask = (le16toh(stat->antenna) >> 4) & 0x7; agc = (le16toh(stat->agc) >> 7) & 0x7f; rssi = 0; #if 0 if (mask & (1 << 0)) /* Ant A */ rssi = max(rssi, stat->rssi[0]); if (mask & (1 << 1)) /* Ant B */ rssi = max(rssi, stat->rssi[2]); if (mask & (1 << 2)) /* Ant C */ rssi = max(rssi, stat->rssi[4]); #else rssi = max(rssi, stat->rssi[0]); rssi = max(rssi, stat->rssi[2]); rssi = max(rssi, stat->rssi[4]); #endif DPRINTF(sc, IWN_DEBUG_RECV, "%s: agc %d mask 0x%x rssi %d %d %d " "result %d\n", __func__, agc, mask, stat->rssi[0], stat->rssi[2], stat->rssi[4], rssi - agc - IWN_RSSI_TO_DBM); return rssi - agc - IWN_RSSI_TO_DBM; } /* * Get the average noise among Rx antennas (in dBm). */ int iwn_get_noise(const struct iwn_rx_general_stats *stats) { int i, total, nbant, noise; total = nbant = 0; for (i = 0; i < 3; i++) { noise = le32toh(stats->noise[i]) & 0xff; if (noise != 0) { total += noise; nbant++; } } /* there should be at least one antenna but check anyway */ return (nbant == 0) ? -127 : (total / nbant) - 107; } /* * Read temperature (in degC) from the on-board thermal sensor. */ int iwn_get_temperature(struct iwn_softc *sc) { struct iwn_ucode_info *uc = &sc->ucode_info; int32_t r1, r2, r3, r4, temp; r1 = le32toh(uc->temp[0].chan20MHz); r2 = le32toh(uc->temp[1].chan20MHz); r3 = le32toh(uc->temp[2].chan20MHz); r4 = le32toh(sc->rawtemp); if (r1 == r3) /* prevents division by 0 (should not happen) */ return 0; /* sign-extend 23-bit R4 value to 32-bit */ r4 = (r4 << 8) >> 8; /* compute temperature */ temp = (259 * (r4 - r2)) / (r3 - r1); temp = (temp * 97) / 100 + 8; return IWN_KTOC(temp); } /* * Initialize sensitivity calibration state machine. */ int iwn_init_sensitivity(struct iwn_softc *sc) { struct iwn_calib_state *calib = &sc->calib; struct iwn_phy_calib_cmd cmd; int error; /* reset calibration state */ memset(calib, 0, sizeof (*calib)); calib->state = IWN_CALIB_STATE_INIT; calib->cck_state = IWN_CCK_STATE_HIFA; /* initial values taken from the reference driver */ calib->corr_ofdm_x1 = 105; calib->corr_ofdm_mrc_x1 = 220; calib->corr_ofdm_x4 = 90; calib->corr_ofdm_mrc_x4 = 170; calib->corr_cck_x4 = 125; calib->corr_cck_mrc_x4 = 200; calib->energy_cck = 100; /* write initial sensitivity values */ error = iwn_send_sensitivity(sc); if (error != 0) return error; memset(&cmd, 0, sizeof cmd); cmd.code = IWN_SET_DIFF_GAIN; /* differential gains initially set to 0 for all 3 antennas */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: calibrate phy\n", __func__); return iwn_cmd(sc, IWN_PHY_CALIB, &cmd, sizeof cmd, 1); } /* * Collect noise and RSSI statistics for the first 20 beacons received * after association and use them to determine connected antennas and * set differential gains. */ void iwn_compute_differential_gain(struct iwn_softc *sc, const struct iwn_rx_general_stats *stats) { struct iwn_calib_state *calib = &sc->calib; struct iwn_phy_calib_cmd cmd; int i, val; /* accumulate RSSI and noise for all 3 antennas */ for (i = 0; i < 3; i++) { calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff; calib->noise[i] += le32toh(stats->noise[i]) & 0xff; } /* we update differential gain only once after 20 beacons */ if (++calib->nbeacons < 20) return; /* determine antenna with highest average RSSI */ val = max(calib->rssi[0], calib->rssi[1]); val = max(calib->rssi[2], val); /* determine which antennas are connected */ sc->antmsk = 0; for (i = 0; i < 3; i++) if (val - calib->rssi[i] <= 15 * 20) sc->antmsk |= 1 << i; /* if neither Ant A and Ant B are connected.. */ if ((sc->antmsk & (1 << 0 | 1 << 1)) == 0) sc->antmsk |= 1 << 1; /* ..mark Ant B as connected! */ /* get minimal noise among connected antennas */ val = INT_MAX; /* ok, there's at least one */ for (i = 0; i < 3; i++) if (sc->antmsk & (1 << i)) val = min(calib->noise[i], val); memset(&cmd, 0, sizeof cmd); cmd.code = IWN_SET_DIFF_GAIN; /* set differential gains for connected antennas */ for (i = 0; i < 3; i++) { if (sc->antmsk & (1 << i)) { cmd.gain[i] = (calib->noise[i] - val) / 30; /* limit differential gain to 3 */ cmd.gain[i] = min(cmd.gain[i], 3); cmd.gain[i] |= IWN_GAIN_SET; } } DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: set differential gains Ant A/B/C: %x/%x/%x (%x)\n", __func__,cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->antmsk); if (iwn_cmd(sc, IWN_PHY_CALIB, &cmd, sizeof cmd, 1) == 0) calib->state = IWN_CALIB_STATE_RUN; } /* * Tune RF Rx sensitivity based on the number of false alarms detected * during the last beacon period. */ void iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) { #define inc_clip(val, inc, max) \ if ((val) < (max)) { \ if ((val) < (max) - (inc)) \ (val) += (inc); \ else \ (val) = (max); \ needs_update = 1; \ } #define dec_clip(val, dec, min) \ if ((val) > (min)) { \ if ((val) > (min) + (dec)) \ (val) -= (dec); \ else \ (val) = (min); \ needs_update = 1; \ } struct iwn_calib_state *calib = &sc->calib; uint32_t val, rxena, fa; uint32_t energy[3], energy_min; uint8_t noise[3], noise_ref; int i, needs_update = 0; /* check that we've been enabled long enough */ if ((rxena = le32toh(stats->general.load)) == 0) return; /* compute number of false alarms since last call for OFDM */ fa = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm; fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm; fa *= 200 * 1024; /* 200TU */ /* save counters values for next call */ calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp); calib->fa_ofdm = le32toh(stats->ofdm.fa); if (fa > 50 * rxena) { /* high false alarm count, decrease sensitivity */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: OFDM high false alarm count: %u\n", __func__, fa); inc_clip(calib->corr_ofdm_x1, 1, 140); inc_clip(calib->corr_ofdm_mrc_x1, 1, 270); inc_clip(calib->corr_ofdm_x4, 1, 120); inc_clip(calib->corr_ofdm_mrc_x4, 1, 210); } else if (fa < 5 * rxena) { /* low false alarm count, increase sensitivity */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: OFDM low false alarm count: %u\n", __func__, fa); dec_clip(calib->corr_ofdm_x1, 1, 105); dec_clip(calib->corr_ofdm_mrc_x1, 1, 220); dec_clip(calib->corr_ofdm_x4, 1, 85); dec_clip(calib->corr_ofdm_mrc_x4, 1, 170); } /* compute maximum noise among 3 antennas */ for (i = 0; i < 3; i++) noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff; val = max(noise[0], noise[1]); val = max(noise[2], val); /* insert it into our samples table */ calib->noise_samples[calib->cur_noise_sample] = val; calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20; /* compute maximum noise among last 20 samples */ noise_ref = calib->noise_samples[0]; for (i = 1; i < 20; i++) noise_ref = max(noise_ref, calib->noise_samples[i]); /* compute maximum energy among 3 antennas */ for (i = 0; i < 3; i++) energy[i] = le32toh(stats->general.energy[i]); val = min(energy[0], energy[1]); val = min(energy[2], val); /* insert it into our samples table */ calib->energy_samples[calib->cur_energy_sample] = val; calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10; /* compute minimum energy among last 10 samples */ energy_min = calib->energy_samples[0]; for (i = 1; i < 10; i++) energy_min = max(energy_min, calib->energy_samples[i]); energy_min += 6; /* compute number of false alarms since last call for CCK */ fa = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck; fa += le32toh(stats->cck.fa) - calib->fa_cck; fa *= 200 * 1024; /* 200TU */ /* save counters values for next call */ calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp); calib->fa_cck = le32toh(stats->cck.fa); if (fa > 50 * rxena) { /* high false alarm count, decrease sensitivity */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: CCK high false alarm count: %u\n", __func__, fa); calib->cck_state = IWN_CCK_STATE_HIFA; calib->low_fa = 0; if (calib->corr_cck_x4 > 160) { calib->noise_ref = noise_ref; if (calib->energy_cck > 2) dec_clip(calib->energy_cck, 2, energy_min); } if (calib->corr_cck_x4 < 160) { calib->corr_cck_x4 = 161; needs_update = 1; } else inc_clip(calib->corr_cck_x4, 3, 200); inc_clip(calib->corr_cck_mrc_x4, 3, 400); } else if (fa < 5 * rxena) { /* low false alarm count, increase sensitivity */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: CCK low false alarm count: %u\n", __func__, fa); calib->cck_state = IWN_CCK_STATE_LOFA; calib->low_fa++; if (calib->cck_state != 0 && ((calib->noise_ref - noise_ref) > 2 || calib->low_fa > 100)) { inc_clip(calib->energy_cck, 2, 97); dec_clip(calib->corr_cck_x4, 3, 125); dec_clip(calib->corr_cck_mrc_x4, 3, 200); } } else { /* not worth to increase or decrease sensitivity */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: CCK normal false alarm count: %u\n", __func__, fa); calib->low_fa = 0; calib->noise_ref = noise_ref; if (calib->cck_state == IWN_CCK_STATE_HIFA) { /* previous interval had many false alarms */ dec_clip(calib->energy_cck, 8, energy_min); } calib->cck_state = IWN_CCK_STATE_INIT; } if (needs_update) (void)iwn_send_sensitivity(sc); #undef dec_clip #undef inc_clip } int iwn_send_sensitivity(struct iwn_softc *sc) { struct iwn_calib_state *calib = &sc->calib; struct iwn_sensitivity_cmd cmd; memset(&cmd, 0, sizeof cmd); cmd.which = IWN_SENSITIVITY_WORKTBL; /* OFDM modulation */ cmd.corr_ofdm_x1 = htole16(calib->corr_ofdm_x1); cmd.corr_ofdm_mrc_x1 = htole16(calib->corr_ofdm_mrc_x1); cmd.corr_ofdm_x4 = htole16(calib->corr_ofdm_x4); cmd.corr_ofdm_mrc_x4 = htole16(calib->corr_ofdm_mrc_x4); cmd.energy_ofdm = htole16(100); cmd.energy_ofdm_th = htole16(62); /* CCK modulation */ cmd.corr_cck_x4 = htole16(calib->corr_cck_x4); cmd.corr_cck_mrc_x4 = htole16(calib->corr_cck_mrc_x4); cmd.energy_cck = htole16(calib->energy_cck); /* Barker modulation: use default values */ cmd.corr_barker = htole16(190); cmd.corr_barker_mrc = htole16(390); DPRINTF(sc, IWN_DEBUG_RESET, "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__, calib->corr_ofdm_x1, calib->corr_ofdm_mrc_x1, calib->corr_ofdm_x4, calib->corr_ofdm_mrc_x4, calib->corr_cck_x4, calib->corr_cck_mrc_x4, calib->energy_cck); return iwn_cmd(sc, IWN_SENSITIVITY, &cmd, sizeof cmd, 1); } int -iwn_auth(struct iwn_softc *sc) +iwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); /*XXX*/ struct ieee80211_node *ni = vap->iv_bss; struct iwn_node_info node; int error; sc->calib.state = IWN_CALIB_STATE_INIT; /* update adapter's configuration */ sc->config.associd = 0; IEEE80211_ADDR_COPY(sc->config.bssid, ni->ni_bssid); sc->config.chan = htole16(ieee80211_chan2ieee(ic, ni->ni_chan)); sc->config.flags = htole32(IWN_CONFIG_TSF); if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) sc->config.flags |= htole32(IWN_CONFIG_AUTO | IWN_CONFIG_24GHZ); if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { sc->config.cck_mask = 0; sc->config.ofdm_mask = 0x15; } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { sc->config.cck_mask = 0x03; sc->config.ofdm_mask = 0; } else { /* XXX assume 802.11b/g */ sc->config.cck_mask = 0x0f; sc->config.ofdm_mask = 0x15; } if (ic->ic_flags & IEEE80211_F_SHSLOT) sc->config.flags |= htole32(IWN_CONFIG_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) sc->config.flags |= htole32(IWN_CONFIG_SHPREAMBLE); sc->config.filter &= ~htole32(IWN_FILTER_BSS); DPRINTF(sc, IWN_DEBUG_STATE, "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x " "ht_single 0x%x ht_dual 0x%x rxchain 0x%x " "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n", __func__, le16toh(sc->config.chan), sc->config.mode, le32toh(sc->config.flags), sc->config.cck_mask, sc->config.ofdm_mask, sc->config.ht_single_mask, sc->config.ht_dual_mask, le16toh(sc->config.rxchain), sc->config.myaddr, ":", sc->config.wlap, ":", sc->config.bssid, ":", le16toh(sc->config.associd), le32toh(sc->config.filter)); error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->config, sizeof (struct iwn_config), 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not configure, error %d\n", __func__, error); return error; } sc->sc_curchan = ic->ic_curchan; /* configuration has changed, set Tx power accordingly */ error = iwn_set_txpower(sc, ni->ni_chan, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set Tx power, error %d\n", __func__, error); return error; } /* * Reconfiguring clears the adapter's nodes table so we must * add the broadcast node again. */ memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr); node.id = IWN_ID_BROADCAST; DPRINTF(sc, IWN_DEBUG_STATE, "%s: add broadcast node\n", __func__); error = iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not add broadcast node, error %d\n", __func__, error); return error; } error = iwn_set_link_quality(sc, node.id, ic->ic_curchan, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not setup MRR for broadcast node, error %d\n", __func__, error); return error; } return 0; } /* * Configure the adapter for associated state. */ int -iwn_run(struct iwn_softc *sc) +iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) { #define MS(v,x) (((v) & x) >> x##_S) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); /*XXX*/ struct ieee80211_node *ni = vap->iv_bss; struct iwn_node_info node; int error, maxrxampdu, ampdudensity; sc->calib.state = IWN_CALIB_STATE_INIT; if (ic->ic_opmode == IEEE80211_M_MONITOR) { /* link LED blinks while monitoring */ iwn_set_led(sc, IWN_LED_LINK, 5, 5); return 0; } iwn_enable_tsf(sc, ni); /* update adapter's configuration */ sc->config.associd = htole16(IEEE80211_AID(ni->ni_associd)); /* short preamble/slot time are negotiated when associating */ sc->config.flags &= ~htole32(IWN_CONFIG_SHPREAMBLE | IWN_CONFIG_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHSLOT) sc->config.flags |= htole32(IWN_CONFIG_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) sc->config.flags |= htole32(IWN_CONFIG_SHPREAMBLE); if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { sc->config.flags &= ~htole32(IWN_CONFIG_HT); if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) sc->config.flags |= htole32(IWN_CONFIG_HT40U); else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) sc->config.flags |= htole32(IWN_CONFIG_HT40D); else sc->config.flags |= htole32(IWN_CONFIG_HT20); sc->config.rxchain = htole16( (3 << IWN_RXCHAIN_VALID_S) | (3 << IWN_RXCHAIN_MIMO_CNT_S) | (1 << IWN_RXCHAIN_CNT_S) | IWN_RXCHAIN_MIMO_FORCE); maxrxampdu = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); ampdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); } else maxrxampdu = ampdudensity = 0; sc->config.filter |= htole32(IWN_FILTER_BSS); DPRINTF(sc, IWN_DEBUG_STATE, "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x " "ht_single 0x%x ht_dual 0x%x rxchain 0x%x " "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n", __func__, le16toh(sc->config.chan), sc->config.mode, le32toh(sc->config.flags), sc->config.cck_mask, sc->config.ofdm_mask, sc->config.ht_single_mask, sc->config.ht_dual_mask, le16toh(sc->config.rxchain), sc->config.myaddr, ":", sc->config.wlap, ":", sc->config.bssid, ":", le16toh(sc->config.associd), le32toh(sc->config.filter)); error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->config, sizeof (struct iwn_config), 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not update configuration, error %d\n", __func__, error); return error; } sc->sc_curchan = ni->ni_chan; /* configuration has changed, set Tx power accordingly */ error = iwn_set_txpower(sc, ni->ni_chan, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set Tx power, error %d\n", __func__, error); return error; } /* add BSS node */ memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); node.id = IWN_ID_BSS; node.htflags = htole32( (maxrxampdu << IWN_MAXRXAMPDU_S) | (ampdudensity << IWN_MPDUDENSITY_S)); DPRINTF(sc, IWN_DEBUG_STATE, "%s: add BSS node, id %d htflags 0x%x\n", __func__, node.id, le32toh(node.htflags)); error = iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 1); if (error != 0) { device_printf(sc->sc_dev,"could not add BSS node\n"); return error; } error = iwn_set_link_quality(sc, node.id, ni->ni_chan, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not setup MRR for node %d, error %d\n", __func__, node.id, error); return error; } error = iwn_init_sensitivity(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set sensitivity, error %d\n", __func__, error); return error; } /* start/restart periodic calibration timer */ sc->calib.state = IWN_CALIB_STATE_ASSOC; iwn_calib_reset(sc); /* link LED always on while associated */ iwn_set_led(sc, IWN_LED_LINK, 0, 1); return 0; #undef MS } /* * Send a scan request to the firmware. Since this command is huge, we map it * into a mbuf instead of using the pre-allocated set of commands. */ int iwn_scan(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_scan_state *ss = ic->ic_scan; /*XXX*/ struct iwn_tx_ring *ring = &sc->txq[4]; struct iwn_tx_desc *desc; struct iwn_tx_data *data; struct iwn_tx_cmd *cmd; struct iwn_cmd_data *tx; struct iwn_scan_hdr *hdr; struct iwn_scan_essid *essid; struct iwn_scan_chan *chan; struct ieee80211_frame *wh; struct ieee80211_rateset *rs; struct ieee80211_channel *c; enum ieee80211_phymode mode; uint8_t *frm; int pktlen, error, nrates; bus_addr_t physaddr; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; /* XXX malloc */ data->m = m_getcl(M_DONTWAIT, MT_DATA, 0); if (data->m == NULL) { device_printf(sc->sc_dev, "%s: could not allocate mbuf for scan command\n", __func__); return ENOMEM; } cmd = mtod(data->m, struct iwn_tx_cmd *); cmd->code = IWN_CMD_SCAN; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; hdr = (struct iwn_scan_hdr *)cmd->data; memset(hdr, 0, sizeof (struct iwn_scan_hdr)); /* XXX use scan state */ /* * Move to the next channel if no packets are received within 5 msecs * after sending the probe request (this helps to reduce the duration * of active scans). */ hdr->quiet = htole16(5); /* timeout in milliseconds */ hdr->plcp_threshold = htole16(1); /* min # of packets */ /* select Ant B and Ant C for scanning */ hdr->rxchain = htole16(0x3e1 | (7 << IWN_RXCHAIN_VALID_S)); tx = (struct iwn_cmd_data *)(hdr + 1); memset(tx, 0, sizeof (struct iwn_cmd_data)); tx->flags = htole32(IWN_TX_AUTO_SEQ | 0x200); /* XXX */ tx->id = IWN_ID_BROADCAST; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); tx->rflags = IWN_RFLAG_ANT_B; if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) { hdr->crc_threshold = htole16(1); /* send probe requests at 6Mbps */ tx->rate = iwn_ridx_to_plcp[IWN_RATE_OFDM6]; } else { hdr->flags = htole32(IWN_CONFIG_24GHZ | IWN_CONFIG_AUTO); /* send probe requests at 1Mbps */ tx->rate = iwn_ridx_to_plcp[IWN_RATE_CCK1]; tx->rflags |= IWN_RFLAG_CCK; } essid = (struct iwn_scan_essid *)(tx + 1); memset(essid, 0, 4 * sizeof (struct iwn_scan_essid)); essid[0].id = IEEE80211_ELEMID_SSID; essid[0].len = ss->ss_ssid[0].len; memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); /* * Build a probe request frame. Most of the following code is a * copy & paste of what is done in net80211. */ wh = (struct ieee80211_frame *)&essid[4]; wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ; wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr); *(u_int16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ *(u_int16_t *)&wh->i_seq[0] = 0; /* filled by h/w */ frm = (uint8_t *)(wh + 1); /* add SSID IE */ *frm++ = IEEE80211_ELEMID_SSID; *frm++ = ss->ss_ssid[0].len; memcpy(frm, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); frm += ss->ss_ssid[0].len; mode = ieee80211_chan2mode(ic->ic_curchan); rs = &ic->ic_sup_rates[mode]; /* add supported rates IE */ *frm++ = IEEE80211_ELEMID_RATES; nrates = rs->rs_nrates; if (nrates > IEEE80211_RATE_SIZE) nrates = IEEE80211_RATE_SIZE; *frm++ = nrates; memcpy(frm, rs->rs_rates, nrates); frm += nrates; /* add supported xrates IE */ if (rs->rs_nrates > IEEE80211_RATE_SIZE) { nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; *frm++ = IEEE80211_ELEMID_XRATES; *frm++ = (uint8_t)nrates; memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); frm += nrates; } /* setup length of probe request */ tx->len = htole16(frm - (uint8_t *)wh); c = ic->ic_curchan; chan = (struct iwn_scan_chan *)frm; chan->chan = ieee80211_chan2ieee(ic, c); chan->flags = 0; if ((c->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) { chan->flags |= IWN_CHAN_ACTIVE; if (ss->ss_nssid > 0) chan->flags |= IWN_CHAN_DIRECT; } chan->dsp_gain = 0x6e; if (IEEE80211_IS_CHAN_5GHZ(c)) { chan->rf_gain = 0x3b; chan->active = htole16(10); chan->passive = htole16(110); } else { chan->rf_gain = 0x28; chan->active = htole16(20); chan->passive = htole16(120); } DPRINTF(sc, IWN_DEBUG_STATE, "%s: chan %u flags 0x%x rf_gain 0x%x " "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__, chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain, chan->active, chan->passive); hdr->nchan++; chan++; frm += sizeof (struct iwn_scan_chan); hdr->len = htole16(frm - (uint8_t *)hdr); pktlen = frm - (uint8_t *)cmd; error = bus_dmamap_load(ring->data_dmat, data->map, cmd, pktlen, iwn_dma_map_addr, &physaddr, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "%s: could not map scan command, error %d\n", __func__, error); m_freem(data->m); data->m = NULL; return error; } IWN_SET_DESC_NSEGS(desc, 1); IWN_SET_DESC_SEG(desc, 0, physaddr, pktlen); sc->shared->len[ring->qid][ring->cur] = htole16(8); if (ring->cur < IWN_TX_WINDOW) sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = htole16(8); bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); /* kick cmd ring */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); return 0; /* will be notified async. of failure/success */ } int iwn_config(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_power power; struct iwn_bluetooth bluetooth; struct iwn_node_info node; int error; /* set power mode */ memset(&power, 0, sizeof power); power.flags = htole16(IWN_POWER_CAM | 0x8); DPRINTF(sc, IWN_DEBUG_RESET, "%s: set power mode\n", __func__); error = iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &power, sizeof power, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set power mode, error %d\n", __func__, error); return error; } /* configure bluetooth coexistence */ memset(&bluetooth, 0, sizeof bluetooth); bluetooth.flags = 3; bluetooth.lead = 0xaa; bluetooth.kill = 1; DPRINTF(sc, IWN_DEBUG_RESET, "%s: config bluetooth coexistence\n", __func__); error = iwn_cmd(sc, IWN_CMD_BLUETOOTH, &bluetooth, sizeof bluetooth, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: could not configure bluetooth coexistence, error %d\n", __func__, error); return error; } /* configure adapter */ memset(&sc->config, 0, sizeof (struct iwn_config)); IEEE80211_ADDR_COPY(sc->config.myaddr, ic->ic_myaddr); IEEE80211_ADDR_COPY(sc->config.wlap, ic->ic_myaddr); /* set default channel */ sc->config.chan = htole16(ieee80211_chan2ieee(ic, ic->ic_curchan)); sc->config.flags = htole32(IWN_CONFIG_TSF); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) sc->config.flags |= htole32(IWN_CONFIG_AUTO | IWN_CONFIG_24GHZ); sc->config.filter = 0; switch (ic->ic_opmode) { case IEEE80211_M_STA: sc->config.mode = IWN_MODE_STA; sc->config.filter |= htole32(IWN_FILTER_MULTICAST); break; case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: sc->config.mode = IWN_MODE_IBSS; break; case IEEE80211_M_HOSTAP: sc->config.mode = IWN_MODE_HOSTAP; break; case IEEE80211_M_MONITOR: sc->config.mode = IWN_MODE_MONITOR; sc->config.filter |= htole32(IWN_FILTER_MULTICAST | IWN_FILTER_CTL | IWN_FILTER_PROMISC); break; default: break; } sc->config.cck_mask = 0x0f; /* not yet negotiated */ sc->config.ofdm_mask = 0xff; /* not yet negotiated */ sc->config.ht_single_mask = 0xff; sc->config.ht_dual_mask = 0xff; sc->config.rxchain = htole16(0x2800 | (7 << IWN_RXCHAIN_VALID_S)); DPRINTF(sc, IWN_DEBUG_STATE, "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x " "ht_single 0x%x ht_dual 0x%x rxchain 0x%x " "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n", __func__, le16toh(sc->config.chan), sc->config.mode, le32toh(sc->config.flags), sc->config.cck_mask, sc->config.ofdm_mask, sc->config.ht_single_mask, sc->config.ht_dual_mask, le16toh(sc->config.rxchain), sc->config.myaddr, ":", sc->config.wlap, ":", sc->config.bssid, ":", le16toh(sc->config.associd), le32toh(sc->config.filter)); error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->config, sizeof (struct iwn_config), 0); if (error != 0) { device_printf(sc->sc_dev, "%s: configure command failed, error %d\n", __func__, error); return error; } sc->sc_curchan = ic->ic_curchan; /* configuration has changed, set Tx power accordingly */ error = iwn_set_txpower(sc, ic->ic_curchan, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set Tx power, error %d\n", __func__, error); return error; } /* add broadcast node */ memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ic->ic_ifp->if_broadcastaddr); node.id = IWN_ID_BROADCAST; node.rate = iwn_plcp_signal(2); DPRINTF(sc, IWN_DEBUG_RESET, "%s: add broadcast node\n", __func__); error = iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: could not add broadcast node, error %d\n", __func__, error); return error; } error = iwn_set_link_quality(sc, node.id, ic->ic_curchan, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: could not setup MRR for node %d, error %d\n", __func__, node.id, error); return error; } error = iwn_set_critical_temp(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set critical temperature, error %d\n", __func__, error); return error; } return 0; } /* * Do post-alive initialization of the NIC (after firmware upload). */ void iwn_post_alive(struct iwn_softc *sc) { uint32_t base; uint16_t offset; int qid; iwn_mem_lock(sc); /* clear SRAM */ base = iwn_mem_read(sc, IWN_SRAM_BASE); for (offset = 0x380; offset < 0x520; offset += 4) { IWN_WRITE(sc, IWN_MEM_WADDR, base + offset); IWN_WRITE(sc, IWN_MEM_WDATA, 0); } /* shared area is aligned on a 1K boundary */ iwn_mem_write(sc, IWN_SRAM_BASE, sc->shared_dma.paddr >> 10); iwn_mem_write(sc, IWN_SELECT_QCHAIN, 0); for (qid = 0; qid < IWN_NTXQUEUES; qid++) { iwn_mem_write(sc, IWN_QUEUE_RIDX(qid), 0); IWN_WRITE(sc, IWN_TX_WIDX, qid << 8 | 0); /* set sched. window size */ IWN_WRITE(sc, IWN_MEM_WADDR, base + IWN_QUEUE_OFFSET(qid)); IWN_WRITE(sc, IWN_MEM_WDATA, 64); /* set sched. frame limit */ IWN_WRITE(sc, IWN_MEM_WADDR, base + IWN_QUEUE_OFFSET(qid) + 4); IWN_WRITE(sc, IWN_MEM_WDATA, 10 << 16); } /* enable interrupts for all 16 queues */ iwn_mem_write(sc, IWN_QUEUE_INTR_MASK, 0xffff); /* identify active Tx rings (0-7) */ iwn_mem_write(sc, IWN_TX_ACTIVE, 0xff); /* mark Tx rings (4 EDCA + cmd + 2 HCCA) as active */ for (qid = 0; qid < 7; qid++) { iwn_mem_write(sc, IWN_TXQ_STATUS(qid), IWN_TXQ_STATUS_ACTIVE | qid << 1); } iwn_mem_unlock(sc); } void iwn_stop_master(struct iwn_softc *sc) { uint32_t tmp; int ntries; tmp = IWN_READ(sc, IWN_RESET); IWN_WRITE(sc, IWN_RESET, tmp | IWN_STOP_MASTER); tmp = IWN_READ(sc, IWN_GPIO_CTL); if ((tmp & IWN_GPIO_PWR_STATUS) == IWN_GPIO_PWR_SLEEP) return; /* already asleep */ for (ntries = 0; ntries < 100; ntries++) { if (IWN_READ(sc, IWN_RESET) & IWN_MASTER_DISABLED) break; DELAY(10); } if (ntries == 100) device_printf(sc->sc_dev, "%s: timeout waiting for master\n", __func__); } int iwn_reset(struct iwn_softc *sc) { uint32_t tmp; int ntries; /* clear any pending interrupts */ IWN_WRITE(sc, IWN_INTR, 0xffffffff); tmp = IWN_READ(sc, IWN_CHICKEN); IWN_WRITE(sc, IWN_CHICKEN, tmp | IWN_CHICKEN_DISLOS); tmp = IWN_READ(sc, IWN_GPIO_CTL); IWN_WRITE(sc, IWN_GPIO_CTL, tmp | IWN_GPIO_INIT); /* wait for clock stabilization */ for (ntries = 0; ntries < 1000; ntries++) { if (IWN_READ(sc, IWN_GPIO_CTL) & IWN_GPIO_CLOCK) break; DELAY(10); } if (ntries == 1000) { device_printf(sc->sc_dev, "%s: timeout waiting for clock stabilization\n", __func__); return ETIMEDOUT; } return 0; } void iwn_hw_config(struct iwn_softc *sc) { uint32_t tmp, hw; /* enable interrupts mitigation */ IWN_WRITE(sc, IWN_INTR_MIT, 512 / 32); /* voodoo from the reference driver */ tmp = pci_read_config(sc->sc_dev, PCIR_REVID,1); if ((tmp & 0x80) && (tmp & 0x7f) < 8) { /* enable "no snoop" field */ tmp = pci_read_config(sc->sc_dev, 0xe8, 1); tmp &= ~IWN_DIS_NOSNOOP; /* clear device specific PCI configuration register 0x41 */ pci_write_config(sc->sc_dev, 0xe8, tmp, 1); } /* disable L1 entry to work around a hardware bug */ tmp = pci_read_config(sc->sc_dev, 0xf0, 1); tmp &= ~IWN_ENA_L1; pci_write_config(sc->sc_dev, 0xf0, tmp, 1 ); hw = IWN_READ(sc, IWN_HWCONFIG); IWN_WRITE(sc, IWN_HWCONFIG, hw | 0x310); iwn_mem_lock(sc); tmp = iwn_mem_read(sc, IWN_MEM_POWER); iwn_mem_write(sc, IWN_MEM_POWER, tmp | IWN_POWER_RESET); DELAY(5); tmp = iwn_mem_read(sc, IWN_MEM_POWER); iwn_mem_write(sc, IWN_MEM_POWER, tmp & ~IWN_POWER_RESET); iwn_mem_unlock(sc); } void iwn_init_locked(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; int error, qid; IWN_LOCK_ASSERT(sc); /* load the firmware */ if (sc->fw_fp == NULL && (error = iwn_load_firmware(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not load firmware, error %d\n", __func__, error); return; } error = iwn_reset(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not reset adapter, error %d\n", __func__, error); return; } iwn_mem_lock(sc); iwn_mem_read(sc, IWN_CLOCK_CTL); iwn_mem_write(sc, IWN_CLOCK_CTL, 0xa00); iwn_mem_read(sc, IWN_CLOCK_CTL); iwn_mem_unlock(sc); DELAY(20); iwn_mem_lock(sc); tmp = iwn_mem_read(sc, IWN_MEM_PCIDEV); iwn_mem_write(sc, IWN_MEM_PCIDEV, tmp | 0x800); iwn_mem_unlock(sc); iwn_mem_lock(sc); tmp = iwn_mem_read(sc, IWN_MEM_POWER); iwn_mem_write(sc, IWN_MEM_POWER, tmp & ~0x03000000); iwn_mem_unlock(sc); iwn_hw_config(sc); /* init Rx ring */ iwn_mem_lock(sc); IWN_WRITE(sc, IWN_RX_CONFIG, 0); IWN_WRITE(sc, IWN_RX_WIDX, 0); /* Rx ring is aligned on a 256-byte boundary */ IWN_WRITE(sc, IWN_RX_BASE, sc->rxq.desc_dma.paddr >> 8); /* shared area is aligned on a 16-byte boundary */ IWN_WRITE(sc, IWN_RW_WIDX_PTR, (sc->shared_dma.paddr + offsetof(struct iwn_shared, closed_count)) >> 4); IWN_WRITE(sc, IWN_RX_CONFIG, 0x80601000); iwn_mem_unlock(sc); IWN_WRITE(sc, IWN_RX_WIDX, (IWN_RX_RING_COUNT - 1) & ~7); iwn_mem_lock(sc); iwn_mem_write(sc, IWN_TX_ACTIVE, 0); /* set physical address of "keep warm" page */ IWN_WRITE(sc, IWN_KW_BASE, sc->kw_dma.paddr >> 4); /* init Tx rings */ for (qid = 0; qid < IWN_NTXQUEUES; qid++) { struct iwn_tx_ring *txq = &sc->txq[qid]; IWN_WRITE(sc, IWN_TX_BASE(qid), txq->desc_dma.paddr >> 8); IWN_WRITE(sc, IWN_TX_CONFIG(qid), 0x80000008); } iwn_mem_unlock(sc); /* clear "radio off" and "disable command" bits (reversed logic) */ IWN_WRITE(sc, IWN_UCODE_CLR, IWN_RADIO_OFF); IWN_WRITE(sc, IWN_UCODE_CLR, IWN_DISABLE_CMD); /* clear any pending interrupts */ IWN_WRITE(sc, IWN_INTR, 0xffffffff); /* enable interrupts */ IWN_WRITE(sc, IWN_MASK, IWN_INTR_MASK); /* not sure why/if this is necessary... */ IWN_WRITE(sc, IWN_UCODE_CLR, IWN_RADIO_OFF); IWN_WRITE(sc, IWN_UCODE_CLR, IWN_RADIO_OFF); /* check that the radio is not disabled by RF switch */ if (!(IWN_READ(sc, IWN_GPIO_CTL) & IWN_GPIO_RF_ENABLED)) { device_printf(sc->sc_dev, "radio is disabled by hardware switch\n"); return; } error = iwn_transfer_firmware(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not load firmware, error %d\n", __func__, error); return; } /* firmware has notified us that it is alive.. */ iwn_post_alive(sc); /* ..do post alive initialization */ sc->rawtemp = sc->ucode_info.temp[3].chan20MHz; sc->temp = iwn_get_temperature(sc); DPRINTF(sc, IWN_DEBUG_RESET, "%s: temperature=%d\n", __func__, sc->temp); error = iwn_config(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not configure device, error %d\n", __func__, error); return; } ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; } void iwn_init(void *arg) { struct iwn_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; IWN_LOCK(sc); iwn_init_locked(sc); IWN_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); } void iwn_stop_locked(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; int i; IWN_LOCK_ASSERT(sc); IWN_WRITE(sc, IWN_RESET, IWN_NEVO_RESET); sc->sc_tx_timer = 0; callout_stop(&sc->sc_timer_to); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); /* disable interrupts */ IWN_WRITE(sc, IWN_MASK, 0); IWN_WRITE(sc, IWN_INTR, 0xffffffff); IWN_WRITE(sc, IWN_INTR_STATUS, 0xffffffff); /* Clear any commands left in the taskq command buffer */ memset(sc->sc_cmd, 0, sizeof(sc->sc_cmd)); /* reset all Tx rings */ for (i = 0; i < IWN_NTXQUEUES; i++) iwn_reset_tx_ring(sc, &sc->txq[i]); /* reset Rx ring */ iwn_reset_rx_ring(sc, &sc->rxq); iwn_mem_lock(sc); iwn_mem_write(sc, IWN_MEM_CLOCK2, 0x200); iwn_mem_unlock(sc); DELAY(5); iwn_stop_master(sc); tmp = IWN_READ(sc, IWN_RESET); IWN_WRITE(sc, IWN_RESET, tmp | IWN_SW_RESET); } void iwn_stop(struct iwn_softc *sc) { IWN_LOCK(sc); iwn_stop_locked(sc); IWN_UNLOCK(sc); } /* * Callback from net80211 to start a scan. */ static void iwn_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; iwn_queue_cmd(sc, IWN_SCAN_START, 0, IWN_QUEUE_NORMAL); } /* * Callback from net80211 to terminate a scan. */ static void iwn_scan_end(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; iwn_queue_cmd(sc, IWN_SCAN_STOP, 0, IWN_QUEUE_NORMAL); } /* * Callback from net80211 to force a channel change. */ static void iwn_set_channel(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; const struct ieee80211_channel *c = ic->ic_curchan; if (c != sc->sc_curchan) { sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq); sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags); sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); iwn_queue_cmd(sc, IWN_SET_CHAN, 0, IWN_QUEUE_NORMAL); } } /* * Callback from net80211 to start scanning of the current channel. */ static void iwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { struct ieee80211vap *vap = ss->ss_vap; struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc; iwn_queue_cmd(sc, IWN_SCAN_CURCHAN, 0, IWN_QUEUE_NORMAL); } /* * Callback from net80211 to handle the minimum dwell time being met. * The intent is to terminate the scan but we just let the firmware * notify us when it's finished as we have no safe way to abort it. */ static void iwn_scan_mindwell(struct ieee80211_scan_state *ss) { /* NB: don't try to abort scan; wait for firmware to finish */ } /* * Carry out work in the taskq context. */ static void iwn_ops(void *arg0, int pending) { struct iwn_softc *sc = arg0; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap; int cmd, arg, error; - enum ieee80211_state nstate; for (;;) { IWN_CMD_LOCK(sc); cmd = sc->sc_cmd[sc->sc_cmd_cur]; if (cmd == 0) { /* No more commands to process */ IWN_CMD_UNLOCK(sc); return; } if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 && cmd != IWN_RADIO_ENABLE ) { IWN_CMD_UNLOCK(sc); return; } arg = sc->sc_cmd_arg[sc->sc_cmd_cur]; sc->sc_cmd[sc->sc_cmd_cur] = 0; /* free the slot */ sc->sc_cmd_cur = (sc->sc_cmd_cur + 1) % IWN_CMD_MAXOPS; IWN_CMD_UNLOCK(sc); IWN_LOCK(sc); /* NB: sync debug printfs on smp */ DPRINTF(sc, IWN_DEBUG_OPS, "%s: %s (cmd 0x%x)\n", __func__, iwn_ops_str(cmd), cmd); vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ switch (cmd) { case IWN_SCAN_START: /* make the link LED blink while we're scanning */ iwn_set_led(sc, IWN_LED_LINK, 20, 2); break; case IWN_SCAN_STOP: break; case IWN_SCAN_NEXT: ieee80211_scan_next(vap); break; case IWN_SCAN_CURCHAN: error = iwn_scan(sc); if (error != 0) { IWN_UNLOCK(sc); ieee80211_cancel_scan(vap); IWN_LOCK(sc); return; } break; case IWN_SET_CHAN: error = iwn_config(sc); if (error != 0) { DPRINTF(sc, IWN_DEBUG_STATE, "%s: set chan failed, cancel scan\n", __func__); IWN_UNLOCK(sc); //XXX Handle failed scan correctly ieee80211_cancel_scan(vap); return; } break; - case IWN_AUTH: - case IWN_RUN: - if (cmd == IWN_AUTH) { - error = iwn_auth(sc); - nstate = IEEE80211_S_AUTH; - } else { - error = iwn_run(sc); - nstate = IEEE80211_S_RUN; - } - if (error == 0) { - IWN_UNLOCK(sc); - IEEE80211_LOCK(ic); - IWN_VAP(vap)->iv_newstate(vap, nstate, arg); - if (vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, nstate, arg); - IEEE80211_UNLOCK(ic); - IWN_LOCK(sc); - } else { - device_printf(sc->sc_dev, - "%s: %s state change failed, error %d\n", - __func__, ieee80211_state_name[nstate], - error); - } - break; case IWN_REINIT: IWN_UNLOCK(sc); iwn_init(sc); IWN_LOCK(sc); ieee80211_notify_radio(ic, 1); break; case IWN_RADIO_ENABLE: KASSERT(sc->fw_fp != NULL, ("Fware Not Loaded, can't load from tq")); IWN_UNLOCK(sc); iwn_init(sc); IWN_LOCK(sc); break; case IWN_RADIO_DISABLE: ieee80211_notify_radio(ic, 0); iwn_stop_locked(sc); break; } IWN_UNLOCK(sc); } } /* * Queue a command for execution in the taskq thread. * This is needed as the net80211 callbacks do not allow * sleeping, since we need to sleep to confirm commands have * been processed by the firmware, we must defer execution to * a sleep enabled thread. */ static int iwn_queue_cmd(struct iwn_softc *sc, int cmd, int arg, int clear) { IWN_CMD_LOCK(sc); if (clear) { sc->sc_cmd[0] = cmd; sc->sc_cmd_arg[0] = arg; sc->sc_cmd_cur = 0; sc->sc_cmd_next = 1; } else { if (sc->sc_cmd[sc->sc_cmd_next] != 0) { IWN_CMD_UNLOCK(sc); DPRINTF(sc, IWN_DEBUG_ANY, "%s: command %d dropped\n", __func__, cmd); return EBUSY; } sc->sc_cmd[sc->sc_cmd_next] = cmd; sc->sc_cmd_arg[sc->sc_cmd_next] = arg; sc->sc_cmd_next = (sc->sc_cmd_next + 1) % IWN_CMD_MAXOPS; } taskqueue_enqueue(sc->sc_tq, &sc->sc_ops_task); IWN_CMD_UNLOCK(sc); return 0; } static void iwn_bpfattach(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap)); sc->sc_rxtap_len = sizeof sc->sc_rxtap; sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(IWN_RX_RADIOTAP_PRESENT); sc->sc_txtap_len = sizeof sc->sc_txtap; sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(IWN_TX_RADIOTAP_PRESENT); } static void iwn_sysctlattach(struct iwn_softc *sc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); #ifdef IWN_DEBUG sc->sc_debug = 0; SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs"); #endif } #ifdef IWN_DEBUG static const char * iwn_ops_str(int cmd) { switch (cmd) { case IWN_SCAN_START: return "SCAN_START"; case IWN_SCAN_CURCHAN: return "SCAN_CURCHAN"; case IWN_SCAN_STOP: return "SCAN_STOP"; case IWN_SET_CHAN: return "SET_CHAN"; - case IWN_AUTH: return "AUTH"; case IWN_SCAN_NEXT: return "SCAN_NEXT"; - case IWN_RUN: return "RUN"; case IWN_RADIO_ENABLE: return "RADIO_ENABLE"; case IWN_RADIO_DISABLE: return "RADIO_DISABLE"; case IWN_REINIT: return "REINIT"; } return "UNKNOWN COMMAND"; } static const char * iwn_intr_str(uint8_t cmd) { switch (cmd) { /* Notifications */ case IWN_UC_READY: return "UC_READY"; case IWN_ADD_NODE_DONE: return "ADD_NODE_DONE"; case IWN_TX_DONE: return "TX_DONE"; case IWN_START_SCAN: return "START_SCAN"; case IWN_STOP_SCAN: return "STOP_SCAN"; case IWN_RX_STATISTICS: return "RX_STATS"; case IWN_BEACON_STATISTICS: return "BEACON_STATS"; case IWN_STATE_CHANGED: return "STATE_CHANGED"; case IWN_BEACON_MISSED: return "BEACON_MISSED"; case IWN_AMPDU_RX_START: return "AMPDU_RX_START"; case IWN_AMPDU_RX_DONE: return "AMPDU_RX_DONE"; case IWN_RX_DONE: return "RX_DONE"; /* Command Notifications */ case IWN_CMD_CONFIGURE: return "IWN_CMD_CONFIGURE"; case IWN_CMD_ASSOCIATE: return "IWN_CMD_ASSOCIATE"; case IWN_CMD_EDCA_PARAMS: return "IWN_CMD_EDCA_PARAMS"; case IWN_CMD_TSF: return "IWN_CMD_TSF"; case IWN_CMD_TX_LINK_QUALITY: return "IWN_CMD_TX_LINK_QUALITY"; case IWN_CMD_SET_LED: return "IWN_CMD_SET_LED"; case IWN_CMD_SET_POWER_MODE: return "IWN_CMD_SET_POWER_MODE"; case IWN_CMD_SCAN: return "IWN_CMD_SCAN"; case IWN_CMD_TXPOWER: return "IWN_CMD_TXPOWER"; case IWN_CMD_BLUETOOTH: return "IWN_CMD_BLUETOOTH"; case IWN_CMD_SET_CRITICAL_TEMP: return "IWN_CMD_SET_CRITICAL_TEMP"; case IWN_SENSITIVITY: return "IWN_SENSITIVITY"; case IWN_PHY_CALIB: return "IWN_PHY_CALIB"; } return "UNKNOWN INTR NOTIF/CMD"; } #endif /* IWN_DEBUG */ static device_method_t iwn_methods[] = { /* Device interface */ DEVMETHOD(device_probe, iwn_probe), DEVMETHOD(device_attach, iwn_attach), DEVMETHOD(device_detach, iwn_detach), DEVMETHOD(device_shutdown, iwn_shutdown), DEVMETHOD(device_suspend, iwn_suspend), DEVMETHOD(device_resume, iwn_resume), { 0, 0 } }; static driver_t iwn_driver = { "iwn", iwn_methods, sizeof (struct iwn_softc) }; static devclass_t iwn_devclass; DRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, 0, 0); MODULE_DEPEND(iwn, pci, 1, 1, 1); MODULE_DEPEND(iwn, firmware, 1, 1, 1); MODULE_DEPEND(iwn, wlan, 1, 1, 1); MODULE_DEPEND(iwn, wlan_amrr, 1, 1, 1); Index: user/thompsa/vaptq/sys/dev/iwn/if_iwnvar.h =================================================================== --- user/thompsa/vaptq/sys/dev/iwn/if_iwnvar.h (revision 185732) +++ user/thompsa/vaptq/sys/dev/iwn/if_iwnvar.h (revision 185733) @@ -1,242 +1,240 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2007 * Damien Bergamini * Copyright (c) 2008 Sam Leffler, Errno Consulting * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ struct iwn_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint64_t wr_tsft; uint8_t wr_flags; uint8_t wr_rate; uint16_t wr_chan_freq; uint16_t wr_chan_flags; int8_t wr_dbm_antsignal; int8_t wr_dbm_antnoise; } __packed; #define IWN_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_TSFT) | \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)) struct iwn_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; uint8_t wt_rate; uint16_t wt_chan_freq; uint16_t wt_chan_flags; } __packed; #define IWN_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL)) struct iwn_dma_info { bus_dma_tag_t tag; bus_dmamap_t map; bus_dma_segment_t seg; bus_addr_t paddr; caddr_t vaddr; bus_size_t size; }; struct iwn_tx_data { bus_dmamap_t map; struct mbuf *m; struct ieee80211_node *ni; }; struct iwn_tx_ring { struct iwn_dma_info desc_dma; struct iwn_dma_info cmd_dma; struct iwn_tx_desc *desc; struct iwn_tx_cmd *cmd; struct iwn_tx_data data[IWN_TX_RING_COUNT]; bus_dma_tag_t data_dmat; int qid; int queued; int cur; }; struct iwn_rx_data { bus_dmamap_t map; struct mbuf *m; }; struct iwn_rx_ring { struct iwn_dma_info desc_dma; uint32_t *desc; struct iwn_rx_data data[IWN_RX_RING_COUNT]; bus_dma_tag_t data_dmat; int cur; }; struct iwn_node { struct ieee80211_node ni; /* must be the first */ struct ieee80211_amrr_node amn; }; #define IWN_NODE(_ni) ((struct iwn_node *)(_ni)) struct iwn_calib_state { uint8_t state; #define IWN_CALIB_STATE_INIT 0 #define IWN_CALIB_STATE_ASSOC 1 #define IWN_CALIB_STATE_RUN 2 u_int nbeacons; uint32_t noise[3]; uint32_t rssi[3]; uint32_t corr_ofdm_x1; uint32_t corr_ofdm_mrc_x1; uint32_t corr_ofdm_x4; uint32_t corr_ofdm_mrc_x4; uint32_t corr_cck_x4; uint32_t corr_cck_mrc_x4; uint32_t bad_plcp_ofdm; uint32_t fa_ofdm; uint32_t bad_plcp_cck; uint32_t fa_cck; uint32_t low_fa; uint8_t cck_state; #define IWN_CCK_STATE_INIT 0 #define IWN_CCK_STATE_LOFA 1 #define IWN_CCK_STATE_HIFA 2 uint8_t noise_samples[20]; u_int cur_noise_sample; uint8_t noise_ref; uint32_t energy_samples[10]; u_int cur_energy_sample; uint32_t energy_cck; }; struct iwn_vap { struct ieee80211vap iv_vap; struct ieee80211_amrr iv_amrr; struct callout iv_amrr_to; int (*iv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define IWN_VAP(_vap) ((struct iwn_vap *)(_vap)) struct iwn_softc { struct ifnet *sc_ifp; int sc_debug; struct callout sc_timer_to; /* calib+watchdog timer */ int sc_tx_timer; /* tx watchdog timer/counter */ const struct ieee80211_channel *sc_curchan; struct iwn_rx_radiotap_header sc_rxtap; int sc_rxtap_len; struct iwn_tx_radiotap_header sc_txtap; int sc_txtap_len; /* locks */ struct mtx sc_mtx; /* bus */ device_t sc_dev; int mem_rid; int irq_rid; struct resource *mem; struct resource *irq; /* shared area */ struct iwn_dma_info shared_dma; struct iwn_shared *shared; /* "keep warm" page */ struct iwn_dma_info kw_dma; /* firmware image */ const struct firmware *fw_fp; /* firmware DMA transfer */ struct iwn_dma_info fw_dma; /* rings */ struct iwn_tx_ring txq[IWN_NTXQUEUES]; struct iwn_rx_ring rxq; bus_space_tag_t sc_st; bus_space_handle_t sc_sh; void *sc_ih; bus_size_t sc_sz; /* command queue related variables */ #define IWN_SCAN_START (1<<0) #define IWN_SCAN_CURCHAN (1<<1) #define IWN_SCAN_STOP (1<<2) #define IWN_SET_CHAN (1<<3) -#define IWN_AUTH (1<<4) -#define IWN_SCAN_NEXT (1<<5) -#define IWN_RUN (1<<6) -#define IWN_RADIO_ENABLE (1<<7) -#define IWN_RADIO_DISABLE (1<<8) -#define IWN_REINIT (1<<9) +#define IWN_SCAN_NEXT (1<<4) +#define IWN_RADIO_ENABLE (1<<5) +#define IWN_RADIO_DISABLE (1<<6) +#define IWN_REINIT (1<<7) #define IWN_CMD_MAXOPS 10 /* command queuing request type */ #define IWN_QUEUE_NORMAL 0 #define IWN_QUEUE_CLEAR 1 int sc_cmd[IWN_CMD_MAXOPS]; int sc_cmd_arg[IWN_CMD_MAXOPS]; int sc_cmd_cur; /* current queued scan task */ int sc_cmd_next; /* last queued scan task */ struct mtx sc_cmdlock; /* Task queues used to control the driver */ struct taskqueue *sc_tq; /* Main command task queue */ /* Tasks used by the driver */ struct task sc_ops_task; /* deferred ops */ struct task sc_bmiss_task; /* beacon miss */ /* Thermal calibration */ int calib_cnt; struct iwn_calib_state calib; struct iwn_rx_stat last_rx_stat; int last_rx_valid; struct iwn_ucode_info ucode_info; struct iwn_config config; uint32_t rawtemp; int temp; int noise; uint8_t antmsk; struct iwn_eeprom_band bands[IWN_NBANDS]; int16_t eeprom_voltage; int8_t maxpwr2GHz; int8_t maxpwr5GHz; }; #define IWN_LOCK_INIT(_sc) \ mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ MTX_NETWORK_LOCK, MTX_DEF) #define IWN_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define IWN_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define IWN_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define IWN_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) #define IWN_CMD_LOCK_INIT(_sc) \ mtx_init(&(_sc)->sc_cmdlock, device_get_nameunit((_sc)->sc_dev), \ NULL, MTX_DEF); #define IWN_CMD_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_cmdlock) #define IWN_CMD_LOCK(_sc) mtx_lock(&(_sc)->sc_cmdlock) #define IWN_CMD_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_cmdlock) Index: user/thompsa/vaptq/sys/dev/usb/if_rum.c =================================================================== --- user/thompsa/vaptq/sys/dev/usb/if_rum.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/usb/if_rum.c (revision 185733) @@ -1,2559 +1,2528 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2005-2007 Damien Bergamini * Copyright (c) 2006 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /*- * Ralink Technology RT2501USB/RT2601USB chipset driver * http://www.ralinktech.com.tw/ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #ifdef USB_DEBUG #define DPRINTF(x) do { if (rumdebug > 0) printf x; } while (0) #define DPRINTFN(n, x) do { if (rumdebug >= (n)) printf x; } while (0) int rumdebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, rum, CTLFLAG_RW, 0, "USB rum"); SYSCTL_INT(_hw_usb_rum, OID_AUTO, debug, CTLFLAG_RW, &rumdebug, 0, "rum debug level"); #else #define DPRINTF(x) #define DPRINTFN(n, x) #endif /* various supported device vendors/products */ static const struct usb_devno rum_devs[] = { { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_HWU54DM }, { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_2 }, { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_3 }, { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_4 }, { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WUG2700 }, { USB_VENDOR_AMIT, USB_PRODUCT_AMIT_CGWLUSB2GO }, { USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_1 }, { USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_2 }, { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050A }, { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D9050V3 }, { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GC }, { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GR }, { USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU2 }, { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GL }, { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GPX }, { USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_CWD854F }, { USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_RT2573 }, { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWLG122C1 }, { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_WUA1340 }, { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA111 }, { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA110 }, { USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWB01GS }, { USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWI05GS }, { USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_RT2573 }, { USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_RT2573 }, { USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254LB }, { USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254V2AP }, { USB_VENDOR_HUAWEI3COM, USB_PRODUCT_HUAWEI3COM_WUB320G }, { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_G54HP }, { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_SG54HP }, { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_1 }, { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_2 }, { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_3 }, { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_4 }, { USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_RT2573 }, { USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54HP }, { USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54MINI2 }, { USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUSMM }, { USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573 }, { USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573_2 }, { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573 }, { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573_2 }, { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2671 }, { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113R2 }, { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL172 }, { USB_VENDOR_SPARKLAN, USB_PRODUCT_SPARKLAN_RT2573 }, { USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2573 } }; MODULE_DEPEND(rum, wlan, 1, 1, 1); MODULE_DEPEND(rum, wlan_amrr, 1, 1, 1); MODULE_DEPEND(rum, usb, 1, 1, 1); static struct ieee80211vap *rum_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void rum_vap_delete(struct ieee80211vap *); static int rum_alloc_tx_list(struct rum_softc *); static void rum_free_tx_list(struct rum_softc *); static int rum_alloc_rx_list(struct rum_softc *); static void rum_free_rx_list(struct rum_softc *); -static void rum_task(void *); static void rum_scantask(void *); static int rum_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void rum_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void rum_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void rum_setup_tx_desc(struct rum_softc *, struct rum_tx_desc *, uint32_t, uint16_t, int, int); static int rum_tx_mgt(struct rum_softc *, struct mbuf *, struct ieee80211_node *); static int rum_tx_raw(struct rum_softc *, struct mbuf *, struct ieee80211_node *, const struct ieee80211_bpf_params *); static int rum_tx_data(struct rum_softc *, struct mbuf *, struct ieee80211_node *); static void rum_start(struct ifnet *); static void rum_watchdog(void *); static int rum_ioctl(struct ifnet *, u_long, caddr_t); static void rum_eeprom_read(struct rum_softc *, uint16_t, void *, int); static uint32_t rum_read(struct rum_softc *, uint16_t); static void rum_read_multi(struct rum_softc *, uint16_t, void *, int); static void rum_write(struct rum_softc *, uint16_t, uint32_t); static void rum_write_multi(struct rum_softc *, uint16_t, void *, size_t); static void rum_bbp_write(struct rum_softc *, uint8_t, uint8_t); static uint8_t rum_bbp_read(struct rum_softc *, uint8_t); static void rum_rf_write(struct rum_softc *, uint8_t, uint32_t); static void rum_select_antenna(struct rum_softc *); static void rum_enable_mrr(struct rum_softc *); static void rum_set_txpreamble(struct rum_softc *); static void rum_set_basicrates(struct rum_softc *); static void rum_select_band(struct rum_softc *, struct ieee80211_channel *); static void rum_set_chan(struct rum_softc *, struct ieee80211_channel *); static void rum_enable_tsf_sync(struct rum_softc *); static void rum_update_slot(struct ifnet *); static void rum_set_bssid(struct rum_softc *, const uint8_t *); static void rum_set_macaddr(struct rum_softc *, const uint8_t *); static void rum_update_promisc(struct rum_softc *); static const char *rum_get_rf(int); static void rum_read_eeprom(struct rum_softc *); static int rum_bbp_init(struct rum_softc *); static void rum_init_locked(struct rum_softc *); static void rum_init(void *); static void rum_stop(void *); static int rum_load_microcode(struct rum_softc *, const u_char *, size_t); static int rum_prepare_beacon(struct rum_softc *, struct ieee80211vap *); static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static struct ieee80211_node *rum_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); static void rum_newassoc(struct ieee80211_node *, int); static void rum_scan_start(struct ieee80211com *); static void rum_scan_end(struct ieee80211com *); static void rum_set_channel(struct ieee80211com *); static int rum_get_rssi(struct rum_softc *, uint8_t); static void rum_amrr_start(struct rum_softc *, struct ieee80211_node *); static void rum_amrr_timeout(void *); static void rum_amrr_update(usbd_xfer_handle, usbd_private_handle, usbd_status); static const struct { uint32_t reg; uint32_t val; } rum_def_mac[] = { { RT2573_TXRX_CSR0, 0x025fb032 }, { RT2573_TXRX_CSR1, 0x9eaa9eaf }, { RT2573_TXRX_CSR2, 0x8a8b8c8d }, { RT2573_TXRX_CSR3, 0x00858687 }, { RT2573_TXRX_CSR7, 0x2e31353b }, { RT2573_TXRX_CSR8, 0x2a2a2a2c }, { RT2573_TXRX_CSR15, 0x0000000f }, { RT2573_MAC_CSR6, 0x00000fff }, { RT2573_MAC_CSR8, 0x016c030a }, { RT2573_MAC_CSR10, 0x00000718 }, { RT2573_MAC_CSR12, 0x00000004 }, { RT2573_MAC_CSR13, 0x00007f00 }, { RT2573_SEC_CSR0, 0x00000000 }, { RT2573_SEC_CSR1, 0x00000000 }, { RT2573_SEC_CSR5, 0x00000000 }, { RT2573_PHY_CSR1, 0x000023b0 }, { RT2573_PHY_CSR5, 0x00040a06 }, { RT2573_PHY_CSR6, 0x00080606 }, { RT2573_PHY_CSR7, 0x00000408 }, { RT2573_AIFSN_CSR, 0x00002273 }, { RT2573_CWMIN_CSR, 0x00002344 }, { RT2573_CWMAX_CSR, 0x000034aa } }; static const struct { uint8_t reg; uint8_t val; } rum_def_bbp[] = { { 3, 0x80 }, { 15, 0x30 }, { 17, 0x20 }, { 21, 0xc8 }, { 22, 0x38 }, { 23, 0x06 }, { 24, 0xfe }, { 25, 0x0a }, { 26, 0x0d }, { 32, 0x0b }, { 34, 0x12 }, { 37, 0x07 }, { 39, 0xf8 }, { 41, 0x60 }, { 53, 0x10 }, { 54, 0x18 }, { 60, 0x10 }, { 61, 0x04 }, { 62, 0x04 }, { 75, 0xfe }, { 86, 0xfe }, { 88, 0xfe }, { 90, 0x0f }, { 99, 0x00 }, { 102, 0x16 }, { 107, 0x04 } }; static const struct rfprog { uint8_t chan; uint32_t r1, r2, r3, r4; } rum_rf5226[] = { { 1, 0x00b03, 0x001e1, 0x1a014, 0x30282 }, { 2, 0x00b03, 0x001e1, 0x1a014, 0x30287 }, { 3, 0x00b03, 0x001e2, 0x1a014, 0x30282 }, { 4, 0x00b03, 0x001e2, 0x1a014, 0x30287 }, { 5, 0x00b03, 0x001e3, 0x1a014, 0x30282 }, { 6, 0x00b03, 0x001e3, 0x1a014, 0x30287 }, { 7, 0x00b03, 0x001e4, 0x1a014, 0x30282 }, { 8, 0x00b03, 0x001e4, 0x1a014, 0x30287 }, { 9, 0x00b03, 0x001e5, 0x1a014, 0x30282 }, { 10, 0x00b03, 0x001e5, 0x1a014, 0x30287 }, { 11, 0x00b03, 0x001e6, 0x1a014, 0x30282 }, { 12, 0x00b03, 0x001e6, 0x1a014, 0x30287 }, { 13, 0x00b03, 0x001e7, 0x1a014, 0x30282 }, { 14, 0x00b03, 0x001e8, 0x1a014, 0x30284 }, { 34, 0x00b03, 0x20266, 0x36014, 0x30282 }, { 38, 0x00b03, 0x20267, 0x36014, 0x30284 }, { 42, 0x00b03, 0x20268, 0x36014, 0x30286 }, { 46, 0x00b03, 0x20269, 0x36014, 0x30288 }, { 36, 0x00b03, 0x00266, 0x26014, 0x30288 }, { 40, 0x00b03, 0x00268, 0x26014, 0x30280 }, { 44, 0x00b03, 0x00269, 0x26014, 0x30282 }, { 48, 0x00b03, 0x0026a, 0x26014, 0x30284 }, { 52, 0x00b03, 0x0026b, 0x26014, 0x30286 }, { 56, 0x00b03, 0x0026c, 0x26014, 0x30288 }, { 60, 0x00b03, 0x0026e, 0x26014, 0x30280 }, { 64, 0x00b03, 0x0026f, 0x26014, 0x30282 }, { 100, 0x00b03, 0x0028a, 0x2e014, 0x30280 }, { 104, 0x00b03, 0x0028b, 0x2e014, 0x30282 }, { 108, 0x00b03, 0x0028c, 0x2e014, 0x30284 }, { 112, 0x00b03, 0x0028d, 0x2e014, 0x30286 }, { 116, 0x00b03, 0x0028e, 0x2e014, 0x30288 }, { 120, 0x00b03, 0x002a0, 0x2e014, 0x30280 }, { 124, 0x00b03, 0x002a1, 0x2e014, 0x30282 }, { 128, 0x00b03, 0x002a2, 0x2e014, 0x30284 }, { 132, 0x00b03, 0x002a3, 0x2e014, 0x30286 }, { 136, 0x00b03, 0x002a4, 0x2e014, 0x30288 }, { 140, 0x00b03, 0x002a6, 0x2e014, 0x30280 }, { 149, 0x00b03, 0x002a8, 0x2e014, 0x30287 }, { 153, 0x00b03, 0x002a9, 0x2e014, 0x30289 }, { 157, 0x00b03, 0x002ab, 0x2e014, 0x30281 }, { 161, 0x00b03, 0x002ac, 0x2e014, 0x30283 }, { 165, 0x00b03, 0x002ad, 0x2e014, 0x30285 } }, rum_rf5225[] = { { 1, 0x00b33, 0x011e1, 0x1a014, 0x30282 }, { 2, 0x00b33, 0x011e1, 0x1a014, 0x30287 }, { 3, 0x00b33, 0x011e2, 0x1a014, 0x30282 }, { 4, 0x00b33, 0x011e2, 0x1a014, 0x30287 }, { 5, 0x00b33, 0x011e3, 0x1a014, 0x30282 }, { 6, 0x00b33, 0x011e3, 0x1a014, 0x30287 }, { 7, 0x00b33, 0x011e4, 0x1a014, 0x30282 }, { 8, 0x00b33, 0x011e4, 0x1a014, 0x30287 }, { 9, 0x00b33, 0x011e5, 0x1a014, 0x30282 }, { 10, 0x00b33, 0x011e5, 0x1a014, 0x30287 }, { 11, 0x00b33, 0x011e6, 0x1a014, 0x30282 }, { 12, 0x00b33, 0x011e6, 0x1a014, 0x30287 }, { 13, 0x00b33, 0x011e7, 0x1a014, 0x30282 }, { 14, 0x00b33, 0x011e8, 0x1a014, 0x30284 }, { 34, 0x00b33, 0x01266, 0x26014, 0x30282 }, { 38, 0x00b33, 0x01267, 0x26014, 0x30284 }, { 42, 0x00b33, 0x01268, 0x26014, 0x30286 }, { 46, 0x00b33, 0x01269, 0x26014, 0x30288 }, { 36, 0x00b33, 0x01266, 0x26014, 0x30288 }, { 40, 0x00b33, 0x01268, 0x26014, 0x30280 }, { 44, 0x00b33, 0x01269, 0x26014, 0x30282 }, { 48, 0x00b33, 0x0126a, 0x26014, 0x30284 }, { 52, 0x00b33, 0x0126b, 0x26014, 0x30286 }, { 56, 0x00b33, 0x0126c, 0x26014, 0x30288 }, { 60, 0x00b33, 0x0126e, 0x26014, 0x30280 }, { 64, 0x00b33, 0x0126f, 0x26014, 0x30282 }, { 100, 0x00b33, 0x0128a, 0x2e014, 0x30280 }, { 104, 0x00b33, 0x0128b, 0x2e014, 0x30282 }, { 108, 0x00b33, 0x0128c, 0x2e014, 0x30284 }, { 112, 0x00b33, 0x0128d, 0x2e014, 0x30286 }, { 116, 0x00b33, 0x0128e, 0x2e014, 0x30288 }, { 120, 0x00b33, 0x012a0, 0x2e014, 0x30280 }, { 124, 0x00b33, 0x012a1, 0x2e014, 0x30282 }, { 128, 0x00b33, 0x012a2, 0x2e014, 0x30284 }, { 132, 0x00b33, 0x012a3, 0x2e014, 0x30286 }, { 136, 0x00b33, 0x012a4, 0x2e014, 0x30288 }, { 140, 0x00b33, 0x012a6, 0x2e014, 0x30280 }, { 149, 0x00b33, 0x012a8, 0x2e014, 0x30287 }, { 153, 0x00b33, 0x012a9, 0x2e014, 0x30289 }, { 157, 0x00b33, 0x012ab, 0x2e014, 0x30281 }, { 161, 0x00b33, 0x012ac, 0x2e014, 0x30283 }, { 165, 0x00b33, 0x012ad, 0x2e014, 0x30285 } }; static int rum_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->iface != NULL) return UMATCH_NONE; return (usb_lookup(rum_devs, uaa->vendor, uaa->product) != NULL) ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE; } static int rum_attach(device_t self) { struct rum_softc *sc = device_get_softc(self); struct usb_attach_arg *uaa = device_get_ivars(self); struct ieee80211com *ic; struct ifnet *ifp; const uint8_t *ucode = NULL; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; usbd_status error; int i, ntries, size; uint8_t bands; uint32_t tmp; sc->sc_udev = uaa->device; sc->sc_dev = self; if (usbd_set_config_no(sc->sc_udev, RT2573_CONFIG_NO, 0) != 0) { device_printf(self, "could not set configuration no\n"); return ENXIO; } /* get the first interface handle */ error = usbd_device2interface_handle(sc->sc_udev, RT2573_IFACE_INDEX, &sc->sc_iface); if (error != 0) { device_printf(self, "could not get interface handle\n"); return ENXIO; } /* * Find endpoints. */ id = usbd_get_interface_descriptor(sc->sc_iface); sc->sc_rx_no = sc->sc_tx_no = -1; for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); if (ed == NULL) { device_printf(self, "no endpoint descriptor for iface %d\n", i); return ENXIO; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) sc->sc_rx_no = ed->bEndpointAddress; else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) sc->sc_tx_no = ed->bEndpointAddress; } if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) { device_printf(self, "missing endpoint\n"); return ENXIO; } ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(self, "can not if_alloc()\n"); return ENXIO; } ic = ifp->if_l2com; mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); - usb_init_task(&sc->sc_task, rum_task, sc); usb_init_task(&sc->sc_scantask, rum_scantask, sc); callout_init(&sc->watchdog_ch, 0); /* retrieve RT2573 rev. no */ for (ntries = 0; ntries < 1000; ntries++) { if ((tmp = rum_read(sc, RT2573_MAC_CSR0)) != 0) break; DELAY(1000); } if (ntries == 1000) { device_printf(self, "timeout waiting for chip to settle\n"); goto bad; } /* retrieve MAC address and various other things from EEPROM */ rum_read_eeprom(sc); device_printf(self, "MAC/BBP RT2573 (rev 0x%05x), RF %s\n", tmp, rum_get_rf(sc->rf_rev)); ucode = rt2573_ucode; size = sizeof rt2573_ucode; error = rum_load_microcode(sc, ucode, size); if (error != 0) { device_printf(self, "could not load 8051 microcode\n"); goto bad; } ifp->if_softc = sc; if_initname(ifp, "rum", device_get_unit(sc->sc_dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_NEEDSGIANT; /* USB stack is still under Giant lock */ ifp->if_init = rum_init; ifp->if_ioctl = rum_ioctl; ifp->if_start = rum_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_HOSTAP /* HostAp mode supported */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* bg scanning supported */ | IEEE80211_C_WPA /* 802.11i */ ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226) setbit(&bands, IEEE80211_MODE_11A); ieee80211_init_channels(ic, NULL, &bands); ieee80211_ifattach(ic); ic->ic_newassoc = rum_newassoc; ic->ic_raw_xmit = rum_raw_xmit; ic->ic_node_alloc = rum_node_alloc; ic->ic_scan_start = rum_scan_start; ic->ic_scan_end = rum_scan_end; ic->ic_set_channel = rum_set_channel; ic->ic_vap_create = rum_vap_create; ic->ic_vap_delete = rum_vap_delete; sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap)); sc->sc_rxtap_len = sizeof sc->sc_rxtap; sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2573_RX_RADIOTAP_PRESENT); sc->sc_txtap_len = sizeof sc->sc_txtap; sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(RT2573_TX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); return 0; bad: mtx_destroy(&sc->sc_mtx); if_free(ifp); return ENXIO; } static int rum_detach(device_t self) { struct rum_softc *sc = device_get_softc(self); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; rum_stop(sc); bpfdetach(ifp); ieee80211_ifdetach(ic); - usb_rem_task(sc->sc_udev, &sc->sc_task); usb_rem_task(sc->sc_udev, &sc->sc_scantask); callout_stop(&sc->watchdog_ch); if (sc->amrr_xfer != NULL) { usbd_free_xfer(sc->amrr_xfer); sc->amrr_xfer = NULL; } if (sc->sc_rx_pipeh != NULL) { usbd_abort_pipe(sc->sc_rx_pipeh); usbd_close_pipe(sc->sc_rx_pipeh); } if (sc->sc_tx_pipeh != NULL) { usbd_abort_pipe(sc->sc_tx_pipeh); usbd_close_pipe(sc->sc_tx_pipeh); } rum_free_rx_list(sc); rum_free_tx_list(sc); if_free(ifp); mtx_destroy(&sc->sc_mtx); return 0; } static struct ieee80211vap * rum_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct rum_vap *rvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; rvp = (struct rum_vap *) malloc(sizeof(struct rum_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (rvp == NULL) return NULL; vap = &rvp->vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override state transition machine */ rvp->newstate = vap->iv_newstate; vap->iv_newstate = rum_newstate; callout_init(&rvp->amrr_ch, 0); ieee80211_amrr_init(&rvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 1000 /* 1 sec */); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return vap; } static void rum_vap_delete(struct ieee80211vap *vap) { struct rum_vap *rvp = RUM_VAP(vap); callout_stop(&rvp->amrr_ch); ieee80211_amrr_cleanup(&rvp->amrr); ieee80211_vap_detach(vap); free(rvp, M_80211_VAP); } static int rum_alloc_tx_list(struct rum_softc *sc) { struct rum_tx_data *data; int i, error; sc->tx_queued = sc->tx_cur = 0; for (i = 0; i < RUM_TX_LIST_COUNT; i++) { data = &sc->tx_data[i]; data->sc = sc; data->xfer = usbd_alloc_xfer(sc->sc_udev); if (data->xfer == NULL) { device_printf(sc->sc_dev, "could not allocate tx xfer\n"); error = ENOMEM; goto fail; } data->buf = usbd_alloc_buffer(data->xfer, RT2573_TX_DESC_SIZE + MCLBYTES); if (data->buf == NULL) { device_printf(sc->sc_dev, "could not allocate tx buffer\n"); error = ENOMEM; goto fail; } /* clean Tx descriptor */ bzero(data->buf, RT2573_TX_DESC_SIZE); } return 0; fail: rum_free_tx_list(sc); return error; } static void rum_free_tx_list(struct rum_softc *sc) { struct rum_tx_data *data; int i; for (i = 0; i < RUM_TX_LIST_COUNT; i++) { data = &sc->tx_data[i]; if (data->xfer != NULL) { usbd_free_xfer(data->xfer); data->xfer = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } } } static int rum_alloc_rx_list(struct rum_softc *sc) { struct rum_rx_data *data; int i, error; for (i = 0; i < RUM_RX_LIST_COUNT; i++) { data = &sc->rx_data[i]; data->sc = sc; data->xfer = usbd_alloc_xfer(sc->sc_udev); if (data->xfer == NULL) { device_printf(sc->sc_dev, "could not allocate rx xfer\n"); error = ENOMEM; goto fail; } if (usbd_alloc_buffer(data->xfer, MCLBYTES) == NULL) { device_printf(sc->sc_dev, "could not allocate rx buffer\n"); error = ENOMEM; goto fail; } data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (data->m == NULL) { device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); error = ENOMEM; goto fail; } data->buf = mtod(data->m, uint8_t *); } return 0; fail: rum_free_rx_list(sc); return error; } static void rum_free_rx_list(struct rum_softc *sc) { struct rum_rx_data *data; int i; for (i = 0; i < RUM_RX_LIST_COUNT; i++) { data = &sc->rx_data[i]; if (data->xfer != NULL) { usbd_free_xfer(data->xfer); data->xfer = NULL; } if (data->m != NULL) { m_freem(data->m); data->m = NULL; } } } -static void -rum_task(void *arg) +static int +rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { - struct rum_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct rum_vap *rvp = RUM_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct rum_softc *sc = ic->ic_ifp->if_softc; const struct ieee80211_txparam *tp; enum ieee80211_state ostate; struct ieee80211_node *ni; uint32_t tmp; + usb_rem_task(sc->sc_udev, &sc->sc_scantask); + callout_stop(&rvp->amrr_ch); ostate = vap->iv_state; + IEEE80211_UNLOCK(ic); RUM_LOCK(sc); - - switch (sc->sc_state) { + switch (nstate) { case IEEE80211_S_INIT: if (ostate == IEEE80211_S_RUN) { /* abort TSF synchronization */ tmp = rum_read(sc, RT2573_TXRX_CSR9); rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff); } break; case IEEE80211_S_RUN: ni = vap->iv_bss; if (vap->iv_opmode != IEEE80211_M_MONITOR) { rum_update_slot(ic->ic_ifp); rum_enable_mrr(sc); rum_set_txpreamble(sc); rum_set_basicrates(sc); rum_set_bssid(sc, ni->ni_bssid); } if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_IBSS) rum_prepare_beacon(sc, vap); if (vap->iv_opmode != IEEE80211_M_MONITOR) rum_enable_tsf_sync(sc); /* enable automatic rate adaptation */ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) rum_amrr_start(sc, ni); break; default: break; } - RUM_UNLOCK(sc); - IEEE80211_LOCK(ic); - rvp->newstate(vap, sc->sc_state, sc->sc_arg); - if (vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg); - IEEE80211_UNLOCK(ic); -} - -static int -rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - struct rum_vap *rvp = RUM_VAP(vap); - struct ieee80211com *ic = vap->iv_ic; - struct rum_softc *sc = ic->ic_ifp->if_softc; - - usb_rem_task(sc->sc_udev, &sc->sc_task); - usb_rem_task(sc->sc_udev, &sc->sc_scantask); - callout_stop(&rvp->amrr_ch); - - /* do it in a process context */ - sc->sc_state = nstate; - sc->sc_arg = arg; - - if (nstate == IEEE80211_S_INIT) { - rvp->newstate(vap, nstate, arg); - return 0; - } else { - usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER); - return EINPROGRESS; - } + rvp->newstate(vap, nstate, arg); + return 0; } static void rum_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct rum_tx_data *data = priv; struct rum_softc *sc = data->sc; struct ifnet *ifp = sc->sc_ifp; if (data->m != NULL && data->m->m_flags & M_TXCB) ieee80211_process_callback(data->ni, data->m, 0/*XXX*/); if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; device_printf(sc->sc_dev, "could not transmit buffer: %s\n", usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_tx_pipeh); ifp->if_oerrors++; return; } m_freem(data->m); data->m = NULL; ieee80211_free_node(data->ni); data->ni = NULL; sc->tx_queued--; ifp->if_opackets++; DPRINTFN(10, ("tx done\n")); sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; rum_start(ifp); } static void rum_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct rum_rx_data *data = priv; struct rum_softc *sc = data->sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct rum_rx_desc *desc; struct ieee80211_node *ni; struct mbuf *mnew, *m; int len, rssi; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh); goto skip; } usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL); if (len < RT2573_RX_DESC_SIZE + sizeof (struct ieee80211_frame_min)) { DPRINTF(("%s: xfer too short %d\n", device_get_nameunit(sc->sc_dev), len)); ifp->if_ierrors++; goto skip; } desc = (struct rum_rx_desc *)data->buf; if (le32toh(desc->flags) & RT2573_RX_CRC_ERROR) { /* * This should not happen since we did not request to receive * those frames when we filled RT2573_TXRX_CSR0. */ DPRINTFN(5, ("CRC error\n")); ifp->if_ierrors++; goto skip; } mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) { ifp->if_ierrors++; goto skip; } m = data->m; data->m = mnew; data->buf = mtod(data->m, uint8_t *); /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_data = (caddr_t)(desc + 1); m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff; rssi = rum_get_rssi(sc, desc->rssi); if (bpf_peers_present(ifp->if_bpf)) { struct rum_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = IEEE80211_RADIOTAP_F_FCS; tap->wr_rate = ieee80211_plcp2rate(desc->rate, (desc->flags & htole32(RT2573_RX_OFDM)) ? IEEE80211_T_OFDM : IEEE80211_T_CCK); tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_antenna = sc->rx_ant; tap->wr_antsignal = rssi; bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { /* Error happened during RSSI conversion. */ if (rssi < 0) rssi = -30; /* XXX ignored by net80211 */ (void) ieee80211_input(ni, m, rssi, RT2573_NOISE_FLOOR, 0); ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, rssi, RT2573_NOISE_FLOOR, 0); DPRINTFN(15, ("rx done\n")); skip: /* setup a new transfer */ usbd_setup_xfer(xfer, sc->sc_rx_pipeh, data, data->buf, MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rum_rxeof); usbd_transfer(xfer); } static uint8_t rum_plcp_signal(int rate) { switch (rate) { /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return 0xb; case 18: return 0xf; case 24: return 0xa; case 36: return 0xe; case 48: return 0x9; case 72: return 0xd; case 96: return 0x8; case 108: return 0xc; /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return 0x0; case 4: return 0x1; case 11: return 0x2; case 22: return 0x3; } return 0xff; /* XXX unsupported/unknown rate */ } static void rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc, uint32_t flags, uint16_t xflags, int len, int rate) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t plcp_length; int remainder; desc->flags = htole32(flags); desc->flags |= htole32(RT2573_TX_VALID); desc->flags |= htole32(len << 16); desc->xflags = htole16(xflags); desc->wme = htole16(RT2573_QID(0) | RT2573_AIFSN(2) | RT2573_LOGCWMIN(4) | RT2573_LOGCWMAX(10)); /* setup PLCP fields */ desc->plcp_signal = rum_plcp_signal(rate); desc->plcp_service = 4; len += IEEE80211_CRC_LEN; if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) { desc->flags |= htole32(RT2573_TX_OFDM); plcp_length = len & 0xfff; desc->plcp_length_hi = plcp_length >> 6; desc->plcp_length_lo = plcp_length & 0x3f; } else { plcp_length = (16 * len + rate - 1) / rate; if (rate == 22) { remainder = (16 * len) % 22; if (remainder != 0 && remainder < 7) desc->plcp_service |= RT2573_PLCP_LENGEXT; } desc->plcp_length_hi = plcp_length >> 8; desc->plcp_length_lo = plcp_length & 0xff; if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) desc->plcp_signal |= 0x08; } } #define RUM_TX_TIMEOUT 5000 static int rum_sendprot(struct rum_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) { struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_frame *wh; struct rum_tx_desc *desc; struct rum_tx_data *data; struct mbuf *mprot; int protrate, ackrate, pktlen, flags, isshort; uint16_t dur; usbd_status error; KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, ("protection %d", prot)); wh = mtod(m, const struct ieee80211_frame *); pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; protrate = ieee80211_ctl_rate(sc->sc_rates, rate); ackrate = ieee80211_ack_rate(sc->sc_rates, rate); isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort); + ieee80211_ack_duration(sc->sc_rates, rate, isshort); flags = RT2573_TX_MORE_FRAG; if (prot == IEEE80211_PROT_RTSCTS) { /* NB: CTS is the same size as an ACK */ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort); flags |= RT2573_TX_NEED_ACK; mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); } else { mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); } if (mprot == NULL) { /* XXX stat + msg */ return ENOBUFS; } data = &sc->tx_data[sc->tx_cur]; desc = (struct rum_tx_desc *)data->buf; data->m = mprot; data->ni = ieee80211_ref_node(ni); m_copydata(mprot, 0, mprot->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE); rum_setup_tx_desc(sc, desc, flags, 0, mprot->m_pkthdr.len, protrate); usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, /* NB: no roundup necessary */ RT2573_TX_DESC_SIZE + mprot->m_pkthdr.len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RUM_TX_TIMEOUT, rum_txeof); error = usbd_transfer(data->xfer); if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) { data->m = NULL; data->ni = NULL; return error; } sc->tx_queued++; sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT; return 0; } static int rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct rum_tx_desc *desc; struct rum_tx_data *data; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp; struct ieee80211_key *k; uint32_t flags = 0; uint16_t dur; usbd_status error; int xferlen; data = &sc->tx_data[sc->tx_cur]; data->m = m0; data->ni = ni; desc = (struct rum_tx_desc *)data->buf; wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } wh = mtod(m0, struct ieee80211_frame *); } tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RT2573_TX_NEED_ACK; dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); *(uint16_t *)wh->i_dur = htole16(dur); /* tell hardware to add timestamp for probe responses */ if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) flags |= RT2573_TX_TIMESTAMP; } if (bpf_peers_present(ifp->if_bpf)) { struct rum_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = tp->mgmtrate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wt_antenna = sc->tx_ant; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE); rum_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, tp->mgmtrate); /* align end on a 4-bytes boundary */ xferlen = (RT2573_TX_DESC_SIZE + m0->m_pkthdr.len + 3) & ~3; /* * No space left in the last URB to store the extra 4 bytes, force * sending of another URB. */ if ((xferlen % 64) == 0) xferlen += 4; DPRINTFN(10, ("sending mgt frame len=%d rate=%d xfer len=%d\n", m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, tp->mgmtrate, xferlen)); usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RUM_TX_TIMEOUT, rum_txeof); error = usbd_transfer(data->xfer); if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) { m_freem(m0); data->m = NULL; data->ni = NULL; return error; } sc->tx_queued++; sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT; return 0; } static int rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct rum_tx_desc *desc; struct rum_tx_data *data; uint32_t flags; usbd_status error; int xferlen, rate; KASSERT(params != NULL, ("no raw xmit params")); data = &sc->tx_data[sc->tx_cur]; desc = (struct rum_tx_desc *)data->buf; rate = params->ibp_rate0 & IEEE80211_RATE_VAL; /* XXX validate */ if (rate == 0) { m_freem(m0); return EINVAL; } flags = 0; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= RT2573_TX_NEED_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { error = rum_sendprot(sc, m0, ni, params->ibp_flags & IEEE80211_BPF_RTS ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); if (error) { m_freem(m0); return error; } flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; } if (bpf_peers_present(ifp->if_bpf)) { struct rum_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wt_antenna = sc->tx_ant; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } data->m = m0; data->ni = ni; m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE); /* XXX need to setup descriptor ourself */ rum_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, rate); /* align end on a 4-bytes boundary */ xferlen = (RT2573_TX_DESC_SIZE + m0->m_pkthdr.len + 3) & ~3; /* * No space left in the last URB to store the extra 4 bytes, force * sending of another URB. */ if ((xferlen % 64) == 0) xferlen += 4; DPRINTFN(10, ("sending raw frame len=%u rate=%u xfer len=%u\n", m0->m_pkthdr.len, rate, xferlen)); usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RUM_TX_TIMEOUT, rum_txeof); error = usbd_transfer(data->xfer); if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) return error; sc->tx_queued++; sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT; return 0; } static int rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct rum_tx_desc *desc; struct rum_tx_data *data; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp; struct ieee80211_key *k; uint32_t flags = 0; uint16_t dur; usbd_status error; int rate, xferlen; wh = mtod(m0, struct ieee80211_frame *); tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else { (void) ieee80211_amrr_choose(ni, &RUM_NODE(ni)->amn); rate = ni->ni_txrate; } if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { int prot = IEEE80211_PROT_NONE; if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) prot = IEEE80211_PROT_RTSCTS; else if ((ic->ic_flags & IEEE80211_F_USEPROT) && ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) prot = ic->ic_protmode; if (prot != IEEE80211_PROT_NONE) { error = rum_sendprot(sc, m0, ni, prot, rate); if (error) { m_freem(m0); return error; } flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; } } data = &sc->tx_data[sc->tx_cur]; desc = (struct rum_tx_desc *)data->buf; data->m = m0; data->ni = ni; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RT2573_TX_NEED_ACK; flags |= RT2573_TX_MORE_FRAG; dur = ieee80211_ack_duration(sc->sc_rates, rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); *(uint16_t *)wh->i_dur = htole16(dur); } if (bpf_peers_present(ifp->if_bpf)) { struct rum_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wt_antenna = sc->tx_ant; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE); rum_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, rate); /* align end on a 4-bytes boundary */ xferlen = (RT2573_TX_DESC_SIZE + m0->m_pkthdr.len + 3) & ~3; /* * No space left in the last URB to store the extra 4 bytes, force * sending of another URB. */ if ((xferlen % 64) == 0) xferlen += 4; DPRINTFN(10, ("sending frame len=%d rate=%d xfer len=%d\n", m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, rate, xferlen)); usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RUM_TX_TIMEOUT, rum_txeof); error = usbd_transfer(data->xfer); if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) { m_freem(m0); data->m = NULL; data->ni = NULL; return error; } sc->tx_queued++; sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT; return 0; } static void rum_start(struct ifnet *ifp) { struct rum_softc *sc = ifp->if_softc; struct ieee80211_node *ni; struct mbuf *m; for (;;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; if (sc->tx_queued >= RUM_TX_LIST_COUNT-1) { IFQ_DRV_PREPEND(&ifp->if_snd, m); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); continue; } if (rum_tx_data(sc, m, ni) != 0) { ieee80211_free_node(ni); ifp->if_oerrors++; break; } sc->sc_tx_timer = 5; callout_reset(&sc->watchdog_ch, hz, rum_watchdog, sc); } } static void rum_watchdog(void *arg) { struct rum_softc *sc = arg; RUM_LOCK(sc); if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "device timeout\n"); /*rum_init(ifp); XXX needs a process context! */ sc->sc_ifp->if_oerrors++; RUM_UNLOCK(sc); return; } callout_reset(&sc->watchdog_ch, hz, rum_watchdog, sc); } RUM_UNLOCK(sc); } static int rum_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct rum_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; switch (cmd) { case SIOCSIFFLAGS: RUM_LOCK(sc); if (ifp->if_flags & IFF_UP) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { rum_init(sc); startall = 1; } else rum_update_promisc(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) rum_stop(sc); } RUM_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return error; } static void rum_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, int len) { usb_device_request_t req; usbd_status error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2573_READ_EEPROM; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); error = usbd_do_request(sc->sc_udev, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not read EEPROM: %s\n", usbd_errstr(error)); } } static uint32_t rum_read(struct rum_softc *sc, uint16_t reg) { uint32_t val; rum_read_multi(sc, reg, &val, sizeof val); return le32toh(val); } static void rum_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, int len) { usb_device_request_t req; usbd_status error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2573_READ_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); error = usbd_do_request(sc->sc_udev, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not multi read MAC register: %s\n", usbd_errstr(error)); } } static void rum_write(struct rum_softc *sc, uint16_t reg, uint32_t val) { uint32_t tmp = htole32(val); rum_write_multi(sc, reg, &tmp, sizeof tmp); } static void rum_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, size_t len) { usb_device_request_t req; usbd_status error; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2573_WRITE_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); error = usbd_do_request(sc->sc_udev, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not multi write MAC register: %s\n", usbd_errstr(error)); } } static void rum_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val) { uint32_t tmp; int ntries; for (ntries = 0; ntries < 5; ntries++) { if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY)) break; } if (ntries == 5) { device_printf(sc->sc_dev, "could not write to BBP\n"); return; } tmp = RT2573_BBP_BUSY | (reg & 0x7f) << 8 | val; rum_write(sc, RT2573_PHY_CSR3, tmp); } static uint8_t rum_bbp_read(struct rum_softc *sc, uint8_t reg) { uint32_t val; int ntries; for (ntries = 0; ntries < 5; ntries++) { if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY)) break; } if (ntries == 5) { device_printf(sc->sc_dev, "could not read BBP\n"); return 0; } val = RT2573_BBP_BUSY | RT2573_BBP_READ | reg << 8; rum_write(sc, RT2573_PHY_CSR3, val); for (ntries = 0; ntries < 100; ntries++) { val = rum_read(sc, RT2573_PHY_CSR3); if (!(val & RT2573_BBP_BUSY)) return val & 0xff; DELAY(1); } device_printf(sc->sc_dev, "could not read BBP\n"); return 0; } static void rum_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val) { uint32_t tmp; int ntries; for (ntries = 0; ntries < 5; ntries++) { if (!(rum_read(sc, RT2573_PHY_CSR4) & RT2573_RF_BUSY)) break; } if (ntries == 5) { device_printf(sc->sc_dev, "could not write to RF\n"); return; } tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | (val & 0xfffff) << 2 | (reg & 3); rum_write(sc, RT2573_PHY_CSR4, tmp); /* remember last written value in sc */ sc->rf_regs[reg] = val; DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 3, val & 0xfffff)); } static void rum_select_antenna(struct rum_softc *sc) { uint8_t bbp4, bbp77; uint32_t tmp; bbp4 = rum_bbp_read(sc, 4); bbp77 = rum_bbp_read(sc, 77); /* TBD */ /* make sure Rx is disabled before switching antenna */ tmp = rum_read(sc, RT2573_TXRX_CSR0); rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); rum_bbp_write(sc, 4, bbp4); rum_bbp_write(sc, 77, bbp77); rum_write(sc, RT2573_TXRX_CSR0, tmp); } /* * Enable multi-rate retries for frames sent at OFDM rates. * In 802.11b/g mode, allow fallback to CCK rates. */ static void rum_enable_mrr(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t tmp; tmp = rum_read(sc, RT2573_TXRX_CSR4); tmp &= ~RT2573_MRR_CCK_FALLBACK; if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) tmp |= RT2573_MRR_CCK_FALLBACK; tmp |= RT2573_MRR_ENABLED; rum_write(sc, RT2573_TXRX_CSR4, tmp); } static void rum_set_txpreamble(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t tmp; tmp = rum_read(sc, RT2573_TXRX_CSR4); tmp &= ~RT2573_SHORT_PREAMBLE; if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) tmp |= RT2573_SHORT_PREAMBLE; rum_write(sc, RT2573_TXRX_CSR4, tmp); } static void rum_set_basicrates(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* update basic rate set */ if (ic->ic_curmode == IEEE80211_MODE_11B) { /* 11b basic rates: 1, 2Mbps */ rum_write(sc, RT2573_TXRX_CSR5, 0x3); } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) { /* 11a basic rates: 6, 12, 24Mbps */ rum_write(sc, RT2573_TXRX_CSR5, 0x150); } else { /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */ rum_write(sc, RT2573_TXRX_CSR5, 0xf); } } /* * Reprogram MAC/BBP to switch to a new band. Values taken from the reference * driver. */ static void rum_select_band(struct rum_softc *sc, struct ieee80211_channel *c) { uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104; uint32_t tmp; /* update all BBP registers that depend on the band */ bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c; bbp35 = 0x50; bbp97 = 0x48; bbp98 = 0x48; if (IEEE80211_IS_CHAN_5GHZ(c)) { bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c; bbp35 += 0x10; bbp97 += 0x10; bbp98 += 0x10; } if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) || (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) { bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10; } sc->bbp17 = bbp17; rum_bbp_write(sc, 17, bbp17); rum_bbp_write(sc, 96, bbp96); rum_bbp_write(sc, 104, bbp104); if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) || (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) { rum_bbp_write(sc, 75, 0x80); rum_bbp_write(sc, 86, 0x80); rum_bbp_write(sc, 88, 0x80); } rum_bbp_write(sc, 35, bbp35); rum_bbp_write(sc, 97, bbp97); rum_bbp_write(sc, 98, bbp98); tmp = rum_read(sc, RT2573_PHY_CSR0); tmp &= ~(RT2573_PA_PE_2GHZ | RT2573_PA_PE_5GHZ); if (IEEE80211_IS_CHAN_2GHZ(c)) tmp |= RT2573_PA_PE_2GHZ; else tmp |= RT2573_PA_PE_5GHZ; rum_write(sc, RT2573_PHY_CSR0, tmp); } static void rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; const struct rfprog *rfprog; uint8_t bbp3, bbp94 = RT2573_BBPR94_DEFAULT; int8_t power; u_int i, chan; chan = ieee80211_chan2ieee(ic, c); if (chan == 0 || chan == IEEE80211_CHAN_ANY) return; /* select the appropriate RF settings based on what EEPROM says */ rfprog = (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_2527) ? rum_rf5225 : rum_rf5226; /* find the settings for this channel (we know it exists) */ for (i = 0; rfprog[i].chan != chan; i++); power = sc->txpow[i]; if (power < 0) { bbp94 += power; power = 0; } else if (power > 31) { bbp94 += power - 31; power = 31; } /* * If we are switching from the 2GHz band to the 5GHz band or * vice-versa, BBP registers need to be reprogrammed. */ if (c->ic_flags != ic->ic_curchan->ic_flags) { rum_select_band(sc, c); rum_select_antenna(sc); } ic->ic_curchan = c; rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7); rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7 | 1); rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7); rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); DELAY(10); /* enable smart mode for MIMO-capable RFs */ bbp3 = rum_bbp_read(sc, 3); bbp3 &= ~RT2573_SMART_MODE; if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_2527) bbp3 |= RT2573_SMART_MODE; rum_bbp_write(sc, 3, bbp3); if (bbp94 != RT2573_BBPR94_DEFAULT) rum_bbp_write(sc, 94, bbp94); } /* * Enable TSF synchronization and tell h/w to start sending beacons for IBSS * and HostAP operating modes. */ static void rum_enable_tsf_sync(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t tmp; if (vap->iv_opmode != IEEE80211_M_STA) { /* * Change default 16ms TBTT adjustment to 8ms. * Must be done before enabling beacon generation. */ rum_write(sc, RT2573_TXRX_CSR10, 1 << 12 | 8); } tmp = rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000; /* set beacon interval (in 1/16ms unit) */ tmp |= vap->iv_bss->ni_intval * 16; tmp |= RT2573_TSF_TICKING | RT2573_ENABLE_TBTT; if (vap->iv_opmode == IEEE80211_M_STA) tmp |= RT2573_TSF_MODE(1); else tmp |= RT2573_TSF_MODE(2) | RT2573_GENERATE_BEACON; rum_write(sc, RT2573_TXRX_CSR9, tmp); } static void rum_update_slot(struct ifnet *ifp) { struct rum_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; uint8_t slottime; uint32_t tmp; slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; tmp = rum_read(sc, RT2573_MAC_CSR9); tmp = (tmp & ~0xff) | slottime; rum_write(sc, RT2573_MAC_CSR9, tmp); DPRINTF(("setting slot time to %uus\n", slottime)); } static void rum_set_bssid(struct rum_softc *sc, const uint8_t *bssid) { uint32_t tmp; tmp = bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24; rum_write(sc, RT2573_MAC_CSR4, tmp); tmp = bssid[4] | bssid[5] << 8 | RT2573_ONE_BSSID << 16; rum_write(sc, RT2573_MAC_CSR5, tmp); } static void rum_set_macaddr(struct rum_softc *sc, const uint8_t *addr) { uint32_t tmp; tmp = addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24; rum_write(sc, RT2573_MAC_CSR2, tmp); tmp = addr[4] | addr[5] << 8 | 0xff << 16; rum_write(sc, RT2573_MAC_CSR3, tmp); } static void rum_update_promisc(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; tmp = rum_read(sc, RT2573_TXRX_CSR0); tmp &= ~RT2573_DROP_NOT_TO_ME; if (!(ifp->if_flags & IFF_PROMISC)) tmp |= RT2573_DROP_NOT_TO_ME; rum_write(sc, RT2573_TXRX_CSR0, tmp); DPRINTF(("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ? "entering" : "leaving")); } static const char * rum_get_rf(int rev) { switch (rev) { case RT2573_RF_2527: return "RT2527 (MIMO XR)"; case RT2573_RF_2528: return "RT2528"; case RT2573_RF_5225: return "RT5225 (MIMO XR)"; case RT2573_RF_5226: return "RT5226"; default: return "unknown"; } } static void rum_read_eeprom(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t val; #ifdef RUM_DEBUG int i; #endif /* read MAC address */ rum_eeprom_read(sc, RT2573_EEPROM_ADDRESS, ic->ic_myaddr, 6); rum_eeprom_read(sc, RT2573_EEPROM_ANTENNA, &val, 2); val = le16toh(val); sc->rf_rev = (val >> 11) & 0x1f; sc->hw_radio = (val >> 10) & 0x1; sc->rx_ant = (val >> 4) & 0x3; sc->tx_ant = (val >> 2) & 0x3; sc->nb_ant = val & 0x3; DPRINTF(("RF revision=%d\n", sc->rf_rev)); rum_eeprom_read(sc, RT2573_EEPROM_CONFIG2, &val, 2); val = le16toh(val); sc->ext_5ghz_lna = (val >> 6) & 0x1; sc->ext_2ghz_lna = (val >> 4) & 0x1; DPRINTF(("External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n", sc->ext_2ghz_lna, sc->ext_5ghz_lna)); rum_eeprom_read(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET, &val, 2); val = le16toh(val); if ((val & 0xff) != 0xff) sc->rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */ /* Only [-10, 10] is valid */ if (sc->rssi_2ghz_corr < -10 || sc->rssi_2ghz_corr > 10) sc->rssi_2ghz_corr = 0; rum_eeprom_read(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET, &val, 2); val = le16toh(val); if ((val & 0xff) != 0xff) sc->rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */ /* Only [-10, 10] is valid */ if (sc->rssi_5ghz_corr < -10 || sc->rssi_5ghz_corr > 10) sc->rssi_5ghz_corr = 0; if (sc->ext_2ghz_lna) sc->rssi_2ghz_corr -= 14; if (sc->ext_5ghz_lna) sc->rssi_5ghz_corr -= 14; DPRINTF(("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n", sc->rssi_2ghz_corr, sc->rssi_5ghz_corr)); rum_eeprom_read(sc, RT2573_EEPROM_FREQ_OFFSET, &val, 2); val = le16toh(val); if ((val & 0xff) != 0xff) sc->rffreq = val & 0xff; DPRINTF(("RF freq=%d\n", sc->rffreq)); /* read Tx power for all a/b/g channels */ rum_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->txpow, 14); /* XXX default Tx power for 802.11a channels */ memset(sc->txpow + 14, 24, sizeof (sc->txpow) - 14); #ifdef RUM_DEBUG for (i = 0; i < 14; i++) DPRINTF(("Channel=%d Tx power=%d\n", i + 1, sc->txpow[i])); #endif /* read default values for BBP registers */ rum_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); #ifdef RUM_DEBUG for (i = 0; i < 14; i++) { if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff) continue; DPRINTF(("BBP R%d=%02x\n", sc->bbp_prom[i].reg, sc->bbp_prom[i].val)); } #endif } static int rum_bbp_init(struct rum_softc *sc) { #define N(a) (sizeof (a) / sizeof ((a)[0])) int i, ntries; /* wait for BBP to be ready */ for (ntries = 0; ntries < 100; ntries++) { const uint8_t val = rum_bbp_read(sc, 0); if (val != 0 && val != 0xff) break; DELAY(1000); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for BBP\n"); return EIO; } /* initialize BBP registers to default values */ for (i = 0; i < N(rum_def_bbp); i++) rum_bbp_write(sc, rum_def_bbp[i].reg, rum_def_bbp[i].val); /* write vendor-specific BBP values (from EEPROM) */ for (i = 0; i < 16; i++) { if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff) continue; rum_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); } return 0; #undef N } static void rum_init_locked(struct rum_softc *sc) { #define N(a) (sizeof (a) / sizeof ((a)[0])) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct rum_rx_data *data; uint32_t tmp; usbd_status error; int i, ntries; rum_stop(sc); /* initialize MAC registers to default values */ for (i = 0; i < N(rum_def_mac); i++) rum_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val); /* set host ready */ rum_write(sc, RT2573_MAC_CSR1, 3); rum_write(sc, RT2573_MAC_CSR1, 0); /* wait for BBP/RF to wakeup */ for (ntries = 0; ntries < 1000; ntries++) { if (rum_read(sc, RT2573_MAC_CSR12) & 8) break; rum_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */ DELAY(1000); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for BBP/RF to wakeup\n"); goto fail; } if ((error = rum_bbp_init(sc)) != 0) goto fail; /* select default channel */ rum_select_band(sc, ic->ic_curchan); rum_select_antenna(sc); rum_set_chan(sc, ic->ic_curchan); /* clear STA registers */ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta); IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); rum_set_macaddr(sc, ic->ic_myaddr); /* initialize ASIC */ rum_write(sc, RT2573_MAC_CSR1, 4); /* * Allocate xfer for AMRR statistics requests. */ sc->amrr_xfer = usbd_alloc_xfer(sc->sc_udev); if (sc->amrr_xfer == NULL) { device_printf(sc->sc_dev, "could not allocate AMRR xfer\n"); goto fail; } /* * Open Tx and Rx USB bulk pipes. */ error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE, &sc->sc_tx_pipeh); if (error != 0) { device_printf(sc->sc_dev, "could not open Tx pipe: %s\n", usbd_errstr(error)); goto fail; } error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE, &sc->sc_rx_pipeh); if (error != 0) { device_printf(sc->sc_dev, "could not open Rx pipe: %s\n", usbd_errstr(error)); goto fail; } /* * Allocate Tx and Rx xfer queues. */ error = rum_alloc_tx_list(sc); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Tx list\n"); goto fail; } error = rum_alloc_rx_list(sc); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Rx list\n"); goto fail; } /* * Start up the receive pipe. */ for (i = 0; i < RUM_RX_LIST_COUNT; i++) { data = &sc->rx_data[i]; usbd_setup_xfer(data->xfer, sc->sc_rx_pipeh, data, data->buf, MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rum_rxeof); usbd_transfer(data->xfer); } /* update Rx filter */ tmp = rum_read(sc, RT2573_TXRX_CSR0) & 0xffff; tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR; if (ic->ic_opmode != IEEE80211_M_MONITOR) { tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR | RT2573_DROP_ACKCTS; if (ic->ic_opmode != IEEE80211_M_HOSTAP) tmp |= RT2573_DROP_TODS; if (!(ifp->if_flags & IFF_PROMISC)) tmp |= RT2573_DROP_NOT_TO_ME; } rum_write(sc, RT2573_TXRX_CSR0, tmp); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; return; fail: rum_stop(sc); #undef N } static void rum_init(void *priv) { struct rum_softc *sc = priv; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; RUM_LOCK(sc); rum_init_locked(sc); RUM_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); /* start all vap's */ } static void rum_stop(void *priv) { struct rum_softc *sc = priv; struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); /* disable Rx */ tmp = rum_read(sc, RT2573_TXRX_CSR0); rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); /* reset ASIC */ rum_write(sc, RT2573_MAC_CSR1, 3); rum_write(sc, RT2573_MAC_CSR1, 0); if (sc->amrr_xfer != NULL) { usbd_free_xfer(sc->amrr_xfer); sc->amrr_xfer = NULL; } if (sc->sc_rx_pipeh != NULL) { usbd_abort_pipe(sc->sc_rx_pipeh); usbd_close_pipe(sc->sc_rx_pipeh); sc->sc_rx_pipeh = NULL; } if (sc->sc_tx_pipeh != NULL) { usbd_abort_pipe(sc->sc_tx_pipeh); usbd_close_pipe(sc->sc_tx_pipeh); sc->sc_tx_pipeh = NULL; } rum_free_rx_list(sc); rum_free_tx_list(sc); } static int rum_load_microcode(struct rum_softc *sc, const u_char *ucode, size_t size) { usb_device_request_t req; uint16_t reg = RT2573_MCU_CODE_BASE; usbd_status error; /* copy firmware image into NIC */ for (; size >= 4; reg += 4, ucode += 4, size -= 4) rum_write(sc, reg, UGETDW(ucode)); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2573_MCU_CNTL; USETW(req.wValue, RT2573_MCU_RUN); USETW(req.wIndex, 0); USETW(req.wLength, 0); error = usbd_do_request(sc->sc_udev, &req, NULL); if (error != 0) { device_printf(sc->sc_dev, "could not run firmware: %s\n", usbd_errstr(error)); } return error; } static int rum_prepare_beacon(struct rum_softc *sc, struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; const struct ieee80211_txparam *tp; struct rum_tx_desc desc; struct mbuf *m0; m0 = ieee80211_beacon_alloc(vap->iv_bss, &RUM_VAP(vap)->bo); if (m0 == NULL) { return ENOBUFS; } tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; rum_setup_tx_desc(sc, &desc, RT2573_TX_TIMESTAMP, RT2573_TX_HWSEQ, m0->m_pkthdr.len, tp->mgmtrate); /* copy the first 24 bytes of Tx descriptor into NIC memory */ rum_write_multi(sc, RT2573_HW_BEACON_BASE0, (uint8_t *)&desc, 24); /* copy beacon header and payload into NIC memory */ rum_write_multi(sc, RT2573_HW_BEACON_BASE0 + 24, mtod(m0, uint8_t *), m0->m_pkthdr.len); m_freem(m0); return 0; } static int rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ifnet *ifp = ni->ni_ic->ic_ifp; struct rum_softc *sc = ifp->if_softc; /* prevent management frames from being sent if we're not ready */ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { m_freem(m); ieee80211_free_node(ni); return ENETDOWN; } if (sc->tx_queued >= RUM_TX_LIST_COUNT-1) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; m_freem(m); ieee80211_free_node(ni); return EIO; } ifp->if_opackets++; if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ if (rum_tx_mgt(sc, m, ni) != 0) goto bad; } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ if (rum_tx_raw(sc, m, ni, params) != 0) goto bad; } sc->sc_tx_timer = 5; callout_reset(&sc->watchdog_ch, hz, rum_watchdog, sc); return 0; bad: ifp->if_oerrors++; ieee80211_free_node(ni); return EIO; } static void rum_amrr_start(struct rum_softc *sc, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct rum_vap *rvp = RUM_VAP(vap); /* clear statistic registers (STA_CSR0 to STA_CSR5) */ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta); ieee80211_amrr_node_init(&rvp->amrr, &RUM_NODE(ni)->amn, ni); callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, vap); } static void rum_amrr_timeout(void *arg) { struct ieee80211vap *vap = arg; struct rum_softc *sc = vap->iv_ic->ic_ifp->if_softc; usb_device_request_t req; /* * Asynchronously read statistic registers (cleared by read). */ req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2573_READ_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, RT2573_STA_CSR0); USETW(req.wLength, sizeof sc->sta); usbd_setup_default_xfer(sc->amrr_xfer, sc->sc_udev, vap, USBD_DEFAULT_TIMEOUT, &req, sc->sta, sizeof sc->sta, 0, rum_amrr_update); (void)usbd_transfer(sc->amrr_xfer); } static void rum_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct ieee80211vap *vap = priv; struct rum_vap *rvp = RUM_VAP(vap); struct ifnet *ifp = vap->iv_ic->ic_ifp; struct rum_softc *sc = ifp->if_softc; int ok, fail; if (status != USBD_NORMAL_COMPLETION) { device_printf(sc->sc_dev, "could not retrieve Tx statistics - " "cancelling automatic rate control\n"); return; } ok = (le32toh(sc->sta[4]) >> 16) + /* TX ok w/o retry */ (le32toh(sc->sta[5]) & 0xffff); /* TX ok w/ retry */ fail = (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */ ieee80211_amrr_tx_update(&RUM_NODE(vap->iv_bss)->amn, ok+fail, ok, (le32toh(sc->sta[5]) & 0xffff) + fail); ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */ callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, vap); } /* ARGUSED */ static struct ieee80211_node * rum_node_alloc(struct ieee80211vap *vap __unused, const uint8_t mac[IEEE80211_ADDR_LEN] __unused) { struct rum_node *rn; rn = malloc(sizeof(struct rum_node), M_80211_NODE, M_NOWAIT | M_ZERO); return rn != NULL ? &rn->ni : NULL; } static void rum_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni); } static void rum_scan_start(struct ieee80211com *ic) { struct rum_softc *sc = ic->ic_ifp->if_softc; usb_rem_task(sc->sc_udev, &sc->sc_scantask); /* do it in a process context */ sc->sc_scan_action = RUM_SCAN_START; usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); } static void rum_scan_end(struct ieee80211com *ic) { struct rum_softc *sc = ic->ic_ifp->if_softc; usb_rem_task(sc->sc_udev, &sc->sc_scantask); /* do it in a process context */ sc->sc_scan_action = RUM_SCAN_END; usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); } static void rum_set_channel(struct ieee80211com *ic) { struct rum_softc *sc = ic->ic_ifp->if_softc; usb_rem_task(sc->sc_udev, &sc->sc_scantask); /* do it in a process context */ sc->sc_scan_action = RUM_SET_CHANNEL; usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); } static void rum_scantask(void *arg) { struct rum_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t tmp; RUM_LOCK(sc); switch (sc->sc_scan_action) { case RUM_SCAN_START: /* abort TSF synchronization */ tmp = rum_read(sc, RT2573_TXRX_CSR9); rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff); rum_set_bssid(sc, ifp->if_broadcastaddr); break; case RUM_SCAN_END: rum_enable_tsf_sync(sc); /* XXX keep local copy */ rum_set_bssid(sc, vap->iv_bss->ni_bssid); break; case RUM_SET_CHANNEL: mtx_lock(&Giant); rum_set_chan(sc, ic->ic_curchan); mtx_unlock(&Giant); break; default: panic("unknown scan action %d\n", sc->sc_scan_action); /* NEVER REACHED */ break; } RUM_UNLOCK(sc); } static int rum_get_rssi(struct rum_softc *sc, uint8_t raw) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int lna, agc, rssi; lna = (raw >> 5) & 0x3; agc = raw & 0x1f; if (lna == 0) { /* * No RSSI mapping * * NB: Since RSSI is relative to noise floor, -1 is * adequate for caller to know error happened. */ return -1; } rssi = (2 * agc) - RT2573_NOISE_FLOOR; if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { rssi += sc->rssi_2ghz_corr; if (lna == 1) rssi -= 64; else if (lna == 2) rssi -= 74; else if (lna == 3) rssi -= 90; } else { rssi += sc->rssi_5ghz_corr; if (!sc->ext_5ghz_lna && lna != 1) rssi += 4; if (lna == 1) rssi -= 64; else if (lna == 2) rssi -= 86; else if (lna == 3) rssi -= 100; } return rssi; } static device_method_t rum_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rum_match), DEVMETHOD(device_attach, rum_attach), DEVMETHOD(device_detach, rum_detach), { 0, 0 } }; static driver_t rum_driver = { "rum", rum_methods, sizeof(struct rum_softc) }; static devclass_t rum_devclass; DRIVER_MODULE(rum, uhub, rum_driver, rum_devclass, usbd_driver_load, 0); Index: user/thompsa/vaptq/sys/dev/usb/if_rumvar.h =================================================================== --- user/thompsa/vaptq/sys/dev/usb/if_rumvar.h (revision 185732) +++ user/thompsa/vaptq/sys/dev/usb/if_rumvar.h (revision 185733) @@ -1,161 +1,157 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2005, 2006 Damien Bergamini * Copyright (c) 2006 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RUM_RX_LIST_COUNT 1 #define RUM_TX_LIST_COUNT 8 struct rum_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint8_t wr_flags; uint8_t wr_rate; uint16_t wr_chan_freq; uint16_t wr_chan_flags; uint8_t wr_antenna; uint8_t wr_antsignal; }; #define RT2573_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_ANTENNA) | \ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) struct rum_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; uint8_t wt_rate; uint16_t wt_chan_freq; uint16_t wt_chan_flags; uint8_t wt_antenna; }; #define RT2573_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_ANTENNA)) struct rum_softc; struct rum_tx_data { struct rum_softc *sc; usbd_xfer_handle xfer; uint8_t *buf; struct mbuf *m; struct ieee80211_node *ni; }; struct rum_rx_data { struct rum_softc *sc; usbd_xfer_handle xfer; uint8_t *buf; struct mbuf *m; }; struct rum_node { struct ieee80211_node ni; struct ieee80211_amrr_node amn; }; #define RUM_NODE(ni) ((struct rum_node *)(ni)) struct rum_vap { struct ieee80211vap vap; struct ieee80211_beacon_offsets bo; struct ieee80211_amrr amrr; struct callout amrr_ch; int (*newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define RUM_VAP(vap) ((struct rum_vap *)(vap)) struct rum_softc { struct ifnet *sc_ifp; const struct ieee80211_rate_table *sc_rates; device_t sc_dev; usbd_device_handle sc_udev; usbd_interface_handle sc_iface; int sc_rx_no; int sc_tx_no; uint8_t rf_rev; uint8_t rffreq; usbd_xfer_handle amrr_xfer; usbd_pipe_handle sc_rx_pipeh; usbd_pipe_handle sc_tx_pipeh; - enum ieee80211_state sc_state; - int sc_arg; - struct usb_task sc_task; - struct usb_task sc_scantask; int sc_scan_action; #define RUM_SCAN_START 0 #define RUM_SCAN_END 1 #define RUM_SET_CHANNEL 2 struct rum_rx_data rx_data[RUM_RX_LIST_COUNT]; struct rum_tx_data tx_data[RUM_TX_LIST_COUNT]; int tx_queued; int tx_cur; struct mtx sc_mtx; struct callout watchdog_ch; int sc_tx_timer; uint32_t sta[6]; uint32_t rf_regs[4]; uint8_t txpow[44]; struct { uint8_t val; uint8_t reg; } __packed bbp_prom[16]; int hw_radio; int rx_ant; int tx_ant; int nb_ant; int ext_2ghz_lna; int ext_5ghz_lna; int rssi_2ghz_corr; int rssi_5ghz_corr; uint8_t bbp17; struct rum_rx_radiotap_header sc_rxtap; int sc_rxtap_len; struct rum_tx_radiotap_header sc_txtap; int sc_txtap_len; }; #if 0 #define RUM_LOCK(sc) mtx_lock(&(sc)->sc_mtx) #define RUM_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) #else #define RUM_LOCK(sc) do { ((sc) = (sc)); mtx_lock(&Giant); } while (0) #define RUM_UNLOCK(sc) mtx_unlock(&Giant) #endif Index: user/thompsa/vaptq/sys/dev/usb/if_upgt.c =================================================================== --- user/thompsa/vaptq/sys/dev/usb/if_upgt.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/usb/if_upgt.c (revision 185733) @@ -1,2375 +1,2343 @@ /* $OpenBSD: if_upgt.c,v 1.35 2008/04/16 18:32:15 damien Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 2007 Marcus Glocker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include /* * Driver for the USB PrismGT devices. * * For now just USB 2.0 devices with the GW3887 chipset are supported. * The driver has been written based on the firmware version 2.13.1.0_LM87. * * TODO's: * - MONITOR mode test. * - Add HOSTAP mode. * - Add IBSS mode. * - Support the USB 1.0 devices (NET2280, ISL3880, ISL3886 chipsets). * * Parts of this driver has been influenced by reading the p54u driver * written by Jean-Baptiste Note and * Sebastien Bourdeauducq . */ SYSCTL_NODE(_hw, OID_AUTO, upgt, CTLFLAG_RD, 0, "USB PrismGT GW3887 driver parameters"); /* * NB: normally `upgt_txbuf' value can be increased to maximum 6, mininum 1. * However, we're using just 2 txbufs to protect packet losses in some cases * so the performance was sacrificed that with this value its speed is about * 2.1Mb/s. * * With setting txbuf value as 6, you can get full speed, 3.0Mb/s, of this * device but sometimes you'd meet some packet losses then retransmision. */ static int upgt_txbuf = UPGT_TX_COUNT; /* # tx buffers to allocate */ SYSCTL_INT(_hw_upgt, OID_AUTO, txbuf, CTLFLAG_RW, &upgt_txbuf, 0, "tx buffers allocated"); TUNABLE_INT("hw.upgt.txbuf", &upgt_txbuf); #ifdef UPGT_DEBUG int upgt_debug = 0; SYSCTL_INT(_hw_upgt, OID_AUTO, debug, CTLFLAG_RW, &upgt_debug, 0, "control debugging printfs"); TUNABLE_INT("hw.upgt.debug", &upgt_debug); enum { UPGT_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ UPGT_DEBUG_RECV = 0x00000002, /* basic recv operation */ UPGT_DEBUG_RESET = 0x00000004, /* reset processing */ UPGT_DEBUG_INTR = 0x00000008, /* INTR */ UPGT_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */ UPGT_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */ UPGT_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */ UPGT_DEBUG_STAT = 0x00000080, /* statistic */ UPGT_DEBUG_FW = 0x00000100, /* firmware */ UPGT_DEBUG_ANY = 0xffffffff }; #define DPRINTF(sc, m, fmt, ...) do { \ if (sc->sc_debug & (m)) \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(sc, m, fmt, ...) do { \ (void) sc; \ } while (0) #endif /* * Prototypes. */ static device_probe_t upgt_match; static device_attach_t upgt_attach; static device_detach_t upgt_detach; static int upgt_alloc_tx(struct upgt_softc *); static int upgt_alloc_rx(struct upgt_softc *); static int upgt_alloc_cmd(struct upgt_softc *); static int upgt_attach_hook(device_t); static int upgt_device_reset(struct upgt_softc *); static int upgt_bulk_xmit(struct upgt_softc *, struct upgt_data *, usbd_pipe_handle, uint32_t *, int); static int upgt_fw_verify(struct upgt_softc *); static int upgt_mem_init(struct upgt_softc *); static int upgt_fw_load(struct upgt_softc *); static int upgt_fw_copy(const uint8_t *, char *, int); static uint32_t upgt_crc32_le(const void *, size_t); static void upgt_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void upgt_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static int upgt_eeprom_read(struct upgt_softc *); static int upgt_eeprom_parse(struct upgt_softc *); static void upgt_eeprom_parse_hwrx(struct upgt_softc *, uint8_t *); static void upgt_eeprom_parse_freq3(struct upgt_softc *, uint8_t *, int); static void upgt_eeprom_parse_freq4(struct upgt_softc *, uint8_t *, int); static void upgt_eeprom_parse_freq6(struct upgt_softc *, uint8_t *, int); static uint32_t upgt_chksum_le(const uint32_t *, size_t); static void upgt_tx_done(struct upgt_softc *, uint8_t *); static void upgt_rx(struct upgt_softc *, uint8_t *, int); static void upgt_init(void *); static void upgt_init_locked(struct upgt_softc *); static int upgt_ioctl(struct ifnet *, u_long, caddr_t); static void upgt_start(struct ifnet *); static int upgt_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void upgt_scan_start(struct ieee80211com *); static void upgt_scan_end(struct ieee80211com *); static void upgt_set_channel(struct ieee80211com *); static struct ieee80211vap *upgt_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void upgt_vap_delete(struct ieee80211vap *); static void upgt_update_mcast(struct ifnet *); static uint8_t upgt_rx_rate(struct upgt_softc *, const int); static void upgt_set_multi(void *); static void upgt_stop(struct upgt_softc *, int); static void upgt_setup_rates(struct ieee80211vap *, struct ieee80211com *); static int upgt_set_macfilter(struct upgt_softc *, uint8_t); static int upgt_newstate(struct ieee80211vap *, enum ieee80211_state, int); -static void upgt_task(void *); static void upgt_scantask(void *); static void upgt_set_chan(struct upgt_softc *, struct ieee80211_channel *); -static void upgt_set_led(struct upgt_softc *, int); +static void upgt_set_led(struct upgt_softc *, int, enum ieee80211_state); static void upgt_set_led_blink(void *); static void upgt_tx_task(void *); static int upgt_get_stats(struct upgt_softc *); static void upgt_mem_free(struct upgt_softc *, uint32_t); static uint32_t upgt_mem_alloc(struct upgt_softc *); static void upgt_free_tx(struct upgt_softc *); static void upgt_free_rx(struct upgt_softc *); static void upgt_free_cmd(struct upgt_softc *); static void upgt_watchdog(void *); static const char *upgt_fwname = "upgt-gw3887"; static const struct usb_devno upgt_devs_2[] = { /* version 2 devices */ { USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_PRISM_GT }, { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050 }, { USB_VENDOR_CONCEPTRONIC, USB_PRODUCT_CONCEPTRONIC_PRISM_GT }, { USB_VENDOR_DELL, USB_PRODUCT_DELL_PRISM_GT_1 }, { USB_VENDOR_DELL, USB_PRODUCT_DELL_PRISM_GT_2 }, { USB_VENDOR_FSC, USB_PRODUCT_FSC_E5400 }, { USB_VENDOR_GLOBESPAN, USB_PRODUCT_GLOBESPAN_PRISM_GT_1 }, { USB_VENDOR_GLOBESPAN, USB_PRODUCT_GLOBESPAN_PRISM_GT_2 }, { USB_VENDOR_INTERSIL, USB_PRODUCT_INTERSIL_PRISM_GT }, { USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG }, { USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_UR045G }, { USB_VENDOR_XYRATEX, USB_PRODUCT_XYRATEX_PRISM_GT_1 }, { USB_VENDOR_XYRATEX, USB_PRODUCT_XYRATEX_PRISM_GT_2 }, { USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_XG703A } }; static int upgt_match(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (!uaa->iface) return UMATCH_NONE; if (usb_lookup(upgt_devs_2, uaa->vendor, uaa->product) != NULL) return (UMATCH_VENDOR_PRODUCT); return (UMATCH_NONE); } static int upgt_attach(device_t dev) { int i; struct upgt_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); usb_endpoint_descriptor_t *ed; usb_interface_descriptor_t *id; usbd_status error; sc->sc_dev = dev; sc->sc_udev = uaa->device; #ifdef UPGT_DEBUG sc->sc_debug = upgt_debug; #endif /* set configuration number */ if (usbd_set_config_no(sc->sc_udev, UPGT_CONFIG_NO, 0) != 0) { device_printf(dev, "could not set configuration no!\n"); return ENXIO; } /* get the first interface handle */ error = usbd_device2interface_handle(sc->sc_udev, UPGT_IFACE_INDEX, &sc->sc_iface); if (error != 0) { device_printf(dev, "could not get interface handle!\n"); return ENXIO; } /* find endpoints */ id = usbd_get_interface_descriptor(sc->sc_iface); sc->sc_rx_no = sc->sc_tx_no = -1; for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); if (ed == NULL) { device_printf(dev, "no endpoint descriptor for iface %d!\n", i); return ENXIO; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) sc->sc_tx_no = ed->bEndpointAddress; if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) sc->sc_rx_no = ed->bEndpointAddress; /* * 0x01 TX pipe * 0x81 RX pipe * * Deprecated scheme (not used with fw version >2.5.6.x): * 0x02 TX MGMT pipe * 0x82 TX MGMT pipe */ if (sc->sc_tx_no != -1 && sc->sc_rx_no != -1) break; } if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) { device_printf(dev, "missing endpoint!\n"); return ENXIO; } /* * Open TX and RX USB bulk pipes. */ error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE, &sc->sc_tx_pipeh); if (error != 0) { device_printf(dev, "could not open TX pipe: %s!\n", usbd_errstr(error)); goto fail; } error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE, &sc->sc_rx_pipeh); if (error != 0) { device_printf(dev, "could not open RX pipe: %s!\n", usbd_errstr(error)); goto fail; } /* Allocate TX, RX, and CMD xfers. */ if (upgt_alloc_tx(sc) != 0) goto fail; if (upgt_alloc_rx(sc) != 0) goto fail; if (upgt_alloc_cmd(sc) != 0) goto fail; /* We need the firmware loaded to complete the attach. */ return upgt_attach_hook(dev); fail: device_printf(dev, "%s failed!\n", __func__); return ENXIO; } static int upgt_attach_hook(device_t dev) { struct ieee80211com *ic; struct ifnet *ifp; struct upgt_softc *sc = device_get_softc(dev); struct upgt_data *data_rx = &sc->rx_data; uint8_t bands; usbd_status error; ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); return ENXIO; } mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); usb_init_task(&sc->sc_mcasttask, upgt_set_multi, sc); usb_init_task(&sc->sc_scantask, upgt_scantask, sc); - usb_init_task(&sc->sc_task, upgt_task, sc); usb_init_task(&sc->sc_task_tx, upgt_tx_task, sc); callout_init(&sc->sc_led_ch, 0); callout_init(&sc->sc_watchdog_ch, 0); /* Initialize the device. */ if (upgt_device_reset(sc) != 0) goto fail; /* Verify the firmware. */ if (upgt_fw_verify(sc) != 0) goto fail; /* Calculate device memory space. */ if (sc->sc_memaddr_frame_start == 0 || sc->sc_memaddr_frame_end == 0) { device_printf(dev, "could not find memory space addresses on FW!\n"); goto fail; } sc->sc_memaddr_frame_end -= UPGT_MEMSIZE_RX + 1; sc->sc_memaddr_rx_start = sc->sc_memaddr_frame_end + 1; DPRINTF(sc, UPGT_DEBUG_FW, "memory address frame start=0x%08x\n", sc->sc_memaddr_frame_start); DPRINTF(sc, UPGT_DEBUG_FW, "memory address frame end=0x%08x\n", sc->sc_memaddr_frame_end); DPRINTF(sc, UPGT_DEBUG_FW, "memory address rx start=0x%08x\n", sc->sc_memaddr_rx_start); upgt_mem_init(sc); /* Load the firmware. */ if (upgt_fw_load(sc) != 0) goto fail; /* Startup the RX pipe. */ usbd_setup_xfer(data_rx->xfer, sc->sc_rx_pipeh, data_rx, data_rx->buf, MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, upgt_rxeof); error = usbd_transfer(data_rx->xfer); if (error != 0 && error != USBD_IN_PROGRESS) { device_printf(dev, "could not queue RX transfer!\n"); goto fail; } usbd_delay_ms(sc->sc_udev, 100); /* Read the whole EEPROM content and parse it. */ if (upgt_eeprom_read(sc) != 0) goto fail; if (upgt_eeprom_parse(sc) != 0) goto fail; /* Setup the 802.11 device. */ ifp->if_softc = sc; if_initname(ifp, "upgt", device_get_unit(sc->sc_dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_NEEDSGIANT; /* USB stack is still under Giant lock */ ifp->if_init = upgt_init; ifp->if_ioctl = upgt_ioctl; ifp->if_start = upgt_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); IFQ_SET_READY(&ifp->if_snd); ic = ifp->if_l2com; ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* capable of bg scanning */ | IEEE80211_C_WPA /* 802.11i */ ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); ieee80211_init_channels(ic, NULL, &bands); ieee80211_ifattach(ic); ic->ic_raw_xmit = upgt_raw_xmit; ic->ic_scan_start = upgt_scan_start; ic->ic_scan_end = upgt_scan_end; ic->ic_set_channel = upgt_set_channel; ic->ic_vap_create = upgt_vap_create; ic->ic_vap_delete = upgt_vap_delete; ic->ic_update_mcast = upgt_update_mcast; bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap)); sc->sc_rxtap_len = sizeof(sc->sc_rxtap); sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(UPGT_RX_RADIOTAP_PRESENT); sc->sc_txtap_len = sizeof(sc->sc_txtap); sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(UPGT_TX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); return 0; fail: device_printf(dev, "%s failed!\n", __func__); mtx_destroy(&sc->sc_mtx); if_free(ifp); return ENXIO; } static void upgt_tx_task(void *arg) { struct upgt_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_frame *wh; struct ieee80211_key *k; struct upgt_data *data_tx; struct upgt_lmac_mem *mem; struct upgt_lmac_tx_desc *txdesc; struct mbuf *m; uint32_t addr; int len, i; usbd_status error; - upgt_set_led(sc, UPGT_LED_BLINK); + upgt_set_led(sc, UPGT_LED_BLINK, vap->iv_state); UPGT_LOCK(sc); for (i = 0; i < upgt_txbuf; i++) { data_tx = &sc->tx_data[i]; if (data_tx->m == NULL) continue; m = data_tx->m; addr = data_tx->addr + UPGT_MEMSIZE_FRAME_HEAD; /* * Software crypto. */ wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(data_tx->ni, m); if (k == NULL) { device_printf(sc->sc_dev, "ieee80211_crypto_encap returns NULL.\n"); goto done; } /* in case packet header moved, reset pointer */ wh = mtod(m, struct ieee80211_frame *); } /* * Transmit the URB containing the TX data. */ bzero(data_tx->buf, MCLBYTES); mem = (struct upgt_lmac_mem *)data_tx->buf; mem->addr = htole32(addr); txdesc = (struct upgt_lmac_tx_desc *)(mem + 1); if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT) { /* mgmt frames */ txdesc->header1.flags = UPGT_H1_FLAGS_TX_MGMT; /* always send mgmt frames at lowest rate (DS1) */ memset(txdesc->rates, 0x10, sizeof(txdesc->rates)); } else { /* data frames */ txdesc->header1.flags = UPGT_H1_FLAGS_TX_DATA; bcopy(sc->sc_cur_rateset, txdesc->rates, sizeof(txdesc->rates)); } txdesc->header1.type = UPGT_H1_TYPE_TX_DATA; txdesc->header1.len = htole16(m->m_pkthdr.len); txdesc->header2.reqid = htole32(data_tx->addr); txdesc->header2.type = htole16(UPGT_H2_TYPE_TX_ACK_YES); txdesc->header2.flags = htole16(UPGT_H2_FLAGS_TX_ACK_YES); txdesc->type = htole32(UPGT_TX_DESC_TYPE_DATA); txdesc->pad3[0] = UPGT_TX_DESC_PAD3_SIZE; if (bpf_peers_present(ifp->if_bpf)) { struct upgt_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = 0; /* XXX where to get from? */ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); } /* copy frame below our TX descriptor header */ m_copydata(m, 0, m->m_pkthdr.len, data_tx->buf + (sizeof(*mem) + sizeof(*txdesc))); /* calculate frame size */ len = sizeof(*mem) + sizeof(*txdesc) + m->m_pkthdr.len; /* we need to align the frame to a 4 byte boundary */ len = (len + 3) & ~3; /* calculate frame checksum */ mem->chksum = upgt_chksum_le((uint32_t *)txdesc, len - sizeof(*mem)); /* we do not need the mbuf anymore */ m_freem(m); data_tx->m = NULL; DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: TX start data sending\n", __func__); KASSERT(len <= MCLBYTES, ("mbuf is small for saving data")); usbd_setup_xfer(data_tx->xfer, sc->sc_tx_pipeh, data_tx, data_tx->buf, len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, UPGT_USB_TIMEOUT, upgt_txeof); UPGT_UNLOCK(sc); mtx_lock(&Giant); error = usbd_transfer(data_tx->xfer); mtx_unlock(&Giant); UPGT_LOCK(sc); if (error != 0 && error != USBD_IN_PROGRESS) { device_printf(sc->sc_dev, "could not transmit TX data URB!\n"); goto done; } DPRINTF(sc, UPGT_DEBUG_XMIT, "TX sent (%d bytes)\n", len); } done: UPGT_UNLOCK(sc); /* * If we don't regulary read the device statistics, the RX queue * will stall. It's strange, but it works, so we keep reading * the statistics here. *shrug* */ (void)upgt_get_stats(sc); } static void upgt_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct upgt_data *data_tx = priv; struct upgt_softc *sc = data_tx->sc; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; if (status == USBD_STALLED) { usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh); return; } device_printf(sc->sc_dev, "TX warning(%s)\n", usbd_errstr(status)); } } static int upgt_get_stats(struct upgt_softc *sc) { struct upgt_data *data_cmd = &sc->cmd_data; struct upgt_lmac_mem *mem; struct upgt_lmac_stats *stats; int len; /* * Transmit the URB containing the CMD data. */ bzero(data_cmd->buf, MCLBYTES); mem = (struct upgt_lmac_mem *)data_cmd->buf; mem->addr = htole32(sc->sc_memaddr_frame_start + UPGT_MEMSIZE_FRAME_HEAD); stats = (struct upgt_lmac_stats *)(mem + 1); stats->header1.flags = 0; stats->header1.type = UPGT_H1_TYPE_CTRL; stats->header1.len = htole16( sizeof(struct upgt_lmac_stats) - sizeof(struct upgt_lmac_header)); stats->header2.reqid = htole32(sc->sc_memaddr_frame_start); stats->header2.type = htole16(UPGT_H2_TYPE_STATS); stats->header2.flags = 0; len = sizeof(*mem) + sizeof(*stats); mem->chksum = upgt_chksum_le((uint32_t *)stats, len - sizeof(*mem)); if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) { device_printf(sc->sc_dev, "could not transmit statistics CMD data URB!\n"); return (EIO); } return (0); } static int upgt_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct upgt_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; switch (cmd) { case SIOCSIFFLAGS: mtx_lock(&Giant); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { if ((ifp->if_flags ^ sc->sc_if_flags) & (IFF_ALLMULTI | IFF_PROMISC)) upgt_set_multi(sc); } else { upgt_init(sc); startall = 1; } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) upgt_stop(sc, 1); } sc->sc_if_flags = ifp->if_flags; if (startall) ieee80211_start_all(ic); mtx_unlock(&Giant); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return error; } static void upgt_stop(struct upgt_softc *sc, int disable) { struct ifnet *ifp = sc->sc_ifp; /* abort and close TX / RX pipes */ if (sc->sc_tx_pipeh != NULL) usbd_abort_pipe(sc->sc_tx_pipeh); if (sc->sc_rx_pipeh != NULL) usbd_abort_pipe(sc->sc_rx_pipeh); /* device down */ sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } static void -upgt_task(void *arg) +upgt_set_led(struct upgt_softc *sc, int action, enum ieee80211_state nstate) { - struct upgt_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - struct upgt_vap *uvp = UPGT_VAP(vap); - - DPRINTF(sc, UPGT_DEBUG_STATE, "%s: %s -> %s\n", __func__, - ieee80211_state_name[vap->iv_state], - ieee80211_state_name[sc->sc_state]); - - switch (sc->sc_state) { - case IEEE80211_S_INIT: - /* do not accept any frames if the device is down */ - UPGT_LOCK(sc); - upgt_set_macfilter(sc, sc->sc_state); - UPGT_UNLOCK(sc); - upgt_set_led(sc, UPGT_LED_OFF); - break; - case IEEE80211_S_SCAN: - upgt_set_chan(sc, ic->ic_curchan); - break; - case IEEE80211_S_AUTH: - upgt_set_chan(sc, ic->ic_curchan); - break; - case IEEE80211_S_ASSOC: - break; - case IEEE80211_S_RUN: - UPGT_LOCK(sc); - upgt_set_macfilter(sc, sc->sc_state); - UPGT_UNLOCK(sc); - upgt_set_led(sc, UPGT_LED_ON); - break; - default: - break; - } - - IEEE80211_LOCK(ic); - uvp->newstate(vap, sc->sc_state, sc->sc_arg); - if (vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg); - IEEE80211_UNLOCK(ic); -} - -static void -upgt_set_led(struct upgt_softc *sc, int action) -{ struct upgt_data *data_cmd = &sc->cmd_data; struct upgt_lmac_mem *mem; struct upgt_lmac_led *led; int len; /* * Transmit the URB containing the CMD data. */ bzero(data_cmd->buf, MCLBYTES); mem = (struct upgt_lmac_mem *)data_cmd->buf; mem->addr = htole32(sc->sc_memaddr_frame_start + UPGT_MEMSIZE_FRAME_HEAD); led = (struct upgt_lmac_led *)(mem + 1); led->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK; led->header1.type = UPGT_H1_TYPE_CTRL; led->header1.len = htole16( sizeof(struct upgt_lmac_led) - sizeof(struct upgt_lmac_header)); led->header2.reqid = htole32(sc->sc_memaddr_frame_start); led->header2.type = htole16(UPGT_H2_TYPE_LED); led->header2.flags = 0; switch (action) { case UPGT_LED_OFF: led->mode = htole16(UPGT_LED_MODE_SET); led->action_fix = 0; led->action_tmp = htole16(UPGT_LED_ACTION_OFF); led->action_tmp_dur = 0; break; case UPGT_LED_ON: led->mode = htole16(UPGT_LED_MODE_SET); led->action_fix = 0; led->action_tmp = htole16(UPGT_LED_ACTION_ON); led->action_tmp_dur = 0; break; case UPGT_LED_BLINK: - if (sc->sc_state != IEEE80211_S_RUN) + if (nstate != IEEE80211_S_RUN) return; if (sc->sc_led_blink) /* previous blink was not finished */ return; led->mode = htole16(UPGT_LED_MODE_SET); led->action_fix = htole16(UPGT_LED_ACTION_OFF); led->action_tmp = htole16(UPGT_LED_ACTION_ON); led->action_tmp_dur = htole16(UPGT_LED_ACTION_TMP_DUR); /* lock blink */ sc->sc_led_blink = 1; callout_reset(&sc->sc_led_ch, hz, upgt_set_led_blink, sc); break; default: return; } len = sizeof(*mem) + sizeof(*led); mem->chksum = upgt_chksum_le((uint32_t *)led, len - sizeof(*mem)); if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) device_printf(sc->sc_dev, "could not transmit led CMD URB!\n"); } static void upgt_set_led_blink(void *arg) { struct upgt_softc *sc = arg; /* blink finished, we are ready for a next one */ sc->sc_led_blink = 0; } static void upgt_init(void *priv) { struct upgt_softc *sc = priv; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; UPGT_LOCK(sc); upgt_init_locked(sc); UPGT_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); /* start all vap's */ } static void upgt_init_locked(struct upgt_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); DPRINTF(sc, UPGT_DEBUG_RESET, "setting MAC address to %s\n", ether_sprintf(ic->ic_myaddr)); upgt_set_macfilter(sc, IEEE80211_S_SCAN); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; } static int upgt_set_macfilter(struct upgt_softc *sc, uint8_t state) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni = vap->iv_bss; struct upgt_data *data_cmd = &sc->cmd_data; struct upgt_lmac_mem *mem; struct upgt_lmac_filter *filter; int len; uint8_t broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; /* * Transmit the URB containing the CMD data. */ bzero(data_cmd->buf, MCLBYTES); mem = (struct upgt_lmac_mem *)data_cmd->buf; mem->addr = htole32(sc->sc_memaddr_frame_start + UPGT_MEMSIZE_FRAME_HEAD); filter = (struct upgt_lmac_filter *)(mem + 1); filter->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK; filter->header1.type = UPGT_H1_TYPE_CTRL; filter->header1.len = htole16( sizeof(struct upgt_lmac_filter) - sizeof(struct upgt_lmac_header)); filter->header2.reqid = htole32(sc->sc_memaddr_frame_start); filter->header2.type = htole16(UPGT_H2_TYPE_MACFILTER); filter->header2.flags = 0; switch (state) { case IEEE80211_S_INIT: DPRINTF(sc, UPGT_DEBUG_STATE, "%s: set MAC filter to INIT\n", __func__); filter->type = htole16(UPGT_FILTER_TYPE_RESET); break; case IEEE80211_S_SCAN: DPRINTF(sc, UPGT_DEBUG_STATE, "set MAC filter to SCAN (bssid %s)\n", ether_sprintf(broadcast)); filter->type = htole16(UPGT_FILTER_TYPE_NONE); IEEE80211_ADDR_COPY(filter->dst, ic->ic_myaddr); IEEE80211_ADDR_COPY(filter->src, broadcast); filter->unknown1 = htole16(UPGT_FILTER_UNKNOWN1); filter->rxaddr = htole32(sc->sc_memaddr_rx_start); filter->unknown2 = htole16(UPGT_FILTER_UNKNOWN2); filter->rxhw = htole32(sc->sc_eeprom_hwrx); filter->unknown3 = htole16(UPGT_FILTER_UNKNOWN3); break; case IEEE80211_S_RUN: /* XXX monitor mode isn't tested yet. */ if (vap->iv_opmode == IEEE80211_M_MONITOR) { filter->type = htole16(UPGT_FILTER_TYPE_MONITOR); IEEE80211_ADDR_COPY(filter->dst, ic->ic_myaddr); IEEE80211_ADDR_COPY(filter->src, ni->ni_bssid); filter->unknown1 = htole16(UPGT_FILTER_MONITOR_UNKNOWN1); filter->rxaddr = htole32(sc->sc_memaddr_rx_start); filter->unknown2 = htole16(UPGT_FILTER_MONITOR_UNKNOWN2); filter->rxhw = htole32(sc->sc_eeprom_hwrx); filter->unknown3 = htole16(UPGT_FILTER_MONITOR_UNKNOWN3); } else { DPRINTF(sc, UPGT_DEBUG_STATE, "set MAC filter to RUN (bssid %s)\n", ether_sprintf(ni->ni_bssid)); filter->type = htole16(UPGT_FILTER_TYPE_STA); IEEE80211_ADDR_COPY(filter->dst, ic->ic_myaddr); IEEE80211_ADDR_COPY(filter->src, ni->ni_bssid); filter->unknown1 = htole16(UPGT_FILTER_UNKNOWN1); filter->rxaddr = htole32(sc->sc_memaddr_rx_start); filter->unknown2 = htole16(UPGT_FILTER_UNKNOWN2); filter->rxhw = htole32(sc->sc_eeprom_hwrx); filter->unknown3 = htole16(UPGT_FILTER_UNKNOWN3); } break; default: device_printf(sc->sc_dev, "MAC filter does not know that state!\n"); break; } len = sizeof(*mem) + sizeof(*filter); mem->chksum = upgt_chksum_le((uint32_t *)filter, len - sizeof(*mem)); UPGT_UNLOCK(sc); if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) { device_printf(sc->sc_dev, "could not transmit macfilter CMD data URB!\n"); UPGT_LOCK(sc); return (EIO); } UPGT_LOCK(sc); return (0); } static void upgt_setup_rates(struct ieee80211vap *vap, struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct upgt_softc *sc = ifp->if_softc; const struct ieee80211_txparam *tp; /* * 0x01 = OFMD6 0x10 = DS1 * 0x04 = OFDM9 0x11 = DS2 * 0x06 = OFDM12 0x12 = DS5 * 0x07 = OFDM18 0x13 = DS11 * 0x08 = OFDM24 * 0x09 = OFDM36 * 0x0a = OFDM48 * 0x0b = OFDM54 */ const uint8_t rateset_auto_11b[] = { 0x13, 0x13, 0x12, 0x11, 0x11, 0x10, 0x10, 0x10 }; const uint8_t rateset_auto_11g[] = { 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x04, 0x01 }; const uint8_t rateset_fix_11bg[] = { 0x10, 0x11, 0x12, 0x13, 0x01, 0x04, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b }; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; /* XXX */ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { /* * Automatic rate control is done by the device. * We just pass the rateset from which the device * will pickup a rate. */ if (ic->ic_curmode == IEEE80211_MODE_11B) bcopy(rateset_auto_11b, sc->sc_cur_rateset, sizeof(sc->sc_cur_rateset)); if (ic->ic_curmode == IEEE80211_MODE_11G || ic->ic_curmode == IEEE80211_MODE_AUTO) bcopy(rateset_auto_11g, sc->sc_cur_rateset, sizeof(sc->sc_cur_rateset)); } else { /* set a fixed rate */ memset(sc->sc_cur_rateset, rateset_fix_11bg[tp->ucastrate], sizeof(sc->sc_cur_rateset)); } } static void upgt_set_multi(void *arg) { struct upgt_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; if (!(ifp->if_flags & IFF_UP)) return; /* * XXX don't know how to set a device. Lack of docs. Just try to set * IFF_ALLMULTI flag here. */ IF_ADDR_LOCK(ifp); ifp->if_flags |= IFF_ALLMULTI; IF_ADDR_UNLOCK(ifp); } static void upgt_start(struct ifnet *ifp) { struct upgt_softc *sc = ifp->if_softc; struct upgt_data *data_tx; struct ieee80211_node *ni; struct mbuf *m; int i; UPGT_LOCK(sc); for (i = 0; i < upgt_txbuf; i++) { data_tx = &sc->tx_data[i]; if (data_tx->use == 1) continue; IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); ifp->if_oerrors++; continue; } if ((data_tx->addr = upgt_mem_alloc(sc)) == 0) { device_printf(sc->sc_dev, "no free prism memory!\n"); UPGT_UNLOCK(sc); return; } data_tx->ni = ni; data_tx->m = m; data_tx->use = 1; sc->tx_queued++; } if (sc->tx_queued > 0) { DPRINTF(sc, UPGT_DEBUG_XMIT, "tx_queued=%d\n", sc->tx_queued); ifp->if_drv_flags |= IFF_DRV_OACTIVE; sc->sc_tx_timer = 5; callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc); /* process the TX queue in process context */ usb_rem_task(sc->sc_udev, &sc->sc_task_tx); usb_add_task(sc->sc_udev, &sc->sc_task_tx, USB_TASKQ_DRIVER); } UPGT_UNLOCK(sc); } static int upgt_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct upgt_softc *sc = ifp->if_softc; struct upgt_data *data_tx = NULL; int i; /* prevent management frames from being sent if we're not ready */ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { m_freem(m); ieee80211_free_node(ni); return ENETDOWN; } UPGT_LOCK(sc); if (sc->tx_queued >= upgt_txbuf) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; m_freem(m); ieee80211_free_node(ni); UPGT_UNLOCK(sc); return ENOBUFS; /* XXX */ } ifp->if_opackets++; /* choose a unused buffer. */ for (i = 0; i < upgt_txbuf; i++) { data_tx = &sc->tx_data[i]; if (data_tx->use == 0) break; } KASSERT(data_tx != NULL, ("data_tx is NULL")); KASSERT(data_tx->use == 0, ("no empty TX queue")); if ((data_tx->addr = upgt_mem_alloc(sc)) == 0) { device_printf(sc->sc_dev, "no free prism memory!\n"); UPGT_UNLOCK(sc); return ENOBUFS; } if (bpf_peers_present(ifp->if_bpf)) { struct upgt_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = 0; /* TODO: where to get from? */ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); } data_tx->ni = ni; data_tx->m = m; data_tx->use = 1; sc->tx_queued++; ifp->if_drv_flags |= IFF_DRV_OACTIVE; UPGT_UNLOCK(sc); sc->sc_tx_timer = 5; callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc); usb_rem_task(sc->sc_udev, &sc->sc_task_tx); usb_add_task(sc->sc_udev, &sc->sc_task_tx, USB_TASKQ_DRIVER); return 0; } static void upgt_watchdog(void *arg) { struct upgt_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "watchdog timeout\n"); /* upgt_init(ifp); XXX needs a process context ? */ ifp->if_oerrors++; return; } callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc); } } static uint32_t upgt_mem_alloc(struct upgt_softc *sc) { int i; for (i = 0; i < sc->sc_memory.pages; i++) { if (sc->sc_memory.page[i].used == 0) { sc->sc_memory.page[i].used = 1; return (sc->sc_memory.page[i].addr); } } return (0); } static void upgt_scantask(void *arg) { struct upgt_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; switch (sc->sc_scan_action) { case UPGT_SET_CHANNEL: upgt_set_chan(sc, ic->ic_curchan); break; default: device_printf(sc->sc_dev, "unknown scan action %d\n", sc->sc_scan_action); break; } } static void upgt_scan_start(struct ieee80211com *ic) { /* do nothing. */ } static void upgt_scan_end(struct ieee80211com *ic) { /* do nothing. */ } static void upgt_set_channel(struct ieee80211com *ic) { struct upgt_softc *sc = ic->ic_ifp->if_softc; usb_rem_task(sc->sc_udev, &sc->sc_scantask); /* do it in a process context */ sc->sc_scan_action = UPGT_SET_CHANNEL; usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); } static void upgt_set_chan(struct upgt_softc *sc, struct ieee80211_channel *c) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct upgt_data *data_cmd = &sc->cmd_data; struct upgt_lmac_mem *mem; struct upgt_lmac_channel *chan; int len, channel; channel = ieee80211_chan2ieee(ic, c); if (channel == 0 || channel == IEEE80211_CHAN_ANY) { /* XXX should NEVER happen */ device_printf(sc->sc_dev, "%s: invalid channel %x\n", __func__, channel); return; } DPRINTF(sc, UPGT_DEBUG_STATE, "%s: channel %d\n", __func__, channel); /* * Transmit the URB containing the CMD data. */ bzero(data_cmd->buf, MCLBYTES); mem = (struct upgt_lmac_mem *)data_cmd->buf; mem->addr = htole32(sc->sc_memaddr_frame_start + UPGT_MEMSIZE_FRAME_HEAD); chan = (struct upgt_lmac_channel *)(mem + 1); chan->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK; chan->header1.type = UPGT_H1_TYPE_CTRL; chan->header1.len = htole16( sizeof(struct upgt_lmac_channel) - sizeof(struct upgt_lmac_header)); chan->header2.reqid = htole32(sc->sc_memaddr_frame_start); chan->header2.type = htole16(UPGT_H2_TYPE_CHANNEL); chan->header2.flags = 0; chan->unknown1 = htole16(UPGT_CHANNEL_UNKNOWN1); chan->unknown2 = htole16(UPGT_CHANNEL_UNKNOWN2); chan->freq6 = sc->sc_eeprom_freq6[channel]; chan->settings = sc->sc_eeprom_freq6_settings; chan->unknown3 = UPGT_CHANNEL_UNKNOWN3; bcopy(&sc->sc_eeprom_freq3[channel].data, chan->freq3_1, sizeof(chan->freq3_1)); bcopy(&sc->sc_eeprom_freq4[channel], chan->freq4, sizeof(sc->sc_eeprom_freq4[channel])); bcopy(&sc->sc_eeprom_freq3[channel].data, chan->freq3_2, sizeof(chan->freq3_2)); len = sizeof(*mem) + sizeof(*chan); mem->chksum = upgt_chksum_le((uint32_t *)chan, len - sizeof(*mem)); if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) device_printf(sc->sc_dev, "could not transmit channel CMD data URB!\n"); } static struct ieee80211vap * upgt_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct upgt_vap *uvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; uvp = (struct upgt_vap *) malloc(sizeof(struct upgt_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (uvp == NULL) return NULL; vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = upgt_newstate; /* setup device rates */ upgt_setup_rates(vap, ic); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return vap; } static int upgt_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct upgt_vap *uvp = UPGT_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct upgt_softc *sc = ic->ic_ifp->if_softc; - usb_rem_task(sc->sc_udev, &sc->sc_task); - - /* do it in a process context */ - sc->sc_state = nstate; - sc->sc_arg = arg; - - if (nstate == IEEE80211_S_INIT) { - uvp->newstate(vap, nstate, arg); - return 0; - } else { - usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER); - return EINPROGRESS; + IEEE80211_UNLOCK(ic); + switch (nstate) { + case IEEE80211_S_INIT: + /* do not accept any frames if the device is down */ + UPGT_LOCK(sc); + upgt_set_macfilter(sc, nstate); + UPGT_UNLOCK(sc); + upgt_set_led(sc, UPGT_LED_OFF, nstate); + break; + case IEEE80211_S_SCAN: + upgt_set_chan(sc, ic->ic_curchan); + break; + case IEEE80211_S_AUTH: + upgt_set_chan(sc, ic->ic_curchan); + break; + case IEEE80211_S_ASSOC: + break; + case IEEE80211_S_RUN: + UPGT_LOCK(sc); + upgt_set_macfilter(sc, nstate); + UPGT_UNLOCK(sc); + upgt_set_led(sc, UPGT_LED_ON, nstate); + break; + default: + break; } + IEEE80211_LOCK(ic); + uvp->newstate(vap, nstate, arg); + return 0; } static void upgt_vap_delete(struct ieee80211vap *vap) { struct upgt_vap *uvp = UPGT_VAP(vap); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static void upgt_update_mcast(struct ifnet *ifp) { struct upgt_softc *sc = ifp->if_softc; usb_add_task(sc->sc_udev, &sc->sc_mcasttask, USB_TASKQ_DRIVER); } static int upgt_eeprom_parse(struct upgt_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct upgt_eeprom_header *eeprom_header; struct upgt_eeprom_option *eeprom_option; uint16_t option_len; uint16_t option_type; uint16_t preamble_len; int option_end = 0; /* calculate eeprom options start offset */ eeprom_header = (struct upgt_eeprom_header *)sc->sc_eeprom; preamble_len = le16toh(eeprom_header->preamble_len); eeprom_option = (struct upgt_eeprom_option *)(sc->sc_eeprom + (sizeof(struct upgt_eeprom_header) + preamble_len)); while (!option_end) { /* the eeprom option length is stored in words */ option_len = (le16toh(eeprom_option->len) - 1) * sizeof(uint16_t); option_type = le16toh(eeprom_option->type); switch (option_type) { case UPGT_EEPROM_TYPE_NAME: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM name len=%d\n", option_len); break; case UPGT_EEPROM_TYPE_SERIAL: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM serial len=%d\n", option_len); break; case UPGT_EEPROM_TYPE_MAC: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM mac len=%d\n", option_len); IEEE80211_ADDR_COPY(ic->ic_myaddr, eeprom_option->data); break; case UPGT_EEPROM_TYPE_HWRX: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM hwrx len=%d\n", option_len); upgt_eeprom_parse_hwrx(sc, eeprom_option->data); break; case UPGT_EEPROM_TYPE_CHIP: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM chip len=%d\n", option_len); break; case UPGT_EEPROM_TYPE_FREQ3: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM freq3 len=%d\n", option_len); upgt_eeprom_parse_freq3(sc, eeprom_option->data, option_len); break; case UPGT_EEPROM_TYPE_FREQ4: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM freq4 len=%d\n", option_len); upgt_eeprom_parse_freq4(sc, eeprom_option->data, option_len); break; case UPGT_EEPROM_TYPE_FREQ5: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM freq5 len=%d\n", option_len); break; case UPGT_EEPROM_TYPE_FREQ6: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM freq6 len=%d\n", option_len); upgt_eeprom_parse_freq6(sc, eeprom_option->data, option_len); break; case UPGT_EEPROM_TYPE_END: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM end len=%d\n", option_len); option_end = 1; break; case UPGT_EEPROM_TYPE_OFF: DPRINTF(sc, UPGT_DEBUG_FW, "%s: EEPROM off without end option!\n", __func__); return (EIO); default: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM unknown type 0x%04x len=%d\n", option_type, option_len); break; } /* jump to next EEPROM option */ eeprom_option = (struct upgt_eeprom_option *) (eeprom_option->data + option_len); } return (0); } static void upgt_eeprom_parse_freq3(struct upgt_softc *sc, uint8_t *data, int len) { struct upgt_eeprom_freq3_header *freq3_header; struct upgt_lmac_freq3 *freq3; int i, elements, flags; unsigned channel; freq3_header = (struct upgt_eeprom_freq3_header *)data; freq3 = (struct upgt_lmac_freq3 *)(freq3_header + 1); flags = freq3_header->flags; elements = freq3_header->elements; DPRINTF(sc, UPGT_DEBUG_FW, "flags=0x%02x elements=%d\n", flags, elements); for (i = 0; i < elements; i++) { channel = ieee80211_mhz2ieee(le16toh(freq3[i].freq), 0); if (!(channel >= 0 && channel < IEEE80211_CHAN_MAX)) continue; sc->sc_eeprom_freq3[channel] = freq3[i]; DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n", le16toh(sc->sc_eeprom_freq3[channel].freq), channel); } } void upgt_eeprom_parse_freq4(struct upgt_softc *sc, uint8_t *data, int len) { struct upgt_eeprom_freq4_header *freq4_header; struct upgt_eeprom_freq4_1 *freq4_1; struct upgt_eeprom_freq4_2 *freq4_2; int i, j, elements, settings, flags; unsigned channel; freq4_header = (struct upgt_eeprom_freq4_header *)data; freq4_1 = (struct upgt_eeprom_freq4_1 *)(freq4_header + 1); flags = freq4_header->flags; elements = freq4_header->elements; settings = freq4_header->settings; /* we need this value later */ sc->sc_eeprom_freq6_settings = freq4_header->settings; DPRINTF(sc, UPGT_DEBUG_FW, "flags=0x%02x elements=%d settings=%d\n", flags, elements, settings); for (i = 0; i < elements; i++) { channel = ieee80211_mhz2ieee(le16toh(freq4_1[i].freq), 0); if (!(channel >= 0 && channel < IEEE80211_CHAN_MAX)) continue; freq4_2 = (struct upgt_eeprom_freq4_2 *)freq4_1[i].data; for (j = 0; j < settings; j++) { sc->sc_eeprom_freq4[channel][j].cmd = freq4_2[j]; sc->sc_eeprom_freq4[channel][j].pad = 0; } DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n", le16toh(freq4_1[i].freq), channel); } } void upgt_eeprom_parse_freq6(struct upgt_softc *sc, uint8_t *data, int len) { struct upgt_lmac_freq6 *freq6; int i, elements; unsigned channel; freq6 = (struct upgt_lmac_freq6 *)data; elements = len / sizeof(struct upgt_lmac_freq6); DPRINTF(sc, UPGT_DEBUG_FW, "elements=%d\n", elements); for (i = 0; i < elements; i++) { channel = ieee80211_mhz2ieee(le16toh(freq6[i].freq), 0); if (!(channel >= 0 && channel < IEEE80211_CHAN_MAX)) continue; sc->sc_eeprom_freq6[channel] = freq6[i]; DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n", le16toh(sc->sc_eeprom_freq6[channel].freq), channel); } } static void upgt_eeprom_parse_hwrx(struct upgt_softc *sc, uint8_t *data) { struct upgt_eeprom_option_hwrx *option_hwrx; option_hwrx = (struct upgt_eeprom_option_hwrx *)data; sc->sc_eeprom_hwrx = option_hwrx->rxfilter - UPGT_EEPROM_RX_CONST; DPRINTF(sc, UPGT_DEBUG_FW, "hwrx option value=0x%04x\n", sc->sc_eeprom_hwrx); } static int upgt_eeprom_read(struct upgt_softc *sc) { struct upgt_data *data_cmd = &sc->cmd_data; struct upgt_lmac_mem *mem; struct upgt_lmac_eeprom *eeprom; int offset, block, len; offset = 0; block = UPGT_EEPROM_BLOCK_SIZE; while (offset < UPGT_EEPROM_SIZE) { DPRINTF(sc, UPGT_DEBUG_FW, "request EEPROM block (offset=%d, len=%d)\n", offset, block); /* * Transmit the URB containing the CMD data. */ bzero(data_cmd->buf, MCLBYTES); mem = (struct upgt_lmac_mem *)data_cmd->buf; mem->addr = htole32(sc->sc_memaddr_frame_start + UPGT_MEMSIZE_FRAME_HEAD); eeprom = (struct upgt_lmac_eeprom *)(mem + 1); eeprom->header1.flags = 0; eeprom->header1.type = UPGT_H1_TYPE_CTRL; eeprom->header1.len = htole16(( sizeof(struct upgt_lmac_eeprom) - sizeof(struct upgt_lmac_header)) + block); eeprom->header2.reqid = htole32(sc->sc_memaddr_frame_start); eeprom->header2.type = htole16(UPGT_H2_TYPE_EEPROM); eeprom->header2.flags = 0; eeprom->offset = htole16(offset); eeprom->len = htole16(block); len = sizeof(*mem) + sizeof(*eeprom) + block; mem->chksum = upgt_chksum_le((uint32_t *)eeprom, len - sizeof(*mem)); if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, USBD_FORCE_SHORT_XFER) != 0) { device_printf(sc->sc_dev, "could not transmit EEPROM data URB!\n"); return (EIO); } if (tsleep(sc, 0, "eeprom_request", UPGT_USB_TIMEOUT)) { device_printf(sc->sc_dev, "timeout while waiting for EEPROM data!\n"); return (EIO); } offset += block; if (UPGT_EEPROM_SIZE - offset < block) block = UPGT_EEPROM_SIZE - offset; } return (0); } /* * The firmware awaits a checksum for each frame we send to it. * The algorithm used therefor is uncommon but somehow similar to CRC32. */ static uint32_t upgt_chksum_le(const uint32_t *buf, size_t size) { int i; uint32_t crc = 0; for (i = 0; i < size; i += sizeof(uint32_t)) { crc = htole32(crc ^ *buf++); crc = htole32((crc >> 5) ^ (crc << 3)); } return (crc); } static void upgt_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct upgt_data *data_rx = priv; struct upgt_softc *sc = data_rx->sc; int len; struct upgt_lmac_header *header; struct upgt_lmac_eeprom *eeprom; uint8_t h1_type; uint16_t h2_type; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh); goto skip; } usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL); /* * Check what type of frame came in. */ header = (struct upgt_lmac_header *)(data_rx->buf + 4); h1_type = header->header1.type; h2_type = le16toh(header->header2.type); if (h1_type == UPGT_H1_TYPE_CTRL && h2_type == UPGT_H2_TYPE_EEPROM) { eeprom = (struct upgt_lmac_eeprom *)(data_rx->buf + 4); uint16_t eeprom_offset = le16toh(eeprom->offset); uint16_t eeprom_len = le16toh(eeprom->len); DPRINTF(sc, UPGT_DEBUG_FW, "received EEPROM block (offset=%d, len=%d)\n", eeprom_offset, eeprom_len); bcopy(data_rx->buf + sizeof(struct upgt_lmac_eeprom) + 4, sc->sc_eeprom + eeprom_offset, eeprom_len); /* EEPROM data has arrived in time, wakeup tsleep() */ wakeup(sc); } else if (h1_type == UPGT_H1_TYPE_CTRL && h2_type == UPGT_H2_TYPE_TX_DONE) { DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: received 802.11 TX done\n", __func__); upgt_tx_done(sc, data_rx->buf + 4); } else if (h1_type == UPGT_H1_TYPE_RX_DATA || h1_type == UPGT_H1_TYPE_RX_DATA_MGMT) { DPRINTF(sc, UPGT_DEBUG_RECV, "%s: received 802.11 RX data\n", __func__); upgt_rx(sc, data_rx->buf + 4, le16toh(header->header1.len)); } else if (h1_type == UPGT_H1_TYPE_CTRL && h2_type == UPGT_H2_TYPE_STATS) { DPRINTF(sc, UPGT_DEBUG_STAT, "%s: received statistic data\n", __func__); /* TODO: what could we do with the statistic data? */ } else { /* ignore unknown frame types */ DPRINTF(sc, UPGT_DEBUG_INTR, "received unknown frame type 0x%02x\n", header->header1.type); } skip: /* setup new transfer */ usbd_setup_xfer(xfer, sc->sc_rx_pipeh, data_rx, data_rx->buf, MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, upgt_rxeof); (void)usbd_transfer(xfer); } static void upgt_rx(struct upgt_softc *sc, uint8_t *data, int pkglen) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct upgt_lmac_rx_desc *rxdesc; struct ieee80211_node *ni; struct mbuf *m; int nf; /* * don't pass packets to the ieee80211 framework if the driver isn't * RUNNING. */ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) return; /* access RX packet descriptor */ rxdesc = (struct upgt_lmac_rx_desc *)data; /* create mbuf which is suitable for strict alignment archs */ KASSERT((pkglen + ETHER_ALIGN) < MCLBYTES, ("A current mbuf storage is small (%d)", pkglen + ETHER_ALIGN)); m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { device_printf(sc->sc_dev, "could not create RX mbuf!\n"); return; } m_adj(m, ETHER_ALIGN); bcopy(rxdesc->data, mtod(m, char *), pkglen); /* trim FCS */ m->m_len = m->m_pkthdr.len = pkglen - IEEE80211_CRC_LEN; m->m_pkthdr.rcvif = ifp; if (bpf_peers_present(ifp->if_bpf)) { struct upgt_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; tap->wr_rate = upgt_rx_rate(sc, rxdesc->rate); tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_antsignal = rxdesc->rssi; bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } ifp->if_ipackets++; nf = -95; /* XXX */ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { (void)ieee80211_input(ni, m, rxdesc->rssi, nf, 0); ieee80211_free_node(ni); } else (void)ieee80211_input_all(ic, m, rxdesc->rssi, nf, 0); DPRINTF(sc, UPGT_DEBUG_RX_PROC, "%s: RX done\n", __func__); } static uint8_t upgt_rx_rate(struct upgt_softc *sc, const int rate) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; static const uint8_t cck_upgt2rate[4] = { 2, 4, 11, 22 }; static const uint8_t ofdm_upgt2rate[12] = { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 }; if (ic->ic_curmode == IEEE80211_MODE_11B && !(rate < 0 || rate > 3)) return cck_upgt2rate[rate & 0xf]; if (ic->ic_curmode == IEEE80211_MODE_11G && !(rate < 0 || rate > 11)) return ofdm_upgt2rate[rate & 0xf]; return (0); } static void upgt_tx_done(struct upgt_softc *sc, uint8_t *data) { struct ifnet *ifp = sc->sc_ifp; struct upgt_lmac_tx_done_desc *desc; int i; desc = (struct upgt_lmac_tx_done_desc *)data; UPGT_LOCK(sc); for (i = 0; i < upgt_txbuf; i++) { struct upgt_data *data_tx = &sc->tx_data[i]; if (data_tx->addr == le32toh(desc->header2.reqid)) { upgt_mem_free(sc, data_tx->addr); ieee80211_free_node(data_tx->ni); data_tx->ni = NULL; data_tx->addr = 0; data_tx->m = NULL; data_tx->use = 0; sc->tx_queued--; ifp->if_opackets++; DPRINTF(sc, UPGT_DEBUG_TX_PROC, "TX done: memaddr=0x%08x, status=0x%04x, rssi=%d, ", le32toh(desc->header2.reqid), le16toh(desc->status), le16toh(desc->rssi)); DPRINTF(sc, UPGT_DEBUG_TX_PROC, "seq=%d\n", le16toh(desc->seq)); break; } } if (sc->tx_queued == 0) { /* TX queued was processed, continue */ sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; UPGT_UNLOCK(sc); upgt_start(ifp); return; } UPGT_UNLOCK(sc); } static void upgt_mem_free(struct upgt_softc *sc, uint32_t addr) { int i; for (i = 0; i < sc->sc_memory.pages; i++) { if (sc->sc_memory.page[i].addr == addr) { sc->sc_memory.page[i].used = 0; return; } } device_printf(sc->sc_dev, "could not free memory address 0x%08x!\n", addr); } static int upgt_fw_load(struct upgt_softc *sc) { const struct firmware *fw; struct upgt_data *data_cmd = &sc->cmd_data; struct upgt_data *data_rx = &sc->rx_data; struct upgt_fw_x2_header *x2; char start_fwload_cmd[] = { 0x3c, 0x0d }; int error = 0, offset, bsize, n, i, len; uint32_t crc32; fw = firmware_get(upgt_fwname); if (fw == NULL) { device_printf(sc->sc_dev, "could not read microcode %s!\n", upgt_fwname); return EIO; } /* send firmware start load command */ len = sizeof(start_fwload_cmd); bcopy(start_fwload_cmd, data_cmd->buf, len); if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) { device_printf(sc->sc_dev, "could not send start_firmware_load command!\n"); error = EIO; goto fail; } /* send X2 header */ len = sizeof(struct upgt_fw_x2_header); x2 = (struct upgt_fw_x2_header *)data_cmd->buf; bcopy(UPGT_X2_SIGNATURE, x2->signature, UPGT_X2_SIGNATURE_SIZE); x2->startaddr = htole32(UPGT_MEMADDR_FIRMWARE_START); x2->len = htole32(fw->datasize); x2->crc = upgt_crc32_le((uint8_t *)data_cmd->buf + UPGT_X2_SIGNATURE_SIZE, sizeof(struct upgt_fw_x2_header) - UPGT_X2_SIGNATURE_SIZE - sizeof(uint32_t)); if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) { device_printf(sc->sc_dev, "could not send firmware X2 header!\n"); error = EIO; goto fail; } /* download firmware */ for (offset = 0; offset < fw->datasize; offset += bsize) { if (fw->datasize - offset > UPGT_FW_BLOCK_SIZE) bsize = UPGT_FW_BLOCK_SIZE; else bsize = fw->datasize - offset; n = upgt_fw_copy((const uint8_t *)fw->data + offset, data_cmd->buf, bsize); DPRINTF(sc, UPGT_DEBUG_FW, "FW offset=%d, read=%d, sent=%d\n", offset, n, bsize); if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &bsize, 0) != 0) { device_printf(sc->sc_dev, "error while downloading firmware block!\n"); error = EIO; goto fail; } bsize = n; } DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware downloaded\n", __func__); /* load firmware */ crc32 = upgt_crc32_le(fw->data, fw->datasize); *((uint32_t *)(data_cmd->buf) ) = crc32; *((uint8_t *)(data_cmd->buf) + 4) = 'g'; *((uint8_t *)(data_cmd->buf) + 5) = '\r'; len = 6; if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) { device_printf(sc->sc_dev, "could not send load_firmware command!\n"); error = EIO; goto fail; } for (i = 0; i < UPGT_FIRMWARE_TIMEOUT; i++) { len = UPGT_FW_BLOCK_SIZE; bzero(data_rx->buf, MCLBYTES); if (upgt_bulk_xmit(sc, data_rx, sc->sc_rx_pipeh, &len, USBD_SHORT_XFER_OK) != 0) { device_printf(sc->sc_dev, "could not read firmware response!\n"); error = EIO; goto fail; } if (memcmp(data_rx->buf, "OK", 2) == 0) break; /* firmware load was successful */ } if (i == UPGT_FIRMWARE_TIMEOUT) { device_printf(sc->sc_dev, "firmware load failed!\n"); error = EIO; } DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware loaded\n", __func__); fail: firmware_put(fw, FIRMWARE_UNLOAD); return (error); } static uint32_t upgt_crc32_le(const void *buf, size_t size) { uint32_t crc; crc = ether_crc32_le(buf, size); /* apply final XOR value as common for CRC-32 */ crc = htole32(crc ^ 0xffffffffU); return (crc); } /* * While copying the version 2 firmware, we need to replace two characters: * * 0x7e -> 0x7d 0x5e * 0x7d -> 0x7d 0x5d */ static int upgt_fw_copy(const uint8_t *src, char *dst, int size) { int i, j; for (i = 0, j = 0; i < size && j < size; i++) { switch (src[i]) { case 0x7e: dst[j] = 0x7d; j++; dst[j] = 0x5e; j++; break; case 0x7d: dst[j] = 0x7d; j++; dst[j] = 0x5d; j++; break; default: dst[j] = src[i]; j++; break; } } return (i); } static int upgt_mem_init(struct upgt_softc *sc) { int i; for (i = 0; i < UPGT_MEMORY_MAX_PAGES; i++) { sc->sc_memory.page[i].used = 0; if (i == 0) { /* * The first memory page is always reserved for * command data. */ sc->sc_memory.page[i].addr = sc->sc_memaddr_frame_start + MCLBYTES; } else { sc->sc_memory.page[i].addr = sc->sc_memory.page[i - 1].addr + MCLBYTES; } if (sc->sc_memory.page[i].addr + MCLBYTES >= sc->sc_memaddr_frame_end) break; DPRINTF(sc, UPGT_DEBUG_FW, "memory address page %d=0x%08x\n", i, sc->sc_memory.page[i].addr); } sc->sc_memory.pages = i; if (upgt_txbuf > sc->sc_memory.pages) DPRINTF(sc, UPGT_DEBUG_FW, "memory pages=%d\n", sc->sc_memory.pages); return (0); } static int upgt_fw_verify(struct upgt_softc *sc) { const struct firmware *fw; const struct upgt_fw_bra_option *bra_opt; const struct upgt_fw_bra_descr *descr; const uint8_t *p; const uint32_t *uc; uint32_t bra_option_type, bra_option_len; int offset, bra_end = 0, error = 0; fw = firmware_get(upgt_fwname); if (fw == NULL) { device_printf(sc->sc_dev, "could not read microcode %s!\n", upgt_fwname); return EIO; } /* * Seek to beginning of Boot Record Area (BRA). */ for (offset = 0; offset < fw->datasize; offset += sizeof(*uc)) { uc = (const uint32_t *)((const uint8_t *)fw->data + offset); if (*uc == 0) break; } for (; offset < fw->datasize; offset += sizeof(*uc)) { uc = (const uint32_t *)((const uint8_t *)fw->data + offset); if (*uc != 0) break; } if (offset == fw->datasize) { device_printf(sc->sc_dev, "firmware Boot Record Area not found!\n"); error = EIO; goto fail; } DPRINTF(sc, UPGT_DEBUG_FW, "firmware Boot Record Area found at offset %d\n", offset); /* * Parse Boot Record Area (BRA) options. */ while (offset < fw->datasize && bra_end == 0) { /* get current BRA option */ p = (const uint8_t *)fw->data + offset; bra_opt = (const struct upgt_fw_bra_option *)p; bra_option_type = le32toh(bra_opt->type); bra_option_len = le32toh(bra_opt->len) * sizeof(*uc); switch (bra_option_type) { case UPGT_BRA_TYPE_FW: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_FW len=%d\n", bra_option_len); if (bra_option_len != UPGT_BRA_FWTYPE_SIZE) { device_printf(sc->sc_dev, "wrong UPGT_BRA_TYPE_FW len!\n"); error = EIO; goto fail; } if (memcmp(UPGT_BRA_FWTYPE_LM86, bra_opt->data, bra_option_len) == 0) { sc->sc_fw_type = UPGT_FWTYPE_LM86; break; } if (memcmp(UPGT_BRA_FWTYPE_LM87, bra_opt->data, bra_option_len) == 0) { sc->sc_fw_type = UPGT_FWTYPE_LM87; break; } device_printf(sc->sc_dev, "unsupported firmware type!\n"); error = EIO; goto fail; case UPGT_BRA_TYPE_VERSION: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_VERSION len=%d\n", bra_option_len); break; case UPGT_BRA_TYPE_DEPIF: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_DEPIF len=%d\n", bra_option_len); break; case UPGT_BRA_TYPE_EXPIF: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_EXPIF len=%d\n", bra_option_len); break; case UPGT_BRA_TYPE_DESCR: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_DESCR len=%d\n", bra_option_len); descr = (const struct upgt_fw_bra_descr *)bra_opt->data; sc->sc_memaddr_frame_start = le32toh(descr->memaddr_space_start); sc->sc_memaddr_frame_end = le32toh(descr->memaddr_space_end); DPRINTF(sc, UPGT_DEBUG_FW, "memory address space start=0x%08x\n", sc->sc_memaddr_frame_start); DPRINTF(sc, UPGT_DEBUG_FW, "memory address space end=0x%08x\n", sc->sc_memaddr_frame_end); break; case UPGT_BRA_TYPE_END: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_END len=%d\n", bra_option_len); bra_end = 1; break; default: DPRINTF(sc, UPGT_DEBUG_FW, "unknown BRA option len=%d\n", bra_option_len); error = EIO; goto fail; } /* jump to next BRA option */ offset += sizeof(struct upgt_fw_bra_option) + bra_option_len; } DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware verified", __func__); fail: firmware_put(fw, FIRMWARE_UNLOAD); return (error); } static int upgt_bulk_xmit(struct upgt_softc *sc, struct upgt_data *data, usbd_pipe_handle pipeh, uint32_t *size, int flags) { usbd_status status; mtx_lock(&Giant); status = usbd_bulk_transfer(data->xfer, pipeh, USBD_NO_COPY | flags, UPGT_USB_TIMEOUT, data->buf, size, "upgt_bulk_xmit"); if (status != USBD_NORMAL_COMPLETION) { device_printf(sc->sc_dev, "%s: error %s!\n", __func__, usbd_errstr(status)); mtx_unlock(&Giant); return (EIO); } mtx_unlock(&Giant); return (0); } static int upgt_device_reset(struct upgt_softc *sc) { struct upgt_data *data_cmd = &sc->cmd_data; char init_cmd[] = { 0x7e, 0x7e, 0x7e, 0x7e }; int len; len = sizeof(init_cmd); bcopy(init_cmd, data_cmd->buf, len); if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) { device_printf(sc->sc_dev, "could not send device init string!\n"); return (EIO); } usbd_delay_ms(sc->sc_udev, 100); DPRINTF(sc, UPGT_DEBUG_FW, "%s: device initialized\n", __func__); return (0); } static int upgt_alloc_tx(struct upgt_softc *sc) { int i; sc->tx_queued = 0; for (i = 0; i < upgt_txbuf; i++) { struct upgt_data *data_tx = &sc->tx_data[i]; data_tx->sc = sc; data_tx->xfer = usbd_alloc_xfer(sc->sc_udev); if (data_tx->xfer == NULL) { device_printf(sc->sc_dev, "could not allocate TX xfer!\n"); return (ENOMEM); } data_tx->buf = usbd_alloc_buffer(data_tx->xfer, MCLBYTES); if (data_tx->buf == NULL) { device_printf(sc->sc_dev, "could not allocate TX buffer!\n"); return (ENOMEM); } bzero(data_tx->buf, MCLBYTES); } return (0); } static int upgt_alloc_rx(struct upgt_softc *sc) { struct upgt_data *data_rx = &sc->rx_data; data_rx->sc = sc; data_rx->xfer = usbd_alloc_xfer(sc->sc_udev); if (data_rx->xfer == NULL) { device_printf(sc->sc_dev, "could not allocate RX xfer!\n"); return (ENOMEM); } data_rx->buf = usbd_alloc_buffer(data_rx->xfer, MCLBYTES); if (data_rx->buf == NULL) { device_printf(sc->sc_dev, "could not allocate RX buffer!\n"); return (ENOMEM); } bzero(data_rx->buf, MCLBYTES); return (0); } static int upgt_alloc_cmd(struct upgt_softc *sc) { struct upgt_data *data_cmd = &sc->cmd_data; data_cmd->sc = sc; data_cmd->xfer = usbd_alloc_xfer(sc->sc_udev); if (data_cmd->xfer == NULL) { device_printf(sc->sc_dev, "could not allocate RX xfer!\n"); return (ENOMEM); } data_cmd->buf = usbd_alloc_buffer(data_cmd->xfer, MCLBYTES); if (data_cmd->buf == NULL) { device_printf(sc->sc_dev, "could not allocate RX buffer!\n"); return (ENOMEM); } bzero(data_cmd->buf, MCLBYTES); return (0); } static int upgt_detach(device_t dev) { struct upgt_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; if (!device_is_attached(dev)) return 0; upgt_stop(sc, 1); /* abort and close TX / RX pipes */ if (sc->sc_tx_pipeh != NULL) usbd_close_pipe(sc->sc_tx_pipeh); if (sc->sc_rx_pipeh != NULL) usbd_close_pipe(sc->sc_rx_pipeh); mtx_destroy(&sc->sc_mtx); usb_rem_task(sc->sc_udev, &sc->sc_mcasttask); usb_rem_task(sc->sc_udev, &sc->sc_scantask); - usb_rem_task(sc->sc_udev, &sc->sc_task); usb_rem_task(sc->sc_udev, &sc->sc_task_tx); callout_stop(&sc->sc_led_ch); callout_stop(&sc->sc_watchdog_ch); /* free xfers */ upgt_free_tx(sc); upgt_free_rx(sc); upgt_free_cmd(sc); bpfdetach(ifp); ieee80211_ifdetach(ic); if_free(ifp); usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); return 0; } static void upgt_free_rx(struct upgt_softc *sc) { struct upgt_data *data_rx = &sc->rx_data; if (data_rx->xfer != NULL) { usbd_free_xfer(data_rx->xfer); data_rx->xfer = NULL; } data_rx->ni = NULL; } static void upgt_free_tx(struct upgt_softc *sc) { int i; for (i = 0; i < upgt_txbuf; i++) { struct upgt_data *data_tx = &sc->tx_data[i]; if (data_tx->xfer != NULL) { usbd_free_xfer(data_tx->xfer); data_tx->xfer = NULL; } data_tx->ni = NULL; } } static void upgt_free_cmd(struct upgt_softc *sc) { struct upgt_data *data_cmd = &sc->cmd_data; if (data_cmd->xfer != NULL) { usbd_free_xfer(data_cmd->xfer); data_cmd->xfer = NULL; } } static device_method_t upgt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, upgt_match), DEVMETHOD(device_attach, upgt_attach), DEVMETHOD(device_detach, upgt_detach), { 0, 0 } }; static driver_t upgt_driver = { "upgt", upgt_methods, sizeof(struct upgt_softc) }; static devclass_t upgt_devclass; DRIVER_MODULE(if_upgt, uhub, upgt_driver, upgt_devclass, usbd_driver_load, 0); MODULE_VERSION(if_upgt, 1); MODULE_DEPEND(if_upgt, usb, 1, 1, 1); MODULE_DEPEND(if_upgt, wlan, 1, 1, 1); MODULE_DEPEND(if_upgt, upgtfw_fw, 1, 1, 1); Index: user/thompsa/vaptq/sys/dev/usb/if_upgtvar.h =================================================================== --- user/thompsa/vaptq/sys/dev/usb/if_upgtvar.h (revision 185732) +++ user/thompsa/vaptq/sys/dev/usb/if_upgtvar.h (revision 185733) @@ -1,462 +1,459 @@ /* $OpenBSD: if_upgtvar.h,v 1.14 2008/02/02 13:48:44 mglocker Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 2007 Marcus Glocker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ struct upgt_softc; /* * General values. */ #define UPGT_IFACE_INDEX 0 #define UPGT_CONFIG_NO 1 #define UPGT_USB_TIMEOUT 1000 #define UPGT_FIRMWARE_TIMEOUT 10 #define UPGT_MEMADDR_FIRMWARE_START 0x00020000 /* 512 bytes large */ #define UPGT_MEMSIZE_FRAME_HEAD 0x0070 #define UPGT_MEMSIZE_RX 0x3500 #define UPGT_TX_COUNT 2 /* device flags */ #define UPGT_DEVICE_ATTACHED (1 << 0) /* leds */ #define UPGT_LED_OFF 0 #define UPGT_LED_ON 1 #define UPGT_LED_BLINK 2 /* * Firmware. */ #define UPGT_FW_BLOCK_SIZE 512 #define UPGT_BRA_FWTYPE_SIZE 4 #define UPGT_BRA_FWTYPE_LM86 "LM86" #define UPGT_BRA_FWTYPE_LM87 "LM87" enum upgt_fw_type { UPGT_FWTYPE_LM86, UPGT_FWTYPE_LM87 }; #define UPGT_BRA_TYPE_FW 0x80000001 #define UPGT_BRA_TYPE_VERSION 0x80000002 #define UPGT_BRA_TYPE_DEPIF 0x80000003 #define UPGT_BRA_TYPE_EXPIF 0x80000004 #define UPGT_BRA_TYPE_DESCR 0x80000101 #define UPGT_BRA_TYPE_END 0xff0000ff struct upgt_fw_bra_option { uint32_t type; uint32_t len; uint8_t data[]; } __packed; struct upgt_fw_bra_descr { uint32_t unknown1; uint32_t memaddr_space_start; uint32_t memaddr_space_end; uint32_t unknown2; uint32_t unknown3; uint8_t rates[20]; } __packed; #define UPGT_X2_SIGNATURE_SIZE 4 #define UPGT_X2_SIGNATURE "x2 " struct upgt_fw_x2_header { uint8_t signature[4]; uint32_t startaddr; uint32_t len; uint32_t crc; } __packed; /* * EEPROM. */ #define UPGT_EEPROM_SIZE 8192 #define UPGT_EEPROM_BLOCK_SIZE 1020 struct upgt_eeprom_header { /* 14 bytes */ uint32_t magic; uint16_t pad1; uint16_t preamble_len; uint32_t pad2; /* data */ } __packed; #define UPGT_EEPROM_TYPE_END 0x0000 #define UPGT_EEPROM_TYPE_NAME 0x0001 #define UPGT_EEPROM_TYPE_SERIAL 0x0003 #define UPGT_EEPROM_TYPE_MAC 0x0101 #define UPGT_EEPROM_TYPE_HWRX 0x1001 #define UPGT_EEPROM_TYPE_CHIP 0x1002 #define UPGT_EEPROM_TYPE_FREQ3 0x1903 #define UPGT_EEPROM_TYPE_FREQ4 0x1904 #define UPGT_EEPROM_TYPE_FREQ5 0x1905 #define UPGT_EEPROM_TYPE_FREQ6 0x1906 #define UPGT_EEPROM_TYPE_OFF 0xffff struct upgt_eeprom_option { uint16_t len; uint16_t type; uint8_t data[]; /* data */ } __packed; #define UPGT_EEPROM_RX_CONST 0x88 struct upgt_eeprom_option_hwrx { uint32_t pad1; uint8_t rxfilter; uint8_t pad2[15]; } __packed; struct upgt_eeprom_freq3_header { uint8_t flags; uint8_t elements; } __packed; struct upgt_eeprom_freq4_header { uint8_t flags; uint8_t elements; uint8_t settings; uint8_t type; } __packed; struct upgt_eeprom_freq4_1 { uint16_t freq; uint8_t data[50]; } __packed; struct upgt_eeprom_freq4_2 { uint16_t head; uint8_t subtails[4]; uint8_t tail; } __packed; /* * LMAC protocol. */ struct upgt_lmac_mem { uint32_t addr; uint32_t chksum; } __packed; #define UPGT_H1_FLAGS_TX_MGMT 0x00 /* for TX: mgmt frame */ #define UPGT_H1_FLAGS_TX_NO_CALLBACK 0x01 /* for TX: no USB callback */ #define UPGT_H1_FLAGS_TX_DATA 0x10 /* for TX: data frame */ #define UPGT_H1_TYPE_RX_DATA 0x00 /* 802.11 RX data frame */ #define UPGT_H1_TYPE_RX_DATA_MGMT 0x04 /* 802.11 RX mgmt frame */ #define UPGT_H1_TYPE_TX_DATA 0x40 /* 802.11 TX data frame */ #define UPGT_H1_TYPE_CTRL 0x80 /* control frame */ struct upgt_lmac_h1 { /* 4 bytes */ uint8_t flags; uint8_t type; uint16_t len; } __packed; #define UPGT_H2_TYPE_TX_ACK_NO 0x0000 #define UPGT_H2_TYPE_TX_ACK_YES 0x0001 #define UPGT_H2_TYPE_MACFILTER 0x0000 #define UPGT_H2_TYPE_CHANNEL 0x0001 #define UPGT_H2_TYPE_TX_DONE 0x0008 #define UPGT_H2_TYPE_STATS 0x000a #define UPGT_H2_TYPE_EEPROM 0x000c #define UPGT_H2_TYPE_LED 0x000d #define UPGT_H2_FLAGS_TX_ACK_NO 0x0101 #define UPGT_H2_FLAGS_TX_ACK_YES 0x0707 struct upgt_lmac_h2 { /* 8 bytes */ uint32_t reqid; uint16_t type; uint16_t flags; } __packed; struct upgt_lmac_header { /* 12 bytes */ struct upgt_lmac_h1 header1; struct upgt_lmac_h2 header2; } __packed; struct upgt_lmac_eeprom { /* 16 bytes */ struct upgt_lmac_h1 header1; struct upgt_lmac_h2 header2; uint16_t offset; uint16_t len; /* data */ } __packed; #define UPGT_FILTER_TYPE_NONE 0x0000 #define UPGT_FILTER_TYPE_STA 0x0001 #define UPGT_FILTER_TYPE_IBSS 0x0002 #define UPGT_FILTER_TYPE_HOSTAP 0x0004 #define UPGT_FILTER_TYPE_MONITOR 0x0010 #define UPGT_FILTER_TYPE_RESET 0x0020 #define UPGT_FILTER_UNKNOWN1 0x0002 #define UPGT_FILTER_UNKNOWN2 0x0ca8 #define UPGT_FILTER_UNKNOWN3 0xffff #define UPGT_FILTER_MONITOR_UNKNOWN1 0x0000 #define UPGT_FILTER_MONITOR_UNKNOWN2 0x0000 #define UPGT_FILTER_MONITOR_UNKNOWN3 0x0000 struct upgt_lmac_filter { struct upgt_lmac_h1 header1; struct upgt_lmac_h2 header2; /* 32 bytes */ uint16_t type; uint8_t dst[IEEE80211_ADDR_LEN]; uint8_t src[IEEE80211_ADDR_LEN]; uint16_t unknown1; uint32_t rxaddr; uint16_t unknown2; uint32_t rxhw; uint16_t unknown3; uint32_t unknown4; } __packed; /* frequence 3 data */ struct upgt_lmac_freq3 { uint16_t freq; uint8_t data[6]; } __packed; /* frequence 4 data */ struct upgt_lmac_freq4 { struct upgt_eeprom_freq4_2 cmd; uint8_t pad; }; /* frequence 6 data */ struct upgt_lmac_freq6 { uint16_t freq; uint8_t data[8]; } __packed; #define UPGT_CHANNEL_UNKNOWN1 0x0001 #define UPGT_CHANNEL_UNKNOWN2 0x0000 #define UPGT_CHANNEL_UNKNOWN3 0x48 struct upgt_lmac_channel { struct upgt_lmac_h1 header1; struct upgt_lmac_h2 header2; /* 112 bytes */ uint16_t unknown1; uint16_t unknown2; uint8_t pad1[20]; struct upgt_lmac_freq6 freq6; uint8_t settings; uint8_t unknown3; uint8_t freq3_1[4]; struct upgt_lmac_freq4 freq4[8]; uint8_t freq3_2[4]; uint32_t pad2; } __packed; #define UPGT_LED_MODE_SET 0x0003 #define UPGT_LED_ACTION_OFF 0x0002 #define UPGT_LED_ACTION_ON 0x0003 #define UPGT_LED_ACTION_TMP_DUR 100 /* ms */ struct upgt_lmac_led { struct upgt_lmac_h1 header1; struct upgt_lmac_h2 header2; uint16_t mode; uint16_t action_fix; uint16_t action_tmp; uint16_t action_tmp_dur; } __packed; struct upgt_lmac_stats { struct upgt_lmac_h1 header1; struct upgt_lmac_h2 header2; uint8_t data[76]; } __packed; struct upgt_lmac_rx_desc { struct upgt_lmac_h1 header1; /* 16 bytes */ uint16_t freq; uint8_t unknown1; uint8_t rate; uint8_t rssi; uint8_t pad; uint16_t unknown2; uint32_t timestamp; uint32_t unknown3; uint8_t data[]; } __packed; #define UPGT_TX_DESC_KEY_EXISTS 0x01 struct upgt_lmac_tx_desc_wep { uint8_t key_exists; uint8_t key_len; uint8_t key_val[16]; } __packed; #define UPGT_TX_DESC_TYPE_BEACON 0x00000000 #define UPGT_TX_DESC_TYPE_PROBE 0x00000001 #define UPGT_TX_DESC_TYPE_MGMT 0x00000002 #define UPGT_TX_DESC_TYPE_DATA 0x00000004 #define UPGT_TX_DESC_PAD3_SIZE 2 struct upgt_lmac_tx_desc { struct upgt_lmac_h1 header1; struct upgt_lmac_h2 header2; uint8_t rates[8]; uint16_t pad1; struct upgt_lmac_tx_desc_wep wep_key; uint32_t type; uint32_t pad2; uint32_t unknown1; uint32_t unknown2; uint8_t pad3[2]; /* 802.11 frame data */ } __packed; #define UPGT_TX_DONE_DESC_STATUS_OK 0x0001 struct upgt_lmac_tx_done_desc { struct upgt_lmac_h1 header1; struct upgt_lmac_h2 header2; uint16_t status; uint16_t rssi; uint16_t seq; uint16_t unknown; } __packed; /* * USB xfers. */ struct upgt_data { struct upgt_softc *sc; usbd_xfer_handle xfer; uint8_t *buf; struct ieee80211_node *ni; struct mbuf *m; uint32_t addr; uint8_t use; }; /* * Prism memory. */ struct upgt_memory_page { uint8_t used; uint32_t addr; } __packed; #define UPGT_MEMORY_MAX_PAGES 8 struct upgt_memory { uint8_t pages; struct upgt_memory_page page[UPGT_MEMORY_MAX_PAGES]; } __packed; /* * BPF */ struct upgt_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint8_t wr_flags; uint8_t wr_rate; uint16_t wr_chan_freq; uint16_t wr_chan_flags; int8_t wr_antsignal; } __packed; #define UPGT_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) struct upgt_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; uint8_t wt_rate; uint16_t wt_chan_freq; uint16_t wt_chan_flags; } __packed; #define UPGT_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL)) struct upgt_vap { struct ieee80211vap vap; int (*newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define UPGT_VAP(vap) ((struct upgt_vap *)(vap)) struct upgt_softc { device_t sc_dev; struct ifnet *sc_ifp; usbd_device_handle sc_udev; usbd_interface_handle sc_iface; struct mtx sc_mtx; int sc_if_flags; int sc_debug; struct usb_task sc_mcasttask; - struct usb_task sc_task; struct usb_task sc_scantask; #define UPGT_SET_CHANNEL 2 int sc_scan_action; - enum ieee80211_state sc_state; - int sc_arg; int sc_led_blink; struct callout sc_led_ch; uint8_t sc_cur_rateset[8]; /* watchdog */ int sc_tx_timer; struct callout sc_watchdog_ch; /* Firmware. */ int sc_fw_type; /* memory addresses on device */ uint32_t sc_memaddr_frame_start; uint32_t sc_memaddr_frame_end; uint32_t sc_memaddr_rx_start; struct upgt_memory sc_memory; /* data which we found in the EEPROM */ uint8_t sc_eeprom[UPGT_EEPROM_SIZE]; uint16_t sc_eeprom_hwrx; struct upgt_lmac_freq3 sc_eeprom_freq3[IEEE80211_CHAN_MAX]; struct upgt_lmac_freq4 sc_eeprom_freq4[IEEE80211_CHAN_MAX][8]; struct upgt_lmac_freq6 sc_eeprom_freq6[IEEE80211_CHAN_MAX]; uint8_t sc_eeprom_freq6_settings; /* RX/TX */ int sc_rx_no; int sc_tx_no; usbd_pipe_handle sc_rx_pipeh; usbd_pipe_handle sc_tx_pipeh; struct upgt_data tx_data[UPGT_TX_COUNT]; struct upgt_data rx_data; struct upgt_data cmd_data; int tx_queued; struct usb_task sc_task_tx; /* BPF */ struct upgt_rx_radiotap_header sc_rxtap; int sc_rxtap_len; struct upgt_tx_radiotap_header sc_txtap; int sc_txtap_len; }; #define UPGT_LOCK(sc) mtx_lock(&(sc)->sc_mtx) #define UPGT_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) Index: user/thompsa/vaptq/sys/dev/usb/if_ural.c =================================================================== --- user/thompsa/vaptq/sys/dev/usb/if_ural.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/usb/if_ural.c (revision 185733) @@ -1,2505 +1,2479 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2005, 2006 * Damien Bergamini * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /*- * Ralink Technology RT2500USB chipset driver * http://www.ralinktech.com/ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #ifdef USB_DEBUG #define DPRINTF(x) do { if (uraldebug > 0) printf x; } while (0) #define DPRINTFN(n, x) do { if (uraldebug >= (n)) printf x; } while (0) int uraldebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ural, CTLFLAG_RW, 0, "USB ural"); SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RW, &uraldebug, 0, "ural debug level"); #else #define DPRINTF(x) #define DPRINTFN(n, x) #endif #define URAL_RSSI(rssi) \ ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \ ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0) /* various supported device vendors/products */ static const struct usb_devno ural_devs[] = { { USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL167G }, { USB_VENDOR_ASUS, USB_PRODUCT_RALINK_RT2570 }, { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050 }, { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7051 }, { USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU }, { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG122 }, { USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWBKG }, { USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GN54G }, { USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254 }, { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54G }, { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GP }, { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_HU200TS }, { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54 }, { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54AI }, { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54YB }, { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_NINWIFI }, { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570 }, { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_2 }, { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_3 }, { USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_NV902 }, { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570 }, { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_2 }, { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_3 }, { USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_WL54G }, { USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG }, { USB_VENDOR_SPHAIRON, USB_PRODUCT_SPHAIRON_UB801R}, { USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2570 }, { USB_VENDOR_VTECH, USB_PRODUCT_VTECH_RT2570 }, { USB_VENDOR_ZINWELL, USB_PRODUCT_ZINWELL_RT2570 } }; MODULE_DEPEND(ural, wlan, 1, 1, 1); MODULE_DEPEND(ural, wlan_amrr, 1, 1, 1); MODULE_DEPEND(ural, usb, 1, 1, 1); static struct ieee80211vap *ural_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void ural_vap_delete(struct ieee80211vap *); static int ural_alloc_tx_list(struct ural_softc *); static void ural_free_tx_list(struct ural_softc *); static int ural_alloc_rx_list(struct ural_softc *); static void ural_free_rx_list(struct ural_softc *); -static void ural_task(void *); static void ural_scantask(void *); static int ural_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void ural_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void ural_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void ural_setup_tx_desc(struct ural_softc *, struct ural_tx_desc *, uint32_t, int, int); static int ural_tx_bcn(struct ural_softc *, struct mbuf *, struct ieee80211_node *); static int ural_tx_mgt(struct ural_softc *, struct mbuf *, struct ieee80211_node *); static int ural_tx_data(struct ural_softc *, struct mbuf *, struct ieee80211_node *); static void ural_start(struct ifnet *); static void ural_watchdog(void *); static int ural_ioctl(struct ifnet *, u_long, caddr_t); static void ural_set_testmode(struct ural_softc *); static void ural_eeprom_read(struct ural_softc *, uint16_t, void *, int); static uint16_t ural_read(struct ural_softc *, uint16_t); static void ural_read_multi(struct ural_softc *, uint16_t, void *, int); static void ural_write(struct ural_softc *, uint16_t, uint16_t); static void ural_write_multi(struct ural_softc *, uint16_t, void *, int) __unused; static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t); static uint8_t ural_bbp_read(struct ural_softc *, uint8_t); static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t); static struct ieee80211_node *ural_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); static void ural_newassoc(struct ieee80211_node *, int); static void ural_scan_start(struct ieee80211com *); static void ural_scan_end(struct ieee80211com *); static void ural_set_channel(struct ieee80211com *); static void ural_set_chan(struct ural_softc *, struct ieee80211_channel *); static void ural_disable_rf_tune(struct ural_softc *); static void ural_enable_tsf_sync(struct ural_softc *); static void ural_update_slot(struct ifnet *); static void ural_set_txpreamble(struct ural_softc *); static void ural_set_basicrates(struct ural_softc *, const struct ieee80211_channel *); static void ural_set_bssid(struct ural_softc *, const uint8_t *); static void ural_set_macaddr(struct ural_softc *, uint8_t *); static void ural_update_promisc(struct ural_softc *); static const char *ural_get_rf(int); static void ural_read_eeprom(struct ural_softc *); static int ural_bbp_init(struct ural_softc *); static void ural_set_txantenna(struct ural_softc *, int); static void ural_set_rxantenna(struct ural_softc *, int); static void ural_init_locked(struct ural_softc *); static void ural_init(void *); static void ural_stop(void *); static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void ural_amrr_start(struct ural_softc *, struct ieee80211_node *); static void ural_amrr_timeout(void *); static void ural_amrr_update(usbd_xfer_handle, usbd_private_handle, usbd_status status); /* * Default values for MAC registers; values taken from the reference driver. */ static const struct { uint16_t reg; uint16_t val; } ural_def_mac[] = { { RAL_TXRX_CSR5, 0x8c8d }, { RAL_TXRX_CSR6, 0x8b8a }, { RAL_TXRX_CSR7, 0x8687 }, { RAL_TXRX_CSR8, 0x0085 }, { RAL_MAC_CSR13, 0x1111 }, { RAL_MAC_CSR14, 0x1e11 }, { RAL_TXRX_CSR21, 0xe78f }, { RAL_MAC_CSR9, 0xff1d }, { RAL_MAC_CSR11, 0x0002 }, { RAL_MAC_CSR22, 0x0053 }, { RAL_MAC_CSR15, 0x0000 }, { RAL_MAC_CSR8, 0x0780 }, { RAL_TXRX_CSR19, 0x0000 }, { RAL_TXRX_CSR18, 0x005a }, { RAL_PHY_CSR2, 0x0000 }, { RAL_TXRX_CSR0, 0x1ec0 }, { RAL_PHY_CSR4, 0x000f } }; /* * Default values for BBP registers; values taken from the reference driver. */ static const struct { uint8_t reg; uint8_t val; } ural_def_bbp[] = { { 3, 0x02 }, { 4, 0x19 }, { 14, 0x1c }, { 15, 0x30 }, { 16, 0xac }, { 17, 0x48 }, { 18, 0x18 }, { 19, 0xff }, { 20, 0x1e }, { 21, 0x08 }, { 22, 0x08 }, { 23, 0x08 }, { 24, 0x80 }, { 25, 0x50 }, { 26, 0x08 }, { 27, 0x23 }, { 30, 0x10 }, { 31, 0x2b }, { 32, 0xb9 }, { 34, 0x12 }, { 35, 0x50 }, { 39, 0xc4 }, { 40, 0x02 }, { 41, 0x60 }, { 53, 0x10 }, { 54, 0x18 }, { 56, 0x08 }, { 57, 0x10 }, { 58, 0x08 }, { 61, 0x60 }, { 62, 0x10 }, { 75, 0xff } }; /* * Default values for RF register R2 indexed by channel numbers. */ static const uint32_t ural_rf2522_r2[] = { 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814, 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e }; static const uint32_t ural_rf2523_r2[] = { 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 }; static const uint32_t ural_rf2524_r2[] = { 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 }; static const uint32_t ural_rf2525_r2[] = { 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d, 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346 }; static const uint32_t ural_rf2525_hi_r2[] = { 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345, 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e }; static const uint32_t ural_rf2525e_r2[] = { 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463, 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b }; static const uint32_t ural_rf2526_hi_r2[] = { 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d, 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241 }; static const uint32_t ural_rf2526_r2[] = { 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229, 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d }; /* * For dual-band RF, RF registers R1 and R4 also depend on channel number; * values taken from the reference driver. */ static const struct { uint8_t chan; uint32_t r1; uint32_t r2; uint32_t r4; } ural_rf5222[] = { { 1, 0x08808, 0x0044d, 0x00282 }, { 2, 0x08808, 0x0044e, 0x00282 }, { 3, 0x08808, 0x0044f, 0x00282 }, { 4, 0x08808, 0x00460, 0x00282 }, { 5, 0x08808, 0x00461, 0x00282 }, { 6, 0x08808, 0x00462, 0x00282 }, { 7, 0x08808, 0x00463, 0x00282 }, { 8, 0x08808, 0x00464, 0x00282 }, { 9, 0x08808, 0x00465, 0x00282 }, { 10, 0x08808, 0x00466, 0x00282 }, { 11, 0x08808, 0x00467, 0x00282 }, { 12, 0x08808, 0x00468, 0x00282 }, { 13, 0x08808, 0x00469, 0x00282 }, { 14, 0x08808, 0x0046b, 0x00286 }, { 36, 0x08804, 0x06225, 0x00287 }, { 40, 0x08804, 0x06226, 0x00287 }, { 44, 0x08804, 0x06227, 0x00287 }, { 48, 0x08804, 0x06228, 0x00287 }, { 52, 0x08804, 0x06229, 0x00287 }, { 56, 0x08804, 0x0622a, 0x00287 }, { 60, 0x08804, 0x0622b, 0x00287 }, { 64, 0x08804, 0x0622c, 0x00287 }, { 100, 0x08804, 0x02200, 0x00283 }, { 104, 0x08804, 0x02201, 0x00283 }, { 108, 0x08804, 0x02202, 0x00283 }, { 112, 0x08804, 0x02203, 0x00283 }, { 116, 0x08804, 0x02204, 0x00283 }, { 120, 0x08804, 0x02205, 0x00283 }, { 124, 0x08804, 0x02206, 0x00283 }, { 128, 0x08804, 0x02207, 0x00283 }, { 132, 0x08804, 0x02208, 0x00283 }, { 136, 0x08804, 0x02209, 0x00283 }, { 140, 0x08804, 0x0220a, 0x00283 }, { 149, 0x08808, 0x02429, 0x00281 }, { 153, 0x08808, 0x0242b, 0x00281 }, { 157, 0x08808, 0x0242d, 0x00281 }, { 161, 0x08808, 0x0242f, 0x00281 } }; static device_probe_t ural_match; static device_attach_t ural_attach; static device_detach_t ural_detach; static device_method_t ural_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ural_match), DEVMETHOD(device_attach, ural_attach), DEVMETHOD(device_detach, ural_detach), { 0, 0 } }; static driver_t ural_driver = { "ural", ural_methods, sizeof(struct ural_softc) }; static devclass_t ural_devclass; DRIVER_MODULE(ural, uhub, ural_driver, ural_devclass, usbd_driver_load, 0); static int ural_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->iface != NULL) return UMATCH_NONE; return (usb_lookup(ural_devs, uaa->vendor, uaa->product) != NULL) ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE; } static int ural_attach(device_t self) { struct ural_softc *sc = device_get_softc(self); struct usb_attach_arg *uaa = device_get_ivars(self); struct ifnet *ifp; struct ieee80211com *ic; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; usbd_status error; int i; uint8_t bands; sc->sc_udev = uaa->device; sc->sc_dev = self; if (usbd_set_config_no(sc->sc_udev, RAL_CONFIG_NO, 0) != 0) { device_printf(self, "could not set configuration no\n"); return ENXIO; } /* get the first interface handle */ error = usbd_device2interface_handle(sc->sc_udev, RAL_IFACE_INDEX, &sc->sc_iface); if (error != 0) { device_printf(self, "could not get interface handle\n"); return ENXIO; } /* * Find endpoints. */ id = usbd_get_interface_descriptor(sc->sc_iface); sc->sc_rx_no = sc->sc_tx_no = -1; for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); if (ed == NULL) { device_printf(self, "no endpoint descriptor for %d\n", i); return ENXIO; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) sc->sc_rx_no = ed->bEndpointAddress; else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) sc->sc_tx_no = ed->bEndpointAddress; } if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) { device_printf(self, "missing endpoint\n"); return ENXIO; } ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(sc->sc_dev, "can not if_alloc()\n"); return ENXIO; } ic = ifp->if_l2com; mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); - usb_init_task(&sc->sc_task, ural_task, sc); usb_init_task(&sc->sc_scantask, ural_scantask, sc); callout_init(&sc->watchdog_ch, 0); /* retrieve RT2570 rev. no */ sc->asic_rev = ural_read(sc, RAL_MAC_CSR0); /* retrieve MAC address and various other things from EEPROM */ ural_read_eeprom(sc); device_printf(sc->sc_dev, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n", sc->asic_rev, ural_get_rf(sc->rf_rev)); ifp->if_softc = sc; if_initname(ifp, "ural", device_get_unit(sc->sc_dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_NEEDSGIANT; /* USB stack is still under Giant lock */ ifp->if_init = ural_init; ifp->if_ioctl = ural_ioctl; ifp->if_start = ural_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_HOSTAP /* HostAp mode supported */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* bg scanning supported */ | IEEE80211_C_WPA /* 802.11i */ ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); if (sc->rf_rev == RAL_RF_5222) setbit(&bands, IEEE80211_MODE_11A); ieee80211_init_channels(ic, NULL, &bands); ieee80211_ifattach(ic); ic->ic_newassoc = ural_newassoc; ic->ic_raw_xmit = ural_raw_xmit; ic->ic_node_alloc = ural_node_alloc; ic->ic_scan_start = ural_scan_start; ic->ic_scan_end = ural_scan_end; ic->ic_set_channel = ural_set_channel; ic->ic_vap_create = ural_vap_create; ic->ic_vap_delete = ural_vap_delete; sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap)); sc->sc_rxtap_len = sizeof sc->sc_rxtap; sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(RAL_RX_RADIOTAP_PRESENT); sc->sc_txtap_len = sizeof sc->sc_txtap; sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(RAL_TX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); return 0; } static int ural_detach(device_t self) { struct ural_softc *sc = device_get_softc(self); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; ural_stop(sc); bpfdetach(ifp); ieee80211_ifdetach(ic); - usb_rem_task(sc->sc_udev, &sc->sc_task); usb_rem_task(sc->sc_udev, &sc->sc_scantask); callout_stop(&sc->watchdog_ch); if (sc->amrr_xfer != NULL) { usbd_free_xfer(sc->amrr_xfer); sc->amrr_xfer = NULL; } if (sc->sc_rx_pipeh != NULL) { usbd_abort_pipe(sc->sc_rx_pipeh); usbd_close_pipe(sc->sc_rx_pipeh); } if (sc->sc_tx_pipeh != NULL) { usbd_abort_pipe(sc->sc_tx_pipeh); usbd_close_pipe(sc->sc_tx_pipeh); } ural_free_rx_list(sc); ural_free_tx_list(sc); if_free(ifp); mtx_destroy(&sc->sc_mtx); return 0; } static struct ieee80211vap * ural_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ural_vap *uvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; uvp = (struct ural_vap *) malloc(sizeof(struct ural_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (uvp == NULL) return NULL; vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = ural_newstate; callout_init(&uvp->amrr_ch, 0); ieee80211_amrr_init(&uvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 1000 /* 1 sec */); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return vap; } static void ural_vap_delete(struct ieee80211vap *vap) { struct ural_vap *uvp = URAL_VAP(vap); callout_stop(&uvp->amrr_ch); ieee80211_amrr_cleanup(&uvp->amrr); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static int ural_alloc_tx_list(struct ural_softc *sc) { struct ural_tx_data *data; int i, error; sc->tx_queued = sc->tx_cur = 0; for (i = 0; i < RAL_TX_LIST_COUNT; i++) { data = &sc->tx_data[i]; data->sc = sc; data->xfer = usbd_alloc_xfer(sc->sc_udev); if (data->xfer == NULL) { device_printf(sc->sc_dev, "could not allocate tx xfer\n"); error = ENOMEM; goto fail; } data->buf = usbd_alloc_buffer(data->xfer, RAL_TX_DESC_SIZE + MCLBYTES); if (data->buf == NULL) { device_printf(sc->sc_dev, "could not allocate tx buffer\n"); error = ENOMEM; goto fail; } } return 0; fail: ural_free_tx_list(sc); return error; } static void ural_free_tx_list(struct ural_softc *sc) { struct ural_tx_data *data; int i; for (i = 0; i < RAL_TX_LIST_COUNT; i++) { data = &sc->tx_data[i]; if (data->xfer != NULL) { usbd_free_xfer(data->xfer); data->xfer = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } } } static int ural_alloc_rx_list(struct ural_softc *sc) { struct ural_rx_data *data; int i, error; for (i = 0; i < RAL_RX_LIST_COUNT; i++) { data = &sc->rx_data[i]; data->sc = sc; data->xfer = usbd_alloc_xfer(sc->sc_udev); if (data->xfer == NULL) { device_printf(sc->sc_dev, "could not allocate rx xfer\n"); error = ENOMEM; goto fail; } if (usbd_alloc_buffer(data->xfer, MCLBYTES) == NULL) { device_printf(sc->sc_dev, "could not allocate rx buffer\n"); error = ENOMEM; goto fail; } data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (data->m == NULL) { device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); error = ENOMEM; goto fail; } data->buf = mtod(data->m, uint8_t *); } return 0; fail: ural_free_rx_list(sc); return error; } static void ural_free_rx_list(struct ural_softc *sc) { struct ural_rx_data *data; int i; for (i = 0; i < RAL_RX_LIST_COUNT; i++) { data = &sc->rx_data[i]; if (data->xfer != NULL) { usbd_free_xfer(data->xfer); data->xfer = NULL; } if (data->m != NULL) { m_freem(data->m); data->m = NULL; } } } static void -ural_task(void *xarg) +ural_scantask(void *arg) { - struct ural_softc *sc = xarg; + struct ural_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + RAL_LOCK(sc); + if (sc->sc_scan_action == URAL_SCAN_START) { + /* abort TSF synchronization */ + ural_write(sc, RAL_TXRX_CSR19, 0); + ural_set_bssid(sc, ifp->if_broadcastaddr); + } else if (sc->sc_scan_action == URAL_SET_CHANNEL) { + mtx_lock(&Giant); + ural_set_chan(sc, ic->ic_curchan); + mtx_unlock(&Giant); + } else { + ural_enable_tsf_sync(sc); + /* XXX keep local copy */ + ural_set_bssid(sc, vap->iv_bss->ni_bssid); + } + RAL_UNLOCK(sc); +} + +static int +ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ struct ural_vap *uvp = URAL_VAP(vap); - const struct ieee80211_txparam *tp; + struct ieee80211com *ic = vap->iv_ic; + struct ural_softc *sc = ic->ic_ifp->if_softc; enum ieee80211_state ostate; + const struct ieee80211_txparam *tp; struct ieee80211_node *ni; struct mbuf *m; + callout_stop(&uvp->amrr_ch); ostate = vap->iv_state; + DPRINTF(("%s: %s -> %s\n", __func__, + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate])); + + IEEE80211_UNLOCK(ic); RAL_LOCK(sc); - switch (sc->sc_state) { + switch (nstate) { case IEEE80211_S_INIT: if (ostate == IEEE80211_S_RUN) { /* abort TSF synchronization */ ural_write(sc, RAL_TXRX_CSR19, 0); /* force tx led to stop blinking */ ural_write(sc, RAL_MAC_CSR20, 0); } break; case IEEE80211_S_RUN: ni = vap->iv_bss; if (vap->iv_opmode != IEEE80211_M_MONITOR) { ural_update_slot(ic->ic_ifp); ural_set_txpreamble(sc); ural_set_basicrates(sc, ic->ic_bsschan); ural_set_bssid(sc, ni->ni_bssid); } if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_IBSS) { m = ieee80211_beacon_alloc(ni, &uvp->bo); if (m == NULL) { device_printf(sc->sc_dev, "could not allocate beacon\n"); - return; + return ENXIO; } if (ural_tx_bcn(sc, m, ni) != 0) { device_printf(sc->sc_dev, "could not send beacon\n"); - return; + return ENXIO; } } /* make tx led blink on tx (controlled by ASIC) */ ural_write(sc, RAL_MAC_CSR20, 1); if (vap->iv_opmode != IEEE80211_M_MONITOR) ural_enable_tsf_sync(sc); /* enable automatic rate adaptation */ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) ural_amrr_start(sc, ni); break; default: break; } - RAL_UNLOCK(sc); - IEEE80211_LOCK(ic); - uvp->newstate(vap, sc->sc_state, sc->sc_arg); - if (vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg); - IEEE80211_UNLOCK(ic); -} - -static void -ural_scantask(void *arg) -{ - struct ural_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - RAL_LOCK(sc); - if (sc->sc_scan_action == URAL_SCAN_START) { - /* abort TSF synchronization */ - ural_write(sc, RAL_TXRX_CSR19, 0); - ural_set_bssid(sc, ifp->if_broadcastaddr); - } else if (sc->sc_scan_action == URAL_SET_CHANNEL) { - mtx_lock(&Giant); - ural_set_chan(sc, ic->ic_curchan); - mtx_unlock(&Giant); - } else { - ural_enable_tsf_sync(sc); - /* XXX keep local copy */ - ural_set_bssid(sc, vap->iv_bss->ni_bssid); - } - RAL_UNLOCK(sc); -} - -static int -ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - struct ural_vap *uvp = URAL_VAP(vap); - struct ieee80211com *ic = vap->iv_ic; - struct ural_softc *sc = ic->ic_ifp->if_softc; - - callout_stop(&uvp->amrr_ch); - - /* do it in a process context */ - sc->sc_state = nstate; - sc->sc_arg = arg; - - usb_rem_task(sc->sc_udev, &sc->sc_task); - if (nstate == IEEE80211_S_INIT) { - uvp->newstate(vap, nstate, arg); - return 0; - } else { - usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER); - return EINPROGRESS; - } + uvp->newstate(vap, nstate, arg); + return 0; } #define RAL_RXTX_TURNAROUND 5 /* us */ static void ural_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct ural_tx_data *data = priv; struct ural_softc *sc = data->sc; struct ifnet *ifp = sc->sc_ifp; if (data->m->m_flags & M_TXCB) ieee80211_process_callback(data->ni, data->m, status == USBD_NORMAL_COMPLETION ? 0 : ETIMEDOUT); if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; device_printf(sc->sc_dev, "could not transmit buffer: %s\n", usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh); ifp->if_oerrors++; /* XXX mbuf leak? */ return; } m_freem(data->m); data->m = NULL; ieee80211_free_node(data->ni); data->ni = NULL; sc->tx_queued--; ifp->if_opackets++; DPRINTFN(10, ("tx done\n")); sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ural_start(ifp); } static void ural_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct ural_rx_data *data = priv; struct ural_softc *sc = data->sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ural_rx_desc *desc; struct ieee80211_node *ni; struct mbuf *mnew, *m; int len, rssi; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh); goto skip; } usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL); if (len < RAL_RX_DESC_SIZE + IEEE80211_MIN_LEN) { DPRINTF(("%s: xfer too short %d\n", device_get_nameunit(sc->sc_dev), len)); ifp->if_ierrors++; goto skip; } /* rx descriptor is located at the end */ desc = (struct ural_rx_desc *)(data->buf + len - RAL_RX_DESC_SIZE); if ((le32toh(desc->flags) & RAL_RX_PHY_ERROR) || (le32toh(desc->flags) & RAL_RX_CRC_ERROR)) { /* * This should not happen since we did not request to receive * those frames when we filled RAL_TXRX_CSR2. */ DPRINTFN(5, ("PHY or CRC error\n")); ifp->if_ierrors++; goto skip; } mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) { ifp->if_ierrors++; goto skip; } m = data->m; data->m = mnew; data->buf = mtod(data->m, uint8_t *); /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff; if (bpf_peers_present(ifp->if_bpf)) { struct ural_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = IEEE80211_RADIOTAP_F_FCS; tap->wr_rate = ieee80211_plcp2rate(desc->rate, (desc->flags & htole32(RAL_RX_OFDM)) ? IEEE80211_T_OFDM : IEEE80211_T_CCK); tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_antenna = sc->rx_ant; tap->wr_antsignal = URAL_RSSI(desc->rssi); bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } /* Strip trailing 802.11 MAC FCS. */ m_adj(m, -IEEE80211_CRC_LEN); rssi = URAL_RSSI(desc->rssi); ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { (void) ieee80211_input(ni, m, rssi, RAL_NOISE_FLOOR, 0); ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, rssi, RAL_NOISE_FLOOR, 0); DPRINTFN(15, ("rx done\n")); skip: /* setup a new transfer */ usbd_setup_xfer(xfer, sc->sc_rx_pipeh, data, data->buf, MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, ural_rxeof); usbd_transfer(xfer); } static uint8_t ural_plcp_signal(int rate) { switch (rate) { /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return 0xb; case 18: return 0xf; case 24: return 0xa; case 36: return 0xe; case 48: return 0x9; case 72: return 0xd; case 96: return 0x8; case 108: return 0xc; /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return 0x0; case 4: return 0x1; case 11: return 0x2; case 22: return 0x3; } return 0xff; /* XXX unsupported/unknown rate */ } static void ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc, uint32_t flags, int len, int rate) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t plcp_length; int remainder; desc->flags = htole32(flags); desc->flags |= htole32(RAL_TX_NEWSEQ); desc->flags |= htole32(len << 16); desc->wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5)); desc->wme |= htole16(RAL_IVOFFSET(sizeof (struct ieee80211_frame))); /* setup PLCP fields */ desc->plcp_signal = ural_plcp_signal(rate); desc->plcp_service = 4; len += IEEE80211_CRC_LEN; if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) { desc->flags |= htole32(RAL_TX_OFDM); plcp_length = len & 0xfff; desc->plcp_length_hi = plcp_length >> 6; desc->plcp_length_lo = plcp_length & 0x3f; } else { plcp_length = (16 * len + rate - 1) / rate; if (rate == 22) { remainder = (16 * len) % 22; if (remainder != 0 && remainder < 7) desc->plcp_service |= RAL_PLCP_LENGEXT; } desc->plcp_length_hi = plcp_length >> 8; desc->plcp_length_lo = plcp_length & 0xff; if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) desc->plcp_signal |= 0x08; } desc->iv = 0; desc->eiv = 0; } #define RAL_TX_TIMEOUT 5000 static int ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ural_tx_desc *desc; usbd_xfer_handle xfer; uint8_t cmd; usbd_status error; uint8_t *buf; int xferlen; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == NULL) return ENOMEM; /* xfer length needs to be a multiple of two! */ xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1; buf = usbd_alloc_buffer(xfer, xferlen); if (buf == NULL) { usbd_free_xfer(xfer); return ENOMEM; } cmd = 0; usbd_setup_xfer(xfer, sc->sc_tx_pipeh, NULL, &cmd, sizeof cmd, USBD_FORCE_SHORT_XFER, RAL_TX_TIMEOUT, NULL); error = usbd_sync_transfer(xfer); if (error != 0) { usbd_free_xfer(xfer); return error; } desc = (struct ural_tx_desc *)buf; m_copydata(m0, 0, m0->m_pkthdr.len, buf + RAL_TX_DESC_SIZE); ural_setup_tx_desc(sc, desc, RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP, m0->m_pkthdr.len, tp->mgmtrate); DPRINTFN(10, ("sending beacon frame len=%u rate=%u xfer len=%u\n", m0->m_pkthdr.len, tp->mgmtrate, xferlen)); usbd_setup_xfer(xfer, sc->sc_tx_pipeh, NULL, buf, xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, NULL); error = usbd_sync_transfer(xfer); usbd_free_xfer(xfer); return error; } static int ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = sc->sc_ifp; const struct ieee80211_txparam *tp; struct ural_tx_desc *desc; struct ural_tx_data *data; struct ieee80211_frame *wh; struct ieee80211_key *k; uint32_t flags; uint16_t dur; usbd_status error; int xferlen; data = &sc->tx_data[sc->tx_cur]; desc = (struct ural_tx_desc *)data->buf; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } wh = mtod(m0, struct ieee80211_frame *); } data->m = m0; data->ni = ni; flags = 0; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RAL_TX_ACK; dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); *(uint16_t *)wh->i_dur = htole16(dur); /* tell hardware to add timestamp for probe responses */ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT && (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= RAL_TX_TIMESTAMP; } if (bpf_peers_present(ifp->if_bpf)) { struct ural_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = tp->mgmtrate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wt_antenna = sc->tx_ant; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE); ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, tp->mgmtrate); /* align end on a 2-bytes boundary */ xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1; /* * No space left in the last URB to store the extra 2 bytes, force * sending of another URB. */ if ((xferlen % 64) == 0) xferlen += 2; DPRINTFN(10, ("sending mgt frame len=%u rate=%u xfer len=%u\n", m0->m_pkthdr.len, tp->mgmtrate, xferlen)); usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, ural_txeof); error = usbd_transfer(data->xfer); if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) { m_freem(m0); data->m = NULL; data->ni = NULL; return error; } sc->tx_queued++; sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT; return 0; } static int ural_sendprot(struct ural_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) { struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_frame *wh; struct ural_tx_desc *desc; struct ural_tx_data *data; struct mbuf *mprot; int protrate, ackrate, pktlen, flags, isshort; uint16_t dur; usbd_status error; KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, ("protection %d", prot)); wh = mtod(m, const struct ieee80211_frame *); pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; protrate = ieee80211_ctl_rate(sc->sc_rates, rate); ackrate = ieee80211_ack_rate(sc->sc_rates, rate); isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort); + ieee80211_ack_duration(sc->sc_rates, rate, isshort); flags = RAL_TX_RETRY(7); if (prot == IEEE80211_PROT_RTSCTS) { /* NB: CTS is the same size as an ACK */ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort); flags |= RAL_TX_ACK; mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); } else { mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); } if (mprot == NULL) { /* XXX stat + msg */ return ENOBUFS; } data = &sc->tx_data[sc->tx_cur]; desc = (struct ural_tx_desc *)data->buf; data->m = mprot; data->ni = ieee80211_ref_node(ni); m_copydata(mprot, 0, mprot->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE); ural_setup_tx_desc(sc, desc, flags, mprot->m_pkthdr.len, protrate); usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, /* NB: no roundup necessary */ RAL_TX_DESC_SIZE + mprot->m_pkthdr.len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, ural_txeof); error = usbd_transfer(data->xfer); if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) { data->m = NULL; data->ni = NULL; return error; } sc->tx_queued++; sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT; return 0; } static int ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ural_tx_desc *desc; struct ural_tx_data *data; uint32_t flags; usbd_status error; int xferlen, rate; KASSERT(params != NULL, ("no raw xmit params")); data = &sc->tx_data[sc->tx_cur]; desc = (struct ural_tx_desc *)data->buf; rate = params->ibp_rate0 & IEEE80211_RATE_VAL; /* XXX validate */ if (rate == 0) { m_freem(m0); return EINVAL; } flags = 0; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= RAL_TX_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { error = ural_sendprot(sc, m0, ni, params->ibp_flags & IEEE80211_BPF_RTS ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); if (error) { m_freem(m0); return error; } flags |= RAL_TX_IFS_SIFS; } if (bpf_peers_present(ifp->if_bpf)) { struct ural_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wt_antenna = sc->tx_ant; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } data->m = m0; data->ni = ni; m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE); /* XXX need to setup descriptor ourself */ ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate); /* align end on a 2-bytes boundary */ xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1; /* * No space left in the last URB to store the extra 2 bytes, force * sending of another URB. */ if ((xferlen % 64) == 0) xferlen += 2; DPRINTFN(10, ("sending raw frame len=%u rate=%u xfer len=%u\n", m0->m_pkthdr.len, rate, xferlen)); usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, ural_txeof); error = usbd_transfer(data->xfer); if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) { m_freem(m0); data->m = NULL; data->ni = NULL; return error; } sc->tx_queued++; sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT; return 0; } static int ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = sc->sc_ifp; struct ural_tx_desc *desc; struct ural_tx_data *data; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp; struct ieee80211_key *k; uint32_t flags = 0; uint16_t dur; usbd_status error; int xferlen, rate; wh = mtod(m0, struct ieee80211_frame *); tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else rate = ni->ni_txrate; if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { int prot = IEEE80211_PROT_NONE; if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) prot = IEEE80211_PROT_RTSCTS; else if ((ic->ic_flags & IEEE80211_F_USEPROT) && ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) prot = ic->ic_protmode; if (prot != IEEE80211_PROT_NONE) { error = ural_sendprot(sc, m0, ni, prot, rate); if (error) { m_freem(m0); return error; } flags |= RAL_TX_IFS_SIFS; } } data = &sc->tx_data[sc->tx_cur]; desc = (struct ural_tx_desc *)data->buf; data->m = m0; data->ni = ni; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RAL_TX_ACK; flags |= RAL_TX_RETRY(7); dur = ieee80211_ack_duration(sc->sc_rates, rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); *(uint16_t *)wh->i_dur = htole16(dur); } if (bpf_peers_present(ifp->if_bpf)) { struct ural_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wt_antenna = sc->tx_ant; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE); ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate); /* align end on a 2-bytes boundary */ xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1; /* * No space left in the last URB to store the extra 2 bytes, force * sending of another URB. */ if ((xferlen % 64) == 0) xferlen += 2; DPRINTFN(10, ("sending data frame len=%u rate=%u xfer len=%u\n", m0->m_pkthdr.len, rate, xferlen)); usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, ural_txeof); error = usbd_transfer(data->xfer); if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) { m_freem(m0); data->m = NULL; data->ni = NULL; return error; } sc->tx_queued++; sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT; return 0; } static void ural_start(struct ifnet *ifp) { struct ural_softc *sc = ifp->if_softc; struct ieee80211_node *ni; struct mbuf *m; for (;;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; if (sc->tx_queued >= RAL_TX_LIST_COUNT-1) { IFQ_DRV_PREPEND(&ifp->if_snd, m); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); continue; } if (ural_tx_data(sc, m, ni) != 0) { ieee80211_free_node(ni); ifp->if_oerrors++; break; } sc->sc_tx_timer = 5; callout_reset(&sc->watchdog_ch, hz, ural_watchdog, sc); } } static void ural_watchdog(void *arg) { struct ural_softc *sc = (struct ural_softc *)arg; RAL_LOCK(sc); if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "device timeout\n"); /*ural_init(sc); XXX needs a process context! */ sc->sc_ifp->if_oerrors++; RAL_UNLOCK(sc); return; } callout_reset(&sc->watchdog_ch, hz, ural_watchdog, sc); } RAL_UNLOCK(sc); } static int ural_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ural_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; switch (cmd) { case SIOCSIFFLAGS: RAL_LOCK(sc); if (ifp->if_flags & IFF_UP) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { ural_init_locked(sc); startall = 1; } else ural_update_promisc(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) ural_stop(sc); } RAL_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; default: error = ether_ioctl(ifp, cmd, data); break; } return error; } static void ural_set_testmode(struct ural_softc *sc) { usb_device_request_t req; usbd_status error; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_VENDOR_REQUEST; USETW(req.wValue, 4); USETW(req.wIndex, 1); USETW(req.wLength, 0); error = usbd_do_request(sc->sc_udev, &req, NULL); if (error != 0) { device_printf(sc->sc_dev, "could not set test mode: %s\n", usbd_errstr(error)); } } static void ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len) { usb_device_request_t req; usbd_status error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_EEPROM; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); error = usbd_do_request(sc->sc_udev, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not read EEPROM: %s\n", usbd_errstr(error)); } } static uint16_t ural_read(struct ural_softc *sc, uint16_t reg) { usb_device_request_t req; usbd_status error; uint16_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, sizeof (uint16_t)); error = usbd_do_request(sc->sc_udev, &req, &val); if (error != 0) { device_printf(sc->sc_dev, "could not read MAC register: %s\n", usbd_errstr(error)); return 0; } return le16toh(val); } static void ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) { usb_device_request_t req; usbd_status error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); error = usbd_do_request(sc->sc_udev, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not read MAC register: %s\n", usbd_errstr(error)); } } static void ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val) { usb_device_request_t req; usbd_status error; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_WRITE_MAC; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); error = usbd_do_request(sc->sc_udev, &req, NULL); if (error != 0) { device_printf(sc->sc_dev, "could not write MAC register: %s\n", usbd_errstr(error)); } } static void ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) { usb_device_request_t req; usbd_status error; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_WRITE_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); error = usbd_do_request(sc->sc_udev, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not write MAC register: %s\n", usbd_errstr(error)); } } static void ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val) { uint16_t tmp; int ntries; for (ntries = 0; ntries < 5; ntries++) { if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) break; } if (ntries == 5) { device_printf(sc->sc_dev, "could not write to BBP\n"); return; } tmp = reg << 8 | val; ural_write(sc, RAL_PHY_CSR7, tmp); } static uint8_t ural_bbp_read(struct ural_softc *sc, uint8_t reg) { uint16_t val; int ntries; val = RAL_BBP_WRITE | reg << 8; ural_write(sc, RAL_PHY_CSR7, val); for (ntries = 0; ntries < 5; ntries++) { if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) break; } if (ntries == 5) { device_printf(sc->sc_dev, "could not read BBP\n"); return 0; } return ural_read(sc, RAL_PHY_CSR7) & 0xff; } static void ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val) { uint32_t tmp; int ntries; for (ntries = 0; ntries < 5; ntries++) { if (!(ural_read(sc, RAL_PHY_CSR10) & RAL_RF_LOBUSY)) break; } if (ntries == 5) { device_printf(sc->sc_dev, "could not write to RF\n"); return; } tmp = RAL_RF_BUSY | RAL_RF_20BIT | (val & 0xfffff) << 2 | (reg & 0x3); ural_write(sc, RAL_PHY_CSR9, tmp & 0xffff); ural_write(sc, RAL_PHY_CSR10, tmp >> 16); /* remember last written value in sc */ sc->rf_regs[reg] = val; DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff)); } /* ARGUSED */ static struct ieee80211_node * ural_node_alloc(struct ieee80211vap *vap __unused, const uint8_t mac[IEEE80211_ADDR_LEN] __unused) { struct ural_node *un; un = malloc(sizeof(struct ural_node), M_80211_NODE, M_NOWAIT | M_ZERO); return un != NULL ? &un->ni : NULL; } static void ural_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni); } static void ural_scan_start(struct ieee80211com *ic) { struct ural_softc *sc = ic->ic_ifp->if_softc; usb_rem_task(sc->sc_udev, &sc->sc_scantask); /* do it in a process context */ sc->sc_scan_action = URAL_SCAN_START; usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); } static void ural_scan_end(struct ieee80211com *ic) { struct ural_softc *sc = ic->ic_ifp->if_softc; usb_rem_task(sc->sc_udev, &sc->sc_scantask); /* do it in a process context */ sc->sc_scan_action = URAL_SCAN_END; usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); } static void ural_set_channel(struct ieee80211com *ic) { struct ural_softc *sc = ic->ic_ifp->if_softc; usb_rem_task(sc->sc_udev, &sc->sc_scantask); /* do it in a process context */ sc->sc_scan_action = URAL_SET_CHANNEL; usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); } static void ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint8_t power, tmp; u_int i, chan; chan = ieee80211_chan2ieee(ic, c); if (chan == 0 || chan == IEEE80211_CHAN_ANY) return; if (IEEE80211_IS_CHAN_2GHZ(c)) power = min(sc->txpow[chan - 1], 31); else power = 31; /* adjust txpower using ifconfig settings */ power -= (100 - ic->ic_txpowlimit) / 8; DPRINTFN(2, ("setting channel to %u, txpower to %u\n", chan, power)); switch (sc->rf_rev) { case RAL_RF_2522: ural_rf_write(sc, RAL_RF1, 0x00814); ural_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); break; case RAL_RF_2523: ural_rf_write(sc, RAL_RF1, 0x08804); ural_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x38044); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2524: ural_rf_write(sc, RAL_RF1, 0x0c808); ural_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2525: ural_rf_write(sc, RAL_RF1, 0x08808); ural_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); ural_rf_write(sc, RAL_RF1, 0x08808); ural_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2525E: ural_rf_write(sc, RAL_RF1, 0x08808); ural_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282); break; case RAL_RF_2526: ural_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]); ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); ural_rf_write(sc, RAL_RF1, 0x08804); ural_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); break; /* dual-band RF */ case RAL_RF_5222: for (i = 0; ural_rf5222[i].chan != chan; i++); ural_rf_write(sc, RAL_RF1, ural_rf5222[i].r1); ural_rf_write(sc, RAL_RF2, ural_rf5222[i].r2); ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); ural_rf_write(sc, RAL_RF4, ural_rf5222[i].r4); break; } if (ic->ic_opmode != IEEE80211_M_MONITOR && (ic->ic_flags & IEEE80211_F_SCAN) == 0) { /* set Japan filter bit for channel 14 */ tmp = ural_bbp_read(sc, 70); tmp &= ~RAL_JAPAN_FILTER; if (chan == 14) tmp |= RAL_JAPAN_FILTER; ural_bbp_write(sc, 70, tmp); /* clear CRC errors */ ural_read(sc, RAL_STA_CSR0); DELAY(10000); ural_disable_rf_tune(sc); } /* XXX doesn't belong here */ /* update basic rate set */ ural_set_basicrates(sc, c); } /* * Disable RF auto-tuning. */ static void ural_disable_rf_tune(struct ural_softc *sc) { uint32_t tmp; if (sc->rf_rev != RAL_RF_2523) { tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE; ural_rf_write(sc, RAL_RF1, tmp); } tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE; ural_rf_write(sc, RAL_RF3, tmp); DPRINTFN(2, ("disabling RF autotune\n")); } /* * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF * synchronization. */ static void ural_enable_tsf_sync(struct ural_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint16_t logcwmin, preload, tmp; /* first, disable TSF synchronization */ ural_write(sc, RAL_TXRX_CSR19, 0); tmp = (16 * vap->iv_bss->ni_intval) << 4; ural_write(sc, RAL_TXRX_CSR18, tmp); logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0; preload = (ic->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6; tmp = logcwmin << 12 | preload; ural_write(sc, RAL_TXRX_CSR20, tmp); /* finally, enable TSF synchronization */ tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN; if (ic->ic_opmode == IEEE80211_M_STA) tmp |= RAL_ENABLE_TSF_SYNC(1); else tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR; ural_write(sc, RAL_TXRX_CSR19, tmp); DPRINTF(("enabling TSF synchronization\n")); } static void ural_update_slot(struct ifnet *ifp) { struct ural_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; uint16_t slottime, sifs, eifs; slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; /* * These settings may sound a bit inconsistent but this is what the * reference driver does. */ if (ic->ic_curmode == IEEE80211_MODE_11B) { sifs = 16 - RAL_RXTX_TURNAROUND; eifs = 364; } else { sifs = 10 - RAL_RXTX_TURNAROUND; eifs = 64; } ural_write(sc, RAL_MAC_CSR10, slottime); ural_write(sc, RAL_MAC_CSR11, sifs); ural_write(sc, RAL_MAC_CSR12, eifs); } static void ural_set_txpreamble(struct ural_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t tmp; tmp = ural_read(sc, RAL_TXRX_CSR10); tmp &= ~RAL_SHORT_PREAMBLE; if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) tmp |= RAL_SHORT_PREAMBLE; ural_write(sc, RAL_TXRX_CSR10, tmp); } static void ural_set_basicrates(struct ural_softc *sc, const struct ieee80211_channel *c) { /* XXX wrong, take from rate set */ /* update basic rate set */ if (IEEE80211_IS_CHAN_5GHZ(c)) { /* 11a basic rates: 6, 12, 24Mbps */ ural_write(sc, RAL_TXRX_CSR11, 0x150); } else if (IEEE80211_IS_CHAN_ANYG(c)) { /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ ural_write(sc, RAL_TXRX_CSR11, 0x15f); } else { /* 11b basic rates: 1, 2Mbps */ ural_write(sc, RAL_TXRX_CSR11, 0x3); } } static void ural_set_bssid(struct ural_softc *sc, const uint8_t *bssid) { uint16_t tmp; tmp = bssid[0] | bssid[1] << 8; ural_write(sc, RAL_MAC_CSR5, tmp); tmp = bssid[2] | bssid[3] << 8; ural_write(sc, RAL_MAC_CSR6, tmp); tmp = bssid[4] | bssid[5] << 8; ural_write(sc, RAL_MAC_CSR7, tmp); DPRINTF(("setting BSSID to %6D\n", bssid, ":")); } static void ural_set_macaddr(struct ural_softc *sc, uint8_t *addr) { uint16_t tmp; tmp = addr[0] | addr[1] << 8; ural_write(sc, RAL_MAC_CSR2, tmp); tmp = addr[2] | addr[3] << 8; ural_write(sc, RAL_MAC_CSR3, tmp); tmp = addr[4] | addr[5] << 8; ural_write(sc, RAL_MAC_CSR4, tmp); DPRINTF(("setting MAC address to %6D\n", addr, ":")); } static void ural_update_promisc(struct ural_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; tmp = ural_read(sc, RAL_TXRX_CSR2); tmp &= ~RAL_DROP_NOT_TO_ME; if (!(ifp->if_flags & IFF_PROMISC)) tmp |= RAL_DROP_NOT_TO_ME; ural_write(sc, RAL_TXRX_CSR2, tmp); DPRINTF(("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ? "entering" : "leaving")); } static const char * ural_get_rf(int rev) { switch (rev) { case RAL_RF_2522: return "RT2522"; case RAL_RF_2523: return "RT2523"; case RAL_RF_2524: return "RT2524"; case RAL_RF_2525: return "RT2525"; case RAL_RF_2525E: return "RT2525e"; case RAL_RF_2526: return "RT2526"; case RAL_RF_5222: return "RT5222"; default: return "unknown"; } } static void ural_read_eeprom(struct ural_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t val; ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2); val = le16toh(val); sc->rf_rev = (val >> 11) & 0x7; sc->hw_radio = (val >> 10) & 0x1; sc->led_mode = (val >> 6) & 0x7; sc->rx_ant = (val >> 4) & 0x3; sc->tx_ant = (val >> 2) & 0x3; sc->nb_ant = val & 0x3; /* read MAC address */ ural_eeprom_read(sc, RAL_EEPROM_ADDRESS, ic->ic_myaddr, 6); /* read default values for BBP registers */ ural_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); /* read Tx power for all b/g channels */ ural_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->txpow, 14); } static int ural_bbp_init(struct ural_softc *sc) { #define N(a) (sizeof (a) / sizeof ((a)[0])) int i, ntries; /* wait for BBP to be ready */ for (ntries = 0; ntries < 100; ntries++) { if (ural_bbp_read(sc, RAL_BBP_VERSION) != 0) break; DELAY(1000); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for BBP\n"); return EIO; } /* initialize BBP registers to default values */ for (i = 0; i < N(ural_def_bbp); i++) ural_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val); #if 0 /* initialize BBP registers to values stored in EEPROM */ for (i = 0; i < 16; i++) { if (sc->bbp_prom[i].reg == 0xff) continue; ural_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); } #endif return 0; #undef N } static void ural_set_txantenna(struct ural_softc *sc, int antenna) { uint16_t tmp; uint8_t tx; tx = ural_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK; if (antenna == 1) tx |= RAL_BBP_ANTA; else if (antenna == 2) tx |= RAL_BBP_ANTB; else tx |= RAL_BBP_DIVERSITY; /* need to force I/Q flip for RF 2525e, 2526 and 5222 */ if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526 || sc->rf_rev == RAL_RF_5222) tx |= RAL_BBP_FLIPIQ; ural_bbp_write(sc, RAL_BBP_TX, tx); /* update values in PHY_CSR5 and PHY_CSR6 */ tmp = ural_read(sc, RAL_PHY_CSR5) & ~0x7; ural_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7)); tmp = ural_read(sc, RAL_PHY_CSR6) & ~0x7; ural_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7)); } static void ural_set_rxantenna(struct ural_softc *sc, int antenna) { uint8_t rx; rx = ural_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK; if (antenna == 1) rx |= RAL_BBP_ANTA; else if (antenna == 2) rx |= RAL_BBP_ANTB; else rx |= RAL_BBP_DIVERSITY; /* need to force no I/Q flip for RF 2525e and 2526 */ if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526) rx &= ~RAL_BBP_FLIPIQ; ural_bbp_write(sc, RAL_BBP_RX, rx); } static void ural_init_locked(struct ural_softc *sc) { #define N(a) (sizeof (a) / sizeof ((a)[0])) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ural_rx_data *data; uint16_t tmp; usbd_status error; int i, ntries; ural_set_testmode(sc); ural_write(sc, 0x308, 0x00f0); /* XXX magic */ ural_stop(sc); /* initialize MAC registers to default values */ for (i = 0; i < N(ural_def_mac); i++) ural_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val); /* wait for BBP and RF to wake up (this can take a long time!) */ for (ntries = 0; ntries < 100; ntries++) { tmp = ural_read(sc, RAL_MAC_CSR17); if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) == (RAL_BBP_AWAKE | RAL_RF_AWAKE)) break; DELAY(1000); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for BBP/RF to wakeup\n"); goto fail; } /* we're ready! */ ural_write(sc, RAL_MAC_CSR1, RAL_HOST_READY); /* set basic rate set (will be updated later) */ ural_write(sc, RAL_TXRX_CSR11, 0x15f); if (ural_bbp_init(sc) != 0) goto fail; ural_set_chan(sc, ic->ic_curchan); /* clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); ural_set_txantenna(sc, sc->tx_ant); ural_set_rxantenna(sc, sc->rx_ant); IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); ural_set_macaddr(sc, ic->ic_myaddr); /* * Allocate xfer for AMRR statistics requests. */ sc->amrr_xfer = usbd_alloc_xfer(sc->sc_udev); if (sc->amrr_xfer == NULL) { device_printf(sc->sc_dev, "could not allocate AMRR xfer\n"); goto fail; } /* * Open Tx and Rx USB bulk pipes. */ error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE, &sc->sc_tx_pipeh); if (error != 0) { device_printf(sc->sc_dev, "could not open Tx pipe: %s\n", usbd_errstr(error)); goto fail; } error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE, &sc->sc_rx_pipeh); if (error != 0) { device_printf(sc->sc_dev, "could not open Rx pipe: %s\n", usbd_errstr(error)); goto fail; } /* * Allocate Tx and Rx xfer queues. */ error = ural_alloc_tx_list(sc); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Tx list\n"); goto fail; } error = ural_alloc_rx_list(sc); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Rx list\n"); goto fail; } /* * Start up the receive pipe. */ for (i = 0; i < RAL_RX_LIST_COUNT; i++) { data = &sc->rx_data[i]; usbd_setup_xfer(data->xfer, sc->sc_rx_pipeh, data, data->buf, MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, ural_rxeof); usbd_transfer(data->xfer); } /* kick Rx */ tmp = RAL_DROP_PHY | RAL_DROP_CRC; if (ic->ic_opmode != IEEE80211_M_MONITOR) { tmp |= RAL_DROP_CTL | RAL_DROP_BAD_VERSION; if (ic->ic_opmode != IEEE80211_M_HOSTAP) tmp |= RAL_DROP_TODS; if (!(ifp->if_flags & IFF_PROMISC)) tmp |= RAL_DROP_NOT_TO_ME; } ural_write(sc, RAL_TXRX_CSR2, tmp); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; return; fail: ural_stop(sc); #undef N } static void ural_init(void *priv) { struct ural_softc *sc = priv; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; RAL_LOCK(sc); ural_init_locked(sc); RAL_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); /* start all vap's */ } static void ural_stop(void *priv) { struct ural_softc *sc = priv; struct ifnet *ifp = sc->sc_ifp; sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); /* disable Rx */ ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX); /* reset ASIC and BBP (but won't reset MAC registers!) */ ural_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP); ural_write(sc, RAL_MAC_CSR1, 0); if (sc->amrr_xfer != NULL) { usbd_free_xfer(sc->amrr_xfer); sc->amrr_xfer = NULL; } if (sc->sc_rx_pipeh != NULL) { usbd_abort_pipe(sc->sc_rx_pipeh); usbd_close_pipe(sc->sc_rx_pipeh); sc->sc_rx_pipeh = NULL; } if (sc->sc_tx_pipeh != NULL) { usbd_abort_pipe(sc->sc_tx_pipeh); usbd_close_pipe(sc->sc_tx_pipeh); sc->sc_tx_pipeh = NULL; } ural_free_rx_list(sc); ural_free_tx_list(sc); } static int ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct ural_softc *sc = ifp->if_softc; /* prevent management frames from being sent if we're not ready */ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { m_freem(m); ieee80211_free_node(ni); return ENETDOWN; } if (sc->tx_queued >= RAL_TX_LIST_COUNT) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; m_freem(m); ieee80211_free_node(ni); return EIO; } ifp->if_opackets++; if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ if (ural_tx_mgt(sc, m, ni) != 0) goto bad; } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ if (ural_tx_raw(sc, m, ni, params) != 0) goto bad; } sc->sc_tx_timer = 5; callout_reset(&sc->watchdog_ch, hz, ural_watchdog, sc); return 0; bad: ifp->if_oerrors++; ieee80211_free_node(ni); return EIO; /* XXX */ } static void ural_amrr_start(struct ural_softc *sc, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ural_vap *uvp = URAL_VAP(vap); /* clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); ieee80211_amrr_node_init(&uvp->amrr, &URAL_NODE(ni)->amn, ni); callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, vap); } static void ural_amrr_timeout(void *arg) { struct ieee80211vap *vap = arg; struct ural_softc *sc = vap->iv_ic->ic_ifp->if_softc; usb_device_request_t req; /* * Asynchronously read statistic registers (cleared by read). */ req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, RAL_STA_CSR0); USETW(req.wLength, sizeof sc->sta); usbd_setup_default_xfer(sc->amrr_xfer, sc->sc_udev, vap, USBD_DEFAULT_TIMEOUT, &req, sc->sta, sizeof sc->sta, 0, ural_amrr_update); (void)usbd_transfer(sc->amrr_xfer); } static void ural_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct ieee80211vap *vap = priv; struct ural_vap *uvp = URAL_VAP(vap); struct ifnet *ifp = vap->iv_ic->ic_ifp; struct ural_softc *sc = ifp->if_softc; struct ieee80211_node *ni = vap->iv_bss; int ok, fail; if (status != USBD_NORMAL_COMPLETION) { device_printf(sc->sc_dev, "could not retrieve Tx statistics - " "cancelling automatic rate control\n"); return; } ok = sc->sta[7] + /* TX ok w/o retry */ sc->sta[8]; /* TX ok w/ retry */ fail = sc->sta[9]; /* TX retry-fail count */ ieee80211_amrr_tx_update(&URAL_NODE(ni)->amn, ok+fail, ok, sc->sta[8] + fail); (void) ieee80211_amrr_choose(ni, &URAL_NODE(ni)->amn); ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */ callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, vap); } Index: user/thompsa/vaptq/sys/dev/usb/if_uralvar.h =================================================================== --- user/thompsa/vaptq/sys/dev/usb/if_uralvar.h (revision 185732) +++ user/thompsa/vaptq/sys/dev/usb/if_uralvar.h (revision 185733) @@ -1,157 +1,154 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2005 * Damien Bergamini * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RAL_RX_LIST_COUNT 1 #define RAL_TX_LIST_COUNT 8 #define URAL_SCAN_START 1 #define URAL_SCAN_END 2 #define URAL_SET_CHANNEL 3 struct ural_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint8_t wr_flags; uint8_t wr_rate; uint16_t wr_chan_freq; uint16_t wr_chan_flags; uint8_t wr_antenna; uint8_t wr_antsignal; }; #define RAL_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_ANTENNA) | \ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) struct ural_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; uint8_t wt_rate; uint16_t wt_chan_freq; uint16_t wt_chan_flags; uint8_t wt_antenna; }; #define RAL_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_ANTENNA)) struct ural_softc; struct ural_tx_data { struct ural_softc *sc; usbd_xfer_handle xfer; uint8_t *buf; struct mbuf *m; struct ieee80211_node *ni; }; struct ural_rx_data { struct ural_softc *sc; usbd_xfer_handle xfer; uint8_t *buf; struct mbuf *m; }; struct ural_node { struct ieee80211_node ni; struct ieee80211_amrr_node amn; }; #define URAL_NODE(ni) ((struct ural_node *)(ni)) struct ural_vap { struct ieee80211vap vap; struct ieee80211_beacon_offsets bo; struct ieee80211_amrr amrr; struct callout amrr_ch; int (*newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define URAL_VAP(vap) ((struct ural_vap *)(vap)) struct ural_softc { struct ifnet *sc_ifp; device_t sc_dev; usbd_device_handle sc_udev; usbd_interface_handle sc_iface; const struct ieee80211_rate_table *sc_rates; int sc_rx_no; int sc_tx_no; uint32_t asic_rev; uint8_t rf_rev; usbd_xfer_handle amrr_xfer; usbd_pipe_handle sc_rx_pipeh; usbd_pipe_handle sc_tx_pipeh; - enum ieee80211_state sc_state; - int sc_arg; int sc_scan_action; /* should be an enum */ - struct usb_task sc_task; struct usb_task sc_scantask; struct ural_rx_data rx_data[RAL_RX_LIST_COUNT]; struct ural_tx_data tx_data[RAL_TX_LIST_COUNT]; int tx_queued; int tx_cur; struct mtx sc_mtx; struct callout watchdog_ch; int sc_tx_timer; uint16_t sta[11]; uint32_t rf_regs[4]; uint8_t txpow[14]; struct { uint8_t val; uint8_t reg; } __packed bbp_prom[16]; int led_mode; int hw_radio; int rx_ant; int tx_ant; int nb_ant; struct ural_rx_radiotap_header sc_rxtap; int sc_rxtap_len; struct ural_tx_radiotap_header sc_txtap; int sc_txtap_len; }; #if 0 #define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx) #define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) #else #define RAL_LOCK(sc) do { ((sc) = (sc)); mtx_lock(&Giant); } while (0) #define RAL_UNLOCK(sc) mtx_unlock(&Giant) #endif Index: user/thompsa/vaptq/sys/dev/usb/if_zyd.c =================================================================== --- user/thompsa/vaptq/sys/dev/usb/if_zyd.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/usb/if_zyd.c (revision 185733) @@ -1,3124 +1,3096 @@ /* $OpenBSD: if_zyd.c,v 1.52 2007/02/11 00:08:04 jsg Exp $ */ /* $NetBSD: if_zyd.c,v 1.7 2007/06/21 04:04:29 kiyohara Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 2006 by Damien Bergamini * Copyright (c) 2006 by Florian Stoehr * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * ZyDAS ZD1211/ZD1211B USB WLAN driver. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #include #include #ifdef ZYD_DEBUG SYSCTL_NODE(_hw_usb, OID_AUTO, zyd, CTLFLAG_RW, 0, "ZyDAS zd1211/zd1211b"); int zyd_debug = 0; SYSCTL_INT(_hw_usb_zyd, OID_AUTO, debug, CTLFLAG_RW, &zyd_debug, 0, "control debugging printfs"); TUNABLE_INT("hw.usb.zyd.debug", &zyd_debug); enum { ZYD_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ ZYD_DEBUG_RECV = 0x00000002, /* basic recv operation */ ZYD_DEBUG_RESET = 0x00000004, /* reset processing */ ZYD_DEBUG_INIT = 0x00000008, /* device init */ ZYD_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */ ZYD_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */ ZYD_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */ ZYD_DEBUG_STAT = 0x00000080, /* statistic */ ZYD_DEBUG_FW = 0x00000100, /* firmware */ ZYD_DEBUG_ANY = 0xffffffff }; #define DPRINTF(sc, m, fmt, ...) do { \ if (sc->sc_debug & (m)) \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(sc, m, fmt, ...) do { \ (void) sc; \ } while (0) #endif static const struct zyd_phy_pair zyd_def_phy[] = ZYD_DEF_PHY; static const struct zyd_phy_pair zyd_def_phyB[] = ZYD_DEF_PHYB; /* various supported device vendors/products */ #define ZYD_ZD1211_DEV(v, p) \ { { USB_VENDOR_##v, USB_PRODUCT_##v##_##p }, ZYD_ZD1211 } #define ZYD_ZD1211B_DEV(v, p) \ { { USB_VENDOR_##v, USB_PRODUCT_##v##_##p }, ZYD_ZD1211B } static const struct zyd_type { struct usb_devno dev; uint8_t rev; #define ZYD_ZD1211 0 #define ZYD_ZD1211B 1 } zyd_devs[] = { ZYD_ZD1211_DEV(3COM2, 3CRUSB10075), ZYD_ZD1211_DEV(ABOCOM, WL54), ZYD_ZD1211_DEV(ASUS, WL159G), ZYD_ZD1211_DEV(CYBERTAN, TG54USB), ZYD_ZD1211_DEV(DRAYTEK, VIGOR550), ZYD_ZD1211_DEV(PLANEX2, GWUS54GD), ZYD_ZD1211_DEV(PLANEX2, GWUS54GZL), ZYD_ZD1211_DEV(PLANEX3, GWUS54GZ), ZYD_ZD1211_DEV(PLANEX3, GWUS54MINI), ZYD_ZD1211_DEV(SAGEM, XG760A), ZYD_ZD1211_DEV(SENAO, NUB8301), ZYD_ZD1211_DEV(SITECOMEU, WL113), ZYD_ZD1211_DEV(SWEEX, ZD1211), ZYD_ZD1211_DEV(TEKRAM, QUICKWLAN), ZYD_ZD1211_DEV(TEKRAM, ZD1211_1), ZYD_ZD1211_DEV(TEKRAM, ZD1211_2), ZYD_ZD1211_DEV(TWINMOS, G240), ZYD_ZD1211_DEV(UMEDIA, ALL0298V2), ZYD_ZD1211_DEV(UMEDIA, TEW429UB_A), ZYD_ZD1211_DEV(UMEDIA, TEW429UB), ZYD_ZD1211_DEV(WISTRONNEWEB, UR055G), ZYD_ZD1211_DEV(ZCOM, ZD1211), ZYD_ZD1211_DEV(ZYDAS, ZD1211), ZYD_ZD1211_DEV(ZYXEL, AG225H), ZYD_ZD1211_DEV(ZYXEL, ZYAIRG220), ZYD_ZD1211_DEV(ZYXEL, G200V2), ZYD_ZD1211_DEV(ZYXEL, G202), ZYD_ZD1211B_DEV(ACCTON, SMCWUSBG), ZYD_ZD1211B_DEV(ACCTON, ZD1211B), ZYD_ZD1211B_DEV(ASUS, A9T_WIFI), ZYD_ZD1211B_DEV(BELKIN, F5D7050_V4000), ZYD_ZD1211B_DEV(BELKIN, ZD1211B), ZYD_ZD1211B_DEV(CISCOLINKSYS, WUSBF54G), ZYD_ZD1211B_DEV(FIBERLINE, WL430U), ZYD_ZD1211B_DEV(MELCO, KG54L), ZYD_ZD1211B_DEV(PHILIPS, SNU5600), ZYD_ZD1211B_DEV(PLANEX2, GW_US54GXS), ZYD_ZD1211B_DEV(SAGEM, XG76NA), ZYD_ZD1211B_DEV(SITECOMEU, ZD1211B), ZYD_ZD1211B_DEV(UMEDIA, TEW429UBC1), #if 0 /* Shall we needs? */ ZYD_ZD1211B_DEV(UNKNOWN1, ZD1211B_1), ZYD_ZD1211B_DEV(UNKNOWN1, ZD1211B_2), ZYD_ZD1211B_DEV(UNKNOWN2, ZD1211B), ZYD_ZD1211B_DEV(UNKNOWN3, ZD1211B), #endif ZYD_ZD1211B_DEV(USR, USR5423), ZYD_ZD1211B_DEV(VTECH, ZD1211B), ZYD_ZD1211B_DEV(ZCOM, ZD1211B), ZYD_ZD1211B_DEV(ZYDAS, ZD1211B), ZYD_ZD1211B_DEV(ZYXEL, M202), ZYD_ZD1211B_DEV(ZYXEL, G220V2), }; #define zyd_lookup(v, p) \ ((const struct zyd_type *)usb_lookup(zyd_devs, v, p)) #define zyd_read16_m(sc, val, data) do { \ error = zyd_read16(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define zyd_write16_m(sc, val, data) do { \ error = zyd_write16(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define zyd_read32_m(sc, val, data) do { \ error = zyd_read32(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define zyd_write32_m(sc, val, data) do { \ error = zyd_write32(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) static device_probe_t zyd_match; static device_attach_t zyd_attach; static device_detach_t zyd_detach; static struct ieee80211vap *zyd_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void zyd_vap_delete(struct ieee80211vap *); static int zyd_open_pipes(struct zyd_softc *); static void zyd_close_pipes(struct zyd_softc *); static int zyd_alloc_tx_list(struct zyd_softc *); static void zyd_free_tx_list(struct zyd_softc *); static int zyd_alloc_rx_list(struct zyd_softc *); static void zyd_free_rx_list(struct zyd_softc *); static struct ieee80211_node *zyd_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); -static void zyd_task(void *); static int zyd_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int zyd_cmd(struct zyd_softc *, uint16_t, const void *, int, void *, int, u_int); static int zyd_read16(struct zyd_softc *, uint16_t, uint16_t *); static int zyd_read32(struct zyd_softc *, uint16_t, uint32_t *); static int zyd_write16(struct zyd_softc *, uint16_t, uint16_t); static int zyd_write32(struct zyd_softc *, uint16_t, uint32_t); static int zyd_rfwrite(struct zyd_softc *, uint32_t); static int zyd_lock_phy(struct zyd_softc *); static int zyd_unlock_phy(struct zyd_softc *); static int zyd_rf_attach(struct zyd_softc *, uint8_t); static const char *zyd_rf_name(uint8_t); static int zyd_hw_init(struct zyd_softc *); static int zyd_read_pod(struct zyd_softc *); static int zyd_read_eeprom(struct zyd_softc *); static int zyd_get_macaddr(struct zyd_softc *); static int zyd_set_macaddr(struct zyd_softc *, const uint8_t *); static int zyd_set_bssid(struct zyd_softc *, const uint8_t *); static int zyd_switch_radio(struct zyd_softc *, int); static int zyd_set_led(struct zyd_softc *, int, int); static void zyd_set_multi(void *); static void zyd_update_mcast(struct ifnet *); static int zyd_set_rxfilter(struct zyd_softc *); static void zyd_set_chan(struct zyd_softc *, struct ieee80211_channel *); static int zyd_set_beacon_interval(struct zyd_softc *, int); static void zyd_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); static void zyd_rx_data(struct zyd_softc *, const uint8_t *, uint16_t); static void zyd_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void zyd_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static int zyd_tx_mgt(struct zyd_softc *, struct mbuf *, struct ieee80211_node *); static int zyd_tx_data(struct zyd_softc *, struct mbuf *, struct ieee80211_node *); static void zyd_start(struct ifnet *); static int zyd_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void zyd_watchdog(void *); static int zyd_ioctl(struct ifnet *, u_long, caddr_t); static void zyd_init_locked(struct zyd_softc *); static void zyd_init(void *); static void zyd_stop(struct zyd_softc *, int); static int zyd_loadfirmware(struct zyd_softc *); static void zyd_newassoc(struct ieee80211_node *, int); static void zyd_scantask(void *); static void zyd_scan_start(struct ieee80211com *); static void zyd_scan_end(struct ieee80211com *); static void zyd_set_channel(struct ieee80211com *); static void zyd_wakeup(struct zyd_softc *); static int zyd_rfmd_init(struct zyd_rf *); static int zyd_rfmd_switch_radio(struct zyd_rf *, int); static int zyd_rfmd_set_channel(struct zyd_rf *, uint8_t); static int zyd_al2230_init(struct zyd_rf *); static int zyd_al2230_switch_radio(struct zyd_rf *, int); static int zyd_al2230_set_channel(struct zyd_rf *, uint8_t); static int zyd_al2230_set_channel_b(struct zyd_rf *, uint8_t); static int zyd_al2230_init_b(struct zyd_rf *); static int zyd_al7230B_init(struct zyd_rf *); static int zyd_al7230B_switch_radio(struct zyd_rf *, int); static int zyd_al7230B_set_channel(struct zyd_rf *, uint8_t); static int zyd_al2210_init(struct zyd_rf *); static int zyd_al2210_switch_radio(struct zyd_rf *, int); static int zyd_al2210_set_channel(struct zyd_rf *, uint8_t); static int zyd_gct_init(struct zyd_rf *); static int zyd_gct_switch_radio(struct zyd_rf *, int); static int zyd_gct_set_channel(struct zyd_rf *, uint8_t); static int zyd_maxim_init(struct zyd_rf *); static int zyd_maxim_switch_radio(struct zyd_rf *, int); static int zyd_maxim_set_channel(struct zyd_rf *, uint8_t); static int zyd_maxim2_init(struct zyd_rf *); static int zyd_maxim2_switch_radio(struct zyd_rf *, int); static int zyd_maxim2_set_channel(struct zyd_rf *, uint8_t); static int zyd_match(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (!uaa->iface) return (UMATCH_NONE); return (zyd_lookup(uaa->vendor, uaa->product) != NULL) ? (UMATCH_VENDOR_PRODUCT) : (UMATCH_NONE); } static int zyd_attach(device_t dev) { int error = ENXIO; struct ieee80211com *ic; struct ifnet *ifp; struct usb_attach_arg *uaa = device_get_ivars(dev); struct zyd_softc *sc = device_get_softc(dev); usb_device_descriptor_t* ddesc; uint8_t bands; sc->sc_dev = dev; sc->sc_udev = uaa->device; sc->sc_macrev = zyd_lookup(uaa->vendor, uaa->product)->rev; #ifdef ZYD_DEBUG sc->sc_debug = zyd_debug; #endif ddesc = usbd_get_device_descriptor(sc->sc_udev); if (UGETW(ddesc->bcdDevice) < 0x4330) { device_printf(dev, "device version mismatch: 0x%x " "(only >= 43.30 supported)\n", UGETW(ddesc->bcdDevice)); return (ENXIO); } if ((error = zyd_get_macaddr(sc)) != 0) { device_printf(sc->sc_dev, "could not read EEPROM\n"); return (ENXIO); } mtx_init(&sc->sc_txmtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, MTX_DEF); usb_init_task(&sc->sc_mcasttask, zyd_set_multi, sc); usb_init_task(&sc->sc_scantask, zyd_scantask, sc); - usb_init_task(&sc->sc_task, zyd_task, sc); callout_init(&sc->sc_watchdog_ch, 0); STAILQ_INIT(&sc->sc_rqh); ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); error = ENXIO; goto fail0; } ifp->if_softc = sc; if_initname(ifp, "zyd", device_get_unit(sc->sc_dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_NEEDSGIANT; /* USB stack is still under Giant lock */ ifp->if_init = zyd_init; ifp->if_ioctl = zyd_ioctl; ifp->if_start = zyd_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); IFQ_SET_READY(&ifp->if_snd); ic = ifp->if_l2com; ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_bssid); /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* capable of bg scanning */ | IEEE80211_C_WPA /* 802.11i */ ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); ieee80211_init_channels(ic, NULL, &bands); ieee80211_ifattach(ic); ic->ic_newassoc = zyd_newassoc; ic->ic_raw_xmit = zyd_raw_xmit; ic->ic_node_alloc = zyd_node_alloc; ic->ic_scan_start = zyd_scan_start; ic->ic_scan_end = zyd_scan_end; ic->ic_set_channel = zyd_set_channel; ic->ic_vap_create = zyd_vap_create; ic->ic_vap_delete = zyd_vap_delete; ic->ic_update_mcast = zyd_update_mcast; bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap)); sc->sc_rxtap_len = sizeof(sc->sc_rxtap); sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(ZYD_RX_RADIOTAP_PRESENT); sc->sc_txtap_len = sizeof(sc->sc_txtap); sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(ZYD_TX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); return (0); fail0: mtx_destroy(&sc->sc_txmtx); return (error); } static int zyd_detach(device_t dev) { struct zyd_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; if (!device_is_attached(dev)) return (0); /* set a flag to indicate we're detaching. */ sc->sc_flags |= ZYD_FLAG_DETACHING; zyd_stop(sc, 1); bpfdetach(ifp); ieee80211_ifdetach(ic); zyd_wakeup(sc); zyd_close_pipes(sc); if_free(ifp); mtx_destroy(&sc->sc_txmtx); usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); return (0); } static struct ieee80211vap * zyd_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct zyd_vap *zvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return (NULL); zvp = (struct zyd_vap *) malloc(sizeof(struct zyd_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (zvp == NULL) return (NULL); vap = &zvp->vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override state transition machine */ zvp->newstate = vap->iv_newstate; vap->iv_newstate = zyd_newstate; ieee80211_amrr_init(&zvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 1000 /* 1 sec */); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return (vap); } static void zyd_vap_delete(struct ieee80211vap *vap) { struct zyd_vap *zvp = ZYD_VAP(vap); ieee80211_amrr_cleanup(&zvp->amrr); ieee80211_vap_detach(vap); free(zvp, M_80211_VAP); } static int zyd_open_pipes(struct zyd_softc *sc) { usb_endpoint_descriptor_t *edesc; int isize; usbd_status error; /* interrupt in */ edesc = usbd_get_endpoint_descriptor(sc->sc_iface, 0x83); if (edesc == NULL) return (EINVAL); isize = UGETW(edesc->wMaxPacketSize); if (isize == 0) /* should not happen */ return (EINVAL); sc->sc_ibuf = malloc(isize, M_USBDEV, M_NOWAIT); if (sc->sc_ibuf == NULL) return (ENOMEM); error = usbd_open_pipe_intr(sc->sc_iface, 0x83, USBD_SHORT_XFER_OK, &sc->sc_ep[ZYD_ENDPT_IIN], sc, sc->sc_ibuf, isize, zyd_intr, USBD_DEFAULT_INTERVAL); if (error != 0) { device_printf(sc->sc_dev, "open rx intr pipe failed: %s\n", usbd_errstr(error)); goto fail; } /* interrupt out (not necessarily an interrupt pipe) */ error = usbd_open_pipe(sc->sc_iface, 0x04, USBD_EXCLUSIVE_USE, &sc->sc_ep[ZYD_ENDPT_IOUT]); if (error != 0) { device_printf(sc->sc_dev, "open tx intr pipe failed: %s\n", usbd_errstr(error)); goto fail; } /* bulk in */ error = usbd_open_pipe(sc->sc_iface, 0x82, USBD_EXCLUSIVE_USE, &sc->sc_ep[ZYD_ENDPT_BIN]); if (error != 0) { device_printf(sc->sc_dev, "open rx pipe failed: %s\n", usbd_errstr(error)); goto fail; } /* bulk out */ error = usbd_open_pipe(sc->sc_iface, 0x01, USBD_EXCLUSIVE_USE, &sc->sc_ep[ZYD_ENDPT_BOUT]); if (error != 0) { device_printf(sc->sc_dev, "open tx pipe failed: %s\n", usbd_errstr(error)); goto fail; } return (0); fail: zyd_close_pipes(sc); return (ENXIO); } static void zyd_close_pipes(struct zyd_softc *sc) { int i; for (i = 0; i < ZYD_ENDPT_CNT; i++) { if (sc->sc_ep[i] != NULL) { usbd_abort_pipe(sc->sc_ep[i]); usbd_close_pipe(sc->sc_ep[i]); sc->sc_ep[i] = NULL; } } if (sc->sc_ibuf != NULL) { free(sc->sc_ibuf, M_USBDEV); sc->sc_ibuf = NULL; } } static int zyd_alloc_tx_list(struct zyd_softc *sc) { int i, error; sc->sc_txqueued = 0; for (i = 0; i < ZYD_TX_LIST_CNT; i++) { struct zyd_tx_data *data = &sc->sc_txdata[i]; data->sc = sc; /* backpointer for callbacks */ data->xfer = usbd_alloc_xfer(sc->sc_udev); if (data->xfer == NULL) { device_printf(sc->sc_dev, "could not allocate tx xfer\n"); error = ENOMEM; goto fail; } data->buf = usbd_alloc_buffer(data->xfer, ZYD_MAX_TXBUFSZ); if (data->buf == NULL) { device_printf(sc->sc_dev, "could not allocate tx buffer\n"); error = ENOMEM; goto fail; } /* clear Tx descriptor */ bzero(data->buf, sizeof(struct zyd_tx_desc)); } return (0); fail: zyd_free_tx_list(sc); return (error); } static void zyd_free_tx_list(struct zyd_softc *sc) { int i; for (i = 0; i < ZYD_TX_LIST_CNT; i++) { struct zyd_tx_data *data = &sc->sc_txdata[i]; if (data->xfer != NULL) { usbd_free_xfer(data->xfer); data->xfer = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } } } static int zyd_alloc_rx_list(struct zyd_softc *sc) { int i, error; for (i = 0; i < ZYD_RX_LIST_CNT; i++) { struct zyd_rx_data *data = &sc->sc_rxdata[i]; data->sc = sc; /* backpointer for callbacks */ data->xfer = usbd_alloc_xfer(sc->sc_udev); if (data->xfer == NULL) { device_printf(sc->sc_dev, "could not allocate rx xfer\n"); error = ENOMEM; goto fail; } data->buf = usbd_alloc_buffer(data->xfer, ZYX_MAX_RXBUFSZ); if (data->buf == NULL) { device_printf(sc->sc_dev, "could not allocate rx buffer\n"); error = ENOMEM; goto fail; } } return (0); fail: zyd_free_rx_list(sc); return (error); } static void zyd_free_rx_list(struct zyd_softc *sc) { int i; for (i = 0; i < ZYD_RX_LIST_CNT; i++) { struct zyd_rx_data *data = &sc->sc_rxdata[i]; if (data->xfer != NULL) { usbd_free_xfer(data->xfer); data->xfer = NULL; } } } /* ARGUSED */ static struct ieee80211_node * zyd_node_alloc(struct ieee80211vap *vap __unused, const uint8_t mac[IEEE80211_ADDR_LEN] __unused) { struct zyd_node *zn; zn = malloc(sizeof(struct zyd_node), M_80211_NODE, M_NOWAIT | M_ZERO); return (zn != NULL) ? (&zn->ni) : (NULL); } -static void -zyd_task(void *arg) +static int +zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { - int error; - struct zyd_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - struct ieee80211_node *ni = vap->iv_bss; struct zyd_vap *zvp = ZYD_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct zyd_softc *sc = ic->ic_ifp->if_softc; + struct ieee80211_node *ni = vap->iv_bss; + int error; - switch (sc->sc_state) { + DPRINTF(sc, ZYD_DEBUG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); + + usb_rem_task(sc->sc_udev, &sc->sc_scantask); + callout_stop(&sc->sc_watchdog_ch); + + IEEE80211_UNLOCK(ic); + switch (nstate) { case IEEE80211_S_AUTH: zyd_set_chan(sc, ic->ic_curchan); break; case IEEE80211_S_RUN: if (vap->iv_opmode == IEEE80211_M_MONITOR) break; /* turn link LED on */ error = zyd_set_led(sc, ZYD_LED1, 1); if (error != 0) goto fail; - + /* make data LED blink upon Tx */ zyd_write32_m(sc, sc->sc_fwbase + ZYD_FW_LINK_STATUS, 1); - + IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); zyd_set_bssid(sc, sc->sc_bssid); break; default: break; } - fail: IEEE80211_LOCK(ic); - zvp->newstate(vap, sc->sc_state, sc->sc_arg); - if (vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg); - IEEE80211_UNLOCK(ic); + zvp->newstate(vap, nstate, arg); + return (0); } static int -zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - struct zyd_vap *zvp = ZYD_VAP(vap); - struct ieee80211com *ic = vap->iv_ic; - struct zyd_softc *sc = ic->ic_ifp->if_softc; - - DPRINTF(sc, ZYD_DEBUG_STATE, "%s: %s -> %s\n", __func__, - ieee80211_state_name[vap->iv_state], - ieee80211_state_name[nstate]); - - usb_rem_task(sc->sc_udev, &sc->sc_scantask); - usb_rem_task(sc->sc_udev, &sc->sc_task); - callout_stop(&sc->sc_watchdog_ch); - - /* do it in a process context */ - sc->sc_state = nstate; - sc->sc_arg = arg; - - if (nstate == IEEE80211_S_INIT) { - zvp->newstate(vap, nstate, arg); - return (0); - } else { - usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER); - return (EINPROGRESS); - } -} - -static int zyd_cmd(struct zyd_softc *sc, uint16_t code, const void *idata, int ilen, void *odata, int olen, u_int flags) { usbd_xfer_handle xfer; struct zyd_cmd cmd; struct zyd_rq rq; uint16_t xferflags; usbd_status error; if (sc->sc_flags & ZYD_FLAG_DETACHING) return (ENXIO); if ((xfer = usbd_alloc_xfer(sc->sc_udev)) == NULL) return (ENOMEM); cmd.code = htole16(code); bcopy(idata, cmd.data, ilen); xferflags = USBD_FORCE_SHORT_XFER; if (!(flags & ZYD_CMD_FLAG_READ)) xferflags |= USBD_SYNCHRONOUS; else { rq.idata = idata; rq.odata = odata; rq.len = olen / sizeof(struct zyd_pair); STAILQ_INSERT_TAIL(&sc->sc_rqh, &rq, rq); } usbd_setup_xfer(xfer, sc->sc_ep[ZYD_ENDPT_IOUT], 0, &cmd, sizeof(uint16_t) + ilen, xferflags, ZYD_INTR_TIMEOUT, NULL); error = usbd_transfer(xfer); if (error != USBD_IN_PROGRESS && error != 0) { device_printf(sc->sc_dev, "could not send command (error=%s)\n", usbd_errstr(error)); (void)usbd_free_xfer(xfer); return (EIO); } if (!(flags & ZYD_CMD_FLAG_READ)) { (void)usbd_free_xfer(xfer); return (0); /* write: don't wait for reply */ } /* wait at most one second for command reply */ error = tsleep(odata, PCATCH, "zydcmd", hz); if (error == EWOULDBLOCK) device_printf(sc->sc_dev, "zyd_read sleep timeout\n"); STAILQ_REMOVE(&sc->sc_rqh, &rq, zyd_rq, rq); (void)usbd_free_xfer(xfer); return (error); } static int zyd_read16(struct zyd_softc *sc, uint16_t reg, uint16_t *val) { struct zyd_pair tmp; int error; reg = htole16(reg); error = zyd_cmd(sc, ZYD_CMD_IORD, ®, sizeof(reg), &tmp, sizeof(tmp), ZYD_CMD_FLAG_READ); if (error == 0) *val = le16toh(tmp.val); return (error); } static int zyd_read32(struct zyd_softc *sc, uint16_t reg, uint32_t *val) { struct zyd_pair tmp[2]; uint16_t regs[2]; int error; regs[0] = htole16(ZYD_REG32_HI(reg)); regs[1] = htole16(ZYD_REG32_LO(reg)); error = zyd_cmd(sc, ZYD_CMD_IORD, regs, sizeof(regs), tmp, sizeof(tmp), ZYD_CMD_FLAG_READ); if (error == 0) *val = le16toh(tmp[0].val) << 16 | le16toh(tmp[1].val); return (error); } static int zyd_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val) { struct zyd_pair pair; pair.reg = htole16(reg); pair.val = htole16(val); return zyd_cmd(sc, ZYD_CMD_IOWR, &pair, sizeof(pair), NULL, 0, 0); } static int zyd_write32(struct zyd_softc *sc, uint16_t reg, uint32_t val) { struct zyd_pair pair[2]; pair[0].reg = htole16(ZYD_REG32_HI(reg)); pair[0].val = htole16(val >> 16); pair[1].reg = htole16(ZYD_REG32_LO(reg)); pair[1].val = htole16(val & 0xffff); return zyd_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0); } static int zyd_rfwrite(struct zyd_softc *sc, uint32_t val) { struct zyd_rf *rf = &sc->sc_rf; struct zyd_rfwrite_cmd req; uint16_t cr203; int error, i; zyd_read16_m(sc, ZYD_CR203, &cr203); cr203 &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA); req.code = htole16(2); req.width = htole16(rf->width); for (i = 0; i < rf->width; i++) { req.bit[i] = htole16(cr203); if (val & (1 << (rf->width - 1 - i))) req.bit[i] |= htole16(ZYD_RF_DATA); } error = zyd_cmd(sc, ZYD_CMD_RFCFG, &req, 4 + 2 * rf->width, NULL, 0, 0); fail: return (error); } static int zyd_rfwrite_cr(struct zyd_softc *sc, uint32_t val) { int error; zyd_write16_m(sc, ZYD_CR244, (val >> 16) & 0xff); zyd_write16_m(sc, ZYD_CR243, (val >> 8) & 0xff); zyd_write16_m(sc, ZYD_CR242, (val >> 0) & 0xff); fail: return (error); } static int zyd_lock_phy(struct zyd_softc *sc) { int error; uint32_t tmp; zyd_read32_m(sc, ZYD_MAC_MISC, &tmp); tmp &= ~ZYD_UNLOCK_PHY_REGS; zyd_write32_m(sc, ZYD_MAC_MISC, tmp); fail: return (error); } static int zyd_unlock_phy(struct zyd_softc *sc) { int error; uint32_t tmp; zyd_read32_m(sc, ZYD_MAC_MISC, &tmp); tmp |= ZYD_UNLOCK_PHY_REGS; zyd_write32_m(sc, ZYD_MAC_MISC, tmp); fail: return (error); } /* * RFMD RF methods. */ static int zyd_rfmd_init(struct zyd_rf *rf) { #define N(a) (sizeof(a) / sizeof((a)[0])) struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_RFMD_PHY; static const uint32_t rfini[] = ZYD_RFMD_RF; int i, error; /* init RF-dependent PHY registers */ for (i = 0; i < N(phyini); i++) { zyd_write16_m(sc, phyini[i].reg, phyini[i].val); } /* init RFMD radio */ for (i = 0; i < N(rfini); i++) { if ((error = zyd_rfwrite(sc, rfini[i])) != 0) return (error); } fail: return (error); #undef N } static int zyd_rfmd_switch_radio(struct zyd_rf *rf, int on) { int error; struct zyd_softc *sc = rf->rf_sc; zyd_write16_m(sc, ZYD_CR10, on ? 0x89 : 0x15); zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x81); fail: return (error); } static int zyd_rfmd_set_channel(struct zyd_rf *rf, uint8_t chan) { int error; struct zyd_softc *sc = rf->rf_sc; static const struct { uint32_t r1, r2; } rfprog[] = ZYD_RFMD_CHANTABLE; error = zyd_rfwrite(sc, rfprog[chan - 1].r1); if (error != 0) goto fail; error = zyd_rfwrite(sc, rfprog[chan - 1].r2); if (error != 0) goto fail; fail: return (error); } /* * AL2230 RF methods. */ static int zyd_al2230_init(struct zyd_rf *rf) { #define N(a) (sizeof(a) / sizeof((a)[0])) struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY; static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT; static const struct zyd_phy_pair phypll[] = { { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f }, { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } }; static const uint32_t rfini1[] = ZYD_AL2230_RF_PART1; static const uint32_t rfini2[] = ZYD_AL2230_RF_PART2; static const uint32_t rfini3[] = ZYD_AL2230_RF_PART3; int i, error; /* init RF-dependent PHY registers */ for (i = 0; i < N(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) { for (i = 0; i < N(phy2230s); i++) zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val); } /* init AL2230 radio */ for (i = 0; i < N(rfini1); i++) { error = zyd_rfwrite(sc, rfini1[i]); if (error != 0) goto fail; } if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) error = zyd_rfwrite(sc, 0x000824); else error = zyd_rfwrite(sc, 0x0005a4); if (error != 0) goto fail; for (i = 0; i < N(rfini2); i++) { error = zyd_rfwrite(sc, rfini2[i]); if (error != 0) goto fail; } for (i = 0; i < N(phypll); i++) zyd_write16_m(sc, phypll[i].reg, phypll[i].val); for (i = 0; i < N(rfini3); i++) { error = zyd_rfwrite(sc, rfini3[i]); if (error != 0) goto fail; } fail: return (error); #undef N } static int zyd_al2230_fini(struct zyd_rf *rf) { #define N(a) (sizeof(a) / sizeof((a)[0])) int error, i; struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phy[] = ZYD_AL2230_PHY_FINI_PART1; for (i = 0; i < N(phy); i++) zyd_write16_m(sc, phy[i].reg, phy[i].val); if (sc->sc_newphy != 0) zyd_write16_m(sc, ZYD_CR9, 0xe1); zyd_write16_m(sc, ZYD_CR203, 0x6); fail: return (error); #undef N } static int zyd_al2230_init_b(struct zyd_rf *rf) { #define N(a) (sizeof(a) / sizeof((a)[0])) struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1; static const struct zyd_phy_pair phy2[] = ZYD_AL2230_PHY_PART2; static const struct zyd_phy_pair phy3[] = ZYD_AL2230_PHY_PART3; static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT; static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY_B; static const uint32_t rfini_part1[] = ZYD_AL2230_RF_B_PART1; static const uint32_t rfini_part2[] = ZYD_AL2230_RF_B_PART2; static const uint32_t rfini_part3[] = ZYD_AL2230_RF_B_PART3; static const uint32_t zyd_al2230_chtable[][3] = ZYD_AL2230_CHANTABLE; int i, error; for (i = 0; i < N(phy1); i++) zyd_write16_m(sc, phy1[i].reg, phy1[i].val); /* init RF-dependent PHY registers */ for (i = 0; i < N(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val); for (i = 0; i < 3; i++) { error = zyd_rfwrite_cr(sc, zyd_al2230_chtable[0][i]); if (error != 0) return (error); } for (i = 0; i < N(rfini_part1); i++) { error = zyd_rfwrite_cr(sc, rfini_part1[i]); if (error != 0) return (error); } if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) error = zyd_rfwrite(sc, 0x241000); else error = zyd_rfwrite(sc, 0x25a000); if (error != 0) goto fail; for (i = 0; i < N(rfini_part2); i++) { error = zyd_rfwrite_cr(sc, rfini_part2[i]); if (error != 0) return (error); } for (i = 0; i < N(phy2); i++) zyd_write16_m(sc, phy2[i].reg, phy2[i].val); for (i = 0; i < N(rfini_part3); i++) { error = zyd_rfwrite_cr(sc, rfini_part3[i]); if (error != 0) return (error); } for (i = 0; i < N(phy3); i++) zyd_write16_m(sc, phy3[i].reg, phy3[i].val); error = zyd_al2230_fini(rf); fail: return (error); #undef N } static int zyd_al2230_switch_radio(struct zyd_rf *rf, int on) { struct zyd_softc *sc = rf->rf_sc; int error, on251 = (sc->sc_macrev == ZYD_ZD1211) ? 0x3f : 0x7f; zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04); zyd_write16_m(sc, ZYD_CR251, on ? on251 : 0x2f); fail: return (error); } static int zyd_al2230_set_channel(struct zyd_rf *rf, uint8_t chan) { #define N(a) (sizeof(a) / sizeof((a)[0])) int error, i; struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phy1[] = { { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 }, }; static const struct { uint32_t r1, r2, r3; } rfprog[] = ZYD_AL2230_CHANTABLE; error = zyd_rfwrite(sc, rfprog[chan - 1].r1); if (error != 0) goto fail; error = zyd_rfwrite(sc, rfprog[chan - 1].r2); if (error != 0) goto fail; error = zyd_rfwrite(sc, rfprog[chan - 1].r3); if (error != 0) goto fail; for (i = 0; i < N(phy1); i++) zyd_write16_m(sc, phy1[i].reg, phy1[i].val); fail: return (error); #undef N } static int zyd_al2230_set_channel_b(struct zyd_rf *rf, uint8_t chan) { #define N(a) (sizeof(a) / sizeof((a)[0])) int error, i; struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1; static const struct { uint32_t r1, r2, r3; } rfprog[] = ZYD_AL2230_CHANTABLE_B; for (i = 0; i < N(phy1); i++) zyd_write16_m(sc, phy1[i].reg, phy1[i].val); error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r1); if (error != 0) goto fail; error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r2); if (error != 0) goto fail; error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r3); if (error != 0) goto fail; error = zyd_al2230_fini(rf); fail: return (error); #undef N } #define ZYD_AL2230_PHY_BANDEDGE6 \ { \ { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ { ZYD_CR47, 0x1e } \ } static int zyd_al2230_bandedge6(struct zyd_rf *rf, struct ieee80211_channel *c) { #define N(a) (sizeof(a) / sizeof((a)[0])) int error = 0, i; struct zyd_softc *sc = rf->rf_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct zyd_phy_pair r[] = ZYD_AL2230_PHY_BANDEDGE6; u_int chan = ieee80211_chan2ieee(ic, c); if (chan == 1 || chan == 11) r[0].val = 0x12; for (i = 0; i < N(r); i++) zyd_write16_m(sc, r[i].reg, r[i].val); fail: return (error); #undef N } /* * AL7230B RF methods. */ static int zyd_al7230B_init(struct zyd_rf *rf) { #define N(a) (sizeof(a) / sizeof((a)[0])) struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini_1[] = ZYD_AL7230B_PHY_1; static const struct zyd_phy_pair phyini_2[] = ZYD_AL7230B_PHY_2; static const struct zyd_phy_pair phyini_3[] = ZYD_AL7230B_PHY_3; static const uint32_t rfini_1[] = ZYD_AL7230B_RF_1; static const uint32_t rfini_2[] = ZYD_AL7230B_RF_2; int i, error; /* for AL7230B, PHY and RF need to be initialized in "phases" */ /* init RF-dependent PHY registers, part one */ for (i = 0; i < N(phyini_1); i++) zyd_write16_m(sc, phyini_1[i].reg, phyini_1[i].val); /* init AL7230B radio, part one */ for (i = 0; i < N(rfini_1); i++) { if ((error = zyd_rfwrite(sc, rfini_1[i])) != 0) return (error); } /* init RF-dependent PHY registers, part two */ for (i = 0; i < N(phyini_2); i++) zyd_write16_m(sc, phyini_2[i].reg, phyini_2[i].val); /* init AL7230B radio, part two */ for (i = 0; i < N(rfini_2); i++) { if ((error = zyd_rfwrite(sc, rfini_2[i])) != 0) return (error); } /* init RF-dependent PHY registers, part three */ for (i = 0; i < N(phyini_3); i++) zyd_write16_m(sc, phyini_3[i].reg, phyini_3[i].val); fail: return (error); #undef N } static int zyd_al7230B_switch_radio(struct zyd_rf *rf, int on) { int error; struct zyd_softc *sc = rf->rf_sc; zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04); zyd_write16_m(sc, ZYD_CR251, on ? 0x3f : 0x2f); fail: return (error); } static int zyd_al7230B_set_channel(struct zyd_rf *rf, uint8_t chan) { #define N(a) (sizeof(a) / sizeof((a)[0])) struct zyd_softc *sc = rf->rf_sc; static const struct { uint32_t r1, r2; } rfprog[] = ZYD_AL7230B_CHANTABLE; static const uint32_t rfsc[] = ZYD_AL7230B_RF_SETCHANNEL; int i, error; zyd_write16_m(sc, ZYD_CR240, 0x57); zyd_write16_m(sc, ZYD_CR251, 0x2f); for (i = 0; i < N(rfsc); i++) { if ((error = zyd_rfwrite(sc, rfsc[i])) != 0) return (error); } zyd_write16_m(sc, ZYD_CR128, 0x14); zyd_write16_m(sc, ZYD_CR129, 0x12); zyd_write16_m(sc, ZYD_CR130, 0x10); zyd_write16_m(sc, ZYD_CR38, 0x38); zyd_write16_m(sc, ZYD_CR136, 0xdf); error = zyd_rfwrite(sc, rfprog[chan - 1].r1); if (error != 0) goto fail; error = zyd_rfwrite(sc, rfprog[chan - 1].r2); if (error != 0) goto fail; error = zyd_rfwrite(sc, 0x3c9000); if (error != 0) goto fail; zyd_write16_m(sc, ZYD_CR251, 0x3f); zyd_write16_m(sc, ZYD_CR203, 0x06); zyd_write16_m(sc, ZYD_CR240, 0x08); fail: return (error); #undef N } /* * AL2210 RF methods. */ static int zyd_al2210_init(struct zyd_rf *rf) { #define N(a) (sizeof(a) / sizeof((a)[0])) struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_AL2210_PHY; static const uint32_t rfini[] = ZYD_AL2210_RF; uint32_t tmp; int i, error; zyd_write32_m(sc, ZYD_CR18, 2); /* init RF-dependent PHY registers */ for (i = 0; i < N(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); /* init AL2210 radio */ for (i = 0; i < N(rfini); i++) { if ((error = zyd_rfwrite(sc, rfini[i])) != 0) return (error); } zyd_write16_m(sc, ZYD_CR47, 0x1e); zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp); zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1); zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1); zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05); zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00); zyd_write16_m(sc, ZYD_CR47, 0x1e); zyd_write32_m(sc, ZYD_CR18, 3); fail: return (error); #undef N } static int zyd_al2210_switch_radio(struct zyd_rf *rf, int on) { /* vendor driver does nothing for this RF chip */ return (0); } static int zyd_al2210_set_channel(struct zyd_rf *rf, uint8_t chan) { int error; struct zyd_softc *sc = rf->rf_sc; static const uint32_t rfprog[] = ZYD_AL2210_CHANTABLE; uint32_t tmp; zyd_write32_m(sc, ZYD_CR18, 2); zyd_write16_m(sc, ZYD_CR47, 0x1e); zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp); zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1); zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1); zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05); zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00); zyd_write16_m(sc, ZYD_CR47, 0x1e); /* actually set the channel */ error = zyd_rfwrite(sc, rfprog[chan - 1]); if (error != 0) goto fail; zyd_write32_m(sc, ZYD_CR18, 3); fail: return (error); } /* * GCT RF methods. */ static int zyd_gct_init(struct zyd_rf *rf) { #define N(a) (sizeof(a) / sizeof((a)[0])) struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_GCT_PHY; static const uint32_t rfini[] = ZYD_GCT_RF; int i, error; /* init RF-dependent PHY registers */ for (i = 0; i < N(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); /* init cgt radio */ for (i = 0; i < N(rfini); i++) { if ((error = zyd_rfwrite(sc, rfini[i])) != 0) return (error); } fail: return (error); #undef N } static int zyd_gct_switch_radio(struct zyd_rf *rf, int on) { /* vendor driver does nothing for this RF chip */ return (0); } static int zyd_gct_set_channel(struct zyd_rf *rf, uint8_t chan) { int error; struct zyd_softc *sc = rf->rf_sc; static const uint32_t rfprog[] = ZYD_GCT_CHANTABLE; error = zyd_rfwrite(sc, 0x1c0000); if (error != 0) goto fail; error = zyd_rfwrite(sc, rfprog[chan - 1]); if (error != 0) goto fail; error = zyd_rfwrite(sc, 0x1c0008); fail: return (error); } /* * Maxim RF methods. */ static int zyd_maxim_init(struct zyd_rf *rf) { #define N(a) (sizeof(a) / sizeof((a)[0])) struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY; static const uint32_t rfini[] = ZYD_MAXIM_RF; uint16_t tmp; int i, error; /* init RF-dependent PHY registers */ for (i = 0; i < N(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); zyd_read16_m(sc, ZYD_CR203, &tmp); zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); /* init maxim radio */ for (i = 0; i < N(rfini); i++) { if ((error = zyd_rfwrite(sc, rfini[i])) != 0) return (error); } zyd_read16_m(sc, ZYD_CR203, &tmp); zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); fail: return (error); #undef N } static int zyd_maxim_switch_radio(struct zyd_rf *rf, int on) { /* vendor driver does nothing for this RF chip */ return (0); } static int zyd_maxim_set_channel(struct zyd_rf *rf, uint8_t chan) { #define N(a) (sizeof(a) / sizeof((a)[0])) struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY; static const uint32_t rfini[] = ZYD_MAXIM_RF; static const struct { uint32_t r1, r2; } rfprog[] = ZYD_MAXIM_CHANTABLE; uint16_t tmp; int i, error; /* * Do the same as we do when initializing it, except for the channel * values coming from the two channel tables. */ /* init RF-dependent PHY registers */ for (i = 0; i < N(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); zyd_read16_m(sc, ZYD_CR203, &tmp); zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); /* first two values taken from the chantables */ error = zyd_rfwrite(sc, rfprog[chan - 1].r1); if (error != 0) goto fail; error = zyd_rfwrite(sc, rfprog[chan - 1].r2); if (error != 0) goto fail; /* init maxim radio - skipping the two first values */ for (i = 2; i < N(rfini); i++) { if ((error = zyd_rfwrite(sc, rfini[i])) != 0) return (error); } zyd_read16_m(sc, ZYD_CR203, &tmp); zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); fail: return (error); #undef N } /* * Maxim2 RF methods. */ static int zyd_maxim2_init(struct zyd_rf *rf) { #define N(a) (sizeof(a) / sizeof((a)[0])) struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; static const uint32_t rfini[] = ZYD_MAXIM2_RF; uint16_t tmp; int i, error; /* init RF-dependent PHY registers */ for (i = 0; i < N(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); zyd_read16_m(sc, ZYD_CR203, &tmp); zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); /* init maxim2 radio */ for (i = 0; i < N(rfini); i++) { if ((error = zyd_rfwrite(sc, rfini[i])) != 0) return (error); } zyd_read16_m(sc, ZYD_CR203, &tmp); zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); fail: return (error); #undef N } static int zyd_maxim2_switch_radio(struct zyd_rf *rf, int on) { /* vendor driver does nothing for this RF chip */ return (0); } static int zyd_maxim2_set_channel(struct zyd_rf *rf, uint8_t chan) { #define N(a) (sizeof(a) / sizeof((a)[0])) struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; static const uint32_t rfini[] = ZYD_MAXIM2_RF; static const struct { uint32_t r1, r2; } rfprog[] = ZYD_MAXIM2_CHANTABLE; uint16_t tmp; int i, error; /* * Do the same as we do when initializing it, except for the channel * values coming from the two channel tables. */ /* init RF-dependent PHY registers */ for (i = 0; i < N(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); zyd_read16_m(sc, ZYD_CR203, &tmp); zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); /* first two values taken from the chantables */ error = zyd_rfwrite(sc, rfprog[chan - 1].r1); if (error != 0) goto fail; error = zyd_rfwrite(sc, rfprog[chan - 1].r2); if (error != 0) goto fail; /* init maxim2 radio - skipping the two first values */ for (i = 2; i < N(rfini); i++) { if ((error = zyd_rfwrite(sc, rfini[i])) != 0) return (error); } zyd_read16_m(sc, ZYD_CR203, &tmp); zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); fail: return (error); #undef N } static int zyd_rf_attach(struct zyd_softc *sc, uint8_t type) { struct zyd_rf *rf = &sc->sc_rf; rf->rf_sc = sc; switch (type) { case ZYD_RF_RFMD: rf->init = zyd_rfmd_init; rf->switch_radio = zyd_rfmd_switch_radio; rf->set_channel = zyd_rfmd_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL2230: case ZYD_RF_AL2230S: if (sc->sc_macrev == ZYD_ZD1211B) { rf->init = zyd_al2230_init_b; rf->set_channel = zyd_al2230_set_channel_b; } else { rf->init = zyd_al2230_init; rf->set_channel = zyd_al2230_set_channel; } rf->switch_radio = zyd_al2230_switch_radio; rf->bandedge6 = zyd_al2230_bandedge6; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL7230B: rf->init = zyd_al7230B_init; rf->switch_radio = zyd_al7230B_switch_radio; rf->set_channel = zyd_al7230B_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL2210: rf->init = zyd_al2210_init; rf->switch_radio = zyd_al2210_switch_radio; rf->set_channel = zyd_al2210_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_GCT: rf->init = zyd_gct_init; rf->switch_radio = zyd_gct_switch_radio; rf->set_channel = zyd_gct_set_channel; rf->width = 21; /* 21-bit RF values */ break; case ZYD_RF_MAXIM_NEW: rf->init = zyd_maxim_init; rf->switch_radio = zyd_maxim_switch_radio; rf->set_channel = zyd_maxim_set_channel; rf->width = 18; /* 18-bit RF values */ break; case ZYD_RF_MAXIM_NEW2: rf->init = zyd_maxim2_init; rf->switch_radio = zyd_maxim2_switch_radio; rf->set_channel = zyd_maxim2_set_channel; rf->width = 18; /* 18-bit RF values */ break; default: device_printf(sc->sc_dev, "sorry, radio \"%s\" is not supported yet\n", zyd_rf_name(type)); return (EINVAL); } return (0); } static const char * zyd_rf_name(uint8_t type) { static const char * const zyd_rfs[] = { "unknown", "unknown", "UW2451", "UCHIP", "AL2230", "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT", "AL2230S", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2", "PHILIPS" }; return zyd_rfs[(type > 15) ? 0 : type]; } static int zyd_hw_init(struct zyd_softc *sc) { int error; const struct zyd_phy_pair *phyp; struct zyd_rf *rf = &sc->sc_rf; uint16_t val; /* specify that the plug and play is finished */ zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1); zyd_read16_m(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->sc_fwbase); DPRINTF(sc, ZYD_DEBUG_FW, "firmware base address=0x%04x\n", sc->sc_fwbase); /* retrieve firmware revision number */ zyd_read16_m(sc, sc->sc_fwbase + ZYD_FW_FIRMWARE_REV, &sc->sc_fwrev); zyd_write32_m(sc, ZYD_CR_GPI_EN, 0); zyd_write32_m(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f); /* set mandatory rates - XXX assumes 802.11b/g */ zyd_write32_m(sc, ZYD_MAC_MAN_RATE, 0x150f); /* disable interrupts */ zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0); if ((error = zyd_read_pod(sc)) != 0) { device_printf(sc->sc_dev, "could not read EEPROM\n"); goto fail; } /* PHY init (resetting) */ error = zyd_lock_phy(sc); if (error != 0) goto fail; phyp = (sc->sc_macrev == ZYD_ZD1211B) ? zyd_def_phyB : zyd_def_phy; for (; phyp->reg != 0; phyp++) zyd_write16_m(sc, phyp->reg, phyp->val); if (sc->sc_macrev == ZYD_ZD1211 && sc->sc_fix_cr157 != 0) { zyd_read16_m(sc, ZYD_EEPROM_PHY_REG, &val); zyd_write32_m(sc, ZYD_CR157, val >> 8); } error = zyd_unlock_phy(sc); if (error != 0) goto fail; /* HMAC init */ zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000020); zyd_write32_m(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808); zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0x00000000); zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0x00000000); zyd_write32_m(sc, ZYD_MAC_GHTBL, 0x00000000); zyd_write32_m(sc, ZYD_MAC_GHTBH, 0x80000000); zyd_write32_m(sc, ZYD_MAC_MISC, 0x000000a4); zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f); zyd_write32_m(sc, ZYD_MAC_BCNCFG, 0x00f00401); zyd_write32_m(sc, ZYD_MAC_PHY_DELAY2, 0x00000000); zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000080); zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000); zyd_write32_m(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100); zyd_write32_m(sc, ZYD_CR_RX_PE_DELAY, 0x00000070); zyd_write32_m(sc, ZYD_CR_PS_CTRL, 0x10000000); zyd_write32_m(sc, ZYD_MAC_RTSCTSRATE, 0x02030203); zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1); zyd_write32_m(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114); zyd_write32_m(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0a47c032); zyd_write32_m(sc, ZYD_MAC_CAM_MODE, 0x3); if (sc->sc_macrev == ZYD_ZD1211) { zyd_write32_m(sc, ZYD_MAC_RETRY, 0x00000002); zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640); } else { zyd_write32_m(sc, ZYD_MACB_MAX_RETRY, 0x02020202); zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f); zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f); zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f); zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f); zyd_write32_m(sc, ZYD_MACB_AIFS_CTL1, 0x00280028); zyd_write32_m(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C); zyd_write32_m(sc, ZYD_MACB_TXOP, 0x01800824); zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0eff); } /* init beacon interval to 100ms */ if ((error = zyd_set_beacon_interval(sc, 100)) != 0) goto fail; if ((error = zyd_rf_attach(sc, sc->sc_rfrev)) != 0) { device_printf(sc->sc_dev, "could not attach RF, rev 0x%x\n", sc->sc_rfrev); goto fail; } /* RF chip init */ error = zyd_lock_phy(sc); if (error != 0) goto fail; error = (*rf->init)(rf); if (error != 0) { device_printf(sc->sc_dev, "radio initialization failed, error %d\n", error); goto fail; } error = zyd_unlock_phy(sc); if (error != 0) goto fail; if ((error = zyd_read_eeprom(sc)) != 0) { device_printf(sc->sc_dev, "could not read EEPROM\n"); goto fail; } fail: return (error); } static int zyd_read_pod(struct zyd_softc *sc) { int error; uint32_t tmp; zyd_read32_m(sc, ZYD_EEPROM_POD, &tmp); sc->sc_rfrev = tmp & 0x0f; sc->sc_ledtype = (tmp >> 4) & 0x01; sc->sc_al2230s = (tmp >> 7) & 0x01; sc->sc_cckgain = (tmp >> 8) & 0x01; sc->sc_fix_cr157 = (tmp >> 13) & 0x01; sc->sc_parev = (tmp >> 16) & 0x0f; sc->sc_bandedge6 = (tmp >> 21) & 0x01; sc->sc_newphy = (tmp >> 31) & 0x01; sc->sc_txled = ((tmp & (1 << 24)) && (tmp & (1 << 29))) ? 0 : 1; fail: return (error); } static int zyd_read_eeprom(struct zyd_softc *sc) { uint16_t val; int error, i; /* read Tx power calibration tables */ for (i = 0; i < 7; i++) { zyd_read16_m(sc, ZYD_EEPROM_PWR_CAL + i, &val); sc->sc_pwrcal[i * 2] = val >> 8; sc->sc_pwrcal[i * 2 + 1] = val & 0xff; zyd_read16_m(sc, ZYD_EEPROM_PWR_INT + i, &val); sc->sc_pwrint[i * 2] = val >> 8; sc->sc_pwrint[i * 2 + 1] = val & 0xff; zyd_read16_m(sc, ZYD_EEPROM_36M_CAL + i, &val); sc->sc_ofdm36_cal[i * 2] = val >> 8; sc->sc_ofdm36_cal[i * 2 + 1] = val & 0xff; zyd_read16_m(sc, ZYD_EEPROM_48M_CAL + i, &val); sc->sc_ofdm48_cal[i * 2] = val >> 8; sc->sc_ofdm48_cal[i * 2 + 1] = val & 0xff; zyd_read16_m(sc, ZYD_EEPROM_54M_CAL + i, &val); sc->sc_ofdm54_cal[i * 2] = val >> 8; sc->sc_ofdm54_cal[i * 2 + 1] = val & 0xff; } fail: return (error); } static int zyd_get_macaddr(struct zyd_softc *sc) { usb_device_request_t req; usbd_status error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = ZYD_READFWDATAREQ; USETW(req.wValue, ZYD_EEPROM_MAC_ADDR_P1); USETW(req.wIndex, 0); USETW(req.wLength, IEEE80211_ADDR_LEN); error = usbd_do_request(sc->sc_udev, &req, sc->sc_bssid); if (error != 0) { device_printf(sc->sc_dev, "could not read EEPROM: %s\n", usbd_errstr(error)); } return (error); } static int zyd_set_macaddr(struct zyd_softc *sc, const uint8_t *addr) { int error; uint32_t tmp; tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; zyd_write32_m(sc, ZYD_MAC_MACADRL, tmp); tmp = addr[5] << 8 | addr[4]; zyd_write32_m(sc, ZYD_MAC_MACADRH, tmp); fail: return (error); } static int zyd_set_bssid(struct zyd_softc *sc, const uint8_t *addr) { int error; uint32_t tmp; tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; zyd_write32_m(sc, ZYD_MAC_BSSADRL, tmp); tmp = addr[5] << 8 | addr[4]; zyd_write32_m(sc, ZYD_MAC_BSSADRH, tmp); fail: return (error); } static int zyd_switch_radio(struct zyd_softc *sc, int on) { struct zyd_rf *rf = &sc->sc_rf; int error; error = zyd_lock_phy(sc); if (error != 0) goto fail; error = (*rf->switch_radio)(rf, on); if (error != 0) goto fail; error = zyd_unlock_phy(sc); fail: return (error); } static int zyd_set_led(struct zyd_softc *sc, int which, int on) { int error; uint32_t tmp; zyd_read32_m(sc, ZYD_MAC_TX_PE_CONTROL, &tmp); tmp &= ~which; if (on) tmp |= which; zyd_write32_m(sc, ZYD_MAC_TX_PE_CONTROL, tmp); fail: return (error); } static void zyd_set_multi(void *arg) { int error; struct zyd_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ifmultiaddr *ifma; uint32_t low, high; uint8_t v; if (!(ifp->if_flags & IFF_UP)) return; low = 0x00000000; high = 0x80000000; if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC))) { low = 0xffffffff; high = 0xffffffff; } else { IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; v = ((uint8_t *)LLADDR((struct sockaddr_dl *) ifma->ifma_addr))[5] >> 2; if (v < 32) low |= 1 << v; else high |= 1 << (v - 32); } IF_ADDR_UNLOCK(ifp); } /* reprogram multicast global hash table */ zyd_write32_m(sc, ZYD_MAC_GHTBL, low); zyd_write32_m(sc, ZYD_MAC_GHTBH, high); fail: if (error != 0) device_printf(sc->sc_dev, "could not set multicast hash table\n"); } static void zyd_update_mcast(struct ifnet *ifp) { struct zyd_softc *sc = ifp->if_softc; if (!(sc->sc_flags & ZYD_FLAG_INITDONE)) return; usb_add_task(sc->sc_udev, &sc->sc_mcasttask, USB_TASKQ_DRIVER); } static int zyd_set_rxfilter(struct zyd_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t rxfilter; switch (ic->ic_opmode) { case IEEE80211_M_STA: rxfilter = ZYD_FILTER_BSS; break; case IEEE80211_M_IBSS: case IEEE80211_M_HOSTAP: rxfilter = ZYD_FILTER_HOSTAP; break; case IEEE80211_M_MONITOR: rxfilter = ZYD_FILTER_MONITOR; break; default: /* should not get there */ return (EINVAL); } return zyd_write32(sc, ZYD_MAC_RXFILTER, rxfilter); } static void zyd_set_chan(struct zyd_softc *sc, struct ieee80211_channel *c) { int error; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct zyd_rf *rf = &sc->sc_rf; uint32_t tmp; u_int chan; chan = ieee80211_chan2ieee(ic, c); if (chan == 0 || chan == IEEE80211_CHAN_ANY) { /* XXX should NEVER happen */ device_printf(sc->sc_dev, "%s: invalid channel %x\n", __func__, chan); return; } error = zyd_lock_phy(sc); if (error != 0) goto fail; error = (*rf->set_channel)(rf, chan); if (error != 0) goto fail; /* update Tx power */ zyd_write16_m(sc, ZYD_CR31, sc->sc_pwrint[chan - 1]); if (sc->sc_macrev == ZYD_ZD1211B) { zyd_write16_m(sc, ZYD_CR67, sc->sc_ofdm36_cal[chan - 1]); zyd_write16_m(sc, ZYD_CR66, sc->sc_ofdm48_cal[chan - 1]); zyd_write16_m(sc, ZYD_CR65, sc->sc_ofdm54_cal[chan - 1]); zyd_write16_m(sc, ZYD_CR68, sc->sc_pwrcal[chan - 1]); zyd_write16_m(sc, ZYD_CR69, 0x28); zyd_write16_m(sc, ZYD_CR69, 0x2a); } if (sc->sc_cckgain) { /* set CCK baseband gain from EEPROM */ if (zyd_read32(sc, ZYD_EEPROM_PHY_REG, &tmp) == 0) zyd_write16_m(sc, ZYD_CR47, tmp & 0xff); } if (sc->sc_bandedge6 && rf->bandedge6 != NULL) { error = (*rf->bandedge6)(rf, c); if (error != 0) goto fail; } zyd_write32_m(sc, ZYD_CR_CONFIG_PHILIPS, 0); error = zyd_unlock_phy(sc); if (error != 0) goto fail; sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); fail: return; } static int zyd_set_beacon_interval(struct zyd_softc *sc, int bintval) { int error; uint32_t val; zyd_read32_m(sc, ZYD_CR_ATIM_WND_PERIOD, &val); sc->sc_atim_wnd = val; zyd_read32_m(sc, ZYD_CR_PRE_TBTT, &val); sc->sc_pre_tbtt = val; sc->sc_bcn_int = bintval; if (sc->sc_bcn_int <= 5) sc->sc_bcn_int = 5; if (sc->sc_pre_tbtt < 4 || sc->sc_pre_tbtt >= sc->sc_bcn_int) sc->sc_pre_tbtt = sc->sc_bcn_int - 1; if (sc->sc_atim_wnd >= sc->sc_pre_tbtt) sc->sc_atim_wnd = sc->sc_pre_tbtt - 1; zyd_write32_m(sc, ZYD_CR_ATIM_WND_PERIOD, sc->sc_atim_wnd); zyd_write32_m(sc, ZYD_CR_PRE_TBTT, sc->sc_pre_tbtt); zyd_write32_m(sc, ZYD_CR_BCN_INTERVAL, sc->sc_bcn_int); fail: return (error); } static void zyd_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct zyd_softc *sc = (struct zyd_softc *)priv; struct zyd_cmd *cmd; uint32_t datalen; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; if (status == USBD_STALLED) { usbd_clear_endpoint_stall_async( sc->sc_ep[ZYD_ENDPT_IIN]); } return; } cmd = (struct zyd_cmd *)sc->sc_ibuf; if (le16toh(cmd->code) == ZYD_NOTIF_RETRYSTATUS) { struct zyd_notif_retry *retry = (struct zyd_notif_retry *)cmd->data; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni; DPRINTF(sc, ZYD_DEBUG_TX_PROC, "retry intr: rate=0x%x addr=%s count=%d (0x%x)\n", le16toh(retry->rate), ether_sprintf(retry->macaddr), le16toh(retry->count) & 0xff, le16toh(retry->count)); /* * Find the node to which the packet was sent and update its * retry statistics. In BSS mode, this node is the AP we're * associated to so no lookup is actually needed. */ ni = ieee80211_find_txnode(vap, retry->macaddr); if (ni != NULL) { ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn, IEEE80211_AMRR_FAILURE, 1); ieee80211_free_node(ni); } if (le16toh(retry->count) & 0x100) ifp->if_oerrors++; /* too many retries */ } else if (le16toh(cmd->code) == ZYD_NOTIF_IORD) { struct zyd_rq *rqp; if (le16toh(*(uint16_t *)cmd->data) == ZYD_CR_INTERRUPT) return; /* HMAC interrupt */ usbd_get_xfer_status(xfer, NULL, NULL, &datalen, NULL); datalen -= sizeof(cmd->code); datalen -= 2; /* XXX: padding? */ STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) { int i; if (sizeof(struct zyd_pair) * rqp->len != datalen) continue; for (i = 0; i < rqp->len; i++) { if (*(((const uint16_t *)rqp->idata) + i) != (((struct zyd_pair *)cmd->data) + i)->reg) break; } if (i != rqp->len) continue; /* copy answer into caller-supplied buffer */ bcopy(cmd->data, rqp->odata, sizeof(struct zyd_pair) * rqp->len); wakeup(rqp->odata); /* wakeup caller */ return; } return; /* unexpected IORD notification */ } else { device_printf(sc->sc_dev, "unknown notification %x\n", le16toh(cmd->code)); } } static void zyd_rx_data(struct zyd_softc *sc, const uint8_t *buf, uint16_t len) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni; const struct zyd_plcphdr *plcp; const struct zyd_rx_stat *stat; struct mbuf *m; int rlen, rssi, nf; if (len < ZYD_MIN_FRAGSZ) { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too short (length=%d)\n", device_get_nameunit(sc->sc_dev), len); ifp->if_ierrors++; return; } plcp = (const struct zyd_plcphdr *)buf; stat = (const struct zyd_rx_stat *) (buf + len - sizeof(struct zyd_rx_stat)); if (stat->flags & ZYD_RX_ERROR) { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: RX status indicated error (%x)\n", device_get_nameunit(sc->sc_dev), stat->flags); ifp->if_ierrors++; return; } /* compute actual frame length */ rlen = len - sizeof(struct zyd_plcphdr) - sizeof(struct zyd_rx_stat) - IEEE80211_CRC_LEN; /* allocate a mbuf to store the frame */ if (rlen > MHLEN) m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); else m = m_gethdr(M_DONTWAIT, MT_DATA); if (m == NULL) { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: could not allocate rx mbuf\n", device_get_nameunit(sc->sc_dev)); ifp->if_ierrors++; return; } m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = rlen; bcopy((const uint8_t *)(plcp + 1), mtod(m, uint8_t *), rlen); if (bpf_peers_present(ifp->if_bpf)) { struct zyd_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; if (stat->flags & (ZYD_RX_BADCRC16 | ZYD_RX_BADCRC32)) tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; /* XXX toss, no way to express errors */ if (stat->flags & ZYD_RX_DECRYPTERR) tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; tap->wr_rate = ieee80211_plcp2rate(plcp->signal, (stat->flags & ZYD_RX_OFDM) ? IEEE80211_T_OFDM : IEEE80211_T_CCK); tap->wr_antsignal = stat->rssi + -95; tap->wr_antnoise = -95; /* XXX */ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } rssi = stat->rssi > 63 ? 127 : 2 * stat->rssi; nf = -95; /* XXX */ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { (void)ieee80211_input(ni, m, rssi, nf, 0); ieee80211_free_node(ni); } else (void)ieee80211_input_all(ic, m, rssi, nf, 0); } static void zyd_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct zyd_rx_data *data = priv; struct zyd_softc *sc = data->sc; struct ifnet *ifp = sc->sc_ifp; const struct zyd_rx_desc *desc; int len; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; if (status == USBD_STALLED) usbd_clear_endpoint_stall(sc->sc_ep[ZYD_ENDPT_BIN]); goto skip; } usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL); if (len < ZYD_MIN_RXBUFSZ) { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: xfer too short (length=%d)\n", device_get_nameunit(sc->sc_dev), len); ifp->if_ierrors++; /* XXX not really errors */ goto skip; } desc = (const struct zyd_rx_desc *) (data->buf + len - sizeof(struct zyd_rx_desc)); if (UGETW(desc->tag) == ZYD_TAG_MULTIFRAME) { const uint8_t *p = data->buf, *end = p + len; int i; DPRINTF(sc, ZYD_DEBUG_RECV, "%s: received multi-frame transfer\n", __func__); for (i = 0; i < ZYD_MAX_RXFRAMECNT; i++) { const uint16_t len16 = UGETW(desc->len[i]); if (len16 == 0 || p + len16 > end) break; zyd_rx_data(sc, p, len16); /* next frame is aligned on a 32-bit boundary */ p += (len16 + 3) & ~3; } } else { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: received single-frame transfer\n", __func__); zyd_rx_data(sc, data->buf, len); } skip: /* setup a new transfer */ usbd_setup_xfer(xfer, sc->sc_ep[ZYD_ENDPT_BIN], data, NULL, ZYX_MAX_RXBUFSZ, USBD_NO_COPY | USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, zyd_rxeof); (void)usbd_transfer(xfer); } static uint8_t zyd_plcp_signal(int rate) { switch (rate) { /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return (0xb); case 18: return (0xf); case 24: return (0xa); case 36: return (0xe); case 48: return (0x9); case 72: return (0xd); case 96: return (0x8); case 108: return (0xc); /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return (0x0); case 4: return (0x1); case 11: return (0x2); case 22: return (0x3); } return (0xff); /* XXX unsupported/unknown rate */ } static int zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = sc->sc_ifp; struct zyd_tx_desc *desc; struct zyd_tx_data *data; struct ieee80211_frame *wh; struct ieee80211_key *k; int data_idx, rate, totlen, xferlen; uint16_t pktlen; usbd_status error; data_idx = sc->sc_txidx; sc->sc_txidx = (sc->sc_txidx + 1) % ZYD_TX_LIST_CNT; data = &sc->sc_txdata[data_idx]; desc = (struct zyd_tx_desc *)data->buf; rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2; wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return (ENOBUFS); } } data->ni = ni; data->m = m0; wh = mtod(m0, struct ieee80211_frame *); xferlen = sizeof(struct zyd_tx_desc) + m0->m_pkthdr.len; totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN; /* fill Tx descriptor */ desc->len = htole16(totlen); desc->flags = ZYD_TX_FLAG_BACKOFF; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { /* multicast frames are not sent at OFDM rates in 802.11b/g */ if (totlen > vap->iv_rtsthreshold) { desc->flags |= ZYD_TX_FLAG_RTS; } else if (ZYD_RATE_IS_OFDM(rate) && (ic->ic_flags & IEEE80211_F_USEPROT)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) desc->flags |= ZYD_TX_FLAG_RTS; } } else desc->flags |= ZYD_TX_FLAG_MULTICAST; if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL)) desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); desc->phy = zyd_plcp_signal(rate); if (ZYD_RATE_IS_OFDM(rate)) { desc->phy |= ZYD_TX_PHY_OFDM; if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) desc->phy |= ZYD_TX_PHY_5GHZ; } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) desc->phy |= ZYD_TX_PHY_SHPREAMBLE; /* actual transmit length (XXX why +10?) */ pktlen = sizeof(struct zyd_tx_desc) + 10; if (sc->sc_macrev == ZYD_ZD1211) pktlen += totlen; desc->pktlen = htole16(pktlen); desc->plcp_length = (16 * totlen + rate - 1) / rate; desc->plcp_service = 0; if (rate == 22) { const int remainder = (16 * totlen) % 22; if (remainder != 0 && remainder < 7) desc->plcp_service |= ZYD_PLCP_LENGEXT; } if (bpf_peers_present(ifp->if_bpf)) { struct zyd_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + sizeof(struct zyd_tx_desc)); DPRINTF(sc, ZYD_DEBUG_XMIT, "%s: sending mgt frame len=%zu rate=%u xferlen=%u\n", device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len, rate, xferlen); usbd_setup_xfer(data->xfer, sc->sc_ep[ZYD_ENDPT_BOUT], data, data->buf, xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, ZYD_TX_TIMEOUT, zyd_txeof); error = usbd_transfer(data->xfer); if (error != USBD_IN_PROGRESS && error != 0) { ifp->if_oerrors++; return (EIO); } sc->sc_txqueued++; return (0); } static void zyd_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct zyd_tx_data *data = priv; struct zyd_softc *sc = data->sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211_node *ni; struct mbuf *m; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; device_printf(sc->sc_dev, "could not transmit buffer: %s\n", usbd_errstr(status)); if (status == USBD_STALLED) { usbd_clear_endpoint_stall_async( sc->sc_ep[ZYD_ENDPT_BOUT]); } ifp->if_oerrors++; return; } ni = data->ni; /* update rate control statistics */ ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn, IEEE80211_AMRR_SUCCESS, 0); /* * Do any tx complete callback. Note this must * be done before releasing the node reference. */ m = data->m; if (m != NULL && m->m_flags & M_TXCB) { ieee80211_process_callback(ni, m, 0); /* XXX status? */ m_freem(m); data->m = NULL; } ieee80211_free_node(ni); data->ni = NULL; ZYD_TX_LOCK(sc); sc->sc_txqueued--; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ZYD_TX_UNLOCK(sc); ifp->if_opackets++; sc->sc_txtimer = 0; zyd_start(ifp); } static int zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = sc->sc_ifp; struct zyd_tx_desc *desc; struct zyd_tx_data *data; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp; struct ieee80211_key *k; int data_idx, rate, totlen, xferlen; uint16_t pktlen; usbd_status error; data_idx = sc->sc_txidx; sc->sc_txidx = (sc->sc_txidx + 1) % ZYD_TX_LIST_CNT; wh = mtod(m0, struct ieee80211_frame *); data = &sc->sc_txdata[data_idx]; desc = (struct zyd_tx_desc *)data->buf; desc->flags = ZYD_TX_FLAG_BACKOFF; tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { rate = tp->mcastrate; desc->flags |= ZYD_TX_FLAG_MULTICAST; } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { rate = tp->ucastrate; } else { (void) ieee80211_amrr_choose(ni, &ZYD_NODE(ni)->amn); rate = ni->ni_txrate; } if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return (ENOBUFS); } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } data->ni = ni; data->m = NULL; xferlen = sizeof(struct zyd_tx_desc) + m0->m_pkthdr.len; totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN; /* fill Tx descriptor */ desc->len = htole16(totlen); if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { /* multicast frames are not sent at OFDM rates in 802.11b/g */ if (totlen > vap->iv_rtsthreshold) { desc->flags |= ZYD_TX_FLAG_RTS; } else if (ZYD_RATE_IS_OFDM(rate) && (ic->ic_flags & IEEE80211_F_USEPROT)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) desc->flags |= ZYD_TX_FLAG_RTS; } } if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL)) desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); desc->phy = zyd_plcp_signal(rate); if (ZYD_RATE_IS_OFDM(rate)) { desc->phy |= ZYD_TX_PHY_OFDM; if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) desc->phy |= ZYD_TX_PHY_5GHZ; } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) desc->phy |= ZYD_TX_PHY_SHPREAMBLE; /* actual transmit length (XXX why +10?) */ pktlen = sizeof(struct zyd_tx_desc) + 10; if (sc->sc_macrev == ZYD_ZD1211) pktlen += totlen; desc->pktlen = htole16(pktlen); desc->plcp_length = (16 * totlen + rate - 1) / rate; desc->plcp_service = 0; if (rate == 22) { const int remainder = (16 * totlen) % 22; if (remainder != 0 && remainder < 7) desc->plcp_service |= ZYD_PLCP_LENGEXT; } if (bpf_peers_present(ifp->if_bpf)) { struct zyd_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + sizeof(struct zyd_tx_desc)); DPRINTF(sc, ZYD_DEBUG_XMIT, "%s: sending data frame len=%zu rate=%u xferlen=%u\n", device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len, rate, xferlen); m_freem(m0); /* mbuf no longer needed */ usbd_setup_xfer(data->xfer, sc->sc_ep[ZYD_ENDPT_BOUT], data, data->buf, xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, ZYD_TX_TIMEOUT, zyd_txeof); error = usbd_transfer(data->xfer); if (error != USBD_IN_PROGRESS && error != 0) { ifp->if_oerrors++; return (EIO); } sc->sc_txqueued++; return (0); } static void zyd_start(struct ifnet *ifp) { struct zyd_softc *sc = ifp->if_softc; struct ieee80211_node *ni; struct mbuf *m; ZYD_TX_LOCK(sc); for (;;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; if (sc->sc_txqueued >= ZYD_TX_LIST_CNT) { IFQ_DRV_PREPEND(&ifp->if_snd, m); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); ifp->if_oerrors++; continue; } if (zyd_tx_data(sc, m, ni) != 0) { ieee80211_free_node(ni); ifp->if_oerrors++; break; } sc->sc_txtimer = 5; } ZYD_TX_UNLOCK(sc); } static int zyd_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct zyd_softc *sc = ifp->if_softc; /* prevent management frames from being sent if we're not ready */ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { m_freem(m); ieee80211_free_node(ni); return (ENETDOWN); } ZYD_TX_LOCK(sc); if (sc->sc_txqueued >= ZYD_TX_LIST_CNT) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; m_freem(m); ieee80211_free_node(ni); return (ENOBUFS); /* XXX */ } /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. * XXX raw path */ if (zyd_tx_mgt(sc, m, ni) != 0) { ZYD_TX_UNLOCK(sc); ifp->if_oerrors++; ieee80211_free_node(ni); return (EIO); } ZYD_TX_UNLOCK(sc); ifp->if_opackets++; sc->sc_txtimer = 5; return (0); } static void zyd_watchdog(void *arg) { struct zyd_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; if (sc->sc_txtimer > 0) { if (--sc->sc_txtimer == 0) { device_printf(sc->sc_dev, "device timeout\n"); /* zyd_init(ifp); XXX needs a process context ? */ ifp->if_oerrors++; return; } callout_reset(&sc->sc_watchdog_ch, hz, zyd_watchdog, sc); } } static int zyd_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct zyd_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; switch (cmd) { case SIOCSIFFLAGS: ZYD_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { if ((ifp->if_flags ^ sc->sc_if_flags) & (IFF_ALLMULTI | IFF_PROMISC)) zyd_set_multi(sc); } else { zyd_init_locked(sc); startall = 1; } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) zyd_stop(sc, 1); } sc->sc_if_flags = ifp->if_flags; ZYD_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return (error); } static void zyd_init_locked(struct zyd_softc *sc) { int error, i; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t val; if (!(sc->sc_flags & ZYD_FLAG_INITONCE)) { error = zyd_loadfirmware(sc); if (error != 0) { device_printf(sc->sc_dev, "could not load firmware (error=%d)\n", error); goto fail; } error = usbd_set_config_no(sc->sc_udev, ZYD_CONFIG_NO, 1); if (error != 0) { device_printf(sc->sc_dev, "setting config no failed\n"); goto fail; } error = usbd_device2interface_handle(sc->sc_udev, ZYD_IFACE_INDEX, &sc->sc_iface); if (error != 0) { device_printf(sc->sc_dev, "getting interface handle failed\n"); goto fail; } if ((error = zyd_open_pipes(sc)) != 0) { device_printf(sc->sc_dev, "could not open pipes\n"); goto fail; } if ((error = zyd_hw_init(sc)) != 0) { device_printf(sc->sc_dev, "hardware initialization failed\n"); goto fail; } device_printf(sc->sc_dev, "HMAC ZD1211%s, FW %02x.%02x, RF %s S%x, PA%x LED %x " "BE%x NP%x Gain%x F%x\n", (sc->sc_macrev == ZYD_ZD1211) ? "": "B", sc->sc_fwrev >> 8, sc->sc_fwrev & 0xff, zyd_rf_name(sc->sc_rfrev), sc->sc_al2230s, sc->sc_parev, sc->sc_ledtype, sc->sc_bandedge6, sc->sc_newphy, sc->sc_cckgain, sc->sc_fix_cr157); /* read regulatory domain (currently unused) */ zyd_read32_m(sc, ZYD_EEPROM_SUBID, &val); sc->sc_regdomain = val >> 16; DPRINTF(sc, ZYD_DEBUG_INIT, "regulatory domain %x\n", sc->sc_regdomain); /* we'll do software WEP decryption for now */ DPRINTF(sc, ZYD_DEBUG_INIT, "%s: setting encryption type\n", __func__); zyd_write32_m(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER); sc->sc_flags |= ZYD_FLAG_INITONCE; } if (ifp->if_drv_flags & IFF_DRV_RUNNING) zyd_stop(sc, 0); /* reset softc variables. */ sc->sc_txidx = 0; IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); DPRINTF(sc, ZYD_DEBUG_INIT, "setting MAC address to %s\n", ether_sprintf(ic->ic_myaddr)); error = zyd_set_macaddr(sc, ic->ic_myaddr); if (error != 0) return; /* set basic rates */ if (ic->ic_curmode == IEEE80211_MODE_11B) zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x0003); else if (ic->ic_curmode == IEEE80211_MODE_11A) zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x1500); else /* assumes 802.11b/g */ zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0xff0f); /* promiscuous mode */ zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0); /* multicast setup */ zyd_set_multi(sc); /* set RX filter */ error = zyd_set_rxfilter(sc); if (error != 0) goto fail; /* switch radio transmitter ON */ error = zyd_switch_radio(sc, 1); if (error != 0) goto fail; /* set default BSS channel */ zyd_set_chan(sc, ic->ic_curchan); /* * Allocate Tx and Rx xfer queues. */ if ((error = zyd_alloc_tx_list(sc)) != 0) { device_printf(sc->sc_dev, "could not allocate Tx list\n"); goto fail; } if ((error = zyd_alloc_rx_list(sc)) != 0) { device_printf(sc->sc_dev, "could not allocate Rx list\n"); goto fail; } /* * Start up the receive pipe. */ for (i = 0; i < ZYD_RX_LIST_CNT; i++) { struct zyd_rx_data *data = &sc->sc_rxdata[i]; usbd_setup_xfer(data->xfer, sc->sc_ep[ZYD_ENDPT_BIN], data, NULL, ZYX_MAX_RXBUFSZ, USBD_NO_COPY | USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, zyd_rxeof); error = usbd_transfer(data->xfer); if (error != USBD_IN_PROGRESS && error != 0) { device_printf(sc->sc_dev, "could not queue Rx transfer\n"); goto fail; } } /* enable interrupts */ zyd_write32_m(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= ZYD_FLAG_INITDONE; callout_reset(&sc->sc_watchdog_ch, hz, zyd_watchdog, sc); return; fail: zyd_stop(sc, 1); return; } static void zyd_init(void *priv) { struct zyd_softc *sc = priv; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; ZYD_LOCK(sc); zyd_init_locked(sc); ZYD_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); /* start all vap's */ } static void zyd_stop(struct zyd_softc *sc, int disable) { int error; struct ifnet *ifp = sc->sc_ifp; sc->sc_txtimer = 0; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); /* switch radio transmitter OFF */ error = zyd_switch_radio(sc, 0); if (error != 0) goto fail; /* disable Rx */ zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0); /* disable interrupts */ zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0); usb_rem_task(sc->sc_udev, &sc->sc_scantask); - usb_rem_task(sc->sc_udev, &sc->sc_task); callout_stop(&sc->sc_watchdog_ch); usbd_abort_pipe(sc->sc_ep[ZYD_ENDPT_BIN]); usbd_abort_pipe(sc->sc_ep[ZYD_ENDPT_BOUT]); zyd_free_rx_list(sc); zyd_free_tx_list(sc); fail: return; } static int zyd_loadfirmware(struct zyd_softc *sc) { usb_device_request_t req; size_t size; u_char *fw; uint8_t stat; uint16_t addr; if (sc->sc_flags & ZYD_FLAG_FWLOADED) return (0); if (sc->sc_macrev == ZYD_ZD1211) { fw = (u_char *)zd1211_firmware; size = sizeof(zd1211_firmware); } else { fw = (u_char *)zd1211b_firmware; size = sizeof(zd1211b_firmware); } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = ZYD_DOWNLOADREQ; USETW(req.wIndex, 0); addr = ZYD_FIRMWARE_START_ADDR; while (size > 0) { /* * When the transfer size is 4096 bytes, it is not * likely to be able to transfer it. * The cause is port or machine or chip? */ const int mlen = min(size, 64); DPRINTF(sc, ZYD_DEBUG_FW, "loading firmware block: len=%d, addr=0x%x\n", mlen, addr); USETW(req.wValue, addr); USETW(req.wLength, mlen); if (usbd_do_request(sc->sc_udev, &req, fw) != 0) return (EIO); addr += mlen / 2; fw += mlen; size -= mlen; } /* check whether the upload succeeded */ req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = ZYD_DOWNLOADSTS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(stat)); if (usbd_do_request(sc->sc_udev, &req, &stat) != 0) return (EIO); sc->sc_flags |= ZYD_FLAG_FWLOADED; return (stat & 0x80) ? (EIO) : (0); } static void zyd_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&ZYD_VAP(vap)->amrr, &ZYD_NODE(ni)->amn, ni); } static void zyd_scan_start(struct ieee80211com *ic) { struct zyd_softc *sc = ic->ic_ifp->if_softc; usb_rem_task(sc->sc_udev, &sc->sc_scantask); /* do it in a process context */ sc->sc_scan_action = ZYD_SCAN_START; usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); } static void zyd_scan_end(struct ieee80211com *ic) { struct zyd_softc *sc = ic->ic_ifp->if_softc; usb_rem_task(sc->sc_udev, &sc->sc_scantask); /* do it in a process context */ sc->sc_scan_action = ZYD_SCAN_END; usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); } static void zyd_set_channel(struct ieee80211com *ic) { struct zyd_softc *sc = ic->ic_ifp->if_softc; usb_rem_task(sc->sc_udev, &sc->sc_scantask); /* do it in a process context */ sc->sc_scan_action = ZYD_SET_CHANNEL; usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER); } static void zyd_scantask(void *arg) { struct zyd_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; ZYD_LOCK(sc); switch (sc->sc_scan_action) { case ZYD_SCAN_START: /* want broadcast address while scanning */ zyd_set_bssid(sc, ifp->if_broadcastaddr); break; case ZYD_SCAN_END: /* restore previous bssid */ zyd_set_bssid(sc, sc->sc_bssid); break; case ZYD_SET_CHANNEL: zyd_set_chan(sc, ic->ic_curchan); break; default: device_printf(sc->sc_dev, "unknown scan action %d\n", sc->sc_scan_action); break; } ZYD_UNLOCK(sc); } static void zyd_wakeup(struct zyd_softc *sc) { struct zyd_rq *rqp; STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) wakeup(rqp->odata); /* wakeup sleeping caller */ } static device_method_t zyd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, zyd_match), DEVMETHOD(device_attach, zyd_attach), DEVMETHOD(device_detach, zyd_detach), { 0, 0 } }; static driver_t zyd_driver = { "zyd", zyd_methods, sizeof(struct zyd_softc) }; static devclass_t zyd_devclass; DRIVER_MODULE(zyd, uhub, zyd_driver, zyd_devclass, usbd_driver_load, 0); MODULE_DEPEND(zyd, wlan, 1, 1, 1); MODULE_DEPEND(zyd, wlan_amrr, 1, 1, 1); MODULE_DEPEND(zyd, usb, 1, 1, 1); Index: user/thompsa/vaptq/sys/dev/usb/if_zydreg.h =================================================================== --- user/thompsa/vaptq/sys/dev/usb/if_zydreg.h (revision 185732) +++ user/thompsa/vaptq/sys/dev/usb/if_zydreg.h (revision 185733) @@ -1,1322 +1,1319 @@ /* $OpenBSD: if_zydreg.h,v 1.19 2006/11/30 19:28:07 damien Exp $ */ /* $NetBSD: if_zydreg.h,v 1.2 2007/06/16 11:18:45 kiyohara Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 2006 by Damien Bergamini * Copyright (c) 2006 by Florian Stoehr * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * ZyDAS ZD1211/ZD1211B USB WLAN driver. */ #define ZYD_CR_GPI_EN 0x9418 #define ZYD_CR_RADIO_PD 0x942c #define ZYD_CR_RF2948_PD 0x942c #define ZYD_CR_EN_PS_MANUAL_AGC 0x943c #define ZYD_CR_CONFIG_PHILIPS 0x9440 #define ZYD_CR_I2C_WRITE 0x9444 #define ZYD_CR_SA2400_SER_RP 0x9448 #define ZYD_CR_RADIO_PE 0x9458 #define ZYD_CR_RST_BUS_MASTER 0x945c #define ZYD_CR_RFCFG 0x9464 #define ZYD_CR_HSTSCHG 0x946c #define ZYD_CR_PHY_ON 0x9474 #define ZYD_CR_RX_DELAY 0x9478 #define ZYD_CR_RX_PE_DELAY 0x947c #define ZYD_CR_GPIO_1 0x9490 #define ZYD_CR_GPIO_2 0x9494 #define ZYD_CR_EnZYD_CRyBufMux 0x94a8 #define ZYD_CR_PS_CTRL 0x9500 #define ZYD_CR_ADDA_PWR_DWN 0x9504 #define ZYD_CR_ADDA_MBIAS_WT 0x9508 #define ZYD_CR_INTERRUPT 0x9510 #define ZYD_CR_MAC_PS_STATE 0x950c #define ZYD_CR_ATIM_WND_PERIOD 0x951c #define ZYD_CR_BCN_INTERVAL 0x9520 #define ZYD_CR_PRE_TBTT 0x9524 /* * MAC registers. */ #define ZYD_MAC_MACADRL 0x9610 /* MAC address (low) */ #define ZYD_MAC_MACADRH 0x9614 /* MAC address (high) */ #define ZYD_MAC_BSSADRL 0x9618 /* BSS address (low) */ #define ZYD_MAC_BSSADRH 0x961c /* BSS address (high) */ #define ZYD_MAC_BCNCFG 0x9620 /* BCN configuration */ #define ZYD_MAC_GHTBL 0x9624 /* Group hash table (low) */ #define ZYD_MAC_GHTBH 0x9628 /* Group hash table (high) */ #define ZYD_MAC_RX_TIMEOUT 0x962c /* Rx timeout value */ #define ZYD_MAC_BAS_RATE 0x9630 /* Basic rate setting */ #define ZYD_MAC_MAN_RATE 0x9634 /* Mandatory rate setting */ #define ZYD_MAC_RTSCTSRATE 0x9638 /* RTS CTS rate */ #define ZYD_MAC_BACKOFF_PROTECT 0x963c /* Backoff protection */ #define ZYD_MAC_RX_THRESHOLD 0x9640 /* Rx threshold */ #define ZYD_MAC_TX_PE_CONTROL 0x9644 /* Tx_PE control */ #define ZYD_MAC_AFTER_PNP 0x9648 /* After PnP */ #define ZYD_MAC_RX_PE_DELAY 0x964c /* Rx_pe delay */ #define ZYD_MAC_RX_ADDR2_L 0x9650 /* RX address2 (low) */ #define ZYD_MAC_RX_ADDR2_H 0x9654 /* RX address2 (high) */ #define ZYD_MAC_SIFS_ACK_TIME 0x9658 /* Dynamic SIFS ack time */ #define ZYD_MAC_PHY_DELAY 0x9660 /* PHY delay */ #define ZYD_MAC_PHY_DELAY2 0x966c /* PHY delay */ #define ZYD_MAC_BCNFIFO 0x9670 /* Beacon FIFO I/O port */ #define ZYD_MAC_SNIFFER 0x9674 /* Sniffer on/off */ #define ZYD_MAC_ENCRYPTION_TYPE 0x9678 /* Encryption type */ #define ZYD_MAC_RETRY 0x967c /* Retry time */ #define ZYD_MAC_MISC 0x9680 /* Misc */ #define ZYD_MAC_STMACHINESTAT 0x9684 /* State machine status */ #define ZYD_MAC_TX_UNDERRUN_CNT 0x9688 /* TX underrun counter */ #define ZYD_MAC_RXFILTER 0x968c /* Send to host settings */ #define ZYD_MAC_ACK_EXT 0x9690 /* Acknowledge extension */ #define ZYD_MAC_BCNFIFOST 0x9694 /* BCN FIFO set and status */ #define ZYD_MAC_DIFS_EIFS_SIFS 0x9698 /* DIFS, EIFS & SIFS settings */ #define ZYD_MAC_RX_TIMEOUT_CNT 0x969c /* RX timeout count */ #define ZYD_MAC_RX_TOTAL_FRAME 0x96a0 /* RX total frame count */ #define ZYD_MAC_RX_CRC32_CNT 0x96a4 /* RX CRC32 frame count */ #define ZYD_MAC_RX_CRC16_CNT 0x96a8 /* RX CRC16 frame count */ #define ZYD_MAC_RX_UDEC 0x96ac /* RX unicast decr. error count */ #define ZYD_MAC_RX_OVERRUN_CNT 0x96b0 /* RX FIFO overrun count */ #define ZYD_MAC_RX_MDEC 0x96bc /* RX multicast decr. err. cnt. */ #define ZYD_MAC_NAV_TCR 0x96c4 /* NAV timer count read */ #define ZYD_MAC_BACKOFF_ST_RD 0x96c8 /* Backoff status read */ #define ZYD_MAC_DM_RETRY_CNT_RD 0x96cc /* DM retry count read */ #define ZYD_MAC_RX_ACR 0x96d0 /* RX arbitration count read */ #define ZYD_MAC_TX_CCR 0x96d4 /* Tx complete count read */ #define ZYD_MAC_TCB_ADDR 0x96e8 /* Current PCI process TCP addr */ #define ZYD_MAC_RCB_ADDR 0x96ec /* Next RCB address */ #define ZYD_MAC_CONT_WIN_LIMIT 0x96f0 /* Contention window limit */ #define ZYD_MAC_TX_PKT 0x96f4 /* Tx total packet count read */ #define ZYD_MAC_DL_CTRL 0x96f8 /* Download control */ #define ZYD_MAC_CAM_MODE 0x9700 /* CAM: Continuous Access Mode */ #define ZYD_MACB_TXPWR_CTL1 0x9b00 #define ZYD_MACB_TXPWR_CTL2 0x9b04 #define ZYD_MACB_TXPWR_CTL3 0x9b08 #define ZYD_MACB_TXPWR_CTL4 0x9b0c #define ZYD_MACB_AIFS_CTL1 0x9b10 #define ZYD_MACB_AIFS_CTL2 0x9b14 #define ZYD_MACB_TXOP 0x9b20 #define ZYD_MACB_MAX_RETRY 0x9b28 /* * Miscellanous registers. */ #define ZYD_FIRMWARE_START_ADDR 0xee00 #define ZYD_FIRMWARE_BASE_ADDR 0xee1d /* Firmware base address */ /* * EEPROM registers. */ #define ZYD_EEPROM_START_HEAD 0xf800 /* EEPROM start */ #define ZYD_EEPROM_SUBID 0xf817 #define ZYD_EEPROM_POD 0xf819 #define ZYD_EEPROM_MAC_ADDR_P1 0xf81b /* Part 1 of the MAC address */ #define ZYD_EEPROM_MAC_ADDR_P2 0xf81d /* Part 2 of the MAC address */ #define ZYD_EEPROM_PWR_CAL 0xf81f /* Calibration */ #define ZYD_EEPROM_PWR_INT 0xf827 /* Calibration */ #define ZYD_EEPROM_ALLOWEDCHAN 0xf82f /* Allowed CH mask, 1 bit each */ #define ZYD_EEPROM_DEVICE_VER 0xf837 /* Device version */ #define ZYD_EEPROM_PHY_REG 0xf83c /* PHY registers */ #define ZYD_EEPROM_36M_CAL 0xf83f /* Calibration */ #define ZYD_EEPROM_11A_INT 0xf847 /* Interpolation */ #define ZYD_EEPROM_48M_CAL 0xf84f /* Calibration */ #define ZYD_EEPROM_48M_INT 0xf857 /* Interpolation */ #define ZYD_EEPROM_54M_CAL 0xf85f /* Calibration */ #define ZYD_EEPROM_54M_INT 0xf867 /* Interpolation */ /* * Firmware registers offsets (relative to fwbase). */ #define ZYD_FW_FIRMWARE_REV 0x0000 /* Firmware version */ #define ZYD_FW_USB_SPEED 0x0001 /* USB speed (!=0 if highspeed) */ #define ZYD_FW_FIX_TX_RATE 0x0002 /* Fixed TX rate */ #define ZYD_FW_LINK_STATUS 0x0003 #define ZYD_FW_SOFT_RESET 0x0004 #define ZYD_FW_FLASH_CHK 0x0005 /* possible flags for register ZYD_FW_LINK_STATUS */ #define ZYD_LED1 (1 << 8) #define ZYD_LED2 (1 << 9) /* * RF IDs. */ #define ZYD_RF_UW2451 0x2 /* not supported yet */ #define ZYD_RF_UCHIP 0x3 /* not supported yet */ #define ZYD_RF_AL2230 0x4 #define ZYD_RF_AL7230B 0x5 #define ZYD_RF_THETA 0x6 /* not supported yet */ #define ZYD_RF_AL2210 0x7 #define ZYD_RF_MAXIM_NEW 0x8 #define ZYD_RF_GCT 0x9 #define ZYD_RF_AL2230S 0xa /* not supported yet */ #define ZYD_RF_RALINK 0xb /* not supported yet */ #define ZYD_RF_INTERSIL 0xc /* not supported yet */ #define ZYD_RF_RFMD 0xd #define ZYD_RF_MAXIM_NEW2 0xe #define ZYD_RF_PHILIPS 0xf /* not supported yet */ /* * PHY registers (8 bits, not documented). */ #define ZYD_CR0 0x9000 #define ZYD_CR1 0x9004 #define ZYD_CR2 0x9008 #define ZYD_CR3 0x900c #define ZYD_CR5 0x9010 #define ZYD_CR6 0x9014 #define ZYD_CR7 0x9018 #define ZYD_CR8 0x901c #define ZYD_CR4 0x9020 #define ZYD_CR9 0x9024 #define ZYD_CR10 0x9028 #define ZYD_CR11 0x902c #define ZYD_CR12 0x9030 #define ZYD_CR13 0x9034 #define ZYD_CR14 0x9038 #define ZYD_CR15 0x903c #define ZYD_CR16 0x9040 #define ZYD_CR17 0x9044 #define ZYD_CR18 0x9048 #define ZYD_CR19 0x904c #define ZYD_CR20 0x9050 #define ZYD_CR21 0x9054 #define ZYD_CR22 0x9058 #define ZYD_CR23 0x905c #define ZYD_CR24 0x9060 #define ZYD_CR25 0x9064 #define ZYD_CR26 0x9068 #define ZYD_CR27 0x906c #define ZYD_CR28 0x9070 #define ZYD_CR29 0x9074 #define ZYD_CR30 0x9078 #define ZYD_CR31 0x907c #define ZYD_CR32 0x9080 #define ZYD_CR33 0x9084 #define ZYD_CR34 0x9088 #define ZYD_CR35 0x908c #define ZYD_CR36 0x9090 #define ZYD_CR37 0x9094 #define ZYD_CR38 0x9098 #define ZYD_CR39 0x909c #define ZYD_CR40 0x90a0 #define ZYD_CR41 0x90a4 #define ZYD_CR42 0x90a8 #define ZYD_CR43 0x90ac #define ZYD_CR44 0x90b0 #define ZYD_CR45 0x90b4 #define ZYD_CR46 0x90b8 #define ZYD_CR47 0x90bc #define ZYD_CR48 0x90c0 #define ZYD_CR49 0x90c4 #define ZYD_CR50 0x90c8 #define ZYD_CR51 0x90cc #define ZYD_CR52 0x90d0 #define ZYD_CR53 0x90d4 #define ZYD_CR54 0x90d8 #define ZYD_CR55 0x90dc #define ZYD_CR56 0x90e0 #define ZYD_CR57 0x90e4 #define ZYD_CR58 0x90e8 #define ZYD_CR59 0x90ec #define ZYD_CR60 0x90f0 #define ZYD_CR61 0x90f4 #define ZYD_CR62 0x90f8 #define ZYD_CR63 0x90fc #define ZYD_CR64 0x9100 #define ZYD_CR65 0x9104 #define ZYD_CR66 0x9108 #define ZYD_CR67 0x910c #define ZYD_CR68 0x9110 #define ZYD_CR69 0x9114 #define ZYD_CR70 0x9118 #define ZYD_CR71 0x911c #define ZYD_CR72 0x9120 #define ZYD_CR73 0x9124 #define ZYD_CR74 0x9128 #define ZYD_CR75 0x912c #define ZYD_CR76 0x9130 #define ZYD_CR77 0x9134 #define ZYD_CR78 0x9138 #define ZYD_CR79 0x913c #define ZYD_CR80 0x9140 #define ZYD_CR81 0x9144 #define ZYD_CR82 0x9148 #define ZYD_CR83 0x914c #define ZYD_CR84 0x9150 #define ZYD_CR85 0x9154 #define ZYD_CR86 0x9158 #define ZYD_CR87 0x915c #define ZYD_CR88 0x9160 #define ZYD_CR89 0x9164 #define ZYD_CR90 0x9168 #define ZYD_CR91 0x916c #define ZYD_CR92 0x9170 #define ZYD_CR93 0x9174 #define ZYD_CR94 0x9178 #define ZYD_CR95 0x917c #define ZYD_CR96 0x9180 #define ZYD_CR97 0x9184 #define ZYD_CR98 0x9188 #define ZYD_CR99 0x918c #define ZYD_CR100 0x9190 #define ZYD_CR101 0x9194 #define ZYD_CR102 0x9198 #define ZYD_CR103 0x919c #define ZYD_CR104 0x91a0 #define ZYD_CR105 0x91a4 #define ZYD_CR106 0x91a8 #define ZYD_CR107 0x91ac #define ZYD_CR108 0x91b0 #define ZYD_CR109 0x91b4 #define ZYD_CR110 0x91b8 #define ZYD_CR111 0x91bc #define ZYD_CR112 0x91c0 #define ZYD_CR113 0x91c4 #define ZYD_CR114 0x91c8 #define ZYD_CR115 0x91cc #define ZYD_CR116 0x91d0 #define ZYD_CR117 0x91d4 #define ZYD_CR118 0x91d8 #define ZYD_CR119 0x91dc #define ZYD_CR120 0x91e0 #define ZYD_CR121 0x91e4 #define ZYD_CR122 0x91e8 #define ZYD_CR123 0x91ec #define ZYD_CR124 0x91f0 #define ZYD_CR125 0x91f4 #define ZYD_CR126 0x91f8 #define ZYD_CR127 0x91fc #define ZYD_CR128 0x9200 #define ZYD_CR129 0x9204 #define ZYD_CR130 0x9208 #define ZYD_CR131 0x920c #define ZYD_CR132 0x9210 #define ZYD_CR133 0x9214 #define ZYD_CR134 0x9218 #define ZYD_CR135 0x921c #define ZYD_CR136 0x9220 #define ZYD_CR137 0x9224 #define ZYD_CR138 0x9228 #define ZYD_CR139 0x922c #define ZYD_CR140 0x9230 #define ZYD_CR141 0x9234 #define ZYD_CR142 0x9238 #define ZYD_CR143 0x923c #define ZYD_CR144 0x9240 #define ZYD_CR145 0x9244 #define ZYD_CR146 0x9248 #define ZYD_CR147 0x924c #define ZYD_CR148 0x9250 #define ZYD_CR149 0x9254 #define ZYD_CR150 0x9258 #define ZYD_CR151 0x925c #define ZYD_CR152 0x9260 #define ZYD_CR153 0x9264 #define ZYD_CR154 0x9268 #define ZYD_CR155 0x926c #define ZYD_CR156 0x9270 #define ZYD_CR157 0x9274 #define ZYD_CR158 0x9278 #define ZYD_CR159 0x927c #define ZYD_CR160 0x9280 #define ZYD_CR161 0x9284 #define ZYD_CR162 0x9288 #define ZYD_CR163 0x928c #define ZYD_CR164 0x9290 #define ZYD_CR165 0x9294 #define ZYD_CR166 0x9298 #define ZYD_CR167 0x929c #define ZYD_CR168 0x92a0 #define ZYD_CR169 0x92a4 #define ZYD_CR170 0x92a8 #define ZYD_CR171 0x92ac #define ZYD_CR172 0x92b0 #define ZYD_CR173 0x92b4 #define ZYD_CR174 0x92b8 #define ZYD_CR175 0x92bc #define ZYD_CR176 0x92c0 #define ZYD_CR177 0x92c4 #define ZYD_CR178 0x92c8 #define ZYD_CR179 0x92cc #define ZYD_CR180 0x92d0 #define ZYD_CR181 0x92d4 #define ZYD_CR182 0x92d8 #define ZYD_CR183 0x92dc #define ZYD_CR184 0x92e0 #define ZYD_CR185 0x92e4 #define ZYD_CR186 0x92e8 #define ZYD_CR187 0x92ec #define ZYD_CR188 0x92f0 #define ZYD_CR189 0x92f4 #define ZYD_CR190 0x92f8 #define ZYD_CR191 0x92fc #define ZYD_CR192 0x9300 #define ZYD_CR193 0x9304 #define ZYD_CR194 0x9308 #define ZYD_CR195 0x930c #define ZYD_CR196 0x9310 #define ZYD_CR197 0x9314 #define ZYD_CR198 0x9318 #define ZYD_CR199 0x931c #define ZYD_CR200 0x9320 #define ZYD_CR201 0x9324 #define ZYD_CR202 0x9328 #define ZYD_CR203 0x932c #define ZYD_CR204 0x9330 #define ZYD_CR205 0x9334 #define ZYD_CR206 0x9338 #define ZYD_CR207 0x933c #define ZYD_CR208 0x9340 #define ZYD_CR209 0x9344 #define ZYD_CR210 0x9348 #define ZYD_CR211 0x934c #define ZYD_CR212 0x9350 #define ZYD_CR213 0x9354 #define ZYD_CR214 0x9358 #define ZYD_CR215 0x935c #define ZYD_CR216 0x9360 #define ZYD_CR217 0x9364 #define ZYD_CR218 0x9368 #define ZYD_CR219 0x936c #define ZYD_CR220 0x9370 #define ZYD_CR221 0x9374 #define ZYD_CR222 0x9378 #define ZYD_CR223 0x937c #define ZYD_CR224 0x9380 #define ZYD_CR225 0x9384 #define ZYD_CR226 0x9388 #define ZYD_CR227 0x938c #define ZYD_CR228 0x9390 #define ZYD_CR229 0x9394 #define ZYD_CR230 0x9398 #define ZYD_CR231 0x939c #define ZYD_CR232 0x93a0 #define ZYD_CR233 0x93a4 #define ZYD_CR234 0x93a8 #define ZYD_CR235 0x93ac #define ZYD_CR236 0x93b0 #define ZYD_CR240 0x93c0 #define ZYD_CR241 0x93c4 #define ZYD_CR242 0x93c8 #define ZYD_CR243 0x93cc #define ZYD_CR244 0x93d0 #define ZYD_CR245 0x93d4 #define ZYD_CR251 0x93ec #define ZYD_CR252 0x93f0 #define ZYD_CR253 0x93f4 #define ZYD_CR254 0x93f8 #define ZYD_CR255 0x93fc /* copied nearly verbatim from the Linux driver rewrite */ #define ZYD_DEF_PHY \ { \ { ZYD_CR0, 0x0a }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \ { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xa0 }, \ { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0x7f }, \ { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \ { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \ { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0c }, \ { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \ { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x19 }, \ { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \ { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \ { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \ { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \ { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \ { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x12 }, { ZYD_CR46, 0xff }, \ { ZYD_CR47, 0x1e }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \ { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \ { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \ { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \ { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \ { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \ { ZYD_CR79, 0x68 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \ { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x00 }, { ZYD_CR84, 0x00 }, \ { ZYD_CR85, 0x02 }, { ZYD_CR86, 0x00 }, { ZYD_CR87, 0x00 }, \ { ZYD_CR88, 0xff }, { ZYD_CR89, 0xfc }, { ZYD_CR90, 0x00 }, \ { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x08 }, \ { ZYD_CR94, 0x00 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0xff }, \ { ZYD_CR97, 0xe7 }, { ZYD_CR98, 0x00 }, { ZYD_CR99, 0x00 }, \ { ZYD_CR100, 0x00 }, { ZYD_CR101, 0xae }, { ZYD_CR102, 0x02 }, \ { ZYD_CR103, 0x00 }, { ZYD_CR104, 0x03 }, { ZYD_CR105, 0x65 }, \ { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \ { ZYD_CR109, 0xaa }, { ZYD_CR110, 0xaa }, { ZYD_CR111, 0x25 }, \ { ZYD_CR112, 0x25 }, { ZYD_CR113, 0x00 }, { ZYD_CR119, 0x1e }, \ { ZYD_CR125, 0x90 }, { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, \ { ZYD_CR5, 0x00 }, { ZYD_CR6, 0x00 }, { ZYD_CR7, 0x00 }, \ { ZYD_CR8, 0x00 }, { ZYD_CR9, 0x20 }, { ZYD_CR12, 0xf0 }, \ { ZYD_CR20, 0x0e }, { ZYD_CR21, 0x0e }, { ZYD_CR27, 0x10 }, \ { ZYD_CR44, 0x33 }, { ZYD_CR47, 0x1E }, { ZYD_CR83, 0x24 }, \ { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0C }, \ { ZYD_CR87, 0x12 }, { ZYD_CR88, 0x0C }, { ZYD_CR89, 0x00 }, \ { ZYD_CR90, 0x10 }, { ZYD_CR91, 0x08 }, { ZYD_CR93, 0x00 }, \ { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x50 }, \ { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR101, 0x13 }, \ { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, \ { ZYD_CR105, 0x12 }, { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, \ { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, \ { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, \ { ZYD_CR117, 0xfc }, { ZYD_CR118, 0xfa }, { ZYD_CR120, 0x4f }, \ { ZYD_CR125, 0xaa }, { ZYD_CR127, 0x03 }, { ZYD_CR128, 0x14 }, \ { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0C }, \ { ZYD_CR136, 0xdf }, { ZYD_CR137, 0x40 }, { ZYD_CR138, 0xa0 }, \ { ZYD_CR139, 0xb0 }, { ZYD_CR140, 0x99 }, { ZYD_CR141, 0x82 }, \ { ZYD_CR142, 0x54 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \ { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x4c }, { ZYD_CR149, 0x50 }, \ { ZYD_CR150, 0x0e }, { ZYD_CR151, 0x18 }, { ZYD_CR160, 0xfe }, \ { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, { ZYD_CR163, 0xfa }, \ { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, { ZYD_CR166, 0xbe }, \ { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, { ZYD_CR169, 0xba }, \ { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, { ZYD_CR204, 0x7d }, \ { ZYD_CR203, 0x30 }, { 0, 0} \ } #define ZYD_DEF_PHYB \ { \ { ZYD_CR0, 0x14 }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \ { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xe0 }, \ { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0xf0 }, \ { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \ { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \ { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x10 }, { ZYD_CR21, 0x0e }, \ { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \ { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x10 }, \ { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \ { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \ { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \ { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \ { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \ { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x33 }, { ZYD_CR46, 0xff }, \ { ZYD_CR47, 0x1E }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \ { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \ { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \ { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \ { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \ { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \ { ZYD_CR79, 0xf0 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \ { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, \ { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0c }, { ZYD_CR87, 0x12 }, \ { ZYD_CR88, 0x0c }, { ZYD_CR89, 0x00 }, { ZYD_CR90, 0x58 }, \ { ZYD_CR91, 0x04 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x00 }, \ { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x20 }, { ZYD_CR96, 0x50 }, \ { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR99, 0x00 }, \ { ZYD_CR100, 0x01 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, \ { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \ { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x27 }, \ { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, \ { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfc }, \ { ZYD_CR118, 0xfa }, { ZYD_CR119, 0x1e }, { ZYD_CR125, 0x90 }, \ { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, { ZYD_CR128, 0x14 }, \ { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0c }, \ { ZYD_CR136, 0xdf }, { ZYD_CR137, 0xa0 }, { ZYD_CR138, 0xa8 }, \ { ZYD_CR139, 0xb4 }, { ZYD_CR140, 0x98 }, { ZYD_CR141, 0x82 }, \ { ZYD_CR142, 0x53 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \ { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x40 }, { ZYD_CR149, 0x40 }, \ { ZYD_CR150, 0x14 }, { ZYD_CR151, 0x18 }, { ZYD_CR159, 0x70 }, \ { ZYD_CR160, 0xfe }, { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, \ { ZYD_CR163, 0xfa }, { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, \ { ZYD_CR166, 0xbe }, { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, \ { ZYD_CR169, 0xba }, { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, \ { ZYD_CR204, 0x7d }, { ZYD_CR203, 0x30 }, \ { 0, 0 } \ } #define ZYD_RFMD_PHY \ { \ { ZYD_CR2, 0x1e }, { ZYD_CR9, 0x20 }, { ZYD_CR10, 0x89 }, \ { ZYD_CR11, 0x00 }, { ZYD_CR15, 0xd0 }, { ZYD_CR17, 0x68 }, \ { ZYD_CR19, 0x4a }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0e }, \ { ZYD_CR23, 0x48 }, { ZYD_CR24, 0x14 }, { ZYD_CR26, 0x90 }, \ { ZYD_CR27, 0x30 }, { ZYD_CR29, 0x20 }, { ZYD_CR31, 0xb2 }, \ { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x28 }, { ZYD_CR38, 0x30 }, \ { ZYD_CR34, 0x0f }, { ZYD_CR35, 0xf0 }, { ZYD_CR41, 0x2a }, \ { ZYD_CR46, 0x7f }, { ZYD_CR47, 0x1e }, { ZYD_CR51, 0xc5 }, \ { ZYD_CR52, 0xc5 }, { ZYD_CR53, 0xc5 }, { ZYD_CR79, 0x58 }, \ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR82, 0x00 }, \ { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, \ { ZYD_CR86, 0x10 }, { ZYD_CR87, 0x2a }, { ZYD_CR88, 0x10 }, \ { ZYD_CR89, 0x24 }, { ZYD_CR90, 0x18 }, { ZYD_CR91, 0x00 }, \ { ZYD_CR92, 0x0a }, { ZYD_CR93, 0x00 }, { ZYD_CR94, 0x01 }, \ { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x40 }, { ZYD_CR97, 0x37 }, \ { ZYD_CR98, 0x05 }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x00 }, \ { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, \ { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, { ZYD_CR106, 0x1a }, \ { ZYD_CR107, 0x24 }, { ZYD_CR108, 0x0a }, { ZYD_CR109, 0x13 }, \ { ZYD_CR110, 0x2f }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, \ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x40 }, \ { ZYD_CR116, 0x40 }, { ZYD_CR117, 0xf0 }, { ZYD_CR118, 0xf0 }, \ { ZYD_CR119, 0x16 }, { ZYD_CR122, 0x00 }, { ZYD_CR127, 0x03 }, \ { ZYD_CR131, 0x08 }, { ZYD_CR138, 0x28 }, { ZYD_CR148, 0x44 }, \ { ZYD_CR150, 0x10 }, { ZYD_CR169, 0xbb }, { ZYD_CR170, 0xbb } \ } #define ZYD_RFMD_RF \ { \ 0x000007, 0x07dd43, 0x080959, 0x0e6666, 0x116a57, 0x17dd43, \ 0x1819f9, 0x1e6666, 0x214554, 0x25e7fa, 0x27fffa, 0x294128, \ 0x2c0000, 0x300000, 0x340000, 0x381e0f, 0x6c180f \ } #define ZYD_RFMD_CHANTABLE \ { \ { 0x181979, 0x1e6666 }, \ { 0x181989, 0x1e6666 }, \ { 0x181999, 0x1e6666 }, \ { 0x1819a9, 0x1e6666 }, \ { 0x1819b9, 0x1e6666 }, \ { 0x1819c9, 0x1e6666 }, \ { 0x1819d9, 0x1e6666 }, \ { 0x1819e9, 0x1e6666 }, \ { 0x1819f9, 0x1e6666 }, \ { 0x181a09, 0x1e6666 }, \ { 0x181a19, 0x1e6666 }, \ { 0x181a29, 0x1e6666 }, \ { 0x181a39, 0x1e6666 }, \ { 0x181a60, 0x1c0000 } \ } #define ZYD_AL2230_PHY \ { \ { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, \ { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, \ { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, \ { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x2b }, \ { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, { ZYD_CR10, 0x89 }, \ { ZYD_CR17, 0x28 }, { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, \ { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, \ { ZYD_CR46, 0x96 }, { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, \ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, \ { ZYD_CR89, 0x04 }, { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, \ { ZYD_CR100, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x09 }, \ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfc }, \ { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, \ { ZYD_CR122, 0xe0 }, { ZYD_CR137, 0x88 }, { ZYD_CR252, 0xff }, \ { ZYD_CR253, 0xff }, { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f }, \ { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } \ } #define ZYD_AL2230_PHY_B \ { \ { ZYD_CR10, 0x89 }, { ZYD_CR15, 0x20 }, { ZYD_CR17, 0x2B }, \ { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x93 }, \ { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, { ZYD_CR33, 0x28 }, \ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, \ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x99 }, { ZYD_CR47, 0x1e }, \ { ZYD_CR48, 0x06 }, { ZYD_CR49, 0xf9 }, { ZYD_CR51, 0x01 }, \ { ZYD_CR52, 0x80 }, { ZYD_CR53, 0x7e }, { ZYD_CR65, 0x00 }, \ { ZYD_CR66, 0x00 }, { ZYD_CR67, 0x00 }, { ZYD_CR68, 0x00 }, \ { ZYD_CR69, 0x28 }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \ { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \ { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x0a }, { ZYD_CR98, 0x8d }, \ { ZYD_CR99, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x13 }, \ { ZYD_CR110, 0x1f }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, \ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfa }, \ { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x6c }, \ { ZYD_CR122, 0xfc }, { ZYD_CR123, 0x57 }, { ZYD_CR125, 0xad }, \ { ZYD_CR126, 0x6c }, { ZYD_CR127, 0x03 }, { ZYD_CR137, 0x50 }, \ { ZYD_CR138, 0xa8 }, { ZYD_CR144, 0xac }, { ZYD_CR150, 0x0d }, \ { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 } \ } #define ZYD_AL2230_PHY_PART1 \ { \ { ZYD_CR240, 0x57 }, { ZYD_CR9, 0xe0 } \ } #define ZYD_AL2230_PHY_PART2 \ { \ { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x7f }, \ } #define ZYD_AL2230_PHY_PART3 \ { \ { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ } #define ZYD_AL2230S_PHY_INIT \ { \ { ZYD_CR47, 0x1e }, { ZYD_CR106, 0x22 }, { ZYD_CR107, 0x2a }, \ { ZYD_CR109, 0x13 }, { ZYD_CR118, 0xf8 }, { ZYD_CR119, 0x12 }, \ { ZYD_CR122, 0xe0 }, { ZYD_CR128, 0x10 }, { ZYD_CR129, 0x0e }, \ { ZYD_CR130, 0x10 } \ } #define ZYD_AL2230_PHY_FINI_PART1 \ { \ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR79, 0x58 }, \ { ZYD_CR12, 0xf0 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x58 }, \ { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 }, \ } #define ZYD_AL2230_RF_PART1 \ { \ 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3 \ } #define ZYD_AL2230_RF_PART2 \ { \ 0x000da4, 0x0f4dc5, 0x0805b6, 0x011687, 0x000688, 0x0403b9, \ 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00500f \ } #define ZYD_AL2230_RF_PART3 \ { \ 0x00d00f, 0x004c0f, 0x00540f, 0x00700f, 0x00500f \ } #define ZYD_AL2230_RF_B \ { \ 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3, \ 0x0005a4, 0x0f4dc5, 0x0805b6, 0x0146c7, 0x000688, 0x0403b9, \ 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00580f \ } #define ZYD_AL2230_RF_B_PART1 \ { \ 0x8cccd0, 0x481dc0, 0xcfff00, 0x25a000 \ } #define ZYD_AL2230_RF_B_PART2 \ { \ 0x25a000, 0xa3b2f0, 0x6da010, 0xe36280, 0x116000, 0x9dc020, \ 0x5ddb00, 0xd99000, 0x3ffbd0, 0xb00000, 0xf01a00 \ } #define ZYD_AL2230_RF_B_PART3 \ { \ 0xf01b00, 0xf01e00, 0xf01a00 \ } #define ZYD_AL2230_CHANTABLE \ { \ { 0x03f790, 0x033331, 0x00000d }, \ { 0x03f790, 0x0b3331, 0x00000d }, \ { 0x03e790, 0x033331, 0x00000d }, \ { 0x03e790, 0x0b3331, 0x00000d }, \ { 0x03f7a0, 0x033331, 0x00000d }, \ { 0x03f7a0, 0x0b3331, 0x00000d }, \ { 0x03e7a0, 0x033331, 0x00000d }, \ { 0x03e7a0, 0x0b3331, 0x00000d }, \ { 0x03f7b0, 0x033331, 0x00000d }, \ { 0x03f7b0, 0x0b3331, 0x00000d }, \ { 0x03e7b0, 0x033331, 0x00000d }, \ { 0x03e7b0, 0x0b3331, 0x00000d }, \ { 0x03f7c0, 0x033331, 0x00000d }, \ { 0x03e7c0, 0x066661, 0x00000d } \ } #define ZYD_AL2230_CHANTABLE_B \ { \ { 0x09efc0, 0x8cccc0, 0xb00000 }, \ { 0x09efc0, 0x8cccd0, 0xb00000 }, \ { 0x09e7c0, 0x8cccc0, 0xb00000 }, \ { 0x09e7c0, 0x8cccd0, 0xb00000 }, \ { 0x05efc0, 0x8cccc0, 0xb00000 }, \ { 0x05efc0, 0x8cccd0, 0xb00000 }, \ { 0x05e7c0, 0x8cccc0, 0xb00000 }, \ { 0x05e7c0, 0x8cccd0, 0xb00000 }, \ { 0x0defc0, 0x8cccc0, 0xb00000 }, \ { 0x0defc0, 0x8cccd0, 0xb00000 }, \ { 0x0de7c0, 0x8cccc0, 0xb00000 }, \ { 0x0de7c0, 0x8cccd0, 0xb00000 }, \ { 0x03efc0, 0x8cccc0, 0xb00000 }, \ { 0x03e7c0, 0x866660, 0xb00000 } \ } #define ZYD_AL7230B_PHY_1 \ { \ { ZYD_CR240, 0x57 }, { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, \ { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, \ { ZYD_CR29, 0x00 }, { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x22 }, \ { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, \ { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, \ { ZYD_CR122, 0xfc }, { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x28 }, \ { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, \ { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x96 }, \ { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \ { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \ { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x02 }, \ { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR106, 0x22 }, \ { ZYD_CR107, 0x3f }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x1f }, \ { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, { ZYD_CR113, 0x27 }, \ { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, { ZYD_CR116, 0x3f }, \ { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfc }, { ZYD_CR119, 0x10 }, \ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR137, 0x88 }, \ { ZYD_CR138, 0xa8 }, { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 }, \ { ZYD_CR251, 0x2f } \ } #define ZYD_AL7230B_PHY_2 \ { \ { ZYD_CR251, 0x3f }, { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, \ { ZYD_CR130, 0x10 }, { ZYD_CR38, 0x38 }, { ZYD_CR136, 0xdf } \ } #define ZYD_AL7230B_PHY_3 \ { \ { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 } \ } #define ZYD_AL7230B_RF_1 \ { \ 0x09ec04, 0x8cccc8, 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, \ 0x6cf56a, 0xe04073, 0x193d76, 0x9dd844, 0x500007, 0xd8c010, \ 0x3c9000, 0xbfffff, 0x700000, 0xf15d58 \ } #define ZYD_AL7230B_RF_2 \ { \ 0xf15d59, 0xf15d5c, 0xf15d58 \ } #define ZYD_AL7230B_RF_SETCHANNEL \ { \ 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, 0x6cf56a, 0xe04073, \ 0x193d76, 0x9dd844, 0x500007, 0xd8c010, 0x3c9000, 0xf15d58 \ } #define ZYD_AL7230B_CHANTABLE \ { \ { 0x09ec00, 0x8cccc8 }, \ { 0x09ec00, 0x8cccd8 }, \ { 0x09ec00, 0x8cccc0 }, \ { 0x09ec00, 0x8cccd0 }, \ { 0x05ec00, 0x8cccc8 }, \ { 0x05ec00, 0x8cccd8 }, \ { 0x05ec00, 0x8cccc0 }, \ { 0x05ec00, 0x8cccd0 }, \ { 0x0dec00, 0x8cccc8 }, \ { 0x0dec00, 0x8cccd8 }, \ { 0x0dec00, 0x8cccc0 }, \ { 0x0dec00, 0x8cccd0 }, \ { 0x03ec00, 0x8cccc8 }, \ { 0x03ec00, 0x866660 } \ } #define ZYD_AL2210_PHY \ { \ { ZYD_CR9, 0xe0 }, { ZYD_CR10, 0x91 }, { ZYD_CR12, 0x90 }, \ { ZYD_CR15, 0xd0 }, { ZYD_CR16, 0x40 }, { ZYD_CR17, 0x58 }, \ { ZYD_CR18, 0x04 }, { ZYD_CR23, 0x66 }, { ZYD_CR24, 0x14 }, \ { ZYD_CR26, 0x90 }, { ZYD_CR31, 0x80 }, { ZYD_CR34, 0x06 }, \ { ZYD_CR35, 0x3e }, { ZYD_CR38, 0x38 }, { ZYD_CR46, 0x90 }, \ { ZYD_CR47, 0x1e }, { ZYD_CR64, 0x64 }, { ZYD_CR79, 0xb5 }, \ { ZYD_CR80, 0x38 }, { ZYD_CR81, 0x30 }, { ZYD_CR113, 0xc0 }, \ { ZYD_CR127, 0x03 } \ } #define ZYD_AL2210_RF \ { \ 0x2396c0, 0x00fcb1, 0x358132, 0x0108b3, 0xc77804, 0x456415, \ 0xff2226, 0x806667, 0x7860f8, 0xbb01c9, 0x00000a, 0x00000b \ } #define ZYD_AL2210_CHANTABLE \ { \ 0x0196c0, 0x019710, 0x019760, 0x0197b0, 0x019800, 0x019850, \ 0x0198a0, 0x0198f0, 0x019940, 0x019990, 0x0199e0, 0x019a30, \ 0x019a80, 0x019b40 \ } #define ZYD_GCT_PHY \ { \ { ZYD_CR47, 0x1e }, { ZYD_CR15, 0xdc }, { ZYD_CR113, 0xc0 }, \ { ZYD_CR20, 0x0c }, { ZYD_CR17, 0x65 }, { ZYD_CR34, 0x04 }, \ { ZYD_CR35, 0x35 }, { ZYD_CR24, 0x20 }, { ZYD_CR9, 0xe0 }, \ { ZYD_CR127, 0x02 }, { ZYD_CR10, 0x91 }, { ZYD_CR23, 0x7f }, \ { ZYD_CR27, 0x10 }, { ZYD_CR28, 0x7a }, { ZYD_CR79, 0xb5 }, \ { ZYD_CR64, 0x80 }, { ZYD_CR33, 0x28 }, { ZYD_CR38, 0x30 } \ } #define ZYD_GCT_RF \ { \ 0x1f0000, 0x1f0000, 0x1f0200, 0x1f0600, 0x1f8600, 0x1f8600, \ 0x002050, 0x1f8000, 0x1f8200, 0x1f8600, 0x1c0000, 0x10c458, \ 0x088e92, 0x187b82, 0x0401b4, 0x140816, 0x0c7000, 0x1c0000, \ 0x02ccae, 0x128023, 0x0a0000, 0x1a0000, 0x06e380, 0x16cb94, \ 0x0e1740, 0x014980, 0x116240, 0x090000, 0x192304, 0x05112f, \ 0x0d54a8, 0x0f8000, 0x1c0008, 0x1c0000, 0x1a0000, 0x1c0008, \ 0x150000, 0x0c7000, 0x150800, 0x150000 \ } #define ZYD_GCT_CHANTABLE \ { \ 0x1a0000, 0x1a8000, 0x1a4000, 0x1ac000, 0x1a2000, 0x1aa000, \ 0x1a6000, 0x1ae000, 0x1a1000, 0x1a9000, 0x1a5000, 0x1ad000, \ 0x1a3000, 0x1ab000 \ } #define ZYD_MAXIM_PHY \ { \ { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \ { ZYD_CR29, 0x00 }, { ZYD_CR26, 0x11 }, { ZYD_CR44, 0x33 }, \ { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x2b }, \ { ZYD_CR110, 0x2b }, { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, \ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfa }, \ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR122, 0xfe }, \ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x13 }, \ { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x13 }, \ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0x00 }, \ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x06 }, { ZYD_CR122, 0xfe }, \ { ZYD_CR150, 0x0d } \ } #define ZYD_MAXIM_RF \ { \ 0x00ccd4, 0x030a03, 0x000400, 0x000ca1, 0x010072, 0x018645, \ 0x004006, 0x0000a7, 0x008258, 0x003fc9, 0x00040a, 0x00000b, \ 0x00026c \ } #define ZYD_MAXIM_CHANTABLE \ { \ { 0x0ccd4, 0x30a03 }, \ { 0x22224, 0x00a13 }, \ { 0x37774, 0x10a13 }, \ { 0x0ccd4, 0x30a13 }, \ { 0x22224, 0x00a23 }, \ { 0x37774, 0x10a23 }, \ { 0x0ccd4, 0x30a23 }, \ { 0x22224, 0x00a33 }, \ { 0x37774, 0x10a33 }, \ { 0x0ccd4, 0x30a33 }, \ { 0x22224, 0x00a43 }, \ { 0x37774, 0x10a43 }, \ { 0x0ccd4, 0x30a43 }, \ { 0x199a4, 0x20a53 } \ } #define ZYD_MAXIM2_PHY \ { \ { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \ { ZYD_CR29, 0x00 }, { ZYD_CR26, 0x11 }, { ZYD_CR44, 0x33 }, \ { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x2b }, \ { ZYD_CR110, 0x2b }, { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, \ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfa }, \ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR122, 0xfe }, \ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR79, 0x58 }, \ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR89, 0x18 }, \ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0x00 }, \ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x06 }, { ZYD_CR122, 0xfe } \ } #define ZYD_MAXIM2_RF \ { \ 0x33334, 0x10a03, 0x00400, 0x00ca1, 0x10072, 0x18645, 0x04006, \ 0x000a7, 0x08258, 0x03fc9, 0x0040a, 0x0000b, 0x0026c \ } #define ZYD_MAXIM2_CHANTABLE_F \ { \ 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, \ 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x26664 \ } #define ZYD_MAXIM2_CHANTABLE \ { \ { 0x33334, 0x10a03 }, \ { 0x08884, 0x20a13 }, \ { 0x1ddd4, 0x30a13 }, \ { 0x33334, 0x10a13 }, \ { 0x08884, 0x20a23 }, \ { 0x1ddd4, 0x30a23 }, \ { 0x33334, 0x10a23 }, \ { 0x08884, 0x20a33 }, \ { 0x1ddd4, 0x30a33 }, \ { 0x33334, 0x10a33 }, \ { 0x08884, 0x20a43 }, \ { 0x1ddd4, 0x30a43 }, \ { 0x33334, 0x10a43 }, \ { 0x26664, 0x20a53 } \ } /* * Control pipe requests. */ #define ZYD_DOWNLOADREQ 0x30 #define ZYD_DOWNLOADSTS 0x31 #define ZYD_READFWDATAREQ 0x32 /* possible values for register ZYD_CR_INTERRUPT */ #define ZYD_HWINT_MASK 0x004f0000 /* possible values for register ZYD_MAC_MISC */ #define ZYD_UNLOCK_PHY_REGS 0x80 /* possible values for register ZYD_MAC_ENCRYPTION_TYPE */ #define ZYD_ENC_SNIFFER 8 /* flags for register ZYD_MAC_RXFILTER */ #define ZYD_FILTER_ASS_REQ (1 << 0) #define ZYD_FILTER_ASS_RSP (1 << 1) #define ZYD_FILTER_REASS_REQ (1 << 2) #define ZYD_FILTER_REASS_RSP (1 << 3) #define ZYD_FILTER_PRB_REQ (1 << 4) #define ZYD_FILTER_PRB_RSP (1 << 5) #define ZYD_FILTER_BCN (1 << 8) #define ZYD_FILTER_ATIM (1 << 9) #define ZYD_FILTER_DEASS (1 << 10) #define ZYD_FILTER_AUTH (1 << 11) #define ZYD_FILTER_DEAUTH (1 << 12) #define ZYD_FILTER_PS_POLL (1 << 26) #define ZYD_FILTER_RTS (1 << 27) #define ZYD_FILTER_CTS (1 << 28) #define ZYD_FILTER_ACK (1 << 29) #define ZYD_FILTER_CFE (1 << 30) #define ZYD_FILTER_CFE_A (1 << 31) /* helpers for register ZYD_MAC_RXFILTER */ #define ZYD_FILTER_MONITOR 0xffffffff #define ZYD_FILTER_BSS \ (ZYD_FILTER_ASS_REQ | ZYD_FILTER_ASS_RSP | \ ZYD_FILTER_REASS_REQ | ZYD_FILTER_REASS_RSP | \ ZYD_FILTER_PRB_REQ | ZYD_FILTER_PRB_RSP | \ (0x3 << 6) | \ ZYD_FILTER_BCN | ZYD_FILTER_ATIM | ZYD_FILTER_DEASS | \ ZYD_FILTER_AUTH | ZYD_FILTER_DEAUTH | \ (0x7 << 13) | \ ZYD_FILTER_PS_POLL | ZYD_FILTER_ACK) #define ZYD_FILTER_HOSTAP \ (ZYD_FILTER_ASS_REQ | ZYD_FILTER_REASS_REQ | \ ZYD_FILTER_PRB_REQ | ZYD_FILTER_DEASS | ZYD_FILTER_AUTH | \ ZYD_FILTER_DEAUTH | ZYD_FILTER_PS_POLL) struct zyd_tx_desc { uint8_t phy; #define ZYD_TX_PHY_SIGNAL(x) ((x) & 0xf) #define ZYD_TX_PHY_OFDM (1 << 4) #define ZYD_TX_PHY_SHPREAMBLE (1 << 5) /* CCK */ #define ZYD_TX_PHY_5GHZ (1 << 5) /* OFDM */ uint16_t len; uint8_t flags; #define ZYD_TX_FLAG_BACKOFF (1 << 0) #define ZYD_TX_FLAG_MULTICAST (1 << 1) #define ZYD_TX_FLAG_TYPE(x) (((x) & 0x3) << 2) #define ZYD_TX_TYPE_DATA 0 #define ZYD_TX_TYPE_PS_POLL 1 #define ZYD_TX_TYPE_MGMT 2 #define ZYD_TX_TYPE_CTL 3 #define ZYD_TX_FLAG_WAKEUP (1 << 4) #define ZYD_TX_FLAG_RTS (1 << 5) #define ZYD_TX_FLAG_ENCRYPT (1 << 6) #define ZYD_TX_FLAG_CTS_TO_SELF (1 << 7) uint16_t pktlen; uint16_t plcp_length; uint8_t plcp_service; #define ZYD_PLCP_LENGEXT 0x80 uint16_t nextlen; } __packed; struct zyd_plcphdr { uint8_t signal; uint8_t reserved[2]; uint16_t service; /* unaligned! */ } __packed; struct zyd_rx_stat { uint8_t signal_cck; uint8_t rssi; uint8_t signal_ofdm; uint8_t cipher; #define ZYD_RX_CIPHER_WEP64 1 #define ZYD_RX_CIPHER_TKIP 2 #define ZYD_RX_CIPHER_AES 4 #define ZYD_RX_CIPHER_WEP128 5 #define ZYD_RX_CIPHER_WEP256 6 #define ZYD_RX_CIPHER_WEP \ (ZYD_RX_CIPHER_WEP64 | ZYD_RX_CIPHER_WEP128 | ZYD_RX_CIPHER_WEP256) uint8_t flags; #define ZYD_RX_OFDM (1 << 0) #define ZYD_RX_TIMEOUT (1 << 1) #define ZYD_RX_OVERRUN (1 << 2) #define ZYD_RX_DECRYPTERR (1 << 3) #define ZYD_RX_BADCRC32 (1 << 4) #define ZYD_RX_NOT2ME (1 << 5) #define ZYD_RX_BADCRC16 (1 << 6) #define ZYD_RX_ERROR (1 << 7) } __packed; /* this structure may be unaligned */ struct zyd_rx_desc { #define ZYD_MAX_RXFRAMECNT 3 uWord len[ZYD_MAX_RXFRAMECNT]; uWord tag; #define ZYD_TAG_MULTIFRAME 0x697e } __packed; /* I2C bus alike */ struct zyd_rfwrite_cmd { uint16_t code; uint16_t width; uint16_t bit[32]; #define ZYD_RF_IF_LE (1 << 1) #define ZYD_RF_CLK (1 << 2) #define ZYD_RF_DATA (1 << 3) } __packed; struct zyd_cmd { uint16_t code; #define ZYD_CMD_IOWR 0x0021 /* write HMAC or PHY register */ #define ZYD_CMD_IORD 0x0022 /* read HMAC or PHY register */ #define ZYD_CMD_RFCFG 0x0023 /* write RF register */ #define ZYD_NOTIF_IORD 0x9001 /* response for ZYD_CMD_IORD */ #define ZYD_NOTIF_MACINTR 0x9001 /* interrupt notification */ #define ZYD_NOTIF_RETRYSTATUS 0xa001 /* Tx retry notification */ uint8_t data[64]; } __packed; /* structure for command ZYD_CMD_IOWR */ struct zyd_pair { uint16_t reg; /* helpers macros to read/write 32-bit registers */ #define ZYD_REG32_LO(reg) (reg) #define ZYD_REG32_HI(reg) \ ((reg) + ((((reg) & 0xf000) == 0x9000) ? 2 : 1)) uint16_t val; } __packed; /* structure for notification ZYD_NOTIF_RETRYSTATUS */ struct zyd_notif_retry { uint16_t rate; uint8_t macaddr[IEEE80211_ADDR_LEN]; uint16_t count; } __packed; #define ZYD_CONFIG_NO 1 #define ZYD_IFACE_INDEX 0 #define ZYD_INTR_TIMEOUT 1000 #define ZYD_TX_TIMEOUT 10000 #define ZYD_MAX_TXBUFSZ \ (sizeof(struct zyd_tx_desc) + MCLBYTES) #define ZYD_MIN_FRAGSZ \ (sizeof(struct zyd_plcphdr) + IEEE80211_MIN_LEN + \ sizeof(struct zyd_rx_stat)) #define ZYD_MIN_RXBUFSZ ZYD_MIN_FRAGSZ #define ZYX_MAX_RXBUFSZ \ ((sizeof (struct zyd_plcphdr) + IEEE80211_MAX_LEN + \ sizeof (struct zyd_rx_stat)) * ZYD_MAX_RXFRAMECNT + \ sizeof (struct zyd_rx_desc)) #define ZYD_RX_LIST_CNT 1 #define ZYD_TX_LIST_CNT 5 #define ZYD_CMD_FLAG_READ (1 << 0) /* quickly determine if a given rate is CCK or OFDM */ #define ZYD_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) struct zyd_phy_pair { uint16_t reg; uint8_t val; }; struct zyd_mac_pair { uint16_t reg; uint32_t val; }; struct zyd_tx_data { struct zyd_softc *sc; usbd_xfer_handle xfer; uint8_t *buf; struct ieee80211_node *ni; struct mbuf *m; }; struct zyd_rx_data { struct zyd_softc *sc; usbd_xfer_handle xfer; const uint8_t *buf; }; struct zyd_node { struct ieee80211_node ni; /* must be the first */ struct ieee80211_amrr_node amn; }; #define ZYD_NODE(ni) ((struct zyd_node *)(ni)) struct zyd_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint8_t wr_flags; uint8_t wr_rate; uint16_t wr_chan_freq; uint16_t wr_chan_flags; int8_t wr_antsignal; int8_t wr_antnoise; } __packed; #define ZYD_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL)) struct zyd_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; uint8_t wt_rate; uint16_t wt_chan_freq; uint16_t wt_chan_flags; } __packed; #define ZYD_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL)) struct zyd_softc; /* forward declaration */ struct zyd_rf { /* RF methods */ int (*init)(struct zyd_rf *); int (*switch_radio)(struct zyd_rf *, int); int (*set_channel)(struct zyd_rf *, uint8_t); int (*bandedge6)(struct zyd_rf *, struct ieee80211_channel *); /* RF attributes */ struct zyd_softc *rf_sc; /* back-pointer */ int width; }; struct zyd_rq { const uint16_t *idata; struct zyd_pair *odata; int len; STAILQ_ENTRY(zyd_rq) rq; }; struct zyd_vap { struct ieee80211vap vap; int (*newstate)(struct ieee80211vap *, enum ieee80211_state, int); struct callout amrr_ch; struct ieee80211_amrr amrr; }; #define ZYD_VAP(vap) ((struct zyd_vap *)(vap)) struct zyd_softc { device_t sc_dev; usbd_device_handle sc_udev; usbd_interface_handle sc_iface; struct ifnet *sc_ifp; - enum ieee80211_state sc_state; - int sc_arg; int sc_flags; #define ZYD_FLAG_FWLOADED (1 << 0) #define ZYD_FLAG_DETACHING (1 << 1) #define ZYD_FLAG_INITONCE (1 << 2) #define ZYD_FLAG_INITDONE (1 << 3) int sc_if_flags; uint32_t sc_debug; struct usb_task sc_mcasttask; struct usb_task sc_scantask; int sc_scan_action; #define ZYD_SCAN_START 0 #define ZYD_SCAN_END 1 #define ZYD_SET_CHANNEL 2 - struct usb_task sc_task; struct callout sc_watchdog_ch; struct zyd_rf sc_rf; STAILQ_HEAD(, zyd_rq) sc_rqh; uint8_t sc_bssid[IEEE80211_ADDR_LEN]; uint16_t sc_fwbase; uint8_t sc_regdomain; uint8_t sc_macrev; uint16_t sc_fwrev; uint8_t sc_rfrev; uint8_t sc_parev; uint8_t sc_al2230s; uint8_t sc_bandedge6; uint8_t sc_newphy; uint8_t sc_cckgain; uint8_t sc_fix_cr157; uint8_t sc_ledtype; uint8_t sc_txled; uint32_t sc_atim_wnd; uint32_t sc_pre_tbtt; uint32_t sc_bcn_int; uint8_t sc_pwrcal[14]; uint8_t sc_pwrint[14]; uint8_t sc_ofdm36_cal[14]; uint8_t sc_ofdm48_cal[14]; uint8_t sc_ofdm54_cal[14]; #define ZYD_ENDPT_BOUT 0 #define ZYD_ENDPT_BIN 1 #define ZYD_ENDPT_IIN 2 #define ZYD_ENDPT_IOUT 3 #define ZYD_ENDPT_CNT 4 usbd_pipe_handle sc_ep[ZYD_ENDPT_CNT]; uint8_t *sc_ibuf; struct mtx sc_txmtx; struct zyd_rx_data sc_rxdata[ZYD_RX_LIST_CNT]; struct zyd_tx_data sc_txdata[ZYD_TX_LIST_CNT]; int sc_txidx; int sc_txqueued; int sc_txtimer; struct zyd_rx_radiotap_header sc_rxtap; int sc_rxtap_len; struct zyd_tx_radiotap_header sc_txtap; int sc_txtap_len; }; #define ZYD_LOCK(sc) do { ((sc) = (sc)); mtx_lock(&Giant); } while (0) #define ZYD_UNLOCK(sc) mtx_unlock(&Giant) #define ZYD_TX_LOCK(sc) mtx_lock(&(sc)->sc_txmtx) #define ZYD_TX_UNLOCK(sc) mtx_unlock(&(sc)->sc_txmtx) Index: user/thompsa/vaptq/sys/dev/usb2/wlan/if_rum2.c =================================================================== --- user/thompsa/vaptq/sys/dev/usb2/wlan/if_rum2.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/usb2/wlan/if_rum2.c (revision 185733) @@ -1,2961 +1,2963 @@ /*- * Copyright (c) 2005-2007 Damien Bergamini * Copyright (c) 2006 Niall O'Higgins * Copyright (c) 2007-2008 Hans Petter Selasky * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * NOTE: all function names beginning like "rum_cfg_" can only * be called from within the config thread function ! */ #include __FBSDID("$FreeBSD$"); /*- * Ralink Technology RT2501USB/RT2601USB chipset driver * http://www.ralinktech.com.tw/ */ #include #include #include #include #define usb2_config_td_cc rum_config_copy #define usb2_config_td_softc rum_softc #define USB_DEBUG_VAR rum_debug #include #include #include #include #include #include #include #include #include #include #include #include #if USB_DEBUG static int rum_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, rum, CTLFLAG_RW, 0, "USB rum"); SYSCTL_INT(_hw_usb2_rum, OID_AUTO, debug, CTLFLAG_RW, &rum_debug, 0, "Debug level"); #endif /* prototypes */ static device_probe_t rum_probe; static device_attach_t rum_attach; static device_detach_t rum_detach; static usb2_callback_t rum_bulk_read_callback; static usb2_callback_t rum_bulk_read_clear_stall_callback; static usb2_callback_t rum_bulk_write_callback; static usb2_callback_t rum_bulk_write_clear_stall_callback; static usb2_config_td_command_t rum_cfg_first_time_setup; static usb2_config_td_command_t rum_config_copy; static usb2_config_td_command_t rum_cfg_scan_start; static usb2_config_td_command_t rum_cfg_scan_end; static usb2_config_td_command_t rum_cfg_select_band; static usb2_config_td_command_t rum_cfg_set_chan; static usb2_config_td_command_t rum_cfg_enable_tsf_sync; static usb2_config_td_command_t rum_cfg_enable_mrr; static usb2_config_td_command_t rum_cfg_update_slot; static usb2_config_td_command_t rum_cfg_select_antenna; static usb2_config_td_command_t rum_cfg_set_txpreamble; static usb2_config_td_command_t rum_cfg_update_promisc; static usb2_config_td_command_t rum_cfg_pre_init; static usb2_config_td_command_t rum_cfg_init; static usb2_config_td_command_t rum_cfg_pre_stop; static usb2_config_td_command_t rum_cfg_stop; static usb2_config_td_command_t rum_cfg_amrr_timeout; static usb2_config_td_command_t rum_cfg_prepare_beacon; static usb2_config_td_command_t rum_cfg_newstate; static const char *rum_get_rf(uint32_t rev); static int rum_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data); static void rum_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func); static void rum_scan_start_cb(struct ieee80211com *); static void rum_scan_end_cb(struct ieee80211com *); static void rum_set_channel_cb(struct ieee80211com *); static uint16_t rum_cfg_eeprom_read_2(struct rum_softc *sc, uint16_t addr); static uint32_t rum_cfg_bbp_disbusy(struct rum_softc *sc); static uint32_t rum_cfg_read(struct rum_softc *sc, uint16_t reg); static uint8_t rum_cfg_bbp_init(struct rum_softc *sc); static uint8_t rum_cfg_bbp_read(struct rum_softc *sc, uint8_t reg); static void rum_cfg_amrr_start(struct rum_softc *sc); static void rum_cfg_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val); static void rum_cfg_do_request(struct rum_softc *sc, struct usb2_device_request *req, void *data); static void rum_cfg_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, uint16_t len); static void rum_cfg_load_microcode(struct rum_softc *sc, const uint8_t *ucode, uint16_t size); static void rum_cfg_read_eeprom(struct rum_softc *sc); static void rum_cfg_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len); static void rum_cfg_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val); static void rum_cfg_set_bssid(struct rum_softc *sc, uint8_t *bssid); static void rum_cfg_set_macaddr(struct rum_softc *sc, uint8_t *addr); static void rum_cfg_write(struct rum_softc *sc, uint16_t reg, uint32_t val); static void rum_cfg_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len); static void rum_end_of_commands(struct rum_softc *sc); static void rum_init_cb(void *arg); static void rum_start_cb(struct ifnet *ifp); static void rum_watchdog(void *arg); static uint8_t rum_get_rssi(struct rum_softc *sc, uint8_t raw); static struct ieee80211vap *rum_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void rum_vap_delete(struct ieee80211vap *); static struct ieee80211_node *rum_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]); static void rum_newassoc(struct ieee80211_node *, int); static void rum_cfg_disable_tsf_sync(struct rum_softc *sc); static void rum_cfg_set_run(struct rum_softc *sc, struct rum_config_copy *cc); static void rum_fill_write_queue(struct rum_softc *sc); static void rum_tx_clean_queue(struct rum_softc *sc); static void rum_tx_freem(struct mbuf *m); static void rum_tx_mgt(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni); static struct ieee80211vap *rum_get_vap(struct rum_softc *sc); static void rum_tx_data(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni); static void rum_tx_prot(struct rum_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, uint8_t prot, uint16_t rate); static void rum_tx_raw(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params); static int rum_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params); static void rum_setup_desc_and_tx(struct rum_softc *sc, struct mbuf *m, uint32_t flags, uint16_t xflags, uint16_t rate); static int rum_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg); static void rum_update_mcast_cb(struct ifnet *ifp); static void rum_update_promisc_cb(struct ifnet *ifp); /* various supported device vendors/products */ static const struct usb2_device_id rum_devs[] = { {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_HWU54DM, 0)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_2, 0)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_3, 0)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_4, 0)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WUG2700, 0)}, {USB_VPI(USB_VENDOR_AMIT, USB_PRODUCT_AMIT_CGWLUSB2GO, 0)}, {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_1, 0)}, {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_2, 0)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050A, 0)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D9050V3, 0)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GC, 0)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GR, 0)}, {USB_VPI(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU2, 0)}, {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GL, 0)}, {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GPX, 0)}, {USB_VPI(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_CWD854F, 0)}, {USB_VPI(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_RT2573, 0)}, {USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWLG122C1, 0)}, {USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_WUA1340, 0)}, {USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA111, 0)}, {USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA110, 0)}, {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWB01GS, 0)}, {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWI05GS, 0)}, {USB_VPI(USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_RT2573, 0)}, {USB_VPI(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_RT2573, 0)}, {USB_VPI(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254LB, 0)}, {USB_VPI(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254V2AP, 0)}, {USB_VPI(USB_VENDOR_HUAWEI3COM, USB_PRODUCT_HUAWEI3COM_WUB320G, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_G54HP, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_SG54HP, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_1, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_2, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_3, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_4, 0)}, {USB_VPI(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_RT2573, 0)}, {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54HP, 0)}, {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54MINI2, 0)}, {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUSMM, 0)}, {USB_VPI(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573, 0)}, {USB_VPI(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573_2, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573_2, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2671, 0)}, {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113R2, 0)}, {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL172, 0)}, {USB_VPI(USB_VENDOR_SPARKLAN, USB_PRODUCT_SPARKLAN_RT2573, 0)}, {USB_VPI(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2573, 0)}, }; struct rum_def_mac { uint32_t reg; uint32_t val; }; static const struct rum_def_mac rum_def_mac[] = { {RT2573_TXRX_CSR0, 0x025fb032}, {RT2573_TXRX_CSR1, 0x9eaa9eaf}, {RT2573_TXRX_CSR2, 0x8a8b8c8d}, {RT2573_TXRX_CSR3, 0x00858687}, {RT2573_TXRX_CSR7, 0x2e31353b}, {RT2573_TXRX_CSR8, 0x2a2a2a2c}, {RT2573_TXRX_CSR15, 0x0000000f}, {RT2573_MAC_CSR6, 0x00000fff}, {RT2573_MAC_CSR8, 0x016c030a}, {RT2573_MAC_CSR10, 0x00000718}, {RT2573_MAC_CSR12, 0x00000004}, {RT2573_MAC_CSR13, 0x00007f00}, {RT2573_SEC_CSR0, 0x00000000}, {RT2573_SEC_CSR1, 0x00000000}, {RT2573_SEC_CSR5, 0x00000000}, {RT2573_PHY_CSR1, 0x000023b0}, {RT2573_PHY_CSR5, 0x00040a06}, {RT2573_PHY_CSR6, 0x00080606}, {RT2573_PHY_CSR7, 0x00000408}, {RT2573_AIFSN_CSR, 0x00002273}, {RT2573_CWMIN_CSR, 0x00002344}, {RT2573_CWMAX_CSR, 0x000034aa} }; struct rum_def_bbp { uint8_t reg; uint8_t val; }; static const struct rum_def_bbp rum_def_bbp[] = { {3, 0x80}, {15, 0x30}, {17, 0x20}, {21, 0xc8}, {22, 0x38}, {23, 0x06}, {24, 0xfe}, {25, 0x0a}, {26, 0x0d}, {32, 0x0b}, {34, 0x12}, {37, 0x07}, {39, 0xf8}, {41, 0x60}, {53, 0x10}, {54, 0x18}, {60, 0x10}, {61, 0x04}, {62, 0x04}, {75, 0xfe}, {86, 0xfe}, {88, 0xfe}, {90, 0x0f}, {99, 0x00}, {102, 0x16}, {107, 0x04} }; struct rfprog { uint8_t chan; uint32_t r1, r2, r3, r4; }; static const struct rfprog rum_rf5226[] = { {1, 0x00b03, 0x001e1, 0x1a014, 0x30282}, {2, 0x00b03, 0x001e1, 0x1a014, 0x30287}, {3, 0x00b03, 0x001e2, 0x1a014, 0x30282}, {4, 0x00b03, 0x001e2, 0x1a014, 0x30287}, {5, 0x00b03, 0x001e3, 0x1a014, 0x30282}, {6, 0x00b03, 0x001e3, 0x1a014, 0x30287}, {7, 0x00b03, 0x001e4, 0x1a014, 0x30282}, {8, 0x00b03, 0x001e4, 0x1a014, 0x30287}, {9, 0x00b03, 0x001e5, 0x1a014, 0x30282}, {10, 0x00b03, 0x001e5, 0x1a014, 0x30287}, {11, 0x00b03, 0x001e6, 0x1a014, 0x30282}, {12, 0x00b03, 0x001e6, 0x1a014, 0x30287}, {13, 0x00b03, 0x001e7, 0x1a014, 0x30282}, {14, 0x00b03, 0x001e8, 0x1a014, 0x30284}, {34, 0x00b03, 0x20266, 0x36014, 0x30282}, {38, 0x00b03, 0x20267, 0x36014, 0x30284}, {42, 0x00b03, 0x20268, 0x36014, 0x30286}, {46, 0x00b03, 0x20269, 0x36014, 0x30288}, {36, 0x00b03, 0x00266, 0x26014, 0x30288}, {40, 0x00b03, 0x00268, 0x26014, 0x30280}, {44, 0x00b03, 0x00269, 0x26014, 0x30282}, {48, 0x00b03, 0x0026a, 0x26014, 0x30284}, {52, 0x00b03, 0x0026b, 0x26014, 0x30286}, {56, 0x00b03, 0x0026c, 0x26014, 0x30288}, {60, 0x00b03, 0x0026e, 0x26014, 0x30280}, {64, 0x00b03, 0x0026f, 0x26014, 0x30282}, {100, 0x00b03, 0x0028a, 0x2e014, 0x30280}, {104, 0x00b03, 0x0028b, 0x2e014, 0x30282}, {108, 0x00b03, 0x0028c, 0x2e014, 0x30284}, {112, 0x00b03, 0x0028d, 0x2e014, 0x30286}, {116, 0x00b03, 0x0028e, 0x2e014, 0x30288}, {120, 0x00b03, 0x002a0, 0x2e014, 0x30280}, {124, 0x00b03, 0x002a1, 0x2e014, 0x30282}, {128, 0x00b03, 0x002a2, 0x2e014, 0x30284}, {132, 0x00b03, 0x002a3, 0x2e014, 0x30286}, {136, 0x00b03, 0x002a4, 0x2e014, 0x30288}, {140, 0x00b03, 0x002a6, 0x2e014, 0x30280}, {149, 0x00b03, 0x002a8, 0x2e014, 0x30287}, {153, 0x00b03, 0x002a9, 0x2e014, 0x30289}, {157, 0x00b03, 0x002ab, 0x2e014, 0x30281}, {161, 0x00b03, 0x002ac, 0x2e014, 0x30283}, {165, 0x00b03, 0x002ad, 0x2e014, 0x30285} }; static const struct rfprog rum_rf5225[] = { {1, 0x00b33, 0x011e1, 0x1a014, 0x30282}, {2, 0x00b33, 0x011e1, 0x1a014, 0x30287}, {3, 0x00b33, 0x011e2, 0x1a014, 0x30282}, {4, 0x00b33, 0x011e2, 0x1a014, 0x30287}, {5, 0x00b33, 0x011e3, 0x1a014, 0x30282}, {6, 0x00b33, 0x011e3, 0x1a014, 0x30287}, {7, 0x00b33, 0x011e4, 0x1a014, 0x30282}, {8, 0x00b33, 0x011e4, 0x1a014, 0x30287}, {9, 0x00b33, 0x011e5, 0x1a014, 0x30282}, {10, 0x00b33, 0x011e5, 0x1a014, 0x30287}, {11, 0x00b33, 0x011e6, 0x1a014, 0x30282}, {12, 0x00b33, 0x011e6, 0x1a014, 0x30287}, {13, 0x00b33, 0x011e7, 0x1a014, 0x30282}, {14, 0x00b33, 0x011e8, 0x1a014, 0x30284}, {34, 0x00b33, 0x01266, 0x26014, 0x30282}, {38, 0x00b33, 0x01267, 0x26014, 0x30284}, {42, 0x00b33, 0x01268, 0x26014, 0x30286}, {46, 0x00b33, 0x01269, 0x26014, 0x30288}, {36, 0x00b33, 0x01266, 0x26014, 0x30288}, {40, 0x00b33, 0x01268, 0x26014, 0x30280}, {44, 0x00b33, 0x01269, 0x26014, 0x30282}, {48, 0x00b33, 0x0126a, 0x26014, 0x30284}, {52, 0x00b33, 0x0126b, 0x26014, 0x30286}, {56, 0x00b33, 0x0126c, 0x26014, 0x30288}, {60, 0x00b33, 0x0126e, 0x26014, 0x30280}, {64, 0x00b33, 0x0126f, 0x26014, 0x30282}, {100, 0x00b33, 0x0128a, 0x2e014, 0x30280}, {104, 0x00b33, 0x0128b, 0x2e014, 0x30282}, {108, 0x00b33, 0x0128c, 0x2e014, 0x30284}, {112, 0x00b33, 0x0128d, 0x2e014, 0x30286}, {116, 0x00b33, 0x0128e, 0x2e014, 0x30288}, {120, 0x00b33, 0x012a0, 0x2e014, 0x30280}, {124, 0x00b33, 0x012a1, 0x2e014, 0x30282}, {128, 0x00b33, 0x012a2, 0x2e014, 0x30284}, {132, 0x00b33, 0x012a3, 0x2e014, 0x30286}, {136, 0x00b33, 0x012a4, 0x2e014, 0x30288}, {140, 0x00b33, 0x012a6, 0x2e014, 0x30280}, {149, 0x00b33, 0x012a8, 0x2e014, 0x30287}, {153, 0x00b33, 0x012a9, 0x2e014, 0x30289}, {157, 0x00b33, 0x012ab, 0x2e014, 0x30281}, {161, 0x00b33, 0x012ac, 0x2e014, 0x30283}, {165, 0x00b33, 0x012ad, 0x2e014, 0x30285} }; static const struct usb2_config rum_config[RUM_N_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = (MCLBYTES + RT2573_TX_DESC_SIZE + 8), .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .mh.callback = &rum_bulk_write_callback, .mh.timeout = 5000, /* ms */ }, [1] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = (MCLBYTES + RT2573_RX_DESC_SIZE), .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &rum_bulk_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &rum_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &rum_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static devclass_t rum_devclass; static device_method_t rum_methods[] = { DEVMETHOD(device_probe, rum_probe), DEVMETHOD(device_attach, rum_attach), DEVMETHOD(device_detach, rum_detach), {0, 0} }; static driver_t rum_driver = { .name = "rum", .methods = rum_methods, .size = sizeof(struct rum_softc), }; DRIVER_MODULE(rum, ushub, rum_driver, rum_devclass, NULL, 0); MODULE_DEPEND(rum, usb2_wlan, 1, 1, 1); MODULE_DEPEND(rum, usb2_core, 1, 1, 1); MODULE_DEPEND(rum, wlan, 1, 1, 1); MODULE_DEPEND(rum, wlan_amrr, 1, 1, 1); static int rum_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != 0) { return (ENXIO); } if (uaa->info.bIfaceIndex != RT2573_IFACE_INDEX) { return (ENXIO); } return (usb2_lookup_id_by_uaa(rum_devs, sizeof(rum_devs), uaa)); } static int rum_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct rum_softc *sc = device_get_softc(dev); int error; uint8_t iface_index; if (sc == NULL) { return (ENOMEM); } device_set_usb2_desc(dev); mtx_init(&sc->sc_mtx, "rum lock", MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); sc->sc_udev = uaa->device; sc->sc_unit = device_get_unit(dev); usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); iface_index = RT2573_IFACE_INDEX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, rum_config, RUM_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "could not allocate USB transfers, " "err=%s\n", usb2_errstr(error)); goto detach; } error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, &rum_end_of_commands, sizeof(struct usb2_config_td_cc), 24); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&sc->sc_mtx); /* start setup */ usb2_config_td_queue_command (&sc->sc_config_td, NULL, &rum_cfg_first_time_setup, 0, 0); /* start watchdog (will exit mutex) */ rum_watchdog(sc); return (0); /* success */ detach: rum_detach(dev); return (ENXIO); /* failure */ } static int rum_detach(device_t dev) { struct rum_softc *sc = device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; usb2_config_td_drain(&sc->sc_config_td); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); rum_cfg_pre_stop(sc, NULL, 0); ifp = sc->sc_ifp; ic = ifp->if_l2com; mtx_unlock(&sc->sc_mtx); /* stop all USB transfers first */ usb2_transfer_unsetup(sc->sc_xfer, RUM_N_TRANSFER); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { bpfdetach(ifp); ieee80211_ifdetach(ic); if_free(ifp); } usb2_config_td_unsetup(&sc->sc_config_td); usb2_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } static void rum_cfg_do_request(struct rum_softc *sc, struct usb2_device_request *req, void *data) { uint16_t length; usb2_error_t err; repeat: if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto error; } err = usb2_do_request_flags (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); if (err) { DPRINTF("device request failed, err=%s " "(ignored)\n", usb2_errstr(err)); /* wait a little before next try */ if (usb2_config_td_sleep(&sc->sc_config_td, hz / 4)) { goto error; } /* try until we are detached */ goto repeat; error: /* the device has been detached */ length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } return; } static void rum_cfg_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2573_READ_EEPROM; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); rum_cfg_do_request(sc, &req, buf); return; } static uint16_t rum_cfg_eeprom_read_2(struct rum_softc *sc, uint16_t addr) { uint16_t tmp; rum_cfg_eeprom_read(sc, addr, &tmp, sizeof(tmp)); return (le16toh(tmp)); } static uint32_t rum_cfg_read(struct rum_softc *sc, uint16_t reg) { uint32_t val; rum_cfg_read_multi(sc, reg, &val, sizeof(val)); return (le32toh(val)); } static void rum_cfg_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2573_READ_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); rum_cfg_do_request(sc, &req, buf); return; } static void rum_cfg_write(struct rum_softc *sc, uint16_t reg, uint32_t val) { uint32_t tmp = htole32(val); rum_cfg_write_multi(sc, reg, &tmp, sizeof(tmp)); return; } static void rum_cfg_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2573_WRITE_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); rum_cfg_do_request(sc, &req, buf); return; } static uint32_t rum_cfg_bbp_disbusy(struct rum_softc *sc) { uint32_t tmp; uint8_t to; for (to = 0;; to++) { if (to < 100) { tmp = rum_cfg_read(sc, RT2573_PHY_CSR3); if ((tmp & RT2573_BBP_BUSY) == 0) { return (tmp); } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { break; } } else { break; } } DPRINTF("could not disbusy BBP\n"); return (RT2573_BBP_BUSY); /* failure */ } static void rum_cfg_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val) { uint32_t tmp; if (rum_cfg_bbp_disbusy(sc) & RT2573_BBP_BUSY) { return; } tmp = RT2573_BBP_BUSY | ((reg & 0x7f) << 8) | val; rum_cfg_write(sc, RT2573_PHY_CSR3, tmp); return; } static uint8_t rum_cfg_bbp_read(struct rum_softc *sc, uint8_t reg) { uint32_t val; if (rum_cfg_bbp_disbusy(sc) & RT2573_BBP_BUSY) { return (0); } val = RT2573_BBP_BUSY | RT2573_BBP_READ | (reg << 8); rum_cfg_write(sc, RT2573_PHY_CSR3, val); val = rum_cfg_bbp_disbusy(sc); return (val & 0xff); } static void rum_cfg_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val) { uint32_t tmp; uint8_t to; reg &= 3; for (to = 0;; to++) { if (to < 100) { tmp = rum_cfg_read(sc, RT2573_PHY_CSR4); if (!(tmp & RT2573_RF_BUSY)) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return; } } else { DPRINTF("could not write to RF\n"); return; } } tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | ((val & 0xfffff) << 2) | reg; rum_cfg_write(sc, RT2573_PHY_CSR4, tmp); DPRINTFN(16, "RF R[%u] <- 0x%05x\n", reg, val & 0xfffff); return; } static void rum_cfg_first_time_setup(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ieee80211com *ic; struct ifnet *ifp; uint32_t tmp; uint16_t i; uint8_t bands; /* setup RX tap header */ sc->sc_rxtap_len = sizeof(sc->sc_rxtap); sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2573_RX_RADIOTAP_PRESENT); /* setup TX tap header */ sc->sc_txtap_len = sizeof(sc->sc_txtap); sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(RT2573_TX_RADIOTAP_PRESENT); /* retrieve RT2573 rev. no */ for (i = 0; i < 100; i++) { tmp = rum_cfg_read(sc, RT2573_MAC_CSR0); if (tmp != 0) { break; } /* wait a little */ if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { /* device detached */ goto done; } } if (tmp == 0) { DPRINTF("chip is maybe not ready\n"); } /* retrieve MAC address and various other things from EEPROM */ rum_cfg_read_eeprom(sc); printf("%s: MAC/BBP RT2573 (rev 0x%05x), RF %s\n", sc->sc_name, tmp, rum_get_rf(sc->sc_rf_rev)); rum_cfg_load_microcode(sc, rt2573_ucode, sizeof(rt2573_ucode)); mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_IEEE80211); mtx_lock(&sc->sc_mtx); if (ifp == NULL) { DPRINTFN(0, "could not if_alloc()!\n"); goto done; } sc->sc_evilhack = ifp; sc->sc_ifp = ifp; ic = ifp->if_l2com; ifp->if_softc = sc; if_initname(ifp, "rum", sc->sc_unit); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = &rum_init_cb; ifp->if_ioctl = &rum_ioctl_cb; ifp->if_start = &rum_start_cb; ifp->if_watchdog = NULL; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); bcopy(sc->sc_myaddr, ic->ic_myaddr, sizeof(ic->ic_myaddr)); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_HOSTAP /* HostAp mode supported */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* bg scanning supported */ | IEEE80211_C_WPA /* 802.11i */ ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); ieee80211_init_channels(ic, NULL, &bands); if ((sc->sc_rf_rev == RT2573_RF_5225) || (sc->sc_rf_rev == RT2573_RF_5226)) { struct ieee80211_channel *c; /* set supported .11a channels */ for (i = 34; i <= 46; i += 4) { c = ic->ic_channels + (ic->ic_nchans++); c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); c->ic_flags = IEEE80211_CHAN_A; c->ic_ieee = i; } for (i = 36; i <= 64; i += 4) { c = ic->ic_channels + (ic->ic_nchans++); c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); c->ic_flags = IEEE80211_CHAN_A; c->ic_ieee = i; } for (i = 100; i <= 140; i += 4) { c = ic->ic_channels + (ic->ic_nchans++); c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); c->ic_flags = IEEE80211_CHAN_A; c->ic_ieee = i; } for (i = 149; i <= 165; i += 4) { c = ic->ic_channels + (ic->ic_nchans++); c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); c->ic_flags = IEEE80211_CHAN_A; c->ic_ieee = i; } } mtx_unlock(&sc->sc_mtx); ieee80211_ifattach(ic); mtx_lock(&sc->sc_mtx); ic->ic_newassoc = &rum_newassoc; ic->ic_raw_xmit = &rum_raw_xmit_cb; ic->ic_node_alloc = &rum_node_alloc; ic->ic_update_mcast = &rum_update_mcast_cb; ic->ic_update_promisc = &rum_update_promisc_cb; ic->ic_scan_start = &rum_scan_start_cb; ic->ic_scan_end = &rum_scan_end_cb; ic->ic_set_channel = &rum_set_channel_cb; ic->ic_vap_create = &rum_vap_create; ic->ic_vap_delete = &rum_vap_delete; sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); mtx_unlock(&sc->sc_mtx); bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap)); if (bootverbose) { ieee80211_announce(ic); } mtx_lock(&sc->sc_mtx); done: return; } static void rum_end_of_commands(struct rum_softc *sc) { sc->sc_flags &= ~RUM_FLAG_WAIT_COMMAND; /* start write transfer, if not started */ usb2_transfer_start(sc->sc_xfer[0]); return; } static void rum_config_copy_chan(struct rum_config_copy_chan *cc, struct ieee80211com *ic, struct ieee80211_channel *c) { if (!c) return; cc->chan_to_ieee = ieee80211_chan2ieee(ic, c); if (c != IEEE80211_CHAN_ANYC) { cc->chan_to_mode = ieee80211_chan2mode(c); if (IEEE80211_IS_CHAN_B(c)) cc->chan_is_b = 1; if (IEEE80211_IS_CHAN_A(c)) cc->chan_is_a = 1; if (IEEE80211_IS_CHAN_2GHZ(c)) cc->chan_is_2ghz = 1; if (IEEE80211_IS_CHAN_5GHZ(c)) cc->chan_is_5ghz = 1; if (IEEE80211_IS_CHAN_ANYG(c)) cc->chan_is_g = 1; } return; } static void rum_config_copy(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp; struct ieee80211com *ic; struct ieee80211_node *ni; struct ieee80211vap *vap; const struct ieee80211_txparam *tp; bzero(cc, sizeof(*cc)); ifp = sc->sc_ifp; if (ifp) { cc->if_flags = ifp->if_flags; bcopy(ifp->if_broadcastaddr, cc->if_broadcastaddr, sizeof(cc->if_broadcastaddr)); ic = ifp->if_l2com; if (ic) { rum_config_copy_chan(&cc->ic_curchan, ic, ic->ic_curchan); rum_config_copy_chan(&cc->ic_bsschan, ic, ic->ic_bsschan); vap = TAILQ_FIRST(&ic->ic_vaps); if (vap) { ni = vap->iv_bss; if (ni) { cc->iv_bss.ni_intval = ni->ni_intval; bcopy(ni->ni_bssid, cc->iv_bss.ni_bssid, sizeof(cc->iv_bss.ni_bssid)); } tp = vap->iv_txparms + cc->ic_bsschan.chan_to_mode; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { cc->iv_bss.fixed_rate_none = 1; } } cc->ic_opmode = ic->ic_opmode; cc->ic_flags = ic->ic_flags; cc->ic_txpowlimit = ic->ic_txpowlimit; cc->ic_curmode = ic->ic_curmode; bcopy(ic->ic_myaddr, cc->ic_myaddr, sizeof(cc->ic_myaddr)); } } sc->sc_flags |= RUM_FLAG_WAIT_COMMAND; return; } static const char * rum_get_rf(uint32_t rev) { ; /* indent fix */ switch (rev) { case RT2573_RF_2527: return "RT2527 (MIMO XR)"; case RT2573_RF_2528: return "RT2528"; case RT2573_RF_5225: return "RT5225 (MIMO XR)"; case RT2573_RF_5226: return "RT5226"; default: return "unknown"; } } static void rum_bulk_read_callback(struct usb2_xfer *xfer) { struct rum_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni; struct mbuf *m = NULL; uint32_t flags; uint32_t max_len; uint8_t rssi = 0; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen); if (xfer->actlen < (RT2573_RX_DESC_SIZE + IEEE80211_MIN_LEN)) { DPRINTF("too short transfer, " "%d bytes\n", xfer->actlen); ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, 0, &sc->sc_rx_desc, RT2573_RX_DESC_SIZE); flags = le32toh(sc->sc_rx_desc.flags); if (flags & RT2573_RX_CRC_ERROR) { /* * This should not happen since we did not * request to receive those frames when we * filled RAL_TXRX_CSR2: */ DPRINTFN(6, "PHY or CRC error\n"); ifp->if_ierrors++; goto tr_setup; } m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { DPRINTF("could not allocate mbuf\n"); ifp->if_ierrors++; goto tr_setup; } max_len = (xfer->actlen - RT2573_RX_DESC_SIZE); usb2_copy_out(xfer->frbuffers, RT2573_RX_DESC_SIZE, m->m_data, max_len); /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; if (m->m_len > max_len) { DPRINTF("invalid length in RX " "descriptor, %u bytes, received %u bytes\n", m->m_len, max_len); ifp->if_ierrors++; m_freem(m); m = NULL; goto tr_setup; } rssi = rum_get_rssi(sc, sc->sc_rx_desc.rssi); DPRINTF("real length=%d bytes, rssi=%d\n", m->m_len, rssi); if (bpf_peers_present(ifp->if_bpf)) { struct rum_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = IEEE80211_RADIOTAP_F_FCS; tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, (sc->sc_rx_desc.flags & htole32(RT2573_RX_OFDM)) ? IEEE80211_T_OFDM : IEEE80211_T_CCK); tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_antenna = sc->sc_rx_ant; tap->wr_antsignal = rssi; bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } case USB_ST_SETUP: tr_setup: if (sc->sc_flags & RUM_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[3]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "ieee80211_input" here, and not some lines up! */ if (m) { mtx_unlock(&sc->sc_mtx); ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { if (ieee80211_input(ni, m, rssi, RT2573_NOISE_FLOOR, 0)) { /* ignore */ } /* node is no longer needed */ ieee80211_free_node(ni); } else { if (ieee80211_input_all(ic, m, rssi, RT2573_NOISE_FLOOR, 0)) { /* ignore */ } } mtx_lock(&sc->sc_mtx); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= RUM_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[3]); } return; } } static void rum_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { struct rum_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[1]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~RUM_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } return; } static uint8_t rum_plcp_signal(uint16_t rate) { ; /* indent fix */ switch (rate) { /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return (0x0); case 4: return (0x1); case 11: return (0x2); case 22: return (0x3); /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return (0xb); case 18: return (0xf); case 24: return (0xa); case 36: return (0xe); case 48: return (0x9); case 72: return (0xd); case 96: return (0x8); case 108: return (0xc); /* XXX unsupported/unknown rate */ default: return (0xff); } } /* * We assume that "m->m_pkthdr.rcvif" is pointing to the "ni" that * should be freed, when "rum_setup_desc_and_tx" is called. */ static void rum_setup_desc_and_tx(struct rum_softc *sc, struct mbuf *m, uint32_t flags, uint16_t xflags, uint16_t rate) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct mbuf *mm; enum ieee80211_phytype phytype; uint16_t plcp_length; uint16_t len; uint8_t remainder; uint8_t is_beacon; if (xflags & RT2573_TX_BEACON) { xflags &= ~RT2573_TX_BEACON; is_beacon = 1; } else { is_beacon = 0; } if (sc->sc_tx_queue.ifq_len >= IFQ_MAXLEN) { /* free packet */ rum_tx_freem(m); ifp->if_oerrors++; return; } if (!((sc->sc_flags & RUM_FLAG_LL_READY) && (sc->sc_flags & RUM_FLAG_HL_READY))) { /* free packet */ rum_tx_freem(m); ifp->if_oerrors++; return; } if (rate < 2) { DPRINTF("rate < 2!\n"); /* avoid division by zero */ rate = 2; } ic->ic_lastdata = ticks; if (bpf_peers_present(ifp->if_bpf)) { struct rum_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wt_antenna = sc->sc_tx_ant; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); } len = m->m_pkthdr.len; flags |= RT2573_TX_VALID; flags |= (len << 16); sc->sc_tx_desc.flags = htole32(flags); sc->sc_tx_desc.xflags = htole16(xflags); sc->sc_tx_desc.wme = htole16(RT2573_QID(0) | RT2573_AIFSN(2) | RT2573_LOGCWMIN(4) | RT2573_LOGCWMAX(10)); /* setup PLCP fields */ sc->sc_tx_desc.plcp_signal = rum_plcp_signal(rate); sc->sc_tx_desc.plcp_service = 4; len += IEEE80211_CRC_LEN; phytype = ieee80211_rate2phytype(sc->sc_rates, rate); if (phytype == IEEE80211_T_OFDM) { sc->sc_tx_desc.flags |= htole32(RT2573_TX_OFDM); plcp_length = (len & 0xfff); sc->sc_tx_desc.plcp_length_hi = plcp_length >> 6; sc->sc_tx_desc.plcp_length_lo = plcp_length & 0x3f; } else { plcp_length = ((16 * len) + rate - 1) / rate; if (rate == 22) { remainder = (16 * len) % 22; if ((remainder != 0) && (remainder < 7)) { sc->sc_tx_desc.plcp_service |= RT2573_PLCP_LENGEXT; } } sc->sc_tx_desc.plcp_length_hi = plcp_length >> 8; sc->sc_tx_desc.plcp_length_lo = plcp_length & 0xff; if ((rate != 2) && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) { sc->sc_tx_desc.plcp_signal |= 0x08; } } if (sizeof(sc->sc_tx_desc) > MHLEN) { DPRINTF("No room for header structure!\n"); rum_tx_freem(m); return; } mm = m_gethdr(M_NOWAIT, MT_DATA); if (mm == NULL) { DPRINTF("Could not allocate header mbuf!\n"); rum_tx_freem(m); return; } bcopy(&sc->sc_tx_desc, mm->m_data, sizeof(sc->sc_tx_desc)); mm->m_len = sizeof(sc->sc_tx_desc); mm->m_next = m; mm->m_pkthdr.len = mm->m_len + m->m_pkthdr.len; mm->m_pkthdr.rcvif = NULL; if (is_beacon) { if (mm->m_pkthdr.len > sizeof(sc->sc_beacon_buf)) { DPRINTFN(0, "Truncating beacon" ", %u bytes!\n", mm->m_pkthdr.len); mm->m_pkthdr.len = sizeof(sc->sc_beacon_buf); } m_copydata(mm, 0, mm->m_pkthdr.len, sc->sc_beacon_buf); /* copy the first 24 bytes of Tx descriptor into NIC memory */ rum_cfg_write_multi(sc, RT2573_HW_BEACON_BASE0, sc->sc_beacon_buf, mm->m_pkthdr.len); rum_tx_freem(mm); return; } /* start write transfer, if not started */ _IF_ENQUEUE(&sc->sc_tx_queue, mm); usb2_transfer_start(sc->sc_xfer[0]); return; } static void rum_bulk_write_callback(struct usb2_xfer *xfer) { struct rum_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint16_t temp_len; uint8_t align; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete\n"); ifp->if_opackets++; case USB_ST_SETUP: if (sc->sc_flags & RUM_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[2]); break; } if (sc->sc_flags & RUM_FLAG_WAIT_COMMAND) { /* * don't send anything while a command is pending ! */ break; } rum_fill_write_queue(sc); _IF_DEQUEUE(&sc->sc_tx_queue, m); if (m) { if (m->m_pkthdr.len > (MCLBYTES + RT2573_TX_DESC_SIZE)) { DPRINTFN(0, "data overflow, %u bytes\n", m->m_pkthdr.len); m->m_pkthdr.len = (MCLBYTES + RT2573_TX_DESC_SIZE); } usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); /* compute transfer length */ temp_len = m->m_pkthdr.len; /* make transfer length 32-bit aligned */ align = (-(temp_len)) & 3; /* check if we need to add four extra bytes */ if (((temp_len + align) % 64) == 0) { align += 4; } /* check if we need to align length */ if (align != 0) { /* zero the extra bytes */ usb2_bzero(xfer->frbuffers, temp_len, align); temp_len += align; } DPRINTFN(11, "sending frame len=%u ferlen=%u\n", m->m_pkthdr.len, temp_len); xfer->frlengths[0] = temp_len; usb2_start_hardware(xfer); /* free mbuf and node */ rum_tx_freem(m); } break; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= RUM_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; break; } return; } static void rum_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { struct rum_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~RUM_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } return; } static void rum_watchdog(void *arg) { struct rum_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); if (sc->sc_amrr_timer) { usb2_config_td_queue_command (&sc->sc_config_td, NULL, &rum_cfg_amrr_timeout, 0, 0); } usb2_callout_reset(&sc->sc_watchdog, hz, &rum_watchdog, sc); mtx_unlock(&sc->sc_mtx); return; } static void rum_init_cb(void *arg) { struct rum_softc *sc = arg; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &rum_cfg_pre_init, &rum_cfg_init, 0, 0); mtx_unlock(&sc->sc_mtx); return; } static int rum_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) { struct rum_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; int error; switch (cmd) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { usb2_config_td_queue_command (&sc->sc_config_td, &rum_cfg_pre_init, &rum_cfg_init, 0, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &rum_cfg_pre_stop, &rum_cfg_stop, 0, 0); } } mtx_unlock(&sc->sc_mtx); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, (void *)data, &ic->ic_media, cmd); break; default: error = ether_ioctl(ifp, cmd, data); } return (error); } static void rum_start_cb(struct ifnet *ifp) { struct rum_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); /* start write transfer, if not started */ usb2_transfer_start(sc->sc_xfer[0]); mtx_unlock(&sc->sc_mtx); return; } static void rum_cfg_newstate(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct rum_vap *uvp = RUM_VAP(vap); enum ieee80211_state ostate; enum ieee80211_state nstate; int arg; ostate = vap->iv_state; nstate = sc->sc_ns_state; arg = sc->sc_ns_arg; if (ostate == IEEE80211_S_INIT) { /* We are leaving INIT. TSF sync should be off. */ rum_cfg_disable_tsf_sync(sc); } switch (nstate) { case IEEE80211_S_INIT: break; case IEEE80211_S_RUN: rum_cfg_set_run(sc, cc); break; default: break; } mtx_unlock(&sc->sc_mtx); IEEE80211_LOCK(ic); uvp->newstate(vap, nstate, arg); + /* XXX Broken! if (vap->iv_newstate_cb != NULL) vap->iv_newstate_cb(vap, nstate, arg); + */ IEEE80211_UNLOCK(ic); mtx_lock(&sc->sc_mtx); return; } static int rum_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct rum_vap *uvp = RUM_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct rum_softc *sc = ic->ic_ifp->if_softc; DPRINTF("setting new state: %d\n", nstate); /* Special case - cannot defer this call and cannot block ! */ if (nstate == IEEE80211_S_INIT) { /* stop timers */ mtx_lock(&sc->sc_mtx); sc->sc_amrr_timer = 0; mtx_unlock(&sc->sc_mtx); return (uvp->newstate(vap, nstate, arg)); } mtx_lock(&sc->sc_mtx); if (usb2_config_td_is_gone(&sc->sc_config_td)) { mtx_unlock(&sc->sc_mtx); return (0); /* nothing to do */ } /* store next state */ sc->sc_ns_state = nstate; sc->sc_ns_arg = arg; /* stop timers */ sc->sc_amrr_timer = 0; /* * USB configuration can only be done from the USB configuration * thread: */ usb2_config_td_queue_command (&sc->sc_config_td, &rum_config_copy, &rum_cfg_newstate, 0, 0); mtx_unlock(&sc->sc_mtx); return (EINPROGRESS); } static void rum_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func) { struct rum_softc *sc = ic->ic_ifp->if_softc; mtx_lock(&sc->sc_mtx); sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); usb2_config_td_queue_command (&sc->sc_config_td, &rum_config_copy, func, 0, 0); mtx_unlock(&sc->sc_mtx); return; } static void rum_scan_start_cb(struct ieee80211com *ic) { rum_std_command(ic, &rum_cfg_scan_start); return; } static void rum_scan_end_cb(struct ieee80211com *ic) { rum_std_command(ic, &rum_cfg_scan_end); return; } static void rum_set_channel_cb(struct ieee80211com *ic) { rum_std_command(ic, &rum_cfg_set_chan); return; } static void rum_cfg_scan_start(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* abort TSF synchronization */ rum_cfg_disable_tsf_sync(sc); rum_cfg_set_bssid(sc, cc->if_broadcastaddr); return; } static void rum_cfg_scan_end(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* enable TSF synchronization */ rum_cfg_enable_tsf_sync(sc, cc, 0); rum_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); return; } /* * Reprogram MAC/BBP to switch to a new band. Values taken from the reference * driver. */ static void rum_cfg_select_band(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104; /* update all BBP registers that depend on the band */ bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c; bbp35 = 0x50; bbp97 = 0x48; bbp98 = 0x48; if (cc->ic_curchan.chan_is_5ghz) { bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c; bbp35 += 0x10; bbp97 += 0x10; bbp98 += 0x10; } if ((cc->ic_curchan.chan_is_2ghz && sc->sc_ext_2ghz_lna) || (cc->ic_curchan.chan_is_5ghz && sc->sc_ext_5ghz_lna)) { bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10; } sc->sc_bbp17 = bbp17; rum_cfg_bbp_write(sc, 17, bbp17); rum_cfg_bbp_write(sc, 96, bbp96); rum_cfg_bbp_write(sc, 104, bbp104); if ((cc->ic_curchan.chan_is_2ghz && sc->sc_ext_2ghz_lna) || (cc->ic_curchan.chan_is_5ghz && sc->sc_ext_5ghz_lna)) { rum_cfg_bbp_write(sc, 75, 0x80); rum_cfg_bbp_write(sc, 86, 0x80); rum_cfg_bbp_write(sc, 88, 0x80); } rum_cfg_bbp_write(sc, 35, bbp35); rum_cfg_bbp_write(sc, 97, bbp97); rum_cfg_bbp_write(sc, 98, bbp98); tmp = rum_cfg_read(sc, RT2573_PHY_CSR0); tmp &= ~(RT2573_PA_PE_2GHZ | RT2573_PA_PE_5GHZ); if (cc->ic_curchan.chan_is_2ghz) tmp |= RT2573_PA_PE_2GHZ; else tmp |= RT2573_PA_PE_5GHZ; rum_cfg_write(sc, RT2573_PHY_CSR0, tmp); /* 802.11a uses a 16 microseconds short interframe space */ sc->sc_sifs = cc->ic_curchan.chan_is_5ghz ? 16 : 10; return; } static void rum_cfg_set_chan(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { enum { N_RF5225 = (sizeof(rum_rf5225) / sizeof(rum_rf5225[0]))}; const struct rfprog *rfprog; uint32_t chan; uint16_t i; uint8_t bbp3; uint8_t bbp94 = RT2573_BBPR94_DEFAULT; int8_t power; chan = cc->ic_curchan.chan_to_ieee; if ((chan == 0) || (chan == IEEE80211_CHAN_ANY)) { /* nothing to do */ return; } if (chan == sc->sc_last_chan) { return; } sc->sc_last_chan = chan; /* select the appropriate RF settings based on what EEPROM says */ rfprog = ((sc->sc_rf_rev == RT2573_RF_5225) || (sc->sc_rf_rev == RT2573_RF_2527)) ? rum_rf5225 : rum_rf5226; /* find the settings for this channel */ for (i = 0;; i++) { if (i == (N_RF5225 - 1)) break; if (rfprog[i].chan == chan) break; } DPRINTF("chan=%d, i=%d\n", chan, i); power = sc->sc_txpow[i]; if (power < 0) { bbp94 += power; power = 0; } else if (power > 31) { bbp94 += power - 31; power = 31; } /* * If we are switching from the 2GHz band to the 5GHz band or * vice-versa, BBP registers need to be reprogrammed. */ rum_cfg_select_band(sc, cc, 0); rum_cfg_select_antenna(sc, cc, 0); rum_cfg_rf_write(sc, RT2573_RF1, rfprog[i].r1); rum_cfg_rf_write(sc, RT2573_RF2, rfprog[i].r2); rum_cfg_rf_write(sc, RT2573_RF3, rfprog[i].r3 | (power << 7)); rum_cfg_rf_write(sc, RT2573_RF4, rfprog[i].r4 | (sc->sc_rffreq << 10)); rum_cfg_rf_write(sc, RT2573_RF1, rfprog[i].r1); rum_cfg_rf_write(sc, RT2573_RF2, rfprog[i].r2); rum_cfg_rf_write(sc, RT2573_RF3, rfprog[i].r3 | (power << 7) | 1); rum_cfg_rf_write(sc, RT2573_RF4, rfprog[i].r4 | (sc->sc_rffreq << 10)); rum_cfg_rf_write(sc, RT2573_RF1, rfprog[i].r1); rum_cfg_rf_write(sc, RT2573_RF2, rfprog[i].r2); rum_cfg_rf_write(sc, RT2573_RF3, rfprog[i].r3 | (power << 7)); rum_cfg_rf_write(sc, RT2573_RF4, rfprog[i].r4 | (sc->sc_rffreq << 10)); if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return; } /* enable smart mode for MIMO-capable RFs */ bbp3 = rum_cfg_bbp_read(sc, 3); if ((sc->sc_rf_rev == RT2573_RF_5225) || (sc->sc_rf_rev == RT2573_RF_2527)) bbp3 &= ~RT2573_SMART_MODE; else bbp3 |= RT2573_SMART_MODE; rum_cfg_bbp_write(sc, 3, bbp3); rum_cfg_bbp_write(sc, 94, bbp94); /* update basic rate set */ if (cc->ic_curchan.chan_is_b) { /* 11b basic rates: 1, 2Mbps */ rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x3); } else if (cc->ic_curchan.chan_is_a) { /* 11a basic rates: 6, 12, 24Mbps */ rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x150); } else { /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */ rum_cfg_write(sc, RT2573_TXRX_CSR5, 0xf); } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return; } return; } static void rum_cfg_set_run(struct rum_softc *sc, struct usb2_config_td_cc *cc) { if (cc->ic_opmode != IEEE80211_M_MONITOR) { rum_cfg_update_slot(sc, cc, 0); rum_cfg_enable_mrr(sc, cc, 0); rum_cfg_set_txpreamble(sc, cc, 0); /* update basic rate set */ if (cc->ic_bsschan.chan_is_5ghz) { /* 11a basic rates: 6, 12, 24Mbps */ rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x150); } else if (cc->ic_bsschan.chan_is_g) { /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */ rum_cfg_write(sc, RT2573_TXRX_CSR5, 0xf); } else { /* 11b basic rates: 1, 2Mbps */ rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x3); } rum_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); } if ((cc->ic_opmode == IEEE80211_M_HOSTAP) || (cc->ic_opmode == IEEE80211_M_IBSS)) { rum_cfg_prepare_beacon(sc, cc, 0); } if (cc->ic_opmode != IEEE80211_M_MONITOR) { rum_cfg_enable_tsf_sync(sc, cc, 0); } if (cc->iv_bss.fixed_rate_none) { /* enable automatic rate adaptation */ rum_cfg_amrr_start(sc); } return; } static void rum_cfg_enable_tsf_sync(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; if (cc->ic_opmode != IEEE80211_M_STA) { /* * Change default 16ms TBTT adjustment to 8ms. * Must be done before enabling beacon generation. */ rum_cfg_write(sc, RT2573_TXRX_CSR10, (1 << 12) | 8); } tmp = rum_cfg_read(sc, RT2573_TXRX_CSR9) & 0xff000000; /* set beacon interval (in 1/16ms unit) */ tmp |= cc->iv_bss.ni_intval * 16; tmp |= RT2573_TSF_TICKING | RT2573_ENABLE_TBTT; if (cc->ic_opmode == IEEE80211_M_STA) tmp |= RT2573_TSF_MODE(1); else tmp |= RT2573_TSF_MODE(2) | RT2573_GENERATE_BEACON; rum_cfg_write(sc, RT2573_TXRX_CSR9, tmp); return; } static void rum_cfg_disable_tsf_sync(struct rum_softc *sc) { uint32_t tmp; /* abort TSF synchronization */ tmp = rum_cfg_read(sc, RT2573_TXRX_CSR9); rum_cfg_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff); return; } /* * Enable multi-rate retries for frames sent at OFDM rates. * In 802.11b/g mode, allow fallback to CCK rates. */ static void rum_cfg_enable_mrr(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; tmp = rum_cfg_read(sc, RT2573_TXRX_CSR4); if (cc->ic_curchan.chan_is_5ghz) tmp &= ~RT2573_MRR_CCK_FALLBACK; else tmp |= RT2573_MRR_CCK_FALLBACK; tmp |= RT2573_MRR_ENABLED; rum_cfg_write(sc, RT2573_TXRX_CSR4, tmp); return; } static void rum_cfg_update_slot(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; uint8_t slottime; slottime = (cc->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; tmp = rum_cfg_read(sc, RT2573_MAC_CSR9); tmp = (tmp & ~0xff) | slottime; rum_cfg_write(sc, RT2573_MAC_CSR9, tmp); DPRINTF("setting slot time to %u us\n", slottime); return; } static void rum_cfg_set_txpreamble(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; tmp = rum_cfg_read(sc, RT2573_TXRX_CSR4); if (cc->ic_flags & IEEE80211_F_SHPREAMBLE) tmp |= RT2573_SHORT_PREAMBLE; else tmp &= ~RT2573_SHORT_PREAMBLE; rum_cfg_write(sc, RT2573_TXRX_CSR4, tmp); return; } static void rum_cfg_set_bssid(struct rum_softc *sc, uint8_t *bssid) { uint32_t tmp; tmp = bssid[0] | (bssid[1] << 8) | (bssid[2] << 16) | (bssid[3] << 24); rum_cfg_write(sc, RT2573_MAC_CSR4, tmp); tmp = (bssid[4]) | (bssid[5] << 8) | (RT2573_ONE_BSSID << 16); rum_cfg_write(sc, RT2573_MAC_CSR5, tmp); return; } static void rum_cfg_set_macaddr(struct rum_softc *sc, uint8_t *addr) { uint32_t tmp; tmp = addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24); rum_cfg_write(sc, RT2573_MAC_CSR2, tmp); tmp = addr[4] | (addr[5] << 8) | (0xff << 16); rum_cfg_write(sc, RT2573_MAC_CSR3, tmp); return; } static void rum_cfg_update_promisc(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0); if (cc->if_flags & IFF_PROMISC) tmp &= ~RT2573_DROP_NOT_TO_ME; else tmp |= RT2573_DROP_NOT_TO_ME; rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp); DPRINTF("%s promiscuous mode\n", (cc->if_flags & IFF_PROMISC) ? "entering" : "leaving"); return; } static void rum_cfg_select_antenna(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; uint8_t bbp3; uint8_t bbp4; uint8_t bbp77; uint8_t rx_ant; uint8_t is_5ghz; bbp3 = rum_cfg_bbp_read(sc, 3); bbp4 = rum_cfg_bbp_read(sc, 4); bbp77 = rum_cfg_bbp_read(sc, 77); bbp3 &= ~0x01; bbp4 &= ~0x23; rx_ant = sc->sc_rx_ant; is_5ghz = cc->ic_curchan.chan_is_5ghz; switch (sc->sc_rf_rev) { case RT2573_RF_5226: case RT2573_RF_5225: if (rx_ant == 0) { /* Diversity */ bbp4 |= 0x02; if (is_5ghz == 0) bbp4 |= 0x20; } else if (rx_ant == 1) { /* RX: Antenna A */ bbp4 |= 0x01; if (is_5ghz) bbp77 &= ~0x03; else bbp77 |= 0x03; } else if (rx_ant == 2) { /* RX: Antenna B */ bbp4 |= 0x01; if (is_5ghz) bbp77 |= 0x03; else bbp77 &= ~0x03; } break; case RT2573_RF_2528: case RT2573_RF_2527: if (rx_ant == 0) { /* Diversity */ bbp4 |= 0x22; } else if (rx_ant == 1) { /* RX: Antenna A */ bbp4 |= 0x21; bbp77 |= 0x03; } else if (rx_ant == 2) { /* RX: Antenna B */ bbp4 |= 0x21; bbp77 &= ~0x03; } break; default: break; } bbp4 &= ~(sc->sc_ftype << 5); /* make sure Rx is disabled before switching antenna */ tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0); rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); rum_cfg_bbp_write(sc, 3, bbp3); rum_cfg_bbp_write(sc, 4, bbp4); rum_cfg_bbp_write(sc, 77, bbp77); rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp); return; } static void rum_cfg_read_eeprom(struct rum_softc *sc) { uint16_t val; /* read MAC address */ rum_cfg_eeprom_read(sc, RT2573_EEPROM_ADDRESS, sc->sc_myaddr, 6); val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_ANTENNA); sc->sc_rf_rev = (val >> 11) & 0x1f; sc->sc_hw_radio = (val >> 10) & 0x1; sc->sc_ftype = (val >> 6) & 0x1; sc->sc_rx_ant = (val >> 4) & 0x3; sc->sc_tx_ant = (val >> 2) & 0x3; sc->sc_nb_ant = (val & 0x3); DPRINTF("RF revision=%d\n", sc->sc_rf_rev); val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_CONFIG2); sc->sc_ext_5ghz_lna = (val >> 6) & 0x1; sc->sc_ext_2ghz_lna = (val >> 4) & 0x1; DPRINTF("External 2GHz LNA=%d, External 5GHz LNA=%d\n", sc->sc_ext_2ghz_lna, sc->sc_ext_5ghz_lna); val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET); if ((val & 0xff) != 0xff) sc->sc_rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */ else sc->sc_rssi_2ghz_corr = 0; /* range check */ if ((sc->sc_rssi_2ghz_corr < -10) || (sc->sc_rssi_2ghz_corr > 10)) { sc->sc_rssi_2ghz_corr = 0; } val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET); if ((val & 0xff) != 0xff) sc->sc_rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */ else sc->sc_rssi_5ghz_corr = 0; /* range check */ if ((sc->sc_rssi_5ghz_corr < -10) || (sc->sc_rssi_5ghz_corr > 10)) { sc->sc_rssi_5ghz_corr = 0; } if (sc->sc_ext_2ghz_lna) { sc->sc_rssi_2ghz_corr -= 14; } if (sc->sc_ext_5ghz_lna) { sc->sc_rssi_5ghz_corr -= 14; } DPRINTF("RSSI 2GHz corr=%d, RSSI 5GHz corr=%d\n", sc->sc_rssi_2ghz_corr, sc->sc_rssi_5ghz_corr); val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_FREQ_OFFSET); if ((val & 0xff) != 0xff) sc->sc_rffreq = (val & 0xff); else sc->sc_rffreq = 0; DPRINTF("RF freq=%d\n", sc->sc_rffreq); /* read Tx power for all a/b/g channels */ rum_cfg_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->sc_txpow, 14); /* XXX default Tx power for 802.11a channels */ memset(sc->sc_txpow + 14, 24, sizeof(sc->sc_txpow) - 14); /* read default values for BBP registers */ rum_cfg_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->sc_bbp_prom, 2 * 16); return; } static uint8_t rum_cfg_bbp_init(struct rum_softc *sc) { enum { N_DEF_BBP = (sizeof(rum_def_bbp) / sizeof(rum_def_bbp[0])), }; uint16_t i; uint8_t to; uint8_t tmp; /* wait for BBP to become ready */ for (to = 0;; to++) { if (to < 100) { tmp = rum_cfg_bbp_read(sc, 0); if ((tmp != 0x00) && (tmp != 0xff)) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return (1); /* failure */ } } else { DPRINTF("timeout waiting for BBP\n"); return (1); /* failure */ } } /* initialize BBP registers to default values */ for (i = 0; i < N_DEF_BBP; i++) { rum_cfg_bbp_write(sc, rum_def_bbp[i].reg, rum_def_bbp[i].val); } /* write vendor-specific BBP values (from EEPROM) */ for (i = 0; i < 16; i++) { if ((sc->sc_bbp_prom[i].reg == 0) || (sc->sc_bbp_prom[i].reg == 0xff)) { continue; } rum_cfg_bbp_write(sc, sc->sc_bbp_prom[i].reg, sc->sc_bbp_prom[i].val); } return (0); } static void rum_cfg_pre_init(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* immediate configuration */ rum_cfg_pre_stop(sc, cc, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= RUM_FLAG_HL_READY; IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); return; } static void rum_cfg_init(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { enum { N_DEF_MAC = (sizeof(rum_def_mac) / sizeof(rum_def_mac[0])), }; uint32_t tmp; uint16_t i; uint8_t to; /* delayed configuration */ rum_cfg_stop(sc, cc, 0); /* initialize MAC registers to default values */ for (i = 0; i < N_DEF_MAC; i++) { rum_cfg_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val); } /* set host ready */ rum_cfg_write(sc, RT2573_MAC_CSR1, 3); rum_cfg_write(sc, RT2573_MAC_CSR1, 0); /* wait for BBP/RF to wakeup */ for (to = 0;; to++) { if (to < 100) { if (rum_cfg_read(sc, RT2573_MAC_CSR12) & 8) { break; } rum_cfg_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */ if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { goto fail; } } else { DPRINTF("timeout waiting for " "BBP/RF to wakeup\n"); goto fail; } } if (rum_cfg_bbp_init(sc)) { goto fail; } /* select default channel */ sc->sc_last_chan = 0; rum_cfg_set_chan(sc, cc, 0); /* clear STA registers */ rum_cfg_read_multi(sc, RT2573_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); /* set MAC address */ rum_cfg_set_macaddr(sc, cc->ic_myaddr); /* initialize ASIC */ rum_cfg_write(sc, RT2573_MAC_CSR1, 4); /* * make sure that the first transaction * clears the stall: */ sc->sc_flags |= (RUM_FLAG_READ_STALL | RUM_FLAG_WRITE_STALL | RUM_FLAG_LL_READY); if ((sc->sc_flags & RUM_FLAG_LL_READY) && (sc->sc_flags & RUM_FLAG_HL_READY)) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* * start the USB transfers, if not already started: */ usb2_transfer_start(sc->sc_xfer[1]); usb2_transfer_start(sc->sc_xfer[0]); /* * start IEEE802.11 layer */ mtx_unlock(&sc->sc_mtx); ieee80211_start_all(ic); mtx_lock(&sc->sc_mtx); } /* update Rx filter */ tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0) & 0xffff; tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR; if (cc->ic_opmode != IEEE80211_M_MONITOR) { tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR | RT2573_DROP_ACKCTS; if (cc->ic_opmode != IEEE80211_M_HOSTAP) { tmp |= RT2573_DROP_TODS; } if (!(cc->if_flags & IFF_PROMISC)) { tmp |= RT2573_DROP_NOT_TO_ME; } } rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp); return; fail: rum_cfg_pre_stop(sc, NULL, 0); if (cc) { rum_cfg_stop(sc, cc, 0); } return; } static void rum_cfg_pre_stop(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if (cc) { /* copy the needed configuration */ rum_config_copy(sc, cc, refcount); } /* immediate configuration */ if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } sc->sc_flags &= ~(RUM_FLAG_HL_READY | RUM_FLAG_LL_READY); /* * stop all the transfers, if not already stopped: */ usb2_transfer_stop(sc->sc_xfer[0]); usb2_transfer_stop(sc->sc_xfer[1]); usb2_transfer_stop(sc->sc_xfer[2]); usb2_transfer_stop(sc->sc_xfer[3]); /* clean up transmission */ rum_tx_clean_queue(sc); return; } static void rum_cfg_stop(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; /* disable Rx */ tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0); rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); /* reset ASIC */ rum_cfg_write(sc, RT2573_MAC_CSR1, 3); /* wait a little */ usb2_config_td_sleep(&sc->sc_config_td, hz / 10); rum_cfg_write(sc, RT2573_MAC_CSR1, 0); /* wait a little */ usb2_config_td_sleep(&sc->sc_config_td, hz / 10); return; } static void rum_cfg_amrr_start(struct rum_softc *sc) { struct ieee80211vap *vap; struct ieee80211_node *ni; vap = rum_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } /* init AMRR */ ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni); /* enable AMRR timer */ sc->sc_amrr_timer = 1; return; } static void rum_cfg_amrr_timeout(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211vap *vap; struct ieee80211_node *ni; uint32_t ok; uint32_t fail; /* clear statistic registers (STA_CSR0 to STA_CSR5) */ rum_cfg_read_multi(sc, RT2573_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); vap = rum_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } if ((sc->sc_flags & RUM_FLAG_LL_READY) && (sc->sc_flags & RUM_FLAG_HL_READY)) { ok = (le32toh(sc->sc_sta[4]) >> 16) + /* TX ok w/o retry */ (le32toh(sc->sc_sta[5]) & 0xffff); /* TX ok w/ retry */ fail = (le32toh(sc->sc_sta[5]) >> 16); /* TX retry-fail count */ if (sc->sc_amrr_timer) { ieee80211_amrr_tx_update(&RUM_NODE(vap->iv_bss)->amn, ok + fail, ok, (le32toh(sc->sc_sta[5]) & 0xffff) + fail); if (ieee80211_amrr_choose(ni, &RUM_NODE(ni)->amn)) { /* ignore */ } } ifp->if_oerrors += fail;/* count TX retry-fail as Tx errors */ } return; } static void rum_cfg_load_microcode(struct rum_softc *sc, const uint8_t *ucode, uint16_t size) { struct usb2_device_request req; uint16_t reg = RT2573_MCU_CODE_BASE; /* copy firmware image into NIC */ while (size >= 4) { rum_cfg_write(sc, reg, UGETDW(ucode)); reg += 4; ucode += 4; size -= 4; } if (size != 0) { DPRINTF("possibly invalid firmware\n"); } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2573_MCU_CNTL; USETW(req.wValue, RT2573_MCU_RUN); USETW(req.wIndex, 0); USETW(req.wLength, 0); rum_cfg_do_request(sc, &req, NULL); return; } static void rum_cfg_prepare_beacon(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ieee80211_node *ni; struct ieee80211vap *vap; struct ieee80211com *ic; const struct ieee80211_txparam *tp; struct mbuf *m; vap = rum_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } ic = vap->iv_ic; if (ic == NULL) { return; } DPRINTFN(11, "Sending beacon frame.\n"); m = ieee80211_beacon_alloc(ni, &RUM_VAP(vap)->bo); if (m == NULL) { DPRINTFN(0, "could not allocate beacon\n"); return; } tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; m->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni); rum_setup_desc_and_tx(sc, m, RT2573_TX_TIMESTAMP, RT2573_TX_HWSEQ | RT2573_TX_BEACON, tp->mgmtrate); return; } static uint8_t rum_get_rssi(struct rum_softc *sc, uint8_t raw) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int16_t rssi; uint8_t lna; uint8_t agc; lna = (raw >> 5) & 0x3; agc = raw & 0x1f; if (lna == 0) { /* * No RSSI mapping * * NB: Since RSSI is relative to noise floor, -1 is * adequate for caller to know error happened. */ return (0); } rssi = (2 * agc) - RT2573_NOISE_FLOOR; if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { rssi += sc->sc_rssi_2ghz_corr; if (lna == 1) rssi -= 64; else if (lna == 2) rssi -= 74; else if (lna == 3) rssi -= 90; } else { rssi += sc->sc_rssi_5ghz_corr; if ((!sc->sc_ext_5ghz_lna) && (lna != 1)) rssi += 4; if (lna == 1) rssi -= 64; else if (lna == 2) rssi -= 86; else if (lna == 3) rssi -= 100; } /* range check */ if (rssi < 0) rssi = 0; else if (rssi > 255) rssi = 255; return (rssi); } static struct ieee80211vap * rum_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct rum_vap *rvp; struct ieee80211vap *vap; struct rum_softc *sc = ic->ic_ifp->if_softc; DPRINTF("\n"); /* Need to sync with config thread: */ mtx_lock(&sc->sc_mtx); if (usb2_config_td_sync(&sc->sc_config_td)) { mtx_unlock(&sc->sc_mtx); /* config thread is gone */ return (NULL); } mtx_unlock(&sc->sc_mtx); if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; rvp = (struct rum_vap *)malloc(sizeof(struct rum_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (rvp == NULL) return NULL; vap = &rvp->vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override state transition machine */ rvp->newstate = vap->iv_newstate; vap->iv_newstate = &rum_newstate_cb; ieee80211_amrr_init(&rvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 1000 /* 1 sec */ ); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); /* store current operation mode */ ic->ic_opmode = opmode; return (vap); } static void rum_vap_delete(struct ieee80211vap *vap) { struct rum_vap *rvp = RUM_VAP(vap); struct rum_softc *sc = vap->iv_ic->ic_ifp->if_softc; DPRINTF("\n"); /* Need to sync with config thread: */ mtx_lock(&sc->sc_mtx); if (usb2_config_td_sync(&sc->sc_config_td)) { /* ignore */ } mtx_unlock(&sc->sc_mtx); ieee80211_amrr_cleanup(&rvp->amrr); ieee80211_vap_detach(vap); free(rvp, M_80211_VAP); return; } /* ARGUSED */ static struct ieee80211_node * rum_node_alloc(struct ieee80211vap *vap __unused, const uint8_t mac[IEEE80211_ADDR_LEN] __unused) { struct rum_node *rn; rn = malloc(sizeof(struct rum_node), M_80211_NODE, M_NOWAIT | M_ZERO); return ((rn != NULL) ? &rn->ni : NULL); } static void rum_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni); return; } static void rum_fill_write_queue(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211_node *ni; struct mbuf *m; /* * We only fill up half of the queue with data frames. The rest is * reserved for other kinds of frames. */ while (sc->sc_tx_queue.ifq_len < (IFQ_MAXLEN / 2)) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; ni = (void *)(m->m_pkthdr.rcvif); m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); continue; } rum_tx_data(sc, m, ni); } return; } static void rum_tx_clean_queue(struct rum_softc *sc) { struct mbuf *m; for (;;) { _IF_DEQUEUE(&sc->sc_tx_queue, m); if (!m) { break; } rum_tx_freem(m); } return; } static void rum_tx_freem(struct mbuf *m) { struct ieee80211_node *ni; while (m) { ni = (void *)(m->m_pkthdr.rcvif); if (!ni) { m = m_free(m); continue; } if (m->m_flags & M_TXCB) { ieee80211_process_callback(ni, m, 0); } m_freem(m); ieee80211_free_node(ni); break; } return; } static void rum_tx_mgt(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ieee80211_frame *wh; struct ieee80211_key *k; uint32_t flags; uint16_t dur; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); ieee80211_free_node(ni); return; } wh = mtod(m, struct ieee80211_frame *); } flags = 0; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RT2573_TX_NEED_ACK; dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); /* tell hardware to add timestamp for probe responses */ if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) flags |= RT2573_TX_TIMESTAMP; } m->m_pkthdr.rcvif = (void *)ni; rum_setup_desc_and_tx(sc, m, flags, 0, tp->mgmtrate); return; } static struct ieee80211vap * rum_get_vap(struct rum_softc *sc) { struct ifnet *ifp; struct ieee80211com *ic; if (sc == NULL) { return NULL; } ifp = sc->sc_ifp; if (ifp == NULL) { return NULL; } ic = ifp->if_l2com; if (ic == NULL) { return NULL; } return TAILQ_FIRST(&ic->ic_vaps); } static void rum_tx_data(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ieee80211_frame *wh; struct ieee80211_key *k; uint32_t flags = 0; uint16_t dur; uint16_t rate; DPRINTFN(11, "Sending data.\n"); wh = mtod(m, struct ieee80211_frame *); tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else rate = ni->ni_txrate; if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); ieee80211_free_node(ni); return; } /* packet header may have moved, reset our local pointer */ wh = mtod(m, struct ieee80211_frame *); } if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { uint8_t prot = IEEE80211_PROT_NONE; if (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) prot = IEEE80211_PROT_RTSCTS; else if ((ic->ic_flags & IEEE80211_F_USEPROT) && ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) prot = ic->ic_protmode; if (prot != IEEE80211_PROT_NONE) { rum_tx_prot(sc, m, ni, prot, rate); flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; } flags |= RT2573_TX_NEED_ACK; flags |= RT2573_TX_MORE_FRAG; dur = ieee80211_ack_duration(sc->sc_rates, rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); } m->m_pkthdr.rcvif = (void *)ni; rum_setup_desc_and_tx(sc, m, flags, 0, rate); return; } static void rum_tx_prot(struct rum_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, uint8_t prot, uint16_t rate) { struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_frame *wh; struct mbuf *mprot; uint32_t flags; uint16_t protrate; uint16_t ackrate; uint16_t pktlen; uint16_t dur; uint8_t isshort; KASSERT((prot == IEEE80211_PROT_RTSCTS) || (prot == IEEE80211_PROT_CTSONLY), ("protection %u", prot)); DPRINTFN(11, "Sending protection frame.\n"); wh = mtod(m, const struct ieee80211_frame *); pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; protrate = ieee80211_ctl_rate(sc->sc_rates, rate); ackrate = ieee80211_ack_rate(sc->sc_rates, rate); isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort); +ieee80211_ack_duration(sc->sc_rates, rate, isshort); flags = RT2573_TX_MORE_FRAG; if (prot == IEEE80211_PROT_RTSCTS) { /* NB: CTS is the same size as an ACK */ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort); flags |= RT2573_TX_NEED_ACK; mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); } else { mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); } if (mprot == NULL) { return; } mprot->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni); rum_setup_desc_and_tx(sc, mprot, flags, 0, protrate); return; } static void rum_tx_raw(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { uint32_t flags; uint16_t rate; DPRINTFN(11, "Sending raw frame.\n"); rate = params->ibp_rate0 & IEEE80211_RATE_VAL; /* XXX validate */ if (rate == 0) { m_freem(m); ieee80211_free_node(ni); return; } flags = 0; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= RT2573_TX_NEED_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS | IEEE80211_BPF_CTS)) { rum_tx_prot(sc, m, ni, params->ibp_flags & IEEE80211_BPF_RTS ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; } m->m_pkthdr.rcvif = (void *)ni; rum_setup_desc_and_tx(sc, m, flags, 0, rate); return; } static int rum_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct rum_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ rum_tx_mgt(sc, m, ni); } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ rum_tx_raw(sc, m, ni, params); } mtx_unlock(&sc->sc_mtx); return (0); } static void rum_update_mcast_cb(struct ifnet *ifp) { /* not supported */ return; } static void rum_update_promisc_cb(struct ifnet *ifp) { struct rum_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &rum_config_copy, &rum_cfg_update_promisc, 0, 0); mtx_unlock(&sc->sc_mtx); return; } Index: user/thompsa/vaptq/sys/dev/usb2/wlan/if_ural2.c =================================================================== --- user/thompsa/vaptq/sys/dev/usb2/wlan/if_ural2.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/usb2/wlan/if_ural2.c (revision 185733) @@ -1,2788 +1,2790 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2005, 2006 * Damien Bergamini * * Copyright (c) 2006, 2008 * Hans Petter Selasky * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /* * * NOTE: all function names beginning like "ural_cfg_" can only * be called from within the config thread function ! */ #include __FBSDID("$FreeBSD$"); /*- * Ralink Technology RT2500USB chipset driver * http://www.ralinktech.com/ */ #include #include #include #include #define usb2_config_td_cc ural_config_copy #define usb2_config_td_softc ural_softc #define USB_DEBUG_VAR ural_debug #include #include #include #include #include #include #include #include #include #include #include #if USB_DEBUG static int ural_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, ural, CTLFLAG_RW, 0, "USB ural"); SYSCTL_INT(_hw_usb2_ural, OID_AUTO, debug, CTLFLAG_RW, &ural_debug, 0, "Debug level"); #endif #define URAL_RSSI(rssi) \ ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \ ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0) /* prototypes */ static device_probe_t ural_probe; static device_attach_t ural_attach; static device_detach_t ural_detach; static usb2_callback_t ural_bulk_read_callback; static usb2_callback_t ural_bulk_read_clear_stall_callback; static usb2_callback_t ural_bulk_write_callback; static usb2_callback_t ural_bulk_write_clear_stall_callback; static usb2_config_td_command_t ural_cfg_first_time_setup; static usb2_config_td_command_t ural_config_copy; static usb2_config_td_command_t ural_cfg_scan_start; static usb2_config_td_command_t ural_cfg_scan_end; static usb2_config_td_command_t ural_cfg_set_chan; static usb2_config_td_command_t ural_cfg_enable_tsf_sync; static usb2_config_td_command_t ural_cfg_update_slot; static usb2_config_td_command_t ural_cfg_set_txpreamble; static usb2_config_td_command_t ural_cfg_update_promisc; static usb2_config_td_command_t ural_cfg_pre_init; static usb2_config_td_command_t ural_cfg_init; static usb2_config_td_command_t ural_cfg_pre_stop; static usb2_config_td_command_t ural_cfg_stop; static usb2_config_td_command_t ural_cfg_amrr_timeout; static usb2_config_td_command_t ural_cfg_newstate; static void ural_cfg_do_request(struct ural_softc *sc, struct usb2_device_request *req, void *data); static void ural_cfg_set_testmode(struct ural_softc *sc); static void ural_cfg_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, uint16_t len); static uint16_t ural_cfg_read(struct ural_softc *sc, uint16_t reg); static void ural_cfg_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, uint16_t len); static void ural_cfg_write(struct ural_softc *sc, uint16_t reg, uint16_t val); static void ural_cfg_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, uint16_t len); static void ural_cfg_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val); static uint8_t ural_cfg_bbp_read(struct ural_softc *sc, uint8_t reg); static void ural_cfg_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val); static void ural_end_of_commands(struct ural_softc *sc); static const char *ural_get_rf(int rev); static void ural_watchdog(void *arg); static void ural_init_cb(void *arg); static int ural_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data); static void ural_start_cb(struct ifnet *ifp); static int ural_newstate_cb(struct ieee80211vap *ic, enum ieee80211_state nstate, int arg); static void ural_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func); static void ural_scan_start_cb(struct ieee80211com *); static void ural_scan_end_cb(struct ieee80211com *); static void ural_set_channel_cb(struct ieee80211com *); static void ural_cfg_disable_rf_tune(struct ural_softc *sc); static void ural_cfg_set_bssid(struct ural_softc *sc, uint8_t *bssid); static void ural_cfg_set_macaddr(struct ural_softc *sc, uint8_t *addr); static void ural_cfg_set_txantenna(struct ural_softc *sc, uint8_t antenna); static void ural_cfg_set_rxantenna(struct ural_softc *sc, uint8_t antenna); static void ural_cfg_read_eeprom(struct ural_softc *sc); static uint8_t ural_cfg_bbp_init(struct ural_softc *sc); static void ural_cfg_amrr_start(struct ural_softc *sc); static struct ieee80211vap *ural_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void ural_vap_delete(struct ieee80211vap *); static struct ieee80211_node *ural_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]); static void ural_newassoc(struct ieee80211_node *, int); static void ural_cfg_disable_tsf_sync(struct ural_softc *sc); static void ural_cfg_set_run(struct ural_softc *sc, struct usb2_config_td_cc *cc); static void ural_fill_write_queue(struct ural_softc *sc); static void ural_tx_clean_queue(struct ural_softc *sc); static void ural_tx_freem(struct mbuf *m); static void ural_tx_mgt(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni); static struct ieee80211vap *ural_get_vap(struct ural_softc *sc); static void ural_tx_bcn(struct ural_softc *sc); static void ural_tx_data(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni); static void ural_tx_prot(struct ural_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, uint8_t prot, uint16_t rate); static void ural_tx_raw(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params); static int ural_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params); static void ural_setup_desc_and_tx(struct ural_softc *sc, struct mbuf *m, uint32_t flags, uint16_t rate); static void ural_update_mcast_cb(struct ifnet *ifp); static void ural_update_promisc_cb(struct ifnet *ifp); /* various supported device vendors/products */ static const struct usb2_device_id ural_devs[] = { {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL167G, 0)}, {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_RALINK_RT2570, 0)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050, 0)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7051, 0)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_HU200TS, 0)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54G, 0)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GP, 0)}, {USB_VPI(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU, 0)}, {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG122, 0)}, {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GN54G, 0)}, {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWBKG, 0)}, {USB_VPI(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54AI, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54YB, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_NINWIFI, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_2, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_3, 0)}, {USB_VPI(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_NV902, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_2, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_3, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573, 0)}, {USB_VPI(USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_WL54G, 0)}, {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG, 0)}, {USB_VPI(USB_VENDOR_SPHAIRON, USB_PRODUCT_SPHAIRON_UB801R, 0)}, {USB_VPI(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2570, 0)}, {USB_VPI(USB_VENDOR_VTECH, USB_PRODUCT_VTECH_RT2570, 0)}, {USB_VPI(USB_VENDOR_ZINWELL, USB_PRODUCT_ZINWELL_RT2570, 0)}, }; /* * Default values for MAC registers; values taken from * the reference driver: */ struct ural_def_mac { uint16_t reg; uint16_t val; }; static const struct ural_def_mac ural_def_mac[] = { {RAL_TXRX_CSR5, 0x8c8d}, {RAL_TXRX_CSR6, 0x8b8a}, {RAL_TXRX_CSR7, 0x8687}, {RAL_TXRX_CSR8, 0x0085}, {RAL_MAC_CSR13, 0x1111}, {RAL_MAC_CSR14, 0x1e11}, {RAL_TXRX_CSR21, 0xe78f}, {RAL_MAC_CSR9, 0xff1d}, {RAL_MAC_CSR11, 0x0002}, {RAL_MAC_CSR22, 0x0053}, {RAL_MAC_CSR15, 0x0000}, {RAL_MAC_CSR8, RAL_FRAME_SIZE}, {RAL_TXRX_CSR19, 0x0000}, {RAL_TXRX_CSR18, 0x005a}, {RAL_PHY_CSR2, 0x0000}, {RAL_TXRX_CSR0, 0x1ec0}, {RAL_PHY_CSR4, 0x000f} }; /* * Default values for BBP registers; values taken from the reference driver. */ struct ural_def_bbp { uint8_t reg; uint8_t val; }; static const struct ural_def_bbp ural_def_bbp[] = { {3, 0x02}, {4, 0x19}, {14, 0x1c}, {15, 0x30}, {16, 0xac}, {17, 0x48}, {18, 0x18}, {19, 0xff}, {20, 0x1e}, {21, 0x08}, {22, 0x08}, {23, 0x08}, {24, 0x80}, {25, 0x50}, {26, 0x08}, {27, 0x23}, {30, 0x10}, {31, 0x2b}, {32, 0xb9}, {34, 0x12}, {35, 0x50}, {39, 0xc4}, {40, 0x02}, {41, 0x60}, {53, 0x10}, {54, 0x18}, {56, 0x08}, {57, 0x10}, {58, 0x08}, {61, 0x60}, {62, 0x10}, {75, 0xff} }; /* * Default values for RF register R2 indexed by channel numbers. */ static const uint32_t ural_rf2522_r2[] = { 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814, 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e }; static const uint32_t ural_rf2523_r2[] = { 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 }; static const uint32_t ural_rf2524_r2[] = { 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 }; static const uint32_t ural_rf2525_r2[] = { 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d, 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346 }; static const uint32_t ural_rf2525_hi_r2[] = { 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345, 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e }; static const uint32_t ural_rf2525e_r2[] = { 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463, 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b }; static const uint32_t ural_rf2526_hi_r2[] = { 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d, 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241 }; static const uint32_t ural_rf2526_r2[] = { 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229, 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d }; /* * For dual-band RF, RF registers R1 and R4 also depend on channel number; * values taken from the reference driver. */ struct ural_rf5222 { uint8_t chan; uint32_t r1; uint32_t r2; uint32_t r4; }; static const struct ural_rf5222 ural_rf5222[] = { {1, 0x08808, 0x0044d, 0x00282}, {2, 0x08808, 0x0044e, 0x00282}, {3, 0x08808, 0x0044f, 0x00282}, {4, 0x08808, 0x00460, 0x00282}, {5, 0x08808, 0x00461, 0x00282}, {6, 0x08808, 0x00462, 0x00282}, {7, 0x08808, 0x00463, 0x00282}, {8, 0x08808, 0x00464, 0x00282}, {9, 0x08808, 0x00465, 0x00282}, {10, 0x08808, 0x00466, 0x00282}, {11, 0x08808, 0x00467, 0x00282}, {12, 0x08808, 0x00468, 0x00282}, {13, 0x08808, 0x00469, 0x00282}, {14, 0x08808, 0x0046b, 0x00286}, {36, 0x08804, 0x06225, 0x00287}, {40, 0x08804, 0x06226, 0x00287}, {44, 0x08804, 0x06227, 0x00287}, {48, 0x08804, 0x06228, 0x00287}, {52, 0x08804, 0x06229, 0x00287}, {56, 0x08804, 0x0622a, 0x00287}, {60, 0x08804, 0x0622b, 0x00287}, {64, 0x08804, 0x0622c, 0x00287}, {100, 0x08804, 0x02200, 0x00283}, {104, 0x08804, 0x02201, 0x00283}, {108, 0x08804, 0x02202, 0x00283}, {112, 0x08804, 0x02203, 0x00283}, {116, 0x08804, 0x02204, 0x00283}, {120, 0x08804, 0x02205, 0x00283}, {124, 0x08804, 0x02206, 0x00283}, {128, 0x08804, 0x02207, 0x00283}, {132, 0x08804, 0x02208, 0x00283}, {136, 0x08804, 0x02209, 0x00283}, {140, 0x08804, 0x0220a, 0x00283}, {149, 0x08808, 0x02429, 0x00281}, {153, 0x08808, 0x0242b, 0x00281}, {157, 0x08808, 0x0242d, 0x00281}, {161, 0x08808, 0x0242f, 0x00281} }; static const struct usb2_config ural_config[URAL_N_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE + 4), .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .mh.callback = &ural_bulk_write_callback, .mh.timeout = 5000, /* ms */ }, [1] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = (RAL_FRAME_SIZE + RAL_RX_DESC_SIZE), .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &ural_bulk_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &ural_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &ural_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static devclass_t ural_devclass; static device_method_t ural_methods[] = { DEVMETHOD(device_probe, ural_probe), DEVMETHOD(device_attach, ural_attach), DEVMETHOD(device_detach, ural_detach), {0, 0} }; static driver_t ural_driver = { .name = "ural", .methods = ural_methods, .size = sizeof(struct ural_softc), }; DRIVER_MODULE(ural, ushub, ural_driver, ural_devclass, NULL, 0); MODULE_DEPEND(ural, usb2_wlan, 1, 1, 1); MODULE_DEPEND(ural, usb2_core, 1, 1, 1); MODULE_DEPEND(ural, wlan, 1, 1, 1); MODULE_DEPEND(ural, wlan_amrr, 1, 1, 1); static int ural_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != 0) { return (ENXIO); } if (uaa->info.bIfaceIndex != RAL_IFACE_INDEX) { return (ENXIO); } return (usb2_lookup_id_by_uaa(ural_devs, sizeof(ural_devs), uaa)); } static int ural_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct ural_softc *sc = device_get_softc(dev); int error; uint8_t iface_index; if (sc == NULL) { return (ENOMEM); } device_set_usb2_desc(dev); mtx_init(&sc->sc_mtx, "ural lock", MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); sc->sc_udev = uaa->device; sc->sc_unit = device_get_unit(dev); usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); iface_index = RAL_IFACE_INDEX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, ural_config, URAL_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "could not allocate USB transfers, " "err=%s\n", usb2_errstr(error)); goto detach; } error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, &ural_end_of_commands, sizeof(struct usb2_config_td_cc), 24); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&sc->sc_mtx); /* start setup */ usb2_config_td_queue_command (&sc->sc_config_td, NULL, &ural_cfg_first_time_setup, 0, 0); /* start watchdog (will exit mutex) */ ural_watchdog(sc); return (0); /* success */ detach: ural_detach(dev); return (ENXIO); /* failure */ } static int ural_detach(device_t dev) { struct ural_softc *sc = device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; usb2_config_td_drain(&sc->sc_config_td); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); ural_cfg_pre_stop(sc, NULL, 0); ifp = sc->sc_ifp; ic = ifp->if_l2com; mtx_unlock(&sc->sc_mtx); /* stop all USB transfers first */ usb2_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { bpfdetach(ifp); ieee80211_ifdetach(ic); if_free(ifp); } usb2_config_td_unsetup(&sc->sc_config_td); usb2_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } /*========================================================================* * REGISTER READ / WRITE WRAPPER ROUTINES *========================================================================*/ static void ural_cfg_do_request(struct ural_softc *sc, struct usb2_device_request *req, void *data) { uint16_t length; usb2_error_t err; repeat: if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto error; } err = usb2_do_request_flags (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); if (err) { DPRINTF("device request failed, err=%s " "(ignored)\n", usb2_errstr(err)); /* wait a little before next try */ if (usb2_config_td_sleep(&sc->sc_config_td, hz / 4)) { goto error; } /* try until we are detached */ goto repeat; error: /* the device has been detached */ length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } return; } static void ural_cfg_set_testmode(struct ural_softc *sc) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_VENDOR_REQUEST; USETW(req.wValue, 4); USETW(req.wIndex, 1); USETW(req.wLength, 0); ural_cfg_do_request(sc, &req, NULL); return; } static void ural_cfg_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_EEPROM; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); ural_cfg_do_request(sc, &req, buf); return; } static uint16_t ural_cfg_read(struct ural_softc *sc, uint16_t reg) { struct usb2_device_request req; uint16_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, sizeof(val)); ural_cfg_do_request(sc, &req, &val); return (le16toh(val)); } static void ural_cfg_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); ural_cfg_do_request(sc, &req, buf); return; } static void ural_cfg_write(struct ural_softc *sc, uint16_t reg, uint16_t val) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_WRITE_MAC; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); ural_cfg_do_request(sc, &req, NULL); return; } static void ural_cfg_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_WRITE_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); ural_cfg_do_request(sc, &req, buf); return; } static uint8_t ural_cfg_bbp_disbusy(struct ural_softc *sc) { uint16_t tmp; uint8_t to; for (to = 0;; to++) { if (to < 100) { tmp = ural_cfg_read(sc, RAL_PHY_CSR8); tmp &= RAL_BBP_BUSY; if (tmp == 0) { return (0); } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { break; } } else { break; } } DPRINTF("could not disbusy BBP\n"); return (1); /* failure */ } static void ural_cfg_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val) { uint16_t tmp; if (ural_cfg_bbp_disbusy(sc)) { return; } tmp = (reg << 8) | val; ural_cfg_write(sc, RAL_PHY_CSR7, tmp); return; } static uint8_t ural_cfg_bbp_read(struct ural_softc *sc, uint8_t reg) { uint16_t val; if (ural_cfg_bbp_disbusy(sc)) { return (0); } val = RAL_BBP_WRITE | (reg << 8); ural_cfg_write(sc, RAL_PHY_CSR7, val); if (ural_cfg_bbp_disbusy(sc)) { return (0); } return (ural_cfg_read(sc, RAL_PHY_CSR7) & 0xff); } static void ural_cfg_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val) { uint32_t tmp; uint8_t to; reg &= 3; /* remember last written value */ sc->sc_rf_regs[reg] = val; for (to = 0;; to++) { if (to < 100) { tmp = ural_cfg_read(sc, RAL_PHY_CSR10); if (!(tmp & RAL_RF_LOBUSY)) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return; } } else { DPRINTF("could not write to RF\n"); return; } } tmp = RAL_RF_BUSY | RAL_RF_20BIT | ((val & 0xfffff) << 2) | reg; ural_cfg_write(sc, RAL_PHY_CSR9, tmp & 0xffff); ural_cfg_write(sc, RAL_PHY_CSR10, tmp >> 16); DPRINTFN(16, "RF R[%u] <- 0x%05x\n", reg, val & 0xfffff); return; } static void ural_cfg_first_time_setup(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ieee80211com *ic; struct ifnet *ifp; uint8_t bands; /* setup RX tap header */ sc->sc_rxtap_len = sizeof(sc->sc_rxtap); sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(RAL_RX_RADIOTAP_PRESENT); /* setup TX tap header */ sc->sc_txtap_len = sizeof(sc->sc_txtap); sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(RAL_TX_RADIOTAP_PRESENT); /* retrieve RT2570 rev. no */ sc->sc_asic_rev = ural_cfg_read(sc, RAL_MAC_CSR0); /* retrieve MAC address and various other things from EEPROM */ ural_cfg_read_eeprom(sc); printf("%s: MAC/BBP RT2570 (rev 0x%02x), RF %s\n", sc->sc_name, sc->sc_asic_rev, ural_get_rf(sc->sc_rf_rev)); mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_IEEE80211); mtx_lock(&sc->sc_mtx); if (ifp == NULL) { DPRINTFN(0, "could not if_alloc()!\n"); goto done; } sc->sc_evilhack = ifp; sc->sc_ifp = ifp; ic = ifp->if_l2com; ifp->if_softc = sc; if_initname(ifp, "ural", sc->sc_unit); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = &ural_init_cb; ifp->if_ioctl = &ural_ioctl_cb; ifp->if_start = &ural_start_cb; ifp->if_watchdog = NULL; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); bcopy(sc->sc_myaddr, ic->ic_myaddr, sizeof(ic->ic_myaddr)); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_HOSTAP /* HostAp mode supported */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* bg scanning supported */ | IEEE80211_C_WPA /* 802.11i */ ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); if (sc->sc_rf_rev == RAL_RF_5222) { setbit(&bands, IEEE80211_MODE_11A); } ieee80211_init_channels(ic, NULL, &bands); mtx_unlock(&sc->sc_mtx); ieee80211_ifattach(ic); mtx_lock(&sc->sc_mtx); ic->ic_newassoc = &ural_newassoc; ic->ic_raw_xmit = &ural_raw_xmit_cb; ic->ic_node_alloc = &ural_node_alloc; ic->ic_update_mcast = &ural_update_mcast_cb; ic->ic_update_promisc = &ural_update_promisc_cb; ic->ic_scan_start = &ural_scan_start_cb; ic->ic_scan_end = &ural_scan_end_cb; ic->ic_set_channel = &ural_set_channel_cb; ic->ic_vap_create = &ural_vap_create; ic->ic_vap_delete = &ural_vap_delete; sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); mtx_unlock(&sc->sc_mtx); bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap)); if (bootverbose) { ieee80211_announce(ic); } mtx_lock(&sc->sc_mtx); done: return; } static void ural_end_of_commands(struct ural_softc *sc) { sc->sc_flags &= ~URAL_FLAG_WAIT_COMMAND; /* start write transfer, if not started */ usb2_transfer_start(sc->sc_xfer[0]); return; } static void ural_config_copy_chan(struct ural_config_copy_chan *cc, struct ieee80211com *ic, struct ieee80211_channel *c) { if (!c) return; cc->chan_to_ieee = ieee80211_chan2ieee(ic, c); if (c != IEEE80211_CHAN_ANYC) { cc->chan_to_mode = ieee80211_chan2mode(c); if (IEEE80211_IS_CHAN_B(c)) cc->chan_is_b = 1; if (IEEE80211_IS_CHAN_A(c)) cc->chan_is_a = 1; if (IEEE80211_IS_CHAN_2GHZ(c)) cc->chan_is_2ghz = 1; if (IEEE80211_IS_CHAN_5GHZ(c)) cc->chan_is_5ghz = 1; if (IEEE80211_IS_CHAN_ANYG(c)) cc->chan_is_g = 1; } return; } static void ural_config_copy(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp; struct ieee80211com *ic; struct ieee80211_node *ni; struct ieee80211vap *vap; const struct ieee80211_txparam *tp; bzero(cc, sizeof(*cc)); ifp = sc->sc_ifp; if (ifp) { cc->if_flags = ifp->if_flags; bcopy(ifp->if_broadcastaddr, cc->if_broadcastaddr, sizeof(cc->if_broadcastaddr)); ic = ifp->if_l2com; if (ic) { ural_config_copy_chan(&cc->ic_curchan, ic, ic->ic_curchan); ural_config_copy_chan(&cc->ic_bsschan, ic, ic->ic_bsschan); vap = TAILQ_FIRST(&ic->ic_vaps); if (vap) { ni = vap->iv_bss; if (ni) { cc->iv_bss.ni_intval = ni->ni_intval; bcopy(ni->ni_bssid, cc->iv_bss.ni_bssid, sizeof(cc->iv_bss.ni_bssid)); } tp = vap->iv_txparms + cc->ic_bsschan.chan_to_mode; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { cc->iv_bss.fixed_rate_none = 1; } } cc->ic_opmode = ic->ic_opmode; cc->ic_flags = ic->ic_flags; cc->ic_txpowlimit = ic->ic_txpowlimit; cc->ic_curmode = ic->ic_curmode; bcopy(ic->ic_myaddr, cc->ic_myaddr, sizeof(cc->ic_myaddr)); } } sc->sc_flags |= URAL_FLAG_WAIT_COMMAND; return; } static const char * ural_get_rf(int rev) { switch (rev) { case RAL_RF_2522:return "RT2522"; case RAL_RF_2523: return "RT2523"; case RAL_RF_2524: return "RT2524"; case RAL_RF_2525: return "RT2525"; case RAL_RF_2525E: return "RT2525e"; case RAL_RF_2526: return "RT2526"; case RAL_RF_5222: return "RT5222"; default: return "unknown"; } } /*------------------------------------------------------------------------* * ural_bulk_read_callback - data read "thread" *------------------------------------------------------------------------*/ static void ural_bulk_read_callback(struct usb2_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni; struct mbuf *m = NULL; uint32_t flags; uint32_t max_len; uint32_t real_len; uint8_t rssi = 0; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen); if (xfer->actlen < RAL_RX_DESC_SIZE) { DPRINTF("too short transfer, " "%d bytes\n", xfer->actlen); ifp->if_ierrors++; goto tr_setup; } max_len = (xfer->actlen - RAL_RX_DESC_SIZE); usb2_copy_out(xfer->frbuffers, max_len, &sc->sc_rx_desc, RAL_RX_DESC_SIZE); flags = le32toh(sc->sc_rx_desc.flags); if (flags & (RAL_RX_PHY_ERROR | RAL_RX_CRC_ERROR)) { /* * This should not happen since we did not * request to receive those frames when we * filled RAL_TXRX_CSR2: */ DPRINTFN(6, "PHY or CRC error\n"); ifp->if_ierrors++; goto tr_setup; } if (max_len > MCLBYTES) { max_len = MCLBYTES; } real_len = (flags >> 16) & 0xfff; if (real_len > max_len) { DPRINTF("invalid length in RX " "descriptor, %u bytes, received %u bytes\n", real_len, max_len); ifp->if_ierrors++; goto tr_setup; } /* ieee80211_input() will check if the mbuf is too short */ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { DPRINTF("could not allocate mbuf\n"); ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, 0, m->m_data, max_len); /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = real_len; DPRINTF("real length=%d bytes\n", real_len); rssi = URAL_RSSI(sc->sc_rx_desc.rssi); if (bpf_peers_present(ifp->if_bpf)) { struct ural_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = IEEE80211_RADIOTAP_F_FCS; tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, (sc->sc_rx_desc.flags & htole32(RAL_RX_OFDM)) ? IEEE80211_T_OFDM : IEEE80211_T_CCK); tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_antenna = sc->sc_rx_ant; tap->wr_antsignal = rssi; bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } /* Strip trailing 802.11 MAC FCS. */ m_adj(m, -IEEE80211_CRC_LEN); case USB_ST_SETUP: tr_setup: if (sc->sc_flags & URAL_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[3]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "ieee80211_input" here, and not some lines up! */ if (m) { mtx_unlock(&sc->sc_mtx); ni = ieee80211_find_rxnode(ic, (void *)(m->m_data)); if (ni) { /* send the frame to the 802.11 layer */ if (ieee80211_input(ni, m, rssi, RAL_NOISE_FLOOR, 0)) { /* ignore */ } /* node is no longer needed */ ieee80211_free_node(ni); } else { /* broadcast */ if (ieee80211_input_all(ic, m, rssi, RAL_NOISE_FLOOR, 0)) { /* ignore */ } } mtx_lock(&sc->sc_mtx); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= URAL_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[3]); } return; } } static void ural_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[1]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~URAL_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } return; } static uint8_t ural_plcp_signal(uint16_t rate) { ; /* indent fix */ switch (rate) { /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return (0x0); case 4: return (0x1); case 11: return (0x2); case 22: return (0x3); /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return (0xb); case 18: return (0xf); case 24: return (0xa); case 36: return (0xe); case 48: return (0x9); case 72: return (0xd); case 96: return (0x8); case 108: return (0xc); /* XXX unsupported/unknown rate */ default: return (0xff); } } /* * We assume that "m->m_pkthdr.rcvif" is pointing to the "ni" that * should be freed, when "ural_setup_desc_and_tx" is called. */ static void ural_setup_desc_and_tx(struct ural_softc *sc, struct mbuf *m, uint32_t flags, uint16_t rate) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct mbuf *mm; enum ieee80211_phytype phytype; uint16_t plcp_length; uint16_t len; uint8_t remainder; DPRINTF("in\n"); if (sc->sc_tx_queue.ifq_len >= IFQ_MAXLEN) { /* free packet */ ural_tx_freem(m); ifp->if_oerrors++; return; } if (!((sc->sc_flags & URAL_FLAG_LL_READY) && (sc->sc_flags & URAL_FLAG_HL_READY))) { /* free packet */ ural_tx_freem(m); ifp->if_oerrors++; return; } if (rate < 2) { DPRINTF("rate < 2!\n"); /* avoid division by zero */ rate = 2; } ic->ic_lastdata = ticks; if (bpf_peers_present(ifp->if_bpf)) { struct ural_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wt_antenna = sc->sc_tx_ant; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); } len = m->m_pkthdr.len; sc->sc_tx_desc.flags = htole32(flags); sc->sc_tx_desc.flags |= htole32(RAL_TX_NEWSEQ); sc->sc_tx_desc.flags |= htole32(len << 16); sc->sc_tx_desc.wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5) | RAL_IVOFFSET(sizeof(struct ieee80211_frame))); /* setup PLCP fields */ sc->sc_tx_desc.plcp_signal = ural_plcp_signal(rate); sc->sc_tx_desc.plcp_service = 4; len += IEEE80211_CRC_LEN; phytype = ieee80211_rate2phytype(sc->sc_rates, rate); if (phytype == IEEE80211_T_OFDM) { sc->sc_tx_desc.flags |= htole32(RAL_TX_OFDM); plcp_length = len & 0xfff; sc->sc_tx_desc.plcp_length_hi = plcp_length >> 6; sc->sc_tx_desc.plcp_length_lo = plcp_length & 0x3f; } else { plcp_length = ((16 * len) + rate - 1) / rate; if (rate == 22) { remainder = (16 * len) % 22; if ((remainder != 0) && (remainder < 7)) { sc->sc_tx_desc.plcp_service |= RAL_PLCP_LENGEXT; } } sc->sc_tx_desc.plcp_length_hi = plcp_length >> 8; sc->sc_tx_desc.plcp_length_lo = plcp_length & 0xff; if ((rate != 2) && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) { sc->sc_tx_desc.plcp_signal |= 0x08; } } sc->sc_tx_desc.iv = 0; sc->sc_tx_desc.eiv = 0; if (sizeof(sc->sc_tx_desc) > MHLEN) { DPRINTF("No room for header structure!\n"); ural_tx_freem(m); return; } mm = m_gethdr(M_NOWAIT, MT_DATA); if (mm == NULL) { DPRINTF("Could not allocate header mbuf!\n"); ural_tx_freem(m); return; } DPRINTF(" %zu %u (out)\n", sizeof(sc->sc_tx_desc), m->m_pkthdr.len); bcopy(&sc->sc_tx_desc, mm->m_data, sizeof(sc->sc_tx_desc)); mm->m_len = sizeof(sc->sc_tx_desc); mm->m_next = m; mm->m_pkthdr.len = mm->m_len + m->m_pkthdr.len; mm->m_pkthdr.rcvif = NULL; /* start write transfer, if not started */ _IF_ENQUEUE(&sc->sc_tx_queue, mm); usb2_transfer_start(sc->sc_xfer[0]); return; } static void ural_bulk_write_callback(struct usb2_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint16_t temp_len; uint8_t align; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen); ifp->if_opackets++; case USB_ST_SETUP: if (sc->sc_flags & URAL_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[2]); break; } if (sc->sc_flags & URAL_FLAG_WAIT_COMMAND) { /* * don't send anything while a command is pending ! */ break; } ural_fill_write_queue(sc); _IF_DEQUEUE(&sc->sc_tx_queue, m); if (m) { if (m->m_pkthdr.len > (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE)) { DPRINTFN(0, "data overflow, %u bytes\n", m->m_pkthdr.len); m->m_pkthdr.len = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE); } usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); /* compute transfer length */ temp_len = m->m_pkthdr.len; /* make transfer length 16-bit aligned */ align = (temp_len & 1); /* check if we need to add two extra bytes */ if (((temp_len + align) % 64) == 0) { align += 2; } /* check if we need to align length */ if (align != 0) { /* zero the extra bytes */ usb2_bzero(xfer->frbuffers, temp_len, align); temp_len += align; } DPRINTFN(11, "sending frame len=%u xferlen=%u\n", m->m_pkthdr.len, temp_len); xfer->frlengths[0] = temp_len; usb2_start_hardware(xfer); /* free mbuf and node */ ural_tx_freem(m); } break; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= URAL_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; break; } return; } static void ural_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~URAL_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } return; } static void ural_watchdog(void *arg) { struct ural_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); if (sc->sc_amrr_timer) { usb2_config_td_queue_command (&sc->sc_config_td, NULL, &ural_cfg_amrr_timeout, 0, 0); } usb2_callout_reset(&sc->sc_watchdog, hz, &ural_watchdog, sc); mtx_unlock(&sc->sc_mtx); return; } /*========================================================================* * IF-net callbacks *========================================================================*/ static void ural_init_cb(void *arg) { struct ural_softc *sc = arg; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &ural_cfg_pre_init, &ural_cfg_init, 0, 0); mtx_unlock(&sc->sc_mtx); return; } static int ural_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ural_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; int error; switch (cmd) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { usb2_config_td_queue_command (&sc->sc_config_td, &ural_cfg_pre_init, &ural_cfg_init, 0, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &ural_cfg_pre_stop, &ural_cfg_stop, 0, 0); } } mtx_unlock(&sc->sc_mtx); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, (void *)data, &ic->ic_media, cmd); break; default: error = ether_ioctl(ifp, cmd, data); } return (error); } static void ural_start_cb(struct ifnet *ifp) { struct ural_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); /* start write transfer, if not started */ usb2_transfer_start(sc->sc_xfer[0]); mtx_unlock(&sc->sc_mtx); return; } static void ural_cfg_newstate(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ural_vap *uvp = URAL_VAP(vap); enum ieee80211_state ostate; enum ieee80211_state nstate; int arg; ostate = vap->iv_state; nstate = sc->sc_ns_state; arg = sc->sc_ns_arg; if (ostate == IEEE80211_S_INIT) { /* We are leaving INIT. TSF sync should be off. */ ural_cfg_disable_tsf_sync(sc); } switch (nstate) { case IEEE80211_S_INIT: break; case IEEE80211_S_RUN: ural_cfg_set_run(sc, cc); break; default: break; } mtx_unlock(&sc->sc_mtx); IEEE80211_LOCK(ic); uvp->newstate(vap, nstate, arg); + /* XXX Broken! if (vap->iv_newstate_cb != NULL) vap->iv_newstate_cb(vap, nstate, arg); + */ IEEE80211_UNLOCK(ic); mtx_lock(&sc->sc_mtx); return; } static int ural_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ural_vap *uvp = URAL_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct ural_softc *sc = ic->ic_ifp->if_softc; DPRINTF("setting new state: %d\n", nstate); /* Special case - cannot defer this call and cannot block ! */ if (nstate == IEEE80211_S_INIT) { /* stop timers */ mtx_lock(&sc->sc_mtx); sc->sc_amrr_timer = 0; mtx_unlock(&sc->sc_mtx); return (uvp->newstate(vap, nstate, arg)); } mtx_lock(&sc->sc_mtx); if (usb2_config_td_is_gone(&sc->sc_config_td)) { mtx_unlock(&sc->sc_mtx); return (0); /* nothing to do */ } /* store next state */ sc->sc_ns_state = nstate; sc->sc_ns_arg = arg; /* stop timers */ sc->sc_amrr_timer = 0; /* * USB configuration can only be done from the USB configuration * thread: */ usb2_config_td_queue_command (&sc->sc_config_td, &ural_config_copy, &ural_cfg_newstate, 0, 0); mtx_unlock(&sc->sc_mtx); return (EINPROGRESS); } static void ural_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func) { struct ural_softc *sc = ic->ic_ifp->if_softc; mtx_lock(&sc->sc_mtx); sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); usb2_config_td_queue_command (&sc->sc_config_td, &ural_config_copy, func, 0, 0); mtx_unlock(&sc->sc_mtx); return; } static void ural_scan_start_cb(struct ieee80211com *ic) { ural_std_command(ic, &ural_cfg_scan_start); return; } static void ural_scan_end_cb(struct ieee80211com *ic) { ural_std_command(ic, &ural_cfg_scan_end); return; } static void ural_set_channel_cb(struct ieee80211com *ic) { ural_std_command(ic, &ural_cfg_set_chan); return; } /*========================================================================* * configure sub-routines, ural_cfg_xxx *========================================================================*/ static void ural_cfg_scan_start(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* abort TSF synchronization */ ural_cfg_disable_tsf_sync(sc); ural_cfg_set_bssid(sc, cc->if_broadcastaddr); return; } static void ural_cfg_scan_end(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* enable TSF synchronization */ ural_cfg_enable_tsf_sync(sc, cc, 0); ural_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); return; } static void ural_cfg_set_chan(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { enum { N_RF5222 = (sizeof(ural_rf5222) / sizeof(ural_rf5222[0])), }; uint32_t i; uint32_t chan; uint8_t power; uint8_t tmp; chan = cc->ic_curchan.chan_to_ieee; if ((chan == 0) || (chan == IEEE80211_CHAN_ANY)) { /* nothing to do */ return; } if (cc->ic_curchan.chan_is_2ghz) power = min(sc->sc_txpow[chan - 1], 31); else power = 31; /* adjust txpower using ifconfig settings */ power -= (100 - cc->ic_txpowlimit) / 8; DPRINTFN(3, "setting channel to %u, " "tx-power to %u\n", chan, power); switch (sc->sc_rf_rev) { case RAL_RF_2522: ural_cfg_rf_write(sc, RAL_RF1, 0x00814); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x00040); break; case RAL_RF_2523: ural_cfg_rf_write(sc, RAL_RF1, 0x08804); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x38044); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2524: ural_cfg_rf_write(sc, RAL_RF1, 0x0c808); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x00040); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2525: ural_cfg_rf_write(sc, RAL_RF1, 0x08808); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); ural_cfg_rf_write(sc, RAL_RF1, 0x08808); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2525E: ural_cfg_rf_write(sc, RAL_RF1, 0x08808); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282); break; case RAL_RF_2526: ural_cfg_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); ural_cfg_rf_write(sc, RAL_RF1, 0x08804); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); ural_cfg_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); break; /* dual-band RF */ case RAL_RF_5222: for (i = 0; i < N_RF5222; i++) { if (ural_rf5222[i].chan == chan) { ural_cfg_rf_write(sc, RAL_RF1, ural_rf5222[i].r1); ural_cfg_rf_write(sc, RAL_RF2, ural_rf5222[i].r2); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x00040); ural_cfg_rf_write(sc, RAL_RF4, ural_rf5222[i].r4); break; } } break; } if ((cc->ic_opmode != IEEE80211_M_MONITOR) && (!(cc->ic_flags & IEEE80211_F_SCAN))) { /* set Japan filter bit for channel 14 */ tmp = ural_cfg_bbp_read(sc, 70); if (chan == 14) { tmp |= RAL_JAPAN_FILTER; } else { tmp &= ~RAL_JAPAN_FILTER; } ural_cfg_bbp_write(sc, 70, tmp); /* clear CRC errors */ ural_cfg_read(sc, RAL_STA_CSR0); ural_cfg_disable_rf_tune(sc); } /* update basic rate set */ if (cc->ic_curchan.chan_is_b) { /* 11b basic rates: 1, 2Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x3); } else if (cc->ic_curchan.chan_is_a) { /* 11a basic rates: 6, 12, 24Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x150); } else { /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x15f); } /* wait a little */ if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return; } return; } static void ural_cfg_set_run(struct ural_softc *sc, struct usb2_config_td_cc *cc) { if (cc->ic_opmode != IEEE80211_M_MONITOR) { ural_cfg_update_slot(sc, cc, 0); ural_cfg_set_txpreamble(sc, cc, 0); /* update basic rate set */ if (cc->ic_bsschan.chan_is_5ghz) { /* 11a basic rates: 6, 12, 24Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x150); } else if (cc->ic_bsschan.chan_is_g) { /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x15f); } else { /* 11b basic rates: 1, 2Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x3); } ural_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); } if ((cc->ic_opmode == IEEE80211_M_HOSTAP) || (cc->ic_opmode == IEEE80211_M_IBSS)) { ural_tx_bcn(sc); } /* make tx led blink on tx (controlled by ASIC) */ ural_cfg_write(sc, RAL_MAC_CSR20, 1); if (cc->ic_opmode != IEEE80211_M_MONITOR) { ural_cfg_enable_tsf_sync(sc, cc, 0); } /* clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_cfg_read_multi(sc, RAL_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); if (cc->iv_bss.fixed_rate_none) { /* enable automatic rate adaptation */ ural_cfg_amrr_start(sc); } return; } /*------------------------------------------------------------------------* * ural_cfg_disable_rf_tune - disable RF auto-tuning *------------------------------------------------------------------------*/ static void ural_cfg_disable_rf_tune(struct ural_softc *sc) { uint32_t tmp; if (sc->sc_rf_rev != RAL_RF_2523) { tmp = sc->sc_rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE; ural_cfg_rf_write(sc, RAL_RF1, tmp); } tmp = sc->sc_rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE; ural_cfg_rf_write(sc, RAL_RF3, tmp); DPRINTFN(3, "disabling RF autotune\n"); return; } /*------------------------------------------------------------------------* * ural_cfg_enable_tsf_sync - refer to IEEE Std 802.11-1999 pp. 123 * for more information on TSF synchronization *------------------------------------------------------------------------*/ static void ural_cfg_enable_tsf_sync(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint16_t logcwmin; uint16_t preload; uint16_t tmp; /* first, disable TSF synchronization */ ural_cfg_write(sc, RAL_TXRX_CSR19, 0); tmp = (16 * cc->iv_bss.ni_intval) << 4; ural_cfg_write(sc, RAL_TXRX_CSR18, tmp); logcwmin = (cc->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0; preload = (cc->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6; tmp = (logcwmin << 12) | preload; ural_cfg_write(sc, RAL_TXRX_CSR20, tmp); /* finally, enable TSF synchronization */ tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN; if (cc->ic_opmode == IEEE80211_M_STA) tmp |= RAL_ENABLE_TSF_SYNC(1); else tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR; ural_cfg_write(sc, RAL_TXRX_CSR19, tmp); DPRINTF("enabling TSF synchronization\n"); return; } static void ural_cfg_disable_tsf_sync(struct ural_softc *sc) { /* abort TSF synchronization */ ural_cfg_write(sc, RAL_TXRX_CSR19, 0); /* force tx led to stop blinking */ ural_cfg_write(sc, RAL_MAC_CSR20, 0); return; } #define RAL_RXTX_TURNAROUND 5 /* us */ static void ural_cfg_update_slot(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint16_t slottime; uint16_t sifs; uint16_t eifs; slottime = (cc->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; /* * These settings may sound a bit inconsistent but this is what the * reference driver does. */ if (cc->ic_curmode == IEEE80211_MODE_11B) { sifs = 16 - RAL_RXTX_TURNAROUND; eifs = 364; } else { sifs = 10 - RAL_RXTX_TURNAROUND; eifs = 64; } ural_cfg_write(sc, RAL_MAC_CSR10, slottime); ural_cfg_write(sc, RAL_MAC_CSR11, sifs); ural_cfg_write(sc, RAL_MAC_CSR12, eifs); return; } static void ural_cfg_set_txpreamble(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint16_t tmp; tmp = ural_cfg_read(sc, RAL_TXRX_CSR10); if (cc->ic_flags & IEEE80211_F_SHPREAMBLE) { tmp |= RAL_SHORT_PREAMBLE; } else { tmp &= ~RAL_SHORT_PREAMBLE; } ural_cfg_write(sc, RAL_TXRX_CSR10, tmp); return; } static void ural_cfg_set_bssid(struct ural_softc *sc, uint8_t *bssid) { ural_cfg_write_multi(sc, RAL_MAC_CSR5, bssid, IEEE80211_ADDR_LEN); DPRINTF("setting BSSID to 0x%02x%02x%02x%02x%02x%02x\n", bssid[5], bssid[4], bssid[3], bssid[2], bssid[1], bssid[0]); return; } static void ural_cfg_set_macaddr(struct ural_softc *sc, uint8_t *addr) { ural_cfg_write_multi(sc, RAL_MAC_CSR2, addr, IEEE80211_ADDR_LEN); DPRINTF("setting MAC to 0x%02x%02x%02x%02x%02x%02x\n", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); return; } static void ural_cfg_update_promisc(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint16_t tmp; tmp = ural_cfg_read(sc, RAL_TXRX_CSR2); if (cc->if_flags & IFF_PROMISC) { tmp &= ~RAL_DROP_NOT_TO_ME; } else { tmp |= RAL_DROP_NOT_TO_ME; } ural_cfg_write(sc, RAL_TXRX_CSR2, tmp); DPRINTF("%s promiscuous mode\n", (cc->if_flags & IFF_PROMISC) ? "entering" : "leaving"); return; } static void ural_cfg_set_txantenna(struct ural_softc *sc, uint8_t antenna) { uint16_t tmp; uint8_t tx; tx = ural_cfg_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK; if (antenna == 1) tx |= RAL_BBP_ANTA; else if (antenna == 2) tx |= RAL_BBP_ANTB; else tx |= RAL_BBP_DIVERSITY; /* need to force I/Q flip for RF 2525e, 2526 and 5222 */ if ((sc->sc_rf_rev == RAL_RF_2525E) || (sc->sc_rf_rev == RAL_RF_2526) || (sc->sc_rf_rev == RAL_RF_5222)) { tx |= RAL_BBP_FLIPIQ; } ural_cfg_bbp_write(sc, RAL_BBP_TX, tx); /* update values in PHY_CSR5 and PHY_CSR6 */ tmp = ural_cfg_read(sc, RAL_PHY_CSR5) & ~0x7; ural_cfg_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7)); tmp = ural_cfg_read(sc, RAL_PHY_CSR6) & ~0x7; ural_cfg_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7)); return; } static void ural_cfg_set_rxantenna(struct ural_softc *sc, uint8_t antenna) { uint8_t rx; rx = ural_cfg_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK; if (antenna == 1) rx |= RAL_BBP_ANTA; else if (antenna == 2) rx |= RAL_BBP_ANTB; else rx |= RAL_BBP_DIVERSITY; /* need to force no I/Q flip for RF 2525e and 2526 */ if ((sc->sc_rf_rev == RAL_RF_2525E) || (sc->sc_rf_rev == RAL_RF_2526)) { rx &= ~RAL_BBP_FLIPIQ; } ural_cfg_bbp_write(sc, RAL_BBP_RX, rx); return; } static void ural_cfg_read_eeprom(struct ural_softc *sc) { uint16_t val; ural_cfg_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2); val = le16toh(val); sc->sc_rf_rev = (val >> 11) & 0x7; sc->sc_hw_radio = (val >> 10) & 0x1; sc->sc_led_mode = (val >> 6) & 0x7; sc->sc_rx_ant = (val >> 4) & 0x3; sc->sc_tx_ant = (val >> 2) & 0x3; sc->sc_nb_ant = (val & 0x3); DPRINTF("val = 0x%04x\n", val); /* read MAC address */ ural_cfg_eeprom_read(sc, RAL_EEPROM_ADDRESS, sc->sc_myaddr, sizeof(sc->sc_myaddr)); /* read default values for BBP registers */ ural_cfg_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->sc_bbp_prom, sizeof(sc->sc_bbp_prom)); /* read Tx power for all b/g channels */ ural_cfg_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->sc_txpow, sizeof(sc->sc_txpow)); return; } static uint8_t ural_cfg_bbp_init(struct ural_softc *sc) { enum { N_DEF_BBP = (sizeof(ural_def_bbp) / sizeof(ural_def_bbp[0])), }; uint16_t i; uint8_t to; /* wait for BBP to become ready */ for (to = 0;; to++) { if (to < 100) { if (ural_cfg_bbp_read(sc, RAL_BBP_VERSION) != 0) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return (1); /* failure */ } } else { DPRINTF("timeout waiting for BBP\n"); return (1); /* failure */ } } /* initialize BBP registers to default values */ for (i = 0; i < N_DEF_BBP; i++) { ural_cfg_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val); } #if 0 /* initialize BBP registers to values stored in EEPROM */ for (i = 0; i < 16; i++) { if (sc->sc_bbp_prom[i].reg == 0xff) { continue; } ural_cfg_bbp_write(sc, sc->sc_bbp_prom[i].reg, sc->sc_bbp_prom[i].val); } #endif return (0); /* success */ } static void ural_cfg_pre_init(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* immediate configuration */ ural_cfg_pre_stop(sc, cc, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= URAL_FLAG_HL_READY; IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); return; } static void ural_cfg_init(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { enum { N_DEF_MAC = (sizeof(ural_def_mac) / sizeof(ural_def_mac[0])), }; uint16_t tmp; uint16_t i; uint8_t to; /* delayed configuration */ ural_cfg_set_testmode(sc); ural_cfg_write(sc, 0x308, 0x00f0); /* XXX magic */ ural_cfg_stop(sc, cc, 0); /* initialize MAC registers to default values */ for (i = 0; i < N_DEF_MAC; i++) { ural_cfg_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val); } /* wait for BBP and RF to wake up (this can take a long time!) */ for (to = 0;; to++) { if (to < 100) { tmp = ural_cfg_read(sc, RAL_MAC_CSR17); if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) == (RAL_BBP_AWAKE | RAL_RF_AWAKE)) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { goto fail; } } else { DPRINTF("timeout waiting for " "BBP/RF to wakeup\n"); goto fail; } } /* we're ready! */ ural_cfg_write(sc, RAL_MAC_CSR1, RAL_HOST_READY); /* set basic rate set (will be updated later) */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x15f); if (ural_cfg_bbp_init(sc)) { goto fail; } /* set default BSS channel */ ural_cfg_set_chan(sc, cc, 0); /* clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_cfg_read_multi(sc, RAL_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); DPRINTF("rx_ant=%d, tx_ant=%d\n", sc->sc_rx_ant, sc->sc_tx_ant); ural_cfg_set_txantenna(sc, sc->sc_tx_ant); ural_cfg_set_rxantenna(sc, sc->sc_rx_ant); ural_cfg_set_macaddr(sc, cc->ic_myaddr); /* * make sure that the first transaction * clears the stall: */ sc->sc_flags |= (URAL_FLAG_READ_STALL | URAL_FLAG_WRITE_STALL | URAL_FLAG_LL_READY); if ((sc->sc_flags & URAL_FLAG_LL_READY) && (sc->sc_flags & URAL_FLAG_HL_READY)) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* * start the USB transfers, if not already started: */ usb2_transfer_start(sc->sc_xfer[1]); usb2_transfer_start(sc->sc_xfer[0]); /* * start IEEE802.11 layer */ mtx_unlock(&sc->sc_mtx); ieee80211_start_all(ic); mtx_lock(&sc->sc_mtx); } /* * start Rx */ tmp = RAL_DROP_PHY | RAL_DROP_CRC; if (cc->ic_opmode != IEEE80211_M_MONITOR) { tmp |= (RAL_DROP_CTL | RAL_DROP_BAD_VERSION); if (cc->ic_opmode != IEEE80211_M_HOSTAP) { tmp |= RAL_DROP_TODS; } if (!(cc->if_flags & IFF_PROMISC)) { tmp |= RAL_DROP_NOT_TO_ME; } } ural_cfg_write(sc, RAL_TXRX_CSR2, tmp); return; fail: ural_cfg_pre_stop(sc, NULL, 0); if (cc) { ural_cfg_stop(sc, cc, 0); } return; } static void ural_cfg_pre_stop(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if (cc) { /* copy the needed configuration */ ural_config_copy(sc, cc, refcount); } /* immediate configuration */ if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } sc->sc_flags &= ~(URAL_FLAG_HL_READY | URAL_FLAG_LL_READY); /* * stop all the transfers, if not already stopped: */ usb2_transfer_stop(sc->sc_xfer[0]); usb2_transfer_stop(sc->sc_xfer[1]); usb2_transfer_stop(sc->sc_xfer[2]); usb2_transfer_stop(sc->sc_xfer[3]); /* clean up transmission */ ural_tx_clean_queue(sc); return; } static void ural_cfg_stop(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* disable Rx */ ural_cfg_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX); /* reset ASIC and BBP (but won't reset MAC registers!) */ ural_cfg_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP); /* wait a little */ usb2_config_td_sleep(&sc->sc_config_td, hz / 10); /* clear reset */ ural_cfg_write(sc, RAL_MAC_CSR1, 0); /* wait a little */ usb2_config_td_sleep(&sc->sc_config_td, hz / 10); return; } static void ural_cfg_amrr_start(struct ural_softc *sc) { struct ieee80211vap *vap; struct ieee80211_node *ni; vap = ural_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } /* init AMRR */ ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni); /* enable AMRR timer */ sc->sc_amrr_timer = 1; return; } static void ural_cfg_amrr_timeout(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211vap *vap; struct ieee80211_node *ni; uint32_t ok; uint32_t fail; /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_cfg_read_multi(sc, RAL_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); vap = ural_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } if ((sc->sc_flags & URAL_FLAG_LL_READY) && (sc->sc_flags & URAL_FLAG_HL_READY)) { ok = sc->sc_sta[7] + /* TX ok w/o retry */ sc->sc_sta[8]; /* TX ok w/ retry */ fail = sc->sc_sta[9]; /* TX retry-fail count */ if (sc->sc_amrr_timer) { ieee80211_amrr_tx_update(&URAL_NODE(ni)->amn, ok + fail, ok, sc->sc_sta[8] + fail); if (ieee80211_amrr_choose(ni, &URAL_NODE(ni)->amn)) { /* ignore */ } } ifp->if_oerrors += fail; } return; } static struct ieee80211vap * ural_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ural_vap *uvp; struct ieee80211vap *vap; struct ural_softc *sc = ic->ic_ifp->if_softc; /* Need to sync with config thread: */ mtx_lock(&sc->sc_mtx); if (usb2_config_td_sync(&sc->sc_config_td)) { mtx_unlock(&sc->sc_mtx); /* config thread is gone */ return (NULL); } mtx_unlock(&sc->sc_mtx); if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; uvp = (struct ural_vap *)malloc(sizeof(struct ural_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (uvp == NULL) return NULL; vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = &ural_newstate_cb; ieee80211_amrr_init(&uvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 1000 /* 1 sec */ ); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); /* store current operation mode */ ic->ic_opmode = opmode; return (vap); } static void ural_vap_delete(struct ieee80211vap *vap) { struct ural_vap *uvp = URAL_VAP(vap); struct ural_softc *sc = vap->iv_ic->ic_ifp->if_softc; /* Need to sync with config thread: */ mtx_lock(&sc->sc_mtx); if (usb2_config_td_sync(&sc->sc_config_td)) { /* ignore */ } mtx_unlock(&sc->sc_mtx); ieee80211_amrr_cleanup(&uvp->amrr); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); return; } /* ARGUSED */ static struct ieee80211_node * ural_node_alloc(struct ieee80211vap *vap __unused, const uint8_t mac[IEEE80211_ADDR_LEN] __unused) { struct ural_node *un; un = malloc(sizeof(struct ural_node), M_80211_NODE, M_NOWAIT | M_ZERO); return ((un != NULL) ? &un->ni : NULL); } static void ural_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni); return; } static void ural_fill_write_queue(struct ural_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211_node *ni; struct mbuf *m; /* * We only fill up half of the queue with data frames. The rest is * reserved for other kinds of frames. */ while (sc->sc_tx_queue.ifq_len < (IFQ_MAXLEN / 2)) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; ni = (void *)(m->m_pkthdr.rcvif); m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); continue; } ural_tx_data(sc, m, ni); } return; } static void ural_tx_clean_queue(struct ural_softc *sc) { struct mbuf *m; for (;;) { _IF_DEQUEUE(&sc->sc_tx_queue, m); if (!m) { break; } ural_tx_freem(m); } return; } static void ural_tx_freem(struct mbuf *m) { struct ieee80211_node *ni; while (m) { ni = (void *)(m->m_pkthdr.rcvif); if (!ni) { m = m_free(m); continue; } if (m->m_flags & M_TXCB) { ieee80211_process_callback(ni, m, 0); } m_freem(m); ieee80211_free_node(ni); break; } return; } static void ural_tx_mgt(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ieee80211_frame *wh; struct ieee80211_key *k; uint32_t flags; uint16_t dur; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); ieee80211_free_node(ni); return; } wh = mtod(m, struct ieee80211_frame *); } flags = 0; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RAL_TX_ACK; dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); /* tell hardware to add timestamp for probe responses */ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT && (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= RAL_TX_TIMESTAMP; } m->m_pkthdr.rcvif = (void *)ni; ural_setup_desc_and_tx(sc, m, flags, tp->mgmtrate); return; } static struct ieee80211vap * ural_get_vap(struct ural_softc *sc) { struct ifnet *ifp; struct ieee80211com *ic; if (sc == NULL) { return NULL; } ifp = sc->sc_ifp; if (ifp == NULL) { return NULL; } ic = ifp->if_l2com; if (ic == NULL) { return NULL; } return TAILQ_FIRST(&ic->ic_vaps); } static void ural_tx_bcn(struct ural_softc *sc) { struct ieee80211_node *ni; struct ieee80211vap *vap; struct ieee80211com *ic; const struct ieee80211_txparam *tp; struct mbuf *m; vap = ural_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } ic = vap->iv_ic; if (ic == NULL) { return; } DPRINTFN(11, "Sending beacon frame.\n"); m = ieee80211_beacon_alloc(ni, &URAL_VAP(vap)->bo); if (m == NULL) { DPRINTFN(0, "could not allocate beacon\n"); return; } tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; m->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni); ural_setup_desc_and_tx(sc, m, RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP, tp->mgmtrate); return; } static void ural_tx_data(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ieee80211_frame *wh; struct ieee80211_key *k; uint32_t flags = 0; uint16_t dur; uint16_t rate; DPRINTFN(11, "Sending data.\n"); wh = mtod(m, struct ieee80211_frame *); tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else rate = ni->ni_txrate; if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); ieee80211_free_node(ni); return; } /* packet header may have moved, reset our local pointer */ wh = mtod(m, struct ieee80211_frame *); } if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { uint8_t prot = IEEE80211_PROT_NONE; if (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) prot = IEEE80211_PROT_RTSCTS; else if ((ic->ic_flags & IEEE80211_F_USEPROT) && ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) prot = ic->ic_protmode; if (prot != IEEE80211_PROT_NONE) { ural_tx_prot(sc, m, ni, prot, rate); flags |= RAL_TX_IFS_SIFS; } flags |= RAL_TX_ACK; flags |= RAL_TX_RETRY(7); dur = ieee80211_ack_duration(sc->sc_rates, rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); } m->m_pkthdr.rcvif = (void *)ni; ural_setup_desc_and_tx(sc, m, flags, rate); return; } static void ural_tx_prot(struct ural_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, uint8_t prot, uint16_t rate) { struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_frame *wh; struct mbuf *mprot; uint32_t flags; uint16_t protrate; uint16_t ackrate; uint16_t pktlen; uint16_t dur; uint8_t isshort; KASSERT((prot == IEEE80211_PROT_RTSCTS) || (prot == IEEE80211_PROT_CTSONLY), ("protection %u", prot)); DPRINTFN(16, "Sending protection frame.\n"); wh = mtod(m, const struct ieee80211_frame *); pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; protrate = ieee80211_ctl_rate(sc->sc_rates, rate); ackrate = ieee80211_ack_rate(sc->sc_rates, rate); isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort); +ieee80211_ack_duration(sc->sc_rates, rate, isshort); flags = RAL_TX_RETRY(7); if (prot == IEEE80211_PROT_RTSCTS) { /* NB: CTS is the same size as an ACK */ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort); flags |= RAL_TX_ACK; mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); } else { mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); } if (mprot == NULL) { return; } mprot->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni); ural_setup_desc_and_tx(sc, mprot, flags, protrate); return; } static void ural_tx_raw(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { uint32_t flags; uint16_t rate; DPRINTFN(11, "Sending raw frame.\n"); rate = params->ibp_rate0 & IEEE80211_RATE_VAL; /* XXX validate */ if (rate == 0) { m_freem(m); ieee80211_free_node(ni); return; } flags = 0; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= RAL_TX_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS | IEEE80211_BPF_CTS)) { ural_tx_prot(sc, m, ni, (params->ibp_flags & IEEE80211_BPF_RTS) ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); flags |= RAL_TX_IFS_SIFS; } m->m_pkthdr.rcvif = (void *)ni; ural_setup_desc_and_tx(sc, m, flags, rate); return; } static int ural_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct ural_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ ural_tx_mgt(sc, m, ni); } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ ural_tx_raw(sc, m, ni, params); } mtx_unlock(&sc->sc_mtx); return (0); } static void ural_update_mcast_cb(struct ifnet *ifp) { /* not supported */ return; } static void ural_update_promisc_cb(struct ifnet *ifp) { struct ural_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &ural_config_copy, &ural_cfg_update_promisc, 0, 0); mtx_unlock(&sc->sc_mtx); return; } Index: user/thompsa/vaptq/sys/dev/usb2/wlan/if_zyd2.c =================================================================== --- user/thompsa/vaptq/sys/dev/usb2/wlan/if_zyd2.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/usb2/wlan/if_zyd2.c (revision 185733) @@ -1,3297 +1,3299 @@ /* $OpenBSD: if_zyd.c,v 1.52 2007/02/11 00:08:04 jsg Exp $ */ /* $NetBSD: if_zyd.c,v 1.7 2007/06/21 04:04:29 kiyohara Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 2006 by Damien Bergamini * Copyright (c) 2006 by Florian Stoehr * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /* * ZyDAS ZD1211/ZD1211B USB WLAN driver * * NOTE: all function names beginning like "zyd_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #define usb2_config_td_cc zyd_config_copy #define usb2_config_td_softc zyd_softc #define USB_DEBUG_VAR zyd_debug #include #include #include #include #include #include #include #include #include #include #include #if USB_DEBUG static int zyd_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, zyd, CTLFLAG_RW, 0, "USB zyd"); SYSCTL_INT(_hw_usb2_zyd, OID_AUTO, debug, CTLFLAG_RW, &zyd_debug, 0, "zyd debug level"); #endif #undef INDEXES #define INDEXES(a) (sizeof(a) / sizeof((a)[0])) static device_probe_t zyd_probe; static device_attach_t zyd_attach; static device_detach_t zyd_detach; static usb2_callback_t zyd_intr_read_clear_stall_callback; static usb2_callback_t zyd_intr_read_callback; static usb2_callback_t zyd_intr_write_clear_stall_callback; static usb2_callback_t zyd_intr_write_callback; static usb2_callback_t zyd_bulk_read_clear_stall_callback; static usb2_callback_t zyd_bulk_read_callback; static usb2_callback_t zyd_bulk_write_clear_stall_callback; static usb2_callback_t zyd_bulk_write_callback; static usb2_config_td_command_t zyd_cfg_first_time_setup; static usb2_config_td_command_t zyd_cfg_update_promisc; static usb2_config_td_command_t zyd_cfg_set_chan; static usb2_config_td_command_t zyd_cfg_pre_init; static usb2_config_td_command_t zyd_cfg_init; static usb2_config_td_command_t zyd_cfg_pre_stop; static usb2_config_td_command_t zyd_cfg_stop; static usb2_config_td_command_t zyd_config_copy; static usb2_config_td_command_t zyd_cfg_scan_start; static usb2_config_td_command_t zyd_cfg_scan_end; static usb2_config_td_command_t zyd_cfg_set_rxfilter; static usb2_config_td_command_t zyd_cfg_amrr_timeout; static uint8_t zyd_plcp2ieee(uint8_t signal, uint8_t isofdm); static void zyd_cfg_usbrequest(struct zyd_softc *sc, struct usb2_device_request *req, uint8_t *data); static void zyd_cfg_usb2_intr_read(struct zyd_softc *sc, void *data, uint32_t size); static void zyd_cfg_usb2_intr_write(struct zyd_softc *sc, const void *data, uint16_t code, uint32_t size); static void zyd_cfg_read16(struct zyd_softc *sc, uint16_t addr, uint16_t *value); static void zyd_cfg_read32(struct zyd_softc *sc, uint16_t addr, uint32_t *value); static void zyd_cfg_write16(struct zyd_softc *sc, uint16_t addr, uint16_t value); static void zyd_cfg_write32(struct zyd_softc *sc, uint16_t addr, uint32_t value); static void zyd_cfg_rfwrite(struct zyd_softc *sc, uint32_t value); static uint8_t zyd_cfg_uploadfirmware(struct zyd_softc *sc, const uint8_t *fw_ptr, uint32_t fw_len); static void zyd_cfg_lock_phy(struct zyd_softc *sc); static void zyd_cfg_unlock_phy(struct zyd_softc *sc); static void zyd_cfg_set_beacon_interval(struct zyd_softc *sc, uint32_t interval); static const char *zyd_rf_name(uint8_t type); static void zyd_cfg_rf_rfmd_init(struct zyd_softc *sc, struct zyd_rf *rf); static void zyd_cfg_rf_rfmd_switch_radio(struct zyd_softc *sc, uint8_t onoff); static void zyd_cfg_rf_rfmd_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel); static void zyd_cfg_rf_al2230_switch_radio(struct zyd_softc *sc, uint8_t onoff); static void zyd_cfg_rf_al2230_init(struct zyd_softc *sc, struct zyd_rf *rf); static void zyd_cfg_rf_al2230_init_b(struct zyd_softc *sc, struct zyd_rf *rf); static void zyd_cfg_rf_al2230_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel); static uint8_t zyd_cfg_rf_init_hw(struct zyd_softc *sc, struct zyd_rf *rf); static uint8_t zyd_cfg_hw_init(struct zyd_softc *sc); static void zyd_cfg_set_mac_addr(struct zyd_softc *sc, const uint8_t *addr); static void zyd_cfg_switch_radio(struct zyd_softc *sc, uint8_t onoff); static void zyd_cfg_set_bssid(struct zyd_softc *sc, uint8_t *addr); static void zyd_start_cb(struct ifnet *ifp); static void zyd_init_cb(void *arg); static int zyd_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); static void zyd_watchdog(void *arg); static void zyd_end_of_commands(struct zyd_softc *sc); static void zyd_newassoc_cb(struct ieee80211_node *ni, int isnew); static void zyd_scan_start_cb(struct ieee80211com *ic); static void zyd_scan_end_cb(struct ieee80211com *ic); static void zyd_set_channel_cb(struct ieee80211com *ic); static void zyd_cfg_set_led(struct zyd_softc *sc, uint32_t which, uint8_t on); static struct ieee80211vap *zyd_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void zyd_vap_delete(struct ieee80211vap *); static struct ieee80211_node *zyd_node_alloc_cb(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]); static void zyd_cfg_set_run(struct zyd_softc *sc, struct usb2_config_td_cc *cc); static void zyd_fill_write_queue(struct zyd_softc *sc); static void zyd_tx_clean_queue(struct zyd_softc *sc); static void zyd_tx_freem(struct mbuf *m); static void zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m, struct ieee80211_node *ni); static struct ieee80211vap *zyd_get_vap(struct zyd_softc *sc); static void zyd_tx_data(struct zyd_softc *sc, struct mbuf *m, struct ieee80211_node *ni); static int zyd_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params); static void zyd_setup_desc_and_tx(struct zyd_softc *sc, struct mbuf *m, uint16_t rate); static int zyd_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg); static void zyd_cfg_amrr_start(struct zyd_softc *sc); static void zyd_update_mcast_cb(struct ifnet *ifp); static void zyd_update_promisc_cb(struct ifnet *ifp); static const struct zyd_phy_pair zyd_def_phy[] = ZYD_DEF_PHY; static const struct zyd_phy_pair zyd_def_phyB[] = ZYD_DEF_PHYB; /* various supported device vendors/products */ #define ZYD_ZD1211 0 #define ZYD_ZD1211B 1 static const struct usb2_device_id zyd_devs[] = { /* ZYD_ZD1211 */ {USB_VPI(USB_VENDOR_3COM2, USB_PRODUCT_3COM2_3CRUSB10075, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WL54, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL159G, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_CYBERTAN, USB_PRODUCT_CYBERTAN_TG54USB, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_DRAYTEK, USB_PRODUCT_DRAYTEK_VIGOR550, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54GD, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54GZL, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GWUS54GZ, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GWUS54MINI, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_XG760A, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_SENAO, USB_PRODUCT_SENAO_NUB8301, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_SWEEX, USB_PRODUCT_SWEEX_ZD1211, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_QUICKWLAN, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_ZD1211_1, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_ZD1211_2, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_TWINMOS, USB_PRODUCT_TWINMOS_G240, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_ALL0298V2, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UB_A, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UB, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_UR055G, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_ZD1211, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1211, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_AG225H, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_ZYAIRG220, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G200V2, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G202, ZYD_ZD1211)}, /* ZYD_ZD1211B */ {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SMCWUSBG, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_ZD1211B, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_A9T_WIFI, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050_V4000, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_ZD1211B, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSBF54G, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_FIBERLINE, USB_PRODUCT_FIBERLINE_WL430U, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54L, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_SNU5600, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GW_US54GXS, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_XG76NA, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_ZD1211B, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UBC1, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_USR, USB_PRODUCT_USR_USR5423, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_VTECH, USB_PRODUCT_VTECH_ZD1211B, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_ZD1211B, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1211B, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_M202, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G220V2, ZYD_ZD1211B)}, }; static const struct usb2_config zyd_config[ZYD_N_TRANSFER] = { [ZYD_TR_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = ZYD_MAX_TXBUFSZ, .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .mh.callback = &zyd_bulk_write_callback, .ep_index = 0, .mh.timeout = 10000, /* 10 seconds */ }, [ZYD_TR_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = ZYX_MAX_RXBUFSZ, .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &zyd_bulk_read_callback, .ep_index = 0, }, [ZYD_TR_BULK_CS_WR] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &zyd_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [ZYD_TR_BULK_CS_RD] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &zyd_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [ZYD_TR_INTR_DT_WR] = { .type = UE_BULK_INTR, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = sizeof(struct zyd_cmd), .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .mh.callback = &zyd_intr_write_callback, .mh.timeout = 1000, /* 1 second */ .ep_index = 1, }, [ZYD_TR_INTR_DT_RD] = { .type = UE_BULK_INTR, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = sizeof(struct zyd_cmd), .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &zyd_intr_read_callback, .ep_index = 1, }, [ZYD_TR_INTR_CS_WR] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &zyd_intr_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [ZYD_TR_INTR_CS_RD] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &zyd_intr_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static devclass_t zyd_devclass; static device_method_t zyd_methods[] = { DEVMETHOD(device_probe, zyd_probe), DEVMETHOD(device_attach, zyd_attach), DEVMETHOD(device_detach, zyd_detach), {0, 0} }; static driver_t zyd_driver = { .name = "zyd", .methods = zyd_methods, .size = sizeof(struct zyd_softc), }; DRIVER_MODULE(zyd, ushub, zyd_driver, zyd_devclass, NULL, 0); MODULE_DEPEND(zyd, usb2_wlan, 1, 1, 1); MODULE_DEPEND(zyd, usb2_core, 1, 1, 1); MODULE_DEPEND(zyd, wlan, 1, 1, 1); MODULE_DEPEND(zyd, wlan_amrr, 1, 1, 1); static uint8_t zyd_plcp2ieee(uint8_t signal, uint8_t isofdm) { if (isofdm) { static const uint8_t ofdmrates[16] = {0, 0, 0, 0, 0, 0, 0, 96, 48, 24, 12, 108, 72, 36, 18}; return ofdmrates[signal & 0xf]; } else { static const uint8_t cckrates[16] = {0, 0, 0, 0, 4, 0, 0, 11, 0, 0, 2, 0, 0, 0, 22, 0}; return cckrates[signal & 0xf]; } } /* * USB request basic wrapper */ static void zyd_cfg_usbrequest(struct zyd_softc *sc, struct usb2_device_request *req, uint8_t *data) { usb2_error_t err; uint16_t length; if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto error; } err = usb2_do_request_flags (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); if (err) { DPRINTFN(0, "%s: device request failed, err=%s " "(ignored)\n", sc->sc_name, usb2_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } return; } static void zyd_intr_read_clear_stall_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[ZYD_TR_INTR_DT_RD]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~ZYD_FLAG_INTR_READ_STALL; usb2_transfer_start(xfer_other); } return; } /* * Callback handler for interrupt transfer */ static void zyd_intr_read_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct zyd_cmd *cmd = &sc->sc_intr_ibuf; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: actlen = xfer->actlen; DPRINTFN(3, "length=%d\n", actlen); if (actlen > sizeof(sc->sc_intr_ibuf)) { actlen = sizeof(sc->sc_intr_ibuf); } usb2_copy_out(xfer->frbuffers, 0, &sc->sc_intr_ibuf, actlen); switch (le16toh(cmd->code)) { case ZYD_NOTIF_RETRYSTATUS: goto handle_notif_retrystatus; case ZYD_NOTIF_IORD: goto handle_notif_iord; default: DPRINTFN(2, "unknown indication: 0x%04x\n", le16toh(cmd->code)); } /* fallthrough */ case USB_ST_SETUP: tr_setup: if (sc->sc_flags & ZYD_FLAG_INTR_READ_STALL) { usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_RD]); break; } xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); break; default: /* Error */ DPRINTFN(3, "error = %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ZYD_FLAG_INTR_READ_STALL; usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_RD]); } break; } return; handle_notif_retrystatus:{ struct zyd_notif_retry *retry = (void *)(cmd->data); struct ifnet *ifp = sc->sc_ifp; struct ieee80211vap *vap; struct ieee80211_node *ni; DPRINTF("retry intr: rate=0x%x " "addr=%02x:%02x:%02x:%02x:%02x:%02x count=%d (0x%x)\n", le16toh(retry->rate), retry->macaddr[0], retry->macaddr[1], retry->macaddr[2], retry->macaddr[3], retry->macaddr[4], retry->macaddr[5], le16toh(retry->count) & 0xff, le16toh(retry->count)); vap = zyd_get_vap(sc); if ((vap != NULL) && (sc->sc_amrr_timer)) { /* * Find the node to which the packet was sent * and update its retry statistics. In BSS * mode, this node is the AP we're associated * to so no lookup is actually needed. */ ni = ieee80211_find_txnode(vap, retry->macaddr); if (ni != NULL) { ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn, IEEE80211_AMRR_FAILURE, 1); ieee80211_free_node(ni); } } if (retry->count & htole16(0x100)) { ifp->if_oerrors++; /* too many retries */ } goto tr_setup; } handle_notif_iord: if (*(uint16_t *)cmd->data == htole16(ZYD_CR_INTERRUPT)) { goto tr_setup; /* HMAC interrupt */ } if (actlen < 4) { DPRINTFN(0, "too short, %u bytes\n", actlen); goto tr_setup; /* too short */ } actlen -= 4; sc->sc_intr_ilen = actlen; if (sc->sc_intr_iwakeup) { sc->sc_intr_iwakeup = 0; usb2_cv_signal(&sc->sc_intr_cv); } else { sc->sc_intr_iwakeup = 1; } /* * We pause reading data from the interrupt endpoint until the * data has been picked up! */ return; } /* * Interrupt call reply transfer, read */ static void zyd_cfg_usb2_intr_read(struct zyd_softc *sc, void *data, uint32_t size) { uint16_t actlen; uint16_t x; if (size > sizeof(sc->sc_intr_ibuf.data)) { DPRINTFN(0, "truncating transfer size!\n"); size = sizeof(sc->sc_intr_ibuf.data); } if (usb2_config_td_is_gone(&sc->sc_config_td)) { bzero(data, size); goto done; } if (sc->sc_intr_iwakeup) { DPRINTF("got data already!\n"); sc->sc_intr_iwakeup = 0; goto skip0; } repeat: sc->sc_intr_iwakeup = 1; while (sc->sc_intr_iwakeup) { /* wait for data */ usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_RD]); if (usb2_cv_timedwait(&sc->sc_intr_cv, &sc->sc_mtx, hz / 2)) { /* should not happen */ } if (usb2_config_td_is_gone(&sc->sc_config_td)) { bzero(data, size); goto done; } } skip0: if (size != sc->sc_intr_ilen) { DPRINTFN(0, "unexpected length %u != %u\n", size, sc->sc_intr_ilen); goto repeat; } actlen = sc->sc_intr_ilen; actlen /= 4; /* verify register values */ for (x = 0; x != actlen; x++) { if (sc->sc_intr_obuf.data[(2 * x)] != sc->sc_intr_ibuf.data[(4 * x)]) { /* invalid register */ DPRINTFN(0, "Invalid register (1) at %u!\n", x); goto repeat; } if (sc->sc_intr_obuf.data[(2 * x) + 1] != sc->sc_intr_ibuf.data[(4 * x) + 1]) { /* invalid register */ DPRINTFN(0, "Invalid register (2) at %u!\n", x); goto repeat; } } bcopy(sc->sc_intr_ibuf.data, data, size); /* * We have fetched the data from the shared buffer and it is * safe to restart the interrupt transfer! */ usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_RD]); done: return; } static void zyd_intr_write_clear_stall_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[ZYD_TR_INTR_DT_WR]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~ZYD_FLAG_INTR_WRITE_STALL; usb2_transfer_start(xfer_other); } return; } static void zyd_intr_write_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(3, "length=%d\n", xfer->actlen); goto wakeup; case USB_ST_SETUP: if (sc->sc_flags & ZYD_FLAG_INTR_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_WR]); goto wakeup; } if (sc->sc_intr_owakeup) { usb2_copy_in(xfer->frbuffers, 0, &sc->sc_intr_obuf, sc->sc_intr_olen); xfer->frlengths[0] = sc->sc_intr_olen; usb2_start_hardware(xfer); } break; default: /* Error */ DPRINTFN(3, "error = %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ZYD_FLAG_INTR_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_WR]); } goto wakeup; } return; wakeup: if (sc->sc_intr_owakeup) { sc->sc_intr_owakeup = 0; usb2_cv_signal(&sc->sc_intr_cv); } return; } /* * Interrupt transfer, write. * * Not always an "interrupt transfer". If operating in * full speed mode, EP4 is bulk out, not interrupt out. */ static void zyd_cfg_usb2_intr_write(struct zyd_softc *sc, const void *data, uint16_t code, uint32_t size) { if (size > sizeof(sc->sc_intr_obuf.data)) { DPRINTFN(0, "truncating transfer size!\n"); size = sizeof(sc->sc_intr_obuf.data); } if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto done; } sc->sc_intr_olen = size + 2; sc->sc_intr_owakeup = 1; sc->sc_intr_obuf.code = htole16(code); bcopy(data, sc->sc_intr_obuf.data, size); usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_WR]); while (sc->sc_intr_owakeup) { if (usb2_cv_timedwait(&sc->sc_intr_cv, &sc->sc_mtx, hz / 2)) { /* should not happen */ } if (usb2_config_td_is_gone(&sc->sc_config_td)) { sc->sc_intr_owakeup = 0; goto done; } } done: return; } static void zyd_cfg_cmd(struct zyd_softc *sc, uint16_t code, const void *idata, uint16_t ilen, void *odata, uint16_t olen, uint16_t flags) { zyd_cfg_usb2_intr_write(sc, idata, code, ilen); if (flags & ZYD_CMD_FLAG_READ) { zyd_cfg_usb2_intr_read(sc, odata, olen); } return; } static void zyd_cfg_read16(struct zyd_softc *sc, uint16_t addr, uint16_t *value) { struct zyd_pair tmp[1]; addr = htole16(addr); zyd_cfg_cmd(sc, ZYD_CMD_IORD, &addr, sizeof(addr), tmp, sizeof(tmp), ZYD_CMD_FLAG_READ); *value = le16toh(tmp[0].val); return; } static void zyd_cfg_read32(struct zyd_softc *sc, uint16_t addr, uint32_t *value) { struct zyd_pair tmp[2]; uint16_t regs[2]; regs[0] = ZYD_REG32_HI(addr); regs[1] = ZYD_REG32_LO(addr); regs[0] = htole16(regs[0]); regs[1] = htole16(regs[1]); zyd_cfg_cmd(sc, ZYD_CMD_IORD, regs, sizeof(regs), tmp, sizeof(tmp), ZYD_CMD_FLAG_READ); *value = (le16toh(tmp[0].val) << 16) | le16toh(tmp[1].val); return; } static void zyd_cfg_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val) { struct zyd_pair pair[1]; pair[0].reg = htole16(reg); pair[0].val = htole16(val); zyd_cfg_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0); return; } static void zyd_cfg_write32(struct zyd_softc *sc, uint16_t reg, uint32_t val) { struct zyd_pair pair[2]; pair[0].reg = htole16(ZYD_REG32_HI(reg)); pair[0].val = htole16(val >> 16); pair[1].reg = htole16(ZYD_REG32_LO(reg)); pair[1].val = htole16(val & 0xffff); zyd_cfg_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0); return; } /*------------------------------------------------------------------------* * zyd_cfg_rfwrite - write RF registers *------------------------------------------------------------------------*/ static void zyd_cfg_rfwrite(struct zyd_softc *sc, uint32_t value) { struct zyd_rf *rf = &sc->sc_rf; struct zyd_rfwrite req; uint16_t cr203; uint16_t i; zyd_cfg_read16(sc, ZYD_CR203, &cr203); cr203 &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA); req.code = htole16(2); req.width = htole16(rf->width); for (i = 0; i != rf->width; i++) { req.bit[i] = htole16(cr203); if (value & (1 << (rf->width - 1 - i))) req.bit[i] |= htole16(ZYD_RF_DATA); } zyd_cfg_cmd(sc, ZYD_CMD_RFCFG, &req, 4 + (2 * rf->width), NULL, 0, 0); return; } static void zyd_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[ZYD_TR_BULK_DT_RD]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~ZYD_FLAG_BULK_READ_STALL; usb2_transfer_start(xfer_other); } return; } static void zyd_bulk_read_callback_sub(struct usb2_xfer *xfer, struct zyd_ifq *mq, uint32_t offset, uint16_t len) { enum { ZYD_OVERHEAD = (ZYD_HW_PADDING + IEEE80211_CRC_LEN), }; struct zyd_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct zyd_plcphdr plcp; struct zyd_rx_stat stat; struct mbuf *m; if (len < ZYD_OVERHEAD) { DPRINTF("frame too " "short (length=%d)\n", len); ifp->if_ierrors++; return; } usb2_copy_out(xfer->frbuffers, offset, &plcp, sizeof(plcp)); usb2_copy_out(xfer->frbuffers, offset + len - sizeof(stat), &stat, sizeof(stat)); if (stat.flags & ZYD_RX_ERROR) { DPRINTF("RX status indicated " "error (0x%02x)\n", stat.flags); ifp->if_ierrors++; return; } /* compute actual frame length */ len -= ZYD_OVERHEAD; /* allocate a mbuf to store the frame */ if (len > MCLBYTES) { DPRINTF("too large frame, " "%u bytes\n", len); return; } else if (len > MHLEN) m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); else m = m_gethdr(M_DONTWAIT, MT_DATA); if (m == NULL) { DPRINTF("could not allocate rx mbuf\n"); ifp->if_ierrors++; return; } m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = len; m->m_len = len; usb2_copy_out(xfer->frbuffers, offset + sizeof(plcp), m->m_data, len); if (bpf_peers_present(ifp->if_bpf)) { struct zyd_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; if (stat.flags & (ZYD_RX_BADCRC16 | ZYD_RX_BADCRC32)) tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; /* XXX toss, no way to express errors */ if (stat.flags & ZYD_RX_DECRYPTERR) tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; tap->wr_rate = zyd_plcp2ieee(plcp.signal, stat.flags & ZYD_RX_OFDM); tap->wr_antsignal = stat.rssi + -95; tap->wr_antnoise = -95; /* XXX */ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } if (sizeof(m->m_hdr.pad) > 0) { m->m_hdr.pad[0] = stat.rssi; /* XXX hack */ } _IF_ENQUEUE(mq, m); return; } static void zyd_bulk_read_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni; struct zyd_rx_desc rx_desc; struct zyd_ifq mq = {NULL, NULL, 0}; struct mbuf *m; uint32_t offset; uint16_t len16; uint8_t x; uint8_t rssi; int8_t nf; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (xfer->actlen < MAX(sizeof(rx_desc), ZYD_MIN_FRAGSZ)) { DPRINTFN(0, "xfer too short, %d bytes\n", xfer->actlen); ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, xfer->actlen - sizeof(rx_desc), &rx_desc, sizeof(rx_desc)); if (UGETW(rx_desc.tag) == ZYD_TAG_MULTIFRAME) { offset = 0; DPRINTFN(4, "received multi-frame transfer, " "%u bytes\n", xfer->actlen); for (x = 0; x < ZYD_MAX_RXFRAMECNT; x++) { len16 = UGETW(rx_desc.len[x]); if ((len16 == 0) || (len16 > xfer->actlen)) { break; } zyd_bulk_read_callback_sub(xfer, &mq, offset, len16); /* * next frame is aligned on a 32-bit * boundary */ len16 = (len16 + 3) & ~3; offset += len16; if (len16 > xfer->actlen) { break; } xfer->actlen -= len16; } } else { DPRINTFN(4, "received single-frame transfer, " "%u bytes\n", xfer->actlen); zyd_bulk_read_callback_sub(xfer, &mq, 0, xfer->actlen); } case USB_ST_SETUP: tr_setup: DPRINTF("setup\n"); if (sc->sc_flags & ZYD_FLAG_BULK_READ_STALL) { usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_RD]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "ieee80211_input" here, and not some lines up! */ if (mq.ifq_head) { mtx_unlock(&sc->sc_mtx); while (1) { _IF_DEQUEUE(&mq, m); if (m == NULL) break; rssi = m->m_hdr.pad[0]; /* XXX hack */ rssi = (rssi > 63) ? 127 : 2 * rssi; nf = -95; /* XXX */ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { if (ieee80211_input(ni, m, rssi, nf, 0)) { /* ignore */ } ieee80211_free_node(ni); } else { if (ieee80211_input_all(ic, m, rssi, nf, 0)) { /* ignore */ } } } mtx_lock(&sc->sc_mtx); } break; default: /* Error */ DPRINTF("frame error: %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ZYD_FLAG_BULK_READ_STALL; usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_RD]); } break; } return; } /*------------------------------------------------------------------------* * zyd_cfg_uploadfirmware * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static uint8_t zyd_cfg_uploadfirmware(struct zyd_softc *sc, const uint8_t *fw_ptr, uint32_t fw_len) { struct usb2_device_request req; uint16_t temp; uint16_t addr; uint8_t stat; DPRINTF("firmware %p size=%u\n", fw_ptr, fw_len); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = ZYD_DOWNLOADREQ; USETW(req.wIndex, 0); temp = 64; addr = ZYD_FIRMWARE_START_ADDR; while (fw_len > 0) { if (fw_len < 64) { temp = fw_len; } DPRINTF("firmware block: fw_len=%u\n", fw_len); USETW(req.wValue, addr); USETW(req.wLength, temp); zyd_cfg_usbrequest(sc, &req, USB_ADD_BYTES(fw_ptr, 0)); addr += (temp / 2); fw_len -= temp; fw_ptr += temp; } /* check whether the upload succeeded */ req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = ZYD_DOWNLOADSTS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(stat)); zyd_cfg_usbrequest(sc, &req, &stat); return ((stat & 0x80) ? 1 : 0); } /* * Driver OS interface */ /* * Probe for a ZD1211-containing product */ static int zyd_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != 0) { return (ENXIO); } if (uaa->info.bIfaceIndex != ZYD_IFACE_INDEX) { return (ENXIO); } return (usb2_lookup_id_by_uaa(zyd_devs, sizeof(zyd_devs), uaa)); } /* * Attach the interface. Allocate softc structures, do * setup and ethernet/BPF attach. */ static int zyd_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct zyd_softc *sc = device_get_softc(dev); int error; uint8_t iface_index; if (sc == NULL) { return (ENOMEM); } if (uaa->info.bcdDevice < 0x4330) { device_printf(dev, "device version mismatch: 0x%X " "(only >= 43.30 supported)\n", uaa->info.bcdDevice); return (EINVAL); } device_set_usb2_desc(dev); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); sc->sc_unit = device_get_unit(dev); sc->sc_udev = uaa->device; sc->sc_mac_rev = USB_GET_DRIVER_INFO(uaa); mtx_init(&sc->sc_mtx, "zyd lock", MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); usb2_cv_init(&sc->sc_intr_cv, "IWAIT"); usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); /* * Endpoint 1 = Bulk out (512b @ high speed / 64b @ full speed) * Endpoint 2 = Bulk in (512b @ high speed / 64b @ full speed) * Endpoint 3 = Intr in (64b) * Endpoint 4 = Intr out @ high speed / bulk out @ full speed (64b) */ iface_index = ZYD_IFACE_INDEX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, zyd_config, ZYD_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "could not allocate USB " "transfers: %s\n", usb2_errstr(error)); goto detach; } error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, &zyd_end_of_commands, sizeof(struct usb2_config_td_cc), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&sc->sc_mtx); /* start setup */ usb2_config_td_queue_command (&sc->sc_config_td, NULL, &zyd_cfg_first_time_setup, 0, 0); /* start watchdog (will exit mutex) */ zyd_watchdog(sc); return (0); detach: zyd_detach(dev); return (ENXIO); } /* * Lock PHY registers */ static void zyd_cfg_lock_phy(struct zyd_softc *sc) { uint32_t temp; zyd_cfg_read32(sc, ZYD_MAC_MISC, &temp); temp &= ~ZYD_UNLOCK_PHY_REGS; zyd_cfg_write32(sc, ZYD_MAC_MISC, temp); } /* * Unlock PHY registers */ static void zyd_cfg_unlock_phy(struct zyd_softc *sc) { uint32_t temp; zyd_cfg_read32(sc, ZYD_MAC_MISC, &temp); temp |= ZYD_UNLOCK_PHY_REGS; zyd_cfg_write32(sc, ZYD_MAC_MISC, temp); } static void zyd_cfg_set_beacon_interval(struct zyd_softc *sc, uint32_t bintval) { /* XXX this is probably broken.. */ zyd_cfg_write32(sc, ZYD_CR_ATIM_WND_PERIOD, bintval - 2); zyd_cfg_write32(sc, ZYD_CR_PRE_TBTT, bintval - 1); zyd_cfg_write32(sc, ZYD_CR_BCN_INTERVAL, bintval); return; } /* * Get RF name */ static const char * zyd_rf_name(uint8_t type) { static const char *const zyd_rfs[] = { "unknown", "unknown", "UW2451", "UCHIP", "AL2230", "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT", "PV2000", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2", "PHILIPS" }; return (zyd_rfs[(type > 15) ? 0 : type]); } /* * RF driver: Init for RFMD chip */ static void zyd_cfg_rf_rfmd_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_RFMD_PHY; static const uint32_t rfini[] = ZYD_RFMD_RF; uint32_t i; /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } /* init RFMD radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } return; } /* * RF driver: Switch radio on/off for RFMD chip */ static void zyd_cfg_rf_rfmd_switch_radio(struct zyd_softc *sc, uint8_t on) { zyd_cfg_write16(sc, ZYD_CR10, on ? 0x89 : 0x15); zyd_cfg_write16(sc, ZYD_CR11, on ? 0x00 : 0x81); return; } /* * RF driver: Channel setting for RFMD chip */ static void zyd_cfg_rf_rfmd_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const struct { uint32_t r1, r2; } rfprog[] = ZYD_RFMD_CHANTABLE; zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); return; } /* * RF driver: Switch radio on/off for AL2230 chip */ static void zyd_cfg_rf_al2230_switch_radio(struct zyd_softc *sc, uint8_t on) { uint8_t on251 = (sc->sc_mac_rev == ZYD_ZD1211) ? 0x3f : 0x7f; zyd_cfg_write16(sc, ZYD_CR11, on ? 0x00 : 0x04); zyd_cfg_write16(sc, ZYD_CR251, on ? on251 : 0x2f); return; } /* * RF driver: Init for AL2230 chip */ static void zyd_cfg_rf_al2230_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY; static const uint32_t rfini[] = ZYD_AL2230_RF; uint32_t i; /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } /* init AL2230 radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } return; } static void zyd_cfg_rf_al2230_init_b(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY_B; static const uint32_t rfini[] = ZYD_AL2230_RF_B; uint32_t i; /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } /* init AL2230 radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } return; } /* * RF driver: Channel setting for AL2230 chip */ static void zyd_cfg_rf_al2230_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const struct { uint32_t r1, r2, r3; } rfprog[] = ZYD_AL2230_CHANTABLE; zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r3); zyd_cfg_write16(sc, ZYD_CR138, 0x28); zyd_cfg_write16(sc, ZYD_CR203, 0x06); return; } /* * AL7230B RF methods. */ static void zyd_cfg_rf_al7230b_switch_radio(struct zyd_softc *sc, uint8_t on) { zyd_cfg_write16(sc, ZYD_CR11, on ? 0x00 : 0x04); zyd_cfg_write16(sc, ZYD_CR251, on ? 0x3f : 0x2f); return; } static void zyd_cfg_rf_al7230b_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini_1[] = ZYD_AL7230B_PHY_1; static const struct zyd_phy_pair phyini_2[] = ZYD_AL7230B_PHY_2; static const struct zyd_phy_pair phyini_3[] = ZYD_AL7230B_PHY_3; static const uint32_t rfini_1[] = ZYD_AL7230B_RF_1; static const uint32_t rfini_2[] = ZYD_AL7230B_RF_2; uint32_t i; /* for AL7230B, PHY and RF need to be initialized in "phases" */ /* init RF-dependent PHY registers, part one */ for (i = 0; i != INDEXES(phyini_1); i++) { zyd_cfg_write16(sc, phyini_1[i].reg, phyini_1[i].val); } /* init AL7230B radio, part one */ for (i = 0; i != INDEXES(rfini_1); i++) { zyd_cfg_rfwrite(sc, rfini_1[i]); } /* init RF-dependent PHY registers, part two */ for (i = 0; i != INDEXES(phyini_2); i++) { zyd_cfg_write16(sc, phyini_2[i].reg, phyini_2[i].val); } /* init AL7230B radio, part two */ for (i = 0; i != INDEXES(rfini_2); i++) { zyd_cfg_rfwrite(sc, rfini_2[i]); } /* init RF-dependent PHY registers, part three */ for (i = 0; i != INDEXES(phyini_3); i++) { zyd_cfg_write16(sc, phyini_3[i].reg, phyini_3[i].val); } return; } static void zyd_cfg_rf_al7230b_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const struct { uint32_t r1, r2; } rfprog[] = ZYD_AL7230B_CHANTABLE; static const uint32_t rfsc[] = ZYD_AL7230B_RF_SETCHANNEL; uint32_t i; zyd_cfg_write16(sc, ZYD_CR240, 0x57); zyd_cfg_write16(sc, ZYD_CR251, 0x2f); for (i = 0; i != INDEXES(rfsc); i++) { zyd_cfg_rfwrite(sc, rfsc[i]); } zyd_cfg_write16(sc, ZYD_CR128, 0x14); zyd_cfg_write16(sc, ZYD_CR129, 0x12); zyd_cfg_write16(sc, ZYD_CR130, 0x10); zyd_cfg_write16(sc, ZYD_CR38, 0x38); zyd_cfg_write16(sc, ZYD_CR136, 0xdf); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); zyd_cfg_rfwrite(sc, 0x3c9000); zyd_cfg_write16(sc, ZYD_CR251, 0x3f); zyd_cfg_write16(sc, ZYD_CR203, 0x06); zyd_cfg_write16(sc, ZYD_CR240, 0x08); return; } /* * AL2210 RF methods. */ static void zyd_cfg_rf_al2210_switch_radio(struct zyd_softc *sc, uint8_t on) { } static void zyd_cfg_rf_al2210_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_AL2210_PHY; static const uint32_t rfini[] = ZYD_AL2210_RF; uint32_t tmp; uint32_t i; zyd_cfg_write32(sc, ZYD_CR18, 2); /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } /* init AL2210 radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } zyd_cfg_write16(sc, ZYD_CR47, 0x1e); zyd_cfg_read32(sc, ZYD_CR_RADIO_PD, &tmp); zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp & ~1); zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp | 1); zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x05); zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x00); zyd_cfg_write16(sc, ZYD_CR47, 0x1e); zyd_cfg_write32(sc, ZYD_CR18, 3); return; } static void zyd_cfg_rf_al2210_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const uint32_t rfprog[] = ZYD_AL2210_CHANTABLE; uint32_t tmp; zyd_cfg_write32(sc, ZYD_CR18, 2); zyd_cfg_write16(sc, ZYD_CR47, 0x1e); zyd_cfg_read32(sc, ZYD_CR_RADIO_PD, &tmp); zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp & ~1); zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp | 1); zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x05); zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x00); zyd_cfg_write16(sc, ZYD_CR47, 0x1e); /* actually set the channel */ zyd_cfg_rfwrite(sc, rfprog[channel - 1]); zyd_cfg_write32(sc, ZYD_CR18, 3); return; } /* * GCT RF methods. */ static void zyd_cfg_rf_gct_switch_radio(struct zyd_softc *sc, uint8_t on) { /* vendor driver does nothing for this RF chip */ return; } static void zyd_cfg_rf_gct_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_GCT_PHY; static const uint32_t rfini[] = ZYD_GCT_RF; uint32_t i; /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } /* init cgt radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } return; } static void zyd_cfg_rf_gct_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const uint32_t rfprog[] = ZYD_GCT_CHANTABLE; zyd_cfg_rfwrite(sc, 0x1c0000); zyd_cfg_rfwrite(sc, rfprog[channel - 1]); zyd_cfg_rfwrite(sc, 0x1c0008); return; } /* * Maxim RF methods. */ static void zyd_cfg_rf_maxim_switch_radio(struct zyd_softc *sc, uint8_t on) { /* vendor driver does nothing for this RF chip */ return; } static void zyd_cfg_rf_maxim_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY; static const uint32_t rfini[] = ZYD_MAXIM_RF; uint16_t tmp; uint32_t i; /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp & ~(1 << 4)); /* init maxim radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp | (1 << 4)); return; } static void zyd_cfg_rf_maxim_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY; static const uint32_t rfini[] = ZYD_MAXIM_RF; static const struct { uint32_t r1, r2; } rfprog[] = ZYD_MAXIM_CHANTABLE; uint16_t tmp; uint32_t i; /* * Do the same as we do when initializing it, except for the channel * values coming from the two channel tables. */ /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp & ~(1 << 4)); /* first two values taken from the chantables */ zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); /* init maxim radio - skipping the two first values */ if (INDEXES(rfini) > 2) { for (i = 2; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp | (1 << 4)); return; } /* * Maxim2 RF methods. */ static void zyd_cfg_rf_maxim2_switch_radio(struct zyd_softc *sc, uint8_t on) { /* vendor driver does nothing for this RF chip */ return; } static void zyd_cfg_rf_maxim2_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; static const uint32_t rfini[] = ZYD_MAXIM2_RF; uint16_t tmp; uint32_t i; /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp & ~(1 << 4)); /* init maxim2 radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp | (1 << 4)); return; } static void zyd_cfg_rf_maxim2_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; static const uint32_t rfini[] = ZYD_MAXIM2_RF; static const struct { uint32_t r1, r2; } rfprog[] = ZYD_MAXIM2_CHANTABLE; uint16_t tmp; uint32_t i; /* * Do the same as we do when initializing it, except for the channel * values coming from the two channel tables. */ /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp & ~(1 << 4)); /* first two values taken from the chantables */ zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); /* init maxim2 radio - skipping the two first values */ if (INDEXES(rfini) > 2) { for (i = 2; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp | (1 << 4)); return; } /* * Assign drivers and init the RF */ static uint8_t zyd_cfg_rf_init_hw(struct zyd_softc *sc, struct zyd_rf *rf) { ; /* fix for indent */ switch (sc->sc_rf_rev) { case ZYD_RF_RFMD: rf->cfg_init_hw = zyd_cfg_rf_rfmd_init; rf->cfg_switch_radio = zyd_cfg_rf_rfmd_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_rfmd_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL2230: if (sc->sc_mac_rev == ZYD_ZD1211B) rf->cfg_init_hw = zyd_cfg_rf_al2230_init_b; else rf->cfg_init_hw = zyd_cfg_rf_al2230_init; rf->cfg_switch_radio = zyd_cfg_rf_al2230_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_al2230_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL7230B: rf->cfg_init_hw = zyd_cfg_rf_al7230b_init; rf->cfg_switch_radio = zyd_cfg_rf_al7230b_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_al7230b_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL2210: rf->cfg_init_hw = zyd_cfg_rf_al2210_init; rf->cfg_switch_radio = zyd_cfg_rf_al2210_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_al2210_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_GCT: rf->cfg_init_hw = zyd_cfg_rf_gct_init; rf->cfg_switch_radio = zyd_cfg_rf_gct_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_gct_set_channel; rf->width = 21; /* 21-bit RF values */ break; case ZYD_RF_MAXIM_NEW: rf->cfg_init_hw = zyd_cfg_rf_maxim_init; rf->cfg_switch_radio = zyd_cfg_rf_maxim_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_maxim_set_channel; rf->width = 18; /* 18-bit RF values */ break; case ZYD_RF_MAXIM_NEW2: rf->cfg_init_hw = zyd_cfg_rf_maxim2_init; rf->cfg_switch_radio = zyd_cfg_rf_maxim2_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_maxim2_set_channel; rf->width = 18; /* 18-bit RF values */ break; default: DPRINTFN(0, "%s: Sorry, radio %s is not supported yet\n", sc->sc_name, zyd_rf_name(sc->sc_rf_rev)); return (1); } zyd_cfg_lock_phy(sc); (rf->cfg_init_hw) (sc, rf); zyd_cfg_unlock_phy(sc); return (0); /* success */ } /* * Init the hardware */ static uint8_t zyd_cfg_hw_init(struct zyd_softc *sc) { const struct zyd_phy_pair *phyp; uint32_t tmp; /* specify that the plug and play is finished */ zyd_cfg_write32(sc, ZYD_MAC_AFTER_PNP, 1); zyd_cfg_read16(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->sc_firmware_base); DPRINTF("firmware base address=0x%04x\n", sc->sc_firmware_base); /* retrieve firmware revision number */ zyd_cfg_read16(sc, sc->sc_firmware_base + ZYD_FW_FIRMWARE_REV, &sc->sc_fw_rev); zyd_cfg_write32(sc, ZYD_CR_GPI_EN, 0); zyd_cfg_write32(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f); /* disable interrupts */ zyd_cfg_write32(sc, ZYD_CR_INTERRUPT, 0); /* PHY init */ zyd_cfg_lock_phy(sc); phyp = (sc->sc_mac_rev == ZYD_ZD1211B) ? zyd_def_phyB : zyd_def_phy; for (; phyp->reg != 0; phyp++) { zyd_cfg_write16(sc, phyp->reg, phyp->val); } if (sc->sc_fix_cr157) { zyd_cfg_read32(sc, ZYD_EEPROM_PHY_REG, &tmp); zyd_cfg_write32(sc, ZYD_CR157, tmp >> 8); } zyd_cfg_unlock_phy(sc); /* HMAC init */ zyd_cfg_write32(sc, ZYD_MAC_ACK_EXT, 0x00000020); zyd_cfg_write32(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808); if (sc->sc_mac_rev == ZYD_ZD1211) { zyd_cfg_write32(sc, ZYD_MAC_RETRY, 0x00000002); } else { zyd_cfg_write32(sc, ZYD_MACB_MAX_RETRY, 0x02020202); zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f); zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f); zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f); zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f); zyd_cfg_write32(sc, ZYD_MACB_AIFS_CTL1, 0x00280028); zyd_cfg_write32(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C); zyd_cfg_write32(sc, ZYD_MACB_TXOP, 0x01800824); } zyd_cfg_write32(sc, ZYD_MAC_SNIFFER, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_RXFILTER, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_GHTBL, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_GHTBH, 0x80000000); zyd_cfg_write32(sc, ZYD_MAC_MISC, 0x000000a4); zyd_cfg_write32(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f); zyd_cfg_write32(sc, ZYD_MAC_BCNCFG, 0x00f00401); zyd_cfg_write32(sc, ZYD_MAC_PHY_DELAY2, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_ACK_EXT, 0x00000080); zyd_cfg_write32(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100); zyd_cfg_write32(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0547c032); zyd_cfg_write32(sc, ZYD_CR_RX_PE_DELAY, 0x00000070); zyd_cfg_write32(sc, ZYD_CR_PS_CTRL, 0x10000000); zyd_cfg_write32(sc, ZYD_MAC_RTSCTSRATE, 0x02030203); zyd_cfg_write32(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640); zyd_cfg_write32(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114); /* init beacon interval to 100ms */ zyd_cfg_set_beacon_interval(sc, 100); return (0); /* success */ } /* * Read information from EEPROM */ static void zyd_cfg_read_eeprom(struct zyd_softc *sc) { uint32_t tmp; uint16_t i; uint16_t val; /* read MAC address */ zyd_cfg_read32(sc, ZYD_EEPROM_MAC_ADDR_P1, &tmp); sc->sc_myaddr[0] = tmp & 0xff; sc->sc_myaddr[1] = tmp >> 8; sc->sc_myaddr[2] = tmp >> 16; sc->sc_myaddr[3] = tmp >> 24; zyd_cfg_read32(sc, ZYD_EEPROM_MAC_ADDR_P2, &tmp); sc->sc_myaddr[4] = tmp & 0xff; sc->sc_myaddr[5] = tmp >> 8; zyd_cfg_read32(sc, ZYD_EEPROM_POD, &tmp); sc->sc_rf_rev = tmp & 0x0f; sc->sc_fix_cr47 = (tmp >> 8) & 0x01; sc->sc_fix_cr157 = (tmp >> 13) & 0x01; sc->sc_pa_rev = (tmp >> 16) & 0x0f; /* read regulatory domain (currently unused) */ zyd_cfg_read32(sc, ZYD_EEPROM_SUBID, &tmp); sc->sc_regdomain = tmp >> 16; DPRINTF("regulatory domain %x\n", sc->sc_regdomain); /* read Tx power calibration tables */ for (i = 0; i < 7; i++) { zyd_cfg_read16(sc, ZYD_EEPROM_PWR_CAL + i, &val); sc->sc_pwr_cal[(i * 2)] = val >> 8; sc->sc_pwr_cal[(i * 2) + 1] = val & 0xff; zyd_cfg_read16(sc, ZYD_EEPROM_PWR_INT + i, &val); sc->sc_pwr_int[(i * 2)] = val >> 8; sc->sc_pwr_int[(i * 2) + 1] = val & 0xff; zyd_cfg_read16(sc, ZYD_EEPROM_36M_CAL + i, &val); sc->sc_ofdm36_cal[(i * 2)] = val >> 8; sc->sc_ofdm36_cal[(i * 2) + 1] = val & 0xff; zyd_cfg_read16(sc, ZYD_EEPROM_48M_CAL + i, &val); sc->sc_ofdm48_cal[(i * 2)] = val >> 8; sc->sc_ofdm48_cal[(i * 2) + 1] = val & 0xff; zyd_cfg_read16(sc, ZYD_EEPROM_54M_CAL + i, &val); sc->sc_ofdm54_cal[(i * 2)] = val >> 8; sc->sc_ofdm54_cal[(i * 2) + 1] = val & 0xff; } return; } static void zyd_cfg_set_mac_addr(struct zyd_softc *sc, const uint8_t *addr) { uint32_t tmp; tmp = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; zyd_cfg_write32(sc, ZYD_MAC_MACADRL, tmp); tmp = (addr[5] << 8) | addr[4]; zyd_cfg_write32(sc, ZYD_MAC_MACADRH, tmp); return; } /* * Switch radio on/off */ static void zyd_cfg_switch_radio(struct zyd_softc *sc, uint8_t onoff) { zyd_cfg_lock_phy(sc); (sc->sc_rf.cfg_switch_radio) (sc, onoff); zyd_cfg_unlock_phy(sc); return; } /* * Set BSSID */ static void zyd_cfg_set_bssid(struct zyd_softc *sc, uint8_t *addr) { uint32_t tmp; tmp = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; zyd_cfg_write32(sc, ZYD_MAC_BSSADRL, tmp); tmp = (addr[5] << 8) | addr[4]; zyd_cfg_write32(sc, ZYD_MAC_BSSADRH, tmp); return; } /* * Complete the attach process */ static void zyd_cfg_first_time_setup(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct usb2_config_descriptor *cd; struct ieee80211com *ic; struct ifnet *ifp; const uint8_t *fw_ptr; uint32_t fw_len; uint8_t bands; usb2_error_t err; /* setup RX tap header */ sc->sc_rxtap_len = sizeof(sc->sc_rxtap); sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(ZYD_RX_RADIOTAP_PRESENT); /* setup TX tap header */ sc->sc_txtap_len = sizeof(sc->sc_txtap); sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(ZYD_TX_RADIOTAP_PRESENT); if (sc->sc_mac_rev == ZYD_ZD1211) { fw_ptr = zd1211_firmware; fw_len = sizeof(zd1211_firmware); } else { fw_ptr = zd1211b_firmware; fw_len = sizeof(zd1211b_firmware); } if (zyd_cfg_uploadfirmware(sc, fw_ptr, fw_len)) { DPRINTFN(0, "%s: could not " "upload firmware!\n", sc->sc_name); return; } cd = usb2_get_config_descriptor(sc->sc_udev); /* reset device */ err = usb2_req_set_config(sc->sc_udev, &sc->sc_mtx, cd->bConfigurationValue); if (err) { DPRINTF("reset failed (ignored)\n"); } /* Read MAC and other stuff rom EEPROM */ zyd_cfg_read_eeprom(sc); /* Init hardware */ if (zyd_cfg_hw_init(sc)) { DPRINTFN(0, "%s: HW init failed!\n", sc->sc_name); return; } /* Now init the RF chip */ if (zyd_cfg_rf_init_hw(sc, &sc->sc_rf)) { DPRINTFN(0, "%s: RF init failed!\n", sc->sc_name); return; } printf("%s: HMAC ZD1211%s, FW %02x.%02x, RF %s, PA %x, address %02x:%02x:%02x:%02x:%02x:%02x\n", sc->sc_name, (sc->sc_mac_rev == ZYD_ZD1211) ? "" : "B", sc->sc_fw_rev >> 8, sc->sc_fw_rev & 0xff, zyd_rf_name(sc->sc_rf_rev), sc->sc_pa_rev, sc->sc_myaddr[0], sc->sc_myaddr[1], sc->sc_myaddr[2], sc->sc_myaddr[3], sc->sc_myaddr[4], sc->sc_myaddr[5]); mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_IEEE80211); mtx_lock(&sc->sc_mtx); if (ifp == NULL) { DPRINTFN(0, "%s: could not if_alloc()!\n", sc->sc_name); goto done; } sc->sc_evilhack = ifp; sc->sc_ifp = ifp; ic = ifp->if_l2com; ifp->if_softc = sc; if_initname(ifp, "zyd", sc->sc_unit); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = &zyd_init_cb; ifp->if_ioctl = &zyd_ioctl_cb; ifp->if_start = &zyd_start_cb; ifp->if_watchdog = NULL; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); bcopy(sc->sc_myaddr, ic->ic_myaddr, sizeof(ic->ic_myaddr)); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; ic->ic_opmode = IEEE80211_M_STA; /* Set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* capable of bg scanning */ | IEEE80211_C_WPA /* 802.11i */ ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); ieee80211_init_channels(ic, NULL, &bands); mtx_unlock(&sc->sc_mtx); ieee80211_ifattach(ic); mtx_lock(&sc->sc_mtx); ic->ic_node_alloc = &zyd_node_alloc_cb; ic->ic_raw_xmit = &zyd_raw_xmit_cb; ic->ic_newassoc = &zyd_newassoc_cb; ic->ic_scan_start = &zyd_scan_start_cb; ic->ic_scan_end = &zyd_scan_end_cb; ic->ic_set_channel = &zyd_set_channel_cb; ic->ic_vap_create = &zyd_vap_create; ic->ic_vap_delete = &zyd_vap_delete; ic->ic_update_mcast = &zyd_update_mcast_cb; ic->ic_update_promisc = &zyd_update_promisc_cb; sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); mtx_unlock(&sc->sc_mtx); bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap)); mtx_lock(&sc->sc_mtx); if (bootverbose) { ieee80211_announce(ic); } usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_RD]); done: return; } /* * Detach device */ static int zyd_detach(device_t dev) { struct zyd_softc *sc = device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; usb2_config_td_drain(&sc->sc_config_td); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); zyd_cfg_pre_stop(sc, NULL, 0); ifp = sc->sc_ifp; ic = ifp->if_l2com; mtx_unlock(&sc->sc_mtx); /* stop all USB transfers first */ usb2_transfer_unsetup(sc->sc_xfer, ZYD_N_TRANSFER); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { bpfdetach(ifp); ieee80211_ifdetach(ic); if_free(ifp); } usb2_config_td_unsetup(&sc->sc_config_td); usb2_callout_drain(&sc->sc_watchdog); usb2_cv_destroy(&sc->sc_intr_cv); mtx_destroy(&sc->sc_mtx); return (0); } static void zyd_cfg_newstate(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct zyd_vap *uvp = ZYD_VAP(vap); enum ieee80211_state ostate; enum ieee80211_state nstate; int arg; ostate = vap->iv_state; nstate = sc->sc_ns_state; arg = sc->sc_ns_arg; switch (nstate) { case IEEE80211_S_INIT: break; case IEEE80211_S_RUN: zyd_cfg_set_run(sc, cc); break; default: break; } mtx_unlock(&sc->sc_mtx); IEEE80211_LOCK(ic); uvp->newstate(vap, nstate, arg); + /* XXX Broken! if (vap->iv_newstate_cb != NULL) vap->iv_newstate_cb(vap, nstate, arg); + */ IEEE80211_UNLOCK(ic); mtx_lock(&sc->sc_mtx); return; } static void zyd_cfg_set_run(struct zyd_softc *sc, struct usb2_config_td_cc *cc) { zyd_cfg_set_chan(sc, cc, 0); if (cc->ic_opmode != IEEE80211_M_MONITOR) { /* turn link LED on */ zyd_cfg_set_led(sc, ZYD_LED1, 1); /* make data LED blink upon Tx */ zyd_cfg_write32(sc, sc->sc_firmware_base + ZYD_FW_LINK_STATUS, 1); zyd_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); } if (cc->iv_bss.fixed_rate_none) { /* enable automatic rate adaptation */ zyd_cfg_amrr_start(sc); } return; } static int zyd_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct zyd_vap *uvp = ZYD_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct zyd_softc *sc = ic->ic_ifp->if_softc; DPRINTF("setting new state: %d\n", nstate); mtx_lock(&sc->sc_mtx); if (usb2_config_td_is_gone(&sc->sc_config_td)) { mtx_unlock(&sc->sc_mtx); /* Special case which happens at detach. */ if (nstate == IEEE80211_S_INIT) { (uvp->newstate) (vap, nstate, arg); } return (0); /* nothing to do */ } /* store next state */ sc->sc_ns_state = nstate; sc->sc_ns_arg = arg; /* stop timers */ sc->sc_amrr_timer = 0; /* * USB configuration can only be done from the USB configuration * thread: */ usb2_config_td_queue_command (&sc->sc_config_td, &zyd_config_copy, &zyd_cfg_newstate, 0, 0); mtx_unlock(&sc->sc_mtx); return EINPROGRESS; } static void zyd_cfg_update_promisc(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t low; uint32_t high; if ((cc->ic_opmode == IEEE80211_M_MONITOR) || (cc->if_flags & (IFF_ALLMULTI | IFF_PROMISC))) { low = 0xffffffff; high = 0xffffffff; } else { low = cc->zyd_multi_low; high = cc->zyd_multi_high; } /* reprogram multicast global hash table */ zyd_cfg_write32(sc, ZYD_MAC_GHTBL, low); zyd_cfg_write32(sc, ZYD_MAC_GHTBH, high); return; } /* * Rate-to-bit-converter (Field "rate" in zyd_controlsetformat) */ static uint8_t zyd_plcp_signal(uint8_t rate) { ; /* fix for indent */ switch (rate) { /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return (0x0); case 4: return (0x1); case 11: return (0x2); case 22: return (0x3); /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return (0xb); case 18: return (0xf); case 24: return (0xa); case 36: return (0xe); case 48: return (0x9); case 72: return (0xd); case 96: return (0x8); case 108: return (0xc); /* XXX unsupported/unknown rate */ default: return (0xff); } } static void zyd_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func) { struct zyd_softc *sc = ic->ic_ifp->if_softc; mtx_lock(&sc->sc_mtx); sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); usb2_config_td_queue_command (&sc->sc_config_td, &zyd_config_copy, func, 0, 0); mtx_unlock(&sc->sc_mtx); return; } static void zyd_scan_start_cb(struct ieee80211com *ic) { zyd_std_command(ic, &zyd_cfg_scan_start); return; } static void zyd_scan_end_cb(struct ieee80211com *ic) { zyd_std_command(ic, &zyd_cfg_scan_end); return; } static void zyd_set_channel_cb(struct ieee80211com *ic) { zyd_std_command(ic, &zyd_cfg_set_chan); return; } /*========================================================================* * configure sub-routines, zyd_cfg_xxx *========================================================================*/ static void zyd_cfg_scan_start(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { zyd_cfg_set_bssid(sc, cc->if_broadcastaddr); return; } static void zyd_cfg_scan_end(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { zyd_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); return; } static void zyd_cfg_set_chan(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t chan; uint32_t tmp; chan = cc->ic_curchan.chan_to_ieee; DPRINTF("Will try %d\n", chan); if ((chan == 0) || (chan == IEEE80211_CHAN_ANY)) { DPRINTF("0 or ANY, exiting\n"); return; } zyd_cfg_lock_phy(sc); (sc->sc_rf.cfg_set_channel) (sc, &sc->sc_rf, chan); /* update Tx power */ zyd_cfg_write16(sc, ZYD_CR31, sc->sc_pwr_int[chan - 1]); if (sc->sc_mac_rev == ZYD_ZD1211B) { zyd_cfg_write16(sc, ZYD_CR67, sc->sc_ofdm36_cal[chan - 1]); zyd_cfg_write16(sc, ZYD_CR66, sc->sc_ofdm48_cal[chan - 1]); zyd_cfg_write16(sc, ZYD_CR65, sc->sc_ofdm54_cal[chan - 1]); zyd_cfg_write16(sc, ZYD_CR68, sc->sc_pwr_cal[chan - 1]); zyd_cfg_write16(sc, ZYD_CR69, 0x28); zyd_cfg_write16(sc, ZYD_CR69, 0x2a); } if (sc->sc_fix_cr47) { /* set CCK baseband gain from EEPROM */ zyd_cfg_read32(sc, ZYD_EEPROM_PHY_REG, &tmp); zyd_cfg_write16(sc, ZYD_CR47, tmp & 0xff); } zyd_cfg_write32(sc, ZYD_CR_CONFIG_PHILIPS, 0); zyd_cfg_unlock_phy(sc); sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq = htole16(cc->ic_curchan.ic_freq); sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags = htole16(cc->ic_flags); return; } /* * Interface: init */ /* immediate configuration */ static void zyd_cfg_pre_init(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; zyd_cfg_pre_stop(sc, cc, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= ZYD_FLAG_HL_READY; IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); return; } /* delayed configuration */ static void zyd_cfg_init(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { zyd_cfg_stop(sc, cc, 0); /* Do initial setup */ zyd_cfg_set_mac_addr(sc, cc->ic_myaddr); zyd_cfg_write32(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER); /* promiscuous mode */ zyd_cfg_write32(sc, ZYD_MAC_SNIFFER, (cc->ic_opmode == IEEE80211_M_MONITOR) ? 1 : 0); /* multicast setup */ zyd_cfg_update_promisc(sc, cc, refcount); zyd_cfg_set_rxfilter(sc, cc, refcount); /* switch radio transmitter ON */ zyd_cfg_switch_radio(sc, 1); /* XXX wrong, can't set here */ /* set basic rates */ if (cc->ic_curmode == IEEE80211_MODE_11B) zyd_cfg_write32(sc, ZYD_MAC_BAS_RATE, 0x0003); else if (cc->ic_curmode == IEEE80211_MODE_11A) zyd_cfg_write32(sc, ZYD_MAC_BAS_RATE, 0x1500); else /* assumes 802.11b/g */ zyd_cfg_write32(sc, ZYD_MAC_BAS_RATE, 0x000f); /* set mandatory rates */ if (cc->ic_curmode == IEEE80211_MODE_11B) zyd_cfg_write32(sc, ZYD_MAC_MAN_RATE, 0x000f); else if (cc->ic_curmode == IEEE80211_MODE_11A) zyd_cfg_write32(sc, ZYD_MAC_MAN_RATE, 0x1500); else /* assumes 802.11b/g */ zyd_cfg_write32(sc, ZYD_MAC_MAN_RATE, 0x150f); /* set default BSS channel */ zyd_cfg_set_chan(sc, cc, 0); /* enable interrupts */ zyd_cfg_write32(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK); /* make sure that the transfers get started */ sc->sc_flags |= ( ZYD_FLAG_BULK_READ_STALL | ZYD_FLAG_BULK_WRITE_STALL | ZYD_FLAG_LL_READY); if ((sc->sc_flags & ZYD_FLAG_LL_READY) && (sc->sc_flags & ZYD_FLAG_HL_READY)) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* * start the USB transfers, if not already started: */ usb2_transfer_start(sc->sc_xfer[1]); usb2_transfer_start(sc->sc_xfer[0]); /* * start IEEE802.11 layer */ mtx_unlock(&sc->sc_mtx); ieee80211_start_all(ic); mtx_lock(&sc->sc_mtx); } return; } /* immediate configuration */ static void zyd_cfg_pre_stop(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if (cc) { /* copy the needed configuration */ zyd_config_copy(sc, cc, refcount); } if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } sc->sc_flags &= ~(ZYD_FLAG_HL_READY | ZYD_FLAG_LL_READY); /* * stop all the transfers, if not already stopped: */ usb2_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_DT_WR]); usb2_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_DT_RD]); usb2_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_CS_WR]); usb2_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_CS_RD]); /* clean up transmission */ zyd_tx_clean_queue(sc); return; } /* delayed configuration */ static void zyd_cfg_stop(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* switch radio transmitter OFF */ zyd_cfg_switch_radio(sc, 0); /* disable Rx */ zyd_cfg_write32(sc, ZYD_MAC_RXFILTER, 0); /* disable interrupts */ zyd_cfg_write32(sc, ZYD_CR_INTERRUPT, 0); return; } static void zyd_update_mcast_cb(struct ifnet *ifp) { struct zyd_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &zyd_config_copy, &zyd_cfg_update_promisc, 0, 0); mtx_unlock(&sc->sc_mtx); return; } static void zyd_update_promisc_cb(struct ifnet *ifp) { struct zyd_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &zyd_config_copy, &zyd_cfg_update_promisc, 0, 0); mtx_unlock(&sc->sc_mtx); return; } static void zyd_cfg_set_rxfilter(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t rxfilter; switch (cc->ic_opmode) { case IEEE80211_M_STA: rxfilter = ZYD_FILTER_BSS; break; case IEEE80211_M_IBSS: case IEEE80211_M_HOSTAP: rxfilter = ZYD_FILTER_HOSTAP; break; case IEEE80211_M_MONITOR: rxfilter = ZYD_FILTER_MONITOR; break; default: /* should not get there */ return; } zyd_cfg_write32(sc, ZYD_MAC_RXFILTER, rxfilter); return; } static void zyd_cfg_set_led(struct zyd_softc *sc, uint32_t which, uint8_t on) { uint32_t tmp; zyd_cfg_read32(sc, ZYD_MAC_TX_PE_CONTROL, &tmp); if (on) tmp |= which; else tmp &= ~which; zyd_cfg_write32(sc, ZYD_MAC_TX_PE_CONTROL, tmp); return; } static void zyd_start_cb(struct ifnet *ifp) { struct zyd_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); return; } static void zyd_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[ZYD_TR_BULK_DT_WR]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~ZYD_FLAG_BULK_WRITE_STALL; usb2_transfer_start(xfer_other); } return; } /* * We assume that "m->m_pkthdr.rcvif" is pointing to the "ni" that * should be freed, when "zyd_setup_desc_and_tx" is called. */ static void zyd_setup_desc_and_tx(struct zyd_softc *sc, struct mbuf *m, uint16_t rate) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct mbuf *mm; enum ieee80211_phytype phytype; uint16_t len; uint16_t totlen; uint16_t pktlen; uint8_t remainder; if (sc->sc_tx_queue.ifq_len >= IFQ_MAXLEN) { /* free packet */ zyd_tx_freem(m); ifp->if_oerrors++; return; } if (!((sc->sc_flags & ZYD_FLAG_LL_READY) && (sc->sc_flags & ZYD_FLAG_HL_READY))) { /* free packet */ zyd_tx_freem(m); ifp->if_oerrors++; return; } if (rate < 2) { DPRINTF("rate < 2!\n"); /* avoid division by zero */ rate = 2; } ic->ic_lastdata = ticks; if (bpf_peers_present(ifp->if_bpf)) { struct zyd_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); } len = m->m_pkthdr.len; totlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; phytype = ieee80211_rate2phytype(sc->sc_rates, rate); sc->sc_tx_desc.len = htole16(totlen); sc->sc_tx_desc.phy = zyd_plcp_signal(rate); if (phytype == IEEE80211_T_OFDM) { sc->sc_tx_desc.phy |= ZYD_TX_PHY_OFDM; if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) sc->sc_tx_desc.phy |= ZYD_TX_PHY_5GHZ; } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) sc->sc_tx_desc.phy |= ZYD_TX_PHY_SHPREAMBLE; /* actual transmit length (XXX why +10?) */ pktlen = sizeof(struct zyd_tx_desc) + 10; if (sc->sc_mac_rev == ZYD_ZD1211) pktlen += totlen; sc->sc_tx_desc.pktlen = htole16(pktlen); sc->sc_tx_desc.plcp_length = ((16 * totlen) + rate - 1) / rate; sc->sc_tx_desc.plcp_service = 0; if (rate == 22) { remainder = (16 * totlen) % 22; if ((remainder != 0) && (remainder < 7)) sc->sc_tx_desc.plcp_service |= ZYD_PLCP_LENGEXT; } if (sizeof(sc->sc_tx_desc) > MHLEN) { DPRINTF("No room for header structure!\n"); zyd_tx_freem(m); return; } mm = m_gethdr(M_NOWAIT, MT_DATA); if (mm == NULL) { DPRINTF("Could not allocate header mbuf!\n"); zyd_tx_freem(m); return; } bcopy(&sc->sc_tx_desc, mm->m_data, sizeof(sc->sc_tx_desc)); mm->m_len = sizeof(sc->sc_tx_desc); mm->m_next = m; mm->m_pkthdr.len = mm->m_len + m->m_pkthdr.len; mm->m_pkthdr.rcvif = NULL; /* start write transfer, if not started */ _IF_ENQUEUE(&sc->sc_tx_queue, mm); usb2_transfer_start(sc->sc_xfer[0]); return; } static void zyd_bulk_write_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint16_t temp_len; DPRINTF("\n"); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete\n"); ifp->if_opackets++; case USB_ST_SETUP: if (sc->sc_flags & ZYD_FLAG_BULK_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_WR]); DPRINTFN(11, "write stalled\n"); break; } if (sc->sc_flags & ZYD_FLAG_WAIT_COMMAND) { /* * don't send anything while a command is pending ! */ DPRINTFN(11, "wait command\n"); break; } zyd_fill_write_queue(sc); _IF_DEQUEUE(&sc->sc_tx_queue, m); if (m) { if (m->m_pkthdr.len > ZYD_MAX_TXBUFSZ) { DPRINTFN(0, "data overflow, %u bytes\n", m->m_pkthdr.len); m->m_pkthdr.len = ZYD_MAX_TXBUFSZ; } usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); /* get transfer length */ temp_len = m->m_pkthdr.len; DPRINTFN(11, "sending frame len=%u xferlen=%u\n", m->m_pkthdr.len, temp_len); xfer->frlengths[0] = temp_len; usb2_start_hardware(xfer); /* free mbuf and node */ zyd_tx_freem(m); } break; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ZYD_FLAG_BULK_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_WR]); } ifp->if_oerrors++; break; } return; } static void zyd_init_cb(void *arg) { struct zyd_softc *sc = arg; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &zyd_cfg_pre_init, &zyd_cfg_init, 0, 0); mtx_unlock(&sc->sc_mtx); return; } static int zyd_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) { struct zyd_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; int error; switch (cmd) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { usb2_config_td_queue_command (&sc->sc_config_td, &zyd_cfg_pre_init, &zyd_cfg_init, 0, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &zyd_cfg_pre_stop, &zyd_cfg_stop, 0, 0); } } mtx_unlock(&sc->sc_mtx); error = 0; break; case SIOCGIFMEDIA: case SIOCADDMULTI: case SIOCDELMULTI: error = ifmedia_ioctl(ifp, (void *)data, &ic->ic_media, cmd); break; default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } static void zyd_watchdog(void *arg) { struct zyd_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); if (sc->sc_amrr_timer) { usb2_config_td_queue_command (&sc->sc_config_td, NULL, &zyd_cfg_amrr_timeout, 0, 0); } usb2_callout_reset(&sc->sc_watchdog, hz, &zyd_watchdog, sc); mtx_unlock(&sc->sc_mtx); return; } static void zyd_config_copy_chan(struct zyd_config_copy_chan *cc, struct ieee80211com *ic, struct ieee80211_channel *c) { if (!c) return; cc->chan_to_ieee = ieee80211_chan2ieee(ic, c); if (c != IEEE80211_CHAN_ANYC) { cc->chan_to_mode = ieee80211_chan2mode(c); cc->ic_freq = c->ic_freq; if (IEEE80211_IS_CHAN_B(c)) cc->chan_is_b = 1; if (IEEE80211_IS_CHAN_A(c)) cc->chan_is_a = 1; if (IEEE80211_IS_CHAN_2GHZ(c)) cc->chan_is_2ghz = 1; if (IEEE80211_IS_CHAN_5GHZ(c)) cc->chan_is_5ghz = 1; if (IEEE80211_IS_CHAN_ANYG(c)) cc->chan_is_g = 1; } return; } static void zyd_config_copy(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { const struct ieee80211_txparam *tp; struct ieee80211vap *vap; struct ifmultiaddr *ifma; struct ieee80211_node *ni; struct ieee80211com *ic; struct ifnet *ifp; bzero(cc, sizeof(*cc)); ifp = sc->sc_ifp; if (ifp) { cc->if_flags = ifp->if_flags; bcopy(ifp->if_broadcastaddr, cc->if_broadcastaddr, sizeof(cc->if_broadcastaddr)); cc->zyd_multi_low = 0x00000000; cc->zyd_multi_high = 0x80000000; IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { uint8_t v; if (ifma->ifma_addr->sa_family != AF_LINK) continue; v = ((uint8_t *)LLADDR((struct sockaddr_dl *) ifma->ifma_addr))[5] >> 2; if (v < 32) cc->zyd_multi_low |= 1 << v; else cc->zyd_multi_high |= 1 << (v - 32); } IF_ADDR_UNLOCK(ifp); ic = ifp->if_l2com; if (ic) { zyd_config_copy_chan(&cc->ic_curchan, ic, ic->ic_curchan); zyd_config_copy_chan(&cc->ic_bsschan, ic, ic->ic_bsschan); vap = TAILQ_FIRST(&ic->ic_vaps); if (vap) { ni = vap->iv_bss; if (ni) { cc->iv_bss.ni_intval = ni->ni_intval; bcopy(ni->ni_bssid, cc->iv_bss.ni_bssid, sizeof(cc->iv_bss.ni_bssid)); } tp = vap->iv_txparms + cc->ic_bsschan.chan_to_mode; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { cc->iv_bss.fixed_rate_none = 1; } } cc->ic_opmode = ic->ic_opmode; cc->ic_flags = ic->ic_flags; cc->ic_txpowlimit = ic->ic_txpowlimit; cc->ic_curmode = ic->ic_curmode; bcopy(ic->ic_myaddr, cc->ic_myaddr, sizeof(cc->ic_myaddr)); } } sc->sc_flags |= ZYD_FLAG_WAIT_COMMAND; return; } static void zyd_end_of_commands(struct zyd_softc *sc) { sc->sc_flags &= ~ZYD_FLAG_WAIT_COMMAND; /* start write transfer, if not started */ usb2_transfer_start(sc->sc_xfer[0]); return; } static void zyd_newassoc_cb(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&ZYD_VAP(vap)->amrr, &ZYD_NODE(ni)->amn, ni); return; } static void zyd_cfg_amrr_timeout(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ieee80211vap *vap; struct ieee80211_node *ni; vap = zyd_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } if ((sc->sc_flags & ZYD_FLAG_LL_READY) && (sc->sc_flags & ZYD_FLAG_HL_READY)) { if (sc->sc_amrr_timer) { if (ieee80211_amrr_choose(ni, &ZYD_NODE(ni)->amn)) { /* ignore */ } } } return; } static void zyd_cfg_amrr_start(struct zyd_softc *sc) { struct ieee80211vap *vap; struct ieee80211_node *ni; vap = zyd_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } /* init AMRR */ ieee80211_amrr_node_init(&ZYD_VAP(vap)->amrr, &ZYD_NODE(ni)->amn, ni); /* enable AMRR timer */ sc->sc_amrr_timer = 1; return; } static struct ieee80211vap * zyd_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct zyd_vap *zvp; struct ieee80211vap *vap; struct zyd_softc *sc = ic->ic_ifp->if_softc; /* Need to sync with config thread: */ mtx_lock(&sc->sc_mtx); if (usb2_config_td_sync(&sc->sc_config_td)) { mtx_unlock(&sc->sc_mtx); /* config thread is gone */ return (NULL); } mtx_unlock(&sc->sc_mtx); if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; zvp = (struct zyd_vap *)malloc(sizeof(struct zyd_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (zvp == NULL) return NULL; vap = &zvp->vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override state transition machine */ zvp->newstate = vap->iv_newstate; vap->iv_newstate = &zyd_newstate_cb; ieee80211_amrr_init(&zvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 1000 /* 1 sec */ ); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return (vap); } static void zyd_vap_delete(struct ieee80211vap *vap) { struct zyd_vap *zvp = ZYD_VAP(vap); struct zyd_softc *sc = vap->iv_ic->ic_ifp->if_softc; /* Need to sync with config thread: */ mtx_lock(&sc->sc_mtx); if (usb2_config_td_sync(&sc->sc_config_td)) { /* ignore */ } mtx_unlock(&sc->sc_mtx); ieee80211_amrr_cleanup(&zvp->amrr); ieee80211_vap_detach(vap); free(zvp, M_80211_VAP); return; } /* ARGUSED */ static struct ieee80211_node * zyd_node_alloc_cb(struct ieee80211vap *vap __unused, const uint8_t mac[IEEE80211_ADDR_LEN] __unused) { struct zyd_node *zn; zn = malloc(sizeof(struct zyd_node), M_80211_NODE, M_NOWAIT | M_ZERO); return ((zn != NULL) ? &zn->ni : NULL); } static void zyd_fill_write_queue(struct zyd_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211_node *ni; struct mbuf *m; /* * We only fill up half of the queue with data frames. The rest is * reserved for other kinds of frames. */ while (sc->sc_tx_queue.ifq_len < (IFQ_MAXLEN / 2)) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; ni = (void *)(m->m_pkthdr.rcvif); m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); continue; } zyd_tx_data(sc, m, ni); } return; } static void zyd_tx_clean_queue(struct zyd_softc *sc) { struct mbuf *m; for (;;) { _IF_DEQUEUE(&sc->sc_tx_queue, m); if (!m) { break; } zyd_tx_freem(m); } return; } static void zyd_tx_freem(struct mbuf *m) { struct ieee80211_node *ni; while (m) { ni = (void *)(m->m_pkthdr.rcvif); if (!ni) { m = m_free(m); continue; } if (m->m_flags & M_TXCB) { ieee80211_process_callback(ni, m, 0); } m_freem(m); ieee80211_free_node(ni); break; } return; } static void zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ieee80211_frame *wh; struct ieee80211_key *k; uint16_t totlen; uint16_t rate; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; rate = tp->mgmtrate; wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); ieee80211_free_node(ni); return; } wh = mtod(m, struct ieee80211_frame *); } /* fill Tx descriptor */ sc->sc_tx_desc.flags = ZYD_TX_FLAG_BACKOFF; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { /* get total length */ totlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; /* multicast frames are not sent at OFDM rates in 802.11b/g */ if (totlen > vap->iv_rtsthreshold) { sc->sc_tx_desc.flags |= ZYD_TX_FLAG_RTS; } else if (ZYD_RATE_IS_OFDM(rate) && (ic->ic_flags & IEEE80211_F_USEPROT)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) sc->sc_tx_desc.flags |= ZYD_TX_FLAG_CTS_TO_SELF; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) sc->sc_tx_desc.flags |= ZYD_TX_FLAG_RTS; } } else sc->sc_tx_desc.flags |= ZYD_TX_FLAG_MULTICAST; if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL)) sc->sc_tx_desc.flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); m->m_pkthdr.rcvif = (void *)ni; zyd_setup_desc_and_tx(sc, m, rate); return; } static void zyd_tx_data(struct zyd_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ieee80211_frame *wh; struct ieee80211_key *k; uint16_t rate; wh = mtod(m, struct ieee80211_frame *); sc->sc_tx_desc.flags = ZYD_TX_FLAG_BACKOFF; tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { rate = tp->mcastrate; sc->sc_tx_desc.flags |= ZYD_TX_FLAG_MULTICAST; } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { rate = tp->ucastrate; } else rate = ni->ni_txrate; if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); ieee80211_free_node(ni); return; } /* packet header may have moved, reset our local pointer */ wh = mtod(m, struct ieee80211_frame *); } /* fill Tx descriptor */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { uint16_t totlen; totlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; /* multicast frames are not sent at OFDM rates in 802.11b/g */ if (totlen > vap->iv_rtsthreshold) { sc->sc_tx_desc.flags |= ZYD_TX_FLAG_RTS; } else if (ZYD_RATE_IS_OFDM(rate) && (ic->ic_flags & IEEE80211_F_USEPROT)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) sc->sc_tx_desc.flags |= ZYD_TX_FLAG_CTS_TO_SELF; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) sc->sc_tx_desc.flags |= ZYD_TX_FLAG_RTS; } } if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL)) sc->sc_tx_desc.flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); m->m_pkthdr.rcvif = (void *)ni; zyd_setup_desc_and_tx(sc, m, rate); return; } static int zyd_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct zyd_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ zyd_tx_mgt(sc, m, ni); } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ zyd_tx_mgt(sc, m, ni); /* XXX zyd_tx_raw() */ } mtx_unlock(&sc->sc_mtx); return (0); } static struct ieee80211vap * zyd_get_vap(struct zyd_softc *sc) { struct ifnet *ifp; struct ieee80211com *ic; if (sc == NULL) { return NULL; } ifp = sc->sc_ifp; if (ifp == NULL) { return NULL; } ic = ifp->if_l2com; if (ic == NULL) { return NULL; } return TAILQ_FIRST(&ic->ic_vaps); } Index: user/thompsa/vaptq/sys/dev/wi/if_wi.c =================================================================== --- user/thompsa/vaptq/sys/dev/wi/if_wi.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/wi/if_wi.c (revision 185733) @@ -1,2151 +1,2111 @@ /*- * 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. */ /* * Lucent WaveLAN/IEEE 802.11 PCMCIA driver. * * Original FreeBSD driver written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The WaveLAN/IEEE adapter is the second generation of the WaveLAN * from Lucent. Unlike the older cards, the new ones are programmed * entirely via a firmware-driven controller called the Hermes. * Unfortunately, Lucent will not release the Hermes programming manual * without an NDA (if at all). What they do release is an API library * called the HCF (Hardware Control Functions) which is supposed to * do the device-specific operations of a device driver for you. The * publically available version of the HCF library (the 'HCF Light') is * a) extremely gross, b) lacks certain features, particularly support * for 802.11 frames, and c) is contaminated by the GNU Public License. * * This driver does not use the HCF or HCF Light at all. Instead, it * programs the Hermes controller directly, using information gleaned * from the HCF Light code and corresponding documentation. * * This driver supports the ISA, PCMCIA and PCI versions of the Lucent * WaveLan cards (based on the Hermes chipset), as well as the newer * Prism 2 chipsets with firmware from Intersil and Symbol. */ #include __FBSDID("$FreeBSD$"); #define WI_HERMES_STATS_WAR /* Work around stats counter bug. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct ieee80211vap *wi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void wi_vap_delete(struct ieee80211vap *vap); static void wi_stop_locked(struct wi_softc *sc, int disable); static void wi_start_locked(struct ifnet *); static void wi_start(struct ifnet *); static int wi_start_tx(struct ifnet *ifp, struct wi_frame *frmhdr, struct mbuf *m0); static int wi_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static int wi_newstate_sta(struct ieee80211vap *, enum ieee80211_state, int); static int wi_newstate_hostap(struct ieee80211vap *, enum ieee80211_state, int); static void wi_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, int rssi, int noise, u_int32_t rstamp); static int wi_reset(struct wi_softc *); static void wi_watchdog(void *); static int wi_ioctl(struct ifnet *, u_long, caddr_t); static void wi_media_status(struct ifnet *, struct ifmediareq *); static void wi_rx_intr(struct wi_softc *); static void wi_tx_intr(struct wi_softc *); static void wi_tx_ex_intr(struct wi_softc *); -static void wi_status_connected(void *, int); -static void wi_status_disconnected(void *, int); static void wi_status_oor(void *, int); -static void wi_status_assoc_failed(void *, int); static void wi_info_intr(struct wi_softc *); static int wi_write_txrate(struct wi_softc *, struct ieee80211vap *); static int wi_write_wep(struct wi_softc *, struct ieee80211vap *); static int wi_write_multi(struct wi_softc *); static void wi_update_mcast(struct ifnet *); static int wi_alloc_fid(struct wi_softc *, int, int *); static void wi_read_nicid(struct wi_softc *); static int wi_write_ssid(struct wi_softc *, int, u_int8_t *, int); static int wi_cmd(struct wi_softc *, int, int, int, int); static int wi_seek_bap(struct wi_softc *, int, int); static int wi_read_bap(struct wi_softc *, int, int, void *, int); static int wi_write_bap(struct wi_softc *, int, int, void *, int); static int wi_mwrite_bap(struct wi_softc *, int, int, struct mbuf *, int); static int wi_read_rid(struct wi_softc *, int, void *, int *); static int wi_write_rid(struct wi_softc *, int, void *, int); static int wi_write_appie(struct wi_softc *, int, const struct ieee80211_appie *); static void wi_scan_start(struct ieee80211com *); static void wi_scan_end(struct ieee80211com *); static void wi_set_channel(struct ieee80211com *); static __inline int wi_write_val(struct wi_softc *sc, int rid, u_int16_t val) { val = htole16(val); return wi_write_rid(sc, rid, &val, sizeof(val)); } SYSCTL_NODE(_hw, OID_AUTO, wi, CTLFLAG_RD, 0, "Wireless driver parameters"); static struct timeval lasttxerror; /* time of last tx error msg */ static int curtxeps; /* current tx error msgs/sec */ static int wi_txerate = 0; /* tx error rate: max msgs/sec */ SYSCTL_INT(_hw_wi, OID_AUTO, txerate, CTLFLAG_RW, &wi_txerate, 0, "max tx error msgs/sec; 0 to disable msgs"); #define WI_DEBUG #ifdef WI_DEBUG static int wi_debug = 0; SYSCTL_INT(_hw_wi, OID_AUTO, debug, CTLFLAG_RW, &wi_debug, 0, "control debugging printfs"); #define DPRINTF(X) if (wi_debug) printf X #else #define DPRINTF(X) #endif #define WI_INTRS (WI_EV_RX | WI_EV_ALLOC | WI_EV_INFO) struct wi_card_ident wi_card_ident[] = { /* CARD_ID CARD_NAME FIRM_TYPE */ { WI_NIC_LUCENT_ID, WI_NIC_LUCENT_STR, WI_LUCENT }, { WI_NIC_SONY_ID, WI_NIC_SONY_STR, WI_LUCENT }, { WI_NIC_LUCENT_EMB_ID, WI_NIC_LUCENT_EMB_STR, WI_LUCENT }, { WI_NIC_EVB2_ID, WI_NIC_EVB2_STR, WI_INTERSIL }, { WI_NIC_HWB3763_ID, WI_NIC_HWB3763_STR, WI_INTERSIL }, { WI_NIC_HWB3163_ID, WI_NIC_HWB3163_STR, WI_INTERSIL }, { WI_NIC_HWB3163B_ID, WI_NIC_HWB3163B_STR, WI_INTERSIL }, { WI_NIC_EVB3_ID, WI_NIC_EVB3_STR, WI_INTERSIL }, { WI_NIC_HWB1153_ID, WI_NIC_HWB1153_STR, WI_INTERSIL }, { WI_NIC_P2_SST_ID, WI_NIC_P2_SST_STR, WI_INTERSIL }, { WI_NIC_EVB2_SST_ID, WI_NIC_EVB2_SST_STR, WI_INTERSIL }, { WI_NIC_3842_EVA_ID, WI_NIC_3842_EVA_STR, WI_INTERSIL }, { WI_NIC_3842_PCMCIA_AMD_ID, WI_NIC_3842_PCMCIA_STR, WI_INTERSIL }, { WI_NIC_3842_PCMCIA_SST_ID, WI_NIC_3842_PCMCIA_STR, WI_INTERSIL }, { WI_NIC_3842_PCMCIA_ATL_ID, WI_NIC_3842_PCMCIA_STR, WI_INTERSIL }, { WI_NIC_3842_PCMCIA_ATS_ID, WI_NIC_3842_PCMCIA_STR, WI_INTERSIL }, { WI_NIC_3842_MINI_AMD_ID, WI_NIC_3842_MINI_STR, WI_INTERSIL }, { WI_NIC_3842_MINI_SST_ID, WI_NIC_3842_MINI_STR, WI_INTERSIL }, { WI_NIC_3842_MINI_ATL_ID, WI_NIC_3842_MINI_STR, WI_INTERSIL }, { WI_NIC_3842_MINI_ATS_ID, WI_NIC_3842_MINI_STR, WI_INTERSIL }, { WI_NIC_3842_PCI_AMD_ID, WI_NIC_3842_PCI_STR, WI_INTERSIL }, { WI_NIC_3842_PCI_SST_ID, WI_NIC_3842_PCI_STR, WI_INTERSIL }, { WI_NIC_3842_PCI_ATS_ID, WI_NIC_3842_PCI_STR, WI_INTERSIL }, { WI_NIC_3842_PCI_ATL_ID, WI_NIC_3842_PCI_STR, WI_INTERSIL }, { WI_NIC_P3_PCMCIA_AMD_ID, WI_NIC_P3_PCMCIA_STR, WI_INTERSIL }, { WI_NIC_P3_PCMCIA_SST_ID, WI_NIC_P3_PCMCIA_STR, WI_INTERSIL }, { WI_NIC_P3_PCMCIA_ATL_ID, WI_NIC_P3_PCMCIA_STR, WI_INTERSIL }, { WI_NIC_P3_PCMCIA_ATS_ID, WI_NIC_P3_PCMCIA_STR, WI_INTERSIL }, { WI_NIC_P3_MINI_AMD_ID, WI_NIC_P3_MINI_STR, WI_INTERSIL }, { WI_NIC_P3_MINI_SST_ID, WI_NIC_P3_MINI_STR, WI_INTERSIL }, { WI_NIC_P3_MINI_ATL_ID, WI_NIC_P3_MINI_STR, WI_INTERSIL }, { WI_NIC_P3_MINI_ATS_ID, WI_NIC_P3_MINI_STR, WI_INTERSIL }, { 0, NULL, 0 }, }; static char *wi_firmware_names[] = { "none", "Hermes", "Intersil", "Symbol" }; devclass_t wi_devclass; int wi_attach(device_t dev) { struct wi_softc *sc = device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; int i, nrates, buflen; u_int16_t val; u_int8_t ratebuf[2 + IEEE80211_RATE_SIZE]; struct ieee80211_rateset *rs; struct sysctl_ctx_list *sctx; struct sysctl_oid *soid; static const u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int error; ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(dev, "can not if_alloc\n"); wi_free(dev); return ENOSPC; } ic = ifp->if_l2com; sc->sc_firmware_type = WI_NOTYPE; sc->wi_cmd_count = 500; /* Reset the NIC. */ if (wi_reset(sc) != 0) { wi_free(dev); return ENXIO; /* XXX */ } /* Read NIC identification */ wi_read_nicid(sc); switch (sc->sc_firmware_type) { case WI_LUCENT: if (sc->sc_sta_firmware_ver < 60006) goto reject; break; case WI_INTERSIL: if (sc->sc_sta_firmware_ver < 800) goto reject; break; default: reject: device_printf(dev, "Sorry, this card is not supported " "(type %d, firmware ver %d)\n", sc->sc_firmware_type, sc->sc_sta_firmware_ver); wi_free(dev); return EOPNOTSUPP; } /* Export info about the device via sysctl */ sctx = device_get_sysctl_ctx(dev); soid = device_get_sysctl_tree(dev); SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "firmware_type", CTLFLAG_RD, wi_firmware_names[sc->sc_firmware_type], 0, "Firmware type string"); SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "sta_version", CTLFLAG_RD, &sc->sc_sta_firmware_ver, 0, "Station Firmware version"); if (sc->sc_firmware_type == WI_INTERSIL) SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "pri_version", CTLFLAG_RD, &sc->sc_pri_firmware_ver, 0, "Primary Firmware version"); SYSCTL_ADD_XINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "nic_id", CTLFLAG_RD, &sc->sc_nic_id, 0, "NIC id"); SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "nic_name", CTLFLAG_RD, sc->sc_nic_name, 0, "NIC name"); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); /* * Read the station address. * And do it twice. I've seen PRISM-based cards that return * an error when trying to read it the first time, which causes * the probe to fail. */ buflen = IEEE80211_ADDR_LEN; error = wi_read_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, &buflen); if (error != 0) { buflen = IEEE80211_ADDR_LEN; error = wi_read_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, &buflen); } if (error || IEEE80211_ADDR_EQ(ic->ic_myaddr, empty_macaddr)) { if (error != 0) device_printf(dev, "mac read failed %d\n", error); else { device_printf(dev, "mac read failed (all zeros)\n"); error = ENXIO; } wi_free(dev); return (error); } ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = wi_ioctl; ifp->if_start = wi_start; ifp->if_init = wi_init; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_DS; ic->ic_opmode = IEEE80211_M_STA; ic->ic_caps = IEEE80211_C_STA | IEEE80211_C_PMGT | IEEE80211_C_MONITOR ; /* * Query the card for available channels and setup the * channel table. We assume these are all 11b channels. */ buflen = sizeof(val); if (wi_read_rid(sc, WI_RID_CHANNEL_LIST, &val, &buflen) != 0) val = htole16(0x1fff); /* assume 1-11 */ KASSERT(val != 0, ("wi_attach: no available channels listed!")); val <<= 1; /* shift for base 1 indices */ for (i = 1; i < 16; i++) { struct ieee80211_channel *c; if (!isset((u_int8_t*)&val, i)) continue; c = &ic->ic_channels[ic->ic_nchans++]; c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_B); c->ic_flags = IEEE80211_CHAN_B; c->ic_ieee = i; /* XXX txpowers? */ } /* * Set flags based on firmware version. */ switch (sc->sc_firmware_type) { case WI_LUCENT: sc->sc_ntxbuf = 1; ic->ic_caps |= IEEE80211_C_IBSS; sc->sc_ibss_port = WI_PORTTYPE_BSS; sc->sc_monitor_port = WI_PORTTYPE_ADHOC; sc->sc_min_rssi = WI_LUCENT_MIN_RSSI; sc->sc_max_rssi = WI_LUCENT_MAX_RSSI; sc->sc_dbm_offset = WI_LUCENT_DBM_OFFSET; break; case WI_INTERSIL: sc->sc_ntxbuf = WI_NTXBUF; sc->sc_flags |= WI_FLAGS_HAS_FRAGTHR | WI_FLAGS_HAS_ROAMING; /* * Old firmware are slow, so give peace a chance. */ if (sc->sc_sta_firmware_ver < 10000) sc->wi_cmd_count = 5000; if (sc->sc_sta_firmware_ver > 10101) sc->sc_flags |= WI_FLAGS_HAS_DBMADJUST; ic->ic_caps |= IEEE80211_C_IBSS; /* * version 0.8.3 and newer are the only ones that are known * to currently work. Earlier versions can be made to work, * at least according to the Linux driver but we require * monitor mode so this is irrelevant. */ ic->ic_caps |= IEEE80211_C_HOSTAP; if (sc->sc_sta_firmware_ver >= 10603) sc->sc_flags |= WI_FLAGS_HAS_ENHSECURITY; if (sc->sc_sta_firmware_ver >= 10700) { /* * 1.7.0+ have the necessary support for sta mode WPA. */ sc->sc_flags |= WI_FLAGS_HAS_WPASUPPORT; ic->ic_caps |= IEEE80211_C_WPA; } sc->sc_ibss_port = WI_PORTTYPE_IBSS; sc->sc_monitor_port = WI_PORTTYPE_APSILENT; sc->sc_min_rssi = WI_PRISM_MIN_RSSI; sc->sc_max_rssi = WI_PRISM_MAX_RSSI; sc->sc_dbm_offset = WI_PRISM_DBM_OFFSET; break; } /* * Find out if we support WEP on this card. */ buflen = sizeof(val); if (wi_read_rid(sc, WI_RID_WEP_AVAIL, &val, &buflen) == 0 && val != htole16(0)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP; /* Find supported rates. */ buflen = sizeof(ratebuf); rs = &ic->ic_sup_rates[IEEE80211_MODE_11B]; if (wi_read_rid(sc, WI_RID_DATA_RATES, ratebuf, &buflen) == 0) { nrates = le16toh(*(u_int16_t *)ratebuf); if (nrates > IEEE80211_RATE_MAXSIZE) nrates = IEEE80211_RATE_MAXSIZE; rs->rs_nrates = 0; for (i = 0; i < nrates; i++) if (ratebuf[2+i]) rs->rs_rates[rs->rs_nrates++] = ratebuf[2+i]; } else { /* XXX fallback on error? */ } buflen = sizeof(val); if ((sc->sc_flags & WI_FLAGS_HAS_DBMADJUST) && wi_read_rid(sc, WI_RID_DBM_ADJUST, &val, &buflen) == 0) { sc->sc_dbm_offset = le16toh(val); } sc->sc_portnum = WI_DEFAULT_PORT; TASK_INIT(&sc->sc_oor_task, 0, wi_status_oor, ic); ieee80211_ifattach(ic); ic->ic_raw_xmit = wi_raw_xmit; ic->ic_scan_start = wi_scan_start; ic->ic_scan_end = wi_scan_end; ic->ic_set_channel = wi_set_channel; ic->ic_vap_create = wi_vap_create; ic->ic_vap_delete = wi_vap_delete; ic->ic_update_mcast = wi_update_mcast; bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th)); /* * Initialize constant fields. * XXX make header lengths a multiple of 32-bits so subsequent * headers are properly aligned; this is a kludge to keep * certain applications happy. * * NB: the channel is setup each time we transition to the * RUN state to avoid filling it in for each frame. */ sc->sc_tx_th_len = roundup(sizeof(sc->sc_tx_th), sizeof(u_int32_t)); sc->sc_tx_th.wt_ihdr.it_len = htole16(sc->sc_tx_th_len); sc->sc_tx_th.wt_ihdr.it_present = htole32(WI_TX_RADIOTAP_PRESENT); sc->sc_rx_th_len = roundup(sizeof(sc->sc_rx_th), sizeof(u_int32_t)); sc->sc_rx_th.wr_ihdr.it_len = htole16(sc->sc_rx_th_len); sc->sc_rx_th.wr_ihdr.it_present = htole32(WI_RX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, wi_intr, sc, &sc->wi_intrhand); if (error) { device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); bpfdetach(ifp); ieee80211_ifdetach(ic); if_free(sc->sc_ifp); wi_free(dev); return error; } return (0); } int wi_detach(device_t dev) { struct wi_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; WI_LOCK(sc); /* check if device was removed */ sc->wi_gone |= !bus_child_present(dev); wi_stop_locked(sc, 0); WI_UNLOCK(sc); bpfdetach(ifp); ieee80211_ifdetach(ic); bus_teardown_intr(dev, sc->irq, sc->wi_intrhand); if_free(sc->sc_ifp); wi_free(dev); mtx_destroy(&sc->sc_mtx); return (0); } static struct ieee80211vap * wi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct wi_softc *sc = ic->ic_ifp->if_softc; struct wi_vap *wvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; wvp = (struct wi_vap *) malloc(sizeof(struct wi_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (wvp == NULL) return NULL; vap = &wvp->wv_vap; ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); vap->iv_max_aid = WI_MAX_AID; switch (opmode) { case IEEE80211_M_STA: sc->sc_porttype = WI_PORTTYPE_BSS; wvp->wv_newstate = vap->iv_newstate; vap->iv_newstate = wi_newstate_sta; /* need to filter mgt frames to avoid confusing state machine */ wvp->wv_recv_mgmt = vap->iv_recv_mgmt; vap->iv_recv_mgmt = wi_recv_mgmt; break; case IEEE80211_M_IBSS: sc->sc_porttype = sc->sc_ibss_port; wvp->wv_newstate = vap->iv_newstate; vap->iv_newstate = wi_newstate_sta; break; case IEEE80211_M_AHDEMO: sc->sc_porttype = WI_PORTTYPE_ADHOC; break; case IEEE80211_M_HOSTAP: sc->sc_porttype = WI_PORTTYPE_HOSTAP; wvp->wv_newstate = vap->iv_newstate; vap->iv_newstate = wi_newstate_hostap; break; case IEEE80211_M_MONITOR: sc->sc_porttype = sc->sc_monitor_port; break; default: break; } - TASK_INIT(&wvp->wv_connected_task, 0, wi_status_connected, vap); - TASK_INIT(&wvp->wv_disconnected_task, 0, wi_status_disconnected, vap); - TASK_INIT(&wvp->wv_assoc_failed_task, 0, wi_status_assoc_failed, vap); - /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, wi_media_status); ic->ic_opmode = opmode; return vap; } static void wi_vap_delete(struct ieee80211vap *vap) { struct wi_vap *wvp = WI_VAP(vap); ieee80211_vap_detach(vap); free(wvp, M_80211_VAP); } void wi_shutdown(device_t dev) { struct wi_softc *sc = device_get_softc(dev); wi_stop(sc, 1); } void wi_intr(void *arg) { struct wi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; u_int16_t status; WI_LOCK(sc); if (sc->wi_gone || !sc->sc_enabled || (ifp->if_flags & IFF_UP) == 0) { CSR_WRITE_2(sc, WI_INT_EN, 0); CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); WI_UNLOCK(sc); return; } /* Disable interrupts. */ CSR_WRITE_2(sc, WI_INT_EN, 0); status = CSR_READ_2(sc, WI_EVENT_STAT); if (status & WI_EV_RX) wi_rx_intr(sc); if (status & WI_EV_ALLOC) wi_tx_intr(sc); if (status & WI_EV_TX_EXC) wi_tx_ex_intr(sc); if (status & WI_EV_INFO) wi_info_intr(sc); if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0 && !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) wi_start_locked(ifp); /* Re-enable interrupts. */ CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); WI_UNLOCK(sc); return; } static void wi_enable(struct wi_softc *sc) { /* Enable interrupts */ CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); /* enable port */ wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0); sc->sc_enabled = 1; } static int wi_setup_locked(struct wi_softc *sc, int porttype, int mode, uint8_t mac[IEEE80211_ADDR_LEN]) { int i; wi_reset(sc); wi_write_val(sc, WI_RID_PORTTYPE, porttype); wi_write_val(sc, WI_RID_CREATE_IBSS, mode); wi_write_val(sc, WI_RID_MAX_DATALEN, 2304); /* XXX IEEE80211_BPF_NOACK wants 0 */ wi_write_val(sc, WI_RID_ALT_RETRY_CNT, 2); if (sc->sc_flags & WI_FLAGS_HAS_ROAMING) wi_write_val(sc, WI_RID_ROAMING_MODE, 3); /* NB: disabled */ wi_write_rid(sc, WI_RID_MAC_NODE, mac, IEEE80211_ADDR_LEN); /* Allocate fids for the card */ sc->sc_buflen = IEEE80211_MAX_LEN + sizeof(struct wi_frame); for (i = 0; i < sc->sc_ntxbuf; i++) { int error = wi_alloc_fid(sc, sc->sc_buflen, &sc->sc_txd[i].d_fid); if (error) { device_printf(sc->sc_dev, "tx buffer allocation failed (error %u)\n", error); return error; } sc->sc_txd[i].d_len = 0; } sc->sc_txcur = sc->sc_txnext = 0; return 0; } static void wi_init_locked(struct wi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int wasenabled; WI_LOCK_ASSERT(sc); wasenabled = sc->sc_enabled; if (wasenabled) wi_stop_locked(sc, 1); IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); if (wi_setup_locked(sc, sc->sc_porttype, 3, ic->ic_myaddr) != 0) { if_printf(ifp, "interface not running\n"); wi_stop_locked(sc, 1); return; } ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc); wi_enable(sc); /* Enable desired port */ } void wi_init(void *arg) { struct wi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; WI_LOCK(sc); wi_init_locked(sc); WI_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); /* start all vap's */ } static void wi_stop_locked(struct wi_softc *sc, int disable) { struct ifnet *ifp = sc->sc_ifp; WI_LOCK_ASSERT(sc); if (sc->sc_enabled && !sc->wi_gone) { CSR_WRITE_2(sc, WI_INT_EN, 0); wi_cmd(sc, WI_CMD_DISABLE | sc->sc_portnum, 0, 0, 0); if (disable) sc->sc_enabled = 0; } else if (sc->wi_gone && disable) /* gone --> not enabled */ sc->sc_enabled = 0; callout_stop(&sc->sc_watchdog); sc->sc_tx_timer = 0; sc->sc_false_syns = 0; ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE | IFF_DRV_RUNNING); } void wi_stop(struct wi_softc *sc, int disable) { WI_LOCK(sc); wi_stop_locked(sc, disable); WI_UNLOCK(sc); } static void wi_set_channel(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct wi_softc *sc = ifp->if_softc; DPRINTF(("%s: channel %d, %sscanning\n", __func__, ieee80211_chan2ieee(ic, ic->ic_curchan), ic->ic_flags & IEEE80211_F_SCAN ? "" : "!")); WI_LOCK(sc); wi_write_val(sc, WI_RID_OWN_CHNL, ieee80211_chan2ieee(ic, ic->ic_curchan)); sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq = htole16(ic->ic_curchan->ic_freq); sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags = htole16(ic->ic_curchan->ic_flags); WI_UNLOCK(sc); } static void wi_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct wi_softc *sc = ifp->if_softc; struct ieee80211_scan_state *ss = ic->ic_scan; DPRINTF(("%s\n", __func__)); WI_LOCK(sc); /* * Switch device to monitor mode. */ wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_monitor_port); if (sc->sc_firmware_type == WI_INTERSIL) { wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0); wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0); } /* force full dwell time to compensate for firmware overhead */ ss->ss_mindwell = ss->ss_maxdwell = msecs_to_ticks(400); WI_UNLOCK(sc); } static void wi_scan_end(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct wi_softc *sc = ifp->if_softc; DPRINTF(("%s: restore port type %d\n", __func__, sc->sc_porttype)); WI_LOCK(sc); wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_porttype); if (sc->sc_firmware_type == WI_INTERSIL) { wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0); wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0); } WI_UNLOCK(sc); } static void wi_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, int rssi, int noise, u_int32_t rstamp) { struct ieee80211vap *vap = ni->ni_vap; switch (subtype) { case IEEE80211_FC0_SUBTYPE_AUTH: case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: /* NB: filter frames that trigger state changes */ return; } WI_VAP(vap)->wv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp); } static int wi_newstate_sta(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct ieee80211_node *bss; struct wi_softc *sc = ifp->if_softc; DPRINTF(("%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate])); if (nstate == IEEE80211_S_AUTH) { WI_LOCK(sc); wi_setup_locked(sc, WI_PORTTYPE_BSS, 3, vap->iv_myaddr); if (vap->iv_flags & IEEE80211_F_PMGTON) { wi_write_val(sc, WI_RID_MAX_SLEEP, ic->ic_lintval); wi_write_val(sc, WI_RID_PM_ENABLED, 1); } wi_write_val(sc, WI_RID_RTS_THRESH, vap->iv_rtsthreshold); if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR) wi_write_val(sc, WI_RID_FRAG_THRESH, vap->iv_fragthreshold); wi_write_txrate(sc, vap); bss = vap->iv_bss; wi_write_ssid(sc, WI_RID_DESIRED_SSID, bss->ni_essid, bss->ni_esslen); wi_write_val(sc, WI_RID_OWN_CHNL, ieee80211_chan2ieee(ic, bss->ni_chan)); /* Configure WEP. */ if (ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP) wi_write_wep(sc, vap); else sc->sc_encryption = 0; if ((sc->sc_flags & WI_FLAGS_HAS_WPASUPPORT) && (vap->iv_flags & IEEE80211_F_WPA)) { wi_write_val(sc, WI_RID_WPA_HANDLING, 1); if (vap->iv_appie_wpa != NULL) wi_write_appie(sc, WI_RID_WPA_DATA, vap->iv_appie_wpa); } wi_enable(sc); /* enable port */ /* Lucent firmware does not support the JOIN RID. */ if (sc->sc_firmware_type == WI_INTERSIL) { struct wi_joinreq join; memset(&join, 0, sizeof(join)); IEEE80211_ADDR_COPY(&join.wi_bssid, bss->ni_bssid); join.wi_chan = htole16( ieee80211_chan2ieee(ic, bss->ni_chan)); wi_write_rid(sc, WI_RID_JOIN_REQ, &join, sizeof(join)); } WI_UNLOCK(sc); /* * NB: don't go through 802.11 layer, it'll send auth frame; * instead we drive the state machine from the link status * notification we get on association. */ vap->iv_state = nstate; return EINPROGRESS; } return WI_VAP(vap)->wv_newstate(vap, nstate, arg); } static int wi_newstate_hostap(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct ieee80211_node *bss; struct wi_softc *sc = ifp->if_softc; int error; DPRINTF(("%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate])); error = WI_VAP(vap)->wv_newstate(vap, nstate, arg); if (error == 0 && nstate == IEEE80211_S_RUN) { WI_LOCK(sc); wi_setup_locked(sc, WI_PORTTYPE_HOSTAP, 0, vap->iv_myaddr); bss = vap->iv_bss; wi_write_ssid(sc, WI_RID_OWN_SSID, bss->ni_essid, bss->ni_esslen); wi_write_val(sc, WI_RID_OWN_CHNL, ieee80211_chan2ieee(ic, bss->ni_chan)); wi_write_val(sc, WI_RID_BASIC_RATE, 0x3); wi_write_val(sc, WI_RID_SUPPORT_RATE, 0xf); wi_write_txrate(sc, vap); wi_write_val(sc, WI_RID_OWN_BEACON_INT, bss->ni_intval); wi_write_val(sc, WI_RID_DTIM_PERIOD, vap->iv_dtim_period); wi_write_val(sc, WI_RID_RTS_THRESH, vap->iv_rtsthreshold); if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR) wi_write_val(sc, WI_RID_FRAG_THRESH, vap->iv_fragthreshold); if ((sc->sc_flags & WI_FLAGS_HAS_ENHSECURITY) && (vap->iv_flags & IEEE80211_F_HIDESSID)) { /* * bit 0 means hide SSID in beacons, * bit 1 means don't respond to bcast probe req */ wi_write_val(sc, WI_RID_ENH_SECURITY, 0x3); } if ((sc->sc_flags & WI_FLAGS_HAS_WPASUPPORT) && (vap->iv_flags & IEEE80211_F_WPA) && vap->iv_appie_wpa != NULL) wi_write_appie(sc, WI_RID_WPA_DATA, vap->iv_appie_wpa); wi_write_val(sc, WI_RID_PROMISC, 0); /* Configure WEP. */ if (ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP) wi_write_wep(sc, vap); else sc->sc_encryption = 0; wi_enable(sc); /* enable port */ WI_UNLOCK(sc); } return error; } static void wi_start_locked(struct ifnet *ifp) { struct wi_softc *sc = ifp->if_softc; struct ieee80211_node *ni; struct ieee80211_frame *wh; struct mbuf *m0; struct ieee80211_key *k; struct wi_frame frmhdr; int cur; WI_LOCK_ASSERT(sc); if (sc->wi_gone) return; memset(&frmhdr, 0, sizeof(frmhdr)); cur = sc->sc_txnext; for (;;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m0); if (m0 == NULL) break; if (sc->sc_txd[cur].d_len != 0) { IFQ_DRV_PREPEND(&ifp->if_snd, m0); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } /* NB: copy before 802.11 header is prepended */ m_copydata(m0, 0, ETHER_HDR_LEN, (caddr_t)&frmhdr.wi_ehdr); ni = (struct ieee80211_node *) m0->m_pkthdr.rcvif; m0 = ieee80211_encap(ni, m0); if (m0 == NULL) { ifp->if_oerrors++; ieee80211_free_node(ni); continue; } wh = mtod(m0, struct ieee80211_frame *); frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { ieee80211_free_node(ni); m_freem(m0); continue; } frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT); } if (bpf_peers_present(ifp->if_bpf)) { sc->sc_tx_th.wt_rate = ni->ni_txrate; bpf_mtap2(ifp->if_bpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0); } m_copydata(m0, 0, sizeof(struct ieee80211_frame), (caddr_t)&frmhdr.wi_whdr); m_adj(m0, sizeof(struct ieee80211_frame)); frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len); ieee80211_free_node(ni); if (wi_start_tx(ifp, &frmhdr, m0)) continue; sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf; ifp->if_opackets++; } } static void wi_start(struct ifnet *ifp) { struct wi_softc *sc = ifp->if_softc; WI_LOCK(sc); wi_start_locked(ifp); WI_UNLOCK(sc); } static int wi_start_tx(struct ifnet *ifp, struct wi_frame *frmhdr, struct mbuf *m0) { struct wi_softc *sc = ifp->if_softc; int cur = sc->sc_txnext; int fid, off, error; fid = sc->sc_txd[cur].d_fid; off = sizeof(*frmhdr); error = wi_write_bap(sc, fid, 0, frmhdr, sizeof(*frmhdr)) != 0 || wi_mwrite_bap(sc, fid, off, m0, m0->m_pkthdr.len) != 0; m_freem(m0); if (error) { ifp->if_oerrors++; return -1; } sc->sc_txd[cur].d_len = off; if (sc->sc_txcur == cur) { if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, fid, 0, 0)) { if_printf(ifp, "xmit failed\n"); sc->sc_txd[cur].d_len = 0; return -1; } sc->sc_tx_timer = 5; } return 0; } static int wi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m0, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct wi_softc *sc = ifp->if_softc; struct ieee80211_key *k; struct ieee80211_frame *wh; struct wi_frame frmhdr; int cur; int rc = 0; WI_LOCK(sc); if (sc->wi_gone) { rc = ENETDOWN; goto out; } memset(&frmhdr, 0, sizeof(frmhdr)); cur = sc->sc_txnext; if (sc->sc_txd[cur].d_len != 0) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; rc = ENOBUFS; goto out; } m0->m_pkthdr.rcvif = NULL; m_copydata(m0, 4, ETHER_ADDR_LEN * 2, (caddr_t)&frmhdr.wi_ehdr); frmhdr.wi_ehdr.ether_type = 0; wh = mtod(m0, struct ieee80211_frame *); frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX); if (params && (params->ibp_flags & IEEE80211_BPF_NOACK)) frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_ALTRTRY); if ((wh->i_fc[1] & IEEE80211_FC1_WEP) && (!params || (params && (params->ibp_flags & IEEE80211_BPF_CRYPTO)))) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { rc = ENOMEM; goto out; } frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT); } if (bpf_peers_present(ifp->if_bpf)) { sc->sc_tx_th.wt_rate = ni->ni_txrate; bpf_mtap2(ifp->if_bpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0); } m_copydata(m0, 0, sizeof(struct ieee80211_frame), (caddr_t)&frmhdr.wi_whdr); m_adj(m0, sizeof(struct ieee80211_frame)); frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len); if (wi_start_tx(ifp, &frmhdr, m0) < 0) { m0 = NULL; rc = EIO; goto out; } m0 = NULL; sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf; out: WI_UNLOCK(sc); if (m0 != NULL) m_freem(m0); ieee80211_free_node(ni); return rc; } static int wi_reset(struct wi_softc *sc) { #define WI_INIT_TRIES 3 int i, error = 0; for (i = 0; i < WI_INIT_TRIES; i++) { error = wi_cmd(sc, WI_CMD_INI, 0, 0, 0); if (error == 0) break; DELAY(WI_DELAY * 1000); } sc->sc_reset = 1; if (i == WI_INIT_TRIES) { if_printf(sc->sc_ifp, "reset failed\n"); return error; } CSR_WRITE_2(sc, WI_INT_EN, 0); CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); /* Calibrate timer. */ wi_write_val(sc, WI_RID_TICK_TIME, 8); return 0; #undef WI_INIT_TRIES } static void wi_watchdog(void *arg) { struct wi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; WI_LOCK_ASSERT(sc); if (!sc->sc_enabled) return; if (sc->sc_tx_timer && --sc->sc_tx_timer == 0) { if_printf(ifp, "device timeout\n"); ifp->if_oerrors++; wi_init_locked(ifp->if_softc); return; } callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc); } static int wi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct wi_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; switch (cmd) { case SIOCSIFFLAGS: WI_LOCK(sc); /* * Can't do promisc and hostap at the same time. If all that's * changing is the promisc flag, try to short-circuit a call to * wi_init() by just setting PROMISC in the hardware. */ if (ifp->if_flags & IFF_UP) { if (ic->ic_opmode != IEEE80211_M_HOSTAP && ifp->if_drv_flags & IFF_DRV_RUNNING) { if ((ifp->if_flags ^ sc->sc_if_flags) & IFF_PROMISC) { wi_write_val(sc, WI_RID_PROMISC, (ifp->if_flags & IFF_PROMISC) != 0); } else { wi_init_locked(sc); startall = 1; } } else { wi_init_locked(sc); startall = 1; } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) wi_stop_locked(sc, 1); sc->wi_gone = 0; } sc->sc_if_flags = ifp->if_flags; WI_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return error; } static void wi_media_status(struct ifnet *ifp, struct ifmediareq *imr) { struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; struct wi_softc *sc = ic->ic_ifp->if_softc; u_int16_t val; int rate, len; len = sizeof(val); if (sc->sc_enabled && wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) == 0 && len == sizeof(val)) { /* convert to 802.11 rate */ val = le16toh(val); rate = val * 2; if (sc->sc_firmware_type == WI_LUCENT) { if (rate == 10) rate = 11; /* 5.5Mbps */ } else { if (rate == 4*2) rate = 11; /* 5.5Mbps */ else if (rate == 8*2) rate = 22; /* 11Mbps */ } vap->iv_bss->ni_txrate = rate; } ieee80211_media_status(ifp, imr); } static void wi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN]) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni = vap->iv_bss; if (IEEE80211_ADDR_EQ(new_bssid, ni->ni_bssid)) return; DPRINTF(("wi_sync_bssid: bssid %s -> ", ether_sprintf(ni->ni_bssid))); DPRINTF(("%s ?\n", ether_sprintf(new_bssid))); /* In promiscuous mode, the BSSID field is not a reliable * indicator of the firmware's BSSID. Damp spurious * change-of-BSSID indications. */ if ((ifp->if_flags & IFF_PROMISC) != 0 && !ppsratecheck(&sc->sc_last_syn, &sc->sc_false_syns, WI_MAX_FALSE_SYNS)) return; sc->sc_false_syns = MAX(0, sc->sc_false_syns - 1); #if 0 /* * XXX hack; we should create a new node with the new bssid * and replace the existing ic_bss with it but since we don't * process management frames to collect state we cheat by * reusing the existing node as we know wi_newstate will be * called and it will overwrite the node state. */ ieee80211_sta_join(ic, ieee80211_ref_node(ni)); #endif } static __noinline void wi_rx_intr(struct wi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct wi_frame frmhdr; struct mbuf *m; struct ieee80211_frame *wh; struct ieee80211_node *ni; int fid, len, off, rssi; u_int8_t dir; u_int16_t status; u_int32_t rstamp; fid = CSR_READ_2(sc, WI_RX_FID); /* First read in the frame header */ if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr))) { CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); ifp->if_ierrors++; DPRINTF(("wi_rx_intr: read fid %x failed\n", fid)); return; } /* * Drop undecryptable or packets with receive errors here */ status = le16toh(frmhdr.wi_status); if (status & WI_STAT_ERRSTAT) { CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); ifp->if_ierrors++; DPRINTF(("wi_rx_intr: fid %x error status %x\n", fid, status)); return; } rssi = frmhdr.wi_rx_signal; rstamp = (le16toh(frmhdr.wi_rx_tstamp0) << 16) | le16toh(frmhdr.wi_rx_tstamp1); len = le16toh(frmhdr.wi_dat_len); off = ALIGN(sizeof(struct ieee80211_frame)); /* * Sometimes the PRISM2.x returns bogusly large frames. Except * in monitor mode, just throw them away. */ if (off + len > MCLBYTES) { if (ic->ic_opmode != IEEE80211_M_MONITOR) { CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); ifp->if_ierrors++; DPRINTF(("wi_rx_intr: oversized packet\n")); return; } else len = 0; } if (off + len > MHLEN) m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); else m = m_gethdr(M_DONTWAIT, MT_DATA); if (m == NULL) { CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); ifp->if_ierrors++; DPRINTF(("wi_rx_intr: MGET failed\n")); return; } m->m_data += off - sizeof(struct ieee80211_frame); memcpy(m->m_data, &frmhdr.wi_whdr, sizeof(struct ieee80211_frame)); wi_read_bap(sc, fid, sizeof(frmhdr), m->m_data + sizeof(struct ieee80211_frame), len); m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame) + len; m->m_pkthdr.rcvif = ifp; CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); if (bpf_peers_present(ifp->if_bpf)) { /* XXX replace divide by table */ sc->sc_rx_th.wr_rate = frmhdr.wi_rx_rate / 5; sc->sc_rx_th.wr_antsignal = frmhdr.wi_rx_signal; sc->sc_rx_th.wr_antnoise = frmhdr.wi_rx_silence; sc->sc_rx_th.wr_flags = 0; if (frmhdr.wi_status & WI_STAT_PCF) sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_CFP; if (m->m_flags & M_WEP) sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_WEP; bpf_mtap2(ifp->if_bpf, &sc->sc_rx_th, sc->sc_rx_th_len, m); } /* synchronize driver's BSSID with firmware's BSSID */ wh = mtod(m, struct ieee80211_frame *); dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; if (ic->ic_opmode == IEEE80211_M_IBSS && dir == IEEE80211_FC1_DIR_NODS) wi_sync_bssid(sc, wh->i_addr3); WI_UNLOCK(sc); ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { (void) ieee80211_input(ni, m, rssi, -95/*XXX*/, rstamp); ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, rssi, -95/*XXX*/, rstamp); WI_LOCK(sc); } static __noinline void wi_tx_ex_intr(struct wi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct wi_frame frmhdr; int fid; fid = CSR_READ_2(sc, WI_TX_CMP_FID); /* Read in the frame header */ if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) == 0) { u_int16_t status = le16toh(frmhdr.wi_status); /* * Spontaneous station disconnects appear as xmit * errors. Don't announce them and/or count them * as an output error. */ if ((status & WI_TXSTAT_DISCONNECT) == 0) { if (ppsratecheck(&lasttxerror, &curtxeps, wi_txerate)) { if_printf(ifp, "tx failed"); if (status & WI_TXSTAT_RET_ERR) printf(", retry limit exceeded"); if (status & WI_TXSTAT_AGED_ERR) printf(", max transmit lifetime exceeded"); if (status & WI_TXSTAT_DISCONNECT) printf(", port disconnected"); if (status & WI_TXSTAT_FORM_ERR) printf(", invalid format (data len %u src %6D)", le16toh(frmhdr.wi_dat_len), frmhdr.wi_ehdr.ether_shost, ":"); if (status & ~0xf) printf(", status=0x%x", status); printf("\n"); } ifp->if_oerrors++; } else { DPRINTF(("port disconnected\n")); ifp->if_collisions++; /* XXX */ } } else DPRINTF(("wi_tx_ex_intr: read fid %x failed\n", fid)); CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); } static __noinline void wi_tx_intr(struct wi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; int fid, cur; if (sc->wi_gone) return; fid = CSR_READ_2(sc, WI_ALLOC_FID); CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); cur = sc->sc_txcur; if (sc->sc_txd[cur].d_fid != fid) { if_printf(ifp, "bad alloc %x != %x, cur %d nxt %d\n", fid, sc->sc_txd[cur].d_fid, cur, sc->sc_txnext); return; } sc->sc_tx_timer = 0; sc->sc_txd[cur].d_len = 0; sc->sc_txcur = cur = (cur + 1) % sc->sc_ntxbuf; if (sc->sc_txd[cur].d_len == 0) ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; else { if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, sc->sc_txd[cur].d_fid, 0, 0)) { if_printf(ifp, "xmit failed\n"); sc->sc_txd[cur].d_len = 0; } else { sc->sc_tx_timer = 5; } } } static void -wi_status_connected(void *arg, int pending) -{ - struct ieee80211vap *vap = arg; - struct ieee80211com *ic = vap->iv_ic; - - IEEE80211_LOCK(ic); - WI_VAP(vap)->wv_newstate(vap, IEEE80211_S_RUN, 0); - if (vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, IEEE80211_S_RUN, 0); - IEEE80211_UNLOCK(ic); -} - -static void -wi_status_disconnected(void *arg, int pending) -{ - struct ieee80211vap *vap = arg; - - if (vap->iv_state == IEEE80211_S_RUN) { - vap->iv_stats.is_rx_deauth++; - ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); - } -} - -static void wi_status_oor(void *arg, int pending) { struct ieee80211com *ic = arg; ieee80211_beacon_miss(ic); } -static void -wi_status_assoc_failed(void *arg, int pending) -{ - struct ieee80211vap *vap = arg; - - ieee80211_new_state(vap, IEEE80211_S_SCAN, IEEE80211_SCAN_FAIL_TIMEOUT); -} - static __noinline void wi_info_intr(struct wi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - struct wi_vap *wvp = WI_VAP(vap); int i, fid, len, off; u_int16_t ltbuf[2]; u_int16_t stat; u_int32_t *ptr; fid = CSR_READ_2(sc, WI_INFO_FID); wi_read_bap(sc, fid, 0, ltbuf, sizeof(ltbuf)); switch (le16toh(ltbuf[1])) { case WI_INFO_LINK_STAT: wi_read_bap(sc, fid, sizeof(ltbuf), &stat, sizeof(stat)); DPRINTF(("wi_info_intr: LINK_STAT 0x%x\n", le16toh(stat))); switch (le16toh(stat)) { case WI_INFO_LINK_STAT_CONNECTED: if (vap->iv_state == IEEE80211_S_RUN && vap->iv_opmode != IEEE80211_M_IBSS) break; /* fall thru... */ case WI_INFO_LINK_STAT_AP_CHG: - taskqueue_enqueue(taskqueue_swi, &wvp->wv_connected_task); + ieee80211_new_state(vap, IEEE80211_S_RUN, 0); break; case WI_INFO_LINK_STAT_AP_INR: break; case WI_INFO_LINK_STAT_DISCONNECTED: /* we dropped off the net; e.g. due to deauth/disassoc */ - taskqueue_enqueue(taskqueue_swi, &wvp->wv_disconnected_task); + ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); break; case WI_INFO_LINK_STAT_AP_OOR: /* XXX does this need to be per-vap? */ taskqueue_enqueue(taskqueue_swi, &sc->sc_oor_task); break; case WI_INFO_LINK_STAT_ASSOC_FAILED: if (vap->iv_opmode == IEEE80211_M_STA) - taskqueue_enqueue(taskqueue_swi, - &wvp->wv_assoc_failed_task); + ieee80211_new_state(vap, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_TIMEOUT); break; } break; case WI_INFO_COUNTERS: /* some card versions have a larger stats structure */ len = min(le16toh(ltbuf[0]) - 1, sizeof(sc->sc_stats) / 4); ptr = (u_int32_t *)&sc->sc_stats; off = sizeof(ltbuf); for (i = 0; i < len; i++, off += 2, ptr++) { wi_read_bap(sc, fid, off, &stat, sizeof(stat)); #ifdef WI_HERMES_STATS_WAR if (stat & 0xf000) stat = ~stat; #endif *ptr += stat; } ifp->if_collisions = sc->sc_stats.wi_tx_single_retries + sc->sc_stats.wi_tx_multi_retries + sc->sc_stats.wi_tx_retry_limit; break; default: DPRINTF(("wi_info_intr: got fid %x type %x len %d\n", fid, le16toh(ltbuf[1]), le16toh(ltbuf[0]))); break; } CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); } static int wi_write_multi(struct wi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; int n; struct ifmultiaddr *ifma; struct wi_mcast mlist; if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { allmulti: memset(&mlist, 0, sizeof(mlist)); return wi_write_rid(sc, WI_RID_MCAST_LIST, &mlist, sizeof(mlist)); } n = 0; IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; if (n >= 16) goto allmulti; IEEE80211_ADDR_COPY(&mlist.wi_mcast[n], (LLADDR((struct sockaddr_dl *)ifma->ifma_addr))); n++; } IF_ADDR_UNLOCK(ifp); return wi_write_rid(sc, WI_RID_MCAST_LIST, &mlist, IEEE80211_ADDR_LEN * n); } static void wi_update_mcast(struct ifnet *ifp) { wi_write_multi(ifp->if_softc); } static void wi_read_nicid(struct wi_softc *sc) { struct wi_card_ident *id; char *p; int len; u_int16_t ver[4]; /* getting chip identity */ memset(ver, 0, sizeof(ver)); len = sizeof(ver); wi_read_rid(sc, WI_RID_CARD_ID, ver, &len); sc->sc_firmware_type = WI_NOTYPE; sc->sc_nic_id = le16toh(ver[0]); for (id = wi_card_ident; id->card_name != NULL; id++) { if (sc->sc_nic_id == id->card_id) { sc->sc_nic_name = id->card_name; sc->sc_firmware_type = id->firm_type; break; } } if (sc->sc_firmware_type == WI_NOTYPE) { if (sc->sc_nic_id & 0x8000) { sc->sc_firmware_type = WI_INTERSIL; sc->sc_nic_name = "Unknown Prism chip"; } else { sc->sc_firmware_type = WI_LUCENT; sc->sc_nic_name = "Unknown Lucent chip"; } } if (bootverbose) device_printf(sc->sc_dev, "using %s\n", sc->sc_nic_name); /* get primary firmware version (Only Prism chips) */ if (sc->sc_firmware_type != WI_LUCENT) { memset(ver, 0, sizeof(ver)); len = sizeof(ver); wi_read_rid(sc, WI_RID_PRI_IDENTITY, ver, &len); sc->sc_pri_firmware_ver = le16toh(ver[2]) * 10000 + le16toh(ver[3]) * 100 + le16toh(ver[1]); } /* get station firmware version */ memset(ver, 0, sizeof(ver)); len = sizeof(ver); wi_read_rid(sc, WI_RID_STA_IDENTITY, ver, &len); sc->sc_sta_firmware_ver = le16toh(ver[2]) * 10000 + le16toh(ver[3]) * 100 + le16toh(ver[1]); if (sc->sc_firmware_type == WI_INTERSIL && (sc->sc_sta_firmware_ver == 10102 || sc->sc_sta_firmware_ver == 20102)) { char ident[12]; memset(ident, 0, sizeof(ident)); len = sizeof(ident); /* value should be the format like "V2.00-11" */ if (wi_read_rid(sc, WI_RID_SYMBOL_IDENTITY, ident, &len) == 0 && *(p = (char *)ident) >= 'A' && p[2] == '.' && p[5] == '-' && p[8] == '\0') { sc->sc_firmware_type = WI_SYMBOL; sc->sc_sta_firmware_ver = (p[1] - '0') * 10000 + (p[3] - '0') * 1000 + (p[4] - '0') * 100 + (p[6] - '0') * 10 + (p[7] - '0'); } } if (bootverbose) { device_printf(sc->sc_dev, "%s Firmware: ", wi_firmware_names[sc->sc_firmware_type]); if (sc->sc_firmware_type != WI_LUCENT) /* XXX */ printf("Primary (%u.%u.%u), ", sc->sc_pri_firmware_ver / 10000, (sc->sc_pri_firmware_ver % 10000) / 100, sc->sc_pri_firmware_ver % 100); printf("Station (%u.%u.%u)\n", sc->sc_sta_firmware_ver / 10000, (sc->sc_sta_firmware_ver % 10000) / 100, sc->sc_sta_firmware_ver % 100); } } static int wi_write_ssid(struct wi_softc *sc, int rid, u_int8_t *buf, int buflen) { struct wi_ssid ssid; if (buflen > IEEE80211_NWID_LEN) return ENOBUFS; memset(&ssid, 0, sizeof(ssid)); ssid.wi_len = htole16(buflen); memcpy(ssid.wi_ssid, buf, buflen); return wi_write_rid(sc, rid, &ssid, sizeof(ssid)); } static int wi_write_txrate(struct wi_softc *sc, struct ieee80211vap *vap) { static const uint16_t lucent_rates[12] = { [ 0] = 3, /* auto */ [ 1] = 1, /* 1Mb/s */ [ 2] = 2, /* 2Mb/s */ [ 5] = 4, /* 5.5Mb/s */ [11] = 5 /* 11Mb/s */ }; static const uint16_t intersil_rates[12] = { [ 0] = 0xf, /* auto */ [ 1] = 0, /* 1Mb/s */ [ 2] = 1, /* 2Mb/s */ [ 5] = 2, /* 5.5Mb/s */ [11] = 3, /* 11Mb/s */ }; const uint16_t *rates = sc->sc_firmware_type == WI_LUCENT ? lucent_rates : intersil_rates; struct ieee80211com *ic = vap->iv_ic; const struct ieee80211_txparam *tp; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; return wi_write_val(sc, WI_RID_TX_RATE, (tp->ucastrate == IEEE80211_FIXED_RATE_NONE ? rates[0] : rates[tp->ucastrate / 2])); } static int wi_write_wep(struct wi_softc *sc, struct ieee80211vap *vap) { int error = 0; int i, keylen; u_int16_t val; struct wi_key wkey[IEEE80211_WEP_NKID]; switch (sc->sc_firmware_type) { case WI_LUCENT: val = (vap->iv_flags & IEEE80211_F_PRIVACY) ? 1 : 0; error = wi_write_val(sc, WI_RID_ENCRYPTION, val); if (error) break; if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) break; error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, vap->iv_def_txkey); if (error) break; memset(wkey, 0, sizeof(wkey)); for (i = 0; i < IEEE80211_WEP_NKID; i++) { keylen = vap->iv_nw_keys[i].wk_keylen; wkey[i].wi_keylen = htole16(keylen); memcpy(wkey[i].wi_keydat, vap->iv_nw_keys[i].wk_key, keylen); } error = wi_write_rid(sc, WI_RID_DEFLT_CRYPT_KEYS, wkey, sizeof(wkey)); sc->sc_encryption = 0; break; case WI_INTERSIL: val = HOST_ENCRYPT | HOST_DECRYPT; if (vap->iv_flags & IEEE80211_F_PRIVACY) { /* * ONLY HWB3163 EVAL-CARD Firmware version * less than 0.8 variant2 * * If promiscuous mode disable, Prism2 chip * does not work with WEP . * It is under investigation for details. * (ichiro@netbsd.org) */ if (sc->sc_sta_firmware_ver < 802 ) { /* firm ver < 0.8 variant 2 */ wi_write_val(sc, WI_RID_PROMISC, 1); } wi_write_val(sc, WI_RID_CNFAUTHMODE, vap->iv_bss->ni_authmode); val |= PRIVACY_INVOKED; } else { wi_write_val(sc, WI_RID_CNFAUTHMODE, IEEE80211_AUTH_OPEN); } error = wi_write_val(sc, WI_RID_P2_ENCRYPTION, val); if (error) break; sc->sc_encryption = val; if ((val & PRIVACY_INVOKED) == 0) break; error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY, vap->iv_def_txkey); break; } return error; } static int wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2) { int i, s = 0; if (sc->wi_gone) return (ENODEV); /* wait for the busy bit to clear */ for (i = sc->wi_cmd_count; i > 0; i--) { /* 500ms */ if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) break; DELAY(1*1000); /* 1ms */ } if (i == 0) { device_printf(sc->sc_dev, "%s: busy bit won't clear, cmd 0x%x\n", __func__, cmd); sc->wi_gone = 1; return(ETIMEDOUT); } CSR_WRITE_2(sc, WI_PARAM0, val0); CSR_WRITE_2(sc, WI_PARAM1, val1); CSR_WRITE_2(sc, WI_PARAM2, val2); CSR_WRITE_2(sc, WI_COMMAND, cmd); if (cmd == WI_CMD_INI) { /* XXX: should sleep here. */ DELAY(100*1000); /* 100ms delay for init */ } for (i = 0; i < WI_TIMEOUT; i++) { /* * Wait for 'command complete' bit to be * set in the event status register. */ s = CSR_READ_2(sc, WI_EVENT_STAT); if (s & WI_EV_CMD) { /* Ack the event and read result code. */ s = CSR_READ_2(sc, WI_STATUS); CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); if (s & WI_STAT_CMD_RESULT) { return(EIO); } break; } DELAY(WI_DELAY); } if (i == WI_TIMEOUT) { device_printf(sc->sc_dev, "%s: timeout on cmd 0x%04x; " "event status 0x%04x\n", __func__, cmd, s); if (s == 0xffff) sc->wi_gone = 1; return(ETIMEDOUT); } return (0); } static int wi_seek_bap(struct wi_softc *sc, int id, int off) { int i, status; CSR_WRITE_2(sc, WI_SEL0, id); CSR_WRITE_2(sc, WI_OFF0, off); for (i = 0; ; i++) { status = CSR_READ_2(sc, WI_OFF0); if ((status & WI_OFF_BUSY) == 0) break; if (i == WI_TIMEOUT) { device_printf(sc->sc_dev, "%s: timeout, id %x off %x\n", __func__, id, off); sc->sc_bap_off = WI_OFF_ERR; /* invalidate */ if (status == 0xffff) sc->wi_gone = 1; return ETIMEDOUT; } DELAY(1); } if (status & WI_OFF_ERR) { device_printf(sc->sc_dev, "%s: error, id %x off %x\n", __func__, id, off); sc->sc_bap_off = WI_OFF_ERR; /* invalidate */ return EIO; } sc->sc_bap_id = id; sc->sc_bap_off = off; return 0; } static int wi_read_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen) { u_int16_t *ptr; int i, error, cnt; if (buflen == 0) return 0; if (id != sc->sc_bap_id || off != sc->sc_bap_off) { if ((error = wi_seek_bap(sc, id, off)) != 0) return error; } cnt = (buflen + 1) / 2; ptr = (u_int16_t *)buf; for (i = 0; i < cnt; i++) *ptr++ = CSR_READ_2(sc, WI_DATA0); sc->sc_bap_off += cnt * 2; return 0; } static int wi_write_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen) { u_int16_t *ptr; int i, error, cnt; if (buflen == 0) return 0; if (id != sc->sc_bap_id || off != sc->sc_bap_off) { if ((error = wi_seek_bap(sc, id, off)) != 0) return error; } cnt = (buflen + 1) / 2; ptr = (u_int16_t *)buf; for (i = 0; i < cnt; i++) CSR_WRITE_2(sc, WI_DATA0, ptr[i]); sc->sc_bap_off += cnt * 2; return 0; } static int wi_mwrite_bap(struct wi_softc *sc, int id, int off, struct mbuf *m0, int totlen) { int error, len; struct mbuf *m; for (m = m0; m != NULL && totlen > 0; m = m->m_next) { if (m->m_len == 0) continue; len = min(m->m_len, totlen); if (((u_long)m->m_data) % 2 != 0 || len % 2 != 0) { m_copydata(m, 0, totlen, (caddr_t)&sc->sc_txbuf); return wi_write_bap(sc, id, off, (caddr_t)&sc->sc_txbuf, totlen); } if ((error = wi_write_bap(sc, id, off, m->m_data, len)) != 0) return error; off += m->m_len; totlen -= len; } return 0; } static int wi_alloc_fid(struct wi_softc *sc, int len, int *idp) { int i; if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) { device_printf(sc->sc_dev, "%s: failed to allocate %d bytes on NIC\n", __func__, len); return ENOMEM; } for (i = 0; i < WI_TIMEOUT; i++) { if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) break; DELAY(1); } if (i == WI_TIMEOUT) { device_printf(sc->sc_dev, "%s: timeout in alloc\n", __func__); return ETIMEDOUT; } *idp = CSR_READ_2(sc, WI_ALLOC_FID); CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); return 0; } static int wi_read_rid(struct wi_softc *sc, int rid, void *buf, int *buflenp) { int error, len; u_int16_t ltbuf[2]; /* Tell the NIC to enter record read mode. */ error = wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_READ, rid, 0, 0); if (error) return error; error = wi_read_bap(sc, rid, 0, ltbuf, sizeof(ltbuf)); if (error) return error; if (le16toh(ltbuf[1]) != rid) { device_printf(sc->sc_dev, "record read mismatch, rid=%x, got=%x\n", rid, le16toh(ltbuf[1])); return EIO; } len = (le16toh(ltbuf[0]) - 1) * 2; /* already got rid */ if (*buflenp < len) { device_printf(sc->sc_dev, "record buffer is too small, " "rid=%x, size=%d, len=%d\n", rid, *buflenp, len); return ENOSPC; } *buflenp = len; return wi_read_bap(sc, rid, sizeof(ltbuf), buf, len); } static int wi_write_rid(struct wi_softc *sc, int rid, void *buf, int buflen) { int error; u_int16_t ltbuf[2]; ltbuf[0] = htole16((buflen + 1) / 2 + 1); /* includes rid */ ltbuf[1] = htole16(rid); error = wi_write_bap(sc, rid, 0, ltbuf, sizeof(ltbuf)); if (error) { device_printf(sc->sc_dev, "%s: bap0 write failure, rid 0x%x\n", __func__, rid); return error; } error = wi_write_bap(sc, rid, sizeof(ltbuf), buf, buflen); if (error) { device_printf(sc->sc_dev, "%s: bap1 write failure, rid 0x%x\n", __func__, rid); return error; } return wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_WRITE, rid, 0, 0); } static int wi_write_appie(struct wi_softc *sc, int rid, const struct ieee80211_appie *ie) { /* NB: 42 bytes is probably ok to have on the stack */ char buf[sizeof(uint16_t) + 40]; if (ie->ie_len > 40) return EINVAL; /* NB: firmware requires 16-bit ie length before ie data */ *(uint16_t *) buf = htole16(ie->ie_len); memcpy(buf + sizeof(uint16_t), ie->ie_data, ie->ie_len); return wi_write_rid(sc, rid, buf, ie->ie_len + sizeof(uint16_t)); } int wi_alloc(device_t dev, int rid) { struct wi_softc *sc = device_get_softc(dev); if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) { sc->iobase_rid = rid; sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->iobase_rid, 0, ~0, (1 << 6), rman_make_alignment_flags(1 << 6) | RF_ACTIVE); if (sc->iobase == NULL) { device_printf(dev, "No I/O space?!\n"); return ENXIO; } sc->wi_io_addr = rman_get_start(sc->iobase); sc->wi_btag = rman_get_bustag(sc->iobase); sc->wi_bhandle = rman_get_bushandle(sc->iobase); } else { sc->mem_rid = rid; sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem == NULL) { device_printf(dev, "No Mem space on prism2.5?\n"); return ENXIO; } sc->wi_btag = rman_get_bustag(sc->mem); sc->wi_bhandle = rman_get_bushandle(sc->mem); } sc->irq_rid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE | ((sc->wi_bus_type == WI_BUS_PCCARD) ? 0 : RF_SHAREABLE)); if (sc->irq == NULL) { wi_free(dev); device_printf(dev, "No irq?!\n"); return ENXIO; } sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); return 0; } void wi_free(device_t dev) { struct wi_softc *sc = device_get_softc(dev); if (sc->iobase != NULL) { bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase); sc->iobase = NULL; } if (sc->irq != NULL) { bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); sc->irq = NULL; } if (sc->mem != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); sc->mem = NULL; } } Index: user/thompsa/vaptq/sys/dev/wi/if_wivar.h =================================================================== --- user/thompsa/vaptq/sys/dev/wi/if_wivar.h (revision 185732) +++ user/thompsa/vaptq/sys/dev/wi/if_wivar.h (revision 185733) @@ -1,192 +1,189 @@ /*- * Copyright (c) 2002 * M Warner Losh . All rights reserved. * 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. * * $FreeBSD$ */ /* * Encryption controls. We can enable or disable encryption as * well as specify up to 4 encryption keys. We can also specify * which of the four keys will be used for transmit encryption. */ #define WI_RID_ENCRYPTION 0xFC20 #define WI_RID_AUTHTYPE 0xFC21 #define WI_RID_DEFLT_CRYPT_KEYS 0xFCB0 #define WI_RID_TX_CRYPT_KEY 0xFCB1 #define WI_RID_WEP_AVAIL 0xFD4F #define WI_RID_P2_TX_CRYPT_KEY 0xFC23 #define WI_RID_P2_CRYPT_KEY0 0xFC24 #define WI_RID_P2_CRYPT_KEY1 0xFC25 #define WI_RID_MICROWAVE_OVEN 0xFC25 #define WI_RID_P2_CRYPT_KEY2 0xFC26 #define WI_RID_P2_CRYPT_KEY3 0xFC27 #define WI_RID_P2_ENCRYPTION 0xFC28 #define WI_RID_ROAMING_MODE 0xFC2D #define WI_RID_CUR_TX_RATE 0xFD44 /* current TX rate */ #define WI_MAX_AID 256 /* max stations for ap operation */ struct wi_vap { struct ieee80211vap wv_vap; struct ieee80211_beacon_offsets wv_bo; - struct task wv_connected_task; - struct task wv_disconnected_task; - struct task wv_assoc_failed_task; void (*wv_recv_mgmt)(struct ieee80211_node *, struct mbuf *, int, int, int, u_int32_t); int (*wv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define WI_VAP(vap) ((struct wi_vap *)(vap)) struct wi_softc { struct ifnet *sc_ifp; device_t sc_dev; struct mtx sc_mtx; struct callout sc_watchdog; struct task sc_oor_task; int sc_unit; int wi_gone; int sc_enabled; int sc_reset; int sc_firmware_type; #define WI_NOTYPE 0 #define WI_LUCENT 1 #define WI_INTERSIL 2 #define WI_SYMBOL 3 int sc_pri_firmware_ver; /* Primary firmware */ int sc_sta_firmware_ver; /* Station firmware */ unsigned int sc_nic_id; /* Type of NIC */ char * sc_nic_name; int wi_bus_type; /* Bus attachment type */ struct resource * local; int local_rid; struct resource * iobase; int iobase_rid; struct resource * irq; int irq_rid; struct resource * mem; int mem_rid; bus_space_handle_t wi_localhandle; bus_space_tag_t wi_localtag; bus_space_handle_t wi_bhandle; bus_space_tag_t wi_btag; bus_space_handle_t wi_bmemhandle; bus_space_tag_t wi_bmemtag; void * wi_intrhand; struct ieee80211_channel *wi_channel; int wi_io_addr; int wi_cmd_count; int sc_flags; int sc_if_flags; int sc_bap_id; int sc_bap_off; int sc_porttype; u_int16_t sc_portnum; u_int16_t sc_encryption; u_int16_t sc_monitor_port; /* RSSI interpretation */ u_int16_t sc_min_rssi; /* clamp sc_min_rssi < RSSI */ u_int16_t sc_max_rssi; /* clamp RSSI < sc_max_rssi */ u_int16_t sc_dbm_offset; /* dBm ~ RSSI - sc_dbm_offset */ int sc_buflen; /* TX buffer size */ int sc_ntxbuf; #define WI_NTXBUF 3 struct { int d_fid; int d_len; } sc_txd[WI_NTXBUF]; /* TX buffers */ int sc_txnext; /* index of next TX */ int sc_txcur; /* index of current TX*/ int sc_tx_timer; struct wi_counters sc_stats; u_int16_t sc_ibss_port; struct timeval sc_last_syn; int sc_false_syns; u_int16_t sc_txbuf[IEEE80211_MAX_LEN/2]; struct wi_tx_radiotap_header sc_tx_th; int sc_tx_th_len; struct wi_rx_radiotap_header sc_rx_th; int sc_rx_th_len; }; /* maximum consecutive false change-of-BSSID indications */ #define WI_MAX_FALSE_SYNS 10 #define WI_FLAGS_HAS_ENHSECURITY 0x0001 #define WI_FLAGS_HAS_WPASUPPORT 0x0002 #define WI_FLAGS_HAS_ROAMING 0x0020 #define WI_FLAGS_HAS_FRAGTHR 0x0200 #define WI_FLAGS_HAS_DBMADJUST 0x0400 struct wi_card_ident { u_int16_t card_id; char *card_name; u_int8_t firm_type; }; #define WI_PRISM_MIN_RSSI 0x1b #define WI_PRISM_MAX_RSSI 0x9a #define WI_PRISM_DBM_OFFSET 100 /* XXX */ #define WI_LUCENT_MIN_RSSI 47 #define WI_LUCENT_MAX_RSSI 138 #define WI_LUCENT_DBM_OFFSET 149 #define WI_RSSI_TO_DBM(sc, rssi) (MIN((sc)->sc_max_rssi, \ MAX((sc)->sc_min_rssi, (rssi))) - (sc)->sc_dbm_offset) #define WI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define WI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define WI_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) int wi_attach(device_t); int wi_detach(device_t); void wi_shutdown(device_t); int wi_alloc(device_t, int); void wi_free(device_t); extern devclass_t wi_devclass; void wi_init(void *); void wi_intr(void *); int wi_mgmt_xmit(struct wi_softc *, caddr_t, int); void wi_stop(struct wi_softc *, int); Index: user/thompsa/vaptq/sys/dev/wpi/if_wpi.c =================================================================== --- user/thompsa/vaptq/sys/dev/wpi/if_wpi.c (revision 185732) +++ user/thompsa/vaptq/sys/dev/wpi/if_wpi.c (revision 185733) @@ -1,3844 +1,3822 @@ /*- * Copyright (c) 2006,2007 * Damien Bergamini * Benjamin Close * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define VERSION "20071127" #include __FBSDID("$FreeBSD$"); /* * Driver for Intel PRO/Wireless 3945ABG 802.11 network adapters. * * The 3945ABG network adapter doesn't use traditional hardware as * many other adaptors do. Instead at run time the eeprom is set into a known * state and told to load boot firmware. The boot firmware loads an init and a * main binary firmware image into SRAM on the card via DMA. * Once the firmware is loaded, the driver/hw then * communicate by way of circular dma rings via the the SRAM to the firmware. * * There is 6 memory rings. 1 command ring, 1 rx data ring & 4 tx data rings. * The 4 tx data rings allow for prioritization QoS. * * The rx data ring consists of 32 dma buffers. Two registers are used to * indicate where in the ring the driver and the firmware are up to. The * driver sets the initial read index (reg1) and the initial write index (reg2), * the firmware updates the read index (reg1) on rx of a packet and fires an * interrupt. The driver then processes the buffers starting at reg1 indicating * to the firmware which buffers have been accessed by updating reg2. At the * same time allocating new memory for the processed buffer. * * A similar thing happens with the tx rings. The difference is the firmware * stop processing buffers once the queue is full and until confirmation * of a successful transmition (tx_intr) has occurred. * * The command ring operates in the same manner as the tx queues. * * All communication direct to the card (ie eeprom) is classed as Stage1 * communication * * All communication via the firmware to the card is classed as State2. * The firmware consists of 2 parts. A bootstrap firmware and a runtime * firmware. The bootstrap firmware and runtime firmware are loaded * from host memory via dma to the card then told to execute. From this point * on the majority of communications between the driver and the card goes * via the firmware. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WPI_DEBUG #ifdef WPI_DEBUG #define DPRINTF(x) do { if (wpi_debug != 0) printf x; } while (0) #define DPRINTFN(n, x) do { if (wpi_debug & n) printf x; } while (0) #define WPI_DEBUG_SET (wpi_debug != 0) enum { WPI_DEBUG_UNUSED = 0x00000001, /* Unused */ WPI_DEBUG_HW = 0x00000002, /* Stage 1 (eeprom) debugging */ WPI_DEBUG_TX = 0x00000004, /* Stage 2 TX intrp debugging*/ WPI_DEBUG_RX = 0x00000008, /* Stage 2 RX intrp debugging */ WPI_DEBUG_CMD = 0x00000010, /* Stage 2 CMD intrp debugging*/ WPI_DEBUG_FIRMWARE = 0x00000020, /* firmware(9) loading debug */ WPI_DEBUG_DMA = 0x00000040, /* DMA (de)allocations/syncs */ WPI_DEBUG_SCANNING = 0x00000080, /* Stage 2 Scanning debugging */ WPI_DEBUG_NOTIFY = 0x00000100, /* State 2 Noftif intr debug */ WPI_DEBUG_TEMP = 0x00000200, /* TXPower/Temp Calibration */ WPI_DEBUG_OPS = 0x00000400, /* wpi_ops taskq debug */ WPI_DEBUG_WATCHDOG = 0x00000800, /* Watch dog debug */ WPI_DEBUG_ANY = 0xffffffff }; static int wpi_debug = 1; SYSCTL_INT(_debug, OID_AUTO, wpi, CTLFLAG_RW, &wpi_debug, 0, "wpi debug level"); TUNABLE_INT("debug.wpi", &wpi_debug); #else #define DPRINTF(x) #define DPRINTFN(n, x) #define WPI_DEBUG_SET 0 #endif struct wpi_ident { uint16_t vendor; uint16_t device; uint16_t subdevice; const char *name; }; static const struct wpi_ident wpi_ident_table[] = { /* The below entries support ABG regardless of the subid */ { 0x8086, 0x4222, 0x0, "Intel(R) PRO/Wireless 3945ABG" }, { 0x8086, 0x4227, 0x0, "Intel(R) PRO/Wireless 3945ABG" }, /* The below entries only support BG */ { 0x8086, 0x4222, 0x1005, "Intel(R) PRO/Wireless 3945BG" }, { 0x8086, 0x4222, 0x1034, "Intel(R) PRO/Wireless 3945BG" }, { 0x8086, 0x4227, 0x1014, "Intel(R) PRO/Wireless 3945BG" }, { 0x8086, 0x4222, 0x1044, "Intel(R) PRO/Wireless 3945BG" }, { 0, 0, 0, NULL } }; static struct ieee80211vap *wpi_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void wpi_vap_delete(struct ieee80211vap *); static int wpi_dma_contig_alloc(struct wpi_softc *, struct wpi_dma_info *, void **, bus_size_t, bus_size_t, int); static void wpi_dma_contig_free(struct wpi_dma_info *); static void wpi_dma_map_addr(void *, bus_dma_segment_t *, int, int); static int wpi_alloc_shared(struct wpi_softc *); static void wpi_free_shared(struct wpi_softc *); static int wpi_alloc_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); static void wpi_reset_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); static void wpi_free_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); static int wpi_alloc_tx_ring(struct wpi_softc *, struct wpi_tx_ring *, int, int); static void wpi_reset_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); static void wpi_free_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); static struct ieee80211_node *wpi_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); static int wpi_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void wpi_mem_lock(struct wpi_softc *); static void wpi_mem_unlock(struct wpi_softc *); static uint32_t wpi_mem_read(struct wpi_softc *, uint16_t); static void wpi_mem_write(struct wpi_softc *, uint16_t, uint32_t); static void wpi_mem_write_region_4(struct wpi_softc *, uint16_t, const uint32_t *, int); static uint16_t wpi_read_prom_data(struct wpi_softc *, uint32_t, void *, int); static int wpi_alloc_fwmem(struct wpi_softc *); static void wpi_free_fwmem(struct wpi_softc *); static int wpi_load_firmware(struct wpi_softc *); static void wpi_unload_firmware(struct wpi_softc *); static int wpi_load_microcode(struct wpi_softc *, const uint8_t *, int); static void wpi_rx_intr(struct wpi_softc *, struct wpi_rx_desc *, struct wpi_rx_data *); static void wpi_tx_intr(struct wpi_softc *, struct wpi_rx_desc *); static void wpi_cmd_intr(struct wpi_softc *, struct wpi_rx_desc *); static void wpi_bmiss(void *, int); static void wpi_notif_intr(struct wpi_softc *); static void wpi_intr(void *); static void wpi_ops(void *, int); static uint8_t wpi_plcp_signal(int); static int wpi_queue_cmd(struct wpi_softc *, int, int, int); static void wpi_watchdog(void *); static int wpi_tx_data(struct wpi_softc *, struct mbuf *, struct ieee80211_node *, int); static void wpi_start(struct ifnet *); static void wpi_start_locked(struct ifnet *); static int wpi_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void wpi_scan_start(struct ieee80211com *); static void wpi_scan_end(struct ieee80211com *); static void wpi_set_channel(struct ieee80211com *); static void wpi_scan_curchan(struct ieee80211_scan_state *, unsigned long); static void wpi_scan_mindwell(struct ieee80211_scan_state *); static int wpi_ioctl(struct ifnet *, u_long, caddr_t); static void wpi_read_eeprom(struct wpi_softc *); static void wpi_read_eeprom_channels(struct wpi_softc *, int); static void wpi_read_eeprom_group(struct wpi_softc *, int); static int wpi_cmd(struct wpi_softc *, int, const void *, int, int); static int wpi_wme_update(struct ieee80211com *); static int wpi_mrr_setup(struct wpi_softc *); static void wpi_set_led(struct wpi_softc *, uint8_t, uint8_t, uint8_t); static void wpi_enable_tsf(struct wpi_softc *, struct ieee80211_node *); #if 0 static int wpi_setup_beacon(struct wpi_softc *, struct ieee80211_node *); #endif static int wpi_auth(struct wpi_softc *, struct ieee80211vap *); static int wpi_run(struct wpi_softc *, struct ieee80211vap *); static int wpi_scan(struct wpi_softc *); static int wpi_config(struct wpi_softc *); static void wpi_stop_master(struct wpi_softc *); static int wpi_power_up(struct wpi_softc *); static int wpi_reset(struct wpi_softc *); static void wpi_hw_config(struct wpi_softc *); static void wpi_init(void *); static void wpi_init_locked(struct wpi_softc *, int); static void wpi_stop(struct wpi_softc *); static void wpi_stop_locked(struct wpi_softc *); static void wpi_newassoc(struct ieee80211_node *, int); static int wpi_set_txpower(struct wpi_softc *, struct ieee80211_channel *, int); static void wpi_calib_timeout(void *); static void wpi_power_calibration(struct wpi_softc *, int); static int wpi_get_power_index(struct wpi_softc *, struct wpi_power_group *, struct ieee80211_channel *, int); #ifdef WPI_DEBUG static const char *wpi_cmd_str(int); #endif static int wpi_probe(device_t); static int wpi_attach(device_t); static int wpi_detach(device_t); static int wpi_shutdown(device_t); static int wpi_suspend(device_t); static int wpi_resume(device_t); static device_method_t wpi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, wpi_probe), DEVMETHOD(device_attach, wpi_attach), DEVMETHOD(device_detach, wpi_detach), DEVMETHOD(device_shutdown, wpi_shutdown), DEVMETHOD(device_suspend, wpi_suspend), DEVMETHOD(device_resume, wpi_resume), { 0, 0 } }; static driver_t wpi_driver = { "wpi", wpi_methods, sizeof (struct wpi_softc) }; static devclass_t wpi_devclass; DRIVER_MODULE(wpi, pci, wpi_driver, wpi_devclass, 0, 0); static const uint8_t wpi_ridx_to_plcp[] = { /* OFDM: IEEE Std 802.11a-1999, pp. 14 Table 80 */ /* R1-R4 (ral/ural is R4-R1) */ 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, /* CCK: device-dependent */ 10, 20, 55, 110 }; static const uint8_t wpi_ridx_to_rate[] = { 12, 18, 24, 36, 48, 72, 96, 108, /* OFDM */ 2, 4, 11, 22 /*CCK */ }; static int wpi_probe(device_t dev) { const struct wpi_ident *ident; for (ident = wpi_ident_table; ident->name != NULL; ident++) { if (pci_get_vendor(dev) == ident->vendor && pci_get_device(dev) == ident->device) { device_set_desc(dev, ident->name); return 0; } } return ENXIO; } /** * Load the firmare image from disk to the allocated dma buffer. * we also maintain the reference to the firmware pointer as there * is times where we may need to reload the firmware but we are not * in a context that can access the filesystem (ie taskq cause by restart) * * @return 0 on success, an errno on failure */ static int wpi_load_firmware(struct wpi_softc *sc) { const struct firmware *fp; struct wpi_dma_info *dma = &sc->fw_dma; const struct wpi_firmware_hdr *hdr; const uint8_t *itext, *idata, *rtext, *rdata, *btext; uint32_t itextsz, idatasz, rtextsz, rdatasz, btextsz; int error; DPRINTFN(WPI_DEBUG_FIRMWARE, ("Attempting Loading Firmware from wpi_fw module\n")); WPI_UNLOCK(sc); if (sc->fw_fp == NULL && (sc->fw_fp = firmware_get("wpifw")) == NULL) { device_printf(sc->sc_dev, "could not load firmware image 'wpifw'\n"); error = ENOENT; WPI_LOCK(sc); goto fail; } fp = sc->fw_fp; WPI_LOCK(sc); /* Validate the firmware is minimum a particular version */ if (fp->version < WPI_FW_MINVERSION) { device_printf(sc->sc_dev, "firmware version is too old. Need %d, got %d\n", WPI_FW_MINVERSION, fp->version); error = ENXIO; goto fail; } if (fp->datasize < sizeof (struct wpi_firmware_hdr)) { device_printf(sc->sc_dev, "firmware file too short: %zu bytes\n", fp->datasize); error = ENXIO; goto fail; } hdr = (const struct wpi_firmware_hdr *)fp->data; /* | RUNTIME FIRMWARE | INIT FIRMWARE | BOOT FW | |HDR|<--TEXT-->|<--DATA-->|<--TEXT-->|<--DATA-->|<--TEXT-->| */ rtextsz = le32toh(hdr->rtextsz); rdatasz = le32toh(hdr->rdatasz); itextsz = le32toh(hdr->itextsz); idatasz = le32toh(hdr->idatasz); btextsz = le32toh(hdr->btextsz); /* check that all firmware segments are present */ if (fp->datasize < sizeof (struct wpi_firmware_hdr) + rtextsz + rdatasz + itextsz + idatasz + btextsz) { device_printf(sc->sc_dev, "firmware file too short: %zu bytes\n", fp->datasize); error = ENXIO; /* XXX appropriate error code? */ goto fail; } /* get pointers to firmware segments */ rtext = (const uint8_t *)(hdr + 1); rdata = rtext + rtextsz; itext = rdata + rdatasz; idata = itext + itextsz; btext = idata + idatasz; DPRINTFN(WPI_DEBUG_FIRMWARE, ("Firmware Version: Major %d, Minor %d, Driver %d, \n" "runtime (text: %u, data: %u) init (text: %u, data %u) boot (text %u)\n", (le32toh(hdr->version) & 0xff000000) >> 24, (le32toh(hdr->version) & 0x00ff0000) >> 16, (le32toh(hdr->version) & 0x0000ffff), rtextsz, rdatasz, itextsz, idatasz, btextsz)); DPRINTFN(WPI_DEBUG_FIRMWARE,("rtext 0x%x\n", *(const uint32_t *)rtext)); DPRINTFN(WPI_DEBUG_FIRMWARE,("rdata 0x%x\n", *(const uint32_t *)rdata)); DPRINTFN(WPI_DEBUG_FIRMWARE,("itext 0x%x\n", *(const uint32_t *)itext)); DPRINTFN(WPI_DEBUG_FIRMWARE,("idata 0x%x\n", *(const uint32_t *)idata)); DPRINTFN(WPI_DEBUG_FIRMWARE,("btext 0x%x\n", *(const uint32_t *)btext)); /* sanity checks */ if (rtextsz > WPI_FW_MAIN_TEXT_MAXSZ || rdatasz > WPI_FW_MAIN_DATA_MAXSZ || itextsz > WPI_FW_INIT_TEXT_MAXSZ || idatasz > WPI_FW_INIT_DATA_MAXSZ || btextsz > WPI_FW_BOOT_TEXT_MAXSZ || (btextsz & 3) != 0) { device_printf(sc->sc_dev, "firmware invalid\n"); error = EINVAL; goto fail; } /* copy initialization images into pre-allocated DMA-safe memory */ memcpy(dma->vaddr, idata, idatasz); memcpy(dma->vaddr + WPI_FW_INIT_DATA_MAXSZ, itext, itextsz); bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); /* tell adapter where to find initialization images */ wpi_mem_lock(sc); wpi_mem_write(sc, WPI_MEM_DATA_BASE, dma->paddr); wpi_mem_write(sc, WPI_MEM_DATA_SIZE, idatasz); wpi_mem_write(sc, WPI_MEM_TEXT_BASE, dma->paddr + WPI_FW_INIT_DATA_MAXSZ); wpi_mem_write(sc, WPI_MEM_TEXT_SIZE, itextsz); wpi_mem_unlock(sc); /* load firmware boot code */ if ((error = wpi_load_microcode(sc, btext, btextsz)) != 0) { device_printf(sc->sc_dev, "Failed to load microcode\n"); goto fail; } /* now press "execute" */ WPI_WRITE(sc, WPI_RESET, 0); /* wait at most one second for the first alive notification */ if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { device_printf(sc->sc_dev, "timeout waiting for adapter to initialize\n"); goto fail; } /* copy runtime images into pre-allocated DMA-sage memory */ memcpy(dma->vaddr, rdata, rdatasz); memcpy(dma->vaddr + WPI_FW_MAIN_DATA_MAXSZ, rtext, rtextsz); bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); /* tell adapter where to find runtime images */ wpi_mem_lock(sc); wpi_mem_write(sc, WPI_MEM_DATA_BASE, dma->paddr); wpi_mem_write(sc, WPI_MEM_DATA_SIZE, rdatasz); wpi_mem_write(sc, WPI_MEM_TEXT_BASE, dma->paddr + WPI_FW_MAIN_DATA_MAXSZ); wpi_mem_write(sc, WPI_MEM_TEXT_SIZE, WPI_FW_UPDATED | rtextsz); wpi_mem_unlock(sc); /* wait at most one second for the first alive notification */ if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { device_printf(sc->sc_dev, "timeout waiting for adapter to initialize2\n"); goto fail; } DPRINTFN(WPI_DEBUG_FIRMWARE, ("Firmware loaded to driver successfully\n")); return error; fail: wpi_unload_firmware(sc); return error; } /** * Free the referenced firmware image */ static void wpi_unload_firmware(struct wpi_softc *sc) { if (sc->fw_fp) { WPI_UNLOCK(sc); firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); WPI_LOCK(sc); sc->fw_fp = NULL; } } static int wpi_attach(device_t dev) { struct wpi_softc *sc = device_get_softc(dev); struct ifnet *ifp; struct ieee80211com *ic; int ac, error, supportsa = 1; uint32_t tmp; const struct wpi_ident *ident; sc->sc_dev = dev; if (bootverbose || WPI_DEBUG_SET) device_printf(sc->sc_dev,"Driver Revision %s\n", VERSION); /* * Some card's only support 802.11b/g not a, check to see if * this is one such card. A 0x0 in the subdevice table indicates * the entire subdevice range is to be ignored. */ for (ident = wpi_ident_table; ident->name != NULL; ident++) { if (ident->subdevice && pci_get_subdevice(dev) == ident->subdevice) { supportsa = 0; break; } } /* * Create the taskqueues used by the driver. Primarily * sc_tq handles most the task */ sc->sc_tq = taskqueue_create("wpi_taskq", M_NOWAIT | M_ZERO, taskqueue_thread_enqueue, &sc->sc_tq); taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(dev)); /* Create the tasks that can be queued */ TASK_INIT(&sc->sc_opstask, 0, wpi_ops, sc); TASK_INIT(&sc->sc_bmiss_task, 0, wpi_bmiss, sc); WPI_LOCK_INIT(sc); WPI_CMD_LOCK_INIT(sc); callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0); callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0); if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { device_printf(dev, "chip is in D%d power mode " "-- setting to D0\n", pci_get_powerstate(dev)); pci_set_powerstate(dev, PCI_POWERSTATE_D0); } /* disable the retry timeout register */ pci_write_config(dev, 0x41, 0, 1); /* enable bus-mastering */ pci_enable_busmaster(dev); sc->mem_rid = PCIR_BAR(0); sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem == NULL) { device_printf(dev, "could not allocate memory resource\n"); error = ENOMEM; goto fail; } sc->sc_st = rman_get_bustag(sc->mem); sc->sc_sh = rman_get_bushandle(sc->mem); sc->irq_rid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->irq == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); error = ENOMEM; goto fail; } /* * Allocate DMA memory for firmware transfers. */ if ((error = wpi_alloc_fwmem(sc)) != 0) { printf(": could not allocate firmware memory\n"); error = ENOMEM; goto fail; } /* * Put adapter into a known state. */ if ((error = wpi_reset(sc)) != 0) { device_printf(dev, "could not reset adapter\n"); goto fail; } wpi_mem_lock(sc); tmp = wpi_mem_read(sc, WPI_MEM_PCIDEV); if (bootverbose || WPI_DEBUG_SET) device_printf(sc->sc_dev, "Hardware Revision (0x%X)\n", tmp); wpi_mem_unlock(sc); /* Allocate shared page */ if ((error = wpi_alloc_shared(sc)) != 0) { device_printf(dev, "could not allocate shared page\n"); goto fail; } /* tx data queues - 4 for QoS purposes */ for (ac = 0; ac < WME_NUM_AC; ac++) { error = wpi_alloc_tx_ring(sc, &sc->txq[ac], WPI_TX_RING_COUNT, ac); if (error != 0) { device_printf(dev, "could not allocate Tx ring %d\n",ac); goto fail; } } /* command queue to talk to the card's firmware */ error = wpi_alloc_tx_ring(sc, &sc->cmdq, WPI_CMD_RING_COUNT, 4); if (error != 0) { device_printf(dev, "could not allocate command ring\n"); goto fail; } /* receive data queue */ error = wpi_alloc_rx_ring(sc, &sc->rxq); if (error != 0) { device_printf(dev, "could not allocate Rx ring\n"); goto fail; } ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); error = ENOMEM; goto fail; } ic = ifp->if_l2com; ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_WPA /* 802.11i */ /* XXX looks like WME is partly supported? */ #if 0 | IEEE80211_C_IBSS /* IBSS mode support */ | IEEE80211_C_BGSCAN /* capable of bg scanning */ | IEEE80211_C_WME /* 802.11e */ | IEEE80211_C_HOSTAP /* Host access point mode */ #endif ; /* * Read in the eeprom and also setup the channels for * net80211. We don't set the rates as net80211 does this for us */ wpi_read_eeprom(sc); if (bootverbose || WPI_DEBUG_SET) { device_printf(sc->sc_dev, "Regulatory Domain: %.4s\n", sc->domain); device_printf(sc->sc_dev, "Hardware Type: %c\n", sc->type > 1 ? 'B': '?'); device_printf(sc->sc_dev, "Hardware Revision: %c\n", ((le16toh(sc->rev) & 0xf0) == 0xd0) ? 'D': '?'); device_printf(sc->sc_dev, "SKU %s support 802.11a\n", supportsa ? "does" : "does not"); /* XXX hw_config uses the PCIDEV for the Hardware rev. Must check what sc->rev really represents - benjsc 20070615 */ } if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = wpi_init; ifp->if_ioctl = wpi_ioctl; ifp->if_start = wpi_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ieee80211_ifattach(ic); /* override default methods */ ic->ic_node_alloc = wpi_node_alloc; ic->ic_newassoc = wpi_newassoc; ic->ic_raw_xmit = wpi_raw_xmit; ic->ic_wme.wme_update = wpi_wme_update; ic->ic_scan_start = wpi_scan_start; ic->ic_scan_end = wpi_scan_end; ic->ic_set_channel = wpi_set_channel; ic->ic_scan_curchan = wpi_scan_curchan; ic->ic_scan_mindwell = wpi_scan_mindwell; ic->ic_vap_create = wpi_vap_create; ic->ic_vap_delete = wpi_vap_delete; bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap)); sc->sc_rxtap_len = sizeof sc->sc_rxtap; sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(WPI_RX_RADIOTAP_PRESENT); sc->sc_txtap_len = sizeof sc->sc_txtap; sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(WPI_TX_RADIOTAP_PRESENT); /* * Hook our interrupt after all initialization is complete. */ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET |INTR_MPSAFE, NULL, wpi_intr, sc, &sc->sc_ih); if (error != 0) { device_printf(dev, "could not set up interrupt\n"); goto fail; } if (bootverbose) ieee80211_announce(ic); #ifdef XXX_DEBUG ieee80211_announce_channels(ic); #endif return 0; fail: wpi_detach(dev); return ENXIO; } static int wpi_detach(device_t dev) { struct wpi_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int ac; if (ifp != NULL) { wpi_stop(sc); callout_drain(&sc->watchdog_to); callout_drain(&sc->calib_to); bpfdetach(ifp); ieee80211_ifdetach(ic); } WPI_LOCK(sc); if (sc->txq[0].data_dmat) { for (ac = 0; ac < WME_NUM_AC; ac++) wpi_free_tx_ring(sc, &sc->txq[ac]); wpi_free_tx_ring(sc, &sc->cmdq); wpi_free_rx_ring(sc, &sc->rxq); wpi_free_shared(sc); } if (sc->fw_fp != NULL) { wpi_unload_firmware(sc); } if (sc->fw_dma.tag) wpi_free_fwmem(sc); WPI_UNLOCK(sc); if (sc->irq != NULL) { bus_teardown_intr(dev, sc->irq, sc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); } if (sc->mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); if (ifp != NULL) if_free(ifp); taskqueue_free(sc->sc_tq); WPI_LOCK_DESTROY(sc); WPI_CMD_LOCK_DESTROY(sc); return 0; } static struct ieee80211vap * wpi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct wpi_vap *wvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; wvp = (struct wpi_vap *) malloc(sizeof(struct wpi_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (wvp == NULL) return NULL; vap = &wvp->vap; ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); /* override with driver methods */ wvp->newstate = vap->iv_newstate; vap->iv_newstate = wpi_newstate; ieee80211_amrr_init(&wvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 500 /*ms*/); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return vap; } static void wpi_vap_delete(struct ieee80211vap *vap) { struct wpi_vap *wvp = WPI_VAP(vap); ieee80211_amrr_cleanup(&wvp->amrr); ieee80211_vap_detach(vap); free(wvp, M_80211_VAP); } static void wpi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); *(bus_addr_t *)arg = segs[0].ds_addr; } /* * Allocates a contiguous block of dma memory of the requested size and * alignment. Due to limitations of the FreeBSD dma subsystem as of 20071217, * allocations greater than 4096 may fail. Hence if the requested alignment is * greater we allocate 'alignment' size extra memory and shift the vaddr and * paddr after the dma load. This bypasses the problem at the cost of a little * more memory. */ static int wpi_dma_contig_alloc(struct wpi_softc *sc, struct wpi_dma_info *dma, void **kvap, bus_size_t size, bus_size_t alignment, int flags) { int error; bus_size_t align; bus_size_t reqsize; DPRINTFN(WPI_DEBUG_DMA, ("Size: %zd - alignment %zd\n", size, alignment)); dma->size = size; dma->tag = NULL; if (alignment > 4096) { align = PAGE_SIZE; reqsize = size + alignment; } else { align = alignment; reqsize = size; } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), align, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, reqsize, 1, reqsize, flags, NULL, NULL, &dma->tag); if (error != 0) { device_printf(sc->sc_dev, "could not create shared page DMA tag\n"); goto fail; } error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr_start, flags | BUS_DMA_ZERO, &dma->map); if (error != 0) { device_printf(sc->sc_dev, "could not allocate shared page DMA memory\n"); goto fail; } error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr_start, reqsize, wpi_dma_map_addr, &dma->paddr_start, flags); /* Save the original pointers so we can free all the memory */ dma->paddr = dma->paddr_start; dma->vaddr = dma->vaddr_start; /* * Check the alignment and increment by 4096 until we get the * requested alignment. Fail if can't obtain the alignment * we requested. */ if ((dma->paddr & (alignment -1 )) != 0) { int i; for (i = 0; i < alignment / 4096; i++) { if ((dma->paddr & (alignment - 1 )) == 0) break; dma->paddr += 4096; dma->vaddr += 4096; } if (i == alignment / 4096) { device_printf(sc->sc_dev, "alignment requirement was not satisfied\n"); goto fail; } } if (error != 0) { device_printf(sc->sc_dev, "could not load shared page DMA map\n"); goto fail; } if (kvap != NULL) *kvap = dma->vaddr; return 0; fail: wpi_dma_contig_free(dma); return error; } static void wpi_dma_contig_free(struct wpi_dma_info *dma) { if (dma->tag) { if (dma->map != NULL) { if (dma->paddr_start != 0) { bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(dma->tag, dma->map); } bus_dmamem_free(dma->tag, &dma->vaddr_start, dma->map); } bus_dma_tag_destroy(dma->tag); } } /* * Allocate a shared page between host and NIC. */ static int wpi_alloc_shared(struct wpi_softc *sc) { int error; error = wpi_dma_contig_alloc(sc, &sc->shared_dma, (void **)&sc->shared, sizeof (struct wpi_shared), PAGE_SIZE, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "could not allocate shared area DMA memory\n"); } return error; } static void wpi_free_shared(struct wpi_softc *sc) { wpi_dma_contig_free(&sc->shared_dma); } static int wpi_alloc_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) { int i, error; ring->cur = 0; error = wpi_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, WPI_RX_RING_COUNT * sizeof (uint32_t), WPI_RING_DMA_ALIGN, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate rx ring DMA memory, error %d\n", __func__, error); goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dma_tag_create_failed, error %d\n", __func__, error); goto fail; } /* * Setup Rx buffers. */ for (i = 0; i < WPI_RX_RING_COUNT; i++) { struct wpi_rx_data *data = &ring->data[i]; struct mbuf *m; bus_addr_t paddr; error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dmamap_create failed, error %d\n", __func__, error); goto fail; } m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (m == NULL) { device_printf(sc->sc_dev, "%s: could not allocate rx mbuf\n", __func__); error = ENOMEM; goto fail; } /* map page */ error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m, caddr_t), MJUMPAGESIZE, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "%s: bus_dmamap_load failed, error %d\n", __func__, error); m_freem(m); error = ENOMEM; /* XXX unique code */ goto fail; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); data->m = m; ring->desc[i] = htole32(paddr); } bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); return 0; fail: wpi_free_rx_ring(sc, ring); return error; } static void wpi_reset_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) { int ntries; wpi_mem_lock(sc); WPI_WRITE(sc, WPI_RX_CONFIG, 0); for (ntries = 0; ntries < 100; ntries++) { if (WPI_READ(sc, WPI_RX_STATUS) & WPI_RX_IDLE) break; DELAY(10); } wpi_mem_unlock(sc); #ifdef WPI_DEBUG if (ntries == 100 && wpi_debug > 0) device_printf(sc->sc_dev, "timeout resetting Rx ring\n"); #endif ring->cur = 0; } static void wpi_free_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) { int i; wpi_dma_contig_free(&ring->desc_dma); for (i = 0; i < WPI_RX_RING_COUNT; i++) if (ring->data[i].m != NULL) m_freem(ring->data[i].m); } static int wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, int count, int qid) { struct wpi_tx_data *data; int i, error; ring->qid = qid; ring->count = count; ring->queued = 0; ring->cur = 0; ring->data = NULL; error = wpi_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, count * sizeof (struct wpi_tx_desc), WPI_RING_DMA_ALIGN, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "could not allocate tx dma memory\n"); goto fail; } /* update shared page with ring's base address */ sc->shared->txbase[qid] = htole32(ring->desc_dma.paddr); error = wpi_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, count * sizeof (struct wpi_tx_cmd), WPI_RING_DMA_ALIGN, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "could not allocate tx command DMA memory\n"); goto fail; } ring->data = malloc(count * sizeof (struct wpi_tx_data), M_DEVBUF, M_NOWAIT | M_ZERO); if (ring->data == NULL) { device_printf(sc->sc_dev, "could not allocate tx data slots\n"); goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, WPI_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create data DMA tag\n"); goto fail; } for (i = 0; i < count; i++) { data = &ring->data[i]; error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "could not create tx buf DMA map\n"); goto fail; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); } return 0; fail: wpi_free_tx_ring(sc, ring); return error; } static void wpi_reset_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) { struct wpi_tx_data *data; int i, ntries; wpi_mem_lock(sc); WPI_WRITE(sc, WPI_TX_CONFIG(ring->qid), 0); for (ntries = 0; ntries < 100; ntries++) { if (WPI_READ(sc, WPI_TX_STATUS) & WPI_TX_IDLE(ring->qid)) break; DELAY(10); } #ifdef WPI_DEBUG if (ntries == 100 && wpi_debug > 0) device_printf(sc->sc_dev, "timeout resetting Tx ring %d\n", ring->qid); #endif wpi_mem_unlock(sc); for (i = 0; i < ring->count; i++) { data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } } ring->queued = 0; ring->cur = 0; } static void wpi_free_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) { struct wpi_tx_data *data; int i; wpi_dma_contig_free(&ring->desc_dma); wpi_dma_contig_free(&ring->cmd_dma); if (ring->data != NULL) { for (i = 0; i < ring->count; i++) { data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } } free(ring->data, M_DEVBUF); } if (ring->data_dmat != NULL) bus_dma_tag_destroy(ring->data_dmat); } static int wpi_shutdown(device_t dev) { struct wpi_softc *sc = device_get_softc(dev); WPI_LOCK(sc); wpi_stop_locked(sc); wpi_unload_firmware(sc); WPI_UNLOCK(sc); return 0; } static int wpi_suspend(device_t dev) { struct wpi_softc *sc = device_get_softc(dev); wpi_stop(sc); return 0; } static int wpi_resume(device_t dev) { struct wpi_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; pci_write_config(dev, 0x41, 0, 1); if (ifp->if_flags & IFF_UP) { wpi_init(ifp->if_softc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) wpi_start(ifp); } return 0; } /* ARGSUSED */ static struct ieee80211_node * wpi_node_alloc(struct ieee80211vap *vap __unused, const uint8_t mac[IEEE80211_ADDR_LEN] __unused) { struct wpi_node *wn; wn = malloc(sizeof (struct wpi_node), M_80211_NODE, M_NOWAIT | M_ZERO); return &wn->ni; } /** * Called by net80211 when ever there is a change to 80211 state machine */ static int wpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct wpi_vap *wvp = WPI_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; int error; DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate], sc->flags)); + IEEE80211_UNLOCK(ic); + WPI_LOCK(sc); if (nstate == IEEE80211_S_AUTH) { - /* Delay the auth transition until we can update the firmware */ - error = wpi_queue_cmd(sc, WPI_AUTH, arg, WPI_QUEUE_NORMAL); - return (error != 0 ? error : EINPROGRESS); + /* The node must be registered in the firmware before auth */ + error = wpi_auth(sc, vap); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not move to auth state, error %d\n", + __func__, error); + } } if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) { - /* set the association id first */ - error = wpi_queue_cmd(sc, WPI_RUN, arg, WPI_QUEUE_NORMAL); - return (error != 0 ? error : EINPROGRESS); + error = wpi_run(sc, vap); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not move to run state, error %d\n", + __func__, error); + } } if (nstate == IEEE80211_S_RUN) { /* RUN -> RUN transition; just restart the timers */ wpi_calib_timeout(sc); /* XXX split out rate control timer */ } + WPI_UNLOCK(sc); + IEEE80211_LOCK(ic); return wvp->newstate(vap, nstate, arg); } /* * Grab exclusive access to NIC memory. */ static void wpi_mem_lock(struct wpi_softc *sc) { int ntries; uint32_t tmp; tmp = WPI_READ(sc, WPI_GPIO_CTL); WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_MAC); /* spin until we actually get the lock */ for (ntries = 0; ntries < 100; ntries++) { if ((WPI_READ(sc, WPI_GPIO_CTL) & (WPI_GPIO_CLOCK | WPI_GPIO_SLEEP)) == WPI_GPIO_CLOCK) break; DELAY(10); } if (ntries == 100) device_printf(sc->sc_dev, "could not lock memory\n"); } /* * Release lock on NIC memory. */ static void wpi_mem_unlock(struct wpi_softc *sc) { uint32_t tmp = WPI_READ(sc, WPI_GPIO_CTL); WPI_WRITE(sc, WPI_GPIO_CTL, tmp & ~WPI_GPIO_MAC); } static uint32_t wpi_mem_read(struct wpi_softc *sc, uint16_t addr) { WPI_WRITE(sc, WPI_READ_MEM_ADDR, WPI_MEM_4 | addr); return WPI_READ(sc, WPI_READ_MEM_DATA); } static void wpi_mem_write(struct wpi_softc *sc, uint16_t addr, uint32_t data) { WPI_WRITE(sc, WPI_WRITE_MEM_ADDR, WPI_MEM_4 | addr); WPI_WRITE(sc, WPI_WRITE_MEM_DATA, data); } static void wpi_mem_write_region_4(struct wpi_softc *sc, uint16_t addr, const uint32_t *data, int wlen) { for (; wlen > 0; wlen--, data++, addr+=4) wpi_mem_write(sc, addr, *data); } /* * Read data from the EEPROM. We access EEPROM through the MAC instead of * using the traditional bit-bang method. Data is read up until len bytes have * been obtained. */ static uint16_t wpi_read_prom_data(struct wpi_softc *sc, uint32_t addr, void *data, int len) { int ntries; uint32_t val; uint8_t *out = data; wpi_mem_lock(sc); for (; len > 0; len -= 2, addr++) { WPI_WRITE(sc, WPI_EEPROM_CTL, addr << 2); for (ntries = 0; ntries < 10; ntries++) { if ((val = WPI_READ(sc, WPI_EEPROM_CTL)) & WPI_EEPROM_READY) break; DELAY(5); } if (ntries == 10) { device_printf(sc->sc_dev, "could not read EEPROM\n"); return ETIMEDOUT; } *out++= val >> 16; if (len > 1) *out ++= val >> 24; } wpi_mem_unlock(sc); return 0; } /* * The firmware text and data segments are transferred to the NIC using DMA. * The driver just copies the firmware into DMA-safe memory and tells the NIC * where to find it. Once the NIC has copied the firmware into its internal * memory, we can free our local copy in the driver. */ static int wpi_load_microcode(struct wpi_softc *sc, const uint8_t *fw, int size) { int error, ntries; DPRINTFN(WPI_DEBUG_HW,("Loading microcode size 0x%x\n", size)); size /= sizeof(uint32_t); wpi_mem_lock(sc); wpi_mem_write_region_4(sc, WPI_MEM_UCODE_BASE, (const uint32_t *)fw, size); wpi_mem_write(sc, WPI_MEM_UCODE_SRC, 0); wpi_mem_write(sc, WPI_MEM_UCODE_DST, WPI_FW_TEXT); wpi_mem_write(sc, WPI_MEM_UCODE_SIZE, size); /* run microcode */ wpi_mem_write(sc, WPI_MEM_UCODE_CTL, WPI_UC_RUN); /* wait while the adapter is busy copying the firmware */ for (error = 0, ntries = 0; ntries < 1000; ntries++) { uint32_t status = WPI_READ(sc, WPI_TX_STATUS); DPRINTFN(WPI_DEBUG_HW, ("firmware status=0x%x, val=0x%x, result=0x%x\n", status, WPI_TX_IDLE(6), status & WPI_TX_IDLE(6))); if (status & WPI_TX_IDLE(6)) { DPRINTFN(WPI_DEBUG_HW, ("Status Match! - ntries = %d\n", ntries)); break; } DELAY(10); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout transferring firmware\n"); error = ETIMEDOUT; } /* start the microcode executing */ wpi_mem_write(sc, WPI_MEM_UCODE_CTL, WPI_UC_ENABLE); wpi_mem_unlock(sc); return (error); } static void wpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc, struct wpi_rx_data *data) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct wpi_rx_ring *ring = &sc->rxq; struct wpi_rx_stat *stat; struct wpi_rx_head *head; struct wpi_rx_tail *tail; struct ieee80211_node *ni; struct mbuf *m, *mnew; bus_addr_t paddr; int error; stat = (struct wpi_rx_stat *)(desc + 1); if (stat->len > WPI_STAT_MAXLEN) { device_printf(sc->sc_dev, "invalid rx statistic header\n"); ifp->if_ierrors++; return; } head = (struct wpi_rx_head *)((caddr_t)(stat + 1) + stat->len); tail = (struct wpi_rx_tail *)((caddr_t)(head + 1) + le16toh(head->len)); DPRINTFN(WPI_DEBUG_RX, ("rx intr: idx=%d len=%d stat len=%d rssi=%d " "rate=%x chan=%d tstamp=%ju\n", ring->cur, le32toh(desc->len), le16toh(head->len), (int8_t)stat->rssi, head->rate, head->chan, (uintmax_t)le64toh(tail->tstamp))); /* XXX don't need mbuf, just dma buffer */ mnew = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (mnew == NULL) { DPRINTFN(WPI_DEBUG_RX, ("%s: no mbuf to restock ring\n", __func__)); ic->ic_stats.is_rx_nobuf++; ifp->if_ierrors++; return; } error = bus_dmamap_load(ring->data_dmat, data->map, mtod(mnew, caddr_t), MJUMPAGESIZE, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "%s: bus_dmamap_load failed, error %d\n", __func__, error); m_freem(mnew); ic->ic_stats.is_rx_nobuf++; /* XXX need stat */ ifp->if_ierrors++; return; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); /* finalize mbuf and swap in new one */ m = data->m; m->m_pkthdr.rcvif = ifp; m->m_data = (caddr_t)(head + 1); m->m_pkthdr.len = m->m_len = le16toh(head->len); data->m = mnew; /* update Rx descriptor */ ring->desc[ring->cur] = htole32(paddr); if (bpf_peers_present(ifp->if_bpf)) { struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; tap->wr_chan_freq = htole16(ic->ic_channels[head->chan].ic_freq); tap->wr_chan_flags = htole16(ic->ic_channels[head->chan].ic_flags); tap->wr_dbm_antsignal = (int8_t)(stat->rssi - WPI_RSSI_OFFSET); tap->wr_dbm_antnoise = (int8_t)le16toh(stat->noise); tap->wr_tsft = tail->tstamp; tap->wr_antenna = (le16toh(head->flags) >> 4) & 0xf; switch (head->rate) { /* CCK rates */ case 10: tap->wr_rate = 2; break; case 20: tap->wr_rate = 4; break; case 55: tap->wr_rate = 11; break; case 110: tap->wr_rate = 22; break; /* OFDM rates */ case 0xd: tap->wr_rate = 12; break; case 0xf: tap->wr_rate = 18; break; case 0x5: tap->wr_rate = 24; break; case 0x7: tap->wr_rate = 36; break; case 0x9: tap->wr_rate = 48; break; case 0xb: tap->wr_rate = 72; break; case 0x1: tap->wr_rate = 96; break; case 0x3: tap->wr_rate = 108; break; /* unknown rate: should not happen */ default: tap->wr_rate = 0; } if (le16toh(head->flags) & 0x4) tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } WPI_UNLOCK(sc); ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { (void) ieee80211_input(ni, m, stat->rssi, 0, 0); ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, stat->rssi, 0, 0); WPI_LOCK(sc); } static void wpi_tx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc) { struct ifnet *ifp = sc->sc_ifp; struct wpi_tx_ring *ring = &sc->txq[desc->qid & 0x3]; struct wpi_tx_data *txdata = &ring->data[desc->idx]; struct wpi_tx_stat *stat = (struct wpi_tx_stat *)(desc + 1); struct wpi_node *wn = (struct wpi_node *)txdata->ni; DPRINTFN(WPI_DEBUG_TX, ("tx done: qid=%d idx=%d retries=%d nkill=%d " "rate=%x duration=%d status=%x\n", desc->qid, desc->idx, stat->ntries, stat->nkill, stat->rate, le32toh(stat->duration), le32toh(stat->status))); /* * Update rate control statistics for the node. * XXX we should not count mgmt frames since they're always sent at * the lowest available bit-rate. * XXX frames w/o ACK shouldn't be used either */ wn->amn.amn_txcnt++; if (stat->ntries > 0) { DPRINTFN(3, ("%d retries\n", stat->ntries)); wn->amn.amn_retrycnt++; } /* XXX oerrors should only count errors !maxtries */ if ((le32toh(stat->status) & 0xff) != 1) ifp->if_oerrors++; else ifp->if_opackets++; bus_dmamap_sync(ring->data_dmat, txdata->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, txdata->map); /* XXX handle M_TXCB? */ m_freem(txdata->m); txdata->m = NULL; ieee80211_free_node(txdata->ni); txdata->ni = NULL; ring->queued--; sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; wpi_start_locked(ifp); } static void wpi_cmd_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc) { struct wpi_tx_ring *ring = &sc->cmdq; struct wpi_tx_data *data; DPRINTFN(WPI_DEBUG_CMD, ("cmd notification qid=%x idx=%d flags=%x " "type=%s len=%d\n", desc->qid, desc->idx, desc->flags, wpi_cmd_str(desc->type), le32toh(desc->len))); if ((desc->qid & 7) != 4) return; /* not a command ack */ data = &ring->data[desc->idx]; /* if the command was mapped in a mbuf, free it */ if (data->m != NULL) { bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } sc->flags &= ~WPI_FLAG_BUSY; wakeup(&ring->cmd[desc->idx]); } static void wpi_bmiss(void *arg, int npending) { struct wpi_softc *sc = arg; struct ieee80211com *ic = sc->sc_ifp->if_l2com; ieee80211_beacon_miss(ic); } static void wpi_notif_intr(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct wpi_rx_desc *desc; struct wpi_rx_data *data; uint32_t hw; hw = le32toh(sc->shared->next); while (sc->rxq.cur != hw) { data = &sc->rxq.data[sc->rxq.cur]; desc = (void *)data->m->m_ext.ext_buf; DPRINTFN(WPI_DEBUG_NOTIFY, ("notify qid=%x idx=%d flags=%x type=%d len=%d\n", desc->qid, desc->idx, desc->flags, desc->type, le32toh(desc->len))); if (!(desc->qid & 0x80)) /* reply to a command */ wpi_cmd_intr(sc, desc); switch (desc->type) { case WPI_RX_DONE: /* a 802.11 frame was received */ wpi_rx_intr(sc, desc, data); break; case WPI_TX_DONE: /* a 802.11 frame has been transmitted */ wpi_tx_intr(sc, desc); break; case WPI_UC_READY: { struct wpi_ucode_info *uc = (struct wpi_ucode_info *)(desc + 1); /* the microcontroller is ready */ DPRINTF(("microcode alive notification version %x " "alive %x\n", le32toh(uc->version), le32toh(uc->valid))); if (le32toh(uc->valid) != 1) { device_printf(sc->sc_dev, "microcontroller initialization failed\n"); wpi_stop_locked(sc); } break; } case WPI_STATE_CHANGED: { uint32_t *status = (uint32_t *)(desc + 1); /* enabled/disabled notification */ DPRINTF(("state changed to %x\n", le32toh(*status))); if (le32toh(*status) & 1) { device_printf(sc->sc_dev, "Radio transmitter is switched off\n"); sc->flags |= WPI_FLAG_HW_RADIO_OFF; ifp->if_drv_flags &= ~IFF_DRV_RUNNING; /* Disable firmware commands */ WPI_WRITE(sc, WPI_UCODE_SET, WPI_DISABLE_CMD); } break; } case WPI_START_SCAN: { #ifdef WPI_DEBUG struct wpi_start_scan *scan = (struct wpi_start_scan *)(desc + 1); #endif DPRINTFN(WPI_DEBUG_SCANNING, ("scanning channel %d status %x\n", scan->chan, le32toh(scan->status))); break; } case WPI_STOP_SCAN: { #ifdef WPI_DEBUG struct wpi_stop_scan *scan = (struct wpi_stop_scan *)(desc + 1); #endif struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); DPRINTFN(WPI_DEBUG_SCANNING, ("scan finished nchan=%d status=%d chan=%d\n", scan->nchan, scan->status, scan->chan)); sc->sc_scan_timer = 0; ieee80211_scan_next(vap); break; } case WPI_MISSED_BEACON: { struct wpi_missed_beacon *beacon = (struct wpi_missed_beacon *)(desc + 1); struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (le32toh(beacon->consecutive) >= vap->iv_bmissthreshold) { DPRINTF(("Beacon miss: %u >= %u\n", le32toh(beacon->consecutive), vap->iv_bmissthreshold)); taskqueue_enqueue(taskqueue_swi, &sc->sc_bmiss_task); } break; } } sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT; } /* tell the firmware what we have processed */ hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; WPI_WRITE(sc, WPI_RX_WIDX, hw & ~7); } static void wpi_intr(void *arg) { struct wpi_softc *sc = arg; uint32_t r; WPI_LOCK(sc); r = WPI_READ(sc, WPI_INTR); if (r == 0 || r == 0xffffffff) { WPI_UNLOCK(sc); return; } /* disable interrupts */ WPI_WRITE(sc, WPI_MASK, 0); /* ack interrupts */ WPI_WRITE(sc, WPI_INTR, r); if (r & (WPI_SW_ERROR | WPI_HW_ERROR)) { device_printf(sc->sc_dev, "fatal firmware error\n"); DPRINTFN(6,("(%s)\n", (r & WPI_SW_ERROR) ? "(Software Error)" : "(Hardware Error)")); wpi_queue_cmd(sc, WPI_RESTART, 0, WPI_QUEUE_CLEAR); sc->flags &= ~WPI_FLAG_BUSY; WPI_UNLOCK(sc); return; } if (r & WPI_RX_INTR) wpi_notif_intr(sc); if (r & WPI_ALIVE_INTR) /* firmware initialized */ wakeup(sc); /* re-enable interrupts */ if (sc->sc_ifp->if_flags & IFF_UP) WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); WPI_UNLOCK(sc); } static uint8_t wpi_plcp_signal(int rate) { switch (rate) { /* CCK rates (returned values are device-dependent) */ case 2: return 10; case 4: return 20; case 11: return 55; case 22: return 110; /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ /* R1-R4 (ral/ural is R4-R1) */ case 12: return 0xd; case 18: return 0xf; case 24: return 0x5; case 36: return 0x7; case 48: return 0x9; case 72: return 0xb; case 96: return 0x1; case 108: return 0x3; /* unsupported rates (should not get there) */ default: return 0; } } /* quickly determine if a given rate is CCK or OFDM */ #define WPI_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) /* * Construct the data packet for a transmit buffer and acutally put * the buffer onto the transmit ring, kicking the card to process the * the buffer. */ static int wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, int ac) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams; struct wpi_tx_ring *ring = &sc->txq[ac]; struct wpi_tx_desc *desc; struct wpi_tx_data *data; struct wpi_tx_cmd *cmd; struct wpi_cmd_data *tx; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp; struct ieee80211_key *k; struct mbuf *mnew; int i, error, nsegs, rate, hdrlen, ismcast; bus_dma_segment_t segs[WPI_MAX_SCATTER]; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; wh = mtod(m0, struct ieee80211_frame *); hdrlen = ieee80211_hdrsize(wh); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } cmd = &ring->cmd[ring->cur]; cmd->code = WPI_CMD_TX_DATA; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; tx = (struct wpi_cmd_data *)cmd->data; tx->flags = htole32(WPI_TX_AUTO_SEQ); tx->timeout = htole16(0); tx->ofdm_mask = 0xff; tx->cck_mask = 0x0f; tx->lifetime = htole32(WPI_LIFETIME_INFINITE); tx->id = ismcast ? WPI_ID_BROADCAST : WPI_ID_BSS; tx->len = htole16(m0->m_pkthdr.len); if (!ismcast) { if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0 || !cap->cap_wmeParams[ac].wmep_noackPolicy) tx->flags |= htole32(WPI_TX_NEED_ACK); if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { tx->flags |= htole32(WPI_TX_NEED_RTS|WPI_TX_FULL_TXOP); tx->rts_ntries = 7; } } /* pick a rate */ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT) { uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; /* tell h/w to set timestamp in probe responses */ if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) tx->flags |= htole32(WPI_TX_INSERT_TSTAMP); if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) tx->timeout = htole16(3); else tx->timeout = htole16(2); rate = tp->mgmtrate; } else if (ismcast) { rate = tp->mcastrate; } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { rate = tp->ucastrate; } else { (void) ieee80211_amrr_choose(ni, &WPI_NODE(ni)->amn); rate = ni->ni_txrate; } tx->rate = wpi_plcp_signal(rate); /* be very persistant at sending frames out */ #if 0 tx->data_ntries = tp->maxretry; #else tx->data_ntries = 15; /* XXX way too high */ #endif if (bpf_peers_present(ifp->if_bpf)) { struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq); tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags); tap->wt_rate = rate; tap->wt_hwqueue = ac; if (wh->i_fc[1] & IEEE80211_FC1_WEP) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } /* save and trim IEEE802.11 header */ m_copydata(m0, 0, hdrlen, (caddr_t)&tx->wh); m_adj(m0, hdrlen); error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); m_freem(m0); return error; } if (error != 0) { /* XXX use m_collapse */ mnew = m_defrag(m0, M_DONTWAIT); if (mnew == NULL) { device_printf(sc->sc_dev, "could not defragment mbuf\n"); m_freem(m0); return ENOBUFS; } m0 = mnew; error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); m_freem(m0); return error; } } data->m = m0; data->ni = ni; DPRINTFN(WPI_DEBUG_TX, ("sending data: qid=%d idx=%d len=%d nsegs=%d\n", ring->qid, ring->cur, m0->m_pkthdr.len, nsegs)); /* first scatter/gather segment is used by the tx data command */ desc->flags = htole32(WPI_PAD32(m0->m_pkthdr.len) << 28 | (1 + nsegs) << 24); desc->segs[0].addr = htole32(ring->cmd_dma.paddr + ring->cur * sizeof (struct wpi_tx_cmd)); desc->segs[0].len = htole32(4 + sizeof (struct wpi_cmd_data)); for (i = 1; i <= nsegs; i++) { desc->segs[i].addr = htole32(segs[i - 1].ds_addr); desc->segs[i].len = htole32(segs[i - 1].ds_len); } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); ring->queued++; /* kick ring */ ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); return 0; } /** * Process data waiting to be sent on the IFNET output queue */ static void wpi_start(struct ifnet *ifp) { struct wpi_softc *sc = ifp->if_softc; WPI_LOCK(sc); wpi_start_locked(ifp); WPI_UNLOCK(sc); } static void wpi_start_locked(struct ifnet *ifp) { struct wpi_softc *sc = ifp->if_softc; struct ieee80211_node *ni; struct mbuf *m; int ac; WPI_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; for (;;) { IFQ_POLL(&ifp->if_snd, m); if (m == NULL) break; /* no QoS encapsulation for EAPOL frames */ ac = M_WME_GETAC(m); if (sc->txq[ac].queued > sc->txq[ac].count - 8) { /* there is no place left in this ring */ IFQ_DRV_PREPEND(&ifp->if_snd, m); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m); ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); ifp->if_oerrors++; continue; } if (wpi_tx_data(sc, m, ni, ac) != 0) { ieee80211_free_node(ni); ifp->if_oerrors++; break; } sc->sc_tx_timer = 5; } } static int wpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; /* prevent management frames from being sent if we're not ready */ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { m_freem(m); ieee80211_free_node(ni); return ENETDOWN; } WPI_LOCK(sc); /* management frames go into ring 0 */ if (sc->txq[0].queued > sc->txq[0].count - 8) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; m_freem(m); WPI_UNLOCK(sc); ieee80211_free_node(ni); return ENOBUFS; /* XXX */ } ifp->if_opackets++; if (wpi_tx_data(sc, m, ni, 0) != 0) goto bad; sc->sc_tx_timer = 5; callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); WPI_UNLOCK(sc); return 0; bad: ifp->if_oerrors++; WPI_UNLOCK(sc); ieee80211_free_node(ni); return EIO; /* XXX */ } static int wpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct wpi_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; switch (cmd) { case SIOCSIFFLAGS: WPI_LOCK(sc); if ((ifp->if_flags & IFF_UP)) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { wpi_init_locked(sc, 0); startall = 1; } } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) || (sc->flags & WPI_FLAG_HW_RADIO_OFF)) wpi_stop_locked(sc); WPI_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return error; } /* * Extract various information from EEPROM. */ static void wpi_read_eeprom(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int i; /* read the hardware capabilities, revision and SKU type */ wpi_read_prom_data(sc, WPI_EEPROM_CAPABILITIES, &sc->cap,1); wpi_read_prom_data(sc, WPI_EEPROM_REVISION, &sc->rev,2); wpi_read_prom_data(sc, WPI_EEPROM_TYPE, &sc->type, 1); /* read the regulatory domain */ wpi_read_prom_data(sc, WPI_EEPROM_DOMAIN, sc->domain, 4); /* read in the hw MAC address */ wpi_read_prom_data(sc, WPI_EEPROM_MAC, ic->ic_myaddr, 6); /* read the list of authorized channels */ for (i = 0; i < WPI_CHAN_BANDS_COUNT; i++) wpi_read_eeprom_channels(sc,i); /* read the power level calibration info for each group */ for (i = 0; i < WPI_POWER_GROUPS_COUNT; i++) wpi_read_eeprom_group(sc,i); } /* * Send a command to the firmware. */ static int wpi_cmd(struct wpi_softc *sc, int code, const void *buf, int size, int async) { struct wpi_tx_ring *ring = &sc->cmdq; struct wpi_tx_desc *desc; struct wpi_tx_cmd *cmd; #ifdef WPI_DEBUG if (!async) { WPI_LOCK_ASSERT(sc); } #endif DPRINTFN(WPI_DEBUG_CMD,("wpi_cmd %d size %d async %d\n", code, size, async)); if (sc->flags & WPI_FLAG_BUSY) { device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n", __func__, code); return EAGAIN; } sc->flags|= WPI_FLAG_BUSY; KASSERT(size <= sizeof cmd->data, ("command %d too large: %d bytes", code, size)); desc = &ring->desc[ring->cur]; cmd = &ring->cmd[ring->cur]; cmd->code = code; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; memcpy(cmd->data, buf, size); desc->flags = htole32(WPI_PAD32(size) << 28 | 1 << 24); desc->segs[0].addr = htole32(ring->cmd_dma.paddr + ring->cur * sizeof (struct wpi_tx_cmd)); desc->segs[0].len = htole32(4 + size); /* kick cmd ring */ ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); if (async) { sc->flags &= ~ WPI_FLAG_BUSY; return 0; } return msleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz); } static int wpi_wme_update(struct ieee80211com *ic) { #define WPI_EXP2(v) htole16((1 << (v)) - 1) #define WPI_USEC(v) htole16(IEEE80211_TXOP_TO_US(v)) struct wpi_softc *sc = ic->ic_ifp->if_softc; const struct wmeParams *wmep; struct wpi_wme_setup wme; int ac; /* don't override default WME values if WME is not actually enabled */ if (!(ic->ic_flags & IEEE80211_F_WME)) return 0; wme.flags = 0; for (ac = 0; ac < WME_NUM_AC; ac++) { wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; wme.ac[ac].aifsn = wmep->wmep_aifsn; wme.ac[ac].cwmin = WPI_EXP2(wmep->wmep_logcwmin); wme.ac[ac].cwmax = WPI_EXP2(wmep->wmep_logcwmax); wme.ac[ac].txop = WPI_USEC(wmep->wmep_txopLimit); DPRINTF(("setting WME for queue %d aifsn=%d cwmin=%d cwmax=%d " "txop=%d\n", ac, wme.ac[ac].aifsn, wme.ac[ac].cwmin, wme.ac[ac].cwmax, wme.ac[ac].txop)); } return wpi_cmd(sc, WPI_CMD_SET_WME, &wme, sizeof wme, 1); #undef WPI_USEC #undef WPI_EXP2 } /* * Configure h/w multi-rate retries. */ static int wpi_mrr_setup(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct wpi_mrr_setup mrr; int i, error; memset(&mrr, 0, sizeof (struct wpi_mrr_setup)); /* CCK rates (not used with 802.11a) */ for (i = WPI_CCK1; i <= WPI_CCK11; i++) { mrr.rates[i].flags = 0; mrr.rates[i].signal = wpi_ridx_to_plcp[i]; /* fallback to the immediate lower CCK rate (if any) */ mrr.rates[i].next = (i == WPI_CCK1) ? WPI_CCK1 : i - 1; /* try one time at this rate before falling back to "next" */ mrr.rates[i].ntries = 1; } /* OFDM rates (not used with 802.11b) */ for (i = WPI_OFDM6; i <= WPI_OFDM54; i++) { mrr.rates[i].flags = 0; mrr.rates[i].signal = wpi_ridx_to_plcp[i]; /* fallback to the immediate lower OFDM rate (if any) */ /* we allow fallback from OFDM/6 to CCK/2 in 11b/g mode */ mrr.rates[i].next = (i == WPI_OFDM6) ? ((ic->ic_curmode == IEEE80211_MODE_11A) ? WPI_OFDM6 : WPI_CCK2) : i - 1; /* try one time at this rate before falling back to "next" */ mrr.rates[i].ntries = 1; } /* setup MRR for control frames */ mrr.which = htole32(WPI_MRR_CTL); error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not setup MRR for control frames\n"); return error; } /* setup MRR for data frames */ mrr.which = htole32(WPI_MRR_DATA); error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not setup MRR for data frames\n"); return error; } return 0; } static void wpi_set_led(struct wpi_softc *sc, uint8_t which, uint8_t off, uint8_t on) { struct wpi_cmd_led led; led.which = which; led.unit = htole32(100000); /* on/off in unit of 100ms */ led.off = off; led.on = on; (void)wpi_cmd(sc, WPI_CMD_SET_LED, &led, sizeof led, 1); } static void wpi_enable_tsf(struct wpi_softc *sc, struct ieee80211_node *ni) { struct wpi_cmd_tsf tsf; uint64_t val, mod; memset(&tsf, 0, sizeof tsf); memcpy(&tsf.tstamp, ni->ni_tstamp.data, 8); tsf.bintval = htole16(ni->ni_intval); tsf.lintval = htole16(10); /* compute remaining time until next beacon */ val = (uint64_t)ni->ni_intval * 1024; /* msec -> usec */ mod = le64toh(tsf.tstamp) % val; tsf.binitval = htole32((uint32_t)(val - mod)); if (wpi_cmd(sc, WPI_CMD_TSF, &tsf, sizeof tsf, 1) != 0) device_printf(sc->sc_dev, "could not enable TSF\n"); } #if 0 /* * Build a beacon frame that the firmware will broadcast periodically in * IBSS or HostAP modes. */ static int wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct wpi_tx_ring *ring = &sc->cmdq; struct wpi_tx_desc *desc; struct wpi_tx_data *data; struct wpi_tx_cmd *cmd; struct wpi_cmd_beacon *bcn; struct ieee80211_beacon_offsets bo; struct mbuf *m0; bus_addr_t physaddr; int error; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; m0 = ieee80211_beacon_alloc(ic, ni, &bo); if (m0 == NULL) { device_printf(sc->sc_dev, "could not allocate beacon frame\n"); return ENOMEM; } cmd = &ring->cmd[ring->cur]; cmd->code = WPI_CMD_SET_BEACON; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; bcn = (struct wpi_cmd_beacon *)cmd->data; memset(bcn, 0, sizeof (struct wpi_cmd_beacon)); bcn->id = WPI_ID_BROADCAST; bcn->ofdm_mask = 0xff; bcn->cck_mask = 0x0f; bcn->lifetime = htole32(WPI_LIFETIME_INFINITE); bcn->len = htole16(m0->m_pkthdr.len); bcn->rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? wpi_plcp_signal(12) : wpi_plcp_signal(2); bcn->flags = htole32(WPI_TX_AUTO_SEQ | WPI_TX_INSERT_TSTAMP); /* save and trim IEEE802.11 header */ m_copydata(m0, 0, sizeof (struct ieee80211_frame), (caddr_t)&bcn->wh); m_adj(m0, sizeof (struct ieee80211_frame)); /* assume beacon frame is contiguous */ error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m0, void *), m0->m_pkthdr.len, wpi_dma_map_addr, &physaddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map beacon\n"); m_freem(m0); return error; } data->m = m0; /* first scatter/gather segment is used by the beacon command */ desc->flags = htole32(WPI_PAD32(m0->m_pkthdr.len) << 28 | 2 << 24); desc->segs[0].addr = htole32(ring->cmd_dma.paddr + ring->cur * sizeof (struct wpi_tx_cmd)); desc->segs[0].len = htole32(4 + sizeof (struct wpi_cmd_beacon)); desc->segs[1].addr = htole32(physaddr); desc->segs[1].len = htole32(m0->m_pkthdr.len); /* kick cmd ring */ ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); return 0; } #endif static int wpi_auth(struct wpi_softc *sc, struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni = vap->iv_bss; struct wpi_node_info node; int error; /* update adapter's configuration */ sc->config.associd = 0; sc->config.filter &= ~htole32(WPI_FILTER_BSS); IEEE80211_ADDR_COPY(sc->config.bssid, ni->ni_bssid); sc->config.chan = ieee80211_chan2ieee(ic, ni->ni_chan); if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) { sc->config.flags |= htole32(WPI_CONFIG_AUTO | WPI_CONFIG_24GHZ); } if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { sc->config.cck_mask = 0; sc->config.ofdm_mask = 0x15; } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { sc->config.cck_mask = 0x03; sc->config.ofdm_mask = 0; } else { /* XXX assume 802.11b/g */ sc->config.cck_mask = 0x0f; sc->config.ofdm_mask = 0x15; } DPRINTF(("config chan %d flags %x cck %x ofdm %x\n", sc->config.chan, sc->config.flags, sc->config.cck_mask, sc->config.ofdm_mask)); error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, sizeof (struct wpi_config), 1); if (error != 0) { device_printf(sc->sc_dev, "could not configure\n"); return error; } /* configuration has changed, set Tx power accordingly */ if ((error = wpi_set_txpower(sc, ni->ni_chan, 1)) != 0) { device_printf(sc->sc_dev, "could not set Tx power\n"); return error; } /* add default node */ memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.bssid, ni->ni_bssid); node.id = WPI_ID_BSS; node.rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? wpi_plcp_signal(12) : wpi_plcp_signal(2); node.action = htole32(WPI_ACTION_SET_RATE); node.antenna = WPI_ANTENNA_BOTH; error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); if (error != 0) device_printf(sc->sc_dev, "could not add BSS node\n"); return (error); } static int wpi_run(struct wpi_softc *sc, struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni = vap->iv_bss; int error; if (vap->iv_opmode == IEEE80211_M_MONITOR) { /* link LED blinks while monitoring */ wpi_set_led(sc, WPI_LED_LINK, 5, 5); return 0; } wpi_enable_tsf(sc, ni); /* update adapter's configuration */ sc->config.associd = htole16(ni->ni_associd & ~0xc000); /* short preamble/slot time are negotiated when associating */ sc->config.flags &= ~htole32(WPI_CONFIG_SHPREAMBLE | WPI_CONFIG_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHSLOT) sc->config.flags |= htole32(WPI_CONFIG_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) sc->config.flags |= htole32(WPI_CONFIG_SHPREAMBLE); sc->config.filter |= htole32(WPI_FILTER_BSS); /* XXX put somewhere HC_QOS_SUPPORT_ASSOC + HC_IBSS_START */ DPRINTF(("config chan %d flags %x\n", sc->config.chan, sc->config.flags)); error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, sizeof (struct wpi_config), 1); if (error != 0) { device_printf(sc->sc_dev, "could not update configuration\n"); return error; } error = wpi_set_txpower(sc, ni->ni_chan, 1); if (error != 0) { device_printf(sc->sc_dev, "could set txpower\n"); return error; } /* link LED always on while associated */ wpi_set_led(sc, WPI_LED_LINK, 0, 1); /* start automatic rate control timer */ callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); return (error); } /* * Send a scan request to the firmware. Since this command is huge, we map it * into a mbufcluster instead of using the pre-allocated set of commands. Note, * much of this code is similar to that in wpi_cmd but because we must manually * construct the probe & channels, we duplicate what's needed here. XXX In the * future, this function should be modified to use wpi_cmd to help cleanup the * code base. */ static int wpi_scan(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_scan_state *ss = ic->ic_scan; struct wpi_tx_ring *ring = &sc->cmdq; struct wpi_tx_desc *desc; struct wpi_tx_data *data; struct wpi_tx_cmd *cmd; struct wpi_scan_hdr *hdr; struct wpi_scan_chan *chan; struct ieee80211_frame *wh; struct ieee80211_rateset *rs; struct ieee80211_channel *c; enum ieee80211_phymode mode; uint8_t *frm; int nrates, pktlen, error, i, nssid; bus_addr_t physaddr; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (data->m == NULL) { device_printf(sc->sc_dev, "could not allocate mbuf for scan command\n"); return ENOMEM; } cmd = mtod(data->m, struct wpi_tx_cmd *); cmd->code = WPI_CMD_SCAN; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; hdr = (struct wpi_scan_hdr *)cmd->data; memset(hdr, 0, sizeof(struct wpi_scan_hdr)); /* * Move to the next channel if no packets are received within 5 msecs * after sending the probe request (this helps to reduce the duration * of active scans). */ hdr->quiet = htole16(5); hdr->threshold = htole16(1); if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) { /* send probe requests at 6Mbps */ hdr->tx.rate = wpi_ridx_to_plcp[WPI_OFDM6]; /* Enable crc checking */ hdr->promotion = htole16(1); } else { hdr->flags = htole32(WPI_CONFIG_24GHZ | WPI_CONFIG_AUTO); /* send probe requests at 1Mbps */ hdr->tx.rate = wpi_ridx_to_plcp[WPI_CCK1]; } hdr->tx.id = WPI_ID_BROADCAST; hdr->tx.lifetime = htole32(WPI_LIFETIME_INFINITE); hdr->tx.flags = htole32(WPI_TX_AUTO_SEQ); memset(hdr->scan_essids, 0, sizeof(hdr->scan_essids)); nssid = MIN(ss->ss_nssid, WPI_SCAN_MAX_ESSIDS); for (i = 0; i < nssid; i++) { hdr->scan_essids[i].id = IEEE80211_ELEMID_SSID; hdr->scan_essids[i].esslen = MIN(ss->ss_ssid[i].len, 32); memcpy(hdr->scan_essids[i].essid, ss->ss_ssid[i].ssid, hdr->scan_essids[i].esslen); #ifdef WPI_DEBUG if (wpi_debug & WPI_DEBUG_SCANNING) { printf("Scanning Essid: "); ieee80211_print_essid(hdr->scan_essids[i].essid, hdr->scan_essids[i].esslen); printf("\n"); } #endif } /* * Build a probe request frame. Most of the following code is a * copy & paste of what is done in net80211. */ wh = (struct ieee80211_frame *)&hdr->scan_essids[4]; wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ; wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr); *(u_int16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ *(u_int16_t *)&wh->i_seq[0] = 0; /* filled by h/w */ frm = (uint8_t *)(wh + 1); /* add essid IE, the hardware will fill this in for us */ *frm++ = IEEE80211_ELEMID_SSID; *frm++ = 0; mode = ieee80211_chan2mode(ic->ic_curchan); rs = &ic->ic_sup_rates[mode]; /* add supported rates IE */ *frm++ = IEEE80211_ELEMID_RATES; nrates = rs->rs_nrates; if (nrates > IEEE80211_RATE_SIZE) nrates = IEEE80211_RATE_SIZE; *frm++ = nrates; memcpy(frm, rs->rs_rates, nrates); frm += nrates; /* add supported xrates IE */ if (rs->rs_nrates > IEEE80211_RATE_SIZE) { nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; *frm++ = IEEE80211_ELEMID_XRATES; *frm++ = nrates; memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); frm += nrates; } /* setup length of probe request */ hdr->tx.len = htole16(frm - (uint8_t *)wh); /* * Construct information about the channel that we * want to scan. The firmware expects this to be directly * after the scan probe request */ c = ic->ic_curchan; chan = (struct wpi_scan_chan *)frm; chan->chan = ieee80211_chan2ieee(ic, c); chan->flags = 0; if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { chan->flags |= WPI_CHAN_ACTIVE; if (nssid != 0) chan->flags |= WPI_CHAN_DIRECT; } chan->gain_dsp = 0x6e; /* Default level */ if (IEEE80211_IS_CHAN_5GHZ(c)) { chan->active = htole16(10); chan->passive = htole16(ss->ss_maxdwell); chan->gain_radio = 0x3b; } else { chan->active = htole16(20); chan->passive = htole16(ss->ss_maxdwell); chan->gain_radio = 0x28; } DPRINTFN(WPI_DEBUG_SCANNING, ("Scanning %u Passive: %d\n", chan->chan, c->ic_flags & IEEE80211_CHAN_PASSIVE)); hdr->nchan++; chan++; frm += sizeof (struct wpi_scan_chan); #if 0 // XXX All Channels.... for (c = &ic->ic_channels[1]; c <= &ic->ic_channels[IEEE80211_CHAN_MAX]; c++) { if ((c->ic_flags & ic->ic_curchan->ic_flags) != ic->ic_curchan->ic_flags) continue; chan->chan = ieee80211_chan2ieee(ic, c); chan->flags = 0; if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { chan->flags |= WPI_CHAN_ACTIVE; if (ic->ic_des_ssid[0].len != 0) chan->flags |= WPI_CHAN_DIRECT; } chan->gain_dsp = 0x6e; /* Default level */ if (IEEE80211_IS_CHAN_5GHZ(c)) { chan->active = htole16(10); chan->passive = htole16(110); chan->gain_radio = 0x3b; } else { chan->active = htole16(20); chan->passive = htole16(120); chan->gain_radio = 0x28; } DPRINTFN(WPI_DEBUG_SCANNING, ("Scanning %u Passive: %d\n", chan->chan, c->ic_flags & IEEE80211_CHAN_PASSIVE)); hdr->nchan++; chan++; frm += sizeof (struct wpi_scan_chan); } #endif hdr->len = htole16(frm - (uint8_t *)hdr); pktlen = frm - (uint8_t *)cmd; error = bus_dmamap_load(ring->data_dmat, data->map, cmd, pktlen, wpi_dma_map_addr, &physaddr, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "could not map scan command\n"); m_freem(data->m); data->m = NULL; return error; } desc->flags = htole32(WPI_PAD32(pktlen) << 28 | 1 << 24); desc->segs[0].addr = htole32(physaddr); desc->segs[0].len = htole32(pktlen); bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); /* kick cmd ring */ ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); sc->sc_scan_timer = 5; return 0; /* will be notified async. of failure/success */ } /** * Configure the card to listen to a particular channel, this transisions the * card in to being able to receive frames from remote devices. */ static int wpi_config(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct wpi_power power; struct wpi_bluetooth bluetooth; struct wpi_node_info node; int error; /* set power mode */ memset(&power, 0, sizeof power); power.flags = htole32(WPI_POWER_CAM|0x8); error = wpi_cmd(sc, WPI_CMD_SET_POWER_MODE, &power, sizeof power, 0); if (error != 0) { device_printf(sc->sc_dev, "could not set power mode\n"); return error; } /* configure bluetooth coexistence */ memset(&bluetooth, 0, sizeof bluetooth); bluetooth.flags = 3; bluetooth.lead = 0xaa; bluetooth.kill = 1; error = wpi_cmd(sc, WPI_CMD_BLUETOOTH, &bluetooth, sizeof bluetooth, 0); if (error != 0) { device_printf(sc->sc_dev, "could not configure bluetooth coexistence\n"); return error; } /* configure adapter */ memset(&sc->config, 0, sizeof (struct wpi_config)); IEEE80211_ADDR_COPY(sc->config.myaddr, ic->ic_myaddr); /*set default channel*/ sc->config.chan = htole16(ieee80211_chan2ieee(ic, ic->ic_curchan)); sc->config.flags = htole32(WPI_CONFIG_TSF); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { sc->config.flags |= htole32(WPI_CONFIG_AUTO | WPI_CONFIG_24GHZ); } sc->config.filter = 0; switch (ic->ic_opmode) { case IEEE80211_M_STA: case IEEE80211_M_WDS: /* No know setup, use STA for now */ sc->config.mode = WPI_MODE_STA; sc->config.filter |= htole32(WPI_FILTER_MULTICAST); break; case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: sc->config.mode = WPI_MODE_IBSS; sc->config.filter |= htole32(WPI_FILTER_BEACON | WPI_FILTER_MULTICAST); break; case IEEE80211_M_HOSTAP: sc->config.mode = WPI_MODE_HOSTAP; break; case IEEE80211_M_MONITOR: sc->config.mode = WPI_MODE_MONITOR; sc->config.filter |= htole32(WPI_FILTER_MULTICAST | WPI_FILTER_CTL | WPI_FILTER_PROMISC); break; } sc->config.cck_mask = 0x0f; /* not yet negotiated */ sc->config.ofdm_mask = 0xff; /* not yet negotiated */ error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, sizeof (struct wpi_config), 0); if (error != 0) { device_printf(sc->sc_dev, "configure command failed\n"); return error; } /* configuration has changed, set Tx power accordingly */ if ((error = wpi_set_txpower(sc, ic->ic_curchan, 0)) != 0) { device_printf(sc->sc_dev, "could not set Tx power\n"); return error; } /* add broadcast node */ memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.bssid, ifp->if_broadcastaddr); node.id = WPI_ID_BROADCAST; node.rate = wpi_plcp_signal(2); error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 0); if (error != 0) { device_printf(sc->sc_dev, "could not add broadcast node\n"); return error; } /* Setup rate scalling */ error = wpi_mrr_setup(sc); if (error != 0) { device_printf(sc->sc_dev, "could not setup MRR\n"); return error; } return 0; } static void wpi_stop_master(struct wpi_softc *sc) { uint32_t tmp; int ntries; DPRINTFN(WPI_DEBUG_HW,("Disabling Firmware execution\n")); tmp = WPI_READ(sc, WPI_RESET); WPI_WRITE(sc, WPI_RESET, tmp | WPI_STOP_MASTER | WPI_NEVO_RESET); tmp = WPI_READ(sc, WPI_GPIO_CTL); if ((tmp & WPI_GPIO_PWR_STATUS) == WPI_GPIO_PWR_SLEEP) return; /* already asleep */ for (ntries = 0; ntries < 100; ntries++) { if (WPI_READ(sc, WPI_RESET) & WPI_MASTER_DISABLED) break; DELAY(10); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for master\n"); } } static int wpi_power_up(struct wpi_softc *sc) { uint32_t tmp; int ntries; wpi_mem_lock(sc); tmp = wpi_mem_read(sc, WPI_MEM_POWER); wpi_mem_write(sc, WPI_MEM_POWER, tmp & ~0x03000000); wpi_mem_unlock(sc); for (ntries = 0; ntries < 5000; ntries++) { if (WPI_READ(sc, WPI_GPIO_STATUS) & WPI_POWERED) break; DELAY(10); } if (ntries == 5000) { device_printf(sc->sc_dev, "timeout waiting for NIC to power up\n"); return ETIMEDOUT; } return 0; } static int wpi_reset(struct wpi_softc *sc) { uint32_t tmp; int ntries; DPRINTFN(WPI_DEBUG_HW, ("Resetting the card - clearing any uploaded firmware\n")); /* clear any pending interrupts */ WPI_WRITE(sc, WPI_INTR, 0xffffffff); tmp = WPI_READ(sc, WPI_PLL_CTL); WPI_WRITE(sc, WPI_PLL_CTL, tmp | WPI_PLL_INIT); tmp = WPI_READ(sc, WPI_CHICKEN); WPI_WRITE(sc, WPI_CHICKEN, tmp | WPI_CHICKEN_RXNOLOS); tmp = WPI_READ(sc, WPI_GPIO_CTL); WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_INIT); /* wait for clock stabilization */ for (ntries = 0; ntries < 25000; ntries++) { if (WPI_READ(sc, WPI_GPIO_CTL) & WPI_GPIO_CLOCK) break; DELAY(10); } if (ntries == 25000) { device_printf(sc->sc_dev, "timeout waiting for clock stabilization\n"); return ETIMEDOUT; } /* initialize EEPROM */ tmp = WPI_READ(sc, WPI_EEPROM_STATUS); if ((tmp & WPI_EEPROM_VERSION) == 0) { device_printf(sc->sc_dev, "EEPROM not found\n"); return EIO; } WPI_WRITE(sc, WPI_EEPROM_STATUS, tmp & ~WPI_EEPROM_LOCKED); return 0; } static void wpi_hw_config(struct wpi_softc *sc) { uint32_t rev, hw; /* voodoo from the Linux "driver".. */ hw = WPI_READ(sc, WPI_HWCONFIG); rev = pci_read_config(sc->sc_dev, PCIR_REVID, 1); if ((rev & 0xc0) == 0x40) hw |= WPI_HW_ALM_MB; else if (!(rev & 0x80)) hw |= WPI_HW_ALM_MM; if (sc->cap == 0x80) hw |= WPI_HW_SKU_MRC; hw &= ~WPI_HW_REV_D; if ((le16toh(sc->rev) & 0xf0) == 0xd0) hw |= WPI_HW_REV_D; if (sc->type > 1) hw |= WPI_HW_TYPE_B; WPI_WRITE(sc, WPI_HWCONFIG, hw); } static void wpi_rfkill_resume(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); int ntries; /* enable firmware again */ WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); WPI_WRITE(sc, WPI_UCODE_CLR, WPI_DISABLE_CMD); /* wait for thermal sensors to calibrate */ for (ntries = 0; ntries < 1000; ntries++) { if ((sc->temp = (int)WPI_READ(sc, WPI_TEMPERATURE)) != 0) break; DELAY(10); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for thermal calibration\n"); WPI_UNLOCK(sc); return; } DPRINTFN(WPI_DEBUG_TEMP,("temperature %d\n", sc->temp)); if (wpi_config(sc) != 0) { device_printf(sc->sc_dev, "device config failed\n"); WPI_UNLOCK(sc); return; } ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; if (vap != NULL) { if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { if (vap->iv_opmode != IEEE80211_M_MONITOR) { taskqueue_enqueue(taskqueue_swi, &sc->sc_bmiss_task); wpi_set_led(sc, WPI_LED_LINK, 0, 1); } else wpi_set_led(sc, WPI_LED_LINK, 5, 5); } else { ieee80211_scan_next(vap); wpi_set_led(sc, WPI_LED_LINK, 20, 2); } } callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); } static void wpi_init_locked(struct wpi_softc *sc, int force) { struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; int ntries, qid; wpi_stop_locked(sc); (void)wpi_reset(sc); wpi_mem_lock(sc); wpi_mem_write(sc, WPI_MEM_CLOCK1, 0xa00); DELAY(20); tmp = wpi_mem_read(sc, WPI_MEM_PCIDEV); wpi_mem_write(sc, WPI_MEM_PCIDEV, tmp | 0x800); wpi_mem_unlock(sc); (void)wpi_power_up(sc); wpi_hw_config(sc); /* init Rx ring */ wpi_mem_lock(sc); WPI_WRITE(sc, WPI_RX_BASE, sc->rxq.desc_dma.paddr); WPI_WRITE(sc, WPI_RX_RIDX_PTR, sc->shared_dma.paddr + offsetof(struct wpi_shared, next)); WPI_WRITE(sc, WPI_RX_WIDX, (WPI_RX_RING_COUNT - 1) & ~7); WPI_WRITE(sc, WPI_RX_CONFIG, 0xa9601010); wpi_mem_unlock(sc); /* init Tx rings */ wpi_mem_lock(sc); wpi_mem_write(sc, WPI_MEM_MODE, 2); /* bypass mode */ wpi_mem_write(sc, WPI_MEM_RA, 1); /* enable RA0 */ wpi_mem_write(sc, WPI_MEM_TXCFG, 0x3f); /* enable all 6 Tx rings */ wpi_mem_write(sc, WPI_MEM_BYPASS1, 0x10000); wpi_mem_write(sc, WPI_MEM_BYPASS2, 0x30002); wpi_mem_write(sc, WPI_MEM_MAGIC4, 4); wpi_mem_write(sc, WPI_MEM_MAGIC5, 5); WPI_WRITE(sc, WPI_TX_BASE_PTR, sc->shared_dma.paddr); WPI_WRITE(sc, WPI_MSG_CONFIG, 0xffff05a5); for (qid = 0; qid < 6; qid++) { WPI_WRITE(sc, WPI_TX_CTL(qid), 0); WPI_WRITE(sc, WPI_TX_BASE(qid), 0); WPI_WRITE(sc, WPI_TX_CONFIG(qid), 0x80200008); } wpi_mem_unlock(sc); /* clear "radio off" and "disable command" bits (reversed logic) */ WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); WPI_WRITE(sc, WPI_UCODE_CLR, WPI_DISABLE_CMD); sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; /* clear any pending interrupts */ WPI_WRITE(sc, WPI_INTR, 0xffffffff); /* enable interrupts */ WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); if ((wpi_load_firmware(sc)) != 0) { device_printf(sc->sc_dev, "A problem occurred loading the firmware to the driver\n"); return; } /* At this point the firmware is up and running. If the hardware * RF switch is turned off thermal calibration will fail, though * the card is still happy to continue to accept commands, catch * this case and schedule a task to watch for it to be turned on. */ wpi_mem_lock(sc); tmp = wpi_mem_read(sc, WPI_MEM_HW_RADIO_OFF); wpi_mem_unlock(sc); if (!(tmp & 0x1)) { sc->flags |= WPI_FLAG_HW_RADIO_OFF; device_printf(sc->sc_dev,"Radio Transmitter is switched off\n"); goto out; } /* wait for thermal sensors to calibrate */ for (ntries = 0; ntries < 1000; ntries++) { if ((sc->temp = (int)WPI_READ(sc, WPI_TEMPERATURE)) != 0) break; DELAY(10); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for thermal sensors calibration\n"); return; } DPRINTFN(WPI_DEBUG_TEMP,("temperature %d\n", sc->temp)); if (wpi_config(sc) != 0) { device_printf(sc->sc_dev, "device config failed\n"); return; } ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; out: callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); } static void wpi_init(void *arg) { struct wpi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; WPI_LOCK(sc); wpi_init_locked(sc, 0); WPI_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); /* start all vaps */ } static void wpi_stop_locked(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; int ac; sc->sc_tx_timer = 0; sc->sc_scan_timer = 0; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; callout_stop(&sc->watchdog_to); callout_stop(&sc->calib_to); /* disable interrupts */ WPI_WRITE(sc, WPI_MASK, 0); WPI_WRITE(sc, WPI_INTR, WPI_INTR_MASK); WPI_WRITE(sc, WPI_INTR_STATUS, 0xff); WPI_WRITE(sc, WPI_INTR_STATUS, 0x00070000); /* Clear any commands left in the command buffer */ memset(sc->sc_cmd, 0, sizeof(sc->sc_cmd)); memset(sc->sc_cmd_arg, 0, sizeof(sc->sc_cmd_arg)); sc->sc_cmd_cur = 0; sc->sc_cmd_next = 0; wpi_mem_lock(sc); wpi_mem_write(sc, WPI_MEM_MODE, 0); wpi_mem_unlock(sc); /* reset all Tx rings */ for (ac = 0; ac < 4; ac++) wpi_reset_tx_ring(sc, &sc->txq[ac]); wpi_reset_tx_ring(sc, &sc->cmdq); /* reset Rx ring */ wpi_reset_rx_ring(sc, &sc->rxq); wpi_mem_lock(sc); wpi_mem_write(sc, WPI_MEM_CLOCK2, 0x200); wpi_mem_unlock(sc); DELAY(5); wpi_stop_master(sc); tmp = WPI_READ(sc, WPI_RESET); WPI_WRITE(sc, WPI_RESET, tmp | WPI_SW_RESET); sc->flags &= ~WPI_FLAG_BUSY; } static void wpi_stop(struct wpi_softc *sc) { WPI_LOCK(sc); wpi_stop_locked(sc); WPI_UNLOCK(sc); } static void wpi_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; struct wpi_vap *wvp = WPI_VAP(vap); ieee80211_amrr_node_init(&wvp->amrr, &WPI_NODE(ni)->amn, ni); } static void wpi_calib_timeout(void *arg) { struct wpi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); int temp; if (vap->iv_state != IEEE80211_S_RUN) return; /* update sensor data */ temp = (int)WPI_READ(sc, WPI_TEMPERATURE); DPRINTFN(WPI_DEBUG_TEMP,("Temp in calibration is: %d\n", temp)); wpi_power_calibration(sc, temp); callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); } /* * This function is called periodically (every 60 seconds) to adjust output * power to temperature changes. */ static void wpi_power_calibration(struct wpi_softc *sc, int temp) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); /* sanity-check read value */ if (temp < -260 || temp > 25) { /* this can't be correct, ignore */ DPRINTFN(WPI_DEBUG_TEMP, ("out-of-range temperature reported: %d\n", temp)); return; } DPRINTFN(WPI_DEBUG_TEMP,("temperature %d->%d\n", sc->temp, temp)); /* adjust Tx power if need be */ if (abs(temp - sc->temp) <= 6) return; sc->temp = temp; if (wpi_set_txpower(sc, vap->iv_bss->ni_chan, 1) != 0) { /* just warn, too bad for the automatic calibration... */ device_printf(sc->sc_dev,"could not adjust Tx power\n"); } } /** * Read the eeprom to find out what channels are valid for the given * band and update net80211 with what we find. */ static void wpi_read_eeprom_channels(struct wpi_softc *sc, int n) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; const struct wpi_chan_band *band = &wpi_bands[n]; struct wpi_eeprom_chan channels[WPI_MAX_CHAN_PER_BAND]; struct ieee80211_channel *c; int chan, i, passive; wpi_read_prom_data(sc, band->addr, channels, band->nchan * sizeof (struct wpi_eeprom_chan)); for (i = 0; i < band->nchan; i++) { if (!(channels[i].flags & WPI_EEPROM_CHAN_VALID)) { DPRINTFN(WPI_DEBUG_HW, ("Channel Not Valid: %d, band %d\n", band->chan[i],n)); continue; } passive = 0; chan = band->chan[i]; c = &ic->ic_channels[ic->ic_nchans++]; /* is active scan allowed on this channel? */ if (!(channels[i].flags & WPI_EEPROM_CHAN_ACTIVE)) { passive = IEEE80211_CHAN_PASSIVE; } if (n == 0) { /* 2GHz band */ c->ic_ieee = chan; c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ); c->ic_flags = IEEE80211_CHAN_B | passive; c = &ic->ic_channels[ic->ic_nchans++]; c->ic_ieee = chan; c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ); c->ic_flags = IEEE80211_CHAN_G | passive; } else { /* 5GHz band */ /* * Some 3945ABG adapters support channels 7, 8, 11 * and 12 in the 2GHz *and* 5GHz bands. * Because of limitations in our net80211(9) stack, * we can't support these channels in 5GHz band. * XXX not true; just need to map to proper frequency */ if (chan <= 14) continue; c->ic_ieee = chan; c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ); c->ic_flags = IEEE80211_CHAN_A | passive; } /* save maximum allowed power for this channel */ sc->maxpwr[chan] = channels[i].maxpwr; #if 0 // XXX We can probably use this an get rid of maxpwr - ben 20070617 ic->ic_channels[chan].ic_maxpower = channels[i].maxpwr; //ic->ic_channels[chan].ic_minpower... //ic->ic_channels[chan].ic_maxregtxpower... #endif DPRINTF(("adding chan %d (%dMHz) flags=0x%x maxpwr=%d" " passive=%d, offset %d\n", chan, c->ic_freq, channels[i].flags, sc->maxpwr[chan], (c->ic_flags & IEEE80211_CHAN_PASSIVE) != 0, ic->ic_nchans)); } } static void wpi_read_eeprom_group(struct wpi_softc *sc, int n) { struct wpi_power_group *group = &sc->groups[n]; struct wpi_eeprom_group rgroup; int i; wpi_read_prom_data(sc, WPI_EEPROM_POWER_GRP + n * 32, &rgroup, sizeof rgroup); /* save power group information */ group->chan = rgroup.chan; group->maxpwr = rgroup.maxpwr; /* temperature at which the samples were taken */ group->temp = (int16_t)le16toh(rgroup.temp); DPRINTF(("power group %d: chan=%d maxpwr=%d temp=%d\n", n, group->chan, group->maxpwr, group->temp)); for (i = 0; i < WPI_SAMPLES_COUNT; i++) { group->samples[i].index = rgroup.samples[i].index; group->samples[i].power = rgroup.samples[i].power; DPRINTF(("\tsample %d: index=%d power=%d\n", i, group->samples[i].index, group->samples[i].power)); } } /* * Update Tx power to match what is defined for channel `c'. */ static int wpi_set_txpower(struct wpi_softc *sc, struct ieee80211_channel *c, int async) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct wpi_power_group *group; struct wpi_cmd_txpower txpower; u_int chan; int i; /* get channel number */ chan = ieee80211_chan2ieee(ic, c); /* find the power group to which this channel belongs */ if (IEEE80211_IS_CHAN_5GHZ(c)) { for (group = &sc->groups[1]; group < &sc->groups[4]; group++) if (chan <= group->chan) break; } else group = &sc->groups[0]; memset(&txpower, 0, sizeof txpower); txpower.band = IEEE80211_IS_CHAN_5GHZ(c) ? 0 : 1; txpower.channel = htole16(chan); /* set Tx power for all OFDM and CCK rates */ for (i = 0; i <= 11 ; i++) { /* retrieve Tx power for this channel/rate combination */ int idx = wpi_get_power_index(sc, group, c, wpi_ridx_to_rate[i]); txpower.rates[i].rate = wpi_ridx_to_plcp[i]; if (IEEE80211_IS_CHAN_5GHZ(c)) { txpower.rates[i].gain_radio = wpi_rf_gain_5ghz[idx]; txpower.rates[i].gain_dsp = wpi_dsp_gain_5ghz[idx]; } else { txpower.rates[i].gain_radio = wpi_rf_gain_2ghz[idx]; txpower.rates[i].gain_dsp = wpi_dsp_gain_2ghz[idx]; } DPRINTFN(WPI_DEBUG_TEMP,("chan %d/rate %d: power index %d\n", chan, wpi_ridx_to_rate[i], idx)); } return wpi_cmd(sc, WPI_CMD_TXPOWER, &txpower, sizeof txpower, async); } /* * Determine Tx power index for a given channel/rate combination. * This takes into account the regulatory information from EEPROM and the * current temperature. */ static int wpi_get_power_index(struct wpi_softc *sc, struct wpi_power_group *group, struct ieee80211_channel *c, int rate) { /* fixed-point arithmetic division using a n-bit fractional part */ #define fdivround(a, b, n) \ ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) /* linear interpolation */ #define interpolate(x, x1, y1, x2, y2, n) \ ((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct wpi_power_sample *sample; int pwr, idx; u_int chan; /* get channel number */ chan = ieee80211_chan2ieee(ic, c); /* default power is group's maximum power - 3dB */ pwr = group->maxpwr / 2; /* decrease power for highest OFDM rates to reduce distortion */ switch (rate) { case 72: /* 36Mb/s */ pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 0 : 5; break; case 96: /* 48Mb/s */ pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 7 : 10; break; case 108: /* 54Mb/s */ pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 9 : 12; break; } /* never exceed channel's maximum allowed Tx power */ pwr = min(pwr, sc->maxpwr[chan]); /* retrieve power index into gain tables from samples */ for (sample = group->samples; sample < &group->samples[3]; sample++) if (pwr > sample[1].power) break; /* fixed-point linear interpolation using a 19-bit fractional part */ idx = interpolate(pwr, sample[0].power, sample[0].index, sample[1].power, sample[1].index, 19); /* * Adjust power index based on current temperature * - if colder than factory-calibrated: decreate output power * - if warmer than factory-calibrated: increase output power */ idx -= (sc->temp - group->temp) * 11 / 100; /* decrease power for CCK rates (-5dB) */ if (!WPI_RATE_IS_OFDM(rate)) idx += 10; /* keep power index in a valid range */ if (idx < 0) return 0; if (idx > WPI_MAX_PWR_INDEX) return WPI_MAX_PWR_INDEX; return idx; #undef interpolate #undef fdivround } /** * Called by net80211 framework to indicate that a scan * is starting. This function doesn't actually do the scan, * wpi_scan_curchan starts things off. This function is more * of an early warning from the framework we should get ready * for the scan. */ static void wpi_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; wpi_queue_cmd(sc, WPI_SCAN_START, 0, WPI_QUEUE_NORMAL); } /** * Called by the net80211 framework, indicates that the * scan has ended. If there is a scan in progress on the card * then it should be aborted. */ static void wpi_scan_end(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; wpi_queue_cmd(sc, WPI_SCAN_STOP, 0, WPI_QUEUE_NORMAL); } /** * Called by the net80211 framework to indicate to the driver * that the channel should be changed */ static void wpi_set_channel(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; /* * Only need to set the channel in Monitor mode. AP scanning and auth * are already taken care of by their respective firmware commands. */ if (ic->ic_opmode == IEEE80211_M_MONITOR) wpi_queue_cmd(sc, WPI_SET_CHAN, 0, WPI_QUEUE_NORMAL); } /** * Called by net80211 to indicate that we need to scan the current * channel. The channel is previously be set via the wpi_set_channel * callback. */ static void wpi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { struct ieee80211vap *vap = ss->ss_vap; struct ifnet *ifp = vap->iv_ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; wpi_queue_cmd(sc, WPI_SCAN_CURCHAN, 0, WPI_QUEUE_NORMAL); } /** * Called by the net80211 framework to indicate * the minimum dwell time has been met, terminate the scan. * We don't actually terminate the scan as the firmware will notify * us when it's finished and we have no way to interrupt it. */ static void wpi_scan_mindwell(struct ieee80211_scan_state *ss) { /* NB: don't try to abort scan; wait for firmware to finish */ } /** * The ops function is called to perform some actual work. * because we can't sleep from any of the ic callbacks, we queue an * op task with wpi_queue_cmd and have the taskqueue process that task. * The task that gets cued is a op task, which ends up calling this function. */ static void wpi_ops(void *arg0, int pending) { struct wpi_softc *sc = arg0; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int cmd, arg, error; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); again: WPI_CMD_LOCK(sc); cmd = sc->sc_cmd[sc->sc_cmd_cur]; arg = sc->sc_cmd_arg[sc->sc_cmd_cur]; if (cmd == 0) { /* No more commands to process */ WPI_CMD_UNLOCK(sc); return; } sc->sc_cmd[sc->sc_cmd_cur] = 0; /* free the slot */ sc->sc_cmd_arg[sc->sc_cmd_cur] = 0; /* free the slot */ sc->sc_cmd_cur = (sc->sc_cmd_cur + 1) % WPI_CMD_MAXOPS; WPI_CMD_UNLOCK(sc); WPI_LOCK(sc); DPRINTFN(WPI_DEBUG_OPS,("wpi_ops: command: %d\n", cmd)); switch (cmd) { case WPI_RESTART: wpi_init_locked(sc, 0); WPI_UNLOCK(sc); return; case WPI_RF_RESTART: wpi_rfkill_resume(sc); WPI_UNLOCK(sc); return; } if (!(sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)) { WPI_UNLOCK(sc); return; } switch (cmd) { case WPI_SCAN_START: /* make the link LED blink while we're scanning */ wpi_set_led(sc, WPI_LED_LINK, 20, 2); sc->flags |= WPI_FLAG_SCANNING; break; case WPI_SCAN_STOP: sc->flags &= ~WPI_FLAG_SCANNING; break; case WPI_SCAN_CURCHAN: if (wpi_scan(sc)) ieee80211_cancel_scan(vap); break; case WPI_SET_CHAN: error = wpi_config(sc); if (error != 0) device_printf(sc->sc_dev, "error %d settting channel\n", error); break; - - case WPI_AUTH: - /* The node must be registered in the firmware before auth */ - error = wpi_auth(sc, vap); - WPI_UNLOCK(sc); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not move to auth state, error %d\n", - __func__, error); - return; - } - IEEE80211_LOCK(ic); - WPI_VAP(vap)->newstate(vap, IEEE80211_S_AUTH, arg); - if (vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, IEEE80211_S_AUTH, arg); - IEEE80211_UNLOCK(ic); - goto again; - - case WPI_RUN: - error = wpi_run(sc, vap); - WPI_UNLOCK(sc); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not move to run state, error %d\n", - __func__, error); - return; - } - IEEE80211_LOCK(ic); - WPI_VAP(vap)->newstate(vap, IEEE80211_S_RUN, arg); - if (vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, IEEE80211_S_RUN, arg); - IEEE80211_UNLOCK(ic); - goto again; } WPI_UNLOCK(sc); /* Take another pass */ goto again; } /** * queue a command for later execution in a different thread. * This is needed as the net80211 callbacks do not allow * sleeping, since we need to sleep to confirm commands have * been processed by the firmware, we must defer execution to * a sleep enabled thread. */ static int wpi_queue_cmd(struct wpi_softc *sc, int cmd, int arg, int flush) { WPI_CMD_LOCK(sc); if (flush) { memset(sc->sc_cmd, 0, sizeof (sc->sc_cmd)); memset(sc->sc_cmd_arg, 0, sizeof (sc->sc_cmd_arg)); sc->sc_cmd_cur = 0; sc->sc_cmd_next = 0; } if (sc->sc_cmd[sc->sc_cmd_next] != 0) { WPI_CMD_UNLOCK(sc); DPRINTF(("%s: command %d dropped\n", __func__, cmd)); return (EBUSY); } sc->sc_cmd[sc->sc_cmd_next] = cmd; sc->sc_cmd_arg[sc->sc_cmd_next] = arg; sc->sc_cmd_next = (sc->sc_cmd_next + 1) % WPI_CMD_MAXOPS; taskqueue_enqueue(sc->sc_tq, &sc->sc_opstask); WPI_CMD_UNLOCK(sc); return 0; } /* * Allocate DMA-safe memory for firmware transfer. */ static int wpi_alloc_fwmem(struct wpi_softc *sc) { /* allocate enough contiguous space to store text and data */ return wpi_dma_contig_alloc(sc, &sc->fw_dma, NULL, WPI_FW_MAIN_TEXT_MAXSZ + WPI_FW_MAIN_DATA_MAXSZ, 1, BUS_DMA_NOWAIT); } static void wpi_free_fwmem(struct wpi_softc *sc) { wpi_dma_contig_free(&sc->fw_dma); } /** * Called every second, wpi_watchdog used by the watch dog timer * to check that the card is still alive */ static void wpi_watchdog(void *arg) { struct wpi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; DPRINTFN(WPI_DEBUG_WATCHDOG,("Watchdog: tick\n")); if (sc->flags & WPI_FLAG_HW_RADIO_OFF) { /* No need to lock firmware memory */ tmp = wpi_mem_read(sc, WPI_MEM_HW_RADIO_OFF); if ((tmp & 0x1) == 0) { /* Radio kill switch is still off */ callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); return; } device_printf(sc->sc_dev, "Hardware Switch Enabled\n"); wpi_queue_cmd(sc, WPI_RF_RESTART, 0, WPI_QUEUE_CLEAR); return; } if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev,"device timeout\n"); ifp->if_oerrors++; wpi_queue_cmd(sc, WPI_RESTART, 0, WPI_QUEUE_CLEAR); } } if (sc->sc_scan_timer > 0) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (--sc->sc_scan_timer == 0 && vap != NULL) { device_printf(sc->sc_dev,"scan timeout\n"); ieee80211_cancel_scan(vap); wpi_queue_cmd(sc, WPI_RESTART, 0, WPI_QUEUE_CLEAR); } } if (ifp->if_drv_flags & IFF_DRV_RUNNING) callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); } #ifdef WPI_DEBUG static const char *wpi_cmd_str(int cmd) { switch (cmd) { case WPI_DISABLE_CMD: return "WPI_DISABLE_CMD"; case WPI_CMD_CONFIGURE: return "WPI_CMD_CONFIGURE"; case WPI_CMD_ASSOCIATE: return "WPI_CMD_ASSOCIATE"; case WPI_CMD_SET_WME: return "WPI_CMD_SET_WME"; case WPI_CMD_TSF: return "WPI_CMD_TSF"; case WPI_CMD_ADD_NODE: return "WPI_CMD_ADD_NODE"; case WPI_CMD_TX_DATA: return "WPI_CMD_TX_DATA"; case WPI_CMD_MRR_SETUP: return "WPI_CMD_MRR_SETUP"; case WPI_CMD_SET_LED: return "WPI_CMD_SET_LED"; case WPI_CMD_SET_POWER_MODE: return "WPI_CMD_SET_POWER_MODE"; case WPI_CMD_SCAN: return "WPI_CMD_SCAN"; case WPI_CMD_SET_BEACON:return "WPI_CMD_SET_BEACON"; case WPI_CMD_TXPOWER: return "WPI_CMD_TXPOWER"; case WPI_CMD_BLUETOOTH: return "WPI_CMD_BLUETOOTH"; default: KASSERT(1, ("Unknown Command: %d\n", cmd)); return "UNKNOWN CMD"; /* Make the compiler happy */ } } #endif MODULE_DEPEND(wpi, pci, 1, 1, 1); MODULE_DEPEND(wpi, wlan, 1, 1, 1); MODULE_DEPEND(wpi, firmware, 1, 1, 1); MODULE_DEPEND(wpi, wlan_amrr, 1, 1, 1); Index: user/thompsa/vaptq/sys/dev/wpi/if_wpivar.h =================================================================== --- user/thompsa/vaptq/sys/dev/wpi/if_wpivar.h (revision 185732) +++ user/thompsa/vaptq/sys/dev/wpi/if_wpivar.h (revision 185733) @@ -1,248 +1,245 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2006,2007 * Damien Bergamini * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include struct wpi_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint64_t wr_tsft; uint8_t wr_flags; uint8_t wr_rate; uint16_t wr_chan_freq; uint16_t wr_chan_flags; int8_t wr_dbm_antsignal; int8_t wr_dbm_antnoise; uint8_t wr_antenna; }; #define WPI_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_TSFT) | \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ (1 << IEEE80211_RADIOTAP_ANTENNA)) struct wpi_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; uint8_t wt_rate; uint16_t wt_chan_freq; uint16_t wt_chan_flags; uint8_t wt_hwqueue; }; #define WPI_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL)) struct wpi_dma_info { bus_dma_tag_t tag; bus_dmamap_t map; bus_addr_t paddr; /* aligned p address */ bus_addr_t paddr_start; /* possibly unaligned p start*/ caddr_t vaddr; /* aligned v address */ caddr_t vaddr_start; /* possibly unaligned v start */ bus_size_t size; }; struct wpi_tx_data { bus_dmamap_t map; struct mbuf *m; struct ieee80211_node *ni; }; struct wpi_tx_ring { struct wpi_dma_info desc_dma; struct wpi_dma_info cmd_dma; struct wpi_tx_desc *desc; struct wpi_tx_cmd *cmd; struct wpi_tx_data *data; bus_dma_tag_t data_dmat; int qid; int count; int queued; int cur; }; #define WPI_RBUF_COUNT ( WPI_RX_RING_COUNT + 16 ) struct wpi_rx_data { bus_dmamap_t map; struct mbuf *m; }; struct wpi_rx_ring { struct wpi_dma_info desc_dma; uint32_t *desc; struct wpi_rx_data data[WPI_RX_RING_COUNT]; bus_dma_tag_t data_dmat; int cur; }; struct wpi_amrr { struct ieee80211_node ni; /* must be the first */ int txcnt; int retrycnt; int success; int success_threshold; int recovery; }; struct wpi_node { struct ieee80211_node ni; /* must be the first */ struct ieee80211_amrr_node amn; }; #define WPI_NODE(ni) ((struct wpi_node *)(ni)) struct wpi_power_sample { uint8_t index; int8_t power; }; struct wpi_power_group { #define WPI_SAMPLES_COUNT 5 struct wpi_power_sample samples[WPI_SAMPLES_COUNT]; uint8_t chan; int8_t maxpwr; int16_t temp; }; struct wpi_vap { struct ieee80211vap vap; struct ieee80211_amrr amrr; int (*newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define WPI_VAP(vap) ((struct wpi_vap *)(vap)) struct wpi_softc { device_t sc_dev; struct ifnet *sc_ifp; struct mtx sc_mtx; /* Flags indicating the current state the driver * expects the hardware to be in */ uint32_t flags; #define WPI_FLAG_HW_RADIO_OFF (1 << 0) #define WPI_FLAG_SCANNING (1 << 1) #define WPI_FLAG_BUSY (1 << 2) #define WPI_FLAG_AUTH (1 << 3) /* shared area */ struct wpi_dma_info shared_dma; struct wpi_shared *shared; struct wpi_tx_ring txq[WME_NUM_AC]; struct wpi_tx_ring cmdq; struct wpi_rx_ring rxq; /* TX Thermal Callibration */ struct callout calib_to; int calib_cnt; /* Watch dog timer */ struct callout watchdog_to; /* Hardware switch polling timer */ struct callout hwswitch_to; struct resource *irq; struct resource *mem; bus_space_tag_t sc_st; bus_space_handle_t sc_sh; void *sc_ih; int mem_rid; int irq_rid; struct wpi_config config; int temp; int sc_tx_timer; int sc_scan_timer; struct bpf_if *sc_drvbpf; struct wpi_rx_radiotap_header sc_rxtap; int sc_rxtap_len; struct wpi_tx_radiotap_header sc_txtap; int sc_txtap_len; /* firmware image */ const struct firmware *fw_fp; /* firmware DMA transfer */ struct wpi_dma_info fw_dma; /* command queue related variables */ #define WPI_SCAN_START (1<<0) #define WPI_SCAN_CURCHAN (1<<1) #define WPI_SCAN_STOP (1<<2) #define WPI_SET_CHAN (1<<3) -#define WPI_AUTH (1<<4) -#define WPI_RUN (1<<5) -#define WPI_SCAN_NEXT (1<<6) -#define WPI_RESTART (1<<7) -#define WPI_RF_RESTART (1<<8) +#define WPI_RESTART (1<<4) +#define WPI_RF_RESTART (1<<5) #define WPI_CMD_MAXOPS 10 /* command queuing request type */ #define WPI_QUEUE_NORMAL 0 #define WPI_QUEUE_CLEAR 1 int sc_cmd[WPI_CMD_MAXOPS]; int sc_cmd_arg[WPI_CMD_MAXOPS]; int sc_cmd_cur; /* current queued scan task */ int sc_cmd_next; /* last queued scan task */ struct mtx sc_cmdlock; /* Task queues used to control the driver */ struct taskqueue *sc_tq; /* Main command task queue */ struct taskqueue *sc_tq2; /* firmware reset task queue */ /* Tasks used by the driver */ struct task sc_radioontask; /* enable rf transmitter task*/ struct task sc_radioofftask;/* disable rf transmitter task*/ struct task sc_opstask; /* operation handling task */ struct task sc_restarttask; /* reset firmware task */ struct task sc_bmiss_task; /* beacon miss */ /* Eeprom info */ uint8_t cap; uint16_t rev; uint8_t type; struct wpi_power_group groups[WPI_POWER_GROUPS_COUNT]; int8_t maxpwr[IEEE80211_CHAN_MAX]; char domain[4]; /*reglatory domain XXX */ }; #define WPI_LOCK_INIT(_sc) \ mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ MTX_NETWORK_LOCK, MTX_DEF) #define WPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define WPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define WPI_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) #define WPI_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) #define WPI_CMD_LOCK_INIT(_sc) \ mtx_init(&(_sc)->sc_cmdlock, device_get_nameunit((_sc)->sc_dev), \ NULL, MTX_DEF) #define WPI_CMD_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_cmdlock) #define WPI_CMD_LOCK(_sc) mtx_lock(&(_sc)->sc_cmdlock) #define WPI_CMD_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_cmdlock) Index: user/thompsa/vaptq/sys/net80211/ieee80211.c =================================================================== --- user/thompsa/vaptq/sys/net80211/ieee80211.c (revision 185732) +++ user/thompsa/vaptq/sys/net80211/ieee80211.c (revision 185733) @@ -1,1466 +1,1480 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 __FBSDID("$FreeBSD$"); /* * IEEE 802.11 generic handler */ #include "opt_wlan.h" #include -#include +#include #include +#include #include #include #include #include #include #include #include #include #include const char *ieee80211_phymode_name[IEEE80211_MODE_MAX] = { [IEEE80211_MODE_AUTO] = "auto", [IEEE80211_MODE_11A] = "11a", [IEEE80211_MODE_11B] = "11b", [IEEE80211_MODE_11G] = "11g", [IEEE80211_MODE_FH] = "FH", [IEEE80211_MODE_TURBO_A] = "turboA", [IEEE80211_MODE_TURBO_G] = "turboG", [IEEE80211_MODE_STURBO_A] = "sturboA", [IEEE80211_MODE_11NA] = "11na", [IEEE80211_MODE_11NG] = "11ng", }; /* map ieee80211_opmode to the corresponding capability bit */ const int ieee80211_opcap[IEEE80211_OPMODE_MAX] = { [IEEE80211_M_IBSS] = IEEE80211_C_IBSS, [IEEE80211_M_WDS] = IEEE80211_C_WDS, [IEEE80211_M_STA] = IEEE80211_C_STA, [IEEE80211_M_AHDEMO] = IEEE80211_C_AHDEMO, [IEEE80211_M_HOSTAP] = IEEE80211_C_HOSTAP, [IEEE80211_M_MONITOR] = IEEE80211_C_MONITOR, }; static const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag); static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag); static int ieee80211_media_setup(struct ieee80211com *ic, struct ifmedia *media, int caps, int addsta, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); static void ieee80211com_media_status(struct ifnet *, struct ifmediareq *); static int ieee80211com_media_change(struct ifnet *); static int media_status(enum ieee80211_opmode, const struct ieee80211_channel *); MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state"); /* * Default supported rates for 802.11 operation (in IEEE .5Mb units). */ #define B(r) ((r) | IEEE80211_RATE_BASIC) static const struct ieee80211_rateset ieee80211_rateset_11a = { 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } }; static const struct ieee80211_rateset ieee80211_rateset_half = { 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } }; static const struct ieee80211_rateset ieee80211_rateset_quarter = { 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } }; static const struct ieee80211_rateset ieee80211_rateset_11b = { 4, { B(2), B(4), B(11), B(22) } }; /* NB: OFDM rates are handled specially based on mode */ static const struct ieee80211_rateset ieee80211_rateset_11g = { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; #undef B /* * Fill in 802.11 available channel set, mark * all available channels as active, and pick * a default channel if not already specified. */ static void ieee80211_chan_init(struct ieee80211com *ic) { #define DEFAULTRATES(m, def) do { \ if (isset(ic->ic_modecaps, m) && ic->ic_sup_rates[m].rs_nrates == 0) \ ic->ic_sup_rates[m] = def; \ } while (0) struct ieee80211_channel *c; int i; KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX, ("invalid number of channels specified: %u", ic->ic_nchans)); memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps)); setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; KASSERT(c->ic_flags != 0, ("channel with no flags")); KASSERT(c->ic_ieee < IEEE80211_CHAN_MAX, ("channel with bogus ieee number %u", c->ic_ieee)); setbit(ic->ic_chan_avail, c->ic_ieee); /* * Identify mode capabilities. */ if (IEEE80211_IS_CHAN_A(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_11A); if (IEEE80211_IS_CHAN_B(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_11B); if (IEEE80211_IS_CHAN_ANYG(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_11G); if (IEEE80211_IS_CHAN_FHSS(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_FH); if (IEEE80211_IS_CHAN_108A(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A); if (IEEE80211_IS_CHAN_108G(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G); if (IEEE80211_IS_CHAN_ST(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A); if (IEEE80211_IS_CHAN_HTA(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_11NA); if (IEEE80211_IS_CHAN_HTG(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_11NG); } /* initialize candidate channels to all available */ memcpy(ic->ic_chan_active, ic->ic_chan_avail, sizeof(ic->ic_chan_avail)); /* sort channel table to allow lookup optimizations */ ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); /* invalidate any previous state */ ic->ic_bsschan = IEEE80211_CHAN_ANYC; ic->ic_prevchan = NULL; ic->ic_csa_newchan = NULL; /* arbitrarily pick the first channel */ ic->ic_curchan = &ic->ic_channels[0]; /* fillin well-known rate sets if driver has not specified */ DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g); DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a); DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a); DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g); /* * Set auto mode to reset active channel state and any desired channel. */ (void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO); #undef DEFAULTRATES } static void null_update_mcast(struct ifnet *ifp) { if_printf(ifp, "need multicast update callback\n"); } static void null_update_promisc(struct ifnet *ifp) { if_printf(ifp, "need promiscuous mode update callback\n"); } static int null_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt0) { if_printf(ifp, "discard raw packet\n"); m_freem(m); return EIO; } static void null_input(struct ifnet *ifp, struct mbuf *m) { if_printf(ifp, "if_input should not be called\n"); m_freem(m); } /* * Attach/setup the common net80211 state. Called by * the driver on attach to prior to creating any vap's. */ void ieee80211_ifattach(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct sockaddr_dl *sdl; struct ifaddr *ifa; KASSERT(ifp->if_type == IFT_IEEE80211, ("if_type %d", ifp->if_type)); IEEE80211_LOCK_INIT(ic, ifp->if_xname); TAILQ_INIT(&ic->ic_vaps); + + /* Create a taskqueue for all state changes */ + ic->ic_tq = taskqueue_create("ic_taskq", M_WAITOK | M_ZERO, + taskqueue_thread_enqueue, &ic->ic_tq); + taskqueue_start_threads(&ic->ic_tq, 1, PI_NET, "%s taskq", + ifp->if_xname); /* * Fill in 802.11 available channel set, mark all * available channels as active, and pick a default * channel if not already specified. */ ieee80211_media_init(ic); ic->ic_update_mcast = null_update_mcast; ic->ic_update_promisc = null_update_promisc; ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; ic->ic_lintval = ic->ic_bintval; ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; ieee80211_crypto_attach(ic); ieee80211_node_attach(ic); ieee80211_power_attach(ic); ieee80211_proto_attach(ic); ieee80211_ht_attach(ic); ieee80211_scan_attach(ic); ieee80211_regdomain_attach(ic); ieee80211_sysctl_attach(ic); ifp->if_addrlen = IEEE80211_ADDR_LEN; ifp->if_hdrlen = 0; if_attach(ifp); ifp->if_mtu = IEEE80211_MTU_MAX; ifp->if_broadcastaddr = ieee80211broadcastaddr; ifp->if_output = null_output; ifp->if_input = null_input; /* just in case */ ifp->if_resolvemulti = NULL; /* NB: callers check */ ifa = ifaddr_byindex(ifp->if_index); KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_ETHER; /* XXX IFT_IEEE80211? */ sdl->sdl_alen = IEEE80211_ADDR_LEN; IEEE80211_ADDR_COPY(LLADDR(sdl), ic->ic_myaddr); } /* * Detach net80211 state on device detach. Tear down * all vap's and reclaim all common state prior to the * device state going away. Note we may call back into * driver; it must be prepared for this. */ void ieee80211_ifdetach(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct ieee80211vap *vap; /* XXX ieee80211_stop_all? */ while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) ieee80211_vap_destroy(vap); ieee80211_sysctl_detach(ic); ieee80211_regdomain_detach(ic); ieee80211_scan_detach(ic); ieee80211_ht_detach(ic); /* NB: must be called before ieee80211_node_detach */ ieee80211_proto_detach(ic); ieee80211_crypto_detach(ic); ieee80211_power_detach(ic); ieee80211_node_detach(ic); ifmedia_removeall(&ic->ic_media); + taskqueue_free(ic->ic_tq); IEEE80211_LOCK_DESTROY(ic); if_detach(ifp); } /* * Default reset method for use with the ioctl support. This * method is invoked after any state change in the 802.11 * layer that should be propagated to the hardware but not * require re-initialization of the 802.11 state machine (e.g * rescanning for an ap). We always return ENETRESET which * should cause the driver to re-initialize the device. Drivers * can override this method to implement more optimized support. */ static int default_reset(struct ieee80211vap *vap, u_long cmd) { return ENETRESET; } /* * Prepare a vap for use. Drivers use this call to * setup net80211 state in new vap's prior attaching * them with ieee80211_vap_attach (below). */ int ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t macaddr[IEEE80211_ADDR_LEN]) { struct ifnet *ifp; ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { if_printf(ic->ic_ifp, "%s: unable to allocate ifnet\n", __func__); return ENOMEM; } if_initname(ifp, name, unit); ifp->if_softc = vap; /* back pointer */ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; ifp->if_start = ieee80211_start; ifp->if_ioctl = ieee80211_ioctl; ifp->if_watchdog = NULL; /* NB: no watchdog routine */ ifp->if_init = ieee80211_init; /* NB: input+output filled in by ether_ifattach */ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); vap->iv_ifp = ifp; vap->iv_ic = ic; vap->iv_flags = ic->ic_flags; /* propagate common flags */ vap->iv_flags_ext = ic->ic_flags_ext; vap->iv_flags_ven = ic->ic_flags_ven; vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE; vap->iv_htcaps = ic->ic_htcaps; vap->iv_opmode = opmode; vap->iv_caps |= ieee80211_opcap[opmode]; switch (opmode) { case IEEE80211_M_WDS: /* * WDS links must specify the bssid of the far end. * For legacy operation this is a static relationship. * For non-legacy operation the station must associate * and be authorized to pass traffic. Plumbing the * vap to the proper node happens when the vap * transitions to RUN state. */ IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid); vap->iv_flags |= IEEE80211_F_DESBSSID; if (flags & IEEE80211_CLONE_WDSLEGACY) vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY; break; } /* auto-enable s/w beacon miss support */ if (flags & IEEE80211_CLONE_NOBEACONS) vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS; /* * Enable various functionality by default if we're * capable; the driver can override us if it knows better. */ if (vap->iv_caps & IEEE80211_C_WME) vap->iv_flags |= IEEE80211_F_WME; if (vap->iv_caps & IEEE80211_C_BURST) vap->iv_flags |= IEEE80211_F_BURST; if (vap->iv_caps & IEEE80211_C_FF) vap->iv_flags |= IEEE80211_F_FF; if (vap->iv_caps & IEEE80211_C_TURBOP) vap->iv_flags |= IEEE80211_F_TURBOP; /* NB: bg scanning only makes sense for station mode right now */ if (vap->iv_opmode == IEEE80211_M_STA && (vap->iv_caps & IEEE80211_C_BGSCAN)) vap->iv_flags |= IEEE80211_F_BGSCAN; vap->iv_flags |= IEEE80211_F_DOTH; /* XXX no cap, just ena */ /* NB: DFS support only makes sense for ap mode right now */ if (vap->iv_opmode == IEEE80211_M_HOSTAP && (vap->iv_caps & IEEE80211_C_DFS)) vap->iv_flags_ext |= IEEE80211_FEXT_DFS; vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT; /* * Install a default reset method for the ioctl support; * the driver can override this. */ vap->iv_reset = default_reset; IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr); ieee80211_sysctl_vattach(vap); ieee80211_crypto_vattach(vap); ieee80211_node_vattach(vap); ieee80211_power_vattach(vap); ieee80211_proto_vattach(vap); ieee80211_ht_vattach(vap); ieee80211_scan_vattach(vap); ieee80211_regdomain_vattach(vap); return 0; } /* * Activate a vap. State should have been prepared with a * call to ieee80211_vap_setup and by the driver. On return * from this call the vap is ready for use. */ int ieee80211_vap_attach(struct ieee80211vap *vap, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) { struct ifnet *ifp = vap->iv_ifp; struct ieee80211com *ic = vap->iv_ic; struct ifmediareq imr; int maxrate; IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s flags 0x%x flags_ext 0x%x\n", __func__, ieee80211_opmode_name[vap->iv_opmode], ic->ic_ifp->if_xname, vap->iv_flags, vap->iv_flags_ext); /* * Do late attach work that cannot happen until after * the driver has had a chance to override defaults. */ ieee80211_node_latevattach(vap); ieee80211_power_latevattach(vap); maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat); ieee80211_media_status(ifp, &imr); /* NB: strip explicit mode; we're actually in autoselect */ ifmedia_set(&vap->iv_media, imr.ifm_active &~ IFM_MMASK); if (maxrate) ifp->if_baudrate = IF_Mbps(maxrate); ether_ifattach(ifp, vap->iv_myaddr); /* hook output method setup by ether_ifattach */ vap->iv_output = ifp->if_output; ifp->if_output = ieee80211_output; /* NB: if_mtu set by ether_ifattach to ETHERMTU */ bpfattach2(ifp, DLT_IEEE802_11, ifp->if_hdrlen, &vap->iv_rawbpf); IEEE80211_LOCK(ic); TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); ieee80211_syncflag_locked(ic, IEEE80211_F_WME); ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT); ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40); ieee80211_syncifflag_locked(ic, IFF_PROMISC); ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); IEEE80211_UNLOCK(ic); return 1; } /* * Tear down vap state and reclaim the ifnet. * The driver is assumed to have prepared for * this; e.g. by turning off interrupts for the * underlying device. */ void ieee80211_vap_detach(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = vap->iv_ifp; IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n", __func__, ieee80211_opmode_name[vap->iv_opmode], ic->ic_ifp->if_xname); IEEE80211_LOCK(ic); /* block traffic from above */ ifp->if_drv_flags |= IFF_DRV_OACTIVE; /* * Evil hack. Clear the backpointer from the ifnet to the * vap so any requests from above will return an error or * be ignored. In particular this short-circuits requests * by the bridge to turn off promiscuous mode as a result * of calling ether_ifdetach. */ ifp->if_softc = NULL; /* * Stop the vap before detaching the ifnet. Ideally we'd * do this in the other order so the ifnet is inaccessible * while we cleanup internal state but that is hard. */ ieee80211_stop_locked(vap); /* XXX accumulate iv_stats in ic_stats? */ TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); ieee80211_syncflag_locked(ic, IEEE80211_F_WME); ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT); ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40); ieee80211_syncifflag_locked(ic, IFF_PROMISC); ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); IEEE80211_UNLOCK(ic); + + /* + * Flush any deferred vap tasks. + * NB: must be before ether_ifdetach(); + */ + taskqueue_drain(ic->ic_tq, &vap->iv_nstate_task); /* XXX can't hold com lock */ /* NB: bpfattach is called by ether_ifdetach and claims all taps */ ether_ifdetach(ifp); ifmedia_removeall(&vap->iv_media); ieee80211_regdomain_vdetach(vap); ieee80211_scan_vdetach(vap); ieee80211_ht_vdetach(vap); /* NB: must be before ieee80211_node_vdetach */ ieee80211_proto_vdetach(vap); ieee80211_crypto_vdetach(vap); ieee80211_power_vdetach(vap); ieee80211_node_vdetach(vap); ieee80211_sysctl_vdetach(vap); if_free(ifp); } /* * Synchronize flag bit state in the parent ifnet structure * according to the state of all vap ifnet's. This is used, * for example, to handle IFF_PROMISC and IFF_ALLMULTI. */ void ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag) { struct ifnet *ifp = ic->ic_ifp; struct ieee80211vap *vap; int bit, oflags; IEEE80211_LOCK_ASSERT(ic); bit = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if (vap->iv_ifp->if_flags & flag) { /* * XXX the bridge sets PROMISC but we don't want to * enable it on the device, discard here so all the * drivers don't need to special-case it */ if (flag == IFF_PROMISC && vap->iv_opmode == IEEE80211_M_HOSTAP) continue; bit = 1; break; } oflags = ifp->if_flags; if (bit) ifp->if_flags |= flag; else ifp->if_flags &= ~flag; if ((ifp->if_flags ^ oflags) & flag) { /* XXX should we return 1/0 and let caller do this? */ if (ifp->if_drv_flags & IFF_DRV_RUNNING) { if (flag == IFF_PROMISC) ic->ic_update_promisc(ifp); else if (flag == IFF_ALLMULTI) ic->ic_update_mcast(ifp); } } } /* * Synchronize flag bit state in the com structure * according to the state of all vap's. This is used, * for example, to handle state changes via ioctls. */ static void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag) { struct ieee80211vap *vap; int bit; IEEE80211_LOCK_ASSERT(ic); bit = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if (vap->iv_flags & flag) { bit = 1; break; } if (bit) ic->ic_flags |= flag; else ic->ic_flags &= ~flag; } void ieee80211_syncflag(struct ieee80211vap *vap, int flag) { struct ieee80211com *ic = vap->iv_ic; IEEE80211_LOCK(ic); if (flag < 0) { flag = -flag; vap->iv_flags &= ~flag; } else vap->iv_flags |= flag; ieee80211_syncflag_locked(ic, flag); IEEE80211_UNLOCK(ic); } /* * Synchronize flag bit state in the com structure * according to the state of all vap's. This is used, * for example, to handle state changes via ioctls. */ static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag) { struct ieee80211vap *vap; int bit; IEEE80211_LOCK_ASSERT(ic); bit = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if (vap->iv_flags_ext & flag) { bit = 1; break; } if (bit) ic->ic_flags_ext |= flag; else ic->ic_flags_ext &= ~flag; } void ieee80211_syncflag_ext(struct ieee80211vap *vap, int flag) { struct ieee80211com *ic = vap->iv_ic; IEEE80211_LOCK(ic); if (flag < 0) { flag = -flag; vap->iv_flags_ext &= ~flag; } else vap->iv_flags_ext |= flag; ieee80211_syncflag_ext_locked(ic, flag); IEEE80211_UNLOCK(ic); } static __inline int mapgsm(u_int freq, u_int flags) { freq *= 10; if (flags & IEEE80211_CHAN_QUARTER) freq += 5; else if (flags & IEEE80211_CHAN_HALF) freq += 10; else freq += 20; /* NB: there is no 907/20 wide but leave room */ return (freq - 906*10) / 5; } static __inline int mappsb(u_int freq, u_int flags) { return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; } /* * Convert MHz frequency to IEEE channel number. */ int ieee80211_mhz2ieee(u_int freq, u_int flags) { #define IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990) if (flags & IEEE80211_CHAN_GSM) return mapgsm(freq, flags); if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ if (freq == 2484) return 14; if (freq < 2484) return ((int) freq - 2407) / 5; else return 15 + ((freq - 2512) / 20); } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ if (freq <= 5000) { /* XXX check regdomain? */ if (IS_FREQ_IN_PSB(freq)) return mappsb(freq, flags); return (freq - 4000) / 5; } else return (freq - 5000) / 5; } else { /* either, guess */ if (freq == 2484) return 14; if (freq < 2484) { if (907 <= freq && freq <= 922) return mapgsm(freq, flags); return ((int) freq - 2407) / 5; } if (freq < 5000) { if (IS_FREQ_IN_PSB(freq)) return mappsb(freq, flags); else if (freq > 4900) return (freq - 4000) / 5; else return 15 + ((freq - 2512) / 20); } return (freq - 5000) / 5; } #undef IS_FREQ_IN_PSB } /* * Convert channel to IEEE channel number. */ int ieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c) { if (c == NULL) { if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); return 0; /* XXX */ } return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee); } /* * Convert IEEE channel number to MHz frequency. */ u_int ieee80211_ieee2mhz(u_int chan, u_int flags) { if (flags & IEEE80211_CHAN_GSM) return 907 + 5 * (chan / 10); if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ if (chan == 14) return 2484; if (chan < 14) return 2407 + chan*5; else return 2512 + ((chan-15)*20); } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) { chan -= 37; return 4940 + chan*5 + (chan % 5 ? 2 : 0); } return 5000 + (chan*5); } else { /* either, guess */ /* XXX can't distinguish PSB+GSM channels */ if (chan == 14) return 2484; if (chan < 14) /* 0-13 */ return 2407 + chan*5; if (chan < 27) /* 15-26 */ return 2512 + ((chan-15)*20); return 5000 + (chan*5); } } /* * Locate a channel given a frequency+flags. We cache * the previous lookup to optimize switching between two * channels--as happens with dynamic turbo. */ struct ieee80211_channel * ieee80211_find_channel(struct ieee80211com *ic, int freq, int flags) { struct ieee80211_channel *c; int i; flags &= IEEE80211_CHAN_ALLTURBO; c = ic->ic_prevchan; if (c != NULL && c->ic_freq == freq && (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) return c; /* brute force search */ for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; if (c->ic_freq == freq && (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) return c; } return NULL; } /* * Locate a channel given a channel number+flags. We cache * the previous lookup to optimize switching between two * channels--as happens with dynamic turbo. */ struct ieee80211_channel * ieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags) { struct ieee80211_channel *c; int i; flags &= IEEE80211_CHAN_ALLTURBO; c = ic->ic_prevchan; if (c != NULL && c->ic_ieee == ieee && (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) return c; /* brute force search */ for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; if (c->ic_ieee == ieee && (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) return c; } return NULL; } static void addmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword) { #define ADD(_ic, _s, _o) \ ifmedia_add(media, \ IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) static const u_int mopts[IEEE80211_MODE_MAX] = { IFM_AUTO, IFM_IEEE80211_11A, IFM_IEEE80211_11B, IFM_IEEE80211_11G, IFM_IEEE80211_FH, IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, IFM_IEEE80211_11NA, IFM_IEEE80211_11NG, }; u_int mopt; mopt = mopts[mode]; if (addsta) ADD(ic, mword, mopt); /* STA mode has no cap */ if (caps & IEEE80211_C_IBSS) ADD(media, mword, mopt | IFM_IEEE80211_ADHOC); if (caps & IEEE80211_C_HOSTAP) ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP); if (caps & IEEE80211_C_AHDEMO) ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); if (caps & IEEE80211_C_MONITOR) ADD(media, mword, mopt | IFM_IEEE80211_MONITOR); if (caps & IEEE80211_C_WDS) ADD(media, mword, mopt | IFM_IEEE80211_WDS); #undef ADD } /* * Setup the media data structures according to the channel and * rate tables. */ static int ieee80211_media_setup(struct ieee80211com *ic, struct ifmedia *media, int caps, int addsta, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) { int i, j, mode, rate, maxrate, mword, r; const struct ieee80211_rateset *rs; struct ieee80211_rateset allrates; /* * Fill in media characteristics. */ ifmedia_init(media, 0, media_change, media_stat); maxrate = 0; /* * Add media for legacy operating modes. */ memset(&allrates, 0, sizeof(allrates)); for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; addmedia(media, caps, addsta, mode, IFM_AUTO); if (mode == IEEE80211_MODE_AUTO) continue; rs = &ic->ic_sup_rates[mode]; for (i = 0; i < rs->rs_nrates; i++) { rate = rs->rs_rates[i]; mword = ieee80211_rate2media(ic, rate, mode); if (mword == 0) continue; addmedia(media, caps, addsta, mode, mword); /* * Add legacy rate to the collection of all rates. */ r = rate & IEEE80211_RATE_VAL; for (j = 0; j < allrates.rs_nrates; j++) if (allrates.rs_rates[j] == r) break; if (j == allrates.rs_nrates) { /* unique, add to the set */ allrates.rs_rates[j] = r; allrates.rs_nrates++; } rate = (rate & IEEE80211_RATE_VAL) / 2; if (rate > maxrate) maxrate = rate; } } for (i = 0; i < allrates.rs_nrates; i++) { mword = ieee80211_rate2media(ic, allrates.rs_rates[i], IEEE80211_MODE_AUTO); if (mword == 0) continue; /* NB: remove media options from mword */ addmedia(media, caps, addsta, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); } /* * Add HT/11n media. Note that we do not have enough * bits in the media subtype to express the MCS so we * use a "placeholder" media subtype and any fixed MCS * must be specified with a different mechanism. */ for (; mode < IEEE80211_MODE_MAX; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; addmedia(media, caps, addsta, mode, IFM_AUTO); addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); } if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { addmedia(media, caps, addsta, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); /* XXX could walk htrates */ /* XXX known array size */ if (ieee80211_htrates[15].ht40_rate_400ns > maxrate) maxrate = ieee80211_htrates[15].ht40_rate_400ns; } return maxrate; } void ieee80211_media_init(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; int maxrate; /* NB: this works because the structure is initialized to zero */ if (!LIST_EMPTY(&ic->ic_media.ifm_list)) { /* * We are re-initializing the channel list; clear * the existing media state as the media routines * don't suppress duplicates. */ ifmedia_removeall(&ic->ic_media); } ieee80211_chan_init(ic); /* * Recalculate media settings in case new channel list changes * the set of available modes. */ maxrate = ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, 1, ieee80211com_media_change, ieee80211com_media_status); /* NB: strip explicit mode; we're actually in autoselect */ ifmedia_set(&ic->ic_media, media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK); if (maxrate) ifp->if_baudrate = IF_Mbps(maxrate); /* XXX need to propagate new media settings to vap's */ } const struct ieee80211_rateset * ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) { if (IEEE80211_IS_CHAN_HALF(c)) return &ieee80211_rateset_half; if (IEEE80211_IS_CHAN_QUARTER(c)) return &ieee80211_rateset_quarter; if (IEEE80211_IS_CHAN_HTA(c)) return &ic->ic_sup_rates[IEEE80211_MODE_11A]; if (IEEE80211_IS_CHAN_HTG(c)) { /* XXX does this work for basic rates? */ return &ic->ic_sup_rates[IEEE80211_MODE_11G]; } return &ic->ic_sup_rates[ieee80211_chan2mode(c)]; } void ieee80211_announce(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; int i, mode, rate, mword; const struct ieee80211_rateset *rs; /* NB: skip AUTO since it has no rates */ for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); rs = &ic->ic_sup_rates[mode]; for (i = 0; i < rs->rs_nrates; i++) { mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); if (mword == 0) continue; rate = ieee80211_media2rate(mword); printf("%s%d%sMbps", (i != 0 ? " " : ""), rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); } printf("\n"); } ieee80211_ht_announce(ic); } void ieee80211_announce_channels(struct ieee80211com *ic) { const struct ieee80211_channel *c; char type; int i, cw; printf("Chan Freq CW RegPwr MinPwr MaxPwr\n"); for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; if (IEEE80211_IS_CHAN_ST(c)) type = 'S'; else if (IEEE80211_IS_CHAN_108A(c)) type = 'T'; else if (IEEE80211_IS_CHAN_108G(c)) type = 'G'; else if (IEEE80211_IS_CHAN_HT(c)) type = 'n'; else if (IEEE80211_IS_CHAN_A(c)) type = 'a'; else if (IEEE80211_IS_CHAN_ANYG(c)) type = 'g'; else if (IEEE80211_IS_CHAN_B(c)) type = 'b'; else type = 'f'; if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c)) cw = 40; else if (IEEE80211_IS_CHAN_HALF(c)) cw = 10; else if (IEEE80211_IS_CHAN_QUARTER(c)) cw = 5; else cw = 20; printf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n" , c->ic_ieee, c->ic_freq, type , cw , IEEE80211_IS_CHAN_HT40U(c) ? '+' : IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' ' , c->ic_maxregpower , c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0 , c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0 ); } } static int media2mode(const struct ifmedia_entry *ime, uint32_t flags, uint16_t *mode) { switch (IFM_MODE(ime->ifm_media)) { case IFM_IEEE80211_11A: *mode = IEEE80211_MODE_11A; break; case IFM_IEEE80211_11B: *mode = IEEE80211_MODE_11B; break; case IFM_IEEE80211_11G: *mode = IEEE80211_MODE_11G; break; case IFM_IEEE80211_FH: *mode = IEEE80211_MODE_FH; break; case IFM_IEEE80211_11NA: *mode = IEEE80211_MODE_11NA; break; case IFM_IEEE80211_11NG: *mode = IEEE80211_MODE_11NG; break; case IFM_AUTO: *mode = IEEE80211_MODE_AUTO; break; default: return 0; } /* * Turbo mode is an ``option''. * XXX does not apply to AUTO */ if (ime->ifm_media & IFM_IEEE80211_TURBO) { if (*mode == IEEE80211_MODE_11A) { if (flags & IEEE80211_F_TURBOP) *mode = IEEE80211_MODE_TURBO_A; else *mode = IEEE80211_MODE_STURBO_A; } else if (*mode == IEEE80211_MODE_11G) *mode = IEEE80211_MODE_TURBO_G; else return 0; } /* XXX HT40 +/- */ return 1; } /* * Handle a media change request on the underlying interface. */ int ieee80211com_media_change(struct ifnet *ifp) { return EINVAL; } /* * Handle a media change request on the vap interface. */ int ieee80211_media_change(struct ifnet *ifp) { struct ieee80211vap *vap = ifp->if_softc; struct ifmedia_entry *ime = vap->iv_media.ifm_cur; uint16_t newmode; if (!media2mode(ime, vap->iv_flags, &newmode)) return EINVAL; if (vap->iv_des_mode != newmode) { vap->iv_des_mode = newmode; return ENETRESET; } return 0; } /* * Common code to calculate the media status word * from the operating mode and channel state. */ static int media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) { int status; status = IFM_IEEE80211; switch (opmode) { case IEEE80211_M_STA: break; case IEEE80211_M_IBSS: status |= IFM_IEEE80211_ADHOC; break; case IEEE80211_M_HOSTAP: status |= IFM_IEEE80211_HOSTAP; break; case IEEE80211_M_MONITOR: status |= IFM_IEEE80211_MONITOR; break; case IEEE80211_M_AHDEMO: status |= IFM_IEEE80211_ADHOC | IFM_FLAG0; break; case IEEE80211_M_WDS: status |= IFM_IEEE80211_WDS; break; } if (IEEE80211_IS_CHAN_HTA(chan)) { status |= IFM_IEEE80211_11NA; } else if (IEEE80211_IS_CHAN_HTG(chan)) { status |= IFM_IEEE80211_11NG; } else if (IEEE80211_IS_CHAN_A(chan)) { status |= IFM_IEEE80211_11A; } else if (IEEE80211_IS_CHAN_B(chan)) { status |= IFM_IEEE80211_11B; } else if (IEEE80211_IS_CHAN_ANYG(chan)) { status |= IFM_IEEE80211_11G; } else if (IEEE80211_IS_CHAN_FHSS(chan)) { status |= IFM_IEEE80211_FH; } /* XXX else complain? */ if (IEEE80211_IS_CHAN_TURBO(chan)) status |= IFM_IEEE80211_TURBO; #if 0 if (IEEE80211_IS_CHAN_HT20(chan)) status |= IFM_IEEE80211_HT20; if (IEEE80211_IS_CHAN_HT40(chan)) status |= IFM_IEEE80211_HT40; #endif return status; } static void ieee80211com_media_status(struct ifnet *ifp, struct ifmediareq *imr) { struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap; imr->ifm_status = IFM_AVALID; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if (vap->iv_ifp->if_flags & IFF_UP) { imr->ifm_status |= IFM_ACTIVE; break; } imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan); if (imr->ifm_status & IFM_ACTIVE) imr->ifm_current = imr->ifm_active; } void ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) { struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; enum ieee80211_phymode mode; imr->ifm_status = IFM_AVALID; /* * NB: use the current channel's mode to lock down a xmit * rate only when running; otherwise we may have a mismatch * in which case the rate will not be convertible. */ if (vap->iv_state == IEEE80211_S_RUN) { imr->ifm_status |= IFM_ACTIVE; mode = ieee80211_chan2mode(ic->ic_curchan); } else mode = IEEE80211_MODE_AUTO; imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan); /* * Calculate a current rate if possible. */ if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) { /* * A fixed rate is set, report that. */ imr->ifm_active |= ieee80211_rate2media(ic, vap->iv_txparms[mode].ucastrate, mode); } else if (vap->iv_opmode == IEEE80211_M_STA) { /* * In station mode report the current transmit rate. */ imr->ifm_active |= ieee80211_rate2media(ic, vap->iv_bss->ni_txrate, mode); } else imr->ifm_active |= IFM_AUTO; if (imr->ifm_status & IFM_ACTIVE) imr->ifm_current = imr->ifm_active; } /* * Set the current phy mode and recalculate the active channel * set based on the available channels for this mode. Also * select a new default/current channel if the current one is * inappropriate for this mode. */ int ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) { /* * Adjust basic rates in 11b/11g supported rate set. * Note that if operating on a hal/quarter rate channel * this is a noop as those rates sets are different * and used instead. */ if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); ic->ic_curmode = mode; ieee80211_reset_erp(ic); /* reset ERP state */ return 0; } /* * Return the phy mode for with the specified channel. */ enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *chan) { if (IEEE80211_IS_CHAN_HTA(chan)) return IEEE80211_MODE_11NA; else if (IEEE80211_IS_CHAN_HTG(chan)) return IEEE80211_MODE_11NG; else if (IEEE80211_IS_CHAN_108G(chan)) return IEEE80211_MODE_TURBO_G; else if (IEEE80211_IS_CHAN_ST(chan)) return IEEE80211_MODE_STURBO_A; else if (IEEE80211_IS_CHAN_TURBO(chan)) return IEEE80211_MODE_TURBO_A; else if (IEEE80211_IS_CHAN_A(chan)) return IEEE80211_MODE_11A; else if (IEEE80211_IS_CHAN_ANYG(chan)) return IEEE80211_MODE_11G; else if (IEEE80211_IS_CHAN_B(chan)) return IEEE80211_MODE_11B; else if (IEEE80211_IS_CHAN_FHSS(chan)) return IEEE80211_MODE_FH; /* NB: should not get here */ printf("%s: cannot map channel to mode; freq %u flags 0x%x\n", __func__, chan->ic_freq, chan->ic_flags); return IEEE80211_MODE_11B; } struct ratemedia { u_int match; /* rate + mode */ u_int media; /* if_media rate */ }; static int findmedia(const struct ratemedia rates[], int n, u_int match) { int i; for (i = 0; i < n; i++) if (rates[i].match == match) return rates[i].media; return IFM_AUTO; } /* * Convert IEEE80211 rate value to ifmedia subtype. * Rate is either a legacy rate in units of 0.5Mbps * or an MCS index. */ int ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) { #define N(a) (sizeof(a) / sizeof(a[0])) static const struct ratemedia rates[] = { { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, /* NB: OFDM72 doesn't realy exist so we don't handle it */ }; static const struct ratemedia htrates[] = { { 0, IFM_IEEE80211_MCS }, { 1, IFM_IEEE80211_MCS }, { 2, IFM_IEEE80211_MCS }, { 3, IFM_IEEE80211_MCS }, { 4, IFM_IEEE80211_MCS }, { 5, IFM_IEEE80211_MCS }, { 6, IFM_IEEE80211_MCS }, { 7, IFM_IEEE80211_MCS }, { 8, IFM_IEEE80211_MCS }, { 9, IFM_IEEE80211_MCS }, { 10, IFM_IEEE80211_MCS }, { 11, IFM_IEEE80211_MCS }, { 12, IFM_IEEE80211_MCS }, { 13, IFM_IEEE80211_MCS }, { 14, IFM_IEEE80211_MCS }, { 15, IFM_IEEE80211_MCS }, }; int m; /* * Check 11n rates first for match as an MCS. */ if (mode == IEEE80211_MODE_11NA) { if (rate & IEEE80211_RATE_MCS) { rate &= ~IEEE80211_RATE_MCS; m = findmedia(htrates, N(htrates), rate); if (m != IFM_AUTO) return m | IFM_IEEE80211_11NA; } } else if (mode == IEEE80211_MODE_11NG) { /* NB: 12 is ambiguous, it will be treated as an MCS */ if (rate & IEEE80211_RATE_MCS) { rate &= ~IEEE80211_RATE_MCS; m = findmedia(htrates, N(htrates), rate); if (m != IFM_AUTO) return m | IFM_IEEE80211_11NG; } } rate &= IEEE80211_RATE_VAL; switch (mode) { case IEEE80211_MODE_11A: case IEEE80211_MODE_11NA: case IEEE80211_MODE_TURBO_A: case IEEE80211_MODE_STURBO_A: return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A); case IEEE80211_MODE_11B: return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B); case IEEE80211_MODE_FH: return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH); case IEEE80211_MODE_AUTO: /* NB: ic may be NULL for some drivers */ if (ic && ic->ic_phytype == IEEE80211_T_FH) return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH); /* NB: hack, 11g matches both 11b+11a rates */ /* fall thru... */ case IEEE80211_MODE_11G: case IEEE80211_MODE_11NG: case IEEE80211_MODE_TURBO_G: return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G); } return IFM_AUTO; #undef N } int ieee80211_media2rate(int mword) { #define N(a) (sizeof(a) / sizeof(a[0])) static const int ieeerates[] = { -1, /* IFM_AUTO */ 0, /* IFM_MANUAL */ 0, /* IFM_NONE */ 2, /* IFM_IEEE80211_FH1 */ 4, /* IFM_IEEE80211_FH2 */ 2, /* IFM_IEEE80211_DS1 */ 4, /* IFM_IEEE80211_DS2 */ 11, /* IFM_IEEE80211_DS5 */ 22, /* IFM_IEEE80211_DS11 */ 44, /* IFM_IEEE80211_DS22 */ 12, /* IFM_IEEE80211_OFDM6 */ 18, /* IFM_IEEE80211_OFDM9 */ 24, /* IFM_IEEE80211_OFDM12 */ 36, /* IFM_IEEE80211_OFDM18 */ 48, /* IFM_IEEE80211_OFDM24 */ 72, /* IFM_IEEE80211_OFDM36 */ 96, /* IFM_IEEE80211_OFDM48 */ 108, /* IFM_IEEE80211_OFDM54 */ 144, /* IFM_IEEE80211_OFDM72 */ 0, /* IFM_IEEE80211_DS354k */ 0, /* IFM_IEEE80211_DS512k */ 6, /* IFM_IEEE80211_OFDM3 */ 9, /* IFM_IEEE80211_OFDM4 */ 54, /* IFM_IEEE80211_OFDM27 */ -1, /* IFM_IEEE80211_MCS */ }; return IFM_SUBTYPE(mword) < N(ieeerates) ? ieeerates[IFM_SUBTYPE(mword)] : 0; #undef N } Index: user/thompsa/vaptq/sys/net80211/ieee80211_proto.c =================================================================== --- user/thompsa/vaptq/sys/net80211/ieee80211_proto.c (revision 185732) +++ user/thompsa/vaptq/sys/net80211/ieee80211_proto.c (revision 185733) @@ -1,1753 +1,1765 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 __FBSDID("$FreeBSD$"); /* * IEEE 802.11 protocol support. */ #include "opt_inet.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include /* XXX for ether_sprintf */ #include #include #include #include #include #include #include /* XXX tunables */ #define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ #define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ const char *ieee80211_mgt_subtype_name[] = { "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", "probe_req", "probe_resp", "reserved#6", "reserved#7", "beacon", "atim", "disassoc", "auth", "deauth", "action", "reserved#14", "reserved#15" }; const char *ieee80211_ctl_subtype_name[] = { "reserved#0", "reserved#1", "reserved#2", "reserved#3", "reserved#3", "reserved#5", "reserved#6", "reserved#7", "reserved#8", "reserved#9", "ps_poll", "rts", "cts", "ack", "cf_end", "cf_end_ack" }; const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = { "IBSS", /* IEEE80211_M_IBSS */ "STA", /* IEEE80211_M_STA */ "WDS", /* IEEE80211_M_WDS */ "AHDEMO", /* IEEE80211_M_AHDEMO */ "HOSTAP", /* IEEE80211_M_HOSTAP */ "MONITOR" /* IEEE80211_M_MONITOR */ }; const char *ieee80211_state_name[IEEE80211_S_MAX] = { "INIT", /* IEEE80211_S_INIT */ "SCAN", /* IEEE80211_S_SCAN */ "AUTH", /* IEEE80211_S_AUTH */ "ASSOC", /* IEEE80211_S_ASSOC */ "CAC", /* IEEE80211_S_CAC */ "RUN", /* IEEE80211_S_RUN */ "CSA", /* IEEE80211_S_CSA */ "SLEEP", /* IEEE80211_S_SLEEP */ }; const char *ieee80211_wme_acnames[] = { "WME_AC_BE", "WME_AC_BK", "WME_AC_VI", "WME_AC_VO", "WME_UPSD", }; static void parent_updown(void *, int); +static void ieee80211_newstate_cb(void *, int); static int ieee80211_new_state_locked(struct ieee80211vap *, enum ieee80211_state, int); static int null_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ifnet *ifp = ni->ni_ic->ic_ifp; if_printf(ifp, "missing ic_raw_xmit callback, drop frame\n"); m_freem(m); return ENETDOWN; } void ieee80211_proto_attach(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; /* override the 802.3 setting */ ifp->if_hdrlen = ic->ic_headroom + sizeof(struct ieee80211_qosframe_addr4) + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN; /* XXX no way to recalculate on ifdetach */ if (ALIGN(ifp->if_hdrlen) > max_linkhdr) { /* XXX sanity check... */ max_linkhdr = ALIGN(ifp->if_hdrlen); max_hdr = max_linkhdr + max_protohdr; max_datalen = MHLEN - max_hdr; } ic->ic_protmode = IEEE80211_PROT_CTSONLY; TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ifp); ic->ic_wme.wme_hipri_switch_hysteresis = AGGRESSIVE_MODE_SWITCH_HYSTERESIS; /* initialize management frame handlers */ ic->ic_send_mgmt = ieee80211_send_mgmt; ic->ic_raw_xmit = null_raw_xmit; ieee80211_adhoc_attach(ic); ieee80211_sta_attach(ic); ieee80211_wds_attach(ic); ieee80211_hostap_attach(ic); ieee80211_monitor_attach(ic); } void ieee80211_proto_detach(struct ieee80211com *ic) { ieee80211_monitor_detach(ic); ieee80211_hostap_detach(ic); ieee80211_wds_detach(ic); ieee80211_adhoc_detach(ic); ieee80211_sta_detach(ic); } static void null_update_beacon(struct ieee80211vap *vap, int item) { } void ieee80211_proto_vattach(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = vap->iv_ifp; int i; /* override the 802.3 setting */ ifp->if_hdrlen = ic->ic_ifp->if_hdrlen; vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT; vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT; vap->iv_bmiss_max = IEEE80211_BMISS_MAX; callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE); callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE); + TASK_INIT(&vap->iv_nstate_task, 0, ieee80211_newstate_cb, vap); /* * Install default tx rate handling: no fixed rate, lowest * supported rate for mgmt and multicast frames. Default * max retry count. These settings can be changed by the * driver and/or user applications. */ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_11NA; i++) { const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i]; vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE; /* NB: we default to min supported rate for channel */ vap->iv_txparms[i].mgmtrate = rs->rs_rates[0] & IEEE80211_RATE_VAL; vap->iv_txparms[i].mcastrate = rs->rs_rates[0] & IEEE80211_RATE_VAL; vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT; } for (; i < IEEE80211_MODE_MAX; i++) { vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE; /* NB: default to MCS 0 */ vap->iv_txparms[i].mgmtrate = 0 | 0x80; vap->iv_txparms[i].mcastrate = 0 | 0x80; vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT; } vap->iv_roaming = IEEE80211_ROAMING_AUTO; vap->iv_update_beacon = null_update_beacon; vap->iv_deliver_data = ieee80211_deliver_data; /* attach support for operating mode */ ic->ic_vattach[vap->iv_opmode](vap); } void ieee80211_proto_vdetach(struct ieee80211vap *vap) { #define FREEAPPIE(ie) do { \ if (ie != NULL) \ FREE(ie, M_80211_NODE_IE); \ } while (0) /* * Detach operating mode module. */ if (vap->iv_opdetach != NULL) vap->iv_opdetach(vap); /* * This should not be needed as we detach when reseting * the state but be conservative here since the * authenticator may do things like spawn kernel threads. */ if (vap->iv_auth->ia_detach != NULL) vap->iv_auth->ia_detach(vap); /* * Detach any ACL'ator. */ if (vap->iv_acl != NULL) vap->iv_acl->iac_detach(vap); FREEAPPIE(vap->iv_appie_beacon); FREEAPPIE(vap->iv_appie_probereq); FREEAPPIE(vap->iv_appie_proberesp); FREEAPPIE(vap->iv_appie_assocreq); FREEAPPIE(vap->iv_appie_assocresp); FREEAPPIE(vap->iv_appie_wpa); #undef FREEAPPIE } /* * Simple-minded authenticator module support. */ #define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) /* XXX well-known names */ static const char *auth_modnames[IEEE80211_AUTH_MAX] = { "wlan_internal", /* IEEE80211_AUTH_NONE */ "wlan_internal", /* IEEE80211_AUTH_OPEN */ "wlan_internal", /* IEEE80211_AUTH_SHARED */ "wlan_xauth", /* IEEE80211_AUTH_8021X */ "wlan_internal", /* IEEE80211_AUTH_AUTO */ "wlan_xauth", /* IEEE80211_AUTH_WPA */ }; static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; static const struct ieee80211_authenticator auth_internal = { .ia_name = "wlan_internal", .ia_attach = NULL, .ia_detach = NULL, .ia_node_join = NULL, .ia_node_leave = NULL, }; /* * Setup internal authenticators once; they are never unregistered. */ static void ieee80211_auth_setup(void) { ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); } SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); const struct ieee80211_authenticator * ieee80211_authenticator_get(int auth) { if (auth >= IEEE80211_AUTH_MAX) return NULL; if (authenticators[auth] == NULL) ieee80211_load_module(auth_modnames[auth]); return authenticators[auth]; } void ieee80211_authenticator_register(int type, const struct ieee80211_authenticator *auth) { if (type >= IEEE80211_AUTH_MAX) return; authenticators[type] = auth; } void ieee80211_authenticator_unregister(int type) { if (type >= IEEE80211_AUTH_MAX) return; authenticators[type] = NULL; } /* * Very simple-minded ACL module support. */ /* XXX just one for now */ static const struct ieee80211_aclator *acl = NULL; void ieee80211_aclator_register(const struct ieee80211_aclator *iac) { printf("wlan: %s acl policy registered\n", iac->iac_name); acl = iac; } void ieee80211_aclator_unregister(const struct ieee80211_aclator *iac) { if (acl == iac) acl = NULL; printf("wlan: %s acl policy unregistered\n", iac->iac_name); } const struct ieee80211_aclator * ieee80211_aclator_get(const char *name) { if (acl == NULL) ieee80211_load_module("wlan_acl"); return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; } void ieee80211_print_essid(const uint8_t *essid, int len) { const uint8_t *p; int i; if (len > IEEE80211_NWID_LEN) len = IEEE80211_NWID_LEN; /* determine printable or not */ for (i = 0, p = essid; i < len; i++, p++) { if (*p < ' ' || *p > 0x7e) break; } if (i == len) { printf("\""); for (i = 0, p = essid; i < len; i++, p++) printf("%c", *p); printf("\""); } else { printf("0x"); for (i = 0, p = essid; i < len; i++, p++) printf("%02x", *p); } } void ieee80211_dump_pkt(struct ieee80211com *ic, const uint8_t *buf, int len, int rate, int rssi) { const struct ieee80211_frame *wh; int i; wh = (const struct ieee80211_frame *)buf; switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { case IEEE80211_FC1_DIR_NODS: printf("NODS %s", ether_sprintf(wh->i_addr2)); printf("->%s", ether_sprintf(wh->i_addr1)); printf("(%s)", ether_sprintf(wh->i_addr3)); break; case IEEE80211_FC1_DIR_TODS: printf("TODS %s", ether_sprintf(wh->i_addr2)); printf("->%s", ether_sprintf(wh->i_addr3)); printf("(%s)", ether_sprintf(wh->i_addr1)); break; case IEEE80211_FC1_DIR_FROMDS: printf("FRDS %s", ether_sprintf(wh->i_addr3)); printf("->%s", ether_sprintf(wh->i_addr1)); printf("(%s)", ether_sprintf(wh->i_addr2)); break; case IEEE80211_FC1_DIR_DSTODS: printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1])); printf("->%s", ether_sprintf(wh->i_addr3)); printf("(%s", ether_sprintf(wh->i_addr2)); printf("->%s)", ether_sprintf(wh->i_addr1)); break; } switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { case IEEE80211_FC0_TYPE_DATA: printf(" data"); break; case IEEE80211_FC0_TYPE_MGT: printf(" %s", ieee80211_mgt_subtype_name[ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) >> IEEE80211_FC0_SUBTYPE_SHIFT]); break; default: printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); break; } if (IEEE80211_QOS_HAS_SEQ(wh)) { const struct ieee80211_qosframe *qwh = (const struct ieee80211_qosframe *)buf; printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID, qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : ""); } if (wh->i_fc[1] & IEEE80211_FC1_WEP) { int off; off = ieee80211_anyhdrspace(ic, wh); printf(" WEP [IV %.02x %.02x %.02x", buf[off+0], buf[off+1], buf[off+2]); if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) printf(" %.02x %.02x %.02x", buf[off+4], buf[off+5], buf[off+6]); printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6); } if (rate >= 0) printf(" %dM", rate / 2); if (rssi >= 0) printf(" +%d", rssi); printf("\n"); if (len > 0) { for (i = 0; i < len; i++) { if ((i & 1) == 0) printf(" "); printf("%02x", buf[i]); } printf("\n"); } } static __inline int findrix(const struct ieee80211_rateset *rs, int r) { int i; for (i = 0; i < rs->rs_nrates; i++) if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r) return i; return -1; } int ieee80211_fix_rate(struct ieee80211_node *ni, struct ieee80211_rateset *nrs, int flags) { #define RV(v) ((v) & IEEE80211_RATE_VAL) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; int i, j, rix, error; int okrate, badrate, fixedrate, ucastrate; const struct ieee80211_rateset *srs; uint8_t r; error = 0; okrate = badrate = 0; ucastrate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].ucastrate; if (ucastrate != IEEE80211_FIXED_RATE_NONE) { /* * Workaround awkwardness with fixed rate. We are called * to check both the legacy rate set and the HT rate set * but we must apply any legacy fixed rate check only to the * legacy rate set and vice versa. We cannot tell what type * of rate set we've been given (legacy or HT) but we can * distinguish the fixed rate type (MCS have 0x80 set). * So to deal with this the caller communicates whether to * check MCS or legacy rate using the flags and we use the * type of any fixed rate to avoid applying an MCS to a * legacy rate and vice versa. */ if (ucastrate & 0x80) { if (flags & IEEE80211_F_DOFRATE) flags &= ~IEEE80211_F_DOFRATE; } else if ((ucastrate & 0x80) == 0) { if (flags & IEEE80211_F_DOFMCS) flags &= ~IEEE80211_F_DOFMCS; } /* NB: required to make MCS match below work */ ucastrate &= IEEE80211_RATE_VAL; } fixedrate = IEEE80211_FIXED_RATE_NONE; /* * XXX we are called to process both MCS and legacy rates; * we must use the appropriate basic rate set or chaos will * ensue; for now callers that want MCS must supply * IEEE80211_F_DOBRS; at some point we'll need to split this * function so there are two variants, one for MCS and one * for legacy rates. */ if (flags & IEEE80211_F_DOBRS) srs = (const struct ieee80211_rateset *) ieee80211_get_suphtrates(ic, ni->ni_chan); else srs = ieee80211_get_suprates(ic, ni->ni_chan); for (i = 0; i < nrs->rs_nrates; ) { if (flags & IEEE80211_F_DOSORT) { /* * Sort rates. */ for (j = i + 1; j < nrs->rs_nrates; j++) { if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { r = nrs->rs_rates[i]; nrs->rs_rates[i] = nrs->rs_rates[j]; nrs->rs_rates[j] = r; } } } r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; badrate = r; /* * Check for fixed rate. */ if (r == ucastrate) fixedrate = r; /* * Check against supported rates. */ rix = findrix(srs, r); if (flags & IEEE80211_F_DONEGO) { if (rix < 0) { /* * A rate in the node's rate set is not * supported. If this is a basic rate and we * are operating as a STA then this is an error. * Otherwise we just discard/ignore the rate. */ if ((flags & IEEE80211_F_JOIN) && (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) error++; } else if ((flags & IEEE80211_F_JOIN) == 0) { /* * Overwrite with the supported rate * value so any basic rate bit is set. */ nrs->rs_rates[i] = srs->rs_rates[rix]; } } if ((flags & IEEE80211_F_DODEL) && rix < 0) { /* * Delete unacceptable rates. */ nrs->rs_nrates--; for (j = i; j < nrs->rs_nrates; j++) nrs->rs_rates[j] = nrs->rs_rates[j + 1]; nrs->rs_rates[j] = 0; continue; } if (rix >= 0) okrate = nrs->rs_rates[i]; i++; } if (okrate == 0 || error != 0 || ((flags & (IEEE80211_F_DOFRATE|IEEE80211_F_DOFMCS)) && fixedrate != ucastrate)) { IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, "%s: flags 0x%x okrate %d error %d fixedrate 0x%x " "ucastrate %x\n", __func__, fixedrate, ucastrate, flags); return badrate | IEEE80211_RATE_BASIC; } else return RV(okrate); #undef RV } /* * Reset 11g-related state. */ void ieee80211_reset_erp(struct ieee80211com *ic) { ic->ic_flags &= ~IEEE80211_F_USEPROT; ic->ic_nonerpsta = 0; ic->ic_longslotsta = 0; /* * Short slot time is enabled only when operating in 11g * and not in an IBSS. We must also honor whether or not * the driver is capable of doing it. */ ieee80211_set_shortslottime(ic, IEEE80211_IS_CHAN_A(ic->ic_curchan) || IEEE80211_IS_CHAN_HT(ic->ic_curchan) || (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && ic->ic_opmode == IEEE80211_M_HOSTAP && (ic->ic_caps & IEEE80211_C_SHSLOT))); /* * Set short preamble and ERP barker-preamble flags. */ if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { ic->ic_flags |= IEEE80211_F_SHPREAMBLE; ic->ic_flags &= ~IEEE80211_F_USEBARKER; } else { ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; ic->ic_flags |= IEEE80211_F_USEBARKER; } } /* * Set the short slot time state and notify the driver. */ void ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) { if (onoff) ic->ic_flags |= IEEE80211_F_SHSLOT; else ic->ic_flags &= ~IEEE80211_F_SHSLOT; /* notify driver */ if (ic->ic_updateslot != NULL) ic->ic_updateslot(ic->ic_ifp); } /* * Check if the specified rate set supports ERP. * NB: the rate set is assumed to be sorted. */ int ieee80211_iserp_rateset(const struct ieee80211_rateset *rs) { #define N(a) (sizeof(a) / sizeof(a[0])) static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; int i, j; if (rs->rs_nrates < N(rates)) return 0; for (i = 0; i < N(rates); i++) { for (j = 0; j < rs->rs_nrates; j++) { int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; if (rates[i] == r) goto next; if (r > rates[i]) return 0; } return 0; next: ; } return 1; #undef N } /* * Mark the basic rates for the rate table based on the * operating mode. For real 11g we mark all the 11b rates * and 6, 12, and 24 OFDM. For 11b compatibility we mark only * 11b rates. There's also a pseudo 11a-mode used to mark only * the basic OFDM rates. */ static void setbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode, int add) { static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = { { .rs_nrates = 0 }, /* IEEE80211_MODE_AUTO */ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ { .rs_nrates = 0 }, /* IEEE80211_MODE_FH */ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_TURBO_A */ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_TURBO_G (mixed b/g) */ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_STURBO_A */ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11NA */ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11NG (mixed b/g) */ }; int i, j; for (i = 0; i < rs->rs_nrates; i++) { if (!add) rs->rs_rates[i] &= IEEE80211_RATE_VAL; for (j = 0; j < basic[mode].rs_nrates; j++) if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { rs->rs_rates[i] |= IEEE80211_RATE_BASIC; break; } } } /* * Set the basic rates in a rate set. */ void ieee80211_setbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) { setbasicrates(rs, mode, 0); } /* * Add basic rates to a rate set. */ void ieee80211_addbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) { setbasicrates(rs, mode, 1); } /* * WME protocol support. * * The default 11a/b/g/n parameters come from the WiFi Alliance WMM * System Interopability Test Plan (v1.4, Appendix F) and the 802.11n * Draft 2.0 Test Plan (Appendix D). * * Static/Dynamic Turbo mode settings come from Atheros. */ typedef struct phyParamType { uint8_t aifsn; uint8_t logcwmin; uint8_t logcwmax; uint16_t txopLimit; uint8_t acm; } paramType; static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_AUTO */ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11A */ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11B */ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11G */ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_FH */ { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NA */ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NG */ }; static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11B */ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_FH */ { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */ }; static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */ { 1, 3, 4, 188, 0 }, /* IEEE80211_MODE_11B */ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */ { 1, 3, 4, 188, 0 }, /* IEEE80211_MODE_FH */ { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */ { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */ { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */ }; static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */ { 1, 2, 3, 102, 0 }, /* IEEE80211_MODE_11B */ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */ { 1, 2, 3, 102, 0 }, /* IEEE80211_MODE_FH */ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ }; static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11B */ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_FH */ { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */ }; static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */ { 2, 3, 4, 188, 0 }, /* IEEE80211_MODE_11B */ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */ { 2, 3, 4, 188, 0 }, /* IEEE80211_MODE_FH */ { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */ { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */ { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */ }; static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */ { 2, 2, 3, 102, 0 }, /* IEEE80211_MODE_11B */ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */ { 2, 2, 3, 102, 0 }, /* IEEE80211_MODE_FH */ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ }; static void ieee80211_wme_initparams_locked(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_wme_state *wme = &ic->ic_wme; const paramType *pPhyParam, *pBssPhyParam; struct wmeParams *wmep; enum ieee80211_phymode mode; int i; IEEE80211_LOCK_ASSERT(ic); if ((ic->ic_caps & IEEE80211_C_WME) == 0) return; /* * Select mode; we can be called early in which case we * always use auto mode. We know we'll be called when * entering the RUN state with bsschan setup properly * so state will eventually get set correctly */ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) mode = ieee80211_chan2mode(ic->ic_bsschan); else mode = IEEE80211_MODE_AUTO; for (i = 0; i < WME_NUM_AC; i++) { switch (i) { case WME_AC_BK: pPhyParam = &phyParamForAC_BK[mode]; pBssPhyParam = &phyParamForAC_BK[mode]; break; case WME_AC_VI: pPhyParam = &phyParamForAC_VI[mode]; pBssPhyParam = &bssPhyParamForAC_VI[mode]; break; case WME_AC_VO: pPhyParam = &phyParamForAC_VO[mode]; pBssPhyParam = &bssPhyParamForAC_VO[mode]; break; case WME_AC_BE: default: pPhyParam = &phyParamForAC_BE[mode]; pBssPhyParam = &bssPhyParamForAC_BE[mode]; break; } wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; if (ic->ic_opmode == IEEE80211_M_HOSTAP) { wmep->wmep_acm = pPhyParam->acm; wmep->wmep_aifsn = pPhyParam->aifsn; wmep->wmep_logcwmin = pPhyParam->logcwmin; wmep->wmep_logcwmax = pPhyParam->logcwmax; wmep->wmep_txopLimit = pPhyParam->txopLimit; } else { wmep->wmep_acm = pBssPhyParam->acm; wmep->wmep_aifsn = pBssPhyParam->aifsn; wmep->wmep_logcwmin = pBssPhyParam->logcwmin; wmep->wmep_logcwmax = pBssPhyParam->logcwmax; wmep->wmep_txopLimit = pBssPhyParam->txopLimit; } IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " "log2(cwmax) %u txpoLimit %u]\n", __func__ , ieee80211_wme_acnames[i] , wmep->wmep_acm , wmep->wmep_aifsn , wmep->wmep_logcwmin , wmep->wmep_logcwmax , wmep->wmep_txopLimit ); wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; wmep->wmep_acm = pBssPhyParam->acm; wmep->wmep_aifsn = pBssPhyParam->aifsn; wmep->wmep_logcwmin = pBssPhyParam->logcwmin; wmep->wmep_logcwmax = pBssPhyParam->logcwmax; wmep->wmep_txopLimit = pBssPhyParam->txopLimit; IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " "log2(cwmax) %u txpoLimit %u]\n", __func__ , ieee80211_wme_acnames[i] , wmep->wmep_acm , wmep->wmep_aifsn , wmep->wmep_logcwmin , wmep->wmep_logcwmax , wmep->wmep_txopLimit ); } /* NB: check ic_bss to avoid NULL deref on initial attach */ if (vap->iv_bss != NULL) { /* * Calculate agressive mode switching threshold based * on beacon interval. This doesn't need locking since * we're only called before entering the RUN state at * which point we start sending beacon frames. */ wme->wme_hipri_switch_thresh = (HIGH_PRI_SWITCH_THRESH * vap->iv_bss->ni_intval) / 100; ieee80211_wme_updateparams(vap); } } void ieee80211_wme_initparams(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; IEEE80211_LOCK(ic); ieee80211_wme_initparams_locked(vap); IEEE80211_UNLOCK(ic); } /* * Update WME parameters for ourself and the BSS. */ void ieee80211_wme_updateparams_locked(struct ieee80211vap *vap) { static const paramType phyParam[IEEE80211_MODE_MAX] = { { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_AUTO */ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11A */ { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_11B */ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11G */ { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_FH */ { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_A */ { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_G */ { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_STURBO_A */ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NA */ /*XXXcheck*/ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NG */ /*XXXcheck*/ }; struct ieee80211com *ic = vap->iv_ic; struct ieee80211_wme_state *wme = &ic->ic_wme; const struct wmeParams *wmep; struct wmeParams *chanp, *bssp; enum ieee80211_phymode mode; int i; /* set up the channel access parameters for the physical device */ for (i = 0; i < WME_NUM_AC; i++) { chanp = &wme->wme_chanParams.cap_wmeParams[i]; wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; chanp->wmep_aifsn = wmep->wmep_aifsn; chanp->wmep_logcwmin = wmep->wmep_logcwmin; chanp->wmep_logcwmax = wmep->wmep_logcwmax; chanp->wmep_txopLimit = wmep->wmep_txopLimit; chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; chanp->wmep_aifsn = wmep->wmep_aifsn; chanp->wmep_logcwmin = wmep->wmep_logcwmin; chanp->wmep_logcwmax = wmep->wmep_logcwmax; chanp->wmep_txopLimit = wmep->wmep_txopLimit; } /* * Select mode; we can be called early in which case we * always use auto mode. We know we'll be called when * entering the RUN state with bsschan setup properly * so state will eventually get set correctly */ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) mode = ieee80211_chan2mode(ic->ic_bsschan); else mode = IEEE80211_MODE_AUTO; /* * This implements agressive mode as found in certain * vendors' AP's. When there is significant high * priority (VI/VO) traffic in the BSS throttle back BE * traffic by using conservative parameters. Otherwise * BE uses agressive params to optimize performance of * legacy/non-QoS traffic. */ if ((vap->iv_opmode == IEEE80211_M_HOSTAP && (wme->wme_flags & WME_F_AGGRMODE) != 0) || (vap->iv_opmode == IEEE80211_M_STA && (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || (vap->iv_flags & IEEE80211_F_WME) == 0) { chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; chanp->wmep_aifsn = bssp->wmep_aifsn = phyParam[mode].aifsn; chanp->wmep_logcwmin = bssp->wmep_logcwmin = phyParam[mode].logcwmin; chanp->wmep_logcwmax = bssp->wmep_logcwmax = phyParam[mode].logcwmax; chanp->wmep_txopLimit = bssp->wmep_txopLimit = (vap->iv_flags & IEEE80211_F_BURST) ? phyParam[mode].txopLimit : 0; IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: %s [acm %u aifsn %u log2(cwmin) %u " "log2(cwmax) %u txpoLimit %u]\n", __func__ , ieee80211_wme_acnames[WME_AC_BE] , chanp->wmep_acm , chanp->wmep_aifsn , chanp->wmep_logcwmin , chanp->wmep_logcwmax , chanp->wmep_txopLimit ); } /* XXX multi-bss */ if (vap->iv_opmode == IEEE80211_M_HOSTAP && ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { static const uint8_t logCwMin[IEEE80211_MODE_MAX] = { 3, /* IEEE80211_MODE_AUTO */ 3, /* IEEE80211_MODE_11A */ 4, /* IEEE80211_MODE_11B */ 3, /* IEEE80211_MODE_11G */ 4, /* IEEE80211_MODE_FH */ 3, /* IEEE80211_MODE_TURBO_A */ 3, /* IEEE80211_MODE_TURBO_G */ 3, /* IEEE80211_MODE_STURBO_A */ 3, /* IEEE80211_MODE_11NA */ 3, /* IEEE80211_MODE_11NG */ }; chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode]; IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: %s log2(cwmin) %u\n", __func__ , ieee80211_wme_acnames[WME_AC_BE] , chanp->wmep_logcwmin ); } if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ /* * Arrange for a beacon update and bump the parameter * set number so associated stations load the new values. */ wme->wme_bssChanParams.cap_info = (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME); } wme->wme_update(ic); IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: WME params updated, cap_info 0x%x\n", __func__, vap->iv_opmode == IEEE80211_M_STA ? wme->wme_wmeChanParams.cap_info : wme->wme_bssChanParams.cap_info); } void ieee80211_wme_updateparams(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; if (ic->ic_caps & IEEE80211_C_WME) { IEEE80211_LOCK(ic); ieee80211_wme_updateparams_locked(vap); IEEE80211_UNLOCK(ic); } } static void parent_updown(void *arg, int npending) { struct ifnet *parent = arg; parent->if_ioctl(parent, SIOCSIFFLAGS, NULL); } /* * Start a vap running. If this is the first vap to be * set running on the underlying device then we * automatically bring the device up. */ void ieee80211_start_locked(struct ieee80211vap *vap) { struct ifnet *ifp = vap->iv_ifp; struct ieee80211com *ic = vap->iv_ic; struct ifnet *parent = ic->ic_ifp; IEEE80211_LOCK_ASSERT(ic); IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, "start running, %d vaps running\n", ic->ic_nrunning); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { /* * Mark us running. Note that it's ok to do this first; * if we need to bring the parent device up we defer that * to avoid dropping the com lock. We expect the device * to respond to being marked up by calling back into us * through ieee80211_start_all at which point we'll come * back in here and complete the work. */ ifp->if_drv_flags |= IFF_DRV_RUNNING; /* * We are not running; if this we are the first vap * to be brought up auto-up the parent if necessary. */ if (ic->ic_nrunning++ == 0 && (parent->if_drv_flags & IFF_DRV_RUNNING) == 0) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, "%s: up parent %s\n", __func__, parent->if_xname); parent->if_flags |= IFF_UP; - taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task); + taskqueue_enqueue(ic->ic_tq, &ic->ic_parent_task); return; } } /* * If the parent is up and running, then kick the * 802.11 state machine as appropriate. */ if ((parent->if_drv_flags & IFF_DRV_RUNNING) && vap->iv_roaming != IEEE80211_ROAMING_MANUAL) { if (vap->iv_opmode == IEEE80211_M_STA) { #if 0 /* XXX bypasses scan too easily; disable for now */ /* * Try to be intelligent about clocking the state * machine. If we're currently in RUN state then * we should be able to apply any new state/parameters * simply by re-associating. Otherwise we need to * re-scan to select an appropriate ap. */ if (vap->iv_state >= IEEE80211_S_RUN) ieee80211_new_state_locked(vap, IEEE80211_S_ASSOC, 1); else #endif ieee80211_new_state_locked(vap, IEEE80211_S_SCAN, 0); } else { /* * For monitor+wds mode there's nothing to do but * start running. Otherwise if this is the first * vap to be brought up, start a scan which may be * preempted if the station is locked to a particular * channel. */ /* XXX needed? */ ieee80211_new_state_locked(vap, IEEE80211_S_INIT, 0); if (vap->iv_opmode == IEEE80211_M_MONITOR || vap->iv_opmode == IEEE80211_M_WDS) ieee80211_new_state_locked(vap, IEEE80211_S_RUN, -1); else ieee80211_new_state_locked(vap, IEEE80211_S_SCAN, 0); } } } /* * Start a single vap. */ void ieee80211_init(void *arg) { struct ieee80211vap *vap = arg; /* * This routine is publicly accessible through the vap's * if_init method so guard against calls during detach. * ieee80211_vap_detach null's the backpointer before * tearing down state to signal any callback should be * rejected/ignored. */ if (vap != NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, "%s\n", __func__); IEEE80211_LOCK(vap->iv_ic); ieee80211_start_locked(vap); IEEE80211_UNLOCK(vap->iv_ic); } } /* * Start all runnable vap's on a device. */ void ieee80211_start_all(struct ieee80211com *ic) { struct ieee80211vap *vap; IEEE80211_LOCK(ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { struct ifnet *ifp = vap->iv_ifp; if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ ieee80211_start_locked(vap); } IEEE80211_UNLOCK(ic); } /* * Stop a vap. We force it down using the state machine * then mark it's ifnet not running. If this is the last * vap running on the underlying device then we close it * too to insure it will be properly initialized when the * next vap is brought up. */ void ieee80211_stop_locked(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = vap->iv_ifp; struct ifnet *parent = ic->ic_ifp; IEEE80211_LOCK_ASSERT(ic); IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, "stop running, %d vaps running\n", ic->ic_nrunning); ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; /* mark us stopped */ if (--ic->ic_nrunning == 0 && (parent->if_drv_flags & IFF_DRV_RUNNING)) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, "down parent %s\n", parent->if_xname); parent->if_flags &= ~IFF_UP; - taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task); + taskqueue_enqueue(ic->ic_tq, &ic->ic_parent_task); } } } void ieee80211_stop(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; IEEE80211_LOCK(ic); ieee80211_stop_locked(vap); IEEE80211_UNLOCK(ic); } /* * Stop all vap's running on a device. */ void ieee80211_stop_all(struct ieee80211com *ic) { struct ieee80211vap *vap; IEEE80211_LOCK(ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { struct ifnet *ifp = vap->iv_ifp; if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ ieee80211_stop_locked(vap); } IEEE80211_UNLOCK(ic); } /* * Stop all vap's running on a device and arrange * for those that were running to be resumed. */ void ieee80211_suspend_all(struct ieee80211com *ic) { struct ieee80211vap *vap; IEEE80211_LOCK(ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { struct ifnet *ifp = vap->iv_ifp; if (IFNET_IS_UP_RUNNING(ifp)) { /* NB: avoid recursion */ vap->iv_flags_ext |= IEEE80211_FEXT_RESUME; ieee80211_stop_locked(vap); } } IEEE80211_UNLOCK(ic); } /* * Start all vap's marked for resume. */ void ieee80211_resume_all(struct ieee80211com *ic) { struct ieee80211vap *vap; IEEE80211_LOCK(ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { struct ifnet *ifp = vap->iv_ifp; if (!IFNET_IS_UP_RUNNING(ifp) && (vap->iv_flags_ext & IEEE80211_FEXT_RESUME)) { vap->iv_flags_ext &= ~IEEE80211_FEXT_RESUME; ieee80211_start_locked(vap); } } IEEE80211_UNLOCK(ic); } /* * Switch between turbo and non-turbo operating modes. * Use the specified channel flags to locate the new * channel, update 802.11 state, and then call back into * the driver to effect the change. */ void ieee80211_dturbo_switch(struct ieee80211vap *vap, int newflags) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_channel *chan; chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags); if (chan == NULL) { /* XXX should not happen */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: no channel with freq %u flags 0x%x\n", __func__, ic->ic_bsschan->ic_freq, newflags); return; } IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: %s -> %s (freq %u flags 0x%x)\n", __func__, ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)], ieee80211_phymode_name[ieee80211_chan2mode(chan)], chan->ic_freq, chan->ic_flags); ic->ic_bsschan = chan; ic->ic_prevchan = ic->ic_curchan; ic->ic_curchan = chan; ic->ic_set_channel(ic); /* NB: do not need to reset ERP state 'cuz we're in sta mode */ } void ieee80211_beacon_miss(struct ieee80211com *ic) { struct ieee80211vap *vap; if (ic->ic_flags & IEEE80211_F_SCAN) return; /* XXX locking */ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { /* * We only pass events through for sta vap's in RUN state; * may be too restrictive but for now this saves all the * handlers duplicating these checks. */ if (vap->iv_opmode == IEEE80211_M_STA && vap->iv_state == IEEE80211_S_RUN && vap->iv_bmiss != NULL) vap->iv_bmiss(vap); } } /* * Software beacon miss handling. Check if any beacons * were received in the last period. If not post a * beacon miss; otherwise reset the counter. */ void ieee80211_swbmiss(void *arg) { struct ieee80211vap *vap = arg; struct ieee80211com *ic = vap->iv_ic; /* XXX sleep state? */ KASSERT(vap->iv_state == IEEE80211_S_RUN, ("wrong state %d", vap->iv_state)); if (ic->ic_flags & IEEE80211_F_SCAN) { /* * If scanning just ignore and reset state. If we get a * bmiss after coming out of scan because we haven't had * time to receive a beacon then we should probe the AP * before posting a real bmiss (unless iv_bmiss_max has * been artifiically lowered). A cleaner solution might * be to disable the timer on scan start/end but to handle * case of multiple sta vap's we'd need to disable the * timers of all affected vap's. */ vap->iv_swbmiss_count = 0; } else if (vap->iv_swbmiss_count == 0) { if (vap->iv_bmiss != NULL) vap->iv_bmiss(vap); if (vap->iv_bmiss_count == 0) /* don't re-arm timer */ return; } else vap->iv_swbmiss_count = 0; callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, ieee80211_swbmiss, vap); } /* * Start an 802.11h channel switch. We record the parameters, * mark the operation pending, notify each vap through the * beacon update mechanism so it can update the beacon frame * contents, and then switch vap's to CSA state to block outbound * traffic. Devices that handle CSA directly can use the state * switch to do the right thing so long as they call * ieee80211_csa_completeswitch when it's time to complete the * channel change. Devices that depend on the net80211 layer can * use ieee80211_beacon_update to handle the countdown and the * channel switch. */ void ieee80211_csa_startswitch(struct ieee80211com *ic, struct ieee80211_channel *c, int mode, int count) { struct ieee80211vap *vap; IEEE80211_LOCK_ASSERT(ic); ic->ic_csa_newchan = c; ic->ic_csa_count = count; /* XXX record mode? */ ic->ic_flags |= IEEE80211_F_CSAPENDING; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_IBSS) ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA); /* switch to CSA state to block outbound traffic */ if (vap->iv_state == IEEE80211_S_RUN) ieee80211_new_state_locked(vap, IEEE80211_S_CSA, 0); } ieee80211_notify_csa(ic, c, mode, count); } /* * Complete an 802.11h channel switch started by ieee80211_csa_startswitch. * We clear state and move all vap's in CSA state to RUN state * so they can again transmit. */ void ieee80211_csa_completeswitch(struct ieee80211com *ic) { struct ieee80211vap *vap; IEEE80211_LOCK_ASSERT(ic); KASSERT(ic->ic_flags & IEEE80211_F_CSAPENDING, ("csa not pending")); ieee80211_setcurchan(ic, ic->ic_csa_newchan); ic->ic_csa_newchan = NULL; ic->ic_flags &= ~IEEE80211_F_CSAPENDING; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if (vap->iv_state == IEEE80211_S_CSA) ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); } /* * Complete a DFS CAC started by ieee80211_dfs_cac_start. * We clear state and move all vap's in CAC state to RUN state. */ void ieee80211_cac_completeswitch(struct ieee80211vap *vap0) { struct ieee80211com *ic = vap0->iv_ic; struct ieee80211vap *vap; IEEE80211_LOCK(ic); /* * Complete CAC state change for lead vap first; then * clock all the other vap's waiting. */ KASSERT(vap0->iv_state == IEEE80211_S_CAC, ("wrong state %d", vap0->iv_state)); ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if (vap->iv_state == IEEE80211_S_CAC) ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); IEEE80211_UNLOCK(ic); } /* * Force all vap's other than the specified vap to the INIT state * and mark them as waiting for a scan to complete. These vaps * will be brought up when the scan completes and the scanning vap * reaches RUN state by wakeupwaiting. * XXX if we do this in threads we can use sleep/wakeup. */ static void markwaiting(struct ieee80211vap *vap0) { struct ieee80211com *ic = vap0->iv_ic; struct ieee80211vap *vap; IEEE80211_LOCK_ASSERT(ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { if (vap == vap0) continue; if (vap->iv_state != IEEE80211_S_INIT) { vap->iv_newstate(vap, IEEE80211_S_INIT, 0); vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; } } } /* * Wakeup all vap's waiting for a scan to complete. This is the * companion to markwaiting (above) and is used to coordinate * multiple vaps scanning. */ static void wakeupwaiting(struct ieee80211vap *vap0) { struct ieee80211com *ic = vap0->iv_ic; struct ieee80211vap *vap; IEEE80211_LOCK_ASSERT(ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { if (vap == vap0) continue; if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) { vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; /* NB: sta's cannot go INIT->RUN */ vap->iv_newstate(vap, vap->iv_opmode == IEEE80211_M_STA ? IEEE80211_S_SCAN : IEEE80211_S_RUN, 0); } } } /* * Handle post state change work common to all operating modes. */ static void -ieee80211_newstate_cb(struct ieee80211vap *vap, - enum ieee80211_state nstate, int arg) +ieee80211_newstate_cb(void *xvap, int npending) { + struct ieee80211vap *vap = xvap; struct ieee80211com *ic = vap->iv_ic; + enum ieee80211_state ostate = vap->iv_state; + enum ieee80211_state nstate = vap->iv_nstate; + int arg = vap->iv_nstate_arg; + int rc; - IEEE80211_LOCK_ASSERT(ic); - IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s arg %d\n", __func__, ieee80211_state_name[nstate], arg); + IEEE80211_LOCK(ic); + rc = vap->iv_newstate(vap, nstate, arg); + if (rc != 0 || vap->iv_state != nstate) { + if (rc == EINPROGRESS) + if_printf(ic->ic_ifp, + "Warning, iv_newstate was deferred again\n"); + /* State transition failed */ + goto out; + } + + /* No actual transition, skip post processing */ + if (ostate == nstate) + goto out; + if (nstate == IEEE80211_S_RUN) { /* * OACTIVE may be set on the vap if the upper layer * tried to transmit (e.g. IPv6 NDP) before we reach * RUN state. Clear it and restart xmit. * * Note this can also happen as a result of SLEEP->RUN * (i.e. coming out of power save mode). */ vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if_start(vap->iv_ifp); /* bring up any vaps waiting on us */ wakeupwaiting(vap); } else if (nstate == IEEE80211_S_INIT) { /* * Flush the scan cache if we did the last scan (XXX?) * and flush any frames on send queues from this vap. * Note the mgt q is used only for legacy drivers and * will go away shortly. */ ieee80211_scan_flush(vap); /* XXX NB: cast for altq */ ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap); } - vap->iv_newstate_cb = NULL; +out: + IEEE80211_UNLOCK(ic); } /* * Public interface for initiating a state machine change. * This routine single-threads the request and coordinates * the scheduling of multiple vaps for the purpose of selecting * an operating channel. Specifically the following scenarios * are handled: * o only one vap can be selecting a channel so on transition to * SCAN state if another vap is already scanning then * mark the caller for later processing and return without * doing anything (XXX? expectations by caller of synchronous operation) * o only one vap can be doing CAC of a channel so on transition to * CAC state if another vap is already scanning for radar then * mark the caller for later processing and return without * doing anything (XXX? expectations by caller of synchronous operation) * o if another vap is already running when a request is made * to SCAN then an operating channel has been chosen; bypass * the scan and just join the channel * * Note that the state change call is done through the iv_newstate * method pointer so any driver routine gets invoked. The driver * will normally call back into operating mode-specific * ieee80211_newstate routines (below) unless it needs to completely * bypass the state machine (e.g. because the firmware has it's * own idea how things should work). Bypassing the net80211 layer * is usually a mistake and indicates lack of proper integration * with the net80211 layer. */ static int ieee80211_new_state_locked(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211vap *vp; enum ieee80211_state ostate; int nrunning, nscanning, rc; IEEE80211_LOCK_ASSERT(ic); nrunning = nscanning = 0; /* XXX can track this state instead of calculating */ TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) { if (vp != vap) { if (vp->iv_state >= IEEE80211_S_RUN) nrunning++; /* XXX doesn't handle bg scan */ /* NB: CAC+AUTH+ASSOC treated like SCAN */ else if (vp->iv_state > IEEE80211_S_INIT) nscanning++; } } ostate = vap->iv_state; IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (nrunning %d nscanning %d)\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate], nrunning, nscanning); switch (nstate) { case IEEE80211_S_SCAN: if (ostate == IEEE80211_S_INIT) { /* * INIT -> SCAN happens on initial bringup. */ KASSERT(!(nscanning && nrunning), ("%d scanning and %d running", nscanning, nrunning)); if (nscanning) { /* * Someone is scanning, defer our state * change until the work has completed. */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: defer %s -> %s\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; rc = 0; goto done; } if (nrunning) { /* * Someone is operating; just join the channel * they have chosen. */ /* XXX kill arg? */ /* XXX check each opmode, adhoc? */ if (vap->iv_opmode == IEEE80211_M_STA) nstate = IEEE80211_S_SCAN; else nstate = IEEE80211_S_RUN; #ifdef IEEE80211_DEBUG if (nstate != IEEE80211_S_SCAN) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: override, now %s -> %s\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); } #endif } } else { /* * SCAN was forced; e.g. on beacon miss. Force * other running vap's to INIT state and mark * them as waiting for the scan to complete. This * insures they don't interfere with our scanning. * * XXX not always right, assumes ap follows sta */ markwaiting(vap); } break; case IEEE80211_S_RUN: if (vap->iv_opmode == IEEE80211_M_WDS && (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) && nscanning) { /* * Legacy WDS with someone else scanning; don't * go online until that completes as we should * follow the other vap to the channel they choose. */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: defer %s -> %s (legacy WDS)\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; rc = 0; goto done; } if (vap->iv_opmode == IEEE80211_M_HOSTAP && IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && (vap->iv_flags_ext & IEEE80211_FEXT_DFS) && !IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) { /* * This is a DFS channel, transition to CAC state * instead of RUN. This allows us to initiate * Channel Availability Check (CAC) as specified * by 11h/DFS. */ nstate = IEEE80211_S_CAC; IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: override %s -> %s (DFS)\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); } break; case IEEE80211_S_INIT: if (ostate == IEEE80211_S_INIT ) { /* XXX don't believe this */ /* INIT -> INIT. nothing to do */ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; } /* fall thru... */ default: break; } - /* XXX on transition RUN->CAC do we need to set nstate = iv_state? */ - if (ostate != nstate) { - /* - * Arrange for work to happen after state change completes. - * If this happens asynchronously the caller must arrange - * for the com lock to be held. - */ - vap->iv_newstate_cb = ieee80211_newstate_cb; - } - rc = vap->iv_newstate(vap, nstate, arg); - if (rc == 0 && vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, nstate, arg); + /* The state change call to the driver runs in a thread */ + vap->iv_nstate = nstate; + vap->iv_nstate_arg = arg; + taskqueue_enqueue(ic->ic_tq, &vap->iv_nstate_task); + return (EINPROGRESS); done: return rc; } int ieee80211_new_state(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; int rc; IEEE80211_LOCK(ic); rc = ieee80211_new_state_locked(vap, nstate, arg); IEEE80211_UNLOCK(ic); return rc; } Index: user/thompsa/vaptq/sys/net80211/ieee80211_var.h =================================================================== --- user/thompsa/vaptq/sys/net80211/ieee80211_var.h (revision 185732) +++ user/thompsa/vaptq/sys/net80211/ieee80211_var.h (revision 185733) @@ -1,765 +1,767 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. * * $FreeBSD$ */ #ifndef _NET80211_IEEE80211_VAR_H_ #define _NET80211_IEEE80211_VAR_H_ /* * Definitions for IEEE 802.11 drivers. */ /* NB: portability glue must go first */ #ifdef __NetBSD__ #include #elif __FreeBSD__ #include #elif __linux__ #include #else #error "No support for your operating system!" #endif #include #include #include #include #include /* for ieee80211_stats */ #include #include #include #include #define IEEE80211_TXPOWER_MAX 100 /* .5 dbM (XXX units?) */ #define IEEE80211_TXPOWER_MIN 0 /* kill radio */ #define IEEE80211_DTIM_DEFAULT 1 /* default DTIM period */ #define IEEE80211_BINTVAL_DEFAULT 100 /* default beacon interval (TU's) */ #define IEEE80211_BMISS_MAX 2 /* maximum consecutive bmiss allowed */ #define IEEE80211_HWBMISS_DEFAULT 7 /* h/w bmiss threshold (beacons) */ #define IEEE80211_BGSCAN_INTVAL_MIN 15 /* min bg scan intvl (secs) */ #define IEEE80211_BGSCAN_INTVAL_DEFAULT (5*60) /* default bg scan intvl */ #define IEEE80211_BGSCAN_IDLE_MIN 100 /* min idle time (ms) */ #define IEEE80211_BGSCAN_IDLE_DEFAULT 250 /* default idle time (ms) */ #define IEEE80211_SCAN_VALID_MIN 10 /* min scan valid time (secs) */ #define IEEE80211_SCAN_VALID_DEFAULT 60 /* default scan valid time */ #define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */ #define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */ #define IEEE80211_FIXED_RATE_NONE 0xff #define IEEE80211_TXMAX_DEFAULT 6 /* default ucast max retries */ #define IEEE80211_RTS_DEFAULT IEEE80211_RTS_MAX #define IEEE80211_FRAG_DEFAULT IEEE80211_FRAG_MAX #define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) #define IEEE80211_TU_TO_MS(x) (((x) * 1024) / 1000) #define IEEE80211_TU_TO_TICKS(x)(((x) * 1024 * hz) / (1000 * 1000)) /* * 802.11 control state is split into a common portion that maps * 1-1 to a physical device and one or more "Virtual AP's" (VAP) * that are bound to an ieee80211com instance and share a single * underlying device. Each VAP has a corresponding OS device * entity through which traffic flows and that applications use * for issuing ioctls, etc. */ /* * Data common to one or more virtual AP's. State shared by * the underlying device and the net80211 layer is exposed here; * e.g. device-specific callbacks. */ struct ieee80211vap; typedef void (*ieee80211vap_attach)(struct ieee80211vap *); struct ieee80211_appie { uint16_t ie_len; /* size of ie_data */ uint8_t ie_data[]; /* user-specified IE's */ }; struct ieee80211com { struct ifnet *ic_ifp; /* associated device */ ieee80211_com_lock_t ic_comlock; /* state update lock */ TAILQ_HEAD(, ieee80211vap) ic_vaps; /* list of vap instances */ struct ieee80211_stats ic_stats; /* statistics */ int ic_headroom; /* driver tx headroom needs */ enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */ enum ieee80211_opmode ic_opmode; /* operation mode */ struct ifmedia ic_media; /* interface media config */ uint8_t ic_myaddr[IEEE80211_ADDR_LEN]; struct callout ic_inact; /* inactivity processing */ + struct taskqueue *ic_tq; /* deferred state thread */ struct task ic_parent_task; /* deferred parent processing */ uint32_t ic_flags; /* state flags */ uint32_t ic_flags_ext; /* extended state flags */ uint32_t ic_flags_ven; /* vendor state flags */ uint32_t ic_caps; /* capabilities */ uint32_t ic_htcaps; /* HT capabilities */ uint32_t ic_cryptocaps; /* crypto capabilities */ uint8_t ic_modecaps[2]; /* set of mode capabilities */ uint8_t ic_promisc; /* vap's needing promisc mode */ uint8_t ic_allmulti; /* vap's needing all multicast*/ uint8_t ic_nrunning; /* vap's marked running */ uint8_t ic_curmode; /* current mode */ uint16_t ic_bintval; /* beacon interval */ uint16_t ic_lintval; /* listen interval */ uint16_t ic_holdover; /* PM hold over duration */ uint16_t ic_txpowlimit; /* global tx power limit */ struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; /* * Channel state: * * ic_channels is the set of available channels for the device; * it is setup by the driver * ic_nchans is the number of valid entries in ic_channels * ic_chan_avail is a bit vector of these channels used to check * whether a channel is available w/o searching the channel table. * ic_chan_active is a (potentially) constrained subset of * ic_chan_avail that reflects any mode setting or user-specified * limit on the set of channels to use/scan * ic_curchan is the current channel the device is set to; it may * be different from ic_bsschan when we are off-channel scanning * or otherwise doing background work * ic_bsschan is the channel selected for operation; it may * be undefined (IEEE80211_CHAN_ANYC) * ic_prevchan is a cached ``previous channel'' used to optimize * lookups when switching back+forth between two channels * (e.g. for dynamic turbo) */ int ic_nchans; /* # entries in ic_channels */ struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1]; uint8_t ic_chan_avail[IEEE80211_CHAN_BYTES]; uint8_t ic_chan_active[IEEE80211_CHAN_BYTES]; uint8_t ic_chan_scan[IEEE80211_CHAN_BYTES]; struct ieee80211_channel *ic_curchan; /* current channel */ struct ieee80211_channel *ic_bsschan; /* bss channel */ struct ieee80211_channel *ic_prevchan; /* previous channel */ struct ieee80211_regdomain ic_regdomain;/* regulatory data */ struct ieee80211_appie *ic_countryie; /* calculated country ie */ struct ieee80211_channel *ic_countryie_chan; /* 802.11h/DFS state */ struct ieee80211_channel *ic_csa_newchan;/* channel for doing CSA */ int ic_csa_count; /* count for doing CSA */ struct ieee80211_dfs_state ic_dfs; /* DFS state */ struct ieee80211_scan_state *ic_scan; /* scan state */ int ic_lastdata; /* time of last data frame */ int ic_lastscan; /* time last scan completed */ /* NB: this is the union of all vap stations/neighbors */ int ic_max_keyix; /* max h/w key index */ struct ieee80211_node_table ic_sta; /* stations/neighbors */ /* XXX multi-bss: split out common/vap parts */ struct ieee80211_wme_state ic_wme; /* WME/WMM state */ /* XXX multi-bss: can per-vap be done/make sense? */ enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */ uint16_t ic_nonerpsta; /* # non-ERP stations */ uint16_t ic_longslotsta; /* # long slot time stations */ uint16_t ic_sta_assoc; /* stations associated */ uint16_t ic_ht_sta_assoc;/* HT stations associated */ uint16_t ic_ht40_sta_assoc;/* HT40 stations associated */ uint8_t ic_curhtprotmode;/* HTINFO bss state */ enum ieee80211_protmode ic_htprotmode; /* HT protection mode */ int ic_lastnonerp; /* last time non-ERP sta noted*/ int ic_lastnonht; /* last time non-HT sta noted */ /* virtual ap create/delete */ struct ieee80211vap* (*ic_vap_create)(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t macaddr[IEEE80211_ADDR_LEN]); void (*ic_vap_delete)(struct ieee80211vap *); /* operating mode attachment */ ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX]; /* return hardware/radio capabilities */ void (*ic_getradiocaps)(struct ieee80211com *, int *, struct ieee80211_channel []); /* check and/or prepare regdomain state change */ int (*ic_setregdomain)(struct ieee80211com *, struct ieee80211_regdomain *, int, struct ieee80211_channel []); /* send/recv 802.11 management frame */ int (*ic_send_mgmt)(struct ieee80211_node *, int, int); /* send raw 802.11 frame */ int (*ic_raw_xmit)(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); /* update device state for 802.11 slot time change */ void (*ic_updateslot)(struct ifnet *); /* handle multicast state changes */ void (*ic_update_mcast)(struct ifnet *); /* handle promiscuous mode changes */ void (*ic_update_promisc)(struct ifnet *); /* new station association callback/notification */ void (*ic_newassoc)(struct ieee80211_node *, int); /* node state management */ struct ieee80211_node* (*ic_node_alloc)(struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); void (*ic_node_free)(struct ieee80211_node *); void (*ic_node_cleanup)(struct ieee80211_node *); void (*ic_node_age)(struct ieee80211_node *); void (*ic_node_drain)(struct ieee80211_node *); int8_t (*ic_node_getrssi)(const struct ieee80211_node*); void (*ic_node_getsignal)(const struct ieee80211_node*, int8_t *, int8_t *); void (*ic_node_getmimoinfo)( const struct ieee80211_node*, struct ieee80211_mimo_info *); /* scanning support */ void (*ic_scan_start)(struct ieee80211com *); void (*ic_scan_end)(struct ieee80211com *); void (*ic_set_channel)(struct ieee80211com *); void (*ic_scan_curchan)(struct ieee80211_scan_state *, unsigned long); void (*ic_scan_mindwell)(struct ieee80211_scan_state *); /* * 802.11n ADDBA support. A simple/generic implementation * of A-MPDU tx aggregation is provided; the driver may * override these methods to provide their own support. * A-MPDU rx re-ordering happens automatically if the * driver passes out-of-order frames to ieee80211_input * from an assocated HT station. */ void (*ic_recv_action)(struct ieee80211_node *, const uint8_t *frm, const uint8_t *efrm); int (*ic_send_action)(struct ieee80211_node *, int category, int action, uint16_t args[4]); /* check if A-MPDU should be enabled this station+ac */ int (*ic_ampdu_enable)(struct ieee80211_node *, struct ieee80211_tx_ampdu *); /* start/stop doing A-MPDU tx aggregation for a station */ int (*ic_addba_request)(struct ieee80211_node *, struct ieee80211_tx_ampdu *, int dialogtoken, int baparamset, int batimeout); int (*ic_addba_response)(struct ieee80211_node *, struct ieee80211_tx_ampdu *, int status, int baparamset, int batimeout); void (*ic_addba_stop)(struct ieee80211_node *, struct ieee80211_tx_ampdu *); /* BAR response received */ void (*ic_bar_response)(struct ieee80211_node *, struct ieee80211_tx_ampdu *, int status); }; struct ieee80211_aclator; struct ieee80211vap { struct ifmedia iv_media; /* interface media config */ struct ifnet *iv_ifp; /* associated device */ struct bpf_if *iv_rawbpf; /* packet filter structure */ struct sysctl_ctx_list *iv_sysctl; /* dynamic sysctl context */ struct sysctl_oid *iv_oid; /* net.wlan.X sysctl oid */ TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */ struct ieee80211com *iv_ic; /* back ptr to common state */ uint32_t iv_debug; /* debug msg flags */ struct ieee80211_stats iv_stats; /* statistics */ uint8_t iv_myaddr[IEEE80211_ADDR_LEN]; uint32_t iv_flags; /* state flags */ uint32_t iv_flags_ext; /* extended state flags */ uint32_t iv_flags_ven; /* vendor state flags */ uint32_t iv_caps; /* capabilities */ uint32_t iv_htcaps; /* HT capabilities */ enum ieee80211_opmode iv_opmode; /* operation mode */ enum ieee80211_state iv_state; /* state machine state */ - void (*iv_newstate_cb)(struct ieee80211vap *, - enum ieee80211_state, int); + enum ieee80211_state iv_nstate; /* pending state */ + int iv_nstate_arg; /* pending state arg */ + struct task iv_nstate_task; /* deferred state processing */ struct callout iv_mgtsend; /* mgmt frame response timer */ /* inactivity timer settings */ int iv_inact_init; /* setting for new station */ int iv_inact_auth; /* auth but not assoc setting */ int iv_inact_run; /* authorized setting */ int iv_inact_probe; /* inactive probe time */ int iv_des_nssid; /* # desired ssids */ struct ieee80211_scan_ssid iv_des_ssid[1];/* desired ssid table */ uint8_t iv_des_bssid[IEEE80211_ADDR_LEN]; struct ieee80211_channel *iv_des_chan; /* desired channel */ uint16_t iv_des_mode; /* desired mode */ int iv_nicknamelen; /* XXX junk */ uint8_t iv_nickname[IEEE80211_NWID_LEN]; u_int iv_bgscanidle; /* bg scan idle threshold */ u_int iv_bgscanintvl; /* bg scan min interval */ u_int iv_scanvalid; /* scan cache valid threshold */ u_int iv_scanreq_duration; u_int iv_scanreq_mindwell; u_int iv_scanreq_maxdwell; uint16_t iv_scanreq_flags;/* held scan request params */ uint8_t iv_scanreq_nssid; struct ieee80211_scan_ssid iv_scanreq_ssid[IEEE80211_SCAN_MAX_SSID]; /* sta-mode roaming state */ enum ieee80211_roamingmode iv_roaming; /* roaming mode */ struct ieee80211_roamparam iv_roamparms[IEEE80211_MODE_MAX]; uint8_t iv_bmissthreshold; uint8_t iv_bmiss_count; /* current beacon miss count */ int iv_bmiss_max; /* max bmiss before scan */ uint16_t iv_swbmiss_count;/* beacons in last period */ uint16_t iv_swbmiss_period;/* s/w bmiss period */ struct callout iv_swbmiss; /* s/w beacon miss timer */ int iv_ampdu_rxmax; /* A-MPDU rx limit (bytes) */ int iv_ampdu_density;/* A-MPDU density */ int iv_ampdu_limit; /* A-MPDU tx limit (bytes) */ int iv_amsdu_limit; /* A-MSDU tx limit (bytes) */ u_int iv_ampdu_mintraffic[WME_NUM_AC]; uint32_t *iv_aid_bitmap; /* association id map */ uint16_t iv_max_aid; uint16_t iv_sta_assoc; /* stations associated */ uint16_t iv_ps_sta; /* stations in power save */ uint16_t iv_ps_pending; /* ps sta's w/ pending frames */ uint16_t iv_txseq; /* mcast xmit seq# space */ uint16_t iv_tim_len; /* ic_tim_bitmap size (bytes) */ uint8_t *iv_tim_bitmap; /* power-save stations w/ data*/ uint8_t iv_dtim_period; /* DTIM period */ uint8_t iv_dtim_count; /* DTIM count from last bcn */ /* set/unset aid pwrsav state */ int iv_csa_count; /* count for doing CSA */ struct ieee80211_node *iv_bss; /* information for this node */ struct ieee80211_txparam iv_txparms[IEEE80211_MODE_MAX]; uint16_t iv_rtsthreshold; uint16_t iv_fragthreshold; int iv_inact_timer; /* inactivity timer wait */ /* application-specified IE's to attach to mgt frames */ struct ieee80211_appie *iv_appie_beacon; struct ieee80211_appie *iv_appie_probereq; struct ieee80211_appie *iv_appie_proberesp; struct ieee80211_appie *iv_appie_assocreq; struct ieee80211_appie *iv_appie_assocresp; struct ieee80211_appie *iv_appie_wpa; uint8_t *iv_wpa_ie; uint8_t *iv_rsn_ie; uint16_t iv_max_keyix; /* max h/w key index */ ieee80211_keyix iv_def_txkey; /* default/group tx key index */ struct ieee80211_key iv_nw_keys[IEEE80211_WEP_NKID]; int (*iv_key_alloc)(struct ieee80211vap *, struct ieee80211_key *, ieee80211_keyix *, ieee80211_keyix *); int (*iv_key_delete)(struct ieee80211vap *, const struct ieee80211_key *); int (*iv_key_set)(struct ieee80211vap *, const struct ieee80211_key *, const uint8_t mac[IEEE80211_ADDR_LEN]); void (*iv_key_update_begin)(struct ieee80211vap *); void (*iv_key_update_end)(struct ieee80211vap *); const struct ieee80211_authenticator *iv_auth; /* authenticator glue */ void *iv_ec; /* private auth state */ const struct ieee80211_aclator *iv_acl; /* acl glue */ void *iv_as; /* private aclator state */ /* operate-mode detach hook */ void (*iv_opdetach)(struct ieee80211vap *); /* receive processing */ int (*iv_input)(struct ieee80211_node *, struct mbuf *, int rssi, int noise, uint32_t rstamp); void (*iv_recv_mgmt)(struct ieee80211_node *, struct mbuf *, int, int, int, uint32_t); void (*iv_deliver_data)(struct ieee80211vap *, struct ieee80211_node *, struct mbuf *); #if 0 /* send processing */ int (*iv_send_mgmt)(struct ieee80211_node *, int, int); #endif /* beacon miss processing */ void (*iv_bmiss)(struct ieee80211vap *); /* reset device state after 802.11 parameter/state change */ int (*iv_reset)(struct ieee80211vap *, u_long); /* [schedule] beacon frame update */ void (*iv_update_beacon)(struct ieee80211vap *, int); /* power save handling */ void (*iv_update_ps)(struct ieee80211vap *, int); int (*iv_set_tim)(struct ieee80211_node *, int); /* state machine processing */ int (*iv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); /* 802.3 output method for raw frame xmit */ int (*iv_output)(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); }; MALLOC_DECLARE(M_80211_VAP); #define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) #define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) /* ic_flags/iv_flags */ #define IEEE80211_F_TURBOP 0x00000001 /* CONF: ATH Turbo enabled*/ #define IEEE80211_F_COMP 0x00000002 /* CONF: ATH comp enabled */ #define IEEE80211_F_FF 0x00000004 /* CONF: ATH FF enabled */ #define IEEE80211_F_BURST 0x00000008 /* CONF: bursting enabled */ /* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */ #define IEEE80211_F_PRIVACY 0x00000010 /* CONF: privacy enabled */ #define IEEE80211_F_PUREG 0x00000020 /* CONF: 11g w/o 11b sta's */ #define IEEE80211_F_SCAN 0x00000080 /* STATUS: scanning */ #define IEEE80211_F_ASCAN 0x00000100 /* STATUS: active scan */ #define IEEE80211_F_SIBSS 0x00000200 /* STATUS: start IBSS */ /* NB: this is intentionally setup to be IEEE80211_CAPINFO_SHORT_SLOTTIME */ #define IEEE80211_F_SHSLOT 0x00000400 /* STATUS: use short slot time*/ #define IEEE80211_F_PMGTON 0x00000800 /* CONF: Power mgmt enable */ #define IEEE80211_F_DESBSSID 0x00001000 /* CONF: des_bssid is set */ #define IEEE80211_F_WME 0x00002000 /* CONF: enable WME use */ #define IEEE80211_F_BGSCAN 0x00004000 /* CONF: bg scan enabled (???)*/ #define IEEE80211_F_SWRETRY 0x00008000 /* CONF: sw tx retry enabled */ #define IEEE80211_F_TXPOW_FIXED 0x00010000 /* TX Power: fixed rate */ #define IEEE80211_F_IBSSON 0x00020000 /* CONF: IBSS creation enable */ #define IEEE80211_F_SHPREAMBLE 0x00040000 /* STATUS: use short preamble */ #define IEEE80211_F_DATAPAD 0x00080000 /* CONF: do alignment pad */ #define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */ #define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/ #define IEEE80211_F_CSAPENDING 0x00400000 /* STATUS: chan switch pending*/ #define IEEE80211_F_WPA1 0x00800000 /* CONF: WPA enabled */ #define IEEE80211_F_WPA2 0x01000000 /* CONF: WPA2 enabled */ #define IEEE80211_F_WPA 0x01800000 /* CONF: WPA/WPA2 enabled */ #define IEEE80211_F_DROPUNENC 0x02000000 /* CONF: drop unencrypted */ #define IEEE80211_F_COUNTERM 0x04000000 /* CONF: TKIP countermeasures */ #define IEEE80211_F_HIDESSID 0x08000000 /* CONF: hide SSID in beacon */ #define IEEE80211_F_NOBRIDGE 0x10000000 /* CONF: dis. internal bridge */ #define IEEE80211_F_PCF 0x20000000 /* CONF: PCF enabled */ #define IEEE80211_F_DOTH 0x40000000 /* CONF: 11h enabled */ #define IEEE80211_F_DWDS 0x80000000 /* CONF: Dynamic WDS enabled */ /* Atheros protocol-specific flags */ #define IEEE80211_F_ATHEROS \ (IEEE80211_F_FF | IEEE80211_F_COMP | IEEE80211_F_TURBOP) /* Check if an Atheros capability was negotiated for use */ #define IEEE80211_ATH_CAP(vap, ni, bit) \ ((vap)->iv_flags & (ni)->ni_ath_flags & (bit)) /* ic_flags_ext/iv_flags_ext */ #define IEEE80211_FEXT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */ #define IEEE80211_FEXT_INACT 0x00000002 /* CONF: sta inact handling */ #define IEEE80211_FEXT_SCANWAIT 0x00000004 /* STATUS: awaiting scan */ /* 0x00000006 reserved */ #define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: complete bgscan */ #define IEEE80211_FEXT_WPS 0x00000010 /* CONF: WPS enabled */ #define IEEE80211_FEXT_TSN 0x00000020 /* CONF: TSN enabled */ #define IEEE80211_FEXT_SCANREQ 0x00000040 /* STATUS: scan req params */ #define IEEE80211_FEXT_RESUME 0x00000080 /* STATUS: start on resume */ #define IEEE80211_FEXT_NONERP_PR 0x00000200 /* STATUS: non-ERP sta present*/ #define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */ #define IEEE80211_FEXT_DFS 0x00000800 /* CONF: DFS enabled */ #define IEEE80211_FEXT_DOTD 0x00001000 /* CONF: 11d enabled */ /* NB: immutable: should be set only when creating a vap */ #define IEEE80211_FEXT_WDSLEGACY 0x00010000 /* CONF: legacy WDS operation */ #define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/ #define IEEE80211_FEXT_HT 0x00080000 /* CONF: HT supported */ #define IEEE80211_FEXT_AMPDU_TX 0x00100000 /* CONF: A-MPDU tx supported */ #define IEEE80211_FEXT_AMPDU_RX 0x00200000 /* CONF: A-MPDU tx supported */ #define IEEE80211_FEXT_AMSDU_TX 0x00400000 /* CONF: A-MSDU tx supported */ #define IEEE80211_FEXT_AMSDU_RX 0x00800000 /* CONF: A-MSDU tx supported */ #define IEEE80211_FEXT_USEHT40 0x01000000 /* CONF: 20/40 use enabled */ #define IEEE80211_FEXT_PUREN 0x02000000 /* CONF: 11n w/o legacy sta's */ #define IEEE80211_FEXT_SHORTGI20 0x04000000 /* CONF: short GI in HT20 */ #define IEEE80211_FEXT_SHORTGI40 0x08000000 /* CONF: short GI in HT40 */ #define IEEE80211_FEXT_HTCOMPAT 0x10000000 /* CONF: HT vendor OUI's */ #define IEEE80211_FEXT_RIFS 0x20000000 /* CONF: RIFS enabled */ /* ic_caps/iv_caps: device driver capabilities */ /* 0x2f available */ #define IEEE80211_C_STA 0x00000001 /* CAPABILITY: STA available */ #define IEEE80211_C_FF 0x00000040 /* CAPABILITY: ATH FF avail */ #define IEEE80211_C_TURBOP 0x00000080 /* CAPABILITY: ATH Turbo avail*/ #define IEEE80211_C_IBSS 0x00000100 /* CAPABILITY: IBSS available */ #define IEEE80211_C_PMGT 0x00000200 /* CAPABILITY: Power mgmt */ #define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */ #define IEEE80211_C_AHDEMO 0x00000800 /* CAPABILITY: Old Adhoc Demo */ #define IEEE80211_C_SWRETRY 0x00001000 /* CAPABILITY: sw tx retry */ #define IEEE80211_C_TXPMGT 0x00002000 /* CAPABILITY: tx power mgmt */ #define IEEE80211_C_SHSLOT 0x00004000 /* CAPABILITY: short slottime */ #define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */ #define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */ #define IEEE80211_C_DFS 0x00020000 /* CAPABILITY: DFS/radar avail*/ /* 0x7c0000 available */ #define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ #define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */ #define IEEE80211_C_WPA 0x01800000 /* CAPABILITY: WPA1+WPA2 avail*/ #define IEEE80211_C_BURST 0x02000000 /* CAPABILITY: frame bursting */ #define IEEE80211_C_WME 0x04000000 /* CAPABILITY: WME avail */ #define IEEE80211_C_WDS 0x08000000 /* CAPABILITY: 4-addr support */ /* 0x10000000 reserved */ #define IEEE80211_C_BGSCAN 0x20000000 /* CAPABILITY: bg scanning */ #define IEEE80211_C_TXFRAG 0x40000000 /* CAPABILITY: tx fragments */ /* XXX protection/barker? */ #define IEEE80211_C_OPMODE \ (IEEE80211_C_STA | IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | \ IEEE80211_C_AHDEMO | IEEE80211_C_MONITOR | IEEE80211_C_WDS) /* * ic_htcaps/iv_htcaps: HT-specific device/driver capabilities * * NB: the low 16-bits are the 802.11 definitions, the upper * 16-bits are used to define s/w/driver capabilities. */ #define IEEE80211_HTC_AMPDU 0x00010000 /* CAPABILITY: A-MPDU tx */ #define IEEE80211_HTC_AMSDU 0x00020000 /* CAPABILITY: A-MSDU tx */ /* NB: HT40 is implied by IEEE80211_HTCAP_CHWIDTH40 */ #define IEEE80211_HTC_HT 0x00040000 /* CAPABILITY: HT operation */ #define IEEE80211_HTC_SMPS 0x00080000 /* CAPABILITY: MIMO power save*/ #define IEEE80211_HTC_RIFS 0x00100000 /* CAPABILITY: RIFS support */ void ieee80211_ifattach(struct ieee80211com *); void ieee80211_ifdetach(struct ieee80211com *); int ieee80211_vap_setup(struct ieee80211com *, struct ieee80211vap *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t macaddr[IEEE80211_ADDR_LEN]); int ieee80211_vap_attach(struct ieee80211vap *, ifm_change_cb_t, ifm_stat_cb_t); void ieee80211_vap_detach(struct ieee80211vap *); const struct ieee80211_rateset *ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *); void ieee80211_announce(struct ieee80211com *); void ieee80211_announce_channels(struct ieee80211com *); void ieee80211_drain(struct ieee80211com *); void ieee80211_media_init(struct ieee80211com *); struct ieee80211com *ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]); int ieee80211_media_change(struct ifnet *); void ieee80211_media_status(struct ifnet *, struct ifmediareq *); int ieee80211_ioctl(struct ifnet *, u_long, caddr_t); int ieee80211_rate2media(struct ieee80211com *, int, enum ieee80211_phymode); int ieee80211_media2rate(int); int ieee80211_mhz2ieee(u_int, u_int); int ieee80211_chan2ieee(struct ieee80211com *, const struct ieee80211_channel *); u_int ieee80211_ieee2mhz(u_int, u_int); struct ieee80211_channel *ieee80211_find_channel(struct ieee80211com *, int freq, int flags); struct ieee80211_channel *ieee80211_find_channel_byieee(struct ieee80211com *, int ieee, int flags); int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode); enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *); /* * Key update synchronization methods. XXX should not be visible. */ static __inline void ieee80211_key_update_begin(struct ieee80211vap *vap) { vap->iv_key_update_begin(vap); } static __inline void ieee80211_key_update_end(struct ieee80211vap *vap) { vap->iv_key_update_end(vap); } /* * XXX these need to be here for IEEE80211_F_DATAPAD */ /* * Return the space occupied by the 802.11 header and any * padding required by the driver. This works for a * management or data frame. */ static __inline int ieee80211_hdrspace(struct ieee80211com *ic, const void *data) { int size = ieee80211_hdrsize(data); if (ic->ic_flags & IEEE80211_F_DATAPAD) size = roundup(size, sizeof(uint32_t)); return size; } /* * Like ieee80211_hdrspace, but handles any type of frame. */ static __inline int ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data) { int size = ieee80211_anyhdrsize(data); if (ic->ic_flags & IEEE80211_F_DATAPAD) size = roundup(size, sizeof(uint32_t)); return size; } /* * Notify a vap that beacon state has been updated. */ static __inline void ieee80211_beacon_notify(struct ieee80211vap *vap, int what) { if (vap->iv_state == IEEE80211_S_RUN) vap->iv_update_beacon(vap, what); } /* * Calculate HT channel promotion flags for a channel. * XXX belongs in ieee80211_ht.h but needs IEEE80211_FEXT_* */ static __inline int ieee80211_htchanflags(const struct ieee80211_channel *c) { return IEEE80211_IS_CHAN_HT40(c) ? IEEE80211_FEXT_HT | IEEE80211_FEXT_USEHT40 : IEEE80211_IS_CHAN_HT(c) ? IEEE80211_FEXT_HT : 0; } /* * Debugging facilities compiled in when IEEE80211_DEBUG is defined. * * The intent is that any problem in the net80211 layer can be * diagnosed by inspecting the statistics (dumped by the wlanstats * program) and/or the msgs generated by net80211. Messages are * broken into functional classes and can be controlled with the * wlandebug program. Certain of these msg groups are for facilities * that are no longer part of net80211 (e.g. IEEE80211_MSG_DOT1X). */ #define IEEE80211_MSG_11N 0x80000000 /* 11n mode debug */ #define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */ #define IEEE80211_MSG_DUMPPKTS 0x20000000 /* IFF_LINK2 equivalant */ #define IEEE80211_MSG_CRYPTO 0x10000000 /* crypto work */ #define IEEE80211_MSG_INPUT 0x08000000 /* input handling */ #define IEEE80211_MSG_XRATE 0x04000000 /* rate set handling */ #define IEEE80211_MSG_ELEMID 0x02000000 /* element id parsing */ #define IEEE80211_MSG_NODE 0x01000000 /* node handling */ #define IEEE80211_MSG_ASSOC 0x00800000 /* association handling */ #define IEEE80211_MSG_AUTH 0x00400000 /* authentication handling */ #define IEEE80211_MSG_SCAN 0x00200000 /* scanning */ #define IEEE80211_MSG_OUTPUT 0x00100000 /* output handling */ #define IEEE80211_MSG_STATE 0x00080000 /* state machine */ #define IEEE80211_MSG_POWER 0x00040000 /* power save handling */ #define IEEE80211_MSG_DOT1X 0x00020000 /* 802.1x authenticator */ #define IEEE80211_MSG_DOT1XSM 0x00010000 /* 802.1x state machine */ #define IEEE80211_MSG_RADIUS 0x00008000 /* 802.1x radius client */ #define IEEE80211_MSG_RADDUMP 0x00004000 /* dump 802.1x radius packets */ #define IEEE80211_MSG_RADKEYS 0x00002000 /* dump 802.1x keys */ #define IEEE80211_MSG_WPA 0x00001000 /* WPA/RSN protocol */ #define IEEE80211_MSG_ACL 0x00000800 /* ACL handling */ #define IEEE80211_MSG_WME 0x00000400 /* WME protocol */ #define IEEE80211_MSG_SUPERG 0x00000200 /* Atheros SuperG protocol */ #define IEEE80211_MSG_DOTH 0x00000100 /* 802.11h support */ #define IEEE80211_MSG_INACT 0x00000080 /* inactivity handling */ #define IEEE80211_MSG_ROAM 0x00000040 /* sta-mode roaming */ #define IEEE80211_MSG_RATECTL 0x00000020 /* tx rate control */ #define IEEE80211_MSG_ACTION 0x00000010 /* action frame handling */ #define IEEE80211_MSG_WDS 0x00000008 /* WDS handling */ #define IEEE80211_MSG_IOCTL 0x00000004 /* ioctl handling */ #define IEEE80211_MSG_ANY 0xffffffff /* anything */ #ifdef IEEE80211_DEBUG #define ieee80211_msg(_vap, _m) ((_vap)->iv_debug & (_m)) #define IEEE80211_DPRINTF(_vap, _m, _fmt, ...) do { \ if (ieee80211_msg(_vap, _m)) \ ieee80211_note(_vap, _fmt, __VA_ARGS__); \ } while (0) #define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...) do { \ if (ieee80211_msg(_vap, _m)) \ ieee80211_note_mac(_vap, (_ni)->ni_macaddr, _fmt, __VA_ARGS__);\ } while (0) #define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...) do { \ if (ieee80211_msg(_vap, _m)) \ ieee80211_note_mac(_vap, _mac, _fmt, __VA_ARGS__); \ } while (0) #define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...) do { \ if (ieee80211_msg(_vap, _m)) \ ieee80211_note_frame(_vap, _wh, _fmt, __VA_ARGS__); \ } while (0) void ieee80211_note(struct ieee80211vap *, const char *, ...); void ieee80211_note_mac(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN], const char *, ...); void ieee80211_note_frame(struct ieee80211vap *, const struct ieee80211_frame *, const char *, ...); #define ieee80211_msg_debug(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_DEBUG) #define ieee80211_msg_dumppkts(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_DUMPPKTS) #define ieee80211_msg_input(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_INPUT) #define ieee80211_msg_radius(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_RADIUS) #define ieee80211_msg_dumpradius(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_RADDUMP) #define ieee80211_msg_dumpradkeys(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_RADKEYS) #define ieee80211_msg_scan(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_SCAN) #define ieee80211_msg_assoc(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_ASSOC) /* * Emit a debug message about discarding a frame or information * element. One format is for extracting the mac address from * the frame header; the other is for when a header is not * available or otherwise appropriate. */ #define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...) do { \ if ((_vap)->iv_debug & (_m)) \ ieee80211_discard_frame(_vap, _wh, _type, _fmt, __VA_ARGS__);\ } while (0) #define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...) do { \ if ((_vap)->iv_debug & (_m)) \ ieee80211_discard_ie(_vap, _wh, _type, _fmt, __VA_ARGS__);\ } while (0) #define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...) do { \ if ((_vap)->iv_debug & (_m)) \ ieee80211_discard_mac(_vap, _mac, _type, _fmt, __VA_ARGS__);\ } while (0) void ieee80211_discard_frame(struct ieee80211vap *, const struct ieee80211_frame *, const char *type, const char *fmt, ...); void ieee80211_discard_ie(struct ieee80211vap *, const struct ieee80211_frame *, const char *type, const char *fmt, ...); void ieee80211_discard_mac(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN], const char *type, const char *fmt, ...); #else #define IEEE80211_DPRINTF(_vap, _m, _fmt, ...) #define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...) #define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...) #define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...) #define ieee80211_msg_dumppkts(_vap) 0 #define ieee80211_msg(_vap, _m) 0 #define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...) #define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...) #define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...) #endif #endif /* _NET80211_IEEE80211_VAR_H_ */