IOService* BrcmPatchRAM::probe(IOService *provider, SInt32 *probeScore) { extern kmod_info_t kmod_info; uint64_t start_time, end_time, nano_secs; DebugLog("probe\n"); AlwaysLog("Version %s starting on OS X Darwin %d.%d.\n", kmod_info.version, version_major, version_minor); clock_get_uptime(&start_time); mWorkLock = IOLockAlloc(); if (!mWorkLock) return NULL; mCompletionLock = IOLockAlloc(); if (!mCompletionLock) return NULL; mDevice = OSDynamicCast(IOUSBDevice, provider); if (!mDevice) { AlwaysLog("Provider is not a USB device.\n"); return NULL; } mDevice->retain(); initBrcmStrings(); OSString* displayName = OSDynamicCast(OSString, getProperty(kDisplayName)); if (displayName) provider->setProperty(kUSBProductString, displayName); mVendorId = mDevice->GetVendorID(); mProductId = mDevice->GetProductID(); // get firmware here to pre-cache for eventual use on wakeup or now if (BrcmFirmwareStore* firmwareStore = getFirmwareStore()) firmwareStore->getFirmware(OSDynamicCast(OSString, getProperty(kFirmwareKey))); uploadFirmware(); publishPersonality(); clock_get_uptime(&end_time); absolutetime_to_nanoseconds(end_time - start_time, &nano_secs); uint64_t milli_secs = nano_secs / 1000000; AlwaysLog("Processing time %llu.%llu seconds.\n", milli_secs / 1000, milli_secs % 1000); return this; }
void BrcmPatchRAM::uploadFirmware() { // get firmware here to pre-cache for eventual use on wakeup or now BrcmFirmwareStore* firmwareStore = getFirmwareStore(); OSArray* instructions = NULL; if (!firmwareStore || !firmwareStore->getFirmware(OSDynamicCast(OSString, getProperty(kFirmwareKey)))) return; if (mDevice->open(this)) { // Print out additional device information printDeviceInfo(); // Set device configuration to composite configuration index 0 // Obtain first interface if (setConfiguration(0) && (mInterface = findInterface()) && mInterface->open(this)) { mInterruptPipe = findPipe(kUSBInterrupt, kUSBIn); mBulkPipe = findPipe(kUSBBulk, kUSBOut); if (mInterruptPipe && mBulkPipe) { if (performUpgrade()) AlwaysLog("[%04x:%04x]: Firmware upgrade completed successfully.\n", mVendorId, mProductId); else AlwaysLog("[%04x:%04x]: Firmware upgrade failed.\n", mVendorId, mProductId); OSSafeReleaseNULL(mReadBuffer); // mReadBuffer is allocated by performUpgrade but not released } mInterface->close(this); } // cleanup if (mInterruptPipe) { mInterruptPipe->Abort(); mInterruptPipe->release(); // retained in findPipe mInterruptPipe = NULL; } if (mBulkPipe) { mBulkPipe->Abort(); mBulkPipe->release(); // retained in findPipe mBulkPipe = NULL; } OSSafeReleaseNULL(mInterface);// retained in findInterface mDevice->close(this); } }
bool BrcmPatchRAM::performUpgrade() { BrcmFirmwareStore* firmwareStore; OSArray* instructions = NULL; OSCollectionIterator* iterator = NULL; OSData* data; #ifdef DEBUG DeviceState previousState = kUnknown; #endif IOLockLock(mCompletionLock); mDeviceState = kInitialize; while (true) { #ifdef DEBUG if (mDeviceState != kInstructionWrite && mDeviceState != kInstructionWritten) DebugLog("[%04x:%04x]: State \"%s\" --> \"%s\".\n", mVendorId, mProductId, getState(previousState), getState(mDeviceState)); previousState = mDeviceState; #endif // Break out when done if (mDeviceState == kUpdateAborted || mDeviceState == kUpdateComplete) break; // Note on following switch/case: // use 'break' when a response from io completion callback is expected // use 'continue' when a change of state with no expected response (loop again) switch (mDeviceState) { case kInitialize: hciCommand(&HCI_VSC_READ_VERBOSE_CONFIG, sizeof(HCI_VSC_READ_VERBOSE_CONFIG)); break; case kFirmwareVersion: // Unable to retrieve firmware store if (!(firmwareStore = getFirmwareStore())) { mDeviceState = kUpdateAborted; continue; } instructions = firmwareStore->getFirmware(OSDynamicCast(OSString, getProperty(kFirmwareKey))); // Unable to retrieve firmware instructions if (!instructions) { mDeviceState = kUpdateAborted; continue; } // Initiate firmware upgrade hciCommand(&HCI_VSC_DOWNLOAD_MINIDRIVER, sizeof(HCI_VSC_DOWNLOAD_MINIDRIVER)); break; case kMiniDriverComplete: // Write firmware data to bulk pipe iterator = OSCollectionIterator::withCollection(instructions); if (!iterator) { mDeviceState = kUpdateAborted; continue; } // If this IOSleep is not issued, the device is not ready to receive // the firmware instructions and we will deadlock due to lack of // responses. IOSleep(10); // Write first 2 instructions to trigger response if ((data = OSDynamicCast(OSData, iterator->getNextObject()))) bulkWrite(data->getBytesNoCopy(), data->getLength()); if ((data = OSDynamicCast(OSData, iterator->getNextObject()))) bulkWrite(data->getBytesNoCopy(), data->getLength()); break; case kInstructionWrite: // should never happen, but would cause a crash if (!iterator) { mDeviceState = kUpdateAborted; continue; } if ((data = OSDynamicCast(OSData, iterator->getNextObject()))) bulkWrite(data->getBytesNoCopy(), data->getLength()); else // Firmware data fully written hciCommand(&HCI_VSC_END_OF_RECORD, sizeof(HCI_VSC_END_OF_RECORD)); break; case kInstructionWritten: mDeviceState = kInstructionWrite; continue; case kFirmwareWritten: hciCommand(&HCI_RESET, sizeof(HCI_RESET)); break; case kResetComplete: resetDevice(); getDeviceStatus(); mDeviceState = kUpdateComplete; continue; case kUnknown: case kUpdateComplete: case kUpdateAborted: DebugLog("Error: kUnkown/kUpdateComplete/kUpdateAborted cases should be unreachable.\n"); break; } // queue async read if (!continuousRead()) { mDeviceState = kUpdateAborted; continue; } // wait for completion of the async read IOLockSleep(mCompletionLock, NULL, 0); } IOLockUnlock(mCompletionLock); OSSafeRelease(iterator); return mDeviceState == kUpdateComplete; }
IOService* BrcmPatchRAM::probe(IOService *provider, SInt32 *probeScore) { uint64_t start_time, end_time, nano_secs; DebugLog("probe\n"); AlwaysLog("Version %s starting on OS X Darwin %d.%d.\n", OSKextGetCurrentVersionString(), version_major, version_minor); #ifdef TARGET_ELCAPITAN // preference towards starting BrcmPatchRAM2.kext when BrcmPatchRAM.kext also exists *probeScore = 2000; #endif #ifndef TARGET_ELCAPITAN // BrcmPatchRAM.kext, if installed on 10.11+... fails immediately if (version_major >= 15) { AlwaysLog("Aborting -- BrcmPatchRAM.kext should not be installed on 10.11+. Use BrcmPatchRAM2.kext instead.\n"); return NULL; } #endif clock_get_uptime(&start_time); #ifndef NON_RESIDENT mWorkLock = IOLockAlloc(); if (!mWorkLock) return NULL; // Note: mLoadFirmwareLock is static (global), not instance data... if (!mLoadFirmwareLock) return NULL; #endif mCompletionLock = IOLockAlloc(); if (!mCompletionLock) return NULL; mDevice.setDevice(provider); if (!mDevice.getValidatedDevice()) { AlwaysLog("Provider type is incorrect (not IOUSBDevice or IOUSBHostDevice)\n"); return NULL; } // personality strings depend on version initBrcmStrings(); #ifndef NON_RESIDENT // longest time seen in normal re-probe was ~200ms (400+ms on 10.11) if (version_major >= 15) mBlurpWait = 800; else mBlurpWait = 400; #endif OSString* displayName = OSDynamicCast(OSString, getProperty(kDisplayName)); if (displayName) provider->setProperty(kUSBProductString, displayName); mVendorId = mDevice.getVendorID(); mProductId = mDevice.getProductID(); // get firmware here to pre-cache for eventual use on wakeup or now if (OSString* firmwareKey = OSDynamicCast(OSString, getProperty(kFirmwareKey))) { if (BrcmFirmwareStore* firmwareStore = getFirmwareStore()) firmwareStore->getFirmware(mVendorId, mProductId, firmwareKey); } uploadFirmware(); publishPersonality(); clock_get_uptime(&end_time); absolutetime_to_nanoseconds(end_time - start_time, &nano_secs); uint64_t milli_secs = nano_secs / 1000000; AlwaysLog("Processing time %llu.%llu seconds.\n", milli_secs / 1000, milli_secs % 1000); #ifdef NON_RESIDENT // maybe residency is not required for 10.11? mDevice.setDevice(NULL); return NULL; #endif return this; }