3 * Bill Paul <wpaul@windriver.com>. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
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 $
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>
46 #include <sys/malloc.h>
50 #include <sys/kernel.h>
51 #include <sys/module.h>
52 #include <sys/kthread.h>
57 #include <net/if_arp.h>
58 #include <net/ethernet.h>
59 #include <net/if_dl.h>
60 #include <net/if_media.h>
62 #include <netproto/802_11/ieee80211_var.h>
63 #include <netproto/802_11/ieee80211_ioctl.h>
67 #include "resource_var.h"
68 #include "ntoskrnl_var.h"
72 #include <dev/netif/ndis/if_ndisvar.h>
74 #define NDIS_DUMMY_PATH "\\\\some\\bogus\\path"
76 __stdcall
static void ndis_status_func(ndis_handle
, ndis_status
,
78 __stdcall
static void ndis_statusdone_func(ndis_handle
);
79 __stdcall
static void ndis_setdone_func(ndis_handle
, ndis_status
);
80 __stdcall
static void ndis_getdone_func(ndis_handle
, ndis_status
);
81 __stdcall
static void ndis_resetdone_func(ndis_handle
, ndis_status
, uint8_t);
82 __stdcall
static void ndis_sendrsrcavail_func(ndis_handle
);
84 struct nd_head ndis_devhead
;
87 void (*nr_func
)(void *);
90 STAILQ_ENTRY(ndis_req
) link
;
94 struct ndisqhead
*np_q
;
99 static void ndis_return(void *);
100 static int ndis_create_kthreads(void);
101 static void ndis_destroy_kthreads(void);
102 static void ndis_stop_thread(int);
103 static int ndis_enlarge_thrqueue(int);
104 static int ndis_shrink_thrqueue(int);
105 static void ndis_runq(void *);
107 static MALLOC_DEFINE(M_NDIS_PACKET
, "ndis_packet", "ndis packet slosh");
108 static MALLOC_DEFINE(M_NDIS_BUFFER
, "ndis_buffer", "ndis buffer slosh");
109 struct lwkt_token ndis_thr_token
;
110 static STAILQ_HEAD(ndisqhead
, ndis_req
) ndis_ttodo
;
111 struct ndisqhead ndis_itodo
;
112 struct ndisqhead ndis_free
;
113 static int ndis_jobs
= 32;
115 static struct ndisproc ndis_tproc
;
116 static struct ndisproc ndis_iproc
;
119 * This allows us to export our symbols to other modules.
120 * Note that we call ourselves 'ndisapi' to avoid a namespace
121 * collision with if_ndis.ko, which internally calls itself
125 ndis_modevent(module_t mod
, int cmd
, void *arg
)
131 /* Initialize subsystems */
135 /* Initialize TX buffer UMA zone. */
136 ndis_create_kthreads();
138 TAILQ_INIT(&ndis_devhead
);
143 ndis_destroy_kthreads();
144 if (TAILQ_FIRST(&ndis_devhead
) == NULL
) {
145 /* Shut down subsystems */
151 malloc_uninit(M_NDIS_PACKET
);
152 malloc_uninit(M_NDIS_BUFFER
);
158 ndis_destroy_kthreads();
160 /* Shut down subsystems */
166 malloc_uninit(M_NDIS_PACKET
);
167 malloc_uninit(M_NDIS_BUFFER
);
177 DEV_MODULE(ndisapi
, ndis_modevent
, NULL
);
178 MODULE_VERSION(ndisapi
, 1);
181 * We create two kthreads for the NDIS subsystem. One of them is a task
182 * queue for performing various odd jobs. The other is an swi thread
183 * reserved exclusively for running interrupt handlers. The reason we
184 * have our own task queue is that there are some cases where we may
185 * need to sleep for a significant amount of time, and if we were to
186 * use one of the taskqueue threads, we might delay the processing
187 * of other pending tasks which might need to run right away. We have
188 * a separate swi thread because we don't want our interrupt handling
189 * to be delayed either.
191 * By default there are 32 jobs available to start, and another 8
192 * are added to the free list each time a new device is created.
198 struct ndis_req
*r
= NULL
, *die
= NULL
;
200 struct lwkt_tokref tokref
;
206 /* Sleep, but preserve our original priority. */
207 ndis_thsuspend(p
->np_td
, 0);
209 /* Look for any jobs on the work queue. */
211 lwkt_gettoken(&tokref
, &ndis_thr_token
);
212 p
->np_state
= NDIS_PSTATE_RUNNING
;
213 while(STAILQ_FIRST(p
->np_q
) != NULL
) {
214 r
= STAILQ_FIRST(p
->np_q
);
215 STAILQ_REMOVE_HEAD(p
->np_q
, link
);
216 lwkt_reltoken(&tokref
);
220 if (r
->nr_func
!= NULL
)
221 (*r
->nr_func
)(r
->nr_arg
);
223 lwkt_gettoken(&tokref
, &ndis_thr_token
);
224 STAILQ_INSERT_HEAD(&ndis_free
, r
, link
);
226 /* Check for a shutdown request */
228 if (r
->nr_exit
== TRUE
)
231 p
->np_state
= NDIS_PSTATE_SLEEPING
;
232 lwkt_reltoken(&tokref
);
234 /* Bail if we were told to shut down. */
245 ndis_create_kthreads(void)
250 lwkt_token_init(&ndis_thr_token
);
252 STAILQ_INIT(&ndis_ttodo
);
253 STAILQ_INIT(&ndis_itodo
);
254 STAILQ_INIT(&ndis_free
);
256 for (i
= 0; i
< ndis_jobs
; i
++) {
257 r
= kmalloc(sizeof(struct ndis_req
), M_DEVBUF
, M_WAITOK
);
258 STAILQ_INSERT_HEAD(&ndis_free
, r
, link
);
262 ndis_tproc
.np_q
= &ndis_ttodo
;
263 ndis_tproc
.np_state
= NDIS_PSTATE_SLEEPING
;
264 error
= kthread_create_stk(ndis_runq
, &ndis_tproc
,
265 &ndis_tproc
.np_td
, NDIS_KSTACK_PAGES
* PAGE_SIZE
,
270 ndis_iproc
.np_q
= &ndis_itodo
;
271 ndis_iproc
.np_state
= NDIS_PSTATE_SLEEPING
;
272 error
= kthread_create_stk(ndis_runq
, &ndis_iproc
,
273 &ndis_iproc
.np_td
, NDIS_KSTACK_PAGES
* PAGE_SIZE
,
278 while ((r
= STAILQ_FIRST(&ndis_free
)) != NULL
) {
279 STAILQ_REMOVE_HEAD(&ndis_free
, link
);
289 ndis_destroy_kthreads(void)
293 /* Stop the threads. */
295 ndis_stop_thread(NDIS_TASKQUEUE
);
296 ndis_stop_thread(NDIS_SWI
);
298 /* Destroy request structures. */
300 while ((r
= STAILQ_FIRST(&ndis_free
)) != NULL
) {
301 STAILQ_REMOVE_HEAD(&ndis_free
, link
);
305 lwkt_token_uninit(&ndis_thr_token
);
311 ndis_stop_thread(int t
)
316 struct lwkt_tokref tokref
;
318 if (t
== NDIS_TASKQUEUE
) {
320 td
= ndis_tproc
.np_td
;
323 td
= ndis_iproc
.np_td
;
326 /* Create and post a special 'exit' job. */
328 lwkt_gettoken(&tokref
, &ndis_thr_token
);
329 r
= STAILQ_FIRST(&ndis_free
);
330 STAILQ_REMOVE_HEAD(&ndis_free
, link
);
334 STAILQ_INSERT_TAIL(q
, r
, link
);
335 lwkt_reltoken(&tokref
);
339 /* wait for thread exit */
341 tsleep(r
, PCATCH
, "ndisthexit", hz
* 60);
343 /* Now empty the job list. */
345 lwkt_gettoken(&tokref
, &ndis_thr_token
);
346 while ((r
= STAILQ_FIRST(q
)) != NULL
) {
347 STAILQ_REMOVE_HEAD(q
, link
);
348 STAILQ_INSERT_HEAD(&ndis_free
, r
, link
);
350 lwkt_reltoken(&tokref
);
356 ndis_enlarge_thrqueue(int cnt
)
360 struct lwkt_tokref tokref
;
362 for (i
= 0; i
< cnt
; i
++) {
363 r
= kmalloc(sizeof(struct ndis_req
), M_DEVBUF
, M_WAITOK
);
364 lwkt_gettoken(&tokref
, &ndis_thr_token
);
365 STAILQ_INSERT_HEAD(&ndis_free
, r
, link
);
367 lwkt_reltoken(&tokref
);
374 ndis_shrink_thrqueue(int cnt
)
378 struct lwkt_tokref tokref
;
380 for (i
= 0; i
< cnt
; i
++) {
381 lwkt_gettoken(&tokref
, &ndis_thr_token
);
382 r
= STAILQ_FIRST(&ndis_free
);
384 lwkt_reltoken(&tokref
);
387 STAILQ_REMOVE_HEAD(&ndis_free
, link
);
389 lwkt_reltoken(&tokref
);
397 ndis_unsched(void (*func
)(void *), void *arg
, int t
)
402 struct lwkt_tokref tokref
;
404 if (t
== NDIS_TASKQUEUE
) {
406 td
= ndis_tproc
.np_td
;
409 td
= ndis_iproc
.np_td
;
412 lwkt_gettoken(&tokref
, &ndis_thr_token
);
413 STAILQ_FOREACH(r
, q
, link
) {
414 if (r
->nr_func
== func
&& r
->nr_arg
== arg
) {
415 STAILQ_REMOVE(q
, r
, ndis_req
, link
);
416 STAILQ_INSERT_HEAD(&ndis_free
, r
, link
);
417 lwkt_reltoken(&tokref
);
422 lwkt_reltoken(&tokref
);
428 ndis_sched(void (*func
)(void *), void *arg
, int t
)
434 struct lwkt_tokref tokref
;
436 if (t
== NDIS_TASKQUEUE
) {
438 td
= ndis_tproc
.np_td
;
441 td
= ndis_iproc
.np_td
;
444 lwkt_gettoken(&tokref
, &ndis_thr_token
);
446 * Check to see if an instance of this job is already
447 * pending. If so, don't bother queuing it again.
449 STAILQ_FOREACH(r
, q
, link
) {
450 if (r
->nr_func
== func
&& r
->nr_arg
== arg
) {
451 lwkt_reltoken(&tokref
);
455 r
= STAILQ_FIRST(&ndis_free
);
457 lwkt_reltoken(&tokref
);
460 STAILQ_REMOVE_HEAD(&ndis_free
, link
);
464 STAILQ_INSERT_TAIL(q
, r
, link
);
465 if (t
== NDIS_TASKQUEUE
)
466 s
= ndis_tproc
.np_state
;
468 s
= ndis_iproc
.np_state
;
469 lwkt_reltoken(&tokref
);
472 * Post the job, but only if the thread is actually blocked
473 * on its own suspend call. If a driver queues up a job with
474 * NdisScheduleWorkItem() which happens to do a KeWaitForObject(),
475 * it may suspend there, and in that case we don't want to wake
476 * it up until KeWaitForObject() gets woken up on its own.
478 if (s
== NDIS_PSTATE_SLEEPING
)
485 ndis_thsuspend(thread_t td
, int timo
)
489 error
= tsleep(td
, 0, "ndissp", timo
);
494 ndis_thresume(struct thread
*td
)
499 __stdcall
static void
500 ndis_sendrsrcavail_func(ndis_handle adapter
)
505 __stdcall
static void
506 ndis_status_func(ndis_handle adapter
, ndis_status status
, void *sbuf
,
509 ndis_miniport_block
*block
;
512 if (block
->nmb_ifp
->if_flags
& IFF_DEBUG
)
513 device_printf (block
->nmb_dev
, "status: %x\n", status
);
517 __stdcall
static void
518 ndis_statusdone_func(ndis_handle adapter
)
520 ndis_miniport_block
*block
;
523 if (block
->nmb_ifp
->if_flags
& IFF_DEBUG
)
524 device_printf (block
->nmb_dev
, "status complete\n");
528 __stdcall
static void
529 ndis_setdone_func(ndis_handle adapter
, ndis_status status
)
531 ndis_miniport_block
*block
;
534 block
->nmb_setstat
= status
;
535 wakeup(&block
->nmb_wkupdpctimer
);
539 __stdcall
static void
540 ndis_getdone_func(ndis_handle adapter
, ndis_status status
)
542 ndis_miniport_block
*block
;
545 block
->nmb_getstat
= status
;
546 wakeup(&block
->nmb_wkupdpctimer
);
550 __stdcall
static void
551 ndis_resetdone_func(ndis_handle adapter
, ndis_status status
,
552 uint8_t addressingreset
)
554 ndis_miniport_block
*block
;
557 if (block
->nmb_ifp
->if_flags
& IFF_DEBUG
)
558 device_printf (block
->nmb_dev
, "reset done...\n");
559 wakeup(block
->nmb_ifp
);
564 ndis_create_sysctls(void *arg
)
566 struct ndis_softc
*sc
;
574 vals
= sc
->ndis_regvals
;
576 TAILQ_INIT(&sc
->ndis_cfglist_head
);
578 #if __FreeBSD_version < 502113
579 /* Create the sysctl tree. */
581 sc
->ndis_tree
= SYSCTL_ADD_NODE(&sc
->ndis_ctx
,
582 SYSCTL_STATIC_CHILDREN(_hw
), OID_AUTO
,
583 device_get_nameunit(sc
->ndis_dev
), CTLFLAG_RD
, 0,
584 device_get_desc(sc
->ndis_dev
));
587 /* Add the driver-specific registry keys. */
589 vals
= sc
->ndis_regvals
;
591 if (vals
->nc_cfgkey
== NULL
)
593 if (vals
->nc_idx
!= sc
->ndis_devidx
) {
598 SYSCTL_ADD_STRING(&sc
->ndis_ctx
,
599 SYSCTL_CHILDREN(sc
->ndis_tree
),
600 OID_AUTO
, vals
->nc_cfgkey
,
601 CTLFLAG_RW
, vals
->nc_val
,
602 sizeof(vals
->nc_val
),
605 SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc
->ndis_dev
),
606 SYSCTL_CHILDREN(device_get_sysctl_tree(sc
->ndis_dev
)),
607 OID_AUTO
, vals
->nc_cfgkey
,
608 CTLFLAG_RW
, vals
->nc_val
,
609 sizeof(vals
->nc_val
),
615 /* Now add a couple of builtin keys. */
618 * Environment can be either Windows (0) or WindowsNT (1).
619 * We qualify as the latter.
621 ndis_add_sysctl(sc
, "Environment",
622 "Windows environment", "1", CTLFLAG_RD
);
624 /* NDIS version should be 5.1. */
625 ndis_add_sysctl(sc
, "NdisVersion",
626 "NDIS API Version", "0x00050001", CTLFLAG_RD
);
628 /* Bus type (PCI, PCMCIA, etc...) */
629 ksprintf(buf
, "%d", (int)sc
->ndis_iftype
);
630 ndis_add_sysctl(sc
, "BusType", "Bus Type", buf
, CTLFLAG_RD
);
632 if (sc
->ndis_res_io
!= NULL
) {
633 ksprintf(buf
, "0x%lx", rman_get_start(sc
->ndis_res_io
));
634 ndis_add_sysctl(sc
, "IOBaseAddress",
635 "Base I/O Address", buf
, CTLFLAG_RD
);
638 if (sc
->ndis_irq
!= NULL
) {
639 ksprintf(buf
, "%lu", rman_get_start(sc
->ndis_irq
));
640 ndis_add_sysctl(sc
, "InterruptNumber",
641 "Interrupt Number", buf
, CTLFLAG_RD
);
648 ndis_add_sysctl(void *arg
, char *key
, char *desc
, char *val
, int flag
)
650 struct ndis_softc
*sc
;
651 struct ndis_cfglist
*cfg
;
656 cfg
= kmalloc(sizeof(struct ndis_cfglist
), M_DEVBUF
, M_WAITOK
|M_ZERO
);
657 cfg
->ndis_cfg
.nc_cfgkey
= kstrdup(key
, M_DEVBUF
);
659 ksnprintf(descstr
, sizeof(descstr
), "%s (dynamic)", key
);
660 cfg
->ndis_cfg
.nc_cfgdesc
= kstrdup(descstr
, M_DEVBUF
);
662 cfg
->ndis_cfg
.nc_cfgdesc
= kstrdup(desc
, M_DEVBUF
);
663 strcpy(cfg
->ndis_cfg
.nc_val
, val
);
665 TAILQ_INSERT_TAIL(&sc
->ndis_cfglist_head
, cfg
, link
);
668 SYSCTL_ADD_STRING(&sc
->ndis_ctx
, SYSCTL_CHILDREN(sc
->ndis_tree
),
669 OID_AUTO
, cfg
->ndis_cfg
.nc_cfgkey
, flag
,
670 cfg
->ndis_cfg
.nc_val
, sizeof(cfg
->ndis_cfg
.nc_val
),
671 cfg
->ndis_cfg
.nc_cfgdesc
);
673 SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc
->ndis_dev
),
674 SYSCTL_CHILDREN(device_get_sysctl_tree(sc
->ndis_dev
)),
675 OID_AUTO
, cfg
->ndis_cfg
.nc_cfgkey
, flag
,
676 cfg
->ndis_cfg
.nc_val
, sizeof(cfg
->ndis_cfg
.nc_val
),
677 cfg
->ndis_cfg
.nc_cfgdesc
);
684 ndis_flush_sysctls(void *arg
)
686 struct ndis_softc
*sc
;
687 struct ndis_cfglist
*cfg
;
691 while (!TAILQ_EMPTY(&sc
->ndis_cfglist_head
)) {
692 cfg
= TAILQ_FIRST(&sc
->ndis_cfglist_head
);
693 TAILQ_REMOVE(&sc
->ndis_cfglist_head
, cfg
, link
);
694 kfree(cfg
->ndis_cfg
.nc_cfgkey
, M_DEVBUF
);
695 kfree(cfg
->ndis_cfg
.nc_cfgdesc
, M_DEVBUF
);
696 kfree(cfg
, M_DEVBUF
);
703 ndis_return(void *arg
)
705 struct ndis_softc
*sc
;
706 ndis_return_handler returnfunc
;
713 adapter
= sc
->ndis_block
.nmb_miniportadapterctx
;
718 returnfunc
= sc
->ndis_chars
.nmc_return_packet_func
;
719 irql
= FASTCALL1(hal_raise_irql
, DISPATCH_LEVEL
);
720 returnfunc(adapter
, p
);
721 FASTCALL1(hal_lower_irql
, irql
);
727 ndis_extref_packet(void *arg
)
729 ndis_packet
*p
= arg
;
735 ndis_extfree_packet(void *arg
)
737 ndis_packet
*p
= arg
;
742 /* Decrement refcount. */
745 /* Release packet when refcount hits zero, otherwise return. */
749 ndis_sched(ndis_return
, p
, NDIS_SWI
);
755 ndis_return_packet(struct ndis_softc
*sc
, ndis_packet
*p
)
757 ndis_extfree_packet(p
);
761 ndis_free_bufs(ndis_buffer
*b0
)
770 kfree(b0
, M_NDIS_BUFFER
);
778 ndis_free_packet(ndis_packet
*p
)
783 ndis_free_bufs(p
->np_private
.npp_head
);
784 kfree(p
, M_NDIS_PACKET
);
790 ndis_convert_res(void *arg
)
792 struct ndis_softc
*sc
;
793 ndis_resource_list
*rl
= NULL
;
794 cm_partial_resource_desc
*prd
= NULL
;
795 ndis_miniport_block
*block
;
797 struct resource_list
*brl
;
798 struct resource_list brl_rev
;
799 struct resource_list_entry
*brle
, *n
;
803 block
= &sc
->ndis_block
;
806 SLIST_INIT(&brl_rev
);
808 rl
= kmalloc(sizeof(ndis_resource_list
) +
809 (sizeof(cm_partial_resource_desc
) * (sc
->ndis_rescnt
- 1)),
810 M_DEVBUF
, M_WAITOK
|M_NULLOK
|M_ZERO
);
815 rl
->cprl_version
= 5;
816 rl
->cprl_version
= 1;
817 rl
->cprl_count
= sc
->ndis_rescnt
;
818 prd
= rl
->cprl_partial_descs
;
820 brl
= BUS_GET_RESOURCE_LIST(dev
, dev
);
825 * We have a small problem. Some PCI devices have
826 * multiple I/O ranges. Windows orders them starting
827 * from lowest numbered BAR to highest. We discover
828 * them in that order too, but insert them into a singly
829 * linked list head first, which means when time comes
830 * to traverse the list, we enumerate them in reverse
831 * order. This screws up some drivers which expect the
832 * BARs to be in ascending order so that they can choose
833 * the "first" one as their register space. Unfortunately,
834 * in order to fix this, we have to create our own
835 * temporary list with the entries in reverse order.
837 SLIST_FOREACH(brle
, brl
, link
) {
838 n
= kmalloc(sizeof(struct resource_list_entry
),
839 M_TEMP
, M_WAITOK
|M_NULLOK
);
844 bcopy((char *)brle
, (char *)n
,
845 sizeof(struct resource_list_entry
));
846 SLIST_INSERT_HEAD(&brl_rev
, n
, link
);
849 SLIST_FOREACH(brle
, &brl_rev
, link
) {
850 switch (brle
->type
) {
852 prd
->cprd_type
= CmResourceTypePort
;
853 prd
->cprd_flags
= CM_RESOURCE_PORT_IO
;
854 prd
->cprd_sharedisp
=
855 CmResourceShareDeviceExclusive
;
856 prd
->u
.cprd_port
.cprd_start
.np_quad
=
858 prd
->u
.cprd_port
.cprd_len
= brle
->count
;
861 prd
->cprd_type
= CmResourceTypeMemory
;
863 CM_RESOURCE_MEMORY_READ_WRITE
;
864 prd
->cprd_sharedisp
=
865 CmResourceShareDeviceExclusive
;
866 prd
->u
.cprd_port
.cprd_start
.np_quad
=
868 prd
->u
.cprd_port
.cprd_len
= brle
->count
;
871 prd
->cprd_type
= CmResourceTypeInterrupt
;
873 prd
->cprd_sharedisp
=
874 CmResourceShareDeviceExclusive
;
875 prd
->u
.cprd_intr
.cprd_level
= brle
->start
;
876 prd
->u
.cprd_intr
.cprd_vector
= brle
->start
;
877 prd
->u
.cprd_intr
.cprd_affinity
= 0;
886 block
->nmb_rlist
= rl
;
890 while (!SLIST_EMPTY(&brl_rev
)) {
891 n
= SLIST_FIRST(&brl_rev
);
892 SLIST_REMOVE_HEAD(&brl_rev
, link
);
900 * Map an NDIS packet to an mbuf list. When an NDIS driver receives a
901 * packet, it will hand it to us in the form of an ndis_packet,
902 * which we need to convert to an mbuf that is then handed off
903 * to the stack. Note: we configure the mbuf list so that it uses
904 * the memory regions specified by the ndis_buffer structures in
905 * the ndis_packet as external storage. In most cases, this will
906 * point to a memory region allocated by the driver (either by
907 * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect
908 * the driver to handle free()ing this region for is, so we set up
909 * a dummy no-op free handler for it.
913 ndis_ptom(struct mbuf
**m0
, ndis_packet
*p
)
915 struct mbuf
*m
, *prev
= NULL
;
917 ndis_packet_private
*priv
;
920 if (p
== NULL
|| m0
== NULL
)
923 priv
= &p
->np_private
;
924 buf
= priv
->npp_head
;
927 for (buf
= priv
->npp_head
; buf
!= NULL
; buf
= buf
->nb_next
) {
928 if (buf
== priv
->npp_head
)
929 MGETHDR(m
, MB_DONTWAIT
, MT_HEADER
);
931 MGET(m
, MB_DONTWAIT
, MT_DATA
);
937 m
->m_len
= buf
->nb_bytecount
;
938 m
->m_data
= MDL_VA(buf
);
939 m
->m_ext
.ext_free
= ndis_extfree_packet
;
940 m
->m_ext
.ext_ref
= ndis_extref_packet
;
941 m
->m_ext
.ext_arg
= p
;
942 m
->m_ext
.ext_buf
= m
->m_data
;
943 m
->m_ext
.ext_size
= m
->m_len
;
946 MEXTADD(m
, m
->m_data
, m
->m_len
, ndis_free_packet
,
951 if (m
->m_flags
& M_PKTHDR
)
958 (*m0
)->m_pkthdr
.len
= totlen
;
964 * Create an mbuf chain from an NDIS packet chain.
965 * This is used mainly when transmitting packets, where we need
966 * to turn an mbuf off an interface's send queue and transform it
967 * into an NDIS packet which will be fed into the NDIS driver's
970 * NDIS packets consist of two parts: an ndis_packet structure,
971 * which is vaguely analagous to the pkthdr portion of an mbuf,
972 * and one or more ndis_buffer structures, which define the
973 * actual memory segments in which the packet data resides.
974 * We need to allocate one ndis_buffer for each mbuf in a chain,
975 * plus one ndis_packet as the header.
979 ndis_mtop(struct mbuf
*m0
, ndis_packet
**p
)
982 ndis_buffer
*buf
= NULL
, *prev
= NULL
;
983 ndis_packet_private
*priv
;
985 if (p
== NULL
|| m0
== NULL
)
988 /* If caller didn't supply a packet, make one. */
990 *p
= kmalloc(sizeof(ndis_packet
), M_NDIS_PACKET
, M_NOWAIT
|M_ZERO
);
995 priv
= &(*p
)->np_private
;
996 priv
->npp_totlen
= m0
->m_pkthdr
.len
;
997 priv
->npp_packetooboffset
= offsetof(ndis_packet
, np_oob
);
998 priv
->npp_ndispktflags
= NDIS_PACKET_ALLOCATED_BY_NDIS
;
1000 for (m
= m0
; m
!= NULL
; m
= m
->m_next
) {
1003 buf
= kmalloc(sizeof(ndis_buffer
), M_NDIS_BUFFER
, M_NOWAIT
|M_ZERO
);
1005 ndis_free_packet(*p
);
1010 MDL_INIT(buf
, m
->m_data
, m
->m_len
);
1011 if (priv
->npp_head
== NULL
)
1012 priv
->npp_head
= buf
;
1014 prev
->nb_next
= buf
;
1018 priv
->npp_tail
= buf
;
1019 priv
->npp_totlen
= m0
->m_pkthdr
.len
;
1025 ndis_get_supported_oids(void *arg
, ndis_oid
**oids
, int *oidcnt
)
1030 if (arg
== NULL
|| oids
== NULL
|| oidcnt
== NULL
)
1033 ndis_get_info(arg
, OID_GEN_SUPPORTED_LIST
, NULL
, &len
);
1035 o
= kmalloc(len
, M_DEVBUF
, M_WAITOK
);
1037 rval
= ndis_get_info(arg
, OID_GEN_SUPPORTED_LIST
, o
, &len
);
1051 ndis_set_info(void *arg
, ndis_oid oid
, void *buf
, int *buflen
)
1053 struct ndis_softc
*sc
;
1055 ndis_handle adapter
;
1056 ndis_setinfo_handler setfunc
;
1057 uint32_t byteswritten
= 0, bytesneeded
= 0;
1062 setfunc
= sc
->ndis_chars
.nmc_setinfo_func
;
1063 adapter
= sc
->ndis_block
.nmb_miniportadapterctx
;
1065 if (adapter
== NULL
|| setfunc
== NULL
)
1068 irql
= FASTCALL1(hal_raise_irql
, DISPATCH_LEVEL
);
1069 rval
= setfunc(adapter
, oid
, buf
, *buflen
,
1070 &byteswritten
, &bytesneeded
);
1071 FASTCALL1(hal_lower_irql
, irql
);
1073 if (rval
== NDIS_STATUS_PENDING
) {
1074 error
= tsleep(&sc
->ndis_block
.nmb_wkupdpctimer
,
1075 0, "ndisset", 5 * hz
);
1076 rval
= sc
->ndis_block
.nmb_setstat
;
1080 *buflen
= byteswritten
;
1082 *buflen
= bytesneeded
;
1084 if (rval
== NDIS_STATUS_INVALID_LENGTH
)
1087 if (rval
== NDIS_STATUS_INVALID_OID
)
1090 if (rval
== NDIS_STATUS_NOT_SUPPORTED
||
1091 rval
== NDIS_STATUS_NOT_ACCEPTED
)
1094 if (rval
!= NDIS_STATUS_SUCCESS
)
1100 typedef __stdcall
void (*ndis_senddone_func
)(ndis_handle
, ndis_packet
*, ndis_status
);
1103 ndis_send_packets(void *arg
, ndis_packet
**packets
, int cnt
)
1105 struct ndis_softc
*sc
;
1106 ndis_handle adapter
;
1107 ndis_sendmulti_handler sendfunc
;
1108 ndis_senddone_func senddonefunc
;
1114 adapter
= sc
->ndis_block
.nmb_miniportadapterctx
;
1115 if (adapter
== NULL
)
1117 sendfunc
= sc
->ndis_chars
.nmc_sendmulti_func
;
1118 senddonefunc
= sc
->ndis_block
.nmb_senddone_func
;
1119 irql
= FASTCALL1(hal_raise_irql
, DISPATCH_LEVEL
);
1120 sendfunc(adapter
, packets
, cnt
);
1121 FASTCALL1(hal_lower_irql
, irql
);
1123 for (i
= 0; i
< cnt
; i
++) {
1126 * Either the driver already handed the packet to
1127 * ndis_txeof() due to a failure, or it wants to keep
1128 * it and release it asynchronously later. Skip to the
1131 if (p
== NULL
|| p
->np_oob
.npo_status
== NDIS_STATUS_PENDING
)
1133 senddonefunc(&sc
->ndis_block
, p
, p
->np_oob
.npo_status
);
1140 ndis_send_packet(void *arg
, ndis_packet
*packet
)
1142 struct ndis_softc
*sc
;
1143 ndis_handle adapter
;
1145 ndis_sendsingle_handler sendfunc
;
1146 ndis_senddone_func senddonefunc
;
1150 adapter
= sc
->ndis_block
.nmb_miniportadapterctx
;
1151 if (adapter
== NULL
)
1153 sendfunc
= sc
->ndis_chars
.nmc_sendsingle_func
;
1154 senddonefunc
= sc
->ndis_block
.nmb_senddone_func
;
1156 irql
= FASTCALL1(hal_raise_irql
, DISPATCH_LEVEL
);
1157 status
= sendfunc(adapter
, packet
, packet
->np_private
.npp_flags
);
1158 FASTCALL1(hal_lower_irql
, irql
);
1160 if (status
== NDIS_STATUS_PENDING
)
1163 senddonefunc(&sc
->ndis_block
, packet
, status
);
1169 ndis_init_dma(void *arg
)
1171 struct ndis_softc
*sc
;
1176 sc
->ndis_tmaps
= kmalloc(sizeof(bus_dmamap_t
) * sc
->ndis_maxpkts
,
1177 M_DEVBUF
, M_WAITOK
|M_ZERO
);
1179 for (i
= 0; i
< sc
->ndis_maxpkts
; i
++) {
1180 error
= bus_dmamap_create(sc
->ndis_ttag
, 0,
1181 &sc
->ndis_tmaps
[i
]);
1183 kfree(sc
->ndis_tmaps
, M_DEVBUF
);
1192 ndis_destroy_dma(void *arg
)
1194 struct ndis_softc
*sc
;
1196 ndis_packet
*p
= NULL
;
1201 for (i
= 0; i
< sc
->ndis_maxpkts
; i
++) {
1202 if (sc
->ndis_txarray
[i
] != NULL
) {
1203 p
= sc
->ndis_txarray
[i
];
1204 m
= (struct mbuf
*)p
->np_rsvd
[1];
1207 ndis_free_packet(sc
->ndis_txarray
[i
]);
1209 bus_dmamap_destroy(sc
->ndis_ttag
, sc
->ndis_tmaps
[i
]);
1212 kfree(sc
->ndis_tmaps
, M_DEVBUF
);
1213 bus_dma_tag_destroy(sc
->ndis_ttag
);
1219 ndis_reset_nic(void *arg
)
1221 struct ndis_softc
*sc
;
1222 ndis_handle adapter
;
1223 ndis_reset_handler resetfunc
;
1224 uint8_t addressing_reset
;
1229 adapter
= sc
->ndis_block
.nmb_miniportadapterctx
;
1230 resetfunc
= sc
->ndis_chars
.nmc_reset_func
;
1231 if (adapter
== NULL
|| resetfunc
== NULL
)
1234 irql
= FASTCALL1(hal_raise_irql
, DISPATCH_LEVEL
);
1235 rval
= resetfunc(&addressing_reset
, adapter
);
1236 FASTCALL1(hal_lower_irql
, irql
);
1238 if (rval
== NDIS_STATUS_PENDING
) {
1239 tsleep(sc
, 0, "ndisrst", 0);
1246 ndis_halt_nic(void *arg
)
1248 struct ndis_softc
*sc
;
1249 ndis_handle adapter
;
1250 ndis_halt_handler haltfunc
;
1254 adapter
= sc
->ndis_block
.nmb_miniportadapterctx
;
1255 if (adapter
== NULL
) {
1260 * The adapter context is only valid after the init
1261 * handler has been called, and is invalid once the
1262 * halt handler has been called.
1265 haltfunc
= sc
->ndis_chars
.nmc_halt_func
;
1269 sc
->ndis_block
.nmb_miniportadapterctx
= NULL
;
1275 ndis_shutdown_nic(void *arg
)
1277 struct ndis_softc
*sc
;
1278 ndis_handle adapter
;
1279 ndis_shutdown_handler shutdownfunc
;
1282 adapter
= sc
->ndis_block
.nmb_miniportadapterctx
;
1283 shutdownfunc
= sc
->ndis_chars
.nmc_shutdown_handler
;
1284 if (adapter
== NULL
|| shutdownfunc
== NULL
)
1287 if (sc
->ndis_chars
.nmc_rsvd0
== NULL
)
1288 shutdownfunc(adapter
);
1290 shutdownfunc(sc
->ndis_chars
.nmc_rsvd0
);
1292 ndis_shrink_thrqueue(8);
1293 TAILQ_REMOVE(&ndis_devhead
, &sc
->ndis_block
, link
);
1299 ndis_init_nic(void *arg
)
1301 struct ndis_softc
*sc
;
1302 ndis_miniport_block
*block
;
1303 ndis_init_handler initfunc
;
1304 ndis_status status
, openstatus
= 0;
1305 ndis_medium mediumarray
[NdisMediumMax
];
1306 uint32_t chosenmedium
, i
;
1312 block
= &sc
->ndis_block
;
1313 initfunc
= sc
->ndis_chars
.nmc_init_func
;
1315 TAILQ_INIT(&block
->nmb_timerlist
);
1317 for (i
= 0; i
< NdisMediumMax
; i
++)
1320 status
= initfunc(&openstatus
, &chosenmedium
,
1321 mediumarray
, NdisMediumMax
, block
, block
);
1324 * If the init fails, blow away the other exported routines
1325 * we obtained from the driver so we can't call them later.
1326 * If the init failed, none of these will work.
1328 if (status
!= NDIS_STATUS_SUCCESS
) {
1329 sc
->ndis_block
.nmb_miniportadapterctx
= NULL
;
1337 ndis_enable_intr(void *arg
)
1339 struct ndis_softc
*sc
;
1340 ndis_handle adapter
;
1341 ndis_enable_interrupts_handler intrenbfunc
;
1344 adapter
= sc
->ndis_block
.nmb_miniportadapterctx
;
1345 intrenbfunc
= sc
->ndis_chars
.nmc_enable_interrupts_func
;
1346 if (adapter
== NULL
|| intrenbfunc
== NULL
)
1348 intrenbfunc(adapter
);
1354 ndis_disable_intr(void *arg
)
1356 struct ndis_softc
*sc
;
1357 ndis_handle adapter
;
1358 ndis_disable_interrupts_handler intrdisfunc
;
1361 adapter
= sc
->ndis_block
.nmb_miniportadapterctx
;
1362 intrdisfunc
= sc
->ndis_chars
.nmc_disable_interrupts_func
;
1363 if (adapter
== NULL
|| intrdisfunc
== NULL
)
1365 intrdisfunc(adapter
);
1371 ndis_isr(void *arg
, int *ourintr
, int *callhandler
)
1373 struct ndis_softc
*sc
;
1374 ndis_handle adapter
;
1375 ndis_isr_handler isrfunc
;
1376 uint8_t accepted
, queue
;
1378 if (arg
== NULL
|| ourintr
== NULL
|| callhandler
== NULL
)
1382 adapter
= sc
->ndis_block
.nmb_miniportadapterctx
;
1383 isrfunc
= sc
->ndis_chars
.nmc_isr_func
;
1384 if (adapter
== NULL
|| isrfunc
== NULL
)
1387 isrfunc(&accepted
, &queue
, adapter
);
1388 *ourintr
= accepted
;
1389 *callhandler
= queue
;
1395 ndis_intrhand(void *arg
)
1397 struct ndis_softc
*sc
;
1398 ndis_handle adapter
;
1399 ndis_interrupt_handler intrfunc
;
1405 adapter
= sc
->ndis_block
.nmb_miniportadapterctx
;
1406 intrfunc
= sc
->ndis_chars
.nmc_interrupt_func
;
1407 if (adapter
== NULL
|| intrfunc
== NULL
)
1416 ndis_get_info(void *arg
, ndis_oid oid
, void *buf
, int *buflen
)
1418 struct ndis_softc
*sc
;
1420 ndis_handle adapter
;
1421 ndis_queryinfo_handler queryfunc
;
1422 uint32_t byteswritten
= 0, bytesneeded
= 0;
1427 queryfunc
= sc
->ndis_chars
.nmc_queryinfo_func
;
1428 adapter
= sc
->ndis_block
.nmb_miniportadapterctx
;
1430 if (adapter
== NULL
|| queryfunc
== NULL
)
1433 irql
= FASTCALL1(hal_raise_irql
, DISPATCH_LEVEL
);
1434 rval
= queryfunc(adapter
, oid
, buf
, *buflen
,
1435 &byteswritten
, &bytesneeded
);
1436 FASTCALL1(hal_lower_irql
, irql
);
1438 /* Wait for requests that block. */
1440 if (rval
== NDIS_STATUS_PENDING
) {
1441 error
= tsleep(&sc
->ndis_block
.nmb_wkupdpctimer
,
1442 0, "ndisget", 5 * hz
);
1443 rval
= sc
->ndis_block
.nmb_getstat
;
1447 *buflen
= byteswritten
;
1449 *buflen
= bytesneeded
;
1451 if (rval
== NDIS_STATUS_INVALID_LENGTH
||
1452 rval
== NDIS_STATUS_BUFFER_TOO_SHORT
)
1455 if (rval
== NDIS_STATUS_INVALID_OID
)
1458 if (rval
== NDIS_STATUS_NOT_SUPPORTED
||
1459 rval
== NDIS_STATUS_NOT_ACCEPTED
)
1462 if (rval
!= NDIS_STATUS_SUCCESS
)
1469 ndis_unload_driver(void *arg
)
1471 struct ndis_softc
*sc
;
1475 kfree(sc
->ndis_block
.nmb_rlist
, M_DEVBUF
);
1477 ndis_flush_sysctls(sc
);
1479 ndis_shrink_thrqueue(8);
1480 TAILQ_REMOVE(&ndis_devhead
, &sc
->ndis_block
, link
);
1485 #define NDIS_LOADED htonl(0x42534F44)
1488 ndis_load_driver(vm_offset_t img
, void *arg
)
1491 image_optional_header opt_hdr
;
1492 image_import_descriptor imp_desc
;
1493 ndis_unicode_string dummystr
;
1494 ndis_miniport_block
*block
;
1498 struct ndis_softc
*sc
;
1503 * Only perform the relocation/linking phase once
1504 * since the binary image may be shared among multiple
1508 ptr
= (uint32_t *)(img
+ 8);
1509 if (*ptr
!= NDIS_LOADED
) {
1510 /* Perform text relocation */
1511 if (pe_relocate(img
))
1514 /* Dynamically link the NDIS.SYS routines -- required. */
1515 if (pe_patch_imports(img
, "NDIS", ndis_functbl
))
1518 /* Dynamically link the HAL.dll routines -- also required. */
1519 if (pe_patch_imports(img
, "HAL", hal_functbl
))
1522 /* Dynamically link ntoskrnl.exe -- optional. */
1523 if (pe_get_import_descriptor(img
,
1524 &imp_desc
, "ntoskrnl") == 0) {
1525 if (pe_patch_imports(img
,
1526 "ntoskrnl", ntoskrnl_functbl
))
1532 /* Locate the driver entry point */
1533 pe_get_optional_header(img
, &opt_hdr
);
1534 entry
= (driver_entry
)pe_translate_addr(img
, opt_hdr
.ioh_entryaddr
);
1536 dummystr
.nus_len
= strlen(NDIS_DUMMY_PATH
) * 2;
1537 dummystr
.nus_maxlen
= strlen(NDIS_DUMMY_PATH
) * 2;
1538 dummystr
.nus_buf
= NULL
;
1539 ndis_ascii_to_unicode(NDIS_DUMMY_PATH
, &dummystr
.nus_buf
);
1542 * Now that we have the miniport driver characteristics,
1543 * create an NDIS block and call the init handler.
1544 * This will cause the driver to try to probe for
1548 block
= &sc
->ndis_block
;
1550 ptr
= (uint32_t *)block
;
1551 for (idx
= 0; idx
< sizeof(ndis_miniport_block
) / 4; idx
++) {
1552 *ptr
= idx
| 0xdead0000;
1556 block
->nmb_signature
= (void *)0xcafebabe;
1557 block
->nmb_setdone_func
= ndis_setdone_func
;
1558 block
->nmb_querydone_func
= ndis_getdone_func
;
1559 block
->nmb_status_func
= ndis_status_func
;
1560 block
->nmb_statusdone_func
= ndis_statusdone_func
;
1561 block
->nmb_resetdone_func
= ndis_resetdone_func
;
1562 block
->nmb_sendrsrc_func
= ndis_sendrsrcavail_func
;
1564 block
->nmb_ifp
= &sc
->arpcom
.ac_if
;
1565 block
->nmb_dev
= sc
->ndis_dev
;
1566 block
->nmb_img
= img
;
1567 block
->nmb_devobj
.do_rsvd
= block
;
1570 * Now call the DriverEntry() routine. This will cause
1571 * a callout to the NdisInitializeWrapper() and
1572 * NdisMRegisterMiniport() routines.
1574 status
= entry(&block
->nmb_devobj
, &dummystr
);
1576 kfree (dummystr
.nus_buf
, M_DEVBUF
);
1578 if (status
!= NDIS_STATUS_SUCCESS
)
1581 ndis_enlarge_thrqueue(8);
1583 TAILQ_INSERT_TAIL(&ndis_devhead
, block
, link
);