diff options
Diffstat (limited to 'private/ntos/nthals/halntp/mips/fxhwsup.c')
-rw-r--r-- | private/ntos/nthals/halntp/mips/fxhwsup.c | 3078 |
1 files changed, 3078 insertions, 0 deletions
diff --git a/private/ntos/nthals/halntp/mips/fxhwsup.c b/private/ntos/nthals/halntp/mips/fxhwsup.c new file mode 100644 index 000000000..73b4bbf49 --- /dev/null +++ b/private/ntos/nthals/halntp/mips/fxhwsup.c @@ -0,0 +1,3078 @@ + +#include "halp.h" +#include "bugcodes.h" +#include "eisa.h" + +// +// Put all code for HAL initialization in the INIT section. It will be +// deallocated by memory management when phase 1 initialization is +// completed. +// + +extern POBJECT_TYPE IoAdapterObjectType; + +// +// Globals used to keep track of the private buffer pool managed by the HAL. +// + +#define HALP_CONTIGUOUS_BUFFER_POOL_SIZE (0x8000 * 8) + +ULONG HalpContiguousBufferCurrentBase = 0; +ULONG HalpContiguousBufferMax = 0; + + +// +// The HAL supports the concept of map registers which provide scatter/gather +// functionality for IO devices whether a device directly supports scatter/gather +// or not. FALCON will use one master adapter object to manage the pool of +// map registers that can be allocated in groups for transfers requiring +// multiple contiguous pages. This pool of map registers will be managed using +// a bitmap data structure and are only available to EISA/ISA devices +// and not PCI devices. In general, this should not be a problem given that the +// majority of PCI devices will be busmaster devices that already support scatter/gather +// on-chip or on-board. In the circumstance where a PCI device wants to do +// multi-page transfers but does not itself support scatter/gather, the HAL will +// attempt to transfer the maximum amount of data that is physically contiguous +// in the described user/device buffer (by interrogating the MDL passed to IoMapTransfer). +// In the worst-case the device will have to transfer a page at a time which is better +// than supporting buffered-io which is slower. +// + + +PADAPTER_OBJECT MasterAdapterObject; +PADAPTER_OBJECT HalpPciAdapterObject; + +ULONG HalpPciMemoryOffset; + +UCHAR HalpDma1Status; +UCHAR HalpDma2Status; + +// +// Variable to save the contents of the +// bus error registers upon an exception +// + +ULONG HalpPmpMemErrAckValue; +ULONG HalpPmpMemErrAddrValue; +ULONG HalpPmpPciErrAckValue; +ULONG HalpPmpPciErrAddrValue; + +// +// Local storage of pointer to physical memory of +// the start of the map buffer pool and how many +// PAGE_SIZE buffers it contains +// + +ULONG HalpMapRegisterPhysicalBase; + +// +// Forward declarations +// + +IO_ALLOCATION_ACTION +HalpAllocationRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID MapRegisterBase, + IN PVOID Context + ); + +ULONG +HalpGetContiguousBufferPoolSize ( + VOID + ); + + + +/*++ + +Routine Description: + + This routine allocates the adapter channel specified by the adapter object. + This is accomplished by placing the device object of the driver that wants + to allocate the adapter on the adapter's queue. If the queue is already + "busy", then the adapter has already been allocated, so the device object + is simply placed onto the queue and waits until the adapter becomes free. + + Once the adapter becomes free (or if it already is), then the driver's + execution routine is invoked. + + Also, a number of map registers may be allocated to the driver by specifying + a non-zero value for NumberOfMapRegisters. Then the map register must be + allocated from the master adapter. Once there are a sufficient number of + map registers available, then the execution routine is called and the + base address of the allocated map registers in the adapter is also passed + to the driver's execution routine. + +Arguments: + + AdapterObject - Pointer to the adapter control object to allocate to the + driver. + + Wcb - Supplies a wait context block for saving the allocation parameters. + The DeviceObject, CurrentIrp and DeviceContext should be initalized. + + NumberOfMapRegisters - The number of map registers that are to be allocated + from the channel, if any. + + ExecutionRoutine - The address of the driver's execution routine that is + invoked once the adapter channel (and possibly map registers) have been + allocated. + +Return Value: + + Returns STATUS_SUCESS unless too many map registers are requested. + +Notes: + + Note that this routine MUST be invoked at DISPATCH_LEVEL or above. + +--*/ + +NTSTATUS +HalAllocateAdapterChannel( + IN PADAPTER_OBJECT AdapterObject, + IN PWAIT_CONTEXT_BLOCK Wcb, + IN ULONG NumberOfMapRegisters, + IN PDRIVER_CONTROL ExecutionRoutine + ) + +{ + PADAPTER_OBJECT MasterAdapter; + BOOLEAN Busy = FALSE; + IO_ALLOCATION_ACTION Action; + LONG MapRegisterNumber; + KIRQL Irql; + ULONG Hint; + + + // + // For PCI devices we don't have to deal with + // map registers, but we do for EISA devices! + // + + if (AdapterObject->InterfaceType == PCIBus) { + + // + // Initialize the device object's wait context block in case this device + // must wait before being able to allocate the adapter. + // + + Wcb->DeviceRoutine = ExecutionRoutine; + Wcb->NumberOfMapRegisters = NumberOfMapRegisters; + + // + // Allocate the adapter object for this particular device. If the + // adapter cannot be allocated because it has already been allocated + // to another device, then return to the caller now; otherwise, + // continue. + // + + if (!KeInsertDeviceQueue( &AdapterObject->ChannelWaitQueue, &Wcb->WaitQueueEntry )) { + + AdapterObject->CurrentWcb = Wcb; + AdapterObject->NumberOfMapRegisters = Wcb->NumberOfMapRegisters; + + // + // Invoke the driver's execution routine now. + // + + Action = ExecutionRoutine( Wcb->DeviceObject, + Wcb->CurrentIrp, + AdapterObject->MapRegisterBase, + Wcb->DeviceContext); + + // + // If the driver wishes to keep the map registers then set the number + // allocated to zero and set the action to deallocate object. + // + + if (Action == DeallocateObjectKeepRegisters) { + AdapterObject->NumberOfMapRegisters = 0; + Action = DeallocateObject; + } + + // + // If the driver would like to have the adapter deallocated, + // then deallocate any map registers allocated and then release + // the adapter object. + // + + if (Action == DeallocateObject) { + IoFreeAdapterChannel( AdapterObject ); + } + + } + + } else { + + // + // Begin by obtaining a pointer to the master adapter associated with this + // request. + // + + if (AdapterObject->MasterAdapter != NULL) { + MasterAdapter = AdapterObject->MasterAdapter; + } else { + MasterAdapter = AdapterObject; + } + + // + // Initialize the device object's wait context block in case this device + // must wait before being able to allocate the adapter. + // + + Wcb->DeviceRoutine = ExecutionRoutine; + Wcb->NumberOfMapRegisters = NumberOfMapRegisters; + + // + // Allocate the adapter object for this particular device. If the + // adapter cannot be allocated because it has already been allocated + // to another device, then return to the caller now; otherwise, + // continue. + // + + if (!KeInsertDeviceQueue( &AdapterObject->ChannelWaitQueue, &Wcb->WaitQueueEntry )) { + + // + // The adapter was not busy so it has been allocated. Now check + // to see whether this driver wishes to allocate any map registers. + // If so, then queue the device object to the master adapter queue + // to wait for them to become available. If the driver wants map + // registers, ensure that this adapter has enough total map registers + // to satisfy the request. + // + + AdapterObject->CurrentWcb = Wcb; + AdapterObject->NumberOfMapRegisters = Wcb->NumberOfMapRegisters; + + if (NumberOfMapRegisters != 0) { + + if (NumberOfMapRegisters > MasterAdapter->MapRegistersPerChannel) { + AdapterObject->NumberOfMapRegisters = 0; + IoFreeAdapterChannel(AdapterObject); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Lock the map register bit map and the adapter queue in the + // master adapter object. The channel structure offset is used as + // a hint for the register search. + // + + KeAcquireSpinLock( &MasterAdapter->SpinLock, &Irql ); + + MapRegisterNumber = -1; + + if (IsListEmpty( &MasterAdapter->AdapterQueue)) { + + Hint = 0; + + MapRegisterNumber = RtlFindClearBitsAndSet(MasterAdapter->MapRegisters, + NumberOfMapRegisters, + Hint); + + // + // Make sure this map register is valid for this adapter. + // + + if ((ULONG) MapRegisterNumber < Hint) { + + // + // Make it look like there are no map registers. + // + + RtlClearBits(MasterAdapter->MapRegisters, + MapRegisterNumber, + NumberOfMapRegisters); + + MapRegisterNumber = -1; + } + } + + if (MapRegisterNumber == -1) { + + // + // There were not enough free map registers. Queue this request + // on the master adapter where is will wait until some registers + // are deallocated. + // + + InsertTailList( &MasterAdapter->AdapterQueue, &AdapterObject->AdapterQueue); + Busy = 1; + + } else { + + AdapterObject->MapRegisterBase = (PVOID) ((PTRANSLATION_ENTRY) MasterAdapter->MapRegisterBase + MapRegisterNumber); + + } + + KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql ); + } + + // + // If there were either enough map registers available or no map + // registers needed to be allocated, invoke the driver's execution + // routine now. + // + + if (!Busy) { + + Action = ExecutionRoutine( Wcb->DeviceObject, + Wcb->CurrentIrp, + AdapterObject->MapRegisterBase, + Wcb->DeviceContext); + + // + // If the driver wishes to keep the map registers then set the number + // allocated to zero and set the action to deallocate object. + // + + if (Action == DeallocateObjectKeepRegisters) { + AdapterObject->NumberOfMapRegisters = 0; + Action = DeallocateObject; + } + + // + // If the driver would like to have the adapter deallocated, + // then deallocate any map registers allocated and then release + // the adapter object. + // + + if (Action == DeallocateObject) { + IoFreeAdapterChannel( AdapterObject ); + } + } + + } + + } + + return(STATUS_SUCCESS); + +} + + +/*++ + +Routine Description: + + This function allocates the memory for a common buffer and maps so that it + can be accessed by a master device and the CPU. + +Arguments: + + AdapterObject - Supplies a pointer to the adapter object used by this + device. + + Length - Supplies the length of the common buffer to be allocated. + + LogicalAddress - Returns the logical address of the common buffer. + + CacheEnable - Indicates whether the memory is cached or not. + +Return Value: + + Returns the virtual address of the common buffer. If the buffer cannot be + allocated then NULL is returned. + +--*/ + +PVOID +HalAllocateCommonBuffer( + IN PADAPTER_OBJECT AdapterObject, + IN ULONG Length, + OUT PPHYSICAL_ADDRESS LogicalAddress, + IN BOOLEAN CacheEnabled + ) + +{ + PVOID virtualAddress; + PVOID mapRegisterBase; + ULONG numberOfMapRegisters; + ULONG mappedLength; + WAIT_CONTEXT_BLOCK wcb; + KEVENT allocationEvent; + NTSTATUS status; + PMDL mdl; + KIRQL irql; + PHYSICAL_ADDRESS physAddr; + + // + // Determine how many map registers (pages) + // the CommonBuffer requires. + // + + numberOfMapRegisters = BYTES_TO_PAGES(Length); + + // + // Allocate the buffer + // + + if (CacheEnabled != FALSE) { + + virtualAddress = ExAllocatePool(NonPagedPoolCacheAligned, Length); + + } else { + + if (Length > PAGE_SIZE) { + + if ((HalpContiguousBufferCurrentBase + Length) <= HalpContiguousBufferMax) { + + // + // This is an absolute hack but it is the only + // way I can get a contiguous buffer for those + // devices which insist on large buffers. Because + // we don't have an IO TLB we cannot guarantee + // contiguousness. Devices such as the Madge Token + // Ring and the 3DLabs Glint insist on having a large + // DMA buffer. + // + + physAddr.HighPart = 0; + physAddr.LowPart = HalpContiguousBufferCurrentBase; + virtualAddress = MmMapIoSpace(physAddr, Length, FALSE); + + if (virtualAddress != (PVOID)NULL) { + + HalpContiguousBufferCurrentBase += (numberOfMapRegisters * PAGE_SIZE); + + } + + } else { + + virtualAddress = (PVOID)NULL; + + } + + } else { + + virtualAddress = MmAllocateNonCachedMemory(Length); + + } + + } + + + if (virtualAddress == NULL) { + + return(virtualAddress); + + } + + // + // Initialize an event. + // + + KeInitializeEvent( &allocationEvent, NotificationEvent, FALSE); + + // + // Initialize the wait context block. Use the device object to indicate + // where the map register base should be stored. + // + + wcb.DeviceObject = &mapRegisterBase; + wcb.CurrentIrp = NULL; + wcb.DeviceContext = &allocationEvent; + + // + // Allocate the adapter and the map registers. + // + + KeRaiseIrql(DISPATCH_LEVEL, &irql); + + status = HalAllocateAdapterChannel(AdapterObject, + &wcb, + numberOfMapRegisters, + HalpAllocationRoutine); + KeLowerIrql(irql); + + if (!NT_SUCCESS(status)) { + + // + // Cleanup and return NULL. + // + + if (CacheEnabled != FALSE) { + ExFreePool(virtualAddress); + + } else { + + if (Length > PAGE_SIZE) { + + MmUnmapIoSpace(virtualAddress, Length); + HalpContiguousBufferCurrentBase -= (numberOfMapRegisters * PAGE_SIZE); + + } else { + + MmFreeNonCachedMemory(virtualAddress, Length); + + } + + } + + return(NULL); + + } + + // + // Wait for the map registers to be allocated. + // + + status = KeWaitForSingleObject( &allocationEvent, + Executive, + KernelMode, + FALSE, + NULL); + + if (!NT_SUCCESS(status)) { + + // + // Cleanup and return NULL. + // + + if (CacheEnabled != FALSE) { + ExFreePool(virtualAddress); + + } else { + + if (Length > PAGE_SIZE) { + + MmUnmapIoSpace(virtualAddress, Length); + HalpContiguousBufferCurrentBase -= (numberOfMapRegisters * PAGE_SIZE); + + } else { + + MmFreeNonCachedMemory(virtualAddress, Length); + + } + + } + + return(NULL); + + } + + // + // Create an mdl to use with call to I/O map transfer. + // + + mdl = IoAllocateMdl(virtualAddress, + Length, + FALSE, + FALSE, + NULL); + + MmBuildMdlForNonPagedPool(mdl); + + // + // Map the transfer so that the controller + // can access the memory. + // + + mappedLength = Length; + *LogicalAddress = IoMapTransfer(NULL, + mdl, + mapRegisterBase, + virtualAddress, + &mappedLength, + TRUE); + + IoFreeMdl(mdl); + + if (mappedLength < Length) { + + // + // Cleanup and indicate that the allocation failed. + // + + HalFreeCommonBuffer(AdapterObject, + Length, + *LogicalAddress, + virtualAddress, + CacheEnabled); + + return(NULL); + + } + + // + // The allocation completed successfully. + // + + return(virtualAddress); + +} + + +/*++ + +Routine Description: + + This function is called to flush any hardware adapter buffers when the + driver needs to read data written by an I/O master device to a common + buffer. + +Arguments: + + AdapterObject - Supplies a pointer to the adapter object used by this + device. + + Length - Supplies the length of the common buffer. This should be the same + value used for the allocation of the buffer. + + LogicalAddress - Supplies the logical address of the common buffer. This + must be the same value return by HalAllocateCommonBuffer. + + VirtualAddress - Supplies the virtual address of the common buffer. This + must be the same value return by HalAllocateCommonBuffer. + +Return Value: + + Returns TRUE if no errors were detected; otherwise, FALSE is return. + +--*/ + +BOOLEAN +HalFlushCommonBuffer( + IN PADAPTER_OBJECT AdapterObject, + IN ULONG Length, + IN PHYSICAL_ADDRESS LogicalAddress, + IN PVOID VirtualAddress + ) + +{ + + return(TRUE); + +} + + +/*++ + +Routine Description: + + This function frees a common buffer and all of the resouces it uses. + +Arguments: + + AdapterObject - Supplies a pointer to the adapter object used by this + device. + + Length - Supplies the length of the common buffer. This should be the same + value used for the allocation of the buffer. + + LogicalAddress - Supplies the logical address of the common buffer. This + must be the same value return by HalAllocateCommonBuffer. + + VirtualAddress - Supplies the virtual address of the common buffer. This + must be the same value return by HalAllocateCommonBuffer. + + CacheEnable - Indicates whether the memeory is cached or not. + +Return Value: + + None + +--*/ + +VOID +HalFreeCommonBuffer( + IN PADAPTER_OBJECT AdapterObject, + IN ULONG Length, + IN PHYSICAL_ADDRESS LogicalAddress, + IN PVOID VirtualAddress, + IN BOOLEAN CacheEnabled + ) + +{ + PTRANSLATION_ENTRY mapRegisterBase; + ULONG numberOfMapRegisters; + ULONG mapRegisterNumber; + + + // + // We only need to free up map registers + // if this was not a PCI device + // + + if (AdapterObject->InterfaceType == PCIBus) { + + // + // Free the memory for the common buffer. + // + + if (CacheEnabled != FALSE) { + + ExFreePool(VirtualAddress); + + } else { + + if (Length < PAGE_SIZE) { + + MmFreeNonCachedMemory(VirtualAddress, Length); + + } else { + + MmUnmapIoSpace(VirtualAddress, Length); + + } + + } + + } else { + + // + // Devices which do not use auto-initialize + // CommonBuffer DMA can use the scatter/gather + // capabilities of the 82374 which we manage + // through map registers. + // + + if (!AdapterObject->AutoInitialize) { + + + // + // Calculate the number of map registers, the map register number and + // the map register base. + // + + numberOfMapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress, Length); + mapRegisterNumber = LogicalAddress.LowPart >> PAGE_SHIFT; + mapRegisterBase = (PTRANSLATION_ENTRY) MasterAdapterObject->MapRegisterBase + mapRegisterNumber; + + // + // Free the map registers. + // + + IoFreeMapRegisters(AdapterObject, + (PVOID) mapRegisterBase, + numberOfMapRegisters); + + } + + // + // Free the memory for the common buffer. + // + + if (CacheEnabled != FALSE) { + ExFreePool(VirtualAddress); + + } else { + + if (Length < PAGE_SIZE) { + + MmFreeNonCachedMemory(VirtualAddress, Length); + + } else { + + MmUnmapIoSpace(VirtualAddress, Length); + + } + } + + } + + return; + +} + + + +/*++ + +Routine Description: + + This function returns the appropriate adapter object for the device defined + in the device description structure. Three bus types are supported for the + system: PCI, Isa, and Eisa. + +Arguments: + + DeviceDescription - Supplies a description of the deivce. + + NumberOfMapRegisters - Returns the maximum number of map registers which + may be allocated by the device driver. + +Return Value: + + A pointer to the requested adapter object or NULL if an adapter could not + be created. + +--*/ + +PADAPTER_OBJECT +HalGetAdapter( + IN PDEVICE_DESCRIPTION DeviceDescription, + IN OUT PULONG NumberOfMapRegisters + ) + +{ + PADAPTER_OBJECT adapterObject; + + + // + // Make sure this is the correct version. + // + + if (DeviceDescription->Version > DEVICE_DESCRIPTION_VERSION1) { + + return(NULL); + + } + + // + // If the request is for a unsupported bus then return NULL. + // + + if (DeviceDescription->InterfaceType != Internal + && DeviceDescription->InterfaceType != PCIBus + && DeviceDescription->InterfaceType != Isa + && DeviceDescription->InterfaceType != Eisa) { + + return(NULL); + + } + + // + // Set the maximum number of map registers if requested. + // + + if (NumberOfMapRegisters != NULL) { + + // + // Return number of map registers requested based on the maximum + // transfer length. + // + + *NumberOfMapRegisters = BYTES_TO_PAGES(DeviceDescription->MaximumLength) + 1; + + } + + if (DeviceDescription->InterfaceType == PCIBus) { + + // + // Create a PCI adapter object. + // + + if (HalpPciAdapterObject == NULL) { + + adapterObject = HalpAllocateAdapter(0, NULL, NULL); + adapterObject->InterfaceType = DeviceDescription->InterfaceType; + adapterObject->MasterAdapter = NULL; + HalpPciAdapterObject = adapterObject; + + } else { + + adapterObject = HalpPciAdapterObject; + + } + + } else { + + // + // Create an EISA adapter object. + // + + adapterObject = HalpAllocateEisaAdapter(DeviceDescription); + + if (adapterObject == NULL) { + + return NULL; + + } else { + + if ( *NumberOfMapRegisters > (MasterAdapterObject->MapRegistersPerChannel / 4) ) { + *NumberOfMapRegisters = MasterAdapterObject->MapRegistersPerChannel / 4; + } + + } + + // + // Set auto-initialize mode according to device's descriptor + // so that we don't use scatter/gather for common buffer + // devices + // + + adapterObject->AutoInitialize = DeviceDescription->AutoInitialize; + + // + // !!! HACK !!! + // + // This may not be necessary but it + // doesn't hurt given that the only + // devices who use this mode are sound + // cards. + // + + if (adapterObject->AutoInitialize) { + + *NumberOfMapRegisters = 1; + + } + + } + + return(adapterObject); +} + + +BOOLEAN +HalpTranslateSystemBusAddress( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ) +{ + + // + // We must replicate bits 31:28 into + // bits 35:32 so that in a wide mode + // system, both system asics can see + // the upper nibble of the address. + // + + TranslatedAddress->QuadPart = BusAddress.QuadPart; + + if (*AddressSpace) { + + // + // EISA IO Space + // + // Note : for the keyboard controller, we need to munge the + // low-order address to be word-aligned as opposed to + // the previous byte-aligned address used by MIPS + // machines such as JAZZ, STRIKER and DUO. + // + + if ((BusAddress.LowPart == 0xE0000061) || (BusAddress.LowPart == 0xA0000061)) { + + TranslatedAddress->LowPart = EISA_IO_PHYSICAL_BASE | 0x00000064; + TranslatedAddress->HighPart = TranslatedAddress->LowPart >> 28; + + } else { + + TranslatedAddress->LowPart = BusAddress.LowPart | EISA_IO_PHYSICAL_BASE; + TranslatedAddress->HighPart = TranslatedAddress->LowPart >> 28; + + } + + } else { + + // + // EISA Memory Space + // + // Note : for the keyboard controller, we need to munge the + // low-order address to be word-aligned as opposed to + // the previous byte-aligned address used by MIPS + // machines such as JAZZ, STRIKER and DUO. + // + + if ((BusAddress.LowPart == 0xE0000061) || (BusAddress.LowPart == 0xA0000061)) { + + TranslatedAddress->LowPart = EISA_IO_PHYSICAL_BASE | 0x00000064; + TranslatedAddress->HighPart = TranslatedAddress->LowPart >> 28; + + } else { + + TranslatedAddress->LowPart = BusAddress.LowPart | EISA_MEMORY_PHYSICAL_BASE; + TranslatedAddress->HighPart = TranslatedAddress->LowPart >> 28; + + } + + } + + *AddressSpace = 0; + + return(TRUE); + +} + +BOOLEAN +HalpTranslateEisaBusAddress( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ) +{ + PSUPPORTED_RANGE pRange; + + pRange = NULL; + switch (*AddressSpace) { + case 0: + // verify memory address is within buses memory limits + for (pRange = &BusHandler->BusAddresses->PrefetchMemory; pRange; pRange = pRange->Next) { + if (BusAddress.QuadPart >= pRange->Base && + BusAddress.QuadPart <= pRange->Limit) { + break; + } + } + + if (!pRange) { + for (pRange = &BusHandler->BusAddresses->Memory; pRange; pRange = pRange->Next) { + if (BusAddress.QuadPart >= pRange->Base && + BusAddress.QuadPart <= pRange->Limit) { + break; + } + } + } + + break; + + case 1: + // verify IO address is within buses IO limits + for (pRange = &BusHandler->BusAddresses->IO; pRange; pRange = pRange->Next) { + if (BusAddress.QuadPart >= pRange->Base && + BusAddress.QuadPart <= pRange->Limit) { + break; + } + } + break; + } + + if (pRange) { + + TranslatedAddress->QuadPart = BusAddress.QuadPart; + + // + // We must replicate bits 31:28 into + // bits 35:32 so that in a wide mode + // system, both system asics can see + // the upper nibble of the address. + // + + + if (*AddressSpace) { + + // + // EISA IO Space + // + // Note : for the keyboard controller, we need to munge the + // low-order address to be word-aligned as opposed to + // the previous byte-aligned address used by MIPS + // machines such as JAZZ, STRIKER and DUO. + // + + if ((BusAddress.LowPart == 0xE0000061) || (BusAddress.LowPart == 0xA0000061)) { + + TranslatedAddress->LowPart = EISA_IO_PHYSICAL_BASE | 0x00000064; + TranslatedAddress->HighPart = TranslatedAddress->LowPart >> 28; + + } else { + + TranslatedAddress->LowPart = BusAddress.LowPart | EISA_IO_PHYSICAL_BASE; + TranslatedAddress->HighPart = TranslatedAddress->LowPart >> 28; + + } + + } else { + + // + // EISA Memory Space + // + // Note : for the keyboard controller, we need to munge the + // low-order address to be word-aligned as opposed to + // the previous byte-aligned address used by MIPS + // machines such as JAZZ, STRIKER and DUO. + // + + if ((BusAddress.LowPart == 0xE0000061) || (BusAddress.LowPart == 0xA0000061)) { + + TranslatedAddress->LowPart = EISA_IO_PHYSICAL_BASE | 0x00000064; + TranslatedAddress->HighPart = TranslatedAddress->LowPart >> 28; + + } else { + + TranslatedAddress->LowPart = BusAddress.LowPart | EISA_MEMORY_PHYSICAL_BASE; + TranslatedAddress->HighPart = TranslatedAddress->LowPart >> 28; + + } + + } + + *AddressSpace = 0; + + return(TRUE); + + } + + return(FALSE); + +} + +BOOLEAN +HalpTranslatePCIBusAddress( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ) +{ + PSUPPORTED_RANGE pRange; + + pRange = NULL; + switch (*AddressSpace) { + case 0: + // verify memory address is within buses memory limits + for (pRange = &BusHandler->BusAddresses->PrefetchMemory; pRange; pRange = pRange->Next) { + if (BusAddress.QuadPart >= pRange->Base && + BusAddress.QuadPart <= pRange->Limit) { + break; + } + } + + if (!pRange) { + for (pRange = &BusHandler->BusAddresses->Memory; pRange; pRange = pRange->Next) { + if (BusAddress.QuadPart >= pRange->Base && + BusAddress.QuadPart <= pRange->Limit) { + break; + } + } + } + + break; + + case 1: + // verify IO address is within buses IO limits + for (pRange = &BusHandler->BusAddresses->IO; pRange; pRange = pRange->Next) { + if (BusAddress.QuadPart >= pRange->Base && + BusAddress.QuadPart <= pRange->Limit) { + break; + } + } + break; + } + + if (pRange) { + + // + // We must replicate bits 31:28 into + // bits 35:32 so that in a wide mode + // system, both system asics can see + // the upper nibble of the address. + // + + TranslatedAddress->QuadPart = BusAddress.QuadPart; + + if (*AddressSpace) { + + // + // PCI IO Space + // + + TranslatedAddress->LowPart = BusAddress.LowPart | PCI_IO_PHYSICAL_BASE; + TranslatedAddress->HighPart = TranslatedAddress->LowPart >> 28; + + } else { + + // + // PCI Memory Space + // + + TranslatedAddress->LowPart = BusAddress.LowPart | PCI_MEMORY_PHYSICAL_BASE; + TranslatedAddress->HighPart = TranslatedAddress->LowPart >> 28; + + } + + *AddressSpace = 0; + + return(TRUE); + + } + + return(FALSE); + +} + + + +/*++ + +Routine Description: + + This routine allocates and initializes an adapter object to represent an + adapter or a DMA controller on the system. + +Arguments: + + MapRegistersPerChannel : 0 means PCI adapter or EISA adapter; + otherwise, its the master adapter + + AdapterBaseVa : Base virtual address of the adapter itself. + If NULL, then assume master adapter object. + + MapRegisterBase : unused. + +Return Value: + + The function value is a pointer to the allocate adapter object. + +--*/ + +PADAPTER_OBJECT +HalpAllocateAdapter( + IN ULONG MapRegistersPerChannel, + IN PVOID AdapterBaseVa, + IN PVOID MapRegisterBase + ) + +{ + + PADAPTER_OBJECT AdapterObject; + OBJECT_ATTRIBUTES ObjectAttributes; + ULONG Size; + ULONG BitmapSize; + HANDLE Handle; + NTSTATUS Status; + + // + // Initalize the master adapter if necessary. + // + + if (MasterAdapterObject == NULL && AdapterBaseVa != NULL ) { + + MasterAdapterObject = HalpAllocateAdapter( DMA_TRANSLATION_LIMIT / sizeof(TRANSLATION_ENTRY), NULL, NULL); + + // + // If we could not allocate the master adapter then give up. + // + + if (MasterAdapterObject == NULL) { + return(NULL); + } + + } + + // + // Begin by initializing the object attributes structure to be used when + // creating the adapter object. + // + + InitializeObjectAttributes( &ObjectAttributes, + NULL, + OBJ_PERMANENT, + (HANDLE) NULL, + (PSECURITY_DESCRIPTOR) NULL); + + // + // Determine the size of the adapter object. If this is the master object + // then allocate space for the register bit map; otherwise, just allocate + // an adapter object. + // + + if ( AdapterBaseVa == NULL && MapRegistersPerChannel ) { + + BitmapSize = (((sizeof( RTL_BITMAP ) + + ((DMA_TRANSLATION_LIMIT / sizeof( TRANSLATION_ENTRY)) + 7 >> 3)) + + 3) & ~3); + + Size = sizeof( ADAPTER_OBJECT ) + BitmapSize; + + } else { + + Size = sizeof( ADAPTER_OBJECT ); + + } + + // + // Now create the adapter object. + // + + Status = ObCreateObject(KernelMode, + *((POBJECT_TYPE *)IoAdapterObjectType), + &ObjectAttributes, + KernelMode, + (PVOID) NULL, + Size, + 0, + 0, + (PVOID *)&AdapterObject ); + + // + // Reference the object. + // + + if (NT_SUCCESS(Status)) { + + Status = ObReferenceObjectByPointer( + AdapterObject, + FILE_READ_DATA | FILE_WRITE_DATA, + *((POBJECT_TYPE *)IoAdapterObjectType), + KernelMode + ); + + } + + // + // If the adapter object was successfully created, then attempt to insert + // it into the the object table. + // + + if (NT_SUCCESS( Status )) { + + Status = ObInsertObject(AdapterObject, + NULL, + FILE_READ_DATA | FILE_WRITE_DATA, + 0, + (PVOID *) NULL, + &Handle ); + + if (NT_SUCCESS( Status )) { + + ZwClose(Handle); + + // + // Initialize the adapter object itself. + // + + AdapterObject->Type = IO_TYPE_ADAPTER; + AdapterObject->Size = (USHORT) Size; + + AdapterObject->MapRegistersPerChannel = DMA_TRANSLATION_LIMIT / sizeof( TRANSLATION_ENTRY); + + AdapterObject->AdapterBaseVa = AdapterBaseVa; + AdapterObject->PagePort = NULL; + AdapterObject->MasterAdapter = MasterAdapterObject; + + // + // Initialize the channel wait queue for this + // adapter. + // + + KeInitializeDeviceQueue( &AdapterObject->ChannelWaitQueue ); + + // + // If this is the MasterAdapter then initialize the register bit map, + // AdapterQueue and the spin lock. + // + + if ( AdapterBaseVa == NULL && MapRegistersPerChannel ) { + + KeInitializeSpinLock( &AdapterObject->SpinLock ); + + InitializeListHead( &AdapterObject->AdapterQueue ); + + AdapterObject->MapRegisters = (PVOID) ( AdapterObject + 1); + + RtlInitializeBitMap(AdapterObject->MapRegisters, + (PULONG) (((PCHAR) (AdapterObject->MapRegisters)) + sizeof( RTL_BITMAP )), + (DMA_TRANSLATION_LIMIT / sizeof( TRANSLATION_ENTRY))); + + RtlClearAllBits( AdapterObject->MapRegisters ); + + // + // Convert the physical address to a non-cached virtual address. + // + + AdapterObject->MapRegisterBase = (PVOID) (HalpMapRegisterPhysicalBase | KSEG1_BASE); + + + } + + } else { + + // + // An error was incurred for some reason. Set the return value + // to NULL. + // + + AdapterObject = (PADAPTER_OBJECT) NULL; + } + + } else { + + AdapterObject = (PADAPTER_OBJECT) NULL; + + } + + return AdapterObject; + +} + + +/*++ + +Routine Description: + + This routine deallocates the map registers for the adapter. If there are + any queued adapter waiting for an attempt is made to allocate the next + entry. + +Arguments: + + AdapterObject - The adapter object to where the map register should be + returned. + + MapRegisterBase - The map register base of the registers to be deallocated. + + NumberOfMapRegisters - The number of registers to be deallocated. + +Return Value: + + None + +--+*/ + +VOID +IoFreeMapRegisters( + PADAPTER_OBJECT AdapterObject, + PVOID MapRegisterBase, + ULONG NumberOfMapRegisters + ) + +{ + PADAPTER_OBJECT MasterAdapter; + LONG MapRegisterNumber; + PLIST_ENTRY Packet; + IO_ALLOCATION_ACTION Action; + PWAIT_CONTEXT_BLOCK Wcb; + KIRQL Irql; + ULONG Hint; + + + // + // Do this only for non-PCI devices + // + + if (AdapterObject->InterfaceType != PCIBus) { + + // + // Begin by getting the address of the master adapter. + // + + if (AdapterObject->MasterAdapter != NULL) { + MasterAdapter = AdapterObject->MasterAdapter; + } else { + MasterAdapter = AdapterObject; + } + + MapRegisterNumber = (PTRANSLATION_ENTRY) MapRegisterBase - + (PTRANSLATION_ENTRY) MasterAdapter->MapRegisterBase; + + // + // Acquire the master adapter spinlock which locks the adapter queue and the + // bit map for the map registers. + // + + KeAcquireSpinLock(&MasterAdapter->SpinLock, &Irql); + + // + // Return the registers to the bit map. + // + + RtlClearBits( MasterAdapter->MapRegisters, MapRegisterNumber, NumberOfMapRegisters); + + // + // Process any requests waiting for map registers in the adapter queue. + // Requests are processed until a request cannot be satisfied or until + // there are no more requests in the queue. + // + + while(TRUE) { + + if ( IsListEmpty(&MasterAdapter->AdapterQueue) ){ + break; + } + + Packet = RemoveHeadList( &MasterAdapter->AdapterQueue ); + AdapterObject = CONTAINING_RECORD( Packet, ADAPTER_OBJECT, AdapterQueue); + Wcb = AdapterObject->CurrentWcb; + + // + // Attempt to allocate map registers for this request. + // + + Hint = 0; + MapRegisterNumber = RtlFindClearBitsAndSet( MasterAdapter->MapRegisters, NumberOfMapRegisters, Hint); + + // + // Make sure this map register is valid for this adapter. + // + + if ((ULONG) MapRegisterNumber < Hint) { + + // + // Make it look like there are no map registers. + // + + RtlClearBits(MasterAdapter->MapRegisters, MapRegisterNumber, NumberOfMapRegisters); + + MapRegisterNumber = -1; + + } + + if (MapRegisterNumber == -1) { + + // + // There were not enough free map registers. Put this request back on + // the adapter queue where it came from. + // + + InsertHeadList( &MasterAdapter->AdapterQueue, &AdapterObject->AdapterQueue); + break; + + } + + // + // Release spin lock + // + + KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql ); + + // + // Adjust map register base address + // + + AdapterObject->MapRegisterBase = (PVOID) ((PTRANSLATION_ENTRY) MasterAdapter->MapRegisterBase + MapRegisterNumber); + + // + // Invoke the driver's execution routine now. + // + + Action = Wcb->DeviceRoutine( Wcb->DeviceObject, + Wcb->CurrentIrp, + AdapterObject->MapRegisterBase, + Wcb->DeviceContext); + + // + // If the driver wishes to keep the map registers then set the number + // allocated to zero and set the action to deallocate object. + // + + if (Action == DeallocateObjectKeepRegisters) { + AdapterObject->NumberOfMapRegisters = 0; + Action = DeallocateObject; + } + + // + // If the driver would like to have the adapter deallocated, + // then deallocate any map registers allocated and then release + // the adapter object. + // + + if (Action == DeallocateObject) { + + // + // The map registers are deallocated here rather than in + // IoFreeAdapterChannel. This limits the number of times + // this routine can be called recursively possibly overflowing + // the stack. The worst case occurs if there is a pending + // request for the adapter that uses map registers and whose + // execution routine decallocates the adapter. In that case, if there + // are no requests in the master adapter queue, then IoFreeMapRegisters + // will get called again. + // + + if (AdapterObject->NumberOfMapRegisters != 0) { + + // + // Deallocate the map registers and clear the count so that + // IoFreeAdapterChannel will not deallocate them again. + // + + KeAcquireSpinLock( &MasterAdapter->SpinLock, &Irql ); + + RtlClearBits( MasterAdapter->MapRegisters, MapRegisterNumber, AdapterObject->NumberOfMapRegisters); + + AdapterObject->NumberOfMapRegisters = 0; + + KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql ); + } + + IoFreeAdapterChannel( AdapterObject ); + } + + KeAcquireSpinLock( &MasterAdapter->SpinLock, &Irql ); + + } + + KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql ); + + } +} + + + +/*++ + +Routine Description: + + This routine is invoked to deallocate the specified adapter object. + Any map registers that were allocated are also automatically deallocated. + No checks are made to ensure that the adapter is really allocated to + a device object. However, if it is not, then kernel will bugcheck. + + If another device is waiting in the queue to allocate the adapter object + it will be pulled from the queue and its execution routine will be + invoked. + +Arguments: + + AdapterObject - Pointer to the adapter object to be deallocated. + +Return Value: + + None. + +--*/ + +VOID +IoFreeAdapterChannel( + IN PADAPTER_OBJECT AdapterObject + ) + +{ + PKDEVICE_QUEUE_ENTRY Packet; + PADAPTER_OBJECT MasterAdapter; + BOOLEAN Busy = FALSE; + IO_ALLOCATION_ACTION Action; + PWAIT_CONTEXT_BLOCK Wcb; + KIRQL Irql; + LONG MapRegisterNumber; + ULONG Hint; + + // + // For PCI devices we don't need to deal with + // map registers. For EISA devices we do. + // + + if (AdapterObject->InterfaceType == PCIBus) { + + while( TRUE ) { + + // + // Simply remove the next entry from the adapter's device wait queue. + // If there were no waiting jobs, break out of loop. + // + + Packet = KeRemoveDeviceQueue( &AdapterObject->ChannelWaitQueue ); + + if (Packet == NULL) { + + // + // There are no more requests break out of the loop. + // + + break; + } + + // + // Get current WCB + // + + Wcb = CONTAINING_RECORD( Packet, WAIT_CONTEXT_BLOCK, WaitQueueEntry); + + // + // Invoke the driver's execution routine now. + // + + AdapterObject->CurrentWcb = Wcb; + AdapterObject->NumberOfMapRegisters = Wcb->NumberOfMapRegisters; + + Action = Wcb->DeviceRoutine( Wcb->DeviceObject, + Wcb->CurrentIrp, + AdapterObject->MapRegisterBase, + Wcb->DeviceContext); + + // + // If the execution routine would like to have the adapter + // deallocated, then release the adapter object. + // + + if (Action == KeepObject) { + + // + // This request wants to keep the channel a while so break + // out of the loop. + // + + break; + } + + // + // If the driver wants to keep the map registers then set the + // number allocated to 0. This keeps the deallocation routine + // from deallocating them. + // + + if (Action == DeallocateObjectKeepRegisters) { + + AdapterObject->NumberOfMapRegisters = 0; + + } + + } + + } else { + + // + // Begin by getting the address of the master adapter. + // + + if (AdapterObject->MasterAdapter != NULL) { + MasterAdapter = AdapterObject->MasterAdapter; + } else { + MasterAdapter = AdapterObject; + } + + // + // Pull requests off the adapter's device wait queue as long as the + // adapter is free and there are sufficient map registers available. + // + + while( TRUE ){ + + // + // Begin by checking to see whether there are any map registers that + // need to be deallocated. If so, then deallocate them now. + // + + if (AdapterObject->NumberOfMapRegisters != 0) { + + IoFreeMapRegisters( AdapterObject, + AdapterObject->MapRegisterBase, + AdapterObject->NumberOfMapRegisters); + + } + + // + // Simply remove the next entry from the adapter's device wait queue. + // If one was successfully removed, allocate any map registers that it + // requires and invoke its execution routine. + // + + Packet = KeRemoveDeviceQueue( &AdapterObject->ChannelWaitQueue ); + + if (Packet == NULL) { + + // + // There are no more requests break out of the loop. + // + + break; + } + + Wcb = CONTAINING_RECORD( Packet, WAIT_CONTEXT_BLOCK, WaitQueueEntry); + AdapterObject->CurrentWcb = Wcb; + AdapterObject->NumberOfMapRegisters = Wcb->NumberOfMapRegisters; + + // + // Check to see whether this driver wishes to allocate any map + // registers. If so, then queue the device object to the master + // adapter queue to wait for them to become available. If the driver + // wants map registers, ensure that this adapter has enough total + // map registers to satisfy the request. + // + + if (Wcb->NumberOfMapRegisters != 0) { + + if (Wcb->NumberOfMapRegisters > MasterAdapter->MapRegistersPerChannel) { + KeBugCheck( INSUFFICIENT_SYSTEM_MAP_REGS ); + } + + // + // Lock the map register bit map and the adapter queue in the + // master adapter object. The channel structure offset is used as + // a hint for the register search. + // + + KeAcquireSpinLock( &MasterAdapter->SpinLock, &Irql ); + + MapRegisterNumber = -1; + + if (IsListEmpty( &MasterAdapter->AdapterQueue)) { + + Hint = 0; + + MapRegisterNumber = RtlFindClearBitsAndSet( MasterAdapter->MapRegisters, + Wcb->NumberOfMapRegisters, + Hint); + + // + // Make sure this map register is valid for this adapter. + // + + if ((ULONG) MapRegisterNumber < Hint) { + + // + // Make it look like there are no map registers. + // + + RtlClearBits( MasterAdapter->MapRegisters, + MapRegisterNumber, + Wcb->NumberOfMapRegisters); + + MapRegisterNumber = -1; + + } + + } + + if (MapRegisterNumber == -1) { + + // + // There were not enough free map registers. Queue this request + // on the master adapter where is will wait until some registers + // are deallocated. + // + + InsertTailList( &MasterAdapter->AdapterQueue, &AdapterObject->AdapterQueue); + Busy = 1; + + } else { + + AdapterObject->MapRegisterBase = (PVOID) ((PTRANSLATION_ENTRY) MasterAdapter->MapRegisterBase + MapRegisterNumber); + + } + + KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql ); + } + + // + // If there were either enough map registers available or no map + // registers needed to be allocated, invoke the driver's execution + // routine now. + // + + if (!Busy) { + + AdapterObject->CurrentWcb = Wcb; + Action = Wcb->DeviceRoutine( Wcb->DeviceObject, + Wcb->CurrentIrp, + AdapterObject->MapRegisterBase, + Wcb->DeviceContext); + + // + // If the execution routine would like to have the adapter + // deallocated, then release the adapter object. + // + + if (Action == KeepObject) { + + // + // This request wants to keep the channel a while so break + // out of the loop. + // + + break; + } + + // + // If the driver wants to keep the map registers then set the + // number allocated to 0. This keeps the deallocation routine + // from deallocating them. + // + + if (Action == DeallocateObjectKeepRegisters) { + + AdapterObject->NumberOfMapRegisters = 0; + + } + + } else { + + // + // This request did not get the requested number of map registers so + // out of the loop. + // + + break; + + } + + } + + } + +} + + + +/*++ + +Routine Description: + + This routine is invoked to set up the map registers in the DMA controller + to allow a transfer to or from a device. + +Arguments: + + AdapterObject - Pointer to the adapter object representing the DMA + controller channel that has been allocated. + + Mdl - Pointer to the MDL that describes the pages of memory that are + being read or written. + + MapRegisterBase - The address of the base map register that has been + allocated to the device driver for use in mapping the transfer. + + CurrentVa - Current virtual address in the buffer described by the MDL + that the transfer is being done to or from. + + Length - Supplies the length of the transfer. This determines the + number of map registers that need to be written to map the transfer. + Returns the length of the transfer which was actually mapped. + + WriteToDevice - Boolean value that indicates whether this is a write + to the device from memory (TRUE), or vice versa. + +Return Value: + + Returns the logical address to be used by bus masters. + +--*/ + +PHYSICAL_ADDRESS +IoMapTransfer( + IN PADAPTER_OBJECT AdapterObject, + IN PMDL Mdl, + IN PVOID MapRegisterBase, + IN PVOID CurrentVa, + IN OUT PULONG Length, + IN BOOLEAN WriteToDevice + ) + +{ + PTRANSLATION_ENTRY DmaMapRegister = (PTRANSLATION_ENTRY)MapRegisterBase; + ULONG SgtMemoryAddress; + PULONG PageFrameNumber; + ULONG NumberOfPages; + ULONG Offset; + ULONG LogicalAddress; + ULONG EisaLogicalAddress; + ULONG TransferLength; + ULONG i; + + + // + // Begin by determining where in the buffer this portion of the operation + // is taking place. + // + + Offset = BYTE_OFFSET( (PCHAR) CurrentVa - (PCHAR) Mdl->StartVa ); + TransferLength = PAGE_SIZE - Offset; + + NumberOfPages = (Offset + *Length + PAGE_SIZE - 1) >> PAGE_SHIFT; + + PageFrameNumber = (PULONG) (Mdl + 1); + PageFrameNumber += (((PCHAR) CurrentVa - (PCHAR) Mdl->StartVa) >> PAGE_SHIFT); + + // + // Calculate LogicalAddress + // + + LogicalAddress = ((*PageFrameNumber << PAGE_SHIFT) + Offset) & 0x7FFFFFFF; + LogicalAddress += HalpPciMemoryOffset; + + + if ( AdapterObject == NULL || AdapterObject->InterfaceType == PCIBus) { + + if (NumberOfPages > 1) { + + // + // Determine how much of the buffer is + // physically contiguous (if a multiple-page transfer) + // and adjust *Length accordingly. + // + + while( TransferLength < *Length ){ + + // + // If next page in mdl not adjacent + // to current page, break out of loop + // + + if ((*PageFrameNumber + 1) != *(PageFrameNumber + 1)) + break; + + // + // Adjust TransferLength and + // mdl pointer + // + + TransferLength += PAGE_SIZE; + PageFrameNumber++; + + } + + // + // Limit the TransferLength to the requested Length. + // + + *Length = (TransferLength > *Length) ? *Length : TransferLength; + + } + + return(RtlConvertUlongToLargeInteger(LogicalAddress)); + + } else { + + // + // Devices which do not use auto-initialize + // CommonBuffer DMA can use the scatter/gather + // capabilities of the 82374 which we manage + // through map registers. + // + + if (!AdapterObject->AutoInitialize) { + + EisaLogicalAddress = LogicalAddress; + + // + // Setup Scatter/Gather Table in the 82374 + // based on the length of the transfer + // + if (NumberOfPages > 1) { + + // + // Setup first page as a special case where + // count is less than a page size. + // + + DmaMapRegister->Address = (ULONG) EisaLogicalAddress; + DmaMapRegister->ByteCountAndEol = (ULONG) (TransferLength - 1); + DmaMapRegister++; + PageFrameNumber++; + EisaLogicalAddress = (*PageFrameNumber << PAGE_SHIFT) & 0x7FFFFFFF; + EisaLogicalAddress += HalpPciMemoryOffset; + + // + // Setup transfers that are each a page size + // in length except for the last page which + // we will special case due to the EOL bit + // that we must set to signify the end of the + // scatter-gather list. + // + + for (i = 1; i < (NumberOfPages-1); i++) { + + DmaMapRegister->Address = (ULONG) EisaLogicalAddress; + DmaMapRegister->ByteCountAndEol = (ULONG) (PAGE_SIZE - 1); + TransferLength += PAGE_SIZE; + DmaMapRegister++; + PageFrameNumber++; + EisaLogicalAddress = (*PageFrameNumber << PAGE_SHIFT) & 0x7FFFFFFF; + EisaLogicalAddress += HalpPciMemoryOffset; + + } + + // + // Now setup last page including the EOL bit. + // + + DmaMapRegister->Address = (ULONG) EisaLogicalAddress; + DmaMapRegister->ByteCountAndEol = (ULONG) (*Length - TransferLength - 1) | SCATTER_GATHER_EOL; + TransferLength += (*Length - TransferLength); + + } else { + + DmaMapRegister->Address = (ULONG) EisaLogicalAddress; + DmaMapRegister->ByteCountAndEol = (ULONG) (*Length - 1) | SCATTER_GATHER_EOL; + TransferLength = *Length; + + } + + // + // Limit the TransferLength to the requested Length. + // + + *Length = (TransferLength > *Length) ? *Length : TransferLength; + + // + // Calculate the start of the SGD Table for this + // particular transfer + // + + SgtMemoryAddress = (ULONG)MapRegisterBase & ~KSEG1_BASE; + SgtMemoryAddress += HalpPciMemoryOffset; + + // + // Start the EISA DMA controller. + // + + HalpEisaMapTransfer( AdapterObject, SgtMemoryAddress, *Length, WriteToDevice); + + return(RtlConvertUlongToLargeInteger(LogicalAddress)); + + } else { + + // + // For CommonBuffer devices (i.e., auto-initialize mode + // which cannot be used in conjunction with the 82374's + // scatter/gather mode) we will attempt to transfer the + // largest quantity possible based on how contiguous the + // buffer is. + // + + if (NumberOfPages > 1) { + + // + // Determine how much of the buffer is + // physically contiguous (if a multiple-page transfer) + // and adjust TransferLength accordingly. + // + + while( TransferLength < *Length ){ + + // + // If next page in mdl not adjacent + // to current page, or if it crosses + // a 64K boundary, break out of loop + // + + if ( (*PageFrameNumber + 1) != *(PageFrameNumber + 1) ) + break; + + // + // Adjust TransferLength and + // mdl pointer + // + + TransferLength += PAGE_SIZE; + PageFrameNumber++; + + } + + // + // Limit the TransferLength to the requested Length + // or less than the requested Length if pages were + // non-contiguous. + // + + *Length = (TransferLength > *Length) ? *Length : TransferLength; + + } + + HalpEisaMapTransfer( AdapterObject, LogicalAddress, *Length, WriteToDevice); + + return(RtlConvertUlongToLargeInteger(LogicalAddress)); + + } + + } + +} + + +/*++ + +Routine Description: + + This routine flushes the DMA adapter object buffers and clears the + enable flag which aborts the dma. + +Arguments: + + AdapterObject - Pointer to the adapter object representing the DMA + controller channel. + + Mdl - A pointer to a Memory Descriptor List (MDL) that maps the locked-down + buffer to/from which the I/O occured. + + MapRegisterBase - A pointer to the base of the map registers in the adapter + or DMA controller. + + CurrentVa - The current virtual address in the buffer described the the Mdl + where the I/O operation occurred. + + Length - Supplies the length of the transfer. + + WriteToDevice - Supplies a BOOLEAN value that indicates the direction of + the data transfer was to the device. + +Return Value: + + TRUE - If the transfer was successful. + + FALSE - If there was an error in the transfer. + +--*/ + +BOOLEAN +IoFlushAdapterBuffers( + IN PADAPTER_OBJECT AdapterObject, + IN PMDL Mdl, + IN PVOID MapRegisterBase, + IN PVOID CurrentVa, + IN ULONG Length, + IN BOOLEAN WriteToDevice + ) + +{ + + UCHAR DataByte; + + if (AdapterObject == NULL) { + + // + // This is a master adapter so there is nothing to do. + // + + return(TRUE); + + } + + // + // If this is a master channel, then just return since the DMA + // request does not need to be disabled. + // + + DataByte = AdapterObject->AdapterMode; + + if (((PDMA_EISA_MODE) &DataByte)->RequestMode == CASCADE_REQUEST_MODE) { + + return(TRUE); + + } + + // + // Clear the EISA DMA adapter. + // + + if (AdapterObject->AdapterNumber == 1) { + + // + // This request is for DMA controller 1 + // + + PDMA1_CONTROL dmaControl; + + dmaControl = AdapterObject->AdapterBaseVa; + + HalpDma1Status = READ_REGISTER_UCHAR( &dmaControl->DmaStatus); + + WRITE_REGISTER_UCHAR( &dmaControl->SingleMask, + (UCHAR) (DMA_SETMASK | AdapterObject->ChannelNumber) ); + + } else { + + // + // This request is for DMA controller 2 + // + + PDMA2_CONTROL dmaControl; + + dmaControl = AdapterObject->AdapterBaseVa; + + HalpDma2Status = READ_REGISTER_UCHAR( &dmaControl->DmaStatus); + + WRITE_REGISTER_UCHAR( &dmaControl->SingleMask, + (UCHAR) (DMA_SETMASK | AdapterObject->ChannelNumber) ); + + } + + return(TRUE); + +} + + + +/*++ + +Routine Description: + + This function is called by HalAllocateAdapterChannel when sufficent resources + are available to the driver. This routine saves the MapRegisterBase, + and set the event pointed to by the context parameter. + +Arguments: + + DeviceObject - Supplies a pointer where the map register base should be + stored. + + Irp - Unused. + + MapRegisterBase - Supplied by the Io subsystem for use in IoMapTransfer. + + Context - Supplies a pointer to an event which is set to indicate the + AdapterObject has been allocated. + +Return Value: + + DeallocateObjectKeepRegisters - Indicates the adapter should be freed + and mapregisters should remain allocated after return. + +--*/ + +IO_ALLOCATION_ACTION +HalpAllocationRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID MapRegisterBase, + IN PVOID Context + ) + +{ + + UNREFERENCED_PARAMETER(Irp); + + *((PVOID *) DeviceObject) = MapRegisterBase; + + (VOID) KeSetEvent( (PKEVENT) Context, 0L, FALSE ); + + return(DeallocateObjectKeepRegisters); +} + + +ULONG +HalpGetEisaData ( + IN PBUS_HANDLER BusHandler, + IN PBUS_HANDLER RootHandler, + IN ULONG SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ) +/*++ + +Routine Description: + + The function returns the Eisa bus data for a slot or address. + +Arguments: + + Buffer - Supplies the space to store the data. + + Length - Supplies a count in bytes of the maximum amount to return. + +Return Value: + + Returns the amount of data stored into the buffer. + +--*/ + +{ + OBJECT_ATTRIBUTES ObjectAttributes; + OBJECT_ATTRIBUTES BusObjectAttributes; + PWSTR EisaPath = L"\\Registry\\Machine\\Hardware\\Description\\System\\EisaAdapter"; + PWSTR ConfigData = L"Configuration Data"; + ANSI_STRING TmpString; + ULONG BusNumber; + UCHAR BusString[] = "00"; + UNICODE_STRING RootName, BusName; + UNICODE_STRING ConfigDataName; + NTSTATUS NtStatus; + PKEY_VALUE_FULL_INFORMATION ValueInformation; + PCM_FULL_RESOURCE_DESCRIPTOR Descriptor; + PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResource; + PCM_EISA_SLOT_INFORMATION SlotInformation; + ULONG PartialCount; + ULONG TotalDataSize, SlotDataSize; + HANDLE EisaHandle, BusHandle; + ULONG BytesWritten, BytesNeeded; + PUCHAR KeyValueBuffer; + ULONG i; + ULONG DataLength = 0; + PUCHAR DataBuffer = Buffer; + BOOLEAN Found = FALSE; + + + RtlInitUnicodeString( + &RootName, + EisaPath + ); + + InitializeObjectAttributes( + &ObjectAttributes, + &RootName, + OBJ_CASE_INSENSITIVE, + (HANDLE)NULL, + NULL + ); + + // + // Open the EISA root + // + + NtStatus = ZwOpenKey( + &EisaHandle, + KEY_READ, + &ObjectAttributes + ); + + if (!NT_SUCCESS(NtStatus)) { +#if DBG + DbgPrint("HAL: Open Status = %x\n",NtStatus); +#endif + return(0); + } + + // + // Init bus number path + // + + BusNumber = BusHandler->BusNumber; + if (BusNumber > 99) { + return (0); + } + + if (BusNumber > 9) { + BusString[0] += (UCHAR) (BusNumber/10); + BusString[1] += (UCHAR) (BusNumber % 10); + } else { + BusString[0] += (UCHAR) BusNumber; + BusString[1] = '\0'; + } + + RtlInitAnsiString( + &TmpString, + BusString + ); + + RtlAnsiStringToUnicodeString( + &BusName, + &TmpString, + TRUE + ); + + + InitializeObjectAttributes( + &BusObjectAttributes, + &BusName, + OBJ_CASE_INSENSITIVE, + (HANDLE)EisaHandle, + NULL + ); + + // + // Open the EISA root + Bus Number + // + + NtStatus = ZwOpenKey( + &BusHandle, + KEY_READ, + &BusObjectAttributes + ); + + if (!NT_SUCCESS(NtStatus)) { +#if DBG + DbgPrint("HAL: Opening Bus Number: Status = %x\n",NtStatus); +#endif + return(0); + } + + // + // opening the configuration data. This first call tells us how + // much memory we need to allocate + // + + RtlInitUnicodeString( + &ConfigDataName, + ConfigData + ); + + // + // This should fail. We need to make this call so we can + // get the actual size of the buffer to allocate. + // + + ValueInformation = (PKEY_VALUE_FULL_INFORMATION) &i; + NtStatus = ZwQueryValueKey( + BusHandle, + &ConfigDataName, + KeyValueFullInformation, + ValueInformation, + 0, + &BytesNeeded + ); + + KeyValueBuffer = ExAllocatePool( + NonPagedPool, + BytesNeeded + ); + + if (KeyValueBuffer == NULL) { +#if DBG + DbgPrint("HAL: Cannot allocate Key Value Buffer\n"); +#endif + ZwClose(BusHandle); + return(0); + } + + ValueInformation = (PKEY_VALUE_FULL_INFORMATION)KeyValueBuffer; + + NtStatus = ZwQueryValueKey( + BusHandle, + &ConfigDataName, + KeyValueFullInformation, + ValueInformation, + BytesNeeded, + &BytesWritten + ); + + + ZwClose(BusHandle); + + if (!NT_SUCCESS(NtStatus)) { +#if DBG + DbgPrint("HAL: Query Config Data: Status = %x\n",NtStatus); +#endif + ExFreePool(KeyValueBuffer); + return(0); + } + + + // + // We get back a Full Resource Descriptor List + // + + Descriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PUCHAR)ValueInformation + + ValueInformation->DataOffset); + + PartialResource = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) + &(Descriptor->PartialResourceList.PartialDescriptors); + PartialCount = Descriptor->PartialResourceList.Count; + + for (i = 0; i < PartialCount; i++) { + + // + // Do each partial Resource + // + + switch (PartialResource->Type) { + case CmResourceTypeNull: + case CmResourceTypePort: + case CmResourceTypeInterrupt: + case CmResourceTypeMemory: + case CmResourceTypeDma: + + // + // We dont care about these. + // + + PartialResource++; + + break; + + case CmResourceTypeDeviceSpecific: + + // + // Bingo! + // + + TotalDataSize = PartialResource->u.DeviceSpecificData.DataSize; + + SlotInformation = (PCM_EISA_SLOT_INFORMATION) + ((PUCHAR)PartialResource + + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)); + + while (((LONG)TotalDataSize) > 0) { + + if (SlotInformation->ReturnCode == EISA_EMPTY_SLOT) { + + SlotDataSize = sizeof(CM_EISA_SLOT_INFORMATION); + + } else { + + SlotDataSize = sizeof(CM_EISA_SLOT_INFORMATION) + + SlotInformation->NumberFunctions * + sizeof(CM_EISA_FUNCTION_INFORMATION); + } + + if (SlotDataSize > TotalDataSize) { + + // + // Something is wrong again + // + + ExFreePool(KeyValueBuffer); + return(0); + + } + + if (SlotNumber != 0) { + + SlotNumber--; + + SlotInformation = (PCM_EISA_SLOT_INFORMATION) + ((PUCHAR)SlotInformation + SlotDataSize); + + TotalDataSize -= SlotDataSize; + + continue; + + } + + // + // This is our slot + // + + Found = TRUE; + break; + + } + + // + // End loop + // + + i = PartialCount; + + break; + + default: + +#if DBG + DbgPrint("Bad Data in registry!\n"); +#endif + + ExFreePool(KeyValueBuffer); + return(0); + + } + + } + + if (Found) { + i = Length + Offset; + if (i > SlotDataSize) { + i = SlotDataSize; + } + + DataLength = i - Offset; + RtlMoveMemory (Buffer, ((PUCHAR)SlotInformation + Offset), DataLength); + } + + ExFreePool(KeyValueBuffer); + return DataLength; +} + +/*++ + +Routine Description: + + This function reads the DMA counter and returns the number of bytes left + to be transfered. + +Arguments: + + AdapterObject - Supplies a pointer to the adapter object to be read. + +Return Value: + + Returns the number of bytes still be be transfered. + +--*/ + +ULONG +HalReadDmaCounter( + IN PADAPTER_OBJECT AdapterObject + ) + +{ + ULONG count; + ULONG high; + KIRQL Irql; + + // + // Grab the spinlock for the system DMA controller. + // + + KeAcquireSpinLock( &AdapterObject->MasterAdapter->SpinLock, &Irql ); + + // + // Determine the controller number based on the Adapter number. + // + + if (AdapterObject->AdapterNumber == 1) { + + // + // This request is for DMA controller 1 + // + + PDMA1_CONTROL dmaControl; + + dmaControl = AdapterObject->AdapterBaseVa; + + // + // Initialize count to a value which will not match. + // + + count = 0xFFFF00; + + // + // Loop until the same high byte is read twice. + // + + do { + + high = count; + + WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 ); + + // + // Read the current DMA count. + // + + count = READ_PORT_UCHAR(&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount); + + count |= READ_PORT_UCHAR(&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount) << 8; + + } while ((count & 0xFFFF00) != (high & 0xFFFF00)); + + } else { + + // + // This request is for DMA controller 2 + // + + PDMA2_CONTROL dmaControl; + + dmaControl = AdapterObject->AdapterBaseVa; + + // + // Initialize count to a value which will not match. + // + + count = 0xFFFF00; + + // + // Loop until the same high byte is read twice. + // + + do { + + high = count; + + WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 ); + + // + // Read the current DMA count. + // + + count = READ_PORT_UCHAR(&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount); + + count |= READ_PORT_UCHAR(&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount) << 8; + + } while ((count & 0xFFFF00) != (high & 0xFFFF00)); + + } + + // + // Release the spinlock for the system DMA controller. + // + + KeReleaseSpinLock( &AdapterObject->MasterAdapter->SpinLock, Irql ); + + // + // The DMA counter has a bias of one and can only be 16 bit long. + // + + count = (count + 1) & 0xFFFF; + + return(count); + +} + + + + +/*++ + +Routine Description: + + This routine allocates memory for map registers directly from the loader + block information. This memory must be non-cached and contiguous. + +Arguments: + + LoaderBlock - Pointer to the loader block which contains the memory descriptors. + +Return Value: + + None. + +--*/ + +VOID +HalpAllocateMapRegisters( + IN PLOADER_PARAMETER_BLOCK LoaderBlock + ) + +{ + PMEMORY_ALLOCATION_DESCRIPTOR Descriptor; + PLIST_ENTRY NextMd; + ULONG MaxPageAddress; + ULONG PhysicalAddress; + ULONG MapRegisterSize; + + // + // FALCON does not support an IO TLB! However, + // the 82374 does support Scatter/Gather so we + // will use it for all EISA/ISA devices that need + // to do multi-page transfers provided that they + // do not use auto-initialize mode which cannot be + // used with the 82374's scatter/gather engine. In + // the case of auto-initialize devices, we will allocate + // a 64K buffer pool from which we will allocate + // common buffers to ensure physical contiguous-ness. + // Otherwise, we would have to break up the auto-initialize + // transfers on page boundaries which does not work + // for devices like SoundBlaster, etc. + // + // In the case of PCI devices, we will break transfers + // up according to the physical contiguous-ness of the + // buffer. There are no auto-initialize limitations + // associated with PCI devices. + // + + + MapRegisterSize = DMA_TRANSLATION_LIMIT + HALP_CONTIGUOUS_BUFFER_POOL_SIZE; + MapRegisterSize = BYTES_TO_PAGES(MapRegisterSize); + + // + // The address must be in KSEG 1. + // + + MaxPageAddress = (KSEG1_BASE >> PAGE_SHIFT) - 1 ; + + // + // Scan the memory allocation descriptors and allocate map buffers + // + + NextMd = LoaderBlock->MemoryDescriptorListHead.Flink; + while (NextMd != &LoaderBlock->MemoryDescriptorListHead) { + + Descriptor = CONTAINING_RECORD(NextMd, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry); + + // + // Search for a block of memory which is contains a memory chuck + // that is greater than size pages, and has a physical address less + // than MAXIMUM_PHYSICAL_ADDRESS. + // + + if ((Descriptor->MemoryType == LoaderFree || + Descriptor->MemoryType == MemoryFirmwareTemporary) && + (Descriptor->BasePage) && + (Descriptor->PageCount >= MapRegisterSize) && + (Descriptor->BasePage + MapRegisterSize < MaxPageAddress)) { + + PhysicalAddress = Descriptor->BasePage << PAGE_SHIFT; + break; + } + + NextMd = NextMd->Flink; + } + + // + // Use the extra descriptor to define the memory at the end of the + // original block. + // + + ASSERT(NextMd != &LoaderBlock->MemoryDescriptorListHead); + + if (NextMd == &LoaderBlock->MemoryDescriptorListHead) + return; + + // + // Adjust the memory descriptors. + // + + Descriptor->BasePage += MapRegisterSize; + Descriptor->PageCount -= MapRegisterSize; + + if (Descriptor->PageCount == 0) { + + // + // The whole block was allocated, + // Remove the entry from the list completely. + // + + RemoveEntryList(&Descriptor->ListEntry); + + } + + // + // Initialize internal data structure + // responsible for maintaining address, + // size, and buffer number information. + // + + HalpMapRegisterPhysicalBase = PhysicalAddress; + + // + // Initialize the HAL buffer pool variables + // + + HalpContiguousBufferCurrentBase = HalpMapRegisterPhysicalBase + (2*PAGE_SIZE); + HalpContiguousBufferMax = HalpContiguousBufferCurrentBase + HALP_CONTIGUOUS_BUFFER_POOL_SIZE - 1; + +} + +PVOID +HalAllocateCrashDumpRegisters( + IN PADAPTER_OBJECT AdapterObject, + IN OUT PULONG NumberOfMapRegisters + ) +/*++ + +Routine Description: + + This routine is called during the crash dump disk driver's initialization + to allocate a number map registers permanently. + +Arguments: + + AdapterObject - Pointer to the adapter control object to allocate to the + driver. + +Return Value: + + Returns STATUS_SUCESS if map registers allocated. + +--*/ + +{ + PADAPTER_OBJECT MasterAdapter; + ULONG MapRegisterNumber; + ULONG Hint; + + + if (0) { + + // + // Begin by obtaining a pointer to the master adapter associated with this + // request. + // + + if (AdapterObject->MasterAdapter) { + MasterAdapter = AdapterObject->MasterAdapter; + } else { + MasterAdapter = AdapterObject; + } + + // + // Set the number of map registers required. + // + + *NumberOfMapRegisters = 16; + + // + // Ensure that this adapter has enough total map registers to satisfy + // the request. + // + + if (*NumberOfMapRegisters > AdapterObject->MapRegistersPerChannel) { + AdapterObject->NumberOfMapRegisters = 0; + return NULL; + } + + // + // Attempt to allocate the required number of map registers w/o + // affecting those registers that were allocated when the system + // crashed. Note that once again the map registers to be allocated + // must be above the 1MB range if this is an EISA bus device. + // + + MapRegisterNumber = (ULONG)-1; + + Hint = 0; + + MapRegisterNumber = RtlFindClearBitsAndSet(MasterAdapter->MapRegisters, + *NumberOfMapRegisters, + Hint); + + // + // Ensure that any allocated map registers are valid for this adapter. + // + + if ((ULONG) MapRegisterNumber < Hint) { + + // + // Make it appear as if there are no map registers. + // + + RtlClearBits(MasterAdapter->MapRegisters, + MapRegisterNumber, + *NumberOfMapRegisters); + + MapRegisterNumber = (ULONG) -1; + } + + if (MapRegisterNumber == (ULONG)-1) { + + // + // Not enough free map registers were found, so they were busy + // being used by the system when it crashed. Force the appropriate + // number to be "allocated" at the base by simply overjamming the + // bits and return the base map register as the start. + // + + RtlSetBits(MasterAdapter->MapRegisters, + Hint, + *NumberOfMapRegisters); + + MapRegisterNumber = Hint; + + } + + // + // Calculate the map register base from the allocated map + // register and base of the master adapter object. + // + + AdapterObject->MapRegisterBase = (PVOID) ((PTRANSLATION_ENTRY) MasterAdapter->MapRegisterBase + MapRegisterNumber); + + return AdapterObject->MapRegisterBase; + + } + +} + +/*++ + +Routine Description: + + This routine gets the contiguous buffer pool size from a + firmware environment variable. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +ULONG +HalpGetContiguousBufferPoolSize( + VOID + ) + +{ + register int i; + ULONG bufferpoolSize; + UCHAR buffer[20]; + ULONG status; + + status = (ULONG)HalGetEnvironmentVariable ("BUFFER_POOL_SIZE", sizeof(buffer), &buffer[0]); + + if (status) + return 0; + + bufferpoolSize = 0; + for (i=0; i < 8; i++) { + + if (buffer[i] >= '0' && buffer[i] <= '9') + bufferpoolSize = 16 * bufferpoolSize + buffer[i] - '0'; + else if (buffer[i] >= 'A' && buffer[i] <= 'F') + bufferpoolSize = 16 * bufferpoolSize + buffer[i] - 'A'; + else if (buffer[i] >= 'a' && buffer[i] <= 'f') + bufferpoolSize = 16 * bufferpoolSize + buffer[i] - 'a'; + + } + + return bufferpoolSize; + +} + + + + |