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;
}
Example #2
0
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;
}