├── .gitignore ├── Accessors.cpp ├── Async.cpp ├── Async.h ├── ChangeLog.txt ├── Command.cpp ├── Compatibility.cpp ├── Compatibility.h ├── Completer.cpp ├── Completer.h ├── Diagnostics.cpp ├── Endpoints.cpp ├── GenericUSBXHCI.cpp ├── GenericUSBXHCI.h ├── GenericUSBXHCI.xcodeproj └── project.pbxproj ├── GenericUSBXHCIEventSource.cpp ├── GenericUSBXHCIUserClient.cpp ├── GenericUSBXHCIUserClient.h ├── Info.plist ├── Interrupts.cpp ├── Isoch.cpp ├── Isoch.h ├── Leaf_Methods.cpp ├── Private.h ├── PwrMgmt.cpp ├── README.md ├── Rings.cpp ├── RootHub.cpp ├── Slots.cpp ├── Streams.cpp ├── Transfers.cpp ├── UnXHCI.cpp ├── V1Overrides.cpp ├── V1Pure.cpp ├── V2Overrides.cpp ├── V2Pure.cpp ├── V3Overrides.cpp ├── V3Pure.cpp ├── XHCIRegs.h ├── XHCITRB.h ├── XHCITypes.h ├── makefile ├── print_version.sh ├── xhcdump.c └── xhcdump.mak /.gitignore: -------------------------------------------------------------------------------- 1 | Build 2 | DerivedData 3 | GenericUSBXHCI.xcodeproj/project.xcworkspace 4 | GenericUSBXHCI.xcodeproj/xcuserdata 5 | xhcdump 6 | Distribute 7 | 8 | -------------------------------------------------------------------------------- /Accessors.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Accessors.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on January 12th 2013. 6 | // Copyright (c) 2013-2014 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCI.h" 10 | #include "XHCITypes.h" 11 | 12 | #define CLASS GenericUSBXHCI 13 | #define super IOUSBControllerV3 14 | 15 | #pragma mark - 16 | #pragma mark Accessors 17 | #pragma mark - 18 | 19 | #pragma mark - 20 | #pragma mark Register Space 21 | #pragma mark - 22 | 23 | __attribute__((noinline, visibility("hidden"))) 24 | uint8_t CLASS::Read8Reg(uint8_t volatile const* p) 25 | { 26 | uint8_t v; 27 | if (m_invalid_regspace) 28 | return UINT8_MAX; 29 | v = *p; 30 | if (v != UINT8_MAX) 31 | return v; 32 | if (ON_THUNDERBOLT) 33 | m_invalid_regspace = true; 34 | return UINT8_MAX; 35 | } 36 | 37 | __attribute__((noinline, visibility("hidden"))) 38 | uint16_t CLASS::Read16Reg(uint16_t volatile const* p) 39 | { 40 | uint16_t v; 41 | if (m_invalid_regspace) 42 | return UINT16_MAX; 43 | v = *p; 44 | if (v != UINT16_MAX) 45 | return v; 46 | if (ON_THUNDERBOLT) 47 | m_invalid_regspace = true; 48 | return UINT16_MAX; 49 | } 50 | 51 | __attribute__((noinline, visibility("hidden"))) 52 | uint32_t CLASS::Read32Reg(uint32_t volatile const* p) 53 | { 54 | uint32_t v; 55 | if (m_invalid_regspace) 56 | return UINT32_MAX; 57 | v = *p; 58 | if (v != UINT32_MAX) 59 | return v; 60 | if (ON_THUNDERBOLT) 61 | m_invalid_regspace = true; 62 | return UINT32_MAX; 63 | } 64 | 65 | __attribute__((noinline, visibility("hidden"))) 66 | uint64_t CLASS::Read64Reg(uint64_t volatile const* p) 67 | { 68 | uint64_t v; 69 | uint32_t lowv, highv; 70 | if (m_invalid_regspace) 71 | return UINT64_MAX; 72 | if (XHCI_HCC_AC64(_HCCLow)) { 73 | #ifdef __LP64__ 74 | if (_vendorID != kVendorFrescoLogic) { 75 | v = *p; 76 | if (v == UINT64_MAX && ON_THUNDERBOLT) 77 | goto scrap; 78 | return v; 79 | } 80 | #endif 81 | lowv = *reinterpret_cast(p); 82 | if (lowv == UINT32_MAX && ON_THUNDERBOLT) 83 | goto scrap; 84 | highv = reinterpret_cast(p)[1]; 85 | if (highv == UINT32_MAX && ON_THUNDERBOLT) 86 | goto scrap; 87 | return (static_cast(highv) << 32) | lowv; 88 | } 89 | lowv = *reinterpret_cast(p); 90 | if (lowv == UINT32_MAX && ON_THUNDERBOLT) 91 | goto scrap; 92 | return lowv; 93 | scrap: 94 | m_invalid_regspace = true; 95 | return UINT64_MAX; 96 | } 97 | 98 | __attribute__((noinline, visibility("hidden"))) 99 | void CLASS::Write32Reg(uint32_t volatile* p, uint32_t v) 100 | { 101 | if (m_invalid_regspace) 102 | return; 103 | *p = v; 104 | } 105 | 106 | __attribute__((noinline, visibility("hidden"))) 107 | void CLASS::Write64Reg(uint64_t volatile* p, uint64_t v, bool) 108 | { 109 | if (m_invalid_regspace) 110 | return; 111 | if (XHCI_HCC_AC64(_HCCLow)) { 112 | #ifdef __LP64__ 113 | if (_vendorID != kVendorFrescoLogic) { 114 | *p = v; 115 | return; 116 | } 117 | #endif 118 | *reinterpret_cast(p) = static_cast(v); 119 | reinterpret_cast(p)[1] = static_cast(v >> 32); 120 | return; 121 | } 122 | *reinterpret_cast(p) = static_cast(v); 123 | reinterpret_cast(p)[1] = 0U; 124 | } 125 | 126 | #pragma mark - 127 | #pragma mark Contexts 128 | #pragma mark - 129 | 130 | __attribute__((noinline, visibility("hidden"))) 131 | ContextStruct* CLASS::GetSlotContext(int32_t slot, int32_t index) 132 | { 133 | ContextStruct* pContext = SlotPtr(slot)->ctx; 134 | if (!pContext || index <= 0) 135 | return pContext; 136 | return XHCI_HCC_CSZ(_HCCLow) ? (pContext + 2 * index) : (pContext + index); 137 | } 138 | 139 | __attribute__((noinline, visibility("hidden"))) 140 | ContextStruct* CLASS::GetInputContextPtr(int32_t index) 141 | { 142 | return XHCI_HCC_CSZ(_HCCLow) ? (_inputContext.ptr + 2 * index) : (_inputContext.ptr + index); 143 | } 144 | 145 | __attribute__((visibility("hidden"))) 146 | uint8_t CLASS::GetSlCtxSpeed(ContextStruct const* pContext) 147 | { 148 | switch (XHCI_SCTX_0_SPEED_GET(pContext->_s.dwSctx0)) { 149 | case XDEV_FS: 150 | return kUSBDeviceSpeedFull; 151 | case XDEV_LS: 152 | return kUSBDeviceSpeedLow; 153 | case XDEV_HS: 154 | return kUSBDeviceSpeedHigh; 155 | default: // (XDEV_SS) 156 | return kUSBDeviceSpeedSuper; 157 | } 158 | } 159 | 160 | __attribute__((visibility("hidden"))) 161 | void CLASS::SetSlCtxSpeed(ContextStruct* pContext, uint32_t speed) 162 | { 163 | pContext->_s.dwSctx0 |= XHCI_SCTX_0_SPEED_SET(speed); 164 | } 165 | 166 | #pragma mark - 167 | #pragma mark TRB 168 | #pragma mark - 169 | 170 | __attribute__((noinline, visibility("hidden"))) 171 | void CLASS::SetTRBAddr64(TRBStruct* trb, uint64_t addr) 172 | { 173 | trb->a = static_cast(addr); 174 | trb->b = static_cast(addr >> 32); 175 | } 176 | 177 | __attribute__((visibility("hidden"))) 178 | void CLASS::ClearTRB(TRBStruct* trb, bool wipeCycleBit) 179 | { 180 | trb->a = 0U; 181 | trb->b = 0U; 182 | trb->c = 0U; 183 | if (wipeCycleBit) 184 | trb->d = 0U; 185 | else 186 | trb->d &= XHCI_TRB_3_CYCLE_BIT; 187 | } 188 | 189 | #pragma mark - 190 | #pragma mark DCBAA 191 | #pragma mark - 192 | 193 | __attribute__((noinline, visibility("hidden"))) 194 | void CLASS::SetDCBAAAddr64(uint64_t* p, uint64_t addr) 195 | { 196 | *reinterpret_cast(p) = static_cast(addr) & ~0x3FU; 197 | reinterpret_cast(p)[1] = static_cast(addr >> 32); 198 | } 199 | -------------------------------------------------------------------------------- /Async.h: -------------------------------------------------------------------------------- 1 | // 2 | // Async.h 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on February 10th 2013. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #ifndef GenericUSBXHCI_Async_h 10 | #define GenericUSBXHCI_Async_h 11 | 12 | struct XHCIAsyncTD; 13 | 14 | struct XHCIAsyncEndpoint 15 | { 16 | ringStruct* pRing; // 0x10 (start) 17 | XHCIAsyncTD* queuedHead; // 0x18 18 | XHCIAsyncTD* queuedTail; // 0x20 19 | XHCIAsyncTD* scheduledHead; // 0x28 20 | XHCIAsyncTD* scheduledTail; // 0x30 21 | XHCIAsyncTD* doneHead; // 0x38 22 | XHCIAsyncTD* doneTail; // 0x40 23 | XHCIAsyncTD* freeHead; // 0x48 24 | XHCIAsyncTD* freeTail; // 0x50 25 | uint32_t numTDsQueued; // 0x58 26 | uint32_t numTDsScheduled; // 0x5C 27 | uint32_t numTDsDone; // 0x60 28 | uint32_t numTDsFree; // 0x64 29 | bool aborting; // 0x68 30 | uint32_t maxPacketSize; // 0x6C 31 | uint32_t maxBurst; // 0x70 32 | uint32_t multiple; // 0x74 33 | uint32_t maxTDBytes; // 0x78 34 | GenericUSBXHCI* provider; // 0x80 35 | // sizeof 0x88 36 | 37 | IOReturn CreateTDs(IOUSBCommand*, uint16_t, uint32_t, uint8_t, uint8_t const*); 38 | void ScheduleTDs(void); 39 | IOReturn Abort(void); 40 | XHCIAsyncTD* GetTDFromActiveQueueWithIndex(uint16_t); 41 | void RetireTDs(XHCIAsyncTD*, IOReturn, bool, bool); 42 | XHCIAsyncTD* GetTDFromFreeQueue(bool); 43 | void PutTDonDoneQueue(XHCIAsyncTD*); 44 | void FlushTDs(IOUSBCommand*, int32_t); 45 | void MoveTDsFromReadyQToDoneQ(IOUSBCommand*); 46 | void MoveAllTDsFromReadyQToDoneQ(void); 47 | void Complete(IOReturn); 48 | bool NeedTimeouts(void); 49 | void UpdateTimeouts(bool, uint32_t, bool); 50 | static XHCIAsyncTD* GetTD(XHCIAsyncTD**, XHCIAsyncTD**, uint32_t*); 51 | static void PutTD(XHCIAsyncTD**, XHCIAsyncTD**, XHCIAsyncTD*, uint32_t*); 52 | static void PutTDAtHead(XHCIAsyncTD**, XHCIAsyncTD**, XHCIAsyncTD*, uint32_t*); 53 | static void wipeAsyncList(XHCIAsyncTD*, XHCIAsyncTD*); 54 | static XHCIAsyncEndpoint* withParameters(GenericUSBXHCI*, ringStruct*, uint32_t, uint32_t, uint32_t); 55 | void setParameters(uint32_t, uint32_t, uint32_t); 56 | bool checkOwnership(GenericUSBXHCI*, ringStruct*); 57 | void release(void); 58 | void nuke(void); 59 | }; 60 | 61 | struct XHCIAsyncTD 62 | { 63 | IOUSBCommand* command; // 0x10 (start) 64 | uint32_t bytesThisTD; // 0x18 65 | size_t bytesFollowingThisTD; // 0x1C - original uint32_t 66 | size_t bytesPreceedingThisTD; // 0x20 67 | uint32_t firstTrbIndex; // 0x28 68 | uint32_t TrbCount; // 0x2C 69 | bool interruptThisTD; // 0x30 70 | bool multiTDTransaction; // 0x31 71 | uint16_t numTDsThisTransaction; // 0x32 72 | uint32_t mystery; // 0x34 73 | uint32_t shortfall; // 0x38 74 | uint16_t maxNumPagesInTD; // 0x3C 75 | bool haveImmediateData; // 0x3E 76 | bool finalTDInTransaction; // 0x3F 77 | uint8_t immediateData[8]; // 0x40 78 | uint16_t streamId; // 0x48 79 | int16_t lastTrbIndex; // 0x4A 80 | bool absoluteShortfall; // Added 81 | XHCIAsyncEndpoint* provider; // 0x50 82 | XHCIAsyncTD* next; // 0x58 83 | // sizeof 0x60 84 | 85 | static XHCIAsyncTD* ForEndpoint(XHCIAsyncEndpoint*); 86 | void reinit(void); 87 | void release(void); 88 | }; 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /ChangeLog.txt: -------------------------------------------------------------------------------- 1 | ChangeLog 2 | ========= 3 | 1.2.8d9 4 | - Adapted to minimal build environment of Xcode 5.1.1 with Mac OS 10.8.5 SDK. [Eliminates use of getV1Ptr, getV3Ptr and assumes fields introduced in OS 10.8.5 are found in IOUSBFamily header files.] 5 | - Added compatibility stubs for targeting OS 10.7.5 - 10.8.4, 10.8.5, 10.9.x. 6 | - Adapted for building with Xcode 6.x. 7 | 8 | 1.2.8d8 9 | - Updated to use new location of _onThunderbolt in OS 10.10. 10 | 11 | 1.2.8d7 12 | - Implement properties UseLegacyInt, IntelDoze, eliminate -gux_no_idle. 13 | - Add GenericUSBXHCIEventSource to do _completer.Flush(). 14 | - Don't park a disabled ring. 15 | - Call DoCMDCompletion from primary interrupt context. 16 | - Correct port recognition as USB2/USB3 in Diagnostics.cpp. 17 | - Eliminate unneeded calls to ClearStopTDS. 18 | - Some corrections in UIMEnableAddressEndpoints, CountRingToED, AdvanceTransferDQ. 19 | - Review UIMCheckForTimeouts. 20 | - Review UIMDeleteEndpoint, UIMAbortStream. 21 | - Implemented DoSoftRetries, updated TranslateXHCIStatus. 22 | - Add invalid doorbell diagnostic. 23 | - Review calls to StopEndpoint. 24 | - Review RetireTDs. 25 | 26 | 1.2.7Fixes 27 | - Implement workaround for ASM1042 Absolute EDTLA bug. 28 | - Correct calculation of shortfall in UpdateTimeouts. 29 | - Revert commit f63329 (Address Device only once) because it causes lag for some devices. 30 | 31 | 1.2.7 32 | - Mark stream as active using ring md. 33 | - Simplify UIMDeleteEndpoint. 34 | - Add AdvanceTransferDQ (not used yet). 35 | - Fix bug in NukeSlot releasing streams. 36 | - Upgrade project files to Xcode 5. 37 | - Speed up stream detection with qsort/bsearch. 38 | - Reorganize source files. 39 | - Move some diagnostics from caps to running. 40 | - Try make SWAssistedXHCI safer for Intel Series 7. 41 | - Correct some code for Intel Series 8. 42 | - Added some FL1100 errata from OS 10.9. 43 | - Allow stream 1 on 2-stream bulk endpoint. 44 | - Set DisableUAS for Etron chip - enables IOUSBFamily 10.8.5, 10.9 workaround for Etron lack of streams support. 45 | - Delay USL completions to safe points. 46 | - Made -gux_no_idle the default for OS 10.9. 47 | - Set DisableUAS for ASMedia 1042. 48 | 49 | 1.2.6 50 | - Added support for VMware virtual xHC device in Workstation 10/Fusion 6. 51 | - Fixed very serious bug that would cause KPs with any USB3 drive supporting streams endpoints. 52 | - Added some diagnostics. 53 | - Fixed support for streams endpoints to use earlier allocation of rings. 54 | - Address Device only once. 55 | - Proper cleanup for incomplete UIMCreateControlEndpoint. 56 | 57 | 1.2.5 58 | - Support for OS 10.8.5, OS 10.9. 59 | - Support for Lynx Point/Intel Series 8 with 16 xHC ports (8 dual ports). 60 | 61 | 1.2.4 62 | - Overhauled error-handling of non-isoch transfers. Fixed a couple of bugs with KP potential related to transfers. 63 | - Optimized handling of transfer timeouts. 64 | - Added kext version to 'xhcdump caps' + a couple of diagnostic counters to 'xhcdump running' 65 | 66 | 1.2.3 67 | - Improved handling of bios semaphore for legacy USB - eliminates a problem that causes some bioses to crash during sleep. 68 | - Made sequence for entering sleep mode more robust and safer. 69 | - Eliminated potential memory corruption if xhci commands time out. 70 | - Eliminated some potential memory leaks, fixed some minor bugs, made some minor optimizations and added a bit more logging. 71 | 72 | 1.2.2 73 | - Fixed extra-current errors mentioned in posts #85, #90. 74 | - Fixed minor issue with 32-bit build. 75 | - Fixed issue with chips that stop clocks for power reduction. 76 | - Added some logging to help debugging. 77 | 78 | 1.2.1 79 | - Fixed alignment bug that made 32-bit build for OS 10.7.5 fail. 80 | 81 | 1.2.0 82 | - Implemented support for Isochronous Endpoints. Should now support webcams, etc... (real-time audio/video streaming). 83 | - Updated UIM interface changes in OS 10.8.3. 84 | - Added workaround for Intel Series 7 chipset that causes instant-wakeup-on-sleep on some motherboards. 85 | 86 | 1.1.6. 87 | - Fixed two critical bugs and seven minor bugs. 88 | 89 | 1.1.5. 90 | - Fixed one critical bug and two minor bugs. 91 | 92 | 1.1.4. 93 | - Reverted change that caused regression discussed in post #41. Turns out 11 msec means 11 msec and not a msec less. :D 94 | - Packaged common binary for 10.7.5 + 10.8.x, as suggested in post #39. 95 | 96 | 1.1.3. 97 | - Prevent instant-wakeup and spontaneous reboot on ASM1042. 98 | - Eliminated some race conditions on power-state and port-status changes that may cause KPs. 99 | 100 | 1.1 101 | - Made suspend/resume code safer from KPs. 102 | - Fixed an issue with use of shared pin interrupt. 103 | - Added diagnostic counters for some errors. These are printed by 'xhcdump running' if non-zero. 104 | 105 | 1.1a4 106 | - Fix for xHC that lack 64-bit physical-addressing (ASM1042) 107 | - Prints some more information in xhcdump caps/running 108 | - Added kernel flag '-gux_nomsi' to force use of pin interrupt 109 | - 'xhcdump options' shows kernel flags supported by kext 110 | 111 | 1.1a3 112 | - Added code to discard spurious events that are a likely cause of stalls described in post #10. 113 | - Made error handling of events more robust and fixed some minor bugs in processing of transfers. 114 | 115 | 1.1a2 116 | - code added to take ownership of xHC from bios. 117 | -------------------------------------------------------------------------------- /Command.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Command.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on January 27th 2013. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCI.h" 10 | 11 | #define CLASS GenericUSBXHCI 12 | #define super IOUSBControllerV3 13 | 14 | #pragma mark - 15 | #pragma mark Helper 16 | #pragma mark - 17 | 18 | template 19 | static 20 | IOReturn WaitForChangeEvent(T volatile const* pEvent, T start_value) 21 | { 22 | uint64_t deadline; 23 | 24 | if (!(*pEvent == start_value)) 25 | return kIOReturnSuccess; 26 | clock_interval_to_deadline(100, kMillisecondScale, &deadline); 27 | do { 28 | if (assert_wait_deadline(const_cast(pEvent), THREAD_ABORTSAFE, deadline) != THREAD_WAITING) 29 | return kIOReturnNoResources; 30 | if (!(*pEvent == start_value)) { 31 | thread_wakeup_prim(const_cast(pEvent), FALSE, THREAD_AWAKENED); 32 | break; 33 | } 34 | switch (thread_block(THREAD_CONTINUE_NULL)) { 35 | case THREAD_AWAKENED: 36 | case THREAD_NOT_WAITING: 37 | break; 38 | case THREAD_TIMED_OUT: 39 | return kIOReturnTimeout; 40 | default: 41 | return kIOReturnAborted; 42 | } 43 | } while (*pEvent == start_value); 44 | return kIOReturnSuccess; 45 | } 46 | 47 | #pragma mark - 48 | #pragma mark Command Ring 49 | #pragma mark - 50 | 51 | __attribute__((visibility("hidden"))) 52 | void CLASS::InitCMDRing(void) 53 | { 54 | if (_commandRing.numTRBs) { 55 | bzero(_commandRing.callbacks, _commandRing.numTRBs * sizeof *_commandRing.callbacks); 56 | bzero(_commandRing.ptr, _commandRing.numTRBs * sizeof *_commandRing.ptr); 57 | SetTRBAddr64(&_commandRing.ptr[_commandRing.numTRBs - 1U], _commandRing.physAddr); 58 | _commandRing.ptr[_commandRing.numTRBs - 1U].d |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) | XHCI_TRB_3_TC_BIT; 59 | } 60 | _commandRing.cycleState = 1U; 61 | _commandRing.enqueueIndex = 0U; 62 | _commandRing.dequeueIndex = 0U; 63 | _commandRing.stopPending = false; 64 | Write64Reg(&_pXHCIOperationalRegisters->CRCr, 65 | (_commandRing.physAddr & ~XHCI_CRCR_LO_MASK) | XHCI_CRCR_LO_RCS, 66 | false); 67 | } 68 | 69 | __attribute__((visibility("hidden"))) 70 | IOReturn CLASS::CommandStop(void) 71 | { 72 | uint32_t lowCRCr = Read32Reg(reinterpret_cast(&_pXHCIOperationalRegisters->CRCr)); 73 | if (m_invalid_regspace) 74 | return kIOReturnNoDevice; 75 | if (!(lowCRCr & static_cast(XHCI_CRCR_LO_CRR))) 76 | return kIOReturnSuccess; 77 | _commandRing.stopPending = true; 78 | Write32Reg(reinterpret_cast(&_pXHCIOperationalRegisters->CRCr), static_cast(XHCI_CRCR_LO_CS)); 79 | WaitForChangeEvent(&_commandRing.stopPending, true); 80 | if (_commandRing.stopPending) { 81 | _commandRing.stopPending = false; 82 | IOLog("%s: Timeout waiting for command ring to stop, 100ms\n", __FUNCTION__); 83 | return kIOReturnTimeout; 84 | } 85 | return kIOReturnSuccess; 86 | } 87 | 88 | __attribute__((visibility("hidden"))) 89 | IOReturn CLASS::CommandAbort(void) 90 | { 91 | uint32_t lowCRCr = Read32Reg(reinterpret_cast(&_pXHCIOperationalRegisters->CRCr)); 92 | if (m_invalid_regspace) 93 | return kIOReturnNoDevice; 94 | if (!(lowCRCr & static_cast(XHCI_CRCR_LO_CRR))) 95 | return kIOReturnSuccess; 96 | _commandRing.stopPending = true; 97 | Write32Reg(reinterpret_cast(&_pXHCIOperationalRegisters->CRCr), static_cast(XHCI_CRCR_LO_CA)); 98 | WaitForChangeEvent(&_commandRing.stopPending, true); 99 | if (_commandRing.stopPending) { 100 | IOLog("%s: Timeout waiting for command to abort, 100ms\n", __FUNCTION__); 101 | _commandRing.stopPending = false; 102 | return kIOReturnTimeout; 103 | } 104 | return kIOReturnSuccess; 105 | } 106 | 107 | __attribute__((visibility("hidden"))) 108 | int32_t CLASS::WaitForCMD(TRBStruct* trb, int32_t trbType, TRBCallback callback) 109 | { 110 | int32_t prevIndex; 111 | int32_t volatile ret = -1; 112 | uint32_t sts = Read32Reg(&_pXHCIOperationalRegisters->USBSts); 113 | if (m_invalid_regspace) 114 | return ret; 115 | if ((sts & XHCI_STS_HSE) && !_HSEDetected) { 116 | IOLog("%s: HSE bit set:%#x (1)\n", __FUNCTION__, sts); 117 | _HSEDetected = true; 118 | } 119 | if (isInactive() || !_controllerAvailable) 120 | return ret; 121 | prevIndex = _commandRing.enqueueIndex; 122 | if (EnqueCMD(trb, trbType, callback ? : CompleteSlotCommand, const_cast(&ret)) != kIOReturnSuccess) 123 | return ret; 124 | WaitForChangeEvent(&ret, -1); 125 | /* 126 | * Note: Scoop up stop TRBs 127 | */ 128 | if (trbType == XHCI_TRB_TYPE_STOP_EP) 129 | PollForCMDCompletions(0); 130 | if (ret != -1) 131 | return ret; 132 | IOLog("%s: Timeout waiting for command completion (opcode %#x), 100ms\n", __FUNCTION__, static_cast(trbType)); 133 | CommandAbort(); 134 | if (ret == -1) { 135 | /* 136 | * If command was still not completed, wipe its callback, so &ret 137 | * does not get overrun. 138 | */ 139 | _commandRing.callbacks[prevIndex].func = 0; 140 | _commandRing.callbacks[prevIndex].param = 0; 141 | } 142 | return ret; 143 | } 144 | 145 | __attribute__((visibility("hidden"))) 146 | IOReturn CLASS::EnqueCMD(TRBStruct* trb, int32_t trbType, TRBCallback callback, int32_t* param) 147 | { 148 | TRBStruct* target; 149 | uint32_t fourth; 150 | int32_t next = _commandRing.enqueueIndex; 151 | if (next < static_cast(_commandRing.numTRBs) - 2) 152 | ++next; 153 | else 154 | next = 0; 155 | if (next == _commandRing.dequeueIndex) { 156 | IOLog("%s: Ring full, enq:%u, deq:%u\n", __FUNCTION__, _commandRing.enqueueIndex, _commandRing.dequeueIndex); 157 | return kIOReturnNoResources; 158 | } 159 | target = &_commandRing.ptr[_commandRing.enqueueIndex]; 160 | target->a = trb->a; 161 | target->b = trb->b; 162 | target->c = trb->c; 163 | _commandRing.callbacks[_commandRing.enqueueIndex].func = callback; 164 | _commandRing.callbacks[_commandRing.enqueueIndex].param = param; 165 | fourth = (trb->d) & ~(XHCI_TRB_3_TYPE_SET(63U) | XHCI_TRB_3_CYCLE_BIT); 166 | fourth |= XHCI_TRB_3_TYPE_SET(trbType); 167 | if (_commandRing.cycleState) 168 | fourth |= XHCI_TRB_3_CYCLE_BIT; 169 | IOSync(); 170 | target->d = fourth; 171 | IOSync(); 172 | if (!next) { 173 | if (_commandRing.cycleState) 174 | _commandRing.ptr[_commandRing.numTRBs - 1U].d |= XHCI_TRB_3_CYCLE_BIT; 175 | else 176 | _commandRing.ptr[_commandRing.numTRBs - 1U].d &= ~XHCI_TRB_3_CYCLE_BIT; 177 | _commandRing.cycleState ^= 1U; 178 | } 179 | _commandRing.enqueueIndex = static_cast(next); 180 | Write32Reg(&_pXHCIDoorbellRegisters[0], 0U); 181 | return kIOReturnSuccess; 182 | } 183 | 184 | __attribute__((visibility("hidden"))) 185 | bool CLASS::DoCMDCompletion(TRBStruct trb) 186 | { 187 | int64_t idx64; 188 | TRBCallbackEntry copy; 189 | uint16_t newIdx; 190 | uint64_t addr = GetTRBAddr64(&trb); 191 | if (!addr) { 192 | if (!ml_at_interrupt_context()) 193 | IOLog("%s: Zero pointer in CCE\n", __FUNCTION__); 194 | return false; 195 | } 196 | idx64 = DiffTRBIndex(addr, _commandRing.physAddr); 197 | if (idx64 < 0 || idx64 >= _commandRing.numTRBs - 1U) { 198 | if (!ml_at_interrupt_context()) 199 | IOLog("%s: bad pointer in CCE: %lld\n", __FUNCTION__, idx64); 200 | return false; 201 | } 202 | if (XHCI_TRB_2_ERROR_GET(trb.c) == XHCI_TRB_ERROR_CMD_RING_STOP) { 203 | _commandRing.dequeueIndex = static_cast(idx64); 204 | _commandRing.stopPending = false; 205 | if (ml_at_interrupt_context()) 206 | thread_wakeup_prim(const_cast(&_commandRing.stopPending), FALSE, THREAD_AWAKENED); 207 | return true; 208 | } 209 | newIdx = static_cast(idx64) + 1U; 210 | if (newIdx >= _commandRing.numTRBs - 1U) 211 | newIdx = 0U; 212 | copy = _commandRing.callbacks[idx64]; 213 | _commandRing.callbacks[idx64].func = 0; 214 | _commandRing.callbacks[idx64].param = 0; 215 | _commandRing.dequeueIndex = newIdx; 216 | if (copy.func) 217 | copy.func(this, &trb, copy.param); 218 | return true; 219 | } 220 | 221 | #pragma mark - 222 | #pragma mark Completion Routines called from DoCMDCompletion 223 | #pragma mark - 224 | 225 | __attribute__((visibility("hidden"))) 226 | void CLASS::CompleteSlotCommand(CLASS*, TRBStruct* pTrb, int32_t* param) 227 | { 228 | int32_t ret, err = static_cast(XHCI_TRB_2_ERROR_GET(pTrb->c)); 229 | if (err == XHCI_TRB_ERROR_SUCCESS) 230 | ret = static_cast(XHCI_TRB_3_SLOT_GET(pTrb->d)); 231 | else 232 | ret = -1000 - err; 233 | *param = ret; 234 | if (ml_at_interrupt_context()) 235 | thread_wakeup_prim(param, FALSE, THREAD_AWAKENED); 236 | } 237 | 238 | #if 0 239 | /* 240 | * Note: Unused 241 | * This is used to handle TRB_RENESAS_GET_FW command 242 | * Lower 16 bits of pTrb->c are FWVersionMajor:FWVersionMinor 243 | * each field 8 bits. 244 | */ 245 | __attribute__((visibility("hidden"))) 246 | void CLASS::CompleteRenesasVendorCommand(CLASS*, TRBStruct* pTrb, int32_t* param) 247 | { 248 | int32_t ret, err = static_cast(XHCI_TRB_2_ERROR_GET(pTrb->c)); 249 | if (err == XHCI_TRB_ERROR_SUCCESS) 250 | ret = static_cast(pTrb->c & UINT16_MAX); 251 | else 252 | ret = -1000 - err; // Note: originally 1000 + err 253 | *param = ret; 254 | if (ml_at_interrupt_context()) 255 | thread_wakeup_prim(param, FALSE, THREAD_AWAKENED); 256 | } 257 | #endif 258 | -------------------------------------------------------------------------------- /Compatibility.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Compatibility.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on September 10th 2014. 6 | // Copyright (c) 2014 Zenith432. All rights reserved. 7 | // 8 | // 9 | 10 | #include "GenericUSBXHCI.h" 11 | 12 | #define CLASS GenericUSBXHCI 13 | #define super IOUSBControllerV3 14 | 15 | #pragma mark - 16 | #pragma mark Build-time Versioning Test 17 | #pragma mark - 18 | 19 | #ifdef REHABMAN_UNIVERSAL_BUILD 20 | #if __MAC_OS_X_VERSION_MAX_ALLOWED < 1080 21 | #error REHABMAN_UNIVERSAL_BUILD requires at least 10.8 SDK. 22 | #endif 23 | #endif 24 | 25 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 && !defined(REHABMAN_UNIVERSAL_BUILD) 26 | #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101000 27 | #error OS 10.10 SDK may only be used to target OS version 10.10. 28 | #endif 29 | #elif __MAC_OS_X_VERSION_MIN_REQUIRED < 1075 30 | #error Target OS version must be 10.7.5 or above. 31 | #endif 32 | 33 | #if 0 34 | 35 | #pragma mark - 36 | #pragma mark Introduced OS 10.10 37 | #pragma mark - 38 | 39 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 && __MAC_OS_X_VERSION_MIN_REQUIRED < 101000 40 | __attribute__((visibility("hidden"))) 41 | bool CLASS::init(IORegistryEntry* from, IORegistryPlane const* inPlane) 42 | { 43 | return super::init(from, inPlane); 44 | } 45 | 46 | __attribute__((visibility("hidden"))) 47 | IOReturn CLASS::GetRootHubPowerExitLatencies(IOUSBHubExitLatencies** latencies) 48 | { 49 | return super::GetRootHubPowerExitLatencies(latencies); 50 | } 51 | 52 | __attribute__((visibility("hidden"))) 53 | IOReturn CLASS::CheckPowerModeBeforeGatedCall(char* fromStr, OSObject* token, UInt32 options) 54 | { 55 | return super::CheckPowerModeBeforeGatedCall(fromStr, token, options); 56 | } 57 | #endif 58 | 59 | #pragma mark - 60 | #pragma mark Introduced OS 10.9 61 | #pragma mark - 62 | 63 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 && __MAC_OS_X_VERSION_MIN_REQUIRED < 1090 64 | __attribute__((visibility("hidden"))) 65 | IOReturn CLASS::updateReport(IOReportChannelList* channels, IOReportUpdateAction action, void* result, void* destination) 66 | { 67 | return super::updateReport(channels, action, result, destination); 68 | } 69 | 70 | __attribute__((visibility("hidden"))) 71 | bool CLASS::DoNotPowerOffPortsOnStop(void) 72 | { 73 | return super::DoNotPowerOffPortsOnStop(); 74 | } 75 | 76 | __attribute__((visibility("hidden"))) 77 | IOReturn CLASS::configureReport(IOReportChannelList* channels, IOReportConfigureAction action, void* result, void* destination) 78 | { 79 | return super::configureReport(channels, action, result, destination); 80 | } 81 | #endif 82 | 83 | #pragma mark - 84 | #pragma mark Introduced OS 10.8.5 85 | #pragma mark - 86 | 87 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 && __MAC_OS_X_VERSION_MIN_REQUIRED < 1085 88 | __attribute__((visibility("hidden"))) 89 | UInt32 CLASS::GetMinimumIdlePowerState(void) 90 | { 91 | return super::GetMinimumIdlePowerState(); 92 | } 93 | 94 | __attribute__((visibility("hidden"))) 95 | IOReturn CLASS::UpdateDeviceAddress(USBDeviceAddress oldDeviceAddress, USBDeviceAddress newDeviceAddress, UInt8 speed, USBDeviceAddress hubAddress, int port) 96 | { 97 | return super::UpdateDeviceAddress(oldDeviceAddress, newDeviceAddress, speed, hubAddress, port); 98 | } 99 | 100 | __attribute__((visibility("hidden"))) 101 | UInt64 CLASS::GetErrata64Bits(UInt16 vendorID, UInt16 deviceID, UInt16 revisionID) 102 | { 103 | return super::GetErrata64Bits(vendorID, deviceID, revisionID); 104 | } 105 | #endif 106 | 107 | #endif //0 -------------------------------------------------------------------------------- /Compatibility.h: -------------------------------------------------------------------------------- 1 | // 2 | // Compatibility.h 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on September 10th 2014. 6 | // Copyright (c) 2014 Zenith432. All rights reserved. 7 | // 8 | // 9 | 10 | #if 0 11 | 12 | #ifndef GenericUSBXHCI_Compatibility_h 13 | #define GenericUSBXHCI_Compatibility_h 14 | 15 | /* 16 | * Introduced OS 10.10 17 | */ 18 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 && __MAC_OS_X_VERSION_MIN_REQUIRED < 101000 19 | bool init(IORegistryEntry* from, IORegistryPlane const* inPlane); 20 | IOReturn GetRootHubPowerExitLatencies(IOUSBHubExitLatencies** latencies); 21 | IOReturn CheckPowerModeBeforeGatedCall(char* fromStr, OSObject* token, UInt32 options); 22 | #endif 23 | 24 | /* 25 | * Introduced OS 10.9 26 | */ 27 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 && __MAC_OS_X_VERSION_MIN_REQUIRED < 1090 28 | IOReturn updateReport(IOReportChannelList* channels, IOReportUpdateAction action, void* result, void* destination); 29 | bool DoNotPowerOffPortsOnStop(void); 30 | IOReturn configureReport(IOReportChannelList* channels, IOReportConfigureAction action, void* result, void* destination); 31 | #endif 32 | 33 | /* 34 | * Introduced OS 10.8.5 35 | */ 36 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 && __MAC_OS_X_VERSION_MIN_REQUIRED < 1085 37 | UInt32 GetMinimumIdlePowerState(void); 38 | IOReturn UpdateDeviceAddress(USBDeviceAddress oldDeviceAddress, USBDeviceAddress newDeviceAddress, UInt8 speed, USBDeviceAddress hubAddress, int port); 39 | UInt64 GetErrata64Bits(UInt16 vendorID, UInt16 deviceID, UInt16 revisionID); 40 | #endif 41 | 42 | #endif 43 | 44 | #endif //0 -------------------------------------------------------------------------------- /Completer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Completer.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on October 18th 2013. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | // 9 | 10 | #include "GenericUSBXHCI.h" 11 | #include "Completer.h" 12 | 13 | #define MAX_FREE_RETENTION 16 14 | 15 | __attribute__((visibility("hidden"))) 16 | bool Completer::AddItem(IOUSBCompletion const* pCompletion, IOReturn status, uint32_t actualByteCount, bool allowImmediate) 17 | { 18 | CompleterItem* pItem; 19 | 20 | if (!pCompletion) 21 | return true; 22 | if (freeHead) { 23 | pItem = freeHead; 24 | if (freeHead == freeTail) { 25 | freeHead = 0; 26 | freeTail = 0; 27 | } else 28 | freeHead = pItem->next; 29 | --freeCount; 30 | } else { 31 | pItem = static_cast(IOMalloc(sizeof *pItem)); 32 | if (!pItem) { 33 | if (allowImmediate) { 34 | if (owner) 35 | owner->Complete(*pCompletion, status, actualByteCount); 36 | return true; 37 | } 38 | return false; 39 | } 40 | } 41 | pItem->completion = *pCompletion; 42 | pItem->status = status; 43 | pItem->actualByteCount = actualByteCount; 44 | if (activeTail) 45 | activeTail->next = pItem; 46 | else 47 | activeHead = pItem; 48 | activeTail = pItem; 49 | /* 50 | * Note: If flushing, we're already executing inside 51 | * InternalFlush, so no need to reschedule. 52 | */ 53 | if (!flushing && owner) 54 | owner->ScheduleEventSource(); 55 | return true; 56 | } 57 | 58 | __attribute__((visibility("hidden"))) 59 | void Completer::InternalFlush(void) 60 | { 61 | CompleterItem* pItem; 62 | flushing = true; 63 | pItem = activeHead; 64 | do { 65 | if (pItem == activeTail) { 66 | activeHead = 0; 67 | activeTail = 0; 68 | } else 69 | activeHead = pItem->next; 70 | if (freeCount < MAX_FREE_RETENTION) { 71 | if (freeTail) 72 | freeTail->next = pItem; 73 | else 74 | freeHead = pItem; 75 | freeTail = pItem; 76 | ++freeCount; 77 | /* 78 | * Note: pItem is on free list and may be reused inside Complete, 79 | * but its content is loaded on stack before the call. 80 | */ 81 | if (owner) 82 | owner->Complete(pItem->completion, pItem->status, pItem->actualByteCount); 83 | } else { 84 | if (owner) 85 | owner->Complete(pItem->completion, pItem->status, pItem->actualByteCount); 86 | IOFree(pItem, sizeof *pItem); 87 | } 88 | } while ((pItem = activeHead)); 89 | flushing = false; 90 | } 91 | 92 | __attribute__((visibility("hidden"))) 93 | void Completer::Finalize(void) 94 | { 95 | CompleterItem* pNext; 96 | while (activeHead) { 97 | if (activeHead == activeTail) 98 | pNext = 0; 99 | else 100 | pNext = activeHead->next; 101 | IOFree(activeHead, sizeof *pNext); 102 | activeHead = pNext; 103 | } 104 | activeTail = 0; 105 | while (freeHead) { 106 | if (freeHead == freeTail) 107 | pNext = 0; 108 | else 109 | pNext = freeHead->next; 110 | IOFree(freeHead, sizeof *pNext); 111 | freeHead = pNext; 112 | } 113 | freeTail = 0; 114 | freeCount = 0; 115 | flushing = false; 116 | } 117 | -------------------------------------------------------------------------------- /Completer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Completer.h 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on October 18th 2013. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | // 9 | 10 | #ifndef GenericUSBXHCI_Completer_h 11 | #define GenericUSBXHCI_Completer_h 12 | 13 | #include 14 | 15 | class GenericUSBXHCI; 16 | 17 | class Completer 18 | { 19 | private: 20 | struct CompleterItem 21 | { 22 | IOUSBCompletion completion; 23 | IOReturn status; 24 | uint32_t actualByteCount; 25 | CompleterItem* next; 26 | }; 27 | 28 | GenericUSBXHCI* owner; 29 | CompleterItem* activeHead; 30 | CompleterItem* activeTail; 31 | CompleterItem* freeHead; 32 | CompleterItem* freeTail; 33 | uint32_t freeCount; 34 | bool flushing; 35 | 36 | void InternalFlush(void); 37 | 38 | public: 39 | __attribute__((always_inline)) 40 | void setOwner(GenericUSBXHCI* owner) { this->owner = owner; } 41 | bool AddItem(IOUSBCompletion const*, IOReturn, uint32_t, bool); 42 | __attribute__((always_inline)) 43 | void Flush(void) { if (activeHead && !flushing) InternalFlush(); } 44 | void Finalize(void); 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /Endpoints.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Endpoints.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on January 26th, 2013. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCI.h" 10 | #include "Async.h" 11 | #include "Isoch.h" 12 | #include "XHCITypes.h" 13 | 14 | #define CLASS GenericUSBXHCI 15 | #define super IOUSBControllerV3 16 | 17 | #pragma mark - 18 | #pragma mark Endpoints 19 | #pragma mark - 20 | 21 | __attribute__((visibility("hidden"))) 22 | IOReturn CLASS::CreateBulkEndpoint(uint8_t functionNumber, uint8_t endpointNumber, uint8_t direction, 23 | uint16_t maxPacketSize, uint32_t maxStream, uint32_t maxBurst) 24 | { 25 | uint8_t slot, endpoint; 26 | 27 | slot = GetSlotID(functionNumber); 28 | if (!slot) 29 | return kIOReturnInternalError; 30 | endpoint = TranslateEndpoint(endpointNumber, direction); 31 | if (endpoint < 2U || endpoint >= kUSBMaxPipes) 32 | return kIOReturnBadArgument; 33 | return CreateEndpoint(slot, endpoint, maxPacketSize, 0, 34 | (direction == kUSBIn) ? BULK_IN_EP : BULK_OUT_EP, maxStream, maxBurst, 0U, 0); 35 | } 36 | 37 | __attribute__((visibility("hidden"))) 38 | IOReturn CLASS::CreateInterruptEndpoint(int16_t functionAddress, int16_t endpointNumber, uint8_t direction, int16_t speed, 39 | uint16_t maxPacketSize, int16_t pollingRate, uint32_t maxBurst) 40 | { 41 | uint8_t slot, endpoint; 42 | int16_t intervalExponent; 43 | 44 | if (functionAddress == _hub3Address || functionAddress == _hub2Address) { 45 | WRITE_V3EXPANSION(_rootHubPollingRate32, pollingRate > 15 ? 4096U : (pollingRate > 4 ? (1U << (pollingRate - 4)) : 1U)); 46 | return kIOReturnSuccess; 47 | } 48 | if (!functionAddress) 49 | return kIOReturnInternalError; 50 | slot = GetSlotID(functionAddress); 51 | if (!slot) 52 | return kIOReturnInternalError; 53 | endpoint = TranslateEndpoint(endpointNumber, direction); 54 | if (endpoint < 2U || endpoint >= kUSBMaxPipes) 55 | return kIOReturnBadArgument; 56 | if (speed > kUSBDeviceSpeedFull) 57 | intervalExponent = pollingRate > 15 ? 15 : (pollingRate > 1 ? (pollingRate - 1) : 0); 58 | else { 59 | if (pollingRate <= 0) 60 | return kIOReturnInternalError; 61 | intervalExponent = static_cast(34 - __builtin_clz(static_cast(pollingRate))); 62 | if (intervalExponent > 11) 63 | intervalExponent = 11; 64 | } 65 | return CreateEndpoint(slot, endpoint, maxPacketSize, intervalExponent, 66 | (direction == kUSBIn) ? INT_IN_EP : INT_OUT_EP, 0U, maxBurst, 0U, 0); 67 | } 68 | 69 | /* 70 | * Allowed ranges are 71 | * slot: 1 - _numSlots (max 255) 72 | * endpoint: 2 - 31 73 | * maxPacketSize: 1 - 1024 74 | * intervalExplonent: 0 - 15 (the interval is 2 ^ intervalExponent microframes) 75 | * endpointType: 1 - 7 (see XHCITypes.h) 76 | * maxStream: Either 0, or (1 + maxStream) is a power-of-2 between 4 and min(_maxPSASize, kMaxStreamsAllowed) 77 | * maxBurst: 0 - 15 78 | * multiple: 0 - 2 79 | */ 80 | __attribute__((visibility("hidden"))) 81 | IOReturn CLASS::CreateEndpoint(int32_t slot, int32_t endpoint, uint16_t maxPacketSize, int16_t intervalExponent, 82 | int32_t endpointType, uint32_t maxStream, uint32_t maxBurst, 83 | uint8_t multiple, void* pIsochEndpoint) 84 | { 85 | ContextStruct *pContext, *pEpContext; 86 | ringStruct* pRing; 87 | GenericUSBXHCIIsochEP* _pIsochEndpoint; 88 | uint32_t numPagesInRingQueue, mask; 89 | int32_t retFromCMD; 90 | IOReturn rc; 91 | uint8_t epState; 92 | TRBStruct localTrb = { 0 }; 93 | 94 | if (gux_log_level >= 2) 95 | IOLog("%s: slot %d ep %d maxPacketSize %u interval %d epType %d maxStream %u maxBurst %u multiple %u\n", __FUNCTION__, 96 | slot, endpoint, maxPacketSize, intervalExponent, endpointType, maxStream, maxBurst, multiple); 97 | _pIsochEndpoint = (endpointType | CTRL_EP) == ISOC_IN_EP ? static_cast(pIsochEndpoint) : 0; 98 | numPagesInRingQueue = _pIsochEndpoint ? _pIsochEndpoint->numPagesInRingQueue : 1U; 99 | /* 100 | * Note: For Isoch, this already checked in CreateIsochEndpoint 101 | */ 102 | if (!_pIsochEndpoint && 103 | _numEndpoints >= _maxNumEndpoints) 104 | return kIOUSBEndpointCountExceeded; 105 | #if 0 106 | pEpContext = GetSlotContext(slot, endpoint); 107 | epState = static_cast(XHCI_EPCTX_0_EPSTATE_GET(pEpContext->_e.dwEpCtx0)); 108 | pRing = (epState != EP_STATE_DISABLED) ? GetRing(slot, endpoint, maxStream) : 0; 109 | /* 110 | * Note: 111 | * It is just plain wrong for software to try and calculate available periodic 112 | * bandwidth by itself. USB3 allows hubs to allocate bandwidth between ports in 113 | * arbitrary ways, and only the hub (or root hub) knows the bandwidth. 114 | * The right thing is to try configure the endpoint, and return an error 115 | * if the bandwidth allocation fails. It is an option in such a case 116 | * to see if BNY capability is supported. If so, issue a negotiate-bandwidth 117 | * command. The xHC then notifies software on event rings which 118 | * other slots are sharing bandwidth that this endpoint wants. Up-stack 119 | * drivers can then be asked to reconfigure other slots to use less 120 | * periodic bandwidth if they have available configuration desciptors 121 | * to do so. Needless to say, this is very complex, requires wide 122 | * support in IOUSBFamily, and most devices would have no other 123 | * configurations to give up bandwidth anyhow (why should they?) 124 | * Another option is to issue GetPortBandwidth command to 125 | * calculate available periodic bandwidth in advance, see it it's 126 | * enough. Unfortunately, said command only returns a relative 127 | * percentage which can only be used for a rough estimate of 128 | * available bandwidth, not exact byte count. W/o an exact byte-count 129 | * it is pointless for the driver to pre-empt the xHC's complex 130 | * internal bookkeeping done to reserve bandwidth for periodic endpoints. 131 | * Addendum: This static check is done because Intel Series 7 chipset 132 | * does not do its own bandwidth allocation. Instead, it lets the 133 | * periodic endpoints be configured, and any bandwidth shortage 134 | * later shows up as errors during transfers. 135 | */ 136 | if (epState == EP_STATE_DISABLED || 137 | XHCI_EPCTX_1_MAXP_SIZE_GET(pEpContext->_e.dwEpCtx1) < maxPacketSize) { 138 | rc = CheckPeriodicBandwidth(slot, 139 | endpoint, 140 | maxPacketSize, 141 | intervalExponent, 142 | endpointType, 143 | maxStream, 144 | maxBurst, 145 | multiple); 146 | if (rc != kIOReturnSuccess) 147 | return rc; 148 | } 149 | if (!pRing) { 150 | #endif 151 | pRing = CreateRing(slot, endpoint, maxStream); 152 | if (!pRing) 153 | return kIOReturnNoMemory; /* Note: originally kIOReturnBadArgument */ 154 | #if 0 155 | } 156 | #endif 157 | if (_pIsochEndpoint) { 158 | if (pRing->isochEndpoint) { 159 | if (pRing->isochEndpoint != _pIsochEndpoint) 160 | return kIOReturnInternalError; 161 | } else 162 | pRing->isochEndpoint = _pIsochEndpoint; 163 | } else { 164 | if (pRing->asyncEndpoint) { 165 | if (!pRing->asyncEndpoint->checkOwnership(this, pRing)) 166 | return kIOReturnInternalError; 167 | pRing->asyncEndpoint->setParameters(maxPacketSize, maxBurst, multiple); 168 | } else { 169 | pRing->asyncEndpoint = XHCIAsyncEndpoint::withParameters(this, pRing, maxPacketSize, maxBurst, multiple); 170 | if (!pRing->asyncEndpoint) 171 | return kIOReturnNoMemory; 172 | static_cast(__sync_fetch_and_add(&_numEndpoints, 1)); 173 | } 174 | } 175 | pRing->epType = static_cast(endpointType); 176 | pRing->nextIsocFrame = 0ULL; 177 | pRing->returnInProgress = false; 178 | pRing->deleteInProgress = false; 179 | pRing->needsDoorbell = false; 180 | GetInputContext(); 181 | pContext = GetInputContextPtr(); 182 | pEpContext = GetSlotContext(slot, endpoint); 183 | epState = static_cast(XHCI_EPCTX_0_EPSTATE_GET(pEpContext->_e.dwEpCtx0)); 184 | mask = XHCI_INCTX_1_ADD_MASK(endpoint); 185 | switch (epState) { 186 | case EP_STATE_DISABLED: 187 | break; 188 | case EP_STATE_RUNNING: 189 | StopEndpoint(slot, endpoint); 190 | if (XHCI_EPCTX_0_EPSTATE_GET(pEpContext->_e.dwEpCtx0) == EP_STATE_HALTED) 191 | ResetEndpoint(slot, endpoint); 192 | default: 193 | pContext->_ic.dwInCtx0 = mask; 194 | break; 195 | } 196 | mask |= XHCI_INCTX_1_ADD_MASK(0U); 197 | pContext->_ic.dwInCtx1 = mask; 198 | pContext = GetInputContextPtr(1); 199 | *pContext = *GetSlotContext(slot); 200 | if (static_cast(XHCI_SCTX_0_CTX_NUM_GET(pContext->_s.dwSctx0)) < endpoint) { 201 | pContext->_s.dwSctx0 &= ~XHCI_SCTX_0_CTX_NUM_SET(0x1FU); 202 | pContext->_s.dwSctx0 |= XHCI_SCTX_0_CTX_NUM_SET(endpoint); 203 | } 204 | pContext->_s.dwSctx0 &= ~(1U << 24); 205 | pContext->_s.dwSctx3 = 0U; 206 | bzero(&pContext->_s.pad, sizeof pContext->_s.pad); 207 | pEpContext = GetInputContextPtr(1 + endpoint); 208 | pEpContext->_e.dwEpCtx0 |= XHCI_EPCTX_0_IVAL_SET(static_cast(intervalExponent)); 209 | if ((endpointType | CTRL_EP) != ISOC_IN_EP) 210 | pEpContext->_e.dwEpCtx1 |= XHCI_EPCTX_1_CERR_SET(3U); 211 | pEpContext->_e.dwEpCtx1 |= XHCI_EPCTX_1_EPTYPE_SET(endpointType); 212 | pEpContext->_e.dwEpCtx0 |= XHCI_EPCTX_0_MULT_SET(static_cast(multiple)); 213 | pEpContext->_e.dwEpCtx1 |= XHCI_EPCTX_1_MAXB_SET(maxBurst); 214 | pEpContext->_e.dwEpCtx1 |= XHCI_EPCTX_1_MAXP_SIZE_SET(static_cast(maxPacketSize)); 215 | if (pRing->md) { 216 | if (maxStream > 1U) { 217 | ReleaseInputContext(); 218 | return kIOReturnNoMemory; 219 | } 220 | ReinitTransferRing(slot, endpoint, 0U); 221 | } else { 222 | if (maxStream > 1U) 223 | rc = AllocStreamsContextArray(pRing, maxStream); 224 | else 225 | rc = AllocRing(pRing, numPagesInRingQueue); 226 | if (rc != kIOReturnSuccess) { 227 | ReleaseInputContext(); 228 | if (_pIsochEndpoint) 229 | pRing->isochEndpoint = 0; 230 | return kIOReturnNoMemory; 231 | } 232 | if (_pIsochEndpoint) 233 | _pIsochEndpoint->pRing = pRing; 234 | } 235 | pEpContext->_e.qwEpCtx2 = (pRing->physAddr + pRing->dequeueIndex * sizeof *pRing->ptr) & XHCI_EPCTX_2_TR_DQ_PTR_MASK; 236 | if (maxStream > 1U) { 237 | pEpContext->_e.dwEpCtx0 |= XHCI_EPCTX_0_LSA_SET(1U); 238 | pEpContext->_e.dwEpCtx0 |= XHCI_EPCTX_0_MAXP_STREAMS_SET(static_cast(__builtin_ctz(maxStream + 1U) - 1)); 239 | } else { 240 | if (pRing->cycleState) 241 | pEpContext->_e.qwEpCtx2 |= 1ULL; 242 | else 243 | pEpContext->_e.qwEpCtx2 &= ~1ULL; 244 | } 245 | pEpContext->_e.dwEpCtx4 |= XHCI_EPCTX_4_AVG_TRB_LEN_SET(static_cast(maxPacketSize)); 246 | /* 247 | * Note: For SS periodic endpoints, Max ESIT Payload should be taken 248 | * from the SS endpoint companion descriptor, wBytesPerInterval, not 249 | * calculated. Unfortunately, IOUSBFamily does not pass this parameter. 250 | * The value below is the maximum allowed, which ensures the endpoint 251 | * can operate at its max throughput, but also results in over-provisioning 252 | * of bandwidth, which can cause the configure-endpoint command to 253 | * be rejected with an insufficient-bandwidth error. 254 | */ 255 | if ((endpointType | CTRL_EP) == ISOC_IN_EP || 256 | (endpointType | CTRL_EP) == INT_IN_EP) 257 | pEpContext->_e.dwEpCtx4 |= XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(maxPacketSize * (1U + maxBurst) * (1U + multiple)); 258 | SetTRBAddr64(&localTrb, _inputContext.physAddr); 259 | localTrb.d |= XHCI_TRB_3_SLOT_SET(static_cast(slot)); 260 | retFromCMD = WaitForCMD(&localTrb, XHCI_TRB_TYPE_CONFIGURE_EP, 0); 261 | ReleaseInputContext(); 262 | if (retFromCMD == -1) 263 | return kIOReturnInternalError; 264 | if (retFromCMD > -1000) 265 | return kIOReturnSuccess; 266 | if (retFromCMD == -1000 - XHCI_TRB_ERROR_RESOURCE) 267 | return kIOUSBEndpointCountExceeded; 268 | if (retFromCMD == -1000 - XHCI_TRB_ERROR_PARAMETER || 269 | retFromCMD == -1000 - XHCI_TRB_ERROR_TRB) { 270 | #if 0 271 | PrintContext(GetInputContextPtr()); 272 | PrintContext(GetInputContextPtr(1)); 273 | PrintContext(GetInputContextPtr(1 + endpoint)); 274 | #endif 275 | } 276 | return kIOReturnInternalError; 277 | } 278 | 279 | __attribute__((visibility("hidden"))) 280 | IOReturn CLASS::StartEndpoint(int32_t slot, int32_t endpoint, uint16_t streamId) 281 | { 282 | #if 0 283 | /* 284 | * Added Mavericks 285 | */ 286 | ringStruct* pRing = GetRing(slot, endpoint, streamId); 287 | if (pRing && pRing->needsSetTRDQPtr) 288 | SetTRDQPtr(slot, endpoint, streamId, pRing->dequeueIndex); 289 | #endif 290 | Write32Reg(&_pXHCIDoorbellRegisters[slot], (static_cast(streamId) << 16) | (static_cast(endpoint) & 0xFFU)); 291 | return kIOReturnSuccess; 292 | } 293 | 294 | __attribute__((visibility("hidden"))) 295 | void CLASS::StopEndpoint(int32_t slot, int32_t endpoint, bool suspend) 296 | { 297 | TRBStruct localTrb = { 0 }; 298 | int32_t retFromCMD; 299 | 300 | #if 0 301 | /* 302 | * Note: UpdateTimeouts is the only consumer of stopTrb 303 | */ 304 | ClearStopTDs(slot, endpoint); 305 | #endif 306 | localTrb.d |= XHCI_TRB_3_SLOT_SET(slot); 307 | localTrb.d |= XHCI_TRB_3_EP_SET(endpoint); 308 | if (suspend) 309 | localTrb.d |= XHCI_TRB_3_SUSP_EP_BIT; 310 | retFromCMD = WaitForCMD(&localTrb, XHCI_TRB_TYPE_STOP_EP, 0); 311 | if (_vendorID == kVendorIntel && retFromCMD == -1000 - 196) // Intel CC_NOSTOP 312 | SetNeedsReset(slot, true); 313 | } 314 | 315 | __attribute__((visibility("hidden"))) 316 | void CLASS::ResetEndpoint(int32_t slot, int32_t endpoint, bool TSP) 317 | { 318 | TRBStruct localTrb = { 0 }; 319 | 320 | localTrb.d |= XHCI_TRB_3_SLOT_SET(slot); 321 | localTrb.d |= XHCI_TRB_3_EP_SET(endpoint); 322 | if (TSP) 323 | localTrb.d |= XHCI_TRB_3_PRSV_BIT; 324 | WaitForCMD(&localTrb, XHCI_TRB_TYPE_RESET_EP, 0); 325 | } 326 | 327 | __attribute__((visibility("hidden"))) 328 | uint32_t CLASS::QuiesceEndpoint(int32_t slot, int32_t endpoint) 329 | { 330 | uint32_t epState; 331 | ContextStruct volatile* pContext; 332 | 333 | #if 0 334 | /* 335 | * Note: Added Mavericks 336 | */ 337 | if (!_controllerAvailable) 338 | return EP_STATE_DISABLED; 339 | #endif 340 | #if 0 341 | /* 342 | * Note: UpdateTimeouts is the only consumer of stopTrb 343 | */ 344 | ClearStopTDs(slot, endpoint); 345 | #endif 346 | pContext = GetSlotContext(slot, endpoint); 347 | epState = XHCI_EPCTX_0_EPSTATE_GET(pContext->_e.dwEpCtx0); 348 | switch (epState) { 349 | case EP_STATE_RUNNING: 350 | StopEndpoint(slot, endpoint); 351 | if (XHCI_EPCTX_0_EPSTATE_GET(pContext->_e.dwEpCtx0) != EP_STATE_HALTED) 352 | break; 353 | epState = EP_STATE_HALTED; 354 | case EP_STATE_HALTED: 355 | ResetEndpoint(slot, endpoint); 356 | break; 357 | } 358 | return epState; 359 | } 360 | 361 | __attribute__((visibility("hidden"))) 362 | bool CLASS::checkEPForTimeOuts(int32_t slot, int32_t endpoint, uint32_t streamId, uint32_t frameNumber, bool abortAll) 363 | { 364 | ringStruct* pRing; 365 | XHCIAsyncEndpoint* pAsyncEp; 366 | ContextStruct* pEpContext; 367 | uint32_t ndto; 368 | uint16_t dq; 369 | bool stopped = false; 370 | 371 | pRing = GetRing(slot, endpoint, streamId); 372 | if (!pRing) 373 | return false; 374 | dq = pRing->dequeueIndex; 375 | if (!abortAll && (pRing->lastSeenDequeueIndex != dq || pRing->enqueueIndex == dq)) { 376 | pRing->lastSeenDequeueIndex = dq; 377 | return false; 378 | } 379 | /* 380 | * Note: Isoch Endpoints are ruled out in CheckSlotForTimeouts 381 | */ 382 | pAsyncEp = pRing->asyncEndpoint; 383 | if (!pAsyncEp || !pAsyncEp->scheduledHead) 384 | return false; 385 | if (abortAll) 386 | pEpContext = GetSlotContext(slot, endpoint); 387 | else if (pAsyncEp->NeedTimeouts()) { 388 | pEpContext = GetSlotContext(slot, endpoint); 389 | switch (XHCI_EPCTX_1_EPTYPE_GET(pEpContext->_e.dwEpCtx1)) { 390 | case BULK_OUT_EP: 391 | case CTRL_EP: 392 | case BULK_IN_EP: 393 | break; 394 | default: 395 | return false; 396 | } 397 | } else 398 | return false; 399 | if (pAsyncEp->scheduledHead->command) 400 | ndto = pAsyncEp->scheduledHead->command->GetNoDataTimeout(); 401 | else 402 | ndto = 0U; 403 | ClearStopTDs(slot, endpoint); 404 | if (ndto) 405 | switch (XHCI_EPCTX_0_EPSTATE_GET(pEpContext->_e.dwEpCtx0)) { 406 | case EP_STATE_DISABLED: 407 | case EP_STATE_STOPPED: 408 | break; 409 | case EP_STATE_RUNNING: 410 | if (!streamId || abortAll) { 411 | StopEndpoint(slot, endpoint); 412 | if (XHCI_EPCTX_0_EPSTATE_GET(pEpContext->_e.dwEpCtx0) == EP_STATE_HALTED) 413 | ResetEndpoint(slot, endpoint); 414 | stopped = true; 415 | } 416 | break; 417 | default: 418 | #if 0 419 | PrintContext(GetSlotContext(slot)); 420 | PrintContext(pEpContext); 421 | #endif 422 | break; 423 | } 424 | pAsyncEp->UpdateTimeouts(abortAll, frameNumber, stopped); 425 | return stopped; 426 | } 427 | 428 | __attribute__((visibility("hidden"))) 429 | bool CLASS::IsIsocEP(int32_t slot, int32_t endpoint) 430 | { 431 | uint32_t epType; 432 | ContextStruct* pContext = GetSlotContext(slot, endpoint); 433 | if (!pContext || 434 | XHCI_EPCTX_0_EPSTATE_GET(pContext->_e.dwEpCtx0) == EP_STATE_DISABLED) 435 | return false; 436 | epType = XHCI_EPCTX_1_EPTYPE_GET(pContext->_e.dwEpCtx1); 437 | return epType == ISOC_OUT_EP || epType == ISOC_IN_EP; 438 | } 439 | 440 | __attribute__((visibility("hidden"))) 441 | void CLASS::ClearEndpoint(int32_t slot, int32_t endpoint) 442 | { 443 | ContextStruct *pContext; 444 | TRBStruct localTrb = { 0 }; 445 | int32_t retFromCMD; 446 | 447 | GetInputContext(); 448 | pContext = GetInputContextPtr(); 449 | pContext->_ic.dwInCtx0 = XHCI_INCTX_0_DROP_MASK(endpoint); 450 | pContext->_ic.dwInCtx1 = XHCI_INCTX_1_ADD_MASK(endpoint) | XHCI_INCTX_1_ADD_MASK(0U); 451 | pContext = GetInputContextPtr(1); 452 | *pContext = *GetSlotContext(slot); 453 | PrintContext(pContext); 454 | pContext->_s.dwSctx0 &= ~(1U << 24); 455 | pContext->_s.dwSctx3 = 0U; 456 | bzero(&pContext->_s.pad[0], sizeof pContext->_s.pad[0]); 457 | pContext = GetInputContextPtr(1 + endpoint); 458 | *pContext = *GetSlotContext(slot, endpoint); 459 | bzero(&pContext->_e.pad[0], sizeof pContext->_e.pad[0]); 460 | PrintContext(pContext); 461 | SetTRBAddr64(&localTrb, _inputContext.physAddr); 462 | localTrb.d |= XHCI_TRB_3_SLOT_SET(slot); 463 | retFromCMD = WaitForCMD(&localTrb, XHCI_TRB_TYPE_CONFIGURE_EP, 0); 464 | ReleaseInputContext(); 465 | if (retFromCMD != -1 && retFromCMD > -1000) 466 | return; 467 | if (retFromCMD == -1000 - XHCI_TRB_ERROR_PARAMETER || 468 | retFromCMD == -1000 - XHCI_TRB_ERROR_TRB) { 469 | #if 0 470 | PrintContext(GetInputContextPtr()); 471 | PrintContext(GetInputContextPtr(1)); 472 | PrintContext(GetInputContextPtr(1 + endpoint)); 473 | #endif 474 | } 475 | } 476 | 477 | __attribute__((visibility("hidden"))) 478 | uint8_t CLASS::TranslateEndpoint(int16_t endpointNumber, int16_t direction) 479 | { 480 | return static_cast((2 * endpointNumber) | (direction ? 1 : 0)); 481 | } 482 | 483 | __attribute__((visibility("hidden"))) 484 | void CLASS::DeconfigureEndpoint(uint8_t slot, uint8_t endpoint, bool parkRing) 485 | { 486 | TRBStruct localTrb = { 0 }; 487 | ContextStruct* pContext; 488 | int32_t prevNumCtx, numCtx; 489 | if (QuiesceEndpoint(slot, endpoint) == EP_STATE_DISABLED) 490 | return; 491 | if (parkRing) 492 | ParkRing(slot, endpoint); 493 | if (endpoint < 2U) 494 | return; 495 | pContext = GetSlotContext(slot); 496 | prevNumCtx = numCtx = static_cast(XHCI_SCTX_0_CTX_NUM_GET(pContext->_s.dwSctx0)); 497 | if (numCtx == endpoint) { 498 | SlotStruct const* pSlot = ConstSlotPtr(slot); 499 | for (--numCtx; numCtx > 1 && pSlot->ringArrayForEndpoint[numCtx]->isInactive(); --numCtx); 500 | } 501 | if (numCtx == 1) { 502 | localTrb.d |= XHCI_TRB_3_SLOT_SET(static_cast(slot)) | XHCI_TRB_3_DCEP_BIT; 503 | WaitForCMD(&localTrb, XHCI_TRB_TYPE_CONFIGURE_EP, 0); 504 | return; 505 | } 506 | GetInputContext(); 507 | pContext = GetInputContextPtr(); 508 | pContext->_ic.dwInCtx0 = XHCI_INCTX_0_DROP_MASK(endpoint); 509 | if (numCtx != prevNumCtx) { 510 | pContext->_ic.dwInCtx1 = XHCI_INCTX_1_ADD_MASK(0U); 511 | pContext = GetInputContextPtr(1); 512 | *pContext = *GetSlotContext(slot); 513 | pContext->_s.dwSctx0 &= ~XHCI_SCTX_0_CTX_NUM_SET(0x1FU); 514 | pContext->_s.dwSctx0 |= XHCI_SCTX_0_CTX_NUM_SET(numCtx); 515 | pContext->_s.dwSctx0 &= ~(1U << 24); 516 | } 517 | SetTRBAddr64(&localTrb, _inputContext.physAddr); 518 | localTrb.d |= XHCI_TRB_3_SLOT_SET(static_cast(slot)); 519 | WaitForCMD(&localTrb, XHCI_TRB_TYPE_CONFIGURE_EP, 0); 520 | ReleaseInputContext(); 521 | } 522 | -------------------------------------------------------------------------------- /GenericUSBXHCI.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * GenericUSBXHCI.cpp 3 | * GenericUSBXHCI 4 | * 5 | * Created by Zenith432 on December 5th 2012. 6 | * Copyright 2012-2014 Zenith432. All rights reserved. 7 | * 8 | */ 9 | 10 | #include "GenericUSBXHCI.h" 11 | #include "GenericUSBXHCIUserClient.h" 12 | #include 13 | #include 14 | 15 | //REVIEW: avoids problem with Xcode 5.1.0 where -dead_strip eliminates these required symbols 16 | #include 17 | void* _org_rehabman_dontstrip_[] = 18 | { 19 | (void*)&OSKextGetCurrentIdentifier, 20 | (void*)&OSKextGetCurrentLoadTag, 21 | (void*)&OSKextGetCurrentVersionString, 22 | }; 23 | 24 | #define CLASS GenericUSBXHCI 25 | #define super IOUSBControllerV3 26 | OSDefineMetaClassAndFinalStructors(GenericUSBXHCI, IOUSBControllerV3); 27 | 28 | #ifndef kIOUSBMessageMuxFromEHCIToXHCI 29 | #define kIOUSBMessageMuxFromEHCIToXHCI iokit_usb_msg(0xe1) // 0xe00040e1 Message from the EHCI HC for ports mux transition from EHCI to XHCI 30 | #define kIOUSBMessageMuxFromXHCIToEHCI iokit_usb_msg(0xe2) // 0xe00040e2 Message from the EHCI HC for ports mux transition from XHCI to EHCI 31 | #endif 32 | 33 | static __used char const copyright[] = "Copyright 2012-2014 Zenith432"; 34 | 35 | /* 36 | * Courtesy RehabMan 37 | */ 38 | #define MakeKernelVersion(maj,min,rev) (static_cast((maj)<<16)|static_cast((min)<<8)|static_cast(rev)) 39 | 40 | #pragma mark - 41 | #pragma mark IOService 42 | #pragma mark - 43 | 44 | IOService* CLASS::probe(IOService* provider, SInt32* score) 45 | { 46 | uint32_t v; 47 | #if 0 48 | uint32_t thisKernelVersion = MakeKernelVersion(version_major, version_minor, version_revision); 49 | bool force11 = false; 50 | if (PE_parse_boot_argn("-gux_force11", &v, sizeof v)) 51 | force11 = true; 52 | if (!force11 && thisKernelVersion >= MakeKernelVersion(15, 0, 0)) { 53 | IOLog("GenericUSBXHCI not loading on OS 10.11 or later without -gux_force11\n"); 54 | return NULL; 55 | } 56 | #endif 57 | if (PE_parse_boot_argn("-gux_disable", &v, sizeof v)) 58 | return NULL; 59 | 60 | IOPCIDevice* pciDevice = OSDynamicCast(IOPCIDevice, provider); 61 | if (!pciDevice) 62 | return NULL; 63 | 64 | // don't load if blacklisted against particular vendor/device-id combinations 65 | UInt32 dvID = pciDevice->extendedConfigRead32(kIOPCIConfigVendorID); 66 | UInt16 vendor = dvID; 67 | UInt16 device = dvID>>16; 68 | // check Info.plist configuration 69 | char keyVendor[sizeof("vvvv")]; 70 | char keyBoth[sizeof("vvvv_dddd")]; 71 | snprintf(keyVendor, sizeof(keyVendor), "%04x", vendor); 72 | snprintf(keyBoth, sizeof(keyBoth), "%04x_%04x", vendor, device); 73 | OSDictionary* whitelist = OSDynamicCast(OSDictionary, getProperty("DeviceWhitelist")); 74 | OSDictionary* blacklist = OSDynamicCast(OSDictionary, getProperty("DeviceBlacklist")); 75 | if (!whitelist && !blacklist) { 76 | // default: don't load for Intel/Fresco Logic XHC 77 | if (0x8086 == vendor || 0x1b73 == vendor) 78 | return NULL; 79 | } 80 | else { 81 | // otherwise: always start if in whitelist, else check blacklist 82 | if ((!whitelist || !whitelist->getObject(keyBoth)) && 83 | blacklist && (blacklist->getObject(keyVendor) || blacklist->getObject(keyBoth))) { 84 | return NULL; 85 | } 86 | } 87 | 88 | return super::probe(provider, score); 89 | } 90 | 91 | bool CLASS::willTerminate(IOService* provider, IOOptionBits options) 92 | { 93 | if (_expansionData && _watchdogTimerActive && _watchdogUSBTimer) 94 | _watchdogUSBTimer->setTimeoutUS(1U); 95 | return super::willTerminate(provider, options); 96 | } 97 | 98 | bool CLASS::terminate(IOOptionBits options) 99 | { 100 | m_invalid_regspace = true; 101 | return super::terminate(options); 102 | } 103 | 104 | IOReturn CLASS::message(UInt32 type, IOService* provider, void* argument) 105 | { 106 | IOReturn rc; 107 | uint8_t controller; 108 | 109 | rc = super::message(type, provider, argument); 110 | switch (type) { 111 | case kIOUSBMessageHubPortDeviceDisconnected: 112 | if (m_invalid_regspace || 113 | (gUSBStackDebugFlags & kUSBDisableMuxedPortsMask) || 114 | !_device || 115 | isInactive() || 116 | !(_errataBits & kErrataIntelPortMuxing)) 117 | return rc; 118 | HCSelectWithMethod(static_cast(argument)); 119 | return rc; 120 | case kIOUSBMessageMuxFromEHCIToXHCI: 121 | controller = 1U; 122 | break; 123 | case kIOUSBMessageMuxFromXHCIToEHCI: 124 | controller = 0U; 125 | break; 126 | default: 127 | return kIOReturnUnsupported; 128 | } 129 | if (m_invalid_regspace || 130 | (gUSBStackDebugFlags & kUSBDisableMuxedPortsMask) || 131 | !_device || 132 | isInactive() || 133 | !(_errataBits & kErrataIntelPortMuxing)) 134 | return rc; 135 | HCSelect(static_cast(reinterpret_cast(argument)), controller); 136 | return rc; 137 | } 138 | 139 | unsigned long CLASS::maxCapabilityForDomainState(IOPMPowerFlags domainState) 140 | { 141 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 || defined(REHABMAN_UNIVERSAL_BUILD) 142 | uint8_t port, portLimit; 143 | uint32_t portSC; 144 | #endif 145 | unsigned long state = super::maxCapabilityForDomainState(domainState); 146 | if (!CHECK_FOR_MAVERICKS) 147 | return state; 148 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 || defined(REHABMAN_UNIVERSAL_BUILD) 149 | if (_wakingFromHibernation) 150 | return state; 151 | if (!(_errataBits & kErrataIntelLynxPoint)) 152 | return state; 153 | if (_myPowerState != kUSBPowerStateSleep) 154 | return state; 155 | if (state < kUSBPowerStateLowPower) 156 | return state; 157 | /* 158 | * Note: This check neutralizes the code below because PM backbone calls this method *before* 159 | * powering the parent IOPCIDevice on when coming back from Sleep to On. 160 | */ 161 | if (!_v3ExpansionData || !READ_V3EXPANSION(_parentDeviceON)) 162 | return state; 163 | port = READ_V3EXPANSION(_rootHubPortsSSStartRange) - 1U; 164 | portLimit = port + READ_V3EXPANSION(_rootHubNumPortsSS); 165 | for (; port < portLimit; ++port) { 166 | portSC = Read32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC); 167 | if (m_invalid_regspace) 168 | return state; 169 | if (!(portSC & XHCI_PS_WRC)) 170 | return state; 171 | } 172 | IOLog("XHCI: All ports have WRC bit set - reseting all of USB\n"); 173 | /* 174 | * Note: Tested and this code causes ejection messages on connected drives. 175 | */ 176 | ResetControllerState(); 177 | EnableAllEndpoints(true); 178 | state = kUSBPowerStateOff; 179 | _wakingFromHibernation = true; 180 | #endif 181 | return state; 182 | } 183 | 184 | IOReturn CLASS::newUserClient(task_t owningTask, void* securityID, UInt32 type, IOUserClient ** handler) 185 | { 186 | IOUserClient *client; 187 | 188 | if (type != kGUXUCType) 189 | return kIOReturnUnsupported; 190 | if (!handler) 191 | return kIOReturnBadArgument; 192 | 193 | client = OSTypeAlloc(GenericUSBXHCIUserClient); 194 | if (!client) 195 | return kIOReturnNoMemory; 196 | if (!client->initWithTask(owningTask, securityID, type)) { 197 | client->release(); 198 | return kIOReturnInternalError; 199 | } 200 | if (!client->attach(this)) { 201 | client->release(); 202 | return kIOReturnInternalError; 203 | } 204 | if (!client->start(this)) { 205 | client->detach(this); 206 | client->release(); 207 | return kIOReturnInternalError; 208 | } 209 | *handler = client; 210 | return kIOReturnSuccess; 211 | } 212 | 213 | #pragma mark - 214 | #pragma mark IORegistryEntry 215 | #pragma mark - 216 | 217 | bool CLASS::init(OSDictionary* dictionary) 218 | { 219 | /* 220 | * (64-bit/32-bit) 221 | * sizeof(USBXHCI) = 0x23B80/0x1A640 222 | * sizeof(IOUSBControllerV3) = 680/492 223 | * sizeof(IOUSBControllerV2) = 440/360 224 | * sizeof(IOUSBController) = 176/100 225 | * sizeof(IOUSBBus) = 136/80 226 | * 227 | * Inheritance 228 | * XHCIIsochEndpoint: public IOUSBControllerIsochEndpoint 229 | * XHCIIsochTransferDescriptor: public IOUSBControllerIsochListElement 230 | * XHCIAsyncEndpoint: public OSObject 231 | * XHCIAsyncTransferDescriptor: public OSObject 232 | * RootHubPortTable: public OSObject 233 | * TTBandwidthTable: public OSObject 234 | * 235 | * Compiler symbol options 236 | * LONG_RESET - enable complex reset sequence related to Intel Series 7 muxed ports 237 | * DEBOUNCING - enable port debounce code 238 | */ 239 | if (!super::init(dictionary)) { 240 | IOLog("%s: super::init failed\n", __FUNCTION__); 241 | return false; 242 | } 243 | _controllerSpeed = kUSBDeviceSpeedSuper; 244 | _isochScheduleLock = IOSimpleLockAlloc(); 245 | if (!_isochScheduleLock) { 246 | IOLog("%s: Unable to allocate SimpleLock\n", __FUNCTION__); 247 | return false; 248 | } 249 | _uimInitialized = false; 250 | _myBusState = kUSBBusStateReset; 251 | return true; 252 | } 253 | 254 | void CLASS::free(void) 255 | { 256 | /* 257 | * Note: _wdhLock is not freed in original code 258 | */ 259 | if (_isochScheduleLock) { 260 | IOSimpleLockFree(_isochScheduleLock); 261 | _isochScheduleLock = 0; 262 | } 263 | super::free(); 264 | } 265 | 266 | #pragma mark - 267 | #pragma mark Module Init 268 | #pragma mark - 269 | 270 | extern "C" 271 | { 272 | 273 | __attribute__((visibility("hidden"))) 274 | int gux_log_level = 1; 275 | 276 | __attribute__((visibility("hidden"))) 277 | int gux_options = 0; 278 | 279 | __attribute__((visibility("hidden"))) 280 | kern_return_t Startup(kmod_info_t* ki, void * d) 281 | { 282 | uint32_t v, thisKernelVersion; 283 | 284 | thisKernelVersion = MakeKernelVersion(version_major, version_minor, version_revision); 285 | if (thisKernelVersion < MakeKernelVersion(11, 4, 2)) { 286 | IOLog("OS 10.7.5 or later required for GenericUSBXHCI\n"); 287 | return KERN_FAILURE; 288 | } 289 | #ifndef REHABMAN_UNIVERSAL_BUILD 290 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 291 | if (thisKernelVersion < MakeKernelVersion(14, 0, 0)) { 292 | IOLog("OS 10.10.0 or later required for this build of GenericUSBXHCI\n"); 293 | return KERN_FAILURE; 294 | } 295 | #else 296 | if (thisKernelVersion >= MakeKernelVersion(14, 0, 0)) { 297 | IOLog("This build of GenericUSBXHCI is not compatible with OS 10.10\n"); 298 | return KERN_FAILURE; 299 | } 300 | #endif 301 | #endif 302 | #ifdef __LP64__ 303 | if (thisKernelVersion >= MakeKernelVersion(12, 5, 0)) 304 | gux_options |= GUX_OPTION_MAVERICKS; 305 | if (thisKernelVersion >= MakeKernelVersion(14, 0, 0)) 306 | gux_options |= GUX_OPTION_YOSEMITE; 307 | #endif 308 | if (PE_parse_boot_argn("-gux_nosleep", &v, sizeof v)) 309 | gux_options |= GUX_OPTION_NO_SLEEP; 310 | if (PE_parse_boot_argn("-gux_defer_usb2", &v, sizeof v)) 311 | gux_options |= GUX_OPTION_DEFER_INTEL_EHC_PORTS; 312 | if (PE_parse_boot_argn("-gux_nomsi", &v, sizeof v)) 313 | gux_options |= GUX_OPTION_NO_MSI; 314 | if (PE_parse_boot_argn("gux_log", &v, sizeof v)) 315 | gux_log_level = static_cast(v); 316 | return KERN_SUCCESS; 317 | } 318 | 319 | __attribute__((visibility("hidden"))) 320 | void ListOptions(PrintSink* pSink) 321 | { 322 | /* 323 | * Note: Keep this in sync with Startup 324 | */ 325 | pSink->print("Kernel Flags\n"); 326 | pSink->print(" -gux_nosleep: Disable XHCI suspend/resume method, and use reset-on-resume (forces USB Bus re-enumeration)\n"); 327 | pSink->print(" -gux_nomsi: Disable MSI and use pin interrupt (if available)\n"); 328 | pSink->print(" -gux_defer_usb2: For Intel Series 7/C210 or Intel Series 8/C220 - Switch USB 2.0 protocol ports from xHC to EHC\n"); 329 | pSink->print(" gux_log=n: Set logging level to n. Available levels 1 - normal, 2 - higher\n\n"); 330 | pSink->print("Properties in Info.plist personality\n"); 331 | pSink->print(" DisableUAS (boolean) - disables USB Attached SCSI\n"); 332 | pSink->print(" ASMediaEDLTAFix (boolean) - enables workaround for ASM 1042 EDTLA bug\n"); 333 | pSink->print(" UseLegacyInt (boolean) - override selection of pin interrupt or MSI\n"); 334 | pSink->print(" IntelDoze (boolean) - For Intel Series 7/C210 only - enables use of Doze mode\n"); 335 | } 336 | 337 | } 338 | -------------------------------------------------------------------------------- /GenericUSBXHCIEventSource.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // GenericUSBXHCIEventSource.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on October 27th 2013. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | // 9 | 10 | #include "GenericUSBXHCI.h" 11 | 12 | class GenericUSBXHCIEventSource : public IOEventSource 13 | { 14 | OSDeclareFinalStructors(GenericUSBXHCIEventSource); 15 | 16 | protected: 17 | bool checkForWork(); 18 | 19 | public: 20 | __attribute__((always_inline)) bool init(OSObject* owner) { return IOEventSource::init(owner, 0); } 21 | }; 22 | 23 | OSDefineMetaClassAndFinalStructors(GenericUSBXHCIEventSource, IOEventSource); 24 | 25 | bool GenericUSBXHCIEventSource::checkForWork() 26 | { 27 | GenericUSBXHCI* _owner = OSDynamicCast(GenericUSBXHCI, owner); 28 | if (_owner && !_owner->isInactive()) 29 | _owner->_completer.Flush(); 30 | return false; 31 | } 32 | 33 | __attribute__((visibility("hidden"))) 34 | IOReturn GenericUSBXHCI::InitializeEventSource(void) 35 | { 36 | GenericUSBXHCIEventSource* es = OSTypeAlloc(GenericUSBXHCIEventSource); 37 | if (!es) 38 | return kIOReturnNoMemory; 39 | if (!es->GenericUSBXHCIEventSource::init(this)) { 40 | es->release(); 41 | return kIOReturnNoMemory; 42 | } 43 | if (_workLoop) { 44 | IOReturn rc = _workLoop->addEventSource(es); 45 | if (rc != kIOReturnSuccess) { 46 | es->release(); 47 | return rc; 48 | } 49 | } 50 | _eventSource = es; 51 | return kIOReturnSuccess; 52 | } 53 | 54 | __attribute__((visibility("hidden"))) 55 | void GenericUSBXHCI::FinalizeEventSource(void) 56 | { 57 | if (!_eventSource) 58 | return; 59 | if (_workLoop) 60 | _workLoop->removeEventSource(_eventSource); 61 | _eventSource->release(); 62 | _eventSource = 0; 63 | } 64 | 65 | __attribute__((visibility("hidden"))) 66 | void GenericUSBXHCI::ScheduleEventSource(void) 67 | { 68 | if (!_eventSource) 69 | return; 70 | #if 1 71 | /* 72 | * Note: This optimization depends on implementation of IOWorkLoop. 73 | * If we're executing inside InterruptHandler(), since _eventSource 74 | * is added to WL after _filterInterruptSource, 75 | * _eventSource->checkForWork() will execute even w/o reschedule. 76 | */ 77 | if (_workLoop && _workLoop->onThread()) 78 | return; 79 | #endif 80 | _eventSource->enable(); // Note: twisted way of calling signalWorkAvailable() 81 | } 82 | -------------------------------------------------------------------------------- /GenericUSBXHCIUserClient.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // GenericUSBXHCIUserClient.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on February 3rd 2012. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCIUserClient.h" 10 | #include "GenericUSBXHCI.h" 11 | #include 12 | #include 13 | 14 | #define CLASS GenericUSBXHCIUserClient 15 | #define super IOUserClient 16 | OSDefineMetaClassAndFinalStructors(GenericUSBXHCIUserClient, IOUserClient); 17 | 18 | #pragma mark - 19 | #pragma mark Helpers 20 | #pragma mark - 21 | 22 | struct BufferedPrintSink : PrintSink 23 | { 24 | void* buffer; 25 | size_t position; 26 | size_t capacity; 27 | }; 28 | 29 | static 30 | void BufferedPrintSinkFunction(struct PrintSink* sink, const char* format, va_list args) 31 | { 32 | int chars; 33 | BufferedPrintSink* _sink = static_cast(sink); 34 | 35 | if (!_sink || !_sink->buffer || _sink->position >= _sink->capacity) 36 | return; 37 | chars = vsnprintf(static_cast(_sink->buffer) + _sink->position, 38 | _sink->capacity - _sink->position, 39 | format, args); 40 | _sink->position += chars; 41 | } 42 | 43 | static 44 | IOReturn MakeMemoryAndPrintSink(size_t capacity, IOBufferMemoryDescriptor** pMd, IOMemoryMap** pMap, BufferedPrintSink* pSink) 45 | { 46 | IOBufferMemoryDescriptor* md; 47 | IOMemoryMap* map; 48 | IOReturn rc; 49 | 50 | md = IOBufferMemoryDescriptor::inTaskWithOptions(0, 51 | kIOMemoryPageable | kIODirectionIn, 52 | capacity); 53 | if (!md) 54 | return kIOReturnNoMemory; 55 | rc = md->prepare(); 56 | if (rc != kIOReturnSuccess) { 57 | md->release(); 58 | return rc; 59 | } 60 | map = md->createMappingInTask(kernel_task, 0U, kIOMapAnywhere); 61 | if (!map) { 62 | md->complete(); 63 | md->release(); 64 | return kIOReturnVMError; 65 | } 66 | *pMd = md; 67 | *pMap = map; 68 | bzero(pSink, sizeof *pSink); 69 | pSink->printer = &BufferedPrintSinkFunction; 70 | pSink->buffer = reinterpret_cast(map->getVirtualAddress()); 71 | pSink->position = 0U; 72 | pSink->capacity = capacity; 73 | return kIOReturnSuccess; 74 | } 75 | 76 | #pragma mark - 77 | #pragma mark User Client 78 | #pragma mark - 79 | 80 | IOReturn CLASS::clientClose(void) 81 | { 82 | if (!terminate()) 83 | IOLog("GenericUSBXHCIUserClient::%s: terminate failed.\n", __FUNCTION__); 84 | return kIOReturnSuccess; 85 | } 86 | 87 | static 88 | IOReturn GatedPrintCapRegs(OSObject* owner, void* pSink, void*, void*, void*) 89 | { 90 | static_cast(owner)->PrintCapRegs(static_cast(pSink)); 91 | return kIOReturnSuccess; 92 | } 93 | 94 | static 95 | IOReturn GatedPrintRuntimeRegs(OSObject* owner, void* pSink, void*, void*, void*) 96 | { 97 | static_cast(owner)->PrintRuntimeRegs(static_cast(pSink)); 98 | return kIOReturnSuccess; 99 | } 100 | 101 | static 102 | IOReturn GatedPrintSlots(OSObject* owner, void* pSink, void*, void*, void*) 103 | { 104 | static_cast(owner)->PrintSlots(static_cast(pSink)); 105 | return kIOReturnSuccess; 106 | } 107 | 108 | static 109 | IOReturn GatedPrintEndpoints(OSObject* owner, void* pSink, void* slot, void*, void*) 110 | { 111 | static_cast(owner)->PrintEndpoints(static_cast(reinterpret_cast(slot)), 112 | static_cast(pSink)); 113 | return kIOReturnSuccess; 114 | } 115 | 116 | static 117 | IOReturn GatedPrintRootHubPortBandwidth(OSObject* owner, void* pSink, void*, void*, void*) 118 | { 119 | static_cast(owner)->PrintRootHubPortBandwidth(static_cast(pSink)); 120 | return kIOReturnSuccess; 121 | } 122 | 123 | extern "C" void ListOptions(PrintSink* pSink); 124 | 125 | IOReturn CLASS::clientMemoryForType(UInt32 type, IOOptionBits* options, IOMemoryDescriptor** memory) 126 | { 127 | IOBufferMemoryDescriptor* md; 128 | IOMemoryMap* kernelMap; 129 | GenericUSBXHCI* provider; 130 | BufferedPrintSink sink; 131 | IOReturn ret = kIOReturnUnsupported; 132 | 133 | *options = 0U; 134 | *memory = 0; 135 | switch (type & 255U) { 136 | case kGUXCapRegsDump: 137 | provider = OSDynamicCast(GenericUSBXHCI, getProvider()); 138 | if (!provider) 139 | break; 140 | ret = MakeMemoryAndPrintSink(PAGE_SIZE, &md, &kernelMap, &sink); 141 | if (ret != kIOReturnSuccess) 142 | break; 143 | provider->getWorkLoop()->runAction(GatedPrintCapRegs, provider, &sink); 144 | kernelMap->release(); 145 | md->complete(); 146 | *options = kIOMapReadOnly; 147 | *memory = md; 148 | ret = kIOReturnSuccess; 149 | break; 150 | case kGUXRunRegsDump: 151 | provider = OSDynamicCast(GenericUSBXHCI, getProvider()); 152 | if (!provider) 153 | break; 154 | ret = MakeMemoryAndPrintSink(PAGE_SIZE, &md, &kernelMap, &sink); 155 | if (ret != kIOReturnSuccess) 156 | break; 157 | provider->getWorkLoop()->runAction(GatedPrintRuntimeRegs, provider, &sink); 158 | kernelMap->release(); 159 | md->complete(); 160 | *options = kIOMapReadOnly; 161 | *memory = md; 162 | ret = kIOReturnSuccess; 163 | break; 164 | case kGUXSlotsDump: 165 | provider = OSDynamicCast(GenericUSBXHCI, getProvider()); 166 | if (!provider) 167 | break; 168 | ret = MakeMemoryAndPrintSink(PAGE_SIZE, &md, &kernelMap, &sink); 169 | if (ret != kIOReturnSuccess) 170 | break; 171 | provider->getWorkLoop()->runAction(GatedPrintSlots, provider, &sink); 172 | kernelMap->release(); 173 | md->complete(); 174 | *options = kIOMapReadOnly; 175 | *memory = md; 176 | ret = kIOReturnSuccess; 177 | break; 178 | case kGUXEndpointsDump: 179 | provider = OSDynamicCast(GenericUSBXHCI, getProvider()); 180 | if (!provider) 181 | break; 182 | ret = MakeMemoryAndPrintSink(PAGE_SIZE, &md, &kernelMap, &sink); 183 | if (ret != kIOReturnSuccess) 184 | break; 185 | provider->getWorkLoop()->runAction(GatedPrintEndpoints, provider, &sink, reinterpret_cast((type >> 8U) & 255U)); 186 | kernelMap->release(); 187 | md->complete(); 188 | *options = kIOMapReadOnly; 189 | *memory = md; 190 | ret = kIOReturnSuccess; 191 | break; 192 | case kGUXBandwidthDump: 193 | provider = OSDynamicCast(GenericUSBXHCI, getProvider()); 194 | if (!provider) 195 | break; 196 | ret = MakeMemoryAndPrintSink(PAGE_SIZE, &md, &kernelMap, &sink); 197 | if (ret != kIOReturnSuccess) 198 | break; 199 | provider->getWorkLoop()->runAction(GatedPrintRootHubPortBandwidth, provider, &sink); 200 | kernelMap->release(); 201 | md->complete(); 202 | *options = kIOMapReadOnly; 203 | *memory = md; 204 | ret = kIOReturnSuccess; 205 | break; 206 | case kGUXOptionsDump: 207 | ret = MakeMemoryAndPrintSink(PAGE_SIZE, &md, &kernelMap, &sink); 208 | if (ret != kIOReturnSuccess) 209 | break; 210 | ListOptions(&sink); 211 | kernelMap->release(); 212 | md->complete(); 213 | *options = kIOMapReadOnly; 214 | *memory = md; 215 | ret = kIOReturnSuccess; 216 | break; 217 | } 218 | return ret; 219 | } 220 | -------------------------------------------------------------------------------- /GenericUSBXHCIUserClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // GenericUSBXHCIUserClient.h 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on February 3rd 2012. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #ifndef __GenericUSBXHCIUserClient__ 10 | #define __GenericUSBXHCIUserClient__ 11 | 12 | #include 13 | 14 | #define EXPORT __attribute__((visibility("default"))) 15 | 16 | #define kGUXUCType 1U 17 | 18 | #define kGUXCapRegsDump 1U 19 | #define kGUXRunRegsDump 2U 20 | #define kGUXSlotsDump 3U 21 | #define kGUXEndpointsDump 4U 22 | #define kGUXBandwidthDump 5U 23 | #define kGUXOptionsDump 6U 24 | 25 | class EXPORT GenericUSBXHCIUserClient : public IOUserClient 26 | { 27 | OSDeclareFinalStructors(GenericUSBXHCIUserClient); 28 | 29 | public: 30 | IOReturn clientClose(void); 31 | IOReturn clientMemoryForType(UInt32 type, IOOptionBits* options, IOMemoryDescriptor** memory); 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | net.osx86.kexts.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | KEXT 17 | CFBundleShortVersionString 18 | ${MODULE_VERSION} 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${MODULE_VERSION} 23 | IOKitPersonalities 24 | 25 | GenericUSBXHCI 26 | 27 | RM,Version 28 | ${PRODUCT_NAME} ${MODULE_VERSION} 29 | RM,Build 30 | ${CONFIGURATION}-${LOGNAME} 31 | CFBundleIdentifier 32 | net.osx86.kexts.${PRODUCT_NAME:rfc1034identifier} 33 | IOClass 34 | GenericUSBXHCI 35 | #IOPCIPrimaryMatch 36 | 0x01941033 37 | IOPCIClassMatch 38 | 0x0c033000 39 | IOPCIPauseCompatible 40 | 41 | IOPCITunnelCompatible 42 | 43 | IOProviderClass 44 | IOPCIDevice 45 | IOUserClientClass 46 | IOUSBControllerUserClient 47 | IOProbeScore 48 | 1 49 | ASMediaEDLTAFix 50 | 51 | IntelDoze 52 | 53 | DeviceWhitelist 54 | 55 | 1b73_1000 56 | 0 57 | 58 | DeviceBlacklist 59 | 60 | 8086 61 | 0 62 | 1b73 63 | 0 64 | 65 | 66 | 67 | NSHumanReadableCopyright 68 | ${COPYRIGHT_NOTICE} 69 | OSBundleCompatibleVersion 70 | ${DYLIB_COMPATIBILITY_VERSION} 71 | OSBundleLibraries 72 | 73 | com.apple.iokit.IOPCIFamily 74 | 2.6 75 | com.apple.iokit.IOUSBFamily 76 | 5.0.0a1 77 | com.apple.kpi.iokit 78 | 10.6 79 | com.apple.kpi.libkern 80 | 10.6 81 | com.apple.kpi.mach 82 | 10.6 83 | com.apple.kpi.unsupported 84 | 10.6 85 | 86 | OSBundleRequired 87 | Root 88 | 89 | 90 | -------------------------------------------------------------------------------- /Isoch.h: -------------------------------------------------------------------------------- 1 | // 2 | // Isoch.h 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on March 12th, 2013. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #ifndef GenericUSBXHCI_Isoch_h 10 | #define GenericUSBXHCI_Isoch_h 11 | 12 | #include 13 | 14 | #define kMaxTransfersPerFrame 8 15 | #define kNumTDSlots 128 16 | 17 | class GenericUSBXHCIIsochTD; 18 | 19 | class GenericUSBXHCIIsochEP : public IOUSBControllerIsochEndpoint 20 | { 21 | OSDeclareFinalStructors(GenericUSBXHCIIsochEP); 22 | 23 | public: 24 | GenericUSBXHCIIsochTD* tdSlots[kNumTDSlots]; // 0x88 25 | struct ringStruct* pRing; // 0x488 26 | GenericUSBXHCIIsochTD volatile* savedDoneQueueHead; // 0x490 27 | uint32_t volatile producerCount; // 0x498 28 | uint32_t volatile consumerCount; // 0x49C 29 | IOSimpleLock* wdhLock; // 0x4A0 30 | uint64_t scheduledFrameNumber; // 0x4A8 31 | uint8_t maxBurst; // 0x4B0 32 | uint8_t multiple; // 0x4B1 33 | uint32_t numPagesInRingQueue; // 0x4B4 34 | uint16_t outSlot; // 0x4B8 35 | uint16_t boundOnPagesPerFrame; // 0x4BA 36 | uint8_t transfersPerTD; // 0x4BC 37 | uint16_t frameNumberIncrease; // 0x4BD - originally uint8_t, but can be up to 4096! 38 | uint8_t intervalExponent; // 0x4BE 39 | uint8_t speed; // 0x4BF 40 | bool schedulingDelayed; // 0x4C0 41 | bool volatile tdsScheduled; // 0x4C1 42 | bool continuousStream; // 0x4C2 43 | 44 | bool init(void); 45 | void free(void); 46 | }; 47 | 48 | class GenericUSBXHCIIsochTD : public IOUSBControllerIsochListElement 49 | { 50 | OSDeclareFinalStructors(GenericUSBXHCIIsochTD); 51 | 52 | public: 53 | IOUSBIsocCommand* command; // offset 0x70 54 | size_t transferOffset; // offset 0x78 55 | uint32_t firstTrbIndex[kMaxTransfersPerFrame]; // offset 0x80 56 | uint32_t trbCount[kMaxTransfersPerFrame]; // offset 0xA0 57 | bool statusUpdated[kMaxTransfersPerFrame]; // offset 0xC0 58 | TRBStruct eventTrb; // offset 0xC8 59 | bool newFrame; // offset 0xD8 60 | bool interruptThisTD; // offset 0xD9 61 | 62 | /* 63 | * Pure Virtual from IOUSBControllerIsochListElement 64 | */ 65 | void SetPhysicalLink(IOPhysicalAddress) {} 66 | IOPhysicalAddress GetPhysicalLink(void) { return 0U; } 67 | IOPhysicalAddress GetPhysicalAddrWithType(void) { return 0U; } 68 | IOReturn UpdateFrameList(AbsoluteTime); 69 | IOReturn Deallocate(IOUSBControllerV2*); 70 | 71 | /* 72 | * Self 73 | */ 74 | static GenericUSBXHCIIsochTD* ForEndpoint(GenericUSBXHCIIsochEP*); 75 | static IOReturn TranslateXHCIIsochTDStatus(uint32_t); 76 | int32_t FrameForEventIndex(uint32_t) const; 77 | }; 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /Leaf_Methods.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Leaf_Methods.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on December 26th, 2012. 6 | // Copyright (c) 2012-2014 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCI.h" 10 | #include "XHCITypes.h" 11 | #include "Async.h" 12 | #include 13 | 14 | #define CLASS GenericUSBXHCI 15 | #define super IOUSBControllerV3 16 | 17 | #pragma mark - 18 | #pragma mark Assorted 19 | #pragma mark - 20 | 21 | __attribute__((visibility("hidden"))) 22 | void CLASS::SetVendorInfo(void) 23 | { 24 | OSData *vendProp, *deviceProp, *revisionProp; 25 | 26 | // get this chips vendID, deviceID, revisionID 27 | vendProp = OSDynamicCast(OSData, _device->getProperty( "vendor-id" )); 28 | if (vendProp) 29 | _vendorID = *static_cast(vendProp->getBytesNoCopy()); 30 | deviceProp = OSDynamicCast(OSData, _device->getProperty( "device-id" )); 31 | if (deviceProp) 32 | _deviceID = *static_cast(deviceProp->getBytesNoCopy()); 33 | revisionProp = OSDynamicCast(OSData, _device->getProperty( "revision-id" )); 34 | if (revisionProp) 35 | _revisionID = *static_cast(revisionProp->getBytesNoCopy()); 36 | } 37 | 38 | 39 | __attribute__((visibility("hidden"))) 40 | IOUSBHubPolicyMaker* CLASS::GetHubForProtocol(uint8_t protocol) 41 | { 42 | if (protocol == kUSBDeviceSpeedHigh && _rootHubDevice) 43 | return _rootHubDevice->GetPolicyMaker(); 44 | if (protocol == kUSBDeviceSpeedSuper && _expansionData && _rootHubDeviceSS) 45 | return _rootHubDeviceSS->GetPolicyMaker(); 46 | return 0; 47 | } 48 | 49 | __attribute__((visibility("hidden"))) 50 | IOReturn CLASS::GatedGetFrameNumberWithTime(OSObject* owner, void* frameNumber, void* theTime, void*, void*) 51 | { 52 | CLASS* me = static_cast(owner); 53 | *static_cast(frameNumber) = me->_millsecondsTimers[3]; 54 | *static_cast(theTime) = me->_millsecondsTimers[1]; 55 | return kIOReturnSuccess; 56 | } 57 | 58 | __attribute__((visibility("hidden"))) 59 | void CLASS::SleepWithGateReleased(IOCommandGate* pGate, uint32_t msec) 60 | { 61 | AbsoluteTime deadline; 62 | uint32_t event = 0U; 63 | clock_interval_to_deadline(msec, 64 | kMillisecondScale, 65 | reinterpret_cast(&deadline)); 66 | pGate->commandSleep(&event, deadline, THREAD_ABORTSAFE); 67 | } 68 | 69 | __attribute__((visibility("hidden"))) 70 | void CLASS::CheckedSleep(uint32_t msec) 71 | { 72 | if (_workLoop->inGate()) 73 | SleepWithGateReleased(_commandGate, msec); 74 | else 75 | IOSleep(msec); 76 | } 77 | 78 | __attribute__((visibility("hidden"))) 79 | void CLASS::OverrideErrataFromProps(void) 80 | { 81 | if (CHECK_FOR_MAVERICKS && (_errataBits & kErrataBrokenStreams) && !getProperty("DisableUAS")) 82 | setProperty("DisableUAS", kOSBooleanTrue); 83 | if ((_errataBits & kErrataAbsoluteEDTLA) && 84 | OSDynamicCast(OSBoolean, getProperty("ASMediaEDLTAFix")) == kOSBooleanFalse) 85 | _errataBits &= ~kErrataAbsoluteEDTLA; 86 | OSBoolean* b = OSDynamicCast(OSBoolean, getProperty("UseLegacyInt")); 87 | if (b) { 88 | if (b->isTrue()) 89 | _errataBits |= kErrataDisableMSI; 90 | else 91 | _errataBits &= ~kErrataDisableMSI; 92 | } 93 | if (_errataBits & kErrataIntelPantherPoint) { 94 | b = OSDynamicCast(OSBoolean, getProperty("IntelDoze")); 95 | if (b) { 96 | if (b->isTrue()) 97 | _errataBits |= kErrataSWAssistedIdle; 98 | else 99 | _errataBits &= ~kErrataSWAssistedIdle; 100 | } 101 | } 102 | } 103 | 104 | #pragma mark - 105 | #pragma mark Buffers 106 | #pragma mark - 107 | 108 | __attribute__((visibility("hidden"))) 109 | IOReturn CLASS::MakeBuffer(uint32_t mem_options, 110 | size_t mem_capacity, 111 | uint64_t mem_mask, 112 | IOBufferMemoryDescriptor **p_desc, 113 | void **p_virt_addr, 114 | uint64_t* p_phys_addr) 115 | { 116 | IOReturn rc; 117 | IOBufferMemoryDescriptor* md; 118 | void* ptr; 119 | uint64_t physAddr; 120 | IOByteCount segLength; 121 | 122 | md = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(kernel_task, 123 | mem_options, 124 | mem_capacity, 125 | XHCI_HCC_AC64(_HCCLow) ? mem_mask : (mem_mask & UINT32_MAX)); 126 | if (!md) { 127 | IOLog("%s: Cannot create IOBufferMemoryDescriptor\n", __FUNCTION__); 128 | return kIOReturnNoMemory; 129 | } 130 | rc = md->prepare(); 131 | if (rc != kIOReturnSuccess) { 132 | IOLog("%s: IOMemoryDescriptor::prepare failed, error == %#x\n", __FUNCTION__, rc); 133 | md->release(); 134 | return rc; 135 | } 136 | ptr = md->getBytesNoCopy(); 137 | if (ptr) 138 | bzero(ptr, mem_capacity); 139 | physAddr = md->getPhysicalSegment(0U, &segLength, 0U); 140 | if (!physAddr) { 141 | IOLog("%s: IOMemoryDescriptor::getPhysicalSegment returned bad data\n", __FUNCTION__); 142 | md->complete(); 143 | md->release(); 144 | return kIOReturnInternalError; 145 | } 146 | *p_desc = md; 147 | *p_virt_addr = ptr; 148 | *p_phys_addr = physAddr; 149 | return kIOReturnSuccess; 150 | } 151 | 152 | __attribute__((visibility("hidden"))) 153 | IOReturn CLASS::MakeBufferUnmapped(uint32_t mem_options, 154 | size_t mem_capacity, 155 | uint64_t mem_mask, 156 | IOBufferMemoryDescriptor **p_desc, 157 | uint64_t* p_phys_addr) 158 | { 159 | IOReturn rc; 160 | IOBufferMemoryDescriptor* md; 161 | uint64_t physAddr; 162 | IOByteCount segLength; 163 | 164 | md = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(0, 165 | mem_options | kIOMemoryPageable, 166 | mem_capacity, 167 | XHCI_HCC_AC64(_HCCLow) ? mem_mask : (mem_mask & UINT32_MAX)); 168 | if (!md) { 169 | IOLog("%s: Cannot create IOBufferMemoryDescriptor\n", __FUNCTION__); 170 | return kIOReturnNoMemory; 171 | } 172 | rc = md->prepare(); 173 | if (rc != kIOReturnSuccess) { 174 | IOLog("%s: IOMemoryDescriptor::prepare failed, error == %#x\n", __FUNCTION__, rc); 175 | md->release(); 176 | return rc; 177 | } 178 | physAddr = md->getPhysicalSegment(0U, &segLength, 0U); 179 | if (!physAddr) { 180 | IOLog("%s: IOMemoryDescriptor::getPhysicalSegment returned bad data\n", __FUNCTION__); 181 | md->complete(); 182 | md->release(); 183 | return kIOReturnInternalError; 184 | } 185 | *p_desc = md; 186 | *p_phys_addr = physAddr; 187 | return kIOReturnSuccess; 188 | } 189 | 190 | #pragma mark - 191 | #pragma mark xHC Capabilities 192 | #pragma mark - 193 | 194 | __attribute__((visibility("hidden"))) 195 | void CLASS::DecodeExtendedCapability(uint32_t hccParams) 196 | { 197 | if (_errataBits & kErrataVMwarePortSwap) { 198 | WRITE_V3EXPANSION(_rootHubNumPortsSS, _rootHubNumPorts / 2U); 199 | WRITE_V3EXPANSION(_rootHubPortsSSStartRange, 1U); 200 | WRITE_V3EXPANSION(_rootHubNumPortsHS, READ_V3EXPANSION(_rootHubNumPortsSS)); 201 | WRITE_V3EXPANSION(_rootHubPortsHSStartRange, READ_V3EXPANSION(_rootHubPortsSSStartRange) + READ_V3EXPANSION(_rootHubNumPortsSS)); 202 | return; 203 | } 204 | uint32_t ecp = XHCI_HCC_XECP(hccParams); 205 | if (!ecp) 206 | return; 207 | _pXHCIExtendedCapRegisters = reinterpret_cast(reinterpret_cast(_pXHCICapRegisters) + ecp); 208 | XHCIXECPStruct volatile* iter = _pXHCIExtendedCapRegisters; 209 | while (true) { 210 | if (iter->capId == 1U) 211 | _pUSBLegSup = reinterpret_cast(iter); 212 | else if (iter->capId == 2U) 213 | DecodeSupportedProtocol(iter); 214 | if (!(iter->next)) 215 | break; 216 | iter = reinterpret_cast(reinterpret_cast(iter) + iter->next); 217 | } 218 | } 219 | 220 | __attribute__((visibility("hidden"))) 221 | void CLASS::DecodeSupportedProtocol(XHCIXECPStruct volatile* pCap) 222 | { 223 | XHCIXECPStruct_SP volatile* pSPCap = reinterpret_cast(pCap); 224 | switch (pSPCap->revisionMajor) { 225 | case 3U: 226 | if (pSPCap->nameString != ' BSU') 227 | break; 228 | WRITE_V3EXPANSION(_rootHubNumPortsSS, pSPCap->compatiblePortCount); 229 | WRITE_V3EXPANSION(_rootHubPortsSSStartRange, pSPCap->compatiblePortOffset); 230 | break; 231 | case 2U: 232 | if (pSPCap->nameString != ' BSU') 233 | break; 234 | WRITE_V3EXPANSION(_rootHubNumPortsHS, pSPCap->compatiblePortCount); 235 | WRITE_V3EXPANSION(_rootHubPortsHSStartRange, pSPCap->compatiblePortOffset); 236 | break; 237 | } 238 | } 239 | 240 | #pragma mark - 241 | #pragma mark xHC Operations 242 | #pragma mark - 243 | 244 | __attribute__((visibility("hidden"))) 245 | void CLASS::TakeOwnershipFromBios(void) 246 | { 247 | uint32_t v; 248 | IOReturn rc; 249 | 250 | if (!_pUSBLegSup) 251 | return; 252 | v = Read32Reg(_pUSBLegSup); 253 | if (m_invalid_regspace) 254 | return; 255 | if (v & XHCI_HC_BIOS_OWNED) { 256 | Write32Reg(_pUSBLegSup, v | XHCI_HC_OS_OWNED); 257 | rc = XHCIHandshake(_pUSBLegSup, XHCI_HC_BIOS_OWNED, 0U, 100); 258 | if (rc == kIOReturnNoDevice) 259 | return; 260 | if (rc == kIOReturnTimeout) { 261 | IOLog("%s: Unable to take ownership of xHC from BIOS within 100 ms\n", __FUNCTION__); 262 | /* 263 | * Fall through to break bios hold by disabling SMI enables 264 | */ 265 | } 266 | } 267 | v = Read32Reg(_pUSBLegSup + 1); 268 | if (m_invalid_regspace) 269 | return; 270 | /* 271 | * Clear all SMI enables 272 | */ 273 | v &= XHCI_LEGACY_DISABLE_SMI; 274 | /* 275 | * Clear RW1C bits 276 | */ 277 | v |= XHCI_LEGACY_SMI_EVENTS; 278 | Write32Reg(_pUSBLegSup + 1, v); 279 | } 280 | 281 | __attribute__((noinline, visibility("hidden"))) 282 | IOReturn CLASS::WaitForUSBSts(uint32_t test_mask, uint32_t test_target) 283 | { 284 | return XHCIHandshake(&_pXHCIOperationalRegisters->USBSts, test_mask, test_target, 100); 285 | } 286 | 287 | __attribute__((noinline, visibility("hidden"))) 288 | IOReturn CLASS::XHCIHandshake(uint32_t volatile const* pReg, uint32_t test_mask, uint32_t test_target, int32_t msec) 289 | { 290 | for (int32_t count = 0; count < msec; ++count) { 291 | if (count) 292 | IOSleep(1U); 293 | uint32_t reg = Read32Reg(pReg); 294 | if (m_invalid_regspace) 295 | return kIOReturnNoDevice; 296 | if ((reg & test_mask) == (test_target & test_mask)) 297 | return kIOReturnSuccess; 298 | } 299 | return kIOReturnTimeout; // Note: originally kIOReturnInternalError 300 | } 301 | 302 | __attribute__((visibility("hidden"))) 303 | IOReturn CLASS::ResetDevice(int32_t slot) 304 | { 305 | int32_t retFromCMD; 306 | TRBStruct localTrb = { 0 }; 307 | 308 | localTrb.d |= XHCI_TRB_3_SLOT_SET(slot); 309 | retFromCMD = WaitForCMD(&localTrb, XHCI_TRB_TYPE_RESET_DEVICE, 0); 310 | return TranslateCommandCompletion(retFromCMD); 311 | } 312 | 313 | __attribute__((visibility("hidden"))) 314 | int32_t CLASS::NegotiateBandwidth(int32_t slot) 315 | { 316 | TRBStruct localTrb = { 0 }; 317 | 318 | localTrb.d |= XHCI_TRB_3_SLOT_SET(slot); 319 | return WaitForCMD(&localTrb, XHCI_TRB_TYPE_NEGOTIATE_BW, 0); 320 | } 321 | 322 | __attribute__((visibility("hidden"))) 323 | int32_t CLASS::SetLTV(uint32_t BELT) 324 | { 325 | TRBStruct localTrb = { 0 }; 326 | 327 | localTrb.d |= (BELT & 0xFFFU) << 16; 328 | return WaitForCMD(&localTrb, XHCI_TRB_TYPE_SET_LATENCY_TOL, 0); 329 | } 330 | 331 | __attribute__((visibility("hidden"))) 332 | IOReturn CLASS::GetPortBandwidth(uint8_t HubSlot, uint8_t speed, uint8_t* pBuffer, size_t* pCount) 333 | { 334 | uint8_t xspeed; 335 | int32_t retFromCMD; 336 | TRBStruct localTrb = { 0 }; 337 | 338 | if (!pBuffer || !pCount) 339 | return kIOReturnBadArgument; 340 | if (!HubSlot && *pCount < _rootHubNumPorts) { 341 | *pCount = _rootHubNumPorts; 342 | return kIOReturnNoMemory; 343 | } 344 | if (HubSlot && *pCount < kMaxExternalHubPorts) { 345 | *pCount = kMaxExternalHubPorts; 346 | return kIOReturnNoMemory; 347 | } 348 | switch (speed) { 349 | case kUSBDeviceSpeedLow: 350 | xspeed = XDEV_LS; 351 | break; 352 | case kUSBDeviceSpeedFull: 353 | xspeed = XDEV_FS; 354 | break; 355 | case kUSBDeviceSpeedHigh: 356 | xspeed = XDEV_HS; 357 | break; 358 | case kUSBDeviceSpeedSuper: 359 | xspeed = XDEV_SS; 360 | break; 361 | default: 362 | return kIOReturnBadArgument; 363 | } 364 | localTrb.d |= XHCI_TRB_3_SLOT_SET(static_cast(HubSlot)); 365 | localTrb.d |= XHCI_TRB_3_TLBPC_SET(static_cast(xspeed)); 366 | GetInputContext(); 367 | SetTRBAddr64(&localTrb, _inputContext.physAddr); 368 | retFromCMD = WaitForCMD(&localTrb, XHCI_TRB_TYPE_GET_PORT_BW, 0); 369 | if (retFromCMD == -1 || retFromCMD <= -1000) { 370 | ReleaseInputContext(); 371 | if (retFromCMD == -1) 372 | return kIOReturnInternalError; 373 | return TranslateXHCIStatus(-1000 - retFromCMD, HubSlot, false); 374 | } 375 | if (!HubSlot) { 376 | bcopy(reinterpret_cast(GetInputContextPtr()) + 1, pBuffer, _rootHubNumPorts); 377 | *pCount = _rootHubNumPorts; 378 | } else { 379 | bcopy(reinterpret_cast(GetInputContextPtr()) + 1, pBuffer, kMaxExternalHubPorts); 380 | *pCount = kMaxExternalHubPorts; 381 | } 382 | ReleaseInputContext(); 383 | return kIOReturnSuccess; 384 | } 385 | 386 | #pragma mark - 387 | #pragma mark Scratchpad Buffers 388 | #pragma mark - 389 | 390 | __attribute__((visibility("hidden"))) 391 | IOReturn CLASS::AllocScratchpadBuffers(void) 392 | { 393 | IOReturn rc; 394 | IOBufferMemoryDescriptor* md; 395 | uint64_t pageMask; 396 | uint32_t pageSizes; 397 | 398 | _scratchpadBuffers.md = 0; 399 | if (!_scratchpadBuffers.max) 400 | return kIOReturnSuccess; 401 | pageSizes = Read32Reg(&_pXHCIOperationalRegisters->PageSize); 402 | if (m_invalid_regspace) { 403 | IOLog("%s: Invalid regspace\n", __FUNCTION__); 404 | return kIOReturnNoDevice; 405 | } 406 | pageSizes <<= 12; 407 | if (!pageSizes) { 408 | IOLog("%s: PageSize register invalid value of zero\n", __FUNCTION__); 409 | return kIOReturnDeviceError; 410 | } 411 | pageSizes &= -pageSizes; // leave just smallest size 412 | _scratchpadBuffers.mdGC = OSArray::withCapacity(_scratchpadBuffers.max); 413 | if (!_scratchpadBuffers.mdGC) { 414 | IOLog("%s: OSArray::withCapacity failed\n", __FUNCTION__); 415 | return kIOReturnNoMemory; 416 | } 417 | rc = MakeBuffer(kIOMemoryPhysicallyContiguous | kIODirectionInOut, 418 | static_cast(_scratchpadBuffers.max) * sizeof *_scratchpadBuffers.ptr, 419 | -PAGE_SIZE, 420 | &_scratchpadBuffers.md, 421 | reinterpret_cast(&_scratchpadBuffers.ptr), 422 | &_scratchpadBuffers.physAddr); 423 | if (rc != kIOReturnSuccess) { 424 | IOLog("%s: MakeBuffer(1) failed, error == %#x\n", __FUNCTION__, rc); 425 | return rc; 426 | } 427 | pageMask = -static_cast(pageSizes); 428 | for (uint32_t spbufs = 0U; spbufs < _scratchpadBuffers.max; ++spbufs) { 429 | rc = MakeBufferUnmapped(kIOMemoryPhysicallyContiguous | kIODirectionInOut, 430 | pageSizes, 431 | pageMask, 432 | &md, 433 | &_scratchpadBuffers.ptr[spbufs]); 434 | if (rc != kIOReturnSuccess) { 435 | IOLog("%s: MakeBuffer(2) failed, error == %#x\n", __FUNCTION__, rc); 436 | return rc; 437 | } 438 | _scratchpadBuffers.mdGC->setObject(md); // Note: ignores error 439 | md->release(); 440 | } 441 | SetDCBAAAddr64(_dcbaa.ptr, _scratchpadBuffers.physAddr); 442 | return kIOReturnSuccess; 443 | } 444 | 445 | __attribute__((visibility("hidden"))) 446 | void CLASS::FinalizeScratchpadBuffers(void) 447 | { 448 | if (_scratchpadBuffers.mdGC) { 449 | uint32_t l = _scratchpadBuffers.mdGC->getCount(); 450 | for (uint32_t i = 0U; i < l; ++i) 451 | static_cast(_scratchpadBuffers.mdGC->getObject(i))->complete(); 452 | _scratchpadBuffers.mdGC->flushCollection(); 453 | _scratchpadBuffers.mdGC->release(); 454 | _scratchpadBuffers.mdGC = 0; 455 | } 456 | if (_scratchpadBuffers.md) { 457 | _scratchpadBuffers.md->complete(); 458 | _scratchpadBuffers.md->release(); 459 | _scratchpadBuffers.md = 0; 460 | } 461 | } 462 | 463 | #pragma mark - 464 | #pragma mark Input Context 465 | #pragma mark - 466 | 467 | __attribute__((visibility("hidden"))) 468 | void CLASS::GetInputContext(void) 469 | { 470 | ++_inputContext.refCount; 471 | bzero(GetInputContextPtr(), GetInputContextSize()); 472 | } 473 | 474 | __attribute__((visibility("hidden"))) 475 | void CLASS::ReleaseInputContext(void) 476 | { 477 | if (_inputContext.refCount) 478 | --_inputContext.refCount; 479 | } 480 | 481 | __attribute__((visibility("hidden"))) 482 | uint32_t CLASS::GetInputContextSize(void) 483 | { 484 | return 33U * (XHCI_HCC_CSZ(_HCCLow) ? 2U : 1U) * sizeof(ContextStruct); 485 | } 486 | 487 | __attribute__((visibility("hidden"))) 488 | uint32_t CLASS::GetDeviceContextSize(void) 489 | { 490 | return 32U * (XHCI_HCC_CSZ(_HCCLow) ? 2U : 1U) * sizeof(ContextStruct); 491 | } 492 | 493 | #pragma mark - 494 | #pragma mark XHCI Status 495 | #pragma mark - 496 | 497 | __attribute__((visibility("hidden"))) 498 | IOReturn CLASS::TranslateXHCIStatus(int32_t xhci_err, uint32_t slot, bool direction) 499 | { 500 | switch (xhci_err) { 501 | case XHCI_TRB_ERROR_SUCCESS: 502 | case XHCI_TRB_ERROR_SHORT_PKT: 503 | return kIOReturnSuccess; 504 | case XHCI_TRB_ERROR_DATA_BUF: 505 | return kIOUSBBufferOverrunErr | (direction ? 0U : 1U); 506 | case XHCI_TRB_ERROR_BABBLE: 507 | case XHCI_TRB_ERROR_RING_OVERRUN: 508 | case XHCI_TRB_ERROR_VF_RING_FULL: 509 | case XHCI_TRB_ERROR_EV_RING_FULL: 510 | case XHCI_TRB_ERROR_MISSED_SERVICE: 511 | case XHCI_TRB_ERROR_ISOC_OVERRUN: 512 | case XHCI_TRB_ERROR_EVENT_LOST: 513 | return kIOReturnOverrun; 514 | case XHCI_TRB_ERROR_XACT: 515 | if (slot && slot <= _numSlots) { 516 | ContextStruct* pContext = GetSlotContext(slot); 517 | if (pContext && XHCI_SCTX_2_TT_HUB_SID_GET(pContext->_s.dwSctx2)) 518 | return kIOUSBHighSpeedSplitError; 519 | } 520 | return kIOReturnNotResponding; 521 | case XHCI_TRB_ERROR_STALL: 522 | return kIOUSBPipeStalled; 523 | case XHCI_TRB_ERROR_RESOURCE: 524 | return kIOReturnNoResources; 525 | case XHCI_TRB_ERROR_BANDWIDTH: 526 | case XHCI_TRB_ERROR_BW_OVERRUN: 527 | case XHCI_TRB_ERROR_SEC_BW: 528 | return kIOReturnNoBandwidth; 529 | case XHCI_TRB_ERROR_NO_SLOTS: 530 | return kIOUSBDeviceCountExceeded; 531 | case XHCI_TRB_ERROR_STREAM_TYPE: 532 | return kIOReturnInvalid; 533 | case XHCI_TRB_ERROR_SLOT_NOT_ON: 534 | case XHCI_TRB_ERROR_ENDP_NOT_ON: 535 | case XHCI_TRB_ERROR_CMD_RING_STOP: 536 | return kIOReturnOffline; 537 | case XHCI_TRB_ERROR_RING_UNDERRUN: 538 | return kIOReturnUnderrun; 539 | case XHCI_TRB_ERROR_PARAMETER: 540 | case XHCI_TRB_ERROR_CONTEXT_STATE: 541 | case XHCI_TRB_ERROR_BAD_MELAT: 542 | case XHCI_TRB_ERROR_INVALID_SID: 543 | return kIOReturnBadArgument; 544 | case XHCI_TRB_ERROR_NO_PING_RESP: 545 | return kIOReturnNotResponding; 546 | case XHCI_TRB_ERROR_INCOMPAT_DEV: 547 | return kIOReturnDeviceError; 548 | case XHCI_TRB_ERROR_CMD_ABORTED: 549 | case XHCI_TRB_ERROR_STOPPED: 550 | case XHCI_TRB_ERROR_LENGTH: 551 | return kIOReturnAborted; 552 | case XHCI_TRB_ERROR_SPLIT_XACT: 553 | return kIOUSBHighSpeedSplitError; 554 | case 193: // Intel FORCE_HDR_USB2_NO_SUPPORT 555 | return _vendorID == kVendorIntel ? kIOReturnUnsupported : kIOReturnInternalError; 556 | case 199: // Intel CMPL_WITH_EMPTY_CONTEXT 557 | return _vendorID == kVendorIntel ? kIOReturnNotOpen : kIOReturnInternalError; 558 | case 200: // Intel VENDOR_CMD_FAILED 559 | return _vendorID == kVendorIntel ? kIOReturnNoBandwidth : kIOReturnInternalError; 560 | default: 561 | return kIOReturnInternalError; 562 | } 563 | } 564 | 565 | __attribute__((visibility("hidden"))) 566 | IOReturn CLASS::TranslateCommandCompletion(int32_t code) 567 | { 568 | if (code == -1) 569 | return kIOReturnTimeout; 570 | if (code > -1000) 571 | return kIOReturnSuccess; 572 | if (code == -1000 - XHCI_TRB_ERROR_CONTEXT_STATE) 573 | return kIOReturnNotPermitted; 574 | if (code == -1000 - XHCI_TRB_ERROR_PARAMETER) 575 | return kIOReturnBadArgument; 576 | return kIOReturnInternalError; 577 | } 578 | -------------------------------------------------------------------------------- /PwrMgmt.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // PwrMgmt.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on January 5th 2013. 6 | // Copyright (c) 2013-2014 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCI.h" 10 | 11 | #define CLASS GenericUSBXHCI 12 | #define super IOUSBControllerV3 13 | 14 | #pragma mark - 15 | #pragma mark Power Management 16 | #pragma mark - 17 | 18 | __attribute__((visibility("hidden"))) 19 | void CLASS::CheckSleepCapability(void) 20 | { 21 | bool haveSleep = false; 22 | 23 | if (!(gux_options & GUX_OPTION_NO_SLEEP) && 24 | _device->hasPCIPowerManagement(kPCIPMCPMESupportFromD3Cold) && 25 | kIOReturnSuccess == _device->enablePCIPowerManagement(kPCIPMCSPowerStateD3)) 26 | haveSleep = true; 27 | else 28 | IOLog("%s: xHC will be unloaded across sleep\n", getName()); 29 | _expansionData->_controllerCanSleep = haveSleep; 30 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 || defined(REHABMAN_UNIVERSAL_BUILD) 31 | if (CHECK_FOR_MAVERICKS) 32 | { 33 | WRITE_V3EXPANSION(_hasPCIPwrMgmt, haveSleep); 34 | } 35 | #endif 36 | /* 37 | * Note: 38 | * Always set the Card Type to Built-in, in order 39 | * to enable the extra-current mechanism, even 40 | * if xHC does not support save/restore. 41 | * See IOUSBRootHubDevice::start 42 | */ 43 | #if 0 44 | setProperty("Card Type", haveSleep ? "Built-in" : "PCI"); 45 | #else 46 | setProperty("Card Type", "Built-in"); 47 | setProperty("ResetOnResume", !haveSleep); 48 | #endif 49 | } 50 | 51 | __attribute__((visibility("hidden"))) 52 | IOReturn CLASS::RHCompleteSuspendOnAllPorts(void) 53 | { 54 | uint32_t wait, portSC, changePortSC, idbmp, wantedWakeBits; 55 | 56 | wait = 0U; 57 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 || defined(REHABMAN_UNIVERSAL_BUILD) 58 | if (CHECK_FOR_MAVERICKS) 59 | { 60 | #if __MAC_OS_X_VERSION_MAX_ALLOWED < 1090 61 | idbmp = ((Yosemite_ExpansionData*)_expansionData)->_ignoreDisconnectBitmap; 62 | #else 63 | idbmp = _expansionData->_ignoreDisconnectBitmap; 64 | #endif 65 | } 66 | else 67 | #endif 68 | idbmp = 0U; 69 | for (uint8_t port = 0U; port < _rootHubNumPorts; ++port) { 70 | changePortSC = 0U; 71 | portSC = Read32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC); 72 | if (m_invalid_regspace) 73 | return kIOReturnNoDevice; 74 | if ((_errataBits & kErrataIntelPantherPoint) && 75 | !(portSC & XHCI_PS_CSC)) 76 | changePortSC = XHCI_PS_CSC; 77 | if ((portSC & XHCI_PS_PED) && 78 | XHCI_PS_PLS_GET(portSC) < XDEV_U3) { 79 | changePortSC |= XHCI_PS_LWS | XHCI_PS_PLS_SET(XDEV_U3); 80 | wait = 15U; 81 | } 82 | if (idbmp & (2U << port)) 83 | wantedWakeBits = XHCI_PS_WOE; 84 | else 85 | wantedWakeBits = XHCI_PS_WAKEBITS; 86 | if (wantedWakeBits != (portSC & XHCI_PS_WAKEBITS)) { 87 | portSC &= ~XHCI_PS_WAKEBITS; 88 | changePortSC |= wantedWakeBits; 89 | } 90 | if (changePortSC) 91 | Write32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC, (portSC & XHCI_PS_WRITEBACK_MASK) | changePortSC); 92 | } 93 | if (wait) 94 | IOSleep(wait); 95 | return kIOReturnSuccess; 96 | } 97 | 98 | __attribute__((visibility("hidden"))) 99 | void CLASS::QuiesceAllEndpoints(void) 100 | { 101 | for (uint8_t slot = 1U; slot <= _numSlots; ++slot) { 102 | SlotStruct* pSlot = SlotPtr(slot); 103 | if (pSlot->isInactive()) 104 | continue; 105 | for (int32_t endpoint = 1; endpoint != kUSBMaxPipes; ++endpoint) { 106 | ringStruct* pRing = pSlot->ringArrayForEndpoint[endpoint]; 107 | if (!pRing->isInactive()) 108 | QuiesceEndpoint(slot, endpoint); 109 | } 110 | } 111 | } 112 | 113 | __attribute__((visibility("hidden"))) 114 | IOReturn CLASS::ResetController(void) 115 | { 116 | IOReturn rc; 117 | 118 | /* 119 | * Wait for Controller Ready in case we're starting from hardware reset 120 | */ 121 | Read32Reg(&_pXHCIOperationalRegisters->USBSts); // Note: CNR may be asserted on 1st read 122 | if (m_invalid_regspace) 123 | return kIOReturnNoDevice; 124 | rc = XHCIHandshake(&_pXHCIOperationalRegisters->USBSts, XHCI_STS_CNR, 0U, 1000); 125 | if (rc != kIOReturnSuccess) { 126 | if (rc == kIOReturnTimeout) 127 | IOLog("%s: chip not ready within 1000 ms\n", __FUNCTION__); 128 | return rc; 129 | } 130 | /* 131 | * Make sure controller is halted in case we're not starting 132 | * from hardware reset 133 | */ 134 | Write32Reg(&_pXHCIOperationalRegisters->USBCmd, 0U); 135 | rc = WaitForUSBSts(XHCI_STS_HCH, UINT32_MAX); 136 | if (rc != kIOReturnSuccess) { 137 | if (rc == kIOReturnTimeout) 138 | IOLog("%s: could not get chip to halt within 100 ms\n", __FUNCTION__); 139 | return rc; 140 | } 141 | if (_errataBits & kErrataFL1100LowRev) 142 | FL1100Tricks(3); 143 | Write32Reg(&_pXHCIOperationalRegisters->USBCmd, XHCI_CMD_HCRST); 144 | rc = XHCIHandshake(&_pXHCIOperationalRegisters->USBCmd, XHCI_CMD_HCRST, 0U, 1000); 145 | if (rc == kIOReturnTimeout) 146 | IOLog("%s: could not get chip to come out of reset within 1000 ms\n", __FUNCTION__); 147 | if (_errataBits & kErrataFL1100LowRev) 148 | FL1100Tricks(4); 149 | return rc; 150 | } 151 | 152 | __attribute__((visibility("hidden"))) 153 | IOReturn CLASS::StopUSBBus(void) 154 | { 155 | uint32_t cmd = Read32Reg(&_pXHCIOperationalRegisters->USBCmd); 156 | if (m_invalid_regspace) 157 | return kIOReturnNoDevice; 158 | if (!(cmd & XHCI_CMD_RS)) { 159 | _myBusState = kUSBBusStateReset; 160 | return kIOReturnSuccess; 161 | } 162 | Write32Reg(&_pXHCIOperationalRegisters->USBCmd, cmd & ~XHCI_CMD_RS); 163 | IOReturn rc = WaitForUSBSts(XHCI_STS_HCH, UINT32_MAX); 164 | if (rc != kIOReturnTimeout) 165 | _myBusState = kUSBBusStateReset; 166 | return rc; 167 | } 168 | 169 | __attribute__((visibility("hidden"))) 170 | void CLASS::RestartUSBBus(void) 171 | { 172 | uint32_t cmd = Read32Reg(&_pXHCIOperationalRegisters->USBCmd); 173 | if (m_invalid_regspace) 174 | return; 175 | cmd |= XHCI_CMD_EWE | XHCI_CMD_INTE | XHCI_CMD_RS; 176 | Write32Reg(&_pXHCIOperationalRegisters->USBCmd, cmd); 177 | IOReturn rc = WaitForUSBSts(XHCI_STS_HCH, 0U); 178 | if (rc != kIOReturnNoDevice) 179 | _myBusState = kUSBBusStateRunning; 180 | } 181 | 182 | __attribute__((visibility("hidden"))) 183 | void CLASS::NotifyRootHubsOfPowerLoss(void) 184 | { 185 | IOUSBHubPolicyMaker *pm2, *pm3; 186 | 187 | /* 188 | * TBD: Couldn't find a decent way to notify up-stack 189 | * drivers of controller power-loss and need 190 | * for re-enumeration. 191 | */ 192 | pm2 = GetHubForProtocol(kUSBDeviceSpeedHigh); 193 | pm3 = GetHubForProtocol(kUSBDeviceSpeedSuper); 194 | if (pm2) 195 | #if 1 196 | pm2->changePowerStateTo(kIOUSBHubPowerStateRestart); 197 | #else 198 | pm2->HubPowerChange(kIOUSBHubPowerStateRestart); 199 | #endif 200 | if (pm3) 201 | #if 1 202 | pm3->changePowerStateTo(kIOUSBHubPowerStateRestart); 203 | #else 204 | pm3->HubPowerChange(kIOUSBHubPowerStateRestart); 205 | #endif 206 | } 207 | 208 | #if 0 209 | __attribute__((visibility("hidden"))) 210 | void CLASS::SantizePortsAfterPowerLoss(void) 211 | { 212 | for (uint8_t port = 0U; port < _rootHubNumPorts; ++port) { 213 | uint32_t portSC = GetPortSCForWriting(port); 214 | if (m_invalid_regspace) 215 | return; 216 | Write32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC, portSC | XHCI_PS_WAKEBITS); 217 | } 218 | } 219 | 220 | __attribute__((visibility("hidden"))) 221 | void CLASS::DisableWakeBits(void) 222 | { 223 | if (!_wakeEnabled || m_invalid_regspace || isInactive()) 224 | return; 225 | for (uint8_t port = 0U; port < _rootHubNumPorts; ++port) { 226 | uint32_t portSC = GetPortSCForWriting(port); 227 | if (m_invalid_regspace) 228 | return; 229 | Write32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC, portSC & ~XHCI_PS_WAKEBITS); 230 | } 231 | _wakeEnabled = false; 232 | } 233 | 234 | __attribute__((visibility("hidden"))) 235 | void CLASS::EnableWakeBits(void) 236 | { 237 | if (_wakeEnabled || m_invalid_regspace || isInactive()) 238 | return; 239 | uint32_t idbmp = _expansionData->_ignoreDisconnectBitmap; 240 | for (uint8_t port = 0U; port < _rootHubNumPorts; ++port) { 241 | uint32_t portSC = GetPortSCForWriting(port); 242 | if (m_invalid_regspace) 243 | return; 244 | if (idbmp & (2U << port)) 245 | portSC = (portSC & ~XHCI_PS_WAKEBITS) | XHCI_PS_WOE; 246 | else 247 | portSC |= XHCI_PS_WAKEBITS; 248 | Write32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC, portSC); 249 | } 250 | _wakeEnabled = true; 251 | } 252 | #endif 253 | 254 | __attribute__((visibility("hidden"))) 255 | void CLASS::SetPropsForBookkeeping(void) 256 | { 257 | /* 258 | * Note: 259 | * IOUSBController::CreateRootHubDevice copies these properties 260 | * from _device to the root hub devices, and also to IOResources. 261 | * The copying to IOResources takes place once (globally). 262 | * IOUSBRootHubDevice::RequestExtraWakePower then uses the global 263 | * values on IOResources to do power-accounting for all root hubs 264 | * in the system (!). So whatever value given here for extra current 265 | * must cover 400mA of extra current for each SS root hub port in 266 | * the system. 267 | * Since there may be more than one xHC, must account for them all. 268 | * Set a high value of 255 to (hopefully) cover everything. 269 | * Additionally, iPhone/iPad ask for 1600mA of extra power on high-speed 270 | * ports, so we allow for that as well. 271 | */ 272 | #if 0 273 | uint32_t defaultTotalExtraCurrent = (kUSB3MaxPowerPerPort - kUSB2MaxPowerPerPort) * _rootHubNumPorts; 274 | uint32_t defaultMaxCurrentPerPort = kUSB3MaxPowerPerPort; 275 | #else 276 | uint32_t defaultTotalExtraCurrent = (kUSB3MaxPowerPerPort - kUSB2MaxPowerPerPort) * 255U; 277 | uint32_t defaultMaxCurrentPerPort = kUSB3MaxPowerPerPort + 1600U; 278 | #endif 279 | /* 280 | * Note: Only set defaults if none were injected via DSDT 281 | */ 282 | if (!OSDynamicCast(OSNumber, _device->getProperty(kAppleMaxPortCurrent))) 283 | _device->setProperty(kAppleMaxPortCurrent, defaultMaxCurrentPerPort, 32U); 284 | if (!OSDynamicCast(OSNumber, _device->getProperty(kAppleCurrentExtra))) 285 | _device->setProperty(kAppleCurrentExtra, defaultTotalExtraCurrent, 32U); 286 | if (!OSDynamicCast(OSNumber, _device->getProperty(kAppleMaxPortCurrentInSleep))) 287 | _device->setProperty(kAppleMaxPortCurrentInSleep, defaultMaxCurrentPerPort, 32U); 288 | if (!OSDynamicCast(OSNumber, _device->getProperty(kAppleCurrentExtraInSleep))) 289 | _device->setProperty(kAppleCurrentExtraInSleep, defaultTotalExtraCurrent, 32U); 290 | } 291 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Fork of Zenith432's GenericUSBXHCI USB 3.0 Driver by RehabMan 2 | 3 | 4 | ### How to Install: 5 | 6 | Install GenericUSBXHCI.kext using Kext Wizard or your favorite kext installer. 7 | 8 | If you were previously using PXHCD.kext, you should probably remove it. 9 | 10 | ``` 11 | rm -rf /System/Library/Extensions/PXHCD.kext 12 | ```` 13 | 14 | 15 | ### Downloads: 16 | 17 | Downloads are available on Bitbucket: 18 | 19 | https://bitbucket.org/RehabMan/os-x-generic-usb3/downloads/ 20 | 21 | Archived (old) builds are available on Google Code: 22 | 23 | https://code.google.com/p/os-x-generic-usb3/downloads/list 24 | 25 | 26 | ### Build Environment 27 | 28 | My build environment is currently Xcode 6.1, using SDK 10.8, targeting OS X 10.7. 29 | 30 | No other build environment is supported. 31 | 32 | 33 | ### 32-bit Builds 34 | 35 | Currently, builds are provided only for 64-bit systems. 32-bit/64-bit FAT binaries are not provided. But you can build your own should you need them. I do not test 32-bit, and there may be times when the repo is broken with respect to 32-bit builds, but I do check on major releases to see if the build still works for 32-bit. 36 | 37 | Here's how to build 32-bit (universal): 38 | 39 | - xcode 4.61 40 | - open GenericUSBXHCI.xcodeproj (do not change the SDK!) 41 | - click on GenericUSBXHCI at the top of the project tree 42 | - change Architectures to 'Standard (32/64-bit Intel)' 43 | 44 | probably not necessary, but a good idea to check that the target doesn't have overrides: 45 | - check/change Architectures to 'Standard (32/64-bit Intel)' 46 | - build (either w/ menu or with make) 47 | 48 | Or, if you have the command line tools installed, just run: 49 | 50 | - For FAT binary (32-bit and 64-bit in one binary) 51 | make BITS=3264 52 | 53 | - For 32-bit only 54 | make BITS=32 55 | 56 | 57 | ### Source Code: 58 | 59 | The source code is maintained at the following sites: 60 | 61 | https://bitbucket.org/RehabMan/os-x-generic-usb3 62 | 63 | https://github.com/RehabMan/OS-X-Generic-USB3 64 | 65 | 66 | ### Feedback: 67 | 68 | Please use the following thread on tonymacx86.com for feedback, questions, and help: 69 | 70 | TODO: provide link 71 | 72 | 73 | ### Known issues: 74 | 75 | 76 | ### Change Log: 77 | 78 | 2015-10-02 (RehabMan) 79 | 80 | - The kext will now fail to load on 10.11+ 81 | 82 | 83 | 2014-10-16 (RehabMan) 84 | 85 | - Merged with latest Zenith432 version 86 | 87 | - Created new Universal build for compatibility with 10.7.5 through 10.10 88 | 89 | 90 | 2013-03-23 (RehabMan) 91 | 92 | - Modified for single binary to work on ML, Lion (10.7.5 only) 93 | 94 | - Optimize build to reduce code size and exported symbols. 95 | 96 | 97 | 2013-03-06 (Zenith432) 98 | 99 | - Initial build provided by Zenith432 on insanelymac.com 100 | 101 | 102 | ### History 103 | 104 | This repository contains a modified version of Zenith432's GenericUSBXHCI USB 3.0 driver. All credits to Zenith432 for the original code and probably further enhancements/bug fixes. 105 | 106 | Original sources came from this post on Insanely Mac: 107 | 108 | http://www.insanelymac.com/forum/topic/286860-genericusbxhci-usb-30-driver-for-os-x-with-source/ 109 | 110 | Original repo: 111 | 112 | http://sourceforge.net/p/genericusbxhci/code 113 | 114 | My goal in creating this repository was just to create a single binary that could be used on 10.8.x, 10.7.5. I simply optimized the build settings for a smaller binary, removed some of the #if conditionals and added runtime checks as appropriate for differences between versions. Having a single optimized build for the Probook Installer makes the package smaller and easier to manage. 115 | 116 | If you install my version on a 10.7.4 or prior, the driver will gracefully exit. 117 | -------------------------------------------------------------------------------- /Rings.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Rings.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on October 10th 2013. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | // 9 | 10 | #include "GenericUSBXHCI.h" 11 | 12 | #define CLASS GenericUSBXHCI 13 | #define super IOUSBControllerV3 14 | 15 | #pragma mark - 16 | #pragma mark Rings 17 | #pragma mark - 18 | 19 | __attribute__((visibility("hidden"))) 20 | IOReturn CLASS::AllocRing(ringStruct* pRing, int32_t numPages) 21 | { 22 | IOReturn rc = MakeBuffer(kIOMemoryPhysicallyContiguous | kIODirectionInOut, 23 | numPages * PAGE_SIZE, 24 | -PAGE_SIZE, 25 | &pRing->md, 26 | reinterpret_cast(&pRing->ptr), 27 | &pRing->physAddr); 28 | if (rc != kIOReturnSuccess) 29 | return kIOReturnNoMemory; 30 | pRing->numTRBs = static_cast(numPages * (PAGE_SIZE / sizeof *pRing->ptr)); 31 | pRing->numPages = static_cast(numPages); 32 | pRing->cycleState = 1U; 33 | InitPreallocedRing(pRing); 34 | return kIOReturnSuccess; 35 | } 36 | 37 | __attribute__((visibility("hidden"))) 38 | void CLASS::InitPreallocedRing(ringStruct* pRing) 39 | { 40 | pRing->enqueueIndex = 0U; 41 | pRing->dequeueIndex = 0U; 42 | pRing->lastSeenDequeueIndex = 0U; 43 | pRing->lastSeenFrame = 0U; 44 | pRing->nextIsocFrame = 0ULL; 45 | TRBStruct* t = &pRing->ptr[pRing->numTRBs - 1U]; 46 | SetTRBAddr64(t, pRing->physAddr); 47 | t->d |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) | XHCI_TRB_3_TC_BIT; 48 | } 49 | 50 | __attribute__((visibility("hidden"))) 51 | ringStruct* CLASS::GetRing(int32_t slot, int32_t endpoint, uint32_t streamId) 52 | { 53 | if (streamId <= ConstSlotPtr(slot)->lastStreamForEndpoint[endpoint]) { 54 | ringStruct* pRing = SlotPtr(slot)->ringArrayForEndpoint[endpoint]; 55 | if (pRing) 56 | return &pRing[streamId]; 57 | } 58 | return 0; 59 | } 60 | 61 | __attribute__((visibility("hidden"))) 62 | void CLASS::DeallocRing(ringStruct* pRing) 63 | { 64 | if (!pRing) 65 | return; 66 | if (pRing->md) { 67 | pRing->md->complete(); 68 | pRing->md->release(); 69 | pRing->md = 0; 70 | } 71 | pRing->ptr = 0; 72 | pRing->physAddr = 0ULL; 73 | pRing->numTRBs = 0U; 74 | } 75 | 76 | __attribute__((visibility("hidden"))) 77 | ringStruct* CLASS::CreateRing(int32_t slot, int32_t endpoint, uint32_t maxStream) 78 | { 79 | SlotStruct* pSlot = SlotPtr(slot); 80 | if (pSlot->ringArrayForEndpoint[endpoint]) { 81 | if (maxStream > pSlot->maxStreamForEndpoint[endpoint]) 82 | return 0; 83 | return pSlot->ringArrayForEndpoint[endpoint]; 84 | } 85 | ringStruct* pRing = static_cast(IOMalloc((1U + maxStream) * sizeof *pRing)); 86 | if (!pRing) 87 | return pRing; 88 | bzero(pRing, (1U + maxStream) * sizeof *pRing); 89 | pSlot->maxStreamForEndpoint[endpoint] = static_cast(maxStream); 90 | pSlot->lastStreamForEndpoint[endpoint] = 0U; 91 | pSlot->ringArrayForEndpoint[endpoint] = pRing; 92 | for (uint32_t streamId = 0U; streamId <= maxStream; ++streamId) { 93 | pRing[streamId].slot = static_cast(slot); 94 | pRing[streamId].endpoint = static_cast(endpoint); 95 | } 96 | return pRing; 97 | } 98 | 99 | __attribute__((visibility("hidden"))) 100 | int32_t CLASS::CountRingToED(ringStruct const* pRing, int32_t trbIndexInRingQueue, uint32_t* pShortFall) 101 | { 102 | int32_t next; 103 | uint32_t trbType; 104 | TRBStruct* pTrb = &pRing->ptr[trbIndexInRingQueue]; 105 | 106 | /* 107 | * Note: CountRingToED is called with an index taken from 108 | * a Transfer Event TRB. For a stop TRB the index 109 | * may be pRing->enqueueIndex, so check to make sure. 110 | */ 111 | if (trbIndexInRingQueue == pRing->enqueueIndex) 112 | return trbIndexInRingQueue; 113 | trbType = XHCI_TRB_3_TYPE_GET(pTrb->d); 114 | while (trbType != XHCI_TRB_TYPE_EVENT_DATA && 115 | (pTrb->d & XHCI_TRB_3_CHAIN_BIT)) { 116 | next = trbIndexInRingQueue + 1; 117 | if (trbType == XHCI_TRB_TYPE_LINK || next >= static_cast(pRing->numTRBs) - 1) 118 | next = 0; 119 | if (next == static_cast(pRing->enqueueIndex)) 120 | break; 121 | trbIndexInRingQueue = next; 122 | pTrb = &pRing->ptr[trbIndexInRingQueue]; 123 | trbType = XHCI_TRB_3_TYPE_GET(pTrb->d); 124 | if (trbType == XHCI_TRB_TYPE_NORMAL) 125 | *pShortFall += XHCI_TRB_2_BYTES_GET(pTrb->c); 126 | } 127 | return trbIndexInRingQueue; 128 | } 129 | 130 | __attribute__((visibility("hidden"))) 131 | uint16_t CLASS::NextTransferDQ(ringStruct const* pRing, int32_t index) 132 | { 133 | /* 134 | * Note: Assumes index != pRing->enqueueIndex 135 | */ 136 | ++index; 137 | if ((index >= static_cast(pRing->numTRBs) - 1) || 138 | (index != pRing->enqueueIndex && 139 | XHCI_TRB_3_TYPE_GET(pRing->ptr[index].d) == XHCI_TRB_TYPE_LINK)) 140 | index = 0; 141 | return static_cast(index); 142 | } 143 | -------------------------------------------------------------------------------- /Slots.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Slots.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on October 10th 2013. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | // 9 | 10 | #include "GenericUSBXHCI.h" 11 | #include "Async.h" 12 | #include "XHCITypes.h" 13 | 14 | #define CLASS GenericUSBXHCI 15 | #define super IOUSBControllerV3 16 | 17 | #pragma mark - 18 | #pragma mark Control Endpoints/Slots 19 | #pragma mark - 20 | 21 | __attribute__((visibility("hidden"))) 22 | uint8_t CLASS::GetSlotID(int32_t address) 23 | { 24 | if (address == _hub3Address || 25 | address == _hub2Address || 26 | static_cast(address) >= kUSBMaxDevices || 27 | !_addressMapper.Active[address]) 28 | return 0U; 29 | return _addressMapper.Slot[address]; 30 | } 31 | 32 | __attribute__((visibility("hidden"))) 33 | IOReturn CLASS::AddressDevice(uint32_t deviceSlot, uint16_t maxPacketSize, bool wantSAR, uint8_t speed, int32_t highSpeedHubSlot, int32_t highSpeedPort) 34 | { 35 | ringStruct* pRing; 36 | ContextStruct *pContext, *pHubContext; 37 | uint32_t routeString; 38 | int32_t retFromCMD; 39 | uint16_t currentPortOnHub, currentHubAddress, addr; 40 | TRBStruct localTrb = { 0 }; 41 | 42 | pRing = GetRing(deviceSlot, 1, 0U); 43 | if (pRing->isInactive()) 44 | return kIOReturnInternalError; 45 | currentPortOnHub = _deviceZero.PortOnHub; 46 | currentHubAddress = _deviceZero.HubAddress; 47 | routeString = 0U; 48 | for (int32_t depth = 0; depth < 5; ++depth) { 49 | if (currentHubAddress == _hub3Address || currentHubAddress == _hub2Address) 50 | break; 51 | if (currentPortOnHub > kMaxExternalHubPorts) 52 | currentPortOnHub = kMaxExternalHubPorts; 53 | routeString = (routeString << 4) + currentPortOnHub; 54 | addr = currentHubAddress; 55 | currentHubAddress = _addressMapper.HubAddress[addr]; 56 | currentPortOnHub = _addressMapper.PortOnHub[addr]; 57 | } 58 | if (currentHubAddress != _hub3Address && currentHubAddress != _hub2Address) { 59 | IOLog("%s: Root hub port not found in topology: hub: %u, rootHubSS: %u rootHubHS: %u\n", __FUNCTION__, 60 | currentHubAddress, _hub3Address, _hub2Address); 61 | return kIOReturnInternalError; 62 | } 63 | if (!currentPortOnHub || currentPortOnHub > _rootHubNumPorts) { 64 | IOLog("%s: Root hub port number invalid: %u\n", __FUNCTION__, currentPortOnHub); 65 | return kIOReturnInternalError; 66 | } 67 | GetInputContext(); 68 | pContext = GetInputContextPtr(); 69 | pContext->_ic.dwInCtx1 = XHCI_INCTX_1_ADD_MASK(1U) | XHCI_INCTX_1_ADD_MASK(0U); 70 | pContext = GetInputContextPtr(1); 71 | pContext->_s.dwSctx1 |= XHCI_SCTX_1_RH_PORT_SET(static_cast(currentPortOnHub)); 72 | pContext->_s.dwSctx0 = XHCI_SCTX_0_CTX_NUM_SET(1U); 73 | #if 0 74 | uint32_t portSpeed = Read32Reg(&_pXHCIOperationalRegisters->prs[currentPortOnHub - 1U].PortSC); 75 | if (m_invalid_regspace) 76 | return kIOReturnNoDevice; 77 | portSpeed = XHCI_PS_SPEED_GET(portSpeed); 78 | if (portSpeed >= XDEV_SS) { 79 | SetSlCtxSpeed(pContext, portSpeed); 80 | maxPacketSize = 512U; 81 | goto skip_low_full; 82 | } 83 | #else 84 | if ((currentHubAddress == _hub3Address) && 85 | (speed < kUSBDeviceSpeedSuper || maxPacketSize != 512U)) 86 | IOLog("%s: Inconsistent device speed %u (maxPacketSize %u) for topology rooted in SuperSpeed hub\n", 87 | __FUNCTION__, speed, maxPacketSize); 88 | #endif 89 | switch (speed) { 90 | case kUSBDeviceSpeedLow: 91 | SetSlCtxSpeed(pContext, XDEV_LS); 92 | break; 93 | case kUSBDeviceSpeedFull: 94 | default: 95 | SetSlCtxSpeed(pContext, XDEV_FS); 96 | break; 97 | case kUSBDeviceSpeedHigh: 98 | SetSlCtxSpeed(pContext, XDEV_HS); 99 | goto skip_low_full; 100 | case kUSBDeviceSpeedSuper: 101 | SetSlCtxSpeed(pContext, XDEV_SS); 102 | goto skip_low_full; 103 | } 104 | /* 105 | * Note: Only for Low or Full Speed devices 106 | */ 107 | if (highSpeedHubSlot) { 108 | pContext->_s.dwSctx2 |= XHCI_SCTX_2_TT_PORT_NUM_SET(highSpeedPort); 109 | pContext->_s.dwSctx2 |= XHCI_SCTX_2_TT_HUB_SID_SET(highSpeedHubSlot); 110 | pHubContext = GetSlotContext(highSpeedHubSlot); 111 | if (pHubContext && XHCI_SCTX_0_MTT_GET(pHubContext->_s.dwSctx0)) 112 | pContext->_s.dwSctx0 |= XHCI_SCTX_0_MTT_SET(1U); 113 | else 114 | pContext->_s.dwSctx0 &= ~XHCI_SCTX_0_MTT_SET(1U); 115 | } 116 | skip_low_full: 117 | #if kMaxActiveInterrupters > 1 118 | pContext->_s.dwSctx2 |= XHCI_SCTX_2_IRQ_TARGET_SET(1U); 119 | #endif 120 | pContext->_s.dwSctx0 |= XHCI_SCTX_0_ROUTE_SET(routeString); 121 | pContext = GetInputContextPtr(2); 122 | pContext->_e.dwEpCtx1 |= XHCI_EPCTX_1_EPTYPE_SET(CTRL_EP); 123 | pContext->_e.dwEpCtx1 |= XHCI_EPCTX_1_MAXP_SIZE_SET(static_cast(maxPacketSize)); 124 | pContext->_e.qwEpCtx2 |= (pRing->physAddr + pRing->dequeueIndex * sizeof *pRing->ptr) & XHCI_EPCTX_2_TR_DQ_PTR_MASK; 125 | if (pRing->cycleState) 126 | pContext->_e.qwEpCtx2 |= XHCI_EPCTX_2_DCS_SET(1U); 127 | else 128 | pContext->_e.qwEpCtx2 &= ~static_cast(XHCI_EPCTX_2_DCS_SET(1U)); 129 | pContext->_e.dwEpCtx1 |= XHCI_EPCTX_1_CERR_SET(3U); 130 | pContext->_e.dwEpCtx4 |= XHCI_EPCTX_4_AVG_TRB_LEN_SET(8U); 131 | SetTRBAddr64(&localTrb, _inputContext.physAddr); 132 | localTrb.d |= XHCI_TRB_3_SLOT_SET(deviceSlot); 133 | if (!wantSAR) 134 | localTrb.d |= XHCI_TRB_3_BSR_BIT; 135 | retFromCMD = WaitForCMD(&localTrb, XHCI_TRB_TYPE_ADDRESS_DEVICE, 0); 136 | ReleaseInputContext(); 137 | if (retFromCMD == -1) 138 | return kIOReturnInternalError; 139 | else if (retFromCMD > -1000) 140 | return kIOReturnSuccess; 141 | else if (retFromCMD == -1000 - XHCI_TRB_ERROR_PARAMETER) { 142 | #if 0 143 | PrintContext(GetInputContextPtr()); 144 | PrintContext(GetInputContextPtr(1)); 145 | PrintContext(GetInputContextPtr(2)); 146 | #endif 147 | } 148 | return TranslateXHCIStatus(-1000 - retFromCMD, deviceSlot, false); 149 | } 150 | 151 | __attribute__((visibility("hidden"))) 152 | void CLASS::NukeSlot(uint8_t slot) 153 | { 154 | SlotStruct* pSlot = SlotPtr(slot); 155 | if (pSlot->isInactive()) 156 | return; 157 | for (int32_t endpoint = 1; endpoint != kUSBMaxPipes; ++endpoint) { 158 | ringStruct* pRing = pSlot->ringArrayForEndpoint[endpoint]; 159 | if (!pRing) { 160 | pSlot->maxStreamForEndpoint[endpoint] = 0U; 161 | pSlot->lastStreamForEndpoint[endpoint] = 0U; 162 | continue; 163 | } 164 | uint16_t lastStream = pSlot->lastStreamForEndpoint[endpoint]; 165 | for (uint16_t streamId = 0U; streamId <= lastStream; ++streamId) { 166 | if ((pRing[streamId].epType | CTRL_EP) == ISOC_IN_EP) { 167 | if (pRing[streamId].isochEndpoint) 168 | NukeIsochEP(pRing[streamId].isochEndpoint); 169 | } else { 170 | #if 1 171 | if (pRing[streamId].asyncEndpoint) 172 | pRing[streamId].asyncEndpoint->nuke(); 173 | #else 174 | /* 175 | * TBD: This is now safe, as Abort() no longer 176 | * calls SetTRDQPtr, however this code will carry 177 | * out the completions from aborted transactions. 178 | * Want that? 179 | */ 180 | if (pRing[streamId].asyncEndpoint) { 181 | pRing[streamId].asyncEndpoint->Abort(); 182 | pRing[streamId].asyncEndpoint->release(); 183 | } 184 | #endif 185 | } 186 | } 187 | DeallocRing(pRing); 188 | IOFree(pRing, (1U + static_cast(pSlot->maxStreamForEndpoint[endpoint])) * sizeof *pRing); 189 | pSlot->maxStreamForEndpoint[endpoint] = 0U; 190 | pSlot->lastStreamForEndpoint[endpoint] = 0U; 191 | pSlot->ringArrayForEndpoint[endpoint] = 0; 192 | } 193 | pSlot->ctx = 0; 194 | pSlot->md->complete(); 195 | pSlot->md->release(); 196 | pSlot->md = 0; 197 | pSlot->physAddr = 0ULL; 198 | pSlot->deviceNeedsReset = false; 199 | } 200 | 201 | __attribute__((visibility("hidden"))) 202 | int32_t CLASS::CleanupControlEndpoint(uint8_t slot, bool justDisable) 203 | { 204 | TRBStruct localTrb = { 0U }; 205 | SlotStruct* pSlot; 206 | ringStruct* pRing; 207 | 208 | if (justDisable) 209 | goto do_disable; 210 | pSlot = SlotPtr(slot); 211 | pRing = pSlot->ringArrayForEndpoint[1]; 212 | if (pRing) { 213 | DeallocRing(pRing); 214 | IOFree(pRing, sizeof *pRing); 215 | pSlot->ringArrayForEndpoint[1] = 0; 216 | } 217 | if (pSlot->md) { 218 | pSlot->md->complete(); 219 | pSlot->md->release(); 220 | pSlot->md = 0; 221 | pSlot->ctx = 0; 222 | pSlot->physAddr = 0U; 223 | } 224 | _addressMapper.Slot[0] = 0U; 225 | _addressMapper.Active[0] = false; 226 | 227 | do_disable: 228 | localTrb.d = XHCI_TRB_3_SLOT_SET(static_cast(slot)); 229 | return WaitForCMD(&localTrb, XHCI_TRB_TYPE_DISABLE_SLOT, 0); 230 | } 231 | 232 | #pragma mark - 233 | #pragma mark Timeouts 234 | #pragma mark - 235 | 236 | __attribute__((visibility("hidden"))) 237 | bool CLASS::IsStillConnectedAndEnabled(int32_t slot) 238 | { 239 | ContextStruct* pContext; 240 | uint32_t portSC; 241 | uint8_t port; 242 | 243 | pContext = GetSlotContext(slot); 244 | if (!pContext) 245 | return false; 246 | port = static_cast(XHCI_SCTX_1_RH_PORT_GET(pContext->_s.dwSctx1)) - 1U; 247 | if (port >= _rootHubNumPorts) 248 | return false; 249 | portSC = Read32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC); 250 | if (m_invalid_regspace) 251 | return false; 252 | return (portSC & (XHCI_PS_PED | XHCI_PS_CCS)) == (XHCI_PS_PED | XHCI_PS_CCS); 253 | } 254 | 255 | __attribute__((visibility("hidden"))) 256 | void CLASS::CheckSlotForTimeouts(int32_t slot, uint32_t frameNumber, bool abortAll) 257 | { 258 | SlotStruct* pSlot = SlotPtr(slot); 259 | for (int32_t endpoint = 1; endpoint != kUSBMaxPipes; ++endpoint) { 260 | ringStruct* pRing = pSlot->ringArrayForEndpoint[endpoint]; 261 | if (pRing->isInactive() || 262 | (pRing->epType | CTRL_EP) == ISOC_IN_EP) 263 | continue; 264 | if (pSlot->IsStreamsEndpoint(endpoint)) { 265 | bool stopped = false; 266 | uint16_t lastStream = pSlot->lastStreamForEndpoint[endpoint]; 267 | for (uint16_t streamId = 1U; streamId <= lastStream; ++streamId) 268 | if (checkEPForTimeOuts(slot, endpoint, streamId, frameNumber, abortAll)) 269 | stopped = true; 270 | if (stopped) 271 | RestartStreams(slot, endpoint, 0U); 272 | } else if (checkEPForTimeOuts(slot, endpoint, 0U, frameNumber, abortAll)) 273 | StartEndpoint(slot, endpoint, 0U); 274 | } 275 | } 276 | 277 | #pragma mark - 278 | #pragma mark External Hubs 279 | #pragma mark - 280 | 281 | __attribute__((visibility("hidden"))) 282 | IOReturn CLASS::configureHub(uint32_t deviceAddress, uint32_t flags) 283 | { 284 | uint8_t slot; 285 | uint32_t nop, ttt; 286 | int32_t retFromCMD; 287 | ContextStruct* pContext; 288 | TRBStruct localTrb = { 0 }; 289 | 290 | if (deviceAddress == _hub3Address || deviceAddress == _hub2Address) 291 | return kIOReturnSuccess; 292 | slot = GetSlotID(static_cast(deviceAddress)); 293 | if (!slot) 294 | return kIOReturnBadArgument; 295 | if (flags & kUSBHSHubFlagsMoreInfoMask) { 296 | ttt = (flags & kUSBHSHubFlagsTTThinkTimeMask) >> kUSBHSHubFlagsTTThinkTimeShift; 297 | nop = (flags & kUSBHSHubFlagsNumPortsMask) >> kUSBHSHubFlagsNumPortsShift; 298 | } else { 299 | ttt = 3U; // 32 FS bit times 300 | nop = kMaxExternalHubPorts; 301 | } 302 | GetInputContext(); 303 | pContext = GetInputContextPtr(); 304 | pContext->_ic.dwInCtx1 = XHCI_INCTX_1_ADD_MASK(0U); 305 | pContext = GetInputContextPtr(1); 306 | *pContext = *GetSlotContext(slot); 307 | pContext->_s.dwSctx0 |= XHCI_SCTX_0_HUB_SET(1U); 308 | if (flags & kUSBHSHubFlagsMultiTTMask) 309 | pContext->_s.dwSctx0 |= XHCI_SCTX_0_MTT_SET(1U); 310 | else 311 | pContext->_s.dwSctx0 &= ~XHCI_SCTX_0_MTT_SET(1U); 312 | pContext->_s.dwSctx1 &= ~XHCI_SCTX_1_NUM_PORTS_SET(255U); 313 | pContext->_s.dwSctx1 |= XHCI_SCTX_1_NUM_PORTS_SET(nop); 314 | pContext->_s.dwSctx2 &= ~XHCI_SCTX_2_TT_THINK_TIME_SET(3U); 315 | pContext->_s.dwSctx2 |= XHCI_SCTX_2_TT_THINK_TIME_SET(ttt); 316 | SetTRBAddr64(&localTrb, _inputContext.physAddr); 317 | localTrb.d |= XHCI_TRB_3_SLOT_SET(static_cast(slot)); 318 | retFromCMD = WaitForCMD(&localTrb, XHCI_TRB_TYPE_CONFIGURE_EP, 0); 319 | ReleaseInputContext(); 320 | if (retFromCMD == -1) 321 | return kIOReturnInternalError; 322 | if (retFromCMD > -1000) 323 | return kIOReturnSuccess; 324 | if (retFromCMD == -1000 - XHCI_TRB_ERROR_PARAMETER) { 325 | #if 0 326 | PrintContext(GetInputContextPtr()); 327 | PrintContext(pContext); 328 | #endif 329 | } 330 | return kIOReturnInternalError; 331 | } 332 | 333 | #pragma mark - 334 | #pragma mark Test Mode 335 | #pragma mark - 336 | 337 | __attribute__((visibility("hidden"))) 338 | IOReturn CLASS::EnterTestMode(void) 339 | { 340 | IOReturn rc; 341 | uint32_t portSC; 342 | 343 | EnableComplianceMode(); 344 | for (uint8_t port = 0U; port < _rootHubNumPorts; ++port) { 345 | portSC = Read32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC); 346 | if (m_invalid_regspace) 347 | return kIOReturnNoDevice; 348 | if (portSC & XHCI_PS_PP) 349 | XHCIRootHubPowerPort(port, false); 350 | } 351 | rc = StopUSBBus(); 352 | if (rc != kIOReturnSuccess) 353 | return rc; 354 | _myBusState = kUSBBusStateReset; 355 | _inTestMode = true; 356 | return kIOReturnSuccess; 357 | } 358 | 359 | __attribute__((visibility("hidden"))) 360 | IOReturn CLASS::LeaveTestMode(void) 361 | { 362 | IOReturn rc; 363 | 364 | DisableComplianceMode(); 365 | rc = StopUSBBus(); 366 | if (rc != kIOReturnSuccess) 367 | return rc; 368 | rc = ResetController(); 369 | if (rc != kIOReturnSuccess) 370 | return rc; 371 | _inTestMode = false; 372 | return kIOReturnSuccess; 373 | } 374 | 375 | __attribute__((visibility("hidden"))) 376 | IOReturn CLASS::PlacePortInMode(uint32_t port, uint32_t mode) 377 | { 378 | IOReturn rc; 379 | uint32_t portPMSC, cmd; 380 | uint16_t _port; 381 | 382 | if (!_inTestMode) 383 | return kIOReturnInternalError; 384 | _port = PortNumberProtocolToCanonical(port, kUSBDeviceSpeedHigh); 385 | if (_port >= _rootHubNumPorts) 386 | return kIOReturnInternalError; 387 | portPMSC = Read32Reg(&_pXHCIOperationalRegisters->prs[_port].PortPmsc); 388 | if (m_invalid_regspace) 389 | return kIOReturnNoDevice; 390 | portPMSC &= ~(15U << 28); // drop TestMode for USB2 port 391 | if (mode != 5U) { 392 | rc = StopUSBBus(); 393 | if (rc != kIOReturnSuccess) 394 | return rc; 395 | } 396 | portPMSC |= (mode & 15U) << 28; 397 | Write32Reg(&_pXHCIOperationalRegisters->prs[_port].PortPmsc, portPMSC); 398 | if (mode != 5U) 399 | return kIOReturnSuccess; 400 | cmd = Read32Reg(&_pXHCIOperationalRegisters->USBCmd); 401 | if (m_invalid_regspace) 402 | return kIOReturnNoDevice; 403 | cmd |= XHCI_CMD_RS; 404 | Write32Reg(&_pXHCIOperationalRegisters->USBCmd, cmd); 405 | WaitForUSBSts(XHCI_STS_HCH, 0U); 406 | return kIOReturnSuccess; 407 | } 408 | -------------------------------------------------------------------------------- /Streams.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Streams.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on October 9th, 2013. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCI.h" 10 | #include "Async.h" 11 | #include "XHCITypes.h" 12 | 13 | #define CLASS GenericUSBXHCI 14 | #define super IOUSBControllerV3 15 | 16 | 17 | #pragma mark - 18 | #pragma mark Sorting Functions 19 | #pragma mark - 20 | 21 | static 22 | void swap(ringStruct* pRing, int32_t L, int32_t R) 23 | { 24 | uint64_t swap_key = pRing[L].physAddr; 25 | IOBufferMemoryDescriptor* swap_value_1 = pRing[L].md; 26 | TRBStruct* swap_value_2 = pRing[L].ptr; 27 | pRing[L].physAddr = pRing[R].physAddr; 28 | pRing[L].md = pRing[R].md; 29 | pRing[L].ptr = pRing[R].ptr; 30 | pRing[R].physAddr = swap_key; 31 | pRing[R].md = swap_value_1; 32 | pRing[R].ptr = swap_value_2; 33 | } 34 | 35 | /* 36 | * Returns index P such that 37 | * for all index > P elements are > pivot_value 38 | * for all index <= P, elements are <= pivot_value 39 | */ 40 | static 41 | int32_t partition(ringStruct* pRing, int32_t L, int32_t R, int32_t pivot_index) 42 | { 43 | uint64_t pivot_value = pRing[pivot_index].physAddr; 44 | while (L <= R) { 45 | while (L <= R && CLASS::DiffTRBIndex(pRing[R].physAddr, pivot_value) > 0) 46 | --R; 47 | while (L <= R && CLASS::DiffTRBIndex(pRing[L].physAddr, pivot_value) <= 0) 48 | ++L; 49 | /* 50 | * Note: Either L < R, or L == R + 1 here 51 | */ 52 | if (L < R) 53 | swap(pRing, L++, R--); 54 | } 55 | return R; 56 | } 57 | 58 | static 59 | void qsort(ringStruct* pRing, uint32_t maxStream) 60 | { 61 | int32_t lefts[9], rights[9], tos, L, R, pivot_index; 62 | tos = 0; 63 | lefts[tos] = 1; 64 | rights[tos] = static_cast(maxStream); 65 | while (tos >= 0) { 66 | L = lefts[tos]; 67 | R = rights[tos]; 68 | if (L >= R) { 69 | --tos; 70 | continue; 71 | } 72 | pivot_index = partition(pRing, L, R, R); 73 | if (pivot_index == R) 74 | /* 75 | * Note: This happens if all elements are <= pivot, in 76 | * which case we're guaranteed that nothing was 77 | * moved, and the pivot is still at R. 78 | */ 79 | --pivot_index; 80 | if ((R - pivot_index) <= (pivot_index - L + 1)) { 81 | lefts[tos] = L; 82 | rights[tos] = pivot_index; 83 | ++tos; 84 | lefts[tos] = pivot_index + 1; 85 | rights[tos] = R; 86 | } else { 87 | lefts[tos] = pivot_index + 1; 88 | rights[tos] = R; 89 | ++tos; 90 | lefts[tos] = L; 91 | rights[tos] = pivot_index; 92 | } 93 | } 94 | } 95 | 96 | #pragma mark - 97 | #pragma mark Streams 98 | #pragma mark - 99 | 100 | __attribute__((visibility("hidden"))) 101 | void CLASS::RestartStreams(int32_t slot, int32_t endpoint, uint32_t streamId) 102 | { 103 | SlotStruct* pSlot = SlotPtr(slot); 104 | ringStruct* pRing = pSlot->ringArrayForEndpoint[endpoint]; 105 | if (!pRing) 106 | return; 107 | uint16_t lastStream = pSlot->lastStreamForEndpoint[endpoint]; 108 | if (lastStream < 2U) 109 | return; 110 | /* 111 | * Note: It is probably enough to ring the doorbell 112 | * for the first stream found with a non-empty 113 | * ring. 114 | */ 115 | for (uint16_t sid = 1U; sid <= lastStream; ++sid) 116 | if (sid != streamId && 117 | pRing[sid].dequeueIndex != pRing[sid].enqueueIndex) 118 | StartEndpoint(slot, endpoint, sid); 119 | } 120 | 121 | __attribute__((visibility("hidden"))) 122 | IOReturn CLASS::CreateStream(ringStruct* pRing, uint16_t streamId) 123 | { 124 | if (!pRing || 125 | streamId >= pRing->numTRBs || 126 | !pRing->ptr) 127 | return kIOReturnBadArgument; 128 | ringStruct* pStreamRing = &pRing[streamId]; 129 | pStreamRing->nextIsocFrame = 0ULL; 130 | pStreamRing->returnInProgress = false; 131 | pStreamRing->deleteInProgress = false; 132 | pStreamRing->needsDoorbell = false; 133 | if (pStreamRing->md) 134 | return kIOReturnInternalError; 135 | pStreamRing->md = pRing->md; 136 | pStreamRing->epType = pRing->epType; 137 | XHCIAsyncEndpoint* pAsyncEp = pRing->asyncEndpoint; 138 | pStreamRing->asyncEndpoint = XHCIAsyncEndpoint::withParameters(this, pStreamRing, 139 | pAsyncEp->maxPacketSize, 140 | pAsyncEp->maxBurst, 141 | pAsyncEp->multiple); 142 | if (!pStreamRing->asyncEndpoint) 143 | return kIOReturnNoMemory; 144 | return kIOReturnSuccess; 145 | } 146 | 147 | __attribute__((visibility("hidden"))) 148 | void CLASS::CleanupPartialStreamAllocations(ringStruct* pRing, uint16_t lastStream) 149 | { 150 | for (uint16_t i = 1U; i <= lastStream; ++i) { 151 | if (pRing[i].asyncEndpoint) { 152 | pRing[i].asyncEndpoint->release(); 153 | pRing[i].asyncEndpoint = 0; 154 | } 155 | pRing[i].md = 0; 156 | } 157 | } 158 | 159 | __attribute__((visibility("hidden"))) 160 | ringStruct* CLASS::FindStream(int32_t slot, int32_t endpoint, uint64_t addr, int32_t* pTrbIndexInRingQueue) 161 | { 162 | int64_t diffIdx64; 163 | SlotStruct* pSlot = SlotPtr(slot); 164 | ringStruct *pStreamRing, *pRing = pSlot->ringArrayForEndpoint[endpoint]; 165 | int32_t M, L = 1, R = pSlot->lastStreamForEndpoint[endpoint]; 166 | while (L <= R) { 167 | M = (L + R) / 2; 168 | pStreamRing = &pRing[M]; 169 | diffIdx64 = DiffTRBIndex(addr, pStreamRing->physAddr); 170 | if (diffIdx64 < 0) { 171 | R = M - 1; 172 | continue; 173 | } 174 | if (diffIdx64 >= pStreamRing->numTRBs - 1U) { // Note: originally > 175 | L = M + 1; 176 | continue; 177 | } 178 | *pTrbIndexInRingQueue = static_cast(diffIdx64); 179 | return pStreamRing; 180 | } 181 | *pTrbIndexInRingQueue = 0; 182 | return 0; 183 | } 184 | 185 | __attribute__((visibility("hidden"))) 186 | void CLASS::DeleteStreams(int32_t slot, int32_t endpoint) 187 | { 188 | SlotStruct* pSlot = SlotPtr(slot); 189 | ringStruct* pRing = pSlot->ringArrayForEndpoint[endpoint]; 190 | if (!pRing) 191 | return; 192 | uint16_t lastStream = pSlot->lastStreamForEndpoint[endpoint]; 193 | for (uint16_t streamId = 1U; streamId <= lastStream; ++streamId) { 194 | XHCIAsyncEndpoint* pAsyncEp = pRing[streamId].asyncEndpoint; 195 | if (pAsyncEp) { 196 | pAsyncEp->Abort(); 197 | pAsyncEp->release(); 198 | pRing[streamId].asyncEndpoint = 0; 199 | } 200 | pRing[streamId].md = 0; 201 | } 202 | } 203 | 204 | __attribute__((visibility("hidden"))) 205 | IOReturn CLASS::AllocStreamsContextArray(ringStruct* pRing, uint32_t maxStream) 206 | { 207 | size_t scaSize = ((1U + maxStream) * sizeof(xhci_stream_ctx) + PAGE_SIZE - 1U) & -PAGE_SIZE; 208 | /* 209 | * TBD: this is contingent on kMaxStreamsAllowed <= 256, so 210 | * maxStream <= 255 and therefore scaSize is always PAGE_SIZE. 211 | * If scaSize > PAGE_SIZE, the first scaSize bytes need 212 | * to be kIOMemoryPhysicallyContiguous. 213 | */ 214 | if (kIOReturnSuccess != MakeBuffer(kIODirectionInOut, 215 | scaSize + maxStream * PAGE_SIZE, 216 | -PAGE_SIZE, 217 | &pRing->md, 218 | reinterpret_cast(&pRing->ptr), 219 | &pRing->physAddr)) 220 | return kIOReturnNoMemory; 221 | pRing->numTRBs = static_cast(1U + maxStream); 222 | pRing->dequeueIndex = 0U; 223 | for (uint16_t streamId = 1U; streamId <= maxStream; ++streamId, scaSize += PAGE_SIZE) { 224 | ringStruct* pStreamRing = &pRing[streamId]; 225 | if (pStreamRing->md) 226 | continue; 227 | IOByteCount segLength; 228 | pStreamRing->ptr = reinterpret_cast(reinterpret_cast(pRing->ptr) + scaSize); 229 | pStreamRing->physAddr = pRing->md->getPhysicalSegment(scaSize, &segLength, 0); 230 | pStreamRing->numTRBs = static_cast(PAGE_SIZE / sizeof *pStreamRing->ptr); 231 | pStreamRing->numPages = 1U; 232 | pStreamRing->cycleState = 1U; 233 | } 234 | qsort(pRing, maxStream); 235 | for (uint16_t streamId = 1U; streamId <= maxStream; ++streamId) { 236 | ringStruct* pStreamRing = &pRing[streamId]; 237 | if (pStreamRing->md) 238 | continue; 239 | InitPreallocedRing(pStreamRing); 240 | SetTRBAddr64(&pRing->ptr[streamId], (pStreamRing->physAddr & ~15ULL) | 3ULL); 241 | } 242 | return kIOReturnSuccess; 243 | } 244 | -------------------------------------------------------------------------------- /UnXHCI.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // UnXHCI.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on January 26th, 2013. 6 | // Copyright (c) 2013-2014 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCI.h" 10 | #include 11 | 12 | #define CLASS GenericUSBXHCI 13 | #define super IOUSBControllerV3 14 | 15 | #pragma mark - 16 | #pragma mark Stuff Extraneous to the xHC Specification 17 | #pragma mark (Mostly Intel-Specific) 18 | #pragma mark - 19 | 20 | __attribute__((visibility("hidden"))) 21 | void CLASS::EnableXHCIPorts(void) 22 | { 23 | uint32_t v1, v2, v3, v4; 24 | 25 | if (_vendorID != kVendorIntel) 26 | return; 27 | v1 = _device->configRead32(PCI_XHCI_INTEL_XUSB2PR); 28 | if (v1 == UINT32_MAX) { 29 | m_invalid_regspace = true; 30 | return; 31 | } 32 | v2 = _device->configRead32(PCI_XHCI_INTEL_XUSB2PRM); 33 | if (v2 == UINT32_MAX) { 34 | m_invalid_regspace = true; 35 | return; 36 | } 37 | v3 = _device->configRead32(PCI_XHCI_INTEL_USB3_PSSEN); 38 | if (v3 == UINT32_MAX) { 39 | m_invalid_regspace = true; 40 | return; 41 | } 42 | v4 = _device->configRead32(PCI_XHCI_INTEL_USB3PRM); 43 | if (v4 == UINT32_MAX) { 44 | m_invalid_regspace = true; 45 | return; 46 | } 47 | v1 &= ~v2; 48 | if (!(gux_options & GUX_OPTION_DEFER_INTEL_EHC_PORTS)) 49 | v1 |= v2; 50 | _device->configWrite32(PCI_XHCI_INTEL_XUSB2PR, v1); 51 | _device->configWrite32(PCI_XHCI_INTEL_USB3_PSSEN, (v3 & ~v4) | v4); 52 | } 53 | 54 | __attribute__((visibility("hidden"))) 55 | bool CLASS::DiscoverMuxedPorts(void) 56 | { 57 | OSObject* o; 58 | uint32_t n; 59 | uint8_t t; 60 | char* string_buf; 61 | 62 | if (!_rootHubDeviceSS) 63 | goto done; 64 | if (_muxedPortsExist) 65 | goto done; 66 | if (!(_errataBits & kErrataIntelPortMuxing)) 67 | goto done; 68 | o = _rootHubDeviceSS->getProperty(kUSBDevicePropertyLocationID); 69 | if (!o) 70 | goto done; 71 | n = static_cast(o)->unsigned32BitValue(); 72 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 || defined(REHABMAN_UNIVERSAL_BUILD) 73 | if (CHECK_FOR_MAVERICKS) 74 | _providerACPIDevice = _v3ExpansionData ? READ_V3EXPANSION(_acpiDevice) : 0; 75 | else 76 | #endif 77 | _providerACPIDevice = CopyACPIDevice(_device); 78 | if (!_providerACPIDevice) 79 | goto done; 80 | string_buf = &_muxName[0]; 81 | t = 0U; 82 | for (uint32_t port = 1U; port != kMaxExternalHubPorts; ++port) { 83 | if (IsPortMuxed(_device, port, n, string_buf)) 84 | ++t; 85 | string_buf += 5; 86 | } 87 | if (t) 88 | _muxedPortsExist = true; 89 | done: 90 | _muxedPortsSearched = true; 91 | return _muxedPortsExist; 92 | } 93 | 94 | #if 0 95 | /* 96 | * TBD: Does this really work? 97 | * It's not documented anywhere 98 | */ 99 | __attribute__((noinline, visibility("hidden"))) 100 | void CLASS::DisableComplianceMode(void) 101 | { 102 | if ((_vendorID == kVendorFrescoLogic || _vendorID == kVendorIntel) && 103 | !(_errataBits & kErrataEnableAutoCompliance)) { 104 | _pXHCIPPTChickenBits = reinterpret_cast(reinterpret_cast(_pXHCICapRegisters) + 0x80EC); 105 | *_pXHCIPPTChickenBits |= 1U; 106 | } 107 | } 108 | 109 | __attribute__((noinline, visibility("hidden"))) 110 | void CLASS::EnableComplianceMode(void) 111 | { 112 | if ((_vendorID == kVendorFrescoLogic || _vendorID == kVendorIntel) && 113 | !(_errataBits & kErrataEnableAutoCompliance)) { 114 | _pXHCIPPTChickenBits = reinterpret_cast(reinterpret_cast(_pXHCICapRegisters) + 0x80EC); 115 | *_pXHCIPPTChickenBits &= ~1U; 116 | } 117 | } 118 | #endif 119 | 120 | __attribute__((visibility("hidden"))) 121 | IOReturn CLASS::FL1100Tricks(int choice) 122 | { 123 | uint32_t volatile* pReg; 124 | uint32_t v; 125 | 126 | switch (choice) { 127 | case 1: 128 | if (FL1100Tricks(4) != kIOReturnSuccess) 129 | return kIOReturnNoDevice; 130 | return FL1100Tricks(3); 131 | case 2: 132 | pReg = reinterpret_cast(reinterpret_cast(_pXHCICapRegisters) + 0x80C0); 133 | v = Read32Reg(pReg); 134 | if (m_invalid_regspace) 135 | return kIOReturnNoDevice; 136 | Write32Reg(pReg, v & ~0x4000U); 137 | break; 138 | case 3: 139 | pReg = reinterpret_cast(reinterpret_cast(_pXHCICapRegisters) + 0x80EC); 140 | v = Read32Reg(pReg); 141 | if (m_invalid_regspace) 142 | return kIOReturnNoDevice; 143 | v &= ~0x7000U; 144 | v |= 0x5000U; 145 | Write32Reg(pReg, v & 0xEFFFFFFFU); 146 | break; 147 | case 4: 148 | pReg = reinterpret_cast(reinterpret_cast(_pXHCICapRegisters) + 0x8094); 149 | v = Read32Reg(pReg); 150 | if (m_invalid_regspace) 151 | return kIOReturnNoDevice; 152 | Write32Reg(pReg, v | 0x800000U); 153 | break; 154 | } 155 | return kIOReturnSuccess; 156 | } 157 | 158 | __attribute__((visibility("hidden"))) 159 | uint32_t CLASS::VMwarePortStatusShuffle(uint32_t statusChangedBitmap, uint8_t numPortsEach) 160 | { 161 | uint8_t i; 162 | uint32_t mask, outss, ouths, inmap; 163 | outss = 0U; 164 | ouths = 0U; 165 | mask = 2U; 166 | inmap = (statusChangedBitmap >> 1); 167 | for (i = 0U; inmap && i < numPortsEach; ++i, inmap >>= 2, mask <<= 1) { 168 | if (inmap & 1U) 169 | outss |= mask; 170 | if (inmap & 2U) 171 | ouths |= mask; 172 | } 173 | return (statusChangedBitmap & 1U) | outss | (ouths << numPortsEach); 174 | } 175 | 176 | #if 0 177 | __attribute__((visibility("hidden"))) 178 | uint32_t CLASS::CheckACPITablesForCaptiveRootHubPorts(uint8_t numPorts) 179 | { 180 | IOReturn rc; 181 | uint32_t v; 182 | uint8_t connectorType; 183 | 184 | if (!numPorts) 185 | return 0U; 186 | v = 0U; 187 | for (uint8_t port = 1U; port <= numPorts; ++port) { 188 | connectorType = 254U; 189 | /* 190 | * IOReturn IOUSBControllerV3::GetConnectorType(IORegistryEntry* provider, UInt32 portNumber, UInt32 locationID, UInt8* connectorType); 191 | */ 192 | rc = GetConnectorType(_device, port, _expansionData->_locationID, &connectorType); 193 | if (rc == kIOReturnSuccess && connectorType == kUSBProprietaryConnector) 194 | v |= 1U << port; 195 | } 196 | return v; 197 | } 198 | #endif 199 | 200 | __attribute__((visibility("hidden"))) 201 | IOReturn CLASS::HCSelect(uint8_t port, uint8_t controllerType) 202 | { 203 | static char const xHCMuxedPorts[80] = "XHCA\0XHCB\0XHCC\0XHCD"; 204 | char const* method; 205 | 206 | if (!_muxedPortsSearched) 207 | DiscoverMuxedPorts(); 208 | if (!_providerACPIDevice || 209 | !_muxedPortsExist || 210 | port >= kMaxExternalHubPorts) 211 | return kIOReturnNoMemory; 212 | if (controllerType == 1U) 213 | method = &xHCMuxedPorts[port * 5U]; 214 | else if (!controllerType) 215 | method = &_muxName[port * 5U]; 216 | else 217 | return kIOReturnNoMemory; 218 | return HCSelectWithMethod(method); 219 | } 220 | 221 | __attribute__((visibility("hidden"))) 222 | IOReturn CLASS::HCSelectWithMethod(char const* method) 223 | { 224 | UInt32 result = 0U; 225 | 226 | if (!_muxedPortsSearched) 227 | DiscoverMuxedPorts(); 228 | if (!_providerACPIDevice) 229 | return kIOReturnUnsupported; 230 | return _providerACPIDevice->evaluateInteger(method, &result); 231 | } 232 | -------------------------------------------------------------------------------- /V1Overrides.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // V1Overrides.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on December 26th, 2012. 6 | // Copyright (c) 2012-2014 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCI.h" 10 | #include "Async.h" 11 | #include "Isoch.h" 12 | #include "XHCITypes.h" 13 | #include 14 | #include 15 | 16 | #define CLASS GenericUSBXHCI 17 | #define super IOUSBControllerV3 18 | 19 | #pragma mark - 20 | #pragma mark IOUSBController Overrides 21 | #pragma mark - 22 | 23 | __attribute__((noinline)) 24 | UInt32 CLASS::GetErrataBits(UInt16 vendorID, UInt16 deviceID, UInt16 revisionID) 25 | { 26 | ErrataListEntry const errataList[] = { 27 | { kVendorFrescoLogic, 0x1000U, 0U, UINT16_MAX, kErrataDisableMSI }, // Fresco Logic FL1000 28 | { kVendorFrescoLogic, 0x1100U, 0U, 15U, kErrataFL1100LowRev | kErrataParkRing }, // Fresco Logic FL1100, rev 0 - 15 29 | { kVendorFrescoLogic, 0x1100U, 16U, UINT16_MAX, kErrataParkRing }, // Fresco Logic FL1100, rev 16 and up 30 | { kVendorIntel, 0x1E31U, 0U, UINT16_MAX, 31 | kErrataSWAssistedIdle | 32 | kErrataParkRing | kErrataIntelPortMuxing | 33 | kErrataEnableAutoCompliance | kErrataIntelPantherPoint }, // Intel Series 7/C210 34 | { kVendorIntel, 0x8C31U, 0U, UINT16_MAX, kErrataEnableAutoCompliance | kErrataParkRing | kErrataIntelLynxPoint }, // Intel Series 8/C220 35 | { kVendorIntel, 0x9C31U, 0U, UINT16_MAX, kErrataEnableAutoCompliance | kErrataParkRing | kErrataIntelLynxPoint }, // Intel Lynx Point 36 | { kVendorVMware, 0x778U, 0U, UINT16_MAX, kErrataVMwarePortSwap }, // VMware Virtual xHC 37 | { kVendorEtron, 0U, 0U, UINT16_MAX, kErrataBrokenStreams }, // All Etron 38 | { kVendorASMedia, 0x1042, 0U, UINT16_MAX, kErrataBrokenStreams | kErrataAbsoluteEDTLA } // ASMedia 1042 39 | }; 40 | ErrataListEntry const* entryPtr; 41 | uint32_t i, errata = 0U; 42 | for (i = 0U, entryPtr = &errataList[0]; i < (sizeof(errataList) / sizeof(errataList[0])); ++i, ++entryPtr) 43 | if (vendorID == entryPtr->vendID && 44 | (deviceID == entryPtr->deviceID || !entryPtr->deviceID) && 45 | revisionID >= entryPtr->revisionLo && 46 | revisionID <= entryPtr->revisionHi) 47 | errata |= entryPtr->errata; 48 | if (gux_options & GUX_OPTION_NO_MSI) 49 | errata |= kErrataDisableMSI; 50 | /* 51 | * Note: This is done in GetErrata64Bits in Mavericks 52 | */ 53 | if (CHECK_FOR_MAVERICKS) 54 | return errata; 55 | if (getProperty(kIOPCITunnelledKey, gIOServicePlane) == kOSBooleanTrue) { 56 | WRITE_ON_THUNDERBOLT(true); 57 | requireMaxBusStall(25000U); 58 | } 59 | return errata; 60 | } 61 | 62 | void CLASS::UIMCheckForTimeouts(void) 63 | { 64 | uint32_t frameNumber, sts, mfIndex; 65 | uint8_t slot; 66 | 67 | if (!_controllerAvailable || _wakingFromHibernation) 68 | return; 69 | frameNumber = GetFrameNumber32(); 70 | sts = Read32Reg(&_pXHCIOperationalRegisters->USBSts); 71 | if (m_invalid_regspace) { 72 | #if 0 73 | for (slot = 1U; slot <= _numSlots; ++slot) 74 | if (!ConstSlotPtr(slot)->isInactive()) 75 | CheckSlotForTimeouts(slot, 0U, true); 76 | #endif 77 | if (_expansionData) { 78 | _watchdogTimerActive = false; 79 | if (_watchdogUSBTimer) 80 | _watchdogUSBTimer->cancelTimeout(); 81 | } 82 | return; 83 | } 84 | if ((sts & XHCI_STS_HSE) && !_HSEDetected) { 85 | IOLog("%s: HSE bit set:%x (1)\n", __FUNCTION__, sts); 86 | _HSEDetected = true; 87 | } 88 | #if 0 89 | for (slot = 1U; slot <= _numSlots; ++slot) { 90 | SlotStruct* pSlot = SlotPtr(slot); 91 | if (pSlot->isInactive()) 92 | continue; 93 | if (!(pSlot->oneBitCache = IsStillConnectedAndEnabled(slot))) 94 | CheckSlotForTimeouts(slot, frameNumber, true); 95 | } 96 | #endif 97 | if (_powerStateChangingTo != kUSBPowerStateStable && _powerStateChangingTo < kUSBPowerStateOn && _powerStateChangingTo > kUSBPowerStateRestart) 98 | return; 99 | mfIndex = Read32Reg(&_pXHCIRuntimeRegisters->MFIndex); 100 | if (m_invalid_regspace) 101 | return; 102 | mfIndex &= XHCI_MFINDEX_MASK; 103 | if (!mfIndex) 104 | return; 105 | for (slot = 1U; slot <= _numSlots; ++slot) { 106 | SlotStruct const* pSlot = ConstSlotPtr(slot); 107 | if (pSlot->isInactive()) 108 | continue; 109 | #if 0 110 | if (pSlot->oneBitCache) 111 | #endif 112 | CheckSlotForTimeouts(slot, frameNumber, pSlot->deviceNeedsReset); 113 | } 114 | } 115 | 116 | IOReturn CLASS::UIMCreateControlTransfer(short functionNumber, short endpointNumber, IOUSBCommand* command, 117 | IOMemoryDescriptor* CBP, bool /* bufferRounding */, UInt32 bufferSize, 118 | short direction) 119 | { 120 | IOReturn rc; 121 | uint32_t mystery; // Note: structure as fourth uint32_t of Transfer TRB 122 | uint8_t slot, immediateDataSize; 123 | ringStruct* pRing; 124 | SetupStageHeader smallbuf1; 125 | SetupStageHeader smallbuf2; 126 | 127 | slot = GetSlotID(functionNumber); 128 | if (!slot) 129 | return kIOUSBEndpointNotFound; 130 | if (endpointNumber) 131 | return kIOReturnBadArgument; 132 | #if 0 133 | /* 134 | * Note: Added Mavericks 135 | */ 136 | if (!IsStillConnectedAndEnabled(slot)) 137 | return kIOReturnNoDevice; 138 | #endif 139 | pRing = GetRing(slot, 1, 0U); 140 | if (pRing->isInactive()) 141 | return kIOReturnBadArgument; 142 | if (GetNeedsReset(slot)) 143 | return AddDummyCommand(pRing, command); 144 | if (pRing->deleteInProgress) 145 | return kIOReturnNoDevice; 146 | if (pRing->epType != CTRL_EP) 147 | return kIOUSBEndpointNotFound; 148 | XHCIAsyncEndpoint* pAsyncEp = pRing->asyncEndpoint; 149 | if (!pAsyncEp) 150 | return kIOUSBEndpointNotFound; 151 | if (pAsyncEp->aborting) 152 | return kIOReturnNotPermitted; 153 | if (CBP && bufferSize) { 154 | IODMACommand* dmac = command->GetDMACommand(); 155 | if (!dmac) { 156 | IOLog("%s: no dmaCommand\n", __FUNCTION__); 157 | return kIOReturnNoMemory; 158 | } 159 | IOMemoryDescriptor const* dmac_md = dmac->getMemoryDescriptor(); 160 | if (dmac_md != CBP) { 161 | IOLog("%s: mismatched CBP (%p) and dmaCommand memory descriptor (%p)\n", __FUNCTION__, 162 | CBP, dmac_md); 163 | return kIOReturnInternalError; 164 | } 165 | } 166 | bzero(&smallbuf1, sizeof smallbuf1); 167 | if (direction == kUSBNone) { 168 | if (bufferSize != sizeof smallbuf2) 169 | return kIOReturnBadArgument; 170 | if (CBP->readBytes(0U, &smallbuf2, sizeof smallbuf2) != sizeof smallbuf2) 171 | return kIOReturnInternalError; 172 | if (smallbuf2.bmRequestType == 0U && smallbuf2.bRequest == 5U) { /* kSetAddress */ 173 | uint16_t deviceAddress = smallbuf2.wValue; 174 | ContextStruct* pContext = GetSlotContext(slot, 1); 175 | uint16_t maxPacketSize = static_cast(XHCI_EPCTX_1_MAXP_SIZE_GET(pContext->_e.dwEpCtx1)); 176 | pContext = GetSlotContext(slot); 177 | _deviceZero.isBeingAddressed = true; 178 | rc = AddressDevice(slot, 179 | maxPacketSize, 180 | true, 181 | GetSlCtxSpeed(pContext), 182 | XHCI_SCTX_2_TT_HUB_SID_GET(pContext->_s.dwSctx2), 183 | XHCI_SCTX_2_TT_PORT_NUM_GET(pContext->_s.dwSctx2)); 184 | if (rc != kIOReturnSuccess) 185 | return rc; 186 | _addressMapper.HubAddress[deviceAddress] = static_cast(_deviceZero.HubAddress); 187 | _addressMapper.PortOnHub[deviceAddress] = static_cast(_deviceZero.PortOnHub); 188 | _addressMapper.Slot[deviceAddress] = static_cast(slot); 189 | _addressMapper.Active[deviceAddress] = true; 190 | _deviceZero.HubAddress = 0U; 191 | _deviceZero.PortOnHub = 0U; 192 | mystery = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NOOP) | XHCI_TRB_3_IOC_BIT; 193 | immediateDataSize = 0U; 194 | } else { 195 | mystery = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SETUP_STAGE) | XHCI_TRB_3_IDT_BIT | XHCI_TRB_3_IOC_BIT; 196 | /* 197 | * Set TRT field 198 | */ 199 | if (smallbuf2.wLength >= 1U) 200 | /* 201 | * Note: Upper bit of bmRequestType is transfer direction (1 - in, 0 - out) 202 | */ 203 | mystery |= (smallbuf2.bmRequestType & 0x80U) ? XHCI_TRB_3_TRT_IN : XHCI_TRB_3_TRT_OUT; 204 | // else XHCI_TRB_3_TRT_NONE 205 | bcopy(&smallbuf2, &smallbuf1, sizeof smallbuf1); 206 | immediateDataSize = sizeof smallbuf1; 207 | } 208 | } else if (bufferSize) { 209 | mystery = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DATA_STAGE); 210 | if (direction == kUSBIn) 211 | mystery |= XHCI_TRB_3_DIR_IN; 212 | immediateDataSize = 0xFFU; // Note: means data is not immediate 213 | } else if (_deviceZero.isBeingAddressed) { 214 | _addressMapper.Slot[0] = 0U; 215 | _addressMapper.Active[0] = false; 216 | _deviceZero.isBeingAddressed = false; 217 | mystery = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NOOP) | XHCI_TRB_3_IOC_BIT; 218 | immediateDataSize = 0U; 219 | } else { 220 | mystery = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STATUS_STAGE) | XHCI_TRB_3_IOC_BIT; 221 | if (direction == kUSBIn) 222 | mystery |= XHCI_TRB_3_DIR_IN; 223 | immediateDataSize = 0U; 224 | } 225 | rc = pAsyncEp->CreateTDs(command, 0U, mystery, immediateDataSize, reinterpret_cast(&smallbuf1)); 226 | pAsyncEp->ScheduleTDs(); 227 | return rc; 228 | } 229 | 230 | IOReturn CLASS::UIMCreateBulkTransfer(IOUSBCommand* command) 231 | { 232 | if (!command) 233 | return kIOReturnBadArgument; 234 | return CreateTransfer(command, command->GetStreamID()); 235 | } 236 | 237 | IOReturn CLASS::UIMCreateInterruptTransfer(IOUSBCommand* command) 238 | { 239 | IOUSBCompletion comp; 240 | IOMemoryDescriptor* md; 241 | USBDeviceAddress addr; 242 | IODMACommand* dmac; 243 | uint32_t speed, tag; 244 | 245 | if (!command) 246 | return kIOReturnBadArgument; 247 | addr = command->GetAddress(); 248 | if (addr == _hub3Address || addr == _hub2Address) { 249 | comp = command->GetUSLCompletion(); 250 | dmac = command->GetDMACommand(); 251 | if (dmac && dmac->getMemoryDescriptor()) 252 | dmac->clearMemoryDescriptor(); 253 | if (command->GetEndpoint() == 1U) { 254 | md = command->GetBuffer(); 255 | if (!md) 256 | return kIOReturnInternalError; 257 | speed = (addr == _hub3Address ? kUSBDeviceSpeedSuper : kUSBDeviceSpeedHigh); 258 | tag = ((static_cast(addr) << kUSBAddress_Shift) & kUSBAddress_Mask); 259 | tag |= ((speed << kUSBSpeed_Shift) & kUSBSpeed_Mask); 260 | md->setTag(tag); 261 | return RootHubQueueInterruptRead(md, static_cast(command->GetReqCount()), comp); 262 | } else { 263 | Complete(comp, kIOUSBEndpointNotFound, static_cast(command->GetReqCount())); 264 | return kIOUSBEndpointNotFound; 265 | } 266 | } 267 | return CreateTransfer(command, 0U); 268 | } 269 | 270 | IOReturn CLASS::UIMCreateIsochTransfer(IOUSBIsocCommand* command) 271 | { 272 | uint64_t curFrameNumber, frameNumberStart; 273 | IODMACommand* dmac; 274 | GenericUSBXHCIIsochEP* pIsochEp; 275 | GenericUSBXHCIIsochTD* pIsochTd; 276 | IOUSBIsocFrame* pFrames; 277 | IOUSBLowLatencyIsocFrame* pLLFrames; 278 | size_t transferOffset; 279 | uint32_t transferCount, updateFrequency, epInterval, transfersPerTD, frameNumberIncrease, frameCount, framesBeforeInterrupt, transfer; 280 | bool lowLatency, newFrame; 281 | 282 | /* 283 | * Note: See UIMCreateIsochTransfer in AppleUSBXHCIUIM.cpp for reference 284 | */ 285 | if (!command) 286 | return kIOReturnBadArgument; 287 | curFrameNumber = GetFrameNumber(); 288 | #if 0 289 | /* 290 | * Note: Added Mavericks 291 | */ 292 | if (!IsStillConnectedAndEnabled(GetSlotID(command->GetAddress()))) 293 | return kIOReturnNoDevice; 294 | #endif 295 | transferCount = command->GetNumFrames(); 296 | if (!transferCount || transferCount > 1000U) 297 | return kIOReturnBadArgument; 298 | lowLatency = command->GetLowLatency(); 299 | updateFrequency = command->GetUpdateFrequency(); 300 | pFrames = command->GetFrameList(); 301 | pLLFrames = reinterpret_cast(pFrames); 302 | frameNumberStart = command->GetStartFrame(); 303 | pIsochEp = OSDynamicCast(GenericUSBXHCIIsochEP, 304 | FindIsochronousEndpoint(command->GetAddress(), 305 | command->GetEndpoint(), 306 | command->GetDirection(), 307 | 0)); 308 | if (!pIsochEp) 309 | return kIOUSBEndpointNotFound; 310 | if (pIsochEp->aborting) 311 | return kIOReturnNotPermitted; 312 | if (pIsochEp->pRing->isInactive()) 313 | return kIOReturnBadArgument; 314 | if (pIsochEp->pRing->deleteInProgress) 315 | return kIOReturnNoDevice; 316 | if (frameNumberStart == kAppleUSBSSIsocContinuousFrame) 317 | pIsochEp->continuousStream = true; 318 | else { 319 | if (frameNumberStart < pIsochEp->firstAvailableFrame) 320 | return kIOReturnIsoTooOld; 321 | if (pIsochEp->continuousStream) 322 | return kIOReturnBadArgument; 323 | } 324 | newFrame = false; 325 | if (!pIsochEp->continuousStream) { 326 | if (frameNumberStart != pIsochEp->firstAvailableFrame) 327 | newFrame = true; 328 | pIsochEp->firstAvailableFrame = frameNumberStart; 329 | if (static_cast(frameNumberStart - curFrameNumber) < -1024) 330 | return kIOReturnIsoTooOld; 331 | else if (static_cast(frameNumberStart - curFrameNumber) > 1024) 332 | return kIOReturnIsoTooNew; 333 | } 334 | dmac = command->GetDMACommand(); 335 | if (!dmac || !dmac->getMemoryDescriptor()) { 336 | IOLog("%s: no DMA Command or missing memory descriptor\n", __FUNCTION__); 337 | return kIOReturnBadArgument; 338 | } 339 | epInterval = pIsochEp->interval; 340 | if (epInterval >= 8U) { 341 | transfersPerTD = 1U; 342 | frameNumberIncrease = epInterval / 8U; 343 | } else { 344 | transfersPerTD = 8U / epInterval; 345 | frameNumberIncrease = 1U; 346 | } 347 | if (!pIsochEp->continuousStream) { 348 | if (newFrame && 349 | frameNumberIncrease > 1U && 350 | (frameNumberStart % frameNumberIncrease)) 351 | return kIOReturnBadArgument; 352 | } 353 | if (transferCount % transfersPerTD) 354 | return kIOReturnBadArgument; 355 | if (!updateFrequency) 356 | updateFrequency = 8U; 357 | if (lowLatency && updateFrequency < 8U) 358 | framesBeforeInterrupt = updateFrequency; 359 | else 360 | framesBeforeInterrupt = 8U; 361 | frameCount = 0U; 362 | transferOffset = 0U; 363 | pIsochTd = 0; 364 | for (uint32_t baseTransferIndex = 0U; baseTransferIndex < transferCount; baseTransferIndex += transfersPerTD) { 365 | pIsochTd = GenericUSBXHCIIsochTD::ForEndpoint(pIsochEp); 366 | if (!pIsochTd) 367 | return kIOReturnNoMemory; 368 | pIsochTd->_lowLatency = lowLatency; 369 | pIsochTd->_framesInTD = 0U; 370 | pIsochTd->newFrame = newFrame; 371 | pIsochTd->interruptThisTD = false; 372 | if (frameCount > framesBeforeInterrupt) { 373 | pIsochTd->interruptThisTD = true; 374 | frameCount -= framesBeforeInterrupt; 375 | } 376 | pIsochEp->firstAvailableFrame += frameNumberIncrease; 377 | if (frameNumberIncrease == 1U) 378 | newFrame = false; 379 | pIsochTd->transferOffset = transferOffset; 380 | for (transfer = 0U; 381 | transfer < transfersPerTD && (baseTransferIndex + transfer) < transferCount; 382 | ++transfer) { 383 | if (lowLatency) { 384 | pLLFrames[baseTransferIndex + transfer].frStatus = kUSBLowLatencyIsochTransferKey; 385 | transferOffset += pLLFrames[baseTransferIndex + transfer].frReqCount; 386 | } else 387 | transferOffset += pFrames[baseTransferIndex + transfer].frReqCount; 388 | } 389 | pIsochTd->_framesInTD = static_cast(transfer); 390 | pIsochTd->_pFrames = pFrames; 391 | pIsochTd->_frameNumber = frameNumberStart; 392 | pIsochTd->_frameIndex = baseTransferIndex; 393 | pIsochTd->_completion.action = 0; 394 | pIsochTd->_pEndpoint = pIsochEp; 395 | pIsochTd->command = command; 396 | PutTDonToDoList(pIsochEp, pIsochTd); 397 | frameNumberStart += frameNumberIncrease; 398 | frameCount += frameNumberIncrease; 399 | } 400 | if (!pIsochTd) 401 | return kIOReturnInternalError; 402 | pIsochTd->_completion = command->GetUSLCompletion(); 403 | pIsochTd->interruptThisTD = true; 404 | AddIsocFramesToSchedule(pIsochEp); 405 | return kIOReturnSuccess; 406 | } 407 | -------------------------------------------------------------------------------- /V2Overrides.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // V2Overrides.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on December 26th, 2012. 6 | // Copyright (c) 2012-2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCI.h" 10 | #include "Isoch.h" 11 | 12 | #define CLASS GenericUSBXHCI 13 | #define super IOUSBControllerV3 14 | 15 | #pragma mark - 16 | #pragma mark IOUSBControllerV2 Overrides 17 | #pragma mark - 18 | 19 | IOReturn CLASS::ConfigureDeviceZero(UInt8 maxPacketSize, UInt8 speed, USBDeviceAddress hub, int port) 20 | { 21 | uint16_t _port = static_cast(port); 22 | 23 | if (hub == _hub2Address || hub == _hub3Address) { 24 | _port = PortNumberProtocolToCanonical(_port, (hub == _hub3Address ? kUSBDeviceSpeedSuper : kUSBDeviceSpeedHigh)); 25 | if (_port >= _rootHubNumPorts) 26 | return kIOReturnNoDevice; 27 | ++_port; 28 | } 29 | _deviceZero.PortOnHub = _port; 30 | _deviceZero.HubAddress = hub; 31 | return super::ConfigureDeviceZero(maxPacketSize, speed, hub, _port); 32 | } 33 | 34 | IOReturn CLASS::UIMHubMaintenance(USBDeviceAddress highSpeedHub, UInt32 highSpeedPort, UInt32 command, UInt32 flags) 35 | { 36 | if (command == kUSBHSHubCommandRemoveHub) 37 | return kIOReturnSuccess; 38 | if (command != kUSBHSHubCommandAddHub) 39 | return kIOReturnBadArgument; 40 | return configureHub(highSpeedHub, flags); 41 | } 42 | 43 | IOReturn CLASS::UIMSetTestMode(UInt32 mode, UInt32 port) 44 | { 45 | switch (mode) { 46 | case 0U: 47 | case 1U: 48 | case 2U: 49 | case 3U: 50 | case 4U: 51 | case 5U: 52 | if (!_inTestMode) 53 | break; 54 | return PlacePortInMode(port, mode); 55 | case 10U: 56 | return EnterTestMode(); 57 | case 11U: 58 | return LeaveTestMode(); 59 | } 60 | return kIOReturnInternalError; 61 | } 62 | 63 | UInt64 CLASS::GetMicroFrameNumber(void) 64 | { 65 | uint64_t counter1, counter2; 66 | uint32_t sts, mfIndex, count; 67 | sts = Read32Reg(&_pXHCIOperationalRegisters->USBSts); 68 | if (m_invalid_regspace || (sts & XHCI_STS_HCH)) 69 | return 0ULL; 70 | /* 71 | * TBD: For 32-bit compile, access to _millsecondCounter 72 | * is non-atomic, here vs FilterEventRing. 73 | */ 74 | for (count = 0U; count < 2U; ++count) { 75 | if (count) 76 | IODelay(126U); 77 | counter1 = _millsecondCounter; 78 | mfIndex = Read32Reg(&_pXHCIRuntimeRegisters->MFIndex); 79 | counter2 = _millsecondCounter; 80 | if (m_invalid_regspace) 81 | return 0ULL; 82 | if (counter1 != counter2) { 83 | /* 84 | * Note: This can only happen if a primary 85 | * interrupt takes place between readings, 86 | * so use the 2nd reading and assume 87 | * MFIndex 0. 88 | */ 89 | return counter2 << 3; 90 | } 91 | mfIndex &= XHCI_MFINDEX_MASK; 92 | if (mfIndex) 93 | break; 94 | /* 95 | * Note: XHCI allows controllers to halt the 96 | * clock if no device is connected. Some, 97 | * such as the Renesas uPD720200a don't 98 | * generate timer interrupts when no device 99 | * is connected initially. No need to delay. 100 | */ 101 | if (!counter1) 102 | break; 103 | } 104 | return (counter1 << 3) + mfIndex; 105 | } 106 | 107 | IOReturn CLASS::UIMCreateIsochEndpoint(short functionAddress, short endpointNumber, UInt32 maxPacketSize, UInt8 direction, 108 | USBDeviceAddress highSpeedHub, int highSpeedPort, UInt8 interval) 109 | { 110 | uint32_t maxBurst = 0U; 111 | 112 | /* 113 | * Preprocessing code added OS 10.8.3 114 | */ 115 | if (maxPacketSize > kUSB_EPDesc_MaxMPS) { 116 | maxBurst = ((maxPacketSize + kUSB_EPDesc_MaxMPS - 1U) / kUSB_EPDesc_MaxMPS); 117 | maxPacketSize = (maxPacketSize + maxBurst - 1U) / maxBurst; 118 | --maxBurst; 119 | } 120 | return CreateIsochEndpoint(functionAddress, endpointNumber, maxPacketSize, direction, interval, maxBurst, 0U); 121 | } 122 | 123 | IOUSBControllerIsochEndpoint* CLASS::AllocateIsochEP(void) 124 | { 125 | GenericUSBXHCIIsochEP* obj = OSTypeAlloc(GenericUSBXHCIIsochEP); 126 | if (obj && !obj->init()) { 127 | obj->release(); 128 | obj = 0; 129 | } 130 | return obj; 131 | } 132 | 133 | IODMACommand* CLASS::GetNewDMACommand(void) 134 | { 135 | return IODMACommand::withSpecification(IODMACommand::OutputHost64, 136 | XHCI_HCC_AC64(_HCCLow) ? 64U : 32U, 137 | 0U); 138 | } 139 | 140 | IOReturn CLASS::GetFrameNumberWithTime(UInt64* frameNumber, AbsoluteTime* theTime) 141 | { 142 | if (!frameNumber || !theTime) 143 | return kIOReturnBadArgument; 144 | if (!_commandGate) 145 | return kIOReturnUnsupported; 146 | return _commandGate->runAction(GatedGetFrameNumberWithTime, frameNumber, theTime); 147 | } 148 | -------------------------------------------------------------------------------- /V2Pure.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // V2Pure.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on December 26th, 2012. 6 | // Copyright (c) 2012-2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCI.h" 10 | #include "Async.h" 11 | #include "XHCITRB.h" 12 | #include "XHCITypes.h" 13 | 14 | #define CLASS GenericUSBXHCI 15 | #define super IOUSBControllerV3 16 | 17 | #pragma mark - 18 | #pragma mark IOUSBControllerV2 Pure 19 | #pragma mark - 20 | 21 | IOReturn CLASS::UIMCreateControlEndpoint(UInt8 functionNumber, UInt8 endpointNumber, UInt16 maxPacketSize, 22 | UInt8 speed, USBDeviceAddress highSpeedHub, int highSpeedPort) 23 | { 24 | TRBStruct localTrb = { 0U }; 25 | IOReturn rc; 26 | ContextStruct* pContext; 27 | ringStruct* pRing; 28 | int32_t retFromCMD; 29 | uint16_t packetSize; 30 | uint8_t slot; 31 | 32 | if (functionNumber == _hub3Address || functionNumber == _hub2Address) 33 | return kIOReturnSuccess; 34 | packetSize = maxPacketSize != 9U ? maxPacketSize : 512U; 35 | if (!functionNumber) { 36 | if (_numEndpoints >= _maxNumEndpoints) 37 | return kIOUSBEndpointCountExceeded; 38 | retFromCMD = WaitForCMD(&localTrb, XHCI_TRB_TYPE_ENABLE_SLOT, 0); 39 | if (retFromCMD == -1 || retFromCMD <= -1000) 40 | return retFromCMD == (-1000 - XHCI_TRB_ERROR_NO_SLOTS) ? kIOUSBDeviceCountExceeded : kIOReturnInternalError; 41 | slot = static_cast(retFromCMD); 42 | #if 0 43 | /* 44 | * Note: Added Mavericks 45 | */ 46 | if (_vendorID == kVendorIntel && _IntelSlotWorkaround && slot == _numSlots) { 47 | _IntelSlotWorkaround = false; 48 | retFromCMD = CleanupControlEndpoint(slot, true); 49 | _IntelSWSlot = slot; 50 | if (retFromCMD == -1 || retFromCMD <= -1000) 51 | return kIOReturnInternalError; 52 | ClearTRB(&localTrb, true); 53 | retFromCMD = WaitForCMD(&localTrb, XHCI_TRB_TYPE_ENABLE_SLOT, 0); 54 | if (retFromCMD == -1 || retFromCMD <= -1000) 55 | return retFromCMD == (-1000 - XHCI_TRB_ERROR_NO_SLOTS) ? kIOUSBDeviceCountExceeded : kIOReturnInternalError; 56 | slot = static_cast(retFromCMD); 57 | if (slot == _numSlots) 58 | ExecuteGetPortBandwidthWorkaround(); 59 | } 60 | _IntelSlotWorkaround = false; 61 | #endif 62 | if (!slot || slot > _numSlots) { 63 | /* 64 | * Sanity check. Bail out, 'cause UIMDeleteEndpoint 65 | * won't handle invalid slot # well. 66 | */ 67 | CleanupControlEndpoint(slot, true); 68 | IOLog("%s: xHC assigned invalid slot number %u\n", __FUNCTION__, slot); 69 | return kIOUSBDeviceCountExceeded; 70 | } 71 | pRing = CreateRing(slot, 1, 0U); 72 | if (!pRing || pRing->md) { 73 | CleanupControlEndpoint(slot, true); 74 | return kIOReturnInternalError; 75 | } 76 | _addressMapper.Slot[0] = slot; 77 | _addressMapper.Active[0] = true; 78 | rc = AllocRing(pRing, 1); 79 | if (rc != kIOReturnSuccess) { 80 | CleanupControlEndpoint(slot, false); 81 | return kIOReturnNoMemory; 82 | } 83 | rc = MakeBuffer(kIOMemoryPhysicallyContiguous | kIODirectionInOut, 84 | GetDeviceContextSize(), 85 | -PAGE_SIZE, 86 | &SlotPtr(slot)->md, 87 | reinterpret_cast(&SlotPtr(slot)->ctx), 88 | &SlotPtr(slot)->physAddr); 89 | if (rc != kIOReturnSuccess) { 90 | CleanupControlEndpoint(slot, false); 91 | return kIOReturnNoMemory; 92 | } 93 | if (!pRing->asyncEndpoint) { 94 | pRing->epType = CTRL_EP; 95 | pRing->asyncEndpoint = XHCIAsyncEndpoint::withParameters(this, pRing, packetSize, 0U, 0U); 96 | if (!pRing->asyncEndpoint) { 97 | CleanupControlEndpoint(slot, false); 98 | return kIOReturnNoMemory; 99 | } 100 | static_cast(__sync_fetch_and_add(&_numEndpoints, 1)); 101 | } 102 | SetDCBAAAddr64(&_dcbaa.ptr[slot], ConstSlotPtr(slot)->physAddr); 103 | return AddressDevice(slot, 104 | packetSize, 105 | false, 106 | speed, 107 | GetSlotID(highSpeedHub), 108 | highSpeedPort); 109 | } 110 | if (endpointNumber) 111 | return kIOReturnInternalError; 112 | slot = GetSlotID(functionNumber); 113 | if (!slot) 114 | return kIOReturnInternalError; 115 | pContext = GetSlotContext(slot, 1); 116 | if (!pContext) 117 | return kIOReturnInternalError; 118 | if (XHCI_EPCTX_1_MAXP_SIZE_GET(pContext->_e.dwEpCtx1) == packetSize) 119 | return kIOReturnSuccess; 120 | GetInputContext(); 121 | pContext = GetInputContextPtr(); 122 | pContext->_ic.dwInCtx1 = XHCI_INCTX_1_ADD_MASK(1U); 123 | pContext = GetInputContextPtr(2); 124 | pContext->_e.dwEpCtx1 = XHCI_EPCTX_1_MAXP_SIZE_SET(static_cast(packetSize)); 125 | SetTRBAddr64(&localTrb, _inputContext.physAddr); 126 | localTrb.d = XHCI_TRB_3_SLOT_SET(static_cast(slot)); 127 | retFromCMD = WaitForCMD(&localTrb, XHCI_TRB_TYPE_EVALUATE_CTX, 0); 128 | ReleaseInputContext(); 129 | if (retFromCMD == -1) 130 | return kIOReturnInternalError; 131 | if (retFromCMD > -1000) 132 | return kIOReturnSuccess; 133 | if (retFromCMD == -1000 - XHCI_TRB_ERROR_PARAMETER) { 134 | #if 0 135 | PrintContext(GetInputContextPtr()); 136 | PrintContext(GetInputContextPtr(2)); 137 | #endif 138 | } 139 | return kIOReturnInternalError; 140 | } 141 | 142 | IOReturn CLASS::UIMCreateBulkEndpoint(UInt8 functionNumber, UInt8 endpointNumber, UInt8 direction, UInt8 speed, 143 | UInt16 maxPacketSize, USBDeviceAddress highSpeedHub, int highSpeedPort) 144 | { 145 | return CreateBulkEndpoint(functionNumber, endpointNumber, direction, maxPacketSize, 0U, 0U); 146 | } 147 | 148 | IOReturn CLASS::UIMCreateInterruptEndpoint(short functionAddress, short endpointNumber,UInt8 direction, 149 | short speed, UInt16 maxPacketSize, short pollingRate, 150 | USBDeviceAddress highSpeedHub, int highSpeedPort) 151 | { 152 | uint32_t maxBurst = 0U; 153 | 154 | /* 155 | * Preprocessing code added OS 10.8.3 156 | */ 157 | if (maxPacketSize > kUSB_EPDesc_MaxMPS) { 158 | maxBurst = ((maxPacketSize + kUSB_EPDesc_MaxMPS - 1U) / kUSB_EPDesc_MaxMPS); 159 | maxPacketSize = (maxPacketSize + maxBurst - 1U) / maxBurst; 160 | --maxBurst; 161 | } 162 | return CreateInterruptEndpoint(functionAddress, endpointNumber, direction, speed, 163 | maxPacketSize, pollingRate, maxBurst); 164 | } 165 | -------------------------------------------------------------------------------- /V3Overrides.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // V3Overrides.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on December 26th, 2012. 6 | // Copyright (c) 2012-2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCI.h" 10 | #include "XHCITypes.h" 11 | 12 | #define CLASS GenericUSBXHCI 13 | #define super IOUSBControllerV3 14 | 15 | #pragma mark - 16 | #pragma mark IOUSBControllerV3 Overrides 17 | #pragma mark - 18 | 19 | void CLASS::ControllerSleep(void) 20 | { 21 | if (_myPowerState == kUSBPowerStateLowPower) 22 | WakeControllerFromDoze(); 23 | /* 24 | * The actual (and correct) order of events is: 25 | * AppleUSBHub down the power tree 26 | * calls UIMEnableAddressEndpoints(,false) to stop endpoints. 27 | * Then it puts enabled ports in U3 state. 28 | * Then arrive here. 29 | * Following 2 calls serve as a watchdog. 30 | */ 31 | QuiesceAllEndpoints(); 32 | RHCompleteSuspendOnAllPorts(); 33 | /* 34 | * Note: Mavericks calls ExecuteGetPortBandwidthWorkaround() here 35 | */ 36 | CommandStop(); 37 | EnableInterruptsFromController(false); 38 | IOSleep(1U); // drain primary interrupts 39 | SaveControllerStateForSleep(); 40 | } 41 | 42 | IOReturn CLASS::GetRootHubBOSDescriptor(OSData* desc) 43 | { 44 | static 45 | struct { 46 | IOUSBBOSDescriptor osDesc; 47 | IOUSBDeviceCapabilityUSB2Extension dcu2eDesc; 48 | IOUSBDeviceCapabilitySuperSpeedUSB dcssuDesc; 49 | IOUSBDeviceCapabilityContainerID dcciDesc; 50 | } __attribute__((packed)) const allDesc = { 51 | { 52 | sizeof(IOUSBBOSDescriptor), 53 | kUSBBOSDescriptor, 54 | HostToUSBWord(sizeof(IOUSBBOSDescriptor) + 55 | sizeof(IOUSBDeviceCapabilityUSB2Extension) + 56 | sizeof(IOUSBDeviceCapabilitySuperSpeedUSB) + 57 | sizeof(IOUSBDeviceCapabilityContainerID)), 58 | 3U, 59 | }, 60 | { 61 | sizeof(IOUSBDeviceCapabilityUSB2Extension), 62 | kUSBDeviceCapability, 63 | kUSBDeviceCapabilityUSB20Extension, 64 | HostToUSBLong(1U << kUSB20ExtensionLPMSupported), 65 | }, 66 | { 67 | sizeof(IOUSBDeviceCapabilitySuperSpeedUSB), 68 | kUSBDeviceCapability, 69 | kUSBDeviceCapabilitySuperSpeedUSB, 70 | HostToUSBLong(1U << kUSBSuperSpeedLTMCapable), 71 | HostToUSBWord((1U << kUSBSuperSpeedSupportsHS) | 72 | (1U << kUSBSuperSpeedSupportsSS)), 73 | 1U << kUSBSuperSpeedSupportsSS, 74 | 10U, 75 | HostToUSBWord(100U), 76 | }, 77 | { 78 | sizeof(IOUSBDeviceCapabilityContainerID), 79 | kUSBDeviceCapability, 80 | kUSBDeviceCapabilityContainerID, 81 | 0U, 82 | { 0xDF, 0xFB, 0x1E, 0x9E, 0x1B, 0x1D, 0x10, 0x46, 0xAE, 0x91, 0x3B, 0x66, 0xF5, 0x56, 0x1D, 0x0A }, 83 | }, 84 | }; 85 | if (!desc || !desc->appendBytes(&allDesc, sizeof allDesc)) 86 | return(kIOReturnNoMemory); 87 | return kIOReturnSuccess; 88 | } 89 | 90 | IOReturn CLASS::GetRootHub3Descriptor(IOUSB3HubDescriptor* desc) 91 | { 92 | IOUSB3HubDescriptor hubDesc; 93 | uint32_t appleCaptive, i, numBytes; 94 | uint8_t* dstPtr; 95 | OSNumber* appleCaptiveProperty; 96 | OSObject* obj; 97 | 98 | hubDesc.length = sizeof hubDesc; 99 | hubDesc.hubType = kUSB3HubDescriptorType; 100 | hubDesc.numPorts = READ_V3EXPANSION(_rootHubNumPortsSS); 101 | hubDesc.characteristics = HostToUSBWord(static_cast(XHCI_HCC_PPC(_HCCLow) ? kPerPortSwitchingBit : 0U)); 102 | hubDesc.powerOnToGood = 250U; 103 | hubDesc.hubCurrent = 0U; 104 | hubDesc.hubHdrDecLat = 0U; 105 | hubDesc.hubDelay = 10U; 106 | numBytes = ((hubDesc.numPorts + 1U) / 8U) + 1U; 107 | obj = _device->getProperty(kAppleInternalUSBDevice); 108 | appleCaptive = 0U; 109 | if (obj) { 110 | appleCaptiveProperty = OSDynamicCast(OSNumber, obj); 111 | if (appleCaptiveProperty) 112 | appleCaptive = appleCaptiveProperty->unsigned32BitValue(); 113 | } else if (CHECK_FOR_MAVERICKS) 114 | appleCaptive = CheckACPITablesForCaptiveRootHubPorts(_rootHubNumPorts); 115 | if (appleCaptive) { 116 | if (READ_V3EXPANSION(_rootHubPortsSSStartRange) > 1U) 117 | appleCaptive >>= (READ_V3EXPANSION(_rootHubPortsSSStartRange) - 1U); 118 | appleCaptive &= (2U << READ_V3EXPANSION(_rootHubNumPortsSS)) - 2U; 119 | } 120 | dstPtr = &hubDesc.removablePortFlags[0]; 121 | for (i = 0U; i < numBytes; i++) { 122 | *dstPtr++ = static_cast(appleCaptive & 0xFFU); 123 | appleCaptive >>= 8; 124 | } 125 | for (i = 0U; i < numBytes; i++) 126 | *dstPtr++ = 0xFFU; 127 | if (!desc) 128 | return kIOReturnNoMemory; 129 | bcopy(&hubDesc, desc, hubDesc.length); 130 | return kIOReturnSuccess; 131 | } 132 | 133 | IOReturn CLASS::UIMDeviceToBeReset(short functionAddress) 134 | { 135 | if (functionAddress == _hub3Address || functionAddress == _hub2Address) { 136 | if (gux_log_level >= 2) 137 | IOLog("%s: got request to reset root hub %d\n", __FUNCTION__, functionAddress); 138 | return kIOReturnSuccess; 139 | } 140 | uint8_t slot = GetSlotID(functionAddress); 141 | if (!slot) 142 | return kIOReturnBadArgument; 143 | if (_vendorID == kVendorIntel) 144 | SetNeedsReset(slot, false); 145 | return kIOReturnSuccess; 146 | } 147 | 148 | IOReturn CLASS::UIMAbortStream(UInt32 streamID, short functionNumber, short endpointNumber, short direction) 149 | { 150 | IOReturn rc1, rc2; 151 | uint16_t lastStream; 152 | uint8_t slot, endpoint; 153 | 154 | if (functionNumber == _hub3Address || functionNumber == _hub2Address) { 155 | if (streamID != kUSBAllStreams) 156 | return kIOReturnInternalError; 157 | if (endpointNumber) { 158 | if (endpointNumber != 1) 159 | return kIOReturnBadArgument; 160 | if (direction != kUSBIn) 161 | return kIOReturnInternalError; 162 | RootHubAbortInterruptRead(); 163 | } 164 | return kIOReturnSuccess; 165 | } 166 | if (gux_log_level >= 2) 167 | IOLog("%s(%#x, %d, %d, %d)\n", __FUNCTION__, streamID, functionNumber, endpointNumber, direction); 168 | slot = GetSlotID(functionNumber); 169 | if (!slot) 170 | return kIOReturnInternalError; 171 | endpoint = TranslateEndpoint(endpointNumber, direction); 172 | if (!endpoint || endpoint >= kUSBMaxPipes) 173 | return kIOReturnBadArgument; 174 | if (streamID == kUSBAllStreams) { 175 | QuiesceEndpoint(slot, endpoint); 176 | } else if (!streamID) { 177 | if (IsStreamsEndpoint(slot, endpoint)) 178 | return kIOReturnBadArgument; 179 | QuiesceEndpoint(slot, endpoint); 180 | return ReturnAllTransfersAndReinitRing(slot, endpoint, streamID); 181 | } else { 182 | if (streamID > GetLastStreamForEndpoint(slot, endpoint)) 183 | return kIOReturnBadArgument; 184 | uint32_t epState = QuiesceEndpoint(slot, endpoint); 185 | rc1 = ReturnAllTransfersAndReinitRing(slot, endpoint, streamID); 186 | /* 187 | * Note: UIMAbortStream on a single stream cannot be used 188 | * to clear a streams endpoint stall. 189 | */ 190 | if (epState == EP_STATE_RUNNING) 191 | RestartStreams(slot, endpoint, streamID); 192 | return rc1; 193 | } 194 | if (!IsStreamsEndpoint(slot, endpoint)) 195 | return ReturnAllTransfersAndReinitRing(slot, endpoint, 0U); 196 | lastStream = GetLastStreamForEndpoint(slot, endpoint); 197 | rc1 = kIOReturnSuccess; 198 | for (uint16_t streamId = 1U; streamId <= lastStream; ++streamId) { 199 | rc2 = ReturnAllTransfersAndReinitRing(slot, endpoint, streamId); 200 | if (rc2 != kIOReturnSuccess) 201 | rc1 = rc2; 202 | } 203 | return rc1; 204 | } 205 | 206 | UInt32 CLASS::UIMMaxSupportedStream(void) 207 | { 208 | if (!_maxPSASize) 209 | return 0U; 210 | return _maxPSASize < kMaxStreamsAllowed ? (_maxPSASize - 1U) : (kMaxStreamsAllowed - 1U); 211 | } 212 | 213 | USBDeviceAddress CLASS::UIMGetActualDeviceAddress(USBDeviceAddress current) 214 | { 215 | USBDeviceAddress addr; 216 | ContextStruct* pContext; 217 | uint8_t slot = GetSlotID(current); 218 | if (!slot) 219 | return 0U; 220 | pContext = GetSlotContext(slot); 221 | if (!pContext) 222 | return 0U; 223 | addr = static_cast(XHCI_SCTX_3_DEV_ADDR_GET(pContext->_s.dwSctx3)); 224 | if (addr == current || _addressMapper.Active[addr]) 225 | return current; 226 | _addressMapper.HubAddress[addr] = _addressMapper.HubAddress[current]; 227 | _addressMapper.PortOnHub[addr] = _addressMapper.PortOnHub[current]; 228 | _addressMapper.Slot[addr] = _addressMapper.Slot[current]; 229 | _addressMapper.Active[addr] = _addressMapper.Active[current]; 230 | _addressMapper.HubAddress[current] = 0U; 231 | _addressMapper.PortOnHub[current] = 0U; 232 | _addressMapper.Slot[current] = 0U; 233 | _addressMapper.Active[current] = false; 234 | return addr; 235 | } 236 | 237 | IOReturn CLASS::UIMCreateSSBulkEndpoint(UInt8 functionNumber, UInt8 endpointNumber, UInt8 direction, UInt8 speed, 238 | UInt16 maxPacketSize, UInt32 maxStream, UInt32 maxBurst) 239 | { 240 | if (maxStream) { 241 | if (!_maxPSASize) 242 | return kIOUSBStreamsNotSupported; 243 | if (maxStream >= kMaxStreamsAllowed || maxStream >= _maxPSASize) 244 | return kIOReturnBadArgument; 245 | if (maxStream == 1U) 246 | maxStream = 0U; 247 | } 248 | if (maxStream & (maxStream + 1U)) // Note: checks if (maxStream + 1U) is a power of 2 249 | return kIOReturnBadArgument; 250 | return CreateBulkEndpoint(functionNumber, endpointNumber, direction, maxPacketSize, maxStream, maxBurst); 251 | } 252 | 253 | IOReturn CLASS::UIMCreateSSInterruptEndpoint(short functionAddress, short endpointNumber, UInt8 direction, short speed, 254 | UInt16 maxPacketSize, short pollingRate, UInt32 maxBurst) 255 | { 256 | /* 257 | * Preprocessing code added OS 10.8.3 258 | */ 259 | if (maxPacketSize > kUSB_EPDesc_MaxMPS) 260 | maxPacketSize = (maxPacketSize + maxBurst) / (maxBurst + 1U); 261 | return CreateInterruptEndpoint(functionAddress, endpointNumber, direction, speed, maxPacketSize, 262 | pollingRate, maxBurst); 263 | } 264 | 265 | /* 266 | * In 10.8.2 -> 10.8.3, maxBurst changed to maxBurstAndMult with some preprocessing 267 | */ 268 | IOReturn CLASS::UIMCreateSSIsochEndpoint(short functionAddress, short endpointNumber, UInt32 maxPacketSize, UInt8 direction, 269 | UInt8 interval, UInt32 maxBurstAndMult) 270 | { 271 | uint32_t maxBurst, multiple; 272 | 273 | /* 274 | * Preprocessing code added OS 10.8.3 275 | */ 276 | maxBurst = maxBurstAndMult & 0xFFU; 277 | multiple = (maxBurstAndMult & 0xFF00U) >> 8; 278 | if (maxPacketSize > kUSB_EPDesc_MaxMPS) 279 | maxPacketSize /= ((maxBurst + 1U) * (multiple + 1U)); 280 | return CreateIsochEndpoint(functionAddress, endpointNumber, maxPacketSize, direction, interval, maxBurst, multiple); 281 | } 282 | 283 | IOReturn CLASS::UIMCreateStreams(UInt8 functionNumber, UInt8 endpointNumber, UInt8 direction, UInt32 maxStream) 284 | { 285 | if (!_maxPSASize) 286 | return kIOUSBStreamsNotSupported; 287 | uint8_t slot = GetSlotID(functionNumber); 288 | if (!slot) 289 | return kIOReturnInternalError; 290 | SlotStruct* pSlot = SlotPtr(slot); 291 | uint8_t endpoint = TranslateEndpoint(endpointNumber, direction); 292 | if (endpoint < 2U || endpoint >= kUSBMaxPipes) 293 | return kIOReturnBadArgument; 294 | if (pSlot->lastStreamForEndpoint[endpoint]) 295 | return maxStream ? kIOReturnBadArgument : kIOReturnInternalError; 296 | switch (maxStream) { 297 | case 0U: 298 | return kIOReturnBadArgument; 299 | case 1U: 300 | if (!pSlot->IsStreamsEndpoint(endpoint)) 301 | return kIOReturnSuccess; 302 | break; 303 | default: 304 | if (maxStream > pSlot->maxStreamForEndpoint[endpoint]) 305 | return kIOReturnBadArgument; 306 | break; 307 | } 308 | pSlot->lastStreamForEndpoint[endpoint] = static_cast(maxStream); 309 | ringStruct* pRing = pSlot->ringArrayForEndpoint[endpoint]; 310 | for (uint16_t streamId = 1U; streamId <= maxStream; ++streamId) { 311 | IOReturn rc = CreateStream(pRing, streamId); 312 | if (rc != kIOReturnSuccess) { 313 | CleanupPartialStreamAllocations(pRing, streamId); 314 | pSlot->lastStreamForEndpoint[endpoint] = 0U; 315 | return rc; 316 | } 317 | } 318 | /* 319 | * Need to reconfigure the endpoint at this stage, in order 320 | * to get the xHC to load all the newly assigned DQPTRs 321 | * for the streams. 322 | */ 323 | ClearEndpoint(slot, endpoint); 324 | return kIOReturnSuccess; 325 | } 326 | 327 | IOReturn CLASS::GetRootHubPortErrorCount(UInt16 port, UInt16* count) 328 | { 329 | if (!count) 330 | return kIOReturnBadArgument; 331 | if (!_controllerAvailable) 332 | return kIOReturnOffline; 333 | uint16_t _port = PortNumberProtocolToCanonical(port, kUSBDeviceSpeedSuper); 334 | if (_port >= _rootHubNumPorts) 335 | return kIOReturnBadArgument; 336 | uint32_t portLi = Read32Reg(&_pXHCIOperationalRegisters->prs[_port].PortLi); 337 | if (m_invalid_regspace) 338 | return kIOReturnNoDevice; 339 | #if 0 340 | /* 341 | * TBD: ??? 342 | */ 343 | if (_errataBits & kErrataIntelLynxPoint) 344 | portLi >>= 16; 345 | #endif 346 | *count = static_cast(portLi); 347 | return kIOReturnSuccess; 348 | } 349 | 350 | IOReturn CLASS::GetBandwidthAvailableForDevice(IOUSBDevice* forDevice, UInt32* pBandwidthAvailable) 351 | { 352 | ContextStruct* pContext; 353 | size_t siz; 354 | IOReturn rc; 355 | uint32_t ret; 356 | USBDeviceAddress addr, hubAddress; 357 | uint8_t slot, hubSlot, portOnHub, speed; 358 | uint8_t buffer[256]; 359 | 360 | if (!forDevice || !pBandwidthAvailable) 361 | return kIOReturnBadArgument; 362 | addr = forDevice->GetAddress(); 363 | slot = GetSlotID(addr); 364 | if (!slot) 365 | return kIOReturnNoDevice; 366 | hubAddress = _addressMapper.HubAddress[addr]; 367 | portOnHub = _addressMapper.PortOnHub[addr]; 368 | if (hubAddress == _hub3Address || hubAddress == _hub2Address) 369 | hubSlot = 0U; 370 | else { 371 | hubSlot = GetSlotID(hubAddress); 372 | if (!hubSlot) 373 | return kIOReturnInternalError; 374 | } 375 | pContext = GetSlotContext(slot); 376 | if (!pContext) 377 | return kIOReturnInternalError; 378 | speed = GetSlCtxSpeed(pContext); 379 | siz = sizeof buffer; 380 | rc = GetPortBandwidth(hubSlot, speed, &buffer[0], &siz); 381 | if (rc != kIOReturnSuccess) { 382 | IOLog("%s: GetPortBandwidth(%u, %u, ...) failed, returning %#x\n", __FUNCTION__, hubSlot, speed, rc); 383 | return rc; 384 | } 385 | ret = buffer[static_cast(portOnHub - 1U)]; 386 | /* 387 | * Note: These are just rough estimates. The exact 388 | * number of bytes per microframe depend on packetization 389 | * overhead, symbol encoding (e.g. 8b/10b for SS) and 390 | * scheduling. 391 | */ 392 | switch (speed) { 393 | case kUSBDeviceSpeedLow: 394 | *pBandwidthAvailable = 234375U * ret / 1000000U; // based on 23.4375 bytes/microframe 395 | break; 396 | case kUSBDeviceSpeedFull: 397 | *pBandwidthAvailable = 1875U * ret / 1000U; // based on 187.5 bytes/microframe 398 | break; 399 | case kUSBDeviceSpeedHigh: 400 | *pBandwidthAvailable = 75U * ret; // based on 7500 bytes/microframe 401 | break; 402 | case kUSBDeviceSpeedSuper: 403 | *pBandwidthAvailable = 78125U * ret / 100U; // based on 78125 bytes/microframe 404 | break; 405 | default: 406 | *pBandwidthAvailable = 0U; 407 | break; 408 | } 409 | return kIOReturnSuccess; 410 | } 411 | -------------------------------------------------------------------------------- /V3Pure.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // V3Pure.cpp 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on December 26th, 2012. 6 | // Copyright (c) 2012-2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #include "GenericUSBXHCI.h" 10 | #include 11 | 12 | #define CLASS GenericUSBXHCI 13 | #define super IOUSBControllerV3 14 | 15 | #pragma mark - 16 | #pragma mark IOUSBControllerV3 Pure 17 | #pragma mark - 18 | 19 | IOReturn CLASS::ResetControllerState(void) 20 | { 21 | if (m_invalid_regspace) 22 | return kIOReturnNotResponding; 23 | IOReturn rc = StopUSBBus(); 24 | EnableInterruptsFromController(false); 25 | IOSleep(1U); // drain primary interrupts 26 | /* 27 | * Note: Mavericks does this in IOUSBControllerV3::ControllerOff 28 | */ 29 | if (!CHECK_FOR_MAVERICKS && 30 | _expansionData && 31 | _expansionData->_controllerCanSleep && 32 | _device) { 33 | /* 34 | * On the ASM1042, shutting down with PME enabled 35 | * causes spontaneous reboot, so disable it. 36 | */ 37 | _device->enablePCIPowerManagement(kPCIPMCSPowerStateD0); 38 | } 39 | if (rc == kIOReturnSuccess) 40 | _uimInitialized = false; 41 | return rc; 42 | } 43 | 44 | IOReturn CLASS::RestartControllerFromReset(void) 45 | { 46 | if (_uimInitialized) 47 | return kIOReturnSuccess; 48 | TakeOwnershipFromBios(); 49 | EnableXHCIPorts(); 50 | DisableComplianceMode(); 51 | IOReturn rc = ResetController(); 52 | if (rc != kIOReturnSuccess) 53 | return rc; 54 | if (_errataBits & kErrataFL1100LowRev) 55 | FL1100Tricks(2); 56 | RHPortStatusChangeBitmapInit(); 57 | bzero(&_rhPortEmulateCSC[0], sizeof _rhPortEmulateCSC); 58 | rc = InitializePorts(); 59 | if (rc != kIOReturnSuccess) 60 | return rc; 61 | _isSleeping = false; 62 | for (uint8_t slot = 1U; slot <= _numSlots; ++slot) 63 | NukeSlot(slot); 64 | bzero(&_addressMapper, sizeof _addressMapper); 65 | bzero(&_rhPortBeingResumed[0], sizeof _rhPortBeingResumed); 66 | bzero(&_rhPortBeingReset[0], sizeof _rhPortBeingReset); 67 | uint32_t config = Read32Reg(&_pXHCIOperationalRegisters->Config); 68 | if (m_invalid_regspace) 69 | return kIOReturnNoDevice; 70 | Write32Reg(&_pXHCIOperationalRegisters->Config, (config & ~XHCI_CONFIG_SLOTS_MASK) | _numSlots); 71 | Write32Reg(&_pXHCIOperationalRegisters->DNCtrl, UINT16_MAX); 72 | Write64Reg(&_pXHCIOperationalRegisters->DCBAap, _dcbaa.physAddr, false); 73 | InitCMDRing(); 74 | for (int32_t interrupter = 0; interrupter < kMaxActiveInterrupters; ++interrupter) 75 | InitEventRing(interrupter, true); 76 | if (_scratchpadBuffers.max) 77 | SetDCBAAAddr64(_dcbaa.ptr, _scratchpadBuffers.physAddr); 78 | bzero(const_cast(&_errorCounters[0]), sizeof _errorCounters); 79 | _inputContext.refCount = 0; 80 | _numEndpoints = 0U; 81 | _deviceZero.isBeingAddressed = false; 82 | _inTestMode = false; 83 | #if 0 84 | _filterInterruptActive = false; 85 | #endif 86 | _millsecondCounter = 0ULL; 87 | bzero(&_interruptCounters[0], sizeof _interruptCounters); 88 | if (_wakingFromHibernation && _expansionData && _expansionData->_controllerCanSleep && _device && 89 | _device->hasPCIPowerManagement(kPCIPMCPMESupportFromD3Cold)) 90 | _device->enablePCIPowerManagement(kPCIPMCSPowerStateD3); 91 | _uimInitialized = true; 92 | return kIOReturnSuccess; 93 | } 94 | 95 | IOReturn CLASS::SaveControllerStateForSleep(void) 96 | { 97 | IOReturn rc = StopUSBBus(); 98 | if (rc != kIOReturnSuccess) 99 | return rc; 100 | _sleepOpSave.USBCmd = Read32Reg(&_pXHCIOperationalRegisters->USBCmd); 101 | _sleepOpSave.DNCtrl = Read32Reg(&_pXHCIOperationalRegisters->DNCtrl); 102 | _sleepOpSave.DCBAap = Read64Reg(&_pXHCIOperationalRegisters->DCBAap); 103 | _sleepOpSave.Config = Read32Reg(&_pXHCIOperationalRegisters->Config); 104 | if (m_invalid_regspace) 105 | return kIOReturnNoDevice; 106 | for (int32_t interrupter = 0; interrupter < kMaxActiveInterrupters; ++interrupter) 107 | SaveAnInterrupter(interrupter); 108 | uint32_t cmd = Read32Reg(&_pXHCIOperationalRegisters->USBCmd); 109 | if (m_invalid_regspace) 110 | return kIOReturnNoDevice; 111 | Write32Reg(&_pXHCIOperationalRegisters->USBCmd, cmd | XHCI_CMD_CSS); 112 | rc = WaitForUSBSts(XHCI_STS_SSS, 0U); 113 | if (rc != kIOReturnSuccess) 114 | return rc; 115 | uint32_t sts = Read32Reg(&_pXHCIOperationalRegisters->USBSts); 116 | if (m_invalid_regspace) 117 | return kIOReturnNoDevice; 118 | _isSleeping = true; 119 | if (sts & XHCI_STS_SRE) { 120 | ++_diagCounters[DIAGCTR_SLEEP]; 121 | Write32Reg(&_pXHCIOperationalRegisters->USBSts, XHCI_STS_SRE); 122 | /* 123 | * Disable PME, on ASM1042 it causes system to wake 124 | * up instantly. 125 | */ 126 | if (_device) 127 | _device->enablePCIPowerManagement(kPCIPMCSPowerStateD0); 128 | IOLog("%s: xHC Save Error\n", __FUNCTION__); 129 | return kIOReturnInternalError; 130 | } 131 | return kIOReturnSuccess; 132 | } 133 | 134 | IOReturn CLASS::RestoreControllerStateFromSleep(void) 135 | { 136 | uint32_t sts = Read32Reg(&_pXHCIOperationalRegisters->USBSts); 137 | if (m_invalid_regspace) 138 | return kIOReturnNoDevice; 139 | if (_errataBits & kErrataFL1100LowRev) 140 | FL1100Tricks(1); 141 | if (sts & XHCI_STS_PCD) { 142 | for (uint8_t port = 0U; port < _rootHubNumPorts; ++port) { 143 | uint32_t portSC = Read32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC); 144 | if (m_invalid_regspace) 145 | return kIOReturnNoDevice; 146 | if (portSC & XHCI_PS_CSC) { 147 | if (portSC & XHCI_PS_PED) { 148 | IOLog("%s: Port %u on bus %#x - connect status changed but still enabled. clearing enable bit: portSC(%#x)\n", 149 | __FUNCTION__, port + 1U, static_cast(_busNumber), portSC); 150 | Write32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC, (portSC & XHCI_PS_WRITEBACK_MASK) | XHCI_PS_PEC); 151 | } else { 152 | char const* nstr = "xHC"; 153 | char const* dstr = (portSC & XHCI_PS_CCS) ? "connected" : "disconnected"; 154 | uint8_t protocol = kUSBDeviceSpeedLow; 155 | uint16_t portNum = PortNumberCanonicalToProtocol(port, &protocol); 156 | if (protocol == kUSBDeviceSpeedHigh && _rootHubDevice) 157 | nstr = _rootHubDevice->getName(); 158 | else if (protocol == kUSBDeviceSpeedSuper && _expansionData && _rootHubDeviceSS) 159 | nstr = _rootHubDeviceSS->getName(); 160 | IOLog("%s(%s): Port %u on bus %#x %s: portSC(%#x)\n", 161 | __FUNCTION__, nstr, portNum, static_cast(_busNumber), dstr, portSC); 162 | EnsureUsability(); 163 | } 164 | } else if (XHCI_PS_PLS_GET(portSC) == XDEV_RESUME) { 165 | uint8_t protocol = kUSBDeviceSpeedHigh; 166 | IOUSBHubPolicyMaker* pm = 0; 167 | uint16_t portNum = PortNumberCanonicalToProtocol(port, &protocol); 168 | if (portNum) 169 | pm = GetHubForProtocol(protocol); 170 | /* 171 | * Note: This message causes HID device that generated PME to 172 | * do a full system wakeup. Without it, the display remains down. 173 | */ 174 | if (pm) 175 | pm->message(kIOUSBMessageRootHubWakeEvent, this, reinterpret_cast(portNum - 1U)); 176 | else 177 | IOLog("%s: Port %u on bus %#x has remote wakeup from some device\n", 178 | __FUNCTION__, port + 1U, static_cast(_busNumber)); 179 | RHCheckForPortResume(port, protocol, portSC); 180 | } 181 | } 182 | } 183 | if (_isSleeping) { 184 | Write32Reg(&_pXHCIOperationalRegisters->USBCmd, _sleepOpSave.USBCmd); 185 | Write32Reg(&_pXHCIOperationalRegisters->DNCtrl, _sleepOpSave.DNCtrl); 186 | Write64Reg(&_pXHCIOperationalRegisters->DCBAap, _sleepOpSave.DCBAap, false); 187 | Write32Reg(&_pXHCIOperationalRegisters->Config, _sleepOpSave.Config); 188 | for (int32_t interrupter = 0; interrupter < kMaxActiveInterrupters; ++interrupter) 189 | RestoreAnInterrupter(interrupter); 190 | uint32_t cmd = Read32Reg(&_pXHCIOperationalRegisters->USBCmd); 191 | if (m_invalid_regspace) 192 | return kIOReturnNoDevice; 193 | Write32Reg(&_pXHCIOperationalRegisters->USBCmd, cmd | XHCI_CMD_CRS); 194 | IOReturn rc = WaitForUSBSts(XHCI_STS_RSS, 0U); 195 | if (rc == kIOReturnNoDevice) 196 | return rc; 197 | #if 0 198 | _IntelSlotWorkaround = true; 199 | #endif 200 | sts = Read32Reg(&_pXHCIOperationalRegisters->USBSts); 201 | if (m_invalid_regspace) 202 | return kIOReturnNoDevice; 203 | if (sts & XHCI_STS_SRE) { 204 | ++_diagCounters[DIAGCTR_RESUME]; 205 | Write32Reg(&_pXHCIOperationalRegisters->USBSts, XHCI_STS_SRE); 206 | IOLog("%s: xHC Restore Error\n", __FUNCTION__); 207 | _uimInitialized = false; 208 | rc = RestartControllerFromReset(); 209 | if (rc != kIOReturnSuccess) 210 | IOLog("%s: RestartControllerFromReset failed with %#x\n", __FUNCTION__, rc); 211 | #if 0 212 | SantizePortsAfterPowerLoss(); 213 | NotifyRootHubsOfPowerLoss(); 214 | #endif 215 | return kIOReturnSuccess; 216 | } 217 | InitCMDRing(); 218 | _isSleeping = false; 219 | } 220 | DisableComplianceMode(); 221 | #if 0 222 | /* 223 | * Note: This is done from AppleUSBHub via UIMEnableAddressEndpoint 224 | * after ports are resumed. 225 | */ 226 | for (uint8_t slot = 1U; slot <= _numSlots; ++slot) { 227 | if (ConstSlotPtr(slot)->isInactive()) 228 | continue; 229 | for (int32_t endpoint = 1; endpoint != kUSBMaxPipes; ++endpoint) { 230 | ringStruct* pRing = GetRing(slot, endpoint, 0U); 231 | if (pRing->isInactive()) 232 | continue; 233 | if (IsStreamsEndpoint(slot, endpoint)) 234 | RestartStreams(slot, endpoint, 0U); 235 | else 236 | StartEndpoint(slot, endpoint, 0U); 237 | } 238 | } 239 | #endif 240 | uint32_t wait = 0U; 241 | for (uint8_t port = 0U; port < _rootHubNumPorts; ++port) { 242 | uint32_t portSC = Read32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC); 243 | if (m_invalid_regspace) 244 | return kIOReturnNoDevice; 245 | if (portSC & XHCI_PS_CAS) { 246 | IOLog("%s: Port %u on bus %#x cold attach detected\n", 247 | __FUNCTION__, port + 1U, static_cast(_busNumber)); 248 | Write32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC, (portSC & XHCI_PS_WRITEBACK_MASK) | XHCI_PS_WPR); 249 | wait = 50U; 250 | } 251 | } 252 | if (wait) 253 | CheckedSleep(wait); 254 | return kIOReturnSuccess; 255 | } 256 | 257 | IOReturn CLASS::DozeController(void) 258 | { 259 | if (!READ_V3EXPANSION(_externalDeviceCount) && 260 | (_errataBits & kErrataSWAssistedIdle)) { 261 | uint16_t xhcc = _device->configRead16(PCI_XHCI_INTEL_XHCC); 262 | if (xhcc == UINT16_MAX) { 263 | #if 0 264 | m_invalid_regspace = true; 265 | return kIOReturnNoDevice; 266 | #else 267 | return kIOReturnSuccess; 268 | #endif 269 | } 270 | xhcc &= ~PCI_XHCI_INTEL_XHCC_SWAXHCIP_SET(3U); 271 | /* 272 | * Set SWAXHCI to clear if 1) software, 2) MMIO, 3) xHC exits idle 273 | */ 274 | xhcc |= PCI_XHCI_INTEL_XHCC_SWAXHCIP_SET(0U); 275 | xhcc |= PCI_XHCI_INTEL_XHCC_SWAXHCI; 276 | _device->configWrite16(PCI_XHCI_INTEL_XHCC, xhcc); 277 | } 278 | return kIOReturnSuccess; 279 | } 280 | 281 | IOReturn CLASS::WakeControllerFromDoze(void) 282 | { 283 | if (_errataBits & kErrataSWAssistedIdle) { 284 | uint16_t xhcc = _device->configRead16(PCI_XHCI_INTEL_XHCC); 285 | /* 286 | * Clear SWAXHCI if it's still on 287 | */ 288 | if (xhcc != UINT16_MAX && (xhcc & PCI_XHCI_INTEL_XHCC_SWAXHCI)) 289 | _device->configWrite16(PCI_XHCI_INTEL_XHCC, xhcc & ~PCI_XHCI_INTEL_XHCC_SWAXHCI); 290 | } 291 | #if 0 292 | bool found_resuming = false; 293 | for (uint8_t port = 0U; port < _rootHubNumPorts; ++port) { 294 | uint32_t portSC = Read32Reg(&_pXHCIOperationalRegisters->prs[port].PortSC); 295 | if (m_invalid_regspace) 296 | return kIOReturnNoDevice; 297 | if (XHCI_PS_PLS_GET(portSC) == XDEV_RESUME) { 298 | _rhPortBeingResumed[port] = true; 299 | found_resuming = true; 300 | } 301 | } 302 | if (found_resuming) { 303 | IOSleep(20U); 304 | RHCompleteResumeOnAllPorts(); 305 | } 306 | #endif 307 | return kIOReturnSuccess; 308 | } 309 | 310 | IOReturn CLASS::UIMEnableAddressEndpoints(USBDeviceAddress address, bool enable) 311 | { 312 | if (address == _hub3Address || address == _hub2Address) 313 | return kIOReturnSuccess; 314 | if (gux_log_level >= 2) 315 | IOLog("%s(%u, %c)\n", __FUNCTION__, address, enable ? 'Y' : 'N'); 316 | uint8_t slot = GetSlotID(address); 317 | if (!slot) { 318 | if (address >= kUSBMaxDevices || !enable) 319 | return kIOReturnBadArgument; 320 | _addressMapper.Active[address] = true; 321 | slot = GetSlotID(address); 322 | if (!slot || slot > _numSlots) { 323 | _addressMapper.Active[address] = false; 324 | return kIOReturnInternalError; 325 | } 326 | /* 327 | * Note: May be called from maxCapabilityForDomainState 328 | */ 329 | if (_myBusState < kUSBBusStateRunning) 330 | return kIOReturnSuccess; 331 | for (int32_t endpoint = 1; endpoint != kUSBMaxPipes; ++endpoint) { 332 | ringStruct* pRing = GetRing(slot, endpoint, 0U); 333 | if (pRing->isInactive()) 334 | continue; 335 | if (IsStreamsEndpoint(slot, endpoint)) 336 | RestartStreams(slot, endpoint, 0U); 337 | else 338 | StartEndpoint(slot, endpoint, 0U); 339 | 340 | } 341 | return kIOReturnSuccess; 342 | } 343 | if (enable) 344 | return kIOReturnSuccess; 345 | for (int32_t endpoint = 1; endpoint != kUSBMaxPipes; ++endpoint) { 346 | ringStruct* pRing = GetRing(slot, endpoint, 0U); 347 | if (pRing->isInactive()) 348 | continue; 349 | /* 350 | * Note: This was changed from StopEndpoint -> QuiesceEndpoint 351 | * in Mavericks. 352 | */ 353 | QuiesceEndpoint(slot, endpoint); 354 | } 355 | _addressMapper.Active[address] = false; 356 | return kIOReturnSuccess; 357 | } 358 | 359 | IOReturn CLASS::UIMEnableAllEndpoints(bool enable) 360 | { 361 | if (!enable) 362 | return kIOReturnBadArgument; 363 | for (uint16_t addr = 0U; addr < kUSBMaxDevices; ++addr) { 364 | if (_addressMapper.Active[addr] || !_addressMapper.Slot) 365 | continue; 366 | UIMEnableAddressEndpoints(addr, true); 367 | } 368 | return kIOReturnSuccess; 369 | } 370 | 371 | IOReturn CLASS::EnableInterruptsFromController(bool enable) 372 | { 373 | if (enable) 374 | RestartUSBBus(); 375 | else { 376 | uint32_t cmd = Read32Reg(&_pXHCIOperationalRegisters->USBCmd); 377 | if (m_invalid_regspace) 378 | return kIOReturnNoDevice; 379 | Write32Reg(&_pXHCIOperationalRegisters->USBCmd, cmd & ~(XHCI_CMD_INTE | XHCI_CMD_EWE)); 380 | } 381 | return kIOReturnSuccess; 382 | } 383 | -------------------------------------------------------------------------------- /XHCIRegs.h: -------------------------------------------------------------------------------- 1 | // 2 | // XHCIRegs.h 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on December 14th 2012. 6 | // Copyright (c) 2012-2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #ifndef __XHCIREGS_H__ 10 | #define __XHCIREGS_H__ 11 | 12 | #define PCI_XHCI_INTEL_XHCC 0x40U /* Intel xHC System Bus Configuration Register */ 13 | #define PCI_XHCI_INTEL_XHCC_SWAXHCI (1U << 11) 14 | #define PCI_XHCI_INTEL_XHCC_SWAXHCIP_SET(x) (((x) & 3U) << 12) 15 | #define PCI_XHCI_INTEL_XUSB2PR 0xD0U /* Intel USB2 Port Routing */ 16 | #define PCI_XHCI_INTEL_XUSB2PRM 0xD4U /* Intel USB2 Port Routing Mask */ 17 | #define PCI_XHCI_INTEL_USB3_PSSEN 0xD8U /* Intel USB3 Port SuperSpeed Enable */ 18 | #define PCI_XHCI_INTEL_USB3PRM 0xDCU /* Intel USB3 Port Routing Mask */ 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | struct XHCICapRegisters 25 | { 26 | uint8_t CapLength; 27 | uint8_t Rsvd; 28 | uint16_t HCIVersion; 29 | uint32_t HCSParams[3]; 30 | uint32_t HCCParams; 31 | uint32_t DBOff; 32 | uint32_t RTSOff; 33 | }; 34 | 35 | /* XHCI capability bits */ 36 | #define XHCI_HCS1_DEVSLOT_MAX(x)((x) & 0xFFU) 37 | #define XHCI_HCS1_IRQ_MAX(x) (((x) >> 8) & 0x7FFU) 38 | #define XHCI_HCS1_N_PORTS(x) (((x) >> 24) & 0xFFU) 39 | #define XHCI_HCS2_IST(x) ((x) & 0xFU) 40 | #define XHCI_HCS2_ERST_MAX(x) (((x) >> 4) & 0xFU) 41 | #define XHCI_HCS2_SPB_MAX(x) (((x) >> 27) & 0x1FU) 42 | #define XHCI_HCC_AC64(x) ((x) & 0x1U) /* 64-bit capable */ 43 | #define XHCI_HCC_BNC(x) (((x) >> 1) & 0x1U) /* BW negotiation */ 44 | #define XHCI_HCC_CSZ(x) (((x) >> 2) & 0x1U) /* context size */ 45 | #define XHCI_HCC_PPC(x) (((x) >> 3) & 0x1U) /* port power control */ 46 | #define XHCI_HCC_PIND(x) (((x) >> 4) & 0x1U) /* port indicators */ 47 | #define XHCI_HCC_LHRC(x) (((x) >> 5) & 0x1U) /* light HC reset */ 48 | #define XHCI_HCC_LTC(x) (((x) >> 6) & 0x1U) /* latency tolerance msg */ 49 | #define XHCI_HCC_NSS(x) (((x) >> 7) & 0x1U) /* no secondary sid */ 50 | #define XHCI_HCC_PSA_SZ_MAX(x) (((x) >> 12) & 0xFU) /* max pri. stream array size */ 51 | #define XHCI_HCC_XECP(x) (((x) >> 16) & 0xFFFFU) /* extended capabilities pointer */ 52 | 53 | struct XHCIPortRegisterSet 54 | { 55 | uint32_t PortSC; 56 | uint32_t PortPmsc; 57 | uint32_t PortLi; 58 | uint32_t RsvdZ; 59 | }; 60 | 61 | struct XHCIOpRegisters 62 | { 63 | uint32_t USBCmd; 64 | uint32_t USBSts; 65 | uint32_t PageSize; 66 | uint32_t RsvdZ1[2]; 67 | uint32_t DNCtrl; 68 | uint64_t CRCr; 69 | uint32_t RsvdZ2[4]; 70 | uint64_t DCBAap; 71 | uint32_t Config; 72 | uint32_t RsvdZ3[241]; 73 | struct XHCIPortRegisterSet prs[0]; 74 | }; 75 | 76 | struct XHCIOpRegistersUnpadded 77 | { 78 | uint32_t USBCmd; 79 | uint32_t USBSts; 80 | uint32_t PageSize; 81 | uint32_t RsvdZ1[2]; 82 | uint32_t DNCtrl; 83 | uint64_t CRCr; 84 | uint32_t RsvdZ2[4]; 85 | uint64_t DCBAap; 86 | uint32_t Config; 87 | }; 88 | 89 | /* XHCI operational bits */ 90 | #define XHCI_CMD_RS 0x00000001U /* RW Run/Stop */ 91 | #define XHCI_CMD_HCRST 0x00000002U /* RW Host Controller Reset */ 92 | #define XHCI_CMD_INTE 0x00000004U /* RW Interrupter Enable */ 93 | #define XHCI_CMD_HSEE 0x00000008U /* RW Host System Error Enable */ 94 | #define XHCI_CMD_LHCRST 0x00000080U /* RO/RW Light Host Controller Reset */ 95 | #define XHCI_CMD_CSS 0x00000100U /* RW Controller Save State */ 96 | #define XHCI_CMD_CRS 0x00000200U /* RW Controller Restore State */ 97 | #define XHCI_CMD_EWE 0x00000400U /* RW Enable Wrap Event */ 98 | #define XHCI_CMD_EU3S 0x00000800U /* RW Enable U3 MFINDEX Stop */ 99 | #define XHCI_STS_HCH 0x00000001U /* RO - Host Controller Halted */ 100 | #define XHCI_STS_HSE 0x00000004U /* RW1C - Host System Error */ 101 | #define XHCI_STS_EINT 0x00000008U /* RW1C - Event Interrupt */ 102 | #define XHCI_STS_PCD 0x00000010U /* RW1C - Port Change Detect */ 103 | #define XHCI_STS_SSS 0x00000100U /* RO - Save State Status */ 104 | #define XHCI_STS_RSS 0x00000200U /* RO - Restore State Status */ 105 | #define XHCI_STS_SRE 0x00000400U /* RW1C - Save/Restore Error */ 106 | #define XHCI_STS_CNR 0x00000800U /* RO - Controller Not Ready */ 107 | #define XHCI_STS_HCE 0x00001000U /* RO - Host Controller Error */ 108 | #define XHCI_CRCR_LO_RCS 0x00000001ULL /* RW - Ring Cycle State */ 109 | #define XHCI_CRCR_LO_CS 0x00000002ULL /* RW1S - Command Stop */ 110 | #define XHCI_CRCR_LO_CA 0x00000004ULL /* RW1S - Command Abort */ 111 | #define XHCI_CRCR_LO_CRR 0x00000008ULL /* RO - Command Ring Running */ 112 | #define XHCI_CRCR_LO_MASK 0x0000003FULL 113 | #define XHCI_CONFIG_SLOTS_MASK 0x000000FFU /* RW - number of device slots enabled */ 114 | 115 | /* XHCI port status bits (sticky) */ 116 | #define XHCI_PS_CCS 0x00000001U /* RO - current connect status */ 117 | #define XHCI_PS_PED 0x00000002U /* RW1C - port enabled / disabled */ 118 | #define XHCI_PS_OCA 0x00000008U /* RO - over current active */ 119 | #define XHCI_PS_PR 0x00000010U /* RW1S - port reset */ 120 | #define XHCI_PS_PLS_GET(x) (((x) >> 5) & 0xFU) /* RW - port link state */ 121 | #define XHCI_PS_PLS_SET(x) (((x) & 0xFU) << 5) /* RW - port link state */ 122 | #define XHCI_PS_PP 0x00000200U /* RW - port power */ 123 | #define XHCI_PS_SPEED_GET(x) (((x) >> 10) & 0xFU) /* RO - port speed */ 124 | #define XHCI_PS_PIC_GET(x) (((x) >> 14) & 0x3U) /* RW - port indicator */ 125 | #define XHCI_PS_PIC_SET(x) (((x) & 0x3U) << 14) /* RW - port indicator */ 126 | #define XHCI_PS_LWS 0x00010000U /* RW1S - port link state write strobe */ 127 | #define XHCI_PS_CSC 0x00020000U /* RW1C - connect status change */ 128 | #define XHCI_PS_PEC 0x00040000U /* RW1C - port enable/disable change */ 129 | #define XHCI_PS_WRC 0x00080000U /* RW1C - warm port reset change (RsvdZ for USB2 ports) */ 130 | #define XHCI_PS_OCC 0x00100000U /* RW1C - over-current change */ 131 | #define XHCI_PS_PRC 0x00200000U /* RW1C - port reset change */ 132 | #define XHCI_PS_PLC 0x00400000U /* RW1C - port link state change */ 133 | #define XHCI_PS_CEC 0x00800000U /* RW1C - config error change (RsvdZ for USB2 ports) */ 134 | #define XHCI_PS_CAS 0x01000000U /* RO - cold attach status */ 135 | #define XHCI_PS_WCE 0x02000000U /* RW - wake on connect enable */ 136 | #define XHCI_PS_WDE 0x04000000U /* RW - wake on disconnect enable */ 137 | #define XHCI_PS_WOE 0x08000000U /* RW - wake on over-current enable */ 138 | #define XHCI_PS_DR 0x40000000U /* RO - device removable */ 139 | #define XHCI_PS_WPR 0x80000000U /* RW1S - warm port reset (RsvdZ for USB2 ports) */ 140 | #define XHCI_PS_CLEAR 0x80FF01FFU /* command bits */ 141 | #define XHCI_PS_WAKEBITS (XHCI_PS_WCE | XHCI_PS_WDE | XHCI_PS_WOE) 142 | #define XHCI_PS_WRITEBACK_MASK (XHCI_PS_PP | XHCI_PS_PIC_SET(3U) | XHCI_PS_WAKEBITS) 143 | #define XHCI_PS_CHANGEBITS (XHCI_PS_CSC | XHCI_PS_PEC | XHCI_PS_WRC | XHCI_PS_OCC | XHCI_PS_PRC | XHCI_PS_PLC | XHCI_PS_CEC) 144 | #define XDEV_U0 0U 145 | #define XDEV_U3 3U 146 | #define XDEV_DISABLED 4U 147 | #define XDEV_RXDETECT 5U 148 | #define XDEV_INACTIVE 6U 149 | #define XDEV_POLLING 7U 150 | #define XDEV_RECOVERY 8U 151 | #define XDEV_HOTRESET 9U 152 | #define XDEV_COMPLIANCE 10U 153 | #define XDEV_TEST 11U 154 | #define XDEV_RESUME 15U 155 | #define XDEV_FS 1U 156 | #define XDEV_LS 2U 157 | #define XDEV_HS 3U 158 | #define XDEV_SS 4U 159 | 160 | struct XHCIInterruptRegisterSet 161 | { 162 | uint32_t iman; 163 | uint32_t imod; 164 | uint32_t erstsz; 165 | uint32_t RsvdP; 166 | uint64_t erstba; 167 | uint64_t erdp; 168 | }; 169 | 170 | struct XHCIRuntimeRegisters 171 | { 172 | uint32_t MFIndex; 173 | uint32_t RsvdZ[7]; 174 | struct XHCIInterruptRegisterSet irs[0]; 175 | }; 176 | 177 | #define XHCI_MFINDEX_MASK 0x00003FFFU /* RO - Microframe Index */ 178 | #define XHCI_IMAN_INTR_PEND 0x00000001U /* RW1C - interrupt pending */ 179 | #define XHCI_IMAN_INTR_ENA 0x00000002U /* RW - interrupt enable */ 180 | #define XHCI_IMOD_IVAL_MASK 0xFFFFU /* RW - 250ns unit */ 181 | #define XHCI_IMOD_ICNT_GET(x) (((x) >> 16) & 0xFFFFU) /* RW - 250ns unit */ 182 | #define XHCI_IMOD_ICNT_SET(x) (((x) & 0xFFFFU) << 16) /* RW - 250ns unit */ 183 | #define XHCI_ERSTS_MASK 0x0000FFFFU /* RW - Event Ring Segment Table Size */ 184 | #define XHCI_ERDP_LO_SINDEX(x) ((x) & 0x7ULL) /* RW - dequeue segment index */ 185 | #define XHCI_ERDP_LO_BUSY 0x00000008ULL /* RW1C - event handler busy */ 186 | 187 | struct XHCIXECPStruct 188 | { 189 | uint8_t capId; 190 | uint8_t next; 191 | uint16_t capSpecific; 192 | }; 193 | 194 | struct XHCIXECPStruct_SP 195 | { 196 | uint8_t capId; 197 | uint8_t next; 198 | uint8_t revisionMinor; 199 | uint8_t revisionMajor; 200 | uint32_t nameString; 201 | uint8_t compatiblePortOffset; 202 | uint8_t compatiblePortCount; 203 | uint16_t Rsvd; 204 | }; 205 | 206 | #define XHCI_HC_BIOS_OWNED (1U << 16) 207 | #define XHCI_HC_OS_OWNED (1U << 24) 208 | #define XHCI_LEGACY_DISABLE_SMI ((7U << 1) | (255U << 5) | (7U << 17)) 209 | #define XHCI_LEGACY_SMI_EVENTS (7U << 29) 210 | 211 | #ifdef __cplusplus 212 | } 213 | #endif 214 | 215 | #endif 216 | -------------------------------------------------------------------------------- /XHCITRB.h: -------------------------------------------------------------------------------- 1 | // 2 | // XHCITRB.h 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on January 26th, 2013. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #ifndef GenericUSBXHCI_XHCITRB_h 10 | #define GenericUSBXHCI_XHCITRB_h 11 | 12 | /* Commands */ 13 | #define XHCI_TRB_TYPE_RESERVED 0x00 14 | #define XHCI_TRB_TYPE_NORMAL 0x01 15 | #define XHCI_TRB_TYPE_SETUP_STAGE 0x02 16 | #define XHCI_TRB_TYPE_DATA_STAGE 0x03 17 | #define XHCI_TRB_TYPE_STATUS_STAGE 0x04 18 | #define XHCI_TRB_TYPE_ISOCH 0x05 19 | #define XHCI_TRB_TYPE_LINK 0x06 20 | #define XHCI_TRB_TYPE_EVENT_DATA 0x07 21 | #define XHCI_TRB_TYPE_NOOP 0x08 22 | #define XHCI_TRB_TYPE_ENABLE_SLOT 0x09 23 | #define XHCI_TRB_TYPE_DISABLE_SLOT 0x0A 24 | #define XHCI_TRB_TYPE_ADDRESS_DEVICE 0x0B 25 | #define XHCI_TRB_TYPE_CONFIGURE_EP 0x0C 26 | #define XHCI_TRB_TYPE_EVALUATE_CTX 0x0D 27 | #define XHCI_TRB_TYPE_RESET_EP 0x0E 28 | #define XHCI_TRB_TYPE_STOP_EP 0x0F 29 | #define XHCI_TRB_TYPE_SET_TR_DEQUEUE 0x10 30 | #define XHCI_TRB_TYPE_RESET_DEVICE 0x11 31 | #define XHCI_TRB_TYPE_FORCE_EVENT 0x12 32 | #define XHCI_TRB_TYPE_NEGOTIATE_BW 0x13 33 | #define XHCI_TRB_TYPE_SET_LATENCY_TOL 0x14 34 | #define XHCI_TRB_TYPE_GET_PORT_BW 0x15 35 | #define XHCI_TRB_TYPE_FORCE_HEADER 0x16 36 | #define XHCI_TRB_TYPE_NOOP_CMD 0x17 37 | #define TRB_RENESAS_GET_FW 49 38 | 39 | /* Events */ 40 | #define XHCI_TRB_EVENT_TRANSFER 0x20 41 | #define XHCI_TRB_EVENT_CMD_COMPLETE 0x21 42 | #define XHCI_TRB_EVENT_PORT_STS_CHANGE 0x22 43 | #define XHCI_TRB_EVENT_BW_REQUEST 0x23 44 | #define XHCI_TRB_EVENT_DOORBELL 0x24 45 | #define XHCI_TRB_EVENT_HOST_CTRL 0x25 46 | #define XHCI_TRB_EVENT_DEVICE_NOTIFY 0x26 47 | #define XHCI_TRB_EVENT_MFINDEX_WRAP 0x27 48 | #define TRB_RENESAS_CMD_COMP 48 49 | 50 | /* Error codes */ 51 | #define XHCI_TRB_ERROR_INVALID 0x00 52 | #define XHCI_TRB_ERROR_SUCCESS 0x01 53 | #define XHCI_TRB_ERROR_DATA_BUF 0x02 54 | #define XHCI_TRB_ERROR_BABBLE 0x03 55 | #define XHCI_TRB_ERROR_XACT 0x04 56 | #define XHCI_TRB_ERROR_TRB 0x05 57 | #define XHCI_TRB_ERROR_STALL 0x06 58 | #define XHCI_TRB_ERROR_RESOURCE 0x07 59 | #define XHCI_TRB_ERROR_BANDWIDTH 0x08 60 | #define XHCI_TRB_ERROR_NO_SLOTS 0x09 61 | #define XHCI_TRB_ERROR_STREAM_TYPE 0x0A 62 | #define XHCI_TRB_ERROR_SLOT_NOT_ON 0x0B 63 | #define XHCI_TRB_ERROR_ENDP_NOT_ON 0x0C 64 | #define XHCI_TRB_ERROR_SHORT_PKT 0x0D 65 | #define XHCI_TRB_ERROR_RING_UNDERRUN 0x0E 66 | #define XHCI_TRB_ERROR_RING_OVERRUN 0x0F 67 | #define XHCI_TRB_ERROR_VF_RING_FULL 0x10 68 | #define XHCI_TRB_ERROR_PARAMETER 0x11 69 | #define XHCI_TRB_ERROR_BW_OVERRUN 0x12 70 | #define XHCI_TRB_ERROR_CONTEXT_STATE 0x13 71 | #define XHCI_TRB_ERROR_NO_PING_RESP 0x14 72 | #define XHCI_TRB_ERROR_EV_RING_FULL 0x15 73 | #define XHCI_TRB_ERROR_INCOMPAT_DEV 0x16 74 | #define XHCI_TRB_ERROR_MISSED_SERVICE 0x17 75 | #define XHCI_TRB_ERROR_CMD_RING_STOP 0x18 76 | #define XHCI_TRB_ERROR_CMD_ABORTED 0x19 77 | #define XHCI_TRB_ERROR_STOPPED 0x1A 78 | #define XHCI_TRB_ERROR_LENGTH 0x1B 79 | #define XHCI_TRB_ERROR_BAD_MELAT 0x1D 80 | #define XHCI_TRB_ERROR_ISOC_OVERRUN 0x1F 81 | #define XHCI_TRB_ERROR_EVENT_LOST 0x20 82 | #define XHCI_TRB_ERROR_UNDEFINED 0x21 83 | #define XHCI_TRB_ERROR_INVALID_SID 0x22 84 | #define XHCI_TRB_ERROR_SEC_BW 0x23 85 | #define XHCI_TRB_ERROR_SPLIT_XACT 0x24 86 | 87 | #ifdef __cplusplus 88 | extern "C" { 89 | #endif 90 | 91 | struct TRBStruct 92 | { 93 | uint32_t a; 94 | uint32_t b; 95 | #define XHCI_TRB_1_WLENGTH_MASK (0xFFFFU << 16) 96 | uint32_t c; 97 | #define XHCI_TRB_2_ERROR_GET(x) (((x) >> 24) & 0xFF) 98 | #define XHCI_TRB_2_ERROR_SET(x) (((x) & 0xFF) << 24) 99 | #define XHCI_TRB_2_TDSZ_GET(x) (((x) >> 17) & 0x1F) 100 | #define XHCI_TRB_2_TDSZ_SET(x) (((x) & 0x1F) << 17) 101 | #define XHCI_TRB_2_REM_GET(x) ((x) & 0xFFFFFF) 102 | #define XHCI_TRB_2_REM_SET(x) ((x) & 0xFFFFFF) 103 | #define XHCI_TRB_2_BYTES_GET(x) ((x) & 0x1FFFF) 104 | #define XHCI_TRB_2_BYTES_SET(x) ((x) & 0x1FFFF) 105 | #define XHCI_TRB_2_IRQ_GET(x) (((x) >> 22) & 0x3FF) 106 | #define XHCI_TRB_2_IRQ_SET(x) (((x) & 0x3FF) << 22) 107 | #define XHCI_TRB_2_STREAM_GET(x) (((x) >> 16) & 0xFFFF) 108 | #define XHCI_TRB_2_STREAM_SET(x) (((x) & 0xFFFF) << 16) 109 | uint32_t d; 110 | #define XHCI_TRB_3_TYPE_GET(x) (((x) >> 10) & 0x3F) 111 | #define XHCI_TRB_3_TYPE_SET(x) (((x) & 0x3F) << 10) 112 | #define XHCI_TRB_3_CYCLE_BIT (1U << 0) 113 | #define XHCI_TRB_3_TC_BIT (1U << 1) /* command ring only */ 114 | #define XHCI_TRB_3_ENT_BIT (1U << 1) /* transfer ring only */ 115 | #define XHCI_TRB_3_ISP_BIT (1U << 2) 116 | #define XHCI_TRB_3_ED_BIT (1U << 2) 117 | #define XHCI_TRB_3_NSNOOP_BIT (1U << 3) 118 | #define XHCI_TRB_3_CHAIN_BIT (1U << 4) 119 | #define XHCI_TRB_3_IOC_BIT (1U << 5) 120 | #define XHCI_TRB_3_IDT_BIT (1U << 6) 121 | #define XHCI_TRB_3_TBC_GET(x) (((x) >> 7) & 3) 122 | #define XHCI_TRB_3_TBC_SET(x) (((x) & 3) << 7) 123 | #define XHCI_TRB_3_BEI_BIT (1U << 9) 124 | #define XHCI_TRB_3_DCEP_BIT (1U << 9) 125 | #define XHCI_TRB_3_PRSV_BIT (1U << 9) 126 | #define XHCI_TRB_3_BSR_BIT (1U << 9) 127 | #define XHCI_TRB_3_TRT_MASK (3U << 16) 128 | #define XHCI_TRB_3_TRT_NONE (0U << 16) 129 | #define XHCI_TRB_3_TRT_OUT (2U << 16) 130 | #define XHCI_TRB_3_TRT_IN (3U << 16) 131 | #define XHCI_TRB_3_DIR_IN (1U << 16) 132 | #define XHCI_TRB_3_TLBPC_GET(x) (((x) >> 16) & 0xF) 133 | #define XHCI_TRB_3_TLBPC_SET(x) (((x) & 0xF) << 16) 134 | #define XHCI_TRB_3_EP_GET(x) (((x) >> 16) & 0x1F) 135 | #define XHCI_TRB_3_EP_SET(x) (((x) & 0x1F) << 16) 136 | #define XHCI_TRB_3_FRID_GET(x) (((x) >> 20) & 0x7FF) 137 | #define XHCI_TRB_3_FRID_SET(x) (((x) & 0x7FF) << 20) 138 | #define XHCI_TRB_3_ISO_SIA_BIT (1U << 31) 139 | #define XHCI_TRB_3_SUSP_EP_BIT (1U << 23) 140 | #define XHCI_TRB_3_SLOT_GET(x) (((x) >> 24) & 0xFF) 141 | #define XHCI_TRB_3_SLOT_SET(x) (((x) & 0xFF) << 24) 142 | }; 143 | 144 | struct SetupStageHeader 145 | { 146 | uint8_t bmRequestType; 147 | uint8_t bRequest; 148 | uint16_t wValue; 149 | uint16_t wIndex; 150 | uint16_t wLength; 151 | }; 152 | 153 | #ifdef __cplusplus 154 | } 155 | #endif 156 | 157 | #endif 158 | -------------------------------------------------------------------------------- /XHCITypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // XHCITypes.h 3 | // GenericUSBXHCI 4 | // 5 | // Created by Zenith432 on January 26th, 2013. 6 | // Copyright (c) 2013 Zenith432. All rights reserved. 7 | // 8 | 9 | #ifndef GenericUSBXHCI_XHCITypes_h 10 | #define GenericUSBXHCI_XHCITypes_h 11 | 12 | #define EP_STATE_DISABLED 0U 13 | #define EP_STATE_RUNNING 1U 14 | #define EP_STATE_HALTED 2U 15 | #define EP_STATE_STOPPED 3U 16 | #define EP_STATE_ERROR 4U 17 | 18 | #define ISOC_OUT_EP 1U 19 | #define BULK_OUT_EP 2U 20 | #define INT_OUT_EP 3U 21 | #define CTRL_EP 4U 22 | #define ISOC_IN_EP 5U 23 | #define BULK_IN_EP 6U 24 | #define INT_IN_EP 7U 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | struct xhci_slot_ctx { 31 | uint32_t dwSctx0; 32 | #define XHCI_SCTX_0_ROUTE_SET(x) ((x) & 0xFFFFF) 33 | #define XHCI_SCTX_0_ROUTE_GET(x) ((x) & 0xFFFFF) 34 | #define XHCI_SCTX_0_SPEED_SET(x) (((x) & 0xF) << 20) 35 | #define XHCI_SCTX_0_SPEED_GET(x) (((x) >> 20) & 0xF) 36 | #define XHCI_SCTX_0_MTT_SET(x) (((x) & 0x1) << 25) 37 | #define XHCI_SCTX_0_MTT_GET(x) (((x) >> 25) & 0x1) 38 | #define XHCI_SCTX_0_HUB_SET(x) (((x) & 0x1) << 26) 39 | #define XHCI_SCTX_0_HUB_GET(x) (((x) >> 26) & 0x1) 40 | #define XHCI_SCTX_0_CTX_NUM_SET(x) (((x) & 0x1F) << 27) 41 | #define XHCI_SCTX_0_CTX_NUM_GET(x) (((x) >> 27) & 0x1F) 42 | uint32_t dwSctx1; 43 | #define XHCI_SCTX_1_MAX_EL_SET(x) ((x) & 0xFFFF) 44 | #define XHCI_SCTX_1_MAX_EL_GET(x) ((x) & 0xFFFF) 45 | #define XHCI_SCTX_1_RH_PORT_SET(x) (((x) & 0xFF) << 16) 46 | #define XHCI_SCTX_1_RH_PORT_GET(x) (((x) >> 16) & 0xFF) 47 | #define XHCI_SCTX_1_NUM_PORTS_SET(x) (((x) & 0xFF) << 24) 48 | #define XHCI_SCTX_1_NUM_PORTS_GET(x) (((x) >> 24) & 0xFF) 49 | uint32_t dwSctx2; 50 | #define XHCI_SCTX_2_TT_HUB_SID_SET(x) ((x) & 0xFF) 51 | #define XHCI_SCTX_2_TT_HUB_SID_GET(x) ((x) & 0xFF) 52 | #define XHCI_SCTX_2_TT_PORT_NUM_SET(x) (((x) & 0xFF) << 8) 53 | #define XHCI_SCTX_2_TT_PORT_NUM_GET(x) (((x) >> 8) & 0xFF) 54 | #define XHCI_SCTX_2_TT_THINK_TIME_SET(x) (((x) & 0x3) << 16) 55 | #define XHCI_SCTX_2_TT_THINK_TIME_GET(x) (((x) >> 16) & 0x3) 56 | #define XHCI_SCTX_2_IRQ_TARGET_SET(x) (((x) & 0x3FF) << 22) 57 | #define XHCI_SCTX_2_IRQ_TARGET_GET(x) (((x) >> 22) & 0x3FF) 58 | uint32_t dwSctx3; 59 | #define XHCI_SCTX_3_DEV_ADDR_SET(x) ((x) & 0xFF) 60 | #define XHCI_SCTX_3_DEV_ADDR_GET(x) ((x) & 0xFF) 61 | #define XHCI_SCTX_3_SLOT_STATE_SET(x) (((x) & 0x1F) << 27) 62 | #define XHCI_SCTX_3_SLOT_STATE_GET(x) (((x) >> 27) & 0x1F) 63 | uint32_t pad[4]; 64 | }; 65 | 66 | struct xhci_endp_ctx { 67 | uint32_t dwEpCtx0; 68 | #define XHCI_EPCTX_0_EPSTATE_SET(x) ((x) & 0x7) 69 | #define XHCI_EPCTX_0_EPSTATE_GET(x) ((x) & 0x7) 70 | #define XHCI_EPCTX_0_MULT_SET(x) (((x) & 0x3) << 8) 71 | #define XHCI_EPCTX_0_MULT_GET(x) (((x) >> 8) & 0x3) 72 | #define XHCI_EPCTX_0_MAXP_STREAMS_SET(x) (((x) & 0x1F) << 10) 73 | #define XHCI_EPCTX_0_MAXP_STREAMS_GET(x) (((x) >> 10) & 0x1F) 74 | #define XHCI_EPCTX_0_LSA_SET(x) (((x) & 0x1) << 15) 75 | #define XHCI_EPCTX_0_LSA_GET(x) (((x) >> 15) & 0x1) 76 | #define XHCI_EPCTX_0_IVAL_SET(x) (((x) & 0xFF) << 16) 77 | #define XHCI_EPCTX_0_IVAL_GET(x) (((x) >> 16) & 0xFF) 78 | uint32_t dwEpCtx1; 79 | #define XHCI_EPCTX_1_CERR_SET(x) (((x) & 0x3) << 1) 80 | #define XHCI_EPCTX_1_CERR_GET(x) (((x) >> 1) & 0x3) 81 | #define XHCI_EPCTX_1_EPTYPE_SET(x) (((x) & 0x7) << 3) 82 | #define XHCI_EPCTX_1_EPTYPE_GET(x) (((x) >> 3) & 0x7) 83 | #define XHCI_EPCTX_1_HID_SET(x) (((x) & 0x1) << 7) 84 | #define XHCI_EPCTX_1_HID_GET(x) (((x) >> 7) & 0x1) 85 | #define XHCI_EPCTX_1_MAXB_SET(x) (((x) & 0xFF) << 8) 86 | #define XHCI_EPCTX_1_MAXB_GET(x) (((x) >> 8) & 0xFF) 87 | #define XHCI_EPCTX_1_MAXP_SIZE_SET(x) (((x) & 0xFFFF) << 16) 88 | #define XHCI_EPCTX_1_MAXP_SIZE_GET(x) (((x) >> 16) & 0xFFFF) 89 | uint64_t qwEpCtx2; 90 | #define XHCI_EPCTX_2_DCS_SET(x) ((x) & 0x1) 91 | #define XHCI_EPCTX_2_DCS_GET(x) ((x) & 0x1) 92 | #define XHCI_EPCTX_2_TR_DQ_PTR_MASK 0xFFFFFFFFFFFFFFF0ULL 93 | uint32_t dwEpCtx4; 94 | #define XHCI_EPCTX_4_AVG_TRB_LEN_SET(x) ((x) & 0xFFFF) 95 | #define XHCI_EPCTX_4_AVG_TRB_LEN_GET(x) ((x) & 0xFFFF) 96 | #define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(x) (((x) & 0xFFFF) << 16) 97 | #define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_GET(x) (((x) >> 16) & 0xFFFF) 98 | uint32_t pad[3]; 99 | }; 100 | 101 | struct xhci_input_control_ctx { 102 | #define XHCI_INCTX_NON_CTRL_MASK 0xFFFFFFFCU 103 | uint32_t dwInCtx0; 104 | #define XHCI_INCTX_0_DROP_MASK(n) (1U << (n)) 105 | uint32_t dwInCtx1; 106 | #define XHCI_INCTX_1_ADD_MASK(n) (1U << (n)) 107 | uint32_t pad[6]; 108 | }; 109 | 110 | union ContextStruct { 111 | struct xhci_slot_ctx _s; 112 | struct xhci_endp_ctx _e; 113 | struct xhci_input_control_ctx _ic; 114 | }; 115 | 116 | struct xhci_stream_ctx { 117 | uint64_t qwSctx0; 118 | #define XHCI_SCTX_0_DCS_GET(x) ((x) & 0x1) 119 | #define XHCI_SCTX_0_DCS_SET(x) ((x) & 0x1) 120 | #define XHCI_SCTX_0_SCT_SET(x) (((x) & 0x7) << 1) 121 | #define XHCI_SCTX_0_SCT_GET(x) (((x) >> 1) & 0x7) 122 | #define XHCI_SCTX_0_SCT_SEC_TR_RING 0x0 123 | #define XHCI_SCTX_0_SCT_PRIM_TR_RING 0x1 124 | #define XHCI_SCTX_0_SCT_PRIM_SSA_8 0x2 125 | #define XHCI_SCTX_0_SCT_PRIM_SSA_16 0x3 126 | #define XHCI_SCTX_0_SCT_PRIM_SSA_32 0x4 127 | #define XHCI_SCTX_0_SCT_PRIM_SSA_64 0x5 128 | #define XHCI_SCTX_0_SCT_PRIM_SSA_128 0x6 129 | #define XHCI_SCTX_0_SCT_PRIM_SSA_256 0x7 130 | #define XHCI_SCTX_0_TR_DQ_PTR_MASK 0xFFFFFFFFFFFFFFF0ULL 131 | uint32_t dwSctx2; 132 | uint32_t dwSctx3; 133 | }; 134 | 135 | struct EventRingSegmentTable 136 | { 137 | uint64_t qwEvrsTablePtr; 138 | uint32_t dwEvrsTableSize; 139 | uint32_t dwEvrsReserved; 140 | }; 141 | 142 | #ifdef __cplusplus 143 | } 144 | #endif 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # really just some handy scripts... 2 | 3 | KEXT=GenericUSBXHCI.kext 4 | DIST=RehabMan-Generic-USB3 5 | 6 | VERSION_ERA=$(shell ./print_version.sh) 7 | ifeq "$(VERSION_ERA)" "10.10-" 8 | INSTDIR=/System/Library/Extensions 9 | else 10 | INSTDIR=/Library/Extensions 11 | endif 12 | 13 | #OPTIONS=LOGNAME=$(LOGNAME) 14 | 15 | ifeq ($(findstring 32,$(BITS)),32) 16 | OPTIONS:=$(OPTIONS) -arch i386 17 | endif 18 | 19 | ifeq ($(findstring 64,$(BITS)),64) 20 | OPTIONS:=$(OPTIONS) -arch x86_64 21 | endif 22 | 23 | INSTALLDIR=Universal 24 | #ifeq ($(OSTYPE),"darwin14") 25 | #INSTALLDIR=Yosemite 26 | #endif 27 | 28 | .PHONY: all 29 | all: 30 | #xcodebuild build $(OPTIONS) -configuration Legacy 31 | #xcodebuild build $(OPTIONS) -configuration Yosemite 32 | #xcodebuild build $(OPTIONS) -configuration Mavericks 33 | xcodebuild build $(OPTIONS) -configuration Universal 34 | make -f xhcdump.mak 35 | 36 | .PHONY: clean 37 | clean: 38 | #xcodebuild clean $(OPTIONS) -configuration Legacy 39 | #xcodebuild clean $(OPTIONS) -configuration Yosemite 40 | #xcodebuild clean $(OPTIONS) -configuration Mavericks 41 | xcodebuild clean $(OPTIONS) -configuration Universal 42 | rm ./xhcdump 43 | 44 | .PHONY: update_kernelcache 45 | update_kernelcache: 46 | sudo touch /System/Library/Extensions 47 | sudo kextcache -update-volume / 48 | 49 | .PHONY: install 50 | install: 51 | sudo cp -R ./Build/$(INSTALLDIR)/$(KEXT) $(INSTDIR) 52 | make update_kernelcache 53 | 54 | .PHONY: distribute 55 | distribute: 56 | if [ -e ./Distribute ]; then rm -r ./Distribute; fi 57 | mkdir ./Distribute 58 | #cp -R ./Build/Legacy ./Distribute 59 | #cp -R ./Build/Mavericks ./Distribute 60 | #cp -R ./Build/Yosemite ./Distribute 61 | cp -R ./Build/Universal ./Distribute 62 | cp ./xhcdump ./Distribute 63 | find ./Distribute -path *.DS_Store -delete 64 | find ./Distribute -path *.dSYM -exec echo rm -r {} \; >/tmp/org.voodoo.rm.dsym.sh 65 | chmod +x /tmp/org.voodoo.rm.dsym.sh 66 | /tmp/org.voodoo.rm.dsym.sh 67 | rm /tmp/org.voodoo.rm.dsym.sh 68 | ditto -c -k --sequesterRsrc --zlibCompressionLevel 9 ./Distribute ./Archive.zip 69 | mv ./Archive.zip ./Distribute/`date +$(DIST)-%Y-%m%d.zip` 70 | -------------------------------------------------------------------------------- /print_version.sh: -------------------------------------------------------------------------------- 1 | #set -x 2 | 3 | # extract minor version (eg. 10.9 vs. 10.10 vs. 10.11) 4 | MINOR_VER=$([[ "$(sw_vers -productVersion)" =~ [0-9]+\.([0-9]+) ]] && echo ${BASH_REMATCH[1]}) 5 | if [[ $MINOR_VER -ge 11 ]]; then 6 | echo 10.11+ 7 | else 8 | echo 10.10- 9 | fi 10 | -------------------------------------------------------------------------------- /xhcdump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define CLASSNAME "GenericUSBXHCI" 7 | 8 | #define kGUXUCType 1U 9 | 10 | #define kGUXCapRegsDump 1U 11 | #define kGUXRunRegsDump 2U 12 | #define kGUXSlotsDump 3U 13 | #define kGUXEndpointsDump 4U 14 | #define kGUXBandwidthDump 5U 15 | #define kGUXOptionsDump 6U 16 | 17 | void printMsgBuffer(io_service_t service, unsigned type) 18 | { 19 | kern_return_t ret; 20 | io_connect_t connect; 21 | #if __LP64__ 22 | mach_vm_address_t address; 23 | mach_vm_size_t size; 24 | #else 25 | vm_address_t address; 26 | vm_size_t size; 27 | #endif 28 | 29 | ret = IOServiceOpen(service, mach_task_self(), kGUXUCType, &connect); 30 | if (ret != KERN_SUCCESS) { 31 | printf("error: IOServiceOpen returned %#x\n", ret); 32 | return; 33 | } 34 | 35 | ret = IOConnectMapMemory(connect, type, mach_task_self(), &address, &size, kIOMapAnywhere); 36 | if (ret != kIOReturnSuccess) { 37 | printf("error: IOConnectMapMemory returned %#x\n", ret); 38 | IOServiceClose(connect); 39 | return; 40 | } 41 | 42 | printf("%s\n", (char *) address); 43 | 44 | IOServiceClose(connect); 45 | } 46 | 47 | void usage(char const* me) 48 | { 49 | fprintf(stderr, "Usage: %s | bandwidth | options>\n", me); 50 | fprintf(stderr, " caps - dumps cap regs\n"); 51 | fprintf(stderr, " running - dumps running regs\n"); 52 | fprintf(stderr, " slots - dumps active device slots\n"); 53 | fprintf(stderr, " endpoints - dumps active endpoints on slot\n"); 54 | fprintf(stderr, " bandwidth - dumps bandwidth for root hub ports\n"); 55 | fprintf(stderr, " options - dumps kernel flags supported by kext\n"); 56 | } 57 | 58 | int main(int argc, char const* argv[]) 59 | { 60 | mach_port_t masterPort; 61 | io_iterator_t iter = 0; 62 | io_service_t service; 63 | kern_return_t ret; 64 | io_string_t path; 65 | unsigned type = kGUXCapRegsDump; 66 | 67 | if (argc < 2) 68 | goto do_usage; 69 | if (!strcmp(argv[1], "caps")) 70 | type = kGUXCapRegsDump; 71 | else if (!strcmp(argv[1], "running")) 72 | type = kGUXRunRegsDump; 73 | else if (!strcmp(argv[1], "slots")) 74 | type = kGUXSlotsDump; 75 | else if (!strcmp(argv[1], "endpoints")) { 76 | if (argc < 3) 77 | goto do_usage; 78 | int slot_num = atoi(argv[2]); 79 | type = ((unsigned) (slot_num & 255) << 8) | kGUXEndpointsDump; 80 | } else if (!strcmp(argv[1], "bandwidth")) 81 | type = kGUXBandwidthDump; 82 | else if (!strcmp(argv[1], "options")) 83 | type = kGUXOptionsDump; 84 | else 85 | goto do_usage; 86 | 87 | ret = IOMasterPort(MACH_PORT_NULL, &masterPort); 88 | if (ret != KERN_SUCCESS) { 89 | printf("error: IOMasterPort returned %#x\n", ret); 90 | return -1; 91 | } 92 | ret = IOServiceGetMatchingServices(masterPort, IOServiceMatching(CLASSNAME), &iter); 93 | if (ret != KERN_SUCCESS) { 94 | printf("error: IOServiceGetMatchingServices returned %#x\n", ret); 95 | return -1; 96 | } 97 | while ((service = IOIteratorNext(iter)) != 0) { 98 | ret = IORegistryEntryGetPath(service, kIOServicePlane, path); 99 | if (ret != KERN_SUCCESS) { 100 | printf("error: IORegistryEntryGetPath returned %#x\n", ret); 101 | IOObjectRelease(service); 102 | continue; 103 | } 104 | printf("Found a device of class "CLASSNAME": %s\n", path); 105 | printMsgBuffer(service, type); 106 | IOObjectRelease(service); 107 | } 108 | IOObjectRelease(iter); 109 | 110 | return 0; 111 | 112 | do_usage: 113 | usage(argv[0]); 114 | return 0; 115 | } 116 | -------------------------------------------------------------------------------- /xhcdump.mak: -------------------------------------------------------------------------------- 1 | CC:=clang 2 | TARGET_ARCH:=-m64 3 | CFLAGS:=-fvisibility=hidden -fno-stack-protector -mmacosx-version-min=10.5 -Wall -Wextra 4 | LDFLAGS:=-framework IOKit -Xlinker -no_function_starts -Xlinker -no_version_load_command -Xlinker -no_data_in_code_info -Xlinker -no_uuid 5 | xhcdump: xhcdump.c 6 | $(LINK.c) -o $@ $^ 7 | /usr/bin/strip -x $@ 8 | --------------------------------------------------------------------------------