Flylib.com

Books Software

 
 
 

Windows 98 Compatibility Notes

[Previous] [Next]

Windows 98 Compatibility Notes

Windows 98 never sends an IRP_MN_SURPRISE_REMOVAL request. Consequently, a WDM driver needs to treat an unexpected IRP_MN_REMOVE_DEVICE as indicating surprise removal. The code samples I showed you in this chapter accomplish that by calling AbortRequests and StopDevice when they get this IRP out of the blue.

Windows 98 fails calls to the IoReportTargetDeviceChange function with STATUS_NOT_IMPLEMENTED. It doesn't export the symbol IoReportTargetDeviceChangeAsynchronous at all; a driver that calls that function will simply fail to load in Windows 98. Refer to Appendix A for information about how you can stub this and other missing support functions so as to be able to ship a single driver binary.

The architecture of Windows 98 doesn't lend itself at all well to blocking in kernel mode while waiting for user -mode programs to do things. This fact bit me especially hard in connection with one of my USB sample drivers (USBINT). The test program for this sample opens a handle and issues an asynchronous DeviceIoControl call. If you now unplug the device, what's supposed to happen is this: the driver receives an IRP_MN_SURPRISE_REMOVAL, whereupon it cancels the outstanding DeviceIoControl. The test program then closes its handle. Meanwhile, back in the driver, the REMOVE_DEVICE handler has blocked on a call to IoReleaseRemoveLockAndWait. When the IRP_MJ_CLOSE arrives, the driver will release the last claim on the remove lock and allow the device removal to proceed. This works just fine in Windows 2000, but it hangs Windows 98 because the test program never gets a chance to run in order to close its handle. (We don't get the SURPRISE_REMOVAL in Windows 98, but we do get a REMOVE_DEVICE that serves the same purpose.) A code path through QUERY_REMOVE does not hang the system, however. Moral: don't acquire the remove lock while a handle is open in Windows 98 if your device can be removed by the user without going through the Device Manager API.

[Previous] [Next]

Chapter 7

Reading and Writing Data

All the infrastructure I've described so far in this book leads up to this chapter, where I finally cover how to read and write data from a device. I'll discuss the service functions you call to perform these important operations on a device plugged in to one of the traditional buses, such as PCI (Peripheral Component Interconnect). Since many devices use a hardware interrupt to notify system software about I/O completion or exceptional events, I'll also discuss how to handle an interrupt. Interrupt processing normally requires you to schedule a deferred procedure call (DPC), so I'll describe the DPC mechanism, too. Finally, I'll tell you how to arrange direct memory access (DMA) transfers between your device and main memory.

[Previous] [Next]

Configuring Your Device

In the previous chapter, I discussed the various IRP_MJ_PNP requests that the Plug and Play (PnP) Manager sends you. IRP_MN_START_DEVICE is the vehicle for giving you information about the I/O resources that have been assigned by the PnP Manager for your use. I showed you how to obtain parallel lists of raw and translated resource descriptions and how to call a StartDevice helper function that would have the following prototype:

NTSTATUSStartDevice(PDEVICE_OBJECTfdo,
PCM_PARTIAL_RESOURCE_LISTraw,
PCM_PARTIAL_RESOURCE_LISTtranslated)
{
...
}

The time has now come to explain what to do with these resource lists. In summary, you'll extract descriptions of your assigned resources from the translated list and use those descriptions to create additional kernel objects that give you access to your hardware.

The CM_PARTIAL_RESOURCE_LIST structures contain a count and an array of CM_PARTIAL_RESOURCE_DESCRIPTOR structures, as illustrated in Figure 7-1. Each resource descriptor in the array has a Type member that indicates what type of resource it describes and some additional members that supply the particulars about some allocated resource. You're not going to be surprised by what you find in this array, by the way: if your device uses an IRQ and a range of I/O ports, you'll get two resource descriptors in the array. One of the descriptors will be for your IRQ, and the other will be for your I/O port range. Unfortunately, you can't predict in advance the order in which these descriptors will happen to appear in the array. Consequently, your StartDevice helper function has to begin with a loop that "flattens" the array by extracting resource values into a collection of local variables. You can later use the local variables to deal with the assigned resources in whatever order you please (which, it goes without saying, can be different from the order in which the PnP Manager chose to present them to you).

click to view at full size.

Figure 7-1. Structure of a partial resource list.

In sketch, then, your StartDevice function looks like this:

1

2

3

4

5

6

NTSTATUSStartDevice(PDEVICE_OBJECTfdo,
PCM_PARTIAL_RESOURCE_LISTraw,
PCM_PARTIAL_RESOURCE_LISTtranslated)
{
PDEVICE_EXTENSIONpdx=(PDEVICE_EXTENSION)fdo->DeviceExtension;
  PCM_PARTIAL_RESOURCE_DESCRIPTORresource=
translated->PartialDescriptors;
  ULONGnres=translated->Count;

<localvariabledeclarations>

for(ULONGi=0;i<nres;++i,++resource)
{
  switch(resource->Type)
{
caseCmResourceTypePort:

<saveportinfoinlocalvariables>

break;
caseCmResourceTypeInterrupt:

<saveinterruptinfoinlocalvariables>

break;
caseCmResourceTypeMemory:

<savememoryinfoinlocalvariables>

break;
caseCmResourceTypeDma:

<saveDMAinfoinlocalvariables>

break;
}
}

<uselocalvariablestoconfiguredriver&hardware>

IoSetDeviceInterfaceState(&pdx->ifname,TRUE);
}
  1. I'll use the resource pointer to point to the current resource descriptor in the variable-length array. By the end of the upcoming loop, it will point past the last valid descriptor.
  2. The Count member of a resource list indicates how many resource descriptors are in the PartialDescriptors array.
  3. You should declare appropriate local variables for each of the I/O resources you expect to receive. I'll detail what these would be later on when I discuss how to deal with each of the standard I/O resources.
  4. Within the loop over resource descriptors, you use a switch statement to save resource description information into the appropriate local variables. In the text, I posited a device that needed just an I/O port range and an interrupt, and such a device would expect to find resource types CmResourceTypePort and CmResourceTypeInterrupt . I'm showing the other two standard resource types— CmResourceTypeMemory and CmResourceTypeDma —for thoroughness.
  5. Once outside the loop, the local variables you initialized in the various case labels will hold the resource information you need.
  6. If you registered a device interface during AddDevice, this is the time to enable that interface so that applications can find you and open handles to your device.

If you have more than one resource of a particular type, you need to invent a way to tell the resource descriptors apart. To give a concrete (but entirely fictitious) example, suppose that your device uses one 4-KB range of memory for control purposes and a different 16-KB range of memory as a data capture buffer. You expect to receive two CmResourceTypeMemory resources from the PnP Manager. The control memory is the block that's 4 KB long, whereas the data memory is the block that's 16 KB long. If your device's resources have a distinguishing characteristic such as the size difference in the example, you'll be able to tell which resource is which.

When dealing with multiple resources of the same type, don't assume that the resource descriptors will be in the same order that your configuration space lists them in, and don't assume that the same bus driver will always construct resource descriptors in the same order on every platform or every release of the operating system. The first assumption is tantamount to assuming that the bus driver programmer adopted a particular algorithm, while the second is tantamount to assuming that all bus driver programmers think alike and will never change their minds.

I'll explain how to deal with each of the four standard I/O resource types at appropriate places in the remainder of this chapter. Table 7-1 presents an overview of the critical step(s) for each type of resource.

Table 7-1. Overview of processing steps for I/O resources.

Resource Type Overview
Port Possibly maps port range; saves base port address in device extension
Memory Maps memory range; saves base address in device extension
Dma Calls IoGetDmaAdapter to create an adapter object
Interrupt Calls IoConnectInterrupt to create an interrupt object that points to your interrupt service routine (ISR)