4GRQTV6[RG #2+WPEVKQP 6TCPUHGT6[RG Input ReadFile Interrupt IN HidD_GetInputReport Windows XP and later Control with Get Report request Output WriteFile Interrupt OUT if available; other
Trang 18% Definitions
internal struct HIDP_CAPS
{
internal Int16 Usage;
internal Int16 UsagePage;
internal Int16 InputReportByteLength;
internal Int16 OutputReportByteLength;
internal Int16 FeatureReportByteLength;
[ MarshalAs( UnmanagedType.ByValArray, SizeConst=17 ) ]
internal Int16[] Reserved;
internal Int16 NumberLinkCollectionNodes;
internal Int16 NumberInputButtonCaps;
internal Int16 NumberInputValueCaps;
internal Int16 NumberInputDataIndices;
internal Int16 NumberOutputButtonCaps;
internal Int16 NumberOutputValueCaps;
internal Int16 NumberOutputDataIndices;
internal Int16 NumberFeatureButtonCaps;
internal Int16 NumberFeatureValueCaps;
internal Int16 NumberFeatureDataIndices;
}
[ DllImport( "hid.dll", SetLastError=true ) ]
internal static extern Int32 HidP_GetCaps
( IntPtr PreparsedData,
ref HIDP_CAPS Capabilities );
Use
internal HIDP_CAPS Capabilities;
Int32 result = 0;
result = HidP_GetCaps( preparsedData, ref Capabilities );
&GVCKNU
T h e p r e p a r s e d D a t a p a r a m e t e r i s t h e p o i n t e r r e t u r n e d b y HidD_GetPreparsedData When the function returns, the application can examine and use whatever values are of interest in the Capabilities structure For example, to look for a joystick, look for UsagePage = 0001h and Usage = 0004h.
The ReportByteLength items are useful when setting buffer sizes for sending and receiving reports.
Trang 2An application can also retrieve the capabilities of each button and value in a report HidP_GetValueCaps returns a pointer to an array of structures contain-ing information about the values in a report The NumberInputValueCaps property of the HIDP_CAPS structure is the number of structures returned by HidP_GetValueCaps.
The items in the structures include many values obtained from the HID’s report descriptor as described in Chapter 12 The items include the Report ID, whether a value is absolute or relative, whether a value has a null state, and log-ical and physlog-ical minimums and maximums A LinkCollection identifier dis-tinguishes between controls with the same Usage and Usage Page in the same collection In a similar way, the HidP_GetButtonCaps function can retrieve information about a report’s buttons The information is stored in a HidP_ButtonCaps structure Not every application needs to retrieve this infor-mation.
5GPFKPICPF4GEGKXKPI4GRQTVU
The previous API functions help in finding and learning about a device that matches what the application is looking for On finding a device of interest, the application and device are ready to exchange data in reports.
Table 13-3 showed API functions for exchanging reports Table 13-6 summa-rizes the transfer types the host uses with different report types The application doesn’t have to know or care which transfer type or endpoint the driver uses.
Table 13-6: The transfer type used to send or receive a report can vary with the API function, operating system edition, and available endpoints 4GRQTV6[RG #2+(WPEVKQP 6TCPUHGT6[RG
Input ReadFile Interrupt IN
HidD_GetInputReport (Windows XP and later)
Control with Get Report request
Output WriteFile Interrupt OUT if available; otherwise
control with Set Report request HidD_SetOutputReport
(Windows XP and later)
Control with Set Report request
Feature IN HidD_GetFeature Control with Get Report request Feature OUT HidD_SetFeature Control with Set Report request
Trang 3On obtaining a handle and learning the number of bytes in an Output report,
an application can send a report to the HID The application places the data to send in a buffer and calls WriteFile.
8$ Definitions
<DllImport("kernel32.dll", SetLastError:=True)> _
Shared Function WriteFile _
(ByVal hFile As SafeFileHandle, _
ByVal lpBuffer() As Byte, _
ByVal nNumberOfBytesToWrite As Int32, _
ByRef lpNumberOfBytesWritten As Int32, _
ByVal lpOverlapped As IntPtr) _
As Boolean
End Function
Use
Dim numberOfBytesWritten As Int32 = 0
Dim outputReportBuffer() As Byte = Nothing
Dim success As Boolean
' Set the size of the Output report buffer
Array.Resize(outputReportBuffer, Capabilities.OutputReportByteLength)
' Store the Report ID in the first byte of the buffer:
outputReportBuffer(0) = 0
' Store the report data following the Report ID Example:
outputReportBuffer(1) = 85
outputReportBuffer(2) = 83
outputReportBuffer(3) = 66
' Send the report
success = WriteFile _
(deviceHandle, _
outputReportBuffer, _
outputReportBuffer.Length, _
numberOfBytesWritten, _
IntPtr.Zero)
Trang 48% Definitions
[ DllImport( "kernel32.dll", SetLastError=true ) ]
internal static extern Boolean WriteFile
( SafeFileHandle hFile,
Byte[] lpBuffer,
Int32 nNumberOfBytesToWrite,
ref Int32 lpNumberOfBytesWritten,
IntPtr lpOverlapped );
Use
Int32 numberOfBytesWritten = 0;
Byte[] outputReportBuffer = null;
Boolean success = false;
// Set the size of the Output report buffer
Array.Resize(ref outFeatureReportBuffer, Capabilities.FeatureReportByteLength);
// Store the Report ID in the first byte of the buffer:
outputReportBuffer[ 0 ] = 0;
// Store the report data following the Report ID Example:
outputReportBuffer[ 1 ] = 85;
outputReportBuffer[ 2 ] = 83;
outputReportBuffer[ 3 ] = 66;
// Send the report
success = WriteFile
(deviceHandle,
outputReportBuffer,
outputReportBuffer.Length,
ref numberOfBytesWritten,
IntPtr.Zero);
&GVCKNU
In the call to WriteFile, the hFile parameter is a handle returned by CreateFile The lpBuffer parameter is a byte array that contains the report ID followed by the report data The nNumberOfBytesToWrite parameter specifies how many bytes to write and should equal the OutputReportByteLength property of the
Trang 5HIDP_CAPS structure retrieved with HidP_GetCaps This value is the report size in bytes plus one byte for the Report ID.
The lpOverlapped parameter is a null pointer in this example, but WriteFile can use overlapped I/O as described in the following section on ReadFile Over-lapped I/O can prevent the application’s thread from hanging if the HID’s interrupt OUT endpoint NAKs endlessly In normal operation, the endpoint should accept received data with little delay.
On success, the function returns true with lpNumberOfBytesWritten pointing
to the number of bytes the function wrote to the HID.
If the interface supports only the default Report ID of zero, the Report ID doesn’t transmit on the bus, but the Report ID must always be the first byte in the buffer the application passes to WriteFile.
When sending a report to an interrupt endpoint, WriteFile returns on success
or error and will wait endlessly if the endpoint continues to NAK the report data When sending a report via the control endpoint, WriteFile returns on suc-cess, an error, or a timeout if the endpoint continues to NAK the report data
A returned error message of CRC Error indicates that the host controller
attempted to send the report, but the device didn’t respond as expected Despite the message, the problem isn’t likely to be due to an error detected in a CRC calculation The error is more likely to be due to a firmware problem that keeps the endpoint from accepting the report data If WriteFile doesn’t return at all, the interrupt OUT endpoint possibly isn’t configured to ACK the report data.
4GCFKPICP+PRWV4GRQTVHTQOVJG&GXKEG
The complement to WriteFile is ReadFile After obtaining a handle to the HID interface and learning the size of the largest Input report, an application can use ReadFile to read an Input report from a device.
When called using non-overlapped I/O, ReadFile is a blocking call If an appli-cation calls the function when the HID driver’s Input buffer is empty, the call-ing thread waits until a report is available, the user closes the application from the Task Manager, or the user removes the device from the bus An overlapped,
or asynchronous, read operation can keep an application’s main thread from hanging as it waits for a report.
With an overlapped read, ReadFile returns immediately whether a report is available or not If the function doesn’t return a report, the application can call WaitForSingleObject with a specified timeout WaitForSingleObject returns
Trang 6when a report is available or on a timeout or other error On a timeout or error, the application can try again or call the CancelIo function to cancel the read operation This approach works well if reports are normally available without delay but the application needs to regain control if for some reason there is no report.
To prevent long delays waiting for WaitForSingleObject to return, an applica-tion can set the timeout to zero and call the funcapplica-tion repeatedly in a loop or at intervals, triggered by a timer The function returns immediately whether or not a report is available, and the application can perform other tasks in the loop
or between timer events.
To improve performance, an application can call ReadFile in a separate thread that notifies the main thread when a report is available A NET application can define an asynchronous delegate and use the BeginInvoke method to call a method that calls ReadFile in a different thread BeginInvoke can specify a call-back routine that executes in the application’s main thread when the method that has called ReadFile returns The callback routine can retrieve the returned report and use the received data as needed.
This example uses overlapped reads with a timeout:
8$ Definitions
Friend Const FILE_FLAG_OVERLAPPED As Int32 = &H40000000
Friend Const GENERIC_READ As UInt32 = &H80000000UL
Friend Const GENERIC_WRITE As UInt32 = &H40000000
Friend Const WAIT_OBJECT_0 As Int32 = 0
Friend Const WAIT_TIMEOUT As Int32 = &H102
<DllImport("kernel32.dll", SetLastError:=True)> _
Shared Function CancelIo _
(ByVal hFile As SafeFileHandle) _
As Int32
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Shared Function CreateEvent _
(ByVal SecurityAttributes As IntPtr, _
ByVal bManualReset As Boolean, _
ByVal bInitialState As Boolean, _
ByVal lpName As String) _
As IntPtr
End Function
Trang 7<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Shared Function GetOverlappedResult _
(ByVal hFile As SafeFileHandle, _
ByVal lpOverlapped As IntPtr, _
ByRef lpNumberOfBytesTransferred As Int32, _
ByVal bWait As Boolean) _
As Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True)> _
Shared Function ReadFile _
(ByVal hFile As SafeFileHandle, _
ByVal lpBuffer As IntPtr, _
ByVal nNumberOfBytesToRead As Int32, _
ByRef lpNumberOfBytesRead As Int32, _
ByVal lpOverlapped As IntPtr) _
As Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True)> _
Shared Function WaitForSingleObject _
(ByVal hHandle As IntPtr, _
ByVal dwMilliseconds As Int32) _
As Int32
End Function
Use
Dim eventObject As IntPtr
Dim HidOverlapped As New NativeOverlapped
Dim inputReportBuffer() As Byte = Nothing
Dim numberOfBytesRead As Int32
Dim result As Int32
Dim success As Boolean
Dim unManagedBuffer As IntPtr
Dim unManagedOverlapped As IntPtr
Array.Resize(inputReportBuffer, Capabilities.InputReportByteLength)
Trang 8eventObject = 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 Or FILE_SHARE_WRITE, _
IntPtr.Zero, _
OPEN_EXISTING, _
FILE_FLAG_OVERLAPPED, _
0)
success = ReadFile _
(readHandle, _
unManagedBuffer, _
inputReportBuffer.Length, _
numberOfBytesRead, _
unManagedOverlapped)
Trang 9' If ReadFile returned True, a report is available Otherwise, check for completion.
If Not (success) Then
result = WaitForSingleObject(eventObject, 3000)
Select Case result
Case WAIT_OBJECT_0 success = True GetOverlappedResult _ (readHandle, _ unManagedOverlapped, _ numberOfBytesRead, _ False)
Case WAIT_TIMEOUT CancelIo(readHandle)
Case Else CancelIo(readHandle)
End Select
End If
If success Then
' A report was received
' Copy the received data to inputReportBuffer for the application to use
Marshal.Copy _
(unManagedBuffer, inputReportBuffer, 0, numberOfBytesRead) End If
Marshal.FreeHGlobal(unManagedOverlapped)
Marshal.FreeHGlobal(unManagedBuffer)
8% Definitions
internal const Int32 FILE_FLAG_OVERLAPPED = 0X40000000;
internal const UInt32 GENERIC_READ = 0X80000000;;
internal const UInt32 GENERIC_WRITE = 0X40000000;
internal const Int32 WAIT_OBJECT_0 = 0;
internal const Int32 WAIT_TIMEOUT = 0X102;
Trang 10[ DllImport( "kernel32.dll", SetLastError=true ) ]
internal static extern Int32 CancelIo
( SafeFileHandle hFile );
[ DllImport( "kernel32.dll", CharSet=CharSet.Auto, SetLastError=true ) ]
internal static extern IntPtr CreateEvent
( IntPtr SecurityAttributes,
Boolean bManualReset,
Boolean bInitialState,
String lpName );
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern Boolean GetOverlappedResult
(SafeFileHandle hFile,
IntPtr lpOverlapped,
ref Int32 lpNumberOfBytesTransferred,
Boolean bWait);
[ DllImport( "kernel32.dll", SetLastError=true ) ]
internal static extern Boolean ReadFile
( SafeFileHandle hFile,
IntPtr lpBuffer,
Int32 nNumberOfBytesToRead,
ref Int32 lpNumberOfBytesRead,
IntPtr lpOverlapped );
[ DllImport( "kernel32.dll", SetLastError=true ) ]
internal static extern Int32 WaitForSingleObject
( IntPtr hHandle,
Int32 dwMilliseconds );
Use
IntPtr eventObject= IntPtr.Zero;
NativeOverlapped HidOverlapped = new NativeOverlapped();
Byte[] inputReportBuffer = null;
Int32 numberOfBytesRead = 0;
Int32 result = 0;
Boolean success = false;
IntPtr unManagedBuffer = IntPtr.Zero;
IntPtr unManagedOverlapped = IntPtr.Zero;
Array.Resize(ref inputReportBuffer, Capabilities.InputReportByteLength);