2 * PROJECT: ReactOS Universal Serial Bus Bulk Driver Library
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: lib/drivers/libusb/memory_manager.cpp
5 * PURPOSE: USB Common Driver Library.
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
16 class CDMAMemoryManager
: public IDMAMemoryManager
19 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
21 STDMETHODIMP_(ULONG
) AddRef()
23 InterlockedIncrement(&m_Ref
);
26 STDMETHODIMP_(ULONG
) Release()
28 InterlockedDecrement(&m_Ref
);
38 // IDMAMemoryManager interface functions
39 virtual NTSTATUS
Initialize(IN PUSBHARDWAREDEVICE Device
, IN PKSPIN_LOCK Lock
, IN ULONG DmaBufferSize
, IN PVOID VirtualBase
, IN PHYSICAL_ADDRESS PhysicalAddress
, IN ULONG DefaultBlockSize
);
40 virtual NTSTATUS
Allocate(IN ULONG Size
, OUT PVOID
*OutVirtualBase
, OUT PPHYSICAL_ADDRESS OutPhysicalAddress
);
41 virtual NTSTATUS
Release(IN PVOID VirtualBase
, IN ULONG Size
);
43 // constructor / destructor
44 CDMAMemoryManager(IUnknown
*OuterUnknown
){}
45 virtual ~CDMAMemoryManager(){}
49 PUSBHARDWAREDEVICE m_Device
;
53 PHYSICAL_ADDRESS m_PhysicalAddress
;
56 PULONG m_BitmapBuffer
;
60 //----------------------------------------------------------------------------------------
63 CDMAMemoryManager::QueryInterface(
67 return STATUS_UNSUCCESSFUL
;
71 CDMAMemoryManager::Initialize(
72 IN PUSBHARDWAREDEVICE Device
,
74 IN ULONG DmaBufferSize
,
76 IN PHYSICAL_ADDRESS PhysicalAddress
,
77 IN ULONG DefaultBlockSize
)
84 PC_ASSERT(DmaBufferSize
>= PAGE_SIZE
);
85 PC_ASSERT(DmaBufferSize
% PAGE_SIZE
== 0);
86 PC_ASSERT(DefaultBlockSize
== 32 || DefaultBlockSize
== 64 || DefaultBlockSize
== 128);
89 // calculate bitmap length
91 BitmapLength
= (DmaBufferSize
/ DefaultBlockSize
) / 8;
94 // allocate bitmap buffer
96 m_BitmapBuffer
= (PULONG
)ExAllocatePoolWithTag(NonPagedPool
, BitmapLength
, TAG_USBLIB
);
102 return STATUS_INSUFFICIENT_RESOURCES
;
108 RtlInitializeBitMap(&m_Bitmap
, m_BitmapBuffer
, BitmapLength
* 8);
113 RtlClearAllBits(&m_Bitmap
);
116 // initialize rest of memory allocator
118 m_PhysicalAddress
= PhysicalAddress
;
119 m_VirtualBase
= VirtualBase
;
120 m_DmaBufferSize
= DmaBufferSize
;
121 m_BitmapBuffer
= m_BitmapBuffer
;
123 m_BlockSize
= DefaultBlockSize
;
126 return STATUS_SUCCESS
;
130 CDMAMemoryManager::Allocate(
132 OUT PVOID
*OutVirtualAddress
,
133 OUT PPHYSICAL_ADDRESS OutPhysicalAddress
)
135 ULONG Length
, BlockCount
, FreeIndex
, StartPage
, EndPage
;
142 ASSERT(Size
<= PAGE_SIZE
);
143 //ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
148 Length
= (Size
+ m_BlockSize
-1) & ~(m_BlockSize
-1);
156 // convert to block count
158 BlockCount
= Length
/ m_BlockSize
;
163 KeAcquireSpinLock(m_Lock
, &OldLevel
);
168 BlocksPerPage
= PAGE_SIZE
/ m_BlockSize
;
177 // search for an free index
179 FreeIndex
= RtlFindClearBits(&m_Bitmap
, BlockCount
, FreeIndex
);
182 // check if there was a block found
184 if (FreeIndex
== MAXULONG
)
187 // no free block found
193 // check that the allocation does not spawn over page boundaries
195 StartPage
= (FreeIndex
* m_BlockSize
);
196 StartPage
= (StartPage
!= 0 ? StartPage
/ PAGE_SIZE
: 0);
197 EndPage
= ((FreeIndex
+ BlockCount
) * m_BlockSize
) / PAGE_SIZE
;
200 // does the request start and end on the same page
202 if (StartPage
== EndPage
)
207 RtlSetBits(&m_Bitmap
, FreeIndex
, BlockCount
);
214 else if ((BlockCount
== BlocksPerPage
) && (FreeIndex
% BlocksPerPage
== 0))
217 // the request equals PAGE_SIZE and is aligned at page boundary
220 RtlSetBits(&m_Bitmap
, FreeIndex
, BlockCount
);
230 // request spawned over page boundary
231 // restart search on next page
233 FreeIndex
= (EndPage
* PAGE_SIZE
) / m_BlockSize
;
241 KeReleaseSpinLock(m_Lock
, OldLevel
);
244 // did allocation succeed
246 if (FreeIndex
== MAXULONG
)
249 // failed to allocate block, requestor must retry
251 return STATUS_UNSUCCESSFUL
;
257 *OutVirtualAddress
= (PVOID
)((ULONG_PTR
)m_VirtualBase
+ FreeIndex
* m_BlockSize
);
258 OutPhysicalAddress
->QuadPart
= m_PhysicalAddress
.QuadPart
+ FreeIndex
* m_BlockSize
;
263 RtlZeroMemory(*OutVirtualAddress
, Length
);
268 return STATUS_SUCCESS
;
272 CDMAMemoryManager::Release(
273 IN PVOID VirtualAddress
,
277 ULONG BlockOffset
= 0, BlockLength
, BlockCount
;
282 PC_ASSERT(VirtualAddress
);
283 PC_ASSERT((ULONG_PTR
)VirtualAddress
>= (ULONG_PTR
)m_VirtualBase
);
284 PC_ASSERT((ULONG_PTR
)m_VirtualBase
+ m_DmaBufferSize
> (ULONG_PTR
)m_VirtualBase
);
287 // calculate block length
289 BlockLength
= ((ULONG_PTR
)VirtualAddress
- (ULONG_PTR
)m_VirtualBase
);
292 // check if its the first block
297 // divide by base block size
299 BlockOffset
= BlockLength
/ m_BlockSize
;
303 // align length to block size
305 Size
= (Size
+ m_BlockSize
- 1) & ~(m_BlockSize
- 1);
310 BlockCount
= Size
/ m_BlockSize
;
316 KeAcquireSpinLock(m_Lock
, &OldLevel
);
321 ASSERT(RtlAreBitsSet(&m_Bitmap
, BlockOffset
, BlockCount
));
326 RtlClearBits(&m_Bitmap
, BlockOffset
, BlockCount
);
331 KeReleaseSpinLock(m_Lock
, OldLevel
);
336 return STATUS_SUCCESS
;
341 CreateDMAMemoryManager(
342 PDMAMEMORYMANAGER
*OutMemoryManager
)
344 CDMAMemoryManager
* This
;
347 // allocate controller
349 This
= new(NonPagedPool
, TAG_USBLIB
) CDMAMemoryManager(0);
353 // failed to allocate
355 return STATUS_INSUFFICIENT_RESOURCES
;
359 // add reference count
366 *OutMemoryManager
= (PDMAMEMORYMANAGER
)This
;
371 return STATUS_SUCCESS
;