IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO Used internally by hub driver
IOCTL_INTERNAL_USB_GET_HUB_NAME Get the device name of the USB hub
IOCTL_INTERNAL_USB_GET_BUS_INFO Fills in a USB_BUS_NOTIFICATION structure (W2000 only)
IOCTL_INTERNAL_USB_GET_CONTROLLER_NAME Get the host controller device name (W2000 only)
URBs

The most important Internal IOCTL is IOCTL_INTERNAL_USB_SUBMIT_URB, which lets you submit a USB Request Block (URB) for processing by the USB class drivers. There are thirty-odd different URB function codes. USB clients use URBs to do most of their hard work.

The URB structure itself is a union of some 16 different _URB_* structures, as shown in Listing 21.1. Each function code uses one of these other URB structures to detail its input or output parameters. All URB structures begin with a common header _URB_HEADER structure. The header Length and Function fields must be filled in before calling the USB Device Interface. The result of processing the URB is returned in the Status field.

Listing 21.1 URB structures

typedef struct _URB {

 union {

  struct _URB_HEADER UrbHeader;

  struct _URB_SELECT_INTERFACE UrbSelectInterface;

  struct _URB_SELECT_CONFIGURATION UrbSelectConfiguration;

  // …

 };

} URB, *PURB;

struct _URB_HEADER {

 USHORT Length;

 USHORT Function;

 USBD_STATUS Status;

 // …

};

The Status field uses its top two bits as a State code to indicate how the request completed. Table 21.3 shows the possible values along with the names of macros that can be used to detect these conditions. The rest of the Status field is filled with more detailed error codes. Some error codes are internal system USB errors (e.g., no memory).

Table 21.3 URB Status field State code bits

State code bits Interpretation Macro
00 Completed successfully USBD_SUCCESS
01 Request is pending USBD_PENDING
10 Error, endpoint not stalled USBD_ERROR
11 Error, endpoint stalled USBD_ERROR or USBD_HALTED

To make it easier to construct suitable URBs, various build macros are provided, such as UsbBuildGetDescriptorRequest, which fill in a preallocated URB. Other useful routines both allocate the memory for a URB and fill it in.

The reference section towards the end of this chapter lists the URB function codes. The reference section also details the other crucial USB structures. However, this chapter first illustrates how to perform most common USB actions by describing how these jobs are done in the UsbKbd driver.

Several URB structures have a UrbLink field. If non-NULL, this specifies a pointer to a URB that is processed if the current one completes successfully.

Calling USBDI

Listing 21.2 shows the Call USBDI routine in Usb.cpp. This is used to issue all the Internal lOCTLs to the USB system class drivers. It has default parameters that make it easy to ask for a URB to be processed.

CallUSBDI has to create a new IRP for the Internal IOCTL, fill in the IRP, and send off the IRP down the device stack to the USB system drivers. Further, it then waits until the IRP has been processed. CallUSBDI can only be called at PASSIVE_LEVEL

The USB Internal IOCTLs do not use the standard input and output IRP stack locations. Instead, the stack Parameters.Others.Argument1 field is set to the URB pointer, etc. The Parameters.Others.Argument2 field is used for one of the USB IOCTLs.

Allocating IRPs

If you want to call a lower driver, it is usually simplest to reuse an existing IRP. You simply fill in the next IRP stack location with the correct function codes and parameters and call the next driver down the stack.

In some cases however, you will need to build an IRP from scratch. For example, you may wish to generate an IRP in your DriverEntry routine. Alternatively, you may process a large incoming request by splitting it into several different IRPs.

UsbKbd could reuse an existing IRP. However, it is straightforward to allocate a new IOCTL IRP. The CallUSBDI routine keeps the entire IRP allocation process wrapped up in one neat location using this technique.

Building and issuing an IOCTL IRP is made particularly easy with the IoBuildDeviceIoControlRequest call. If you pass an initialized event, you can wait for the IRP to complete simply by waiting for the event to become signalled. You do not need to set up a completion routine.

IoBuilDeviceIoControlRequest can be used to make both IOCTL and Internal IOCTL IRPs, depending on the InternalDeviceIoControl parameter. In a similar way to the Win32 DeviceIoControl call, both input and output buffers can be used. The IOCTL control code must use buffered I/O.

Internally, it seems as though IoBuildDeviceIoControlRequest allocates some nonpaged memory to hold the combined input and output buffer, to make it work exactly like a standard IOCTL call. IoBuildDeviceIoControlRequest copies the input buffer there initially. It must set its own completion routine that copies the data back into your given output buffer (as well as setting the event and freeing the IRP memory).

The USB Internal IOCTLs do not use the standard input and output buffers[51]. Instead, you have to set up the next stack location Parameters.Others.Argument1 field. This is more complicated than you might think.

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

0

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

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