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.7 2005/06/09 20:17:53 hsu 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);
261 STAILQ_INSERT_HEAD(&ndis_free, r, link);
265 ndis_tproc.np_q = &ndis_ttodo;
266 ndis_tproc.np_state = NDIS_PSTATE_SLEEPING;
267 error = kthread_create_stk(ndis_runq, &ndis_tproc,
268 &ndis_tproc.np_td, NDIS_KSTACK_PAGES * PAGE_SIZE,
273 ndis_iproc.np_q = &ndis_itodo;
274 ndis_iproc.np_state = NDIS_PSTATE_SLEEPING;
275 error = kthread_create_stk(ndis_runq, &ndis_iproc,
276 &ndis_iproc.np_td, NDIS_KSTACK_PAGES * PAGE_SIZE,
281 while ((r = STAILQ_FIRST(&ndis_free)) != NULL) {
282 STAILQ_REMOVE_HEAD(&ndis_free, link);
292 ndis_destroy_kthreads()
296 /* Stop the threads. */
298 ndis_stop_thread(NDIS_TASKQUEUE);
299 ndis_stop_thread(NDIS_SWI);
301 /* Destroy request structures. */
303 while ((r = STAILQ_FIRST(&ndis_free)) != NULL) {
304 STAILQ_REMOVE_HEAD(&ndis_free, link);
308 lwkt_token_uninit(&ndis_thr_token);
320 struct lwkt_tokref tokref;
322 if (t == NDIS_TASKQUEUE) {
324 td = ndis_tproc.np_td;
327 td = ndis_iproc.np_td;
330 /* Create and post a special 'exit' job. */
332 lwkt_gettoken(&tokref, &ndis_thr_token);
333 r = STAILQ_FIRST(&ndis_free);
334 STAILQ_REMOVE_HEAD(&ndis_free, link);
338 STAILQ_INSERT_TAIL(q, r, link);
339 lwkt_reltoken(&tokref);
343 /* wait for thread exit */
345 tsleep(r, PCATCH, "ndisthexit", hz * 60);
347 /* Now empty the job list. */
349 lwkt_gettoken(&tokref, &ndis_thr_token);
350 while ((r = STAILQ_FIRST(q)) != NULL) {
351 STAILQ_REMOVE_HEAD(q, link);
352 STAILQ_INSERT_HEAD(&ndis_free, r, link);
354 lwkt_reltoken(&tokref);
360 ndis_enlarge_thrqueue(cnt)
365 struct lwkt_tokref tokref;
367 for (i = 0; i < cnt; i++) {
368 r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK);
369 lwkt_gettoken(&tokref, &ndis_thr_token);
370 STAILQ_INSERT_HEAD(&ndis_free, r, link);
372 lwkt_reltoken(&tokref);
379 ndis_shrink_thrqueue(cnt)
384 struct lwkt_tokref tokref;
386 for (i = 0; i < cnt; i++) {
387 lwkt_gettoken(&tokref, &ndis_thr_token);
388 r = STAILQ_FIRST(&ndis_free);
390 lwkt_reltoken(&tokref);
393 STAILQ_REMOVE_HEAD(&ndis_free, link);
395 lwkt_reltoken(&tokref);
403 ndis_unsched(func, arg, t)
404 void (*func)(void *);
411 struct lwkt_tokref tokref;
413 if (t == NDIS_TASKQUEUE) {
415 td = ndis_tproc.np_td;
418 td = ndis_iproc.np_td;
421 lwkt_gettoken(&tokref, &ndis_thr_token);
422 STAILQ_FOREACH(r, q, link) {
423 if (r->nr_func == func && r->nr_arg == arg) {
424 STAILQ_REMOVE(q, r, ndis_req, link);
425 STAILQ_INSERT_HEAD(&ndis_free, r, link);
426 lwkt_reltoken(&tokref);
431 lwkt_reltoken(&tokref);
437 ndis_sched(func, arg, t)
438 void (*func)(void *);
446 struct lwkt_tokref tokref;
448 if (t == NDIS_TASKQUEUE) {
450 td = ndis_tproc.np_td;
453 td = ndis_iproc.np_td;
456 lwkt_gettoken(&tokref, &ndis_thr_token);
458 * Check to see if an instance of this job is already
459 * pending. If so, don't bother queuing it again.
461 STAILQ_FOREACH(r, q, link) {
462 if (r->nr_func == func && r->nr_arg == arg) {
463 lwkt_reltoken(&tokref);
467 r = STAILQ_FIRST(&ndis_free);
469 lwkt_reltoken(&tokref);
472 STAILQ_REMOVE_HEAD(&ndis_free, link);
476 STAILQ_INSERT_TAIL(q, r, link);
477 if (t == NDIS_TASKQUEUE)
478 s = ndis_tproc.np_state;
480 s = ndis_iproc.np_state;
481 lwkt_reltoken(&tokref);
484 * Post the job, but only if the thread is actually blocked
485 * on its own suspend call. If a driver queues up a job with
486 * NdisScheduleWorkItem() which happens to do a KeWaitForObject(),
487 * it may suspend there, and in that case we don't want to wake
488 * it up until KeWaitForObject() gets woken up on its own.
490 if (s == NDIS_PSTATE_SLEEPING)
497 ndis_thsuspend(td, timo)
503 error = tsleep(td, 0, "ndissp", timo);
514 __stdcall static void
515 ndis_sendrsrcavail_func(adapter)
521 __stdcall static void
522 ndis_status_func(adapter, status, sbuf, slen)
528 ndis_miniport_block *block;
531 if (block->nmb_ifp->if_flags & IFF_DEBUG)
532 device_printf (block->nmb_dev, "status: %x\n", status);
536 __stdcall static void
537 ndis_statusdone_func(adapter)
540 ndis_miniport_block *block;
543 if (block->nmb_ifp->if_flags & IFF_DEBUG)
544 device_printf (block->nmb_dev, "status complete\n");
548 __stdcall static void
549 ndis_setdone_func(adapter, status)
553 ndis_miniport_block *block;
556 block->nmb_setstat = status;
557 wakeup(&block->nmb_wkupdpctimer);
561 __stdcall static void
562 ndis_getdone_func(adapter, status)
566 ndis_miniport_block *block;
569 block->nmb_getstat = status;
570 wakeup(&block->nmb_wkupdpctimer);
574 __stdcall static void
575 ndis_resetdone_func(adapter, status, addressingreset)
578 uint8_t addressingreset;
580 ndis_miniport_block *block;
583 if (block->nmb_ifp->if_flags & IFF_DEBUG)
584 device_printf (block->nmb_dev, "reset done...\n");
585 wakeup(block->nmb_ifp);
590 ndis_create_sysctls(arg)
593 struct ndis_softc *sc;
601 vals = sc->ndis_regvals;
603 TAILQ_INIT(&sc->ndis_cfglist_head);
605 #if __FreeBSD_version < 502113
606 /* Create the sysctl tree. */
608 sc->ndis_tree = SYSCTL_ADD_NODE(&sc->ndis_ctx,
609 SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
610 device_get_nameunit(sc->ndis_dev), CTLFLAG_RD, 0,
611 device_get_desc(sc->ndis_dev));
614 /* Add the driver-specific registry keys. */
616 vals = sc->ndis_regvals;
618 if (vals->nc_cfgkey == NULL)
620 if (vals->nc_idx != sc->ndis_devidx) {
625 SYSCTL_ADD_STRING(&sc->ndis_ctx,
626 SYSCTL_CHILDREN(sc->ndis_tree),
627 OID_AUTO, vals->nc_cfgkey,
628 CTLFLAG_RW, vals->nc_val,
629 sizeof(vals->nc_val),
632 SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev),
633 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)),
634 OID_AUTO, vals->nc_cfgkey,
635 CTLFLAG_RW, vals->nc_val,
636 sizeof(vals->nc_val),
642 /* Now add a couple of builtin keys. */
645 * Environment can be either Windows (0) or WindowsNT (1).
646 * We qualify as the latter.
648 ndis_add_sysctl(sc, "Environment",
649 "Windows environment", "1", CTLFLAG_RD);
651 /* NDIS version should be 5.1. */
652 ndis_add_sysctl(sc, "NdisVersion",
653 "NDIS API Version", "0x00050001", CTLFLAG_RD);
655 /* Bus type (PCI, PCMCIA, etc...) */
656 sprintf(buf, "%d", (int)sc->ndis_iftype);
657 ndis_add_sysctl(sc, "BusType", "Bus Type", buf, CTLFLAG_RD);
659 if (sc->ndis_res_io != NULL) {
660 sprintf(buf, "0x%lx", rman_get_start(sc->ndis_res_io));
661 ndis_add_sysctl(sc, "IOBaseAddress",
662 "Base I/O Address", buf, CTLFLAG_RD);
665 if (sc->ndis_irq != NULL) {
666 sprintf(buf, "%lu", rman_get_start(sc->ndis_irq));
667 ndis_add_sysctl(sc, "InterruptNumber",
668 "Interrupt Number", buf, CTLFLAG_RD);
675 ndis_add_sysctl(arg, key, desc, val, flag)
682 struct ndis_softc *sc;
683 struct ndis_cfglist *cfg;
688 cfg = malloc(sizeof(struct ndis_cfglist), M_DEVBUF, M_WAITOK|M_ZERO);
689 cfg->ndis_cfg.nc_cfgkey = strdup(key, M_DEVBUF);
691 snprintf(descstr, sizeof(descstr), "%s (dynamic)", key);
692 cfg->ndis_cfg.nc_cfgdesc = strdup(descstr, M_DEVBUF);
694 cfg->ndis_cfg.nc_cfgdesc = strdup(desc, M_DEVBUF);
695 strcpy(cfg->ndis_cfg.nc_val, val);
697 TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link);
700 SYSCTL_ADD_STRING(&sc->ndis_ctx, SYSCTL_CHILDREN(sc->ndis_tree),
701 OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag,
702 cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val),
703 cfg->ndis_cfg.nc_cfgdesc);
705 SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev),
706 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)),
707 OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag,
708 cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val),
709 cfg->ndis_cfg.nc_cfgdesc);
716 ndis_flush_sysctls(arg)
719 struct ndis_softc *sc;
720 struct ndis_cfglist *cfg;
724 while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) {
725 cfg = TAILQ_FIRST(&sc->ndis_cfglist_head);
726 TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link);
727 free(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF);
728 free(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF);
739 struct ndis_softc *sc;
740 ndis_return_handler returnfunc;
747 adapter = sc->ndis_block.nmb_miniportadapterctx;
752 returnfunc = sc->ndis_chars.nmc_return_packet_func;
753 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
754 returnfunc(adapter, p);
755 FASTCALL1(hal_lower_irql, irql);
761 ndis_extref_packet(void *arg)
763 ndis_packet *p = arg;
769 ndis_extfree_packet(void *arg)
771 ndis_packet *p = arg;
776 /* Decrement refcount. */
779 /* Release packet when refcount hits zero, otherwise return. */
783 ndis_sched(ndis_return, p, NDIS_SWI);
789 ndis_return_packet(struct ndis_softc *sc, ndis_packet *p)
791 ndis_extfree_packet(p);
805 free(b0, M_NDIS_BUFFER);
819 ndis_free_bufs(p->np_private.npp_head);
820 free(p, M_NDIS_PACKET);
826 ndis_convert_res(arg)
829 struct ndis_softc *sc;
830 ndis_resource_list *rl = NULL;
831 cm_partial_resource_desc *prd = NULL;
832 ndis_miniport_block *block;
834 struct resource_list *brl;
835 struct resource_list brl_rev;
836 struct resource_list_entry *brle, *n;
840 block = &sc->ndis_block;
843 SLIST_INIT(&brl_rev);
845 rl = malloc(sizeof(ndis_resource_list) +
846 (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)),
847 M_DEVBUF, M_WAITOK|M_NULLOK|M_ZERO);
852 rl->cprl_version = 5;
853 rl->cprl_version = 1;
854 rl->cprl_count = sc->ndis_rescnt;
855 prd = rl->cprl_partial_descs;
857 brl = BUS_GET_RESOURCE_LIST(dev, dev);
862 * We have a small problem. Some PCI devices have
863 * multiple I/O ranges. Windows orders them starting
864 * from lowest numbered BAR to highest. We discover
865 * them in that order too, but insert them into a singly
866 * linked list head first, which means when time comes
867 * to traverse the list, we enumerate them in reverse
868 * order. This screws up some drivers which expect the
869 * BARs to be in ascending order so that they can choose
870 * the "first" one as their register space. Unfortunately,
871 * in order to fix this, we have to create our own
872 * temporary list with the entries in reverse order.
874 SLIST_FOREACH(brle, brl, link) {
875 n = malloc(sizeof(struct resource_list_entry),
876 M_TEMP, M_WAITOK|M_NULLOK);
881 bcopy((char *)brle, (char *)n,
882 sizeof(struct resource_list_entry));
883 SLIST_INSERT_HEAD(&brl_rev, n, link);
886 SLIST_FOREACH(brle, &brl_rev, link) {
887 switch (brle->type) {
889 prd->cprd_type = CmResourceTypePort;
890 prd->cprd_flags = CM_RESOURCE_PORT_IO;
891 prd->cprd_sharedisp =
892 CmResourceShareDeviceExclusive;
893 prd->u.cprd_port.cprd_start.np_quad =
895 prd->u.cprd_port.cprd_len = brle->count;
898 prd->cprd_type = CmResourceTypeMemory;
900 CM_RESOURCE_MEMORY_READ_WRITE;
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 = CmResourceTypeInterrupt;
910 prd->cprd_sharedisp =
911 CmResourceShareDeviceExclusive;
912 prd->u.cprd_intr.cprd_level = brle->start;
913 prd->u.cprd_intr.cprd_vector = brle->start;
914 prd->u.cprd_intr.cprd_affinity = 0;
923 block->nmb_rlist = rl;
927 while (!SLIST_EMPTY(&brl_rev)) {
928 n = SLIST_FIRST(&brl_rev);
929 SLIST_REMOVE_HEAD(&brl_rev, link);
937 * Map an NDIS packet to an mbuf list. When an NDIS driver receives a
938 * packet, it will hand it to us in the form of an ndis_packet,
939 * which we need to convert to an mbuf that is then handed off
940 * to the stack. Note: we configure the mbuf list so that it uses
941 * the memory regions specified by the ndis_buffer structures in
942 * the ndis_packet as external storage. In most cases, this will
943 * point to a memory region allocated by the driver (either by
944 * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect
945 * the driver to handle free()ing this region for is, so we set up
946 * a dummy no-op free handler for it.
954 struct mbuf *m, *prev = NULL;
956 ndis_packet_private *priv;
959 if (p == NULL || m0 == NULL)
962 priv = &p->np_private;
963 buf = priv->npp_head;
966 for (buf = priv->npp_head; buf != NULL; buf = buf->nb_next) {
967 if (buf == priv->npp_head)
968 MGETHDR(m, MB_DONTWAIT, MT_HEADER);
970 MGET(m, MB_DONTWAIT, MT_DATA);
976 m->m_len = buf->nb_bytecount;
977 m->m_data = MDL_VA(buf);
978 m->m_ext.ext_free = ndis_extfree_packet;
979 m->m_ext.ext_ref = ndis_extref_packet;
980 m->m_ext.ext_arg = p;
981 m->m_ext.ext_buf = m->m_data;
982 m->m_ext.ext_size = m->m_len;
985 MEXTADD(m, m->m_data, m->m_len, ndis_free_packet,
990 if (m->m_flags & M_PKTHDR)
997 (*m0)->m_pkthdr.len = totlen;
1003 * Create an mbuf chain from an NDIS packet chain.
1004 * This is used mainly when transmitting packets, where we need
1005 * to turn an mbuf off an interface's send queue and transform it
1006 * into an NDIS packet which will be fed into the NDIS driver's
1009 * NDIS packets consist of two parts: an ndis_packet structure,
1010 * which is vaguely analagous to the pkthdr portion of an mbuf,
1011 * and one or more ndis_buffer structures, which define the
1012 * actual memory segments in which the packet data resides.
1013 * We need to allocate one ndis_buffer for each mbuf in a chain,
1014 * plus one ndis_packet as the header.
1023 ndis_buffer *buf = NULL, *prev = NULL;
1024 ndis_packet_private *priv;
1026 if (p == NULL || m0 == NULL)
1029 /* If caller didn't supply a packet, make one. */
1031 *p = malloc(sizeof(ndis_packet), M_NDIS_PACKET, M_NOWAIT|M_ZERO);
1036 priv = &(*p)->np_private;
1037 priv->npp_totlen = m0->m_pkthdr.len;
1038 priv->npp_packetooboffset = offsetof(ndis_packet, np_oob);
1039 priv->npp_ndispktflags = NDIS_PACKET_ALLOCATED_BY_NDIS;
1041 for (m = m0; m != NULL; m = m->m_next) {
1044 buf = malloc(sizeof(ndis_buffer), M_NDIS_BUFFER, M_NOWAIT|M_ZERO);
1046 ndis_free_packet(*p);
1051 MDL_INIT(buf, m->m_data, m->m_len);
1052 if (priv->npp_head == NULL)
1053 priv->npp_head = buf;
1055 prev->nb_next = buf;
1059 priv->npp_tail = buf;
1060 priv->npp_totlen = m0->m_pkthdr.len;
1066 ndis_get_supported_oids(arg, oids, oidcnt)
1074 if (arg == NULL || oids == NULL || oidcnt == NULL)
1077 ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len);
1079 o = malloc(len, M_DEVBUF, M_WAITOK);
1081 rval = ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, o, &len);
1095 ndis_set_info(arg, oid, buf, buflen)
1101 struct ndis_softc *sc;
1103 ndis_handle adapter;
1104 ndis_setinfo_handler setfunc;
1105 uint32_t byteswritten = 0, bytesneeded = 0;
1112 setfunc = sc->ndis_chars.nmc_setinfo_func;
1113 adapter = sc->ndis_block.nmb_miniportadapterctx;
1116 if (adapter == NULL || setfunc == NULL)
1119 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1120 rval = setfunc(adapter, oid, buf, *buflen,
1121 &byteswritten, &bytesneeded);
1122 FASTCALL1(hal_lower_irql, irql);
1124 if (rval == NDIS_STATUS_PENDING) {
1125 error = tsleep(&sc->ndis_block.nmb_wkupdpctimer,
1126 0, "ndisset", 5 * hz);
1127 rval = sc->ndis_block.nmb_setstat;
1131 *buflen = byteswritten;
1133 *buflen = bytesneeded;
1135 if (rval == NDIS_STATUS_INVALID_LENGTH)
1138 if (rval == NDIS_STATUS_INVALID_OID)
1141 if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1142 rval == NDIS_STATUS_NOT_ACCEPTED)
1145 if (rval != NDIS_STATUS_SUCCESS)
1151 typedef __stdcall void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status);
1154 ndis_send_packets(arg, packets, cnt)
1156 ndis_packet **packets;
1159 struct ndis_softc *sc;
1160 ndis_handle adapter;
1161 ndis_sendmulti_handler sendfunc;
1162 ndis_senddone_func senddonefunc;
1168 adapter = sc->ndis_block.nmb_miniportadapterctx;
1169 if (adapter == NULL)
1171 sendfunc = sc->ndis_chars.nmc_sendmulti_func;
1172 senddonefunc = sc->ndis_block.nmb_senddone_func;
1173 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1174 sendfunc(adapter, packets, cnt);
1175 FASTCALL1(hal_lower_irql, irql);
1177 for (i = 0; i < cnt; i++) {
1180 * Either the driver already handed the packet to
1181 * ndis_txeof() due to a failure, or it wants to keep
1182 * it and release it asynchronously later. Skip to the
1185 if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING)
1187 senddonefunc(&sc->ndis_block, p, p->np_oob.npo_status);
1194 ndis_send_packet(arg, packet)
1196 ndis_packet *packet;
1198 struct ndis_softc *sc;
1199 ndis_handle adapter;
1201 ndis_sendsingle_handler sendfunc;
1202 ndis_senddone_func senddonefunc;
1206 adapter = sc->ndis_block.nmb_miniportadapterctx;
1207 if (adapter == NULL)
1209 sendfunc = sc->ndis_chars.nmc_sendsingle_func;
1210 senddonefunc = sc->ndis_block.nmb_senddone_func;
1212 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1213 status = sendfunc(adapter, packet, packet->np_private.npp_flags);
1214 FASTCALL1(hal_lower_irql, irql);
1216 if (status == NDIS_STATUS_PENDING)
1219 senddonefunc(&sc->ndis_block, packet, status);
1228 struct ndis_softc *sc;
1233 sc->ndis_tmaps = malloc(sizeof(bus_dmamap_t) * sc->ndis_maxpkts,
1234 M_DEVBUF, M_WAITOK|M_ZERO);
1236 for (i = 0; i < sc->ndis_maxpkts; i++) {
1237 error = bus_dmamap_create(sc->ndis_ttag, 0,
1238 &sc->ndis_tmaps[i]);
1240 free(sc->ndis_tmaps, M_DEVBUF);
1249 ndis_destroy_dma(arg)
1252 struct ndis_softc *sc;
1254 ndis_packet *p = NULL;
1259 for (i = 0; i < sc->ndis_maxpkts; i++) {
1260 if (sc->ndis_txarray[i] != NULL) {
1261 p = sc->ndis_txarray[i];
1262 m = (struct mbuf *)p->np_rsvd[1];
1265 ndis_free_packet(sc->ndis_txarray[i]);
1267 bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]);
1270 free(sc->ndis_tmaps, M_DEVBUF);
1271 bus_dma_tag_destroy(sc->ndis_ttag);
1280 struct ndis_softc *sc;
1281 ndis_handle adapter;
1282 ndis_reset_handler resetfunc;
1283 uint8_t addressing_reset;
1290 ifp = &sc->arpcom.ac_if;
1292 adapter = sc->ndis_block.nmb_miniportadapterctx;
1293 resetfunc = sc->ndis_chars.nmc_reset_func;
1295 if (adapter == NULL || resetfunc == NULL)
1298 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1299 rval = resetfunc(&addressing_reset, adapter);
1300 FASTCALL1(hal_lower_irql, irql);
1302 if (rval == NDIS_STATUS_PENDING) {
1303 tsleep(sc, 0, "ndisrst", 0);
1313 struct ndis_softc *sc;
1314 ndis_handle adapter;
1315 ndis_halt_handler haltfunc;
1320 ifp = &sc->arpcom.ac_if;
1323 adapter = sc->ndis_block.nmb_miniportadapterctx;
1324 if (adapter == NULL) {
1330 * The adapter context is only valid after the init
1331 * handler has been called, and is invalid once the
1332 * halt handler has been called.
1335 haltfunc = sc->ndis_chars.nmc_halt_func;
1341 sc->ndis_block.nmb_miniportadapterctx = NULL;
1348 ndis_shutdown_nic(arg)
1351 struct ndis_softc *sc;
1352 ndis_handle adapter;
1353 ndis_shutdown_handler shutdownfunc;
1358 adapter = sc->ndis_block.nmb_miniportadapterctx;
1359 shutdownfunc = sc->ndis_chars.nmc_shutdown_handler;
1361 if (adapter == NULL || shutdownfunc == NULL)
1364 if (sc->ndis_chars.nmc_rsvd0 == NULL)
1365 shutdownfunc(adapter);
1367 shutdownfunc(sc->ndis_chars.nmc_rsvd0);
1369 ndis_shrink_thrqueue(8);
1370 TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link);
1379 struct ndis_softc *sc;
1380 ndis_miniport_block *block;
1381 ndis_init_handler initfunc;
1382 ndis_status status, openstatus = 0;
1383 ndis_medium mediumarray[NdisMediumMax];
1384 uint32_t chosenmedium, i;
1392 block = &sc->ndis_block;
1393 initfunc = sc->ndis_chars.nmc_init_func;
1396 TAILQ_INIT(&block->nmb_timerlist);
1398 for (i = 0; i < NdisMediumMax; i++)
1401 status = initfunc(&openstatus, &chosenmedium,
1402 mediumarray, NdisMediumMax, block, block);
1405 * If the init fails, blow away the other exported routines
1406 * we obtained from the driver so we can't call them later.
1407 * If the init failed, none of these will work.
1409 if (status != NDIS_STATUS_SUCCESS) {
1411 sc->ndis_block.nmb_miniportadapterctx = NULL;
1420 ndis_enable_intr(arg)
1423 struct ndis_softc *sc;
1424 ndis_handle adapter;
1425 ndis_enable_interrupts_handler intrenbfunc;
1428 adapter = sc->ndis_block.nmb_miniportadapterctx;
1429 intrenbfunc = sc->ndis_chars.nmc_enable_interrupts_func;
1430 if (adapter == NULL || intrenbfunc == NULL)
1432 intrenbfunc(adapter);
1438 ndis_disable_intr(arg)
1441 struct ndis_softc *sc;
1442 ndis_handle adapter;
1443 ndis_disable_interrupts_handler intrdisfunc;
1448 adapter = sc->ndis_block.nmb_miniportadapterctx;
1449 intrdisfunc = sc->ndis_chars.nmc_disable_interrupts_func;
1451 if (adapter == NULL || intrdisfunc == NULL)
1453 intrdisfunc(adapter);
1459 ndis_isr(arg, ourintr, callhandler)
1464 struct ndis_softc *sc;
1465 ndis_handle adapter;
1466 ndis_isr_handler isrfunc;
1467 uint8_t accepted, queue;
1469 if (arg == NULL || ourintr == NULL || callhandler == NULL)
1473 adapter = sc->ndis_block.nmb_miniportadapterctx;
1474 isrfunc = sc->ndis_chars.nmc_isr_func;
1475 if (adapter == NULL || isrfunc == NULL)
1478 isrfunc(&accepted, &queue, adapter);
1479 *ourintr = accepted;
1480 *callhandler = queue;
1489 struct ndis_softc *sc;
1490 ndis_handle adapter;
1491 ndis_interrupt_handler intrfunc;
1499 adapter = sc->ndis_block.nmb_miniportadapterctx;
1500 intrfunc = sc->ndis_chars.nmc_interrupt_func;
1502 if (adapter == NULL || intrfunc == NULL)
1511 ndis_get_info(arg, oid, buf, buflen)
1517 struct ndis_softc *sc;
1519 ndis_handle adapter;
1520 ndis_queryinfo_handler queryfunc;
1521 uint32_t byteswritten = 0, bytesneeded = 0;
1528 queryfunc = sc->ndis_chars.nmc_queryinfo_func;
1529 adapter = sc->ndis_block.nmb_miniportadapterctx;
1532 if (adapter == NULL || queryfunc == NULL)
1535 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1536 rval = queryfunc(adapter, oid, buf, *buflen,
1537 &byteswritten, &bytesneeded);
1538 FASTCALL1(hal_lower_irql, irql);
1540 /* Wait for requests that block. */
1542 if (rval == NDIS_STATUS_PENDING) {
1543 error = tsleep(&sc->ndis_block.nmb_wkupdpctimer,
1544 0, "ndisget", 5 * hz);
1545 rval = sc->ndis_block.nmb_getstat;
1549 *buflen = byteswritten;
1551 *buflen = bytesneeded;
1553 if (rval == NDIS_STATUS_INVALID_LENGTH ||
1554 rval == NDIS_STATUS_BUFFER_TOO_SHORT)
1557 if (rval == NDIS_STATUS_INVALID_OID)
1560 if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1561 rval == NDIS_STATUS_NOT_ACCEPTED)
1564 if (rval != NDIS_STATUS_SUCCESS)
1571 ndis_unload_driver(arg)
1574 struct ndis_softc *sc;
1578 free(sc->ndis_block.nmb_rlist, M_DEVBUF);
1580 ndis_flush_sysctls(sc);
1582 ndis_shrink_thrqueue(8);
1583 TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link);
1588 #define NDIS_LOADED htonl(0x42534F44)
1591 ndis_load_driver(img, arg)
1596 image_optional_header opt_hdr;
1597 image_import_descriptor imp_desc;
1598 ndis_unicode_string dummystr;
1599 ndis_miniport_block *block;
1603 struct ndis_softc *sc;
1608 * Only perform the relocation/linking phase once
1609 * since the binary image may be shared among multiple
1613 ptr = (uint32_t *)(img + 8);
1614 if (*ptr != NDIS_LOADED) {
1615 /* Perform text relocation */
1616 if (pe_relocate(img))
1619 /* Dynamically link the NDIS.SYS routines -- required. */
1620 if (pe_patch_imports(img, "NDIS", ndis_functbl))
1623 /* Dynamically link the HAL.dll routines -- also required. */
1624 if (pe_patch_imports(img, "HAL", hal_functbl))
1627 /* Dynamically link ntoskrnl.exe -- optional. */
1628 if (pe_get_import_descriptor(img,
1629 &imp_desc, "ntoskrnl") == 0) {
1630 if (pe_patch_imports(img,
1631 "ntoskrnl", ntoskrnl_functbl))
1637 /* Locate the driver entry point */
1638 pe_get_optional_header(img, &opt_hdr);
1639 entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
1641 dummystr.nus_len = strlen(NDIS_DUMMY_PATH) * 2;
1642 dummystr.nus_maxlen = strlen(NDIS_DUMMY_PATH) * 2;
1643 dummystr.nus_buf = NULL;
1644 ndis_ascii_to_unicode(NDIS_DUMMY_PATH, &dummystr.nus_buf);
1647 * Now that we have the miniport driver characteristics,
1648 * create an NDIS block and call the init handler.
1649 * This will cause the driver to try to probe for
1653 block = &sc->ndis_block;
1655 ptr = (uint32_t *)block;
1656 for (idx = 0; idx < sizeof(ndis_miniport_block) / 4; idx++) {
1657 *ptr = idx | 0xdead0000;
1661 block->nmb_signature = (void *)0xcafebabe;
1662 block->nmb_setdone_func = ndis_setdone_func;
1663 block->nmb_querydone_func = ndis_getdone_func;
1664 block->nmb_status_func = ndis_status_func;
1665 block->nmb_statusdone_func = ndis_statusdone_func;
1666 block->nmb_resetdone_func = ndis_resetdone_func;
1667 block->nmb_sendrsrc_func = ndis_sendrsrcavail_func;
1669 block->nmb_ifp = &sc->arpcom.ac_if;
1670 block->nmb_dev = sc->ndis_dev;
1671 block->nmb_img = img;
1672 block->nmb_devobj.do_rsvd = block;
1675 * Now call the DriverEntry() routine. This will cause
1676 * a callout to the NdisInitializeWrapper() and
1677 * NdisMRegisterMiniport() routines.
1679 status = entry(&block->nmb_devobj, &dummystr);
1681 free (dummystr.nus_buf, M_DEVBUF);
1683 if (status != NDIS_STATUS_SUCCESS)
1686 ndis_enlarge_thrqueue(8);
1688 TAILQ_INSERT_TAIL(&ndis_devhead, block, link);