joysticks and mice.

The following HID-specific class requests are supported on the default pipe. GET_REPORT and SET_REPORT get or send a specific input, output, or feature report. An idle rate can be defined using SET_IDLE and read using GET_IDLE. SET_PROTOCOL and GET_PROTOCOL are used for boot devices, as described in the following text.

USB Boot Devices

A USB keyboard or mouse must be available during boot so the user can configure the BIOS settings, select the operating system, etc.

The BIOS can read the USB interface descriptor with relative ease. If it finds that iInterfaceSubClass field is 1, the device must be a keyboard or mouse that supports the boot protocol. The Report descriptor for boot keyboards and mice must be in a standard format. Therefore, the BIOS does not have to read and decode the Report descriptor. It simply uses the SET_PROTOCOL command to enable the standard reports.

A boot keyboard input report is usually eight bytes long, in the format described in the previous chapter. Other requirements for a boot keyboard are given in the HID specification.

A mouse boot input report is at least three bytes long. The first three bits are the mouse button states. The second and third bytes are the X and Y displacements.

User Mode HID Clients

The DDK recommends that you talk to HID devices using a user mode application, if possible. This makes sense, as it is usually far easier to write and debug a Win32 application than a device driver. See the next section if you really need to write a HID client device driver. A user mode HID application has three main jobs to do.

1. Find all HID devices.

2. For each HID device, inspect its capabilities to see if it is of interest.

3. Read HID input reports or write HID output reports when needed. Alternatively send and receive feature reports.

The HidUsbUser example in HidUsbUserHidKbdUser.cpp illustrates these tasks. HidUsbUser looks for a HID keyboard, reads input keys until Esc is pressed, and then — hey hey — flashes the keyboard LEDs. HidUsbUser does not care what sort of HID keyboard is attached. All HID keyboards ought to respond in a similar way.

HidUsbUser uses various standard routines. The HidD… routines are available only to user mode applications. The 'parsing routines' HidP… can be used by kernel mode clients as well. In user mode, all these routines are provided by Hid.dll. In kernel mode, the parsing routines are in HidParse.sys.

Finding HID Devices

The HID class driver registers all its device objects as having the HID device interface. HidUsbUser can, therefore, use the GetDeviceViaInterface routine to find all existing HID devices. Instead of using one of the book software GUIDs, HidUsbUser looks for the HID class GUID. Use HidD_GetHidGuid to find this GUID.

GUID HidGuid;

HidD_GetHidGuid(&HidGuid);

Going back to Chapter 5, you will see that GetDeviceViaInterface uses the SetupDi… routines to find a device that matches the given GUID. Previously, I have always looked for the first device with the desired device interface. HidUsbUser, however, looks through all the available HID devices looking for a HID keyboard.

GetDeviceViaInterface opens a handle to each device. HidUsbUser then gets each device's capabilities. If it is a HID keyboard, the main program carries on. Otherwise it closes the device handle, and loops again so that GetDeviceViaInterface can open the next HID device. HidUsbUser reports an error and gives up if a HID keyboard is not found.

Note that HidUsbUser only looks for an appropriate HID device when it starts. As it stands, it will not discover any new HID devices that suddenly are plugged in. The Win32 RegisterDeviceNotification function can be used to listen for Plug and Play Notification events in user mode applications. The Wdm2Notify application described in Chapter 9 shows how to use RegisterDeviceNotification to listen for device interface events. This can easily be modified to spot new HID class devices.

If the HID USB keyboard is unceremoniously unplugged while HidUsbUser is running, all I/O requests fail, and GetLastError returns 1167 (ERROR_PROCESS_ABORTED).

Getting HID Capabilities

HidUsbUser uses GetCapabilities to get a HID device's capabilities, as shown in Listing 23.1. In particular, it looks for a HID keyboard. If it finds a keyboard, GetCapabilities returns a preparsed data pointer and the maximum input and output report sizes. The preparsed data is the HID Report descriptor parsed into a format that makes it easier for the support routines to decode and encode reports. You will need to pass the preparsed data pointer whenever you call any of the HID parsing routines.

Some applications will simply want to look for the device vendor and product IDs. If you know the capabilities of your device, there is no need to do any more checking. However, you still need to get the preparsed data. If possible, you should write code that does not rely on specific vendor and product IDs.

Get the device ID using HidD_GetAttributes. This fills in the supplied HIDD_ATTRIBUTES structure. If successful, the VendorID, ProductID, and VersionNumber fields are filled in. For USB devices, these values come from the USB Device descriptor.

Use HidD_GetPreparsedData to obtain a pointer to the preparsed data. This time, Windows allocates the buffer and returns a pointer to you. Make sure that you keep the preparsed data pointer handy for the rest of your HID operations. When finished with it, free the preparsed data memory using HidD_FreePreparsedData.

The first step when analyzing your device capabilities is to call HidP_GetCaps, which fills in a HIDP_CAPS structure. The UsagePage and Usage fields in there tell you the usage information for the Report descriptor top-level collection. In GetCapabilities, this is all the information that is needed to determine if the device is a keyboard. It checks that these fields are HID_ USAGE_PAGE_GENERIC and HID_USAGE_GENERIC_KEYBOARD, respectively.

The HIDP_CAPS structure also contains much other useful information. First, it contains the maximum length of input, output, and feature reports in three fields. The first two of these values are saved, because they indicate how big a buffer to allocate to send and receive reports. The other HIDP_CAPS fields are used if you check the detailed control capabilities.

Listing 23.1 Getting a HID device's capabilities

bool GetCapabilities(HANDLE hHidKbd, PHIDP_PREPARSED_DATA& HidPreparsedData, USHORT& InputReportLen, USHORT& OutputReportLen) {

 // Get attributes, i.e. find vendor and product ids

 HIDD_ATTRIBUTES HidAttributes;

 if (!HidD_GetAttributes(hHidKbd, &HidAttributes)) {

  printf('XXX Could not get HID attributes ');

  return false;

 }

 printf('HID attributes: VendorID=%04X, ProductID=%04X, VersionNumber=%04X ',

  HidAttributes.VendorID, HidAttributes.ProductID, HidAttributes.VersionNumber);

 // Get preparsed data

 if (!HidD_GetPreparsedData( hHidKbd, &HidPreparsedData)) {

  printf('XXX Could not get HID preparsed data ');

  return false;

 }

 // Work out capabilities

 HIDP_CAPS HidCaps;

 bool found = false;

 NTSTATUS status = HidP_GetCaps(HidPreparsedData, &HidCaps);

 if (status==HIDP_STATUS_SUCCESS) {

  printf('Top level Usage page %d usage %d ', HidCaps.UsagePage, HidCaps.Usage);

  if (HidCaps.UsagePage==HID_USAGE_PAGE_GENERIC && HidCaps.Usage==HD_USAGE_GENERIC_KEYBOARD) {

   printf(' Found HID keyboard ');

   found = true;

  }

  // Remember max lengths of input and output reports

  InputReportLen = HidCaps.InputReportByteLength;

  OutputReportLen = HidCaps.OutputReportByteLength;

  printf('InputReportByteLength %d ', HidCaps.InputReportByteLength);

  printf('OutputReportByteLength %d ', HidCaps.OutputReportByteLength);

  printf('FeatureReportByteLength %d ', HidCaps.FeatureReportByteLength);

  printf('NumberLinkCollectionNodes %d ', HidCaps.NumberLinkCollectionNodes);

  printf('NumberInputButtonCaps %d ', HidCaps.NumberInputButtonCaps);

  printf('NumberInputValueCaps %d ', HidCaps.NumberInputValueCaps);

  printf('NumberOutputButtonCaps %d ', HidCaps.NumberOutputButtonCaps);

  printf('NumberOutputValueCaps %d ', HidCaps.NumberOutputValueCaps);

  printf('NumberFeatureButtonCaps %d ', HidCaps.NumberFeatureButtonCaps);

  printf('NumberFeatureValueCaps %d ', HidCaps.NumberFeatureValueCaps);

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату
×