kern_ndis.c revision 140751
1139743Simp/*-
2123474Swpaul * Copyright (c) 2003
3123474Swpaul *	Bill Paul <wpaul@windriver.com>.  All rights reserved.
4123474Swpaul *
5123474Swpaul * Redistribution and use in source and binary forms, with or without
6123474Swpaul * modification, are permitted provided that the following conditions
7123474Swpaul * are met:
8123474Swpaul * 1. Redistributions of source code must retain the above copyright
9123474Swpaul *    notice, this list of conditions and the following disclaimer.
10123474Swpaul * 2. Redistributions in binary form must reproduce the above copyright
11123474Swpaul *    notice, this list of conditions and the following disclaimer in the
12123474Swpaul *    documentation and/or other materials provided with the distribution.
13123474Swpaul * 3. All advertising materials mentioning features or use of this software
14123474Swpaul *    must display the following acknowledgement:
15123474Swpaul *	This product includes software developed by Bill Paul.
16123474Swpaul * 4. Neither the name of the author nor the names of any co-contributors
17123474Swpaul *    may be used to endorse or promote products derived from this software
18123474Swpaul *    without specific prior written permission.
19123474Swpaul *
20123474Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21123474Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22123474Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23123474Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24123474Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25123474Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26123474Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27123474Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28123474Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29123474Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30123474Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
31123474Swpaul */
32123474Swpaul
33123474Swpaul#include <sys/cdefs.h>
34123474Swpaul__FBSDID("$FreeBSD: head/sys/compat/ndis/kern_ndis.c 140751 2005-01-24 18:18:12Z wpaul $");
35123474Swpaul
36123474Swpaul#include <sys/param.h>
37124697Swpaul#include <sys/systm.h>
38124697Swpaul#include <sys/unistd.h>
39123474Swpaul#include <sys/types.h>
40123474Swpaul#include <sys/errno.h>
41123474Swpaul#include <sys/callout.h>
42123474Swpaul#include <sys/socket.h>
43123474Swpaul#include <sys/queue.h>
44123474Swpaul#include <sys/sysctl.h>
45124697Swpaul#include <sys/proc.h>
46123474Swpaul#include <sys/malloc.h>
47123474Swpaul#include <sys/lock.h>
48123474Swpaul#include <sys/mutex.h>
49123474Swpaul#include <sys/conf.h>
50123474Swpaul
51123474Swpaul#include <sys/kernel.h>
52129970Swpaul#include <sys/module.h>
53124697Swpaul#include <sys/kthread.h>
54123474Swpaul#include <machine/bus.h>
55123474Swpaul#include <machine/resource.h>
56123474Swpaul#include <sys/bus.h>
57123474Swpaul#include <sys/rman.h>
58123474Swpaul
59124060Swpaul#include <vm/uma.h>
60124060Swpaul
61123474Swpaul#include <net/if.h>
62123474Swpaul#include <net/if_arp.h>
63123474Swpaul#include <net/ethernet.h>
64123474Swpaul#include <net/if_dl.h>
65123474Swpaul#include <net/if_media.h>
66123474Swpaul
67123695Swpaul#include <net80211/ieee80211_var.h>
68123695Swpaul#include <net80211/ieee80211_ioctl.h>
69123695Swpaul
70123474Swpaul#include <compat/ndis/pe_var.h>
71123474Swpaul#include <compat/ndis/resource_var.h>
72125551Swpaul#include <compat/ndis/ntoskrnl_var.h>
73123474Swpaul#include <compat/ndis/ndis_var.h>
74123474Swpaul#include <compat/ndis/hal_var.h>
75123474Swpaul#include <compat/ndis/cfg_var.h>
76123474Swpaul#include <dev/if_ndis/if_ndisvar.h>
77123474Swpaul
78123474Swpaul#define NDIS_DUMMY_PATH "\\\\some\\bogus\\path"
79123474Swpaul
80123474Swpaul__stdcall static void ndis_status_func(ndis_handle, ndis_status,
81123474Swpaul	void *, uint32_t);
82123474Swpaul__stdcall static void ndis_statusdone_func(ndis_handle);
83123474Swpaul__stdcall static void ndis_setdone_func(ndis_handle, ndis_status);
84123535Swpaul__stdcall static void ndis_getdone_func(ndis_handle, ndis_status);
85123474Swpaul__stdcall static void ndis_resetdone_func(ndis_handle, ndis_status, uint8_t);
86124100Swpaul__stdcall static void ndis_sendrsrcavail_func(ndis_handle);
87123474Swpaul
88125057Swpaulstruct nd_head ndis_devhead;
89125057Swpaul
90124724Swpaulstruct ndis_req {
91124724Swpaul	void			(*nr_func)(void *);
92124724Swpaul	void			*nr_arg;
93124724Swpaul	int			nr_exit;
94124724Swpaul	STAILQ_ENTRY(ndis_req)	link;
95124724Swpaul};
96124724Swpaul
97124724Swpaulstruct ndisproc {
98124724Swpaul	struct ndisqhead	*np_q;
99124724Swpaul	struct proc		*np_p;
100125814Swpaul	int			np_state;
101124724Swpaul};
102124724Swpaul
103128546Swpaulstatic void ndis_return(void *);
104124697Swpaulstatic int ndis_create_kthreads(void);
105124697Swpaulstatic void ndis_destroy_kthreads(void);
106124697Swpaulstatic void ndis_stop_thread(int);
107124697Swpaulstatic int ndis_enlarge_thrqueue(int);
108124697Swpaulstatic int ndis_shrink_thrqueue(int);
109124697Swpaulstatic void ndis_runq(void *);
110124697Swpaul
111124060Swpaulstatic uma_zone_t ndis_packet_zone, ndis_buffer_zone;
112128229Swpaulstruct mtx ndis_thr_mtx;
113140267Swpaulstruct mtx ndis_req_mtx;
114124697Swpaulstatic STAILQ_HEAD(ndisqhead, ndis_req) ndis_ttodo;
115124697Swpaulstruct ndisqhead ndis_itodo;
116124697Swpaulstruct ndisqhead ndis_free;
117124697Swpaulstatic int ndis_jobs = 32;
118124060Swpaul
119124724Swpaulstatic struct ndisproc ndis_tproc;
120124724Swpaulstatic struct ndisproc ndis_iproc;
121124697Swpaul
122123474Swpaul/*
123123474Swpaul * This allows us to export our symbols to other modules.
124123474Swpaul * Note that we call ourselves 'ndisapi' to avoid a namespace
125123474Swpaul * collision with if_ndis.ko, which internally calls itself
126123474Swpaul * 'ndis.'
127123474Swpaul */
128123474Swpaulstatic int
129123474Swpaulndis_modevent(module_t mod, int cmd, void *arg)
130123474Swpaul{
131124060Swpaul	int			error = 0;
132124060Swpaul
133124060Swpaul	switch (cmd) {
134124060Swpaul	case MOD_LOAD:
135124122Swpaul		/* Initialize subsystems */
136124122Swpaul		ndis_libinit();
137124122Swpaul		ntoskrnl_libinit();
138124122Swpaul
139124122Swpaul		/* Initialize TX buffer UMA zone. */
140124060Swpaul		ndis_packet_zone = uma_zcreate("NDIS packet",
141124060Swpaul		    sizeof(ndis_packet), NULL, NULL, NULL,
142124060Swpaul		    NULL, UMA_ALIGN_PTR, 0);
143124060Swpaul		ndis_buffer_zone = uma_zcreate("NDIS buffer",
144124060Swpaul		    sizeof(ndis_buffer), NULL, NULL, NULL,
145124060Swpaul		    NULL, UMA_ALIGN_PTR, 0);
146124697Swpaul
147124697Swpaul		ndis_create_kthreads();
148124697Swpaul
149125057Swpaul		TAILQ_INIT(&ndis_devhead);
150125057Swpaul
151124060Swpaul		break;
152124060Swpaul	case MOD_SHUTDOWN:
153124697Swpaul		/* stop kthreads */
154124697Swpaul		ndis_destroy_kthreads();
155127311Swpaul		if (TAILQ_FIRST(&ndis_devhead) == NULL) {
156125006Swpaul			/* Shut down subsystems */
157125006Swpaul			ndis_libfini();
158125006Swpaul			ntoskrnl_libfini();
159124697Swpaul
160125006Swpaul			/* Remove zones */
161125006Swpaul			uma_zdestroy(ndis_packet_zone);
162125006Swpaul			uma_zdestroy(ndis_buffer_zone);
163125006Swpaul		}
164125006Swpaul		break;
165125006Swpaul	case MOD_UNLOAD:
166125006Swpaul		/* stop kthreads */
167125006Swpaul		ndis_destroy_kthreads();
168125006Swpaul
169124122Swpaul		/* Shut down subsystems */
170124122Swpaul		ndis_libfini();
171124122Swpaul		ntoskrnl_libfini();
172124122Swpaul
173124122Swpaul		/* Remove zones */
174124060Swpaul		uma_zdestroy(ndis_packet_zone);
175124060Swpaul		uma_zdestroy(ndis_buffer_zone);
176124060Swpaul		break;
177124060Swpaul	default:
178124060Swpaul		error = EINVAL;
179124060Swpaul		break;
180124060Swpaul	}
181124060Swpaul
182124060Swpaul	return(error);
183123474Swpaul}
184123474SwpaulDEV_MODULE(ndisapi, ndis_modevent, NULL);
185123474SwpaulMODULE_VERSION(ndisapi, 1);
186123474Swpaul
187124697Swpaul/*
188124697Swpaul * We create two kthreads for the NDIS subsystem. One of them is a task
189124697Swpaul * queue for performing various odd jobs. The other is an swi thread
190124697Swpaul * reserved exclusively for running interrupt handlers. The reason we
191124697Swpaul * have our own task queue is that there are some cases where we may
192124697Swpaul * need to sleep for a significant amount of time, and if we were to
193124697Swpaul * use one of the taskqueue threads, we might delay the processing
194124697Swpaul * of other pending tasks which might need to run right away. We have
195124697Swpaul * a separate swi thread because we don't want our interrupt handling
196124697Swpaul * to be delayed either.
197124697Swpaul *
198124697Swpaul * By default there are 32 jobs available to start, and another 8
199124697Swpaul * are added to the free list each time a new device is created.
200124697Swpaul */
201123474Swpaul
202124697Swpaulstatic void
203124724Swpaulndis_runq(arg)
204124724Swpaul	void			*arg;
205124697Swpaul{
206124697Swpaul	struct ndis_req		*r = NULL, *die = NULL;
207124724Swpaul	struct ndisproc		*p;
208124697Swpaul
209124724Swpaul	p = arg;
210124724Swpaul
211124697Swpaul	while (1) {
212124697Swpaul
213128229Swpaul		/* Sleep, but preserve our original priority. */
214128229Swpaul		ndis_thsuspend(p->np_p, 0);
215128229Swpaul
216124697Swpaul		/* Look for any jobs on the work queue. */
217124697Swpaul
218128229Swpaul		mtx_lock(&ndis_thr_mtx);
219125814Swpaul		p->np_state = NDIS_PSTATE_RUNNING;
220124724Swpaul		while(STAILQ_FIRST(p->np_q) != NULL) {
221124724Swpaul			r = STAILQ_FIRST(p->np_q);
222124724Swpaul			STAILQ_REMOVE_HEAD(p->np_q, link);
223128229Swpaul			mtx_unlock(&ndis_thr_mtx);
224124697Swpaul
225124697Swpaul			/* Do the work. */
226124697Swpaul
227124724Swpaul			if (r->nr_func != NULL)
228124724Swpaul				(*r->nr_func)(r->nr_arg);
229124697Swpaul
230128229Swpaul			mtx_lock(&ndis_thr_mtx);
231124697Swpaul			STAILQ_INSERT_HEAD(&ndis_free, r, link);
232124697Swpaul
233124697Swpaul			/* Check for a shutdown request */
234124697Swpaul
235124724Swpaul			if (r->nr_exit == TRUE)
236124697Swpaul				die = r;
237124697Swpaul		}
238125814Swpaul		p->np_state = NDIS_PSTATE_SLEEPING;
239128229Swpaul		mtx_unlock(&ndis_thr_mtx);
240124697Swpaul
241124697Swpaul		/* Bail if we were told to shut down. */
242124697Swpaul
243124697Swpaul		if (die != NULL)
244124697Swpaul			break;
245124697Swpaul	}
246124697Swpaul
247124697Swpaul	wakeup(die);
248130166Swpaul#if __FreeBSD_version < 502113
249130166Swpaul	mtx_lock(&Giant);
250130166Swpaul#endif
251124697Swpaul	kthread_exit(0);
252128229Swpaul	return; /* notreached */
253124697Swpaul}
254124697Swpaul
255124697Swpaulstatic int
256124697Swpaulndis_create_kthreads()
257124697Swpaul{
258124697Swpaul	struct ndis_req		*r;
259124697Swpaul	int			i, error = 0;
260124697Swpaul
261128229Swpaul	mtx_init(&ndis_thr_mtx, "NDIS thread lock",
262128229Swpaul	   MTX_NDIS_LOCK, MTX_DEF);
263140267Swpaul	mtx_init(&ndis_req_mtx, "NDIS request lock",
264140267Swpaul	   MTX_NDIS_LOCK, MTX_DEF);
265128229Swpaul
266124697Swpaul	STAILQ_INIT(&ndis_ttodo);
267124697Swpaul	STAILQ_INIT(&ndis_itodo);
268124697Swpaul	STAILQ_INIT(&ndis_free);
269124697Swpaul
270124697Swpaul	for (i = 0; i < ndis_jobs; i++) {
271124697Swpaul		r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK);
272124697Swpaul		if (r == NULL) {
273124697Swpaul			error = ENOMEM;
274124697Swpaul			break;
275124697Swpaul		}
276124697Swpaul		STAILQ_INSERT_HEAD(&ndis_free, r, link);
277124697Swpaul	}
278124697Swpaul
279124724Swpaul	if (error == 0) {
280124724Swpaul		ndis_tproc.np_q = &ndis_ttodo;
281125814Swpaul		ndis_tproc.np_state = NDIS_PSTATE_SLEEPING;
282124724Swpaul		error = kthread_create(ndis_runq, &ndis_tproc,
283127284Swpaul		    &ndis_tproc.np_p, RFHIGHPID,
284127284Swpaul		    NDIS_KSTACK_PAGES, "ndis taskqueue");
285124724Swpaul	}
286124697Swpaul
287124724Swpaul	if (error == 0) {
288124724Swpaul		ndis_iproc.np_q = &ndis_itodo;
289125814Swpaul		ndis_iproc.np_state = NDIS_PSTATE_SLEEPING;
290124724Swpaul		error = kthread_create(ndis_runq, &ndis_iproc,
291127284Swpaul		    &ndis_iproc.np_p, RFHIGHPID,
292127284Swpaul		    NDIS_KSTACK_PAGES, "ndis swi");
293124724Swpaul	}
294124697Swpaul
295124697Swpaul	if (error) {
296124697Swpaul		while ((r = STAILQ_FIRST(&ndis_free)) != NULL) {
297124697Swpaul			STAILQ_REMOVE_HEAD(&ndis_free, link);
298124697Swpaul			free(r, M_DEVBUF);
299124697Swpaul		}
300124697Swpaul		return(error);
301124697Swpaul	}
302124697Swpaul
303124697Swpaul	return(0);
304124697Swpaul}
305124697Swpaul
306124697Swpaulstatic void
307124697Swpaulndis_destroy_kthreads()
308124697Swpaul{
309124697Swpaul	struct ndis_req		*r;
310124697Swpaul
311124697Swpaul	/* Stop the threads. */
312124697Swpaul
313124697Swpaul	ndis_stop_thread(NDIS_TASKQUEUE);
314124697Swpaul	ndis_stop_thread(NDIS_SWI);
315124697Swpaul
316124697Swpaul	/* Destroy request structures. */
317124697Swpaul
318124697Swpaul	while ((r = STAILQ_FIRST(&ndis_free)) != NULL) {
319124697Swpaul		STAILQ_REMOVE_HEAD(&ndis_free, link);
320124697Swpaul		free(r, M_DEVBUF);
321124697Swpaul	}
322124697Swpaul
323140267Swpaul	mtx_destroy(&ndis_req_mtx);
324128229Swpaul	mtx_destroy(&ndis_thr_mtx);
325128229Swpaul
326124697Swpaul	return;
327124697Swpaul}
328124697Swpaul
329124697Swpaulstatic void
330124697Swpaulndis_stop_thread(t)
331124697Swpaul	int			t;
332124697Swpaul{
333124697Swpaul	struct ndis_req		*r;
334124697Swpaul	struct ndisqhead	*q;
335124697Swpaul	struct proc		*p;
336124697Swpaul
337124697Swpaul	if (t == NDIS_TASKQUEUE) {
338124697Swpaul		q = &ndis_ttodo;
339124724Swpaul		p = ndis_tproc.np_p;
340124697Swpaul	} else {
341124697Swpaul		q = &ndis_itodo;
342124724Swpaul		p = ndis_iproc.np_p;
343124697Swpaul	}
344124697Swpaul
345124697Swpaul	/* Create and post a special 'exit' job. */
346124697Swpaul
347128229Swpaul	mtx_lock(&ndis_thr_mtx);
348124697Swpaul	r = STAILQ_FIRST(&ndis_free);
349124697Swpaul	STAILQ_REMOVE_HEAD(&ndis_free, link);
350124724Swpaul	r->nr_func = NULL;
351124724Swpaul	r->nr_arg = NULL;
352124724Swpaul	r->nr_exit = TRUE;
353124697Swpaul	STAILQ_INSERT_TAIL(q, r, link);
354128229Swpaul	mtx_unlock(&ndis_thr_mtx);
355124697Swpaul
356128229Swpaul	ndis_thresume(p);
357124697Swpaul
358124697Swpaul	/* wait for thread exit */
359124697Swpaul
360128295Swpaul	tsleep(r, curthread->td_priority|PCATCH, "ndisthexit", hz * 60);
361124697Swpaul
362124697Swpaul	/* Now empty the job list. */
363124697Swpaul
364128229Swpaul	mtx_lock(&ndis_thr_mtx);
365124697Swpaul	while ((r = STAILQ_FIRST(q)) != NULL) {
366124697Swpaul		STAILQ_REMOVE_HEAD(q, link);
367124697Swpaul		STAILQ_INSERT_HEAD(&ndis_free, r, link);
368124697Swpaul	}
369128229Swpaul	mtx_unlock(&ndis_thr_mtx);
370124697Swpaul
371124697Swpaul	return;
372124697Swpaul}
373124697Swpaul
374124697Swpaulstatic int
375124697Swpaulndis_enlarge_thrqueue(cnt)
376124697Swpaul	int			cnt;
377124697Swpaul{
378124697Swpaul	struct ndis_req		*r;
379124697Swpaul	int			i;
380124697Swpaul
381124697Swpaul	for (i = 0; i < cnt; i++) {
382124697Swpaul		r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK);
383124697Swpaul		if (r == NULL)
384124697Swpaul			return(ENOMEM);
385128229Swpaul		mtx_lock(&ndis_thr_mtx);
386124697Swpaul		STAILQ_INSERT_HEAD(&ndis_free, r, link);
387124697Swpaul		ndis_jobs++;
388128229Swpaul		mtx_unlock(&ndis_thr_mtx);
389124697Swpaul	}
390124697Swpaul
391124697Swpaul	return(0);
392124697Swpaul}
393124697Swpaul
394124697Swpaulstatic int
395124697Swpaulndis_shrink_thrqueue(cnt)
396124697Swpaul	int			cnt;
397124697Swpaul{
398124697Swpaul	struct ndis_req		*r;
399124697Swpaul	int			i;
400124697Swpaul
401124697Swpaul	for (i = 0; i < cnt; i++) {
402128229Swpaul		mtx_lock(&ndis_thr_mtx);
403124697Swpaul		r = STAILQ_FIRST(&ndis_free);
404124697Swpaul		if (r == NULL) {
405128229Swpaul			mtx_unlock(&ndis_thr_mtx);
406124697Swpaul			return(ENOMEM);
407124697Swpaul		}
408124697Swpaul		STAILQ_REMOVE_HEAD(&ndis_free, link);
409124697Swpaul		ndis_jobs--;
410128229Swpaul		mtx_unlock(&ndis_thr_mtx);
411124697Swpaul		free(r, M_DEVBUF);
412124697Swpaul	}
413124697Swpaul
414124697Swpaul	return(0);
415124697Swpaul}
416124697Swpaul
417124697Swpaulint
418127393Swpaulndis_unsched(func, arg, t)
419127393Swpaul	void			(*func)(void *);
420127393Swpaul	void			*arg;
421127393Swpaul	int			t;
422127393Swpaul{
423127393Swpaul	struct ndis_req		*r;
424127393Swpaul	struct ndisqhead	*q;
425127393Swpaul	struct proc		*p;
426127393Swpaul
427127393Swpaul	if (t == NDIS_TASKQUEUE) {
428127393Swpaul		q = &ndis_ttodo;
429127393Swpaul		p = ndis_tproc.np_p;
430127393Swpaul	} else {
431127393Swpaul		q = &ndis_itodo;
432127393Swpaul		p = ndis_iproc.np_p;
433127393Swpaul	}
434127393Swpaul
435128229Swpaul	mtx_lock(&ndis_thr_mtx);
436127393Swpaul	STAILQ_FOREACH(r, q, link) {
437127393Swpaul		if (r->nr_func == func && r->nr_arg == arg) {
438127393Swpaul			STAILQ_REMOVE(q, r, ndis_req, link);
439127393Swpaul			STAILQ_INSERT_HEAD(&ndis_free, r, link);
440128229Swpaul			mtx_unlock(&ndis_thr_mtx);
441127393Swpaul			return(0);
442127393Swpaul		}
443127393Swpaul	}
444127393Swpaul
445128229Swpaul	mtx_unlock(&ndis_thr_mtx);
446127393Swpaul
447127393Swpaul	return(ENOENT);
448127393Swpaul}
449127393Swpaul
450127393Swpaulint
451124697Swpaulndis_sched(func, arg, t)
452124697Swpaul	void			(*func)(void *);
453124697Swpaul	void			*arg;
454124697Swpaul	int			t;
455124697Swpaul{
456124697Swpaul	struct ndis_req		*r;
457124697Swpaul	struct ndisqhead	*q;
458124697Swpaul	struct proc		*p;
459125814Swpaul	int			s;
460124697Swpaul
461124697Swpaul	if (t == NDIS_TASKQUEUE) {
462124697Swpaul		q = &ndis_ttodo;
463124724Swpaul		p = ndis_tproc.np_p;
464124697Swpaul	} else {
465124697Swpaul		q = &ndis_itodo;
466124724Swpaul		p = ndis_iproc.np_p;
467124697Swpaul	}
468124697Swpaul
469128229Swpaul	mtx_lock(&ndis_thr_mtx);
470124697Swpaul	/*
471124697Swpaul	 * Check to see if an instance of this job is already
472124697Swpaul	 * pending. If so, don't bother queuing it again.
473124697Swpaul	 */
474124697Swpaul	STAILQ_FOREACH(r, q, link) {
475124724Swpaul		if (r->nr_func == func && r->nr_arg == arg) {
476128229Swpaul			mtx_unlock(&ndis_thr_mtx);
477124697Swpaul			return(0);
478124697Swpaul		}
479124697Swpaul	}
480124697Swpaul	r = STAILQ_FIRST(&ndis_free);
481124697Swpaul	if (r == NULL) {
482128229Swpaul		mtx_unlock(&ndis_thr_mtx);
483124697Swpaul		return(EAGAIN);
484124697Swpaul	}
485124697Swpaul	STAILQ_REMOVE_HEAD(&ndis_free, link);
486124724Swpaul	r->nr_func = func;
487124724Swpaul	r->nr_arg = arg;
488124724Swpaul	r->nr_exit = FALSE;
489124697Swpaul	STAILQ_INSERT_TAIL(q, r, link);
490125814Swpaul	if (t == NDIS_TASKQUEUE)
491125814Swpaul		s = ndis_tproc.np_state;
492125814Swpaul	else
493125814Swpaul		s = ndis_iproc.np_state;
494128229Swpaul	mtx_unlock(&ndis_thr_mtx);
495124697Swpaul
496125814Swpaul	/*
497125814Swpaul	 * Post the job, but only if the thread is actually blocked
498125814Swpaul	 * on its own suspend call. If a driver queues up a job with
499125814Swpaul	 * NdisScheduleWorkItem() which happens to do a KeWaitForObject(),
500125814Swpaul	 * it may suspend there, and in that case we don't want to wake
501125814Swpaul	 * it up until KeWaitForObject() gets woken up on its own.
502125814Swpaul	 */
503125814Swpaul	if (s == NDIS_PSTATE_SLEEPING)
504128229Swpaul		ndis_thresume(p);
505124697Swpaul
506124697Swpaul	return(0);
507124697Swpaul}
508124697Swpaul
509128229Swpaulint
510128229Swpaulndis_thsuspend(p, timo)
511128229Swpaul	struct proc		*p;
512128229Swpaul	int			timo;
513128229Swpaul{
514128229Swpaul	int			error;
515128229Swpaul
516128229Swpaul	PROC_LOCK(p);
517128229Swpaul	error = msleep(&p->p_siglist, &p->p_mtx,
518128229Swpaul	    curthread->td_priority|PDROP, "ndissp", timo);
519128229Swpaul	return(error);
520128229Swpaul}
521128229Swpaul
522128229Swpaulvoid
523128229Swpaulndis_thresume(p)
524128229Swpaul	struct proc		*p;
525128229Swpaul{
526128229Swpaul	wakeup(&p->p_siglist);
527128229Swpaul	return;
528128229Swpaul}
529128229Swpaul
530123474Swpaul__stdcall static void
531124100Swpaulndis_sendrsrcavail_func(adapter)
532124100Swpaul	ndis_handle		adapter;
533124100Swpaul{
534124100Swpaul	return;
535124100Swpaul}
536124100Swpaul
537124100Swpaul__stdcall static void
538123474Swpaulndis_status_func(adapter, status, sbuf, slen)
539123474Swpaul	ndis_handle		adapter;
540123474Swpaul	ndis_status		status;
541123474Swpaul	void			*sbuf;
542123474Swpaul	uint32_t		slen;
543123474Swpaul{
544124060Swpaul	ndis_miniport_block	*block;
545124060Swpaul	block = adapter;
546124060Swpaul
547124409Swpaul	if (block->nmb_ifp->if_flags & IFF_DEBUG)
548124409Swpaul		device_printf (block->nmb_dev, "status: %x\n", status);
549123474Swpaul	return;
550123474Swpaul}
551123474Swpaul
552123474Swpaul__stdcall static void
553123474Swpaulndis_statusdone_func(adapter)
554123474Swpaul	ndis_handle		adapter;
555123474Swpaul{
556124060Swpaul	ndis_miniport_block	*block;
557124060Swpaul	block = adapter;
558124060Swpaul
559124409Swpaul	if (block->nmb_ifp->if_flags & IFF_DEBUG)
560124409Swpaul		device_printf (block->nmb_dev, "status complete\n");
561123474Swpaul	return;
562123474Swpaul}
563123474Swpaul
564123474Swpaul__stdcall static void
565123474Swpaulndis_setdone_func(adapter, status)
566123474Swpaul	ndis_handle		adapter;
567123474Swpaul	ndis_status		status;
568123474Swpaul{
569123695Swpaul	ndis_miniport_block	*block;
570123695Swpaul	block = adapter;
571123695Swpaul
572123695Swpaul	block->nmb_setstat = status;
573140267Swpaul	wakeup(&block->nmb_setstat);
574123474Swpaul	return;
575123474Swpaul}
576123474Swpaul
577123474Swpaul__stdcall static void
578123535Swpaulndis_getdone_func(adapter, status)
579123535Swpaul	ndis_handle		adapter;
580123535Swpaul	ndis_status		status;
581123535Swpaul{
582123695Swpaul	ndis_miniport_block	*block;
583123695Swpaul	block = adapter;
584123695Swpaul
585123695Swpaul	block->nmb_getstat = status;
586140267Swpaul	wakeup(&block->nmb_getstat);
587123535Swpaul	return;
588123535Swpaul}
589123535Swpaul
590123535Swpaul__stdcall static void
591123474Swpaulndis_resetdone_func(adapter, status, addressingreset)
592123474Swpaul	ndis_handle		adapter;
593123474Swpaul	ndis_status		status;
594123474Swpaul	uint8_t			addressingreset;
595123474Swpaul{
596124060Swpaul	ndis_miniport_block	*block;
597124060Swpaul	block = adapter;
598124060Swpaul
599124409Swpaul	if (block->nmb_ifp->if_flags & IFF_DEBUG)
600124409Swpaul		device_printf (block->nmb_dev, "reset done...\n");
601127887Swpaul	wakeup(block->nmb_ifp);
602123474Swpaul	return;
603123474Swpaul}
604123474Swpaul
605123474Swpaulint
606123474Swpaulndis_create_sysctls(arg)
607123474Swpaul	void			*arg;
608123474Swpaul{
609123474Swpaul	struct ndis_softc	*sc;
610123474Swpaul	ndis_cfg		*vals;
611123474Swpaul	char			buf[256];
612132973Swpaul	struct sysctl_oid	*oidp;
613132973Swpaul	struct sysctl_ctx_entry	*e;
614123474Swpaul
615123474Swpaul	if (arg == NULL)
616123474Swpaul		return(EINVAL);
617123474Swpaul
618123474Swpaul	sc = arg;
619123474Swpaul	vals = sc->ndis_regvals;
620123474Swpaul
621123474Swpaul	TAILQ_INIT(&sc->ndis_cfglist_head);
622123474Swpaul
623130097Sdes#if __FreeBSD_version < 502113
624123474Swpaul	/* Create the sysctl tree. */
625123474Swpaul
626123474Swpaul	sc->ndis_tree = SYSCTL_ADD_NODE(&sc->ndis_ctx,
627123474Swpaul	    SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
628123474Swpaul	    device_get_nameunit(sc->ndis_dev), CTLFLAG_RD, 0,
629123474Swpaul	    device_get_desc(sc->ndis_dev));
630123474Swpaul
631130097Sdes#endif
632123474Swpaul	/* Add the driver-specific registry keys. */
633123474Swpaul
634123474Swpaul	vals = sc->ndis_regvals;
635123474Swpaul	while(1) {
636123474Swpaul		if (vals->nc_cfgkey == NULL)
637123474Swpaul			break;
638123620Swpaul		if (vals->nc_idx != sc->ndis_devidx) {
639123620Swpaul			vals++;
640123620Swpaul			continue;
641123620Swpaul		}
642132973Swpaul
643132973Swpaul		/* See if we already have a sysctl with this name */
644132973Swpaul
645132973Swpaul		oidp = NULL;
646130097Sdes#if __FreeBSD_version < 502113
647132973Swpaul		TAILQ_FOREACH(e, &sc->ndis_ctx, link) {
648132973Swpaul#else
649132973Swpaul		TAILQ_FOREACH(e, device_get_sysctl_ctx(sc->ndis_dev), link) {
650132973Swpaul#endif
651132973Swpaul                	oidp = e->entry;
652132973Swpaul			if (ndis_strcasecmp(oidp->oid_name,
653132973Swpaul			    vals->nc_cfgkey) == 0)
654132973Swpaul				break;
655132973Swpaul			oidp = NULL;
656132973Swpaul		}
657132973Swpaul
658132973Swpaul		if (oidp != NULL) {
659132973Swpaul			vals++;
660132973Swpaul			continue;
661132973Swpaul		}
662132973Swpaul
663132973Swpaul#if __FreeBSD_version < 502113
664123474Swpaul		SYSCTL_ADD_STRING(&sc->ndis_ctx,
665123474Swpaul		    SYSCTL_CHILDREN(sc->ndis_tree),
666130097Sdes#else
667130097Sdes		SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev),
668130097Sdes		    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)),
669130097Sdes#endif
670123474Swpaul		    OID_AUTO, vals->nc_cfgkey,
671123474Swpaul		    CTLFLAG_RW, vals->nc_val,
672123474Swpaul		    sizeof(vals->nc_val),
673123474Swpaul		    vals->nc_cfgdesc);
674123474Swpaul		vals++;
675123474Swpaul	}
676123474Swpaul
677123474Swpaul	/* Now add a couple of builtin keys. */
678123474Swpaul
679123474Swpaul	/*
680123474Swpaul	 * Environment can be either Windows (0) or WindowsNT (1).
681123474Swpaul	 * We qualify as the latter.
682123474Swpaul	 */
683123474Swpaul	ndis_add_sysctl(sc, "Environment",
684123474Swpaul	    "Windows environment", "1", CTLFLAG_RD);
685123474Swpaul
686123474Swpaul	/* NDIS version should be 5.1. */
687123474Swpaul	ndis_add_sysctl(sc, "NdisVersion",
688123474Swpaul	    "NDIS API Version", "0x00050001", CTLFLAG_RD);
689123474Swpaul
690123474Swpaul	/* Bus type (PCI, PCMCIA, etc...) */
691124272Swpaul	sprintf(buf, "%d", (int)sc->ndis_iftype);
692123474Swpaul	ndis_add_sysctl(sc, "BusType", "Bus Type", buf, CTLFLAG_RD);
693123474Swpaul
694123474Swpaul	if (sc->ndis_res_io != NULL) {
695124272Swpaul		sprintf(buf, "0x%lx", rman_get_start(sc->ndis_res_io));
696123474Swpaul		ndis_add_sysctl(sc, "IOBaseAddress",
697123474Swpaul		    "Base I/O Address", buf, CTLFLAG_RD);
698123474Swpaul	}
699123474Swpaul
700123474Swpaul	if (sc->ndis_irq != NULL) {
701124272Swpaul		sprintf(buf, "%lu", rman_get_start(sc->ndis_irq));
702123474Swpaul		ndis_add_sysctl(sc, "InterruptNumber",
703123474Swpaul		    "Interrupt Number", buf, CTLFLAG_RD);
704123474Swpaul	}
705123474Swpaul
706123474Swpaul	return(0);
707123474Swpaul}
708123474Swpaul
709123474Swpaulint
710123474Swpaulndis_add_sysctl(arg, key, desc, val, flag)
711123474Swpaul	void			*arg;
712123474Swpaul	char			*key;
713123474Swpaul	char			*desc;
714123474Swpaul	char			*val;
715123474Swpaul	int			flag;
716123474Swpaul{
717123474Swpaul	struct ndis_softc	*sc;
718123474Swpaul	struct ndis_cfglist	*cfg;
719123474Swpaul	char			descstr[256];
720123474Swpaul
721123474Swpaul	sc = arg;
722123474Swpaul
723123474Swpaul	cfg = malloc(sizeof(struct ndis_cfglist), M_DEVBUF, M_NOWAIT|M_ZERO);
724123474Swpaul
725123474Swpaul	if (cfg == NULL)
726123474Swpaul		return(ENOMEM);
727123474Swpaul
728123474Swpaul	cfg->ndis_cfg.nc_cfgkey = strdup(key, M_DEVBUF);
729123474Swpaul	if (desc == NULL) {
730123474Swpaul		snprintf(descstr, sizeof(descstr), "%s (dynamic)", key);
731123474Swpaul		cfg->ndis_cfg.nc_cfgdesc = strdup(descstr, M_DEVBUF);
732123474Swpaul	} else
733123474Swpaul		cfg->ndis_cfg.nc_cfgdesc = strdup(desc, M_DEVBUF);
734123474Swpaul	strcpy(cfg->ndis_cfg.nc_val, val);
735123474Swpaul
736123474Swpaul	TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link);
737123474Swpaul
738130097Sdes#if __FreeBSD_version < 502113
739123474Swpaul	SYSCTL_ADD_STRING(&sc->ndis_ctx, SYSCTL_CHILDREN(sc->ndis_tree),
740130097Sdes#else
741130097Sdes	SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev),
742130097Sdes	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)),
743130097Sdes#endif
744123474Swpaul	    OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag,
745123474Swpaul	    cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val),
746123474Swpaul	    cfg->ndis_cfg.nc_cfgdesc);
747123474Swpaul
748123474Swpaul	return(0);
749123474Swpaul}
750123474Swpaul
751123474Swpaulint
752123474Swpaulndis_flush_sysctls(arg)
753123474Swpaul	void			*arg;
754123474Swpaul{
755123474Swpaul	struct ndis_softc	*sc;
756123474Swpaul	struct ndis_cfglist	*cfg;
757123474Swpaul
758123474Swpaul	sc = arg;
759123474Swpaul
760123474Swpaul	while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) {
761123474Swpaul		cfg = TAILQ_FIRST(&sc->ndis_cfglist_head);
762123474Swpaul		TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link);
763123474Swpaul		free(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF);
764123474Swpaul		free(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF);
765123474Swpaul		free(cfg, M_DEVBUF);
766123474Swpaul	}
767123474Swpaul
768123474Swpaul	return(0);
769123474Swpaul}
770123474Swpaul
771128546Swpaulstatic void
772128546Swpaulndis_return(arg)
773123474Swpaul	void			*arg;
774123474Swpaul{
775123474Swpaul	struct ndis_softc	*sc;
776128546Swpaul	__stdcall ndis_return_handler	returnfunc;
777123474Swpaul	ndis_handle		adapter;
778123535Swpaul	ndis_packet		*p;
779128229Swpaul	uint8_t			irql;
780123474Swpaul
781128546Swpaul	p = arg;
782128546Swpaul	sc = p->np_softc;
783128546Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
784128546Swpaul
785128546Swpaul	if (adapter == NULL)
786128546Swpaul		return;
787128546Swpaul
788128546Swpaul	returnfunc = sc->ndis_chars.nmc_return_packet_func;
789140751Swpaul	irql = KeRaiseIrql(DISPATCH_LEVEL);
790128546Swpaul	returnfunc(adapter, p);
791140751Swpaul	KeLowerIrql(irql);
792128546Swpaul
793128546Swpaul	return;
794128546Swpaul}
795128546Swpaul
796128546Swpaulvoid
797128546Swpaulndis_return_packet(buf, arg)
798128546Swpaul	void			*buf;	/* not used */
799128546Swpaul	void			*arg;
800128546Swpaul{
801128546Swpaul	ndis_packet		*p;
802128546Swpaul
803123826Swpaul	if (arg == NULL)
804123474Swpaul		return;
805123474Swpaul
806123826Swpaul	p = arg;
807123535Swpaul
808123535Swpaul	/* Decrement refcount. */
809123826Swpaul	p->np_refcnt--;
810123535Swpaul
811123535Swpaul	/* Release packet when refcount hits zero, otherwise return. */
812123826Swpaul	if (p->np_refcnt)
813123535Swpaul		return;
814123536Swpaul
815128546Swpaul	ndis_sched(ndis_return, p, NDIS_SWI);
816123858Swpaul
817123474Swpaul	return;
818123474Swpaul}
819123474Swpaul
820123848Swpaulvoid
821123848Swpaulndis_free_bufs(b0)
822123848Swpaul	ndis_buffer		*b0;
823123848Swpaul{
824123848Swpaul	ndis_buffer		*next;
825123848Swpaul
826123848Swpaul	if (b0 == NULL)
827123848Swpaul		return;
828123848Swpaul
829123848Swpaul	while(b0 != NULL) {
830140751Swpaul		next = b0->mdl_next;
831124060Swpaul		uma_zfree (ndis_buffer_zone, b0);
832123848Swpaul		b0 = next;
833123848Swpaul	}
834123848Swpaul
835123848Swpaul	return;
836123848Swpaul}
837123848Swpaul
838123848Swpaulvoid
839123848Swpaulndis_free_packet(p)
840123848Swpaul	ndis_packet		*p;
841123848Swpaul{
842123848Swpaul	if (p == NULL)
843123848Swpaul		return;
844123848Swpaul
845123848Swpaul	ndis_free_bufs(p->np_private.npp_head);
846124060Swpaul	uma_zfree(ndis_packet_zone, p);
847123848Swpaul
848123848Swpaul	return;
849123848Swpaul}
850123848Swpaul
851123474Swpaulint
852123474Swpaulndis_convert_res(arg)
853123474Swpaul	void			*arg;
854123474Swpaul{
855123474Swpaul	struct ndis_softc	*sc;
856123474Swpaul	ndis_resource_list	*rl = NULL;
857123474Swpaul	cm_partial_resource_desc	*prd = NULL;
858123474Swpaul	ndis_miniport_block	*block;
859123976Swpaul	device_t		dev;
860123976Swpaul	struct resource_list	*brl;
861127411Swpaul	struct resource_list	brl_rev;
862127411Swpaul	struct resource_list_entry	*brle, *n;
863127411Swpaul	int 			error = 0;
864123474Swpaul
865123474Swpaul	sc = arg;
866123474Swpaul	block = &sc->ndis_block;
867123976Swpaul	dev = sc->ndis_dev;
868123474Swpaul
869128012Swpaul	SLIST_INIT(&brl_rev);
870128012Swpaul
871123474Swpaul	rl = malloc(sizeof(ndis_resource_list) +
872123474Swpaul	    (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)),
873123474Swpaul	    M_DEVBUF, M_NOWAIT|M_ZERO);
874123474Swpaul
875123474Swpaul	if (rl == NULL)
876123474Swpaul		return(ENOMEM);
877123474Swpaul
878123474Swpaul	rl->cprl_version = 5;
879123474Swpaul	rl->cprl_version = 1;
880123474Swpaul	rl->cprl_count = sc->ndis_rescnt;
881123474Swpaul	prd = rl->cprl_partial_descs;
882123474Swpaul
883131953Swpaul	brl = BUS_GET_RESOURCE_LIST(dev, dev);
884131953Swpaul
885123976Swpaul	if (brl != NULL) {
886127411Swpaul
887127411Swpaul		/*
888127411Swpaul		 * We have a small problem. Some PCI devices have
889127411Swpaul		 * multiple I/O ranges. Windows orders them starting
890127411Swpaul		 * from lowest numbered BAR to highest. We discover
891127411Swpaul		 * them in that order too, but insert them into a singly
892127411Swpaul		 * linked list head first, which means when time comes
893127411Swpaul		 * to traverse the list, we enumerate them in reverse
894127411Swpaul		 * order. This screws up some drivers which expect the
895127411Swpaul		 * BARs to be in ascending order so that they can choose
896127411Swpaul		 * the "first" one as their register space. Unfortunately,
897127411Swpaul		 * in order to fix this, we have to create our own
898127411Swpaul		 * temporary list with the entries in reverse order.
899127411Swpaul		 */
900123976Swpaul		SLIST_FOREACH(brle, brl, link) {
901127411Swpaul			n = malloc(sizeof(struct resource_list_entry),
902127411Swpaul			    M_TEMP, M_NOWAIT);
903127411Swpaul			if (n == NULL) {
904127411Swpaul				error = ENOMEM;
905127411Swpaul				goto bad;
906127411Swpaul			}
907127411Swpaul			bcopy((char *)brle, (char *)n,
908127411Swpaul			    sizeof(struct resource_list_entry));
909127411Swpaul			SLIST_INSERT_HEAD(&brl_rev, n, link);
910127411Swpaul		}
911127411Swpaul
912127411Swpaul		SLIST_FOREACH(brle, &brl_rev, link) {
913123976Swpaul			switch (brle->type) {
914123976Swpaul			case SYS_RES_IOPORT:
915123976Swpaul				prd->cprd_type = CmResourceTypePort;
916127552Swpaul				prd->cprd_flags = CM_RESOURCE_PORT_IO;
917127552Swpaul				prd->cprd_sharedisp =
918127552Swpaul				    CmResourceShareDeviceExclusive;
919123976Swpaul				prd->u.cprd_port.cprd_start.np_quad =
920123976Swpaul				    brle->start;
921123976Swpaul				prd->u.cprd_port.cprd_len = brle->count;
922123976Swpaul				break;
923123976Swpaul			case SYS_RES_MEMORY:
924123976Swpaul				prd->cprd_type = CmResourceTypeMemory;
925127552Swpaul				prd->cprd_flags =
926127552Swpaul				    CM_RESOURCE_MEMORY_READ_WRITE;
927127552Swpaul				prd->cprd_sharedisp =
928127552Swpaul				    CmResourceShareDeviceExclusive;
929123976Swpaul				prd->u.cprd_port.cprd_start.np_quad =
930123976Swpaul				    brle->start;
931123976Swpaul				prd->u.cprd_port.cprd_len = brle->count;
932123976Swpaul				break;
933123976Swpaul			case SYS_RES_IRQ:
934123976Swpaul				prd->cprd_type = CmResourceTypeInterrupt;
935127552Swpaul				prd->cprd_flags = 0;
936127552Swpaul				prd->cprd_sharedisp =
937127552Swpaul				    CmResourceShareDeviceExclusive;
938123976Swpaul				prd->u.cprd_intr.cprd_level = brle->start;
939123976Swpaul				prd->u.cprd_intr.cprd_vector = brle->start;
940123976Swpaul				prd->u.cprd_intr.cprd_affinity = 0;
941123976Swpaul				break;
942123976Swpaul			default:
943123976Swpaul				break;
944123976Swpaul			}
945123976Swpaul			prd++;
946123976Swpaul		}
947123474Swpaul	}
948123474Swpaul
949123474Swpaul	block->nmb_rlist = rl;
950123474Swpaul
951127411Swpaulbad:
952127411Swpaul
953127411Swpaul	while (!SLIST_EMPTY(&brl_rev)) {
954127411Swpaul		n = SLIST_FIRST(&brl_rev);
955127411Swpaul		SLIST_REMOVE_HEAD(&brl_rev, link);
956127411Swpaul		free (n, M_TEMP);
957127411Swpaul	}
958127411Swpaul
959127411Swpaul	return(error);
960123474Swpaul}
961123474Swpaul
962123474Swpaul/*
963123474Swpaul * Map an NDIS packet to an mbuf list. When an NDIS driver receives a
964123474Swpaul * packet, it will hand it to us in the form of an ndis_packet,
965123474Swpaul * which we need to convert to an mbuf that is then handed off
966123474Swpaul * to the stack. Note: we configure the mbuf list so that it uses
967123474Swpaul * the memory regions specified by the ndis_buffer structures in
968123474Swpaul * the ndis_packet as external storage. In most cases, this will
969123474Swpaul * point to a memory region allocated by the driver (either by
970123474Swpaul * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect
971123474Swpaul * the driver to handle free()ing this region for is, so we set up
972123474Swpaul * a dummy no-op free handler for it.
973123474Swpaul */
974123474Swpaul
975123474Swpaulint
976123474Swpaulndis_ptom(m0, p)
977123474Swpaul	struct mbuf		**m0;
978123474Swpaul	ndis_packet		*p;
979123474Swpaul{
980123474Swpaul	struct mbuf		*m, *prev = NULL;
981123474Swpaul	ndis_buffer		*buf;
982123474Swpaul	ndis_packet_private	*priv;
983123474Swpaul	uint32_t		totlen = 0;
984123474Swpaul
985123474Swpaul	if (p == NULL || m0 == NULL)
986123474Swpaul		return(EINVAL);
987123474Swpaul
988123474Swpaul	priv = &p->np_private;
989123474Swpaul	buf = priv->npp_head;
990123826Swpaul	p->np_refcnt = 0;
991123474Swpaul
992140751Swpaul	for (buf = priv->npp_head; buf != NULL; buf = buf->mdl_next) {
993123474Swpaul		if (buf == priv->npp_head)
994123474Swpaul			MGETHDR(m, M_DONTWAIT, MT_HEADER);
995123474Swpaul		else
996123474Swpaul			MGET(m, M_DONTWAIT, MT_DATA);
997123474Swpaul		if (m == NULL) {
998123474Swpaul			m_freem(*m0);
999123474Swpaul			*m0 = NULL;
1000123474Swpaul			return(ENOBUFS);
1001123474Swpaul		}
1002140751Swpaul		m->m_len = MmGetMdlByteCount(buf);
1003140751Swpaul		m->m_data = MmGetMdlVirtualAddress(buf);
1004123535Swpaul		MEXTADD(m, m->m_data, m->m_len, ndis_return_packet,
1005123826Swpaul		    p, 0, EXT_NDIS);
1006123826Swpaul		p->np_refcnt++;
1007123474Swpaul		totlen += m->m_len;
1008123474Swpaul		if (m->m_flags & MT_HEADER)
1009123474Swpaul			*m0 = m;
1010123474Swpaul		else
1011123474Swpaul			prev->m_next = m;
1012123474Swpaul		prev = m;
1013123474Swpaul	}
1014123474Swpaul
1015123474Swpaul	(*m0)->m_pkthdr.len = totlen;
1016123474Swpaul
1017123474Swpaul	return(0);
1018123474Swpaul}
1019123474Swpaul
1020123474Swpaul/*
1021123474Swpaul * Create an mbuf chain from an NDIS packet chain.
1022123474Swpaul * This is used mainly when transmitting packets, where we need
1023123474Swpaul * to turn an mbuf off an interface's send queue and transform it
1024123474Swpaul * into an NDIS packet which will be fed into the NDIS driver's
1025123474Swpaul * send routine.
1026123474Swpaul *
1027123474Swpaul * NDIS packets consist of two parts: an ndis_packet structure,
1028123474Swpaul * which is vaguely analagous to the pkthdr portion of an mbuf,
1029123474Swpaul * and one or more ndis_buffer structures, which define the
1030123474Swpaul * actual memory segments in which the packet data resides.
1031123474Swpaul * We need to allocate one ndis_buffer for each mbuf in a chain,
1032123474Swpaul * plus one ndis_packet as the header.
1033123474Swpaul */
1034123474Swpaul
1035123474Swpaulint
1036123474Swpaulndis_mtop(m0, p)
1037123474Swpaul	struct mbuf		*m0;
1038123474Swpaul	ndis_packet		**p;
1039123474Swpaul{
1040123474Swpaul	struct mbuf		*m;
1041123474Swpaul	ndis_buffer		*buf = NULL, *prev = NULL;
1042123474Swpaul	ndis_packet_private	*priv;
1043123474Swpaul
1044123474Swpaul	if (p == NULL || m0 == NULL)
1045123474Swpaul		return(EINVAL);
1046123474Swpaul
1047123474Swpaul	/* If caller didn't supply a packet, make one. */
1048123474Swpaul	if (*p == NULL) {
1049124060Swpaul		*p = uma_zalloc(ndis_packet_zone, M_NOWAIT|M_ZERO);
1050123474Swpaul
1051123474Swpaul		if (*p == NULL)
1052123474Swpaul			return(ENOMEM);
1053123474Swpaul	}
1054123474Swpaul
1055123474Swpaul	priv = &(*p)->np_private;
1056123474Swpaul	priv->npp_totlen = m0->m_pkthdr.len;
1057123474Swpaul        priv->npp_packetooboffset = offsetof(ndis_packet, np_oob);
1058124278Swpaul	priv->npp_ndispktflags = NDIS_PACKET_ALLOCATED_BY_NDIS;
1059123474Swpaul
1060123474Swpaul	for (m = m0; m != NULL; m = m->m_next) {
1061123810Salfred		if (m->m_len == 0)
1062123474Swpaul			continue;
1063124060Swpaul		buf = uma_zalloc(ndis_buffer_zone, M_NOWAIT | M_ZERO);
1064123474Swpaul		if (buf == NULL) {
1065123474Swpaul			ndis_free_packet(*p);
1066123474Swpaul			*p = NULL;
1067123474Swpaul			return(ENOMEM);
1068123474Swpaul		}
1069123474Swpaul
1070140751Swpaul		MmInitializeMdl(buf, m->m_data, m->m_len);
1071123474Swpaul		if (priv->npp_head == NULL)
1072123474Swpaul			priv->npp_head = buf;
1073123474Swpaul		else
1074140751Swpaul			prev->mdl_next = buf;
1075123474Swpaul		prev = buf;
1076123474Swpaul	}
1077123474Swpaul
1078123474Swpaul	priv->npp_tail = buf;
1079124060Swpaul	priv->npp_totlen = m0->m_pkthdr.len;
1080123474Swpaul
1081123474Swpaul	return(0);
1082123474Swpaul}
1083123474Swpaul
1084123474Swpaulint
1085123474Swpaulndis_get_supported_oids(arg, oids, oidcnt)
1086123474Swpaul	void			*arg;
1087123474Swpaul	ndis_oid		**oids;
1088123474Swpaul	int			*oidcnt;
1089123474Swpaul{
1090123474Swpaul	int			len, rval;
1091123474Swpaul	ndis_oid		*o;
1092123474Swpaul
1093123474Swpaul	if (arg == NULL || oids == NULL || oidcnt == NULL)
1094123474Swpaul		return(EINVAL);
1095123474Swpaul	len = 0;
1096123474Swpaul	ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len);
1097123474Swpaul
1098123474Swpaul	o = malloc(len, M_DEVBUF, M_NOWAIT);
1099123474Swpaul	if (o == NULL)
1100123474Swpaul		return(ENOMEM);
1101123474Swpaul
1102123474Swpaul	rval = ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, o, &len);
1103123474Swpaul
1104123474Swpaul	if (rval) {
1105123474Swpaul		free(o, M_DEVBUF);
1106123474Swpaul		return(rval);
1107123474Swpaul	}
1108123474Swpaul
1109123474Swpaul	*oids = o;
1110123474Swpaul	*oidcnt = len / 4;
1111123474Swpaul
1112123474Swpaul	return(0);
1113123474Swpaul}
1114123474Swpaul
1115123474Swpaulint
1116123474Swpaulndis_set_info(arg, oid, buf, buflen)
1117123474Swpaul	void			*arg;
1118123474Swpaul	ndis_oid		oid;
1119123474Swpaul	void			*buf;
1120123474Swpaul	int			*buflen;
1121123474Swpaul{
1122123474Swpaul	struct ndis_softc	*sc;
1123123474Swpaul	ndis_status		rval;
1124123474Swpaul	ndis_handle		adapter;
1125123474Swpaul	__stdcall ndis_setinfo_handler	setfunc;
1126123474Swpaul	uint32_t		byteswritten = 0, bytesneeded = 0;
1127123695Swpaul	int			error;
1128128229Swpaul	uint8_t			irql;
1129123474Swpaul
1130123474Swpaul	sc = arg;
1131125718Swpaul	NDIS_LOCK(sc);
1132123474Swpaul	setfunc = sc->ndis_chars.nmc_setinfo_func;
1133123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1134125718Swpaul	NDIS_UNLOCK(sc);
1135123474Swpaul
1136125718Swpaul	if (adapter == NULL || setfunc == NULL)
1137125676Swpaul		return(ENXIO);
1138125676Swpaul
1139140751Swpaul	KeAcquireSpinLock(&sc->ndis_block.nmb_lock, &irql);
1140123474Swpaul	rval = setfunc(adapter, oid, buf, *buflen,
1141123474Swpaul	    &byteswritten, &bytesneeded);
1142140751Swpaul	KeReleaseSpinLock(&sc->ndis_block.nmb_lock, irql);
1143123474Swpaul
1144123695Swpaul	if (rval == NDIS_STATUS_PENDING) {
1145140267Swpaul		mtx_lock(&ndis_req_mtx);
1146140267Swpaul		error = msleep(&sc->ndis_block.nmb_setstat,
1147140267Swpaul		    &ndis_req_mtx,
1148128229Swpaul		    curthread->td_priority|PDROP,
1149127887Swpaul		    "ndisset", 5 * hz);
1150123695Swpaul		rval = sc->ndis_block.nmb_setstat;
1151123695Swpaul	}
1152123695Swpaul
1153123474Swpaul	if (byteswritten)
1154123474Swpaul		*buflen = byteswritten;
1155123474Swpaul	if (bytesneeded)
1156123474Swpaul		*buflen = bytesneeded;
1157123474Swpaul
1158123474Swpaul	if (rval == NDIS_STATUS_INVALID_LENGTH)
1159123474Swpaul		return(ENOSPC);
1160123474Swpaul
1161123474Swpaul	if (rval == NDIS_STATUS_INVALID_OID)
1162123474Swpaul		return(EINVAL);
1163123474Swpaul
1164123474Swpaul	if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1165123474Swpaul	    rval == NDIS_STATUS_NOT_ACCEPTED)
1166123474Swpaul		return(ENOTSUP);
1167123474Swpaul
1168124809Swpaul	if (rval != NDIS_STATUS_SUCCESS)
1169124809Swpaul		return(ENODEV);
1170124809Swpaul
1171123474Swpaul	return(0);
1172123474Swpaul}
1173123474Swpaul
1174124202Swpaultypedef void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status);
1175124202Swpaul
1176123474Swpaulint
1177123474Swpaulndis_send_packets(arg, packets, cnt)
1178123474Swpaul	void			*arg;
1179123474Swpaul	ndis_packet		**packets;
1180123474Swpaul	int			cnt;
1181123474Swpaul{
1182123474Swpaul	struct ndis_softc	*sc;
1183123474Swpaul	ndis_handle		adapter;
1184123474Swpaul	__stdcall ndis_sendmulti_handler	sendfunc;
1185124202Swpaul	__stdcall ndis_senddone_func		senddonefunc;
1186124202Swpaul	int			i;
1187123858Swpaul	ndis_packet		*p;
1188128229Swpaul	uint8_t			irql;
1189123474Swpaul
1190123474Swpaul	sc = arg;
1191123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1192125718Swpaul	if (adapter == NULL)
1193125718Swpaul		return(ENXIO);
1194123474Swpaul	sendfunc = sc->ndis_chars.nmc_sendmulti_func;
1195124202Swpaul	senddonefunc = sc->ndis_block.nmb_senddone_func;
1196140751Swpaul	irql = KeRaiseIrql(DISPATCH_LEVEL);
1197123474Swpaul	sendfunc(adapter, packets, cnt);
1198140751Swpaul	KeLowerIrql(irql);
1199123474Swpaul
1200123858Swpaul	for (i = 0; i < cnt; i++) {
1201123858Swpaul		p = packets[i];
1202124005Swpaul		/*
1203124005Swpaul		 * Either the driver already handed the packet to
1204124005Swpaul		 * ndis_txeof() due to a failure, or it wants to keep
1205124005Swpaul		 * it and release it asynchronously later. Skip to the
1206124005Swpaul		 * next one.
1207124005Swpaul		 */
1208124005Swpaul		if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING)
1209123858Swpaul			continue;
1210124202Swpaul		senddonefunc(&sc->ndis_block, p, p->np_oob.npo_status);
1211123858Swpaul	}
1212123858Swpaul
1213123474Swpaul	return(0);
1214123474Swpaul}
1215123474Swpaul
1216123474Swpaulint
1217125377Swpaulndis_send_packet(arg, packet)
1218125377Swpaul	void			*arg;
1219125377Swpaul	ndis_packet		*packet;
1220125377Swpaul{
1221125377Swpaul	struct ndis_softc	*sc;
1222125377Swpaul	ndis_handle		adapter;
1223125377Swpaul	ndis_status		status;
1224125377Swpaul	__stdcall ndis_sendsingle_handler	sendfunc;
1225125377Swpaul	__stdcall ndis_senddone_func		senddonefunc;
1226128229Swpaul	uint8_t			irql;
1227125377Swpaul
1228125377Swpaul	sc = arg;
1229125377Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1230125718Swpaul	if (adapter == NULL)
1231125718Swpaul		return(ENXIO);
1232125377Swpaul	sendfunc = sc->ndis_chars.nmc_sendsingle_func;
1233125377Swpaul	senddonefunc = sc->ndis_block.nmb_senddone_func;
1234125377Swpaul
1235140751Swpaul	irql = KeRaiseIrql(DISPATCH_LEVEL);
1236125377Swpaul	status = sendfunc(adapter, packet, packet->np_private.npp_flags);
1237140751Swpaul	KeLowerIrql(irql);
1238125377Swpaul
1239125377Swpaul	if (status == NDIS_STATUS_PENDING)
1240125377Swpaul		return(0);
1241125377Swpaul
1242125377Swpaul	senddonefunc(&sc->ndis_block, packet, status);
1243125377Swpaul
1244125377Swpaul	return(0);
1245125377Swpaul}
1246125377Swpaul
1247125377Swpaulint
1248123474Swpaulndis_init_dma(arg)
1249123474Swpaul	void			*arg;
1250123474Swpaul{
1251123474Swpaul	struct ndis_softc	*sc;
1252123474Swpaul	int			i, error;
1253123474Swpaul
1254123474Swpaul	sc = arg;
1255123474Swpaul
1256123474Swpaul	sc->ndis_tmaps = malloc(sizeof(bus_dmamap_t) * sc->ndis_maxpkts,
1257123474Swpaul	    M_DEVBUF, M_NOWAIT|M_ZERO);
1258123474Swpaul
1259123474Swpaul	if (sc->ndis_tmaps == NULL)
1260123474Swpaul		return(ENOMEM);
1261123474Swpaul
1262123474Swpaul	for (i = 0; i < sc->ndis_maxpkts; i++) {
1263123474Swpaul		error = bus_dmamap_create(sc->ndis_ttag, 0,
1264123474Swpaul		    &sc->ndis_tmaps[i]);
1265123474Swpaul		if (error) {
1266123474Swpaul			free(sc->ndis_tmaps, M_DEVBUF);
1267123474Swpaul			return(ENODEV);
1268123474Swpaul		}
1269123474Swpaul	}
1270123474Swpaul
1271123474Swpaul	return(0);
1272123474Swpaul}
1273123474Swpaul
1274123474Swpaulint
1275123474Swpaulndis_destroy_dma(arg)
1276123474Swpaul	void			*arg;
1277123474Swpaul{
1278123474Swpaul	struct ndis_softc	*sc;
1279123535Swpaul	struct mbuf		*m;
1280123535Swpaul	ndis_packet		*p = NULL;
1281123474Swpaul	int			i;
1282123474Swpaul
1283123474Swpaul	sc = arg;
1284123474Swpaul
1285123474Swpaul	for (i = 0; i < sc->ndis_maxpkts; i++) {
1286123535Swpaul		if (sc->ndis_txarray[i] != NULL) {
1287123535Swpaul			p = sc->ndis_txarray[i];
1288123535Swpaul			m = (struct mbuf *)p->np_rsvd[1];
1289123535Swpaul			if (m != NULL)
1290123535Swpaul				m_freem(m);
1291123535Swpaul			ndis_free_packet(sc->ndis_txarray[i]);
1292123535Swpaul		}
1293123474Swpaul		bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]);
1294123474Swpaul	}
1295123474Swpaul
1296123474Swpaul	free(sc->ndis_tmaps, M_DEVBUF);
1297123474Swpaul
1298123474Swpaul	bus_dma_tag_destroy(sc->ndis_ttag);
1299123474Swpaul
1300123474Swpaul	return(0);
1301123474Swpaul}
1302123474Swpaul
1303123474Swpaulint
1304123474Swpaulndis_reset_nic(arg)
1305123474Swpaul	void			*arg;
1306123474Swpaul{
1307123474Swpaul	struct ndis_softc	*sc;
1308123474Swpaul	ndis_handle		adapter;
1309123474Swpaul	__stdcall ndis_reset_handler	resetfunc;
1310123474Swpaul	uint8_t			addressing_reset;
1311123474Swpaul	struct ifnet		*ifp;
1312127887Swpaul	int			rval;
1313128229Swpaul	uint8_t			irql;
1314123474Swpaul
1315123474Swpaul	sc = arg;
1316123474Swpaul	ifp = &sc->arpcom.ac_if;
1317125718Swpaul	NDIS_LOCK(sc);
1318123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1319125718Swpaul	resetfunc = sc->ndis_chars.nmc_reset_func;
1320125718Swpaul	NDIS_UNLOCK(sc);
1321125718Swpaul	if (adapter == NULL || resetfunc == NULL)
1322123474Swpaul		return(EIO);
1323123474Swpaul
1324140751Swpaul	irql = KeRaiseIrql(DISPATCH_LEVEL);
1325127887Swpaul	rval = resetfunc(&addressing_reset, adapter);
1326140751Swpaul	KeLowerIrql(irql);
1327128229Swpaul
1328127887Swpaul	if (rval == NDIS_STATUS_PENDING) {
1329140267Swpaul		mtx_lock(&ndis_req_mtx);
1330140267Swpaul		msleep(sc, &ndis_req_mtx,
1331128229Swpaul		    curthread->td_priority|PDROP, "ndisrst", 0);
1332127887Swpaul	}
1333123474Swpaul
1334123474Swpaul	return(0);
1335123474Swpaul}
1336123474Swpaul
1337123474Swpaulint
1338123474Swpaulndis_halt_nic(arg)
1339123474Swpaul	void			*arg;
1340123474Swpaul{
1341123474Swpaul	struct ndis_softc	*sc;
1342123474Swpaul	ndis_handle		adapter;
1343123474Swpaul	__stdcall ndis_halt_handler	haltfunc;
1344123474Swpaul	struct ifnet		*ifp;
1345123474Swpaul
1346123474Swpaul	sc = arg;
1347123474Swpaul	ifp = &sc->arpcom.ac_if;
1348125718Swpaul
1349125718Swpaul	NDIS_LOCK(sc);
1350123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1351125718Swpaul	if (adapter == NULL) {
1352125718Swpaul		NDIS_UNLOCK(sc);
1353123474Swpaul		return(EIO);
1354125718Swpaul	}
1355123821Swpaul
1356123474Swpaul	/*
1357123474Swpaul	 * The adapter context is only valid after the init
1358123474Swpaul	 * handler has been called, and is invalid once the
1359123474Swpaul	 * halt handler has been called.
1360123474Swpaul	 */
1361123474Swpaul
1362125718Swpaul	haltfunc = sc->ndis_chars.nmc_halt_func;
1363125718Swpaul	NDIS_UNLOCK(sc);
1364125718Swpaul
1365125718Swpaul	haltfunc(adapter);
1366125718Swpaul
1367125718Swpaul	NDIS_LOCK(sc);
1368123474Swpaul	sc->ndis_block.nmb_miniportadapterctx = NULL;
1369125718Swpaul	NDIS_UNLOCK(sc);
1370123821Swpaul
1371123474Swpaul	return(0);
1372123474Swpaul}
1373123474Swpaul
1374123474Swpaulint
1375123474Swpaulndis_shutdown_nic(arg)
1376123474Swpaul	void			*arg;
1377123474Swpaul{
1378123474Swpaul	struct ndis_softc	*sc;
1379123474Swpaul	ndis_handle		adapter;
1380123474Swpaul	__stdcall ndis_shutdown_handler	shutdownfunc;
1381123474Swpaul
1382123474Swpaul	sc = arg;
1383125718Swpaul	NDIS_LOCK(sc);
1384123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1385125718Swpaul	shutdownfunc = sc->ndis_chars.nmc_shutdown_handler;
1386125718Swpaul	NDIS_UNLOCK(sc);
1387125718Swpaul	if (adapter == NULL || shutdownfunc == NULL)
1388123474Swpaul		return(EIO);
1389123474Swpaul
1390123485Swpaul	if (sc->ndis_chars.nmc_rsvd0 == NULL)
1391123485Swpaul		shutdownfunc(adapter);
1392123485Swpaul	else
1393123485Swpaul		shutdownfunc(sc->ndis_chars.nmc_rsvd0);
1394123474Swpaul
1395125006Swpaul	ndis_shrink_thrqueue(8);
1396125057Swpaul	TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link);
1397125006Swpaul
1398123474Swpaul	return(0);
1399123474Swpaul}
1400123474Swpaul
1401123474Swpaulint
1402123474Swpaulndis_init_nic(arg)
1403123474Swpaul	void			*arg;
1404123474Swpaul{
1405123474Swpaul	struct ndis_softc	*sc;
1406123474Swpaul	ndis_miniport_block	*block;
1407123474Swpaul        __stdcall ndis_init_handler	initfunc;
1408123474Swpaul	ndis_status		status, openstatus = 0;
1409123474Swpaul	ndis_medium		mediumarray[NdisMediumMax];
1410123474Swpaul	uint32_t		chosenmedium, i;
1411123474Swpaul
1412123474Swpaul	if (arg == NULL)
1413123474Swpaul		return(EINVAL);
1414123474Swpaul
1415123474Swpaul	sc = arg;
1416125718Swpaul	NDIS_LOCK(sc);
1417123474Swpaul	block = &sc->ndis_block;
1418123474Swpaul	initfunc = sc->ndis_chars.nmc_init_func;
1419125718Swpaul	NDIS_UNLOCK(sc);
1420123474Swpaul
1421123821Swpaul	TAILQ_INIT(&block->nmb_timerlist);
1422123821Swpaul
1423123474Swpaul	for (i = 0; i < NdisMediumMax; i++)
1424123474Swpaul		mediumarray[i] = i;
1425123474Swpaul
1426123474Swpaul        status = initfunc(&openstatus, &chosenmedium,
1427123474Swpaul            mediumarray, NdisMediumMax, block, block);
1428123474Swpaul
1429123474Swpaul	/*
1430123474Swpaul	 * If the init fails, blow away the other exported routines
1431123474Swpaul	 * we obtained from the driver so we can't call them later.
1432123474Swpaul	 * If the init failed, none of these will work.
1433123474Swpaul	 */
1434123474Swpaul	if (status != NDIS_STATUS_SUCCESS) {
1435125718Swpaul		NDIS_LOCK(sc);
1436125676Swpaul		sc->ndis_block.nmb_miniportadapterctx = NULL;
1437125718Swpaul		NDIS_UNLOCK(sc);
1438123474Swpaul		return(ENXIO);
1439123474Swpaul	}
1440123474Swpaul
1441123474Swpaul	return(0);
1442123474Swpaul}
1443123474Swpaul
1444123474Swpaulvoid
1445123474Swpaulndis_enable_intr(arg)
1446123474Swpaul	void			*arg;
1447123474Swpaul{
1448123474Swpaul	struct ndis_softc	*sc;
1449123474Swpaul	ndis_handle		adapter;
1450123474Swpaul	__stdcall ndis_enable_interrupts_handler	intrenbfunc;
1451123474Swpaul
1452123474Swpaul	sc = arg;
1453123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1454123474Swpaul	intrenbfunc = sc->ndis_chars.nmc_enable_interrupts_func;
1455125718Swpaul	if (adapter == NULL || intrenbfunc == NULL)
1456123474Swpaul		return;
1457123474Swpaul	intrenbfunc(adapter);
1458123474Swpaul
1459123474Swpaul	return;
1460123474Swpaul}
1461123474Swpaul
1462123474Swpaulvoid
1463123474Swpaulndis_disable_intr(arg)
1464123474Swpaul	void			*arg;
1465123474Swpaul{
1466123474Swpaul	struct ndis_softc	*sc;
1467123474Swpaul	ndis_handle		adapter;
1468123474Swpaul	__stdcall ndis_disable_interrupts_handler	intrdisfunc;
1469123474Swpaul
1470123474Swpaul	sc = arg;
1471125718Swpaul	NDIS_LOCK(sc);
1472123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1473125718Swpaul	intrdisfunc = sc->ndis_chars.nmc_disable_interrupts_func;
1474125718Swpaul	NDIS_UNLOCK(sc);
1475126834Swpaul	if (adapter == NULL || intrdisfunc == NULL)
1476123474Swpaul	    return;
1477123474Swpaul	intrdisfunc(adapter);
1478123474Swpaul
1479123474Swpaul	return;
1480123474Swpaul}
1481123474Swpaul
1482123474Swpaulint
1483123474Swpaulndis_isr(arg, ourintr, callhandler)
1484123474Swpaul	void			*arg;
1485123474Swpaul	int			*ourintr;
1486123474Swpaul	int			*callhandler;
1487123474Swpaul{
1488123474Swpaul	struct ndis_softc	*sc;
1489123474Swpaul	ndis_handle		adapter;
1490123474Swpaul	__stdcall ndis_isr_handler	isrfunc;
1491123474Swpaul	uint8_t			accepted, queue;
1492123474Swpaul
1493123474Swpaul	if (arg == NULL || ourintr == NULL || callhandler == NULL)
1494123474Swpaul		return(EINVAL);
1495123474Swpaul
1496123474Swpaul	sc = arg;
1497123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1498123474Swpaul	isrfunc = sc->ndis_chars.nmc_isr_func;
1499125718Swpaul	if (adapter == NULL || isrfunc == NULL)
1500125718Swpaul		return(ENXIO);
1501125718Swpaul
1502123474Swpaul	isrfunc(&accepted, &queue, adapter);
1503123474Swpaul	*ourintr = accepted;
1504123474Swpaul	*callhandler = queue;
1505123474Swpaul
1506123474Swpaul	return(0);
1507123474Swpaul}
1508123474Swpaul
1509123474Swpaulint
1510123474Swpaulndis_intrhand(arg)
1511123474Swpaul	void			*arg;
1512123474Swpaul{
1513123474Swpaul	struct ndis_softc	*sc;
1514123474Swpaul	ndis_handle		adapter;
1515123474Swpaul	__stdcall ndis_interrupt_handler	intrfunc;
1516123474Swpaul
1517123474Swpaul	if (arg == NULL)
1518123474Swpaul		return(EINVAL);
1519123474Swpaul
1520123474Swpaul	sc = arg;
1521125718Swpaul	NDIS_LOCK(sc);
1522123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1523123474Swpaul	intrfunc = sc->ndis_chars.nmc_interrupt_func;
1524125718Swpaul	NDIS_UNLOCK(sc);
1525125718Swpaul	if (adapter == NULL || intrfunc == NULL)
1526125718Swpaul		return(EINVAL);
1527125718Swpaul
1528123474Swpaul	intrfunc(adapter);
1529123474Swpaul
1530123474Swpaul	return(0);
1531123474Swpaul}
1532123474Swpaul
1533123474Swpaulint
1534123474Swpaulndis_get_info(arg, oid, buf, buflen)
1535123474Swpaul	void			*arg;
1536123474Swpaul	ndis_oid		oid;
1537123474Swpaul	void			*buf;
1538123474Swpaul	int			*buflen;
1539123474Swpaul{
1540123474Swpaul	struct ndis_softc	*sc;
1541123474Swpaul	ndis_status		rval;
1542123474Swpaul	ndis_handle		adapter;
1543123474Swpaul	__stdcall ndis_queryinfo_handler	queryfunc;
1544123474Swpaul	uint32_t		byteswritten = 0, bytesneeded = 0;
1545123695Swpaul	int			error;
1546128229Swpaul	uint8_t			irql;
1547123474Swpaul
1548123474Swpaul	sc = arg;
1549125718Swpaul	NDIS_LOCK(sc);
1550123474Swpaul	queryfunc = sc->ndis_chars.nmc_queryinfo_func;
1551123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1552125718Swpaul	NDIS_UNLOCK(sc);
1553123474Swpaul
1554125718Swpaul	if (adapter == NULL || queryfunc == NULL)
1555125676Swpaul		return(ENXIO);
1556125676Swpaul
1557140751Swpaul	KeAcquireSpinLock(&sc->ndis_block.nmb_lock, &irql);
1558123474Swpaul	rval = queryfunc(adapter, oid, buf, *buflen,
1559123474Swpaul	    &byteswritten, &bytesneeded);
1560140751Swpaul	KeReleaseSpinLock(&sc->ndis_block.nmb_lock, irql);
1561123474Swpaul
1562123695Swpaul	/* Wait for requests that block. */
1563123695Swpaul
1564123695Swpaul	if (rval == NDIS_STATUS_PENDING) {
1565140267Swpaul		mtx_lock(&ndis_req_mtx);
1566140267Swpaul		error = msleep(&sc->ndis_block.nmb_getstat,
1567140267Swpaul		    &ndis_req_mtx,
1568128229Swpaul		    curthread->td_priority|PDROP,
1569127887Swpaul		    "ndisget", 5 * hz);
1570123695Swpaul		rval = sc->ndis_block.nmb_getstat;
1571123695Swpaul	}
1572123695Swpaul
1573123474Swpaul	if (byteswritten)
1574123474Swpaul		*buflen = byteswritten;
1575123474Swpaul	if (bytesneeded)
1576123474Swpaul		*buflen = bytesneeded;
1577123474Swpaul
1578123474Swpaul	if (rval == NDIS_STATUS_INVALID_LENGTH ||
1579123474Swpaul	    rval == NDIS_STATUS_BUFFER_TOO_SHORT)
1580123474Swpaul		return(ENOSPC);
1581123474Swpaul
1582123474Swpaul	if (rval == NDIS_STATUS_INVALID_OID)
1583123474Swpaul		return(EINVAL);
1584123474Swpaul
1585123474Swpaul	if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1586123474Swpaul	    rval == NDIS_STATUS_NOT_ACCEPTED)
1587123474Swpaul		return(ENOTSUP);
1588123474Swpaul
1589124809Swpaul	if (rval != NDIS_STATUS_SUCCESS)
1590124809Swpaul		return(ENODEV);
1591124809Swpaul
1592123474Swpaul	return(0);
1593123474Swpaul}
1594123474Swpaul
1595123474Swpaulint
1596123474Swpaulndis_unload_driver(arg)
1597123474Swpaul	void			*arg;
1598123474Swpaul{
1599123474Swpaul	struct ndis_softc	*sc;
1600123474Swpaul
1601123474Swpaul	sc = arg;
1602123474Swpaul
1603123474Swpaul	free(sc->ndis_block.nmb_rlist, M_DEVBUF);
1604123474Swpaul
1605123474Swpaul	ndis_flush_sysctls(sc);
1606123474Swpaul
1607124697Swpaul	ndis_shrink_thrqueue(8);
1608125057Swpaul	TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link);
1609124697Swpaul
1610123474Swpaul	return(0);
1611123474Swpaul}
1612123474Swpaul
1613127887Swpaul#define NDIS_LOADED		htonl(0x42534F44)
1614124446Swpaul
1615123474Swpaulint
1616123474Swpaulndis_load_driver(img, arg)
1617123474Swpaul	vm_offset_t		img;
1618123474Swpaul	void			*arg;
1619123474Swpaul{
1620133876Swpaul	driver_entry		entry;
1621123474Swpaul	image_optional_header	opt_hdr;
1622123474Swpaul	image_import_descriptor imp_desc;
1623123474Swpaul	ndis_unicode_string	dummystr;
1624123474Swpaul        ndis_miniport_block     *block;
1625123474Swpaul	ndis_status		status;
1626123474Swpaul	int			idx;
1627123474Swpaul	uint32_t		*ptr;
1628123474Swpaul	struct ndis_softc	*sc;
1629123474Swpaul
1630123474Swpaul	sc = arg;
1631123474Swpaul
1632124446Swpaul	/*
1633124446Swpaul	 * Only perform the relocation/linking phase once
1634124446Swpaul	 * since the binary image may be shared among multiple
1635124446Swpaul	 * device instances.
1636124446Swpaul	 */
1637123474Swpaul
1638124446Swpaul	ptr = (uint32_t *)(img + 8);
1639124446Swpaul	if (*ptr != NDIS_LOADED) {
1640124446Swpaul		/* Perform text relocation */
1641124446Swpaul		if (pe_relocate(img))
1642124446Swpaul			return(ENOEXEC);
1643123474Swpaul
1644124446Swpaul		/* Dynamically link the NDIS.SYS routines -- required. */
1645124446Swpaul		if (pe_patch_imports(img, "NDIS", ndis_functbl))
1646124446Swpaul			return(ENOEXEC);
1647123474Swpaul
1648124446Swpaul		/* Dynamically link the HAL.dll routines -- also required. */
1649124446Swpaul		if (pe_patch_imports(img, "HAL", hal_functbl))
1650123474Swpaul			return(ENOEXEC);
1651124446Swpaul
1652124446Swpaul		/* Dynamically link ntoskrnl.exe -- optional. */
1653124446Swpaul		if (pe_get_import_descriptor(img,
1654124446Swpaul		    &imp_desc, "ntoskrnl") == 0) {
1655124446Swpaul			if (pe_patch_imports(img,
1656124446Swpaul			    "ntoskrnl", ntoskrnl_functbl))
1657124446Swpaul				return(ENOEXEC);
1658124446Swpaul		}
1659124446Swpaul		*ptr = NDIS_LOADED;
1660123474Swpaul	}
1661123474Swpaul
1662123474Swpaul        /* Locate the driver entry point */
1663123474Swpaul	pe_get_optional_header(img, &opt_hdr);
1664123474Swpaul	entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
1665123474Swpaul
1666125551Swpaul	dummystr.nus_len = strlen(NDIS_DUMMY_PATH) * 2;
1667125551Swpaul	dummystr.nus_maxlen = strlen(NDIS_DUMMY_PATH) * 2;
1668123474Swpaul	dummystr.nus_buf = NULL;
1669123474Swpaul	ndis_ascii_to_unicode(NDIS_DUMMY_PATH, &dummystr.nus_buf);
1670123474Swpaul
1671123474Swpaul	/*
1672123474Swpaul	 * Now that we have the miniport driver characteristics,
1673123474Swpaul	 * create an NDIS block and call the init handler.
1674123474Swpaul	 * This will cause the driver to try to probe for
1675123474Swpaul	 * a device.
1676123474Swpaul	 */
1677123474Swpaul
1678123474Swpaul	block = &sc->ndis_block;
1679123474Swpaul
1680125551Swpaul	ptr = (uint32_t *)block;
1681123474Swpaul	for (idx = 0; idx < sizeof(ndis_miniport_block) / 4; idx++) {
1682123474Swpaul		*ptr = idx | 0xdead0000;
1683123474Swpaul		ptr++;
1684123474Swpaul	}
1685123474Swpaul
1686123474Swpaul	block->nmb_signature = (void *)0xcafebabe;
1687123474Swpaul	block->nmb_setdone_func = ndis_setdone_func;
1688123535Swpaul	block->nmb_querydone_func = ndis_getdone_func;
1689123474Swpaul	block->nmb_status_func = ndis_status_func;
1690123474Swpaul	block->nmb_statusdone_func = ndis_statusdone_func;
1691123474Swpaul	block->nmb_resetdone_func = ndis_resetdone_func;
1692124100Swpaul	block->nmb_sendrsrc_func = ndis_sendrsrcavail_func;
1693123474Swpaul
1694123474Swpaul	block->nmb_ifp = &sc->arpcom.ac_if;
1695123474Swpaul	block->nmb_dev = sc->ndis_dev;
1696124165Swpaul	block->nmb_img = img;
1697125551Swpaul	block->nmb_devobj.do_rsvd = block;
1698123474Swpaul
1699125551Swpaul	/*
1700125551Swpaul	 * Now call the DriverEntry() routine. This will cause
1701125551Swpaul	 * a callout to the NdisInitializeWrapper() and
1702125551Swpaul	 * NdisMRegisterMiniport() routines.
1703125551Swpaul	 */
1704125551Swpaul	status = entry(&block->nmb_devobj, &dummystr);
1705125551Swpaul
1706125551Swpaul	free (dummystr.nus_buf, M_DEVBUF);
1707125551Swpaul
1708125551Swpaul	if (status != NDIS_STATUS_SUCCESS)
1709125551Swpaul		return(ENODEV);
1710125551Swpaul
1711124697Swpaul	ndis_enlarge_thrqueue(8);
1712124697Swpaul
1713125057Swpaul	TAILQ_INSERT_TAIL(&ndis_devhead, block, link);
1714140751Swpaul	KeInitializeSpinLock(&block->nmb_lock);
1715125006Swpaul
1716123474Swpaul	return(0);
1717123474Swpaul}
1718