BusEnum: Enabling Finer Control of Device Driver Loading (Windows Embedded CE 6.0)
1/19/2010
Yuqun Cao, SDE, Windows Embedded CE JDP
July 2008
Summary
In this white paper, we present an enhanced version of the bus enumerator driver, also known as BusEnum, which gives better control over when a device driver will be loaded. Traditionally, the built-in drivers are loaded by BusEnum in a single thread. The enhanced BusEnum driver allows for deferred loading of some nonessential drivers by using a simple registry configuration. With this enhanced capability, the device can be up-and-ready for user interaction sooner, while the remaining drivers are being loaded in the background. This deferred loading feature can be used to speed up device boot time. It is especially effective if some drivers spend a long time in the driver Init() function. The enhanced BusEnum driver presented in this white paper has already been used on a production device to improve the device boot time.
BusEnum2 Download Site BusEnum2.zip: Windows Embedded CE 6.0 R2 BusEnum2 Sample code
Applies To
Windows CE 5.0, Windows Embedded CE 6.0, Windows Embedded CE 6.0 R2
Example Scenario
Architecture
Driver Loading Registry Keys
BusEnum Driver Modifications
BUSENUM2\inc\defbus2.h
BUSENUM2\BUSDEF\defbus.cpp
Building the Sample BusEnum2 Driver
Platform.reg
Introduction
The bus enumerator, BusEnum.dll, initializes the process of scanning the registry under the Device Manager RootKey and looking for additional buses and devices to be loaded at boot time. Within a single thread, it enumerates the device under the internal system bus, or root bus. The boot process will not enter the next phase until after it finishes enumerating all the drivers under the root bus. The boot process could take a long time initializing all the drivers before the device is ready for use. This is especially true if some drivers spend a long time in the driver Init() function.
There are several possible solutions that you can implement to shorten the boot time for a Windows Embedded CE powered device. As one method, you can delay the loading of some drivers, services, and applications. When you use this method, the device can be ready for user interaction sooner.
The enhanced BusEnum driver allows for more precise control over when to load a driver during the bus-enumeration process. The enhanced BusEnum driver supports asynchronous driver-loading. It spawns threads to load drivers that are configured to load asynchronously so that the main enumeration thread can continue. It also has a way to synchronize the driver loading threads, in case there is a dependency between the drivers that it launches. Drivers can also be grouped together under a bus key and be loaded by another instance of BusEnum. The configuration of drivers to be loaded by BusEnum exists in the registry. This is flexible for a broad range of customers.
The existing BusEnum driver is enhanced to support a new DEVFLAGS_LOAD_ASYNC Flag bit and two optional driver-loading registry subkeys, LoadAsyncEvent and LoadAsyncDelay. DEVFLAGS_LOAD_ASYNC is used to control the driver asynchronous loading. LoadAsyncEvent and LoadAsyncDelay are for loading synchronization. We will describe the code changes in the following sections of this white paper. The modified BusEnum driver sample code BusEnum2 is available for download from Code Gallery.
For more information about the device-driver loading process and the BusEnum driver, see the following topics available at MSDN Library:
ActivateDeviceEx (with descriptions of Driver load flags)
Example Scenario
We will use the following scenario throughout out this article:
Let's assume that we have drivers A, B, C, D, E, F, and G to be loaded. Driver A and B are very important and have to be loaded first. We can start some applications or services after A and B are loaded. Driver C and D does not depend on any other drivers and can be loaded at any time in any order. Driver E, F, and G can be loaded later, but have to be loaded in this order: E-F-G.
Architecture
Currently in Windows CE 5.0, Windows Embedded CE 6.0, and Windows Embedded CE 6.0 R2, all the built-in drivers are loaded by BusEnum.dll in a single thread (Fig. 1). These drivers are loaded synchronously. Drivers A, B, C are loaded in the main enumeration thread of the root bus enumerator.
The enhanced BusEnum driver will spawn a new asynchronous thread to load a driver if the newly defined DEVFLAGS_LOAD_ASYNC bit is set for the driver. The main enumeration thread will continue after spawning the new asynchronous thread. The asynchronous thread might delay or wait for an event before loading the driver (Fig. 2). Drivers A and B are loaded in the main enumeration thread. Driver D is loaded in a different thread.
Starting all the driver-loading threads at the same time is not going to help us much. We want to have better control over when the driver is loaded by the asynchronous thread. Therefore, two optional driver-loading registry keys are introduced: LoadAsyncEvent and LoadAsyncDelay. They will be covered in more detail in the "Driver Loading Registry Keys" section later in this white paper.
BusEnum can load other bus drivers to enumerate other buses (for example, PCI, USB, PCCard, SD, etc.). We can have BusEnum load itself with a different bus name. We can also use a second instance of BusEnum to perform the asynchronous driver loading. This will enable us to control the loading of a group of drivers by controlling the loading of the second instance of BusEnum. This is illustrated in Fig. 3.
Drivers A, B, C and the second instance of BusEnum are loaded in the main enumeration thread of the root bus enumerator. Drivers E and F are in another group and are loaded under the AsyncBus key in the enumerator thread of the second instance of BusEnum. The driver-loading scheme can become complicated and can go many levels deep. However, to improve boot time, two levels of BusEnum are usually enough.
To enable the loading of drivers in groups, the enhanced BusEnum driver introduces the concept of a driver-loading group. Each group is controlled by an instance of the BusEnum. So, instead of controlling each driver loading by using the registry key, we can control the loading of a group of drivers by controlling the time to load the BusEnum instance. The load order of the drivers in a group is still controlled by the “order” key. A group can contain another group, which produces the hierarchical bus tree.
This kind of configuration is used for boot-time improvement. The non-boot essential drivers are organized in an AsyncBus group. They are loaded by the second instance of BusEnum later, after the essential drivers are loaded by the root bus enumerator and the services by the Services Enumerator. You can learn more about this concept by studying the sample registry configuration described in the "Driver Loading Registry Keys" section later in this white paper.
The BusEnum driver loading process is configured by using the registry.
Driver Loading Registry Keys
1. New Flags registry subkey bit: DEVFLAGS_LOAD_ASYNC (0x01000000)
As the root bus enumerator initializes the system, it traverses the subkeys of HKEY_LOCAL_MACHINE\Drivers\BuiltIn or wherever the HKEY_LOCAL_MACHINE\Drivers\RootKey value points. The BusEnum traverses the subkeys one by one, initializing a driver for each subkey. BusEnum examines the first level of keys underneath the key passed to it, according to the Order registry subkey. It invokes ActivateDeviceEx on each subkey that it finds.
The Flags registry subkey is a set of bits that modify the behavior of ActivateDeviceEx.
The Flags subkey is optional, and its value is set to zero if there is no Flags key present. Bits 3 through 23 are reserved and must be set to zero. The driver being loaded can use bits 24 through 31 of the Flags value. The currently supported bits are defined in %_WINCEROOT%\Public\Common\DDK\Inc\Devload.h.
For the enhanced BusEnum driver, we defined a new Flags bit, DEVFLAGS_LOAD_ASYNC (0x01000000), to indicate that the driver loads in a different asynchronous thread, instead of the main enumeration thread. A new thread will be spawned to actually load the driver by calling ActivateDeviceEx. The main thread will be able to continue without waiting for the driver loading to finish.
The following registry example shows how to use the DEVFLAGS_LOAD_ASYNC Flags bit for loading drivers C and D in two threads:
; BusEnum spawns a thread to load the driver.
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\C]
“Dll”=”C.dll”
“Order”=dword:10
“Flags”= dword: DEVFLAGS_LOAD_ASYNC ; load in async thread
; BusEnum spawns a thread to load the driver.
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AsyncBus\D]
“Dll”=”D.dll”
“Order”=dword:10
“Flags”= dword: DEVFLAGS_LOAD_ASYNC ; load in async thread
Now, we can load drivers in parallel. This is good, but we want to have better control on the thread start time. To get this control, we can use two optional subkeys: LoadAsyncEvent and LoadAsyncDelay.
2. New thread-controlling subkeys
We define two new subkeys for you to use in order to achieve finer control of the asynchronous driver-loading thread. They are optional and are only used if DEVFLAGS_LOAD_ASYNC Flags bit is set.
Subkey | Type | Description |
---|---|---|
LoadAsyncDelay |
DWORD |
Indicates the additional delay in milliseconds before the asynchronous thread starts loading the driver. |
LoadAsyncEvent |
SZ |
Specifies the name of the start event for driver loading. The asynchronous thread will wait for this named event before it starts driver loading. The event can be signaled from an application when the driver is needed. If both LoadAsyncDelay and LoadAsyncEvent are present, LoadAsyncDelay is treated as the wait-event time out. |
Now, we can modify the drivers C and D registry keys to have some delay or to wait for a starting event, as follows:
; BusEnum spawns a thread to load the driver.
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\C]
“Dll”=”C.dll”
“Order”=dword:10
“Flags”= dword: DEVFLAGS_LOAD_ASYNC ; load in async thread
"LoadAsyncDelay"=dword:2710 ; delay 10000 ms
; BusEnum spawns a thread to load the driver.
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\D]
“Dll”=”D.dll”
“Order”=dword:10
“Flags”= dword: DEVFLAGS_LOAD_ASYNC ; load in async thread
"LoadAsyncEvent"="StartD" ; waiting for the "StartD" event
By using this modification, driver C will be loaded 10 seconds after BusEnum starts the asynchronous loading thread for driver C. BusEnum will start another thread which waits for the "StartD" name event before it loads driver D. However, the BusEnum main thread is not blocked, and the enumeration continues for the next driver based on the Order subkey.
Now, we can control exactly when to start loading the drivers. For a group of drivers that must be loaded in certain order (for example, E-F-G), we can have different events for each driver and signal the events in the sequence. We have a better way to do this. Because the bus enumerator supports hierarchical usage, it can load itself over a different registry key. We can group the drivers under a different bus key and use the second instance of BusEnum to load the group asynchronously. See step 3 for more information about how to do this.
3. Second instance of BusEnum
The following is an example of how to use the second instance of BusEnum to load a group of drivers (E-F-G). The second instance of BusEnum is loaded asynchronously by the root bus enumerator (first instance of BusEnum). Then, drivers E, F, and G are loaded in a single thread in the second instance of BusEnum. The load order is observed within the group.
; Root bus key
[HKEY_LOCAL_MACHINE\Drivers]
“RootKey”=”Drivers\\BuiltIn”; for the root bus enumerator
; Root bus enumerator. First instance of BusEnum.dll, for driver under "BuiltIn."
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn]
“Dll”=”BusEnum.dll”
“BusName”=”BuiltIn”
“Flags”=dword: DEVFLAGS_NAKEDENTRIES
; to be loaded by BusEnum for BuiltIn
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\A]
“Dll”=”A.dll”
“Order”=dword:1
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\B]
“Dll”=”B.dll”
“Order”=dword:2
; BusEnum spawns a thread to load the driver.
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\D]
“Dll”=”D.dll”
“Order”=dword:3
“Flags”= dword: DEVFLAGS_LOAD_ASYNC ; load in async thread
"LoadAsyncEvent"="StartD" ; waiting for the "StartD" event
; Second instance of BusEnum.dll, for drivers under "BuiltIn\AsyncBus" subkey.
; Start in a separate thread. Waiting for "Start" event to continue.
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AsyncBus]
“Dll”=”BusEnum.dll”
“BusName”=”AsyncBus”
“Order”=dword:10
“Flags”=dword: DEVFLAGS_NAKEDENTRIES |DEVFLAGS_LOAD_ASYNC ; Async
"LoadAsyncEvent"="StartG" ; waiting for the "StartG" event
"LoadAsyncDelay"=dword:2710 ; event time out after 10 seconds
; To be loaded by second instance of BusEnum for BuiltIn\AsyncBus bus.
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AsyncBus\E]
“Dll”=”E.dll”
“Order”=dword:5
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AsyncBus\F]
“Dll”=”F.dll”
“Order”=dword:6
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AsyncBus\G]
“Dll”=”G.dll”
“Order”=dword:7
The following describes the order in which items are loaded during the boot process:
- BuiltIn\BusEnum.dll; Device Manager loads root bus enumerator (first instance).
- BuiltIn\A — driver A starts, load Order = 1.
- BuiltIn\B — driver B starts, load Order = 2.
- BuiltIn\D — driver D load Order = 3, waits for event.
- BuiltIn\AsynBus — second instance of BusEnum load Order = 10, waits for event.
- Root bus enumerator main thread returns.
- Boot process continues.
- "StartG" event signaled (by application, or event time out).
- The second instance of BusEnum starts.
- BuildIn\AsynBus\E — driver E starts, load Order = 5 within AsynBus group.
- BuildIn\AsynBus\F — driver F starts, load Order = 6 within AsynBus group.
- BuildIn\AsynBus\G — driver G starts, load Order = 7 within AsynBus group.
- The second instance BusEnum main thread returns.
- Boot process continues.
- "StartD" event is signaled by application.
- Driver D starts.
- Boot process continues.
In order to achieve the fine control of driver loading described thus far, we have to make some simple changes to the default bus enumerator BusEnum. These modifications are described in the following section of this white paper.
BusEnum Driver Modifications
The default bus enumerator sample source code is available in Platform Builder at %_WINCEROOT%\Public\Common\OAK\DRIVERS\BUSENUM\.
It has been modified in the new enhanced driver, BusEnum2, to support the enhanced features described in the previous sections. We recommend that you copy the BusEnum2 driver source code from Code Gallery to the Platform directory, under the BSP drivers folder. The key changes will be explained as follows (the complete source code can be found in the BusEnum2 sample driver).
BUSENUM2\inc\defbus2.h
This header file is based on %_WINCEROOT%\Public\Common\OAK\inc\defbus.h.
In this file, we defined the new Flags bit and registry key names and types, as follows:
#ifdef BUSENUM2
// Should be in devload.h
#define DEVFLAGS_LOAD_ASYNC 0x1000000 // Asynchronous loading flag, bit-24
// Event name for async loading (optional)
#define DEVLOAD_LOAD_ASYNC_EVENT_VALNAME TEXT("LoadAsyncEvent")
#define DEVLOAD_LOAD_ASYNC_EVENT_VALTYPE REG_SZ
// Async Event time out/delay in MSec (optional)
#define DEVLOAD_LOAD_ASYNC_EVENT_TIMEOUT_VALNAME TEXT("LoadAsyncDelay")
#define DEVLOAD_LOAD_ASYNC_EVENT_TIMEOUT_VALTYPE REG_DWORD
#endif
The following new code is added to the end of the class DeviceFolder declaration:
#ifdef BUSENUM2
// Unless it is in the public\common\oak\inc\defbus.h, this must be added to
// the end of the DeviceFolder class declaration in order to be compatible to
// the libraries/components built with defbus.h.
private:
HANDLE m_hAsyncLoadThread;
HANDLE m_hAsyncLoadStartEvent;
BOOL m_PendingLoadAsync;
static DWORD AsyncLoadThread(DeviceFolder *pData)
{
return pData->LoadDeviceAsync();
};
public:
virtual BOOL LoadDeviceAsync();
#endif
BUSENUM2\BUSDEF\defbus.cpp
This source code is based on %_WINCEROOT%\Public\Common\OAK\drivers\BUSENUM\BUSDEF\defbus.cpp.
The modification provided is for the ActivateDeviceEx function, located within DeviceFolder::LoadDevice().
This modification checks for the DEVFLAGS_LOAD_ASYNC bit in the Flags registry key of the driver. If this bit is set in Flags, it will start a new thread to load the driver.
#ifdef BUSENUM2
DEBUGMSG(DBG_DEVICE_ENUM,(TEXT("DeviceFolder::LoadDevice(%s): Flags=0x%x\r\n"), m_lpTemplateRegPath, GetLoadFlag()));
// Check for async loading flag bit and start async thread if set
if(GetLoadFlag() & DEVFLAGS_LOAD_ASYNC)
{
m_hAsyncLoadThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) AsyncLoadThread, this, CREATE_SUSPENDED, NULL);
if(m_hAsyncLoadThread)
{
Lock();
m_PendingLoadAsync = TRUE;
Unlock();
ResumeThread(m_hAsyncLoadThread);
return TRUE;
}
// Failed creating the async loading thread for this driver.
return FALSE;
}
#endif
The thread function DeviceFolder::LoadDeviceAsync() checks for the optional event name and delay/time-out value. It waits for the event, or the necessary delay, before it loads the driver, as follows:
#ifdef BUSENUM2
BOOL DeviceFolder::LoadDeviceAsync()
{
WCHAR szEventName[MAX_PATH] = {};
DWORD dwEventTimeout = 0;
if (!GetRegValue(DEVLOAD_LOAD_ASYNC_EVENT_TIMEOUT_VALNAME,(LPBYTE)&dwEventTimeout,sizeof(dwEventTimeout)))
{
// No delay/time-out option set.
dwEventTimeout = INFINITE;
}
if (GetRegValue(DEVLOAD_LOAD_ASYNC_EVENT_VALNAME,(PUCHAR)szEventName,sizeof(szEventName)))
{
m_hAsyncLoadStartEvent = CreateEvent(NULL, TRUE, FALSE, szEventName);
if(m_hAsyncLoadStartEvent && dwEventTimeout)
{
// Wait for event or time out
if(WAIT_FAILED == WaitForSingleObject(m_hAsyncLoadStartEvent, dwEventTimeout))
{
return FALSE;
}
}
}
else
{
// No event time-out option is set. Will start loading after
// delay.
if(dwEventTimeout && (dwEventTimeout != INFINITE))
{
Sleep(dwEventTimeout);
}
}
// Time to load the driver.
m_hDevice = ActivateDeviceEx(m_lpTemplateRegPath, m_dwInitRegArray, (m_dwInitRegArray!=NULL?m_dwInitRegCount:0) , NULL);
Lock();
m_PendingLoadAsync = FALSE;
m_fDriverLoaded = (m_hDevice!=NULL);
Unlock();
return m_fDriverLoaded;
}
#endif
Building the Sample BusEnum2 Driver
The complete enhanced BusEnum Windows Embedded CE 6.0 sample source code is available in BusEnum2.zip.
To build the sample driver:
- Download the .zip file.
- Unzip the file, and then copy the whole folder BusEnum2 to the BSP Drivers folder, next to the other drivers.
- Update the Drivers DIRS file to include BusEnum2 during the build process. In a Platform Builder build window, run the build command in the new BusEnum2 directory to build the BusEnum2 sample driver.
The DLL name is BusEnum2.dll. It can be used to replace the original bus enumerator BusEnum in a run-time image. To replace the original bus enumerator, you should rename BusEnum2.dll to "busenum.dll", to overwrite the original driver. The next section describes how to configure a driver for asynchronous loading by the new BusEnum driver.
Platform.reg
The following describes how to move a particular driver to the AsyncBus group for deferred loading.
This procedure assumes that the driver already has the following registry settings before you modify platform.reg:
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Sample]
“Dll”=”sample.dll”
“Order”=dword:10
. . .
To move a driver to the AsyncBus group for deferred loading:
Copy the registry settings to your platform.reg file.
; put a “-“ sign in front of the key to delete previous key [-HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Sample] ; new key for AyncBus [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AyncBus\Sample] “Dll”=”sample.dll” “Order”=dword:10 . . .
At the end of the registry code, add the second instance of the BusEnum driver, as follows:
; Second instance of BusEnum.dll, for drivers under "BuiltIn\AsyncBus" subkey. ; Start in a separate thread. Waiting for "Start" event to continue. [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AsyncBus] “Dll”=”BusEnum.dll” “BusName”=”AsyncBus” “Order”=dword:10 “Flags”=dword: DEVFLAGS_NAKEDENTRIES |DEVFLAGS_LOAD_ASYNC ; Async "LoadAsyncEvent"="Start" ; waiting for the "Start" event "LoadAsyncDelay"=dword:2710 ; event time out after 10 seconds
The following are the CeLog results from a production device that is based on Windows CE 5.0. It shows the driver-loading sequence before the program uses the asynchronous group-loading method, and after the program uses the asynchronous group-loading method. As you can see in the CeLog time stamps, the applications (for example, "explorer.exe") are started much sooner in the second case; the loading of some drivers is delayed.
The following is the result of CeLog output before the program uses asynchronous group loading:
. . .
0:00:00.907.900 : filesys.exe: Launching device.exe
0:00:00.968.000 : device.exe: Activating Drivers\BuiltIn
0:00:01.010.685 : device.exe: Activating Drivers\BuiltIn\notify
0:00:01.036.533 : device.exe: Activating Drivers\BuiltIn\Battery
0:00:01.045.789 : device.exe: Activating Drivers\BuiltIn\Serial
0:00:01.154.647 : device.exe: Activating Drivers\BuiltIn\TMC
0:00:01.167.472 : device.exe: Activating Drivers\BuiltIn\GPS
0:00:01.179.450 : device.exe: Activating Drivers\BuiltIn\I2C
0:00:01.188.550 : device.exe: Activating Drivers\BuiltIn\PButton
0:00:01.212.648 : device.exe: Activating Drivers\BuiltIn\Audio
0:00:01.220.068 : device.exe: Activating Drivers\BuiltIn\NDIS
0:00:01.799.231 : device.exe: Activating Drivers\BuiltIn\TAPI
0:00:01.825.711 : device.exe: Activating Drivers\BuiltIn\WAPIMAN
0:00:01.867.276 : device.exe: Activating Drivers\BuiltIn\SIP
0:00:01.891.484 : device.exe: Activating Drivers\BuiltIn\FlashDisk
0:00:03.297.054 : device.exe: Activating Drivers\BuiltIn\ipsecsvc
0:00:03.980.234 : device.exe: Activating Drivers\BuiltIn\BCHS
0:00:03.988.243 : device.exe: Activating Drivers\BuiltIn\SDBusDriver
0:00:04.010.068 : device.exe: Activating Drivers\BuiltIn\SDHC
0:00:04.025.374 : device.exe: Activating Drivers\BuiltIn\TIMESVC
0:00:04.035.565 : device.exe: Activating Drivers\BuiltIn\WaveDev
0:00:04.036.316 : device.exe: Activating Drivers\BuiltIn\VSPD0
0:00:04.062.240 : device.exe: Activating Drivers\BuiltIn\VSPD1
0:00:04.066.477 : device.exe: Activating Drivers\BuiltIn\VSPD2
0:00:04.070.703 : device.exe: Activating Drivers\BuiltIn\VSPD3
0:00:04.075.148 : device.exe: Activating Drivers\BuiltIn\VSPD5
0:00:04.079.291 : device.exe: Activating Drivers\BuiltIn\VSPD4
0:00:04.083.223 : device.exe: Finished initializing
0:00:04.149.660 : filesys.exe: Launching gwes.exe
0:00:04.695.440 : filesys.exe: Launching explorer.exe <= starts after all drivers are loaded
. . .
The following is the result of CeLog output after the program uses asynchronous group loading:
. . .
0:00:00.907.415 : filesys.exe: Launching device.exe
0:00:00.968.432 : device.exe: Activating Drivers\BuiltIn
0:00:01.000.624 : device.exe: Activating Drivers\BuiltIn\I2C
0:00:01.009.135 : device.exe: Activating Drivers\BuiltIn\Audio
0:00:01.016.597 : device.exe: Activating Drivers\BuiltIn\WAPIMAN
0:00:01.054.390 : device.exe: Activating Drivers\BuiltIn\SIP
0:00:01.958.448 : device.exe: Activating Drivers\BuiltIn\WaveDev
0:00:01.958.822 : device.exe: Finished initializing
0:00:01.959.961 : filesys.exe: Launching gwes.exe
0:00:02.497.157 : filesys.exe: Launching explorer.exe <= starts before drivers in AsynBus group
0:00:02.656.141 : filesys.exe: Launching btp_notification_ui.exe
0:00:03.576.325 : filesys.exe: Launching AsyncGroup.exe
0:00:03.620.883 : filesys.exe: PSLs notified of system started
0:00:03.925.578 : device.exe: Activating Drivers\BuiltIn\AsyncBus
0:00:03.940.267 : device.exe: Activating Drivers\BuiltIn\AsyncBus\notify
0:00:03.961.098 : device.exe: Activating Drivers\BuiltIn\AsyncBus\Battery
0:00:03.971.371 : device.exe: Activating Drivers\BuiltIn\AsyncBus\Serial
0:00:04.083.334 : device.exe: Activating Drivers\BuiltIn\AsyncBus\TMC
0:00:04.096.942 : device.exe: Activating Drivers\BuiltIn\AsyncBus\GPS
0:00:04.108.012 : device.exe: Activating Drivers\BuiltIn\AsyncBus\PButton
0:00:04.127.735 : device.exe: Activating Drivers\BuiltIn\AsyncBus\NDIS
0:00:04.134.236 : device.exe: Activating Drivers\BuiltIn\AsyncBus\TAPI
0:00:04.160.482 : device.exe: Activating Drivers\BuiltIn\AsyncBus\FlashDisk
0:00:04.884.722 : device.exe: Activating Drivers\BuiltIn\AsyncBus\ipsecsvc
0:00:05.596.738 : device.exe: Activating Drivers\BuiltIn\AsyncBus\BCHS
0:00:05.616.669 : device.exe: Activating Drivers\BuiltIn\AsyncBus\SDBusDriver
0:00:05.868.429 : device.exe: Activating Drivers\BuiltIn\AsyncBus\SDHC
0:00:05.884.178 : device.exe: Activating Drivers\BuiltIn\AsyncBus\TIMESVC
0:00:05.894.704 : device.exe: Activating Drivers\BuiltIn\AsyncBus\VSPD0
0:00:05.921.138 : device.exe: Activating Drivers\BuiltIn\AsyncBus\VSPD1
0:00:05.925.791 : device.exe: Activating Drivers\BuiltIn\AsyncBus\VSPD2
0:00:05.930.127 : device.exe: Activating Drivers\BuiltIn\AsyncBus\VSPD3
0:00:05.934.492 : device.exe: Activating Drivers\BuiltIn\AsyncBus\VSPD5
0:00:05.938.756 : device.exe: Activating Drivers\BuiltIn\AsyncBus\VSPD4
. . .
In the first case, the CeLog shows that explorer.exe starts at 0:00:04.695.440, after all drivers are loaded and GWES.exe starts. In the second case, explorer.exe starts at 0:00:02.497.157, before the drivers in the AsyncBus group are loaded.
Conclusion
The change made to the existing public sample bus enumerator driver, BusEnum, is very simple and straightforward. It is completely backward compatible with the current BusEnum driver. Although the change is simple, it gives the much-needed flexibility of device-driver loading at boot time through the registry configuration. A device driver can still be enumerated as usual in the main thread. However, if the load flag DEVFLAGS_LOAD_ASYNC bit is set for the driver, the BusEnum will spawn a new thread to load the driver asynchronously. Additionally, if the optional delay or start-event registry key is present, the new load thread will wait for the delay or start event before actually loading the driver.
A second instance of the BusEnum can be loaded under the root bus with a different bus name. This new bus name will be a subkey under the Root-bus registry key. This new bus is known as the "sub-bus" because it is under the root-bus hierarchy, and its registry key is known as the "sub-bus key". The driver entries under this sub-bus key will be loaded by the second instance of BusEnum. This enables us to control the loading of the drivers as a group. The concept can be extended to have a hierarchical tree of "buses" in the registry.
The new BusEnum was used in a customer device to replace the default bus enumerator supplied with Platform Builder for Windows CE 5.0. It moves the nonessential device-driver loading to a later time. As a result, the application user interface (UI) can start earlier. It achieves the customer's cold-boot requirement of under 5 seconds, measured from power-on to UI-screen ready.
You could implement an optional event LoadAsyncStarted to signal the completion of loading a driver. The event is signaled after BusEnum successfully returns from the ActivateDeviceEx() function call to load a driver. An application can wait for this event before accessing the driver. There are other methods to check for the driver availability before accessing. So, this event is optional and not implemented in the sample code.
In Windows Embedded CE 6.0 and Windows Embedded CE 6.0 R2, there is a service enumerator, ServiceEnum (ServiceEnum.dll), under the Public directory, at %_WINCEROOT%\Public\Common\OAK\drivers\BUSENUM\ServiceEnum\. If you want to apply a similar improvement for achieving finer control of starting services in Windows Embedded CE 6.0, you might consider doing the work to apply modifications similar to those that are described in this article to ServiceEnum.