kern_ndis.c revision 131953
1123474Swpaul/*
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 131953 2004-07-11 00:19:30Z 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;
113124697Swpaulstatic STAILQ_HEAD(ndisqhead, ndis_req) ndis_ttodo;
114124697Swpaulstruct ndisqhead ndis_itodo;
115124697Swpaulstruct ndisqhead ndis_free;
116124697Swpaulstatic int ndis_jobs = 32;
117124060Swpaul
118124724Swpaulstatic struct ndisproc ndis_tproc;
119124724Swpaulstatic struct ndisproc ndis_iproc;
120124697Swpaul
121123474Swpaul/*
122123474Swpaul * This allows us to export our symbols to other modules.
123123474Swpaul * Note that we call ourselves 'ndisapi' to avoid a namespace
124123474Swpaul * collision with if_ndis.ko, which internally calls itself
125123474Swpaul * 'ndis.'
126123474Swpaul */
127123474Swpaulstatic int
128123474Swpaulndis_modevent(module_t mod, int cmd, void *arg)
129123474Swpaul{
130124060Swpaul	int			error = 0;
131124060Swpaul
132124060Swpaul	switch (cmd) {
133124060Swpaul	case MOD_LOAD:
134124122Swpaul		/* Initialize subsystems */
135124122Swpaul		ndis_libinit();
136124122Swpaul		ntoskrnl_libinit();
137124122Swpaul
138124122Swpaul		/* Initialize TX buffer UMA zone. */
139124060Swpaul		ndis_packet_zone = uma_zcreate("NDIS packet",
140124060Swpaul		    sizeof(ndis_packet), NULL, NULL, NULL,
141124060Swpaul		    NULL, UMA_ALIGN_PTR, 0);
142124060Swpaul		ndis_buffer_zone = uma_zcreate("NDIS buffer",
143124060Swpaul		    sizeof(ndis_buffer), NULL, NULL, NULL,
144124060Swpaul		    NULL, UMA_ALIGN_PTR, 0);
145124697Swpaul
146124697Swpaul		ndis_create_kthreads();
147124697Swpaul
148125057Swpaul		TAILQ_INIT(&ndis_devhead);
149125057Swpaul
150124060Swpaul		break;
151124060Swpaul	case MOD_SHUTDOWN:
152124697Swpaul		/* stop kthreads */
153124697Swpaul		ndis_destroy_kthreads();
154127311Swpaul		if (TAILQ_FIRST(&ndis_devhead) == NULL) {
155125006Swpaul			/* Shut down subsystems */
156125006Swpaul			ndis_libfini();
157125006Swpaul			ntoskrnl_libfini();
158124697Swpaul
159125006Swpaul			/* Remove zones */
160125006Swpaul			uma_zdestroy(ndis_packet_zone);
161125006Swpaul			uma_zdestroy(ndis_buffer_zone);
162125006Swpaul		}
163125006Swpaul		break;
164125006Swpaul	case MOD_UNLOAD:
165125006Swpaul		/* stop kthreads */
166125006Swpaul		ndis_destroy_kthreads();
167125006Swpaul
168124122Swpaul		/* Shut down subsystems */
169124122Swpaul		ndis_libfini();
170124122Swpaul		ntoskrnl_libfini();
171124122Swpaul
172124122Swpaul		/* Remove zones */
173124060Swpaul		uma_zdestroy(ndis_packet_zone);
174124060Swpaul		uma_zdestroy(ndis_buffer_zone);
175124060Swpaul		break;
176124060Swpaul	default:
177124060Swpaul		error = EINVAL;
178124060Swpaul		break;
179124060Swpaul	}
180124060Swpaul
181124060Swpaul	return(error);
182123474Swpaul}
183123474SwpaulDEV_MODULE(ndisapi, ndis_modevent, NULL);
184123474SwpaulMODULE_VERSION(ndisapi, 1);
185123474Swpaul
186124697Swpaul/*
187124697Swpaul * We create two kthreads for the NDIS subsystem. One of them is a task
188124697Swpaul * queue for performing various odd jobs. The other is an swi thread
189124697Swpaul * reserved exclusively for running interrupt handlers. The reason we
190124697Swpaul * have our own task queue is that there are some cases where we may
191124697Swpaul * need to sleep for a significant amount of time, and if we were to
192124697Swpaul * use one of the taskqueue threads, we might delay the processing
193124697Swpaul * of other pending tasks which might need to run right away. We have
194124697Swpaul * a separate swi thread because we don't want our interrupt handling
195124697Swpaul * to be delayed either.
196124697Swpaul *
197124697Swpaul * By default there are 32 jobs available to start, and another 8
198124697Swpaul * are added to the free list each time a new device is created.
199124697Swpaul */
200123474Swpaul
201124697Swpaulstatic void
202124724Swpaulndis_runq(arg)
203124724Swpaul	void			*arg;
204124697Swpaul{
205124697Swpaul	struct ndis_req		*r = NULL, *die = NULL;
206124724Swpaul	struct ndisproc		*p;
207124697Swpaul
208124724Swpaul	p = arg;
209124724Swpaul
210124697Swpaul	while (1) {
211124697Swpaul
212128229Swpaul		/* Sleep, but preserve our original priority. */
213128229Swpaul		ndis_thsuspend(p->np_p, 0);
214128229Swpaul
215124697Swpaul		/* Look for any jobs on the work queue. */
216124697Swpaul
217128229Swpaul		mtx_lock(&ndis_thr_mtx);
218125814Swpaul		p->np_state = NDIS_PSTATE_RUNNING;
219124724Swpaul		while(STAILQ_FIRST(p->np_q) != NULL) {
220124724Swpaul			r = STAILQ_FIRST(p->np_q);
221124724Swpaul			STAILQ_REMOVE_HEAD(p->np_q, link);
222128229Swpaul			mtx_unlock(&ndis_thr_mtx);
223124697Swpaul
224124697Swpaul			/* Do the work. */
225124697Swpaul
226124724Swpaul			if (r->nr_func != NULL)
227124724Swpaul				(*r->nr_func)(r->nr_arg);
228124697Swpaul
229128229Swpaul			mtx_lock(&ndis_thr_mtx);
230124697Swpaul			STAILQ_INSERT_HEAD(&ndis_free, r, link);
231124697Swpaul
232124697Swpaul			/* Check for a shutdown request */
233124697Swpaul
234124724Swpaul			if (r->nr_exit == TRUE)
235124697Swpaul				die = r;
236124697Swpaul		}
237125814Swpaul		p->np_state = NDIS_PSTATE_SLEEPING;
238128229Swpaul		mtx_unlock(&ndis_thr_mtx);
239124697Swpaul
240124697Swpaul		/* Bail if we were told to shut down. */
241124697Swpaul
242124697Swpaul		if (die != NULL)
243124697Swpaul			break;
244124697Swpaul	}
245124697Swpaul
246124697Swpaul	wakeup(die);
247130166Swpaul#if __FreeBSD_version < 502113
248130166Swpaul	mtx_lock(&Giant);
249130166Swpaul#endif
250124697Swpaul	kthread_exit(0);
251128229Swpaul	return; /* notreached */
252124697Swpaul}
253124697Swpaul
254124697Swpaulstatic int
255124697Swpaulndis_create_kthreads()
256124697Swpaul{
257124697Swpaul	struct ndis_req		*r;
258124697Swpaul	int			i, error = 0;
259124697Swpaul
260128229Swpaul	mtx_init(&ndis_thr_mtx, "NDIS thread lock",
261128229Swpaul	   MTX_NDIS_LOCK, MTX_DEF);
262128229Swpaul
263124697Swpaul	STAILQ_INIT(&ndis_ttodo);
264124697Swpaul	STAILQ_INIT(&ndis_itodo);
265124697Swpaul	STAILQ_INIT(&ndis_free);
266124697Swpaul
267124697Swpaul	for (i = 0; i < ndis_jobs; i++) {
268124697Swpaul		r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK);
269124697Swpaul		if (r == NULL) {
270124697Swpaul			error = ENOMEM;
271124697Swpaul			break;
272124697Swpaul		}
273124697Swpaul		STAILQ_INSERT_HEAD(&ndis_free, r, link);
274124697Swpaul	}
275124697Swpaul
276124724Swpaul	if (error == 0) {
277124724Swpaul		ndis_tproc.np_q = &ndis_ttodo;
278125814Swpaul		ndis_tproc.np_state = NDIS_PSTATE_SLEEPING;
279124724Swpaul		error = kthread_create(ndis_runq, &ndis_tproc,
280127284Swpaul		    &ndis_tproc.np_p, RFHIGHPID,
281127284Swpaul		    NDIS_KSTACK_PAGES, "ndis taskqueue");
282124724Swpaul	}
283124697Swpaul
284124724Swpaul	if (error == 0) {
285124724Swpaul		ndis_iproc.np_q = &ndis_itodo;
286125814Swpaul		ndis_iproc.np_state = NDIS_PSTATE_SLEEPING;
287124724Swpaul		error = kthread_create(ndis_runq, &ndis_iproc,
288127284Swpaul		    &ndis_iproc.np_p, RFHIGHPID,
289127284Swpaul		    NDIS_KSTACK_PAGES, "ndis swi");
290124724Swpaul	}
291124697Swpaul
292124697Swpaul	if (error) {
293124697Swpaul		while ((r = STAILQ_FIRST(&ndis_free)) != NULL) {
294124697Swpaul			STAILQ_REMOVE_HEAD(&ndis_free, link);
295124697Swpaul			free(r, M_DEVBUF);
296124697Swpaul		}
297124697Swpaul		return(error);
298124697Swpaul	}
299124697Swpaul
300124697Swpaul	return(0);
301124697Swpaul}
302124697Swpaul
303124697Swpaulstatic void
304124697Swpaulndis_destroy_kthreads()
305124697Swpaul{
306124697Swpaul	struct ndis_req		*r;
307124697Swpaul
308124697Swpaul	/* Stop the threads. */
309124697Swpaul
310124697Swpaul	ndis_stop_thread(NDIS_TASKQUEUE);
311124697Swpaul	ndis_stop_thread(NDIS_SWI);
312124697Swpaul
313124697Swpaul	/* Destroy request structures. */
314124697Swpaul
315124697Swpaul	while ((r = STAILQ_FIRST(&ndis_free)) != NULL) {
316124697Swpaul		STAILQ_REMOVE_HEAD(&ndis_free, link);
317124697Swpaul		free(r, M_DEVBUF);
318124697Swpaul	}
319124697Swpaul
320128229Swpaul	mtx_destroy(&ndis_thr_mtx);
321128229Swpaul
322124697Swpaul	return;
323124697Swpaul}
324124697Swpaul
325124697Swpaulstatic void
326124697Swpaulndis_stop_thread(t)
327124697Swpaul	int			t;
328124697Swpaul{
329124697Swpaul	struct ndis_req		*r;
330124697Swpaul	struct ndisqhead	*q;
331124697Swpaul	struct proc		*p;
332124697Swpaul
333124697Swpaul	if (t == NDIS_TASKQUEUE) {
334124697Swpaul		q = &ndis_ttodo;
335124724Swpaul		p = ndis_tproc.np_p;
336124697Swpaul	} else {
337124697Swpaul		q = &ndis_itodo;
338124724Swpaul		p = ndis_iproc.np_p;
339124697Swpaul	}
340124697Swpaul
341124697Swpaul	/* Create and post a special 'exit' job. */
342124697Swpaul
343128229Swpaul	mtx_lock(&ndis_thr_mtx);
344124697Swpaul	r = STAILQ_FIRST(&ndis_free);
345124697Swpaul	STAILQ_REMOVE_HEAD(&ndis_free, link);
346124724Swpaul	r->nr_func = NULL;
347124724Swpaul	r->nr_arg = NULL;
348124724Swpaul	r->nr_exit = TRUE;
349124697Swpaul	STAILQ_INSERT_TAIL(q, r, link);
350128229Swpaul	mtx_unlock(&ndis_thr_mtx);
351124697Swpaul
352128229Swpaul	ndis_thresume(p);
353124697Swpaul
354124697Swpaul	/* wait for thread exit */
355124697Swpaul
356128295Swpaul	tsleep(r, curthread->td_priority|PCATCH, "ndisthexit", hz * 60);
357124697Swpaul
358124697Swpaul	/* Now empty the job list. */
359124697Swpaul
360128229Swpaul	mtx_lock(&ndis_thr_mtx);
361124697Swpaul	while ((r = STAILQ_FIRST(q)) != NULL) {
362124697Swpaul		STAILQ_REMOVE_HEAD(q, link);
363124697Swpaul		STAILQ_INSERT_HEAD(&ndis_free, r, link);
364124697Swpaul	}
365128229Swpaul	mtx_unlock(&ndis_thr_mtx);
366124697Swpaul
367124697Swpaul	return;
368124697Swpaul}
369124697Swpaul
370124697Swpaulstatic int
371124697Swpaulndis_enlarge_thrqueue(cnt)
372124697Swpaul	int			cnt;
373124697Swpaul{
374124697Swpaul	struct ndis_req		*r;
375124697Swpaul	int			i;
376124697Swpaul
377124697Swpaul	for (i = 0; i < cnt; i++) {
378124697Swpaul		r = malloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK);
379124697Swpaul		if (r == NULL)
380124697Swpaul			return(ENOMEM);
381128229Swpaul		mtx_lock(&ndis_thr_mtx);
382124697Swpaul		STAILQ_INSERT_HEAD(&ndis_free, r, link);
383124697Swpaul		ndis_jobs++;
384128229Swpaul		mtx_unlock(&ndis_thr_mtx);
385124697Swpaul	}
386124697Swpaul
387124697Swpaul	return(0);
388124697Swpaul}
389124697Swpaul
390124697Swpaulstatic int
391124697Swpaulndis_shrink_thrqueue(cnt)
392124697Swpaul	int			cnt;
393124697Swpaul{
394124697Swpaul	struct ndis_req		*r;
395124697Swpaul	int			i;
396124697Swpaul
397124697Swpaul	for (i = 0; i < cnt; i++) {
398128229Swpaul		mtx_lock(&ndis_thr_mtx);
399124697Swpaul		r = STAILQ_FIRST(&ndis_free);
400124697Swpaul		if (r == NULL) {
401128229Swpaul			mtx_unlock(&ndis_thr_mtx);
402124697Swpaul			return(ENOMEM);
403124697Swpaul		}
404124697Swpaul		STAILQ_REMOVE_HEAD(&ndis_free, link);
405124697Swpaul		ndis_jobs--;
406128229Swpaul		mtx_unlock(&ndis_thr_mtx);
407124697Swpaul		free(r, M_DEVBUF);
408124697Swpaul	}
409124697Swpaul
410124697Swpaul	return(0);
411124697Swpaul}
412124697Swpaul
413124697Swpaulint
414127393Swpaulndis_unsched(func, arg, t)
415127393Swpaul	void			(*func)(void *);
416127393Swpaul	void			*arg;
417127393Swpaul	int			t;
418127393Swpaul{
419127393Swpaul	struct ndis_req		*r;
420127393Swpaul	struct ndisqhead	*q;
421127393Swpaul	struct proc		*p;
422127393Swpaul
423127393Swpaul	if (t == NDIS_TASKQUEUE) {
424127393Swpaul		q = &ndis_ttodo;
425127393Swpaul		p = ndis_tproc.np_p;
426127393Swpaul	} else {
427127393Swpaul		q = &ndis_itodo;
428127393Swpaul		p = ndis_iproc.np_p;
429127393Swpaul	}
430127393Swpaul
431128229Swpaul	mtx_lock(&ndis_thr_mtx);
432127393Swpaul	STAILQ_FOREACH(r, q, link) {
433127393Swpaul		if (r->nr_func == func && r->nr_arg == arg) {
434127393Swpaul			STAILQ_REMOVE(q, r, ndis_req, link);
435127393Swpaul			STAILQ_INSERT_HEAD(&ndis_free, r, link);
436128229Swpaul			mtx_unlock(&ndis_thr_mtx);
437127393Swpaul			return(0);
438127393Swpaul		}
439127393Swpaul	}
440127393Swpaul
441128229Swpaul	mtx_unlock(&ndis_thr_mtx);
442127393Swpaul
443127393Swpaul	return(ENOENT);
444127393Swpaul}
445127393Swpaul
446127393Swpaulint
447124697Swpaulndis_sched(func, arg, t)
448124697Swpaul	void			(*func)(void *);
449124697Swpaul	void			*arg;
450124697Swpaul	int			t;
451124697Swpaul{
452124697Swpaul	struct ndis_req		*r;
453124697Swpaul	struct ndisqhead	*q;
454124697Swpaul	struct proc		*p;
455125814Swpaul	int			s;
456124697Swpaul
457124697Swpaul	if (t == NDIS_TASKQUEUE) {
458124697Swpaul		q = &ndis_ttodo;
459124724Swpaul		p = ndis_tproc.np_p;
460124697Swpaul	} else {
461124697Swpaul		q = &ndis_itodo;
462124724Swpaul		p = ndis_iproc.np_p;
463124697Swpaul	}
464124697Swpaul
465128229Swpaul	mtx_lock(&ndis_thr_mtx);
466124697Swpaul	/*
467124697Swpaul	 * Check to see if an instance of this job is already
468124697Swpaul	 * pending. If so, don't bother queuing it again.
469124697Swpaul	 */
470124697Swpaul	STAILQ_FOREACH(r, q, link) {
471124724Swpaul		if (r->nr_func == func && r->nr_arg == arg) {
472128229Swpaul			mtx_unlock(&ndis_thr_mtx);
473124697Swpaul			return(0);
474124697Swpaul		}
475124697Swpaul	}
476124697Swpaul	r = STAILQ_FIRST(&ndis_free);
477124697Swpaul	if (r == NULL) {
478128229Swpaul		mtx_unlock(&ndis_thr_mtx);
479124697Swpaul		return(EAGAIN);
480124697Swpaul	}
481124697Swpaul	STAILQ_REMOVE_HEAD(&ndis_free, link);
482124724Swpaul	r->nr_func = func;
483124724Swpaul	r->nr_arg = arg;
484124724Swpaul	r->nr_exit = FALSE;
485124697Swpaul	STAILQ_INSERT_TAIL(q, r, link);
486125814Swpaul	if (t == NDIS_TASKQUEUE)
487125814Swpaul		s = ndis_tproc.np_state;
488125814Swpaul	else
489125814Swpaul		s = ndis_iproc.np_state;
490128229Swpaul	mtx_unlock(&ndis_thr_mtx);
491124697Swpaul
492125814Swpaul	/*
493125814Swpaul	 * Post the job, but only if the thread is actually blocked
494125814Swpaul	 * on its own suspend call. If a driver queues up a job with
495125814Swpaul	 * NdisScheduleWorkItem() which happens to do a KeWaitForObject(),
496125814Swpaul	 * it may suspend there, and in that case we don't want to wake
497125814Swpaul	 * it up until KeWaitForObject() gets woken up on its own.
498125814Swpaul	 */
499125814Swpaul	if (s == NDIS_PSTATE_SLEEPING)
500128229Swpaul		ndis_thresume(p);
501124697Swpaul
502124697Swpaul	return(0);
503124697Swpaul}
504124697Swpaul
505128229Swpaulint
506128229Swpaulndis_thsuspend(p, timo)
507128229Swpaul	struct proc		*p;
508128229Swpaul	int			timo;
509128229Swpaul{
510128229Swpaul	int			error;
511128229Swpaul
512128229Swpaul	PROC_LOCK(p);
513128229Swpaul	error = msleep(&p->p_siglist, &p->p_mtx,
514128229Swpaul	    curthread->td_priority|PDROP, "ndissp", timo);
515128229Swpaul	return(error);
516128229Swpaul}
517128229Swpaul
518128229Swpaulvoid
519128229Swpaulndis_thresume(p)
520128229Swpaul	struct proc		*p;
521128229Swpaul{
522128229Swpaul	wakeup(&p->p_siglist);
523128229Swpaul	return;
524128229Swpaul}
525128229Swpaul
526123474Swpaul__stdcall static void
527124100Swpaulndis_sendrsrcavail_func(adapter)
528124100Swpaul	ndis_handle		adapter;
529124100Swpaul{
530124100Swpaul	return;
531124100Swpaul}
532124100Swpaul
533124100Swpaul__stdcall static void
534123474Swpaulndis_status_func(adapter, status, sbuf, slen)
535123474Swpaul	ndis_handle		adapter;
536123474Swpaul	ndis_status		status;
537123474Swpaul	void			*sbuf;
538123474Swpaul	uint32_t		slen;
539123474Swpaul{
540124060Swpaul	ndis_miniport_block	*block;
541124060Swpaul	block = adapter;
542124060Swpaul
543124409Swpaul	if (block->nmb_ifp->if_flags & IFF_DEBUG)
544124409Swpaul		device_printf (block->nmb_dev, "status: %x\n", status);
545123474Swpaul	return;
546123474Swpaul}
547123474Swpaul
548123474Swpaul__stdcall static void
549123474Swpaulndis_statusdone_func(adapter)
550123474Swpaul	ndis_handle		adapter;
551123474Swpaul{
552124060Swpaul	ndis_miniport_block	*block;
553124060Swpaul	block = adapter;
554124060Swpaul
555124409Swpaul	if (block->nmb_ifp->if_flags & IFF_DEBUG)
556124409Swpaul		device_printf (block->nmb_dev, "status complete\n");
557123474Swpaul	return;
558123474Swpaul}
559123474Swpaul
560123474Swpaul__stdcall static void
561123474Swpaulndis_setdone_func(adapter, status)
562123474Swpaul	ndis_handle		adapter;
563123474Swpaul	ndis_status		status;
564123474Swpaul{
565123695Swpaul	ndis_miniport_block	*block;
566123695Swpaul	block = adapter;
567123695Swpaul
568123695Swpaul	block->nmb_setstat = status;
569123695Swpaul	wakeup(&block->nmb_wkupdpctimer);
570123474Swpaul	return;
571123474Swpaul}
572123474Swpaul
573123474Swpaul__stdcall static void
574123535Swpaulndis_getdone_func(adapter, status)
575123535Swpaul	ndis_handle		adapter;
576123535Swpaul	ndis_status		status;
577123535Swpaul{
578123695Swpaul	ndis_miniport_block	*block;
579123695Swpaul	block = adapter;
580123695Swpaul
581123695Swpaul	block->nmb_getstat = status;
582123695Swpaul	wakeup(&block->nmb_wkupdpctimer);
583123535Swpaul	return;
584123535Swpaul}
585123535Swpaul
586123535Swpaul__stdcall static void
587123474Swpaulndis_resetdone_func(adapter, status, addressingreset)
588123474Swpaul	ndis_handle		adapter;
589123474Swpaul	ndis_status		status;
590123474Swpaul	uint8_t			addressingreset;
591123474Swpaul{
592124060Swpaul	ndis_miniport_block	*block;
593124060Swpaul	block = adapter;
594124060Swpaul
595124409Swpaul	if (block->nmb_ifp->if_flags & IFF_DEBUG)
596124409Swpaul		device_printf (block->nmb_dev, "reset done...\n");
597127887Swpaul	wakeup(block->nmb_ifp);
598123474Swpaul	return;
599123474Swpaul}
600123474Swpaul
601123474Swpaulint
602123474Swpaulndis_create_sysctls(arg)
603123474Swpaul	void			*arg;
604123474Swpaul{
605123474Swpaul	struct ndis_softc	*sc;
606123474Swpaul	ndis_cfg		*vals;
607123474Swpaul	char			buf[256];
608123474Swpaul
609123474Swpaul	if (arg == NULL)
610123474Swpaul		return(EINVAL);
611123474Swpaul
612123474Swpaul	sc = arg;
613123474Swpaul	vals = sc->ndis_regvals;
614123474Swpaul
615123474Swpaul	TAILQ_INIT(&sc->ndis_cfglist_head);
616123474Swpaul
617130097Sdes#if __FreeBSD_version < 502113
618123474Swpaul	/* Create the sysctl tree. */
619123474Swpaul
620123474Swpaul	sc->ndis_tree = SYSCTL_ADD_NODE(&sc->ndis_ctx,
621123474Swpaul	    SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
622123474Swpaul	    device_get_nameunit(sc->ndis_dev), CTLFLAG_RD, 0,
623123474Swpaul	    device_get_desc(sc->ndis_dev));
624123474Swpaul
625130097Sdes#endif
626123474Swpaul	/* Add the driver-specific registry keys. */
627123474Swpaul
628123474Swpaul	vals = sc->ndis_regvals;
629123474Swpaul	while(1) {
630123474Swpaul		if (vals->nc_cfgkey == NULL)
631123474Swpaul			break;
632123620Swpaul		if (vals->nc_idx != sc->ndis_devidx) {
633123620Swpaul			vals++;
634123620Swpaul			continue;
635123620Swpaul		}
636130097Sdes#if __FreeBSD_version < 502113
637123474Swpaul		SYSCTL_ADD_STRING(&sc->ndis_ctx,
638123474Swpaul		    SYSCTL_CHILDREN(sc->ndis_tree),
639130097Sdes#else
640130097Sdes		SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev),
641130097Sdes		    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)),
642130097Sdes#endif
643123474Swpaul		    OID_AUTO, vals->nc_cfgkey,
644123474Swpaul		    CTLFLAG_RW, vals->nc_val,
645123474Swpaul		    sizeof(vals->nc_val),
646123474Swpaul		    vals->nc_cfgdesc);
647123474Swpaul		vals++;
648123474Swpaul	}
649123474Swpaul
650123474Swpaul	/* Now add a couple of builtin keys. */
651123474Swpaul
652123474Swpaul	/*
653123474Swpaul	 * Environment can be either Windows (0) or WindowsNT (1).
654123474Swpaul	 * We qualify as the latter.
655123474Swpaul	 */
656123474Swpaul	ndis_add_sysctl(sc, "Environment",
657123474Swpaul	    "Windows environment", "1", CTLFLAG_RD);
658123474Swpaul
659123474Swpaul	/* NDIS version should be 5.1. */
660123474Swpaul	ndis_add_sysctl(sc, "NdisVersion",
661123474Swpaul	    "NDIS API Version", "0x00050001", CTLFLAG_RD);
662123474Swpaul
663123474Swpaul	/* Bus type (PCI, PCMCIA, etc...) */
664124272Swpaul	sprintf(buf, "%d", (int)sc->ndis_iftype);
665123474Swpaul	ndis_add_sysctl(sc, "BusType", "Bus Type", buf, CTLFLAG_RD);
666123474Swpaul
667123474Swpaul	if (sc->ndis_res_io != NULL) {
668124272Swpaul		sprintf(buf, "0x%lx", rman_get_start(sc->ndis_res_io));
669123474Swpaul		ndis_add_sysctl(sc, "IOBaseAddress",
670123474Swpaul		    "Base I/O Address", buf, CTLFLAG_RD);
671123474Swpaul	}
672123474Swpaul
673123474Swpaul	if (sc->ndis_irq != NULL) {
674124272Swpaul		sprintf(buf, "%lu", rman_get_start(sc->ndis_irq));
675123474Swpaul		ndis_add_sysctl(sc, "InterruptNumber",
676123474Swpaul		    "Interrupt Number", buf, CTLFLAG_RD);
677123474Swpaul	}
678123474Swpaul
679123474Swpaul	return(0);
680123474Swpaul}
681123474Swpaul
682123474Swpaulint
683123474Swpaulndis_add_sysctl(arg, key, desc, val, flag)
684123474Swpaul	void			*arg;
685123474Swpaul	char			*key;
686123474Swpaul	char			*desc;
687123474Swpaul	char			*val;
688123474Swpaul	int			flag;
689123474Swpaul{
690123474Swpaul	struct ndis_softc	*sc;
691123474Swpaul	struct ndis_cfglist	*cfg;
692123474Swpaul	char			descstr[256];
693123474Swpaul
694123474Swpaul	sc = arg;
695123474Swpaul
696123474Swpaul	cfg = malloc(sizeof(struct ndis_cfglist), M_DEVBUF, M_NOWAIT|M_ZERO);
697123474Swpaul
698123474Swpaul	if (cfg == NULL)
699123474Swpaul		return(ENOMEM);
700123474Swpaul
701123474Swpaul	cfg->ndis_cfg.nc_cfgkey = strdup(key, M_DEVBUF);
702123474Swpaul	if (desc == NULL) {
703123474Swpaul		snprintf(descstr, sizeof(descstr), "%s (dynamic)", key);
704123474Swpaul		cfg->ndis_cfg.nc_cfgdesc = strdup(descstr, M_DEVBUF);
705123474Swpaul	} else
706123474Swpaul		cfg->ndis_cfg.nc_cfgdesc = strdup(desc, M_DEVBUF);
707123474Swpaul	strcpy(cfg->ndis_cfg.nc_val, val);
708123474Swpaul
709123474Swpaul	TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link);
710123474Swpaul
711130097Sdes#if __FreeBSD_version < 502113
712123474Swpaul	SYSCTL_ADD_STRING(&sc->ndis_ctx, SYSCTL_CHILDREN(sc->ndis_tree),
713130097Sdes#else
714130097Sdes	SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev),
715130097Sdes	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)),
716130097Sdes#endif
717123474Swpaul	    OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag,
718123474Swpaul	    cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val),
719123474Swpaul	    cfg->ndis_cfg.nc_cfgdesc);
720123474Swpaul
721123474Swpaul	return(0);
722123474Swpaul}
723123474Swpaul
724123474Swpaulint
725123474Swpaulndis_flush_sysctls(arg)
726123474Swpaul	void			*arg;
727123474Swpaul{
728123474Swpaul	struct ndis_softc	*sc;
729123474Swpaul	struct ndis_cfglist	*cfg;
730123474Swpaul
731123474Swpaul	sc = arg;
732123474Swpaul
733123474Swpaul	while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) {
734123474Swpaul		cfg = TAILQ_FIRST(&sc->ndis_cfglist_head);
735123474Swpaul		TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link);
736123474Swpaul		free(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF);
737123474Swpaul		free(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF);
738123474Swpaul		free(cfg, M_DEVBUF);
739123474Swpaul	}
740123474Swpaul
741123474Swpaul	return(0);
742123474Swpaul}
743123474Swpaul
744128546Swpaulstatic void
745128546Swpaulndis_return(arg)
746123474Swpaul	void			*arg;
747123474Swpaul{
748123474Swpaul	struct ndis_softc	*sc;
749128546Swpaul	__stdcall ndis_return_handler	returnfunc;
750123474Swpaul	ndis_handle		adapter;
751123535Swpaul	ndis_packet		*p;
752128229Swpaul	uint8_t			irql;
753123474Swpaul
754128546Swpaul	p = arg;
755128546Swpaul	sc = p->np_softc;
756128546Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
757128546Swpaul
758128546Swpaul	if (adapter == NULL)
759128546Swpaul		return;
760128546Swpaul
761128546Swpaul	returnfunc = sc->ndis_chars.nmc_return_packet_func;
762128546Swpaul	irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
763128546Swpaul	returnfunc(adapter, p);
764128546Swpaul	FASTCALL1(hal_lower_irql, irql);
765128546Swpaul
766128546Swpaul	return;
767128546Swpaul}
768128546Swpaul
769128546Swpaulvoid
770128546Swpaulndis_return_packet(buf, arg)
771128546Swpaul	void			*buf;	/* not used */
772128546Swpaul	void			*arg;
773128546Swpaul{
774128546Swpaul	ndis_packet		*p;
775128546Swpaul
776123826Swpaul	if (arg == NULL)
777123474Swpaul		return;
778123474Swpaul
779123826Swpaul	p = arg;
780123535Swpaul
781123535Swpaul	/* Decrement refcount. */
782123826Swpaul	p->np_refcnt--;
783123535Swpaul
784123535Swpaul	/* Release packet when refcount hits zero, otherwise return. */
785123826Swpaul	if (p->np_refcnt)
786123535Swpaul		return;
787123536Swpaul
788128546Swpaul	ndis_sched(ndis_return, p, NDIS_SWI);
789123858Swpaul
790123474Swpaul	return;
791123474Swpaul}
792123474Swpaul
793123848Swpaulvoid
794123848Swpaulndis_free_bufs(b0)
795123848Swpaul	ndis_buffer		*b0;
796123848Swpaul{
797123848Swpaul	ndis_buffer		*next;
798123848Swpaul
799123848Swpaul	if (b0 == NULL)
800123848Swpaul		return;
801123848Swpaul
802123848Swpaul	while(b0 != NULL) {
803123848Swpaul		next = b0->nb_next;
804124060Swpaul		uma_zfree (ndis_buffer_zone, b0);
805123848Swpaul		b0 = next;
806123848Swpaul	}
807123848Swpaul
808123848Swpaul	return;
809123848Swpaul}
810123848Swpaul
811123848Swpaulvoid
812123848Swpaulndis_free_packet(p)
813123848Swpaul	ndis_packet		*p;
814123848Swpaul{
815123848Swpaul	if (p == NULL)
816123848Swpaul		return;
817123848Swpaul
818123848Swpaul	ndis_free_bufs(p->np_private.npp_head);
819124060Swpaul	uma_zfree(ndis_packet_zone, p);
820123848Swpaul
821123848Swpaul	return;
822123848Swpaul}
823123848Swpaul
824123474Swpaulint
825123474Swpaulndis_convert_res(arg)
826123474Swpaul	void			*arg;
827123474Swpaul{
828123474Swpaul	struct ndis_softc	*sc;
829123474Swpaul	ndis_resource_list	*rl = NULL;
830123474Swpaul	cm_partial_resource_desc	*prd = NULL;
831123474Swpaul	ndis_miniport_block	*block;
832123976Swpaul	device_t		dev;
833123976Swpaul	struct resource_list	*brl;
834127411Swpaul	struct resource_list	brl_rev;
835127411Swpaul	struct resource_list_entry	*brle, *n;
836127411Swpaul	int 			error = 0;
837123474Swpaul
838123474Swpaul	sc = arg;
839123474Swpaul	block = &sc->ndis_block;
840123976Swpaul	dev = sc->ndis_dev;
841123474Swpaul
842128012Swpaul	SLIST_INIT(&brl_rev);
843128012Swpaul
844123474Swpaul	rl = malloc(sizeof(ndis_resource_list) +
845123474Swpaul	    (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)),
846123474Swpaul	    M_DEVBUF, M_NOWAIT|M_ZERO);
847123474Swpaul
848123474Swpaul	if (rl == NULL)
849123474Swpaul		return(ENOMEM);
850123474Swpaul
851123474Swpaul	rl->cprl_version = 5;
852123474Swpaul	rl->cprl_version = 1;
853123474Swpaul	rl->cprl_count = sc->ndis_rescnt;
854123474Swpaul	prd = rl->cprl_partial_descs;
855123474Swpaul
856131953Swpaul	brl = BUS_GET_RESOURCE_LIST(dev, dev);
857131953Swpaul
858123976Swpaul	if (brl != NULL) {
859127411Swpaul
860127411Swpaul		/*
861127411Swpaul		 * We have a small problem. Some PCI devices have
862127411Swpaul		 * multiple I/O ranges. Windows orders them starting
863127411Swpaul		 * from lowest numbered BAR to highest. We discover
864127411Swpaul		 * them in that order too, but insert them into a singly
865127411Swpaul		 * linked list head first, which means when time comes
866127411Swpaul		 * to traverse the list, we enumerate them in reverse
867127411Swpaul		 * order. This screws up some drivers which expect the
868127411Swpaul		 * BARs to be in ascending order so that they can choose
869127411Swpaul		 * the "first" one as their register space. Unfortunately,
870127411Swpaul		 * in order to fix this, we have to create our own
871127411Swpaul		 * temporary list with the entries in reverse order.
872127411Swpaul		 */
873123976Swpaul		SLIST_FOREACH(brle, brl, link) {
874127411Swpaul			n = malloc(sizeof(struct resource_list_entry),
875127411Swpaul			    M_TEMP, M_NOWAIT);
876127411Swpaul			if (n == NULL) {
877127411Swpaul				error = ENOMEM;
878127411Swpaul				goto bad;
879127411Swpaul			}
880127411Swpaul			bcopy((char *)brle, (char *)n,
881127411Swpaul			    sizeof(struct resource_list_entry));
882127411Swpaul			SLIST_INSERT_HEAD(&brl_rev, n, link);
883127411Swpaul		}
884127411Swpaul
885127411Swpaul		SLIST_FOREACH(brle, &brl_rev, link) {
886123976Swpaul			switch (brle->type) {
887123976Swpaul			case SYS_RES_IOPORT:
888123976Swpaul				prd->cprd_type = CmResourceTypePort;
889127552Swpaul				prd->cprd_flags = CM_RESOURCE_PORT_IO;
890127552Swpaul				prd->cprd_sharedisp =
891127552Swpaul				    CmResourceShareDeviceExclusive;
892123976Swpaul				prd->u.cprd_port.cprd_start.np_quad =
893123976Swpaul				    brle->start;
894123976Swpaul				prd->u.cprd_port.cprd_len = brle->count;
895123976Swpaul				break;
896123976Swpaul			case SYS_RES_MEMORY:
897123976Swpaul				prd->cprd_type = CmResourceTypeMemory;
898127552Swpaul				prd->cprd_flags =
899127552Swpaul				    CM_RESOURCE_MEMORY_READ_WRITE;
900127552Swpaul				prd->cprd_sharedisp =
901127552Swpaul				    CmResourceShareDeviceExclusive;
902123976Swpaul				prd->u.cprd_port.cprd_start.np_quad =
903123976Swpaul				    brle->start;
904123976Swpaul				prd->u.cprd_port.cprd_len = brle->count;
905123976Swpaul				break;
906123976Swpaul			case SYS_RES_IRQ:
907123976Swpaul				prd->cprd_type = CmResourceTypeInterrupt;
908127552Swpaul				prd->cprd_flags = 0;
909127552Swpaul				prd->cprd_sharedisp =
910127552Swpaul				    CmResourceShareDeviceExclusive;
911123976Swpaul				prd->u.cprd_intr.cprd_level = brle->start;
912123976Swpaul				prd->u.cprd_intr.cprd_vector = brle->start;
913123976Swpaul				prd->u.cprd_intr.cprd_affinity = 0;
914123976Swpaul				break;
915123976Swpaul			default:
916123976Swpaul				break;
917123976Swpaul			}
918123976Swpaul			prd++;
919123976Swpaul		}
920123474Swpaul	}
921123474Swpaul
922123474Swpaul	block->nmb_rlist = rl;
923123474Swpaul
924127411Swpaulbad:
925127411Swpaul
926127411Swpaul	while (!SLIST_EMPTY(&brl_rev)) {
927127411Swpaul		n = SLIST_FIRST(&brl_rev);
928127411Swpaul		SLIST_REMOVE_HEAD(&brl_rev, link);
929127411Swpaul		free (n, M_TEMP);
930127411Swpaul	}
931127411Swpaul
932127411Swpaul	return(error);
933123474Swpaul}
934123474Swpaul
935123474Swpaul/*
936123474Swpaul * Map an NDIS packet to an mbuf list. When an NDIS driver receives a
937123474Swpaul * packet, it will hand it to us in the form of an ndis_packet,
938123474Swpaul * which we need to convert to an mbuf that is then handed off
939123474Swpaul * to the stack. Note: we configure the mbuf list so that it uses
940123474Swpaul * the memory regions specified by the ndis_buffer structures in
941123474Swpaul * the ndis_packet as external storage. In most cases, this will
942123474Swpaul * point to a memory region allocated by the driver (either by
943123474Swpaul * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect
944123474Swpaul * the driver to handle free()ing this region for is, so we set up
945123474Swpaul * a dummy no-op free handler for it.
946123474Swpaul */
947123474Swpaul
948123474Swpaulint
949123474Swpaulndis_ptom(m0, p)
950123474Swpaul	struct mbuf		**m0;
951123474Swpaul	ndis_packet		*p;
952123474Swpaul{
953123474Swpaul	struct mbuf		*m, *prev = NULL;
954123474Swpaul	ndis_buffer		*buf;
955123474Swpaul	ndis_packet_private	*priv;
956123474Swpaul	uint32_t		totlen = 0;
957123474Swpaul
958123474Swpaul	if (p == NULL || m0 == NULL)
959123474Swpaul		return(EINVAL);
960123474Swpaul
961123474Swpaul	priv = &p->np_private;
962123474Swpaul	buf = priv->npp_head;
963123826Swpaul	p->np_refcnt = 0;
964123474Swpaul
965123474Swpaul	for (buf = priv->npp_head; buf != NULL; buf = buf->nb_next) {
966123474Swpaul		if (buf == priv->npp_head)
967123474Swpaul			MGETHDR(m, M_DONTWAIT, MT_HEADER);
968123474Swpaul		else
969123474Swpaul			MGET(m, M_DONTWAIT, MT_DATA);
970123474Swpaul		if (m == NULL) {
971123474Swpaul			m_freem(*m0);
972123474Swpaul			*m0 = NULL;
973123474Swpaul			return(ENOBUFS);
974123474Swpaul		}
975123757Swpaul		m->m_len = buf->nb_bytecount;
976123757Swpaul		m->m_data = MDL_VA(buf);
977123535Swpaul		MEXTADD(m, m->m_data, m->m_len, ndis_return_packet,
978123826Swpaul		    p, 0, EXT_NDIS);
979123826Swpaul		p->np_refcnt++;
980123474Swpaul		totlen += m->m_len;
981123474Swpaul		if (m->m_flags & MT_HEADER)
982123474Swpaul			*m0 = m;
983123474Swpaul		else
984123474Swpaul			prev->m_next = m;
985123474Swpaul		prev = m;
986123474Swpaul	}
987123474Swpaul
988123474Swpaul	(*m0)->m_pkthdr.len = totlen;
989123474Swpaul
990123474Swpaul	return(0);
991123474Swpaul}
992123474Swpaul
993123474Swpaul/*
994123474Swpaul * Create an mbuf chain from an NDIS packet chain.
995123474Swpaul * This is used mainly when transmitting packets, where we need
996123474Swpaul * to turn an mbuf off an interface's send queue and transform it
997123474Swpaul * into an NDIS packet which will be fed into the NDIS driver's
998123474Swpaul * send routine.
999123474Swpaul *
1000123474Swpaul * NDIS packets consist of two parts: an ndis_packet structure,
1001123474Swpaul * which is vaguely analagous to the pkthdr portion of an mbuf,
1002123474Swpaul * and one or more ndis_buffer structures, which define the
1003123474Swpaul * actual memory segments in which the packet data resides.
1004123474Swpaul * We need to allocate one ndis_buffer for each mbuf in a chain,
1005123474Swpaul * plus one ndis_packet as the header.
1006123474Swpaul */
1007123474Swpaul
1008123474Swpaulint
1009123474Swpaulndis_mtop(m0, p)
1010123474Swpaul	struct mbuf		*m0;
1011123474Swpaul	ndis_packet		**p;
1012123474Swpaul{
1013123474Swpaul	struct mbuf		*m;
1014123474Swpaul	ndis_buffer		*buf = NULL, *prev = NULL;
1015123474Swpaul	ndis_packet_private	*priv;
1016123474Swpaul
1017123474Swpaul	if (p == NULL || m0 == NULL)
1018123474Swpaul		return(EINVAL);
1019123474Swpaul
1020123474Swpaul	/* If caller didn't supply a packet, make one. */
1021123474Swpaul	if (*p == NULL) {
1022124060Swpaul		*p = uma_zalloc(ndis_packet_zone, M_NOWAIT|M_ZERO);
1023123474Swpaul
1024123474Swpaul		if (*p == NULL)
1025123474Swpaul			return(ENOMEM);
1026123474Swpaul	}
1027123474Swpaul
1028123474Swpaul	priv = &(*p)->np_private;
1029123474Swpaul	priv->npp_totlen = m0->m_pkthdr.len;
1030123474Swpaul        priv->npp_packetooboffset = offsetof(ndis_packet, np_oob);
1031124278Swpaul	priv->npp_ndispktflags = NDIS_PACKET_ALLOCATED_BY_NDIS;
1032123474Swpaul
1033123474Swpaul	for (m = m0; m != NULL; m = m->m_next) {
1034123810Salfred		if (m->m_len == 0)
1035123474Swpaul			continue;
1036124060Swpaul		buf = uma_zalloc(ndis_buffer_zone, M_NOWAIT | M_ZERO);
1037123474Swpaul		if (buf == NULL) {
1038123474Swpaul			ndis_free_packet(*p);
1039123474Swpaul			*p = NULL;
1040123474Swpaul			return(ENOMEM);
1041123474Swpaul		}
1042123474Swpaul
1043123757Swpaul		MDL_INIT(buf, m->m_data, m->m_len);
1044123474Swpaul		if (priv->npp_head == NULL)
1045123474Swpaul			priv->npp_head = buf;
1046123474Swpaul		else
1047123474Swpaul			prev->nb_next = buf;
1048123474Swpaul		prev = buf;
1049123474Swpaul	}
1050123474Swpaul
1051123474Swpaul	priv->npp_tail = buf;
1052124060Swpaul	priv->npp_totlen = m0->m_pkthdr.len;
1053123474Swpaul
1054123474Swpaul	return(0);
1055123474Swpaul}
1056123474Swpaul
1057123474Swpaulint
1058123474Swpaulndis_get_supported_oids(arg, oids, oidcnt)
1059123474Swpaul	void			*arg;
1060123474Swpaul	ndis_oid		**oids;
1061123474Swpaul	int			*oidcnt;
1062123474Swpaul{
1063123474Swpaul	int			len, rval;
1064123474Swpaul	ndis_oid		*o;
1065123474Swpaul
1066123474Swpaul	if (arg == NULL || oids == NULL || oidcnt == NULL)
1067123474Swpaul		return(EINVAL);
1068123474Swpaul	len = 0;
1069123474Swpaul	ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len);
1070123474Swpaul
1071123474Swpaul	o = malloc(len, M_DEVBUF, M_NOWAIT);
1072123474Swpaul	if (o == NULL)
1073123474Swpaul		return(ENOMEM);
1074123474Swpaul
1075123474Swpaul	rval = ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, o, &len);
1076123474Swpaul
1077123474Swpaul	if (rval) {
1078123474Swpaul		free(o, M_DEVBUF);
1079123474Swpaul		return(rval);
1080123474Swpaul	}
1081123474Swpaul
1082123474Swpaul	*oids = o;
1083123474Swpaul	*oidcnt = len / 4;
1084123474Swpaul
1085123474Swpaul	return(0);
1086123474Swpaul}
1087123474Swpaul
1088123474Swpaulint
1089123474Swpaulndis_set_info(arg, oid, buf, buflen)
1090123474Swpaul	void			*arg;
1091123474Swpaul	ndis_oid		oid;
1092123474Swpaul	void			*buf;
1093123474Swpaul	int			*buflen;
1094123474Swpaul{
1095123474Swpaul	struct ndis_softc	*sc;
1096123474Swpaul	ndis_status		rval;
1097123474Swpaul	ndis_handle		adapter;
1098123474Swpaul	__stdcall ndis_setinfo_handler	setfunc;
1099123474Swpaul	uint32_t		byteswritten = 0, bytesneeded = 0;
1100123695Swpaul	int			error;
1101128229Swpaul	uint8_t			irql;
1102123474Swpaul
1103123474Swpaul	sc = arg;
1104125718Swpaul	NDIS_LOCK(sc);
1105123474Swpaul	setfunc = sc->ndis_chars.nmc_setinfo_func;
1106123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1107125718Swpaul	NDIS_UNLOCK(sc);
1108123474Swpaul
1109125718Swpaul	if (adapter == NULL || setfunc == NULL)
1110125676Swpaul		return(ENXIO);
1111125676Swpaul
1112128229Swpaul	irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1113123474Swpaul	rval = setfunc(adapter, oid, buf, *buflen,
1114123474Swpaul	    &byteswritten, &bytesneeded);
1115128229Swpaul	FASTCALL1(hal_lower_irql, irql);
1116123474Swpaul
1117123695Swpaul	if (rval == NDIS_STATUS_PENDING) {
1118127887Swpaul		PROC_LOCK(curthread->td_proc);
1119127887Swpaul		error = msleep(&sc->ndis_block.nmb_wkupdpctimer,
1120128229Swpaul		    &curthread->td_proc->p_mtx,
1121128229Swpaul		    curthread->td_priority|PDROP,
1122127887Swpaul		    "ndisset", 5 * hz);
1123123695Swpaul		rval = sc->ndis_block.nmb_setstat;
1124123695Swpaul	}
1125123695Swpaul
1126123474Swpaul	if (byteswritten)
1127123474Swpaul		*buflen = byteswritten;
1128123474Swpaul	if (bytesneeded)
1129123474Swpaul		*buflen = bytesneeded;
1130123474Swpaul
1131123474Swpaul	if (rval == NDIS_STATUS_INVALID_LENGTH)
1132123474Swpaul		return(ENOSPC);
1133123474Swpaul
1134123474Swpaul	if (rval == NDIS_STATUS_INVALID_OID)
1135123474Swpaul		return(EINVAL);
1136123474Swpaul
1137123474Swpaul	if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1138123474Swpaul	    rval == NDIS_STATUS_NOT_ACCEPTED)
1139123474Swpaul		return(ENOTSUP);
1140123474Swpaul
1141124809Swpaul	if (rval != NDIS_STATUS_SUCCESS)
1142124809Swpaul		return(ENODEV);
1143124809Swpaul
1144123474Swpaul	return(0);
1145123474Swpaul}
1146123474Swpaul
1147124202Swpaultypedef void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status);
1148124202Swpaul
1149123474Swpaulint
1150123474Swpaulndis_send_packets(arg, packets, cnt)
1151123474Swpaul	void			*arg;
1152123474Swpaul	ndis_packet		**packets;
1153123474Swpaul	int			cnt;
1154123474Swpaul{
1155123474Swpaul	struct ndis_softc	*sc;
1156123474Swpaul	ndis_handle		adapter;
1157123474Swpaul	__stdcall ndis_sendmulti_handler	sendfunc;
1158124202Swpaul	__stdcall ndis_senddone_func		senddonefunc;
1159124202Swpaul	int			i;
1160123858Swpaul	ndis_packet		*p;
1161128229Swpaul	uint8_t			irql;
1162123474Swpaul
1163123474Swpaul	sc = arg;
1164123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1165125718Swpaul	if (adapter == NULL)
1166125718Swpaul		return(ENXIO);
1167123474Swpaul	sendfunc = sc->ndis_chars.nmc_sendmulti_func;
1168124202Swpaul	senddonefunc = sc->ndis_block.nmb_senddone_func;
1169128229Swpaul	irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1170123474Swpaul	sendfunc(adapter, packets, cnt);
1171128229Swpaul	FASTCALL1(hal_lower_irql, irql);
1172123474Swpaul
1173123858Swpaul	for (i = 0; i < cnt; i++) {
1174123858Swpaul		p = packets[i];
1175124005Swpaul		/*
1176124005Swpaul		 * Either the driver already handed the packet to
1177124005Swpaul		 * ndis_txeof() due to a failure, or it wants to keep
1178124005Swpaul		 * it and release it asynchronously later. Skip to the
1179124005Swpaul		 * next one.
1180124005Swpaul		 */
1181124005Swpaul		if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING)
1182123858Swpaul			continue;
1183124202Swpaul		senddonefunc(&sc->ndis_block, p, p->np_oob.npo_status);
1184123858Swpaul	}
1185123858Swpaul
1186123474Swpaul	return(0);
1187123474Swpaul}
1188123474Swpaul
1189123474Swpaulint
1190125377Swpaulndis_send_packet(arg, packet)
1191125377Swpaul	void			*arg;
1192125377Swpaul	ndis_packet		*packet;
1193125377Swpaul{
1194125377Swpaul	struct ndis_softc	*sc;
1195125377Swpaul	ndis_handle		adapter;
1196125377Swpaul	ndis_status		status;
1197125377Swpaul	__stdcall ndis_sendsingle_handler	sendfunc;
1198125377Swpaul	__stdcall ndis_senddone_func		senddonefunc;
1199128229Swpaul	uint8_t			irql;
1200125377Swpaul
1201125377Swpaul	sc = arg;
1202125377Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1203125718Swpaul	if (adapter == NULL)
1204125718Swpaul		return(ENXIO);
1205125377Swpaul	sendfunc = sc->ndis_chars.nmc_sendsingle_func;
1206125377Swpaul	senddonefunc = sc->ndis_block.nmb_senddone_func;
1207125377Swpaul
1208128229Swpaul	irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1209125377Swpaul	status = sendfunc(adapter, packet, packet->np_private.npp_flags);
1210128229Swpaul	FASTCALL1(hal_lower_irql, irql);
1211125377Swpaul
1212125377Swpaul	if (status == NDIS_STATUS_PENDING)
1213125377Swpaul		return(0);
1214125377Swpaul
1215125377Swpaul	senddonefunc(&sc->ndis_block, packet, status);
1216125377Swpaul
1217125377Swpaul	return(0);
1218125377Swpaul}
1219125377Swpaul
1220125377Swpaulint
1221123474Swpaulndis_init_dma(arg)
1222123474Swpaul	void			*arg;
1223123474Swpaul{
1224123474Swpaul	struct ndis_softc	*sc;
1225123474Swpaul	int			i, error;
1226123474Swpaul
1227123474Swpaul	sc = arg;
1228123474Swpaul
1229123474Swpaul	sc->ndis_tmaps = malloc(sizeof(bus_dmamap_t) * sc->ndis_maxpkts,
1230123474Swpaul	    M_DEVBUF, M_NOWAIT|M_ZERO);
1231123474Swpaul
1232123474Swpaul	if (sc->ndis_tmaps == NULL)
1233123474Swpaul		return(ENOMEM);
1234123474Swpaul
1235123474Swpaul	for (i = 0; i < sc->ndis_maxpkts; i++) {
1236123474Swpaul		error = bus_dmamap_create(sc->ndis_ttag, 0,
1237123474Swpaul		    &sc->ndis_tmaps[i]);
1238123474Swpaul		if (error) {
1239123474Swpaul			free(sc->ndis_tmaps, M_DEVBUF);
1240123474Swpaul			return(ENODEV);
1241123474Swpaul		}
1242123474Swpaul	}
1243123474Swpaul
1244123474Swpaul	return(0);
1245123474Swpaul}
1246123474Swpaul
1247123474Swpaulint
1248123474Swpaulndis_destroy_dma(arg)
1249123474Swpaul	void			*arg;
1250123474Swpaul{
1251123474Swpaul	struct ndis_softc	*sc;
1252123535Swpaul	struct mbuf		*m;
1253123535Swpaul	ndis_packet		*p = NULL;
1254123474Swpaul	int			i;
1255123474Swpaul
1256123474Swpaul	sc = arg;
1257123474Swpaul
1258123474Swpaul	for (i = 0; i < sc->ndis_maxpkts; i++) {
1259123535Swpaul		if (sc->ndis_txarray[i] != NULL) {
1260123535Swpaul			p = sc->ndis_txarray[i];
1261123535Swpaul			m = (struct mbuf *)p->np_rsvd[1];
1262123535Swpaul			if (m != NULL)
1263123535Swpaul				m_freem(m);
1264123535Swpaul			ndis_free_packet(sc->ndis_txarray[i]);
1265123535Swpaul		}
1266123474Swpaul		bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]);
1267123474Swpaul	}
1268123474Swpaul
1269123474Swpaul	free(sc->ndis_tmaps, M_DEVBUF);
1270123474Swpaul
1271123474Swpaul	bus_dma_tag_destroy(sc->ndis_ttag);
1272123474Swpaul
1273123474Swpaul	return(0);
1274123474Swpaul}
1275123474Swpaul
1276123474Swpaulint
1277123474Swpaulndis_reset_nic(arg)
1278123474Swpaul	void			*arg;
1279123474Swpaul{
1280123474Swpaul	struct ndis_softc	*sc;
1281123474Swpaul	ndis_handle		adapter;
1282123474Swpaul	__stdcall ndis_reset_handler	resetfunc;
1283123474Swpaul	uint8_t			addressing_reset;
1284123474Swpaul	struct ifnet		*ifp;
1285127887Swpaul	int			rval;
1286128229Swpaul	uint8_t			irql;
1287123474Swpaul
1288123474Swpaul	sc = arg;
1289123474Swpaul	ifp = &sc->arpcom.ac_if;
1290125718Swpaul	NDIS_LOCK(sc);
1291123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1292125718Swpaul	resetfunc = sc->ndis_chars.nmc_reset_func;
1293125718Swpaul	NDIS_UNLOCK(sc);
1294125718Swpaul	if (adapter == NULL || resetfunc == NULL)
1295123474Swpaul		return(EIO);
1296123474Swpaul
1297128229Swpaul	irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1298127887Swpaul	rval = resetfunc(&addressing_reset, adapter);
1299128229Swpaul	FASTCALL1(hal_lower_irql, irql);
1300128229Swpaul
1301127887Swpaul	if (rval == NDIS_STATUS_PENDING) {
1302127887Swpaul		PROC_LOCK(curthread->td_proc);
1303127887Swpaul		msleep(sc, &curthread->td_proc->p_mtx,
1304128229Swpaul		    curthread->td_priority|PDROP, "ndisrst", 0);
1305127887Swpaul	}
1306123474Swpaul
1307123474Swpaul	return(0);
1308123474Swpaul}
1309123474Swpaul
1310123474Swpaulint
1311123474Swpaulndis_halt_nic(arg)
1312123474Swpaul	void			*arg;
1313123474Swpaul{
1314123474Swpaul	struct ndis_softc	*sc;
1315123474Swpaul	ndis_handle		adapter;
1316123474Swpaul	__stdcall ndis_halt_handler	haltfunc;
1317123474Swpaul	struct ifnet		*ifp;
1318123474Swpaul
1319123474Swpaul	sc = arg;
1320123474Swpaul	ifp = &sc->arpcom.ac_if;
1321125718Swpaul
1322125718Swpaul	NDIS_LOCK(sc);
1323123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1324125718Swpaul	if (adapter == NULL) {
1325125718Swpaul		NDIS_UNLOCK(sc);
1326123474Swpaul		return(EIO);
1327125718Swpaul	}
1328123821Swpaul
1329123474Swpaul	/*
1330123474Swpaul	 * The adapter context is only valid after the init
1331123474Swpaul	 * handler has been called, and is invalid once the
1332123474Swpaul	 * halt handler has been called.
1333123474Swpaul	 */
1334123474Swpaul
1335125718Swpaul	haltfunc = sc->ndis_chars.nmc_halt_func;
1336125718Swpaul	NDIS_UNLOCK(sc);
1337125718Swpaul
1338125718Swpaul	haltfunc(adapter);
1339125718Swpaul
1340125718Swpaul	NDIS_LOCK(sc);
1341123474Swpaul	sc->ndis_block.nmb_miniportadapterctx = NULL;
1342125718Swpaul	NDIS_UNLOCK(sc);
1343123821Swpaul
1344123474Swpaul	return(0);
1345123474Swpaul}
1346123474Swpaul
1347123474Swpaulint
1348123474Swpaulndis_shutdown_nic(arg)
1349123474Swpaul	void			*arg;
1350123474Swpaul{
1351123474Swpaul	struct ndis_softc	*sc;
1352123474Swpaul	ndis_handle		adapter;
1353123474Swpaul	__stdcall ndis_shutdown_handler	shutdownfunc;
1354123474Swpaul
1355123474Swpaul	sc = arg;
1356125718Swpaul	NDIS_LOCK(sc);
1357123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1358125718Swpaul	shutdownfunc = sc->ndis_chars.nmc_shutdown_handler;
1359125718Swpaul	NDIS_UNLOCK(sc);
1360125718Swpaul	if (adapter == NULL || shutdownfunc == NULL)
1361123474Swpaul		return(EIO);
1362123474Swpaul
1363123485Swpaul	if (sc->ndis_chars.nmc_rsvd0 == NULL)
1364123485Swpaul		shutdownfunc(adapter);
1365123485Swpaul	else
1366123485Swpaul		shutdownfunc(sc->ndis_chars.nmc_rsvd0);
1367123474Swpaul
1368125006Swpaul	ndis_shrink_thrqueue(8);
1369125057Swpaul	TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link);
1370125006Swpaul
1371123474Swpaul	return(0);
1372123474Swpaul}
1373123474Swpaul
1374123474Swpaulint
1375123474Swpaulndis_init_nic(arg)
1376123474Swpaul	void			*arg;
1377123474Swpaul{
1378123474Swpaul	struct ndis_softc	*sc;
1379123474Swpaul	ndis_miniport_block	*block;
1380123474Swpaul        __stdcall ndis_init_handler	initfunc;
1381123474Swpaul	ndis_status		status, openstatus = 0;
1382123474Swpaul	ndis_medium		mediumarray[NdisMediumMax];
1383123474Swpaul	uint32_t		chosenmedium, i;
1384123474Swpaul
1385123474Swpaul	if (arg == NULL)
1386123474Swpaul		return(EINVAL);
1387123474Swpaul
1388123474Swpaul	sc = arg;
1389125718Swpaul	NDIS_LOCK(sc);
1390123474Swpaul	block = &sc->ndis_block;
1391123474Swpaul	initfunc = sc->ndis_chars.nmc_init_func;
1392125718Swpaul	NDIS_UNLOCK(sc);
1393123474Swpaul
1394123821Swpaul	TAILQ_INIT(&block->nmb_timerlist);
1395123821Swpaul
1396123474Swpaul	for (i = 0; i < NdisMediumMax; i++)
1397123474Swpaul		mediumarray[i] = i;
1398123474Swpaul
1399123474Swpaul        status = initfunc(&openstatus, &chosenmedium,
1400123474Swpaul            mediumarray, NdisMediumMax, block, block);
1401123474Swpaul
1402123474Swpaul	/*
1403123474Swpaul	 * If the init fails, blow away the other exported routines
1404123474Swpaul	 * we obtained from the driver so we can't call them later.
1405123474Swpaul	 * If the init failed, none of these will work.
1406123474Swpaul	 */
1407123474Swpaul	if (status != NDIS_STATUS_SUCCESS) {
1408125718Swpaul		NDIS_LOCK(sc);
1409125676Swpaul		sc->ndis_block.nmb_miniportadapterctx = NULL;
1410125718Swpaul		NDIS_UNLOCK(sc);
1411123474Swpaul		return(ENXIO);
1412123474Swpaul	}
1413123474Swpaul
1414123474Swpaul	return(0);
1415123474Swpaul}
1416123474Swpaul
1417123474Swpaulvoid
1418123474Swpaulndis_enable_intr(arg)
1419123474Swpaul	void			*arg;
1420123474Swpaul{
1421123474Swpaul	struct ndis_softc	*sc;
1422123474Swpaul	ndis_handle		adapter;
1423123474Swpaul	__stdcall ndis_enable_interrupts_handler	intrenbfunc;
1424123474Swpaul
1425123474Swpaul	sc = arg;
1426123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1427123474Swpaul	intrenbfunc = sc->ndis_chars.nmc_enable_interrupts_func;
1428125718Swpaul	if (adapter == NULL || intrenbfunc == NULL)
1429123474Swpaul		return;
1430123474Swpaul	intrenbfunc(adapter);
1431123474Swpaul
1432123474Swpaul	return;
1433123474Swpaul}
1434123474Swpaul
1435123474Swpaulvoid
1436123474Swpaulndis_disable_intr(arg)
1437123474Swpaul	void			*arg;
1438123474Swpaul{
1439123474Swpaul	struct ndis_softc	*sc;
1440123474Swpaul	ndis_handle		adapter;
1441123474Swpaul	__stdcall ndis_disable_interrupts_handler	intrdisfunc;
1442123474Swpaul
1443123474Swpaul	sc = arg;
1444125718Swpaul	NDIS_LOCK(sc);
1445123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1446125718Swpaul	intrdisfunc = sc->ndis_chars.nmc_disable_interrupts_func;
1447125718Swpaul	NDIS_UNLOCK(sc);
1448126834Swpaul	if (adapter == NULL || intrdisfunc == NULL)
1449123474Swpaul	    return;
1450123474Swpaul	intrdisfunc(adapter);
1451123474Swpaul
1452123474Swpaul	return;
1453123474Swpaul}
1454123474Swpaul
1455123474Swpaulint
1456123474Swpaulndis_isr(arg, ourintr, callhandler)
1457123474Swpaul	void			*arg;
1458123474Swpaul	int			*ourintr;
1459123474Swpaul	int			*callhandler;
1460123474Swpaul{
1461123474Swpaul	struct ndis_softc	*sc;
1462123474Swpaul	ndis_handle		adapter;
1463123474Swpaul	__stdcall ndis_isr_handler	isrfunc;
1464123474Swpaul	uint8_t			accepted, queue;
1465123474Swpaul
1466123474Swpaul	if (arg == NULL || ourintr == NULL || callhandler == NULL)
1467123474Swpaul		return(EINVAL);
1468123474Swpaul
1469123474Swpaul	sc = arg;
1470123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1471123474Swpaul	isrfunc = sc->ndis_chars.nmc_isr_func;
1472125718Swpaul	if (adapter == NULL || isrfunc == NULL)
1473125718Swpaul		return(ENXIO);
1474125718Swpaul
1475123474Swpaul	isrfunc(&accepted, &queue, adapter);
1476123474Swpaul	*ourintr = accepted;
1477123474Swpaul	*callhandler = queue;
1478123474Swpaul
1479123474Swpaul	return(0);
1480123474Swpaul}
1481123474Swpaul
1482123474Swpaulint
1483123474Swpaulndis_intrhand(arg)
1484123474Swpaul	void			*arg;
1485123474Swpaul{
1486123474Swpaul	struct ndis_softc	*sc;
1487123474Swpaul	ndis_handle		adapter;
1488123474Swpaul	__stdcall ndis_interrupt_handler	intrfunc;
1489123474Swpaul
1490123474Swpaul	if (arg == NULL)
1491123474Swpaul		return(EINVAL);
1492123474Swpaul
1493123474Swpaul	sc = arg;
1494125718Swpaul	NDIS_LOCK(sc);
1495123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1496123474Swpaul	intrfunc = sc->ndis_chars.nmc_interrupt_func;
1497125718Swpaul	NDIS_UNLOCK(sc);
1498125718Swpaul	if (adapter == NULL || intrfunc == NULL)
1499125718Swpaul		return(EINVAL);
1500125718Swpaul
1501123474Swpaul	intrfunc(adapter);
1502123474Swpaul
1503123474Swpaul	return(0);
1504123474Swpaul}
1505123474Swpaul
1506123474Swpaulint
1507123474Swpaulndis_get_info(arg, oid, buf, buflen)
1508123474Swpaul	void			*arg;
1509123474Swpaul	ndis_oid		oid;
1510123474Swpaul	void			*buf;
1511123474Swpaul	int			*buflen;
1512123474Swpaul{
1513123474Swpaul	struct ndis_softc	*sc;
1514123474Swpaul	ndis_status		rval;
1515123474Swpaul	ndis_handle		adapter;
1516123474Swpaul	__stdcall ndis_queryinfo_handler	queryfunc;
1517123474Swpaul	uint32_t		byteswritten = 0, bytesneeded = 0;
1518123695Swpaul	int			error;
1519128229Swpaul	uint8_t			irql;
1520123474Swpaul
1521123474Swpaul	sc = arg;
1522125718Swpaul	NDIS_LOCK(sc);
1523123474Swpaul	queryfunc = sc->ndis_chars.nmc_queryinfo_func;
1524123474Swpaul	adapter = sc->ndis_block.nmb_miniportadapterctx;
1525125718Swpaul	NDIS_UNLOCK(sc);
1526123474Swpaul
1527125718Swpaul	if (adapter == NULL || queryfunc == NULL)
1528125676Swpaul		return(ENXIO);
1529125676Swpaul
1530128229Swpaul	irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1531123474Swpaul	rval = queryfunc(adapter, oid, buf, *buflen,
1532123474Swpaul	    &byteswritten, &bytesneeded);
1533128229Swpaul	FASTCALL1(hal_lower_irql, irql);
1534123474Swpaul
1535123695Swpaul	/* Wait for requests that block. */
1536123695Swpaul
1537123695Swpaul	if (rval == NDIS_STATUS_PENDING) {
1538127887Swpaul		PROC_LOCK(curthread->td_proc);
1539127887Swpaul		error = msleep(&sc->ndis_block.nmb_wkupdpctimer,
1540128229Swpaul		    &curthread->td_proc->p_mtx,
1541128229Swpaul		    curthread->td_priority|PDROP,
1542127887Swpaul		    "ndisget", 5 * hz);
1543123695Swpaul		rval = sc->ndis_block.nmb_getstat;
1544123695Swpaul	}
1545123695Swpaul
1546123474Swpaul	if (byteswritten)
1547123474Swpaul		*buflen = byteswritten;
1548123474Swpaul	if (bytesneeded)
1549123474Swpaul		*buflen = bytesneeded;
1550123474Swpaul
1551123474Swpaul	if (rval == NDIS_STATUS_INVALID_LENGTH ||
1552123474Swpaul	    rval == NDIS_STATUS_BUFFER_TOO_SHORT)
1553123474Swpaul		return(ENOSPC);
1554123474Swpaul
1555123474Swpaul	if (rval == NDIS_STATUS_INVALID_OID)
1556123474Swpaul		return(EINVAL);
1557123474Swpaul
1558123474Swpaul	if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1559123474Swpaul	    rval == NDIS_STATUS_NOT_ACCEPTED)
1560123474Swpaul		return(ENOTSUP);
1561123474Swpaul
1562124809Swpaul	if (rval != NDIS_STATUS_SUCCESS)
1563124809Swpaul		return(ENODEV);
1564124809Swpaul
1565123474Swpaul	return(0);
1566123474Swpaul}
1567123474Swpaul
1568123474Swpaulint
1569123474Swpaulndis_unload_driver(arg)
1570123474Swpaul	void			*arg;
1571123474Swpaul{
1572123474Swpaul	struct ndis_softc	*sc;
1573123474Swpaul
1574123474Swpaul	sc = arg;
1575123474Swpaul
1576123474Swpaul	free(sc->ndis_block.nmb_rlist, M_DEVBUF);
1577123474Swpaul
1578123474Swpaul	ndis_flush_sysctls(sc);
1579123474Swpaul
1580124697Swpaul	ndis_shrink_thrqueue(8);
1581125057Swpaul	TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link);
1582124697Swpaul
1583123474Swpaul	return(0);
1584123474Swpaul}
1585123474Swpaul
1586127887Swpaul#define NDIS_LOADED		htonl(0x42534F44)
1587124446Swpaul
1588123474Swpaulint
1589123474Swpaulndis_load_driver(img, arg)
1590123474Swpaul	vm_offset_t		img;
1591123474Swpaul	void			*arg;
1592123474Swpaul{
1593123474Swpaul	__stdcall driver_entry	entry;
1594123474Swpaul	image_optional_header	opt_hdr;
1595123474Swpaul	image_import_descriptor imp_desc;
1596123474Swpaul	ndis_unicode_string	dummystr;
1597123474Swpaul        ndis_miniport_block     *block;
1598123474Swpaul	ndis_status		status;
1599123474Swpaul	int			idx;
1600123474Swpaul	uint32_t		*ptr;
1601123474Swpaul	struct ndis_softc	*sc;
1602123474Swpaul
1603123474Swpaul	sc = arg;
1604123474Swpaul
1605124446Swpaul	/*
1606124446Swpaul	 * Only perform the relocation/linking phase once
1607124446Swpaul	 * since the binary image may be shared among multiple
1608124446Swpaul	 * device instances.
1609124446Swpaul	 */
1610123474Swpaul
1611124446Swpaul	ptr = (uint32_t *)(img + 8);
1612124446Swpaul	if (*ptr != NDIS_LOADED) {
1613124446Swpaul		/* Perform text relocation */
1614124446Swpaul		if (pe_relocate(img))
1615124446Swpaul			return(ENOEXEC);
1616123474Swpaul
1617124446Swpaul		/* Dynamically link the NDIS.SYS routines -- required. */
1618124446Swpaul		if (pe_patch_imports(img, "NDIS", ndis_functbl))
1619124446Swpaul			return(ENOEXEC);
1620123474Swpaul
1621124446Swpaul		/* Dynamically link the HAL.dll routines -- also required. */
1622124446Swpaul		if (pe_patch_imports(img, "HAL", hal_functbl))
1623123474Swpaul			return(ENOEXEC);
1624124446Swpaul
1625124446Swpaul		/* Dynamically link ntoskrnl.exe -- optional. */
1626124446Swpaul		if (pe_get_import_descriptor(img,
1627124446Swpaul		    &imp_desc, "ntoskrnl") == 0) {
1628124446Swpaul			if (pe_patch_imports(img,
1629124446Swpaul			    "ntoskrnl", ntoskrnl_functbl))
1630124446Swpaul				return(ENOEXEC);
1631124446Swpaul		}
1632124446Swpaul		*ptr = NDIS_LOADED;
1633123474Swpaul	}
1634123474Swpaul
1635123474Swpaul        /* Locate the driver entry point */
1636123474Swpaul	pe_get_optional_header(img, &opt_hdr);
1637123474Swpaul	entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
1638123474Swpaul
1639125551Swpaul	dummystr.nus_len = strlen(NDIS_DUMMY_PATH) * 2;
1640125551Swpaul	dummystr.nus_maxlen = strlen(NDIS_DUMMY_PATH) * 2;
1641123474Swpaul	dummystr.nus_buf = NULL;
1642123474Swpaul	ndis_ascii_to_unicode(NDIS_DUMMY_PATH, &dummystr.nus_buf);
1643123474Swpaul
1644123474Swpaul	/*
1645123474Swpaul	 * Now that we have the miniport driver characteristics,
1646123474Swpaul	 * create an NDIS block and call the init handler.
1647123474Swpaul	 * This will cause the driver to try to probe for
1648123474Swpaul	 * a device.
1649123474Swpaul	 */
1650123474Swpaul
1651123474Swpaul	block = &sc->ndis_block;
1652123474Swpaul
1653125551Swpaul	ptr = (uint32_t *)block;
1654123474Swpaul	for (idx = 0; idx < sizeof(ndis_miniport_block) / 4; idx++) {
1655123474Swpaul		*ptr = idx | 0xdead0000;
1656123474Swpaul		ptr++;
1657123474Swpaul	}
1658123474Swpaul
1659123474Swpaul	block->nmb_signature = (void *)0xcafebabe;
1660123474Swpaul	block->nmb_setdone_func = ndis_setdone_func;
1661123535Swpaul	block->nmb_querydone_func = ndis_getdone_func;
1662123474Swpaul	block->nmb_status_func = ndis_status_func;
1663123474Swpaul	block->nmb_statusdone_func = ndis_statusdone_func;
1664123474Swpaul	block->nmb_resetdone_func = ndis_resetdone_func;
1665124100Swpaul	block->nmb_sendrsrc_func = ndis_sendrsrcavail_func;
1666123474Swpaul
1667123474Swpaul	block->nmb_ifp = &sc->arpcom.ac_if;
1668123474Swpaul	block->nmb_dev = sc->ndis_dev;
1669124165Swpaul	block->nmb_img = img;
1670125551Swpaul	block->nmb_devobj.do_rsvd = block;
1671123474Swpaul
1672125551Swpaul	/*
1673125551Swpaul	 * Now call the DriverEntry() routine. This will cause
1674125551Swpaul	 * a callout to the NdisInitializeWrapper() and
1675125551Swpaul	 * NdisMRegisterMiniport() routines.
1676125551Swpaul	 */
1677125551Swpaul	status = entry(&block->nmb_devobj, &dummystr);
1678125551Swpaul
1679125551Swpaul	free (dummystr.nus_buf, M_DEVBUF);
1680125551Swpaul
1681125551Swpaul	if (status != NDIS_STATUS_SUCCESS)
1682125551Swpaul		return(ENODEV);
1683125551Swpaul
1684124697Swpaul	ndis_enlarge_thrqueue(8);
1685124697Swpaul
1686125057Swpaul	TAILQ_INSERT_TAIL(&ndis_devhead, block, link);
1687125006Swpaul
1688123474Swpaul	return(0);
1689123474Swpaul}
1690