// Read/Write request processors for AIOWDM driver #include "stddcls.h" #include "driver.h" NTSTATUS GetClearIOFor(PDEVICE_EXTENSION); PDEVICE_EXTENSION GlobalExtension[MAX_SLOTS_OPEN]; /////////////////////////////////////////////////////////////////////////////// #pragma PAGEDCODE NTSTATUS DispatchCreate(PDEVICE_OBJECT fdo, PIRP Irp) { // DispatchCreate PAGED_CODE(); PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); // Claim the remove lock in Win2K so that removal waits until the // handle closes. Don't do this in Win98, however, because this // device might be removed by surprise with handles open, whereupon // we'll deadlock in HandleRemoveDevice waiting for a close that // can never happen because we can't run the user-mode code that // would do the close. NTSTATUS status; if ( win98 || ! pdx->bRealDevice ) status = STATUS_SUCCESS; else status = IoAcquireRemoveLock(&pdx->RemoveLock, stack->FileObject); if (NT_SUCCESS(status)) { // okay to open if (InterlockedIncrement(&pdx->handles) == 1) { // first open handle } // first open handle KdPrint(("Opened handle, %d handle%s open\n", pdx->handles, pdx->handles-1 ? "s" : "")); } // okay to open return CompleteRequest(Irp, status, 0); } // DispatchCreate /////////////////////////////////////////////////////////////////////////////// #pragma PAGEDCODE void ForgetIRP(PIRP IRP) { PAGED_CODE(); //This function is evil. However, if we complete the IRP, it'll ret into a thread that no //longer exists. This way, it'll remain pending indefinitely, consuming resources //including nonpaged memory... but the system won't bugcheck. //It would be really nice of us to delete the IRP without completing it, but we don't //know how, and furthermore don't believe there's a way(since it wouldn't be necessary //without this "ret into a thread that no longer exists" bug). } /////////////////////////////////////////////////////////////////////////////// #pragma PAGEDCODE NTSTATUS DispatchClose(PDEVICE_OBJECT fdo, PIRP Irp) { // DispatchClose PAGED_CODE(); PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); if (InterlockedDecrement(&pdx->handles) == 0) { // no more open handles if (pdx->bRealDevice && pdx->InterruptRequested && pdx->HasPendingIrp) { pdx->InterruptRequested = FALSE; pdx->HasPendingIrp = FALSE; ForgetIRP(pdx->PendingIrp); pdx->PendingIrp = NULL; pdx->InterruptOccurred = FALSE; } } // no more open handles KdPrint(("Closed handle, %d handle%s open\n", pdx->handles, pdx->handles-1 ? "s" : "")); // Release the remove lock to match the acquisition done in DispatchCreate if ( pdx->bRealDevice && ! win98 ) IoReleaseRemoveLock(&pdx->RemoveLock, stack->FileObject); return CompleteRequest(Irp, STATUS_SUCCESS, 0); } // DispatchClose /////////////////////////////////////////////////////////////////////////////// #pragma LOCKEDCODE VOID DoIOAction(PUCHAR Base, IOACTION Action) { switch (Action.Operation) { case 'R': READ_PORT_UCHAR(Base + Action.Offset); break; case 'W': WRITE_PORT_UCHAR(Base + Action.Offset, Action.Data); break; } } /////////////////////////////////////////////////////////////////////////////// #if DBG #define READ_OFF_IO_ACTION(Base, Action) \ if ( Action.Operation == 'R') KdPrint((DRIVERNAME " - Reading from %03LX\n", 0L + Base + Action.Offset)); \ else if ( Action.Operation == 'W') KdPrint((DRIVERNAME " - Writing %02LX to %03LX\n", 0L + Action.Data, 0L + Base + Action.Offset)) #else //DBG #define READ_OFF_IO_ACTION(Base, Action) #endif //DBG /////////////////////////////////////////////////////////////////////////////// #pragma LOCKEDCODE VOID DpcForIsr(PKDPC Dpc, PDEVICE_OBJECT fdo, PIRP junk, PDEVICE_EXTENSION pdx) { // TrueDPC DoIOAction(pdx->portbase, pdx->Enable); if (pdx->InterruptRequested && pdx->HasPendingIrp) { pdx->InterruptRequested = FALSE; pdx->InterruptOccurred = FALSE; pdx->HasPendingIrp = FALSE; pdx->PendingIrp->IoStatus.Status = STATUS_NO_MORE_FILES;//I wish STATUS_SUCCESS worked... pdx->PendingIrp->IoStatus.Information = pdx->COSSize; IoSetCancelRoutine(pdx->PendingIrp, NULL); IoCompleteRequest(pdx->PendingIrp, IO_SOUND_INCREMENT); } else { pdx->InterruptOccurred = TRUE; } } // TrueDPC /////////////////////////////////////////////////////////////////////////////// #pragma LOCKEDCODE BOOLEAN TrueISR(PDEVICE_EXTENSION pdx) { // TrueISR KdPrint((DRIVERNAME " - IRQ from %04LX?\n", pdx->DeviceID)); UCHAR Data = 0xFF; if (pdx->IAm.Operation == 'R') Data = READ_PORT_UCHAR(pdx->portbase + pdx->IAm.Offset) & pdx->IAm.Data; else if (pdx->IAm.Operation == 'Q') Data = READ_PORT_UCHAR(pdx->StatusBase + pdx->IAm.Offset) & pdx->IAm.Data; if (!Data) //then it wasn't me return FALSE; KdPrint((DRIVERNAME " - Yes!\n")); DoIOAction(pdx->portbase, pdx->Disable); READ_OFF_IO_ACTION(pdx->portbase, pdx->Disable); if (pdx->InterruptRequested && pdx->HasPendingIrp) for ( ULONG I = 0; I < pdx->COSSize; ++I ) ((PUCHAR)pdx->PendingIrp->AssociatedIrp.SystemBuffer)[I] = READ_PORT_UCHAR((PUCHAR)pdx->portbase + I + (I / 3)); DoIOAction(pdx->portbase, pdx->Clear); //The DPC enables IoRequestDpc(pdx->DeviceObject, NULL, pdx); if (!pdx->busy) return TRUE; return TRUE; } // TrueISR BOOLEAN OnInterrupt(PKINTERRUPT InterruptObject, PVOID corrupt) { KdPrint((DRIVERNAME " - IRQ...\n")); BOOLEAN Anybody = FALSE; for (int I = MAX_SLOTS_OPEN - 1; I >= 0 && GlobalExtension[I]; --I) if (TrueISR(GlobalExtension[I])) Anybody = TRUE; return Anybody; } /////////////////////////////////////////////////////////////////////////////// #pragma PAGEDCODE NTSTATUS ClaimStandardFailReason(void) { UNICODE_STRING KeyPath, ValueName; HANDLE Key; OBJECT_ATTRIBUTES Attr; NTSTATUS Status; RtlInitUnicodeString(&KeyPath, L"\\REGISTRY\\MACHINE\\Software\\ACCES\\AIOWDM"); RtlInitUnicodeString(&ValueName, L"FailReasonString"); InitializeObjectAttributes(&Attr, &KeyPath, 0, NULL, NULL); Status = ZwOpenKey(&Key, KEY_SET_VALUE, &Attr); if (!NT_SUCCESS(Status)) return Status; Status = ZwSetValueKey(Key, &ValueName, 0, REG_SZ, L"Windows Sucks", 13*2); ZwClose(Key); return Status; } #pragma PAGEDCODE NTSTATUS DumpRegDWord(PWCHAR ValueNom, ULONG Data) { UNICODE_STRING KeyPath, ValueName; HANDLE Key; OBJECT_ATTRIBUTES Attr; NTSTATUS Status; RtlInitUnicodeString(&KeyPath, L"\\REGISTRY\\MACHINE\\Software\\ACCES\\AIOWDM"); RtlInitUnicodeString(&ValueName, ValueNom); InitializeObjectAttributes(&Attr, &KeyPath, 0, NULL, NULL); Status = ZwOpenKey(&Key, KEY_SET_VALUE, &Attr); if (!NT_SUCCESS(Status)) return Status; Status = ZwSetValueKey(Key, &ValueName, 0, REG_DWORD, &Data, 4); ZwClose(Key); return Status; } /////////////////////////////////////////////////////////////////////////////// #pragma PAGEDCODE NTSTATUS StartDevice(PDEVICE_OBJECT fdo, PCM_PARTIAL_RESOURCE_LIST raw, PCM_PARTIAL_RESOURCE_LIST translated) { // StartDevice PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; NTSTATUS status; // Identify the I/O resources we're supposed to use. ULONG vector; KIRQL irql; KINTERRUPT_MODE mode; KAFFINITY affinity; BOOLEAN irqshare; BOOLEAN gotinterrupt = FALSE; ULONG StatusSize, AuxSize; PHYSICAL_ADDRESS portbase, StatusBase, AuxBase; BOOLEAN gotport = FALSE, GotStatus = FALSE, GotAux = FALSE; //ClaimStandardFailReason(); if (!translated) return STATUS_DEVICE_CONFIGURATION_ERROR; // no resources assigned?? PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = translated->PartialDescriptors; ULONG nres = translated->Count; for (ULONG i = 0; i < nres; ++i, ++resource) { // for each resource switch (resource->Type) { // switch on resource type case CmResourceTypePort: if ( ! GotStatus ) { //This is the I/O-mapped PLX config space. StatusBase = resource->u.Port.Start; StatusSize = resource->u.Port.Length; pdx->mappedport = (resource->Flags & CM_RESOURCE_PORT_IO) == 0; GotStatus = TRUE; } else if ( ! gotport ) { //This is the main space. portbase = resource->u.Port.Start; pdx->nports = resource->u.Port.Length; pdx->mappedport = (resource->Flags & CM_RESOURCE_PORT_IO) == 0; gotport = TRUE; } else { //This is the aux space, if any. AuxBase = resource->u.Port.Start; AuxSize = resource->u.Port.Length; pdx->mappedport = (resource->Flags & CM_RESOURCE_PORT_IO) == 0; GotAux = TRUE; } break; case CmResourceTypeInterrupt: irql = (KIRQL) resource->u.Interrupt.Level; vector = resource->u.Interrupt.Vector; affinity = resource->u.Interrupt.Affinity; mode = (resource->Flags == CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive; irqshare = resource->ShareDisposition == CmResourceShareShared; gotinterrupt = TRUE; break; case CmResourceTypeMemory: //This is the memory-mapped PLX config space case CmResourceTypeDevicePrivate: break; default: KdPrint((DRIVERNAME " - Unexpected I/O resource type %d\n", resource->Type)); break; } // switch on resource type } // for each resource // Verify that we got all the resources we were expecting if (!(gotport && GotStatus)) { KdPrint((DRIVERNAME " - Didn't get expected I/O resources\n")); return STATUS_DEVICE_CONFIGURATION_ERROR; } pdx->UnmappedBase = (USHORT)portbase.LowPart; if ( GotAux ) pdx->UnmappedAuxBase = (USHORT)AuxBase.LowPart; if (pdx->mappedport) { // map port address for RISC platform pdx->portbase = (PUCHAR) MmMapIoSpace(portbase, pdx->nports, MmNonCached); if ( ! pdx->portbase ) { KdPrint((DRIVERNAME " - Unable to map port range %I64X, length %X\n", portbase, pdx->nports)); return STATUS_INSUFFICIENT_RESOURCES; } pdx->StatusBase = (PUCHAR) MmMapIoSpace(StatusBase, StatusSize, MmNonCached); if ( ! pdx->StatusBase ) { KdPrint((DRIVERNAME " - Unable to map port range %I64X, length %X\n", StatusBase, StatusSize)); MmUnmapIoSpace(pdx->portbase, pdx->nports); return STATUS_INSUFFICIENT_RESOURCES; } } // map port address for RISC platform else { pdx->portbase = (PUCHAR) portbase.QuadPart; pdx->StatusBase = (PUCHAR) StatusBase.QuadPart; } status = GetClearIOFor(pdx); if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME " - Could not get clear I/O methods!\n")); return STATUS_DEVICE_CONFIGURATION_ERROR; } pdx->IAm.Operation = 'Q'; if ( StatusSize >= 0x100 ) { pdx->IAm.Offset = 0x69; pdx->IAm.Data = 0x80; } else { pdx->IAm.Offset = 0x4C; pdx->IAm.Data = 0x04; } DoIOAction(pdx->portbase, pdx->Disable); DoIOAction(pdx->portbase, pdx->Clear); //Attempts to correct .BIN error. Doesn't work. //if ( StatusSize >= 0x100 ) WRITE_PORT_ULONG(PULONG(pdx->StatusBase + 0x34), 0x00000050); pdx->GotIRQ = gotinterrupt; if (pdx->GotIRQ) { status = IoConnectInterrupt(&pdx->InterruptObject, OnInterrupt, (PVOID) pdx, NULL, vector, irql, irql, mode, irqshare, affinity, FALSE); if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME " - IoConnectInterrupt failed - %X\n", status)); if (pdx->mappedport) { MmUnmapIoSpace(pdx->portbase, pdx->nports); MmUnmapIoSpace(pdx->StatusBase, StatusSize); } pdx->portbase = NULL; return status; } if ( StatusSize >= 0x100 ) WRITE_PORT_UCHAR(pdx->StatusBase + 0x69, 0x09); } else { pdx->InterruptObject = NULL; } KdPrint((DRIVERNAME " - DeviceID = %04X, I/O base = %04X, status base = %04X\n", pdx->DeviceID, pdx->portbase, pdx->StatusBase)); return STATUS_SUCCESS; } // StartDevice /////////////////////////////////////////////////////////////////////////////// #pragma PAGEDCODE VOID StopDevice(IN PDEVICE_OBJECT fdo, BOOLEAN oktouch /* = FALSE */) { // StopDevice PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; if (pdx->InterruptObject) { // disconnect interrupt DoIOAction(pdx->portbase, pdx->Disable); IoDisconnectInterrupt(pdx->InterruptObject); pdx->InterruptObject = NULL; } // disconnect interrupt if (pdx->portbase && pdx->mappedport) MmUnmapIoSpace(pdx->portbase, pdx->nports); pdx->portbase = NULL; } // StopDevice /////////////////////////////////////////////////////////////////////////////// /****************************************************************************** //Addendum: IAm is now ignored; AIOWDM determines IAm nature in a fixed manner.// GetClearIOFor retrieves from the registry the I/O operations necessary to disable, clear, and enable IRQs. The registry entry is as follows: "[IAm]|[Disable]|[Clear]|[Enable]" IAm is empty or as follows: "[Offset]&[Mask]" Disable, Clear, and Enable are each empty or match one of these patterns: "[Offset]<[Data]" (write) "[Offset]>" (read) If any member is empty, there is no such action. Offset, Mask, and Data are hexadecimal bytes with no leading 0xes or $s. In addition, Data can be empty; in this case, zero is written. Invalid characters are dropped. If IAm is nonempty, the card has an IRQ status register at base + [Offset]. If any of the bits in [Mask] are set in the register, the card is generating an IRQ. Clear will almost always be nonempty(though on some cards the act of disabling and reenabling IRQs clears it). The card's IRQ is cleared by performing the specified action(writing [Data] or reading) at base + [Offset]. Disable and Enable will almost always either both be present or both be empty). If present, the specified action will disable or enable IRQs. Example: A card has registers as follows: IRQ status register = base + 8, least significant bit indicates an IRQ Disable = write FF to base + B Clear = write anything to base + F Enable = write 00 to base + B Its registry entry might be: "8&01|BDisable.Data = 0; pdx->Disable.Offset = 0; pdx->Disable.Operation = 'N'; pdx->Clear.Data = 0; pdx->Clear.Offset = 0; pdx->Clear.Operation = 'N'; pdx->Enable.Data = 0; pdx->Enable.Offset = 0; pdx->Enable.Operation = 'N'; { WCHAR HID[256]; ULONG Size; NTSTATUS Status = IoGetDeviceProperty(pdx->Pdo, DevicePropertyHardwareID, sizeof(HID), HID, &Size); if (!NT_SUCCESS(Status)) return Status; enum DevSearchState {FindingD, FindingE, FindingV, Finding_, FindingDone} State = FindingD; ULONG I; for (I = 0; I < Size && State != FindingDone; ++I) switch(HID[I]) { case 'D': case 'd': if (State == FindingD) State = FindingE; else State = FindingD; break; case 'E': case 'e': if (State == FindingE) State = FindingV; else State = FindingD; break; case 'V': case 'v': if (State == FindingV) State = Finding_; else State = FindingD; break; case '_': if (State == Finding_) State = FindingDone; else State = FindingD; break; default: State = FindingD; break; } //Add ISA virtual device ID code here if (State != FindingDone) return STATUS_OBJECT_NAME_INVALID; pdx->DeviceID = 0; {for (int J = 0; J < 4; ++J, ++I) { switch(HID[I]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': pdx->DeviceID = pdx->DeviceID * 0x10 + (HID[I] - '0'); break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': pdx->DeviceID = pdx->DeviceID * 0x10 + (HID[I] - 'A' + 10); break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': pdx->DeviceID = pdx->DeviceID * 0x10 + (HID[I] - 'a' + 10); break; } } } } /* Pseudocode (actually the Borland equivalent) AnsiString Content; PRegistry Reg = new TRegistry; Reg->Access = KEY_QUERY_VALUE; Content = Reg->ReadString(IntToHex(DeviceID, 4)); Reg->Free(); */ PKEY_VALUE_PARTIAL_INFORMATION pKVPI; { HANDLE Key; OBJECT_ATTRIBUTES Attr; InitializeObjectAttributes(&Attr, &servkey, 0, NULL, NULL); NTSTATUS status = ZwOpenKey(&Key, KEY_QUERY_VALUE, &Attr); if (!NT_SUCCESS(status)) return status; UNICODE_STRING Value; char Buf[5]; WCHAR LBuf[5]; sprintf(Buf, "%04X", pdx->DeviceID); {for (int I = 0; I < 5; ++I) LBuf[I] = Buf[I] ;} RtlInitUnicodeString(&Value, LBuf); ULONG Size; status = ZwQueryValueKey(Key, &Value, KeyValuePartialInformation, NULL, 0, &Size); //We expect STATUS_BUFFER_TOO_SMALL on NT2K or STATUS_BUFFER_OVERFLOW on 98 //we passed a zero-size buffer to get the needed buffer size if (status != STATUS_BUFFER_TOO_SMALL && status != STATUS_BUFFER_OVERFLOW) { ZwClose(Key); return status; } pKVPI = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(PagedPool, Size + 1); if (!pKVPI) { ZwClose(Key); return STATUS_NO_MEMORY; } status = ZwQueryValueKey(Key, &Value, KeyValuePartialInformation, pKVPI, Size, &Size); ZwClose(Key); if (!NT_SUCCESS(status)) { ExFreePool(pKVPI); return status; } } /**/ WCHAR *IOData = (PWCHAR)pKVPI->Data; IOData[pKVPI->DataLength / 2 - 1] = '|'; enum ClearIOReadState {ReadingIAm, ReadingDisable, ReadingClear, ReadingEnable} State = ReadingIAm; BOOLEAN Done = FALSE; PIOACTION Parent = &(pdx->IAm); PUCHAR Subject = &(Parent->Offset); pdx->Disable.Data = 0; pdx->Disable.Offset = 0; pdx->Disable.Operation = 'N'; pdx->Clear.Data = 0; pdx->Clear.Offset = 0; pdx->Clear.Operation = 'N'; pdx->Enable.Data = 0; pdx->Enable.Offset = 0; pdx->Enable.Operation = 'N'; while (!Done) { switch(*IOData) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *Subject = *Subject * 0x10 + (*IOData - '0'); break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': *Subject = *Subject * 0x10 + (*IOData - 'A' + 10); break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': *Subject = *Subject * 0x10 + (*IOData - 'a' + 10); break; case '<': //IAm doesn't use a write if (State != ReadingIAm) { Parent->Operation = 'W'; Subject = &(Parent->Data); } break; case '>': //IAm doesn't use a standard read if (State != ReadingIAm) { Parent->Operation = 'R'; while(*(++IOData) != '|') ; //Enable/disable read has no data section continue; } break; case '&': //Only IAm uses a read with a mask if (State == ReadingIAm) { Parent->Operation = 'R'; Subject = &(Parent->Data); } case '|': switch(State) { case ReadingIAm: #if DBG if (pdx->IAm.Operation == 'R') KdPrint(("IAm = read from base+%X ORed with $%02X\n", pdx->IAm.Offset, pdx->IAm.Data)); else KdPrint(("IAm = assume\n")); #endif //DBG State = ReadingDisable; Parent = &(pdx->Disable); Subject = &(Parent->Offset); break; case ReadingDisable: #if DBG switch (Parent->Operation) { case 'R': KdPrint(("Disable = read from base+%X\n", Parent->Offset)); break; case 'W': KdPrint(("Disable = write $%02X to base+%X\n", Parent->Data, Parent->Offset)); break; case 'N': KdPrint(("Disable = nothing\n")); break; } #endif //DBG State = ReadingClear; Parent = &(pdx->Clear); Subject = &(Parent->Offset); break; case ReadingClear: #if DBG switch (Parent->Operation) { case 'R': KdPrint(("Clear = read from base+%X\n", Parent->Offset)); break; case 'W': KdPrint(("Clear = write $%02X to base+%X\n", Parent->Data, Parent->Offset)); break; case 'N': KdPrint(("Clear = nothing\n")); break; } #endif //DBG State = ReadingEnable; Parent = &(pdx->Enable); Subject = &(Parent->Offset); break; case ReadingEnable: #if DBG switch (Parent->Operation) { case 'R': KdPrint(("Enable = read from base+%X\n", Parent->Offset)); break; case 'W': KdPrint(("Enable = write $%02X to base+%X\n", Parent->Data, Parent->Offset)); break; case 'N': KdPrint(("Enable = nothing\n")); break; } #endif //DBG Done = TRUE; break; } } ++IOData; } ExFreePool(pKVPI); return STATUS_SUCCESS; }