ReactOS 0.4.17-dev-1281-g065abb3
isoxfer.cpp
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS xHCI Host Controller Driver
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: Isochronous transfer rings: frame scheduling, staging, TD writing, events and reclaim
5 * COPYRIGHT: Copyright 2026 Justin Miller <justin.miller@reactos.org>
6 */
7
8#include "xhcidrv.h"
9
10#define NDEBUG
11#include <debug.h>
12
13static const ULONG XhciIsochTransferTag = 'hcsI';
14
15static const ULONG XhciIsochMaxInFlight = 5;
17static const ULONG XhciIsochAsapDelay = 5;
18static const LONG XhciIsochFrameWindow = 1024;
19static const ULONG XhciIsochRestartFast = 1024;
20static const ULONG XhciIsochRestartSlow = 255;
21static const ULONG XhciIsochInterruptEvery = 0x7F;
22static const ULONG XhciIsochWatchdogMs = 10000;
23static const ULONG XhciIsochTrbBytesMax = 0x10000;
24static const ULONG XhciIsochMaxPackets = 0xFFFF;
25static const USBD_STATUS XhciIsochNotSet = (USBD_STATUS)0xFFFFFFFF;
26
27/* Microframes added to MFINDEX before it becomes a frame number */
28static const ULONG XhciIsochBiasNow = 1;
29static const ULONG XhciIsochBiasLate = 2;
30
31/* Reason code handed to the controller with a fatal error */
32static const ULONG XhciIsochReasonBadEventData = 0x101E;
33
34/* m_Flags bits, all cleared when mapping starts */
35static const LONG XhciIsochDoorbellRung = 0x01;
36static const LONG XhciIsochOkToReclaim = 0x02;
37static const LONG XhciIsochCanceled = 0x04;
38static const LONG XhciIsochReclaimAck = 0x08;
39static const LONG XhciIsochEmptyExpected = 0x10;
40static const LONG XhciIsochLastFrameValid = 0x20;
41static const LONG XhciIsochStoppedSeen = 0x40;
42
43/* m_SegmentWait values */
44static const LONG XhciIsochWaitIdle = 0;
45static const LONG XhciIsochWaitParked = 1;
46static const LONG XhciIsochWaitArrived = 2;
48
49/* Small helpers that only use public state *********************************/
50
51static
56{
57 return Code == static_cast<ULONG>(XhciCompletionCode::Stopped) ||
60}
61
62static
66 _In_ const XhciTransferRing* Ring)
67{
68 return (Ring->m_Endpoint->Descriptor()->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) != 0;
69}
70
71static
75 _In_ ULONG Packets,
76 _In_ ULONG PerFrame)
77{
78 return (Packets + PerFrame - 1) / PerFrame;
79}
80
81static
85 _In_ const XhciIsochTransfer* Transfer,
87{
88 const struct _URB_ISOCH_TRANSFER* Urb = Transfer->Urb;
89 ULONG End;
90
91 End = (Packet + 1 < Transfer->PacketsInUrb) ? Urb->IsoPacket[Packet + 1].Offset : Transfer->BytesTotal;
92 return End - Urb->IsoPacket[Packet].Offset;
93}
94
95static
99 _In_ XhciIsochTransfer* Transfer,
101{
102 return &Transfer->Stages[(Transfer->StageHead + Position) % XhciIsochTransfer::StageSlots];
103}
104
106static
107ULONG
108NTAPI
110 _In_ const XhciTransferRing* Ring)
111{
112 ULONG Payload = Ring->m_Endpoint->MaxEsitPayload();
113
114 if (Payload == 0)
115 Payload = Ring->m_Endpoint->MaxPacketSize();
116
117 return Payload;
118}
119
121static
123NTAPI
125 _In_ const XhciTransferRing* Ring)
126{
127 const XHCI_SLOT_CONTEXT* Slot;
128
129 if (!Ring->m_Controller->HasErrata(XhciErrata::IsochFsNoBareLinkTd) ||
130 Ring->m_Device->Speed() != UsbFullSpeed)
131 {
132 return FALSE;
133 }
134
135 Slot = static_cast<const XHCI_SLOT_CONTEXT*>(Ring->m_Device->OutputContext(0));
136 return Slot != NULL && Slot->TTHubSlotId != 0;
137}
138
139static
141NTAPI
143 _In_ WDFREQUEST Request)
144{
146
148 WdfRequestGetParameters(Request, &Parameters);
149 return static_cast<struct _URB_ISOCH_TRANSFER*>(Parameters.Parameters.Others.Arg1);
150}
151
152static
153ULONG
154NTAPI
156 _In_ const LIST_ENTRY* Head)
157{
158 const LIST_ENTRY* Entry;
159 ULONG Count = 0;
160
161 for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
162 Count++;
163
164 return Count;
165}
166
167static
168VOID
169NTAPI
173{
174 while (!IsListEmpty(Source))
176}
177
179static
181NTAPI
183 _In_ const LIST_ENTRY* Pending,
184 _In_ const XhciIsochTransfer* Transfer,
185 _In_ ULONG Sequence)
186{
187 const LIST_ENTRY* Entry;
188
189 for (Entry = Pending->Flink; Entry != Pending; Entry = Entry->Flink)
190 {
192 return Transfer->Sequence == Sequence;
193 }
194
195 return FALSE;
196}
197
198/* Scatter gather walking */
199
200static
201VOID
202NTAPI
205 _Inout_ PULONG Element,
207{
208 while (*Element < SgList->NumberOfElements && *Offset >= SgList->Elements[*Element].Length)
209 {
210 (*Element)++;
211 *Offset = 0;
212 }
213}
214
219static
220VOID
221NTAPI
224 _Inout_ PULONG Element,
228 _Out_ PULONG Chunk)
229{
231 ULONG Left;
232 ULONG Boundary;
233
235
236 if (*Element >= SgList->NumberOfElements)
237 {
238 *Address = 0;
239 *Chunk = 0;
240 return;
241 }
242
243 Start = (ULONG64)SgList->Elements[*Element].Address.QuadPart + *Offset;
244 Left = SgList->Elements[*Element].Length - *Offset;
246
247 *Address = Start;
248 *Chunk = min(min(Bytes, Left), Boundary);
249 *Offset += *Chunk;
250}
251
252static
253ULONG
254NTAPI
257 _In_ ULONG Element,
260{
262 ULONG Chunk;
263 ULONG Pieces = 0;
264
265 while (Bytes != 0)
266 {
267 XhciIsochTakePiece(SgList, &Element, &Offset, Bytes, &Address, &Chunk);
268 if (Chunk == 0)
269 break;
270
271 Bytes -= Chunk;
272 Pieces++;
273 }
274
275 return (Pieces == 0) ? 1 : Pieces;
276}
277
278static
279VOID
280NTAPI
283 _Inout_ PULONG Element,
286{
287 while (Bytes != 0)
288 {
289 ULONG Left;
290
292 if (*Element >= SgList->NumberOfElements)
293 return;
294
295 Left = SgList->Elements[*Element].Length - *Offset;
296 if (Left > Bytes)
297 Left = Bytes;
298
299 *Offset += Left;
300 Bytes -= Left;
301 }
302}
303
304/* Stage TRB ranges */
305
306static
308NTAPI
312 _In_ ULONG OnePastEnd,
314{
315 ULONG64 Base = (ULONG64)Segment->LogicalAddress.QuadPart;
316
317 return Address >= Base + (ULONG64)Start * sizeof(XHCI_TRB) &&
318 Address < Base + (ULONG64)OnePastEnd * sizeof(XHCI_TRB);
319}
320
321static
323NTAPI
325 _In_ const XhciIsochStage* Stage,
327{
328 const LIST_ENTRY* Entry;
329 ULONG Start = Stage->FirstIndex;
330
331 for (Entry = Stage->UsedSegments.Flink; Entry != &Stage->UsedSegments; Entry = Entry->Flink)
332 {
334
336 return TRUE;
337 Start = 0;
338 }
339
340 return XhciIsochSegmentHas(Stage->LastSegment, Start, Stage->EndIndex, Address);
341}
342
344static
346NTAPI
348 _In_ const XhciIsochStage* Stage,
350{
351 if (Segment == Stage->LastSegment)
352 return NULL;
353
354 if (Segment->Link.Flink == &Stage->UsedSegments)
355 return Stage->LastSegment;
356
357 return CONTAINING_RECORD(Segment->Link.Flink, XhciDmaBuffer, Link);
358}
359
360static
361ULONG
362NTAPI
364 _In_ const XhciIsochStage* Stage,
366{
367 return (Segment == Stage->LastSegment) ? Stage->EndIndex : Segment->Size / sizeof(XHCI_TRB);
368}
369
371static
372ULONG
373NTAPI
375 _In_ const XhciTransferRing* Ring,
378 _In_ ULONG Residual,
379 _In_ ULONG Sum)
380{
381 switch (static_cast<XhciCompletionCode>(Code))
382 {
384 if (!Ring->m_Controller->m_Registers.MseLengthUsable())
385 return Sum;
386 break;
387
395 break;
396
398 return Residual;
399
402 return Sum;
403
404 default:
405 DPRINT1("Isoch DCI %lu: code %lu on a data TRB\n", Ring->m_Endpoint->Dci(), Code);
406 return Sum;
407 }
408
409 /* QUIRK: a residual above the TRB length wraps the count */
410 if (Residual > Length)
411 {
412 DPRINT1("Isoch DCI %lu: residual %lu above TRB length %lu, code %lu\n",
413 Ring->m_Endpoint->Dci(), Residual, Length, Code);
414 }
415
416 return Sum + Length - Residual;
417}
418
419/* Ring type entry points ****************************************************/
420
424{
425 XhciIsochRing* Self = &Ring->m_Type.Isoch;
426 USB_DEVICE_SPEED Speed = Ring->m_Device->Speed();
427 ULONG Interval = Ring->m_Endpoint->Descriptor()->bInterval;
428 WDF_TIMER_CONFIG TimerConfig;
432
433 RtlZeroMemory(Self, sizeof(*Self));
437
438 if (Speed >= UsbHighSpeed)
439 {
440 /* QUIRK: the raw bInterval is used, and 0 behaves as an 8 microframe period */
441 Period = (Interval == 0 || Interval > 4) ? 8 : (1UL << (Interval - 1));
442 Self->m_PacketsPerFrame = 8 / Period;
443 }
444 else
445 {
446 NT_ASSERT(Speed == UsbFullSpeed);
447 Self->m_PacketsPerFrame = 1;
448 }
449
450 WDF_TIMER_CONFIG_INIT(&TimerConfig, EvtWatchdog);
451 TimerConfig.TolerableDelay = 0;
453 Attributes.ParentObject = Ring->m_Queue;
454
455 Status = WdfTimerCreate(&TimerConfig, &Attributes, &Self->m_Watchdog);
456 if (!NT_SUCCESS(Status))
457 {
458 DPRINT1("Isoch DCI %lu: ring empty watchdog not created, 0x%08lx\n",
459 Ring->m_Endpoint->Dci(), Status);
460 return Status;
461 }
462
463 DPRINT("Isoch DCI %lu: %lu packets per frame\n", Ring->m_Endpoint->Dci(), Self->m_PacketsPerFrame);
464 return STATUS_SUCCESS;
465}
466
467VOID
470{
472
473 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
474 Ring->m_Type.Isoch.m_FreshStream = TRUE;
475 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
476}
477
478VOID
481{
482 XhciIsochRing* Self = &Ring->m_Type.Isoch;
484
486 WdfTimerStop(Self->m_Watchdog, FALSE);
487
488 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
489 if (OwnsRequests(Ring))
490 DPRINT1("Isoch DCI %lu disabled while it still owns requests\n", Ring->m_Endpoint->Dci());
491 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
492}
493
494VOID
497{
498 XhciIsochRing* Self = &Ring->m_Type.Isoch;
499
500 /* A pass parked on a segment wait only resumes from a PASSIVE work item; let it go */
503 {
504 return;
505 }
506
507 DPRINT1("Isoch DCI %lu disabled while waiting for segments\n", Ring->m_Endpoint->Dci());
508
509 if (Ring->ChangeMapState(FALSE, XhciMapState::Halting, XhciMapState::Halted) != XhciMapState::Halting)
510 {
511 InterlockedCompareExchange(&Ring->m_MapState,
512 static_cast<LONG>(XhciMapState::PausedFill),
513 static_cast<LONG>(XhciMapState::Filling));
514 }
515}
516
517VOID
520{
521 BOOLEAN Owned;
523
524 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
525 Owned = OwnsRequests(Ring);
526 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
527
528 if (Owned)
529 {
530 DPRINT1("Isoch DCI %lu cleaned up with requests still owned\n", Ring->m_Endpoint->Dci());
532 }
533}
534
535VOID
536NTAPI
538 _In_ WDFQUEUE Queue,
540{
542
544
545 if (Ring->ChangeMapState(FALSE, XhciMapState::PausedFill, XhciMapState::Filling) == XhciMapState::PausedFill)
546 {
547 BuildTdsFromQueue(Ring);
548 return;
549 }
550
551 /* A pass that is about to pause sees this and goes round once more */
553 if (Ring->ChangeMapState(FALSE, XhciMapState::PausedFill, XhciMapState::Filling) == XhciMapState::PausedFill)
554 BuildTdsFromQueue(Ring);
555}
556
557VOID
558NTAPI
560 _In_ WDFQUEUE Queue,
561 _In_ WDFREQUEST Request)
562{
564 XhciIsochRing* Self = &Ring->m_Type.Isoch;
565 XhciIsochTransfer* Transfer = &XhciGetRequestData(Request)->Isoch;
567 ULONG Index;
569
570 /* QUIRK: the packet statuses are left at the private "not set" value */
571 Urb->Hdr.Status = USBD_STATUS_CANCELED;
572 Urb->TransferBufferLength = 0;
573 Urb->ErrorCount = 0;
574 for (Index = 0; Index < Urb->NumberOfPackets && Index < XhciIsochMaxPackets; Index++)
575 Urb->IsoPacket[Index].Status = XhciIsochNotSet;
576
577 RtlZeroMemory(Transfer, sizeof(*Transfer));
579 Transfer->Request = Request;
580 Transfer->Urb = Urb;
581 Transfer->Ring = Ring;
582 Transfer->CancelState = XhciIsochCancel::Done;
583 Transfer->Status = STATUS_CANCELLED;
584
586 InsertTailList(&Self->m_Completion, &Transfer->Link);
588
589 WdfDpcEnqueue(Ring->m_CompletionDpc);
590}
591
592VOID
593NTAPI
595 _In_ WDFDPC Dpc)
596{
597 XhciTransferRing* Ring = XhciTransferRing::FromQueue(static_cast<WDFQUEUE>(WdfDpcGetParentObject(Dpc)));
598 XhciIsochRing* Self = &Ring->m_Type.Isoch;
600 BOOLEAN Acknowledge = FALSE;
602
604 if (Self->m_DpcRunning)
605 {
607 return;
608 }
609 Self->m_DpcRunning = TRUE;
610
611 while (!IsListEmpty(&Self->m_Completion))
612 {
616
617 CompleteBatch(Ring, &Batch);
618
620 }
621
622 if ((Self->m_Flags & XhciIsochReclaimAck) && !OwnsRequests(Ring))
623 {
625 Acknowledge = TRUE;
626 }
627
628 Self->m_DpcRunning = FALSE;
630
631 if (Acknowledge)
633}
634
635VOID
638{
639 XhciIsochRing* Self = &Ring->m_Type.Isoch;
642
643 InterlockedExchange(&Self->m_Flags, 0);
644
645 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
646 Self->m_PendingStages = 0;
647 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
648
649 Previous = Ring->ChangeMapState(TRUE, XhciMapState::Halted, XhciMapState::Filling);
650 if (Previous == XhciMapState::Halted)
651 {
652 BuildTdsFromQueue(Ring);
653 return;
654 }
655
656 DPRINT1("Isoch DCI %lu started mapping from map state %ld\n",
657 Ring->m_Endpoint->Dci(), static_cast<LONG>(Previous));
658}
659
660VOID
663{
665
666 Previous = Ring->ChangeMapState(TRUE, XhciMapState::Halted, XhciMapState::Halting);
667
668 switch (Previous)
669 {
670 case XhciMapState::AwaitingStarve:
671 WdfTimerStop(Ring->m_Type.Isoch.m_Watchdog, FALSE);
672 Ring->ChangeMapState(FALSE, XhciMapState::Halting, XhciMapState::Halted);
673 break;
674
675 case XhciMapState::PausedFill:
676 Ring->ChangeMapState(FALSE, XhciMapState::Halting, XhciMapState::Halted);
677 break;
678
679 case XhciMapState::Filling:
680 /* The context that owns mapping acknowledges the stop */
681 break;
682
683 default:
684 DPRINT1("Isoch DCI %lu stopped mapping from map state %ld\n",
685 Ring->m_Endpoint->Dci(), static_cast<LONG>(Previous));
686 break;
687 }
688}
689
690VOID
693{
695
696 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
697 Ring->m_Type.Isoch.m_FreshStream = TRUE;
698 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
699}
700
701VOID
704{
705 XhciIsochRing* Self = &Ring->m_Type.Isoch;
708
709 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
710 if (Self->m_Flags & XhciIsochCanceled)
711 {
713 Queue = TRUE;
714 }
715 else
716 {
718 }
719 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
720
721 if (Queue)
722 WdfDpcEnqueue(Ring->m_CompletionDpc);
723}
724
725VOID
728{
729 /* Lost Missed Service events mean there is nothing reliable to wait for; late ones are dropped */
730 Ring->m_Endpoint->OnExpectedEventsProcessed();
731}
732
733VOID
736{
737 XhciIsochRing* Self = &Ring->m_Type.Isoch;
739
740 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
743 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
744
745 /* Only the DPC can tell that nothing is owned anymore */
746 WdfDpcEnqueue(Ring->m_CompletionDpc);
747}
748
751 _In_ const XhciTransferRing* Ring)
752{
753 return (Ring->m_Type.Isoch.m_Flags & XhciIsochDoorbellRung) != 0;
754}
755
758 _In_ const XhciTransferRing* Ring)
759{
760 PKSPIN_LOCK Lock = const_cast<PKSPIN_LOCK>(&Ring->m_Lock);
763
765 Pending = !IsListEmpty(&Ring->m_Type.Isoch.m_Pending);
767
768 return Pending;
769}
770
774 _In_ const XHCI_TRB* Event)
775{
776 if (Event->Dword[3] & XHCI_TRANSFER_EVENT_ED)
777 Ed1Event(Ring, Event);
778 else
779 Ed0Event(Ring, Event);
780
781 return TRUE;
782}
783
784VOID
788{
789 XhciIsochRing* Self = &Ring->m_Type.Isoch;
790
791 if (!NT_SUCCESS(Status))
792 DPRINT1("Isoch DCI %lu segment growth failed, 0x%08lx\n", Ring->m_Endpoint->Dci(), Status);
793
794 Self->m_BufferRetries = 0;
795
796 for (;;)
797 {
798 /* QUIRK: the pass runs even when a stop came in meanwhile, so one more stage can go out */
801 {
802 BuildTdsFromQueue(Ring);
803 return;
804 }
805
808 {
809 return;
810 }
811
812 /* The pass has not left yet; it sees this and retries the stage itself */
815 {
816 return;
817 }
818 }
819}
820
821/* Mapping pass **************************************************************/
822
823VOID
826{
827 XhciIsochRing* Self = &Ring->m_Type.Isoch;
828 LONG State;
830
832
833 for (;;)
834 {
835 FillLoop(Ring);
836
837 State = InterlockedCompareExchange(&Ring->m_MapState, 0, 0);
838
839 if (State == static_cast<LONG>(XhciMapState::PausedFill) && Self->m_FillWanted != 0)
840 {
841 if (Ring->ChangeMapState(FALSE, XhciMapState::PausedFill, XhciMapState::Filling) == XhciMapState::PausedFill)
842 continue;
843 }
844 else if (State == static_cast<LONG>(XhciMapState::AwaitingStarve) && Self->m_RingEmptySeen != 0)
845 {
846 if (LeaveRingEmptyWait(Ring))
847 continue;
848 }
849
850 break;
851 }
852
854}
855
856VOID
859{
860 XhciIsochRing* Self = &Ring->m_Type.Isoch;
861 XhciIsochStage* Stage;
863
864 for (;;)
865 {
868
869 Stage = NextTdSlot(Ring);
870 if (Stage == NULL)
871 {
872 Ring->ChangeMapState(FALSE, XhciMapState::Filling, XhciMapState::PausedFill);
873 return;
874 }
875
876 Result = PrimeStage(Ring, Stage);
878 continue;
880 return;
881 if (Result == Outcome::Gap)
882 {
883 AwaitRingEmpty(Ring);
884 return;
885 }
886 if (Result == Outcome::Failed)
887 {
888 Ring->ChangeMapState(FALSE, XhciMapState::Filling, XhciMapState::PausedFill);
889 return;
890 }
891
892 Result = EncodeStage(Ring, Stage);
894 return;
895 if (Result == Outcome::Late)
896 {
897 AwaitRingEmpty(Ring);
898 return;
899 }
900
901 /* Also completes a stop that raced in */
902 if (Ring->ChangeMapState(FALSE, XhciMapState::Filling, XhciMapState::Filling) != XhciMapState::Filling)
903 return;
904 }
905}
906
907VOID
910{
911 if (Ring->ChangeMapState(FALSE, XhciMapState::Filling, XhciMapState::AwaitingStarve) == XhciMapState::Filling)
912 WdfTimerStart(Ring->m_Type.Isoch.m_Watchdog, WDF_REL_TIMEOUT_IN_MS(XhciIsochWatchdogMs));
913}
914
918{
919 if (Ring->ChangeMapState(FALSE, XhciMapState::AwaitingStarve, XhciMapState::Filling) !=
920 XhciMapState::AwaitingStarve)
921 {
922 return FALSE;
923 }
924
925 WdfTimerStop(Ring->m_Type.Isoch.m_Watchdog, FALSE);
926 return TRUE;
927}
928
930VOID
933 _In_ ULONG InFlight)
934{
935 InterlockedExchange(&Ring->m_Type.Isoch.m_FillWanted, 1);
936
937 if (Ring->ChangeMapState(FALSE, XhciMapState::PausedFill, XhciMapState::Filling) == XhciMapState::PausedFill)
938 {
939 BuildTdsFromQueue(Ring);
940 return;
941 }
942
943 if (InFlight == 0 && LeaveRingEmptyWait(Ring))
944 BuildTdsFromQueue(Ring);
945}
946
950{
951 XhciIsochRing* Self = &Ring->m_Type.Isoch;
952
955 {
956 return TRUE;
957 }
958
960 return FALSE;
961}
962
963/* Intake and stage selection ************************************************/
964
968{
969 XhciIsochRing* Self = &Ring->m_Type.Isoch;
970 XhciIsochTransfer* Transfer;
971 XhciIsochStage* Stage = NULL;
972 WDFREQUEST Request;
976
977 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
978 Self->m_CurrentStage = NULL;
979
981 {
982 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
983 return NULL;
984 }
985
986 if (!IsListEmpty(&Self->m_Pending))
987 {
989
991 {
992 /* QUIRK: nothing counts the retries up, so this is never reached */
993 DPRINT1("Isoch DCI %lu gave up waiting for segments\n", Ring->m_Endpoint->Dci());
994
995 if (Transfer->TdsRetired != Transfer->TdsQueued)
996 {
997 SkipUnmapped(Transfer);
998 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
999 return NULL;
1000 }
1001
1003 }
1004 else if (Transfer->PacketsQueued + Transfer->PacketsDropped < Transfer->PacketsInUrb)
1005 {
1006 FlagMissedPackets(Ring, Transfer);
1007
1008 if (Transfer->Urb->IsoPacket[Transfer->PacketsInUrb - 1].Status != USBD_STATUS_ISO_NOT_ACCESSED_LATE)
1009 {
1010 Stage = AcquireStage(Ring, Transfer);
1011 Self->m_CurrentStage = Stage;
1012 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1013 return Stage;
1014 }
1015
1016 if (Transfer->TdsRetired != Transfer->TdsQueued)
1017 {
1018 SkipUnmapped(Transfer);
1019 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1020 return NULL;
1021 }
1022
1023 /* QUIRK: like the USB 2.0 stack, a URB that ran late reports success, even when canceled */
1025 }
1026 }
1027
1028 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1029
1030 for (;;)
1031 {
1032 Status = WdfIoQueueRetrieveNextRequest(Ring->m_Queue, &Request);
1033 if (!NT_SUCCESS(Status))
1034 {
1035 if (Self->m_PendingStages == 0)
1036 DPRINT("Isoch DCI %lu has nothing queued, the stream may starve\n", Ring->m_Endpoint->Dci());
1037 return NULL;
1038 }
1039
1040 if (TakeRequest(Ring, Request, &Transfer))
1041 break;
1042 }
1043
1044 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
1045 Idle = (Self->m_PendingStages == 0);
1046 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1047
1048 /* Every TD on the ring reports to one event ring so its events stay in order */
1049 if (Idle)
1050 Ring->UpdateInterrupterTarget(XhciTransferRing::UrbProcessorIndex((PURB)Transfer->Urb));
1051
1052 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
1053 Stage = AcquireStage(Ring, Transfer);
1054 InsertTailList(&Self->m_Pending, &Transfer->Link);
1055 Self->m_CurrentStage = Stage;
1056 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1057
1058 return Stage;
1059}
1060
1062BOOLEAN
1064 _In_ XhciTransferRing* Ring,
1065 _In_ WDFREQUEST Request,
1066 _Out_ XhciIsochTransfer** Taken)
1067{
1068 XhciIsochRing* Self = &Ring->m_Type.Isoch;
1069 XhciRequestData* Data = XhciGetRequestData(Request);
1070 struct _URB_ISOCH_TRANSFER* Urb;
1071 XhciIsochTransfer* Transfer;
1073
1074 *Taken = NULL;
1075
1076 if (Data == NULL)
1077 {
1078 DPRINT1("Isoch request %p has no driver context\n", Request);
1079 WdfRequestComplete(Request, STATUS_INVALID_DEVICE_STATE);
1080 return FALSE;
1081 }
1082
1084 Transfer = &Data->Isoch;
1085
1086 /* UCX reuses requests, so the record starts from zero every time */
1087 RtlZeroMemory(Transfer, sizeof(*Transfer));
1088 XhciClearListEntry(&Transfer->Link);
1090 Transfer->Sequence = ++Self->m_NextSequence;
1091 Transfer->Request = Request;
1092 Transfer->Urb = Urb;
1093 Transfer->Ring = Ring;
1094 Transfer->CancelState = XhciIsochCancel::Idle;
1095 Transfer->Status = STATUS_PENDING;
1096 Transfer->BytesTotal = Urb->TransferBufferLength;
1097 Transfer->PacketsInUrb = (Urb->NumberOfPackets <= XhciIsochMaxPackets) ? Urb->NumberOfPackets : 0;
1098
1099 if (Urb->TransferBufferMDL != NULL)
1100 {
1101 Transfer->Mdl = Urb->TransferBufferMDL;
1102 }
1103 else if (Transfer->BytesTotal != 0)
1104 {
1105 if (Urb->TransferBuffer != NULL)
1106 Transfer->Mdl = IoAllocateMdl(Urb->TransferBuffer, Transfer->BytesTotal, FALSE, FALSE, NULL);
1107
1108 if (Transfer->Mdl == NULL)
1109 {
1110 DPRINT1("Isoch DCI %lu request %p: no MDL for a %lu byte buffer\n",
1111 Ring->m_Endpoint->Dci(), Request, Transfer->BytesTotal);
1112
1113 /* QUIRK: the caller's packet statuses and error count are kept */
1115 return FALSE;
1116 }
1117
1118 Transfer->OwnMdl = TRUE;
1119 MmBuildMdlForNonPagedPool(Transfer->Mdl);
1120 }
1121
1122 /* Packet offsets must lie inside the buffer before the MDL chain is walked */
1123 if (!PacketsValid(Transfer))
1124 {
1125 DPRINT1("Isoch DCI %lu request %p: %lu packets with bad offsets for %lu bytes\n",
1126 Ring->m_Endpoint->Dci(), Request, Urb->NumberOfPackets, Transfer->BytesTotal);
1127 Urb->ErrorCount = 0;
1129 return FALSE;
1130 }
1131
1132 Status = ScheduleStartFrame(Ring, Transfer);
1133 if (!NT_SUCCESS(Status))
1134 {
1136 return FALSE;
1137 }
1138
1139 FlagMissedPackets(Ring, Transfer);
1140 if (Urb->IsoPacket[Transfer->PacketsInUrb - 1].Status == USBD_STATUS_ISO_NOT_ACCESSED_LATE)
1141 {
1142 DPRINT("Isoch DCI %lu request %p is late in full\n", Ring->m_Endpoint->Dci(), Request);
1144 return FALSE;
1145 }
1146
1147 if (Transfer->BytesTotal == 0)
1148 {
1149 DPRINT("Isoch DCI %lu request %p has no data\n", Ring->m_Endpoint->Dci(), Request);
1151 return FALSE;
1152 }
1153
1154 Transfer->CancelState = XhciIsochCancel::Armed;
1155 Status = WdfRequestMarkCancelableEx(Request, EvtRequestCancel);
1156 if (!NT_SUCCESS(Status))
1157 {
1158 DPRINT1("Isoch DCI %lu request %p canceled before mapping, 0x%08lx\n",
1159 Ring->m_Endpoint->Dci(), Request, Status);
1160 Transfer->CancelState = XhciIsochCancel::Done;
1162 return FALSE;
1163 }
1164
1165 *Taken = Transfer;
1166 return TRUE;
1167}
1168
1169BOOLEAN
1171 _In_ const XhciIsochTransfer* Transfer)
1172{
1173 const struct _URB_ISOCH_TRANSFER* Urb = Transfer->Urb;
1174 ULONG Index;
1175
1176 if (Transfer->PacketsInUrb == 0)
1177 return FALSE;
1178
1179 for (Index = 0; Index + 1 < Transfer->PacketsInUrb; Index++)
1180 {
1181 if (Urb->IsoPacket[Index].Offset > Urb->IsoPacket[Index + 1].Offset)
1182 return FALSE;
1183 }
1184
1185 return Urb->IsoPacket[Transfer->PacketsInUrb - 1].Offset <= Transfer->BytesTotal;
1186}
1187
1190 _In_ XhciTransferRing* Ring,
1191 _Inout_ XhciIsochTransfer* Transfer)
1192{
1193 XhciIsochRing* Self = &Ring->m_Type.Isoch;
1194 struct _URB_ISOCH_TRANSFER* Urb = Transfer->Urb;
1195 ULONG Threshold;
1196 ULONG StartFrame;
1197 ULONG Now = 0;
1198 ULONG Index;
1199 LONG Distance;
1200 KIRQL OldIrql;
1201
1202 Threshold = (Ring->m_Device->Speed() >= UsbHighSpeed) ? XhciIsochRestartFast : XhciIsochRestartSlow;
1203
1204 if (Urb->TransferFlags & USBD_START_ISO_TRANSFER_ASAP)
1205 Now = Ring->m_Controller->GetFrameNumber(XhciIsochBiasNow);
1206
1207 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
1208
1209 if (Urb->TransferFlags & USBD_START_ISO_TRANSFER_ASAP)
1210 {
1211 StartFrame = Self->m_NextStartFrame;
1212
1213 /* QUIRK: unsigned and without wrap handling, and the threshold is really a packet count */
1214 if (Self->m_FreshStream || (Now > StartFrame && Now - StartFrame > Threshold))
1215 {
1216 StartFrame = Now + XhciIsochAsapDelay;
1218 }
1219
1220 Urb->StartFrame = StartFrame;
1221 }
1222 else
1223 {
1224 StartFrame = Urb->StartFrame;
1225 }
1226
1227 Self->m_NextStartFrame = StartFrame + XhciIsochFrames(Transfer->PacketsInUrb, Self->m_PacketsPerFrame);
1228 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1229
1230 Transfer->StartFrame = StartFrame;
1231 Urb->TransferBufferLength = 0;
1232 Urb->ErrorCount = 0;
1233 for (Index = 0; Index < Transfer->PacketsInUrb; Index++)
1234 Urb->IsoPacket[Index].Status = XhciIsochNotSet;
1235
1236 Distance = (LONG)(StartFrame - Ring->m_Controller->GetFrameNumber(XhciIsochBiasLate));
1237 if (Distance > XhciIsochFrameWindow || Distance < -XhciIsochFrameWindow)
1238 {
1239 DPRINT1("Isoch DCI %lu: start frame 0x%lx is %ld frames away\n",
1240 Ring->m_Endpoint->Dci(), StartFrame, Distance);
1241 return STATUS_UNSUCCESSFUL;
1242 }
1243
1244 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
1245 Self->m_FreshStream = FALSE;
1246 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1247
1248 return STATUS_SUCCESS;
1249}
1250
1252VOID
1254 _In_ XhciTransferRing* Ring,
1255 _Inout_ XhciIsochTransfer* Transfer)
1256{
1257 ULONG PerFrame = Ring->m_Type.Isoch.m_PacketsPerFrame;
1258 ULONG First = Transfer->PacketsQueued + Transfer->PacketsDropped;
1259 ULONG Frame = Transfer->StartFrame + XhciIsochFrames(First, PerFrame);
1260 ULONG End = Frame + XhciIsochFrames(Transfer->PacketsInUrb - First, PerFrame);
1261 ULONG Now = Ring->m_Controller->GetFrameNumber(XhciIsochBiasLate);
1262 ULONG Packet;
1263 ULONG Index;
1264
1265 /* QUIRK: unsigned compares without wrap handling */
1266 while (Frame < End && Frame <= Now)
1267 {
1268 Packet = (Frame - Transfer->StartFrame) * PerFrame;
1269
1270 for (Index = 0; Index < PerFrame && Packet + Index < Transfer->PacketsInUrb; Index++)
1271 {
1272 Transfer->Urb->IsoPacket[Packet + Index].Status = USBD_STATUS_ISO_NOT_ACCESSED_LATE;
1273 Transfer->BytesDropped += XhciIsochPacketLength(Transfer, Packet + Index);
1274 Transfer->PacketsDropped++;
1275 }
1276
1277 Frame++;
1278 }
1279}
1280
1281VOID
1283 _Inout_ XhciIsochTransfer* Transfer)
1284{
1285 Transfer->PacketsDropped = Transfer->PacketsInUrb - Transfer->PacketsQueued;
1286 Transfer->BytesDropped = (Transfer->BytesTotal > Transfer->BytesQueued) ?
1287 Transfer->BytesTotal - Transfer->BytesQueued : 0;
1288}
1289
1292XhciIsochRing::AcquireStage(
1293 _In_ XhciTransferRing* Ring,
1294 _Inout_ XhciIsochTransfer* Transfer)
1295{
1296 XhciIsochStage* Stage;
1297
1298 if (Transfer->StageCount == XhciIsochTransfer::StageSlots)
1299 return NULL;
1300
1301 Stage = XhciIsochStageAt(Transfer, Transfer->StageCount);
1302 Transfer->StageCount++;
1303
1304 RtlZeroMemory(Stage, sizeof(*Stage));
1305 Stage->InUse = TRUE;
1306 Stage->Transfer = Transfer;
1307 Stage->FirstPacket = 0xFFFFFFFE;
1308 Stage->LastPacket = 0xFFFFFFFE;
1309 Stage->NextPacket = 1;
1312 Stage->FirstSegment = Ring->m_Segment;
1313 Stage->LastSegment = Ring->m_Segment;
1314 Stage->ScanSegment = Ring->m_Segment;
1315 Stage->FirstIndex = Ring->m_EnqueueIndex;
1316 Stage->EndIndex = Ring->m_EnqueueIndex;
1317 Stage->ScanIndex = Ring->m_EnqueueIndex;
1318
1319 return Stage;
1320}
1321
1324VOID
1325XhciIsochRing::FreeStage(
1326 _In_ XhciTransferRing* Ring,
1327 _Inout_ XhciIsochStage* Stage)
1328{
1329 XhciIsochTransfer* Transfer = Stage->Transfer;
1330
1331 NT_ASSERT(Stage->SgList == NULL);
1332
1333 if (Stage->FreeMdl)
1334 IoFreeMdl(Stage->Mdl);
1335 Stage->Mdl = NULL;
1336 Stage->FreeMdl = FALSE;
1337
1338 Ring->ReturnSegments(&Stage->FreeSegments, TRUE);
1339 Ring->ReturnSegments(&Stage->UsedSegments, FALSE);
1340 InitializeListHead(&Stage->FreeSegments);
1341 InitializeListHead(&Stage->UsedSegments);
1342
1343 if (Ring->m_Type.Isoch.m_CurrentStage == Stage)
1344 Ring->m_Type.Isoch.m_CurrentStage = NULL;
1345
1346 /* Only the oldest or the newest stage of a transfer ever goes */
1347 if (Stage == XhciIsochStageAt(Transfer, 0))
1348 {
1349 Transfer->StageHead = (Transfer->StageHead + 1) % XhciIsochTransfer::StageSlots;
1350 }
1351 else
1352 {
1353 NT_ASSERT(Stage == XhciIsochStageAt(Transfer, Transfer->StageCount - 1));
1354 }
1355 Transfer->StageCount--;
1356
1357 Stage->InUse = FALSE;
1358 Stage->OnHardware = FALSE;
1359}
1360
1362VOID
1363XhciIsochRing::PutScatterGather(
1364 _In_ XhciTransferRing* Ring,
1366{
1367 PDMA_ADAPTER Adapter;
1368 KIRQL OldIrql;
1369
1370 if (SgList == NULL)
1371 return;
1372
1373 /* Never under the ring lock: the HAL may run another DMA callback of this ring */
1374 Adapter = Ring->m_Controller->m_Buffers.DmaAdapter();
1376 Adapter->DmaOperations->PutScatterGatherList(Adapter, SgList, !XhciIsochEndpointIn(Ring));
1378}
1379
1381BOOLEAN
1383 _In_ const XhciIsochTransfer* Transfer)
1384{
1385 return Transfer->StageCount == 0 &&
1386 Transfer->TdsRetired == Transfer->TdsQueued &&
1387 Transfer->PacketsQueued + Transfer->PacketsDropped >= Transfer->PacketsInUrb;
1388}
1389
1390/* Stage preparation *********************************************************/
1391
1394 _In_ XhciTransferRing* Ring,
1395 _Inout_ XhciIsochStage* Stage)
1396{
1397 XhciIsochRing* Self = &Ring->m_Type.Isoch;
1398 XhciIsochTransfer* Transfer = Stage->Transfer;
1399 ULONG Frame;
1400 ULONG Segments;
1401 BOOLEAN Gap = FALSE;
1403 KIRQL OldIrql;
1404
1405 Stage->FirstPacket = Transfer->PacketsQueued + Transfer->PacketsDropped;
1406 Frame = Transfer->StartFrame + XhciIsochFrames(Stage->FirstPacket, Self->m_PacketsPerFrame);
1407
1408 /* Without contiguous Frame IDs the controller must run dry before a jump (xHCI 4.11.2.5) */
1409 if (!Ring->m_Controller->m_Registers.ContiguousFrameId())
1410 {
1411 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
1412 if ((Self->m_Flags & XhciIsochLastFrameValid) &&
1413 Frame != Self->m_LastScheduledFrame + 1 &&
1414 ((Self->m_Flags & XhciIsochEmptyExpected) || Self->m_PendingStages != 0))
1415 {
1416 FreeStage(Ring, Stage);
1417 Gap = TRUE;
1418 }
1419 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1420
1421 if (Gap)
1422 {
1423 DPRINT("Isoch DCI %lu: frame 0x%lx does not follow 0x%lx, waiting for the ring to drain\n",
1424 Ring->m_Endpoint->Dci(), Frame, Self->m_LastScheduledFrame);
1425 return Outcome::Gap;
1426 }
1427 }
1428
1429 if (!NT_SUCCESS(AcquireStageMdl(Ring, Stage)))
1430 return FailStage(Ring, Stage);
1431
1432 if (!SizeStage(Ring, Stage))
1433 return FailStage(Ring, Stage);
1434
1435 Segments = EstimateSegments(Ring, Stage);
1436 if (Segments == 0)
1437 return Outcome::Ready;
1438
1439 Status = Ring->TakeSegments(Segments, &Stage->FreeSegments);
1440 if (Status == STATUS_PENDING)
1441 {
1442 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
1443 FreeStage(Ring, Stage);
1444 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1445
1446 if (ParkOnSegments(Ring))
1447 return Outcome::WaitSegments;
1448
1449 /* The segments already arrived; take the stage again from the top */
1450 return Outcome::Completed;
1451 }
1452
1453 if (!NT_SUCCESS(Status))
1454 {
1455 DPRINT1("Isoch DCI %lu: %lu segments not available, 0x%08lx\n",
1456 Ring->m_Endpoint->Dci(), Segments, Status);
1457 return FailStage(Ring, Stage);
1458 }
1459
1460 Self->m_BufferRetries = 0;
1461 return Outcome::Ready;
1462}
1463
1466 _In_ XhciTransferRing* Ring,
1467 _Inout_ XhciIsochStage* Stage)
1468{
1469 XhciIsochTransfer* Transfer = Stage->Transfer;
1470 PMDL Mdl = Transfer->Mdl;
1471 ULONG Offset = Transfer->BytesQueued + Transfer->BytesDropped;
1472 PUCHAR Start;
1473 ULONG Length;
1474 PMDL Partial;
1475
1476 while (Mdl != NULL && Offset >= MmGetMdlByteCount(Mdl))
1477 {
1479 Mdl = Mdl->Next;
1480 }
1481
1482 if (Mdl == NULL)
1483 {
1484 DPRINT1("Isoch DCI %lu: MDL chain shorter than the transfer\n", Ring->m_Endpoint->Dci());
1486 }
1487
1488 if (Offset == 0)
1489 {
1490 Stage->Mdl = Mdl;
1491 return STATUS_SUCCESS;
1492 }
1493
1494 Start = static_cast<PUCHAR>(MmGetMdlVirtualAddress(Mdl)) + Offset;
1496
1497 Partial = IoAllocateMdl(Start, Length, FALSE, FALSE, NULL);
1498 if (Partial == NULL)
1499 {
1500 DPRINT1("Isoch DCI %lu: partial MDL allocation failed\n", Ring->m_Endpoint->Dci());
1502 }
1503
1504 IoBuildPartialMdl(Mdl, Partial, Start, Length);
1505 Partial->Next = Mdl->Next;
1506 Stage->Mdl = Partial;
1507 Stage->FreeMdl = TRUE;
1508 return STATUS_SUCCESS;
1509}
1510
1512BOOLEAN
1514 _In_ XhciTransferRing* Ring,
1515 _Inout_ XhciIsochStage* Stage)
1516{
1517 XhciIsochTransfer* Transfer = Stage->Transfer;
1518 ULONG PerFrame = Ring->m_Type.Isoch.m_PacketsPerFrame;
1519 ULONG Used = Transfer->BytesQueued + Transfer->BytesDropped;
1520 ULONG Remaining = (Transfer->BytesTotal > Used) ? Transfer->BytesTotal - Used : 0;
1521 ULONG Limit = Ring->m_MaxStageSize;
1522 ULONG Registers = 0;
1523 PDMA_ADAPTER Adapter;
1525 ULONG Packet;
1526 ULONG Total = 0;
1527 PMDL Mdl;
1528 ULONG Left;
1529
1530 Adapter = Ring->m_Controller->m_Buffers.DmaAdapter();
1531 if (Adapter->Version >= DEVICE_DESCRIPTION_VERSION3 &&
1532 Adapter->DmaOperations->Size > FIELD_OFFSET(DMA_OPERATIONS, GetDmaTransferInfo) &&
1533 Adapter->DmaOperations->GetDmaTransferInfo != NULL)
1534 {
1535 RtlZeroMemory(&Info, sizeof(Info));
1537 if (NT_SUCCESS(Adapter->DmaOperations->GetDmaTransferInfo(Adapter,
1538 Stage->Mdl,
1539 0,
1540 Remaining,
1541 !XhciIsochEndpointIn(Ring),
1542 &Info)))
1543 {
1544 Registers = Info.V1.MapRegisterCount;
1545 }
1546 }
1547
1548 if (Registers == 0)
1549 {
1550 /* Without the HAL query count the pages the chain spans */
1551 for (Mdl = Stage->Mdl, Left = Remaining; Mdl != NULL && Left != 0; Mdl = Mdl->Next)
1552 {
1553 ULONG Chunk = min(Left, MmGetMdlByteCount(Mdl));
1554
1556 Left -= Chunk;
1557 }
1558 }
1559
1560 if (Registers > Ring->m_MapRegisterCount && Ring->m_MapRegisterCount > 1)
1561 Limit = min(Limit, (Ring->m_MapRegisterCount - 1) * PAGE_SIZE);
1562
1563 if (Remaining <= Limit)
1564 {
1565 Stage->LastPacket = Transfer->PacketsInUrb - 1;
1566 Stage->Size = Remaining;
1567 return TRUE;
1568 }
1569
1570 /* Whole frames only */
1571 Packet = Stage->FirstPacket;
1572 while (Packet < Transfer->PacketsInUrb)
1573 {
1574 ULONG FrameBytes = 0;
1575 ULONG Count;
1576
1577 for (Count = 0; Count < PerFrame && Packet + Count < Transfer->PacketsInUrb; Count++)
1578 FrameBytes += XhciIsochPacketLength(Transfer, Packet + Count);
1579
1580 if (Total + FrameBytes > Limit)
1581 break;
1582
1583 Total += FrameBytes;
1584 Packet += Count;
1585 }
1586
1587 /* Fail instead of building an empty stage when one frame exceeds the limit */
1588 if (Packet == Stage->FirstPacket)
1589 {
1590 DPRINT1("Isoch DCI %lu: one frame needs more than %lu bytes of mapping\n",
1591 Ring->m_Endpoint->Dci(), Limit);
1592 return FALSE;
1593 }
1594
1595 Stage->LastPacket = Packet - 1;
1596 Stage->Size = Total;
1597 return TRUE;
1598}
1599
1600ULONG
1602 _In_ XhciTransferRing* Ring,
1603 _In_ const XhciIsochStage* Stage)
1604{
1605 XhciIsochRing* Self = &Ring->m_Type.Isoch;
1606 BOOLEAN Guard = XhciIsochNeedsLinkGuard(Ring);
1607 ULONG PerTd;
1608 ULONG Needed;
1609 ULONG Available;
1610 ULONG Usable;
1611 ULONG Segments = 0;
1612
1613 PerTd = 1 + ((XhciIsochEsitPayload(Ring) + 2 * PAGE_SIZE - 2) >> PAGE_SHIFT);
1614 if (Guard)
1615 PerTd += 1;
1616 if (MmGetMdlByteCount(Stage->Mdl) < Stage->Size)
1617 PerTd += 2;
1618
1619 /* A TD never needs more than one segment */
1620 if (PerTd > Ring->m_LastIndex)
1621 PerTd = Ring->m_LastIndex;
1622
1623 Self->m_TrbsPerTd = PerTd;
1624
1625 Needed = PerTd * (Stage->LastPacket - Stage->FirstPacket + 1);
1626 Available = Ring->m_LastIndex - Ring->m_EnqueueIndex;
1627 Available -= Available % PerTd;
1628
1629 if (Needed > Available)
1630 {
1631 Usable = Ring->m_LastIndex - (Ring->m_LastIndex % PerTd);
1632 Segments = (Needed - Available + Usable - 1) / Usable;
1633 }
1634
1635 if (Guard)
1636 Segments++;
1637
1638 return Segments;
1639}
1640
1644 _In_ XhciTransferRing* Ring,
1645 _Inout_ XhciIsochStage* Stage)
1646{
1647 XhciIsochTransfer* Transfer = Stage->Transfer;
1648 PSCATTER_GATHER_LIST SgList = Stage->SgList;
1650 KIRQL OldIrql;
1651
1652 Stage->SgList = NULL;
1653 PutScatterGather(Ring, SgList);
1654
1655 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
1656 FreeStage(Ring, Stage);
1657
1658 if (Transfer->TdsRetired == Transfer->TdsQueued)
1659 {
1662 }
1663 else
1664 {
1665 /* The request completes when its last mapped stage comes back */
1666 SkipUnmapped(Transfer);
1668 }
1669
1670 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1671 return Result;
1672}
1673
1676 _In_ XhciTransferRing* Ring,
1677 _Inout_ XhciIsochStage* Stage)
1678{
1679 XhciIsochRing* Self = &Ring->m_Type.Isoch;
1680 PDMA_ADAPTER Adapter = Ring->m_Controller->m_Buffers.DmaAdapter();
1682 KIRQL OldIrql;
1683
1685
1687 Status = Adapter->DmaOperations->GetScatterGatherList(Adapter,
1688 WdfDeviceWdmGetDeviceObject(Ring->m_Controller->m_Device),
1689 Stage->Mdl,
1690 MmGetMdlVirtualAddress(Stage->Mdl),
1691 Stage->Size,
1693 Stage,
1694 !XhciIsochEndpointIn(Ring));
1696
1697 if (!NT_SUCCESS(Status))
1698 {
1699 DPRINT1("Isoch DCI %lu: GetScatterGatherList for %lu bytes failed, 0x%08lx\n",
1700 Ring->m_Endpoint->Dci(), Stage->Size, Status);
1701 (VOID)FailStage(Ring, Stage);
1702 return Outcome::Ready;
1703 }
1704
1705 /* Whichever of this context and the callback gets here second keeps mapping */
1706 if (InterlockedXor(&Self->m_FillAgain, 1) == 1)
1707 return (Self->m_LateStage != 0) ? Outcome::Late : Outcome::Ready;
1708
1709 return Outcome::WaitDma;
1710}
1711
1712VOID
1713NTAPI
1716 _In_ PIRP Irp,
1719{
1720 XhciIsochStage* Stage = static_cast<XhciIsochStage*>(Context);
1721 XhciTransferRing* Ring = Stage->Transfer->Ring;
1722 XhciIsochRing* Self = &Ring->m_Type.Isoch;
1723
1726
1727 Stage->SgList = ScatterGather;
1728 WriteStage(Ring, Stage);
1729
1730 if (InterlockedXor(&Self->m_FillAgain, 1) != 1)
1731 return;
1732
1733 if (Self->m_LateStage != 0)
1734 {
1735 AwaitRingEmpty(Ring);
1736 return;
1737 }
1738
1739 if (Ring->ChangeMapState(FALSE, XhciMapState::Filling, XhciMapState::Filling) == XhciMapState::Filling)
1740 BuildTdsFromQueue(Ring);
1741}
1742
1743/* Ring writing **************************************************************/
1744
1746VOID
1748 _In_ XhciTransferRing* Ring,
1749 _Inout_ XhciIsochStage* Stage)
1750{
1751 XhciIsochRing* Self = &Ring->m_Type.Isoch;
1752 XhciIsochTransfer* Transfer = Stage->Transfer;
1754 ULONG Frame;
1755 ULONG Now;
1756 ULONG Fit;
1757 ULONG Packet;
1758 ULONG Size = 0;
1759 KIRQL OldIrql;
1760
1761 if (Ring->m_Controller->HasErrata(XhciErrata::IsochSkipPastFrames))
1762 {
1763 Frame = Transfer->StartFrame + Stage->FirstPacket / Self->m_PacketsPerFrame;
1764 Now = Ring->m_Controller->GetFrameNumber(XhciIsochBiasLate);
1765
1766 if ((LONG)(Now - Frame) >= 0)
1767 {
1768 DPRINT1("Isoch DCI %lu: stage for frame 0x%lx is late at 0x%lx\n",
1769 Ring->m_Endpoint->Dci(), Frame, Now);
1770
1771 SgList = Stage->SgList;
1772 Stage->SgList = NULL;
1773 PutScatterGather(Ring, SgList);
1774
1775 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
1776 FreeStage(Ring, Stage);
1778 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1779
1780 /* The controller runs off the end and reports the ring empty */
1781 Ring->RingDoorbell();
1783 return;
1784 }
1785 }
1786
1787 Fit = LayoutStage(Ring, Stage, FALSE);
1788 if (Fit == 0)
1789 {
1790 DPRINT1("Isoch DCI %lu: packet %lu does not fit the reserved segments\n",
1791 Ring->m_Endpoint->Dci(), Stage->FirstPacket);
1792 (VOID)FailStage(Ring, Stage);
1793 return;
1794 }
1795
1796 /* End the stage at the last TD that fits in the segment */
1797 if (Stage->FirstPacket + Fit - 1 != Stage->LastPacket)
1798 {
1799 DPRINT1("Isoch DCI %lu: stage cut after %lu of %lu packets\n",
1800 Ring->m_Endpoint->Dci(), Fit, Stage->LastPacket - Stage->FirstPacket + 1);
1801
1802 for (Packet = Stage->FirstPacket; Packet < Stage->FirstPacket + Fit; Packet++)
1803 Size += XhciIsochPacketLength(Transfer, Packet);
1804
1805 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
1806 Stage->LastPacket = Stage->FirstPacket + Fit - 1;
1807 Stage->Size = Size;
1808 Stage->Truncated = TRUE;
1809 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1810 }
1811
1812 (VOID)LayoutStage(Ring, Stage, TRUE);
1813
1814 /* The stage may be gone already; only the ring is used from here */
1815 Ring->RingDoorbell();
1816}
1817
1822ULONG
1824 _In_ XhciTransferRing* Ring,
1825 _Inout_ XhciIsochStage* Stage,
1826 _In_ BOOLEAN Commit)
1827{
1828 XhciIsochRing* Self = &Ring->m_Type.Isoch;
1829 XhciIsochTransfer* Transfer = Stage->Transfer;
1830 const SCATTER_GATHER_LIST* SgList = Stage->SgList;
1831 ULONG LastSlot = Ring->m_LastIndex;
1832 ULONG LastPacket = Stage->LastPacket;
1833 ULONG PerFrame = Self->m_PacketsPerFrame;
1834 BOOLEAN Guard = XhciIsochNeedsLinkGuard(Ring);
1835 BOOLEAN AllowBei = !Ring->m_Controller->HasErrata(XhciErrata::IsochInterruptEveryTd);
1836 ULONG Hcc2 = Ring->m_Controller->m_Registers.Hccparams2();
1837 BOOLEAN ExtendedTbc = (Hcc2 & XHCI_HCC2_LEC) && (Hcc2 & XHCI_HCC2_ETC);
1838 ULONG Esit = XhciIsochEsitPayload(Ring);
1839 ULONG MaxPacket = Ring->m_Endpoint->MaxPacketSize();
1840 ULONG BurstSize = Ring->m_Endpoint->MaxBurst() + 1;
1841 ULONG Target = (Ring->m_InterrupterTarget & XHCI_TRB_INTERRUPTER_MASK) << XHCI_TRB_INTERRUPTER_SHIFT;
1842 ULONG Spares = XhciIsochCountList(&Stage->FreeSegments);
1843 ULONG Index = Ring->m_EnqueueIndex;
1844 ULONG Element = 0;
1845 ULONG Offset = 0;
1846 ULONG Placed = 0;
1847 ULONG Fit = 0;
1848 ULONG Packet;
1849 KIRQL OldIrql;
1850
1851 if (Commit)
1852 {
1853 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
1854 Stage->FirstSegment = Ring->m_Segment;
1855 Stage->LastSegment = Ring->m_Segment;
1856 Stage->ScanSegment = Ring->m_Segment;
1857 Stage->FirstIndex = Ring->m_EnqueueIndex;
1858 Stage->EndIndex = Ring->m_EnqueueIndex;
1859 Stage->ScanIndex = Ring->m_EnqueueIndex;
1860 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
1861 }
1862
1863 for (Packet = Stage->FirstPacket; Packet <= LastPacket; Packet++)
1864 {
1866 ULONG Carried = min(Length, Esit);
1867 ULONG Pieces = XhciIsochCountPieces(SgList, Element, Offset, Carried);
1868 BOOLEAN IsLast = (Packet == LastPacket);
1869 ULONG Frame = Transfer->StartFrame + Packet / PerFrame;
1871 volatile UCHAR* CycleByte;
1872 BOOLEAN Tail;
1873 BOOLEAN Bei;
1874 XHCI_TRB Trb;
1875 ULONG Tdpc;
1876 ULONG Tbc;
1877 ULONG Tlbpc;
1878 ULONG Mapped = 0;
1879 ULONG Piece;
1880 ULONG64 Value;
1881
1882 if (Commit)
1883 Index = Ring->m_EnqueueIndex;
1884
1885 /* The data TRBs and the Event Data TRB of a TD stay in front of the Link slot */
1886 if (Index + Pieces + 1 > LastSlot)
1887 {
1888 if (!Commit)
1889 {
1890 if (Spares == 0 || Pieces + 1 > LastSlot)
1891 break;
1892 Spares--;
1893 }
1894 else
1895 {
1896 First = &Ring->m_Trbs[Ring->m_EnqueueIndex];
1897 InsertLink(Ring, Stage, TRUE);
1898 }
1899 Index = 0;
1900 }
1901
1902 /* A full speed TD behind a TT ends in a new segment instead of meeting a Link first */
1903 Tail = Guard && (IsLast || LastSlot - (Index + Pieces) < Self->m_TrbsPerTd);
1904
1905 if (!Commit)
1906 {
1907 if (!Guard || Spares != 0)
1908 Fit = Placed + 1;
1909
1910 if (Tail)
1911 {
1912 if (Spares == 0)
1913 break;
1914 Spares--;
1915 Index = 1;
1916 }
1917 else
1918 {
1919 Index += Pieces + 1;
1920 }
1921
1922 Placed++;
1923 XhciIsochAdvance(SgList, &Element, &Offset, Length);
1924 continue;
1925 }
1926
1927 /* Burst fields (xHCI 4.11.2.3) */
1928 Tdpc = (MaxPacket != 0) ? (Carried + MaxPacket - 1) / MaxPacket : 1;
1929 if (Tdpc == 0)
1930 Tdpc = 1;
1931 Tbc = (Tdpc + BurstSize - 1) / BurstSize - 1;
1932 Tlbpc = (Tdpc % BurstSize) ? (Tdpc % BurstSize) - 1 : BurstSize - 1;
1933
1934 if (First == NULL)
1935 First = &Ring->m_Trbs[Ring->m_EnqueueIndex];
1936
1937 for (Piece = 0; Piece < Pieces; Piece++)
1938 {
1939 BOOLEAN LastData = (Piece + 1 == Pieces);
1940 PXHCI_TRB Slot = &Ring->m_Trbs[Ring->m_EnqueueIndex];
1942 ULONG Chunk;
1943 ULONG SizeField;
1944
1945 /* QUIRK: bytes beyond the max ESIT payload of a packet are never sent */
1946 XhciIsochTakePiece(SgList, &Element, &Offset, Carried - Mapped, &Address, &Chunk);
1947 Mapped += Chunk;
1948
1949 RtlZeroMemory(&Trb, sizeof(Trb));
1950 Trb.Dword[0] = (ULONG)Address;
1951 Trb.Dword[1] = (ULONG)(Address >> 32);
1952
1953 if (Piece == 0 && ExtendedTbc)
1954 SizeField = Tbc;
1955 else
1956 SizeField = Ring->TdSize(Ring->PacketCount(Carried), Mapped, LastData);
1957
1958 Trb.Dword[2] = Chunk | (SizeField << XHCI_TRB_TD_SIZE_SHIFT) | Target;
1959 Trb.Dword[3] = XHCI_TRB_CHAIN | (LastData ? XHCI_TRB_ENT : 0);
1960
1961 if (Piece == 0)
1962 {
1963 /* QUIRK: every TD carries its Frame ID, SIA is never used */
1964 Trb.Dword[3] |= (static_cast<ULONG>(XhciTrbType::Isoch) << XHCI_TRB_TYPE_SHIFT) |
1965 (Tlbpc << XHCI_ISOCH_TLBPC_SHIFT) |
1967 if (!ExtendedTbc)
1968 Trb.Dword[3] |= Tbc << XHCI_ISOCH_TBC_SHIFT;
1969 }
1970 else
1971 {
1972 Trb.Dword[3] |= static_cast<ULONG>(XhciTrbType::Normal) << XHCI_TRB_TYPE_SHIFT;
1973 }
1974
1975 /* The first slot of the TD stays invalid until the whole TD is written */
1976 Trb.Dword[3] |= (Slot == First) ? (Ring->m_Cycle ^ XHCI_TRB_CYCLE) : Ring->m_Cycle;
1977
1978 *Slot = Trb;
1979 Ring->m_EnqueueIndex++;
1980 }
1981
1982 XhciIsochAdvance(SgList, &Element, &Offset, Length - Mapped);
1983
1984 if (Tail)
1985 {
1986 RtlZeroMemory(&Trb, sizeof(Trb));
1987 Trb.Dword[3] = (static_cast<ULONG>(XhciTrbType::Normal) << XHCI_TRB_TYPE_SHIFT) |
1988 XHCI_TRB_CHAIN | XHCI_TRB_ENT | Ring->m_Cycle;
1989 Ring->m_Trbs[Ring->m_EnqueueIndex] = Trb;
1990 Ring->m_EnqueueIndex++;
1991
1992 InsertLink(Ring, Stage, FALSE);
1993 }
1994
1995 /* An interrupt at least every 128 packets and at every stage end */
1996 Bei = AllowBei &&
1997 !IsLast &&
1998 Packet + 1 != Transfer->PacketsInUrb &&
1999 ((Packet + 1) & XhciIsochInterruptEvery) != 0;
2000
2001 Value = USB_ENDPOINT_TYPE_ISOCHRONOUS | ((ULONG64)Packet << 16) | ((ULONG64)Transfer->StartFrame << 32);
2002 RtlZeroMemory(&Trb, sizeof(Trb));
2003 Trb.Dword[0] = (ULONG)Value;
2004 Trb.Dword[1] = (ULONG)(Value >> 32);
2005 Trb.Dword[2] = Target;
2006 Trb.Dword[3] = (static_cast<ULONG>(XhciTrbType::EventData) << XHCI_TRB_TYPE_SHIFT) |
2007 XHCI_TRB_IOC | (Bei ? XHCI_TRB_BEI : 0) | Ring->m_Cycle;
2008 Ring->m_Trbs[Ring->m_EnqueueIndex] = Trb;
2009 Ring->m_EnqueueIndex++;
2010
2011 /* Recycled segments are not cleared, and a stale TRB could look valid */
2012 Stage->EndIndex = Ring->m_EnqueueIndex;
2013 RtlZeroMemory(&Ring->m_Trbs[Ring->m_EnqueueIndex], sizeof(XHCI_TRB));
2014
2015 if (IsLast || Self->m_DoorbellAfterFirstTd)
2016 {
2017 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2018 if (IsLast)
2019 {
2020 Transfer->PacketsQueued = Packet + 1 - Transfer->PacketsDropped;
2021 Transfer->TdsQueued++;
2022 Transfer->BytesQueued += Stage->Size;
2023 Stage->OnHardware = TRUE;
2024 Self->m_PendingStages++;
2025 Self->m_LastScheduledFrame = Frame;
2027
2028 /* A cut stage leaves the rest of the URB to skip */
2029 if (Stage->Truncated)
2030 SkipUnmapped(Transfer);
2031 }
2033 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2034 }
2035
2036 /* A one byte register write publishes the TD after every store above */
2038 CycleByte = reinterpret_cast<volatile UCHAR*>(&First->Dword[3]);
2039 WRITE_REGISTER_UCHAR(const_cast<PUCHAR>(CycleByte), (UCHAR)(*CycleByte ^ XHCI_TRB_CYCLE));
2041
2042 if (Self->m_DoorbellAfterFirstTd)
2043 {
2045 Ring->RingDoorbell();
2046 }
2047
2048 Placed++;
2049 }
2050
2051 return Commit ? Placed : Fit;
2052}
2053
2055VOID
2057 _In_ XhciTransferRing* Ring,
2058 _Inout_ XhciIsochStage* Stage,
2059 _In_ BOOLEAN FirstOfTd)
2060{
2062 XHCI_TRB Trb;
2063 KIRQL OldIrql;
2064
2065 /* The layout pass made sure a reserved segment is there */
2066 NT_ASSERT(!IsListEmpty(&Stage->FreeSegments));
2067
2068 Next = CONTAINING_RECORD(RemoveHeadList(&Stage->FreeSegments), XhciDmaBuffer, Link);
2069 XhciClearListEntry(&Next->Link);
2070
2071 Ring->BuildLinkTrb(&Trb, FirstOfTd, !FirstOfTd);
2072 Trb.Dword[0] = Next->LogicalAddress.LowPart;
2073 Trb.Dword[1] = (ULONG)Next->LogicalAddress.HighPart;
2074 Ring->m_Trbs[Ring->m_EnqueueIndex] = Trb;
2075
2076 /* Never Toggle Cycle: the ring is an endless forward chain of fresh segments */
2077 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2078 InsertTailList(&Stage->UsedSegments, &Ring->m_Segment->Link);
2079 Ring->m_Segment = Next;
2080 Ring->m_Trbs = static_cast<PXHCI_TRB>(Next->VirtualAddress);
2081 Ring->m_EnqueueIndex = 0;
2082 Stage->LastSegment = Next;
2083 Stage->EndIndex = 0;
2084 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2085}
2086
2087/* Completion ****************************************************************/
2088
2090VOID
2092 _In_ XhciTransferRing* Ring,
2093 _In_ XhciIsochTransfer* Transfer,
2094 _In_ XhciIsochStage* Stage,
2095 _In_ ULONG Sequence,
2096 _In_ ULONG Code,
2098 _In_ BOOLEAN CloseStage)
2099{
2100 XhciIsochRing* Self = &Ring->m_Type.Isoch;
2101 struct _URB_ISOCH_TRANSFER* Urb;
2103 ULONG Packet;
2104 ULONG Index;
2105 ULONG InFlight;
2106 KIRQL OldIrql;
2107
2108 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2109
2110 /* A reclaim or a stale pass may have taken the stage meanwhile */
2111 if (!XhciIsochIsActive(&Self->m_Pending, Transfer, Sequence) ||
2112 !Stage->InUse ||
2113 Stage->Transfer != Transfer)
2114 {
2115 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2116 return;
2117 }
2118
2119 Urb = Transfer->Urb;
2120 Packet = Stage->NextPacket - 1;
2121 if (Packet >= Transfer->PacketsInUrb)
2122 {
2123 DPRINT1("Isoch DCI %lu: event for packet %lu of %lu\n",
2124 Ring->m_Endpoint->Dci(), Packet, Transfer->PacketsInUrb);
2125 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2126 return;
2127 }
2128
2129 /* TDs the controller passed without a Missed Service event */
2130 for (Index = Packet; Index > 0 && Urb->IsoPacket[Index - 1].Status == XhciIsochNotSet; Index--)
2131 {
2132 DPRINT1("Isoch DCI %lu: packet %lu was skipped by the controller\n",
2133 Ring->m_Endpoint->Dci(), Index - 1);
2134 Urb->IsoPacket[Index - 1].Status = USBD_STATUS_ISO_TD_ERROR;
2135 }
2136
2137 if (Urb->IsoPacket[Packet].Status == XhciIsochNotSet && (!XhciIsochStoppedCode(Code) || Bytes != 0))
2138 {
2140 Transfer->BytesTransferred += Bytes;
2141 if (XhciIsochEndpointIn(Ring))
2142 Urb->IsoPacket[Packet].Length = Bytes;
2143 }
2144
2145 if (Packet != Stage->LastPacket || !CloseStage)
2146 {
2147 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2148 return;
2149 }
2150
2151 if (!Stage->OnHardware)
2152 {
2153 DPRINT1("Isoch DCI %lu: end of a stage still being written was reported\n", Ring->m_Endpoint->Dci());
2154 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2155 return;
2156 }
2157
2158 SgList = Stage->SgList;
2159 Stage->SgList = NULL;
2160 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2161
2162 PutScatterGather(Ring, SgList);
2163
2164 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2165 if (!XhciIsochIsActive(&Self->m_Pending, Transfer, Sequence) || !Stage->InUse)
2166 {
2167 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2168 return;
2169 }
2170
2171 FreeStage(Ring, Stage);
2172 Transfer->TdsRetired++;
2173 if (Self->m_PendingStages != 0)
2174 Self->m_PendingStages--;
2175 InFlight = Self->m_PendingStages;
2176
2177 /* Complete only once no stage is in use and every packet was queued or dropped */
2178 if (TransferDone(Transfer))
2180
2181 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2182
2183 KickAfterStage(Ring, InFlight);
2184}
2185
2187VOID
2189 _In_ XhciTransferRing* Ring,
2190 _In_ const XhciIsochTransfer* Newest,
2191 _In_ ULONG Sequence)
2192{
2193 XhciIsochRing* Self = &Ring->m_Type.Isoch;
2194 XhciIsochTransfer* Transfer;
2195 LIST_ENTRY Stale;
2196 KIRQL OldIrql;
2197
2198 InitializeListHead(&Stale);
2199
2200 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2201 if (XhciIsochIsActive(&Self->m_Pending, Newest, Sequence))
2202 {
2203 while (Self->m_Pending.Flink != &Newest->Link)
2204 InsertTailList(&Stale, RemoveHeadList(&Self->m_Pending));
2205 }
2206 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2207
2208 while (!IsListEmpty(&Stale))
2209 {
2211 XhciClearListEntry(&Transfer->Link);
2212
2213 DPRINT("Isoch DCI %lu: completing stale request %p\n", Ring->m_Endpoint->Dci(), Transfer->Request);
2214 ReleaseAllStages(Ring, Transfer);
2215
2216 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2218 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2219 }
2220}
2221
2223VOID
2225 _In_ XhciTransferRing* Ring,
2226 _In_ XhciIsochTransfer* Transfer,
2227 _In_ XhciIsochStage* Stage,
2228 _In_ ULONG Sequence)
2229{
2230 XhciIsochRing* Self = &Ring->m_Type.Isoch;
2231 XhciIsochStage* Oldest;
2232 BOOLEAN Progress;
2233 KIRQL OldIrql;
2234
2235 for (;;)
2236 {
2237 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2238 if (!XhciIsochIsActive(&Self->m_Pending, Transfer, Sequence) ||
2239 !Stage->InUse ||
2240 Transfer->StageCount == 0)
2241 {
2242 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2243 return;
2244 }
2245
2246 Oldest = XhciIsochStageAt(Transfer, 0);
2247 if (Oldest == Stage)
2248 {
2249 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2250 return;
2251 }
2252
2253 Oldest->NextPacket = Oldest->LastPacket + 1;
2254 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2255
2256 CompleteTd(Ring, Transfer, Oldest, Sequence, static_cast<ULONG>(XhciCompletionCode::MissedService), 0, TRUE);
2257
2258 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2259 Progress = !Oldest->InUse || XhciIsochStageAt(Transfer, 0) != Oldest;
2260 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2261
2262 if (!Progress)
2263 return;
2264 }
2265}
2266
2268ULONG
2270 _In_ XhciTransferRing* Ring,
2271 _Inout_ XhciIsochTransfer* Transfer)
2272{
2273 XhciIsochRing* Self = &Ring->m_Type.Isoch;
2274 XhciIsochStage* Stage;
2276 ULONG Freed = 0;
2277 KIRQL OldIrql;
2278
2279 for (;;)
2280 {
2281 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2282 if (Transfer->StageCount == 0)
2283 {
2284 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2285 break;
2286 }
2287
2288 Stage = XhciIsochStageAt(Transfer, 0);
2289 SgList = Stage->SgList;
2290 Stage->SgList = NULL;
2291 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2292
2293 PutScatterGather(Ring, SgList);
2294
2295 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2296 if (Stage->OnHardware)
2297 {
2298 Freed++;
2299 Transfer->TdsRetired++;
2300 if (Self->m_PendingStages != 0)
2301 Self->m_PendingStages--;
2302 }
2303 FreeStage(Ring, Stage);
2304 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2305 }
2306
2307 return Freed;
2308}
2309
2311VOID
2313 _In_ XhciTransferRing* Ring,
2314 _Inout_ XhciIsochTransfer* Transfer,
2315 _In_ USBD_STATUS PacketStatus,
2316 _In_ USBD_STATUS HeaderStatus)
2317{
2318 XhciIsochRing* Self = &Ring->m_Type.Isoch;
2319 struct _URB_ISOCH_TRANSFER* Urb = Transfer->Urb;
2321 ULONG Index;
2322
2323 NT_ASSERT(Transfer->StageCount == 0);
2324
2325 Urb->TransferBufferLength = Transfer->BytesTransferred;
2326
2327 for (Index = 0; Index < Transfer->PacketsInUrb; Index++)
2328 {
2329 if (PacketStatus != XhciIsochNotSet)
2330 Urb->IsoPacket[Index].Status = PacketStatus;
2331 else if (Urb->IsoPacket[Index].Status == XhciIsochNotSet)
2332 Urb->IsoPacket[Index].Status = USBD_STATUS_ISO_NOT_ACCESSED_BY_HW;
2333
2334 /* QUIRK: added to whatever the URB held, as the USB 2.0 stack does */
2335 if (Urb->IsoPacket[Index].Status != USBD_STATUS_SUCCESS)
2336 {
2337 Urb->ErrorCount++;
2338 Self->m_TdFailures++;
2339 }
2340 Self->m_Tds++;
2341 }
2342
2343 if (HeaderStatus != XhciIsochNotSet)
2344 Status = HeaderStatus;
2345 else if (Urb->ErrorCount == Transfer->PacketsInUrb)
2347 else
2349
2350 Urb->Hdr.Status = Status;
2352
2353 Self->m_Transfers++;
2354 Self->m_Bytes += Transfer->BytesTransferred;
2355 if (!NT_SUCCESS(Transfer->Status))
2356 {
2357 Self->m_FailedTransfers++;
2358
2359 if (Transfer->Status != STATUS_CANCELLED)
2360 {
2361 DPRINT1("Isoch DCI %lu request %p failed, 0x%08lx USBD 0x%08lx, %lu of %lu packets in error\n",
2362 Ring->m_Endpoint->Dci(), Transfer->Request, Transfer->Status, Status,
2363 Urb->ErrorCount, Transfer->PacketsInUrb);
2364 }
2365 }
2366
2367 if (Transfer->OwnMdl)
2368 {
2369 IoFreeMdl(Transfer->Mdl);
2370 Transfer->OwnMdl = FALSE;
2371 }
2372 Transfer->Mdl = NULL;
2373 Transfer->Initialized = 0;
2374}
2375
2378VOID
2379XhciIsochRing::CompletePlain(
2380 _In_ XhciTransferRing* Ring,
2381 _Inout_ XhciIsochTransfer* Transfer,
2382 _In_ USBD_STATUS PacketStatus,
2383 _In_ USBD_STATUS HeaderStatus,
2384 _In_ BOOLEAN Async)
2385{
2386 KIRQL OldIrql;
2387
2388 Finalize(Ring, Transfer, PacketStatus, HeaderStatus);
2389
2390 if (!Async)
2391 {
2392 WdfRequestComplete(Transfer->Request, Transfer->Status);
2393 return;
2394 }
2395
2396 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2397 InsertTailList(&Ring->m_Type.Isoch.m_Completion, &Transfer->Link);
2398 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2399
2400 WdfDpcEnqueue(Ring->m_CompletionDpc);
2401}
2402
2403_Requires_lock_held_(Ring->m_Lock)
2404VOID
2405XhciIsochRing::CompleteAfterUncancel(
2406 _In_ XhciTransferRing* Ring,
2407 _Inout_ XhciIsochTransfer* Transfer,
2408 _In_ USBD_STATUS PacketStatus,
2409 _In_ USBD_STATUS HeaderStatus,
2410 _In_ BOOLEAN FinishCanceled,
2411 _In_ BOOLEAN Async)
2412{
2413 XhciIsochRing* Self = &Ring->m_Type.Isoch;
2415
2416 switch (Transfer->CancelState)
2417 {
2419 break;
2420
2422 if (XhciIsListEntryLinked(&Transfer->Link))
2423 {
2424 RemoveEntryList(&Transfer->Link);
2425 XhciClearListEntry(&Transfer->Link);
2426 }
2427
2428 Status = WdfRequestUnmarkCancelable(Transfer->Request);
2429 if (!NT_SUCCESS(Status))
2430 {
2431 /* The cancel callback is about to run and finishes the job */
2432 Transfer->CancelState = XhciIsochCancel::CancelRoutinePending;
2433 InsertTailList(&Self->m_WaitingForCancel, &Transfer->Link);
2434 return;
2435 }
2436 Transfer->CancelState = XhciIsochCancel::Idle;
2437 break;
2438
2440 if (!FinishCanceled)
2441 return;
2442 if (HeaderStatus == XhciIsochNotSet)
2443 HeaderStatus = USBD_STATUS_CANCELED;
2444 break;
2445
2446 default:
2448 return;
2449 }
2450
2451 if (XhciIsListEntryLinked(&Transfer->Link))
2452 {
2453 RemoveEntryList(&Transfer->Link);
2454 XhciClearListEntry(&Transfer->Link);
2455 }
2456
2457 KeReleaseSpinLockFromDpcLevel(&Ring->m_Lock);
2458
2459 Finalize(Ring, Transfer, PacketStatus, HeaderStatus);
2460
2461 if (Async)
2462 {
2463 KeAcquireSpinLockAtDpcLevel(&Ring->m_Lock);
2464 InsertTailList(&Self->m_Completion, &Transfer->Link);
2465 KeReleaseSpinLockFromDpcLevel(&Ring->m_Lock);
2466 WdfDpcEnqueue(Ring->m_CompletionDpc);
2467 }
2468 else
2469 {
2470 WdfRequestComplete(Transfer->Request, Transfer->Status);
2471 }
2472
2473 KeAcquireSpinLockAtDpcLevel(&Ring->m_Lock);
2474}
2475
2476VOID
2478 _In_ XhciTransferRing* Ring,
2480{
2481 XhciIsochTransfer* Transfer;
2482 KIRQL OldIrql;
2483
2484 while (!IsListEmpty(Batch))
2485 {
2487 XhciClearListEntry(&Transfer->Link);
2488
2489 if (Transfer->Status != STATUS_PENDING)
2490 {
2491 WdfRequestComplete(Transfer->Request, Transfer->Status);
2492 continue;
2493 }
2494
2495 ReleaseAllStages(Ring, Transfer);
2496
2497 /* QUIRK: everything reclaimed reports canceled, canceled or not */
2498 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2500 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2501 }
2502}
2503
2504_Requires_lock_held_(Ring->m_Lock)
2505BOOLEAN
2506XhciIsochRing::OwnsRequests(
2508{
2509 const XhciIsochRing* Self = &Ring->m_Type.Isoch;
2510
2511 return !IsListEmpty(&Self->m_Pending) ||
2513 !IsListEmpty(&Self->m_Completion);
2514}
2515
2516VOID
2517NTAPI
2519 _In_ WDFREQUEST Request)
2520{
2521 XhciIsochTransfer* Transfer = &XhciGetRequestData(Request)->Isoch;
2522 XhciTransferRing* Ring = Transfer->Ring;
2523 XhciIsochRing* Self = &Ring->m_Type.Isoch;
2526 KIRQL OldIrql;
2527
2529 if (Transfer->CancelState == XhciIsochCancel::Armed)
2530 {
2531 if (Self->m_Flags & XhciIsochOkToReclaim)
2532 {
2534 Queue = TRUE;
2535 }
2536 else
2537 {
2539 Report = TRUE;
2540 }
2541 }
2543
2544 if (Report)
2546
2548 if (Transfer->CancelState == XhciIsochCancel::CancelRoutinePending)
2549 {
2550 RemoveEntryList(&Transfer->Link);
2551 InsertTailList(&Self->m_Completion, &Transfer->Link);
2552 Queue = TRUE;
2553 }
2554 Transfer->CancelState = XhciIsochCancel::Done;
2556
2557 if (Queue)
2558 WdfDpcEnqueue(Ring->m_CompletionDpc);
2559}
2560
2561/* Transfer events ***********************************************************/
2562
2563VOID
2565 _In_ XhciTransferRing* Ring,
2566 _In_ const XHCI_TRB* Event)
2567{
2568 XhciIsochRing* Self = &Ring->m_Type.Isoch;
2572 ULONG Packet = (ULONG)(Value >> 16) & 0xFFFF;
2573 ULONG Frame = (ULONG)(Value >> 32);
2574 XhciIsochTransfer* Transfer = NULL;
2575 XhciIsochStage* Stage = NULL;
2576 ULONG Sequence = 0;
2578 ULONG Index;
2579 KIRQL OldIrql;
2580
2582 {
2583 DPRINT1("Isoch DCI %lu: Event Data 0x%I64x is not an isoch tag, resetting\n",
2584 Ring->m_Endpoint->Dci(), Value);
2585 Ring->m_Controller->RaiseControllerFault(XhciRecovery::ResetHost, XhciIsochReasonBadEventData);
2586 return;
2587 }
2588
2589 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2590
2591 if (Self->m_Flags & XhciIsochStoppedSeen)
2592 {
2593 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2594 return;
2595 }
2596
2597 /* QUIRK: found by start frame, so of two URBs with one start frame the older one matches */
2598 for (Entry = Self->m_Pending.Flink; Entry != &Self->m_Pending; Entry = Entry->Flink)
2599 {
2601
2602 if (Candidate->StartFrame == Frame)
2603 {
2604 Transfer = Candidate;
2605 break;
2606 }
2607 }
2608
2609 if (Transfer != NULL)
2610 {
2611 Sequence = Transfer->Sequence;
2612
2613 for (Index = 0; Index < Transfer->StageCount; Index++)
2614 {
2615 XhciIsochStage* Candidate = XhciIsochStageAt(Transfer, Index);
2616
2617 if (Packet <= Candidate->LastPacket)
2618 {
2619 Stage = Candidate;
2620 break;
2621 }
2622 }
2623
2624 if (Stage == NULL)
2625 {
2626 DPRINT1("Isoch DCI %lu: no stage holds packet %lu of request %p\n",
2627 Ring->m_Endpoint->Dci(), Packet, Transfer->Request);
2628 }
2629 else if (Packet == 0 || Packet > Stage->NextPacket - 1)
2630 {
2631 Stage->NextPacket = Packet + 1;
2632 Transfer->PacketsRetired = Packet + 1;
2633 }
2634 }
2635
2637 {
2638 /* Some controllers report the stop on the Event Data TRB */
2640 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2641
2642 if (Stage != NULL)
2643 CompleteTd(Ring, Transfer, Stage, Sequence, Code, Bytes, FALSE);
2644
2645 Ring->m_Endpoint->OnStoppedEvent();
2646 return;
2647 }
2648
2649 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2650
2651 /* A late event of a request that is gone already */
2652 if (Stage == NULL)
2653 return;
2654
2655 FinishExpiredIsoch(Ring, Transfer, Sequence);
2656 RetireExpiredStages(Ring, Transfer, Stage, Sequence);
2657 CompleteTd(Ring, Transfer, Stage, Sequence, Code, Bytes, TRUE);
2658}
2659
2660VOID
2662 _In_ XhciTransferRing* Ring,
2663 _In_ const XHCI_TRB* Event)
2664{
2665 XhciIsochRing* Self = &Ring->m_Type.Isoch;
2667 ULONG64 Pointer = XhciTrbPointer(Event);
2668 XhciIsochTransfer* Transfer = NULL;
2669 XhciIsochStage* Stage;
2670 ULONG Sequence = 0;
2671 ULONG Bytes;
2672 BOOLEAN Matched;
2673 KIRQL OldIrql;
2674
2675 switch (static_cast<XhciCompletionCode>(Code))
2676 {
2677 case XhciCompletionCode::RingUnderrun:
2678 case XhciCompletionCode::RingOverrun:
2679 OnRingStarved(Ring);
2680 return;
2681
2682 case XhciCompletionCode::MissedService:
2683 Self->m_MissedServiceErrors++;
2684
2685 /* A Missed Service event without a TRB pointer has nothing to complete */
2686 if (Pointer == 0)
2687 return;
2688 break;
2689
2690 case XhciCompletionCode::NoPingResponse:
2691 UcxEndpointNoPingResponseError(Ring->m_Endpoint->Handle());
2692
2693 /* The length is invalid without a TRB pointer */
2694 if (Pointer == 0)
2695 return;
2696 break;
2697
2698 case XhciCompletionCode::Stopped:
2699 case XhciCompletionCode::StoppedLengthInvalid:
2700 case XhciCompletionCode::StoppedShortPacket:
2701 case XhciCompletionCode::DataBufferError:
2702 case XhciCompletionCode::BabbleDetected:
2703 case XhciCompletionCode::IsochBufferOverrun:
2704 case XhciCompletionCode::UsbTransactionError:
2705 case XhciCompletionCode::StallError:
2706 case XhciCompletionCode::EventLost:
2707 case XhciCompletionCode::SplitTransactionError:
2708 break;
2709
2710 default:
2711 DPRINT1("Isoch DCI %lu: unexpected code %lu without Event Data\n", Ring->m_Endpoint->Dci(), Code);
2712 break;
2713 }
2714
2715 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2716
2717 if (Self->m_Flags & XhciIsochStoppedSeen)
2718 {
2719 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2720 return;
2721 }
2722
2723 Matched = FindTrb(Ring, Event, &Stage, &Bytes);
2724 if (Stage != NULL)
2725 {
2726 Transfer = Stage->Transfer;
2727 Sequence = Transfer->Sequence;
2728 }
2729
2731 {
2732 /* Also covers a stop at the enqueue pointer or on a Link TRB after the last stage */
2734 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2735
2736 if (Stage != NULL)
2737 CompleteTd(Ring, Transfer, Stage, Sequence, Code, Bytes, FALSE);
2738
2739 Ring->m_Endpoint->OnStoppedEvent();
2740 return;
2741 }
2742
2743 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2744
2745 if (Stage == NULL)
2746 {
2747 if (!Matched)
2748 {
2749 DPRINT("Isoch DCI %lu: event at 0x%I64x code %lu matched no stage, dropped\n",
2750 Ring->m_Endpoint->Dci(), Pointer, Code);
2751 }
2752 return;
2753 }
2754
2755 /* The following Event Data event or a later stale pass closes the stage */
2756 FinishExpiredIsoch(Ring, Transfer, Sequence);
2757 RetireExpiredStages(Ring, Transfer, Stage, Sequence);
2758 CompleteTd(Ring, Transfer, Stage, Sequence, Code, Bytes, FALSE);
2759}
2760
2766BOOLEAN
2767XhciIsochRing::FindTrb(
2768 _In_ XhciTransferRing* Ring,
2772{
2773 XhciIsochRing* Self = &Ring->m_Type.Isoch;
2774 ULONG64 Pointer = XhciTrbPointer(Event);
2775 ULONG Residual = Event->Dword[2] & XHCI_TRANSFER_EVENT_LENGTH_MASK;
2777 XhciIsochStage* Stage = NULL;
2779 const XHCI_TRB* Trbs;
2781 ULONG Index;
2782 ULONG Limit;
2783 ULONG Sum = 0;
2784 ULONG Count;
2785 BOOLEAN Hit = FALSE;
2786
2787 *Found = NULL;
2788 *Bytes = 0;
2789
2790 if (Pointer == 0)
2791 {
2792 DPRINT1("Isoch DCI %lu: event code %lu without a TRB pointer\n", Ring->m_Endpoint->Dci(), Code);
2793 return FALSE;
2794 }
2795
2796 if (Pointer == Ring->EnqueuePointer())
2797 return TRUE;
2798
2799 for (Entry = Self->m_Pending.Flink; Entry != &Self->m_Pending && Stage == NULL; Entry = Entry->Flink)
2800 {
2802
2803 for (Count = 0; Count < Transfer->StageCount; Count++)
2804 {
2805 XhciIsochStage* Candidate = XhciIsochStageAt(Transfer, Count);
2806
2807 if (XhciIsochStageHas(Candidate, Pointer))
2808 {
2809 Stage = Candidate;
2810 break;
2811 }
2812 }
2813 }
2814
2815 if (Stage == NULL)
2816 return FALSE;
2817
2818 *Found = Stage;
2819
2820 /* Sum the TD's data TRBs up to the one named, from where the last scan stopped */
2821 Segment = Stage->ScanSegment;
2822 Index = Stage->ScanIndex;
2823
2824 while (Segment != NULL && !Hit)
2825 {
2826 ULONG64 Base = (ULONG64)Segment->LogicalAddress.QuadPart;
2827
2828 Trbs = static_cast<const XHCI_TRB*>(Segment->VirtualAddress);
2830
2831 for (; Index < Limit; Index++)
2832 {
2833 BOOLEAN Match = (Base + (ULONG64)Index * sizeof(XHCI_TRB) == Pointer);
2834 ULONG Type = XhciTrbType(&Trbs[Index]);
2835 BOOLEAN Data = (Type == static_cast<ULONG>(XhciTrbType::Normal) ||
2836 Type == static_cast<ULONG>(XhciTrbType::Isoch));
2837
2838 if (Type == static_cast<ULONG>(XhciTrbType::Link))
2839 {
2840 if (!Match)
2841 break;
2842
2843 /* QUIRK: a stop on a Link TRB leaves the packet where it was */
2844 Stage->ScanSegment = Segment;
2845 Stage->ScanIndex = Index;
2846 *Bytes = Sum;
2847 return TRUE;
2848 }
2849
2850 if (!Data && Type != static_cast<ULONG>(XhciTrbType::EventData))
2851 DPRINT1("Isoch DCI %lu: TRB type %lu inside a stage\n", Ring->m_Endpoint->Dci(), Type);
2852
2853 if (!Match)
2854 {
2855 if (Data)
2856 Sum += Trbs[Index].Dword[2] & XHCI_TRB_LENGTH_MASK;
2857 else if (Type == static_cast<ULONG>(XhciTrbType::EventData))
2858 Sum = 0;
2859 continue;
2860 }
2861
2862 if (Data)
2863 Sum = XhciIsochMatchBytes(Ring, Code, Trbs[Index].Dword[2] & XHCI_TRB_LENGTH_MASK, Residual, Sum);
2864
2865 Hit = TRUE;
2866 break;
2867 }
2868
2869 if (!Hit)
2870 {
2872 Index = 0;
2873 }
2874 }
2875
2876 if (!Hit)
2877 {
2878 DPRINT1("Isoch DCI %lu: TRB 0x%I64x not found past the scan position\n", Ring->m_Endpoint->Dci(), Pointer);
2879 return TRUE;
2880 }
2881
2882 *Bytes = Sum;
2883 Stage->ScanSegment = Segment;
2884 Stage->ScanIndex = Index;
2885
2886 /* The TD's Event Data TRB names its packet */
2887 while (Segment != NULL)
2888 {
2889 Trbs = static_cast<const XHCI_TRB*>(Segment->VirtualAddress);
2891
2892 for (; Index < Limit; Index++)
2893 {
2894 ULONG Type = XhciTrbType(&Trbs[Index]);
2895
2896 if (Type == static_cast<ULONG>(XhciTrbType::EventData))
2897 {
2898 ULONG Packet = (Trbs[Index].Dword[0] >> 16) & 0xFFFF;
2899
2900 Stage->NextPacket = Packet + 1;
2901 Stage->Transfer->PacketsRetired = Packet + 1;
2902 return TRUE;
2903 }
2904
2905 if (Type == static_cast<ULONG>(XhciTrbType::Link))
2906 {
2907 if (!XhciIsochNeedsLinkGuard(Ring))
2908 DPRINT1("Isoch DCI %lu: Link TRB inside a TD\n", Ring->m_Endpoint->Dci());
2909 break;
2910 }
2911
2912 if (Type != static_cast<ULONG>(XhciTrbType::Normal) && Type != static_cast<ULONG>(XhciTrbType::Isoch))
2913 DPRINT1("Isoch DCI %lu: TRB type %lu before the Event Data TRB\n", Ring->m_Endpoint->Dci(), Type);
2914 }
2915
2917 Index = 0;
2918 }
2919
2920 return TRUE;
2921}
2922
2923VOID
2925 _In_ XhciTransferRing* Ring)
2926{
2927 XhciIsochRing* Self = &Ring->m_Type.Isoch;
2928 ULONG Now = Ring->m_Controller->GetFrameNumber(XhciIsochBiasNow);
2929 BOOLEAN Abort;
2930 KIRQL OldIrql;
2931
2932 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2933 Self->m_RingEmptyEvents++;
2935 if (Self->m_PendingStages == 0)
2937
2938 Abort = Ring->m_Controller->HasErrata(XhciErrata::IsochMissedServiceSilent) &&
2939 Self->m_PendingStages != 0 &&
2940 (Self->m_Flags & XhciIsochLastFrameValid) &&
2941 (LONG)(Now - Self->m_LastScheduledFrame) >= 0;
2942 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2943
2944 if (Abort)
2945 {
2946 /* The controller ran past TDs without a word. Their events may still sit
2947 * on a secondary event ring, so the endpoint stops and reclaims instead. */
2948 DPRINT1("Isoch DCI %lu: ring empty with stages unreported, reclaiming\n", Ring->m_Endpoint->Dci());
2949 Ring->m_Endpoint->OnTransferCanceled();
2950 return;
2951 }
2952
2954 if (LeaveRingEmptyWait(Ring))
2955 BuildTdsFromQueue(Ring);
2956}
2957
2959BOOLEAN
2961 _In_ XhciTransferRing* Ring)
2962{
2963 XhciIsochRing* Self = &Ring->m_Type.Isoch;
2964 ULONG Now = Ring->m_Controller->GetFrameNumber(XhciIsochBiasNow);
2965 XhciIsochTransfer* Transfer;
2966 XhciIsochStage* Stage;
2968 ULONG Sequence;
2969 ULONG Index;
2970 BOOLEAN Stuck;
2971 KIRQL OldIrql;
2972
2973 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2974 if ((Self->m_Flags & XhciIsochLastFrameValid) && (LONG)(Now - Self->m_LastScheduledFrame) <= 0)
2975 {
2976 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2977 return FALSE;
2978 }
2979 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
2980
2981 for (;;)
2982 {
2983 Stage = NULL;
2984 Transfer = NULL;
2985 Sequence = 0;
2986
2987 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
2988 for (Entry = Self->m_Pending.Flink; Entry != &Self->m_Pending && Stage == NULL; Entry = Entry->Flink)
2989 {
2991
2992 for (Index = 0; Index < Transfer->StageCount; Index++)
2993 {
2994 if (XhciIsochStageAt(Transfer, Index)->OnHardware)
2995 {
2996 Stage = XhciIsochStageAt(Transfer, Index);
2997 Stage->NextPacket = Stage->LastPacket + 1;
2998 Sequence = Transfer->Sequence;
2999 break;
3000 }
3001 }
3002 }
3003 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
3004
3005 if (Stage == NULL)
3006 break;
3007
3008 DPRINT1("Isoch DCI %lu: closing a stage whose events never came\n", Ring->m_Endpoint->Dci());
3009 CompleteTd(Ring, Transfer, Stage, Sequence, static_cast<ULONG>(XhciCompletionCode::MissedService), 0, TRUE);
3010
3011 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
3012 Stuck = Stage->InUse && Stage->OnHardware && Stage->Transfer == Transfer;
3013 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
3014
3015 if (Stuck)
3016 break;
3017 }
3018
3019 KeAcquireSpinLock(&Ring->m_Lock, &OldIrql);
3021 if (Self->m_PendingStages == 0)
3023 KeReleaseSpinLock(&Ring->m_Lock, OldIrql);
3024
3025 return TRUE;
3026}
3027
3028VOID
3029NTAPI
3031 _In_ WDFTIMER Timer)
3032{
3033 XhciTransferRing* Ring = XhciTransferRing::FromQueue(static_cast<WDFQUEUE>(WdfTimerGetParentObject(Timer)));
3034 XhciIsochRing* Self = &Ring->m_Type.Isoch;
3035
3036 if (InterlockedCompareExchange(&Ring->m_MapState, 0, 0) != static_cast<LONG>(XhciMapState::AwaitingStarve))
3037 return;
3038
3039 DPRINT1("Isoch DCI %lu: no ring empty event for %lu ms\n", Ring->m_Endpoint->Dci(), XhciIsochWatchdogMs);
3040
3041 /* Close stages whose frames have passed so the ring does not stay parked */
3042 if (!FinishLostStages(Ring))
3043 {
3045 return;
3046 }
3047
3049 if (LeaveRingEmptyWait(Ring))
3050 BuildTdsFromQueue(Ring);
3051}
WCHAR First[]
Definition: FormatMessage.c:11
static _Out_opt_ PULONGLONG Start
Type
Definition: Type.h:7
#define VOID
Definition: acefi.h:82
unsigned char BOOLEAN
Definition: actypes.h:127
#define InterlockedExchange
Definition: armddk.h:54
static FCRET Finalize(FILECOMPARE *pFC, struct list *ptr0, struct list *ptr1, BOOL fDifferent)
Definition: text.h:515
LONG NTSTATUS
Definition: precomp.h:26
#define DPRINT1
Definition: precomp.h:8
return Found
Definition: dirsup.c:1270
_In_ ULONG const Usable
Definition: cdrom.h:1084
ULONG Dci() const
Definition: epctx.cpp:257
VOID OnTransferCanceled()
Definition: epctx.cpp:1183
VOID OnTransfersReclaimed()
Definition: epctx.cpp:1243
static VOID AllowReclaimOnCancel(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:702
volatile LONG m_Flags
Definition: isoxfer.h:344
static ULONG EstimateSegments(_In_ XhciTransferRing *Ring, _In_ const XhciIsochStage *Stage)
Definition: isoxfer.cpp:1601
static VOID SuspendRingFill(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:661
static EVT_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE EvtIoCanceledOnQueue
Definition: isoxfer.h:102
static XhciIsochStage * AcquireStage(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochTransfer *Transfer)
static VOID OnRingStarved(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:2924
static BOOLEAN OnTransferEvent(_In_ XhciTransferRing *Ring, _In_ const XHCI_TRB *Event)
Definition: isoxfer.cpp:772
static BOOLEAN TakeRequest(_In_ XhciTransferRing *Ring, _In_ WDFREQUEST Request, _Out_ XhciIsochTransfer **Taken)
Definition: isoxfer.cpp:1063
static EVT_WDF_REQUEST_CANCEL EvtRequestCancel
Definition: isoxfer.h:153
static ULONG ReleaseAllStages(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochTransfer *Transfer)
Definition: isoxfer.cpp:2269
static VOID ReleaseParkedMapping(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:495
static EVT_WDF_TIMER EvtWatchdog
Definition: isoxfer.h:154
ULONG m_TrbsPerTd
Definition: isoxfer.h:341
volatile LONG m_SegmentWait
Definition: isoxfer.h:349
static BOOLEAN TransferDone(_In_ const XhciIsochTransfer *Transfer)
Definition: isoxfer.cpp:1382
static VOID Enable(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:468
static Outcome EncodeStage(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochStage *Stage)
Definition: isoxfer.cpp:1675
static VOID AwaitRingEmpty(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:908
static VOID SkipUnmapped(_Inout_ XhciIsochTransfer *Transfer)
Definition: isoxfer.cpp:1282
static VOID RecoverTransfers(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:734
static VOID FinishExpiredIsoch(_In_ XhciTransferRing *Ring, _In_ const XhciIsochTransfer *Newest, _In_ ULONG Sequence)
Definition: isoxfer.cpp:2188
BOOLEAN m_DoorbellAfterFirstTd
Definition: isoxfer.h:335
static VOID WriteStage(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochStage *Stage)
Definition: isoxfer.cpp:1747
static EVT_WDF_DPC EvtCompletionDpc
Definition: isoxfer.h:103
static VOID FreeStage(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochStage *Stage)
static VOID FillLoop(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:857
static VOID CompleteAfterUncancel(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochTransfer *Transfer, _In_ USBD_STATUS PacketStatus, _In_ USBD_STATUS HeaderStatus, _In_ BOOLEAN FinishCanceled, _In_ BOOLEAN Async)
ULONG64 m_Bytes
Definition: isoxfer.h:359
XhciIsochStage * m_CurrentStage
Definition: isoxfer.h:350
static NTSTATUS Initialize(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:422
static NTSTATUS AcquireStageMdl(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochStage *Stage)
Definition: isoxfer.cpp:1465
static Outcome FailStage(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochStage *Stage)
Definition: isoxfer.cpp:1643
BOOLEAN m_FreshStream
Definition: isoxfer.h:334
volatile LONG m_FillWanted
Definition: isoxfer.h:345
static DRIVER_LIST_CONTROL DmaCallback
Definition: isoxfer.h:155
ULONG m_RingEmptyEvents
Definition: isoxfer.h:363
static BOOLEAN HasQueuedWork(_In_ const XhciTransferRing *Ring)
Definition: isoxfer.cpp:757
WDFTIMER m_Watchdog
Definition: isoxfer.h:354
static VOID CompletePlain(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochTransfer *Transfer, _In_ USBD_STATUS PacketStatus, _In_ USBD_STATUS HeaderStatus, _In_ BOOLEAN Async)
static VOID InsertLink(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochStage *Stage, _In_ BOOLEAN FirstOfTd)
Definition: isoxfer.cpp:2056
static Outcome PrimeStage(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochStage *Stage)
Definition: isoxfer.cpp:1393
static BOOLEAN SizeStage(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochStage *Stage)
Definition: isoxfer.cpp:1513
static VOID CompleteBatch(_In_ XhciTransferRing *Ring, _Inout_ PLIST_ENTRY Batch)
Definition: isoxfer.cpp:2477
ULONG m_Tds
Definition: isoxfer.h:360
ULONG m_Transfers
Definition: isoxfer.h:357
static VOID CompleteTd(_In_ XhciTransferRing *Ring, _In_ XhciIsochTransfer *Transfer, _In_ XhciIsochStage *Stage, _In_ ULONG Sequence, _In_ ULONG Code, _In_ ULONG Bytes, _In_ BOOLEAN CloseStage)
Definition: isoxfer.cpp:2091
ULONG m_MissedServiceErrors
Definition: isoxfer.h:362
static VOID Ed0Event(_In_ XhciTransferRing *Ring, _In_ const XHCI_TRB *Event)
Definition: isoxfer.cpp:2661
static VOID OnClientPipeReset(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:691
static BOOLEAN ParkOnSegments(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:948
static VOID KickAfterStage(_In_ XhciTransferRing *Ring, _In_ ULONG InFlight)
Definition: isoxfer.cpp:931
static BOOLEAN FindTrb(_In_ XhciTransferRing *Ring, _In_ const XHCI_TRB *Event, _Outptr_result_maybenull_ XhciIsochStage **Found, _Out_ PULONG Bytes)
ULONG m_BufferRetries
Definition: isoxfer.h:342
static XhciIsochStage * NextTdSlot(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:966
LIST_ENTRY m_WaitingForCancel
Definition: isoxfer.h:352
static VOID Disable(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:479
LIST_ENTRY m_Pending
Definition: isoxfer.h:351
static VOID Ed1Event(_In_ XhciTransferRing *Ring, _In_ const XHCI_TRB *Event)
Definition: isoxfer.cpp:2564
static BOOLEAN PacketsValid(_In_ const XhciIsochTransfer *Transfer)
Definition: isoxfer.cpp:1170
ULONG m_TdFailures
Definition: isoxfer.h:361
static VOID ConsumePendingEvents(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:726
static BOOLEAN DoorbellRungSinceFill(_In_ const XhciTransferRing *Ring)
Definition: isoxfer.cpp:750
LIST_ENTRY m_Completion
Definition: isoxfer.h:353
static BOOLEAN FinishLostStages(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:2960
static VOID Finalize(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochTransfer *Transfer, _In_ USBD_STATUS PacketStatus, _In_ USBD_STATUS HeaderStatus)
Definition: isoxfer.cpp:2312
static VOID PutScatterGather(_In_ XhciTransferRing *Ring, _In_opt_ PSCATTER_GATHER_LIST SgList)
volatile LONG m_RingEmptySeen
Definition: isoxfer.h:346
static VOID Cleanup(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:518
static ULONG LayoutStage(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochStage *Stage, _In_ BOOLEAN Commit)
Definition: isoxfer.cpp:1823
BOOLEAN m_DpcRunning
Definition: isoxfer.h:336
ULONG m_NextSequence
Definition: isoxfer.h:343
static VOID SegmentsArrived(_In_ XhciTransferRing *Ring, _In_ NTSTATUS Status)
Definition: isoxfer.cpp:785
static EVT_WDF_IO_QUEUE_STATE ReadyNotification
Definition: isoxfer.h:101
static BOOLEAN OwnsRequests(_In_ const XhciTransferRing *Ring)
ULONG m_LastScheduledFrame
Definition: isoxfer.h:338
volatile LONG m_LateStage
Definition: isoxfer.h:348
static VOID FlagMissedPackets(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochTransfer *Transfer)
Definition: isoxfer.cpp:1253
static VOID BuildTdsFromQueue(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:824
ULONG m_NextStartFrame
Definition: isoxfer.h:337
static VOID RetireExpiredStages(_In_ XhciTransferRing *Ring, _In_ XhciIsochTransfer *Transfer, _In_ XhciIsochStage *Stage, _In_ ULONG Sequence)
Definition: isoxfer.cpp:2224
ULONG m_PacketsPerFrame
Definition: isoxfer.h:339
ULONG m_FailedTransfers
Definition: isoxfer.h:358
static NTSTATUS ScheduleStartFrame(_In_ XhciTransferRing *Ring, _Inout_ XhciIsochTransfer *Transfer)
Definition: isoxfer.cpp:1189
static BOOLEAN LeaveRingEmptyWait(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:916
ULONG m_PendingStages
Definition: isoxfer.h:340
static VOID ResumeRingFill(_In_ XhciTransferRing *Ring)
Definition: isoxfer.cpp:636
volatile LONG m_FillAgain
Definition: isoxfer.h:347
static XhciTransferRing * FromQueue(_In_ WDFQUEUE Queue)
Definition: xferring.cpp:308
XhciMapState ChangeMapState(_In_ BOOLEAN FromAny, _In_ XhciMapState From, _In_ XhciMapState To)
Definition: xferring.cpp:1048
XhciIsochRing Isoch
Definition: xferring.h:200
static ULONG UrbProcessorIndex(_In_ PURB Urb)
Definition: xferring.cpp:1168
WDFDPC m_CompletionDpc
Definition: xferring.h:176
KSPIN_LOCK m_Lock
Definition: xferring.h:177
static NTSTATUS NtStatusFromUsbdStatus(_In_ USBD_STATUS UsbdStatus)
Definition: xferring.cpp:1237
union XhciTransferRing::@1691 m_Type
static USBD_STATUS UsbdStatusFromCompletionCode(_In_ ULONG CompletionCode)
Definition: xferring.cpp:1184
volatile LONG m_MapState
Definition: xferring.h:178
XhciEndpoint * m_Endpoint
Definition: xferring.h:172
#define _Requires_lock_held_(lock)
#define _Requires_lock_not_held_(lock)
PUCHAR Limit
Definition: desccheck.cpp:2230
Walk Base
Definition: desccheck.cpp:2270
PUCHAR End
Definition: desccheck.cpp:2229
_Inout_ HubConfiguration ** Slot
Definition: devhcd.cpp:1260
PLIST_ENTRY Entry
Definition: devhcd.cpp:1262
_In_ ULONG Attributes
Definition: dispmprt.h:1077
_In_ HANDLE _In_ CONST PDXGKMDT_OPM_GET_INFO_PARAMETERS Parameters
Definition: dispmprt.h:321
_Check_return_ _In_ DXGK_EVENT_TYPE _In_ ULONG Event
Definition: dispmprt.h:1287
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:33
#define RemoveEntryList(Entry)
Definition: env_spec_w32.h:986
#define InsertTailList(ListHead, Entry)
#define IsListEmpty(ListHead)
Definition: env_spec_w32.h:954
UCHAR KIRQL
Definition: env_spec_w32.h:591
#define KeRaiseIrql(irql, oldIrql)
Definition: env_spec_w32.h:597
KSPIN_LOCK * PKSPIN_LOCK
Definition: env_spec_w32.h:73
#define KeReleaseSpinLock(sl, irql)
Definition: env_spec_w32.h:627
#define PAGE_SIZE
Definition: env_spec_w32.h:49
#define KeLowerIrql(oldIrql)
Definition: env_spec_w32.h:602
#define PAGE_SHIFT
Definition: env_spec_w32.h:45
#define KeAcquireSpinLock(sl, irql)
Definition: env_spec_w32.h:609
#define RemoveHeadList(ListHead)
Definition: env_spec_w32.h:964
#define InitializeListHead(ListHead)
Definition: env_spec_w32.h:944
#define DISPATCH_LEVEL
Definition: env_spec_w32.h:696
@ Pending
Definition: fs_rec.h:186
#define IoFreeMdl
Definition: fxmdl.h:89
#define IoAllocateMdl
Definition: fxmdl.h:88
Status
Definition: gdiplustypes.h:24
static void Report(const char *msg)
Definition: glmodeswitch.c:58
const UCHAR * Tail
Definition: hubmsos.cpp:145
Previous
ULONG Total
Definition: hubwmi.cpp:548
#define InterlockedOr
Definition: interlocked.h:239
#define InterlockedCompareExchange
Definition: interlocked.h:119
#define InterlockedXor
Definition: interlocked.h:281
#define InterlockedAnd
Definition: interlocked.h:77
VOID NTAPI IoBuildPartialMdl(IN PMDL SourceMdl, IN PMDL TargetMdl, IN PVOID VirtualAddress, IN ULONG Length)
Definition: iomdl.c:96
static const ULONG XhciIsochTrbBytesMax
Definition: isoxfer.cpp:23
static const ULONG XhciIsochAsapDelay
Definition: isoxfer.cpp:17
static ULONG NTAPI XhciIsochFrames(_In_ ULONG Packets, _In_ ULONG PerFrame)
Definition: isoxfer.cpp:74
static const ULONG XhciIsochBiasLate
Definition: isoxfer.cpp:29
static const LONG XhciIsochStoppedSeen
Definition: isoxfer.cpp:41
static const ULONG XhciIsochTransferTag
Definition: isoxfer.cpp:13
static const LONG XhciIsochOkToReclaim
Definition: isoxfer.cpp:36
static XhciIsochStage *NTAPI XhciIsochStageAt(_In_ XhciIsochTransfer *Transfer, _In_ ULONG Position)
Definition: isoxfer.cpp:98
static VOID NTAPI XhciIsochTakePiece(_In_ const SCATTER_GATHER_LIST *SgList, _Inout_ PULONG Element, _Inout_ PULONG Offset, _In_ ULONG Bytes, _Out_ PULONG64 Address, _Out_ PULONG Chunk)
Definition: isoxfer.cpp:222
static BOOLEAN NTAPI XhciIsochStoppedCode(_In_ ULONG Code)
Definition: isoxfer.cpp:54
static const ULONG XhciIsochMaxPackets
Definition: isoxfer.cpp:24
static const LONG XhciIsochWaitIdle
Definition: isoxfer.cpp:44
static struct _URB_ISOCH_TRANSFER *NTAPI XhciIsochUrbOf(_In_ WDFREQUEST Request)
Definition: isoxfer.cpp:142
static ULONG NTAPI XhciIsochMatchBytes(_In_ const XhciTransferRing *Ring, _In_ ULONG Code, _In_ ULONG Length, _In_ ULONG Residual, _In_ ULONG Sum)
Definition: isoxfer.cpp:374
static BOOLEAN NTAPI XhciIsochSegmentHas(_In_ const XhciDmaBuffer *Segment, _In_ ULONG Start, _In_ ULONG OnePastEnd, _In_ ULONG64 Address)
Definition: isoxfer.cpp:309
static ULONG NTAPI XhciIsochSegmentLimit(_In_ const XhciIsochStage *Stage, _In_ const XhciDmaBuffer *Segment)
Definition: isoxfer.cpp:363
static const ULONG XhciIsochRestartFast
Definition: isoxfer.cpp:19
static VOID NTAPI XhciIsochMoveList(_Inout_ PLIST_ENTRY Destination, _Inout_ PLIST_ENTRY Source)
Definition: isoxfer.cpp:170
static const ULONG XhciIsochWatchdogMs
Definition: isoxfer.cpp:22
static ULONG NTAPI XhciIsochCountPieces(_In_ const SCATTER_GATHER_LIST *SgList, _In_ ULONG Element, _In_ ULONG Offset, _In_ ULONG Bytes)
Definition: isoxfer.cpp:255
static BOOLEAN NTAPI XhciIsochIsActive(_In_ const LIST_ENTRY *Pending, _In_ const XhciIsochTransfer *Transfer, _In_ ULONG Sequence)
Definition: isoxfer.cpp:182
static const ULONG XhciIsochBiasNow
Definition: isoxfer.cpp:28
static XhciDmaBuffer *NTAPI XhciIsochNextSegment(_In_ const XhciIsochStage *Stage, _In_ const XhciDmaBuffer *Segment)
Definition: isoxfer.cpp:347
static const USBD_STATUS XhciIsochNotSet
Definition: isoxfer.cpp:25
static const LONG XhciIsochCanceled
Definition: isoxfer.cpp:37
static const LONG XhciIsochWaitArrived
Definition: isoxfer.cpp:46
static const LONG XhciIsochWaitAbandoned
Definition: isoxfer.cpp:47
static ULONG NTAPI XhciIsochPacketLength(_In_ const XhciIsochTransfer *Transfer, _In_ ULONG Packet)
Definition: isoxfer.cpp:84
static const LONG XhciIsochLastFrameValid
Definition: isoxfer.cpp:40
static const LONG XhciIsochDoorbellRung
Definition: isoxfer.cpp:35
static BOOLEAN NTAPI XhciIsochStageHas(_In_ const XhciIsochStage *Stage, _In_ ULONG64 Address)
Definition: isoxfer.cpp:324
static BOOLEAN NTAPI XhciIsochNeedsLinkGuard(_In_ const XhciTransferRing *Ring)
Definition: isoxfer.cpp:124
static BOOLEAN NTAPI XhciIsochEndpointIn(_In_ const XhciTransferRing *Ring)
Definition: isoxfer.cpp:65
static VOID NTAPI XhciIsochSkipEmpty(_In_ const SCATTER_GATHER_LIST *SgList, _Inout_ PULONG Element, _Inout_ PULONG Offset)
Definition: isoxfer.cpp:203
static const ULONG XhciIsochInterruptEvery
Definition: isoxfer.cpp:21
static const LONG XhciIsochReclaimAck
Definition: isoxfer.cpp:38
static const LONG XhciIsochFrameWindow
Definition: isoxfer.cpp:18
static const ULONG XhciIsochRestartSlow
Definition: isoxfer.cpp:20
static const ULONG XhciIsochMaxInFlight
Definition: isoxfer.cpp:15
static ULONG NTAPI XhciIsochCountList(_In_ const LIST_ENTRY *Head)
Definition: isoxfer.cpp:155
static const LONG XhciIsochWaitParked
Definition: isoxfer.cpp:45
static const ULONG XhciIsochReasonBadEventData
Definition: isoxfer.cpp:32
static const LONG XhciIsochEmptyExpected
Definition: isoxfer.cpp:39
static VOID NTAPI XhciIsochAdvance(_In_ const SCATTER_GATHER_LIST *SgList, _Inout_ PULONG Element, _Inout_ PULONG Offset, _In_ ULONG Bytes)
Definition: isoxfer.cpp:281
static const ULONG XhciIsochBufferRetryLimit
Definition: isoxfer.cpp:16
static ULONG NTAPI XhciIsochEsitPayload(_In_ const XhciTransferRing *Ring)
Definition: isoxfer.cpp:109
_In_ PIRP Irp
Definition: kse.h:79
_In_ PIRP _In_ PIO_COMPLETION_ROUTINE _In_ PVOID Context
Definition: kse.h:81
if(dx< 0)
Definition: linetemp.h:194
VOID NTAPI MmBuildMdlForNonPagedPool(IN PMDL Mdl)
Definition: mdlsup.c:423
_In_ UINT Bytes
Definition: mmcopy.h:9
unsigned __int64 ULONG64
Definition: imports.h:198
unsigned __int64 * PULONG64
Definition: imports.h:198
#define min(a, b)
Definition: monoChain.cc:55
_In_ NDIS_HANDLE _In_ PNDIS_PACKET Packet
Definition: ndis.h:1549
_In_ UINT _In_ UINT _In_ PNDIS_PACKET Source
Definition: ndis.h:3169
_In_ PUNICODE_STRING _Inout_ PUNICODE_STRING Destination
Definition: rtlfuncs.h:3051
DWORD Interval
Definition: netstat.c:28
#define _Inout_
Definition: no_sal2.h:162
#define _Outptr_result_maybenull_
Definition: no_sal2.h:266
#define _Out_
Definition: no_sal2.h:160
#define _In_
Definition: no_sal2.h:158
#define _In_opt_
Definition: no_sal2.h:212
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:329
_In_ ULONG _In_ ULONG Offset
Definition: ntddpcm.h:101
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:102
long LONG
Definition: pedump.c:60
static WCHAR Address[46]
Definition: ping.c:68
@ Batch
Definition: ntsecapi.h:291
FORCEINLINE VOID KeMemoryBarrier(VOID)
Definition: ke.h:58
#define KeAcquireSpinLockAtDpcLevel(SpinLock)
Definition: ke.h:125
#define KeReleaseSpinLockFromDpcLevel(SpinLock)
Definition: ke.h:135
struct tm * Now
Definition: output.c:19
#define STATUS_SUCCESS
Definition: shellext.h:65
PACPI_METHOD_ARGUMENT Next
Definition: slotdev.cpp:96
#define DPRINT
Definition: sndvol32.h:73
LIST_ENTRY UsedSegments
Definition: isoxfer.h:38
ULONG FirstPacket
Definition: isoxfer.h:31
PSCATTER_GATHER_LIST SgList
Definition: isoxfer.h:36
XhciDmaBuffer * ScanSegment
Definition: isoxfer.h:43
BOOLEAN OnHardware
Definition: isoxfer.h:27
ULONG ScanIndex
Definition: isoxfer.h:44
ULONG NextPacket
Definition: isoxfer.h:33
ULONG EndIndex
Definition: isoxfer.h:42
LIST_ENTRY FreeSegments
Definition: isoxfer.h:37
XhciIsochTransfer * Transfer
Definition: isoxfer.h:30
ULONG LastPacket
Definition: isoxfer.h:32
ULONG FirstIndex
Definition: isoxfer.h:40
BOOLEAN InUse
Definition: isoxfer.h:26
XhciDmaBuffer * FirstSegment
Definition: isoxfer.h:39
XhciDmaBuffer * LastSegment
Definition: isoxfer.h:41
static const ULONG StageSlots
Definition: isoxfer.h:50
ULONG StartFrame
Definition: isoxfer.h:62
BOOLEAN OwnMdl
Definition: isoxfer.h:56
ULONG TdsRetired
Definition: isoxfer.h:71
ULONG PacketsQueued
Definition: isoxfer.h:69
ULONG BytesDropped
Definition: isoxfer.h:66
ULONG BytesQueued
Definition: isoxfer.h:65
ULONG TdsQueued
Definition: isoxfer.h:72
ULONG StageHead
Definition: isoxfer.h:73
NTSTATUS Status
Definition: isoxfer.h:60
ULONG PacketsRetired
Definition: isoxfer.h:68
WDFREQUEST Request
Definition: isoxfer.h:57
XhciIsochCancel CancelState
Definition: isoxfer.h:55
ULONG StageCount
Definition: isoxfer.h:74
ULONG Sequence
Definition: isoxfer.h:54
struct _URB_ISOCH_TRANSFER * Urb
Definition: isoxfer.h:58
ULONG BytesTotal
Definition: isoxfer.h:63
XhciTransferRing * Ring
Definition: isoxfer.h:59
ULONG Initialized
Definition: isoxfer.h:53
ULONG PacketsDropped
Definition: isoxfer.h:70
LIST_ENTRY Link
Definition: isoxfer.h:52
ULONG PacketsInUrb
Definition: isoxfer.h:67
struct _DMA_OPERATIONS * DmaOperations
Definition: iotypes.h:2310
Definition: typedefs.h:120
struct _LIST_ENTRY * Blink
Definition: typedefs.h:122
struct _LIST_ENTRY * Flink
Definition: typedefs.h:121
Definition: usb.h:565
ULONG TolerableDelay
Definition: wdftimer.h:95
ULONG Dword[4]
Definition: xhcihw.h:17
static MOUSE_PACKET LastPacket
Definition: mouse.c:32
static COORD Position
Definition: mouse.c:34
#define STATUS_PENDING
Definition: telnetd.h:14
uint32_t * PULONG
Definition: typedefs.h:59
unsigned char UCHAR
Definition: typedefs.h:53
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
#define NTAPI
Definition: typedefs.h:36
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
#define CONTAINING_RECORD(address, type, field)
Definition: typedefs.h:260
unsigned char * PUCHAR
Definition: typedefs.h:53
uint32_t ULONG
Definition: typedefs.h:59
_Must_inspect_result_ _In_ WDFDEVICE _In_ WDFREQUEST Request
#define STATUS_UNSUCCESSFUL
Definition: udferr_usr.h:132
#define STATUS_INVALID_DEVICE_STATE
Definition: udferr_usr.h:178
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
#define STATUS_CANCELLED
Definition: udferr_usr.h:170
#define USB_ENDPOINT_TYPE_ISOCHRONOUS
Definition: usb100.h:63
#define USB_ENDPOINT_DIRECTION_MASK
Definition: usb100.h:73
@ UsbHighSpeed
Definition: usb200.h:44
@ UsbFullSpeed
Definition: usb200.h:43
enum _USB_DEVICE_SPEED USB_DEVICE_SPEED
#define USBD_STATUS_ISO_NOT_ACCESSED_BY_HW
Definition: usb.h:226
#define USBD_STATUS_CANCELED
Definition: usb.h:225
#define USBD_STATUS_ISO_TD_ERROR
Definition: usb.h:227
#define USBD_STATUS_SUCCESS
Definition: usb.h:177
#define USBD_STATUS_INSUFFICIENT_RESOURCES
Definition: usb.h:215
#define USBD_STATUS_INVALID_PARAMETER
Definition: usb.h:203
#define USBD_START_ISO_TRANSFER_ASAP
Definition: usb.h:162
LONG USBD_STATUS
Definition: usb.h:172
#define USBD_STATUS_ISO_NOT_ACCESSED_LATE
Definition: usb.h:229
#define USBD_STATUS_BAD_START_FRAME
Definition: usb.h:209
#define USBD_STATUS_ISOCH_REQUEST_FAILED
Definition: usb.h:210
#define USBD_STATUS(Status)
Definition: usbdi.h:52
_In_opt_ PUSB_CONFIGURATION_DESCRIPTOR _Inout_ PUSBD_INTERFACE_LIST_ENTRY _Outptr_ PURB * Urb
Definition: usbdlib.h:333
USHORT Count
Definition: userreq.cpp:200
_Must_inspect_result_ _In_ WDFCHILDLIST _In_ PWDF_CHILD_LIST_ITERATOR _Out_ WDFDEVICE _Inout_opt_ PWDF_CHILD_RETRIEVE_INFO Info
Definition: wdfchildlist.h:690
_In_ WDFCOLLECTION _In_ ULONG Index
FORCEINLINE LONGLONG WDF_REL_TIMEOUT_IN_MS(_In_ ULONGLONG Time)
Definition: wdfcore.h:80
_In_ PDEVICE_OBJECT DeviceObject
Definition: wdfdevice.h:2061
_Must_inspect_result_ _In_ WDFDEVICE _In_ PIRP _In_ WDFQUEUE Queue
Definition: wdfdevice.h:2231
_Must_inspect_result_ _In_ WDFDEVICE _In_ PWDF_DEVICE_PROPERTY_DATA _In_ DEVPROPTYPE _In_ ULONG Size
Definition: wdfdevice.h:4539
_In_ UCHAR _In_ UCHAR _In_ ULONG Code
Definition: wdfdevice.h:1707
WDF_EXTERN_C_START typedef _In_ WDFDEVICE _In_ WDFCONTEXT _In_ WDF_DMA_DIRECTION _In_ PSCATTER_GATHER_LIST SgList
_In_ WDFDEVICE _In_ PVOID _In_opt_ PMDL Mdl
_Must_inspect_result_ _In_ PWDF_DPC_CONFIG _In_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFDPC * Dpc
Definition: wdfdpc.h:112
FORCEINLINE VOID WDF_OBJECT_ATTRIBUTES_INIT(_Out_ PWDF_OBJECT_ATTRIBUTES Attributes)
Definition: wdfobject.h:147
_Must_inspect_result_ _In_ WDFKEY _In_ PCUNICODE_STRING _Out_opt_ PUSHORT _Inout_opt_ PUNICODE_STRING Value
Definition: wdfregistry.h:413
_In_ WDFIOTARGET Target
Definition: wdfrequest.h:306
FORCEINLINE VOID WDF_REQUEST_PARAMETERS_INIT(_Out_ PWDF_REQUEST_PARAMETERS Parameters)
Definition: wdfrequest.h:211
_In_ WDFREQUEST _In_ PFN_WDF_REQUEST_CANCEL EvtRequestCancel
Definition: wdfrequest.h:730
_Must_inspect_result_ _In_opt_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFWAITLOCK * Lock
Definition: wdfsync.h:127
FORCEINLINE VOID WDF_TIMER_CONFIG_INIT(_Out_ PWDF_TIMER_CONFIG Config, _In_ PFN_WDF_TIMER EvtTimerFunc)
Definition: wdftimer.h:111
_At_(*)(_In_ PWSK_CLIENT Client, _In_opt_ PUNICODE_STRING NodeName, _In_opt_ PUNICODE_STRING ServiceName, _In_opt_ ULONG NameSpace, _In_opt_ GUID *Provider, _In_opt_ PADDRINFOEXW Hints, _Outptr_ PADDRINFOEXW *Result, _In_opt_ PEPROCESS OwningProcess, _In_opt_ PETHREAD OwningThread, _Inout_ PIRP Irp Result)(Mem)) NTSTATUS(WSKAPI *PFN_WSK_GET_ADDRESS_INFO
Definition: wsk.h:409
_Inout_ PVOID Segment
Definition: exfuncs.h:1101
NTKERNELAPI VOID NTAPI WRITE_REGISTER_UCHAR(IN PUCHAR Register, IN UCHAR Value)
#define DMA_TRANSFER_INFO_VERSION1
Definition: iotypes.h:2116
struct _SCATTER_GATHER_LIST SCATTER_GATHER_LIST
Definition: iotypes.h:2219
struct _SCATTER_GATHER_LIST * PSCATTER_GATHER_LIST
Definition: iotypes.h:2219
_In_ struct _IRP _In_ struct _SCATTER_GATHER_LIST * ScatterGather
Definition: iotypes.h:2390
#define DEVICE_DESCRIPTION_VERSION3
Definition: iotypes.h:2066
_Requires_lock_held_ Interrupt _Releases_lock_ Interrupt _In_ _IRQL_restores_ KIRQL OldIrql
Definition: kefuncs.h:778
_In_ LARGE_INTEGER _In_ ULONG Period
Definition: kefuncs.h:1304
#define MmGetMdlByteCount(_Mdl)
#define MmGetMdlVirtualAddress(_Mdl)
#define ADDRESS_AND_SIZE_TO_SPAN_PAGES(_Va, _Size)
#define NT_ASSERT
Definition: rtlfuncs.h:3334
XhciMapState
Definition: xferring.h:19
@ IsochFsNoBareLinkTd
#define XHCI_TRB_BEI
Definition: xhcihw.h:638
#define XHCI_TRB_LENGTH_MASK
Definition: xhcihw.h:621
#define XHCI_TRB_TYPE_SHIFT
Definition: xhcihw.h:23
FORCEINLINE ULONG64 NTAPI XhciTrbPointer(_In_ const XHCI_TRB *Trb)
Definition: xhcihw.h:60
#define XHCI_TRANSFER_EVENT_ED
Definition: xhcihw.h:321
#define XHCI_TRB_ENT
Definition: xhcihw.h:623
#define XHCI_TRB_TD_SIZE_SHIFT
Definition: xhcihw.h:622
#define XHCI_TRB_INTERRUPTER_MASK
Definition: xhcihw.h:611
#define XHCI_ISOCH_FRAME_ID_MASK
Definition: xhcihw.h:642
#define XHCI_TRB_INTERRUPTER_SHIFT
Definition: xhcihw.h:610
#define XHCI_ISOCH_FRAME_ID_SHIFT
Definition: xhcihw.h:641
#define XHCI_ISOCH_TLBPC_SHIFT
Definition: xhcihw.h:640
#define XHCI_TRANSFER_EVENT_LENGTH_MASK
Definition: xhcihw.h:635
#define XHCI_TRB_IOC
Definition: xhcihw.h:624
XhciTrbType
Definition: xhcihw.h:68
#define XHCI_EVENT_DATA_TYPE_MASK
Definition: xhcihw.h:324
#define XHCI_TRB_CYCLE
Definition: xhcihw.h:22
struct _XHCI_TRB XHCI_TRB
#define XHCI_TRB_CHAIN
Definition: xhcihw.h:609
XhciCompletionCode
Definition: xhcihw.h:110
#define XHCI_ISOCH_TBC_SHIFT
Definition: xhcihw.h:639
#define XHCI_HCC2_ETC
Definition: xhcihw.h:378
#define XHCI_HCC2_LEC
Definition: xhcihw.h:377
FORCEINLINE ULONG NTAPI XhciTrbCompletionCode(_In_ const XHCI_TRB *Trb)
Definition: xhcihw.h:42
FORCEINLINE BOOLEAN NTAPI XhciIsListEntryLinked(_In_ const LIST_ENTRY *Entry)
Definition: xhciutil.h:127
FORCEINLINE VOID NTAPI XhciClearListEntry(_Out_ PLIST_ENTRY Entry)
Definition: xhciutil.h:117
#define const
Definition: zconf.h:233