TianoCore EDK2 master
Loading...
Searching...
No Matches
EhciSched.c
Go to the documentation of this file.
1
12#include "EhcPeim.h"
13
26 )
27{
28 USB_ENDPOINT Ep;
29 PEI_EHC_QH *Qh;
30 QH_HW *QhHw;
31 PEI_EHC_QTD *Qtd;
32
33 //
34 // Create an inactive Qtd to terminate the short packet read.
35 //
36 Qtd = EhcCreateQtd (Ehc, NULL, 0, QTD_PID_INPUT, 0, 64);
37
38 if (Qtd == NULL) {
39 return EFI_OUT_OF_RESOURCES;
40 }
41
42 Qtd->QtdHw.Status = QTD_STAT_HALTED;
43 Ehc->ShortReadStop = Qtd;
44
45 //
46 // Create a QH to act as the EHC reclamation header.
47 // Set the header to loopback to itself.
48 //
49 Ep.DevAddr = 0;
50 Ep.EpAddr = 1;
51 Ep.Direction = EfiUsbDataIn;
52 Ep.DevSpeed = EFI_USB_SPEED_HIGH;
53 Ep.MaxPacket = 64;
54 Ep.HubAddr = 0;
55 Ep.HubPort = 0;
56 Ep.Toggle = 0;
57 Ep.Type = EHC_BULK_TRANSFER;
58 Ep.PollRate = 1;
59
60 Qh = EhcCreateQh (Ehc, &Ep);
61
62 if (Qh == NULL) {
63 return EFI_OUT_OF_RESOURCES;
64 }
65
66 QhHw = &Qh->QhHw;
67 QhHw->HorizonLink = QH_LINK (QhHw, EHC_TYPE_QH, FALSE);
68 QhHw->Status = QTD_STAT_HALTED;
69 QhHw->ReclaimHead = 1;
70 Ehc->ReclaimHead = Qh;
71
72 //
73 // Create a dummy QH to act as the terminator for periodical schedule
74 //
75 Ep.EpAddr = 2;
76 Ep.Type = EHC_INT_TRANSFER_SYNC;
77
78 Qh = EhcCreateQh (Ehc, &Ep);
79
80 if (Qh == NULL) {
81 return EFI_OUT_OF_RESOURCES;
82 }
83
84 Qh->QhHw.Status = QTD_STAT_HALTED;
85 Ehc->PeriodOne = Qh;
86
87 return EFI_SUCCESS;
88}
89
102 )
103{
104 VOID *Buf;
105 EFI_PHYSICAL_ADDRESS PhyAddr;
106 VOID *Map;
107 UINTN Index;
108 UINT32 *Desc;
109 EFI_STATUS Status;
110 EFI_PHYSICAL_ADDRESS PciAddr;
111
112 //
113 // First initialize the periodical schedule data:
114 // 1. Allocate and map the memory for the frame list
115 // 2. Create the help QTD/QH
116 // 3. Initialize the frame entries
117 // 4. Set the frame list register
118 //
119 //
120 // The Frame List ocupies 4K bytes,
121 // and must be aligned on 4-Kbyte boundaries.
122 //
123 Status = IoMmuAllocateBuffer (
124 Ehc->IoMmu,
125 1,
126 &Buf,
127 &PhyAddr,
128 &Map
129 );
130
131 if (EFI_ERROR (Status) || (Buf == NULL)) {
132 return EFI_OUT_OF_RESOURCES;
133 }
134
135 Ehc->PeriodFrame = Buf;
136 Ehc->PeriodFrameMap = Map;
137 Ehc->High32bitAddr = EHC_HIGH_32BIT (PhyAddr);
138
139 //
140 // Init memory pool management then create the helper
141 // QTD/QH. If failed, previously allocated resources
142 // will be freed by EhcFreeSched
143 //
144 Ehc->MemPool = UsbHcInitMemPool (
145 Ehc,
146 EHC_BIT_IS_SET (Ehc->HcCapParams, HCCP_64BIT),
147 Ehc->High32bitAddr
148 );
149
150 if (Ehc->MemPool == NULL) {
151 return EFI_OUT_OF_RESOURCES;
152 }
153
154 Status = EhcCreateHelpQ (Ehc);
155
156 if (EFI_ERROR (Status)) {
157 return Status;
158 }
159
160 //
161 // Initialize the frame list entries then set the registers
162 //
163 Desc = (UINT32 *)Ehc->PeriodFrame;
164 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));
165 for (Index = 0; Index < EHC_FRAME_LEN; Index++) {
166 Desc[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);
167 }
168
169 EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (PhyAddr));
170
171 //
172 // Second initialize the asynchronous schedule:
173 // Only need to set the AsynListAddr register to
174 // the reclamation header
175 //
176 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));
177 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (PciAddr));
178 return EFI_SUCCESS;
179}
180
187VOID
190 )
191{
192 EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0);
193 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0);
194
195 if (Ehc->PeriodOne != NULL) {
196 UsbHcFreeMem (Ehc, Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));
197 Ehc->PeriodOne = NULL;
198 }
199
200 if (Ehc->ReclaimHead != NULL) {
201 UsbHcFreeMem (Ehc, Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));
202 Ehc->ReclaimHead = NULL;
203 }
204
205 if (Ehc->ShortReadStop != NULL) {
206 UsbHcFreeMem (Ehc, Ehc->MemPool, Ehc->ShortReadStop, sizeof (PEI_EHC_QTD));
207 Ehc->ShortReadStop = NULL;
208 }
209
210 if (Ehc->MemPool != NULL) {
211 UsbHcFreeMemPool (Ehc, Ehc->MemPool);
212 Ehc->MemPool = NULL;
213 }
214
215 if (Ehc->PeriodFrame != NULL) {
216 IoMmuFreeBuffer (Ehc->IoMmu, 1, Ehc->PeriodFrame, Ehc->PeriodFrameMap);
217 Ehc->PeriodFrame = NULL;
218 }
219}
220
232VOID
234 IN PEI_USB2_HC_DEV *Ehc,
235 IN PEI_EHC_QH *Qh
236 )
237{
238 PEI_EHC_QH *Head;
239
240 //
241 // Append the queue head after the reclaim header, then
242 // fix the hardware visiable parts (EHCI R1.0 page 72).
243 // ReclaimHead is always linked to the EHCI's AsynListAddr.
244 //
245 Head = Ehc->ReclaimHead;
246
247 Qh->NextQh = Head->NextQh;
248 Head->NextQh = Qh;
249
250 Qh->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);
251 Head->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);
252}
253
262VOID
264 IN PEI_USB2_HC_DEV *Ehc,
265 IN PEI_EHC_QH *Qh
266 )
267{
268 PEI_EHC_QH *Head;
269
270 ASSERT (Ehc->ReclaimHead->NextQh == Qh);
271
272 //
273 // Remove the QH from reclamation head, then update the hardware
274 // visiable part: Only need to loopback the ReclaimHead. The Qh
275 // is pointing to ReclaimHead (which is staill in the list).
276 //
277 Head = Ehc->ReclaimHead;
278
279 Head->NextQh = Qh->NextQh;
280 Qh->NextQh = NULL;
281
282 Head->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);
283
284 //
285 // Set and wait the door bell to synchronize with the hardware
286 //
287 EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);
288
289 return;
290}
291
303BOOLEAN
305 IN PEI_USB2_HC_DEV *Ehc,
306 IN PEI_URB *Urb
307 )
308{
309 EFI_LIST_ENTRY *Entry;
310 PEI_EHC_QTD *Qtd;
311 QTD_HW *QtdHw;
312 UINT8 State;
313 BOOLEAN Finished;
314
315 ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL));
316
317 Finished = TRUE;
318 Urb->Completed = 0;
319
320 Urb->Result = EFI_USB_NOERROR;
321
322 if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
323 Urb->Result |= EFI_USB_ERR_SYSTEM;
324 goto ON_EXIT;
325 }
326
327 BASE_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {
328 Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);
329 QtdHw = &Qtd->QtdHw;
330 State = (UINT8)QtdHw->Status;
331
332 if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) {
333 //
334 // EHCI will halt the queue head when met some error.
335 // If it is halted, the result of URB is finialized.
336 //
337 if ((State & QTD_STAT_ERR_MASK) == 0) {
338 Urb->Result |= EFI_USB_ERR_STALL;
339 }
340
341 if (EHC_BIT_IS_SET (State, QTD_STAT_BABBLE_ERR)) {
342 Urb->Result |= EFI_USB_ERR_BABBLE;
343 }
344
345 if (EHC_BIT_IS_SET (State, QTD_STAT_BUFF_ERR)) {
346 Urb->Result |= EFI_USB_ERR_BUFFER;
347 }
348
349 if (EHC_BIT_IS_SET (State, QTD_STAT_TRANS_ERR) && (QtdHw->ErrCnt == 0)) {
350 Urb->Result |= EFI_USB_ERR_TIMEOUT;
351 }
352
353 Finished = TRUE;
354 goto ON_EXIT;
355 } else if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) {
356 //
357 // The QTD is still active, no need to check furthur.
358 //
359 Urb->Result |= EFI_USB_ERR_NOTEXECUTE;
360
361 Finished = FALSE;
362 goto ON_EXIT;
363 } else {
364 //
365 // This QTD is finished OK or met short packet read. Update the
366 // transfer length if it isn't a setup.
367 //
368 if (QtdHw->Pid != QTD_PID_SETUP) {
369 Urb->Completed += Qtd->DataLen - QtdHw->TotalBytes;
370 }
371
372 if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) {
373 // EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));
374
375 //
376 // Short packet read condition. If it isn't a setup transfer,
377 // no need to check furthur: the queue head will halt at the
378 // ShortReadStop. If it is a setup transfer, need to check the
379 // Status Stage of the setup transfer to get the finial result
380 //
381 if (QtdHw->AltNext == QTD_LINK (Ehc->ShortReadStop, FALSE)) {
382 Finished = TRUE;
383 goto ON_EXIT;
384 }
385 }
386 }
387 }
388
389ON_EXIT:
390 //
391 // Return the data toggle set by EHCI hardware, bulk and interrupt
392 // transfer will use this to initialize the next transaction. For
393 // Control transfer, it always start a new data toggle sequence for
394 // new transfer.
395 //
396 // NOTICE: don't move DT update before the loop, otherwise there is
397 // a race condition that DT is wrong.
398 //
399 Urb->DataToggle = (UINT8)Urb->Qh->QhHw.DataToggle;
400
401 return Finished;
402}
403
418 IN PEI_USB2_HC_DEV *Ehc,
419 IN PEI_URB *Urb,
420 IN UINTN TimeOut
421 )
422{
423 EFI_STATUS Status;
424 UINTN Index;
425 UINTN Loop;
426 BOOLEAN Finished;
427 BOOLEAN InfiniteLoop;
428
429 Status = EFI_SUCCESS;
430 Loop = TimeOut * EHC_1_MILLISECOND;
431 Finished = FALSE;
432 InfiniteLoop = FALSE;
433
434 //
435 // If Timeout is 0, then the caller must wait for the function to be completed
436 // until EFI_SUCCESS or EFI_DEVICE_ERROR is returned.
437 //
438 if (TimeOut == 0) {
439 InfiniteLoop = TRUE;
440 }
441
442 for (Index = 0; InfiniteLoop || (Index < Loop); Index++) {
443 Finished = EhcCheckUrbResult (Ehc, Urb);
444
445 if (Finished) {
446 break;
447 }
448
449 MicroSecondDelay (EHC_1_MICROSECOND);
450 }
451
452 if (!Finished) {
453 Status = EFI_TIMEOUT;
454 } else if (Urb->Result != EFI_USB_NOERROR) {
455 Status = EFI_DEVICE_ERROR;
456 }
457
458 return Status;
459}
UINT64 UINTN
EFI_STATUS IoMmuAllocateBuffer(IN UINTN Pages, OUT VOID **HostAddress, OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, OUT VOID **Mapping)
Definition: DmaMem.c:170
EFI_STATUS IoMmuFreeBuffer(IN UINTN Pages, IN VOID *HostAddress, IN VOID *Mapping)
Definition: DmaMem.c:251
UINTN EFIAPI MicroSecondDelay(IN UINTN MicroSeconds)
#define BASE_LIST_FOR_EACH(Entry, ListHead)
Definition: BaseLib.h:2913
BOOLEAN EhcCheckUrbResult(IN USB2_HC_DEV *Ehc, IN URB *Urb)
Definition: EhciSched.c:549
EFI_STATUS EhcInitSched(IN USB2_HC_DEV *Ehc)
Definition: EhciSched.c:102
VOID EhcLinkQhToAsync(IN USB2_HC_DEV *Ehc, IN EHC_QH *Qh)
Definition: EhciSched.c:312
VOID EhcFreeSched(IN USB2_HC_DEV *Ehc)
Definition: EhciSched.c:250
EFI_STATUS EhcCreateHelpQ(IN USB2_HC_DEV *Ehc)
Definition: EhciSched.c:23
EFI_STATUS EhcExecTransfer(IN USB2_HC_DEV *Ehc, IN URB *Urb, IN UINTN TimeOut)
Definition: EhciSched.c:668
VOID EhcUnlinkQhFromAsync(IN USB2_HC_DEV *Ehc, IN EHC_QH *Qh)
Definition: EhciSched.c:345
EHC_QTD * EhcCreateQtd(IN USB2_HC_DEV *Ehc, IN UINT8 *Data, IN UINT8 *DataPhy, IN UINTN DataLen, IN UINT8 PktId, IN UINT8 Toggle, IN UINTN MaxPacket)
Definition: EhciUrb.c:29
EHC_QH * EhcCreateQh(IN USB2_HC_DEV *Ehci, IN USB_ENDPOINT *Ep)
Definition: EhciUrb.c:167
EFI_PHYSICAL_ADDRESS UsbHcGetPciAddressForHostMem(IN USBHC_MEM_POOL *Pool, IN VOID *Mem, IN UINTN Size)
Definition: UsbHcMem.c:223
EFI_STATUS UsbHcFreeMemPool(IN USBHC_MEM_POOL *Pool)
Definition: UsbHcMem.c:385
VOID UsbHcFreeMem(IN USBHC_MEM_POOL *Pool, IN VOID *Mem, IN UINTN Size)
Definition: UsbHcMem.c:493
USBHC_MEM_POOL * UsbHcInitMemPool(IN EFI_PCI_IO_PROTOCOL *PciIo, IN BOOLEAN Check4G, IN UINT32 Which4G)
Definition: UsbHcMem.c:348
VOID EhcWriteOpReg(IN USB2_HC_DEV *Ehc, IN UINT32 Offset, IN UINT32 Data)
Definition: EhciReg.c:183
EFI_STATUS EhcSetAndWaitDoorBell(IN USB2_HC_DEV *Ehc, IN UINT32 Timeout)
Definition: EhciReg.c:343
BOOLEAN EhcIsHalt(IN USB2_HC_DEV *Ehc)
Definition: EhciReg.c:442
BOOLEAN EhcIsSysError(IN USB2_HC_DEV *Ehc)
Definition: EhciReg.c:459
#define NULL
Definition: Base.h:319
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
Definition: EhciUrb.h:104
Definition: EhciUrb.h:88