]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c
91c2a485017a4695d6afc50b3ce93bf55fd05360
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / UhciDxe / UhciQueue.c
1 /** @file
2
3 The UHCI register operation routines.
4
5 Copyright (c) 2007 - 2008, Intel Corporation
6 All rights reserved. This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "Uhci.h"
17
18
19 /**
20 Map address of request structure buffer.
21
22 @param Uhc The UHCI device.
23 @param Request The user request buffer.
24 @param MappedAddr Mapped address of request.
25 @param Map Identificaion of this mapping to return.
26
27 @return EFI_SUCCESS Success.
28 @return EFI_DEVICE_ERROR Fail to map the user request.
29
30 **/
31 EFI_STATUS
32 UhciMapUserRequest (
33 IN USB_HC_DEV *Uhc,
34 IN OUT VOID *Request,
35 OUT UINT8 **MappedAddr,
36 OUT VOID **Map
37 )
38 {
39 EFI_STATUS Status;
40 UINTN Len;
41 EFI_PHYSICAL_ADDRESS PhyAddr;
42
43 Len = sizeof (EFI_USB_DEVICE_REQUEST);
44 Status = Uhc->PciIo->Map (
45 Uhc->PciIo,
46 EfiPciIoOperationBusMasterRead,
47 Request,
48 &Len,
49 &PhyAddr,
50 Map
51 );
52
53 if (!EFI_ERROR (Status)) {
54 *MappedAddr = (UINT8 *) (UINTN) PhyAddr;
55 }
56
57 return Status;
58 }
59
60
61 /**
62 Map address of user data buffer.
63
64 @param Uhc The UHCI device.
65 @param Direction Direction of the data transfer.
66 @param Data The user data buffer.
67 @param Len Length of the user data.
68 @param PktId Packet identificaion.
69 @param MappedAddr Mapped address to return.
70 @param Map Identificaion of this mapping to return.
71
72 @return EFI_SUCCESS Success.
73 @return EFI_DEVICE_ERROR Fail to map the user data.
74
75 **/
76 EFI_STATUS
77 UhciMapUserData (
78 IN USB_HC_DEV *Uhc,
79 IN EFI_USB_DATA_DIRECTION Direction,
80 IN VOID *Data,
81 IN OUT UINTN *Len,
82 OUT UINT8 *PktId,
83 OUT UINT8 **MappedAddr,
84 OUT VOID **Map
85 )
86 {
87 EFI_STATUS Status;
88 EFI_PHYSICAL_ADDRESS PhyAddr;
89
90 Status = EFI_SUCCESS;
91
92 switch (Direction) {
93 case EfiUsbDataIn:
94 //
95 // BusMasterWrite means cpu read
96 //
97 *PktId = INPUT_PACKET_ID;
98 Status = Uhc->PciIo->Map (
99 Uhc->PciIo,
100 EfiPciIoOperationBusMasterWrite,
101 Data,
102 Len,
103 &PhyAddr,
104 Map
105 );
106
107 if (EFI_ERROR (Status)) {
108 goto EXIT;
109 }
110
111 *MappedAddr = (UINT8 *) (UINTN) PhyAddr;
112 break;
113
114 case EfiUsbDataOut:
115 *PktId = OUTPUT_PACKET_ID;
116 Status = Uhc->PciIo->Map (
117 Uhc->PciIo,
118 EfiPciIoOperationBusMasterRead,
119 Data,
120 Len,
121 &PhyAddr,
122 Map
123 );
124
125 if (EFI_ERROR (Status)) {
126 goto EXIT;
127 }
128
129 *MappedAddr = (UINT8 *) (UINTN) PhyAddr;
130 break;
131
132 case EfiUsbNoData:
133 if ((Len != NULL) && (*Len != 0)) {
134 Status = EFI_INVALID_PARAMETER;
135 goto EXIT;
136 }
137
138 *PktId = OUTPUT_PACKET_ID;
139 *MappedAddr = NULL;
140 *Map = NULL;
141 break;
142
143 default:
144 Status = EFI_INVALID_PARAMETER;
145 }
146
147 EXIT:
148 return Status;
149 }
150
151
152 /**
153 Link the TD To QH.
154
155 @param Qh The queue head for the TD to link to.
156 @param Td The TD to link.
157
158 @return None.
159
160 **/
161 VOID
162 UhciLinkTdToQh (
163 IN UHCI_QH_SW *Qh,
164 IN UHCI_TD_SW *Td
165 )
166 {
167 ASSERT ((Qh != NULL) && (Td != NULL));
168
169 Qh->QhHw.VerticalLink = QH_VLINK (Td, FALSE);
170 Qh->TDs = (VOID *) Td;
171 }
172
173
174 /**
175 Unlink TD from the QH.
176
177 @param Qh The queue head to unlink from.
178 @param Td The TD to unlink.
179
180 @return None.
181
182 **/
183 VOID
184 UhciUnlinkTdFromQh (
185 IN UHCI_QH_SW *Qh,
186 IN UHCI_TD_SW *Td
187 )
188 {
189 ASSERT ((Qh != NULL) && (Td != NULL));
190
191 Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
192 Qh->TDs = NULL;
193 }
194
195
196 /**
197 Append a new TD To the previous TD.
198
199 @param PrevTd Previous UHCI_TD_SW to be linked to.
200 @param ThisTd TD to link.
201
202 @return None.
203
204 **/
205 VOID
206 UhciAppendTd (
207 IN UHCI_TD_SW *PrevTd,
208 IN UHCI_TD_SW *ThisTd
209 )
210 {
211 ASSERT ((PrevTd != NULL) && (ThisTd != NULL));
212
213 PrevTd->TdHw.NextLink = TD_LINK (ThisTd, TRUE, FALSE);
214 PrevTd->NextTd = (VOID *) ThisTd;
215 }
216
217
218 /**
219 Delete a list of TDs.
220
221 @param Uhc The UHCI device.
222 @param FirstTd TD link list head.
223
224 @return None.
225
226 **/
227 VOID
228 UhciDestoryTds (
229 IN USB_HC_DEV *Uhc,
230 IN UHCI_TD_SW *FirstTd
231 )
232 {
233 UHCI_TD_SW *NextTd;
234 UHCI_TD_SW *ThisTd;
235
236 NextTd = FirstTd;
237
238 while (NextTd != NULL) {
239 ThisTd = NextTd;
240 NextTd = ThisTd->NextTd;
241 UsbHcFreeMem (Uhc->MemPool, ThisTd, sizeof (UHCI_TD_SW));
242 }
243 }
244
245
246 /**
247 Create an initialize a new queue head.
248
249 @param Uhc The UHCI device.
250 @param Interval The polling interval for the queue.
251
252 @return The newly created queue header.
253
254 **/
255 UHCI_QH_SW *
256 UhciCreateQh (
257 IN USB_HC_DEV *Uhc,
258 IN UINTN Interval
259 )
260 {
261 UHCI_QH_SW *Qh;
262
263 Qh = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_QH_SW));
264
265 if (Qh == NULL) {
266 return NULL;
267 }
268
269 Qh->QhHw.HorizonLink = QH_HLINK (NULL, TRUE);
270 Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
271 Qh->Interval = UhciConvertPollRate(Interval);
272 Qh->TDs = NULL;
273 Qh->NextQh = NULL;
274
275 return Qh;
276 }
277
278
279 /**
280 Create and intialize a TD.
281
282 @param Uhc The UHCI device.
283
284 @return The newly allocated and initialized TD.
285
286 **/
287 UHCI_TD_SW *
288 UhciCreateTd (
289 IN USB_HC_DEV *Uhc
290 )
291 {
292 UHCI_TD_SW *Td;
293
294 Td = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_TD_SW));
295 if (Td == NULL) {
296 return NULL;
297 }
298
299 Td->TdHw.NextLink = TD_LINK (NULL, FALSE, TRUE);
300 Td->NextTd = NULL;
301 Td->Data = NULL;
302 Td->DataLen = 0;
303
304 return Td;
305 }
306
307
308 /**
309 Create and initialize a TD for Setup Stage of a control transfer.
310
311 @param Uhc The UHCI device.
312 @param DevAddr Device address.
313 @param Request Device request.
314 @param IsLow Full speed or low speed.
315
316 @return The created setup Td Pointer.
317
318 **/
319 UHCI_TD_SW *
320 UhciCreateSetupTd (
321 IN USB_HC_DEV *Uhc,
322 IN UINT8 DevAddr,
323 IN UINT8 *Request,
324 IN BOOLEAN IsLow
325 )
326 {
327 UHCI_TD_SW *Td;
328
329 Td = UhciCreateTd (Uhc);
330
331 if (Td == NULL) {
332 return NULL;
333 }
334
335 Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);
336 Td->TdHw.ShortPacket = FALSE;
337 Td->TdHw.IsIsoch = FALSE;
338 Td->TdHw.IntOnCpl = FALSE;
339 Td->TdHw.ErrorCount = 0x03;
340 Td->TdHw.Status |= USBTD_ACTIVE;
341 Td->TdHw.DataToggle = 0;
342 Td->TdHw.EndPoint = 0;
343 Td->TdHw.LowSpeed = IsLow ? 1 : 0;
344 Td->TdHw.DeviceAddr = DevAddr & 0x7F;
345 Td->TdHw.MaxPacketLen = (UINT32) (sizeof (EFI_USB_DEVICE_REQUEST) - 1);
346 Td->TdHw.PidCode = SETUP_PACKET_ID;
347 Td->TdHw.DataBuffer = (UINT32) (UINTN) Request;
348
349 Td->Data = Request;
350 Td->DataLen = sizeof (EFI_USB_DEVICE_REQUEST);
351
352 return Td;
353 }
354
355
356 /**
357 Create a TD for data.
358
359 @param Uhc The UHCI device.
360 @param DevAddr Device address.
361 @param Endpoint Endpoint number.
362 @param DataPtr Data buffer.
363 @param Len Data length.
364 @param PktId Packet ID.
365 @param Toggle Data toggle value.
366 @param IsLow Full speed or low speed.
367
368 @return Data Td pointer if success, otherwise NULL.
369
370 **/
371 UHCI_TD_SW *
372 UhciCreateDataTd (
373 IN USB_HC_DEV *Uhc,
374 IN UINT8 DevAddr,
375 IN UINT8 Endpoint,
376 IN UINT8 *DataPtr,
377 IN UINTN Len,
378 IN UINT8 PktId,
379 IN UINT8 Toggle,
380 IN BOOLEAN IsLow
381 )
382 {
383 UHCI_TD_SW *Td;
384
385 //
386 // Code as length - 1, and the max valid length is 0x500
387 //
388 ASSERT (Len <= 0x500);
389
390 Td = UhciCreateTd (Uhc);
391
392 if (Td == NULL) {
393 return NULL;
394 }
395
396 Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);
397 Td->TdHw.ShortPacket = FALSE;
398 Td->TdHw.IsIsoch = FALSE;
399 Td->TdHw.IntOnCpl = FALSE;
400 Td->TdHw.ErrorCount = 0X03;
401 Td->TdHw.Status = USBTD_ACTIVE;
402 Td->TdHw.LowSpeed = IsLow ? 1 : 0;
403 Td->TdHw.DataToggle = Toggle & 0x01;
404 Td->TdHw.EndPoint = Endpoint & 0x0F;
405 Td->TdHw.DeviceAddr = DevAddr & 0x7F;
406 Td->TdHw.MaxPacketLen = (UINT32) (Len - 1);
407 Td->TdHw.PidCode = (UINT8) PktId;
408 Td->TdHw.DataBuffer = (UINT32) (UINTN) DataPtr;
409
410 Td->Data = DataPtr;
411 Td->DataLen = (UINT16) Len;
412
413 return Td;
414 }
415
416
417 /**
418 Create TD for the Status Stage of control transfer.
419
420 @param Uhc The UHCI device.
421 @param DevAddr Device address.
422 @param PktId Packet ID.
423 @param IsLow Full speed or low speed.
424
425 @return Status Td Pointer.
426
427 **/
428 UHCI_TD_SW *
429 UhciCreateStatusTd (
430 IN USB_HC_DEV *Uhc,
431 IN UINT8 DevAddr,
432 IN UINT8 PktId,
433 IN BOOLEAN IsLow
434 )
435 {
436 UHCI_TD_SW *Td;
437
438 Td = UhciCreateTd (Uhc);
439
440 if (Td == NULL) {
441 return NULL;
442 }
443
444 Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);
445 Td->TdHw.ShortPacket = FALSE;
446 Td->TdHw.IsIsoch = FALSE;
447 Td->TdHw.IntOnCpl = FALSE;
448 Td->TdHw.ErrorCount = 0x03;
449 Td->TdHw.Status |= USBTD_ACTIVE;
450 Td->TdHw.MaxPacketLen = 0x7FF; //0x7FF: there is no data (refer to UHCI spec)
451 Td->TdHw.DataToggle = 1;
452 Td->TdHw.EndPoint = 0;
453 Td->TdHw.LowSpeed = IsLow ? 1 : 0;
454 Td->TdHw.DeviceAddr = DevAddr & 0x7F;
455 Td->TdHw.PidCode = (UINT8) PktId;
456 Td->TdHw.DataBuffer = (UINT32) (UINTN) NULL;
457
458 Td->Data = NULL;
459 Td->DataLen = 0;
460
461 return Td;
462 }
463
464
465 /**
466 Create Tds list for Control Transfer.
467
468 @param Uhc The UHCI device.
469 @param DeviceAddr The device address.
470 @param DataPktId Packet Identification of Data Tds.
471 @param Request A pointer to request structure buffer to transfer.
472 @param Data A pointer to user data buffer to transfer.
473 @param DataLen Length of user data to transfer.
474 @param MaxPacket Maximum packet size for control transfer.
475 @param IsLow Full speed or low speed.
476
477 @return The Td list head for the control transfer.
478
479 **/
480 UHCI_TD_SW *
481 UhciCreateCtrlTds (
482 IN USB_HC_DEV *Uhc,
483 IN UINT8 DeviceAddr,
484 IN UINT8 DataPktId,
485 IN UINT8 *Request,
486 IN UINT8 *Data,
487 IN UINTN DataLen,
488 IN UINT8 MaxPacket,
489 IN BOOLEAN IsLow
490 )
491 {
492 UHCI_TD_SW *SetupTd;
493 UHCI_TD_SW *FirstDataTd;
494 UHCI_TD_SW *DataTd;
495 UHCI_TD_SW *PrevDataTd;
496 UHCI_TD_SW *StatusTd;
497 UINT8 DataToggle;
498 UINT8 StatusPktId;
499 UINTN ThisTdLen;
500
501
502 DataTd = NULL;
503 SetupTd = NULL;
504 FirstDataTd = NULL;
505 PrevDataTd = NULL;
506 StatusTd = NULL;
507
508 //
509 // Create setup packets for the transfer
510 //
511 SetupTd = UhciCreateSetupTd (Uhc, DeviceAddr, Request, IsLow);
512
513 if (SetupTd == NULL) {
514 return NULL;
515 }
516
517 //
518 // Create data packets for the transfer
519 //
520 DataToggle = 1;
521
522 while (DataLen > 0) {
523 //
524 // PktSize is the data load size in each Td.
525 //
526 ThisTdLen = (DataLen > MaxPacket ? MaxPacket : DataLen);
527
528 DataTd = UhciCreateDataTd (
529 Uhc,
530 DeviceAddr,
531 0,
532 Data,
533 ThisTdLen,
534 DataPktId,
535 DataToggle,
536 IsLow
537 );
538
539 if (DataTd == NULL) {
540 goto FREE_TD;
541 }
542
543 if (FirstDataTd == NULL) {
544 FirstDataTd = DataTd;
545 FirstDataTd->NextTd = NULL;
546 } else {
547 UhciAppendTd (PrevDataTd, DataTd);
548 }
549
550 DataToggle ^= 1;
551 PrevDataTd = DataTd;
552 Data += ThisTdLen;
553 DataLen -= ThisTdLen;
554 }
555
556 //
557 // Status packet is on the opposite direction to data packets
558 //
559 if (OUTPUT_PACKET_ID == DataPktId) {
560 StatusPktId = INPUT_PACKET_ID;
561 } else {
562 StatusPktId = OUTPUT_PACKET_ID;
563 }
564
565 StatusTd = UhciCreateStatusTd (Uhc, DeviceAddr, StatusPktId, IsLow);
566
567 if (StatusTd == NULL) {
568 goto FREE_TD;
569 }
570
571 //
572 // Link setup Td -> data Tds -> status Td together
573 //
574 if (FirstDataTd != NULL) {
575 UhciAppendTd (SetupTd, FirstDataTd);
576 UhciAppendTd (PrevDataTd, StatusTd);
577 } else {
578 UhciAppendTd (SetupTd, StatusTd);
579 }
580
581 return SetupTd;
582
583 FREE_TD:
584 if (SetupTd != NULL) {
585 UhciDestoryTds (Uhc, SetupTd);
586 }
587
588 if (FirstDataTd != NULL) {
589 UhciDestoryTds (Uhc, FirstDataTd);
590 }
591
592 return NULL;
593 }
594
595
596 /**
597 Create Tds list for Bulk/Interrupt Transfer.
598
599 @param Uhc USB_HC_DEV.
600 @param DevAddr Address of Device.
601 @param EndPoint Endpoint Number.
602 @param PktId Packet Identification of Data Tds.
603 @param Data A pointer to user data buffer to transfer.
604 @param DataLen Length of user data to transfer.
605 @param DataToggle Data Toggle Pointer.
606 @param MaxPacket Maximum packet size for Bulk/Interrupt transfer.
607 @param IsLow Is Low Speed Device.
608
609 @return The Tds list head for the bulk transfer.
610
611 **/
612 UHCI_TD_SW *
613 UhciCreateBulkOrIntTds (
614 IN USB_HC_DEV *Uhc,
615 IN UINT8 DevAddr,
616 IN UINT8 EndPoint,
617 IN UINT8 PktId,
618 IN UINT8 *Data,
619 IN UINTN DataLen,
620 IN OUT UINT8 *DataToggle,
621 IN UINT8 MaxPacket,
622 IN BOOLEAN IsLow
623 )
624 {
625 UHCI_TD_SW *DataTd;
626 UHCI_TD_SW *FirstDataTd;
627 UHCI_TD_SW *PrevDataTd;
628 UINTN ThisTdLen;
629
630 DataTd = NULL;
631 FirstDataTd = NULL;
632 PrevDataTd = NULL;
633
634 //
635 // Create data packets for the transfer
636 //
637 while (DataLen > 0) {
638 //
639 // PktSize is the data load size that each Td.
640 //
641 ThisTdLen = DataLen;
642
643 if (DataLen > MaxPacket) {
644 ThisTdLen = MaxPacket;
645 }
646
647 DataTd = UhciCreateDataTd (
648 Uhc,
649 DevAddr,
650 EndPoint,
651 Data,
652 ThisTdLen,
653 PktId,
654 *DataToggle,
655 IsLow
656 );
657
658 if (DataTd == NULL) {
659 goto FREE_TD;
660 }
661
662 if (PktId == INPUT_PACKET_ID) {
663 DataTd->TdHw.ShortPacket = TRUE;
664 }
665
666 if (FirstDataTd == NULL) {
667 FirstDataTd = DataTd;
668 FirstDataTd->NextTd = NULL;
669 } else {
670 UhciAppendTd (PrevDataTd, DataTd);
671 }
672
673 *DataToggle ^= 1;
674 PrevDataTd = DataTd;
675 Data += ThisTdLen;
676 DataLen -= ThisTdLen;
677 }
678
679 return FirstDataTd;
680
681 FREE_TD:
682 if (FirstDataTd != NULL) {
683 UhciDestoryTds (Uhc, FirstDataTd);
684 }
685
686 return NULL;
687 }