#include "ntddk.h" #include "irqgenm.h" /********************************************************************************* * * 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; int i; PIRQGENM_DEVICE_EXTENSION extension; // Create dispatch points for create/open, close, unload, and ioctl DriverObject->MajorFunction[IRP_MJ_CREATE] = IRQGENMOpenClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = IRQGENMOpenClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IRQGENMDeviceControl; DriverObject->DriverUnload = IRQGENMUnload; // Create counted string version of our device name. RtlInitUnicodeString( &uniNtNameString, NT_DEVICE_NAME ); // Create the device object status = IoCreateDevice(DriverObject, sizeof(IRQGENM_DEVICE_EXTENSION), &uniNtNameString, FILE_DEVICE_KRNLDRVR, 0, FALSE, &deviceObject ); if ( NT_SUCCESS(status) ) { // fill in the device extension extension = (PIRQGENM_DEVICE_EXTENSION) deviceObject->DeviceExtension; extension->DeviceObject = deviceObject; for (i=0; i<16; i++) { extension->PendingIrp[i] = NULL; extension->InterruptObject[i] = NULL; extension->InterruptRequested[i] = extension->InterruptOccurred[i] = extension->InterruptConnected[i] = extension->HasPendingIrp[i] = FALSE; extension->BaseAddress[i] = extension->Operation[i] = extension->ClearOffset[i] = 0; } // 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, IRQGENMDpcForIsrRoutine); // 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 IRQGENMOpenClose( 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 IRQGENMUnload( IN PDRIVER_OBJECT DriverObject ) { UNICODE_STRING uniWin32NameString; PIRQGENM_DEVICE_EXTENSION extension; int i; extension = DriverObject->DeviceObject->DeviceExtension; // Disconnect the interrupt(s) for (i=0; i<16; i++) if (extension->InterruptConnected[i]) IoDisconnectInterrupt(extension->InterruptObject[i]); // 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 ); } /********************************************************************************* * * DEVICE CONTROL * **********************************************************************************/ NTSTATUS IRQGENMDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS status; PIO_STACK_LOCATION irpSp; PIRQGENM_DEVICE_EXTENSION extension; ULONG ioControlCode; 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_IRQGENM_INIT_GEN_DRIVER: status = IRQGENMInitDriver(DeviceObject, Irp); break; case IOCTL_IRQGENM_DETECT_IRQ: status = IRQGENMDetectIRQ(DeviceObject, Irp); break; case IOCTL_IRQGENM_SEND_EOI: status = IRQGENMSendEOI(DeviceObject, Irp); break; case IOCTL_IRQGENM_DISCONNECT_IRQ: status = IRQGENMDisconnectIRQ(DeviceObject, Irp); break; case IOCTL_IRQGENM_ABORT: status = IRQGENMAbort(DeviceObject, Irp); break; default: status = STATUS_INVALID_DEVICE_REQUEST; break; } if (status != STATUS_PENDING) // do not complete Irp if pending { Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return status; } /********************************************************************************* * * INIT DRIVER * **********************************************************************************/ NTSTATUS IRQGENMInitDriver( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS status, ioConnectStatus; PIO_STACK_LOCATION irpSp; PIRQGENM_DEVICE_EXTENSION extension; KIRQL irql; KAFFINITY Affinity; ULONG MappedVector; PULONG pIOBuffer; ULONG InBufferSize; USHORT BusNumber; short BusType; UCHAR IRQ; extension = DeviceObject->DeviceExtension; irpSp = IoGetCurrentIrpStackLocation( Irp ); InBufferSize = irpSp->Parameters.DeviceIoControl.InputBufferLength; if (InBufferSize < sizeof(IRQGENM_INIT_DATA)) { status = STATUS_INVALID_PARAMETER; return status; } pIOBuffer = (PULONG)Irp->AssociatedIrp.SystemBuffer; if ((IRQ = ((PIRQGENM_INIT_DATA)pIOBuffer)->IRQ) > 15) { return STATUS_INVALID_PARAMETER; } if (extension->InterruptConnected[IRQ]) { // 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[IRQ] = FALSE; IoDisconnectInterrupt(extension->InterruptObject[IRQ]); if (extension->HasPendingIrp[IRQ]) { extension->PendingIrp[IRQ]->IoStatus.Status = STATUS_CANCELLED; extension->PendingIrp[IRQ]->IoStatus.Information = 1; IoSetCancelRoutine(extension->PendingIrp[IRQ], NULL); IoCompleteRequest(extension->PendingIrp[IRQ], IO_NO_INCREMENT); extension->HasPendingIrp[IRQ] = FALSE; } } irql = IRQ; BusType = ((PIRQGENM_INIT_DATA)pIOBuffer)->BusType; BusNumber = ((PIRQGENM_INIT_DATA)pIOBuffer)->BusNumber; extension->BaseAddress[IRQ] = ((PIRQGENM_INIT_DATA)pIOBuffer)->BaseAddress; extension->ClearOffset[IRQ] = ((PIRQGENM_INIT_DATA)pIOBuffer)->ClearOffset; extension->Operation[IRQ] = ((PIRQGENM_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[IRQ], IRQGENMInterruptServiceRoutine, extension->DeviceObject, NULL, MappedVector, irql, irql, Latched, FALSE, Affinity, FALSE ); status = ioConnectStatus; if ( NT_SUCCESS (status) ) { extension->InterruptConnected[IRQ] = TRUE; } return status; } /********************************************************************************* * * DETECT IRQ * **********************************************************************************/ NTSTATUS IRQGENMDetectIRQ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS status; PIRQGENM_DEVICE_EXTENSION extension; PULONG pIOBuffer; UCHAR IRQ; extension = DeviceObject->DeviceExtension; pIOBuffer = (PULONG)Irp->AssociatedIrp.SystemBuffer; if ((IRQ = *pIOBuffer) > 15) return STATUS_INVALID_PARAMETER; if (!extension->InterruptConnected[IRQ] || extension->InterruptRequested[IRQ]) { Irp->IoStatus.Information = 1; status = STATUS_INVALID_DEVICE_REQUEST; } else if (extension->InterruptOccurred[IRQ]) { extension->InterruptOccurred[IRQ] = FALSE; status = STATUS_SUCCESS; } else { extension->PendingIrp[IRQ] = Irp; extension->HasPendingIrp[IRQ] = TRUE; IoMarkIrpPending(Irp); IoSetCancelRoutine(Irp, IRQGENMCancelIrp); extension->InterruptRequested[IRQ] = TRUE; status = STATUS_PENDING; } return status; } /********************************************************************************* * * ABORT * **********************************************************************************/ NTSTATUS IRQGENMAbort( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS status; PIRQGENM_DEVICE_EXTENSION extension; PULONG pIOBuffer; UCHAR IRQ; extension = DeviceObject->DeviceExtension; pIOBuffer = (PULONG)Irp->AssociatedIrp.SystemBuffer; if ((IRQ = *pIOBuffer) > 15) return STATUS_INVALID_PARAMETER; if (!extension->InterruptRequested[IRQ]) { Irp->IoStatus.Information = 1; return STATUS_INVALID_DEVICE_REQUEST; } else { extension->InterruptRequested[IRQ] = FALSE; extension->HasPendingIrp[IRQ] = FALSE; extension->PendingIrp[IRQ]->IoStatus.Status = STATUS_SUCCESS; extension->PendingIrp[IRQ]->IoStatus.Information = 1; IoSetCancelRoutine(extension->PendingIrp[IRQ], NULL); IoCompleteRequest(extension->PendingIrp[IRQ], IO_NO_INCREMENT); } return STATUS_SUCCESS; } /********************************************************************************* * * SEND EOI * **********************************************************************************/ NTSTATUS IRQGENMSendEOI( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS status; PIRQGENM_DEVICE_EXTENSION extension; ULONG nPort; PULONG pIOBuffer; UCHAR IRQ; extension = DeviceObject->DeviceExtension; pIOBuffer = (PULONG)Irp->AssociatedIrp.SystemBuffer; if ((IRQ = *pIOBuffer) > 15) return STATUS_INVALID_PARAMETER; if (IRQ >= 8) { nPort = 0xA0; WRITE_PORT_UCHAR((PUCHAR)nPort, 0x20); } nPort = 0x20; WRITE_PORT_UCHAR((PUCHAR)nPort, 0x20); status = STATUS_SUCCESS; return status; } /********************************************************************************* * * DISCONNECT IRQ * **********************************************************************************/ NTSTATUS IRQGENMDisconnectIRQ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS status; PIRQGENM_DEVICE_EXTENSION extension; PULONG pIOBuffer; UCHAR IRQ; extension = DeviceObject->DeviceExtension; pIOBuffer = (PULONG)Irp->AssociatedIrp.SystemBuffer; if ((IRQ = *pIOBuffer) > 15) return STATUS_INVALID_PARAMETER; if (extension->InterruptConnected[IRQ]) { // this might crash if an interrupt is waiting but it is up to the user to disable // interrupts before re-initializing an IRQ extension->InterruptConnected[IRQ] = FALSE; extension->InterruptRequested[IRQ] = FALSE; IoDisconnectInterrupt(extension->InterruptObject[IRQ]); if (extension->HasPendingIrp[IRQ]) { extension->PendingIrp[IRQ]->IoStatus.Status = STATUS_CANCELLED; extension->PendingIrp[IRQ]->IoStatus.Information = 1; IoSetCancelRoutine(extension->PendingIrp[IRQ], NULL); IoCompleteRequest(extension->PendingIrp[IRQ], IO_NO_INCREMENT); extension->HasPendingIrp[IRQ] = FALSE; } } return STATUS_SUCCESS; } /********************************************************************************* * * INTERRUPT SERVICE ROUTINE * **********************************************************************************/ BOOLEAN IRQGENMInterruptServiceRoutine( IN PKINTERRUPT Interrupt, IN OUT PVOID Context ) { PDEVICE_OBJECT DeviceObject; PIRQGENM_DEVICE_EXTENSION extension; unsigned long nPort; UCHAR IRQ; DeviceObject = Context; extension = DeviceObject->DeviceExtension; for (IRQ=0; IRQ<16; IRQ++) if (Interrupt == extension->InterruptObject[IRQ]) break; if (IRQ > 15) return TRUE; if (extension->InterruptRequested[IRQ] && extension->HasPendingIrp[IRQ]) { extension->InterruptRequested[IRQ] = FALSE; extension->HasPendingIrp[IRQ] = FALSE; extension->PendingIrp[IRQ]->IoStatus.Status = STATUS_SUCCESS; extension->PendingIrp[IRQ]->IoStatus.Information = 0; IoSetCancelRoutine(extension->PendingIrp[IRQ], NULL); IoCompleteRequest(extension->PendingIrp[IRQ], IO_SOUND_INCREMENT); } else extension->InterruptOccurred[IRQ] = TRUE; // IoRequestDpc(DeviceObject, extension->PendingIrp[IRQ], NULL); switch (extension->Operation[IRQ]) { case WRITE_TO_CLEAR: nPort = extension->BaseAddress[IRQ] + extension->ClearOffset[IRQ]; WRITE_PORT_UCHAR((PUCHAR)nPort, 0); break; case READ_TO_CLEAR: nPort = extension->BaseAddress[IRQ] + extension->ClearOffset[IRQ]; READ_PORT_UCHAR((PUCHAR)nPort); break; } return TRUE; } /********************************************************************************* * * DEFERRED PROCEDURE CALL FOR ISR * **********************************************************************************/ /* VOID IRQGENMDpcForIsrRoutine( IN PKDPC Dpc, IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PIRQGENM_DEVICE_EXTENSION extension; extension = DeviceObject->DeviceExtension; if (extension->InterruptRequested && extension->HasPendingIrp) { extension->InterruptRequested = FALSE; extension->HasPendingIrp = FALSE; Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoSetCancelRoutine(Irp, NULL); IoCompleteRequest(Irp, IO_SOUND_INCREMENT); } else extension->InterruptOccurred = TRUE; return; } */ /********************************************************************************* * * CANCEL IRP * **********************************************************************************/ VOID IRQGENMCancelIrp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PIRQGENM_DEVICE_EXTENSION extension; int i; extension = DeviceObject->DeviceExtension; Irp->IoStatus.Information = 0; for (i=0; i<16; i++) if (Irp == extension->PendingIrp[i]) { extension->HasPendingIrp[i] = FALSE; Irp->IoStatus.Information = 1; } IoReleaseCancelSpinLock( Irp->CancelIrql ); Irp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(Irp, IO_NO_INCREMENT); }