Starting and Stopping Your Device

Starting and Stopping Your Device

Working with the bus driver, the PnP Manager automatically detects hardware and assigns I/O resources in Windows XP and Windows 98/Me. Most modern devices have PnP features that allow system software to detect them automatically and to electronically determine which I/O resources they require. In the case of legacy devices that have no electronic means of identifying themselves to the operating system or of expressing their resource requirements, the registry database contains the information needed for the detection and assignment operations.

NOTE
I find it hard to give an abstract definition of the term I/O resource that isn t circular (for example, a resource used for I/O), so I ll give a concrete one instead. The WDM encompasses four standard I/O resource types: I/O ports, memory registers, direct memory access (DMA) channels, and interrupt requests.

When the PnP Manager detects hardware, it consults the registry to learn which filter drivers and function drivers will manage the hardware. As I discussed in Chapter 2, the PnP Manager loads these drivers (if necessary one or more of them might already be present, having been called into memory on behalf of some other hardware) and calls their AddDevice functions. The AddDevice functions, in turn, create device objects and link them into a stack. At this point, the stage is set for the PnP Manager, working with all of the device drivers, to assign I/O resources.

The PnP Manager initially creates a list of resource requirements for each device and allows the drivers to filter that list. I m going to ignore the filtering step for now because not every driver will need to participate in this step. Given a list of requirements, the PnP Manager can then assign resources so as to harmonize the potentially conflicting requirements of all the hardware present on the system. Figure 6-2 illustrates how the PnP Manager can arbitrate between two different devices that have overlapping requirements for an interrupt request number, for example.

figure 6-2 arbitration of conflicting i/o resource requirements.

Figure 6-2. Arbitration of conflicting I/O resource requirements.

IRP_MN_START_DEVICE

Once the resource assignments are known, the PnP Manager notifies each device by sending it a PnP request with the minor function code IRP_MN_START_DEVICE. Filter drivers are typically not interested in this IRP, so they usually pass the request down the stack by using the DefaultPnpHandler technique I showed you earlier in IRP_MJ_PNP Dispatch Function. Function drivers, on the other hand, need to do a great deal of work on the IRP to allocate and configure additional software resources and to prepare the device for operation. This work needs to be done, furthermore, at PASSIVE_LEVELafter the lower layers in the device hierarchy have processed this IRP.

You might implement IRP_MN_START_DEVICE in a subdispatch routine reached from the DispatchPnp dispatch routine shown earlier that has the following skeletal form:

NTSTATUS HandleStartDevice(PDEVICE_OBJECT fdo, PIRP Irp) { 

Irp->IoStatus.Status = STATUS_SUCCESS;

NTSTATUS status = ForwardAndWait(fdo, Irp); if (!NT_SUCCESS(status)) return CompleteRequest(Irp, status);

PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);

status = StartDevice(fdo, <additional args>);

EnableAllInterfaces (pdx, True);

return CompleteRequest(Irp, status); }

  1. The bus driver uses the incoming setting of IoStatus.Status to determine whether upper-level drivers have handled this IRP. The bus driver makes a similar determination for several other minor functions of IRP_MJ_PNP. We therefore need to initialize the Status field of the IRP to STATUS_SUCCESS before passing it down.

  2. ForwardAndWait is the function I showed you in Chapter 5 in connection with IRP-handling scenario 7 (synchronous pass down). The function returns a status code. If the status code denotes some sort of failure in the lower layers, we propagate the code back to our own caller. Because our completion routine returned STATUS_MORE_PRO CESSING_REQUIRED, we halted the completion process inside IoCompleteRequest. Therefore, we have to complete the request all over again, as shown here.

  3. Our configuration information is buried inside the stack parameters. I ll show you where a bit further on.

  4. StartDevice is a helper routine you write to handle the details of extracting and dealing with configuration information. In my sample drivers, I ve placed it in a separate source module named READWRITE.CPP. I ll explain shortly what arguments you would pass to this routine besides the address of the device object.

  5. EnableAllInterfaces enables all the device interfaces that you registered in your AddDevice routine. This step allows applications to find your device when they use SetupDiXxx functions to enumerate instances of your registered interfaces.

  6. Since ForwardAndWait short-circuited the completion process for the START_DEVICE request, we need to complete the IRP a second time. In this example, I m using an overloaded version of Complete Request that doesn t change IoStatus.Information, in accordance with the DDK rules for handling PnP requests.

You might guess (correctly!) that the IRP_MN_START_DEVICE handler has work to do that concerns the transition from the initial STOPPED state to the WORKING state. I can t explain that yet because I need to first explain the ramifications of other PnP requests on state transitions, IRP queuing, and IRP cancellation. So I m going to concentrate for a while on the configuration aspects of the PnP requests.

The I/O stack location s Parameters union has a substructure named StartDevice that contains the configuration information you pass to the StartDevice helper function. See Table 6-2.

Table 6-2. Fields in the Parameters.StartDevice Substructure of an I/O Stack Location

Field Name

Description

AllocatedResources

Contains raw resource assignments

AllocatedResourcesTranslated

Contains translated resource assignments

Both AllocatedResources and AllocatedResourcesTranslated are instances of the same kind of data structure, called a CM_RESOURCE_LIST. This seems like a very complicated data structure if you judge only by its declaration in WDM.H. As used in a start device IRP, however, all that remains of the complication is a great deal of typing. The lists will have just one entry, a CM_PARTIAL_RESOURCE_LIST that describes all of the I/O resources assigned to the device. You can use statements like the following to access the two lists:

PCM_PARTIAL_RESOURCE_LIST raw, translated; raw = &stack->Parameters.StartDevice .AllocatedResources->List[0].PartialResourceList; translated = &stack->Parameters.StartDevice .AllocatedResourcesTranslated->List[0].PartialResourceList;

The only difference between the last two statements is the reference to either the AllocatedResources or AllocatedResourcesTranslated member of the parameters structure.

The raw and translated resource lists are the logical arguments to send to the StartDevice helper function, by the way:

status = StartDevice(fdo, raw, translated);

There are two different lists of resources because I/O buses and the CPU can address the same physical hardware in different ways. The raw resources contain numbers that are bus-relative, whereas the translated resources contain numbers that are system-relative. Prior to the WDM, a kernel-mode driver might expect to retrieve raw resource values from the registry, the Peripheral Component Interconnect (PCI) configuration space, or some other source, and to translate them by calling routines such as HalTranslateBusAddress and HalGetInterruptVector. See, for example, Art Baker s The Windows NT Device Driver Book: A Guide for Programmers (Prentice Hall, 1997), pages 122-62. Both the retrieval and translation steps are done by the PnP Manager now, and all a WDM driver needs to do is access the parameters of a start device IRP as I m now describing.

What you actually do with the resource descriptions inside your Start Device function is a subject for Chapter 7.

IRP_MN_STOP_DEVICE

The stop device request tells you to shut your device down so that the PnP Manager can reassign I/O resources. At the hardware level, shutting down involves pausing or halting current activity and preventing further interrupts. At the software level, it involves releasing the I/O resources you configured at start device time. Within the framework of the dispatch/subdispatch architecture I ve been illustrating, you might have a subdispatch function like this one:

NTSTATUS HandleStopDevice(PDEVICE_OBJECT fdo, PIRP Irp) { 

<complicated stuff>

StopDevice(fdo, oktouch); Irp->IoStatus.Status = STATUS_SUCCESS;

return DefaultPnpHandler(fdo, Irp); }

  1. Right about here, you need to insert some more or less complicated code that concerns IRP queuing and cancellation. I ll show you the code that belongs in this spot further on in this chapter in While the Device Is Stopped.

  2. In contrast with the start device case, in which we passed the request down and then did device-dependent work, here we do our device-dependent stuff first and then pass the request down. The idea is that our hardware will be quiescent by the time the lower layers see this request. I wrote a helper function named StopDevice to do the shutdown work. The second argument indicates whether it will be OK for StopDevice to touch the hardware if it needs to. Refer to the sidebar Touching the Hardware When Stopping the Device for an explanation of how to set this argument.

  3. We always pass PnP requests down the stack. In this case, we don t care what the lower layers do with the request, so we can simply use the DefaultPnpHandler code to perform the mechanics.

The StopDevice helper function called in the preceding example is code you write that essentially reverses the configuration steps you took in Start Device. I ll show you that function in the next chapter. One important fact about the function is that you should code it in such a way that it can be called more than once for a single call to StartDevice. It s not always easy for a PnP IRP handler to know whether you ve already called StopDevice, but it is easy to make StopDevice proof against duplicative calls.

Touching the Hardware When Stopping the Device

In the skeleton of HandleStopDevice, I used an oktouch variable that I didn t show you how to initialize. In the scheme I m teaching you in this book for writing a driver, the StopDevice function gets a BOOLEAN argument that indicates whether it should be safe to address actual I/O operations to the hardware. The idea behind this argument is that you might want to send certain instructions to your device as part of your shutdown protocol, but there might be some reason why you can t. You might want to tell your Personal Computer Memory Card International Association (PCMCIA) modem to hang up the phone, for example, but there s no point in trying if the end user has already removed the modem card from the computer.

There s no certain way to know whether your hardware is physically connected to the computer except by trying to access it. Microsoft recommends, however, that if you succeeded in processing a START_DEVICE request, you should go ahead and try to access your hardware when you process STOP_DEVICE and certain other PnP requests. When I discuss how you track PnP state changes later in this chapter, I ll honor this recommendation by setting the oktouch argument to TRUE if we believe that the device is currently working and FALSE otherwise.

IRP_MN_REMOVE_DEVICE

Recall that the PnP Manager calls the AddDevice function in your driver to notify you about an instance of the hardware you manage and to give you an opportunity to create a device object. Instead of calling a function to do the complementary operation, however, the PnP Manager sends you a PnP IRP with the minor function code IRP_MN_REMOVE_DEVICE. In response to that, you ll do the same things you did for IRP_MN_STOP_DEVICE to shut down your device, and then you ll delete the device object:

NTSTATUS HandleRemoveDevice(PDEVICE_OBJECT fdo, PIRP Irp) { PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; <complicated stuff> DeregisterAllInterfaces(pdx); StopDevice(fdo, oktouch); Irp->IoStatus.Status = STATUS_SUCCESS; NTSTATUS status = DefaultPnpHandler(fdo, Irp); RemoveDevice(fdo); return status; }

This fragment looks similar to HandleStopDevice, with a couple of additions. DeregisterAllInterfaces will disable any device interfaces you registered (probably in AddDevice) and enabled (probably in StartDevice), and it will release the memory occupied by their symbolic link names. RemoveDevice will undo all the work you did inside AddDevice. For example:

VOID RemoveDevice(PDEVICE_OBJECT fdo) { PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; 

IoDetachDevice(pdx->LowerDeviceObject);

IoDeleteDevice(fdo); }

  1. This call to IoDetachDevice balances the call AddDevice made to IoAttachDeviceToDeviceStack.

  2. This call to IoDeleteDevice balances the call AddDevice made to IoCreateDevice. Once this function returns, you should act as if the device object no longer exists. If your driver isn t managing any other devices, it will shortly be unloaded from memory too.

Note, by the way, that you don t get a stop device request followed by a remove device request. The remove device request implies a shutdown, so you do both pieces of work in reply.

IRP_MN_SURPRISE_REMOVAL

Sometimes the end user has the physical ability to remove a device without going through any user interface elements first. If the system detects that such a surprise removal has occurred, or that the device appears to be broken, it sends the driver a PnP request with the minor function code IRP_MN_SURPRISE_REMOVAL. It will later send an IRP_MN_REMOVE_DEVICE. Unless you previously set the SurpriseRemovalOK flag while processing IRP_MN_QUERY_CAPABILITIES (as I ll discuss in Chapter 8), some platforms also post a dialog box to inform the user that it s potentially dangerous to yank hardware out of the computer.

In response to the surprise removal request, a device driver should disable any registered interfaces. This will give applications a chance to close handles to your device if they re on the lookout for the notifications I discuss later in PnP Notifications. Then the driver should release I/O resources and pass the request down:

NTSTATUS HandleSurpriseRemoval(PDEVICE_OBJECT fdo, PIRP Irp) { PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; <complicated stuff> EnableAllInterfaces(pdx, FALSE); StopDevice(fdo, oktouch); Irp->IoStatus.Status = STATUS_SUCCESS; return DefaultPnpHandler(fdo, Irp); }

Whence IRP_MN_SURPRISE_REMOVAL?

The surprise removal PnP notification doesn t happen as a simple and direct result of the end user yanking the device from the computer. Some bus drivers can know when a device disappears. For example, removing a universal serial bus (USB) device generates an electronic signal that the bus driver notices. For many other buses, however, there isn t any signal to alert the bus driver. The PnP Manager therefore relies on other methods to decide that a device has disappeared.

A function driver can signal the disappearance of its device (if it knows) by calling IoInvalidateDeviceState and then returning any of the values PNP_DEVICE_FAILED, PNP_DEVICE_REMOVED, or PNP_DE VICE_DIS ABLED from the ensuing IRP_MN_QUERY_PNP_DEVICE_STATE. You might want to do this in your own driver if to give one example of many your interrupt service routines (ISRs) read all 1 bits from a status port that normally returns a mixture of 1s and 0s. More commonly, a bus driver calls IoInvalidateDeviceRelations to trigger a re-enumeration and then fails to report the newly missing device. It s worth knowing that when the end user removes a device while the system is hibernating or in another low-power state, when power is restored, the driver receives a series of power management IRPs before it receives the IRP_MN_SURPRISE_REMOVAL request.

What these facts mean, practically speaking, is that your driver should be able to cope with errors that might arise from having your device suddenly not present.



Programming the Microsoft Windows Driver Model
Programming the Microsoft Windows Driver Model
ISBN: 0735618038
EAN: 2147483647
Year: 2003
Pages: 119
Authors: Walter Oney

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net