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