diff options
author | wpaul <wpaul@FreeBSD.org> | 2004-03-25 18:31:52 +0000 |
---|---|---|
committer | wpaul <wpaul@FreeBSD.org> | 2004-03-25 18:31:52 +0000 |
commit | 2c5e07e637980819a6f0b56a6d24ffbe4b42e95d (patch) | |
tree | 066b1ba903a678d6424e697b989df260afebbb15 | |
parent | 657983b5690bac6169e6515f4bdffc2a12860174 (diff) | |
download | FreeBSD-src-2c5e07e637980819a6f0b56a6d24ffbe4b42e95d.zip FreeBSD-src-2c5e07e637980819a6f0b56a6d24ffbe4b42e95d.tar.gz |
- In subr_ndis.c:ndis_init_event(), initialize events as notification
objects rather than synchronization objects. When a sync object is
signaled, only the first thread waiting on it is woken up, and then
it's automatically reset to the not-signaled state. When a
notification object is signaled, all threads waiting on it will
be woken up, and it remains in the signaled state until someone
resets it manually. We want the latter behavior for NDIS events.
- In kern_ndis.c:ndis_convert_res(), we have to create a temporary
copy of the list returned by BUS_GET_RESOURCE_LIST(). When the PCI
bus code probes resources for a given device, it enters them into
a singly linked list, head first. The result is that traversing
this list gives you the resources in reverse order. This means when
we create the Windows resource list, it will be in reverse order too.
Unfortunately, this can hose drivers for devices with multiple I/O
ranges of the same type, like, say, two memory mapped I/O regions (one
for registers, one to map the NVRAM/bootrom/whatever). Some drivers
test the range size to figure out which region is which, but others
just assume that the resources will be listed in ascending order from
lowest numbered BAR to highest. Reversing the order means such drivers
will choose the wrong resource as their I/O register range.
Since we can't traverse the resource SLIST backwards, we have to
make a temporary copy of the list in the right order and then build
the Windows resource list from that. I suppose we could just fix
the PCI bus code to use a TAILQ instead, but then I'd have to track
down all the consumers of the BUS_GET_RESOURCE_LIST() and fix them
too.
-rw-r--r-- | sys/compat/ndis/kern_ndis.c | 41 | ||||
-rw-r--r-- | sys/compat/ndis/subr_ndis.c | 4 |
2 files changed, 41 insertions, 4 deletions
diff --git a/sys/compat/ndis/kern_ndis.c b/sys/compat/ndis/kern_ndis.c index f633a1c..b59ba87 100644 --- a/sys/compat/ndis/kern_ndis.c +++ b/sys/compat/ndis/kern_ndis.c @@ -771,7 +771,9 @@ ndis_convert_res(arg) ndis_miniport_block *block; device_t dev; struct resource_list *brl; - struct resource_list_entry *brle; + struct resource_list brl_rev; + struct resource_list_entry *brle, *n; + int error = 0; sc = arg; block = &sc->ndis_block; @@ -791,7 +793,34 @@ ndis_convert_res(arg) brl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); if (brl != NULL) { + + /* + * We have a small problem. Some PCI devices have + * multiple I/O ranges. Windows orders them starting + * from lowest numbered BAR to highest. We discover + * them in that order too, but insert them into a singly + * linked list head first, which means when time comes + * to traverse the list, we enumerate them in reverse + * order. This screws up some drivers which expect the + * BARs to be in ascending order so that they can choose + * the "first" one as their register space. Unfortunately, + * in order to fix this, we have to create our own + * temporary list with the entries in reverse order. + */ + SLIST_INIT(&brl_rev); SLIST_FOREACH(brle, brl, link) { + n = malloc(sizeof(struct resource_list_entry), + M_TEMP, M_NOWAIT); + if (n == NULL) { + error = ENOMEM; + goto bad; + } + bcopy((char *)brle, (char *)n, + sizeof(struct resource_list_entry)); + SLIST_INSERT_HEAD(&brl_rev, n, link); + } + + SLIST_FOREACH(brle, &brl_rev, link) { switch (brle->type) { case SYS_RES_IOPORT: prd->cprd_type = CmResourceTypePort; @@ -820,7 +849,15 @@ ndis_convert_res(arg) block->nmb_rlist = rl; - return(0); +bad: + + while (!SLIST_EMPTY(&brl_rev)) { + n = SLIST_FIRST(&brl_rev); + SLIST_REMOVE_HEAD(&brl_rev, link); + free (n, M_TEMP); + } + + return(error); } /* diff --git a/sys/compat/ndis/subr_ndis.c b/sys/compat/ndis/subr_ndis.c index 9fa87e9..ba6f285 100644 --- a/sys/compat/ndis/subr_ndis.c +++ b/sys/compat/ndis/subr_ndis.c @@ -1906,12 +1906,12 @@ ndis_init_event(event) ndis_event *event; { /* - * NDIS events are always synchronization + * NDIS events are always notification * events, and should be initialized to the * not signaled state. */ - ntoskrnl_init_event(&event->ne_event, EVENT_TYPE_SYNC, FALSE); + ntoskrnl_init_event(&event->ne_event, EVENT_TYPE_NOTIFY, FALSE); return; } |