[USBEHCI]
[reactos.git] / drivers / usb / usbehci_new / 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 DPRINT(__FUNCTION__ " acquire\n");
161 KeAcquireSpinLock(m_Lock, &OldLevel);
162 DPRINT(__FUNCTION__ " acquired\n");
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 DPRINT(__FUNCTION__ "release\n");
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;
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 // acquire lock
309 //
310 DPRINT(__FUNCTION__ " acquire\n");
311 KeAcquireSpinLock(m_Lock, &OldLevel);
312 DPRINT(__FUNCTION__ " acquired\n");
313
314 //
315 // release buffer
316 //
317 RtlClearBits(&m_Bitmap, BlockOffset, Size);
318
319 //
320 // release lock
321 //
322 DPRINT(__FUNCTION__ "release\n");
323 KeReleaseSpinLock(m_Lock, OldLevel);
324
325 //
326 // done
327 //
328 return STATUS_SUCCESS;
329 }
330
331 NTSTATUS
332 CreateDMAMemoryManager(
333 PDMAMEMORYMANAGER *OutMemoryManager)
334 {
335 CDMAMemoryManager* This;
336
337 //
338 // allocate controller
339 //
340 This = new(NonPagedPool, TAG_USBEHCI) CDMAMemoryManager(0);
341 if (!This)
342 {
343 //
344 // failed to allocate
345 //
346 return STATUS_INSUFFICIENT_RESOURCES;
347 }
348
349 //
350 // add reference count
351 //
352 This->AddRef();
353
354 //
355 // return result
356 //
357 *OutMemoryManager = (PDMAMEMORYMANAGER)This;
358
359 //
360 // done
361 //
362 return STATUS_SUCCESS;
363 }
364