From e43ef1f3e7630a112378773e0ba9c6c020b2e65f Mon Sep 17 00:00:00 2001 From: Cástor Muñoz Date: Fri, 5 Feb 2016 00:25:31 +0100 Subject: rbutil: add support for iPod Classic 6G bootloader Change-Id: I0e237a81098a2d4df8d9f5d6deaaab3863a84fc6 --- rbutil/rbutilqt/base/bootloaderinstallhelper.cpp | 4 + rbutil/rbutilqt/base/bootloaderinstalls5l.cpp | 428 +++++++++++++++++++++++ rbutil/rbutilqt/base/bootloaderinstalls5l.h | 71 ++++ 3 files changed, 503 insertions(+) create mode 100644 rbutil/rbutilqt/base/bootloaderinstalls5l.cpp create mode 100644 rbutil/rbutilqt/base/bootloaderinstalls5l.h (limited to 'rbutil/rbutilqt/base') diff --git a/rbutil/rbutilqt/base/bootloaderinstallhelper.cpp b/rbutil/rbutilqt/base/bootloaderinstallhelper.cpp index fe962d2b1d..d6d2dbc7a1 100644 --- a/rbutil/rbutilqt/base/bootloaderinstallhelper.cpp +++ b/rbutil/rbutilqt/base/bootloaderinstallhelper.cpp @@ -31,6 +31,7 @@ #include "bootloaderinstalltcc.h" #include "bootloaderinstallmpio.h" #include "bootloaderinstallimx.h" +#include "bootloaderinstalls5l.h" BootloaderInstallBase* BootloaderInstallHelper::createBootloaderInstaller(QObject* parent, QString type) { @@ -64,6 +65,9 @@ BootloaderInstallBase* BootloaderInstallHelper::createBootloaderInstaller(QObjec else if(type == "imx") { return new BootloaderInstallImx(parent); } + else if(type == "s5l") { + return new BootloaderInstallS5l(parent); + } else { return NULL; } diff --git a/rbutil/rbutilqt/base/bootloaderinstalls5l.cpp b/rbutil/rbutilqt/base/bootloaderinstalls5l.cpp new file mode 100644 index 0000000000..2442970655 --- /dev/null +++ b/rbutil/rbutilqt/base/bootloaderinstalls5l.cpp @@ -0,0 +1,428 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2008 by Dominik Riebeling + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include "bootloaderinstallbase.h" +#include "bootloaderinstalls5l.h" +#include "Logger.h" +#include "utils.h" +#include "system.h" +#include "rbsettings.h" +#include "systeminfo.h" + +#include "../mks5lboot/mks5lboot.h" + + +BootloaderInstallS5l::BootloaderInstallS5l(QObject *parent) + : BootloaderInstallBase(parent) +{ +} + + +bool BootloaderInstallS5l::install(void) +{ + LOG_INFO() << "installing bootloader"; + doInstall = true; + return installStage1(); +} + + +bool BootloaderInstallS5l::uninstall(void) +{ + LOG_INFO() << "uninstalling bootloader"; + doInstall = false; + return installStage1(); +} + + +bool BootloaderInstallS5l::installStage1(void) +{ + LOG_INFO() << "installStage1"; + + mntpoint = RbSettings::value(RbSettings::Mountpoint).toString(); + + if (!Utils::mountpoints(Utils::MountpointsSupported).contains(mntpoint)) { + LOG_ERROR() << "iPod not mounted:" << mntpoint; + emit logItem(tr("Could not find mounted iPod."), LOGERROR); + emit done(true); + return false; + } + + if (doInstall) { + // download firmware from server + emit logItem(tr("Downloading bootloader file..."), LOGINFO); + connect(this, SIGNAL(downloadDone()), this, SLOT(installStageMkdfu())); + downloadBlStart(m_blurl); + } + else { + installStageMkdfu(); + } + + return true; +} + + +void BootloaderInstallS5l::installStageMkdfu(void) +{ + int dfu_type; + QString dfu_arg; + char errstr[200]; + + LOG_INFO() << "installStageMkdfu"; + + setProgress(0); + aborted = false; + connect(this, SIGNAL(installAborted()), this, SLOT(abortInstall())); + connect(this, SIGNAL(done(bool)), this, SLOT(installDone(bool))); + + if (doInstall) { + dfu_type = DFU_INST; + m_tempfile.open(); + dfu_arg = m_tempfile.fileName(); + m_tempfile.close(); + } + else { + dfu_type = DFU_UNINST; + dfu_arg = RbSettings::value(RbSettings::Platform).toString(); + } + + // build DFU image + dfu_buf = mkdfu(dfu_type, dfu_arg.toLocal8Bit().data(), + &dfu_size, errstr, sizeof(errstr)); + if (!dfu_buf) { + LOG_ERROR() << "mkdfu() failed:" << errstr; + emit logItem(errstr, LOGERROR); + emit logItem(tr("Could not make DFU image."), LOGERROR); + emit done(true); + return; + } + + LOG_INFO() << "preparing installStageWaitForEject"; + emit logItem(tr("Ejecting iPod..."), LOGINFO); + setProgress(10); + scanTimer = QTime(); + installStageWaitForEject(); +} + + +void BootloaderInstallS5l::installStageWaitForEject(void) +{ + if (!updateProgress()) + return; /* aborted */ + + if (scanTimer.isNull() || (scanTimer.elapsed() > 3000)) { + scanSuccess = Utils::ejectDevice(mntpoint); + if (!scanSuccess) { + scanSuccess = !Utils::mountpoints( + Utils::MountpointsSupported).contains(mntpoint); + } + scanTimer.start(); + } + if (!scanSuccess) { + if (!actionShown) { + emit logItem(tr("Action required:\n" + " Please make sure no programs are accessing\n" + " files on the device. If ejecting still fails\n" + " please use your computers eject funtionality."), + LOGWARNING); + actionShown = true; + } + QTimer::singleShot(250, this, SLOT(installStageWaitForEject())); + return; + } + emit logItem(tr("Device successfully ejected."), LOGINFO); + + LOG_INFO() << "preparing installStageWaitForProcs"; + setProgress(40, 18); + scanTimer = QTime(); + installStageWaitForProcs(); +} + + +void BootloaderInstallS5l::installStageWaitForProcs(void) +{ + if (!updateProgress()) + return; /* aborted */ + + if (scanTimer.isNull() || (scanTimer.elapsed() > 1000)) { + scanSuccess = Utils::findRunningProcess(QStringList("iTunes")).isEmpty(); + scanTimer.start(); + } + if (!scanSuccess) { + if (!actionShown) { + emit logItem(tr("Action required:\n" + " Quit iTunes application."), LOGWARNING); + actionShown = true; + } + QTimer::singleShot(250, this, SLOT(installStageWaitForProcs())); + return; + } + if (actionShown) { + emit logItem(tr("iTunes closed."), LOGINFO); + if (!updateProgress()) + return; /* aborted */ + } + + QList helperPids = Utils::findRunningProcess( +#if defined(Q_OS_WIN32) + QStringList("iTunesHelper"))["iTunesHelper.exe"]; +#else + QStringList("iTunesHelper"))["iTunesHelper"]; +#endif + suspendedPids = Utils::suspendProcess(helperPids, true); + if (suspendedPids.size() != helperPids.size()) { + emit logItem(tr("Could not suspend iTunesHelper. Stop it\n" + "using the Task Manager, and try again."), LOGERROR); + emit done(true); + return; + } + + LOG_INFO() << "preparing installStageWaitForSpindown"; + // for Windows: skip waiting if the HDD was ejected a time ago + if (progressTimer.elapsed() < progressTimeout) + emit logItem(tr("Waiting for HDD spin-down..."), LOGINFO); + installStageWaitForSpindown(); +} + + +void BootloaderInstallS5l::installStageWaitForSpindown(void) +{ + if (!updateProgress()) + return; /* aborted */ + + if (progressTimer.elapsed() < progressTimeout) { + QTimer::singleShot(250, this, SLOT(installStageWaitForSpindown())); + return; + } + + LOG_INFO() << "preparing installStageWaitForDfu"; + emit logItem(tr("Waiting for DFU mode..."), LOGINFO); + emit logItem(tr("Action required:\n" + " Press and hold SELECT+MENU buttons, after\n" + " about 12 seconds a new action will require\n" + " you to release the buttons, DO IT QUICKLY,\n" + " otherwise the process could fail."), LOGWARNING); + scanTimer = QTime(); + installStageWaitForDfu(); +} + + +void BootloaderInstallS5l::installStageWaitForDfu(void) +{ + if (!updateProgress()) + return; /* aborted */ + + if (scanTimer.isNull() || (scanTimer.elapsed() > 2000)) { + scanSuccess = System::listUsbIds().contains(0x05ac1223); + scanTimer.start(); + } + if (!scanSuccess) { + QTimer::singleShot(250, this, SLOT(installStageWaitForDfu())); + return; + } + emit logItem(tr("DFU mode detected."), LOGINFO); + + emit logItem(tr("Action required:\n" + " Release SELECT+MENU buttons and wait..."), LOGWARNING); + + // Once the iPod enters DFU mode, the device will reset again if + // SELECT+MENU remains pressed for another 8 seconds. To avoid a + // reset while the NOR is being written, we wait ~10 seconds + // before sending the DFU image. + LOG_INFO() << "preparing installStageSendDfu"; + setProgress(60, 10); + installStageSendDfu(); +} + + +void BootloaderInstallS5l::installStageSendDfu(void) +{ + if (!updateProgress()) + return; /* aborted */ + + if (progressTimer.elapsed() < progressTimeout) { + QTimer::singleShot(250, this, SLOT(installStageSendDfu())); + return; + } + + if (!System::listUsbIds().contains(0x05ac1223)) { + LOG_ERROR() << "device not in DFU mode"; + emit logItem(tr("Device is not in DFU mode. It seems that\n" + "the previous required action failed, please\n" + "try again."), LOGERROR); + emit done(true); + return; + } + + emit logItem(tr("Transfering DFU image..."), LOGINFO); + if (!updateProgress()) + return; /* aborted */ + + char errstr[200]; + if (!ipoddfu_send(0x1223, dfu_buf, dfu_size, errstr, sizeof(errstr))) { + LOG_ERROR() << "ipoddfu_send() failed:" << errstr; +#if defined(Q_OS_WIN32) + if (strstr(errstr, "DFU device not found")) + { + emit logItem(tr("No valid DFU USB driver found.\n" + "Install iTunes (or the Apple Device Driver)\n" + "and try again."), + LOGERROR); + } + else +#endif + { + emit logItem(errstr, LOGERROR); + emit logItem(tr("Could not transfer DFU image."), LOGERROR); + } + emit done(true); + return; + } + emit logItem(tr("DFU transfer completed."), LOGINFO); + + LOG_INFO() << "preparing installStageWaitForRemount"; + emit logItem(tr("Restarting iPod, waiting for remount..."), LOGINFO); + setProgress(99, 45); + scanTimer = QTime(); + installStageWaitForRemount(); +} + + +void BootloaderInstallS5l::installStageWaitForRemount(void) +{ + if (!updateProgress()) + return; /* aborted */ + + if (scanTimer.isNull() || (scanTimer.elapsed() > 5000)) { + scanSuccess = Utils::mountpoints( + Utils::MountpointsSupported).contains(mntpoint); + scanTimer.start(); + } + if (!scanSuccess) { + if (!actionShown && (progressTimer.elapsed() > progressTimeout)) { + emit logItem(tr("Action required:\n" + " Could not remount the device, try to do it\n" + " manually. If the iPod didn't restart, force\n" + " a reset by pressing SELECT+MENU buttons\n" + " for about 5 seconds. If the problem could\n" + " not be solved then click 'Abort' to cancel."), + LOGWARNING); + actionShown = true; + } + QTimer::singleShot(250, this, SLOT(installStageWaitForRemount())); + return; + } + emit logItem(tr("Device remounted."), LOGINFO); + + emit logItem(tr("Bootloader successfully %1."). + arg(tr(doInstall ? "installed" : "uninstalled")), LOGOK); + + logInstall(doInstall ? LogAdd : LogRemove); + emit logProgress(1, 1); + emit done(false); +} + + +void BootloaderInstallS5l::installDone(bool status) +{ + LOG_INFO() << "installDone, status:" << status; + if (Utils::suspendProcess(suspendedPids, false).size() != suspendedPids.size()) + emit logItem(tr("Could not resume iTunesHelper."), LOGWARNING); +} + + +void BootloaderInstallS5l::abortInstall(void) +{ + LOG_INFO() << "abortInstall"; + aborted = true; + disconnect(this, SIGNAL(installAborted()), this, SLOT(abortInstall())); +} + + +bool BootloaderInstallS5l::abortDetected(void) +{ + if (aborted) { + LOG_ERROR() << "abortDetected"; + emit logItem(tr("%1 aborted by user."). + arg(tr(doInstall ? "Install" : "Uninstall")), LOGERROR); + emit done(true); + return true; + } + return false; +} + + +void BootloaderInstallS5l::setProgress(int progress, int secondsTimeout) +{ + progressTimer.start(); + progressTimeout = secondsTimeout * 1000; + progOrigin = progTarget; + progTarget = progress; + actionShown = false; +} + + +bool BootloaderInstallS5l::updateProgress(void) +{ + if (progressTimeout) { + progCurrent = qMin(progTarget, progOrigin + + progressTimer.elapsed()*(progTarget-progOrigin)/progressTimeout); + } + else { + progCurrent = progTarget; + } + emit logProgress(progCurrent, 100); + QCoreApplication::sendPostedEvents(); + QCoreApplication::processEvents(); + return !abortDetected(); +} + + +BootloaderInstallBase::BootloaderType BootloaderInstallS5l::installed(void) +{ + bool rbblInstalled; + + QString device = Utils::resolveDevicename(m_blfile); + if (device.isEmpty()) { + LOG_INFO() << "installed: BootloaderUnknown"; + return BootloaderUnknown; + } + + // rely on logfile + QString logfile = RbSettings::value(RbSettings::Mountpoint).toString() + + "/.rockbox/rbutil.log"; + QSettings s(logfile, QSettings::IniFormat, this); + QString section = SystemInfo::value( + SystemInfo::CurBootloaderName).toString().section('/', -1); + rbblInstalled = s.contains("Bootloader/" + section); + + if (rbblInstalled) { + LOG_INFO() << "installed: BootloaderRockbox"; + return BootloaderRockbox; + } + else { + LOG_INFO() << "installed: BootloaderOther"; + return BootloaderOther; + } +} + + +BootloaderInstallBase::Capabilities BootloaderInstallS5l::capabilities(void) +{ + return (Install | Uninstall); +} diff --git a/rbutil/rbutilqt/base/bootloaderinstalls5l.h b/rbutil/rbutilqt/base/bootloaderinstalls5l.h new file mode 100644 index 0000000000..a59e5d378b --- /dev/null +++ b/rbutil/rbutilqt/base/bootloaderinstalls5l.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2008 by Dominik Riebeling + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef BOOTLOADERINSTALLS5L_H +#define BOOTLOADERINSTALLS5L_H + +#include +#include "bootloaderinstallbase.h" + + +//! bootloader installation derivate based on mks5lboot +class BootloaderInstallS5l : public BootloaderInstallBase +{ + Q_OBJECT + + public: + BootloaderInstallS5l(QObject *parent); + bool install(void); + bool uninstall(void); + BootloaderInstallBase::BootloaderType installed(void); + Capabilities capabilities(void); + + private slots: + bool installStage1(void); + void installStageMkdfu(void); + void installStageWaitForEject(void); + void installStageWaitForSpindown(void); + void installStageWaitForProcs(void); + void installStageWaitForDfu(void); + void installStageSendDfu(void); + void installStageWaitForRemount(void); + void abortInstall(void); + void installDone(bool); + + private: + bool doInstall; + QString mntpoint; + unsigned char* dfu_buf; + int dfu_size; + QList suspendedPids; + bool aborted; + bool abortDetected(void); + QTime scanTimer; + bool scanSuccess; + // progress + QTime progressTimer; + int progressTimeout; + int progCurrent; + int progOrigin; + int progTarget; + bool actionShown; + void setProgress(int, int=0); + bool updateProgress(void); +}; + +#endif -- cgit v1.2.3