From 50e5f57b18a494d75ec364f6540a71dee20ce283 Mon Sep 17 00:00:00 2001 From: Huy Duong Date: Fri, 16 Jun 2023 11:06:35 +0700 Subject: [PATCH] Fix LE device not working on Ventura Look like starting from Ventura, the bluetooth daemon expects an LE PHY Update Complete event to be triggered from the controller before starting the connection. However for Intel cards, that event is not sent by the firmware, so we can fake it by issuing an extra LE command and modify its response. --- IntelBTPatcher/IntelBTPatcher.cpp | 111 ++++++++++++++++++++++++++++-- IntelBTPatcher/IntelBTPatcher.hpp | 15 ++-- 2 files changed, 112 insertions(+), 14 deletions(-) diff --git a/IntelBTPatcher/IntelBTPatcher.cpp b/IntelBTPatcher/IntelBTPatcher.cpp index 6351761..6fd525d 100644 --- a/IntelBTPatcher/IntelBTPatcher.cpp +++ b/IntelBTPatcher/IntelBTPatcher.cpp @@ -66,6 +66,8 @@ static KernelPatcher::KextInfo IntelBTPatcher_IOUsbHostInfo { KernelPatcher::KextInfo::Unloaded }; +void *CIntelBTPatcher::_hookPipeInstance = nullptr; +AsyncOwnerData *CIntelBTPatcher::_interruptPipeAsyncOwner = nullptr; bool CIntelBTPatcher::_randomAddressInit = false; bool CIntelBTPatcher::init() @@ -128,6 +130,32 @@ void CIntelBTPatcher::processKext(KernelPatcher &patcher, size_t index, mach_vm_ SYSLOG(DRV_NAME, "failed to resolve %s, error = %d", hostDeviceRequest.symbol, patcher.getError()); patcher.clearError(); } + + KernelPatcher::RouteRequest asyncIORequest { + "__ZN13IOUSBHostPipe2ioEP18IOMemoryDescriptorjP19IOUSBHostCompletionj", + newAsyncIO, + oldAsyncIO + }; + patcher.routeMultiple(index, &asyncIORequest, 1, address, size); + if (patcher.getError() == KernelPatcher::Error::NoError) { + SYSLOG(DRV_NAME, "routed %s", asyncIORequest.symbol); + } else { + SYSLOG(DRV_NAME, "failed to resolve %s, error = %d", asyncIORequest.symbol, patcher.getError()); + patcher.clearError(); + } + + KernelPatcher::RouteRequest initPipeRequest { + "__ZN13IOUSBHostPipe28initWithDescriptorsAndOwnersEPKN11StandardUSB18EndpointDescriptorEPKNS0_37SuperSpeedEndpointCompanionDescriptorEP22AppleUSBHostControllerP15IOUSBHostDeviceP18IOUSBHostInterfaceht", + newInitPipe, + oldInitPipe + }; + patcher.routeMultiple(index, &initPipeRequest, 1, address, size); + if (patcher.getError() == KernelPatcher::Error::NoError) { + SYSLOG(DRV_NAME, "routed %s", initPipeRequest.symbol); + } else { + SYSLOG(DRV_NAME, "failed to resolve %s, error = %d", initPipeRequest.symbol, patcher.getError()); + patcher.clearError(); + } } } } @@ -153,10 +181,11 @@ StandardUSB::DeviceRequest randomAddressRequest; const uint8_t randomAddressHci[9] = {0x05, 0x20, 0x06, 0x94, 0x50, 0x64, 0xD0, 0x78, 0x6B}; IOBufferMemoryDescriptor *writeHCIDescriptor = nullptr; -#define MAX_HCI_BUF_LEN 255 -#define HCI_OP_RESET 0x0c03 -#define HCI_OP_LE_SET_SCAN_PARAM 0x200B -#define HCI_OP_LE_SET_SCAN_ENABLE 0x200C +#define MAX_HCI_BUF_LEN 255 +#define HCI_OP_RESET 0x0c03 +#define HCI_OP_LE_SET_SCAN_PARAM 0x200B +#define HCI_OP_LE_SET_SCAN_ENABLE 0x200C +#define HCI_OP_LE_READ_REMOTE_FEATURES 0x2016 IOReturn CIntelBTPatcher::newHostDeviceRequest(void *that, IOService *provider, StandardUSB::DeviceRequest &request, void *data, IOMemoryDescriptor *descriptor, unsigned int &length, IOUSBHostCompletion *completion, unsigned int timeout) { @@ -185,12 +214,15 @@ IOReturn CIntelBTPatcher::newHostDeviceRequest(void *that, IOService *provider, writeHCIDescriptor->complete(); const char *randAddressDump = _hexDumpHCIData((uint8_t *)randomAddressHci, 9); if (randAddressDump) { - SYSLOG(DRV_NAME, "[PATCH] Sending Random Address HCI %lld %s", ret, randAddressDump); + SYSLOG(DRV_NAME, "[PATCH] Sending Random Address HCI %d %s", ret, randAddressDump); IOFree((void *)randAddressDump, 9 * 3 + 1); } _randomAddressInit = true; - SYSLOG(DRV_NAME, "[PATCH] Resend LE SCAN PARAM HCI %lld", ret); + SYSLOG(DRV_NAME, "[PATCH] Resend LE SCAN PARAM HCI %d", ret); } + } else if (hdr->opcode == HCI_OP_LE_READ_REMOTE_FEATURES) { + IOReturn ret = FunctionCast(newHostDeviceRequest, callbackIBTPatcher->oldHostDeviceRequest)(that, provider, request, nullptr, descriptor, length, nullptr, timeout); + SYSLOG(DRV_NAME, "[PATCH] Sending extra LE Read Remote Features command %d", ret); } } else { hdr = (HciCommandHdr *)data; @@ -213,3 +245,70 @@ IOReturn CIntelBTPatcher::newHostDeviceRequest(void *that, IOService *provider, } return FunctionCast(newHostDeviceRequest, callbackIBTPatcher->oldHostDeviceRequest)(that, provider, request, data, descriptor, length, completion, timeout); } + +#define HCI_EVT_LE_META 0x3E +#define HCI_EVT_LE_META_READ_REMOTE_FEATURES_COMPLETE 0x04 + +uint8_t fakePhyUpdateCompleteEvent[8] = {0x3E, 0x06, 0x0C, 0x00, 0x00, 0x00, 0x02, 0x02}; + +static void asyncIOCompletion(void* owner, void* parameter, IOReturn status, uint32_t bytesTransferred) +{ + AsyncOwnerData *asyncOwner = (AsyncOwnerData *)owner; + IOMemoryDescriptor* dataBuffer = asyncOwner->dataBuffer; + static bool skipExtraReadRemoteFeaturesComplete = true; + + if (dataBuffer && bytesTransferred) { + void *buffer = IOMalloc(bytesTransferred); + dataBuffer->readBytes(0, buffer, bytesTransferred); + HciEventHdr *hdr = (HciEventHdr *)buffer; + if (hdr->evt == HCI_EVT_LE_META && hdr->data[0] == HCI_EVT_LE_META_READ_REMOTE_FEATURES_COMPLETE) { + if (skipExtraReadRemoteFeaturesComplete) { + skipExtraReadRemoteFeaturesComplete = false; + } else { + // Copy Connection Handle + fakePhyUpdateCompleteEvent[4] = hdr->data[2]; + fakePhyUpdateCompleteEvent[5] = hdr->data[3]; + dataBuffer->writeBytes(0, fakePhyUpdateCompleteEvent, 8); + skipExtraReadRemoteFeaturesComplete = true; + } + } + IOFree(buffer, bytesTransferred); + } + if (asyncOwner->action) + asyncOwner->action(asyncOwner->owner, parameter, status, bytesTransferred); +} + +IOReturn CIntelBTPatcher:: +newAsyncIO(void *that, IOMemoryDescriptor* dataBuffer, uint32_t bytesTransferred, IOUSBHostCompletion* completion, uint32_t completionTimeoutMs) +{ + if (that == _hookPipeInstance && completion) { + _interruptPipeAsyncOwner->action = completion->action; + _interruptPipeAsyncOwner->owner = completion->owner; + _interruptPipeAsyncOwner->dataBuffer = dataBuffer; + completion->action = asyncIOCompletion; + completion->owner = _interruptPipeAsyncOwner; + } + return FunctionCast(newAsyncIO, callbackIBTPatcher->oldAsyncIO)(that, dataBuffer, bytesTransferred, completion, completionTimeoutMs); +} + +#define VENDOR_USB_INTEL 0x8087 + +int CIntelBTPatcher:: +newInitPipe(void *that, StandardUSB::EndpointDescriptor const *descriptor, StandardUSB::SuperSpeedEndpointCompanionDescriptor const *superDescriptor, AppleUSBHostController *controller, IOUSBHostDevice *device, IOUSBHostInterface *interface, unsigned char a7, unsigned short a8) +{ + int ret = FunctionCast(newInitPipe, callbackIBTPatcher->oldInitPipe)(that, descriptor, superDescriptor, controller, device, interface, a7, a8); + if (device) { + const StandardUSB::DeviceDescriptor *deviceDescriptor = device->getDeviceDescriptor(); + if (deviceDescriptor && + deviceDescriptor->idVendor == VENDOR_USB_INTEL) { + uint8_t epType = StandardUSB::getEndpointType(descriptor); + if (epType == kIOUSBEndpointTypeInterrupt) { + CIntelBTPatcher::_hookPipeInstance = that; + if (!CIntelBTPatcher::_interruptPipeAsyncOwner) + CIntelBTPatcher::_interruptPipeAsyncOwner = new AsyncOwnerData; + CIntelBTPatcher::_randomAddressInit = false; + } + } + } + return ret; +} diff --git a/IntelBTPatcher/IntelBTPatcher.hpp b/IntelBTPatcher/IntelBTPatcher.hpp index 0e5f7b3..fd37fe7 100644 --- a/IntelBTPatcher/IntelBTPatcher.hpp +++ b/IntelBTPatcher/IntelBTPatcher.hpp @@ -33,15 +33,8 @@ typedef struct __attribute__((packed)) { uint8_t evt; uint8_t len; -} HciEventHdr; - -typedef struct __attribute__((packed)) -{ - HciEventHdr evt; - uint8_t numCommands; - uint16_t opcode; uint8_t data[]; -} HciResponse; +} HciEventHdr; const char *requestDirectionNames[] = { "OUT", @@ -82,12 +75,18 @@ class CIntelBTPatcher { static IOReturn newFindQueueRequest(void *that, unsigned short arg1, void *addr, unsigned short arg2, bool arg3, void **hciRequestPtr); static IOReturn newHostDeviceRequest(void *that, IOService *provider, StandardUSB::DeviceRequest &request, void *data, IOMemoryDescriptor *descriptor, unsigned int &length,IOUSBHostCompletion *completion, unsigned int timeout); + static IOReturn newAsyncIO(void *that, IOMemoryDescriptor* dataBuffer, uint32_t dataBufferLength, IOUSBHostCompletion* completion, uint32_t completionTimeoutMs); + static int newInitPipe(void *that, StandardUSB::EndpointDescriptor const *descriptor, StandardUSB::SuperSpeedEndpointCompanionDescriptor const *superDescriptor,AppleUSBHostController *controller, IOUSBHostDevice *device, IOUSBHostInterface *interface, unsigned char, unsigned short); mach_vm_address_t oldFindQueueRequest {}; mach_vm_address_t oldHostDeviceRequest {}; + mach_vm_address_t oldAsyncIO {}; + mach_vm_address_t oldInitPipe {}; private: + static void *_hookPipeInstance; + static AsyncOwnerData *_interruptPipeAsyncOwner; static bool _randomAddressInit; };