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; }