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