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