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