kern_ndis.c revision 124202
1/*
2 * Copyright (c) 2003
3 *	Bill Paul <wpaul@windriver.com>.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30 * THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: head/sys/compat/ndis/kern_ndis.c 124202 2004-01-07 06:15:56Z wpaul $");
35
36#include <sys/param.h>
37#include <sys/types.h>
38#include <sys/errno.h>
39#include <sys/callout.h>
40#include <sys/socket.h>
41#include <sys/queue.h>
42#include <sys/sysctl.h>
43#include <sys/systm.h>
44#include <sys/malloc.h>
45#include <sys/lock.h>
46#include <sys/mutex.h>
47#include <sys/conf.h>
48#include <sys/taskqueue.h>
49
50#include <sys/kernel.h>
51#include <machine/bus.h>
52#include <machine/resource.h>
53#include <sys/bus.h>
54#include <sys/rman.h>
55
56#include <vm/uma.h>
57
58#include <net/if.h>
59#include <net/if_arp.h>
60#include <net/ethernet.h>
61#include <net/if_dl.h>
62#include <net/if_media.h>
63
64#include <net80211/ieee80211_var.h>
65#include <net80211/ieee80211_ioctl.h>
66
67#include <dev/pccard/pccardvar.h>
68#include "card_if.h"
69
70#include <compat/ndis/pe_var.h>
71#include <compat/ndis/resource_var.h>
72#include <compat/ndis/ndis_var.h>
73#include <compat/ndis/hal_var.h>
74#include <compat/ndis/ntoskrnl_var.h>
75#include <compat/ndis/cfg_var.h>
76#include <dev/if_ndis/if_ndisvar.h>
77
78#define __stdcall __attribute__((__stdcall__))
79#define NDIS_DUMMY_PATH "\\\\some\\bogus\\path"
80
81__stdcall static void ndis_status_func(ndis_handle, ndis_status,
82	void *, uint32_t);
83__stdcall static void ndis_statusdone_func(ndis_handle);
84__stdcall static void ndis_setdone_func(ndis_handle, ndis_status);
85__stdcall static void ndis_getdone_func(ndis_handle, ndis_status);
86__stdcall static void ndis_resetdone_func(ndis_handle, ndis_status, uint8_t);
87__stdcall static void ndis_sendrsrcavail_func(ndis_handle);
88
89static uma_zone_t ndis_packet_zone, ndis_buffer_zone;
90
91/*
92 * This allows us to export our symbols to other modules.
93 * Note that we call ourselves 'ndisapi' to avoid a namespace
94 * collision with if_ndis.ko, which internally calls itself
95 * 'ndis.'
96 */
97static int
98ndis_modevent(module_t mod, int cmd, void *arg)
99{
100	int			error = 0;
101
102	switch (cmd) {
103	case MOD_LOAD:
104		/* Initialize subsystems */
105		ndis_libinit();
106		ntoskrnl_libinit();
107
108		/* Initialize TX buffer UMA zone. */
109		ndis_packet_zone = uma_zcreate("NDIS packet",
110		    sizeof(ndis_packet), NULL, NULL, NULL,
111		    NULL, UMA_ALIGN_PTR, 0);
112		ndis_buffer_zone = uma_zcreate("NDIS buffer",
113		    sizeof(ndis_buffer), NULL, NULL, NULL,
114		    NULL, UMA_ALIGN_PTR, 0);
115		break;
116	case MOD_UNLOAD:
117	case MOD_SHUTDOWN:
118		/* Shut down subsystems */
119		ndis_libfini();
120		ntoskrnl_libfini();
121
122		/* Remove zones */
123		uma_zdestroy(ndis_packet_zone);
124		uma_zdestroy(ndis_buffer_zone);
125		break;
126	default:
127		error = EINVAL;
128		break;
129	}
130
131	return(error);
132}
133DEV_MODULE(ndisapi, ndis_modevent, NULL);
134MODULE_VERSION(ndisapi, 1);
135
136
137__stdcall static void
138ndis_sendrsrcavail_func(adapter)
139	ndis_handle		adapter;
140{
141	return;
142}
143
144__stdcall static void
145ndis_status_func(adapter, status, sbuf, slen)
146	ndis_handle		adapter;
147	ndis_status		status;
148	void			*sbuf;
149	uint32_t		slen;
150{
151	ndis_miniport_block	*block;
152	block = adapter;
153
154	device_printf (block->nmb_dev, "status: %x\n", status);
155	return;
156}
157
158__stdcall static void
159ndis_statusdone_func(adapter)
160	ndis_handle		adapter;
161{
162	ndis_miniport_block	*block;
163	block = adapter;
164
165	device_printf (block->nmb_dev, "status complete\n");
166	return;
167}
168
169__stdcall static void
170ndis_setdone_func(adapter, status)
171	ndis_handle		adapter;
172	ndis_status		status;
173{
174	ndis_miniport_block	*block;
175	block = adapter;
176
177	block->nmb_setstat = status;
178	wakeup(&block->nmb_wkupdpctimer);
179	return;
180}
181
182__stdcall static void
183ndis_getdone_func(adapter, status)
184	ndis_handle		adapter;
185	ndis_status		status;
186{
187	ndis_miniport_block	*block;
188	block = adapter;
189
190	block->nmb_getstat = status;
191	wakeup(&block->nmb_wkupdpctimer);
192	return;
193}
194
195__stdcall static void
196ndis_resetdone_func(adapter, status, addressingreset)
197	ndis_handle		adapter;
198	ndis_status		status;
199	uint8_t			addressingreset;
200{
201	ndis_miniport_block	*block;
202	block = adapter;
203
204	device_printf (block->nmb_dev, "reset done...\n");
205	return;
206}
207
208#define NDIS_AM_RID	3
209
210int
211ndis_alloc_amem(arg)
212	void			*arg;
213{
214	struct ndis_softc	*sc;
215	int			error, rid;
216
217	if (arg == NULL)
218		return(EINVAL);
219
220	sc = arg;
221	rid = NDIS_AM_RID;
222	sc->ndis_res_am = bus_alloc_resource(sc->ndis_dev, SYS_RES_MEMORY,
223	    &rid, 0UL, ~0UL, 0x1000, RF_ACTIVE);
224
225	if (sc->ndis_res_am == NULL) {
226		device_printf(sc->ndis_dev,
227		    "failed to allocate attribute memory\n");
228		return(ENXIO);
229	}
230
231	error = CARD_SET_MEMORY_OFFSET(device_get_parent(sc->ndis_dev),
232	    sc->ndis_dev, rid, 0, NULL);
233
234	if (error) {
235		device_printf(sc->ndis_dev,
236		    "CARD_SET_MEMORY_OFFSET() returned 0x%x\n", error);
237		return(error);
238	}
239
240	error = CARD_SET_RES_FLAGS(device_get_parent(sc->ndis_dev),
241	    sc->ndis_dev, SYS_RES_MEMORY, rid, PCCARD_A_MEM_ATTR);
242
243	if (error) {
244		device_printf(sc->ndis_dev,
245		    "CARD_SET_RES_FLAGS() returned 0x%x\n", error);
246		return(error);
247	}
248
249	return(0);
250}
251
252int
253ndis_create_sysctls(arg)
254	void			*arg;
255{
256	struct ndis_softc	*sc;
257	ndis_cfg		*vals;
258	char			buf[256];
259
260	if (arg == NULL)
261		return(EINVAL);
262
263	sc = arg;
264	vals = sc->ndis_regvals;
265
266	TAILQ_INIT(&sc->ndis_cfglist_head);
267
268	/* Create the sysctl tree. */
269
270	sc->ndis_tree = SYSCTL_ADD_NODE(&sc->ndis_ctx,
271	    SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
272	    device_get_nameunit(sc->ndis_dev), CTLFLAG_RD, 0,
273	    device_get_desc(sc->ndis_dev));
274
275	/* Add the driver-specific registry keys. */
276
277	vals = sc->ndis_regvals;
278	while(1) {
279		if (vals->nc_cfgkey == NULL)
280			break;
281		if (vals->nc_idx != sc->ndis_devidx) {
282			vals++;
283			continue;
284		}
285		SYSCTL_ADD_STRING(&sc->ndis_ctx,
286		    SYSCTL_CHILDREN(sc->ndis_tree),
287		    OID_AUTO, vals->nc_cfgkey,
288		    CTLFLAG_RW, vals->nc_val,
289		    sizeof(vals->nc_val),
290		    vals->nc_cfgdesc);
291		vals++;
292	}
293
294	/* Now add a couple of builtin keys. */
295
296	/*
297	 * Environment can be either Windows (0) or WindowsNT (1).
298	 * We qualify as the latter.
299	 */
300	ndis_add_sysctl(sc, "Environment",
301	    "Windows environment", "1", CTLFLAG_RD);
302
303	/* NDIS version should be 5.1. */
304	ndis_add_sysctl(sc, "NdisVersion",
305	    "NDIS API Version", "0x00050001", CTLFLAG_RD);
306
307	/* Bus type (PCI, PCMCIA, etc...) */
308	sprintf(buf, "%d\n", (int)sc->ndis_iftype);
309	ndis_add_sysctl(sc, "BusType", "Bus Type", buf, CTLFLAG_RD);
310
311	if (sc->ndis_res_io != NULL) {
312		sprintf(buf, "0x%lx\n", rman_get_start(sc->ndis_res_io));
313		ndis_add_sysctl(sc, "IOBaseAddress",
314		    "Base I/O Address", buf, CTLFLAG_RD);
315	}
316
317	if (sc->ndis_irq != NULL) {
318		sprintf(buf, "%lu\n", rman_get_start(sc->ndis_irq));
319		ndis_add_sysctl(sc, "InterruptNumber",
320		    "Interrupt Number", buf, CTLFLAG_RD);
321	}
322
323	return(0);
324}
325
326int
327ndis_add_sysctl(arg, key, desc, val, flag)
328	void			*arg;
329	char			*key;
330	char			*desc;
331	char			*val;
332	int			flag;
333{
334	struct ndis_softc	*sc;
335	struct ndis_cfglist	*cfg;
336	char			descstr[256];
337
338	sc = arg;
339
340	cfg = malloc(sizeof(struct ndis_cfglist), M_DEVBUF, M_NOWAIT|M_ZERO);
341
342	if (cfg == NULL)
343		return(ENOMEM);
344
345	cfg->ndis_cfg.nc_cfgkey = strdup(key, M_DEVBUF);
346	if (desc == NULL) {
347		snprintf(descstr, sizeof(descstr), "%s (dynamic)", key);
348		cfg->ndis_cfg.nc_cfgdesc = strdup(descstr, M_DEVBUF);
349	} else
350		cfg->ndis_cfg.nc_cfgdesc = strdup(desc, M_DEVBUF);
351	strcpy(cfg->ndis_cfg.nc_val, val);
352
353	TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link);
354
355	SYSCTL_ADD_STRING(&sc->ndis_ctx, SYSCTL_CHILDREN(sc->ndis_tree),
356	    OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag,
357	    cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val),
358	    cfg->ndis_cfg.nc_cfgdesc);
359
360	return(0);
361}
362
363int
364ndis_flush_sysctls(arg)
365	void			*arg;
366{
367	struct ndis_softc	*sc;
368	struct ndis_cfglist	*cfg;
369
370	sc = arg;
371
372	while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) {
373		cfg = TAILQ_FIRST(&sc->ndis_cfglist_head);
374		TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link);
375		free(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF);
376		free(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF);
377		free(cfg, M_DEVBUF);
378	}
379
380	return(0);
381}
382
383void
384ndis_return_packet(buf, arg)
385	void			*buf;	/* not used */
386	void			*arg;
387{
388	struct ndis_softc	*sc;
389	ndis_handle		adapter;
390	ndis_packet		*p;
391	__stdcall ndis_return_handler	returnfunc;
392
393	if (arg == NULL)
394		return;
395
396	p = arg;
397
398	/* Decrement refcount. */
399	p->np_refcnt--;
400
401	/* Release packet when refcount hits zero, otherwise return. */
402	if (p->np_refcnt)
403		return;
404
405	sc = p->np_softc;
406	returnfunc = sc->ndis_chars.nmc_return_packet_func;
407	adapter = sc->ndis_block.nmb_miniportadapterctx;
408	if (returnfunc != NULL)
409		returnfunc(adapter, p);
410
411	return;
412}
413
414void
415ndis_free_bufs(b0)
416	ndis_buffer		*b0;
417{
418	ndis_buffer		*next;
419
420	if (b0 == NULL)
421		return;
422
423	while(b0 != NULL) {
424		next = b0->nb_next;
425		uma_zfree (ndis_buffer_zone, b0);
426		b0 = next;
427	}
428
429	return;
430}
431
432void
433ndis_free_packet(p)
434	ndis_packet		*p;
435{
436	if (p == NULL)
437		return;
438
439	ndis_free_bufs(p->np_private.npp_head);
440	uma_zfree(ndis_packet_zone, p);
441
442	return;
443}
444
445int
446ndis_convert_res(arg)
447	void			*arg;
448{
449	struct ndis_softc	*sc;
450	ndis_resource_list	*rl = NULL;
451	cm_partial_resource_desc	*prd = NULL;
452	ndis_miniport_block	*block;
453	device_t		dev;
454	struct resource_list	*brl;
455	struct resource_list_entry	*brle;
456
457	sc = arg;
458	block = &sc->ndis_block;
459	dev = sc->ndis_dev;
460
461	rl = malloc(sizeof(ndis_resource_list) +
462	    (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)),
463	    M_DEVBUF, M_NOWAIT|M_ZERO);
464
465	if (rl == NULL)
466		return(ENOMEM);
467
468	rl->cprl_version = 5;
469	rl->cprl_version = 1;
470	rl->cprl_count = sc->ndis_rescnt;
471	prd = rl->cprl_partial_descs;
472
473	brl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
474	if (brl != NULL) {
475		SLIST_FOREACH(brle, brl, link) {
476			switch (brle->type) {
477			case SYS_RES_IOPORT:
478				prd->cprd_type = CmResourceTypePort;
479				prd->u.cprd_port.cprd_start.np_quad =
480				    brle->start;
481				prd->u.cprd_port.cprd_len = brle->count;
482				break;
483			case SYS_RES_MEMORY:
484				prd->cprd_type = CmResourceTypeMemory;
485				prd->u.cprd_port.cprd_start.np_quad =
486				    brle->start;
487				prd->u.cprd_port.cprd_len = brle->count;
488				break;
489			case SYS_RES_IRQ:
490				prd->cprd_type = CmResourceTypeInterrupt;
491				prd->u.cprd_intr.cprd_level = brle->start;
492				prd->u.cprd_intr.cprd_vector = brle->start;
493				prd->u.cprd_intr.cprd_affinity = 0;
494				break;
495			default:
496				break;
497			}
498			prd++;
499		}
500	}
501
502	block->nmb_rlist = rl;
503
504	return(0);
505}
506
507/*
508 * Map an NDIS packet to an mbuf list. When an NDIS driver receives a
509 * packet, it will hand it to us in the form of an ndis_packet,
510 * which we need to convert to an mbuf that is then handed off
511 * to the stack. Note: we configure the mbuf list so that it uses
512 * the memory regions specified by the ndis_buffer structures in
513 * the ndis_packet as external storage. In most cases, this will
514 * point to a memory region allocated by the driver (either by
515 * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect
516 * the driver to handle free()ing this region for is, so we set up
517 * a dummy no-op free handler for it.
518 */
519
520int
521ndis_ptom(m0, p)
522	struct mbuf		**m0;
523	ndis_packet		*p;
524{
525	struct mbuf		*m, *prev = NULL;
526	ndis_buffer		*buf;
527	ndis_packet_private	*priv;
528	uint32_t		totlen = 0;
529
530	if (p == NULL || m0 == NULL)
531		return(EINVAL);
532
533	priv = &p->np_private;
534	buf = priv->npp_head;
535	p->np_refcnt = 0;
536
537	for (buf = priv->npp_head; buf != NULL; buf = buf->nb_next) {
538		if (buf == priv->npp_head)
539			MGETHDR(m, M_DONTWAIT, MT_HEADER);
540		else
541			MGET(m, M_DONTWAIT, MT_DATA);
542		if (m == NULL) {
543			m_freem(*m0);
544			*m0 = NULL;
545			return(ENOBUFS);
546		}
547		m->m_len = buf->nb_bytecount;
548		m->m_data = MDL_VA(buf);
549		MEXTADD(m, m->m_data, m->m_len, ndis_return_packet,
550		    p, 0, EXT_NDIS);
551		p->np_refcnt++;
552		totlen += m->m_len;
553		if (m->m_flags & MT_HEADER)
554			*m0 = m;
555		else
556			prev->m_next = m;
557		prev = m;
558	}
559
560	(*m0)->m_pkthdr.len = totlen;
561
562	return(0);
563}
564
565/*
566 * Create an mbuf chain from an NDIS packet chain.
567 * This is used mainly when transmitting packets, where we need
568 * to turn an mbuf off an interface's send queue and transform it
569 * into an NDIS packet which will be fed into the NDIS driver's
570 * send routine.
571 *
572 * NDIS packets consist of two parts: an ndis_packet structure,
573 * which is vaguely analagous to the pkthdr portion of an mbuf,
574 * and one or more ndis_buffer structures, which define the
575 * actual memory segments in which the packet data resides.
576 * We need to allocate one ndis_buffer for each mbuf in a chain,
577 * plus one ndis_packet as the header.
578 */
579
580int
581ndis_mtop(m0, p)
582	struct mbuf		*m0;
583	ndis_packet		**p;
584{
585	struct mbuf		*m;
586	ndis_buffer		*buf = NULL, *prev = NULL;
587	ndis_packet_private	*priv;
588
589	if (p == NULL || m0 == NULL)
590		return(EINVAL);
591
592	/* If caller didn't supply a packet, make one. */
593	if (*p == NULL) {
594		*p = uma_zalloc(ndis_packet_zone, M_NOWAIT|M_ZERO);
595
596		if (*p == NULL)
597			return(ENOMEM);
598	}
599
600	priv = &(*p)->np_private;
601	priv->npp_totlen = m0->m_pkthdr.len;
602        priv->npp_packetooboffset = offsetof(ndis_packet, np_oob);
603
604	for (m = m0; m != NULL; m = m->m_next) {
605		if (m->m_len == 0)
606			continue;
607		buf = uma_zalloc(ndis_buffer_zone, M_NOWAIT | M_ZERO);
608		if (buf == NULL) {
609			ndis_free_packet(*p);
610			*p = NULL;
611			return(ENOMEM);
612		}
613
614		MDL_INIT(buf, m->m_data, m->m_len);
615		if (priv->npp_head == NULL)
616			priv->npp_head = buf;
617		else
618			prev->nb_next = buf;
619		prev = buf;
620	}
621
622	priv->npp_tail = buf;
623	priv->npp_totlen = m0->m_pkthdr.len;
624
625	return(0);
626}
627
628int
629ndis_get_supported_oids(arg, oids, oidcnt)
630	void			*arg;
631	ndis_oid		**oids;
632	int			*oidcnt;
633{
634	int			len, rval;
635	ndis_oid		*o;
636
637	if (arg == NULL || oids == NULL || oidcnt == NULL)
638		return(EINVAL);
639	len = 0;
640	ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len);
641
642	o = malloc(len, M_DEVBUF, M_NOWAIT);
643	if (o == NULL)
644		return(ENOMEM);
645
646	rval = ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, o, &len);
647
648	if (rval) {
649		free(o, M_DEVBUF);
650		return(rval);
651	}
652
653	*oids = o;
654	*oidcnt = len / 4;
655
656	return(0);
657}
658
659int
660ndis_set_info(arg, oid, buf, buflen)
661	void			*arg;
662	ndis_oid		oid;
663	void			*buf;
664	int			*buflen;
665{
666	struct ndis_softc	*sc;
667	ndis_status		rval;
668	ndis_handle		adapter;
669	__stdcall ndis_setinfo_handler	setfunc;
670	uint32_t		byteswritten = 0, bytesneeded = 0;
671	struct timeval		tv;
672	int			error;
673
674	sc = arg;
675	setfunc = sc->ndis_chars.nmc_setinfo_func;
676	adapter = sc->ndis_block.nmb_miniportadapterctx;
677
678	rval = setfunc(adapter, oid, buf, *buflen,
679	    &byteswritten, &bytesneeded);
680
681	if (rval == NDIS_STATUS_PENDING) {
682		tv.tv_sec = 60;
683		tv.tv_usec = 0;
684		error = tsleep(&sc->ndis_block.nmb_wkupdpctimer,
685		    PPAUSE|PCATCH, "ndisset", tvtohz(&tv));
686		rval = sc->ndis_block.nmb_setstat;
687	}
688
689	if (byteswritten)
690		*buflen = byteswritten;
691	if (bytesneeded)
692		*buflen = bytesneeded;
693
694	if (rval == NDIS_STATUS_INVALID_LENGTH)
695		return(ENOSPC);
696
697	if (rval == NDIS_STATUS_INVALID_OID)
698		return(EINVAL);
699
700	if (rval == NDIS_STATUS_NOT_SUPPORTED ||
701	    rval == NDIS_STATUS_NOT_ACCEPTED)
702		return(ENOTSUP);
703
704	return(0);
705}
706
707typedef void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status);
708
709int
710ndis_send_packets(arg, packets, cnt)
711	void			*arg;
712	ndis_packet		**packets;
713	int			cnt;
714{
715	struct ndis_softc	*sc;
716	ndis_handle		adapter;
717	__stdcall ndis_sendmulti_handler	sendfunc;
718	__stdcall ndis_senddone_func		senddonefunc;
719	int			i;
720	ndis_packet		*p;
721
722	sc = arg;
723	adapter = sc->ndis_block.nmb_miniportadapterctx;
724	sendfunc = sc->ndis_chars.nmc_sendmulti_func;
725	senddonefunc = sc->ndis_block.nmb_senddone_func;
726	sendfunc(adapter, packets, cnt);
727
728	for (i = 0; i < cnt; i++) {
729		p = packets[i];
730		/*
731		 * Either the driver already handed the packet to
732		 * ndis_txeof() due to a failure, or it wants to keep
733		 * it and release it asynchronously later. Skip to the
734		 * next one.
735		 */
736		if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING)
737			continue;
738		senddonefunc(&sc->ndis_block, p, p->np_oob.npo_status);
739	}
740
741	return(0);
742}
743
744int
745ndis_init_dma(arg)
746	void			*arg;
747{
748	struct ndis_softc	*sc;
749	int			i, error;
750
751	sc = arg;
752
753	sc->ndis_tmaps = malloc(sizeof(bus_dmamap_t) * sc->ndis_maxpkts,
754	    M_DEVBUF, M_NOWAIT|M_ZERO);
755
756	if (sc->ndis_tmaps == NULL)
757		return(ENOMEM);
758
759	for (i = 0; i < sc->ndis_maxpkts; i++) {
760		error = bus_dmamap_create(sc->ndis_ttag, 0,
761		    &sc->ndis_tmaps[i]);
762		if (error) {
763			free(sc->ndis_tmaps, M_DEVBUF);
764			return(ENODEV);
765		}
766	}
767
768	return(0);
769}
770
771int
772ndis_destroy_dma(arg)
773	void			*arg;
774{
775	struct ndis_softc	*sc;
776	struct mbuf		*m;
777	ndis_packet		*p = NULL;
778	int			i;
779
780	sc = arg;
781
782	for (i = 0; i < sc->ndis_maxpkts; i++) {
783		if (sc->ndis_txarray[i] != NULL) {
784			p = sc->ndis_txarray[i];
785			m = (struct mbuf *)p->np_rsvd[1];
786			if (m != NULL)
787				m_freem(m);
788			ndis_free_packet(sc->ndis_txarray[i]);
789		}
790		bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]);
791	}
792
793	free(sc->ndis_tmaps, M_DEVBUF);
794
795	bus_dma_tag_destroy(sc->ndis_ttag);
796
797	return(0);
798}
799
800int
801ndis_reset_nic(arg)
802	void			*arg;
803{
804	struct ndis_softc	*sc;
805	ndis_handle		adapter;
806	__stdcall ndis_reset_handler	resetfunc;
807	uint8_t			addressing_reset;
808	struct ifnet		*ifp;
809
810	sc = arg;
811	ifp = &sc->arpcom.ac_if;
812	adapter = sc->ndis_block.nmb_miniportadapterctx;
813	if (adapter == NULL)
814		return(EIO);
815	resetfunc = sc->ndis_chars.nmc_reset_func;
816
817	if (resetfunc == NULL)
818		return(EINVAL);
819
820	resetfunc(&addressing_reset, adapter);
821
822	return(0);
823}
824
825int
826ndis_halt_nic(arg)
827	void			*arg;
828{
829	struct ndis_softc	*sc;
830	ndis_handle		adapter;
831	__stdcall ndis_halt_handler	haltfunc;
832	struct ifnet		*ifp;
833	struct ndis_timer_entry	*ne;
834
835	sc = arg;
836	ifp = &sc->arpcom.ac_if;
837	adapter = sc->ndis_block.nmb_miniportadapterctx;
838	if (adapter == NULL)
839		return(EIO);
840
841	haltfunc = sc->ndis_chars.nmc_halt_func;
842
843	if (haltfunc == NULL)
844		return(EINVAL);
845
846	haltfunc(adapter);
847
848	/*
849	 * The adapter context is only valid after the init
850	 * handler has been called, and is invalid once the
851	 * halt handler has been called.
852	 */
853
854	sc->ndis_block.nmb_miniportadapterctx = NULL;
855
856	/* Clobber all the timers in case the driver left one running. */
857
858	while (!TAILQ_EMPTY(&sc->ndis_block.nmb_timerlist)) {
859		ne = TAILQ_FIRST(&sc->ndis_block.nmb_timerlist);
860		TAILQ_REMOVE(&sc->ndis_block.nmb_timerlist, ne, link);
861		callout_stop(&ne->nte_ch);
862		free(ne, M_DEVBUF);
863	}
864
865	return(0);
866}
867
868int
869ndis_shutdown_nic(arg)
870	void			*arg;
871{
872	struct ndis_softc	*sc;
873	ndis_handle		adapter;
874	__stdcall ndis_shutdown_handler	shutdownfunc;
875
876
877	sc = arg;
878	adapter = sc->ndis_block.nmb_miniportadapterctx;
879	if (adapter == NULL)
880		return(EIO);
881	shutdownfunc = sc->ndis_chars.nmc_shutdown_handler;
882
883	if (shutdownfunc == NULL)
884		return(EINVAL);
885
886	if (sc->ndis_chars.nmc_rsvd0 == NULL)
887		shutdownfunc(adapter);
888	else
889		shutdownfunc(sc->ndis_chars.nmc_rsvd0);
890
891	return(0);
892}
893
894int
895ndis_init_nic(arg)
896	void			*arg;
897{
898	struct ndis_softc	*sc;
899	ndis_miniport_block	*block;
900        __stdcall ndis_init_handler	initfunc;
901	ndis_status		status, openstatus = 0;
902	ndis_medium		mediumarray[NdisMediumMax];
903	uint32_t		chosenmedium, i;
904
905	if (arg == NULL)
906		return(EINVAL);
907
908	sc = arg;
909	block = &sc->ndis_block;
910	initfunc = sc->ndis_chars.nmc_init_func;
911
912	TAILQ_INIT(&block->nmb_timerlist);
913
914	for (i = 0; i < NdisMediumMax; i++)
915		mediumarray[i] = i;
916
917        status = initfunc(&openstatus, &chosenmedium,
918            mediumarray, NdisMediumMax, block, block);
919
920	/*
921	 * If the init fails, blow away the other exported routines
922	 * we obtained from the driver so we can't call them later.
923	 * If the init failed, none of these will work.
924	 */
925	if (status != NDIS_STATUS_SUCCESS) {
926		bzero((char *)&sc->ndis_chars,
927		    sizeof(ndis_miniport_characteristics));
928		return(ENXIO);
929	}
930
931	return(0);
932}
933
934void
935ndis_enable_intr(arg)
936	void			*arg;
937{
938	struct ndis_softc	*sc;
939	ndis_handle		adapter;
940	__stdcall ndis_enable_interrupts_handler	intrenbfunc;
941
942	sc = arg;
943	adapter = sc->ndis_block.nmb_miniportadapterctx;
944	if (adapter == NULL)
945	    return;
946	intrenbfunc = sc->ndis_chars.nmc_enable_interrupts_func;
947	if (intrenbfunc == NULL)
948		return;
949	intrenbfunc(adapter);
950
951	return;
952}
953
954void
955ndis_disable_intr(arg)
956	void			*arg;
957{
958	struct ndis_softc	*sc;
959	ndis_handle		adapter;
960	__stdcall ndis_disable_interrupts_handler	intrdisfunc;
961
962	sc = arg;
963	adapter = sc->ndis_block.nmb_miniportadapterctx;
964	if (adapter == NULL)
965	    return;
966	intrdisfunc = sc->ndis_chars.nmc_disable_interrupts_func;
967	if (intrdisfunc == NULL)
968		return;
969	intrdisfunc(adapter);
970
971	return;
972}
973
974int
975ndis_isr(arg, ourintr, callhandler)
976	void			*arg;
977	int			*ourintr;
978	int			*callhandler;
979{
980	struct ndis_softc	*sc;
981	ndis_handle		adapter;
982	__stdcall ndis_isr_handler	isrfunc;
983	uint8_t			accepted, queue;
984
985	if (arg == NULL || ourintr == NULL || callhandler == NULL)
986		return(EINVAL);
987
988	sc = arg;
989	adapter = sc->ndis_block.nmb_miniportadapterctx;
990	isrfunc = sc->ndis_chars.nmc_isr_func;
991	isrfunc(&accepted, &queue, adapter);
992	*ourintr = accepted;
993	*callhandler = queue;
994
995	return(0);
996}
997
998int
999ndis_intrhand(arg)
1000	void			*arg;
1001{
1002	struct ndis_softc	*sc;
1003	ndis_handle		adapter;
1004	__stdcall ndis_interrupt_handler	intrfunc;
1005
1006	if (arg == NULL)
1007		return(EINVAL);
1008
1009	sc = arg;
1010	adapter = sc->ndis_block.nmb_miniportadapterctx;
1011	intrfunc = sc->ndis_chars.nmc_interrupt_func;
1012	intrfunc(adapter);
1013
1014	return(0);
1015}
1016
1017int
1018ndis_get_info(arg, oid, buf, buflen)
1019	void			*arg;
1020	ndis_oid		oid;
1021	void			*buf;
1022	int			*buflen;
1023{
1024	struct ndis_softc	*sc;
1025	ndis_status		rval;
1026	ndis_handle		adapter;
1027	__stdcall ndis_queryinfo_handler	queryfunc;
1028	uint32_t		byteswritten = 0, bytesneeded = 0;
1029	struct timeval		tv;
1030	int			error;
1031
1032	sc = arg;
1033	queryfunc = sc->ndis_chars.nmc_queryinfo_func;
1034	adapter = sc->ndis_block.nmb_miniportadapterctx;
1035
1036	rval = queryfunc(adapter, oid, buf, *buflen,
1037	    &byteswritten, &bytesneeded);
1038
1039	/* Wait for requests that block. */
1040
1041	if (rval == NDIS_STATUS_PENDING) {
1042		tv.tv_sec = 60;
1043		tv.tv_usec = 0;
1044		error = tsleep(&sc->ndis_block.nmb_wkupdpctimer,
1045		    PPAUSE|PCATCH, "ndisget", tvtohz(&tv));
1046		rval = sc->ndis_block.nmb_getstat;
1047	}
1048
1049	if (byteswritten)
1050		*buflen = byteswritten;
1051	if (bytesneeded)
1052		*buflen = bytesneeded;
1053
1054	if (rval == NDIS_STATUS_INVALID_LENGTH ||
1055	    rval == NDIS_STATUS_BUFFER_TOO_SHORT)
1056		return(ENOSPC);
1057
1058	if (rval == NDIS_STATUS_INVALID_OID)
1059		return(EINVAL);
1060
1061	if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1062	    rval == NDIS_STATUS_NOT_ACCEPTED)
1063		return(ENOTSUP);
1064
1065	return(0);
1066}
1067
1068int
1069ndis_unload_driver(arg)
1070	void			*arg;
1071{
1072	struct ndis_softc	*sc;
1073
1074	sc = arg;
1075
1076	free(sc->ndis_block.nmb_rlist, M_DEVBUF);
1077
1078	ndis_flush_sysctls(sc);
1079
1080	return(0);
1081}
1082
1083int
1084ndis_load_driver(img, arg)
1085	vm_offset_t		img;
1086	void			*arg;
1087{
1088	__stdcall driver_entry	entry;
1089	image_optional_header	opt_hdr;
1090	image_import_descriptor imp_desc;
1091	ndis_unicode_string	dummystr;
1092	ndis_driver_object	drv;
1093        ndis_miniport_block     *block;
1094	ndis_status		status;
1095	int			idx;
1096	uint32_t		*ptr;
1097	struct ndis_softc	*sc;
1098
1099	sc = arg;
1100
1101	/* Perform text relocation */
1102	if (pe_relocate(img))
1103		return(ENOEXEC);
1104
1105        /* Dynamically link the NDIS.SYS routines -- required. */
1106	if (pe_patch_imports(img, "NDIS", ndis_functbl))
1107		return(ENOEXEC);
1108
1109	/* Dynamically link the HAL.dll routines -- also required. */
1110	if (pe_patch_imports(img, "HAL", hal_functbl))
1111		return(ENOEXEC);
1112
1113	/* Dynamically link ntoskrnl.exe -- optional. */
1114	if (pe_get_import_descriptor(img, &imp_desc, "ntoskrnl") == 0) {
1115		if (pe_patch_imports(img, "ntoskrnl", ntoskrnl_functbl))
1116			return(ENOEXEC);
1117	}
1118
1119        /* Locate the driver entry point */
1120	pe_get_optional_header(img, &opt_hdr);
1121	entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
1122
1123	/*
1124	 * Now call the DriverEntry() routine. This will cause
1125	 * a callout to the NdisInitializeWrapper() and
1126	 * NdisMRegisterMiniport() routines.
1127	 */
1128	dummystr.nus_len = strlen(NDIS_DUMMY_PATH);
1129	dummystr.nus_maxlen = strlen(NDIS_DUMMY_PATH);
1130	dummystr.nus_buf = NULL;
1131	ndis_ascii_to_unicode(NDIS_DUMMY_PATH, &dummystr.nus_buf);
1132	drv.ndo_ifname = "ndis0";
1133
1134	status = entry(&drv, &dummystr);
1135
1136	free (dummystr.nus_buf, M_DEVBUF);
1137
1138	if (status != NDIS_STATUS_SUCCESS)
1139		return(ENODEV);
1140
1141	/*
1142	 * Now that we have the miniport driver characteristics,
1143	 * create an NDIS block and call the init handler.
1144	 * This will cause the driver to try to probe for
1145	 * a device.
1146	 */
1147
1148	block = &sc->ndis_block;
1149	bcopy((char *)&drv.ndo_chars, (char *)&sc->ndis_chars,
1150	    sizeof(ndis_miniport_characteristics));
1151
1152	/*block->nmb_signature = 0xcafebabe;*/
1153
1154		ptr = (uint32_t *)block;
1155	for (idx = 0; idx < sizeof(ndis_miniport_block) / 4; idx++) {
1156		*ptr = idx | 0xdead0000;
1157		ptr++;
1158	}
1159
1160	block->nmb_signature = (void *)0xcafebabe;
1161	block->nmb_setdone_func = ndis_setdone_func;
1162	block->nmb_querydone_func = ndis_getdone_func;
1163	block->nmb_status_func = ndis_status_func;
1164	block->nmb_statusdone_func = ndis_statusdone_func;
1165	block->nmb_resetdone_func = ndis_resetdone_func;
1166	block->nmb_sendrsrc_func = ndis_sendrsrcavail_func;
1167
1168	block->nmb_ifp = &sc->arpcom.ac_if;
1169	block->nmb_dev = sc->ndis_dev;
1170	block->nmb_img = img;
1171
1172	return(0);
1173}
1174