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