windows-nt/Source/XPSP1/NT/drivers/wdm/input/client/hclient/hclient.htm
2020-09-26 16:20:57 +08:00

258 lines
19 KiB
HTML
Raw Permalink Blame History

<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<TITLE>HCLIENT</TITLE>
<META NAME="Template" CONTENT="C:\PROGRAM FILES\MICROSOFT OFFICE\OFFICE\html.dot">
</HEAD>
<BODY TEXT="#000000" LINK="#0000ff" VLINK="#800080" BGCOLOR="#ffffff" leftmargin="8">
<FONT FACE="Verdana"><H2><A NAME="MYSAMPLE">HCLIENT</A> </H2>
<span style="color:#FF0000">[This is preliminary documentation and subject to change.]</span>
<H3>SUMMARY</H3>
</FONT><FONT FACE="Verdana" SIZE=2><P>
<P>This document and associated sample code describe how to write a user-mode client application to communicate with devices that conform to the HID device class specification. This article is useful to application writers who need to develop a user-mode application which communicates with and extracts information from a HID-compatible device. This sample illustrates the method for detecting HID devices, opening those HID devices for communication, and extracting and/or formatting the data into/from device reports.</P>
<P>The HID class consists primarily of devices that are used by humans to control the operation of computer systems. Typical HID devices include keyboards, mice, and joysticks. Non-typical devices might include front-panel controls (knobs, switches, or buttons) or controls found on devices such as telephones, VCR remote controls, games, and simulation devices. The underlying common feature of all HID devices is the need for guaranteed delivery of small amounts of non-periodic data. </P>
<P>The basic communication mechanism for HID class devices is the HID report. Every HID device must supply a report descriptor that details the format of the different reports that it creates for its device. The HID class drivers and HID.DLL is provide an interface for extracting the relevant data from these reports.</P>
<P>Although the HClient sample is a user-mode application, many of the functions available in HID.DLL are available to kernel-mode HID clients as well. The functions exported by HID.DLL have a prefix of either HidD_ or HidP_. All functions with a HidP_ prefix are available to kernel-mode clients. However, the mechanism for opening HID devices and obtaining the necessary information such as preparsed data is different in this context.
</FONT>
<H3>BUILDING THE SAMPLE</H3>
<FONT FACE="Verdana" SIZE=2>
<P>To build the HClient.exe sample, follow these instructions.</P>
<OL>
<LI>Run the standard Windows NT&#174;/Windows&#174; 2000 DDK build environment (checked or free)</LI>
<LI>Change to the .\src\wdm\hid\hclient directory</LI>
<LI>Execute <B>build</B></LI>
<LI>The built HClient.exe will be found in .\lib\&lt;i386|alpha&gt;\&lt;checked|free&gt;</LI></OL>
<P>The HClient sources are dependent on the following system include files and libraries.</P></FONT>
<FONT FACE="Courier">
<PRE>&nbsp;&#9;HIDSDI.H &#9;User-mode only definitions and declarations
&#9;HIDPI.H &#9;Definitions and declarations for user-mode and kernel-mode HID clients
&#9;HIDUSAGE.H &#9;Macro definitions for predefined usage table and usage values as
&#9;&#9;&#9;defined in the HID Spec 1.0 and HID Usage Table Spec 1.0
&#9;HID.LIB &#9;Library file needed to resolve exported HID.DLL functions</PRE>
</FONT>
<FONT FACE="Verdana" SIZE=2>
<P>Kernel-mode clients may also need the following files:</P>
</FONT>
<FONT FACE="Courier">
<PRE>&nbsp;&#9;HIDPDDI.H &#9;Declarations and definitions for features available through the HID ioctl interface
&#9;HIDPARSE.LIB &#9;Library file needed to resolve exported HIDPARSE.SYS functions</PRE>
</FONT>
<FONT FACE="Verdana">
<H3>TOOLS</H3>
<P></FONT>
<FONT FACE="Verdana" SIZE=2>
The only tools needed to work with HClient are HID devices. In the Windows NT/Windows 2000 system, file handles cannot be opened on mice and keyboards. However, all other HID devices will be available and recognized by HClient for testing purposes.</P>
</FONT>
<FONT FACE="Verdana">
<H3>RESOURCES</H3>
</FONT>
<FONT FACE="Verdana" SIZE=2>
<P>See the <a href=http://www.usb.org/>Universal Serial Bus</a> Device Class Definition for Human Interface Devices (HID) Version 1.0 and Universal Serial Bus HID Usage Tables 1.0. </P>
</FONT>
<FONT FACE="Verdana">
<H3>CODE TOUR</H3>
</FONT>
<H4>File Manifest</H4>
<PRE><U>Files Description</U>
<FONT FACE="Courier">
HCLIENT.HTM&#9;The documentation for this sample (this file)
SOURCES&#9;&#9;The generic file for building the code sample
BUFFERS.C&#9;Code for displaying HID report buffers in the extended calls
&#9;&#9;dialog box.&#9;&#9;
BUFFERS.H&#9;Function and structure declaration visible to other modules
DEBUG.C&#9;&#9;Contains the function definitions for debug memory allocation tracking&#9;
DEBUG.H &#9;Contains public macro definitions and function declarations for
&#9;&#9;the debugging routines to deal with asserts, traps, and memory allocations
ECDISP.C &#9;Code to handle the extended calls dialog box
ECDISP.H &#9;Contains public declarations for the extended calls dialog
&#9;&#9;box
HCLIENT.C&#9;Code for handling HClient's main dialog box
HCLIENT.H&#9;Contains public declarations and definitions for HCLIENT.C and
&#9;&#9;visible to other modules
HCLIENT.RC&#9;Visual C++ generated resource file for HClient
HID.H &#9;&#9;Contains declarations and definitions for handling devices and
&#9;&#9;data within HClient
LIST.H &#9;&#9;Contains public macro definitions for manipulating doubly-linked lists
LOGPNP.C&#9;Code for finding, loading and building logical HID device
&#9;&#9;structures
LOGPNP.H &#9;Contains public function declarations for LOGPNP.C
MAKEFILE&#9;NT DDK build environment makefile&#9;
PNP.C &#9;&#9;Contains the code for finding, adding, removing, and
&#9;&#9;identifying hid devices.
REPORT.C &#9;Contains the code for reading/writing hid reports and
&#9;&#9;translating those HID reports into useful information.
RESOURCE.H&#9;Visual C++ generated resource definition file
STRINGS.C&#9;Code for converting data buffers and integer values
&#9;&#9;to and from string representation for display.
STRINGS.H&#9;Contains public function definitions for STRINGS.C</PRE>
</FONT>
<H4>Programming Tour</H4>
<FONT FACE="Verdana" SIZE=2>
<P>The core functionality relevant to HID client applications is contained in the files REPORT.C and PNP.C. The code in these files implements the basic features that most clients will require. Most of the other source files contain code for handling the user interface and calling the main tasks that are in the above two files. This section covers those relevant topics. </P>
<P>The major topics covered in this tour are: </P>
<UL>
<LI>Detecting Installed HID devices </LI>
<LI>Opening HID Devices </LI>
<LI>Communicating with HID Devices </LI>
<LI>Building/Interpreting HID Reports</LI></UL>
<P><U>Detecting Installed HID Devices</U></P>
<P>A necessary component of a HID client application is the detection of installed HID devices. The function FindKnownHidDevices() in PNP.C details how to do that work. The basic steps for identifying attached HID devices are:</P>
<UL>
<LI>Call HidD_GetHidGuid() <20> Get the HID device class GUID </LI>
<LI>Call SetupDiGetClassDevs() <20> Get a handle to a set of devices which implement the HID interface </LI>
<LI>Call SetupDiEnumDeviceInterfaces() <20> For each device in the returned set of devices, obtain the interface information for all exposed HID interfaces. </LI>
<LI>Call SetupDiGetDeviceInterfaceDetail() <20> For each interface obtained in the previous call, get the detailed information block for that interface. This detailed information includes the string that can be passed to CreateFile() to open a handle to the device </LI>
<LI>Call SetupDiDestroyDeviceInfoList() <20> Free up the device information set that was obtained in the call to SetupDiGetClassDevs().</LI></UL>
<P>The remainder of the code implemented in the function deals with creating a list of HID_DEVICE structures which contain information for each HID device in the system. This sample client accesses all HID devices in the system. A more specific implementation may only be looking for a certain type of HID device such as a joystick/gamepad.</P>
<P><U>Opening HID Devices</U></P>
<P>After detecting a HID device, HClient proceeds to open that device. When opening the device, HClient creates a HID_DEVICE structure to contain any information about the device that further routines might use. HID_DEVICE is defined as follows in HID.H:</P>
<P ALIGN="CENTER"><CENTER><TABLE CELLSPACING=0 BORDER=0 WIDTH=499>
<TR><TD VALIGN="MIDDLE">
<FONT FACE="Courier">
<PRE>&nbsp;typedef struct _HID_DEVICE {
HANDLE HidDevice; // A file handle to the hid device.
PHIDP_PREPARSED_DATA Ppd; // The opaque parser info describing this device
HIDP_CAPS Caps; // The Capabilities of this hid device.
HIDD_ATTRIBUTES Attributes;
PCHAR InputReportBuffer;
PHID_DATA InputData; // array of hid data structures
ULONG InputDataLength; // Num elements in this array.
PHIDP_BUTTON_CAPS InputButtonCaps;
PHIDP_VALUE_CAPS InputValueCaps;
PCHAR OutputReportBuffer;
PHID_DATA OutputData;
ULONG OutputDataLength;
PHIDP_BUTTON_CAPS OutputButtonCaps;
PHIDP_VALUE_CAPS OutputValueCaps;
PCHAR FeatureReportBuffer;
PHID_DATA FeatureData;
ULONG FeatureDataLength;
PHIDP_BUTTON_CAPS FeatureButtonCaps;
PHIDP_VALUE_CAPS FeatureValueCaps;
} HID_DEVICE, *PHID_DEVICE;</PRE></FONT>
</TD></TR>
</TABLE></CENTER>
</P>
<FONT FACE="Verdana" SIZE=2>
<P>In addition to storing the basic HID structures as defined in HIDPI.H and HIDSDI.H, the HID_DEVICE structure also maintains an array of HID_DATA structures for each report type. These structures contain the most recent value used for each of the controls defined in the HIDP_VALUE_CAPS list and HIDP_BUTTON_CAPS list for the given report type. The fields within this structure are used by routines that pack/unpack data reports. The usage of these fields is discussed further in the section entitled <I>Building/Interpreting HID Reports</I>.</P>
<P>The function OpenHidDevice() in PNP.C performs the necessary steps to fill a HID_DEVICE structure for a device. In order to do so, it performs the following steps:</P>
<UL>
<LI>Call CreateFile() <20> Open a handle to the specific HID device. In this case, we request read/write access to the device, allow the device to be shared, and synchronize access to the device handle (no OVERLAPPED flag). </LI>
<LI>Call HidD_GetPreparsedData() <20> Get the preparsed data for the device. The preparsed data is a HID parser specific block of data used for processing HID reports. The memory for this structure is allocated by the HID.DLL function and must be freed when no longer needed with HidD_FreePreparsedData(). </LI>
<LI>Call HidD_GetAttributes() <20> Get the attributes of the device. The attributes structure contains the vendor ID, product ID and version number of the given HID device </LI>
<LI>Call HidD_GetCaps() <20> Get the capabilities of the device. The device capabilities include the usage page and usage of the device, the required buffer length for the different reports on the device, the number of link collection nodes on the device and a count of the number of buttons and values for each of the different report types. </LI>
<LI>Call FillDeviceInfo() <20> Fill in the rest of the HID_DEVICE structure. This function allocates space for and retrieves the link collection nodes, the value caps, and the button caps for the device.</LI></UL>
<P>A given client application may not need to perform all of the above steps. For instance, if a client application only operates with a specific HID device (ie. vendor ID/product ID), it may forego any more processing after retrieving the attributes if the device it is currently opening is not the one it<69>s interested in. </P>
<P>A client application may also work only for a given usage page/usage combination. For example, a monitor control application would only need to open devices that match the monitor usage page and usage combination. </P>
<P>Lastly, a HID client may require only a subset of the detailed information currently stored in the HID_DEVICE structure. This sample application performs a broad range of functionality to provide a detailed sample.</P>
<P><U>Communicating with HID Devices</U></P>
<P>As mentioned above, a HID device<63>s basic method of communication is through reports. A HID device can contain as many as 255 reports for each report type (Input, Output, and Feature). In order to properly communicate with a device, a client application must be able to create reports (when sending data) or extract data from reports (when receiving data). See the section titled: <I>Building/Interpreting HID Reports</I> for information on how to manipulate these report buffers. </P>
<P>A HID device reports information to the host through Input reports or Feature Reports. Typically, input reports contain the data generated by user interaction, such as a button press. Feature reports, on the other hand, report the current state or settings for a device. The methods for obtaining these two different reports are implemented in the functions Read() and GetFeature() in REPORT.C. </P>
<P>In order to receive data, HClient must use the file handle created in OpenHidDevice that has READ access and the buffer allocated for the given report type. For input reports, Read() calls the Win32 API ReadFile() and waits for the device to return a report. For feature reports, GetFeature() sets the first byte of the report buffer to the desired report ID and calls HidD_GetFeature() to obtain that feature report. After receiving a report back from the underlying drivers, these routines call UnpackReport() to fill in the corresponding HID_DATA structures.</P>
<P>Outputting data to a device is a bit more complicated. Once again, there are two different types of reports that can be sent to a device, Output and Feature. The corresponding functions for outputting data are Write() and SetFeature() in REPORT.C.</P>
<P>The first step that both of these functions perform is creating the desired report buffer. First, the HID_DATA structure with the desired report ID value is found. Then, these functions call PackReport() to set the data values within the allocated report buffer for that report ID. Once the report buffer has been created, either WriteFile() or HidD_SetFeature() is called to send the report packet to the device. </P>
<P>It is important to understand that all access to a HID device file handle is synchronized. Therefore, all threads using that handle will block until all previous requests to the device have completed. One possible client implementation would have one thread continuously read input reports while another thread sends or receives feature reports. Since the HidD_GetFeature() and HidD_SetFeature functions are implemented as DeviceIoControl calls, they are also synchronized with ReadFile and WriteFile(). There are two solutions to this scenario. One solutions uses overlapped I/O for asynchronous control. The second is to open two handles for the device, one for the read thread and one for the feature thread. </P>
<P><U>Building/Interpreting HID Reports</U></P>
<P>The last important idea when dealing with reports is extracting/setting data values from/in a given report buffer. The section covers the details of the functions PackReport() and UnpackReport() as implemented in REPORT.C. </P>
<P>As mentioned above, in order to communicate with a HID device, the client application must be able to either create the appropriate report to send to the device or extract the relevant information from a report received from the device. When Hclient initially opened the HID device and created the HID_DEVICE structure for that device, it also created an array of HID_DATA structures for each of the report types. The format of this HID_DATA structure is as follows:</P>
<P ALIGN="CENTER"><CENTER><TABLE CELLSPACING=0 BORDER=0 WIDTH=499>
<TR><TD VALIGN="MIDDLE">
<FONT FACE="Courier">
<PRE>&nbsp;typedef struct _HID_DATA {
BOOLEAN IsButtonData;
UCHAR Reserved;
USAGE UsagePage; // The usage page for which we are looking.
ULONG Status; // The last status returned from the accessor function
// when updating this field.
ULONG ReportID; // ReportID for this given data structure
BOOLEAN IsDataSet; // Variable to track whether a given data structure
// has already been added to a report structure
union {
struct {
ULONG UsageMin; // Variables to track the usage minimum and max
ULONG UsageMax; // If equal, then only a single usage
ULONG MaxUsageLength; // Usages buffer length.
PUSAGE Usages; // list of usages (buttons ``down'' on the device.
} ButtonData;
struct {
USAGE Usage; // The usage describing this value;
USHORT Reserved;
ULONG Value;
LONG ScaledValue;
} ValueData;
};
} HID_DATA, *PHID_DATA;</PRE>
</TD></TR>
</TABLE>
</CENTER>
</P>
<FONT FACE="Verdana" SIZE=2>
<P>When creating the HID reports for sending to the device, the function PackReport() is used. PackReport takes as input a pointer to a HID_DATA structure and the appropriate report buffer. The pointer should point to the first HID_DATA structure in the array that contains the report ID for the report to be created. With this information, PackReport() performs the following steps:</P>
<UL>
<LI>Zeroes out the current report buffer </LI>
<LI>Searches the array of HID_DATA structures looking all data values that match the report ID </LI>
<LI>For each data value structure found, it calls HidP_SetUsageValue to set the value currently stored in the structure into the report. </LI>
<LI>For each button structure found, it calls HidP_SetUsages with the corresponding usage value to set the button state in the report to "On". </LI></UL>
<P>After having looped through the array of HID_DATA structures, the report buffer will have the appropriate report ID set as the first byte in the buffer and is ready to be sent to the device.</P>
<P>In a similar manner, UnpackReport() extracts data from a given report. Like PackReport(), this function receives a report buffer returned by the device and an array of HID_DATA structures that could possibly be filled in. This routine performs the following steps:</P>
<UL>
<LI>Extracts the report ID from the first byte of the report buffer </LI>
<LI>Searches the array of HID_DATA structures looking for all structures that match the report ID </LI>
<LI>For each data value structure found, it call HidP_GetUsageValue and HidP_GetScaledUsageValue to set the Value and ScaledValue fields. </LI>
<LI>For each button structure, UnpackReport() calls HidP_GetUsages() to retrieve all the "On" buttons for that data structure.</LI></UL>
<P>Once finished, the array of HID_DATA structures will contain the new settings based on the information returned in the report buffer.</P>
</FONT>
</FONT><P ALIGN="CENTER"><A HREF="#top"><FONT FACE="Verdana" SIZE=2>Top of page</FONT></A><FONT FACE="Verdana" SIZE=2> </P></FONT>
<TABLE CELLSPACING=0 BORDER=0 WIDTH=624>
<TR><TD VALIGN="MIDDLE" BGCOLOR="#00ffff" HEIGHT=2>
<P></TD>
</TR>
</TABLE>
<FONT FACE="MS Sans Serif" SIZE=1><P>&copy; 1999 Microsoft Corporation</FONT><FONT FACE="Verdana" SIZE=2> </P></FONT></BODY>
</HTML>