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