Previously I wrote about adding power management support to a stream interface driver in Windows CE: Stream Interface Driver Power Management. What I didn’t discuss was that the Power Manager needs to open the driver so that it can call DeviceIoControl() to access the IOCTLs. Of course that isn’t a problem if the driver is written to support multiple opens, but many drivers neither need or support multiple opens. These drivers usually have a flag or counter to restrict the number of open handles.
The following is an implementation of XXX_Open() that supports the Power Manager opening the driver, but restricting its access to the driver. This code is an extension to the code that can be downloaded from Windows CE: Stream Interface Driver Power Management.
The first change is to add a counter, OpenCount, to the context structure. I like a counter because if I later want to support multiple opens the code is already written, but just needs minor change to support. This counter needs to be initialized in XXX_Init() to zero of course.
typedef struct _DRIVERSHELL_CONTEXT
{
                DWORD Instance;
                CEDEVICE_POWER_STATE CurrentPowerState;
                HANDLE hDDKPower;
                DWORD OpenCount;
} DRIVERSHELL_CONTEXT;
Then we need to track the AccessCode and ShareMode values that are passed into XXX_Open(), so add these to the open context structure
typedef struct _OPEN_CONTEXT
{
                DRIVERSHELL_CONTEXT *pHWContext;
                DWORD AccessCode;
                DWORD ShareMode;
} OPEN_CONTEXT;
After reviewing the Power Manager code, I discovered that when it opens the driver by calling CreateFile() it passes a zero value for the AccessCode. An AccessCode value of zero means that it isn’t really requesting to access the read and write functions of the driver, which means that it won’t be accessing the hardware. You could argue that the IoControl function may give access to the hardware, but that is why we are going to save the AccessCode. By saving the AccessCode in the open context, we can check it before giving access to the hardware.
 
The following is the XXX_Open() function with notes:
 
DWORD XXX_Open(DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode)
{
                OPEN_CONTEXT *pOpenContext;
                DRIVERSHELL_CONTEXT pDriverContext = (DRIVERSHELL_CONTEXT *)hDeviceContext;
               
                RETAILMSG( 1, (TEXT("XXX_Open\n")));
Check to see if the driver has already been opened, but don’t worry about it if the AccessCode is zero. Then, allocate an open context structure, code unchanged from the original.
                if(pDriverContext->OpenCount && AccessCode != 0)
                {
                                RETAILMSG(1, (TEXT("DriverShell: Device Already open\r\n")));
                                return FALSE;
                }
               
                pOpenContext = LocalAlloc( LMEM_FIXED, sizeof( OPEN_CONTEXT ));
                if( pOpenContext == NULL )
                {
                                RETAILMSG( 1, (TEXT("XXX_Open failed, unable to allocate open context\n")));
                                return FALSE;
                }
Now initialize the open context by saving the AccessCode and ShareMode for use other places in the driver.
                pOpenContext->AccessCode = AccessCode;
                pOpenContext->ShareMode = ShareMode;
                pOpenContext->pHWContext = pDriverContext;
Now initialize the hardware for use and do other things that need to be done when the driver is opened, but only if the AccessCode is non-zero. Also, now is the time to increment the OpenCount.
                // If the AccessCode == 0 then this is being called from the power manager
                // so the driver isn't going to be used for read/write. In this case,
                // don't reinitialize the driver or hardware
                if( AccessCode != 0 )
                {
                                // Initialize the driver and hardware for use by the application
 
                                // Then count this open
                                pDriverContext-> OpenCount ++;
                }
               
                return (DWORD)pOpenContext;
}
So now we can open the driver when called from the Power Manager and when called from an application.
But, I know that you are poised and ready to add a comment about using the AccessCode flag DEVACCESS_BUSNAMESPACE which you may have seen in COM_MDD2.   That is an option, but only if the device advertises the CE_DRIVER_BUS_GUID. What you may want to do is support both zero and the DEVACCESS_BUSNAMESPACE flag like:
 
                if(pDriverContext->OpenCount && ( AccessCode != 0 && !(AccessCode & DEVACCESS_BUSNAMESPACE) )  )
 
With this and similar changes, the driver supports both the generic stream interface and the bus driver interface.
The next step is to modify the rest of the code to honor the AccessCode appropriately. This will of course vary by specific driver implementation.
Copyright © 2009 – Bruce Eitman
All Rights Reserved