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