[DELAYIMP] Fix 2 Clang-Cl warnings about __pfnDliNotifyHook2Default and __pfnDliFailu...
[reactos.git] / sdk / lib / drivers / libusb / memory_manager.cpp
1 /*
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.
6 * PROGRAMMERS:
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
9 */
10
11 #include "libusb.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 class CDMAMemoryManager : public IDMAMemoryManager
17 {
18 public:
19 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
20
21 STDMETHODIMP_(ULONG) AddRef()
22 {
23 InterlockedIncrement(&m_Ref);
24 return m_Ref;
25 }
26 STDMETHODIMP_(ULONG) Release()
27 {
28 InterlockedDecrement(&m_Ref);
29
30 if (!m_Ref)
31 {
32 delete this;
33 return 0;
34 }
35 return m_Ref;
36 }
37
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);
42
43 // constructor / destructor
44 CDMAMemoryManager(IUnknown *OuterUnknown){}
45 virtual ~CDMAMemoryManager(){}
46
47 protected:
48 LONG m_Ref;
49 PUSBHARDWAREDEVICE m_Device;
50 PKSPIN_LOCK m_Lock;
51 LONG m_DmaBufferSize;
52 PVOID m_VirtualBase;
53 PHYSICAL_ADDRESS m_PhysicalAddress;
54 ULONG m_BlockSize;
55
56 PULONG m_BitmapBuffer;
57 RTL_BITMAP m_Bitmap;
58 };
59
60 //----------------------------------------------------------------------------------------
61 NTSTATUS
62 STDMETHODCALLTYPE
63 CDMAMemoryManager::QueryInterface(
64 IN REFIID refiid,
65 OUT PVOID* Output)
66 {
67 return STATUS_UNSUCCESSFUL;
68 }
69
70 NTSTATUS
71 CDMAMemoryManager::Initialize(
72 IN PUSBHARDWAREDEVICE Device,
73 IN PKSPIN_LOCK Lock,
74 IN ULONG DmaBufferSize,
75 IN PVOID VirtualBase,
76 IN PHYSICAL_ADDRESS PhysicalAddress,
77 IN ULONG DefaultBlockSize)
78 {
79 ULONG BitmapLength;
80
81 //
82 // sanity checks
83 //
84 PC_ASSERT(DmaBufferSize >= PAGE_SIZE);
85 PC_ASSERT(DmaBufferSize % PAGE_SIZE == 0);
86 PC_ASSERT(DefaultBlockSize == 32 || DefaultBlockSize == 64 || DefaultBlockSize == 128);
87
88 //
89 // calculate bitmap length
90 //
91 BitmapLength = (DmaBufferSize / DefaultBlockSize) / 8;
92
93 //
94 // allocate bitmap buffer
95 //
96 m_BitmapBuffer = (PULONG)ExAllocatePoolWithTag(NonPagedPool, BitmapLength, TAG_USBLIB);
97 if (!m_BitmapBuffer)
98 {
99 //
100 // no memory
101 //
102 return STATUS_INSUFFICIENT_RESOURCES;
103 }
104
105 //
106 // initialize bitmap
107 //
108 RtlInitializeBitMap(&m_Bitmap, m_BitmapBuffer, BitmapLength * 8);
109
110 //
111 // clear all bits
112 //
113 RtlClearAllBits(&m_Bitmap);
114
115 //
116 // initialize rest of memory allocator
117 //
118 m_PhysicalAddress = PhysicalAddress;
119 m_VirtualBase = VirtualBase;
120 m_DmaBufferSize = DmaBufferSize;
121 m_Lock = Lock;
122 m_BlockSize = DefaultBlockSize;
123
124 /* done */
125 return STATUS_SUCCESS;
126 }
127
128 NTSTATUS
129 CDMAMemoryManager::Allocate(
130 IN ULONG Size,
131 OUT PVOID *OutVirtualAddress,
132 OUT PPHYSICAL_ADDRESS OutPhysicalAddress)
133 {
134 ULONG Length, BlockCount, FreeIndex, StartPage, EndPage;
135 KIRQL OldLevel;
136 ULONG BlocksPerPage;
137
138 //
139 // sanity checks
140 //
141 ASSERT(Size <= PAGE_SIZE);
142 //ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
143
144 //
145 // align request
146 //
147 Length = (Size + m_BlockSize -1) & ~(m_BlockSize -1);
148
149 //
150 // sanity check
151 //
152 ASSERT(Length);
153
154 //
155 // convert to block count
156 //
157 BlockCount = Length / m_BlockSize;
158
159 //
160 // acquire lock
161 //
162 KeAcquireSpinLock(m_Lock, &OldLevel);
163
164 //
165 // helper variable
166 //
167 BlocksPerPage = PAGE_SIZE / m_BlockSize;
168
169 //
170 // start search
171 //
172 FreeIndex = 0;
173 do
174 {
175 //
176 // search for an free index
177 //
178 FreeIndex = RtlFindClearBits(&m_Bitmap, BlockCount, FreeIndex);
179
180 //
181 // check if there was a block found
182 //
183 if (FreeIndex == MAXULONG)
184 {
185 //
186 // no free block found
187 //
188 break;
189 }
190
191 //
192 // check that the allocation does not spawn over page boundaries
193 //
194 StartPage = (FreeIndex * m_BlockSize);
195 StartPage = (StartPage != 0 ? StartPage / PAGE_SIZE : 0);
196 EndPage = ((FreeIndex + BlockCount) * m_BlockSize) / PAGE_SIZE;
197
198 //
199 // does the request start and end on the same page
200 //
201 if (StartPage == EndPage)
202 {
203 //
204 // reserve block
205 //
206 RtlSetBits(&m_Bitmap, FreeIndex, BlockCount);
207
208 //
209 // reserve block
210 //
211 break;
212 }
213 else if ((BlockCount == BlocksPerPage) && (FreeIndex % BlocksPerPage == 0))
214 {
215 //
216 // the request equals PAGE_SIZE and is aligned at page boundary
217 // reserve block
218 //
219 RtlSetBits(&m_Bitmap, FreeIndex, BlockCount);
220
221 //
222 // reserve block
223 //
224 break;
225 }
226 else
227 {
228 //
229 // request spawned over page boundary
230 // restart search on next page
231 //
232 FreeIndex = (EndPage * PAGE_SIZE) / m_BlockSize;
233 }
234 }
235 while(TRUE);
236
237 //
238 // release lock
239 //
240 KeReleaseSpinLock(m_Lock, OldLevel);
241
242 //
243 // did allocation succeed
244 //
245 if (FreeIndex == MAXULONG)
246 {
247 //
248 // failed to allocate block, requestor must retry
249 //
250 return STATUS_UNSUCCESSFUL;
251 }
252
253 //
254 // return result
255 //
256 *OutVirtualAddress = (PVOID)((ULONG_PTR)m_VirtualBase + FreeIndex * m_BlockSize);
257 OutPhysicalAddress->QuadPart = m_PhysicalAddress.QuadPart + FreeIndex * m_BlockSize;
258
259 //
260 // clear block
261 //
262 RtlZeroMemory(*OutVirtualAddress, Length);
263
264 //
265 // done
266 //
267 return STATUS_SUCCESS;
268 }
269
270 NTSTATUS
271 CDMAMemoryManager::Release(
272 IN PVOID VirtualAddress,
273 IN ULONG Size)
274 {
275 KIRQL OldLevel;
276 ULONG BlockOffset = 0, BlockLength, BlockCount;
277
278 //
279 // sanity checks
280 //
281 PC_ASSERT(VirtualAddress);
282 PC_ASSERT((ULONG_PTR)VirtualAddress >= (ULONG_PTR)m_VirtualBase);
283 PC_ASSERT((ULONG_PTR)m_VirtualBase + m_DmaBufferSize > (ULONG_PTR)m_VirtualBase);
284
285 //
286 // calculate block length
287 //
288 BlockLength = ((ULONG_PTR)VirtualAddress - (ULONG_PTR)m_VirtualBase);
289
290 //
291 // check if its the first block
292 //
293 if (BlockLength)
294 {
295 //
296 // divide by base block size
297 //
298 BlockOffset = BlockLength / m_BlockSize;
299 }
300
301 //
302 // align length to block size
303 //
304 Size = (Size + m_BlockSize - 1) & ~(m_BlockSize - 1);
305
306 //
307 // convert to blocks
308 //
309 BlockCount = Size / m_BlockSize;
310 ASSERT(BlockCount);
311
312 //
313 // acquire lock
314 //
315 KeAcquireSpinLock(m_Lock, &OldLevel);
316
317 //
318 // sanity check
319 //
320 ASSERT(RtlAreBitsSet(&m_Bitmap, BlockOffset, BlockCount));
321
322 //
323 // release buffer
324 //
325 RtlClearBits(&m_Bitmap, BlockOffset, BlockCount);
326
327 //
328 // release lock
329 //
330 KeReleaseSpinLock(m_Lock, OldLevel);
331
332 //
333 // done
334 //
335 return STATUS_SUCCESS;
336 }
337
338 NTSTATUS
339 NTAPI
340 CreateDMAMemoryManager(
341 PDMAMEMORYMANAGER *OutMemoryManager)
342 {
343 CDMAMemoryManager* This;
344
345 //
346 // allocate controller
347 //
348 This = new(NonPagedPool, TAG_USBLIB) CDMAMemoryManager(0);
349 if (!This)
350 {
351 //
352 // failed to allocate
353 //
354 return STATUS_INSUFFICIENT_RESOURCES;
355 }
356
357 //
358 // add reference count
359 //
360 This->AddRef();
361
362 //
363 // return result
364 //
365 *OutMemoryManager = (PDMAMEMORYMANAGER)This;
366
367 //
368 // done
369 //
370 return STATUS_SUCCESS;
371 }
372