kernel - lwkt_token revamp
[dragonfly.git] / sys / emulation / ndis / kern_ndis.c
CommitLineData
60d6dac1
MD
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 * $FreeBSD: src/sys/compat/ndis/kern_ndis.c,v 1.57 2004/07/11 00:19:30 wpaul Exp $
f8c7a42d 33 * $DragonFly: src/sys/emulation/ndis/kern_ndis.c,v 1.14 2006/12/20 18:14:41 dillon Exp $
60d6dac1
MD
34 */
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/unistd.h>
39#include <sys/types.h>
40#include <sys/errno.h>
41#include <sys/callout.h>
42#include <sys/socket.h>
43#include <sys/queue.h>
44#include <sys/sysctl.h>
45#include <sys/proc.h>
46#include <sys/malloc.h>
47#include <sys/lock.h>
48#include <sys/conf.h>
49
50#include <sys/kernel.h>
51#include <sys/module.h>
52#include <sys/kthread.h>
60d6dac1
MD
53#include <sys/bus.h>
54#include <sys/rman.h>
55
56#include <net/if.h>
57#include <net/if_arp.h>
58#include <net/ethernet.h>
59#include <net/if_dl.h>
60#include <net/if_media.h>
61
62#include <netproto/802_11/ieee80211_var.h>
63#include <netproto/802_11/ieee80211_ioctl.h>
64
65#include "regcall.h"
66#include "pe_var.h"
67#include "resource_var.h"
68#include "ntoskrnl_var.h"
69#include "ndis_var.h"
70#include "hal_var.h"
71#include "cfg_var.h"
72#include <dev/netif/ndis/if_ndisvar.h>
73
74#define NDIS_DUMMY_PATH "\\\\some\\bogus\\path"
75
76__stdcall static void ndis_status_func(ndis_handle, ndis_status,
77 void *, uint32_t);
78__stdcall static void ndis_statusdone_func(ndis_handle);
79__stdcall static void ndis_setdone_func(ndis_handle, ndis_status);
80__stdcall static void ndis_getdone_func(ndis_handle, ndis_status);
81__stdcall static void ndis_resetdone_func(ndis_handle, ndis_status, uint8_t);
82__stdcall static void ndis_sendrsrcavail_func(ndis_handle);
83
84struct nd_head ndis_devhead;
85
86struct ndis_req {
87 void (*nr_func)(void *);
88 void *nr_arg;
89 int nr_exit;
90 STAILQ_ENTRY(ndis_req) link;
91};
92
93struct ndisproc {
94 struct ndisqhead *np_q;
95 struct thread *np_td;
96 int np_state;
97};
98
99static void ndis_return(void *);
100static int ndis_create_kthreads(void);
101static void ndis_destroy_kthreads(void);
102static void ndis_stop_thread(int);
103static int ndis_enlarge_thrqueue(int);
104static int ndis_shrink_thrqueue(int);
105static void ndis_runq(void *);
106
107static MALLOC_DEFINE(M_NDIS_PACKET, "ndis_packet", "ndis packet slosh");
108static MALLOC_DEFINE(M_NDIS_BUFFER, "ndis_buffer", "ndis buffer slosh");
109struct lwkt_token ndis_thr_token;
110static STAILQ_HEAD(ndisqhead, ndis_req) ndis_ttodo;
111struct ndisqhead ndis_itodo;
112struct ndisqhead ndis_free;
113static int ndis_jobs = 32;
114
115static struct ndisproc ndis_tproc;
116static struct ndisproc ndis_iproc;
117
118/*
119 * This allows us to export our symbols to other modules.
120 * Note that we call ourselves 'ndisapi' to avoid a namespace
121 * collision with if_ndis.ko, which internally calls itself
122 * 'ndis.'
123 */
124static int
125ndis_modevent(module_t mod, int cmd, void *arg)
126{
127 int error = 0;
128
129 switch (cmd) {
130 case MOD_LOAD:
131 /* Initialize subsystems */
132 ndis_libinit();
133 ntoskrnl_libinit();
134
135 /* Initialize TX buffer UMA zone. */
136 ndis_create_kthreads();
137
138 TAILQ_INIT(&ndis_devhead);
139
140 break;
141 case MOD_SHUTDOWN:
142 /* stop kthreads */
143 ndis_destroy_kthreads();
144 if (TAILQ_FIRST(&ndis_devhead) == NULL) {
145 /* Shut down subsystems */
146 ndis_libfini();
147 ntoskrnl_libfini();
148
149 /* Remove zones */
150#if 0 /* YYY */
151 malloc_uninit(M_NDIS_PACKET);
152 malloc_uninit(M_NDIS_BUFFER);
153#endif
154 }
155 break;
156 case MOD_UNLOAD:
157 /* stop kthreads */
158 ndis_destroy_kthreads();
159
160 /* Shut down subsystems */
161 ndis_libfini();
162 ntoskrnl_libfini();
163
164 /* Remove zones */
165#if 0 /* YYY */
166 malloc_uninit(M_NDIS_PACKET);
167 malloc_uninit(M_NDIS_BUFFER);
168#endif
169 break;
170 default:
171 error = EINVAL;
172 break;
173 }
174
175 return(error);
176}
177DEV_MODULE(ndisapi, ndis_modevent, NULL);
178MODULE_VERSION(ndisapi, 1);
179
180/*
181 * We create two kthreads for the NDIS subsystem. One of them is a task
182 * queue for performing various odd jobs. The other is an swi thread
183 * reserved exclusively for running interrupt handlers. The reason we
184 * have our own task queue is that there are some cases where we may
185 * need to sleep for a significant amount of time, and if we were to
186 * use one of the taskqueue threads, we might delay the processing
187 * of other pending tasks which might need to run right away. We have
188 * a separate swi thread because we don't want our interrupt handling
189 * to be delayed either.
190 *
191 * By default there are 32 jobs available to start, and another 8
192 * are added to the free list each time a new device is created.
193 */
194
195static void
2da2a8af 196ndis_runq(void *arg)
60d6dac1
MD
197{
198 struct ndis_req *r = NULL, *die = NULL;
199 struct ndisproc *p;
60d6dac1
MD
200
201 p = arg;
202
203 while (1) {
204
205 /* Sleep, but preserve our original priority. */
206 ndis_thsuspend(p->np_td, 0);
207
208 /* Look for any jobs on the work queue. */
209
3b998fa9 210 lwkt_gettoken(&ndis_thr_token);
60d6dac1
MD
211 p->np_state = NDIS_PSTATE_RUNNING;
212 while(STAILQ_FIRST(p->np_q) != NULL) {
213 r = STAILQ_FIRST(p->np_q);
214 STAILQ_REMOVE_HEAD(p->np_q, link);
3b998fa9 215 lwkt_reltoken(&ndis_thr_token);
60d6dac1
MD
216
217 /* Do the work. */
218
219 if (r->nr_func != NULL)
220 (*r->nr_func)(r->nr_arg);
221
3b998fa9 222 lwkt_gettoken(&ndis_thr_token);
60d6dac1
MD
223 STAILQ_INSERT_HEAD(&ndis_free, r, link);
224
225 /* Check for a shutdown request */
226
227 if (r->nr_exit == TRUE)
228 die = r;
229 }
230 p->np_state = NDIS_PSTATE_SLEEPING;
3b998fa9 231 lwkt_reltoken(&ndis_thr_token);
60d6dac1
MD
232
233 /* Bail if we were told to shut down. */
234
235 if (die != NULL)
236 break;
237 }
238
239 wakeup(die);
240 kthread_exit();
241}
242
243static int
2da2a8af 244ndis_create_kthreads(void)
60d6dac1
MD
245{
246 struct ndis_req *r;
247 int i, error = 0;
248
3b998fa9 249 lwkt_token_init(&ndis_thr_token, 1);
60d6dac1
MD
250
251 STAILQ_INIT(&ndis_ttodo);
252 STAILQ_INIT(&ndis_itodo);
253 STAILQ_INIT(&ndis_free);
254
255 for (i = 0; i < ndis_jobs; i++) {
efda3bd0 256 r = kmalloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK);
60d6dac1
MD
257 STAILQ_INSERT_HEAD(&ndis_free, r, link);
258 }
259
260 if (error == 0) {
261 ndis_tproc.np_q = &ndis_ttodo;
262 ndis_tproc.np_state = NDIS_PSTATE_SLEEPING;
263 error = kthread_create_stk(ndis_runq, &ndis_tproc,
264 &ndis_tproc.np_td, NDIS_KSTACK_PAGES * PAGE_SIZE,
265 "ndis taskqueue");
266 }
267
268 if (error == 0) {
269 ndis_iproc.np_q = &ndis_itodo;
270 ndis_iproc.np_state = NDIS_PSTATE_SLEEPING;
271 error = kthread_create_stk(ndis_runq, &ndis_iproc,
272 &ndis_iproc.np_td, NDIS_KSTACK_PAGES * PAGE_SIZE,
273 "ndis swi");
274 }
275
276 if (error) {
277 while ((r = STAILQ_FIRST(&ndis_free)) != NULL) {
278 STAILQ_REMOVE_HEAD(&ndis_free, link);
efda3bd0 279 kfree(r, M_DEVBUF);
60d6dac1
MD
280 }
281 return(error);
282 }
283
284 return(0);
285}
286
287static void
2da2a8af 288ndis_destroy_kthreads(void)
60d6dac1
MD
289{
290 struct ndis_req *r;
291
292 /* Stop the threads. */
293
294 ndis_stop_thread(NDIS_TASKQUEUE);
295 ndis_stop_thread(NDIS_SWI);
296
297 /* Destroy request structures. */
298
299 while ((r = STAILQ_FIRST(&ndis_free)) != NULL) {
300 STAILQ_REMOVE_HEAD(&ndis_free, link);
efda3bd0 301 kfree(r, M_DEVBUF);
60d6dac1
MD
302 }
303
304 lwkt_token_uninit(&ndis_thr_token);
305
306 return;
307}
308
309static void
2da2a8af 310ndis_stop_thread(int t)
60d6dac1
MD
311{
312 struct ndis_req *r;
313 struct ndisqhead *q;
314 thread_t td;
60d6dac1
MD
315
316 if (t == NDIS_TASKQUEUE) {
317 q = &ndis_ttodo;
318 td = ndis_tproc.np_td;
319 } else {
320 q = &ndis_itodo;
321 td = ndis_iproc.np_td;
322 }
323
324 /* Create and post a special 'exit' job. */
325
3b998fa9 326 lwkt_gettoken(&ndis_thr_token);
60d6dac1
MD
327 r = STAILQ_FIRST(&ndis_free);
328 STAILQ_REMOVE_HEAD(&ndis_free, link);
329 r->nr_func = NULL;
330 r->nr_arg = NULL;
331 r->nr_exit = TRUE;
332 STAILQ_INSERT_TAIL(q, r, link);
3b998fa9 333 lwkt_reltoken(&ndis_thr_token);
60d6dac1
MD
334
335 ndis_thresume(td);
336
337 /* wait for thread exit */
338
339 tsleep(r, PCATCH, "ndisthexit", hz * 60);
340
341 /* Now empty the job list. */
342
3b998fa9 343 lwkt_gettoken(&ndis_thr_token);
60d6dac1
MD
344 while ((r = STAILQ_FIRST(q)) != NULL) {
345 STAILQ_REMOVE_HEAD(q, link);
346 STAILQ_INSERT_HEAD(&ndis_free, r, link);
347 }
3b998fa9 348 lwkt_reltoken(&ndis_thr_token);
60d6dac1
MD
349}
350
351static int
2da2a8af 352ndis_enlarge_thrqueue(int cnt)
60d6dac1
MD
353{
354 struct ndis_req *r;
355 int i;
60d6dac1
MD
356
357 for (i = 0; i < cnt; i++) {
efda3bd0 358 r = kmalloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK);
3b998fa9 359 lwkt_gettoken(&ndis_thr_token);
60d6dac1
MD
360 STAILQ_INSERT_HEAD(&ndis_free, r, link);
361 ndis_jobs++;
3b998fa9 362 lwkt_reltoken(&ndis_thr_token);
60d6dac1
MD
363 }
364
365 return(0);
366}
367
368static int
2da2a8af 369ndis_shrink_thrqueue(int cnt)
60d6dac1
MD
370{
371 struct ndis_req *r;
372 int i;
60d6dac1
MD
373
374 for (i = 0; i < cnt; i++) {
3b998fa9 375 lwkt_gettoken(&ndis_thr_token);
60d6dac1
MD
376 r = STAILQ_FIRST(&ndis_free);
377 if (r == NULL) {
3b998fa9 378 lwkt_reltoken(&ndis_thr_token);
60d6dac1
MD
379 return(ENOMEM);
380 }
381 STAILQ_REMOVE_HEAD(&ndis_free, link);
382 ndis_jobs--;
3b998fa9 383 lwkt_reltoken(&ndis_thr_token);
efda3bd0 384 kfree(r, M_DEVBUF);
60d6dac1
MD
385 }
386
387 return(0);
388}
389
390int
2da2a8af 391ndis_unsched(void (*func)(void *), void *arg, int t)
60d6dac1
MD
392{
393 struct ndis_req *r;
394 struct ndisqhead *q;
395 thread_t td;
60d6dac1
MD
396
397 if (t == NDIS_TASKQUEUE) {
398 q = &ndis_ttodo;
399 td = ndis_tproc.np_td;
400 } else {
401 q = &ndis_itodo;
402 td = ndis_iproc.np_td;
403 }
404
3b998fa9 405 lwkt_gettoken(&ndis_thr_token);
60d6dac1
MD
406 STAILQ_FOREACH(r, q, link) {
407 if (r->nr_func == func && r->nr_arg == arg) {
408 STAILQ_REMOVE(q, r, ndis_req, link);
409 STAILQ_INSERT_HEAD(&ndis_free, r, link);
3b998fa9 410 lwkt_reltoken(&ndis_thr_token);
60d6dac1
MD
411 return(0);
412 }
413 }
414
3b998fa9 415 lwkt_reltoken(&ndis_thr_token);
60d6dac1
MD
416
417 return(ENOENT);
418}
419
420int
2da2a8af 421ndis_sched(void (*func)(void *), void *arg, int t)
60d6dac1
MD
422{
423 struct ndis_req *r;
424 struct ndisqhead *q;
425 thread_t td;
426 int s;
60d6dac1
MD
427
428 if (t == NDIS_TASKQUEUE) {
429 q = &ndis_ttodo;
430 td = ndis_tproc.np_td;
431 } else {
432 q = &ndis_itodo;
433 td = ndis_iproc.np_td;
434 }
435
3b998fa9 436 lwkt_gettoken(&ndis_thr_token);
60d6dac1
MD
437 /*
438 * Check to see if an instance of this job is already
439 * pending. If so, don't bother queuing it again.
440 */
441 STAILQ_FOREACH(r, q, link) {
442 if (r->nr_func == func && r->nr_arg == arg) {
3b998fa9 443 lwkt_reltoken(&ndis_thr_token);
60d6dac1
MD
444 return(0);
445 }
446 }
447 r = STAILQ_FIRST(&ndis_free);
448 if (r == NULL) {
3b998fa9 449 lwkt_reltoken(&ndis_thr_token);
60d6dac1
MD
450 return(EAGAIN);
451 }
452 STAILQ_REMOVE_HEAD(&ndis_free, link);
453 r->nr_func = func;
454 r->nr_arg = arg;
455 r->nr_exit = FALSE;
456 STAILQ_INSERT_TAIL(q, r, link);
457 if (t == NDIS_TASKQUEUE)
458 s = ndis_tproc.np_state;
459 else
460 s = ndis_iproc.np_state;
3b998fa9 461 lwkt_reltoken(&ndis_thr_token);
60d6dac1
MD
462
463 /*
464 * Post the job, but only if the thread is actually blocked
465 * on its own suspend call. If a driver queues up a job with
466 * NdisScheduleWorkItem() which happens to do a KeWaitForObject(),
467 * it may suspend there, and in that case we don't want to wake
468 * it up until KeWaitForObject() gets woken up on its own.
469 */
470 if (s == NDIS_PSTATE_SLEEPING)
471 ndis_thresume(td);
472
473 return(0);
474}
475
476int
2da2a8af 477ndis_thsuspend(thread_t td, int timo)
60d6dac1
MD
478{
479 int error;
480
481 error = tsleep(td, 0, "ndissp", timo);
482 return(error);
483}
484
485void
2da2a8af 486ndis_thresume(struct thread *td)
60d6dac1
MD
487{
488 wakeup(td);
489}
490
491__stdcall static void
2da2a8af 492ndis_sendrsrcavail_func(ndis_handle adapter)
60d6dac1
MD
493{
494 return;
495}
496
497__stdcall static void
2da2a8af
SW
498ndis_status_func(ndis_handle adapter, ndis_status status, void *sbuf,
499 uint32_t slen)
60d6dac1
MD
500{
501 ndis_miniport_block *block;
502 block = adapter;
503
504 if (block->nmb_ifp->if_flags & IFF_DEBUG)
505 device_printf (block->nmb_dev, "status: %x\n", status);
506 return;
507}
508
509__stdcall static void
2da2a8af 510ndis_statusdone_func(ndis_handle adapter)
60d6dac1
MD
511{
512 ndis_miniport_block *block;
513 block = adapter;
514
515 if (block->nmb_ifp->if_flags & IFF_DEBUG)
516 device_printf (block->nmb_dev, "status complete\n");
517 return;
518}
519
520__stdcall static void
2da2a8af 521ndis_setdone_func(ndis_handle adapter, ndis_status status)
60d6dac1
MD
522{
523 ndis_miniport_block *block;
524 block = adapter;
525
526 block->nmb_setstat = status;
527 wakeup(&block->nmb_wkupdpctimer);
528 return;
529}
530
531__stdcall static void
2da2a8af 532ndis_getdone_func(ndis_handle adapter, ndis_status status)
60d6dac1
MD
533{
534 ndis_miniport_block *block;
535 block = adapter;
536
537 block->nmb_getstat = status;
538 wakeup(&block->nmb_wkupdpctimer);
539 return;
540}
541
542__stdcall static void
2da2a8af
SW
543ndis_resetdone_func(ndis_handle adapter, ndis_status status,
544 uint8_t addressingreset)
60d6dac1
MD
545{
546 ndis_miniport_block *block;
547 block = adapter;
548
549 if (block->nmb_ifp->if_flags & IFF_DEBUG)
550 device_printf (block->nmb_dev, "reset done...\n");
551 wakeup(block->nmb_ifp);
552 return;
553}
554
555int
2da2a8af 556ndis_create_sysctls(void *arg)
60d6dac1
MD
557{
558 struct ndis_softc *sc;
559 ndis_cfg *vals;
560 char buf[256];
561
562 if (arg == NULL)
563 return(EINVAL);
564
565 sc = arg;
566 vals = sc->ndis_regvals;
567
568 TAILQ_INIT(&sc->ndis_cfglist_head);
569
570#if __FreeBSD_version < 502113
571 /* Create the sysctl tree. */
572
573 sc->ndis_tree = SYSCTL_ADD_NODE(&sc->ndis_ctx,
574 SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
575 device_get_nameunit(sc->ndis_dev), CTLFLAG_RD, 0,
576 device_get_desc(sc->ndis_dev));
577
578#endif
579 /* Add the driver-specific registry keys. */
580
581 vals = sc->ndis_regvals;
582 while(1) {
583 if (vals->nc_cfgkey == NULL)
584 break;
585 if (vals->nc_idx != sc->ndis_devidx) {
586 vals++;
587 continue;
588 }
589#if 1
590 SYSCTL_ADD_STRING(&sc->ndis_ctx,
591 SYSCTL_CHILDREN(sc->ndis_tree),
592 OID_AUTO, vals->nc_cfgkey,
593 CTLFLAG_RW, vals->nc_val,
594 sizeof(vals->nc_val),
595 vals->nc_cfgdesc);
596#else
597 SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev),
598 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)),
599 OID_AUTO, vals->nc_cfgkey,
600 CTLFLAG_RW, vals->nc_val,
601 sizeof(vals->nc_val),
602 vals->nc_cfgdesc);
603#endif
604 vals++;
605 }
606
607 /* Now add a couple of builtin keys. */
608
609 /*
610 * Environment can be either Windows (0) or WindowsNT (1).
611 * We qualify as the latter.
612 */
613 ndis_add_sysctl(sc, "Environment",
614 "Windows environment", "1", CTLFLAG_RD);
615
616 /* NDIS version should be 5.1. */
617 ndis_add_sysctl(sc, "NdisVersion",
618 "NDIS API Version", "0x00050001", CTLFLAG_RD);
619
620 /* Bus type (PCI, PCMCIA, etc...) */
f8c7a42d 621 ksprintf(buf, "%d", (int)sc->ndis_iftype);
60d6dac1
MD
622 ndis_add_sysctl(sc, "BusType", "Bus Type", buf, CTLFLAG_RD);
623
624 if (sc->ndis_res_io != NULL) {
f8c7a42d 625 ksprintf(buf, "0x%lx", rman_get_start(sc->ndis_res_io));
60d6dac1
MD
626 ndis_add_sysctl(sc, "IOBaseAddress",
627 "Base I/O Address", buf, CTLFLAG_RD);
628 }
629
630 if (sc->ndis_irq != NULL) {
f8c7a42d 631 ksprintf(buf, "%lu", rman_get_start(sc->ndis_irq));
60d6dac1
MD
632 ndis_add_sysctl(sc, "InterruptNumber",
633 "Interrupt Number", buf, CTLFLAG_RD);
634 }
635
636 return(0);
637}
638
639int
2da2a8af 640ndis_add_sysctl(void *arg, char *key, char *desc, char *val, int flag)
60d6dac1
MD
641{
642 struct ndis_softc *sc;
643 struct ndis_cfglist *cfg;
644 char descstr[256];
645
646 sc = arg;
647
efda3bd0 648 cfg = kmalloc(sizeof(struct ndis_cfglist), M_DEVBUF, M_WAITOK|M_ZERO);
59302080 649 cfg->ndis_cfg.nc_cfgkey = kstrdup(key, M_DEVBUF);
60d6dac1 650 if (desc == NULL) {
f8c7a42d 651 ksnprintf(descstr, sizeof(descstr), "%s (dynamic)", key);
59302080 652 cfg->ndis_cfg.nc_cfgdesc = kstrdup(descstr, M_DEVBUF);
60d6dac1 653 } else
59302080 654 cfg->ndis_cfg.nc_cfgdesc = kstrdup(desc, M_DEVBUF);
60d6dac1
MD
655 strcpy(cfg->ndis_cfg.nc_val, val);
656
657 TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link);
658
659#if 1
660 SYSCTL_ADD_STRING(&sc->ndis_ctx, SYSCTL_CHILDREN(sc->ndis_tree),
661 OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag,
662 cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val),
663 cfg->ndis_cfg.nc_cfgdesc);
664#else
665 SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev),
666 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)),
667 OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag,
668 cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val),
669 cfg->ndis_cfg.nc_cfgdesc);
670#endif
671
672 return(0);
673}
674
675int
2da2a8af 676ndis_flush_sysctls(void *arg)
60d6dac1
MD
677{
678 struct ndis_softc *sc;
679 struct ndis_cfglist *cfg;
680
681 sc = arg;
682
683 while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) {
684 cfg = TAILQ_FIRST(&sc->ndis_cfglist_head);
685 TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link);
efda3bd0
MD
686 kfree(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF);
687 kfree(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF);
688 kfree(cfg, M_DEVBUF);
60d6dac1
MD
689 }
690
691 return(0);
692}
693
694static void
2da2a8af 695ndis_return(void *arg)
60d6dac1
MD
696{
697 struct ndis_softc *sc;
385c8bc5 698 ndis_return_handler returnfunc;
60d6dac1
MD
699 ndis_handle adapter;
700 ndis_packet *p;
701 uint8_t irql;
702
703 p = arg;
704 sc = p->np_softc;
705 adapter = sc->ndis_block.nmb_miniportadapterctx;
706
707 if (adapter == NULL)
708 return;
709
710 returnfunc = sc->ndis_chars.nmc_return_packet_func;
711 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
712 returnfunc(adapter, p);
713 FASTCALL1(hal_lower_irql, irql);
714
715 return;
716}
717
718static void
719ndis_extref_packet(void *arg)
720{
721 ndis_packet *p = arg;
722
723 ++p->np_refcnt;
724}
725
726static void
727ndis_extfree_packet(void *arg)
728{
729 ndis_packet *p = arg;
730
731 if (p == NULL)
732 return;
733
734 /* Decrement refcount. */
735 p->np_refcnt--;
736
737 /* Release packet when refcount hits zero, otherwise return. */
738 if (p->np_refcnt)
739 return;
740
741 ndis_sched(ndis_return, p, NDIS_SWI);
742
743 return;
744}
745
746void
747ndis_return_packet(struct ndis_softc *sc, ndis_packet *p)
748{
749 ndis_extfree_packet(p);
750}
751
752void
2da2a8af 753ndis_free_bufs(ndis_buffer *b0)
60d6dac1
MD
754{
755 ndis_buffer *next;
756
757 if (b0 == NULL)
758 return;
759
760 while(b0 != NULL) {
761 next = b0->nb_next;
efda3bd0 762 kfree(b0, M_NDIS_BUFFER);
60d6dac1
MD
763 b0 = next;
764 }
765
766 return;
767}
768
769void
2da2a8af 770ndis_free_packet(ndis_packet *p)
60d6dac1
MD
771{
772 if (p == NULL)
773 return;
774
775 ndis_free_bufs(p->np_private.npp_head);
efda3bd0 776 kfree(p, M_NDIS_PACKET);
60d6dac1
MD
777
778 return;
779}
780
781int
2da2a8af 782ndis_convert_res(void *arg)
60d6dac1
MD
783{
784 struct ndis_softc *sc;
785 ndis_resource_list *rl = NULL;
786 cm_partial_resource_desc *prd = NULL;
787 ndis_miniport_block *block;
788 device_t dev;
789 struct resource_list *brl;
790 struct resource_list brl_rev;
791 struct resource_list_entry *brle, *n;
792 int error = 0;
793
794 sc = arg;
795 block = &sc->ndis_block;
796 dev = sc->ndis_dev;
797
798 SLIST_INIT(&brl_rev);
799
77652cad 800 rl = kmalloc(sizeof(ndis_resource_list) +
60d6dac1 801 (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)),
6147a8af 802 M_DEVBUF, M_WAITOK|M_NULLOK|M_ZERO);
60d6dac1
MD
803
804 if (rl == NULL)
805 return(ENOMEM);
806
807 rl->cprl_version = 5;
808 rl->cprl_version = 1;
809 rl->cprl_count = sc->ndis_rescnt;
810 prd = rl->cprl_partial_descs;
811
812 brl = BUS_GET_RESOURCE_LIST(dev, dev);
813
814 if (brl != NULL) {
815
816 /*
817 * We have a small problem. Some PCI devices have
818 * multiple I/O ranges. Windows orders them starting
819 * from lowest numbered BAR to highest. We discover
820 * them in that order too, but insert them into a singly
821 * linked list head first, which means when time comes
822 * to traverse the list, we enumerate them in reverse
823 * order. This screws up some drivers which expect the
824 * BARs to be in ascending order so that they can choose
825 * the "first" one as their register space. Unfortunately,
826 * in order to fix this, we have to create our own
827 * temporary list with the entries in reverse order.
828 */
829 SLIST_FOREACH(brle, brl, link) {
77652cad 830 n = kmalloc(sizeof(struct resource_list_entry),
6147a8af 831 M_TEMP, M_WAITOK|M_NULLOK);
60d6dac1
MD
832 if (n == NULL) {
833 error = ENOMEM;
834 goto bad;
835 }
836 bcopy((char *)brle, (char *)n,
837 sizeof(struct resource_list_entry));
838 SLIST_INSERT_HEAD(&brl_rev, n, link);
839 }
840
841 SLIST_FOREACH(brle, &brl_rev, link) {
842 switch (brle->type) {
843 case SYS_RES_IOPORT:
844 prd->cprd_type = CmResourceTypePort;
845 prd->cprd_flags = CM_RESOURCE_PORT_IO;
846 prd->cprd_sharedisp =
847 CmResourceShareDeviceExclusive;
848 prd->u.cprd_port.cprd_start.np_quad =
849 brle->start;
850 prd->u.cprd_port.cprd_len = brle->count;
851 break;
852 case SYS_RES_MEMORY:
853 prd->cprd_type = CmResourceTypeMemory;
854 prd->cprd_flags =
855 CM_RESOURCE_MEMORY_READ_WRITE;
856 prd->cprd_sharedisp =
857 CmResourceShareDeviceExclusive;
858 prd->u.cprd_port.cprd_start.np_quad =
859 brle->start;
860 prd->u.cprd_port.cprd_len = brle->count;
861 break;
862 case SYS_RES_IRQ:
863 prd->cprd_type = CmResourceTypeInterrupt;
864 prd->cprd_flags = 0;
865 prd->cprd_sharedisp =
866 CmResourceShareDeviceExclusive;
867 prd->u.cprd_intr.cprd_level = brle->start;
868 prd->u.cprd_intr.cprd_vector = brle->start;
869 prd->u.cprd_intr.cprd_affinity = 0;
870 break;
871 default:
872 break;
873 }
874 prd++;
875 }
876 }
877
878 block->nmb_rlist = rl;
879
880bad:
881
882 while (!SLIST_EMPTY(&brl_rev)) {
883 n = SLIST_FIRST(&brl_rev);
884 SLIST_REMOVE_HEAD(&brl_rev, link);
efda3bd0 885 kfree (n, M_TEMP);
60d6dac1
MD
886 }
887
888 return(error);
889}
890
891/*
892 * Map an NDIS packet to an mbuf list. When an NDIS driver receives a
893 * packet, it will hand it to us in the form of an ndis_packet,
894 * which we need to convert to an mbuf that is then handed off
895 * to the stack. Note: we configure the mbuf list so that it uses
896 * the memory regions specified by the ndis_buffer structures in
897 * the ndis_packet as external storage. In most cases, this will
898 * point to a memory region allocated by the driver (either by
899 * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect
900 * the driver to handle free()ing this region for is, so we set up
901 * a dummy no-op free handler for it.
902 */
903
904int
2da2a8af 905ndis_ptom(struct mbuf **m0, ndis_packet *p)
60d6dac1
MD
906{
907 struct mbuf *m, *prev = NULL;
908 ndis_buffer *buf;
909 ndis_packet_private *priv;
910 uint32_t totlen = 0;
911
912 if (p == NULL || m0 == NULL)
913 return(EINVAL);
914
915 priv = &p->np_private;
916 buf = priv->npp_head;
917 p->np_refcnt = 0;
918
919 for (buf = priv->npp_head; buf != NULL; buf = buf->nb_next) {
920 if (buf == priv->npp_head)
921 MGETHDR(m, MB_DONTWAIT, MT_HEADER);
922 else
923 MGET(m, MB_DONTWAIT, MT_DATA);
924 if (m == NULL) {
925 m_freem(*m0);
926 *m0 = NULL;
927 return(ENOBUFS);
928 }
929 m->m_len = buf->nb_bytecount;
930 m->m_data = MDL_VA(buf);
b542cd49
JS
931 m->m_ext.ext_free = ndis_extfree_packet;
932 m->m_ext.ext_ref = ndis_extref_packet;
60d6dac1
MD
933 m->m_ext.ext_arg = p;
934 m->m_ext.ext_buf = m->m_data;
935 m->m_ext.ext_size = m->m_len;
936 m->m_flags |= M_EXT;
937#if 0
938 MEXTADD(m, m->m_data, m->m_len, ndis_free_packet,
939 p, 0, EXT_NDIS);
940#endif
941 p->np_refcnt++;
942 totlen += m->m_len;
6c46447a 943 if (m->m_flags & M_PKTHDR)
60d6dac1
MD
944 *m0 = m;
945 else
946 prev->m_next = m;
947 prev = m;
948 }
949
950 (*m0)->m_pkthdr.len = totlen;
951
952 return(0);
953}
954
955/*
956 * Create an mbuf chain from an NDIS packet chain.
957 * This is used mainly when transmitting packets, where we need
958 * to turn an mbuf off an interface's send queue and transform it
959 * into an NDIS packet which will be fed into the NDIS driver's
960 * send routine.
961 *
962 * NDIS packets consist of two parts: an ndis_packet structure,
963 * which is vaguely analagous to the pkthdr portion of an mbuf,
964 * and one or more ndis_buffer structures, which define the
965 * actual memory segments in which the packet data resides.
966 * We need to allocate one ndis_buffer for each mbuf in a chain,
967 * plus one ndis_packet as the header.
968 */
969
970int
2da2a8af 971ndis_mtop(struct mbuf *m0, ndis_packet **p)
60d6dac1
MD
972{
973 struct mbuf *m;
974 ndis_buffer *buf = NULL, *prev = NULL;
975 ndis_packet_private *priv;
976
977 if (p == NULL || m0 == NULL)
978 return(EINVAL);
979
980 /* If caller didn't supply a packet, make one. */
981 if (*p == NULL) {
efda3bd0 982 *p = kmalloc(sizeof(ndis_packet), M_NDIS_PACKET, M_NOWAIT|M_ZERO);
60d6dac1
MD
983 if (*p == NULL)
984 return(ENOMEM);
985 }
986
987 priv = &(*p)->np_private;
988 priv->npp_totlen = m0->m_pkthdr.len;
989 priv->npp_packetooboffset = offsetof(ndis_packet, np_oob);
990 priv->npp_ndispktflags = NDIS_PACKET_ALLOCATED_BY_NDIS;
991
992 for (m = m0; m != NULL; m = m->m_next) {
993 if (m->m_len == 0)
994 continue;
efda3bd0 995 buf = kmalloc(sizeof(ndis_buffer), M_NDIS_BUFFER, M_NOWAIT|M_ZERO);
60d6dac1
MD
996 if (buf == NULL) {
997 ndis_free_packet(*p);
998 *p = NULL;
999 return(ENOMEM);
1000 }
1001
1002 MDL_INIT(buf, m->m_data, m->m_len);
1003 if (priv->npp_head == NULL)
1004 priv->npp_head = buf;
1005 else
1006 prev->nb_next = buf;
1007 prev = buf;
1008 }
1009
1010 priv->npp_tail = buf;
1011 priv->npp_totlen = m0->m_pkthdr.len;
1012
1013 return(0);
1014}
1015
1016int
2da2a8af 1017ndis_get_supported_oids(void *arg, ndis_oid **oids, int *oidcnt)
60d6dac1
MD
1018{
1019 int len, rval;
1020 ndis_oid *o;
1021
1022 if (arg == NULL || oids == NULL || oidcnt == NULL)
1023 return(EINVAL);
1024 len = 0;
1025 ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len);
1026
efda3bd0 1027 o = kmalloc(len, M_DEVBUF, M_WAITOK);
60d6dac1
MD
1028
1029 rval = ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, o, &len);
1030
1031 if (rval) {
efda3bd0 1032 kfree(o, M_DEVBUF);
60d6dac1
MD
1033 return(rval);
1034 }
1035
1036 *oids = o;
1037 *oidcnt = len / 4;
1038
1039 return(0);
1040}
1041
1042int
2da2a8af 1043ndis_set_info(void *arg, ndis_oid oid, void *buf, int *buflen)
60d6dac1
MD
1044{
1045 struct ndis_softc *sc;
1046 ndis_status rval;
1047 ndis_handle adapter;
385c8bc5 1048 ndis_setinfo_handler setfunc;
60d6dac1
MD
1049 uint32_t byteswritten = 0, bytesneeded = 0;
1050 int error;
1051 uint8_t irql;
60d6dac1
MD
1052
1053 sc = arg;
60d6dac1
MD
1054 setfunc = sc->ndis_chars.nmc_setinfo_func;
1055 adapter = sc->ndis_block.nmb_miniportadapterctx;
60d6dac1
MD
1056
1057 if (adapter == NULL || setfunc == NULL)
1058 return(ENXIO);
1059
1060 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1061 rval = setfunc(adapter, oid, buf, *buflen,
1062 &byteswritten, &bytesneeded);
1063 FASTCALL1(hal_lower_irql, irql);
1064
1065 if (rval == NDIS_STATUS_PENDING) {
1066 error = tsleep(&sc->ndis_block.nmb_wkupdpctimer,
1067 0, "ndisset", 5 * hz);
1068 rval = sc->ndis_block.nmb_setstat;
1069 }
1070
1071 if (byteswritten)
1072 *buflen = byteswritten;
1073 if (bytesneeded)
1074 *buflen = bytesneeded;
1075
1076 if (rval == NDIS_STATUS_INVALID_LENGTH)
1077 return(ENOSPC);
1078
1079 if (rval == NDIS_STATUS_INVALID_OID)
1080 return(EINVAL);
1081
1082 if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1083 rval == NDIS_STATUS_NOT_ACCEPTED)
1084 return(ENOTSUP);
1085
1086 if (rval != NDIS_STATUS_SUCCESS)
1087 return(ENODEV);
1088
1089 return(0);
1090}
1091
385c8bc5 1092typedef __stdcall void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status);
60d6dac1
MD
1093
1094int
2da2a8af 1095ndis_send_packets(void *arg, ndis_packet **packets, int cnt)
60d6dac1
MD
1096{
1097 struct ndis_softc *sc;
1098 ndis_handle adapter;
385c8bc5
MD
1099 ndis_sendmulti_handler sendfunc;
1100 ndis_senddone_func senddonefunc;
60d6dac1
MD
1101 int i;
1102 ndis_packet *p;
1103 uint8_t irql;
1104
1105 sc = arg;
1106 adapter = sc->ndis_block.nmb_miniportadapterctx;
1107 if (adapter == NULL)
1108 return(ENXIO);
1109 sendfunc = sc->ndis_chars.nmc_sendmulti_func;
1110 senddonefunc = sc->ndis_block.nmb_senddone_func;
1111 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1112 sendfunc(adapter, packets, cnt);
1113 FASTCALL1(hal_lower_irql, irql);
1114
1115 for (i = 0; i < cnt; i++) {
1116 p = packets[i];
1117 /*
1118 * Either the driver already handed the packet to
1119 * ndis_txeof() due to a failure, or it wants to keep
1120 * it and release it asynchronously later. Skip to the
1121 * next one.
1122 */
1123 if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING)
1124 continue;
1125 senddonefunc(&sc->ndis_block, p, p->np_oob.npo_status);
1126 }
1127
1128 return(0);
1129}
1130
1131int
2da2a8af 1132ndis_send_packet(void *arg, ndis_packet *packet)
60d6dac1
MD
1133{
1134 struct ndis_softc *sc;
1135 ndis_handle adapter;
1136 ndis_status status;
385c8bc5
MD
1137 ndis_sendsingle_handler sendfunc;
1138 ndis_senddone_func senddonefunc;
60d6dac1
MD
1139 uint8_t irql;
1140
1141 sc = arg;
1142 adapter = sc->ndis_block.nmb_miniportadapterctx;
1143 if (adapter == NULL)
1144 return(ENXIO);
1145 sendfunc = sc->ndis_chars.nmc_sendsingle_func;
1146 senddonefunc = sc->ndis_block.nmb_senddone_func;
1147
1148 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1149 status = sendfunc(adapter, packet, packet->np_private.npp_flags);
1150 FASTCALL1(hal_lower_irql, irql);
1151
1152 if (status == NDIS_STATUS_PENDING)
1153 return(0);
1154
1155 senddonefunc(&sc->ndis_block, packet, status);
1156
1157 return(0);
1158}
1159
1160int
2da2a8af 1161ndis_init_dma(void *arg)
60d6dac1
MD
1162{
1163 struct ndis_softc *sc;
1164 int i, error;
1165
1166 sc = arg;
1167
77652cad 1168 sc->ndis_tmaps = kmalloc(sizeof(bus_dmamap_t) * sc->ndis_maxpkts,
6147a8af 1169 M_DEVBUF, M_WAITOK|M_ZERO);
60d6dac1 1170
60d6dac1
MD
1171 for (i = 0; i < sc->ndis_maxpkts; i++) {
1172 error = bus_dmamap_create(sc->ndis_ttag, 0,
1173 &sc->ndis_tmaps[i]);
1174 if (error) {
efda3bd0 1175 kfree(sc->ndis_tmaps, M_DEVBUF);
60d6dac1
MD
1176 return(ENODEV);
1177 }
1178 }
1179
1180 return(0);
1181}
1182
1183int
2da2a8af 1184ndis_destroy_dma(void *arg)
60d6dac1
MD
1185{
1186 struct ndis_softc *sc;
1187 struct mbuf *m;
1188 ndis_packet *p = NULL;
1189 int i;
1190
1191 sc = arg;
1192
1193 for (i = 0; i < sc->ndis_maxpkts; i++) {
1194 if (sc->ndis_txarray[i] != NULL) {
1195 p = sc->ndis_txarray[i];
1196 m = (struct mbuf *)p->np_rsvd[1];
1197 if (m != NULL)
1198 m_freem(m);
1199 ndis_free_packet(sc->ndis_txarray[i]);
1200 }
1201 bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]);
1202 }
6147a8af 1203 if (sc->ndis_tmaps)
efda3bd0 1204 kfree(sc->ndis_tmaps, M_DEVBUF);
60d6dac1
MD
1205 bus_dma_tag_destroy(sc->ndis_ttag);
1206
1207 return(0);
1208}
1209
1210int
2da2a8af 1211ndis_reset_nic(void *arg)
60d6dac1
MD
1212{
1213 struct ndis_softc *sc;
1214 ndis_handle adapter;
385c8bc5 1215 ndis_reset_handler resetfunc;
60d6dac1 1216 uint8_t addressing_reset;
60d6dac1
MD
1217 int rval;
1218 uint8_t irql;
60d6dac1
MD
1219
1220 sc = arg;
60d6dac1
MD
1221 adapter = sc->ndis_block.nmb_miniportadapterctx;
1222 resetfunc = sc->ndis_chars.nmc_reset_func;
60d6dac1
MD
1223 if (adapter == NULL || resetfunc == NULL)
1224 return(EIO);
1225
1226 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1227 rval = resetfunc(&addressing_reset, adapter);
1228 FASTCALL1(hal_lower_irql, irql);
1229
1230 if (rval == NDIS_STATUS_PENDING) {
1231 tsleep(sc, 0, "ndisrst", 0);
1232 }
1233
1234 return(0);
1235}
1236
1237int
2da2a8af 1238ndis_halt_nic(void *arg)
60d6dac1
MD
1239{
1240 struct ndis_softc *sc;
1241 ndis_handle adapter;
385c8bc5 1242 ndis_halt_handler haltfunc;
60d6dac1
MD
1243
1244 sc = arg;
60d6dac1 1245
60d6dac1
MD
1246 adapter = sc->ndis_block.nmb_miniportadapterctx;
1247 if (adapter == NULL) {
60d6dac1
MD
1248 return(EIO);
1249 }
1250
1251 /*
1252 * The adapter context is only valid after the init
1253 * handler has been called, and is invalid once the
1254 * halt handler has been called.
1255 */
1256
1257 haltfunc = sc->ndis_chars.nmc_halt_func;
60d6dac1
MD
1258
1259 haltfunc(adapter);
1260
60d6dac1 1261 sc->ndis_block.nmb_miniportadapterctx = NULL;
60d6dac1
MD
1262
1263 return(0);
1264}
1265
1266int
2da2a8af 1267ndis_shutdown_nic(void *arg)
60d6dac1
MD
1268{
1269 struct ndis_softc *sc;
1270 ndis_handle adapter;
385c8bc5 1271 ndis_shutdown_handler shutdownfunc;
60d6dac1
MD
1272
1273 sc = arg;
60d6dac1
MD
1274 adapter = sc->ndis_block.nmb_miniportadapterctx;
1275 shutdownfunc = sc->ndis_chars.nmc_shutdown_handler;
60d6dac1
MD
1276 if (adapter == NULL || shutdownfunc == NULL)
1277 return(EIO);
1278
1279 if (sc->ndis_chars.nmc_rsvd0 == NULL)
1280 shutdownfunc(adapter);
1281 else
1282 shutdownfunc(sc->ndis_chars.nmc_rsvd0);
1283
1284 ndis_shrink_thrqueue(8);
1285 TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link);
1286
1287 return(0);
1288}
1289
1290int
2da2a8af 1291ndis_init_nic(void *arg)
60d6dac1
MD
1292{
1293 struct ndis_softc *sc;
1294 ndis_miniport_block *block;
385c8bc5 1295 ndis_init_handler initfunc;
60d6dac1
MD
1296 ndis_status status, openstatus = 0;
1297 ndis_medium mediumarray[NdisMediumMax];
1298 uint32_t chosenmedium, i;
60d6dac1
MD
1299
1300 if (arg == NULL)
1301 return(EINVAL);
1302
1303 sc = arg;
60d6dac1
MD
1304 block = &sc->ndis_block;
1305 initfunc = sc->ndis_chars.nmc_init_func;
60d6dac1
MD
1306
1307 TAILQ_INIT(&block->nmb_timerlist);
1308
1309 for (i = 0; i < NdisMediumMax; i++)
1310 mediumarray[i] = i;
1311
1312 status = initfunc(&openstatus, &chosenmedium,
1313 mediumarray, NdisMediumMax, block, block);
1314
1315 /*
1316 * If the init fails, blow away the other exported routines
1317 * we obtained from the driver so we can't call them later.
1318 * If the init failed, none of these will work.
1319 */
1320 if (status != NDIS_STATUS_SUCCESS) {
60d6dac1 1321 sc->ndis_block.nmb_miniportadapterctx = NULL;
60d6dac1
MD
1322 return(ENXIO);
1323 }
1324
1325 return(0);
1326}
1327
1328void
2da2a8af 1329ndis_enable_intr(void *arg)
60d6dac1
MD
1330{
1331 struct ndis_softc *sc;
1332 ndis_handle adapter;
385c8bc5 1333 ndis_enable_interrupts_handler intrenbfunc;
60d6dac1
MD
1334
1335 sc = arg;
1336 adapter = sc->ndis_block.nmb_miniportadapterctx;
1337 intrenbfunc = sc->ndis_chars.nmc_enable_interrupts_func;
1338 if (adapter == NULL || intrenbfunc == NULL)
1339 return;
1340 intrenbfunc(adapter);
1341
1342 return;
1343}
1344
1345void
2da2a8af 1346ndis_disable_intr(void *arg)
60d6dac1
MD
1347{
1348 struct ndis_softc *sc;
1349 ndis_handle adapter;
385c8bc5 1350 ndis_disable_interrupts_handler intrdisfunc;
60d6dac1
MD
1351
1352 sc = arg;
60d6dac1
MD
1353 adapter = sc->ndis_block.nmb_miniportadapterctx;
1354 intrdisfunc = sc->ndis_chars.nmc_disable_interrupts_func;
60d6dac1
MD
1355 if (adapter == NULL || intrdisfunc == NULL)
1356 return;
1357 intrdisfunc(adapter);
1358
1359 return;
1360}
1361
1362int
2da2a8af 1363ndis_isr(void *arg, int *ourintr, int *callhandler)
60d6dac1
MD
1364{
1365 struct ndis_softc *sc;
1366 ndis_handle adapter;
385c8bc5 1367 ndis_isr_handler isrfunc;
60d6dac1
MD
1368 uint8_t accepted, queue;
1369
1370 if (arg == NULL || ourintr == NULL || callhandler == NULL)
1371 return(EINVAL);
1372
1373 sc = arg;
1374 adapter = sc->ndis_block.nmb_miniportadapterctx;
1375 isrfunc = sc->ndis_chars.nmc_isr_func;
1376 if (adapter == NULL || isrfunc == NULL)
1377 return(ENXIO);
1378
1379 isrfunc(&accepted, &queue, adapter);
1380 *ourintr = accepted;
1381 *callhandler = queue;
1382
1383 return(0);
1384}
1385
1386int
2da2a8af 1387ndis_intrhand(void *arg)
60d6dac1
MD
1388{
1389 struct ndis_softc *sc;
1390 ndis_handle adapter;
385c8bc5 1391 ndis_interrupt_handler intrfunc;
60d6dac1
MD
1392
1393 if (arg == NULL)
1394 return(EINVAL);
1395
1396 sc = arg;
60d6dac1
MD
1397 adapter = sc->ndis_block.nmb_miniportadapterctx;
1398 intrfunc = sc->ndis_chars.nmc_interrupt_func;
60d6dac1
MD
1399 if (adapter == NULL || intrfunc == NULL)
1400 return(EINVAL);
1401
1402 intrfunc(adapter);
1403
1404 return(0);
1405}
1406
1407int
2da2a8af 1408ndis_get_info(void *arg, ndis_oid oid, void *buf, int *buflen)
60d6dac1
MD
1409{
1410 struct ndis_softc *sc;
1411 ndis_status rval;
1412 ndis_handle adapter;
1413 ndis_queryinfo_handler queryfunc;
1414 uint32_t byteswritten = 0, bytesneeded = 0;
1415 int error;
1416 uint8_t irql;
60d6dac1
MD
1417
1418 sc = arg;
60d6dac1
MD
1419 queryfunc = sc->ndis_chars.nmc_queryinfo_func;
1420 adapter = sc->ndis_block.nmb_miniportadapterctx;
60d6dac1
MD
1421
1422 if (adapter == NULL || queryfunc == NULL)
1423 return(ENXIO);
1424
1425 irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
1426 rval = queryfunc(adapter, oid, buf, *buflen,
1427 &byteswritten, &bytesneeded);
1428 FASTCALL1(hal_lower_irql, irql);
1429
1430 /* Wait for requests that block. */
1431
1432 if (rval == NDIS_STATUS_PENDING) {
1433 error = tsleep(&sc->ndis_block.nmb_wkupdpctimer,
1434 0, "ndisget", 5 * hz);
1435 rval = sc->ndis_block.nmb_getstat;
1436 }
1437
1438 if (byteswritten)
1439 *buflen = byteswritten;
1440 if (bytesneeded)
1441 *buflen = bytesneeded;
1442
1443 if (rval == NDIS_STATUS_INVALID_LENGTH ||
1444 rval == NDIS_STATUS_BUFFER_TOO_SHORT)
1445 return(ENOSPC);
1446
1447 if (rval == NDIS_STATUS_INVALID_OID)
1448 return(EINVAL);
1449
1450 if (rval == NDIS_STATUS_NOT_SUPPORTED ||
1451 rval == NDIS_STATUS_NOT_ACCEPTED)
1452 return(ENOTSUP);
1453
1454 if (rval != NDIS_STATUS_SUCCESS)
1455 return(ENODEV);
1456
1457 return(0);
1458}
1459
1460int
2da2a8af 1461ndis_unload_driver(void *arg)
60d6dac1
MD
1462{
1463 struct ndis_softc *sc;
1464
1465 sc = arg;
1466
efda3bd0 1467 kfree(sc->ndis_block.nmb_rlist, M_DEVBUF);
60d6dac1
MD
1468
1469 ndis_flush_sysctls(sc);
1470
1471 ndis_shrink_thrqueue(8);
1472 TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link);
1473
1474 return(0);
1475}
1476
1477#define NDIS_LOADED htonl(0x42534F44)
1478
1479int
2da2a8af 1480ndis_load_driver(vm_offset_t img, void *arg)
60d6dac1 1481{
385c8bc5 1482 driver_entry entry;
60d6dac1
MD
1483 image_optional_header opt_hdr;
1484 image_import_descriptor imp_desc;
1485 ndis_unicode_string dummystr;
1486 ndis_miniport_block *block;
1487 ndis_status status;
1488 int idx;
1489 uint32_t *ptr;
1490 struct ndis_softc *sc;
1491
1492 sc = arg;
1493
1494 /*
1495 * Only perform the relocation/linking phase once
1496 * since the binary image may be shared among multiple
1497 * device instances.
1498 */
1499
1500 ptr = (uint32_t *)(img + 8);
1501 if (*ptr != NDIS_LOADED) {
1502 /* Perform text relocation */
1503 if (pe_relocate(img))
1504 return(ENOEXEC);
1505
1506 /* Dynamically link the NDIS.SYS routines -- required. */
1507 if (pe_patch_imports(img, "NDIS", ndis_functbl))
1508 return(ENOEXEC);
1509
1510 /* Dynamically link the HAL.dll routines -- also required. */
1511 if (pe_patch_imports(img, "HAL", hal_functbl))
1512 return(ENOEXEC);
1513
1514 /* Dynamically link ntoskrnl.exe -- optional. */
1515 if (pe_get_import_descriptor(img,
1516 &imp_desc, "ntoskrnl") == 0) {
1517 if (pe_patch_imports(img,
1518 "ntoskrnl", ntoskrnl_functbl))
1519 return(ENOEXEC);
1520 }
1521 *ptr = NDIS_LOADED;
1522 }
1523
1524 /* Locate the driver entry point */
1525 pe_get_optional_header(img, &opt_hdr);
1526 entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
1527
1528 dummystr.nus_len = strlen(NDIS_DUMMY_PATH) * 2;
1529 dummystr.nus_maxlen = strlen(NDIS_DUMMY_PATH) * 2;
1530 dummystr.nus_buf = NULL;
1531 ndis_ascii_to_unicode(NDIS_DUMMY_PATH, &dummystr.nus_buf);
1532
1533 /*
1534 * Now that we have the miniport driver characteristics,
1535 * create an NDIS block and call the init handler.
1536 * This will cause the driver to try to probe for
1537 * a device.
1538 */
1539
1540 block = &sc->ndis_block;
1541
1542 ptr = (uint32_t *)block;
1543 for (idx = 0; idx < sizeof(ndis_miniport_block) / 4; idx++) {
1544 *ptr = idx | 0xdead0000;
1545 ptr++;
1546 }
1547
1548 block->nmb_signature = (void *)0xcafebabe;
1549 block->nmb_setdone_func = ndis_setdone_func;
1550 block->nmb_querydone_func = ndis_getdone_func;
1551 block->nmb_status_func = ndis_status_func;
1552 block->nmb_statusdone_func = ndis_statusdone_func;
1553 block->nmb_resetdone_func = ndis_resetdone_func;
1554 block->nmb_sendrsrc_func = ndis_sendrsrcavail_func;
1555
1556 block->nmb_ifp = &sc->arpcom.ac_if;
1557 block->nmb_dev = sc->ndis_dev;
1558 block->nmb_img = img;
1559 block->nmb_devobj.do_rsvd = block;
1560
1561 /*
1562 * Now call the DriverEntry() routine. This will cause
1563 * a callout to the NdisInitializeWrapper() and
1564 * NdisMRegisterMiniport() routines.
1565 */
1566 status = entry(&block->nmb_devobj, &dummystr);
1567
efda3bd0 1568 kfree (dummystr.nus_buf, M_DEVBUF);
60d6dac1
MD
1569
1570 if (status != NDIS_STATUS_SUCCESS)
1571 return(ENODEV);
1572
1573 ndis_enlarge_thrqueue(8);
1574
1575 TAILQ_INSERT_TAIL(&ndis_devhead, block, link);
1576
1577 return(0);
1578}
1579