Chapter 13 336 eventObject = CreateEvent (IntPtr.Zero, false, false, String.Empty); HidOverlapped.OffsetLow = 0; HidOverlapped.OffsetHigh = 0; HidOverlapped.EventHandle = eventObject; unManagedBuffer = Marshal.AllocHGlobal(inputReportBuffer.Length); unManagedOverlapped = Marshal.AllocHGlobal(Marshal.SizeOf(HidOverlapped)); Marshal.StructureToPtr(HidOverlapped, unManagedOverlapped, false); readHandle = CreateFile (devicePathName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); success = ReadFile (readHandle, unManagedBuffer, inputReportBuffer.Length, ref numberOfBytesRead, unManagedOverlapped); Human Interface Devices: Host Application 337 // If ReadFile returned true, a report is available. Otherwise, check for completion. if (!success) { result = WaitForSingleObject (eventObject, 3000); switch ( result ) { case WAIT_OBJECT_0: success = True; GetOverlappedResult (readHandle, unManagedOverlapped, ref numberOfBytesRead, false); break; case WAIT_TIMEOUT: CancelIo(readHandle); break; default: CancelIo(readHandle); break; } } if (success) { // A report was received. // Copy the received data to inputReportBuffer for the application to use. Marshal.Copy(unManagedBuffer, inputReportBuffer, 0, numberOfBytesRead); } Marshal.FreeHGlobal(unManagedOverlapped); Marshal.FreeHGlobal(unManagedBuffer); Chapter 13 338 &GVCKNU The buffer passed to ReadFile should be at least the size reported in the InputReportByteLength property of the HIDP_CAPS structure returned by HidP_GetCaps. The CreateEvent function returns a pointer to an event object that will be set to the signaled state when the read operation succeeds or the function times out or returns another error. The call to ReadFile passes the returned pointer in the HidOverlapped structure. Marshaling allocates memory for the overlapped structure and the report buffer to ensure that their contents remain accessible for the life of the overlapped operation. CreateFile obtains a handle for overlapped I/O by setting the dwFlagsAndAttributes parameter to FILE_FLAG_OVERLAPPED. The call to ReadFile passes the handle returned by CreateFile, an array to store the returned report, the report’s length, a pointer to a variable to hold the num- ber of bytes read, and a pointer to a NativeOverlapped structure. The struc- ture’s EventHandle member is the handle returned by CreateEvent. ReadFile returns immediately. A return value of true indicates that the function has retrieved one or more reports. False means that a report wasn’t available. To detect when a report arrives, the application calls WaitForSingleObject, passing a pointer to the event object and a timeout value in milliseconds. If WaitForSingleObject returns success (WAIT_OBJECT_0), GetOverlappe- dResult returns the number of bytes read. The Marshal.Copy method copies the report data to the managed inputReportBuffer array. The application can then use the report data as desired and free the memory previously allocated and no longer needed. The first byte in inputReportBuffer is the Report ID, and the following bytes are the report data. If the interface supports only the default Report ID of zero, the Report ID doesn’t transmit on the bus but is still present in the buffer returned by ReadFile. A call to ReadFile doesn’t initiate traffic on the bus. The host begins requesting reports when the HID driver loads during enumeration, and the driver stores received reports in a ring buffer. When the buffer is full and a new report arrives, the buffer drops the oldest report. A call to ReadFile reads the oldest report in the buffer. If the driver’s buffer is empty, ReadFile waits for a report to arrive. Human Interface Devices: Host Application 339 Under Windows 98 SE and later, HidD_SetNumInputBuffers can set the buffer size. Different Windows editions have different default buffer sizes, rang- ing from 2 under Windows 98 Gold to 32 under Windows XP. Each handle with read access to the HID has its own Input buffer, so multiple applications can read the same reports. If the application doesn’t request reports as frequently as the endpoint sends them, some reports will be lost. One way to keep from losing reports is to increase the size of the report buffer passed to ReadFile. If multiple reports are available, ReadFile returns as many as will fit in the buffer. If you need to be absolutely sure not to lose a report, use Feature reports instead. Also see the tips in Chapter 3 about performing time-critical transfers. The Idle rate introduced in Chapter 11 determines whether or not a device sends a report if the data hasn’t changed since the last transfer. If ReadFile isn’t returning, these are possible reasons: • The HID’s interrupt IN endpoint is NAKing the IN token packets because the endpoint hasn’t been armed to send report data. An endpoint’s inter- rupt typically triggers only after endpoint sends data, so the device must arm the endpoint to send the first report before the first interrupt. • The number of bytes the endpoint is sending doesn’t equal the number of bytes in a report (for HIDs that use the default Report ID) or the number of bytes in a report + 1 (for HIDs that use other Report IDs). • For HIDs with multiple Report IDs, the first byte doesn’t match a valid Report ID. 9TKVKPIC(GCVWTG4GRQTVVQVJG&GXKEG To send a Feature report to a device, use HidD_SetFeature, which sends a Set Report request and a report in a control transfer. Chapter 13 340 8$ Definitions <DllImport("hid.dll", SetLastError:=True)> _ Shared Function HidD_SetFeature _ (ByVal HidDeviceObject As SafeFileHandle, _ ByVal lpReportBuffer() As Byte, _ ByVal ReportBufferLength As Int32) _ As Boolean End Function Use Dim outFeatureReportBuffer() As Byte = Nothing Dim success As Boolean Array.Resize(outFeatureReportBuffer, Capabilities.FeatureReportByteLength) ' Store the Report ID in the first byte of the buffer: outFeatureReportBuffer(0) = 0 ' Store the report data following the Report ID. Example: outFeatureReportBuffer(1) = 79 outFeatureReportBuffer(2) = 75 success = HidD_SetFeature _ (deviceHandle, _ outFeatureReportBuffer, _ outFeatureReportBuffer.Length) 8% Definitions [ DllImport( "hid.dll", SetLastError=true ) ] internal static extern Boolean HidD_SetFeature ( SafeFileHandle HidDeviceObject, Byte lpReportBuffer[], Int32 ReportBufferLength ); Use Byte[] outFeatureReportBuffer = null; Boolean success = false; Array.Resize(ref outFeatureReportBuffer, Capabilities.FeatureReportByteLength); Human Interface Devices: Host Application 341 // Store the Report ID in the first byte of the buffer: outFeaturetReportBuffer[ 0 ] = 0; // Store the report data following the Report ID. Example: outFeatureReportBuffer[ 1 ] = 79; outFeatureReportBuffer[ 2 ] = 75; success = HidD_SetFeature (deviceHandle, outFeatureReportBuffer, outFeatureReportBuffer.Length); &GVCKNU HidD_SetFeature requires a handle to the HID, an array to write, and the array’s length. The first byte in the outFeatureReportBuffer array is the Report ID. The array’s length is in the HIDP_CAPS structure retrieved by HidP_GetCaps. The function returns true on success. If the device continues to NAK the report data, the function times out and returns. A call to HidD_SetOutputReport works in much the same way to send an Output report using a control transfer. The function passes a handle to the HID, a pointer to a byte array containing an Output report, and the number of bytes in the report plus one byte for the Report ID. 4GCFKPIC(GCVWTG4GRQTVHTQOC&GXKEG To read a Feature report from a device, use HidD_GetFeature, which sends a Get_Feature request in a control transfer. The endpoint returns the report in the Data stage. 8$ Definitions <DllImport("hid.dll", SetLastError:=True)> _ Shared Function HidD_GetFeature _ (ByVal HidDeviceObject As SafeFileHandle, _ ByVal lpReportBuffer() As Byte, _ ByVal ReportBufferLength As Int32) _ As Boolean End Function Chapter 13 342 Use Dim inFeatureReportBuffer() As Byte = Nothing Dim success As Boolean Array.Resize(inFeatureReportBuffer, Capabilities.FeatureReportByteLength) 'The first byte in the report buffer is the Report ID: InFeatureReportBuffer(0) = 0 success = HidD_GetFeature _ (deviceHandle, _ inFeatureReportBuffer, _ inFeatureReportBuffer.Length) 8% Definitions [ DllImport( "hid.dll", SetLastError=true ) ] internal static extern Boolean HidD_GetFeature ( SafeFileHandle HidDeviceObject, Byte[] lpReportBuffer, Int32 ReportBufferLength ); Use Byte[] inFeatureReportBuffer = null; Boolean success = false; Array.Resize(ref inFeatureReportBuffer, Capabilities.FeatureReportByteLength); // The first byte in the report buffer is the Report ID: inFeatureReportBuffer[0] = 0; success = HidD_GetFeature (deviceHandle, inFeatureReportBuffer, inFeatureReportBuffer.Length); &GVCKNU HidD_GetFeature requires a handle to the HID, an array to hold the retrieved report(s), and the array’s length. The inFeatureReportBuffer array holds the retrieved report. The first byte in the array is the Report ID. The array’s length is in the HIDP_CAPS structure retrieved by HidP_GetCaps. Human Interface Devices: Host Application 343 The function returns true on success. If the device continues to return NAK in the Data stage of the transfer, the function times out and returns. A call to HidD_GetInputReport works in much the same way to request an Input report using a control transfer. The function passes a handle to the HID, an array to hold the Input report, and the number of bytes in the report plus one byte for the Report ID. %NQUKPI%QOOWPKECVKQPU When finished communicating, the application should call the Close method to close any SafeFileHandles opened by CreateFile as described in Chapter 10. When finished using the PreparsedData buffer that HidD_GetPreparsedData returned, the application should call HidD_FreePreparsedData. 8$ Definitions <DllImport("hid.dll", SetLastError:=True)> _ Shared Function HidD_FreePreparsedData _ (ByVal PreparsedData As IntPtr) _ As Boolean End Function Use Dim success As Boolean success = HidD_FreePreparsedData(preparsedData) 8% Definitions [ DllImport( "hid.dll", SetLastError=true ) ] internal static extern Boolean HidD_FreePreparsedData ( IntPtr PreparsedData ); Use Boolean success = false; success = HidD_FreePreparsedData( preparsedData ); 345 7UKPI9KP75$HQT 8GPFQT&GHKPGF(WPEVKQPU An option for devices that perform vendor-specific functions is Microsoft’s WinUSB driver. This chapter shows how to develop a device that uses the WinUSB driver and how to use the WinUSB API to access the device from applications. %CRCDKNKVKGUCPF.KOKVU A device is a candidate for using the WinUSB driver if the device and its host computer(s) meet the requirements below. &GXKEG4GSWKTGOGPVU The device: • Exchanges application data using any combination of control, interrupt, and bulk endpoints. (The driver doesn’t support isochronous transfers.) • Has descriptors that specify a vendor-specific class. . vendor-specific functions is Microsoft’s WinUSB driver. This chapter shows how to develop a device that uses the WinUSB driver and how to use the WinUSB API to access the device from applications. %CRCDKNKVKGUCPF.KOKVU A. the device from applications. %CRCDKNKVKGUCPF.KOKVU A device is a candidate for using the WinUSB driver if the device and its host computer(s) meet the requirements below. &GXKEG4GSWKTGOGPVU The