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