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 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 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 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 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 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 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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