[bus,driver] Introduce bus threading
authorShao Miller <Shao.Miller@yrdsb.edu.on.ca>
Wed, 15 Dec 2010 02:55:55 +0000 (21:55 -0500)
committerShao Miller <Shao.Miller@yrdsb.edu.on.ca>
Wed, 15 Dec 2010 02:55:55 +0000 (21:55 -0500)
The driver now calls bus__start_thread() on the main bus.

One can implement their own bus__thread_func routine and
assign it to the bus::thread member.  If one does so, one
should call bus__process_work_items() within the thread's
loop, and one should be responsible for freeing their bus.

src/include/bus.h
src/winvblock/bus/bus.c
src/winvblock/driver.c

index 574fe2f..cb32db7 100644 (file)
  * Bus specifics.
  */
 
+/**
+ * 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
+ * bus__process_work_items() within its loop.
+ */
+typedef void STDCALL bus__thread_func(IN struct bus__type *);
+
+/* The bus type. */
 struct bus__type {
     struct device__type * device;
     PDEVICE_OBJECT LowerDeviceObject;
@@ -42,6 +53,8 @@ struct bus__type {
     winvblock__bool named;
     LIST_ENTRY work_items;
     KSPIN_LOCK work_items_lock;
+    KEVENT work_signal;
+    bus__thread_func * thread;
   };
 
 /* Exports. */
@@ -58,5 +71,6 @@ extern winvblock__lib_func winvblock__bool STDCALL bus__add_child(
   );
 extern winvblock__lib_func struct bus__type * bus__get(struct device__type *);
 extern winvblock__lib_func void bus__process_work_items(struct bus__type *);
+extern winvblock__lib_func NTSTATUS bus__start_thread(struct bus__type *);
 
 #endif  /* _BUS_H */
index 7316e1d..c6ea8b8 100644 (file)
@@ -46,6 +46,7 @@ static device__create_pdo_func bus__create_pdo_;
 static device__dispatch_func bus__power_;
 static device__dispatch_func bus__sys_ctl_;
 static device__pnp_func bus__pnp_dispatch_;
+static bus__thread_func bus__default_thread_;
 
 /* Globals. */
 struct device__irp_mj bus__irp_mj_ = {
@@ -253,15 +254,17 @@ winvblock__lib_func struct bus__type * bus__create(void) {
     /* Populate non-zero device defaults. */
     bus_ptr->device = dev_ptr;
     bus_ptr->prev_free = dev_ptr->ops.free;
+    bus_ptr->thread = bus__default_thread_;
+    KeInitializeSpinLock(&bus_ptr->SpinLock);
+    KeInitializeSpinLock(&bus_ptr->work_items_lock);
+    InitializeListHead(&bus_ptr->work_items);
+    KeInitializeEvent(&bus_ptr->work_signal, SynchronizationEvent, FALSE);
     dev_ptr->ops.create_pdo = bus__create_pdo_;
     dev_ptr->ops.init = bus__init_;
     dev_ptr->ops.free = bus__free_;
     dev_ptr->ext = bus_ptr;
     dev_ptr->irp_mj = &bus__irp_mj_;
     dev_ptr->IsBus = TRUE;
-    KeInitializeSpinLock(&bus_ptr->SpinLock);
-    KeInitializeSpinLock(&bus_ptr->work_items_lock);
-    InitializeListHead(&bus_ptr->work_items);
 
     return bus_ptr;
 
@@ -390,9 +393,6 @@ static winvblock__bool bus__add_work_item_(
     struct bus__type * bus,
     struct bus__work_item_ * work_item
   ) {
-    if (!bus || !work_item)
-      return FALSE;
-
     ExInterlockedInsertTailList(
         &bus->work_items,
         &work_item->list_entry,
@@ -413,9 +413,6 @@ static struct bus__work_item_ * bus__get_work_item_(
   ) {
     PLIST_ENTRY list_entry;
 
-    if (!bus)
-      return NULL;
-
     list_entry = ExInterlockedRemoveHeadList(
         &bus->work_items,
         &bus->work_items_lock
@@ -450,3 +447,100 @@ winvblock__lib_func void bus__process_work_items(struct bus__type * bus) {
       }
     return;
   }
+
+/**
+ * The bus thread wrapper.
+ *
+ * @v context           The thread context.  In our case, it points to
+ *                      the bus that the thread should use in processing.
+ */
+static void STDCALL bus__thread_(IN void *context) {
+    struct bus__type * bus = context;
+
+    if (!bus || !bus->thread) {
+        DBG("No bus or no thread!\n");
+        return;
+      }
+
+    bus->thread(bus);
+    return;
+  }
+
+/**
+ * The default bus thread routine.
+ *
+ * @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 bus__process_work_items() within
+ * its loop.  To start a bus thread, use bus__start_thread()
+ * If you implement your own thread routine, you are also responsible
+ * for freeing the bus.
+ */
+static void STDCALL bus__default_thread_(IN struct bus__type * bus) {
+    LARGE_INTEGER timeout;
+
+    /* Wake up at least every second. */
+    timeout.QuadPart = -10000000LL;
+
+    /* When bus::thread is cleared, we shut down. */
+    while (bus->thread) {
+        DBG("Alive.\n");
+
+        /* Wait for the work signal or the timeout. */
+        KeWaitForSingleObject(
+            &bus->work_signal,
+            Executive,
+            KernelMode,
+            FALSE,
+            &timeout
+          );
+        /* Reset the work signal. */
+        KeResetEvent(&bus->work_signal);
+
+        bus__process_work_items(bus);
+      } /* while bus->alive */
+
+    bus__free_(bus->device);
+    return;
+  }
+
+/**
+ * Start a bus thread.
+ *
+ * @v bus               The bus to start a thread for.
+ * @ret NTSTATUS        The status of the thread creation operation.
+ *
+ * Also see bus__thread_func in the header for details about the prototype
+ * for implementing your own bus thread routine.  You set bus::thread to
+ * specify your own thread routine, then call this function to start it.
+ */
+winvblock__lib_func NTSTATUS bus__start_thread(
+    struct bus__type * bus
+  ) {
+    OBJECT_ATTRIBUTES obj_attrs;
+    HANDLE thread_handle;
+
+    if (!bus) {
+        DBG("No bus specified!\n");
+        return STATUS_INVALID_PARAMETER;
+      }
+
+    InitializeObjectAttributes(
+        &obj_attrs,
+        NULL,
+        OBJ_KERNEL_HANDLE,
+        NULL,
+        NULL
+      );
+    return PsCreateSystemThread(
+        &thread_handle,
+        THREAD_ALL_ACCESS,
+        &obj_attrs,
+        NULL,
+        NULL,
+        bus__thread_,
+        bus
+      );
+  }
index 36c1089..d1eea88 100644 (file)
@@ -194,6 +194,11 @@ static NTSTATUS STDCALL driver__attach_fdo_(
         DBG("IoAttachDeviceToDeviceStack() failed!\n");
         goto err_attach;
       }
+    status = bus__start_thread(bus);
+    if (!NT_SUCCESS(status)) {
+        DBG("Couldn't start bus thread!\n");
+        goto err_thread;
+      }
     /* Ok! */
     KeAcquireSpinLock(&driver__bus_fdo_lock_, &irql);
     if (driver__bus_fdo_) {
@@ -213,6 +218,8 @@ static NTSTATUS STDCALL driver__attach_fdo_(
 
     err_race_failed:
 
+    err_thread:
+
     err_attach:
 
     IoDeleteSymbolicLink(&bus->dos_dev_name);