1/*	$NetBSD$	*/
2
3/*-
4 * Copyright (c) 2003
5 *	Bill Paul <wpaul@windriver.com>.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by Bill Paul.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36#ifdef __FreeBSD__
37__FBSDID("$FreeBSD: src/sys/compat/ndis/kern_ndis.c,v 1.60.2.5 2005/04/01 17:14:20 wpaul Exp $");
38#endif
39#ifdef __NetBSD__
40__KERNEL_RCSID(0, "$NetBSD: kern_ndis.c,v 1.21 2009/05/11 21:31:29 cegger Exp $");
41#endif
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/unistd.h>
46#include <sys/types.h>
47#include <sys/errno.h>
48#include <sys/callout.h>
49#include <sys/socket.h>
50#include <sys/queue.h>
51#include <sys/sysctl.h>
52#include <sys/proc.h>
53#include <sys/malloc.h>
54#include <sys/lock.h>
55#include <sys/mutex.h>
56#include <sys/conf.h>
57
58#include <sys/kernel.h>
59#include <sys/module.h>
60#include <sys/mbuf.h>
61#include <sys/kthread.h>
62#include <sys/bus.h>
63#ifdef __FreeBSD__
64#include <machine/resource.h>
65#include <sys/rman.h>
66#endif
67
68#ifdef __NetBSD__
69#include <dev/pci/pcivar.h>
70#include <dev/pci/pcireg.h>
71#endif
72
73#include <net/if.h>
74#include <net/if_arp.h>
75#ifdef __FreeBSD__
76#include <net/ethernet.h>
77#else
78#include <net/if_ether.h>
79#endif
80#include <net/if_dl.h>
81#include <net/if_media.h>
82
83#include <net80211/ieee80211_var.h>
84#include <net80211/ieee80211_ioctl.h>
85
86#include <compat/ndis/pe_var.h>
87#include <compat/ndis/resource_var.h>
88#include <compat/ndis/ntoskrnl_var.h>
89#include <compat/ndis/ndis_var.h>
90#include <compat/ndis/hal_var.h>
91#include <compat/ndis/cfg_var.h>
92#include <compat/ndis/usbd_var.h>
93#include <dev/if_ndis/if_ndisvar.h>
94
95MODULE(MODULE_CLASS_MISC, ndis, NULL);
96
97#define NDIS_DUMMY_PATH "\\\\some\\bogus\\path"
98
99__stdcall static void ndis_status_func(ndis_handle, ndis_status,
100	void *, uint32_t);
101__stdcall static void ndis_statusdone_func(ndis_handle);
102__stdcall static void ndis_setdone_func(ndis_handle, ndis_status);
103__stdcall static void ndis_getdone_func(ndis_handle, ndis_status);
104__stdcall static void ndis_resetdone_func(ndis_handle, ndis_status, uint8_t);
105__stdcall static void ndis_sendrsrcavail_func(ndis_handle);
106__stdcall static void ndis_intrhand(kdpc *, device_object *,
107	irp *, struct ndis_softc *);
108
109static image_patch_table kernndis_functbl[] = {
110	IMPORT_FUNC(ndis_status_func),
111	IMPORT_FUNC(ndis_statusdone_func),
112	IMPORT_FUNC(ndis_setdone_func),
113	IMPORT_FUNC(ndis_getdone_func),
114	IMPORT_FUNC(ndis_resetdone_func),
115	IMPORT_FUNC(ndis_sendrsrcavail_func),
116	IMPORT_FUNC(ndis_intrhand),
117
118	{ NULL, NULL, NULL }
119};
120
121struct nd_head ndis_devhead;
122
123struct ndis_req {
124	void			(*nr_func)(void *);
125	void			*nr_arg;
126	int			nr_exit;
127	STAILQ_ENTRY(ndis_req)	link;
128	/* just for debugging */
129	int 			area;
130};
131
132struct ndisproc {
133	struct ndisqhead	*np_q;
134	struct proc		*np_p;
135	int			np_state;
136	uint8_t			np_stack[PAGE_SIZE*NDIS_KSTACK_PAGES];
137};
138
139static void ndis_return(void *);
140static int ndis_create_kthreads(void);
141static void ndis_destroy_kthreads(void);
142static void ndis_stop_thread(int);
143static int ndis_enlarge_thrqueue(int);
144static int ndis_shrink_thrqueue(int);
145//#ifdef NDIS_LKM
146static void ndis_runq(void *);
147//#endif
148
149#ifdef __FreeBSD__
150static struct mtx ndis_thr_mtx;
151#else /* __NetBSD__ */
152static kmutex_t ndis_thr_mtx;
153#endif
154
155static struct mtx ndis_req_mtx;
156static STAILQ_HEAD(ndisqhead, ndis_req) ndis_ttodo;
157static struct ndisqhead ndis_itodo;
158static struct ndisqhead ndis_free;
159static int ndis_jobs = 32;
160
161static struct ndisproc ndis_tproc;
162static struct ndisproc ndis_iproc;
163
164/*
165 * This allows us to export our symbols to other modules.
166 * Note that we call ourselves 'ndisapi' to avoid a namespace
167 * collision with if_ndis.ko, which internally calls itself
168 * 'ndis.'
169 */
170
171#ifdef __FreeBSD__
172static int
173ndis_modevent(module_t mod, int cmd, void *arg)
174{
175	int			error = 0;
176	image_patch_table	*patch;
177
178	switch (cmd) {
179	case MOD_LOAD:
180		/* Initialize subsystems */
181		windrv_libinit();
182		hal_libinit();
183		ndis_libinit();
184		ntoskrnl_libinit();
185#ifdef usbimplemented
186		usbd_libinit();
187#endif
188
189		patch = kernndis_functbl;
190		while (patch->ipt_func != NULL) {
191			windrv_wrap((funcptr)patch->ipt_func,
192			    (funcptr *)&patch->ipt_wrap);
193			patch++;
194		}
195
196		ndis_create_kthreads();
197
198		TAILQ_INIT(&ndis_devhead);
199
200		break;
201	case MOD_SHUTDOWN:
202		/* stop kthreads */
203		ndis_destroy_kthreads();
204		if (TAILQ_FIRST(&ndis_devhead) == NULL) {
205			/* Shut down subsystems */
206			hal_libfini();
207			ndis_libfini();
208			ntoskrnl_libfini();
209#ifdef usbimplemented
210			usbd_libfini();
211#endif
212			windrv_libfini();
213
214			patch = kernndis_functbl;
215			while (patch->ipt_func != NULL) {
216				windrv_unwrap(patch->ipt_wrap);
217				patch++;
218			}
219		}
220		break;
221	case MOD_UNLOAD:
222		/* stop kthreads */
223		ndis_destroy_kthreads();
224
225		/* Shut down subsystems */
226		hal_libfini();
227		ndis_libfini();
228		ntoskrnl_libfini();
229		usbd_libfini();
230		windrv_libfini();
231
232		patch = kernndis_functbl;
233		while (patch->ipt_func != NULL) {
234			windrv_unwrap(patch->ipt_wrap);
235			patch++;
236		}
237
238		break;
239	default:
240		error = EINVAL;
241		break;
242	}
243
244	return(error);
245}
246DEV_MODULE(ndisapi, ndis_modevent, NULL);
247MODULE_VERSION(ndisapi, 1);
248#endif
249
250static int
251ndis_modcmd(modcmd_t cmd, void *arg)
252{
253	int			error = 0;
254	image_patch_table	*patch;
255
256	switch (cmd) {
257	case MODULE_CMD_INIT:
258		/* Initialize subsystems */
259		windrv_libinit();
260		hal_libinit();
261		ndis_libinit();
262		ntoskrnl_libinit();
263#ifdef usbimplemented
264		usbd_libinit();
265#endif
266
267		patch = kernndis_functbl;
268		while (patch->ipt_func != NULL) {
269			windrv_wrap((funcptr)patch->ipt_func,
270			    (funcptr *)&patch->ipt_wrap);
271			patch++;
272		}
273
274		TAILQ_INIT(&ndis_devhead);
275
276		ndis_create_kthreads();
277		break;
278
279	case MODULE_CMD_FINI:
280		/* stop kthreads */
281		ndis_destroy_kthreads();
282
283		/* Shut down subsystems */
284		hal_libfini();
285		ndis_libfini();
286		ntoskrnl_libfini();
287#ifdef usbimplemented
288		usbd_libfini();
289#endif
290		windrv_libfini();
291
292		patch = kernndis_functbl;
293		while (patch->ipt_func != NULL) {
294			windrv_unwrap(patch->ipt_wrap);
295			patch++;
296		}
297
298		break;
299
300	default:
301		error = ENOTTY;
302		break;
303	}
304
305	return(error);
306}
307
308/*
309 * We create two kthreads for the NDIS subsystem. One of them is a task
310 * queue for performing various odd jobs. The other is an swi thread
311 * reserved exclusively for running interrupt handlers. The reason we
312 * have our own task queue is that there are some cases where we may
313 * need to sleep for a significant amount of time, and if we were to
314 * use one of the taskqueue threads, we might delay the processing
315 * of other pending tasks which might need to run right away. We have
316 * a separate swi thread because we don't want our interrupt handling
317 * to be delayed either.
318 *
319 * By default there are 32 jobs available to start, and another 8
320 * are added to the free list each time a new device is created.
321 */
322
323/* Just for testing this can be removed later */
324struct ndis_req *_ndis_taskqueue_req;
325struct ndis_req *_ndis_swi_req;
326int calling_in_swi = FALSE;
327int calling_in_tq  = FALSE;
328int num_swi		 = 0;
329int num_tq		 = 0;
330
331static void
332ndis_runq(void *arg)
333{
334	struct ndis_req		*r = NULL, *die = NULL;
335	struct ndisproc		*p;
336
337	p = arg;
338
339	for (;;) {
340		mtx_lock_spin(&ndis_thr_mtx);
341		ndis_thsuspend(p->np_p, &ndis_thr_mtx, 0);
342
343		/* Look for any jobs on the work queue. */
344		p->np_state = NDIS_PSTATE_RUNNING;
345		while (!STAILQ_EMPTY(p->np_q)) {
346			r = STAILQ_FIRST(p->np_q);
347			STAILQ_REMOVE_HEAD(p->np_q, link);
348
349			/* for debugging */
350
351			if(p == &ndis_tproc) {
352				num_tq++;
353				_ndis_taskqueue_req = r;
354				r->area = 1;
355			} else if(p == &ndis_iproc) {
356				num_swi++;
357				_ndis_swi_req = r;
358				r->area = 2;
359			}
360			mtx_unlock_spin(&ndis_thr_mtx);
361
362			/* Just for debugging */
363
364			if(p == &ndis_tproc) {
365				calling_in_tq = TRUE;
366			} else if(p == &ndis_iproc) {
367				calling_in_swi	   = TRUE;
368			}
369
370			/* Do the work. */
371			if (r->nr_func != NULL)
372				(*r->nr_func)(r->nr_arg);
373
374			/* Just for debugging */
375			if(p == &ndis_tproc) {
376				calling_in_tq = FALSE;
377			} else if(p == &ndis_iproc) {
378				calling_in_swi	   = FALSE;
379			}
380
381			mtx_lock_spin(&ndis_thr_mtx);
382
383			/* Zeroing out the ndis_req is just for debugging */
384			//memset(r, 0, sizeof(struct ndis_req));
385			STAILQ_INSERT_HEAD(&ndis_free, r, link);
386
387			/* Check for a shutdown request */
388			if (r->nr_exit == TRUE)
389				die = r;
390		}
391		p->np_state = NDIS_PSTATE_SLEEPING;
392
393		mtx_unlock_spin(&ndis_thr_mtx);
394
395		/* Bail if we were told to shut down. */
396
397		if (die != NULL)
398			break;
399	}
400
401	wakeup(die);
402
403	kthread_exit(0);
404	/* NOTREACHED */
405}
406
407/*static*/ int
408ndis_create_kthreads(void)
409{
410	struct ndis_req		*r;
411	int			i, error = 0;
412
413	printf("in ndis_create_kthreads\n");
414
415#ifdef __FreeBSD__
416	mtx_init(&ndis_thr_mtx, "NDIS thread lock", NULL, MTX_SPIN);
417#else /* __NetBSD__ */
418	mutex_init(&ndis_thr_mtx, MUTEX_DEFAULT, IPL_NET);
419#endif
420	mtx_init(&ndis_req_mtx, "NDIS request lock", MTX_NDIS_LOCK, MTX_DEF);
421
422	STAILQ_INIT(&ndis_ttodo);
423	STAILQ_INIT(&ndis_itodo);
424	STAILQ_INIT(&ndis_free);
425
426	for (i = 0; i < ndis_jobs; i++) {
427		r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK|M_ZERO);
428		if (r == NULL) {
429			error = ENOMEM;
430			break;
431		}
432		STAILQ_INSERT_HEAD(&ndis_free, r, link);
433	}
434
435	if (error == 0) {
436		ndis_tproc.np_q = &ndis_ttodo;
437		ndis_tproc.np_state = NDIS_PSTATE_SLEEPING;
438#ifdef __FreeBSD__
439		error = kthread_create(ndis_runq, &ndis_tproc,
440		    &ndis_tproc.np_p, RFHIGHPID,
441		    NDIS_KSTACK_PAGES, "ndis taskqueue");
442#else /* __NetBSD__ */
443		error = ndis_kthread_create(ndis_runq, &ndis_tproc,
444		    &ndis_tproc.np_p, ndis_tproc.np_stack, PAGE_SIZE*NDIS_KSTACK_PAGES, "ndis taskqueue");
445#endif
446	}
447
448	if (error == 0) {
449		ndis_iproc.np_q = &ndis_itodo;
450		ndis_iproc.np_state = NDIS_PSTATE_SLEEPING;
451#ifdef __FreeBSD__
452		error = kthread_create(ndis_runq, &ndis_iproc,
453		    &ndis_iproc.np_p, RFHIGHPID,
454		    NDIS_KSTACK_PAGES, "ndis swi");
455#else
456		error = ndis_kthread_create(ndis_runq, &ndis_iproc,
457		    &ndis_iproc.np_p, ndis_iproc.np_stack, PAGE_SIZE*NDIS_KSTACK_PAGES, "ndis swi");
458#endif
459	}
460
461	if (error) {
462		while ((r = STAILQ_FIRST(&ndis_free)) != NULL) {
463			STAILQ_REMOVE_HEAD(&ndis_free, link);
464			free(r, M_DEVBUF);
465		}
466		return(error);
467	}
468
469	return(0);
470}
471
472static void
473ndis_destroy_kthreads(void)
474{
475	struct ndis_req		*r;
476
477	/* Stop the threads. */
478
479	ndis_stop_thread(NDIS_TASKQUEUE);
480	ndis_stop_thread(NDIS_SWI);
481
482	/* Destroy request structures. */
483
484	while ((r = STAILQ_FIRST(&ndis_free)) != NULL) {
485		STAILQ_REMOVE_HEAD(&ndis_free, link);
486		free(r, M_DEVBUF);
487	}
488
489	mtx_destroy(&ndis_req_mtx);
490	mtx_destroy(&ndis_thr_mtx);
491
492	return;
493}
494
495static void
496ndis_stop_thread(int t)
497{
498	struct ndis_req		*r;
499	struct ndisqhead	*q;
500	struct proc		*p;
501
502	if (t == NDIS_TASKQUEUE) {
503		q = &ndis_ttodo;
504		p = ndis_tproc.np_p;
505	} else {
506		q = &ndis_itodo;
507		p = ndis_iproc.np_p;
508	}
509
510	/* Create and post a special 'exit' job. */
511
512	mtx_lock_spin(&ndis_thr_mtx);
513
514	r = STAILQ_FIRST(&ndis_free);
515	STAILQ_REMOVE_HEAD(&ndis_free, link);
516	r->nr_func = NULL;
517	r->nr_arg = NULL;
518	r->nr_exit = TRUE;
519	r->area	   = 3;
520	STAILQ_INSERT_TAIL(q, r, link);
521	mtx_unlock_spin(&ndis_thr_mtx);
522
523	ndis_thresume(p);
524
525	/* Wait for thread exit */
526	tsleep(r, PZERO | PCATCH, "ndisthexit", hz * 60);
527
528	/* Now empty the job list. */
529	mtx_lock_spin(&ndis_thr_mtx);
530	while ((r = STAILQ_FIRST(q)) != NULL) {
531		STAILQ_REMOVE_HEAD(q, link);
532		STAILQ_INSERT_HEAD(&ndis_free, r, link);
533	}
534	mtx_unlock_spin(&ndis_thr_mtx);
535}
536
537static int
538ndis_enlarge_thrqueue(int cnt)
539{
540	struct ndis_req		*r;
541	int			i;
542
543	for (i = 0; i < cnt; i++) {
544		r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK);
545		if (r == NULL)
546			return(ENOMEM);
547
548		mtx_lock_spin(&ndis_thr_mtx);
549		STAILQ_INSERT_HEAD(&ndis_free, r, link);
550		ndis_jobs++;
551		mtx_unlock_spin(&ndis_thr_mtx);
552	}
553
554	return(0);
555}
556
557static int
558ndis_shrink_thrqueue(int cnt)
559{
560	struct ndis_req		*r;
561	int			i;
562
563	for (i = 0; i < cnt; i++) {
564		mtx_lock_spin(&ndis_thr_mtx);
565		r = STAILQ_FIRST(&ndis_free);
566		if (r == NULL) {
567			mtx_unlock_spin(&ndis_thr_mtx);
568			return(ENOMEM);
569		}
570		STAILQ_REMOVE_HEAD(&ndis_free, link);
571		ndis_jobs--;
572		mtx_unlock_spin(&ndis_thr_mtx);
573		free(r, M_DEVBUF);
574	}
575
576	return(0);
577}
578
579int
580ndis_unsched(void (*func)(void *), void *arg, int t)
581{
582	struct ndis_req		*r;
583	struct ndisqhead	*q;
584	struct proc		*p;
585
586	if (t == NDIS_TASKQUEUE) {
587		q = &ndis_ttodo;
588		p = ndis_tproc.np_p;
589	} else {
590		q = &ndis_itodo;
591		p = ndis_iproc.np_p;
592	}
593
594	mtx_lock_spin(&ndis_thr_mtx);
595	STAILQ_FOREACH(r, q, link) {
596		if (r->nr_func == func && r->nr_arg == arg) {
597			r->area = 4;
598			STAILQ_REMOVE(q, r, ndis_req, link);
599			STAILQ_INSERT_HEAD(&ndis_free, r, link);
600			mtx_unlock_spin(&ndis_thr_mtx);
601			return(0);
602		}
603	}
604	mtx_unlock_spin(&ndis_thr_mtx);
605
606	return(ENOENT);
607}
608
609/* just for testing */
610struct ndis_req *ls_tq_req = NULL;
611struct ndis_req *ls_swi_req = NULL;
612
613int
614ndis_sched(void (*func)(void *), void *arg, int t)
615{
616	struct ndis_req		*r;
617	struct ndisqhead	*q;
618	struct proc		*p;
619	int			s;
620#ifdef __NetBSD__
621	/* just for debugging */
622	struct ndis_req		**ls;
623	//struct lwp		*l = curlwp;
624#endif
625
626	if (t == NDIS_TASKQUEUE) {
627		ls = &ls_tq_req;
628		q = &ndis_ttodo;
629		p = ndis_tproc.np_p;
630	} else {
631		ls = &ls_swi_req;
632		q = &ndis_itodo;
633		p = ndis_iproc.np_p;
634	}
635
636	mtx_lock_spin(&ndis_thr_mtx);
637
638	/*
639	 * Check to see if an instance of this job is already
640	 * pending. If so, don't bother queuing it again.
641	 */
642	STAILQ_FOREACH(r, q, link) {
643		if (r->nr_func == func && r->nr_arg == arg) {
644#ifdef __NetBSD__
645			if (t == NDIS_TASKQUEUE)
646				s = ndis_tproc.np_state;
647			else
648				s = ndis_iproc.np_state;
649#endif
650			mtx_unlock_spin(&ndis_thr_mtx);
651#ifdef __NetBSD__
652			/* The swi thread seemed to be going to sleep, and not waking up
653			 * again, so I thought I'd try this out...
654			 */
655			if (s == NDIS_PSTATE_SLEEPING)
656				ndis_thresume(p);
657#endif
658			return(0);
659		}
660	}
661	r = STAILQ_FIRST(&ndis_free);
662	if (r == NULL) {
663		mtx_unlock_spin(&ndis_thr_mtx);
664		return(EAGAIN);
665	}
666	STAILQ_REMOVE_HEAD(&ndis_free, link);
667#ifdef __NetBSD__
668	//memset(r, 0, sizeof(struct ndis_req));
669#endif
670	*ls = r;
671	r->nr_func = func;
672	r->nr_arg = arg;
673	r->nr_exit = FALSE;
674	r->area	   = 5;
675	STAILQ_INSERT_TAIL(q, r, link);
676	if (t == NDIS_TASKQUEUE) {
677		s = ndis_tproc.np_state;
678	} else {
679		s = ndis_iproc.np_state;
680	}
681	mtx_unlock_spin(&ndis_thr_mtx);
682
683	/*
684	 * Post the job, but only if the thread is actually blocked
685	 * on its own suspend call. If a driver queues up a job with
686	 * NdisScheduleWorkItem() which happens to do a KeWaitForObject(),
687	 * it may suspend there, and in that case we don't want to wake
688	 * it up until KeWaitForObject() gets woken up on its own.
689	 */
690	if (s == NDIS_PSTATE_SLEEPING) {
691		ndis_thresume(p);
692	}
693
694	return(0);
695}
696
697/* Try out writing my own version of ndis_sched() for NetBSD in which I just
698 * call the function instead of scheduling it.  I know this isn't
699 * what's supposed to be done, but I've been having a lot of problems
700 * with the SWI and taskqueue threads, and just thought I'd give this
701 * a try.
702 */
703
704 /* I don't think this will work, because it means that DPC's will be
705  * called from the bottom half of the kernel, so they won't be able
706  * to sleep using KeWaitForSingleObject.
707  */
708 /*
709 int
710ndis_sched(void (*func)(void *), void *arg, int t)
711{
712	if(func != NULL) {
713		(*func)(arg);
714	}
715
716	return 0;
717}
718*/
719
720int
721ndis_thsuspend(proc_t *p, kmutex_t *m, int timo)
722{
723
724	return mtsleep(&p->p_sigpend.sp_set, PZERO,  "ndissp", timo, m);
725}
726
727void
728ndis_thresume(struct proc *p)
729{
730
731	wakeup(&p->p_sigpend.sp_set);
732}
733
734__stdcall static void
735ndis_sendrsrcavail_func(ndis_handle adapter)
736{
737	return;
738}
739
740__stdcall static void
741ndis_status_func(ndis_handle adapter, ndis_status status, void *sbuf,
742    uint32_t slen)
743{
744	ndis_miniport_block	*block;
745	struct ndis_softc	*sc;
746	struct ifnet		*ifp;
747
748	block = adapter;
749#ifdef __FreeBSD__
750	sc = device_get_softc(block->nmb_physdeviceobj->do_devext);
751#else /* __NetBSD__ */
752	sc = (struct ndis_softc *)block->nmb_physdeviceobj->pdo_sc;
753#endif
754
755#ifdef __FreeBSD__
756	ifp = &sc->arpcom.ac_if;
757#else
758	ifp = &sc->arpcom.ec_if;
759#endif
760	if (ifp->if_flags & IFF_DEBUG)
761		printf("%s: status: %x\n",
762		       device_xname(sc->ndis_dev), status);
763	return;
764}
765
766__stdcall static void
767ndis_statusdone_func(ndis_handle adapter)
768{
769	ndis_miniport_block	*block;
770	struct ndis_softc	*sc;
771	struct ifnet		*ifp;
772
773	block = adapter;
774#ifdef __FreeBSD__
775	sc = device_get_softc(block->nmb_physdeviceobj->do_devext);
776#else /* __NetBSD__ */
777	sc = (struct ndis_softc *)block->nmb_physdeviceobj->pdo_sc;
778#endif
779
780#ifdef __FreeBSD__
781	ifp = &sc->arpcom.ac_if;
782#else
783	ifp = &sc->arpcom.ec_if;
784#endif
785	if (ifp->if_flags & IFF_DEBUG)
786		printf("%s: status complete\n",
787		       device_xname(sc->ndis_dev));
788	return;
789}
790
791__stdcall static void
792ndis_setdone_func(ndis_handle adapter, ndis_status status)
793{
794	ndis_miniport_block	*block;
795	block = adapter;
796
797	block->nmb_setstat = status;
798	wakeup(&block->nmb_setstat);
799	return;
800}
801
802__stdcall static void
803ndis_getdone_func(ndis_handle adapter, ndis_status status)
804{
805	ndis_miniport_block	*block;
806	block = adapter;
807
808	block->nmb_getstat = status;
809	wakeup(&block->nmb_getstat);
810	return;
811}
812
813__stdcall static void
814ndis_resetdone_func(ndis_handle adapter, ndis_status status,
815    uint8_t addressingreset)
816{
817	ndis_miniport_block	*block;
818	struct ndis_softc	*sc;
819	struct ifnet		*ifp;
820
821	block = adapter;
822#ifdef __FreeBSD__
823	sc = device_get_softc(block->nmb_physdeviceobj->do_devext);
824#else /* __NetBSD__ */
825	sc = (struct ndis_softc *)block->nmb_physdeviceobj->pdo_sc;
826#endif
827
828#ifdef __FreeBSD__
829	ifp = &sc->arpcom.ac_if;
830#else
831	ifp = &sc->arpcom.ec_if;
832#endif
833
834	if (ifp->if_flags & IFF_DEBUG)
835		printf("%s: reset done...\n",
836		       device_xname(sc->ndis_dev));
837	wakeup(sc);
838	return;
839}
840
841#ifdef __FreeBSD__
842/* FreeBSD version of ndis_create_sysctls() */
843int
844ndis_create_sysctls(void *arg)
845{
846	struct ndis_softc	*sc;
847	ndis_cfg		*vals;
848	char			buf[256];
849	struct sysctl_oid	*oidp;
850	struct sysctl_ctx_entry	*e;
851
852	if (arg == NULL)
853		return(EINVAL);
854
855	sc = arg;
856	vals = sc->ndis_regvals;
857
858	TAILQ_INIT(&sc->ndis_cfglist_head);
859
860#if __FreeBSD_version < 502113
861	/* Create the sysctl tree. */
862
863	sc->ndis_tree = SYSCTL_ADD_NODE(&sc->ndis_ctx,
864	    SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
865	    device_get_nameunit(sc->ndis_dev), CTLFLAG_RD, 0,
866	    device_get_desc(sc->ndis_dev));
867
868#endif
869	/* Add the driver-specific registry keys. */
870
871	vals = sc->ndis_regvals;
872	while(1) {
873		if (vals->nc_cfgkey == NULL)
874			break;
875		if (vals->nc_idx != sc->ndis_devidx) {
876			vals++;
877			continue;
878		}
879
880		/* See if we already have a sysctl with this name */
881
882		oidp = NULL;
883#if __FreeBSD_version < 502113
884		TAILQ_FOREACH(e, &sc->ndis_ctx, link) {
885#else
886		TAILQ_FOREACH(e, device_get_sysctl_ctx(sc->ndis_dev), link) {
887#endif
888                	oidp = e->entry;
889			if (ndis_strcasecmp(oidp->oid_name,
890			    vals->nc_cfgkey) == 0)
891				break;
892			oidp = NULL;
893		}
894
895		if (oidp != NULL) {
896			vals++;
897			continue;
898		}
899
900#if __FreeBSD_version < 502113
901		SYSCTL_ADD_STRING(&sc->ndis_ctx,
902		    SYSCTL_CHILDREN(sc->ndis_tree),
903#else
904		SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev),
905		    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)),
906#endif
907		    OID_AUTO, vals->nc_cfgkey,
908		    CTLFLAG_RW, vals->nc_val,
909		    sizeof(vals->nc_val),
910		    vals->nc_cfgdesc);
911		vals++;
912	}
913
914	/* Now add a couple of builtin keys. */
915
916	/*
917	 * Environment can be either Windows (0) or WindowsNT (1).
918	 * We qualify as the latter.
919	 */
920	ndis_add_sysctl(sc, "Environment",
921	    "Windows environment", "1", CTLFLAG_RD);
922
923	/* NDIS version should be 5.1. */
924	ndis_add_sysctl(sc, "NdisVersion",
925	    "NDIS API Version", "0x00050001", CTLFLAG_RD);
926
927	/* Bus type (PCI, PCMCIA, etc...) */
928	sprintf(buf, "%d", (int)sc->ndis_iftype);
929	ndis_add_sysctl(sc, "BusType", "Bus Type", buf, CTLFLAG_RD);
930
931	if (sc->ndis_res_io != NULL) {
932		sprintf(buf, "0x%lx", rman_get_start(sc->ndis_res_io));
933		ndis_add_sysctl(sc, "IOBaseAddress",
934		    "Base I/O Address", buf, CTLFLAG_RD);
935	}
936
937	if (sc->ndis_irq != NULL) {
938		sprintf(buf, "%lu", rman_get_start(sc->ndis_irq));
939		ndis_add_sysctl(sc, "InterruptNumber",
940		    "Interrupt Number", buf, CTLFLAG_RD);
941	}
942
943	return(0);
944}
945#endif /* __FreeBSD__ */
946
947#ifdef __NetBSD__
948/* NetBSD version of ndis_create_sysctls() */
949int
950ndis_create_sysctls(void *arg)
951{
952	struct ndis_softc	*sc;
953	ndis_cfg		*vals;
954	const struct sysctlnode *ndis_node;
955	char buf[256];
956
957	printf("in ndis_create_sysctls()\n");
958
959	if (arg == NULL)
960		return(EINVAL);
961
962	sc = arg;
963	vals = sc->ndis_regvals;
964
965	TAILQ_INIT(&sc->ndis_cfglist_head);
966
967	/* Create the sysctl tree. */
968	sysctl_createv(&sc->sysctllog, 0, NULL, &ndis_node, CTLFLAG_READWRITE, CTLTYPE_NODE,
969					device_xname(sc->ndis_dev), NULL, NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
970
971	/* Store the number of the ndis mib */
972	sc->ndis_sysctl_mib = ndis_node->sysctl_num;
973
974	/* Add the driver-specific registry keys. */
975	vals = sc->ndis_regvals;
976	while(1) {
977		if (vals->nc_cfgkey == NULL)
978			break;
979		if (vals->nc_idx != sc->ndis_devidx) {
980			vals++;
981			continue;
982		}
983
984		/* See if we already have a sysctl with this name */
985/* TODO: Is something like this necessary in NetBSD?  I'm guessing this
986   TODO: is just checking if any of the information in the .inf file was
987   TODO: already determined by FreeBSD's autoconfiguration which seems to
988   TODO: add dev.XXX sysctl's beginning with %.  (NetBSD dosen't seem to do this).
989*/
990
991/* TODO: use CTLFLAG_OWNDATA or not? */
992		   /*
993		sysctl_createv(&sc->sysctllog, 0, NULL, NULL,
994						CTLFLAG_READWRITE|CTLFLAG_OWNDESC|CTLFLAG_OWNDATA, CTLTYPE_STRING,
995						vals->nc_cfgkey, vals->nc_cfgdesc, NULL, 0, vals->nc_val, strlen(vals->nc_val),
996					    ndis_node->sysctl_num, CTL_CREATE, CTL_EOL);
997		   */
998		   ndis_add_sysctl(sc, vals->nc_cfgkey,
999						   vals->nc_cfgdesc, vals->nc_val, CTLFLAG_READWRITE);
1000
1001   		vals++;
1002	} /* end while */
1003
1004		/* Now add a couple of builtin keys. */
1005
1006	/*
1007	 * Environment can be either Windows (0) or WindowsNT (1).
1008	 * We qualify as the latter.
1009	 */
1010#ifdef __NetBSD__
1011#define CTLFLAG_RD CTLFLAG_READONLY
1012/* TODO: do we need something like rman_get_start? */
1013#define rman_get_start(x) x
1014#endif
1015		ndis_add_sysctl(sc, "Environment",
1016						"Windows environment", "1", CTLFLAG_RD);
1017
1018		/* NDIS version should be 5.1. */
1019		ndis_add_sysctl(sc, "NdisVersion",
1020						/*"NDIS API Version"*/ "Version", "0x00050001", CTLFLAG_RD);
1021
1022		/* Bus type (PCI, PCMCIA, etc...) */
1023		sprintf(buf, "%d", (int)sc->ndis_iftype);
1024		ndis_add_sysctl(sc, "BusType", "Bus Type", buf, CTLFLAG_RD);
1025
1026		if (sc->ndis_res_io != NULL) {
1027			sprintf(buf, "0x%lx", (long unsigned int)rman_get_start(sc->ndis_res_io));
1028			ndis_add_sysctl(sc, "IOBaseAddress",
1029							/*"Base I/O Address"*/ "Base I/O", buf, CTLFLAG_RD);
1030		}
1031
1032		if (sc->ndis_irq != NULL) {
1033			sprintf(buf, "%lu", (long unsigned int)rman_get_start(sc->ndis_irq));
1034			ndis_add_sysctl(sc, "InterruptNumber",
1035							"Interrupt Number", buf, CTLFLAG_RD);
1036		}
1037
1038		return(0);
1039}
1040#endif /* __NetBSD__ */
1041
1042char *ndis_strdup(const char *src);
1043
1044char *ndis_strdup(const char *src)
1045{
1046	char *ret;
1047
1048	ret = malloc(strlen(src), M_DEVBUF, M_NOWAIT|M_ZERO);
1049	if (ret == NULL) {
1050		printf("ndis_strdup failed\n");
1051		return(NULL);
1052	}
1053	strcpy(ret, src);
1054
1055	return ret;
1056}
1057
1058int
1059ndis_add_sysctl(void *arg, const char *key, const char *desc, const char *val, int flag)
1060{
1061	struct ndis_softc	*sc;
1062	struct ndis_cfglist	*cfg;
1063	char			descstr[256];
1064#ifdef __NetBSD__
1065	char newkey[MAX_SYSCTL_LEN+1];
1066#endif
1067
1068	sc = arg;
1069
1070	cfg = malloc(sizeof(struct ndis_cfglist), M_DEVBUF, M_NOWAIT|M_ZERO);
1071
1072	if (cfg == NULL)
1073		return(ENOMEM);
1074
1075	/* I added this because NetBSD sysctl node names can't begin with
1076	 * a digit.
1077	 */
1078#ifdef __NetBSD__
1079	if(strlen(key) + strlen("ndis_") > MAX_SYSCTL_LEN) {
1080		panic("sysctl name too long: %s\n", key);
1081	}
1082	strcpy(newkey, "ndis_");
1083	strcpy(newkey + strlen("ndis_"), key);
1084	key = newkey;
1085#endif
1086
1087	cfg->ndis_cfg.nc_cfgkey = ndis_strdup(key);
1088
1089	if (desc == NULL) {
1090		snprintf(descstr, sizeof(descstr), "%s (dynamic)", key);
1091		cfg->ndis_cfg.nc_cfgdesc = ndis_strdup(descstr);
1092	} else
1093		cfg->ndis_cfg.nc_cfgdesc = ndis_strdup(desc);
1094	strcpy(cfg->ndis_cfg.nc_val, val);
1095
1096	TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link);
1097
1098#ifdef __FreeBSD__
1099#if __FreeBSD_version < 502113
1100	SYSCTL_ADD_STRING(&sc->ndis_ctx, SYSCTL_CHILDREN(sc->ndis_tree),
1101#else
1102	SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev),
1103	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)),
1104#endif
1105	    OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag,
1106	    cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val),
1107	    cfg->ndis_cfg.nc_cfgdesc);
1108#else /* __NetBSD__ */
1109/* TODO: use CTLFLAG_OWNDATA or not? */
1110	sysctl_createv(&sc->sysctllog, 0, NULL, NULL, flag/*|CTLFLAG_OWNDESC|CTLFLAG_OWNDATA*/, CTLTYPE_STRING,
1111					cfg->ndis_cfg.nc_cfgkey, cfg->ndis_cfg.nc_cfgdesc, NULL, 0, cfg->ndis_cfg.nc_val,
1112					strlen(cfg->ndis_cfg.nc_val), sc->ndis_sysctl_mib, CTL_CREATE, CTL_EOL);
1113#endif
1114	return(0);
1115}
1116
1117int
1118ndis_flush_sysctls(void *arg)
1119{
1120	struct ndis_softc	*sc;
1121	struct ndis_cfglist	*cfg;
1122
1123	sc = arg;
1124
1125	while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) {
1126		cfg = TAILQ_FIRST(&sc->ndis_cfglist_head);
1127		TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link);
1128#ifdef __FreeBSD__
1129		free(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF);
1130		free(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF);
1131#endif
1132		free(cfg, M_DEVBUF);
1133	}
1134
1135	return(0);
1136}
1137
1138static void
1139ndis_return(void *arg)
1140{
1141	struct ndis_softc	*sc;
1142	__stdcall ndis_return_handler	returnfunc;
1143	ndis_handle		adapter;
1144	ndis_packet		*p;
1145	uint8_t			irql = 0;	/* XXX: gcc */
1146
1147	p = arg;
1148	sc = p->np_softc;
1149	adapter = sc->ndis_block->nmb_miniportadapterctx;
1150
1151	if (adapter == NULL)
1152		return;
1153
1154	returnfunc = sc->ndis_chars->nmc_return_packet_func;
1155
1156	KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql);
1157	MSCALL2(returnfunc, adapter, p);
1158	KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql);
1159
1160	return;
1161}
1162
1163void
1164#ifdef __FreeBSD__
1165ndis_return_packet(buf, arg)
1166	void			*buf;	/* not used */
1167	void			*arg;
1168#else
1169ndis_return_packet(struct mbuf *m, void *buf,
1170    size_t size, void *arg)
1171#endif
1172
1173{
1174	ndis_packet		*p;
1175
1176	if (arg == NULL)
1177		return;
1178
1179	p = arg;
1180
1181	/* Decrement refcount. */
1182	p->np_refcnt--;
1183
1184	/* Release packet when refcount hits zero, otherwise return. */
1185	if (p->np_refcnt)
1186		return;
1187
1188	ndis_sched(ndis_return, p, NDIS_TASKQUEUE);
1189
1190	return;
1191}
1192
1193void
1194ndis_free_bufs(ndis_buffer *b0)
1195{
1196	ndis_buffer		*next;
1197
1198	if (b0 == NULL)
1199		return;
1200
1201	while(b0 != NULL) {
1202		next = b0->mdl_next;
1203		IoFreeMdl(b0);
1204		b0 = next;
1205	}
1206
1207	return;
1208}
1209int in_reset = 0;
1210void
1211ndis_free_packet(ndis_packet *p)
1212{
1213	if (p == NULL)
1214		return;
1215
1216	ndis_free_bufs(p->np_private.npp_head);
1217	NdisFreePacket(p);
1218	return;
1219}
1220
1221#ifdef __FreeBSD__
1222int
1223ndis_convert_res(void *arg)
1224{
1225	struct ndis_softc	*sc;
1226	ndis_resource_list	*rl = NULL;
1227	cm_partial_resource_desc	*prd = NULL;
1228	ndis_miniport_block	*block;
1229	device_t		dev;
1230	struct resource_list	*brl;
1231	struct resource_list_entry	*brle;
1232#if __FreeBSD_version < 600022
1233	struct resource_list	brl_rev;
1234	struct resource_list_entry	*n;
1235#endif
1236	int 			error = 0;
1237
1238	sc = arg;
1239	block = sc->ndis_block;
1240	dev = sc->ndis_dev;
1241
1242#if __FreeBSD_version < 600022
1243	SLIST_INIT(&brl_rev);
1244#endif
1245	rl = malloc(sizeof(ndis_resource_list) +
1246	    (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)),
1247	    M_DEVBUF, M_NOWAIT|M_ZERO);
1248
1249	if (rl == NULL)
1250		return(ENOMEM);
1251
1252	rl->cprl_version = 5;
1253	rl->cprl_version = 1;
1254	rl->cprl_count = sc->ndis_rescnt;
1255	prd = rl->cprl_partial_descs;
1256
1257	brl = BUS_GET_RESOURCE_LIST(dev, dev);
1258
1259	if (brl != NULL) {
1260
1261#if __FreeBSD_version < 600022
1262		/*
1263		 * We have a small problem. Some PCI devices have
1264		 * multiple I/O ranges. Windows orders them starting
1265		 * from lowest numbered BAR to highest. We discover
1266		 * them in that order too, but insert them into a singly
1267		 * linked list head first, which means when time comes
1268		 * to traverse the list, we enumerate them in reverse
1269		 * order. This screws up some drivers which expect the
1270		 * BARs to be in ascending order so that they can choose
1271		 * the "first" one as their register space. Unfortunately,
1272		 * in order to fix this, we have to create our own
1273		 * temporary list with the entries in reverse order.
1274		 */
1275		SLIST_FOREACH(brle, brl, link) {
1276			n = malloc(sizeof(struct resource_list_entry),
1277			    M_TEMP, M_NOWAIT);
1278			if (n == NULL) {
1279				error = ENOMEM;
1280				goto bad;
1281			}
1282			memcpy( (char *)n, (char *)brle,
1283			    sizeof(struct resource_list_entry));
1284			SLIST_INSERT_HEAD(&brl_rev, n, link);
1285		}
1286
1287		SLIST_FOREACH(brle, &brl_rev, link) {
1288#else
1289		STAILQ_FOREACH(brle, brl, link) {
1290#endif
1291			switch (brle->type) {
1292			case SYS_RES_IOPORT:
1293				prd->cprd_type = CmResourceTypePort;
1294				prd->cprd_flags = CM_RESOURCE_PORT_IO;
1295				prd->cprd_sharedisp =
1296				    CmResourceShareDeviceExclusive;
1297				prd->u.cprd_port.cprd_start.np_quad =
1298				    brle->start;
1299				prd->u.cprd_port.cprd_len = brle->count;
1300				break;
1301			case SYS_RES_MEMORY:
1302				prd->cprd_type = CmResourceTypeMemory;
1303				prd->cprd_flags =
1304				    CM_RESOURCE_MEMORY_READ_WRITE;
1305				prd->cprd_sharedisp =
1306				    CmResourceShareDeviceExclusive;
1307				prd->u.cprd_port.cprd_start.np_quad =
1308				    brle->start;
1309				prd->u.cprd_port.cprd_len = brle->count;
1310				break;
1311			case SYS_RES_IRQ:
1312				prd->cprd_type = CmResourceTypeInterrupt;
1313				prd->cprd_flags = 0;
1314				prd->cprd_sharedisp =
1315				    CmResourceShareDeviceExclusive;
1316				prd->u.cprd_intr.cprd_level = brle->start;
1317				prd->u.cprd_intr.cprd_vector = brle->start;
1318				prd->u.cprd_intr.cprd_affinity = 0;
1319				break;
1320			default:
1321				break;
1322			}
1323			prd++;
1324		}
1325	}
1326
1327	block->nmb_rlist = rl;
1328
1329#if __FreeBSD_version < 600022
1330bad:
1331
1332	while (!SLIST_EMPTY(&brl_rev)) {
1333		n = SLIST_FIRST(&brl_rev);
1334		SLIST_REMOVE_HEAD(&brl_rev, link);
1335		free (n, M_TEMP);
1336	}
1337#endif
1338
1339	return(error);
1340}
1341#endif /* __FreeBSD__ */
1342/*
1343 * Map an NDIS packet to an mbuf list. When an NDIS driver receives a
1344 * packet, it will hand it to us in the form of an ndis_packet,
1345 * which we need to convert to an mbuf that is then handed off
1346 * to the stack. Note: we configure the mbuf list so that it uses
1347 * the memory regions specified by the ndis_buffer structures in
1348 * the ndis_packet as external storage. In most cases, this will
1349 * point to a memory region allocated by the driver (either by
1350 * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect
1351 * the driver to handle free()ing this region for is, so we set up
1352 * a dummy no-op free handler for it.
1353 */
1354
1355int
1356ndis_ptom(struct mbuf **m0, ndis_packet *p)
1357{
1358	struct mbuf		*m, *prev = NULL;
1359	ndis_buffer		*buf;
1360	ndis_packet_private	*priv;
1361	uint32_t		totlen = 0;
1362
1363	if (p == NULL || m0 == NULL)
1364		return(EINVAL);
1365
1366	priv = &p->np_private;
1367	buf = priv->npp_head;
1368	p->np_refcnt = 0;
1369
1370	for (buf = priv->npp_head; buf != NULL; buf = buf->mdl_next) {
1371		if (buf == priv->npp_head)
1372			MGETHDR(m, M_DONTWAIT, MT_HEADER);
1373		else
1374			MGET(m, M_DONTWAIT, MT_DATA);
1375		if (m == NULL) {
1376			m_freem(*m0);
1377			*m0 = NULL;
1378			return(ENOBUFS);
1379		}
1380		m->m_len = MmGetMdlByteCount(buf);
1381		m->m_data = MmGetMdlVirtualAddress(buf);
1382#ifdef __FreeBSD__
1383		MEXTADD(m, m->m_data, m->m_len, ndis_return_packet,
1384			p, 0, EXT_NDIS);
1385#else
1386		MEXTADD(m, m->m_data, m->m_len, M_DEVBUF,
1387			ndis_return_packet, p);
1388#endif
1389		p->np_refcnt++;
1390		totlen += m->m_len;
1391		if (m->m_flags & MT_HEADER)
1392			*m0 = m;
1393		else
1394			prev->m_next = m;
1395		prev = m;
1396	}
1397
1398	(*m0)->m_pkthdr.len = totlen;
1399
1400	return(0);
1401}
1402
1403/*
1404 * Create an NDIS packet from an mbuf chain.
1405 * This is used mainly when transmitting packets, where we need
1406 * to turn an mbuf off an interface's send queue and transform it
1407 * into an NDIS packet which will be fed into the NDIS driver's
1408 * send routine.
1409 *
1410 * NDIS packets consist of two parts: an ndis_packet structure,
1411 * which is vaguely analagous to the pkthdr portion of an mbuf,
1412 * and one or more ndis_buffer structures, which define the
1413 * actual memory segments in which the packet data resides.
1414 * We need to allocate one ndis_buffer for each mbuf in a chain,
1415 * plus one ndis_packet as the header.
1416 */
1417
1418int
1419ndis_mtop(struct mbuf *m0, ndis_packet **p)
1420{
1421	struct mbuf		*m;
1422	ndis_buffer		*buf = NULL, *prev = NULL;
1423	ndis_packet_private	*priv;
1424
1425	if (p == NULL || *p == NULL || m0 == NULL)
1426		return(EINVAL);
1427
1428	priv = &(*p)->np_private;
1429	priv->npp_totlen = m0->m_pkthdr.len;
1430
1431	for (m = m0; m != NULL; m = m->m_next) {
1432		if (m->m_len == 0)
1433			continue;
1434		buf = IoAllocateMdl(m->m_data, m->m_len, FALSE, FALSE, NULL);
1435		if (buf == NULL) {
1436			ndis_free_packet(*p);
1437			*p = NULL;
1438			return(ENOMEM);
1439		}
1440
1441		if (priv->npp_head == NULL)
1442			priv->npp_head = buf;
1443		else
1444			prev->mdl_next = buf;
1445		prev = buf;
1446	}
1447
1448	priv->npp_tail = buf;
1449
1450	return(0);
1451}
1452
1453int
1454ndis_get_supported_oids(void *arg, ndis_oid **oids, int *oidcnt)
1455{
1456	int			len, rval;
1457	ndis_oid		*o;
1458
1459	if (arg == NULL || oids == NULL || oidcnt == NULL)
1460		return(EINVAL);
1461	len = 0;
1462	ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len);
1463
1464	o = malloc(len, M_DEVBUF, M_NOWAIT);
1465	if (o == NULL)
1466		return(ENOMEM);
1467
1468	rval = ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, o, &len);
1469
1470	if (rval) {
1471		free(o, M_DEVBUF);
1472		return(rval);
1473	}
1474
1475	*oids = o;
1476	*oidcnt = len / 4;
1477
1478	return(0);
1479}
1480
1481int
1482ndis_set_info(void *arg, ndis_oid oid, void *buf, int *buflen)
1483{
1484	struct ndis_softc	*sc;
1485	ndis_status		rval;
1486	ndis_handle		adapter;
1487	__stdcall ndis_setinfo_handler	setfunc;
1488	uint32_t		byteswritten = 0, bytesneeded = 0;
1489	int			error;
1490	uint8_t			irql = 0;	/* XXX: gcc */
1491
1492	/*
1493	 * According to the NDIS spec, MiniportQueryInformation()
1494	 * and MiniportSetInformation() requests are handled serially:
1495	 * once one request has been issued, we must wait for it to
1496 	 * finish before allowing another request to proceed.
1497	 */
1498
1499	sc = arg;
1500
1501	KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql);
1502
1503	if (sc->ndis_block->nmb_pendingreq != NULL)
1504		panic("ndis_set_info() called while other request pending");
1505	else
1506		sc->ndis_block->nmb_pendingreq = (ndis_request *)sc;
1507
1508	/* I added this lock because it was present in the FreeBSD-current sources */
1509	NDIS_LOCK(sc);
1510
1511	setfunc = sc->ndis_chars->nmc_setinfo_func;
1512	adapter = sc->ndis_block->nmb_miniportadapterctx;
1513
1514	if (adapter == NULL || setfunc == NULL) {
1515		sc->ndis_block->nmb_pendingreq = NULL;
1516		KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql);
1517		NDIS_UNLOCK(sc);
1518		return(ENXIO);
1519	}
1520
1521	NDIS_UNLOCK(sc);
1522
1523	rval = MSCALL6(setfunc, adapter, oid, buf, *buflen,
1524	    &byteswritten, &bytesneeded);
1525
1526	sc->ndis_block->nmb_pendingreq = NULL;
1527
1528	KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql);
1529
1530	if (rval == NDIS_STATUS_PENDING) {
1531		mtx_lock(&ndis_req_mtx);
1532		error = mtsleep(&sc->ndis_block->nmb_setstat,
1533				PZERO | PNORELOCK,
1534				"ndisset", 5 * hz, &ndis_req_mtx);
1535		rval = sc->ndis_block->nmb_setstat;
1536	}
1537
1538	if (byteswritten)
1539		*buflen = byteswritten;
1540	if (bytesneeded)
1541		*buflen = bytesneeded;
1542
1543	if (rval == NDIS_STATUS_INVALID_LENGTH)
1544		return(ENOSPC);
1545
1546	if (rval == NDIS_STATUS_INVALID_OID)
1547		return(EINVAL);
1548
1549	if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1550	    rval == NDIS_STATUS_NOT_ACCEPTED)
1551		return(ENOTSUP);
1552
1553	if (rval != NDIS_STATUS_SUCCESS)
1554		return(ENODEV);
1555
1556	return(0);
1557}
1558
1559typedef void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status);
1560
1561int
1562ndis_send_packets(void *arg, ndis_packet **packets, int cnt)
1563{
1564	struct ndis_softc	*sc;
1565	ndis_handle		adapter;
1566	__stdcall ndis_sendmulti_handler	sendfunc;
1567	__stdcall ndis_senddone_func		senddonefunc;
1568	int			i;
1569	ndis_packet		*p;
1570	uint8_t			irql = 0;	/* XXX: gcc */
1571
1572	sc = arg;
1573	adapter = sc->ndis_block->nmb_miniportadapterctx;
1574	if (adapter == NULL)
1575		return(ENXIO);
1576	sendfunc = sc->ndis_chars->nmc_sendmulti_func;
1577	senddonefunc = sc->ndis_block->nmb_senddone_func;
1578
1579	if (NDIS_SERIALIZED(sc->ndis_block))
1580		KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql);
1581
1582	MSCALL3(sendfunc, adapter, packets, cnt);
1583
1584	for (i = 0; i < cnt; i++) {
1585		p = packets[i];
1586		/*
1587		 * Either the driver already handed the packet to
1588		 * ndis_txeof() due to a failure, or it wants to keep
1589		 * it and release it asynchronously later. Skip to the
1590		 * next one.
1591		 */
1592		if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING)
1593			continue;
1594		MSCALL3(senddonefunc, sc->ndis_block, p, p->np_oob.npo_status);
1595	}
1596
1597	if (NDIS_SERIALIZED(sc->ndis_block))
1598		KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql);
1599
1600	return(0);
1601}
1602
1603int
1604ndis_send_packet(void *arg, ndis_packet *packet)
1605{
1606	struct ndis_softc	*sc;
1607	ndis_handle		adapter;
1608	ndis_status		status;
1609	__stdcall ndis_sendsingle_handler	sendfunc;
1610	__stdcall ndis_senddone_func		senddonefunc;
1611	uint8_t			irql = 0;	/* XXX: gcc */
1612
1613	sc = arg;
1614	adapter = sc->ndis_block->nmb_miniportadapterctx;
1615	if (adapter == NULL)
1616		return(ENXIO);
1617	sendfunc = sc->ndis_chars->nmc_sendsingle_func;
1618	senddonefunc = sc->ndis_block->nmb_senddone_func;
1619
1620	if (NDIS_SERIALIZED(sc->ndis_block))
1621		KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql);
1622	status = MSCALL3(sendfunc, adapter, packet,
1623	    packet->np_private.npp_flags);
1624
1625	if (status == NDIS_STATUS_PENDING) {
1626		if (NDIS_SERIALIZED(sc->ndis_block))
1627			KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql);
1628		return(0);
1629	}
1630
1631	MSCALL3(senddonefunc, sc->ndis_block, packet, status);
1632
1633	if (NDIS_SERIALIZED(sc->ndis_block))
1634		KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql);
1635
1636	return(0);
1637}
1638
1639int
1640ndis_init_dma(void *arg)
1641{
1642	struct ndis_softc	*sc;
1643	int			i, error = 0;
1644
1645	sc = arg;
1646
1647	sc->ndis_tmaps = malloc(sizeof(bus_dmamap_t) * sc->ndis_maxpkts,
1648	    M_DEVBUF, M_NOWAIT|M_ZERO);
1649
1650	if (sc->ndis_tmaps == NULL)
1651		return(ENOMEM);
1652
1653	for (i = 0; i < sc->ndis_maxpkts; i++) {
1654#ifdef __FreeBSD__
1655		error = bus_dmamap_create(sc->ndis_ttag, 0,
1656		    &sc->ndis_tmaps[i]);
1657#else
1658		/*
1659		bus_dmamap_create(sc->ndis_mtag, sizeof(bus_dmamap_t),
1660				  1, sizeof(bus_dmamap_t), BUS_DMA_NOWAIT,
1661				  0, &sc->ndis_mmaps[i]);
1662		*/
1663		bus_dmamap_create(sc->ndis_ttag, NDIS_MAXSEG * MCLBYTES,
1664				  NDIS_MAXSEG, MCLBYTES, 0,
1665				  BUS_DMA_NOWAIT, &sc->ndis_tmaps[i]);
1666#endif
1667		if (error) {
1668			free(sc->ndis_tmaps, M_DEVBUF);
1669			return(ENODEV);
1670		}
1671	}
1672
1673	return(0);
1674}
1675
1676int
1677ndis_destroy_dma(void *arg)
1678{
1679	struct ndis_softc	*sc;
1680	struct mbuf		*m;
1681	ndis_packet		*p = NULL;
1682	int			i;
1683
1684	sc = arg;
1685
1686	for (i = 0; i < sc->ndis_maxpkts; i++) {
1687		if (sc->ndis_txarray[i] != NULL) {
1688			p = sc->ndis_txarray[i];
1689			m = (struct mbuf *)p->np_rsvd[1];
1690			if (m != NULL)
1691				m_freem(m);
1692			ndis_free_packet(sc->ndis_txarray[i]);
1693		}
1694		bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]);
1695	}
1696
1697	free(sc->ndis_tmaps, M_DEVBUF);
1698
1699#ifdef __FreeBSD__
1700	bus_dma_tag_destroy(sc->ndis_ttag);
1701#endif
1702
1703	return(0);
1704}
1705
1706int
1707ndis_reset_nic(void *arg)
1708{
1709	struct ndis_softc	*sc;
1710	ndis_handle		adapter;
1711	__stdcall ndis_reset_handler	resetfunc;
1712	uint8_t			addressing_reset;
1713	struct ifnet		*ifp;
1714	int			rval;
1715	uint8_t			irql = 0;	/* XXX: gcc */
1716
1717	sc = arg;
1718#ifdef __FreeBSD__
1719	ifp = &sc->arpcom.ac_if;
1720#else
1721	ifp = &sc->arpcom.ec_if;
1722#endif
1723
1724	adapter = sc->ndis_block->nmb_miniportadapterctx;
1725	resetfunc = sc->ndis_chars->nmc_reset_func;
1726
1727	if (adapter == NULL || resetfunc == NULL)
1728		return(EIO);
1729
1730	if (NDIS_SERIALIZED(sc->ndis_block))
1731		KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql);
1732
1733	rval = MSCALL2(resetfunc, &addressing_reset, adapter);
1734
1735	if (NDIS_SERIALIZED(sc->ndis_block))
1736		KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql);
1737
1738	if (rval == NDIS_STATUS_PENDING) {
1739		mtsleep(sc, PZERO | PNORELOCK, "ndisrst", 0, &ndis_req_mtx);
1740	}
1741
1742	return(0);
1743}
1744
1745int
1746ndis_halt_nic(void *arg)
1747{
1748	struct ndis_softc	*sc;
1749	ndis_handle		adapter;
1750	__stdcall ndis_halt_handler	haltfunc;
1751	struct ifnet		*ifp;
1752
1753	sc = arg;
1754#ifdef __FreeBSD__
1755	ifp = &sc->arpcom.ac_if;
1756#else
1757	ifp = &sc->arpcom.ec_if;
1758#endif
1759
1760	NDIS_LOCK(sc);
1761
1762	adapter = sc->ndis_block->nmb_miniportadapterctx;
1763	if (adapter == NULL) {
1764		NDIS_UNLOCK(sc);
1765		return(EIO);
1766	}
1767
1768	/*
1769	 * The adapter context is only valid after the init
1770	 * handler has been called, and is invalid once the
1771	 * halt handler has been called.
1772	 */
1773
1774	haltfunc = sc->ndis_chars->nmc_halt_func;
1775
1776	NDIS_UNLOCK(sc);
1777
1778	MSCALL1(haltfunc, adapter);
1779
1780	NDIS_LOCK(sc);
1781
1782	sc->ndis_block->nmb_miniportadapterctx = NULL;
1783
1784	NDIS_UNLOCK(sc);
1785
1786	return(0);
1787}
1788
1789int
1790ndis_shutdown_nic(void *arg)
1791{
1792	struct ndis_softc	*sc;
1793	ndis_handle		adapter;
1794	__stdcall ndis_shutdown_handler	shutdownfunc;
1795
1796	sc = arg;
1797
1798	NDIS_LOCK(sc);
1799
1800	adapter = sc->ndis_block->nmb_miniportadapterctx;
1801	shutdownfunc = sc->ndis_chars->nmc_shutdown_handler;
1802
1803	NDIS_UNLOCK(sc);
1804
1805	if (adapter == NULL || shutdownfunc == NULL)
1806		return(EIO);
1807
1808	if (sc->ndis_chars->nmc_rsvd0 == NULL)
1809		MSCALL1(shutdownfunc, adapter);
1810	else
1811		MSCALL1(shutdownfunc, sc->ndis_chars->nmc_rsvd0);
1812
1813	ndis_shrink_thrqueue(8);
1814	TAILQ_REMOVE(&ndis_devhead, sc->ndis_block, link);
1815
1816	return(0);
1817}
1818
1819int
1820ndis_init_nic(void *arg)
1821{
1822	struct ndis_softc	*sc;
1823	ndis_miniport_block	*block;
1824        __stdcall ndis_init_handler	initfunc;
1825	ndis_status		status, openstatus = 0;
1826	ndis_medium		mediumarray[NdisMediumMax];
1827	uint32_t		chosenmedium, i;
1828
1829	if (arg == NULL)
1830		return(EINVAL);
1831
1832	sc = arg;
1833
1834	NDIS_LOCK(sc);
1835
1836	block = sc->ndis_block;
1837	initfunc = sc->ndis_chars->nmc_init_func;
1838
1839	NDIS_UNLOCK(sc);
1840
1841	printf("sc->ndis_chars->nmc_version_major = %d\n\
1842			sc->ndis_chars->nmc_version_minor = %d\n",
1843			sc->ndis_chars->nmc_version_major,
1844			sc->ndis_chars->nmc_version_minor);
1845
1846	for (i = 0; i < NdisMediumMax; i++)
1847		mediumarray[i] = i;
1848
1849        status = MSCALL6(initfunc, &openstatus, &chosenmedium,
1850            mediumarray, NdisMediumMax, block, block);
1851
1852	printf("status = %x", status);
1853
1854	/*
1855	 * If the init fails, blow away the other exported routines
1856	 * we obtained from the driver so we can't call them later.
1857	 * If the init failed, none of these will work.
1858	 */
1859	if (status != NDIS_STATUS_SUCCESS) {
1860		NDIS_LOCK(sc);
1861
1862		sc->ndis_block->nmb_miniportadapterctx = NULL;
1863
1864		NDIS_UNLOCK(sc);
1865		return(ENXIO);
1866	}
1867
1868	return(0);
1869}
1870
1871void
1872ndis_enable_intr(void *arg)
1873{
1874	struct ndis_softc	*sc;
1875	ndis_handle		adapter;
1876	__stdcall ndis_enable_interrupts_handler	intrenbfunc;
1877
1878	sc = arg;
1879	adapter = sc->ndis_block->nmb_miniportadapterctx;
1880	intrenbfunc = sc->ndis_chars->nmc_enable_interrupts_func;
1881	if (adapter == NULL || intrenbfunc == NULL)
1882		return;
1883	MSCALL1(intrenbfunc, adapter);
1884
1885	return;
1886}
1887
1888void
1889ndis_disable_intr(void *arg)
1890{
1891	struct ndis_softc	*sc;
1892	ndis_handle		adapter;
1893	__stdcall ndis_disable_interrupts_handler	intrdisfunc;
1894
1895	sc = arg;
1896	adapter = sc->ndis_block->nmb_miniportadapterctx;
1897	intrdisfunc = sc->ndis_chars->nmc_disable_interrupts_func;
1898	if (adapter == NULL || intrdisfunc == NULL)
1899	    return;
1900
1901	MSCALL1(intrdisfunc, adapter);
1902
1903	return;
1904}
1905
1906int
1907ndis_isr(void *arg, int *ourintr, int *callhandler)
1908{
1909	struct ndis_softc	*sc;
1910	ndis_handle		adapter;
1911	__stdcall ndis_isr_handler	isrfunc;
1912	uint8_t			accepted, queue;
1913
1914	if (arg == NULL || ourintr == NULL || callhandler == NULL)
1915		return(EINVAL);
1916
1917	sc = arg;
1918	adapter = sc->ndis_block->nmb_miniportadapterctx;
1919	isrfunc = sc->ndis_chars->nmc_isr_func;
1920
1921	if (adapter == NULL || isrfunc == NULL)
1922		return(ENXIO);
1923
1924	MSCALL3(isrfunc, &accepted, &queue, adapter);
1925
1926	*ourintr = accepted;
1927	*callhandler = queue;
1928
1929	return(0);
1930}
1931
1932__stdcall static void
1933ndis_intrhand(kdpc *dpc, device_object *dobj,
1934    irp *ip, struct ndis_softc *sc)
1935{
1936	ndis_handle		adapter;
1937	__stdcall ndis_interrupt_handler	intrfunc;
1938	uint8_t			irql = 0;	/* XXX: gcc */
1939
1940	adapter = sc->ndis_block->nmb_miniportadapterctx;
1941	intrfunc = sc->ndis_chars->nmc_interrupt_func;
1942
1943	if (adapter == NULL || intrfunc == NULL)
1944		return;
1945
1946	if (NDIS_SERIALIZED(sc->ndis_block))
1947		KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql);
1948
1949	MSCALL1(intrfunc, adapter);
1950
1951	/* If there's a MiniportEnableInterrupt() routine, call it. */
1952
1953	ndis_enable_intr(sc);
1954
1955	if (NDIS_SERIALIZED(sc->ndis_block))
1956		KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql);
1957
1958	return;
1959}
1960
1961int
1962ndis_get_info(void *arg, ndis_oid oid, void *buf, int *buflen)
1963{
1964	struct ndis_softc	*sc;
1965	ndis_status		rval;
1966	ndis_handle		adapter;
1967	__stdcall ndis_queryinfo_handler	queryfunc;
1968	uint32_t		byteswritten = 0, bytesneeded = 0;
1969#ifdef __FreeBSD__
1970	int			error;
1971#endif
1972	uint8_t			irql = 0;	/* XXX: gcc */
1973
1974	//printf("in ndis_get_info\n");
1975
1976	sc = arg;
1977	KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql);
1978
1979	if (sc->ndis_block->nmb_pendingreq != NULL)
1980		panic("ndis_get_info() called while other request pending");
1981	else
1982		sc->ndis_block->nmb_pendingreq = (ndis_request *)sc;
1983
1984	queryfunc = sc->ndis_chars->nmc_queryinfo_func;
1985	adapter = sc->ndis_block->nmb_miniportadapterctx;
1986
1987	if (adapter == NULL || queryfunc == NULL) {
1988		sc->ndis_block->nmb_pendingreq = NULL;
1989		KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql);
1990		return(ENXIO);
1991	}
1992
1993	rval = MSCALL6(queryfunc, adapter, oid, buf, *buflen,
1994	    &byteswritten, &bytesneeded);
1995
1996	sc->ndis_block->nmb_pendingreq = NULL;
1997
1998	KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql);
1999
2000	/* Wait for requests that block. */
2001
2002	if (rval == NDIS_STATUS_PENDING) {
2003		mtx_lock(&ndis_req_mtx);
2004		mtsleep(&sc->ndis_block->nmb_getstat, PZERO | PNORELOCK,
2005		    "ndisget", 5 * hz, &ndis_req_mtx);
2006		rval = sc->ndis_block->nmb_getstat;
2007	}
2008
2009	if (byteswritten)
2010		*buflen = byteswritten;
2011	if (bytesneeded)
2012		*buflen = bytesneeded;
2013
2014	if (rval == NDIS_STATUS_INVALID_LENGTH ||
2015	    rval == NDIS_STATUS_BUFFER_TOO_SHORT)
2016		return(ENOSPC);
2017
2018	if (rval == NDIS_STATUS_INVALID_OID)
2019		return(EINVAL);
2020
2021	if (rval == NDIS_STATUS_NOT_SUPPORTED ||
2022	    rval == NDIS_STATUS_NOT_ACCEPTED)
2023		return(ENOTSUP);
2024
2025	if (rval != NDIS_STATUS_SUCCESS)
2026		return(ENODEV);
2027
2028	return(0);
2029}
2030
2031__stdcall uint32_t
2032NdisAddDevice(driver_object *drv, device_object *pdo)
2033{
2034	device_object		*fdo;
2035	ndis_miniport_block	*block;
2036	struct ndis_softc	*sc;
2037	uint32_t		status;
2038
2039	status = IoCreateDevice(drv, sizeof(ndis_miniport_block), NULL,
2040	    FILE_DEVICE_UNKNOWN, 0, FALSE, &fdo);
2041
2042	if (status != STATUS_SUCCESS)
2043		return(status);
2044
2045	block = fdo->do_devext;
2046	block->nmb_deviceobj = fdo;
2047	block->nmb_physdeviceobj = pdo;
2048	block->nmb_nextdeviceobj = IoAttachDeviceToDeviceStack(fdo, pdo);
2049	KeInitializeSpinLock(&block->nmb_lock);
2050
2051#ifdef __NetBSD__
2052	/* NetBSD has a pointer to the callout object */
2053	block->nmb_wkupdpctimer.nt_ktimer.k_handle =
2054		malloc(sizeof(struct callout), M_DEVBUF, M_NOWAIT|M_ZERO);
2055#endif
2056
2057	/*
2058	 * Stash pointers to the miniport block and miniport
2059	 * characteristics info in the if_ndis softc so the
2060	 * UNIX wrapper driver can get to them later.
2061     */
2062#ifdef __FreeBSD__
2063	sc = device_get_softc(pdo->do_devext);
2064#else /* __NetBSD__ */
2065	sc = pdo->pdo_sc;
2066	fdo->fdo_sc = sc;
2067#endif
2068	sc->ndis_block = block;
2069	sc->ndis_chars = IoGetDriverObjectExtension(drv, (void *)1);
2070
2071	IoInitializeDpcRequest(fdo, kernndis_functbl[6].ipt_wrap);
2072
2073	/* Finish up BSD-specific setup. */
2074
2075	block->nmb_signature = (void *)0xcafebabe;
2076	block->nmb_status_func = kernndis_functbl[0].ipt_wrap;
2077	block->nmb_statusdone_func = kernndis_functbl[1].ipt_wrap;
2078	block->nmb_setdone_func = kernndis_functbl[2].ipt_wrap;
2079	block->nmb_querydone_func = kernndis_functbl[3].ipt_wrap;
2080	block->nmb_resetdone_func = kernndis_functbl[4].ipt_wrap;
2081	block->nmb_sendrsrc_func = kernndis_functbl[5].ipt_wrap;
2082	block->nmb_pendingreq = NULL;
2083
2084	ndis_enlarge_thrqueue(8);
2085
2086	TAILQ_INSERT_TAIL(&ndis_devhead, block, link);
2087
2088	return (STATUS_SUCCESS);
2089}
2090
2091int
2092ndis_unload_driver(void *arg)
2093{
2094	struct ndis_softc	*sc;
2095	device_object		*fdo;
2096
2097	sc = arg;
2098
2099	if (sc->ndis_block->nmb_rlist != NULL)
2100		free(sc->ndis_block->nmb_rlist, M_DEVBUF);
2101
2102	ndis_flush_sysctls(sc);
2103
2104	ndis_shrink_thrqueue(8);
2105	TAILQ_REMOVE(&ndis_devhead, sc->ndis_block, link);
2106
2107	fdo = sc->ndis_block->nmb_deviceobj;
2108	IoDetachDevice(sc->ndis_block->nmb_nextdeviceobj);
2109	IoDeleteDevice(fdo);
2110
2111	return(0);
2112}
2113