1 /** @file
2
3 The UHCI register operation routines.
4
5 Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
6 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
UhciMapUserRequest(IN USB_HC_DEV * Uhc,IN OUT VOID * Request,OUT UINT8 ** MappedAddr,OUT VOID ** Map)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
UhciMapUserData(IN USB_HC_DEV * Uhc,IN EFI_USB_DATA_DIRECTION Direction,IN VOID * Data,IN OUT UINTN * Len,OUT UINT8 * PktId,OUT UINT8 ** MappedAddr,OUT VOID ** Map)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 Uhc The UHCI device.
156 @param Qh The queue head for the TD to link to.
157 @param Td The TD to link.
158
159 **/
160 VOID
UhciLinkTdToQh(IN USB_HC_DEV * Uhc,IN UHCI_QH_SW * Qh,IN UHCI_TD_SW * Td)161 UhciLinkTdToQh (
162 IN USB_HC_DEV *Uhc,
163 IN UHCI_QH_SW *Qh,
164 IN UHCI_TD_SW *Td
165 )
166 {
167 EFI_PHYSICAL_ADDRESS PhyAddr;
168
169 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Td, sizeof (UHCI_TD_HW));
170
171 ASSERT ((Qh != NULL) && (Td != NULL));
172
173 Qh->QhHw.VerticalLink = QH_VLINK (PhyAddr, FALSE);
174 Qh->TDs = (VOID *) Td;
175 }
176
177
178 /**
179 Unlink TD from the QH.
180
181 @param Qh The queue head to unlink from.
182 @param Td The TD to unlink.
183
184 **/
185 VOID
UhciUnlinkTdFromQh(IN UHCI_QH_SW * Qh,IN UHCI_TD_SW * Td)186 UhciUnlinkTdFromQh (
187 IN UHCI_QH_SW *Qh,
188 IN UHCI_TD_SW *Td
189 )
190 {
191 ASSERT ((Qh != NULL) && (Td != NULL));
192
193 Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
194 Qh->TDs = NULL;
195 }
196
197
198 /**
199 Append a new TD To the previous TD.
200
201 @param Uhc The UHCI device.
202 @param PrevTd Previous UHCI_TD_SW to be linked to.
203 @param ThisTd TD to link.
204
205 **/
206 VOID
UhciAppendTd(IN USB_HC_DEV * Uhc,IN UHCI_TD_SW * PrevTd,IN UHCI_TD_SW * ThisTd)207 UhciAppendTd (
208 IN USB_HC_DEV *Uhc,
209 IN UHCI_TD_SW *PrevTd,
210 IN UHCI_TD_SW *ThisTd
211 )
212 {
213 EFI_PHYSICAL_ADDRESS PhyAddr;
214
215 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, ThisTd, sizeof (UHCI_TD_HW));
216
217 ASSERT ((PrevTd != NULL) && (ThisTd != NULL));
218
219 PrevTd->TdHw.NextLink = TD_LINK (PhyAddr, TRUE, FALSE);
220 PrevTd->NextTd = (VOID *) ThisTd;
221 }
222
223
224 /**
225 Delete a list of TDs.
226
227 @param Uhc The UHCI device.
228 @param FirstTd TD link list head.
229
230 @return None.
231
232 **/
233 VOID
UhciDestoryTds(IN USB_HC_DEV * Uhc,IN UHCI_TD_SW * FirstTd)234 UhciDestoryTds (
235 IN USB_HC_DEV *Uhc,
236 IN UHCI_TD_SW *FirstTd
237 )
238 {
239 UHCI_TD_SW *NextTd;
240 UHCI_TD_SW *ThisTd;
241
242 NextTd = FirstTd;
243
244 while (NextTd != NULL) {
245 ThisTd = NextTd;
246 NextTd = ThisTd->NextTd;
247 UsbHcFreeMem (Uhc->MemPool, ThisTd, sizeof (UHCI_TD_SW));
248 }
249 }
250
251
252 /**
253 Create an initialize a new queue head.
254
255 @param Uhc The UHCI device.
256 @param Interval The polling interval for the queue.
257
258 @return The newly created queue header.
259
260 **/
261 UHCI_QH_SW *
UhciCreateQh(IN USB_HC_DEV * Uhc,IN UINTN Interval)262 UhciCreateQh (
263 IN USB_HC_DEV *Uhc,
264 IN UINTN Interval
265 )
266 {
267 UHCI_QH_SW *Qh;
268
269 Qh = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_QH_SW));
270
271 if (Qh == NULL) {
272 return NULL;
273 }
274
275 Qh->QhHw.HorizonLink = QH_HLINK (NULL, TRUE);
276 Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
277 Qh->Interval = UhciConvertPollRate(Interval);
278 Qh->TDs = NULL;
279 Qh->NextQh = NULL;
280
281 return Qh;
282 }
283
284
285 /**
286 Create and intialize a TD.
287
288 @param Uhc The UHCI device.
289
290 @return The newly allocated and initialized TD.
291
292 **/
293 UHCI_TD_SW *
UhciCreateTd(IN USB_HC_DEV * Uhc)294 UhciCreateTd (
295 IN USB_HC_DEV *Uhc
296 )
297 {
298 UHCI_TD_SW *Td;
299
300 Td = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_TD_SW));
301 if (Td == NULL) {
302 return NULL;
303 }
304
305 Td->TdHw.NextLink = TD_LINK (NULL, FALSE, TRUE);
306 Td->NextTd = NULL;
307 Td->Data = NULL;
308 Td->DataLen = 0;
309
310 return Td;
311 }
312
313
314 /**
315 Create and initialize a TD for Setup Stage of a control transfer.
316
317 @param Uhc The UHCI device.
318 @param DevAddr Device address.
319 @param Request A pointer to cpu memory address of Device request.
320 @param RequestPhy A pointer to pci memory address of Device request.
321 @param IsLow Full speed or low speed.
322
323 @return The created setup Td Pointer.
324
325 **/
326 UHCI_TD_SW *
UhciCreateSetupTd(IN USB_HC_DEV * Uhc,IN UINT8 DevAddr,IN UINT8 * Request,IN UINT8 * RequestPhy,IN BOOLEAN IsLow)327 UhciCreateSetupTd (
328 IN USB_HC_DEV *Uhc,
329 IN UINT8 DevAddr,
330 IN UINT8 *Request,
331 IN UINT8 *RequestPhy,
332 IN BOOLEAN IsLow
333 )
334 {
335 UHCI_TD_SW *Td;
336
337 Td = UhciCreateTd (Uhc);
338
339 if (Td == NULL) {
340 return NULL;
341 }
342
343 Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);
344 Td->TdHw.ShortPacket = FALSE;
345 Td->TdHw.IsIsoch = FALSE;
346 Td->TdHw.IntOnCpl = FALSE;
347 Td->TdHw.ErrorCount = 0x03;
348 Td->TdHw.Status |= USBTD_ACTIVE;
349 Td->TdHw.DataToggle = 0;
350 Td->TdHw.EndPoint = 0;
351 Td->TdHw.LowSpeed = IsLow ? 1 : 0;
352 Td->TdHw.DeviceAddr = DevAddr & 0x7F;
353 Td->TdHw.MaxPacketLen = (UINT32) (sizeof (EFI_USB_DEVICE_REQUEST) - 1);
354 Td->TdHw.PidCode = SETUP_PACKET_ID;
355 Td->TdHw.DataBuffer = (UINT32) (UINTN) RequestPhy;
356
357 Td->Data = Request;
358 Td->DataLen = (UINT16) sizeof (EFI_USB_DEVICE_REQUEST);
359
360 return Td;
361 }
362
363
364 /**
365 Create a TD for data.
366
367 @param Uhc The UHCI device.
368 @param DevAddr Device address.
369 @param Endpoint Endpoint number.
370 @param DataPtr A pointer to cpu memory address of Data buffer.
371 @param DataPhyPtr A pointer to pci memory address of Data buffer.
372 @param Len Data length.
373 @param PktId Packet ID.
374 @param Toggle Data toggle value.
375 @param IsLow Full speed or low speed.
376
377 @return Data Td pointer if success, otherwise NULL.
378
379 **/
380 UHCI_TD_SW *
UhciCreateDataTd(IN USB_HC_DEV * Uhc,IN UINT8 DevAddr,IN UINT8 Endpoint,IN UINT8 * DataPtr,IN UINT8 * DataPhyPtr,IN UINTN Len,IN UINT8 PktId,IN UINT8 Toggle,IN BOOLEAN IsLow)381 UhciCreateDataTd (
382 IN USB_HC_DEV *Uhc,
383 IN UINT8 DevAddr,
384 IN UINT8 Endpoint,
385 IN UINT8 *DataPtr,
386 IN UINT8 *DataPhyPtr,
387 IN UINTN Len,
388 IN UINT8 PktId,
389 IN UINT8 Toggle,
390 IN BOOLEAN IsLow
391 )
392 {
393 UHCI_TD_SW *Td;
394
395 //
396 // Code as length - 1, and the max valid length is 0x500
397 //
398 ASSERT (Len <= 0x500);
399
400 Td = UhciCreateTd (Uhc);
401
402 if (Td == NULL) {
403 return NULL;
404 }
405
406 Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);
407 Td->TdHw.ShortPacket = FALSE;
408 Td->TdHw.IsIsoch = FALSE;
409 Td->TdHw.IntOnCpl = FALSE;
410 Td->TdHw.ErrorCount = 0x03;
411 Td->TdHw.Status = USBTD_ACTIVE;
412 Td->TdHw.LowSpeed = IsLow ? 1 : 0;
413 Td->TdHw.DataToggle = Toggle & 0x01;
414 Td->TdHw.EndPoint = Endpoint & 0x0F;
415 Td->TdHw.DeviceAddr = DevAddr & 0x7F;
416 Td->TdHw.MaxPacketLen = (UINT32) (Len - 1);
417 Td->TdHw.PidCode = (UINT8) PktId;
418 Td->TdHw.DataBuffer = (UINT32) (UINTN) DataPhyPtr;
419
420 Td->Data = DataPtr;
421 Td->DataLen = (UINT16) Len;
422
423 return Td;
424 }
425
426
427 /**
428 Create TD for the Status Stage of control transfer.
429
430 @param Uhc The UHCI device.
431 @param DevAddr Device address.
432 @param PktId Packet ID.
433 @param IsLow Full speed or low speed.
434
435 @return Status Td Pointer.
436
437 **/
438 UHCI_TD_SW *
UhciCreateStatusTd(IN USB_HC_DEV * Uhc,IN UINT8 DevAddr,IN UINT8 PktId,IN BOOLEAN IsLow)439 UhciCreateStatusTd (
440 IN USB_HC_DEV *Uhc,
441 IN UINT8 DevAddr,
442 IN UINT8 PktId,
443 IN BOOLEAN IsLow
444 )
445 {
446 UHCI_TD_SW *Td;
447
448 Td = UhciCreateTd (Uhc);
449
450 if (Td == NULL) {
451 return NULL;
452 }
453
454 Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE);
455 Td->TdHw.ShortPacket = FALSE;
456 Td->TdHw.IsIsoch = FALSE;
457 Td->TdHw.IntOnCpl = FALSE;
458 Td->TdHw.ErrorCount = 0x03;
459 Td->TdHw.Status |= USBTD_ACTIVE;
460 Td->TdHw.MaxPacketLen = 0x7FF; //0x7FF: there is no data (refer to UHCI spec)
461 Td->TdHw.DataToggle = 1;
462 Td->TdHw.EndPoint = 0;
463 Td->TdHw.LowSpeed = IsLow ? 1 : 0;
464 Td->TdHw.DeviceAddr = DevAddr & 0x7F;
465 Td->TdHw.PidCode = (UINT8) PktId;
466 Td->TdHw.DataBuffer = (UINT32) (UINTN) NULL;
467
468 Td->Data = NULL;
469 Td->DataLen = 0;
470
471 return Td;
472 }
473
474
475 /**
476 Create Tds list for Control Transfer.
477
478 @param Uhc The UHCI device.
479 @param DeviceAddr The device address.
480 @param DataPktId Packet Identification of Data Tds.
481 @param Request A pointer to cpu memory address of request structure buffer to transfer.
482 @param RequestPhy A pointer to pci memory address of request structure buffer to transfer.
483 @param Data A pointer to cpu memory address of user data buffer to transfer.
484 @param DataPhy A pointer to pci memory address of user data buffer to transfer.
485 @param DataLen Length of user data to transfer.
486 @param MaxPacket Maximum packet size for control transfer.
487 @param IsLow Full speed or low speed.
488
489 @return The Td list head for the control transfer.
490
491 **/
492 UHCI_TD_SW *
UhciCreateCtrlTds(IN USB_HC_DEV * Uhc,IN UINT8 DeviceAddr,IN UINT8 DataPktId,IN UINT8 * Request,IN UINT8 * RequestPhy,IN UINT8 * Data,IN UINT8 * DataPhy,IN UINTN DataLen,IN UINT8 MaxPacket,IN BOOLEAN IsLow)493 UhciCreateCtrlTds (
494 IN USB_HC_DEV *Uhc,
495 IN UINT8 DeviceAddr,
496 IN UINT8 DataPktId,
497 IN UINT8 *Request,
498 IN UINT8 *RequestPhy,
499 IN UINT8 *Data,
500 IN UINT8 *DataPhy,
501 IN UINTN DataLen,
502 IN UINT8 MaxPacket,
503 IN BOOLEAN IsLow
504 )
505 {
506 UHCI_TD_SW *SetupTd;
507 UHCI_TD_SW *FirstDataTd;
508 UHCI_TD_SW *DataTd;
509 UHCI_TD_SW *PrevDataTd;
510 UHCI_TD_SW *StatusTd;
511 UINT8 DataToggle;
512 UINT8 StatusPktId;
513 UINTN ThisTdLen;
514
515
516 DataTd = NULL;
517 SetupTd = NULL;
518 FirstDataTd = NULL;
519 PrevDataTd = NULL;
520 StatusTd = NULL;
521
522 //
523 // Create setup packets for the transfer
524 //
525 SetupTd = UhciCreateSetupTd (Uhc, DeviceAddr, Request, RequestPhy, IsLow);
526
527 if (SetupTd == NULL) {
528 return NULL;
529 }
530
531 //
532 // Create data packets for the transfer
533 //
534 DataToggle = 1;
535
536 while (DataLen > 0) {
537 //
538 // PktSize is the data load size in each Td.
539 //
540 ThisTdLen = (DataLen > MaxPacket ? MaxPacket : DataLen);
541
542 DataTd = UhciCreateDataTd (
543 Uhc,
544 DeviceAddr,
545 0,
546 Data, //cpu memory address
547 DataPhy, //Pci memory address
548 ThisTdLen,
549 DataPktId,
550 DataToggle,
551 IsLow
552 );
553
554 if (DataTd == NULL) {
555 goto FREE_TD;
556 }
557
558 if (FirstDataTd == NULL) {
559 FirstDataTd = DataTd;
560 FirstDataTd->NextTd = NULL;
561 } else {
562 UhciAppendTd (Uhc, PrevDataTd, DataTd);
563 }
564
565 DataToggle ^= 1;
566 PrevDataTd = DataTd;
567 Data += ThisTdLen;
568 DataPhy += ThisTdLen;
569 DataLen -= ThisTdLen;
570 }
571
572 //
573 // Status packet is on the opposite direction to data packets
574 //
575 if (OUTPUT_PACKET_ID == DataPktId) {
576 StatusPktId = INPUT_PACKET_ID;
577 } else {
578 StatusPktId = OUTPUT_PACKET_ID;
579 }
580
581 StatusTd = UhciCreateStatusTd (Uhc, DeviceAddr, StatusPktId, IsLow);
582
583 if (StatusTd == NULL) {
584 goto FREE_TD;
585 }
586
587 //
588 // Link setup Td -> data Tds -> status Td together
589 //
590 if (FirstDataTd != NULL) {
591 UhciAppendTd (Uhc, SetupTd, FirstDataTd);
592 UhciAppendTd (Uhc, PrevDataTd, StatusTd);
593 } else {
594 UhciAppendTd (Uhc, SetupTd, StatusTd);
595 }
596
597 return SetupTd;
598
599 FREE_TD:
600 if (SetupTd != NULL) {
601 UhciDestoryTds (Uhc, SetupTd);
602 }
603
604 if (FirstDataTd != NULL) {
605 UhciDestoryTds (Uhc, FirstDataTd);
606 }
607
608 return NULL;
609 }
610
611
612 /**
613 Create Tds list for Bulk/Interrupt Transfer.
614
615 @param Uhc USB_HC_DEV.
616 @param DevAddr Address of Device.
617 @param EndPoint Endpoint Number.
618 @param PktId Packet Identification of Data Tds.
619 @param Data A pointer to cpu memory address of user data buffer to transfer.
620 @param DataPhy A pointer to pci memory address of user data buffer to transfer.
621 @param DataLen Length of user data to transfer.
622 @param DataToggle Data Toggle Pointer.
623 @param MaxPacket Maximum packet size for Bulk/Interrupt transfer.
624 @param IsLow Is Low Speed Device.
625
626 @return The Tds list head for the bulk transfer.
627
628 **/
629 UHCI_TD_SW *
UhciCreateBulkOrIntTds(IN USB_HC_DEV * Uhc,IN UINT8 DevAddr,IN UINT8 EndPoint,IN UINT8 PktId,IN UINT8 * Data,IN UINT8 * DataPhy,IN UINTN DataLen,IN OUT UINT8 * DataToggle,IN UINT8 MaxPacket,IN BOOLEAN IsLow)630 UhciCreateBulkOrIntTds (
631 IN USB_HC_DEV *Uhc,
632 IN UINT8 DevAddr,
633 IN UINT8 EndPoint,
634 IN UINT8 PktId,
635 IN UINT8 *Data,
636 IN UINT8 *DataPhy,
637 IN UINTN DataLen,
638 IN OUT UINT8 *DataToggle,
639 IN UINT8 MaxPacket,
640 IN BOOLEAN IsLow
641 )
642 {
643 UHCI_TD_SW *DataTd;
644 UHCI_TD_SW *FirstDataTd;
645 UHCI_TD_SW *PrevDataTd;
646 UINTN ThisTdLen;
647
648 DataTd = NULL;
649 FirstDataTd = NULL;
650 PrevDataTd = NULL;
651
652 //
653 // Create data packets for the transfer
654 //
655 while (DataLen > 0) {
656 //
657 // PktSize is the data load size that each Td.
658 //
659 ThisTdLen = DataLen;
660
661 if (DataLen > MaxPacket) {
662 ThisTdLen = MaxPacket;
663 }
664
665 DataTd = UhciCreateDataTd (
666 Uhc,
667 DevAddr,
668 EndPoint,
669 Data,
670 DataPhy,
671 ThisTdLen,
672 PktId,
673 *DataToggle,
674 IsLow
675 );
676
677 if (DataTd == NULL) {
678 goto FREE_TD;
679 }
680
681 if (PktId == INPUT_PACKET_ID) {
682 DataTd->TdHw.ShortPacket = TRUE;
683 }
684
685 if (FirstDataTd == NULL) {
686 FirstDataTd = DataTd;
687 FirstDataTd->NextTd = NULL;
688 } else {
689 UhciAppendTd (Uhc, PrevDataTd, DataTd);
690 }
691
692 *DataToggle ^= 1;
693 PrevDataTd = DataTd;
694 Data += ThisTdLen;
695 DataPhy += ThisTdLen;
696 DataLen -= ThisTdLen;
697 }
698
699 return FirstDataTd;
700
701 FREE_TD:
702 if (FirstDataTd != NULL) {
703 UhciDestoryTds (Uhc, FirstDataTd);
704 }
705
706 return NULL;
707 }
708