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.1 2004/07/29 20:51:33 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>
53 #include <machine/bus.h>
54 #include <machine/resource.h>
59 #include <net/if_arp.h>
60 #include <net/ethernet.h>
61 #include <net/if_dl.h>
62 #include <net/if_media.h>
64 #include <netproto/802_11/ieee80211_var.h>
65 #include <netproto/802_11/ieee80211_ioctl.h>
69 #include "resource_var.h"
70 #include "ntoskrnl_var.h"
74 #include <dev/netif/ndis/if_ndisvar.h>
76 #define NDIS_DUMMY_PATH "\\\\some\\bogus\\path"
78 __stdcall static void ndis_status_func(ndis_handle, ndis_status,
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);
86 struct nd_head ndis_devhead;
89 void (*nr_func)(void *);
92 STAILQ_ENTRY(ndis_req) link;
96 struct ndisqhead *np_q;
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 *);
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;
117 static struct ndisproc ndis_tproc;
118 static struct ndisproc ndis_iproc;
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
127 ndis_modevent(module_t mod, int cmd, void *arg)
133 /* Initialize subsystems */
137 /* Initialize TX buffer UMA zone. */
138 ndis_create_kthreads();
140 TAILQ_INIT(&ndis_devhead);
145 ndis_destroy_kthreads();
146 if (TAILQ_FIRST(&ndis_devhead) == NULL) {
147 /* Shut down subsystems */
153 malloc_uninit(M_NDIS_PACKET);
154 malloc_uninit(M_NDIS_BUFFER);
160 ndis_destroy_kthreads();
162 /* Shut down subsystems */
168 malloc_uninit(M_NDIS_PACKET);
169 malloc_uninit(M_NDIS_BUFFER);
179 DEV_MODULE(ndisapi, ndis_modevent, NULL);
180 MODULE_VERSION(ndisapi, 1);
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.
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.
201 struct ndis_req *r = NULL, *die = NULL;
203 struct lwkt_tokref tokref;
209 /* Sleep, but preserve our original priority. */
210 ndis_thsuspend(p->np_td, 0);
212 /* Look for any jobs on the work queue. */
214 lwkt_gettoken(&tokref, &ndis_thr_token);
215 p->np_state = NDIS_PSTATE_RUNNING;
216 while(STAILQ_FIRST(p->np_q) != NULL) {
217 r = STAILQ_FIRST(p->np_q);
218 STAILQ_REMOVE_HEAD(p->np_q, link);
219 lwkt_reltoken(&tokref);
223 if (r->nr_func != NULL)
224 (*r->nr_func)(r->nr_arg);
226 lwkt_gettoken(&tokref, &ndis_thr_token);
227 STAILQ_INSERT_HEAD(&ndis_free, r, link);
229 /* Check for a shutdown request */
231 if (r->nr_exit == TRUE)
234 p->np_state = NDIS_PSTATE_SLEEPING;
235 lwkt_reltoken(&tokref);
237 /* Bail if we were told to shut down. */
248 ndis_create_kthreads()
253 lwkt_token_init(&ndis_thr_token);
255 STAILQ_INIT(&ndis_ttodo);
256 STAILQ_INIT(&ndis_itodo);
257 STAILQ_INIT(&ndis_free);
259 for (i = 0; i < ndis_jobs; i++) {
260 r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK);
265 STAILQ_INSERT_HEAD(&ndis_free, r, link);
269 ndis_tproc.np_q = &ndis_ttodo;
270 ndis_tproc.np_state = NDIS_PSTATE_SLEEPING;
271 error = kthread_create_stk(ndis_runq, &ndis_tproc,
272 &ndis_tproc.np_td, NDIS_KSTACK_PAGES * PAGE_SIZE,
277 ndis_iproc.np_q = &ndis_itodo;
278 ndis_iproc.np_state = NDIS_PSTATE_SLEEPING;
279 error = kthread_create_stk(ndis_runq, &ndis_iproc,
280 &ndis_iproc.np_td, NDIS_KSTACK_PAGES * PAGE_SIZE,
285 while ((r = STAILQ_FIRST(&ndis_free)) != NULL) {
286 STAILQ_REMOVE_HEAD(&ndis_free, link);
296 ndis_destroy_kthreads()
300 /* Stop the threads. */
302 ndis_stop_thread(NDIS_TASKQUEUE);
303 ndis_stop_thread(NDIS_SWI);
305 /* Destroy request structures. */
307 while ((r = STAILQ_FIRST(&ndis_free)) != NULL) {
308 STAILQ_REMOVE_HEAD(&ndis_free, link);
312 lwkt_token_uninit(&ndis_thr_token);
324 struct lwkt_tokref tokref;
326 if (t == NDIS_TASKQUEUE) {
328 td = ndis_tproc.np_td;
331 td = ndis_iproc.np_td;
334 /* Create and post a special 'exit' job. */
336 lwkt_gettoken(&tokref, &ndis_thr_token);
337 r = STAILQ_FIRST(&ndis_free);
338 STAILQ_REMOVE_HEAD(&ndis_free, link);
342 STAILQ_INSERT_TAIL(q, r, link);
343 lwkt_reltoken(&tokref);
347 /* wait for thread exit */
349 tsleep(r, PCATCH, "ndisthexit", hz * 60);
351 /* Now empty the job list. */
353 lwkt_gettoken(&tokref, &ndis_thr_token);
354 while ((r = STAILQ_FIRST(q)) != NULL) {
355 STAILQ_REMOVE_HEAD(q, link);
356 STAILQ_INSERT_HEAD(&ndis_free, r, link);
358 lwkt_reltoken(&tokref);
364 ndis_enlarge_thrqueue(cnt)
369 struct lwkt_tokref tokref;
371 for (i = 0; i < cnt; i++) {
372 r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK);
375 lwkt_gettoken(&tokref, &ndis_thr_token);
376 STAILQ_INSERT_HEAD(&ndis_free, r, link);
378 lwkt_reltoken(&tokref);
385 ndis_shrink_thrqueue(cnt)
390 struct lwkt_tokref tokref;
392 for (i = 0; i < cnt; i++) {
393 lwkt_gettoken(&tokref, &ndis_thr_token);
394 r = STAILQ_FIRST(&ndis_free);
396 lwkt_reltoken(&tokref);
399 STAILQ_REMOVE_HEAD(&ndis_free, link);
401 lwkt_reltoken(&tokref);
409 ndis_unsched(func, arg, t)
410 void (*func)(void *);
417 struct lwkt_tokref tokref;
419 if (t == NDIS_TASKQUEUE) {
421 td = ndis_tproc.np_td;
424 td = ndis_iproc.np_td;
427 lwkt_gettoken(&tokref, &ndis_thr_token);
428 STAILQ_FOREACH(r, q, link) {
429 if (r->nr_func == func && r->nr_arg == arg) {
430 STAILQ_REMOVE(q, r, ndis_req, link);
431 STAILQ_INSERT_HEAD(&ndis_free, r, link);
432 lwkt_reltoken(&tokref);
437 lwkt_reltoken(&tokref);
443 ndis_sched(func, arg, t)
444 void (*func)(void *);
452 struct lwkt_tokref tokref;
454 if (t == NDIS_TASKQUEUE) {
456 td = ndis_tproc.np_td;
459 td = ndis_iproc.np_td;
462 lwkt_gettoken(&tokref, &ndis_thr_token);
464 * Check to see if an instance of this job is already
465 * pending. If so, don't bother queuing it again.
467 STAILQ_FOREACH(r, q, link) {
468 if (r->nr_func == func && r->nr_arg == arg) {
469 lwkt_reltoken(&tokref);
473 r = STAILQ_FIRST(&ndis_free);
475 lwkt_reltoken(&tokref);
478 STAILQ_REMOVE_HEAD(&ndis_free, link);
482 STAILQ_INSERT_TAIL(q, r, link);
483 if (t == NDIS_TASKQUEUE)
484 s = ndis_tproc.np_state;
486 s = ndis_iproc.np_state;
487 lwkt_reltoken(&tokref);
490 * Post the job, but only if the thread is actually blocked
491 * on its own suspend call. If a driver queues up a job with
492 * NdisScheduleWorkItem() which happens to do a KeWaitForObject(),
493 * it may suspend there, and in that case we don't want to wake
494 * it up until KeWaitForObject() gets woken up on its own.
496 if (s == NDIS_PSTATE_SLEEPING)
503 ndis_thsuspend(td, timo)
509 error = tsleep(td, 0, "ndissp", timo);
520 __stdcall static void
521 ndis_sendrsrcavail_func(adapter)
527 __stdcall static void
528 ndis_status_func(adapter, status, sbuf, slen)
534 ndis_miniport_block *block;
537 if (block->nmb_ifp->if_flags & IFF_DEBUG)
538 device_printf (block->nmb_dev, "status: %x\n", status);
542 __stdcall static void
543 ndis_statusdone_func(adapter)
546 ndis_miniport_block *block;
549 if (block->nmb_ifp->if_flags & IFF_DEBUG)
550 device_printf (block->nmb_dev, "status complete\n");
554 __stdcall static void
555 ndis_setdone_func(adapter, status)
559 ndis_miniport_block *block;
562 block->nmb_setstat = status;
563 wakeup(&block->nmb_wkupdpctimer);
567 __stdcall static void
568 ndis_getdone_func(adapter, status)
572 ndis_miniport_block *block;
575 block->nmb_getstat = status;
576 wakeup(&block->nmb_wkupdpctimer);
580 __stdcall static void
581 ndis_resetdone_func(adapter, status, addressingreset)
584 uint8_t addressingreset;
586 ndis_miniport_block *block;
589 if (block->nmb_ifp->if_flags & IFF_DEBUG)
590 device_printf (block->nmb_dev, "reset done...\n");
591 wakeup(block->nmb_ifp);
596 ndis_create_sysctls(arg)
599 struct ndis_softc *sc;
607 vals = sc->ndis_regvals;
609 TAILQ_INIT(&sc->ndis_cfglist_head);
611 #if __FreeBSD_version < 502113
612 /* Create the sysctl tree. */
614 sc->ndis_tree = SYSCTL_ADD_NODE(&sc->ndis_ctx,
615 SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
616 device_get_nameunit(sc->ndis_dev), CTLFLAG_RD, 0,
617 device_get_desc(sc->ndis_dev));
620 /* Add the driver-specific registry keys. */
622 vals = sc->ndis_regvals;
624 if (vals->nc_cfgkey == NULL)
626 if (vals->nc_idx != sc->ndis_devidx) {
631 SYSCTL_ADD_STRING(&sc->ndis_ctx,
632 SYSCTL_CHILDREN(sc->ndis_tree),
633 OID_AUTO, vals->nc_cfgkey,
634 CTLFLAG_RW, vals->nc_val,
635 sizeof(vals->nc_val),
638 SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev),
639 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)),
640 OID_AUTO, vals->nc_cfgkey,
641 CTLFLAG_RW, vals->nc_val,
642 sizeof(vals->nc_val),
648 /* Now add a couple of builtin keys. */
651 * Environment can be either Windows (0) or WindowsNT (1).
652 * We qualify as the latter.
654 ndis_add_sysctl(sc, "Environment",
655 "Windows environment", "1", CTLFLAG_RD);
657 /* NDIS version should be 5.1. */
658 ndis_add_sysctl(sc, "NdisVersion",
659 "NDIS API Version", "0x00050001", CTLFLAG_RD);
661 /* Bus type (PCI, PCMCIA, etc...) */
662 sprintf(buf, "%d", (int)sc->ndis_iftype);
663 ndis_add_sysctl(sc, "BusType", "Bus Type", buf, CTLFLAG_RD);
665 if (sc->ndis_res_io != NULL) {
666 sprintf(buf, "0x%lx", rman_get_start(sc->ndis_res_io));
667 ndis_add_sysctl(sc, "IOBaseAddress",
668 "Base I/O Address", buf, CTLFLAG_RD);
671 if (sc->ndis_irq != NULL) {
672 sprintf(buf, "%lu", rman_get_start(sc->ndis_irq));
673 ndis_add_sysctl(sc, "InterruptNumber",
674 "Interrupt Number", buf, CTLFLAG_RD);
681 ndis_add_sysctl(arg, key, desc, val, flag)
688 struct ndis_softc *sc;
689 struct ndis_cfglist *cfg;
694 cfg = malloc(sizeof(struct ndis_cfglist), M_DEVBUF, M_NOWAIT|M_ZERO);
699 cfg->ndis_cfg.nc_cfgkey = strdup(key, M_DEVBUF);
701 snprintf(descstr, sizeof(descstr), "%s (dynamic)", key);
702 cfg->ndis_cfg.nc_cfgdesc = strdup(descstr, M_DEVBUF);
704 cfg->ndis_cfg.nc_cfgdesc = strdup(desc, M_DEVBUF);
705 strcpy(cfg->ndis_cfg.nc_val, val);
707 TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link);
710 SYSCTL_ADD_STRING(&sc->ndis_ctx, SYSCTL_CHILDREN(sc->ndis_tree),
711 OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag,
712 cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val),
713 cfg->ndis_cfg.nc_cfgdesc);
715 SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev),
716 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)),
717 OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag,
718 cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val),
719 cfg->ndis_cfg.nc_cfgdesc);
726 ndis_flush_sysctls(arg)
729 struct ndis_softc *sc;
730 struct ndis_cfglist *cfg;
734 while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) {
735 cfg = TAILQ_FIRST(&sc->ndis_cfglist_head);
736 TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link);
737 free(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF);
738 free(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF);
749 struct ndis_softc *sc;
750 __stdcall ndis_return_handler returnfunc;
757 adapter = sc->ndis_block.nmb_miniportadapterctx;
762 returnfunc = sc->ndis_chars.nmc_return_packet_func;
763 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
764 returnfunc(adapter, p);
765 FASTCALL1(hal_lower_irql, irql);
771 ndis_extref_packet(void *arg)
773 ndis_packet *p = arg;
779 ndis_extfree_packet(void *arg)
781 ndis_packet *p = arg;
786 /* Decrement refcount. */
789 /* Release packet when refcount hits zero, otherwise return. */
793 ndis_sched(ndis_return, p, NDIS_SWI);
799 ndis_return_packet(struct ndis_softc *sc, ndis_packet *p)
801 ndis_extfree_packet(p);
815 free(b0, M_NDIS_BUFFER);
829 ndis_free_bufs(p->np_private.npp_head);
830 free(p, M_NDIS_PACKET);
836 ndis_convert_res(arg)
839 struct ndis_softc *sc;
840 ndis_resource_list *rl = NULL;
841 cm_partial_resource_desc *prd = NULL;
842 ndis_miniport_block *block;
844 struct resource_list *brl;
845 struct resource_list brl_rev;
846 struct resource_list_entry *brle, *n;
850 block = &sc->ndis_block;
853 SLIST_INIT(&brl_rev);
855 rl = malloc(sizeof(ndis_resource_list) +
856 (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)),
857 M_DEVBUF, M_NOWAIT|M_ZERO);
862 rl->cprl_version = 5;
863 rl->cprl_version = 1;
864 rl->cprl_count = sc->ndis_rescnt;
865 prd = rl->cprl_partial_descs;
867 brl = BUS_GET_RESOURCE_LIST(dev, dev);
872 * We have a small problem. Some PCI devices have
873 * multiple I/O ranges. Windows orders them starting
874 * from lowest numbered BAR to highest. We discover
875 * them in that order too, but insert them into a singly
876 * linked list head first, which means when time comes
877 * to traverse the list, we enumerate them in reverse
878 * order. This screws up some drivers which expect the
879 * BARs to be in ascending order so that they can choose
880 * the "first" one as their register space. Unfortunately,
881 * in order to fix this, we have to create our own
882 * temporary list with the entries in reverse order.
884 SLIST_FOREACH(brle, brl, link) {
885 n = malloc(sizeof(struct resource_list_entry),
891 bcopy((char *)brle, (char *)n,
892 sizeof(struct resource_list_entry));
893 SLIST_INSERT_HEAD(&brl_rev, n, link);
896 SLIST_FOREACH(brle, &brl_rev, link) {
897 switch (brle->type) {
899 prd->cprd_type = CmResourceTypePort;
900 prd->cprd_flags = CM_RESOURCE_PORT_IO;
901 prd->cprd_sharedisp =
902 CmResourceShareDeviceExclusive;
903 prd->u.cprd_port.cprd_start.np_quad =
905 prd->u.cprd_port.cprd_len = brle->count;
908 prd->cprd_type = CmResourceTypeMemory;
910 CM_RESOURCE_MEMORY_READ_WRITE;
911 prd->cprd_sharedisp =
912 CmResourceShareDeviceExclusive;
913 prd->u.cprd_port.cprd_start.np_quad =
915 prd->u.cprd_port.cprd_len = brle->count;
918 prd->cprd_type = CmResourceTypeInterrupt;
920 prd->cprd_sharedisp =
921 CmResourceShareDeviceExclusive;
922 prd->u.cprd_intr.cprd_level = brle->start;
923 prd->u.cprd_intr.cprd_vector = brle->start;
924 prd->u.cprd_intr.cprd_affinity = 0;
933 block->nmb_rlist = rl;
937 while (!SLIST_EMPTY(&brl_rev)) {
938 n = SLIST_FIRST(&brl_rev);
939 SLIST_REMOVE_HEAD(&brl_rev, link);
947 * Map an NDIS packet to an mbuf list. When an NDIS driver receives a
948 * packet, it will hand it to us in the form of an ndis_packet,
949 * which we need to convert to an mbuf that is then handed off
950 * to the stack. Note: we configure the mbuf list so that it uses
951 * the memory regions specified by the ndis_buffer structures in
952 * the ndis_packet as external storage. In most cases, this will
953 * point to a memory region allocated by the driver (either by
954 * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect
955 * the driver to handle free()ing this region for is, so we set up
956 * a dummy no-op free handler for it.
964 struct mbuf *m, *prev = NULL;
966 ndis_packet_private *priv;
969 if (p == NULL || m0 == NULL)
972 priv = &p->np_private;
973 buf = priv->npp_head;
976 for (buf = priv->npp_head; buf != NULL; buf = buf->nb_next) {
977 if (buf == priv->npp_head)
978 MGETHDR(m, MB_DONTWAIT, MT_HEADER);
980 MGET(m, MB_DONTWAIT, MT_DATA);
986 m->m_len = buf->nb_bytecount;
987 m->m_data = MDL_VA(buf);
988 m->m_ext.ext_nfree.new = ndis_extfree_packet;
989 m->m_ext.ext_nref.new = ndis_extref_packet;
990 m->m_ext.ext_arg = p;
991 m->m_ext.ext_buf = m->m_data;
992 m->m_ext.ext_size = m->m_len;
995 MEXTADD(m, m->m_data, m->m_len, ndis_free_packet,
1000 if (m->m_flags & MT_HEADER)
1007 (*m0)->m_pkthdr.len = totlen;
1013 * Create an mbuf chain from an NDIS packet chain.
1014 * This is used mainly when transmitting packets, where we need
1015 * to turn an mbuf off an interface's send queue and transform it
1016 * into an NDIS packet which will be fed into the NDIS driver's
1019 * NDIS packets consist of two parts: an ndis_packet structure,
1020 * which is vaguely analagous to the pkthdr portion of an mbuf,
1021 * and one or more ndis_buffer structures, which define the
1022 * actual memory segments in which the packet data resides.
1023 * We need to allocate one ndis_buffer for each mbuf in a chain,
1024 * plus one ndis_packet as the header.
1033 ndis_buffer *buf = NULL, *prev = NULL;
1034 ndis_packet_private *priv;
1036 if (p == NULL || m0 == NULL)
1039 /* If caller didn't supply a packet, make one. */
1041 *p = malloc(sizeof(ndis_packet), M_NDIS_PACKET, M_NOWAIT|M_ZERO);
1046 priv = &(*p)->np_private;
1047 priv->npp_totlen = m0->m_pkthdr.len;
1048 priv->npp_packetooboffset = offsetof(ndis_packet, np_oob);
1049 priv->npp_ndispktflags = NDIS_PACKET_ALLOCATED_BY_NDIS;
1051 for (m = m0; m != NULL; m = m->m_next) {
1054 buf = malloc(sizeof(ndis_buffer), M_NDIS_BUFFER, M_NOWAIT|M_ZERO);
1056 ndis_free_packet(*p);
1061 MDL_INIT(buf, m->m_data, m->m_len);
1062 if (priv->npp_head == NULL)
1063 priv->npp_head = buf;
1065 prev->nb_next = buf;
1069 priv->npp_tail = buf;
1070 priv->npp_totlen = m0->m_pkthdr.len;
1076 ndis_get_supported_oids(arg, oids, oidcnt)
1084 if (arg == NULL || oids == NULL || oidcnt == NULL)
1087 ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len);
1089 o = malloc(len, M_DEVBUF, M_NOWAIT);
1093 rval = ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, o, &len);
1107 ndis_set_info(arg, oid, buf, buflen)
1113 struct ndis_softc *sc;
1115 ndis_handle adapter;
1116 __stdcall ndis_setinfo_handler setfunc;
1117 uint32_t byteswritten = 0, bytesneeded = 0;
1124 setfunc = sc->ndis_chars.nmc_setinfo_func;
1125 adapter = sc->ndis_block.nmb_miniportadapterctx;
1128 if (adapter == NULL || setfunc == NULL)
1131 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1132 rval = setfunc(adapter, oid, buf, *buflen,
1133 &byteswritten, &bytesneeded);
1134 FASTCALL1(hal_lower_irql, irql);
1136 if (rval == NDIS_STATUS_PENDING) {
1137 error = tsleep(&sc->ndis_block.nmb_wkupdpctimer,
1138 0, "ndisset", 5 * hz);
1139 rval = sc->ndis_block.nmb_setstat;
1143 *buflen = byteswritten;
1145 *buflen = bytesneeded;
1147 if (rval == NDIS_STATUS_INVALID_LENGTH)
1150 if (rval == NDIS_STATUS_INVALID_OID)
1153 if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1154 rval == NDIS_STATUS_NOT_ACCEPTED)
1157 if (rval != NDIS_STATUS_SUCCESS)
1163 typedef void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status);
1166 ndis_send_packets(arg, packets, cnt)
1168 ndis_packet **packets;
1171 struct ndis_softc *sc;
1172 ndis_handle adapter;
1173 __stdcall ndis_sendmulti_handler sendfunc;
1174 __stdcall ndis_senddone_func senddonefunc;
1180 adapter = sc->ndis_block.nmb_miniportadapterctx;
1181 if (adapter == NULL)
1183 sendfunc = sc->ndis_chars.nmc_sendmulti_func;
1184 senddonefunc = sc->ndis_block.nmb_senddone_func;
1185 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1186 sendfunc(adapter, packets, cnt);
1187 FASTCALL1(hal_lower_irql, irql);
1189 for (i = 0; i < cnt; i++) {
1192 * Either the driver already handed the packet to
1193 * ndis_txeof() due to a failure, or it wants to keep
1194 * it and release it asynchronously later. Skip to the
1197 if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING)
1199 senddonefunc(&sc->ndis_block, p, p->np_oob.npo_status);
1206 ndis_send_packet(arg, packet)
1208 ndis_packet *packet;
1210 struct ndis_softc *sc;
1211 ndis_handle adapter;
1213 __stdcall ndis_sendsingle_handler sendfunc;
1214 __stdcall ndis_senddone_func senddonefunc;
1218 adapter = sc->ndis_block.nmb_miniportadapterctx;
1219 if (adapter == NULL)
1221 sendfunc = sc->ndis_chars.nmc_sendsingle_func;
1222 senddonefunc = sc->ndis_block.nmb_senddone_func;
1224 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1225 status = sendfunc(adapter, packet, packet->np_private.npp_flags);
1226 FASTCALL1(hal_lower_irql, irql);
1228 if (status == NDIS_STATUS_PENDING)
1231 senddonefunc(&sc->ndis_block, packet, status);
1240 struct ndis_softc *sc;
1245 sc->ndis_tmaps = malloc(sizeof(bus_dmamap_t) * sc->ndis_maxpkts,
1246 M_DEVBUF, M_NOWAIT|M_ZERO);
1248 if (sc->ndis_tmaps == NULL)
1251 for (i = 0; i < sc->ndis_maxpkts; i++) {
1252 error = bus_dmamap_create(sc->ndis_ttag, 0,
1253 &sc->ndis_tmaps[i]);
1255 free(sc->ndis_tmaps, M_DEVBUF);
1264 ndis_destroy_dma(arg)
1267 struct ndis_softc *sc;
1269 ndis_packet *p = NULL;
1274 for (i = 0; i < sc->ndis_maxpkts; i++) {
1275 if (sc->ndis_txarray[i] != NULL) {
1276 p = sc->ndis_txarray[i];
1277 m = (struct mbuf *)p->np_rsvd[1];
1280 ndis_free_packet(sc->ndis_txarray[i]);
1282 bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]);
1285 free(sc->ndis_tmaps, M_DEVBUF);
1287 bus_dma_tag_destroy(sc->ndis_ttag);
1296 struct ndis_softc *sc;
1297 ndis_handle adapter;
1298 __stdcall ndis_reset_handler resetfunc;
1299 uint8_t addressing_reset;
1306 ifp = &sc->arpcom.ac_if;
1308 adapter = sc->ndis_block.nmb_miniportadapterctx;
1309 resetfunc = sc->ndis_chars.nmc_reset_func;
1311 if (adapter == NULL || resetfunc == NULL)
1314 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1315 rval = resetfunc(&addressing_reset, adapter);
1316 FASTCALL1(hal_lower_irql, irql);
1318 if (rval == NDIS_STATUS_PENDING) {
1319 tsleep(sc, 0, "ndisrst", 0);
1329 struct ndis_softc *sc;
1330 ndis_handle adapter;
1331 __stdcall ndis_halt_handler haltfunc;
1336 ifp = &sc->arpcom.ac_if;
1339 adapter = sc->ndis_block.nmb_miniportadapterctx;
1340 if (adapter == NULL) {
1346 * The adapter context is only valid after the init
1347 * handler has been called, and is invalid once the
1348 * halt handler has been called.
1351 haltfunc = sc->ndis_chars.nmc_halt_func;
1357 sc->ndis_block.nmb_miniportadapterctx = NULL;
1364 ndis_shutdown_nic(arg)
1367 struct ndis_softc *sc;
1368 ndis_handle adapter;
1369 __stdcall ndis_shutdown_handler shutdownfunc;
1374 adapter = sc->ndis_block.nmb_miniportadapterctx;
1375 shutdownfunc = sc->ndis_chars.nmc_shutdown_handler;
1377 if (adapter == NULL || shutdownfunc == NULL)
1380 if (sc->ndis_chars.nmc_rsvd0 == NULL)
1381 shutdownfunc(adapter);
1383 shutdownfunc(sc->ndis_chars.nmc_rsvd0);
1385 ndis_shrink_thrqueue(8);
1386 TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link);
1395 struct ndis_softc *sc;
1396 ndis_miniport_block *block;
1397 __stdcall ndis_init_handler initfunc;
1398 ndis_status status, openstatus = 0;
1399 ndis_medium mediumarray[NdisMediumMax];
1400 uint32_t chosenmedium, i;
1408 block = &sc->ndis_block;
1409 initfunc = sc->ndis_chars.nmc_init_func;
1412 TAILQ_INIT(&block->nmb_timerlist);
1414 for (i = 0; i < NdisMediumMax; i++)
1417 status = initfunc(&openstatus, &chosenmedium,
1418 mediumarray, NdisMediumMax, block, block);
1421 * If the init fails, blow away the other exported routines
1422 * we obtained from the driver so we can't call them later.
1423 * If the init failed, none of these will work.
1425 if (status != NDIS_STATUS_SUCCESS) {
1427 sc->ndis_block.nmb_miniportadapterctx = NULL;
1436 ndis_enable_intr(arg)
1439 struct ndis_softc *sc;
1440 ndis_handle adapter;
1441 __stdcall ndis_enable_interrupts_handler intrenbfunc;
1444 adapter = sc->ndis_block.nmb_miniportadapterctx;
1445 intrenbfunc = sc->ndis_chars.nmc_enable_interrupts_func;
1446 if (adapter == NULL || intrenbfunc == NULL)
1448 intrenbfunc(adapter);
1454 ndis_disable_intr(arg)
1457 struct ndis_softc *sc;
1458 ndis_handle adapter;
1459 __stdcall ndis_disable_interrupts_handler intrdisfunc;
1464 adapter = sc->ndis_block.nmb_miniportadapterctx;
1465 intrdisfunc = sc->ndis_chars.nmc_disable_interrupts_func;
1467 if (adapter == NULL || intrdisfunc == NULL)
1469 intrdisfunc(adapter);
1475 ndis_isr(arg, ourintr, callhandler)
1480 struct ndis_softc *sc;
1481 ndis_handle adapter;
1482 __stdcall ndis_isr_handler isrfunc;
1483 uint8_t accepted, queue;
1485 if (arg == NULL || ourintr == NULL || callhandler == NULL)
1489 adapter = sc->ndis_block.nmb_miniportadapterctx;
1490 isrfunc = sc->ndis_chars.nmc_isr_func;
1491 if (adapter == NULL || isrfunc == NULL)
1494 isrfunc(&accepted, &queue, adapter);
1495 *ourintr = accepted;
1496 *callhandler = queue;
1505 struct ndis_softc *sc;
1506 ndis_handle adapter;
1507 __stdcall ndis_interrupt_handler intrfunc;
1515 adapter = sc->ndis_block.nmb_miniportadapterctx;
1516 intrfunc = sc->ndis_chars.nmc_interrupt_func;
1518 if (adapter == NULL || intrfunc == NULL)
1527 ndis_get_info(arg, oid, buf, buflen)
1533 struct ndis_softc *sc;
1535 ndis_handle adapter;
1536 ndis_queryinfo_handler queryfunc;
1537 uint32_t byteswritten = 0, bytesneeded = 0;
1544 queryfunc = sc->ndis_chars.nmc_queryinfo_func;
1545 adapter = sc->ndis_block.nmb_miniportadapterctx;
1548 if (adapter == NULL || queryfunc == NULL)
1551 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1552 rval = queryfunc(adapter, oid, buf, *buflen,
1553 &byteswritten, &bytesneeded);
1554 FASTCALL1(hal_lower_irql, irql);
1556 /* Wait for requests that block. */
1558 if (rval == NDIS_STATUS_PENDING) {
1559 error = tsleep(&sc->ndis_block.nmb_wkupdpctimer,
1560 0, "ndisget", 5 * hz);
1561 rval = sc->ndis_block.nmb_getstat;
1565 *buflen = byteswritten;
1567 *buflen = bytesneeded;
1569 if (rval == NDIS_STATUS_INVALID_LENGTH ||
1570 rval == NDIS_STATUS_BUFFER_TOO_SHORT)
1573 if (rval == NDIS_STATUS_INVALID_OID)
1576 if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1577 rval == NDIS_STATUS_NOT_ACCEPTED)
1580 if (rval != NDIS_STATUS_SUCCESS)
1587 ndis_unload_driver(arg)
1590 struct ndis_softc *sc;
1594 free(sc->ndis_block.nmb_rlist, M_DEVBUF);
1596 ndis_flush_sysctls(sc);
1598 ndis_shrink_thrqueue(8);
1599 TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link);
1604 #define NDIS_LOADED htonl(0x42534F44)
1607 ndis_load_driver(img, arg)
1611 __stdcall driver_entry entry;
1612 image_optional_header opt_hdr;
1613 image_import_descriptor imp_desc;
1614 ndis_unicode_string dummystr;
1615 ndis_miniport_block *block;
1619 struct ndis_softc *sc;
1624 * Only perform the relocation/linking phase once
1625 * since the binary image may be shared among multiple
1629 ptr = (uint32_t *)(img + 8);
1630 if (*ptr != NDIS_LOADED) {
1631 /* Perform text relocation */
1632 if (pe_relocate(img))
1635 /* Dynamically link the NDIS.SYS routines -- required. */
1636 if (pe_patch_imports(img, "NDIS", ndis_functbl))
1639 /* Dynamically link the HAL.dll routines -- also required. */
1640 if (pe_patch_imports(img, "HAL", hal_functbl))
1643 /* Dynamically link ntoskrnl.exe -- optional. */
1644 if (pe_get_import_descriptor(img,
1645 &imp_desc, "ntoskrnl") == 0) {
1646 if (pe_patch_imports(img,
1647 "ntoskrnl", ntoskrnl_functbl))
1653 /* Locate the driver entry point */
1654 pe_get_optional_header(img, &opt_hdr);
1655 entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
1657 dummystr.nus_len = strlen(NDIS_DUMMY_PATH) * 2;
1658 dummystr.nus_maxlen = strlen(NDIS_DUMMY_PATH) * 2;
1659 dummystr.nus_buf = NULL;
1660 ndis_ascii_to_unicode(NDIS_DUMMY_PATH, &dummystr.nus_buf);
1663 * Now that we have the miniport driver characteristics,
1664 * create an NDIS block and call the init handler.
1665 * This will cause the driver to try to probe for
1669 block = &sc->ndis_block;
1671 ptr = (uint32_t *)block;
1672 for (idx = 0; idx < sizeof(ndis_miniport_block) / 4; idx++) {
1673 *ptr = idx | 0xdead0000;
1677 block->nmb_signature = (void *)0xcafebabe;
1678 block->nmb_setdone_func = ndis_setdone_func;
1679 block->nmb_querydone_func = ndis_getdone_func;
1680 block->nmb_status_func = ndis_status_func;
1681 block->nmb_statusdone_func = ndis_statusdone_func;
1682 block->nmb_resetdone_func = ndis_resetdone_func;
1683 block->nmb_sendrsrc_func = ndis_sendrsrcavail_func;
1685 block->nmb_ifp = &sc->arpcom.ac_if;
1686 block->nmb_dev = sc->ndis_dev;
1687 block->nmb_img = img;
1688 block->nmb_devobj.do_rsvd = block;
1691 * Now call the DriverEntry() routine. This will cause
1692 * a callout to the NdisInitializeWrapper() and
1693 * NdisMRegisterMiniport() routines.
1695 status = entry(&block->nmb_devobj, &dummystr);
1697 free (dummystr.nus_buf, M_DEVBUF);
1699 if (status != NDIS_STATUS_SUCCESS)
1702 ndis_enlarge_thrqueue(8);
1704 TAILQ_INSERT_TAIL(&ndis_devhead, block, link);