5880b61c03ecf0040967c97d2638b5f71e46c4b2
[reactos.git] / drivers / usb / usbohci / usb_queue.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/usbohci/usb_queue.cpp
5 * PURPOSE: USB OHCI device driver.
6 * PROGRAMMERS:
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
9 */
10
11 #include "usbohci.h"
12 #include "hardware.h"
13
14 class CUSBQueue : public IUSBQueue
15 {
16 public:
17 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
18
19 STDMETHODIMP_(ULONG) AddRef()
20 {
21 InterlockedIncrement(&m_Ref);
22 return m_Ref;
23 }
24 STDMETHODIMP_(ULONG) Release()
25 {
26 InterlockedDecrement(&m_Ref);
27
28 if (!m_Ref)
29 {
30 delete this;
31 return 0;
32 }
33 return m_Ref;
34 }
35
36 // com
37 virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Hardware, PDMA_ADAPTER AdapterObject, IN PDMAMEMORYMANAGER MemManager, IN OPTIONAL PKSPIN_LOCK Lock);
38 virtual ULONG GetPendingRequestCount();
39 virtual NTSTATUS AddUSBRequest(IUSBRequest * Request);
40 virtual NTSTATUS CancelRequests();
41 virtual NTSTATUS CreateUSBRequest(IUSBRequest **OutRequest);
42 virtual VOID TransferDescriptorCompletionCallback(ULONG TransferDescriptorLogicalAddress);
43
44 // local functions
45 BOOLEAN IsTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG TransferDescriptorLogicalAddress);
46 NTSTATUS FindTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor);
47 NTSTATUS FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor);
48
49 VOID CleanupEndpointDescriptor(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor);
50 POHCI_ENDPOINT_DESCRIPTOR FindInterruptEndpointDescriptor(UCHAR InterruptInterval);
51 VOID PrintEndpointList(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor);
52 VOID LinkEndpoint(POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor, POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor);
53
54 // constructor / destructor
55 CUSBQueue(IUnknown *OuterUnknown){}
56 virtual ~CUSBQueue(){}
57
58 protected:
59 LONG m_Ref; // reference count
60 KSPIN_LOCK m_Lock; // list lock
61 PUSBHARDWAREDEVICE m_Hardware; // hardware
62 POHCI_ENDPOINT_DESCRIPTOR m_BulkHeadEndpointDescriptor; // bulk head descriptor
63 POHCI_ENDPOINT_DESCRIPTOR m_ControlHeadEndpointDescriptor; // control head descriptor
64 POHCI_ENDPOINT_DESCRIPTOR * m_InterruptEndpoints;
65 };
66
67 //=================================================================================================
68 // COM
69 //
70 NTSTATUS
71 STDMETHODCALLTYPE
72 CUSBQueue::QueryInterface(
73 IN REFIID refiid,
74 OUT PVOID* Output)
75 {
76 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
77 {
78 *Output = PVOID(PUNKNOWN(this));
79 PUNKNOWN(*Output)->AddRef();
80 return STATUS_SUCCESS;
81 }
82
83 return STATUS_UNSUCCESSFUL;
84 }
85
86 NTSTATUS
87 CUSBQueue::Initialize(
88 IN PUSBHARDWAREDEVICE Hardware,
89 IN PDMA_ADAPTER AdapterObject,
90 IN PDMAMEMORYMANAGER MemManager,
91 IN OPTIONAL PKSPIN_LOCK Lock)
92 {
93 //
94 // get bulk endpoint descriptor
95 //
96 Hardware->GetBulkHeadEndpointDescriptor(&m_BulkHeadEndpointDescriptor);
97
98 //
99 // get control endpoint descriptor
100 //
101 Hardware->GetControlHeadEndpointDescriptor(&m_ControlHeadEndpointDescriptor);
102
103 //
104 // get interrupt endpoints
105 //
106 Hardware->GetInterruptEndpointDescriptors(&m_InterruptEndpoints);
107
108 //
109 // initialize spinlock
110 //
111 KeInitializeSpinLock(&m_Lock);
112
113 //
114 // store hardware
115 //
116 m_Hardware = Hardware;
117
118 return STATUS_SUCCESS;
119 }
120
121 ULONG
122 CUSBQueue::GetPendingRequestCount()
123 {
124 //
125 // Loop through the pending list and iterrate one for each QueueHead that
126 // has a IRP to complete.
127 //
128
129 return 0;
130 }
131
132 VOID
133 CUSBQueue::LinkEndpoint(
134 POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor,
135 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor)
136 {
137 POHCI_ENDPOINT_DESCRIPTOR CurrentEndpointDescriptor = HeadEndpointDescriptor;
138
139 //
140 // get last descriptor in queue
141 //
142 while(CurrentEndpointDescriptor->NextDescriptor)
143 {
144 //
145 // move to last descriptor
146 //
147 CurrentEndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)CurrentEndpointDescriptor->NextDescriptor;
148 }
149
150 //
151 // link endpoints
152 //
153 CurrentEndpointDescriptor->NextPhysicalEndpoint = EndpointDescriptor->PhysicalAddress.LowPart;
154 CurrentEndpointDescriptor->NextDescriptor = EndpointDescriptor;
155
156 }
157
158 NTSTATUS
159 CUSBQueue::AddUSBRequest(
160 IUSBRequest * Request)
161 {
162 NTSTATUS Status;
163 ULONG Type;
164 KIRQL OldLevel;
165 POHCI_ENDPOINT_DESCRIPTOR HeadDescriptor;
166 POHCI_ENDPOINT_DESCRIPTOR Descriptor;
167
168 DPRINT("CUSBQueue::AddUSBRequest\n");
169
170 //
171 // sanity check
172 //
173 ASSERT(Request != NULL);
174
175 //
176 // get request type
177 //
178 Type = Request->GetTransferType();
179
180 //
181 // check if supported
182 //
183 switch(Type)
184 {
185 case USB_ENDPOINT_TYPE_ISOCHRONOUS:
186 /* NOT IMPLEMENTED IN QUEUE */
187 Status = STATUS_NOT_SUPPORTED;
188 break;
189 case USB_ENDPOINT_TYPE_INTERRUPT:
190 case USB_ENDPOINT_TYPE_CONTROL:
191 case USB_ENDPOINT_TYPE_BULK:
192 Status = STATUS_SUCCESS;
193 break;
194 default:
195 /* BUG */
196 PC_ASSERT(FALSE);
197 Status = STATUS_NOT_SUPPORTED;
198 }
199
200 //
201 // check for success
202 //
203 if (!NT_SUCCESS(Status))
204 {
205 //
206 // request not supported, please try later
207 //
208 DPRINT1("Request Type %x not supported\n", Type);
209 ASSERT(FALSE);
210 return Status;
211 }
212
213 //
214 // add extra reference which is released when the request is completed
215 //
216 Request->AddRef();
217
218 //
219 // get transfer descriptors
220 //
221 Status = Request->GetEndpointDescriptor(&Descriptor);
222 if (!NT_SUCCESS(Status))
223 {
224 //
225 // failed to get transfer descriptor
226 //
227 DPRINT1("CUSBQueue::AddUSBRequest GetEndpointDescriptor failed with %x\n", Status);
228
229 //
230 // release reference
231 //
232 Request->Release();
233 return Status;
234 }
235
236 //
237 // check type
238 //
239 if (Type == USB_ENDPOINT_TYPE_BULK)
240 {
241 //
242 // get head descriptor
243 //
244 HeadDescriptor = m_BulkHeadEndpointDescriptor;
245 }
246 else if (Type == USB_ENDPOINT_TYPE_CONTROL)
247 {
248 //
249 // get head descriptor
250 //
251 HeadDescriptor = m_ControlHeadEndpointDescriptor;
252 }
253 else if (Type == USB_ENDPOINT_TYPE_INTERRUPT)
254 {
255 //
256 // get head descriptor
257 //
258 HeadDescriptor = FindInterruptEndpointDescriptor(Request->GetInterval());
259 ASSERT(HeadDescriptor);
260 }
261
262 //
263 // insert endpoint at end
264 //
265 LinkEndpoint(HeadDescriptor, Descriptor);
266
267 //
268 // set descriptor active
269 //
270 Descriptor->Flags &= ~OHCI_ENDPOINT_SKIP;
271
272 DPRINT("Request %x Logical %x added to queue Queue %p Logical %x\n", Descriptor, Descriptor->PhysicalAddress.LowPart, HeadDescriptor, HeadDescriptor->PhysicalAddress.LowPart);
273
274
275 if (Type == USB_ENDPOINT_TYPE_CONTROL || Type == USB_ENDPOINT_TYPE_BULK)
276 {
277 //
278 // notify hardware of our request
279 //
280 m_Hardware->HeadEndpointDescriptorModified(Type);
281 }
282
283 return STATUS_SUCCESS;
284 }
285
286 NTSTATUS
287 CUSBQueue::CancelRequests()
288 {
289 UNIMPLEMENTED
290 return STATUS_NOT_IMPLEMENTED;
291 }
292
293 NTSTATUS
294 CUSBQueue::CreateUSBRequest(
295 IUSBRequest **OutRequest)
296 {
297 PUSBREQUEST UsbRequest;
298 NTSTATUS Status;
299
300 *OutRequest = NULL;
301 Status = InternalCreateUSBRequest(&UsbRequest);
302
303 if (NT_SUCCESS(Status))
304 {
305 *OutRequest = UsbRequest;
306 }
307
308 return Status;
309 }
310
311 NTSTATUS
312 CUSBQueue::FindTransferDescriptorInEndpoint(
313 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor,
314 IN ULONG TransferDescriptorLogicalAddress,
315 OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor,
316 OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor)
317 {
318 POHCI_ENDPOINT_DESCRIPTOR LastDescriptor = EndpointDescriptor;
319
320
321 //
322 // skip first endpoint head
323 //
324 EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor;
325
326 while(EndpointDescriptor)
327 {
328 //
329 // check if the transfer descriptor is inside the list
330 //
331 if (IsTransferDescriptorInEndpoint(EndpointDescriptor, TransferDescriptorLogicalAddress))
332 {
333 //
334 // found endpoint
335 //
336 *OutEndpointDescriptor = EndpointDescriptor;
337 *OutPreviousEndpointDescriptor = LastDescriptor;
338
339 //
340 // done
341 //
342 return STATUS_SUCCESS;
343 }
344
345 //
346 // store last endpoint
347 //
348 LastDescriptor = EndpointDescriptor;
349
350 //
351 // move to next
352 //
353 EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor;
354 }
355
356 //
357 // failed to endpoint
358 //
359 return STATUS_NOT_FOUND;
360 }
361
362 NTSTATUS
363 CUSBQueue::FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor)
364 {
365 ULONG Index;
366 NTSTATUS Status;
367
368 //
369 // search descriptor in endpoint list
370 //
371 for(Index = 0; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++)
372 {
373 //
374 // is it in current endpoint
375 //
376 Status = FindTransferDescriptorInEndpoint(m_InterruptEndpoints[Index], TransferDescriptorLogicalAddress, OutEndpointDescriptor, OutPreviousEndpointDescriptor);
377 if (NT_SUCCESS(Status))
378 {
379 //
380 // found transfer descriptor
381 //
382 return STATUS_SUCCESS;
383 }
384 }
385
386 //
387 // not found
388 //
389 return STATUS_NOT_FOUND;
390 }
391
392 BOOLEAN
393 CUSBQueue::IsTransferDescriptorInEndpoint(
394 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor,
395 IN ULONG TransferDescriptorLogicalAddress)
396 {
397 POHCI_GENERAL_TD Descriptor;
398
399 //
400 // get first general transfer descriptor
401 //
402 Descriptor = (POHCI_GENERAL_TD)EndpointDescriptor->HeadLogicalDescriptor;
403
404 //
405 // sanity check
406 //
407 ASSERT(Descriptor);
408
409 do
410 {
411 if (Descriptor->PhysicalAddress.LowPart == TransferDescriptorLogicalAddress)
412 {
413 //
414 // found descriptor
415 //
416 return TRUE;
417 }
418
419 //
420 // move to next
421 //
422 Descriptor = (POHCI_GENERAL_TD)Descriptor->NextLogicalDescriptor;
423 }while(Descriptor);
424
425
426 //
427 // no descriptor found
428 //
429 return FALSE;
430 }
431
432 VOID
433 CUSBQueue::CleanupEndpointDescriptor(
434 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor,
435 POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor)
436 {
437 PUSBREQUEST Request;
438
439 //
440 // FIXME: verify unlinking process
441 //
442 PreviousEndpointDescriptor->NextDescriptor = EndpointDescriptor->NextDescriptor;
443 PreviousEndpointDescriptor->NextPhysicalEndpoint = EndpointDescriptor->NextPhysicalEndpoint;
444
445 //
446 // get corresponding request
447 //
448 Request = PUSBREQUEST(EndpointDescriptor->Request);
449 ASSERT(Request);
450
451 //
452 // notify of completion
453 //
454 Request->CompletionCallback(EndpointDescriptor);
455
456 //
457 // free endpoint descriptor
458 //
459 Request->FreeEndpointDescriptor(EndpointDescriptor);
460
461 //
462 // FIXME: check if complete
463 //
464 //ASSERT(Request->IsRequestComplete());
465
466 //
467 // release request
468 //
469 Request->Release();
470
471 }
472 VOID
473 CUSBQueue::PrintEndpointList(
474 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor)
475 {
476 DPRINT1("CUSBQueue::PrintEndpointList HeadEndpoint %p Logical %x\n", EndpointDescriptor, EndpointDescriptor->PhysicalAddress.LowPart);
477
478 //
479 // get first general transfer descriptor
480 //
481 EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor;
482
483 while(EndpointDescriptor)
484 {
485 DPRINT1(" CUSBQueue::PrintEndpointList Endpoint %p Logical %x\n", EndpointDescriptor, EndpointDescriptor->PhysicalAddress.LowPart);
486
487 //
488 // move to next
489 //
490 EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor;
491 }
492 }
493
494 VOID
495 CUSBQueue::TransferDescriptorCompletionCallback(
496 ULONG TransferDescriptorLogicalAddress)
497 {
498 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, PreviousEndpointDescriptor;
499 NTSTATUS Status;
500
501 DPRINT("CUSBQueue::TransferDescriptorCompletionCallback transfer descriptor %x\n", TransferDescriptorLogicalAddress);
502
503 //
504 // find transfer descriptor in control list
505 //
506 Status = FindTransferDescriptorInEndpoint(m_ControlHeadEndpointDescriptor, TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor);
507 if (NT_SUCCESS(Status))
508 {
509 //
510 // cleanup endpoint
511 //
512 CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor);
513
514 //
515 // done
516 //
517 return;
518 }
519
520 //
521 // find transfer descriptor in bulk list
522 //
523 Status = FindTransferDescriptorInEndpoint(m_BulkHeadEndpointDescriptor, TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor);
524 if (NT_SUCCESS(Status))
525 {
526 //
527 // cleanup endpoint
528 //
529 CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor);
530
531 //
532 // done
533 //
534 return;
535 }
536
537 //
538 // find transfer descriptor in interrupt list
539 //
540 Status = FindTransferDescriptorInInterruptHeadEndpoints(TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor);
541 if (NT_SUCCESS(Status))
542 {
543 //
544 // cleanup endpoint
545 //
546 CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor);
547
548 //
549 // done
550 //
551 return;
552 }
553
554
555 //
556 // hardware reported dead endpoint completed
557 //
558 DPRINT("CUSBQueue::TransferDescriptorCompletionCallback invalid transfer descriptor %x\n", TransferDescriptorLogicalAddress);
559 ASSERT(FALSE);
560
561 }
562
563 POHCI_ENDPOINT_DESCRIPTOR
564 CUSBQueue::FindInterruptEndpointDescriptor(
565 UCHAR InterruptInterval)
566 {
567 ULONG Index = 0;
568 ULONG Power = 1;
569
570 //
571 // sanity check
572 //
573 ASSERT(InterruptInterval < OHCI_BIGGEST_INTERVAL);
574
575 //
576 // find interrupt index
577 //
578 while (Power <= OHCI_BIGGEST_INTERVAL / 2)
579 {
580 //
581 // is current interval greater
582 //
583 if (Power * 2 > InterruptInterval)
584 break;
585
586 //
587 // increment power
588 //
589 Power *= 2;
590
591 //
592 // move to next interrupt
593 //
594 Index++;
595 }
596
597 DPRINT("InterruptInterval %lu Selected InterruptIndex %lu Choosen Interval %lu\n", InterruptInterval, Index, Power);
598
599 //
600 // return endpoint
601 //
602 return m_InterruptEndpoints[Index];
603 }
604
605 NTSTATUS
606 CreateUSBQueue(
607 PUSBQUEUE *OutUsbQueue)
608 {
609 PUSBQUEUE This;
610
611 //
612 // allocate controller
613 //
614 This = new(NonPagedPool, TAG_USBOHCI) CUSBQueue(0);
615 if (!This)
616 {
617 //
618 // failed to allocate
619 //
620 return STATUS_INSUFFICIENT_RESOURCES;
621 }
622
623 //
624 // add reference count
625 //
626 This->AddRef();
627
628 //
629 // return result
630 //
631 *OutUsbQueue = (PUSBQUEUE)This;
632
633 //
634 // done
635 //
636 return STATUS_SUCCESS;
637 }