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