kern_ndis.c revision 124724
1/* 2 * Copyright (c) 2003 3 * Bill Paul <wpaul@windriver.com>. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Bill Paul. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30 * THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: head/sys/compat/ndis/kern_ndis.c 124724 2004-01-19 18:56:31Z wpaul $"); 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/unistd.h> 39#include <sys/types.h> 40#include <sys/errno.h> 41#include <sys/callout.h> 42#include <sys/socket.h> 43#include <sys/queue.h> 44#include <sys/sysctl.h> 45#include <sys/proc.h> 46#include <sys/malloc.h> 47#include <sys/lock.h> 48#include <sys/mutex.h> 49#include <sys/conf.h> 50 51#include <sys/kernel.h> 52#include <sys/kthread.h> 53#include <machine/bus.h> 54#include <machine/resource.h> 55#include <sys/bus.h> 56#include <sys/rman.h> 57 58#include <vm/uma.h> 59 60#include <net/if.h> 61#include <net/if_arp.h> 62#include <net/ethernet.h> 63#include <net/if_dl.h> 64#include <net/if_media.h> 65 66#include <net80211/ieee80211_var.h> 67#include <net80211/ieee80211_ioctl.h> 68 69#include <dev/pccard/pccardvar.h> 70#include "card_if.h" 71 72#include <compat/ndis/pe_var.h> 73#include <compat/ndis/resource_var.h> 74#include <compat/ndis/ndis_var.h> 75#include <compat/ndis/hal_var.h> 76#include <compat/ndis/ntoskrnl_var.h> 77#include <compat/ndis/cfg_var.h> 78#include <dev/if_ndis/if_ndisvar.h> 79 80#define NDIS_DUMMY_PATH "\\\\some\\bogus\\path" 81 82__stdcall static void ndis_status_func(ndis_handle, ndis_status, 83 void *, uint32_t); 84__stdcall static void ndis_statusdone_func(ndis_handle); 85__stdcall static void ndis_setdone_func(ndis_handle, ndis_status); 86__stdcall static void ndis_getdone_func(ndis_handle, ndis_status); 87__stdcall static void ndis_resetdone_func(ndis_handle, ndis_status, uint8_t); 88__stdcall static void ndis_sendrsrcavail_func(ndis_handle); 89 90struct ndis_req { 91 void (*nr_func)(void *); 92 void *nr_arg; 93 int nr_exit; 94 STAILQ_ENTRY(ndis_req) link; 95}; 96 97struct ndisproc { 98 struct ndisqhead *np_q; 99 struct proc *np_p; 100}; 101 102static int ndis_create_kthreads(void); 103static void ndis_destroy_kthreads(void); 104static void ndis_stop_thread(int); 105static int ndis_enlarge_thrqueue(int); 106static int ndis_shrink_thrqueue(int); 107static void ndis_runq(void *); 108 109extern struct mtx_pool *ndis_mtxpool; 110static uma_zone_t ndis_packet_zone, ndis_buffer_zone; 111struct mtx *ndis_thr_mtx; 112static STAILQ_HEAD(ndisqhead, ndis_req) ndis_ttodo; 113struct ndisqhead ndis_itodo; 114struct ndisqhead ndis_free; 115static int ndis_jobs = 32; 116 117static struct ndisproc ndis_tproc; 118static struct ndisproc ndis_iproc; 119 120/* 121 * This allows us to export our symbols to other modules. 122 * Note that we call ourselves 'ndisapi' to avoid a namespace 123 * collision with if_ndis.ko, which internally calls itself 124 * 'ndis.' 125 */ 126static int 127ndis_modevent(module_t mod, int cmd, void *arg) 128{ 129 int error = 0; 130 131 switch (cmd) { 132 case MOD_LOAD: 133 /* Initialize subsystems */ 134 ndis_libinit(); 135 ntoskrnl_libinit(); 136 137 /* Initialize TX buffer UMA zone. */ 138 ndis_packet_zone = uma_zcreate("NDIS packet", 139 sizeof(ndis_packet), NULL, NULL, NULL, 140 NULL, UMA_ALIGN_PTR, 0); 141 ndis_buffer_zone = uma_zcreate("NDIS buffer", 142 sizeof(ndis_buffer), NULL, NULL, NULL, 143 NULL, UMA_ALIGN_PTR, 0); 144 145 ndis_create_kthreads(); 146 147 break; 148 case MOD_UNLOAD: 149 case MOD_SHUTDOWN: 150 /* stop kthreads */ 151 ndis_destroy_kthreads(); 152 153 /* Shut down subsystems */ 154 ndis_libfini(); 155 ntoskrnl_libfini(); 156 157 /* Remove zones */ 158 uma_zdestroy(ndis_packet_zone); 159 uma_zdestroy(ndis_buffer_zone); 160 break; 161 default: 162 error = EINVAL; 163 break; 164 } 165 166 return(error); 167} 168DEV_MODULE(ndisapi, ndis_modevent, NULL); 169MODULE_VERSION(ndisapi, 1); 170 171/* 172 * We create two kthreads for the NDIS subsystem. One of them is a task 173 * queue for performing various odd jobs. The other is an swi thread 174 * reserved exclusively for running interrupt handlers. The reason we 175 * have our own task queue is that there are some cases where we may 176 * need to sleep for a significant amount of time, and if we were to 177 * use one of the taskqueue threads, we might delay the processing 178 * of other pending tasks which might need to run right away. We have 179 * a separate swi thread because we don't want our interrupt handling 180 * to be delayed either. 181 * 182 * By default there are 32 jobs available to start, and another 8 183 * are added to the free list each time a new device is created. 184 */ 185 186static void 187ndis_runq(arg) 188 void *arg; 189{ 190 struct ndis_req *r = NULL, *die = NULL; 191 struct ndisproc *p; 192 193 p = arg; 194 195 while (1) { 196 kthread_suspend(p->np_p, 0); 197 198 /* Look for any jobs on the work queue. */ 199 200 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 201 while(STAILQ_FIRST(p->np_q) != NULL) { 202 r = STAILQ_FIRST(p->np_q); 203 STAILQ_REMOVE_HEAD(p->np_q, link); 204 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 205 206 /* Do the work. */ 207 208 if (r->nr_func != NULL) 209 (*r->nr_func)(r->nr_arg); 210 211 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 212 STAILQ_INSERT_HEAD(&ndis_free, r, link); 213 214 /* Check for a shutdown request */ 215 216 if (r->nr_exit == TRUE) 217 die = r; 218 } 219 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 220 221 /* Bail if we were told to shut down. */ 222 223 if (die != NULL) 224 break; 225 } 226 227 wakeup(die); 228 mtx_lock(&Giant); 229 kthread_exit(0); 230} 231 232static int 233ndis_create_kthreads() 234{ 235 struct ndis_req *r; 236 int i, error = 0; 237 238 ndis_thr_mtx = mtx_pool_alloc(ndis_mtxpool); 239 STAILQ_INIT(&ndis_ttodo); 240 STAILQ_INIT(&ndis_itodo); 241 STAILQ_INIT(&ndis_free); 242 243 for (i = 0; i < ndis_jobs; i++) { 244 r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK); 245 if (r == NULL) { 246 error = ENOMEM; 247 break; 248 } 249 STAILQ_INSERT_HEAD(&ndis_free, r, link); 250 } 251 252 if (error == 0) { 253 ndis_tproc.np_q = &ndis_ttodo; 254 error = kthread_create(ndis_runq, &ndis_tproc, 255 &ndis_tproc.np_p, RFHIGHPID, 0, "ndis taskqueue"); 256 } 257 258 if (error == 0) { 259 ndis_iproc.np_q = &ndis_itodo; 260 error = kthread_create(ndis_runq, &ndis_iproc, 261 &ndis_iproc.np_p, RFHIGHPID, 0, "ndis swi"); 262 } 263 264 if (error) { 265 while ((r = STAILQ_FIRST(&ndis_free)) != NULL) { 266 STAILQ_REMOVE_HEAD(&ndis_free, link); 267 free(r, M_DEVBUF); 268 } 269 return(error); 270 } 271 272 return(0); 273} 274 275static void 276ndis_destroy_kthreads() 277{ 278 struct ndis_req *r; 279 280 /* Stop the threads. */ 281 282 ndis_stop_thread(NDIS_TASKQUEUE); 283 ndis_stop_thread(NDIS_SWI); 284 285 /* Destroy request structures. */ 286 287 while ((r = STAILQ_FIRST(&ndis_free)) != NULL) { 288 STAILQ_REMOVE_HEAD(&ndis_free, link); 289 free(r, M_DEVBUF); 290 } 291 292 return; 293} 294 295static void 296ndis_stop_thread(t) 297 int t; 298{ 299 struct ndis_req *r; 300 struct timeval tv; 301 struct ndisqhead *q; 302 struct proc *p; 303 304 if (t == NDIS_TASKQUEUE) { 305 q = &ndis_ttodo; 306 p = ndis_tproc.np_p; 307 } else { 308 q = &ndis_itodo; 309 p = ndis_iproc.np_p; 310 } 311 312 /* Create and post a special 'exit' job. */ 313 314 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 315 r = STAILQ_FIRST(&ndis_free); 316 STAILQ_REMOVE_HEAD(&ndis_free, link); 317 r->nr_func = NULL; 318 r->nr_arg = NULL; 319 r->nr_exit = TRUE; 320 STAILQ_INSERT_TAIL(q, r, link); 321 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 322 323 kthread_resume(p); 324 325 /* wait for thread exit */ 326 327 tv.tv_sec = 60; 328 tv.tv_usec = 0; 329 tsleep(r, PPAUSE|PCATCH, "ndisthrexit", tvtohz(&tv)); 330 331 /* Now empty the job list. */ 332 333 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 334 while ((r = STAILQ_FIRST(q)) != NULL) { 335 STAILQ_REMOVE_HEAD(q, link); 336 STAILQ_INSERT_HEAD(&ndis_free, r, link); 337 } 338 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 339 340 return; 341} 342 343static int 344ndis_enlarge_thrqueue(cnt) 345 int cnt; 346{ 347 struct ndis_req *r; 348 int i; 349 350 for (i = 0; i < cnt; i++) { 351 r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK); 352 if (r == NULL) 353 return(ENOMEM); 354 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 355 STAILQ_INSERT_HEAD(&ndis_free, r, link); 356 ndis_jobs++; 357 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 358 } 359 360 return(0); 361} 362 363static int 364ndis_shrink_thrqueue(cnt) 365 int cnt; 366{ 367 struct ndis_req *r; 368 int i; 369 370 for (i = 0; i < cnt; i++) { 371 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 372 r = STAILQ_FIRST(&ndis_free); 373 if (r == NULL) { 374 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 375 return(ENOMEM); 376 } 377 STAILQ_REMOVE_HEAD(&ndis_free, link); 378 ndis_jobs--; 379 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 380 free(r, M_DEVBUF); 381 } 382 383 return(0); 384} 385 386int 387ndis_sched(func, arg, t) 388 void (*func)(void *); 389 void *arg; 390 int t; 391{ 392 struct ndis_req *r; 393 struct ndisqhead *q; 394 struct proc *p; 395 396 if (t == NDIS_TASKQUEUE) { 397 q = &ndis_ttodo; 398 p = ndis_tproc.np_p; 399 } else { 400 q = &ndis_itodo; 401 p = ndis_iproc.np_p; 402 } 403 404 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 405 /* 406 * Check to see if an instance of this job is already 407 * pending. If so, don't bother queuing it again. 408 */ 409 STAILQ_FOREACH(r, q, link) { 410 if (r->nr_func == func && r->nr_arg == arg) { 411 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 412 return(0); 413 } 414 } 415 r = STAILQ_FIRST(&ndis_free); 416 if (r == NULL) { 417 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 418 return(EAGAIN); 419 } 420 STAILQ_REMOVE_HEAD(&ndis_free, link); 421 r->nr_func = func; 422 r->nr_arg = arg; 423 r->nr_exit = FALSE; 424 STAILQ_INSERT_TAIL(q, r, link); 425 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 426 427 /* Post the job. */ 428 kthread_resume(p); 429 430 return(0); 431} 432 433__stdcall static void 434ndis_sendrsrcavail_func(adapter) 435 ndis_handle adapter; 436{ 437 return; 438} 439 440__stdcall static void 441ndis_status_func(adapter, status, sbuf, slen) 442 ndis_handle adapter; 443 ndis_status status; 444 void *sbuf; 445 uint32_t slen; 446{ 447 ndis_miniport_block *block; 448 block = adapter; 449 450 if (block->nmb_ifp->if_flags & IFF_DEBUG) 451 device_printf (block->nmb_dev, "status: %x\n", status); 452 return; 453} 454 455__stdcall static void 456ndis_statusdone_func(adapter) 457 ndis_handle adapter; 458{ 459 ndis_miniport_block *block; 460 block = adapter; 461 462 if (block->nmb_ifp->if_flags & IFF_DEBUG) 463 device_printf (block->nmb_dev, "status complete\n"); 464 return; 465} 466 467__stdcall static void 468ndis_setdone_func(adapter, status) 469 ndis_handle adapter; 470 ndis_status status; 471{ 472 ndis_miniport_block *block; 473 block = adapter; 474 475 block->nmb_setstat = status; 476 wakeup(&block->nmb_wkupdpctimer); 477 return; 478} 479 480__stdcall static void 481ndis_getdone_func(adapter, status) 482 ndis_handle adapter; 483 ndis_status status; 484{ 485 ndis_miniport_block *block; 486 block = adapter; 487 488 block->nmb_getstat = status; 489 wakeup(&block->nmb_wkupdpctimer); 490 return; 491} 492 493__stdcall static void 494ndis_resetdone_func(adapter, status, addressingreset) 495 ndis_handle adapter; 496 ndis_status status; 497 uint8_t addressingreset; 498{ 499 ndis_miniport_block *block; 500 block = adapter; 501 502 if (block->nmb_ifp->if_flags & IFF_DEBUG) 503 device_printf (block->nmb_dev, "reset done...\n"); 504 return; 505} 506 507#define NDIS_AM_RID 3 508 509int 510ndis_alloc_amem(arg) 511 void *arg; 512{ 513 struct ndis_softc *sc; 514 int error, rid; 515 516 if (arg == NULL) 517 return(EINVAL); 518 519 sc = arg; 520 rid = NDIS_AM_RID; 521 sc->ndis_res_am = bus_alloc_resource(sc->ndis_dev, SYS_RES_MEMORY, 522 &rid, 0UL, ~0UL, 0x1000, RF_ACTIVE); 523 524 if (sc->ndis_res_am == NULL) { 525 device_printf(sc->ndis_dev, 526 "failed to allocate attribute memory\n"); 527 return(ENXIO); 528 } 529 530 error = CARD_SET_MEMORY_OFFSET(device_get_parent(sc->ndis_dev), 531 sc->ndis_dev, rid, 0, NULL); 532 533 if (error) { 534 device_printf(sc->ndis_dev, 535 "CARD_SET_MEMORY_OFFSET() returned 0x%x\n", error); 536 return(error); 537 } 538 539 error = CARD_SET_RES_FLAGS(device_get_parent(sc->ndis_dev), 540 sc->ndis_dev, SYS_RES_MEMORY, rid, PCCARD_A_MEM_ATTR); 541 542 if (error) { 543 device_printf(sc->ndis_dev, 544 "CARD_SET_RES_FLAGS() returned 0x%x\n", error); 545 return(error); 546 } 547 548 return(0); 549} 550 551int 552ndis_create_sysctls(arg) 553 void *arg; 554{ 555 struct ndis_softc *sc; 556 ndis_cfg *vals; 557 char buf[256]; 558 559 if (arg == NULL) 560 return(EINVAL); 561 562 sc = arg; 563 vals = sc->ndis_regvals; 564 565 TAILQ_INIT(&sc->ndis_cfglist_head); 566 567 /* Create the sysctl tree. */ 568 569 sc->ndis_tree = SYSCTL_ADD_NODE(&sc->ndis_ctx, 570 SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, 571 device_get_nameunit(sc->ndis_dev), CTLFLAG_RD, 0, 572 device_get_desc(sc->ndis_dev)); 573 574 /* Add the driver-specific registry keys. */ 575 576 vals = sc->ndis_regvals; 577 while(1) { 578 if (vals->nc_cfgkey == NULL) 579 break; 580 if (vals->nc_idx != sc->ndis_devidx) { 581 vals++; 582 continue; 583 } 584 SYSCTL_ADD_STRING(&sc->ndis_ctx, 585 SYSCTL_CHILDREN(sc->ndis_tree), 586 OID_AUTO, vals->nc_cfgkey, 587 CTLFLAG_RW, vals->nc_val, 588 sizeof(vals->nc_val), 589 vals->nc_cfgdesc); 590 vals++; 591 } 592 593 /* Now add a couple of builtin keys. */ 594 595 /* 596 * Environment can be either Windows (0) or WindowsNT (1). 597 * We qualify as the latter. 598 */ 599 ndis_add_sysctl(sc, "Environment", 600 "Windows environment", "1", CTLFLAG_RD); 601 602 /* NDIS version should be 5.1. */ 603 ndis_add_sysctl(sc, "NdisVersion", 604 "NDIS API Version", "0x00050001", CTLFLAG_RD); 605 606 /* Bus type (PCI, PCMCIA, etc...) */ 607 sprintf(buf, "%d", (int)sc->ndis_iftype); 608 ndis_add_sysctl(sc, "BusType", "Bus Type", buf, CTLFLAG_RD); 609 610 if (sc->ndis_res_io != NULL) { 611 sprintf(buf, "0x%lx", rman_get_start(sc->ndis_res_io)); 612 ndis_add_sysctl(sc, "IOBaseAddress", 613 "Base I/O Address", buf, CTLFLAG_RD); 614 } 615 616 if (sc->ndis_irq != NULL) { 617 sprintf(buf, "%lu", rman_get_start(sc->ndis_irq)); 618 ndis_add_sysctl(sc, "InterruptNumber", 619 "Interrupt Number", buf, CTLFLAG_RD); 620 } 621 622 return(0); 623} 624 625int 626ndis_add_sysctl(arg, key, desc, val, flag) 627 void *arg; 628 char *key; 629 char *desc; 630 char *val; 631 int flag; 632{ 633 struct ndis_softc *sc; 634 struct ndis_cfglist *cfg; 635 char descstr[256]; 636 637 sc = arg; 638 639 cfg = malloc(sizeof(struct ndis_cfglist), M_DEVBUF, M_NOWAIT|M_ZERO); 640 641 if (cfg == NULL) 642 return(ENOMEM); 643 644 cfg->ndis_cfg.nc_cfgkey = strdup(key, M_DEVBUF); 645 if (desc == NULL) { 646 snprintf(descstr, sizeof(descstr), "%s (dynamic)", key); 647 cfg->ndis_cfg.nc_cfgdesc = strdup(descstr, M_DEVBUF); 648 } else 649 cfg->ndis_cfg.nc_cfgdesc = strdup(desc, M_DEVBUF); 650 strcpy(cfg->ndis_cfg.nc_val, val); 651 652 TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link); 653 654 SYSCTL_ADD_STRING(&sc->ndis_ctx, SYSCTL_CHILDREN(sc->ndis_tree), 655 OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag, 656 cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val), 657 cfg->ndis_cfg.nc_cfgdesc); 658 659 return(0); 660} 661 662int 663ndis_flush_sysctls(arg) 664 void *arg; 665{ 666 struct ndis_softc *sc; 667 struct ndis_cfglist *cfg; 668 669 sc = arg; 670 671 while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) { 672 cfg = TAILQ_FIRST(&sc->ndis_cfglist_head); 673 TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link); 674 free(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF); 675 free(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF); 676 free(cfg, M_DEVBUF); 677 } 678 679 return(0); 680} 681 682void 683ndis_return_packet(buf, arg) 684 void *buf; /* not used */ 685 void *arg; 686{ 687 struct ndis_softc *sc; 688 ndis_handle adapter; 689 ndis_packet *p; 690 __stdcall ndis_return_handler returnfunc; 691 692 if (arg == NULL) 693 return; 694 695 p = arg; 696 697 /* Decrement refcount. */ 698 p->np_refcnt--; 699 700 /* Release packet when refcount hits zero, otherwise return. */ 701 if (p->np_refcnt) 702 return; 703 704 sc = p->np_softc; 705 returnfunc = sc->ndis_chars.nmc_return_packet_func; 706 adapter = sc->ndis_block.nmb_miniportadapterctx; 707 if (returnfunc != NULL) 708 returnfunc(adapter, p); 709 710 return; 711} 712 713void 714ndis_free_bufs(b0) 715 ndis_buffer *b0; 716{ 717 ndis_buffer *next; 718 719 if (b0 == NULL) 720 return; 721 722 while(b0 != NULL) { 723 next = b0->nb_next; 724 uma_zfree (ndis_buffer_zone, b0); 725 b0 = next; 726 } 727 728 return; 729} 730 731void 732ndis_free_packet(p) 733 ndis_packet *p; 734{ 735 if (p == NULL) 736 return; 737 738 ndis_free_bufs(p->np_private.npp_head); 739 uma_zfree(ndis_packet_zone, p); 740 741 return; 742} 743 744int 745ndis_convert_res(arg) 746 void *arg; 747{ 748 struct ndis_softc *sc; 749 ndis_resource_list *rl = NULL; 750 cm_partial_resource_desc *prd = NULL; 751 ndis_miniport_block *block; 752 device_t dev; 753 struct resource_list *brl; 754 struct resource_list_entry *brle; 755 756 sc = arg; 757 block = &sc->ndis_block; 758 dev = sc->ndis_dev; 759 760 rl = malloc(sizeof(ndis_resource_list) + 761 (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)), 762 M_DEVBUF, M_NOWAIT|M_ZERO); 763 764 if (rl == NULL) 765 return(ENOMEM); 766 767 rl->cprl_version = 5; 768 rl->cprl_version = 1; 769 rl->cprl_count = sc->ndis_rescnt; 770 prd = rl->cprl_partial_descs; 771 772 brl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); 773 if (brl != NULL) { 774 SLIST_FOREACH(brle, brl, link) { 775 switch (brle->type) { 776 case SYS_RES_IOPORT: 777 prd->cprd_type = CmResourceTypePort; 778 prd->u.cprd_port.cprd_start.np_quad = 779 brle->start; 780 prd->u.cprd_port.cprd_len = brle->count; 781 break; 782 case SYS_RES_MEMORY: 783 prd->cprd_type = CmResourceTypeMemory; 784 prd->u.cprd_port.cprd_start.np_quad = 785 brle->start; 786 prd->u.cprd_port.cprd_len = brle->count; 787 break; 788 case SYS_RES_IRQ: 789 prd->cprd_type = CmResourceTypeInterrupt; 790 prd->u.cprd_intr.cprd_level = brle->start; 791 prd->u.cprd_intr.cprd_vector = brle->start; 792 prd->u.cprd_intr.cprd_affinity = 0; 793 break; 794 default: 795 break; 796 } 797 prd++; 798 } 799 } 800 801 block->nmb_rlist = rl; 802 803 return(0); 804} 805 806/* 807 * Map an NDIS packet to an mbuf list. When an NDIS driver receives a 808 * packet, it will hand it to us in the form of an ndis_packet, 809 * which we need to convert to an mbuf that is then handed off 810 * to the stack. Note: we configure the mbuf list so that it uses 811 * the memory regions specified by the ndis_buffer structures in 812 * the ndis_packet as external storage. In most cases, this will 813 * point to a memory region allocated by the driver (either by 814 * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect 815 * the driver to handle free()ing this region for is, so we set up 816 * a dummy no-op free handler for it. 817 */ 818 819int 820ndis_ptom(m0, p) 821 struct mbuf **m0; 822 ndis_packet *p; 823{ 824 struct mbuf *m, *prev = NULL; 825 ndis_buffer *buf; 826 ndis_packet_private *priv; 827 uint32_t totlen = 0; 828 829 if (p == NULL || m0 == NULL) 830 return(EINVAL); 831 832 priv = &p->np_private; 833 buf = priv->npp_head; 834 p->np_refcnt = 0; 835 836 for (buf = priv->npp_head; buf != NULL; buf = buf->nb_next) { 837 if (buf == priv->npp_head) 838 MGETHDR(m, M_DONTWAIT, MT_HEADER); 839 else 840 MGET(m, M_DONTWAIT, MT_DATA); 841 if (m == NULL) { 842 m_freem(*m0); 843 *m0 = NULL; 844 return(ENOBUFS); 845 } 846 m->m_len = buf->nb_bytecount; 847 m->m_data = MDL_VA(buf); 848 MEXTADD(m, m->m_data, m->m_len, ndis_return_packet, 849 p, 0, EXT_NDIS); 850 p->np_refcnt++; 851 totlen += m->m_len; 852 if (m->m_flags & MT_HEADER) 853 *m0 = m; 854 else 855 prev->m_next = m; 856 prev = m; 857 } 858 859 (*m0)->m_pkthdr.len = totlen; 860 861 return(0); 862} 863 864/* 865 * Create an mbuf chain from an NDIS packet chain. 866 * This is used mainly when transmitting packets, where we need 867 * to turn an mbuf off an interface's send queue and transform it 868 * into an NDIS packet which will be fed into the NDIS driver's 869 * send routine. 870 * 871 * NDIS packets consist of two parts: an ndis_packet structure, 872 * which is vaguely analagous to the pkthdr portion of an mbuf, 873 * and one or more ndis_buffer structures, which define the 874 * actual memory segments in which the packet data resides. 875 * We need to allocate one ndis_buffer for each mbuf in a chain, 876 * plus one ndis_packet as the header. 877 */ 878 879int 880ndis_mtop(m0, p) 881 struct mbuf *m0; 882 ndis_packet **p; 883{ 884 struct mbuf *m; 885 ndis_buffer *buf = NULL, *prev = NULL; 886 ndis_packet_private *priv; 887 888 if (p == NULL || m0 == NULL) 889 return(EINVAL); 890 891 /* If caller didn't supply a packet, make one. */ 892 if (*p == NULL) { 893 *p = uma_zalloc(ndis_packet_zone, M_NOWAIT|M_ZERO); 894 895 if (*p == NULL) 896 return(ENOMEM); 897 } 898 899 priv = &(*p)->np_private; 900 priv->npp_totlen = m0->m_pkthdr.len; 901 priv->npp_packetooboffset = offsetof(ndis_packet, np_oob); 902 priv->npp_ndispktflags = NDIS_PACKET_ALLOCATED_BY_NDIS; 903 904 for (m = m0; m != NULL; m = m->m_next) { 905 if (m->m_len == 0) 906 continue; 907 buf = uma_zalloc(ndis_buffer_zone, M_NOWAIT | M_ZERO); 908 if (buf == NULL) { 909 ndis_free_packet(*p); 910 *p = NULL; 911 return(ENOMEM); 912 } 913 914 MDL_INIT(buf, m->m_data, m->m_len); 915 if (priv->npp_head == NULL) 916 priv->npp_head = buf; 917 else 918 prev->nb_next = buf; 919 prev = buf; 920 } 921 922 priv->npp_tail = buf; 923 priv->npp_totlen = m0->m_pkthdr.len; 924 925 return(0); 926} 927 928int 929ndis_get_supported_oids(arg, oids, oidcnt) 930 void *arg; 931 ndis_oid **oids; 932 int *oidcnt; 933{ 934 int len, rval; 935 ndis_oid *o; 936 937 if (arg == NULL || oids == NULL || oidcnt == NULL) 938 return(EINVAL); 939 len = 0; 940 ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len); 941 942 o = malloc(len, M_DEVBUF, M_NOWAIT); 943 if (o == NULL) 944 return(ENOMEM); 945 946 rval = ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, o, &len); 947 948 if (rval) { 949 free(o, M_DEVBUF); 950 return(rval); 951 } 952 953 *oids = o; 954 *oidcnt = len / 4; 955 956 return(0); 957} 958 959int 960ndis_set_info(arg, oid, buf, buflen) 961 void *arg; 962 ndis_oid oid; 963 void *buf; 964 int *buflen; 965{ 966 struct ndis_softc *sc; 967 ndis_status rval; 968 ndis_handle adapter; 969 __stdcall ndis_setinfo_handler setfunc; 970 uint32_t byteswritten = 0, bytesneeded = 0; 971 struct timeval tv; 972 int error; 973 974 sc = arg; 975 setfunc = sc->ndis_chars.nmc_setinfo_func; 976 adapter = sc->ndis_block.nmb_miniportadapterctx; 977 978 rval = setfunc(adapter, oid, buf, *buflen, 979 &byteswritten, &bytesneeded); 980 981 if (rval == NDIS_STATUS_PENDING) { 982 tv.tv_sec = 60; 983 tv.tv_usec = 0; 984 error = tsleep(&sc->ndis_block.nmb_wkupdpctimer, 985 PPAUSE|PCATCH, "ndisset", tvtohz(&tv)); 986 rval = sc->ndis_block.nmb_setstat; 987 } 988 989 if (byteswritten) 990 *buflen = byteswritten; 991 if (bytesneeded) 992 *buflen = bytesneeded; 993 994 if (rval == NDIS_STATUS_INVALID_LENGTH) 995 return(ENOSPC); 996 997 if (rval == NDIS_STATUS_INVALID_OID) 998 return(EINVAL); 999 1000 if (rval == NDIS_STATUS_NOT_SUPPORTED || 1001 rval == NDIS_STATUS_NOT_ACCEPTED) 1002 return(ENOTSUP); 1003 1004 return(0); 1005} 1006 1007typedef void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status); 1008 1009int 1010ndis_send_packets(arg, packets, cnt) 1011 void *arg; 1012 ndis_packet **packets; 1013 int cnt; 1014{ 1015 struct ndis_softc *sc; 1016 ndis_handle adapter; 1017 __stdcall ndis_sendmulti_handler sendfunc; 1018 __stdcall ndis_senddone_func senddonefunc; 1019 int i; 1020 ndis_packet *p; 1021 1022 sc = arg; 1023 adapter = sc->ndis_block.nmb_miniportadapterctx; 1024 sendfunc = sc->ndis_chars.nmc_sendmulti_func; 1025 senddonefunc = sc->ndis_block.nmb_senddone_func; 1026 sendfunc(adapter, packets, cnt); 1027 1028 for (i = 0; i < cnt; i++) { 1029 p = packets[i]; 1030 /* 1031 * Either the driver already handed the packet to 1032 * ndis_txeof() due to a failure, or it wants to keep 1033 * it and release it asynchronously later. Skip to the 1034 * next one. 1035 */ 1036 if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING) 1037 continue; 1038 senddonefunc(&sc->ndis_block, p, p->np_oob.npo_status); 1039 } 1040 1041 return(0); 1042} 1043 1044int 1045ndis_init_dma(arg) 1046 void *arg; 1047{ 1048 struct ndis_softc *sc; 1049 int i, error; 1050 1051 sc = arg; 1052 1053 sc->ndis_tmaps = malloc(sizeof(bus_dmamap_t) * sc->ndis_maxpkts, 1054 M_DEVBUF, M_NOWAIT|M_ZERO); 1055 1056 if (sc->ndis_tmaps == NULL) 1057 return(ENOMEM); 1058 1059 for (i = 0; i < sc->ndis_maxpkts; i++) { 1060 error = bus_dmamap_create(sc->ndis_ttag, 0, 1061 &sc->ndis_tmaps[i]); 1062 if (error) { 1063 free(sc->ndis_tmaps, M_DEVBUF); 1064 return(ENODEV); 1065 } 1066 } 1067 1068 return(0); 1069} 1070 1071int 1072ndis_destroy_dma(arg) 1073 void *arg; 1074{ 1075 struct ndis_softc *sc; 1076 struct mbuf *m; 1077 ndis_packet *p = NULL; 1078 int i; 1079 1080 sc = arg; 1081 1082 for (i = 0; i < sc->ndis_maxpkts; i++) { 1083 if (sc->ndis_txarray[i] != NULL) { 1084 p = sc->ndis_txarray[i]; 1085 m = (struct mbuf *)p->np_rsvd[1]; 1086 if (m != NULL) 1087 m_freem(m); 1088 ndis_free_packet(sc->ndis_txarray[i]); 1089 } 1090 bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]); 1091 } 1092 1093 free(sc->ndis_tmaps, M_DEVBUF); 1094 1095 bus_dma_tag_destroy(sc->ndis_ttag); 1096 1097 return(0); 1098} 1099 1100int 1101ndis_reset_nic(arg) 1102 void *arg; 1103{ 1104 struct ndis_softc *sc; 1105 ndis_handle adapter; 1106 __stdcall ndis_reset_handler resetfunc; 1107 uint8_t addressing_reset; 1108 struct ifnet *ifp; 1109 1110 sc = arg; 1111 ifp = &sc->arpcom.ac_if; 1112 adapter = sc->ndis_block.nmb_miniportadapterctx; 1113 if (adapter == NULL) 1114 return(EIO); 1115 resetfunc = sc->ndis_chars.nmc_reset_func; 1116 1117 if (resetfunc == NULL) 1118 return(EINVAL); 1119 1120 resetfunc(&addressing_reset, adapter); 1121 1122 return(0); 1123} 1124 1125int 1126ndis_halt_nic(arg) 1127 void *arg; 1128{ 1129 struct ndis_softc *sc; 1130 ndis_handle adapter; 1131 __stdcall ndis_halt_handler haltfunc; 1132 struct ifnet *ifp; 1133 struct ndis_timer_entry *ne; 1134 1135 sc = arg; 1136 ifp = &sc->arpcom.ac_if; 1137 adapter = sc->ndis_block.nmb_miniportadapterctx; 1138 if (adapter == NULL) 1139 return(EIO); 1140 1141 haltfunc = sc->ndis_chars.nmc_halt_func; 1142 1143 if (haltfunc == NULL) 1144 return(EINVAL); 1145 1146 haltfunc(adapter); 1147 1148 /* 1149 * The adapter context is only valid after the init 1150 * handler has been called, and is invalid once the 1151 * halt handler has been called. 1152 */ 1153 1154 sc->ndis_block.nmb_miniportadapterctx = NULL; 1155 1156 /* Clobber all the timers in case the driver left one running. */ 1157 1158 while (!TAILQ_EMPTY(&sc->ndis_block.nmb_timerlist)) { 1159 ne = TAILQ_FIRST(&sc->ndis_block.nmb_timerlist); 1160 TAILQ_REMOVE(&sc->ndis_block.nmb_timerlist, ne, link); 1161 callout_stop(&ne->nte_ch); 1162 free(ne, M_DEVBUF); 1163 } 1164 1165 return(0); 1166} 1167 1168int 1169ndis_shutdown_nic(arg) 1170 void *arg; 1171{ 1172 struct ndis_softc *sc; 1173 ndis_handle adapter; 1174 __stdcall ndis_shutdown_handler shutdownfunc; 1175 1176 1177 sc = arg; 1178 adapter = sc->ndis_block.nmb_miniportadapterctx; 1179 if (adapter == NULL) 1180 return(EIO); 1181 shutdownfunc = sc->ndis_chars.nmc_shutdown_handler; 1182 1183 if (shutdownfunc == NULL) 1184 return(EINVAL); 1185 1186 if (sc->ndis_chars.nmc_rsvd0 == NULL) 1187 shutdownfunc(adapter); 1188 else 1189 shutdownfunc(sc->ndis_chars.nmc_rsvd0); 1190 1191 return(0); 1192} 1193 1194int 1195ndis_init_nic(arg) 1196 void *arg; 1197{ 1198 struct ndis_softc *sc; 1199 ndis_miniport_block *block; 1200 __stdcall ndis_init_handler initfunc; 1201 ndis_status status, openstatus = 0; 1202 ndis_medium mediumarray[NdisMediumMax]; 1203 uint32_t chosenmedium, i; 1204 1205 if (arg == NULL) 1206 return(EINVAL); 1207 1208 sc = arg; 1209 block = &sc->ndis_block; 1210 initfunc = sc->ndis_chars.nmc_init_func; 1211 1212 TAILQ_INIT(&block->nmb_timerlist); 1213 1214 for (i = 0; i < NdisMediumMax; i++) 1215 mediumarray[i] = i; 1216 1217 status = initfunc(&openstatus, &chosenmedium, 1218 mediumarray, NdisMediumMax, block, block); 1219 1220 /* 1221 * If the init fails, blow away the other exported routines 1222 * we obtained from the driver so we can't call them later. 1223 * If the init failed, none of these will work. 1224 */ 1225 if (status != NDIS_STATUS_SUCCESS) { 1226 bzero((char *)&sc->ndis_chars, 1227 sizeof(ndis_miniport_characteristics)); 1228 return(ENXIO); 1229 } 1230 1231 return(0); 1232} 1233 1234void 1235ndis_enable_intr(arg) 1236 void *arg; 1237{ 1238 struct ndis_softc *sc; 1239 ndis_handle adapter; 1240 __stdcall ndis_enable_interrupts_handler intrenbfunc; 1241 1242 sc = arg; 1243 adapter = sc->ndis_block.nmb_miniportadapterctx; 1244 if (adapter == NULL) 1245 return; 1246 intrenbfunc = sc->ndis_chars.nmc_enable_interrupts_func; 1247 if (intrenbfunc == NULL) 1248 return; 1249 intrenbfunc(adapter); 1250 1251 return; 1252} 1253 1254void 1255ndis_disable_intr(arg) 1256 void *arg; 1257{ 1258 struct ndis_softc *sc; 1259 ndis_handle adapter; 1260 __stdcall ndis_disable_interrupts_handler intrdisfunc; 1261 1262 sc = arg; 1263 adapter = sc->ndis_block.nmb_miniportadapterctx; 1264 if (adapter == NULL) 1265 return; 1266 intrdisfunc = sc->ndis_chars.nmc_disable_interrupts_func; 1267 if (intrdisfunc == NULL) 1268 return; 1269 intrdisfunc(adapter); 1270 1271 return; 1272} 1273 1274int 1275ndis_isr(arg, ourintr, callhandler) 1276 void *arg; 1277 int *ourintr; 1278 int *callhandler; 1279{ 1280 struct ndis_softc *sc; 1281 ndis_handle adapter; 1282 __stdcall ndis_isr_handler isrfunc; 1283 uint8_t accepted, queue; 1284 1285 if (arg == NULL || ourintr == NULL || callhandler == NULL) 1286 return(EINVAL); 1287 1288 sc = arg; 1289 adapter = sc->ndis_block.nmb_miniportadapterctx; 1290 isrfunc = sc->ndis_chars.nmc_isr_func; 1291 isrfunc(&accepted, &queue, adapter); 1292 *ourintr = accepted; 1293 *callhandler = queue; 1294 1295 return(0); 1296} 1297 1298int 1299ndis_intrhand(arg) 1300 void *arg; 1301{ 1302 struct ndis_softc *sc; 1303 ndis_handle adapter; 1304 __stdcall ndis_interrupt_handler intrfunc; 1305 1306 if (arg == NULL) 1307 return(EINVAL); 1308 1309 sc = arg; 1310 adapter = sc->ndis_block.nmb_miniportadapterctx; 1311 intrfunc = sc->ndis_chars.nmc_interrupt_func; 1312 intrfunc(adapter); 1313 1314 return(0); 1315} 1316 1317int 1318ndis_get_info(arg, oid, buf, buflen) 1319 void *arg; 1320 ndis_oid oid; 1321 void *buf; 1322 int *buflen; 1323{ 1324 struct ndis_softc *sc; 1325 ndis_status rval; 1326 ndis_handle adapter; 1327 __stdcall ndis_queryinfo_handler queryfunc; 1328 uint32_t byteswritten = 0, bytesneeded = 0; 1329 struct timeval tv; 1330 int error; 1331 1332 sc = arg; 1333 queryfunc = sc->ndis_chars.nmc_queryinfo_func; 1334 adapter = sc->ndis_block.nmb_miniportadapterctx; 1335 1336 rval = queryfunc(adapter, oid, buf, *buflen, 1337 &byteswritten, &bytesneeded); 1338 1339 /* Wait for requests that block. */ 1340 1341 if (rval == NDIS_STATUS_PENDING) { 1342 tv.tv_sec = 60; 1343 tv.tv_usec = 0; 1344 error = tsleep(&sc->ndis_block.nmb_wkupdpctimer, 1345 PPAUSE|PCATCH, "ndisget", tvtohz(&tv)); 1346 rval = sc->ndis_block.nmb_getstat; 1347 } 1348 1349 if (byteswritten) 1350 *buflen = byteswritten; 1351 if (bytesneeded) 1352 *buflen = bytesneeded; 1353 1354 if (rval == NDIS_STATUS_INVALID_LENGTH || 1355 rval == NDIS_STATUS_BUFFER_TOO_SHORT) 1356 return(ENOSPC); 1357 1358 if (rval == NDIS_STATUS_INVALID_OID) 1359 return(EINVAL); 1360 1361 if (rval == NDIS_STATUS_NOT_SUPPORTED || 1362 rval == NDIS_STATUS_NOT_ACCEPTED) 1363 return(ENOTSUP); 1364 1365 return(0); 1366} 1367 1368int 1369ndis_unload_driver(arg) 1370 void *arg; 1371{ 1372 struct ndis_softc *sc; 1373 1374 sc = arg; 1375 1376 free(sc->ndis_block.nmb_rlist, M_DEVBUF); 1377 1378 ndis_flush_sysctls(sc); 1379 1380 ndis_shrink_thrqueue(8); 1381 1382 return(0); 1383} 1384 1385#define NDIS_LOADED 0x42534F44 1386 1387int 1388ndis_load_driver(img, arg) 1389 vm_offset_t img; 1390 void *arg; 1391{ 1392 __stdcall driver_entry entry; 1393 image_optional_header opt_hdr; 1394 image_import_descriptor imp_desc; 1395 ndis_unicode_string dummystr; 1396 ndis_driver_object drv; 1397 ndis_miniport_block *block; 1398 ndis_status status; 1399 int idx; 1400 uint32_t *ptr; 1401 struct ndis_softc *sc; 1402 1403 sc = arg; 1404 1405 /* 1406 * Only perform the relocation/linking phase once 1407 * since the binary image may be shared among multiple 1408 * device instances. 1409 */ 1410 1411 ptr = (uint32_t *)(img + 8); 1412 if (*ptr != NDIS_LOADED) { 1413 /* Perform text relocation */ 1414 if (pe_relocate(img)) 1415 return(ENOEXEC); 1416 1417 /* Dynamically link the NDIS.SYS routines -- required. */ 1418 if (pe_patch_imports(img, "NDIS", ndis_functbl)) 1419 return(ENOEXEC); 1420 1421 /* Dynamically link the HAL.dll routines -- also required. */ 1422 if (pe_patch_imports(img, "HAL", hal_functbl)) 1423 return(ENOEXEC); 1424 1425 /* Dynamically link ntoskrnl.exe -- optional. */ 1426 if (pe_get_import_descriptor(img, 1427 &imp_desc, "ntoskrnl") == 0) { 1428 if (pe_patch_imports(img, 1429 "ntoskrnl", ntoskrnl_functbl)) 1430 return(ENOEXEC); 1431 } 1432 *ptr = NDIS_LOADED; 1433 } 1434 1435 /* Locate the driver entry point */ 1436 pe_get_optional_header(img, &opt_hdr); 1437 entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr); 1438 1439 /* 1440 * Now call the DriverEntry() routine. This will cause 1441 * a callout to the NdisInitializeWrapper() and 1442 * NdisMRegisterMiniport() routines. 1443 */ 1444 dummystr.nus_len = strlen(NDIS_DUMMY_PATH); 1445 dummystr.nus_maxlen = strlen(NDIS_DUMMY_PATH); 1446 dummystr.nus_buf = NULL; 1447 ndis_ascii_to_unicode(NDIS_DUMMY_PATH, &dummystr.nus_buf); 1448 drv.ndo_ifname = "ndis0"; 1449 1450 status = entry(&drv, &dummystr); 1451 1452 free (dummystr.nus_buf, M_DEVBUF); 1453 1454 if (status != NDIS_STATUS_SUCCESS) 1455 return(ENODEV); 1456 1457 /* 1458 * Now that we have the miniport driver characteristics, 1459 * create an NDIS block and call the init handler. 1460 * This will cause the driver to try to probe for 1461 * a device. 1462 */ 1463 1464 block = &sc->ndis_block; 1465 bcopy((char *)&drv.ndo_chars, (char *)&sc->ndis_chars, 1466 sizeof(ndis_miniport_characteristics)); 1467 1468 /*block->nmb_signature = 0xcafebabe;*/ 1469 1470 ptr = (uint32_t *)block; 1471 for (idx = 0; idx < sizeof(ndis_miniport_block) / 4; idx++) { 1472 *ptr = idx | 0xdead0000; 1473 ptr++; 1474 } 1475 1476 block->nmb_signature = (void *)0xcafebabe; 1477 block->nmb_setdone_func = ndis_setdone_func; 1478 block->nmb_querydone_func = ndis_getdone_func; 1479 block->nmb_status_func = ndis_status_func; 1480 block->nmb_statusdone_func = ndis_statusdone_func; 1481 block->nmb_resetdone_func = ndis_resetdone_func; 1482 block->nmb_sendrsrc_func = ndis_sendrsrcavail_func; 1483 1484 block->nmb_ifp = &sc->arpcom.ac_if; 1485 block->nmb_dev = sc->ndis_dev; 1486 block->nmb_img = img; 1487 1488 ndis_enlarge_thrqueue(8); 1489 1490 return(0); 1491} 1492