[PRINTING]
[reactos.git] / reactos / 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_BitmapBuffer = m_BitmapBuffer;
122 m_Lock = Lock;
123 m_BlockSize = DefaultBlockSize;
124
125 /* done */
126 return STATUS_SUCCESS;
127 }
128
129 NTSTATUS
130 CDMAMemoryManager::Allocate(
131 IN ULONG Size,
132 OUT PVOID *OutVirtualAddress,
133 OUT PPHYSICAL_ADDRESS OutPhysicalAddress)
134 {
135 ULONG Length, BlockCount, FreeIndex, StartPage, EndPage;
136 KIRQL OldLevel;
137 ULONG BlocksPerPage;
138
139 //
140 // sanity checks
141 //
142 ASSERT(Size <= PAGE_SIZE);
143 //ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
144
145 //
146 // align request
147 //
148 Length = (Size + m_BlockSize -1) & ~(m_BlockSize -1);
149
150 //
151 // sanity check
152 //
153 ASSERT(Length);
154
155 //
156 // convert to block count
157 //
158 BlockCount = Length / m_BlockSize;
159
160 //
161 // acquire lock
162 //
163 KeAcquireSpinLock(m_Lock, &OldLevel);
164
165 //
166 // helper variable
167 //
168 BlocksPerPage = PAGE_SIZE / m_BlockSize;
169
170 //
171 // start search
172 //
173 FreeIndex = 0;
174 do
175 {
176 //
177 // search for an free index
178 //
179 FreeIndex = RtlFindClearBits(&m_Bitmap, BlockCount, FreeIndex);
180
181 //
182 // check if there was a block found
183 //
184 if (FreeIndex == MAXULONG)
185 {
186 //
187 // no free block found
188 //
189 break;
190 }
191
192 //
193 // check that the allocation does not spawn over page boundaries
194 //
195 StartPage = (FreeIndex * m_BlockSize);
196 StartPage = (StartPage != 0 ? StartPage / PAGE_SIZE : 0);
197 EndPage = ((FreeIndex + BlockCount) * m_BlockSize) / PAGE_SIZE;
198
199 //
200 // does the request start and end on the same page
201 //
202 if (StartPage == EndPage)
203 {
204 //
205 // reserve block
206 //
207 RtlSetBits(&m_Bitmap, FreeIndex, BlockCount);
208
209 //
210 // reserve block
211 //
212 break;
213 }
214 else if ((BlockCount == BlocksPerPage) && (FreeIndex % BlocksPerPage == 0))
215 {
216 //
217 // the request equals PAGE_SIZE and is aligned at page boundary
218 // reserve block
219 //
220 RtlSetBits(&m_Bitmap, FreeIndex, BlockCount);
221
222 //
223 // reserve block
224 //
225 break;
226 }
227 else
228 {
229 //
230 // request spawned over page boundary
231 // restart search on next page
232 //
233 FreeIndex = (EndPage * PAGE_SIZE) / m_BlockSize;
234 }
235 }
236 while(TRUE);
237
238 //
239 // release lock
240 //
241 KeReleaseSpinLock(m_Lock, OldLevel);
242
243 //
244 // did allocation succeed
245 //
246 if (FreeIndex == MAXULONG)
247 {
248 //
249 // failed to allocate block, requestor must retry
250 //
251 return STATUS_UNSUCCESSFUL;
252 }
253
254 //
255 // return result
256 //
257 *OutVirtualAddress = (PVOID)((ULONG_PTR)m_VirtualBase + FreeIndex * m_BlockSize);
258 OutPhysicalAddress->QuadPart = m_PhysicalAddress.QuadPart + FreeIndex * m_BlockSize;
259
260 //
261 // clear block
262 //
263 RtlZeroMemory(*OutVirtualAddress, Length);
264
265 //
266 // done
267 //
268 return STATUS_SUCCESS;
269 }
270
271 NTSTATUS
272 CDMAMemoryManager::Release(
273 IN PVOID VirtualAddress,
274 IN ULONG Size)
275 {
276 KIRQL OldLevel;
277 ULONG BlockOffset = 0, BlockLength, BlockCount;
278
279 //
280 // sanity checks
281 //
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);
285
286 //
287 // calculate block length
288 //
289 BlockLength = ((ULONG_PTR)VirtualAddress - (ULONG_PTR)m_VirtualBase);
290
291 //
292 // check if its the first block
293 //
294 if (BlockLength)
295 {
296 //
297 // divide by base block size
298 //
299 BlockOffset = BlockLength / m_BlockSize;
300 }
301
302 //
303 // align length to block size
304 //
305 Size = (Size + m_BlockSize - 1) & ~(m_BlockSize - 1);
306
307 //
308 // convert to blocks
309 //
310 BlockCount = Size / m_BlockSize;
311 ASSERT(BlockCount);
312
313 //
314 // acquire lock
315 //
316 KeAcquireSpinLock(m_Lock, &OldLevel);
317
318 //
319 // sanity check
320 //
321 ASSERT(RtlAreBitsSet(&m_Bitmap, BlockOffset, BlockCount));
322
323 //
324 // release buffer
325 //
326 RtlClearBits(&m_Bitmap, BlockOffset, BlockCount);
327
328 //
329 // release lock
330 //
331 KeReleaseSpinLock(m_Lock, OldLevel);
332
333 //
334 // done
335 //
336 return STATUS_SUCCESS;
337 }
338
339 NTSTATUS
340 NTAPI
341 CreateDMAMemoryManager(
342 PDMAMEMORYMANAGER *OutMemoryManager)
343 {
344 CDMAMemoryManager* This;
345
346 //
347 // allocate controller
348 //
349 This = new(NonPagedPool, TAG_USBLIB) CDMAMemoryManager(0);
350 if (!This)
351 {
352 //
353 // failed to allocate
354 //
355 return STATUS_INSUFFICIENT_RESOURCES;
356 }
357
358 //
359 // add reference count
360 //
361 This->AddRef();
362
363 //
364 // return result
365 //
366 *OutMemoryManager = (PDMAMEMORYMANAGER)This;
367
368 //
369 // done
370 //
371 return STATUS_SUCCESS;
372 }
373