2025-04-27 07:49:33 -04:00

1416 lines
36 KiB
C

/*++
Copyright (c) 1995,1996 Microsoft Corporation
:ts=4
Module Name:
dscrptor.c
Abstract:
The module manages descriptor allocation.
Descriptors are structures in physical memory used by the
host controller to manage transfers, the HC requires two
types of TDs: Queue head TDs and Transfer TDs.
A memory descriptor describes a buffer that can be accessed by the
Host Controller (ie allocated with HalAllocateCommonBuffer).
The descriptor code maintains three types of memory descriptor
lists UHCD_LARGE_COMMON_BUFFERS, UHCD_MEDIUM_COMMON_BUFFERS and
UHCD_SMALL_COMMON_BUFFERS
UHCD_LARGE_COMMON_BUFFERS are blocks of memory we use for a queue
head and its associated transfer descriptors for iso and large bulk
endpoints.
UHCD_MEDIUM_COMMON_BUFFERS are blocks of memory we use for a queue
head and its associated transfer descriptors.
UHCD_SMALL_COMMON_BUFFERS are blocks of memory we use to double
buffer non-isochronous packets if needed.
Environment:
kernel mode only
Notes:
Revision History:
11-01-95 : created
--*/
#include "wdm.h"
#include "stdarg.h"
#include "stdio.h"
#include "usbdi.h"
#include "hcdi.h"
#include "uhcd.h"
#ifdef PAGE_CODE
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, UHCD_InitializeCommonBufferPool)
#endif
#endif
LONG UHCD_CommonBufferBytes = 0;
#if DBG
#define UHCD_ASSERT_BUFFER_POOL(bp) \
(((PUHCD_BUFFER_POOL) (bp))->Sig == UHCD_BP_SIG)
#else
#define UHCD_ASSERT_BUFFER_POOL(bp)
#endif
#define UHCD_BP_SIG 0x444a444a
PUHCD_BUFFER_POOL
UHCD_SelectBufferPool(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG CommonBufferLength
)
/*++
Routine Description:
select the appropriate buffer pool based on the
size of the CommonBufferBlock requested.
Arguments:
DeviceObject -
Return Value:
None
--*/
{
PUHCD_BUFFER_POOL bufferPool;
PDEVICE_EXTENSION deviceExtension;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
UHCD_ASSERT(CommonBufferLength <= UHCD_LARGE_COMMON_BUFFER_SIZE);
if (CommonBufferLength <= UHCD_SMALL_COMMON_BUFFER_SIZE) {
bufferPool = &deviceExtension->SmallBufferPool;
} else if (CommonBufferLength <= UHCD_MEDIUM_COMMON_BUFFER_SIZE) {
bufferPool = &deviceExtension->MediumBufferPool;
} else if (CommonBufferLength <= UHCD_LARGE_COMMON_BUFFER_SIZE) {
bufferPool = &deviceExtension->LargeBufferPool;
} else {
bufferPool = NULL;
}
LOGENTRY(LOG_MISC, 'sbPL', bufferPool, CommonBufferLength, 0);
#if DBG
if (bufferPool) {
UHCD_ASSERT_BUFFER_POOL(bufferPool);
}
#endif
return bufferPool;
}
ULONG
UHCD_FreePoolSize(
IN PUHCD_BUFFER_POOL BufferPool,
IN OUT PULONG ByteCount
)
/*++
Routine Description:
returns the number of entries in the free pool
Arguments:
Return Value:
None.
--*/
{
ULONG count = 0;
PSINGLE_LIST_ENTRY current;
PSINGLE_LIST_ENTRY listEntry;
PUHCD_MEMORY_DESCRIPTOR memoryDescriptor;
KIRQL oldIrql;
*ByteCount = 0;
KeAcquireSpinLock(&BufferPool->MemoryDescriptorFreePoolSpin, &oldIrql);
listEntry = &BufferPool->MemoryDescriptorFreePool;
// walk the list
current = listEntry->Next;
LOGENTRY(LOG_MISC, 'fpsz', listEntry, 0, current);
while (current != NULL) {
memoryDescriptor = CONTAINING_RECORD(current,
UHCD_MEMORY_DESCRIPTOR,
SingleListEntry);
UHCD_ASSERT(memoryDescriptor->Sig == SIG_MD);
count++;
*ByteCount+=BufferPool->CommonBufferLength;
current = current->Next;
LOGENTRY(LOG_MISC, 'fpsN', count, 0, current);
}
KeReleaseSpinLock(&BufferPool->MemoryDescriptorFreePoolSpin, oldIrql);
return count;
}
VOID
UHCD_InitializeHardwareQueueHeadDescriptor(
IN PDEVICE_OBJECT DeviceObject,
IN PHW_QUEUE_HEAD QueueHead,
IN HW_DESCRIPTOR_PHYSICAL_ADDRESS LogicalAddress
)
/*++
Routine Description:
set up a newly created queue head
Arguments:
DeviceObject - device object for this controller.
QueueHead - ptr to queue head to initialize.
Return Value:
None.
--*/
{
RtlZeroMemory(QueueHead, sizeof(HW_QUEUE_HEAD));
UHCD_ASSERT((LogicalAddress & 0xf) == 0);
QueueHead->PhysicalAddress = SET_Q_BIT(LogicalAddress);
QueueHead->Sig = SIG_QH;
QueueHead->Flags = 0;
QueueHead->HW_HLink = QueueHead->PhysicalAddress;
SET_T_BIT(QueueHead->HW_HLink);
QueueHead->HW_VLink = LIST_END; // T-bit set, no descriptors
return;
}
VOID
UHCD_InitializeHardwareTransferDescriptor(
IN PDEVICE_OBJECT DeviceObject,
IN PHW_TRANSFER_DESCRIPTOR TransferDescriptor,
IN HW_DESCRIPTOR_PHYSICAL_ADDRESS LogicalAddress
)
/*++
Routine Description:
set up a newly created transfer descriptor.
Arguments:
DeviceObject - device object for this controller.
Transfer - ptr to queue head to initialize
Return Value:
None.
--*/
{
RtlZeroMemory(TransferDescriptor, sizeof(HW_TRANSFER_DESCRIPTOR));
UHCD_ASSERT((LogicalAddress & 0xf) == 0);
TransferDescriptor->PhysicalAddress = LogicalAddress;
TransferDescriptor->Sig = SIG_TD;
return;
}
BOOLEAN
UHCD_AllocateHardwareDescriptors(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_HARDWARE_DESCRIPTOR_LIST *HardwareDescriptorList,
IN ULONG NumberOfTransferDescriptors
)
/*++
Routine Description:
This function allocates a descriptor list of a specified size,
the descriptor list includes a queue head and array of
'NumberOfTransferDescriptors' transfer descriptors.
Arguments:
DeviceObject - device object for this controller.
HardwareDescriptorList - pointer to descriptor list structure,
filled in with information about descriptors allocated.
NumberOfTransferDescriptors - number of transfer descriptors to allocate.
Return Value:
returns TRUE if successful.
--*/
{
ULONG i;
BOOLEAN status = FALSE;
PDEVICE_EXTENSION deviceExtension;
PUHCD_HARDWARE_DESCRIPTOR_LIST hardwareDescriptorList;
PUHCD_MEMORY_DESCRIPTOR memoryDescriptor;
UHCD_KdPrint((2, "'enter UHCD_AllocateHardwareDescriptors\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
*HardwareDescriptorList = NULL;
UHCD_ASSERT(sizeof(HW_QUEUE_HEAD) == UHCD_HW_DESCRIPTOR_SIZE);
UHCD_ASSERT(sizeof(HW_TRANSFER_DESCRIPTOR) == UHCD_HW_DESCRIPTOR_SIZE);
UHCD_ASSERT(NumberOfTransferDescriptors == MAX_TDS_PER_ENDPOINT ||
NumberOfTransferDescriptors == MIN_TDS_PER_ENDPOINT);
// first allocate the descriptor list structure
hardwareDescriptorList = GETHEAP(NonPagedPool,
sizeof(*hardwareDescriptorList));
LOGENTRY(LOG_MISC, 'alDL', hardwareDescriptorList, 0, 0);
if (hardwareDescriptorList) {
ULONG need;
// Allocate some shared adapter memory for this
// descriptor list.
//
// Note: we need one extra descriptor for the Queue Head
// and one extra for a scratch buffer
need = TD_LIST_SIZE(NumberOfTransferDescriptors);
hardwareDescriptorList->MemoryDescriptor =
UHCD_AllocateCommonBuffer(DeviceObject, need);
hardwareDescriptorList->NumberOfHWDescriptors =
NumberOfTransferDescriptors+1;
if (hardwareDescriptorList->MemoryDescriptor) {
memoryDescriptor = hardwareDescriptorList->MemoryDescriptor;
LOGENTRY(LOG_MISC, 'iHDL', hardwareDescriptorList,
NumberOfTransferDescriptors, 0);
UHCD_InitializeHardwareQueueHeadDescriptor(
DeviceObject,
(PHW_QUEUE_HEAD) memoryDescriptor->VirtualAddress,
memoryDescriptor->LogicalAddress);
for (i = 0; i < NumberOfTransferDescriptors; i++) {
LOGENTRY(LOG_MISC, 'iHTD', hardwareDescriptorList,
NumberOfTransferDescriptors, 0);
UHCD_InitializeHardwareTransferDescriptor(DeviceObject,
(PHW_TRANSFER_DESCRIPTOR)
(memoryDescriptor->VirtualAddress +
i * sizeof(HW_TRANSFER_DESCRIPTOR) +
sizeof(HW_QUEUE_HEAD)),
memoryDescriptor->LogicalAddress +
i * sizeof(HW_TRANSFER_DESCRIPTOR) +
sizeof(HW_QUEUE_HEAD));
}
hardwareDescriptorList->ScratchBufferVirtualAddress =
memoryDescriptor->VirtualAddress + i *
sizeof(HW_TRANSFER_DESCRIPTOR) +
sizeof(HW_QUEUE_HEAD);
UHCD_ASSERT((ULONG)((PUCHAR)hardwareDescriptorList->
ScratchBufferVirtualAddress -
(PUCHAR)memoryDescriptor->VirtualAddress) < need);
hardwareDescriptorList->ScratchBufferLogicalAddress =
memoryDescriptor->LogicalAddress + i *
sizeof(HW_TRANSFER_DESCRIPTOR) +
sizeof(HW_QUEUE_HEAD);
*HardwareDescriptorList = hardwareDescriptorList;
status = TRUE;
} else {
RETHEAP(hardwareDescriptorList);
}
}
UHCD_KdPrint((2, "'exit UHCD_AllocateHardwareDescriptors descriptors 0x%x\n",
status));
return status;
}
VOID
UHCD_FreeHardwareDescriptors(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_HARDWARE_DESCRIPTOR_LIST HardwareDescriptorList
)
/*++
Routine Description:
This function returns descriptors to the system.
Arguments:
DeviceObject - device object for this controller.
HardwareDescriptorList - pointer to descriptor list structure,
filled in with information about descriptors allocated.
Return Value:
None
--*/
{
PDEVICE_EXTENSION deviceExtension;
UHCD_KdPrint((2, "'enter UHCD_FreeHardwareDesriptors\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
//
// Put this one back on the free list
//
LOGENTRY(LOG_MISC, 'frDL', HardwareDescriptorList,
HardwareDescriptorList->MemoryDescriptor, 0);
//
// free the shared memory
//
UHCD_FreeCommonBuffer(DeviceObject,
HardwareDescriptorList->MemoryDescriptor);
// free the list header structure
RETHEAP(HardwareDescriptorList);
UHCD_KdPrint((2, "'exit UHCD_FreeHardwareDescriptors\n"));
}
PUHCD_MEMORY_DESCRIPTOR
UHCD_DoAllocateCommonBuffer(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG CommonBufferLength
)
/*++
Routine Description:
This function creates a new block of memory that both the HCD and
the HC harware can access, this memory can be used for HW descriptors
or packet buffers.
Arguments:
DeviceObject -
CommonBufferLength - minimum number of bytes this shared memory block
must contain.
Return Value:
None
--*/
{
PDEVICE_EXTENSION deviceExtension;
PUHCD_MEMORY_DESCRIPTOR memoryDescriptor = NULL;
PSINGLE_LIST_ENTRY singleListEntry;
PSINGLE_LIST_ENTRY memoryDescriptorFreePool;
PKSPIN_LOCK memoryDescriptorFreePoolSpin;
PUHCD_BUFFER_POOL bufferPool;
UHCD_KdPrint((2, "'enter UHCD_DoAllocateCommonBuffer\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
//
// select the list to allocate from
//
bufferPool = UHCD_SelectBufferPool(DeviceObject, CommonBufferLength);
memoryDescriptorFreePool = &bufferPool->MemoryDescriptorFreePool;
memoryDescriptorFreePoolSpin =
&bufferPool->MemoryDescriptorFreePoolSpin;
singleListEntry = ExInterlockedPopEntryList(memoryDescriptorFreePool,
memoryDescriptorFreePoolSpin);
if (singleListEntry) {
memoryDescriptor = CONTAINING_RECORD(singleListEntry,
UHCD_MEMORY_DESCRIPTOR,
SingleListEntry);
LOGENTRY(LOG_MISC, 'alMD', memoryDescriptor,
memoryDescriptor->VirtualAddress, 0);
ASSERT_MD(memoryDescriptor);
memoryDescriptor->InUse++;
}
UHCD_KdPrint((2, "'exit UHCD_DoAllocateCommonBuffer 0x%x\n",
memoryDescriptor));
return memoryDescriptor;
}
PUHCD_MEMORY_DESCRIPTOR
UHCD_AllocateCommonBuffer(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG CommonBufferLength
)
/*++
Routine Description:
This function creates a new block of memory that both the HCD and
the HC harware can access, this memory can be used for HW descriptors
or packet buffers.
Arguments:
DeviceObject -
CommonBufferLength - minimum number of bytes this shared memory block
must contain.
Return Value:
None
--*/
{
PUHCD_MEMORY_DESCRIPTOR memoryDescriptor = NULL;
// first attempt to satisfy the request whit what is available
UHCD_KdPrint((2, "'enter UHCD_AllocateCommonBuffer\n"));
LOGENTRY(LOG_MISC, 'acbr', 0, CommonBufferLength, 0);
// try to grow the pool
UHCD_MoreCommonBuffers(DeviceObject);
memoryDescriptor =
UHCD_DoAllocateCommonBuffer(DeviceObject,
CommonBufferLength);
if (memoryDescriptor == NULL) {
// non available, attempt to grow the pool
UHCD_AllocateCommonBufferBlock(DeviceObject,
CommonBufferLength,
1);
memoryDescriptor =
UHCD_DoAllocateCommonBuffer(DeviceObject,
CommonBufferLength);
#if DBG
if (memoryDescriptor == NULL) {
UHCD_KdTrap(("UHCD failed to allocate common buffer\n"));
}
#endif
}
//
// if we are at passive level then grow the pool now
//
if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
UHCD_MoreCommonBuffers(DeviceObject);
}
UHCD_KdPrint((2, "'exit UHCD_AllocateCommonBuffer 0x%x\n", memoryDescriptor));
return memoryDescriptor;
}
VOID
UHCD_FreeCommonBuffer(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_MEMORY_DESCRIPTOR MemoryDescriptor
)
/*++
Routine Description:
Arguments:
DeviceObject -
MemoryDescriptor - memory descriptor to free
Return Value:
None
--*/
{
PDEVICE_EXTENSION deviceExtension;
PSINGLE_LIST_ENTRY memoryDescriptorFreePool;
PKSPIN_LOCK memoryDescriptorFreePoolSpin;
PUHCD_BUFFER_POOL bufferPool;
UHCD_KdPrint((2, "'enter UHCD_FreeCommonBuffer\n"));
LOGENTRY(LOG_MISC, 'frMD', MemoryDescriptor,
MemoryDescriptor->VirtualAddress, MemoryDescriptor->Length);
ASSERT_MD(MemoryDescriptor);
bufferPool = MemoryDescriptor->BufferPool;
#if DBG
// RtlZeroMemory(MemoryDescriptor->VirtualAddress, MemoryDescriptor->Length);
RtlFillMemory(MemoryDescriptor->VirtualAddress,
MemoryDescriptor->Length,
-1);
#endif
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
memoryDescriptorFreePool = &bufferPool->MemoryDescriptorFreePool;
memoryDescriptorFreePoolSpin =
&bufferPool->MemoryDescriptorFreePoolSpin;
ExInterlockedPushEntryList(memoryDescriptorFreePool,
&MemoryDescriptor->SingleListEntry,
memoryDescriptorFreePoolSpin);
MemoryDescriptor->InUse--;
UHCD_KdPrint((2, "'exit UHCD_FreeCommonBuffer\n"));
}
VOID
UHCD_AllocateCommonBufferBlock(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG CommonBufferLength,
IN ULONG NumberOfPages
)
/*++
Routine Description:
This function tries to increase the pool of common memory
blocks used for descriptors and packet buffers.
Each Memory Descriptor looks like this:
offset
memoryDescriptor =
00 [Header] 32 bytes, (UHCD_HW_DESCRIPTOR_SIZE)
memoryDescriptor->VirtualAddress =
32 [memory] CommonBufferLength
......
all memoryDescriptors are aligned on a 32 byte
boundry.
Arguments:
DeviceObject -
CommonBufferLength - Number of contiguous bytes needed
in the in each common buffer.
NumberOfPages - number of pages to allocate for common buffers.
Return Value:
None
--*/
{
PUCHAR virtualAddress, baseVirtualAddress, endVirtualAddress;
PDEVICE_EXTENSION deviceExtension;
PUHCD_MEMORY_DESCRIPTOR memoryDescriptor = NULL;
PSINGLE_LIST_ENTRY memoryDescriptorFreePool;
HW_DESCRIPTOR_PHYSICAL_ADDRESS hwLogicalAddress;
PHYSICAL_ADDRESS logicalAddress;
ULONG blockSize, remain;
PUHCD_BUFFER_POOL bufferPool;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
//
// first figure our what size buffer we are dealing with
//
bufferPool = UHCD_SelectBufferPool(DeviceObject, CommonBufferLength);
LOGENTRY(LOG_MISC, 'ACbb', bufferPool, NumberOfPages, CommonBufferLength);
if (bufferPool == NULL) {
//
// Generally this means that CommonBufferLength is an illegal size
// so we punt without growing the pools
//
return;
}
//
// select the descriptor list based on the size of the request
//
memoryDescriptorFreePool = &bufferPool->MemoryDescriptorFreePool;
//
// set CommonBufferLength based on the descriptor list we selected
//
CommonBufferLength = bufferPool->CommonBufferLength;
#if DBG
{
//
// Dump out the size of the free pool for this buffer type
//
ULONG freeBytes, freeCommonBuffers;
freeCommonBuffers = UHCD_FreePoolSize(bufferPool, &freeBytes);
LOGENTRY(LOG_MISC, 'frBB', freeCommonBuffers, freeBytes, bufferPool);
UHCD_KdPrint(
(2, "'before alloc pool size = %d # buffs = %d total = %d bytes\n",
bufferPool->CommonBufferLength, freeCommonBuffers, freeBytes));
}
#endif
//
// Grow the pool..
// BUGBUG
// we always grow by one page
//
LOGENTRY(LOG_MISC, 'grow', NumberOfPages, bufferPool, CommonBufferLength);
//
// calc the number of bytes
//
blockSize = NumberOfPages*PAGE_SIZE;
//
// allocate some shared memory
//
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
#ifdef MAX_DEBUG
if (KeGetCurrentIrql() > APC_LEVEL) {
UHCD_KdPrint((2, "UHCD calling HalAllocateCommonBuffer at DPC level\n"));
TEST_TRAP();
}
#endif
virtualAddress =
baseVirtualAddress =
HalAllocateCommonBuffer(deviceExtension->AdapterObject,
blockSize,
&logicalAddress,
TRUE);
#if DBG
UHCD_CommonBufferBytes += blockSize;
#endif
// remember the base Virtual Address so we can free it later
// we need to keep track of the common buffers we allocate
if (baseVirtualAddress) {
PUHCD_PAGE_LIST_ENTRY pageListEntry;
UHCD_KdPrint((2, "'page list size = %d\n", sizeof(UHCD_PAGE_LIST_ENTRY)));
//
// CIMEXCIMEX
//
UHCD_ASSERT((sizeof(UHCD_PAGE_LIST_ENTRY) % 32) == 0);
pageListEntry =
(PUHCD_PAGE_LIST_ENTRY) baseVirtualAddress;
pageListEntry->Length = blockSize;
pageListEntry->LogicalAddress = logicalAddress;
pageListEntry->Flags = 0;
baseVirtualAddress += sizeof(UHCD_PAGE_LIST_ENTRY);
virtualAddress = baseVirtualAddress;
blockSize -= sizeof(UHCD_PAGE_LIST_ENTRY);
logicalAddress.LowPart += sizeof(UHCD_PAGE_LIST_ENTRY);
// link it in
InsertTailList(&deviceExtension->PageList,
&pageListEntry->ListEntry);
}
//
// break the new block up in to memory descriptors
// and put them on the free list.
//
if (baseVirtualAddress) {
ULONG length;
remain = blockSize;
//
// first grow the current pool by as many buffers
// as possible
//
hwLogicalAddress = logicalAddress.LowPart;
endVirtualAddress = baseVirtualAddress + (blockSize - 1);
length = UHCD_GrowBufferPool(DeviceObject, bufferPool,
CommonBufferLength, virtualAddress,
endVirtualAddress, hwLogicalAddress);
virtualAddress+=length;
hwLogicalAddress+=length;
//
// use what is left over for smaller buffers
//
remain -= length;
if (remain > (UHCD_MEDIUM_COMMON_BUFFER_SIZE + UHCD_HW_DESCRIPTOR_SIZE)) {
UHCD_ASSERT(virtualAddress < endVirtualAddress);
length = UHCD_GrowBufferPool(DeviceObject,
&deviceExtension->SmallBufferPool,
UHCD_MEDIUM_COMMON_BUFFER_SIZE,
virtualAddress, endVirtualAddress,
hwLogicalAddress);
virtualAddress+=length;
hwLogicalAddress+=length;
remain -= length;
}
if (remain >
(UHCD_SMALL_COMMON_BUFFER_SIZE + UHCD_HW_DESCRIPTOR_SIZE)) {
UHCD_ASSERT(virtualAddress < endVirtualAddress);
length = UHCD_GrowBufferPool(DeviceObject,
&deviceExtension->SmallBufferPool,
UHCD_SMALL_COMMON_BUFFER_SIZE,
virtualAddress, endVirtualAddress,
hwLogicalAddress);
remain -= length;
}
LOGENTRY(LOG_MISC, 'WSTE', remain, 0, 0);
} /* Grow the Pool */
#if DBG
{
//
// Dump out the size of the free pool for this buffer type
//
ULONG freeBytes, freeCommonBuffers;
freeCommonBuffers = UHCD_FreePoolSize(bufferPool, &freeBytes);
LOGENTRY(LOG_MISC, 'frB2', freeCommonBuffers, freeBytes, bufferPool);
UHCD_KdPrint(
(2, "'after alloc pool size = %d # buffs = %d total = %d bytes\n",
bufferPool->CommonBufferLength, freeCommonBuffers, freeBytes));
}
#endif
return;
}
VOID
UHCD_FreeCommonBufferBlocks(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Attempt to release unused shared memory to the system.
Arguments:
DeviceObject -
Return Value:
None
--*/
{
// not implemented
UHCD_KdTrap(("FreeCommonBufferBlocks not supported yet\n"));
}
ULONG
UHCD_CheckCommonBufferPool(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_BUFFER_POOL BufferPool,
IN BOOLEAN Allocate
)
/*++
Routine Description:
Attempt to grow of ree common buffers relative to the maximum
free pool size.
Arguments:
DeviceObject -
Return Value:
Returns number of pages needed or grown
None
--*/
{
ULONG freeBytes, freeCommonBuffers;
ULONG pagesNeeded = 0;
//
// make sure our reserve of common buffers is
// kept up
//
//
// Dump out the size of the free pool for this buffer type
//
UHCD_ASSERT_BUFFER_POOL(BufferPool);
freeCommonBuffers = UHCD_FreePoolSize(BufferPool, &freeBytes);
UHCD_KdPrint((2, "'pool size = %d # buffs = %d total = %d bytes\n",
BufferPool->CommonBufferLength, freeCommonBuffers, freeBytes));
if (freeCommonBuffers < BufferPool->MaximumFreeBuffers) {
// we are below the maximum buffers we like to
// keep available, grow the pool to this size
//
// allocate more common buffers, request the
// number of pages needed to satisfy the request
//
pagesNeeded = ((BufferPool->MaximumFreeBuffers - freeCommonBuffers)*
BufferPool->CommonBufferLength) / PAGE_SIZE;
pagesNeeded++;
if (Allocate) {
UHCD_AllocateCommonBufferBlock(DeviceObject,
BufferPool->CommonBufferLength,
pagesNeeded);
}
}
return pagesNeeded;
}
VOID
UHCD_MoreCommonBuffers(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Attempt to release unused shared memory to the system.
Arguments:
DeviceObject -
Return Value:
None
--*/
{
PDEVICE_EXTENSION deviceExtension;
PUHCD_WORKITEM workItem;
KIRQL irql;
ULONG need;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
LOGENTRY(LOG_MISC, 'mCMB', 0, KeGetCurrentIrql(), 0);
if (KeGetCurrentIrql() > APC_LEVEL) {
LOGENTRY(LOG_MISC, 'mCPS', 0, 0, 0);
need = UHCD_CheckCommonBufferPool(DeviceObject,
&deviceExtension->LargeBufferPool,
FALSE)+
UHCD_CheckCommonBufferPool(DeviceObject,
&deviceExtension->MediumBufferPool,
FALSE)+
UHCD_CheckCommonBufferPool(DeviceObject,
&deviceExtension->SmallBufferPool,
FALSE);
KeAcquireSpinLock(&deviceExtension->HcFlagSpin, &irql);
if (deviceExtension->HcFlags & HCFLAG_WORK_ITEM_QUEUED || need == 0) {
KeReleaseSpinLock(&deviceExtension->HcFlagSpin, irql);
return;
} else {
deviceExtension->HcFlags |= HCFLAG_WORK_ITEM_QUEUED;
KeReleaseSpinLock(&deviceExtension->HcFlagSpin, irql);
workItem = GETHEAP(NonPagedPool, sizeof(UHCD_WORKITEM));
if (workItem) {
workItem->DeviceObject = DeviceObject;
ExInitializeWorkItem(&workItem->WorkQueueItem,
UHCD_GrowPoolWorker,
workItem);
ExQueueWorkItem(&workItem->WorkQueueItem,
DelayedWorkQueue);
} else {
KeAcquireSpinLock(&deviceExtension->HcFlagSpin, &irql);
deviceExtension->HcFlags &= ~HCFLAG_WORK_ITEM_QUEUED;
KeReleaseSpinLock(&deviceExtension->HcFlagSpin, irql);
}
}
} else {
LOGENTRY(LOG_MISC, 'mCnS', 0, 0, 0);
//
// make sure our reserve of common buffers is
// kept up
//
UHCD_CheckCommonBufferPool(DeviceObject,
&deviceExtension->LargeBufferPool,
TRUE);
UHCD_CheckCommonBufferPool(DeviceObject,
&deviceExtension->MediumBufferPool,
TRUE);
UHCD_CheckCommonBufferPool(DeviceObject,
&deviceExtension->SmallBufferPool,
TRUE);
}
}
VOID
UHCD_InitializeCommonBufferPool(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PUHCD_BUFFER_POOL BufferPool,
IN ULONG CommonBufferLength,
IN ULONG MaximumFreeBuffers
)
/*++
Routine Description:
Arguments:
DeviceObject -
Return Value:
None
--*/
{
PDEVICE_EXTENSION deviceExtension;
PAGED_CODE();
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
// BUGBUG Note:
// for now we only support one size pool
//
KeInitializeSpinLock(&BufferPool->MemoryDescriptorFreePoolSpin);
BufferPool->MemoryDescriptorFreePool.Next = NULL;
BufferPool->CommonBufferLength = CommonBufferLength;
BufferPool->MaximumFreeBuffers = MaximumFreeBuffers;
BufferPool->Sig = UHCD_BP_SIG;
//
// This is where we intially grow the pool to
// some default size.
//
}
#if 0
VOID
UHCD_BufferPoolCheck(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Called by open_endpoint start_io.
This routine sanity checks our buffer pools.
Arguments:
DeviceObject -
Return Value:
None
--*/
{
PUHCD_BUFFER_POOL smallBufferPool;
PUHCD_BUFFER_POOL mediumBufferPool;
PUHCD_BUFFER_POOL LargeBufferPool;
PDEVICE_EXTENSION deviceExtension;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
// UHCD_ASSERT(smallBufferPool->ReservedBuffers == 0);
// UHCD_ASSERT(largeBufferPool->ReservedBuffers == 0);
}
#endif
ULONG
UHCD_GrowBufferPool(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_BUFFER_POOL BufferPool,
IN ULONG Length,
IN PUCHAR VirtualAddress,
IN PUCHAR EndVirtualAddress,
IN HW_DESCRIPTOR_PHYSICAL_ADDRESS HwLogicalAddress
)
/*++
Routine Description:
Called to grow a specific buffer pool
Arguments:
DeviceObject -
BufferPool - buffer pool to add these descriptors to.
Length - length of buffers to allocate
Return Value:
None
--*/
{
PUHCD_MEMORY_DESCRIPTOR memoryDescriptor = NULL;
ULONG consumed = 0;
PAGED_CODE();
//
// buffers must be 32 byte aligned for the hardware
//
UHCD_ASSERT((Length/32)*32 == Length);
// we have a buffer, break it up in to memory descriptors
// and add them to the free list
do {
//
// CIMEXCIMEX
//
UHCD_ASSERT(sizeof(*memoryDescriptor) == UHCD_HW_DESCRIPTOR_SIZE);
memoryDescriptor = (PUHCD_MEMORY_DESCRIPTOR) VirtualAddress;
memoryDescriptor->VirtualAddress =
VirtualAddress+UHCD_HW_DESCRIPTOR_SIZE;
memoryDescriptor->LogicalAddress =
HwLogicalAddress+UHCD_HW_DESCRIPTOR_SIZE;
memoryDescriptor->Sig = SIG_MD;
memoryDescriptor->InUse++;
LOGENTRY(LOG_MISC, 'grad', VirtualAddress , memoryDescriptor, Length);
memoryDescriptor->Length = Length;
consumed+=memoryDescriptor->Length+UHCD_HW_DESCRIPTOR_SIZE;
HwLogicalAddress += memoryDescriptor->Length+UHCD_HW_DESCRIPTOR_SIZE;
VirtualAddress += memoryDescriptor->Length+UHCD_HW_DESCRIPTOR_SIZE;
// put it on the free list by
// calling the free routine
memoryDescriptor->BufferPool = BufferPool;
UHCD_FreeCommonBuffer(DeviceObject, memoryDescriptor);
LOGENTRY(LOG_MISC, 'grbf', VirtualAddress , Length, EndVirtualAddress);
} while (VirtualAddress + Length +
UHCD_HW_DESCRIPTOR_SIZE <= EndVirtualAddress);
LOGENTRY(LOG_MISC, 'grpl', consumed , BufferPool, 0);
return consumed;
}
VOID
UHCD_GrowPoolWorker(
IN PVOID Context
)
/* ++
*
* Description:
*
* Arguments:
*
* Return:
*
* NTSTATUS
*
* -- */
{
PUHCD_WORKITEM workItem = Context;
PDEVICE_EXTENSION deviceExtension;
KIRQL irql;
LOGENTRY(LOG_MISC, 'gpwk', Context , 0, 0);
deviceExtension = workItem->DeviceObject->DeviceExtension;
UHCD_MoreCommonBuffers(workItem->DeviceObject);
KeAcquireSpinLock(&deviceExtension->HcFlagSpin, &irql);
deviceExtension->HcFlags &= ~HCFLAG_WORK_ITEM_QUEUED;
KeReleaseSpinLock(&deviceExtension->HcFlagSpin, irql);
RETHEAP(workItem);
}
#define UHCD_NO_DMA_BUFFER_USED 0x00000001
#define UHCD_NO_DMA_BUFFER_FREE 0x00000002
VOID
UHCD_Free_NoDMA_Buffer(
IN PDEVICE_OBJECT DeviceObject,
IN PUCHAR NoDMABuffer
)
/* ++
*
* Description:
*
* This function adds a No DMA buffer to our page list
* so that i will be freed when the HCD driver unloads
*
* Arguments:
*
* Return:
*
* NTSTATUS
*
* -- */
{
PUHCD_PAGE_LIST_ENTRY pageListEntry;
PDEVICE_EXTENSION deviceExtension;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
NoDMABuffer -= sizeof(UHCD_PAGE_LIST_ENTRY);
pageListEntry =
(PUHCD_PAGE_LIST_ENTRY) NoDMABuffer;
// just mark it free
pageListEntry->Flags = UHCD_NO_DMA_BUFFER_FREE;
}
PUCHAR
UHCD_Alloc_NoDMA_Buffer(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_ENDPOINT Endpoint,
IN ULONG Length
)
/* ++
*
* Description:
*
* This function adds a No DMA buffer to our page list
* so that i will be freed when the HCD driver unloads
*
* Arguments:
*
* Return:
*
* NTSTATUS
*
* -- */
{
PUHCD_PAGE_LIST_ENTRY pageListEntry;
PUCHAR noDMABuffer;
PHYSICAL_ADDRESS logicalAddress;
PUCHAR baseVirtualAddress = NULL;
PDEVICE_EXTENSION deviceExtension;
PLIST_ENTRY listEntry;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
UHCD_KdPrint((2, "'page list size = %d\n", sizeof(UHCD_PAGE_LIST_ENTRY)));
UHCD_ASSERT(sizeof(UHCD_PAGE_LIST_ENTRY) == 32);
noDMABuffer = NULL;
Length+=sizeof(UHCD_PAGE_LIST_ENTRY);
// first see if we have a buffer we can use
// walk the list, see if there is a free double_buffer entry
listEntry = &deviceExtension->PageList;
if (!IsListEmpty(listEntry)) {
listEntry = deviceExtension->PageList.Flink;
}
while (listEntry != &deviceExtension->PageList) {
pageListEntry = (PUHCD_PAGE_LIST_ENTRY) CONTAINING_RECORD(
listEntry,
struct _UHCD_PAGE_LIST_ENTRY,
ListEntry);
LOGENTRY(LOG_MISC, 'ple>', 0 , pageListEntry->Length, pageListEntry);
if (pageListEntry->Flags == UHCD_NO_DMA_BUFFER_FREE &&
pageListEntry->Length >= Length) {
UHCD_KdPrint((2, "'re-use page list\n"));
baseVirtualAddress = (PUCHAR) pageListEntry;
break;
}
listEntry = pageListEntry->ListEntry.Flink;
}
if (baseVirtualAddress) {
// mark it used
pageListEntry->Flags = UHCD_NO_DMA_BUFFER_USED;
logicalAddress = pageListEntry->LogicalAddress;
Length = pageListEntry->Length;
LOGENTRY(LOG_MISC, 'rUse', baseVirtualAddress , 0, pageListEntry);
UHCD_ASSERT(baseVirtualAddress == (PUCHAR) pageListEntry);
}
if (baseVirtualAddress == NULL) {
// no buffer -- allocate one
baseVirtualAddress =
HalAllocateCommonBuffer(deviceExtension->AdapterObject,
Length,
&logicalAddress,
TRUE);
if (baseVirtualAddress != NULL) {
pageListEntry =
(PUHCD_PAGE_LIST_ENTRY) baseVirtualAddress;
pageListEntry->Length = Length;
pageListEntry->LogicalAddress = logicalAddress;
pageListEntry->Flags = UHCD_NO_DMA_BUFFER_USED;
// link it in
InsertTailList(&deviceExtension->PageList,
&pageListEntry->ListEntry);
LOGENTRY(LOG_MISC, 'Nple', 0 , 0, pageListEntry);
}
}
if (baseVirtualAddress) {
pageListEntry->Flags = UHCD_NO_DMA_BUFFER_USED;
noDMABuffer = baseVirtualAddress + sizeof(UHCD_PAGE_LIST_ENTRY);
Length -= sizeof(UHCD_PAGE_LIST_ENTRY);
logicalAddress.LowPart += sizeof(UHCD_PAGE_LIST_ENTRY);
Endpoint->NoDMABufferLength = Length;
// Endpoint->NoDMALogicalAddress = logicalAddress;
Endpoint->NoDMAPhysicalAddress = logicalAddress.LowPart;
}
return noDMABuffer;
}