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