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