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