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