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