Index: stable/11/sys/compat/ndis/kern_ndis.c =================================================================== --- stable/11/sys/compat/ndis/kern_ndis.c (revision 319725) +++ stable/11/sys/compat/ndis/kern_ndis.c (revision 319726) @@ -1,1441 +1,1444 @@ /*- * 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. */ #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_DUMMY_PATH "\\\\some\\bogus\\path" #define NDIS_FLAG_RDONLY 1 static void ndis_status_func(ndis_handle, ndis_status, void *, uint32_t); static void ndis_statusdone_func(ndis_handle); static void ndis_setdone_func(ndis_handle, ndis_status); static void ndis_getdone_func(ndis_handle, ndis_status); static void ndis_resetdone_func(ndis_handle, ndis_status, uint8_t); static void ndis_sendrsrcavail_func(ndis_handle); static void ndis_intrsetup(kdpc *, device_object *, irp *, struct ndis_softc *); static void ndis_return(device_object *, void *); static image_patch_table kernndis_functbl[] = { IMPORT_SFUNC(ndis_status_func, 4), IMPORT_SFUNC(ndis_statusdone_func, 1), IMPORT_SFUNC(ndis_setdone_func, 2), IMPORT_SFUNC(ndis_getdone_func, 2), IMPORT_SFUNC(ndis_resetdone_func, 3), IMPORT_SFUNC(ndis_sendrsrcavail_func, 1), IMPORT_SFUNC(ndis_intrsetup, 4), IMPORT_SFUNC(ndis_return, 1), { NULL, NULL, NULL } }; static struct nd_head ndis_devhead; /* * This allows us to export our symbols to other modules. * Note that we call ourselves 'ndisapi' to avoid a namespace * collision with if_ndis.ko, which internally calls itself * 'ndis.' * * Note: some of the subsystems depend on each other, so the * order in which they're started is important. The order of * importance is: * * HAL - spinlocks and IRQL manipulation * ntoskrnl - DPC and workitem threads, object waiting * windrv - driver/device registration * * The HAL should also be the last thing shut down, since * the ntoskrnl subsystem will use spinlocks right up until * the DPC and workitem threads are terminated. */ static int ndis_modevent(module_t mod, int cmd, void *arg) { int error = 0; image_patch_table *patch; switch (cmd) { case MOD_LOAD: /* Initialize subsystems */ hal_libinit(); ntoskrnl_libinit(); windrv_libinit(); ndis_libinit(); usbd_libinit(); patch = kernndis_functbl; while (patch->ipt_func != NULL) { windrv_wrap((funcptr)patch->ipt_func, (funcptr *)&patch->ipt_wrap, patch->ipt_argcnt, patch->ipt_ftype); patch++; } TAILQ_INIT(&ndis_devhead); break; case MOD_SHUTDOWN: if (TAILQ_FIRST(&ndis_devhead) == NULL) { /* Shut down subsystems */ ndis_libfini(); usbd_libfini(); windrv_libfini(); ntoskrnl_libfini(); hal_libfini(); patch = kernndis_functbl; while (patch->ipt_func != NULL) { windrv_unwrap(patch->ipt_wrap); patch++; } } break; case MOD_UNLOAD: /* Shut down subsystems */ ndis_libfini(); usbd_libfini(); windrv_libfini(); ntoskrnl_libfini(); hal_libfini(); patch = kernndis_functbl; while (patch->ipt_func != NULL) { windrv_unwrap(patch->ipt_wrap); patch++; } break; default: error = EINVAL; break; } return (error); } DEV_MODULE(ndisapi, ndis_modevent, NULL); MODULE_VERSION(ndisapi, 1); static void ndis_sendrsrcavail_func(adapter) ndis_handle adapter; { } static void ndis_status_func(adapter, status, sbuf, slen) ndis_handle adapter; ndis_status status; void *sbuf; uint32_t slen; { 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 (ifp->if_flags & IFF_DEBUG) + ifp = NDISUSB_GET_IFNET(sc); + if ( ifp && ifp->if_flags & IFF_DEBUG) device_printf(sc->ndis_dev, "status: %x\n", status); } static void ndis_statusdone_func(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 (ifp->if_flags & IFF_DEBUG) + ifp = NDISUSB_GET_IFNET(sc); + if (ifp && ifp->if_flags & IFF_DEBUG) device_printf(sc->ndis_dev, "status complete\n"); } static void ndis_setdone_func(adapter, status) ndis_handle adapter; ndis_status status; { ndis_miniport_block *block; block = adapter; block->nmb_setstat = status; KeSetEvent(&block->nmb_setevent, IO_NO_INCREMENT, FALSE); } static void ndis_getdone_func(adapter, status) ndis_handle adapter; ndis_status status; { ndis_miniport_block *block; block = adapter; block->nmb_getstat = status; KeSetEvent(&block->nmb_getevent, IO_NO_INCREMENT, FALSE); } static void ndis_resetdone_func(ndis_handle adapter, ndis_status status, uint8_t addressingreset) { 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; + ifp = NDISUSB_GET_IFNET(sc); - if (ifp->if_flags & IFF_DEBUG) + if (ifp && ifp->if_flags & IFF_DEBUG) device_printf(sc->ndis_dev, "reset done...\n"); KeSetEvent(&block->nmb_resetevent, IO_NO_INCREMENT, FALSE); } int ndis_create_sysctls(arg) void *arg; { struct ndis_softc *sc; ndis_cfg *vals; char buf[256]; struct sysctl_oid *oidp; struct sysctl_ctx_entry *e; if (arg == NULL) return (EINVAL); sc = arg; + /* + device_printf(sc->ndis_dev, "ndis_create_sysctls() sc=%p\n", sc); + */ vals = sc->ndis_regvals; TAILQ_INIT(&sc->ndis_cfglist_head); /* Add the driver-specific registry keys. */ while(1) { if (vals->nc_cfgkey == NULL) break; if (vals->nc_idx != sc->ndis_devidx) { vals++; continue; } /* See if we already have a sysctl with this name */ oidp = NULL; TAILQ_FOREACH(e, device_get_sysctl_ctx(sc->ndis_dev), link) { oidp = e->entry; if (strcasecmp(oidp->oid_name, vals->nc_cfgkey) == 0) break; oidp = NULL; } if (oidp != NULL) { vals++; continue; } ndis_add_sysctl(sc, vals->nc_cfgkey, vals->nc_cfgdesc, vals->nc_val, CTLFLAG_RW); vals++; } /* Now add a couple of builtin keys. */ /* * Environment can be either Windows (0) or WindowsNT (1). * We qualify as the latter. */ ndis_add_sysctl(sc, "Environment", "Windows environment", "1", NDIS_FLAG_RDONLY); /* NDIS version should be 5.1. */ ndis_add_sysctl(sc, "NdisVersion", "NDIS API Version", "0x00050001", NDIS_FLAG_RDONLY); /* * Some miniport drivers rely on the existence of the SlotNumber, * NetCfgInstanceId and DriverDesc keys. */ ndis_add_sysctl(sc, "SlotNumber", "Slot Numer", "01", NDIS_FLAG_RDONLY); ndis_add_sysctl(sc, "NetCfgInstanceId", "NetCfgInstanceId", "{12345678-1234-5678-CAFE0-123456789ABC}", NDIS_FLAG_RDONLY); ndis_add_sysctl(sc, "DriverDesc", "Driver Description", "NDIS Network Adapter", NDIS_FLAG_RDONLY); /* Bus type (PCI, PCMCIA, etc...) */ sprintf(buf, "%d", (int)sc->ndis_iftype); ndis_add_sysctl(sc, "BusType", "Bus Type", buf, NDIS_FLAG_RDONLY); if (sc->ndis_res_io != NULL) { sprintf(buf, "0x%jx", rman_get_start(sc->ndis_res_io)); ndis_add_sysctl(sc, "IOBaseAddress", "Base I/O Address", buf, NDIS_FLAG_RDONLY); } if (sc->ndis_irq != NULL) { sprintf(buf, "%ju", rman_get_start(sc->ndis_irq)); ndis_add_sysctl(sc, "InterruptNumber", "Interrupt Number", buf, NDIS_FLAG_RDONLY); } return (0); } int ndis_add_sysctl(arg, key, desc, val, flag_rdonly) void *arg; char *key; char *desc; char *val; int flag_rdonly; { struct ndis_softc *sc; struct ndis_cfglist *cfg; char descstr[256]; sc = arg; cfg = malloc(sizeof(struct ndis_cfglist), M_DEVBUF, M_NOWAIT|M_ZERO); if (cfg == NULL) { printf("failed for %s\n", key); return (ENOMEM); } cfg->ndis_cfg.nc_cfgkey = strdup(key, M_DEVBUF); if (desc == NULL) { snprintf(descstr, sizeof(descstr), "%s (dynamic)", key); cfg->ndis_cfg.nc_cfgdesc = strdup(descstr, M_DEVBUF); } else cfg->ndis_cfg.nc_cfgdesc = strdup(desc, M_DEVBUF); strcpy(cfg->ndis_cfg.nc_val, val); TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link); if (flag_rdonly != 0) { cfg->ndis_oid = SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)), OID_AUTO, cfg->ndis_cfg.nc_cfgkey, CTLFLAG_RD, cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val), cfg->ndis_cfg.nc_cfgdesc); } else { cfg->ndis_oid = SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)), OID_AUTO, cfg->ndis_cfg.nc_cfgkey, CTLFLAG_RW, cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val), cfg->ndis_cfg.nc_cfgdesc); } return (0); } /* * Somewhere, somebody decided "hey, let's automatically create * a sysctl tree for each device instance as it's created -- it'll * make life so much easier!" Lies. Why must they turn the kernel * into a house of lies? */ int ndis_flush_sysctls(arg) void *arg; { struct ndis_softc *sc; struct ndis_cfglist *cfg; struct sysctl_ctx_list *clist; sc = arg; clist = device_get_sysctl_ctx(sc->ndis_dev); while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) { cfg = TAILQ_FIRST(&sc->ndis_cfglist_head); TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link); sysctl_ctx_entry_del(clist, cfg->ndis_oid); sysctl_remove_oid(cfg->ndis_oid, 1, 0); free(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF); free(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF); free(cfg, M_DEVBUF); } return (0); } void * ndis_get_routine_address(functbl, name) struct image_patch_table *functbl; char *name; { int i; for (i = 0; functbl[i].ipt_name != NULL; i++) if (strcmp(name, functbl[i].ipt_name) == 0) return (functbl[i].ipt_wrap); return (NULL); } static void ndis_return(dobj, arg) device_object *dobj; void *arg; { ndis_miniport_block *block; ndis_miniport_characteristics *ch; ndis_return_handler returnfunc; ndis_handle adapter; ndis_packet *p; uint8_t irql; list_entry *l; block = arg; ch = IoGetDriverObjectExtension(dobj->do_drvobj, (void *)1); p = arg; adapter = block->nmb_miniportadapterctx; if (adapter == NULL) return; returnfunc = ch->nmc_return_packet_func; KeAcquireSpinLock(&block->nmb_returnlock, &irql); while (!IsListEmpty(&block->nmb_returnlist)) { l = RemoveHeadList((&block->nmb_returnlist)); p = CONTAINING_RECORD(l, ndis_packet, np_list); InitializeListHead((&p->np_list)); KeReleaseSpinLock(&block->nmb_returnlock, irql); MSCALL2(returnfunc, adapter, p); KeAcquireSpinLock(&block->nmb_returnlock, &irql); } KeReleaseSpinLock(&block->nmb_returnlock, irql); } void ndis_return_packet(struct mbuf *m, void *buf, void *arg) { ndis_packet *p; ndis_miniport_block *block; if (arg == NULL) return; p = arg; /* Decrement refcount. */ p->np_refcnt--; /* Release packet when refcount hits zero, otherwise return. */ if (p->np_refcnt) return; block = ((struct ndis_softc *)p->np_softc)->ndis_block; KeAcquireSpinLockAtDpcLevel(&block->nmb_returnlock); InitializeListHead((&p->np_list)); InsertHeadList((&block->nmb_returnlist), (&p->np_list)); KeReleaseSpinLockFromDpcLevel(&block->nmb_returnlock); IoQueueWorkItem(block->nmb_returnitem, (io_workitem_func)kernndis_functbl[7].ipt_wrap, WORKQUEUE_CRITICAL, block); } void ndis_free_bufs(b0) ndis_buffer *b0; { ndis_buffer *next; if (b0 == NULL) return; while(b0 != NULL) { next = b0->mdl_next; IoFreeMdl(b0); b0 = next; } } void ndis_free_packet(p) ndis_packet *p; { if (p == NULL) return; ndis_free_bufs(p->np_private.npp_head); NdisFreePacket(p); } int ndis_convert_res(arg) void *arg; { struct ndis_softc *sc; ndis_resource_list *rl = NULL; cm_partial_resource_desc *prd = NULL; ndis_miniport_block *block; device_t dev; struct resource_list *brl; struct resource_list_entry *brle; int error = 0; sc = arg; block = sc->ndis_block; dev = sc->ndis_dev; rl = malloc(sizeof(ndis_resource_list) + (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)), M_DEVBUF, M_NOWAIT|M_ZERO); if (rl == NULL) return (ENOMEM); rl->cprl_version = 5; rl->cprl_revision = 1; rl->cprl_count = sc->ndis_rescnt; prd = rl->cprl_partial_descs; brl = BUS_GET_RESOURCE_LIST(dev, dev); if (brl != NULL) { STAILQ_FOREACH(brle, brl, link) { switch (brle->type) { case SYS_RES_IOPORT: prd->cprd_type = CmResourceTypePort; prd->cprd_flags = CM_RESOURCE_PORT_IO; prd->cprd_sharedisp = CmResourceShareDeviceExclusive; prd->u.cprd_port.cprd_start.np_quad = brle->start; prd->u.cprd_port.cprd_len = brle->count; break; case SYS_RES_MEMORY: prd->cprd_type = CmResourceTypeMemory; prd->cprd_flags = CM_RESOURCE_MEMORY_READ_WRITE; prd->cprd_sharedisp = CmResourceShareDeviceExclusive; prd->u.cprd_mem.cprd_start.np_quad = brle->start; prd->u.cprd_mem.cprd_len = brle->count; break; case SYS_RES_IRQ: prd->cprd_type = CmResourceTypeInterrupt; prd->cprd_flags = 0; /* * Always mark interrupt resources as * shared, since in our implementation, * they will be. */ prd->cprd_sharedisp = CmResourceShareShared; prd->u.cprd_intr.cprd_level = brle->start; prd->u.cprd_intr.cprd_vector = brle->start; prd->u.cprd_intr.cprd_affinity = 0; break; default: break; } prd++; } } block->nmb_rlist = rl; return (error); } /* * Map an NDIS packet to an mbuf list. When an NDIS driver receives a * packet, it will hand it to us in the form of an ndis_packet, * which we need to convert to an mbuf that is then handed off * to the stack. Note: we configure the mbuf list so that it uses * the memory regions specified by the ndis_buffer structures in * the ndis_packet as external storage. In most cases, this will * point to a memory region allocated by the driver (either by * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect * the driver to handle free()ing this region for is, so we set up * a dummy no-op free handler for it. */ int ndis_ptom(m0, p) struct mbuf **m0; ndis_packet *p; { struct mbuf *m = NULL, *prev = NULL; ndis_buffer *buf; ndis_packet_private *priv; uint32_t totlen = 0; struct ifnet *ifp; struct ether_header *eh; int diff; if (p == NULL || m0 == NULL) return (EINVAL); priv = &p->np_private; buf = priv->npp_head; p->np_refcnt = 0; for (buf = priv->npp_head; buf != NULL; buf = buf->mdl_next) { if (buf == priv->npp_head) m = m_gethdr(M_NOWAIT, MT_DATA); else m = m_get(M_NOWAIT, MT_DATA); if (m == NULL) { m_freem(*m0); *m0 = NULL; return (ENOBUFS); } m->m_len = MmGetMdlByteCount(buf); m->m_data = MmGetMdlVirtualAddress(buf); MEXTADD(m, m->m_data, m->m_len, ndis_return_packet, m->m_data, p, 0, EXT_NDIS); p->np_refcnt++; totlen += m->m_len; if (m->m_flags & M_PKTHDR) *m0 = m; else prev->m_next = m; prev = m; } /* * This is a hack to deal with the Marvell 8335 driver * which, when associated with an AP in WPA-PSK mode, * seems to overpad its frames by 8 bytes. I don't know * that the extra 8 bytes are for, and they're not there * in open mode, so for now clamp the frame size at 1514 * until I can figure out how to deal with this properly, * otherwise if_ethersubr() will spank us by discarding * the 'oversize' frames. */ eh = mtod((*m0), struct ether_header *); - ifp = ((struct ndis_softc *)p->np_softc)->ifp; - if (totlen > ETHER_MAX_FRAME(ifp, eh->ether_type, FALSE)) { + ifp = NDISUSB_GET_IFNET((struct ndis_softc *)p->np_softc); + if (ifp && totlen > ETHER_MAX_FRAME(ifp, eh->ether_type, FALSE)) { diff = totlen - ETHER_MAX_FRAME(ifp, eh->ether_type, FALSE); totlen -= diff; m->m_len -= diff; } (*m0)->m_pkthdr.len = totlen; return (0); } /* * Create an NDIS packet from an mbuf chain. * This is used mainly when transmitting packets, where we need * to turn an mbuf off an interface's send queue and transform it * into an NDIS packet which will be fed into the NDIS driver's * send routine. * * NDIS packets consist of two parts: an ndis_packet structure, * which is vaguely analogous to the pkthdr portion of an mbuf, * and one or more ndis_buffer structures, which define the * actual memory segments in which the packet data resides. * We need to allocate one ndis_buffer for each mbuf in a chain, * plus one ndis_packet as the header. */ int ndis_mtop(m0, p) struct mbuf *m0; ndis_packet **p; { struct mbuf *m; ndis_buffer *buf = NULL, *prev = NULL; ndis_packet_private *priv; if (p == NULL || *p == NULL || m0 == NULL) return (EINVAL); priv = &(*p)->np_private; priv->npp_totlen = m0->m_pkthdr.len; for (m = m0; m != NULL; m = m->m_next) { if (m->m_len == 0) continue; buf = IoAllocateMdl(m->m_data, m->m_len, FALSE, FALSE, NULL); if (buf == NULL) { ndis_free_packet(*p); *p = NULL; return (ENOMEM); } MmBuildMdlForNonPagedPool(buf); if (priv->npp_head == NULL) priv->npp_head = buf; else prev->mdl_next = buf; prev = buf; } priv->npp_tail = buf; return (0); } int ndis_get_supported_oids(arg, oids, oidcnt) void *arg; ndis_oid **oids; int *oidcnt; { int len, rval; ndis_oid *o; if (arg == NULL || oids == NULL || oidcnt == NULL) return (EINVAL); len = 0; ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len); o = malloc(len, M_DEVBUF, M_NOWAIT); if (o == NULL) return (ENOMEM); rval = ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, o, &len); if (rval) { free(o, M_DEVBUF); return (rval); } *oids = o; *oidcnt = len / 4; return (0); } int ndis_set_info(arg, oid, buf, buflen) void *arg; ndis_oid oid; void *buf; int *buflen; { struct ndis_softc *sc; ndis_status rval; ndis_handle adapter; ndis_setinfo_handler setfunc; uint32_t byteswritten = 0, bytesneeded = 0; uint8_t irql; uint64_t duetime; /* * According to the NDIS spec, MiniportQueryInformation() * and MiniportSetInformation() requests are handled serially: * once one request has been issued, we must wait for it to * finish before allowing another request to proceed. */ sc = arg; KeResetEvent(&sc->ndis_block->nmb_setevent); KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); if (sc->ndis_block->nmb_pendingreq != NULL) { KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); panic("ndis_set_info() called while other request pending"); } else sc->ndis_block->nmb_pendingreq = (ndis_request *)sc; setfunc = sc->ndis_chars->nmc_setinfo_func; adapter = sc->ndis_block->nmb_miniportadapterctx; if (adapter == NULL || setfunc == NULL || sc->ndis_block->nmb_devicectx == NULL) { sc->ndis_block->nmb_pendingreq = NULL; KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); return (ENXIO); } rval = MSCALL6(setfunc, adapter, oid, buf, *buflen, &byteswritten, &bytesneeded); sc->ndis_block->nmb_pendingreq = NULL; KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); if (rval == NDIS_STATUS_PENDING) { /* Wait up to 5 seconds. */ duetime = (5 * 1000000) * -10; KeWaitForSingleObject(&sc->ndis_block->nmb_setevent, 0, 0, FALSE, &duetime); rval = sc->ndis_block->nmb_setstat; } if (byteswritten) *buflen = byteswritten; if (bytesneeded) *buflen = bytesneeded; if (rval == NDIS_STATUS_INVALID_LENGTH) return (ENOSPC); if (rval == NDIS_STATUS_INVALID_OID) return (EINVAL); if (rval == NDIS_STATUS_NOT_SUPPORTED || rval == NDIS_STATUS_NOT_ACCEPTED) return (ENOTSUP); if (rval != NDIS_STATUS_SUCCESS) return (ENODEV); return (0); } typedef void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status); int ndis_send_packets(arg, packets, cnt) void *arg; ndis_packet **packets; int cnt; { struct ndis_softc *sc; ndis_handle adapter; ndis_sendmulti_handler sendfunc; ndis_senddone_func senddonefunc; int i; ndis_packet *p; uint8_t irql = 0; sc = arg; adapter = sc->ndis_block->nmb_miniportadapterctx; if (adapter == NULL) return (ENXIO); sendfunc = sc->ndis_chars->nmc_sendmulti_func; senddonefunc = sc->ndis_block->nmb_senddone_func; if (NDIS_SERIALIZED(sc->ndis_block)) KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); MSCALL3(sendfunc, adapter, packets, cnt); for (i = 0; i < cnt; i++) { p = packets[i]; /* * Either the driver already handed the packet to * ndis_txeof() due to a failure, or it wants to keep * it and release it asynchronously later. Skip to the * next one. */ if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING) continue; MSCALL3(senddonefunc, sc->ndis_block, p, p->np_oob.npo_status); } if (NDIS_SERIALIZED(sc->ndis_block)) KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); return (0); } int ndis_send_packet(arg, packet) void *arg; ndis_packet *packet; { struct ndis_softc *sc; ndis_handle adapter; ndis_status status; ndis_sendsingle_handler sendfunc; ndis_senddone_func senddonefunc; uint8_t irql = 0; sc = arg; adapter = sc->ndis_block->nmb_miniportadapterctx; if (adapter == NULL) return (ENXIO); sendfunc = sc->ndis_chars->nmc_sendsingle_func; senddonefunc = sc->ndis_block->nmb_senddone_func; if (NDIS_SERIALIZED(sc->ndis_block)) KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); status = MSCALL3(sendfunc, adapter, packet, packet->np_private.npp_flags); if (status == NDIS_STATUS_PENDING) { if (NDIS_SERIALIZED(sc->ndis_block)) KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); return (0); } MSCALL3(senddonefunc, sc->ndis_block, packet, status); if (NDIS_SERIALIZED(sc->ndis_block)) KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); return (0); } int ndis_init_dma(arg) void *arg; { struct ndis_softc *sc; int i, error; sc = arg; sc->ndis_tmaps = malloc(sizeof(bus_dmamap_t) * sc->ndis_maxpkts, M_DEVBUF, M_NOWAIT|M_ZERO); if (sc->ndis_tmaps == NULL) return (ENOMEM); for (i = 0; i < sc->ndis_maxpkts; i++) { error = bus_dmamap_create(sc->ndis_ttag, 0, &sc->ndis_tmaps[i]); if (error) { free(sc->ndis_tmaps, M_DEVBUF); return (ENODEV); } } return (0); } int ndis_destroy_dma(arg) void *arg; { struct ndis_softc *sc; struct mbuf *m; ndis_packet *p = NULL; int i; sc = arg; for (i = 0; i < sc->ndis_maxpkts; i++) { if (sc->ndis_txarray[i] != NULL) { p = sc->ndis_txarray[i]; m = (struct mbuf *)p->np_rsvd[1]; if (m != NULL) m_freem(m); ndis_free_packet(sc->ndis_txarray[i]); } bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]); } free(sc->ndis_tmaps, M_DEVBUF); bus_dma_tag_destroy(sc->ndis_ttag); return (0); } int ndis_reset_nic(arg) void *arg; { struct ndis_softc *sc; ndis_handle adapter; ndis_reset_handler resetfunc; uint8_t addressing_reset; int rval; uint8_t irql = 0; sc = arg; NDIS_LOCK(sc); adapter = sc->ndis_block->nmb_miniportadapterctx; resetfunc = sc->ndis_chars->nmc_reset_func; if (adapter == NULL || resetfunc == NULL || sc->ndis_block->nmb_devicectx == NULL) { NDIS_UNLOCK(sc); return (EIO); } NDIS_UNLOCK(sc); KeResetEvent(&sc->ndis_block->nmb_resetevent); if (NDIS_SERIALIZED(sc->ndis_block)) KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); rval = MSCALL2(resetfunc, &addressing_reset, adapter); if (NDIS_SERIALIZED(sc->ndis_block)) KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); if (rval == NDIS_STATUS_PENDING) KeWaitForSingleObject(&sc->ndis_block->nmb_resetevent, 0, 0, FALSE, NULL); return (0); } int ndis_halt_nic(arg) void *arg; { struct ndis_softc *sc; ndis_handle adapter; ndis_halt_handler haltfunc; ndis_miniport_block *block; int empty = 0; uint8_t irql; sc = arg; block = sc->ndis_block; if (!cold) KeFlushQueuedDpcs(); /* * Wait for all packets to be returned. */ while (1) { KeAcquireSpinLock(&block->nmb_returnlock, &irql); empty = IsListEmpty(&block->nmb_returnlist); KeReleaseSpinLock(&block->nmb_returnlock, irql); if (empty) break; NdisMSleep(1000); } NDIS_LOCK(sc); adapter = sc->ndis_block->nmb_miniportadapterctx; if (adapter == NULL) { NDIS_UNLOCK(sc); return (EIO); } sc->ndis_block->nmb_devicectx = NULL; /* * The adapter context is only valid after the init * handler has been called, and is invalid once the * halt handler has been called. */ haltfunc = sc->ndis_chars->nmc_halt_func; NDIS_UNLOCK(sc); MSCALL1(haltfunc, adapter); NDIS_LOCK(sc); sc->ndis_block->nmb_miniportadapterctx = NULL; NDIS_UNLOCK(sc); return (0); } int ndis_shutdown_nic(arg) void *arg; { struct ndis_softc *sc; ndis_handle adapter; ndis_shutdown_handler shutdownfunc; sc = arg; NDIS_LOCK(sc); adapter = sc->ndis_block->nmb_miniportadapterctx; shutdownfunc = sc->ndis_chars->nmc_shutdown_handler; NDIS_UNLOCK(sc); if (adapter == NULL || shutdownfunc == NULL) return (EIO); if (sc->ndis_chars->nmc_rsvd0 == NULL) MSCALL1(shutdownfunc, adapter); else MSCALL1(shutdownfunc, sc->ndis_chars->nmc_rsvd0); TAILQ_REMOVE(&ndis_devhead, sc->ndis_block, link); return (0); } int ndis_pnpevent_nic(arg, type) void *arg; int type; { device_t dev; struct ndis_softc *sc; ndis_handle adapter; ndis_pnpevent_handler pnpeventfunc; dev = arg; sc = device_get_softc(arg); NDIS_LOCK(sc); adapter = sc->ndis_block->nmb_miniportadapterctx; pnpeventfunc = sc->ndis_chars->nmc_pnpevent_handler; NDIS_UNLOCK(sc); if (adapter == NULL || pnpeventfunc == NULL) return (EIO); if (sc->ndis_chars->nmc_rsvd0 == NULL) MSCALL4(pnpeventfunc, adapter, type, NULL, 0); else MSCALL4(pnpeventfunc, sc->ndis_chars->nmc_rsvd0, type, NULL, 0); return (0); } int ndis_init_nic(arg) void *arg; { struct ndis_softc *sc; ndis_miniport_block *block; ndis_init_handler initfunc; ndis_status status, openstatus = 0; ndis_medium mediumarray[NdisMediumMax]; uint32_t chosenmedium, i; if (arg == NULL) return (EINVAL); sc = arg; NDIS_LOCK(sc); block = sc->ndis_block; initfunc = sc->ndis_chars->nmc_init_func; NDIS_UNLOCK(sc); sc->ndis_block->nmb_timerlist = NULL; for (i = 0; i < NdisMediumMax; i++) mediumarray[i] = i; status = MSCALL6(initfunc, &openstatus, &chosenmedium, mediumarray, NdisMediumMax, block, block); /* * If the init fails, blow away the other exported routines * we obtained from the driver so we can't call them later. * If the init failed, none of these will work. */ if (status != NDIS_STATUS_SUCCESS) { NDIS_LOCK(sc); sc->ndis_block->nmb_miniportadapterctx = NULL; NDIS_UNLOCK(sc); return (ENXIO); } /* * This may look really goofy, but apparently it is possible * to halt a miniport too soon after it's been initialized. * After MiniportInitialize() finishes, pause for 1 second * to give the chip a chance to handle any short-lived timers * that were set in motion. If we call MiniportHalt() too soon, * some of the timers may not be cancelled, because the driver * expects them to fire before the halt is called. */ pause("ndwait", hz); NDIS_LOCK(sc); sc->ndis_block->nmb_devicectx = sc; NDIS_UNLOCK(sc); return (0); } static void ndis_intrsetup(dpc, dobj, ip, sc) kdpc *dpc; device_object *dobj; irp *ip; struct ndis_softc *sc; { ndis_miniport_interrupt *intr; intr = sc->ndis_block->nmb_interrupt; /* Sanity check. */ if (intr == NULL) return; KeAcquireSpinLockAtDpcLevel(&intr->ni_dpccountlock); KeResetEvent(&intr->ni_dpcevt); if (KeInsertQueueDpc(&intr->ni_dpc, NULL, NULL) == TRUE) intr->ni_dpccnt++; KeReleaseSpinLockFromDpcLevel(&intr->ni_dpccountlock); } int ndis_get_info(arg, oid, buf, buflen) void *arg; ndis_oid oid; void *buf; int *buflen; { struct ndis_softc *sc; ndis_status rval; ndis_handle adapter; ndis_queryinfo_handler queryfunc; uint32_t byteswritten = 0, bytesneeded = 0; uint8_t irql; uint64_t duetime; sc = arg; KeResetEvent(&sc->ndis_block->nmb_getevent); KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); if (sc->ndis_block->nmb_pendingreq != NULL) { KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); panic("ndis_get_info() called while other request pending"); } else sc->ndis_block->nmb_pendingreq = (ndis_request *)sc; queryfunc = sc->ndis_chars->nmc_queryinfo_func; adapter = sc->ndis_block->nmb_miniportadapterctx; if (adapter == NULL || queryfunc == NULL || sc->ndis_block->nmb_devicectx == NULL) { sc->ndis_block->nmb_pendingreq = NULL; KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); return (ENXIO); } rval = MSCALL6(queryfunc, adapter, oid, buf, *buflen, &byteswritten, &bytesneeded); sc->ndis_block->nmb_pendingreq = NULL; KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); /* Wait for requests that block. */ if (rval == NDIS_STATUS_PENDING) { /* Wait up to 5 seconds. */ duetime = (5 * 1000000) * -10; KeWaitForSingleObject(&sc->ndis_block->nmb_getevent, 0, 0, FALSE, &duetime); rval = sc->ndis_block->nmb_getstat; } if (byteswritten) *buflen = byteswritten; if (bytesneeded) *buflen = bytesneeded; if (rval == NDIS_STATUS_INVALID_LENGTH || rval == NDIS_STATUS_BUFFER_TOO_SHORT) return (ENOSPC); if (rval == NDIS_STATUS_INVALID_OID) return (EINVAL); if (rval == NDIS_STATUS_NOT_SUPPORTED || rval == NDIS_STATUS_NOT_ACCEPTED) return (ENOTSUP); if (rval != NDIS_STATUS_SUCCESS) return (ENODEV); return (0); } uint32_t NdisAddDevice(drv, pdo) driver_object *drv; device_object *pdo; { device_object *fdo; ndis_miniport_block *block; struct ndis_softc *sc; uint32_t status; int error; sc = device_get_softc(pdo->do_devext); if (sc->ndis_iftype == PCMCIABus || sc->ndis_iftype == PCIBus) { error = bus_setup_intr(sc->ndis_dev, sc->ndis_irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, ntoskrnl_intr, NULL, &sc->ndis_intrhand); if (error) return (NDIS_STATUS_FAILURE); } status = IoCreateDevice(drv, sizeof(ndis_miniport_block), NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &fdo); if (status != STATUS_SUCCESS) return (status); block = fdo->do_devext; block->nmb_filterdbs.nf_ethdb = block; block->nmb_deviceobj = fdo; block->nmb_physdeviceobj = pdo; block->nmb_nextdeviceobj = IoAttachDeviceToDeviceStack(fdo, pdo); KeInitializeSpinLock(&block->nmb_lock); KeInitializeSpinLock(&block->nmb_returnlock); KeInitializeEvent(&block->nmb_getevent, EVENT_TYPE_NOTIFY, TRUE); KeInitializeEvent(&block->nmb_setevent, EVENT_TYPE_NOTIFY, TRUE); KeInitializeEvent(&block->nmb_resetevent, EVENT_TYPE_NOTIFY, TRUE); InitializeListHead(&block->nmb_parmlist); InitializeListHead(&block->nmb_returnlist); block->nmb_returnitem = IoAllocateWorkItem(fdo); /* * Stash pointers to the miniport block and miniport * characteristics info in the if_ndis softc so the * UNIX wrapper driver can get to them later. */ sc->ndis_block = block; sc->ndis_chars = IoGetDriverObjectExtension(drv, (void *)1); /* * If the driver has a MiniportTransferData() function, * we should allocate a private RX packet pool. */ if (sc->ndis_chars->nmc_transferdata_func != NULL) { NdisAllocatePacketPool(&status, &block->nmb_rxpool, 32, PROTOCOL_RESERVED_SIZE_IN_PACKET); if (status != NDIS_STATUS_SUCCESS) { IoDetachDevice(block->nmb_nextdeviceobj); IoDeleteDevice(fdo); return (status); } InitializeListHead((&block->nmb_packetlist)); } /* Give interrupt handling priority over timers. */ IoInitializeDpcRequest(fdo, kernndis_functbl[6].ipt_wrap); KeSetImportanceDpc(&fdo->do_dpc, KDPC_IMPORTANCE_HIGH); /* Finish up BSD-specific setup. */ block->nmb_signature = (void *)0xcafebabe; block->nmb_status_func = kernndis_functbl[0].ipt_wrap; block->nmb_statusdone_func = kernndis_functbl[1].ipt_wrap; block->nmb_setdone_func = kernndis_functbl[2].ipt_wrap; block->nmb_querydone_func = kernndis_functbl[3].ipt_wrap; block->nmb_resetdone_func = kernndis_functbl[4].ipt_wrap; block->nmb_sendrsrc_func = kernndis_functbl[5].ipt_wrap; block->nmb_pendingreq = NULL; TAILQ_INSERT_TAIL(&ndis_devhead, block, link); return (STATUS_SUCCESS); } int ndis_unload_driver(arg) void *arg; { struct ndis_softc *sc; device_object *fdo; sc = arg; if (sc->ndis_intrhand) bus_teardown_intr(sc->ndis_dev, sc->ndis_irq, sc->ndis_intrhand); if (sc->ndis_block->nmb_rlist != NULL) free(sc->ndis_block->nmb_rlist, M_DEVBUF); ndis_flush_sysctls(sc); TAILQ_REMOVE(&ndis_devhead, sc->ndis_block, link); if (sc->ndis_chars->nmc_transferdata_func != NULL) NdisFreePacketPool(sc->ndis_block->nmb_rxpool); fdo = sc->ndis_block->nmb_deviceobj; IoFreeWorkItem(sc->ndis_block->nmb_returnitem); IoDetachDevice(sc->ndis_block->nmb_nextdeviceobj); IoDeleteDevice(fdo); return (0); } Index: stable/11/sys/compat/ndis/subr_ndis.c =================================================================== --- stable/11/sys/compat/ndis/subr_ndis.c (revision 319725) +++ stable/11/sys/compat/ndis/subr_ndis.c (revision 319726) @@ -1,3369 +1,3374 @@ /*- * 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. */ #include __FBSDID("$FreeBSD$"); /* * This file implements a translation layer between the BSD networking * infrasturcture and Windows(R) NDIS network driver modules. A Windows * NDIS driver calls into several functions in the NDIS.SYS Windows * kernel module and exports a table of functions designed to be called * by the NDIS subsystem. Using the PE loader, we can patch our own * versions of the NDIS routines into a given Windows driver module and * convince the driver that it is in fact running on Windows. * * We provide a table of all our implemented NDIS routines which is patched * into the driver object code. All our exported routines must use the * _stdcall calling convention, since that's what the Windows object code * expects. */ #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 #include #include #include #include #include #include #include #include #include #include #include #include static char ndis_filepath[MAXPATHLEN]; SYSCTL_STRING(_hw, OID_AUTO, ndis_filepath, CTLFLAG_RW, ndis_filepath, MAXPATHLEN, "Path used by NdisOpenFile() to search for files"); static void NdisInitializeWrapper(ndis_handle *, driver_object *, void *, void *); static ndis_status NdisMRegisterMiniport(ndis_handle, ndis_miniport_characteristics *, int); static ndis_status NdisAllocateMemoryWithTag(void **, uint32_t, uint32_t); static ndis_status NdisAllocateMemory(void **, uint32_t, uint32_t, ndis_physaddr); static void NdisFreeMemory(void *, uint32_t, uint32_t); static ndis_status NdisMSetAttributesEx(ndis_handle, ndis_handle, uint32_t, uint32_t, ndis_interface_type); static void NdisOpenConfiguration(ndis_status *, ndis_handle *, ndis_handle); static void NdisOpenConfigurationKeyByIndex(ndis_status *, ndis_handle, uint32_t, unicode_string *, ndis_handle *); static void NdisOpenConfigurationKeyByName(ndis_status *, ndis_handle, unicode_string *, ndis_handle *); static ndis_status ndis_encode_parm(ndis_miniport_block *, struct sysctl_oid *, ndis_parm_type, ndis_config_parm **); static ndis_status ndis_decode_parm(ndis_miniport_block *, ndis_config_parm *, char *); static void NdisReadConfiguration(ndis_status *, ndis_config_parm **, ndis_handle, unicode_string *, ndis_parm_type); static void NdisWriteConfiguration(ndis_status *, ndis_handle, unicode_string *, ndis_config_parm *); static void NdisCloseConfiguration(ndis_handle); static void NdisAllocateSpinLock(ndis_spin_lock *); static void NdisFreeSpinLock(ndis_spin_lock *); static void NdisAcquireSpinLock(ndis_spin_lock *); static void NdisReleaseSpinLock(ndis_spin_lock *); static void NdisDprAcquireSpinLock(ndis_spin_lock *); static void NdisDprReleaseSpinLock(ndis_spin_lock *); static void NdisInitializeReadWriteLock(ndis_rw_lock *); static void NdisAcquireReadWriteLock(ndis_rw_lock *, uint8_t, ndis_lock_state *); static void NdisReleaseReadWriteLock(ndis_rw_lock *, ndis_lock_state *); static uint32_t NdisReadPciSlotInformation(ndis_handle, uint32_t, uint32_t, void *, uint32_t); static uint32_t NdisWritePciSlotInformation(ndis_handle, uint32_t, uint32_t, void *, uint32_t); static void NdisWriteErrorLogEntry(ndis_handle, ndis_error_code, uint32_t, ...); static void ndis_map_cb(void *, bus_dma_segment_t *, int, int); static void NdisMStartBufferPhysicalMapping(ndis_handle, ndis_buffer *, uint32_t, uint8_t, ndis_paddr_unit *, uint32_t *); static void NdisMCompleteBufferPhysicalMapping(ndis_handle, ndis_buffer *, uint32_t); static void NdisMInitializeTimer(ndis_miniport_timer *, ndis_handle, ndis_timer_function, void *); static void NdisInitializeTimer(ndis_timer *, ndis_timer_function, void *); static void NdisSetTimer(ndis_timer *, uint32_t); static void NdisMSetPeriodicTimer(ndis_miniport_timer *, uint32_t); static void NdisMCancelTimer(ndis_timer *, uint8_t *); static void ndis_timercall(kdpc *, ndis_miniport_timer *, void *, void *); static void NdisMQueryAdapterResources(ndis_status *, ndis_handle, ndis_resource_list *, uint32_t *); static ndis_status NdisMRegisterIoPortRange(void **, ndis_handle, uint32_t, uint32_t); static void NdisMDeregisterIoPortRange(ndis_handle, uint32_t, uint32_t, void *); static void NdisReadNetworkAddress(ndis_status *, void **, uint32_t *, ndis_handle); static ndis_status NdisQueryMapRegisterCount(uint32_t, uint32_t *); static ndis_status NdisMAllocateMapRegisters(ndis_handle, uint32_t, uint8_t, uint32_t, uint32_t); static void NdisMFreeMapRegisters(ndis_handle); static void ndis_mapshared_cb(void *, bus_dma_segment_t *, int, int); static void NdisMAllocateSharedMemory(ndis_handle, uint32_t, uint8_t, void **, ndis_physaddr *); static void ndis_asyncmem_complete(device_object *, void *); static ndis_status NdisMAllocateSharedMemoryAsync(ndis_handle, uint32_t, uint8_t, void *); static void NdisMFreeSharedMemory(ndis_handle, uint32_t, uint8_t, void *, ndis_physaddr); static ndis_status NdisMMapIoSpace(void **, ndis_handle, ndis_physaddr, uint32_t); static void NdisMUnmapIoSpace(ndis_handle, void *, uint32_t); static uint32_t NdisGetCacheFillSize(void); static void *NdisGetRoutineAddress(unicode_string *); static uint32_t NdisMGetDmaAlignment(ndis_handle); static ndis_status NdisMInitializeScatterGatherDma(ndis_handle, uint8_t, uint32_t); static void NdisUnchainBufferAtFront(ndis_packet *, ndis_buffer **); static void NdisUnchainBufferAtBack(ndis_packet *, ndis_buffer **); static void NdisAllocateBufferPool(ndis_status *, ndis_handle *, uint32_t); static void NdisFreeBufferPool(ndis_handle); static void NdisAllocateBuffer(ndis_status *, ndis_buffer **, ndis_handle, void *, uint32_t); static void NdisFreeBuffer(ndis_buffer *); static uint32_t NdisBufferLength(ndis_buffer *); static void NdisQueryBuffer(ndis_buffer *, void **, uint32_t *); static void NdisQueryBufferSafe(ndis_buffer *, void **, uint32_t *, uint32_t); static void *NdisBufferVirtualAddress(ndis_buffer *); static void *NdisBufferVirtualAddressSafe(ndis_buffer *, uint32_t); static void NdisAdjustBufferLength(ndis_buffer *, int); static uint32_t NdisInterlockedIncrement(uint32_t *); static uint32_t NdisInterlockedDecrement(uint32_t *); static void NdisInitializeEvent(ndis_event *); static void NdisSetEvent(ndis_event *); static void NdisResetEvent(ndis_event *); static uint8_t NdisWaitEvent(ndis_event *, uint32_t); static ndis_status NdisUnicodeStringToAnsiString(ansi_string *, unicode_string *); static ndis_status NdisAnsiStringToUnicodeString(unicode_string *, ansi_string *); static ndis_status NdisMPciAssignResources(ndis_handle, uint32_t, ndis_resource_list **); static ndis_status NdisMRegisterInterrupt(ndis_miniport_interrupt *, ndis_handle, uint32_t, uint32_t, uint8_t, uint8_t, ndis_interrupt_mode); static void NdisMDeregisterInterrupt(ndis_miniport_interrupt *); static void NdisMRegisterAdapterShutdownHandler(ndis_handle, void *, ndis_shutdown_handler); static void NdisMDeregisterAdapterShutdownHandler(ndis_handle); static uint32_t NDIS_BUFFER_TO_SPAN_PAGES(ndis_buffer *); static void NdisGetBufferPhysicalArraySize(ndis_buffer *, uint32_t *); static void NdisQueryBufferOffset(ndis_buffer *, uint32_t *, uint32_t *); static uint32_t NdisReadPcmciaAttributeMemory(ndis_handle, uint32_t, void *, uint32_t); static uint32_t NdisWritePcmciaAttributeMemory(ndis_handle, uint32_t, void *, uint32_t); static list_entry *NdisInterlockedInsertHeadList(list_entry *, list_entry *, ndis_spin_lock *); static list_entry *NdisInterlockedRemoveHeadList(list_entry *, ndis_spin_lock *); static list_entry *NdisInterlockedInsertTailList(list_entry *, list_entry *, ndis_spin_lock *); static uint8_t NdisMSynchronizeWithInterrupt(ndis_miniport_interrupt *, void *, void *); static void NdisGetCurrentSystemTime(uint64_t *); static void NdisGetSystemUpTime(uint32_t *); static uint32_t NdisGetVersion(void); static void NdisInitializeString(unicode_string *, char *); static void NdisInitAnsiString(ansi_string *, char *); static void NdisInitUnicodeString(unicode_string *, uint16_t *); static void NdisFreeString(unicode_string *); static ndis_status NdisMRemoveMiniport(ndis_handle *); static void NdisTerminateWrapper(ndis_handle, void *); static void NdisMGetDeviceProperty(ndis_handle, device_object **, device_object **, device_object **, cm_resource_list *, cm_resource_list *); static void NdisGetFirstBufferFromPacket(ndis_packet *, ndis_buffer **, void **, uint32_t *, uint32_t *); static void NdisGetFirstBufferFromPacketSafe(ndis_packet *, ndis_buffer **, void **, uint32_t *, uint32_t *, uint32_t); static int ndis_find_sym(linker_file_t, char *, char *, caddr_t *); static void NdisOpenFile(ndis_status *, ndis_handle *, uint32_t *, unicode_string *, ndis_physaddr); static void NdisMapFile(ndis_status *, void **, ndis_handle); static void NdisUnmapFile(ndis_handle); static void NdisCloseFile(ndis_handle); static uint8_t NdisSystemProcessorCount(void); static void NdisGetCurrentProcessorCounts(uint32_t *, uint32_t *, uint32_t *); static void NdisMIndicateStatusComplete(ndis_handle); static void NdisMIndicateStatus(ndis_handle, ndis_status, void *, uint32_t); static uint8_t ndis_intr(kinterrupt *, void *); static void ndis_intrhand(kdpc *, ndis_miniport_interrupt *, void *, void *); static funcptr ndis_findwrap(funcptr); static void NdisCopyFromPacketToPacket(ndis_packet *, uint32_t, uint32_t, ndis_packet *, uint32_t, uint32_t *); static void NdisCopyFromPacketToPacketSafe(ndis_packet *, uint32_t, uint32_t, ndis_packet *, uint32_t, uint32_t *, uint32_t); static void NdisIMCopySendPerPacketInfo(ndis_packet *, ndis_packet *); static ndis_status NdisMRegisterDevice(ndis_handle, unicode_string *, unicode_string *, driver_dispatch **, void **, ndis_handle *); static ndis_status NdisMDeregisterDevice(ndis_handle); static ndis_status NdisMQueryAdapterInstanceName(unicode_string *, ndis_handle); static void NdisMRegisterUnloadHandler(ndis_handle, void *); static void dummy(void); /* * Some really old drivers do not properly check the return value * from NdisAllocatePacket() and NdisAllocateBuffer() and will * sometimes allocate few more buffers/packets that they originally * requested when they created the pool. To prevent this from being * a problem, we allocate a few extra buffers/packets beyond what * the driver asks for. This #define controls how many. */ #define NDIS_POOL_EXTRA 16 int ndis_libinit() { image_patch_table *patch; strcpy(ndis_filepath, "/compat/ndis"); patch = ndis_functbl; while (patch->ipt_func != NULL) { windrv_wrap((funcptr)patch->ipt_func, (funcptr *)&patch->ipt_wrap, patch->ipt_argcnt, patch->ipt_ftype); patch++; } return (0); } int ndis_libfini() { image_patch_table *patch; patch = ndis_functbl; while (patch->ipt_func != NULL) { windrv_unwrap(patch->ipt_wrap); patch++; } return (0); } static funcptr ndis_findwrap(func) funcptr func; { image_patch_table *patch; patch = ndis_functbl; while (patch->ipt_func != NULL) { if ((funcptr)patch->ipt_func == func) return ((funcptr)patch->ipt_wrap); patch++; } return (NULL); } /* * This routine does the messy Windows Driver Model device attachment * stuff on behalf of NDIS drivers. We register our own AddDevice * routine here */ static void NdisInitializeWrapper(wrapper, drv, path, unused) ndis_handle *wrapper; driver_object *drv; void *path; void *unused; { /* * As of yet, I haven't come up with a compelling * reason to define a private NDIS wrapper structure, * so we use a pointer to the driver object as the * wrapper handle. The driver object has the miniport * characteristics struct for this driver hung off it * via IoAllocateDriverObjectExtension(), and that's * really all the private data we need. */ *wrapper = drv; /* * If this was really Windows, we'd be registering dispatch * routines for the NDIS miniport module here, but we're * not Windows so all we really need to do is set up an * AddDevice function that'll be invoked when a new device * instance appears. */ drv->dro_driverext->dre_adddevicefunc = NdisAddDevice; } static void NdisTerminateWrapper(handle, syspec) ndis_handle handle; void *syspec; { /* Nothing to see here, move along. */ } static ndis_status NdisMRegisterMiniport(handle, characteristics, len) ndis_handle handle; ndis_miniport_characteristics *characteristics; int len; { ndis_miniport_characteristics *ch = NULL; driver_object *drv; drv = (driver_object *)handle; /* * We need to save the NDIS miniport characteristics * somewhere. This data is per-driver, not per-device * (all devices handled by the same driver have the * same characteristics) so we hook it onto the driver * object using IoAllocateDriverObjectExtension(). * The extra extension info is automagically deleted when * the driver is unloaded (see windrv_unload()). */ if (IoAllocateDriverObjectExtension(drv, (void *)1, sizeof(ndis_miniport_characteristics), (void **)&ch) != STATUS_SUCCESS) { return (NDIS_STATUS_RESOURCES); } bzero((char *)ch, sizeof(ndis_miniport_characteristics)); bcopy((char *)characteristics, (char *)ch, len); if (ch->nmc_version_major < 5 || ch->nmc_version_minor < 1) { ch->nmc_shutdown_handler = NULL; ch->nmc_canceltxpkts_handler = NULL; ch->nmc_pnpevent_handler = NULL; } return (NDIS_STATUS_SUCCESS); } static ndis_status NdisAllocateMemoryWithTag(vaddr, len, tag) void **vaddr; uint32_t len; uint32_t tag; { void *mem; mem = ExAllocatePoolWithTag(NonPagedPool, len, tag); if (mem == NULL) { return (NDIS_STATUS_RESOURCES); } *vaddr = mem; return (NDIS_STATUS_SUCCESS); } static ndis_status NdisAllocateMemory(vaddr, len, flags, highaddr) void **vaddr; uint32_t len; uint32_t flags; ndis_physaddr highaddr; { void *mem; mem = ExAllocatePoolWithTag(NonPagedPool, len, 0); if (mem == NULL) return (NDIS_STATUS_RESOURCES); *vaddr = mem; return (NDIS_STATUS_SUCCESS); } static void NdisFreeMemory(vaddr, len, flags) void *vaddr; uint32_t len; uint32_t flags; { if (len == 0) return; ExFreePool(vaddr); } static ndis_status NdisMSetAttributesEx(adapter_handle, adapter_ctx, hangsecs, flags, iftype) ndis_handle adapter_handle; ndis_handle adapter_ctx; uint32_t hangsecs; uint32_t flags; ndis_interface_type iftype; { ndis_miniport_block *block; /* * Save the adapter context, we need it for calling * the driver's internal functions. */ block = (ndis_miniport_block *)adapter_handle; block->nmb_miniportadapterctx = adapter_ctx; block->nmb_checkforhangsecs = hangsecs; block->nmb_flags = flags; return (NDIS_STATUS_SUCCESS); } static void NdisOpenConfiguration(status, cfg, wrapctx) ndis_status *status; ndis_handle *cfg; ndis_handle wrapctx; { *cfg = wrapctx; *status = NDIS_STATUS_SUCCESS; } static void NdisOpenConfigurationKeyByName(status, cfg, subkey, subhandle) ndis_status *status; ndis_handle cfg; unicode_string *subkey; ndis_handle *subhandle; { *subhandle = cfg; *status = NDIS_STATUS_SUCCESS; } static void NdisOpenConfigurationKeyByIndex(status, cfg, idx, subkey, subhandle) ndis_status *status; ndis_handle cfg; uint32_t idx; unicode_string *subkey; ndis_handle *subhandle; { *status = NDIS_STATUS_FAILURE; } static ndis_status ndis_encode_parm(block, oid, type, parm) ndis_miniport_block *block; struct sysctl_oid *oid; ndis_parm_type type; ndis_config_parm **parm; { ndis_config_parm *p; ndis_parmlist_entry *np; unicode_string *us; ansi_string as; int base = 0; uint32_t val; char tmp[32]; np = ExAllocatePoolWithTag(NonPagedPool, sizeof(ndis_parmlist_entry), 0); if (np == NULL) return (NDIS_STATUS_RESOURCES); InsertHeadList((&block->nmb_parmlist), (&np->np_list)); *parm = p = &np->np_parm; switch(type) { case ndis_parm_string: /* See if this might be a number. */ val = strtoul((char *)oid->oid_arg1, NULL, 10); us = &p->ncp_parmdata.ncp_stringdata; p->ncp_type = ndis_parm_string; if (val) { snprintf(tmp, 32, "%x", val); RtlInitAnsiString(&as, tmp); } else { RtlInitAnsiString(&as, (char *)oid->oid_arg1); } if (RtlAnsiStringToUnicodeString(us, &as, TRUE)) { ExFreePool(np); return (NDIS_STATUS_RESOURCES); } break; case ndis_parm_int: if (strncmp((char *)oid->oid_arg1, "0x", 2) == 0) base = 16; else base = 10; p->ncp_type = ndis_parm_int; p->ncp_parmdata.ncp_intdata = strtol((char *)oid->oid_arg1, NULL, base); break; case ndis_parm_hexint: #ifdef notdef if (strncmp((char *)oid->oid_arg1, "0x", 2) == 0) base = 16; else base = 10; #endif base = 16; p->ncp_type = ndis_parm_hexint; p->ncp_parmdata.ncp_intdata = strtoul((char *)oid->oid_arg1, NULL, base); break; default: return (NDIS_STATUS_FAILURE); break; } return (NDIS_STATUS_SUCCESS); } static void NdisReadConfiguration(status, parm, cfg, key, type) ndis_status *status; ndis_config_parm **parm; ndis_handle cfg; unicode_string *key; ndis_parm_type type; { char *keystr = NULL; ndis_miniport_block *block; struct ndis_softc *sc; struct sysctl_oid *oidp; struct sysctl_ctx_entry *e; ansi_string as; block = (ndis_miniport_block *)cfg; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + /* + device_printf(sc->ndis_dev, "NdisReadConfiguration sc=%p\n", sc); + */ if (key->us_len == 0 || key->us_buf == NULL) { *status = NDIS_STATUS_FAILURE; return; } if (RtlUnicodeStringToAnsiString(&as, key, TRUE)) { *status = NDIS_STATUS_RESOURCES; return; } keystr = as.as_buf; /* * See if registry key is already in a list of known keys * included with the driver. */ TAILQ_FOREACH(e, device_get_sysctl_ctx(sc->ndis_dev), link) { oidp = e->entry; if (strcasecmp(oidp->oid_name, keystr) == 0) { if (strcmp((char *)oidp->oid_arg1, "UNSET") == 0) { RtlFreeAnsiString(&as); *status = NDIS_STATUS_FAILURE; return; } *status = ndis_encode_parm(block, oidp, type, parm); RtlFreeAnsiString(&as); return; } } /* * If the key didn't match, add it to the list of dynamically * created ones. Sometimes, drivers refer to registry keys * that aren't documented in their .INF files. These keys * are supposed to be created by some sort of utility or * control panel snap-in that comes with the driver software. * Sometimes it's useful to be able to manipulate these. * If the driver requests the key in the form of a string, * make its default value an empty string, otherwise default * it to "0". */ if (type == ndis_parm_int || type == ndis_parm_hexint) ndis_add_sysctl(sc, keystr, "(dynamic integer key)", "UNSET", CTLFLAG_RW); else ndis_add_sysctl(sc, keystr, "(dynamic string key)", "UNSET", CTLFLAG_RW); RtlFreeAnsiString(&as); *status = NDIS_STATUS_FAILURE; } static ndis_status ndis_decode_parm(block, parm, val) ndis_miniport_block *block; ndis_config_parm *parm; char *val; { unicode_string *ustr; ansi_string as; switch(parm->ncp_type) { case ndis_parm_string: ustr = &parm->ncp_parmdata.ncp_stringdata; if (RtlUnicodeStringToAnsiString(&as, ustr, TRUE)) return (NDIS_STATUS_RESOURCES); bcopy(as.as_buf, val, as.as_len); RtlFreeAnsiString(&as); break; case ndis_parm_int: sprintf(val, "%d", parm->ncp_parmdata.ncp_intdata); break; case ndis_parm_hexint: sprintf(val, "%xu", parm->ncp_parmdata.ncp_intdata); break; default: return (NDIS_STATUS_FAILURE); break; } return (NDIS_STATUS_SUCCESS); } static void NdisWriteConfiguration(status, cfg, key, parm) ndis_status *status; ndis_handle cfg; unicode_string *key; ndis_config_parm *parm; { ansi_string as; char *keystr = NULL; ndis_miniport_block *block; struct ndis_softc *sc; struct sysctl_oid *oidp; struct sysctl_ctx_entry *e; char val[256]; block = (ndis_miniport_block *)cfg; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); if (RtlUnicodeStringToAnsiString(&as, key, TRUE)) { *status = NDIS_STATUS_RESOURCES; return; } keystr = as.as_buf; /* Decode the parameter into a string. */ bzero(val, sizeof(val)); *status = ndis_decode_parm(block, parm, val); if (*status != NDIS_STATUS_SUCCESS) { RtlFreeAnsiString(&as); return; } /* See if the key already exists. */ TAILQ_FOREACH(e, device_get_sysctl_ctx(sc->ndis_dev), link) { oidp = e->entry; if (strcasecmp(oidp->oid_name, keystr) == 0) { /* Found it, set the value. */ strcpy((char *)oidp->oid_arg1, val); RtlFreeAnsiString(&as); return; } } /* Not found, add a new key with the specified value. */ ndis_add_sysctl(sc, keystr, "(dynamically set key)", val, CTLFLAG_RW); RtlFreeAnsiString(&as); *status = NDIS_STATUS_SUCCESS; } static void NdisCloseConfiguration(cfg) ndis_handle cfg; { list_entry *e; ndis_parmlist_entry *pe; ndis_miniport_block *block; ndis_config_parm *p; block = (ndis_miniport_block *)cfg; while (!IsListEmpty(&block->nmb_parmlist)) { e = RemoveHeadList(&block->nmb_parmlist); pe = CONTAINING_RECORD(e, ndis_parmlist_entry, np_list); p = &pe->np_parm; if (p->ncp_type == ndis_parm_string) RtlFreeUnicodeString(&p->ncp_parmdata.ncp_stringdata); ExFreePool(e); } } /* * Initialize a Windows spinlock. */ static void NdisAllocateSpinLock(lock) ndis_spin_lock *lock; { KeInitializeSpinLock(&lock->nsl_spinlock); lock->nsl_kirql = 0; } /* * Destroy a Windows spinlock. This is a no-op for now. There are two reasons * for this. One is that it's sort of superfluous: we don't have to do anything * special to deallocate the spinlock. The other is that there are some buggy * drivers which call NdisFreeSpinLock() _after_ calling NdisFreeMemory() on * the block of memory in which the spinlock resides. (Yes, ADMtek, I'm * talking to you.) */ static void NdisFreeSpinLock(lock) ndis_spin_lock *lock; { #ifdef notdef KeInitializeSpinLock(&lock->nsl_spinlock); lock->nsl_kirql = 0; #endif } /* * Acquire a spinlock from IRQL <= DISPATCH_LEVEL. */ static void NdisAcquireSpinLock(lock) ndis_spin_lock *lock; { KeAcquireSpinLock(&lock->nsl_spinlock, &lock->nsl_kirql); } /* * Release a spinlock from IRQL == DISPATCH_LEVEL. */ static void NdisReleaseSpinLock(lock) ndis_spin_lock *lock; { KeReleaseSpinLock(&lock->nsl_spinlock, lock->nsl_kirql); } /* * Acquire a spinlock when already running at IRQL == DISPATCH_LEVEL. */ static void NdisDprAcquireSpinLock(lock) ndis_spin_lock *lock; { KeAcquireSpinLockAtDpcLevel(&lock->nsl_spinlock); } /* * Release a spinlock without leaving IRQL == DISPATCH_LEVEL. */ static void NdisDprReleaseSpinLock(lock) ndis_spin_lock *lock; { KeReleaseSpinLockFromDpcLevel(&lock->nsl_spinlock); } static void NdisInitializeReadWriteLock(lock) ndis_rw_lock *lock; { KeInitializeSpinLock(&lock->nrl_spinlock); bzero((char *)&lock->nrl_rsvd, sizeof(lock->nrl_rsvd)); } static void NdisAcquireReadWriteLock(ndis_rw_lock *lock, uint8_t writeacc, ndis_lock_state *state) { if (writeacc == TRUE) { KeAcquireSpinLock(&lock->nrl_spinlock, &state->nls_oldirql); lock->nrl_rsvd[0]++; } else lock->nrl_rsvd[1]++; } static void NdisReleaseReadWriteLock(lock, state) ndis_rw_lock *lock; ndis_lock_state *state; { if (lock->nrl_rsvd[0]) { lock->nrl_rsvd[0]--; KeReleaseSpinLock(&lock->nrl_spinlock, state->nls_oldirql); } else lock->nrl_rsvd[1]--; } static uint32_t NdisReadPciSlotInformation(adapter, slot, offset, buf, len) ndis_handle adapter; uint32_t slot; uint32_t offset; void *buf; uint32_t len; { ndis_miniport_block *block; uint32_t i; char *dest; device_t dev; block = (ndis_miniport_block *)adapter; dest = buf; if (block == NULL) return (0); dev = block->nmb_physdeviceobj->do_devext; /* * I have a test system consisting of a Sun w2100z * dual 2.4Ghz Opteron machine and an Atheros 802.11a/b/g * "Aries" miniPCI NIC. (The NIC is installed in the * machine using a miniPCI to PCI bus adapter card.) * When running in SMP mode, I found that * performing a large number of consecutive calls to * NdisReadPciSlotInformation() would result in a * sudden system reset (or in some cases a freeze). * My suspicion is that the multiple reads are somehow * triggering a fatal PCI bus error that leads to a * machine check. The 1us delay in the loop below * seems to prevent this problem. */ for (i = 0; i < len; i++) { DELAY(1); dest[i] = pci_read_config(dev, i + offset, 1); } return (len); } static uint32_t NdisWritePciSlotInformation(adapter, slot, offset, buf, len) ndis_handle adapter; uint32_t slot; uint32_t offset; void *buf; uint32_t len; { ndis_miniport_block *block; uint32_t i; char *dest; device_t dev; block = (ndis_miniport_block *)adapter; dest = buf; if (block == NULL) return (0); dev = block->nmb_physdeviceobj->do_devext; for (i = 0; i < len; i++) { DELAY(1); pci_write_config(dev, i + offset, dest[i], 1); } return (len); } /* * The errorlog routine uses a variable argument list, so we * have to declare it this way. */ #define ERRMSGLEN 512 static void NdisWriteErrorLogEntry(ndis_handle adapter, ndis_error_code code, uint32_t numerrors, ...) { ndis_miniport_block *block; va_list ap; int i, error; char *str = NULL; uint16_t flags; device_t dev; driver_object *drv; struct ndis_softc *sc; struct ifnet *ifp; unicode_string us; ansi_string as = { 0, 0, NULL }; block = (ndis_miniport_block *)adapter; dev = block->nmb_physdeviceobj->do_devext; drv = block->nmb_deviceobj->do_drvobj; sc = device_get_softc(dev); - ifp = sc->ifp; + ifp = NDISUSB_GET_IFNET(sc); if (ifp != NULL && ifp->if_flags & IFF_DEBUG) { error = pe_get_message((vm_offset_t)drv->dro_driverstart, code, &str, &i, &flags); if (error == 0) { if (flags & MESSAGE_RESOURCE_UNICODE) { RtlInitUnicodeString(&us, (uint16_t *)str); if (RtlUnicodeStringToAnsiString(&as, &us, TRUE) == STATUS_SUCCESS) str = as.as_buf; else str = NULL; } } } device_printf(dev, "NDIS ERROR: %x (%s)\n", code, str == NULL ? "unknown error" : str); if (ifp != NULL && ifp->if_flags & IFF_DEBUG) { device_printf(dev, "NDIS NUMERRORS: %x\n", numerrors); va_start(ap, numerrors); for (i = 0; i < numerrors; i++) device_printf(dev, "argptr: %p\n", va_arg(ap, void *)); va_end(ap); } if (as.as_len) RtlFreeAnsiString(&as); } static void ndis_map_cb(arg, segs, nseg, error) void *arg; bus_dma_segment_t *segs; int nseg; int error; { struct ndis_map_arg *ctx; int i; if (error) return; ctx = arg; for (i = 0; i < nseg; i++) { ctx->nma_fraglist[i].npu_physaddr.np_quad = segs[i].ds_addr; ctx->nma_fraglist[i].npu_len = segs[i].ds_len; } ctx->nma_cnt = nseg; } static void NdisMStartBufferPhysicalMapping(ndis_handle adapter, ndis_buffer *buf, uint32_t mapreg, uint8_t writedev, ndis_paddr_unit *addrarray, uint32_t *arraysize) { ndis_miniport_block *block; struct ndis_softc *sc; struct ndis_map_arg nma; bus_dmamap_t map; int error; if (adapter == NULL) return; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); if (mapreg > sc->ndis_mmapcnt) return; map = sc->ndis_mmaps[mapreg]; nma.nma_fraglist = addrarray; error = bus_dmamap_load(sc->ndis_mtag, map, MmGetMdlVirtualAddress(buf), MmGetMdlByteCount(buf), ndis_map_cb, (void *)&nma, BUS_DMA_NOWAIT); if (error) return; bus_dmamap_sync(sc->ndis_mtag, map, writedev ? BUS_DMASYNC_PREWRITE : BUS_DMASYNC_PREREAD); *arraysize = nma.nma_cnt; } static void NdisMCompleteBufferPhysicalMapping(adapter, buf, mapreg) ndis_handle adapter; ndis_buffer *buf; uint32_t mapreg; { ndis_miniport_block *block; struct ndis_softc *sc; bus_dmamap_t map; if (adapter == NULL) return; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); if (mapreg > sc->ndis_mmapcnt) return; map = sc->ndis_mmaps[mapreg]; bus_dmamap_sync(sc->ndis_mtag, map, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->ndis_mtag, map); } /* * This is an older (?) timer init routine which doesn't * accept a miniport context handle. Serialized miniports should * never call this function. */ static void NdisInitializeTimer(timer, func, ctx) ndis_timer *timer; ndis_timer_function func; void *ctx; { KeInitializeTimer(&timer->nt_ktimer); KeInitializeDpc(&timer->nt_kdpc, func, ctx); KeSetImportanceDpc(&timer->nt_kdpc, KDPC_IMPORTANCE_LOW); } static void ndis_timercall(dpc, timer, sysarg1, sysarg2) kdpc *dpc; ndis_miniport_timer *timer; void *sysarg1; void *sysarg2; { /* * Since we're called as a DPC, we should be running * at DISPATCH_LEVEL here. This means to acquire the * spinlock, we can use KeAcquireSpinLockAtDpcLevel() * rather than KeAcquireSpinLock(). */ if (NDIS_SERIALIZED(timer->nmt_block)) KeAcquireSpinLockAtDpcLevel(&timer->nmt_block->nmb_lock); MSCALL4(timer->nmt_timerfunc, dpc, timer->nmt_timerctx, sysarg1, sysarg2); if (NDIS_SERIALIZED(timer->nmt_block)) KeReleaseSpinLockFromDpcLevel(&timer->nmt_block->nmb_lock); } /* * For a long time I wondered why there were two NDIS timer initialization * routines, and why this one needed an NDIS_MINIPORT_TIMER and the * MiniportAdapterHandle. The NDIS_MINIPORT_TIMER has its own callout * function and context pointers separate from those in the DPC, which * allows for another level of indirection: when the timer fires, we * can have our own timer function invoked, and from there we can call * the driver's function. But why go to all that trouble? Then it hit * me: for serialized miniports, the timer callouts are not re-entrant. * By trapping the callouts and having access to the MiniportAdapterHandle, * we can protect the driver callouts by acquiring the NDIS serialization * lock. This is essential for allowing serialized miniports to work * correctly on SMP systems. On UP hosts, setting IRQL to DISPATCH_LEVEL * is enough to prevent other threads from pre-empting you, but with * SMP, you must acquire a lock as well, otherwise the other CPU is * free to clobber you. */ static void NdisMInitializeTimer(timer, handle, func, ctx) ndis_miniport_timer *timer; ndis_handle handle; ndis_timer_function func; void *ctx; { ndis_miniport_block *block; struct ndis_softc *sc; block = (ndis_miniport_block *)handle; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); /* Save the driver's funcptr and context */ timer->nmt_timerfunc = func; timer->nmt_timerctx = ctx; timer->nmt_block = handle; /* * Set up the timer so it will call our intermediate DPC. * Be sure to use the wrapped entry point, since * ntoskrnl_run_dpc() expects to invoke a function with * Microsoft calling conventions. */ KeInitializeTimer(&timer->nmt_ktimer); KeInitializeDpc(&timer->nmt_kdpc, ndis_findwrap((funcptr)ndis_timercall), timer); timer->nmt_ktimer.k_dpc = &timer->nmt_kdpc; } /* * In Windows, there's both an NdisMSetTimer() and an NdisSetTimer(), * but the former is just a macro wrapper around the latter. */ static void NdisSetTimer(timer, msecs) ndis_timer *timer; uint32_t msecs; { /* * KeSetTimer() wants the period in * hundred nanosecond intervals. */ KeSetTimer(&timer->nt_ktimer, ((int64_t)msecs * -10000), &timer->nt_kdpc); } static void NdisMSetPeriodicTimer(timer, msecs) ndis_miniport_timer *timer; uint32_t msecs; { KeSetTimerEx(&timer->nmt_ktimer, ((int64_t)msecs * -10000), msecs, &timer->nmt_kdpc); } /* * Technically, this is really NdisCancelTimer(), but we also * (ab)use it for NdisMCancelTimer(), since in our implementation * we don't need the extra info in the ndis_miniport_timer * structure just to cancel a timer. */ static void NdisMCancelTimer(timer, cancelled) ndis_timer *timer; uint8_t *cancelled; { *cancelled = KeCancelTimer(&timer->nt_ktimer); } static void NdisMQueryAdapterResources(status, adapter, list, buflen) ndis_status *status; ndis_handle adapter; ndis_resource_list *list; uint32_t *buflen; { ndis_miniport_block *block; struct ndis_softc *sc; int rsclen; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); rsclen = sizeof(ndis_resource_list) + (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)); if (*buflen < rsclen) { *buflen = rsclen; *status = NDIS_STATUS_INVALID_LENGTH; return; } bcopy((char *)block->nmb_rlist, (char *)list, rsclen); *status = NDIS_STATUS_SUCCESS; } static ndis_status NdisMRegisterIoPortRange(offset, adapter, port, numports) void **offset; ndis_handle adapter; uint32_t port; uint32_t numports; { struct ndis_miniport_block *block; struct ndis_softc *sc; if (adapter == NULL) return (NDIS_STATUS_FAILURE); block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); if (sc->ndis_res_io == NULL) return (NDIS_STATUS_FAILURE); /* Don't let the device map more ports than we have. */ if (rman_get_size(sc->ndis_res_io) < numports) return (NDIS_STATUS_INVALID_LENGTH); *offset = (void *)rman_get_start(sc->ndis_res_io); return (NDIS_STATUS_SUCCESS); } static void NdisMDeregisterIoPortRange(adapter, port, numports, offset) ndis_handle adapter; uint32_t port; uint32_t numports; void *offset; { } static void NdisReadNetworkAddress(status, addr, addrlen, adapter) ndis_status *status; void **addr; uint32_t *addrlen; ndis_handle adapter; { struct ndis_softc *sc; + struct ifnet *ifp; ndis_miniport_block *block; uint8_t empty[] = { 0, 0, 0, 0, 0, 0 }; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); - if (sc->ifp == NULL) { + ifp = NDISUSB_GET_IFNET(sc); + if (ifp == NULL) { *status = NDIS_STATUS_FAILURE; return; } - if (sc->ifp->if_addr == NULL || + if (ifp->if_addr == NULL || bcmp(IF_LLADDR(sc->ifp), empty, ETHER_ADDR_LEN) == 0) *status = NDIS_STATUS_FAILURE; else { *addr = IF_LLADDR(sc->ifp); *addrlen = ETHER_ADDR_LEN; *status = NDIS_STATUS_SUCCESS; } } static ndis_status NdisQueryMapRegisterCount(bustype, cnt) uint32_t bustype; uint32_t *cnt; { *cnt = 8192; return (NDIS_STATUS_SUCCESS); } static ndis_status NdisMAllocateMapRegisters(ndis_handle adapter, uint32_t dmachannel, uint8_t dmasize, uint32_t physmapneeded, uint32_t maxmap) { struct ndis_softc *sc; ndis_miniport_block *block; int error, i, nseg = NDIS_MAXSEG; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); sc->ndis_mmaps = malloc(sizeof(bus_dmamap_t) * physmapneeded, M_DEVBUF, M_NOWAIT|M_ZERO); if (sc->ndis_mmaps == NULL) return (NDIS_STATUS_RESOURCES); error = bus_dma_tag_create(sc->ndis_parent_tag, ETHER_ALIGN, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, maxmap * nseg, nseg, maxmap, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->ndis_mtag); if (error) { free(sc->ndis_mmaps, M_DEVBUF); return (NDIS_STATUS_RESOURCES); } for (i = 0; i < physmapneeded; i++) bus_dmamap_create(sc->ndis_mtag, 0, &sc->ndis_mmaps[i]); sc->ndis_mmapcnt = physmapneeded; return (NDIS_STATUS_SUCCESS); } static void NdisMFreeMapRegisters(adapter) ndis_handle adapter; { struct ndis_softc *sc; ndis_miniport_block *block; int i; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); for (i = 0; i < sc->ndis_mmapcnt; i++) bus_dmamap_destroy(sc->ndis_mtag, sc->ndis_mmaps[i]); free(sc->ndis_mmaps, M_DEVBUF); bus_dma_tag_destroy(sc->ndis_mtag); } static void ndis_mapshared_cb(arg, segs, nseg, error) void *arg; bus_dma_segment_t *segs; int nseg; int error; { ndis_physaddr *p; if (error || nseg > 1) return; p = arg; p->np_quad = segs[0].ds_addr; } /* * This maps to bus_dmamem_alloc(). */ static void NdisMAllocateSharedMemory(ndis_handle adapter, uint32_t len, uint8_t cached, void **vaddr, ndis_physaddr *paddr) { ndis_miniport_block *block; struct ndis_softc *sc; struct ndis_shmem *sh; int error; if (adapter == NULL) return; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); sh = malloc(sizeof(struct ndis_shmem), M_DEVBUF, M_NOWAIT|M_ZERO); if (sh == NULL) return; InitializeListHead(&sh->ndis_list); /* * When performing shared memory allocations, create a tag * with a lowaddr limit that restricts physical memory mappings * so that they all fall within the first 1GB of memory. * At least one device/driver combination (Linksys Instant * Wireless PCI Card V2.7, Broadcom 802.11b) seems to have * problems with performing DMA operations with physical * addresses that lie above the 1GB mark. I don't know if this * is a hardware limitation or if the addresses are being * truncated within the driver, but this seems to be the only * way to make these cards work reliably in systems with more * than 1GB of physical memory. */ error = bus_dma_tag_create(sc->ndis_parent_tag, 64, 0, NDIS_BUS_SPACE_SHARED_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, len, 1, len, BUS_DMA_ALLOCNOW, NULL, NULL, &sh->ndis_stag); if (error) { free(sh, M_DEVBUF); return; } error = bus_dmamem_alloc(sh->ndis_stag, vaddr, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sh->ndis_smap); if (error) { bus_dma_tag_destroy(sh->ndis_stag); free(sh, M_DEVBUF); return; } error = bus_dmamap_load(sh->ndis_stag, sh->ndis_smap, *vaddr, len, ndis_mapshared_cb, (void *)paddr, BUS_DMA_NOWAIT); if (error) { bus_dmamem_free(sh->ndis_stag, *vaddr, sh->ndis_smap); bus_dma_tag_destroy(sh->ndis_stag); free(sh, M_DEVBUF); return; } /* * Save the physical address along with the source address. * The AirGo MIMO driver will call NdisMFreeSharedMemory() * with a bogus virtual address sometimes, but with a valid * physical address. To keep this from causing trouble, we * use the physical address to as a sanity check in case * searching based on the virtual address fails. */ NDIS_LOCK(sc); sh->ndis_paddr.np_quad = paddr->np_quad; sh->ndis_saddr = *vaddr; InsertHeadList((&sc->ndis_shlist), (&sh->ndis_list)); NDIS_UNLOCK(sc); } struct ndis_allocwork { uint32_t na_len; uint8_t na_cached; void *na_ctx; io_workitem *na_iw; }; static void ndis_asyncmem_complete(dobj, arg) device_object *dobj; void *arg; { ndis_miniport_block *block; struct ndis_softc *sc; struct ndis_allocwork *w; void *vaddr; ndis_physaddr paddr; ndis_allocdone_handler donefunc; w = arg; block = (ndis_miniport_block *)dobj->do_devext; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); vaddr = NULL; paddr.np_quad = 0; donefunc = sc->ndis_chars->nmc_allocate_complete_func; NdisMAllocateSharedMemory(block, w->na_len, w->na_cached, &vaddr, &paddr); MSCALL5(donefunc, block, vaddr, &paddr, w->na_len, w->na_ctx); IoFreeWorkItem(w->na_iw); free(w, M_DEVBUF); } static ndis_status NdisMAllocateSharedMemoryAsync(ndis_handle adapter, uint32_t len, uint8_t cached, void *ctx) { ndis_miniport_block *block; struct ndis_allocwork *w; io_workitem *iw; io_workitem_func ifw; if (adapter == NULL) return (NDIS_STATUS_FAILURE); block = adapter; iw = IoAllocateWorkItem(block->nmb_deviceobj); if (iw == NULL) return (NDIS_STATUS_FAILURE); w = malloc(sizeof(struct ndis_allocwork), M_TEMP, M_NOWAIT); if (w == NULL) return (NDIS_STATUS_FAILURE); w->na_cached = cached; w->na_len = len; w->na_ctx = ctx; w->na_iw = iw; ifw = (io_workitem_func)ndis_findwrap((funcptr)ndis_asyncmem_complete); IoQueueWorkItem(iw, ifw, WORKQUEUE_DELAYED, w); return (NDIS_STATUS_PENDING); } static void NdisMFreeSharedMemory(ndis_handle adapter, uint32_t len, uint8_t cached, void *vaddr, ndis_physaddr paddr) { ndis_miniport_block *block; struct ndis_softc *sc; struct ndis_shmem *sh = NULL; list_entry *l; if (vaddr == NULL || adapter == NULL) return; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); /* Sanity check: is list empty? */ if (IsListEmpty(&sc->ndis_shlist)) return; NDIS_LOCK(sc); l = sc->ndis_shlist.nle_flink; while (l != &sc->ndis_shlist) { sh = CONTAINING_RECORD(l, struct ndis_shmem, ndis_list); if (sh->ndis_saddr == vaddr) break; /* * Check the physaddr too, just in case the driver lied * about the virtual address. */ if (sh->ndis_paddr.np_quad == paddr.np_quad) break; l = l->nle_flink; } if (sh == NULL) { NDIS_UNLOCK(sc); printf("NDIS: buggy driver tried to free " "invalid shared memory: vaddr: %p paddr: 0x%jx\n", vaddr, (uintmax_t)paddr.np_quad); return; } RemoveEntryList(&sh->ndis_list); NDIS_UNLOCK(sc); bus_dmamap_unload(sh->ndis_stag, sh->ndis_smap); bus_dmamem_free(sh->ndis_stag, sh->ndis_saddr, sh->ndis_smap); bus_dma_tag_destroy(sh->ndis_stag); free(sh, M_DEVBUF); } static ndis_status NdisMMapIoSpace(vaddr, adapter, paddr, len) void **vaddr; ndis_handle adapter; ndis_physaddr paddr; uint32_t len; { if (adapter == NULL) return (NDIS_STATUS_FAILURE); *vaddr = MmMapIoSpace(paddr.np_quad, len, 0); if (*vaddr == NULL) return (NDIS_STATUS_FAILURE); return (NDIS_STATUS_SUCCESS); } static void NdisMUnmapIoSpace(adapter, vaddr, len) ndis_handle adapter; void *vaddr; uint32_t len; { MmUnmapIoSpace(vaddr, len); } static uint32_t NdisGetCacheFillSize(void) { return (128); } static void * NdisGetRoutineAddress(ustr) unicode_string *ustr; { ansi_string astr; if (RtlUnicodeStringToAnsiString(&astr, ustr, TRUE)) return (NULL); return (ndis_get_routine_address(ndis_functbl, astr.as_buf)); } static uint32_t NdisMGetDmaAlignment(handle) ndis_handle handle; { return (16); } /* * NDIS has two methods for dealing with NICs that support DMA. * One is to just pass packets to the driver and let it call * NdisMStartBufferPhysicalMapping() to map each buffer in the packet * all by itself, and the other is to let the NDIS library handle the * buffer mapping internally, and hand the driver an already populated * scatter/gather fragment list. If the driver calls * NdisMInitializeScatterGatherDma(), it wants to use the latter * method. */ static ndis_status NdisMInitializeScatterGatherDma(ndis_handle adapter, uint8_t is64, uint32_t maxphysmap) { struct ndis_softc *sc; ndis_miniport_block *block; int error; if (adapter == NULL) return (NDIS_STATUS_FAILURE); block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); /* Don't do this twice. */ if (sc->ndis_sc == 1) return (NDIS_STATUS_SUCCESS); error = bus_dma_tag_create(sc->ndis_parent_tag, ETHER_ALIGN, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES * NDIS_MAXSEG, NDIS_MAXSEG, MCLBYTES, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->ndis_ttag); sc->ndis_sc = 1; return (NDIS_STATUS_SUCCESS); } void NdisAllocatePacketPool(status, pool, descnum, protrsvdlen) ndis_status *status; ndis_handle *pool; uint32_t descnum; uint32_t protrsvdlen; { ndis_packet_pool *p; ndis_packet *packets; int i; p = ExAllocatePoolWithTag(NonPagedPool, sizeof(ndis_packet_pool), 0); if (p == NULL) { *status = NDIS_STATUS_RESOURCES; return; } p->np_cnt = descnum + NDIS_POOL_EXTRA; p->np_protrsvd = protrsvdlen; p->np_len = sizeof(ndis_packet) + protrsvdlen; packets = ExAllocatePoolWithTag(NonPagedPool, p->np_cnt * p->np_len, 0); if (packets == NULL) { ExFreePool(p); *status = NDIS_STATUS_RESOURCES; return; } p->np_pktmem = packets; for (i = 0; i < p->np_cnt; i++) InterlockedPushEntrySList(&p->np_head, (struct slist_entry *)&packets[i]); #ifdef NDIS_DEBUG_PACKETS p->np_dead = 0; KeInitializeSpinLock(&p->np_lock); KeInitializeEvent(&p->np_event, EVENT_TYPE_NOTIFY, TRUE); #endif *pool = p; *status = NDIS_STATUS_SUCCESS; } void NdisAllocatePacketPoolEx(status, pool, descnum, oflowdescnum, protrsvdlen) ndis_status *status; ndis_handle *pool; uint32_t descnum; uint32_t oflowdescnum; uint32_t protrsvdlen; { return (NdisAllocatePacketPool(status, pool, descnum + oflowdescnum, protrsvdlen)); } uint32_t NdisPacketPoolUsage(pool) ndis_handle pool; { ndis_packet_pool *p; p = (ndis_packet_pool *)pool; return (p->np_cnt - ExQueryDepthSList(&p->np_head)); } void NdisFreePacketPool(pool) ndis_handle pool; { ndis_packet_pool *p; int usage; #ifdef NDIS_DEBUG_PACKETS uint8_t irql; #endif p = (ndis_packet_pool *)pool; #ifdef NDIS_DEBUG_PACKETS KeAcquireSpinLock(&p->np_lock, &irql); #endif usage = NdisPacketPoolUsage(pool); #ifdef NDIS_DEBUG_PACKETS if (usage) { p->np_dead = 1; KeResetEvent(&p->np_event); KeReleaseSpinLock(&p->np_lock, irql); KeWaitForSingleObject(&p->np_event, 0, 0, FALSE, NULL); } else KeReleaseSpinLock(&p->np_lock, irql); #endif ExFreePool(p->np_pktmem); ExFreePool(p); } void NdisAllocatePacket(status, packet, pool) ndis_status *status; ndis_packet **packet; ndis_handle pool; { ndis_packet_pool *p; ndis_packet *pkt; #ifdef NDIS_DEBUG_PACKETS uint8_t irql; #endif p = (ndis_packet_pool *)pool; #ifdef NDIS_DEBUG_PACKETS KeAcquireSpinLock(&p->np_lock, &irql); if (p->np_dead) { KeReleaseSpinLock(&p->np_lock, irql); printf("NDIS: tried to allocate packet from dead pool %p\n", pool); *status = NDIS_STATUS_RESOURCES; return; } #endif pkt = (ndis_packet *)InterlockedPopEntrySList(&p->np_head); #ifdef NDIS_DEBUG_PACKETS KeReleaseSpinLock(&p->np_lock, irql); #endif if (pkt == NULL) { *status = NDIS_STATUS_RESOURCES; return; } bzero((char *)pkt, sizeof(ndis_packet)); /* Save pointer to the pool. */ pkt->np_private.npp_pool = pool; /* Set the oob offset pointer. Lots of things expect this. */ pkt->np_private.npp_packetooboffset = offsetof(ndis_packet, np_oob); /* * We must initialize the packet flags correctly in order * for the NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO() and * NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO() macros to work * correctly. */ pkt->np_private.npp_ndispktflags = NDIS_PACKET_ALLOCATED_BY_NDIS; pkt->np_private.npp_validcounts = FALSE; *packet = pkt; *status = NDIS_STATUS_SUCCESS; } void NdisFreePacket(packet) ndis_packet *packet; { ndis_packet_pool *p; #ifdef NDIS_DEBUG_PACKETS uint8_t irql; #endif p = (ndis_packet_pool *)packet->np_private.npp_pool; #ifdef NDIS_DEBUG_PACKETS KeAcquireSpinLock(&p->np_lock, &irql); #endif InterlockedPushEntrySList(&p->np_head, (slist_entry *)packet); #ifdef NDIS_DEBUG_PACKETS if (p->np_dead) { if (ExQueryDepthSList(&p->np_head) == p->np_cnt) KeSetEvent(&p->np_event, IO_NO_INCREMENT, FALSE); } KeReleaseSpinLock(&p->np_lock, irql); #endif } static void NdisUnchainBufferAtFront(packet, buf) ndis_packet *packet; ndis_buffer **buf; { ndis_packet_private *priv; if (packet == NULL || buf == NULL) return; priv = &packet->np_private; priv->npp_validcounts = FALSE; if (priv->npp_head == priv->npp_tail) { *buf = priv->npp_head; priv->npp_head = priv->npp_tail = NULL; } else { *buf = priv->npp_head; priv->npp_head = (*buf)->mdl_next; } } static void NdisUnchainBufferAtBack(packet, buf) ndis_packet *packet; ndis_buffer **buf; { ndis_packet_private *priv; ndis_buffer *tmp; if (packet == NULL || buf == NULL) return; priv = &packet->np_private; priv->npp_validcounts = FALSE; if (priv->npp_head == priv->npp_tail) { *buf = priv->npp_head; priv->npp_head = priv->npp_tail = NULL; } else { *buf = priv->npp_tail; tmp = priv->npp_head; while (tmp->mdl_next != priv->npp_tail) tmp = tmp->mdl_next; priv->npp_tail = tmp; tmp->mdl_next = NULL; } } /* * The NDIS "buffer" is really an MDL (memory descriptor list) * which is used to describe a buffer in a way that allows it * to mapped into different contexts. We have to be careful how * we handle them: in some versions of Windows, the NdisFreeBuffer() * routine is an actual function in the NDIS API, but in others * it's just a macro wrapper around IoFreeMdl(). There's really * no way to use the 'descnum' parameter to count how many * "buffers" are allocated since in order to use IoFreeMdl() to * dispose of a buffer, we have to use IoAllocateMdl() to allocate * them, and IoAllocateMdl() just grabs them out of the heap. */ static void NdisAllocateBufferPool(status, pool, descnum) ndis_status *status; ndis_handle *pool; uint32_t descnum; { /* * The only thing we can really do here is verify that descnum * is a reasonable value, but I really don't know what to check * it against. */ *pool = NonPagedPool; *status = NDIS_STATUS_SUCCESS; } static void NdisFreeBufferPool(pool) ndis_handle pool; { } static void NdisAllocateBuffer(status, buffer, pool, vaddr, len) ndis_status *status; ndis_buffer **buffer; ndis_handle pool; void *vaddr; uint32_t len; { ndis_buffer *buf; buf = IoAllocateMdl(vaddr, len, FALSE, FALSE, NULL); if (buf == NULL) { *status = NDIS_STATUS_RESOURCES; return; } MmBuildMdlForNonPagedPool(buf); *buffer = buf; *status = NDIS_STATUS_SUCCESS; } static void NdisFreeBuffer(buf) ndis_buffer *buf; { IoFreeMdl(buf); } /* Aw c'mon. */ static uint32_t NdisBufferLength(buf) ndis_buffer *buf; { return (MmGetMdlByteCount(buf)); } /* * Get the virtual address and length of a buffer. * Note: the vaddr argument is optional. */ static void NdisQueryBuffer(buf, vaddr, len) ndis_buffer *buf; void **vaddr; uint32_t *len; { if (vaddr != NULL) *vaddr = MmGetMdlVirtualAddress(buf); *len = MmGetMdlByteCount(buf); } /* Same as above -- we don't care about the priority. */ static void NdisQueryBufferSafe(buf, vaddr, len, prio) ndis_buffer *buf; void **vaddr; uint32_t *len; uint32_t prio; { if (vaddr != NULL) *vaddr = MmGetMdlVirtualAddress(buf); *len = MmGetMdlByteCount(buf); } /* Damnit Microsoft!! How many ways can you do the same thing?! */ static void * NdisBufferVirtualAddress(buf) ndis_buffer *buf; { return (MmGetMdlVirtualAddress(buf)); } static void * NdisBufferVirtualAddressSafe(buf, prio) ndis_buffer *buf; uint32_t prio; { return (MmGetMdlVirtualAddress(buf)); } static void NdisAdjustBufferLength(buf, len) ndis_buffer *buf; int len; { MmGetMdlByteCount(buf) = len; } static uint32_t NdisInterlockedIncrement(addend) uint32_t *addend; { atomic_add_long((u_long *)addend, 1); return (*addend); } static uint32_t NdisInterlockedDecrement(addend) uint32_t *addend; { atomic_subtract_long((u_long *)addend, 1); return (*addend); } static uint32_t NdisGetVersion(void) { return (0x00050001); } static void NdisInitializeEvent(event) ndis_event *event; { /* * NDIS events are always notification * events, and should be initialized to the * not signaled state. */ KeInitializeEvent(&event->ne_event, EVENT_TYPE_NOTIFY, FALSE); } static void NdisSetEvent(event) ndis_event *event; { KeSetEvent(&event->ne_event, IO_NO_INCREMENT, FALSE); } static void NdisResetEvent(event) ndis_event *event; { KeResetEvent(&event->ne_event); } static uint8_t NdisWaitEvent(event, msecs) ndis_event *event; uint32_t msecs; { int64_t duetime; uint32_t rval; duetime = ((int64_t)msecs * -10000); rval = KeWaitForSingleObject(event, 0, 0, TRUE, msecs ? & duetime : NULL); if (rval == STATUS_TIMEOUT) return (FALSE); return (TRUE); } static ndis_status NdisUnicodeStringToAnsiString(dstr, sstr) ansi_string *dstr; unicode_string *sstr; { uint32_t rval; rval = RtlUnicodeStringToAnsiString(dstr, sstr, FALSE); if (rval == STATUS_INSUFFICIENT_RESOURCES) return (NDIS_STATUS_RESOURCES); if (rval) return (NDIS_STATUS_FAILURE); return (NDIS_STATUS_SUCCESS); } static ndis_status NdisAnsiStringToUnicodeString(dstr, sstr) unicode_string *dstr; ansi_string *sstr; { uint32_t rval; rval = RtlAnsiStringToUnicodeString(dstr, sstr, FALSE); if (rval == STATUS_INSUFFICIENT_RESOURCES) return (NDIS_STATUS_RESOURCES); if (rval) return (NDIS_STATUS_FAILURE); return (NDIS_STATUS_SUCCESS); } static ndis_status NdisMPciAssignResources(adapter, slot, list) ndis_handle adapter; uint32_t slot; ndis_resource_list **list; { ndis_miniport_block *block; if (adapter == NULL || list == NULL) return (NDIS_STATUS_FAILURE); block = (ndis_miniport_block *)adapter; *list = block->nmb_rlist; return (NDIS_STATUS_SUCCESS); } static uint8_t ndis_intr(iobj, arg) kinterrupt *iobj; void *arg; { struct ndis_softc *sc; uint8_t is_our_intr = FALSE; int call_isr = 0; ndis_miniport_interrupt *intr; sc = arg; intr = sc->ndis_block->nmb_interrupt; if (intr == NULL || sc->ndis_block->nmb_miniportadapterctx == NULL) return (FALSE); if (sc->ndis_block->nmb_interrupt->ni_isrreq == TRUE) MSCALL3(intr->ni_isrfunc, &is_our_intr, &call_isr, sc->ndis_block->nmb_miniportadapterctx); else { MSCALL1(sc->ndis_chars->nmc_disable_interrupts_func, sc->ndis_block->nmb_miniportadapterctx); call_isr = 1; } if (call_isr) IoRequestDpc(sc->ndis_block->nmb_deviceobj, NULL, sc); return (is_our_intr); } static void ndis_intrhand(dpc, intr, sysarg1, sysarg2) kdpc *dpc; ndis_miniport_interrupt *intr; void *sysarg1; void *sysarg2; { struct ndis_softc *sc; ndis_miniport_block *block; ndis_handle adapter; block = intr->ni_block; adapter = block->nmb_miniportadapterctx; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); if (NDIS_SERIALIZED(sc->ndis_block)) KeAcquireSpinLockAtDpcLevel(&block->nmb_lock); MSCALL1(intr->ni_dpcfunc, adapter); /* If there's a MiniportEnableInterrupt() routine, call it. */ if (sc->ndis_chars->nmc_enable_interrupts_func != NULL) MSCALL1(sc->ndis_chars->nmc_enable_interrupts_func, adapter); if (NDIS_SERIALIZED(sc->ndis_block)) KeReleaseSpinLockFromDpcLevel(&block->nmb_lock); /* * Set the completion event if we've drained all * pending interrupts. */ KeAcquireSpinLockAtDpcLevel(&intr->ni_dpccountlock); intr->ni_dpccnt--; if (intr->ni_dpccnt == 0) KeSetEvent(&intr->ni_dpcevt, IO_NO_INCREMENT, FALSE); KeReleaseSpinLockFromDpcLevel(&intr->ni_dpccountlock); } static ndis_status NdisMRegisterInterrupt(ndis_miniport_interrupt *intr, ndis_handle adapter, uint32_t ivec, uint32_t ilevel, uint8_t reqisr, uint8_t shared, ndis_interrupt_mode imode) { ndis_miniport_block *block; ndis_miniport_characteristics *ch; struct ndis_softc *sc; int error; block = adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); ch = IoGetDriverObjectExtension(block->nmb_deviceobj->do_drvobj, (void *)1); intr->ni_rsvd = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct mtx), 0); if (intr->ni_rsvd == NULL) return (NDIS_STATUS_RESOURCES); intr->ni_block = adapter; intr->ni_isrreq = reqisr; intr->ni_shared = shared; intr->ni_dpccnt = 0; intr->ni_isrfunc = ch->nmc_isr_func; intr->ni_dpcfunc = ch->nmc_interrupt_func; KeInitializeEvent(&intr->ni_dpcevt, EVENT_TYPE_NOTIFY, TRUE); KeInitializeDpc(&intr->ni_dpc, ndis_findwrap((funcptr)ndis_intrhand), intr); KeSetImportanceDpc(&intr->ni_dpc, KDPC_IMPORTANCE_LOW); error = IoConnectInterrupt(&intr->ni_introbj, ndis_findwrap((funcptr)ndis_intr), sc, NULL, ivec, ilevel, 0, imode, shared, 0, FALSE); if (error != STATUS_SUCCESS) return (NDIS_STATUS_FAILURE); block->nmb_interrupt = intr; return (NDIS_STATUS_SUCCESS); } static void NdisMDeregisterInterrupt(intr) ndis_miniport_interrupt *intr; { ndis_miniport_block *block; uint8_t irql; block = intr->ni_block; /* Should really be KeSynchronizeExecution() */ KeAcquireSpinLock(intr->ni_introbj->ki_lock, &irql); block->nmb_interrupt = NULL; KeReleaseSpinLock(intr->ni_introbj->ki_lock, irql); /* KeFlushQueuedDpcs(); */ /* Disconnect our ISR */ IoDisconnectInterrupt(intr->ni_introbj); KeWaitForSingleObject(&intr->ni_dpcevt, 0, 0, FALSE, NULL); KeResetEvent(&intr->ni_dpcevt); } static void NdisMRegisterAdapterShutdownHandler(adapter, shutdownctx, shutdownfunc) ndis_handle adapter; void *shutdownctx; ndis_shutdown_handler shutdownfunc; { ndis_miniport_block *block; ndis_miniport_characteristics *chars; struct ndis_softc *sc; if (adapter == NULL) return; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); chars = sc->ndis_chars; chars->nmc_shutdown_handler = shutdownfunc; chars->nmc_rsvd0 = shutdownctx; } static void NdisMDeregisterAdapterShutdownHandler(adapter) ndis_handle adapter; { ndis_miniport_block *block; ndis_miniport_characteristics *chars; struct ndis_softc *sc; if (adapter == NULL) return; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); chars = sc->ndis_chars; chars->nmc_shutdown_handler = NULL; chars->nmc_rsvd0 = NULL; } static uint32_t NDIS_BUFFER_TO_SPAN_PAGES(buf) ndis_buffer *buf; { if (buf == NULL) return (0); if (MmGetMdlByteCount(buf) == 0) return (1); return (SPAN_PAGES(MmGetMdlVirtualAddress(buf), MmGetMdlByteCount(buf))); } static void NdisGetBufferPhysicalArraySize(buf, pages) ndis_buffer *buf; uint32_t *pages; { if (buf == NULL) return; *pages = NDIS_BUFFER_TO_SPAN_PAGES(buf); } static void NdisQueryBufferOffset(buf, off, len) ndis_buffer *buf; uint32_t *off; uint32_t *len; { if (buf == NULL) return; *off = MmGetMdlByteOffset(buf); *len = MmGetMdlByteCount(buf); } void NdisMSleep(usecs) uint32_t usecs; { ktimer timer; /* * During system bootstrap, (i.e. cold == 1), we aren't * allowed to sleep, so we have to do a hard DELAY() * instead. */ if (cold) DELAY(usecs); else { KeInitializeTimer(&timer); KeSetTimer(&timer, ((int64_t)usecs * -10), NULL); KeWaitForSingleObject(&timer, 0, 0, FALSE, NULL); } } static uint32_t NdisReadPcmciaAttributeMemory(handle, offset, buf, len) ndis_handle handle; uint32_t offset; void *buf; uint32_t len; { struct ndis_softc *sc; ndis_miniport_block *block; bus_space_handle_t bh; bus_space_tag_t bt; char *dest; uint32_t i; if (handle == NULL) return (0); block = (ndis_miniport_block *)handle; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); dest = buf; bh = rman_get_bushandle(sc->ndis_res_am); bt = rman_get_bustag(sc->ndis_res_am); for (i = 0; i < len; i++) dest[i] = bus_space_read_1(bt, bh, (offset + i) * 2); return (i); } static uint32_t NdisWritePcmciaAttributeMemory(handle, offset, buf, len) ndis_handle handle; uint32_t offset; void *buf; uint32_t len; { struct ndis_softc *sc; ndis_miniport_block *block; bus_space_handle_t bh; bus_space_tag_t bt; char *src; uint32_t i; if (handle == NULL) return (0); block = (ndis_miniport_block *)handle; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); src = buf; bh = rman_get_bushandle(sc->ndis_res_am); bt = rman_get_bustag(sc->ndis_res_am); for (i = 0; i < len; i++) bus_space_write_1(bt, bh, (offset + i) * 2, src[i]); return (i); } static list_entry * NdisInterlockedInsertHeadList(head, entry, lock) list_entry *head; list_entry *entry; ndis_spin_lock *lock; { list_entry *flink; KeAcquireSpinLock(&lock->nsl_spinlock, &lock->nsl_kirql); flink = head->nle_flink; entry->nle_flink = flink; entry->nle_blink = head; flink->nle_blink = entry; head->nle_flink = entry; KeReleaseSpinLock(&lock->nsl_spinlock, lock->nsl_kirql); return (flink); } static list_entry * NdisInterlockedRemoveHeadList(head, lock) list_entry *head; ndis_spin_lock *lock; { list_entry *flink; list_entry *entry; KeAcquireSpinLock(&lock->nsl_spinlock, &lock->nsl_kirql); entry = head->nle_flink; flink = entry->nle_flink; head->nle_flink = flink; flink->nle_blink = head; KeReleaseSpinLock(&lock->nsl_spinlock, lock->nsl_kirql); return (entry); } static list_entry * NdisInterlockedInsertTailList(head, entry, lock) list_entry *head; list_entry *entry; ndis_spin_lock *lock; { list_entry *blink; KeAcquireSpinLock(&lock->nsl_spinlock, &lock->nsl_kirql); blink = head->nle_blink; entry->nle_flink = head; entry->nle_blink = blink; blink->nle_flink = entry; head->nle_blink = entry; KeReleaseSpinLock(&lock->nsl_spinlock, lock->nsl_kirql); return (blink); } static uint8_t NdisMSynchronizeWithInterrupt(intr, syncfunc, syncctx) ndis_miniport_interrupt *intr; void *syncfunc; void *syncctx; { return (KeSynchronizeExecution(intr->ni_introbj, syncfunc, syncctx)); } static void NdisGetCurrentSystemTime(tval) uint64_t *tval; { ntoskrnl_time(tval); } /* * Return the number of milliseconds since the system booted. */ static void NdisGetSystemUpTime(tval) uint32_t *tval; { struct timespec ts; nanouptime(&ts); *tval = ts.tv_nsec / 1000000 + ts.tv_sec * 1000; } static void NdisInitializeString(dst, src) unicode_string *dst; char *src; { ansi_string as; RtlInitAnsiString(&as, src); RtlAnsiStringToUnicodeString(dst, &as, TRUE); } static void NdisFreeString(str) unicode_string *str; { RtlFreeUnicodeString(str); } static ndis_status NdisMRemoveMiniport(adapter) ndis_handle *adapter; { return (NDIS_STATUS_SUCCESS); } static void NdisInitAnsiString(dst, src) ansi_string *dst; char *src; { RtlInitAnsiString(dst, src); } static void NdisInitUnicodeString(dst, src) unicode_string *dst; uint16_t *src; { RtlInitUnicodeString(dst, src); } static void NdisMGetDeviceProperty(adapter, phydevobj, funcdevobj, nextdevobj, resources, transresources) ndis_handle adapter; device_object **phydevobj; device_object **funcdevobj; device_object **nextdevobj; cm_resource_list *resources; cm_resource_list *transresources; { ndis_miniport_block *block; block = (ndis_miniport_block *)adapter; if (phydevobj != NULL) *phydevobj = block->nmb_physdeviceobj; if (funcdevobj != NULL) *funcdevobj = block->nmb_deviceobj; if (nextdevobj != NULL) *nextdevobj = block->nmb_nextdeviceobj; } static void NdisGetFirstBufferFromPacket(packet, buf, firstva, firstlen, totlen) ndis_packet *packet; ndis_buffer **buf; void **firstva; uint32_t *firstlen; uint32_t *totlen; { ndis_buffer *tmp; tmp = packet->np_private.npp_head; *buf = tmp; if (tmp == NULL) { *firstva = NULL; *firstlen = *totlen = 0; } else { *firstva = MmGetMdlVirtualAddress(tmp); *firstlen = *totlen = MmGetMdlByteCount(tmp); for (tmp = tmp->mdl_next; tmp != NULL; tmp = tmp->mdl_next) *totlen += MmGetMdlByteCount(tmp); } } static void NdisGetFirstBufferFromPacketSafe(packet, buf, firstva, firstlen, totlen, prio) ndis_packet *packet; ndis_buffer **buf; void **firstva; uint32_t *firstlen; uint32_t *totlen; uint32_t prio; { NdisGetFirstBufferFromPacket(packet, buf, firstva, firstlen, totlen); } static int ndis_find_sym(lf, filename, suffix, sym) linker_file_t lf; char *filename; char *suffix; caddr_t *sym; { char *fullsym; char *suf; u_int i; fullsym = ExAllocatePoolWithTag(NonPagedPool, MAXPATHLEN, 0); if (fullsym == NULL) return (ENOMEM); bzero(fullsym, MAXPATHLEN); strncpy(fullsym, filename, MAXPATHLEN); if (strlen(filename) < 4) { ExFreePool(fullsym); return (EINVAL); } /* If the filename has a .ko suffix, strip if off. */ suf = fullsym + (strlen(filename) - 3); if (strcmp(suf, ".ko") == 0) *suf = '\0'; for (i = 0; i < strlen(fullsym); i++) { if (fullsym[i] == '.') fullsym[i] = '_'; else fullsym[i] = tolower(fullsym[i]); } strcat(fullsym, suffix); *sym = linker_file_lookup_symbol(lf, fullsym, 0); ExFreePool(fullsym); if (*sym == 0) return (ENOENT); return (0); } struct ndis_checkmodule { char *afilename; ndis_fh *fh; }; /* * See if a single module contains the symbols for a specified file. */ static int NdisCheckModule(linker_file_t lf, void *context) { struct ndis_checkmodule *nc; caddr_t kldstart, kldend; nc = (struct ndis_checkmodule *)context; if (ndis_find_sym(lf, nc->afilename, "_start", &kldstart)) return (0); if (ndis_find_sym(lf, nc->afilename, "_end", &kldend)) return (0); nc->fh->nf_vp = lf; nc->fh->nf_map = NULL; nc->fh->nf_type = NDIS_FH_TYPE_MODULE; nc->fh->nf_maplen = (kldend - kldstart) & 0xFFFFFFFF; return (1); } /* can also return NDIS_STATUS_RESOURCES/NDIS_STATUS_ERROR_READING_FILE */ static void NdisOpenFile(status, filehandle, filelength, filename, highestaddr) ndis_status *status; ndis_handle *filehandle; uint32_t *filelength; unicode_string *filename; ndis_physaddr highestaddr; { ansi_string as; char *afilename = NULL; struct thread *td = curthread; struct nameidata nd; int flags, error; struct vattr vat; struct vattr *vap = &vat; ndis_fh *fh; char *path; struct ndis_checkmodule nc; if (RtlUnicodeStringToAnsiString(&as, filename, TRUE)) { *status = NDIS_STATUS_RESOURCES; return; } afilename = strdup(as.as_buf, M_DEVBUF); RtlFreeAnsiString(&as); fh = ExAllocatePoolWithTag(NonPagedPool, sizeof(ndis_fh), 0); if (fh == NULL) { free(afilename, M_DEVBUF); *status = NDIS_STATUS_RESOURCES; return; } fh->nf_name = afilename; /* * During system bootstrap, it's impossible to load files * from the rootfs since it's not mounted yet. We therefore * offer the possibility of opening files that have been * preloaded as modules instead. Both choices will work * when kldloading a module from multiuser, but only the * module option will work during bootstrap. The module * loading option works by using the ndiscvt(8) utility * to convert the arbitrary file into a .ko using objcopy(1). * This file will contain two special symbols: filename_start * and filename_end. All we have to do is traverse the KLD * list in search of those symbols and we've found the file * data. As an added bonus, ndiscvt(8) will also generate * a normal .o file which can be linked statically with * the kernel. This means that the symbols will actual reside * in the kernel's symbol table, but that doesn't matter to * us since the kernel appears to us as just another module. */ nc.afilename = afilename; nc.fh = fh; if (linker_file_foreach(NdisCheckModule, &nc)) { *filelength = fh->nf_maplen; *filehandle = fh; *status = NDIS_STATUS_SUCCESS; return; } if (TAILQ_EMPTY(&mountlist)) { ExFreePool(fh); *status = NDIS_STATUS_FILE_NOT_FOUND; printf("NDIS: could not find file %s in linker list\n", afilename); printf("NDIS: and no filesystems mounted yet, " "aborting NdisOpenFile()\n"); free(afilename, M_DEVBUF); return; } path = ExAllocatePoolWithTag(NonPagedPool, MAXPATHLEN, 0); if (path == NULL) { ExFreePool(fh); free(afilename, M_DEVBUF); *status = NDIS_STATUS_RESOURCES; return; } snprintf(path, MAXPATHLEN, "%s/%s", ndis_filepath, afilename); /* Some threads don't have a current working directory. */ pwd_ensure_dirs(); NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td); flags = FREAD; error = vn_open(&nd, &flags, 0, NULL); if (error) { *status = NDIS_STATUS_FILE_NOT_FOUND; ExFreePool(fh); printf("NDIS: open file %s failed: %d\n", path, error); ExFreePool(path); free(afilename, M_DEVBUF); return; } ExFreePool(path); NDFREE(&nd, NDF_ONLY_PNBUF); /* Get the file size. */ VOP_GETATTR(nd.ni_vp, vap, td->td_ucred); VOP_UNLOCK(nd.ni_vp, 0); fh->nf_vp = nd.ni_vp; fh->nf_map = NULL; fh->nf_type = NDIS_FH_TYPE_VFS; *filehandle = fh; *filelength = fh->nf_maplen = vap->va_size & 0xFFFFFFFF; *status = NDIS_STATUS_SUCCESS; } static void NdisMapFile(status, mappedbuffer, filehandle) ndis_status *status; void **mappedbuffer; ndis_handle filehandle; { ndis_fh *fh; struct thread *td = curthread; linker_file_t lf; caddr_t kldstart; int error; ssize_t resid; struct vnode *vp; if (filehandle == NULL) { *status = NDIS_STATUS_FAILURE; return; } fh = (ndis_fh *)filehandle; if (fh->nf_vp == NULL) { *status = NDIS_STATUS_FAILURE; return; } if (fh->nf_map != NULL) { *status = NDIS_STATUS_ALREADY_MAPPED; return; } if (fh->nf_type == NDIS_FH_TYPE_MODULE) { lf = fh->nf_vp; if (ndis_find_sym(lf, fh->nf_name, "_start", &kldstart)) { *status = NDIS_STATUS_FAILURE; return; } fh->nf_map = kldstart; *status = NDIS_STATUS_SUCCESS; *mappedbuffer = fh->nf_map; return; } fh->nf_map = ExAllocatePoolWithTag(NonPagedPool, fh->nf_maplen, 0); if (fh->nf_map == NULL) { *status = NDIS_STATUS_RESOURCES; return; } vp = fh->nf_vp; error = vn_rdwr(UIO_READ, vp, fh->nf_map, fh->nf_maplen, 0, UIO_SYSSPACE, 0, td->td_ucred, NOCRED, &resid, td); if (error) *status = NDIS_STATUS_FAILURE; else { *status = NDIS_STATUS_SUCCESS; *mappedbuffer = fh->nf_map; } } static void NdisUnmapFile(filehandle) ndis_handle filehandle; { ndis_fh *fh; fh = (ndis_fh *)filehandle; if (fh->nf_map == NULL) return; if (fh->nf_type == NDIS_FH_TYPE_VFS) ExFreePool(fh->nf_map); fh->nf_map = NULL; } static void NdisCloseFile(filehandle) ndis_handle filehandle; { struct thread *td = curthread; ndis_fh *fh; struct vnode *vp; if (filehandle == NULL) return; fh = (ndis_fh *)filehandle; if (fh->nf_map != NULL) { if (fh->nf_type == NDIS_FH_TYPE_VFS) ExFreePool(fh->nf_map); fh->nf_map = NULL; } if (fh->nf_vp == NULL) return; if (fh->nf_type == NDIS_FH_TYPE_VFS) { vp = fh->nf_vp; vn_close(vp, FREAD, td->td_ucred, td); } fh->nf_vp = NULL; free(fh->nf_name, M_DEVBUF); ExFreePool(fh); } static uint8_t NdisSystemProcessorCount() { return (mp_ncpus); } static void NdisGetCurrentProcessorCounts(idle_count, kernel_and_user, index) uint32_t *idle_count; uint32_t *kernel_and_user; uint32_t *index; { struct pcpu *pcpu; pcpu = pcpu_find(curthread->td_oncpu); *index = pcpu->pc_cpuid; *idle_count = pcpu->pc_cp_time[CP_IDLE]; *kernel_and_user = pcpu->pc_cp_time[CP_INTR]; } typedef void (*ndis_statusdone_handler)(ndis_handle); typedef void (*ndis_status_handler)(ndis_handle, ndis_status, void *, uint32_t); static void NdisMIndicateStatusComplete(adapter) ndis_handle adapter; { ndis_miniport_block *block; ndis_statusdone_handler statusdonefunc; block = (ndis_miniport_block *)adapter; statusdonefunc = block->nmb_statusdone_func; MSCALL1(statusdonefunc, adapter); } static void NdisMIndicateStatus(adapter, status, sbuf, slen) ndis_handle adapter; ndis_status status; void *sbuf; uint32_t slen; { ndis_miniport_block *block; ndis_status_handler statusfunc; block = (ndis_miniport_block *)adapter; statusfunc = block->nmb_status_func; MSCALL4(statusfunc, adapter, status, sbuf, slen); } /* * The DDK documentation says that you should use IoQueueWorkItem() * instead of ExQueueWorkItem(). The problem is, IoQueueWorkItem() * is fundamentally incompatible with NdisScheduleWorkItem(), which * depends on the API semantics of ExQueueWorkItem(). In our world, * ExQueueWorkItem() is implemented on top of IoAllocateQueueItem() * anyway. * * There are actually three distinct APIs here. NdisScheduleWorkItem() * takes a pointer to an NDIS_WORK_ITEM. ExQueueWorkItem() takes a pointer * to a WORK_QUEUE_ITEM. And finally, IoQueueWorkItem() takes a pointer * to an opaque work item thingie which you get from IoAllocateWorkItem(). * An NDIS_WORK_ITEM is not the same as a WORK_QUEUE_ITEM. However, * the NDIS_WORK_ITEM has some opaque storage at the end of it, and we * (ab)use this storage as a WORK_QUEUE_ITEM, which is what we submit * to ExQueueWorkItem(). * * Got all that? (Sheesh.) */ ndis_status NdisScheduleWorkItem(work) ndis_work_item *work; { work_queue_item *wqi; wqi = (work_queue_item *)work->nwi_wraprsvd; ExInitializeWorkItem(wqi, (work_item_func)work->nwi_func, work->nwi_ctx); ExQueueWorkItem(wqi, WORKQUEUE_DELAYED); return (NDIS_STATUS_SUCCESS); } static void NdisCopyFromPacketToPacket(dpkt, doff, reqlen, spkt, soff, cpylen) ndis_packet *dpkt; uint32_t doff; uint32_t reqlen; ndis_packet *spkt; uint32_t soff; uint32_t *cpylen; { ndis_buffer *src, *dst; char *sptr, *dptr; int resid, copied, len, scnt, dcnt; *cpylen = 0; src = spkt->np_private.npp_head; dst = dpkt->np_private.npp_head; sptr = MmGetMdlVirtualAddress(src); dptr = MmGetMdlVirtualAddress(dst); scnt = MmGetMdlByteCount(src); dcnt = MmGetMdlByteCount(dst); while (soff) { if (MmGetMdlByteCount(src) > soff) { sptr += soff; scnt = MmGetMdlByteCount(src)- soff; break; } soff -= MmGetMdlByteCount(src); src = src->mdl_next; if (src == NULL) return; sptr = MmGetMdlVirtualAddress(src); } while (doff) { if (MmGetMdlByteCount(dst) > doff) { dptr += doff; dcnt = MmGetMdlByteCount(dst) - doff; break; } doff -= MmGetMdlByteCount(dst); dst = dst->mdl_next; if (dst == NULL) return; dptr = MmGetMdlVirtualAddress(dst); } resid = reqlen; copied = 0; while(1) { if (resid < scnt) len = resid; else len = scnt; if (dcnt < len) len = dcnt; bcopy(sptr, dptr, len); copied += len; resid -= len; if (resid == 0) break; dcnt -= len; if (dcnt == 0) { dst = dst->mdl_next; if (dst == NULL) break; dptr = MmGetMdlVirtualAddress(dst); dcnt = MmGetMdlByteCount(dst); } scnt -= len; if (scnt == 0) { src = src->mdl_next; if (src == NULL) break; sptr = MmGetMdlVirtualAddress(src); scnt = MmGetMdlByteCount(src); } } *cpylen = copied; } static void NdisCopyFromPacketToPacketSafe(dpkt, doff, reqlen, spkt, soff, cpylen, prio) ndis_packet *dpkt; uint32_t doff; uint32_t reqlen; ndis_packet *spkt; uint32_t soff; uint32_t *cpylen; uint32_t prio; { NdisCopyFromPacketToPacket(dpkt, doff, reqlen, spkt, soff, cpylen); } static void NdisIMCopySendPerPacketInfo(dpkt, spkt) ndis_packet *dpkt; ndis_packet *spkt; { memcpy(&dpkt->np_ext, &spkt->np_ext, sizeof(ndis_packet_extension)); } static ndis_status NdisMRegisterDevice(handle, devname, symname, majorfuncs, devobj, devhandle) ndis_handle handle; unicode_string *devname; unicode_string *symname; driver_dispatch *majorfuncs[]; void **devobj; ndis_handle *devhandle; { uint32_t status; device_object *dobj; status = IoCreateDevice(handle, 0, devname, FILE_DEVICE_UNKNOWN, 0, FALSE, &dobj); if (status == STATUS_SUCCESS) { *devobj = dobj; *devhandle = dobj; } return (status); } static ndis_status NdisMDeregisterDevice(handle) ndis_handle handle; { IoDeleteDevice(handle); return (NDIS_STATUS_SUCCESS); } static ndis_status NdisMQueryAdapterInstanceName(name, handle) unicode_string *name; ndis_handle handle; { ndis_miniport_block *block; device_t dev; ansi_string as; block = (ndis_miniport_block *)handle; dev = block->nmb_physdeviceobj->do_devext; RtlInitAnsiString(&as, __DECONST(char *, device_get_nameunit(dev))); if (RtlAnsiStringToUnicodeString(name, &as, TRUE)) return (NDIS_STATUS_RESOURCES); return (NDIS_STATUS_SUCCESS); } static void NdisMRegisterUnloadHandler(handle, func) ndis_handle handle; void *func; { } static void dummy() { printf("NDIS dummy called...\n"); } /* * Note: a couple of entries in this table specify the * number of arguments as "foo + 1". These are routines * that accept a 64-bit argument, passed by value. On * x86, these arguments consume two longwords on the stack, * so we lie and say there's one additional argument so * that the wrapping routines will do the right thing. */ image_patch_table ndis_functbl[] = { IMPORT_SFUNC(NdisCopyFromPacketToPacket, 6), IMPORT_SFUNC(NdisCopyFromPacketToPacketSafe, 7), IMPORT_SFUNC(NdisIMCopySendPerPacketInfo, 2), IMPORT_SFUNC(NdisScheduleWorkItem, 1), IMPORT_SFUNC(NdisMIndicateStatusComplete, 1), IMPORT_SFUNC(NdisMIndicateStatus, 4), IMPORT_SFUNC(NdisSystemProcessorCount, 0), IMPORT_SFUNC(NdisGetCurrentProcessorCounts, 3), IMPORT_SFUNC(NdisUnchainBufferAtBack, 2), IMPORT_SFUNC(NdisGetFirstBufferFromPacket, 5), IMPORT_SFUNC(NdisGetFirstBufferFromPacketSafe, 6), IMPORT_SFUNC(NdisGetBufferPhysicalArraySize, 2), IMPORT_SFUNC(NdisMGetDeviceProperty, 6), IMPORT_SFUNC(NdisInitAnsiString, 2), IMPORT_SFUNC(NdisInitUnicodeString, 2), IMPORT_SFUNC(NdisWriteConfiguration, 4), IMPORT_SFUNC(NdisAnsiStringToUnicodeString, 2), IMPORT_SFUNC(NdisTerminateWrapper, 2), IMPORT_SFUNC(NdisOpenConfigurationKeyByName, 4), IMPORT_SFUNC(NdisOpenConfigurationKeyByIndex, 5), IMPORT_SFUNC(NdisMRemoveMiniport, 1), IMPORT_SFUNC(NdisInitializeString, 2), IMPORT_SFUNC(NdisFreeString, 1), IMPORT_SFUNC(NdisGetCurrentSystemTime, 1), IMPORT_SFUNC(NdisGetRoutineAddress, 1), IMPORT_SFUNC(NdisGetSystemUpTime, 1), IMPORT_SFUNC(NdisGetVersion, 0), IMPORT_SFUNC(NdisMSynchronizeWithInterrupt, 3), IMPORT_SFUNC(NdisMAllocateSharedMemoryAsync, 4), IMPORT_SFUNC(NdisInterlockedInsertHeadList, 3), IMPORT_SFUNC(NdisInterlockedInsertTailList, 3), IMPORT_SFUNC(NdisInterlockedRemoveHeadList, 2), IMPORT_SFUNC(NdisInitializeWrapper, 4), IMPORT_SFUNC(NdisMRegisterMiniport, 3), IMPORT_SFUNC(NdisAllocateMemoryWithTag, 3), IMPORT_SFUNC(NdisAllocateMemory, 4 + 1), IMPORT_SFUNC(NdisMSetAttributesEx, 5), IMPORT_SFUNC(NdisCloseConfiguration, 1), IMPORT_SFUNC(NdisReadConfiguration, 5), IMPORT_SFUNC(NdisOpenConfiguration, 3), IMPORT_SFUNC(NdisAcquireSpinLock, 1), IMPORT_SFUNC(NdisReleaseSpinLock, 1), IMPORT_SFUNC(NdisDprAcquireSpinLock, 1), IMPORT_SFUNC(NdisDprReleaseSpinLock, 1), IMPORT_SFUNC(NdisAllocateSpinLock, 1), IMPORT_SFUNC(NdisInitializeReadWriteLock, 1), IMPORT_SFUNC(NdisAcquireReadWriteLock, 3), IMPORT_SFUNC(NdisReleaseReadWriteLock, 2), IMPORT_SFUNC(NdisFreeSpinLock, 1), IMPORT_SFUNC(NdisFreeMemory, 3), IMPORT_SFUNC(NdisReadPciSlotInformation, 5), IMPORT_SFUNC(NdisWritePciSlotInformation, 5), IMPORT_SFUNC_MAP(NdisImmediateReadPciSlotInformation, NdisReadPciSlotInformation, 5), IMPORT_SFUNC_MAP(NdisImmediateWritePciSlotInformation, NdisWritePciSlotInformation, 5), IMPORT_CFUNC(NdisWriteErrorLogEntry, 0), IMPORT_SFUNC(NdisMStartBufferPhysicalMapping, 6), IMPORT_SFUNC(NdisMCompleteBufferPhysicalMapping, 3), IMPORT_SFUNC(NdisMInitializeTimer, 4), IMPORT_SFUNC(NdisInitializeTimer, 3), IMPORT_SFUNC(NdisSetTimer, 2), IMPORT_SFUNC(NdisMCancelTimer, 2), IMPORT_SFUNC_MAP(NdisCancelTimer, NdisMCancelTimer, 2), IMPORT_SFUNC(NdisMSetPeriodicTimer, 2), IMPORT_SFUNC(NdisMQueryAdapterResources, 4), IMPORT_SFUNC(NdisMRegisterIoPortRange, 4), IMPORT_SFUNC(NdisMDeregisterIoPortRange, 4), IMPORT_SFUNC(NdisReadNetworkAddress, 4), IMPORT_SFUNC(NdisQueryMapRegisterCount, 2), IMPORT_SFUNC(NdisMAllocateMapRegisters, 5), IMPORT_SFUNC(NdisMFreeMapRegisters, 1), IMPORT_SFUNC(NdisMAllocateSharedMemory, 5), IMPORT_SFUNC(NdisMMapIoSpace, 4 + 1), IMPORT_SFUNC(NdisMUnmapIoSpace, 3), IMPORT_SFUNC(NdisGetCacheFillSize, 0), IMPORT_SFUNC(NdisMGetDmaAlignment, 1), IMPORT_SFUNC(NdisMInitializeScatterGatherDma, 3), IMPORT_SFUNC(NdisAllocatePacketPool, 4), IMPORT_SFUNC(NdisAllocatePacketPoolEx, 5), IMPORT_SFUNC(NdisAllocatePacket, 3), IMPORT_SFUNC(NdisFreePacket, 1), IMPORT_SFUNC(NdisFreePacketPool, 1), IMPORT_SFUNC_MAP(NdisDprAllocatePacket, NdisAllocatePacket, 3), IMPORT_SFUNC_MAP(NdisDprFreePacket, NdisFreePacket, 1), IMPORT_SFUNC(NdisAllocateBufferPool, 3), IMPORT_SFUNC(NdisAllocateBuffer, 5), IMPORT_SFUNC(NdisQueryBuffer, 3), IMPORT_SFUNC(NdisQueryBufferSafe, 4), IMPORT_SFUNC(NdisBufferVirtualAddress, 1), IMPORT_SFUNC(NdisBufferVirtualAddressSafe, 2), IMPORT_SFUNC(NdisBufferLength, 1), IMPORT_SFUNC(NdisFreeBuffer, 1), IMPORT_SFUNC(NdisFreeBufferPool, 1), IMPORT_SFUNC(NdisInterlockedIncrement, 1), IMPORT_SFUNC(NdisInterlockedDecrement, 1), IMPORT_SFUNC(NdisInitializeEvent, 1), IMPORT_SFUNC(NdisSetEvent, 1), IMPORT_SFUNC(NdisResetEvent, 1), IMPORT_SFUNC(NdisWaitEvent, 2), IMPORT_SFUNC(NdisUnicodeStringToAnsiString, 2), IMPORT_SFUNC(NdisMPciAssignResources, 3), IMPORT_SFUNC(NdisMFreeSharedMemory, 5 + 1), IMPORT_SFUNC(NdisMRegisterInterrupt, 7), IMPORT_SFUNC(NdisMDeregisterInterrupt, 1), IMPORT_SFUNC(NdisMRegisterAdapterShutdownHandler, 3), IMPORT_SFUNC(NdisMDeregisterAdapterShutdownHandler, 1), IMPORT_SFUNC(NDIS_BUFFER_TO_SPAN_PAGES, 1), IMPORT_SFUNC(NdisQueryBufferOffset, 3), IMPORT_SFUNC(NdisAdjustBufferLength, 2), IMPORT_SFUNC(NdisPacketPoolUsage, 1), IMPORT_SFUNC(NdisMSleep, 1), IMPORT_SFUNC(NdisUnchainBufferAtFront, 2), IMPORT_SFUNC(NdisReadPcmciaAttributeMemory, 4), IMPORT_SFUNC(NdisWritePcmciaAttributeMemory, 4), IMPORT_SFUNC(NdisOpenFile, 5 + 1), IMPORT_SFUNC(NdisMapFile, 3), IMPORT_SFUNC(NdisUnmapFile, 1), IMPORT_SFUNC(NdisCloseFile, 1), IMPORT_SFUNC(NdisMRegisterDevice, 6), IMPORT_SFUNC(NdisMDeregisterDevice, 1), IMPORT_SFUNC(NdisMQueryAdapterInstanceName, 2), IMPORT_SFUNC(NdisMRegisterUnloadHandler, 2), IMPORT_SFUNC(ndis_timercall, 4), IMPORT_SFUNC(ndis_asyncmem_complete, 2), IMPORT_SFUNC(ndis_intr, 2), IMPORT_SFUNC(ndis_intrhand, 4), /* * This last entry is a catch-all for any function we haven't * implemented yet. The PE import list patching routine will * use it for any function that doesn't have an explicit match * in this table. */ { NULL, (FUNC)dummy, NULL, 0, WINDRV_WRAP_STDCALL }, /* End of list. */ { NULL, NULL, NULL } }; Index: stable/11/sys/dev/if_ndis/if_ndis.c =================================================================== --- stable/11/sys/dev/if_ndis/if_ndis.c (revision 319725) +++ stable/11/sys/dev/if_ndis/if_ndis.c (revision 319726) @@ -1,3415 +1,3424 @@ /*- * 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 #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 SYSCTL_DECL(_hw_ndisusb); int ndisusb_halt = 1; SYSCTL_INT(_hw_ndisusb, OID_AUTO, halt, CTLFLAG_RW, &ndisusb_halt, 0, "Halt NDIS USB driver when it's attached"); /* 0 - 30 dBm to mW conversion table */ static const uint16_t dBm2mW[] = { 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 6, 6, 7, 8, 9, 10, 11, 13, 14, 16, 18, 20, 22, 25, 28, 32, 35, 40, 45, 50, 56, 63, 71, 79, 89, 100, 112, 126, 141, 158, 178, 200, 224, 251, 282, 316, 355, 398, 447, 501, 562, 631, 708, 794, 891, 1000 }; 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_ifstarttask_wrap; static funcptr ndis_resettask_wrap; static funcptr ndis_inputtask_wrap; static struct ieee80211vap *ndis_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [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 ieee80211com *); static void ndis_update_promisc (struct ieee80211com *); -static void ndis_start (struct ifnet *); -static void ndis_starttask (device_object *, void *); +static void ndis_ifstart (struct ifnet *); +static void ndis_ifstarttask (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_ifioctl (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 *); 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 int ndis_get_bssid_list (struct ndis_softc *, ndis_80211_bssid_list_ex **); 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 void ndis_media_status (struct ifnet *, struct ifmediareq *); 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 *); 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 ndis_ifattach(struct ndis_softc *); static int ndis_80211attach(struct ndis_softc *); static int ndis_80211ioctl(struct ieee80211com *, u_long , void *); static int ndis_80211transmit(struct ieee80211com *, struct mbuf *); static void ndis_80211parent(struct ieee80211com *); 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, + windrv_wrap((funcptr)ndis_ifstarttask, &ndis_ifstarttask_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_ifstarttask_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 (sc->ndis_80211) + return; + + ifp = sc->ifp; 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_maddr_rlock(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_maddr_runlock(ifp); sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; sc->ndis_filter &= ~NDIS_PACKET_TYPE_MULTICAST; goto out; } } if_maddr_runlock(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); } 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); + if (sc->ndis_80211) + return (EINVAL); /* See if there's anything to set. */ + ifp = sc->ifp; 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(device_t dev) { struct ndis_softc *sc; driver_object *pdrv; device_object *pdo; int error = 0, len; 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); KeInitializeSpinLock(&sc->ndisusb_tasklock); KeInitializeSpinLock(&sc->ndisusb_xferdonelock); InitializeListHead(&sc->ndis_shlist); InitializeListHead(&sc->ndisusb_tasklist); InitializeListHead(&sc->ndisusb_xferdonelist); callout_init(&sc->ndis_stat_callout, 1); mbufq_init(&sc->ndis_rxqueue, INT_MAX); /* XXXGL: sane maximum */ 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); sc->ndisusb_xferdoneitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); sc->ndisusb_taskitem = 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; } /* * 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 = 1; break; } if (sc->ndis_80211) error = ndis_80211attach(sc); else error = ndis_ifattach(sc); fail: if (error) { ndis_detach(dev); return (error); } if (sc->ndis_iftype == PNPBus && ndisusb_halt == 0) return (error); DPRINTF(("attach done.\n")); /* We're done talking to the NIC for now; halt it. */ ndis_halt_nic(sc); DPRINTF(("halting done.\n")); return (error); } static int ndis_80211attach(struct ndis_softc *sc) { struct ieee80211com *ic = &sc->ndis_ic; ndis_80211_rates_ex rates; struct ndis_80211_nettype_list *ntl; uint32_t arg; int mode, i, r, len, nonettypes = 1; uint8_t bands[IEEE80211_MODE_BYTES] = { 0 }; callout_init(&sc->ndis_scan_callout, 1); ic->ic_softc = sc; ic->ic_ioctl = ndis_80211ioctl; ic->ic_name = device_get_nameunit(sc->ndis_dev); ic->ic_opmode = IEEE80211_M_STA; ic->ic_phytype = IEEE80211_T_DS; ic->ic_caps = IEEE80211_C_8023ENCAP | 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_WAITOK | 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) { nonettypes = 0; setbit(ic->ic_modecaps, mode); setbit(bands, mode); } else device_printf(sc->ndis_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 (nonettypes) { 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 != 0) device_printf(sc->ndis_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, 48); 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, 48); TESTSETRATE(IEEE80211_MODE_11A, 72); TESTSETRATE(IEEE80211_MODE_11A, 96); TESTSETRATE(IEEE80211_MODE_11A, 108); } #undef SETRATE #undef INCRATE #undef TESTSETRATE 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; r = ndis_get_info(sc, OID_802_11_TX_POWER_LEVEL, &arg, &i); if (r == 0) ic->ic_caps |= IEEE80211_C_TXPMGT; /* * Get station address from the driver. */ len = sizeof(ic->ic_macaddr); ndis_get_info(sc, OID_802_3_CURRENT_ADDRESS, &ic->ic_macaddr, &len); 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_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; ic->ic_transmit = ndis_80211transmit; ic->ic_parent = ndis_80211parent; if (bootverbose) ieee80211_announce(ic); return (0); } static int ndis_ifattach(struct ndis_softc *sc) { struct ifnet *ifp; u_char eaddr[ETHER_ADDR_LEN]; int len; ifp = if_alloc(IFT_ETHER); if (ifp == NULL) return (ENOSPC); sc->ifp = ifp; ifp->if_softc = sc; /* Check for task offload support. */ ndis_probe_offload(sc); /* * Get station address from the driver. */ len = sizeof(eaddr); ndis_get_info(sc, OID_802_3_CURRENT_ADDRESS, eaddr, &len); if_initname(ifp, device_get_name(sc->ndis_dev), device_get_unit(sc->ndis_dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_ioctl = ndis_ioctl; - ifp->if_start = ndis_start; + ifp->if_ioctl = ndis_ifioctl; + ifp->if_start = ndis_ifstart; 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; 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); return (0); } static struct ieee80211vap * ndis_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode 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 = malloc(sizeof(struct ndis_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &nvp->vap; ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); /* override with driver methods */ nvp->newstate = vap->iv_newstate; vap->iv_newstate = ndis_newstate; /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ndis_media_status, mac); 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); struct ieee80211com *ic = vap->iv_ic; struct ndis_softc *sc = ic->ic_softc; ndis_stop(sc); callout_drain(&sc->ndis_scan_callout); 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(device_t dev) { struct ifnet *ifp; struct ndis_softc *sc; driver_object *drv; sc = device_get_softc(dev); NDIS_LOCK(sc); if (!sc->ndis_80211) ifp = sc->ifp; else ifp = NULL; if (ifp != NULL) ifp->if_flags &= ~IFF_UP; if (device_is_attached(dev)) { NDIS_UNLOCK(sc); ndis_stop(sc); if (sc->ndis_80211) ieee80211_ifdetach(&sc->ndis_ic); else if (ifp != NULL) ether_ifdetach(ifp); } else NDIS_UNLOCK(sc); 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); if (sc->ndisusb_xferdoneitem != NULL) IoFreeWorkItem(sc->ndisusb_xferdoneitem); if (sc->ndisusb_taskitem != NULL) IoFreeWorkItem(sc->ndisusb_taskitem); 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); 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_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) 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); } /* * 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); } /* * 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); mbufq_enqueue(&sc->ndis_rxqueue, m); KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); IoQueueWorkItem(sc->ndis_inputitem, (io_workitem_func)ndis_inputtask_wrap, WORKQUEUE_CRITICAL, sc); } if (status == NDIS_STATUS_FAILURE) m_freem(m); /* Advance to next packet */ l = block->nmb_packetlist.nle_flink; } KeReleaseSpinLockFromDpcLevel(&block->nmb_lock); } /* * 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); mbufq_enqueue(&sc->ndis_rxqueue, m); KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); IoQueueWorkItem(sc->ndis_inputitem, (io_workitem_func)ndis_inputtask_wrap, WORKQUEUE_CRITICAL, sc); } /* * 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 possession 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 (!sc->ndis_running) { for (i = 0; i < pktcnt; i++) { p = packets[i]; if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS) { p->np_refcnt++; (void)ndis_return_packet(NULL ,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) (void)ndis_return_packet(NULL, p, block); } else { #ifdef notdef if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) { m = m_dup(m0, M_NOWAIT); /* * 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) if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); else m0 = m; } else p->np_oob.npo_status = NDIS_STATUS_PENDING; #endif m = m_dup(m0, M_NOWAIT); 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) { if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 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); mbufq_enqueue(&sc->ndis_rxqueue, m0); KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); IoQueueWorkItem(sc->ndis_inputitem, (io_workitem_func)ndis_inputtask_wrap, WORKQUEUE_CRITICAL, sc); } } } /* * 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(device_object *dobj, void *arg) { ndis_miniport_block *block; struct ndis_softc *sc = arg; struct mbuf *m; uint8_t irql; block = dobj->do_devext; KeAcquireSpinLock(&sc->ndis_rxlock, &irql); while ((m = mbufq_dequeue(&sc->ndis_rxqueue)) != NULL) { KeReleaseSpinLock(&sc->ndis_rxlock, irql); if ((sc->ndis_80211 != 0)) { struct ieee80211com *ic = &sc->ndis_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (vap != NULL) vap->iv_deliver_data(vap, vap->iv_bss, m); } else { struct ifnet *ifp = sc->ifp; (*ifp->if_input)(ifp, m); } KeAcquireSpinLock(&sc->ndis_rxlock, &irql); } KeReleaseSpinLock(&sc->ndis_rxlock, irql); } /* * 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) - if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); - else - if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); - + if (!sc->ndis_80211) { + struct ifnet *ifp = sc->ifp; + if (status == NDIS_STATUS_SUCCESS) + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + else + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + } 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); + if (!sc->ndis_80211) + IoQueueWorkItem(sc->ndis_startitem, + (io_workitem_func)ndis_ifstarttask_wrap, + WORKQUEUE_CRITICAL, sc); + DPRINTF(("%s: ndis_ifstarttask_wrap sc=%p\n", __func__, sc)); } 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); } 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); + if (!sc->ndis_80211) + IoQueueWorkItem(sc->ndis_startitem, + (io_workitem_func)ndis_ifstarttask_wrap, + WORKQUEUE_CRITICAL, sc); 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; } } 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) { if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1); 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); + if (!sc->ndis_80211) + IoQueueWorkItem(sc->ndis_startitem, + (io_workitem_func)ndis_ifstarttask_wrap, + WORKQUEUE_CRITICAL, sc); } callout_reset(&sc->ndis_stat_callout, hz, ndis_tick, sc); } static void ndis_ticktask(device_object *d, void *xsc) { struct ndis_softc *sc = xsc; ndis_checkforhang_handler hangfunc; uint8_t rval; 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; if (sc->ndis_80211 != 0) { struct ieee80211com *ic = &sc->ndis_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (vap != NULL) { NDIS_UNLOCK(sc); ndis_getstate_80211(sc); ieee80211_new_state(vap, IEEE80211_S_RUN, -1); NDIS_LOCK(sc); if_link_state_change(vap->iv_ifp, LINK_STATE_UP); } } else 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; if (sc->ndis_80211 != 0) { struct ieee80211com *ic = &sc->ndis_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (vap != NULL) { NDIS_UNLOCK(sc); ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); NDIS_LOCK(sc); if_link_state_change(vap->iv_ifp, LINK_STATE_DOWN); } } else if_link_state_change(sc->ifp, LINK_STATE_DOWN); } NDIS_UNLOCK(sc); } 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; } } 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 ieee80211com *ic) { struct ndis_softc *sc = ic->ic_softc; ndis_setmulti(sc); } static void ndis_update_promisc(struct ieee80211com *ic) { /* not supported */ } static void -ndis_starttask(d, arg) - device_object *d; - void *arg; +ndis_ifstarttask(device_object *d, void *arg) { - struct ifnet *ifp; + struct ndis_softc *sc = arg; + DPRINTF(("%s: sc=%p, ifp=%p\n", __func__, sc, sc->ifp)); + if (sc->ndis_80211) + return; - ifp = arg; - + struct ifnet *ifp = sc->ifp; if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) - ndis_start(ifp); + ndis_ifstart(ifp); } /* * 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; +ndis_ifstart(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 int ndis_80211transmit(struct ieee80211com *ic, struct mbuf *m) { struct ndis_softc *sc = ic->ic_softc; ndis_packet **p0 = NULL, *p = NULL; int status; NDIS_LOCK(sc); if (!sc->ndis_link || !sc->ndis_running) { NDIS_UNLOCK(sc); return (ENXIO); } if (sc->ndis_txpending == 0) { NDIS_UNLOCK(sc); return (ENOBUFS); } p0 = &sc->ndis_txarray[sc->ndis_txidx]; NdisAllocatePacket(&status, &sc->ndis_txarray[sc->ndis_txidx], sc->ndis_txpool); if (status != NDIS_STATUS_SUCCESS) { NDIS_UNLOCK(sc); return (ENOBUFS); } if (ndis_mtop(m, &sc->ndis_txarray[sc->ndis_txidx])) { NDIS_UNLOCK(sc); return (ENOBUFS); } /* * 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; } NDIS_INC(sc); sc->ndis_txpending--; /* * 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, 1); else ndis_send_packet(sc, p); return (0); } static void ndis_80211parent(struct ieee80211com *ic) { struct ndis_softc *sc = ic->ic_softc; /*NDIS_LOCK(sc);*/ if (ic->ic_nrunning > 0) { if (!sc->ndis_running) ndis_init(sc); } else if (sc->ndis_running) ndis_stop(sc); /*NDIS_UNLOCK(sc);*/ } static void ndis_init(void *xsc) { struct ndis_softc *sc = xsc; 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 (!(sc->ndis_iftype == PNPBus && ndisusb_halt == 0)) { error = ndis_init_nic(sc); if (error != 0) { device_printf(sc->ndis_dev, "failed to initialize the device: %d\n", error); return; } } /* Program the packet filter */ sc->ndis_filter = NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_BROADCAST; if (sc->ndis_80211) { struct ieee80211com *ic = &sc->ndis_ic; if (ic->ic_promisc > 0) sc->ndis_filter |= NDIS_PACKET_TYPE_PROMISCUOUS; } else { struct ifnet *ifp = sc->ifp; 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. */ if (sc->ndis_80211) i = ETHERMTU; else i = sc->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); NDIS_LOCK(sc); sc->ndis_txidx = 0; sc->ndis_txpending = sc->ndis_maxpkts; sc->ndis_link = 0; if (!sc->ndis_80211) { if_link_state_change(sc->ifp, LINK_STATE_UNKNOWN); sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->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); sc->ndis_running = 1; NDIS_UNLOCK(sc); /* XXX force handling */ if (sc->ndis_80211) ieee80211_start_all(&sc->ndis_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 len; ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; sc = ifp->if_softc; if (!NDIS_INITIALIZED(sc)) return; len = sizeof(linkstate); ndis_get_info(sc, OID_GEN_MEDIA_CONNECT_STATUS, (void *)&linkstate, &len); len = sizeof(media_info); 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; } } static int ndis_set_cipher(struct ndis_softc *sc, int cipher) { struct ieee80211com *ic = &sc->ndis_ic; int rval = 0, len; uint32_t arg, save; len = sizeof(arg); if (cipher == WPA_CSE_WEP40 || cipher == 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_media_status(struct ifnet *ifp, struct ifmediareq *imr) { struct ieee80211vap *vap = ifp->if_softc; struct ndis_softc *sc = vap->iv_ic->ic_softc; uint32_t txrate; int len; if (!NDIS_INITIALIZED(sc)) return; len = sizeof(txrate); if (ndis_get_info(sc, OID_GEN_LINK_SPEED, &txrate, &len) == 0) vap->iv_bss->ni_txrate = txrate / 5000; ieee80211_media_status(ifp, imr); } static void ndis_setstate_80211(struct ndis_softc *sc) { struct ieee80211com *ic = &sc->ndis_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); ndis_80211_macaddr bssid; ndis_80211_config config; int rval = 0, len; uint32_t arg; 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 (vap->iv_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); /* Set TX power */ if ((ic->ic_caps & IEEE80211_C_TXPMGT) && ic->ic_txpowlimit < nitems(dBm2mW)) { arg = dBm2mW[ic->ic_txpowlimit]; len = sizeof(arg); ndis_set_info(sc, OID_802_11_TX_POWER_LEVEL, &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_802_11_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(vap->iv_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_and_assoc(struct ndis_softc *sc, struct ieee80211vap *vap) { struct ieee80211_node *ni = vap->iv_bss; ndis_80211_ssid ssid; ndis_80211_macaddr bssid; ndis_80211_wep wep; int i, rval = 0, len, error; uint32_t arg; 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 * 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(ieee80211broadcastaddr, 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); return; } static int ndis_get_bssid_list(sc, bl) struct ndis_softc *sc; ndis_80211_bssid_list_ex **bl; { int len, error; len = sizeof(uint32_t) + (sizeof(ndis_wlan_bssid_ex) * 16); *bl = malloc(len, M_DEVBUF, 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_DEVBUF); *bl = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO); if (*bl == NULL) return (ENOMEM); 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 (error); } return (0); } static int ndis_get_assoc(struct ndis_softc *sc, ndis_wlan_bssid_ex **assoc) { struct ieee80211com *ic = &sc->ndis_ic; 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; error = ndis_get_bssid_list(sc, &bl); if (error) 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(struct ndis_softc *sc) { struct ieee80211com *ic = &sc->ndis_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni = vap->iv_bss; ndis_wlan_bssid_ex *bs; int rval, len, i = 0; int chanflag; uint32_t arg; 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; 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) vap->iv_flags &= ~IEEE80211_F_PMGTON; else vap->iv_flags |= IEEE80211_F_PMGTON; } /* Get TX power */ if (ic->ic_caps & IEEE80211_C_TXPMGT) { len = sizeof(arg); ndis_get_info(sc, OID_802_11_TX_POWER_LEVEL, &arg, &len); for (i = 0; i < nitems(dBm2mW); i++) if (dBm2mW[i] >= arg) break; ic->ic_txpowlimit = i; } /* * 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. */ 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 { vap->iv_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; vap->iv_flags |= IEEE80211_F_WPA1; break; case NDIS_80211_AUTHMODE_WPA2: case NDIS_80211_AUTHMODE_WPA2PSK: ni->ni_authmode = IEEE80211_AUTH_WPA; vap->iv_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) vap->iv_flags |= IEEE80211_F_PRIVACY|IEEE80211_F_DROPUNENC; else vap->iv_flags &= ~(IEEE80211_F_PRIVACY|IEEE80211_F_DROPUNENC); } static int -ndis_ioctl(ifp, command, data) +ndis_ifioctl(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 (sc->ndis_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 (sc->ndis_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 (sc->ndis_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_80211ioctl(struct ieee80211com *ic, u_long cmd, void *data) { struct ndis_softc *sc = ic->ic_softc; struct ifreq *ifr = data; struct ndis_oid_data oid; struct ndis_evt evt; void *oidbuf = NULL; int error = 0; if ((error = priv_check(curthread, PRIV_DRIVER)) != 0) return (error); switch (cmd) { case SIOCGDRVSPEC: case SIOCSDRVSPEC: error = copyin(ifr->ifr_data, &oid, sizeof(oid)); if (error) break; oidbuf = malloc(oid.len, M_TEMP, M_WAITOK | M_ZERO); error = copyin(ifr->ifr_data + sizeof(oid), oidbuf, oid.len); } if (error) { free(oidbuf, M_TEMP); return (error); } switch (cmd) { case SIOCGDRVSPEC: error = ndis_get_info(sc, oid.oid, oidbuf, &oid.len); break; case SIOCSDRVSPEC: error = ndis_set_info(sc, oid.oid, oidbuf, &oid.len); break; case SIOCGPRIVATE_0: 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; default: error = ENOTTY; break; } switch (cmd) { case SIOCGDRVSPEC: case SIOCSDRVSPEC: error = copyout(&oid, ifr->ifr_data, sizeof(oid)); if (error) break; error = copyout(oidbuf, ifr->ifr_data + sizeof(oid), oid.len); } free(oidbuf, M_TEMP); return (error); } int ndis_del_key(struct ieee80211vap *vap, const struct ieee80211_key *key) { struct ndis_softc *sc = vap->iv_ic->ic_softc; ndis_80211_key rkey; int len, error = 0; 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(struct ieee80211vap *vap, const struct ieee80211_key *key) { struct ndis_softc *sc = vap->iv_ic->ic_softc; ndis_80211_key rkey; int len, error = 0; 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(ieee80211broadcastaddr, 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); } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void ndis_stop(struct ndis_softc *sc) { int i; callout_drain(&sc->ndis_stat_callout); NDIS_LOCK(sc); sc->ndis_tx_timer = 0; sc->ndis_link = 0; if (!sc->ndis_80211) sc->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->ndis_running = 0; NDIS_UNLOCK(sc); if (sc->ndis_iftype != PNPBus || (sc->ndis_iftype == PNPBus && !(sc->ndisusb_status & NDISUSB_STATUS_DETACH) && ndisusb_halt != 0)) 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_buf = NULL; } 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); } /* * 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); } 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 ndis_softc *sc = ic->ic_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) { IEEE80211_UNLOCK(ic); ndis_auth_and_assoc(sc, vap); IEEE80211_LOCK(ic); } break; case IEEE80211_S_AUTH: 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) { struct ieee80211vap *vap = arg; ieee80211_scan_done(vap); } static void ndis_scan_results(struct ndis_softc *sc) { struct ieee80211com *ic = &sc->ndis_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 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 rssi, noise, freq, chanflag; uint8_t ssid[2+IEEE80211_NWID_LEN]; uint8_t rates[2+IEEE80211_RATE_MAXSIZE]; uint8_t *frm, *efrm; saved_chan = ic->ic_curchan; noise = -96; if (ndis_get_bssid_list(sc, &bl)) return; DPRINTF(("%s: %d results\n", __func__, bl->nblx_items)); 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; sp.ies = frm; sp.ies_len = efrm - frm; } 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, ic->ic_curchan, &sp, &wh, 0, rssi, noise); 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 ndis_softc *sc = ic->ic_softc; struct ieee80211vap *vap; struct ieee80211_scan_state *ss; ndis_80211_ssid ssid; int error, len; 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; } /* Set a timer to collect the results */ callout_reset(&sc->ndis_scan_callout, hz * 3, ndis_scan, vap); } 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) { struct ndis_softc *sc = ic->ic_softc; ndis_scan_results(sc); } Index: stable/11/sys/dev/if_ndis/if_ndisvar.h =================================================================== --- stable/11/sys/dev/if_ndis/if_ndisvar.h (revision 319725) +++ stable/11/sys/dev/if_ndis/if_ndisvar.h (revision 319726) @@ -1,260 +1,261 @@ /*- * 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_usb_type { uint16_t ndis_vid; uint16_t 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)) #define NDISUSB_CONFIG_NO 0 #define NDISUSB_IFACE_INDEX 0 /* XXX at USB2 there's no USBD_NO_TIMEOUT macro anymore */ #define NDISUSB_NO_TIMEOUT 0 #define NDISUSB_INTR_TIMEOUT 1000 #define NDISUSB_TX_TIMEOUT 10000 struct ndisusb_xfer; struct ndisusb_ep { struct usb_xfer *ne_xfer[1]; list_entry ne_active; list_entry ne_pending; kspin_lock ne_lock; uint8_t ne_dirin; }; struct ndisusb_xfer { struct ndisusb_ep *nx_ep; void *nx_priv; uint8_t *nx_urbbuf; uint32_t nx_urbactlen; uint32_t nx_urblen; uint8_t nx_shortxfer; list_entry nx_next; }; struct ndisusb_xferdone { struct ndisusb_xfer *nd_xfer; usb_error_t nd_status; list_entry nd_donelist; }; struct ndisusb_task { unsigned nt_type; #define NDISUSB_TASK_TSTART 0 #define NDISUSB_TASK_IRPCANCEL 1 #define NDISUSB_TASK_VENDOR 2 void *nt_ctx; list_entry nt_tasklist; }; struct ndis_softc { +#define NDISUSB_GET_IFNET(ndis_softc) ( (ndis_softc)->ndis_80211 ? NULL : (ndis_softc)->ifp ) u_int ndis_80211:1, ndis_link:1, ndis_running:1; union { struct { /* Ethernet */ struct ifnet *ifp; struct ifmedia ifmedia; int ndis_if_flags; }; struct { /* Wireless */ struct ieee80211com ndis_ic; struct callout ndis_scan_callout; int (*ndis_newstate)(struct ieee80211com *, enum ieee80211_state, int); }; }; 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; uint32_t ndis_sts; uint32_t ndis_filter; 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 mbufq ndis_rxqueue; kspin_lock ndis_rxlock; int ndis_tx_timer; int ndis_hang_timer; struct usb_device *ndisusb_dev; struct mtx ndisusb_mtx; struct ndisusb_ep ndisusb_dread_ep; struct ndisusb_ep ndisusb_dwrite_ep; #define NDISUSB_GET_ENDPT(addr) \ ((UE_GET_DIR(addr) >> 7) | (UE_GET_ADDR(addr) << 1)) #define NDISUSB_ENDPT_MAX ((UE_ADDR + 1) * 2) struct ndisusb_ep ndisusb_ep[NDISUSB_ENDPT_MAX]; io_workitem *ndisusb_xferdoneitem; list_entry ndisusb_xferdonelist; kspin_lock ndisusb_xferdonelock; io_workitem *ndisusb_taskitem; list_entry ndisusb_tasklist; kspin_lock ndisusb_tasklock; int ndisusb_status; #define NDISUSB_STATUS_DETACH 0x1 #define NDISUSB_STATUS_SETUP_EP 0x2 }; #define NDIS_LOCK(_sc) mtx_lock(&(_sc)->ndis_mtx) #define NDIS_UNLOCK(_sc) mtx_unlock(&(_sc)->ndis_mtx) #define NDIS_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->ndis_mtx, t) #define NDISUSB_LOCK(_sc) mtx_lock(&(_sc)->ndisusb_mtx) #define NDISUSB_UNLOCK(_sc) mtx_unlock(&(_sc)->ndisusb_mtx) #define NDISUSB_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->ndisusb_mtx, t) Index: stable/11 =================================================================== --- stable/11 (revision 319725) +++ stable/11 (revision 319726) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r318677