[libbus,driver,dummy] Don't thread the main bus
authorShao Miller <Shao.Miller@yrdsb.edu.on.ca>
Fri, 7 Jan 2011 06:50:16 +0000 (01:50 -0500)
committerShao Miller <Shao.Miller@yrdsb.edu.on.ca>
Fri, 7 Jan 2011 14:33:33 +0000 (09:33 -0500)
*sigh* There was a major problem with the strategy of
enqueueing all IRPs bound for the bus; one cannot return
STATUS_PENDING for just any IRP.

The symptom that led to this realization was: while
performing a driver update for the WinVBlock bus in
Device Manager, an IRP_MN_QUERY_ID was being sent
immediately after an IRP_MN_REMOVE_DEVICE.  This doesn't
appear to make sense until realizing that the PnP
manager most likely took the STATUS_PENDING for the
removal the wrong way; it assumed that the bus FDO would
still be attached for its next IRP, which was actually
intended to be bound for the PDO.

So we don't enqueue any IRPs for the bus any more.  One
of the original reasons for doing so was so that dummy
PDO child devices could be created by the thread.  So
this commit allows for an IOCTL to produce a dummy PDO,
which was on the to-do list, anyway.  The fun of that
is the potential to add arbitrary PDOs from user-land;
arbitrary in the sense of their reported IDs.  It will
be interesting to test the installation of non-existent
hardware and find out what IRPs their function drivers
send down.

The dummy ID generation macro(s) have been changed for
greater automation and aesthetic.  It creates an object
large enough to hold the dummy ID "header" as well as
the WCHAR string data, and initializes the object.  One
can qualify the object as 'const', even.  Quite nice.

Threading was decoupled from the bus library entirely.
One of the other reasons for their association was in
order to keep the list of children race-free.  So now
we have WvlBusLock() and WvlBusUnlock() functions to
assist with race-free iteration.

src/aoe/bus.c
src/aoe/driver.c
src/include/bus.h
src/include/driver.h
src/include/dummy.h
src/winvblock/bus.c
src/winvblock/driver.c
src/winvblock/dummy.c
src/winvblock/libbus/libbus.c
src/winvblock/libbus/pnp.c

index 99eef13..0c4253d 100644 (file)
@@ -30,6 +30,7 @@
 #include "winvblock.h"
 #include "wv_stdlib.h"
 #include "irp.h"
+#include "driver.h"
 #include "bus.h"
 #include "device.h"
 #include "dummy.h"
@@ -70,18 +71,6 @@ static NTSTATUS STDCALL AoeBusDevCtlDetach_(IN PIRP irp) {
     UINT32 unit_num;
     WVL_SP_BUS_NODE walker;
 
-    if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
-        NTSTATUS status;
-
-        /* Enqueue the IRP. */
-        status = WvlBusEnqueueIrp(&AoeBusMain, irp);
-        if (status != STATUS_PENDING)
-          /* Problem. */
-          return WvlIrpComplete(irp, 0, status);
-        /* Ok. */
-        return status;
-      }
-    /* If we get here, we should be called by WvlBusProcessWorkItems() */
     unit_num = *((PUINT32) irp->AssociatedIrp.SystemBuffer);
     DBG("Request to detach unit: %d\n", unit_num);
 
@@ -137,12 +126,16 @@ NTSTATUS STDCALL AoeBusDevCtl(
   }
 
 /* Generate dummy IDs for the AoE bus PDO. */
-#define AOE_M_BUS_IDS(X_, Y_)             \
-  X_(Y_, Dev,      WVL_M_WLIT L"\\AoE"  ) \
-  X_(Y_, Instance, L"0"                 ) \
-  X_(Y_, Hardware, WVL_M_WLIT L"\\AoE\0") \
-  X_(Y_, Compat,   WVL_M_WLIT L"\\AoE\0")
-WV_M_DUMMY_ID_GEN(AoeBusDummyIds, AOE_M_BUS_IDS);
+WV_M_DUMMY_ID_GEN(
+    static const,
+    AoeBusDummyIds_,
+    WVL_M_WLIT L"\\AoE",
+    L"0",
+    WVL_M_WLIT L"\\AoE\0",
+    WVL_M_WLIT L"\\AoE\0",
+    FILE_DEVICE_CONTROLLER,
+    FILE_DEVICE_SECURE_OPEN
+  );
 
 /* Destroy the AoE bus. */
 VOID AoeBusFree(void) {
@@ -295,6 +288,52 @@ NTSTATUS STDCALL AoeBusAttachFdo(
     return status;
   }
 
+/**
+ * Request a PDO for the AoE bus from the WinVBlock bus.
+ *
+ * @ret NTSTATUS        The status of the operation.
+ */
+static NTSTATUS AoeBusCreatePdo_(void) {
+    PDEVICE_OBJECT wv_fdo;
+    KEVENT signal;
+    PIRP irp;
+    IO_STATUS_BLOCK io_status = {0};
+    NTSTATUS status;
+
+    wv_fdo = WvBusFdo();
+    if (!wv_fdo)
+      return STATUS_NO_SUCH_DEVICE;
+
+    /* Prepare the request. */
+    KeInitializeEvent(&signal, SynchronizationEvent, FALSE);
+    irp = IoBuildDeviceIoControlRequest(
+        IOCTL_WV_DUMMY,
+        WvBusFdo(),
+        (PVOID) &AoeBusDummyIds_,
+        sizeof AoeBusDummyIds_,
+        NULL,
+        0,
+        FALSE,
+        &signal,
+        &io_status
+      );
+    if (!irp)
+      return STATUS_INSUFFICIENT_RESOURCES;
+
+    status = IoCallDriver(wv_fdo, irp);
+    if (status == STATUS_PENDING) {
+        KeWaitForSingleObject(
+            &signal,
+            Executive,
+            KernelMode,
+            FALSE,
+            NULL
+          );
+        status = io_status.Status;
+      }
+    return status;
+  }
+
 /**
  * Create the AoE bus PDO and FDO.
  *
@@ -309,14 +348,9 @@ NTSTATUS AoeBusCreate(IN PDRIVER_OBJECT driver_obj) {
         return STATUS_UNSUCCESSFUL;
       }
     /* Create the PDO for the sub-bus on the WinVBlock bus. */
-    status = WvDummyAdd(
-        &AoeBusDummyIds,
-        FILE_DEVICE_CONTROLLER,
-        FILE_DEVICE_SECURE_OPEN,
-        &AoeBusPdo
-      );
+    status = AoeBusCreatePdo_();
     if (!NT_SUCCESS(status)) {
-        DBG("Couldn't add AoE bus to WinVBlock bus!\n");
+        DBG("Couldn't create AoE bus PDO!\n");
         goto err_pdo;
       }
     /* Initialize the bus. */
@@ -362,7 +396,7 @@ NTSTATUS AoeBusCreate(IN PDRIVER_OBJECT driver_obj) {
     IoDeleteDevice(AoeBusMain.Fdo);
     err_fdo:
 
-    WvDummyRemove(AoeBusPdo);
+    /* TODO: Remove dummy PDO. */
     AoeBusPdo = NULL;
     err_pdo:
 
index 16bb73f..c7cf1d8 100644 (file)
@@ -1273,7 +1273,6 @@ static VOID STDCALL AoeThread_(IN PVOID StartContext) {
         KeResetEvent(&AoeSignal_);
         if (AoeStop_) {
             DBG("Stopping...\n");
-            WvlBusCancelWorkItems(&AoeBusMain);
             /* Detach from any lower DEVICE_OBJECT */
             if (AoeBusMain.LowerDeviceObject)
               IoDetachDevice(AoeBusMain.LowerDeviceObject);
@@ -1283,8 +1282,6 @@ static VOID STDCALL AoeThread_(IN PVOID StartContext) {
             AoeBusMain.Fdo = NULL;
             PsTerminateSystemThread(STATUS_SUCCESS);
           }
-        WvlBusProcessWorkItems(&AoeBusMain);
-
         KeQuerySystemTime(&CurrentTime);
         /* TODO: Make the below value a #defined constant. */
         if (CurrentTime.QuadPart > (ReportTime.QuadPart + 10000000LL)) {
@@ -1624,18 +1621,6 @@ NTSTATUS STDCALL AoeBusDevCtlShow(IN PIRP irp) {
     wv_size_t size;
     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
 
-    if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
-        NTSTATUS status;
-
-        /* Enqueue the IRP. */
-        status = WvlBusEnqueueIrp(&AoeBusMain, irp);
-        if (status != STATUS_PENDING)
-          /* Problem. */
-          return WvlIrpComplete(irp, 0, status);
-        /* Ok. */
-        return status;
-      }
-    /* If we get here, we should be called by WvlBusProcessWorkItems() */
     DBG("Got IOCTL_AOE_SHOW...\n");
 
     count = WvlBusGetNodeCount(&AoeBusMain);
@@ -1954,7 +1939,7 @@ static NTSTATUS AoeIrpPnp(
     if (dev_obj == AoeBusMain.Fdo) {
         NTSTATUS status;
 
-        status = WvlBusPnpIrp(&AoeBusMain, irp, code);
+        status = WvlBusPnp(&AoeBusMain, irp);
         if (NT_SUCCESS(status) && (code == IRP_MN_REMOVE_DEVICE))
           AoeBusPdo = NULL;
         return status;
index 1a459a3..63a7bc2 100644 (file)
 /* Forward declaration. */
 struct WVL_BUS_T;
 
-/**
- * A bus thread routine.
- *
- * @v bus       The bus to be used in the thread routine.
- *
- * If you implement your own bus thread routine, you should call
- * WvlBusProcessWorkItems() within its loop.
- */
-typedef VOID STDCALL WVL_F_BUS_THREAD(IN struct WVL_BUS_T *);
-typedef WVL_F_BUS_THREAD * WVL_FP_BUS_THREAD;
-
 /**
  * A bus PnP routine.
  *
@@ -68,19 +57,14 @@ typedef struct WVL_BUS_T {
     PDEVICE_OBJECT LowerDeviceObject;
     PDEVICE_OBJECT Pdo;
     PDEVICE_OBJECT Fdo;
-    UINT32 Children;
-    WVL_FP_BUS_THREAD Thread;
-    KEVENT ThreadSignal;
-    BOOLEAN Stop;
     WVL_E_BUS_STATE OldState;
     WVL_E_BUS_STATE State;
     WVL_FP_BUS_PNP QueryDevText;
     struct {
         LIST_ENTRY Nodes;
+        KSPIN_LOCK NodeLock;
+        KIRQL NodeLockIrql;
         USHORT NodeCount;
-        LIST_ENTRY WorkItems;
-        KSPIN_LOCK WorkItemsLock;
-        PETHREAD Thread;
       } BusPrivate_;
   } WVL_S_BUS_T, * WVL_SP_BUS_T;
 
@@ -96,32 +80,11 @@ typedef struct WVL_BUS_NODE {
     BOOLEAN Linked;
   } WVL_S_BUS_NODE, * WVL_SP_BUS_NODE;
 
-/**
- * A custom work-item function.
- *
- * @v context           Function-specific data.
- *
- * If a driver needs to enqueue a work item which should execute in the
- * context of the bus' controlling thread (this is the thread which calls
- * WvlBusProcessWorkItems()), then this is the function prototype to be
- * used.
- */
-typedef VOID STDCALL WVL_F_BUS_WORK_ITEM(PVOID);
-typedef WVL_F_BUS_WORK_ITEM * WVL_FP_BUS_WORK_ITEM;
-
-typedef struct WVL_BUS_CUSTOM_WORK_ITEM {
-    WVL_FP_BUS_WORK_ITEM Func;
-    PVOID Context;
-  } WVL_S_BUS_CUSTOM_WORK_ITEM, * WVL_SP_BUS_CUSTOM_WORK_ITEM;
-
 /* Exports. */
 extern WVL_M_LIB VOID WvlBusInit(WVL_SP_BUS_T);
-extern WVL_M_LIB VOID WvlBusProcessWorkItems(WVL_SP_BUS_T);
-extern WVL_M_LIB VOID WvlBusCancelWorkItems(WVL_SP_BUS_T);
-extern WVL_M_LIB NTSTATUS WvlBusStartThread(
-    IN WVL_SP_BUS_T,
-    OUT PETHREAD *
-  );
+extern WVL_M_LIB VOID STDCALL WvlBusClear(IN WVL_SP_BUS_T);
+extern WVL_M_LIB VOID WvlBusLock(IN WVL_SP_BUS_T);
+extern WVL_M_LIB VOID WvlBusUnlock(IN WVL_SP_BUS_T);
 extern WVL_M_LIB BOOLEAN STDCALL WvlBusInitNode(
     OUT WVL_SP_BUS_NODE,
     IN PDEVICE_OBJECT
@@ -131,25 +94,6 @@ extern WVL_M_LIB NTSTATUS STDCALL WvlBusAddNode(
     WVL_SP_BUS_NODE
   );
 extern WVL_M_LIB NTSTATUS STDCALL WvlBusRemoveNode(WVL_SP_BUS_NODE);
-extern WVL_M_LIB NTSTATUS STDCALL WvlBusEnqueueIrp(WVL_SP_BUS_T, PIRP);
-extern WVL_M_LIB NTSTATUS STDCALL WvlBusEnqueueCustomWorkItem(
-    WVL_SP_BUS_T,
-    WVL_SP_BUS_CUSTOM_WORK_ITEM
-  );
-extern WVL_M_LIB NTSTATUS STDCALL WvlBusSysCtl(
-    IN WVL_SP_BUS_T,
-    IN PIRP
-  );
-extern WVL_M_LIB NTSTATUS STDCALL WvlBusPower(
-    IN WVL_SP_BUS_T,
-    IN PIRP
-  );
-/* IRP_MJ_PNP dispatcher in bus/pnp.c */
-extern WVL_M_LIB NTSTATUS STDCALL WvlBusPnpIrp(
-    IN WVL_SP_BUS_T,
-    IN PIRP,
-    IN UCHAR
-  );
 extern WVL_M_LIB UINT32 STDCALL WvlBusGetNodeNum(
     IN WVL_SP_BUS_NODE
   );
@@ -163,7 +107,19 @@ extern WVL_M_LIB PDEVICE_OBJECT STDCALL WvlBusGetNodePdo(
 extern WVL_M_LIB UINT32 STDCALL WvlBusGetNodeCount(
     WVL_SP_BUS_T
   );
-extern WVL_M_LIB BOOLEAN STDCALL WvlBusRegisterOwnerThread(IN WVL_SP_BUS_T);
-extern WVL_M_LIB BOOLEAN STDCALL WvlBusNotOwned(IN WVL_SP_BUS_T);
+/* IRP-related. */
+extern WVL_M_LIB NTSTATUS STDCALL WvlBusSysCtl(
+    IN WVL_SP_BUS_T,
+    IN PIRP
+  );
+extern WVL_M_LIB NTSTATUS STDCALL WvlBusPower(
+    IN WVL_SP_BUS_T,
+    IN PIRP
+  );
+/* IRP_MJ_PNP dispatcher in libbus/pnp.c */
+extern WVL_M_LIB NTSTATUS STDCALL WvlBusPnp(
+    IN WVL_SP_BUS_T,
+    IN PIRP
+  );
 
 #endif  /* WVL_M_BUS_H_ */
index 9b41c6d..6027a0d 100644 (file)
@@ -51,6 +51,8 @@ extern NTSTATUS STDCALL WvDriverGetDevCapabilities(
     IN PDEVICE_OBJECT,
     IN PDEVICE_CAPABILITIES
   );
+/* From bus.c */
+extern WVL_M_LIB PDEVICE_OBJECT WvBusFdo(void);
 
 /**
  * Miscellaneous: Grouped memory allocation functions.
index 61a2417..aee6a0d 100644 (file)
  * Dummy device specifics.
  */
 
+#  define IOCTL_WV_DUMMY    \
+CTL_CODE(                   \
+    FILE_DEVICE_CONTROLLER, \
+    0x806,                  \
+    METHOD_IN_DIRECT,       \
+    FILE_WRITE_DATA         \
+  )
+
 /* PnP IDs for a dummy device. */
 typedef struct WV_DUMMY_IDS {
     UINT32 DevOffset;
@@ -36,72 +44,93 @@ typedef struct WV_DUMMY_IDS {
     UINT32 CompatOffset;
     UINT32 CompatLen;
     UINT32 Len;
-    const WCHAR * Ids;
-    WCHAR Text[1];
+    UINT32 Offset;
+    DEVICE_TYPE DevType;
+    ULONG DevCharacteristics;
   } WV_S_DUMMY_IDS, * WV_SP_DUMMY_IDS;
 
 /* Macro support for dummy ID generation. */
-#define WV_M_DUMMY_IDS_X_ENUM(prefix_, name_, literal_)           \
+#define WV_M_DUMMY_IDS_ENUM(prefix_, name_, literal_)             \
   prefix_ ## name_ ## Offset_,                                    \
   prefix_ ## name_ ## Len_ = sizeof (literal_) / sizeof (WCHAR),  \
   prefix_ ## name_ ## End_ =                                      \
     prefix_ ## name_ ## Offset_ + prefix_ ## name_ ## Len_ - 1,
 
-#define WV_M_DUMMY_IDS_X_LITERALS(prefix_, name_, literal_) \
-  literal_ L"\0"
+#define WV_M_DUMMY_IDS_LITERALS(dev_id_, inst_id_, hard_id_, compat_id_) \
+  dev_id_ L"\0" inst_id_ L"\0" hard_id_ L"\0" compat_id_
 
-#define WV_M_DUMMY_IDS_X_FILL(prefix_, name_, literal_) \
-  prefix_ ## name_ ## Offset_,                          \
+#define WV_M_DUMMY_IDS_FILL(prefix_, name_) \
+  prefix_ ## name_ ## Offset_,              \
   prefix_ ## name_ ## Len_,
 
 /**
  * Generate a static const WV_S_DUMMY_IDS object.
  *
- * @v DummyIds          The name of the desired object.  Also used as prefix.
- * @v XMacro            The x-macro with the ID text.
+ * @v Qualifiers        Such as 'static const', perhaps.
+ * @v Name              The name of the desired object.  Also used as prefix.
+ * @v DevId             The device ID.
+ * @v InstanceId        The instance ID.
+ * @v HardwareId        The hardware ID(s).
+ * @v CompatId          Compatible ID(s).
+ * @v DevType           The device type for the dummy PDO.
+ * @v DevCharacts       The device characteristics for the dummy PDO.
  *
  * This macro will produce the following:
  *   enum values:
- *     [DummyIds]DevOffset_
- *     [DummyIds]DevLen_
- *     [DummyIds]DevEnd_
- *     [DummyIds]InstanceOffset_
- *     [DummyIds]InstanceLen_
- *     [DummyIds]InstanceEnd_
- *     [DummyIds]HardwareOffset_
- *     [DummyIds]HardwareLen_
- *     [DummyIds]HardwareEnd_
- *     [DummyIds]CompatOffset_
- *     [DummyIds]CompatLen_
- *     [DummyIds]CompatEnd_
- *     [DummyIds]Len_
- *   WCHAR[]:
- *     [DummyIds]String_
- *   static const WV_S_DUMMY_IDS:
- *     [DummyIds]
+ *     [Name]DevOffset_
+ *     [Name]DevLen_
+ *     [Name]DevEnd_
+ *     [Name]InstanceOffset_
+ *     [Name]InstanceLen_
+ *     [Name]InstanceEnd_
+ *     [Name]HardwareOffset_
+ *     [Name]HardwareLen_
+ *     [Name]HardwareEnd_
+ *     [Name]CompatOffset_
+ *     [Name]CompatLen_
+ *     [Name]CompatEnd_
+ *     [Name]Len_
+ *   [Qualifiers] WV_S_DUMMY_IDS:
+ *     [Name]
  */
-#define WV_M_DUMMY_ID_GEN(DummyIds, XMacro)     \
-                                                \
-enum {                                          \
-    XMacro(WV_M_DUMMY_IDS_X_ENUM, DummyIds)     \
-    DummyIds ## Len_                            \
-  };                                            \
-                                                \
-static const WCHAR DummyIds ## String_[] =      \
-  XMacro(WV_M_DUMMY_IDS_X_LITERALS, DummyIds);  \
-                                                \
-static const WV_S_DUMMY_IDS DummyIds = {        \
-    XMacro(WV_M_DUMMY_IDS_X_FILL, DummyIds)     \
-    DummyIds ## Len_,                           \
-    DummyIds ## String_                         \
+#define WV_M_DUMMY_ID_GEN(                                            \
+    Qualifiers,                                                       \
+    Name,                                                             \
+    DevId,                                                            \
+    InstanceId,                                                       \
+    HardwareId,                                                       \
+    CompatId,                                                         \
+    DevType,                                                          \
+    DevCharacts                                                       \
+  )                                                                   \
+                                                                      \
+enum {                                                                \
+    WV_M_DUMMY_IDS_ENUM(Name, Dev, DevId)                             \
+    WV_M_DUMMY_IDS_ENUM(Name, Instance, InstanceId)                   \
+    WV_M_DUMMY_IDS_ENUM(Name, Hardware, HardwareId)                   \
+    WV_M_DUMMY_IDS_ENUM(Name, Compat, CompatId)                       \
+    Name ## Len_                                                      \
+  };                                                                  \
+                                                                      \
+struct Name ## Struct_ {                                              \
+    WV_S_DUMMY_IDS DummyIds;                                          \
+    WCHAR Ids[Name ## Len_];                                          \
+  };                                                                  \
+Qualifiers struct Name ## Struct_ Name = {                            \
+    {                                                                 \
+        WV_M_DUMMY_IDS_FILL(Name, Dev)                                \
+        WV_M_DUMMY_IDS_FILL(Name, Instance)                           \
+        WV_M_DUMMY_IDS_FILL(Name, Hardware)                           \
+        WV_M_DUMMY_IDS_FILL(Name, Compat)                             \
+        sizeof (struct Name ## Struct_),                              \
+        FIELD_OFFSET(struct Name ## Struct_, Ids),                    \
+        DevType,                                                      \
+        DevCharacts,                                                  \
+      },                                                              \
+    WV_M_DUMMY_IDS_LITERALS(DevId, InstanceId, HardwareId, CompatId), \
   }
 
-extern WVL_M_LIB NTSTATUS STDCALL WvDummyAdd(
-    IN const WV_S_DUMMY_IDS *,
-    IN DEVICE_TYPE,
-    IN ULONG,
-    OUT PDEVICE_OBJECT *
-  );
 extern WVL_M_LIB NTSTATUS STDCALL WvDummyRemove(IN PDEVICE_OBJECT);
+extern NTSTATUS STDCALL WvDummyIoctl(IN PIRP);
 
 #endif /* WV_M_DUMMY_H_ */
index c36d51c..f237e90 100644 (file)
@@ -43,7 +43,7 @@
 #include "mount.h"
 #include "probe.h"
 #include "filedisk.h"
-#include "ramdisk.h"
+#include "dummy.h"
 #include "debug.h"
 
 /* Names for the main bus. */
@@ -62,162 +62,65 @@ UNICODE_STRING WvBusDosname = {
     WV_M_BUS_DOSNAME
   };
 BOOLEAN WvSymlinkDone = FALSE;
-KEVENT WvBusStartedSignal_;
+KSPIN_LOCK WvBusPdoLock;
 /* The main bus. */
 WVL_S_BUS_T WvBus = {0};
 WV_S_DEV_T WvBusDev = {0};
 
 /* Forward declarations. */
-NTSTATUS STDCALL WvBusDevCtl(
-    IN PIRP,
-    IN ULONG POINTER_ALIGNMENT
-  );
+NTSTATUS STDCALL WvBusDevCtl(IN PIRP, IN ULONG POINTER_ALIGNMENT);
 WVL_F_BUS_PNP WvBusPnpQueryDevText;
 
-static WVL_S_THREAD WvBusThread_ = {0};
-static WVL_F_THREAD_ITEM WvBusThread;
-static VOID STDCALL WvBusThread(IN OUT WVL_SP_THREAD_ITEM item) {
-    LARGE_INTEGER timeout;
-    PVOID signals[] = {&WvBusThread_.Signal, &WvBus.ThreadSignal};
-    WVL_SP_THREAD_ITEM work_item;
-
-    if (!item) {
-        DBG("Bad call!\n");
-        return;
-      }
-
-    /* Wake up at most every 30 seconds. */
-    timeout.QuadPart = -300000000LL;
-    WvBusThread_.State = WvlThreadStateStarted;
-    /* Notify WvBusEstablish(). */
-    KeSetEvent(&WvBusStartedSignal_, 0, FALSE);
-
-    do {
-        WvlBusProcessWorkItems(&WvBus);
-        while (work_item = WvlThreadGetItem(&WvBusThread_))
-          /* Launch the item. */
-          work_item->Func(work_item);
-        /* Only WvBusCleanup() should be used to stop us. */
-        if (WvBusThread_.State == WvlThreadStateStopping) {
-            WvBusThread_.State = WvlThreadStateStopped;
-            break;
-          }
-        /* Check for detach. */
-        if (WvBus.Stop && WvBus.Pdo) {
-            /* Detach from any lower DEVICE_OBJECT */
-            DBG("Detaching from PDO %p.\n", WvBus.Pdo);
-            if (WvBus.LowerDeviceObject)
-              IoDetachDevice(WvBus.LowerDeviceObject);
-            /* Disassociate. */
-            WvBus.LowerDeviceObject = NULL;
-            WvBus.Pdo = NULL;
-          }
-        /* Wait for the work signal or the timeout. */
-        KeWaitForMultipleObjects(
-            sizeof signals / sizeof *signals,
-            signals,
-            WaitAny,
-            Executive,
-            KernelMode,
-            FALSE,
-            &timeout,
-            NULL
-          );
-        /* Reset the work signals. */
-        KeResetEvent(&WvBusThread_.Signal);
-        KeResetEvent(&WvBus.ThreadSignal);
-      } while (
-        (WvBusThread_.State == WvlThreadStateStarted) ||
-        (WvBusThread_.State == WvlThreadStateStopping)
-      );
-    WvlBusCancelWorkItems(&WvBus);
-    return;
-  }
-
-typedef struct WV_BUS_ATTACH_ {
-    WVL_S_THREAD_ITEM item;
-    PDEVICE_OBJECT pdo;
-    KEVENT signal;
-    NTSTATUS status;
-  } WV_S_BUS_ATTACH_, * WV_SP_BUS_ATTACH_;
-
-/* Attempt to attach the bus to a PDO, with bus thread context.  Internal. */
-static WVL_F_THREAD_ITEM WvBusAttach_;
-static VOID STDCALL WvBusAttach_(IN OUT WVL_SP_THREAD_ITEM item) {
-    WV_SP_BUS_ATTACH_ bus_attach;
+/**
+ * Attempt to attach the bus to a PDO.
+ *
+ * @v pdo               The PDO to attach to.
+ * @ret NTSTATUS        The status of the operation.
+ *
+ * This function will return a failure status if the bus is already attached.
+ */
+NTSTATUS STDCALL WvBusAttach(PDEVICE_OBJECT Pdo) {
+    KIRQL irql;
     NTSTATUS status;
     PDEVICE_OBJECT lower;
 
     /* Do we alreay have our main bus? */
+    KeAcquireSpinLock(&WvBusPdoLock, &irql);
     if (WvBus.Pdo) {
+        KeReleaseSpinLock(&WvBusPdoLock, irql);
         DBG("Already have the main bus.  Refusing.\n");
         status = STATUS_NOT_SUPPORTED;
         goto err_already_established;
       }
-    WvBus.Stop = FALSE;
-    bus_attach = CONTAINING_RECORD(
-        item,
-        WV_S_BUS_ATTACH_,
-        item
-      );
+    WvBus.Pdo = Pdo;
+    KeReleaseSpinLock(&WvBusPdoLock, irql);
+    WvBus.State = WvlBusStateStarted;
     /* Attach the FDO to the PDO. */
-    DBG("Attaching to PDO %p...\n", bus_attach->pdo);
     lower = IoAttachDeviceToDeviceStack(
         WvBus.Fdo,
-        bus_attach->pdo
+        Pdo
       );
     if (lower == NULL) {
         status = STATUS_NO_SUCH_DEVICE;
         DBG("IoAttachDeviceToDeviceStack() failed!\n");
         goto err_attach;
       }
-    /* Set associations for the bus, device, FDO, PDO. */
-    WvBus.Pdo = bus_attach->pdo;
+    DBG("Attached to PDO %p.\n", Pdo);
     WvBus.LowerDeviceObject = lower;
-    DBG("Attached.\n");
+
     /* Probe for disks. */
     WvProbeDisks();
-    WvlBusProcessWorkItems(&WvBus);
-    bus_attach->status = STATUS_SUCCESS;
-    KeSetEvent(&bus_attach->signal, 0, FALSE);
-    return;
+
+    return STATUS_SUCCESS;
 
     IoDetachDevice(lower);
     err_attach:
 
+    WvBus.Pdo = NULL;
     err_already_established:
 
     DBG("Failed to attach.\n");
-    bus_attach->status = status;
-    KeSetEvent(&bus_attach->signal, 0, FALSE);
-    return;
-  }
-
-/**
- * Attempt to attach the bus to a PDO.
- *
- * @v pdo               The PDO to attach to.
- * @ret NTSTATUS        The status of the operation.
- *
- * This function will return a failure status if the bus is already
- * attached.  The actual attach operation occurs within the bus thread.
- */
-NTSTATUS STDCALL WvBusAttach(PDEVICE_OBJECT Pdo) {
-    WV_S_BUS_ATTACH_ bus_attach;
-
-    KeInitializeEvent(&bus_attach.signal, SynchronizationEvent, FALSE);
-    bus_attach.item.Func = WvBusAttach_;
-    bus_attach.pdo = Pdo;
-    if (!WvlThreadAddItem(&WvBusThread_, &bus_attach.item))
-      return STATUS_NO_SUCH_DEVICE;
-    KeWaitForSingleObject(
-        &bus_attach.signal,
-        Executive,
-        KernelMode,
-        FALSE,
-        NULL
-      );
-    return bus_attach.status;
+    return status;
   }
 
 /* Establish the bus FDO, thread, and possibly PDO. */
@@ -235,6 +138,9 @@ NTSTATUS STDCALL WvBusEstablish(IN PUNICODE_STRING RegistryPath) {
     UINT32 pdo_done = 0;
     PDEVICE_OBJECT pdo = NULL;
 
+    /* Initialize the spin-lock for WvBusAttach(). */
+    KeInitializeSpinLock(&WvBusPdoLock);
+
     /* Initialize the bus. */
     WvlBusInit(&WvBus);
     WvDevInit(&WvBusDev);
@@ -277,24 +183,6 @@ NTSTATUS STDCALL WvBusEstablish(IN PUNICODE_STRING RegistryPath) {
       }
     WvSymlinkDone = TRUE;
 
-    /* Start thread. */
-    KeInitializeEvent(&WvBusStartedSignal_, NotificationEvent, FALSE);
-    WvBusThread_.Main.Func = WvBusThread;
-    DBG("Starting thread...\n");
-    status = WvlThreadStart(&WvBusThread_);
-    if (!NT_SUCCESS(status)) {
-        DBG("Couldn't start bus thread!\n");
-        goto err_thread;
-      }
-    KeWaitForSingleObject(
-        &WvBusStartedSignal_,
-        Executive,
-        KernelMode,
-        FALSE,
-        NULL
-      );
-    KeResetEvent(&WvBusStartedSignal_);
-
     /* Open our Registry path. */
     status = WvlRegOpenKey(RegistryPath->Buffer, &reg_key);
     if (!NT_SUCCESS(status)) {
@@ -309,7 +197,7 @@ NTSTATUS STDCALL WvBusEstablish(IN PUNICODE_STRING RegistryPath) {
         return status;
       }
 
-    /* Create a root-enumerated PDO for our bus. */
+    /* If not, create a root-enumerated PDO for our bus. */
     IoReportDetectedDevice(
         WvDriverObj,
         InterfaceTypeUndefined,
@@ -349,9 +237,6 @@ NTSTATUS STDCALL WvBusEstablish(IN PUNICODE_STRING RegistryPath) {
     WvlRegCloseKey(reg_key);
     err_reg:
 
-    /* Cleanup by WvBusCleanup() */
-    err_thread:
-
     /* Cleanup by WvBusCleanup() */
     err_dos_symlink:
 
@@ -365,11 +250,8 @@ NTSTATUS STDCALL WvBusEstablish(IN PUNICODE_STRING RegistryPath) {
 VOID WvBusCleanup(void) {
     if (WvSymlinkDone)
       IoDeleteSymbolicLink(&WvBusDosname);
-    WvBus.Stop = TRUE;
-    if (WvBusThread_.State != WvlThreadStateNotStarted) {
-        DBG("Stopping thread...\n");
-        WvlThreadSendStopAndWait(&WvBusThread_);
-      }
+    /* Similar to IRP_MJ_PNP:IRP_MN_REMOVE_DEVICE */
+    WvlBusClear(&WvBus);
     IoDeleteDevice(WvBus.Fdo);
     WvBus.Fdo = NULL;
     return;
@@ -403,7 +285,7 @@ BOOLEAN STDCALL WvBusAddDev(
     Dev->Parent = WvBus.Fdo;
     /*
      * Initialize the device.  For disks, this routine is responsible for
-     * determining the disk's geometry appropriately for AoE/RAM/file disks.
+     * determining the disk's geometry appropriately for RAM/file disks.
      */
     Dev->Ops.Init(Dev);
     dev_obj->Flags &= ~DO_DEVICE_INITIALIZING;
@@ -411,39 +293,10 @@ BOOLEAN STDCALL WvBusAddDev(
     WvlBusAddNode(&WvBus, &Dev->BusNode);
     Dev->DevNum = WvlBusGetNodeNum(&Dev->BusNode);
 
-    DBG("Exit\n");
+    DBG("Added device %p with PDO %p.\n", Dev, Dev->Self);
     return TRUE;
   }
 
-/* Bus node removal thread item.  Internal. */
-typedef struct WV_BUS_NODE_REMOVAL_ {
-    WVL_S_THREAD_ITEM item;
-    WV_SP_DEV_T dev;
-    KEVENT signal;
-  } WV_S_BUS_NODE_REMOVAL_, * WV_SP_BUS_NODE_REMOVAL_;
-
-/* Remove a bus node within the bus thread's context.  Internal. */
-static WVL_F_THREAD_ITEM WvBusRemoveDev_;
-static VOID STDCALL WvBusRemoveDev_(IN OUT WVL_SP_THREAD_ITEM item) {
-    WV_SP_BUS_NODE_REMOVAL_ removal;
-
-    removal = CONTAINING_RECORD(item, WV_S_BUS_NODE_REMOVAL_, item);
-    /* If the node has been unlinked and we are called again, delete it. */
-    if (!removal->dev->BusNode.Linked) {
-        WvDevClose(removal->dev);
-        IoDeleteDevice(removal->dev->Self);
-        WvDevFree(removal->dev);
-      } else {        
-        /* Enqueue the node's removal. */
-        WvlBusRemoveNode(&removal->dev->BusNode);
-        /* We are in the thread's context, so process the removal right now. */
-        WvlBusProcessWorkItems(&WvBus);
-      }
-    /* The removal should be complete.  Signal completion. */
-    KeSetEvent(&removal->signal, 0, FALSE);
-    return;
-  }
-
 /**
  * Remove a device node from the WinVBlock bus.
  *
@@ -451,74 +304,51 @@ static VOID STDCALL WvBusRemoveDev_(IN OUT WVL_SP_THREAD_ITEM item) {
  * @ret NTSTATUS        The status of the operation.
  */
 NTSTATUS STDCALL WvBusRemoveDev(IN WV_SP_DEV_T Dev) {
-    WV_S_BUS_NODE_REMOVAL_ removal;
     NTSTATUS status;
 
-    removal.dev = Dev;
-    KeInitializeEvent(&removal.signal, SynchronizationEvent, FALSE);
-    removal.item.Func = WvBusRemoveDev_;
-    /* Enqueue the removal. */
-    status = WvlThreadAddItem(&WvBusThread_, &removal.item);
-    if (!NT_SUCCESS(status))
-      return status;
-
-    /* Wait for it. */
-    KeWaitForSingleObject(&removal.signal, Executive, KernelMode, FALSE, NULL);
-    return status;
+    if (!Dev->BusNode.Linked) {
+        WvDevClose(Dev);
+        IoDeleteDevice(Dev->Self);
+        WvDevFree(Dev);
+      } else
+        WvlBusRemoveNode(&Dev->BusNode);
+    return STATUS_SUCCESS;
   }
 
 static NTSTATUS STDCALL WvBusDevCtlDetach(
     IN PIRP irp
   ) {
-    PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
     UINT32 unit_num;
     WVL_SP_BUS_NODE walker;
+    WV_SP_DEV_T dev = NULL;
 
-    if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
-        NTSTATUS status;
-
-        /* Enqueue the IRP. */
-        status = WvlBusEnqueueIrp(&WvBus, irp);
-        if (status != STATUS_PENDING)
-          /* Problem. */
-          return WvlIrpComplete(irp, 0, status);
-        /* Ok. */
-        return status;
-      }
-    /* If we get here, we should be called by WvlBusProcessWorkItems() */
     unit_num = *((PUINT32) irp->AssociatedIrp.SystemBuffer);
-    DBG("Request to detach unit: %d\n", unit_num);
+    DBG("Request to detach unit %d...\n", unit_num);
 
     walker = NULL;
     /* For each node on the bus... */
+    WvlBusLock(&WvBus);
     while (walker = WvlBusGetNextNode(&WvBus, walker)) {
-        WV_SP_DEV_T dev = WvDevFromDevObj(WvlBusGetNodePdo(walker));
-        WV_S_BUS_NODE_REMOVAL_ removal;
-
         /* If the unit number matches... */
         if (WvlBusGetNodeNum(walker) == unit_num) {
+            dev = WvDevFromDevObj(WvlBusGetNodePdo(walker));
             /* If it's not a boot-time device... */
             if (dev->Boot) {
                 DBG("Cannot detach a boot-time device.\n");
                 /* Signal error. */
-                walker = NULL;
-                break;
+                dev = NULL;
               }
-            /*
-             * Detach the node and free it.  Since we are in the
-             * bus thread context, we use the internal removal.
-             */
-            DBG("Removing unit %d\n", unit_num);
-            removal.dev = dev;
-            removal.item.Func = WvBusRemoveDev_;
-            /* We ignore the signal, but initialize it for WvBusRemoveDev_() */
-            KeInitializeEvent(&removal.signal, SynchronizationEvent, FALSE);
-            WvBusRemoveDev_(&removal.item);
             break;
           }
       }
-    if (!walker)
-      return WvlIrpComplete(irp, 0, STATUS_INVALID_PARAMETER);
+    WvlBusUnlock(&WvBus);
+    if (!dev) {
+        DBG("Unit %d not found.\n", unit_num);
+        return WvlIrpComplete(irp, 0, STATUS_INVALID_PARAMETER);
+      }
+    /* Detach the node. */
+    WvBusRemoveDev(dev);
+    DBG("Removed unit %d.\n", unit_num);
     return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
   }
 
@@ -536,6 +366,9 @@ NTSTATUS STDCALL WvBusDevCtl(
         case IOCTL_FILE_DETACH:
           return WvBusDevCtlDetach(irp);
 
+        case IOCTL_WV_DUMMY:
+          return WvDummyIoctl(irp);
+
         default:
           irp->IoStatus.Information = 0;
           status = STATUS_INVALID_DEVICE_REQUEST;
@@ -594,89 +427,13 @@ NTSTATUS STDCALL WvBusPnpQueryDevText(
     return WvlIrpComplete(irp, irp->IoStatus.Information, status);
   }
 
-typedef struct WV_BUS_IRP_ {
-    WVL_S_THREAD_ITEM item;
-    PIRP irp;
-  } WV_S_BUS_IRP_, * WV_SP_BUS_IRP_;
-
-static WVL_F_THREAD_ITEM WvBusIrp_;
-static VOID STDCALL WvBusIrp_(IN OUT WVL_SP_THREAD_ITEM item) {
-    WV_SP_BUS_IRP_ bus_irp = CONTAINING_RECORD(item, WV_S_BUS_IRP_, item);
-    PIRP irp = bus_irp->irp;
-    PIO_STACK_LOCATION io_stack_loc;
-    UCHAR major, minor;
-
-    wv_free(item);
-    /* We are in the context of the bus thread. */
-    if (WvBus.Stop) {
-        WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
-        return;
-      }
-
-    io_stack_loc = IoGetCurrentIrpStackLocation(irp);
-    major = io_stack_loc->MajorFunction;
-    minor = io_stack_loc->MinorFunction;
-    switch (major) {
-
-        case IRP_MJ_PNP:
-          DBG(WVL_M_LIT " IRP_MJ_PNP.\n");
-          WvlBusPnpIrp(&WvBus, irp, io_stack_loc->MinorFunction);
-          break;
-
-        case IRP_MJ_DEVICE_CONTROL:
-          DBG(WVL_M_LIT " IRP_MJ_DEVICE_CONTROL.\n");
-          WvBusDevCtl(
-              irp,
-              io_stack_loc->Parameters.DeviceIoControl.IoControlCode
-            );
-          break;
-
-        case IRP_MJ_POWER:
-          DBG(WVL_M_LIT " IRP_MJ_POWER.\n");
-          WvlBusPower(&WvBus, irp);
-          break;
-
-        case IRP_MJ_SYSTEM_CONTROL:
-          DBG(WVL_M_LIT " IRP_MJ_SYSTEM_CONTROL.\n");
-          WvlBusSysCtl(&WvBus, irp);
-          break;
-
-        case IRP_MJ_CREATE:
-        case IRP_MJ_CLOSE:
-          DBG(WVL_M_LIT " IRP_MJ_[CREATE|CLOSE].\n");
-          /* Succeed with nothing to do. */
-          WvlIrpComplete(irp, 0, STATUS_SUCCESS);
-          break;
-
-        default:
-          DBG(WVL_M_LIT " unknown IRP: %d, %d.\n", major, minor);
-          WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
-          break;
-      }
-    return;
-  }
-
 /**
- * Enqueue an IRP for the WinVBlock bus.
+ * Fetch the WinVBlock main bus FDO.
  *
- * @v Irp               The IRP to enqueue.
- * @ret NTSTATUS        The status of the operation.
+ * @ret PDEVICE_OBJECT          The FDO.
+ *
+ * This function is useful to drivers trying to communicate with this one.
  */
-NTSTATUS STDCALL WvBusEnqueueIrp(IN OUT PIRP Irp) {
-    WV_SP_BUS_IRP_ bus_irp;
-    NTSTATUS status;
-
-    if (!Irp)
-      return STATUS_INVALID_PARAMETER;
-
-    bus_irp = wv_malloc(sizeof *bus_irp);
-    if (!bus_irp)
-      return WvlIrpComplete(Irp, 0, STATUS_INSUFFICIENT_RESOURCES);
-
-    bus_irp->item.Func = WvBusIrp_;
-    bus_irp->irp = Irp;
-    IoMarkIrpPending(Irp);
-    if (!WvlThreadAddItem(&WvBusThread_, &bus_irp->item))
-      WvlIrpComplete(Irp, 0, STATUS_NO_SUCH_DEVICE);
-    return STATUS_PENDING;
+WVL_M_LIB PDEVICE_OBJECT WvBusFdo(void) {
+    return WvBus.Fdo;
   }
index e743d53..a417441 100644 (file)
@@ -47,9 +47,9 @@
 
 /* From bus.c */
 extern WVL_S_BUS_T WvBus;
-extern NTSTATUS STDCALL WvBusEnqueueIrp(IN OUT PIRP);
 extern NTSTATUS STDCALL WvBusAttach(IN PDEVICE_OBJECT);
 extern NTSTATUS STDCALL WvBusEstablish(IN PUNICODE_STRING);
+extern NTSTATUS STDCALL WvBusDevCtl(IN PIRP, IN ULONG POINTER_ALIGNMENT);
 extern VOID WvBusCleanup(void);
 
 /* Exported. */
@@ -227,7 +227,7 @@ static NTSTATUS WvIrpPower(
     WVL_M_DEBUG_IRP_START(dev_obj, irp);
     /* Check for a bus IRP. */
     if (dev_obj == WvBus.Fdo)
-      return WvBusEnqueueIrp(irp);
+      return WvlBusPower(&WvBus, irp);
     /* WvDevFromDevObj() checks for a NULL dev_obj */
     dev = WvDevFromDevObj(dev_obj);
     /* Check that the device exists. */
@@ -253,7 +253,8 @@ static NTSTATUS WvIrpCreateClose(
     WVL_M_DEBUG_IRP_START(dev_obj, irp);
     /* Check for a bus IRP. */
     if (dev_obj == WvBus.Fdo)
-      return WvBusEnqueueIrp(irp);
+      /* Always succeed with nothing to do. */
+      return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
     /* WvDevFromDevObj() checks for a NULL dev_obj */
     dev = WvDevFromDevObj(dev_obj);
     /* Check that the device exists. */
@@ -273,7 +274,7 @@ static NTSTATUS WvIrpSysCtl(
     WVL_M_DEBUG_IRP_START(dev_obj, irp);
     /* Check for a bus IRP. */
     if (dev_obj == WvBus.Fdo)
-      return WvBusEnqueueIrp(irp);
+      return WvlBusSysCtl(&WvBus, irp);
     /* WvDevFromDevObj() checks for a NULL dev_obj */
     dev = WvDevFromDevObj(dev_obj);
     /* Check that the device exists. */
@@ -291,12 +292,15 @@ static NTSTATUS WvIrpDevCtl(
     IN PDEVICE_OBJECT dev_obj,
     IN PIRP irp
   ) {
+    PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
+    ULONG POINTER_ALIGNMENT code =
+      io_stack_loc->Parameters.DeviceIoControl.IoControlCode;
     WV_SP_DEV_T dev;
 
     WVL_M_DEBUG_IRP_START(dev_obj, irp);
     /* Check for a bus IRP. */
     if (dev_obj == WvBus.Fdo)
-      return WvBusEnqueueIrp(irp);
+      return WvBusDevCtl(irp, code);
     /* WvDevFromDevObj() checks for a NULL dev_obj */
     dev = WvDevFromDevObj(dev_obj);
     /* Check that the device exists. */
@@ -304,12 +308,10 @@ static NTSTATUS WvIrpDevCtl(
       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
     /* Call the particular device's power handler. */
     if (dev->IrpMj && dev->IrpMj->DevCtl) {
-        PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
-
         return dev->IrpMj->DevCtl(
             dev,
             irp,
-            io_stack_loc->Parameters.DeviceIoControl.IoControlCode
+            code
           );
       }
     /* Otherwise, we don't support the IRP. */
@@ -327,7 +329,8 @@ static NTSTATUS WvIrpScsi(
     WVL_M_DEBUG_IRP_START(dev_obj, irp);
     /* Check for a bus IRP. */
     if (dev_obj == WvBus.Fdo)
-      return WvBusEnqueueIrp(irp);
+      /* Not supported. */
+      return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
     /* WvDevFromDevObj() checks for a NULL dev_obj */
     dev = WvDevFromDevObj(dev_obj);
     /* Check that the device exists. */
@@ -356,7 +359,7 @@ static NTSTATUS WvIrpPnp(
     WVL_M_DEBUG_IRP_START(dev_obj, irp);
     /* Check for a bus IRP. */
     if (dev_obj == WvBus.Fdo)
-      return WvBusEnqueueIrp(irp);
+      return WvlBusPnp(&WvBus, irp);
     /* WvDevFromDevObj() checks for a NULL dev_obj */
     dev = WvDevFromDevObj(dev_obj);
     /* Check that the device exists. */
index 5553726..2ae5180 100644 (file)
@@ -42,7 +42,6 @@ extern NTSTATUS STDCALL WvBusRemoveDev(IN WV_SP_DEV_T);
 /* Forward declarations. */
 static NTSTATUS STDCALL WvDummyIds(IN PIRP, IN WV_SP_DUMMY_IDS);
 static WV_F_DEV_PNP WvDummyPnp;
-static WVL_F_BUS_WORK_ITEM WvDummyAdd_;
 
 /* Dummy PnP IRP handler. */
 static NTSTATUS STDCALL WvDummyPnp(
@@ -80,17 +79,18 @@ typedef struct WV_ADD_DUMMY {
   } WV_S_ADD_DUMMY, * WV_SP_ADD_DUMMY;
 
 /**
- * Add a dummy PDO child node in the context of the bus' thread.  Internal.
+ * Produce a dummy PDO node on the main bus.
  *
- * @v context           Points to the WV_S_ADD_DUMMY to process.
+ * @v DummyIds                  The PnP IDs for the dummy.  Also includes
+ *                              the device type and characteristics.
+ * @v Pdo                       Filled with a pointer to the created PDO.
+ *                              This parameter is optional.
+ * @ret NTSTATUS                The status of the operation.
  */
-static VOID STDCALL WvDummyAdd_(PVOID context) {
-    WV_SP_ADD_DUMMY dummy_context = context;
-    NTSTATUS status;
-    PDEVICE_OBJECT pdo = NULL;
-    WV_SP_DEV_T dev;
-    SIZE_T dummy_ids_size;
-    WV_SP_DUMMY_IDS dummy_ids;
+static NTSTATUS STDCALL WvDummyAdd(
+    IN WV_SP_DUMMY_IDS DummyIds,
+    IN PDEVICE_OBJECT * Pdo
+  ) {
     static WV_S_DEV_IRP_MJ irp_mj = {
         (WV_FP_DEV_DISPATCH) 0,
         (WV_FP_DEV_DISPATCH) 0,
@@ -98,55 +98,51 @@ static VOID STDCALL WvDummyAdd_(PVOID context) {
         (WV_FP_DEV_SCSI) 0,
         WvDummyPnp,
       };
+    NTSTATUS status;
+    PDEVICE_OBJECT pdo = NULL;
+    WV_SP_DEV_T dev;
+    WV_SP_DUMMY_IDS new_dummy_ids;
 
     status = IoCreateDevice(
         WvDriverObj,
         sizeof (WV_S_DEV_EXT),
         NULL,
-        dummy_context->DevType,
-        dummy_context->DevCharacteristics,
+        DummyIds->DevType,
+        DummyIds->DevCharacteristics,
         FALSE,
         &pdo
       );
     if (!NT_SUCCESS(status) || !pdo) {
         DBG("Couldn't create dummy device.\n");
-        dummy_context->Status = STATUS_INSUFFICIENT_RESOURCES;
+        status = STATUS_INSUFFICIENT_RESOURCES;
         goto err_create_pdo;
       }
 
     dev = wv_malloc(sizeof *dev);
     if (!dev) {
         DBG("Couldn't allocate dummy device.\n");
-        dummy_context->Status = STATUS_INSUFFICIENT_RESOURCES;
+        status = STATUS_INSUFFICIENT_RESOURCES;
         goto err_dev;
       }
 
-    dummy_ids_size =
-      sizeof *dummy_ids +
-      dummy_context->DummyIds->Len * sizeof dummy_ids->Text[0] -
-      sizeof dummy_ids->Text[0];        /* The struct hack uses a WCHAR[1]. */
-    dummy_ids = wv_malloc(dummy_ids_size);
-    if (!dummy_ids) {
+    new_dummy_ids = wv_malloc(DummyIds->Len);
+    if (!new_dummy_ids) {
         DBG("Couldn't allocate dummy IDs.\n");
-        dummy_context->Status = STATUS_INSUFFICIENT_RESOURCES;
+        status = STATUS_INSUFFICIENT_RESOURCES;
         goto err_dummy_ids;
       }
-    /* Copy the IDs offsets and lengths. */
-    RtlCopyMemory(dummy_ids, dummy_context->DummyIds, sizeof *dummy_ids);
-    /* Copy the text of the IDs. */
-    RtlCopyMemory(
-        &dummy_ids->Text,
-        dummy_context->DummyIds->Ids,
-        dummy_context->DummyIds->Len * sizeof dummy_ids->Text[0]
-      );
-    /* Point to the copy of the text. */
-    dummy_ids->Ids = dummy_ids->Text;
+
+    /* Copy the IDs' offsets and lengths. */
+    RtlCopyMemory(new_dummy_ids, DummyIds, DummyIds->Len);
 
     /* Ok! */
     WvDevInit(dev);
     dev->IrpMj = &irp_mj;
-    dev->ext = dummy_ids;       /* TODO: Implement a dummy free.  Leaking. */
+    dev->ext = new_dummy_ids;   /* TODO: Implement a dummy free.  Leaking. */
     dev->Self = pdo;
+    /* Optionally fill the caller's PDO pointer. */
+    if (Pdo)
+      *Pdo = pdo;
     WvDevForDevObj(pdo, dev);
     WvlBusInitNode(&dev->BusNode, pdo);
     /* Associate the parent bus. */
@@ -156,12 +152,9 @@ static VOID STDCALL WvDummyAdd_(PVOID context) {
     dev->DevNum = WvlBusGetNodeNum(&dev->BusNode);
     pdo->Flags &= ~DO_DEVICE_INITIALIZING;
 
-    dummy_context->Status = STATUS_SUCCESS;
-    dummy_context->Pdo = pdo;
-    KeSetEvent(dummy_context->Event, 0, FALSE);
-    return;
+    return STATUS_SUCCESS;
 
-    wv_free(dummy_ids);
+    wv_free(new_dummy_ids);
     err_dummy_ids:
 
     wv_free(dev);
@@ -170,64 +163,7 @@ static VOID STDCALL WvDummyAdd_(PVOID context) {
     IoDeleteDevice(pdo);
     err_create_pdo:
 
-    KeSetEvent(dummy_context->Event, 0, FALSE);
-    return;
-  }
-
-/**
- * Produce a dummy PDO node on the main bus.
- *
- * @v DummyIds                  The PnP IDs for the dummy.
- * @v DevType                   The type for the dummy device.
- * @v DevCharacteristics        The dummy device characteristics.
- * @v Pdo                       Filled with a pointer to the created PDO.
- * @ret NTSTATUS                The status of the operation.
- */
-WVL_M_LIB NTSTATUS STDCALL WvDummyAdd(
-    IN const WV_S_DUMMY_IDS * DummyIds,
-    IN DEVICE_TYPE DevType,
-    IN ULONG DevCharacteristics,
-    OUT PDEVICE_OBJECT * Pdo
-  ) {
-    KEVENT event;
-    WV_S_ADD_DUMMY context = {
-        DummyIds,
-        DevType,
-        DevCharacteristics,
-        &event,
-        STATUS_UNSUCCESSFUL,
-        NULL
-      };
-    WVL_S_BUS_CUSTOM_WORK_ITEM work_item = {
-        WvDummyAdd_,
-        &context
-      };
-    NTSTATUS status;
-
-    if (!DummyIds)
-      return STATUS_INVALID_PARAMETER;
-
-    if (!WvBus.Fdo)
-      return STATUS_NO_SUCH_DEVICE;
-
-    KeInitializeEvent(&event, SynchronizationEvent, FALSE);
-
-    status = WvlBusEnqueueCustomWorkItem(&WvBus, &work_item);
-    if (!NT_SUCCESS(status))
-      return status;
-
-    /* Wait for WvDummyAdd_() to complete. */
-    KeWaitForSingleObject(
-        &event,
-        Executive,
-        KernelMode,
-        FALSE,
-        NULL
-      );
-
-    if (context.Pdo)
-      *Pdo = context.Pdo;
-    return context.Status;
+    return status;
   }
 
 /**
@@ -242,6 +178,28 @@ WVL_M_LIB NTSTATUS STDCALL WvDummyRemove(IN PDEVICE_OBJECT Pdo) {
     return WvBusRemoveDev(WvDevFromDevObj(Pdo));
   }
 
+/**
+ * Handle a dummy IOCTL for creating a dummy PDO.
+ *
+ * @v Irp               An IRP with an associated buffer containing
+ *                      WV_S_DUMMY_IDS data.
+ * @ret NTSTATUS        The status of the operation.
+ */
+NTSTATUS STDCALL WvDummyIoctl(IN PIRP Irp) {
+    WV_SP_DUMMY_IDS dummy_ids = Irp->AssociatedIrp.SystemBuffer;
+    PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(Irp);
+
+    if (
+        io_stack_loc->Parameters.DeviceIoControl.InputBufferLength <
+        sizeof *dummy_ids
+      ) {
+        DBG("Dummy IDs too small in IRP %p.\n", Irp);
+        return WvlIrpComplete(Irp, 0, STATUS_INVALID_PARAMETER);
+      }
+
+    return WvlIrpComplete(Irp, 0, WvDummyAdd(dummy_ids, NULL));
+  }
+
 /**
  * Handle a PnP ID query with a WV_S_DUMMY_IDS object.
  *
@@ -255,28 +213,31 @@ static NTSTATUS STDCALL WvDummyIds(
   ) {
     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(Irp);
     BUS_QUERY_ID_TYPE query_type = io_stack_loc->Parameters.QueryId.IdType;
+    const CHAR * start;
     const WCHAR * ids;
     UINT32 len;
     NTSTATUS status;
 
+    start = (const CHAR *) DummyIds + DummyIds->Offset;
+    ids = (const WCHAR *) start;
     switch (query_type) {
         case BusQueryDeviceID:
-          ids = DummyIds->Ids + DummyIds->DevOffset;
+          ids += DummyIds->DevOffset;
           len = DummyIds->DevLen;
           break;
 
         case BusQueryInstanceID:
-          ids = DummyIds->Ids + DummyIds->InstanceOffset;
+          ids += DummyIds->InstanceOffset;
           len = DummyIds->InstanceLen;
           break;
 
         case BusQueryHardwareIDs:
-          ids = DummyIds->Ids + DummyIds->HardwareOffset;
+          ids += DummyIds->HardwareOffset;
           len = DummyIds->HardwareLen;
           break;
 
         case BusQueryCompatibleIDs:
-          ids = DummyIds->Ids + DummyIds->CompatOffset;
+          ids += DummyIds->CompatOffset;
           len = DummyIds->CompatLen;
           break;
 
index 5a47068..d365a1b 100644 (file)
 #include "bus.h"
 #include "debug.h"
 
-/* Types. */
-typedef enum WVL_BUS_WORK_ITEM_CMD {
-    WvlBusWorkItemCmdAddPdo_,
-    WvlBusWorkItemCmdRemovePdo_,
-    WvlBusWorkItemCmdProcessIrp_,
-    WvlBusWorkItemCmdCustom_,
-    WvlBusWorkItemCmds_
-  } WVL_E_BUS_WORK_ITEM_CMD, * WVL_EP_BUS_WORK_ITEM_CMD;
-
-typedef struct WVL_BUS_WORK_ITEM {
-    LIST_ENTRY Link;
-    WVL_E_BUS_WORK_ITEM_CMD Cmd;
-    union {
-        WVL_SP_BUS_NODE Node;
-        PIRP Irp;
-        WVL_SP_BUS_CUSTOM_WORK_ITEM Custom;
-      } Context;
-  } WVL_S_BUS_WORK_ITEM, * WVL_SP_BUS_WORK_ITEM;
-
-/* Forward declarations. */
-static WVL_F_BUS_THREAD WvlBusDefaultThread;
-static BOOLEAN WvlBusAddWorkItem(
-    WVL_SP_BUS_T,
-    WVL_SP_BUS_WORK_ITEM
-  );
-static WVL_SP_BUS_WORK_ITEM WvlBusGetWorkItem(WVL_SP_BUS_T);
-
 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
 WVL_M_LIB NTSTATUS STDCALL WvlBusSysCtl(
     IN WVL_SP_BUS_T Bus,
@@ -68,6 +41,7 @@ WVL_M_LIB NTSTATUS STDCALL WvlBusSysCtl(
   ) {
     PDEVICE_OBJECT lower = Bus->LowerDeviceObject;
 
+    /* We might be a floating FDO. */
     if (lower) {
         DBG("Passing IRP_MJ_SYSTEM_CONTROL down\n");
         IoSkipCurrentIrpStackLocation(Irp);
@@ -84,6 +58,7 @@ WVL_M_LIB NTSTATUS STDCALL WvlBusPower(
     PDEVICE_OBJECT lower = Bus->LowerDeviceObject;
 
     PoStartNextPowerIrp(Irp);
+    /* We might be a floating FDO. */
     if (lower) {
         IoSkipCurrentIrpStackLocation(Irp);
         return PoCallDriver(lower, Irp);
@@ -99,54 +74,8 @@ WVL_M_LIB NTSTATUS STDCALL WvlBusPower(
 WVL_M_LIB VOID WvlBusInit(WVL_SP_BUS_T Bus) {
     RtlZeroMemory(Bus, sizeof *Bus);
     /* Populate non-zero bus device defaults. */
-    Bus->Thread = WvlBusDefaultThread;
     InitializeListHead(&Bus->BusPrivate_.Nodes);
-    KeInitializeSpinLock(&Bus->BusPrivate_.WorkItemsLock);
-    InitializeListHead(&Bus->BusPrivate_.WorkItems);
-    KeInitializeEvent(&Bus->ThreadSignal, SynchronizationEvent, FALSE);
-  }
-
-/**
- * Add a work item for a bus to process.
- *
- * @v bus                       The bus to process the work item.
- * @v work_item                 The work item to add.
- * @ret BOOLEAN         TRUE if added, else FALSE
- *
- * Note that this function will initialize the work item's completion signal.
- */
-static BOOLEAN WvlBusAddWorkItem(
-    WVL_SP_BUS_T bus,
-    WVL_SP_BUS_WORK_ITEM work_item
-  ) {
-    ExInterlockedInsertTailList(
-        &bus->BusPrivate_.WorkItems,
-        &work_item->Link,
-        &bus->BusPrivate_.WorkItemsLock
-      );
-
-    return TRUE;
-  }
-
-/**
- * Get (and dequeue) a work item from a bus' queue.
- *
- * @v bus                       The bus processing the work item.
- * @ret WVL_SP_BUS_WORK_ITEM    The work item, or NULL for an empty queue.
- */
-static WVL_SP_BUS_WORK_ITEM WvlBusGetWorkItem(
-    WVL_SP_BUS_T bus
-  ) {
-    PLIST_ENTRY list_entry;
-
-    list_entry = ExInterlockedRemoveHeadList(
-        &bus->BusPrivate_.WorkItems,
-        &bus->BusPrivate_.WorkItemsLock
-      );
-    if (!list_entry)
-      return NULL;
-
-    return CONTAINING_RECORD(list_entry, WVL_S_BUS_WORK_ITEM, Link);
+    KeInitializeSpinLock(&Bus->BusPrivate_.NodeLock);
   }
 
 /**
@@ -155,25 +84,25 @@ static WVL_SP_BUS_WORK_ITEM WvlBusGetWorkItem(
  * @v bus               The bus to add the node to.
  * @v new_node          The PDO node to add to the bus.
  *
- * Don't call this function yourself.  It expects to have exclusive
- * access to the bus' list of children.
+ * Don't call this function yourself.  It doesn't perform any error-checking.
  */
 static VOID STDCALL WvlBusAddNode_(WVL_SP_BUS_T bus, WVL_SP_BUS_NODE new_node) {
     PLIST_ENTRY walker;
+    KIRQL irql;
 
     DBG(
-        "Adding PDO 0x%08X to bus 0x%08X.\n",
+        "Adding PDO %p to bus %p.\n",
         (PVOID) new_node->BusPrivate_.Pdo,
         (PVOID) bus
       );
     ObReferenceObject(new_node->BusPrivate_.Pdo);
-    bus->BusPrivate_.NodeCount++;
     /* It's too bad about having both linked list and bus ref. */
     new_node->BusPrivate_.Bus = bus;
 
     /* Find a slot for the new child. */
     walker = &bus->BusPrivate_.Nodes;
     new_node->BusPrivate_.Num = 0;
+    KeAcquireSpinLock(&bus->BusPrivate_.NodeLock, &irql);
     while ((walker = walker->Flink) != &bus->BusPrivate_.Nodes) {
         WVL_SP_BUS_NODE node = CONTAINING_RECORD(
             walker,
@@ -197,7 +126,12 @@ static VOID STDCALL WvlBusAddNode_(WVL_SP_BUS_T bus, WVL_SP_BUS_NODE new_node) {
       } /* while */
     /* Insert before walker. */
     InsertTailList(walker, &new_node->BusPrivate_.Link);
+    bus->BusPrivate_.NodeCount++;
+    KeReleaseSpinLock(&bus->BusPrivate_.NodeLock, irql);
     new_node->Linked = TRUE;
+    /* We might be floating. */
+    if (bus->Pdo)
+      IoInvalidateDeviceRelations(bus->Pdo, BusRelations);
     return;
   }
 
@@ -207,237 +141,127 @@ static VOID STDCALL WvlBusAddNode_(WVL_SP_BUS_T bus, WVL_SP_BUS_NODE new_node) {
  * @v bus             The bus to remove the node from.
  * @v node            The PDO node to remove from its parent bus.
  *
- * Don't call this function yourself.  It expects to have exclusive
- * access to the bus' list of children.
+ * Don't call this function yourself.  It doesn't perform any error-checking.
  */
 static VOID STDCALL WvlBusRemoveNode_(
     WVL_SP_BUS_T bus,
     WVL_SP_BUS_NODE node
   ) {
+    KIRQL irql;
+
     DBG(
         "Removing PDO 0x%08X from bus 0x%08X.\n",
         (PVOID) node->BusPrivate_.Pdo,
         (PVOID) bus
       );
+    KeAcquireSpinLock(&bus->BusPrivate_.NodeLock, &irql);
     RemoveEntryList(&node->BusPrivate_.Link);
+    bus->BusPrivate_.NodeCount--;
+    KeReleaseSpinLock(&bus->BusPrivate_.NodeLock, irql);
     node->Linked = FALSE;
     ObDereferenceObject(node->BusPrivate_.Pdo);
-    bus->BusPrivate_.NodeCount--;
+    /* We might be floating. */
+    if (bus->Pdo)
+      IoInvalidateDeviceRelations(bus->Pdo, BusRelations);
     return;    
   }
 
 /**
- * Process work items for a bus.
- *
- * @v Bus               The bus to process its work items.
- */
-WVL_M_LIB VOID WvlBusProcessWorkItems(WVL_SP_BUS_T Bus) {
-    WVL_SP_BUS_WORK_ITEM work_item;
-    WVL_SP_BUS_NODE node;
-    PIRP irp;
-    PIO_STACK_LOCATION io_stack_loc;
-    PDEVICE_OBJECT dev_obj;
-    PDRIVER_OBJECT driver_obj;
-    BOOLEAN nodes_changed = FALSE;
-
-    while (work_item = WvlBusGetWorkItem(Bus)) {
-        switch (work_item->Cmd) {
-            case WvlBusWorkItemCmdAddPdo_:
-              node = work_item->Context.Node;
-              WvlBusAddNode_(Bus, node);
-              nodes_changed = TRUE;
-              break;
-
-            case WvlBusWorkItemCmdRemovePdo_:
-              node = work_item->Context.Node;
-              WvlBusRemoveNode_(Bus, node);
-              nodes_changed = TRUE;
-              break;
-
-            case WvlBusWorkItemCmdProcessIrp_:
-              irp = work_item->Context.Irp;
-              io_stack_loc = IoGetCurrentIrpStackLocation(irp);
-              dev_obj = Bus->Fdo;
-              if (!dev_obj)
-                WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
-              driver_obj = dev_obj->DriverObject;
-              driver_obj->MajorFunction[io_stack_loc->MajorFunction](
-                  dev_obj,
-                  irp
-                );
-              break;
-
-            case WvlBusWorkItemCmdCustom_:
-              DBG("Custom work item.\n");
-              work_item->Context.Custom->Func(
-                  work_item->Context.Custom->Context
-                );
-              break;
-
-            default:
-              DBG("Unknown work item type!\n");
-          }
-        wv_free(work_item);
-      }
-    if (nodes_changed && Bus->Pdo) {
-        nodes_changed = FALSE;
-        IoInvalidateDeviceRelations(
-            Bus->Pdo,
-            BusRelations
-          );
-      }
-    return;
-  }
-
-/**
- * Cancel pending work items for a bus.
+ * Initialize a bus node with an associated PDO.
  *
- * @v Bus       The bus to cancel pending work items for.
+ * @v Node              The node to initialize.
+ * @v Pdo               The PDO to associate the node with.
+ * @ret BOOLEAN FALSE for a NULL argument, otherwise TRUE
  */
-WVL_M_LIB VOID WvlBusCancelWorkItems(WVL_SP_BUS_T Bus) {
-    WVL_SP_BUS_WORK_ITEM work_item;
+WVL_M_LIB BOOLEAN STDCALL WvlBusInitNode(
+    OUT WVL_SP_BUS_NODE Node,
+    IN PDEVICE_OBJECT Pdo
+  ) {
+    if (!Node || !Pdo)
+      return FALSE;
 
-    DBG("Canceling work items.\n");
-    while (work_item = WvlBusGetWorkItem(Bus))
-      wv_free(work_item);
-    return;
+    RtlZeroMemory(Node, sizeof *Node);
+    Node->BusPrivate_.Pdo = Pdo;
+    return TRUE;
   }
 
 /**
- * The bus thread wrapper.
+ * Clear a bus of its associations.
  *
- * @v context           The thread context.  In our case, it points to
- *                      the bus that the thread should use in processing.
+ * @v Bus               The bus to clear.
  *
- * Note that we do not attempt to free the bus data; this is a bus
- * implementor's responsibility.  We do, however, set the ThreadStopped
- * signal which should mean that resources can be freed, from a completed
- * thread's perspective.
+ * Note that you should set the bus start != WvlBusStateStarted before
+ * calling this function.  Otherwise, if an IRP comes through, it might
+ * race with the associations being cleared.
  */
-static VOID STDCALL WvlBusThread(IN PVOID context) {
-    WVL_SP_BUS_T bus = context;
+WVL_M_LIB VOID STDCALL WvlBusClear(IN WVL_SP_BUS_T Bus) {
+    KIRQL irql;
+    PLIST_ENTRY node_link;
+
+    if (Bus->State == WvlBusStateStarted)
+      DBG("Caller did not stop the bus!\n");
+    /*
+     * Remove all children.  The state should have been set by the caller,
+     * so wait for anyone working with the node list to finish.
+     */
+    KeAcquireSpinLock(&Bus->BusPrivate_.NodeLock, &irql);
+    KeReleaseSpinLock(&Bus->BusPrivate_.NodeLock, irql);
+    /* Safe now, assuming no-one after us goes for the lock. */
+    node_link = &Bus->BusPrivate_.Nodes;
+    while ((node_link = node_link->Flink) != &Bus->BusPrivate_.Nodes) {
+        WVL_SP_BUS_NODE node = CONTAINING_RECORD(
+            node_link,
+            WVL_S_BUS_NODE,
+            BusPrivate_.Link
+          );
 
-    if (!bus || !bus->Thread) {
-        DBG("No bus or no thread!\n");
-        return;
+        RemoveEntryList(&node->BusPrivate_.Link);
+        node->Linked = FALSE;
+        ObDereferenceObject(node->BusPrivate_.Pdo);
+        Bus->BusPrivate_.NodeCount--;
+        DBG("Removed PDO from bus %p.\n", Bus);
       }
-
-    bus->Thread(bus);
-    DBG("Exiting.\n");
-    PsTerminateSystemThread(STATUS_SUCCESS);
+    /* Detach and disassociate. */
+    if (Bus->LowerDeviceObject)
+      IoDetachDevice(Bus->LowerDeviceObject);
+    /* Disassociate. */
+    Bus->LowerDeviceObject = NULL;
+    Bus->Pdo = NULL;
     return;
   }
 
 /**
- * The default bus thread routine.
+ * Lock a bus' list of child nodes for iteration.
  *
- * @v bus       Points to the bus device for the thread to work with.
- *
- * Note that if you implement your own bus type using this library,
- * you can override the thread routine with your own.  If you do so,
- * your thread routine should call WvlBusProcessWorkItems() within
- * its loop.  To start a bus thread, use WvlBusStartThread()
- * If you implement your own thread routine, you are also responsible
- * for calling WvlBusCancelWorkItems() and freeing the bus.
+ * @v Bus               The bus to be locked.
  */
-static VOID STDCALL WvlBusDefaultThread(IN WVL_SP_BUS_T bus) {
-    LARGE_INTEGER timeout;
-
-    /* Wake up at least every 30 seconds. */
-    timeout.QuadPart = -300000000LL;
-
-    /* When WVL_S_BUS_T::Stop is set, we shut down. */
-    while (!bus->Stop) {
-        DBG("Alive.\n");
-
-        /* Wait for the work signal or the timeout. */
-        KeWaitForSingleObject(
-            &bus->ThreadSignal,
-            Executive,
-            KernelMode,
-            FALSE,
-            &timeout
-          );
-        /* Reset the work signal. */
-        KeResetEvent(&bus->ThreadSignal);
-
-        WvlBusProcessWorkItems(bus);
-      } /* while !bus->Stop */
-
-    WvlBusCancelWorkItems(bus);
+WVL_M_LIB VOID STDCALL WvlBusLock(IN WVL_SP_BUS_T Bus) {
+    if (!Bus) {
+        DBG("No bus specified!\n");
+        return;
+      }
+    KeAcquireSpinLock(
+        &Bus->BusPrivate_.NodeLock,
+        &Bus->BusPrivate_.NodeLockIrql
+      );
     return;
   }
 
 /**
- * Start a bus thread.
+ * Unlock a bus' list of child nodes from iteration.
  *
- * @v Bus               The bus to start a thread for.
- * @v Thread            A PETHREAD to be filled to reference the thread.
- * @ret NTSTATUS        The status of the thread creation operation.
- *
- * Also see WVL_F_BUS_THREAD in the header for details about the prototype
- * for implementing your own bus thread routine.  You set WVL_S_BUS_T::Thread
- * to specify your own thread routine, then call this function to start it.
- * When stopping the thread, you can wait on the thread handle.
+ * @v Bus               The bus to be unlocked.
  */
-WVL_M_LIB NTSTATUS WvlBusStartThread(
-    IN WVL_SP_BUS_T Bus,
-    OUT PETHREAD * Thread
-  ) {
-    OBJECT_ATTRIBUTES obj_attrs;
-    HANDLE thread_handle;
-    NTSTATUS status;
-
+WVL_M_LIB VOID STDCALL WvlBusUnlock(IN WVL_SP_BUS_T Bus) {
     if (!Bus) {
         DBG("No bus specified!\n");
-        return STATUS_INVALID_PARAMETER;
+        return;
       }
-
-    InitializeObjectAttributes(
-        &obj_attrs,
-        NULL,
-        OBJ_KERNEL_HANDLE,
-        NULL,
-        NULL
-      );
-    status = PsCreateSystemThread(
-        &thread_handle,
-        THREAD_ALL_ACCESS,
-        &obj_attrs,
-        NULL,
-        NULL,
-        WvlBusThread,
-        Bus
+    KeReleaseSpinLock(
+        &Bus->BusPrivate_.NodeLock,
+        Bus->BusPrivate_.NodeLockIrql
       );
-    if (!NT_SUCCESS(status))
-      return status;
-    return ObReferenceObjectByHandle(
-        thread_handle,
-        THREAD_ALL_ACCESS,
-        *PsThreadType,
-        KernelMode,
-        Thread,
-        NULL
-      );
-  }
-
-/**
- * Initialize a bus node with an associated PDO.
- *
- * @v Node              The node to initialize.
- * @v Pdo               The PDO to associate the node with.
- * @ret BOOLEAN FALSE for a NULL argument, otherwise TRUE
- */
-WVL_M_LIB BOOLEAN STDCALL WvlBusInitNode(
-    OUT WVL_SP_BUS_NODE Node,
-    IN PDEVICE_OBJECT Pdo
-  ) {
-    if (!Node || !Pdo)
-      return FALSE;
-
-    RtlZeroMemory(Node, sizeof *Node);
-    Node->BusPrivate_.Pdo = Pdo;
-    return TRUE;
+    return;
   }
 
 /**
@@ -447,16 +271,13 @@ WVL_M_LIB BOOLEAN STDCALL WvlBusInitNode(
  * @v Node              The PDO node to add to the bus.
  * @ret NTSTATUS        The status of the operation.
  *
- * Do not attempt to add the same node to more than one bus.
- * When WvlBusProcessWorkItems() is called for the bus, the
- * node will be added.  This is usually from the bus' thread.
+ * Do not attempt to add the same node to more than one bus.  Do not call
+ * this function between WvlBusLock() and WvlBusUnlock() on the same bus.
  */
 WVL_M_LIB NTSTATUS STDCALL WvlBusAddNode(
     WVL_SP_BUS_T Bus,
     WVL_SP_BUS_NODE Node
   ) {
-    WVL_SP_BUS_WORK_ITEM work_item;
-
     if (
         !Bus ||
         !Node ||
@@ -464,20 +285,11 @@ WVL_M_LIB NTSTATUS STDCALL WvlBusAddNode(
       )
       return STATUS_INVALID_PARAMETER;
 
-    if (Bus->Stop)
+    if (Bus->State != WvlBusStateStarted)
       return STATUS_NO_SUCH_DEVICE;
 
-    if (!(work_item = wv_malloc(sizeof *work_item)))
-      return STATUS_INSUFFICIENT_RESOURCES;
-
-    work_item->Cmd = WvlBusWorkItemCmdAddPdo_;
-    work_item->Context.Node = Node;
-    if (!WvlBusAddWorkItem(Bus, work_item)) {
-        wv_free(work_item);
-        return STATUS_UNSUCCESSFUL;
-      }
-    /* Fire and forget. */
-    KeSetEvent(&Bus->ThreadSignal, 0, FALSE);
+    /* Nothing can stop me now, 'cause I don't care. any. more. */
+    WvlBusAddNode_(Bus, Node);
     return STATUS_SUCCESS;
   }
 
@@ -487,100 +299,19 @@ WVL_M_LIB NTSTATUS STDCALL WvlBusAddNode(
  * @v Node              The PDO node to remove from its parent bus.
  * @ret NTSTATUS        The status of the operation.
  *
- * When WvlBusProcessWorkItems() is called for the bus, it will
- * then remove the node.  This is usually from the bus' thread.
+ * Do not call this function between WvlBusLock() and WvlBusUnlock() on
+ * the same bus.
  */
 WVL_M_LIB NTSTATUS STDCALL WvlBusRemoveNode(
     WVL_SP_BUS_NODE Node
   ) {
     WVL_SP_BUS_T bus;
-    WVL_SP_BUS_WORK_ITEM work_item;
 
     if (!Node || !(bus = Node->BusPrivate_.Bus))
       return STATUS_INVALID_PARAMETER;
 
-    if (bus->Stop)
-      return STATUS_NO_SUCH_DEVICE;
-
-    if (!(work_item = wv_malloc(sizeof *work_item)))
-      return STATUS_INSUFFICIENT_RESOURCES;
-
-    work_item->Cmd = WvlBusWorkItemCmdRemovePdo_;
-    work_item->Context.Node = Node;
-    if (!WvlBusAddWorkItem(bus, work_item)) {
-        wv_free(work_item);
-        return STATUS_UNSUCCESSFUL;
-      }
-    /* Fire and forget. */
-    KeSetEvent(&bus->ThreadSignal, 0, FALSE);
-    return STATUS_SUCCESS;
-  }
-
-/**
- * Enqueue an IRP for a bus' thread to process.
- *
- * @v Bus               The bus for the IRP.
- * @v Irp               The IRP for the bus.
- * @ret NTSTATUS        The status of the operation.  Returns STATUS_PENDING
- *                      if the IRP is successfully added to the queue.
- */
-WVL_M_LIB NTSTATUS STDCALL WvlBusEnqueueIrp(
-    WVL_SP_BUS_T Bus,
-    PIRP Irp
-  ) {
-    WVL_SP_BUS_WORK_ITEM work_item;
-
-    if (!Bus || !Irp)
-      return STATUS_INVALID_PARAMETER;
-
-    if (Bus->Stop)
-      return STATUS_NO_SUCH_DEVICE;
-
-    if (!(work_item = wv_malloc(sizeof *work_item)))
-      return STATUS_INSUFFICIENT_RESOURCES;
-
-    work_item->Cmd = WvlBusWorkItemCmdProcessIrp_;
-    work_item->Context.Irp = Irp;
-    IoMarkIrpPending(Irp);
-    if (!WvlBusAddWorkItem(Bus, work_item)) {
-        wv_free(work_item);
-        return STATUS_UNSUCCESSFUL;
-      }
-    /* Fire and forget. */
-    KeSetEvent(&Bus->ThreadSignal, 0, FALSE);
-    return STATUS_PENDING;
-  }
-
-/**
- * Enqueue a custom work item for a bus' thread to process.
- *
- * @v Bus               The bus for the IRP.
- * @v CustomWorkItem    The custom work item for the bus' thread to process.
- * @ret NTSTATUS        The status of the operation.
- */
-WVL_M_LIB NTSTATUS STDCALL WvlBusEnqueueCustomWorkItem(
-    WVL_SP_BUS_T Bus,
-    WVL_SP_BUS_CUSTOM_WORK_ITEM CustomWorkItem
-  ) {
-    WVL_SP_BUS_WORK_ITEM work_item;
-
-    if (!Bus || !CustomWorkItem)
-      return STATUS_INVALID_PARAMETER;
-
-    if (Bus->Stop)
-      return STATUS_NO_SUCH_DEVICE;
-
-    if (!(work_item = wv_malloc(sizeof *work_item)))
-      return STATUS_INSUFFICIENT_RESOURCES;
-
-    work_item->Cmd = WvlBusWorkItemCmdCustom_;
-    work_item->Context.Custom = CustomWorkItem;
-    if (!WvlBusAddWorkItem(Bus, work_item)) {
-        wv_free(work_item);
-        return STATUS_UNSUCCESSFUL;
-      }
-    /* Fire and forget. */
-    KeSetEvent(&Bus->ThreadSignal, 0, FALSE);
+    /* Remove the node. */
+    WvlBusRemoveNode_(bus, Node);
     return STATUS_SUCCESS;
   }
 
@@ -603,9 +334,9 @@ WVL_M_LIB UINT32 STDCALL WvlBusGetNodeNum(
  * @v PrevNode          The previous node.  Pass NULL to begin.
  * @ret WVL_SP_BUS_NODE  Returns NULL when there are no more nodes.
  *
- * This function should only be called within the thread context of
- * whichever thread calls WvlBusProcessWorkItems() because it expects
- * the list of child nodes to remain static between calls.
+ * This function does not lock the bus, so you should lock the bus first
+ * with WvlBusLock(), then unlock it with WvlBusUnlock() when your
+ * iteration is finished.
  */
 WVL_M_LIB WVL_SP_BUS_NODE STDCALL WvlBusGetNextNode(
     IN WVL_SP_BUS_T Bus,
@@ -642,45 +373,10 @@ WVL_M_LIB PDEVICE_OBJECT STDCALL WvlBusGetNodePdo(
  * @v UINT32            The count of nodes on the bus.
  *
  * In order for this function to yield a race-free, useful result, it
- * should be used by whatever thread calls WvlBusProcessWorkItems()
+ * should be used inside a WvlBusLock() <-> WvlBusUnlock() section.
  */
 WVL_M_LIB UINT32 STDCALL WvlBusGetNodeCount(
     WVL_SP_BUS_T Bus
   ) {
     return Bus->BusPrivate_.NodeCount;
   }
-
-/**
- * Register an owning thread for a bus.
- *
- * @v Bus               The bus to take ownership of with the current thread.
- * @ret BOOLEAN         FALSE if the bus is already owned, else TRUE.
- *
- * Some operations, such as manipulations of the child node list,
- * are best carried out serialized on a single, owning thread.
- * This is to avoid race conditions but without holding an
- * expensive lock.
- */
-WVL_M_LIB BOOLEAN STDCALL WvlBusRegisterOwnerThread(IN WVL_SP_BUS_T Bus) {
-    if (Bus->BusPrivate_.Thread)
-      return FALSE;
-    Bus->BusPrivate_.Thread = PsGetCurrentThread();
-    return TRUE;
-  }
-
-/**
- * Check if the current thread owns a bus.
- *
- * @v Bus               The bus to check ownership of.
- * @ret BOOLEAN         FALSE if the bus is owned or ownerless, else TRUE.
- *
- * If a bus doesn't have an owning thread, this returns FALSE.
- */
-WVL_M_LIB BOOLEAN STDCALL WvlBusNotOwned(IN WVL_SP_BUS_T Bus) {
-    if (
-        !Bus->BusPrivate_.Thread ||
-        (Bus->BusPrivate_.Thread == PsGetCurrentThread())
-      )
-      return FALSE;
-    return TRUE;
-  }
index f75f9a4..9b74b6d 100644 (file)
@@ -79,23 +79,9 @@ static NTSTATUS STDCALL WvlBusPnpStartDev(IN WVL_SP_BUS_T bus, IN PIRP irp) {
   }
 
 static NTSTATUS STDCALL WvlBusPnpRemoveDev(IN WVL_SP_BUS_T bus, IN PIRP irp) {
-    PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
+    PDEVICE_OBJECT lower = bus->LowerDeviceObject;
     NTSTATUS status;
-    PDEVICE_OBJECT lower;
-    PLIST_ENTRY node_link;
 
-    if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
-        /* Enqueue the IRP. */
-        status = WvlBusEnqueueIrp(bus, irp);
-        if (status != STATUS_PENDING)
-          /* Problem. */
-          return WvlIrpComplete(irp, 0, status);
-        /* Ok. */
-        return status;
-      }
-    /* If we get here, we should be called by WvlBusProcessWorkItems() */
-    status = STATUS_SUCCESS;
-    lower = bus->LowerDeviceObject;
     bus->OldState = bus->State;
     bus->State = WvlBusStateDeleted;
     /* Pass the IRP on to any lower DEVICE_OBJECT */
@@ -105,23 +91,10 @@ static NTSTATUS STDCALL WvlBusPnpRemoveDev(IN WVL_SP_BUS_T bus, IN PIRP irp) {
         IoSkipCurrentIrpStackLocation(irp);
         status = IoCallDriver(lower, irp);
       }
-    /* Remove all children. */
-    node_link = &bus->BusPrivate_.Nodes;
-    while ((node_link = node_link->Flink) != &bus->BusPrivate_.Nodes) {
-        WVL_SP_BUS_NODE node = CONTAINING_RECORD(
-            node_link,
-            WVL_S_BUS_NODE,
-            BusPrivate_.Link
-          );
-
-        DBG("Removing PDO from bus...\n");
-        RemoveEntryList(&node->BusPrivate_.Link);
-        node->Linked = FALSE;
-        ObDereferenceObject(node->BusPrivate_.Pdo);
-        bus->BusPrivate_.NodeCount--;
-      }
-    /* Stop the thread. */
-    bus->Stop = TRUE;
+    WvlBusClear(bus);
+    /* Without a lower DEVICE_OBJECT, we have to complete the IRP. */
+    if (!lower)
+      return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
     return status;
   }
 
@@ -129,23 +102,13 @@ static NTSTATUS STDCALL WvlBusPnpQueryDevRelations(
     IN WVL_SP_BUS_T bus,
     IN PIRP irp
   ) {
-    NTSTATUS status;
     PDEVICE_OBJECT lower = bus->LowerDeviceObject;
     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
-    UINT32 i;
+    KIRQL irql;
     PDEVICE_RELATIONS dev_relations;
+    UINT32 i;
     PLIST_ENTRY node_link;
 
-    if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
-        /* Enqueue the IRP. */
-        status = WvlBusEnqueueIrp(bus, irp);
-        if (status != STATUS_PENDING)
-          /* Problem. */
-          return WvlIrpComplete(irp, 0, status);
-        /* Ok. */
-        return status;
-      }
-    /* If we get here, we should be called by WvlBusProcessWorkItems() */
     if (
         io_stack_loc->Parameters.QueryDeviceRelations.Type != BusRelations ||
         irp->IoStatus.Information
@@ -160,12 +123,14 @@ static NTSTATUS STDCALL WvlBusPnpQueryDevRelations(
             irp->IoStatus.Status
           );
       }
+    KeAcquireSpinLock(&bus->BusPrivate_.NodeLock, &irql);
     dev_relations = wv_malloc(
         sizeof *dev_relations +
           (sizeof (PDEVICE_OBJECT) * bus->BusPrivate_.NodeCount)
       );
     if (dev_relations == NULL) {
         /* Couldn't allocate dev_relations, but silently succeed. */
+        KeReleaseSpinLock(&bus->BusPrivate_.NodeLock, irql);
         if (lower) {
             IoSkipCurrentIrpStackLocation(irp);
             return IoCallDriver(lower, irp);
@@ -187,13 +152,26 @@ static NTSTATUS STDCALL WvlBusPnpQueryDevRelations(
         ObReferenceObject(node->BusPrivate_.Pdo);
         i++;
       }
+    KeReleaseSpinLock(&bus->BusPrivate_.NodeLock, irql);
+    /* Assertion. */
+    if (i != dev_relations->Count) {
+        DBG("Inconsistent node list count!\n");
+        /* Pass it on or report failure. */
+        if (lower) {
+            IoSkipCurrentIrpStackLocation(irp);
+            return IoCallDriver(lower, irp);
+          }
+        return WvlIrpComplete(irp, 0, STATUS_UNSUCCESSFUL);
+      }
     irp->IoStatus.Information = (ULONG_PTR) dev_relations;
-    irp->IoStatus.Status = status = STATUS_SUCCESS;
+    irp->IoStatus.Status = STATUS_SUCCESS;
+    /* Should we pass it on? */
     if (lower) {
         IoSkipCurrentIrpStackLocation(irp);
         return IoCallDriver(lower, irp);
       }
-    return WvlIrpComplete(irp, irp->IoStatus.Information, status);
+    /* Success. */
+    return WvlIrpComplete(irp, irp->IoStatus.Information, STATUS_SUCCESS);
   }
 
 static NTSTATUS STDCALL WvlBusPnpQueryCapabilities(
@@ -205,10 +183,11 @@ static NTSTATUS STDCALL WvlBusPnpQueryCapabilities(
       io_stack_loc->Parameters.DeviceCapabilities.Capabilities;
     NTSTATUS status;
     DEVICE_CAPABILITIES ParentDeviceCapabilities;
-    PDEVICE_OBJECT lower;
+    PDEVICE_OBJECT lower = bus->LowerDeviceObject;;
 
     if (DeviceCapabilities->Version != 1 ||
-        DeviceCapabilities->Size < sizeof (DEVICE_CAPABILITIES)
+        DeviceCapabilities->Size < sizeof (DEVICE_CAPABILITIES) ||
+        !lower
       )
       return WvlIrpComplete(irp, 0, STATUS_UNSUCCESSFUL);
     /* Let the lower DEVICE_OBJECT handle the IRP. */
@@ -326,6 +305,7 @@ static NTSTATUS STDCALL WvlBusPnpSimple(
       }
 
     irp->IoStatus.Status = status;
+    /* Should we pass it on?  We might be a floating FDO. */
     if (lower) {
         IoSkipCurrentIrpStackLocation(irp);
         return IoCallDriver(lower, irp);
@@ -334,12 +314,13 @@ static NTSTATUS STDCALL WvlBusPnpSimple(
   }
 
 /* Bus PnP dispatch routine. */
-WVL_M_LIB NTSTATUS STDCALL WvlBusPnpIrp(
+WVL_M_LIB NTSTATUS STDCALL WvlBusPnp(
     IN WVL_SP_BUS_T Bus,
-    IN PIRP Irp,
-    IN UCHAR Code
+    IN PIRP Irp
   ) {
-    switch (Code) {
+    UCHAR code = IoGetCurrentIrpStackLocation(Irp)->MinorFunction;
+
+    switch (code) {
         case IRP_MN_QUERY_DEVICE_TEXT:
           DBG("IRP_MN_QUERY_DEVICE_TEXT\n");
           return Bus->QueryDevText(Bus, Irp);
@@ -365,7 +346,6 @@ WVL_M_LIB NTSTATUS STDCALL WvlBusPnpIrp(
           return WvlBusPnpStartDev(Bus, Irp);
 
         default:
-          DBG("Simple\n");
-          return WvlBusPnpSimple(Bus, Irp, Code);
+          return WvlBusPnpSimple(Bus, Irp, code);
       }
   }