This modified version of the parallel port driver disables interrupts and uses a CustomTimerDpc routine to transfer data at fixed intervals. The code for this example is included on the companion CD and on the companion Web site, http://www.W2KDriverBook.com. Device Extension AdditionsThe Device Extension is modified to include the DPC and Timer objects, plus a polling interval value, stored in microseconds. typedef struct _DEVICE_EXTENSION { ... KDPC pollingDPC; // reserve custom DPC object KTIMER pollingTimer; // and the Timer object LARGE_INTEGER pollingInterval; // timeout counter // in us ... } DEVICE_EXTENSION, *PDEVICE_EXTENSION; // Define the interval between polls of device in us // 100 us #define POLLING_INTERVAL 100 AddDevice ModificationsAddDevice is changed in that IoConnectInterrupt is no longer called for this polling driver. The polling timer and DPC are initialized. NTSTATUS AddDevice(IN PDRIVER_OBJECT pDriverObject, IN PDEVICE_OBJECT pdo) { PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) pDevObj->DeviceExtension; ... // Near the end of the function, after IoCreateDevice // has been called... // // Calculate the polling interval in microseconds // and keep as relative time (negative value) pDevExt->pollingInterval = RtlConvertLongToLargeInteger( POLLING_INTERVAL * -10 ); // // Prepare the polling timer and DPC KeInitializeTimer( &pDevExt->pollingTimer ); // Notice that the DPC routine receives the fdo KeInitializeDpc( &pDevExt->pollingDPC, PollingTimerDpc, (PVOID) pfdo ); ... } TransmitBytes ChangesEach time a transfer is started using the TransmitBytes routine, the timer is initialized so that the polling timer DPC runs when the polling interval expires. Too large a polling interval does not keep the printer busy; too small an interval needlessly wastes CPU time. BOOLEAN TransmitBytes( IN PDEVICE_EXTENSION pDevExt) { // Do all the work necessary to transfer bytes // to the physical device, as usual ... // Then start the polling timer KeSetTimer( &pDevExt->pollingTimer, pDevExt->pollingInterval, &pDevExt->pollingDPC ); return TRUE; } PollingTimerDpc RoutineFinally, the DPC routine itself is presented. The DPC routine executes each time the timer object expires. The DPC has the responsibility for checking on the device if more room is available in the printer buffer, more data is sent. If the IRP's requested transfer is complete (or if the transfer encountered an error from the device), the IRP is completed. VOID PollingTimerDpc( IN PKDPC pDpc, IN PVOID pContext, IN PVOID SysArg1, IN PVOID SysArg2 ) { PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT) pContext; PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) pDevObj->DeviceExtension; // Try to send more data if (!TransmitBytes( pDevExt ) ) { // Transfer complete (normal or error) // Complete the IRP appropriately PIRP pIrp = pDevObj->CurrentIrp; pIrp->IoStatus.Information = pDevExt->xferCount; // Figure out what the final status should be pIrp->IoStatus.Status = STATUS_SUCCESS; // Based on HW error status bit, change Status ... // Now complete the IRP IoCompleteRequest( pIrp, IO_PARALLEL_INCREMENT ); // And request another IRP IoStartNextPacket( pDevObj, FALSE ); }
|