Previous examples have used
The
In summary,
1. Initializes an IRP completion event
2. Builds an Internal IOCTL
3. Stores the URB pointer, etc., in the IRP's next stack location
4. Calls the next driver
5. If the request is still pending, wait for the completion event to become signalled. The KeWaitForSingleObjectWaitReason parameter must be set to Suspended.
Listing 21.2 Calling USBDI
NTSTATUS CallUSBDI(IN PUSBKBD_DEVICE_EXTENSION dx, IN PVOID UrbEtc,
IN ULONG IoControlCode/*=IOCTL_INTERNAL_USB_SUBMIT_URB*/,
IN ULONG Arg2/*=0*/) {
IO_STATUS_BLOCK IoStatus;
KEVENT event;
// Initialise IRP completion event
KeInitializeEvent(&event, NotificationEvent, FALSE);
// Build Internal IOCTL IRP
PIRP Irp = IoBuildDeviceIoControlRequest(
IoControlCode, dx->NextStackDevice,
NULL, 0, // Input buffer
NULL, 0, // Output buffer
TRUE, &event, &IoStatus);
// Get IRP stack location for next driver down (already set up)
PIO_STACK_LOCATION NextIrpStack = IoGetNextIrpStackLocation(Irp);
// Store pointer to the URB etc
NextIrpStack->Parameters.Others.Argument1 = UrbEtc;
NextIrpStack->Parameters.Others.Argument2 = (PVOID)Arg2;
// Call the driver and wait for completion if necessary
NTSTATUS status = IoCallDriver(dx->NextStackDevice, Irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
status = IoStatus.Status;
}
// return IRP completion status
return status;
}
Other IRP Allocations
Other kernel calls can be used to allocate IRPs.
There are two final macho ways of building IRPs.
The old DDK documentation wrongly says that you can call
Multiple USBDI Calls
If you were reading carefully, you will have noticed that the Call USBDI routine can only be called at PASSIVE_LEVEL. This means that it cannot be called from a
In UsbKbd, it is possible for a user program to issue two overlapped read requests 'simultaneously'. This might easily result in the USB class drivers being sent two IRPs for processing at the same time. There is nothing in the documentation that says this is a problem. I suspect that it is not, as the USB class driver will almost certainly serialize requests.
If you feel that you ought to serialize your USBDI calls, you will have to use a
In many cases, it might be useful to send off a series of IRPs to the USB class drivers. As there will usually be a spare IRP queued up for processing, no incoming data will be lost.
Talking USB
There are several jobs that a USB client driver must do to initialize its connection to its device. The UsbKbd driver does these jobs in its Create IRP handler. However, most USB drivers will want to initialize their device when processing the
1. Check device enabled. Reset and enable the device, if necessary.
2. Select one interface in one of the configurations.
3. Possibly read other descriptors, such as the string-, class-, or vendor-specific descriptors.
4. Talk to your device to issue whatever commands are relevant, get device status, and initialize pipes.
Device Reset
Listing 21.3 shows how UsbKbd resets its device in routine
You may need to reset the port if some drastic communication problem has arisen with your device (e.g., if control transfers to the default pipe keep failing). However, if another pipe stalls, do not reset the port.
If a pipe stalls or USBDI detects a timeout, the pipe becomes
However, the default pipe can never be halted. If a timeout or stall occurs here, an error is reported, which is detectable using the USBD_ERROR macro, but not USBD_HALTED. The halt condition is cleared automatically to give transfers a chance of succeeding on this pipe. If they keep failing, you will have to reset the port.
Listing 21.3 Device reset routines
NTSTATUS UsbGetPortStatus(IN PUSBKBD_DEVICE_EXTENSION dx, OUT ULONG& PortStatus) {
DebugPrintMsg('Getting port status');
PortStatus = 0;
NTSTATUS status = CallUSBDI(dx, &PortStatus, IOCTL_INTERNAL_USB_GET_PORT_STATUS);
DebugPrint('Got port status %x', PortStatus);
return status;