1139743Simp/*- 2123474Swpaul * Copyright (c) 2003 3123474Swpaul * Bill Paul <wpaul@windriver.com>. All rights reserved. 4123474Swpaul * 5123474Swpaul * Redistribution and use in source and binary forms, with or without 6123474Swpaul * modification, are permitted provided that the following conditions 7123474Swpaul * are met: 8123474Swpaul * 1. Redistributions of source code must retain the above copyright 9123474Swpaul * notice, this list of conditions and the following disclaimer. 10123474Swpaul * 2. Redistributions in binary form must reproduce the above copyright 11123474Swpaul * notice, this list of conditions and the following disclaimer in the 12123474Swpaul * documentation and/or other materials provided with the distribution. 13123474Swpaul * 3. All advertising materials mentioning features or use of this software 14123474Swpaul * must display the following acknowledgement: 15123474Swpaul * This product includes software developed by Bill Paul. 16123474Swpaul * 4. Neither the name of the author nor the names of any co-contributors 17123474Swpaul * may be used to endorse or promote products derived from this software 18123474Swpaul * without specific prior written permission. 19123474Swpaul * 20123474Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21123474Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22123474Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23123474Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 24123474Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25123474Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26123474Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27123474Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28123474Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29123474Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30123474Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 31123474Swpaul */ 32123474Swpaul 33123474Swpaul#include <sys/cdefs.h> 34123474Swpaul__FBSDID("$FreeBSD$"); 35123474Swpaul 36123474Swpaul#include <sys/param.h> 37124697Swpaul#include <sys/systm.h> 38124697Swpaul#include <sys/unistd.h> 39123474Swpaul#include <sys/types.h> 40123474Swpaul#include <sys/errno.h> 41123474Swpaul#include <sys/callout.h> 42123474Swpaul#include <sys/socket.h> 43123474Swpaul#include <sys/queue.h> 44123474Swpaul#include <sys/sysctl.h> 45124697Swpaul#include <sys/proc.h> 46123474Swpaul#include <sys/malloc.h> 47123474Swpaul#include <sys/lock.h> 48123474Swpaul#include <sys/mutex.h> 49123474Swpaul#include <sys/conf.h> 50123474Swpaul 51123474Swpaul#include <sys/kernel.h> 52129970Swpaul#include <sys/module.h> 53124697Swpaul#include <sys/kthread.h> 54123474Swpaul#include <machine/bus.h> 55123474Swpaul#include <machine/resource.h> 56123474Swpaul#include <sys/bus.h> 57123474Swpaul#include <sys/rman.h> 58123474Swpaul 59123474Swpaul#include <net/if.h> 60123474Swpaul#include <net/if_arp.h> 61123474Swpaul#include <net/ethernet.h> 62123474Swpaul#include <net/if_dl.h> 63123474Swpaul#include <net/if_media.h> 64123474Swpaul 65123695Swpaul#include <net80211/ieee80211_var.h> 66123695Swpaul#include <net80211/ieee80211_ioctl.h> 67123695Swpaul 68189488Sweongyo#include <dev/usb/usb.h> 69194677Sthompsa#include <dev/usb/usbdi.h> 70186507Sweongyo 71123474Swpaul#include <compat/ndis/pe_var.h> 72145485Swpaul#include <compat/ndis/cfg_var.h> 73123474Swpaul#include <compat/ndis/resource_var.h> 74125551Swpaul#include <compat/ndis/ntoskrnl_var.h> 75123474Swpaul#include <compat/ndis/ndis_var.h> 76123474Swpaul#include <compat/ndis/hal_var.h> 77142399Swpaul#include <compat/ndis/usbd_var.h> 78123474Swpaul#include <dev/if_ndis/if_ndisvar.h> 79123474Swpaul 80123474Swpaul#define NDIS_DUMMY_PATH "\\\\some\\bogus\\path" 81273736Shselasky#define NDIS_FLAG_RDONLY 1 82123474Swpaul 83144888Swpaulstatic void ndis_status_func(ndis_handle, ndis_status, void *, uint32_t); 84144888Swpaulstatic void ndis_statusdone_func(ndis_handle); 85144888Swpaulstatic void ndis_setdone_func(ndis_handle, ndis_status); 86144888Swpaulstatic void ndis_getdone_func(ndis_handle, ndis_status); 87144888Swpaulstatic void ndis_resetdone_func(ndis_handle, ndis_status, uint8_t); 88144888Swpaulstatic void ndis_sendrsrcavail_func(ndis_handle); 89151207Swpaulstatic void ndis_intrsetup(kdpc *, device_object *, 90144174Swpaul irp *, struct ndis_softc *); 91151451Swpaulstatic void ndis_return(device_object *, void *); 92123474Swpaul 93141963Swpaulstatic image_patch_table kernndis_functbl[] = { 94144888Swpaul IMPORT_SFUNC(ndis_status_func, 4), 95144888Swpaul IMPORT_SFUNC(ndis_statusdone_func, 1), 96144888Swpaul IMPORT_SFUNC(ndis_setdone_func, 2), 97144888Swpaul IMPORT_SFUNC(ndis_getdone_func, 2), 98144888Swpaul IMPORT_SFUNC(ndis_resetdone_func, 3), 99144888Swpaul IMPORT_SFUNC(ndis_sendrsrcavail_func, 1), 100151207Swpaul IMPORT_SFUNC(ndis_intrsetup, 4), 101145895Swpaul IMPORT_SFUNC(ndis_return, 1), 102141963Swpaul 103141963Swpaul { NULL, NULL, NULL } 104141963Swpaul}; 105141963Swpaul 106151248Swpaulstatic struct nd_head ndis_devhead; 107125057Swpaul 108123474Swpaul/* 109123474Swpaul * This allows us to export our symbols to other modules. 110123474Swpaul * Note that we call ourselves 'ndisapi' to avoid a namespace 111123474Swpaul * collision with if_ndis.ko, which internally calls itself 112123474Swpaul * 'ndis.' 113151451Swpaul * 114151451Swpaul * Note: some of the subsystems depend on each other, so the 115151451Swpaul * order in which they're started is important. The order of 116151451Swpaul * importance is: 117151451Swpaul * 118151451Swpaul * HAL - spinlocks and IRQL manipulation 119151451Swpaul * ntoskrnl - DPC and workitem threads, object waiting 120151451Swpaul * windrv - driver/device registration 121151451Swpaul * 122151451Swpaul * The HAL should also be the last thing shut down, since 123151451Swpaul * the ntoskrnl subsystem will use spinlocks right up until 124151451Swpaul * the DPC and workitem threads are terminated. 125123474Swpaul */ 126141524Swpaul 127123474Swpaulstatic int 128123474Swpaulndis_modevent(module_t mod, int cmd, void *arg) 129123474Swpaul{ 130124060Swpaul int error = 0; 131141963Swpaul image_patch_table *patch; 132124060Swpaul 133124060Swpaul switch (cmd) { 134124060Swpaul case MOD_LOAD: 135124122Swpaul /* Initialize subsystems */ 136151451Swpaul hal_libinit(); 137151451Swpaul ntoskrnl_libinit(); 138141524Swpaul windrv_libinit(); 139124122Swpaul ndis_libinit(); 140142399Swpaul usbd_libinit(); 141124122Swpaul 142141963Swpaul patch = kernndis_functbl; 143141963Swpaul while (patch->ipt_func != NULL) { 144141963Swpaul windrv_wrap((funcptr)patch->ipt_func, 145144888Swpaul (funcptr *)&patch->ipt_wrap, 146144888Swpaul patch->ipt_argcnt, patch->ipt_ftype); 147141963Swpaul patch++; 148141963Swpaul } 149141963Swpaul 150125057Swpaul TAILQ_INIT(&ndis_devhead); 151124060Swpaul break; 152124060Swpaul case MOD_SHUTDOWN: 153127311Swpaul if (TAILQ_FIRST(&ndis_devhead) == NULL) { 154125006Swpaul /* Shut down subsystems */ 155125006Swpaul ndis_libfini(); 156142399Swpaul usbd_libfini(); 157141963Swpaul windrv_libfini(); 158151451Swpaul ntoskrnl_libfini(); 159151451Swpaul hal_libfini(); 160124697Swpaul 161141963Swpaul patch = kernndis_functbl; 162141963Swpaul while (patch->ipt_func != NULL) { 163141963Swpaul windrv_unwrap(patch->ipt_wrap); 164141963Swpaul patch++; 165141963Swpaul } 166125006Swpaul } 167125006Swpaul break; 168125006Swpaul case MOD_UNLOAD: 169124122Swpaul /* Shut down subsystems */ 170124122Swpaul ndis_libfini(); 171142399Swpaul usbd_libfini(); 172141524Swpaul windrv_libfini(); 173151451Swpaul ntoskrnl_libfini(); 174151451Swpaul hal_libfini(); 175124122Swpaul 176141963Swpaul patch = kernndis_functbl; 177141963Swpaul while (patch->ipt_func != NULL) { 178141963Swpaul windrv_unwrap(patch->ipt_wrap); 179141963Swpaul patch++; 180141963Swpaul } 181141963Swpaul 182124060Swpaul break; 183124060Swpaul default: 184124060Swpaul error = EINVAL; 185124060Swpaul break; 186124060Swpaul } 187124060Swpaul 188198786Srpaulo return (error); 189123474Swpaul} 190123474SwpaulDEV_MODULE(ndisapi, ndis_modevent, NULL); 191123474SwpaulMODULE_VERSION(ndisapi, 1); 192123474Swpaul 193144888Swpaulstatic void 194124100Swpaulndis_sendrsrcavail_func(adapter) 195124100Swpaul ndis_handle adapter; 196124100Swpaul{ 197124100Swpaul} 198124100Swpaul 199144888Swpaulstatic void 200123474Swpaulndis_status_func(adapter, status, sbuf, slen) 201123474Swpaul ndis_handle adapter; 202123474Swpaul ndis_status status; 203123474Swpaul void *sbuf; 204123474Swpaul uint32_t slen; 205123474Swpaul{ 206124060Swpaul ndis_miniport_block *block; 207141524Swpaul struct ndis_softc *sc; 208141524Swpaul struct ifnet *ifp; 209141524Swpaul 210124060Swpaul block = adapter; 211141524Swpaul sc = device_get_softc(block->nmb_physdeviceobj->do_devext); 212147256Sbrooks ifp = sc->ifp; 213141524Swpaul if (ifp->if_flags & IFF_DEBUG) 214198786Srpaulo device_printf(sc->ndis_dev, "status: %x\n", status); 215123474Swpaul} 216123474Swpaul 217144888Swpaulstatic void 218123474Swpaulndis_statusdone_func(adapter) 219123474Swpaul ndis_handle adapter; 220123474Swpaul{ 221124060Swpaul ndis_miniport_block *block; 222141524Swpaul struct ndis_softc *sc; 223141524Swpaul struct ifnet *ifp; 224141524Swpaul 225124060Swpaul block = adapter; 226141524Swpaul sc = device_get_softc(block->nmb_physdeviceobj->do_devext); 227147256Sbrooks ifp = sc->ifp; 228141524Swpaul if (ifp->if_flags & IFF_DEBUG) 229198786Srpaulo device_printf(sc->ndis_dev, "status complete\n"); 230123474Swpaul} 231123474Swpaul 232144888Swpaulstatic void 233123474Swpaulndis_setdone_func(adapter, status) 234123474Swpaul ndis_handle adapter; 235123474Swpaul ndis_status status; 236123474Swpaul{ 237123695Swpaul ndis_miniport_block *block; 238123695Swpaul block = adapter; 239123695Swpaul 240123695Swpaul block->nmb_setstat = status; 241151248Swpaul KeSetEvent(&block->nmb_setevent, IO_NO_INCREMENT, FALSE); 242123474Swpaul} 243123474Swpaul 244144888Swpaulstatic void 245123535Swpaulndis_getdone_func(adapter, status) 246123535Swpaul ndis_handle adapter; 247123535Swpaul ndis_status status; 248123535Swpaul{ 249123695Swpaul ndis_miniport_block *block; 250123695Swpaul block = adapter; 251123695Swpaul 252123695Swpaul block->nmb_getstat = status; 253151248Swpaul KeSetEvent(&block->nmb_getevent, IO_NO_INCREMENT, FALSE); 254123535Swpaul} 255123535Swpaul 256144888Swpaulstatic void 257189004Srdivackyndis_resetdone_func(ndis_handle adapter, ndis_status status, 258189004Srdivacky uint8_t addressingreset) 259123474Swpaul{ 260124060Swpaul ndis_miniport_block *block; 261141524Swpaul struct ndis_softc *sc; 262141524Swpaul struct ifnet *ifp; 263141524Swpaul 264124060Swpaul block = adapter; 265141524Swpaul sc = device_get_softc(block->nmb_physdeviceobj->do_devext); 266147256Sbrooks ifp = sc->ifp; 267124060Swpaul 268141524Swpaul if (ifp->if_flags & IFF_DEBUG) 269198786Srpaulo device_printf(sc->ndis_dev, "reset done...\n"); 270151248Swpaul KeSetEvent(&block->nmb_resetevent, IO_NO_INCREMENT, FALSE); 271123474Swpaul} 272123474Swpaul 273123474Swpaulint 274123474Swpaulndis_create_sysctls(arg) 275123474Swpaul void *arg; 276123474Swpaul{ 277123474Swpaul struct ndis_softc *sc; 278123474Swpaul ndis_cfg *vals; 279123474Swpaul char buf[256]; 280132973Swpaul struct sysctl_oid *oidp; 281132973Swpaul struct sysctl_ctx_entry *e; 282123474Swpaul 283123474Swpaul if (arg == NULL) 284198786Srpaulo return (EINVAL); 285123474Swpaul 286123474Swpaul sc = arg; 287123474Swpaul vals = sc->ndis_regvals; 288123474Swpaul 289123474Swpaul TAILQ_INIT(&sc->ndis_cfglist_head); 290123474Swpaul 291123474Swpaul /* Add the driver-specific registry keys. */ 292123474Swpaul 293123474Swpaul while(1) { 294123474Swpaul if (vals->nc_cfgkey == NULL) 295123474Swpaul break; 296145895Swpaul 297123620Swpaul if (vals->nc_idx != sc->ndis_devidx) { 298123620Swpaul vals++; 299123620Swpaul continue; 300123620Swpaul } 301132973Swpaul 302132973Swpaul /* See if we already have a sysctl with this name */ 303132973Swpaul 304132973Swpaul oidp = NULL; 305132973Swpaul TAILQ_FOREACH(e, device_get_sysctl_ctx(sc->ndis_dev), link) { 306189488Sweongyo oidp = e->entry; 307168421Spjd if (strcasecmp(oidp->oid_name, vals->nc_cfgkey) == 0) 308132973Swpaul break; 309132973Swpaul oidp = NULL; 310132973Swpaul } 311132973Swpaul 312132973Swpaul if (oidp != NULL) { 313132973Swpaul vals++; 314132973Swpaul continue; 315132973Swpaul } 316132973Swpaul 317151207Swpaul ndis_add_sysctl(sc, vals->nc_cfgkey, vals->nc_cfgdesc, 318151207Swpaul vals->nc_val, CTLFLAG_RW); 319123474Swpaul vals++; 320123474Swpaul } 321123474Swpaul 322123474Swpaul /* Now add a couple of builtin keys. */ 323123474Swpaul 324123474Swpaul /* 325123474Swpaul * Environment can be either Windows (0) or WindowsNT (1). 326123474Swpaul * We qualify as the latter. 327123474Swpaul */ 328123474Swpaul ndis_add_sysctl(sc, "Environment", 329273736Shselasky "Windows environment", "1", NDIS_FLAG_RDONLY); 330123474Swpaul 331123474Swpaul /* NDIS version should be 5.1. */ 332123474Swpaul ndis_add_sysctl(sc, "NdisVersion", 333273736Shselasky "NDIS API Version", "0x00050001", NDIS_FLAG_RDONLY); 334123474Swpaul 335216049Sbschmidt /* 336216049Sbschmidt * Some miniport drivers rely on the existence of the SlotNumber, 337216049Sbschmidt * NetCfgInstanceId and DriverDesc keys. 338216049Sbschmidt */ 339273736Shselasky ndis_add_sysctl(sc, "SlotNumber", "Slot Numer", "01", NDIS_FLAG_RDONLY); 340216049Sbschmidt ndis_add_sysctl(sc, "NetCfgInstanceId", "NetCfgInstanceId", 341273736Shselasky "{12345678-1234-5678-CAFE0-123456789ABC}", NDIS_FLAG_RDONLY); 342216049Sbschmidt ndis_add_sysctl(sc, "DriverDesc", "Driver Description", 343273736Shselasky "NDIS Network Adapter", NDIS_FLAG_RDONLY); 344216049Sbschmidt 345123474Swpaul /* Bus type (PCI, PCMCIA, etc...) */ 346124272Swpaul sprintf(buf, "%d", (int)sc->ndis_iftype); 347273736Shselasky ndis_add_sysctl(sc, "BusType", "Bus Type", buf, NDIS_FLAG_RDONLY); 348123474Swpaul 349123474Swpaul if (sc->ndis_res_io != NULL) { 350124272Swpaul sprintf(buf, "0x%lx", rman_get_start(sc->ndis_res_io)); 351123474Swpaul ndis_add_sysctl(sc, "IOBaseAddress", 352273736Shselasky "Base I/O Address", buf, NDIS_FLAG_RDONLY); 353123474Swpaul } 354123474Swpaul 355123474Swpaul if (sc->ndis_irq != NULL) { 356124272Swpaul sprintf(buf, "%lu", rman_get_start(sc->ndis_irq)); 357123474Swpaul ndis_add_sysctl(sc, "InterruptNumber", 358273736Shselasky "Interrupt Number", buf, NDIS_FLAG_RDONLY); 359123474Swpaul } 360123474Swpaul 361198786Srpaulo return (0); 362123474Swpaul} 363123474Swpaul 364123474Swpaulint 365273736Shselaskyndis_add_sysctl(arg, key, desc, val, flag_rdonly) 366123474Swpaul void *arg; 367123474Swpaul char *key; 368123474Swpaul char *desc; 369123474Swpaul char *val; 370273736Shselasky int flag_rdonly; 371123474Swpaul{ 372123474Swpaul struct ndis_softc *sc; 373123474Swpaul struct ndis_cfglist *cfg; 374123474Swpaul char descstr[256]; 375123474Swpaul 376123474Swpaul sc = arg; 377123474Swpaul 378123474Swpaul cfg = malloc(sizeof(struct ndis_cfglist), M_DEVBUF, M_NOWAIT|M_ZERO); 379123474Swpaul 380151207Swpaul if (cfg == NULL) { 381151207Swpaul printf("failed for %s\n", key); 382198786Srpaulo return (ENOMEM); 383151207Swpaul } 384123474Swpaul 385123474Swpaul cfg->ndis_cfg.nc_cfgkey = strdup(key, M_DEVBUF); 386123474Swpaul if (desc == NULL) { 387123474Swpaul snprintf(descstr, sizeof(descstr), "%s (dynamic)", key); 388123474Swpaul cfg->ndis_cfg.nc_cfgdesc = strdup(descstr, M_DEVBUF); 389123474Swpaul } else 390123474Swpaul cfg->ndis_cfg.nc_cfgdesc = strdup(desc, M_DEVBUF); 391123474Swpaul strcpy(cfg->ndis_cfg.nc_val, val); 392123474Swpaul 393123474Swpaul TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link); 394123474Swpaul 395273736Shselasky if (flag_rdonly != 0) { 396273736Shselasky cfg->ndis_oid = 397273736Shselasky SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev), 398273736Shselasky SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)), 399273736Shselasky OID_AUTO, cfg->ndis_cfg.nc_cfgkey, CTLFLAG_RD, 400273736Shselasky cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val), 401273736Shselasky cfg->ndis_cfg.nc_cfgdesc); 402273736Shselasky } else { 403273736Shselasky cfg->ndis_oid = 404273736Shselasky SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev), 405273736Shselasky SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)), 406273736Shselasky OID_AUTO, cfg->ndis_cfg.nc_cfgkey, CTLFLAG_RW, 407273736Shselasky cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val), 408273736Shselasky cfg->ndis_cfg.nc_cfgdesc); 409273736Shselasky } 410198786Srpaulo return (0); 411123474Swpaul} 412123474Swpaul 413151207Swpaul/* 414151207Swpaul * Somewhere, somebody decided "hey, let's automatically create 415151207Swpaul * a sysctl tree for each device instance as it's created -- it'll 416151207Swpaul * make life so much easier!" Lies. Why must they turn the kernel 417151207Swpaul * into a house of lies? 418151207Swpaul */ 419151207Swpaul 420123474Swpaulint 421123474Swpaulndis_flush_sysctls(arg) 422123474Swpaul void *arg; 423123474Swpaul{ 424123474Swpaul struct ndis_softc *sc; 425123474Swpaul struct ndis_cfglist *cfg; 426151207Swpaul struct sysctl_ctx_list *clist; 427123474Swpaul 428123474Swpaul sc = arg; 429123474Swpaul 430151207Swpaul clist = device_get_sysctl_ctx(sc->ndis_dev); 431151207Swpaul 432123474Swpaul while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) { 433123474Swpaul cfg = TAILQ_FIRST(&sc->ndis_cfglist_head); 434123474Swpaul TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link); 435151207Swpaul sysctl_ctx_entry_del(clist, cfg->ndis_oid); 436151207Swpaul sysctl_remove_oid(cfg->ndis_oid, 1, 0); 437123474Swpaul free(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF); 438123474Swpaul free(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF); 439123474Swpaul free(cfg, M_DEVBUF); 440123474Swpaul } 441123474Swpaul 442198786Srpaulo return (0); 443123474Swpaul} 444123474Swpaul 445216242Sbschmidtvoid * 446216242Sbschmidtndis_get_routine_address(functbl, name) 447216242Sbschmidt struct image_patch_table *functbl; 448216242Sbschmidt char *name; 449216242Sbschmidt{ 450216242Sbschmidt int i; 451216242Sbschmidt 452216242Sbschmidt for (i = 0; functbl[i].ipt_name != NULL; i++) 453216242Sbschmidt if (strcmp(name, functbl[i].ipt_name) == 0) 454216242Sbschmidt return (functbl[i].ipt_wrap); 455216242Sbschmidt return (NULL); 456216242Sbschmidt} 457216242Sbschmidt 458128546Swpaulstatic void 459151451Swpaulndis_return(dobj, arg) 460151451Swpaul device_object *dobj; 461123474Swpaul void *arg; 462123474Swpaul{ 463151451Swpaul ndis_miniport_block *block; 464151451Swpaul ndis_miniport_characteristics *ch; 465144888Swpaul ndis_return_handler returnfunc; 466123474Swpaul ndis_handle adapter; 467123535Swpaul ndis_packet *p; 468128229Swpaul uint8_t irql; 469151451Swpaul list_entry *l; 470123474Swpaul 471151451Swpaul block = arg; 472151451Swpaul ch = IoGetDriverObjectExtension(dobj->do_drvobj, (void *)1); 473151451Swpaul 474128546Swpaul p = arg; 475151451Swpaul adapter = block->nmb_miniportadapterctx; 476128546Swpaul 477128546Swpaul if (adapter == NULL) 478128546Swpaul return; 479128546Swpaul 480151451Swpaul returnfunc = ch->nmc_return_packet_func; 481144174Swpaul 482151451Swpaul KeAcquireSpinLock(&block->nmb_returnlock, &irql); 483151451Swpaul while (!IsListEmpty(&block->nmb_returnlist)) { 484151451Swpaul l = RemoveHeadList((&block->nmb_returnlist)); 485151451Swpaul p = CONTAINING_RECORD(l, ndis_packet, np_list); 486151451Swpaul InitializeListHead((&p->np_list)); 487151451Swpaul KeReleaseSpinLock(&block->nmb_returnlock, irql); 488151451Swpaul MSCALL2(returnfunc, adapter, p); 489151451Swpaul KeAcquireSpinLock(&block->nmb_returnlock, &irql); 490151451Swpaul } 491151451Swpaul KeReleaseSpinLock(&block->nmb_returnlock, irql); 492128546Swpaul} 493128546Swpaul 494254842Sandreint 495254842Sandrendis_return_packet(struct mbuf *m, void *buf, void *arg) 496128546Swpaul{ 497128546Swpaul ndis_packet *p; 498151451Swpaul ndis_miniport_block *block; 499128546Swpaul 500123826Swpaul if (arg == NULL) 501254842Sandre return (EXT_FREE_OK); 502123474Swpaul 503123826Swpaul p = arg; 504123535Swpaul 505123535Swpaul /* Decrement refcount. */ 506123826Swpaul p->np_refcnt--; 507123535Swpaul 508123535Swpaul /* Release packet when refcount hits zero, otherwise return. */ 509123826Swpaul if (p->np_refcnt) 510254842Sandre return (EXT_FREE_OK); 511123536Swpaul 512151451Swpaul block = ((struct ndis_softc *)p->np_softc)->ndis_block; 513123858Swpaul 514151451Swpaul KeAcquireSpinLockAtDpcLevel(&block->nmb_returnlock); 515151451Swpaul InitializeListHead((&p->np_list)); 516151451Swpaul InsertHeadList((&block->nmb_returnlist), (&p->np_list)); 517151451Swpaul KeReleaseSpinLockFromDpcLevel(&block->nmb_returnlock); 518151451Swpaul 519151451Swpaul IoQueueWorkItem(block->nmb_returnitem, 520151451Swpaul (io_workitem_func)kernndis_functbl[7].ipt_wrap, 521151451Swpaul WORKQUEUE_CRITICAL, block); 522254842Sandre 523254842Sandre return (EXT_FREE_OK); 524123474Swpaul} 525123474Swpaul 526123848Swpaulvoid 527123848Swpaulndis_free_bufs(b0) 528123848Swpaul ndis_buffer *b0; 529123848Swpaul{ 530123848Swpaul ndis_buffer *next; 531123848Swpaul 532123848Swpaul if (b0 == NULL) 533123848Swpaul return; 534123848Swpaul 535123848Swpaul while(b0 != NULL) { 536140751Swpaul next = b0->mdl_next; 537142530Swpaul IoFreeMdl(b0); 538123848Swpaul b0 = next; 539123848Swpaul } 540123848Swpaul} 541192692Santoine 542123848Swpaulvoid 543123848Swpaulndis_free_packet(p) 544123848Swpaul ndis_packet *p; 545123848Swpaul{ 546123848Swpaul if (p == NULL) 547123848Swpaul return; 548123848Swpaul 549123848Swpaul ndis_free_bufs(p->np_private.npp_head); 550141963Swpaul NdisFreePacket(p); 551123848Swpaul} 552123848Swpaul 553123474Swpaulint 554123474Swpaulndis_convert_res(arg) 555123474Swpaul void *arg; 556123474Swpaul{ 557123474Swpaul struct ndis_softc *sc; 558123474Swpaul ndis_resource_list *rl = NULL; 559123474Swpaul cm_partial_resource_desc *prd = NULL; 560123474Swpaul ndis_miniport_block *block; 561123976Swpaul device_t dev; 562123976Swpaul struct resource_list *brl; 563144176Swpaul struct resource_list_entry *brle; 564189488Sweongyo int error = 0; 565123474Swpaul 566123474Swpaul sc = arg; 567141524Swpaul block = sc->ndis_block; 568123976Swpaul dev = sc->ndis_dev; 569123474Swpaul 570123474Swpaul rl = malloc(sizeof(ndis_resource_list) + 571123474Swpaul (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)), 572123474Swpaul M_DEVBUF, M_NOWAIT|M_ZERO); 573123474Swpaul 574123474Swpaul if (rl == NULL) 575198786Srpaulo return (ENOMEM); 576123474Swpaul 577123474Swpaul rl->cprl_version = 5; 578247595Sdelphij rl->cprl_revision = 1; 579123474Swpaul rl->cprl_count = sc->ndis_rescnt; 580123474Swpaul prd = rl->cprl_partial_descs; 581123474Swpaul 582131953Swpaul brl = BUS_GET_RESOURCE_LIST(dev, dev); 583131953Swpaul 584123976Swpaul if (brl != NULL) { 585127411Swpaul 586144238Swpaul STAILQ_FOREACH(brle, brl, link) { 587123976Swpaul switch (brle->type) { 588123976Swpaul case SYS_RES_IOPORT: 589123976Swpaul prd->cprd_type = CmResourceTypePort; 590127552Swpaul prd->cprd_flags = CM_RESOURCE_PORT_IO; 591127552Swpaul prd->cprd_sharedisp = 592127552Swpaul CmResourceShareDeviceExclusive; 593123976Swpaul prd->u.cprd_port.cprd_start.np_quad = 594123976Swpaul brle->start; 595123976Swpaul prd->u.cprd_port.cprd_len = brle->count; 596123976Swpaul break; 597123976Swpaul case SYS_RES_MEMORY: 598123976Swpaul prd->cprd_type = CmResourceTypeMemory; 599127552Swpaul prd->cprd_flags = 600127552Swpaul CM_RESOURCE_MEMORY_READ_WRITE; 601127552Swpaul prd->cprd_sharedisp = 602127552Swpaul CmResourceShareDeviceExclusive; 603218985Sbrucec prd->u.cprd_mem.cprd_start.np_quad = 604123976Swpaul brle->start; 605218985Sbrucec prd->u.cprd_mem.cprd_len = brle->count; 606123976Swpaul break; 607123976Swpaul case SYS_RES_IRQ: 608123976Swpaul prd->cprd_type = CmResourceTypeInterrupt; 609127552Swpaul prd->cprd_flags = 0; 610151451Swpaul /* 611151451Swpaul * Always mark interrupt resources as 612151451Swpaul * shared, since in our implementation, 613151451Swpaul * they will be. 614151451Swpaul */ 615127552Swpaul prd->cprd_sharedisp = 616151451Swpaul CmResourceShareShared; 617123976Swpaul prd->u.cprd_intr.cprd_level = brle->start; 618123976Swpaul prd->u.cprd_intr.cprd_vector = brle->start; 619123976Swpaul prd->u.cprd_intr.cprd_affinity = 0; 620123976Swpaul break; 621123976Swpaul default: 622123976Swpaul break; 623123976Swpaul } 624123976Swpaul prd++; 625123976Swpaul } 626123474Swpaul } 627123474Swpaul 628123474Swpaul block->nmb_rlist = rl; 629123474Swpaul 630198786Srpaulo return (error); 631123474Swpaul} 632123474Swpaul 633123474Swpaul/* 634123474Swpaul * Map an NDIS packet to an mbuf list. When an NDIS driver receives a 635123474Swpaul * packet, it will hand it to us in the form of an ndis_packet, 636123474Swpaul * which we need to convert to an mbuf that is then handed off 637123474Swpaul * to the stack. Note: we configure the mbuf list so that it uses 638123474Swpaul * the memory regions specified by the ndis_buffer structures in 639123474Swpaul * the ndis_packet as external storage. In most cases, this will 640123474Swpaul * point to a memory region allocated by the driver (either by 641123474Swpaul * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect 642123474Swpaul * the driver to handle free()ing this region for is, so we set up 643123474Swpaul * a dummy no-op free handler for it. 644123474Swpaul */ 645123474Swpaul 646123474Swpaulint 647123474Swpaulndis_ptom(m0, p) 648123474Swpaul struct mbuf **m0; 649123474Swpaul ndis_packet *p; 650123474Swpaul{ 651151207Swpaul struct mbuf *m = NULL, *prev = NULL; 652123474Swpaul ndis_buffer *buf; 653123474Swpaul ndis_packet_private *priv; 654123474Swpaul uint32_t totlen = 0; 655151207Swpaul struct ifnet *ifp; 656151207Swpaul struct ether_header *eh; 657151207Swpaul int diff; 658123474Swpaul 659123474Swpaul if (p == NULL || m0 == NULL) 660198786Srpaulo return (EINVAL); 661123474Swpaul 662123474Swpaul priv = &p->np_private; 663123474Swpaul buf = priv->npp_head; 664123826Swpaul p->np_refcnt = 0; 665123474Swpaul 666140751Swpaul for (buf = priv->npp_head; buf != NULL; buf = buf->mdl_next) { 667123474Swpaul if (buf == priv->npp_head) 668248324Sglebius m = m_gethdr(M_NOWAIT, MT_DATA); 669123474Swpaul else 670248324Sglebius m = m_get(M_NOWAIT, MT_DATA); 671123474Swpaul if (m == NULL) { 672123474Swpaul m_freem(*m0); 673123474Swpaul *m0 = NULL; 674198786Srpaulo return (ENOBUFS); 675123474Swpaul } 676140751Swpaul m->m_len = MmGetMdlByteCount(buf); 677140751Swpaul m->m_data = MmGetMdlVirtualAddress(buf); 678123535Swpaul MEXTADD(m, m->m_data, m->m_len, ndis_return_packet, 679175872Sphk m->m_data, p, 0, EXT_NDIS); 680123826Swpaul p->np_refcnt++; 681151207Swpaul 682123474Swpaul totlen += m->m_len; 683149632Sandre if (m->m_flags & M_PKTHDR) 684123474Swpaul *m0 = m; 685123474Swpaul else 686123474Swpaul prev->m_next = m; 687123474Swpaul prev = m; 688123474Swpaul } 689123474Swpaul 690151207Swpaul /* 691151207Swpaul * This is a hack to deal with the Marvell 8335 driver 692151207Swpaul * which, when associated with an AP in WPA-PSK mode, 693151207Swpaul * seems to overpad its frames by 8 bytes. I don't know 694151207Swpaul * that the extra 8 bytes are for, and they're not there 695151207Swpaul * in open mode, so for now clamp the frame size at 1514 696151207Swpaul * until I can figure out how to deal with this properly, 697151207Swpaul * otherwise if_ethersubr() will spank us by discarding 698151207Swpaul * the 'oversize' frames. 699151207Swpaul */ 700151207Swpaul 701151207Swpaul eh = mtod((*m0), struct ether_header *); 702151207Swpaul ifp = ((struct ndis_softc *)p->np_softc)->ifp; 703151207Swpaul if (totlen > ETHER_MAX_FRAME(ifp, eh->ether_type, FALSE)) { 704151207Swpaul diff = totlen - ETHER_MAX_FRAME(ifp, eh->ether_type, FALSE); 705151207Swpaul totlen -= diff; 706151207Swpaul m->m_len -= diff; 707151207Swpaul } 708123474Swpaul (*m0)->m_pkthdr.len = totlen; 709123474Swpaul 710198786Srpaulo return (0); 711123474Swpaul} 712123474Swpaul 713123474Swpaul/* 714144174Swpaul * Create an NDIS packet from an mbuf chain. 715123474Swpaul * This is used mainly when transmitting packets, where we need 716123474Swpaul * to turn an mbuf off an interface's send queue and transform it 717123474Swpaul * into an NDIS packet which will be fed into the NDIS driver's 718123474Swpaul * send routine. 719123474Swpaul * 720123474Swpaul * NDIS packets consist of two parts: an ndis_packet structure, 721123474Swpaul * which is vaguely analagous to the pkthdr portion of an mbuf, 722123474Swpaul * and one or more ndis_buffer structures, which define the 723123474Swpaul * actual memory segments in which the packet data resides. 724123474Swpaul * We need to allocate one ndis_buffer for each mbuf in a chain, 725123474Swpaul * plus one ndis_packet as the header. 726123474Swpaul */ 727123474Swpaul 728123474Swpaulint 729123474Swpaulndis_mtop(m0, p) 730123474Swpaul struct mbuf *m0; 731123474Swpaul ndis_packet **p; 732123474Swpaul{ 733123474Swpaul struct mbuf *m; 734123474Swpaul ndis_buffer *buf = NULL, *prev = NULL; 735123474Swpaul ndis_packet_private *priv; 736123474Swpaul 737141963Swpaul if (p == NULL || *p == NULL || m0 == NULL) 738198786Srpaulo return (EINVAL); 739123474Swpaul 740123474Swpaul priv = &(*p)->np_private; 741123474Swpaul priv->npp_totlen = m0->m_pkthdr.len; 742123474Swpaul 743123474Swpaul for (m = m0; m != NULL; m = m->m_next) { 744123810Salfred if (m->m_len == 0) 745123474Swpaul continue; 746142530Swpaul buf = IoAllocateMdl(m->m_data, m->m_len, FALSE, FALSE, NULL); 747123474Swpaul if (buf == NULL) { 748123474Swpaul ndis_free_packet(*p); 749123474Swpaul *p = NULL; 750198786Srpaulo return (ENOMEM); 751123474Swpaul } 752151207Swpaul MmBuildMdlForNonPagedPool(buf); 753123474Swpaul 754123474Swpaul if (priv->npp_head == NULL) 755123474Swpaul priv->npp_head = buf; 756123474Swpaul else 757140751Swpaul prev->mdl_next = buf; 758123474Swpaul prev = buf; 759123474Swpaul } 760123474Swpaul 761123474Swpaul priv->npp_tail = buf; 762123474Swpaul 763198786Srpaulo return (0); 764123474Swpaul} 765123474Swpaul 766123474Swpaulint 767123474Swpaulndis_get_supported_oids(arg, oids, oidcnt) 768123474Swpaul void *arg; 769123474Swpaul ndis_oid **oids; 770123474Swpaul int *oidcnt; 771123474Swpaul{ 772123474Swpaul int len, rval; 773123474Swpaul ndis_oid *o; 774123474Swpaul 775123474Swpaul if (arg == NULL || oids == NULL || oidcnt == NULL) 776198786Srpaulo return (EINVAL); 777123474Swpaul len = 0; 778123474Swpaul ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len); 779123474Swpaul 780123474Swpaul o = malloc(len, M_DEVBUF, M_NOWAIT); 781123474Swpaul if (o == NULL) 782198786Srpaulo return (ENOMEM); 783123474Swpaul 784123474Swpaul rval = ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, o, &len); 785123474Swpaul 786123474Swpaul if (rval) { 787123474Swpaul free(o, M_DEVBUF); 788198786Srpaulo return (rval); 789123474Swpaul } 790123474Swpaul 791123474Swpaul *oids = o; 792123474Swpaul *oidcnt = len / 4; 793123474Swpaul 794198786Srpaulo return (0); 795123474Swpaul} 796123474Swpaul 797123474Swpaulint 798123474Swpaulndis_set_info(arg, oid, buf, buflen) 799123474Swpaul void *arg; 800123474Swpaul ndis_oid oid; 801123474Swpaul void *buf; 802123474Swpaul int *buflen; 803123474Swpaul{ 804123474Swpaul struct ndis_softc *sc; 805123474Swpaul ndis_status rval; 806123474Swpaul ndis_handle adapter; 807144888Swpaul ndis_setinfo_handler setfunc; 808123474Swpaul uint32_t byteswritten = 0, bytesneeded = 0; 809128229Swpaul uint8_t irql; 810151248Swpaul uint64_t duetime; 811123474Swpaul 812143204Swpaul /* 813143204Swpaul * According to the NDIS spec, MiniportQueryInformation() 814143204Swpaul * and MiniportSetInformation() requests are handled serially: 815143204Swpaul * once one request has been issued, we must wait for it to 816143204Swpaul * finish before allowing another request to proceed. 817143204Swpaul */ 818143204Swpaul 819123474Swpaul sc = arg; 820143204Swpaul 821180754Sweongyo KeResetEvent(&sc->ndis_block->nmb_setevent); 822180754Sweongyo 823144174Swpaul KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); 824144174Swpaul 825151248Swpaul if (sc->ndis_block->nmb_pendingreq != NULL) { 826151248Swpaul KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); 827143204Swpaul panic("ndis_set_info() called while other request pending"); 828151248Swpaul } else 829143204Swpaul sc->ndis_block->nmb_pendingreq = (ndis_request *)sc; 830143204Swpaul 831141524Swpaul setfunc = sc->ndis_chars->nmc_setinfo_func; 832141524Swpaul adapter = sc->ndis_block->nmb_miniportadapterctx; 833123474Swpaul 834145898Swpaul if (adapter == NULL || setfunc == NULL || 835145898Swpaul sc->ndis_block->nmb_devicectx == NULL) { 836144317Swpaul sc->ndis_block->nmb_pendingreq = NULL; 837144174Swpaul KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); 838198786Srpaulo return (ENXIO); 839143204Swpaul } 840125676Swpaul 841141963Swpaul rval = MSCALL6(setfunc, adapter, oid, buf, *buflen, 842123474Swpaul &byteswritten, &bytesneeded); 843123474Swpaul 844144174Swpaul sc->ndis_block->nmb_pendingreq = NULL; 845144174Swpaul 846144174Swpaul KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); 847144174Swpaul 848123695Swpaul if (rval == NDIS_STATUS_PENDING) { 849151248Swpaul /* Wait up to 5 seconds. */ 850151248Swpaul duetime = (5 * 1000000) * -10; 851151248Swpaul KeWaitForSingleObject(&sc->ndis_block->nmb_setevent, 852151248Swpaul 0, 0, FALSE, &duetime); 853141524Swpaul rval = sc->ndis_block->nmb_setstat; 854145898Swpaul } 855123695Swpaul 856123474Swpaul if (byteswritten) 857123474Swpaul *buflen = byteswritten; 858123474Swpaul if (bytesneeded) 859123474Swpaul *buflen = bytesneeded; 860123474Swpaul 861123474Swpaul if (rval == NDIS_STATUS_INVALID_LENGTH) 862198786Srpaulo return (ENOSPC); 863123474Swpaul 864123474Swpaul if (rval == NDIS_STATUS_INVALID_OID) 865198786Srpaulo return (EINVAL); 866123474Swpaul 867123474Swpaul if (rval == NDIS_STATUS_NOT_SUPPORTED || 868123474Swpaul rval == NDIS_STATUS_NOT_ACCEPTED) 869198786Srpaulo return (ENOTSUP); 870123474Swpaul 871124809Swpaul if (rval != NDIS_STATUS_SUCCESS) 872198786Srpaulo return (ENODEV); 873124809Swpaul 874198786Srpaulo return (0); 875123474Swpaul} 876123474Swpaul 877124202Swpaultypedef void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status); 878124202Swpaul 879123474Swpaulint 880123474Swpaulndis_send_packets(arg, packets, cnt) 881123474Swpaul void *arg; 882123474Swpaul ndis_packet **packets; 883123474Swpaul int cnt; 884123474Swpaul{ 885123474Swpaul struct ndis_softc *sc; 886123474Swpaul ndis_handle adapter; 887144888Swpaul ndis_sendmulti_handler sendfunc; 888144888Swpaul ndis_senddone_func senddonefunc; 889124202Swpaul int i; 890123858Swpaul ndis_packet *p; 891170487Smjacob uint8_t irql = 0; 892123474Swpaul 893123474Swpaul sc = arg; 894141524Swpaul adapter = sc->ndis_block->nmb_miniportadapterctx; 895125718Swpaul if (adapter == NULL) 896198786Srpaulo return (ENXIO); 897141524Swpaul sendfunc = sc->ndis_chars->nmc_sendmulti_func; 898141524Swpaul senddonefunc = sc->ndis_block->nmb_senddone_func; 899143204Swpaul 900144174Swpaul if (NDIS_SERIALIZED(sc->ndis_block)) 901143204Swpaul KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); 902144174Swpaul 903141963Swpaul MSCALL3(sendfunc, adapter, packets, cnt); 904123474Swpaul 905123858Swpaul for (i = 0; i < cnt; i++) { 906123858Swpaul p = packets[i]; 907124005Swpaul /* 908124005Swpaul * Either the driver already handed the packet to 909124005Swpaul * ndis_txeof() due to a failure, or it wants to keep 910124005Swpaul * it and release it asynchronously later. Skip to the 911124005Swpaul * next one. 912124005Swpaul */ 913124005Swpaul if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING) 914123858Swpaul continue; 915141963Swpaul MSCALL3(senddonefunc, sc->ndis_block, p, p->np_oob.npo_status); 916123858Swpaul } 917123858Swpaul 918144174Swpaul if (NDIS_SERIALIZED(sc->ndis_block)) 919144174Swpaul KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); 920144174Swpaul 921198786Srpaulo return (0); 922123474Swpaul} 923123474Swpaul 924123474Swpaulint 925125377Swpaulndis_send_packet(arg, packet) 926125377Swpaul void *arg; 927125377Swpaul ndis_packet *packet; 928125377Swpaul{ 929125377Swpaul struct ndis_softc *sc; 930125377Swpaul ndis_handle adapter; 931125377Swpaul ndis_status status; 932144888Swpaul ndis_sendsingle_handler sendfunc; 933144888Swpaul ndis_senddone_func senddonefunc; 934170487Smjacob uint8_t irql = 0; 935125377Swpaul 936125377Swpaul sc = arg; 937141524Swpaul adapter = sc->ndis_block->nmb_miniportadapterctx; 938125718Swpaul if (adapter == NULL) 939198786Srpaulo return (ENXIO); 940141524Swpaul sendfunc = sc->ndis_chars->nmc_sendsingle_func; 941141524Swpaul senddonefunc = sc->ndis_block->nmb_senddone_func; 942125377Swpaul 943144174Swpaul if (NDIS_SERIALIZED(sc->ndis_block)) 944143204Swpaul KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); 945141963Swpaul status = MSCALL3(sendfunc, adapter, packet, 946141963Swpaul packet->np_private.npp_flags); 947125377Swpaul 948144174Swpaul if (status == NDIS_STATUS_PENDING) { 949144174Swpaul if (NDIS_SERIALIZED(sc->ndis_block)) 950144174Swpaul KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); 951198786Srpaulo return (0); 952144174Swpaul } 953125377Swpaul 954141963Swpaul MSCALL3(senddonefunc, sc->ndis_block, packet, status); 955125377Swpaul 956144174Swpaul if (NDIS_SERIALIZED(sc->ndis_block)) 957144174Swpaul KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); 958144174Swpaul 959198786Srpaulo return (0); 960125377Swpaul} 961125377Swpaul 962125377Swpaulint 963123474Swpaulndis_init_dma(arg) 964123474Swpaul void *arg; 965123474Swpaul{ 966123474Swpaul struct ndis_softc *sc; 967123474Swpaul int i, error; 968123474Swpaul 969123474Swpaul sc = arg; 970123474Swpaul 971123474Swpaul sc->ndis_tmaps = malloc(sizeof(bus_dmamap_t) * sc->ndis_maxpkts, 972123474Swpaul M_DEVBUF, M_NOWAIT|M_ZERO); 973123474Swpaul 974123474Swpaul if (sc->ndis_tmaps == NULL) 975198786Srpaulo return (ENOMEM); 976123474Swpaul 977123474Swpaul for (i = 0; i < sc->ndis_maxpkts; i++) { 978123474Swpaul error = bus_dmamap_create(sc->ndis_ttag, 0, 979123474Swpaul &sc->ndis_tmaps[i]); 980123474Swpaul if (error) { 981123474Swpaul free(sc->ndis_tmaps, M_DEVBUF); 982198786Srpaulo return (ENODEV); 983123474Swpaul } 984123474Swpaul } 985123474Swpaul 986198786Srpaulo return (0); 987123474Swpaul} 988123474Swpaul 989123474Swpaulint 990123474Swpaulndis_destroy_dma(arg) 991123474Swpaul void *arg; 992123474Swpaul{ 993123474Swpaul struct ndis_softc *sc; 994123535Swpaul struct mbuf *m; 995123535Swpaul ndis_packet *p = NULL; 996123474Swpaul int i; 997123474Swpaul 998123474Swpaul sc = arg; 999123474Swpaul 1000123474Swpaul for (i = 0; i < sc->ndis_maxpkts; i++) { 1001123535Swpaul if (sc->ndis_txarray[i] != NULL) { 1002123535Swpaul p = sc->ndis_txarray[i]; 1003123535Swpaul m = (struct mbuf *)p->np_rsvd[1]; 1004123535Swpaul if (m != NULL) 1005123535Swpaul m_freem(m); 1006123535Swpaul ndis_free_packet(sc->ndis_txarray[i]); 1007123535Swpaul } 1008123474Swpaul bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]); 1009123474Swpaul } 1010123474Swpaul 1011123474Swpaul free(sc->ndis_tmaps, M_DEVBUF); 1012123474Swpaul 1013123474Swpaul bus_dma_tag_destroy(sc->ndis_ttag); 1014123474Swpaul 1015198786Srpaulo return (0); 1016123474Swpaul} 1017123474Swpaul 1018123474Swpaulint 1019123474Swpaulndis_reset_nic(arg) 1020123474Swpaul void *arg; 1021123474Swpaul{ 1022123474Swpaul struct ndis_softc *sc; 1023123474Swpaul ndis_handle adapter; 1024144888Swpaul ndis_reset_handler resetfunc; 1025123474Swpaul uint8_t addressing_reset; 1026127887Swpaul int rval; 1027170487Smjacob uint8_t irql = 0; 1028123474Swpaul 1029123474Swpaul sc = arg; 1030143204Swpaul 1031145898Swpaul NDIS_LOCK(sc); 1032141524Swpaul adapter = sc->ndis_block->nmb_miniportadapterctx; 1033141524Swpaul resetfunc = sc->ndis_chars->nmc_reset_func; 1034143204Swpaul 1035145898Swpaul if (adapter == NULL || resetfunc == NULL || 1036145898Swpaul sc->ndis_block->nmb_devicectx == NULL) { 1037145898Swpaul NDIS_UNLOCK(sc); 1038198786Srpaulo return (EIO); 1039145898Swpaul } 1040123474Swpaul 1041145898Swpaul NDIS_UNLOCK(sc); 1042145895Swpaul 1043180754Sweongyo KeResetEvent(&sc->ndis_block->nmb_resetevent); 1044180754Sweongyo 1045144174Swpaul if (NDIS_SERIALIZED(sc->ndis_block)) 1046144174Swpaul KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); 1047144174Swpaul 1048141963Swpaul rval = MSCALL2(resetfunc, &addressing_reset, adapter); 1049128229Swpaul 1050144174Swpaul if (NDIS_SERIALIZED(sc->ndis_block)) 1051144174Swpaul KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); 1052144174Swpaul 1053180754Sweongyo if (rval == NDIS_STATUS_PENDING) 1054151248Swpaul KeWaitForSingleObject(&sc->ndis_block->nmb_resetevent, 1055151248Swpaul 0, 0, FALSE, NULL); 1056123474Swpaul 1057198786Srpaulo return (0); 1058123474Swpaul} 1059123474Swpaul 1060123474Swpaulint 1061123474Swpaulndis_halt_nic(arg) 1062123474Swpaul void *arg; 1063123474Swpaul{ 1064123474Swpaul struct ndis_softc *sc; 1065123474Swpaul ndis_handle adapter; 1066144888Swpaul ndis_halt_handler haltfunc; 1067151451Swpaul ndis_miniport_block *block; 1068151451Swpaul int empty = 0; 1069151451Swpaul uint8_t irql; 1070123474Swpaul 1071123474Swpaul sc = arg; 1072151451Swpaul block = sc->ndis_block; 1073125718Swpaul 1074146427Swpaul if (!cold) 1075146427Swpaul KeFlushQueuedDpcs(); 1076145895Swpaul 1077151451Swpaul /* 1078151451Swpaul * Wait for all packets to be returned. 1079151451Swpaul */ 1080151451Swpaul 1081151451Swpaul while (1) { 1082151451Swpaul KeAcquireSpinLock(&block->nmb_returnlock, &irql); 1083151451Swpaul empty = IsListEmpty(&block->nmb_returnlist); 1084151451Swpaul KeReleaseSpinLock(&block->nmb_returnlock, irql); 1085151451Swpaul if (empty) 1086151451Swpaul break; 1087151451Swpaul NdisMSleep(1000); 1088151451Swpaul } 1089151451Swpaul 1090146230Swpaul NDIS_LOCK(sc); 1091146230Swpaul adapter = sc->ndis_block->nmb_miniportadapterctx; 1092146230Swpaul if (adapter == NULL) { 1093146230Swpaul NDIS_UNLOCK(sc); 1094198786Srpaulo return (EIO); 1095146230Swpaul } 1096146230Swpaul 1097146230Swpaul sc->ndis_block->nmb_devicectx = NULL; 1098146230Swpaul 1099145895Swpaul /* 1100123474Swpaul * The adapter context is only valid after the init 1101123474Swpaul * handler has been called, and is invalid once the 1102123474Swpaul * halt handler has been called. 1103123474Swpaul */ 1104123474Swpaul 1105141524Swpaul haltfunc = sc->ndis_chars->nmc_halt_func; 1106125718Swpaul NDIS_UNLOCK(sc); 1107125718Swpaul 1108141963Swpaul MSCALL1(haltfunc, adapter); 1109125718Swpaul 1110151207Swpaul NDIS_LOCK(sc); 1111151207Swpaul sc->ndis_block->nmb_miniportadapterctx = NULL; 1112151207Swpaul NDIS_UNLOCK(sc); 1113151207Swpaul 1114198786Srpaulo return (0); 1115123474Swpaul} 1116123474Swpaul 1117123474Swpaulint 1118123474Swpaulndis_shutdown_nic(arg) 1119123474Swpaul void *arg; 1120123474Swpaul{ 1121123474Swpaul struct ndis_softc *sc; 1122123474Swpaul ndis_handle adapter; 1123144888Swpaul ndis_shutdown_handler shutdownfunc; 1124123474Swpaul 1125123474Swpaul sc = arg; 1126125718Swpaul NDIS_LOCK(sc); 1127141524Swpaul adapter = sc->ndis_block->nmb_miniportadapterctx; 1128141524Swpaul shutdownfunc = sc->ndis_chars->nmc_shutdown_handler; 1129125718Swpaul NDIS_UNLOCK(sc); 1130125718Swpaul if (adapter == NULL || shutdownfunc == NULL) 1131198786Srpaulo return (EIO); 1132123474Swpaul 1133141524Swpaul if (sc->ndis_chars->nmc_rsvd0 == NULL) 1134141963Swpaul MSCALL1(shutdownfunc, adapter); 1135123485Swpaul else 1136141963Swpaul MSCALL1(shutdownfunc, sc->ndis_chars->nmc_rsvd0); 1137123474Swpaul 1138141524Swpaul TAILQ_REMOVE(&ndis_devhead, sc->ndis_block, link); 1139125006Swpaul 1140198786Srpaulo return (0); 1141123474Swpaul} 1142123474Swpaul 1143123474Swpaulint 1144186507Sweongyondis_pnpevent_nic(arg, type) 1145186507Sweongyo void *arg; 1146186507Sweongyo int type; 1147186507Sweongyo{ 1148186509Sweongyo device_t dev; 1149186507Sweongyo struct ndis_softc *sc; 1150186507Sweongyo ndis_handle adapter; 1151186507Sweongyo ndis_pnpevent_handler pnpeventfunc; 1152186507Sweongyo 1153186509Sweongyo dev = arg; 1154186509Sweongyo sc = device_get_softc(arg); 1155186507Sweongyo NDIS_LOCK(sc); 1156186507Sweongyo adapter = sc->ndis_block->nmb_miniportadapterctx; 1157186507Sweongyo pnpeventfunc = sc->ndis_chars->nmc_pnpevent_handler; 1158186507Sweongyo NDIS_UNLOCK(sc); 1159186507Sweongyo if (adapter == NULL || pnpeventfunc == NULL) 1160198786Srpaulo return (EIO); 1161186507Sweongyo 1162186507Sweongyo if (sc->ndis_chars->nmc_rsvd0 == NULL) 1163186507Sweongyo MSCALL4(pnpeventfunc, adapter, type, NULL, 0); 1164186507Sweongyo else 1165186507Sweongyo MSCALL4(pnpeventfunc, sc->ndis_chars->nmc_rsvd0, type, NULL, 0); 1166186507Sweongyo 1167186507Sweongyo return (0); 1168186507Sweongyo} 1169186507Sweongyo 1170186507Sweongyoint 1171123474Swpaulndis_init_nic(arg) 1172123474Swpaul void *arg; 1173123474Swpaul{ 1174123474Swpaul struct ndis_softc *sc; 1175123474Swpaul ndis_miniport_block *block; 1176189488Sweongyo ndis_init_handler initfunc; 1177123474Swpaul ndis_status status, openstatus = 0; 1178123474Swpaul ndis_medium mediumarray[NdisMediumMax]; 1179123474Swpaul uint32_t chosenmedium, i; 1180123474Swpaul 1181123474Swpaul if (arg == NULL) 1182198786Srpaulo return (EINVAL); 1183123474Swpaul 1184123474Swpaul sc = arg; 1185125718Swpaul NDIS_LOCK(sc); 1186141524Swpaul block = sc->ndis_block; 1187141524Swpaul initfunc = sc->ndis_chars->nmc_init_func; 1188125718Swpaul NDIS_UNLOCK(sc); 1189123474Swpaul 1190145895Swpaul sc->ndis_block->nmb_timerlist = NULL; 1191145895Swpaul 1192123474Swpaul for (i = 0; i < NdisMediumMax; i++) 1193123474Swpaul mediumarray[i] = i; 1194123474Swpaul 1195189488Sweongyo status = MSCALL6(initfunc, &openstatus, &chosenmedium, 1196189488Sweongyo mediumarray, NdisMediumMax, block, block); 1197123474Swpaul 1198123474Swpaul /* 1199123474Swpaul * If the init fails, blow away the other exported routines 1200123474Swpaul * we obtained from the driver so we can't call them later. 1201123474Swpaul * If the init failed, none of these will work. 1202123474Swpaul */ 1203123474Swpaul if (status != NDIS_STATUS_SUCCESS) { 1204125718Swpaul NDIS_LOCK(sc); 1205141524Swpaul sc->ndis_block->nmb_miniportadapterctx = NULL; 1206125718Swpaul NDIS_UNLOCK(sc); 1207198816Srpaulo return (ENXIO); 1208123474Swpaul } 1209123474Swpaul 1210145895Swpaul /* 1211145895Swpaul * This may look really goofy, but apparently it is possible 1212145895Swpaul * to halt a miniport too soon after it's been initialized. 1213145895Swpaul * After MiniportInitialize() finishes, pause for 1 second 1214145895Swpaul * to give the chip a chance to handle any short-lived timers 1215145895Swpaul * that were set in motion. If we call MiniportHalt() too soon, 1216145895Swpaul * some of the timers may not be cancelled, because the driver 1217145895Swpaul * expects them to fire before the halt is called. 1218145895Swpaul */ 1219145895Swpaul 1220166909Sjhb pause("ndwait", hz); 1221145895Swpaul 1222145898Swpaul NDIS_LOCK(sc); 1223145898Swpaul sc->ndis_block->nmb_devicectx = sc; 1224145898Swpaul NDIS_UNLOCK(sc); 1225145898Swpaul 1226198786Srpaulo return (0); 1227123474Swpaul} 1228123474Swpaul 1229144888Swpaulstatic void 1230151207Swpaulndis_intrsetup(dpc, dobj, ip, sc) 1231144174Swpaul kdpc *dpc; 1232144174Swpaul device_object *dobj; 1233144174Swpaul irp *ip; 1234144174Swpaul struct ndis_softc *sc; 1235123474Swpaul{ 1236151207Swpaul ndis_miniport_interrupt *intr; 1237123474Swpaul 1238151207Swpaul intr = sc->ndis_block->nmb_interrupt; 1239143204Swpaul 1240151207Swpaul /* Sanity check. */ 1241151207Swpaul 1242151207Swpaul if (intr == NULL) 1243144174Swpaul return; 1244125718Swpaul 1245151207Swpaul KeAcquireSpinLockAtDpcLevel(&intr->ni_dpccountlock); 1246151207Swpaul KeResetEvent(&intr->ni_dpcevt); 1247151207Swpaul if (KeInsertQueueDpc(&intr->ni_dpc, NULL, NULL) == TRUE) 1248151207Swpaul intr->ni_dpccnt++; 1249151207Swpaul KeReleaseSpinLockFromDpcLevel(&intr->ni_dpccountlock); 1250123474Swpaul} 1251123474Swpaul 1252123474Swpaulint 1253123474Swpaulndis_get_info(arg, oid, buf, buflen) 1254123474Swpaul void *arg; 1255123474Swpaul ndis_oid oid; 1256123474Swpaul void *buf; 1257123474Swpaul int *buflen; 1258123474Swpaul{ 1259123474Swpaul struct ndis_softc *sc; 1260123474Swpaul ndis_status rval; 1261123474Swpaul ndis_handle adapter; 1262144888Swpaul ndis_queryinfo_handler queryfunc; 1263123474Swpaul uint32_t byteswritten = 0, bytesneeded = 0; 1264128229Swpaul uint8_t irql; 1265151248Swpaul uint64_t duetime; 1266145895Swpaul 1267144174Swpaul sc = arg; 1268145898Swpaul 1269180754Sweongyo KeResetEvent(&sc->ndis_block->nmb_getevent); 1270180754Sweongyo 1271144174Swpaul KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); 1272123474Swpaul 1273151248Swpaul if (sc->ndis_block->nmb_pendingreq != NULL) { 1274151248Swpaul KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); 1275143204Swpaul panic("ndis_get_info() called while other request pending"); 1276151248Swpaul } else 1277143204Swpaul sc->ndis_block->nmb_pendingreq = (ndis_request *)sc; 1278143204Swpaul 1279141524Swpaul queryfunc = sc->ndis_chars->nmc_queryinfo_func; 1280141524Swpaul adapter = sc->ndis_block->nmb_miniportadapterctx; 1281123474Swpaul 1282145898Swpaul if (adapter == NULL || queryfunc == NULL || 1283145898Swpaul sc->ndis_block->nmb_devicectx == NULL) { 1284144317Swpaul sc->ndis_block->nmb_pendingreq = NULL; 1285144174Swpaul KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); 1286198786Srpaulo return (ENXIO); 1287143204Swpaul } 1288125676Swpaul 1289141963Swpaul rval = MSCALL6(queryfunc, adapter, oid, buf, *buflen, 1290123474Swpaul &byteswritten, &bytesneeded); 1291123474Swpaul 1292144174Swpaul sc->ndis_block->nmb_pendingreq = NULL; 1293144174Swpaul 1294144174Swpaul KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); 1295144174Swpaul 1296123695Swpaul /* Wait for requests that block. */ 1297123695Swpaul 1298123695Swpaul if (rval == NDIS_STATUS_PENDING) { 1299151248Swpaul /* Wait up to 5 seconds. */ 1300151248Swpaul duetime = (5 * 1000000) * -10; 1301151248Swpaul KeWaitForSingleObject(&sc->ndis_block->nmb_getevent, 1302151248Swpaul 0, 0, FALSE, &duetime); 1303141524Swpaul rval = sc->ndis_block->nmb_getstat; 1304145898Swpaul } 1305123695Swpaul 1306123474Swpaul if (byteswritten) 1307123474Swpaul *buflen = byteswritten; 1308123474Swpaul if (bytesneeded) 1309123474Swpaul *buflen = bytesneeded; 1310123474Swpaul 1311123474Swpaul if (rval == NDIS_STATUS_INVALID_LENGTH || 1312123474Swpaul rval == NDIS_STATUS_BUFFER_TOO_SHORT) 1313198786Srpaulo return (ENOSPC); 1314123474Swpaul 1315123474Swpaul if (rval == NDIS_STATUS_INVALID_OID) 1316198786Srpaulo return (EINVAL); 1317123474Swpaul 1318123474Swpaul if (rval == NDIS_STATUS_NOT_SUPPORTED || 1319123474Swpaul rval == NDIS_STATUS_NOT_ACCEPTED) 1320198786Srpaulo return (ENOTSUP); 1321123474Swpaul 1322124809Swpaul if (rval != NDIS_STATUS_SUCCESS) 1323198786Srpaulo return (ENODEV); 1324124809Swpaul 1325198786Srpaulo return (0); 1326123474Swpaul} 1327123474Swpaul 1328144888Swpauluint32_t 1329141524SwpaulNdisAddDevice(drv, pdo) 1330141524Swpaul driver_object *drv; 1331141524Swpaul device_object *pdo; 1332123474Swpaul{ 1333141524Swpaul device_object *fdo; 1334141524Swpaul ndis_miniport_block *block; 1335123474Swpaul struct ndis_softc *sc; 1336141524Swpaul uint32_t status; 1337151451Swpaul int error; 1338123474Swpaul 1339151451Swpaul sc = device_get_softc(pdo->do_devext); 1340151451Swpaul 1341189488Sweongyo if (sc->ndis_iftype == PCMCIABus || sc->ndis_iftype == PCIBus) { 1342151451Swpaul error = bus_setup_intr(sc->ndis_dev, sc->ndis_irq, 1343151451Swpaul INTR_TYPE_NET | INTR_MPSAFE, 1344166901Spiso NULL, ntoskrnl_intr, NULL, &sc->ndis_intrhand); 1345151451Swpaul if (error) 1346198786Srpaulo return (NDIS_STATUS_FAILURE); 1347151451Swpaul } 1348151451Swpaul 1349141524Swpaul status = IoCreateDevice(drv, sizeof(ndis_miniport_block), NULL, 1350141524Swpaul FILE_DEVICE_UNKNOWN, 0, FALSE, &fdo); 1351123474Swpaul 1352141524Swpaul if (status != STATUS_SUCCESS) 1353198786Srpaulo return (status); 1354123474Swpaul 1355141524Swpaul block = fdo->do_devext; 1356146230Swpaul 1357146230Swpaul block->nmb_filterdbs.nf_ethdb = block; 1358141524Swpaul block->nmb_deviceobj = fdo; 1359141524Swpaul block->nmb_physdeviceobj = pdo; 1360141524Swpaul block->nmb_nextdeviceobj = IoAttachDeviceToDeviceStack(fdo, pdo); 1361141524Swpaul KeInitializeSpinLock(&block->nmb_lock); 1362151451Swpaul KeInitializeSpinLock(&block->nmb_returnlock); 1363151248Swpaul KeInitializeEvent(&block->nmb_getevent, EVENT_TYPE_NOTIFY, TRUE); 1364151248Swpaul KeInitializeEvent(&block->nmb_setevent, EVENT_TYPE_NOTIFY, TRUE); 1365151248Swpaul KeInitializeEvent(&block->nmb_resetevent, EVENT_TYPE_NOTIFY, TRUE); 1366151451Swpaul InitializeListHead(&block->nmb_parmlist); 1367151451Swpaul InitializeListHead(&block->nmb_returnlist); 1368151451Swpaul block->nmb_returnitem = IoAllocateWorkItem(fdo); 1369123474Swpaul 1370124446Swpaul /* 1371141524Swpaul * Stash pointers to the miniport block and miniport 1372141524Swpaul * characteristics info in the if_ndis softc so the 1373141524Swpaul * UNIX wrapper driver can get to them later. 1374189488Sweongyo */ 1375141524Swpaul sc->ndis_block = block; 1376141524Swpaul sc->ndis_chars = IoGetDriverObjectExtension(drv, (void *)1); 1377141963Swpaul 1378146230Swpaul /* 1379146230Swpaul * If the driver has a MiniportTransferData() function, 1380146230Swpaul * we should allocate a private RX packet pool. 1381146230Swpaul */ 1382146230Swpaul 1383146230Swpaul if (sc->ndis_chars->nmc_transferdata_func != NULL) { 1384146230Swpaul NdisAllocatePacketPool(&status, &block->nmb_rxpool, 1385146230Swpaul 32, PROTOCOL_RESERVED_SIZE_IN_PACKET); 1386146230Swpaul if (status != NDIS_STATUS_SUCCESS) { 1387146230Swpaul IoDetachDevice(block->nmb_nextdeviceobj); 1388146230Swpaul IoDeleteDevice(fdo); 1389198786Srpaulo return (status); 1390146230Swpaul } 1391151207Swpaul InitializeListHead((&block->nmb_packetlist)); 1392146230Swpaul } 1393146230Swpaul 1394145895Swpaul /* Give interrupt handling priority over timers. */ 1395144495Swpaul IoInitializeDpcRequest(fdo, kernndis_functbl[6].ipt_wrap); 1396145895Swpaul KeSetImportanceDpc(&fdo->do_dpc, KDPC_IMPORTANCE_HIGH); 1397144174Swpaul 1398141524Swpaul /* Finish up BSD-specific setup. */ 1399123474Swpaul 1400123474Swpaul block->nmb_signature = (void *)0xcafebabe; 1401141963Swpaul block->nmb_status_func = kernndis_functbl[0].ipt_wrap; 1402141963Swpaul block->nmb_statusdone_func = kernndis_functbl[1].ipt_wrap; 1403141963Swpaul block->nmb_setdone_func = kernndis_functbl[2].ipt_wrap; 1404141963Swpaul block->nmb_querydone_func = kernndis_functbl[3].ipt_wrap; 1405141963Swpaul block->nmb_resetdone_func = kernndis_functbl[4].ipt_wrap; 1406141963Swpaul block->nmb_sendrsrc_func = kernndis_functbl[5].ipt_wrap; 1407143204Swpaul block->nmb_pendingreq = NULL; 1408123474Swpaul 1409141524Swpaul TAILQ_INSERT_TAIL(&ndis_devhead, block, link); 1410125551Swpaul 1411141524Swpaul return (STATUS_SUCCESS); 1412141524Swpaul} 1413125551Swpaul 1414141524Swpaulint 1415141524Swpaulndis_unload_driver(arg) 1416141524Swpaul void *arg; 1417141524Swpaul{ 1418141524Swpaul struct ndis_softc *sc; 1419141524Swpaul device_object *fdo; 1420125551Swpaul 1421141524Swpaul sc = arg; 1422124697Swpaul 1423151451Swpaul if (sc->ndis_intrhand) 1424151451Swpaul bus_teardown_intr(sc->ndis_dev, 1425151451Swpaul sc->ndis_irq, sc->ndis_intrhand); 1426151451Swpaul 1427142387Swpaul if (sc->ndis_block->nmb_rlist != NULL) 1428142387Swpaul free(sc->ndis_block->nmb_rlist, M_DEVBUF); 1429125006Swpaul 1430141524Swpaul ndis_flush_sysctls(sc); 1431141524Swpaul 1432141524Swpaul TAILQ_REMOVE(&ndis_devhead, sc->ndis_block, link); 1433141524Swpaul 1434146230Swpaul if (sc->ndis_chars->nmc_transferdata_func != NULL) 1435146230Swpaul NdisFreePacketPool(sc->ndis_block->nmb_rxpool); 1436141524Swpaul fdo = sc->ndis_block->nmb_deviceobj; 1437151451Swpaul IoFreeWorkItem(sc->ndis_block->nmb_returnitem); 1438141524Swpaul IoDetachDevice(sc->ndis_block->nmb_nextdeviceobj); 1439141524Swpaul IoDeleteDevice(fdo); 1440141524Swpaul 1441198786Srpaulo return (0); 1442123474Swpaul} 1443