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