#include "ntddk.h" #include "irqgen.h" //DBG is always defined, but it's 1 or 0 with checked or free build #if DBG #define DBGONLY(_X_) _X_ #define KdPrintLine DbgPrint("%d\n", __LINE__) #define KOH "IRQGEN - " #else #define DBGONLY(_X_) #define KdPrintLine #endif /********************************************************************************* * * DRIVER ENTRY * **********************************************************************************/ NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { PDEVICE_OBJECT deviceObject = NULL; NTSTATUS status; UNICODE_STRING uniNtNameString; UNICODE_STRING uniWin32NameString; KAFFINITY Affinity; ULONG MappedVector; PIRQGEN_DEVICE_EXTENSION extension; DBGONLY(DbgPrint(KOH "loading...\n")); DBGONLY(DbgPrint(KOH "build: " __DATE__ " " __TIME__ "\n")); // Create dispatch points for create/open, close, unload, and ioctl DriverObject->MajorFunction[IRP_MJ_CREATE] = IRQGenOpenClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = IRQGenOpenClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IRQGenDeviceControl; DriverObject->DriverUnload = IRQGenUnload; // Create counted string version of our device name. RtlInitUnicodeString( &uniNtNameString, NT_DEVICE_NAME ); // Create the device object status = IoCreateDevice(DriverObject, sizeof(IRQGEN_DEVICE_EXTENSION), &uniNtNameString, FILE_DEVICE_KRNLDRVR, 0, FALSE, &deviceObject ); if ( NT_SUCCESS(status) ) { // fill in the device extension extension = (PIRQGEN_DEVICE_EXTENSION) deviceObject->DeviceExtension; extension->DeviceObject = deviceObject; extension->PendingIrp = NULL; extension->InterruptOccurred = extension->InterruptRequested = extension->InterruptConnected = extension->HasPendingIrp = FALSE; // tell I/O Manager we want it to read/write data via buffers (NOT relevant to DeviceIOControl) deviceObject->Flags |= DO_BUFFERED_IO; // register DPC routine IoInitializeDpcRequest(deviceObject, IRQGenDpcForIsrRoutine); // Create counted string version of our Win32 device name. RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME ); // Create a link from our device name to a name in the Win32 namespace. status = IoCreateSymbolicLink( &uniWin32NameString, &uniNtNameString ); if (!NT_SUCCESS(status)) { IoDeleteDevice( DriverObject->DeviceObject ); } } return status; } /********************************************************************************* * * OPEN / CLOSE (nothing happens so they're the same) * **********************************************************************************/ NTSTATUS IRQGenOpenClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return STATUS_SUCCESS; } /********************************************************************************* * * UNLOAD * **********************************************************************************/ VOID IRQGenUnload( IN PDRIVER_OBJECT DriverObject ) { UNICODE_STRING uniWin32NameString; PIRQGEN_DEVICE_EXTENSION extension; extension = DriverObject->DeviceObject->DeviceExtension; // Disconnect the interrupt if (extension->InterruptConnected) IoDisconnectInterrupt(extension->InterruptObject); // Create counted string version of our Win32 device name. RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME ); // Delete the link from our device name to a name in the Win32 namespace. IoDeleteSymbolicLink( &uniWin32NameString ); // Finally delete our device object IoDeleteDevice( DriverObject->DeviceObject ); DBGONLY(DbgPrint(KOH "Unloaded\n")); } /********************************************************************************* * * DEVICE CONTROL * **********************************************************************************/ NTSTATUS IRQGenDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS status, ioConnectStatus; PIO_STACK_LOCATION irpSp; PIRQGEN_DEVICE_EXTENSION extension; ULONG ioControlCode; KIRQL irql; KAFFINITY Affinity; ULONG MappedVector; PULONG pIOBuffer; ULONG nPort, InBufferSize; USHORT BusNumber; short BusType; extension = DeviceObject->DeviceExtension; irpSp = IoGetCurrentIrpStackLocation( Irp ); Irp->IoStatus.Information = 0; // Save the control code in the extension so we can use it later ioControlCode = irpSp->Parameters.DeviceIoControl.IoControlCode; switch (ioControlCode) { case IOCTL_IRQGEN_INIT_GEN_DRIVER: DBGONLY(DbgPrint(KOH "IOCTL_IRQGEN_INIT_GEN_DRIVER\n")); InBufferSize = irpSp->Parameters.DeviceIoControl.InputBufferLength; if (InBufferSize < sizeof(IRQGEN_INIT_DATA)) { status = STATUS_INVALID_PARAMETER; DBGONLY(DbgPrint(KOH "IOCTL_IRQGEN_INIT_GEN_DRIVER Failed\n")); break; } if (extension->InterruptConnected) { // this might crash if an interrupt is waiting but it is up to the user to disable // interrupts before re-initializing an IRQ extension->InterruptRequested = FALSE; IoDisconnectInterrupt(extension->InterruptObject); if (extension->HasPendingIrp) { extension->PendingIrp->IoStatus.Status = STATUS_CANCELLED; extension->PendingIrp->IoStatus.Information = 1; IoSetCancelRoutine(extension->PendingIrp, NULL); IoCompleteRequest(extension->PendingIrp, IO_NO_INCREMENT); extension->HasPendingIrp = FALSE; } } pIOBuffer = (PULONG)Irp->AssociatedIrp.SystemBuffer; irql = extension->IRQ = (UCHAR)((PIRQGEN_INIT_DATA)pIOBuffer)->IRQ; BusType = ((PIRQGEN_INIT_DATA)pIOBuffer)->BusType; BusNumber = ((PIRQGEN_INIT_DATA)pIOBuffer)->BusNumber; extension->BaseAddress = ((PIRQGEN_INIT_DATA)pIOBuffer)->BaseAddress; extension->ClearOffset = ((PIRQGEN_INIT_DATA)pIOBuffer)->ClearOffset; extension->Operation = (UCHAR)((PIRQGEN_INIT_DATA)pIOBuffer)->Operation; // // This call will map our IRQ to a system vector. It will also fill // in the IRQL (the kernel-defined level at which our ISR will run), // and affinity mask (which processors our ISR can run on). // // We need to do this so that when we connect to the interrupt, we // can supply the kernel with this information. // MappedVector = HalGetInterruptVector( BusType, // Interface type BusNumber, // Bus number irql, // Bus interrupt level irql, // Bus interrupt vector &irql, // IRQ level &Affinity // Affinity mask ); ioConnectStatus = IoConnectInterrupt( &extension->InterruptObject, IRQGenInterruptServiceRoutine, extension->DeviceObject, NULL, MappedVector, irql, irql, Latched, TRUE, Affinity, FALSE ); status = ioConnectStatus; if ( NT_SUCCESS (status) ) { extension->InterruptConnected = TRUE; } DBGONLY(DbgPrint(KOH "IOCTL_IRQGEN_INIT_GEN_DRIVER Complete\n")); break; case IOCTL_IRQGEN_DETECT_IRQ: if (!extension->InterruptConnected || extension->InterruptRequested) { Irp->IoStatus.Information = 1; status = STATUS_INVALID_DEVICE_REQUEST; } else if (extension->InterruptOccurred) { extension->InterruptOccurred = FALSE; status = STATUS_SUCCESS; } else { extension->PendingIrp = Irp; extension->HasPendingIrp = TRUE; IoMarkIrpPending(Irp); IoSetCancelRoutine(Irp, IRQGenCancelIrp); extension->InterruptRequested = TRUE; return STATUS_PENDING; // do not complete Irp of course } // DBGONLY(DbgPrint(KOH "IOCTL_IRQGEN_DETECT_IRQ, returned STATUS_SUCCESS\n")); break; case IOCTL_IRQGEN_SEND_EOI: /* //This is bad-bad-bad - we're never an ISR, our "ISR" is just a function called by an ISR, and the user-mode code is *certainly* never an ISR! if (extension->IRQ >= 8 && extension->IRQ <= 15) { nPort = 0xA0; WRITE_PORT_UCHAR((PUCHAR)nPort, 0x20); } nPort = 0x20; WRITE_PORT_UCHAR((PUCHAR)nPort, 0x20); */ status = STATUS_SUCCESS; break; case IOCTL_IRQGEN_ABORT: DBGONLY(DbgPrint(KOH "IOCTL_IRQGEN_ABORT\n")); if (!extension->InterruptRequested) status = STATUS_INVALID_DEVICE_REQUEST; else { extension->InterruptRequested = FALSE; extension->HasPendingIrp = FALSE; extension->PendingIrp->IoStatus.Status = STATUS_SUCCESS; extension->PendingIrp->IoStatus.Information = 1; IoSetCancelRoutine(extension->PendingIrp, NULL); IoCompleteRequest(extension->PendingIrp, IO_SOUND_INCREMENT); status = STATUS_SUCCESS; } break; default: DBGONLY(DbgPrint(" Unknown\n")); status = STATUS_INVALID_DEVICE_REQUEST; break; } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } /********************************************************************************* * * INTERRUPT SERVICE ROUTINE * **********************************************************************************/ BOOLEAN IRQGenInterruptServiceRoutine( IN PKINTERRUPT Interrupt, IN OUT PVOID Context ) { PDEVICE_OBJECT DeviceObject; PIRQGEN_DEVICE_EXTENSION extension; unsigned long nPort; DeviceObject = Context; extension = DeviceObject->DeviceExtension; /* if (extension->InterruptRequested && extension->HasPendingIrp) { extension->InterruptRequested = FALSE; extension->HasPendingIrp = FALSE; extension->PendingIrp->IoStatus.Status = STATUS_SUCCESS; extension->PendingIrp->IoStatus.Information = 0; IoSetCancelRoutine(extension->PendingIrp, NULL); IoCompleteRequest(extension->PendingIrp, IO_SOUND_INCREMENT); } else extension->InterruptOccurred = TRUE; */ IoRequestDpc(DeviceObject, NULL, NULL); switch (extension->Operation) { case WRITE_TO_CLEAR: nPort = extension->BaseAddress + extension->ClearOffset; WRITE_PORT_UCHAR((PUCHAR)nPort, 0); break; case READ_TO_CLEAR: nPort = extension->BaseAddress + extension->ClearOffset; READ_PORT_UCHAR((PUCHAR)nPort); break; } return TRUE; } /********************************************************************************* * * DEFERRED PROCEDURE CALL FOR ISR * **********************************************************************************/ VOID IRQGenDpcForIsrRoutine( IN PKDPC Dpc, IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PIRQGEN_DEVICE_EXTENSION extension; unsigned long nPort; extension = DeviceObject->DeviceExtension; if (extension->InterruptRequested && extension->HasPendingIrp) { extension->InterruptRequested = FALSE; extension->HasPendingIrp = FALSE; extension->PendingIrp->IoStatus.Status = STATUS_SUCCESS; extension->PendingIrp->IoStatus.Information = 0; IoSetCancelRoutine(extension->PendingIrp, NULL); IoCompleteRequest(extension->PendingIrp, IO_SOUND_INCREMENT); } else extension->InterruptOccurred = TRUE; } /********************************************************************************* * * CANCEL IRP * **********************************************************************************/ VOID IRQGenCancelIrp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PIRQGEN_DEVICE_EXTENSION extension; extension = DeviceObject->DeviceExtension; Irp->IoStatus.Information = 0; if (Irp == extension->PendingIrp) { extension->HasPendingIrp = FALSE; Irp->IoStatus.Information = 1; } IoReleaseCancelSpinLock( Irp->CancelIrql ); Irp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(Irp, IO_NO_INCREMENT); }