kern_ndis.c revision 124278
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 124278 2004-01-09 06:53:49Z 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", (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", 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", 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	priv->npp_ndispktflags = NDIS_PACKET_ALLOCATED_BY_NDIS;
604
605	for (m = m0; m != NULL; m = m->m_next) {
606		if (m->m_len == 0)
607			continue;
608		buf = uma_zalloc(ndis_buffer_zone, M_NOWAIT | M_ZERO);
609		if (buf == NULL) {
610			ndis_free_packet(*p);
611			*p = NULL;
612			return(ENOMEM);
613		}
614
615		MDL_INIT(buf, m->m_data, m->m_len);
616		if (priv->npp_head == NULL)
617			priv->npp_head = buf;
618		else
619			prev->nb_next = buf;
620		prev = buf;
621	}
622
623	priv->npp_tail = buf;
624	priv->npp_totlen = m0->m_pkthdr.len;
625
626	return(0);
627}
628
629int
630ndis_get_supported_oids(arg, oids, oidcnt)
631	void			*arg;
632	ndis_oid		**oids;
633	int			*oidcnt;
634{
635	int			len, rval;
636	ndis_oid		*o;
637
638	if (arg == NULL || oids == NULL || oidcnt == NULL)
639		return(EINVAL);
640	len = 0;
641	ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len);
642
643	o = malloc(len, M_DEVBUF, M_NOWAIT);
644	if (o == NULL)
645		return(ENOMEM);
646
647	rval = ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, o, &len);
648
649	if (rval) {
650		free(o, M_DEVBUF);
651		return(rval);
652	}
653
654	*oids = o;
655	*oidcnt = len / 4;
656
657	return(0);
658}
659
660int
661ndis_set_info(arg, oid, buf, buflen)
662	void			*arg;
663	ndis_oid		oid;
664	void			*buf;
665	int			*buflen;
666{
667	struct ndis_softc	*sc;
668	ndis_status		rval;
669	ndis_handle		adapter;
670	__stdcall ndis_setinfo_handler	setfunc;
671	uint32_t		byteswritten = 0, bytesneeded = 0;
672	struct timeval		tv;
673	int			error;
674
675	sc = arg;
676	setfunc = sc->ndis_chars.nmc_setinfo_func;
677	adapter = sc->ndis_block.nmb_miniportadapterctx;
678
679	rval = setfunc(adapter, oid, buf, *buflen,
680	    &byteswritten, &bytesneeded);
681
682	if (rval == NDIS_STATUS_PENDING) {
683		tv.tv_sec = 60;
684		tv.tv_usec = 0;
685		error = tsleep(&sc->ndis_block.nmb_wkupdpctimer,
686		    PPAUSE|PCATCH, "ndisset", tvtohz(&tv));
687		rval = sc->ndis_block.nmb_setstat;
688	}
689
690	if (byteswritten)
691		*buflen = byteswritten;
692	if (bytesneeded)
693		*buflen = bytesneeded;
694
695	if (rval == NDIS_STATUS_INVALID_LENGTH)
696		return(ENOSPC);
697
698	if (rval == NDIS_STATUS_INVALID_OID)
699		return(EINVAL);
700
701	if (rval == NDIS_STATUS_NOT_SUPPORTED ||
702	    rval == NDIS_STATUS_NOT_ACCEPTED)
703		return(ENOTSUP);
704
705	return(0);
706}
707
708typedef void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status);
709
710int
711ndis_send_packets(arg, packets, cnt)
712	void			*arg;
713	ndis_packet		**packets;
714	int			cnt;
715{
716	struct ndis_softc	*sc;
717	ndis_handle		adapter;
718	__stdcall ndis_sendmulti_handler	sendfunc;
719	__stdcall ndis_senddone_func		senddonefunc;
720	int			i;
721	ndis_packet		*p;
722
723	sc = arg;
724	adapter = sc->ndis_block.nmb_miniportadapterctx;
725	sendfunc = sc->ndis_chars.nmc_sendmulti_func;
726	senddonefunc = sc->ndis_block.nmb_senddone_func;
727	sendfunc(adapter, packets, cnt);
728
729	for (i = 0; i < cnt; i++) {
730		p = packets[i];
731		/*
732		 * Either the driver already handed the packet to
733		 * ndis_txeof() due to a failure, or it wants to keep
734		 * it and release it asynchronously later. Skip to the
735		 * next one.
736		 */
737		if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING)
738			continue;
739		senddonefunc(&sc->ndis_block, p, p->np_oob.npo_status);
740	}
741
742	return(0);
743}
744
745int
746ndis_init_dma(arg)
747	void			*arg;
748{
749	struct ndis_softc	*sc;
750	int			i, error;
751
752	sc = arg;
753
754	sc->ndis_tmaps = malloc(sizeof(bus_dmamap_t) * sc->ndis_maxpkts,
755	    M_DEVBUF, M_NOWAIT|M_ZERO);
756
757	if (sc->ndis_tmaps == NULL)
758		return(ENOMEM);
759
760	for (i = 0; i < sc->ndis_maxpkts; i++) {
761		error = bus_dmamap_create(sc->ndis_ttag, 0,
762		    &sc->ndis_tmaps[i]);
763		if (error) {
764			free(sc->ndis_tmaps, M_DEVBUF);
765			return(ENODEV);
766		}
767	}
768
769	return(0);
770}
771
772int
773ndis_destroy_dma(arg)
774	void			*arg;
775{
776	struct ndis_softc	*sc;
777	struct mbuf		*m;
778	ndis_packet		*p = NULL;
779	int			i;
780
781	sc = arg;
782
783	for (i = 0; i < sc->ndis_maxpkts; i++) {
784		if (sc->ndis_txarray[i] != NULL) {
785			p = sc->ndis_txarray[i];
786			m = (struct mbuf *)p->np_rsvd[1];
787			if (m != NULL)
788				m_freem(m);
789			ndis_free_packet(sc->ndis_txarray[i]);
790		}
791		bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]);
792	}
793
794	free(sc->ndis_tmaps, M_DEVBUF);
795
796	bus_dma_tag_destroy(sc->ndis_ttag);
797
798	return(0);
799}
800
801int
802ndis_reset_nic(arg)
803	void			*arg;
804{
805	struct ndis_softc	*sc;
806	ndis_handle		adapter;
807	__stdcall ndis_reset_handler	resetfunc;
808	uint8_t			addressing_reset;
809	struct ifnet		*ifp;
810
811	sc = arg;
812	ifp = &sc->arpcom.ac_if;
813	adapter = sc->ndis_block.nmb_miniportadapterctx;
814	if (adapter == NULL)
815		return(EIO);
816	resetfunc = sc->ndis_chars.nmc_reset_func;
817
818	if (resetfunc == NULL)
819		return(EINVAL);
820
821	resetfunc(&addressing_reset, adapter);
822
823	return(0);
824}
825
826int
827ndis_halt_nic(arg)
828	void			*arg;
829{
830	struct ndis_softc	*sc;
831	ndis_handle		adapter;
832	__stdcall ndis_halt_handler	haltfunc;
833	struct ifnet		*ifp;
834	struct ndis_timer_entry	*ne;
835
836	sc = arg;
837	ifp = &sc->arpcom.ac_if;
838	adapter = sc->ndis_block.nmb_miniportadapterctx;
839	if (adapter == NULL)
840		return(EIO);
841
842	haltfunc = sc->ndis_chars.nmc_halt_func;
843
844	if (haltfunc == NULL)
845		return(EINVAL);
846
847	haltfunc(adapter);
848
849	/*
850	 * The adapter context is only valid after the init
851	 * handler has been called, and is invalid once the
852	 * halt handler has been called.
853	 */
854
855	sc->ndis_block.nmb_miniportadapterctx = NULL;
856
857	/* Clobber all the timers in case the driver left one running. */
858
859	while (!TAILQ_EMPTY(&sc->ndis_block.nmb_timerlist)) {
860		ne = TAILQ_FIRST(&sc->ndis_block.nmb_timerlist);
861		TAILQ_REMOVE(&sc->ndis_block.nmb_timerlist, ne, link);
862		callout_stop(&ne->nte_ch);
863		free(ne, M_DEVBUF);
864	}
865
866	return(0);
867}
868
869int
870ndis_shutdown_nic(arg)
871	void			*arg;
872{
873	struct ndis_softc	*sc;
874	ndis_handle		adapter;
875	__stdcall ndis_shutdown_handler	shutdownfunc;
876
877
878	sc = arg;
879	adapter = sc->ndis_block.nmb_miniportadapterctx;
880	if (adapter == NULL)
881		return(EIO);
882	shutdownfunc = sc->ndis_chars.nmc_shutdown_handler;
883
884	if (shutdownfunc == NULL)
885		return(EINVAL);
886
887	if (sc->ndis_chars.nmc_rsvd0 == NULL)
888		shutdownfunc(adapter);
889	else
890		shutdownfunc(sc->ndis_chars.nmc_rsvd0);
891
892	return(0);
893}
894
895int
896ndis_init_nic(arg)
897	void			*arg;
898{
899	struct ndis_softc	*sc;
900	ndis_miniport_block	*block;
901        __stdcall ndis_init_handler	initfunc;
902	ndis_status		status, openstatus = 0;
903	ndis_medium		mediumarray[NdisMediumMax];
904	uint32_t		chosenmedium, i;
905
906	if (arg == NULL)
907		return(EINVAL);
908
909	sc = arg;
910	block = &sc->ndis_block;
911	initfunc = sc->ndis_chars.nmc_init_func;
912
913	TAILQ_INIT(&block->nmb_timerlist);
914
915	for (i = 0; i < NdisMediumMax; i++)
916		mediumarray[i] = i;
917
918        status = initfunc(&openstatus, &chosenmedium,
919            mediumarray, NdisMediumMax, block, block);
920
921	/*
922	 * If the init fails, blow away the other exported routines
923	 * we obtained from the driver so we can't call them later.
924	 * If the init failed, none of these will work.
925	 */
926	if (status != NDIS_STATUS_SUCCESS) {
927		bzero((char *)&sc->ndis_chars,
928		    sizeof(ndis_miniport_characteristics));
929		return(ENXIO);
930	}
931
932	return(0);
933}
934
935void
936ndis_enable_intr(arg)
937	void			*arg;
938{
939	struct ndis_softc	*sc;
940	ndis_handle		adapter;
941	__stdcall ndis_enable_interrupts_handler	intrenbfunc;
942
943	sc = arg;
944	adapter = sc->ndis_block.nmb_miniportadapterctx;
945	if (adapter == NULL)
946	    return;
947	intrenbfunc = sc->ndis_chars.nmc_enable_interrupts_func;
948	if (intrenbfunc == NULL)
949		return;
950	intrenbfunc(adapter);
951
952	return;
953}
954
955void
956ndis_disable_intr(arg)
957	void			*arg;
958{
959	struct ndis_softc	*sc;
960	ndis_handle		adapter;
961	__stdcall ndis_disable_interrupts_handler	intrdisfunc;
962
963	sc = arg;
964	adapter = sc->ndis_block.nmb_miniportadapterctx;
965	if (adapter == NULL)
966	    return;
967	intrdisfunc = sc->ndis_chars.nmc_disable_interrupts_func;
968	if (intrdisfunc == NULL)
969		return;
970	intrdisfunc(adapter);
971
972	return;
973}
974
975int
976ndis_isr(arg, ourintr, callhandler)
977	void			*arg;
978	int			*ourintr;
979	int			*callhandler;
980{
981	struct ndis_softc	*sc;
982	ndis_handle		adapter;
983	__stdcall ndis_isr_handler	isrfunc;
984	uint8_t			accepted, queue;
985
986	if (arg == NULL || ourintr == NULL || callhandler == NULL)
987		return(EINVAL);
988
989	sc = arg;
990	adapter = sc->ndis_block.nmb_miniportadapterctx;
991	isrfunc = sc->ndis_chars.nmc_isr_func;
992	isrfunc(&accepted, &queue, adapter);
993	*ourintr = accepted;
994	*callhandler = queue;
995
996	return(0);
997}
998
999int
1000ndis_intrhand(arg)
1001	void			*arg;
1002{
1003	struct ndis_softc	*sc;
1004	ndis_handle		adapter;
1005	__stdcall ndis_interrupt_handler	intrfunc;
1006
1007	if (arg == NULL)
1008		return(EINVAL);
1009
1010	sc = arg;
1011	adapter = sc->ndis_block.nmb_miniportadapterctx;
1012	intrfunc = sc->ndis_chars.nmc_interrupt_func;
1013	intrfunc(adapter);
1014
1015	return(0);
1016}
1017
1018int
1019ndis_get_info(arg, oid, buf, buflen)
1020	void			*arg;
1021	ndis_oid		oid;
1022	void			*buf;
1023	int			*buflen;
1024{
1025	struct ndis_softc	*sc;
1026	ndis_status		rval;
1027	ndis_handle		adapter;
1028	__stdcall ndis_queryinfo_handler	queryfunc;
1029	uint32_t		byteswritten = 0, bytesneeded = 0;
1030	struct timeval		tv;
1031	int			error;
1032
1033	sc = arg;
1034	queryfunc = sc->ndis_chars.nmc_queryinfo_func;
1035	adapter = sc->ndis_block.nmb_miniportadapterctx;
1036
1037	rval = queryfunc(adapter, oid, buf, *buflen,
1038	    &byteswritten, &bytesneeded);
1039
1040	/* Wait for requests that block. */
1041
1042	if (rval == NDIS_STATUS_PENDING) {
1043		tv.tv_sec = 60;
1044		tv.tv_usec = 0;
1045		error = tsleep(&sc->ndis_block.nmb_wkupdpctimer,
1046		    PPAUSE|PCATCH, "ndisget", tvtohz(&tv));
1047		rval = sc->ndis_block.nmb_getstat;
1048	}
1049
1050	if (byteswritten)
1051		*buflen = byteswritten;
1052	if (bytesneeded)
1053		*buflen = bytesneeded;
1054
1055	if (rval == NDIS_STATUS_INVALID_LENGTH ||
1056	    rval == NDIS_STATUS_BUFFER_TOO_SHORT)
1057		return(ENOSPC);
1058
1059	if (rval == NDIS_STATUS_INVALID_OID)
1060		return(EINVAL);
1061
1062	if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1063	    rval == NDIS_STATUS_NOT_ACCEPTED)
1064		return(ENOTSUP);
1065
1066	return(0);
1067}
1068
1069int
1070ndis_unload_driver(arg)
1071	void			*arg;
1072{
1073	struct ndis_softc	*sc;
1074
1075	sc = arg;
1076
1077	free(sc->ndis_block.nmb_rlist, M_DEVBUF);
1078
1079	ndis_flush_sysctls(sc);
1080
1081	return(0);
1082}
1083
1084int
1085ndis_load_driver(img, arg)
1086	vm_offset_t		img;
1087	void			*arg;
1088{
1089	__stdcall driver_entry	entry;
1090	image_optional_header	opt_hdr;
1091	image_import_descriptor imp_desc;
1092	ndis_unicode_string	dummystr;
1093	ndis_driver_object	drv;
1094        ndis_miniport_block     *block;
1095	ndis_status		status;
1096	int			idx;
1097	uint32_t		*ptr;
1098	struct ndis_softc	*sc;
1099
1100	sc = arg;
1101
1102	/* Perform text relocation */
1103	if (pe_relocate(img))
1104		return(ENOEXEC);
1105
1106        /* Dynamically link the NDIS.SYS routines -- required. */
1107	if (pe_patch_imports(img, "NDIS", ndis_functbl))
1108		return(ENOEXEC);
1109
1110	/* Dynamically link the HAL.dll routines -- also required. */
1111	if (pe_patch_imports(img, "HAL", hal_functbl))
1112		return(ENOEXEC);
1113
1114	/* Dynamically link ntoskrnl.exe -- optional. */
1115	if (pe_get_import_descriptor(img, &imp_desc, "ntoskrnl") == 0) {
1116		if (pe_patch_imports(img, "ntoskrnl", ntoskrnl_functbl))
1117			return(ENOEXEC);
1118	}
1119
1120        /* Locate the driver entry point */
1121	pe_get_optional_header(img, &opt_hdr);
1122	entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
1123
1124	/*
1125	 * Now call the DriverEntry() routine. This will cause
1126	 * a callout to the NdisInitializeWrapper() and
1127	 * NdisMRegisterMiniport() routines.
1128	 */
1129	dummystr.nus_len = strlen(NDIS_DUMMY_PATH);
1130	dummystr.nus_maxlen = strlen(NDIS_DUMMY_PATH);
1131	dummystr.nus_buf = NULL;
1132	ndis_ascii_to_unicode(NDIS_DUMMY_PATH, &dummystr.nus_buf);
1133	drv.ndo_ifname = "ndis0";
1134
1135	status = entry(&drv, &dummystr);
1136
1137	free (dummystr.nus_buf, M_DEVBUF);
1138
1139	if (status != NDIS_STATUS_SUCCESS)
1140		return(ENODEV);
1141
1142	/*
1143	 * Now that we have the miniport driver characteristics,
1144	 * create an NDIS block and call the init handler.
1145	 * This will cause the driver to try to probe for
1146	 * a device.
1147	 */
1148
1149	block = &sc->ndis_block;
1150	bcopy((char *)&drv.ndo_chars, (char *)&sc->ndis_chars,
1151	    sizeof(ndis_miniport_characteristics));
1152
1153	/*block->nmb_signature = 0xcafebabe;*/
1154
1155		ptr = (uint32_t *)block;
1156	for (idx = 0; idx < sizeof(ndis_miniport_block) / 4; idx++) {
1157		*ptr = idx | 0xdead0000;
1158		ptr++;
1159	}
1160
1161	block->nmb_signature = (void *)0xcafebabe;
1162	block->nmb_setdone_func = ndis_setdone_func;
1163	block->nmb_querydone_func = ndis_getdone_func;
1164	block->nmb_status_func = ndis_status_func;
1165	block->nmb_statusdone_func = ndis_statusdone_func;
1166	block->nmb_resetdone_func = ndis_resetdone_func;
1167	block->nmb_sendrsrc_func = ndis_sendrsrcavail_func;
1168
1169	block->nmb_ifp = &sc->arpcom.ac_if;
1170	block->nmb_dev = sc->ndis_dev;
1171	block->nmb_img = img;
1172
1173	return(0);
1174}
1175