}

NTSTATUS UsbResetPort(IN PUSBKBD_DEVICE_EXTENSION dx) {

 DebugPrintMsg('Resetting port');

 NTSTATUS status = CallUSBDI(dx, NULL, IOCTL_INTERNAL_USB_RESET_PORT);

 DebugPrint('Port reset %x', status);

 return status;

}

NTSTATUS UsbResetDevice(IN PUSBKBD_DEVICE_EXTENSION dx) {

 ULONG PortStatus;

 NTSTATUS status = UsbGetPortStatus(dx, PortStatus);

 if (!NT_SUCCESS(status)) return status;

 // Give up if device not connected

 if (!(PortStatus & USBD_PORT_CONNECTED)) return STATUS_NO_SUCH_DEVICE;

 // Return OK if port enabled

 if (PortStatus & USBD_PORT_ENABLED) return status;

 // Port disabled so attempt reset

 status = UsbResetPort(dx);

 if (!NT_SUCCESS(status)) return status;

 // See if it is now working

 status = UsbGetPortStatus(dx, PortStatus);

 if (!NT_SUCCESS(status)) return status;

 if (!(PortStatus & USBD_PORT_CONNECTED) || !(PortStatus & USBD_PORT_ENABLED)) return STATUS_NO_SUCH_DEVICE;

 return status;

}

Issuing URBs

The Usb.cpp module contains many routines that build and send off URBs for processing by the USB class drivers. UsbGetDeviceDescriptor is used to retrieve the USB device descriptor. This routine is not essential in UsbKbd, but it is used to implement one of the IOCTLs that are available to user mode programs. UsbGetDeviceDescriptor allocates nonpaged memory for the device descriptor and puts the count of bytes transferred in its Size parameter. The routine that calls UsbGetDeviceDescriptor must free the memory using ExFreePool.

UsbGetDeviceDescriptor starts by allocating some nonpaged memory for the URB. The Get Descriptor URB function uses the URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE function code. For this function code, the _URB_CONTROL_DESCRIPTOR_REQUEST structure is used.

Next, UsbGetDeviceDescriptor allocates memory for the device descriptor. Then, it calls UsbBuildGetDescriptorRequest to do the hard work of formatting the URB. The URB pointer and its size are passed as the first two parameters. The next parameter specifies the descriptor type, a device descriptor in this case. The following Index and LanguageId fields are only used when requesting configuration and string descriptors.

The following parameters to UsbBuildGetDescriptorRequest specify the descriptor buffer and its length. This buffer can be specified as a plain pointer to nonpaged memory. Alternatively, an MDL can be used. The final parameter is UrbLink, an optional link to a follow on URB.

UsbGetDeviceDescriptor sends off the built URB using CallUSBDI. It checks both the CallUSBDI NTSTATUS return status and the URB completion status.

The final job for UsbGetDeviceDescriptor is simply to save the count of bytes transferred. This was stored in the URB UrbControlDescriptorRequest.TransferBufferLength field. The USB memory is freed.

Listing 21.4 Getting a USB device descriptor

NTSTATUS UsbGetDeviceDescriptor(IN PUSBKBD_DEVICE_EXTENSION dx, OUT PUSB_DEVICE_DESCRIPT0R& deviceDescriptor, OUT ULONGS Size) {

 // Allocate memory for URB

 USHORT UrbSize = sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST);

 PURB urb = (PURB)ExAllocatePool(NonPagedPool, UrbSize);

 if (urb==NULL) {

  DebugPrintMsg('No URB memory');

  return STATUS_INSUFFICIENT_RESOURCES;

 }

 // Allocate memory for device descriptor

 ULONG sizeDescriptor = sizeof(USB_DEVICE_DESCRIPTOR);

 deviceDescriptor = (PUSB_DEVICE_DESCRIPTOR)ExAllocatePool(NonPagedPool, sizeDescriptor);

 if (deviceDescriptor==NULL) {

  ExFreePool(urb);

  DebugPrintMsg('No descriptor memory');

  return STATUS_INSUFFICIENT_RESOURCES;

 }

 // Build the Get Descriptor URB

 UsbBuildGetDescriptorRequest(urb, UrbSize,

  USB_DEVICE_QESCRIPTOR_TYPE, 0, 0, // Types, Index & LanguageId

  deviceDescriptor, NULL, sizeDescriptor, // Transfer buffer

  NULL); // Link URB

 // Call the USB driver

 DebugPrintMsg('Getting device descriptor');

 NTSTATUS 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;

 }

 // Remember count of bytes actually transferred

 Size = urb->UrbControlDescriptorRequest.TransferBufferLength;

 ExFreePool(urb);

 return status;

}

Selecting an Interface

To start using a device, your driver must select one interface within one configuration. This is more of a job than you might expect. Routine UsbSelectConfiguration performs these steps, as shown in Listing 21.5.

1. Get the configuration, interface, endpoint descriptors, etc.

2. Find the appropriate interface descriptor.

3. Issue a select configuration URB.

4. Save the configuration handle and handles to any pipes that you use.

UsbGetConfigurationDescriptors (not listed) is used to retrieve all the descriptors that are needed. This routine issues a Get Descriptor request twice. The first call returns just the basic configuration descriptor. The wTotalLength field in there tells you how much memory to allocate to retrieve all the associated descriptors.

USBD_ParseConfigurationDescriptorEx is used to find an interface. The parameters to this routine specify the criteria that must match. In this case UsbKbd is not interested in matching the interface number or alternate settings, but is interested in the device class. The HID device class is 3 (USB_DEVICE_CLASS_HUMAN_INTERFACE). To match a HID keyboard

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

0

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

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