//**************************************************************************** //**************************************************************************** //** ** //** (C)Copyright 1985-2016, American Megatrends, Inc. ** //** ** //** All Rights Reserved. ** //** ** //** 5555 Oakbrook Pkwy, Norcross, GA 30093 ** //** ** //** Phone (770)-246-8600 ** //** ** //**************************************************************************** //**************************************************************************** //**************************************************************************** // $Header: /Alaska/SOURCE/Modules/USB/ALASKA/RT/ehci.c 152 10/16/16 10:12p Wilsonlee $ // // $Revision: 152 $ // // $Date: 10/16/16 10:12p $ //**************************************************************************** //**************************************************************************** // Revision History // ---------------- // $Log: /Alaska/SOURCE/Modules/USB/ALASKA/RT/ehci.c $ // // 152 10/16/16 10:12p Wilsonlee // [TAG] EIP288158 // [Category] Improvement // [Description] Check if gUsbData is integrity. // [Files] amiusb.cif, usbsb.c, AmiUsbLib.cif, AmiUsbLib.sdl, // AmiUsbSmmGlobalDataValidationLib.c, // AmiUsbSmmGlobalDataValidationLib.cif, // AmiUsbSmmGlobalDataValidationLib.mak, Crc32.c, amiusb.c, amiusb.h, // ehci.c, elib.c, ohci.c, syskbc.c, uhci.c, usb.c, usbCCID.c, usbdef.h, // usbhid.c, usbhub.c, usbkbd.c, usbmass.c, usbms.c, usbrt.mak, xhci.c, // amiusbhc.c, efiusbccid.c, efiusbmass.c, uhcd.c, usbmisc.c, // AmiUsbController.h, AmiUsbLibInclude.cif, // AmiUsbSmmGlobalDataValidationLib.h // // 151 7/22/16 3:50a Wilsonlee // [TAG] EIP277810 // [Category] Improvement // [Description] Validate the memory buffer is entirely outside of SMM. // [Files] usbsb.c, amiusb.c, ehci.c, ohci.c, uhci.c, usbCCID.c, // usbdef.h, usbmass.c, xhci.c, amiusbhc.c, efiusbccid.c, efiusbmass.c, // uhcd.c, uhcd.h, usbmisc.c // // 150 7/07/16 2:08a Wilsonlee // [TAG] EIP268836 // [Category] Bug Fix // [Severity] Normal // [Symptom] The system will loop in USB runtime module during IPMI are // resetting USB controller. // [RootCause] Qh->dLinkPointer is invalid after device is disconnected. // [Solution] Check if Qh is changed after we process it. // [Files] ehci.c // // 149 7/07/16 1:09a Wilsonlee // [TAG] EIP277810 // [Category] Improvement // [Description] Validate the memory buffer is entirely outside of SMM. // [Files] usbsb.c, amiusb.c, ehci.c, ohci.c, uhci.c, usbCCID.c, // usbdef.h, usbmass.c, xhci.c, amiusbhc.c, efiusbccid.c, efiusbmass.c, // uhcd.c, uhcd.h, usbmisc.c // // 148 3/14/16 10:21p Wilsonlee // [TAG] EIP257506 // [Category] Improvement // [Description] Add USB_KEYREPEAT_INTERVAL token to change the key // repeat interval. // [Files] usb.sdl, xhci.h, usbkbd.h, uhci.c, ohci.c, ehci.c // // 147 3/02/16 9:42p Wilsonlee // [TAG] EIP254309 // [Category] Bug Fix // [Severity] Normal // [Symptom] GK-FORCE K83 USB KB function abnormal. // [RootCause] This device has an interrupt out endpoint and doesn't // support "Set Report" request. // [Solution] Use the interrupt out endpoint instead of sending "Set // Report" request. // [Files] AmiUsbController.h, xhci.c, usbmass.c, usbkbd.h, usbkbd.c, // usbhub.c, usbhid.c, usbdef.h, usbCCID.c, usb.c, uhci.c, ohci.c, ehci.c, // amiusb.h, efiusbms,c, amiusbhc.c // // 146 9/06/15 10:05p Wilsonlee // // 145 7/24/15 4:41a Wilsonlee // [TAG] EIP226493 // [Category] Improvement // [Description] Block to process periodic list to prevent that we might // send the wrong command sequences to the same device. // [Files] usbmass.c, ehci.c, xhci.h, xhci.c, usbdef.h, uhcd.c // // 144 5/26/15 10:48p Wilsonlee // [TAG] EIP219177 // [Category] Improvement // [Description] Fixed static code analysis issues in Usb module. // [Files] UsbInt13.c, ehci.c, usbbus.c // // 143 4/10/15 3:11a Wilsonlee // [TAG] EIP207413 // [Category] Improvement // [Description] Install UsbApiTable and UsbMassApitTable in // AmiUsbSmmProtocol. // [Files] amiusbhc.c, AmiUsbController.h, usbdef.h, usbCCID.c, uhci.c, // ehci.c, amiusbrtCCID.h, amiusb.h, amiusb.c, uhcd.c // // 142 4/07/15 4:36a Wilsonlee // [TAG] EIP212211 // [Category] Bug Fix // [Severity] Minor // [Symptom] USB transfer speed is slower than before in EHCI. // [RootCause] If we parse through the all periodic list, the // performance is low in AMD platforms. // [Solution] Only parse the first entry through // MAX_SPLIT_PERIODIC_NUMBER. // [Files] ehci.c // // 141 3/22/15 11:12p Wilsonlee // [TAG] EIP206118 // [Category] Improvement // [Description] Remove dead loop in EHCIStartAsyncSchedule and // EHCIStopAsyncSchedule functions. // [Files] ehci.c // // 140 3/08/15 10:50p Wilsonlee // [TAG] EIP207774 // [Category] Improvement // [Description] Set USB_FLAG_DRIVER_STARTED flag when HC is running and // clear it if we don't start any HC. // [Files] uhci.c, usb.c, ehci.c, ohci.c, xhci.c, amiusb.h // // 139 2/24/15 9:53p Wilsonlee // [TAG] EIP205784 // [Category] Improvement // [Description] Don't read Periodic Frame List Base Address Register if // the controller doesn't support periodic schedule. // [Files] ehci.c // // 138 2/24/15 9:35p Wilsonlee // [TAG] EIP206118 // [Category] Improvement // [Description] Remove dead loop in EHCIStartAsyncSchedule and // EHCIStopAsyncSchedule functions. // [Files] ehci.c // // 137 1/22/15 10:19p Wilsonlee // [TAG] EIP201434 // [Category] Bug Fix // [Severity] Normal // [Symptom] Number of connected devices isn't correct if we plug out // keyboards or mice behind hub in xhci. // [RootCause] The PortConnectChange bit is cleared when we check port // status for interrupt endpoint transaction error. // [Solution] Don't clear change bits if we check port status for // interrupt endpoint transaction error. // [Files] xhci.c, usbhub.c, usbdef.h, usb.c, uhci.c, ohci.c, ehci.c, // amiusbhc.c // // 136 12/21/14 9:30p Wilsonlee // [TAG] EIP194553 // [Category] Improvement // [Description] Realtek 8111EP EHCI controller workaround, this // controller doesn't work if the buffer address isn't DWORD alignment // (current offset of qTD). Provide the workaround to locate DWORD // alignment buffer. // [Files] ehci.c // // 135 11/24/14 12:50a Wilsonlee // [TAG] EIP185972 // [Category] Improvement // [Description] To acquire more bandwidth, a dynamically transfer queue // allocation mechanism is required. // [Files] ehci.c, usbdef.h // // 134 5/01/14 3:56a Ryanchou // [TAG] EIP162589 // [Category] Improvement // [Description] Do not register external controller as key repeat // controller. // [Files] ehci.c, ohci.c, uhci.c // // 133 4/30/14 6:12a Ryanchou // [TAG] EIP151374 // [Category] Improvement // [Description] Calculates maximum data length to be reported in the // HID device. // [Files] ehci.c, ohci.c, uhci.c, usbCCID.c, usbdef.h, usbhid.c, // usbhub.c, xhci.c // // 132 4/30/14 5:25a Wilsonlee // [TAG] EIP164842 // [Category] Improvement // [Description] Check if the devices have put into to our queue before // we put them. // [Files] UsbInt13.c, amiusb.c, ehci.c, ohci.c, usb.c, usbdef.h, // usbmass.c, xhci.c, amiusbhc.c, efiusbmass.c, uhcd.c, usbbus.c, usbsb.c // // 131 2/26/14 1:56a Wilsonlee // [TAG] EIP149854 // [Category] Improvement // [Description] Add data length parameter to polling callback function. // [Files] usbkbd.c, uhci.c, usb.c, usbhub.c, usbCCID.c, usbms.c, // usbhid.c, usbpoint.c, usbkbd.h, ehci.c, ohci.c, xhci.c, usbdef.h // // 130 11/26/13 4:15a Ryanchou // [TAG] EIP142940 // [Category] Bug Fix // [Severity] Normal // [Symptom] USB keyboard cannot work if token USB_RUNTIME_DRIVER_IN_SMM // is disabled. // [RootCause] The variable Qh in EhciAddPeriodicQh() does not have // initialize value. // [Solution] Check if the value of variable Qh is valid first. // [Files] ehci.c // // 129 9/05/13 12:14a Wilsonlee // [TAG] EIP135237 // [Category] Bug Fix // [Severity] Normal // [Symptom] USB can't work when turn on AMIDEBUG_RX_SUPPORT. // [RootCause] EHCI_PORT_CHANGE_DETECT bit isn't set when turn on // AMIDEBUG_RX_SUPPORT. // [Solution] Ignore the EHCI_PORT_CHANGE_DETECT bit to check the root // hub ports at first time. // [Files] ehci.c // // 128 7/26/13 2:33a Ryanchou // [TAG] EIP122142 // [Category] Improvement // [Description] Improve periodic schedule mechanism // [Files] ehci.c, ehci.h, ohci.c, ohci.h, uhci.c, uhci.h, usbdef.h, // amiusbhc.c // // 127 7/22/13 10:31p Wilsonlee // [TAG] EIP125357 // [Category] Improvement // [Description] Check if the port releases to a select host controller. // [Files] uhci.c, usb.c, usbhub.c, ehci.c, ohci.c, xhci.c, usbdef.h // // 126 6/26/13 2:18a Roberthsu // [TAG] EIP122174 // [Category] Bug Fix // [Severity] Normal // [Symptom] Usb keyboard can not work.When special usb key insert // [RootCause] This keyboard detect connect status signal during this // key initial. So port detect not service. // [Solution] Clear port detect status before check port change. // [Files] ehci.c // // 125 6/02/13 11:41p Wilsonlee // [TAG] EIP123235 // [Category] Improvement // [Description] Stop the usb host controller at ExitBootService if it // is an extend card or it doesn't support HW SMI. // [Files] xhci.c, ehci.c, uhci.c, ohci.c, amiusb.c, usbdef.h, usbsb.c, // uhcd.c // // 124 5/12/13 11:52p Wilsonlee // Set the data toggle to QH at EHCI_ActivatePolling and // EHCI_InterruptTransfer. // // 123 5/02/13 2:35a Wilsonlee // [TAG] EIP121790 // [Category] Bug Fix // [Severity] Minor // [Symptom] The usb keyboard doesn't work fine behind usb 2.0 hub. // [RootCause] Teradici ehci controlle doesn't preserves DT bit in the // qTD. // [Solution] Don't set "Data toggle control" bit and check DT bit in // the queue head. // [Files] ehci.c // // 122 5/01/13 9:54p Wilsonlee // [TAG] EIP121643 // [Category] Bug Fix // [Severity] Minor // [Symptom] Cannot detect the usb floppy. // [RootCause] This device doesn't return data when we sned // get-config-descriptor command. // [Solution] We retry to send get-config-descriptor command if there is // no data. // [Files] usb.c, ehci.c // // 121 4/18/13 1:02p Ryanchou // Add Teradici USB controller support. // // 120 4/16/13 6:44a Ryanchou // [TAG] EIP118912 // [Category] Improvement // [Description] Add VIA VT6212 EHCI controller support. // [Files] ehci.c, uhci.c, usbdef.h, uhcd.c // // 119 3/19/13 3:59a Ryanchou // [TAG] EIP118177 // [Category] Improvement // [Description] Dynamically allocate HCStrucTable at runtime. // [Files] usb.sdl, usbport.c, usbsb.c, amiusb.c, ehci.c, ohci.c, // syskbc.c, sysnokbc.c, uhci.c, usb.c, usbCCID.c, usbdef.h, usbhid.c, // usbhub.c, usbmass.c, usbrt.mak, usb.sd, amiusbhc.c, efiusbccid.c, // efiusbhid.c, efiusbmass.c, efiusbms.c, uhcd.c, uhcd.h, uhcd.mak, // usbmisc.c, usbsrc.sdl // // 118 3/18/13 4:47a Ryanchou // [TAG] EIP98377 // [Category] Improvement // [Description] Optimize USB controller timing. // [Files] usb.sdl, usbport.c, ehci.c, elib.c, ohci.c, uhci.c, // usbdef.h, usbhub.c, xhci.c, uhcd.c // // 117 2/24/13 8:59p Wilsonlee // [TAG] EIP113541 // [Category] Bug Fix // [Severity] Critical // [Symptom] System hangs at checkpoint 0xA2 when Win8 resume from S4. // [RootCause] The "HCHalted" bit and "Port Change Detect" bit are set // when the system S4 resume to Win8 OS. // [Solution] We need to clear the interrupt status even if the // "HCHalted" bit is set. // [Files] ehci.c, ohci.c, uhci.c // // 116 1/24/13 3:20a Roberthsu // [TAG] EIP111010 // [Category] Bug Fix // [Severity] Normal // [Symptom] Touch panel button not work. // [RootCause] Device write wrong buffer. // [Solution] Clear bufferptr in pollingtdcallback. // [Files] ehci.c // // 115 1/11/13 4:16a Ryanchou // [TAG] EIP102491 // [Category] Improvement // [Description] Synchronized with Aptio V USB module // [Files] usbport.c, usbsb.c, ehci.c, ehci.h, ohci.c, ohci.h, uhci.h, // usb.c, usbdef.h, usbhid.c, usbhub.c, usbkbd.c, usbkbd.h, usbmass.c. // usbms.c, usbpoint.c, xhci.h, usb.sd, amiusbhc.c, componentname.c, // efiusbkc.c, efiusbmass.c, uhcd.c, uhcd.h, usbbus.c, usbbus.h, usbmisc.c // // 114 12/24/12 5:06a Ryanchou // [TAG] EIP103031 // [Category] Bug Fix // [Severity] Normal // [Symptom] System hans when loading QNX 6.5.0 // [RootCause] The EHCI port detect change SMI is generated after // ownership change to OS. // [Solution] Clear the SMI enable bits and status bits even the // controller is OS owned. // [Files] ehci.c, ohci.c // // 113 12/06/12 12:38a Wilsonlee // [TAG] EIP103186 // [Category] Improvement // [Description] Handle the error case "MEMIO was disabled" in USB // driver. // [Files] uhci.c, ohci.c, ehci.c, xhci.c // // 112 11/24/12 9:00p Wilsonlee // [TAG] EIP107513 // [Category] Improvement // [Description] Stop periodic schedule before removing QH. // [Files] ehci.c // // 111 11/13/12 7:11a Wilsonlee // [TAG] EIP82553 // [Category] New Feature // [Description] Support usb S5 wake up function for XHCI. // [Files] usb.c, ehci.c, ohci.c, xhci.c, xhci.h // // 110 11/10/12 6:40a Ryanchou // [TAG] EIP99431 // [Category] Bug Fix // [Severity] Minor // [Symptom] Cannot use the UsbIo's UsbAsyncInterruptTransfer for // keyboard input // [RootCause] Stopping EFI USB keyboard driver does not stop the // endpoint polling, then application calls UsbAsyncInterruptTransfer, // error will be returned. // [Solution] Stops endpoint polling and release resource when // disconnecting the device driver. And improve the // UsbSyncInterruptTransfer. // [Files] amiusb.c, amiusb.h, ehci.c, ohci.c, uhci.c, usb.c, // usbCCID.c, usbdef.h, usbhub.c, usbkbd.c, usbmass.c, usbms.c, // usbpoint.c, amiusbhc.c, efiusbhid.c, usbbus.c, usbbus.h // // 109 10/26/12 8:58a Roberthsu // [TAG] EIP102781 // [Category] Bug Fix // [Severity] Normal // [Symptom] After update usb module, DebugRx hang at UHCD.Start(). // [RootCause] dHCSParams vaule not vaild. // [Solution] Function EhciIsolateDebugPort move after variable // dHCSParams fill. // [Files] ehci.c // // 108 10/25/12 4:28a Wilsonlee // [TAG] EIP103051 // [Category] Bug Fix // [Severity] Minor // [Symptom] With USB3.0 flash on USB3.0 port and set to USB mode2, // system will hang at post code 0x92 during the first cold boot after // connecting AC power. // [RootCause] This device connects to ehci first and it was // disconnected after we reset the port. // [Solution] We need to check the connect status after resetting the // port. // [Files] ehci.c // // 107 10/25/12 4:15a Wilsonlee // [TAG] EIP82354 // [Category] New Feature // [Description] Support usb S5 wake up function for OHCI. // [Files] usb.c, ehci.c, ohci.c // // 106 9/28/12 2:37a Wilsonlee // [TAG] EIP93154 // [Category] Improvement // [Description] Change the unit of the FixedDelay from 15 us to 1 us. // [Files] amiusb.h, xhci.c, ehci.c, ohci.c, uhci.c, usb.c, usbCCID.c, // usbmass.c, usbhub.c, elib.c // // 105 8/29/12 9:32a Ryanchou // [TAG] EIP88307 // [Category] Bug Fix // [Severity] Minor // [Symptom] Key repeat cannot be stopped if the keyboard is connected // to xHCI. // [RootCause] Periodic timer SMI stop generating when USB SMI and // periodic timer SMI is generated frequently. // [Solution] Reduces the key repeat rate to avoid this issue. // [Files] ehci.c, usb.c, usb.sdl // // 104 8/29/12 8:16a Ryanchou // [TAG] EIP77262 // [Category] New Feature // [Description] Remove SMM dependency of USB. // [Files] usb.sdl, usbport.c, amiusb.c, amiusb.dxs, amiusb.h, ehci.c, // elib.c, ohci.c, uhci.c, usb.c, usbdef.h, usbrt.mak, xhci.c, amiusbhc.c, // efiusbccid.c, efiusbhid.c, efiusbkb.c, efiusbmass.c, uhcd.c, uhcd.dxs, // uhcd.h, usbmisc.c, AmiUsbController.h // // 103 5/04/12 6:38a Ryanchou // [TAG] EIP82875 // [Category] Improvement // [Description] Support start/stop individual USB host to avoid // reconnect issues. // [Files] usbport.c, usbsb.c, amiusb.c, amiusb.h, ehci.c, ohci.c, // uhci.c, uhci.h, usb.c, usbdef.h, xhci.c, amiusbhc.c, uhcd.c, uhcd.h, // usbbus.c, usbmisc.c // // 102 5/03/12 5:59a Roberthsu // [TAG] EIP84455 // [Category] Improvement // [Description] Implement usb hid device gencric. // [Files] amiusb.c,amiusbhc.c,efiusbhid.c,efiusbkb.c,ehci.c,ohci.c,uhc // d.c,uhci.c,usbdef.h,usbhid.c,usbhub.c,usbkbd.c,usbkbd.h,usbms.c,usbsb.c // ,usbsrc.sdl // // 101 5/03/12 5:17a Ryanchou // [TAG] EIP88085 // [Category] Improvement // [Description] Added 1 ms delay after port reset completed. // [Files] ehci.c // // 100 5/03/12 5:08a Ryanchou // [TAG] EIP83361 // [Category] New Feature // [Description] Added "USB 2.0 Controller Mode" setup item. // [Files] ehci.c, usb.sd, usb.sdl, usb.uni, usbdef.h, UsbPolicy.h, // usbport.c // // 99 4/10/12 10:12p Wilsonlee // [TAG] EIP84790 // [Category] Bug Fix // [Severity] Minor // [Symptom] USB IO PROTOCOL under XHCI issue // [RootCause] The DevMiscInfo is NULL. // [Solution] Fill the DevMiscInfo for the xhci controller. // [Files] ehci.c, xhci.c, amiusbhc.c // // 98 4/05/12 7:42a Wilsonlee // [TAG] EIP86001 // [Category] Improvement // [Description] Free the chunk of memory allocated using the // USBMem_Alloc call when we didn't use it. // [Files] usbhid.c, ehci.c // // 97 3/20/12 10:34p Wilsonlee // [TAG] EIP83295 // [Category] Bug Fix // [Severity] Normal // [Symptom] System will hang at B2 when do S3/S4 long run test or DOS // reboot test. // [RootCause] It causes the stack problem when we call // IsUSBKeyboardBufferEmpty function. // [Solution] Change to use pointer as parameter to // IsUSBKeyboardBufferEmpty function. // [Files] efiusbkb.c, efisubkb.h, usbmass.c, ehci.c // // 96 2/16/12 9:04p Wilsonlee // [TAG] EIP82307 // [Category] Improvement // [Description] Port routing route to EHCI when EHCI initialization. // [Files] ehci.c // // 95 1/16/12 1:05a Wilsonlee // [TAG] EIP81030 // [Category] Bug Fix // [Severity] Critical // [Symptom] System hangs randomly at POST. // [RootCause] This is EIP71067 side effect. It uses error memory // address. // [Solution] The change used the correct memory address. // [Files] ehci.c // // 94 12/23/11 8:39p Wilsonlee // [TAG] EIP71067 // [Category] Bug Fix // [Severity] Minor // [Symptom] SMI Timeout with USB FD and USB Flash Memory // [RootCause] It uses the same area to bulk transfer on EHCI/ // [Solution] It uses local QH and QTD for each control and bulk // transfer on EHCI // [Files] ehci.c // // 93 11/09/11 3:30a Ryanchou // [TAG] EIP73692 // [Category] Improvement // [Description] Implement the ownership change mechanism for PCH. // [Files] ehci.c, usbdef.h, usbsrc.sdl // // 92 11/08/11 1:57a Ryanchou // [TAG] EIP63188 // [Category] Improvement // [Description] External USB controller support. // [Files] amidef.h, amiusb.c, ehci.c, ohci.c, uhcd.c, uhcd.h, uhci.c, // usbdef.h, usbmisc.c, usbsb.c, xhci.c // // 91 9/09/11 5:06a Roberthsu // [TAG] EIP68865 // [Category] Bug Fix // [Severity] Normal // [Symptom] VIA plug in USB KB/MS cause system hang // [RootCause] After release port.Ehci_line_status still exist. // [Solution] Release port owner when connect status change. // [Files] ehci.c // // 90 8/08/11 6:58a Ryanchou // [TAG] EIP54018 // [Category] New Feature // [Description] Added USB S5 wake up support. // [Files] amiusb.c, ehci.c, ohci.c, uhci.c, usb.c, usb.sdl, usbdef.h, // usbsb.c xhci.c // // 89 8/08/11 5:14a Ryanchou // [TAG] EIP60561 // [Category] New Feature // [Description] Add USB timing policy protocol for timing override. // [Files] ehci.c, guids.c, ohci.c, uhcd.c, uhci.c usb.c, usbdef.h, // usbhub.c, usbmass.c, UsbPolicy.h, usbport.c usbsrc.sdl // // 88 7/19/11 5:16a Ryanchou // [TAG] EIP64498 // [Category] New Feature // [Description] Implement EHCI key repeat function. // [Files] ehci.c, ehci.h, usb.c, usbdef.h // // 87 7/15/11 6:11a Ryanchou // [TAG] EIP38434 // [Category] New Feature // [Description] Added USB HID report protocol support. // [Files] amiusb.c, AmiUsbController.h, amiusbhc.c, efiusbkb.c, // efiusbkb.h, ehci.c, ohci.c, uhcd.c uhcd.cif, uhci.c, usb.c, usbdef.h, // usbkbd.c, usbkbd.h, usbms.c, usbrt.cif, usbsb.c, usbsetup.c, // usbsrc.sdl, xhci.c // // 86 7/12/11 8:09a Ryanchou // [TAG] EIP56918 // [Category] New Feature // [Description] Added CCID device support. // [Files] amiusb.c, amiusb.h, amiusbrtCCID.h, ehci.c, ohci.c, uhci.c, // usb.c, UsbCCID.c, usbdef.h, usbrt.cif, usbsetup.c, efiusbccid.c, // framework.cif, uhcd.c, uhcd.cif, uhcd.h, usbsrc.sdl, AmiusbCCID.h, // AmiUsbController.h, AmiUSBProtocols.cif // // 85 7/01/11 3:18a Ryanchou // [TAG] EIP61385 // [Category] Bug Fix // [Severity] Important // [Symptom] USB devices can't detected. // [RootCause] This is the side effect of EIP59663, the port status // doesn't reflect connect status and connect status change. // [Solution] Add 100 us delay. // [Files] ehci.c, uhci.c // // 84 6/21/11 10:26a Ryanchou // [TAG] EIP60632 // [Category] Bug Fix // [Severity] Normal // [Symptom] Generic Usb Redirection caused system hung at checkpoint A2 // [RootCause] The timeout value is zero, and the bulk transfer never // completed. // [Solution] UEFI spec defines "If Timeout is 0, then the // caller must wait for the function to be completed until // EFI_SUCCESS or EFI_DEVICE_ERROR is returned". // [Files] ehci.c // // 83 6/21/11 10:00a Ryanchou // [TAG] EIP62708 // [Category] Bug Fix // [Severity] Normal // [Symptom] Unexpected USB SMI after USB Owner Ship Change // [RootCause] Unexpected USB SMI causes an expection after USB onwer // ship change // executed. // [Solution] Check if HC is still under BIOS control before service USB // interrupts. // [Files] ehci.c // // 82 6/21/11 9:55a Ryanchou // [TAG] EIP59663 // [Category] Bug Fix // [Severity] Normal // [Symptom] Plug USB WLAN device may causes OHCI malfunction. // [RootCause] The devices have to connect to OHCI first, or it can't be // configured. // [Solution] Port routing route to EHCI after OHCI initialization. // [Files] ehci.c, ohci.c // // 81 5/30/11 8:41a Ryanchou // [TAG] EIP61030 // [Category] Improvement // [Description] PORTSC register should be read before write the // register, and set the USB_PORT_STAT_DEV_CONNECT_CHANGED flag if // bIgnoreConnectStsChng is set and connect status change bit is not set. // [Files] ehci.c // // 80 5/03/11 10:10a Ryanchou // [TAG] EIP54283 // [Category] Improvement // [Description] Follow XHCI spec ver 1.0 section 4.6.8 to recovery // endpoint halt. // [Files] ehci.c, ohci.c, uhci.c, usbdef.h, usbmass.c, xhci.c // // 79 5/03/11 8:54a Ryanchou // [TAG] EIP58108 // [Category] Improvement // [Description] Disable port only if this port is enabled and remove // the delay after port disable. // [Files] ehci.c // // 78 4/06/11 3:52a Ryanchou // [TAG] EIP55960 // [Category] Improvement // [Description] The Host Controller must halt within 16 micro-frames // after software clears the Run bit. // [Files] ehci.c, elib.c // // 77 4/06/11 1:32a Ryanchou // [TAG] EIP54782 // [Category] Improvement // [Description] Change polling data size of HID devices to max packet // size of interrupt endpoint. // [Files] ehci.c, ohci.c, uhci.c, usb.c, usbdef.h, xhci.c // // 76 3/29/11 10:47p Ryanchou // [TAG] EIP55401 // [Category] Improvement // [Description] Improve the USB 3.0 device compatibility. // [Files] ehci.c, ehci.h, ohci.c, uhci.c, usb.c, usbdef.h, usbhub.c, // xhci.c // // 75 3/29/11 10:10a Ryanchou // [TAG] EIP53518 // [Category] Improvement // [Description] Added chipset xHCI chip support. // [Files] amiusb.c, amiusb.h, ehci.c, ohci.c, uhcd.c, uhci.c, usb.c, // usb.sdl, usbdef.h, usbport, usbsb.c, xhci.c // // 74 2/17/11 8:03a Ryanchou // [TAG] EIP48592 // [Category] Improvement // [Description] Add timeout check in EHCIStartPeriodicSchedule and // EHCIStopPeriodicSchedule to avoid the endless loop. // [Files] ehci.c // // 73 11/11/10 11:32p Ryanchou // [TAG] EIP45578 // [Category] Bug Fix // [Severity] Minor // [Symptom] USB 3.0 device can't be detected. // [RootCause] Address Device Command fails. // [Solution] Reset the device and attempt the Address Device Command // again. // [Files] ehci.c, ohci.c, uhci.c, usb.c, usbdef.h, usbhub.c, xhci.c // // 72 10/20/10 1:02a Ryanchou // EIP45689: Uncomment code in EHCI_GetRootHubStatus that wait 20ms for // host controller could report accurate port status properly. // // 71 9/24/10 5:38p Olegi // EIP38221: Added the code that properly initializes // DEV_INFO.bIntEndpoint field; interrupt endpoint polling is using this // endpoint number. // // 70 8/09/10 1:29a Ryanchou // EIP41187: Set CERR field as 3 in a qTD, and service all interrupts // after transfer complete. // // 69 7/13/10 7:16a Ryanchou // EIP38356: Implement shutdown USB legacy support in ACPI enable call. // // 68 6/10/10 3:07a Ryanchou // EIP38648: Enable USB Error SMI. // // 67 3/11/10 9:42a Olegi // // 66 3/02/10 4:51p Olegi // Undone changes in EHCI_ControlTransfer for EIP32956. // // 65 2/27/10 11:59a Olegi // // 64 2/26/10 5:14p Olegi // // 63 2/08/10 9:59a Olegi // EIP33381: Implement multiple bulk endpoint in UsbIoProtocol. // // 62 1/26/10 10:21a Olegi // EIP32624: fix Change in InterruptTransfer. // // 61 1/04/10 9:20a Olegi // EIP32956: Polling rate for the keyboards has been changed from 8 ms to // 32 ms. // // 60 12/23/09 11:59a Olegi // // 59 12/09/09 1:07p Davidd // Corrected the USBkeyboard can't hotplug in DOS issue. (EIP 26477) // // 58 12/09/09 12:33p Olegi // Code cleanup. // // 57 11/30/09 6:13p Olegi // // 56 11/25/09 1:48p Olegi // // 55 11/18/09 7:10p Olegi // Restored EHCIResetHC call in Start function that was removed for // EIP23479. This EIP is resolved differently. // // 54 10/30/09 5:47p Olegi // // 52 10/15/09 3:12p Olegi // EIP24437: Change EHCI_ProcessInterrupt to handle the HC error bit in // the periodic TDs. // // 51 10/07/09 9:48a Olegi // USB Hub error handling improvement. EIP#25601. // // 50 9/17/09 11:13a Olegi // Change in ProcessInterrupt: // There may be a condition in which OS changes the base address before // owning the semaphore and SMI may occur on USB complete or port change // detect. This may cause SMI storm. Disabling SMI will not affect // anything since we dont have any control over it. // // 49 9/15/09 10:28a Olegi // EIP26685: uncommented code in EHCI_Stop that disconnects all devices. // // 48 9/15/09 10:21a Olegi // Added USB_INCMPT_HID_DATA_OVERFLOW incompatibility type. // // 47 8/28/09 11:49a Olegi // EIP#25760: change the sequence of periodic schedule and async schedule // execution. // // 46 7/02/09 10:06a Olegi // Fix for EIP#23479: while processing EHCI change ownership request, we // will not reset the HC. This change will preserve the state of // EHCI_CONFIGURE register (MMIO reg 60h, bit0) so the controller // switching will not occur. // // 45 6/24/09 2:35p Olegi // Correction in EHCIInitializePeriodicSchedule(); fix for the EIP#23498. // // 44 6/02/09 2:47p Olegi // // 43 5/22/09 1:48p Olegi // Bugfix in EHCI_GetRootHubStatus. // // 42 5/15/09 6:16p Olegi // Skip enabling PortPower for the ports that already have it enabled. // Saves ~100ms per controller. // // 41 3/19/09 4:56p Olegi // // 40 3/09/09 8:49a Olegi // Modified EHCIProgramLegacyRegisters, EIP#20084. // // 39 2/20/09 2:30p Olegi // Modifications in ProcessOwnerShipChangeSMI function, EIP#19525 // // 38 2/18/09 10:10a Olegi // Fast unplug/insert fix, EIP#19206. // // 37 2/17/09 4:01p Olegi // // 36 1/16/09 4:11p Olegi // Added dependency on MAX_BULK_DATA in EHCI_BulkTransfer. // // 35 11/06/08 1:59p Olegi // Change in EHCI_EnumeratePorts that will avoid unnecessary delays during // enabling the port power. // // 34 10/06/08 3:33p Olegi // EHCI change ownership testing in DOS fix (EIP#14855). // // 33 9/02/08 10:28a Olegi // EIP14855 bugfix: change ownership request is not processed properly in // case of multiple controllers of the same type. // // 32 8/08/08 2:37p Olegi // Fix in EHCI_GetRootHubStatus that did not report connection properly in // some cases. // // 31 5/16/08 12:01p Olegi // Compliance with AMI coding standard. // // 30 7/09/07 2:11p Olegi // Changed the maximum data size of the BulkTransfer from 1kB to 64kB. // // 29 4/17/07 8:24a Olegi // Device detection algorythm update, in sync with Core8. // // 28 3/20/07 12:21p Olegi // // 27 1/25/07 10:19a Olegi // // 26 12/28/06 5:27p Olegi // // 25 12/26/06 10:23a Olegi // // 24 12/22/06 4:05p Olegi // Timeout implementation. // // 23 12/20/06 2:30p Olegi // // 22 11/21/06 5:35p Olegi // // 21 11/16/06 6:10p Olegi // DebugPort support initial changes. // // 19 10/19/06 5:16p Andriyn // // 18 10/12/06 9:37p Andriyn // Fix: unexpected plug-off hangs with endless TIMEOUTs // // 17 10/12/06 9:07p Andriyn // // 16 7/21/06 6:34p Olegi // // 15 7/19/06 3:53p Olegi // Bugfix in EHCIRemoveQH routine. // // 14 6/09/06 10:29a Olegi // USB_FLAG_ENABLE_BEEP_MESSAGE flag is reset while handling change of the // controller ownership. // // 13 5/22/06 9:08a Olegi // ASYNC_BELL_SUPPORT modifications // // 12 5/16/06 11:23a Olegi // // 11 5/16/06 11:19a Olegi // Removed DisconnectDevice call from EHCI_Stop // // 10 4/14/06 6:39p Olegi // Conversion to be able to use x64 compiler. // // 9 3/20/06 3:37p Olegi // Version 8.5 - x64 compatible. // // 8 3/16/06 2:34p Olegi // // 7 3/06/06 6:25p Olegi // // 6 1/11/06 11:52a Olegi // // 5 11/29/05 12:33p Andriyn // // 4 6/03/05 6:09p Olegi // HW SMI registration change. // // 3 5/19/05 8:07p Olegi // Aptio changes in driver 8.1 implementation. // // 2 5/17/05 7:51p Andriyn // USB BUS pre-release // // 1 3/28/05 6:20p Olegi // // 1 3/15/05 9:23a Olegi // Initial VSS check-in. // //**************************************************************************** // //----------------------------------------------------------------------------- // // Name: Ehci.h // // Description: AMI USB EHCI support // //----------------------------------------------------------------------------- // #include "amidef.h" #include "usbdef.h" #include "amiusb.h" #include "usbkbd.h" #if USB_RUNTIME_DRIVER_IN_SMM #include #endif UINT8 EHCI_Start (HC_STRUC*); UINT8 EHCI_Stop (HC_STRUC*); UINT8 EHCI_EnumeratePorts (HC_STRUC*); UINT8 EHCI_DisableInterrupts (HC_STRUC*); UINT8 EHCI_EnableInterrupts (HC_STRUC*); UINT8 EHCI_ProcessInterrupt(HC_STRUC*); UINT8 EHCI_GetRootHubStatus (HC_STRUC*,UINT8, BOOLEAN); UINT8 EHCI_DisableRootHub (HC_STRUC*,UINT8); UINT8 EHCI_EnableRootHub (HC_STRUC*,UINT8); UINT16 EHCI_ControlTransfer (HC_STRUC*,DEV_INFO*,UINT16,UINT16,UINT16,UINT8*,UINT16); UINT32 EHCI_BulkTransfer (HC_STRUC*,DEV_INFO*,UINT8,UINT8*,UINT32); UINT16 EHCI_InterruptTransfer (HC_STRUC*, DEV_INFO*, UINT8, UINT16, UINT8*, UINT16); UINT8 EHCI_DeactivatePolling (HC_STRUC*,DEV_INFO*); UINT8 EHCI_ActivatePolling (HC_STRUC*,DEV_INFO*); UINT8 EHCI_DisableKeyRepeat (HC_STRUC*); UINT8 EHCI_EnableKeyRepeat (HC_STRUC*); UINT8 EHCI_ResetRootHub (HC_STRUC*,UINT8); UINT8 EHCI_GlobalSuspend (HC_STRUC*); //(EIP54018+) UINT8 EHCIResetHC(HC_STRUC*); UINT8 EHCIInitializePeriodicSchedule(HC_STRUC*, UINT32); UINT8 EHCIProgramLegacyRegisters(HC_STRUC*, UINT8); UINT8 EHCIStartAsyncSchedule(HC_STRUC*); UINT8 EHCIStopAsyncSchedule(HC_STRUC*); UINT8 EHCIStartPeriodicSchedule(HC_STRUC*); UINT8 EHCIStopPeriodicSchedule(HC_STRUC*); UINT8 EHCIProcessQH(HC_STRUC*, EHCI_QH*); VOID EHCIProcessPeriodicList(HC_STRUC*); VOID EHCIInitializeQueueHead (EHCI_QH*); VOID EHCISetQTDBufferPointers(EHCI_QTD*, UINT8*, UINT32); UINT16 EHCIWaitForTransferComplete(HC_STRUC*, EHCI_QH*,DEV_INFO* ); UINT8 EhciAddPeriodicQh(HC_STRUC*,EHCI_QH*); UINT8 EhciRemovePeriodicQh(HC_STRUC*,EHCI_QH*); VOID ProcessOwnerShipChangeSMI(HC_STRUC*); VOID ProcessSmiChangeToEHCD(HC_STRUC*); VOID ProcessSmiChangeToBIOS(HC_STRUC*); UINT8 EHCIGetLegacySupportOffset(HC_STRUC*, UINT16); VOID EHCIRemoveQHFromAsyncList(HC_STRUC*, EHCI_QH*); UINT8 EhciPollingTDCallback(HC_STRUC*, DEV_INFO*, UINT8*, UINT8*, UINT16); UINT8 EhciRepeatTDCallback(HC_STRUC*, DEV_INFO*, UINT8*, UINT8*, UINT16); UINT32 ReadPCIConfig(UINT16, UINT8); VOID WordWritePCIConfig(UINT16, UINT8, UINT16); VOID DwordWritePCIConfig(UINT16, UINT8, UINT32); UINT32 DwordReadMem(UINT32, UINT16); VOID DwordWriteMem(UINT32, UINT16, UINT32); VOID DwordResetMem(UINT32, UINT16, UINT32); VOID DwordSetMem(UINT32, UINT16, UINT32); UINT32 EhciReadPciReg(HC_STRUC*, UINT32); VOID EhciWritePciReg(HC_STRUC*, UINT32, UINT32); UINT32 EhciReadHcMem(HC_STRUC*, UINT32); VOID EhciWriteHcMem(HC_STRUC*, UINT32, UINT32); UINT32 EhciReadOpReg(HC_STRUC*, UINT32); VOID EhciWriteOpReg(HC_STRUC*, UINT32, UINT32); VOID EhciClearOpReg(HC_STRUC*, UINT32, UINT32); VOID EhciSetOpReg(HC_STRUC*, UINT32, UINT32); UINT32 EhciReadDebugReg(HC_STRUC*, UINT8, UINT32); VOID* EhciMemAlloc(HC_STRUC*, UINT16); VOID EhciMemFree(HC_STRUC*, VOID*, UINT16); UINT8 EhciDmaMap(HC_STRUC*, UINT8, UINT8*, UINT32, UINT8**, VOID**); UINT8 EhciDmaUnmap(HC_STRUC*, VOID*); BOOLEAN EhciIsHalted(HC_STRUC*); UINT16 EhciTranslateInterval(UINT8, UINT8); UINT8 USBCheckPortChange (HC_STRUC*, UINT8, UINT8); UINT8 USBLogError(UINT16); UINT8 UsbGetDataToggle(DEV_INFO*,UINT8); VOID UsbUpdateDataToggle(DEV_INFO*, UINT8, UINT8); VOID USB_InitFrameList (HC_STRUC*, UINT32); VOID FixedDelay(UINTN); VOID* USB_MemAlloc (UINT16); UINT8 USB_MemFree (VOID _FAR_ *, UINT16); UINT8 USB_DisconnectDevice (HC_STRUC*, UINT8, UINT8); DEV_INFO* USB_GetDeviceInfoStruc(UINT8, DEV_INFO*, UINT8, HC_STRUC*); UINT8 USB_StopDevice (HC_STRUC*, UINT8, UINT8); UINT8 USB_InstallCallBackFunction (CALLBACK_FUNC); VOID USBKeyRepeat(HC_STRUC*, UINT8); #if USB_DEV_KBD VOID USBKBDPeriodicInterruptHandler(HC_STRUC*); #endif extern USB_GLOBAL_DATA *gUsbData; extern BOOLEAN gCheckUsbApiParameter; // //--------------------------------------------------------------------------- // // Procedure: EHCI_FillHCDEntries (HCD_HEADER *fpHCDHeader) // // Description: This function fills the host controller driver // routine pointers // // Parameters: fpHCDHeader Ptr to the host controller header structure // // Output: Status: USB_SUCCESS = Success // USB_ERROR = Failure // //--------------------------------------------------------------------------- // UINT8 EHCI_FillHCDEntries (HCD_HEADER *fpHCDHeader) { fpHCDHeader->pfnHCDStart = EHCI_Start; fpHCDHeader->pfnHCDStop = EHCI_Stop; fpHCDHeader->pfnHCDEnumeratePorts = EHCI_EnumeratePorts; fpHCDHeader->pfnHCDDisableInterrupts = EHCI_DisableInterrupts; fpHCDHeader->pfnHCDEnableInterrupts = EHCI_EnableInterrupts; fpHCDHeader->pfnHCDProcessInterrupt = EHCI_ProcessInterrupt; fpHCDHeader->pfnHCDGetRootHubStatus = EHCI_GetRootHubStatus; fpHCDHeader->pfnHCDDisableRootHub = EHCI_DisableRootHub; fpHCDHeader->pfnHCDEnableRootHub = EHCI_EnableRootHub; fpHCDHeader->pfnHCDControlTransfer = EHCI_ControlTransfer; fpHCDHeader->pfnHCDBulkTransfer = EHCI_BulkTransfer; fpHCDHeader->pfnHCDInterruptTransfer = EHCI_InterruptTransfer; fpHCDHeader->pfnHCDDeactivatePolling = EHCI_DeactivatePolling; fpHCDHeader->pfnHCDActivatePolling = EHCI_ActivatePolling; fpHCDHeader->pfnHCDDisableKeyRepeat = EHCI_DisableKeyRepeat; fpHCDHeader->pfnHCDEnableKeyRepeat = EHCI_EnableKeyRepeat; fpHCDHeader->pfnHCDEnableEndpoints = USB_EnableEndpointsDummy; fpHCDHeader->pfnHCDInitDeviceData = USB_InitDeviceDataDummy; fpHCDHeader->pfnHCDDeinitDeviceData = USB_DeinitDeviceDataDummy; fpHCDHeader->pfnHCDResetRootHub = EHCI_ResetRootHub; fpHCDHeader->pfnHCDClearEndpointState = 0; //(EIP54283+) fpHCDHeader->pfnHCDGlobalSuspend = EHCI_GlobalSuspend; //(EIP54018+) USB_InstallCallBackFunction(EhciRepeatTDCallback); USB_InstallCallBackFunction(EhciPollingTDCallback); return USB_SUCCESS; } // //--------------------------------------------------------------------------- // // Procedure: EhciIsolateDebugPort // // Description: This routine locates EHCI debug port and determines whether // or not the debug port is initialized and being used by other // agents. If so, the global flag will be set to instruct the // EHCI runtime routines about debug port presence and prevent // any unwanted reset/reconfiguration of this port. // // Parameters: fpHCStruc Ptr to the host controller structure // // Output: fpHCStruc->DebugPort is updated if Debug Port is active on // this controller; otherwise it will remain 0. // //--------------------------------------------------------------------------- // VOID EhciIsolateDebugPort(HC_STRUC *fpHCStruc) { UINT32 HcSParams = fpHCStruc->dHCSParams; // Host Controller Structural Parameters UINT8 DebugPortNo; UINT32 NextCap; UINT8 DebugPortBarIndex; UINT16 DebugPortOffset; // // Locate debug port by looking at the PCI capabilities // DebugPortNo = (UINT8)((HcSParams & (EHCI_DEBUG_N)) >> 20); //ASSERT(DebugPortNo); // No debug port implemented fpHCStruc->DebugPort = 0; if (DebugPortNo == 0) return; ASSERT(DebugPortNo <= (UINT8)(HcSParams & (EHCI_N_PORTS))); // Invalid debug port number if (DebugPortNo > (UINT8)(HcSParams & (EHCI_N_PORTS))) return; // // Check whether device implements Capability list that starts at register 0x34 // if (!(EhciReadPciReg(fpHCStruc, 4) & BIT20)) { //ASSERT(FALSE); // Capabilities list is not implemented return; } // // Find the beginning of Debug Port registers block // for (NextCap = EhciReadPciReg(fpHCStruc, 0x34); (UINT8)NextCap > 0; ) { NextCap = EhciReadPciReg(fpHCStruc, (UINT8)NextCap); if ((UINT8)NextCap == 0xA) break; // Debug port capability found NextCap >>= 8; } if ((UINT8)NextCap == 0) { //ASSERT(FALSE); // Debug capabilities not found return; } DebugPortBarIndex = (UINT8)((NextCap >> 29) - 1); DebugPortOffset = (UINT16)((NextCap >> 16) & 0x1FFF); ASSERT(DebugPortBarIndex >= 0 && DebugPortBarIndex <= 5); // Wrong BAR if (!(DebugPortBarIndex >= 0 && DebugPortBarIndex <= 5)) return; // // See whether Debug Port is acquired by other software // if (EhciReadDebugReg(fpHCStruc, DebugPortBarIndex, DebugPortOffset) & BIT28) { fpHCStruc->DebugPort = DebugPortNo; USB_DEBUG(DEBUG_LEVEL_3, "EHCI HC Debug Port #%d enabled.\n", DebugPortNo); } } // //--------------------------------------------------------------------------- // // Procedure: EHCI_Start // // Description: This API function is called to start a EHCI host controller. // The input to the routine is the pointer to the HC structure // that defines this host controller // // Parameters: fpHCStruc Ptr to the host controller structure // // Output: Status: USB_SUCCESS = Success // USB_ERROR = Failure // //--------------------------------------------------------------------------- // UINT8 EHCI_Start (HC_STRUC* fpHCStruc) { UINT32 dTemp; //#if EHCI_ASYNC_BELL_SUPPORT // EHCI_QH *fpQHAsyncXfer; //#endif EHCI_QH *fpQHRepeat = NULL; EHCI_QTD *fpqTDRepeat = NULL; UINT32 i; //(EIP55960+) BOOLEAN SetPortPower = FALSE; UINT16 PortReg; EHCI_DESC_PTRS *DescPtr = NULL; EFI_STATUS EfiStatus = EFI_SUCCESS; /* USB_DEBUG(DEBUG_LEVEL_3, "Enabling MEM/BM for EHCI HC %02X\n", fpHCStruc->wBusDevFuncNum); // // Enable IO access and Bus Mastering // WordWritePCIConfig((UINT16)fpHCStruc->wBusDevFuncNum, 4, BIT1 + BIT2); */ // // Get memory base address of the HC and store it in the HCStruc // fpHCStruc->BaseAddress = EhciReadPciReg(fpHCStruc, USB_MEM_BASE_ADDRESS) & 0xFFFFFFF0; USB_DEBUG(DEBUG_LEVEL_3, "EHCI HC Mem Addr: %X\n", fpHCStruc->BaseAddress); #if USB_RUNTIME_DRIVER_IN_SMM EfiStatus = AmiValidateMmioBuffer((VOID*)fpHCStruc->BaseAddress, fpHCStruc->BaseAddressSize); if (EFI_ERROR(EfiStatus)) { USB_DEBUG(3, "Usb Mmio address is invalid, it is in SMRAM\n"); return USB_ERROR; } #endif // // Get the number of ports supported by the host controller (Offset 4) // and store it in HCStruc // fpHCStruc->dHCSParams = EhciReadHcMem(fpHCStruc, EHCI_HCSPARAMS); fpHCStruc->bNumPorts = (UINT8)(fpHCStruc->dHCSParams & EHCI_N_PORTS); USB_DEBUG(DEBUG_LEVEL_3, "EHCI HC Number of ports: %d\n", fpHCStruc->bNumPorts); EhciIsolateDebugPort(fpHCStruc); //(EIP102781) // // Read the Capability Registers Length to find the Offset address for the // beginning of the operational registers // fpHCStruc->bOpRegOffset = (UINT8)EhciReadHcMem(fpHCStruc, EHCI_VERCAPLENGTH); USB_DEBUG(DEBUG_LEVEL_3, "EHCI HC Operational Registers Offset: %d\n", fpHCStruc->bOpRegOffset); // // Read and store the HCCPARAMS value // fpHCStruc->dHCCParams = EhciReadHcMem(fpHCStruc, EHCI_HCCPARAMS); //USB_DEBUG(DEBUG_LEVEL_3, "EHCI HC HCPARAMS: %x\n", gUsbData->dHCCParams); // // Get PCI register offset for the legacy support in EHCI controller // and store it in HC_STRUC // fpHCStruc->bExtCapPtr = EHCIGetLegacySupportOffset( fpHCStruc, fpHCStruc->wBusDevFuncNum); #if EHCI_64BIT_DATA_STRUCTURE == 0 // // 64bit data structures are not enabled. So check whether this host controller // needs 64bit data structure or not. // if (fpHCStruc->dHCCParams & EHCI_64BIT_CAP) { // // Engineer has to enable the 64bit capability. Post an error message // USBLogError(ERRUSB_EHCI_64BIT_DATA_STRUC); ASSERT(FALSE); // // Connect all ports to the classic host controller // EhciClearOpReg(fpHCStruc, EHCI_CONFIGFLAG, BIT0); return USB_ERROR; } #endif #if HIDE_USB_HISPEED_SUPPORT_SETUP_QUESTION == 0 if ((gUsbData->UsbHiSpeedSupport == 0) && ((fpHCStruc->dHCSParams & EHCI_N_CC) != 0)) { EhciClearOpReg(fpHCStruc, EHCI_CONFIGFLAG, BIT0); return USB_ERROR; } #endif //---------------------------------------------------------------------------- // Note: after this point any access to the operational registers is through // the macros EHCI_DWORD_READ_MEM and EHCI_DWORD_WRITE_MEM; access to the // capability registers is through the macro USBPORT_DWORD_READ_MEM and // there is no macro to write to the registers //---------------------------------------------------------------------------- //(EIP55960)> if ((EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_HCHALTED) == 0) { // Turn HC off and wait for the Halted bit to get set EhciClearOpReg(fpHCStruc, EHCI_USBCMD, EHCI_RUNSTOP); // The Host Controller must halt within 16 micro-frames after // software clears the Run bit. for (i = 0; i < 16; i++) { if (EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_HCHALTED) { break; } FixedDelay(125); // 125 us delay } //while ((DwordReadMem(dMemAddr, EHCI_USBSTS) & EHCI_HCHALTED) == 0) {}; } //<(EIP55960) // /* EIP#23479 // // Reset the host controller (HC must be halted) // if (EHCIResetHC(fpHCStruc) == USB_ERROR) { return USB_ERROR; // HC reset error, error log is updated } //*/ // // Get the frame list size from the EHCI command register // dTemp = EhciReadOpReg(fpHCStruc, EHCI_USBCMD); dTemp = (dTemp & (BIT2 + BIT3)) >> 2; // // Calculate number of elements in the asynchronous list // and store the value in the HCStruc // switch (dTemp) { case 0: fpHCStruc->wAsyncListSize = 1024; break; case 1: fpHCStruc->wAsyncListSize = 512; break; case 2: fpHCStruc->wAsyncListSize = 256; break; case 3: return USB_ERROR; } USB_DEBUG(DEBUG_LEVEL_3, "EHCI AsyncListSize: %d\n", fpHCStruc->wAsyncListSize); // // Set the max bulk data size // fpHCStruc->dMaxBulkDataSize = MAX_EHCI_DATA_SIZE; // // Initialize the frame list pointers // USB_InitFrameList (fpHCStruc, EHCI_TERMINATE); // // Write the base address of the Periodic Frame List to the PERIODIC BASE // register // EhciWriteOpReg(fpHCStruc, EHCI_PERIODICLISTBASE, (UINT32)(UINTN)fpHCStruc->fpFrameList); // // Initialize the periodic schedule // EHCIInitializePeriodicSchedule(fpHCStruc, (UINT32)fpHCStruc->BaseAddress); /* #if EHCI_ASYNC_BELL_SUPPORT // // Allocate and initialize an queue head for Async transfer // Set the QHDummy as Async list head // fpQHAsyncXfer = EhciMemAlloc (fpHCStruc, GET_MEM_BLK_COUNT_STRUC(EHCI_QH)); if (!fpQHAsyncXfer) { return USB_ERROR; } gUsbData->fpQHAsyncXfer = fpQHAsyncXfer; fpQHAsyncXfer->dEndPntCap = QH_ONE_XFER; fpQHAsyncXfer->fpFirstqTD = 0; fpQHAsyncXfer->dAltNextqTDPtr = EHCI_TERMINATE; fpQHAsyncXfer->dNextqTDPtr = EHCI_TERMINATE; // // Assume as a high speed device // dTemp = QH_HIGH_SPEED; // 10b - High speed // // Use data toggle from qTD and this QH is the head of the queue // dTemp |= (QH_USE_QTD_DT | QH_HEAD_OF_LIST | DUMMY_DEVICE_ADDR); // Endpoint is 0 // // dTemp[6:0] = Dev. Addr, dTemp[7] = I bit(0) & dTemp[11:8] = Endpoint (0) // fpQHAsyncXfer->dEndPntCharac = dTemp; // // Set the ASYNCLISTADDR register to point to the QHDummy // EhciWriteOpReg(fpHCStruc, EHCI_ASYNCLISTADDR, (UINT32)(UINTN)fpQHAsyncXfer); // // Set next QH pointer to itself (circular link) // fpQHAsyncXfer->dLinkPointer = (UINT32)(UINTN)fpQHAsyncXfer | EHCI_QUEUE_HEAD; fpQHAsyncXfer->bActive = TRUE; #endif // EHCI_ASYNC_BELL_SUPPORT */ #if USB_RUNTIME_DRIVER_IN_SMM // Check whether no companion host controllers if (!(fpHCStruc->dHCFlag & HC_STATE_EXTERNAL) && (fpHCStruc->dHCSParams & EHCI_N_CC) == 0) { // // Allocate a QH/qTD for QHRepeat/qTDRepeat // fpQHRepeat = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT(sizeof(EHCI_QH)+sizeof(EHCI_QTD))); if (!fpQHRepeat) { return USB_ERROR; // Memory allocation error } DescPtr = fpHCStruc->stDescPtrs.fpEHCIDescPtrs; DescPtr->fpQHRepeat = fpQHRepeat; fpqTDRepeat = (EHCI_QTD*)((UINT32)fpQHRepeat + sizeof(EHCI_QH)); DescPtr->fpqTDRepeat = fpqTDRepeat; // // Setup QHRepeat and qTDRepeat. It will run a interrupt transaction to a // nonexistant dummy device. This will have the effect of generating // a periodic interrupt used to generate keyboard repeat. This QH/qTD // is normally inactive, and is only activated when a key is pressed. // // // Set the first qTD pointer // fpQHRepeat->fpFirstqTD = fpqTDRepeat; //fpQHRepeat->fpDevInfoPtr = (UINT8*)fpDevInfo; fpQHRepeat->dNextqTDPtr = (UINT32)fpqTDRepeat; // // Intialize the queue head // fpQHRepeat->dAltNextqTDPtr = EHCI_TERMINATE; fpQHRepeat->dLinkPointer = EHCI_TERMINATE; // // Set max packet size, address, endpoint and high speed // Update the AH's endpoint characteristcs field with the data formed // fpQHRepeat->dEndPntCharac |= ((0x40 << 16) | DUMMY_DEVICE_ADDR | QH_HIGH_SPEED); // // Set a bit in interrupt mask // fpQHRepeat->dEndPntCap = (BIT0 | QH_ONE_XFER); fpQHRepeat->Interval = REPEAT_INTERVAL; // // Fill the repeat qTD with relevant information // The token field will be set so // Direction PID = QTD_IN_TOKEN, // Size = size of the data, // Data Toggle = QTD_DATA0_TOGGLE, // Error Count = QTD_NO_ERRORS, // Status code = QTD_ACTIVE // The buffer pointers field will point to the fpBuffer buffer // which was before initialized to contain a DeviceRequest struc. // The dNextqTDPtr field will point to the qTDControlSetup // The dAltNextqTDPtr field will be set to EHCI_TERMINATE // fpQHRepeat->dTokenReload = ((UINT32)8 << 16) | QTD_IN_TOKEN | QTD_ONE_ERROR; fpqTDRepeat->dToken = ((UINT32)8 << 16) | QTD_IN_TOKEN | QTD_ONE_ERROR; EHCISetQTDBufferPointers(fpqTDRepeat, &fpQHRepeat->aDataBuffer[0], 8); // // Update next & alternate next qTD pointers // fpqTDRepeat->dNextqTDPtr = EHCI_TERMINATE; fpqTDRepeat->dAltNextqTDPtr = EHCI_TERMINATE; // // Schedule the QHRepeat to 8ms schedule // EhciAddPeriodicQh(fpHCStruc,fpQHRepeat); fpQHRepeat->bCallBackIndex = USB_InstallCallBackFunction(EhciRepeatTDCallback); fpQHRepeat->bActive = FALSE; USBKeyRepeat(fpHCStruc, 0); } #endif // // Clear status register - all R/WC bits // EhciWriteOpReg(fpHCStruc, EHCI_USBSTS, EHCI_USB_INTERRUPT | // Interrupt EHCI_USB_ERROR_INTERRUPT | // Error interrupt EHCI_PORT_CHANGE_DETECT | // Port Change Detect EHCI_FRAME_LIST_ROLLOVER | // Frame List Rollover EHCI_HOST_SYSTEM_ERROR | // Host System Error EHCI_INT_ASYNC_ADVANCE); // Interrupt on Async Advance // // Program the HC BIOS owned bit and return the legacy support register offset // if (fpHCStruc->bExtCapPtr) { EHCIProgramLegacyRegisters(fpHCStruc, 1); // Set HC BIOS owned semaphore // // Enable USB SMI, SMI on port change and SMI on ownership change // dTemp = EHCI_SMI + EHCI_PORT_CHANGE_SMI + EHCI_OWNERSHIP_CHANGE_SMI; EhciWritePciReg(fpHCStruc, fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG, dTemp); } // // Turn HC on // EhciSetOpReg(fpHCStruc, EHCI_USBCMD, \ (EHCI_RUNSTOP | EHCI_PER_SCHED_ENABLE)); // Wait for halt bit get cleared for (i = 0; i < 20; i++) { if (!(EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_HCHALTED)) { break; } FixedDelay(100); // 100 us delay } // // If the port has the power switch then enable the port. Otherwise // Power for the port is already present. So don't need to enable the power. // ( Refer EHCI Spec 2.2.3 HCSPARAMS Structural Parameters Bit 4 (PPC) ) if (fpHCStruc->dHCSParams & EHCI_PPC) { // // Enable port power // for (i = 1, PortReg = EHCI_PORTSC; i <= fpHCStruc->bNumPorts; i++, PortReg += 4) { // // Skip enabling DebugPort // if (fpHCStruc->DebugPort && fpHCStruc->DebugPort == i) continue; if (EhciReadOpReg(fpHCStruc, PortReg) & EHCI_PORTPOWER) { continue; } EhciSetOpReg(fpHCStruc, PortReg, EHCI_PORTPOWER); SetPortPower = TRUE; } // // Delay till the ports power is stabilised // if (SetPortPower) { FixedDelay(20 * 1000); // 20 msec delay } } // Set HC flag as running fpHCStruc->dHCFlag |= HC_STATE_RUNNING; // Set USB_FLAG_DRIVER_STARTED flag when HC is running. if (!(gUsbData->dUSBStateFlag & USB_FLAG_DRIVER_STARTED)) { gUsbData->dUSBStateFlag |= USB_FLAG_DRIVER_STARTED; } // // Disconnect all ports from companion HC (if any) and route them to EHCI // EhciSetOpReg(fpHCStruc, EHCI_CONFIGFLAG, BIT0); //(EIP59663-) //(EIP80307+) if (fpHCStruc->dHCFlag & HC_STATE_CONTROLLER_WITH_RMH) { // Wait for port change detect bit set for (i = 0; i < 50; i++) { if (EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_PORT_CHANGE_DETECT) { break; } FixedDelay(100); // 100 us delay } } else { FixedDelay(100); // 100 us delay } #if USB_RUNTIME_DRIVER_IN_SMM // // Register the USB HW SMI handler // if (!(fpHCStruc->dHCFlag & HC_STATE_EXTERNAL)) { UsbInstallHwSmiHandler(fpHCStruc); } else { USBSB_InstallUsbIntTimerHandler(); } #endif return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCIGetLegacySupportOffset // // Description: This function returns the PCI register offset for the legacy // support in EHCI controller // // Input: fpHCStruc - HCStruc pointer // wPciAddr - PCI address of the EHCI host controller // // Output: 0 If the feature is not present // <>0 Legacy support capability offset // //---------------------------------------------------------------------------- // UINT8 EHCIGetLegacySupportOffset( HC_STRUC* fpHCStruc, UINT16 wPciAddr) { UINT8 bData = 0; UINT32 dData = 0; #if USB_RUNTIME_DRIVER_IN_SMM if (fpHCStruc->dHCFlag & HC_STATE_EXTERNAL) { return 0; } // // Get EHCI Extended Capabilities Pointer // bData = (UINT8)((fpHCStruc->dHCCParams >> 8) & 0xFF); if (!bData) { return 0; // No extended capabilities are implemented. } dData = EhciReadPciReg(fpHCStruc, bData); if (!((UINT8)dData & 1)) { return 0; } #endif return bData; } // //---------------------------------------------------------------------------- // // Procedure: EHCIProgramLegacyRegisters // // Description: This function programs the EHCI legacy registers as per the // input. Also this routine returns the PCI register offset // for the legacy support in EHCI controller // // Input: fpHCStruc HCStruc pointer // bSetReset: // 0 Reset HC BIOS owned bit // 1 Set HC BIOS owned bit // // Output: 0 If the feature is not present // <>0 Legacy support capability offset // //---------------------------------------------------------------------------- // UINT8 EHCIProgramLegacyRegisters( HC_STRUC* fpHCStruc, UINT8 bSetReset) { UINT32 dTemp; // // Check whether EHCI extended capabilities pointer is present // if (!fpHCStruc->bExtCapPtr) { return 0; // No extended capabilities are implemented. } // // Program 'HC BIOS owned semaphore bit' // dTemp = EhciReadPciReg(fpHCStruc, fpHCStruc->bExtCapPtr); dTemp &= ~BIT16; if (bSetReset) { dTemp |= BIT16; } // (USB_S4_RESUME_ISSUE, EIP#20084)> if (gUsbData->dUSBStateFlag & USB_FLAG_RUNNING_UNDER_EFI) dTemp &= ~BIT24; // <(USB_S4_RESUME_ISSUE, EIP#20084) EhciWritePciReg(fpHCStruc, fpHCStruc->bExtCapPtr, dTemp); // // Reset all enable bits and clear the status // dTemp = 0xE0000000 | EHCI_OWNERSHIP_CHANGE_SMI; EhciWritePciReg(fpHCStruc, fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG, dTemp); return fpHCStruc->bExtCapPtr; } // //---------------------------------------------------------------------------- // // Procedure: ClearEECPstatus // // Description: This procedure clear EHCI legacy support status. // // Input: fpHCStruc - HCStruc pointer // wSTatus - Legacy status to clear // // Output: None // //---------------------------------------------------------------------------- // VOID ClearEECPstatus( HC_STRUC* fpHCStruc, UINT16 wStatus) { UINT32 dTemp; if (!fpHCStruc->bExtCapPtr) { return; // No extended capabilities are implemented. } // // Read control and status register // dTemp = EhciReadPciReg(fpHCStruc, fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG); // // Keep enable bits and set clear status bit // dTemp = (dTemp & 0xFFFF) | ((UINT32)wStatus << 16); EhciWritePciReg( fpHCStruc, fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG, dTemp); } // //---------------------------------------------------------------------------- // // Procedure: GetEhciUSBLEGSUP // // Description: This routine return USBLEGSUP register content. It could be // used to check EHCI semaphore owened by BIOS or OS. // // Input: fpHCStruc HCStruc pointer // // Output: UINT32 Legacy support extended capability register content. // -1 if no extended capabilities are implemented. // //---------------------------------------------------------------------------- // UINT32 GetEhciUSBLEGSUP(HC_STRUC* fpHCStruc) { UINT32 dTemp; // // Check whether EHCI extended capabilities pointer is present // if (!fpHCStruc->bExtCapPtr) { return 0xFFFFFFFF; // No extended capabilities are implemented. } // // Read Legacy support register // dTemp = EhciReadPciReg( fpHCStruc, fpHCStruc->bExtCapPtr); return dTemp; } // //---------------------------------------------------------------------------- // // Procedure: EHCI_EnumeratePorts // // Description: This function enumerates the HC ports for devices // // Input: fpHCStruc Host controller's HCStruc structure // // Output: None // //---------------------------------------------------------------------------- // UINT8 EHCI_EnumeratePorts(HC_STRUC* fpHCStruc) { UINT16 wPortCtl = EHCI_PORTSC; // Port Status and Control Register (44h) UINT8 bHCNumber; UINT8 bPortNum; EFI_STATUS EfiStatus; EfiStatus = UsbHcStrucValidation(fpHCStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { return USB_ERROR; } if (EhciIsHalted(fpHCStruc)) { return USB_ERROR; } bHCNumber = (UINT8)(fpHCStruc->bHCNumber | BIT7); // // Enable port power so that new devices can be detected. // // Check whether enumeration flag is set by us or by somebody else by checking // local enum flag. // if (gUsbData->bEnumFlag == FALSE) { gUsbData->bIgnoreConnectStsChng = TRUE; gUsbData->bEnumFlag = TRUE; //(EIP122174+)> do { // // Clear the EHCI_PCD bit of the interrupt status register EHCI_USBSTS // EhciWriteOpReg(fpHCStruc, EHCI_USBSTS, EHCI_PORT_CHANGE_DETECT); // // Check the root hub ports to see if a device is connected. If so, then // call USBCheckPortChange to handle the attachment of a new device. // for ( bPortNum = 1; bPortNum <= fpHCStruc->bNumPorts; bPortNum++) { // // Skip DebugPort enumeration // if (fpHCStruc->DebugPort && fpHCStruc->DebugPort==bPortNum) continue; // // Process device connect/disconnect // USBCheckPortChange(fpHCStruc, bHCNumber, bPortNum); } } while ((EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_PORT_CHANGE_DETECT)); //<(EIP122174+) gUsbData->bIgnoreConnectStsChng = FALSE; // // Reset enumeration flag and enable hub enumeration // gUsbData->bEnumFlag = FALSE; } // // Enable appropriate interrupts // EhciWriteOpReg(fpHCStruc, EHCI_USBINTR, EHCI_USBINT_EN | EHCI_PCDINT_EN); return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCICheckHCStatus // // Description: This function checks whether the host controller is still // under BIOS // // Input: fpHCStruc - Host controller's HCStruc structure // // Output: USB_SUCCESS - If the control is with the BIOS // USB_ERROR - If the control is not with the BIOS // //---------------------------------------------------------------------------- // UINT8 EHCICheckHCStatus( HC_STRUC* HcStruc ) { UINT32 Cmd; UINT32 Sts; Cmd = EhciReadOpReg(HcStruc, EHCI_USBCMD); Sts = EhciReadOpReg(HcStruc, EHCI_USBSTS); // Don't read Periodic Frame List Base Address Register if the controller // doesn't support periodic schedule. if (Cmd & EHCI_PER_SCHED_ENABLE) { if (!(Sts & EHCI_PER_SCHED_STATUS)) { return USB_SUCCESS; } } // // Check whether the controller is still under BIOS control // Read the base address of the Periodic Frame List to the PERIODIC BASE // register and compare with stored value // if ((UINTN)HcStruc->fpFrameList == (EhciReadOpReg(HcStruc, EHCI_PERIODICLISTBASE) & 0xFFFFF000)) { return USB_SUCCESS; // Control is with BIOS } return USB_ERROR; // HC is controlled by someone else } // //---------------------------------------------------------------------------- // // Procedure: EHCIStop // // Description: This function stops the EHCI controller. // // Input: fpHCStruc Host controller's HCStruc structure // // Output: None // //---------------------------------------------------------------------------- // UINT8 EHCI_Stop (HC_STRUC* fpHCStruc) { UINT8 bPortNum; //(EIP26685+) UINT8 Status; UINT8 i; //(EIP55960+) EHCI_DESC_PTRS *DescPtr; EFI_STATUS EfiStatus; UINT8 *MemBlockEnd = gUsbData->fpMemBlockStart + (gUsbData->MemPages << 12); EfiStatus = UsbHcStrucValidation(fpHCStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { return USB_ERROR; } DescPtr = fpHCStruc->stDescPtrs.fpEHCIDescPtrs; #if USB_RUNTIME_DRIVER_IN_SMM if (((UINT8*)DescPtr < gUsbData->fpMemBlockStart) || ((UINT8*)(DescPtr + sizeof(EHCI_DESC_PTRS)) > MemBlockEnd)) { return USB_ERROR; } #endif // // Check whether the control is with BIOS or not // if (EHCICheckHCStatus(fpHCStruc) == USB_SUCCESS) // Controlled by BIOS { #if PCH_EHCI_OWNERSHIP_CHANGE_MECHANISM if (fpHCStruc->dHCFlag & HC_STATE_OWNERSHIP_CHANGE_IN_PROGRESS) { UINT16 PortReg; UINT32 PortSts; UINT32 Data32; // Disconnect all the devices connected to its ports for (bPortNum = 1; bPortNum <= fpHCStruc->bNumPorts; bPortNum++) { USB_StopDevice(fpHCStruc, (UINT8)(fpHCStruc->bHCNumber | BIT7), bPortNum); } // Stop the asynchronous schedule EHCIStopAsyncSchedule(fpHCStruc); // Stop the periodic schedule EHCIStopPeriodicSchedule(fpHCStruc); for (bPortNum = 1; bPortNum <= fpHCStruc->bNumPorts; bPortNum++) { PortReg = (UINT16)((bPortNum-1)*4 + EHCI_PORTSC); PortSts = EhciReadOpReg(fpHCStruc, PortReg); if (!(PortSts & EHCI_PORTENABLE)) { continue; } EhciWriteOpReg(fpHCStruc, PortReg, PortSts | EHCI_SUSPEND); } FixedDelay(250); // 250 us delay // Stop the host controller (Reset bit 0) EhciClearOpReg(fpHCStruc, EHCI_USBCMD, EHCI_RUNSTOP); // The Host Controller must halt within 16 micro-frames after // software clears the Run bit. for (i = 0; i < 16; i++) { if (EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_HCHALTED) { break; } FixedDelay(125); // 125 us delay } // Clear the SMI enable bits if (fpHCStruc->bExtCapPtr) { Data32 = EhciReadPciReg(fpHCStruc, fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG); EhciWritePciReg(fpHCStruc, fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG, Data32 & ~(0x3F)); } // Clear the USBSTS register bits EhciWriteOpReg(fpHCStruc, EHCI_USBSTS, EhciReadOpReg(fpHCStruc, EHCI_USBSTS)); // Clear the Configure Flag bit EhciClearOpReg(fpHCStruc, EHCI_CONFIGFLAG, BIT0); } else #endif { //(EIP26685+)> // // Disconnect all the devices connected to its ports // for (bPortNum = 1; bPortNum <= fpHCStruc->bNumPorts; bPortNum++) { USB_DisconnectDevice(fpHCStruc, (UINT8)(fpHCStruc->bHCNumber | BIT7), bPortNum); } //<(EIP26685+) if (fpHCStruc->DebugPort == 0) { // // Stop the host controller (Reset bit 0) // EhciClearOpReg(fpHCStruc, EHCI_USBCMD, EHCI_RUNSTOP); //(EIP55960)> // The Host Controller must halt within 16 micro-frames after // software clears the Run bit. for (i = 0; i < 16; i++) { if (EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_HCHALTED) { break; } FixedDelay(125); // 125 us delay } //<(EIP55960) // // Reset the host controller // // EIP#23479 EHCIResetHC(fpHCStruc); // ERROR CONDITION RETURNED IS NOT TAKEN CARE Status = EHCIResetHC(fpHCStruc); ASSERT(Status == USB_SUCCESS); } } // // Program the HC BIOS owned bit and return the legacy // support register offset // EHCIProgramLegacyRegisters(fpHCStruc, 0); // Reset HC BIOS owned semaphore // ERROR CONDITION IS NOT HANDLED // // Clear the frame list pointers // USB_InitFrameList (fpHCStruc, EHCI_TERMINATE); // // Disable TD schedule and free the data structures // if (DescPtr->fpQHRepeat) { EhciMemFree(fpHCStruc, DescPtr->fpQHRepeat, GET_MEM_BLK_COUNT(sizeof(EHCI_QH) + sizeof(EHCI_QTD) )); } // // Free the scheduling QHs // EhciMemFree(fpHCStruc, DescPtr->PeriodicQh, GET_MEM_BLK_COUNT(1 * sizeof(EHCI_QH))); // // Free descriptor structure // EhciMemFree(fpHCStruc, DescPtr, GET_MEM_BLK_COUNT_STRUC(EHCI_DESC_PTRS)); //#if EHCI_ASYNC_BELL_SUPPORT // // Free the Async transfer QH // // EhciMemFree(fpHCStruc, gUsbData->fpQHAsyncXfer, GET_MEM_BLK_COUNT_STRUC(EHCI_QH)); //#endif USBKeyRepeat(fpHCStruc, 3); } else // not controlled by BIOS { // // Program the HC BIOS owned bit and return the legacy // support register offset // EHCIProgramLegacyRegisters(fpHCStruc, 0); // Reset HC BIOS owned semaphore } // // Set the HC state to stopped // fpHCStruc->dHCFlag &= ~(HC_STATE_RUNNING); CheckBiosOwnedHc(); return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCI_DisableInterrupts // // Description: This function disables the HC interrupts // // Input: fpHCStruc Pointer to the HCStruc structure // // Output: USB_ERROR On error // USB_SUCCESS On success // //---------------------------------------------------------------------------- // UINT8 EHCI_DisableInterrupts (HC_STRUC* fpHCStruc) { EFI_STATUS EfiStatus; EfiStatus = UsbHcStrucValidation(fpHCStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } // // Disable interrupt generation // EhciClearOpReg(fpHCStruc, EHCI_USBINTR, EHCI_USBINT_EN | EHCI_PCDINT_EN); // // Stop periodic and asynchoronous schedule // EHCIStopAsyncSchedule(fpHCStruc); EHCIStopPeriodicSchedule(fpHCStruc); return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCI_EnableInterrupts // // Description: This function enables the HC interrupts // // Input: fpHCStruc Pointer to the HCStruc structure // // Output: USB_ERROR On error // USB_SUCCESS On success // //---------------------------------------------------------------------------- // UINT8 EHCI_EnableInterrupts (HC_STRUC* fpHCStruc) { EFI_STATUS EfiStatus; EfiStatus = UsbHcStrucValidation(fpHCStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } // // Start periodic and asynchoronous schedule // EHCIStartAsyncSchedule(fpHCStruc); EHCIStartPeriodicSchedule(fpHCStruc); // // Enable interrupt generation // EhciSetOpReg(fpHCStruc, EHCI_USBINTR, EHCI_USBINT_EN | EHCI_PCDINT_EN); return USB_SUCCESS; } // //--------------------------------------------------------------------------- // // Procedure: ProcessRootHubChanges // // Description: Root hub change processing code // // Parameters: fpHCStruc Pointer to the HCStruc structure // // //--------------------------------------------------------------------------- // UINT8 ProcessRootHubChanges( HC_STRUC* fpHCStruc ) { UINT8 bPortNum; // // Check bEnumFlag before enumerating devices behind root hub // if ((gUsbData->bEnumFlag) == TRUE) { return USB_ERROR; } // // Clear the port change bit of the interrupt status register EHCI_USBSTS // EhciWriteOpReg(fpHCStruc, EHCI_USBSTS, EHCI_PORT_CHANGE_DETECT); // // Check all the ports on the root hub for any change in connect status. // If the connect status has been changed on either or both of these ports, // then call the routine UsbHubPortChange for each changed port. // // Set enumeration flag and avoid hub port enumeration // gUsbData->bEnumFlag = TRUE; for (bPortNum = 1; bPortNum <= fpHCStruc->bNumPorts; bPortNum++) { if (fpHCStruc->DebugPort && fpHCStruc->DebugPort==bPortNum) continue; // // Process device connect/disconnect // Note: port connect status is cleared while processing // connect/disconnect (EHCIGetRootHubStatus) // USBCheckPortChange(fpHCStruc, (UINT8)(fpHCStruc->bHCNumber | BIT7), bPortNum); } // // Reset enumeration flag and enable hub enumeration // gUsbData->bEnumFlag = FALSE; return USB_SUCCESS; } // //--------------------------------------------------------------------------- // // Procedure: EHCI_ProcessInterrupt // // Description: This function is called when the USB interrupt bit is // set. This function will parse through the TDs and QHs to // find out completed TDs and call their respective call // back functions // // Parameters: fpHCStruc Pointer to the HCStruc structure // // Output: USB_ERROR - Need more Interrupt processing // USB_SUCCESS - No interrupts pending // //--------------------------------------------------------------------------- // UINT8 EHCI_ProcessInterrupt(HC_STRUC* fpHCStruc) { UINT32 dSts, dTmp; UINT16 wStatus; EFI_STATUS EfiStatus; EfiStatus = UsbHcStrucValidation(fpHCStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } //(EIP71067-)> //#if (EHCI_ASYNC_BELL_SUPPORT==0) // EHCI_QH *fpQH; //#endif //<(EIP71067-) // // If EHCI extended capabilities pointer is present, // then service OwnerShipChange SMI // if (fpHCStruc->bExtCapPtr) { // // Read control and status register // dTmp = EhciReadPciReg( fpHCStruc, fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG); wStatus = (UINT16)dTmp; wStatus &= (UINT16)(dTmp >> 16); // "And" enable and status bits if (wStatus & EHCI_OWNERSHIP_CHANGE_SMI_STS) { ClearEECPstatus(fpHCStruc, wStatus); ProcessOwnerShipChangeSMI(fpHCStruc); return USB_SUCCESS; // Break from Interrupt process loop } } // // Check whether the controller is still under BIOS control // Read the base address of the Periodic Frame List to the PERIODIC BASE // register and compare with stored value // if (EHCICheckHCStatus(fpHCStruc) == USB_ERROR) { // // Control is not with us anymore, we should disable SMI generation // and come out. // if (fpHCStruc->bExtCapPtr) { // // Read control and status register // dTmp = EhciReadPciReg( fpHCStruc, fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG); // // Leave only Ownership SMI active. // dTmp &= 0xE0002000; EhciWritePciReg( fpHCStruc, fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG, dTmp); } return USB_SUCCESS; } if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { return USB_SUCCESS; } while(TRUE){ // // Get the interrupt status // dSts = EhciReadOpReg(fpHCStruc, EHCI_USBSTS); //USB_DEBUG(DEBUG_LEVEL_3, "-->> %x <<--\n", dSts); if (dSts & EHCI_HOST_SYSTEM_ERROR) { gUsbData->dUSBStateFlag &= (~USB_FLAG_ENABLE_BEEP_MESSAGE); EHCI_Start(fpHCStruc); EHCI_EnumeratePorts(fpHCStruc); gUsbData->dUSBStateFlag |= USB_FLAG_ENABLE_BEEP_MESSAGE; continue; } if (dSts & EHCI_HCHALTED) { // Clear the USBSTS register bits EhciWriteOpReg(fpHCStruc, EHCI_USBSTS, EhciReadOpReg(fpHCStruc, EHCI_USBSTS)); break; } // // Check for transaction complete // if ((gUsbData->ProcessingPeriodicList == TRUE) && (dSts & EHCI_USB_INTERRUPT)) { // // Clear the interrupt status // EhciWriteOpReg(fpHCStruc, EHCI_USBSTS, EHCI_USB_INTERRUPT); //Section 4.4 Schedule traversal rules. //if the periodic schedule is enabled (see Section 4.6) then the host controller must //execute from the periodic schedule before executing from the asynchronous schedule. //It will only execute from the asynchronous schedule after it encounters the end of //the periodic schedule. // // Check and process periodic schedule // if (dSts & EHCI_PER_SCHED_STATUS) { // // Check the command register for Async status // dTmp = EhciReadOpReg(fpHCStruc, EHCI_USBCMD); if (dTmp & EHCI_PER_SCHED_ENABLE) { EHCIProcessPeriodicList(fpHCStruc); } } // // Check for Asynchronous schedule completion // /* //(EIP71067-)> if (dSts & EHCI_ASYNC_SCHED_STATUS) { dTmp = DwordReadMem(dMemAddr, EHCI_USBCMD); if (dTmp & EHCI_ASYNC_SCHED_ENABLE) { // // Check and process Async. QH // #if EHCI_ASYNC_BELL_SUPPORT EHCIProcessQH(fpHCStruc, fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpQHControl); EHCIProcessQH(fpHCStruc, fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpQHBulk); #else // // Get the Async list address // fpQH = (EHCI_QH*)(UINTN)DwordReadMem(dMemAddr, EHCI_ASYNCLISTADDR); if (EHCIProcessQH(fpHCStruc, fpQH) == USB_ERROR) { //continue; //return USB_SUCCESS; } else { // // Async list processed; stop the Async transfer // EHCIStopAsyncSchedule(fpHCStruc); } #endif } } */ //<(EIP71067-) continue; } // // Check PORT_CHANGE_DETECT bit // if ((dSts & EHCI_PORT_CHANGE_DETECT )) { if(ProcessRootHubChanges(fpHCStruc) == USB_SUCCESS) { continue; } } break; // No more statuses to process } return USB_SUCCESS; // Set as interrupt processed } // //---------------------------------------------------------------------------- // // Procedure: ProcessOwnerShipChangeSMI // // Description: This procedure process EHCI OwnerShipChange SMI. // // Input: fpHCStruc HCStruc pointer // // Output: None // //---------------------------------------------------------------------------- // VOID ProcessOwnerShipChangeSMI(HC_STRUC* fpHCStruc) { UINT32 dTemp = GetEhciUSBLEGSUP(fpHCStruc); fpHCStruc->dHCFlag |= HC_STATE_OWNERSHIP_CHANGE_IN_PROGRESS; if (dTemp & EHCI_HC_OS) { gUsbData->dUSBStateFlag &= (~USB_FLAG_ENABLE_BEEP_MESSAGE); ProcessSmiChangeToEHCD(fpHCStruc); } else { gUsbData->dUSBStateFlag |= USB_FLAG_ENABLE_BEEP_MESSAGE; ProcessSmiChangeToBIOS(fpHCStruc); } fpHCStruc->dHCFlag &= ~(HC_STATE_OWNERSHIP_CHANGE_IN_PROGRESS); } // //---------------------------------------------------------------------------- // // Procedure: ProcessSmiChangeToEHCD // // Description: This procedure process OwnerShipChange for BIOS -> EHCD. // // Input: fpHCStruc HCStruc pointer // // Output: None // //---------------------------------------------------------------------------- // VOID ProcessSmiChangeToEHCD (HC_STRUC* fpHCStruc) { EHCI_Stop(fpHCStruc); // Stop EHCI legacy } // //---------------------------------------------------------------------------- // // Procedure: ProcessSmiChangeToBIOS // // Description: This procedure process OwnerShipChange for EHCD -> BIOS. // // Input: SI HCStruc pointer // // Output: None // // Modified: None //---------------------------------------------------------------------------- // VOID ProcessSmiChangeToBIOS ( HC_STRUC *fpHCStruc ) { HC_STRUC *Hc; UINT8 Count; DEV_INFO *Device; // Stop UHCI devices connected to the companions // Core8 executes this under MKF_PCCHECK_PATCH==1 condition, EIP10272 for (Count = 1; Count < MAX_DEVICES; Count++) { Device = &gUsbData->aDevInfoTable[Count]; Hc = gUsbData->HcTable[Device->bHCNumber - 1]; if (Hc->bHCType != USB_HC_UHCI) continue; // Not UHCI if ((Device->Flag & DEV_INFO_VALID_STRUC)==0) continue; // Not valid if ((Hc->wBusDevFuncNum & 0xfff8) != (fpHCStruc->wBusDevFuncNum & 0xfff8)) continue; // Not a companion // Found a device connected to UHCI companion controller. Stop it. USB_StopDevice(Hc, Device->bHubDeviceNumber, Device->bHubPortNumber); } EHCI_Start(fpHCStruc); // Reinitialize EHCI host controller } // //---------------------------------------------------------------------------- // // Procedure: EHCI_ReleasePortOwner // // Description: // // Input: HcStruc - Pointer to HCStruc of the host controller // PortNum - Port in the HC whose status is requested // // Output: // //---------------------------------------------------------------------------- // UINT8 EHCI_ReleasePortOwner( HC_STRUC* HcStruc, UINT8 PortNum ) { UINT16 PortReg = (UINT16)((PortNum-1)*4 + EHCI_PORTSC); UINT16 i; if ((HcStruc->dHCSParams & EHCI_N_CC) == 0) { return USB_SUCCESS; } if (!(EhciReadOpReg(HcStruc, PortReg) & EHCI_CURRENTCONNECTSTATUS)) { return USB_ERROR; } USB_DEBUG(DEBUG_LEVEL_3, "Release EHCI port %d\n", PortNum); EhciSetOpReg(HcStruc, PortReg, EHCI_PORTOWNER); // Loop until Full speed device disconnect event process done. // This change is done in sync with Core8 except the extra 400mS delay for (i = 0; i < 200; i++) { if (EhciReadOpReg(HcStruc, PortReg) & EHCI_CONNECTSTATUSCHANGE) { break; } FixedDelay(100); } EhciSetOpReg(HcStruc, PortReg, EHCI_CONNECTSTATUSCHANGE); return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCIGetRootHubStatus // // Description: This function returns the port connect status for the // root hub port // // Input: fpHCStruc - Pointer to HCStruc of the host controller // bPortNum - Port in the HC whose status is requested // // Output: Port status flags (see USB_PORT_STAT_XX equates) // //---------------------------------------------------------------------------- // UINT8 EHCI_GetRootHubStatus( HC_STRUC* fpHCStruc, UINT8 bPortNum, BOOLEAN ClearChangeBits ) { UINT32 dTmp; UINT8 bStatus = USB_PORT_STAT_DEV_OWNER; UINT16 wPortReg = (UINT16)((bPortNum-1)*4 + EHCI_PORTSC); EFI_STATUS EfiStatus; EfiStatus = UsbHcStrucValidation(fpHCStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } if (fpHCStruc->DebugPort && fpHCStruc->DebugPort==bPortNum) return 0; // // Read the status of the port // dTmp = EhciReadOpReg(fpHCStruc, wPortReg); USB_DEBUG(3, "Ehci port[%d] status: %08x\n", bPortNum, dTmp); // Detect the high-speed device. // In case of low-speed or full-speed change the ownership to a // companion 1.1 controller (if any) if (dTmp & EHCI_CURRENTCONNECTSTATUS) { // Analyze Line Status if ((dTmp & EHCI_LINE_STATUS) == EHCI_DMINUSBIT) { // Low speed device connected EHCI_ReleasePortOwner(fpHCStruc, bPortNum); dTmp = EhciReadOpReg(fpHCStruc, wPortReg); } } // // Check the connect status change bit // if (dTmp & EHCI_CONNECTSTATUSCHANGE) { // // Set connect status change flag // bStatus |= USB_PORT_STAT_DEV_CONNECT_CHANGED; // // Wait 20ms for host controller could report accurate port status properly. // //FixedDelay(gUsbData->UsbTimingPolicy.EhciPortConnect * 1000); // 20ms delay // // Read the status of the port // //dTmp = EhciReadOpReg(fpHCStruc, wPortReg); // Clear connect status change if (ClearChangeBits == TRUE) { EhciSetOpReg(fpHCStruc, wPortReg, EHCI_CONNECTSTATUSCHANGE); //(EIP61030+) } } if (dTmp & EHCI_CURRENTCONNECTSTATUS) { bStatus |= USB_PORT_STAT_DEV_CONNECTED; if (dTmp & EHCI_PORTENABLE) { bStatus |= USB_PORT_STAT_DEV_HISPEED; // Patch for CloverTrail if (fpHCStruc->Vid == 0x8086 && (fpHCStruc->Did == 0xE006 || fpHCStruc->Did == 0x08F2)) { if ((dTmp & EHCI_LINE_STATUS) == EHCI_DMINUSBIT) { bStatus &= ~USB_PORT_STAT_DEV_HISPEED; bStatus |= USB_PORT_STAT_DEV_LOWSPEED; } else if ((dTmp & EHCI_LINE_STATUS) == EHCI_DPLUSBIT) { bStatus &= ~USB_PORT_STAT_DEV_HISPEED; bStatus |= USB_PORT_STAT_DEV_FULLSPEED; } } bStatus |= USB_PORT_STAT_DEV_ENABLED; //(EIP61030+)> } else { if (gUsbData->bIgnoreConnectStsChng == TRUE) { if (!(dTmp & EHCI_CONNECTSTATUSCHANGE)) { bStatus |= USB_PORT_STAT_DEV_CONNECT_CHANGED; } } //<(EIP61030+) } } if (dTmp & EHCI_PORTOWNER) { bStatus &= ~USB_PORT_STAT_DEV_OWNER; } return bStatus; } // //---------------------------------------------------------------------------- // // Procedure: EHCIDisableRootHub // // Description: This function disables the EHCI HC Ruoot hub port. // // Input: fpHCStruc - Pointer to HCStruc of the host controller // bPortNum - Port in the HC to disable // // Output: USB_SUCCESS on success // USB_ERROR on error // //---------------------------------------------------------------------------- // UINT8 EHCI_DisableRootHub( HC_STRUC* fpHCStruc, UINT8 bPortNum) { //(EIP58108+)> UINT16 PortReg = (UINT16)((bPortNum-1)*4 + EHCI_PORTSC); UINT32 i; EFI_STATUS EfiStatus; EfiStatus = UsbHcStrucValidation(fpHCStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } if (fpHCStruc->DebugPort && fpHCStruc->DebugPort==bPortNum) return USB_SUCCESS; if (!(EhciReadOpReg(fpHCStruc, PortReg) & EHCI_PORTENABLE)) { return USB_SUCCESS; } EhciClearOpReg(fpHCStruc, PortReg, EHCI_PORTENABLE); for (i = 0; i < 100; i++) { if ((EhciReadOpReg(fpHCStruc, PortReg) & EHCI_PORTENABLE) == 0) { break; } FixedDelay(100); } //<(EIP58108+) return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCIEnableRootHub // // Description: This function enables the EHCI HC Root hub port. // // Input: fpHCStruc - Pointer to HCStruc of the host controller // bPortNum - Port in the HC to enable // // Output: USB_SUCCESS on success // USB_ERROR on error // //---------------------------------------------------------------------------- // UINT8 EHCI_EnableRootHub( HC_STRUC* fpHCStruc, UINT8 bPortNum) { // // Software can only enable the EHCI root hub ports by port RESET. HC will // enable the port only if it is a high speed device // return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCI_ResetRootHub // // Description: This function resets the EHCI HC Root hub port. // // Input: HcStruc - Pointer to HCStruc of the host controller // PortNum - Port in the HC to enable // // Output: USB_SUCCESS on success // USB_ERROR on error // //---------------------------------------------------------------------------- // UINT8 EHCI_ResetRootHub( HC_STRUC* HcStruc, UINT8 PortNum ) { UINT16 PortReg = (UINT16)((PortNum-1)*4 + EHCI_PORTSC); UINT32 i; EFI_STATUS EfiStatus; EfiStatus = UsbHcStrucValidation(HcStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } // Disable the port if it is enabled if (EhciReadOpReg(HcStruc, PortReg) & EHCI_PORTENABLE) { EhciClearOpReg(HcStruc, PortReg, EHCI_PORTENABLE); // There may be a delay in disabling or enabling a port due to other // host controller and bus events. for (i = 0; i < 100; i++) { if ((EhciReadOpReg(HcStruc, PortReg) & EHCI_PORTENABLE) == 0) { break; } FixedDelay(100); // 100 us delay } } // Reset the port EhciSetOpReg(HcStruc, PortReg, EHCI_PORTRESET); if ((HcStruc->dHCFlag & HC_STATE_CONTROLLER_WITH_RMH) && (PortNum == 1)) { FixedDelay(3 * 1000); // 3 ms delay } else { // Wait til port disable is complete (Tdrstr=50ms Ref 7.1.7.5 of USB Spec 2.0) FixedDelay(50 * 1000); // 50 ms delay } EhciClearOpReg(HcStruc, PortReg, EHCI_PORTRESET); // Terminate reset if (!(EhciReadOpReg(HcStruc, PortReg) & EHCI_CURRENTCONNECTSTATUS)) { return USB_ERROR; } // if the port detects that the attached device is high-speed during reset, // then the host controller must have the port in the enabled state within 2ms // of software writing this bit to a zero. for (i = 0; i < 20; i++) { if ((EhciReadOpReg(HcStruc, PortReg) & (EHCI_PORTRESET | EHCI_PORTENABLE)) == EHCI_PORTENABLE) { break; } FixedDelay(100); // 100 us delay } if (EhciReadOpReg(HcStruc, PortReg) & EHCI_PORTRESET) { // Reset failed USBLogError(USB_ERR_PORT_RESET_FAILED); return USB_ERROR; } if (!(EhciReadOpReg(HcStruc, PortReg) & EHCI_PORTENABLE)) { if (!(EhciReadOpReg(HcStruc, PortReg) & EHCI_CURRENTCONNECTSTATUS)) { return USB_ERROR; } else { EHCI_ReleasePortOwner(HcStruc, PortNum); return USB_ERROR; } } FixedDelay(1 * 1000); // 1 ms delay return USB_SUCCESS; } //(EIP54018+)> // //---------------------------------------------------------------------------- // // Name: EHCI_GlobalSuspend // // Description: // This function suspend the EHCI HC. // //---------------------------------------------------------------------------- // UINT8 EHCI_GlobalSuspend( HC_STRUC* HcStruc ) { UINT16 PortReg; UINT32 PortSts; UINT8 PortNum; UINT8 i; EFI_STATUS EfiStatus; EfiStatus = UsbHcStrucValidation(HcStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { return USB_ERROR; } if (EhciIsHalted(HcStruc)) { return USB_ERROR; } for (PortNum = 1; PortNum <= HcStruc->bNumPorts; PortNum++) { PortReg = (UINT16)(EHCI_PORTSC + (PortNum - 1) * 4 ); PortSts = EhciReadOpReg(HcStruc, PortReg ); USB_DEBUG(DEBUG_LEVEL_3,"EHCI PortSts[%x] %x \n", PortNum, PortSts); // Check if port is disabled or suspended. if((PortSts & EHCI_PORTENABLE) && (!(PortSts & EHCI_SUSPEND))) { // Suspend if necessary. EhciClearOpReg(HcStruc, PortReg, EHCI_WKOC_E | EHCI_WKDSCNNT_E | EHCI_WKCNNT_E); EhciSetOpReg(HcStruc, PortReg, EHCI_SUSPEND); // Read PortSc until port shows suspended. for(i = 0; i < 100; i++) { if(EhciReadOpReg(HcStruc, PortReg) & EHCI_SUSPEND) { break; } FixedDelay(100); // 100 us delay } } } // Turn HC off and wait for the Halted bit to get set EhciClearOpReg(HcStruc, EHCI_USBCMD, EHCI_RUNSTOP); // The Host Controller must halt within 16 micro-frames after // software clears the Run bit. for (i = 0; i < 16; i++) { if(EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_HCHALTED) { break; } FixedDelay(125); // 125 us delay } HcStruc->dHCFlag &= ~(HC_STATE_RUNNING); HcStruc->dHCFlag |= HC_STATE_SUSPEND; return USB_SUCCESS; } //<(EIP54018+) // //---------------------------------------------------------------------------- // // Procedure: EHCI_GetHiSpeedHubPortNumber // // Description: This function gets the hi-speed hub's device and port number // to which this low speed device is connected. It parses // through its parents until it finds the correct device. This // information is used for split transaction // // Input: fpDevInfo - Device info pointer of the device // // Output: UINT16 - Device/port number of the hi-speed hub // // Notes: This low/full speed device may be behind different hubs as // shown below. In any case this routine will get the device // address of the hub number HISP_A : // Notations used: // MBPortX Motherboard USB port // HISP_X Hi-speed hub number X // FUSP_X Full-speed hub number X // Device Low/Full speed device // Config 1: // MBPortX --> HISP_A --> Device // Config 2: // MBPortX --> HISP_A --> FUSP_1 --> Device // Config 3: // MBPortX --> HISP_B --> HISP_A --> Device // Config 4: // MBPortX --> HISP_A --> FUSP_1 --> HISP_B --> Device // In the above configuration the HISP_B will be operated in // full speed rather than hi-speed since it is connected to a // full speed hub //---------------------------------------------------------------------------- // UINT16 EHCI_GetHiSpeedHubPortNumber(DEV_INFO* fpDevInfo) { DEV_INFO* fpHubDev = fpDevInfo; DEV_INFO* fpParentHubDev; UINT16 wRetCode; if( !VALID_DEVINFO( fpDevInfo) ) return 0; // // Get the device info structure for the matching device address // // // Get the device number of the immediate hub, then get the device // info structure for this device number // for(;;) { fpParentHubDev = USB_GetDeviceInfoStruc(USB_SRCH_DEV_ADDR, 0, fpHubDev->bHubDeviceNumber, 0); if ( !fpParentHubDev ) // Error. Exit ! { return 0; } if (((fpParentHubDev->bEndpointSpeed << USB_PORT_STAT_DEV_SPEED_MASK_SHIFT) & USB_PORT_STAT_DEV_SPEED_MASK) == 0) break; fpHubDev = fpParentHubDev; } // // The first USB 2.0 hub found as fpHubDev to which the low/full speed // device is connected // wRetCode = (UINT16)((fpHubDev->bHubPortNumber << 7) | fpHubDev->bHubDeviceNumber); return wRetCode; } // //---------------------------------------------------------------------------- // // Procedure: EhciExexuteAsyncSchedule // // Description: This function insert the requested QH to asynchronous schedule // and waits until the QH completes or the transaction time-out. // // Input: HcStruc - Pointer to HCStruc of the host controller // XferQh - Pointer to the QH which has to be completed // // Output: USB_ERROR On error // USB_SUCCESS On success // //---------------------------------------------------------------------------- // UINT16 EhciExexuteAsyncSchedule( HC_STRUC *HcStruc, EHCI_QH *XferQh ) { UINT16 Status = USB_SUCCESS; UINT32 Count; UINT32 TimeOut = gUsbData->wTimeOutValue * 100; // in 10 macrosecond unit // //#if EHCI_ASYNC_BELL_SUPPORT // UINT32 Tmp; // XferQh->dLinkPointer = EHCI_TERMINATE; // XferQh->bActive = TRUE; // // Insert the Control/Bulk QH into the Async list // // Tmp = gUsbData->fpQHAsyncXfer->dLinkPointer; // gUsbData->fpQHAsyncXfer->dLinkPointer = (UINT32)XferQh | EHCI_QUEUE_HEAD; // XferQh->dLinkPointer = Tmp; //#else // // Set the ASYNCLISTADDR register to point to the Control/Bulk QH // EhciWriteOpReg(HcStruc, EHCI_ASYNCLISTADDR, (UINT32)(UINTN)XferQh); // // Set next QH pointer to itself (circular link) // XferQh->dLinkPointer = (UINT32)((UINTN)XferQh | EHCI_QUEUE_HEAD); XferQh->bActive = TRUE; //#endif // // Now put the Control/Bulk QH into the HC's schedule by // setting the Async. schedule enabled field of USBCMD register // This will cause the HC to execute the transaction in the next active frame. // Status = EHCIStartAsyncSchedule(HcStruc); if (Status == USB_ERROR) { return Status; } // Wait for tansfer complete for(Count = 0; !TimeOut || Count < TimeOut; Count++) { EHCIProcessQH(HcStruc, XferQh); if(XferQh->bActive == FALSE) { break; } FixedDelay(10); // 10 microsec } //#if EHCI_ASYNC_BELL_SUPPORT // // Disconnect Control/Bulk QH from the Async list // // EHCIRemoveQHFromAsyncList(HcStruc, XferQh); //#else // // Stop the Async transfer // EHCIStopAsyncSchedule(HcStruc); //#endif if(XferQh->bActive == TRUE) { XferQh->bActive = FALSE; Status = USB_ERROR; USB_DEBUG (DEBUG_LEVEL_3, "EHCI Time-Out\n"); } // Service all interrupts EHCI_ProcessInterrupt(HcStruc); return Status; } // //--------------------------------------------------------------------------- // // Procedure: EHCI_ControlTransfer // // Description: This function executes a device request command transaction // on the USB. One setup packet is generated containing the // device request parameters supplied by the caller. The setup // packet may be followed by data in or data out packets // containing data sent from the host to the device // or vice-versa. This function will not return until the // request either completes successfully or completes in error // (due to time out, etc.) // // Parameters: fpHCStruc Pointer to HCStruc of the host controller // pDevInfo DeviceInfo structure (if available else 0) // wRequest Request type (low byte) // Bit 7 : Data direction // 0 = Host sending data to device // 1 = Device sending data to host // Bit 6-5 : Type // 00 = Standard USB request // 01 = Class specific // 10 = Vendor specific // 11 = Reserved // Bit 4-0 : Recipient // 00000 = Device // 00001 = Interface // 00010 = Endpoint // 00100 - 11111 = Reserved // Request code, a one byte code describing // the actual device request to be executed // (ex: Get Configuration, Set Address etc) // wIndex wIndex request parameter (meaning varies) // wValue wValue request parameter (meaning varies) // fpBuffer Buffer containing data to be sent to the // device or buffer to be used to receive data // wLength wLength request parameter, number of bytes // of data to be transferred in or out // of the host controller // // // Output: Number of bytes actually transferred // //--------------------------------------------------------------------------- // UINT16 EHCI_ControlTransfer ( HC_STRUC *fpHCStruc, DEV_INFO *fpDevInfo, UINT16 wRequest, UINT16 wIndex, UINT16 wValue, UINT8 *fpBuffer, UINT16 wLength) { UINT32 dTmp, dTmp1; UINT16 wRetCode = 0; // Initialize with error EHCI_QH *fpQHCtl; EHCI_QTD *fpQTDCtlSetup, *fpQTDCtlData, *fpQTDCtlStatus; DEV_REQ *fpRequest = NULL; UINT8 bEndpointSpeed; UINT8 *BufPhyAddr = NULL; VOID *BufferMapping = NULL; EFI_STATUS EfiStatus = EFI_SUCCESS; EfiStatus = UsbHcStrucValidation(fpHCStruc); if (EFI_ERROR(EfiStatus)) { return 0; } EfiStatus = UsbDevInfoValidation(fpDevInfo); if (EFI_ERROR(EfiStatus)) { return 0; } #if USB_RUNTIME_DRIVER_IN_SMM if (gCheckUsbApiParameter) { if (wLength != 0) { EfiStatus = AmiValidateMemoryBuffer((VOID*)fpBuffer, wLength); if (EFI_ERROR(EfiStatus)) { USB_DEBUG(3, "Ehci ControlTransfer Invalid Pointer, Buffer is in SMRAM.\n"); return 0; } } gCheckUsbApiParameter = FALSE; } #endif if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { return 0; } if (EhciIsHalted(fpHCStruc)) { return 0; } if( !VALID_DEVINFO( fpDevInfo) ) return 0; gUsbData->dLastCommandStatusExtended = 0; //(EIP84790+) //USB_DEBUG(DEBUG_LEVEL_3, "EHCI_ControlTransfer..\n"); // // Build the device request in the data area of the control setup qTD // fpRequest = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT(sizeof(DEV_REQ))); ASSERT(fpRequest); if (fpRequest == NULL) { return 0; } fpRequest->wRequestType = wRequest; fpRequest->wIndex = wIndex; fpRequest->wValue = wValue; fpRequest->wDataLength = wLength; // // The QH endpoint characteristic field will be set so // Function address & Endpoint number = From DeviceInfo structure, // Direction = From TD, // Speed = DeviceInfo.bEndpointSpeed, // Skip = 1, Format = 0, // Max packet size = DeviceInfo.wEndp0MaxPacket // The dNextqTDPtr field will be set to qTDControlSetup // The dAltNextqTDPtr field will be set to EHCI_TERMINATE // The dCurrentqTDPtr field will be set to 0 // // // Intialize the queue head with null pointers // //(EIP71067)> fpQHCtl = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT((1 * sizeof(EHCI_QH))+ (3 * sizeof(EHCI_QTD)))); //(EIP83295+)> if(!fpQHCtl) { return 0; } //<(EIP83295+) //(EIP81030)> fpQTDCtlSetup = (EHCI_QTD*)((UINTN)fpQHCtl + sizeof(EHCI_QH)); fpQTDCtlData = (EHCI_QTD*)((UINTN)fpQTDCtlSetup + sizeof(EHCI_QTD)); fpQTDCtlStatus = (EHCI_QTD*)((UINTN)fpQTDCtlData + sizeof(EHCI_QTD)); //<(EIP71067) //bEndpointSpeed; // 00/01/10 for HI/LO/FULL // // Assume as a high speed device // dTmp = QH_HIGH_SPEED; // 10b - High speed // // Check for high speed // if (bEndpointSpeed) // Low/Full speed device { dTmp = ((UINT32)bEndpointSpeed & 1) << 12; // Bit 12 = full/low speed flag dTmp |= QH_CONTROL_ENDPOINT; // // Set the hub address and port number // Get the Hispeed hub port number & device number // dTmp1 = (UINT32)EHCI_GetHiSpeedHubPortNumber(fpDevInfo); dTmp1 = (dTmp1 << 16); // Split complete Xaction fpQHCtl->dEndPntCap |= dTmp1; } //USB_DEBUG(DEBUG_LEVEL_3, "Tmp1..%x\n", dTmp); // // Use data toggle from qTD and this QH is the head of the queue // //#if EHCI_ASYNC_BELL_SUPPORT // dTmp |= QH_USE_QTD_DT; //#else dTmp |= QH_USE_QTD_DT; // Do not set QH_HEAD_OF_LIST bit on VIA controller if (fpHCStruc->Vid != 0x1106) { dTmp |= QH_HEAD_OF_LIST; } //#endif dTmp |= (UINT32)fpDevInfo->bDeviceAddress; // // dTmp[Bits 6:0] = Dev. Addr // dTmp[Bit7] = I bit(0) // dTmp[Bits11:8] = Endpoint (0) // dTmp1 = (UINT32)fpDevInfo->wEndp0MaxPacket; dTmp |= (dTmp1 << 16); // Tmp[Bits26:16] = device's packet size fpQHCtl->dEndPntCharac = dTmp; // // Fill in various fields in the qTDControlSetup. // //fpQTDCtlSetup = fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpqTDControlSetup; //(EIP71067-) // // The token field will be set so // Direction PID = QTD_SETUP_TOKEN, // Size = size of the data, // Data Toggle = QTD_SETUP_TOGGLE, // Error Count = QTD_THREE_ERRORS, // Status code = QTD_DO_OUT + QTD_ACTIVE // The buffer pointers field will point to the aControlSetupData buffer // which was before initialized to contain a DeviceRequest struc. // The dNextqTDPtr field will point to the qTDControlData if data will // be sent/received or to the qTDControlStatus if no data is expected. // The dAltNextqTDPtr field will be set to EHCI_TERMINATE // fpQTDCtlSetup->dToken = QTD_SETUP_TOKEN | QTD_SETUP_TOGGLE | QTD_IOC_BIT | QTD_THREE_ERRORS | QTD_DO_OUT | QTD_ACTIVE | (8 << 16); // Data size // // Update buffer pointers // EHCISetQTDBufferPointers(fpQTDCtlSetup, (UINT8*)fpRequest, 8); //fpQTDCtlData = fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpqTDControlData; //(EIP71067-) if (wLength) // br if no data to transfer { // // Fill in various fields in the qTDControlData // // The token field will be set so // Direction PID = QTD_OUT_TOKEN/QTD_IN_TOKEN, // Size = size of the data, // Data Toggle = QTD_DATA1_TOGGLE, // Error Count = QTD_THREE_ERRORS, // Status code = QTD_DO_OUT(if it is out) + QTD_ACTIVE // The buffer pointers field will point to the fpBuffer buffer // which was before initialized to contain a DeviceRequest struc. // The dNextqTDPtr field will point to the qTDControlSetup // The dAltNextqTDPtr field will be set to EHCI_TERMINATE // fpQTDCtlData->dToken = QTD_IN_TOKEN | QTD_DATA1_TOGGLE | QTD_IOC_BIT | QTD_THREE_ERRORS | QTD_ACTIVE; if (!(wRequest & BIT7)) // Br if host sending data to device (OUT) { fpQTDCtlData->dToken = QTD_OUT_TOKEN | QTD_DATA1_TOGGLE | QTD_IOC_BIT | QTD_THREE_ERRORS | QTD_DO_OUT | QTD_ACTIVE; } // // Set length // fpQTDCtlData->dToken |= ((UINT32)wLength << 16); EhciDmaMap(fpHCStruc, (UINT8)(wRequest & BIT7), fpBuffer, wLength, &BufPhyAddr, &BufferMapping); // // Update buffer pointers // EHCISetQTDBufferPointers(fpQTDCtlData, (UINT8*)BufPhyAddr, (UINT32)wLength); } // // Fill in various fields in the qTDControlStatus // //fpQTDCtlStatus = fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpqTDControlStatus; //(EIP71067-) // // The token field will be set so // Direction PID = QTD_OUT_TOKEN/QTD_IN_TOKEN, // Size = 0, // Data Toggle = QTD_DATA1_TOGGLE, // Error Count = QTD_THREE_ERRORS, // Status code = QTD_DO_OUT(if it is out) + QTD_ACTIVE // The buffer pointers field will be 0 // The dNextqTDPtr field will set to EHCI_TERMINATE // The dAltNextqTDPtr field will be set to EHCI_TERMINATE // // For OUT control transfer status should be IN and // for IN cotrol transfer, status should be OUT // fpQTDCtlStatus->dToken = QTD_IN_TOKEN | QTD_DATA1_TOGGLE | QTD_IOC_BIT | QTD_THREE_ERRORS | QTD_ACTIVE; if(wRequest & BIT7) { fpQTDCtlStatus->dToken = QTD_OUT_TOKEN | QTD_DATA1_TOGGLE | QTD_IOC_BIT | QTD_THREE_ERRORS | QTD_DO_OUT | QTD_ACTIVE; } EHCISetQTDBufferPointers(fpQTDCtlStatus, NULL, 0); // // Link the qTD formed now and connect them with the control queue head // fpQHCtl->fpFirstqTD = fpQTDCtlSetup; fpQHCtl->dNextqTDPtr = (UINT32)(UINTN)fpQTDCtlSetup; if(wLength) { fpQTDCtlSetup->dNextqTDPtr = (UINT32)(UINTN)fpQTDCtlData; fpQTDCtlData->dNextqTDPtr = (UINT32)(UINTN)fpQTDCtlStatus; } else { fpQTDCtlSetup->dNextqTDPtr = (UINT32)(UINTN)fpQTDCtlStatus; } fpQTDCtlStatus->dNextqTDPtr = EHCI_TERMINATE; wRetCode = EhciExexuteAsyncSchedule(fpHCStruc, fpQHCtl); fpQHCtl->fpFirstqTD = 0; fpQHCtl->dNextqTDPtr = EHCI_TERMINATE; if (wLength) { EhciDmaUnmap(fpHCStruc, BufferMapping); wLength = wLength - (UINT16)((fpQTDCtlData->dToken & ~(QTD_DATA_TOGGLE)) >> 16); } // // Clear the stalled condition flag // gUsbData->bLastCommandStatus &= ~USB_CONTROL_STALLED; // // Check whether the QH stopped or timed out // if (wRetCode != USB_SUCCESS) { gUsbData->dLastCommandStatusExtended |= USB_TRNSFR_TIMEOUT; //(EIP84790+) wLength = 0; //(EIP71067) } if (fpQHCtl->bErrorStatus & QTD_HALTED) { // // Command stalled set the error bit appropriately // gUsbData->bLastCommandStatus |= USB_CONTROL_STALLED; wLength = 0; //(EIP71067) } //(EIP71067+)> EhciMemFree(fpHCStruc, fpQHCtl, GET_MEM_BLK_COUNT((1 * sizeof(EHCI_QH))+ (3 * sizeof(EHCI_QTD)))); //<(EIP71067+) EhciMemFree(fpHCStruc, fpRequest, GET_MEM_BLK_COUNT(sizeof(DEV_REQ))); return wLength; } // //--------------------------------------------------------------------------- // // Procedure: EHCI_BulkTransfer // // Description: This function executes a bulk transaction on the USB. The // transfer may be either DATA_IN or DATA_OUT packets containing // data sent from the host to the device or vice-versa. This // function wil not return until the request either completes // successfully or completes with error (due to time out, etc.) // NOTE: Make sure that amount of bytes to transfer should not // exceed MAX_EHCI_DATA_SIZE // // Parameters: pHCStruc Pointer to HCStruc of the host controller // pDevInfo DeviceInfo structure (if available else 0) // bXferDir Transfer direction // Bit 7: Data direction // 0 Host sending data to device // 1 Device sending data to host // Bit 6-0 : Reserved // fpBuffer Buffer containing data to be sent to the // device or buffer to be used to receive data // value in Segment:Offset format // dwLength dwLength request parameter, number of bytes // of data to be transferred in or out // of the host controller // // Output: Amount of data transferred // //--------------------------------------------------------------------------- // UINT32 EHCI_BulkTransfer( HC_STRUC *fpHCStruc, DEV_INFO *fpDevInfo, UINT8 bXferDir, UINT8 *fpBuffer, UINT32 dwLength) { UINT16 wMaxPkt; UINT8 bEndp, bDatToggle; EHCI_QH *fpQHBulk; EHCI_QTD *fpQTDBulkData; UINT32 dTmp, dTmp1; UINT16 Status; UINT32 dBytesToTransfer, dBytesRemaining; UINT32 dBytesTransferred; UINT8 *BufPhyAddr = NULL; VOID *BufferMapping = NULL; UINT8 *TempBuffer = NULL; EFI_STATUS EfiStatus = EFI_SUCCESS; EfiStatus = UsbHcStrucValidation(fpHCStruc); if (EFI_ERROR(EfiStatus)) { return 0; } EfiStatus = UsbDevInfoValidation(fpDevInfo); if (EFI_ERROR(EfiStatus)) { return 0; } #if USB_RUNTIME_DRIVER_IN_SMM if (gCheckUsbApiParameter) { EfiStatus = AmiValidateMemoryBuffer((VOID*)fpBuffer, dwLength); if (EFI_ERROR(EfiStatus)) { USB_DEBUG(3, "Ehci BulkTransfer Invalid Pointer, Buffer is in SMRAM.\n"); return 0; } gCheckUsbApiParameter = FALSE; } #endif if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { return 0; } if (EhciIsHalted(fpHCStruc)) { return 0; } if (!VALID_DEVINFO(fpDevInfo)) { return 0; } // Realtek 8111EP EHCI controller workaround // The controller doesn't work if the buffer address isn't DWORD alignment // (current offset of qTD). Provide the workaround to locate DWORD alignment buffer. if ((fpHCStruc->Vid == 0x10EC) && (fpHCStruc->Did == 0x816D)) { if ((UINTN)fpBuffer & (BIT0 | BIT1)) { if (dwLength < HIGHSPEED_MAX_BULK_DATA_SIZE) { TempBuffer = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT((UINT16)dwLength)); } else { TempBuffer = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT(HIGHSPEED_MAX_BULK_DATA_SIZE)); } } } //clear HW source of error gUsbData->dLastCommandStatusExtended = 0; dBytesRemaining = dwLength; dBytesTransferred = 0; // // Get Bulk IN/OUT enpoint number, data sync value & max packet size // if (bXferDir & BIT7) { wMaxPkt = fpDevInfo->wBulkInMaxPkt; bEndp = fpDevInfo->bBulkInEndpoint; } else { wMaxPkt = fpDevInfo->wBulkOutMaxPkt; bEndp = fpDevInfo->bBulkOutEndpoint; } if( wMaxPkt == 0){ return 0; } //(EIP71067+)> fpQHBulk = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT((1 * sizeof(EHCI_QH))+ (1 * sizeof(EHCI_QTD)))); //(EIP83295+)> if(!fpQHBulk) { return 0; } //<(EIP83295+) fpQTDBulkData = (EHCI_QTD*)((UINTN)fpQHBulk + sizeof(EHCI_QH)); //(EIP81030) //<(EIP71067+) EhciDmaMap(fpHCStruc, bXferDir, fpBuffer, dwLength, &BufPhyAddr, &BufferMapping); while (dBytesRemaining) { dBytesToTransfer = (dBytesRemaining < HIGHSPEED_MAX_BULK_DATA_SIZE)? dBytesRemaining : HIGHSPEED_MAX_BULK_DATA_SIZE; // // Get data toggle value // bDatToggle = UsbGetDataToggle(fpDevInfo, bEndp | bXferDir); // // Set the QH's dNextqTDPtr field to bulk data qTD and dAltqTDPtr field to // EHCI_TERMINATE. Also set QH's link pointer to itself // //(EIP71067-)> //fpQHBulk = fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpQHBulk; //fpQTDBulkData = fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpqTDBulkData; //<(EIP71067-) // // Intialize the queue head // EHCIInitializeQueueHead(fpQHBulk); // // Set the first qTD pointer // fpQHBulk->fpFirstqTD = fpQTDBulkData; fpQHBulk->dNextqTDPtr = (UINT32)(UINTN)fpQTDBulkData; fpQHBulk->dLinkPointer = (UINT32)((UINTN)fpQHBulk | EHCI_QUEUE_HEAD); // // Device address & Endpoint // dTmp = (UINT32)(fpDevInfo->bDeviceAddress | (bEndp << 8)); // // Set max packet size // dTmp = dTmp | ((UINT32)wMaxPkt << 16); // // Set the data toggle control // // #if EHCI_ASYNC_BELL_SUPPORT // dTmp |= QH_USE_QTD_DT; // #else dTmp |= QH_USE_QTD_DT; // Do not set QH_HEAD_OF_LIST bit on VIA controller if (fpHCStruc->Vid != 0x1106) { dTmp |= QH_HEAD_OF_LIST; } // #endif // // Set the device speed // Reset the device speed bits // dTmp1 = (UINT32)fpDevInfo->bEndpointSpeed; // 00/01/10 for HI/LO/FULL // // Assume as a high speed device // dTmp |= QH_HIGH_SPEED; // 10b - High speed // // Check for high speed // if (dTmp1) { dTmp1 = (dTmp1 & 1) << 12; // Bit 12 = full/low speed flag dTmp &= ~(QH_ENDPOINT_SPEED); dTmp |= dTmp1; // // Set the hub address and port number // dTmp1 = (UINT32)EHCI_GetHiSpeedHubPortNumber(fpDevInfo); dTmp1 = (dTmp1 << 16); // Hispeed hub port number & device number fpQHBulk->dEndPntCap |= dTmp1; // Split complete Xaction } // // Update the endpoint characteristcs field with the data formed // fpQHBulk->dEndPntCharac = dTmp; // // Fill the bulk data qTD with relevant information // The token field will be set so // Direction PID = QTD_OUT_TOKEN/QTD_IN_TOKEN, // Size = size of the data, // Data Toggle = bDatToggle, // Error Count = QTD_THREE_ERRORS, // Status code = QTD_DO_OUT(if it is out) + QTD_ACTIVE // The buffer pointers field will point to the fpBuffer buffer // which was before initialized to contain a DeviceRequest struc. // The dNextqTDPtr field will point to the qTDControlSetup // The dAltNextqTDPtr field will be set to EHCI_TERMINATE // if (bXferDir & BIT7) { fpQTDBulkData->dToken = QTD_IN_TOKEN | QTD_IOC_BIT | QTD_THREE_ERRORS | QTD_ACTIVE; } else { fpQTDBulkData->dToken = QTD_OUT_TOKEN | QTD_IOC_BIT | QTD_THREE_ERRORS | QTD_DO_OUT | QTD_ACTIVE; if (TempBuffer != NULL) { MemCpy(BufPhyAddr, TempBuffer, dBytesToTransfer); } } // // Set the data toggle depending on the bDatToggle value // fpQTDBulkData->dToken |= (UINT32)bDatToggle << 31; // // Set length // fpQTDBulkData->dToken |= (dBytesToTransfer << 16); // // Update buffer pointers // if (TempBuffer != NULL) { EHCISetQTDBufferPointers(fpQTDBulkData, TempBuffer, dBytesToTransfer); } else { EHCISetQTDBufferPointers(fpQTDBulkData, BufPhyAddr, dBytesToTransfer); } // // Update next & alternate next qTD pointers // fpQTDBulkData->dNextqTDPtr = EHCI_TERMINATE; fpQTDBulkData->dAltNextqTDPtr = EHCI_TERMINATE; fpQHBulk->bActive = TRUE; // // Set bulk condition as not stalled // gUsbData->bLastCommandStatus &= ~(USB_BULK_STALLED + USB_BULK_TIMEDOUT); // // Now wait for bulk transaction to be complete // the EHCIProcessInterrupt will set its active flag to FALSE. // Now wait for the bulk transfer to complete // Status = EhciExexuteAsyncSchedule(fpHCStruc, fpQHBulk); fpQHBulk->fpFirstqTD = 0; fpQHBulk->dNextqTDPtr = EHCI_TERMINATE; if (Status != USB_SUCCESS) { // // Set time out status // gUsbData->bLastCommandStatus |= USB_BULK_TIMEDOUT; gUsbData->dLastCommandStatusExtended |= USB_TRNSFR_TIMEOUT; break; } if (fpQHBulk->bErrorStatus & QTD_HALTED) { // // Stall condition // gUsbData->bLastCommandStatus &= ~(USB_BULK_TIMEDOUT); gUsbData->bLastCommandStatus |= USB_BULK_STALLED; gUsbData->dLastCommandStatusExtended |= USB_TRSFR_STALLED; break; } // // Update the data toggle value into the mass info structure // bDatToggle = (UINT8)(((fpQHBulk->dToken & QH_DATA_TOGGLE) >> 31) & 1); UsbUpdateDataToggle(fpDevInfo, bEndp | bXferDir, bDatToggle); // // Get the size of data transferred // dTmp = (fpQTDBulkData->dToken & ~(QTD_DATA_TOGGLE)) >> 16; dTmp = (dTmp)? dBytesToTransfer-dTmp : dBytesToTransfer; if (!dTmp) { break; } if (TempBuffer != NULL) { if (bXferDir & BIT7) { MemCpy(TempBuffer, BufPhyAddr, dTmp); } } // // Adjust loop variables // dBytesRemaining = dBytesRemaining - dTmp; dBytesTransferred += dTmp; if (dTmp < dBytesToTransfer) { break; } BufPhyAddr += dTmp; } EhciDmaUnmap(fpHCStruc, BufferMapping); //(EIP71067+)> EhciMemFree(fpHCStruc, fpQHBulk, GET_MEM_BLK_COUNT( (1 * sizeof(EHCI_QH)) + (1 * sizeof(EHCI_QTD)))); //<(EIP71067+) if (TempBuffer != NULL) { if (dwLength < HIGHSPEED_MAX_BULK_DATA_SIZE) { EhciMemFree(fpHCStruc, TempBuffer, GET_MEM_BLK_COUNT((UINT16)dwLength)); } else { EhciMemFree(fpHCStruc, TempBuffer, GET_MEM_BLK_COUNT(HIGHSPEED_MAX_BULK_DATA_SIZE)); } } return dBytesTransferred; } // //--------------------------------------------------------------------------- // // Procedure: EHCI_InterruptTransfer // // Description: This function executes an interrupt transaction on the USB. // The data transfer direction is always DATA_IN. This // function wil not return until the request either completes // successfully or completes in error (due to time out, etc.) // // Parameters: fpHCStruc Pointer to HCStruc of the host controller // fpDevInfo DeviceInfo structure (if available else 0) // EndpointAddress The destination USB device endpoint to which the device request // is being sent. // MaxPktSize Indicates the maximum packet size the target endpoint is capable // of sending or receiving. // fpBuffer Buffer containing data to be sent to the // device or buffer to be used to receive data // wLength wLength request parameter, number of bytes // of data to be transferred in // // Output: Number of bytes transferred // // // Notes: DO NOT TOUCH THE LINK POINTER OF THE TDInterruptData. It is // statically allocated and linked with other items in the // 1ms schedule // //--------------------------------------------------------------------------- // UINT16 EHCI_InterruptTransfer ( HC_STRUC *fpHCStruc, DEV_INFO *fpDevInfo, UINT8 EndpointAddress, UINT16 MaxPktSize, UINT8 *fpBuffer, UINT16 wLength ) { UINT8 bDatToggle; EHCI_QH *fpQHInt; UINT32 dTmp, dTmp1; EHCI_QTD *fpqTDIntData; UINT32 Count; UINT32 Timeout; UINT32 BytesTransferred; UINT8 *BufPhyAddr = NULL; VOID *BufferMapping = NULL; EFI_STATUS EfiStatus = EFI_SUCCESS; EfiStatus = UsbHcStrucValidation(fpHCStruc); if (EFI_ERROR(EfiStatus)) { return 0; } EfiStatus = UsbDevInfoValidation(fpDevInfo); if (EFI_ERROR(EfiStatus)) { return 0; } #if USB_RUNTIME_DRIVER_IN_SMM if (gCheckUsbApiParameter) { EfiStatus = AmiValidateMemoryBuffer((VOID*)fpBuffer, wLength); if (EFI_ERROR(EfiStatus)) { USB_DEBUG(3, "Ehci InterruptTransfer Invalid Pointer, Buffer is in SMRAM.\n"); return 0; } gCheckUsbApiParameter = FALSE; } #endif if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { return 0; } if (EhciIsHalted(fpHCStruc)) { return 0; } gUsbData->dLastCommandStatusExtended = 0; bDatToggle = UsbGetDataToggle(fpDevInfo, EndpointAddress); // // Get the QHInterrupt pointer // fpQHInt = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT((1 * sizeof(EHCI_QH))+ (1 * sizeof(EHCI_QTD)))); if (fpQHInt == NULL) { return 0; } fpqTDIntData = (EHCI_QTD*)((UINTN)fpQHInt + sizeof(EHCI_QH)); // // Intialize the queue head // EHCIInitializeQueueHead(fpQHInt); // // Set the first qTD pointer // fpQHInt->fpFirstqTD = fpqTDIntData; fpQHInt->dNextqTDPtr = (UINT32)(UINTN)fpqTDIntData; fpQHInt->dLinkPointer = EHCI_TERMINATE; // // Get Device address & Endpoint // dTmp = (UINT32)fpDevInfo->bDeviceAddress; dTmp |= (UINT32)(EndpointAddress & 0xF) << 8; // // Set max packet size // dTmp |= (UINT32)(MaxPktSize) << 16; // // Set the device speed, reset the device speed bits // dTmp1 = (UINT32)fpDevInfo->bEndpointSpeed; // 00/01/10 for HI/LO/FULL // // Assume as a high speed device // dTmp |= QH_HIGH_SPEED; // 10b - High speed // // Check for high speed // if (dTmp1) { dTmp1 = (dTmp1 & 1) << 12; // Bit 12 = full/low speed flag dTmp &= ~(QH_ENDPOINT_SPEED); dTmp |= dTmp1; // // Set the hub address and port number // dTmp1 = (UINT32)EHCI_GetHiSpeedHubPortNumber(fpDevInfo); dTmp1 = (dTmp1 << 16) | BIT10 | BIT11 | BIT12; fpQHInt->dEndPntCap |= dTmp1; // Split complete Xaction } // // Update the endpoint characteristcs field with the data formed // fpQHInt->dEndPntCharac = dTmp; fpQHInt->dEndPntCap |= (BIT0 | QH_ONE_XFER); // Interrupt schedule mask fpQHInt->Interval = EhciTranslateInterval(fpDevInfo->bEndpointSpeed, fpDevInfo->bPollInterval); // // Set the data toggle depending on the bDatToggle value // fpQHInt->dToken |= (UINT32)bDatToggle << 31; // // Fill the interrupt data qTD with relevant information // The token field will be set so // Direction PID = QTD_IN_TOKEN, // Size = size of the data, // Data Toggle = bDatToggle, // Error Count = QTD_THREE_ERRORS, // Status code = QTD_ACTIVE // The buffer pointers field will point to the EDX // which was before initialized to contain a DeviceRequest struc. // The dNextqTDPtr field will point to the qTDControlSetup // The dAltNextqTDPtr field will be set to EHCI_TERMINATE // fpqTDIntData->dToken = QTD_IOC_BIT | QTD_THREE_ERRORS | QTD_ACTIVE; if (EndpointAddress & BIT7) { fpqTDIntData->dToken |= QTD_IN_TOKEN; } else { fpqTDIntData->dToken |= QTD_OUT_TOKEN; } // // Set length // fpqTDIntData->dToken |= (UINT32)wLength << 16; EhciDmaMap(fpHCStruc, EndpointAddress & BIT7, fpBuffer, wLength, &BufPhyAddr, &BufferMapping); // // Update buffer pointers // EHCISetQTDBufferPointers(fpqTDIntData, BufPhyAddr, (UINT32)wLength); // // Update next & alternate next qTD pointers // fpqTDIntData->dNextqTDPtr = EHCI_TERMINATE; fpqTDIntData->dAltNextqTDPtr = EHCI_TERMINATE; // // Schedule the QHInterrupt to 1msec schedule // EhciAddPeriodicQh(fpHCStruc,fpQHInt); // Set the QH as active fpQHInt->bActive = TRUE; // // Now wait for interrupt transaction to be complete; // the EHCIProcessInterrupt will set its active flag to FALSE. // Timeout = gUsbData->wTimeOutValue * 100; // makes it number of 10 macrosecond units for (Count = 0; Timeout == 0 || Count < Timeout; Count++) { if (!(fpqTDIntData->dToken & QTD_ACTIVE)) { break; } FixedDelay(10); // 60 microsec } //Status = EHCIWaitForTransferComplete(fpHCStruc, fpQHInt, fpDevInfo); // Remove the QH from periodic schedule EhciRemovePeriodicQh(fpHCStruc,(EHCI_QH*)fpQHInt); // // Check whether the QH stopped or timed out // BytesTransferred = 0; if (fpqTDIntData->dToken & QTD_ACTIVE) { USB_DEBUG (DEBUG_LEVEL_3, "EHCI Time-Out\n"); gUsbData->dLastCommandStatusExtended |= USB_TRNSFR_TIMEOUT; } else if (fpqTDIntData->dToken & QTD_HALTED) { gUsbData->dLastCommandStatusExtended |= USB_TRSFR_STALLED; } else { BytesTransferred = (UINT16)(wLength - ((fpqTDIntData->dToken & ~(QTD_DATA_TOGGLE)) >> 16)); // // Update the data toggle value into the mass info structure // bDatToggle = (UINT8)(((fpQHInt->dToken & QH_DATA_TOGGLE) >> 31) & 1); UsbUpdateDataToggle(fpDevInfo, EndpointAddress, bDatToggle); } EhciDmaUnmap(fpHCStruc, BufferMapping); // Free the allocated QH and qTD EhciMemFree(fpHCStruc, fpQHInt, GET_MEM_BLK_COUNT((1 * sizeof(EHCI_QH)) + (1 * sizeof(EHCI_QTD)))); // Service all interrupts EHCI_ProcessInterrupt(fpHCStruc); return (UINT16)BytesTransferred; } // //---------------------------------------------------------------------------- // // Procedure: EHCI_DeactivatePolling // // Description: This function de-activates the polling QH for the requested // device. The device may be a USB keyboard or USB hub // // Input: fpHCStruc - Pointer to the HC structure // fpDevInfo - Pointer to the device information structure // // Output: USB_ERROR on error, USB_SUCCESS on success // //---------------------------------------------------------------------------- // UINT8 EHCI_DeactivatePolling( HC_STRUC* fpHCStruc, DEV_INFO* fpDevInfo) { UINT8 *fpPollED; EFI_STATUS EfiStatus; EfiStatus = UsbHcStrucValidation(fpHCStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } EfiStatus = UsbDevInfoValidation(fpDevInfo); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { return USB_ERROR; } if (EhciIsHalted(fpHCStruc)) { return USB_ERROR; } // // Get a pointer to the device's QH from the poll QH pointer and remove // the polling ED from the schedule // fpPollED = fpDevInfo->fpPollEDPtr; if(!fpPollED) return USB_ERROR; ((EHCI_QH*)fpPollED)->bActive = FALSE; EhciRemovePeriodicQh(fpHCStruc,(EHCI_QH*)fpPollED); UsbUpdateDataToggle(fpDevInfo, fpDevInfo->IntInEndpoint, (UINT8)((((EHCI_QH*)fpPollED)->dToken & QH_DATA_TOGGLE) >> 31)); EhciMemFree(fpHCStruc, fpPollED, GET_MEM_BLK_COUNT(sizeof(EHCI_QH)+sizeof(EHCI_QTD))); fpDevInfo->fpPollEDPtr = NULL; fpDevInfo->fpPollTDPtr = NULL; if(fpDevInfo->fpPollDataBuffer) { EhciMemFree(fpHCStruc, fpDevInfo->fpPollDataBuffer, GET_MEM_BLK_COUNT(fpDevInfo->PollingLength)); fpDevInfo->fpPollDataBuffer = 0; } return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCI_ActivatePolling // // Description: This function activates the polling QH for the requested // device. The device may be a USB keyboard or USB hub // // Input: fpHCStruc - Pointer to the HC structure // fpDevInfo - Pointer to the device information structure // // Output: USB_ERROR on error, USB_SUCCESS on success // // Notes: For the keyboard device this routine allocates TDRepeat // also, if it is not already allocated. This routine allocate // a polling TD and schedule it to 8ms schedule for keyboards // and to 1024ms schedule for hubs. // //---------------------------------------------------------------------------- // UINT8 EHCI_ActivatePolling( HC_STRUC* fpHCStruc, DEV_INFO* fpDevInfo) { EHCI_QH *fpPollED; EHCI_QTD *fpPollTD; UINT32 dTmp, dTmp1; EFI_STATUS EfiStatus; EfiStatus = UsbHcStrucValidation(fpHCStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } EfiStatus = UsbDevInfoValidation(fpDevInfo); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { return USB_ERROR; } USB_DEBUG (DEBUG_LEVEL_3, "EHCI_AP dev type %d\n", fpDevInfo->bDeviceType); if (EhciIsHalted(fpHCStruc)) { return USB_ERROR; } // // Allocate a QH/qTD for polling QH & qTD // fpPollED = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT( sizeof(EHCI_QH)+sizeof(EHCI_QTD))); if (!fpPollED) { return USB_ERROR; // Memory allocation error } // // Save the pointers in DeviceInfo structure // fpDevInfo->fpPollEDPtr = (UINT8*)fpPollED; fpPollTD = (EHCI_QTD*)((UINTN)fpPollED + sizeof(EHCI_QH)); fpDevInfo->fpPollTDPtr = (UINT8*)fpPollTD; // // Setup the polling QH // Set the QH's dNextqTDPtr field to polling qTD and dAltqTDPtr field to // EHCI_TERMINATE // fpPollED->fpFirstqTD = fpPollTD; fpPollED->dNextqTDPtr = (UINT32)(UINTN)fpPollTD; // // Intialize the queue head // fpPollED->dAltNextqTDPtr = EHCI_TERMINATE; fpPollED->dLinkPointer = EHCI_TERMINATE; // // Set the device info pointer in the QH // fpPollED->fpDevInfoPtr = (UINT8*)fpDevInfo; // // Get Device address & Endpoint // dTmp = ((UINT32)fpDevInfo->bDeviceAddress) | ((UINT32)(fpDevInfo->IntInEndpoint & 0xF) << 8); dTmp |= ((UINT32)fpDevInfo->IntInMaxPkt) << 16; // Set max packet size //(EIP54782) dTmp1 = (UINT32)fpDevInfo->bEndpointSpeed; // 00/01/10 for HI/LO/FULL // // Assume as a high speed device // dTmp |= QH_HIGH_SPEED; // 10b - High speed // // Check for high speed // if (dTmp1) { dTmp1 = (dTmp1 & 1) << 12; // Bit 12 = full/low speed flag dTmp &= ~(QH_ENDPOINT_SPEED); dTmp |= dTmp1; // // Set the hub address and port number // dTmp1 = (UINT32)EHCI_GetHiSpeedHubPortNumber(fpDevInfo); dTmp1 = (dTmp1 << 16) | (BIT10 + BIT11 + BIT12); // Split complete Xaction fpPollED->dEndPntCap |= dTmp1; } // // Update the endpoint characteristcs field with the data formed // fpPollED->dEndPntCharac = dTmp; // // Set a bit in interrupt mask // fpPollED->dEndPntCap |= (BIT0 + QH_ONE_XFER); fpPollED->Interval = EhciTranslateInterval(fpDevInfo->bEndpointSpeed, fpDevInfo->bPollInterval); // // Set the data toggle // fpPollED->dToken |= (UINT32)(UsbGetDataToggle(fpDevInfo, fpDevInfo->IntInEndpoint) << 31); // // Fill the polling qTD with relevant information // The token field will be set so // Direction PID = QTD_IN_TOKEN, // Size = size of the data, // Data Toggle = QTD_DATA0_TOGGLE, // Error Count = QTD_THREE_ERRORS, // Status code = QTD_ACTIVE // The buffer pointers field will point to the fpBuffer buffer // which was before initialized to contain a DeviceRequest struc. // The dNextqTDPtr field will point to the qTDControlSetup // The dAltNextqTDPtr field will be set to EHCI_TERMINATE // fpPollTD->dToken = QTD_IN_TOKEN | QTD_IOC_BIT | QTD_THREE_ERRORS | QTD_ACTIVE; // // Set length // fpPollTD->dToken |= (UINT32)fpDevInfo->PollingLength << 16; fpDevInfo->fpPollDataBuffer = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT(fpDevInfo->PollingLength)); ASSERT(fpDevInfo->fpPollDataBuffer); // // Update buffer pointers // EHCISetQTDBufferPointers(fpPollTD, fpDevInfo->fpPollDataBuffer, fpDevInfo->PollingLength); // // Update next & alternate next qTD pointers // fpPollTD->dNextqTDPtr = EHCI_TERMINATE; fpPollTD->dAltNextqTDPtr = EHCI_TERMINATE; EhciAddPeriodicQh(fpHCStruc,fpPollED); fpPollED->bCallBackIndex = USB_InstallCallBackFunction(EhciPollingTDCallback); fpPollED->bActive = TRUE; return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCIDisableKeyRepeat // // Description: This function disables the keyboard repeat rate logic by // enabling the repeat TD // // Input: fpHCStruc - Pointer to the HCStruc structure // // Output: None // //---------------------------------------------------------------------------- // UINT8 EHCI_DisableKeyRepeat ( HC_STRUC *HcStruc ) { EHCI_DESC_PTRS *DescPtr; EHCI_QH *RepeatQh; EFI_STATUS EfiStatus; UINT8 *MemBlockEnd = gUsbData->fpMemBlockStart + (gUsbData->MemPages << 12); EfiStatus = UsbHcStrucValidation(HcStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { return USB_ERROR; } if (EhciIsHalted(HcStruc)) { return USB_ERROR; } DescPtr = HcStruc->stDescPtrs.fpEHCIDescPtrs; if (DescPtr == NULL) { return USB_ERROR; } #if USB_RUNTIME_DRIVER_IN_SMM if (((UINT8*)DescPtr < gUsbData->fpMemBlockStart) || ((UINT8*)(DescPtr + sizeof(EHCI_DESC_PTRS)) > MemBlockEnd)) { return USB_ERROR; } #endif RepeatQh = DescPtr->fpQHRepeat; if (RepeatQh == NULL) { return USB_ERROR; } #if USB_RUNTIME_DRIVER_IN_SMM if (((UINT8*)RepeatQh < gUsbData->fpMemBlockStart) || ((UINT8*)(RepeatQh + sizeof(EHCI_QH)) > MemBlockEnd)) { return USB_ERROR; } #endif if (RepeatQh) { RepeatQh->dTokenReload = ((UINT32)8 << 16) | QTD_IN_TOKEN | QTD_ONE_ERROR; RepeatQh->bActive = FALSE; } return USB_SUCCESS; } // //--------------------------------------------------------------------------- // // Procedure: EHCI_EnableKeyRepeat // // Description: This function disables the keyboard repeat rate logic by // enabling the repeat TD // // Parameters: fpHCStruc Pointer to the HCStruc structure // // Output: USB_SUCCESS or USB_ERROR // //--------------------------------------------------------------------------- // UINT8 EHCI_EnableKeyRepeat ( HC_STRUC* HcStruc ) { EHCI_DESC_PTRS *DescPtr; EHCI_QH *RepeatQh; EHCI_QTD *RepeatQtd; EFI_STATUS EfiStatus; UINT8 *MemBlockEnd = gUsbData->fpMemBlockStart + (gUsbData->MemPages << 12); EfiStatus = UsbHcStrucValidation(HcStruc); if (EFI_ERROR(EfiStatus)) { return USB_ERROR; } if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { return USB_ERROR; } if (EhciIsHalted(HcStruc)) { return USB_ERROR; } DescPtr = HcStruc->stDescPtrs.fpEHCIDescPtrs; if (DescPtr == NULL) { return USB_ERROR; } #if USB_RUNTIME_DRIVER_IN_SMM if (((UINT8*)DescPtr < gUsbData->fpMemBlockStart) || ((UINT8*)(DescPtr + sizeof(EHCI_DESC_PTRS)) > MemBlockEnd)) { return USB_ERROR; } #endif RepeatQh = DescPtr->fpQHRepeat; if (RepeatQh == NULL) { return USB_ERROR; } #if USB_RUNTIME_DRIVER_IN_SMM if (((UINT8*)RepeatQh < gUsbData->fpMemBlockStart) || (((UINT8*)RepeatQh + sizeof(EHCI_QH)) > MemBlockEnd)) { return USB_ERROR; } #endif if ((RepeatQh->dTokenReload & QTD_ACTIVE) == 0) { RepeatQtd = DescPtr->fpqTDRepeat; if (RepeatQtd == NULL) { return USB_ERROR; } #if USB_RUNTIME_DRIVER_IN_SMM if (((UINT8*)RepeatQtd < gUsbData->fpMemBlockStart) || (((UINT8*)RepeatQtd + sizeof(EHCI_QTD)) > MemBlockEnd)) { return USB_ERROR; } #endif RepeatQh->dTokenReload = ((UINT32)8 << 16) | QTD_IN_TOKEN | QTD_ONE_ERROR | QTD_IOC_BIT | QTD_ACTIVE; // Update buffer pointers EHCISetQTDBufferPointers(RepeatQtd, &RepeatQh->aDataBuffer[0], 8); // Re-init the QH pointers RepeatQh->dCurqTDPtr = 0; RepeatQh->dAltNextqTDPtr = EHCI_TERMINATE; RepeatQh->dNextqTDPtr = (UINT32)(UINTN)RepeatQtd; // // Restart the qTD // RepeatQh->dToken = 0; RepeatQtd->dToken = RepeatQh->dTokenReload; RepeatQh->bActive = TRUE; } return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCIResetHC // // Description: This function resets the EHCI controller // // Input: Pointer to the HCStruc structure // // Output: USB_SUCCESS HC successfully reset // USB_ERROR Error, error log is updated // //---------------------------------------------------------------------------- // UINT8 EHCIResetHC(HC_STRUC* fpHCStruc) { UINT16 count; if (fpHCStruc->DebugPort) return USB_SUCCESS; // // Check HC is halted: attempting to reset an actively running HC will // result in undefined behavior. // if (EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_HCHALTED) { // // Issue reset // EhciSetOpReg(fpHCStruc, EHCI_USBCMD, EHCI_HCRESET); // // EHCI_HCRESET bit is set to zero by the Host Controller when the reset // process is complete. // for (count = 0; count < 500; count++) { if (!(EhciReadOpReg(fpHCStruc, EHCI_USBCMD) & EHCI_HCRESET)) { return USB_SUCCESS; } FixedDelay(100); // 100 us delay } } // // Error - HC reset failed // USBLogError(USB_ERR_HC_RESET_FAILED); return USB_ERROR; } // //---------------------------------------------------------------------------- // // Procedure: EHCIInitializesPeriodicSchedule // // Description: This function initializes the periodic schedules for the // EHCI host controller // // Input: fpHCStruc - HCStruc for the controller // dMemAddr - Membase address // // Output: USB_ERROR On error // USB_SUCCESS On success // // Notes: This routine creates 8ms and 32ms schedules // //---------------------------------------------------------------------------- // UINT8 EHCIInitializePeriodicSchedule( HC_STRUC* fpHCStruc, UINT32 dMemBase) { UINT8 *fpPtr; EHCI_DESC_PTRS *fpDescPtr; // // Allocate descriptor structure and fill it in HCStruc // fpDescPtr = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT_STRUC(EHCI_DESC_PTRS)); if (fpDescPtr == NULL) { USB_DEBUG(DEBUG_LEVEL_4, "EHCI Descriptor struc alloc failed. %d \n", GET_MEM_BLK_COUNT_STRUC(EHCI_DESC_PTRS)); return USB_ERROR; } // // Save the value in the HC struc // fpHCStruc->stDescPtrs.fpEHCIDescPtrs = fpDescPtr; // Allocate QH/qTD for PeriodicQh fpPtr = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT((1 * sizeof(EHCI_QH))+ (0 * sizeof(EHCI_QTD)))); if (fpPtr == NULL) { USB_DEBUG(DEBUG_LEVEL_4, "Schedule of EHCI QH alloc failed.\n"); return USB_ERROR; } // Save the 1 QH in appropriate location fpDescPtr->PeriodicQh = (EHCI_QH*)fpPtr; EHCIInitializeQueueHead(fpDescPtr->PeriodicQh); fpDescPtr->PeriodicQh->dNextqTDPtr = EHCI_TERMINATE; fpDescPtr->PeriodicQh->dAltNextqTDPtr = EHCI_TERMINATE; fpDescPtr->PeriodicQh->dEndPntCharac = ((UINT32)0x40 << 16) + QH_HIGH_SPEED; fpDescPtr->PeriodicQh->dLinkPointer = EHCI_TERMINATE; fpDescPtr->PeriodicQh->dToken = QTD_HALTED; fpDescPtr->PeriodicQh->Interval = 1; EhciAddPeriodicQh(fpHCStruc, fpDescPtr->PeriodicQh); return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCIInitializeQueueHead // // Description: This function initializes the queue head with default values // // Input: fpQH Pointer to queue head // // Output: None // //---------------------------------------------------------------------------- // VOID EHCIInitializeQueueHead(EHCI_QH *fpQH) { fpQH->dNextqTDPtr = 1; fpQH->dAltNextqTDPtr = 1; fpQH->dCurqTDPtr = 0; fpQH->dEndPntCap = QH_ONE_XFER; fpQH->dToken = 0; fpQH->dEndPntCharac = 0; fpQH->dBufferPtr0 = 0; fpQH->dBufferPtr1 = 0; fpQH->dBufferPtr2 = 0; fpQH->dBufferPtr3 = 0; fpQH->dBufferPtr4 = 0; fpQH->bErrorStatus = 0; } // //---------------------------------------------------------------------------- // // Procedure: EHCIStartPeriodicSchedule // // Description: This function starts the periodic schedule for the // EHCI host controller // // Input: Pointer to HcStruc // // Output: USB_ERROR On error // USB_SUCCESS On success // //---------------------------------------------------------------------------- // UINT8 EHCIStartPeriodicSchedule( HC_STRUC* HcStruc ) { UINT16 i; // // Start periodic schedule // EhciSetOpReg(HcStruc, EHCI_USBCMD, EHCI_PER_SCHED_ENABLE); // // Make sure the HC started the schedules // for (i = 0; i < 1000; i++) { if (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_PER_SCHED_STATUS) { break; } FixedDelay(10); } if (!(EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_PER_SCHED_STATUS)) { return USB_ERROR; } return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCIStopPeriodicSchedule // // Description: This function stops the periodic schedule for the // EHCI USB host controller // // Input: HcStruc for the controller // // Output: USB_ERROR On error // USB_SUCCESS On success // //---------------------------------------------------------------------------- // UINT8 EHCIStopPeriodicSchedule( HC_STRUC* HcStruc ) { UINT16 i; // // Stop periodic schedule // EhciClearOpReg(HcStruc, EHCI_USBCMD, EHCI_PER_SCHED_ENABLE); // // Make sure the HC stopped the schedules // for (i = 0; i < 1000; i++) { if (!(EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_PER_SCHED_STATUS)) { break; } FixedDelay(10); } if (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_PER_SCHED_STATUS) { return USB_ERROR; } return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCIStartAsyncSchedule // // Description: This function starts the asynchronous schedule // // Input: Pointer to HcStruc // // Output: USB_ERROR On error // USB_SUCCESS On success // //---------------------------------------------------------------------------- // UINT8 EHCIStartAsyncSchedule( HC_STRUC* HcStruc ) { UINT16 i; // // Start the Async schedule // EhciSetOpReg(HcStruc, EHCI_USBCMD, EHCI_ASYNC_SCHED_ENABLE); // // Make sure the HC started the async. execution // for (i = 0; i < 1000; i++) { if (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_ASYNC_SCHED_STATUS) { break; } FixedDelay(10); } if (!(EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_ASYNC_SCHED_STATUS)) { // // Stop the host controller (Reset bit 0) // EhciClearOpReg(HcStruc, EHCI_USBCMD, EHCI_RUNSTOP); // The Host Controller must halt within 16 micro-frames after // software clears the Run bit. for (i = 0; i < 16; i++) { if (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_HCHALTED) { break; } FixedDelay(125); // 125 us delay } return USB_ERROR; } return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCIStopAsyncSchedule // // Description: This function stops the asynchronous transfer and sets the // asynchronous pointer to null // // Input: Pointer to HcStruc // // Output: USB_ERROR On error // USB_SUCCESS On success // //---------------------------------------------------------------------------- // UINT8 EHCIStopAsyncSchedule( HC_STRUC* HcStruc ) { UINT16 i; if (!(EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_ASYNC_SCHED_STATUS)) { return USB_SUCCESS; } // // Stop periodic schedule // EhciClearOpReg(HcStruc, EHCI_USBCMD, EHCI_ASYNC_SCHED_ENABLE); // // Make sure the HC stopped the async. execution // for (i = 0; i < 1000; i++) { if (!(EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_ASYNC_SCHED_STATUS)) { break; } FixedDelay(10); } if (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_ASYNC_SCHED_STATUS) { // // Stop the host controller (Reset bit 0) // EhciClearOpReg(HcStruc, EHCI_USBCMD, EHCI_RUNSTOP); // The Host Controller must halt within 16 micro-frames after // software clears the Run bit. for (i = 0; i < 16; i++) { if (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_HCHALTED) { break; } FixedDelay(125); // 125 us delay } return USB_ERROR; } return USB_SUCCESS; } /* #if EHCI_ASYNC_BELL_SUPPORT // //---------------------------------------------------------------------------- // // Procedure: EHCIRemoveQHFromAsyncList // // Description: This function stops the asynchronous transfer and sets the // asynchronous pointer to null // // Input: Pointer to HCStruc // Pointer to the Queue head that has to be removed // from the asynchronous schedule // // Output: None // //---------------------------------------------------------------------------- // VOID EHCIRemoveQHFromAsyncList( HC_STRUC *fpHCStruc, EHCI_QH *fpQH ) { // // Stop the Async transfer // EHCIStopAsyncSchedule(fpHCStruc); // // Remove the queue head from the Async list // gUsbData->fpQHAsyncXfer->dLinkPointer = fpQH->dLinkPointer; // // Pointer is advanced. The queue head is totally removed from the list! // } #endif */ // //---------------------------------------------------------------------------- // // Procedure: EHCIAddPeriodicQH // // Description: This function adds a QH to the frame list // // Input: Pointer to the QH to be added // Absolute pointer to the frame list // // Output: None // //---------------------------------------------------------------------------- // UINT8 EhciAddPeriodicQh( HC_STRUC *HcStruc, EHCI_QH *NewQh ) { UINT16 Index; UINT32 *PrevPtr; UINT32 LinkPtr; EHCI_QH *Qh; EFI_STATUS Status = EFI_SUCCESS; if (NewQh == NULL || NewQh->Interval == 0) { return USB_ERROR; } for (Index = HcStruc->SplitPeriodicIndex; Index < HcStruc->wAsyncListSize; Index += NewQh->Interval) { PrevPtr = &HcStruc->fpFrameList[Index]; LinkPtr = *PrevPtr; while (!(LinkPtr & EHCI_TERMINATE)){ Qh = (EHCI_QH*)(LinkPtr & EHCI_POINTER_MASK); #if USB_RUNTIME_DRIVER_IN_SMM Status = AmiValidateMemoryBuffer((VOID*)Qh, sizeof(EHCI_QH)); if (EFI_ERROR(Status)) { return USB_ERROR; } #endif if (Qh->Interval <= NewQh->Interval) { break; } PrevPtr = &Qh->dLinkPointer; LinkPtr = *PrevPtr; } if (!(LinkPtr & EHCI_TERMINATE) && (Qh == NewQh)) { continue; } #if USB_RUNTIME_DRIVER_IN_SMM Status = AmiValidateMemoryBuffer((VOID*)PrevPtr, sizeof(UINT32)); if (EFI_ERROR(Status)) { return USB_ERROR; } #endif NewQh->dLinkPointer = *PrevPtr; *PrevPtr = (UINT32)((UINTN)NewQh | EHCI_QUEUE_HEAD); } if (HcStruc->SplitPeriodicIndex == MAX_SPLIT_PERIODIC_NUMBER) { HcStruc->SplitPeriodicIndex = 0; } else { HcStruc->SplitPeriodicIndex++; } return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EhciRemovePeriodicQh // // Description: This function removes a QH from the frame list // // Input: Pointer to the QH to be added // Absolute pointer to the frame list // // Output: None // //---------------------------------------------------------------------------- // UINT8 EhciRemovePeriodicQh ( HC_STRUC *HcStruc, EHCI_QH *RetiredQh ) { UINT16 Index; UINT32 *PrevPtr; UINT32 LinkPtr; EHCI_QH *Qh; EFI_STATUS Status = EFI_SUCCESS; if (RetiredQh == NULL || RetiredQh->Interval == 0) { return USB_ERROR; } EHCIStopPeriodicSchedule(HcStruc); for (Index = 0; Index < HcStruc->wAsyncListSize; Index++) { PrevPtr = &HcStruc->fpFrameList[Index]; LinkPtr = *PrevPtr; while (!(LinkPtr & EHCI_TERMINATE)){ Qh = (EHCI_QH*)(LinkPtr & EHCI_POINTER_MASK); #if USB_RUNTIME_DRIVER_IN_SMM Status = AmiValidateMemoryBuffer((VOID*)Qh, sizeof(EHCI_QH)); if (EFI_ERROR(Status)) { return USB_ERROR; } #endif if (Qh == RetiredQh) { break; } PrevPtr = &Qh->dLinkPointer; LinkPtr = *PrevPtr; } if (LinkPtr & EHCI_TERMINATE) { continue; } #if USB_RUNTIME_DRIVER_IN_SMM Status = AmiValidateMemoryBuffer((VOID*)PrevPtr, sizeof(UINT32)); if (EFI_ERROR(Status)) { return USB_ERROR; } #endif *PrevPtr = RetiredQh->dLinkPointer; } RetiredQh->dLinkPointer = EHCI_TERMINATE; EHCIStartPeriodicSchedule(HcStruc); return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCISetQTDBufferPointers // // Description: This function will set the 5 buffer pointer in the qTD // appropriately depending upon the input size // // Input: fpQtd - Pointer to the qTD // fpBuf - 32bit absolute buffer pointer // wSize - Amount of data to be transferred // // Output: None // //---------------------------------------------------------------------------- // VOID EHCISetQTDBufferPointers( EHCI_QTD *fpQtd, UINT8 *fpBuf, UINT32 dSize ) { UINT16 wBufSize; UINT8 *fpBuffer = fpBuf; UINT32 *fpBufferPtr; UINT16 w4KRemainder; // // Fill the buffer pointers with 0s // fpQtd->dBufferPtr0 = 0; fpQtd->dBufferPtr1 = 0; fpQtd->dBufferPtr2 = 0; fpQtd->dBufferPtr3 = 0; fpQtd->dBufferPtr4 = 0; fpQtd->dAltNextqTDPtr = 1; // // If size to transfer is 0 skip updating pointers // if (!dSize) { return; } // // Make sure the amount of data to be xferred is 16K or less // wBufSize = (UINT16)((dSize > MAX_EHCI_DATA_SIZE) ? MAX_EHCI_DATA_SIZE : dSize); fpBufferPtr = &fpQtd->dBufferPtr0; for (;;) { *fpBufferPtr = (UINT32)(UINTN)fpBuffer; // // Calculate the number of bytes that can be transferred using current // buffer pointer // w4KRemainder = (UINT16)((((UINT32)((UINTN)fpBuffer+0x1000)) & 0xFFFFF000) - (UINT32)(UINTN)fpBuffer); // // Check whether all the bytes can be accomadated in the current buffer // if (w4KRemainder >= wBufSize) { break; // Yes. Current Buffer is sufficient for the rest of data } // // We have more data to transfer - adjust data and store it in the next pointer // wBufSize = (UINT16)(wBufSize - w4KRemainder); // Amount of data remaining fpBuffer = fpBuffer + w4KRemainder; // Adjust buffer (4K bound) fpBufferPtr++; // Next buffer pointer } } // //---------------------------------------------------------------------------- // // Procedure: EHCIWaitForTransferComplete // // Description: This function waits until the requested QH completes or // the transaction time-out // // Input: fpHCStruc - Pointer to HCStruc of the host controller // fpQH - Pointer to the QH which has to be completed // // Output: USB_ERROR On error // USB_SUCCESS On success // //---------------------------------------------------------------------------- // UINT16 EHCIWaitForTransferComplete( HC_STRUC *fpHCStruc, EHCI_QH *fpQH, DEV_INFO* fpDevInfo ) { UINT32 dCount ; UINT32 countLimit = gUsbData->wTimeOutValue * 100; // makes it number of macrosecond units UINT16 Status = USB_ERROR; // // Check status change loop iteration // for(dCount = 0; !countLimit || dCount < countLimit; dCount++) { EHCIProcessQH(fpHCStruc, fpQH); if(fpQH->bActive == FALSE) { Status = USB_SUCCESS; break; } FixedDelay(10); // 60 microsec } if(fpQH->bActive == TRUE) { // Set the QH as in-active fpQH->bActive = FALSE; Status = USB_ERROR; USB_DEBUG (DEBUG_LEVEL_3, "EHCI Time-Out\n"); } // Service all interrupts EHCI_ProcessInterrupt(fpHCStruc); return Status; } // //---------------------------------------------------------------------------- // // Procedure: EHCIProcessQH // // Description: This function whether all the TD's in the QH is completed // // Input: fpQH - Pointer to the QH which has to be completed // // Output: USB_ERROR On error // USB_SUCCESS On success // //---------------------------------------------------------------------------- // UINT8 EHCIProcessQH( HC_STRUC *fpHCStruc, EHCI_QH *fpQH ) { EHCI_QTD *fpQTD = fpQH->fpFirstqTD; EFI_STATUS Status = EFI_SUCCESS; for (;;) { if (fpQTD == NULL) { return USB_ERROR; } #if USB_RUNTIME_DRIVER_IN_SMM Status = AmiValidateMemoryBuffer((VOID*)fpQTD, sizeof(EHCI_QTD)); if (EFI_ERROR(Status)) { return USB_ERROR; } #endif // // Check whether the qTD is active, if so. Exit! // if (fpQTD->dToken & QTD_ACTIVE) { return USB_ERROR; } // // Check for halt condition, if halted - exit // if (fpQTD->dToken & QTD_HALTED) { // // Set the QH halted status // fpQH->bErrorStatus = QTD_HALTED; // // Set the QH as in-active // fpQH->bActive = FALSE; break; } // // qTD is not active and not halted. That is it is completed successfully // Check whether this qTD is the last one in the list // if (fpQTD->dNextqTDPtr & EHCI_TERMINATE) { // // Set the QH as in-active // fpQH->bActive = FALSE; break; } // // More qTDs are in the list. Process next qTD // fpQTD = (EHCI_QTD*)(UINTN)fpQTD->dNextqTDPtr; } if ((fpQH->bCallBackIndex) && (fpQH->bCallBackIndex <= MAX_CALLBACK_FUNCTION)) { if (gUsbData->aCallBackFunctionTable[fpQH->bCallBackIndex - 1]) { if ((gUsbData->aCallBackFunctionTable[fpQH->bCallBackIndex - 1]) != EhciRepeatTDCallback) { Status = UsbDevInfoValidation((DEV_INFO*)fpQH->fpDevInfoPtr); if (EFI_ERROR(Status)) { return USB_ERROR; } } (*gUsbData->aCallBackFunctionTable[fpQH->bCallBackIndex - 1]) (fpHCStruc, (DEV_INFO*)fpQH->fpDevInfoPtr, (UINT8*)fpQH, NULL, 0); } } return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCIProcessPeriodicList // // Description: This function parses through the periodic list to find // completed qTD. If a qTD is done it will call its associated // call back function (in device info structure) and restarts // the polling qTD // // Input: fpHCStruc - Pointer to HCStruc of the host controller // // Output: None // //---------------------------------------------------------------------------- // VOID EHCIProcessPeriodicList( HC_STRUC *HcStruc ) { UINT16 Index; UINT32 *PrevPtr; UINT32 LinkPtr; EHCI_QH *Qh; UINT32 OrgQhLinkPointer; EFI_STATUS Status = EFI_SUCCESS; // // Get the first entry in the periodic list. This QH list will link to all // the periodic QH's // for (Index = 0; Index <= MAX_SPLIT_PERIODIC_NUMBER; Index ++) { PrevPtr = &HcStruc->fpFrameList[Index]; LinkPtr = *PrevPtr; while (!(LinkPtr & EHCI_TERMINATE)) { Qh = (EHCI_QH*)(LinkPtr & EHCI_POINTER_MASK); #if USB_RUNTIME_DRIVER_IN_SMM Status = AmiValidateMemoryBuffer((VOID*)Qh, sizeof(EHCI_QH)); if (EFI_ERROR(Status)) { return; } #endif // Process only QHeads, skip the other types if ((Qh->dLinkPointer & 6) == EHCI_QUEUE_HEAD) { // Check whether this QH is actived if (Qh->bActive == TRUE) { OrgQhLinkPointer = Qh->dLinkPointer; EHCIProcessQH(HcStruc, Qh); // Qh is changed, we re-parses through the list. if (Qh->dLinkPointer != OrgQhLinkPointer) { LinkPtr = *PrevPtr; continue; } } } LinkPtr = Qh->dLinkPointer; } } } // //---------------------------------------------------------------------------- // // Procedure: EhciPollingTDCallback // // Description: This function is called when a polling TD from the TD pool // completes an interrupt transaction to its assigned device. // This routine should process any data in the TD's data buffer, // handle any errors, and then copy the TD's CSReloadValue field // into its control status field to put the TD back into service. // // Input: HcStruc Pointer to the HCStruc structure // DevInfo NULL (pDevInfo is not valid) // QueueHead Pointer to the QH that completed // Buffer Not used // // Output: None // //---------------------------------------------------------------------------- // UINT8 EhciPollingTDCallback( HC_STRUC *HcStruc, DEV_INFO *DevInfo, UINT8 *QueueHead, UINT8 *Buffer, UINT16 DataLength ) { EHCI_QH* PollQh = (EHCI_QH*)QueueHead; EHCI_QTD *PollQtd = PollQh->fpFirstqTD; UINT16 BytesTransferred; EFI_STATUS Status = EFI_SUCCESS; #if USB_RUNTIME_DRIVER_IN_SMM Status = AmiValidateMemoryBuffer((VOID*)PollQtd, sizeof(EHCI_QTD)); if (EFI_ERROR(Status)) { return USB_ERROR; } #endif // Update datat toggle value UsbUpdateDataToggle(DevInfo, DevInfo->IntInEndpoint, (UINT8)((PollQh->dToken & QH_DATA_TOGGLE) >> 31)); if (!(PollQh->dToken & QTD_STATUS_FIELD)) { if ((DevInfo->bCallBackIndex) && (DevInfo->bCallBackIndex <= MAX_CALLBACK_FUNCTION)) { if (gUsbData->aCallBackFunctionTable[DevInfo->bCallBackIndex-1]) { // // Get the size of data transferred // BytesTransferred = DevInfo->PollingLength - (UINT16)((PollQtd->dToken & ~(QTD_DATA_TOGGLE)) >> 16); (*gUsbData->aCallBackFunctionTable[DevInfo->bCallBackIndex-1]) (HcStruc, DevInfo, (UINT8*)PollQtd, DevInfo->fpPollDataBuffer, BytesTransferred); } } } // Keep the PID code bit in the qTD PollQtd->dToken &= QTD_DIRECTION_PID; // // Set length // PollQtd->dToken |= (UINT32)DevInfo->PollingLength << 16; // // Update buffer pointers // EHCISetQTDBufferPointers(PollQtd, DevInfo->fpPollDataBuffer, DevInfo->PollingLength); // // Re-init the QH pointers // PollQh->dToken &= QH_DATA_TOGGLE; PollQh->dCurqTDPtr = 0; PollQh->dAltNextqTDPtr = EHCI_TERMINATE; PollQh->dBufferPtr0 = 0; PollQh->dBufferPtr1 = 0; PollQh->dBufferPtr2 = 0; PollQh->dBufferPtr3 = 0; PollQh->dBufferPtr4 = 0; // // Update next & alternate next qTD pointers // PollQtd->dNextqTDPtr = EHCI_TERMINATE; PollQtd->dAltNextqTDPtr = EHCI_TERMINATE; PollQh->dNextqTDPtr = (UINT32)(UINTN)PollQtd; // // Restart the qTD // PollQtd->dToken |= (QTD_IOC_BIT | QTD_THREE_ERRORS | QTD_ACTIVE); PollQh->bActive = TRUE; return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EHCIRepeatTDCallback // // Description: This function is called when qTdRepeat completes // a transaction. This qTD runs a dummy interrupt transaction // to a non-existant device address for the purpose of // generating a periodic timeout interrupt which in turn // is used to generate keyboard repeat or update LED status. // // Input: HcStruc Pointer to the HCStruc structure // DevInfo NULL (pDevInfo is not valid) // QueueHead Pointer to the QH that completed // Buffer Not used // // Output: None // //---------------------------------------------------------------------------- // UINT8 EhciRepeatTDCallback( HC_STRUC *HcStruc, DEV_INFO *DevInfo, UINT8 *QueueHead, UINT8 *Buffer, UINT16 DataLength ) { EHCI_QH *RepeatQh = (EHCI_QH*)QueueHead; EHCI_QTD *RepeatQtd = RepeatQh->fpFirstqTD; EFI_STATUS Status = EFI_SUCCESS; #if USB_RUNTIME_DRIVER_IN_SMM Status = AmiValidateMemoryBuffer((VOID*)RepeatQtd, sizeof(EHCI_QTD)); if (EFI_ERROR(Status)) { return USB_ERROR; } #endif #if USB_DEV_KBD USBKBDPeriodicInterruptHandler(HcStruc); #endif if (RepeatQh->dTokenReload & QTD_ACTIVE) { // // Update buffer pointers // EHCISetQTDBufferPointers(RepeatQtd, &RepeatQh->aDataBuffer[0], 8); // // Re-init the QH pointers // RepeatQh->dToken &= QH_DATA_TOGGLE; RepeatQh->dCurqTDPtr = 0; RepeatQh->dAltNextqTDPtr = EHCI_TERMINATE; RepeatQh->dBufferPtr0 = 0; RepeatQh->dBufferPtr1 = 0; RepeatQh->dBufferPtr2 = 0; RepeatQh->dBufferPtr3 = 0; RepeatQh->dBufferPtr4 = 0; RepeatQh->dNextqTDPtr = (UINT32)(UINTN)RepeatQtd; // // Restart the qTD // RepeatQh->dToken = 0; RepeatQtd->dToken = RepeatQh->dTokenReload; RepeatQh->bActive = TRUE; } return USB_SUCCESS; } // //---------------------------------------------------------------------------- // // Procedure: EhciIsHalted // // Description: This function check whether HC is halted. // //---------------------------------------------------------------------------- // BOOLEAN EhciIsHalted ( HC_STRUC *HcStruc ) { return (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_HCHALTED) == EHCI_HCHALTED; } // //---------------------------------------------------------------------------- // // Procedure: EhciTranslateInterval // // Description: This function calculates the polling rate in frames unit. // //---------------------------------------------------------------------------- // UINT16 EhciTranslateInterval( UINT8 Speed, UINT8 Interval ) { UINT8 BitCount = 0; UINT16 PollingRate = 0; if (Speed == USB_PORT_STAT_DEV_HISPEED) { // For high-speed interrupt endpoints, the Interval value must be // from 1 to 16 ASSERT(Interval >= 1 && Interval <= 16); PollingRate = (1 << (Interval - 1)) >> 3; return PollingRate != 0 ? PollingRate : 1; } // For full-/low-speed interrupt endpoints, the Interval value should // be from 1 to 255 ASSERT(Interval >= 1 && Interval <= 255); for (BitCount = 0; Interval != 0; BitCount++) { Interval >>= 1; } return (1 << (BitCount - 1)); } //========================================================================== UINT32 EhciReadPciReg( HC_STRUC *HcStruc, UINT32 Offset ) { #if USB_RUNTIME_DRIVER_IN_SMM return ReadPCIConfig(HcStruc->wBusDevFuncNum, Offset); #else EFI_STATUS Status; UINT32 Data = 0; EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, Offset, 1, &Data); ASSERT_EFI_ERROR(Status); return Data; #endif } VOID EhciWritePciReg( HC_STRUC *HcStruc, UINT32 Offset, UINT32 Data ) { #if USB_RUNTIME_DRIVER_IN_SMM DwordWritePCIConfig(HcStruc->wBusDevFuncNum, Offset, Data); return; #else EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; Status = PciIo->Pci.Write ( PciIo, EfiPciIoWidthUint32, Offset, 1, &Data); ASSERT_EFI_ERROR(Status); return; #endif } UINT32 EhciReadHcMem( HC_STRUC *HcStruc, UINT32 Offset ) { #if USB_RUNTIME_DRIVER_IN_SMM if (Offset > HcStruc->BaseAddressSize) { return 0; } return DwordReadMem((UINT32)HcStruc->BaseAddress, Offset); #else EFI_STATUS Status; UINT32 Data = 0; EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; Status = PciIo->Mem.Read ( PciIo, EfiPciIoWidthUint32, 0, Offset, 1, &Data); ASSERT_EFI_ERROR(Status); return Data; #endif } VOID EhciWriteHcMem( HC_STRUC *HcStruc, UINT32 Offset, UINT32 Data ) { #if USB_RUNTIME_DRIVER_IN_SMM if (Offset > HcStruc->BaseAddressSize) { return; } DwordWriteMem((UINT32)HcStruc->BaseAddress, Offset, Data); return; #else EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; Status = PciIo->Mem.Write ( PciIo, EfiPciIoWidthUint32, 0, Offset, 1, &Data); ASSERT_EFI_ERROR(Status); return; #endif } UINT32 EhciReadOpReg( HC_STRUC *HcStruc, UINT32 Offset ) { return EhciReadHcMem(HcStruc, HcStruc->bOpRegOffset + Offset); } VOID EhciWriteOpReg( HC_STRUC *HcStruc, UINT32 Offset, UINT32 Data ) { EhciWriteHcMem(HcStruc, HcStruc->bOpRegOffset + Offset, Data); return; } VOID EhciSetOpReg( HC_STRUC *HcStruc, UINT32 Offset, UINT32 Bit ) { UINT32 Data; Data = EhciReadOpReg(HcStruc, Offset) | Bit; EhciWriteOpReg(HcStruc, Offset, Data); return; } VOID EhciClearOpReg( HC_STRUC *HcStruc, UINT32 Offset, UINT32 Bit ) { UINT32 Data; Data = EhciReadOpReg(HcStruc, Offset) & ~Bit; EhciWriteOpReg(HcStruc, Offset, Data); return; } UINT32 EhciReadDebugReg( HC_STRUC *HcStruc, UINT8 BarIndex, UINT32 Offset ) { #if USB_RUNTIME_DRIVER_IN_SMM UINT8 BarOffset[6] = {0x10, 0x14, 0x18, 0x1C, 0x20, 0x24}; UINT32 DebugPortsMem; DebugPortsMem = EhciReadPciReg(HcStruc, BarOffset[BarIndex]); return DwordReadMem(DebugPortsMem, Offset); #else EFI_STATUS Status; UINT32 Data = 0; EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; Status = PciIo->Mem.Read ( PciIo, EfiPciIoWidthUint32, BarIndex, Offset, 1, &Data); ASSERT_EFI_ERROR(Status); return Data; #endif } VOID* EhciMemAlloc( HC_STRUC *HcStruc, UINT16 NumBlks ) { #if USB_RUNTIME_DRIVER_IN_SMM return USB_MemAlloc(NumBlks); #else UINT32 SavedMemPages = gUsbData->MemPages; UINT8 *SavedMemPool = gUsbData->fpMemBlockStart; UINT32 SavedMemBlkStsBytes = gUsbData->MemBlkStsBytes; UINT32 *SavedMemBlsSts = gUsbData->aMemBlkSts; VOID* MemPtr; gUsbData->MemPages = HcStruc->MemPoolPages; gUsbData->fpMemBlockStart = HcStruc->MemPool; gUsbData->MemBlkStsBytes = HcStruc->MemBlkStsBytes; gUsbData->aMemBlkSts = HcStruc->MemBlkSts; MemPtr = USB_MemAlloc(NumBlks); gUsbData->MemPages = SavedMemPages; gUsbData->fpMemBlockStart = SavedMemPool; gUsbData->MemBlkStsBytes = SavedMemBlkStsBytes; gUsbData->aMemBlkSts = SavedMemBlsSts; return MemPtr; #endif } VOID EhciMemFree( HC_STRUC *HcStruc, VOID* MemPtr, UINT16 NumBlks ) { #if USB_RUNTIME_DRIVER_IN_SMM USB_MemFree(MemPtr, NumBlks); #else UINT32 SavedMemPages = gUsbData->MemPages; UINT8 *SavedMemPool = gUsbData->fpMemBlockStart; UINT32 SavedMemBlkStsBytes = gUsbData->MemBlkStsBytes; UINT32 *SavedMemBlsSts = gUsbData->aMemBlkSts; gUsbData->MemPages = HcStruc->MemPoolPages; gUsbData->fpMemBlockStart = HcStruc->MemPool; gUsbData->MemBlkStsBytes = HcStruc->MemBlkStsBytes; gUsbData->aMemBlkSts = HcStruc->MemBlkSts; USB_MemFree(MemPtr, NumBlks); gUsbData->MemPages = SavedMemPages; gUsbData->fpMemBlockStart = SavedMemPool; gUsbData->MemBlkStsBytes = SavedMemBlkStsBytes; gUsbData->aMemBlkSts = SavedMemBlsSts; return; #endif } UINT8 EhciDmaMap( HC_STRUC *HcStruc, UINT8 Direction, UINT8 *BufferAddr, UINT32 BufferSize, UINT8 **PhyAddr, VOID **Mapping ) { #if USB_RUNTIME_DRIVER_IN_SMM *PhyAddr = BufferAddr; #else EFI_PCI_IO_PROTOCOL_OPERATION Operation; EFI_PHYSICAL_ADDRESS Addr; EFI_STATUS Status; UINTN Bytes = BufferSize; EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; if (Direction & BIT7) { Operation = EfiPciIoOperationBusMasterWrite; } else { Operation = EfiPciIoOperationBusMasterRead; } Status = PciIo->Map ( PciIo, Operation, BufferAddr, &Bytes, &Addr, Mapping ); ASSERT_EFI_ERROR(Status); if (EFI_ERROR(Status) || Bytes != BufferSize) { return USB_ERROR; } *PhyAddr = (UINT8*)Addr; #endif return USB_SUCCESS; } UINT8 EhciDmaUnmap( HC_STRUC *HcStruc, VOID *Mapping ) { #if !USB_RUNTIME_DRIVER_IN_SMM EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; Status = PciIo->Unmap(PciIo, Mapping); ASSERT_EFI_ERROR(Status); if (EFI_ERROR(Status)) { return USB_ERROR; } #endif return USB_SUCCESS; } //**************************************************************************** //**************************************************************************** //** ** //** (C)Copyright 1985-2016, American Megatrends, Inc. ** //** ** //** All Rights Reserved. ** //** ** //** 5555 Oakbrook Pkwy, Norcross, GA 30093 ** //** ** //** Phone (770)-246-8600 ** //** ** //**************************************************************************** //****************************************************************************