summaryrefslogtreecommitdiffstats
path: root/sys/compat/ndis/ntoskrnl_var.h
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>2005-10-10 16:46:39 +0000
committerwpaul <wpaul@FreeBSD.org>2005-10-10 16:46:39 +0000
commitef07dbe57fe381dac96f0b1470d99bd3a2ccd3aa (patch)
tree899e14a42961670f9a34b64d8df4d8a3abe94542 /sys/compat/ndis/ntoskrnl_var.h
parent01a20da53d2f99b5654208b24ad8d6ebfe324da9 (diff)
downloadFreeBSD-src-ef07dbe57fe381dac96f0b1470d99bd3a2ccd3aa.zip
FreeBSD-src-ef07dbe57fe381dac96f0b1470d99bd3a2ccd3aa.tar.gz
This commit makes a big round of updates and fixes many, many things.
First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
Diffstat (limited to 'sys/compat/ndis/ntoskrnl_var.h')
-rw-r--r--sys/compat/ndis/ntoskrnl_var.h142
1 files changed, 105 insertions, 37 deletions
diff --git a/sys/compat/ndis/ntoskrnl_var.h b/sys/compat/ndis/ntoskrnl_var.h
index 622a387..7a1f3af 100644
--- a/sys/compat/ndis/ntoskrnl_var.h
+++ b/sys/compat/ndis/ntoskrnl_var.h
@@ -48,6 +48,14 @@ struct unicode_string {
typedef struct unicode_string unicode_string;
+struct ansi_string {
+ uint16_t as_len;
+ uint16_t as_maxlen;
+ char *as_buf;
+};
+
+typedef struct ansi_string ansi_string;
+
/*
* Windows memory descriptor list. In Windows, it's possible for
* buffers to be passed between user and kernel contexts without
@@ -197,43 +205,54 @@ struct list_entry {
typedef struct list_entry list_entry;
-#define INIT_LIST_HEAD(l) \
+#define InitializeListHead(l) \
(l)->nle_flink = (l)->nle_blink = (l)
-#define REMOVE_LIST_ENTRY(e) \
+#define IsListEmpty(h) \
+ ((h)->nle_flink == (h))
+
+#define RemoveEntryList(e) \
do { \
list_entry *b; \
list_entry *f; \
\
- f = e->nle_flink; \
- b = e->nle_blink; \
+ f = (e)->nle_flink; \
+ b = (e)->nle_blink; \
b->nle_flink = f; \
f->nle_blink = b; \
} while (0)
-#define REMOVE_LIST_HEAD(l) \
- do { \
- list_entry *f; \
- list_entry *e; \
- \
- e = l->nle_flink; \
- f = e->nle_flink; \
- l->nle_flink = f; \
- f->nle_blink = l; \
- } while (0)
+/* These two have to be inlined since they return things. */
-#define REMOVE_LIST_TAIL(l) \
- do { \
- list_entry *b; \
- list_entry *e; \
- \
- e = l->nle_blink; \
- b = e->nle_blink; \
- l->nle_blink = b; \
- b->nle_flink = l; \
- } while (0)
+static __inline__ list_entry *
+RemoveHeadList(list_entry *l)
+{
+ list_entry *f;
+ list_entry *e;
+
+ e = l->nle_flink;
+ f = e->nle_flink;
+ l->nle_flink = f;
+ f->nle_blink = l;
+
+ return (e);
+}
+
+static __inline__ list_entry *
+RemoveTailList(list_entry *l)
+{
+ list_entry *b;
+ list_entry *e;
+
+ e = l->nle_blink;
+ b = e->nle_blink;
+ l->nle_blink = b;
+ b->nle_flink = l;
-#define INSERT_LIST_TAIL(l, e) \
+ return (e);
+}
+
+#define InsertTailList(l, e) \
do { \
list_entry *b; \
\
@@ -244,7 +263,7 @@ typedef struct list_entry list_entry;
l->nle_blink = (e); \
} while (0)
-#define INSERT_LIST_HEAD(l, e) \
+#define InsertHeadList(l, e) \
do { \
list_entry *f; \
\
@@ -263,12 +282,24 @@ struct nt_dispatch_header {
uint8_t dh_abs;
uint8_t dh_size;
uint8_t dh_inserted;
- uint32_t dh_sigstate;
+ int32_t dh_sigstate;
list_entry dh_waitlisthead;
};
typedef struct nt_dispatch_header nt_dispatch_header;
+/* Dispatcher object types */
+
+#define DISP_TYPE_NOTIFICATION_EVENT 0 /* KEVENT */
+#define DISP_TYPE_SYNCHRONIZATION_EVENT 1 /* KEVENT */
+#define DISP_TYPE_MUTANT 2 /* KMUTANT/KMUTEX */
+#define DISP_TYPE_PROCESS 3 /* KPROCESS */
+#define DISP_TYPE_QUEUE 4 /* KQUEUE */
+#define DISP_TYPE_SEMAPHORE 5 /* KSEMAPHORE */
+#define DISP_TYPE_THREAD 6 /* KTHREAD */
+#define DISP_TYPE_NOTIFICATION_TIMER 8 /* KTIMER */
+#define DISP_TYPE_SYNCHRONIZATION_TIMER 9 /* KTIMER */
+
#define OTYPE_EVENT 0
#define OTYPE_MUTEX 1
#define OTYPE_THREAD 2
@@ -334,14 +365,14 @@ struct ktimer {
uint64_t k_duetime;
union {
list_entry k_timerlistentry;
- struct callout_handle k_handle;
+ struct callout *k_callout;
} u;
void *k_dpc;
uint32_t k_period;
};
#define k_timerlistentry u.k_timerlistentry
-#define k_handle u.k_handle
+#define k_callout u.k_callout
typedef struct ktimer ktimer;
@@ -389,18 +420,12 @@ typedef struct kdpc kdpc;
*/
struct kmutant {
nt_dispatch_header km_header;
- union {
- list_entry km_listentry;
- uint32_t km_acquirecnt;
- } u;
+ list_entry km_listentry;
void *km_ownerthread;
uint8_t km_abandoned;
uint8_t km_apcdisable;
};
-#define km_listentry u.km_listentry
-#define km_acquirecnt u.km_acquirecnt
-
typedef struct kmutant kmutant;
#define LOOKASIDE_DEPTH 256
@@ -485,18 +510,28 @@ struct wait_block {
void *wb_kthread;
nt_dispatch_header *wb_object;
struct wait_block *wb_next;
+#ifdef notdef
uint16_t wb_waitkey;
uint16_t wb_waittype;
+#endif
+ uint8_t wb_waitkey;
+ uint8_t wb_waittype;
+ uint8_t wb_awakened;
+ uint8_t wb_oldpri;
};
typedef struct wait_block wait_block;
+#define wb_ext wb_kthread
+
#define THREAD_WAIT_OBJECTS 3
#define MAX_WAIT_OBJECTS 64
#define WAITTYPE_ALL 0
#define WAITTYPE_ANY 1
+#define WAITKEY_VALID 0x8000
+
struct thread_context {
void *tc_thrctx;
void *tc_thrfunc;
@@ -533,6 +568,23 @@ struct custom_extension {
typedef struct custom_extension custom_extension;
/*
+ * The KINTERRUPT structure in Windows is opaque to drivers.
+ * We define our own custom version with things we need.
+ */
+
+struct kinterrupt {
+ device_t ki_dev;
+ void *ki_cookie;
+ struct resource *ki_irq;
+ kspin_lock ki_lock_priv;
+ kspin_lock *ki_lock;
+ void *ki_svcfunc;
+ void *ki_svcctx;
+};
+
+typedef struct kinterrupt kinterrupt;
+
+/*
* In Windows, there are Physical Device Objects (PDOs) and
* Functional Device Objects (FDOs). Physical Device Objects are
* created and maintained by bus drivers. For example, the PCI
@@ -1199,7 +1251,7 @@ typedef struct work_queue_item work_queue_item;
do { \
(w)->wqi_func = (func); \
(w)->wqi_ctx = (ctx); \
- INIT_LIST_HEAD(&((w)->wqi_entry)); \
+ InitializeListHead(&((w)->wqi_entry)); \
} while (0); \
@@ -1251,6 +1303,16 @@ extern void ctxsw_wtou(void);
extern int ntoskrnl_libinit(void);
extern int ntoskrnl_libfini(void);
+
+extern uint32_t RtlUnicodeStringToAnsiString(ansi_string *,
+ unicode_string *, uint8_t);
+extern uint32_t RtlAnsiStringToUnicodeString(unicode_string *,
+ ansi_string *, uint8_t);
+extern void RtlInitAnsiString(ansi_string *, char *);
+extern void RtlInitUnicodeString(unicode_string *,
+ uint16_t *);
+extern void RtlFreeUnicodeString(unicode_string *);
+extern void RtlFreeAnsiString(ansi_string *);
extern void KeInitializeDpc(kdpc *, void *, void *);
extern uint8_t KeInsertQueueDpc(kdpc *, void *, void *);
extern uint8_t KeRemoveQueueDpc(kdpc *);
@@ -1280,10 +1342,16 @@ extern void KeAcquireSpinLockAtDpcLevel(kspin_lock *);
extern void KeReleaseSpinLockFromDpcLevel(kspin_lock *);
#endif
extern void KeInitializeSpinLock(kspin_lock *);
+extern uint8_t KeSynchronizeExecution(kinterrupt *, void *, void *);
extern uintptr_t InterlockedExchange(volatile uint32_t *,
uintptr_t);
extern void *ExAllocatePoolWithTag(uint32_t, size_t, uint32_t);
extern void ExFreePool(void *);
+extern uint32_t IoConnectInterrupt(kinterrupt **, void *, void *,
+ kspin_lock *, uint32_t, uint8_t, uint8_t, uint8_t, uint8_t,
+ uint32_t, uint8_t);
+extern void MmBuildMdlForNonPagedPool(mdl *);
+extern void IoDisconnectInterrupt(kinterrupt *);
extern uint32_t IoAllocateDriverObjectExtension(driver_object *,
void *, uint32_t, void **);
extern void *IoGetDriverObjectExtension(driver_object *, void *);
OpenPOWER on IntegriCloud