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