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