the subclass and protocol interface descriptor fields must both be 1.

If USBD_ParseConfigurationDescriptorEx finds a matching interface, it returns the interface descriptor pointer. The next task is to build a suitable Select Configuration URB. The helper function USBD_CreateConfigurationRequestEx allocates and fills this URB. UsbSelectConfiguration specifies an array of USBD_INTERFACE_LIST_ENTRY structures as an input to this routine, with a NULL InterfaceDescriptor field indicating the end of the list. Each structure specifies the interface descriptor. When the Select Configuration URB has been run, the Interface field points to valid data.

The Select Configuration URB is then issued. This may fail if there is not enough USB bandwidth available. If it works, a configuration handle is returned. You can use this to select a different alternate interface setting. UsbKbd stores the configuration handle in its device extension, but does not use it.

UsbKbd does need to store the pipe handle for the HID keyboard's interrupt pipe. For a HID USB keyboard, this is the first and only pipe in the USBD_INTERFACE_INFORMATION structure. UsbKbd makes DebugPrint calls to display other potentially useful information. UsbKbd finally frees the URB and configuration descriptor memory.

Listing 21.5 Selecting a configuration and interface

NTSTATUS UsbSelectConfiguration(IN PUSBKBD_DEVICE_EXTENSION dx) {

 dx->UsbPipeHandle = NULL;

 // Get all first configuration descriptors

 PUSB_CONFIGURATIONDESCRIPTOR Descriptors = NULL;

 ULONG size;

 NTSTATUS status = UsbGetConfigurationDescriptors(dx, Descriptors, 0, size);

 if (!NT_SUCCESS(status)) {

  DebugPrint('UsbGetConfigurationDescriptors failed %x', status);

  FreeIfAllocated(Descriptors);

  return status;

 }

 // Search for an interface with HID keyboard device class

 PUSB_INTERFACE_DESCRIPTOR id = USBD_ParseConfigurationDescriptorEx(Descriptors, Descriptors,

  -1, –1, // Do not search by InterfaceNumber or AlternateSetting

  3, 1, 1); // Search for a HID device, boot protocol, keyboard

 if (id==NULL) {

  DebugPrintMsg('No matching interface found');

  FreeIfAllocated(Descriptors);

  return STATUS_NO_SUCH_DEVICE;

 }

 // Build list of interfaces we are interested in

 USBD_INTERFACE_LIST_ENTRY ilist[2];

 ilist[0].InterfaceDescriptor = id;

 ilist[0].Interface = NULL; // Will point to

                            // urb->UrbUsbSelectConfiguration.Interface

 ilist[1].InterfaceDescriptor = NULL;

 // Create select configuration URB

 PURB urb = USBD_CreateConfigurationRequestEx(Descriptors, ilist);

 // Call the USB driver

 DebugPrintMsg('Select ing configuration');

 status = CallUSBDI(dx, urb);

 // Check statuses

 if (!NT_SUCCESS(status) || !USBD_SUCCESS( urb->UrbHeader.Status)) {

  DebugPrint('status %x URB status %x', status, urb->UrbHeader.Status);

  status = STATUS_UNSUCCESSFUL;

 } else {

  // Select config worked

  DebugPrintMsg('Select configuration worked');

  dx->UsbConfigurationHandle = urb->UrbSelectConfiguration.ConfigurationHandle;

  // Find pipe handle of first pipe,

  // ie interrupt pipe that returns input HID reports

  PUSBD_INTERFACE_INFORMATION InterfaceInfo = &urb->UrbSelectConfiguration.Interface;

  DebugPrint('interface Class %d NumberOfPipes %d', InterfaceInfo->Class, InterfaceInfo->NumberOfPipes);

  if (InterfaceInfo->NumberOfPipes>0) {

   PUSBD_PIPE_INFORMATION pi = &InterfaceInfo->Pipes[0];

   dx->UsbPipeHandle = pi->PipeHandle;

   DebugPrint('PipeHandle = %x', dx->UsbPipeHandle);

   DebugPrint('Pipes[0] EndpointAddress %2x'

    'Interval %dms PipeType %d MaximumTransferSize %c',

    pi->EndpointAddress, pi->Interval, pi->PipeType, pi->MaximumTransferSize);

  }

  if (dx->UsbPipeHandle==NULL) status = STATUS_UNSUCCESSFUL;

 }

 FreeIfAllocated(urb);

 FreeIfAllocated(Descriptors);

 return status;

}

Other Initialization

As mentioned earlier, having selected your configuration and interface, there may be some more steps needed to initialize your USB peripheral. You may want to read other descriptors. The UsbKbd Create IRP handler shows how this might be done by getting the first string descriptor. This in fact just returns the language ID that the device supports. You will have to issue further Get Descriptor requests to get each String Descriptor. The UsbGetSpecifiedDescriptor routine in Usb.cpp can be used to get any descriptor. This routine is also used by the IOCTL_USBKBD_GET_SPECIFIED_DESCRIPTOR handler. The UsbKbdTest test program issues this IOCTL to get the HID Descriptor.

The UsbKbd Create IRP handler, UsbKbdCreate, also retrieves some general information about the USB bus, such as the amount of bandwidth used and the host controller device name. The required Internal IOCTLs are only implemented in Windows 2000, so preprocessor directives are used to remove the body of the function that does this job, UsbGetUsbInfo, when compiled for Windows 98.

Most USB device drivers will now want to talk to their devices over control or other pipes. Shortly, I describe how to perform such transfers.

Deselecting a Configuration

When a driver wants to stop accessing its device, it should deselect its configuration. In UsbKbd, the Close file handle IRP handler does this job. The UsbDeselectConfiguration routine uses UsbBuildSelectConfigurationRequest to build a Select Configuration URB with a NULL configuration descriptor. This disables the configuration and its interfaces.

Interrupt Transfers

The specification for UsbKbd says that it reports raw keyboard data in response to ReadFile Win32 requests. It also must implement a time-out, which defaults to 10 seconds.

Before I describe how to handle the Read IRPs, I must discuss how a USB HID keyboard produces data. The keyboard responds to USB Interrupt transfers with an 8-byte data report. The exact format of this block is discussed in the next chapter on Human Input Devices (HID).

A keyboard report is produced whenever a keypress or release occurs. A report is also produced regularly even if no keypresses occur. This is what happens on the USB bus. The USB class drivers initiate an Interrupt transfer regularly. My USB keyboard specifies that interrupt transfers should occur every 8ms. However, USB HID keyboards implement an idle rate. If there are no state changes during the idle period, the keyboard NAKs each Interrupt request. At the end of the idle period (every 500ms or so), the keyboard returns data regardless. The idle rate can be read and changed using class specific control transfers on the default pipe[53].

UsbKbd issues a Do Bulk or Interrupt Transfer URB to receive any keyboard reports. Keyboard reports are not 'saved up' ready to fulfil any interrupt transfer requests. It is not clear whether the USB class drivers simply do not do any transfers, or whether they do the transfers but just dump the data. Any keypresses before an interrupt transfer is requested are ignored. I understand that the Microsoft keyboard drivers always try to leave two Interrupt transfer requests outstanding; the hope is that this should ensure that no keypresses are lost.

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

0

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

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