Index: stable/5/sys/compat/ndis/kern_ndis.c =================================================================== --- stable/5/sys/compat/ndis/kern_ndis.c (revision 151520) +++ stable/5/sys/compat/ndis/kern_ndis.c (revision 151521) @@ -1,1496 +1,1556 @@ /*- * 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 #define NDIS_DUMMY_PATH "\\\\some\\bogus\\path" 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_intrhand(kdpc *, device_object *, +static void ndis_intrsetup(kdpc *, device_object *, irp *, struct ndis_softc *); -static void ndis_return(kdpc *, void *, void *, void *); +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_intrhand, 4), + IMPORT_SFUNC(ndis_intrsetup, 4), IMPORT_SFUNC(ndis_return, 1), { NULL, NULL, NULL } }; -struct nd_head ndis_devhead; +static struct nd_head ndis_devhead; -static struct mtx ndis_req_mtx; - /* * 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 */ - windrv_libinit(); hal_libinit(); - ndis_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); - mtx_init(&ndis_req_mtx, "NDIS request lock", - MTX_NDIS_LOCK, MTX_DEF); - break; case MOD_SHUTDOWN: if (TAILQ_FIRST(&ndis_devhead) == NULL) { /* Shut down subsystems */ - hal_libfini(); ndis_libfini(); - ntoskrnl_libfini(); usbd_libfini(); windrv_libfini(); + ntoskrnl_libfini(); + hal_libfini(); patch = kernndis_functbl; while (patch->ipt_func != NULL) { windrv_unwrap(patch->ipt_wrap); patch++; } - mtx_destroy(&ndis_req_mtx); } break; case MOD_UNLOAD: /* Shut down subsystems */ - hal_libfini(); ndis_libfini(); - ntoskrnl_libfini(); usbd_libfini(); windrv_libfini(); + ntoskrnl_libfini(); + hal_libfini(); patch = kernndis_functbl; while (patch->ipt_func != NULL) { windrv_unwrap(patch->ipt_wrap); patch++; } - mtx_destroy(&ndis_req_mtx); - break; default: error = EINVAL; break; } return(error); } DEV_MODULE(ndisapi, ndis_modevent, NULL); MODULE_VERSION(ndisapi, 1); -int -ndis_thsuspend(p, m, timo) - struct proc *p; - struct mtx *m; - int timo; -{ - int error; - - if (m != NULL) { - error = msleep(&p->p_siglist, m, - curthread->td_priority, "ndissp", timo); - } else { - PROC_LOCK(p); - error = msleep(&p->p_siglist, &p->p_mtx, - curthread->td_priority|PDROP, "ndissp", timo); - } - - return(error); -} - -void -ndis_thresume(p) - struct proc *p; -{ - wakeup(&p->p_siglist); - return; -} - static void ndis_sendrsrcavail_func(adapter) ndis_handle adapter; { return; } 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->arpcom.ac_if; + ifp = sc->ifp; if (ifp->if_flags & IFF_DEBUG) device_printf (sc->ndis_dev, "status: %x\n", status); return; } 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->arpcom.ac_if; + ifp = sc->ifp; if (ifp->if_flags & IFF_DEBUG) device_printf (sc->ndis_dev, "status complete\n"); return; } static void ndis_setdone_func(adapter, status) ndis_handle adapter; ndis_status status; { ndis_miniport_block *block; block = adapter; block->nmb_setstat = status; - wakeup(&block->nmb_setstat); + KeSetEvent(&block->nmb_setevent, IO_NO_INCREMENT, FALSE); return; } static void ndis_getdone_func(adapter, status) ndis_handle adapter; ndis_status status; { ndis_miniport_block *block; block = adapter; block->nmb_getstat = status; - wakeup(&block->nmb_getstat); + KeSetEvent(&block->nmb_getevent, IO_NO_INCREMENT, FALSE); return; } static void ndis_resetdone_func(adapter, status, addressingreset) 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->arpcom.ac_if; + ifp = sc->ifp; if (ifp->if_flags & IFF_DEBUG) device_printf (sc->ndis_dev, "reset done...\n"); - wakeup(sc); + KeSetEvent(&block->nmb_resetevent, IO_NO_INCREMENT, FALSE); + return; } 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; vals = sc->ndis_regvals; TAILQ_INIT(&sc->ndis_cfglist_head); #if __FreeBSD_version < 502113 /* Create the sysctl tree. */ sc->ndis_tree = SYSCTL_ADD_NODE(&sc->ndis_ctx, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, device_get_nameunit(sc->ndis_dev), CTLFLAG_RD, 0, device_get_desc(sc->ndis_dev)); #endif /* Add the driver-specific registry keys. */ - vals = sc->ndis_regvals; 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; #if __FreeBSD_version < 502113 TAILQ_FOREACH(e, &sc->ndis_ctx, link) { #else TAILQ_FOREACH(e, device_get_sysctl_ctx(sc->ndis_dev), link) { #endif oidp = e->entry; if (ndis_strcasecmp(oidp->oid_name, vals->nc_cfgkey) == 0) break; oidp = NULL; } if (oidp != NULL) { vals++; continue; } -#if __FreeBSD_version < 502113 - SYSCTL_ADD_STRING(&sc->ndis_ctx, - SYSCTL_CHILDREN(sc->ndis_tree), -#else - SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)), -#endif - OID_AUTO, vals->nc_cfgkey, - CTLFLAG_RW, vals->nc_val, - sizeof(vals->nc_val), - vals->nc_cfgdesc); + 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", CTLFLAG_RD); /* NDIS version should be 5.1. */ ndis_add_sysctl(sc, "NdisVersion", "NDIS API Version", "0x00050001", CTLFLAG_RD); /* Bus type (PCI, PCMCIA, etc...) */ sprintf(buf, "%d", (int)sc->ndis_iftype); ndis_add_sysctl(sc, "BusType", "Bus Type", buf, CTLFLAG_RD); if (sc->ndis_res_io != NULL) { sprintf(buf, "0x%lx", rman_get_start(sc->ndis_res_io)); ndis_add_sysctl(sc, "IOBaseAddress", "Base I/O Address", buf, CTLFLAG_RD); } if (sc->ndis_irq != NULL) { sprintf(buf, "%lu", rman_get_start(sc->ndis_irq)); ndis_add_sysctl(sc, "InterruptNumber", "Interrupt Number", buf, CTLFLAG_RD); } return(0); } int ndis_add_sysctl(arg, key, desc, val, flag) void *arg; char *key; char *desc; char *val; int flag; { 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) + 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); + cfg->ndis_oid = #if __FreeBSD_version < 502113 SYSCTL_ADD_STRING(&sc->ndis_ctx, SYSCTL_CHILDREN(sc->ndis_tree), #else SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)), #endif OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag, 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; +#if __FreeBSD_version < 502113 + clist = &sc->ndis_ctx; +#else + clist = device_get_sysctl_ctx(sc->ndis_dev); +#endif + 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); } static void -ndis_return(dpc, arg, sysarg1, sysarg2) - kdpc *dpc; +ndis_return(dobj, arg) + device_object *dobj; void *arg; - void *sysarg1; - void *sysarg2; { - struct ndis_softc *sc; + 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; - sc = p->np_softc; - adapter = sc->ndis_block->nmb_miniportadapterctx; + adapter = block->nmb_miniportadapterctx; if (adapter == NULL) return; - returnfunc = sc->ndis_chars->nmc_return_packet_func; + returnfunc = ch->nmc_return_packet_func; - KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); - MSCALL2(returnfunc, adapter, p); - KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); + 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); return; } void ndis_return_packet(buf, arg) void *buf; /* not used */ 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; - KeInitializeDpc(&p->np_dpc, kernndis_functbl[7].ipt_wrap, p); - KeInsertQueueDpc(&p->np_dpc, NULL, NULL); + 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); + return; } 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; } return; } int in_reset = 0; void ndis_free_packet(p) ndis_packet *p; { if (p == NULL) return; ndis_free_bufs(p->np_private.npp_head); NdisFreePacket(p); return; } 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; #if __FreeBSD_version < 600022 struct resource_list brl_rev; struct resource_list_entry *n; #endif int error = 0; sc = arg; block = sc->ndis_block; dev = sc->ndis_dev; #if __FreeBSD_version < 600022 SLIST_INIT(&brl_rev); #endif 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_version = 1; rl->cprl_count = sc->ndis_rescnt; prd = rl->cprl_partial_descs; brl = BUS_GET_RESOURCE_LIST(dev, dev); if (brl != NULL) { #if __FreeBSD_version < 600022 /* * We have a small problem. Some PCI devices have * multiple I/O ranges. Windows orders them starting * from lowest numbered BAR to highest. We discover * them in that order too, but insert them into a singly * linked list head first, which means when time comes * to traverse the list, we enumerate them in reverse * order. This screws up some drivers which expect the * BARs to be in ascending order so that they can choose * the "first" one as their register space. Unfortunately, * in order to fix this, we have to create our own * temporary list with the entries in reverse order. */ SLIST_FOREACH(brle, brl, link) { n = malloc(sizeof(struct resource_list_entry), M_TEMP, M_NOWAIT); if (n == NULL) { error = ENOMEM; goto bad; } bcopy((char *)brle, (char *)n, sizeof(struct resource_list_entry)); SLIST_INSERT_HEAD(&brl_rev, n, link); } SLIST_FOREACH(brle, &brl_rev, link) { #else STAILQ_FOREACH(brle, brl, link) { #endif 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_port.cprd_start.np_quad = brle->start; prd->u.cprd_port.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 = - CmResourceShareDeviceExclusive; + 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; #if __FreeBSD_version < 600022 bad: while (!SLIST_EMPTY(&brl_rev)) { n = SLIST_FIRST(&brl_rev); SLIST_REMOVE_HEAD(&brl_rev, link); free (n, M_TEMP); } #endif 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, *prev = NULL; + 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) MGETHDR(m, M_DONTWAIT, MT_HEADER); else MGET(m, M_DONTWAIT, 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, p, 0, EXT_NDIS); p->np_refcnt++; + totlen += m->m_len; - if (m->m_flags & MT_HEADER) + 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)) { + 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 analagous 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; - int error; 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; KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); - if (sc->ndis_block->nmb_pendingreq != NULL) + if (sc->ndis_block->nmb_pendingreq != NULL) { + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); panic("ndis_set_info() called while other request pending"); - else + } else sc->ndis_block->nmb_pendingreq = (ndis_request *)sc; - NDIS_LOCK(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; - NDIS_UNLOCK(sc); KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); return(ENXIO); } - NDIS_UNLOCK(sc); - 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) { - mtx_lock(&ndis_req_mtx); - error = msleep(&sc->ndis_block->nmb_setstat, - &ndis_req_mtx, - curthread->td_priority|PDROP, - "ndisset", 5 * hz); + /* Wait up to 5 seconds. */ + duetime = (5 * 1000000) * -10; + KeResetEvent(&sc->ndis_block->nmb_setevent); + 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; 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; 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; 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); 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) { - mtx_lock(&ndis_req_mtx); - msleep(sc, &ndis_req_mtx, - curthread->td_priority|PDROP, "ndisrst", 0); + KeResetEvent(&sc->ndis_block->nmb_resetevent); + KeWaitForSingleObject(&sc->ndis_block->nmb_resetevent, + 0, 0, FALSE, NULL); } return(0); } -#define NDIS_REAP_TIMERS +#undef NDIS_REAP_TIMERS int ndis_halt_nic(arg) void *arg; { struct ndis_softc *sc; ndis_handle adapter; ndis_halt_handler haltfunc; #ifdef NDIS_REAP_TIMERS ndis_miniport_timer *t, *n; #endif + ndis_miniport_block *block; + int empty = 0; + uint8_t irql; sc = arg; + block = sc->ndis_block; #ifdef NDIS_REAP_TIMERS /* * Drivers are sometimes very lax about cancelling all * their timers. Cancel them all ourselves, just to be * safe. We must do this before invoking MiniportHalt(), * since if we wait until after, the memory in which * the timers reside will no longer be valid. */ t = sc->ndis_block->nmb_timerlist; while (t != NULL) { KeCancelTimer(&t->nmt_ktimer); n = t; t = t->nmt_nexttimer; n->nmt_nexttimer = NULL; } sc->ndis_block->nmb_timerlist = NULL; +#endif if (!cold) KeFlushQueuedDpcs(); -#endif + /* + * 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_miniportadapterctx = NULL; 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_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. */ - ndis_thsuspend(curthread->td_proc, NULL, hz); + tsleep(curthread->td_proc, PWAIT, "ndwait", hz); NDIS_LOCK(sc); sc->ndis_block->nmb_devicectx = sc; NDIS_UNLOCK(sc); return(0); } void ndis_enable_intr(arg) void *arg; { struct ndis_softc *sc; ndis_handle adapter; ndis_enable_interrupts_handler intrenbfunc; sc = arg; adapter = sc->ndis_block->nmb_miniportadapterctx; intrenbfunc = sc->ndis_chars->nmc_enable_interrupts_func; if (adapter == NULL || intrenbfunc == NULL) return; MSCALL1(intrenbfunc, adapter); return; } void ndis_disable_intr(arg) void *arg; { struct ndis_softc *sc; ndis_handle adapter; ndis_disable_interrupts_handler intrdisfunc; sc = arg; adapter = sc->ndis_block->nmb_miniportadapterctx; intrdisfunc = sc->ndis_chars->nmc_disable_interrupts_func; if (adapter == NULL || intrdisfunc == NULL) return; MSCALL1(intrdisfunc, adapter); return; } int ndis_isr(arg, ourintr, callhandler) void *arg; int *ourintr; int *callhandler; { struct ndis_softc *sc; ndis_handle adapter; ndis_isr_handler isrfunc; uint8_t accepted, queue; if (arg == NULL || ourintr == NULL || callhandler == NULL) return(EINVAL); sc = arg; adapter = sc->ndis_block->nmb_miniportadapterctx; isrfunc = sc->ndis_chars->nmc_isr_func; if (adapter == NULL || isrfunc == NULL) return(ENXIO); MSCALL3(isrfunc, &accepted, &queue, adapter); *ourintr = accepted; *callhandler = queue; return(0); } static void -ndis_intrhand(dpc, dobj, ip, sc) +ndis_intrsetup(dpc, dobj, ip, sc) kdpc *dpc; device_object *dobj; irp *ip; struct ndis_softc *sc; { - ndis_handle adapter; - ndis_interrupt_handler intrfunc; - uint8_t irql; + ndis_miniport_interrupt *intr; - adapter = sc->ndis_block->nmb_miniportadapterctx; - intrfunc = sc->ndis_chars->nmc_interrupt_func; + intr = sc->ndis_block->nmb_interrupt; - if (adapter == NULL || intrfunc == NULL) + /* Sanity check. */ + + if (intr == NULL) return; - if (NDIS_SERIALIZED(sc->ndis_block)) - KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); + KeAcquireSpinLockAtDpcLevel(&intr->ni_dpccountlock); + KeResetEvent(&intr->ni_dpcevt); + if (KeInsertQueueDpc(&intr->ni_dpc, NULL, NULL) == TRUE) + intr->ni_dpccnt++; + KeReleaseSpinLockFromDpcLevel(&intr->ni_dpccountlock); - MSCALL1(intrfunc, adapter); - - /* If there's a MiniportEnableInterrupt() routine, call it. */ - - ndis_enable_intr(sc); - - if (NDIS_SERIALIZED(sc->ndis_block)) - KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); - return; } 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; - int error; uint8_t irql; + uint64_t duetime; sc = arg; KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); - if (sc->ndis_block->nmb_pendingreq != NULL) + if (sc->ndis_block->nmb_pendingreq != NULL) { + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); panic("ndis_get_info() called while other request pending"); - else + } else sc->ndis_block->nmb_pendingreq = (ndis_request *)sc; - NDIS_LOCK(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; - NDIS_UNLOCK(sc); KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); return(ENXIO); } - NDIS_UNLOCK(sc); - 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) { - mtx_lock(&ndis_req_mtx); - error = msleep(&sc->ndis_block->nmb_getstat, - &ndis_req_mtx, - curthread->td_priority|PDROP, - "ndisget", 5 * hz); + /* Wait up to 5 seconds. */ + duetime = (5 * 1000000) * -10; + KeResetEvent(&sc->ndis_block->nmb_getevent); + 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, + 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 = device_get_softc(pdo->do_devext); 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); } - INIT_LIST_HEAD((&block->nmb_packetlist)); + 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/5/sys/compat/ndis/kern_windrv.c =================================================================== --- stable/5/sys/compat/ndis/kern_windrv.c (revision 151520) +++ stable/5/sys/compat/ndis/kern_windrv.c (revision 151521) @@ -1,964 +1,966 @@ /*- * Copyright (c) 2005 * 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 #ifdef __i386__ #include #endif #include #include #include #include #include #include #include struct mtx drvdb_mtx; static STAILQ_HEAD(drvdb, drvdb_ent) drvdb_head; static driver_object fake_pci_driver; /* serves both PCI and cardbus */ static driver_object fake_pccard_driver; #ifdef __i386__ static void x86_oldldt(void *); static void x86_newldt(void *); struct tid { void *tid_except_list; /* 0x00 */ uint32_t tid_oldfs; /* 0x04 */ uint32_t tid_selector; /* 0x08 */ struct tid *tid_self; /* 0x0C */ int tid_cpu; /* 0x10 */ }; static struct tid *my_tids; #endif /* __i386__ */ #define DUMMY_REGISTRY_PATH "\\\\some\\bogus\\path" int windrv_libinit(void) { STAILQ_INIT(&drvdb_head); mtx_init(&drvdb_mtx, "Windows driver DB lock", "Windows internal lock", MTX_DEF); /* * PCI and pccard devices don't need to use IRPs to * interact with their bus drivers (usually), so our * emulated PCI and pccard drivers are just stubs. * USB devices, on the other hand, do all their I/O * by exchanging IRPs with the USB bus driver, so * for that we need to provide emulator dispatcher * routines, which are in a separate module. */ windrv_bus_attach(&fake_pci_driver, "PCI Bus"); windrv_bus_attach(&fake_pccard_driver, "PCCARD Bus"); #ifdef __i386__ /* * In order to properly support SMP machines, we have * to modify the GDT on each CPU, since we never know * on which one we'll end up running. */ my_tids = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct tid) * mp_ncpus, 0); if (my_tids == NULL) panic("failed to allocate thread info blocks"); smp_rendezvous(NULL, x86_newldt, NULL, NULL); #endif return(0); } int windrv_libfini(void) { struct drvdb_ent *d; mtx_lock(&drvdb_mtx); while(STAILQ_FIRST(&drvdb_head) != NULL) { d = STAILQ_FIRST(&drvdb_head); STAILQ_REMOVE_HEAD(&drvdb_head, link); free(d, M_DEVBUF); } mtx_unlock(&drvdb_mtx); - free(fake_pci_driver.dro_drivername.us_buf, M_DEVBUF); - free(fake_pccard_driver.dro_drivername.us_buf, M_DEVBUF); + RtlFreeUnicodeString(&fake_pci_driver.dro_drivername); + RtlFreeUnicodeString(&fake_pccard_driver.dro_drivername); mtx_destroy(&drvdb_mtx); #ifdef __i386__ smp_rendezvous(NULL, x86_oldldt, NULL, NULL); ExFreePool(my_tids); #endif return(0); } /* * Given the address of a driver image, find its corresponding * driver_object. */ driver_object * windrv_lookup(img, name) vm_offset_t img; char *name; { struct drvdb_ent *d; unicode_string us; + ansi_string as; bzero((char *)&us, sizeof(us)); /* Damn unicode. */ if (name != NULL) { - us.us_len = strlen(name) * 2; - us.us_maxlen = strlen(name) * 2; - us.us_buf = NULL; - ndis_ascii_to_unicode(name, &us.us_buf); + RtlInitAnsiString(&as, name); + if (RtlAnsiStringToUnicodeString(&us, &as, TRUE)) + return(NULL); } mtx_lock(&drvdb_mtx); STAILQ_FOREACH(d, &drvdb_head, link) { if (d->windrv_object->dro_driverstart == (void *)img || (bcmp((char *)d->windrv_object->dro_drivername.us_buf, (char *)us.us_buf, us.us_len) == 0 && us.us_len)) { mtx_unlock(&drvdb_mtx); if (name != NULL) ExFreePool(us.us_buf); return(d->windrv_object); } } mtx_unlock(&drvdb_mtx); if (name != NULL) - ExFreePool(us.us_buf); + RtlFreeUnicodeString(&us); return(NULL); } struct drvdb_ent * windrv_match(matchfunc, ctx) matchfuncptr matchfunc; void *ctx; { struct drvdb_ent *d; int match; mtx_lock(&drvdb_mtx); STAILQ_FOREACH(d, &drvdb_head, link) { if (d->windrv_devlist == NULL) continue; match = matchfunc(d->windrv_bustype, d->windrv_devlist, ctx); if (match == TRUE) { mtx_unlock(&drvdb_mtx); return(d); } } mtx_unlock(&drvdb_mtx); return(NULL); } /* * Remove a driver_object from our datatabase and destroy it. Throw * away any custom driver extension info that may have been added. */ int windrv_unload(mod, img, len) module_t mod; vm_offset_t img; int len; { struct drvdb_ent *db, *r = NULL; driver_object *drv; device_object *d, *pdo; device_t dev; - list_entry *e, *c; + list_entry *e; drv = windrv_lookup(img, NULL); /* * When we unload a driver image, we need to force a * detach of any devices that might be using it. We * need the PDOs of all attached devices for this. * Getting at them is a little hard. We basically * have to walk the device lists of all our bus * drivers. */ mtx_lock(&drvdb_mtx); STAILQ_FOREACH(db, &drvdb_head, link) { /* * Fake bus drivers have no devlist info. * If this driver has devlist info, it's * a loaded Windows driver and has no PDOs, * so skip it. */ if (db->windrv_devlist != NULL) continue; pdo = db->windrv_object->dro_devobj; while (pdo != NULL) { d = pdo->do_attacheddev; if (d->do_drvobj != drv) { pdo = pdo->do_nextdev; continue; } dev = pdo->do_devext; pdo = pdo->do_nextdev; mtx_unlock(&drvdb_mtx); device_detach(dev); mtx_lock(&drvdb_mtx); } } - + STAILQ_FOREACH(db, &drvdb_head, link) { if (db->windrv_object->dro_driverstart == (void *)img) { r = db; STAILQ_REMOVE(&drvdb_head, db, drvdb_ent, link); break; } } mtx_unlock(&drvdb_mtx); if (r == NULL) return (ENOENT); + if (drv == NULL) + return(ENOENT); + /* * Destroy any custom extensions that may have been added. */ drv = r->windrv_object; - e = drv->dro_driverext->dre_usrext.nle_flink; - while (e != &drv->dro_driverext->dre_usrext) { - c = e->nle_flink; - REMOVE_LIST_ENTRY(e); + while (!IsListEmpty(&drv->dro_driverext->dre_usrext)) { + e = RemoveHeadList(&drv->dro_driverext->dre_usrext); ExFreePool(e); - e = c; } /* Free the driver extension */ free(drv->dro_driverext, M_DEVBUF); /* Free the driver name */ - free(drv->dro_drivername.us_buf, M_DEVBUF); + RtlFreeUnicodeString(&drv->dro_drivername); /* Free driver object */ free(drv, M_DEVBUF); /* Free our DB handle */ free(r, M_DEVBUF); return(0); } #define WINDRV_LOADED htonl(0x42534F44) /* * Loader routine for actual Windows driver modules, ultimately * calls the driver's DriverEntry() routine. */ int windrv_load(mod, img, len, bustype, devlist, regvals) module_t mod; vm_offset_t img; int len; interface_type bustype; void *devlist; ndis_cfg *regvals; { image_import_descriptor imp_desc; image_optional_header opt_hdr; driver_entry entry; struct drvdb_ent *new; struct driver_object *drv; int status; uint32_t *ptr; + ansi_string as; /* * First step: try to relocate and dynalink the executable * driver image. */ ptr = (uint32_t *)(img + 8); if (*ptr == WINDRV_LOADED) goto skipreloc; /* Perform text relocation */ if (pe_relocate(img)) return(ENOEXEC); /* Dynamically link the NDIS.SYS routines -- required. */ if (pe_patch_imports(img, "NDIS", ndis_functbl)) return(ENOEXEC); /* Dynamically link the HAL.dll routines -- also required. */ if (pe_patch_imports(img, "HAL", hal_functbl)) return(ENOEXEC); /* Dynamically link ntoskrnl.exe -- optional. */ if (pe_get_import_descriptor(img, &imp_desc, "ntoskrnl") == 0) { if (pe_patch_imports(img, "ntoskrnl", ntoskrnl_functbl)) return(ENOEXEC); } /* Dynamically link USBD.SYS -- optional */ if (pe_get_import_descriptor(img, &imp_desc, "USBD") == 0) { if (pe_patch_imports(img, "USBD", usbd_functbl)) return(ENOEXEC); } *ptr = WINDRV_LOADED; skipreloc: /* Next step: find the driver entry point. */ pe_get_optional_header(img, &opt_hdr); entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr); /* Next step: allocate and store a driver object. */ new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO); if (new == NULL) return (ENOMEM); drv = malloc(sizeof(driver_object), M_DEVBUF, M_NOWAIT|M_ZERO); if (drv == NULL) { free (new, M_DEVBUF); return (ENOMEM); } /* Allocate a driver extension structure too. */ drv->dro_driverext = malloc(sizeof(driver_extension), M_DEVBUF, M_NOWAIT|M_ZERO); if (drv->dro_driverext == NULL) { free(new, M_DEVBUF); free(drv, M_DEVBUF); return(ENOMEM); } - INIT_LIST_HEAD((&drv->dro_driverext->dre_usrext)); + InitializeListHead((&drv->dro_driverext->dre_usrext)); drv->dro_driverstart = (void *)img; drv->dro_driversize = len; - drv->dro_drivername.us_len = strlen(DUMMY_REGISTRY_PATH) * 2; - drv->dro_drivername.us_maxlen = strlen(DUMMY_REGISTRY_PATH) * 2; - drv->dro_drivername.us_buf = NULL; - ndis_ascii_to_unicode(DUMMY_REGISTRY_PATH, - &drv->dro_drivername.us_buf); + RtlInitAnsiString(&as, DUMMY_REGISTRY_PATH); + if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE)) { + free(new, M_DEVBUF); + free(drv, M_DEVBUF); + return(ENOMEM); + } new->windrv_object = drv; new->windrv_regvals = regvals; new->windrv_devlist = devlist; new->windrv_bustype = bustype; /* Now call the DriverEntry() function. */ status = MSCALL2(entry, drv, &drv->dro_drivername); if (status != STATUS_SUCCESS) { - free(drv->dro_drivername.us_buf, M_DEVBUF); + RtlFreeUnicodeString(&drv->dro_drivername); free(drv, M_DEVBUF); free(new, M_DEVBUF); return(ENODEV); } mtx_lock(&drvdb_mtx); STAILQ_INSERT_HEAD(&drvdb_head, new, link); mtx_unlock(&drvdb_mtx); return (0); } /* * Make a new Physical Device Object for a device that was * detected/plugged in. For us, the PDO is just a way to * get at the device_t. */ int windrv_create_pdo(drv, bsddev) driver_object *drv; device_t bsddev; { device_object *dev; /* * This is a new physical device object, which technically * is the "top of the stack." Consequently, we don't do * an IoAttachDeviceToDeviceStack() here. */ mtx_lock(&drvdb_mtx); IoCreateDevice(drv, 0, NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &dev); mtx_unlock(&drvdb_mtx); /* Stash pointer to our BSD device handle. */ dev->do_devext = bsddev; return(STATUS_SUCCESS); } void windrv_destroy_pdo(drv, bsddev) driver_object *drv; device_t bsddev; { device_object *pdo; pdo = windrv_find_pdo(drv, bsddev); /* Remove reference to device_t */ pdo->do_devext = NULL; mtx_lock(&drvdb_mtx); IoDeleteDevice(pdo); mtx_unlock(&drvdb_mtx); return; } /* * Given a device_t, find the corresponding PDO in a driver's * device list. */ device_object * windrv_find_pdo(drv, bsddev) driver_object *drv; device_t bsddev; { device_object *pdo; mtx_lock(&drvdb_mtx); pdo = drv->dro_devobj; while (pdo != NULL) { if (pdo->do_devext == bsddev) { mtx_unlock(&drvdb_mtx); return(pdo); } pdo = pdo->do_nextdev; } mtx_unlock(&drvdb_mtx); return(NULL); } /* * Add an internally emulated driver to the database. We need this * to set up an emulated bus driver so that it can receive IRPs. */ int windrv_bus_attach(drv, name) driver_object *drv; char *name; { struct drvdb_ent *new; + ansi_string as; new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO); if (new == NULL) return (ENOMEM); - drv->dro_drivername.us_len = strlen(name) * 2; - drv->dro_drivername.us_maxlen = strlen(name) * 2; - drv->dro_drivername.us_buf = NULL; - ndis_ascii_to_unicode(name, &drv->dro_drivername.us_buf); + RtlInitAnsiString(&as, name); + if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE)) + return(ENOMEM); /* * Set up a fake image pointer to avoid false matches * in windrv_lookup(). */ drv->dro_driverstart = (void *)0xFFFFFFFF; new->windrv_object = drv; new->windrv_devlist = NULL; new->windrv_regvals = NULL; mtx_lock(&drvdb_mtx); STAILQ_INSERT_HEAD(&drvdb_head, new, link); mtx_unlock(&drvdb_mtx); return(0); } #ifdef __amd64__ extern void x86_64_wrap(void); extern void x86_64_wrap_call(void); extern void x86_64_wrap_end(void); int windrv_wrap(func, wrap, argcnt, ftype) funcptr func; funcptr *wrap; int argcnt; int ftype; { funcptr p; vm_offset_t *calladdr; vm_offset_t wrapstart, wrapend, wrapcall; wrapstart = (vm_offset_t)&x86_64_wrap; wrapend = (vm_offset_t)&x86_64_wrap_end; wrapcall = (vm_offset_t)&x86_64_wrap_call; /* Allocate a new wrapper instance. */ p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT); if (p == NULL) return(ENOMEM); /* Copy over the code. */ bcopy((char *)wrapstart, p, (wrapend - wrapstart)); /* Insert the function address into the new wrapper instance. */ calladdr = (uint64_t *)((char *)p + (wrapcall - wrapstart) + 2); *calladdr = (vm_offset_t)func; *wrap = p; return(0); } #endif /* __amd64__ */ #ifdef __i386__ struct x86desc { uint16_t x_lolimit; uint16_t x_base0; uint8_t x_base1; uint8_t x_flags; uint8_t x_hilimit; uint8_t x_base2; }; struct gdt { uint16_t limit; void *base; } __attribute__((__packed__)); extern uint16_t x86_getfs(void); extern void x86_setfs(uint16_t); extern void *x86_gettid(void); extern void x86_critical_enter(void); extern void x86_critical_exit(void); extern void x86_getldt(struct gdt *, uint16_t *); extern void x86_setldt(struct gdt *, uint16_t); #define SEL_LDT 4 /* local descriptor table */ #define SEL_TO_FS(x) (((x) << 3)) /* * FreeBSD 6.0 and later has a special GDT segment reserved * specifically for us, so if GNDIS_SEL is defined, use that. * If not, use GTGATE_SEL, which is uninitialized and infrequently * used. */ #ifdef GNDIS_SEL #define FREEBSD_EMPTYSEL GNDIS_SEL #else #define FREEBSD_EMPTYSEL GTGATE_SEL /* slot 7 */ #endif /* * The meanings of various bits in a descriptor vary a little * depending on whether the descriptor will be used as a * code, data or system descriptor. (And that in turn depends * on which segment register selects the descriptor.) * We're only trying to create a data segment, so the definitions * below are the ones that apply to a data descriptor. */ #define SEGFLAGLO_PRESENT 0x80 /* segment is present */ #define SEGFLAGLO_PRIVLVL 0x60 /* privlevel needed for this seg */ #define SEGFLAGLO_CD 0x10 /* 1 = code/data, 0 = system */ #define SEGFLAGLO_MBZ 0x08 /* must be zero */ #define SEGFLAGLO_EXPANDDOWN 0x04 /* limit expands down */ #define SEGFLAGLO_WRITEABLE 0x02 /* segment is writeable */ #define SEGGLAGLO_ACCESSED 0x01 /* segment has been accessed */ #define SEGFLAGHI_GRAN 0x80 /* granularity, 1 = byte, 0 = page */ #define SEGFLAGHI_BIG 0x40 /* 1 = 32 bit stack, 0 = 16 bit */ /* * Context switch from UNIX to Windows. Save the existing value * of %fs for this processor, then change it to point to our * fake TID. Note that it is also possible to pin ourselves * to our current CPU, though I'm not sure this is really * necessary. It depends on whether or not an interrupt might * preempt us while Windows code is running and we wind up * scheduled onto another CPU as a result. So far, it doesn't * seem like this is what happens. */ void ctxsw_utow(void) { struct tid *t; t = &my_tids[curthread->td_oncpu]; /* * Ugly hack. During system bootstrap (cold == 1), only CPU 0 * is running. So if we were loaded at bootstrap, only CPU 0 * will have our special GDT entry. This is a problem for SMP * systems, so to deal with this, we check here to make sure * the TID for this processor has been initialized, and if it * hasn't, we need to do it right now or else things will * explode. */ - + if (t->tid_self != t) x86_newldt(NULL); t->tid_oldfs = x86_getfs(); t->tid_cpu = curthread->td_oncpu; x86_setfs(SEL_TO_FS(t->tid_selector)); /* Now entering Windows land, population: you. */ return; } /* * Context switch from Windows back to UNIX. Restore %fs to * its previous value. This always occurs after a call to * ctxsw_utow(). */ void ctxsw_wtou(void) { struct tid *t; t = x86_gettid(); x86_setfs(t->tid_oldfs); /* Welcome back to UNIX land, we missed you. */ #ifdef EXTRA_SANITY if (t->tid_cpu != curthread->td_oncpu) panic("ctxsw GOT MOVED TO OTHER CPU!"); #endif return; } static int windrv_wrap_stdcall(funcptr, funcptr *, int); static int windrv_wrap_fastcall(funcptr, funcptr *, int); static int windrv_wrap_regparm(funcptr, funcptr *); extern void x86_fastcall_wrap(void); extern void x86_fastcall_wrap_call(void); extern void x86_fastcall_wrap_arg(void); extern void x86_fastcall_wrap_end(void); static int windrv_wrap_fastcall(func, wrap, argcnt) funcptr func; funcptr *wrap; int8_t argcnt; { funcptr p; vm_offset_t *calladdr; uint8_t *argaddr; vm_offset_t wrapstart, wrapend, wrapcall, wraparg; wrapstart = (vm_offset_t)&x86_fastcall_wrap; wrapend = (vm_offset_t)&x86_fastcall_wrap_end; wrapcall = (vm_offset_t)&x86_fastcall_wrap_call; wraparg = (vm_offset_t)&x86_fastcall_wrap_arg; /* Allocate a new wrapper instance. */ p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT); if (p == NULL) return(ENOMEM); /* Copy over the code. */ bcopy((char *)wrapstart, p, (wrapend - wrapstart)); /* Insert the function address into the new wrapper instance. */ calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1)); *calladdr = (vm_offset_t)func; argcnt -= 2; if (argcnt < 1) argcnt = 0; argaddr = (u_int8_t *)((char *)p + ((wraparg - wrapstart) + 1)); *argaddr = argcnt * sizeof(uint32_t); *wrap = p; return(0); } extern void x86_stdcall_wrap(void); extern void x86_stdcall_wrap_call(void); extern void x86_stdcall_wrap_arg(void); extern void x86_stdcall_wrap_end(void); static int windrv_wrap_stdcall(func, wrap, argcnt) funcptr func; funcptr *wrap; uint8_t argcnt; { funcptr p; vm_offset_t *calladdr; uint8_t *argaddr; vm_offset_t wrapstart, wrapend, wrapcall, wraparg; wrapstart = (vm_offset_t)&x86_stdcall_wrap; wrapend = (vm_offset_t)&x86_stdcall_wrap_end; wrapcall = (vm_offset_t)&x86_stdcall_wrap_call; wraparg = (vm_offset_t)&x86_stdcall_wrap_arg; /* Allocate a new wrapper instance. */ p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT); if (p == NULL) return(ENOMEM); /* Copy over the code. */ bcopy((char *)wrapstart, p, (wrapend - wrapstart)); /* Insert the function address into the new wrapper instance. */ calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1)); *calladdr = (vm_offset_t)func; argaddr = (u_int8_t *)((char *)p + ((wraparg - wrapstart) + 1)); *argaddr = argcnt * sizeof(uint32_t); *wrap = p; return(0); } extern void x86_regparm_wrap(void); extern void x86_regparm_wrap_call(void); extern void x86_regparm_wrap_end(void); static int windrv_wrap_regparm(func, wrap) funcptr func; funcptr *wrap; { funcptr p; vm_offset_t *calladdr; vm_offset_t wrapstart, wrapend, wrapcall; wrapstart = (vm_offset_t)&x86_regparm_wrap; wrapend = (vm_offset_t)&x86_regparm_wrap_end; wrapcall = (vm_offset_t)&x86_regparm_wrap_call; /* Allocate a new wrapper instance. */ p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT); if (p == NULL) return(ENOMEM); /* Copy over the code. */ bcopy(x86_regparm_wrap, p, (wrapend - wrapstart)); /* Insert the function address into the new wrapper instance. */ calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1)); *calladdr = (vm_offset_t)func; *wrap = p; return(0); } int windrv_wrap(func, wrap, argcnt, ftype) funcptr func; funcptr *wrap; int argcnt; int ftype; { switch(ftype) { case WINDRV_WRAP_FASTCALL: return(windrv_wrap_fastcall(func, wrap, argcnt)); case WINDRV_WRAP_STDCALL: return(windrv_wrap_stdcall(func, wrap, argcnt)); case WINDRV_WRAP_REGPARM: return(windrv_wrap_regparm(func, wrap)); case WINDRV_WRAP_CDECL: return(windrv_wrap_stdcall(func, wrap, 0)); default: break; } return(EINVAL); } static void x86_oldldt(dummy) void *dummy; { struct thread *t; struct x86desc *gdt; struct gdt gtable; uint16_t ltable; mtx_lock_spin(&sched_lock); t = curthread; /* Grab location of existing GDT. */ x86_getldt(>able, <able); /* Find the slot we updated. */ gdt = gtable.base; gdt += FREEBSD_EMPTYSEL; /* Empty it out. */ bzero((char *)gdt, sizeof(struct x86desc)); /* Restore GDT. */ x86_setldt(>able, ltable); mtx_unlock_spin(&sched_lock); return; } static void x86_newldt(dummy) void *dummy; { struct gdt gtable; uint16_t ltable; struct x86desc *l; struct thread *t; mtx_lock_spin(&sched_lock); t = curthread; /* Grab location of existing GDT. */ x86_getldt(>able, <able); /* Get pointer to the GDT table. */ l = gtable.base; /* Get pointer to empty slot */ l += FREEBSD_EMPTYSEL; /* Initialize TID for this CPU. */ my_tids[t->td_oncpu].tid_selector = FREEBSD_EMPTYSEL; my_tids[t->td_oncpu].tid_self = &my_tids[t->td_oncpu]; /* Set up new GDT entry. */ l->x_lolimit = sizeof(struct tid); l->x_hilimit = SEGFLAGHI_GRAN|SEGFLAGHI_BIG; l->x_base0 = (vm_offset_t)(&my_tids[t->td_oncpu]) & 0xFFFF; l->x_base1 = ((vm_offset_t)(&my_tids[t->td_oncpu]) >> 16) & 0xFF; l->x_base2 = ((vm_offset_t)(&my_tids[t->td_oncpu]) >> 24) & 0xFF; l->x_flags = SEGFLAGLO_PRESENT|SEGFLAGLO_CD|SEGFLAGLO_WRITEABLE; /* Update the GDT. */ x86_setldt(>able, ltable); mtx_unlock_spin(&sched_lock); /* Whew. */ return; } #endif /* __i386__ */ int windrv_unwrap(func) funcptr func; { free(func, M_DEVBUF); return(0); } Index: stable/5/sys/compat/ndis/ndis_var.h =================================================================== --- stable/5/sys/compat/ndis/ndis_var.h (revision 151520) +++ stable/5/sys/compat/ndis/ndis_var.h (revision 151521) @@ -1,1651 +1,1769 @@ /*- * 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$ */ #ifndef _NDIS_VAR_H_ #define _NDIS_VAR_H_ /* Forward declarations */ struct ndis_miniport_block; struct ndis_mdriver_block; typedef struct ndis_miniport_block ndis_miniport_block; typedef struct ndis_mdriver_block ndis_mdriver_block; /* Base types */ typedef uint32_t ndis_status; typedef void *ndis_handle; typedef uint32_t ndis_oid; typedef uint32_t ndis_error_code; typedef register_t ndis_kspin_lock; typedef uint8_t ndis_kirql; /* * NDIS status codes (there are lots of them). The ones that * don't seem to fit the pattern are actually mapped to generic * NT status codes. */ #define NDIS_STATUS_SUCCESS 0 #define NDIS_STATUS_PENDING 0x00000103 #define NDIS_STATUS_NOT_RECOGNIZED 0x00010001 #define NDIS_STATUS_NOT_COPIED 0x00010002 #define NDIS_STATUS_NOT_ACCEPTED 0x00010003 #define NDIS_STATUS_CALL_ACTIVE 0x00010007 #define NDIS_STATUS_ONLINE 0x40010003 #define NDIS_STATUS_RESET_START 0x40010004 #define NDIS_STATUS_RESET_END 0x40010005 #define NDIS_STATUS_RING_STATUS 0x40010006 #define NDIS_STATUS_CLOSED 0x40010007 #define NDIS_STATUS_WAN_LINE_UP 0x40010008 #define NDIS_STATUS_WAN_LINE_DOWN 0x40010009 #define NDIS_STATUS_WAN_FRAGMENT 0x4001000A #define NDIS_STATUS_MEDIA_CONNECT 0x4001000B #define NDIS_STATUS_MEDIA_DISCONNECT 0x4001000C #define NDIS_STATUS_HARDWARE_LINE_UP 0x4001000D #define NDIS_STATUS_HARDWARE_LINE_DOWN 0x4001000E #define NDIS_STATUS_INTERFACE_UP 0x4001000F #define NDIS_STATUS_INTERFACE_DOWN 0x40010010 #define NDIS_STATUS_MEDIA_BUSY 0x40010011 #define NDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012 #define NDIS_STATUS_WW_INDICATION NDIS_STATUS_MEDIA_SPECIFIC_INDICATION #define NDIS_STATUS_LINK_SPEED_CHANGE 0x40010013 #define NDIS_STATUS_WAN_GET_STATS 0x40010014 #define NDIS_STATUS_WAN_CO_FRAGMENT 0x40010015 #define NDIS_STATUS_WAN_CO_LINKPARAMS 0x40010016 #define NDIS_STATUS_NOT_RESETTABLE 0x80010001 #define NDIS_STATUS_SOFT_ERRORS 0x80010003 #define NDIS_STATUS_HARD_ERRORS 0x80010004 #define NDIS_STATUS_BUFFER_OVERFLOW 0x80000005 #define NDIS_STATUS_FAILURE 0xC0000001 #define NDIS_STATUS_RESOURCES 0xC000009A #define NDIS_STATUS_CLOSING 0xC0010002 #define NDIS_STATUS_BAD_VERSION 0xC0010004 #define NDIS_STATUS_BAD_CHARACTERISTICS 0xC0010005 #define NDIS_STATUS_ADAPTER_NOT_FOUND 0xC0010006 #define NDIS_STATUS_OPEN_FAILED 0xC0010007 #define NDIS_STATUS_DEVICE_FAILED 0xC0010008 #define NDIS_STATUS_MULTICAST_FULL 0xC0010009 #define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A #define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B #define NDIS_STATUS_REQUEST_ABORTED 0xC001000C #define NDIS_STATUS_RESET_IN_PROGRESS 0xC001000D #define NDIS_STATUS_CLOSING_INDICATING 0xC001000E #define NDIS_STATUS_NOT_SUPPORTED 0xC00000BB #define NDIS_STATUS_INVALID_PACKET 0xC001000F #define NDIS_STATUS_OPEN_LIST_FULL 0xC0010010 #define NDIS_STATUS_ADAPTER_NOT_READY 0xC0010011 #define NDIS_STATUS_ADAPTER_NOT_OPEN 0xC0010012 #define NDIS_STATUS_NOT_INDICATING 0xC0010013 #define NDIS_STATUS_INVALID_LENGTH 0xC0010014 #define NDIS_STATUS_INVALID_DATA 0xC0010015 #define NDIS_STATUS_BUFFER_TOO_SHORT 0xC0010016 #define NDIS_STATUS_INVALID_OID 0xC0010017 #define NDIS_STATUS_ADAPTER_REMOVED 0xC0010018 #define NDIS_STATUS_UNSUPPORTED_MEDIA 0xC0010019 #define NDIS_STATUS_GROUP_ADDRESS_IN_USE 0xC001001A #define NDIS_STATUS_FILE_NOT_FOUND 0xC001001B #define NDIS_STATUS_ERROR_READING_FILE 0xC001001C #define NDIS_STATUS_ALREADY_MAPPED 0xC001001D #define NDIS_STATUS_RESOURCE_CONFLICT 0xC001001E #define NDIS_STATUS_NO_CABLE 0xC001001F #define NDIS_STATUS_INVALID_SAP 0xC0010020 #define NDIS_STATUS_SAP_IN_USE 0xC0010021 #define NDIS_STATUS_INVALID_ADDRESS 0xC0010022 #define NDIS_STATUS_VC_NOT_ACTIVATED 0xC0010023 #define NDIS_STATUS_DEST_OUT_OF_ORDER 0xC0010024 #define NDIS_STATUS_VC_NOT_AVAILABLE 0xC0010025 #define NDIS_STATUS_CELLRATE_NOT_AVAILABLE 0xC0010026 #define NDIS_STATUS_INCOMPATABLE_QOS 0xC0010027 #define NDIS_STATUS_AAL_PARAMS_UNSUPPORTED 0xC0010028 #define NDIS_STATUS_NO_ROUTE_TO_DESTINATION 0xC0010029 #define NDIS_STATUS_TOKEN_RING_OPEN_ERROR 0xC0011000 #define NDIS_STATUS_INVALID_DEVICE_REQUEST 0xC0000010 #define NDIS_STATUS_NETWORK_UNREACHABLE 0xC000023C /* * NDIS event codes. They are usually reported to NdisWriteErrorLogEntry(). */ #define EVENT_NDIS_RESOURCE_CONFLICT 0xC0001388 #define EVENT_NDIS_OUT_OF_RESOURCE 0xC0001389 #define EVENT_NDIS_HARDWARE_FAILURE 0xC000138A #define EVENT_NDIS_ADAPTER_NOT_FOUND 0xC000138B #define EVENT_NDIS_INTERRUPT_CONNECT 0xC000138C #define EVENT_NDIS_DRIVER_FAILURE 0xC000138D #define EVENT_NDIS_BAD_VERSION 0xC000138E #define EVENT_NDIS_TIMEOUT 0x8000138F #define EVENT_NDIS_NETWORK_ADDRESS 0xC0001390 #define EVENT_NDIS_UNSUPPORTED_CONFIGURATION 0xC0001391 #define EVENT_NDIS_INVALID_VALUE_FROM_ADAPTER 0xC0001392 #define EVENT_NDIS_MISSING_CONFIGURATION_PARAMETER 0xC0001393 #define EVENT_NDIS_BAD_IO_BASE_ADDRESS 0xC0001394 #define EVENT_NDIS_RECEIVE_SPACE_SMALL 0x40001395 #define EVENT_NDIS_ADAPTER_DISABLED 0x80001396 #define EVENT_NDIS_IO_PORT_CONFLICT 0x80001397 #define EVENT_NDIS_PORT_OR_DMA_CONFLICT 0x80001398 #define EVENT_NDIS_MEMORY_CONFLICT 0x80001399 #define EVENT_NDIS_INTERRUPT_CONFLICT 0x8000139A #define EVENT_NDIS_DMA_CONFLICT 0x8000139B #define EVENT_NDIS_INVALID_DOWNLOAD_FILE_ERROR 0xC000139C #define EVENT_NDIS_MAXRECEIVES_ERROR 0x8000139D #define EVENT_NDIS_MAXTRANSMITS_ERROR 0x8000139E #define EVENT_NDIS_MAXFRAMESIZE_ERROR 0x8000139F #define EVENT_NDIS_MAXINTERNALBUFS_ERROR 0x800013A0 #define EVENT_NDIS_MAXMULTICAST_ERROR 0x800013A1 #define EVENT_NDIS_PRODUCTID_ERROR 0x800013A2 #define EVENT_NDIS_LOBE_FAILUE_ERROR 0x800013A3 #define EVENT_NDIS_SIGNAL_LOSS_ERROR 0x800013A4 #define EVENT_NDIS_REMOVE_RECEIVED_ERROR 0x800013A5 #define EVENT_NDIS_TOKEN_RING_CORRECTION 0x400013A6 #define EVENT_NDIS_ADAPTER_CHECK_ERROR 0xC00013A7 #define EVENT_NDIS_RESET_FAILURE_ERROR 0x800013A8 #define EVENT_NDIS_CABLE_DISCONNECTED_ERROR 0x800013A9 #define EVENT_NDIS_RESET_FAILURE_CORRECTION 0x800013AA /* * NDIS OIDs used by the queryinfo/setinfo routines. * Some are required by all NDIS drivers, some are specific to * a particular type of device, and some are purely optional. * Unfortunately, one of the purely optional OIDs is the one * that lets us set the MAC address of the device. */ /* Required OIDs */ #define OID_GEN_SUPPORTED_LIST 0x00010101 #define OID_GEN_HARDWARE_STATUS 0x00010102 #define OID_GEN_MEDIA_SUPPORTED 0x00010103 #define OID_GEN_MEDIA_IN_USE 0x00010104 #define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 #define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 #define OID_GEN_LINK_SPEED 0x00010107 #define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 #define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 #define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A #define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B #define OID_GEN_VENDOR_ID 0x0001010C #define OID_GEN_VENDOR_DESCRIPTION 0x0001010D #define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E #define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F #define OID_GEN_DRIVER_VERSION 0x00010110 #define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 #define OID_GEN_PROTOCOL_OPTIONS 0x00010112 #define OID_GEN_MAC_OPTIONS 0x00010113 #define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 #define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 #define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 #define OID_GEN_SUPPORTED_GUIDS 0x00010117 #define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 /* Set only */ #define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 /* Set only */ #define OID_GEN_MACHINE_NAME 0x0001021A #define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B /* Set only */ #define OID_GEN_VLAN_ID 0x0001021C /* Optional OIDs. */ #define OID_GEN_MEDIA_CAPABILITIES 0x00010201 #define OID_GEN_PHYSICAL_MEDIUM 0x00010202 /* Required statistics OIDs. */ #define OID_GEN_XMIT_OK 0x00020101 #define OID_GEN_RCV_OK 0x00020102 #define OID_GEN_XMIT_ERROR 0x00020103 #define OID_GEN_RCV_ERROR 0x00020104 #define OID_GEN_RCV_NO_BUFFER 0x00020105 /* Optional OID statistics */ #define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 #define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 #define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 #define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 #define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 #define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 #define OID_GEN_DIRECTED_BYTES_RCV 0x00020207 #define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 #define OID_GEN_MULTICAST_BYTES_RCV 0x00020209 #define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A #define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B #define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C #define OID_GEN_RCV_CRC_ERROR 0x0002020D #define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E #define OID_GEN_GET_TIME_CAPS 0x0002020F #define OID_GEN_GET_NETCARD_TIME 0x00020210 #define OID_GEN_NETCARD_LOAD 0x00020211 #define OID_GEN_DEVICE_PROFILE 0x00020212 /* 802.3 (ethernet) OIDs */ #define OID_802_3_PERMANENT_ADDRESS 0x01010101 #define OID_802_3_CURRENT_ADDRESS 0x01010102 #define OID_802_3_MULTICAST_LIST 0x01010103 #define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 #define OID_802_3_MAC_OPTIONS 0x01010105 #define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 #define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 #define OID_802_3_XMIT_ONE_COLLISION 0x01020102 #define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 #define OID_802_3_XMIT_DEFERRED 0x01020201 #define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 #define OID_802_3_RCV_OVERRUN 0x01020203 #define OID_802_3_XMIT_UNDERRUN 0x01020204 #define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 #define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 #define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 /* PnP and power management OIDs */ #define OID_PNP_CAPABILITIES 0xFD010100 #define OID_PNP_SET_POWER 0xFD010101 #define OID_PNP_QUERY_POWER 0xFD010102 #define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103 #define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104 #define OID_PNP_WAKE_UP_PATTERN_LIST 0xFD010105 #define OID_PNP_ENABLE_WAKE_UP 0xFD010106 +/* + * These are the possible power states for + * OID_PNP_SET_POWER and OID_PNP_QUERY_POWER. + */ +#define NDIS_POWERSTATE_UNSPEC 0 +#define NDIS_POWERSTATE_D0 1 +#define NDIS_POWERSTATE_D1 2 +#define NDIS_POWERSTATE_D2 3 +#define NDIS_POWERSTATE_D3 4 + +/* + * These are used with the MiniportPnpEventNotify() method. + */ + +#define NDIS_POWERPROFILE_BATTERY 0 +#define NDIS_POWERPROFILE_ACONLINE 1 + +#define NDIS_PNP_EVENT_QUERY_REMOVED 0 +#define NDIS_PNP_EVENT_REMOVED 1 +#define NDIS_PNP_EVENT_SURPRISE_REMOVED 2 +#define NDIS_PNP_EVENT_QUERY_STOPPED 3 +#define NDIS_PNP_EVENT_STOPPED 4 +#define NDIS_PNP_EVENT_PROFILECHANGED 5 + + /* PnP/PM Statistics (Optional). */ #define OID_PNP_WAKE_UP_OK 0xFD020200 #define OID_PNP_WAKE_UP_ERROR 0xFD020201 /* The following bits are defined for OID_PNP_ENABLE_WAKE_UP */ #define NDIS_PNP_WAKE_UP_MAGIC_PACKET 0x00000001 #define NDIS_PNP_WAKE_UP_PATTERN_MATCH 0x00000002 #define NDIS_PNP_WAKE_UP_LINK_CHANGE 0x00000004 /* 802.11 OIDs */ #define OID_802_11_BSSID 0x0D010101 #define OID_802_11_SSID 0x0D010102 #define OID_802_11_NETWORK_TYPES_SUPPORTED 0x0D010203 #define OID_802_11_NETWORK_TYPE_IN_USE 0x0D010204 #define OID_802_11_TX_POWER_LEVEL 0x0D010205 #define OID_802_11_RSSI 0x0D010206 #define OID_802_11_RSSI_TRIGGER 0x0D010207 #define OID_802_11_INFRASTRUCTURE_MODE 0x0D010108 #define OID_802_11_FRAGMENTATION_THRESHOLD 0x0D010209 #define OID_802_11_RTS_THRESHOLD 0x0D01020A #define OID_802_11_NUMBER_OF_ANTENNAS 0x0D01020B #define OID_802_11_RX_ANTENNA_SELECTED 0x0D01020C #define OID_802_11_TX_ANTENNA_SELECTED 0x0D01020D #define OID_802_11_SUPPORTED_RATES 0x0D01020E #define OID_802_11_DESIRED_RATES 0x0D010210 #define OID_802_11_CONFIGURATION 0x0D010211 #define OID_802_11_STATISTICS 0x0D020212 #define OID_802_11_ADD_WEP 0x0D010113 #define OID_802_11_REMOVE_WEP 0x0D010114 #define OID_802_11_DISASSOCIATE 0x0D010115 #define OID_802_11_POWER_MODE 0x0D010216 #define OID_802_11_BSSID_LIST 0x0D010217 #define OID_802_11_AUTHENTICATION_MODE 0x0D010118 #define OID_802_11_PRIVACY_FILTER 0x0D010119 #define OID_802_11_BSSID_LIST_SCAN 0x0D01011A #define OID_802_11_WEP_STATUS 0x0D01011B #define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS #define OID_802_11_RELOAD_DEFAULTS 0x0D01011C #define OID_802_11_ADD_KEY 0x0D01011D #define OID_802_11_REMOVE_KEY 0x0D01011E #define OID_802_11_ASSOCIATION_INFORMATION 0x0D01011F #define OID_802_11_TEST 0x0D010120 +#define OID_802_11_CAPABILITY 0x0D010122 +#define OID_802_11_PMKID 0x0D010123 /* structures/definitions for 802.11 */ #define NDIS_80211_NETTYPE_11FH 0x00000000 #define NDIS_80211_NETTYPE_11DS 0x00000001 #define NDIS_80211_NETTYPE_11OFDM5 0x00000002 #define NDIS_80211_NETTYPE_11OFDM24 0x00000003 +#define NDIS_80211_NETTYPE_AUTO 0x00000004 struct ndis_80211_nettype_list { uint32_t ntl_items; uint32_t ntl_type[1]; }; #define NDIS_80211_POWERMODE_CAM 0x00000000 #define NDIS_80211_POWERMODE_MAX_PSP 0x00000001 #define NDIS_80211_POWERMODE_FAST_PSP 0x00000002 typedef uint32_t ndis_80211_power; /* Power in milliwatts */ typedef uint32_t ndis_80211_rssi; /* Signal strength in dBm */ struct ndis_80211_config_fh { uint32_t ncf_length; uint32_t ncf_hoppatterh; uint32_t ncf_hopset; uint32_t ncf_dwelltime; }; typedef struct ndis_80211_config_fh ndis_80211_config_fh; struct ndis_80211_config { uint32_t nc_length; uint32_t nc_beaconperiod; uint32_t nc_atimwin; uint32_t nc_dsconfig; ndis_80211_config_fh nc_fhconfig; }; typedef struct ndis_80211_config ndis_80211_config; struct ndis_80211_stats { uint32_t ns_length; uint64_t ns_txfragcnt; uint64_t ns_txmcastcnt; uint64_t ns_failedcnt; uint64_t ns_retrycnt; uint64_t ns_multiretrycnt; uint64_t ns_rtssuccesscnt; uint64_t ns_rtsfailcnt; uint64_t ns_ackfailcnt; uint64_t ns_dupeframecnt; uint64_t ns_rxfragcnt; uint64_t ns_rxmcastcnt; uint64_t ns_fcserrcnt; }; typedef struct ndis_80211_stats ndis_80211_stats; typedef uint32_t ndis_80211_key_idx; struct ndis_80211_wep { uint32_t nw_length; uint32_t nw_keyidx; uint32_t nw_keylen; uint8_t nw_keydata[256]; }; typedef struct ndis_80211_wep ndis_80211_wep; #define NDIS_80211_WEPKEY_TX 0x80000000 #define NDIS_80211_WEPKEY_PERCLIENT 0x40000000 #define NDIS_80211_NET_INFRA_IBSS 0x00000000 #define NDIS_80211_NET_INFRA_BSS 0x00000001 #define NDIS_80211_NET_INFRA_AUTO 0x00000002 #define NDIS_80211_AUTHMODE_OPEN 0x00000000 #define NDIS_80211_AUTHMODE_SHARED 0x00000001 #define NDIS_80211_AUTHMODE_AUTO 0x00000002 #define NDIS_80211_AUTHMODE_WPA 0x00000003 #define NDIS_80211_AUTHMODE_WPAPSK 0x00000004 #define NDIS_80211_AUTHMODE_WPANONE 0x00000005 +#define NDIS_80211_AUTHMODE_WPA2 0x00000006 +#define NDIS_80211_AUTHMODE_WPA2PSK 0x00000007 typedef uint8_t ndis_80211_rates[8]; typedef uint8_t ndis_80211_rates_ex[16]; typedef uint8_t ndis_80211_macaddr[6]; struct ndis_80211_ssid { uint32_t ns_ssidlen; uint8_t ns_ssid[32]; }; typedef struct ndis_80211_ssid ndis_80211_ssid; struct ndis_wlan_bssid { uint32_t nwb_length; ndis_80211_macaddr nwb_macaddr; uint8_t nwb_rsvd[2]; ndis_80211_ssid nwb_ssid; uint32_t nwb_privacy; ndis_80211_rssi nwb_rssi; uint32_t nwb_nettype; ndis_80211_config nwb_config; uint32_t nwb_netinfra; ndis_80211_rates nwb_supportedrates; }; typedef struct ndis_wlan_bssid ndis_wlan_bssid; struct ndis_80211_bssid_list { uint32_t nbl_items; ndis_wlan_bssid nbl_bssid[1]; }; typedef struct ndis_80211_bssid_list ndis_80211_bssid_list; struct ndis_wlan_bssid_ex { uint32_t nwbx_len; ndis_80211_macaddr nwbx_macaddr; uint8_t nwbx_rsvd[2]; ndis_80211_ssid nwbx_ssid; uint32_t nwbx_privacy; ndis_80211_rssi nwbx_rssi; uint32_t nwbx_nettype; ndis_80211_config nwbx_config; uint32_t nwbx_netinfra; ndis_80211_rates_ex nwbx_supportedrates; uint32_t nwbx_ielen; uint32_t nwbx_ies[1]; }; typedef struct ndis_wlan_bssid_ex ndis_wlan_bssid_ex; struct ndis_80211_bssid_list_ex { uint32_t nblx_items; ndis_wlan_bssid_ex nblx_bssid[1]; }; typedef struct ndis_80211_bssid_list_ex ndis_80211_bssid_list_ex; struct ndis_80211_fixed_ies { uint8_t nfi_tstamp[8]; uint16_t nfi_beaconint; uint16_t nfi_caps; }; struct ndis_80211_variable_ies { uint8_t nvi_elemid; uint8_t nvi_len; uint8_t nvi_data[1]; }; typedef uint32_t ndis_80211_fragthresh; typedef uint32_t ndis_80211_rtsthresh; typedef uint32_t ndis_80211_antenna; #define NDIS_80211_PRIVFILT_ACCEPTALL 0x00000000 #define NDIS_80211_PRIVFILT_8021XWEP 0x00000001 #define NDIS_80211_WEPSTAT_ENABLED 0x00000000 #define NDIS_80211_WEPSTAT_ENC1ENABLED NDIS_80211_WEPSTAT_ENABLED #define NDIS_80211_WEPSTAT_DISABLED 0x00000001 #define NDIS_80211_WEPSTAT_ENCDISABLED NDIS_80211_WEPSTAT_DISABLED #define NDIS_80211_WEPSTAT_KEYABSENT 0x00000002 #define NDIS_80211_WEPSTAT_ENC1KEYABSENT NDIS_80211_WEPSTAT_KEYABSENT #define NDIS_80211_WEPSTAT_NOTSUPPORTED 0x00000003 #define NDIS_80211_WEPSTAT_ENCNOTSUPPORTED NDIS_80211_WEPSTAT_NOTSUPPORTED #define NDIS_80211_WEPSTAT_ENC2ENABLED 0x00000004 #define NDIS_80211_WEPSTAT_ENC2KEYABSENT 0x00000005 #define NDIS_80211_WEPSTAT_ENC3ENABLED 0x00000006 #define NDIS_80211_WEPSTAT_ENC3KEYABSENT 0x00000007 #define NDIS_80211_RELOADDEFAULT_WEP 0x00000000 #define NDIS_80211_STATUSTYPE_AUTH 0x00000000 +#define NDIS_80211_STATUSTYPE_PMKIDLIST 0x00000001 struct ndis_80211_status_indication { uint32_t nsi_type; }; typedef struct ndis_80211_status_indication ndis_80211_status_indication; +#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 +#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 +#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 +#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E + struct ndis_80211_auth_request { uint32_t nar_len; ndis_80211_macaddr nar_bssid; uint32_t nar_flags; }; typedef struct ndis_80211_auth_request ndis_80211_auth_request; struct ndis_80211_key { uint32_t nk_len; uint32_t nk_keyidx; uint32_t nk_keylen; ndis_80211_macaddr nk_bssid; + uint8_t nk_pad[6]; uint64_t nk_keyrsc; - uint8_t nk_keydata[256]; + uint8_t nk_keydata[32]; }; typedef struct ndis_80211_key ndis_80211_key; struct ndis_80211_remove_key { uint32_t nk_len; uint32_t nk_keyidx; ndis_80211_macaddr nk_bssid; }; typedef struct ndis_80211_remove_key ndis_80211_remove_key; #define NDIS_80211_AI_REQFI_CAPABILITIES 0x00000001 #define NDIS_80211_AI_REQFI_LISTENINTERVAL 0x00000002 #define NDIS_80211_AI_REQFI_CURRENTAPADDRESS 0x00000004 #define NDIS_80211_AI_RESFI_CAPABILITIES 0x00000001 #define NDIS_80211_AI_RESFI_STATUSCODE 0x00000002 #define NDIS_80211_AI_RESFI_ASSOCIATIONID 0x00000004 struct ndis_80211_ai_reqfi { uint16_t naq_caps; uint16_t naq_listentint; ndis_80211_macaddr naq_currentapaddr; }; typedef struct ndis_80211_ai_reqfi ndis_80211_ai_reqfi; struct ndis_80211_ai_resfi { uint16_t nas_caps; uint16_t nas_statuscode; uint16_t nas_associd; }; typedef struct ndis_80211_ai_resfi ndis_80211_ai_resfi; struct ndis_80211_assoc_info { uint32_t nai_len; uint16_t nai_avail_req_fixed_ies; ndis_80211_ai_reqfi nai_req_fixed_ies; uint32_t nai_req_ielen; uint32_t nai_offset_req_ies; uint16_t nai_avail_resp_fixed_ies; ndis_80211_ai_resfi nai_resp_fixed_iex; uint32_t nai_resp_ielen; uint32_t nai_offset_resp_ies; }; typedef struct ndis_80211_assoc_info ndis_80211_assoc_info; struct ndis_80211_auth_event { ndis_80211_status_indication nae_status; ndis_80211_auth_request nae_request[1]; }; typedef struct ndis_80211_auth_event ndis_80211_auth_event; struct ndis_80211_test { uint32_t nt_len; uint32_t nt_type; union { ndis_80211_auth_event nt_authevent; uint32_t nt_rssitrigger; } u; }; typedef struct ndis_80211_test ndis_80211_test; +struct ndis_80211_auth_encrypt { + uint32_t ne_authmode; + uint32_t ne_cryptstat; +}; + +typedef struct ndis_80211_auth_encrypt ndis_80211_auth_encrypt; + +struct ndis_80211_caps { + uint32_t nc_len; + uint32_t nc_ver; + uint32_t nc_numpmkids; + ndis_80211_auth_encrypt nc_authencs[1]; +}; + +typedef struct ndis_80211_caps ndis_80211_caps; + +struct ndis_80211_bssidinfo { + ndis_80211_macaddr nb_bssid; + uint8_t nb_pmkid[16]; +}; + +typedef struct ndis_80211_bssidinfo ndis_80211_bssidinfo; + +struct ndis_80211_pmkid { + uint32_t np_len; + uint32_t np_bssidcnt; + ndis_80211_bssidinfo np_bssidinfo[1]; +}; + +typedef struct ndis_80211_pmkid ndis_80211_pmkid; + +struct ndis_80211_pmkid_cand { + ndis_80211_macaddr npc_bssid; + uint32_t npc_flags; +}; + +typedef struct ndis_80211_pmkid_cand ndis_80211_pmkid_cand; + +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED (0x01) + +struct ndis_80211_pmkid_candidate_list { + uint32_t npcl_version; + uint32_t npcl_numcandidates; + ndis_80211_pmkid_cand npcl_candidatelist[1]; +}; + +typedef struct ndis_80211_pmkid_candidate_list ndis_80211_pmkid_candidate_list; + +struct ndis_80211_enc_indication { + uint32_t nei_statustype; + ndis_80211_pmkid_candidate_list nei_pmkidlist; +}; + +typedef struct ndis_80211_enc_indication ndis_80211_enc_indication; + /* TCP OIDs. */ #define OID_TCP_TASK_OFFLOAD 0xFC010201 #define OID_TCP_TASK_IPSEC_ADD_SA 0xFC010202 #define OID_TCP_TASK_IPSEC_DELETE_SA 0xFC010203 #define OID_TCP_SAN_SUPPORT 0xFC010204 #define NDIS_TASK_OFFLOAD_VERSION 1 #define NDIS_TASK_TCPIP_CSUM 0x00000000 #define NDIS_TASK_IPSEC 0x00000001 #define NDIS_TASK_TCP_LARGESEND 0x00000002 #define NDIS_ENCAP_UNSPEC 0x00000000 #define NDIS_ENCAP_NULL 0x00000001 #define NDIS_ENCAP_IEEE802_3 0x00000002 #define NDIS_ENCAP_IEEE802_5 0x00000003 #define NDIS_ENCAP_SNAP_ROUTED 0x00000004 #define NDIS_ENCAP_SNAP_BRIDGED 0x00000005 #define NDIS_ENCAPFLAG_FIXEDHDRLEN 0x00000001 struct ndis_encap_fmt { uint32_t nef_encap; uint32_t nef_flags; uint32_t nef_encaphdrlen; }; typedef struct ndis_encap_fmt ndis_encap_fmt; struct ndis_task_offload_hdr { uint32_t ntoh_vers; uint32_t ntoh_len; uint32_t ntoh_rsvd; uint32_t ntoh_offset_firsttask; ndis_encap_fmt ntoh_encapfmt; }; typedef struct ndis_task_offload_hdr ndis_task_offload_hdr; struct ndis_task_offload { uint32_t nto_vers; uint32_t nto_len; uint32_t nto_task; uint32_t nto_offset_nexttask; uint32_t nto_taskbuflen; uint8_t nto_taskbuf[1]; }; typedef struct ndis_task_offload ndis_task_offload; #define NDIS_TCPSUM_FLAGS_IP_OPTS 0x00000001 #define NDIS_TCPSUM_FLAGS_TCP_OPTS 0x00000002 #define NDIS_TCPSUM_FLAGS_TCP_CSUM 0x00000004 #define NDIS_TCPSUM_FLAGS_UDP_CSUM 0x00000008 #define NDIS_TCPSUM_FLAGS_IP_CSUM 0x00000010 struct ndis_task_tcpip_csum { uint32_t nttc_v4tx; uint32_t nttc_v4rx; uint32_t nttc_v6tx; uint32_t nttc_v6rx; }; typedef struct ndis_task_tcpip_csum ndis_task_tcpip_csum; struct ndis_task_tcp_largesend { uint32_t nttl_vers; uint32_t nttl_maxofflen; uint32_t nttl_minsegcnt; uint8_t nttl_tcpopt; uint8_t nttl_ipopt; }; typedef struct ndis_task_tcp_largesend ndis_task_tcp_largesend; #define NDIS_IPSEC_AH_MD5 0x00000001 #define NDIS_IPSEC_AH_SHA1 0x00000002 #define NDIS_IPSEC_AH_TRANSPORT 0x00000004 #define NDIS_IPSEC_AH_TUNNEL 0x00000008 #define NDIS_IPSEC_AH_SEND 0x00000010 #define NDIS_IPSEC_AH_RECEIVE 0x00000020 #define NDIS_IPSEC_ESP_DES 0x00000001 #define NDIS_IPSEC_ESP_RSVD 0x00000002 #define NDIS_IPSEC_ESP_3DES 0x00000004 #define NDIS_IPSEC_ESP_NULL 0x00000008 #define NDIS_IPSEC_ESP_TRANSPORT 0x00000010 #define NDIS_IPSEC_ESP_TUNNEL 0x00000020 #define NDIS_IPSEC_ESP_SEND 0x00000040 #define NDIS_IPSEC_ESP_RECEIVE 0x00000080 struct ndis_task_ipsec { uint32_t nti_ah_esp_combined; uint32_t nti_ah_transport_tunnel_combined; uint32_t nti_v4_options; uint32_t nti_rsvd; uint32_t nti_v4ah; uint32_t nti_v4esp; }; typedef struct ndis_task_ipsec ndis_task_ipsec; /* * Attribures of NDIS drivers. Not all drivers support * all attributes. */ +#define NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT 0x00000001 #define NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT 0x00000002 #define NDIS_ATTRIBUTE_IGNORE_TOKEN_RING_ERRORS 0x00000004 #define NDIS_ATTRIBUTE_BUS_MASTER 0x00000008 #define NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER 0x00000010 #define NDIS_ATTRIBUTE_DESERIALIZE 0x00000020 #define NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND 0x00000040 #define NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK 0x00000080 #define NDIS_ATTRIBUTE_NOT_CO_NDIS 0x00000100 #define NDIS_ATTRIBUTE_USES_SAFE_BUFFER_APIS 0x00000200 #define NDIS_SERIALIZED(block) \ (((block)->nmb_flags & NDIS_ATTRIBUTE_DESERIALIZE) == 0) enum ndis_media_state { nmc_connected, nmc_disconnected }; typedef enum ndis_media_state ndis_media_state; /* Ndis Packet Filter Bits (OID_GEN_CURRENT_PACKET_FILTER). */ #define NDIS_PACKET_TYPE_DIRECTED 0x00000001 #define NDIS_PACKET_TYPE_MULTICAST 0x00000002 #define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 #define NDIS_PACKET_TYPE_BROADCAST 0x00000008 #define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 #define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 #define NDIS_PACKET_TYPE_SMT 0x00000040 #define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 #define NDIS_PACKET_TYPE_GROUP 0x00001000 #define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00002000 #define NDIS_PACKET_TYPE_FUNCTIONAL 0x00004000 #define NDIS_PACKET_TYPE_MAC_FRAME 0x00008000 /* Ndis MAC option bits (OID_GEN_MAC_OPTIONS). */ #define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001 #define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002 #define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004 #define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008 #define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010 #define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020 #define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040 #define NDIS_MAC_OPTION_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00000080 #define NDIS_MAC_OPTION_RECEIVE_AT_DPC 0x00000100 #define NDIS_MAC_OPTION_8021Q_VLAN 0x00000200 #define NDIS_MAC_OPTION_RESERVED 0x80000000 #define NDIS_DMA_24BITS 0x00 #define NDIS_DMA_32BITS 0x01 #define NDIS_DMA_64BITS 0x02 /* struct ndis_physaddr { #ifdef __i386__ uint64_t np_quad; #endif #ifdef __amd64__ uint32_t np_low; uint32_t np_high; #define np_quad np_low #endif #ifdef notdef uint32_t np_low; uint32_t np_high; #endif }; */ typedef struct physaddr ndis_physaddr; struct ndis_ansi_string { uint16_t nas_len; uint16_t nas_maxlen; char *nas_buf; }; typedef struct ndis_ansi_string ndis_ansi_string; #ifdef notdef /* * nus_buf is really a wchar_t *, but it's inconvenient to include * all the necessary header goop needed to define it, and it's a * pointer anyway, so for now, just make it a uint16_t *. */ struct ndis_unicode_string { uint16_t nus_len; uint16_t nus_maxlen; uint16_t *nus_buf; }; typedef struct ndis_unicode_string ndis_unicode_string; #endif typedef unicode_string ndis_unicode_string; enum ndis_parm_type { ndis_parm_int, ndis_parm_hexint, ndis_parm_string, ndis_parm_multistring, ndis_parm_binary }; typedef enum ndis_parm_type ndis_parm_type; struct ndis_binary_data { uint16_t nbd_len; void *nbd_buf; }; typedef struct ndis_binary_data ndis_binary_data; struct ndis_config_parm { ndis_parm_type ncp_type; union { uint32_t ncp_intdata; ndis_unicode_string ncp_stringdata; ndis_binary_data ncp_binarydata; } ncp_parmdata; }; +/* + * Not part of Windows NDIS spec; we uses this to keep a + * list of ndis_config_parm structures that we've allocated. + */ + typedef struct ndis_config_parm ndis_config_parm; +struct ndis_parmlist_entry { + list_entry np_list; + ndis_config_parm np_parm; +}; + +typedef struct ndis_parmlist_entry ndis_parmlist_entry; + #ifdef notdef struct ndis_list_entry { struct ndis_list_entry *nle_flink; struct ndis_list_entry *nle_blink; }; typedef struct ndis_list_entry ndis_list_entry; #endif struct ndis_bind_paths { uint32_t nbp_number; ndis_unicode_string nbp_paths[1]; }; typedef struct ndis_bind_paths ndis_bind_paths; #ifdef notdef struct dispatch_header { uint8_t dh_type; uint8_t dh_abs; uint8_t dh_size; uint8_t dh_inserted; uint32_t dh_sigstate; list_entry dh_waitlisthead; }; #endif #define dispatch_header nt_dispatch_header struct ndis_ktimer { struct dispatch_header nk_header; uint64_t nk_duetime; list_entry nk_timerlistentry; void *nk_dpc; uint32_t nk_period; }; struct ndis_kevent { struct dispatch_header nk_header; }; struct ndis_event { struct nt_kevent ne_event; }; typedef struct ndis_event ndis_event; /* Kernel defered procedure call (i.e. timer callback) */ struct ndis_kdpc; typedef void (*ndis_kdpc_func)(struct ndis_kdpc *, void *, void *, void *); struct ndis_kdpc { uint16_t nk_type; uint8_t nk_num; uint8_t nk_importance; list_entry nk_dpclistentry; ndis_kdpc_func nk_deferedfunc; void *nk_deferredctx; void *nk_sysarg1; void *nk_sysarg2; uint32_t *nk_lock; }; struct ndis_timer { struct ktimer nt_ktimer; struct kdpc nt_kdpc; }; typedef struct ndis_timer ndis_timer; typedef void (*ndis_timer_function)(void *, void *, void *, void *); struct ndis_miniport_timer { struct ktimer nmt_ktimer; struct kdpc nmt_kdpc; ndis_timer_function nmt_timerfunc; void *nmt_timerctx; ndis_miniport_block *nmt_block; struct ndis_miniport_timer *nmt_nexttimer; }; typedef struct ndis_miniport_timer ndis_miniport_timer; struct ndis_spin_lock { ndis_kspin_lock nsl_spinlock; ndis_kirql nsl_kirql; }; typedef struct ndis_spin_lock ndis_spin_lock; struct ndis_rw_lock { union { kspin_lock nrl_spinlock; void *nrl_ctx; } u; uint8_t nrl_rsvd[16]; }; #define nrl_spinlock u.nrl_spinlock #define nrl_ctx u.nrl_ctx; typedef struct ndis_rw_lock ndis_rw_lock; struct ndis_lock_state { uint16_t nls_lockstate; ndis_kirql nls_oldirql; }; typedef struct ndis_lock_state ndis_lock_state; struct ndis_request { uint8_t nr_macreserved[4*sizeof(void *)]; uint32_t nr_requesttype; union _ndis_data { struct _ndis_query_information { ndis_oid nr_oid; void *nr_infobuf; uint32_t nr_infobuflen; uint32_t nr_byteswritten; uint32_t nr_bytesneeded; } ndis_query_information; struct _ndis_set_information { ndis_oid nr_oid; void *nr_infobuf; uint32_t nr_infobuflen; uint32_t nr_byteswritten; uint32_t nr_bytesneeded; } ndis_set_information; } ndis_data; /* NDIS 5.0 extentions */ uint8_t nr_ndis_rsvd[9 * sizeof(void *)]; union { uint8_t nr_callmgr_rsvd[2 * sizeof(void *)]; uint8_t nr_protocol_rsvd[2 * sizeof(void *)]; } u; uint8_t nr_miniport_rsvd[2 * sizeof(void *)]; }; typedef struct ndis_request ndis_request; /* * Filler, not used. */ struct ndis_miniport_interrupt { - void *ni_introbj; + kinterrupt *ni_introbj; ndis_kspin_lock ni_dpccountlock; void *ni_rsvd; void *ni_isrfunc; void *ni_dpcfunc; - struct ndis_kdpc ni_dpc; + kdpc ni_dpc; ndis_miniport_block *ni_block; uint8_t ni_dpccnt; uint8_t ni_filler1; - struct ndis_kevent ni_dpcsdoneevent; + struct nt_kevent ni_dpcevt; uint8_t ni_shared; uint8_t ni_isrreq; }; typedef struct ndis_miniport_interrupt ndis_miniport_interrupt; enum ndis_interrupt_mode { nim_level, nim_latched }; typedef enum ndis_interrupt_mode ndis_interrupt_mode; #define NUMBER_OF_SINGLE_WORK_ITEMS 6 struct ndis_work_item; typedef void (*ndis_proc)(struct ndis_work_item *, void *); struct ndis_work_item { void *nwi_ctx; ndis_proc nwi_func; uint8_t nwi_wraprsvd[sizeof(void *) * 8]; }; typedef struct ndis_work_item ndis_work_item; #define NdisInitializeWorkItem(w, f, c) \ do { \ (w)->nwi_ctx = c; \ (w)->nwi_func = f; \ } while (0) #ifdef notdef struct ndis_buffer { struct ndis_buffer *nb_next; uint16_t nb_size; uint16_t nb_flags; void *nb_process; void *nb_mappedsystemva; void *nb_startva; uint32_t nb_bytecount; uint32_t nb_byteoffset; }; typedef struct ndis_buffer ndis_buffer; #endif struct ndis_sc_element { ndis_physaddr nse_addr; uint32_t nse_len; uint32_t *nse_rsvd; }; typedef struct ndis_sc_element ndis_sc_element; #define NDIS_MAXSEG 32 #define NDIS_BUS_SPACE_SHARED_MAXADDR 0x3E7FFFFF struct ndis_sc_list { uint32_t nsl_frags; uint32_t *nsl_rsvd; ndis_sc_element nsl_elements[NDIS_MAXSEG]; }; typedef struct ndis_sc_list ndis_sc_list; struct ndis_tcpip_csum { union { uint32_t ntc_txflags; uint32_t ntc_rxflags; uint32_t ntc_val; } u; }; typedef struct ndis_tcpip_csum ndis_tcpip_csum; #define NDIS_TXCSUM_DO_IPV4 0x00000001 #define NDIS_TXCSUM_DO_IPV6 0x00000002 #define NDIS_TXCSUM_DO_TCP 0x00000004 #define NDIS_TXCSUM_DO_UDP 0x00000008 #define NDIS_TXCSUM_DO_IP 0x00000010 #define NDIS_RXCSUM_TCP_FAILED 0x00000001 #define NDIS_RXCSUM_UDP_FAILED 0x00000002 #define NDIS_RXCSUM_IP_FAILED 0x00000004 #define NDIS_RXCSUM_TCP_PASSED 0x00000008 #define NDIS_RXCSUM_UDP_PASSED 0x00000010 #define NDIS_RXCSUM_IP_PASSED 0x00000020 #define NDIS_RXCSUM_LOOPBACK 0x00000040 struct ndis_vlan { union { struct { uint32_t nvt_userprio:3; uint32_t nvt_canformatid:1; uint32_t nvt_vlanid:12; uint32_t nvt_rsvd:16; } nv_taghdr; } u; }; typedef struct ndis_vlan ndis_vlan; enum ndis_perpkt_info { ndis_tcpipcsum_info, ndis_ipsec_info, ndis_largesend_info, ndis_classhandle_info, ndis_rsvd, ndis_sclist_info, ndis_ieee8021q_info, ndis_originalpkt_info, ndis_packetcancelid, ndis_maxpkt_info }; typedef enum ndis_perpkt_info ndis_perpkt_info; struct ndis_packet_extension { void *npe_info[ndis_maxpkt_info]; }; typedef struct ndis_packet_extension ndis_packet_extension; struct ndis_packet_private { uint32_t npp_physcnt; uint32_t npp_totlen; ndis_buffer *npp_head; ndis_buffer *npp_tail; void *npp_pool; uint32_t npp_count; uint32_t npp_flags; uint8_t npp_validcounts; uint8_t npp_ndispktflags; uint16_t npp_packetooboffset; }; #define NDIS_FLAGS_PROTOCOL_ID_MASK 0x0000000F #define NDIS_FLAGS_MULTICAST_PACKET 0x00000010 #define NDIS_FLAGS_RESERVED2 0x00000020 #define NDIS_FLAGS_RESERVED3 0x00000040 #define NDIS_FLAGS_DONT_LOOPBACK 0x00000080 #define NDIS_FLAGS_IS_LOOPBACK_PACKET 0x00000100 #define NDIS_FLAGS_LOOPBACK_ONLY 0x00000200 #define NDIS_FLAGS_RESERVED4 0x00000400 #define NDIS_FLAGS_DOUBLE_BUFFERED 0x00000800 #define NDIS_FLAGS_SENT_AT_DPC 0x00001000 #define NDIS_FLAGS_USES_SG_BUFFER_LIST 0x00002000 #define NDIS_PACKET_WRAPPER_RESERVED 0x3F #define NDIS_PACKET_CONTAINS_MEDIA_SPECIFIC_INFO 0x40 #define NDIS_PACKET_ALLOCATED_BY_NDIS 0x80 #define NDIS_PROTOCOL_ID_DEFAULT 0x00 #define NDIS_PROTOCOL_ID_TCP_IP 0x02 #define NDIS_PROTOCOL_ID_IPX 0x06 #define NDIS_PROTOCOL_ID_NBF 0x07 #define NDIS_PROTOCOL_ID_MAX 0x0F #define NDIS_PROTOCOL_ID_MASK 0x0F typedef struct ndis_packet_private ndis_packet_private; enum ndis_classid { ndis_class_802_3prio, ndis_class_wirelesswan_mbx, ndis_class_irda_packetinfo, ndis_class_atm_aainfo }; typedef enum ndis_classid ndis_classid; struct ndis_mediaspecific_info { uint32_t nmi_nextentoffset; ndis_classid nmi_classid; uint32_t nmi_size; uint8_t nmi_classinfo[1]; }; typedef struct ndis_mediaspecific_info ndis_mediaspecific_info; struct ndis_packet_oob { union { uint64_t npo_timetotx; uint64_t npo_timetxed; } u; uint64_t npo_timerxed; uint32_t npo_hdrlen; uint32_t npo_mediaspecific_len; void *npo_mediaspecific; ndis_status npo_status; }; typedef struct ndis_packet_oob ndis_packet_oob; /* * Our protocol private region for handling ethernet. * We need this to stash some of the things returned * by NdisMEthIndicateReceive(). */ struct ndis_ethpriv { void *nep_ctx; /* packet context */ long nep_offset; /* residual data to transfer */ void *nep_pad[2]; }; typedef struct ndis_ethpriv ndis_ethpriv; #define PROTOCOL_RESERVED_SIZE_IN_PACKET (4 * sizeof(void *)) struct ndis_packet { ndis_packet_private np_private; union { /* For connectionless miniports. */ struct { uint8_t np_miniport_rsvd[2 * sizeof(void *)]; uint8_t np_wrapper_rsvd[2 * sizeof(void *)]; } np_clrsvd; /* For de-serialized miniports */ struct { uint8_t np_miniport_rsvdex[3 * sizeof(void *)]; uint8_t np_wrapper_rsvdex[sizeof(void *)]; } np_dsrsvd; struct { uint8_t np_mac_rsvd[4 * sizeof(void *)]; } np_macrsvd; } u; uint32_t *np_rsvd[2]; uint8_t np_protocolreserved[PROTOCOL_RESERVED_SIZE_IN_PACKET]; /* * This next part is probably wrong, but we need some place * to put the out of band data structure... */ ndis_packet_oob np_oob; ndis_packet_extension np_ext; ndis_sc_list np_sclist; /* BSD-specific stuff which should be invisible to drivers. */ uint32_t np_refcnt; void *np_softc; void *np_m0; int np_txidx; - kdpc np_dpc; - kspin_lock np_lock; + list_entry np_list; }; typedef struct ndis_packet ndis_packet; +struct ndis_packet_pool { + slist_header np_head; + int np_dead; + nt_kevent np_event; + kspin_lock np_lock; + int np_cnt; + int np_len; + int np_protrsvd; + void *np_pktmem; +}; + +typedef struct ndis_packet_pool ndis_packet_pool; + /* mbuf ext type for NDIS */ #define EXT_NDIS 0x999 /* mtx type for NDIS */ #define MTX_NDIS_LOCK "NDIS lock" struct ndis_filterdbs { union { void *nf_ethdb; void *nf_nulldb; } u; void *nf_trdb; void *nf_fddidb; void *nf_arcdb; }; typedef struct ndis_filterdbs ndis_filterdbs; #define nf_ethdb u.nf_ethdb enum ndis_medium { NdisMedium802_3, NdisMedium802_5, NdisMediumFddi, NdisMediumWan, NdisMediumLocalTalk, NdisMediumDix, /* defined for convenience, not a real medium */ NdisMediumArcnetRaw, NdisMediumArcnet878_2, NdisMediumAtm, NdisMediumWirelessWan, NdisMediumIrda, NdisMediumBpc, NdisMediumCoWan, NdisMedium1394, NdisMediumMax }; typedef enum ndis_medium ndis_medium; /* enum interface_type { InterfaceTypeUndefined = -1, Internal, Isa, Eisa, MicroChannel, TurboChannel, PCIBus, VMEBus, NuBus, PCMCIABus, CBus, MPIBus, MPSABus, ProcessorInternal, InternalPowerBus, PNPISABus, PNPBus, MaximumInterfaceType }; */ enum ndis_interface_type { NdisInterfaceInternal = Internal, NdisInterfaceIsa = Isa, NdisInterfaceEisa = Eisa, NdisInterfaceMca = MicroChannel, NdisInterfaceTurboChannel = TurboChannel, NdisInterfacePci = PCIBus, NdisInterfacePcMcia = PCMCIABus }; typedef enum ndis_interface_type ndis_interface_type; struct ndis_paddr_unit { ndis_physaddr npu_physaddr; uint32_t npu_len; }; typedef struct ndis_paddr_unit ndis_paddr_unit; struct ndis_map_arg { ndis_paddr_unit *nma_fraglist; int nma_cnt; int nma_max; }; /* * Miniport characteristics were originally defined in the NDIS 3.0 * spec and then extended twice, in NDIS 4.0 and 5.0. */ struct ndis_miniport_characteristics { /* NDIS 3.0 */ uint8_t nmc_version_major; uint8_t nmc_version_minor; uint16_t nmc_pad; uint32_t nmc_rsvd; void * nmc_checkhang_func; void * nmc_disable_interrupts_func; void * nmc_enable_interrupts_func; void * nmc_halt_func; void * nmc_interrupt_func; void * nmc_init_func; void * nmc_isr_func; void * nmc_queryinfo_func; void * nmc_reconfig_func; void * nmc_reset_func; void * nmc_sendsingle_func; void * nmc_setinfo_func; void * nmc_transferdata_func; /* NDIS 4.0 extentions */ void * nmc_return_packet_func; void * nmc_sendmulti_func; void * nmc_allocate_complete_func; /* NDIS 5.0 extensions */ void * nmc_cocreatevc_func; void * nmc_codeletevc_func; void * nmc_coactivatevc_func; void * nmc_codeactivatevc_func; void * nmc_comultisend_func; void * nmc_corequest_func; /* NDIS 5.1 extentions */ void * nmc_canceltxpkts_handler; void * nmc_pnpevent_handler; void * nmc_shutdown_handler; void * nmc_rsvd0; void * nmc_rsvd1; void * nmc_rsvd2; void * nmc_rsvd3; }; typedef struct ndis_miniport_characteristics ndis_miniport_characteristics; struct ndis_driver_object { char *ndo_ifname; void *ndo_softc; ndis_miniport_characteristics ndo_chars; }; typedef struct ndis_driver_object ndis_driver_object; struct ndis_reference { ndis_kspin_lock nr_spinlock; uint16_t nr_refcnt; uint8_t nr_closing; }; typedef struct ndis_reference ndis_reference; struct ndis_timer_entry { struct callout nte_ch; ndis_miniport_timer *nte_timer; TAILQ_ENTRY(ndis_timer_entry) link; }; TAILQ_HEAD(nte_head, ndis_timer_entry); #define NDIS_FH_TYPE_VFS 0 #define NDIS_FH_TYPE_MODULE 1 struct ndis_fh { int nf_type; char *nf_name; void *nf_vp; void *nf_map; uint32_t nf_maplen; }; typedef struct ndis_fh ndis_fh; /* * The miniport block is basically the internal NDIS handle. We need * to define this because, unfortunately, it is not entirely opaque * to NDIS drivers. For one thing, it contains the function pointer * to the NDIS packet receive handler, which is invoked out of the * NDIS block via a macro rather than a function pointer. (The * NdisMIndicateReceivePacket() routine is a macro rather than * a function.) For another, the driver maintains a pointer to the * miniport block and passes it as a handle to various NDIS functions. * (The driver never really knows this because it's hidden behind * an ndis_handle though.) * * The miniport block has two parts: the first part contains fields * that must never change, since they are referenced by driver * binaries through macros. The second part is ignored by the driver, * but contains various things used internaly by NDIS.SYS. In our * case, we define the first 'immutable' part exactly as it appears * in Windows, but don't bother duplicating the Windows definitions * for the second part. Instead, we replace them with a few BSD-specific * things. */ struct ndis_miniport_block { /* * Windows-specific portion -- DO NOT MODIFY OR NDIS * DRIVERS WILL NOT WORK. */ void *nmb_signature; /* magic number */ ndis_miniport_block *nmb_nextminiport; ndis_mdriver_block *nmb_driverhandle; ndis_handle nmb_miniportadapterctx; ndis_unicode_string nmb_name; ndis_bind_paths *nmb_bindpaths; ndis_handle nmb_openqueue; ndis_reference nmb_ref; ndis_handle nmb_devicectx; uint8_t nmb_padding; uint8_t nmb_lockacquired; uint8_t nmb_pmodeopens; uint8_t nmb_assignedcpu; ndis_kspin_lock nmb_lock; ndis_request *nmb_mediarequest; ndis_miniport_interrupt *nmb_interrupt; uint32_t nmb_flags; uint32_t nmb_pnpflags; list_entry nmb_packetlist; ndis_packet *nmb_firstpendingtxpacket; ndis_packet *nmb_returnpacketqueue; uint32_t nmb_requestbuffer; void *nmb_setmcastbuf; ndis_miniport_block *nmb_primaryminiport; void *nmb_wrapperctx; void *nmb_busdatactx; uint32_t nmb_pnpcaps; cm_resource_list *nmb_resources; ndis_timer nmb_wkupdpctimer; ndis_unicode_string nmb_basename; ndis_unicode_string nmb_symlinkname; uint32_t nmb_checkforhangsecs; uint16_t nmb_cfhticks; uint16_t nmb_cfhcurrticks; ndis_status nmb_resetstatus; ndis_handle nmb_resetopen; ndis_filterdbs nmb_filterdbs; void *nmb_pktind_func; void *nmb_senddone_func; void *nmb_sendrsrc_func; void *nmb_resetdone_func; ndis_medium nmb_medium; uint32_t nmb_busnum; uint32_t nmb_bustype; uint32_t nmb_adaptertype; device_object *nmb_deviceobj; /* Functional device */ device_object *nmb_physdeviceobj; /* Physical device */ device_object *nmb_nextdeviceobj; /* Next dev in stack */ void *nmb_mapreg; void *nmb_callmgraflist; void *nmb_miniportthread; void *nmb_setinfobuf; uint16_t nmb_setinfobuflen; uint16_t nmb_maxsendpkts; ndis_status nmb_fakestatus; void *nmb_lockhandler; ndis_unicode_string *nmb_adapterinstancename; void *nmb_timerqueue; uint32_t nmb_mactoptions; ndis_request *nmb_pendingreq; uint32_t nmb_maxlongaddrs; uint32_t nmb_maxshortaddrs; uint32_t nmb_currlookahead; uint32_t nmb_maxlookahead; void *nmb_interrupt_func; void *nmb_disableintr_func; void *nmb_enableintr_func; void *nmb_sendpkts_func; void *nmb_deferredsend_func; void *nmb_ethrxindicate_func; void *nmb_txrxindicate_func; void *nmb_fddirxindicate_func; void *nmb_ethrxdone_func; void *nmb_txrxdone_func; void *nmb_fddirxcond_func; void *nmb_status_func; void *nmb_statusdone_func; void *nmb_tdcond_func; void *nmb_querydone_func; void *nmb_setdone_func; void *nmb_wantxdone_func; void *nmb_wanrx_func; void *nmb_wanrxdone_func; /* * End of windows-specific portion of miniport block. Everything * below is BSD-specific. */ - uint8_t nmb_dummybuf[128]; - ndis_config_parm nmb_replyparm; + list_entry nmb_parmlist; ndis_resource_list *nmb_rlist; ndis_status nmb_getstat; + nt_kevent nmb_getevent; ndis_status nmb_setstat; + nt_kevent nmb_setevent; + nt_kevent nmb_resetevent; + io_workitem *nmb_returnitem; ndis_miniport_timer *nmb_timerlist; ndis_handle nmb_rxpool; + list_entry nmb_returnlist; + kspin_lock nmb_returnlock; TAILQ_ENTRY(ndis_miniport_block) link; }; TAILQ_HEAD(nd_head, ndis_miniport_block); typedef ndis_status (*ndis_init_handler)(ndis_status *, uint32_t *, ndis_medium *, uint32_t, ndis_handle, ndis_handle); typedef ndis_status (*ndis_queryinfo_handler)(ndis_handle, ndis_oid, void *, uint32_t, uint32_t *, uint32_t *); typedef ndis_status (*ndis_setinfo_handler)(ndis_handle, ndis_oid, void *, uint32_t, uint32_t *, uint32_t *); typedef ndis_status (*ndis_sendsingle_handler)(ndis_handle, ndis_packet *, uint32_t); typedef ndis_status (*ndis_sendmulti_handler)(ndis_handle, ndis_packet **, uint32_t); typedef void (*ndis_isr_handler)(uint8_t *, uint8_t *, ndis_handle); typedef void (*ndis_interrupt_handler)(ndis_handle); typedef int (*ndis_reset_handler)(uint8_t *, ndis_handle); typedef void (*ndis_halt_handler)(ndis_handle); typedef void (*ndis_return_handler)(ndis_handle, ndis_packet *); typedef void (*ndis_enable_interrupts_handler)(ndis_handle); typedef void (*ndis_disable_interrupts_handler)(ndis_handle); typedef void (*ndis_shutdown_handler)(void *); typedef void (*ndis_allocdone_handler)(ndis_handle, void *, ndis_physaddr *, uint32_t, void *); typedef uint8_t (*ndis_checkforhang_handler)(ndis_handle); typedef ndis_status (*driver_entry)(void *, unicode_string *); extern image_patch_table ndis_functbl[]; #define NDIS_TASKQUEUE 1 #define NDIS_SWI 2 #define NDIS_PSTATE_RUNNING 1 #define NDIS_PSTATE_SLEEPING 2 #define NdisQueryPacket(p, pbufcnt, bufcnt, firstbuf, plen) \ do { \ if ((firstbuf) != NULL) { \ ndis_buffer **_first; \ _first = firstbuf; \ *(_first) = (p)->np_private.npp_head; \ } \ if ((plen) || (bufcnt) || (pbufcnt)) { \ if ((p)->np_private.npp_validcounts == FALSE) { \ ndis_buffer *tmp; \ unsigned int tlen = 0, pcnt = 0; \ unsigned int add = 0; \ unsigned int pktlen, off; \ \ tmp = (p)->np_private.npp_head; \ while (tmp != NULL) { \ off = MmGetMdlByteOffset(tmp); \ pktlen = MmGetMdlByteCount(tmp);\ tlen += pktlen; \ pcnt += \ NDIS_BUFFER_TO_SPAN_PAGES(tmp); \ add++; \ tmp = tmp->mdl_next; \ } \ (p)->np_private.npp_count = add; \ (p)->np_private.npp_totlen = tlen; \ (p)->np_private.npp_physcnt = pcnt; \ (p)->np_private.npp_validcounts = TRUE; \ } \ if (pbufcnt) { \ unsigned int *_pbufcnt; \ _pbufcnt = (pbufcnt); \ *(_pbufcnt) = (p)->np_private.npp_physcnt; \ } \ if (bufcnt) { \ unsigned int *_bufcnt; \ _bufcnt = (bufcnt); \ *(_bufcnt) = (p)->np_private.npp_count; \ } \ if (plen) { \ unsigned int *_plen; \ _plen = (plen); \ *(_plen) = (p)->np_private.npp_totlen; \ } \ } \ } while (0) __BEGIN_DECLS extern int ndis_libinit(void); extern int ndis_libfini(void); -extern int ndis_ascii_to_unicode(char *, uint16_t **); -extern int ndis_unicode_to_ascii(uint16_t *, int, char **); extern int ndis_load_driver(vm_offset_t, void *); extern int ndis_unload_driver(void *); extern int ndis_mtop(struct mbuf *, ndis_packet **); extern int ndis_ptom(struct mbuf **, ndis_packet *); extern int ndis_get_info(void *, ndis_oid, void *, int *); extern int ndis_set_info(void *, ndis_oid, void *, int *); extern int ndis_get_supported_oids(void *, ndis_oid **, int *); extern int ndis_send_packets(void *, ndis_packet **, int); extern int ndis_send_packet(void *, ndis_packet *); extern int ndis_convert_res(void *); extern int ndis_alloc_amem(void *); extern void ndis_free_amem(void *); extern void ndis_free_packet(ndis_packet *); extern void ndis_free_bufs(ndis_buffer *); extern int ndis_reset_nic(void *); extern int ndis_halt_nic(void *); extern int ndis_shutdown_nic(void *); extern int ndis_init_nic(void *); extern int ndis_isr(void *, int *, int *); extern void ndis_return_packet(void *, void *); extern void ndis_enable_intr(void *); extern void ndis_disable_intr(void *); extern int ndis_init_dma(void *); extern int ndis_destroy_dma(void *); extern int ndis_create_sysctls(void *); extern int ndis_add_sysctl(void *, char *, char *, char *, int); extern int ndis_flush_sysctls(void *); -extern int ndis_thsuspend(struct proc *, struct mtx *, int); -extern void ndis_thresume(struct proc *); extern int ndis_strcasecmp(const char *, const char *); extern int ndis_strncasecmp(const char *, const char *, size_t); extern uint32_t NdisAddDevice(driver_object *, device_object *); extern void NdisAllocatePacketPool(ndis_status *, ndis_handle *, uint32_t, uint32_t); extern void NdisAllocatePacketPoolEx(ndis_status *, ndis_handle *, uint32_t, uint32_t, uint32_t); extern uint32_t NdisPacketPoolUsage(ndis_handle); extern void NdisFreePacketPool(ndis_handle); extern void NdisAllocatePacket(ndis_status *, ndis_packet **, ndis_handle); extern void NdisFreePacket(ndis_packet *); extern ndis_status NdisScheduleWorkItem(ndis_work_item *); - +extern void NdisMSleep(uint32_t); __END_DECLS #endif /* _NDIS_VAR_H_ */ Index: stable/5/sys/compat/ndis/ntoskrnl_var.h =================================================================== --- stable/5/sys/compat/ndis/ntoskrnl_var.h (revision 151520) +++ stable/5/sys/compat/ndis/ntoskrnl_var.h (revision 151521) @@ -1,1340 +1,1420 @@ /*- * 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$ */ #ifndef _NTOSKRNL_VAR_H_ #define _NTOSKRNL_VAR_H_ +#define MTX_NTOSKRNL_SPIN_LOCK "NDIS thread lock" + /* * us_buf is really a wchar_t *, but it's inconvenient to include * all the necessary header goop needed to define it, and it's a * pointer anyway, so for now, just make it a uint16_t *. */ struct unicode_string { uint16_t us_len; uint16_t us_maxlen; uint16_t *us_buf; }; typedef struct unicode_string unicode_string; +struct ansi_string { + uint16_t as_len; + uint16_t as_maxlen; + char *as_buf; +}; + +typedef struct ansi_string ansi_string; + /* * Windows memory descriptor list. In Windows, it's possible for * buffers to be passed between user and kernel contexts without * copying. Buffers may also be allocated in either paged or * non-paged memory regions. An MDL describes the pages of memory * used to contain a particular buffer. Note that a single MDL * may describe a buffer that spans multiple pages. An array of * page addresses appears immediately after the MDL structure itself. * MDLs are therefore implicitly variably sized, even though they * don't look it. * * Note that in FreeBSD, we can take many shortcuts in the way * we handle MDLs because: * * - We are only concerned with pages in kernel context. This means * we will only ever use the kernel's memory map, and remapping * of buffers is never needed. * * - Kernel pages can never be paged out, so we don't have to worry * about whether or not a page is actually mapped before going to * touch it. */ struct mdl { struct mdl *mdl_next; uint16_t mdl_size; uint16_t mdl_flags; void *mdl_process; void *mdl_mappedsystemva; void *mdl_startva; uint32_t mdl_bytecount; uint32_t mdl_byteoffset; }; typedef struct mdl mdl, ndis_buffer; /* MDL flags */ #define MDL_MAPPED_TO_SYSTEM_VA 0x0001 #define MDL_PAGES_LOCKED 0x0002 #define MDL_SOURCE_IS_NONPAGED_POOL 0x0004 #define MDL_ALLOCATED_FIXED_SIZE 0x0008 #define MDL_PARTIAL 0x0010 #define MDL_PARTIAL_HAS_BEEN_MAPPED 0x0020 #define MDL_IO_PAGE_READ 0x0040 #define MDL_WRITE_OPERATION 0x0080 #define MDL_PARENT_MAPPED_SYSTEM_VA 0x0100 #define MDL_FREE_EXTRA_PTES 0x0200 #define MDL_IO_SPACE 0x0800 #define MDL_NETWORK_HEADER 0x1000 #define MDL_MAPPING_CAN_FAIL 0x2000 #define MDL_ALLOCATED_MUST_SUCCEED 0x4000 #define MDL_ZONE_ALLOCED 0x8000 /* BSD private */ #define MDL_ZONE_PAGES 16 #define MDL_ZONE_SIZE (sizeof(mdl) + (sizeof(vm_offset_t) * MDL_ZONE_PAGES)) /* Note: assumes x86 page size of 4K. */ #if PAGE_SIZE == 4096 #define PAGE_SHIFT 12 #elif PAGE_SIZE == 8192 #define PAGE_SHIFT 13 #else #error PAGE_SHIFT undefined! #endif #define SPAN_PAGES(ptr, len) \ ((uint32_t)((((uintptr_t)(ptr) & (PAGE_SIZE - 1)) + \ (len) + (PAGE_SIZE - 1)) >> PAGE_SHIFT)) #define PAGE_ALIGN(ptr) \ ((void *)((uintptr_t)(ptr) & ~(PAGE_SIZE - 1))) #define BYTE_OFFSET(ptr) \ ((uint32_t)((uintptr_t)(ptr) & (PAGE_SIZE - 1))) #define MDL_PAGES(m) (vm_offset_t *)(m + 1) #define MmInitializeMdl(b, baseva, len) \ (b)->mdl_next = NULL; \ (b)->mdl_size = (uint16_t)(sizeof(mdl) + \ (sizeof(vm_offset_t) * SPAN_PAGES((baseva), (len)))); \ (b)->mdl_flags = 0; \ (b)->mdl_startva = (void *)PAGE_ALIGN((baseva)); \ (b)->mdl_byteoffset = BYTE_OFFSET((baseva)); \ (b)->mdl_bytecount = (uint32_t)(len); #define MmGetMdlByteOffset(mdl) ((mdl)->mdl_byteoffset) #define MmGetMdlByteCount(mdl) ((mdl)->mdl_bytecount) #define MmGetMdlVirtualAddress(mdl) \ ((void *)((char *)((mdl)->mdl_startva) + (mdl)->mdl_byteoffset)) #define MmGetMdlStartVa(mdl) ((mdl)->mdl_startva) #define MmGetMdlPfnArray(mdl) MDL_PAGES(mdl) #define WDM_MAJOR 1 #define WDM_MINOR_WIN98 0x00 #define WDM_MINOR_WINME 0x05 #define WDM_MINOR_WIN2000 0x10 #define WDM_MINOR_WINXP 0x20 #define WDM_MINOR_WIN2003 0x30 /*- * The ndis_kspin_lock type is called KSPIN_LOCK in MS-Windows. * According to the Windows DDK header files, KSPIN_LOCK is defined like this: * typedef ULONG_PTR KSPIN_LOCK; * * From basetsd.h (SDK, Feb. 2003): * typedef [public] unsigned __int3264 ULONG_PTR, *PULONG_PTR; * typedef unsigned __int64 ULONG_PTR, *PULONG_PTR; * typedef _W64 unsigned long ULONG_PTR, *PULONG_PTR; * * The keyword __int3264 specifies an integral type that has the following * properties: * + It is 32-bit on 32-bit platforms * + It is 64-bit on 64-bit platforms * + It is 32-bit on the wire for backward compatibility. * It gets truncated on the sending side and extended appropriately * (signed or unsigned) on the receiving side. * * Thus register_t seems the proper mapping onto FreeBSD for spin locks. */ typedef register_t kspin_lock; struct slist_entry { struct slist_entry *sl_next; }; typedef struct slist_entry slist_entry; union slist_header { uint64_t slh_align; struct { struct slist_entry *slh_next; uint16_t slh_depth; uint16_t slh_seq; } slh_list; }; typedef union slist_header slist_header; struct list_entry { struct list_entry *nle_flink; struct list_entry *nle_blink; }; typedef struct list_entry list_entry; -#define INIT_LIST_HEAD(l) \ +#define InitializeListHead(l) \ (l)->nle_flink = (l)->nle_blink = (l) -#define REMOVE_LIST_ENTRY(e) \ +#define IsListEmpty(h) \ + ((h)->nle_flink == (h)) + +#define RemoveEntryList(e) \ do { \ list_entry *b; \ list_entry *f; \ \ - f = e->nle_flink; \ - b = e->nle_blink; \ + f = (e)->nle_flink; \ + b = (e)->nle_blink; \ b->nle_flink = f; \ f->nle_blink = b; \ } while (0) -#define REMOVE_LIST_HEAD(l) \ - do { \ - list_entry *f; \ - list_entry *e; \ - \ - e = l->nle_flink; \ - f = e->nle_flink; \ - l->nle_flink = f; \ - f->nle_blink = l; \ - } while (0) +/* These two have to be inlined since they return things. */ -#define REMOVE_LIST_TAIL(l) \ - do { \ - list_entry *b; \ - list_entry *e; \ - \ - e = l->nle_blink; \ - b = e->nle_blink; \ - l->nle_blink = b; \ - b->nle_flink = l; \ - } while (0) +static __inline__ list_entry * +RemoveHeadList(list_entry *l) +{ + list_entry *f; + list_entry *e; -#define INSERT_LIST_TAIL(l, e) \ + e = l->nle_flink; + f = e->nle_flink; + l->nle_flink = f; + f->nle_blink = l; + + return (e); +} + +static __inline__ list_entry * +RemoveTailList(list_entry *l) +{ + list_entry *b; + list_entry *e; + + e = l->nle_blink; + b = e->nle_blink; + l->nle_blink = b; + b->nle_flink = l; + + return (e); +} + +#define InsertTailList(l, e) \ do { \ list_entry *b; \ \ b = l->nle_blink; \ e->nle_flink = l; \ e->nle_blink = b; \ b->nle_flink = (e); \ l->nle_blink = (e); \ } while (0) -#define INSERT_LIST_HEAD(l, e) \ +#define InsertHeadList(l, e) \ do { \ list_entry *f; \ \ f = l->nle_flink; \ e->nle_flink = f; \ e->nle_blink = l; \ f->nle_blink = e; \ l->nle_flink = e; \ } while (0) #define CONTAINING_RECORD(addr, type, field) \ ((type *)((vm_offset_t)(addr) - (vm_offset_t)(&((type *)0)->field))) struct nt_dispatch_header { uint8_t dh_type; uint8_t dh_abs; uint8_t dh_size; uint8_t dh_inserted; - uint32_t dh_sigstate; + int32_t dh_sigstate; list_entry dh_waitlisthead; }; typedef struct nt_dispatch_header nt_dispatch_header; +/* Dispatcher object types */ + +#define DISP_TYPE_NOTIFICATION_EVENT 0 /* KEVENT */ +#define DISP_TYPE_SYNCHRONIZATION_EVENT 1 /* KEVENT */ +#define DISP_TYPE_MUTANT 2 /* KMUTANT/KMUTEX */ +#define DISP_TYPE_PROCESS 3 /* KPROCESS */ +#define DISP_TYPE_QUEUE 4 /* KQUEUE */ +#define DISP_TYPE_SEMAPHORE 5 /* KSEMAPHORE */ +#define DISP_TYPE_THREAD 6 /* KTHREAD */ +#define DISP_TYPE_NOTIFICATION_TIMER 8 /* KTIMER */ +#define DISP_TYPE_SYNCHRONIZATION_TIMER 9 /* KTIMER */ + #define OTYPE_EVENT 0 #define OTYPE_MUTEX 1 #define OTYPE_THREAD 2 #define OTYPE_TIMER 3 /* Windows dispatcher levels. */ #define PASSIVE_LEVEL 0 #define LOW_LEVEL 0 #define APC_LEVEL 1 #define DISPATCH_LEVEL 2 #define DEVICE_LEVEL (DISPATCH_LEVEL + 1) #define PROFILE_LEVEL 27 #define CLOCK1_LEVEL 28 #define CLOCK2_LEVEL 28 #define IPI_LEVEL 29 #define POWER_LEVEL 30 #define HIGH_LEVEL 31 #define SYNC_LEVEL_UP DISPATCH_LEVEL #define SYNC_LEVEL_MP (IPI_LEVEL - 1) #define AT_PASSIVE_LEVEL(td) \ ((td)->td_proc->p_flag & P_KTHREAD == FALSE) #define AT_DISPATCH_LEVEL(td) \ ((td)->td_base_pri == PI_REALTIME) #define AT_DIRQL_LEVEL(td) \ ((td)->td_priority <= PI_NET) #define AT_HIGH_LEVEL(td) \ ((td)->td_critnest != 0) struct nt_objref { nt_dispatch_header no_dh; void *no_obj; TAILQ_ENTRY(nt_objref) link; }; TAILQ_HEAD(nt_objref_head, nt_objref); typedef struct nt_objref nt_objref; #define EVENT_TYPE_NOTIFY 0 #define EVENT_TYPE_SYNC 1 /* * We need to use the timeout()/untimeout() API for ktimers * since timers can be initialized, but not destroyed (so * malloc()ing our own callout structures would mean a leak, * since there'd be no way to free() them). This means we * need to use struct callout_handle, which is really just a * pointer. To make it easier to deal with, we use a union * to overlay the callout_handle over the k_timerlistentry. * The latter is a list_entry, which is two pointers, so * there's enough space available to hide a callout_handle * there. */ struct ktimer { nt_dispatch_header k_header; uint64_t k_duetime; union { list_entry k_timerlistentry; - struct callout_handle k_handle; + struct callout *k_callout; } u; void *k_dpc; uint32_t k_period; }; #define k_timerlistentry u.k_timerlistentry -#define k_handle u.k_handle +#define k_callout u.k_callout typedef struct ktimer ktimer; struct nt_kevent { nt_dispatch_header k_header; }; typedef struct nt_kevent nt_kevent; /* Kernel defered procedure call (i.e. timer callback) */ struct kdpc; typedef void (*kdpc_func)(struct kdpc *, void *, void *, void *); struct kdpc { uint16_t k_type; uint8_t k_num; /* CPU number */ uint8_t k_importance; /* priority */ list_entry k_dpclistentry; void *k_deferedfunc; void *k_deferredctx; void *k_sysarg1; void *k_sysarg2; void *k_lock; }; #define KDPC_IMPORTANCE_LOW 0 #define KDPC_IMPORTANCE_MEDIUM 1 #define KDPC_IMPORTANCE_HIGH 2 #define KDPC_CPU_DEFAULT 255 typedef struct kdpc kdpc; /* * Note: the acquisition count is BSD-specific. The Microsoft * documentation says that mutexes can be acquired recursively * by a given thread, but that you must release the mutex as * many times as you acquired it before it will be set to the * signalled state (i.e. before any other threads waiting on * the object will be woken up). However the Windows KMUTANT * structure has no field for keeping track of the number of * acquisitions, so we need to add one ourselves. As long as * driver code treats the mutex as opaque, we should be ok. */ struct kmutant { nt_dispatch_header km_header; - union { - list_entry km_listentry; - uint32_t km_acquirecnt; - } u; + list_entry km_listentry; void *km_ownerthread; uint8_t km_abandoned; uint8_t km_apcdisable; }; -#define km_listentry u.km_listentry -#define km_acquirecnt u.km_acquirecnt - typedef struct kmutant kmutant; #define LOOKASIDE_DEPTH 256 struct general_lookaside { slist_header gl_listhead; uint16_t gl_depth; uint16_t gl_maxdepth; uint32_t gl_totallocs; union { uint32_t gl_allocmisses; uint32_t gl_allochits; } u_a; uint32_t gl_totalfrees; union { uint32_t gl_freemisses; uint32_t gl_freehits; } u_m; uint32_t gl_type; uint32_t gl_tag; uint32_t gl_size; void *gl_allocfunc; void *gl_freefunc; list_entry gl_listent; uint32_t gl_lasttotallocs; union { uint32_t gl_lastallocmisses; uint32_t gl_lastallochits; } u_l; uint32_t gl_rsvd[2]; }; typedef struct general_lookaside general_lookaside; struct npaged_lookaside_list { general_lookaside nll_l; #ifdef __i386__ kspin_lock nll_obsoletelock; #endif }; typedef struct npaged_lookaside_list npaged_lookaside_list; typedef struct npaged_lookaside_list paged_lookaside_list; typedef void * (*lookaside_alloc_func)(uint32_t, size_t, uint32_t); typedef void (*lookaside_free_func)(void *); struct irp; struct kdevice_qentry { list_entry kqe_devlistent; uint32_t kqe_sortkey; uint8_t kqe_inserted; }; typedef struct kdevice_qentry kdevice_qentry; struct kdevice_queue { uint16_t kq_type; uint16_t kq_size; list_entry kq_devlisthead; kspin_lock kq_lock; uint8_t kq_busy; }; typedef struct kdevice_queue kdevice_queue; struct wait_ctx_block { kdevice_qentry wcb_waitqueue; void *wcb_devfunc; void *wcb_devctx; uint32_t wcb_mapregcnt; void *wcb_devobj; void *wcb_curirp; void *wcb_bufchaindpc; }; typedef struct wait_ctx_block wait_ctx_block; struct wait_block { list_entry wb_waitlist; void *wb_kthread; nt_dispatch_header *wb_object; struct wait_block *wb_next; +#ifdef notdef uint16_t wb_waitkey; uint16_t wb_waittype; +#endif + uint8_t wb_waitkey; + uint8_t wb_waittype; + uint8_t wb_awakened; + uint8_t wb_oldpri; }; typedef struct wait_block wait_block; +#define wb_ext wb_kthread + #define THREAD_WAIT_OBJECTS 3 #define MAX_WAIT_OBJECTS 64 #define WAITTYPE_ALL 0 #define WAITTYPE_ANY 1 +#define WAITKEY_VALID 0x8000 + struct thread_context { void *tc_thrctx; void *tc_thrfunc; }; typedef struct thread_context thread_context; /* Forward declaration */ struct driver_object; struct devobj_extension; struct driver_extension { struct driver_object *dre_driverobj; void *dre_adddevicefunc; uint32_t dre_reinitcnt; unicode_string dre_srvname; /* * Drivers are allowed to add one or more custom extensions * to the driver object, but there's no special pointer * for them. Hang them off here for now. */ list_entry dre_usrext; }; typedef struct driver_extension driver_extension; struct custom_extension { list_entry ce_list; void *ce_clid; }; typedef struct custom_extension custom_extension; /* + * The KINTERRUPT structure in Windows is opaque to drivers. + * We define our own custom version with things we need. + */ + +struct kinterrupt { + list_entry ki_list; + device_t ki_dev; + int ki_rid; + void *ki_cookie; + struct resource *ki_irq; + kspin_lock ki_lock_priv; + kspin_lock *ki_lock; + void *ki_svcfunc; + void *ki_svcctx; +}; + +typedef struct kinterrupt kinterrupt; + +/* * In Windows, there are Physical Device Objects (PDOs) and * Functional Device Objects (FDOs). Physical Device Objects are * created and maintained by bus drivers. For example, the PCI * bus driver might detect two PCI ethernet cards on a given * bus. The PCI bus driver will then allocate two device_objects * for its own internal bookeeping purposes. This is analagous * to the device_t that the FreeBSD PCI code allocates and passes * into each PCI driver's probe and attach routines. * * When an ethernet driver claims one of the ethernet cards * on the bus, it will create its own device_object. This is * the Functional Device Object. This object is analagous to the * device-specific softc structure. */ struct device_object { uint16_t do_type; uint16_t do_size; uint32_t do_refcnt; struct driver_object *do_drvobj; struct device_object *do_nextdev; struct device_object *do_attacheddev; struct irp *do_currirp; void *do_iotimer; uint32_t do_flags; uint32_t do_characteristics; void *do_vpb; void *do_devext; uint32_t do_devtype; uint8_t do_stacksize; union { list_entry do_listent; wait_ctx_block do_wcb; } queue; uint32_t do_alignreq; kdevice_queue do_devqueue; struct kdpc do_dpc; uint32_t do_activethreads; void *do_securitydesc; struct nt_kevent do_devlock; uint16_t do_sectorsz; uint16_t do_spare1; struct devobj_extension *do_devobj_ext; void *do_rsvd; }; typedef struct device_object device_object; struct devobj_extension { uint16_t dve_type; uint16_t dve_size; device_object *dve_devobj; }; typedef struct devobj_extension devobj_extension; /* Device object flags */ #define DO_VERIFY_VOLUME 0x00000002 #define DO_BUFFERED_IO 0x00000004 #define DO_EXCLUSIVE 0x00000008 #define DO_DIRECT_IO 0x00000010 #define DO_MAP_IO_BUFFER 0x00000020 #define DO_DEVICE_HAS_NAME 0x00000040 #define DO_DEVICE_INITIALIZING 0x00000080 #define DO_SYSTEM_BOOT_PARTITION 0x00000100 #define DO_LONG_TERM_REQUESTS 0x00000200 #define DO_NEVER_LAST_DEVICE 0x00000400 #define DO_SHUTDOWN_REGISTERED 0x00000800 #define DO_BUS_ENUMERATED_DEVICE 0x00001000 #define DO_POWER_PAGABLE 0x00002000 #define DO_POWER_INRUSH 0x00004000 #define DO_LOW_PRIORITY_FILESYSTEM 0x00010000 /* Priority boosts */ #define IO_NO_INCREMENT 0 #define IO_CD_ROM_INCREMENT 1 #define IO_DISK_INCREMENT 1 #define IO_KEYBOARD_INCREMENT 6 #define IO_MAILSLOT_INCREMENT 2 #define IO_MOUSE_INCREMENT 6 #define IO_NAMED_PIPE_INCREMENT 2 #define IO_NETWORK_INCREMENT 2 #define IO_PARALLEL_INCREMENT 1 #define IO_SERIAL_INCREMENT 2 #define IO_SOUND_INCREMENT 8 #define IO_VIDEO_INCREMENT 1 /* IRP major codes */ #define IRP_MJ_CREATE 0x00 #define IRP_MJ_CREATE_NAMED_PIPE 0x01 #define IRP_MJ_CLOSE 0x02 #define IRP_MJ_READ 0x03 #define IRP_MJ_WRITE 0x04 #define IRP_MJ_QUERY_INFORMATION 0x05 #define IRP_MJ_SET_INFORMATION 0x06 #define IRP_MJ_QUERY_EA 0x07 #define IRP_MJ_SET_EA 0x08 #define IRP_MJ_FLUSH_BUFFERS 0x09 #define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a #define IRP_MJ_SET_VOLUME_INFORMATION 0x0b #define IRP_MJ_DIRECTORY_CONTROL 0x0c #define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d #define IRP_MJ_DEVICE_CONTROL 0x0e #define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f #define IRP_MJ_SHUTDOWN 0x10 #define IRP_MJ_LOCK_CONTROL 0x11 #define IRP_MJ_CLEANUP 0x12 #define IRP_MJ_CREATE_MAILSLOT 0x13 #define IRP_MJ_QUERY_SECURITY 0x14 #define IRP_MJ_SET_SECURITY 0x15 #define IRP_MJ_POWER 0x16 #define IRP_MJ_SYSTEM_CONTROL 0x17 #define IRP_MJ_DEVICE_CHANGE 0x18 #define IRP_MJ_QUERY_QUOTA 0x19 #define IRP_MJ_SET_QUOTA 0x1a #define IRP_MJ_PNP 0x1b #define IRP_MJ_PNP_POWER IRP_MJ_PNP // Obsolete.... #define IRP_MJ_MAXIMUM_FUNCTION 0x1b #define IRP_MJ_SCSI IRP_MJ_INTERNAL_DEVICE_CONTROL /* IRP minor codes */ #define IRP_MN_QUERY_DIRECTORY 0x01 #define IRP_MN_NOTIFY_CHANGE_DIRECTORY 0x02 #define IRP_MN_USER_FS_REQUEST 0x00 #define IRP_MN_MOUNT_VOLUME 0x01 #define IRP_MN_VERIFY_VOLUME 0x02 #define IRP_MN_LOAD_FILE_SYSTEM 0x03 #define IRP_MN_TRACK_LINK 0x04 #define IRP_MN_KERNEL_CALL 0x04 #define IRP_MN_LOCK 0x01 #define IRP_MN_UNLOCK_SINGLE 0x02 #define IRP_MN_UNLOCK_ALL 0x03 #define IRP_MN_UNLOCK_ALL_BY_KEY 0x04 #define IRP_MN_NORMAL 0x00 #define IRP_MN_DPC 0x01 #define IRP_MN_MDL 0x02 #define IRP_MN_COMPLETE 0x04 #define IRP_MN_COMPRESSED 0x08 #define IRP_MN_MDL_DPC (IRP_MN_MDL | IRP_MN_DPC) #define IRP_MN_COMPLETE_MDL (IRP_MN_COMPLETE | IRP_MN_MDL) #define IRP_MN_COMPLETE_MDL_DPC (IRP_MN_COMPLETE_MDL | IRP_MN_DPC) #define IRP_MN_SCSI_CLASS 0x01 #define IRP_MN_START_DEVICE 0x00 #define IRP_MN_QUERY_REMOVE_DEVICE 0x01 #define IRP_MN_REMOVE_DEVICE 0x02 #define IRP_MN_CANCEL_REMOVE_DEVICE 0x03 #define IRP_MN_STOP_DEVICE 0x04 #define IRP_MN_QUERY_STOP_DEVICE 0x05 #define IRP_MN_CANCEL_STOP_DEVICE 0x06 #define IRP_MN_QUERY_DEVICE_RELATIONS 0x07 #define IRP_MN_QUERY_INTERFACE 0x08 #define IRP_MN_QUERY_CAPABILITIES 0x09 #define IRP_MN_QUERY_RESOURCES 0x0A #define IRP_MN_QUERY_RESOURCE_REQUIREMENTS 0x0B #define IRP_MN_QUERY_DEVICE_TEXT 0x0C #define IRP_MN_FILTER_RESOURCE_REQUIREMENTS 0x0D #define IRP_MN_READ_CONFIG 0x0F #define IRP_MN_WRITE_CONFIG 0x10 #define IRP_MN_EJECT 0x11 #define IRP_MN_SET_LOCK 0x12 #define IRP_MN_QUERY_ID 0x13 #define IRP_MN_QUERY_PNP_DEVICE_STATE 0x14 #define IRP_MN_QUERY_BUS_INFORMATION 0x15 #define IRP_MN_DEVICE_USAGE_NOTIFICATION 0x16 #define IRP_MN_SURPRISE_REMOVAL 0x17 #define IRP_MN_QUERY_LEGACY_BUS_INFORMATION 0x18 #define IRP_MN_WAIT_WAKE 0x00 #define IRP_MN_POWER_SEQUENCE 0x01 #define IRP_MN_SET_POWER 0x02 #define IRP_MN_QUERY_POWER 0x03 #define IRP_MN_QUERY_ALL_DATA 0x00 #define IRP_MN_QUERY_SINGLE_INSTANCE 0x01 #define IRP_MN_CHANGE_SINGLE_INSTANCE 0x02 #define IRP_MN_CHANGE_SINGLE_ITEM 0x03 #define IRP_MN_ENABLE_EVENTS 0x04 #define IRP_MN_DISABLE_EVENTS 0x05 #define IRP_MN_ENABLE_COLLECTION 0x06 #define IRP_MN_DISABLE_COLLECTION 0x07 #define IRP_MN_REGINFO 0x08 #define IRP_MN_EXECUTE_METHOD 0x09 #define IRP_MN_REGINFO_EX 0x0b /* IRP flags */ #define IRP_NOCACHE 0x00000001 #define IRP_PAGING_IO 0x00000002 #define IRP_MOUNT_COMPLETION 0x00000002 #define IRP_SYNCHRONOUS_API 0x00000004 #define IRP_ASSOCIATED_IRP 0x00000008 #define IRP_BUFFERED_IO 0x00000010 #define IRP_DEALLOCATE_BUFFER 0x00000020 #define IRP_INPUT_OPERATION 0x00000040 #define IRP_SYNCHRONOUS_PAGING_IO 0x00000040 #define IRP_CREATE_OPERATION 0x00000080 #define IRP_READ_OPERATION 0x00000100 #define IRP_WRITE_OPERATION 0x00000200 #define IRP_CLOSE_OPERATION 0x00000400 #define IRP_DEFER_IO_COMPLETION 0x00000800 #define IRP_OB_QUERY_NAME 0x00001000 #define IRP_HOLD_DEVICE_QUEUE 0x00002000 #define IRP_RETRY_IO_COMPLETION 0x00004000 #define IRP_CLASS_CACHE_OPERATION 0x00008000 #define IRP_SET_USER_EVENT IRP_CLOSE_OPERATION /* IRP I/O control flags */ #define IRP_QUOTA_CHARGED 0x01 #define IRP_ALLOCATED_MUST_SUCCEED 0x02 #define IRP_ALLOCATED_FIXED_SIZE 0x04 #define IRP_LOOKASIDE_ALLOCATION 0x08 /* I/O method types */ #define METHOD_BUFFERED 0 #define METHOD_IN_DIRECT 1 #define METHOD_OUT_DIRECT 2 #define METHOD_NEITHER 3 /* File access types */ #define FILE_ANY_ACCESS 0x0000 #define FILE_SPECIAL_ACCESS FILE_ANY_ACCESS #define FILE_READ_ACCESS 0x0001 #define FILE_WRITE_ACCESS 0x0002 /* Recover I/O access method from IOCTL code. */ #define IO_METHOD(x) ((x) & 0xFFFFFFFC) /* Recover function code from IOCTL code */ #define IO_FUNC(x) (((x) & 0x7FFC) >> 2) /* Macro to construct an IOCTL code. */ #define IOCTL_CODE(dev, func, iomethod, acc) \ ((dev) << 16) | (acc << 14) | (func << 2) | (iomethod)) struct io_status_block { union { uint32_t isb_status; void *isb_ptr; } u; register_t isb_info; }; #define isb_status u.isb_status #define isb_ptr u.isb_ptr typedef struct io_status_block io_status_block; struct kapc { uint16_t apc_type; uint16_t apc_size; uint32_t apc_spare0; void *apc_thread; list_entry apc_list; void *apc_kernfunc; void *apc_rundownfunc; void *apc_normalfunc; void *apc_normctx; void *apc_sysarg1; void *apc_sysarg2; uint8_t apc_stateidx; uint8_t apc_cpumode; uint8_t apc_inserted; }; typedef struct kapc kapc; typedef uint32_t (*completion_func)(device_object *, struct irp *, void *); typedef uint32_t (*cancel_func)(device_object *, struct irp *); struct io_stack_location { uint8_t isl_major; uint8_t isl_minor; uint8_t isl_flags; uint8_t isl_ctl; /* * There's a big-ass union here in the actual Windows * definition of the stucture, but it contains stuff * that doesn't really apply to BSD, and defining it * all properly would require duplicating over a dozen * other structures that we'll never use. Since the * io_stack_location structure is opaque to drivers * anyway, I'm not going to bother with the extra crap. */ union { struct { uint32_t isl_len; uint32_t *isl_key; uint64_t isl_byteoff; } isl_read; struct { uint32_t isl_len; uint32_t *isl_key; uint64_t isl_byteoff; } isl_write; struct { uint32_t isl_obuflen; uint32_t isl_ibuflen; uint32_t isl_iocode; void *isl_type3ibuf; } isl_ioctl; struct { void *isl_arg1; void *isl_arg2; void *isl_arg3; void *isl_arg4; } isl_others; } isl_parameters __attribute__((packed)); void *isl_devobj; void *isl_fileobj; completion_func isl_completionfunc; void *isl_completionctx; }; typedef struct io_stack_location io_stack_location; /* Stack location control flags */ #define SL_PENDING_RETURNED 0x01 #define SL_INVOKE_ON_CANCEL 0x20 #define SL_INVOKE_ON_SUCCESS 0x40 #define SL_INVOKE_ON_ERROR 0x80 struct irp { uint16_t irp_type; uint16_t irp_size; mdl *irp_mdl; uint32_t irp_flags; union { struct irp *irp_master; uint32_t irp_irpcnt; void *irp_sysbuf; } irp_assoc; list_entry irp_thlist; io_status_block irp_iostat; uint8_t irp_reqmode; uint8_t irp_pendingreturned; uint8_t irp_stackcnt; uint8_t irp_currentstackloc; uint8_t irp_cancel; uint8_t irp_cancelirql; uint8_t irp_apcenv; uint8_t irp_allocflags; io_status_block *irp_usriostat; nt_kevent *irp_usrevent; union { struct { void *irp_apcfunc; void *irp_apcctx; } irp_asyncparms; uint64_t irp_allocsz; } irp_overlay; cancel_func irp_cancelfunc; void *irp_userbuf; /* Windows kernel info */ union { struct { union { kdevice_qentry irp_dqe; struct { void *irp_drvctx[4]; } s1; } u1; void *irp_thread; char *irp_auxbuf; struct { list_entry irp_list; union { io_stack_location *irp_csl; uint32_t irp_pkttype; } u2; } s2; void *irp_fileobj; } irp_overlay; kapc irp_apc; void *irp_compkey; } irp_tail; }; #define irp_csl s2.u2.irp_csl #define irp_pkttype s2.u2.irp_pkttype typedef struct irp irp; #define InterlockedExchangePointer(dst, val) \ (void *)InterlockedExchange((uint32_t *)(dst), (uintptr_t)(val)) #define IoSizeOfIrp(ssize) \ ((uint16_t) (sizeof(irp) + ((ssize) * (sizeof(io_stack_location))))) #define IoSetCancelRoutine(irp, func) \ (cancel_func)InterlockedExchangePointer( \ (void *)&(ip)->irp_cancelfunc, (void *)(func)) #define IoGetCurrentIrpStackLocation(irp) \ (irp)->irp_tail.irp_overlay.irp_csl #define IoGetNextIrpStackLocation(irp) \ ((irp)->irp_tail.irp_overlay.irp_csl - 1) #define IoSetNextIrpStackLocation(irp) \ do { \ irp->irp_currentstackloc--; \ irp->irp_tail.irp_overlay.irp_csl--; \ } while(0) #define IoSetCompletionRoutine(irp, func, ctx, ok, err, cancel) \ do { \ io_stack_location *s; \ s = IoGetNextIrpStackLocation((irp)); \ s->isl_completionfunc = (func); \ s->isl_completionctx = (ctx); \ s->isl_ctl = 0; \ if (ok) s->isl_ctl = SL_INVOKE_ON_SUCCESS; \ if (err) s->isl_ctl |= SL_INVOKE_ON_ERROR; \ if (cancel) s->isl_ctl |= SL_INVOKE_ON_CANCEL; \ } while(0) #define IoMarkIrpPending(irp) \ IoGetCurrentIrpStackLocation(irp)->isl_ctl |= SL_PENDING_RETURNED #define IoCopyCurrentIrpStackLocationToNext(irp) \ do { \ io_stack_location *src, *dst; \ src = IoGetCurrentIrpStackLocation(irp); \ dst = IoGetNextIrpStackLocation(irp); \ bcopy((char *)src, (char *)dst, \ offsetof(io_stack_location, isl_completionfunc)); \ } while(0) #define IoSkipCurrentIrpStackLocation(irp) \ do { \ (irp)->irp_currentstackloc++; \ (irp)->irp_tail.irp_overlay.irp_csl++; \ } while(0) #define IoInitializeDpcRequest(dobj, dpcfunc) \ KeInitializeDpc(&(dobj)->do_dpc, dpcfunc, dobj) #define IoRequestDpc(dobj, irp, ctx) \ KeInsertQueueDpc(&(dobj)->do_dpc, irp, ctx) typedef uint32_t (*driver_dispatch)(device_object *, irp *); /* * The driver_object is allocated once for each driver that's loaded * into the system. A new one is allocated for each driver and * populated a bit via the driver's DriverEntry function. * In general, a Windows DriverEntry() function will provide a pointer * to its AddDevice() method and set up the dispatch table. * For NDIS drivers, this is all done behind the scenes in the * NdisInitializeWrapper() and/or NdisMRegisterMiniport() routines. */ struct driver_object { uint16_t dro_type; uint16_t dro_size; device_object *dro_devobj; uint32_t dro_flags; void *dro_driverstart; uint32_t dro_driversize; void *dro_driversection; driver_extension *dro_driverext; unicode_string dro_drivername; unicode_string *dro_hwdb; void *dro_pfastiodispatch; void *dro_driverinitfunc; void *dro_driverstartiofunc; void *dro_driverunloadfunc; driver_dispatch dro_dispatch[IRP_MJ_MAXIMUM_FUNCTION + 1]; }; typedef struct driver_object driver_object; #define DEVPROP_DEVICE_DESCRIPTION 0x00000000 #define DEVPROP_HARDWARE_ID 0x00000001 #define DEVPROP_COMPATIBLE_IDS 0x00000002 #define DEVPROP_BOOTCONF 0x00000003 #define DEVPROP_BOOTCONF_TRANSLATED 0x00000004 #define DEVPROP_CLASS_NAME 0x00000005 #define DEVPROP_CLASS_GUID 0x00000006 #define DEVPROP_DRIVER_KEYNAME 0x00000007 #define DEVPROP_MANUFACTURER 0x00000008 #define DEVPROP_FRIENDLYNAME 0x00000009 #define DEVPROP_LOCATION_INFO 0x0000000A #define DEVPROP_PHYSDEV_NAME 0x0000000B #define DEVPROP_BUSTYPE_GUID 0x0000000C #define DEVPROP_LEGACY_BUSTYPE 0x0000000D #define DEVPROP_BUS_NUMBER 0x0000000E #define DEVPROP_ENUMERATOR_NAME 0x0000000F #define DEVPROP_ADDRESS 0x00000010 #define DEVPROP_UINUMBER 0x00000011 #define DEVPROP_INSTALL_STATE 0x00000012 #define DEVPROP_REMOVAL_POLICY 0x00000013 /* Various supported device types (used with IoCreateDevice()) */ #define FILE_DEVICE_BEEP 0x00000001 #define FILE_DEVICE_CD_ROM 0x00000002 #define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 #define FILE_DEVICE_CONTROLLER 0x00000004 #define FILE_DEVICE_DATALINK 0x00000005 #define FILE_DEVICE_DFS 0x00000006 #define FILE_DEVICE_DISK 0x00000007 #define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 #define FILE_DEVICE_FILE_SYSTEM 0x00000009 #define FILE_DEVICE_INPORT_PORT 0x0000000A #define FILE_DEVICE_KEYBOARD 0x0000000B #define FILE_DEVICE_MAILSLOT 0x0000000C #define FILE_DEVICE_MIDI_IN 0x0000000D #define FILE_DEVICE_MIDI_OUT 0x0000000E #define FILE_DEVICE_MOUSE 0x0000000F #define FILE_DEVICE_MULTI_UNC_PROVIDER 0x00000010 #define FILE_DEVICE_NAMED_PIPE 0x00000011 #define FILE_DEVICE_NETWORK 0x00000012 #define FILE_DEVICE_NETWORK_BROWSER 0x00000013 #define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 #define FILE_DEVICE_NULL 0x00000015 #define FILE_DEVICE_PARALLEL_PORT 0x00000016 #define FILE_DEVICE_PHYSICAL_NETCARD 0x00000017 #define FILE_DEVICE_PRINTER 0x00000018 #define FILE_DEVICE_SCANNER 0x00000019 #define FILE_DEVICE_SERIAL_MOUSE_PORT 0x0000001A #define FILE_DEVICE_SERIAL_PORT 0x0000001B #define FILE_DEVICE_SCREEN 0x0000001C #define FILE_DEVICE_SOUND 0x0000001D #define FILE_DEVICE_STREAMS 0x0000001E #define FILE_DEVICE_TAPE 0x0000001F #define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 #define FILE_DEVICE_TRANSPORT 0x00000021 #define FILE_DEVICE_UNKNOWN 0x00000022 #define FILE_DEVICE_VIDEO 0x00000023 #define FILE_DEVICE_VIRTUAL_DISK 0x00000024 #define FILE_DEVICE_WAVE_IN 0x00000025 #define FILE_DEVICE_WAVE_OUT 0x00000026 #define FILE_DEVICE_8042_PORT 0x00000027 #define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 #define FILE_DEVICE_BATTERY 0x00000029 #define FILE_DEVICE_BUS_EXTENDER 0x0000002A #define FILE_DEVICE_MODEM 0x0000002B #define FILE_DEVICE_VDM 0x0000002C #define FILE_DEVICE_MASS_STORAGE 0x0000002D #define FILE_DEVICE_SMB 0x0000002E #define FILE_DEVICE_KS 0x0000002F #define FILE_DEVICE_CHANGER 0x00000030 #define FILE_DEVICE_SMARTCARD 0x00000031 #define FILE_DEVICE_ACPI 0x00000032 #define FILE_DEVICE_DVD 0x00000033 #define FILE_DEVICE_FULLSCREEN_VIDEO 0x00000034 #define FILE_DEVICE_DFS_FILE_SYSTEM 0x00000035 #define FILE_DEVICE_DFS_VOLUME 0x00000036 #define FILE_DEVICE_SERENUM 0x00000037 #define FILE_DEVICE_TERMSRV 0x00000038 #define FILE_DEVICE_KSEC 0x00000039 #define FILE_DEVICE_FIPS 0x0000003A /* Device characteristics */ #define FILE_REMOVABLE_MEDIA 0x00000001 #define FILE_READ_ONLY_DEVICE 0x00000002 #define FILE_FLOPPY_DISKETTE 0x00000004 #define FILE_WRITE_ONCE_MEDIA 0x00000008 #define FILE_REMOTE_DEVICE 0x00000010 #define FILE_DEVICE_IS_MOUNTED 0x00000020 #define FILE_VIRTUAL_VOLUME 0x00000040 #define FILE_AUTOGENERATED_DEVICE_NAME 0x00000080 #define FILE_DEVICE_SECURE_OPEN 0x00000100 /* Status codes */ #define STATUS_SUCCESS 0x00000000 #define STATUS_USER_APC 0x000000C0 #define STATUS_KERNEL_APC 0x00000100 #define STATUS_ALERTED 0x00000101 #define STATUS_TIMEOUT 0x00000102 #define STATUS_PENDING 0x00000103 #define STATUS_INVALID_PARAMETER 0xC000000D #define STATUS_INVALID_DEVICE_REQUEST 0xC0000010 #define STATUS_MORE_PROCESSING_REQUIRED 0xC0000016 #define STATUS_BUFFER_TOO_SMALL 0xC0000023 #define STATUS_MUTANT_NOT_OWNED 0xC0000046 #define STATUS_INVALID_PARAMETER_2 0xC00000F0 #define STATUS_INSUFFICIENT_RESOURCES 0xC000009A #define STATUS_WAIT_0 0x00000000 /* Memory pool types, for ExAllocatePoolWithTag() */ #define NonPagedPool 0x00000000 #define PagedPool 0x00000001 #define NonPagedPoolMustSucceed 0x00000002 #define DontUseThisType 0x00000003 #define NonPagedPoolCacheAligned 0x00000004 #define PagedPoolCacheAligned 0x00000005 #define NonPagedPoolCacheAlignedMustS 0x00000006 #define MaxPoolType 0x00000007 /* * IO_WORKITEM is an opaque structures that must be allocated * via IoAllocateWorkItem() and released via IoFreeWorkItem(). * Consequently, we can define it any way we want. */ typedef void (*io_workitem_func)(device_object *, void *); struct io_workitem { io_workitem_func iw_func; void *iw_ctx; list_entry iw_listentry; device_object *iw_dobj; int iw_idx; }; typedef struct io_workitem io_workitem; #define WORKQUEUE_CRITICAL 0 #define WORKQUEUE_DELAYED 1 #define WORKQUEUE_HYPERCRITICAL 2 #define WORKITEM_THREADS 4 #define WORKITEM_LEGACY_THREAD 3 #define WORKIDX_INC(x) (x) = (x + 1) % WORKITEM_LEGACY_THREAD /* * Older, deprecated work item API, needed to support NdisQueueWorkItem(). */ struct work_queue_item; typedef void (*work_item_func)(struct work_queue_item *, void *); struct work_queue_item { list_entry wqi_entry; work_item_func wqi_func; void *wqi_ctx; }; typedef struct work_queue_item work_queue_item; #define ExInitializeWorkItem(w, func, ctx) \ do { \ (w)->wqi_func = (func); \ (w)->wqi_ctx = (ctx); \ - INIT_LIST_HEAD(&((w)->wqi_entry)); \ + InitializeListHead(&((w)->wqi_entry)); \ } while (0); \ /* * FreeBSD's kernel stack is 2 pages in size by default. The * Windows stack is larger, so we need to give our threads more * stack pages. 4 should be enough, we use 8 just to extra safe. */ #define NDIS_KSTACK_PAGES 8 /* * Different kinds of function wrapping we can do. */ #define WINDRV_WRAP_STDCALL 1 #define WINDRV_WRAP_FASTCALL 2 #define WINDRV_WRAP_REGPARM 3 #define WINDRV_WRAP_CDECL 4 #define WINDRV_WRAP_AMD64 5 struct drvdb_ent { driver_object *windrv_object; void *windrv_devlist; ndis_cfg *windrv_regvals; interface_type windrv_bustype; STAILQ_ENTRY(drvdb_ent) link; }; extern image_patch_table ntoskrnl_functbl[]; typedef void (*funcptr)(void); typedef int (*matchfuncptr)(interface_type, void *, void *); __BEGIN_DECLS extern int windrv_libinit(void); extern int windrv_libfini(void); extern driver_object *windrv_lookup(vm_offset_t, char *); extern struct drvdb_ent *windrv_match(matchfuncptr, void *); extern int windrv_load(module_t, vm_offset_t, int, interface_type, void *, ndis_cfg *); extern int windrv_unload(module_t, vm_offset_t, int); extern int windrv_create_pdo(driver_object *, device_t); extern void windrv_destroy_pdo(driver_object *, device_t); extern device_object *windrv_find_pdo(driver_object *, device_t); extern int windrv_bus_attach(driver_object *, char *); extern int windrv_wrap(funcptr, funcptr *, int, int); extern int windrv_unwrap(funcptr); extern void ctxsw_utow(void); extern void ctxsw_wtou(void); extern int ntoskrnl_libinit(void); extern int ntoskrnl_libfini(void); + +extern void ntoskrnl_intr(void *); + +extern uint16_t ExQueryDepthSList(slist_header *); +extern slist_entry + *InterlockedPushEntrySList(slist_header *, slist_entry *); +extern slist_entry *InterlockedPopEntrySList(slist_header *); +extern uint32_t RtlUnicodeStringToAnsiString(ansi_string *, + unicode_string *, uint8_t); +extern uint32_t RtlAnsiStringToUnicodeString(unicode_string *, + ansi_string *, uint8_t); +extern void RtlInitAnsiString(ansi_string *, char *); +extern void RtlInitUnicodeString(unicode_string *, + uint16_t *); +extern void RtlFreeUnicodeString(unicode_string *); +extern void RtlFreeAnsiString(ansi_string *); extern void KeInitializeDpc(kdpc *, void *, void *); extern uint8_t KeInsertQueueDpc(kdpc *, void *, void *); extern uint8_t KeRemoveQueueDpc(kdpc *); extern void KeSetImportanceDpc(kdpc *, uint32_t); extern void KeSetTargetProcessorDpc(kdpc *, uint8_t); extern void KeFlushQueuedDpcs(void); extern uint32_t KeGetCurrentProcessorNumber(void); extern void KeInitializeTimer(ktimer *); extern void KeInitializeTimerEx(ktimer *, uint32_t); extern uint8_t KeSetTimer(ktimer *, int64_t, kdpc *); extern uint8_t KeSetTimerEx(ktimer *, int64_t, uint32_t, kdpc *); extern uint8_t KeCancelTimer(ktimer *); extern uint8_t KeReadStateTimer(ktimer *); -extern uint32_t KeWaitForSingleObject(nt_dispatch_header *, uint32_t, +extern uint32_t KeWaitForSingleObject(void *, uint32_t, uint32_t, uint8_t, int64_t *); extern void KeInitializeEvent(nt_kevent *, uint32_t, uint8_t); extern void KeClearEvent(nt_kevent *); extern uint32_t KeReadStateEvent(nt_kevent *); extern uint32_t KeSetEvent(nt_kevent *, uint32_t, uint8_t); extern uint32_t KeResetEvent(nt_kevent *); #ifdef __i386__ extern void KefAcquireSpinLockAtDpcLevel(kspin_lock *); extern void KefReleaseSpinLockFromDpcLevel(kspin_lock *); extern uint8_t KeAcquireSpinLockRaiseToDpc(kspin_lock *); #else extern void KeAcquireSpinLockAtDpcLevel(kspin_lock *); extern void KeReleaseSpinLockFromDpcLevel(kspin_lock *); #endif extern void KeInitializeSpinLock(kspin_lock *); +extern uint8_t KeAcquireInterruptSpinLock(kinterrupt *); +extern void KeReleaseInterruptSpinLock(kinterrupt *, uint8_t); +extern uint8_t KeSynchronizeExecution(kinterrupt *, void *, void *); extern uintptr_t InterlockedExchange(volatile uint32_t *, uintptr_t); extern void *ExAllocatePoolWithTag(uint32_t, size_t, uint32_t); extern void ExFreePool(void *); +extern uint32_t IoConnectInterrupt(kinterrupt **, void *, void *, + kspin_lock *, uint32_t, uint8_t, uint8_t, uint8_t, uint8_t, + uint32_t, uint8_t); +extern void MmBuildMdlForNonPagedPool(mdl *); +extern void IoDisconnectInterrupt(kinterrupt *); extern uint32_t IoAllocateDriverObjectExtension(driver_object *, void *, uint32_t, void **); extern void *IoGetDriverObjectExtension(driver_object *, void *); extern uint32_t IoCreateDevice(driver_object *, uint32_t, unicode_string *, uint32_t, uint32_t, uint8_t, device_object **); extern void IoDeleteDevice(device_object *); extern device_object *IoGetAttachedDevice(device_object *); extern uint32_t IofCallDriver(device_object *, irp *); extern void IofCompleteRequest(irp *, uint8_t); extern void IoAcquireCancelSpinLock(uint8_t *); extern void IoReleaseCancelSpinLock(uint8_t); extern uint8_t IoCancelIrp(irp *); extern void IoDetachDevice(device_object *); extern device_object *IoAttachDeviceToDeviceStack(device_object *, device_object *); extern mdl *IoAllocateMdl(void *, uint32_t, uint8_t, uint8_t, irp *); extern void IoFreeMdl(mdl *); extern io_workitem *IoAllocateWorkItem(device_object *); extern void ExQueueWorkItem(work_queue_item *, u_int32_t); extern void IoFreeWorkItem(io_workitem *); extern void IoQueueWorkItem(io_workitem *, io_workitem_func, uint32_t, void *); #define IoCallDriver(a, b) IofCallDriver(a, b) #define IoCompleteRequest(a, b) IofCompleteRequest(a, b) /* * On the Windows x86 arch, KeAcquireSpinLock() and KeReleaseSpinLock() * routines live in the HAL. We try to imitate this behavior. */ #ifdef __i386__ #define KeAcquireSpinLock(a, b) *(b) = KfAcquireSpinLock(a) #define KeReleaseSpinLock(a, b) KfReleaseSpinLock(a, b) #define KeRaiseIrql(a) KfRaiseIrql(a) #define KeLowerIrql(a) KfLowerIrql(a) #define KeAcquireSpinLockAtDpcLevel(a) KefAcquireSpinLockAtDpcLevel(a) #define KeReleaseSpinLockFromDpcLevel(a) KefReleaseSpinLockFromDpcLevel(a) #endif /* __i386__ */ #ifdef __amd64__ #define KeAcquireSpinLock(a, b) *(b) = KfAcquireSpinLock(a) #define KeReleaseSpinLock(a, b) KfReleaseSpinLock(a, b) /* * These may need to be redefined later; * not sure where they live on amd64 yet. */ #define KeRaiseIrql(a) KfRaiseIrql(a) #define KeLowerIrql(a) KfLowerIrql(a) #endif /* __amd64__ */ __END_DECLS #endif /* _NTOSKRNL_VAR_H_ */ Index: stable/5/sys/compat/ndis/subr_hal.c =================================================================== --- stable/5/sys/compat/ndis/subr_hal.c (revision 151520) +++ stable/5/sys/compat/ndis/subr_hal.c (revision 151521) @@ -1,419 +1,485 @@ /*- * 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 static void KeStallExecutionProcessor(uint32_t); static void WRITE_PORT_BUFFER_ULONG(uint32_t *, uint32_t *, uint32_t); static void WRITE_PORT_BUFFER_USHORT(uint16_t *, uint16_t *, uint32_t); static void WRITE_PORT_BUFFER_UCHAR(uint8_t *, uint8_t *, uint32_t); static void WRITE_PORT_ULONG(uint32_t *, uint32_t); static void WRITE_PORT_USHORT(uint16_t *, uint16_t); static void WRITE_PORT_UCHAR(uint8_t *, uint8_t); static uint32_t READ_PORT_ULONG(uint32_t *); static uint16_t READ_PORT_USHORT(uint16_t *); static uint8_t READ_PORT_UCHAR(uint8_t *); static void READ_PORT_BUFFER_ULONG(uint32_t *, uint32_t *, uint32_t); static void READ_PORT_BUFFER_USHORT(uint16_t *, uint16_t *, uint32_t); static void READ_PORT_BUFFER_UCHAR(uint8_t *, uint8_t *, uint32_t); static uint64_t KeQueryPerformanceCounter(uint64_t *); static void dummy (void); -extern struct mtx_pool *ndis_mtxpool; +#define NDIS_MAXCPUS 64 +static struct mtx disp_lock[NDIS_MAXCPUS]; int hal_libinit() { image_patch_table *patch; + int i; + for (i = 0; i < NDIS_MAXCPUS; i++) + mtx_init(&disp_lock[i], "HAL preemption lock", + "HAL lock", MTX_RECURSE|MTX_DEF); + patch = hal_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 hal_libfini() { image_patch_table *patch; + int i; + for (i = 0; i < NDIS_MAXCPUS; i++) + mtx_destroy(&disp_lock[i]); + patch = hal_functbl; while (patch->ipt_func != NULL) { windrv_unwrap(patch->ipt_wrap); patch++; } return(0); } static void KeStallExecutionProcessor(usecs) uint32_t usecs; { DELAY(usecs); return; } static void WRITE_PORT_ULONG(port, val) uint32_t *port; uint32_t val; { bus_space_write_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val); return; } static void WRITE_PORT_USHORT(port, val) uint16_t *port; uint16_t val; { bus_space_write_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val); return; } static void WRITE_PORT_UCHAR(port, val) uint8_t *port; uint8_t val; { bus_space_write_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val); return; } static void WRITE_PORT_BUFFER_ULONG(port, val, cnt) uint32_t *port; uint32_t *val; uint32_t cnt; { bus_space_write_multi_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val, cnt); return; } static void WRITE_PORT_BUFFER_USHORT(port, val, cnt) uint16_t *port; uint16_t *val; uint32_t cnt; { bus_space_write_multi_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val, cnt); return; } static void WRITE_PORT_BUFFER_UCHAR(port, val, cnt) uint8_t *port; uint8_t *val; uint32_t cnt; { bus_space_write_multi_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val, cnt); return; } static uint16_t READ_PORT_USHORT(port) uint16_t *port; { return(bus_space_read_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port)); } static uint32_t READ_PORT_ULONG(port) uint32_t *port; { return(bus_space_read_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port)); } static uint8_t READ_PORT_UCHAR(port) uint8_t *port; { return(bus_space_read_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port)); } static void READ_PORT_BUFFER_ULONG(port, val, cnt) uint32_t *port; uint32_t *val; uint32_t cnt; { bus_space_read_multi_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val, cnt); return; } static void READ_PORT_BUFFER_USHORT(port, val, cnt) uint16_t *port; uint16_t *val; uint32_t cnt; { bus_space_read_multi_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val, cnt); return; } static void READ_PORT_BUFFER_UCHAR(port, val, cnt) uint8_t *port; uint8_t *val; uint32_t cnt; { bus_space_read_multi_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val, cnt); return; } /* * The spinlock implementation in Windows differs from that of FreeBSD. * The basic operation of spinlocks involves two steps: 1) spin in a * tight loop while trying to acquire a lock, 2) after obtaining the * lock, disable preemption. (Note that on uniprocessor systems, you're * allowed to skip the first step and just lock out pre-emption, since * it's not possible for you to be in contention with another running * thread.) Later, you release the lock then re-enable preemption. * The difference between Windows and FreeBSD lies in how preemption * is disabled. In FreeBSD, it's done using critical_enter(), which on * the x86 arch translates to a cli instruction. This masks off all * interrupts, and effectively stops the scheduler from ever running * so _nothing_ can execute except the current thread. In Windows, * preemption is disabled by raising the processor IRQL to DISPATCH_LEVEL. * This stops other threads from running, but does _not_ block device * interrupts. This means ISRs can still run, and they can make other * threads runable, but those other threads won't be able to execute * until the current thread lowers the IRQL to something less than * DISPATCH_LEVEL. * * There's another commonly used IRQL in Windows, which is APC_LEVEL. * An APC is an Asynchronous Procedure Call, which differs from a DPC * (Defered Procedure Call) in that a DPC is queued up to run in * another thread, while an APC runs in the thread that scheduled * it (similar to a signal handler in a UNIX process). We don't * actually support the notion of APCs in FreeBSD, so for now, the * only IRQLs we're interested in are DISPATCH_LEVEL and PASSIVE_LEVEL. * * To simulate DISPATCH_LEVEL, we raise the current thread's priority * to PI_REALTIME, which is the highest we can give it. This should, * if I understand things correctly, prevent anything except for an * interrupt thread from preempting us. PASSIVE_LEVEL is basically * everything else. * * Be aware that, at least on the x86 arch, the Windows spinlock * functions are divided up in peculiar ways. The actual spinlock * functions are KfAcquireSpinLock() and KfReleaseSpinLock(), and * they live in HAL.dll. Meanwhile, KeInitializeSpinLock(), * KefAcquireSpinLockAtDpcLevel() and KefReleaseSpinLockFromDpcLevel() * live in ntoskrnl.exe. Most Windows source code will call * KeAcquireSpinLock() and KeReleaseSpinLock(), but these are just * macros that call KfAcquireSpinLock() and KfReleaseSpinLock(). * KefAcquireSpinLockAtDpcLevel() and KefReleaseSpinLockFromDpcLevel() * perform the lock aquisition/release functions without doing the * IRQL manipulation, and are used when one is already running at * DISPATCH_LEVEL. Make sense? Good. * * According to the Microsoft documentation, any thread that calls * KeAcquireSpinLock() must be running at IRQL <= DISPATCH_LEVEL. If * we detect someone trying to acquire a spinlock from DEVICE_LEVEL * or HIGH_LEVEL, we panic. + * + * Alternate sleep-lock-based spinlock implementation + * -------------------------------------------------- + * + * The earlier spinlock implementation was arguably a bit of a hack + * and presented several problems. It was basically designed to provide + * the functionality of spinlocks without incurring the wrath of + * WITNESS. We could get away with using both our spinlock implementation + * and FreeBSD sleep locks at the same time, but if WITNESS knew what + * we were really up to, it would have spanked us rather severely. + * + * There's another method we can use based entirely on sleep locks. + * First, it's important to realize that everything we're locking + * resides inside Project Evil itself: any critical data being locked + * by drivers belongs to the drivers, and should not be referenced + * by any other OS code outside of the NDISulator. The priority-based + * locking scheme has system-wide effects, just like real spinlocks + * (blocking preemption affects the whole CPU), but since we keep all + * our critical data private, we can use a simpler mechanism that + * affects only code/threads directly related to Project Evil. + * + * The idea is to create a sleep lock mutex for each CPU in the system. + * When a CPU running in the NDISulator wants to acquire a spinlock, it + * does the following: + * - Pin ourselves to the current CPU + * - Acquire the mutex for the current CPU + * - Spin on the spinlock variable using atomic test and set, just like + * a real spinlock. + * - Once we have the lock, we execute our critical code + * + * To give up the lock, we do: + * - Clear the spinlock variable with an atomic op + * - Release the per-CPU mutex + * - Unpin ourselves from the current CPU. + * + * On a uniprocessor system, this means all threads that access protected + * data are serialized through the per-CPU mutex. After one thread + * acquires the 'spinlock,' any other thread that uses a spinlock on the + * current CPU will block on the per-CPU mutex, which has the same general + * effect of blocking pre-emption, but _only_ for those threads that are + * running NDISulator code. + * + * On a multiprocessor system, threads on different CPUs all block on + * their respective per-CPU mutex, and the atomic test/set operation + * on the spinlock variable provides inter-CPU synchronization, though + * only for threads running NDISulator code. + * + * This method solves an important problem. In Windows, you're allowed + * to do an ExAllocatePoolWithTag() with a spinlock held, provided you + * allocate from NonPagedPool. This implies an atomic heap allocation + * that will not cause the current thread to sleep. (You can't sleep + * while holding real spinlock: clowns will eat you.) But in FreeBSD, + * malloc(9) _always_ triggers the acquisition of a sleep lock, even + * when you use M_NOWAIT. This is not a problem for FreeBSD native + * code: you're allowed to sleep in things like interrupt threads. But + * it is a problem with the old priority-based spinlock implementation: + * even though we get away with it most of the time, we really can't + * do a malloc(9) after doing a KeAcquireSpinLock() or KeRaiseIrql(). + * With the new implementation, it's not a problem: you're allowed to + * acquire more than one sleep lock (as long as you avoid lock order + * reversals). + * + * The one drawback to this approach is that now we have a lot of + * contention on one per-CPU mutex within the NDISulator code. Whether + * or not this is preferable to the expected Windows spinlock behavior + * of blocking pre-emption is debatable. */ uint8_t KfAcquireSpinLock(lock) kspin_lock *lock; { uint8_t oldirql; /* I am so going to hell for this. */ if (KeGetCurrentIrql() > DISPATCH_LEVEL) panic("IRQL_NOT_LESS_THAN_OR_EQUAL"); oldirql = KeRaiseIrql(DISPATCH_LEVEL); KeAcquireSpinLockAtDpcLevel(lock); return(oldirql); } void KfReleaseSpinLock(lock, newirql) kspin_lock *lock; uint8_t newirql; { KeReleaseSpinLockFromDpcLevel(lock); KeLowerIrql(newirql); return; } - uint8_t +uint8_t KeGetCurrentIrql() { - if (AT_DISPATCH_LEVEL(curthread)) + if (mtx_owned(&disp_lock[curthread->td_oncpu])) return(DISPATCH_LEVEL); return(PASSIVE_LEVEL); } static uint64_t KeQueryPerformanceCounter(freq) uint64_t *freq; { if (freq != NULL) *freq = hz; return((uint64_t)ticks); } uint8_t KfRaiseIrql(irql) uint8_t irql; { uint8_t oldirql; - if (irql < KeGetCurrentIrql()) + oldirql = KeGetCurrentIrql(); + if (irql < oldirql) panic("IRQL_NOT_LESS_THAN"); - if (KeGetCurrentIrql() == DISPATCH_LEVEL) - return(DISPATCH_LEVEL); + if (oldirql != DISPATCH_LEVEL) { + sched_pin(); + mtx_lock(&disp_lock[curthread->td_oncpu]); + } - mtx_lock_spin(&sched_lock); - oldirql = curthread->td_base_pri; - sched_prio(curthread, PI_REALTIME); -#if __FreeBSD_version < 600000 - curthread->td_base_pri = PI_REALTIME; -#endif - mtx_unlock_spin(&sched_lock); - return(oldirql); } void KfLowerIrql(oldirql) uint8_t oldirql; { if (oldirql == DISPATCH_LEVEL) return; if (KeGetCurrentIrql() != DISPATCH_LEVEL) panic("IRQL_NOT_GREATER_THAN"); - mtx_lock_spin(&sched_lock); -#if __FreeBSD_version < 600000 - curthread->td_base_pri = oldirql; -#endif - sched_prio(curthread, oldirql); - mtx_unlock_spin(&sched_lock); + mtx_unlock(&disp_lock[curthread->td_oncpu]); + sched_unpin(); return; } static void dummy() { printf ("hal dummy called...\n"); return; } image_patch_table hal_functbl[] = { IMPORT_SFUNC(KeStallExecutionProcessor, 1), IMPORT_SFUNC(WRITE_PORT_ULONG, 2), IMPORT_SFUNC(WRITE_PORT_USHORT, 2), IMPORT_SFUNC(WRITE_PORT_UCHAR, 2), IMPORT_SFUNC(WRITE_PORT_BUFFER_ULONG, 3), IMPORT_SFUNC(WRITE_PORT_BUFFER_USHORT, 3), IMPORT_SFUNC(WRITE_PORT_BUFFER_UCHAR, 3), IMPORT_SFUNC(READ_PORT_ULONG, 1), IMPORT_SFUNC(READ_PORT_USHORT, 1), IMPORT_SFUNC(READ_PORT_UCHAR, 1), IMPORT_SFUNC(READ_PORT_BUFFER_ULONG, 3), IMPORT_SFUNC(READ_PORT_BUFFER_USHORT, 3), IMPORT_SFUNC(READ_PORT_BUFFER_UCHAR, 3), IMPORT_FFUNC(KfAcquireSpinLock, 1), IMPORT_FFUNC(KfReleaseSpinLock, 1), IMPORT_SFUNC(KeGetCurrentIrql, 0), IMPORT_SFUNC(KeQueryPerformanceCounter, 1), IMPORT_FFUNC(KfLowerIrql, 1), IMPORT_FFUNC(KfRaiseIrql, 1), /* * 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/5/sys/compat/ndis/subr_ndis.c =================================================================== --- stable/5/sys/compat/ndis/subr_ndis.c (revision 151520) +++ stable/5/sys/compat/ndis/subr_ndis.c (revision 151521) @@ -1,3440 +1,3544 @@ /*- * 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 + static char ndis_filepath[MAXPATHLEN]; -extern struct nd_head ndis_devhead; 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, ndis_unicode_string *, ndis_handle *); + ndis_handle, uint32_t, unicode_string *, ndis_handle *); static void NdisOpenConfigurationKeyByName(ndis_status *, - ndis_handle, ndis_unicode_string *, ndis_handle *); + 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, ndis_unicode_string *, ndis_parm_type); + ndis_handle, unicode_string *, ndis_parm_type); static void NdisWriteConfiguration(ndis_status *, ndis_handle, - ndis_unicode_string *, ndis_config_parm *); + 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 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(ndis_ansi_string *, - ndis_unicode_string *); +static ndis_status NdisUnicodeStringToAnsiString(ansi_string *, + unicode_string *); static ndis_status - NdisAnsiStringToUnicodeString(ndis_unicode_string *, - ndis_ansi_string *); + 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 void NdisMSleep(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 void NdisInitializeString(ndis_unicode_string *, char *); -static void NdisInitAnsiString(ndis_ansi_string *, char *); -static void NdisInitUnicodeString(ndis_unicode_string *, - uint16_t *); -static void NdisFreeString(ndis_unicode_string *); +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 *, - ndis_unicode_string *, ndis_physaddr); + 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 NdisMIndicateStatusComplete(ndis_handle); static void NdisMIndicateStatus(ndis_handle, ndis_status, void *, uint32_t); +static void ndis_intr(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 ndis_status NdisMRegisterDevice(ndis_handle, - ndis_unicode_string *, ndis_unicode_string *, driver_dispatch **, + unicode_string *, unicode_string *, driver_dispatch **, void **, ndis_handle *); static ndis_status NdisMDeregisterDevice(ndis_handle); static ndis_status - NdisMQueryAdapterInstanceName(ndis_unicode_string *, - ndis_handle); + 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); } /* - * NDIS deals with strings in unicode format, so we have - * do deal with them that way too. For now, we only handle - * conversion between unicode and ASCII since that's all - * that device drivers care about. - */ - -int -ndis_ascii_to_unicode(ascii, unicode) - char *ascii; - uint16_t **unicode; -{ - uint16_t *ustr; - int i; - - if (*unicode == NULL) - *unicode = malloc(strlen(ascii) * 2, M_DEVBUF, M_NOWAIT); - - if (*unicode == NULL) - return(ENOMEM); - ustr = *unicode; - for (i = 0; i < strlen(ascii); i++) { - *ustr = (uint16_t)ascii[i]; - ustr++; - } - - return(0); -} - -int -ndis_unicode_to_ascii(unicode, ulen, ascii) - uint16_t *unicode; - int ulen; - char **ascii; -{ - uint8_t *astr; - int i; - - if (*ascii == NULL) - *ascii = malloc((ulen / 2) + 1, M_DEVBUF, M_NOWAIT|M_ZERO); - if (*ascii == NULL) - return(ENOMEM); - astr = *ascii; - for (i = 0; i < ulen / 2; i++) { - *astr = (uint8_t)unicode[i]; - astr++; - } - - return(0); -} - -/* * 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; return; } static void NdisTerminateWrapper(handle, syspec) ndis_handle handle; void *syspec; { /* Nothing to see here, move along. */ return; } 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) + 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) + 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); return; } 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; return; } static void NdisOpenConfigurationKeyByName(status, cfg, subkey, subhandle) ndis_status *status; ndis_handle cfg; - ndis_unicode_string *subkey; + unicode_string *subkey; ndis_handle *subhandle; { *subhandle = cfg; *status = NDIS_STATUS_SUCCESS; return; } static void NdisOpenConfigurationKeyByIndex(status, cfg, idx, subkey, subhandle) ndis_status *status; ndis_handle cfg; uint32_t idx; - ndis_unicode_string *subkey; + unicode_string *subkey; ndis_handle *subhandle; { *status = NDIS_STATUS_FAILURE; return; } 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; { - uint16_t *unicode; - ndis_unicode_string *ustr; + ndis_config_parm *p; + ndis_parmlist_entry *np; + unicode_string *us; + ansi_string as; int base = 0; + uint32_t val; + char tmp[32]; - unicode = (uint16_t *)&block->nmb_dummybuf; + 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: - ndis_ascii_to_unicode((char *)oid->oid_arg1, &unicode); - (*parm)->ncp_type = ndis_parm_string; - ustr = &(*parm)->ncp_parmdata.ncp_stringdata; - ustr->us_len = strlen((char *)oid->oid_arg1) * 2; - ustr->us_buf = unicode; + /* 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; - (*parm)->ncp_type = ndis_parm_int; - (*parm)->ncp_parmdata.ncp_intdata = + 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; - (*parm)->ncp_type = ndis_parm_hexint; - (*parm)->ncp_parmdata.ncp_intdata = +#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); } int ndis_strcasecmp(s1, s2) const char *s1; const char *s2; { char a, b; /* * In the kernel, toupper() is a macro. Have to be careful * not to use pointer arithmetic when passing it arguments. */ while(1) { a = *s1; b = *s2++; if (toupper(a) != toupper(b)) break; if (*s1++ == '\0') return(0); } return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1)); } int ndis_strncasecmp(s1, s2, n) const char *s1; const char *s2; size_t n; { char a, b; if (n != 0) { do { a = *s1; b = *s2++; if (toupper(a) != toupper(b)) return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1)); if (*s1++ == '\0') break; } while (--n != 0); } return(0); } static void NdisReadConfiguration(status, parm, cfg, key, type) ndis_status *status; ndis_config_parm **parm; ndis_handle cfg; - ndis_unicode_string *key; + unicode_string *key; ndis_parm_type type; { char *keystr = NULL; - uint16_t *unicode; 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); if (key->us_len == 0 || key->us_buf == NULL) { *status = NDIS_STATUS_FAILURE; return; } - ndis_unicode_to_ascii(key->us_buf, key->us_len, &keystr); - *parm = &block->nmb_replyparm; - bzero((char *)&block->nmb_replyparm, sizeof(ndis_config_parm)); - unicode = (uint16_t *)&block->nmb_dummybuf; + 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. */ #if __FreeBSD_version < 502113 TAILQ_FOREACH(e, &sc->ndis_ctx, link) { #else TAILQ_FOREACH(e, device_get_sysctl_ctx(sc->ndis_dev), link) { #endif oidp = e->entry; if (ndis_strcasecmp(oidp->oid_name, keystr) == 0) { if (strcmp((char *)oidp->oid_arg1, "UNSET") == 0) { - free(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); *status = NDIS_STATUS_FAILURE; return; } + *status = ndis_encode_parm(block, oidp, type, parm); - free(keystr, M_DEVBUF); + 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); - free(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); *status = NDIS_STATUS_FAILURE; return; } static ndis_status ndis_decode_parm(block, parm, val) ndis_miniport_block *block; ndis_config_parm *parm; char *val; { - ndis_unicode_string *ustr; - char *astr = NULL; + unicode_string *ustr; + ansi_string as; switch(parm->ncp_type) { case ndis_parm_string: ustr = &parm->ncp_parmdata.ncp_stringdata; - ndis_unicode_to_ascii(ustr->us_buf, ustr->us_len, &astr); - bcopy(astr, val, 254); - free(astr, M_DEVBUF); + 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; - ndis_unicode_string *key; + 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); - ndis_unicode_to_ascii(key->us_buf, key->us_len, &keystr); + 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) { - free(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); return; } /* See if the key already exists. */ #if __FreeBSD_version < 502113 TAILQ_FOREACH(e, &sc->ndis_ctx, link) { #else TAILQ_FOREACH(e, device_get_sysctl_ctx(sc->ndis_dev), link) { #endif oidp = e->entry; if (ndis_strcasecmp(oidp->oid_name, keystr) == 0) { /* Found it, set the value. */ strcpy((char *)oidp->oid_arg1, val); - free(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); return; } } /* Not found, add a new key with the specified value. */ ndis_add_sysctl(sc, keystr, "(dynamically set key)", val, CTLFLAG_RW); - free(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); *status = NDIS_STATUS_SUCCESS; return; } 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); + } + return; } /* * Initialize a Windows spinlock. */ static void NdisAllocateSpinLock(lock) ndis_spin_lock *lock; { KeInitializeSpinLock(&lock->nsl_spinlock); lock->nsl_kirql = 0; return; } /* * 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 return; } /* * Acquire a spinlock from IRQL <= DISPATCH_LEVEL. */ static void NdisAcquireSpinLock(lock) ndis_spin_lock *lock; { KeAcquireSpinLock(&lock->nsl_spinlock, &lock->nsl_kirql); return; } /* * Release a spinlock from IRQL == DISPATCH_LEVEL. */ static void NdisReleaseSpinLock(lock) ndis_spin_lock *lock; { KeReleaseSpinLock(&lock->nsl_spinlock, lock->nsl_kirql); return; } /* * Acquire a spinlock when already running at IRQL == DISPATCH_LEVEL. */ static void NdisDprAcquireSpinLock(lock) ndis_spin_lock *lock; { KeAcquireSpinLockAtDpcLevel(&lock->nsl_spinlock); return; } /* * Release a spinlock without leaving IRQL == DISPATCH_LEVEL. */ static void NdisDprReleaseSpinLock(lock) ndis_spin_lock *lock; { KeReleaseSpinLockFromDpcLevel(&lock->nsl_spinlock); return; } static void NdisInitializeReadWriteLock(lock) ndis_rw_lock *lock; { KeInitializeSpinLock(&lock->nrl_spinlock); bzero((char *)&lock->nrl_rsvd, sizeof(lock->nrl_rsvd)); return; } static void NdisAcquireReadWriteLock(lock, writeacc, state) 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]++; return; } 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]--; return; } 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; int 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; int 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, *ustr = NULL; + char *str = NULL; uint16_t flags; char msgbuf[ERRMSGLEN]; device_t dev; driver_object *drv; struct ndis_softc *sc; struct ifnet *ifp; + unicode_string us; + ansi_string as; block = (ndis_miniport_block *)adapter; dev = block->nmb_physdeviceobj->do_devext; drv = block->nmb_deviceobj->do_drvobj; sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; error = pe_get_message((vm_offset_t)drv->dro_driverstart, code, &str, &i, &flags); if (error == 0 && flags & MESSAGE_RESOURCE_UNICODE && ifp->if_flags & IFF_DEBUG) { - ustr = msgbuf; - ndis_unicode_to_ascii((uint16_t *)str, - ((i / 2)) > (ERRMSGLEN - 1) ? ERRMSGLEN : i, &ustr); - str = ustr; + RtlInitUnicodeString(&us, (uint16_t *)msgbuf); + if (RtlUnicodeStringToAnsiString(&as, &us, TRUE)) + return; + str = as.as_buf; } device_printf (dev, "NDIS ERROR: %x (%s)\n", code, str == NULL ? "unknown error" : str); if (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 (str != NULL) + RtlFreeAnsiString(&as); + return; } 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; return; } static void NdisMStartBufferPhysicalMapping(adapter, buf, mapreg, writedev, addrarray, arraysize) 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; return; } 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); return; } /* * 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); return; } 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); return; } /* * 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; { uint8_t irql; /* 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; KeAcquireSpinLock(&timer->nmt_block->nmb_lock, &irql); timer->nmt_nexttimer = timer->nmt_block->nmb_timerlist; timer->nmt_block->nmb_timerlist = timer; KeReleaseSpinLock(&timer->nmt_block->nmb_lock, irql); return; } /* * 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); return; } static void NdisMSetPeriodicTimer(timer, msecs) ndis_miniport_timer *timer; uint32_t msecs; { KeSetTimerEx(&timer->nmt_ktimer, ((int64_t)msecs * -10000), msecs, &timer->nmt_kdpc); return; } /* * 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); return; } 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; return; } 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; { return; } static void NdisReadNetworkAddress(status, addr, addrlen, adapter) ndis_status *status; void **addr; uint32_t *addrlen; ndis_handle adapter; { struct ndis_softc *sc; 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); +#ifdef IFP2ENADDR + if (bcmp(IFP2ENADDR(sc->ifp), empty, ETHER_ADDR_LEN) == 0) +#else if (bcmp(sc->arpcom.ac_enaddr, empty, ETHER_ADDR_LEN) == 0) +#endif *status = NDIS_STATUS_FAILURE; else { +#ifdef IFP2ENADDR + *addr = IFP2ENADDR(sc->ifp); +#else *addr = sc->arpcom.ac_enaddr; +#endif *addrlen = ETHER_ADDR_LEN; *status = NDIS_STATUS_SUCCESS; } return; } static ndis_status NdisQueryMapRegisterCount(bustype, cnt) uint32_t bustype; uint32_t *cnt; { *cnt = 8192; return(NDIS_STATUS_SUCCESS); } static ndis_status NdisMAllocateMapRegisters(adapter, dmachannel, dmasize, physmapneeded, maxmap) 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); return; } 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; return; } /* * This maps to bus_dmamem_alloc(). */ static void NdisMAllocateSharedMemory(adapter, len, cached, vaddr, paddr) 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; - sh->ndis_next = sc->ndis_shlist; - sc->ndis_shlist = sh; + InsertHeadList((&sc->ndis_shlist), (&sh->ndis_list)); + NDIS_UNLOCK(sc); return; } 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); return; } static ndis_status NdisMAllocateSharedMemoryAsync(adapter, len, cached, ctx) 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(adapter, len, cached, vaddr, paddr) 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, *prev; + 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); - sh = prev = sc->ndis_shlist; /* Sanity check: is list empty? */ - if (sh == NULL) + if (IsListEmpty(&sc->ndis_shlist)) return; - while (sh) { + 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; - prev = sh; - sh = sh->ndis_next; + 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); - if (sh == sc->ndis_shlist) - sc->ndis_shlist = sh->ndis_next; - else - prev->ndis_next = sh->ndis_next; - free(sh, M_DEVBUF); return; } static ndis_status NdisMMapIoSpace(vaddr, adapter, paddr, len) void **vaddr; ndis_handle adapter; ndis_physaddr paddr; uint32_t len; { 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_mem != NULL && paddr.np_quad == rman_get_start(sc->ndis_res_mem)) *vaddr = (void *)rman_get_virtual(sc->ndis_res_mem); else if (sc->ndis_res_altmem != NULL && paddr.np_quad == rman_get_start(sc->ndis_res_altmem)) *vaddr = (void *)rman_get_virtual(sc->ndis_res_altmem); else if (sc->ndis_res_am != NULL && paddr.np_quad == rman_get_start(sc->ndis_res_am)) *vaddr = (void *)rman_get_virtual(sc->ndis_res_am); else return(NDIS_STATUS_FAILURE); return(NDIS_STATUS_SUCCESS); } static void NdisMUnmapIoSpace(adapter, vaddr, len) ndis_handle adapter; void *vaddr; uint32_t len; { return; } static uint32_t NdisGetCacheFillSize(void) { return(128); } 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(adapter, is64, maxphysmap) 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 *cur; + ndis_packet_pool *p; + ndis_packet *packets; int i; - *pool = malloc((sizeof(ndis_packet) + protrsvdlen) * - ((descnum + NDIS_POOL_EXTRA) + 1), - M_DEVBUF, M_NOWAIT|M_ZERO); - - if (*pool == NULL) { + p = ExAllocatePoolWithTag(NonPagedPool, sizeof(ndis_packet_pool), 0); + if (p == NULL) { *status = NDIS_STATUS_RESOURCES; return; } - cur = (ndis_packet *)*pool; - KeInitializeSpinLock(&cur->np_lock); - cur->np_private.npp_flags = 0x1; /* mark the head of the list */ - cur->np_private.npp_totlen = 0; /* init deletetion flag */ - for (i = 0; i < (descnum + NDIS_POOL_EXTRA); i++) { - cur->np_private.npp_head = (ndis_handle)(cur + 1); - cur++; + 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; return; } 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 *head; - uint8_t irql; - uint32_t cnt; + ndis_packet_pool *p; - head = (ndis_packet *)pool; - KeAcquireSpinLock(&head->np_lock, &irql); - cnt = head->np_private.npp_count; - KeReleaseSpinLock(&head->np_lock, irql); - - return(cnt); + p = (ndis_packet_pool *)pool; + return(p->np_cnt - ExQueryDepthSList(&p->np_head)); } void NdisFreePacketPool(pool) ndis_handle pool; { - ndis_packet *head; + ndis_packet_pool *p; + int usage; +#ifdef NDIS_DEBUG_PACKETS uint8_t irql; +#endif - head = pool; + p = (ndis_packet_pool *)pool; - /* Mark this pool as 'going away.' */ +#ifdef NDIS_DEBUG_PACKETS + KeAcquireSpinLock(&p->np_lock, &irql); +#endif - KeAcquireSpinLock(&head->np_lock, &irql); - head->np_private.npp_totlen = 1; + usage = NdisPacketPoolUsage(pool); - /* If there are no buffers loaned out, destroy the 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 - if (head->np_private.npp_count == 0) { - KeReleaseSpinLock(&head->np_lock, irql); - free(pool, M_DEVBUF); - } else { - printf("NDIS: buggy driver deleting active packet pool!\n"); - KeReleaseSpinLock(&head->np_lock, irql); - } + ExFreePool(p->np_pktmem); + ExFreePool(p); return; } void NdisAllocatePacket(status, packet, pool) ndis_status *status; ndis_packet **packet; ndis_handle pool; { - ndis_packet *head, *pkt; + ndis_packet_pool *p; + ndis_packet *pkt; +#ifdef NDIS_DEBUG_PACKETS uint8_t irql; +#endif - head = (ndis_packet *)pool; - KeAcquireSpinLock(&head->np_lock, &irql); + p = (ndis_packet_pool *)pool; - if (head->np_private.npp_flags != 0x1) { - *status = NDIS_STATUS_FAILURE; - KeReleaseSpinLock(&head->np_lock, irql); +#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 - /* - * If this pool is marked as 'going away' don't allocate any - * more packets out of it. - */ + pkt = (ndis_packet *)InterlockedPopEntrySList(&p->np_head); - if (head->np_private.npp_totlen) { - *status = NDIS_STATUS_FAILURE; - KeReleaseSpinLock(&head->np_lock, irql); - return; - } +#ifdef NDIS_DEBUG_PACKETS + KeReleaseSpinLock(&p->np_lock, irql); +#endif - pkt = (ndis_packet *)head->np_private.npp_head; - if (pkt == NULL) { *status = NDIS_STATUS_RESOURCES; - KeReleaseSpinLock(&head->np_lock, irql); return; } - head->np_private.npp_head = pkt->np_private.npp_head; - pkt->np_private.npp_head = pkt->np_private.npp_tail = NULL; + bzero((char *)pkt, sizeof(ndis_packet)); + /* Save pointer to the pool. */ - pkt->np_private.npp_pool = head; + 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; - head->np_private.npp_count++; *status = NDIS_STATUS_SUCCESS; - KeReleaseSpinLock(&head->np_lock, irql); - return; } void NdisFreePacket(packet) ndis_packet *packet; { - ndis_packet *head; + ndis_packet_pool *p; +#ifdef NDIS_DEBUG_PACKETS uint8_t irql; +#endif - if (packet == NULL || packet->np_private.npp_pool == NULL) - return; + p = (ndis_packet_pool *)packet->np_private.npp_pool; - head = packet->np_private.npp_pool; - KeAcquireSpinLock(&head->np_lock, &irql); +#ifdef NDIS_DEBUG_PACKETS + KeAcquireSpinLock(&p->np_lock, &irql); +#endif - if (head->np_private.npp_flags != 0x1) { - KeReleaseSpinLock(&head->np_lock, irql); - return; + 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 - packet->np_private.npp_head = head->np_private.npp_head; - head->np_private.npp_head = (ndis_buffer *)packet; - head->np_private.npp_count--; - - /* - * If the pool has been marked for deletion and there are - * no more packets outstanding, nuke the pool. - */ - - if (head->np_private.npp_totlen && head->np_private.npp_count == 0) { - KeReleaseSpinLock(&head->np_lock, irql); - free(head, M_DEVBUF); - } else - KeReleaseSpinLock(&head->np_lock, irql); - return; } 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; } return; } 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; } return; } /* * 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; return; } static void NdisFreeBufferPool(pool) ndis_handle pool; { return; } 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; return; } static void NdisFreeBuffer(buf) ndis_buffer *buf; { IoFreeMdl(buf); return; } /* 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); return; } /* 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); return; } /* 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; return; } 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 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); return; } static void NdisSetEvent(event) ndis_event *event; { - KeSetEvent(&event->ne_event, 0, 0); + KeSetEvent(&event->ne_event, IO_NO_INCREMENT, FALSE); return; } static void NdisResetEvent(event) ndis_event *event; { KeResetEvent(&event->ne_event); return; } 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((nt_dispatch_header *)event, + rval = KeWaitForSingleObject(event, 0, 0, TRUE, msecs ? & duetime : NULL); if (rval == STATUS_TIMEOUT) return(FALSE); return(TRUE); } static ndis_status NdisUnicodeStringToAnsiString(dstr, sstr) - ndis_ansi_string *dstr; - ndis_unicode_string *sstr; + ansi_string *dstr; + unicode_string *sstr; { - if (dstr == NULL || sstr == NULL) + uint32_t rval; + + rval = RtlUnicodeStringToAnsiString(dstr, sstr, FALSE); + + if (rval == STATUS_INSUFFICIENT_RESOURCES) + return(NDIS_STATUS_RESOURCES); + if (rval) return(NDIS_STATUS_FAILURE); - if (ndis_unicode_to_ascii(sstr->us_buf, - sstr->us_len, &dstr->nas_buf)) - return(NDIS_STATUS_FAILURE); - dstr->nas_len = dstr->nas_maxlen = strlen(dstr->nas_buf); + return (NDIS_STATUS_SUCCESS); } static ndis_status NdisAnsiStringToUnicodeString(dstr, sstr) - ndis_unicode_string *dstr; - ndis_ansi_string *sstr; + unicode_string *dstr; + ansi_string *sstr; { - char *str; - if (dstr == NULL || sstr == NULL) + uint32_t rval; + + rval = RtlAnsiStringToUnicodeString(dstr, sstr, FALSE); + + if (rval == STATUS_INSUFFICIENT_RESOURCES) + return(NDIS_STATUS_RESOURCES); + if (rval) return(NDIS_STATUS_FAILURE); - str = malloc(sstr->nas_len + 1, M_DEVBUF, M_NOWAIT); - if (str == NULL) - return(NDIS_STATUS_FAILURE); - strncpy(str, sstr->nas_buf, sstr->nas_len); - *(str + sstr->nas_len) = '\0'; - if (ndis_ascii_to_unicode(str, &dstr->us_buf)) { - free(str, M_DEVBUF); - return(NDIS_STATUS_FAILURE); - } - dstr->us_len = dstr->us_maxlen = sstr->nas_len * 2; - free(str, M_DEVBUF); + 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 void +ndis_intr(arg) + void *arg; +{ + struct ndis_softc *sc; + struct ifnet *ifp; + int is_our_intr = 0; + int call_isr = 0; + ndis_miniport_interrupt *intr; + + sc = arg; + ifp = sc->ifp; + intr = sc->ndis_block->nmb_interrupt; + + if (intr == NULL || sc->ndis_block->nmb_miniportadapterctx == NULL) + return; + + if (sc->ndis_block->nmb_interrupt->ni_isrreq == TRUE) + ndis_isr(sc, &is_our_intr, &call_isr); + else { + ndis_disable_intr(sc); + call_isr = 1; + } + + if ((is_our_intr || call_isr)) + IoRequestDpc(sc->ndis_block->nmb_deviceobj, NULL, sc); + + return; +} + +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_isrfunc, adapter); + + /* If there's a MiniportEnableInterrupt() routine, call it. */ + + ndis_enable_intr(sc); + + 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); + + return; +} + static ndis_status NdisMRegisterInterrupt(intr, adapter, ivec, ilevel, reqisr, shared, imode) 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; - block->nmb_interrupt = intr; + intr->ni_dpccnt = 0; + intr->ni_isrfunc = ch->nmc_interrupt_func; + intr->ni_dpcfunc = ch->nmc_isr_func; - KeInitializeSpinLock(&intr->ni_dpccountlock); + 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; + struct ndis_softc *sc; + uint8_t irql; + + block = intr->ni_block; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + + /* 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); + return; } 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; return; } 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; return; } 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); return; } 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); return; } -static void +void NdisMSleep(usecs) uint32_t usecs; { - struct timeval tv; + ktimer timer; /* * During system bootstrap, (i.e. cold == 1), we aren't - * allowed to msleep(), so calling ndis_thsuspend() here - * will return 0, and we won't actually have delayed. This - * is a problem because some drivers expect NdisMSleep() - * to always wait, and might fail if the expected delay - * period does not in fact elapse. As a workaround, if the - * attempt to sleep delay fails, we do a hard DELAY() instead. + * allowed to sleep, so we have to do a hard DELAY() + * instead. */ - tv.tv_sec = 0; - tv.tv_usec = usecs; - if (ndis_thsuspend(curthread->td_proc, NULL, tvtohz(&tv)) == 0) + if (cold) DELAY(usecs); + else { + KeInitializeTimer(&timer); + KeSetTimer(&timer, ((int64_t)usecs * -10), NULL); + KeWaitForSingleObject(&timer, 0, 0, FALSE, NULL); + } return; } 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; int 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; int 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; { - uint8_t (*sync)(void *); - uint8_t rval; - uint8_t irql; - - if (syncfunc == NULL || syncctx == NULL) - return(0); - - sync = syncfunc; - KeAcquireSpinLock(&intr->ni_dpccountlock, &irql); - rval = MSCALL1(sync, syncctx); - KeReleaseSpinLock(&intr->ni_dpccountlock, irql); - - return(rval); + return(KeSynchronizeExecution(intr->ni_introbj, syncfunc, syncctx)); } /* * Return the number of 100 nanosecond intervals since * January 1, 1601. (?!?!) */ static void NdisGetCurrentSystemTime(tval) uint64_t *tval; { struct timespec ts; nanotime(&ts); *tval = (uint64_t)ts.tv_nsec / 100 + (uint64_t)ts.tv_sec * 10000000 + 11644473600; return; } /* * 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; return; } static void NdisInitializeString(dst, src) - ndis_unicode_string *dst; + unicode_string *dst; char *src; { - ndis_unicode_string *u; - - u = dst; - u->us_buf = NULL; - if (ndis_ascii_to_unicode(src, &u->us_buf)) - return; - u->us_len = u->us_maxlen = strlen(src) * 2; + ansi_string as; + RtlInitAnsiString(&as, src); + RtlAnsiStringToUnicodeString(dst, &as, TRUE); return; } static void NdisFreeString(str) - ndis_unicode_string *str; + unicode_string *str; { - if (str == NULL) - return; - if (str->us_buf != NULL) - free(str->us_buf, M_DEVBUF); - free(str, M_DEVBUF); + RtlFreeUnicodeString(str); return; } static ndis_status NdisMRemoveMiniport(adapter) ndis_handle *adapter; { return(NDIS_STATUS_SUCCESS); } static void NdisInitAnsiString(dst, src) - ndis_ansi_string *dst; + ansi_string *dst; char *src; { - ndis_ansi_string *a; - - a = dst; - if (a == NULL) - return; - if (src == NULL) { - a->nas_len = a->nas_maxlen = 0; - a->nas_buf = NULL; - } else { - a->nas_buf = src; - a->nas_len = a->nas_maxlen = strlen(src); - } - + RtlInitAnsiString(dst, src); return; } static void NdisInitUnicodeString(dst, src) - ndis_unicode_string *dst; + unicode_string *dst; uint16_t *src; { - ndis_unicode_string *u; - int i; - - u = dst; - if (u == NULL) - return; - if (src == NULL) { - u->us_len = u->us_maxlen = 0; - u->us_buf = NULL; - } else { - i = 0; - while(src[i] != 0) - i++; - u->us_buf = src; - u->us_len = u->us_maxlen = i * 2; - } - + RtlInitUnicodeString(dst, src); return; } 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; return; } 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); } return; } 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; 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); } /* 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; - ndis_unicode_string *filename; + 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; linker_file_t head, lf; caddr_t kldstart, kldend; - ndis_unicode_to_ascii(filename->us_buf, - filename->us_len, &afilename); + 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. */ /* * This is an evil trick for getting the head of the linked * file list, which is not exported from kern_linker.o. It * happens that linker file #1 is always the kernel, and is * always the first element in the list. */ head = linker_find_file_by_id(1); for (lf = head; lf != NULL; lf = TAILQ_NEXT(lf, link)) { if (ndis_find_sym(lf, afilename, "_start", &kldstart)) continue; if (ndis_find_sym(lf, afilename, "_end", &kldend)) continue; fh->nf_vp = lf; fh->nf_map = NULL; fh->nf_type = NDIS_FH_TYPE_MODULE; *filelength = fh->nf_maplen = (kldend - kldstart) & 0xFFFFFFFF; *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); mtx_lock(&Giant); /* Some threads don't have a current working directory. */ if (td->td_proc->p_fd->fd_rdir == NULL) td->td_proc->p_fd->fd_rdir = rootvnode; if (td->td_proc->p_fd->fd_cdir == NULL) td->td_proc->p_fd->fd_cdir = rootvnode; NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td); flags = FREAD; error = vn_open(&nd, &flags, 0, -1); if (error) { mtx_unlock(&Giant); *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, td); VOP_UNLOCK(nd.ni_vp, 0, td); mtx_unlock(&Giant); 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; return; } 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, resid; 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; } mtx_lock(&Giant); error = vn_rdwr(UIO_READ, fh->nf_vp, fh->nf_map, fh->nf_maplen, 0, UIO_SYSSPACE, 0, td->td_ucred, NOCRED, &resid, td); mtx_unlock(&Giant); if (error) *status = NDIS_STATUS_FAILURE; else { *status = NDIS_STATUS_SUCCESS; *mappedbuffer = fh->nf_map; } return; } 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; return; } static void NdisCloseFile(filehandle) ndis_handle filehandle; { struct thread *td = curthread; ndis_fh *fh; 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) { mtx_lock(&Giant); vn_close(fh->nf_vp, FREAD, td->td_ucred, td); mtx_unlock(&Giant); } fh->nf_vp = NULL; free(fh->nf_name, M_DEVBUF); ExFreePool(fh); return; } static uint8_t NdisSystemProcessorCount() { return(mp_ncpus); } 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); return; } 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); return; } /* * 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; return; } 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); return; } static ndis_status NdisMRegisterDevice(handle, devname, symname, majorfuncs, devobj, devhandle) ndis_handle handle; - ndis_unicode_string *devname; - ndis_unicode_string *symname; + 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) - ndis_unicode_string *name; + 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; - ndis_ascii_to_unicode(__DECONST(char *, - device_get_nameunit(dev)), &name->us_buf); - name->us_len = strlen(device_get_nameunit(dev)) * 2; + 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; { return; } static void dummy() { printf ("NDIS dummy called...\n"); return; } /* * 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(NdisScheduleWorkItem, 1), IMPORT_SFUNC(NdisMIndicateStatusComplete, 1), IMPORT_SFUNC(NdisMIndicateStatus, 4), IMPORT_SFUNC(NdisSystemProcessorCount, 0), 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(NdisGetSystemUpTime, 1), 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, 1), + 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/5/sys/compat/ndis/subr_ntoskrnl.c =================================================================== --- stable/5/sys/compat/ndis/subr_ntoskrnl.c (revision 151520) +++ stable/5/sys/compat/ndis/subr_ntoskrnl.c (revision 151521) @@ -1,3442 +1,4091 @@ /*- * 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 #if __FreeBSD_version > 502113 #include #endif #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 +#ifdef NTOSKRNL_DEBUG_TIMERS +static int sysctl_show_timers(SYSCTL_HANDLER_ARGS); + +SYSCTL_PROC(_debug, OID_AUTO, ntoskrnl_timers, CTLFLAG_RW, 0, 0, + sysctl_show_timers, "I", "Show ntoskrnl timer stats"); +#endif + struct kdpc_queue { - list_entry kq_high; - list_entry kq_low; - list_entry kq_med; + list_entry kq_disp; struct thread *kq_td; - int kq_state; int kq_cpu; int kq_exit; - struct mtx kq_lock; + int kq_running; + kspin_lock kq_lock; nt_kevent kq_proc; nt_kevent kq_done; nt_kevent kq_dead; }; typedef struct kdpc_queue kdpc_queue; -static uint8_t RtlEqualUnicodeString(ndis_unicode_string *, - ndis_unicode_string *, uint8_t); -static void RtlCopyUnicodeString(ndis_unicode_string *, - ndis_unicode_string *); -static ndis_status RtlUnicodeStringToAnsiString(ndis_ansi_string *, - ndis_unicode_string *, uint8_t); -static ndis_status RtlAnsiStringToUnicodeString(ndis_unicode_string *, - ndis_ansi_string *, uint8_t); +struct wb_ext { + struct cv we_cv; + struct thread *we_td; +}; + +typedef struct wb_ext wb_ext; + +#define NTOSKRNL_TIMEOUTS 256 +#ifdef NTOSKRNL_DEBUG_TIMERS +static uint64_t ntoskrnl_timer_fires; +static uint64_t ntoskrnl_timer_sets; +static uint64_t ntoskrnl_timer_reloads; +static uint64_t ntoskrnl_timer_cancels; +#endif + +struct callout_entry { + struct callout ce_callout; + list_entry ce_list; +}; + +typedef struct callout_entry callout_entry; + +static struct list_entry ntoskrnl_calllist; +static struct mtx ntoskrnl_calllock; + +static struct list_entry ntoskrnl_intlist; +static kspin_lock ntoskrnl_intlock; + +static uint8_t RtlEqualUnicodeString(unicode_string *, + unicode_string *, uint8_t); +static void RtlCopyUnicodeString(unicode_string *, + unicode_string *); static irp *IoBuildSynchronousFsdRequest(uint32_t, device_object *, void *, uint32_t, uint64_t *, nt_kevent *, io_status_block *); static irp *IoBuildAsynchronousFsdRequest(uint32_t, device_object *, void *, uint32_t, uint64_t *, io_status_block *); static irp *IoBuildDeviceIoControlRequest(uint32_t, device_object *, void *, uint32_t, void *, uint32_t, uint8_t, nt_kevent *, io_status_block *); static irp *IoAllocateIrp(uint8_t, uint8_t); static void IoReuseIrp(irp *, uint32_t); static void IoFreeIrp(irp *); static void IoInitializeIrp(irp *, uint16_t, uint8_t); static irp *IoMakeAssociatedIrp(irp *, uint8_t); static uint32_t KeWaitForMultipleObjects(uint32_t, nt_dispatch_header **, uint32_t, uint32_t, uint32_t, uint8_t, int64_t *, wait_block *); -static void ntoskrnl_wakeup(void *); +static void ntoskrnl_waittest(nt_dispatch_header *, uint32_t); +static void ntoskrnl_satisfy_wait(nt_dispatch_header *, struct thread *); +static void ntoskrnl_satisfy_multiple_waits(wait_block *); +static int ntoskrnl_is_signalled(nt_dispatch_header *, struct thread *); +static void ntoskrnl_insert_timer(ktimer *, int); +static void ntoskrnl_remove_timer(ktimer *); +#ifdef NTOSKRNL_DEBUG_TIMERS +static void ntoskrnl_show_timers(void); +#endif static void ntoskrnl_timercall(void *); -static void ntoskrnl_run_dpc(void *); static void ntoskrnl_dpc_thread(void *); static void ntoskrnl_destroy_dpc_threads(void); static void ntoskrnl_destroy_workitem_threads(void); static void ntoskrnl_workitem_thread(void *); static void ntoskrnl_workitem(device_object *, void *); +static void ntoskrnl_unicode_to_ascii(uint16_t *, char *, int); +static void ntoskrnl_ascii_to_unicode(char *, uint16_t *, int); static uint8_t ntoskrnl_insert_dpc(list_entry *, kdpc *); static void WRITE_REGISTER_USHORT(uint16_t *, uint16_t); static uint16_t READ_REGISTER_USHORT(uint16_t *); static void WRITE_REGISTER_ULONG(uint32_t *, uint32_t); static uint32_t READ_REGISTER_ULONG(uint32_t *); static void WRITE_REGISTER_UCHAR(uint8_t *, uint8_t); static uint8_t READ_REGISTER_UCHAR(uint8_t *); static int64_t _allmul(int64_t, int64_t); static int64_t _alldiv(int64_t, int64_t); static int64_t _allrem(int64_t, int64_t); static int64_t _allshr(int64_t, uint8_t); static int64_t _allshl(int64_t, uint8_t); static uint64_t _aullmul(uint64_t, uint64_t); static uint64_t _aulldiv(uint64_t, uint64_t); static uint64_t _aullrem(uint64_t, uint64_t); static uint64_t _aullshr(uint64_t, uint8_t); static uint64_t _aullshl(uint64_t, uint8_t); static slist_entry *ntoskrnl_pushsl(slist_header *, slist_entry *); static slist_entry *ntoskrnl_popsl(slist_header *); static void ExInitializePagedLookasideList(paged_lookaside_list *, lookaside_alloc_func *, lookaside_free_func *, uint32_t, size_t, uint32_t, uint16_t); static void ExDeletePagedLookasideList(paged_lookaside_list *); static void ExInitializeNPagedLookasideList(npaged_lookaside_list *, lookaside_alloc_func *, lookaside_free_func *, uint32_t, size_t, uint32_t, uint16_t); static void ExDeleteNPagedLookasideList(npaged_lookaside_list *); static slist_entry - *InterlockedPushEntrySList(slist_header *, slist_entry *); -static slist_entry *InterlockedPopEntrySList(slist_header *); -static slist_entry *ExInterlockedPushEntrySList(slist_header *, slist_entry *, kspin_lock *); static slist_entry *ExInterlockedPopEntrySList(slist_header *, kspin_lock *); -static uint16_t ExQueryDepthSList(slist_header *); static uint32_t InterlockedIncrement(volatile uint32_t *); static uint32_t InterlockedDecrement(volatile uint32_t *); static void ExInterlockedAddLargeStatistic(uint64_t *, uint32_t); static uint32_t MmSizeOfMdl(void *, size_t); -static void MmBuildMdlForNonPagedPool(mdl *); static void *MmMapLockedPages(mdl *, uint8_t); static void *MmMapLockedPagesSpecifyCache(mdl *, uint8_t, uint32_t, void *, uint32_t, uint32_t); static void MmUnmapLockedPages(void *, mdl *); static uint8_t MmIsAddressValid(void *); static size_t RtlCompareMemory(const void *, const void *, size_t); -static void RtlInitAnsiString(ndis_ansi_string *, char *); -static void RtlInitUnicodeString(ndis_unicode_string *, - uint16_t *); -static void RtlFreeUnicodeString(ndis_unicode_string *); -static void RtlFreeAnsiString(ndis_ansi_string *); -static ndis_status RtlUnicodeStringToInteger(ndis_unicode_string *, +static ndis_status RtlUnicodeStringToInteger(unicode_string *, uint32_t, uint32_t *); static int atoi (const char *); static long atol (const char *); static int rand(void); static void srand(unsigned int); static void ntoskrnl_time(uint64_t *); static uint8_t IoIsWdmVersionAvailable(uint8_t, uint8_t); static void ntoskrnl_thrfunc(void *); static ndis_status PsCreateSystemThread(ndis_handle *, uint32_t, void *, ndis_handle, void *, void *, void *); static ndis_status PsTerminateSystemThread(ndis_status); static ndis_status IoGetDeviceProperty(device_object *, uint32_t, uint32_t, void *, uint32_t *); static void KeInitializeMutex(kmutant *, uint32_t); static uint32_t KeReleaseMutex(kmutant *, uint8_t); static uint32_t KeReadStateMutex(kmutant *); static ndis_status ObReferenceObjectByHandle(ndis_handle, uint32_t, void *, uint8_t, void **, void **); static void ObfDereferenceObject(void *); static uint32_t ZwClose(ndis_handle); static void *ntoskrnl_memset(void *, int, size_t); static char *ntoskrnl_strstr(char *, char *); +static int ntoskrnl_toupper(int); +static int ntoskrnl_tolower(int); static funcptr ntoskrnl_findwrap(funcptr); static uint32_t DbgPrint(char *, ...); static void DbgBreakPoint(void); static void dummy(void); static struct mtx ntoskrnl_dispatchlock; -static kspin_lock ntoskrnl_global; +static struct mtx ntoskrnl_interlock; static kspin_lock ntoskrnl_cancellock; static int ntoskrnl_kth = 0; static struct nt_objref_head ntoskrnl_reflist; static uma_zone_t mdl_zone; static uma_zone_t iw_zone; static struct kdpc_queue *kq_queues; static struct kdpc_queue *wq_queues; static int wq_idx = 0; int ntoskrnl_libinit() { image_patch_table *patch; int error; struct proc *p; kdpc_queue *kq; + callout_entry *e; int i; char name[64]; mtx_init(&ntoskrnl_dispatchlock, "ntoskrnl dispatch lock", MTX_NDIS_LOCK, MTX_DEF|MTX_RECURSE); - KeInitializeSpinLock(&ntoskrnl_global); + mtx_init(&ntoskrnl_interlock, MTX_NTOSKRNL_SPIN_LOCK, NULL, MTX_SPIN); KeInitializeSpinLock(&ntoskrnl_cancellock); + KeInitializeSpinLock(&ntoskrnl_intlock); TAILQ_INIT(&ntoskrnl_reflist); + InitializeListHead(&ntoskrnl_calllist); + InitializeListHead(&ntoskrnl_intlist); + mtx_init(&ntoskrnl_calllock, MTX_NTOSKRNL_SPIN_LOCK, NULL, MTX_SPIN); + kq_queues = ExAllocatePoolWithTag(NonPagedPool, sizeof(kdpc_queue) * mp_ncpus, 0); if (kq_queues == NULL) return(ENOMEM); wq_queues = ExAllocatePoolWithTag(NonPagedPool, sizeof(kdpc_queue) * WORKITEM_THREADS, 0); if (wq_queues == NULL) return(ENOMEM); bzero((char *)kq_queues, sizeof(kdpc_queue) * mp_ncpus); bzero((char *)wq_queues, sizeof(kdpc_queue) * WORKITEM_THREADS); /* * Launch the DPC threads. */ for (i = 0; i < mp_ncpus; i++) { kq = kq_queues + i; kq->kq_cpu = i; sprintf(name, "Windows DPC %d", i); error = kthread_create(ntoskrnl_dpc_thread, kq, &p, RFHIGHPID, NDIS_KSTACK_PAGES, name); if (error) panic("failed to launch DPC thread"); } /* * Launch the workitem threads. */ for (i = 0; i < WORKITEM_THREADS; i++) { kq = wq_queues + i; sprintf(name, "Windows Workitem %d", i); error = kthread_create(ntoskrnl_workitem_thread, kq, &p, RFHIGHPID, NDIS_KSTACK_PAGES, name); if (error) panic("failed to launch workitem thread"); } patch = ntoskrnl_functbl; while (patch->ipt_func != NULL) { windrv_wrap((funcptr)patch->ipt_func, (funcptr *)&patch->ipt_wrap, patch->ipt_argcnt, patch->ipt_ftype); patch++; } + for (i = 0; i < NTOSKRNL_TIMEOUTS; i++) { + e = ExAllocatePoolWithTag(NonPagedPool, + sizeof(callout_entry), 0); + if (e == NULL) + panic("failed to allocate timeouts"); + mtx_lock_spin(&ntoskrnl_calllock); + InsertHeadList((&ntoskrnl_calllist), (&e->ce_list)); + mtx_unlock_spin(&ntoskrnl_calllock); + } + /* * MDLs are supposed to be variable size (they describe * buffers containing some number of pages, but we don't * know ahead of time how many pages that will be). But * always allocating them off the heap is very slow. As * a compromise, we create an MDL UMA zone big enough to * handle any buffer requiring up to 16 pages, and we * use those for any MDLs for buffers of 16 pages or less * in size. For buffers larger than that (which we assume * will be few and far between, we allocate the MDLs off * the heap. */ mdl_zone = uma_zcreate("Windows MDL", MDL_ZONE_SIZE, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); iw_zone = uma_zcreate("Windows WorkItem", sizeof(io_workitem), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); return(0); } int ntoskrnl_libfini() { image_patch_table *patch; + callout_entry *e; + list_entry *l; patch = ntoskrnl_functbl; while (patch->ipt_func != NULL) { windrv_unwrap(patch->ipt_wrap); patch++; } - /* Stop the DPC queues. */ - ntoskrnl_destroy_dpc_threads(); /* Stop the workitem queues. */ ntoskrnl_destroy_workitem_threads(); + /* Stop the DPC queues. */ + ntoskrnl_destroy_dpc_threads(); ExFreePool(kq_queues); ExFreePool(wq_queues); uma_zdestroy(mdl_zone); uma_zdestroy(iw_zone); + mtx_lock_spin(&ntoskrnl_calllock); + while(!IsListEmpty(&ntoskrnl_calllist)) { + l = RemoveHeadList(&ntoskrnl_calllist); + e = CONTAINING_RECORD(l, callout_entry, ce_list); + mtx_unlock_spin(&ntoskrnl_calllock); + ExFreePool(e); + mtx_lock_spin(&ntoskrnl_calllock); + } + mtx_unlock_spin(&ntoskrnl_calllock); + mtx_destroy(&ntoskrnl_dispatchlock); + mtx_destroy(&ntoskrnl_interlock); + mtx_destroy(&ntoskrnl_calllock); return(0); } /* * We need to be able to reference this externally from the wrapper; * GCC only generates a local implementation of memset. */ static void * ntoskrnl_memset(buf, ch, size) void *buf; int ch; size_t size; { return(memset(buf, ch, size)); } static char * ntoskrnl_strstr(s, find) char *s, *find; { char c, sc; size_t len; if ((c = *find++) != 0) { len = strlen(find); do { do { if ((sc = *s++) == 0) return (NULL); } while (sc != c); } while (strncmp(s, find, len) != 0); s--; } return ((char *)s); } +static int +ntoskrnl_toupper(c) + int c; +{ + return(toupper(c)); +} +static int +ntoskrnl_tolower(c) + int c; +{ + return(tolower(c)); +} + static uint8_t RtlEqualUnicodeString(str1, str2, caseinsensitive) - ndis_unicode_string *str1; - ndis_unicode_string *str2; + unicode_string *str1; + unicode_string *str2; uint8_t caseinsensitive; { int i; if (str1->us_len != str2->us_len) return(FALSE); for (i = 0; i < str1->us_len; i++) { if (caseinsensitive == TRUE) { if (toupper((char)(str1->us_buf[i] & 0xFF)) != toupper((char)(str2->us_buf[i] & 0xFF))) return(FALSE); } else { if (str1->us_buf[i] != str2->us_buf[i]) return(FALSE); } } return(TRUE); } static void RtlCopyUnicodeString(dest, src) - ndis_unicode_string *dest; - ndis_unicode_string *src; + unicode_string *dest; + unicode_string *src; { if (dest->us_maxlen >= src->us_len) dest->us_len = src->us_len; else dest->us_len = dest->us_maxlen; memcpy(dest->us_buf, src->us_buf, dest->us_len); return; } -static ndis_status +static void +ntoskrnl_ascii_to_unicode(ascii, unicode, len) + char *ascii; + uint16_t *unicode; + int len; +{ + int i; + uint16_t *ustr; + + ustr = unicode; + for (i = 0; i < len; i++) { + *ustr = (uint16_t)ascii[i]; + ustr++; + } + + return; +} + +static void +ntoskrnl_unicode_to_ascii(unicode, ascii, len) + uint16_t *unicode; + char *ascii; + int len; +{ + int i; + uint8_t *astr; + + astr = ascii; + for (i = 0; i < len / 2; i++) { + *astr = (uint8_t)unicode[i]; + astr++; + } + + return; +} + +uint32_t RtlUnicodeStringToAnsiString(dest, src, allocate) - ndis_ansi_string *dest; - ndis_unicode_string *src; + ansi_string *dest; + unicode_string *src; uint8_t allocate; { - char *astr = NULL; - if (dest == NULL || src == NULL) return(NDIS_STATUS_FAILURE); + + dest->as_len = src->us_len / 2; + if (dest->as_maxlen < dest->as_len) + dest->as_len = dest->as_maxlen; + if (allocate == TRUE) { - if (ndis_unicode_to_ascii(src->us_buf, src->us_len, &astr)) - return(NDIS_STATUS_FAILURE); - dest->nas_buf = astr; - dest->nas_len = dest->nas_maxlen = strlen(astr); + dest->as_buf = ExAllocatePoolWithTag(NonPagedPool, + (src->us_len / 2) + 1, 0); + if (dest->as_buf == NULL) + return(STATUS_INSUFFICIENT_RESOURCES); + dest->as_len = dest->as_maxlen = src->us_len / 2; } else { - dest->nas_len = src->us_len / 2; /* XXX */ - if (dest->nas_maxlen < dest->nas_len) - dest->nas_len = dest->nas_maxlen; - ndis_unicode_to_ascii(src->us_buf, dest->nas_len * 2, - &dest->nas_buf); + dest->as_len = src->us_len / 2; /* XXX */ + if (dest->as_maxlen < dest->as_len) + dest->as_len = dest->as_maxlen; } - return (NDIS_STATUS_SUCCESS); + + ntoskrnl_unicode_to_ascii(src->us_buf, dest->as_buf, + dest->as_len * 2); + + return (STATUS_SUCCESS); } -static ndis_status +uint32_t RtlAnsiStringToUnicodeString(dest, src, allocate) - ndis_unicode_string *dest; - ndis_ansi_string *src; + unicode_string *dest; + ansi_string *src; uint8_t allocate; { - uint16_t *ustr = NULL; - if (dest == NULL || src == NULL) return(NDIS_STATUS_FAILURE); if (allocate == TRUE) { - if (ndis_ascii_to_unicode(src->nas_buf, &ustr)) - return(NDIS_STATUS_FAILURE); - dest->us_buf = ustr; - dest->us_len = dest->us_maxlen = strlen(src->nas_buf) * 2; + dest->us_buf = ExAllocatePoolWithTag(NonPagedPool, + src->as_len * 2, 0); + if (dest->us_buf == NULL) + return(STATUS_INSUFFICIENT_RESOURCES); + dest->us_len = dest->us_maxlen = strlen(src->as_buf) * 2; } else { - dest->us_len = src->nas_len * 2; /* XXX */ + dest->us_len = src->as_len * 2; /* XXX */ if (dest->us_maxlen < dest->us_len) dest->us_len = dest->us_maxlen; - ndis_ascii_to_unicode(src->nas_buf, &dest->us_buf); } - return (NDIS_STATUS_SUCCESS); + + ntoskrnl_ascii_to_unicode(src->as_buf, dest->us_buf, + dest->us_len / 2); + + return (STATUS_SUCCESS); } void * ExAllocatePoolWithTag(pooltype, len, tag) uint32_t pooltype; size_t len; uint32_t tag; { void *buf; - buf = malloc(len, M_DEVBUF, M_NOWAIT); + buf = malloc(len, M_DEVBUF, M_NOWAIT|M_ZERO); if (buf == NULL) return(NULL); + return(buf); } void ExFreePool(buf) void *buf; { free(buf, M_DEVBUF); return; } uint32_t IoAllocateDriverObjectExtension(drv, clid, extlen, ext) driver_object *drv; void *clid; uint32_t extlen; void **ext; { custom_extension *ce; ce = ExAllocatePoolWithTag(NonPagedPool, sizeof(custom_extension) + extlen, 0); if (ce == NULL) return(STATUS_INSUFFICIENT_RESOURCES); ce->ce_clid = clid; - INSERT_LIST_TAIL((&drv->dro_driverext->dre_usrext), (&ce->ce_list)); + InsertTailList((&drv->dro_driverext->dre_usrext), (&ce->ce_list)); *ext = (void *)(ce + 1); return(STATUS_SUCCESS); } void * IoGetDriverObjectExtension(drv, clid) driver_object *drv; void *clid; { list_entry *e; custom_extension *ce; /* * Sanity check. Our dummy bus drivers don't have * any driver extentions. */ if (drv->dro_driverext == NULL) return(NULL); e = drv->dro_driverext->dre_usrext.nle_flink; while (e != &drv->dro_driverext->dre_usrext) { ce = (custom_extension *)e; if (ce->ce_clid == clid) return((void *)(ce + 1)); e = e->nle_flink; } return(NULL); } uint32_t IoCreateDevice(drv, devextlen, devname, devtype, devchars, exclusive, newdev) driver_object *drv; uint32_t devextlen; unicode_string *devname; uint32_t devtype; uint32_t devchars; uint8_t exclusive; device_object **newdev; { device_object *dev; dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device_object), 0); if (dev == NULL) return(STATUS_INSUFFICIENT_RESOURCES); dev->do_type = devtype; dev->do_drvobj = drv; dev->do_currirp = NULL; dev->do_flags = 0; if (devextlen) { dev->do_devext = ExAllocatePoolWithTag(NonPagedPool, devextlen, 0); if (dev->do_devext == NULL) { ExFreePool(dev); return(STATUS_INSUFFICIENT_RESOURCES); } bzero(dev->do_devext, devextlen); } else dev->do_devext = NULL; dev->do_size = sizeof(device_object) + devextlen; dev->do_refcnt = 1; dev->do_attacheddev = NULL; dev->do_nextdev = NULL; dev->do_devtype = devtype; dev->do_stacksize = 1; dev->do_alignreq = 1; dev->do_characteristics = devchars; dev->do_iotimer = NULL; KeInitializeEvent(&dev->do_devlock, EVENT_TYPE_SYNC, TRUE); /* * Vpd is used for disk/tape devices, * but we don't support those. (Yet.) */ dev->do_vpb = NULL; dev->do_devobj_ext = ExAllocatePoolWithTag(NonPagedPool, sizeof(devobj_extension), 0); if (dev->do_devobj_ext == NULL) { if (dev->do_devext != NULL) ExFreePool(dev->do_devext); ExFreePool(dev); return(STATUS_INSUFFICIENT_RESOURCES); } dev->do_devobj_ext->dve_type = 0; dev->do_devobj_ext->dve_size = sizeof(devobj_extension); dev->do_devobj_ext->dve_devobj = dev; /* * Attach this device to the driver object's list * of devices. Note: this is not the same as attaching * the device to the device stack. The driver's AddDevice * routine must explicitly call IoAddDeviceToDeviceStack() * to do that. */ if (drv->dro_devobj == NULL) { drv->dro_devobj = dev; dev->do_nextdev = NULL; } else { dev->do_nextdev = drv->dro_devobj; drv->dro_devobj = dev; } *newdev = dev; return(STATUS_SUCCESS); } void IoDeleteDevice(dev) device_object *dev; { device_object *prev; if (dev == NULL) return; if (dev->do_devobj_ext != NULL) ExFreePool(dev->do_devobj_ext); if (dev->do_devext != NULL) ExFreePool(dev->do_devext); /* Unlink the device from the driver's device list. */ prev = dev->do_drvobj->dro_devobj; if (prev == dev) dev->do_drvobj->dro_devobj = dev->do_nextdev; else { while (prev->do_nextdev != dev) prev = prev->do_nextdev; prev->do_nextdev = dev->do_nextdev; } ExFreePool(dev); return; } device_object * IoGetAttachedDevice(dev) device_object *dev; { device_object *d; if (dev == NULL) return (NULL); d = dev; while (d->do_attacheddev != NULL) d = d->do_attacheddev; return (d); } static irp * IoBuildSynchronousFsdRequest(func, dobj, buf, len, off, event, status) uint32_t func; device_object *dobj; void *buf; uint32_t len; uint64_t *off; nt_kevent *event; io_status_block *status; { irp *ip; ip = IoBuildAsynchronousFsdRequest(func, dobj, buf, len, off, status); if (ip == NULL) return(NULL); ip->irp_usrevent = event; return(ip); } static irp * IoBuildAsynchronousFsdRequest(func, dobj, buf, len, off, status) uint32_t func; device_object *dobj; void *buf; uint32_t len; uint64_t *off; io_status_block *status; { irp *ip; io_stack_location *sl; ip = IoAllocateIrp(dobj->do_stacksize, TRUE); if (ip == NULL) return(NULL); ip->irp_usriostat = status; ip->irp_tail.irp_overlay.irp_thread = NULL; sl = IoGetNextIrpStackLocation(ip); sl->isl_major = func; sl->isl_minor = 0; sl->isl_flags = 0; sl->isl_ctl = 0; sl->isl_devobj = dobj; sl->isl_fileobj = NULL; sl->isl_completionfunc = NULL; ip->irp_userbuf = buf; if (dobj->do_flags & DO_BUFFERED_IO) { ip->irp_assoc.irp_sysbuf = ExAllocatePoolWithTag(NonPagedPool, len, 0); if (ip->irp_assoc.irp_sysbuf == NULL) { IoFreeIrp(ip); return(NULL); } bcopy(buf, ip->irp_assoc.irp_sysbuf, len); } if (dobj->do_flags & DO_DIRECT_IO) { ip->irp_mdl = IoAllocateMdl(buf, len, FALSE, FALSE, ip); if (ip->irp_mdl == NULL) { if (ip->irp_assoc.irp_sysbuf != NULL) ExFreePool(ip->irp_assoc.irp_sysbuf); IoFreeIrp(ip); return(NULL); } ip->irp_userbuf = NULL; ip->irp_assoc.irp_sysbuf = NULL; } if (func == IRP_MJ_READ) { sl->isl_parameters.isl_read.isl_len = len; if (off != NULL) sl->isl_parameters.isl_read.isl_byteoff = *off; else sl->isl_parameters.isl_read.isl_byteoff = 0; } if (func == IRP_MJ_WRITE) { sl->isl_parameters.isl_write.isl_len = len; if (off != NULL) sl->isl_parameters.isl_write.isl_byteoff = *off; else sl->isl_parameters.isl_write.isl_byteoff = 0; } return(ip); } static irp * IoBuildDeviceIoControlRequest(iocode, dobj, ibuf, ilen, obuf, olen, isinternal, event, status) uint32_t iocode; device_object *dobj; void *ibuf; uint32_t ilen; void *obuf; uint32_t olen; uint8_t isinternal; nt_kevent *event; io_status_block *status; { irp *ip; io_stack_location *sl; uint32_t buflen; ip = IoAllocateIrp(dobj->do_stacksize, TRUE); if (ip == NULL) return(NULL); ip->irp_usrevent = event; ip->irp_usriostat = status; ip->irp_tail.irp_overlay.irp_thread = NULL; sl = IoGetNextIrpStackLocation(ip); sl->isl_major = isinternal == TRUE ? IRP_MJ_INTERNAL_DEVICE_CONTROL : IRP_MJ_DEVICE_CONTROL; sl->isl_minor = 0; sl->isl_flags = 0; sl->isl_ctl = 0; sl->isl_devobj = dobj; sl->isl_fileobj = NULL; sl->isl_completionfunc = NULL; sl->isl_parameters.isl_ioctl.isl_iocode = iocode; sl->isl_parameters.isl_ioctl.isl_ibuflen = ilen; sl->isl_parameters.isl_ioctl.isl_obuflen = olen; switch(IO_METHOD(iocode)) { case METHOD_BUFFERED: if (ilen > olen) buflen = ilen; else buflen = olen; if (buflen) { ip->irp_assoc.irp_sysbuf = ExAllocatePoolWithTag(NonPagedPool, buflen, 0); if (ip->irp_assoc.irp_sysbuf == NULL) { IoFreeIrp(ip); return(NULL); } } if (ilen && ibuf != NULL) { bcopy(ibuf, ip->irp_assoc.irp_sysbuf, ilen); bzero((char *)ip->irp_assoc.irp_sysbuf + ilen, buflen - ilen); } else bzero(ip->irp_assoc.irp_sysbuf, ilen); ip->irp_userbuf = obuf; break; case METHOD_IN_DIRECT: case METHOD_OUT_DIRECT: if (ilen && ibuf != NULL) { ip->irp_assoc.irp_sysbuf = ExAllocatePoolWithTag(NonPagedPool, ilen, 0); if (ip->irp_assoc.irp_sysbuf == NULL) { IoFreeIrp(ip); return(NULL); } bcopy(ibuf, ip->irp_assoc.irp_sysbuf, ilen); } if (olen && obuf != NULL) { ip->irp_mdl = IoAllocateMdl(obuf, olen, FALSE, FALSE, ip); /* * Normally we would MmProbeAndLockPages() * here, but we don't have to in our * imlementation. */ } break; case METHOD_NEITHER: ip->irp_userbuf = obuf; sl->isl_parameters.isl_ioctl.isl_type3ibuf = ibuf; break; default: break; } /* * Ideally, we should associate this IRP with the calling * thread here. */ return (ip); } static irp * IoAllocateIrp(stsize, chargequota) uint8_t stsize; uint8_t chargequota; { irp *i; i = ExAllocatePoolWithTag(NonPagedPool, IoSizeOfIrp(stsize), 0); if (i == NULL) return (NULL); IoInitializeIrp(i, IoSizeOfIrp(stsize), stsize); return (i); } static irp * IoMakeAssociatedIrp(ip, stsize) irp *ip; uint8_t stsize; { irp *associrp; associrp = IoAllocateIrp(stsize, FALSE); if (associrp == NULL) return(NULL); mtx_lock(&ntoskrnl_dispatchlock); associrp->irp_flags |= IRP_ASSOCIATED_IRP; associrp->irp_tail.irp_overlay.irp_thread = ip->irp_tail.irp_overlay.irp_thread; associrp->irp_assoc.irp_master = ip; mtx_unlock(&ntoskrnl_dispatchlock); return(associrp); } static void IoFreeIrp(ip) irp *ip; { ExFreePool(ip); return; } static void IoInitializeIrp(io, psize, ssize) irp *io; uint16_t psize; uint8_t ssize; { bzero((char *)io, IoSizeOfIrp(ssize)); io->irp_size = psize; io->irp_stackcnt = ssize; io->irp_currentstackloc = ssize; - INIT_LIST_HEAD(&io->irp_thlist); + InitializeListHead(&io->irp_thlist); io->irp_tail.irp_overlay.irp_csl = (io_stack_location *)(io + 1) + ssize; return; } static void IoReuseIrp(ip, status) irp *ip; uint32_t status; { uint8_t allocflags; allocflags = ip->irp_allocflags; IoInitializeIrp(ip, ip->irp_size, ip->irp_stackcnt); ip->irp_iostat.isb_status = status; ip->irp_allocflags = allocflags; return; } void IoAcquireCancelSpinLock(irql) uint8_t *irql; { KeAcquireSpinLock(&ntoskrnl_cancellock, irql); return; } void IoReleaseCancelSpinLock(irql) uint8_t irql; { KeReleaseSpinLock(&ntoskrnl_cancellock, irql); return; } uint8_t IoCancelIrp(irp *ip) { cancel_func cfunc; IoAcquireCancelSpinLock(&ip->irp_cancelirql); cfunc = IoSetCancelRoutine(ip, NULL); ip->irp_cancel = TRUE; if (ip->irp_cancelfunc == NULL) { IoReleaseCancelSpinLock(ip->irp_cancelirql); return(FALSE); } MSCALL2(cfunc, IoGetCurrentIrpStackLocation(ip)->isl_devobj, ip); return(TRUE); } uint32_t IofCallDriver(dobj, ip) device_object *dobj; irp *ip; { driver_object *drvobj; io_stack_location *sl; uint32_t status; driver_dispatch disp; drvobj = dobj->do_drvobj; if (ip->irp_currentstackloc <= 0) panic("IoCallDriver(): out of stack locations"); IoSetNextIrpStackLocation(ip); sl = IoGetCurrentIrpStackLocation(ip); sl->isl_devobj = dobj; disp = drvobj->dro_dispatch[sl->isl_major]; status = MSCALL2(disp, dobj, ip); return(status); } void IofCompleteRequest(ip, prioboost) irp *ip; uint8_t prioboost; { uint32_t i; uint32_t status; device_object *dobj; io_stack_location *sl; completion_func cf; ip->irp_pendingreturned = IoGetCurrentIrpStackLocation(ip)->isl_ctl & SL_PENDING_RETURNED; sl = (io_stack_location *)(ip + 1); for (i = ip->irp_currentstackloc; i < (uint32_t)ip->irp_stackcnt; i++) { if (ip->irp_currentstackloc < ip->irp_stackcnt - 1) { IoSkipCurrentIrpStackLocation(ip); dobj = IoGetCurrentIrpStackLocation(ip)->isl_devobj; } else dobj = NULL; if (sl[i].isl_completionfunc != NULL && ((ip->irp_iostat.isb_status == STATUS_SUCCESS && sl->isl_ctl & SL_INVOKE_ON_SUCCESS) || (ip->irp_iostat.isb_status != STATUS_SUCCESS && sl->isl_ctl & SL_INVOKE_ON_ERROR) || (ip->irp_cancel == TRUE && sl->isl_ctl & SL_INVOKE_ON_CANCEL))) { cf = sl->isl_completionfunc; status = MSCALL3(cf, dobj, ip, sl->isl_completionctx); if (status == STATUS_MORE_PROCESSING_REQUIRED) return; } if (IoGetCurrentIrpStackLocation(ip)->isl_ctl & SL_PENDING_RETURNED) ip->irp_pendingreturned = TRUE; } /* Handle any associated IRPs. */ if (ip->irp_flags & IRP_ASSOCIATED_IRP) { uint32_t masterirpcnt; irp *masterirp; mdl *m; masterirp = ip->irp_assoc.irp_master; masterirpcnt = InterlockedDecrement(&masterirp->irp_assoc.irp_irpcnt); while ((m = ip->irp_mdl) != NULL) { ip->irp_mdl = m->mdl_next; IoFreeMdl(m); } IoFreeIrp(ip); if (masterirpcnt == 0) IoCompleteRequest(masterirp, IO_NO_INCREMENT); return; } /* With any luck, these conditions will never arise. */ if (ip->irp_flags & (IRP_PAGING_IO|IRP_CLOSE_OPERATION)) { if (ip->irp_usriostat != NULL) *ip->irp_usriostat = ip->irp_iostat; if (ip->irp_usrevent != NULL) KeSetEvent(ip->irp_usrevent, prioboost, FALSE); if (ip->irp_flags & IRP_PAGING_IO) { if (ip->irp_mdl != NULL) IoFreeMdl(ip->irp_mdl); IoFreeIrp(ip); } } return; } +void +ntoskrnl_intr(arg) + void *arg; +{ + kinterrupt *iobj; + uint8_t irql; + list_entry *l; + + KeAcquireSpinLock(&ntoskrnl_intlock, &irql); + l = ntoskrnl_intlist.nle_flink; + while (l != &ntoskrnl_intlist) { + iobj = CONTAINING_RECORD(l, kinterrupt, ki_list); + MSCALL1(iobj->ki_svcfunc, iobj->ki_svcctx); + l = l->nle_flink; + } + KeReleaseSpinLock(&ntoskrnl_intlock, irql); + + return; +} + +uint8_t +KeAcquireInterruptSpinLock(iobj) + kinterrupt *iobj; +{ + uint8_t irql; + KeAcquireSpinLock(&ntoskrnl_intlock, &irql); + return(irql); +} + +void +KeReleaseInterruptSpinLock(iobj, irql) + kinterrupt *iobj; + uint8_t irql; +{ + KeReleaseSpinLock(&ntoskrnl_intlock, irql); + return; +} + +uint8_t +KeSynchronizeExecution(iobj, syncfunc, syncctx) + kinterrupt *iobj; + void *syncfunc; + void *syncctx; +{ + uint8_t irql; + + KeAcquireSpinLock(&ntoskrnl_intlock, &irql); + MSCALL1(syncfunc, syncctx); + KeReleaseSpinLock(&ntoskrnl_intlock, irql); + + return(TRUE); +} + +/* + * IoConnectInterrupt() is passed only the interrupt vector and + * irql that a device wants to use, but no device-specific tag + * of any kind. This conflicts rather badly with FreeBSD's + * bus_setup_intr(), which needs the device_t for the device + * requesting interrupt delivery. In order to bypass this + * inconsistency, we implement a second level of interrupt + * dispatching on top of bus_setup_intr(). All devices use + * ntoskrnl_intr() as their ISR, and any device requesting + * interrupts will be registered with ntoskrnl_intr()'s interrupt + * dispatch list. When an interrupt arrives, we walk the list + * and invoke all the registered ISRs. This effectively makes all + * interrupts shared, but it's the only way to duplicate the + * semantics of IoConnectInterrupt() and IoDisconnectInterrupt() properly. + */ + +uint32_t +IoConnectInterrupt(iobj, svcfunc, svcctx, lock, vector, irql, + syncirql, imode, shared, affinity, savefloat) + kinterrupt **iobj; + void *svcfunc; + void *svcctx; + uint32_t vector; + kspin_lock *lock; + uint8_t irql; + uint8_t syncirql; + uint8_t imode; + uint8_t shared; + uint32_t affinity; + uint8_t savefloat; +{ + uint8_t curirql; + + *iobj = ExAllocatePoolWithTag(NonPagedPool, sizeof(kinterrupt), 0); + if (*iobj == NULL) + return(STATUS_INSUFFICIENT_RESOURCES); + + (*iobj)->ki_svcfunc = svcfunc; + (*iobj)->ki_svcctx = svcctx; + + if (lock == NULL) { + KeInitializeSpinLock(&(*iobj)->ki_lock_priv); + (*iobj)->ki_lock = &(*iobj)->ki_lock_priv; + } else + (*iobj)->ki_lock = lock; + + KeAcquireSpinLock(&ntoskrnl_intlock, &curirql); + InsertHeadList((&ntoskrnl_intlist), (&(*iobj)->ki_list)); + KeReleaseSpinLock(&ntoskrnl_intlock, curirql); + + return(STATUS_SUCCESS); +} + +void +IoDisconnectInterrupt(iobj) + kinterrupt *iobj; +{ + uint8_t irql; + + if (iobj == NULL) + return; + + KeAcquireSpinLock(&ntoskrnl_intlock, &irql); + RemoveEntryList((&iobj->ki_list)); + KeReleaseSpinLock(&ntoskrnl_intlock, irql); + + ExFreePool(iobj); + + return; +} + device_object * IoAttachDeviceToDeviceStack(src, dst) device_object *src; device_object *dst; { device_object *attached; mtx_lock(&ntoskrnl_dispatchlock); attached = IoGetAttachedDevice(dst); attached->do_attacheddev = src; src->do_attacheddev = NULL; src->do_stacksize = attached->do_stacksize + 1; mtx_unlock(&ntoskrnl_dispatchlock); return(attached); } void IoDetachDevice(topdev) device_object *topdev; { device_object *tail; mtx_lock(&ntoskrnl_dispatchlock); /* First, break the chain. */ tail = topdev->do_attacheddev; if (tail == NULL) { mtx_unlock(&ntoskrnl_dispatchlock); return; } topdev->do_attacheddev = tail->do_attacheddev; topdev->do_refcnt--; - /* Now reduce the stacksize count for the tail objects. */ + /* Now reduce the stacksize count for the takm_il objects. */ tail = topdev->do_attacheddev; while (tail != NULL) { tail->do_stacksize--; tail = tail->do_attacheddev; } mtx_unlock(&ntoskrnl_dispatchlock); return; } -/* Always called with dispatcher lock held. */ -static void -ntoskrnl_wakeup(arg) - void *arg; +/* + * For the most part, an object is considered signalled if + * dh_sigstate == TRUE. The exception is for mutant objects + * (mutexes), where the logic works like this: + * + * - If the thread already owns the object and sigstate is + * less than or equal to 0, then the object is considered + * signalled (recursive acquisition). + * - If dh_sigstate == 1, the object is also considered + * signalled. + */ + +static int +ntoskrnl_is_signalled(obj, td) + nt_dispatch_header *obj; + struct thread *td; { + kmutant *km; + + if (obj->dh_type == DISP_TYPE_MUTANT) { + km = (kmutant *)obj; + if ((obj->dh_sigstate <= 0 && km->km_ownerthread == td) || + obj->dh_sigstate == 1) + return(TRUE); + return(FALSE); + } + + if (obj->dh_sigstate > 0) + return(TRUE); + return(FALSE); +} + +static void +ntoskrnl_satisfy_wait(obj, td) nt_dispatch_header *obj; - wait_block *w; - list_entry *e; struct thread *td; +{ + kmutant *km; - obj = arg; + switch (obj->dh_type) { + case DISP_TYPE_MUTANT: + km = (struct kmutant *)obj; + obj->dh_sigstate--; + /* + * If sigstate reaches 0, the mutex is now + * non-signalled (the new thread owns it). + */ + if (obj->dh_sigstate == 0) { + km->km_ownerthread = td; + if (km->km_abandoned == TRUE) + km->km_abandoned = FALSE; + } + break; + /* Synchronization objects get reset to unsignalled. */ + case DISP_TYPE_SYNCHRONIZATION_EVENT: + case DISP_TYPE_SYNCHRONIZATION_TIMER: + obj->dh_sigstate = 0; + break; + case DISP_TYPE_SEMAPHORE: + obj->dh_sigstate--; + break; + default: + break; + } - e = obj->dh_waitlisthead.nle_flink; + return; +} - obj->dh_sigstate = TRUE; +static void +ntoskrnl_satisfy_multiple_waits(wb) + wait_block *wb; +{ + wait_block *cur; + struct thread *td; + cur = wb; + td = wb->wb_kthread; + + do { + ntoskrnl_satisfy_wait(wb->wb_object, td); + cur->wb_awakened = TRUE; + cur = cur->wb_next; + } while (cur != wb); + + return; +} + +/* Always called with dispatcher lock held. */ +static void +ntoskrnl_waittest(obj, increment) + nt_dispatch_header *obj; + uint32_t increment; +{ + wait_block *w, *next; + list_entry *e; + struct thread *td; + wb_ext *we; + int satisfied; + /* - * What happens if someone tells us to wake up - * threads waiting on an object, but nobody's - * waiting on it at the moment? For sync events, - * the signal state is supposed to be automatically - * reset, but this only happens in the KeWaitXXX() - * functions. If nobody is waiting, the state never - * gets cleared. + * Once an object has been signalled, we walk its list of + * wait blocks. If a wait block can be awakened, then satisfy + * waits as necessary and wake the thread. + * + * The rules work like this: + * + * If a wait block is marked as WAITTYPE_ANY, then + * we can satisfy the wait conditions on the current + * object and wake the thread right away. Satisfying + * the wait also has the effect of breaking us out + * of the search loop. + * + * If the object is marked as WAITTYLE_ALL, then the + * wait block will be part of a circularly linked + * list of wait blocks belonging to a waiting thread + * that's sleeping in KeWaitForMultipleObjects(). In + * order to wake the thread, all the objects in the + * wait list must be in the signalled state. If they + * are, we then satisfy all of them and wake the + * thread. + * */ - if (e == &obj->dh_waitlisthead) { - if (obj->dh_type == EVENT_TYPE_SYNC) - obj->dh_sigstate = FALSE; - return; - } + e = obj->dh_waitlisthead.nle_flink; - while (e != &obj->dh_waitlisthead) { - w = (wait_block *)e; - td = w->wb_kthread; - ndis_thresume(td->td_proc); - /* - * For synchronization objects, only wake up - * the first waiter. - */ - if (obj->dh_type == EVENT_TYPE_SYNC) - break; + while (e != &obj->dh_waitlisthead && obj->dh_sigstate > 0) { + w = CONTAINING_RECORD(e, wait_block, wb_waitlist); + we = w->wb_ext; + td = we->we_td; + satisfied = FALSE; + if (w->wb_waittype == WAITTYPE_ANY) { + /* + * Thread can be awakened if + * any wait is satisfied. + */ + ntoskrnl_satisfy_wait(obj, td); + satisfied = TRUE; + w->wb_awakened = TRUE; + } else { + /* + * Thread can only be woken up + * if all waits are satisfied. + * If the thread is waiting on multiple + * objects, they should all be linked + * through the wb_next pointers in the + * wait blocks. + */ + satisfied = TRUE; + next = w->wb_next; + while (next != w) { + if (ntoskrnl_is_signalled(obj, td) == FALSE) { + satisfied = FALSE; + break; + } + next = next->wb_next; + } + ntoskrnl_satisfy_multiple_waits(w); + } + + if (satisfied == TRUE) + cv_broadcastpri(&we->we_cv, w->wb_oldpri - + (increment * 4)); + e = e->nle_flink; } return; } static void ntoskrnl_time(tval) uint64_t *tval; { struct timespec ts; nanotime(&ts); *tval = (uint64_t)ts.tv_nsec / 100 + (uint64_t)ts.tv_sec * 10000000 + 11644473600; return; } /* * KeWaitForSingleObject() is a tricky beast, because it can be used * with several different object types: semaphores, timers, events, * mutexes and threads. Semaphores don't appear very often, but the * other object types are quite common. KeWaitForSingleObject() is * what's normally used to acquire a mutex, and it can be used to * wait for a thread termination. * * The Windows NDIS API is implemented in terms of Windows kernel * primitives, and some of the object manipulation is duplicated in * NDIS. For example, NDIS has timers and events, which are actually * Windows kevents and ktimers. Now, you're supposed to only use the * NDIS variants of these objects within the confines of the NDIS API, * but there are some naughty developers out there who will use * KeWaitForSingleObject() on NDIS timer and event objects, so we * have to support that as well. Conseqently, our NDIS timer and event * code has to be closely tied into our ntoskrnl timer and event code, * just as it is in Windows. * * KeWaitForSingleObject() may do different things for different kinds * of objects: * * - For events, we check if the event has been signalled. If the * event is already in the signalled state, we just return immediately, * otherwise we wait for it to be set to the signalled state by someone * else calling KeSetEvent(). Events can be either synchronization or * notification events. * * - For timers, if the timer has already fired and the timer is in * the signalled state, we just return, otherwise we wait on the * timer. Unlike an event, timers get signalled automatically when * they expire rather than someone having to trip them manually. * Timers initialized with KeInitializeTimer() are always notification * events: KeInitializeTimerEx() lets you initialize a timer as * either a notification or synchronization event. * * - For mutexes, we try to acquire the mutex and if we can't, we wait * on the mutex until it's available and then grab it. When a mutex is - * released, it enters the signaled state, which wakes up one of the + * released, it enters the signalled state, which wakes up one of the * threads waiting to acquire it. Mutexes are always synchronization * events. * * - For threads, the only thing we do is wait until the thread object * enters a signalled state, which occurs when the thread terminates. * Threads are always notification events. * * A notification event wakes up all threads waiting on an object. A * synchronization event wakes up just one. Also, a synchronization event * is auto-clearing, which means we automatically set the event back to * the non-signalled state once the wakeup is done. */ uint32_t -KeWaitForSingleObject(obj, reason, mode, alertable, duetime) - nt_dispatch_header *obj; +KeWaitForSingleObject(arg, reason, mode, alertable, duetime) + void *arg; uint32_t reason; uint32_t mode; uint8_t alertable; int64_t *duetime; { - struct thread *td = curthread; - kmutant *km; wait_block w; + struct thread *td = curthread; struct timeval tv; int error = 0; uint64_t curtime; + wb_ext we; + nt_dispatch_header *obj; + obj = arg; + if (obj == NULL) return(STATUS_INVALID_PARAMETER); mtx_lock(&ntoskrnl_dispatchlock); + cv_init(&we.we_cv, "KeWFS"); + we.we_td = td; + /* - * See if the object is a mutex. If so, and we already own - * it, then just increment the acquisition count and return. - * - * For any other kind of object, see if it's already in the - * signalled state, and if it is, just return. If the object - * is marked as a synchronization event, reset the state to - * unsignalled. + * Check to see if this object is already signalled, + * and just return without waiting if it is. */ - - if (obj->dh_size == OTYPE_MUTEX) { - km = (kmutant *)obj; - if (km->km_ownerthread == NULL || - km->km_ownerthread == curthread->td_proc) { - obj->dh_sigstate = FALSE; - km->km_acquirecnt++; - km->km_ownerthread = curthread->td_proc; + if (ntoskrnl_is_signalled(obj, td) == TRUE) { + /* Sanity check the signal state value. */ + if (obj->dh_sigstate != INT32_MIN) { + ntoskrnl_satisfy_wait(obj, curthread); mtx_unlock(&ntoskrnl_dispatchlock); return (STATUS_SUCCESS); + } else { + /* + * There's a limit to how many times we can + * recursively acquire a mutant. If we hit + * the limit, something is very wrong. + */ + if (obj->dh_type == DISP_TYPE_MUTANT) { + mtx_unlock(&ntoskrnl_dispatchlock); + panic("mutant limit exceeded"); + } } - } else if (obj->dh_sigstate == TRUE) { - if (obj->dh_type == EVENT_TYPE_SYNC) - obj->dh_sigstate = FALSE; - mtx_unlock(&ntoskrnl_dispatchlock); - return (STATUS_SUCCESS); } + bzero((char *)&w, sizeof(wait_block)); w.wb_object = obj; - w.wb_kthread = td; + w.wb_ext = &we; + w.wb_waittype = WAITTYPE_ANY; + w.wb_next = &w; + w.wb_waitkey = 0; + w.wb_awakened = FALSE; + w.wb_oldpri = td->td_priority; - INSERT_LIST_TAIL((&obj->dh_waitlisthead), (&w.wb_waitlist)); + InsertTailList((&obj->dh_waitlisthead), (&w.wb_waitlist)); /* * The timeout value is specified in 100 nanosecond units * and can be a positive or negative number. If it's positive, * then the duetime is absolute, and we need to convert it * to an absolute offset relative to now in order to use it. * If it's negative, then the duetime is relative and we * just have to convert the units. */ if (duetime != NULL) { if (*duetime < 0) { tv.tv_sec = - (*duetime) / 10000000; tv.tv_usec = (- (*duetime) / 10) - (tv.tv_sec * 1000000); } else { ntoskrnl_time(&curtime); if (*duetime < curtime) tv.tv_sec = tv.tv_usec = 0; else { tv.tv_sec = ((*duetime) - curtime) / 10000000; tv.tv_usec = ((*duetime) - curtime) / 10 - (tv.tv_sec * 1000000); } } } - error = ndis_thsuspend(td->td_proc, &ntoskrnl_dispatchlock, - duetime == NULL ? 0 : tvtohz(&tv)); + if (duetime == NULL) + cv_wait(&we.we_cv, &ntoskrnl_dispatchlock); + else + error = cv_timedwait(&we.we_cv, + &ntoskrnl_dispatchlock, tvtohz(&tv)); + RemoveEntryList(&w.wb_waitlist); + + cv_destroy(&we.we_cv); + /* We timed out. Leave the object alone and return status. */ if (error == EWOULDBLOCK) { - REMOVE_LIST_ENTRY((&w.wb_waitlist)); - INIT_LIST_HEAD((&w.wb_waitlist)); mtx_unlock(&ntoskrnl_dispatchlock); return(STATUS_TIMEOUT); } - /* - * Mutexes are always synchronization objects, which means - * if several threads are waiting to acquire it, only one will - * be woken up. If that one is us, and the mutex is up for grabs, - * grab it. - */ - - if (obj->dh_size == OTYPE_MUTEX) { - km = (kmutant *)obj; - if (km->km_ownerthread == NULL) { - km->km_ownerthread = curthread->td_proc; - km->km_acquirecnt++; - } - } - - if (obj->dh_type == EVENT_TYPE_SYNC) - obj->dh_sigstate = FALSE; - REMOVE_LIST_ENTRY((&w.wb_waitlist)); - INIT_LIST_HEAD((&w.wb_waitlist)); - mtx_unlock(&ntoskrnl_dispatchlock); return(STATUS_SUCCESS); +/* + return(KeWaitForMultipleObjects(1, &obj, WAITTYPE_ALL, reason, + mode, alertable, duetime, &w)); +*/ } static uint32_t KeWaitForMultipleObjects(cnt, obj, wtype, reason, mode, alertable, duetime, wb_array) uint32_t cnt; nt_dispatch_header *obj[]; uint32_t wtype; uint32_t reason; uint32_t mode; uint8_t alertable; int64_t *duetime; wait_block *wb_array; { struct thread *td = curthread; - kmutant *km; - wait_block _wb_array[THREAD_WAIT_OBJECTS]; - wait_block *w; + wait_block *whead, *w; + wait_block _wb_array[MAX_WAIT_OBJECTS]; + nt_dispatch_header *cur; struct timeval tv; - int i, wcnt = 0, widx = 0, error = 0; + int i, wcnt = 0, error = 0; uint64_t curtime; struct timespec t1, t2; + uint32_t status = STATUS_SUCCESS; + wb_ext we; if (cnt > MAX_WAIT_OBJECTS) return(STATUS_INVALID_PARAMETER); if (cnt > THREAD_WAIT_OBJECTS && wb_array == NULL) return(STATUS_INVALID_PARAMETER); mtx_lock(&ntoskrnl_dispatchlock); + cv_init(&we.we_cv, "KeWFM"); + we.we_td = td; + if (wb_array == NULL) - w = &_wb_array[0]; + whead = _wb_array; else - w = wb_array; + whead = wb_array; + bzero((char *)whead, sizeof(wait_block) * cnt); + /* First pass: see if we can satisfy any waits immediately. */ + wcnt = 0; + w = whead; + for (i = 0; i < cnt; i++) { - if (obj[i]->dh_size == OTYPE_MUTEX) { - km = (kmutant *)obj[i]; - if (km->km_ownerthread == NULL || - km->km_ownerthread == curthread->td_proc) { - obj[i]->dh_sigstate = FALSE; - km->km_acquirecnt++; - km->km_ownerthread = curthread->td_proc; - if (wtype == WAITTYPE_ANY) { - mtx_unlock(&ntoskrnl_dispatchlock); - return (STATUS_WAIT_0 + i); - } + InsertTailList((&obj[i]->dh_waitlisthead), + (&w->wb_waitlist)); + w->wb_ext = &we; + w->wb_object = obj[i]; + w->wb_waittype = wtype; + w->wb_waitkey = i; + w->wb_awakened = FALSE; + w->wb_oldpri = td->td_priority; + w->wb_next = w + 1; + w++; + wcnt++; + if (ntoskrnl_is_signalled(obj[i], td)) { + /* + * There's a limit to how many times + * we can recursively acquire a mutant. + * If we hit the limit, something + * is very wrong. + */ + if (obj[i]->dh_sigstate == INT32_MIN && + obj[i]->dh_type == DISP_TYPE_MUTANT) { + mtx_unlock(&ntoskrnl_dispatchlock); + panic("mutant limit exceeded"); } - } else if (obj[i]->dh_sigstate == TRUE) { - if (obj[i]->dh_type == EVENT_TYPE_SYNC) - obj[i]->dh_sigstate = FALSE; + + /* + * If this is a WAITTYPE_ANY wait, then + * satisfy the waited object and exit + * right now. + */ + if (wtype == WAITTYPE_ANY) { - mtx_unlock(&ntoskrnl_dispatchlock); - return (STATUS_WAIT_0 + i); + ntoskrnl_satisfy_wait(obj[i], td); + status = STATUS_WAIT_0 + i; + goto wait_done; + } else { + w--; + wcnt--; + w->wb_object = NULL; + RemoveEntryList(&w->wb_waitlist); } } } /* - * Second pass: set up wait for anything we can't - * satisfy immediately. + * If this is a WAITTYPE_ALL wait and all objects are + * already signalled, satisfy the waits and exit now. */ - for (i = 0; i < cnt; i++) { - if (obj[i]->dh_sigstate == TRUE) - continue; - INSERT_LIST_TAIL((&obj[i]->dh_waitlisthead), - (&w[i].wb_waitlist)); - w[i].wb_kthread = td; - w[i].wb_object = obj[i]; - wcnt++; + if (wtype == WAITTYPE_ALL && wcnt == 0) { + for (i = 0; i < cnt; i++) + ntoskrnl_satisfy_wait(obj[i], td); + status = STATUS_SUCCESS; + goto wait_done; } + /* + * Create a circular waitblock list. The waitcount + * must always be non-zero when we get here. + */ + + (w - 1)->wb_next = whead; + + /* Wait on any objects that aren't yet signalled. */ + + /* Calculate timeout, if any. */ + if (duetime != NULL) { if (*duetime < 0) { tv.tv_sec = - (*duetime) / 10000000; tv.tv_usec = (- (*duetime) / 10) - (tv.tv_sec * 1000000); } else { ntoskrnl_time(&curtime); if (*duetime < curtime) tv.tv_sec = tv.tv_usec = 0; else { tv.tv_sec = ((*duetime) - curtime) / 10000000; tv.tv_usec = ((*duetime) - curtime) / 10 - (tv.tv_sec * 1000000); } } } while (wcnt) { nanotime(&t1); - error = ndis_thsuspend(td->td_proc, &ntoskrnl_dispatchlock, - duetime == NULL ? 0 : tvtohz(&tv)); + if (duetime == NULL) + cv_wait(&we.we_cv, &ntoskrnl_dispatchlock); + else + error = cv_timedwait(&we.we_cv, + &ntoskrnl_dispatchlock, tvtohz(&tv)); + /* Wait with timeout expired. */ + + if (error) { + status = STATUS_TIMEOUT; + goto wait_done; + } + nanotime(&t2); - for (i = 0; i < cnt; i++) { - if (obj[i]->dh_size == OTYPE_MUTEX) { - km = (kmutant *)obj; - if (km->km_ownerthread == NULL) { - km->km_ownerthread = - curthread->td_proc; - km->km_acquirecnt++; + /* See what's been signalled. */ + + w = whead; + do { + cur = w->wb_object; + if (ntoskrnl_is_signalled(cur, td) == TRUE || + w->wb_awakened == TRUE) { + /* Sanity check the signal state value. */ + if (cur->dh_sigstate == INT32_MIN && + cur->dh_type == DISP_TYPE_MUTANT) { + mtx_unlock(&ntoskrnl_dispatchlock); + panic("mutant limit exceeded"); } - } - if (obj[i]->dh_sigstate == TRUE) { - widx = i; - if (obj[i]->dh_type == EVENT_TYPE_SYNC) - obj[i]->dh_sigstate = FALSE; - REMOVE_LIST_ENTRY((&w[i].wb_waitlist)); - INIT_LIST_HEAD((&w[i].wb_waitlist)); wcnt--; + if (wtype == WAITTYPE_ANY) { + status = w->wb_waitkey & + STATUS_WAIT_0; + goto wait_done; + } } + w = w->wb_next; + } while (w != whead); + + /* + * If all objects have been signalled, or if this + * is a WAITTYPE_ANY wait and we were woke up by + * someone, we can bail. + */ + + if (wcnt == 0) { + status = STATUS_SUCCESS; + goto wait_done; } - if (error || wtype == WAITTYPE_ANY) - break; + /* + * If this is WAITTYPE_ALL wait, and there's still + * objects that haven't been signalled, deduct the + * time that's elapsed so far from the timeout and + * wait again (or continue waiting indefinitely if + * there's no timeout). + */ if (duetime != NULL) { tv.tv_sec -= (t2.tv_sec - t1.tv_sec); tv.tv_usec -= (t2.tv_nsec - t1.tv_nsec) / 1000; } } - if (wcnt) { - for (i = 0; i < cnt; i++) { - REMOVE_LIST_ENTRY((&w[i].wb_waitlist)); - INIT_LIST_HEAD((&w[i].wb_waitlist)); - } - } - if (error == EWOULDBLOCK) { - mtx_unlock(&ntoskrnl_dispatchlock); - return(STATUS_TIMEOUT); - } +wait_done: - if (wtype == WAITTYPE_ANY && wcnt) { - mtx_unlock(&ntoskrnl_dispatchlock); - return(STATUS_WAIT_0 + widx); - } + cv_destroy(&we.we_cv); + for (i = 0; i < cnt; i++) { + if (whead[i].wb_object != NULL) + RemoveEntryList(&whead[i].wb_waitlist); + + } mtx_unlock(&ntoskrnl_dispatchlock); - return(STATUS_SUCCESS); + return(status); } static void WRITE_REGISTER_USHORT(reg, val) uint16_t *reg; uint16_t val; { bus_space_write_2(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg, val); return; } static uint16_t READ_REGISTER_USHORT(reg) uint16_t *reg; { return(bus_space_read_2(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg)); } static void WRITE_REGISTER_ULONG(reg, val) uint32_t *reg; uint32_t val; { bus_space_write_4(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg, val); return; } static uint32_t READ_REGISTER_ULONG(reg) uint32_t *reg; { return(bus_space_read_4(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg)); } static uint8_t READ_REGISTER_UCHAR(reg) uint8_t *reg; { return(bus_space_read_1(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg)); } static void WRITE_REGISTER_UCHAR(reg, val) uint8_t *reg; uint8_t val; { bus_space_write_1(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg, val); return; } static int64_t _allmul(a, b) int64_t a; int64_t b; { return (a * b); } static int64_t _alldiv(a, b) int64_t a; int64_t b; { return (a / b); } static int64_t _allrem(a, b) int64_t a; int64_t b; { return (a % b); } static uint64_t _aullmul(a, b) uint64_t a; uint64_t b; { return (a * b); } static uint64_t _aulldiv(a, b) uint64_t a; uint64_t b; { return (a / b); } static uint64_t _aullrem(a, b) uint64_t a; uint64_t b; { return (a % b); } static int64_t _allshl(a, b) int64_t a; uint8_t b; { return (a << b); } static uint64_t _aullshl(a, b) uint64_t a; uint8_t b; { return (a << b); } static int64_t _allshr(a, b) int64_t a; uint8_t b; { return (a >> b); } static uint64_t _aullshr(a, b) uint64_t a; uint8_t b; { return (a >> b); } static slist_entry * ntoskrnl_pushsl(head, entry) slist_header *head; slist_entry *entry; { slist_entry *oldhead; oldhead = head->slh_list.slh_next; entry->sl_next = head->slh_list.slh_next; head->slh_list.slh_next = entry; head->slh_list.slh_depth++; head->slh_list.slh_seq++; return(oldhead); } static slist_entry * ntoskrnl_popsl(head) slist_header *head; { slist_entry *first; first = head->slh_list.slh_next; if (first != NULL) { head->slh_list.slh_next = first->sl_next; head->slh_list.slh_depth--; head->slh_list.slh_seq++; } return(first); } /* * We need this to make lookaside lists work for amd64. * We pass a pointer to ExAllocatePoolWithTag() the lookaside * list structure. For amd64 to work right, this has to be a * pointer to the wrapped version of the routine, not the * original. Letting the Windows driver invoke the original * function directly will result in a convention calling * mismatch and a pretty crash. On x86, this effectively * becomes a no-op since ipt_func and ipt_wrap are the same. */ static funcptr ntoskrnl_findwrap(func) funcptr func; { image_patch_table *patch; patch = ntoskrnl_functbl; while (patch->ipt_func != NULL) { if ((funcptr)patch->ipt_func == func) return((funcptr)patch->ipt_wrap); patch++; } return(NULL); } static void ExInitializePagedLookasideList(lookaside, allocfunc, freefunc, flags, size, tag, depth) paged_lookaside_list *lookaside; lookaside_alloc_func *allocfunc; lookaside_free_func *freefunc; uint32_t flags; size_t size; uint32_t tag; uint16_t depth; { bzero((char *)lookaside, sizeof(paged_lookaside_list)); if (size < sizeof(slist_entry)) lookaside->nll_l.gl_size = sizeof(slist_entry); else lookaside->nll_l.gl_size = size; lookaside->nll_l.gl_tag = tag; if (allocfunc == NULL) lookaside->nll_l.gl_allocfunc = ntoskrnl_findwrap((funcptr)ExAllocatePoolWithTag); else lookaside->nll_l.gl_allocfunc = allocfunc; if (freefunc == NULL) lookaside->nll_l.gl_freefunc = ntoskrnl_findwrap((funcptr)ExFreePool); else lookaside->nll_l.gl_freefunc = freefunc; #ifdef __i386__ KeInitializeSpinLock(&lookaside->nll_obsoletelock); #endif lookaside->nll_l.gl_type = NonPagedPool; lookaside->nll_l.gl_depth = depth; lookaside->nll_l.gl_maxdepth = LOOKASIDE_DEPTH; return; } static void ExDeletePagedLookasideList(lookaside) paged_lookaside_list *lookaside; { void *buf; void (*freefunc)(void *); freefunc = lookaside->nll_l.gl_freefunc; while((buf = ntoskrnl_popsl(&lookaside->nll_l.gl_listhead)) != NULL) MSCALL1(freefunc, buf); return; } static void ExInitializeNPagedLookasideList(lookaside, allocfunc, freefunc, flags, size, tag, depth) npaged_lookaside_list *lookaside; lookaside_alloc_func *allocfunc; lookaside_free_func *freefunc; uint32_t flags; size_t size; uint32_t tag; uint16_t depth; { bzero((char *)lookaside, sizeof(npaged_lookaside_list)); if (size < sizeof(slist_entry)) lookaside->nll_l.gl_size = sizeof(slist_entry); else lookaside->nll_l.gl_size = size; lookaside->nll_l.gl_tag = tag; if (allocfunc == NULL) lookaside->nll_l.gl_allocfunc = ntoskrnl_findwrap((funcptr)ExAllocatePoolWithTag); else lookaside->nll_l.gl_allocfunc = allocfunc; if (freefunc == NULL) lookaside->nll_l.gl_freefunc = ntoskrnl_findwrap((funcptr)ExFreePool); else lookaside->nll_l.gl_freefunc = freefunc; #ifdef __i386__ KeInitializeSpinLock(&lookaside->nll_obsoletelock); #endif lookaside->nll_l.gl_type = NonPagedPool; lookaside->nll_l.gl_depth = depth; lookaside->nll_l.gl_maxdepth = LOOKASIDE_DEPTH; return; } static void ExDeleteNPagedLookasideList(lookaside) npaged_lookaside_list *lookaside; { void *buf; void (*freefunc)(void *); freefunc = lookaside->nll_l.gl_freefunc; while((buf = ntoskrnl_popsl(&lookaside->nll_l.gl_listhead)) != NULL) MSCALL1(freefunc, buf); return; } -static slist_entry * +slist_entry * InterlockedPushEntrySList(head, entry) slist_header *head; slist_entry *entry; { slist_entry *oldhead; - oldhead = ExInterlockedPushEntrySList(head, entry, &ntoskrnl_global); + mtx_lock_spin(&ntoskrnl_interlock); + oldhead = ntoskrnl_pushsl(head, entry); + mtx_unlock_spin(&ntoskrnl_interlock); return(oldhead); } -static slist_entry * +slist_entry * InterlockedPopEntrySList(head) slist_header *head; { slist_entry *first; - first = ExInterlockedPopEntrySList(head, &ntoskrnl_global); + mtx_lock_spin(&ntoskrnl_interlock); + first = ntoskrnl_popsl(head); + mtx_unlock_spin(&ntoskrnl_interlock); return(first); } static slist_entry * ExInterlockedPushEntrySList(head, entry, lock) slist_header *head; slist_entry *entry; kspin_lock *lock; { - slist_entry *oldhead; - uint8_t irql; - - KeAcquireSpinLock(lock, &irql); - oldhead = ntoskrnl_pushsl(head, entry); - KeReleaseSpinLock(lock, irql); - - return(oldhead); + return(InterlockedPushEntrySList(head, entry)); } static slist_entry * ExInterlockedPopEntrySList(head, lock) slist_header *head; kspin_lock *lock; { - slist_entry *first; - uint8_t irql; - - KeAcquireSpinLock(lock, &irql); - first = ntoskrnl_popsl(head); - KeReleaseSpinLock(lock, irql); - - return(first); + return(InterlockedPopEntrySList(head)); } -static uint16_t +uint16_t ExQueryDepthSList(head) slist_header *head; { uint16_t depth; - uint8_t irql; - KeAcquireSpinLock(&ntoskrnl_global, &irql); + mtx_lock_spin(&ntoskrnl_interlock); depth = head->slh_list.slh_depth; - KeReleaseSpinLock(&ntoskrnl_global, irql); + mtx_unlock_spin(&ntoskrnl_interlock); return(depth); } -/* - * The KeInitializeSpinLock(), KefAcquireSpinLockAtDpcLevel() - * and KefReleaseSpinLockFromDpcLevel() appear to be analagous - * to splnet()/splx() in their use. We can't create a new mutex - * lock here because there is no complimentary KeFreeSpinLock() - * function. Instead, we grab a mutex from the mutex pool. - */ void KeInitializeSpinLock(lock) kspin_lock *lock; { *lock = 0; return; } #ifdef __i386__ void KefAcquireSpinLockAtDpcLevel(lock) kspin_lock *lock; { - while (atomic_cmpset_acq_int((volatile u_int *)lock, 0, 1) == 0) +#ifdef NTOSKRNL_DEBUG_SPINLOCKS + int i = 0; +#endif + + while (atomic_cmpset_acq_int((volatile u_int *)lock, 0, 1) == 0) { /* sit and spin */; +#ifdef NTOSKRNL_DEBUG_SPINLOCKS + i++; + if (i > 200000000) + panic("DEADLOCK!"); +#endif + } return; } void KefReleaseSpinLockFromDpcLevel(lock) kspin_lock *lock; { atomic_store_rel_int((volatile u_int *)lock, 0); return; } uint8_t KeAcquireSpinLockRaiseToDpc(kspin_lock *lock) { uint8_t oldirql; if (KeGetCurrentIrql() > DISPATCH_LEVEL) panic("IRQL_NOT_LESS_THAN_OR_EQUAL"); oldirql = KeRaiseIrql(DISPATCH_LEVEL); KeAcquireSpinLockAtDpcLevel(lock); return(oldirql); } #else void KeAcquireSpinLockAtDpcLevel(kspin_lock *lock) { while (atomic_cmpset_acq_int((volatile u_int *)lock, 0, 1) == 0) /* sit and spin */; return; } void KeReleaseSpinLockFromDpcLevel(kspin_lock *lock) { atomic_store_rel_int((volatile u_int *)lock, 0); return; } #endif /* __i386__ */ uintptr_t InterlockedExchange(dst, val) volatile uint32_t *dst; uintptr_t val; { - uint8_t irql; uintptr_t r; - KeAcquireSpinLock(&ntoskrnl_global, &irql); + mtx_lock_spin(&ntoskrnl_interlock); r = *dst; *dst = val; - KeReleaseSpinLock(&ntoskrnl_global, irql); + mtx_unlock_spin(&ntoskrnl_interlock); return(r); } static uint32_t InterlockedIncrement(addend) volatile uint32_t *addend; { atomic_add_long((volatile u_long *)addend, 1); return(*addend); } static uint32_t InterlockedDecrement(addend) volatile uint32_t *addend; { atomic_subtract_long((volatile u_long *)addend, 1); return(*addend); } static void ExInterlockedAddLargeStatistic(addend, inc) uint64_t *addend; uint32_t inc; { - uint8_t irql; - - KeAcquireSpinLock(&ntoskrnl_global, &irql); + mtx_lock_spin(&ntoskrnl_interlock); *addend += inc; - KeReleaseSpinLock(&ntoskrnl_global, irql); + mtx_unlock_spin(&ntoskrnl_interlock); return; }; mdl * IoAllocateMdl(vaddr, len, secondarybuf, chargequota, iopkt) void *vaddr; uint32_t len; uint8_t secondarybuf; uint8_t chargequota; irp *iopkt; { mdl *m; int zone = 0; if (MmSizeOfMdl(vaddr, len) > MDL_ZONE_SIZE) m = ExAllocatePoolWithTag(NonPagedPool, MmSizeOfMdl(vaddr, len), 0); else { m = uma_zalloc(mdl_zone, M_NOWAIT | M_ZERO); zone++; } if (m == NULL) return (NULL); MmInitializeMdl(m, vaddr, len); /* * MmInitializMdl() clears the flags field, so we * have to set this here. If the MDL came from the * MDL UMA zone, tag it so we can release it to * the right place later. */ if (zone) m->mdl_flags = MDL_ZONE_ALLOCED; if (iopkt != NULL) { if (secondarybuf == TRUE) { mdl *last; last = iopkt->irp_mdl; while (last->mdl_next != NULL) last = last->mdl_next; last->mdl_next = m; } else { if (iopkt->irp_mdl != NULL) panic("leaking an MDL in IoAllocateMdl()"); iopkt->irp_mdl = m; } } return (m); } void IoFreeMdl(m) mdl *m; { if (m == NULL) return; if (m->mdl_flags & MDL_ZONE_ALLOCED) uma_zfree(mdl_zone, m); else ExFreePool(m); return; } static uint32_t MmSizeOfMdl(vaddr, len) void *vaddr; size_t len; { uint32_t l; l = sizeof(struct mdl) + (sizeof(vm_offset_t *) * SPAN_PAGES(vaddr, len)); return(l); } /* * The Microsoft documentation says this routine fills in the * page array of an MDL with the _physical_ page addresses that * comprise the buffer, but we don't really want to do that here. * Instead, we just fill in the page array with the kernel virtual * addresses of the buffers. */ -static void +void MmBuildMdlForNonPagedPool(m) mdl *m; { vm_offset_t *mdl_pages; int pagecnt, i; pagecnt = SPAN_PAGES(m->mdl_byteoffset, m->mdl_bytecount); if (pagecnt > (m->mdl_size - sizeof(mdl)) / sizeof(vm_offset_t *)) panic("not enough pages in MDL to describe buffer"); mdl_pages = MmGetMdlPfnArray(m); for (i = 0; i < pagecnt; i++) *mdl_pages = (vm_offset_t)m->mdl_startva + (i * PAGE_SIZE); m->mdl_flags |= MDL_SOURCE_IS_NONPAGED_POOL; m->mdl_mappedsystemva = MmGetMdlVirtualAddress(m); return; } static void * MmMapLockedPages(buf, accessmode) mdl *buf; uint8_t accessmode; { buf->mdl_flags |= MDL_MAPPED_TO_SYSTEM_VA; return(MmGetMdlVirtualAddress(buf)); } static void * MmMapLockedPagesSpecifyCache(buf, accessmode, cachetype, vaddr, bugcheck, prio) mdl *buf; uint8_t accessmode; uint32_t cachetype; void *vaddr; uint32_t bugcheck; uint32_t prio; { return(MmMapLockedPages(buf, accessmode)); } static void MmUnmapLockedPages(vaddr, buf) void *vaddr; mdl *buf; { buf->mdl_flags &= ~MDL_MAPPED_TO_SYSTEM_VA; return; } /* * This function has a problem in that it will break if you * compile this module without PAE and try to use it on a PAE * kernel. Unfortunately, there's no way around this at the * moment. It's slightly less broken that using pmap_kextract(). * You'd think the virtual memory subsystem would help us out * here, but it doesn't. */ static uint8_t MmIsAddressValid(vaddr) void *vaddr; { if (pmap_extract(kernel_map->pmap, (vm_offset_t)vaddr)) return(TRUE); return(FALSE); } /* * Workitems are unlike DPCs, in that they run in a user-mode thread * context rather than at DISPATCH_LEVEL in kernel context. In our * case we run them in kernel context anyway. */ static void ntoskrnl_workitem_thread(arg) void *arg; { kdpc_queue *kq; list_entry *l; io_workitem *iw; + uint8_t irql; kq = arg; - INIT_LIST_HEAD(&kq->kq_med); + InitializeListHead(&kq->kq_disp); kq->kq_td = curthread; kq->kq_exit = 0; - kq->kq_state = NDIS_PSTATE_SLEEPING; - mtx_init(&kq->kq_lock, "NDIS thread lock", NULL, MTX_SPIN); + KeInitializeSpinLock(&kq->kq_lock); KeInitializeEvent(&kq->kq_proc, EVENT_TYPE_SYNC, FALSE); KeInitializeEvent(&kq->kq_dead, EVENT_TYPE_SYNC, FALSE); - while (1) { + /* + * Boost the priority of the workitem threads, + * though don't boost it as much as the DPC threads. + */ - KeWaitForSingleObject((nt_dispatch_header *)&kq->kq_proc, - 0, 0, TRUE, NULL); + mtx_lock_spin(&sched_lock); + sched_prio(curthread, PRIBIO); +#if __FreeBSD_version < 600000 + curthread->td_base_pri = PRIBIO; +#endif + mtx_unlock_spin(&sched_lock); - mtx_lock_spin(&kq->kq_lock); + while (1) { + KeWaitForSingleObject(&kq->kq_proc, 0, 0, TRUE, NULL); + KeAcquireSpinLock(&kq->kq_lock, &irql); + if (kq->kq_exit) { - mtx_unlock_spin(&kq->kq_lock); - KeSetEvent(&kq->kq_dead, 0, FALSE); + KeReleaseSpinLock(&kq->kq_lock, irql); + KeSetEvent(&kq->kq_dead, IO_NO_INCREMENT, FALSE); break; } - kq->kq_state = NDIS_PSTATE_RUNNING; - - l = kq->kq_med.nle_flink; - while (l != & kq->kq_med) { + while (!IsListEmpty(&kq->kq_disp)) { + l = RemoveHeadList(&kq->kq_disp); iw = CONTAINING_RECORD(l, io_workitem, iw_listentry); - REMOVE_LIST_HEAD((&kq->kq_med)); - INIT_LIST_HEAD(l); - if (iw->iw_func == NULL) { - l = kq->kq_med.nle_flink; + InitializeListHead((&iw->iw_listentry)); + if (iw->iw_func == NULL) continue; - } - mtx_unlock_spin(&kq->kq_lock); + KeReleaseSpinLock(&kq->kq_lock, irql); MSCALL2(iw->iw_func, iw->iw_dobj, iw->iw_ctx); - mtx_lock_spin(&kq->kq_lock); - l = kq->kq_med.nle_flink; + KeAcquireSpinLock(&kq->kq_lock, &irql); } - kq->kq_state = NDIS_PSTATE_SLEEPING; - - mtx_unlock_spin(&kq->kq_lock); + KeReleaseSpinLock(&kq->kq_lock, irql); } - mtx_destroy(&kq->kq_lock); #if __FreeBSD_version < 502113 mtx_lock(&Giant); #endif kthread_exit(0); return; /* notreached */ } static void ntoskrnl_destroy_workitem_threads(void) { kdpc_queue *kq; int i; for (i = 0; i < WORKITEM_THREADS; i++) { kq = wq_queues + i; - kq->kq_exit = 1; - KeSetEvent(&kq->kq_proc, 0, FALSE); - KeWaitForSingleObject((nt_dispatch_header *)&kq->kq_dead, - 0, 0, TRUE, NULL); + KeSetEvent(&kq->kq_proc, IO_NO_INCREMENT, FALSE); + KeWaitForSingleObject(&kq->kq_dead, 0, 0, TRUE, NULL); } return; } io_workitem * IoAllocateWorkItem(dobj) device_object *dobj; { io_workitem *iw; iw = uma_zalloc(iw_zone, M_NOWAIT); if (iw == NULL) return(NULL); - INIT_LIST_HEAD(&iw->iw_listentry); + InitializeListHead(&iw->iw_listentry); iw->iw_dobj = dobj; mtx_lock(&ntoskrnl_dispatchlock); iw->iw_idx = wq_idx; WORKIDX_INC(wq_idx); mtx_unlock(&ntoskrnl_dispatchlock); return(iw); } void IoFreeWorkItem(iw) io_workitem *iw; { uma_zfree(iw_zone, iw); return; } void IoQueueWorkItem(iw, iw_func, qtype, ctx) io_workitem *iw; io_workitem_func iw_func; uint32_t qtype; void *ctx; { - int state; kdpc_queue *kq; + list_entry *l; + io_workitem *cur; + uint8_t irql; + kq = wq_queues + iw->iw_idx; + + KeAcquireSpinLock(&kq->kq_lock, &irql); + + /* + * Traverse the list and make sure this workitem hasn't + * already been inserted. Queuing the same workitem + * twice will hose the list but good. + */ + + l = kq->kq_disp.nle_flink; + while (l != &kq->kq_disp) { + cur = CONTAINING_RECORD(l, io_workitem, iw_listentry); + if (cur == iw) { + /* Already queued -- do nothing. */ + KeReleaseSpinLock(&kq->kq_lock, irql); + return; + } + l = l->nle_flink; + } + iw->iw_func = iw_func; iw->iw_ctx = ctx; + InsertTailList((&kq->kq_disp), (&iw->iw_listentry)); + KeReleaseSpinLock(&kq->kq_lock, irql); - kq = wq_queues + iw->iw_idx; + KeSetEvent(&kq->kq_proc, IO_NO_INCREMENT, FALSE); - mtx_lock_spin(&kq->kq_lock); - INSERT_LIST_TAIL((&kq->kq_med), (&iw->iw_listentry)); - state = kq->kq_state; - mtx_unlock_spin(&kq->kq_lock); - if (state == NDIS_PSTATE_SLEEPING) - KeSetEvent(&kq->kq_proc, 0, FALSE); - return; } static void ntoskrnl_workitem(dobj, arg) device_object *dobj; void *arg; { io_workitem *iw; work_queue_item *w; work_item_func f; iw = arg; w = (work_queue_item *)dobj; f = (work_item_func)w->wqi_func; uma_zfree(iw_zone, iw); MSCALL2(f, w, w->wqi_ctx); return; } +/* + * The ExQueueWorkItem() API is deprecated in Windows XP. Microsoft + * warns that it's unsafe and to use IoQueueWorkItem() instead. The + * problem with ExQueueWorkItem() is that it can't guard against + * the condition where a driver submits a job to the work queue and + * is then unloaded before the job is able to run. IoQueueWorkItem() + * acquires a reference to the device's device_object via the + * object manager and retains it until after the job has completed, + * which prevents the driver from being unloaded before the job + * runs. (We don't currently support this behavior, though hopefully + * that will change once the object manager API is fleshed out a bit.) + * + * Having said all that, the ExQueueWorkItem() API remains, because + * there are still other parts of Windows that use it, including + * NDIS itself: NdisScheduleWorkItem() calls ExQueueWorkItem(). + * We fake up the ExQueueWorkItem() API on top of our implementation + * of IoQueueWorkItem(). Workitem thread #3 is reserved exclusively + * for ExQueueWorkItem() jobs, and we pass a pointer to the work + * queue item (provided by the caller) in to IoAllocateWorkItem() + * instead of the device_object. We need to save this pointer so + * we can apply a sanity check: as with the DPC queue and other + * workitem queues, we can't allow the same work queue item to + * be queued twice. If it's already pending, we silently return + */ + void ExQueueWorkItem(w, qtype) work_queue_item *w; uint32_t qtype; { io_workitem *iw; io_workitem_func iwf; + kdpc_queue *kq; + list_entry *l; + io_workitem *cur; + uint8_t irql; + + /* + * We need to do a special sanity test to make sure + * the ExQueueWorkItem() API isn't used to queue + * the same workitem twice. Rather than checking the + * io_workitem pointer itself, we test the attached + * device object, which is really a pointer to the + * legacy work queue item structure. + */ + + kq = wq_queues + WORKITEM_LEGACY_THREAD; + KeAcquireSpinLock(&kq->kq_lock, &irql); + l = kq->kq_disp.nle_flink; + while (l != &kq->kq_disp) { + cur = CONTAINING_RECORD(l, io_workitem, iw_listentry); + if (cur->iw_dobj == (device_object *)w) { + /* Already queued -- do nothing. */ + KeReleaseSpinLock(&kq->kq_lock, irql); + return; + } + l = l->nle_flink; + } + KeReleaseSpinLock(&kq->kq_lock, irql); + iw = IoAllocateWorkItem((device_object *)w); if (iw == NULL) return; iw->iw_idx = WORKITEM_LEGACY_THREAD; iwf = (io_workitem_func)ntoskrnl_findwrap((funcptr)ntoskrnl_workitem); IoQueueWorkItem(iw, iwf, qtype, iw); return; } static size_t RtlCompareMemory(s1, s2, len) const void *s1; const void *s2; size_t len; { size_t i, total = 0; uint8_t *m1, *m2; m1 = __DECONST(char *, s1); m2 = __DECONST(char *, s2); for (i = 0; i < len; i++) { if (m1[i] == m2[i]) total++; } return(total); } -static void +void RtlInitAnsiString(dst, src) - ndis_ansi_string *dst; + ansi_string *dst; char *src; { - ndis_ansi_string *a; + ansi_string *a; a = dst; if (a == NULL) return; if (src == NULL) { - a->nas_len = a->nas_maxlen = 0; - a->nas_buf = NULL; + a->as_len = a->as_maxlen = 0; + a->as_buf = NULL; } else { - a->nas_buf = src; - a->nas_len = a->nas_maxlen = strlen(src); + a->as_buf = src; + a->as_len = a->as_maxlen = strlen(src); } return; } -static void +void RtlInitUnicodeString(dst, src) - ndis_unicode_string *dst; + unicode_string *dst; uint16_t *src; { - ndis_unicode_string *u; + unicode_string *u; int i; u = dst; if (u == NULL) return; if (src == NULL) { u->us_len = u->us_maxlen = 0; u->us_buf = NULL; } else { i = 0; while(src[i] != 0) i++; u->us_buf = src; u->us_len = u->us_maxlen = i * 2; } return; } ndis_status RtlUnicodeStringToInteger(ustr, base, val) - ndis_unicode_string *ustr; + unicode_string *ustr; uint32_t base; uint32_t *val; { uint16_t *uchr; int len, neg = 0; char abuf[64]; char *astr; uchr = ustr->us_buf; len = ustr->us_len; bzero(abuf, sizeof(abuf)); if ((char)((*uchr) & 0xFF) == '-') { neg = 1; uchr++; len -= 2; } else if ((char)((*uchr) & 0xFF) == '+') { neg = 0; uchr++; len -= 2; } if (base == 0) { if ((char)((*uchr) & 0xFF) == 'b') { base = 2; uchr++; len -= 2; } else if ((char)((*uchr) & 0xFF) == 'o') { base = 8; uchr++; len -= 2; } else if ((char)((*uchr) & 0xFF) == 'x') { base = 16; uchr++; len -= 2; } else base = 10; } astr = abuf; if (neg) { strcpy(astr, "-"); astr++; } - ndis_unicode_to_ascii(uchr, len, &astr); + ntoskrnl_unicode_to_ascii(uchr, astr, len); *val = strtoul(abuf, NULL, base); return(NDIS_STATUS_SUCCESS); } -static void +void RtlFreeUnicodeString(ustr) - ndis_unicode_string *ustr; + unicode_string *ustr; { if (ustr->us_buf == NULL) return; - free(ustr->us_buf, M_DEVBUF); + ExFreePool(ustr->us_buf); ustr->us_buf = NULL; return; } -static void +void RtlFreeAnsiString(astr) - ndis_ansi_string *astr; + ansi_string *astr; { - if (astr->nas_buf == NULL) + if (astr->as_buf == NULL) return; - free(astr->nas_buf, M_DEVBUF); - astr->nas_buf = NULL; + ExFreePool(astr->as_buf); + astr->as_buf = NULL; return; } static int atoi(str) const char *str; { return (int)strtol(str, (char **)NULL, 10); } static long atol(str) const char *str; { return strtol(str, (char **)NULL, 10); } static int rand(void) { struct timeval tv; microtime(&tv); srandom(tv.tv_usec); return((int)random()); } static void srand(seed) unsigned int seed; { srandom(seed); return; } static uint8_t IoIsWdmVersionAvailable(major, minor) uint8_t major; uint8_t minor; { if (major == WDM_MAJOR && minor == WDM_MINOR_WINXP) return(TRUE); return(FALSE); } static ndis_status IoGetDeviceProperty(devobj, regprop, buflen, prop, reslen) device_object *devobj; uint32_t regprop; uint32_t buflen; void *prop; uint32_t *reslen; { driver_object *drv; uint16_t **name; drv = devobj->do_drvobj; switch (regprop) { case DEVPROP_DRIVER_KEYNAME: name = prop; *name = drv->dro_drivername.us_buf; *reslen = drv->dro_drivername.us_len; break; default: return(STATUS_INVALID_PARAMETER_2); break; } return(STATUS_SUCCESS); } static void KeInitializeMutex(kmutex, level) kmutant *kmutex; uint32_t level; { - INIT_LIST_HEAD((&kmutex->km_header.dh_waitlisthead)); + InitializeListHead((&kmutex->km_header.dh_waitlisthead)); kmutex->km_abandoned = FALSE; kmutex->km_apcdisable = 1; - kmutex->km_header.dh_sigstate = TRUE; - kmutex->km_header.dh_type = EVENT_TYPE_SYNC; - kmutex->km_header.dh_size = OTYPE_MUTEX; - kmutex->km_acquirecnt = 0; + kmutex->km_header.dh_sigstate = 1; + kmutex->km_header.dh_type = DISP_TYPE_MUTANT; + kmutex->km_header.dh_size = sizeof(kmutant) / sizeof(uint32_t); kmutex->km_ownerthread = NULL; return; } static uint32_t KeReleaseMutex(kmutex, kwait) kmutant *kmutex; uint8_t kwait; { + uint32_t prevstate; + mtx_lock(&ntoskrnl_dispatchlock); - if (kmutex->km_ownerthread != curthread->td_proc) { + prevstate = kmutex->km_header.dh_sigstate; + if (kmutex->km_ownerthread != curthread) { mtx_unlock(&ntoskrnl_dispatchlock); return(STATUS_MUTANT_NOT_OWNED); } - kmutex->km_acquirecnt--; - if (kmutex->km_acquirecnt == 0) { + + kmutex->km_header.dh_sigstate++; + kmutex->km_abandoned = FALSE; + + if (kmutex->km_header.dh_sigstate == 1) { kmutex->km_ownerthread = NULL; - ntoskrnl_wakeup(&kmutex->km_header); + ntoskrnl_waittest(&kmutex->km_header, IO_NO_INCREMENT); } + mtx_unlock(&ntoskrnl_dispatchlock); - return(kmutex->km_acquirecnt); + return(prevstate); } static uint32_t KeReadStateMutex(kmutex) kmutant *kmutex; { return(kmutex->km_header.dh_sigstate); } void KeInitializeEvent(kevent, type, state) nt_kevent *kevent; uint32_t type; uint8_t state; { - INIT_LIST_HEAD((&kevent->k_header.dh_waitlisthead)); + InitializeListHead((&kevent->k_header.dh_waitlisthead)); kevent->k_header.dh_sigstate = state; - kevent->k_header.dh_type = type; - kevent->k_header.dh_size = OTYPE_EVENT; + if (type == EVENT_TYPE_NOTIFY) + kevent->k_header.dh_type = DISP_TYPE_NOTIFICATION_EVENT; + else + kevent->k_header.dh_type = DISP_TYPE_SYNCHRONIZATION_EVENT; + kevent->k_header.dh_size = sizeof(nt_kevent) / sizeof(uint32_t); return; } uint32_t KeResetEvent(kevent) nt_kevent *kevent; { uint32_t prevstate; mtx_lock(&ntoskrnl_dispatchlock); prevstate = kevent->k_header.dh_sigstate; kevent->k_header.dh_sigstate = FALSE; mtx_unlock(&ntoskrnl_dispatchlock); return(prevstate); } uint32_t KeSetEvent(kevent, increment, kwait) nt_kevent *kevent; uint32_t increment; uint8_t kwait; { uint32_t prevstate; + wait_block *w; + nt_dispatch_header *dh; + struct thread *td; + wb_ext *we; mtx_lock(&ntoskrnl_dispatchlock); prevstate = kevent->k_header.dh_sigstate; - ntoskrnl_wakeup(&kevent->k_header); + dh = &kevent->k_header; + + if (IsListEmpty(&dh->dh_waitlisthead)) + /* + * If there's nobody in the waitlist, just set + * the state to signalled. + */ + dh->dh_sigstate = 1; + else { + /* + * Get the first waiter. If this is a synchronization + * event, just wake up that one thread (don't bother + * setting the state to signalled since we're supposed + * to automatically clear synchronization events anyway). + * + * If it's a notification event, or the the first + * waiter is doing a WAITTYPE_ALL wait, go through + * the full wait satisfaction process. + */ + w = CONTAINING_RECORD(dh->dh_waitlisthead.nle_flink, + wait_block, wb_waitlist); + we = w->wb_ext; + td = we->we_td; + if (kevent->k_header.dh_type == DISP_TYPE_NOTIFICATION_EVENT || + w->wb_waittype == WAITTYPE_ALL) { + if (prevstate == 0) { + dh->dh_sigstate = 1; + ntoskrnl_waittest(dh, increment); + } + } else { + w->wb_awakened |= TRUE; + cv_broadcastpri(&we->we_cv, w->wb_oldpri - + (increment * 4)); + } + } + mtx_unlock(&ntoskrnl_dispatchlock); return(prevstate); } void KeClearEvent(kevent) nt_kevent *kevent; { kevent->k_header.dh_sigstate = FALSE; return; } uint32_t KeReadStateEvent(kevent) nt_kevent *kevent; { return(kevent->k_header.dh_sigstate); } +/* + * The object manager in Windows is responsible for managing + * references and access to various types of objects, including + * device_objects, events, threads, timers and so on. However, + * there's a difference in the way objects are handled in user + * mode versus kernel mode. + * + * In user mode (i.e. Win32 applications), all objects are + * managed by the object manager. For example, when you create + * a timer or event object, you actually end up with an + * object_header (for the object manager's bookkeeping + * purposes) and an object body (which contains the actual object + * structure, e.g. ktimer, kevent, etc...). This allows Windows + * to manage resource quotas and to enforce access restrictions + * on basically every kind of system object handled by the kernel. + * + * However, in kernel mode, you only end up using the object + * manager some of the time. For example, in a driver, you create + * a timer object by simply allocating the memory for a ktimer + * structure and initializing it with KeInitializeTimer(). Hence, + * the timer has no object_header and no reference counting or + * security/resource checks are done on it. The assumption in + * this case is that if you're running in kernel mode, you know + * what you're doing, and you're already at an elevated privilege + * anyway. + * + * There are some exceptions to this. The two most important ones + * for our purposes are device_objects and threads. We need to use + * the object manager to do reference counting on device_objects, + * and for threads, you can only get a pointer to a thread's + * dispatch header by using ObReferenceObjectByHandle() on the + * handle returned by PsCreateSystemThread(). + */ + static ndis_status ObReferenceObjectByHandle(handle, reqaccess, otype, accessmode, object, handleinfo) ndis_handle handle; uint32_t reqaccess; void *otype; uint8_t accessmode; void **object; void **handleinfo; { nt_objref *nr; nr = malloc(sizeof(nt_objref), M_DEVBUF, M_NOWAIT|M_ZERO); if (nr == NULL) return(NDIS_STATUS_FAILURE); - INIT_LIST_HEAD((&nr->no_dh.dh_waitlisthead)); + InitializeListHead((&nr->no_dh.dh_waitlisthead)); nr->no_obj = handle; - nr->no_dh.dh_size = OTYPE_THREAD; + nr->no_dh.dh_type = DISP_TYPE_THREAD; + nr->no_dh.dh_sigstate = 0; + nr->no_dh.dh_size = (uint8_t)(sizeof(struct thread) / + sizeof(uint32_t)); TAILQ_INSERT_TAIL(&ntoskrnl_reflist, nr, link); *object = nr; return(NDIS_STATUS_SUCCESS); } static void ObfDereferenceObject(object) void *object; { nt_objref *nr; nr = object; TAILQ_REMOVE(&ntoskrnl_reflist, nr, link); free(nr, M_DEVBUF); return; } static uint32_t ZwClose(handle) ndis_handle handle; { return(STATUS_SUCCESS); } /* * This is here just in case the thread returns without calling * PsTerminateSystemThread(). */ static void ntoskrnl_thrfunc(arg) void *arg; { thread_context *thrctx; uint32_t (*tfunc)(void *); void *tctx; uint32_t rval; thrctx = arg; tfunc = thrctx->tc_thrfunc; tctx = thrctx->tc_thrctx; free(thrctx, M_TEMP); rval = MSCALL1(tfunc, tctx); PsTerminateSystemThread(rval); return; /* notreached */ } static ndis_status PsCreateSystemThread(handle, reqaccess, objattrs, phandle, clientid, thrfunc, thrctx) ndis_handle *handle; uint32_t reqaccess; void *objattrs; ndis_handle phandle; void *clientid; void *thrfunc; void *thrctx; { int error; char tname[128]; thread_context *tc; struct proc *p; tc = malloc(sizeof(thread_context), M_TEMP, M_NOWAIT); if (tc == NULL) return(NDIS_STATUS_FAILURE); tc->tc_thrctx = thrctx; tc->tc_thrfunc = thrfunc; sprintf(tname, "windows kthread %d", ntoskrnl_kth); error = kthread_create(ntoskrnl_thrfunc, tc, &p, RFHIGHPID, NDIS_KSTACK_PAGES, tname); *handle = p; ntoskrnl_kth++; return(error); } /* * In Windows, the exit of a thread is an event that you're allowed * to wait on, assuming you've obtained a reference to the thread using * ObReferenceObjectByHandle(). Unfortunately, the only way we can * simulate this behavior is to register each thread we create in a * reference list, and if someone holds a reference to us, we poke * them. */ static ndis_status PsTerminateSystemThread(status) ndis_status status; { struct nt_objref *nr; mtx_lock(&ntoskrnl_dispatchlock); TAILQ_FOREACH(nr, &ntoskrnl_reflist, link) { if (nr->no_obj != curthread->td_proc) continue; - ntoskrnl_wakeup(&nr->no_dh); + nr->no_dh.dh_sigstate = 1; + ntoskrnl_waittest(&nr->no_dh, IO_NO_INCREMENT); break; } mtx_unlock(&ntoskrnl_dispatchlock); ntoskrnl_kth--; #if __FreeBSD_version < 502113 mtx_lock(&Giant); #endif kthread_exit(0); return(0); /* notreached */ } static uint32_t DbgPrint(char *fmt, ...) { va_list ap; if (bootverbose) { va_start(ap, fmt); vprintf(fmt, ap); } return(STATUS_SUCCESS); } static void DbgBreakPoint(void) { #if __FreeBSD_version < 502113 Debugger("DbgBreakPoint(): breakpoint"); #else kdb_enter("DbgBreakPoint(): breakpoint"); #endif } static void ntoskrnl_timercall(arg) void *arg; { ktimer *timer; struct timeval tv; + kdpc *dpc; - mtx_unlock(&Giant); - mtx_lock(&ntoskrnl_dispatchlock); timer = arg; +#ifdef NTOSKRNL_DEBUG_TIMERS + ntoskrnl_timer_fires++; +#endif + ntoskrnl_remove_timer(timer); + + /* + * This should never happen, but complain + * if it does. + */ + + if (timer->k_header.dh_inserted == FALSE) { + mtx_unlock(&ntoskrnl_dispatchlock); + printf("NTOS: timer %p fired even though " + "it was canceled\n", timer); + return; + } + + /* Mark the timer as no longer being on the timer queue. */ + timer->k_header.dh_inserted = FALSE; + /* Now signal the object and satisfy any waits on it. */ + + timer->k_header.dh_sigstate = 1; + ntoskrnl_waittest(&timer->k_header, IO_NO_INCREMENT); + /* * If this is a periodic timer, re-arm it * so it will fire again. We do this before * calling any deferred procedure calls because * it's possible the DPC might cancel the timer, * in which case it would be wrong for us to * re-arm it again afterwards. */ if (timer->k_period) { tv.tv_sec = 0; tv.tv_usec = timer->k_period * 1000; timer->k_header.dh_inserted = TRUE; - timer->k_handle = timeout(ntoskrnl_timercall, - timer, tvtohz(&tv)); + ntoskrnl_insert_timer(timer, tvtohz(&tv)); +#ifdef NTOSKRNL_DEBUG_TIMERS + ntoskrnl_timer_reloads++; +#endif } - if (timer->k_dpc != NULL) - KeInsertQueueDpc(timer->k_dpc, NULL, NULL); + dpc = timer->k_dpc; - ntoskrnl_wakeup(&timer->k_header); mtx_unlock(&ntoskrnl_dispatchlock); - mtx_lock(&Giant); + /* If there's a DPC associated with the timer, queue it up. */ + if (dpc != NULL) + KeInsertQueueDpc(dpc, NULL, NULL); + return; } +#ifdef NTOSKRNL_DEBUG_TIMERS +static int +sysctl_show_timers(SYSCTL_HANDLER_ARGS) +{ + int ret; + + ret = 0; + ntoskrnl_show_timers(); + return (sysctl_handle_int(oidp, &ret, 0, req)); +} + +static void +ntoskrnl_show_timers() +{ + int i = 0; + list_entry *l; + + mtx_lock_spin(&ntoskrnl_calllock); + l = ntoskrnl_calllist.nle_flink; + while(l != &ntoskrnl_calllist) { + i++; + l = l->nle_flink; + } + mtx_unlock_spin(&ntoskrnl_calllock); + + printf("\n"); + printf("%d timers available (out of %d)\n", i, NTOSKRNL_TIMEOUTS); + printf("timer sets: %qu\n", ntoskrnl_timer_sets); + printf("timer reloads: %qu\n", ntoskrnl_timer_reloads); + printf("timer cancels: %qu\n", ntoskrnl_timer_cancels); + printf("timer fires: %qu\n", ntoskrnl_timer_fires); + printf("\n"); + + return; +} +#endif + +/* + * Must be called with dispatcher lock held. + */ + +static void +ntoskrnl_insert_timer(timer, ticks) + ktimer *timer; + int ticks; +{ + callout_entry *e; + list_entry *l; + struct callout *c; + + /* + * Try and allocate a timer. + */ + mtx_lock_spin(&ntoskrnl_calllock); + if (IsListEmpty(&ntoskrnl_calllist)) { + mtx_unlock_spin(&ntoskrnl_calllock); +#ifdef NTOSKRNL_DEBUG_TIMERS + ntoskrnl_show_timers(); +#endif + panic("out of timers!"); + } + l = RemoveHeadList(&ntoskrnl_calllist); + mtx_unlock_spin(&ntoskrnl_calllock); + + e = CONTAINING_RECORD(l, callout_entry, ce_list); + c = &e->ce_callout; + + timer->k_callout = c; + + callout_init(c, CALLOUT_MPSAFE); + callout_reset(c, ticks, ntoskrnl_timercall, timer); + + return; +} + +static void +ntoskrnl_remove_timer(timer) + ktimer *timer; +{ + callout_entry *e; + + e = (callout_entry *)timer->k_callout; + callout_stop(timer->k_callout); + + mtx_lock_spin(&ntoskrnl_calllock); + InsertHeadList((&ntoskrnl_calllist), (&e->ce_list)); + mtx_unlock_spin(&ntoskrnl_calllock); + + return; +} + void KeInitializeTimer(timer) ktimer *timer; { if (timer == NULL) return; KeInitializeTimerEx(timer, EVENT_TYPE_NOTIFY); return; } void KeInitializeTimerEx(timer, type) ktimer *timer; uint32_t type; { if (timer == NULL) return; bzero((char *)timer, sizeof(ktimer)); - INIT_LIST_HEAD((&timer->k_header.dh_waitlisthead)); + InitializeListHead((&timer->k_header.dh_waitlisthead)); timer->k_header.dh_sigstate = FALSE; timer->k_header.dh_inserted = FALSE; - timer->k_header.dh_type = type; - timer->k_header.dh_size = OTYPE_TIMER; - callout_handle_init(&timer->k_handle); + if (type == EVENT_TYPE_NOTIFY) + timer->k_header.dh_type = DISP_TYPE_NOTIFICATION_TIMER; + else + timer->k_header.dh_type = DISP_TYPE_SYNCHRONIZATION_TIMER; + timer->k_header.dh_size = sizeof(ktimer) / sizeof(uint32_t); return; } /* * DPC subsystem. A Windows Defered Procedure Call has the following * properties: * - It runs at DISPATCH_LEVEL. * - It can have one of 3 importance values that control when it * runs relative to other DPCs in the queue. * - On SMP systems, it can be set to run on a specific processor. * In order to satisfy the last property, we create a DPC thread for * each CPU in the system and bind it to that CPU. Each thread * maintains three queues with different importance levels, which * will be processed in order from lowest to highest. * * In Windows, interrupt handlers run as DPCs. (Not to be confused * with ISRs, which run in interrupt context and can preempt DPCs.) * ISRs are given the highest importance so that they'll take * precedence over timers and other things. */ static void ntoskrnl_dpc_thread(arg) void *arg; { kdpc_queue *kq; kdpc *d; list_entry *l; + uint8_t irql; kq = arg; - INIT_LIST_HEAD(&kq->kq_high); - INIT_LIST_HEAD(&kq->kq_low); - INIT_LIST_HEAD(&kq->kq_med); + InitializeListHead(&kq->kq_disp); kq->kq_td = curthread; kq->kq_exit = 0; - kq->kq_state = NDIS_PSTATE_SLEEPING; - mtx_init(&kq->kq_lock, "NDIS thread lock", NULL, MTX_SPIN); + kq->kq_running = FALSE; + KeInitializeSpinLock(&kq->kq_lock); KeInitializeEvent(&kq->kq_proc, EVENT_TYPE_SYNC, FALSE); KeInitializeEvent(&kq->kq_done, EVENT_TYPE_SYNC, FALSE); KeInitializeEvent(&kq->kq_dead, EVENT_TYPE_SYNC, FALSE); - sched_pin(); + /* + * Elevate our priority. DPCs are used to run interrupt + * handlers, and they should trigger as soon as possible + * once scheduled by an ISR. + */ + mtx_lock_spin(&sched_lock); +#if __FreeBSD_version >= 502102 + sched_bind(curthread, kq->kq_cpu); +#endif + sched_prio(curthread, PRI_MIN_KERN); +#if __FreeBSD_version < 600000 + curthread->td_base_pri = PRI_MIN_KERN; +#endif + mtx_unlock_spin(&sched_lock); + while (1) { - KeWaitForSingleObject((nt_dispatch_header *)&kq->kq_proc, - 0, 0, TRUE, NULL); + KeWaitForSingleObject(&kq->kq_proc, 0, 0, TRUE, NULL); - mtx_lock_spin(&kq->kq_lock); + KeAcquireSpinLock(&kq->kq_lock, &irql); if (kq->kq_exit) { - mtx_unlock_spin(&kq->kq_lock); - KeSetEvent(&kq->kq_dead, 0, FALSE); + KeReleaseSpinLock(&kq->kq_lock, irql); + KeSetEvent(&kq->kq_dead, IO_NO_INCREMENT, FALSE); break; } - kq->kq_state = NDIS_PSTATE_RUNNING; + kq->kq_running = TRUE; - /* Process high importance list first. */ - - l = kq->kq_high.nle_flink; - while (l != &kq->kq_high) { + while (!IsListEmpty(&kq->kq_disp)) { + l = RemoveHeadList((&kq->kq_disp)); d = CONTAINING_RECORD(l, kdpc, k_dpclistentry); - REMOVE_LIST_ENTRY((&d->k_dpclistentry)); - INIT_LIST_HEAD((&d->k_dpclistentry)); - d->k_lock = NULL; - mtx_unlock_spin(&kq->kq_lock); - ntoskrnl_run_dpc(d); - mtx_lock_spin(&kq->kq_lock); - l = kq->kq_high.nle_flink; + InitializeListHead((&d->k_dpclistentry)); + KeReleaseSpinLockFromDpcLevel(&kq->kq_lock); + MSCALL4(d->k_deferedfunc, d, d->k_deferredctx, + d->k_sysarg1, d->k_sysarg2); + KeAcquireSpinLockAtDpcLevel(&kq->kq_lock); } - /* Now the medium importance list. */ + kq->kq_running = FALSE; - l = kq->kq_med.nle_flink; - while (l != &kq->kq_med) { - d = CONTAINING_RECORD(l, kdpc, k_dpclistentry); - REMOVE_LIST_ENTRY((&d->k_dpclistentry)); - INIT_LIST_HEAD((&d->k_dpclistentry)); - d->k_lock = NULL; - mtx_unlock_spin(&kq->kq_lock); - ntoskrnl_run_dpc(d); - mtx_lock_spin(&kq->kq_lock); - l = kq->kq_med.nle_flink; - } + KeReleaseSpinLock(&kq->kq_lock, irql); - /* And finally the low importance list. */ - - l = kq->kq_low.nle_flink; - while (l != &kq->kq_low) { - d = CONTAINING_RECORD(l, kdpc, k_dpclistentry); - REMOVE_LIST_ENTRY((&d->k_dpclistentry)); - INIT_LIST_HEAD((&d->k_dpclistentry)); - d->k_lock = NULL; - mtx_unlock_spin(&kq->kq_lock); - ntoskrnl_run_dpc(d); - mtx_lock_spin(&kq->kq_lock); - l = kq->kq_low.nle_flink; - } - - kq->kq_state = NDIS_PSTATE_SLEEPING; - - mtx_unlock_spin(&kq->kq_lock); - - KeSetEvent(&kq->kq_done, 0, FALSE); - + KeSetEvent(&kq->kq_done, IO_NO_INCREMENT, FALSE); } - mtx_destroy(&kq->kq_lock); #if __FreeBSD_version < 502113 mtx_lock(&Giant); #endif kthread_exit(0); return; /* notreached */ } - -/* - * This is a wrapper for Windows deferred procedure calls that - * have been placed on an NDIS thread work queue. We need it - * since the DPC could be a _stdcall function. Also, as far as - * I can tell, defered procedure calls must run at DISPATCH_LEVEL. - */ static void -ntoskrnl_run_dpc(arg) - void *arg; -{ - kdpc_func dpcfunc; - kdpc *dpc; - uint8_t irql; - - dpc = arg; - dpcfunc = dpc->k_deferedfunc; - if (dpcfunc == NULL) - return; - irql = KeRaiseIrql(DISPATCH_LEVEL); - MSCALL4(dpcfunc, dpc, dpc->k_deferredctx, - dpc->k_sysarg1, dpc->k_sysarg2); - KeLowerIrql(irql); - - return; -} - -static void ntoskrnl_destroy_dpc_threads(void) { kdpc_queue *kq; kdpc dpc; int i; kq = kq_queues; for (i = 0; i < mp_ncpus; i++) { kq += i; kq->kq_exit = 1; KeInitializeDpc(&dpc, NULL, NULL); KeSetTargetProcessorDpc(&dpc, i); KeInsertQueueDpc(&dpc, NULL, NULL); - - KeWaitForSingleObject((nt_dispatch_header *)&kq->kq_dead, - 0, 0, TRUE, NULL); + KeWaitForSingleObject(&kq->kq_dead, 0, 0, TRUE, NULL); } return; } static uint8_t ntoskrnl_insert_dpc(head, dpc) list_entry *head; kdpc *dpc; { list_entry *l; kdpc *d; l = head->nle_flink; while (l != head) { d = CONTAINING_RECORD(l, kdpc, k_dpclistentry); if (d == dpc) return(FALSE); l = l->nle_flink; } - INSERT_LIST_TAIL((head), (&dpc->k_dpclistentry)); + if (dpc->k_importance == KDPC_IMPORTANCE_LOW) + InsertTailList((head), (&dpc->k_dpclistentry)); + else + InsertHeadList((head), (&dpc->k_dpclistentry)); + return (TRUE); } void KeInitializeDpc(dpc, dpcfunc, dpcctx) kdpc *dpc; void *dpcfunc; void *dpcctx; { if (dpc == NULL) return; dpc->k_deferedfunc = dpcfunc; dpc->k_deferredctx = dpcctx; dpc->k_num = KDPC_CPU_DEFAULT; dpc->k_importance = KDPC_IMPORTANCE_MEDIUM; - dpc->k_num = KeGetCurrentProcessorNumber(); - /* - * In case someone tries to dequeue a DPC that - * hasn't been queued yet. - */ - dpc->k_lock = NULL /*&ntoskrnl_dispatchlock*/; - INIT_LIST_HEAD((&dpc->k_dpclistentry)); + InitializeListHead((&dpc->k_dpclistentry)); return; } uint8_t KeInsertQueueDpc(dpc, sysarg1, sysarg2) kdpc *dpc; void *sysarg1; void *sysarg2; { kdpc_queue *kq; uint8_t r; - int state; + uint8_t irql; if (dpc == NULL) return(FALSE); - dpc->k_sysarg1 = sysarg1; - dpc->k_sysarg2 = sysarg2; + irql = KeRaiseIrql(DISPATCH_LEVEL); /* * By default, the DPC is queued to run on the same CPU * that scheduled it. */ kq = kq_queues; if (dpc->k_num == KDPC_CPU_DEFAULT) kq += curthread->td_oncpu; else kq += dpc->k_num; - /* - * Also by default, we put the DPC on the medium - * priority queue. - */ + KeAcquireSpinLockAtDpcLevel(&kq->kq_lock); + r = ntoskrnl_insert_dpc(&kq->kq_disp, dpc); + if (r == TRUE) { + dpc->k_sysarg1 = sysarg1; + dpc->k_sysarg2 = sysarg2; + } + KeReleaseSpinLock(&kq->kq_lock, irql); - mtx_lock_spin(&kq->kq_lock); - if (dpc->k_importance == KDPC_IMPORTANCE_HIGH) - r = ntoskrnl_insert_dpc(&kq->kq_high, dpc); - else if (dpc->k_importance == KDPC_IMPORTANCE_LOW) - r = ntoskrnl_insert_dpc(&kq->kq_low, dpc); - else - r = ntoskrnl_insert_dpc(&kq->kq_med, dpc); - if (r == TRUE) - dpc->k_lock = &kq->kq_lock; - state = kq->kq_state; - mtx_unlock_spin(&kq->kq_lock); if (r == FALSE) return(r); - if (state == NDIS_PSTATE_SLEEPING) - KeSetEvent(&kq->kq_proc, 0, FALSE); + KeSetEvent(&kq->kq_proc, IO_NO_INCREMENT, FALSE); return(r); } uint8_t KeRemoveQueueDpc(dpc) kdpc *dpc; { + kdpc_queue *kq; + uint8_t irql; + if (dpc == NULL) return(FALSE); - if (dpc->k_lock == NULL) - return(FALSE); + irql = KeRaiseIrql(DISPATCH_LEVEL); - mtx_lock_spin((struct mtx*)(dpc->k_lock)); + kq = kq_queues + dpc->k_num; + + KeAcquireSpinLockAtDpcLevel(&kq->kq_lock); if (dpc->k_dpclistentry.nle_flink == &dpc->k_dpclistentry) { - mtx_unlock_spin((struct mtx*)(dpc->k_lock)); + KeReleaseSpinLockFromDpcLevel(&kq->kq_lock); + KeLowerIrql(irql); return(FALSE); } - REMOVE_LIST_ENTRY((&dpc->k_dpclistentry)); - INIT_LIST_HEAD((&dpc->k_dpclistentry)); - mtx_unlock_spin((struct mtx*)(dpc->k_lock)); + RemoveEntryList((&dpc->k_dpclistentry)); + InitializeListHead((&dpc->k_dpclistentry)); + KeReleaseSpinLock(&kq->kq_lock, irql); + return(TRUE); } void KeSetImportanceDpc(dpc, imp) kdpc *dpc; uint32_t imp; { if (imp != KDPC_IMPORTANCE_LOW && imp != KDPC_IMPORTANCE_MEDIUM && imp != KDPC_IMPORTANCE_HIGH) return; dpc->k_importance = (uint8_t)imp; return; } void KeSetTargetProcessorDpc(dpc, cpu) kdpc *dpc; uint8_t cpu; { if (cpu > mp_ncpus) return; dpc->k_num = cpu; return; } void KeFlushQueuedDpcs(void) { kdpc_queue *kq; int i; /* * Poke each DPC queue and wait * for them to drain. */ for (i = 0; i < mp_ncpus; i++) { kq = kq_queues + i; - KeSetEvent(&kq->kq_proc, 0, FALSE); - KeWaitForSingleObject((nt_dispatch_header *)&kq->kq_done, - 0, 0, TRUE, NULL); + KeSetEvent(&kq->kq_proc, IO_NO_INCREMENT, FALSE); + KeWaitForSingleObject(&kq->kq_done, 0, 0, TRUE, NULL); } return; } uint32_t KeGetCurrentProcessorNumber(void) { return((uint32_t)curthread->td_oncpu); } uint8_t KeSetTimerEx(timer, duetime, period, dpc) ktimer *timer; int64_t duetime; uint32_t period; kdpc *dpc; { struct timeval tv; uint64_t curtime; uint8_t pending; if (timer == NULL) return(FALSE); mtx_lock(&ntoskrnl_dispatchlock); if (timer->k_header.dh_inserted == TRUE) { - untimeout(ntoskrnl_timercall, timer, timer->k_handle); + ntoskrnl_remove_timer(timer); +#ifdef NTOSKRNL_DEBUG_TIMERS + ntoskrnl_timer_cancels++; +#endif timer->k_header.dh_inserted = FALSE; pending = TRUE; } else pending = FALSE; timer->k_duetime = duetime; timer->k_period = period; timer->k_header.dh_sigstate = FALSE; timer->k_dpc = dpc; if (duetime < 0) { tv.tv_sec = - (duetime) / 10000000; tv.tv_usec = (- (duetime) / 10) - (tv.tv_sec * 1000000); } else { ntoskrnl_time(&curtime); if (duetime < curtime) tv.tv_sec = tv.tv_usec = 0; else { tv.tv_sec = ((duetime) - curtime) / 10000000; tv.tv_usec = ((duetime) - curtime) / 10 - (tv.tv_sec * 1000000); } } timer->k_header.dh_inserted = TRUE; - timer->k_handle = timeout(ntoskrnl_timercall, timer, tvtohz(&tv)); + ntoskrnl_insert_timer(timer, tvtohz(&tv)); +#ifdef NTOSKRNL_DEBUG_TIMERS + ntoskrnl_timer_sets++; +#endif mtx_unlock(&ntoskrnl_dispatchlock); return(pending); } uint8_t KeSetTimer(timer, duetime, dpc) ktimer *timer; int64_t duetime; kdpc *dpc; { return (KeSetTimerEx(timer, duetime, 0, dpc)); } +/* + * The Windows DDK documentation seems to say that cancelling + * a timer that has a DPC will result in the DPC also being + * cancelled, but this isn't really the case. + */ + uint8_t KeCancelTimer(timer) ktimer *timer; { uint8_t pending; if (timer == NULL) return(FALSE); mtx_lock(&ntoskrnl_dispatchlock); + pending = timer->k_header.dh_inserted; + if (timer->k_header.dh_inserted == TRUE) { - untimeout(ntoskrnl_timercall, timer, timer->k_handle); - if (timer->k_dpc != NULL) - KeRemoveQueueDpc(timer->k_dpc); timer->k_header.dh_inserted = FALSE; - pending = TRUE; - } else - pending = KeRemoveQueueDpc(timer->k_dpc); + ntoskrnl_remove_timer(timer); +#ifdef NTOSKRNL_DEBUG_TIMERS + ntoskrnl_timer_cancels++; +#endif + } mtx_unlock(&ntoskrnl_dispatchlock); return(pending); } uint8_t KeReadStateTimer(timer) ktimer *timer; { return(timer->k_header.dh_sigstate); } static void dummy() { printf ("ntoskrnl dummy called...\n"); return; } image_patch_table ntoskrnl_functbl[] = { IMPORT_SFUNC(RtlCompareMemory, 3), IMPORT_SFUNC(RtlEqualUnicodeString, 3), IMPORT_SFUNC(RtlCopyUnicodeString, 2), IMPORT_SFUNC(RtlUnicodeStringToAnsiString, 3), IMPORT_SFUNC(RtlAnsiStringToUnicodeString, 3), IMPORT_SFUNC(RtlInitAnsiString, 2), IMPORT_SFUNC_MAP(RtlInitString, RtlInitAnsiString, 2), IMPORT_SFUNC(RtlInitUnicodeString, 2), IMPORT_SFUNC(RtlFreeAnsiString, 1), IMPORT_SFUNC(RtlFreeUnicodeString, 1), IMPORT_SFUNC(RtlUnicodeStringToInteger, 3), IMPORT_CFUNC(sprintf, 0), IMPORT_CFUNC(vsprintf, 0), IMPORT_CFUNC_MAP(_snprintf, snprintf, 0), IMPORT_CFUNC_MAP(_vsnprintf, vsnprintf, 0), IMPORT_CFUNC(DbgPrint, 0), IMPORT_SFUNC(DbgBreakPoint, 0), IMPORT_CFUNC(strncmp, 0), IMPORT_CFUNC(strcmp, 0), IMPORT_CFUNC(strncpy, 0), IMPORT_CFUNC(strcpy, 0), IMPORT_CFUNC(strlen, 0), + IMPORT_CFUNC_MAP(toupper, ntoskrnl_toupper, 0), + IMPORT_CFUNC_MAP(tolower, ntoskrnl_tolower, 0), IMPORT_CFUNC_MAP(strstr, ntoskrnl_strstr, 0), IMPORT_CFUNC_MAP(strchr, index, 0), IMPORT_CFUNC(memcpy, 0), IMPORT_CFUNC_MAP(memmove, ntoskrnl_memset, 0), IMPORT_CFUNC_MAP(memset, ntoskrnl_memset, 0), IMPORT_SFUNC(IoAllocateDriverObjectExtension, 4), IMPORT_SFUNC(IoGetDriverObjectExtension, 2), IMPORT_FFUNC(IofCallDriver, 2), IMPORT_FFUNC(IofCompleteRequest, 2), IMPORT_SFUNC(IoAcquireCancelSpinLock, 1), IMPORT_SFUNC(IoReleaseCancelSpinLock, 1), IMPORT_SFUNC(IoCancelIrp, 1), + IMPORT_SFUNC(IoConnectInterrupt, 11), + IMPORT_SFUNC(IoDisconnectInterrupt, 1), IMPORT_SFUNC(IoCreateDevice, 7), IMPORT_SFUNC(IoDeleteDevice, 1), IMPORT_SFUNC(IoGetAttachedDevice, 1), IMPORT_SFUNC(IoAttachDeviceToDeviceStack, 2), IMPORT_SFUNC(IoDetachDevice, 1), IMPORT_SFUNC(IoBuildSynchronousFsdRequest, 7), IMPORT_SFUNC(IoBuildAsynchronousFsdRequest, 6), IMPORT_SFUNC(IoBuildDeviceIoControlRequest, 9), IMPORT_SFUNC(IoAllocateIrp, 2), IMPORT_SFUNC(IoReuseIrp, 2), IMPORT_SFUNC(IoMakeAssociatedIrp, 2), IMPORT_SFUNC(IoFreeIrp, 1), IMPORT_SFUNC(IoInitializeIrp, 3), + IMPORT_SFUNC(KeAcquireInterruptSpinLock, 1), + IMPORT_SFUNC(KeReleaseInterruptSpinLock, 2), + IMPORT_SFUNC(KeSynchronizeExecution, 3), IMPORT_SFUNC(KeWaitForSingleObject, 5), IMPORT_SFUNC(KeWaitForMultipleObjects, 8), IMPORT_SFUNC(_allmul, 4), IMPORT_SFUNC(_alldiv, 4), IMPORT_SFUNC(_allrem, 4), IMPORT_RFUNC(_allshr, 0), IMPORT_RFUNC(_allshl, 0), IMPORT_SFUNC(_aullmul, 4), IMPORT_SFUNC(_aulldiv, 4), IMPORT_SFUNC(_aullrem, 4), IMPORT_RFUNC(_aullshr, 0), IMPORT_RFUNC(_aullshl, 0), IMPORT_CFUNC(atoi, 0), IMPORT_CFUNC(atol, 0), IMPORT_CFUNC(rand, 0), IMPORT_CFUNC(srand, 0), IMPORT_SFUNC(WRITE_REGISTER_USHORT, 2), IMPORT_SFUNC(READ_REGISTER_USHORT, 1), IMPORT_SFUNC(WRITE_REGISTER_ULONG, 2), IMPORT_SFUNC(READ_REGISTER_ULONG, 1), IMPORT_SFUNC(READ_REGISTER_UCHAR, 1), IMPORT_SFUNC(WRITE_REGISTER_UCHAR, 2), IMPORT_SFUNC(ExInitializePagedLookasideList, 7), IMPORT_SFUNC(ExDeletePagedLookasideList, 1), IMPORT_SFUNC(ExInitializeNPagedLookasideList, 7), IMPORT_SFUNC(ExDeleteNPagedLookasideList, 1), IMPORT_FFUNC(InterlockedPopEntrySList, 1), IMPORT_FFUNC(InterlockedPushEntrySList, 2), IMPORT_SFUNC(ExQueryDepthSList, 1), IMPORT_FFUNC_MAP(ExpInterlockedPopEntrySList, InterlockedPopEntrySList, 1), IMPORT_FFUNC_MAP(ExpInterlockedPushEntrySList, InterlockedPushEntrySList, 2), IMPORT_FFUNC(ExInterlockedPopEntrySList, 2), IMPORT_FFUNC(ExInterlockedPushEntrySList, 3), IMPORT_SFUNC(ExAllocatePoolWithTag, 3), IMPORT_SFUNC(ExFreePool, 1), #ifdef __i386__ IMPORT_FFUNC(KefAcquireSpinLockAtDpcLevel, 1), IMPORT_FFUNC(KefReleaseSpinLockFromDpcLevel,1), IMPORT_FFUNC(KeAcquireSpinLockRaiseToDpc, 1), #else /* * For AMD64, we can get away with just mapping * KeAcquireSpinLockRaiseToDpc() directly to KfAcquireSpinLock() * because the calling conventions end up being the same. * On i386, we have to be careful because KfAcquireSpinLock() * is _fastcall but KeAcquireSpinLockRaiseToDpc() isn't. */ IMPORT_SFUNC(KeAcquireSpinLockAtDpcLevel, 1), IMPORT_SFUNC(KeReleaseSpinLockFromDpcLevel, 1), IMPORT_SFUNC_MAP(KeAcquireSpinLockRaiseToDpc, KfAcquireSpinLock, 1), #endif IMPORT_SFUNC_MAP(KeReleaseSpinLock, KfReleaseSpinLock, 1), IMPORT_FFUNC(InterlockedIncrement, 1), IMPORT_FFUNC(InterlockedDecrement, 1), IMPORT_FFUNC(InterlockedExchange, 2), IMPORT_FFUNC(ExInterlockedAddLargeStatistic, 2), IMPORT_SFUNC(IoAllocateMdl, 5), IMPORT_SFUNC(IoFreeMdl, 1), IMPORT_SFUNC(MmSizeOfMdl, 1), IMPORT_SFUNC(MmMapLockedPages, 2), IMPORT_SFUNC(MmMapLockedPagesSpecifyCache, 6), IMPORT_SFUNC(MmUnmapLockedPages, 2), IMPORT_SFUNC(MmBuildMdlForNonPagedPool, 1), IMPORT_SFUNC(MmIsAddressValid, 1), IMPORT_SFUNC(KeInitializeSpinLock, 1), IMPORT_SFUNC(IoIsWdmVersionAvailable, 2), IMPORT_SFUNC(IoGetDeviceProperty, 5), IMPORT_SFUNC(IoAllocateWorkItem, 1), IMPORT_SFUNC(IoFreeWorkItem, 1), IMPORT_SFUNC(IoQueueWorkItem, 4), IMPORT_SFUNC(ExQueueWorkItem, 2), IMPORT_SFUNC(ntoskrnl_workitem, 2), IMPORT_SFUNC(KeInitializeMutex, 2), IMPORT_SFUNC(KeReleaseMutex, 2), IMPORT_SFUNC(KeReadStateMutex, 1), IMPORT_SFUNC(KeInitializeEvent, 3), IMPORT_SFUNC(KeSetEvent, 3), IMPORT_SFUNC(KeResetEvent, 1), IMPORT_SFUNC(KeClearEvent, 1), IMPORT_SFUNC(KeReadStateEvent, 1), IMPORT_SFUNC(KeInitializeTimer, 1), IMPORT_SFUNC(KeInitializeTimerEx, 2), IMPORT_SFUNC(KeSetTimer, 3), IMPORT_SFUNC(KeSetTimerEx, 4), IMPORT_SFUNC(KeCancelTimer, 1), IMPORT_SFUNC(KeReadStateTimer, 1), IMPORT_SFUNC(KeInitializeDpc, 3), IMPORT_SFUNC(KeInsertQueueDpc, 3), IMPORT_SFUNC(KeRemoveQueueDpc, 1), IMPORT_SFUNC(KeSetImportanceDpc, 2), IMPORT_SFUNC(KeSetTargetProcessorDpc, 2), IMPORT_SFUNC(KeFlushQueuedDpcs, 0), IMPORT_SFUNC(KeGetCurrentProcessorNumber, 1), IMPORT_SFUNC(ObReferenceObjectByHandle, 6), IMPORT_FFUNC(ObfDereferenceObject, 1), IMPORT_SFUNC(ZwClose, 1), IMPORT_SFUNC(PsCreateSystemThread, 7), IMPORT_SFUNC(PsTerminateSystemThread, 1), /* * 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/5/sys/compat/ndis/winx32_wrap.S =================================================================== --- stable/5/sys/compat/ndis/winx32_wrap.S (revision 151520) +++ stable/5/sys/compat/ndis/winx32_wrap.S (revision 151521) @@ -1,356 +1,372 @@ /*- * Copyright (c) 2005 * 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$ */ #include /* * This file contains assembly language wrappers for the different * calling conventions supported by Windows on the i386 architecture. * In FreeBSD, the whole OS typically use same C calling convention * everywhere, namely _cdecl. Windows, on the other hand, uses several * different C calling conventions depending on the circumstances: * * _stdcall: Used for most ordinary Windows APIs. With _stdcall, * arguments are passed on the stack, and the callee unwinds the stack * before returning control to the caller. Not suitable for variadic * functions. * * _fastcall: Used for some APIs that may be invoked frequently and * where speed is a critical factor (e.g. KeAcquireSpinLock() and * KeReleaseSpinLock()) Similar to _stdcall, except the first 2 32-bit * or smaller arguments are passed in the %ecx and %edx registers * instead of on the stack. Not suitable for variadic functions. * * _cdecl: Used for standard C library routines and for variadic * functions. * * _regparm(3): Used for certain assembly routines. All arguments * passed in %eax, %ecx and %edx. * * Furthermore, there is an additional wrinkle that's not obvious * with all code: Microsoft supports the use of exceptions in C * (__try/__except) both in user _and_ kernel mode. Sadly, Windows * structured exception handling uses machine-specific features * that conflict rather badly with FreeBSD. (See utility routines * at the end of this module for more details.) * * We want to support these calling conventions in as portable a manner * as possible. The trick is doing it not only with different versions * of GNU C, but with compilers other than GNU C (e.g. the Solaris * SunOne C compiler). The only sure fire method is with assembly * language trampoline code which both fixes up the argument passing, * stack unwinding and exception/thread context all at once. * * You'll notice that we call the thunk/unthunk routines in the * *_wrap() functions in an awkward way. Rather than branching * directly to the address, we load the address into a register * first as a literal value, then we branch to it. This is done * to insure that the assembler doesn't translate the branch into * a relative branch. We use the *_wrap() routines here as templates * and create the actual trampolines at run time, at which point * we only know the absolute addresses of the thunk and unthunk * routines. So we need to make sure the templates have enough * room in them for the full address. + * + * Also note that when we call the a thunk/unthunk routine after + * invoking a wrapped function, we have to make sure to preserve + * the value returned from that function. Most functions return + * a 32-bit value in %eax, however some routines return 64-bit + * values, which span both %eax and %edx. Consequently, we have + * to preserve both registers. */ /* * Handle _stdcall going from Windows to UNIX. * This is frustrating, because to do it right you have to * know how many arguments the called function takes, and there's * no way to figure this out on the fly: you just have to be told * ahead of time. We assume there will be 16 arguments. I don't * think there are any Windows APIs that require this many. */ .globl x86_stdcall_wrap_call .globl x86_stdcall_wrap_arg .globl x86_stdcall_wrap_end ENTRY(x86_stdcall_wrap) push %esi push %edi sub $64,%esp mov %esp,%esi add $64+8+4,%esi mov %esp,%edi mov $16,%ecx # handle up to 16 args rep movsl movl $ctxsw_wtou, %eax call *%eax # unthunk x86_stdcall_wrap_call: movl $0,%eax call *%eax # jump to routine - mov %eax,%esi # preserve return val + push %eax # preserve return val + push %edx movl $ctxsw_utow, %eax call *%eax # thunk + pop %edx + pop %eax # restore return val + add $64,%esp # clean the stack - mov %esi,%eax # restore return val pop %edi pop %esi x86_stdcall_wrap_arg: ret $0xFF x86_stdcall_wrap_end: /* * Handle _stdcall going from UNIX to Windows. This routine * expects to be passed the function to be called, number of * args and the arguments for the Windows function on the stack. */ ENTRY(x86_stdcall_call) push %esi # must preserve %esi push %edi # and %edi mov 16(%esp),%eax # get arg cnt mov %eax,%ecx # save as copy count mov %esp,%esi # Set source address register to point to add $20,%esi # first agument to be forwarded. shl $2,%eax # turn arg cnt into offset sub %eax,%esp # shift stack to new location mov %esp,%edi # store dest copy addr rep # do the copy movsl call ctxsw_utow # thunk call *12(%edi) # branch to stdcall routine - mov %eax,%esi # preserve return val + push %eax # preserve return val + push %edx call ctxsw_wtou # unthunk + pop %edx + pop %eax # restore return val mov %edi,%esp # restore stack - mov %esi,%eax # restore return val pop %edi # restore %edi pop %esi # and %esi ret /* * Fastcall support. Similar to _stdcall, except the first * two arguments are passed in %ecx and %edx. It happens we * only support a small number of _fastcall APIs, none of them * take more than three arguments. So to keep the code size * and complexity down, we only handle 3 arguments here. */ /* Call _fastcall function going from Windows to UNIX. */ .globl x86_fastcall_wrap_call .globl x86_fastcall_wrap_arg .globl x86_fastcall_wrap_end ENTRY(x86_fastcall_wrap) mov 4(%esp),%eax push %eax push %edx push %ecx movl $ctxsw_wtou, %eax call *%eax # unthunk x86_fastcall_wrap_call: mov $0,%eax call *%eax # branch to fastcall routine push %eax # preserve return val + push %edx movl $ctxsw_utow, %eax call *%eax # thunk + pop %edx pop %eax # restore return val add $12,%esp # clean the stack x86_fastcall_wrap_arg: ret $0xFF x86_fastcall_wrap_end: /* * Call _fastcall function going from UNIX to Windows. * This routine isn't normally used since NDIS miniport drivers * only have _stdcall entry points, but it's provided anyway * to round out the API, and for testing purposes. */ ENTRY(x86_fastcall_call) mov 4(%esp),%eax push 16(%esp) call ctxsw_utow # thunk mov 12(%esp),%ecx mov 16(%esp),%edx call *8(%esp) # branch to fastcall routine push %eax # preserve return val + push %edx call ctxsw_wtou # unthunk + pop %edx pop %eax # restore return val add $4,%esp # clean the stack ret /* * Call regparm(3) function going from Windows to UNIX. Arguments * are passed in %eax, %edx and %ecx. Note that while additional * arguments are passed on the stack, we never bother when them, * since the only regparm(3) routines we need to wrap never take * more than 3 arguments. */ .globl x86_regparm_wrap_call .globl x86_regparm_wrap_end ENTRY(x86_regparm_wrap) push %ecx push %edx push %eax movl $ctxsw_wtou, %eax call *%eax # unthunk x86_regparm_wrap_call: movl $0,%eax call *%eax # jump to routine push %eax # preserve return val push %edx # preserve return val movl $ctxsw_utow, %eax call *%eax # thunk pop %edx # restore return val pop %eax # restore return val add $12,%esp # restore stack ret x86_regparm_wrap_end: /* * Call regparm(3) function going from UNIX to Windows. * This routine isn't normally used since NDIS miniport drivers * only have _stdcall entry points, but it's provided anyway * to round out the API, and for testing purposes. */ ENTRY(x86_regparm_call) call ctxsw_utow # thunk mov 8(%esp),%eax mov 12(%esp),%edx mov 16(%esp),%ecx call *4(%esp) # branch to fastcall routine push %eax # preserve return val push %edx # preserve return val call ctxsw_wtou # unthunk pop %edx # restore return val pop %eax # restore return val ret /* * Ugly hack alert: * * On Win32/i386, using __try/__except results in code that tries to * manipulate what's supposed to be the Windows Threada Environment * Block (TEB), which one accesses via the %fs register. In particular, * %fs:0 (the first DWORD in the TEB) points to the exception * registration list. Unfortunately, FreeBSD uses %fs for the * per-cpu data structure (pcpu), and we can't allow Windows code * to muck with that. I don't even know what Solaris uses %fs for * (or if it even uses it at all). * * Even worse, in 32-bit protected mode, %fs is a selector that * refers to an entry in either the GDT or the LDT. Ideally, we would * like to be able to temporarily point it at another descriptor * while Windows code executes, but to do that we need a separate * descriptor entry of our own to play with. * * Therefore, we go to some trouble to learn the existing layout of * the GDT and update it to include an extra entry that we can use. * We need the following utility routines to help us do that. On * FreeBSD, index #7 in the GDT happens to be unused, so we turn * this into our own data segment descriptor. It would be better * if we could use a private LDT entry, but there's no easy way to * do that in SMP mode because of the way FreeBSD handles user LDTs. * * Once we have a custom descriptor, we have to thunk/unthunk whenever * we cross between FreeBSD code and Windows code. The thunking is * based on the premise that when executing instructions in the * Windows binary itself, we won't go to sleep. This is because in * order to yield the CPU, the code has to call back out to a FreeBSD * routine first, and when that happens we can unthunk in order to * restore FreeBSD context. What we're desperately trying to avoid is * being involuntarily pre-empted with the %fs register still pointing * to our fake TIB: if FreeBSD code runs with %fs pointing at our * Windows TIB instead of pcpu, we'll panic the kernel. Fortunately, * the only way involuntary preemption can occur is if an interrupt * fires, and the trap handler saves/restores %fs for us. * * The thunking routines themselves, ctxsw_utow() (Context SWitch UNIX * to Windows) and ctxsw_wtou() (Context SWitch Windows to UNIX), are * external to this module. This is done simply because it's easier * to manipulate data structures in C rather than assembly. */ ENTRY(x86_getldt) movl 4(%esp),%eax sgdtl (%eax) movl 8(%esp),%eax sldt (%eax) xor %eax,%eax ret ENTRY(x86_setldt) movl 4(%esp),%eax lgdt (%eax) jmp 1f nop 1: movl 8(%esp),%eax lldt %ax xor %eax,%eax ret ENTRY(x86_getfs) mov %fs,%ax ret ENTRY(x86_setfs) movl 4(%esp),%fs ret ENTRY(x86_gettid) mov %fs:12,%eax ret Index: stable/5/sys/dev/if_ndis/if_ndis.c =================================================================== --- stable/5/sys/dev/if_ndis/if_ndis.c (revision 151520) +++ stable/5/sys/dev/if_ndis/if_ndis.c (revision 151521) @@ -1,2530 +1,3408 @@ /*- * 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 "opt_bdg.h" - #include #include #include #include #include #include #include #include #include #include #if __FreeBSD_version < 502113 #include #endif + #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 MODULE_DEPEND(ndis, ether, 1, 1, 1); MODULE_DEPEND(ndis, wlan, 1, 1, 1); MODULE_DEPEND(ndis, ndisapi, 1, 1, 1); MODULE_VERSION(ndis, 1); int ndis_attach (device_t); int ndis_detach (device_t); int ndis_suspend (device_t); int ndis_resume (device_t); void ndis_shutdown (device_t); int ndisdrv_modevent (module_t, int, void *); static void ndis_txeof (ndis_handle, ndis_packet *, ndis_status); static void ndis_rxeof (ndis_handle, ndis_packet **, uint32_t); static void ndis_rxeof_eth (ndis_handle, ndis_handle, char *, void *, uint32_t, void *, uint32_t, uint32_t); static void ndis_rxeof_done (ndis_handle); static void ndis_rxeof_xfr (kdpc *, ndis_handle, void *, void *); static void ndis_rxeof_xfr_done (ndis_handle, ndis_packet *, uint32_t, uint32_t); static void ndis_linksts (ndis_handle, ndis_status, void *, uint32_t); static void ndis_linksts_done (ndis_handle); /* We need to wrap these functions for amd64. */ static funcptr ndis_txeof_wrap; static funcptr ndis_rxeof_wrap; static funcptr ndis_rxeof_eth_wrap; static funcptr ndis_rxeof_done_wrap; static funcptr ndis_rxeof_xfr_wrap; static funcptr ndis_rxeof_xfr_done_wrap; static funcptr ndis_linksts_wrap; static funcptr ndis_linksts_done_wrap; static funcptr ndis_ticktask_wrap; static funcptr ndis_starttask_wrap; static funcptr ndis_resettask_wrap; +static funcptr ndis_inputtask_wrap; -static void ndis_intr (void *); static void ndis_tick (void *); -static void ndis_ticktask (ndis_work_item *, void *); +static void ndis_ticktask (device_object *, void *); static void ndis_start (struct ifnet *); -static void ndis_starttask (ndis_work_item *, void *); -static void ndis_resettask (ndis_work_item *, void *); +static void ndis_starttask (device_object *, void *); +static void ndis_resettask (device_object *, void *); +static void ndis_inputtask (device_object *, void *); static int ndis_ioctl (struct ifnet *, u_long, caddr_t); static int ndis_wi_ioctl_get (struct ifnet *, u_long, caddr_t); static int ndis_wi_ioctl_set (struct ifnet *, u_long, caddr_t); +static int ndis_80211_ioctl_get (struct ifnet *, u_long, caddr_t); +static int ndis_80211_ioctl_set (struct ifnet *, u_long, caddr_t); static void ndis_init (void *); static void ndis_stop (struct ndis_softc *); static void ndis_watchdog (struct ifnet *); static int ndis_ifmedia_upd (struct ifnet *); static void ndis_ifmedia_sts (struct ifnet *, struct ifmediareq *); 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 *); +#ifdef IEEE80211_F_WPA +static int ndis_set_cipher (struct ndis_softc *, int); +static int ndis_set_wpa (struct ndis_softc *); +static int ndis_add_key (struct ieee80211com *, + const struct ieee80211_key *, const u_int8_t []); +static int ndis_del_key (struct ieee80211com *, + const struct ieee80211_key *); +#endif static void ndis_media_status (struct ifnet *, struct ifmediareq *); static void ndis_setmulti (struct ndis_softc *); static void ndis_map_sclist (void *, bus_dma_segment_t *, int, bus_size_t, int); static int ndisdrv_loaded = 0; /* * This routine should call windrv_load() once for each driver * image. This will do the relocation and dynalinking for the * image, and create a Windows driver object which will be * saved in our driver database. */ int ndisdrv_modevent(mod, cmd, arg) module_t mod; int cmd; void *arg; { int error = 0; switch (cmd) { case MOD_LOAD: ndisdrv_loaded++; if (ndisdrv_loaded > 1) break; windrv_wrap((funcptr)ndis_rxeof, &ndis_rxeof_wrap, 3, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_rxeof_eth, &ndis_rxeof_eth_wrap, 8, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_rxeof_done, &ndis_rxeof_done_wrap, 1, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_rxeof_xfr, &ndis_rxeof_xfr_wrap, 4, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_rxeof_xfr_done, &ndis_rxeof_xfr_done_wrap, 4, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_txeof, &ndis_txeof_wrap, 3, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_linksts, &ndis_linksts_wrap, 4, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_linksts_done, &ndis_linksts_done_wrap, 1, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_ticktask, &ndis_ticktask_wrap, 2, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_starttask, &ndis_starttask_wrap, 2, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)ndis_resettask, &ndis_resettask_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)ndis_inputtask, &ndis_inputtask_wrap, + 2, WINDRV_WRAP_STDCALL); break; case MOD_UNLOAD: ndisdrv_loaded--; if (ndisdrv_loaded > 0) break; /* fallthrough */ case MOD_SHUTDOWN: windrv_unwrap(ndis_rxeof_wrap); windrv_unwrap(ndis_rxeof_eth_wrap); windrv_unwrap(ndis_rxeof_done_wrap); windrv_unwrap(ndis_rxeof_xfr_wrap); windrv_unwrap(ndis_rxeof_xfr_done_wrap); windrv_unwrap(ndis_txeof_wrap); windrv_unwrap(ndis_linksts_wrap); windrv_unwrap(ndis_linksts_done_wrap); windrv_unwrap(ndis_ticktask_wrap); windrv_unwrap(ndis_starttask_wrap); windrv_unwrap(ndis_resettask_wrap); + windrv_unwrap(ndis_inputtask_wrap); break; default: error = EINVAL; break; } return (error); } /* * Program the 64-bit multicast hash filter. */ static void ndis_setmulti(sc) struct ndis_softc *sc; { struct ifnet *ifp; struct ifmultiaddr *ifma; int len, mclistsz, error; uint8_t *mclist; - ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; if (!NDIS_INITIALIZED(sc)) return; if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; len = sizeof(sc->ndis_filter); error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, &sc->ndis_filter, &len); if (error) device_printf (sc->ndis_dev, - "set filter failed: %d\n", error); + "set allmulti failed: %d\n", error); return; } if (TAILQ_EMPTY(&ifp->if_multiaddrs)) return; len = sizeof(mclistsz); ndis_get_info(sc, OID_802_3_MAXIMUM_LIST_SIZE, &mclistsz, &len); mclist = malloc(ETHER_ADDR_LEN * mclistsz, M_TEMP, M_NOWAIT|M_ZERO); if (mclist == NULL) { sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; goto out; } sc->ndis_filter |= NDIS_PACKET_TYPE_MULTICAST; len = 0; + IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), mclist + (ETHER_ADDR_LEN * len), ETHER_ADDR_LEN); len++; if (len > mclistsz) { + IF_ADDR_UNLOCK(ifp); sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; sc->ndis_filter &= ~NDIS_PACKET_TYPE_MULTICAST; goto out; } } + IF_ADDR_UNLOCK(ifp); len = len * ETHER_ADDR_LEN; error = ndis_set_info(sc, OID_802_3_MULTICAST_LIST, mclist, &len); if (error) { device_printf (sc->ndis_dev, "set mclist failed: %d\n", error); sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; sc->ndis_filter &= ~NDIS_PACKET_TYPE_MULTICAST; } out: free(mclist, M_TEMP); len = sizeof(sc->ndis_filter); error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, &sc->ndis_filter, &len); if (error) - device_printf (sc->ndis_dev, "set filter failed: %d\n", error); + device_printf (sc->ndis_dev, "set multi failed: %d\n", error); return; } static int ndis_set_offload(sc) struct ndis_softc *sc; { ndis_task_offload *nto; ndis_task_offload_hdr *ntoh; ndis_task_tcpip_csum *nttc; struct ifnet *ifp; int len, error; - ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; if (!NDIS_INITIALIZED(sc)) return(EINVAL); /* See if there's anything to set. */ error = ndis_probe_offload(sc); if (error) return(error); if (sc->ndis_hwassist == 0 && ifp->if_capabilities == 0) return(0); len = sizeof(ndis_task_offload_hdr) + sizeof(ndis_task_offload) + sizeof(ndis_task_tcpip_csum); ntoh = malloc(len, M_TEMP, M_NOWAIT|M_ZERO); if (ntoh == NULL) return(ENOMEM); ntoh->ntoh_vers = NDIS_TASK_OFFLOAD_VERSION; ntoh->ntoh_len = sizeof(ndis_task_offload_hdr); ntoh->ntoh_offset_firsttask = sizeof(ndis_task_offload_hdr); ntoh->ntoh_encapfmt.nef_encaphdrlen = sizeof(struct ether_header); ntoh->ntoh_encapfmt.nef_encap = NDIS_ENCAP_IEEE802_3; ntoh->ntoh_encapfmt.nef_flags = NDIS_ENCAPFLAG_FIXEDHDRLEN; nto = (ndis_task_offload *)((char *)ntoh + ntoh->ntoh_offset_firsttask); nto->nto_vers = NDIS_TASK_OFFLOAD_VERSION; nto->nto_len = sizeof(ndis_task_offload); nto->nto_task = NDIS_TASK_TCPIP_CSUM; nto->nto_offset_nexttask = 0; nto->nto_taskbuflen = sizeof(ndis_task_tcpip_csum); nttc = (ndis_task_tcpip_csum *)nto->nto_taskbuf; if (ifp->if_capenable & IFCAP_TXCSUM) nttc->nttc_v4tx = sc->ndis_v4tx; if (ifp->if_capenable & IFCAP_RXCSUM) nttc->nttc_v4rx = sc->ndis_v4rx; error = ndis_set_info(sc, OID_TCP_TASK_OFFLOAD, ntoh, &len); free(ntoh, M_TEMP); return(error); } static int ndis_probe_offload(sc) struct ndis_softc *sc; { ndis_task_offload *nto; ndis_task_offload_hdr *ntoh; ndis_task_tcpip_csum *nttc = NULL; struct ifnet *ifp; int len, error, dummy; - ifp = &sc->arpcom.ac_if; + 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); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ int ndis_attach(dev) device_t dev; { u_char eaddr[ETHER_ADDR_LEN]; struct ndis_softc *sc; driver_object *pdrv; device_object *pdo; struct ifnet *ifp = NULL; int error = 0, len; int i; sc = device_get_softc(dev); - - mtx_init(&sc->ndis_mtx, "ndis softc lock", - MTX_NETWORK_LOCK, MTX_DEF); - - /* - * Hook interrupt early, since calling the driver's - * init routine may trigger an interrupt. Note that - * we don't need to do any explicit interrupt setup - * for USB. - */ - - if (sc->ndis_iftype == PCMCIABus || sc->ndis_iftype == PCIBus) { - error = bus_setup_intr(dev, sc->ndis_irq, - INTR_TYPE_NET | INTR_MPSAFE, - ndis_intr, sc, &sc->ndis_intrhand); - - if (error) { - device_printf(dev, "couldn't set up irq\n"); - goto fail; - } +#if __FreeBSD_version < 600000 + sc->ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; +#else + ifp = sc->ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + error = ENOSPC; + goto fail; } +#endif + ifp->if_softc = sc; + KeInitializeSpinLock(&sc->ndis_spinlock); + KeInitializeSpinLock(&sc->ndis_rxlock); + InitializeListHead(&sc->ndis_shlist); + if (sc->ndis_iftype == PCMCIABus) { error = ndis_alloc_amem(sc); if (error) { device_printf(dev, "failed to allocate " "attribute memory\n"); goto fail; } } #if __FreeBSD_version < 502113 sysctl_ctx_init(&sc->ndis_ctx); #endif /* 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; /* Call driver's init routine. */ if (ndis_init_nic(sc)) { device_printf (dev, "init handler failed\n"); error = ENXIO; goto fail; } /* * Get station address from the driver. */ len = sizeof(eaddr); ndis_get_info(sc, OID_802_3_CURRENT_ADDRESS, &eaddr, &len); - bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); - /* - * Figure out if we're allowed to use multipacket sends - * with this driver, and if so, how many. + * Figure out how big to make the TX buffer pool. */ - if (sc->ndis_chars->nmc_sendsingle_func && - sc->ndis_chars->nmc_sendmulti_func == NULL) { - sc->ndis_maxpkts = 1; - } else { - len = sizeof(sc->ndis_maxpkts); - ndis_get_info(sc, OID_GEN_MAXIMUM_SEND_PACKETS, - &sc->ndis_maxpkts, &len); + 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); + NDIS_TXPKTS, 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); + NDIS_TXPKTS, PROTOCOL_RESERVED_SIZE_IN_PACKET); if (i != NDIS_STATUS_SUCCESS) { sc->ndis_txpool = NULL; device_printf(dev, "failed to allocate TX packet pool"); error = ENOMEM; goto fail; } sc->ndis_txpending = sc->ndis_maxpkts; sc->ndis_oidcnt = 0; /* Get supported oid list. */ ndis_get_supported_oids(sc, &sc->ndis_oids, &sc->ndis_oidcnt); /* If the NDIS module requested scatter/gather, init maps. */ if (sc->ndis_sc) ndis_init_dma(sc); /* * See if the OID_802_11_CONFIGURATION OID is * supported by this driver. If it is, then this an 802.11 * wireless driver, and we should set up media for wireless. */ for (i = 0; i < sc->ndis_oidcnt; i++) { if (sc->ndis_oids[i] == OID_802_11_CONFIGURATION) { sc->ndis_80211++; break; } } /* Check for task offload support. */ ndis_probe_offload(sc); - ifp = &sc->arpcom.ac_if; - ifp->if_softc = sc; +#if __FreeBSD_version < 502109 + /* + * An NDIS device was detected. Inform the world. + */ + device_printf(dev, "%s address: %6D\n", + sc->ndis_80211 ? "802.11" : "Ethernet", eaddr, ":"); +#endif + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = ndis_ioctl; ifp->if_start = ndis_start; ifp->if_watchdog = ndis_watchdog; ifp->if_init = ndis_init; ifp->if_baudrate = 10000000; #if __FreeBSD_version < 502114 ifp->if_snd.ifq_maxlen = 50; #else IFQ_SET_MAXLEN(&ifp->if_snd, 50); ifp->if_snd.ifq_drv_maxlen = 25; IFQ_SET_READY(&ifp->if_snd); #endif ifp->if_capenable = ifp->if_capabilities; ifp->if_hwassist = sc->ndis_hwassist; /* Do media setup */ if (sc->ndis_80211) { - struct ieee80211com *ic = (void *)ifp; + struct ieee80211com *ic = (void *)&sc->ic; ndis_80211_rates_ex rates; struct ndis_80211_nettype_list *ntl; uint32_t arg; int r; +#if __FreeBSD_version >= 600000 + ic->ic_ifp = ifp; +#endif ic->ic_phytype = IEEE80211_T_DS; ic->ic_opmode = IEEE80211_M_STA; ic->ic_caps = IEEE80211_C_IBSS; ic->ic_state = IEEE80211_S_ASSOC; ic->ic_modecaps = (1<ntl_items; i++) { switch (ntl->ntl_type[i]) { case NDIS_80211_NETTYPE_11FH: case NDIS_80211_NETTYPE_11DS: ic->ic_modecaps |= (1<ic_modecaps |= (1<ic_modecaps |= (1<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 (ic->ic_modecaps & (1<ic_sup_rates[IEEE80211_MODE_11A].rs_nrates = 0; if (ic->ic_modecaps & (1<ic_sup_rates[IEEE80211_MODE_11B].rs_nrates = 0; if (ic->ic_modecaps & (1<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 (!(ic->ic_modecaps & (1<ic_modecaps |= (1<ic_sup_rates[IEEE80211_MODE_11B]. rs_nrates = 0; } SETRATE(IEEE80211_MODE_11B, rates[i]); INCRATE(IEEE80211_MODE_11B); break; default: if (ic->ic_modecaps & (1<ic_modecaps & (1<ic_modecaps & (1<ic_modecaps & (1<ic_modecaps & (1<ic_sup_rates[IEEE80211_MODE_11G].rs_nrates) chanflag |= IEEE80211_CHAN_G; if (i <= 14) chanflag |= IEEE80211_CHAN_B; if (ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates && i > 14) chanflag = IEEE80211_CHAN_A; if (chanflag == 0) break; ic->ic_channels[i].ic_freq = ieee80211_ieee2mhz(i, chanflag); ic->ic_channels[i].ic_flags = chanflag; } + /* + * To test for WPA support, we need to see if we can + * set AUTHENTICATION_MODE to WPA and read it back + * successfully. + */ +#ifdef IEEE80211_F_WPA i = sizeof(arg); - r = ndis_get_info(sc, OID_802_11_WEP_STATUS, &arg, &i); - if (arg != NDIS_80211_WEPSTAT_NOTSUPPORTED) + 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_caps |= IEEE80211_C_WEP|IEEE80211_C_TKIP| + IEEE80211_C_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_caps |= IEEE80211_C_WEP|IEEE80211_C_TKIP; + goto got_crypto; + } +#endif + arg = NDIS_80211_WEPSTAT_ENC1ENABLED; + r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); + if (r == 0) ic->ic_caps |= IEEE80211_C_WEP; +#ifdef IEEE80211_F_WPA +got_crypto: +#endif i = sizeof(arg); r = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &i); if (r == 0) ic->ic_caps |= IEEE80211_C_PMGT; bcopy(eaddr, &ic->ic_myaddr, sizeof(eaddr)); +#if __FreeBSD_version < 600000 ieee80211_ifattach(ifp); ieee80211_media_init(ifp, ieee80211_media_change, ndis_media_status); +#else + ieee80211_ifattach(ic); + ieee80211_media_init(ic, ieee80211_media_change, + ndis_media_status); +#endif ic->ic_ibss_chan = IEEE80211_CHAN_ANYC; ic->ic_bss->ni_chan = ic->ic_ibss_chan; +#ifdef IEEE80211_F_WPA + /* install key handing routines */ + ic->ic_crypto.cs_key_set = ndis_add_key; + ic->ic_crypto.cs_key_delete = ndis_del_key; +#endif } else { ifmedia_init(&sc->ifmedia, IFM_IMASK, ndis_ifmedia_upd, ndis_ifmedia_sts); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_AUTO); ether_ifattach(ifp, eaddr); } /* 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. */ - NdisInitializeWorkItem(&sc->ndis_tickitem, - (ndis_proc)ndis_ticktask_wrap, sc); - NdisInitializeWorkItem(&sc->ndis_startitem, - (ndis_proc)ndis_starttask_wrap, ifp); - NdisInitializeWorkItem(&sc->ndis_resetitem, - (ndis_proc)ndis_resettask_wrap, sc); + sc->ndis_tickitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); + sc->ndis_startitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); + sc->ndis_resetitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); + sc->ndis_inputitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); KeInitializeDpc(&sc->ndis_rxdpc, ndis_rxeof_xfr_wrap, sc->ndis_block); fail: if (error) ndis_detach(dev); else /* We're done talking to the NIC for now; halt it. */ ndis_halt_nic(sc); return(error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ int ndis_detach(dev) device_t dev; { struct ndis_softc *sc; struct ifnet *ifp; driver_object *drv; sc = device_get_softc(dev); - KASSERT(mtx_initialized(&sc->ndis_mtx), - ("ndis mutex not initialized")); NDIS_LOCK(sc); - ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; ifp->if_flags &= ~IFF_UP; if (device_is_attached(dev)) { NDIS_UNLOCK(sc); ndis_stop(sc); if (sc->ndis_80211) +#if __FreeBSD_version < 600000 ieee80211_ifdetach(ifp); +#else + ieee80211_ifdetach(&sc->ic); +#endif else 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); + bus_generic_detach(dev); - if (sc->ndis_intrhand) - bus_teardown_intr(dev, sc->ndis_irq, sc->ndis_intrhand); 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 __FreeBSD_version >= 600000 + if (ifp != NULL) + if_free(ifp); +#endif + 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); ndis_unload_driver(sc); /* Destroy the PDO for this device. */ if (sc->ndis_iftype == PCIBus) drv = windrv_lookup(0, "PCI Bus"); else if (sc->ndis_iftype == PCMCIABus) drv = windrv_lookup(0, "PCCARD Bus"); else drv = windrv_lookup(0, "USB Bus"); if (drv == NULL) panic("couldn't find driver object"); windrv_destroy_pdo(drv, dev); if (sc->ndis_iftype == PCIBus) bus_dma_tag_destroy(sc->ndis_parent_tag); #if __FreeBSD_version < 502113 sysctl_ctx_free(&sc->ndis_ctx); #endif - mtx_destroy(&sc->ndis_mtx); - return(0); } int ndis_suspend(dev) device_t dev; { struct ndis_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; + 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->arpcom.ac_if; + 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; uint32_t status; ndis_buffer *b; ndis_packet *p; struct mbuf *m; ndis_ethpriv *priv; block = adapter; m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { NdisFreePacket(p); return; } /* Save the data provided to us so far. */ m->m_len = lookaheadlen + hdrlen; m->m_pkthdr.len = pktlen + hdrlen; m->m_next = NULL; m_copyback(m, 0, hdrlen, hdr); m_copyback(m, hdrlen, lookaheadlen, lookahead); /* Now create a fake NDIS_PACKET to hold the data */ NdisAllocatePacket(&status, &p, block->nmb_rxpool); if (status != NDIS_STATUS_SUCCESS) { m_freem(m); return; } p->np_m0 = m; b = IoAllocateMdl(m->m_data, m->m_pkthdr.len, FALSE, FALSE, NULL); if (b == NULL) { NdisFreePacket(p); m_freem(m); return; } p->np_private.npp_head = p->np_private.npp_tail = b; p->np_private.npp_totlen = m->m_pkthdr.len; /* Save the packet RX context somewhere. */ priv = (ndis_ethpriv *)&p->np_protocolreserved; priv->nep_ctx = ctx; KeAcquireSpinLock(&block->nmb_lock, &irql); - INSERT_LIST_TAIL((&block->nmb_packetlist), - ((list_entry *)&p->u.np_clrsvd.np_miniport_rsvd)); + InsertTailList((&block->nmb_packetlist), (&p->np_list)); KeReleaseSpinLock(&block->nmb_lock, irql); return; } +/* + * NdisMEthIndicateReceiveComplete() handler, runs at DISPATCH_LEVEL + * for serialized miniports, or IRQL <= DISPATCH_LEVEL for deserialized + * miniports. + */ + static void ndis_rxeof_done(adapter) ndis_handle adapter; { struct ndis_softc *sc; ndis_miniport_block *block; block = adapter; /* Schedule transfer/RX of queued packets. */ sc = device_get_softc(block->nmb_physdeviceobj->do_devext); KeInsertQueueDpc(&sc->ndis_rxdpc, NULL, NULL); return; } /* - * Runs at DISPATCH_LEVEL. + * 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->arpcom.ac_if; + ifp = sc->ifp; KeAcquireSpinLockAtDpcLevel(&block->nmb_lock); l = block->nmb_packetlist.nle_flink; - while(l != &block->nmb_packetlist) { - REMOVE_LIST_HEAD((&block->nmb_packetlist)); - p = CONTAINING_RECORD(l, ndis_packet, - u.np_clrsvd.np_miniport_rsvd); + 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); - ifp->if_ipackets++; - (*ifp->if_input)(ifp, m); + KeAcquireSpinLockAtDpcLevel(&sc->ndis_rxlock); + _IF_ENQUEUE(&sc->ndis_rxqueue, m); + KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); + IoQueueWorkItem(sc->ndis_inputitem, + (io_workitem_func)ndis_inputtask_wrap, + WORKQUEUE_CRITICAL, ifp); } if (status == NDIS_STATUS_FAILURE) m_freem(m); /* Advance to next packet */ l = block->nmb_packetlist.nle_flink; } KeReleaseSpinLockFromDpcLevel(&block->nmb_lock); return; } +/* + * NdisMTransferDataComplete() handler, runs at DISPATCH_LEVEL. + */ static void ndis_rxeof_xfr_done(adapter, packet, status, len) ndis_handle adapter; ndis_packet *packet; uint32_t status; uint32_t len; { ndis_miniport_block *block; struct ndis_softc *sc; struct ifnet *ifp; struct mbuf *m; block = adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); - ifp = &sc->arpcom.ac_if; + 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; - ifp->if_ipackets++; - (*ifp->if_input)(ifp, m); + KeAcquireSpinLockAtDpcLevel(&sc->ndis_rxlock); + _IF_ENQUEUE(&sc->ndis_rxqueue, m); + KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); + IoQueueWorkItem(sc->ndis_inputitem, + (io_workitem_func)ndis_inputtask_wrap, + WORKQUEUE_CRITICAL, ifp); + return; } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. * * When handling received NDIS packets, the 'status' field in the * out-of-band portion of the ndis_packet has special meaning. In the * most common case, the underlying NDIS driver will set this field * to NDIS_STATUS_SUCCESS, which indicates that it's ok for us to * take posession of it. We then change the status field to * NDIS_STATUS_PENDING to tell the driver that we now own the packet, * and that we will return it at some point in the future via the * return packet handler. * * If the driver hands us a packet with a status of NDIS_STATUS_RESOURCES, * this means the driver is running out of packet/buffer resources and * wants to maintain ownership of the packet. In this case, we have to * copy the packet data into local storage and let the driver keep the * packet. */ static void ndis_rxeof(adapter, packets, pktcnt) ndis_handle adapter; ndis_packet **packets; uint32_t pktcnt; { struct ndis_softc *sc; ndis_miniport_block *block; ndis_packet *p; uint32_t s; ndis_tcpip_csum *csum; struct ifnet *ifp; struct mbuf *m0, *m; int i; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); - ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; for (i = 0; i < pktcnt; i++) { p = packets[i]; /* Stash the softc here so ptom can use it. */ p->np_softc = sc; if (ndis_ptom(&m0, p)) { device_printf (sc->ndis_dev, "ptom failed\n"); if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS) ndis_return_packet(sc, p); } else { +#ifdef notdef if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) { m = m_dup(m0, M_DONTWAIT); /* * NOTE: we want to destroy the mbuf here, but * we don't actually want to return it to the * driver via the return packet handler. By * bumping np_refcnt, we can prevent the * ndis_return_packet() routine from actually * doing anything. */ p->np_refcnt++; m_freem(m0); if (m == NULL) ifp->if_ierrors++; else m0 = m; } else p->np_oob.npo_status = NDIS_STATUS_PENDING; +#endif + m = m_dup(m0, M_DONTWAIT); + if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) + p->np_refcnt++; + else + p->np_oob.npo_status = NDIS_STATUS_PENDING; + m_freem(m0); + if (m == NULL) { + ifp->if_ierrors++; + continue; + } + m0 = m; m0->m_pkthdr.rcvif = ifp; - ifp->if_ipackets++; /* 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; } } - (*ifp->if_input)(ifp, m0); + KeAcquireSpinLockAtDpcLevel(&sc->ndis_rxlock); + _IF_ENQUEUE(&sc->ndis_rxqueue, m); + KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); + IoQueueWorkItem(sc->ndis_inputitem, + (io_workitem_func)ndis_inputtask_wrap, + WORKQUEUE_CRITICAL, ifp); } } return; } /* + * This routine is run at PASSIVE_LEVEL. We use this routine to pass + * packets into the stack in order to avoid calling (*ifp->if_input)() + * with any locks held (at DISPATCH_LEVEL, we'll be holding the + * 'dispatch level' per-cpu sleep lock). + */ + +static void +ndis_inputtask(dobj, arg) + device_object *dobj; + void *arg; +{ + ndis_miniport_block *block; + struct ifnet *ifp; + struct ndis_softc *sc; + struct mbuf *m; + uint8_t irql; + + ifp = arg; + sc = ifp->if_softc; + block = dobj->do_devext; + + KeAcquireSpinLock(&sc->ndis_rxlock, &irql); + while(1) { + _IF_DEQUEUE(&sc->ndis_rxqueue, m); + if (m == NULL) + break; + KeReleaseSpinLock(&sc->ndis_rxlock, irql); + ifp->if_ipackets++; + (*ifp->if_input)(ifp, m); + KeAcquireSpinLock(&sc->ndis_rxlock, &irql); + } + KeReleaseSpinLock(&sc->ndis_rxlock, irql); + + return; +} + +/* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void ndis_txeof(adapter, packet, status) ndis_handle adapter; ndis_packet *packet; ndis_status status; { struct ndis_softc *sc; ndis_miniport_block *block; struct ifnet *ifp; int idx; struct mbuf *m; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); - ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; m = packet->np_m0; idx = packet->np_txidx; if (sc->ndis_sc) bus_dmamap_unload(sc->ndis_ttag, sc->ndis_tmaps[idx]); ndis_free_packet(packet); m_freem(m); NDIS_LOCK(sc); sc->ndis_txarray[idx] = NULL; sc->ndis_txpending++; if (status == NDIS_STATUS_SUCCESS) ifp->if_opackets++; else ifp->if_oerrors++; + ifp->if_timer = 0; - ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; NDIS_UNLOCK(sc); - NdisScheduleWorkItem(&sc->ndis_startitem); + IoQueueWorkItem(sc->ndis_startitem, + (io_workitem_func)ndis_starttask_wrap, + WORKQUEUE_CRITICAL, ifp); return; } static void ndis_linksts(adapter, status, sbuf, slen) ndis_handle adapter; ndis_status status; void *sbuf; uint32_t slen; { ndis_miniport_block *block; struct ndis_softc *sc; block = adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + sc->ndis_sts = status; - block->nmb_getstat = status; + /* Event list is all full up, drop this one. */ + NDIS_LOCK(sc); + if (sc->ndis_evt[sc->ndis_evtpidx].ne_sts) { + NDIS_UNLOCK(sc); + return; + } + + /* Cache the event. */ + + if (slen) { + sc->ndis_evt[sc->ndis_evtpidx].ne_buf = malloc(slen, + M_TEMP, M_NOWAIT); + if (sc->ndis_evt[sc->ndis_evtpidx].ne_buf == NULL) { + NDIS_UNLOCK(sc); + return; + } + bcopy((char *)sbuf, + sc->ndis_evt[sc->ndis_evtpidx].ne_buf, slen); + } + sc->ndis_evt[sc->ndis_evtpidx].ne_sts = status; + sc->ndis_evt[sc->ndis_evtpidx].ne_len = slen; + NDIS_EVTINC(sc->ndis_evtpidx); + NDIS_UNLOCK(sc); + return; } static void ndis_linksts_done(adapter) ndis_handle adapter; { ndis_miniport_block *block; struct ndis_softc *sc; struct ifnet *ifp; block = adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); - ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; if (!NDIS_INITIALIZED(sc)) return; - switch (block->nmb_getstat) { + switch (sc->ndis_sts) { case NDIS_STATUS_MEDIA_CONNECT: - NdisScheduleWorkItem(&sc->ndis_tickitem); - NdisScheduleWorkItem(&sc->ndis_startitem); + IoQueueWorkItem(sc->ndis_tickitem, + (io_workitem_func)ndis_ticktask_wrap, + WORKQUEUE_CRITICAL, sc); + IoQueueWorkItem(sc->ndis_startitem, + (io_workitem_func)ndis_starttask_wrap, + WORKQUEUE_CRITICAL, ifp); break; case NDIS_STATUS_MEDIA_DISCONNECT: if (sc->ndis_link) - NdisScheduleWorkItem(&sc->ndis_tickitem); + IoQueueWorkItem(sc->ndis_tickitem, + (io_workitem_func)ndis_ticktask_wrap, + WORKQUEUE_CRITICAL, sc); break; default: break; } - return; -} + /* Notify possible listners of interface change. */ -static void -ndis_intr(arg) - void *arg; -{ - struct ndis_softc *sc; - struct ifnet *ifp; - int is_our_intr = 0; - int call_isr = 0; - uint8_t irql; - ndis_miniport_interrupt *intr; + rt_ifmsg(ifp); - sc = arg; - ifp = &sc->arpcom.ac_if; - intr = sc->ndis_block->nmb_interrupt; - - if (intr == NULL || sc->ndis_block->nmb_miniportadapterctx == NULL) - return; - - KeAcquireSpinLock(&intr->ni_dpccountlock, &irql); - if (sc->ndis_block->nmb_interrupt->ni_isrreq == TRUE) - ndis_isr(sc, &is_our_intr, &call_isr); - else { - ndis_disable_intr(sc); - call_isr = 1; - } - KeReleaseSpinLock(&intr->ni_dpccountlock, irql); - - if ((is_our_intr || call_isr)) - IoRequestDpc(sc->ndis_block->nmb_deviceobj, NULL, sc); - return; } static void ndis_tick(xsc) void *xsc; { struct ndis_softc *sc; mtx_unlock(&Giant); sc = xsc; - NdisScheduleWorkItem(&sc->ndis_tickitem); + IoQueueWorkItem(sc->ndis_tickitem, + (io_workitem_func)ndis_ticktask_wrap, + WORKQUEUE_CRITICAL, sc); sc->ndis_stat_ch = timeout(ndis_tick, sc, hz * sc->ndis_block->nmb_checkforhangsecs); mtx_lock(&Giant); return; } static void -ndis_ticktask(w, xsc) - ndis_work_item *w; +ndis_ticktask(d, xsc) + device_object *d; void *xsc; { struct ndis_softc *sc; + struct ieee80211com *ic; ndis_checkforhang_handler hangfunc; uint8_t rval; - ndis_media_state linkstate; - int error, len; sc = xsc; + ic = &sc->ic; 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; } } - len = sizeof(linkstate); - error = ndis_get_info(sc, OID_GEN_MEDIA_CONNECT_STATUS, - (void *)&linkstate, &len); - NDIS_LOCK(sc); - - if (sc->ndis_link == 0 && linkstate == nmc_connected) { - device_printf(sc->ndis_dev, "link up\n"); + if (sc->ndis_link == 0 && + sc->ndis_sts == NDIS_STATUS_MEDIA_CONNECT) { sc->ndis_link = 1; NDIS_UNLOCK(sc); - if (sc->ndis_80211) + if (sc->ndis_80211) { ndis_getstate_80211(sc); + ic->ic_state = IEEE80211_S_RUN; + } NDIS_LOCK(sc); #ifdef LINK_STATE_UP - sc->arpcom.ac_if.if_link_state = LINK_STATE_UP; - rt_ifmsg(&(sc->arpcom.ac_if)); + if_link_state_change(sc->ifp, LINK_STATE_UP); #endif /* LINK_STATE_UP */ } - if (sc->ndis_link == 1 && linkstate == nmc_disconnected) { - device_printf(sc->ndis_dev, "link down\n"); + if (sc->ndis_link == 1 && + sc->ndis_sts == NDIS_STATUS_MEDIA_DISCONNECT) { sc->ndis_link = 0; + if (sc->ndis_80211) + ic->ic_state = IEEE80211_S_ASSOC; #ifdef LINK_STATE_DOWN - sc->arpcom.ac_if.if_link_state = LINK_STATE_DOWN; - rt_ifmsg(&(sc->arpcom.ac_if)); + if_link_state_change(sc->ifp, LINK_STATE_DOWN); #endif /* LINK_STATE_DOWN */ } NDIS_UNLOCK(sc); return; } static void ndis_map_sclist(arg, segs, nseg, mapsize, error) void *arg; bus_dma_segment_t *segs; int nseg; bus_size_t mapsize; int error; { struct ndis_sc_list *sclist; int i; if (error || arg == NULL) return; sclist = arg; sclist->nsl_frags = nseg; for (i = 0; i < nseg; i++) { sclist->nsl_elements[i].nse_addr.np_quad = segs[i].ds_addr; sclist->nsl_elements[i].nse_len = segs[i].ds_len; } return; } static void -ndis_starttask(w, arg) - ndis_work_item *w; +ndis_starttask(d, arg) + device_object *d; void *arg; { struct ifnet *ifp; ifp = arg; #if __FreeBSD_version < 502114 if (ifp->if_snd.ifq_head != NULL) #else if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) #endif ndis_start(ifp); return; } /* * Main transmit routine. To make NDIS drivers happy, we need to * transform mbuf chains into NDIS packets and feed them to the * send packet routines. Most drivers allow you to send several * packets at once (up to the maxpkts limit). Unfortunately, rather * that accepting them in the form of a linked list, they expect * a contiguous array of pointers to packets. * * For those drivers which use the NDIS scatter/gather DMA mechanism, * we need to perform busdma work here. Those that use map registers * will do the mapping themselves on a buffer by buffer basis. */ static void ndis_start(ifp) struct ifnet *ifp; { struct ndis_softc *sc; struct mbuf *m = NULL; ndis_packet **p0 = NULL, *p = NULL; ndis_tcpip_csum *csum; int pcnt = 0, status; sc = ifp->if_softc; NDIS_LOCK(sc); - if (!sc->ndis_link || ifp->if_flags & IFF_OACTIVE) { + 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) { #if __FreeBSD_version < 502114 IF_DEQUEUE(&ifp->if_snd, m); #else IFQ_DRV_DEQUEUE(&ifp->if_snd, m); #endif 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])) { #if __FreeBSD_version >= 502114 IFQ_DRV_PREPEND(&ifp->if_snd, m); #endif NDIS_UNLOCK(sc); #if __FreeBSD_version < 502114 IF_PREPEND(&ifp->if_snd, m); #endif 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. */ 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_flags |= IFF_OACTIVE; + ifp->if_drv_flags |= IFF_DRV_OACTIVE; /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; NDIS_UNLOCK(sc); - if (sc->ndis_maxpkts == 1) - ndis_send_packet(sc, p); - else + /* + * According to NDIS documentation, if a driver exports + * a MiniportSendPackets() routine, we prefer that over + * a MiniportSend() routine (which sends just a single + * packet). + */ + + if (sc->ndis_chars->nmc_sendmulti_func != NULL) ndis_send_packets(sc, p0, pcnt); + else + ndis_send_packet(sc, p); return; } static void ndis_init(xsc) void *xsc; { struct ndis_softc *sc = xsc; - struct ifnet *ifp = &sc->arpcom.ac_if; - int i, error; + struct ifnet *ifp = sc->ifp; + 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 && sc->ndis_skip) return; /* * Cancel pending I/O and free all RX/TX buffers. */ ndis_stop(sc); + if (ndis_init_nic(sc)) return; /* Init our MAC address */ /* Program the packet filter */ sc->ndis_filter = NDIS_PACKET_TYPE_DIRECTED; if (ifp->if_flags & IFF_BROADCAST) sc->ndis_filter |= NDIS_PACKET_TYPE_BROADCAST; if (ifp->if_flags & IFF_PROMISC) sc->ndis_filter |= NDIS_PACKET_TYPE_PROMISCUOUS; - i = sizeof(sc->ndis_filter); + len = sizeof(sc->ndis_filter); error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, - &sc->ndis_filter, &i); + &sc->ndis_filter, &len); if (error) device_printf (sc->ndis_dev, "set filter failed: %d\n", error); /* + * Set lookahead. + */ + + i = ifp->if_mtu; + len = sizeof(i); + ndis_set_info(sc, OID_GEN_CURRENT_LOOKAHEAD, &i, &len); + + /* * Program the multicast filter, if necessary. */ ndis_setmulti(sc); /* Setup task offload. */ ndis_set_offload(sc); /* Enable interrupts. */ ndis_enable_intr(sc); if (sc->ndis_80211) ndis_setstate_80211(sc); NDIS_LOCK(sc); sc->ndis_txidx = 0; sc->ndis_txpending = sc->ndis_maxpkts; sc->ndis_link = 0; - ifp->if_flags |= IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; +#ifdef LINK_STATE_UNKNOWN + if_link_state_change(sc->ifp, LINK_STATE_UNKNOWN); +#endif /* LINK_STATE_UNKNOWN */ + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + NDIS_UNLOCK(sc); /* * 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_stat_ch = timeout(ndis_tick, sc, hz * sc->ndis_block->nmb_checkforhangsecs); return; } /* * Set media options. */ static int ndis_ifmedia_upd(ifp) struct ifnet *ifp; { struct ndis_softc *sc; sc = ifp->if_softc; if (NDIS_INITIALIZED(sc)) ndis_init(sc); return(0); } /* * Report current media status. */ static void ndis_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct ndis_softc *sc; uint32_t media_info; ndis_media_state linkstate; int error, len; ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; sc = ifp->if_softc; if (!NDIS_INITIALIZED(sc)) return; len = sizeof(linkstate); error = ndis_get_info(sc, OID_GEN_MEDIA_CONNECT_STATUS, (void *)&linkstate, &len); len = sizeof(media_info); error = ndis_get_info(sc, OID_GEN_LINK_SPEED, (void *)&media_info, &len); if (linkstate == nmc_connected) ifmr->ifm_status |= IFM_ACTIVE; switch(media_info) { case 100000: ifmr->ifm_active |= IFM_10_T; break; case 1000000: ifmr->ifm_active |= IFM_100_TX; break; case 10000000: ifmr->ifm_active |= IFM_1000_T; break; default: device_printf(sc->ndis_dev, "unknown speed: %d\n", media_info); break; } return; } +#ifdef IEEE80211_F_WPA +static int +ndis_set_cipher(sc, cipher) + struct ndis_softc *sc; + int cipher; +{ + struct ieee80211com *ic; + int rval = 0, len; + uint32_t arg, save; + + ic = &sc->ic; + + len = sizeof(arg); + + if (cipher == WPA_CSE_WEP40 || WPA_CSE_WEP104) { + if (!(ic->ic_caps & IEEE80211_C_WEP)) + return(ENOTSUP); + arg = NDIS_80211_WEPSTAT_ENC1ENABLED; + } + + if (cipher == WPA_CSE_TKIP) { + if (!(ic->ic_caps & IEEE80211_C_TKIP)) + return(ENOTSUP); + arg = NDIS_80211_WEPSTAT_ENC2ENABLED; + } + + if (cipher == WPA_CSE_CCMP) { + if (!(ic->ic_caps & IEEE80211_C_AES_CCM)) + return(ENOTSUP); + arg = NDIS_80211_WEPSTAT_ENC3ENABLED; + } + + 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) + struct ndis_softc *sc; +{ + struct ieee80211com *ic; + struct ieee80211_ie_wpa *w; + struct ndis_ie *n; + char *pos; + uint32_t arg; + int i; + + ic = &sc->ic; + + /* + * 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 *)ic->ic_opt_ie; + + /* Check for the right kind of IE. */ + if (w->wpa_id != IEEE80211_ELEMID_VENDOR) + 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; + + 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); +} +#endif + static void ndis_setstate_80211(sc) struct ndis_softc *sc; { struct ieee80211com *ic; ndis_80211_ssid ssid; + ndis_80211_macaddr bssid; ndis_80211_config config; ndis_80211_wep wep; int i, rval = 0, len; uint32_t arg; struct ifnet *ifp; ic = &sc->ic; - ifp = &sc->ic.ic_ac.ac_if; + ifp = sc->ifp; if (!NDIS_INITIALIZED(sc)) 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 RTS threshold */ + + len = sizeof(arg); + arg = ic->ic_rtsthreshold; + ndis_set_info(sc, OID_802_11_RTS_THRESHOLD, &arg, &len); + + /* Set fragmentation threshold */ + + len = sizeof(arg); + arg = ic->ic_fragthreshold; + ndis_set_info(sc, OID_802_11_FRAGMENTATION_THRESHOLD, &arg, &len); + + /* Set power management */ + + len = sizeof(arg); + if (ic->ic_flags & IEEE80211_F_PMGTON) + arg = NDIS_80211_POWERMODE_FAST_PSP; + else + arg = NDIS_80211_POWERMODE_CAM; + ndis_set_info(sc, OID_802_11_POWER_MODE, &arg, &len); + + /* + * Default encryption mode to off, authentication + * to open and privacy to 'accept everything.' + */ + + len = sizeof(arg); + arg = NDIS_80211_WEPSTAT_DISABLED; + ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); + + len = sizeof(arg); + arg = NDIS_80211_AUTHMODE_OPEN; + ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len); + + /* + * Note that OID_80211_PRIVACY_FILTER is optional: + * not all drivers implement it. + */ + len = sizeof(arg); + arg = NDIS_80211_PRIVFILT_8021XWEP; + ndis_set_info(sc, OID_802_11_PRIVACY_FILTER, &arg, &len); + /* Set WEP */ -#ifdef IEEE80211_F_WEPON +#if __FreeBSD_version < 600000 if (ic->ic_flags & IEEE80211_F_WEPON) { #else - if (ic->ic_wep_mode >= IEEE80211_WEP_ON) { + if (ic->ic_flags & IEEE80211_F_PRIVACY && + !(ic->ic_flags & IEEE80211_F_WPA)) { #endif + int keys_set = 0; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { - if (ic->ic_nw_keys[i].wk_len) { + if (ic->ic_nw_keys[i].wk_keylen) { +#if __FreeBSD_version >= 600000 + if (ic->ic_nw_keys[i].wk_cipher->ic_cipher != + IEEE80211_CIPHER_WEP) + continue; +#endif bzero((char *)&wep, sizeof(wep)); - wep.nw_keylen = ic->ic_nw_keys[i].wk_len; -#ifdef notdef - /* 5 and 13 are the only valid key lengths */ - if (ic->ic_nw_keys[i].wk_len < 5) + wep.nw_keylen = ic->ic_nw_keys[i].wk_keylen; + + /* + * 5, 13 and 16 are the only valid + * only valid key lengths. Anything + * in between will be zero padded out to + * the next highest boundary. + */ + if (ic->ic_nw_keys[i].wk_keylen < 5) wep.nw_keylen = 5; - else if (ic->ic_nw_keys[i].wk_len > 5 && - ic->ic_nw_keys[i].wk_len < 13) + else if (ic->ic_nw_keys[i].wk_keylen > 5 && + ic->ic_nw_keys[i].wk_keylen < 13) wep.nw_keylen = 13; -#endif + else if (ic->ic_nw_keys[i].wk_keylen > 13 && + ic->ic_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 == ic->ic_wep_txkey) + if (i == ic->ic_def_txkey) wep.nw_keyidx |= NDIS_80211_WEPKEY_TX; bcopy(ic->ic_nw_keys[i].wk_key, wep.nw_keydata, wep.nw_length); len = sizeof(wep); 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++; } } - 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); -#ifndef IEEE80211_F_WEPON - if (ic->ic_wep_mode != IEEE80211_WEP_8021X && - ic->ic_wep_mode != IEEE80211_WEP_ON) - arg = NDIS_80211_PRIVFILT_ACCEPTALL; - else -#endif - arg = NDIS_80211_PRIVFILT_8021XWEP; - len = sizeof(arg); - rval = ndis_set_info(sc, OID_802_11_PRIVACY_FILTER, &arg, &len); -#ifdef IEEE80211_WEP_8021X /*IEEE80211_F_WEPON*/ - /* Accept that we only have "shared" and 802.1x modes. */ - if (rval == 0) { - if (arg == NDIS_80211_PRIVFILT_ACCEPTALL) - ic->ic_wep_mode = IEEE80211_WEP_MIXED; + if (keys_set) { + 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); +#ifdef IEEE80211_F_DROPUNENC + if (ic->ic_flags & IEEE80211_F_DROPUNENC) + arg = NDIS_80211_PRIVFILT_8021XWEP; else - ic->ic_wep_mode = IEEE80211_WEP_8021X; - } + arg = NDIS_80211_PRIVFILT_ACCEPTALL; + + len = sizeof(arg); + ndis_set_info(sc, + OID_802_11_PRIVACY_FILTER, &arg, &len); #endif - arg = NDIS_80211_AUTHMODE_OPEN; - } else { - arg = NDIS_80211_WEPSTAT_DISABLED; - len = sizeof(arg); - ndis_set_info(sc, OID_802_11_WEP_STATUS, &arg, &len); - arg = NDIS_80211_AUTHMODE_OPEN; - } + } + } - len = sizeof(arg); - rval = ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len); +#ifdef IEEE80211_F_WPA + /* Set up WPA. */ -#ifdef notyet - if (rval) - device_printf (sc->ndis_dev, "set auth failed: %d\n", rval); + if (ic->ic_flags & IEEE80211_F_WPA1 && ic->ic_opt_ie_len && + ic->ic_caps & IEEE80211_C_WPA) + if (ndis_set_wpa(sc)) + device_printf(sc->ndis_dev, "WPA setup failed\n"); #endif #ifdef notyet /* Set network type. */ arg = 0; switch (ic->ic_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", ic->ic_curmode); } if (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 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_ibss_chan != IEEE80211_CHAN_ANYC) { int chan, chanflag; chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); 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_ibss_chan->ic_freq * 1000; ic->ic_bss->ni_chan = ic->ic_ibss_chan; len = sizeof(config); config.nc_length = len; config.nc_fhconfig.ncf_length = sizeof(ndis_80211_config_fh); 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); + /* + * 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 (ic->ic_flags & IEEE80211_F_DESBSSID && + ic->ic_opmode != IEEE80211_M_IBSS) + bcopy(ic->ic_des_bssid, bssid, len); + else + bcopy(ifp->if_broadcastaddr, bssid, len); + + 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. */ len = sizeof(ssid); bzero((char *)&ssid, len); ssid.ns_ssidlen = ic->ic_des_esslen; if (ssid.ns_ssidlen == 0) { ssid.ns_ssidlen = 1; } else bcopy(ic->ic_des_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); + ic->ic_state = IEEE80211_S_ASSOC; + return; } static void ndis_media_status(struct ifnet *ifp, struct ifmediareq *imr) { - struct ieee80211com *ic = (void *)ifp; + struct ieee80211com *ic = &((struct ndis_softc *)ifp->if_softc)->ic; struct ieee80211_node *ni = NULL; imr->ifm_status = IFM_AVALID; imr->ifm_active = IFM_IEEE80211; if (ic->ic_state == IEEE80211_S_RUN) imr->ifm_status |= IFM_ACTIVE; imr->ifm_active |= IFM_AUTO; switch (ic->ic_opmode) { case IEEE80211_M_STA: ni = ic->ic_bss; /* calculate rate subtype */ imr->ifm_active |= ieee80211_rate2media(ic, ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode); break; case IEEE80211_M_IBSS: ni = ic->ic_bss; /* calculate rate subtype */ imr->ifm_active |= ieee80211_rate2media(ic, ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode); imr->ifm_active |= IFM_IEEE80211_ADHOC; break; case IEEE80211_M_AHDEMO: /* should not come here */ break; case IEEE80211_M_HOSTAP: imr->ifm_active |= IFM_IEEE80211_HOSTAP; break; case IEEE80211_M_MONITOR: imr->ifm_active |= IFM_IEEE80211_MONITOR; break; } switch (ic->ic_curmode) { case IEEE80211_MODE_11A: imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A); break; case IEEE80211_MODE_11B: imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11B); break; case IEEE80211_MODE_11G: imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11G); break; - case IEEE80211_MODE_TURBO: +#ifdef IEEE80211_MODE_TURBO_A + case IEEE80211_MODE_TURBO_A: imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A) | IFM_IEEE80211_TURBO; +#endif break; } } static int ndis_get_assoc(sc, assoc) struct ndis_softc *sc; ndis_wlan_bssid_ex **assoc; { 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); } len = 0; error = ndis_get_info(sc, OID_802_11_BSSID_LIST, NULL, &len); if (error != ENOSPC) { device_printf(sc->ndis_dev, "bssid_list failed\n"); return (error); } bl = malloc(len, M_TEMP, M_NOWAIT|M_ZERO); error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len); if (error) { free(bl, M_TEMP); device_printf(sc->ndis_dev, "bssid_list failed\n"); return (error); } bs = (ndis_wlan_bssid_ex *)&bl->nblx_bssid[0]; for (i = 0; i < bl->nblx_items; i++) { if (bcmp(bs->nwbx_macaddr, bssid, sizeof(bssid)) == 0) { *assoc = malloc(bs->nwbx_len, M_TEMP, M_NOWAIT); if (*assoc == NULL) { free(bl, M_TEMP); return(ENOMEM); } bcopy((char *)bs, (char *)*assoc, bs->nwbx_len); free(bl, M_TEMP); return(0); } bs = (ndis_wlan_bssid_ex *)((char *)bs + bs->nwbx_len); } free(bl, M_TEMP); return(ENOENT); } static void ndis_getstate_80211(sc) struct ndis_softc *sc; { struct ieee80211com *ic; ndis_80211_ssid ssid; ndis_80211_config config; ndis_wlan_bssid_ex *bs; int rval, len, i = 0; uint32_t arg; struct ifnet *ifp; ic = &sc->ic; - ifp = &sc->ic.ic_ac.ac_if; + ifp = sc->ifp; if (!NDIS_INITIALIZED(sc)) return; if (sc->ndis_link) ic->ic_state = IEEE80211_S_RUN; else ic->ic_state = IEEE80211_S_ASSOC; /* * If we're associated, retrieve info on the current bssid. */ if ((rval = ndis_get_assoc(sc, &bs)) == 0) { switch(bs->nwbx_nettype) { case NDIS_80211_NETTYPE_11FH: case NDIS_80211_NETTYPE_11DS: ic->ic_curmode = IEEE80211_MODE_11B; break; case NDIS_80211_NETTYPE_11OFDM5: ic->ic_curmode = IEEE80211_MODE_11A; break; case NDIS_80211_NETTYPE_11OFDM24: ic->ic_curmode = IEEE80211_MODE_11G; break; default: device_printf(sc->ndis_dev, "unknown nettype %d\n", arg); break; } + IEEE80211_ADDR_COPY(ic->ic_bss->ni_bssid, bs->nwbx_macaddr); free(bs, M_TEMP); } else return; len = sizeof(ssid); bzero((char *)&ssid, len); rval = ndis_get_info(sc, OID_802_11_SSID, &ssid, &len); if (rval) device_printf (sc->ndis_dev, "get ssid failed: %d\n", rval); bcopy(ssid.ns_ssid, ic->ic_bss->ni_essid, ssid.ns_ssidlen); ic->ic_bss->ni_esslen = ssid.ns_ssidlen; len = sizeof(arg); rval = ndis_get_info(sc, OID_GEN_LINK_SPEED, &arg, &len); if (rval) device_printf (sc->ndis_dev, "get link speed failed: %d\n", rval); if (ic->ic_modecaps & (1<ic_bss->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B]; for (i = 0; i < ic->ic_bss->ni_rates.rs_nrates; i++) { if ((ic->ic_bss->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) == arg / 5000) break; } } if (i == ic->ic_bss->ni_rates.rs_nrates && ic->ic_modecaps & (1<ic_bss->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11G]; for (i = 0; i < ic->ic_bss->ni_rates.rs_nrates; i++) { if ((ic->ic_bss->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) == arg / 5000) break; } } if (i == ic->ic_bss->ni_rates.rs_nrates) device_printf(sc->ndis_dev, "no matching rate for: %d\n", arg / 5000); else ic->ic_bss->ni_txrate = i; if (ic->ic_caps & IEEE80211_C_PMGT) { len = sizeof(arg); rval = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &len); if (rval) device_printf(sc->ndis_dev, "get power mode failed: %d\n", rval); if (arg == NDIS_80211_POWERMODE_CAM) ic->ic_flags &= ~IEEE80211_F_PMGTON; else ic->ic_flags |= IEEE80211_F_PMGTON; } 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); if (rval == 0) { int chan; chan = ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0); if (chan < 0 || chan >= IEEE80211_CHAN_MAX) { if (ifp->if_flags & IFF_DEBUG) device_printf(sc->ndis_dev, "current channel " "(%uMHz) out of bounds\n", config.nc_dsconfig / 1000); ic->ic_bss->ni_chan = &ic->ic_channels[1]; } else ic->ic_bss->ni_chan = &ic->ic_channels[chan]; } else device_printf(sc->ndis_dev, "couldn't retrieve " "channel info: %d\n", rval); /* len = sizeof(arg); rval = ndis_get_info(sc, OID_802_11_WEP_STATUS, &arg, &len); if (rval) device_printf (sc->ndis_dev, "get wep status failed: %d\n", rval); if (arg == NDIS_80211_WEPSTAT_ENABLED) ic->ic_flags |= IEEE80211_F_WEPON; else ic->ic_flags &= ~IEEE80211_F_WEPON; */ return; } static int ndis_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct ndis_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; + struct ndis_oid_data oid; + struct ndis_evt evt; + void *oidbuf; int i, error = 0; /*NDIS_LOCK(sc);*/ switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { - if (ifp->if_flags & IFF_RUNNING && + if (ifp->if_drv_flags & IFF_DRV_RUNNING && ifp->if_flags & IFF_PROMISC && !(sc->ndis_if_flags & IFF_PROMISC)) { sc->ndis_filter |= NDIS_PACKET_TYPE_PROMISCUOUS; i = sizeof(sc->ndis_filter); error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, &sc->ndis_filter, &i); - } else if (ifp->if_flags & IFF_RUNNING && + } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && !(ifp->if_flags & IFF_PROMISC) && sc->ndis_if_flags & IFF_PROMISC) { sc->ndis_filter &= ~NDIS_PACKET_TYPE_PROMISCUOUS; i = sizeof(sc->ndis_filter); error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, &sc->ndis_filter, &i); } else ndis_init(sc); } else { - if (ifp->if_flags & IFF_RUNNING) + if (ifp->if_drv_flags & IFF_DRV_RUNNING) ndis_stop(sc); } sc->ndis_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: ndis_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: if (sc->ndis_80211) { +#if __FreeBSD_version < 600000 error = ieee80211_ioctl(ifp, command, data); +#else + error = ieee80211_ioctl(&sc->ic, command, data); +#endif if (error == ENETRESET) { ndis_setstate_80211(sc); /*ndis_init(sc);*/ error = 0; } } else 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; + case SIOCG80211: + if (!NDIS_INITIALIZED(sc)) + goto do_80211; + if (sc->ndis_80211) + error = ndis_80211_ioctl_get(ifp, command, data); + else + error = ENOTTY; + break; + case SIOCS80211: + if (!NDIS_INITIALIZED(sc)) + goto do_80211; + if (sc->ndis_80211) + error = ndis_80211_ioctl_set(ifp, command, data); + else + error = ENOTTY; + break; + case SIOCGDRVSPEC: + if ((error = suser(curthread))) + break; + error = copyin(ifr->ifr_data, &oid, sizeof(oid)); + if (error) + break; + oidbuf = malloc(oid.len, M_TEMP, M_NOWAIT|M_ZERO); + if (oidbuf == NULL) { + error = ENOMEM; + break; + } + error = copyin(ifr->ifr_data + sizeof(oid), oidbuf, oid.len); + if (error) { + free(oidbuf, M_TEMP); + break; + } + error = ndis_get_info(sc, oid.oid, oidbuf, &oid.len); + if (error) { + free(oidbuf, M_TEMP); + break; + } + error = copyout(&oid, ifr->ifr_data, sizeof(oid)); + if (error) { + free(oidbuf, M_TEMP); + break; + } + error = copyout(oidbuf, ifr->ifr_data + sizeof(oid), oid.len); + free(oidbuf, M_TEMP); + break; + case SIOCSDRVSPEC: + if ((error = suser(curthread))) + break; + error = copyin(ifr->ifr_data, &oid, sizeof(oid)); + if (error) + break; + oidbuf = malloc(oid.len, M_TEMP, M_NOWAIT|M_ZERO); + if (oidbuf == NULL) { + error = ENOMEM; + break; + } + error = copyin(ifr->ifr_data + sizeof(oid), oidbuf, oid.len); + if (error) { + free(oidbuf, M_TEMP); + break; + } + error = ndis_set_info(sc, oid.oid, oidbuf, &oid.len); + if (error) { + free(oidbuf, M_TEMP); + break; + } + error = copyout(&oid, ifr->ifr_data, sizeof(oid)); + if (error) { + free(oidbuf, M_TEMP); + break; + } + error = copyout(oidbuf, ifr->ifr_data + sizeof(oid), oid.len); + free(oidbuf, M_TEMP); + break; + case SIOCGPRIVATE_0: + if ((error = suser(curthread))) + break; + NDIS_LOCK(sc); + if (sc->ndis_evt[sc->ndis_evtcidx].ne_sts == 0) { + error = ENOENT; + NDIS_UNLOCK(sc); + break; + } + error = copyin(ifr->ifr_data, &evt, sizeof(evt)); + if (error) { + NDIS_UNLOCK(sc); + break; + } + if (evt.ne_len < sc->ndis_evt[sc->ndis_evtcidx].ne_len) { + error = ENOSPC; + NDIS_UNLOCK(sc); + break; + } + error = copyout(&sc->ndis_evt[sc->ndis_evtcidx], + ifr->ifr_data, sizeof(uint32_t) * 2); + if (error) { + NDIS_UNLOCK(sc); + break; + } + if (sc->ndis_evt[sc->ndis_evtcidx].ne_len) { + error = copyout(sc->ndis_evt[sc->ndis_evtcidx].ne_buf, + ifr->ifr_data + (sizeof(uint32_t) * 2), + sc->ndis_evt[sc->ndis_evtcidx].ne_len); + if (error) { + NDIS_UNLOCK(sc); + break; + } + free(sc->ndis_evt[sc->ndis_evtcidx].ne_buf, M_TEMP); + sc->ndis_evt[sc->ndis_evtcidx].ne_buf = NULL; + } + sc->ndis_evt[sc->ndis_evtcidx].ne_len = 0; + sc->ndis_evt[sc->ndis_evtcidx].ne_sts = 0; + NDIS_EVTINC(sc->ndis_evtcidx); + NDIS_UNLOCK(sc); + break; case SIOCGIFGENERIC: case SIOCSIFGENERIC: if (sc->ndis_80211 && NDIS_INITIALIZED(sc)) { if (command == SIOCGIFGENERIC) error = ndis_wi_ioctl_get(ifp, command, data); else error = ndis_wi_ioctl_set(ifp, command, data); } else error = ENOTTY; if (error != ENOTTY) break; default: +do_80211: sc->ndis_skip = 1; if (sc->ndis_80211) { +#if __FreeBSD_version < 600000 error = ieee80211_ioctl(ifp, command, data); +#else + error = ieee80211_ioctl(&sc->ic, command, data); +#endif if (error == ENETRESET) { ndis_setstate_80211(sc); error = 0; } } else error = ether_ioctl(ifp, command, data); sc->ndis_skip = 0; break; } /*NDIS_UNLOCK(sc);*/ return(error); } static int ndis_wi_ioctl_get(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct wi_req wreq; struct ifreq *ifr; struct ndis_softc *sc; ndis_80211_bssid_list_ex *bl; ndis_wlan_bssid_ex *wb; struct wi_apinfo *api; int error, i, j, len, maxaps; sc = ifp->if_softc; ifr = (struct ifreq *)data; error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); if (error) return (error); switch (wreq.wi_type) { case WI_RID_READ_APS: len = 0; error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN, NULL, &len); if (error == 0) tsleep(&error, PPAUSE|PCATCH, "ssidscan", hz * 2); len = 0; error = ndis_get_info(sc, OID_802_11_BSSID_LIST, NULL, &len); if (error != ENOSPC) break; - bl = malloc(len, M_DEVBUF, M_WAITOK|M_ZERO); + bl = malloc(len, M_DEVBUF, M_NOWAIT|M_ZERO); error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len); if (error) { free(bl, M_DEVBUF); break; } maxaps = (2 * wreq.wi_len - sizeof(int)) / sizeof(*api); maxaps = MIN(maxaps, bl->nblx_items); wreq.wi_len = (maxaps * sizeof(*api) + sizeof(int)) / 2; *(int *)&wreq.wi_val = maxaps; api = (struct wi_apinfo *)&((int *)&wreq.wi_val)[1]; wb = bl->nblx_bssid; while (maxaps--) { bzero(api, sizeof(*api)); bcopy(&wb->nwbx_macaddr, &api->bssid, sizeof(api->bssid)); api->namelen = wb->nwbx_ssid.ns_ssidlen; bcopy(&wb->nwbx_ssid.ns_ssid, &api->name, api->namelen); if (wb->nwbx_privacy) api->capinfo |= IEEE80211_CAPINFO_PRIVACY; /* XXX Where can we get noise information? */ api->signal = wb->nwbx_rssi + 149; /* XXX */ api->quality = api->signal; api->channel = ieee80211_mhz2ieee(wb->nwbx_config.nc_dsconfig / 1000, 0); /* In "auto" infrastructure mode, this is useless. */ if (wb->nwbx_netinfra == NDIS_80211_NET_INFRA_IBSS) api->capinfo |= IEEE80211_CAPINFO_IBSS; if (wb->nwbx_len > sizeof(ndis_wlan_bssid)) { j = sizeof(ndis_80211_rates_ex); /* handle other extended things */ } else j = sizeof(ndis_80211_rates); for (i = api->rate = 0; i < j; i++) api->rate = MAX(api->rate, 5 * (wb->nwbx_supportedrates[i] & 0x7f)); api++; wb = (ndis_wlan_bssid_ex *)((char *)wb + wb->nwbx_len); } free(bl, M_DEVBUF); error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); break; default: error = ENOTTY; break; } return (error); } static int ndis_wi_ioctl_set(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct wi_req wreq; struct ifreq *ifr; struct ndis_softc *sc; uint32_t foo; int error, len; error = suser(curthread); if (error) return (error); sc = ifp->if_softc; ifr = (struct ifreq *)data; error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); if (error) return (error); switch (wreq.wi_type) { case WI_RID_SCAN_APS: case WI_RID_SCAN_REQ: /* arguments ignored */ len = sizeof(foo); foo = 0; error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN, &foo, &len); break; default: error = ENOTTY; break; } return (error); } +static int +ndis_80211_ioctl_get(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct ndis_softc *sc; + struct ieee80211req *ireq; +#ifdef RTM_IEEE80211_SCAN + ndis_80211_bssid_list_ex *bl; + ndis_wlan_bssid_ex *wb; + struct ieee80211req_scan_result *sr, *bsr; + int i, j; + char *cp; +#endif + int error, len; + uint16_t nodename_u[IEEE80211_NWID_LEN + 1]; + unicode_string us; + ansi_string as; + + sc = ifp->if_softc; + ireq = (struct ieee80211req *) data; + + switch (ireq->i_type) { +#ifdef IEEE80211_IOC_MLME + case IEEE80211_IOC_MLME: + error = 0; + break; +#endif +#ifdef RTM_IEEE80211_SCAN + case IEEE80211_IOC_SCAN_RESULTS: + len = 0; + error = ndis_get_info(sc, OID_802_11_BSSID_LIST, NULL, &len); + if (error != ENOSPC) + break; + bl = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO); + error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len); + if (error) { + free(bl, M_DEVBUF); + break; + } + sr = bsr = malloc(ireq->i_len, M_DEVBUF, M_NOWAIT | M_ZERO); + wb = &bl->nblx_bssid[0]; + len = 0; + for (i = 0; i < bl->nblx_items; i++) { + /* + * Check if we have enough space left for this ap. + * Note that the size of the SSID list structure + * can vary: the extended structure with info + * elements is only supported by NDIS 5.1 drivers. + */ + if (wb->nwbx_len > sizeof(ndis_wlan_bssid)) { + j = roundup(sizeof(*sr) + + wb->nwbx_ssid.ns_ssidlen + + wb->nwbx_ielen - + sizeof(struct ndis_80211_fixed_ies), + sizeof(uint32_t)); + } else { + j = roundup(sizeof(*sr) + + wb->nwbx_ssid.ns_ssidlen + + sizeof(struct ndis_80211_fixed_ies), + sizeof(uint32_t)); + } + if (len + j > ireq->i_len) + break; + bcopy(&wb->nwbx_macaddr, &sr->isr_bssid, + sizeof(sr->isr_bssid)); + if (wb->nwbx_privacy) + sr->isr_capinfo |= IEEE80211_CAPINFO_PRIVACY; + sr->isr_rssi = wb->nwbx_rssi + 200; + sr->isr_freq = wb->nwbx_config.nc_dsconfig / 1000; + sr->isr_intval = wb->nwbx_config.nc_beaconperiod; + switch (wb->nwbx_netinfra) { + case NDIS_80211_NET_INFRA_IBSS: + sr->isr_capinfo |= IEEE80211_CAPINFO_IBSS; + break; + case NDIS_80211_NET_INFRA_BSS: + sr->isr_capinfo |= IEEE80211_CAPINFO_ESS; + break; + } + for (j = 0; j < sizeof(sr->isr_rates); j++) { + /* XXX - check units */ + if (wb->nwbx_supportedrates[j] == 0) + break; + sr->isr_rates[j] = + wb->nwbx_supportedrates[j] & 0x7f; + } + sr->isr_nrates = j; + sr->isr_ssid_len = wb->nwbx_ssid.ns_ssidlen; + cp = (char *)sr + sizeof(*sr); + bcopy(&wb->nwbx_ssid.ns_ssid, cp, sr->isr_ssid_len); + cp += sr->isr_ssid_len; + if (wb->nwbx_len > sizeof(ndis_wlan_bssid)) { + sr->isr_ie_len = wb->nwbx_ielen + - sizeof(struct ndis_80211_fixed_ies); + bcopy((char *)wb->nwbx_ies + + sizeof(struct ndis_80211_fixed_ies), + cp, sr->isr_ie_len); + } + sr->isr_len = roundup(sizeof(*sr) + sr->isr_ssid_len + + sr->isr_ie_len, sizeof(uint32_t)); + len += sr->isr_len; + sr = (struct ieee80211req_scan_result *)((char *)sr + + sr->isr_len); + wb = (ndis_wlan_bssid_ex *)((char *)wb + wb->nwbx_len); + } + ireq->i_len = len; + error = copyout(bsr, ireq->i_data, len); + free(bl, M_DEVBUF); + free(bsr, M_DEVBUF); + break; +#endif + case IEEE80211_IOC_STATIONNAME: + error = ndis_get_info(sc, OID_GEN_MACHINE_NAME, + &nodename_u, &len); + if (error) + break; + us.us_len = us.us_maxlen = len; + us.us_buf = nodename_u; + if (RtlUnicodeStringToAnsiString(&as, &us, TRUE)) { + error = ENOMEM; + break; + } + ireq->i_len = as.as_len; + error = copyout(as.as_buf, ireq->i_data, ireq->i_len); + RtlFreeAnsiString(&as); + break; + default: +#if __FreeBSD_version < 600000 + error = ieee80211_ioctl(ifp, command, data); +#else + error = ieee80211_ioctl(&sc->ic, command, data); +#endif + break; + } + + return(error); +} + +#ifdef IEEE80211_F_WPA +int +ndis_del_key(ic, key) + struct ieee80211com *ic; + const struct ieee80211_key *key; +{ + struct ndis_softc *sc; + ndis_80211_key rkey; + int len, error = 0; + + sc = ic->ic_ifp->if_softc; + + bzero((char *)&rkey, sizeof(rkey)); + len = sizeof(rkey); + + rkey.nk_len = len; + rkey.nk_keyidx = key->wk_keyix; + + bcopy(ic->ic_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(ic, key, mac) + struct ieee80211com *ic; + const struct ieee80211_key *key; + const uint8_t mac[IEEE80211_ADDR_LEN]; +{ + struct ndis_softc *sc; + ndis_80211_key rkey; + int len, error = 0; + + sc = ic->ic_ifp->if_softc; + + switch (key->wk_cipher->ic_cipher) { + case IEEE80211_CIPHER_TKIP: + + len = sizeof(ndis_80211_key); + bzero((char *)&rkey, sizeof(rkey)); + + rkey.nk_len = len; + rkey.nk_keylen = key->wk_keylen; + + if (key->wk_flags & IEEE80211_KEY_SWMIC) + rkey.nk_keylen += 16; + + /* key index - gets weird in NDIS */ + + if (key->wk_keyix != IEEE80211_KEYIX_NONE) + rkey.nk_keyidx = key->wk_keyix; + else + rkey.nk_keyidx = 0; + + if (key->wk_flags & IEEE80211_KEY_XMIT) + rkey.nk_keyidx |= 1 << 31; + + if (key->wk_flags & IEEE80211_KEY_GROUP) { + bcopy(ic->ic_ifp->if_broadcastaddr, + rkey.nk_bssid, IEEE80211_ADDR_LEN); + } else { + bcopy(ic->ic_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; + + 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); +} +#endif + +static int +ndis_80211_ioctl_set(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct ndis_softc *sc; + struct ieee80211req *ireq; + int error = EINVAL, len; + ansi_string as; + unicode_string us; + + sc = ifp->if_softc; + ireq = (struct ieee80211req *) data; + + switch (ireq->i_type) { +#ifdef IEEE80211_IOC_MLME + case IEEE80211_IOC_MLME: + case IEEE80211_IOC_ROAMING: + case IEEE80211_IOC_COUNTERMEASURES: + case IEEE80211_IOC_DROPUNENCRYPTED: + error = 0; + break; +#endif +#ifdef RTM_IEEE80211_SCAN + case IEEE80211_IOC_SCAN_REQ: + len = 0; + error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN, + NULL, &len); + tsleep(&error, PPAUSE|PCATCH, "ssidscan", hz * 2); + rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0); + break; +#endif + case IEEE80211_IOC_STATIONNAME: + error = suser(curthread); + if (error) + break; + if (ireq->i_val != 0 || + ireq->i_len > IEEE80211_NWID_LEN) { + error = EINVAL; + break; + } + as.as_len = as.as_maxlen = ireq->i_len; + as.as_buf = ireq->i_data; + if (RtlAnsiStringToUnicodeString(&us, &as, TRUE)) { + error = ENOMEM; + break; + } + len = us.us_len; + error = ndis_set_info(sc, OID_GEN_MACHINE_NAME, + us.us_buf, &len); + RtlFreeUnicodeString(&us); + break; + default: +#if __FreeBSD_version < 600000 + error = ieee80211_ioctl(ifp, command, data); +#else + error = ieee80211_ioctl(&sc->ic, command, data); +#endif + if (error == ENETRESET) { + ndis_setstate_80211(sc); + error = 0; + } + } + + return(error); +} + static void -ndis_resettask(w, arg) - ndis_work_item *w; +ndis_resettask(d, arg) + device_object *d; void *arg; { - struct ndis_softc *sc; + struct ndis_softc *sc; sc = arg; ndis_reset_nic(sc); return; } static void ndis_watchdog(ifp) struct ifnet *ifp; { struct ndis_softc *sc; sc = ifp->if_softc; NDIS_LOCK(sc); ifp->if_oerrors++; device_printf(sc->ndis_dev, "watchdog timeout\n"); NDIS_UNLOCK(sc); - NdisScheduleWorkItem(&sc->ndis_resetitem); - NdisScheduleWorkItem(&sc->ndis_startitem); + 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, ifp); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void ndis_stop(sc) struct ndis_softc *sc; { struct ifnet *ifp; + struct ieee80211com *ic; + int i; - ifp = &sc->arpcom.ac_if; + ic = &sc->ic; + if (sc->ndis_80211) + ic->ic_state = IEEE80211_S_INIT; + + ifp = sc->ifp; untimeout(ndis_tick, sc, sc->ndis_stat_ch); NDIS_LOCK(sc); ifp->if_timer = 0; sc->ndis_link = 0; - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); NDIS_UNLOCK(sc); ndis_halt_nic(sc); + + NDIS_LOCK(sc); + for (i = 0; i < NDIS_EVENTS; i++) { + if (sc->ndis_evt[i].ne_sts && sc->ndis_evt[i].ne_buf != NULL) + free(sc->ndis_evt[i].ne_buf, M_TEMP); + sc->ndis_evt[i].ne_sts = 0; + sc->ndis_evt[i].ne_len = 0; + } + sc->ndis_evtcidx = 0; + sc->ndis_evtpidx = 0; + NDIS_UNLOCK(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ void ndis_shutdown(dev) device_t dev; { struct ndis_softc *sc; sc = device_get_softc(dev); ndis_stop(sc); return; } Index: stable/5/sys/dev/if_ndis/if_ndisvar.h =================================================================== --- stable/5/sys/dev/if_ndis/if_ndisvar.h (revision 151520) +++ stable/5/sys/dev/if_ndis/if_ndisvar.h (revision 151521) @@ -1,142 +1,209 @@ /*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #define NDIS_DEFAULT_NODENAME "FreeBSD NDIS node" #define NDIS_NODENAME_LEN 32 +/* For setting/getting OIDs from userspace. */ + +struct ndis_oid_data { + uint32_t oid; + uint32_t len; +#ifdef notdef + uint8_t data[1]; +#endif +}; + struct ndis_pci_type { uint16_t ndis_vid; uint16_t ndis_did; uint32_t ndis_subsys; char *ndis_name; }; struct ndis_pccard_type { const char *ndis_vid; const char *ndis_did; char *ndis_name; }; struct ndis_shmem { + list_entry ndis_list; bus_dma_tag_t ndis_stag; bus_dmamap_t ndis_smap; void *ndis_saddr; ndis_physaddr ndis_paddr; - struct ndis_shmem *ndis_next; }; 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_miniportadapterctx != NULL) +#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 + (x)->ndis_txidx = ((x)->ndis_txidx + 1) % NDIS_TXPKTS +#if __FreeBSD_version < 600000 #define arpcom ic.ic_ac +#endif +#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_softc { struct ieee80211com ic; /* interface info */ -#ifdef notdef - struct ieee80211com arpcom; /* interface info */ -#endif + struct ifnet *ifp; struct ifmedia ifmedia; /* media info */ u_long ndis_hwassist; uint32_t ndis_v4tx; uint32_t ndis_v4rx; bus_space_handle_t ndis_bhandle; bus_space_tag_t ndis_btag; void *ndis_intrhand; struct resource *ndis_irq; struct resource *ndis_res; struct resource *ndis_res_io; int ndis_io_rid; struct resource *ndis_res_mem; int ndis_mem_rid; struct resource *ndis_res_altmem; int ndis_altmem_rid; struct resource *ndis_res_am; /* attribute mem (pccard) */ int ndis_am_rid; struct resource *ndis_res_cm; /* common mem (pccard) */ struct resource_list ndis_rl; int ndis_rescnt; - struct mtx ndis_mtx; + kspin_lock ndis_spinlock; + 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_handle ndis_stat_ch; int ndis_maxpkts; ndis_oid *ndis_oids; int ndis_oidcnt; int ndis_txidx; int ndis_txpending; ndis_packet **ndis_txarray; ndis_handle ndis_txpool; int ndis_sc; ndis_cfg *ndis_regvals; struct nch ndis_cfglist_head; int ndis_80211; int ndis_link; + uint32_t ndis_sts; uint32_t ndis_filter; int ndis_if_flags; int ndis_skip; #if __FreeBSD_version < 502113 struct sysctl_ctx_list ndis_ctx; struct sysctl_oid *ndis_tree; #endif int ndis_devidx; interface_type ndis_iftype; driver_object *ndis_dobj; - ndis_work_item ndis_tickitem; - ndis_work_item ndis_startitem; - ndis_work_item ndis_resetitem; + 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; - struct ndis_shmem *ndis_shlist; + list_entry ndis_shlist; bus_dma_tag_t ndis_mtag; bus_dma_tag_t ndis_ttag; bus_dmamap_t *ndis_mmaps; bus_dmamap_t *ndis_tmaps; int ndis_mmapcnt; + struct ndis_evt ndis_evt[NDIS_EVENTS]; + int ndis_evtpidx; + int ndis_evtcidx; + struct ifqueue ndis_rxqueue; + kspin_lock ndis_rxlock; }; -#define NDIS_LOCK(_sc) mtx_lock(&(_sc)->ndis_mtx) -#define NDIS_UNLOCK(_sc) mtx_unlock(&(_sc)->ndis_mtx) +#define NDIS_LOCK(_sc) KeAcquireSpinLock(&(_sc)->ndis_spinlock, \ + &(_sc)->ndis_irql); +#define NDIS_UNLOCK(_sc) KeReleaseSpinLock(&(_sc)->ndis_spinlock, \ + (_sc)->ndis_irql); + +/* + * Backwards compatibility defines. + */ + +#ifndef IF_ADDR_LOCK +#define IF_ADDR_LOCK(x) +#define IF_ADDR_UNLOCK(x) +#endif + +#ifndef IFF_DRV_OACTIVE +#define IFF_DRV_OACTIVE IFF_OACTIVE +#define IFF_DRV_RUNNING IFF_RUNNING +#define if_drv_flags if_flags +#endif + +#ifndef ic_def_txkey +#define ic_def_txkey ic_wep_txkey +#define wk_keylen wk_len +#endif + +#ifndef SIOCGDRVSPEC +#define SIOCSDRVSPEC _IOW('i', 123, struct ifreq) /* set driver-specific + parameters */ +#define SIOCGDRVSPEC _IOWR('i', 123, struct ifreq) /* get driver-specific + parameters */ +#endif