Every windows embedded developer knows that Windows CE is used for customized devices and some requirements demanded from the customers to restrict certain USB devices based on the Vendor ID (VID) or Product ID(PID). Device has to respond only to their USB accessories, for example, Customer has designed their own USB HID device accessories for their devices and expected to respond only for their accessories. Even with their accessories they are introducing special featured accessories and expecting the device (application) to act accordingly for each accessory dynamically. So we need to take some decision on the application which requires some unique ID(VID/PID) to identify the connected accessory.
When a USB device is plugged or unplugged, Windows CE doesn’t not support applications to retrieve the Vendor ID and Product ID of USB devices directly using through APIs or WinProc Messages like PC. At the maximum we can retrieve the driver prefixes for the plugged/unplugged devices through some APIs.
Since Windows CE is a customizable OS, we can play around the USB Host driver to support this feature up to an extend (Our case we need VID/PID and interface protocol information of HID devices).We planned the same way to retrieve the VID/PID information like PC application through WinProc Messages to avoid complexity. Let us see the interesting stubs.
USB host driver changes
We used SendNotifyMessage() API to broadcast the device information from the driver to application so that this will be received in WinProc which could be easy to handle. We tried various messages and finally we succeed with the following message.
Message broadcast when device plugged
- SendNotifyMessage(HWND_BROADCAST,WM_DEVICECHANGE, DBT_DEVICEARRIVAL,(LPARAM)&UserData);
Message broadcast when device unplugged
- SendNotifyMessage(HWND_BROADCAST,WM_DEVICECHANGE, DBT_DEVICEREMOVECOMPLETE,(LPARAM)&UserData);
Here the UserData is the user defined structure we are using to pass the VID /PID and required data to application from driver.
- typedef struct _DEVICE_VID_PID_INFO {
DWORD dev_size;
DWORD dev_vid;
DWORD dev_pid;
DWORD dev_InterfaceProtocol;
} DEVICE_VID_PID_INFO;
DEVICE_VID_PID_INFO UserData;
Now we need to know where to add the SendNotifyMessage in USB Host driver. Since we are using high speed EHCI host, we have cloned the corresponding USB Host driver from the \WINCE600\PUBLIC\COMMON\OAK\DRIVERS\USB\HCD\USB20\USB2COM directory to our BSP. We have to play with some functions in cdevice.cpp file.
Whenever the USB device is plugged, the CHub::AttachDevice() will be called and all descriptors will be received in this function through the series of calls to GetDescriptor() function in various cases. VID/PID will be received in device descriptor but we need InterfaceProtocol information to identify more about our HID devices (our case) which is in the interface descriptor, we have received the necessary information in the DEVICE_CONFIG_STATUS_DETERMINE_CONFIG_TO_CHOOSE, this case will be called after the DEVICE_CONFIG_STATUS_SCHEDULING_GET_CONFIG_DESCRIPTOR case in this AttachDevice() function. You can see the structure deviceInfo on the function which contains the required data.
USB_DEVICE_INFO deviceInfo;
Above structure has the necessary device information that you need. I have shared the code snippet of CHub::AttachDevice() for your reference.
- case DEVICE_CONFIG_STATUS_DETERMINE_CONFIG_TO_CHOOSE:
{
….
….
/*End of this case call our function to send notification*/
BroadCastDeviceInfo(deviceInfo,TRUE); //TRUE means attached
break;
}
We have written a separate function for broadcasting the device information which will be used both in attach and detach. We have called the BroadCastDeviceInfo() function in CFunction::HandleDetach() When the device is detached.
- void CFunction::HandleDetach( void )
{
DEBUGMSG( ZONE_ATTACH || ZONE_FUNCTION, (TEXT(“+CFunction(tier %d)::HandleDetach\n”), m_tierNumber) );
BroadCastDeviceInfo(m_deviceInfo,FALSE); //FALSE means detached
…..
…..
}
Next is the actual function, which we are passing messages to application. Function arguments are the USB_DEVICE_INFO structure and device status (attach or detach).
- void BroadCastDeviceInfo(USB_DEVICE_INFO deviceInfo,BOOL IsAttached)
{
DEVICE_VID_PID_INFO UserData;
DWORD Return=WaitForAPIReady(SH_WMGR,200);
//This is must. Since the SendNotifyMessage is // the Window Manger API and this won’t be available during boot and need to skip the
// SendNotifyMessage call otherwise exception will happen.
switch(Return)
{
case WAIT_OBJECT_0:
{
UserData.dev_size = (sizeof( DEVICE_VID_PID_INFO));
UserData.dev_pid = deviceInfo.Descriptor.idProduct;
UserData.dev_vid = deviceInfo.Descriptor.idVendor;
UserData.dev_InterfaceProtocol=deviceInfo.lpActiveConfig->lpInterfaces-> Descriptor.bInterfaceProtocol;
if(IsAttached)
SendNotifyMessage(HWND_BROADCAST,WM_DEVICECHANGE, DBT_DEVICEARRIVAL, (LPARAM)&UserData);
else
SendNotifyMessage(HWND_BROADCAST,WM_DEVICECHANGE, DBT_DEVICEREMOVECOMPLETE,(LPARAM)&UserData);
}
break;
case WAIT_TIMEOUT:
case WAIT_FAILED:
{
RETAILMSG(1,(TEXT(” Skipped SendNotifyMessage…………\r\n”)));
}
break;
}
}
SendNotifyMessage() function is a Window Manger function and which will not be ready during the boot time since USB Host driver will be loaded earlier than window Manager. Suppose if the device is attached during boot time, calling this API will end with fatal. So we have to check whether this API is ready to call using WaitForAPIReady() before calling this API(As shown in the above code).if it is not ready you have to skip the call. Since we are skipping the API call, application won’t get message about this attach/detach during this case. In this case, we can save/delete the necessary information in user defined registry when the device is attached or detached respectively. When launching the application, it has to check for these predefined registry entries and act accordingly. Once the API is ready, after that we can get notification to application. Now Let us see how can we receive the VID/PID during device attach/detach in application.
Retrieving VID/PID in application
Since it is a broadcast message, Any running WIN32 application can receive the notification message in its registered WinProc. I have shown the WinProc example for retrieving the VID/PID in application below. UINT message – will receive the device change notification and wParam – will receive the current device status (plugged / unplugged) and lparam – will receive the user defined structure passed from the USB Host driver.
- LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
…….
…….
…….
switch (message)
{
…….
…….
case WM_DEVICECHANGE:
{
NKDbgPrintfW(L” Device Change Notification++\r\n”);
If(lParam!=NULL)
userMsg = (DEVICE_VID_PID_INFO*)lParam;
switch(LOWORD(wParam))
{
case DBT_DEVICEREMOVECOMPLETE:
if(userMsg!=NULL)
NKDbgPrinftW(L”USB Device Unplugged,VID=0x%x,PID=0x%x, IP=0x%x\r\n”, userMsg->dev_vid,userMsg->dev_pid, userMsg->dev_InterfaceProtocol);
break;
case DBT_DEVICEARRIVAL:
if(userMsg!=NULL)
NKDbgPrinftW(L”USB Device plugged, VID=0x%x, PID=0x%x, IP=0x%x\r\n”, userMsg->dev_vid,userMsg->dev_pid, userMsg->dev_InterfaceProtocol);
break;
}
}
break;
…….
…….
…….
}