Filter device drivers are a new feature in Windows Embedded Compact 7. Currently the documentat...
Filter device drivers are a new feature in Windows Embedded Compact 7. Filter device drivers are optional and basically insert themselves “in front” of a device driver to filter I/O requests to this device driver. The documentation in Windows Embedded Compact resembles the WDM documentation; however there is little comparison to the desktop model. Filter device drivers can relate to bus device drivers, kernel mode device drivers, user mode device drivers or device drivers loaded by GWES.
A useful example of such a filter device driver would be a finite impulse response filter driver for some sampling low level stream device driver. The best way to understand how to develop such a filter device driver would be to understand how Device Manager handles its loading, and how it relates to the device driver which I/O it filters.
Unlike the desktop where filter drivers are inserted into the driver stack, in Windows Embedded Compact, Device Manager has a new component: Filter manager. Filter manager handles a list of filter drivers chained together by registry settings. When you want to relate a filter driver to some stream device driver, or any device driver for that matter, you have to add a “Filter” key to the device driver’s registry setting and the value assigned to it is a GUID relating to the filter driver. listing 1 shows the registry settings for a device driver that supports a filter driver. The filter driver registry settings entry for the filter device driver for that device driver is shown in listing 2.
; TesTdrvr driver
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Testdrvr]
"Prefix"="TST"
"DLL"="Testdrvr.DLL"
"Order"=dword:0
"DisplayName"="TesTdrvr driver"
"Flags"=dword:0
"Filter"="{fbf9a438-f6c8-46dc-a278-df2900638f3f}"
Listing 1 Registry settings entry for a sample stream device driver supporting a filter driver
[HKEY_LOCAL_MACHINE\Drivers\Filters\{fbf9a438-f6c8-46dc-a278-df2900638f3f}\FIRFilter]
"DLL"="Firfilterdrvr.dll"
"InitEntry"="FIRFilterInit"
; You may want to change the loading order as this is a temporary value
; Add this line to the registry settings of device driver being filtered
;"Filter"=”{fbf9a438-f6c8-46dc-a278-df2900638f3f}”
Listing 2 Registry settings entry for a sample filter driver
The stream device driver is a vanilla stream device driver, with the addition of a new IOCTL code to help demonstrate the filtering process. The filter driver was created using the Windows CE Device Driver Wizard hence the last two lines of the listing. Figure 2 shows how to create a filter driver using the Windows CE Device Driver Wizard.
Device Manager starts its device driver loading process and BusEnum loads Testdrvr.dll by calling ActivateDeviceEx. In essence Device Manager starts the process of loading the stream device driver within an internal function LoadLib. It loads first Testdrvr.dll and assigns all of its entry points and goes on to load the related filter driver. Once the filter driver dll is loaded it calls the function pointed to by the “InitEntry” key in the registry settings of the filter driver. And really both drivers are initially loaded. Figure 1 shows the call stack of the loading process. Now Device Manager calls the Init entry point and this is where it becomes interesting because both device drivers are chained and the filter driver Init entry point is called first and if it does what it does to initialize the filter driver and would not trigger the call to TST_Init Device Manager will not call it by itself. See listing 5 for an example of a FilterInit function.
Figure 1 Stream device driver loading and the related filter driver loading and initializing
The filter driver provides a FIR filter (really very rudimentary for the example) that will handle sampling data from the stream device driver. The filter driver is implemented as a C++ class derived from DriverFilterBase class which Microsoft provides in C:\WINCE700\public\common\ddk\inc\drfilter.h. listing 3 shows a jumpstart filter driver generated by the device driver wizard.
class FIRFilter : public DriverFilterBase
{
protected:
// Value returned from xxx_Init() DWORD m_initReturn;
// Value returned from xxx_Init()
DWORD m_initReturn;
// The pointer to the device driver that this filters PDRIVER_FILTER m_pNext;
// The pointer to the device driver that this filters
PDRIVER_FILTER m_pNext;
public:
FIRFilter(LPCTSTR lpcFilterRegistryPath, LPCTSTR lpcDriverRegistryPath, PDRIVER_FILTER pNextFilterParam); ~FIRFilter();
FIRFilter(LPCTSTR lpcFilterRegistryPath,
LPCTSTR lpcDriverRegistryPath,
PDRIVER_FILTER pNextFilterParam);
~FIRFilter();
DWORD FilterInit(DWORD dwContext,LPVOID lpParam);
BOOL FilterPreDeinit(DWORD dwContext); BOOL FilterDeinit(DWORD dwContext); DWORD FilterOpen(DWORD dwContext, DWORD AccessCode, DWORD ShareMode); BOOL FilterPreClose(DWORD dwOpenCont); BOOL FilterClose(DWORD dwOpenCont); BOOL FilterControl(DWORD dwOpenCont,DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut, HANDLE hAsyncRef); void FilterPowerdn(DWORD dwConext); void FilterPowerup(DWORD dwConext); DWORD FilterRead(DWORD dwOpenCont, LPVOID pBuffer, DWORD Count); DWORD FilterWrite(DWORD dwOpenCont, LPCVOID pSourceBytes, DWORD NumberOfBytes); DWORD FilterSeek(DWORD dwOpenCont, long Amount, DWORD Type); BOOL FilterCancelIo(DWORD dwOpenCont, HANDLE hAsyncHandle);
BOOL FilterPreDeinit(DWORD dwContext);
BOOL FilterDeinit(DWORD dwContext);
DWORD FilterOpen(DWORD dwContext, DWORD AccessCode, DWORD ShareMode);
BOOL FilterPreClose(DWORD dwOpenCont);
BOOL FilterClose(DWORD dwOpenCont);
BOOL FilterControl(DWORD dwOpenCont,DWORD dwCode, PBYTE pBufIn,
DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
PDWORD pdwActualOut, HANDLE hAsyncRef);
void FilterPowerdn(DWORD dwConext);
void FilterPowerup(DWORD dwConext);
DWORD FilterRead(DWORD dwOpenCont, LPVOID pBuffer, DWORD Count);
DWORD FilterWrite(DWORD dwOpenCont, LPCVOID pSourceBytes,
DWORD NumberOfBytes);
DWORD FilterSeek(DWORD dwOpenCont, long Amount, DWORD Type);
BOOL FilterCancelIo(DWORD dwOpenCont, HANDLE hAsyncHandle);
};
Listing 3 An example filter driver class
Figure 2 Using Windows CE Device Driver Wizard to create a filter driver
A simplified basic implementation of the Init entry point which is the only function exposed by the filter driver is seen in listing 4. All it does is create a new instance of the FirFilter class saves the pointer to the next filter which is really a pointer to our stream device driver, and returns the pointer to the filter driver object.
extern "C" PDRIVER_FILTER FIRFilterInit(LPCTSTR lpcFilterRegistryPath, LPCTSTR lpcDriverRegistryPath, PDRIVER_FILTER pNextFilter)
FIRFilter* pFIRFilter = NULL;
InitializeCriticalSection(&g_CriticalSection); DEBUGMSG(ZONE_INIT, (L"FIRFilter: Creating new filter driver for <%s>\r\n", lpcDriverRegistryPath)); EnterCriticalSection(&g_CriticalSection);
InitializeCriticalSection(&g_CriticalSection);
DEBUGMSG(ZONE_INIT, (L"FIRFilter: Creating new filter driver for <%s>\r\n", lpcDriverRegistryPath));
EnterCriticalSection(&g_CriticalSection);
pFIRFilter = new FIRFilter(lpcFilterRegistryPath, lpcDriverRegistryPath,pNextFilter); if (!pFIRFilter) { DEBUGMSG(ZONE_ERROR, (L"FIRFilter: Error, Unable to allocate memory. %s cannot be initialized\r\n", lpcDriverRegistryPath)); goto done; }
pFIRFilter = new FIRFilter(lpcFilterRegistryPath, lpcDriverRegistryPath,pNextFilter);
if (!pFIRFilter)
DEBUGMSG(ZONE_ERROR,
(L"FIRFilter: Error, Unable to allocate memory. %s cannot be initialized\r\n",
lpcDriverRegistryPath));
goto done;
}
done:
pFIRFilter->pNextFilter = pNextFilter; LeaveCriticalSection(&g_CriticalSection); return (PDRIVER_FILTER)pFIRFilter;
pFIRFilter->pNextFilter = pNextFilter;
LeaveCriticalSection(&g_CriticalSection);
return (PDRIVER_FILTER)pFIRFilter;
Listing 4 A sample Init entry to the filter driver.
Following in listing 5 is an example of FIRFilter::FilterInit entry point implementation that uses the pointer to the stream device driver that we saved to call into its Init entry point, in this case TST_Init.
DWORD FIRFilter::FilterInit(DWORD dwContext,LPVOID lpParam)
DWORD dwRet = 0;
m_initReturn = pNextFilter->fnInit(dwContext, lpParam, this);
return m_initReturn;
Listing 5 A sample FilterInit entry point
You can similarly implement FIRFilter::FilterControl function to forward the call to TST_IOControl and compute the appropriate filter on the data that returns from it and pass back to the caller the filtered data. This really is all there is to it.
While it is true that using hardware to filter data provides much better performance, you may find that sometimes this feature may be very useful. Have a go at it and try it out for yourself.