]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c
Replace SHA1 with SHA256 digest algorithm.
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / XhciDxe / XhciSched.c
CommitLineData
92870c98 1/** @file\r
2\r
3 XHCI transfer scheduling routines.\r
4\r
5Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
6This program and the accompanying materials\r
7are licensed and made available under the terms and conditions of the BSD License\r
8which accompanies this distribution. The full text of the license may be found at\r
9http://opensource.org/licenses/bsd-license.php\r
10\r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "Xhci.h"\r
17\r
18/**\r
19 Allocates a buffer of a certain pool type at a specified alignment.\r
20\r
21 Allocates the number bytes specified by AllocationSize of a certain pool type with an alignment\r
22 specified by Alignment. The allocated buffer is returned. If AllocationSize is 0, then a valid\r
23 buffer of 0 size is returned. If there is not enough memory at the specified alignment remaining\r
24 to satisfy the request, then NULL is returned.\r
25 If Alignment is not a power of two and Alignment is not zero, then ASSERT().\r
26\r
27 @param PoolType The type of pool to allocate.\r
28 @param AllocationSize The number of bytes to allocate.\r
29 @param Alignment The requested alignment of the allocation. Must be a power of two.\r
30 If Alignment is zero, then byte alignment is used.\r
31\r
32 @return A pointer to the allocated buffer or NULL if allocation fails.\r
33\r
34**/\r
35VOID *\r
36InternalAllocateAlignedPool (\r
37 IN EFI_MEMORY_TYPE PoolType,\r
38 IN UINTN AllocationSize,\r
39 IN UINTN Alignment\r
40 )\r
41{\r
42 VOID *RawAddress;\r
43 UINTN AlignedAddress;\r
44 UINTN AlignmentMask;\r
45 UINTN OverAllocationSize;\r
46 UINTN RealAllocationSize;\r
47 VOID **FreePointer;\r
48\r
49 //\r
50 // Alignment must be a power of two or zero.\r
51 //\r
52 ASSERT ((Alignment & (Alignment - 1)) == 0);\r
53\r
54 if (Alignment == 0) {\r
55 AlignmentMask = Alignment;\r
56 } else {\r
57 AlignmentMask = Alignment - 1;\r
58 }\r
59 //\r
60 // Calculate the extra memory size, over-allocate memory pool and get the aligned memory address.\r
61 //\r
62 OverAllocationSize = sizeof (RawAddress) + AlignmentMask;\r
63 RealAllocationSize = AllocationSize + OverAllocationSize;\r
64 //\r
65 // Make sure that AllocationSize plus OverAllocationSize does not overflow.\r
66 //\r
67 ASSERT (RealAllocationSize > AllocationSize);\r
68\r
69 RawAddress = NULL;\r
70 gBS->AllocatePool (PoolType, RealAllocationSize, &RawAddress);\r
71 if (RawAddress == NULL) {\r
72 return NULL;\r
73 }\r
74 AlignedAddress = ((UINTN) RawAddress + OverAllocationSize) & ~AlignmentMask;\r
75 //\r
76 // Save the original memory address just before the aligned address.\r
77 //\r
78 FreePointer = (VOID **)(AlignedAddress - sizeof (RawAddress));\r
79 *FreePointer = RawAddress;\r
80\r
81 return (VOID *) AlignedAddress;\r
82}\r
83\r
84/**\r
85 Allocates and zeros a buffer of a certain pool type at a specified alignment.\r
86\r
87 Allocates the number bytes specified by AllocationSize of a certain pool type with an alignment\r
88 specified by Alignment, clears the buffer with zeros, and returns a pointer to the allocated\r
89 buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there is not\r
90 enough memory at the specified alignment remaining to satisfy the request, then NULL is returned.\r
91 If Alignment is not a power of two and Alignment is not zero, then ASSERT().\r
92\r
93 @param PoolType The type of pool to allocate.\r
94 @param AllocationSize The number of bytes to allocate.\r
95 @param Alignment The requested alignment of the allocation. Must be a power of two.\r
96 If Alignment is zero, then byte alignment is used.\r
97\r
98 @return A pointer to the allocated buffer or NULL if allocation fails.\r
99\r
100**/\r
101VOID *\r
102InternalAllocateAlignedZeroPool (\r
103 IN EFI_MEMORY_TYPE PoolType,\r
104 IN UINTN AllocationSize,\r
105 IN UINTN Alignment\r
106 )\r
107{\r
108 VOID *Memory;\r
109 Memory = InternalAllocateAlignedPool (PoolType, AllocationSize, Alignment);\r
110 if (Memory != NULL) {\r
111 ZeroMem (Memory, AllocationSize);\r
112 }\r
113 return Memory;\r
114}\r
115\r
116/**\r
117 Allocates and zeros a buffer of type EfiBootServicesData at a specified alignment.\r
118\r
119 Allocates the number bytes specified by AllocationSize of type EfiBootServicesData with an\r
120 alignment specified by Alignment, clears the buffer with zeros, and returns a pointer to the\r
121 allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there\r
122 is not enough memory at the specified alignment remaining to satisfy the request, then NULL is\r
123 returned.\r
124 If Alignment is not a power of two and Alignment is not zero, then ASSERT().\r
125\r
126 @param AllocationSize The number of bytes to allocate.\r
127 @param Alignment The requested alignment of the allocation. Must be a power of two.\r
128 If Alignment is zero, then byte alignment is used.\r
129\r
130 @return A pointer to the allocated buffer or NULL if allocation fails.\r
131\r
132**/\r
133VOID *\r
134EFIAPI\r
135AllocateAlignedZeroPool (\r
136 IN UINTN AllocationSize,\r
137 IN UINTN Alignment\r
138 )\r
139{\r
140 return InternalAllocateAlignedZeroPool (EfiBootServicesData, AllocationSize, Alignment);\r
141}\r
142\r
143/**\r
144 Frees a buffer that was previously allocated with one of the aligned pool allocation functions\r
145 in the Memory Allocation Library.\r
146\r
147 Frees the buffer specified by Buffer. Buffer must have been allocated on a previous call to the\r
148 aligned pool allocation services of the Memory Allocation Library.\r
149 If Buffer was not allocated with an aligned pool allocation function in the Memory Allocation\r
150 Library, then ASSERT().\r
151\r
152 @param Buffer Pointer to the buffer to free.\r
153\r
154**/\r
155VOID\r
156EFIAPI\r
157FreeAlignedPool (\r
158 IN VOID *Buffer\r
159 )\r
160{\r
161 VOID *RawAddress;\r
162 VOID **FreePointer;\r
163 EFI_STATUS Status;\r
164\r
165 //\r
166 // Get the pre-saved original address in the over-allocate pool.\r
167 //\r
168 FreePointer = (VOID **)((UINTN) Buffer - sizeof (RawAddress));\r
169 RawAddress = *FreePointer;\r
170\r
171 Status = gBS->FreePool (RawAddress);\r
172 ASSERT_EFI_ERROR (Status);\r
173}\r
174\r
175/**\r
176 Create a command transfer TRB to support XHCI command interfaces.\r
177\r
178 @param Xhc The XHCI device.\r
179 @param CmdTrb The cmd TRB to be executed.\r
180\r
181 @return Created URB or NULL.\r
182\r
183**/\r
184URB*\r
185XhcCreateCmdTrb (\r
186 IN USB_XHCI_DEV *Xhc,\r
187 IN TRB *CmdTrb\r
188 )\r
189{\r
190 URB *Urb;\r
191\r
192 Urb = AllocateZeroPool (sizeof (URB));\r
193 if (Urb == NULL) {\r
194 return NULL;\r
195 }\r
196\r
197 Urb->Signature = XHC_URB_SIG;\r
198\r
199 Urb->Ring = &Xhc->CmdRing;\r
200 XhcSyncTrsRing (Xhc, Urb->Ring);\r
201 Urb->TrbNum = 1;\r
202 Urb->TrbStart = Urb->Ring->RingEnqueue;\r
203 CopyMem (Urb->TrbStart, CmdTrb, sizeof (TRB));\r
204 Urb->TrbStart->CycleBit = Urb->Ring->RingPCS & BIT0;\r
205 Urb->TrbEnd = Urb->TrbStart;\r
206\r
207 Urb->EvtRing = &Xhc->CmdEventRing;\r
208 XhcSyncEventRing (Xhc, Urb->EvtRing);\r
209 Urb->EvtTrbStart = Urb->EvtRing->EventRingEnqueue;\r
210\r
211 return Urb;\r
212}\r
213\r
214/**\r
215 Execute a XHCI cmd TRB pointed by CmdTrb.\r
216\r
217 @param Xhc The XHCI device.\r
218 @param CmdTrb The cmd TRB to be executed.\r
219 @param TimeOut Indicates the maximum time, in millisecond, which the\r
220 transfer is allowed to complete.\r
221 @param EvtTrb The event TRB corresponding to the cmd TRB.\r
222\r
223 @retval EFI_SUCCESS The transfer was completed successfully.\r
224 @retval EFI_INVALID_PARAMETER Some parameters are invalid.\r
225 @retval EFI_TIMEOUT The transfer failed due to timeout.\r
226 @retval EFI_DEVICE_ERROR The transfer failed due to host controller error.\r
227\r
228**/\r
229EFI_STATUS\r
230EFIAPI\r
231XhcCmdTransfer (\r
232 IN USB_XHCI_DEV *Xhc,\r
233 IN TRB *CmdTrb,\r
234 IN UINTN TimeOut,\r
235 OUT TRB **EvtTrb\r
236 )\r
237{\r
238 EFI_STATUS Status;\r
239 URB *Urb;\r
240\r
241 //\r
242 // Validate the parameters\r
243 //\r
244 if ((Xhc == NULL) || (CmdTrb == NULL)) {\r
245 return EFI_INVALID_PARAMETER;\r
246 }\r
247\r
248 Status = EFI_DEVICE_ERROR;\r
249\r
250 if (XhcIsHalt (Xhc) || XhcIsSysError (Xhc)) {\r
251 DEBUG ((EFI_D_ERROR, "XhcCmdTransfer: HC is halted\n"));\r
252 goto ON_EXIT;\r
253 }\r
254\r
255 //\r
256 // Create a new URB, then poll the execution status.\r
257 //\r
258 Urb = XhcCreateCmdTrb (Xhc, CmdTrb);\r
259\r
260 if (Urb == NULL) {\r
261 DEBUG ((EFI_D_ERROR, "XhcCmdTransfer: failed to create URB\n"));\r
262 Status = EFI_OUT_OF_RESOURCES;\r
263 goto ON_EXIT;\r
264 }\r
265\r
266 ASSERT (Urb->EvtRing == &Xhc->CmdEventRing);\r
267\r
268 Status = XhcExecTransfer (Xhc, TRUE, Urb, TimeOut);\r
269 *EvtTrb = Urb->EvtTrbStart;\r
270\r
271 if (Urb->Result == EFI_USB_NOERROR) {\r
272 Status = EFI_SUCCESS;\r
273 }\r
274\r
275 FreePool (Urb);\r
276\r
277ON_EXIT:\r
278 return Status;\r
279}\r
280\r
281/**\r
282 Create a new URB for a new transaction.\r
283\r
284 @param Xhc The XHCI device\r
285 @param DevAddr The device address\r
286 @param EpAddr Endpoint addrress\r
287 @param DevSpeed The device speed\r
288 @param MaxPacket The max packet length of the endpoint\r
289 @param Type The transaction type\r
290 @param Request The standard USB request for control transfer\r
291 @param Data The user data to transfer\r
292 @param DataLen The length of data buffer\r
293 @param Callback The function to call when data is transferred\r
294 @param Context The context to the callback\r
295\r
296 @return Created URB or NULL\r
297\r
298**/\r
299URB*\r
300XhcCreateUrb (\r
301 IN USB_XHCI_DEV *Xhc,\r
302 IN UINT8 DevAddr,\r
303 IN UINT8 EpAddr,\r
304 IN UINT8 DevSpeed,\r
305 IN UINTN MaxPacket,\r
306 IN UINTN Type,\r
307 IN EFI_USB_DEVICE_REQUEST *Request,\r
308 IN VOID *Data,\r
309 IN UINTN DataLen,\r
310 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,\r
311 IN VOID *Context\r
312 )\r
313{\r
314 USB_ENDPOINT *Ep;\r
315 EFI_STATUS Status;\r
316 URB *Urb;\r
317\r
318 Urb = AllocateZeroPool (sizeof (URB));\r
319 if (Urb == NULL) {\r
320 return NULL;\r
321 }\r
322\r
323 Urb->Signature = XHC_URB_SIG;\r
324 InitializeListHead (&Urb->UrbList);\r
325\r
326 Ep = &Urb->Ep;\r
327 Ep->DevAddr = DevAddr;\r
ce9b5900 328 Ep->EpAddr = (UINT8)(EpAddr & 0x0F);\r
92870c98 329 Ep->Direction = ((EpAddr & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut;\r
330 Ep->DevSpeed = DevSpeed;\r
331 Ep->MaxPacket = MaxPacket;\r
332 Ep->Type = Type;\r
333\r
334 Urb->Request = Request;\r
335 Urb->Data = Data;\r
336 Urb->DataLen = DataLen;\r
337 Urb->Callback = Callback;\r
338 Urb->Context = Context;\r
339\r
340 Status = XhcCreateTransferTrb (Xhc, Urb);\r
341\r
342 return Urb;\r
343}\r
344\r
345/**\r
346 Create a transfer TRB.\r
347\r
348 @param Xhc The XHCI device\r
349 @param Urb The urb used to construct the transfer TRB.\r
350\r
351 @return Created TRB or NULL\r
352\r
353**/\r
354EFI_STATUS\r
92870c98 355XhcCreateTransferTrb (\r
356 IN USB_XHCI_DEV *Xhc,\r
357 IN URB *Urb\r
358 )\r
359{\r
360 DEVICE_CONTEXT *OutputDevContxt;\r
361 TRANSFER_RING *EPRing;\r
362 UINT8 EPType;\r
363 UINT8 SlotId;\r
364 UINT8 Dci;\r
365 TRB *TrbStart;\r
366 UINTN TotalLen;\r
367 UINTN Len;\r
368 UINTN TrbNum;\r
369\r
370 SlotId = XhcDevAddrToSlotId(Urb->Ep.DevAddr);\r
ce9b5900 371 Dci = XhcEndpointToDci (Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction));\r
92870c98 372 EPRing = (TRANSFER_RING *)(UINTN) UsbDevContext[SlotId].EndpointTransferRing[Dci-1];\r
373 Urb->Ring = EPRing;\r
374 OutputDevContxt = (DEVICE_CONTEXT *)(UINTN) Xhc->DCBAA[SlotId];\r
375 EPType = (UINT8) OutputDevContxt->EP[Dci-1].EPType;\r
376\r
377 //\r
378 // Construct the TRB\r
379 //\r
380 XhcSyncTrsRing (Xhc, EPRing);\r
381 Urb->TrbStart = EPRing->RingEnqueue;\r
382 switch (EPType) {\r
383 case ED_CONTROL_BIDIR:\r
384 Urb->EvtRing = &Xhc->CtrlTrEventRing;\r
385 XhcSyncEventRing (Xhc, Urb->EvtRing);\r
386 Urb->EvtTrbStart = Urb->EvtRing->EventRingEnqueue;\r
387 //\r
388 // For control transfer, create SETUP_STAGE_TRB first.\r
389 //\r
390 TrbStart = EPRing->RingEnqueue;\r
391 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->bmRequestType = Urb->Request->RequestType;\r
392 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->bRequest = Urb->Request->Request;\r
393 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->wValue = Urb->Request->Value;\r
394 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->wIndex = Urb->Request->Index;\r
395 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->wLength = Urb->Request->Length;\r
396 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->Lenth = 8;\r
397 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->IntTarget = Urb->EvtRing->EventInterrupter;\r
398 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->IOC = 1;\r
399 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->IDT = 1;\r
400 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->Type = TRB_TYPE_SETUP_STAGE;\r
401 if (Urb->Ep.Direction == EfiUsbDataIn) {\r
402 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->TRT = 3;\r
403 } else if (Urb->Ep.Direction == EfiUsbDataOut) {\r
404 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->TRT = 2;\r
405 } else {\r
406 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->TRT = 0;\r
407 }\r
408 //\r
409 // Update the cycle bit\r
410 //\r
411 ((TRANSFER_TRB_CONTROL_SETUP *) TrbStart)->CycleBit = EPRing->RingPCS & BIT0;\r
412 Urb->TrbNum++;\r
413\r
414 //\r
415 // For control transfer, create DATA_STAGE_TRB.\r
416 //\r
417 if (Urb->DataLen > 0) {\r
418 XhcSyncTrsRing (Xhc, EPRing);\r
419 TrbStart = EPRing->RingEnqueue;\r
420 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->TRBPtrLo = XHC_LOW_32BIT(Urb->Data);\r
421 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->TRBPtrHi = XHC_HIGH_32BIT(Urb->Data);\r
422 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->Lenth = (UINT32) Urb->DataLen;\r
423 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->TDSize = 0;\r
424 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->IntTarget = Urb->EvtRing->EventInterrupter;\r
425 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->ISP = 1;\r
426 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->IOC = 1;\r
427 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->IDT = 0;\r
428 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->CH = 0;\r
429 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->Type = TRB_TYPE_DATA_STAGE;\r
430 if (Urb->Ep.Direction == EfiUsbDataIn) {\r
431 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->DIR = 1;\r
432 } else if (Urb->Ep.Direction == EfiUsbDataOut) {\r
433 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->DIR = 0;\r
434 } else {\r
435 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->DIR = 0;\r
436 }\r
437 //\r
438 // Update the cycle bit\r
439 //\r
440 ((TRANSFER_TRB_CONTROL_DATA *) TrbStart)->CycleBit = EPRing->RingPCS & BIT0;\r
441 Urb->TrbNum++;\r
442 }\r
443 //\r
444 // For control transfer, create STATUS_STAGE_TRB.\r
445 // Get the pointer to next TRB for status stage use\r
446 //\r
447 XhcSyncTrsRing (Xhc, EPRing);\r
448 TrbStart = EPRing->RingEnqueue;\r
449 ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->IntTarget = Urb->EvtRing->EventInterrupter;\r
450 ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->IOC = 1;\r
451 ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->CH = 0;\r
452 ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->Type = TRB_TYPE_STATUS_STAGE;\r
453 if (Urb->Ep.Direction == EfiUsbDataIn) {\r
454 ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->DIR = 0;\r
455 } else if (Urb->Ep.Direction == EfiUsbDataOut) {\r
456 ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->DIR = 1;\r
457 } else {\r
458 ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->DIR = 0;\r
459 }\r
460 //\r
461 // Update the cycle bit\r
462 //\r
463 ((TRANSFER_TRB_CONTROL_STATUS *) TrbStart)->CycleBit = EPRing->RingPCS & BIT0;\r
464 //\r
465 // Update the enqueue pointer\r
466 //\r
467 XhcSyncTrsRing (Xhc, EPRing);\r
468 Urb->TrbNum++;\r
469 Urb->TrbEnd = TrbStart;\r
470\r
471 break;\r
472\r
473 case ED_BULK_OUT:\r
474 case ED_BULK_IN:\r
475 Urb->EvtRing = &Xhc->BulkTrEventRing;\r
476 XhcSyncEventRing (Xhc, Urb->EvtRing);\r
477 Urb->EvtTrbStart = Urb->EvtRing->EventRingEnqueue;\r
478\r
479 TotalLen = 0;\r
480 Len = 0;\r
481 TrbNum = 0;\r
482 TrbStart = EPRing->RingEnqueue;\r
483 while (TotalLen < Urb->DataLen) {\r
484 if ((TotalLen + 0x10000) >= Urb->DataLen) {\r
485 Len = Urb->DataLen - TotalLen;\r
486 } else {\r
487 Len = 0x10000;\r
488 }\r
489 TrbStart = EPRing->RingEnqueue;\r
490 ((TRANSFER_TRB_NORMAL *) TrbStart)->TRBPtrLo = XHC_LOW_32BIT((UINT8 *) Urb->Data + TotalLen);\r
491 ((TRANSFER_TRB_NORMAL *) TrbStart)->TRBPtrHi = XHC_HIGH_32BIT((UINT8 *) Urb->Data + TotalLen);\r
492 ((TRANSFER_TRB_NORMAL *) TrbStart)->Lenth = (UINT32) Len;\r
493 ((TRANSFER_TRB_NORMAL *) TrbStart)->TDSize = 0;\r
494 ((TRANSFER_TRB_NORMAL *) TrbStart)->IntTarget = Urb->EvtRing->EventInterrupter;\r
495 ((TRANSFER_TRB_NORMAL *) TrbStart)->ISP = 1;\r
496 ((TRANSFER_TRB_NORMAL *) TrbStart)->IOC = 1;\r
497 ((TRANSFER_TRB_NORMAL *) TrbStart)->Type = TRB_TYPE_NORMAL;\r
498 //\r
499 // Update the cycle bit\r
500 //\r
501 ((TRANSFER_TRB_NORMAL *) TrbStart)->CycleBit = EPRing->RingPCS & BIT0;\r
502\r
503 XhcSyncTrsRing (Xhc, EPRing);\r
504 TrbNum++;\r
505 TotalLen += Len;\r
506 }\r
507\r
508 Urb->TrbNum = TrbNum;\r
509 Urb->TrbEnd = TrbStart;\r
510 break;\r
511\r
512 case ED_INTERRUPT_OUT:\r
513 case ED_INTERRUPT_IN:\r
514 if (Urb->Ep.Type == XHC_INT_TRANSFER_ASYNC) {\r
515 Urb->EvtRing = &Xhc->AsynIntTrEventRing;\r
516 } else if(Urb->Ep.Type == XHC_INT_TRANSFER_SYNC){\r
517 Urb->EvtRing = &Xhc->IntTrEventRing;\r
518 } else {\r
519 DEBUG ((EFI_D_ERROR, "EP Interrupt type error!\n"));\r
520 ASSERT(FALSE);\r
521 }\r
522 XhcSyncEventRing (Xhc, Urb->EvtRing);\r
523 Urb->EvtTrbStart = Urb->EvtRing->EventRingEnqueue;\r
524\r
525 TotalLen = 0;\r
526 Len = 0;\r
527 TrbNum = 0;\r
528 TrbStart = EPRing->RingEnqueue;\r
529 while (TotalLen < Urb->DataLen) {\r
530 if ((TotalLen + 0x10000) >= Urb->DataLen) {\r
531 Len = Urb->DataLen - TotalLen;\r
532 } else {\r
533 Len = 0x10000;\r
534 }\r
535 TrbStart = EPRing->RingEnqueue;\r
536 ((TRANSFER_TRB_NORMAL *) TrbStart)->TRBPtrLo = XHC_LOW_32BIT((UINT8 *) Urb->Data + TotalLen);\r
537 ((TRANSFER_TRB_NORMAL *) TrbStart)->TRBPtrHi = XHC_HIGH_32BIT((UINT8 *) Urb->Data + TotalLen);\r
538 ((TRANSFER_TRB_NORMAL *) TrbStart)->Lenth = (UINT32) Len;\r
539 ((TRANSFER_TRB_NORMAL *) TrbStart)->TDSize = 0;\r
540 ((TRANSFER_TRB_NORMAL *) TrbStart)->IntTarget = Urb->EvtRing->EventInterrupter;\r
541 ((TRANSFER_TRB_NORMAL *) TrbStart)->ISP = 1;\r
542 ((TRANSFER_TRB_NORMAL *) TrbStart)->IOC = 1;\r
543 ((TRANSFER_TRB_NORMAL *) TrbStart)->Type = TRB_TYPE_NORMAL;\r
544 //\r
545 // Update the cycle bit\r
546 //\r
547 ((TRANSFER_TRB_NORMAL *) TrbStart)->CycleBit = EPRing->RingPCS & BIT0;\r
548\r
549 XhcSyncTrsRing (Xhc, EPRing);\r
550 TrbNum++;\r
551 TotalLen += Len;\r
552 }\r
553\r
554 Urb->TrbNum = TrbNum;\r
555 Urb->TrbEnd = TrbStart;\r
556 break;\r
557\r
558 default:\r
559 DEBUG ((EFI_D_INFO, "Not supported EPType 0x%x!\n",EPType));\r
560 ASSERT (FALSE);\r
561 break;\r
562 }\r
563\r
564 return EFI_SUCCESS;\r
565}\r
566\r
567\r
568/**\r
569 Initialize the XHCI host controller for schedule.\r
570\r
571 @param Xhc The XHCI device to be initialized.\r
572\r
573**/\r
574VOID\r
575XhcInitSched (\r
576 IN USB_XHCI_DEV *Xhc\r
577 )\r
578{\r
579 VOID *Dcbaa;\r
580 UINT64 CmdRing;\r
581 UINTN Entries;\r
582 UINT32 MaxScratchpadBufs;\r
583 UINT64 *ScratchBuf;\r
584 UINT64 *ScratchEntryBuf;\r
585 UINT32 Index;\r
586\r
587 //\r
588 // Program the Max Device Slots Enabled (MaxSlotsEn) field in the CONFIG register (5.4.7)\r
589 // to enable the device slots that system software is going to use.\r
590 //\r
591 Xhc->MaxSlotsEn = Xhc->HcSParams1.Data.MaxSlots;\r
592 ASSERT (Xhc->MaxSlotsEn >= 1 && Xhc->MaxSlotsEn <= 255);\r
593 XhcWriteOpReg (Xhc, XHC_CONFIG_OFFSET, Xhc->MaxSlotsEn);\r
594\r
595 //\r
596 // The Device Context Base Address Array entry associated with each allocated Device Slot\r
597 // shall contain a 64-bit pointer to the base of the associated Device Context.\r
598 // The Device Context Base Address Array shall contain MaxSlotsEn + 1 entries.\r
599 // Software shall set Device Context Base Address Array entries for unallocated Device Slots to '0'.\r
600 //\r
601 Entries = (Xhc->MaxSlotsEn + 1) * sizeof(UINT64);\r
602 Dcbaa = AllocateAlignedZeroPool(Entries, 64);\r
603 ASSERT (Dcbaa != NULL);\r
604\r
605 //\r
606 // A Scratchpad Buffer is a PAGESIZE block of system memory located on a PAGESIZE boundary.\r
607 // System software shall allocate the Scratchpad Buffer(s) before placing the xHC in to Run\r
608 // mode (Run/Stop(R/S) ='1').\r
609 //\r
610 MaxScratchpadBufs = ((Xhc->HcSParams2.Data.ScratchBufHi) << 5) | (Xhc->HcSParams2.Data.ScratchBufLo);\r
611 Xhc->MaxScratchpadBufs = MaxScratchpadBufs;\r
ce9b5900 612 ASSERT (MaxScratchpadBufs <= 1023);\r
92870c98 613 if (MaxScratchpadBufs != 0) {\r
614 ScratchBuf = AllocateAlignedZeroPool(MaxScratchpadBufs * sizeof (UINT64), Xhc->PageSize);\r
615 ASSERT (ScratchBuf != NULL);\r
616 Xhc->ScratchBuf = ScratchBuf;\r
617\r
618 for (Index = 0; Index < MaxScratchpadBufs; Index++) {\r
619 ScratchEntryBuf = AllocateAlignedZeroPool(Xhc->PageSize, Xhc->PageSize);\r
620 *ScratchBuf++ = (UINT64)(UINTN)ScratchEntryBuf;\r
621 }\r
622\r
623 //\r
624 // The Scratchpad Buffer Array contains pointers to the Scratchpad Buffers. Entry 0 of the\r
625 // Device Context Base Address Array points to the Scratchpad Buffer Array.\r
626 //\r
627 *(UINT64 *)Dcbaa = (UINT64)(UINTN)Xhc->ScratchBuf;\r
628 }\r
629\r
630 //\r
631 // Program the Device Context Base Address Array Pointer (DCBAAP) register (5.4.6) with\r
632 // a 64-bit address pointing to where the Device Context Base Address Array is located.\r
633 //\r
634 Xhc->DCBAA = (UINT64 *)(UINTN)Dcbaa;\r
ce9b5900 635 XhcWriteOpReg64 (Xhc, XHC_DCBAAP_OFFSET, (UINT64)(UINTN)Xhc->DCBAA);\r
636 DEBUG ((EFI_D_INFO, "XhcInitSched:DCBAA=0x%x\n", (UINT64)(UINTN)Xhc->DCBAA));\r
92870c98 637\r
638 //\r
639 // Define the Command Ring Dequeue Pointer by programming the Command Ring Control Register\r
640 // (5.4.5) with a 64-bit address pointing to the starting address of the first TRB of the Command Ring.\r
641 // Note: The Command Ring is 64 byte aligned, so the low order 6 bits of the Command Ring Pointer shall\r
642 // always be '0'.\r
643 //\r
644 CreateTransferRing (Xhc, CMD_RING_TRB_NUMBER, &Xhc->CmdRing);\r
645 //\r
646 // The xHC uses the Enqueue Pointer to determine when a Transfer Ring is empty. As it fetches TRBs from a\r
647 // Transfer Ring it checks for a Cycle bit transition. If a transition detected, the ring is empty.\r
648 // So we set RCS as inverted PCS init value to let Command Ring empty\r
649 //\r
650 CmdRing = (UINT64)(UINTN)Xhc->CmdRing.RingSeg0;\r
651 ASSERT ((CmdRing & 0x3F) == 0);\r
652 CmdRing |= XHC_CRCR_RCS;\r
653 XhcWriteOpReg64 (Xhc, XHC_CRCR_OFFSET, CmdRing);\r
654\r
655 DEBUG ((EFI_D_INFO, "XhcInitSched:XHC_CRCR=0x%x\n", Xhc->CmdRing.RingSeg0));\r
656\r
657 //\r
658 // Disable the 'interrupter enable' bit in USB_CMD\r
659 // and clear IE & IP bit in all Interrupter X Management Registers.\r
660 //\r
661 XhcClearOpRegBit (Xhc, XHC_USBCMD_OFFSET, XHC_USBCMD_INTE);\r
662 for (Index = 0; Index < (UINT16)(Xhc->HcSParams1.Data.MaxIntrs); Index++) {\r
663 XhcClearRuntimeRegBit (Xhc, XHC_IMAN_OFFSET + (Index * 32), XHC_IMAN_IE);\r
664 XhcSetRuntimeRegBit (Xhc, XHC_IMAN_OFFSET + (Index * 32), XHC_IMAN_IP);\r
665 }\r
666\r
667 //\r
668 // Allocate EventRing for Cmd, Ctrl, Bulk, Interrupt, AsynInterrupt transfer\r
669 //\r
670 CreateEventRing (Xhc, CMD_INTER, &Xhc->CmdEventRing);\r
671 CreateEventRing (Xhc, CTRL_INTER, &Xhc->CtrlTrEventRing);\r
672 CreateEventRing (Xhc, BULK_INTER, &Xhc->BulkTrEventRing);\r
673 CreateEventRing (Xhc, INT_INTER, &Xhc->IntTrEventRing);\r
674 CreateEventRing (Xhc, INT_INTER_ASYNC, &Xhc->AsynIntTrEventRing);\r
675}\r
676\r
677/**\r
678 System software shall use a Reset Endpoint Command (section 4.11.4.7) to remove the Halted\r
679 condition in the xHC. After the successful completion of the Reset Endpoint Command, the Endpoint\r
680 Context is transitioned from the Halted to the Stopped state and the Transfer Ring of the endpoint is\r
681 reenabled. The next write to the Doorbell of the Endpoint will transition the Endpoint Context from the\r
682 Stopped to the Running state.\r
683\r
684 @param Xhc The XHCI device.\r
685 @param Urb The urb which makes the endpoint halted.\r
686\r
687 @retval EFI_SUCCESS The recovery is successful.\r
688 @retval Others Failed to recovery halted endpoint.\r
689\r
690**/\r
691EFI_STATUS\r
692EFIAPI\r
693XhcRecoverHaltedEndpoint (\r
694 IN USB_XHCI_DEV *Xhc,\r
695 IN URB *Urb\r
696 )\r
697{\r
698 EFI_STATUS Status;\r
699 EVT_TRB_COMMAND *EvtTrb;\r
700 CMD_TRB_RESET_ED CmdTrbResetED;\r
701 CMD_SET_TR_DEQ CmdSetTRDeq;\r
702 UINT8 Dci;\r
703 UINT8 SlotId;\r
704\r
705 Status = EFI_SUCCESS;\r
706 SlotId = XhcDevAddrToSlotId(Urb->Ep.DevAddr);\r
ce9b5900 707 Dci = XhcEndpointToDci(Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction));\r
92870c98 708\r
709 DEBUG ((EFI_D_INFO, "Recovery Halted Slot = %x,Dci = %x\n", SlotId, Dci));\r
710\r
711 //\r
712 // 1) Send Reset endpoint command to transit from halt to stop state\r
713 //\r
714 ZeroMem (&CmdTrbResetED, sizeof (CmdTrbResetED));\r
715 CmdTrbResetED.CycleBit = 1;\r
716 CmdTrbResetED.Type = TRB_TYPE_RESET_ENDPOINT;\r
717 CmdTrbResetED.EDID = Dci;\r
718 CmdTrbResetED.SlotId = SlotId;\r
719 Status = XhcCmdTransfer (\r
720 Xhc,\r
721 (TRB *) (UINTN) &CmdTrbResetED,\r
722 XHC_GENERIC_TIMEOUT,\r
723 (TRB **) (UINTN) &EvtTrb\r
724 );\r
725 ASSERT (!EFI_ERROR(Status));\r
726\r
727 //\r
728 // 2)Set dequeue pointer\r
729 //\r
730 ZeroMem (&CmdSetTRDeq, sizeof (CmdSetTRDeq));\r
731 CmdSetTRDeq.PtrLo = XHC_LOW_32BIT (Urb->Ring->RingEnqueue) | Urb->Ring->RingPCS;\r
732 CmdSetTRDeq.PtrHi = XHC_HIGH_32BIT (Urb->Ring->RingEnqueue);\r
733 CmdSetTRDeq.CycleBit = 1;\r
734 CmdSetTRDeq.Type = TRB_TYPE_SET_TR_DEQUE;\r
735 CmdSetTRDeq.Endpoint = Dci;\r
736 CmdSetTRDeq.SlotId = SlotId;\r
737 Status = XhcCmdTransfer (\r
738 Xhc,\r
739 (TRB *) (UINTN) &CmdSetTRDeq,\r
740 XHC_GENERIC_TIMEOUT,\r
741 (TRB **) (UINTN) &EvtTrb\r
742 );\r
743 ASSERT (!EFI_ERROR(Status));\r
744\r
745 //\r
746 // 3)Ring the doorbell to transit from stop to active\r
747 //\r
748 XhcRingDoorBell (Xhc, SlotId, Dci);\r
749\r
750 return Status;\r
751}\r
752\r
753/**\r
754 Create XHCI event ring.\r
755\r
756 @param Xhc The XHCI device.\r
757 @param EventInterrupter The interrupter of event.\r
758 @param EventRing The created event ring.\r
759\r
760**/\r
761VOID\r
92870c98 762CreateEventRing (\r
763 IN USB_XHCI_DEV *Xhc,\r
764 IN UINT8 EventInterrupter,\r
765 OUT EVENT_RING *EventRing\r
766 )\r
767{\r
768 VOID *Buf;\r
769 EVENT_RING_SEG_TABLE_ENTRY *ERSTBase;\r
770\r
771 ASSERT (EventRing != NULL);\r
772\r
773 Buf = AllocateAlignedZeroPool(sizeof (TRB) * EVENT_RING_TRB_NUMBER, 64);\r
774 ASSERT (Buf != NULL);\r
775 ASSERT (((UINTN) Buf & 0x3F) == 0);\r
776\r
777 EventRing->EventRingSeg0 = Buf;\r
778 EventRing->EventInterrupter = EventInterrupter;\r
779 EventRing->TrbNumber = EVENT_RING_TRB_NUMBER;\r
780 EventRing->EventRingDequeue = (TRB *) EventRing->EventRingSeg0;\r
781 EventRing->EventRingEnqueue = (TRB *) EventRing->EventRingSeg0;\r
782 //\r
783 // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1'\r
784 // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring.\r
785 //\r
786 EventRing->EventRingCCS = 1;\r
787\r
788 Buf = AllocateAlignedZeroPool(sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER, 64);\r
789 ASSERT (Buf != NULL);\r
790 ASSERT (((UINTN) Buf & 0x3F) == 0);\r
791\r
792 ERSTBase = (EVENT_RING_SEG_TABLE_ENTRY *) Buf;\r
793 EventRing->ERSTBase = ERSTBase;\r
794 ERSTBase->PtrLo = XHC_LOW_32BIT (EventRing->EventRingSeg0);\r
795 ERSTBase->PtrHi = XHC_HIGH_32BIT (EventRing->EventRingSeg0);\r
796 ERSTBase->RingTrbSize = EVENT_RING_TRB_NUMBER;\r
797\r
798 //\r
799 // Program the Interrupter Event Ring Segment Table Size (ERSTSZ) register (5.5.2.3.1)\r
800 //\r
801 XhcWriteRuntimeReg (\r
802 Xhc,\r
803 XHC_ERSTSZ_OFFSET + (32 * EventRing->EventInterrupter),\r
804 ERST_NUMBER\r
805 );\r
806 //\r
807 // Program the Interrupter Event Ring Dequeue Pointer (ERDP) register (5.5.2.3.3)\r
808 //\r
809 XhcWriteRuntimeReg64 (\r
810 Xhc,\r
811 XHC_ERDP_OFFSET + (32 * EventRing->EventInterrupter),\r
ce9b5900 812 (UINT64)(UINTN)EventRing->EventRingDequeue\r
92870c98 813 );\r
814 //\r
815 // Program the Interrupter Event Ring Segment Table Base Address (ERSTBA) register(5.5.2.3.2)\r
816 //\r
817 XhcWriteRuntimeReg64 (\r
818 Xhc,\r
819 XHC_ERSTBA_OFFSET + (32 * EventRing->EventInterrupter),\r
ce9b5900 820 (UINT64)(UINTN)ERSTBase\r
92870c98 821 );\r
822 //\r
823 // Need set IMAN IE bit to enble the ring interrupt\r
824 //\r
825 XhcSetRuntimeRegBit (Xhc, XHC_IMAN_OFFSET + (32 * EventRing->EventInterrupter), XHC_IMAN_IE);\r
826}\r
827\r
828/**\r
829 Create XHCI transfer ring.\r
830\r
831 @param Xhc The XHCI device.\r
832 @param TrbNum The number of TRB in the ring.\r
833 @param TransferRing The created transfer ring.\r
834\r
835**/\r
836VOID\r
92870c98 837CreateTransferRing (\r
838 IN USB_XHCI_DEV *Xhc,\r
839 IN UINTN TrbNum,\r
840 OUT TRANSFER_RING *TransferRing\r
841 )\r
842{\r
843 VOID *Buf;\r
844 LNK_TRB *EndTrb;\r
845\r
846 Buf = AllocateAlignedZeroPool(sizeof (TRB) * TrbNum, 64);\r
847 ASSERT (Buf != NULL);\r
848 ASSERT (((UINTN) Buf & 0x3F) == 0);\r
849\r
850 TransferRing->RingSeg0 = Buf;\r
851 TransferRing->TrbNumber = TrbNum;\r
852 TransferRing->RingEnqueue = (TRB *) TransferRing->RingSeg0;\r
853 TransferRing->RingDequeue = (TRB *) TransferRing->RingSeg0;\r
854 TransferRing->RingPCS = 1;\r
855 //\r
856 // 4.9.2 Transfer Ring Management\r
857 // To form a ring (or circular queue) a Link TRB may be inserted at the end of a ring to\r
858 // point to the first TRB in the ring.\r
859 //\r
860 EndTrb = (LNK_TRB*) ((UINTN)Buf + sizeof (TRB) * (TrbNum - 1));\r
861 EndTrb->Type = TRB_TYPE_LINK;\r
862 EndTrb->PtrLo = XHC_LOW_32BIT (Buf);\r
863 EndTrb->PtrHi = XHC_HIGH_32BIT (Buf);\r
864 //\r
865 // Toggle Cycle (TC). When set to '1', the xHC shall toggle its interpretation of the Cycle bit.\r
866 //\r
867 EndTrb->TC = 1;\r
868 //\r
869 // Set Cycle bit as other TRB PCS init value\r
870 //\r
871 EndTrb->CycleBit = 0;\r
872}\r
873\r
874/**\r
875 Free XHCI event ring.\r
876\r
877 @param Xhc The XHCI device.\r
878 @param EventRing The event ring to be freed.\r
879\r
880**/\r
881EFI_STATUS\r
882EFIAPI\r
883XhcFreeEventRing (\r
884 IN USB_XHCI_DEV *Xhc,\r
885 IN EVENT_RING *EventRing\r
886)\r
887{\r
888 UINT8 Index;\r
889 EVENT_RING_SEG_TABLE_ENTRY *TablePtr;\r
890 VOID *RingBuf;\r
891 EVENT_RING_SEG_TABLE_ENTRY *EventRingPtr;\r
892 UINTN InterrupterTarget;\r
893\r
894 if(EventRing->EventRingSeg0 == NULL) {\r
895 return EFI_SUCCESS;\r
896 }\r
897\r
898 InterrupterTarget = EventRing->EventInterrupter;\r
899 //\r
900 // Get the Event Ring Segment Table base address\r
901 //\r
902 TablePtr = (EVENT_RING_SEG_TABLE_ENTRY *)(EventRing->ERSTBase);\r
903\r
904 //\r
905 // Get all the TRBs Ring and release\r
906 //\r
907 for (Index = 0; Index < ERST_NUMBER; Index++) {\r
908 EventRingPtr = TablePtr + Index;\r
909 RingBuf = (VOID *)(UINTN)(EventRingPtr->PtrLo | ((UINT64)EventRingPtr->PtrHi << 32));\r
910\r
911 if(RingBuf != NULL) {\r
912 FreeAlignedPool (RingBuf);\r
913 ZeroMem (EventRingPtr, sizeof (EVENT_RING_SEG_TABLE_ENTRY));\r
914 }\r
915 }\r
916\r
917 FreeAlignedPool (TablePtr);\r
918 return EFI_SUCCESS;\r
919}\r
920\r
921/**\r
922 Free the resouce allocated at initializing schedule.\r
923\r
924 @param Xhc The XHCI device.\r
925\r
926**/\r
927VOID\r
928XhcFreeSched (\r
929 IN USB_XHCI_DEV *Xhc\r
930 )\r
931{\r
932 UINT32 Index;\r
933\r
934 if (Xhc->ScratchBuf != NULL) {\r
935 for (Index = 0; Index < Xhc->MaxScratchpadBufs; Index++) {\r
936 FreeAlignedPool ((VOID*)(UINTN)*Xhc->ScratchBuf++);\r
937 }\r
938 }\r
939\r
940 if (Xhc->DCBAA != NULL) {\r
941 FreeAlignedPool (Xhc->DCBAA);\r
942 Xhc->DCBAA = NULL;\r
943 }\r
944\r
945 if (Xhc->CmdRing.RingSeg0 != NULL){\r
946 FreeAlignedPool (Xhc->CmdRing.RingSeg0);\r
947 Xhc->CmdRing.RingSeg0 = NULL;\r
948 }\r
949 XhcFreeEventRing (Xhc,&Xhc->CmdEventRing);\r
950 XhcFreeEventRing (Xhc,&Xhc->CtrlTrEventRing);\r
951 XhcFreeEventRing (Xhc,&Xhc->BulkTrEventRing);\r
952 XhcFreeEventRing (Xhc,&Xhc->AsynIntTrEventRing);\r
953 XhcFreeEventRing (Xhc,&Xhc->IntTrEventRing);\r
954}\r
955\r
956/**\r
957 Check if it is ring TRB.\r
958\r
959 @param Ring The transfer ring\r
960 @param Trb The TRB to check if it's in the transfer ring\r
961\r
962 @retval TRUE It is in the ring\r
963 @retval FALSE It is not in the ring\r
964\r
965**/\r
966BOOLEAN\r
967IsTransferRingTrb (\r
968 IN TRANSFER_RING *Ring,\r
969 IN TRB *Trb\r
970 )\r
971{\r
972 BOOLEAN Flag;\r
973 TRB *Trb1;\r
974 UINTN Index;\r
975\r
976 Trb1 = Ring->RingSeg0;\r
977 Flag = FALSE;\r
978\r
979 ASSERT (Ring->TrbNumber == CMD_RING_TRB_NUMBER || Ring->TrbNumber == TR_RING_TRB_NUMBER);\r
980\r
981 for (Index = 0; Index < Ring->TrbNumber; Index++) {\r
982 if (Trb == Trb1) {\r
983 Flag = TRUE;\r
984 break;\r
985 }\r
986 Trb1++;\r
987 }\r
988\r
989 return Flag;\r
990}\r
991\r
992/**\r
993 Check the URB's execution result and update the URB's\r
994 result accordingly.\r
995\r
996 @param Xhc The XHCI device.\r
997 @param Urb The URB to check result.\r
998\r
999 @return Whether the result of URB transfer is finialized.\r
1000\r
1001**/\r
1002EFI_STATUS\r
1003XhcCheckUrbResult (\r
1004 IN USB_XHCI_DEV *Xhc,\r
1005 IN URB *Urb\r
1006 )\r
1007{\r
1008 BOOLEAN StartDone;\r
1009 BOOLEAN EndDone;\r
1010 EVT_TRB_TRANSFER *EvtTrb;\r
1011 TRB *TRBPtr;\r
1012 UINTN Index;\r
1013 UINT8 TRBType;\r
1014 EFI_STATUS Status;\r
1015\r
1016 ASSERT ((Xhc != NULL) && (Urb != NULL));\r
1017\r
1018 Urb->Completed = 0;\r
1019 Urb->Result = EFI_USB_NOERROR;\r
1020 Status = EFI_SUCCESS;\r
1021 EvtTrb = NULL;\r
1022\r
1023 if (XhcIsHalt (Xhc) || XhcIsSysError (Xhc)) {\r
1024 Urb->Result |= EFI_USB_ERR_SYSTEM;\r
1025 Status = EFI_DEVICE_ERROR;\r
1026 goto EXIT;\r
1027 }\r
1028\r
1029 //\r
1030 // Restore the EventRingDequeue and poll the transfer event ring from beginning\r
1031 //\r
1032 StartDone = FALSE;\r
1033 EndDone = FALSE;\r
1034 Urb->EvtRing->EventRingDequeue = Urb->EvtTrbStart;\r
1035 for (Index = 0; Index < Urb->EvtRing->TrbNumber; Index++) {\r
1036 XhcSyncEventRing (Xhc, Urb->EvtRing);\r
ce9b5900 1037 Status = XhcCheckNewEvent (Xhc, Urb->EvtRing, ((TRB **)&EvtTrb));\r
92870c98 1038 if (Status == EFI_NOT_READY) {\r
1039 Urb->Result |= EFI_USB_ERR_TIMEOUT;\r
1040 goto EXIT;\r
1041 }\r
1042\r
1043 TRBPtr = (TRB *)(UINTN)(EvtTrb->TRBPtrLo | (UINT64) EvtTrb->TRBPtrHi << 32);\r
1044\r
1045 switch (EvtTrb->Completcode) {\r
1046 case TRB_COMPLETION_STALL_ERROR:\r
1047 Urb->Result |= EFI_USB_ERR_STALL;\r
1048 Status = EFI_DEVICE_ERROR;\r
1049 DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: STALL_ERROR! Completcode = %x\n",EvtTrb->Completcode));\r
1050 goto EXIT;\r
1051 break;\r
1052\r
1053 case TRB_COMPLETION_BABBLE_ERROR:\r
1054 Urb->Result |= EFI_USB_ERR_BABBLE;\r
1055 Status = EFI_DEVICE_ERROR;\r
1056 DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: BABBLE_ERROR! Completcode = %x\n",EvtTrb->Completcode));\r
1057 goto EXIT;\r
1058 break;\r
1059\r
1060 case TRB_COMPLETION_DATA_BUFFER_ERROR:\r
1061 Urb->Result |= EFI_USB_ERR_BUFFER;\r
1062 Status = EFI_DEVICE_ERROR;\r
1063 DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: ERR_BUFFER! Completcode = %x\n",EvtTrb->Completcode));\r
1064 goto EXIT;\r
1065 break;\r
1066\r
1067 case TRB_COMPLETION_USB_TRANSACTION_ERROR:\r
1068 Urb->Result |= EFI_USB_ERR_TIMEOUT;\r
1069 Status = EFI_DEVICE_ERROR;\r
1070 DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: TRANSACTION_ERROR! Completcode = %x\n",EvtTrb->Completcode));\r
1071 goto EXIT;\r
1072 break;\r
1073\r
1074 case TRB_COMPLETION_SHORT_PACKET:\r
1075 case TRB_COMPLETION_SUCCESS:\r
1076 if (IsTransferRingTrb (Urb->Ring, TRBPtr)) {\r
1077 if (EvtTrb->Completcode == TRB_COMPLETION_SHORT_PACKET) {\r
1078 DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: short packet happens!\n"));\r
1079 }\r
1080 TRBType = (UINT8) (TRBPtr->Type);\r
1081 if ((TRBType == TRB_TYPE_DATA_STAGE) ||\r
1082 (TRBType == TRB_TYPE_NORMAL) ||\r
1083 (TRBType == TRB_TYPE_ISOCH)) {\r
1084 Urb->Completed += (Urb->DataLen - EvtTrb->Lenth);\r
1085 }\r
1086 }\r
1087 Status = EFI_SUCCESS;\r
1088 break;\r
1089\r
1090 default:\r
1091 DEBUG ((EFI_D_ERROR, "Transfer Default Error Occur! Completcode = 0x%x!\n",EvtTrb->Completcode));\r
1092 Urb->Result |= EFI_USB_ERR_TIMEOUT;\r
1093 Status = EFI_DEVICE_ERROR;\r
1094 goto EXIT;\r
1095 break;\r
1096 }\r
1097\r
1098 //\r
1099 // Only check first and end Trb event address\r
1100 //\r
1101 if (TRBPtr == Urb->TrbStart) {\r
1102 StartDone = TRUE;\r
1103 }\r
1104\r
1105 if (TRBPtr == Urb->TrbEnd) {\r
1106 EndDone = TRUE;\r
1107 }\r
1108\r
1109 if (StartDone && EndDone) {\r
1110 break;\r
1111 }\r
1112 }\r
1113\r
1114EXIT:\r
1115 return Status;\r
1116}\r
1117\r
1118\r
1119/**\r
1120 Execute the transfer by polling the URB. This is a synchronous operation.\r
1121\r
1122 @param Xhc The XHCI device.\r
1123 @param CmdTransfer The executed URB is for cmd transfer or not.\r
1124 @param Urb The URB to execute.\r
1125 @param TimeOut The time to wait before abort, in millisecond.\r
1126\r
1127 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.\r
1128 @return EFI_TIMEOUT The transfer failed due to time out.\r
1129 @return EFI_SUCCESS The transfer finished OK.\r
1130\r
1131**/\r
1132EFI_STATUS\r
1133XhcExecTransfer (\r
1134 IN USB_XHCI_DEV *Xhc,\r
1135 IN BOOLEAN CmdTransfer,\r
1136 IN URB *Urb,\r
1137 IN UINTN TimeOut\r
1138 )\r
1139{\r
1140 EFI_STATUS Status;\r
1141 UINTN Index;\r
1142 UINTN Loop;\r
1143 UINT8 SlotId;\r
1144 UINT8 Dci;\r
1145\r
1146 if (CmdTransfer) {\r
1147 SlotId = 0;\r
1148 Dci = 0;\r
1149 } else {\r
1150 SlotId = XhcDevAddrToSlotId(Urb->Ep.DevAddr);\r
ce9b5900 1151 Dci = XhcEndpointToDci(Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction));\r
92870c98 1152 }\r
1153\r
1154 Status = EFI_SUCCESS;\r
1155 Loop = (TimeOut * XHC_1_MILLISECOND / XHC_SYNC_POLL_INTERVAL) + 1;\r
1156 if (TimeOut == 0) {\r
1157 Loop = 0xFFFFFFFF;\r
1158 }\r
1159\r
1160 XhcRingDoorBell (Xhc, SlotId, Dci);\r
1161\r
1162 for (Index = 0; Index < Loop; Index++) {\r
1163 Status = XhcCheckUrbResult (Xhc, Urb);\r
1164 if ((Status != EFI_NOT_READY)) {\r
1165 break;\r
1166 }\r
1167 gBS->Stall (XHC_SYNC_POLL_INTERVAL);\r
1168 }\r
1169\r
1170 return Status;\r
1171}\r
1172\r
1173/**\r
1174 Delete a single asynchronous interrupt transfer for\r
1175 the device and endpoint.\r
1176\r
1177 @param Xhc The XHCI device.\r
1178 @param DevAddr The address of the target device.\r
1179 @param EpNum The endpoint of the target.\r
1180\r
1181 @retval EFI_SUCCESS An asynchronous transfer is removed.\r
1182 @retval EFI_NOT_FOUND No transfer for the device is found.\r
1183\r
1184**/\r
1185EFI_STATUS\r
1186XhciDelAsyncIntTransfer (\r
1187 IN USB_XHCI_DEV *Xhc,\r
1188 IN UINT8 DevAddr,\r
1189 IN UINT8 EpNum\r
1190 )\r
1191{\r
1192 LIST_ENTRY *Entry;\r
1193 LIST_ENTRY *Next;\r
1194 URB *Urb;\r
1195 EFI_USB_DATA_DIRECTION Direction;\r
1196 BOOLEAN Found;\r
1197\r
1198 Direction = ((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut;\r
1199 EpNum &= 0x0F;\r
1200\r
1201 Found = FALSE;\r
1202 Urb = NULL;\r
1203\r
1204 EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Xhc->AsyncIntTransfers) {\r
1205 Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);\r
1206 if ((Urb->Ep.DevAddr == DevAddr) &&\r
1207 (Urb->Ep.EpAddr == EpNum) &&\r
1208 (Urb->Ep.Direction == Direction)) {\r
1209 RemoveEntryList (&Urb->UrbList);\r
1210 FreePool (Urb->Data);\r
1211 FreePool (Urb);\r
1212 return EFI_SUCCESS;\r
1213 }\r
1214 }\r
1215\r
1216 return EFI_NOT_FOUND;\r
1217}\r
1218\r
1219/**\r
1220 Remove all the asynchronous interrutp transfers.\r
1221\r
1222 @param Xhc The XHCI device.\r
1223\r
1224**/\r
1225VOID\r
1226XhciDelAllAsyncIntTransfers (\r
1227 IN USB_XHCI_DEV *Xhc\r
1228 )\r
1229{\r
1230 LIST_ENTRY *Entry;\r
1231 LIST_ENTRY *Next;\r
1232 URB *Urb;\r
1233\r
1234 EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Xhc->AsyncIntTransfers) {\r
1235 Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);\r
1236 RemoveEntryList (&Urb->UrbList);\r
1237 FreePool (Urb->Data);\r
1238 FreePool (Urb);\r
1239 }\r
1240}\r
1241\r
1242/**\r
1243 Update the queue head for next round of asynchronous transfer\r
1244\r
1245 @param Xhc The XHCI device.\r
1246 @param Urb The URB to update\r
1247\r
1248**/\r
1249VOID\r
1250XhcUpdateAsyncRequest (\r
1251 IN USB_XHCI_DEV* Xhc,\r
1252 IN URB *Urb\r
1253 )\r
1254{\r
1255 EFI_STATUS Status;\r
1256\r
1257 if (Urb->Result == EFI_USB_NOERROR) {\r
1258 Status = XhcCreateTransferTrb (Xhc, Urb);\r
1259 ASSERT_EFI_ERROR (Status);\r
1260 Status = RingIntTransferDoorBell (Xhc, Urb);\r
1261 ASSERT_EFI_ERROR (Status);\r
1262 }\r
1263}\r
1264\r
1265\r
1266/**\r
1267 Interrupt transfer periodic check handler.\r
1268\r
1269 @param Event Interrupt event.\r
1270 @param Context Pointer to USB_XHCI_DEV.\r
1271\r
1272**/\r
1273VOID\r
1274EFIAPI\r
1275XhcMonitorAsyncRequests (\r
1276 IN EFI_EVENT Event,\r
1277 IN VOID *Context\r
1278 )\r
1279{\r
1280 USB_XHCI_DEV *Xhc;\r
1281 LIST_ENTRY *Entry;\r
1282 LIST_ENTRY *Next;\r
1283 UINT8 *ProcBuf;\r
1284 URB *Urb;\r
1285 UINT8 SlotId;\r
1286 EFI_STATUS Status;\r
1287 EFI_TPL OldTpl;\r
1288\r
1289 OldTpl = gBS->RaiseTPL (XHC_TPL);\r
1290\r
1291 Xhc = (USB_XHCI_DEV*) Context;\r
1292\r
1293 EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Xhc->AsyncIntTransfers) {\r
1294 Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);\r
1295\r
1296 //\r
1297 // Make sure that the device is available before every check.\r
1298 //\r
1299 SlotId = XhcDevAddrToSlotId(Urb->Ep.DevAddr);\r
1300 if (SlotId == 0) {\r
1301 continue;\r
1302 }\r
1303\r
1304 //\r
1305 // Check the result of URB execution. If it is still\r
1306 // active, check the next one.\r
1307 //\r
1308 Status = XhcCheckUrbResult (Xhc, Urb);\r
1309\r
1310 if (Status == EFI_NOT_READY) {\r
1311 continue;\r
1312 }\r
1313\r
1314 //\r
1315 // Allocate a buffer then copy the transferred data for user.\r
1316 // If failed to allocate the buffer, update the URB for next\r
1317 // round of transfer. Ignore the data of this round.\r
1318 //\r
1319 ProcBuf = NULL;\r
1320 if (Urb->Result == EFI_USB_NOERROR) {\r
1321 ASSERT (Urb->Completed <= Urb->DataLen);\r
1322\r
1323 ProcBuf = AllocatePool (Urb->Completed);\r
1324\r
1325 if (ProcBuf == NULL) {\r
1326 XhcUpdateAsyncRequest (Xhc, Urb);\r
1327 continue;\r
1328 }\r
1329\r
1330 CopyMem (ProcBuf, Urb->Data, Urb->Completed);\r
1331 }\r
1332\r
1333 XhcUpdateAsyncRequest (Xhc, Urb);\r
1334\r
1335 //\r
1336 // Leave error recovery to its related device driver. A\r
1337 // common case of the error recovery is to re-submit the\r
1338 // interrupt transfer which is linked to the head of the\r
1339 // list. This function scans from head to tail. So the\r
1340 // re-submitted interrupt transfer's callback function\r
1341 // will not be called again in this round. Don't touch this\r
1342 // URB after the callback, it may have been removed by the\r
1343 // callback.\r
1344 //\r
1345 if (Urb->Callback != NULL) {\r
1346 //\r
1347 // Restore the old TPL, USB bus maybe connect device in\r
1348 // his callback. Some drivers may has a lower TPL restriction.\r
1349 //\r
1350 gBS->RestoreTPL (OldTpl);\r
1351 (Urb->Callback) (ProcBuf, Urb->Completed, Urb->Context, Urb->Result);\r
1352 OldTpl = gBS->RaiseTPL (XHC_TPL);\r
1353 }\r
1354\r
1355 if (ProcBuf != NULL) {\r
1356 gBS->FreePool (ProcBuf);\r
1357 }\r
1358 }\r
1359 gBS->RestoreTPL (OldTpl);\r
1360}\r
1361\r
1362/**\r
1363 Monitor the port status change. Enable/Disable device slot if there is a device attached/detached.\r
1364\r
1365 @param Xhc The XHCI device.\r
1366 @param ParentRouteChart The route string pointed to the parent device if it exists.\r
1367 @param Port The port to be polled.\r
1368 @param PortState The port state.\r
1369\r
1370 @retval EFI_SUCCESS Successfully enable/disable device slot according to port state.\r
1371 @retval Others Should not appear.\r
1372\r
1373**/\r
1374EFI_STATUS\r
1375EFIAPI\r
1376XhcPollPortStatusChange (\r
1377 IN USB_XHCI_DEV* Xhc,\r
1378 IN USB_DEV_ROUTE ParentRouteChart,\r
1379 IN UINT8 Port,\r
1380 IN EFI_USB_PORT_STATUS *PortState\r
1381 )\r
1382{\r
1383 EFI_STATUS Status;\r
1384 UINT8 Speed;\r
1385 UINT8 SlotId;\r
1386 USB_DEV_ROUTE RouteChart;\r
1387\r
1388 Status = EFI_SUCCESS;\r
1389\r
1390 if (ParentRouteChart.Dword == 0) {\r
1391 RouteChart.Field.RouteString = 0;\r
1392 RouteChart.Field.RootPortNum = Port + 1;\r
1393 RouteChart.Field.TierNum = 1;\r
1394 } else {\r
1395 if(Port < 14) {\r
1396 RouteChart.Field.RouteString = ParentRouteChart.Field.RouteString | (Port << (4 * (ParentRouteChart.Field.TierNum - 1)));\r
1397 } else {\r
1398 RouteChart.Field.RouteString = ParentRouteChart.Field.RouteString | (15 << (4 * (ParentRouteChart.Field.TierNum - 1)));\r
1399 }\r
1400 RouteChart.Field.RootPortNum = ParentRouteChart.Field.RootPortNum;\r
1401 RouteChart.Field.TierNum = ParentRouteChart.Field.TierNum + 1;\r
1402 }\r
1403\r
1404 if (((PortState->PortStatus & USB_PORT_STAT_ENABLE) != 0) &&\r
1405 ((PortState->PortStatus & USB_PORT_STAT_CONNECTION) != 0)) {\r
1406 //\r
1407 // Has a device attached, Identify device speed after port is enabled.\r
1408 //\r
1409 Speed = EFI_USB_SPEED_FULL;\r
1410 if ((PortState->PortStatus & USB_PORT_STAT_LOW_SPEED) != 0) {\r
1411 Speed = EFI_USB_SPEED_LOW;\r
1412 } else if ((PortState->PortStatus & USB_PORT_STAT_HIGH_SPEED) != 0) {\r
1413 Speed = EFI_USB_SPEED_HIGH;\r
1414 } else if ((PortState->PortStatus & USB_PORT_STAT_SUPER_SPEED) != 0) {\r
1415 Speed = EFI_USB_SPEED_SUPER;\r
1416 }\r
1417 //\r
1418 // Execute Enable_Slot cmd for attached device, initialize device context and assign device address.\r
1419 //\r
1420 SlotId = XhcRouteStringToSlotId (RouteChart);\r
1421 if (SlotId == 0) {\r
1422 Status = XhcInitializeDeviceSlot (Xhc, ParentRouteChart, Port, RouteChart, Speed);\r
1423 ASSERT_EFI_ERROR (Status);\r
1424 }\r
1425 } else if ((PortState->PortStatus & USB_PORT_STAT_CONNECTION) == 0) {\r
1426 //\r
1427 // Device is detached. Disable the allocated device slot and release resource.\r
1428 //\r
1429 SlotId = XhcRouteStringToSlotId (RouteChart);\r
1430 if (SlotId != 0) {\r
1431 Status = XhcDisableSlotCmd (Xhc, SlotId);\r
1432 ASSERT_EFI_ERROR (Status);\r
1433 }\r
1434 }\r
1435 return Status;\r
1436}\r
1437\r
1438\r
1439/**\r
1440 Calculate the device context index by endpoint address and direction.\r
1441\r
1442 @param EpAddr The target endpoint number.\r
1443 @param Direction The direction of the target endpoint.\r
1444\r
1445 @return The device context index of endpoint.\r
1446\r
1447**/\r
1448UINT8\r
1449XhcEndpointToDci (\r
1450 IN UINT8 EpAddr,\r
1451 IN UINT8 Direction\r
1452 )\r
1453{\r
1454 UINT8 Index;\r
1455\r
1456 if (EpAddr == 0) {\r
1457 return 1;\r
1458 } else {\r
ce9b5900 1459 Index = (UINT8) (2 * EpAddr);\r
92870c98 1460 if (Direction == EfiUsbDataIn) {\r
1461 Index += 1;\r
1462 }\r
1463 return Index;\r
1464 }\r
1465}\r
1466\r
1467/**\r
1468 Find out the slot id according to device address assigned by XHCI's Address_Device cmd.\r
1469\r
1470 @param DevAddr The device address of the target device.\r
1471\r
1472 @return The slot id used by the device.\r
1473\r
1474**/\r
1475UINT8\r
92870c98 1476XhcDevAddrToSlotId (\r
1477 IN UINT8 DevAddr\r
1478 )\r
1479{\r
1480 UINT8 Index;\r
1481\r
1482 for (Index = 0; Index < 255; Index++) {\r
1483 if (UsbDevContext[Index + 1].Enabled &&\r
1484 (UsbDevContext[Index + 1].SlotId != 0) &&\r
1485 (UsbDevContext[Index + 1].XhciDevAddr == DevAddr)) {\r
1486 break;\r
1487 }\r
1488 }\r
1489\r
1490 if (Index == 255) {\r
1491 return 0;\r
1492 }\r
1493\r
1494 return UsbDevContext[Index + 1].SlotId;\r
1495}\r
1496\r
1497/**\r
1498 Find out the actual device address according to the requested device address from UsbBus.\r
1499\r
1500 @param BusDevAddr The requested device address by UsbBus upper driver.\r
1501\r
1502 @return The actual device address assigned to the device.\r
1503\r
1504**/\r
1505UINT8\r
1506EFIAPI\r
1507XhcBusDevAddrToSlotId (\r
1508 IN UINT8 BusDevAddr\r
1509 )\r
1510{\r
1511 UINT8 Index;\r
1512\r
1513 for (Index = 0; Index < 255; Index++) {\r
1514 if (UsbDevContext[Index + 1].Enabled &&\r
1515 (UsbDevContext[Index + 1].SlotId != 0) &&\r
1516 (UsbDevContext[Index + 1].BusDevAddr == BusDevAddr)) {\r
1517 break;\r
1518 }\r
1519 }\r
1520\r
1521 if (Index == 255) {\r
1522 return 0;\r
1523 }\r
1524\r
1525 return UsbDevContext[Index + 1].SlotId;\r
1526}\r
1527\r
1528/**\r
1529 Find out the slot id according to the device's route string.\r
1530\r
1531 @param RouteString The route string described the device location.\r
1532\r
1533 @return The slot id used by the device.\r
1534\r
1535**/\r
1536UINT8\r
1537EFIAPI\r
1538XhcRouteStringToSlotId (\r
1539 IN USB_DEV_ROUTE RouteString\r
1540 )\r
1541{\r
1542 UINT8 Index;\r
1543\r
1544 for (Index = 0; Index < 255; Index++) {\r
1545 if (UsbDevContext[Index + 1].Enabled &&\r
1546 (UsbDevContext[Index + 1].SlotId != 0) &&\r
1547 (UsbDevContext[Index + 1].RouteString.Dword == RouteString.Dword)) {\r
1548 break;\r
1549 }\r
1550 }\r
1551\r
1552 if (Index == 255) {\r
1553 return 0;\r
1554 }\r
1555\r
1556 return UsbDevContext[Index + 1].SlotId;\r
1557}\r
1558\r
1559/**\r
1560 Synchronize the specified event ring to update the enqueue and dequeue pointer.\r
1561\r
1562 @param Xhc The XHCI device.\r
1563 @param EvtRing The event ring to sync.\r
1564\r
1565 @retval EFI_SUCCESS The event ring is synchronized successfully.\r
1566\r
1567**/\r
1568EFI_STATUS\r
1569EFIAPI\r
1570XhcSyncEventRing (\r
1571 IN USB_XHCI_DEV *Xhc,\r
1572 IN EVENT_RING *EvtRing\r
1573 )\r
1574{\r
1575 UINTN Index;\r
1576 TRB *EvtTrb1;\r
1577 TRB *EvtTrb2;\r
1578 TRB *XhcDequeue;\r
1579\r
1580 ASSERT (EvtRing != NULL);\r
1581\r
1582 //\r
1583 // Calculate the EventRingEnqueue and EventRingCCS.\r
1584 // Note: only support single Segment\r
1585 //\r
1586 EvtTrb1 = EvtRing->EventRingSeg0;\r
1587 EvtTrb2 = EvtRing->EventRingSeg0;\r
1588\r
1589 for (Index = 0; Index < EvtRing->TrbNumber; Index++) {\r
1590 if (EvtTrb1->CycleBit != EvtTrb2->CycleBit) {\r
1591 break;\r
1592 }\r
1593 EvtTrb1++;\r
1594 }\r
1595\r
1596 if (Index < EvtRing->TrbNumber) {\r
1597 EvtRing->EventRingEnqueue = EvtTrb1;\r
1598 EvtRing->EventRingCCS = (EvtTrb2->CycleBit) ? 1 : 0;\r
1599 } else {\r
1600 EvtRing->EventRingEnqueue = EvtTrb2;\r
1601 EvtRing->EventRingCCS = (EvtTrb2->CycleBit) ? 0 : 1;\r
1602 }\r
1603\r
1604 //\r
1605 // Apply the EventRingDequeue to Xhc\r
1606 //\r
1607 XhcDequeue = (TRB *)(UINTN) XhcReadRuntimeReg64 (\r
1608 Xhc,\r
1609 XHC_ERDP_OFFSET + (32 * EvtRing->EventInterrupter)\r
1610 );\r
1611\r
ce9b5900 1612 if (((UINT64)(UINTN)XhcDequeue & (~0x0F)) != ((UINT64)(UINTN)EvtRing->EventRingDequeue & (~0x0F))) {\r
92870c98 1613 XhcWriteRuntimeReg64 (\r
1614 Xhc,\r
1615 XHC_ERDP_OFFSET + (32 * EvtRing->EventInterrupter),\r
ce9b5900 1616 (UINT64)(UINTN)EvtRing->EventRingDequeue | BIT3\r
92870c98 1617 );\r
1618 }\r
1619\r
1620 return EFI_SUCCESS;\r
1621}\r
1622\r
1623/**\r
1624 Synchronize the specified transfer ring to update the enqueue and dequeue pointer.\r
1625\r
1626 @param Xhc The XHCI device.\r
1627 @param TrsRing The transfer ring to sync.\r
1628\r
1629 @retval EFI_SUCCESS The transfer ring is synchronized successfully.\r
1630\r
1631**/\r
1632EFI_STATUS\r
1633EFIAPI\r
1634XhcSyncTrsRing (\r
1635 IN USB_XHCI_DEV *Xhc,\r
1636 IN TRANSFER_RING *TrsRing\r
1637 )\r
1638{\r
1639 UINTN Index;\r
1640 TRB *TrsTrb;\r
1641\r
1642 ASSERT (TrsRing != NULL);\r
1643 //\r
1644 // Calculate the latest RingEnqueue and RingPCS\r
1645 //\r
1646 TrsTrb = TrsRing->RingEnqueue;\r
1647 ASSERT (TrsTrb != NULL);\r
1648\r
1649 for (Index = 0; Index < TrsRing->TrbNumber; Index++) {\r
1650 if (TrsTrb->CycleBit != (TrsRing->RingPCS & BIT0)) {\r
1651 break;\r
1652 }\r
1653 TrsTrb++;\r
1654 if ((UINT8) TrsTrb->Type == TRB_TYPE_LINK) {\r
1655 ASSERT (((LNK_TRB*)TrsTrb)->TC != 0);\r
1656 //\r
1657 // set cycle bit in Link TRB as normal\r
1658 //\r
1659 ((LNK_TRB*)TrsTrb)->CycleBit = TrsRing->RingPCS & BIT0;\r
1660 //\r
1661 // Toggle PCS maintained by software\r
1662 //\r
1663 TrsRing->RingPCS = (TrsRing->RingPCS & BIT0) ? 0 : 1;\r
1664 TrsTrb = (TRB*)(UINTN)((TrsTrb->Dword1 | ((UINT64)TrsTrb->Dword2 << 32)) & ~0x0F);\r
1665 }\r
1666 }\r
1667\r
1668 ASSERT (Index != TrsRing->TrbNumber);\r
1669\r
1670 if (TrsTrb != TrsRing->RingEnqueue) {\r
1671 TrsRing->RingEnqueue = TrsTrb;\r
1672 }\r
1673\r
1674 //\r
1675 // Clear the Trb context for enqueue, but reserve the PCS bit\r
1676 //\r
1677 TrsTrb->Dword1 = 0;\r
1678 TrsTrb->Dword2 = 0;\r
1679 TrsTrb->Dword3 = 0;\r
1680 TrsTrb->RsvdZ1 = 0;\r
1681 TrsTrb->Type = 0;\r
1682 TrsTrb->RsvdZ2 = 0;\r
1683\r
1684 return EFI_SUCCESS;\r
1685}\r
1686\r
1687/**\r
1688 Check if there is a new generated event.\r
1689\r
1690 @param Xhc The XHCI device.\r
1691 @param EvtRing The event ring to check.\r
1692 @param NewEvtTrb The new event TRB found.\r
1693\r
1694 @retval EFI_SUCCESS Found a new event TRB at the event ring.\r
1695 @retval EFI_NOT_READY The event ring has no new event.\r
1696\r
1697**/\r
1698EFI_STATUS\r
1699EFIAPI\r
1700XhcCheckNewEvent (\r
1701 IN USB_XHCI_DEV *Xhc,\r
1702 IN EVENT_RING *EvtRing,\r
1703 OUT TRB **NewEvtTrb\r
1704 )\r
1705{\r
1706 EFI_STATUS Status;\r
1707 TRB *EvtTrb;\r
1708\r
1709 ASSERT (EvtRing != NULL);\r
1710\r
1711 EvtTrb = EvtRing->EventRingDequeue;\r
1712 *NewEvtTrb = EvtRing->EventRingDequeue;\r
1713\r
1714 if (EvtRing->EventRingDequeue == EvtRing->EventRingEnqueue) {\r
1715 return EFI_NOT_READY;\r
1716 }\r
1717\r
1718 Status = EFI_SUCCESS;\r
1719\r
1720 if (((EvtTrb->Dword3 >> 24) & 0xFF) != TRB_COMPLETION_SUCCESS) {\r
1721 Status = EFI_DEVICE_ERROR;\r
1722 }\r
1723\r
1724 EvtRing->EventRingDequeue++;\r
1725 //\r
1726 // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.\r
1727 //\r
1728 if ((UINTN)EvtRing->EventRingDequeue >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB) * EvtRing->TrbNumber)) {\r
1729 EvtRing->EventRingDequeue = EvtRing->EventRingSeg0;\r
1730 }\r
1731\r
1732 return Status;\r
1733}\r
1734\r
1735/**\r
1736 Ring the door bell to notify XHCI there is a transaction to be executed.\r
1737\r
1738 @param Xhc The XHCI device.\r
1739 @param SlotId The slot id of the target device.\r
1740 @param Dci The device context index of the target slot or endpoint.\r
1741\r
1742 @retval EFI_SUCCESS Successfully ring the door bell.\r
1743\r
1744**/\r
1745EFI_STATUS\r
1746EFIAPI\r
1747XhcRingDoorBell (\r
1748 IN USB_XHCI_DEV *Xhc,\r
1749 IN UINT8 SlotId,\r
1750 IN UINT8 Dci\r
1751 )\r
1752{\r
1753 if (SlotId == 0) {\r
1754 XhcWriteDoorBellReg (Xhc, 0, 0);\r
1755 } else {\r
1756 XhcWriteDoorBellReg (Xhc, SlotId * sizeof (UINT32), Dci);\r
1757 }\r
1758\r
1759 return EFI_SUCCESS;\r
1760}\r
1761\r
1762/**\r
1763 Ring the door bell to notify XHCI there is a transaction to be executed through URB.\r
1764\r
1765 @param Xhc The XHCI device.\r
1766 @param Urb The URB to be rung.\r
1767\r
1768 @retval EFI_SUCCESS Successfully ring the door bell.\r
1769\r
1770**/\r
1771EFI_STATUS\r
1772RingIntTransferDoorBell (\r
1773 IN USB_XHCI_DEV *Xhc,\r
1774 IN URB *Urb\r
1775 )\r
1776{\r
1777 UINT8 SlotId;\r
1778 UINT8 Dci;\r
1779\r
1780 SlotId = XhcDevAddrToSlotId(Urb->Ep.DevAddr);\r
ce9b5900 1781 Dci = XhcEndpointToDci(Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction));\r
92870c98 1782 XhcRingDoorBell (Xhc, SlotId, Dci);\r
1783 return EFI_SUCCESS;\r
1784}\r
1785\r
1786/**\r
1787 Assign and initialize the device slot for a new device.\r
1788\r
1789 @param Xhc The XHCI device.\r
1790 @param ParentRouteChart The route string pointed to the parent device.\r
1791 @param ParentPort The port at which the device is located.\r
1792 @param RouteChart The route string pointed to the device.\r
1793 @param DeviceSpeed The device speed.\r
1794\r
1795 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.\r
1796\r
1797**/\r
1798EFI_STATUS\r
1799EFIAPI\r
1800XhcInitializeDeviceSlot (\r
1801 IN USB_XHCI_DEV *Xhc,\r
1802 IN USB_DEV_ROUTE ParentRouteChart,\r
1803 IN UINT16 ParentPort,\r
1804 IN USB_DEV_ROUTE RouteChart,\r
1805 IN UINT8 DeviceSpeed\r
1806 )\r
1807{\r
1808 EFI_STATUS Status;\r
1809 EVT_TRB_COMMAND *EvtTrb;\r
1810 INPUT_CONTEXT *InputContext;\r
1811 DEVICE_CONTEXT *OutputDevContxt;\r
1812 TRANSFER_RING *EndpointTransferRing;\r
1813 CMD_TRB_ADDR_DEV CmdTrbAddr;\r
1814 UINT8 DeviceAddress;\r
1815 CMD_TRB_EN_SLOT CmdTrb;\r
1816 UINT8 SlotId;\r
1817 UINT8 ParentSlotId;\r
1818 DEVICE_CONTEXT *ParentDeviceContext;\r
1819\r
1820 ZeroMem (&CmdTrb, sizeof (CMD_TRB_EN_SLOT));\r
1821 CmdTrb.CycleBit = 1;\r
1822 CmdTrb.Type = TRB_TYPE_EN_SLOT;\r
1823\r
1824 Status = XhcCmdTransfer (\r
1825 Xhc,\r
1826 (TRB *) (UINTN) &CmdTrb,\r
1827 XHC_GENERIC_TIMEOUT,\r
1828 (TRB **) (UINTN) &EvtTrb\r
1829 );\r
1830 ASSERT_EFI_ERROR (Status);\r
1831 ASSERT (EvtTrb->SlotId <= Xhc->MaxSlotsEn);\r
1832 DEBUG ((EFI_D_INFO, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb->SlotId));\r
1833 SlotId = (UINT8)EvtTrb->SlotId;\r
1834 ASSERT (SlotId != 0);\r
1835\r
1836 ZeroMem (&UsbDevContext[SlotId], sizeof (USB_DEV_CONTEXT));\r
1837 UsbDevContext[SlotId].Enabled = TRUE;\r
1838 UsbDevContext[SlotId].SlotId = SlotId;\r
1839 UsbDevContext[SlotId].RouteString.Dword = RouteChart.Dword;\r
1840 UsbDevContext[SlotId].ParentRouteString.Dword = ParentRouteChart.Dword;\r
1841\r
1842 //\r
1843 // 4.3.3 Device Slot Initialization\r
1844 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.\r
1845 //\r
1846 InputContext = AllocateAlignedZeroPool(sizeof (INPUT_CONTEXT), 64);\r
1847 ASSERT (InputContext != NULL);\r
1848 ASSERT (((UINTN) InputContext & 0x3F) == 0);\r
1849\r
1850 UsbDevContext[SlotId].InputContext = (VOID *) InputContext;\r
1851\r
1852 //\r
1853 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1\r
1854 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input\r
1855 // Context are affected by the command.\r
1856 //\r
1857 InputContext->InputControlContext.Dword2 |= (BIT0 | BIT1);\r
1858\r
1859 //\r
1860 // 3) Initialize the Input Slot Context data structure\r
1861 //\r
1862 InputContext->Slot.RouteStr = RouteChart.Field.RouteString;\r
1863 InputContext->Slot.Speed = DeviceSpeed + 1;\r
1864 InputContext->Slot.ContextEntries = 1;\r
1865 InputContext->Slot.RootHubPortNum = RouteChart.Field.RootPortNum;\r
1866\r
1867 if (RouteChart.Field.RouteString) {\r
1868 //\r
1869 // The device is behind of hub device.\r
1870 //\r
1871 ParentSlotId = XhcRouteStringToSlotId(ParentRouteChart);\r
1872 ASSERT (ParentSlotId != 0);\r
1873 //\r
1874 //if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context\r
1875 //\r
1876 ParentDeviceContext = (DEVICE_CONTEXT *)UsbDevContext[ParentSlotId].OutputDevContxt;\r
1877 if ((ParentDeviceContext->Slot.TTPortNum == 0) &&\r
1878 (ParentDeviceContext->Slot.TTHubSlotId == 0)) {\r
1879 if ((ParentDeviceContext->Slot.Speed == (EFI_USB_SPEED_HIGH + 1)) && (DeviceSpeed < EFI_USB_SPEED_HIGH)) {\r
1880 //\r
1881 // Full/Low device attached to High speed hub port that isolates the high speed signaling\r
1882 // environment from Full/Low speed signaling environment for a device\r
1883 //\r
1884 InputContext->Slot.TTPortNum = ParentPort;\r
1885 InputContext->Slot.TTHubSlotId = ParentSlotId;\r
1886 }\r
1887 } else {\r
1888 //\r
1889 // Inherit the TT parameters from parent device.\r
1890 //\r
1891 InputContext->Slot.TTPortNum = ParentDeviceContext->Slot.TTPortNum;\r
1892 InputContext->Slot.TTHubSlotId = ParentDeviceContext->Slot.TTHubSlotId;\r
1893 //\r
1894 // If the device is a High speed device then down the speed to be the same as its parent Hub\r
1895 //\r
1896 if (DeviceSpeed == EFI_USB_SPEED_HIGH) {\r
1897 InputContext->Slot.Speed = ParentDeviceContext->Slot.Speed;\r
1898 }\r
1899 }\r
1900 }\r
1901\r
1902 //\r
1903 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.\r
1904 //\r
1905 EndpointTransferRing = AllocateAlignedZeroPool(sizeof (TRANSFER_RING), 64);\r
1906 UsbDevContext[SlotId].EndpointTransferRing[0] = EndpointTransferRing;\r
1907 CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)UsbDevContext[SlotId].EndpointTransferRing[0]);\r
1908 //\r
1909 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).\r
1910 //\r
1911 InputContext->EP[0].EPType = ED_CONTROL_BIDIR;\r
1912\r
1913 if (DeviceSpeed == EFI_USB_SPEED_SUPER) {\r
1914 InputContext->EP[0].MaxPacketSize = 512;\r
1915 } else if (DeviceSpeed == EFI_USB_SPEED_HIGH) {\r
1916 InputContext->EP[0].MaxPacketSize = 64;\r
1917 } else {\r
1918 InputContext->EP[0].MaxPacketSize = 8;\r
1919 }\r
1920 //\r
1921 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints\r
1922 // 1KB, and Bulk and Isoch endpoints 3KB.\r
1923 //\r
1924 InputContext->EP[0].AverageTRBLength = 8;\r
1925 InputContext->EP[0].MaxBurstSize = 0;\r
1926 InputContext->EP[0].Interval = 0;\r
1927 InputContext->EP[0].MaxPStreams = 0;\r
1928 InputContext->EP[0].Mult = 0;\r
1929 InputContext->EP[0].CErr = 3;\r
1930\r
1931 //\r
1932 // Init the DCS(dequeue cycle state) as the transfer ring's CCS\r
1933 //\r
1934 InputContext->EP[0].PtrLo = XHC_LOW_32BIT (((TRANSFER_RING *)(UINTN)UsbDevContext[SlotId].EndpointTransferRing[0])->RingSeg0) | BIT0;\r
1935 InputContext->EP[0].PtrHi = XHC_HIGH_32BIT (((TRANSFER_RING *)(UINTN)UsbDevContext[SlotId].EndpointTransferRing[0])->RingSeg0);\r
1936\r
1937 //\r
1938 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.\r
1939 //\r
1940 OutputDevContxt = AllocateAlignedZeroPool(sizeof (DEVICE_CONTEXT), 64);\r
1941 ASSERT (OutputDevContxt != NULL);\r
1942 ASSERT (((UINTN) OutputDevContxt & 0x3F) == 0);\r
1943\r
1944 UsbDevContext[SlotId].OutputDevContxt = OutputDevContxt;\r
1945 //\r
1946 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with\r
1947 // a pointer to the Output Device Context data structure (6.2.1).\r
1948 //\r
1949 Xhc->DCBAA[SlotId] = (UINT64) (UINTN) OutputDevContxt;\r
1950\r
1951 //\r
1952 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input\r
1953 // Context data structure described above.\r
1954 //\r
1955 ZeroMem (&CmdTrbAddr, sizeof (CmdTrbAddr));\r
1956 CmdTrbAddr.PtrLo = XHC_LOW_32BIT (UsbDevContext[SlotId].InputContext);\r
1957 CmdTrbAddr.PtrHi = XHC_HIGH_32BIT (UsbDevContext[SlotId].InputContext);\r
1958 CmdTrbAddr.CycleBit = 1;\r
1959 CmdTrbAddr.Type = TRB_TYPE_ADDRESS_DEV;\r
1960 CmdTrbAddr.SlotId = UsbDevContext[SlotId].SlotId;\r
1961 Status = XhcCmdTransfer (\r
1962 Xhc,\r
1963 (TRB *) (UINTN) &CmdTrbAddr,\r
1964 XHC_GENERIC_TIMEOUT,\r
1965 (TRB **) (UINTN) &EvtTrb\r
1966 );\r
1967 ASSERT (!EFI_ERROR(Status));\r
1968\r
1969 DeviceAddress = (UINT8) ((DEVICE_CONTEXT *) OutputDevContxt)->Slot.DeviceAddress;\r
1970 DEBUG ((EFI_D_INFO, " Address %d assigned succeefully\n", DeviceAddress));\r
1971\r
1972 UsbDevContext[SlotId].XhciDevAddr = DeviceAddress;\r
1973\r
1974 return Status;\r
1975}\r
1976\r
1977/**\r
1978 Disable the specified device slot.\r
1979\r
1980 @param Xhc The XHCI device.\r
1981 @param SlotId The slot id to be disabled.\r
1982\r
1983 @retval EFI_SUCCESS Successfully disable the device slot.\r
1984\r
1985**/\r
1986EFI_STATUS\r
1987EFIAPI\r
1988XhcDisableSlotCmd (\r
1989 IN USB_XHCI_DEV *Xhc,\r
1990 IN UINT8 SlotId\r
1991 )\r
1992{\r
1993 EFI_STATUS Status;\r
1994 TRB *EvtTrb;\r
1995 CMD_TRB_DIS_SLOT CmdTrbDisSlot;\r
1996 UINT8 Index;\r
1997 VOID *RingSeg;\r
1998\r
1999 //\r
2000 // Disable the device slots occupied by these devices on its downstream ports.\r
2001 // Entry 0 is reserved.\r
2002 //\r
2003 for (Index = 0; Index < 255; Index++) {\r
2004 if (!UsbDevContext[Index + 1].Enabled ||\r
2005 (UsbDevContext[Index + 1].SlotId == 0) ||\r
2006 (UsbDevContext[Index + 1].ParentRouteString.Dword != UsbDevContext[SlotId].RouteString.Dword)) {\r
2007 continue;\r
2008 }\r
2009\r
2010 Status = XhcDisableSlotCmd (Xhc, UsbDevContext[Index + 1].SlotId);\r
2011\r
2012 if (EFI_ERROR (Status)) {\r
2013 DEBUG ((EFI_D_ERROR, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));\r
2014 UsbDevContext[Index + 1].SlotId = 0;\r
2015 }\r
2016 }\r
2017\r
2018 //\r
2019 // Construct the disable slot command\r
2020 //\r
2021 DEBUG ((EFI_D_INFO, "Disable device slot %d!\n", SlotId));\r
2022\r
2023 ZeroMem (&CmdTrbDisSlot, sizeof (CmdTrbDisSlot));\r
2024 CmdTrbDisSlot.CycleBit = 1;\r
2025 CmdTrbDisSlot.Type = TRB_TYPE_DIS_SLOT;\r
2026 CmdTrbDisSlot.SlotId = SlotId;\r
2027 Status = XhcCmdTransfer (\r
2028 Xhc,\r
2029 (TRB *) (UINTN) &CmdTrbDisSlot,\r
2030 XHC_GENERIC_TIMEOUT,\r
2031 (TRB **) (UINTN) &EvtTrb\r
2032 );\r
2033 ASSERT_EFI_ERROR(Status);\r
2034 //\r
2035 // Free the slot's device context entry\r
2036 //\r
2037 Xhc->DCBAA[SlotId] = 0;\r
2038\r
2039 //\r
2040 // Free the slot related data structure\r
2041 //\r
2042 for (Index = 0; Index < 31; Index++) {\r
2043 if (UsbDevContext[SlotId].EndpointTransferRing[Index] != NULL) {\r
2044 RingSeg = ((TRANSFER_RING *)(UINTN)UsbDevContext[SlotId].EndpointTransferRing[Index])->RingSeg0;\r
2045 if (RingSeg != NULL) {\r
2046 FreeAlignedPool(RingSeg);\r
2047 }\r
2048 FreeAlignedPool(UsbDevContext[SlotId].EndpointTransferRing[Index]);\r
2049 }\r
2050 }\r
2051\r
2052 for (Index = 0; Index < UsbDevContext[SlotId].DevDesc.NumConfigurations; Index++) {\r
2053 if (UsbDevContext[SlotId].ConfDesc[Index] != NULL) {\r
2054 FreePool (UsbDevContext[SlotId].ConfDesc[Index]);\r
2055 }\r
2056 }\r
2057\r
2058 if (UsbDevContext[SlotId].InputContext != NULL) {\r
2059 FreeAlignedPool (UsbDevContext[SlotId].InputContext);\r
2060 }\r
2061\r
2062 if (UsbDevContext[SlotId].OutputDevContxt != NULL) {\r
2063 FreeAlignedPool (UsbDevContext[SlotId].OutputDevContxt);\r
2064 }\r
2065 //\r
2066 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established\r
2067 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to\r
2068 // remove urb from XHCI's asynchronous transfer list.\r
2069 //\r
2070 UsbDevContext[SlotId].Enabled = FALSE;\r
2071\r
2072 return Status;\r
2073}\r
2074\r
2075/**\r
2076 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.\r
2077\r
2078 @param Xhc The XHCI device.\r
2079 @param SlotId The slot id to be configured.\r
2080 @param DeviceSpeed The device's speed.\r
2081 @param ConfigDesc The pointer to the usb device configuration descriptor.\r
2082\r
2083 @retval EFI_SUCCESS Successfully configure all the device endpoints.\r
2084\r
2085**/\r
2086EFI_STATUS\r
2087EFIAPI\r
2088XhcSetConfigCmd (\r
2089 IN USB_XHCI_DEV *Xhc,\r
2090 IN UINT8 SlotId,\r
2091 IN UINT8 DeviceSpeed,\r
2092 IN USB_CONFIG_DESCRIPTOR *ConfigDesc\r
2093 )\r
2094{\r
2095 EFI_STATUS Status;\r
2096\r
2097 USB_INTERFACE_DESCRIPTOR *IfDesc;\r
2098 USB_ENDPOINT_DESCRIPTOR *EpDesc;\r
2099 UINT8 Index;\r
2100 UINTN NumEp;\r
2101 UINTN EpIndex;\r
2102 UINT8 EpAddr;\r
2103 UINT8 Direction;\r
2104 UINT8 Dci;\r
2105 UINT8 MaxDci;\r
2106 UINT32 PhyAddr;\r
2107 UINT8 Interval;\r
2108\r
2109 TRANSFER_RING *EndpointTransferRing;\r
2110 CMD_CFG_ED CmdTrbCfgEP;\r
2111 INPUT_CONTEXT *InputContext;\r
2112 DEVICE_CONTEXT *OutputDevContxt;\r
2113 EVT_TRB_COMMAND *EvtTrb;\r
2114 //\r
2115 // 4.6.6 Configure Endpoint\r
2116 //\r
2117 InputContext = UsbDevContext[SlotId].InputContext;\r
2118 OutputDevContxt = UsbDevContext[SlotId].OutputDevContxt;\r
2119 ZeroMem (InputContext, sizeof (INPUT_CONTEXT));\r
2120 CopyMem (&InputContext->Slot, &OutputDevContxt->Slot, sizeof (SLOT_CONTEXT));\r
2121\r
2122 ASSERT (ConfigDesc != NULL);\r
2123\r
2124 MaxDci = 0;\r
2125\r
2126 IfDesc = (USB_INTERFACE_DESCRIPTOR *)(ConfigDesc + 1);\r
2127 for (Index = 0; Index < ConfigDesc->NumInterfaces; Index++) {\r
2128 while (IfDesc->DescriptorType != USB_DESC_TYPE_INTERFACE) {\r
2129 IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length);\r
2130 }\r
2131\r
2132 NumEp = IfDesc->NumEndpoints;\r
2133\r
2134 EpDesc = (USB_ENDPOINT_DESCRIPTOR *)(IfDesc + 1);\r
2135 for (EpIndex = 0; EpIndex < NumEp; EpIndex++) {\r
2136 while (EpDesc->DescriptorType != USB_DESC_TYPE_ENDPOINT) {\r
2137 EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length);\r
2138 }\r
2139\r
ce9b5900 2140 EpAddr = (UINT8)(EpDesc->EndpointAddress & 0x0F);\r
92870c98 2141 Direction = (UINT8)((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut);\r
2142\r
2143 Dci = XhcEndpointToDci (EpAddr, Direction);\r
2144 if (Dci > MaxDci) {\r
2145 MaxDci = Dci;\r
2146 }\r
2147\r
2148 InputContext->InputControlContext.Dword2 |= (BIT0 << Dci);\r
2149 InputContext->EP[Dci-1].MaxPacketSize = EpDesc->MaxPacketSize;\r
2150\r
2151 if (DeviceSpeed == EFI_USB_SPEED_SUPER) {\r
2152 //\r
2153 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.\r
2154 //\r
2155 InputContext->EP[Dci-1].MaxBurstSize = 0x0;\r
2156 } else {\r
2157 InputContext->EP[Dci-1].MaxBurstSize = 0x0;\r
2158 }\r
2159\r
2160 switch (EpDesc->Attributes & USB_ENDPOINT_TYPE_MASK) {\r
2161 case USB_ENDPOINT_BULK:\r
2162 if (Direction == EfiUsbDataIn) {\r
2163 InputContext->EP[Dci-1].CErr = 3;\r
2164 InputContext->EP[Dci-1].EPType = ED_BULK_IN;\r
2165 } else {\r
2166 InputContext->EP[Dci-1].CErr = 3;\r
2167 InputContext->EP[Dci-1].EPType = ED_BULK_OUT;\r
2168 }\r
2169\r
2170 InputContext->EP[Dci-1].AverageTRBLength = 0x1000;\r
2171 if (UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) {\r
2172 EndpointTransferRing = AllocateAlignedZeroPool(sizeof (TRANSFER_RING), 64);\r
2173 UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing;\r
2174 CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)UsbDevContext[SlotId].EndpointTransferRing[Dci-1]);\r
2175 }\r
2176\r
2177 break;\r
2178 case USB_ENDPOINT_ISO:\r
2179 if (Direction == EfiUsbDataIn) {\r
2180 InputContext->EP[Dci-1].CErr = 0;\r
2181 InputContext->EP[Dci-1].EPType = ED_ISOCH_IN;\r
2182 } else {\r
2183 InputContext->EP[Dci-1].CErr = 0;\r
2184 InputContext->EP[Dci-1].EPType = ED_ISOCH_OUT;\r
2185 }\r
2186 break;\r
2187 case USB_ENDPOINT_INTERRUPT:\r
2188 if (Direction == EfiUsbDataIn) {\r
2189 InputContext->EP[Dci-1].CErr = 3;\r
2190 InputContext->EP[Dci-1].EPType = ED_INTERRUPT_IN;\r
2191 } else {\r
2192 InputContext->EP[Dci-1].CErr = 3;\r
2193 InputContext->EP[Dci-1].EPType = ED_INTERRUPT_OUT;\r
2194 }\r
2195 InputContext->EP[Dci-1].AverageTRBLength = 0x1000;\r
2196 InputContext->EP[Dci-1].MaxESITPayload = EpDesc->MaxPacketSize;\r
2197 //\r
2198 // Get the bInterval from descriptor and init the the interval field of endpoint context\r
2199 //\r
2200 if ((DeviceSpeed == EFI_USB_SPEED_FULL) || (DeviceSpeed == EFI_USB_SPEED_LOW)) {\r
2201 Interval = EpDesc->Interval;\r
2202 //\r
2203 // BUGBUG: Hard code the interval to MAX\r
2204 //\r
2205 InputContext->EP[Dci-1].Interval = 6;\r
2206 } else if (DeviceSpeed == EFI_USB_SPEED_SUPER) {\r
2207 Interval = EpDesc->Interval;\r
2208 InputContext->EP[Dci-1].Interval = 0x0F;\r
2209 InputContext->EP[Dci-1].AverageTRBLength = 0x1000;\r
2210 InputContext->EP[Dci-1].MaxESITPayload = 0x0002;\r
2211 InputContext->EP[Dci-1].MaxBurstSize = 0x0;\r
2212 InputContext->EP[Dci-1].CErr = 3;\r
2213 }\r
2214\r
2215 if (UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) {\r
2216 EndpointTransferRing = AllocateAlignedZeroPool(sizeof (TRANSFER_RING), 64);\r
2217 UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing;\r
2218 CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)UsbDevContext[SlotId].EndpointTransferRing[Dci-1]);\r
2219 }\r
2220 break;\r
2221\r
2222 case USB_ENDPOINT_CONTROL:\r
2223 default:\r
2224 ASSERT (0);\r
2225 break;\r
2226 }\r
2227\r
2228 PhyAddr = XHC_LOW_32BIT (((TRANSFER_RING *)(UINTN)UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingSeg0);\r
2229 PhyAddr &= ~(0x0F);\r
2230 PhyAddr |= ((TRANSFER_RING *)(UINTN)UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingPCS;\r
2231 InputContext->EP[Dci-1].PtrLo = PhyAddr;\r
2232 InputContext->EP[Dci-1].PtrHi = XHC_HIGH_32BIT (((TRANSFER_RING *)(UINTN)UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingSeg0);\r
2233\r
2234 EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length);\r
2235 }\r
2236 IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length);\r
2237 }\r
2238\r
2239 InputContext->InputControlContext.Dword2 |= BIT0;\r
2240 InputContext->Slot.ContextEntries = MaxDci;\r
2241 //\r
2242 // configure endpoint\r
2243 //\r
2244 ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP));\r
2245 CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (InputContext);\r
2246 CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (InputContext);\r
2247 CmdTrbCfgEP.CycleBit = 1;\r
2248 CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT;\r
2249 CmdTrbCfgEP.SlotId = UsbDevContext[SlotId].SlotId;\r
2250 DEBUG ((EFI_D_INFO, "Configure Endpoint\n"));\r
2251 Status = XhcCmdTransfer (\r
2252 Xhc,\r
2253 (TRB *) (UINTN) &CmdTrbCfgEP,\r
2254 XHC_GENERIC_TIMEOUT,\r
2255 (TRB **) (UINTN) &EvtTrb\r
2256 );\r
2257 ASSERT_EFI_ERROR(Status);\r
2258\r
2259 return Status;\r
2260}\r
2261\r
2262/**\r
2263 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.\r
2264\r
2265 @param Xhc The XHCI device.\r
2266 @param SlotId The slot id to be evaluated.\r
2267 @param MaxPacketSize The max packet size supported by the device control transfer.\r
2268\r
2269 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.\r
2270\r
2271**/\r
2272EFI_STATUS\r
2273EFIAPI\r
2274XhcEvaluateContext (\r
2275 IN USB_XHCI_DEV *Xhc,\r
2276 IN UINT8 SlotId,\r
2277 IN UINT32 MaxPacketSize\r
2278 )\r
2279{\r
2280 EFI_STATUS Status;\r
2281 CMD_TRB_EVALU_CONTX CmdTrbEvalu;\r
2282 EVT_TRB_COMMAND *EvtTrb;\r
2283 INPUT_CONTEXT *InputContext;\r
2284\r
2285 ASSERT (UsbDevContext[SlotId].SlotId != 0);\r
2286\r
2287 //\r
2288 // 4.6.7 Evaluate Context\r
2289 //\r
2290 InputContext = UsbDevContext[SlotId].InputContext;\r
2291 ZeroMem (InputContext, sizeof (INPUT_CONTEXT));\r
2292\r
2293 InputContext->InputControlContext.Dword2 |= BIT1;\r
2294 InputContext->EP[0].MaxPacketSize = MaxPacketSize;\r
2295\r
2296 ZeroMem (&CmdTrbEvalu, sizeof (CmdTrbEvalu));\r
2297 CmdTrbEvalu.PtrLo = XHC_LOW_32BIT (InputContext);\r
2298 CmdTrbEvalu.PtrHi = XHC_HIGH_32BIT (InputContext);\r
2299 CmdTrbEvalu.CycleBit = 1;\r
2300 CmdTrbEvalu.Type = TRB_TYPE_EVALU_CONTXT;\r
2301 CmdTrbEvalu.SlotId = UsbDevContext[SlotId].SlotId;\r
2302 DEBUG ((EFI_D_INFO, "Evaluate context\n"));\r
2303 Status = XhcCmdTransfer (\r
2304 Xhc,\r
2305 (TRB *) (UINTN) &CmdTrbEvalu,\r
2306 XHC_GENERIC_TIMEOUT,\r
2307 (TRB **) (UINTN) &EvtTrb\r
2308 );\r
2309 ASSERT (!EFI_ERROR(Status));\r
2310\r
2311 return Status;\r
2312}\r
2313\r
2314/**\r
2315 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.\r
2316\r
2317 @param Xhc The XHCI device.\r
2318 @param SlotId The slot id to be configured.\r
2319 @param PortNum The total number of downstream port supported by the hub.\r
2320 @param TTT The TT think time of the hub device.\r
2321 @param MTT The multi-TT of the hub device.\r
2322\r
2323 @retval EFI_SUCCESS Successfully configure the hub device's slot context.\r
2324\r
2325**/\r
2326EFI_STATUS\r
2327XhcConfigHubContext (\r
2328 IN USB_XHCI_DEV *Xhc,\r
2329 IN UINT8 SlotId,\r
2330 IN UINT8 PortNum,\r
2331 IN UINT8 TTT,\r
2332 IN UINT8 MTT\r
2333 )\r
2334{\r
2335 EFI_STATUS Status;\r
2336\r
2337 EVT_TRB_COMMAND *EvtTrb;\r
2338 INPUT_CONTEXT *InputContext;\r
2339 DEVICE_CONTEXT *OutputDevContxt;\r
2340 CMD_CFG_ED CmdTrbCfgEP;\r
2341\r
2342 ASSERT (UsbDevContext[SlotId].SlotId != 0);\r
2343 InputContext = UsbDevContext[SlotId].InputContext;\r
2344 OutputDevContxt = UsbDevContext[SlotId].OutputDevContxt;\r
2345\r
2346 //\r
2347 // 4.6.7 Evaluate Context\r
2348 //\r
2349 ZeroMem (InputContext, sizeof (INPUT_CONTEXT));\r
2350\r
2351 InputContext->InputControlContext.Dword2 |= BIT0;\r
2352\r
2353 //\r
2354 // Copy the slot context from OutputContext to Input context\r
2355 //\r
2356 CopyMem(&(InputContext->Slot), &(OutputDevContxt->Slot), sizeof (SLOT_CONTEXT));\r
2357 InputContext->Slot.Hub = 1;\r
2358 InputContext->Slot.PortNum = PortNum;\r
2359 InputContext->Slot.TTT = TTT;\r
2360 InputContext->Slot.MTT = MTT;\r
2361\r
2362 ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP));\r
2363 CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (InputContext);\r
2364 CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (InputContext);\r
2365 CmdTrbCfgEP.CycleBit = 1;\r
2366 CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT;\r
2367 CmdTrbCfgEP.SlotId = UsbDevContext[SlotId].SlotId;\r
2368 DEBUG ((EFI_D_INFO, "Configure Hub Slot Context\n"));\r
2369 Status = XhcCmdTransfer (\r
2370 Xhc,\r
2371 (TRB *) (UINTN) &CmdTrbCfgEP,\r
2372 XHC_GENERIC_TIMEOUT,\r
2373 (TRB **) (UINTN) &EvtTrb\r
2374 );\r
2375 ASSERT (!EFI_ERROR(Status));\r
2376\r
2377 return Status;\r
2378}\r
2379\r