kern_ndis.c revision 128012
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 128012 2004-04-07 17:02:55Z 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 <compat/ndis/pe_var.h> 70#include <compat/ndis/resource_var.h> 71#include <compat/ndis/ntoskrnl_var.h> 72#include <compat/ndis/ndis_var.h> 73#include <compat/ndis/hal_var.h> 74#include <compat/ndis/cfg_var.h> 75#include <dev/if_ndis/if_ndisvar.h> 76 77#define NDIS_DUMMY_PATH "\\\\some\\bogus\\path" 78 79__stdcall static void ndis_status_func(ndis_handle, ndis_status, 80 void *, uint32_t); 81__stdcall static void ndis_statusdone_func(ndis_handle); 82__stdcall static void ndis_setdone_func(ndis_handle, ndis_status); 83__stdcall static void ndis_getdone_func(ndis_handle, ndis_status); 84__stdcall static void ndis_resetdone_func(ndis_handle, ndis_status, uint8_t); 85__stdcall static void ndis_sendrsrcavail_func(ndis_handle); 86 87struct nd_head ndis_devhead; 88 89struct ndis_req { 90 void (*nr_func)(void *); 91 void *nr_arg; 92 int nr_exit; 93 STAILQ_ENTRY(ndis_req) link; 94}; 95 96struct ndisproc { 97 struct ndisqhead *np_q; 98 struct proc *np_p; 99 int np_state; 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 TAILQ_INIT(&ndis_devhead); 148 149 break; 150 case MOD_SHUTDOWN: 151 /* stop kthreads */ 152 ndis_destroy_kthreads(); 153 if (TAILQ_FIRST(&ndis_devhead) == NULL) { 154 /* Shut down subsystems */ 155 ndis_libfini(); 156 ntoskrnl_libfini(); 157 158 /* Remove zones */ 159 uma_zdestroy(ndis_packet_zone); 160 uma_zdestroy(ndis_buffer_zone); 161 } 162 break; 163 case MOD_UNLOAD: 164 /* stop kthreads */ 165 ndis_destroy_kthreads(); 166 167 /* Shut down subsystems */ 168 ndis_libfini(); 169 ntoskrnl_libfini(); 170 171 /* Remove zones */ 172 uma_zdestroy(ndis_packet_zone); 173 uma_zdestroy(ndis_buffer_zone); 174 break; 175 default: 176 error = EINVAL; 177 break; 178 } 179 180 return(error); 181} 182DEV_MODULE(ndisapi, ndis_modevent, NULL); 183MODULE_VERSION(ndisapi, 1); 184 185/* 186 * We create two kthreads for the NDIS subsystem. One of them is a task 187 * queue for performing various odd jobs. The other is an swi thread 188 * reserved exclusively for running interrupt handlers. The reason we 189 * have our own task queue is that there are some cases where we may 190 * need to sleep for a significant amount of time, and if we were to 191 * use one of the taskqueue threads, we might delay the processing 192 * of other pending tasks which might need to run right away. We have 193 * a separate swi thread because we don't want our interrupt handling 194 * to be delayed either. 195 * 196 * By default there are 32 jobs available to start, and another 8 197 * are added to the free list each time a new device is created. 198 */ 199 200static void 201ndis_runq(arg) 202 void *arg; 203{ 204 struct ndis_req *r = NULL, *die = NULL; 205 struct ndisproc *p; 206 207 p = arg; 208 209 while (1) { 210 kthread_suspend(p->np_p, 0); 211 212 /* Look for any jobs on the work queue. */ 213 214 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 215 p->np_state = NDIS_PSTATE_RUNNING; 216 while(STAILQ_FIRST(p->np_q) != NULL) { 217 r = STAILQ_FIRST(p->np_q); 218 STAILQ_REMOVE_HEAD(p->np_q, link); 219 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 220 221 /* Do the work. */ 222 223 if (r->nr_func != NULL) 224 (*r->nr_func)(r->nr_arg); 225 226 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 227 STAILQ_INSERT_HEAD(&ndis_free, r, link); 228 229 /* Check for a shutdown request */ 230 231 if (r->nr_exit == TRUE) 232 die = r; 233 } 234 p->np_state = NDIS_PSTATE_SLEEPING; 235 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 236 237 /* Bail if we were told to shut down. */ 238 239 if (die != NULL) 240 break; 241 } 242 243 wakeup(die); 244 kthread_exit(0); 245} 246 247static int 248ndis_create_kthreads() 249{ 250 struct ndis_req *r; 251 int i, error = 0; 252 253 ndis_thr_mtx = mtx_pool_alloc(ndis_mtxpool); 254 STAILQ_INIT(&ndis_ttodo); 255 STAILQ_INIT(&ndis_itodo); 256 STAILQ_INIT(&ndis_free); 257 258 for (i = 0; i < ndis_jobs; i++) { 259 r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK); 260 if (r == NULL) { 261 error = ENOMEM; 262 break; 263 } 264 STAILQ_INSERT_HEAD(&ndis_free, r, link); 265 } 266 267 if (error == 0) { 268 ndis_tproc.np_q = &ndis_ttodo; 269 ndis_tproc.np_state = NDIS_PSTATE_SLEEPING; 270 error = kthread_create(ndis_runq, &ndis_tproc, 271 &ndis_tproc.np_p, RFHIGHPID, 272 NDIS_KSTACK_PAGES, "ndis taskqueue"); 273 } 274 275 if (error == 0) { 276 ndis_iproc.np_q = &ndis_itodo; 277 ndis_iproc.np_state = NDIS_PSTATE_SLEEPING; 278 error = kthread_create(ndis_runq, &ndis_iproc, 279 &ndis_iproc.np_p, RFHIGHPID, 280 NDIS_KSTACK_PAGES, "ndis swi"); 281 } 282 283 if (error) { 284 while ((r = STAILQ_FIRST(&ndis_free)) != NULL) { 285 STAILQ_REMOVE_HEAD(&ndis_free, link); 286 free(r, M_DEVBUF); 287 } 288 return(error); 289 } 290 291 return(0); 292} 293 294static void 295ndis_destroy_kthreads() 296{ 297 struct ndis_req *r; 298 299 /* Stop the threads. */ 300 301 ndis_stop_thread(NDIS_TASKQUEUE); 302 ndis_stop_thread(NDIS_SWI); 303 304 /* Destroy request structures. */ 305 306 while ((r = STAILQ_FIRST(&ndis_free)) != NULL) { 307 STAILQ_REMOVE_HEAD(&ndis_free, link); 308 free(r, M_DEVBUF); 309 } 310 311 return; 312} 313 314static void 315ndis_stop_thread(t) 316 int t; 317{ 318 struct ndis_req *r; 319 struct ndisqhead *q; 320 struct proc *p; 321 322 if (t == NDIS_TASKQUEUE) { 323 q = &ndis_ttodo; 324 p = ndis_tproc.np_p; 325 } else { 326 q = &ndis_itodo; 327 p = ndis_iproc.np_p; 328 } 329 330 /* Create and post a special 'exit' job. */ 331 332 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 333 r = STAILQ_FIRST(&ndis_free); 334 STAILQ_REMOVE_HEAD(&ndis_free, link); 335 r->nr_func = NULL; 336 r->nr_arg = NULL; 337 r->nr_exit = TRUE; 338 STAILQ_INSERT_TAIL(q, r, link); 339 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 340 341 kthread_resume(p); 342 343 /* wait for thread exit */ 344 345 tsleep(r, PPAUSE|PCATCH, "ndisthrexit", hz * 60); 346 347 /* Now empty the job list. */ 348 349 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 350 while ((r = STAILQ_FIRST(q)) != NULL) { 351 STAILQ_REMOVE_HEAD(q, link); 352 STAILQ_INSERT_HEAD(&ndis_free, r, link); 353 } 354 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 355 356 return; 357} 358 359static int 360ndis_enlarge_thrqueue(cnt) 361 int cnt; 362{ 363 struct ndis_req *r; 364 int i; 365 366 for (i = 0; i < cnt; i++) { 367 r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK); 368 if (r == NULL) 369 return(ENOMEM); 370 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 371 STAILQ_INSERT_HEAD(&ndis_free, r, link); 372 ndis_jobs++; 373 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 374 } 375 376 return(0); 377} 378 379static int 380ndis_shrink_thrqueue(cnt) 381 int cnt; 382{ 383 struct ndis_req *r; 384 int i; 385 386 for (i = 0; i < cnt; i++) { 387 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 388 r = STAILQ_FIRST(&ndis_free); 389 if (r == NULL) { 390 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 391 return(ENOMEM); 392 } 393 STAILQ_REMOVE_HEAD(&ndis_free, link); 394 ndis_jobs--; 395 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 396 free(r, M_DEVBUF); 397 } 398 399 return(0); 400} 401 402int 403ndis_unsched(func, arg, t) 404 void (*func)(void *); 405 void *arg; 406 int t; 407{ 408 struct ndis_req *r; 409 struct ndisqhead *q; 410 struct proc *p; 411 412 if (t == NDIS_TASKQUEUE) { 413 q = &ndis_ttodo; 414 p = ndis_tproc.np_p; 415 } else { 416 q = &ndis_itodo; 417 p = ndis_iproc.np_p; 418 } 419 420 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 421 STAILQ_FOREACH(r, q, link) { 422 if (r->nr_func == func && r->nr_arg == arg) { 423 STAILQ_REMOVE(q, r, ndis_req, link); 424 STAILQ_INSERT_HEAD(&ndis_free, r, link); 425 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 426 return(0); 427 } 428 } 429 430 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 431 432 return(ENOENT); 433} 434 435int 436ndis_sched(func, arg, t) 437 void (*func)(void *); 438 void *arg; 439 int t; 440{ 441 struct ndis_req *r; 442 struct ndisqhead *q; 443 struct proc *p; 444 int s; 445 446 if (t == NDIS_TASKQUEUE) { 447 q = &ndis_ttodo; 448 p = ndis_tproc.np_p; 449 } else { 450 q = &ndis_itodo; 451 p = ndis_iproc.np_p; 452 } 453 454 mtx_pool_lock(ndis_mtxpool, ndis_thr_mtx); 455 /* 456 * Check to see if an instance of this job is already 457 * pending. If so, don't bother queuing it again. 458 */ 459 STAILQ_FOREACH(r, q, link) { 460 if (r->nr_func == func && r->nr_arg == arg) { 461 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 462 return(0); 463 } 464 } 465 r = STAILQ_FIRST(&ndis_free); 466 if (r == NULL) { 467 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 468 return(EAGAIN); 469 } 470 STAILQ_REMOVE_HEAD(&ndis_free, link); 471 r->nr_func = func; 472 r->nr_arg = arg; 473 r->nr_exit = FALSE; 474 STAILQ_INSERT_TAIL(q, r, link); 475 if (t == NDIS_TASKQUEUE) 476 s = ndis_tproc.np_state; 477 else 478 s = ndis_iproc.np_state; 479 mtx_pool_unlock(ndis_mtxpool, ndis_thr_mtx); 480 481 /* 482 * Post the job, but only if the thread is actually blocked 483 * on its own suspend call. If a driver queues up a job with 484 * NdisScheduleWorkItem() which happens to do a KeWaitForObject(), 485 * it may suspend there, and in that case we don't want to wake 486 * it up until KeWaitForObject() gets woken up on its own. 487 */ 488 if (s == NDIS_PSTATE_SLEEPING) 489 kthread_resume(p); 490 491 return(0); 492} 493 494__stdcall static void 495ndis_sendrsrcavail_func(adapter) 496 ndis_handle adapter; 497{ 498 return; 499} 500 501__stdcall static void 502ndis_status_func(adapter, status, sbuf, slen) 503 ndis_handle adapter; 504 ndis_status status; 505 void *sbuf; 506 uint32_t slen; 507{ 508 ndis_miniport_block *block; 509 block = adapter; 510 511 if (block->nmb_ifp->if_flags & IFF_DEBUG) 512 device_printf (block->nmb_dev, "status: %x\n", status); 513 return; 514} 515 516__stdcall static void 517ndis_statusdone_func(adapter) 518 ndis_handle adapter; 519{ 520 ndis_miniport_block *block; 521 block = adapter; 522 523 if (block->nmb_ifp->if_flags & IFF_DEBUG) 524 device_printf (block->nmb_dev, "status complete\n"); 525 return; 526} 527 528__stdcall static void 529ndis_setdone_func(adapter, status) 530 ndis_handle adapter; 531 ndis_status status; 532{ 533 ndis_miniport_block *block; 534 block = adapter; 535 536 block->nmb_setstat = status; 537 wakeup(&block->nmb_wkupdpctimer); 538 return; 539} 540 541__stdcall static void 542ndis_getdone_func(adapter, status) 543 ndis_handle adapter; 544 ndis_status status; 545{ 546 ndis_miniport_block *block; 547 block = adapter; 548 549 block->nmb_getstat = status; 550 wakeup(&block->nmb_wkupdpctimer); 551 return; 552} 553 554__stdcall static void 555ndis_resetdone_func(adapter, status, addressingreset) 556 ndis_handle adapter; 557 ndis_status status; 558 uint8_t addressingreset; 559{ 560 ndis_miniport_block *block; 561 block = adapter; 562 563 if (block->nmb_ifp->if_flags & IFF_DEBUG) 564 device_printf (block->nmb_dev, "reset done...\n"); 565 wakeup(block->nmb_ifp); 566 return; 567} 568 569int 570ndis_create_sysctls(arg) 571 void *arg; 572{ 573 struct ndis_softc *sc; 574 ndis_cfg *vals; 575 char buf[256]; 576 577 if (arg == NULL) 578 return(EINVAL); 579 580 sc = arg; 581 vals = sc->ndis_regvals; 582 583 TAILQ_INIT(&sc->ndis_cfglist_head); 584 585 /* Create the sysctl tree. */ 586 587 sc->ndis_tree = SYSCTL_ADD_NODE(&sc->ndis_ctx, 588 SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, 589 device_get_nameunit(sc->ndis_dev), CTLFLAG_RD, 0, 590 device_get_desc(sc->ndis_dev)); 591 592 /* Add the driver-specific registry keys. */ 593 594 vals = sc->ndis_regvals; 595 while(1) { 596 if (vals->nc_cfgkey == NULL) 597 break; 598 if (vals->nc_idx != sc->ndis_devidx) { 599 vals++; 600 continue; 601 } 602 SYSCTL_ADD_STRING(&sc->ndis_ctx, 603 SYSCTL_CHILDREN(sc->ndis_tree), 604 OID_AUTO, vals->nc_cfgkey, 605 CTLFLAG_RW, vals->nc_val, 606 sizeof(vals->nc_val), 607 vals->nc_cfgdesc); 608 vals++; 609 } 610 611 /* Now add a couple of builtin keys. */ 612 613 /* 614 * Environment can be either Windows (0) or WindowsNT (1). 615 * We qualify as the latter. 616 */ 617 ndis_add_sysctl(sc, "Environment", 618 "Windows environment", "1", CTLFLAG_RD); 619 620 /* NDIS version should be 5.1. */ 621 ndis_add_sysctl(sc, "NdisVersion", 622 "NDIS API Version", "0x00050001", CTLFLAG_RD); 623 624 /* Bus type (PCI, PCMCIA, etc...) */ 625 sprintf(buf, "%d", (int)sc->ndis_iftype); 626 ndis_add_sysctl(sc, "BusType", "Bus Type", buf, CTLFLAG_RD); 627 628 if (sc->ndis_res_io != NULL) { 629 sprintf(buf, "0x%lx", rman_get_start(sc->ndis_res_io)); 630 ndis_add_sysctl(sc, "IOBaseAddress", 631 "Base I/O Address", buf, CTLFLAG_RD); 632 } 633 634 if (sc->ndis_irq != NULL) { 635 sprintf(buf, "%lu", rman_get_start(sc->ndis_irq)); 636 ndis_add_sysctl(sc, "InterruptNumber", 637 "Interrupt Number", buf, CTLFLAG_RD); 638 } 639 640 return(0); 641} 642 643int 644ndis_add_sysctl(arg, key, desc, val, flag) 645 void *arg; 646 char *key; 647 char *desc; 648 char *val; 649 int flag; 650{ 651 struct ndis_softc *sc; 652 struct ndis_cfglist *cfg; 653 char descstr[256]; 654 655 sc = arg; 656 657 cfg = malloc(sizeof(struct ndis_cfglist), M_DEVBUF, M_NOWAIT|M_ZERO); 658 659 if (cfg == NULL) 660 return(ENOMEM); 661 662 cfg->ndis_cfg.nc_cfgkey = strdup(key, M_DEVBUF); 663 if (desc == NULL) { 664 snprintf(descstr, sizeof(descstr), "%s (dynamic)", key); 665 cfg->ndis_cfg.nc_cfgdesc = strdup(descstr, M_DEVBUF); 666 } else 667 cfg->ndis_cfg.nc_cfgdesc = strdup(desc, M_DEVBUF); 668 strcpy(cfg->ndis_cfg.nc_val, val); 669 670 TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link); 671 672 SYSCTL_ADD_STRING(&sc->ndis_ctx, SYSCTL_CHILDREN(sc->ndis_tree), 673 OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag, 674 cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val), 675 cfg->ndis_cfg.nc_cfgdesc); 676 677 return(0); 678} 679 680int 681ndis_flush_sysctls(arg) 682 void *arg; 683{ 684 struct ndis_softc *sc; 685 struct ndis_cfglist *cfg; 686 687 sc = arg; 688 689 while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) { 690 cfg = TAILQ_FIRST(&sc->ndis_cfglist_head); 691 TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link); 692 free(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF); 693 free(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF); 694 free(cfg, M_DEVBUF); 695 } 696 697 return(0); 698} 699 700void 701ndis_return_packet(buf, arg) 702 void *buf; /* not used */ 703 void *arg; 704{ 705 struct ndis_softc *sc; 706 ndis_handle adapter; 707 ndis_packet *p; 708 __stdcall ndis_return_handler returnfunc; 709 710 if (arg == NULL) 711 return; 712 713 p = arg; 714 715 /* Decrement refcount. */ 716 p->np_refcnt--; 717 718 /* Release packet when refcount hits zero, otherwise return. */ 719 if (p->np_refcnt) 720 return; 721 722 sc = p->np_softc; 723 returnfunc = sc->ndis_chars.nmc_return_packet_func; 724 adapter = sc->ndis_block.nmb_miniportadapterctx; 725 if (adapter != NULL) 726 returnfunc(adapter, p); 727 728 return; 729} 730 731void 732ndis_free_bufs(b0) 733 ndis_buffer *b0; 734{ 735 ndis_buffer *next; 736 737 if (b0 == NULL) 738 return; 739 740 while(b0 != NULL) { 741 next = b0->nb_next; 742 uma_zfree (ndis_buffer_zone, b0); 743 b0 = next; 744 } 745 746 return; 747} 748 749void 750ndis_free_packet(p) 751 ndis_packet *p; 752{ 753 if (p == NULL) 754 return; 755 756 ndis_free_bufs(p->np_private.npp_head); 757 uma_zfree(ndis_packet_zone, p); 758 759 return; 760} 761 762int 763ndis_convert_res(arg) 764 void *arg; 765{ 766 struct ndis_softc *sc; 767 ndis_resource_list *rl = NULL; 768 cm_partial_resource_desc *prd = NULL; 769 ndis_miniport_block *block; 770 device_t dev; 771 struct resource_list *brl; 772 struct resource_list brl_rev; 773 struct resource_list_entry *brle, *n; 774 int error = 0; 775 776 sc = arg; 777 block = &sc->ndis_block; 778 dev = sc->ndis_dev; 779 780 SLIST_INIT(&brl_rev); 781 782 rl = malloc(sizeof(ndis_resource_list) + 783 (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)), 784 M_DEVBUF, M_NOWAIT|M_ZERO); 785 786 if (rl == NULL) 787 return(ENOMEM); 788 789 rl->cprl_version = 5; 790 rl->cprl_version = 1; 791 rl->cprl_count = sc->ndis_rescnt; 792 prd = rl->cprl_partial_descs; 793 794 brl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); 795 if (brl != NULL) { 796 797 /* 798 * We have a small problem. Some PCI devices have 799 * multiple I/O ranges. Windows orders them starting 800 * from lowest numbered BAR to highest. We discover 801 * them in that order too, but insert them into a singly 802 * linked list head first, which means when time comes 803 * to traverse the list, we enumerate them in reverse 804 * order. This screws up some drivers which expect the 805 * BARs to be in ascending order so that they can choose 806 * the "first" one as their register space. Unfortunately, 807 * in order to fix this, we have to create our own 808 * temporary list with the entries in reverse order. 809 */ 810 SLIST_FOREACH(brle, brl, link) { 811 n = malloc(sizeof(struct resource_list_entry), 812 M_TEMP, M_NOWAIT); 813 if (n == NULL) { 814 error = ENOMEM; 815 goto bad; 816 } 817 bcopy((char *)brle, (char *)n, 818 sizeof(struct resource_list_entry)); 819 SLIST_INSERT_HEAD(&brl_rev, n, link); 820 } 821 822 SLIST_FOREACH(brle, &brl_rev, link) { 823 switch (brle->type) { 824 case SYS_RES_IOPORT: 825 prd->cprd_type = CmResourceTypePort; 826 prd->cprd_flags = CM_RESOURCE_PORT_IO; 827 prd->cprd_sharedisp = 828 CmResourceShareDeviceExclusive; 829 prd->u.cprd_port.cprd_start.np_quad = 830 brle->start; 831 prd->u.cprd_port.cprd_len = brle->count; 832 break; 833 case SYS_RES_MEMORY: 834 prd->cprd_type = CmResourceTypeMemory; 835 prd->cprd_flags = 836 CM_RESOURCE_MEMORY_READ_WRITE; 837 prd->cprd_sharedisp = 838 CmResourceShareDeviceExclusive; 839 prd->u.cprd_port.cprd_start.np_quad = 840 brle->start; 841 prd->u.cprd_port.cprd_len = brle->count; 842 break; 843 case SYS_RES_IRQ: 844 prd->cprd_type = CmResourceTypeInterrupt; 845 prd->cprd_flags = 0; 846 prd->cprd_sharedisp = 847 CmResourceShareDeviceExclusive; 848 prd->u.cprd_intr.cprd_level = brle->start; 849 prd->u.cprd_intr.cprd_vector = brle->start; 850 prd->u.cprd_intr.cprd_affinity = 0; 851 break; 852 default: 853 break; 854 } 855 prd++; 856 } 857 } 858 859 block->nmb_rlist = rl; 860 861bad: 862 863 while (!SLIST_EMPTY(&brl_rev)) { 864 n = SLIST_FIRST(&brl_rev); 865 SLIST_REMOVE_HEAD(&brl_rev, link); 866 free (n, M_TEMP); 867 } 868 869 return(error); 870} 871 872/* 873 * Map an NDIS packet to an mbuf list. When an NDIS driver receives a 874 * packet, it will hand it to us in the form of an ndis_packet, 875 * which we need to convert to an mbuf that is then handed off 876 * to the stack. Note: we configure the mbuf list so that it uses 877 * the memory regions specified by the ndis_buffer structures in 878 * the ndis_packet as external storage. In most cases, this will 879 * point to a memory region allocated by the driver (either by 880 * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect 881 * the driver to handle free()ing this region for is, so we set up 882 * a dummy no-op free handler for it. 883 */ 884 885int 886ndis_ptom(m0, p) 887 struct mbuf **m0; 888 ndis_packet *p; 889{ 890 struct mbuf *m, *prev = NULL; 891 ndis_buffer *buf; 892 ndis_packet_private *priv; 893 uint32_t totlen = 0; 894 895 if (p == NULL || m0 == NULL) 896 return(EINVAL); 897 898 priv = &p->np_private; 899 buf = priv->npp_head; 900 p->np_refcnt = 0; 901 902 for (buf = priv->npp_head; buf != NULL; buf = buf->nb_next) { 903 if (buf == priv->npp_head) 904 MGETHDR(m, M_DONTWAIT, MT_HEADER); 905 else 906 MGET(m, M_DONTWAIT, MT_DATA); 907 if (m == NULL) { 908 m_freem(*m0); 909 *m0 = NULL; 910 return(ENOBUFS); 911 } 912 m->m_len = buf->nb_bytecount; 913 m->m_data = MDL_VA(buf); 914 MEXTADD(m, m->m_data, m->m_len, ndis_return_packet, 915 p, 0, EXT_NDIS); 916 p->np_refcnt++; 917 totlen += m->m_len; 918 if (m->m_flags & MT_HEADER) 919 *m0 = m; 920 else 921 prev->m_next = m; 922 prev = m; 923 } 924 925 (*m0)->m_pkthdr.len = totlen; 926 927 return(0); 928} 929 930/* 931 * Create an mbuf chain from an NDIS packet chain. 932 * This is used mainly when transmitting packets, where we need 933 * to turn an mbuf off an interface's send queue and transform it 934 * into an NDIS packet which will be fed into the NDIS driver's 935 * send routine. 936 * 937 * NDIS packets consist of two parts: an ndis_packet structure, 938 * which is vaguely analagous to the pkthdr portion of an mbuf, 939 * and one or more ndis_buffer structures, which define the 940 * actual memory segments in which the packet data resides. 941 * We need to allocate one ndis_buffer for each mbuf in a chain, 942 * plus one ndis_packet as the header. 943 */ 944 945int 946ndis_mtop(m0, p) 947 struct mbuf *m0; 948 ndis_packet **p; 949{ 950 struct mbuf *m; 951 ndis_buffer *buf = NULL, *prev = NULL; 952 ndis_packet_private *priv; 953 954 if (p == NULL || m0 == NULL) 955 return(EINVAL); 956 957 /* If caller didn't supply a packet, make one. */ 958 if (*p == NULL) { 959 *p = uma_zalloc(ndis_packet_zone, M_NOWAIT|M_ZERO); 960 961 if (*p == NULL) 962 return(ENOMEM); 963 } 964 965 priv = &(*p)->np_private; 966 priv->npp_totlen = m0->m_pkthdr.len; 967 priv->npp_packetooboffset = offsetof(ndis_packet, np_oob); 968 priv->npp_ndispktflags = NDIS_PACKET_ALLOCATED_BY_NDIS; 969 970 for (m = m0; m != NULL; m = m->m_next) { 971 if (m->m_len == 0) 972 continue; 973 buf = uma_zalloc(ndis_buffer_zone, M_NOWAIT | M_ZERO); 974 if (buf == NULL) { 975 ndis_free_packet(*p); 976 *p = NULL; 977 return(ENOMEM); 978 } 979 980 MDL_INIT(buf, m->m_data, m->m_len); 981 if (priv->npp_head == NULL) 982 priv->npp_head = buf; 983 else 984 prev->nb_next = buf; 985 prev = buf; 986 } 987 988 priv->npp_tail = buf; 989 priv->npp_totlen = m0->m_pkthdr.len; 990 991 return(0); 992} 993 994int 995ndis_get_supported_oids(arg, oids, oidcnt) 996 void *arg; 997 ndis_oid **oids; 998 int *oidcnt; 999{ 1000 int len, rval; 1001 ndis_oid *o; 1002 1003 if (arg == NULL || oids == NULL || oidcnt == NULL) 1004 return(EINVAL); 1005 len = 0; 1006 ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len); 1007 1008 o = malloc(len, M_DEVBUF, M_NOWAIT); 1009 if (o == NULL) 1010 return(ENOMEM); 1011 1012 rval = ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, o, &len); 1013 1014 if (rval) { 1015 free(o, M_DEVBUF); 1016 return(rval); 1017 } 1018 1019 *oids = o; 1020 *oidcnt = len / 4; 1021 1022 return(0); 1023} 1024 1025int 1026ndis_set_info(arg, oid, buf, buflen) 1027 void *arg; 1028 ndis_oid oid; 1029 void *buf; 1030 int *buflen; 1031{ 1032 struct ndis_softc *sc; 1033 ndis_status rval; 1034 ndis_handle adapter; 1035 __stdcall ndis_setinfo_handler setfunc; 1036 uint32_t byteswritten = 0, bytesneeded = 0; 1037 int error; 1038 1039 sc = arg; 1040 NDIS_LOCK(sc); 1041 setfunc = sc->ndis_chars.nmc_setinfo_func; 1042 adapter = sc->ndis_block.nmb_miniportadapterctx; 1043 NDIS_UNLOCK(sc); 1044 1045 if (adapter == NULL || setfunc == NULL) 1046 return(ENXIO); 1047 1048 rval = setfunc(adapter, oid, buf, *buflen, 1049 &byteswritten, &bytesneeded); 1050 1051 if (rval == NDIS_STATUS_PENDING) { 1052 PROC_LOCK(curthread->td_proc); 1053 error = msleep(&sc->ndis_block.nmb_wkupdpctimer, 1054 &curthread->td_proc->p_mtx, PPAUSE|PDROP, 1055 "ndisset", 5 * hz); 1056 rval = sc->ndis_block.nmb_setstat; 1057 } 1058 1059 if (byteswritten) 1060 *buflen = byteswritten; 1061 if (bytesneeded) 1062 *buflen = bytesneeded; 1063 1064 if (rval == NDIS_STATUS_INVALID_LENGTH) 1065 return(ENOSPC); 1066 1067 if (rval == NDIS_STATUS_INVALID_OID) 1068 return(EINVAL); 1069 1070 if (rval == NDIS_STATUS_NOT_SUPPORTED || 1071 rval == NDIS_STATUS_NOT_ACCEPTED) 1072 return(ENOTSUP); 1073 1074 if (rval != NDIS_STATUS_SUCCESS) 1075 return(ENODEV); 1076 1077 return(0); 1078} 1079 1080typedef void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status); 1081 1082int 1083ndis_send_packets(arg, packets, cnt) 1084 void *arg; 1085 ndis_packet **packets; 1086 int cnt; 1087{ 1088 struct ndis_softc *sc; 1089 ndis_handle adapter; 1090 __stdcall ndis_sendmulti_handler sendfunc; 1091 __stdcall ndis_senddone_func senddonefunc; 1092 int i; 1093 ndis_packet *p; 1094 1095 sc = arg; 1096 adapter = sc->ndis_block.nmb_miniportadapterctx; 1097 if (adapter == NULL) 1098 return(ENXIO); 1099 sendfunc = sc->ndis_chars.nmc_sendmulti_func; 1100 senddonefunc = sc->ndis_block.nmb_senddone_func; 1101 sendfunc(adapter, packets, cnt); 1102 1103 for (i = 0; i < cnt; i++) { 1104 p = packets[i]; 1105 /* 1106 * Either the driver already handed the packet to 1107 * ndis_txeof() due to a failure, or it wants to keep 1108 * it and release it asynchronously later. Skip to the 1109 * next one. 1110 */ 1111 if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING) 1112 continue; 1113 senddonefunc(&sc->ndis_block, p, p->np_oob.npo_status); 1114 } 1115 1116 return(0); 1117} 1118 1119int 1120ndis_send_packet(arg, packet) 1121 void *arg; 1122 ndis_packet *packet; 1123{ 1124 struct ndis_softc *sc; 1125 ndis_handle adapter; 1126 ndis_status status; 1127 __stdcall ndis_sendsingle_handler sendfunc; 1128 __stdcall ndis_senddone_func senddonefunc; 1129 1130 sc = arg; 1131 adapter = sc->ndis_block.nmb_miniportadapterctx; 1132 if (adapter == NULL) 1133 return(ENXIO); 1134 sendfunc = sc->ndis_chars.nmc_sendsingle_func; 1135 senddonefunc = sc->ndis_block.nmb_senddone_func; 1136 1137 status = sendfunc(adapter, packet, packet->np_private.npp_flags); 1138 1139 if (status == NDIS_STATUS_PENDING) 1140 return(0); 1141 1142 senddonefunc(&sc->ndis_block, packet, status); 1143 1144 return(0); 1145} 1146 1147int 1148ndis_init_dma(arg) 1149 void *arg; 1150{ 1151 struct ndis_softc *sc; 1152 int i, error; 1153 1154 sc = arg; 1155 1156 sc->ndis_tmaps = malloc(sizeof(bus_dmamap_t) * sc->ndis_maxpkts, 1157 M_DEVBUF, M_NOWAIT|M_ZERO); 1158 1159 if (sc->ndis_tmaps == NULL) 1160 return(ENOMEM); 1161 1162 for (i = 0; i < sc->ndis_maxpkts; i++) { 1163 error = bus_dmamap_create(sc->ndis_ttag, 0, 1164 &sc->ndis_tmaps[i]); 1165 if (error) { 1166 free(sc->ndis_tmaps, M_DEVBUF); 1167 return(ENODEV); 1168 } 1169 } 1170 1171 return(0); 1172} 1173 1174int 1175ndis_destroy_dma(arg) 1176 void *arg; 1177{ 1178 struct ndis_softc *sc; 1179 struct mbuf *m; 1180 ndis_packet *p = NULL; 1181 int i; 1182 1183 sc = arg; 1184 1185 for (i = 0; i < sc->ndis_maxpkts; i++) { 1186 if (sc->ndis_txarray[i] != NULL) { 1187 p = sc->ndis_txarray[i]; 1188 m = (struct mbuf *)p->np_rsvd[1]; 1189 if (m != NULL) 1190 m_freem(m); 1191 ndis_free_packet(sc->ndis_txarray[i]); 1192 } 1193 bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]); 1194 } 1195 1196 free(sc->ndis_tmaps, M_DEVBUF); 1197 1198 bus_dma_tag_destroy(sc->ndis_ttag); 1199 1200 return(0); 1201} 1202 1203int 1204ndis_reset_nic(arg) 1205 void *arg; 1206{ 1207 struct ndis_softc *sc; 1208 ndis_handle adapter; 1209 __stdcall ndis_reset_handler resetfunc; 1210 uint8_t addressing_reset; 1211 struct ifnet *ifp; 1212 int rval; 1213 1214 sc = arg; 1215 ifp = &sc->arpcom.ac_if; 1216 NDIS_LOCK(sc); 1217 adapter = sc->ndis_block.nmb_miniportadapterctx; 1218 resetfunc = sc->ndis_chars.nmc_reset_func; 1219 NDIS_UNLOCK(sc); 1220 if (adapter == NULL || resetfunc == NULL) 1221 return(EIO); 1222 1223 rval = resetfunc(&addressing_reset, adapter); 1224 if (rval == NDIS_STATUS_PENDING) { 1225 PROC_LOCK(curthread->td_proc); 1226 msleep(sc, &curthread->td_proc->p_mtx, 1227 PPAUSE|PDROP, "ndisrst", 0); 1228 } 1229 1230 return(0); 1231} 1232 1233int 1234ndis_halt_nic(arg) 1235 void *arg; 1236{ 1237 struct ndis_softc *sc; 1238 ndis_handle adapter; 1239 __stdcall ndis_halt_handler haltfunc; 1240 struct ifnet *ifp; 1241 1242 sc = arg; 1243 ifp = &sc->arpcom.ac_if; 1244 1245 NDIS_LOCK(sc); 1246 adapter = sc->ndis_block.nmb_miniportadapterctx; 1247 if (adapter == NULL) { 1248 NDIS_UNLOCK(sc); 1249 return(EIO); 1250 } 1251 1252 /* 1253 * The adapter context is only valid after the init 1254 * handler has been called, and is invalid once the 1255 * halt handler has been called. 1256 */ 1257 1258 haltfunc = sc->ndis_chars.nmc_halt_func; 1259 NDIS_UNLOCK(sc); 1260 1261 haltfunc(adapter); 1262 1263 NDIS_LOCK(sc); 1264 sc->ndis_block.nmb_miniportadapterctx = NULL; 1265 NDIS_UNLOCK(sc); 1266 1267 return(0); 1268} 1269 1270int 1271ndis_shutdown_nic(arg) 1272 void *arg; 1273{ 1274 struct ndis_softc *sc; 1275 ndis_handle adapter; 1276 __stdcall ndis_shutdown_handler shutdownfunc; 1277 1278 sc = arg; 1279 NDIS_LOCK(sc); 1280 adapter = sc->ndis_block.nmb_miniportadapterctx; 1281 shutdownfunc = sc->ndis_chars.nmc_shutdown_handler; 1282 NDIS_UNLOCK(sc); 1283 if (adapter == NULL || shutdownfunc == NULL) 1284 return(EIO); 1285 1286 if (sc->ndis_chars.nmc_rsvd0 == NULL) 1287 shutdownfunc(adapter); 1288 else 1289 shutdownfunc(sc->ndis_chars.nmc_rsvd0); 1290 1291 ndis_shrink_thrqueue(8); 1292 TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link); 1293 1294 return(0); 1295} 1296 1297int 1298ndis_init_nic(arg) 1299 void *arg; 1300{ 1301 struct ndis_softc *sc; 1302 ndis_miniport_block *block; 1303 __stdcall ndis_init_handler initfunc; 1304 ndis_status status, openstatus = 0; 1305 ndis_medium mediumarray[NdisMediumMax]; 1306 uint32_t chosenmedium, i; 1307 1308 if (arg == NULL) 1309 return(EINVAL); 1310 1311 sc = arg; 1312 NDIS_LOCK(sc); 1313 block = &sc->ndis_block; 1314 initfunc = sc->ndis_chars.nmc_init_func; 1315 NDIS_UNLOCK(sc); 1316 1317 TAILQ_INIT(&block->nmb_timerlist); 1318 1319 for (i = 0; i < NdisMediumMax; i++) 1320 mediumarray[i] = i; 1321 1322 status = initfunc(&openstatus, &chosenmedium, 1323 mediumarray, NdisMediumMax, block, block); 1324 1325 /* 1326 * If the init fails, blow away the other exported routines 1327 * we obtained from the driver so we can't call them later. 1328 * If the init failed, none of these will work. 1329 */ 1330 if (status != NDIS_STATUS_SUCCESS) { 1331 NDIS_LOCK(sc); 1332 sc->ndis_block.nmb_miniportadapterctx = NULL; 1333 NDIS_UNLOCK(sc); 1334 return(ENXIO); 1335 } 1336 1337 return(0); 1338} 1339 1340void 1341ndis_enable_intr(arg) 1342 void *arg; 1343{ 1344 struct ndis_softc *sc; 1345 ndis_handle adapter; 1346 __stdcall ndis_enable_interrupts_handler intrenbfunc; 1347 1348 sc = arg; 1349 NDIS_LOCK(sc); 1350 adapter = sc->ndis_block.nmb_miniportadapterctx; 1351 intrenbfunc = sc->ndis_chars.nmc_enable_interrupts_func; 1352 NDIS_UNLOCK(sc); 1353 if (adapter == NULL || intrenbfunc == NULL) 1354 return; 1355 intrenbfunc(adapter); 1356 1357 return; 1358} 1359 1360void 1361ndis_disable_intr(arg) 1362 void *arg; 1363{ 1364 struct ndis_softc *sc; 1365 ndis_handle adapter; 1366 __stdcall ndis_disable_interrupts_handler intrdisfunc; 1367 1368 sc = arg; 1369 NDIS_LOCK(sc); 1370 adapter = sc->ndis_block.nmb_miniportadapterctx; 1371 intrdisfunc = sc->ndis_chars.nmc_disable_interrupts_func; 1372 NDIS_UNLOCK(sc); 1373 if (adapter == NULL || intrdisfunc == NULL) 1374 return; 1375 intrdisfunc(adapter); 1376 1377 return; 1378} 1379 1380int 1381ndis_isr(arg, ourintr, callhandler) 1382 void *arg; 1383 int *ourintr; 1384 int *callhandler; 1385{ 1386 struct ndis_softc *sc; 1387 ndis_handle adapter; 1388 __stdcall ndis_isr_handler isrfunc; 1389 uint8_t accepted, queue; 1390 1391 if (arg == NULL || ourintr == NULL || callhandler == NULL) 1392 return(EINVAL); 1393 1394 sc = arg; 1395 NDIS_LOCK(sc); 1396 adapter = sc->ndis_block.nmb_miniportadapterctx; 1397 isrfunc = sc->ndis_chars.nmc_isr_func; 1398 NDIS_UNLOCK(sc); 1399 if (adapter == NULL || isrfunc == NULL) 1400 return(ENXIO); 1401 1402 isrfunc(&accepted, &queue, adapter); 1403 *ourintr = accepted; 1404 *callhandler = queue; 1405 1406 return(0); 1407} 1408 1409int 1410ndis_intrhand(arg) 1411 void *arg; 1412{ 1413 struct ndis_softc *sc; 1414 ndis_handle adapter; 1415 __stdcall ndis_interrupt_handler intrfunc; 1416 1417 if (arg == NULL) 1418 return(EINVAL); 1419 1420 sc = arg; 1421 NDIS_LOCK(sc); 1422 adapter = sc->ndis_block.nmb_miniportadapterctx; 1423 intrfunc = sc->ndis_chars.nmc_interrupt_func; 1424 NDIS_UNLOCK(sc); 1425 if (adapter == NULL || intrfunc == NULL) 1426 return(EINVAL); 1427 1428 intrfunc(adapter); 1429 1430 return(0); 1431} 1432 1433int 1434ndis_get_info(arg, oid, buf, buflen) 1435 void *arg; 1436 ndis_oid oid; 1437 void *buf; 1438 int *buflen; 1439{ 1440 struct ndis_softc *sc; 1441 ndis_status rval; 1442 ndis_handle adapter; 1443 __stdcall ndis_queryinfo_handler queryfunc; 1444 uint32_t byteswritten = 0, bytesneeded = 0; 1445 int error; 1446 1447 sc = arg; 1448 NDIS_LOCK(sc); 1449 queryfunc = sc->ndis_chars.nmc_queryinfo_func; 1450 adapter = sc->ndis_block.nmb_miniportadapterctx; 1451 NDIS_UNLOCK(sc); 1452 1453 if (adapter == NULL || queryfunc == NULL) 1454 return(ENXIO); 1455 1456 rval = queryfunc(adapter, oid, buf, *buflen, 1457 &byteswritten, &bytesneeded); 1458 1459 /* Wait for requests that block. */ 1460 1461 if (rval == NDIS_STATUS_PENDING) { 1462 PROC_LOCK(curthread->td_proc); 1463 error = msleep(&sc->ndis_block.nmb_wkupdpctimer, 1464 &curthread->td_proc->p_mtx, PPAUSE|PDROP, 1465 "ndisget", 5 * hz); 1466 rval = sc->ndis_block.nmb_getstat; 1467 } 1468 1469 if (byteswritten) 1470 *buflen = byteswritten; 1471 if (bytesneeded) 1472 *buflen = bytesneeded; 1473 1474 if (rval == NDIS_STATUS_INVALID_LENGTH || 1475 rval == NDIS_STATUS_BUFFER_TOO_SHORT) 1476 return(ENOSPC); 1477 1478 if (rval == NDIS_STATUS_INVALID_OID) 1479 return(EINVAL); 1480 1481 if (rval == NDIS_STATUS_NOT_SUPPORTED || 1482 rval == NDIS_STATUS_NOT_ACCEPTED) 1483 return(ENOTSUP); 1484 1485 if (rval != NDIS_STATUS_SUCCESS) 1486 return(ENODEV); 1487 1488 return(0); 1489} 1490 1491int 1492ndis_unload_driver(arg) 1493 void *arg; 1494{ 1495 struct ndis_softc *sc; 1496 1497 sc = arg; 1498 1499 free(sc->ndis_block.nmb_rlist, M_DEVBUF); 1500 1501 ndis_flush_sysctls(sc); 1502 1503 ndis_shrink_thrqueue(8); 1504 TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link); 1505 1506 return(0); 1507} 1508 1509#define NDIS_LOADED htonl(0x42534F44) 1510 1511int 1512ndis_load_driver(img, arg) 1513 vm_offset_t img; 1514 void *arg; 1515{ 1516 __stdcall driver_entry entry; 1517 image_optional_header opt_hdr; 1518 image_import_descriptor imp_desc; 1519 ndis_unicode_string dummystr; 1520 ndis_miniport_block *block; 1521 ndis_status status; 1522 int idx; 1523 uint32_t *ptr; 1524 struct ndis_softc *sc; 1525 1526 sc = arg; 1527 1528 /* 1529 * Only perform the relocation/linking phase once 1530 * since the binary image may be shared among multiple 1531 * device instances. 1532 */ 1533 1534 ptr = (uint32_t *)(img + 8); 1535 if (*ptr != NDIS_LOADED) { 1536 /* Perform text relocation */ 1537 if (pe_relocate(img)) 1538 return(ENOEXEC); 1539 1540 /* Dynamically link the NDIS.SYS routines -- required. */ 1541 if (pe_patch_imports(img, "NDIS", ndis_functbl)) 1542 return(ENOEXEC); 1543 1544 /* Dynamically link the HAL.dll routines -- also required. */ 1545 if (pe_patch_imports(img, "HAL", hal_functbl)) 1546 return(ENOEXEC); 1547 1548 /* Dynamically link ntoskrnl.exe -- optional. */ 1549 if (pe_get_import_descriptor(img, 1550 &imp_desc, "ntoskrnl") == 0) { 1551 if (pe_patch_imports(img, 1552 "ntoskrnl", ntoskrnl_functbl)) 1553 return(ENOEXEC); 1554 } 1555 *ptr = NDIS_LOADED; 1556 } 1557 1558 /* Locate the driver entry point */ 1559 pe_get_optional_header(img, &opt_hdr); 1560 entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr); 1561 1562 dummystr.nus_len = strlen(NDIS_DUMMY_PATH) * 2; 1563 dummystr.nus_maxlen = strlen(NDIS_DUMMY_PATH) * 2; 1564 dummystr.nus_buf = NULL; 1565 ndis_ascii_to_unicode(NDIS_DUMMY_PATH, &dummystr.nus_buf); 1566 1567 /* 1568 * Now that we have the miniport driver characteristics, 1569 * create an NDIS block and call the init handler. 1570 * This will cause the driver to try to probe for 1571 * a device. 1572 */ 1573 1574 block = &sc->ndis_block; 1575 1576 ptr = (uint32_t *)block; 1577 for (idx = 0; idx < sizeof(ndis_miniport_block) / 4; idx++) { 1578 *ptr = idx | 0xdead0000; 1579 ptr++; 1580 } 1581 1582 block->nmb_signature = (void *)0xcafebabe; 1583 block->nmb_setdone_func = ndis_setdone_func; 1584 block->nmb_querydone_func = ndis_getdone_func; 1585 block->nmb_status_func = ndis_status_func; 1586 block->nmb_statusdone_func = ndis_statusdone_func; 1587 block->nmb_resetdone_func = ndis_resetdone_func; 1588 block->nmb_sendrsrc_func = ndis_sendrsrcavail_func; 1589 1590 block->nmb_ifp = &sc->arpcom.ac_if; 1591 block->nmb_dev = sc->ndis_dev; 1592 block->nmb_img = img; 1593 block->nmb_devobj.do_rsvd = block; 1594 1595 /* 1596 * Now call the DriverEntry() routine. This will cause 1597 * a callout to the NdisInitializeWrapper() and 1598 * NdisMRegisterMiniport() routines. 1599 */ 1600 status = entry(&block->nmb_devobj, &dummystr); 1601 1602 free (dummystr.nus_buf, M_DEVBUF); 1603 1604 if (status != NDIS_STATUS_SUCCESS) 1605 return(ENODEV); 1606 1607 ndis_enlarge_thrqueue(8); 1608 1609 TAILQ_INSERT_TAIL(&ndis_devhead, block, link); 1610 1611 return(0); 1612} 1613