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