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