//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: bm.c // //-------------------------------------------------------------------------- #include "pciidex.h" NTSTATUS BmSetupOnePage ( IN PVOID PdoExtension, IN PVOID DataVirtualPageAddress, IN ULONG TransferByteCount, IN PMDL Mdl, IN BOOLEAN DataIn, IN PVOID RegionDescriptorTablePage ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, BusMasterInitialize) #pragma alloc_text(PAGE, BmQueryInterface) #pragma alloc_text(NONPAGE, BmSetup) #pragma alloc_text(NONPAGE, BmReceiveScatterGatherList) #pragma alloc_text(NONPAGE, BmRebuildScatterGatherList) #pragma alloc_text(NONPAGE, BmPrepareController) #pragma alloc_text(NONPAGE, BmSetupOnePage) #pragma alloc_text(NONPAGE, BmArm) #pragma alloc_text(NONPAGE, BmDisarm) #pragma alloc_text(NONPAGE, BmFlush) #pragma alloc_text(NONPAGE, BmStatus) #pragma alloc_text(NONPAGE, BmTimingSetup) #endif // ALLOC_PRAGMA NTSTATUS BusMasterInitialize ( PCHANPDO_EXTENSION PdoExtension ) { NTSTATUS status; ULONG numberOfMapRegisters; ULONG scatterListSize; BOOLEAN noBmRegister; PAGED_CODE(); if (PdoExtension->ParentDeviceExtension->TranslatedBusMasterBaseAddress == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; noBmRegister = TRUE; } else { if (PdoExtension->ChannelNumber == 0) { PdoExtension->BmRegister = PdoExtension->ParentDeviceExtension->TranslatedBusMasterBaseAddress; } else if (PdoExtension->ChannelNumber == 1) { PdoExtension->BmRegister = (PIDE_BUS_MASTER_REGISTERS) (((PUCHAR) PdoExtension->ParentDeviceExtension->TranslatedBusMasterBaseAddress) + 8); } else { ASSERT (FALSE); } if (READ_PORT_UCHAR (&PdoExtension->BmRegister->Status) & BUSMASTER_ZERO_BITS) { // // The must-be-zero bits are not zero // DebugPrint ((0, "BusMasterInitialize: bad busmaster status register value (0x%x). will never do busmastering ide\n")); PdoExtension->BmRegister = NULL; status = STATUS_INSUFFICIENT_RESOURCES; noBmRegister = TRUE; } else { status = STATUS_SUCCESS; noBmRegister = FALSE; } } // // Allocate Adapter Object // if (status == STATUS_SUCCESS) { DEVICE_DESCRIPTION deviceDescription; RtlZeroMemory(&deviceDescription, sizeof(deviceDescription)); deviceDescription.Version = DEVICE_DESCRIPTION_VERSION; deviceDescription.Master = TRUE; deviceDescription.ScatterGather = TRUE; deviceDescription.DemandMode = FALSE; deviceDescription.AutoInitialize = FALSE; deviceDescription.Dma32BitAddresses = TRUE; deviceDescription.IgnoreCount = FALSE; deviceDescription.BusNumber = PdoExtension->ParentDeviceExtension->BmResourceList->List[0].BusNumber, deviceDescription.InterfaceType = PCIBus; // // make sure MAX_TRANSFER_SIZE_PER_SRB is never larger than what // the ide bus master controller can handle // ASSERT (MAX_TRANSFER_SIZE_PER_SRB <= (PAGE_SIZE * (PAGE_SIZE / sizeof(PHYSICAL_REGION_DESCRIPTOR)))); deviceDescription.MaximumLength = MAX_TRANSFER_SIZE_PER_SRB; PdoExtension->DmaAdapterObject = IoGetDmaAdapter( PdoExtension->ParentDeviceExtension->AttacheePdo, &deviceDescription, &numberOfMapRegisters ); ASSERT(PdoExtension->DmaAdapterObject); PdoExtension->MaximumPhysicalPages = numberOfMapRegisters; if (!PdoExtension->DmaAdapterObject) { status = STATUS_INSUFFICIENT_RESOURCES; } } if (status == STATUS_SUCCESS) { scatterListSize = PdoExtension->MaximumPhysicalPages * sizeof (PHYSICAL_REGION_DESCRIPTOR); PdoExtension->RegionDescriptorTable = PdoExtension->DmaAdapterObject->DmaOperations->AllocateCommonBuffer( PdoExtension->DmaAdapterObject, scatterListSize, &PdoExtension->PhysicalRegionDescriptorTable, FALSE ); ASSERT (PdoExtension->RegionDescriptorTable); ASSERT (PdoExtension->PhysicalRegionDescriptorTable.QuadPart); if (PdoExtension->RegionDescriptorTable) { RtlZeroMemory ( PdoExtension->RegionDescriptorTable, scatterListSize ); } else { status = STATUS_INSUFFICIENT_RESOURCES; } } if (status != STATUS_SUCCESS) { // // free resources // if (PdoExtension->RegionDescriptorTable) { PdoExtension->DmaAdapterObject->DmaOperations->FreeCommonBuffer( PdoExtension->DmaAdapterObject, scatterListSize, PdoExtension->PhysicalRegionDescriptorTable, PdoExtension->RegionDescriptorTable, FALSE ); PdoExtension->PhysicalRegionDescriptorTable.QuadPart = 0; PdoExtension->RegionDescriptorTable = NULL; } if (PdoExtension->DmaAdapterObject) { KIRQL currentIrql; KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql); PdoExtension->DmaAdapterObject->DmaOperations->PutDmaAdapter ( PdoExtension->DmaAdapterObject ); KeLowerIrql(currentIrql); PdoExtension->DmaAdapterObject = NULL; } } // // init. is still ok if we just not a bm controller // if (noBmRegister) { status = STATUS_SUCCESS; } return status; } // BusMasterInitialize NTSTATUS BusMasterUninitialize ( PCHANPDO_EXTENSION PdoExtension ) { ULONG scatterListSize; KIRQL currentIrql; ASSERT (PdoExtension->BmState == BmIdle); if (PdoExtension->DmaAdapterObject) { scatterListSize = PdoExtension->MaximumPhysicalPages * sizeof (PHYSICAL_REGION_DESCRIPTOR); if (PdoExtension->PhysicalRegionDescriptorTable.QuadPart) { PdoExtension->DmaAdapterObject->DmaOperations->FreeCommonBuffer( PdoExtension->DmaAdapterObject, scatterListSize, PdoExtension->PhysicalRegionDescriptorTable, PdoExtension->RegionDescriptorTable, FALSE ); PdoExtension->RegionDescriptorTable = NULL; PdoExtension->PhysicalRegionDescriptorTable.QuadPart = 0; } KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql); PdoExtension->DmaAdapterObject->DmaOperations->PutDmaAdapter ( PdoExtension->DmaAdapterObject ); KeLowerIrql(currentIrql); PdoExtension->DmaAdapterObject = NULL; } return STATUS_SUCCESS; } NTSTATUS BmSetup ( IN PVOID PdoExtension, IN PVOID DataVirtualAddress, IN ULONG TransferByteCount, IN PMDL Mdl, IN BOOLEAN DataIn, IN VOID (* BmCallback) (PVOID Context), IN PVOID CallbackContext ) { PCHANPDO_EXTENSION pdoExtension = PdoExtension; NTSTATUS status; PIDE_BUS_MASTER_REGISTERS bmRegister; ASSERT (pdoExtension->BmState == BmIdle); bmRegister = pdoExtension->BmRegister; pdoExtension->DataVirtualAddress = DataVirtualAddress; pdoExtension->TransferLength = TransferByteCount; pdoExtension->Mdl = Mdl; pdoExtension->DataIn = DataIn; pdoExtension->BmCallback = BmCallback; pdoExtension->BmCallbackContext = CallbackContext; status = (*pdoExtension->DmaAdapterObject->DmaOperations->GetScatterGatherList)( pdoExtension->DmaAdapterObject, pdoExtension->DeviceObject, pdoExtension->Mdl, pdoExtension->DataVirtualAddress, pdoExtension->TransferLength, BmReceiveScatterGatherList, pdoExtension, (BOOLEAN) !pdoExtension->DataIn ); return status; } // BmSetup VOID BmReceiveScatterGatherList( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PSCATTER_GATHER_LIST ScatterGather, IN PVOID Context ) { PCHANPDO_EXTENSION pdoExtension = Context; ASSERT (pdoExtension); BmRebuildScatterGatherList (pdoExtension, ScatterGather); BmPrepareController (pdoExtension); // // Call the FDO back // pdoExtension->BmCallback (pdoExtension->BmCallbackContext); return; } // BmReceiveScatterGatherList VOID BmRebuildScatterGatherList( IN PCHANPDO_EXTENSION PdoExtension, IN PSCATTER_GATHER_LIST ScatterGather ) { ULONG bytesToMap; ULONG i, j; ASSERT (ScatterGather); ASSERT (PdoExtension); ASSERT (PdoExtension->TransferLength); ASSERT (PdoExtension->Mdl); DebugPrint ((3, "PciIdeX: BmReceiveScatterGatherList() DataBuffer 0x%x, length 0x%x\n", PdoExtension->DataVirtualAddress, PdoExtension->TransferLength)); // // save the original list // PdoExtension->HalScatterGatherList = ScatterGather; for (i=j=0; jNumberOfElements; j++) { ULONG physicalAddress; PSCATTER_GATHER_ELEMENT sgElements; sgElements = ScatterGather->Elements + j; // // get the next block physical address // physicalAddress = sgElements->Address.LowPart; ASSERT (!(physicalAddress & 0x1)); ASSERT (!sgElements->Address.HighPart); // // get the next block byte size // bytesToMap = sgElements->Length; while (bytesToMap) { ULONG bytesLeftInCurrent64KPage; ASSERT (i < PdoExtension->MaximumPhysicalPages); PdoExtension->RegionDescriptorTable[i].PhysicalAddress = physicalAddress; bytesLeftInCurrent64KPage = 0x10000 - (physicalAddress & 0xffff); if (bytesLeftInCurrent64KPage < bytesToMap) { PdoExtension->RegionDescriptorTable[i].ByteCount = bytesLeftInCurrent64KPage; physicalAddress += bytesLeftInCurrent64KPage; bytesToMap -= bytesLeftInCurrent64KPage; } else if (bytesToMap <= 0x10000) { // // got a perfect page, map all of it // PdoExtension->RegionDescriptorTable[i].ByteCount = bytesToMap & 0xfffe; physicalAddress += bytesToMap & 0xfffe; bytesToMap = 0; } else { // // got a perfectly aligned 64k page, map all of it but the count // need to be 0 // PdoExtension->RegionDescriptorTable[i].ByteCount = 0; // 64K physicalAddress += 0x10000; bytesToMap -= 0x10000; } PdoExtension->RegionDescriptorTable[i].EndOfTable = 0; i++; } } // // the bus master circutry need to know it hits the end of the PRDT // PdoExtension->RegionDescriptorTable[i - 1].EndOfTable = 1; // end of table return; } // BmReceiveScatterGatherList VOID BmPrepareController ( PCHANPDO_EXTENSION PdoExtension ) { PCHANPDO_EXTENSION pdoExtension = PdoExtension; PIDE_BUS_MASTER_REGISTERS bmRegister; KeFlushIoBuffers(pdoExtension->Mdl, (BOOLEAN) (pdoExtension->DataIn), TRUE); bmRegister = pdoExtension->BmRegister; // // Init bus master contoller, but keep it disabled // // // Disable Controller // WRITE_PORT_UCHAR ( &bmRegister->Command, 0 ); // // Clear Errors // WRITE_PORT_UCHAR ( &bmRegister->Status, BUSMASTER_INTERRUPT | BUSMASTER_ERROR ); // // Init. Scatter Gather List Register // WRITE_PORT_ULONG ( &bmRegister->DescriptionTable, PdoExtension->PhysicalRegionDescriptorTable.LowPart ); pdoExtension->BmState = BmSet; return; } // BmPrepareController NTSTATUS BmSetupOnePage ( IN PVOID PdoExtension, IN PVOID DataVirtualPageAddress, IN ULONG TransferByteCount, IN PMDL Mdl, IN BOOLEAN DataIn, IN PVOID RegionDescriptorTablePage ) /*++ Routine Description: Does the same thing as BmSetup except that it sets up DMA controller for one page only and therefore it simple and straightforward and does not use any of kernel services unlike BmSetup. Arguments: PdoExtension - context pointer DataVirtualPageAddress- address of IO page TransferByteCount - size of IO (IO region shall not cross page boundary) Mdl - MDL descriptor containing DataVirtualAddress DataIn - TRUE if input, FALSE if output RegionDescriptorTable - memory to store 1 entry of RegionDescriptor (shall be page-aligned) Attention! Obviousely, it's caller responsibility to retain the values addressed by DataMemoryAddress and RegionDescriptor table until completion of DMA transfer Return Value: STATUS_SUCCESS if all conditions listed above were met, STATUS_UNSUCCESSFUL otherwise Environment: Kernel mode. Currently used by only by ATAPI during hibernation. --*/ { PCHANPDO_EXTENSION pdoExtension = PdoExtension; PPHYSICAL_REGION_DESCRIPTOR RegionDescriptorTable = RegionDescriptorTablePage; PHYSICAL_ADDRESS OldPhysicalRegionDescriptorTable; PPHYSICAL_REGION_DESCRIPTOR OldRegionDescriptorTable; PHYSICAL_ADDRESS DataPhysicalPageAddress; ULONG Size; // // Check alignment of addresses and transfer size // Size = PAGE_SIZE - ((ULONG) (((ULONG_PTR) DataVirtualPageAddress) & (PAGE_SIZE-1))); if ( TransferByteCount == 0 || TransferByteCount > Size || ((ULONG) ((ULONG_PTR)DataVirtualPageAddress | TransferByteCount) & 3) != 0 || ((ULONG) (((ULONG_PTR)RegionDescriptorTablePage) & (PAGE_SIZE-1))) ) { // Necessary requirements was not met, failure return (STATUS_UNSUCCESSFUL); } // // Initialize descriptor table // DataPhysicalPageAddress =(*pdoExtension->DmaAdapterObject->DmaOperations->MapTransfer)( (pdoExtension->DmaAdapterObject), Mdl, pdoExtension->MapRegisterBase, DataVirtualPageAddress, &TransferByteCount, !DataIn ); //DataPhysicalPageAddress = MmGetPhysicalAddress (DataVirtualPageAddress); RegionDescriptorTable[0].PhysicalAddress = DataPhysicalPageAddress.LowPart; RegionDescriptorTable[0].ByteCount = TransferByteCount; RegionDescriptorTable[0].EndOfTable = 1; // // Preserve existing data table from context // OldPhysicalRegionDescriptorTable = pdoExtension->PhysicalRegionDescriptorTable; OldRegionDescriptorTable = pdoExtension->RegionDescriptorTable; // // Set up IO request parameters // pdoExtension->PhysicalRegionDescriptorTable = MmGetPhysicalAddress (RegionDescriptorTable); pdoExtension->RegionDescriptorTable = RegionDescriptorTable; pdoExtension->Mdl = Mdl; pdoExtension->DataIn = DataIn; // // Setup controller // BmPrepareController (pdoExtension); // // Restore original table values // pdoExtension->PhysicalRegionDescriptorTable = OldPhysicalRegionDescriptorTable; pdoExtension->RegionDescriptorTable = OldRegionDescriptorTable; // // Done // return (STATUS_SUCCESS); } NTSTATUS BmArm ( IN PVOID PdoExtension ) { PCHANPDO_EXTENSION pdoExtension = PdoExtension; PIDE_BUS_MASTER_REGISTERS bmRegister; UCHAR bmStatus; ASSERT ((pdoExtension->BmState == BmSet) || (pdoExtension->BmState == BmDisarmed)); bmRegister = pdoExtension->BmRegister; // if (Device == 0) // bmStatus = BUSMASTER_DEVICE0_DMA_OK; // else // bmStatus = BUSMASTER_DEVICE1_DMA_OK; // // clear the status bit // bmStatus = BUSMASTER_INTERRUPT | BUSMASTER_ERROR; WRITE_PORT_UCHAR (&bmRegister->Status, bmStatus); // // on your mark...get set...go!! // #if !defined (FAKE_BAD_IDE_DMA_DEVICE) if (pdoExtension->DataIn) { WRITE_PORT_UCHAR (&bmRegister->Command, 0x09); // enable BM read } else { WRITE_PORT_UCHAR (&bmRegister->Command, 0x01); // enable BM write } #endif // !FAKE_BAD_IDE_DMA_DEVICE pdoExtension->BmState = BmArmed; DebugPrint ((3, "PciIde: BmArm()\n")); return STATUS_SUCCESS; } // BmArm BMSTATUS BmDisarm ( IN PVOID PdoExtension ) { PCHANPDO_EXTENSION pdoExtension = PdoExtension; PIDE_BUS_MASTER_REGISTERS bmRegister = pdoExtension->BmRegister; BMSTATUS bmStatus; bmStatus = BmStatus (PdoExtension); WRITE_PORT_UCHAR (&bmRegister->Command, 0x0); // disable BM WRITE_PORT_UCHAR (&bmRegister->Status, BUSMASTER_INTERRUPT); // clear interrupt BM if (pdoExtension->BmState != BmIdle) { pdoExtension->BmState = BmDisarmed; } if (bmStatus) { DebugPrint ((1, "PciIdeX: BM 0x%x status = 0x%x\n", bmRegister, bmStatus)); } return bmStatus; } // BmDisarm BMSTATUS BmFlush ( IN PVOID PdoExtension ) { PCHANPDO_EXTENSION pdoExtension = PdoExtension; ASSERT (pdoExtension->BmState != BmArmed); (*pdoExtension->DmaAdapterObject->DmaOperations->PutScatterGatherList)( pdoExtension->DmaAdapterObject, pdoExtension->HalScatterGatherList, (BOOLEAN)(!pdoExtension->DataIn)); pdoExtension->HalScatterGatherList = NULL; pdoExtension->DataVirtualAddress = NULL; pdoExtension->TransferLength = 0; pdoExtension->Mdl = NULL; pdoExtension->BmState = BmIdle; DebugPrint ((3, "PciIde: BmFlush()\n")); return STATUS_SUCCESS; } // BmFlush BMSTATUS BmStatus ( IN PVOID PdoExtension ) { PCHANPDO_EXTENSION pdoExtension = PdoExtension; PIDE_BUS_MASTER_REGISTERS bmRegister; BMSTATUS bmStatus; UCHAR bmRawStatus; bmRegister = pdoExtension->BmRegister; bmRawStatus = READ_PORT_UCHAR (&bmRegister->Status); bmStatus = 0; // // if we get back 0xff from the port, then the decodes // are probably not enabled (or the device is powered down). return 0. // if (bmRawStatus == 0xff) { return bmStatus; } if (bmRawStatus & BUSMASTER_ACTIVE) { bmStatus |= BMSTATUS_NOT_REACH_END_OF_TRANSFER; } if (bmRawStatus & BUSMASTER_ERROR) { bmStatus |= BMSTATUS_ERROR_TRANSFER; } if (bmRawStatus & BUSMASTER_INTERRUPT) { bmStatus |= BMSTATUS_INTERRUPT; } return bmStatus; } // BmStatus NTSTATUS BmTimingSetup ( IN PVOID PdoExtension ) { return STATUS_SUCCESS; } // BmTimingSetup NTSTATUS BmCrashDumpInitialize ( IN PVOID PdoExtension ) { PCHANPDO_EXTENSION pdoExtension = PdoExtension; ULONG nMapRegisters = pdoExtension->MaximumPhysicalPages-1; if (pdoExtension->DmaAdapterObject != NULL) { pdoExtension->MapRegisterBase = HalAllocateCrashDumpRegisters((PADAPTER_OBJECT)pdoExtension->DmaAdapterObject, &nMapRegisters ); } return STATUS_SUCCESS; } NTSTATUS BmQueryInterface ( IN PCHANPDO_EXTENSION PdoExtension, IN OUT PPCIIDE_BUSMASTER_INTERFACE BusMasterInterface ) { PCTRLFDO_EXTENSION fdoExtension = PdoExtension->ParentDeviceExtension; PAGED_CODE(); if (PdoExtension->BmRegister) { BusMasterInterface->Size = sizeof(PCIIDE_BUSMASTER_INTERFACE); BusMasterInterface->SupportedTransferMode[0] = fdoExtension->ControllerProperties.SupportedTransferMode[PdoExtension->ChannelNumber][0]; BusMasterInterface->SupportedTransferMode[1] = fdoExtension->ControllerProperties.SupportedTransferMode[PdoExtension->ChannelNumber][1]; BusMasterInterface->MaxTransferByteSize = (PdoExtension->MaximumPhysicalPages - 1) * PAGE_SIZE; BusMasterInterface->Context = PdoExtension; BusMasterInterface->ContextSize = sizeof (*PdoExtension); BusMasterInterface->BmSetup = BmSetup; BusMasterInterface->BmArm = BmArm; BusMasterInterface->BmDisarm = BmDisarm; BusMasterInterface->BmFlush = BmFlush; BusMasterInterface->BmStatus = BmStatus; BusMasterInterface->BmTimingSetup = BmTimingSetup; BusMasterInterface->BmSetupOnePage= BmSetupOnePage; BusMasterInterface->BmCrashDumpInitialize= BmCrashDumpInitialize; BusMasterInterface->BmFlushAdapterBuffers= BmFlushAdapterBuffers; BusMasterInterface->IgnoreActiveBitForAtaDevice = fdoExtension->ControllerProperties.IgnoreActiveBitForAtaDevice; BusMasterInterface->AlwaysClearBusMasterInterrupt = (fdoExtension->ControllerProperties.AlwaysClearBusMasterInterrupt || IsNativeMode(fdoExtension)); return STATUS_SUCCESS; } else { return STATUS_NOT_IMPLEMENTED; } } // BmQueryInterface NTSTATUS BmFlushAdapterBuffers ( IN PVOID PdoExtension, IN PVOID DataVirtualPageAddress, IN ULONG TransferByteCount, IN PMDL Mdl, IN BOOLEAN DataIn ) /*++ --*/ { PCHANPDO_EXTENSION pdoExtension = PdoExtension; ASSERT (pdoExtension->BmState != BmArmed); (pdoExtension->DmaAdapterObject->DmaOperations->FlushAdapterBuffers)( (pdoExtension->DmaAdapterObject), Mdl, pdoExtension->MapRegisterBase, DataVirtualPageAddress, TransferByteCount, !DataIn ); pdoExtension->BmState = BmIdle; return STATUS_SUCCESS; }