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