home | login | register | DMCA | contacts | help |      
mobile | donate |

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z


my bookshelf | genres | recommend | rating of books | rating of authors | reviews | new | | collections | | | add
fantasy
space fantasy
fantasy is horrors
heroic
prose
  military
  child
  russian
detective
  action
  child
  ironical
  historical
  political
western
adventure
adventure (child)
child's stories
love
religion
antique
Scientific literature
biography
business
home pets
animals
art
history
computers
linguistics
mathematics
religion
home_garden
sport
technique
publicism
philosophy
chemistry
close

Loading...

close [X]





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.

Previous examples have used IoSkipCurrentIrpStackLocation to reuse the current IRP stack location, and IoCopyCurrentIrpStackLocationToNext to copy the current stack location to the next when setting a completion routine. We cannot use these routines, as the current stack location is not set up yet.

The IoCallDriver call moves onto the next IRP stack location. CallUSBDI wants to change the stack location that the next lower driver sees. The IoGetNextIrpStackLocation call returns the required IRP stack location pointer[52]. The IoBuildDeviceIoControlRequest call has already set up most of the correct values for this stack location. The required values are set into the Parameters.Others.Argument1 field, etc.

In summary, CallUSBDI does the following jobs.

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. IoBuildSynchronousFsdRequest builds a Read, Write, Flush, or Shutdown IRP that uses an event to signal completion in the same way as IoBuildDeviceIoControlRequest. IoBuildSynchronousFsdRequest must be called at PASSIVE_LEVEL IRQL.

IoBuildAsynchronousFsdRequest works asynchronously, as it does not use an event to signal its completion. Consequently, it can be called at or below DISPATCH_LEVEL. Note that the IRP must be freed using IoFreeIrp. A common way to do this is to attach a completion routine. It is OK to call IoFreeIrp here and then return STATUS_MORE_PROCESSING_REQUIRED.

There are two final macho ways of building IRPs. IoAllocateIrp allocates an IRP, while IoInitializeIrp makes an IRP out of some driver allocated memory. Be very careful to set up all IRP and IRP stack locations correctly. Both of these methods allow you specify the size of the IRP stack size. Use IoGetNextIrpStackLocation to get the first IRP stack location if you need to set up a completion routine. Call IoFreeIrp to free an IRP created by IoAllocateIrp.

The old DDK documentation wrongly says that you can call IoInitializeIrp to reuse an IRP allocated using IoAllocateIrp. You can use this function as long as you preserve the IRP AllocationFlags field, as shown in Chapter 23. In W2000, you can reuse an IRP created with IoAllocateIrp using IoReuseIrp.

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 StartIo routine, which runs at DISPATCH_LEVEL. UsbKbd makes its USB calls direct from its dispatch routines that run at PASSIVE_LEVEL.

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 StartIo routine. A single prebuilt IRP can be reused for each call. Chapter 23 shows how to build, use, and free such an IRP.

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.


| Writing Windows WDM Device Drivers | Initializing a USB Device



: 27
4.7 5




Loading...