break;

  case CmResourceTypeDma:

  case CmResourceTypeDeviceSpecific:

  case CmResourceTypeBusNumber:

  default:

   DebugPrint('RetrieveResources: Unrecognised resource type %d', resource->Type);

   GotFrror = true;

   break;

  }

 }

 // Check we've got the resources we need

 if (GotError /*|| !GotPortOrMemory || !GotInterrupt*/)

  return STATUS_DEVICE_CONFIGURATION_ERROR;

 return STATUS_SUCCESS;

}

Allocating Resources

The StartDevice routine now allocates the system resources it requires. The Wdm2 driver does not need or expect any resources, so this code is commented out. The WdmIo driver covered in Chapters 15-17 does use system resources. These chapters cover Critical section routines and Interrupts in detail. However, I will briefly introduce these topics now.

For memory mapped I/O ports (with the CM_RESOURCE_PORT_MEMORY flag bit set) you must call MmMapIoSpace to get a pointer that can be used by a driver. Do not forget to call MmUnmapIoSpace when the device is stopped.

dx->PortBase = (PUCHAR)MmMapIoSpace(dx->PortStartAddress, dx->PortLength, MmNonCached);

For ordinary I/O ports and memory, simply use the low 32 bits of the PortStartAddress[22].

dx->PortBase = (PUCHAR)dx->PortStartAddress.LowPart;

For interrupts, IoConnectInterrupt is called to install an interrupt handler. You must be ready to handle an interrupt straightaway, so make sure that everything is set up correctly. It is common to be able to disable interrupts by writing some value to a device register. Write a DisableDeviceInterrupts routine to disable interrupts before calling IoConnectInterrupt and call your EnableDeviceInterrupts routine when you are ready to receive interrupts.

Any code that tries to access some real hardware must synchronize its activities with the interrupt handler. It is no good having an interrupt during a complicated device access procedure. Critical section routines solve this problem. You call KeSynchronizeExecution passing the name of the function you want called. KeSynchronizeExecution raises the IRQL to the correct interrupt level and calls your routine. When it has completed, the IRQL is lowered again. Critical section routines obviously need to be in nonpaged memory and cannot access paged memory.

EnableDeviceInterrupts and DisableDeviceInterrupts are usually Critical section routines and will usually need to be called via KeSynchronizeExecution.

Finally, StartDevice powers up its device, as described in the next chapter.

Port and Memory I/O

There are several standard kernel routines to access I/O ports and memory, as this code snippet shows.

void WriteByte(IN PWDM2_DEVICE_EXTENSION dx, IN ULONG offset, IN UCHAR byte) {

 if (dx->PortInIOSpace) WRITE_PORT_UCHAR(dx->PortBase+offset, byte);

 else WRITE_REGISTER_UCHAR(dx->PortBase+offset, byte);

}

Read data using the routines with READ in their name and write data with WRITE routines. PORT routines access I/O registers in I/O port space, while REGISTER routines access registers in memory space. Each type has UCHAR, USHORT, and ULONG variants. Finally, BUFFER variants transfer more than one data value. For example, use the following code to read a set of ULONG values from I/O port space into a buffer.

READ_PORT_BUFFER_ULONG( PortBase, Buffer, Count);

As mentioned previously, synchronize all your hardware accesses with any interrupt routines using Critical section routines and KeSynchronizeExecution. Chapter 16 covers Critical section routines.

Device Access

Most WDM drivers do not, in fact, need to access I/O ports and memory or handle interrupts. Instead, they access their devices using the facilities provided by class drivers. For example, a USB client driver uses the oft-mentioned USB Request Blocks (URBs) to access its device.

When a USB client driver handles a PnP Start Device message, it first waits for the IRP to be processed by lower level drivers. It then typically issues one or more URBs to its device before completing its IRP.

For Stop Device messages, a USB client driver might well want to issue one or more URBs to its device before the IRP is sent down the stack.

Testing Wdm2

The Wdm2Test Win32 console application in the Wdm2exe subdirectory of the book software tests the Wdm2 driver. Wdm1Test is the same as Wdm1Test with only one change, apart from referencing the Wdm2 driver. Wdm2Test halts halfway through, waiting for the user to press a key. While it is waiting, it has a handle to the first Wdm2 device still open.

First, install a Wdm2 device using one of the installation INF files in the book software Wdm2sys directory. It does not matter whether you use the free or checked build, although the checked build produces DebugPrint trace output.

Wdm2Test tests to see if a Query Remove request is rejected while there are any open handles to a Wdm2 device. Run Wdm2Test but do not press a key when it stops halfway through. Now try to remove the Wdm2 device or reinstall its driver. The request should be rejected. Windows will state that the system must be restarted for the operation to complete.

Check that the Wdm2 driver can be removed or reinstalled when Wdm2Test has completed.

It is very difficult to test the other new aspects of Plug and Play support in the Wdm2 driver. First, Stop Device and Surprise Removal requests should never be issued for virtual devices that have no resources. Second, it is not possible to suddenly remove a Wdm2 device in such a way that the Wdm2 driver has to wait for pending I/O to complete. Do appropriate tests for drivers that have resources and can be suddenly removed.

If the test for open handles is not made, then W2000 in fact will still not allow the device to be removed, as it must contain its own internal reference count for the device. However, W98 would let the device be removed. Any I/O requests on open handles would then simply fail.

Actual Plug and Play Messages

The DebugPrint output from Wdm2 shows exactly which Plug and Plug messages are sent by Windows during add device and remove device operations.

Adding a Device

The following PnP calls are made when a Wdm2 device is successfully added, or when the driver for a device is reinstalled. Two of the messages are issued only by Windows 2000.

AddDevice

(W2000) IRP_MN_QUERY_LEGACY_BUS_INFORMATION

IRP_MN_FILTER_RESOURCE_REQUIREMENTS

IRP_MN_START_DEVICE

IRP_MN_QUERY_CAPABILITIES

IRP_MN_QUERY_PNP_DEVICE_STATE

IRP_MN_QUERY_DEVICE_RELATIONS BusRelations

IRP_MN_QUERY_DEVICE_RELATIONS BusRelations (W2000)

Removing a Device

The following PnP messages are sent when a Wdm2 device is successfully removed, or when the driver for a device is reinstalled.

IRP_MN_QUERY_DEVICE_RELATIONS RemoveRelations

IRP_MN_QUERY_REMOVE_DEVICE

IRP_MN_REMOVE_DEVICE

Unknown Status Returns

It is interesting to note the IRP status values are returned by the lower Unknown driver when Wdm2 sends IRPs down the stack. Windows is supposed to set the IRP status return value to STATUS_NOT_SUPPORTED before it is issued to the top of the device stack. If Wdm2 sees this value on return from its PnpDefaultHandler routine, it means that the lower drivers have not processed the IRP or have deliberately not returned STATUS_NOT_SUPPORTED.

For Wdm2, W98 succeeds all the PnP IRPs that it receives.

For Wdm2, W2000 does not process IRP_MN_FILTER_RESOURCE_REQUIREMENTS, IRP_MN_QUERY_PNP_DEVICE_STATE, IRP_MN_QUERY_BUS_INFORMATION, and IRP_MN_QUERY_DEVICE_RELATIONS IRPs.

Other PnP IRPs

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

0

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

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