summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/b2qt-flashing-wizard/.gitignore5
-rw-r--r--src/b2qt-flashing-wizard/B2Qt_QtC_icon@2x.pngbin0 -> 2739 bytes
-rw-r--r--src/b2qt-flashing-wizard/README24
-rw-r--r--src/b2qt-flashing-wizard/actor.cpp29
-rw-r--r--src/b2qt-flashing-wizard/actor.h41
-rw-r--r--src/b2qt-flashing-wizard/b2qt-flashing-wizard.pro35
-rw-r--r--src/b2qt-flashing-wizard/b2qt-flashing-wizard.qrc7
-rw-r--r--src/b2qt-flashing-wizard/background.pngbin0 -> 92958 bytes
-rw-r--r--src/b2qt-flashing-wizard/commit_page.cpp157
-rw-r--r--src/b2qt-flashing-wizard/commit_page.h47
-rw-r--r--src/b2qt-flashing-wizard/common.cpp78
-rw-r--r--src/b2qt-flashing-wizard/common.h21
-rw-r--r--src/b2qt-flashing-wizard/device_page.cpp269
-rw-r--r--src/b2qt-flashing-wizard/device_page.h63
-rw-r--r--src/b2qt-flashing-wizard/disk_page.cpp246
-rw-r--r--src/b2qt-flashing-wizard/disk_page.h68
-rw-r--r--src/b2qt-flashing-wizard/elevator.cpp48
-rw-r--r--src/b2qt-flashing-wizard/elevator.h22
-rw-r--r--src/b2qt-flashing-wizard/finish_page.cpp33
-rw-r--r--src/b2qt-flashing-wizard/finish_page.h34
-rw-r--r--src/b2qt-flashing-wizard/introduction_page.cpp33
-rw-r--r--src/b2qt-flashing-wizard/introduction_page.h35
-rw-r--r--src/b2qt-flashing-wizard/logo.pngbin0 -> 2589 bytes
-rw-r--r--src/b2qt-flashing-wizard/main.cpp38
-rw-r--r--src/b2qt-flashing-wizard/mainwindow.cpp64
-rw-r--r--src/b2qt-flashing-wizard/mainwindow.h35
-rw-r--r--src/b2qt-flashing-wizard/platform_page.cpp180
-rw-r--r--src/b2qt-flashing-wizard/platform_page.h52
-rw-r--r--src/b2qt-flashing-wizard/progress_page.cpp90
-rw-r--r--src/b2qt-flashing-wizard/progress_page.h50
-rw-r--r--src/b2qt-flashing-wizard/scriptwriter.cpp148
-rw-r--r--src/b2qt-flashing-wizard/scriptwriter.h52
-rw-r--r--src/b2qt-flashing-wizard/watermark.pngbin0 -> 79148 bytes
-rw-r--r--src/b2qt-update-application/.gitignore4
-rw-r--r--src/b2qt-update-application/b2qt-update-application.pro35
-rw-r--r--src/b2qt-update-application/filewrapper.cpp53
-rw-r--r--src/b2qt-update-application/filewrapper.h40
-rw-r--r--src/b2qt-update-application/main.cpp204
-rw-r--r--src/b2qt-update-application/tar.cpp309
-rw-r--r--src/b2qt-update-application/tar.h99
-rw-r--r--src/b2qt-update-application/update.cpp202
-rw-r--r--src/b2qt-update-application/update.h50
-rw-r--r--src/b2qt-update-util/.gitignore4
-rw-r--r--src/b2qt-update-util/b2qt-update-util.pro23
-rw-r--r--src/b2qt-update-util/main.cpp144
-rw-r--r--src/doc/config/b2qt.qdocconf3
-rw-r--r--src/doc/src/devices/qtee-bd-sl-imx6.qdoc4
-rw-r--r--src/doc/src/devices/qtee-beagleboard-xm.qdoc2
-rw-r--r--src/doc/src/devices/qtee-beaglebone-black.qdoc4
-rw-r--r--src/doc/src/devices/qtee-nexus-7.qdoc4
-rw-r--r--src/doc/src/devices/qtee-raspberry-pi.qdoc2
-rw-r--r--src/doc/src/devices/qtee-sabre-sd-imx6quad.qdoc2
-rw-r--r--src/doc/src/devices/qtee-toradex-apalis.qdoc23
-rw-r--r--src/doc/src/qtee-custom-embedded-linux.qdoc26
-rw-r--r--src/doc/src/qtee-install-guide.qdoc8
-rw-r--r--src/doc/src/qtee-qml-reference.qdoc4
-rw-r--r--src/doppelganger/appops.cpp50
-rw-r--r--src/doppelganger/appops.h29
-rw-r--r--src/doppelganger/doppelganger.pro6
-rw-r--r--src/doppelganger/main.cpp2
-rw-r--r--src/imports/utils/plugin.cpp115
-rw-r--r--src/imports/utils/qmldir4
-rw-r--r--src/imports/utils/utils.pro10
-rw-r--r--src/qt_hw_init/main.cpp28
-rw-r--r--src/qt_hw_init/qt_hw_init.pro4
-rw-r--r--src/src.pro1
-rw-r--r--src/utils/b2qtdevice.cpp (renamed from src/utils/qdroidutils.cpp)373
-rw-r--r--src/utils/b2qtdevice.h59
-rw-r--r--src/utils/qdroidutils.h83
-rw-r--r--src/utils/utils.pro16
-rw-r--r--sync.profile2
71 files changed, 3721 insertions, 314 deletions
diff --git a/src/b2qt-flashing-wizard/.gitignore b/src/b2qt-flashing-wizard/.gitignore
new file mode 100644
index 0000000..9f9ec21
--- /dev/null
+++ b/src/b2qt-flashing-wizard/.gitignore
@@ -0,0 +1,5 @@
+*.o
+Makefile
+b2qt-wand
+qrc_*.cpp
+moc_*.cpp
diff --git a/src/b2qt-flashing-wizard/B2Qt_QtC_icon@2x.png b/src/b2qt-flashing-wizard/B2Qt_QtC_icon@2x.png
new file mode 100644
index 0000000..d407a1f
--- /dev/null
+++ b/src/b2qt-flashing-wizard/B2Qt_QtC_icon@2x.png
Binary files differ
diff --git a/src/b2qt-flashing-wizard/README b/src/b2qt-flashing-wizard/README
new file mode 100644
index 0000000..a04bc96
--- /dev/null
+++ b/src/b2qt-flashing-wizard/README
@@ -0,0 +1,24 @@
+If you want to use this tool outside of the SDK installation directory
+you have to set SDKDIR environment variable.
+
+How to get root permission:
+
+sudo -A /bin/askpass-tool
+
+Sudo will ask when starting scripts
+
+sudo -E ./b2qt-flashing-wizard
+
+-E is needed to keep the X vars
+
+Variables needed:
+
+#!/bin/bash
+export HOME=/home/rkeller
+export XDG_SESSION_COOKIE=0dbc1b47e88f5cfa62c5818b00000237-1381472004.761811-1314725543
+./b2qt-flashing-wizard
+
+GUI stuff:
+
+kdesu
+gsu
diff --git a/src/b2qt-flashing-wizard/actor.cpp b/src/b2qt-flashing-wizard/actor.cpp
new file mode 100644
index 0000000..1cd76bf
--- /dev/null
+++ b/src/b2qt-flashing-wizard/actor.cpp
@@ -0,0 +1,29 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "actor.h"
+
+Actor::Actor(QObject *parent)
+ : QObject(parent)
+{
+}
+
+Actor::~Actor()
+{
+}
diff --git a/src/b2qt-flashing-wizard/actor.h b/src/b2qt-flashing-wizard/actor.h
new file mode 100644
index 0000000..f310f0b
--- /dev/null
+++ b/src/b2qt-flashing-wizard/actor.h
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef ACTOR_H
+#define ACTOR_H
+
+#include <QObject>
+
+class Actor : public QObject
+{
+ Q_OBJECT
+
+public:
+ Actor(QObject *parent);
+ virtual ~Actor();
+ virtual bool ready(QString &) const = 0;
+ virtual void start() = 0;
+
+signals:
+ void progress(const QString &step);
+ void finished();
+ void details(const QByteArray &);
+};
+
+#endif // ACTOR_H
diff --git a/src/b2qt-flashing-wizard/b2qt-flashing-wizard.pro b/src/b2qt-flashing-wizard/b2qt-flashing-wizard.pro
new file mode 100644
index 0000000..05403b9
--- /dev/null
+++ b/src/b2qt-flashing-wizard/b2qt-flashing-wizard.pro
@@ -0,0 +1,35 @@
+QT += widgets
+CONFIG += c++11
+
+SOURCES += \
+ actor.cpp \
+ commit_page.cpp \
+ common.cpp \
+ device_page.cpp \
+ disk_page.cpp \
+ elevator.cpp \
+ finish_page.cpp \
+ introduction_page.cpp \
+ main.cpp \
+ mainwindow.cpp \
+ platform_page.cpp \
+ progress_page.cpp \
+ scriptwriter.cpp \
+
+HEADERS += \
+ actor.h \
+ commit_page.h \
+ common.h \
+ device_page.h \
+ disk_page.h \
+ elevator.h \
+ finish_page.h \
+ introduction_page.h \
+ mainwindow.h \
+ platform_page.h \
+ progress_page.h \
+ scriptwriter.h \
+
+RESOURCES += b2qt-flashing-wizard.qrc
+INSTALLS += target
+target.path = /bin
diff --git a/src/b2qt-flashing-wizard/b2qt-flashing-wizard.qrc b/src/b2qt-flashing-wizard/b2qt-flashing-wizard.qrc
new file mode 100644
index 0000000..1522ca4
--- /dev/null
+++ b/src/b2qt-flashing-wizard/b2qt-flashing-wizard.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource>
+ <file>logo.png</file>
+ <file>background.png</file>
+ <file>watermark.png</file>
+ </qresource>
+</RCC>
diff --git a/src/b2qt-flashing-wizard/background.png b/src/b2qt-flashing-wizard/background.png
new file mode 100644
index 0000000..330b301
--- /dev/null
+++ b/src/b2qt-flashing-wizard/background.png
Binary files differ
diff --git a/src/b2qt-flashing-wizard/commit_page.cpp b/src/b2qt-flashing-wizard/commit_page.cpp
new file mode 100644
index 0000000..e7a3c16
--- /dev/null
+++ b/src/b2qt-flashing-wizard/commit_page.cpp
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "commit_page.h"
+#include "scriptwriter.h"
+#include "progress_page.h"
+#include "mainwindow.h"
+#include <QDebug>
+#include <QVBoxLayout>
+#include <QLabel>
+
+extern QString G_platform;
+extern QString G_version;
+extern QString G_os;
+extern QString G_device;
+extern QString G_board;
+extern QString G_SDKDIR;
+extern QString G_mode;
+QLabel *createErrorLabel(QWidget *parent);
+
+CommitPage::CommitPage(QWidget *parent)
+ : QWizardPage(parent)
+ , mText(new QLabel(this))
+ , mActor(0)
+ , mError(createErrorLabel(this))
+ , mLayout(new QVBoxLayout(this))
+ , mComplete(false)
+{
+ setButtonText(QWizard::CommitButton, "Write");
+ setCommitPage(true);
+ setTitle("Confirm action");
+ setSubTitle("Confirm the action to be done");
+ mLayout->addWidget(mText);
+ mLayout->addSpacerItem(new QSpacerItem(40,40,QSizePolicy::Minimum, QSizePolicy::Expanding));
+ mLayout->addWidget(mError);
+ setLayout(mLayout);
+}
+
+CommitPage::~CommitPage()
+{
+}
+
+bool CommitPage::isComplete() const
+{
+ if (!mComplete)
+ return false;
+
+ QString error;
+ bool result = mActor->ready(error);
+ if (result)
+ mError->clear();
+ else
+ mError->setText(error);
+
+ return result;
+}
+
+void CommitPage::initializePage()
+{
+ qDebug() << "platform:" << G_platform << "version:" << G_version << "os:" << G_os
+ << "device:" << G_device << "board:" << G_board << "mode:" << G_mode;
+ QString text("Write %1-%2 (%3) to device %4.");
+ mText->setText(text.arg(G_platform, G_os, G_version, G_device));
+ mComplete = true;
+
+ qDebug() << "os:" << G_os << "platform:" << G_platform << "device:" << G_device;
+
+ if (G_platform == "generic-4.2" && G_os == "eAndroid" && G_board == "iMX6") {
+ ScriptWriter *i = new ScriptWriter(this);
+ i->setScriptFile(G_SDKDIR + G_version + "/" + G_platform + "-" + G_os + "/images/" + G_board + "/deploy.sh");
+ i->setAdditionalArgs(QStringList() << G_device << "--verbose");
+ i->setEnvironment("VERBOSE","1");
+ mActor = i;
+ } else if (G_platform == "iMX6" && G_os == "eLinux") {
+ ScriptWriter *i = new ScriptWriter(this);
+ i->setScriptFile(G_SDKDIR + G_version + "/" + G_platform + "-" + G_os + "/images/deploy.sh");
+ i->setAdditionalArgs(QStringList() << G_device << "--verbose");
+ i->setEnvironment("VERBOSE","1");
+ mActor = i;
+ } else if (G_platform == "beaglebone" && G_os == "eLinux") {
+ ScriptWriter *i = new ScriptWriter(this);
+ i->setScriptFile(G_SDKDIR + G_version + "/" + G_platform + "-" + G_os + "/images/deploy.sh");
+ i->setAdditionalArgs(QStringList() << G_device << "--verbose");
+ i->setEnvironment("VERBOSE","1");
+ mActor = i;
+ } else if (G_platform == "generic-4.4" && G_os == "eAndroid" && G_board == "beaglebone") {
+ ScriptWriter *i = new ScriptWriter(this);
+ i->setScriptFile(G_SDKDIR + G_version + "/" + G_platform + "-" + G_os + "/images/" + G_board + "/deploy.sh");
+ i->setAdditionalArgs(QStringList() << G_device << "--verbose");
+ i->setEnvironment("VERBOSE","1");
+ mActor = i;
+ } else if (G_platform == "generic-4.4" && G_os == "eAndroid" && G_board == "nexus7v2") {
+ ScriptWriter *i = new ScriptWriter(this);
+ i->setScriptFile(G_SDKDIR + G_version + "/" + G_platform + "-" + G_os + "/images/" + G_board + "/deploy.sh");
+ if (G_mode == "fastboot")
+ i->setAdditionalArgs(QStringList() << "-fastboot");
+ mActor = i;
+ } else if (G_platform == "generic-4.4" && G_os == "eAndroid" && G_board == "nexus7") {
+ ScriptWriter *i = new ScriptWriter(this);
+ i->setScriptFile(G_SDKDIR + G_version + "/" + G_platform + "-" + G_os + "/images/" + G_board + "/deploy.sh");
+ if (G_mode == "fastboot")
+ i->setAdditionalArgs(QStringList() << "-fastboot");
+ mActor = i;
+ } else if (G_platform == "generic-4.2" && G_os == "eAndroid" && G_board == "nexus7") {
+ ScriptWriter *i = new ScriptWriter(this);
+ i->setScriptFile(G_SDKDIR + G_version + "/" + G_platform + "-" + G_os + "/images/" + G_board + "/deploy.sh");
+ if (G_mode == "fastboot")
+ i->setAdditionalArgs(QStringList() << "-fastboot");
+ mActor = i;
+ } else if (G_platform == "raspberrypi" && G_os == "eLinux") {
+ ScriptWriter *i = new ScriptWriter(this);
+ i->setScriptFile(G_SDKDIR + G_version + "/" + G_platform + "-" + G_os + "/images/deploy.sh");
+ i->setAdditionalArgs(QStringList() << G_device << "--verbose");
+ i->setEnvironment("VERBOSE","1");
+ mActor = i;
+ } else if (G_platform == "imx6qsabresd" && G_os == "eLinux") {
+ ScriptWriter *i = new ScriptWriter(this);
+ i->setScriptFile(G_SDKDIR + G_version + "/" + G_platform + "-" + G_os + "/images/deploy.sh");
+ i->setAdditionalArgs(QStringList() << G_device << "--verbose");
+ i->setEnvironment("VERBOSE","1");
+ mActor = i;
+ } else if (G_platform == "apalis-imx6" && G_os == "eLinux") {
+ ScriptWriter *i = new ScriptWriter(this);
+ i->setScriptFile(G_SDKDIR + G_version + "/" + G_platform + "-" + G_os + "/images/deploy.sh");
+ i->setAdditionalArgs(QStringList() << G_device << "--verbose");
+ i->setEnvironment("VERBOSE","1");
+ mActor = i;
+ } else {
+ mError->setText("Unsupported platform combination");
+ mComplete = false;
+ }
+}
+
+bool CommitPage::validatePage()
+{
+ ProgressPage *p = qobject_cast<ProgressPage*>(wizard()->page(MainWindow::Page_Progress));
+ Q_ASSERT(p);
+
+ p->setActor(mActor);
+ return true;
+}
diff --git a/src/b2qt-flashing-wizard/commit_page.h b/src/b2qt-flashing-wizard/commit_page.h
new file mode 100644
index 0000000..e934152
--- /dev/null
+++ b/src/b2qt-flashing-wizard/commit_page.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef COMMIT_PAGE_H
+#define COMMIT_PAGE_H
+
+#include <QWizardPage>
+class QLabel;
+class Actor;
+class QLabel;
+class QVBoxLayout;
+
+class CommitPage : public QWizardPage
+{
+ Q_OBJECT
+public:
+ CommitPage(QWidget *parent = 0);
+ virtual ~CommitPage();
+ void initializePage();
+ bool validatePage();
+ bool isComplete() const;
+
+private:
+ QLabel *mText;
+ Actor *mActor;
+ QLabel *mError;
+ QVBoxLayout *mLayout;
+ bool mComplete;
+};
+
+#endif // COMMIT_PAGE_H
diff --git a/src/b2qt-flashing-wizard/common.cpp b/src/b2qt-flashing-wizard/common.cpp
new file mode 100644
index 0000000..bd95a3c
--- /dev/null
+++ b/src/b2qt-flashing-wizard/common.cpp
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include <QByteArray>
+#include <QFile>
+#include <QPair>
+#include <QDebug>
+
+QByteArray readAll(const QString &fileName)
+{
+ QByteArray rc;
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly)) {
+ qWarning() << "Could not open file:" << fileName;
+ return rc;
+ }
+
+ rc = file.readAll();
+ file.close();
+ return rc;
+}
+
+
+static QList<QPair<QString, QString> > getMounts()
+{
+ QList<QPair<QString, QString> > rc;
+
+ QByteArray ba = readAll("/proc/mounts");
+ foreach (const QString &line, ba.split('\n')) {
+ if (line.isEmpty())
+ continue;
+
+ QStringList token = line.split(' ');
+ if (token.size() < 2) {
+ qWarning() << "Error parsing mounts:" << line;
+ continue;
+ }
+ rc.append(qMakePair(token[0], token[1]));
+ }
+ return rc;
+}
+
+bool checkForDeviceMounted(const QString &device, QString &mountPoint, QString &partition)
+{
+ QList<QPair<QString, QString> > mounts = getMounts();
+ if (mounts.isEmpty()) {
+ qWarning() << "No mounts found";
+ return false;
+ }
+
+ foreach (const auto &item, mounts) {
+ if (item.first.startsWith(device)) {
+ mountPoint = item.second;
+ partition = item.first;
+ return true;
+ }
+ }
+ mountPoint.clear();
+ partition.clear();
+ return false;
+}
+
diff --git a/src/b2qt-flashing-wizard/common.h b/src/b2qt-flashing-wizard/common.h
new file mode 100644
index 0000000..1884532
--- /dev/null
+++ b/src/b2qt-flashing-wizard/common.h
@@ -0,0 +1,21 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+QByteArray readAll(const QString &fileName);
+bool checkForDeviceMounted(const QString &device, QString &mountPoint, QString &partition);
diff --git a/src/b2qt-flashing-wizard/device_page.cpp b/src/b2qt-flashing-wizard/device_page.cpp
new file mode 100644
index 0000000..313ccce
--- /dev/null
+++ b/src/b2qt-flashing-wizard/device_page.cpp
@@ -0,0 +1,269 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "device_page.h"
+#include "common.h"
+#include "mainwindow.h" // for Page_ enum
+#include <QRadioButton>
+#include <QLayout>
+#include <QDebug>
+#include <QDir>
+#include <QListWidget>
+#include <QLabel>
+#include <QProcess>
+#include <QTimer>
+
+extern QString G_SDKDIR;
+extern QString G_device;
+extern QString G_board;
+extern QString G_mode;
+
+QLabel *createErrorLabel(QWidget *parent);
+
+DevicePage::DevicePage(QWidget *parent)
+ : QWizardPage(parent)
+ , mListWidget(new QListWidget(this))
+ , mError(createErrorLabel(this))
+ , mLayout(new QVBoxLayout(this))
+{
+ setTitle("Device selection");
+ setSubTitle("Select a device to be used");
+ mLayout->addWidget(mListWidget);
+ mLayout->addSpacerItem(new QSpacerItem(40,40,QSizePolicy::Minimum, QSizePolicy::Expanding));
+ mLayout->addWidget(mError);
+ setLayout(mLayout);
+ connect(mListWidget, &QListWidget::itemClicked, this, &DevicePage::itemSelected);
+
+ QTimer *timer = new QTimer(this);
+ timer->setInterval(1000);
+ connect(timer, &QTimer::timeout, this, &DevicePage::updateDeviceList);
+ timer->start();
+}
+
+DevicePage::~DevicePage()
+{
+}
+
+QList<DevicePage::DeviceInfo> DevicePage::list() const
+{
+ QList<DeviceInfo> diList;
+
+ QProcess process;
+ process.start(G_SDKDIR + "/Tools/b2qt/adb", QStringList() << "devices" << "-l");
+ process.waitForFinished();
+
+ QList<QByteArray> lines = process.readAll().split('\n');
+ foreach (const QByteArray &ba, lines) {
+ if (ba.startsWith("List of"))
+ continue;
+ if (ba.isEmpty())
+ continue;
+ if (ba.startsWith("192.168.56.101:"))
+ continue;
+ QList<QByteArray> token = ba.simplified().split(' ');
+
+ if (token.size() == 6){
+ DeviceInfo d;
+
+ if (token[5].startsWith("device:")) {
+ QString deviceName = token[5].mid(7);
+ if (deviceName == "grouper")
+ d.name = "Nexus7";
+ else if (deviceName == "flo")
+ d.name = "Nexus7v2";
+ else
+ d.name = deviceName;
+ }
+ d.serial = token[0];
+ d.mode = "adb";
+ if (token[1] == "device")
+ d.state = "ready";
+ else
+ d.state = "not ready";
+ diList.append(d);
+ } else if (token.size() == 3) {
+ DeviceInfo d;
+ d.serial = token[0];
+ d.mode = "adb";
+ d.name = "Unknown device";
+ if (token[1] == "offline")
+ d.state = "not ready";
+ else {
+ qDebug() << "Unknown state:" << token[1];
+ continue;
+ }
+ diList.append(d);
+ } else {
+ qDebug() << "Invalid token count:" << token.size() << token;
+ continue;
+ }
+
+ }
+
+ process.start(G_SDKDIR + "/Tools/b2qt/fastboot", QStringList("devices") << "-l");
+ process.waitForFinished();
+ lines = process.readAll().split('\n');
+ foreach (const QByteArray &ba, lines) {
+ if (ba.isEmpty())
+ continue;
+
+ QList<QByteArray> token = ba.simplified().split(' ');
+
+ if (token.size() != 3) {
+ qDebug() << "Invalid token count:" << token;
+ continue;
+ }
+
+
+ DeviceInfo d;
+ d.serial = token[0];
+
+ QProcess p2;
+ p2.start(G_SDKDIR + "/Tools/b2qt/fastboot", QStringList() << "-s" << d.serial << "getvar" << "product");
+ if (!p2.waitForFinished()) {
+ qDebug() << "Could not get product type";
+ d.name = "Unknown";
+ }
+
+ QString productString = p2.readAllStandardError().split('\n')[0].simplified();
+ if (productString.startsWith("product: ")) {
+ QString productName = productString.mid(9);
+ if (productName == "grouper")
+ d.name = "Nexus7";
+ else if (productName == "flo")
+ d.name = "Nexus7v2";
+ else
+ d.name = "Unknown";
+ } else
+ d.name = "Unknown";
+
+ d.mode = "fastboot";
+ d.state = "ready";
+ diList.append(d);
+ }
+
+ return diList;
+}
+
+bool DevicePage::isComplete() const
+{
+ int index = mListWidget->currentRow();
+ if (index < 0 || index >= mListItems.size()) {
+ return false;
+ }
+
+ return true;
+}
+
+void DevicePage::itemSelected()
+{
+ emit completeChanged();
+}
+
+void DevicePage::updateDeviceList()
+{
+ if (wizard()->currentPage() != this)
+ return;
+
+ QList<DeviceInfo> diList = list();
+
+ QStringList currentDevices;
+
+ foreach (const DeviceInfo &di, diList)
+ currentDevices += di.serial;
+
+ QMutableMapIterator<QString, QListWidgetItem*> iter(mListItems);
+ while (iter.hasNext()) {
+ iter.next();
+ if (!currentDevices.contains(iter.key())) {
+ mDeviceInfo.remove(iter.key());
+ delete iter.value();
+ iter.remove();
+ }
+ }
+
+ foreach (const DeviceInfo &di, diList) {
+ QString text = "<h3>%1</h3><table style=\"margin-left:10px\"><tr><td>Serial number</td><td style=\"padding-left:10\">%2</td></tr><tr><td>Mode</td><td style=\"padding-left:10\">%3</td></tr><tr><td>State</td><td style=\"padding-left:10\">%4</td></tr></table>";
+ text = text.arg(di.name, di.serial, di.mode, di.state);
+
+ mDeviceInfo[di.serial] = di;
+ if (mListItems.contains(di.serial)) {
+ // update existing item
+ QListWidgetItem *item = mListItems.value(di.serial);
+ QLabel *label = qobject_cast<QLabel*>(mListWidget->itemWidget(item));
+ label->setText(text);
+ } else {
+ // new item
+ QListWidgetItem *item = new QListWidgetItem();
+ item->setData(Qt::UserRole, di.serial);
+ QLabel *label = new QLabel;
+ label->setText(text);
+ mListWidget->addItem(item);
+ mListWidget->setItemWidget(item, label);
+ item->setSizeHint(label->size());
+ mListItems.insert(di.serial, item);
+ }
+ }
+
+ if (mListWidget->count() == 0)
+ mError->setText("No suitable device found");
+ else
+ mError->setText("");
+
+ emit completeChanged();
+}
+
+
+void DevicePage::initializePage()
+{
+ mError->clear();
+ updateDeviceList();
+}
+
+bool DevicePage::validatePage()
+{
+ QListWidgetItem *item = mListWidget->currentItem();
+ if (!item) {
+ return false;
+ }
+
+ QString serial = item->data(Qt::UserRole).toString();
+ if (serial.isEmpty()) {
+ return false;
+ }
+
+ if (!mDeviceInfo.contains(serial)) {
+ return false;
+ }
+
+ DeviceInfo deviceInfo = mDeviceInfo[serial];
+ if (deviceInfo.state != "ready") {
+ return false;
+ }
+
+ G_device = deviceInfo.serial;
+ G_board = deviceInfo.name.toLower();
+ G_mode = deviceInfo.mode;
+ return true;
+}
+
+int DevicePage::nextId() const
+{
+ return MainWindow::Page_Commit;
+}
diff --git a/src/b2qt-flashing-wizard/device_page.h b/src/b2qt-flashing-wizard/device_page.h
new file mode 100644
index 0000000..d0f26a2
--- /dev/null
+++ b/src/b2qt-flashing-wizard/device_page.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef DEVICE_PAGE_H
+#define DEVICE_PAGE_H
+
+#include <QWizardPage>
+#include <QMap>
+class QListWidget;
+class QLabel;
+class QVBoxLayout;
+class QListWidgetItem;
+
+class DevicePage : public QWizardPage
+{
+ Q_OBJECT
+public:
+ DevicePage(QWidget *parent = 0);
+ virtual ~DevicePage();
+ bool isComplete() const;
+ void initializePage();
+ bool validatePage();
+ int nextId() const;
+
+private slots:
+ void itemSelected();
+ void updateDeviceList();
+
+private:
+ struct DeviceInfo
+ {
+ QString name;
+ QString serial;
+ QString mode;
+ QString state;
+ };
+ QList<DeviceInfo> list() const;
+
+private:
+ QListWidget *mListWidget;
+ QMap<QString, QListWidgetItem*> mListItems;
+ QMap<QString, DeviceInfo> mDeviceInfo;
+ QLabel *mError;
+ QVBoxLayout *mLayout;
+};
+
+#endif // DEVICE_PAGE_H
diff --git a/src/b2qt-flashing-wizard/disk_page.cpp b/src/b2qt-flashing-wizard/disk_page.cpp
new file mode 100644
index 0000000..c96e347
--- /dev/null
+++ b/src/b2qt-flashing-wizard/disk_page.cpp
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "disk_page.h"
+#include "common.h"
+#include "elevator.h"
+#include "mainwindow.h" // for Page_ enum
+#include <QRadioButton>
+#include <QLayout>
+#include <QDebug>
+#include <QDir>
+#include <QListWidget>
+#include <QLabel>
+#include <QTimer>
+#include <QProcess>
+#include <QMessageBox>
+
+extern QString G_device;
+QLabel *createErrorLabel(QWidget *parent);
+
+DiskPage::DiskPage(QWidget *parent)
+ : QWizardPage(parent)
+ , mListWidget(new QListWidget(this))
+ , mError(createErrorLabel(this))
+ , mLayout(new QVBoxLayout(this))
+{
+ setTitle("Disk selection");
+ setSubTitle("Select a disk to be used");
+ mLayout->addWidget(mListWidget);
+ mLayout->addSpacerItem(new QSpacerItem(40,40,QSizePolicy::Minimum, QSizePolicy::Expanding));
+ mLayout->addWidget(mError);
+ setLayout(mLayout);
+ connect(mListWidget, &QListWidget::itemClicked, this, &DiskPage::itemSelected);
+
+ QTimer *timer = new QTimer(this);
+ timer->setInterval(1000);
+ connect(timer, &QTimer::timeout, this, &DiskPage::updateDeviceList);
+ timer->start();
+
+ // Remember devices already present at startup
+ foreach (const DiskInfo &di, list()) {
+ if (!di.removable)
+ mHiddenDevices += di.path;
+ }
+}
+
+DiskPage::~DiskPage()
+{
+}
+
+QList<DiskPage::DiskInfo> DiskPage::list() const
+{
+ QList<DiskInfo> diList;
+
+ QDir sys("/sys/block");
+
+ foreach (const QString &i, sys.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
+ DiskInfo di;
+
+ if (readAll(sys.absoluteFilePath(i) + "/removable").trimmed() == "1")
+ di.removable = true;
+ else
+ di.removable = false;
+
+ di.logicalBlockSize = readAll(sys.absoluteFilePath(i) + "/queue/logical_block_size").trimmed().toULong();
+ di.blocks = readAll(sys.absoluteFilePath(i) + "/size").trimmed().toULong();
+ di.name = i;
+ di.path = "/dev/" + i;
+
+ checkForDeviceMounted(di.path, di.mountPoint, di.mountPartition);
+
+ diList.append(di);
+ }
+
+ return diList;
+}
+
+bool DiskPage::isComplete() const
+{
+ QListWidgetItem *item = mListWidget->currentItem();
+ if (!item)
+ return false;
+
+ QString path = item->data(Qt::UserRole).toString();
+ if (path.isEmpty())
+ return false;
+ if (!mListItems.contains(path))
+ return false;
+
+ return true;
+}
+
+void DiskPage::itemSelected()
+{
+ emit completeChanged();
+}
+
+void DiskPage::updateDeviceList()
+{
+ if (wizard()->currentPage() != this)
+ return;
+
+ QList<DiskInfo> diList = list();
+
+ QSet<QString> currentDevices;
+
+ { // filter devices by name, size etc.
+ QMutableListIterator<DiskInfo> iter(diList);
+
+ while (iter.hasNext()) {
+ iter.next();
+ const DiskInfo &di = iter.value();
+
+ if (di.logicalBlockSize == 0 || di.blocks == 0 || di.name.startsWith("ram")) {
+ iter.remove();
+ continue;
+ }
+ currentDevices += di.path;
+ }
+ }
+
+ { // Update hidden devices list if a device was removed
+ QMutableSetIterator<QString> iter(mHiddenDevices);
+
+ while (iter.hasNext()) {
+ iter.next();
+ if (!currentDevices.contains(iter.value()))
+ iter.remove();
+ }
+ }
+ currentDevices.subtract(mHiddenDevices);
+
+ // Remove devices from map and UI
+ QMutableMapIterator<QString, QListWidgetItem*> iter(mListItems);
+ while (iter.hasNext()) {
+ iter.next();
+ if (!currentDevices.contains(iter.key())) {
+ mDiskInfo.remove(iter.key());
+ delete iter.value();
+ iter.remove();
+ }
+ }
+
+ foreach (const DiskInfo &di, diList) {
+ if (!currentDevices.contains(di.path))
+ continue;
+
+ QString text = "<h3>%1</h3><table style=\"margin-left:10px\"><tr><td>Size</td><td style=\"padding-left:10\">%2</td></tr><tr><td>Removable</td><td style=\"padding-left:10\">%3</td><tr><td>Mounted</td><td style=\"padding-left:10\">%4</td></tr></table>";
+ double size = di.logicalBlockSize * di.blocks;
+ QString sizeText;
+
+ if (size < 1000) {
+ sizeText = QString::number(size) + QLatin1String(" B");
+ } else if (size < 1000000) {
+ sizeText = QString::number(size / 1000) + QLatin1String(" KB");
+ } else if (size < 1000000000) {
+ sizeText = QString::number(size / 1000000) + QLatin1String(" MB");
+ } else {
+ sizeText = QString::number(qRound(size / 1000 / 1000 / 1000)) + QLatin1String(" GB");
+ }
+
+ text = text.arg(di.name, sizeText, di.removable?QLatin1String("yes"):QLatin1String("no"), di.mountPoint.isEmpty()?"no":di.mountPoint);
+
+ mDiskInfo[di.path] = di;
+ if (mListItems.contains(di.path)) {
+ // update existing item
+ QListWidgetItem *item = mListItems.value(di.path);
+ QLabel *label = qobject_cast<QLabel*>(mListWidget->itemWidget(item));
+ label->setText(text);
+ } else {
+ // new item
+ QListWidgetItem *item = new QListWidgetItem();
+ item->setData(Qt::UserRole, di.path);
+ QLabel *label = new QLabel;
+ label->setText(text);
+ mListWidget->addItem(item);
+ mListWidget->setItemWidget(item, label);
+ item->setSizeHint(label->size());
+ mListItems.insert(di.path, item);
+ }
+ }
+
+ if (mListWidget->count() == 0)
+ mError->setText("No suitable disk device found. Insert device now.");
+ else
+ mError->setText("");
+
+ emit completeChanged();
+}
+
+void DiskPage::initializePage()
+{
+ mError->clear();
+ updateDeviceList();
+}
+
+bool DiskPage::validatePage()
+{
+ QListWidgetItem *item = mListWidget->currentItem();
+ if (!item)
+ return false;
+ QString path = item->data(Qt::UserRole).toString();
+ if (path.isEmpty())
+ return false;
+
+ DiskInfo di = mDiskInfo[path];
+
+ if (!di.mountPoint.isEmpty()) {
+ // Ask the user before unmount
+ if (QMessageBox::Ok != QMessageBox::warning(this, "Unmount action", "The disk you selected is mounted at " + di.mountPoint + ". It will be unmounted now.", QMessageBox::Ok | QMessageBox::Cancel))
+ return false;
+
+ QStringList args = elevate();
+ args << "umount" << di.mountPoint;
+
+ int rc = QProcess::execute(args.takeFirst(), args);
+ if (rc != 0) {
+ QMessageBox::critical(this, "Unmount failed", "Could not umount the disk.");
+ return false;
+ }
+ }
+
+ G_device = path;
+ return true;
+}
+
+int DiskPage::nextId() const
+{
+ return MainWindow::Page_Commit;
+}
diff --git a/src/b2qt-flashing-wizard/disk_page.h b/src/b2qt-flashing-wizard/disk_page.h
new file mode 100644
index 0000000..abc0ba5
--- /dev/null
+++ b/src/b2qt-flashing-wizard/disk_page.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef DISK_PAGE_H
+#define DISK_PAGE_H
+
+#include <QWizardPage>
+#include <QMap>
+#include <QSet>
+class QListWidget;
+class QLabel;
+class QVBoxLayout;
+class QListWidgetItem;
+
+class DiskPage : public QWizardPage
+{
+ Q_OBJECT
+public:
+ DiskPage(QWidget *parent = 0);
+ virtual ~DiskPage();
+ bool isComplete() const;
+ void initializePage();
+ bool validatePage();
+ int nextId() const;
+
+private slots:
+ void itemSelected();
+ void updateDeviceList();
+
+private:
+ struct DiskInfo
+ {
+ QString path;
+ bool removable;
+ QString name;
+ quint64 logicalBlockSize;
+ quint64 blocks;
+ QString mountPoint;
+ QString mountPartition;
+ };
+ QList<DiskInfo> list() const;
+
+private:
+ QListWidget *mListWidget;
+ QMap<QString, QListWidgetItem*> mListItems;
+ QMap<QString, DiskInfo> mDiskInfo;
+ QLabel *mError;
+ QVBoxLayout *mLayout;
+ QSet<QString> mHiddenDevices;
+};
+
+#endif // DISK_PAGE_H
diff --git a/src/b2qt-flashing-wizard/elevator.cpp b/src/b2qt-flashing-wizard/elevator.cpp
new file mode 100644
index 0000000..f4e087a
--- /dev/null
+++ b/src/b2qt-flashing-wizard/elevator.cpp
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "elevator.h"
+
+#include <QFile>
+#include <QDebug>
+#include <QProcess>
+
+QStringList elevate()
+{
+ QStringList rc;
+
+ int trySudo = QProcess::execute("sudo", QStringList() << "-n" << "echo" << "foo");
+ if (trySudo == 0) {
+ qDebug() << "Sudo seems to work";
+ rc << "sudo" << "--";
+ return rc;
+ }
+
+ if (QFile::exists("/usr/bin/kdesu")) {
+ rc << "/usr/bin/kdesu" << "-u" << "root" << "-t" << "--noignorebutton" << "--"; // @ARGS
+ } else if (QFile::exists("/usr/bin/gksu")) {
+ rc << "/usr/bin/gksu" << "-m" << "message" << "-u" << "root" << "--"; // @ARGS
+// } else if (!QFile::exists("/usr/lib/ssh/x11-ssh-askpass")) {
+// // SUDO_ASKPASS = @askpass-tool;
+// qDebug() << "askpass";
+// rc << "sudo" << "-A" << "--"; // @ARGS
+ }
+ return rc;
+}
+
diff --git a/src/b2qt-flashing-wizard/elevator.h b/src/b2qt-flashing-wizard/elevator.h
new file mode 100644
index 0000000..903acc6
--- /dev/null
+++ b/src/b2qt-flashing-wizard/elevator.h
@@ -0,0 +1,22 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include <QStringList>
+
+QStringList elevate();
diff --git a/src/b2qt-flashing-wizard/finish_page.cpp b/src/b2qt-flashing-wizard/finish_page.cpp
new file mode 100644
index 0000000..854f21b
--- /dev/null
+++ b/src/b2qt-flashing-wizard/finish_page.cpp
@@ -0,0 +1,33 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "finish_page.h"
+#include <QLabel>
+
+FinishPage::FinishPage(QWidget *parent)
+ : QWizardPage(parent)
+{
+ setCommitPage(true);
+ setTitle("Finish");
+ new QLabel("Setting up a Boot to Qt device completed.", this);
+}
+
+FinishPage::~FinishPage()
+{
+}
diff --git a/src/b2qt-flashing-wizard/finish_page.h b/src/b2qt-flashing-wizard/finish_page.h
new file mode 100644
index 0000000..a333e7c
--- /dev/null
+++ b/src/b2qt-flashing-wizard/finish_page.h
@@ -0,0 +1,34 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef FINISH_PAGE_H
+#define FINISH_PAGE_H
+
+#include <QWizardPage>
+
+class FinishPage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ FinishPage(QWidget *parent = 0);
+ virtual ~FinishPage();
+};
+
+#endif // FINISH_PAGE_H
diff --git a/src/b2qt-flashing-wizard/introduction_page.cpp b/src/b2qt-flashing-wizard/introduction_page.cpp
new file mode 100644
index 0000000..1417fe7
--- /dev/null
+++ b/src/b2qt-flashing-wizard/introduction_page.cpp
@@ -0,0 +1,33 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "introduction_page.h"
+#include <QLabel>
+
+IntroductionPage::IntroductionPage(QWidget *parent)
+ : QWizardPage(parent)
+{
+ setTitle("Introduction");
+// setSubTitle(" ");
+ new QLabel("This wizard helps you setting up a Boot to Qt device", this);
+}
+
+IntroductionPage::~IntroductionPage()
+{
+}
diff --git a/src/b2qt-flashing-wizard/introduction_page.h b/src/b2qt-flashing-wizard/introduction_page.h
new file mode 100644
index 0000000..43575d6
--- /dev/null
+++ b/src/b2qt-flashing-wizard/introduction_page.h
@@ -0,0 +1,35 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef INTRODUCTION_PAGE_H
+#define INTRODUCTION_PAGE_H
+
+#include <QWizardPage>
+
+class IntroductionPage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ IntroductionPage(QWidget *parent = 0);
+ virtual ~IntroductionPage();
+// void initializePage();
+};
+
+#endif // INTRODUCTION_PAGE_H
diff --git a/src/b2qt-flashing-wizard/logo.png b/src/b2qt-flashing-wizard/logo.png
new file mode 100644
index 0000000..1dfa8dc
--- /dev/null
+++ b/src/b2qt-flashing-wizard/logo.png
Binary files differ
diff --git a/src/b2qt-flashing-wizard/main.cpp b/src/b2qt-flashing-wizard/main.cpp
new file mode 100644
index 0000000..0321e3e
--- /dev/null
+++ b/src/b2qt-flashing-wizard/main.cpp
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+#include <QApplication>
+//#include <QCleanlooksStyle>
+#include <QStyleFactory>
+#include <QDebug>
+#include <QIcon>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+// qDebug() << QStyleFactory::keys();
+ QStyle *style = QStyleFactory::create("Fusion");
+ if (style)
+ app.setStyle(style);
+ app.setWindowIcon(QIcon(":logo.png"));
+ MainWindow mw;
+ mw.show();
+ return app.exec();
+}
diff --git a/src/b2qt-flashing-wizard/mainwindow.cpp b/src/b2qt-flashing-wizard/mainwindow.cpp
new file mode 100644
index 0000000..fe66ed0
--- /dev/null
+++ b/src/b2qt-flashing-wizard/mainwindow.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+#include "introduction_page.h"
+#include "platform_page.h"
+#include "disk_page.h"
+#include "device_page.h"
+#include "commit_page.h"
+#include "progress_page.h"
+#include "finish_page.h"
+#include <QDebug>
+#include <QDir>
+
+QString G_platform; // generic-4.4 / iMX6
+QString G_version; // Boot2Qt version
+QString G_os; // eAndroid / eLinux
+QString G_device; // serial number or SD Card
+QString G_board; // nexus7v2,nexus7, etc...
+QString G_SDKDIR; // install directory
+QString G_mode; // adb or fastboot
+
+MainWindow::MainWindow()
+ : QWizard()
+{
+ setPixmap(QWizard::LogoPixmap, QPixmap(":logo.png"));
+ setPixmap(QWizard::BackgroundPixmap, QPixmap(":background.png"));
+ setPixmap(QWizard::WatermarkPixmap, QPixmap(":watermark.png"));
+ G_SDKDIR = qgetenv("SDKDIR");
+ if (G_SDKDIR.isEmpty())
+ G_SDKDIR = QDir::currentPath();
+ if (!G_SDKDIR.endsWith('/'))
+ G_SDKDIR.append('/');
+
+ setWizardStyle(QWizard::ModernStyle);
+ setPage(Page_Intro, new IntroductionPage);
+ setPage(Page_Platform, new PlatformPage);
+ setPage(Page_Disk, new DiskPage);
+ setPage(Page_Device, new DevicePage);
+ setPage(Page_Commit, new CommitPage);
+ setPage(Page_Progress, new ProgressPage);
+ setPage(Page_Finish, new FinishPage);
+}
+
+MainWindow::~MainWindow()
+{
+}
+
diff --git a/src/b2qt-flashing-wizard/mainwindow.h b/src/b2qt-flashing-wizard/mainwindow.h
new file mode 100644
index 0000000..517520e
--- /dev/null
+++ b/src/b2qt-flashing-wizard/mainwindow.h
@@ -0,0 +1,35 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QWizard>
+
+class MainWindow : public QWizard
+{
+ Q_OBJECT
+
+public:
+ enum {Page_Intro, Page_Platform, Page_Disk, Page_Device, Page_Commit, Page_Progress, Page_Finish};
+ MainWindow();
+ virtual ~MainWindow();
+};
+
+#endif // MAINWINDOW_H
diff --git a/src/b2qt-flashing-wizard/platform_page.cpp b/src/b2qt-flashing-wizard/platform_page.cpp
new file mode 100644
index 0000000..3adf9b9
--- /dev/null
+++ b/src/b2qt-flashing-wizard/platform_page.cpp
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "platform_page.h"
+#include <QRadioButton>
+#include <QLayout>
+#include <QDebug>
+#include <QDir>
+#include <QLabel>
+#include <QSpacerItem>
+#include "mainwindow.h" // Page_ enum
+
+extern QString G_platform;
+extern QString G_version;
+extern QString G_os;
+extern QString G_device;
+extern QString G_board;
+extern QString G_SDKDIR;
+
+QLabel *createErrorLabel(QWidget *parent)
+{
+ QLabel *label = new QLabel(parent);
+ label->setAlignment(Qt::AlignHCenter);
+ label->setWordWrap(true);
+
+ QFont f = label->font();
+ f.setBold(true);
+ label->setFont(f);
+
+ QPalette p = label->palette();
+ p.setColor(QPalette::WindowText, Qt::red);
+ label->setPalette(p);
+
+ return label;
+}
+
+PlatformPage::PlatformPage(QWidget *parent)
+ : QWizardPage(parent)
+ , mError(createErrorLabel(this))
+ , mLayout(new QVBoxLayout(this))
+{
+ setTitle("Platform");
+ setSubTitle("Select a platform to create a disk for");
+ mLayout->addSpacerItem(new QSpacerItem(40,40,QSizePolicy::Minimum, QSizePolicy::Expanding));
+ mLayout->addWidget(mError);
+ setLayout(mLayout);
+}
+
+PlatformPage::~PlatformPage()
+{
+}
+
+bool PlatformPage::isComplete() const
+{
+ QStringList data = buttonData();
+ if (data.isEmpty())
+ return false;
+
+ if (data[0] == "nexus7") {
+ mError->setText("The selected platform is currently not supported.");
+ return false;
+ }
+ if (data[0] == "iMX6" && data[1] == "eAndroid") {
+ mError->setText("The selected platform is currently not supported.");
+ return false;
+ }
+
+ return !data.isEmpty();
+}
+
+void PlatformPage::itemSelected()
+{
+ mError->clear();
+ emit completeChanged();
+}
+
+void PlatformPage::initializePage()
+{
+ mError->clear();
+
+ qDeleteAll(mButtons);
+ mButtons.clear();
+ mButtonData.clear();
+
+ QDir dir(G_SDKDIR);
+ foreach (const QString i, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
+ if (!i.startsWith("Boot2Qt-"))
+ continue;
+
+ QDir dir2(dir.absoluteFilePath(i));
+ foreach (const QString j, dir2.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
+ if (j.startsWith("emulator-"))
+ continue;
+ QStringList token = j.split('-');
+ QString os = token.takeLast();
+ QString name = token.join("-");
+
+ if (os == "eAndroid" && name.startsWith("generic-")) {
+ QString version = token[1];
+ QDir dir3(dir2.absoluteFilePath(j) + "/images");
+ foreach (const QString k, dir3.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
+ if (k == "common")
+ continue;
+
+ if (!QFile::exists(dir3.absoluteFilePath(k) + "/deploy.sh"))
+ continue;
+
+ QRadioButton *button = new QRadioButton;
+ button->setText(k + "-" + version + "-" + os + " (" + i + ")");
+ mLayout->insertWidget(0, button);
+ connect(button, &QRadioButton::toggled, this, &PlatformPage::itemSelected);
+ mButtons.append(button);
+ mButtonData.insert(button, QStringList() << name << os << i << k);
+ }
+
+ } else if (os == "eAndroid" || os == "eLinux") {
+ QRadioButton *button = new QRadioButton;
+ button->setText(j + " (" + i + ")");
+ mLayout->insertWidget(0, button);
+ connect(button, &QRadioButton::toggled, this, &PlatformPage::itemSelected);
+ mButtons.append(button);
+ mButtonData.insert(button, QStringList() << name << os << i << name);
+ }
+ }
+ }
+ if (mButtons.isEmpty()) {
+ mError->setText("No suitable platform found.\nMake sure you have installed at least one hardware platform.");
+ }
+}
+
+QStringList PlatformPage::buttonData() const
+{
+ QStringList data;
+
+ foreach (QRadioButton *button, mButtons) {
+ if (button->isChecked()) {
+ data = mButtonData[button];
+ break;
+ }
+ }
+
+ return data;
+}
+
+bool PlatformPage::validatePage()
+{
+ QStringList data = buttonData();
+
+ G_platform = data[0];
+ G_os = data[1];
+ G_version = data[2];
+ G_board = data[3];
+
+ qDebug() << "Selected:" << G_platform << G_os << G_version << G_board;
+ return true;
+}
+
+int PlatformPage::nextId() const
+{
+ if (G_board.startsWith("nexus7"))
+ return MainWindow::Page_Device;
+ else
+ return MainWindow::Page_Disk;
+}
diff --git a/src/b2qt-flashing-wizard/platform_page.h b/src/b2qt-flashing-wizard/platform_page.h
new file mode 100644
index 0000000..215023a
--- /dev/null
+++ b/src/b2qt-flashing-wizard/platform_page.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef PLATFORM_PAGE_H
+#define PLATFORM_PAGE_H
+
+#include <QWizardPage>
+#include <QMap>
+class QRadioButton;
+class QLabel;
+class QVBoxLayout;
+
+class PlatformPage : public QWizardPage
+{
+ Q_OBJECT
+public:
+ PlatformPage(QWidget *parent = 0);
+ virtual ~PlatformPage();
+ bool isComplete() const;
+ void initializePage();
+ bool validatePage();
+ int nextId() const;
+
+private slots:
+ void itemSelected();
+
+private:
+ QStringList buttonData() const;
+
+ QList<QRadioButton*> mButtons;
+ QMap<QRadioButton*, QStringList> mButtonData;
+ QLabel *mError;
+ QVBoxLayout *mLayout;
+};
+
+#endif // PLATFORM_PAGE_H
diff --git a/src/b2qt-flashing-wizard/progress_page.cpp b/src/b2qt-flashing-wizard/progress_page.cpp
new file mode 100644
index 0000000..7986df9
--- /dev/null
+++ b/src/b2qt-flashing-wizard/progress_page.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "progress_page.h"
+#include "actor.h"
+#include <QDebug>
+#include <QLabel>
+#include <QLayout>
+#include <QPushButton>
+#include <QTextEdit>
+#include <QTimer>
+
+ProgressPage::ProgressPage(QWidget *parent)
+ : QWizardPage(parent)
+ , mProgress(new QLabel(this))
+ , mActor(0)
+ , mFinished(false)
+ , mTextEdit(new QTextEdit(this))
+{
+ setTitle("Disk creation");
+ setSubTitle("Progress of writing the disk");
+ setLayout(new QVBoxLayout(this));
+ mProgress->setText(tr("Starting"));
+ layout()->addWidget(mProgress);
+ QPushButton *button = new QPushButton(this);
+ button->setText("Show details");
+ layout()->addWidget(button);
+ layout()->addWidget(mTextEdit);
+ QPushButton *copy = new QPushButton(this);
+ copy->setText("Copy to clipboard");
+ layout()->addWidget(copy);
+}
+
+ProgressPage::~ProgressPage()
+{
+}
+
+bool ProgressPage::isComplete() const
+{
+ return mFinished;
+}
+
+void ProgressPage::initializePage()
+{
+ Q_ASSERT(mActor);
+ mActor->start();
+}
+
+void ProgressPage::progress(const QString &step)
+{
+ mProgress->setText(step);
+}
+
+void ProgressPage::finished()
+{
+ mFinished = true;
+ emit completeChanged();
+ wizard()->next(); // progress to next page automatically
+}
+
+void ProgressPage::setActor(Actor *actor)
+{
+ Q_ASSERT(actor);
+ mActor = actor;
+ connect(actor, &Actor::finished, this, &ProgressPage::finished);
+ connect(actor, &Actor::details, this, &ProgressPage::addDetails);
+ connect(actor, &Actor::progress, this, &ProgressPage::progress);
+}
+
+void ProgressPage::addDetails(QByteArray newData)
+{
+ newData.replace(0x08 /* backspace */, ' ');
+ mTextEdit->append(QString::fromLocal8Bit(newData));
+}
diff --git a/src/b2qt-flashing-wizard/progress_page.h b/src/b2qt-flashing-wizard/progress_page.h
new file mode 100644
index 0000000..d0a3b79
--- /dev/null
+++ b/src/b2qt-flashing-wizard/progress_page.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef PROGRESS_PAGE_H
+#define PROGRESS_PAGE_H
+
+#include <QWizardPage>
+class QLabel;
+class Actor;
+class QTextEdit;
+
+class ProgressPage : public QWizardPage
+{
+ Q_OBJECT
+public:
+ ProgressPage(QWidget *parent = 0);
+ virtual ~ProgressPage();
+ bool isComplete() const;
+ void initializePage();
+ void setActor(Actor *actor);
+
+public slots:
+ void progress(const QString &step);
+ void finished();
+ void addDetails(QByteArray newData);
+
+private:
+ QLabel *mProgress;
+ Actor *mActor;
+ bool mFinished;
+ QTextEdit *mTextEdit;
+};
+
+#endif // PROGRESS_PAGE_H
diff --git a/src/b2qt-flashing-wizard/scriptwriter.cpp b/src/b2qt-flashing-wizard/scriptwriter.cpp
new file mode 100644
index 0000000..dfe554e
--- /dev/null
+++ b/src/b2qt-flashing-wizard/scriptwriter.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "scriptwriter.h"
+#include <QPair>
+#include <QStringList>
+#include <QFileInfo>
+#include <QDebug>
+#include <QTimer>
+#include <QProcessEnvironment>
+#include <QElapsedTimer>
+#include <sys/stat.h>
+#include "elevator.h"
+#include "common.h"
+
+ScriptWriter::ScriptWriter(QObject *parent)
+ : Actor(parent)
+ , mDebug(false)
+{
+ mProcess.setProcessChannelMode(QProcess::MergedChannels);
+ mDebug = qEnvironmentVariableIsSet("DEBUG");
+}
+
+ScriptWriter::~ScriptWriter()
+{
+}
+
+void ScriptWriter::setScriptFile(const QString &fileName)
+{
+ mScriptName = fileName;
+}
+
+bool ScriptWriter::ready(QString &error) const
+{
+ if (mScriptName.isEmpty()) {
+ error = "File name is empty";
+ return false;
+ }
+
+ QFileInfo file(mScriptName);
+ if (!file.exists()) {
+ error = "File does not exist: " + mScriptName;
+ return false;
+ }
+ if (!file.isFile()) {
+ error = "File " + mScriptName + " is not a file";
+ return false;
+ }
+ if (!file.isReadable()) {
+ error = "File is not readable";
+ return false;
+ }
+ if (!file.isExecutable()) {
+ error = "File is not executable";
+ return false;
+ }
+ if (file.size() == 0) {
+ error = "File is empty";
+ return false;
+ }
+
+ return true;
+}
+
+void ScriptWriter::start()
+{
+ connect(&mProcess, &QProcess::readyReadStandardOutput, this, &ScriptWriter::readOutput);
+ connect(&mProcess, (void (QProcess::*)(QProcess::ProcessError))&QProcess::error, this, &ScriptWriter::processError);
+ connect(&mProcess, (void (QProcess::*)(int, QProcess::ExitStatus))&QProcess::finished, this, &ScriptWriter::processFinished);
+
+ // Due to some random convenience output in the deploy scripts "set -x" has to be used to synchronize
+ QStringList args = elevate() << "/bin/sh" << "-x" << mScriptName << mAdditionalArgs;
+ qDebug() << "Executing" << args;
+
+/* QProcessEnvironment pe ;
+ QString var = qgetenv("XDG_SESSION_COOKIE");
+ pe.insert("XDG_SESSION_COOKIE", var);
+ var = qgetenv("HOME");
+ pe.insert("HOME", var);
+ mProcess.setProcessEnvironment(pe);
+ */
+ mProcess.setProcessEnvironment(QProcessEnvironment::systemEnvironment());
+
+ mProcess.start(args.takeFirst(), args);
+ if (!mProcess.waitForStarted())
+ qFatal("Failed to start script");
+ mProcess.write("y\n");
+}
+
+void ScriptWriter::readOutput()
+{
+ QByteArray ba = mProcess.readAllStandardOutput();
+ QList<QByteArray> baList = ba.split('\n');
+
+ foreach (const QByteArray &line, baList) {
+ if (line.startsWith("-- STEP -- "))
+ emit progress(QString::fromLocal8Bit(line.mid(11).trimmed()));
+ }
+
+ emit details(ba);
+}
+
+void ScriptWriter::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ if (exitStatus != QProcess::NormalExit) {
+ qWarning("process crashed");
+ return;
+ }
+
+ if (exitCode != 0) {
+ qWarning("process failed");
+ return;
+ }
+ emit finished();
+}
+
+void ScriptWriter::processError(QProcess::ProcessError error)
+{
+ qWarning() << "processError" << error;
+}
+
+void ScriptWriter::setEnvironment(const QString &key, const QString &value)
+{
+ QProcessEnvironment env = mProcess.processEnvironment();
+ env.insert(key, value);
+ mProcess.setProcessEnvironment(env);
+}
+
+void ScriptWriter::setAdditionalArgs(const QStringList &args)
+{
+ mAdditionalArgs = args;
+}
diff --git a/src/b2qt-flashing-wizard/scriptwriter.h b/src/b2qt-flashing-wizard/scriptwriter.h
new file mode 100644
index 0000000..a1ed7a3
--- /dev/null
+++ b/src/b2qt-flashing-wizard/scriptwriter.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef SCRIPTWRITER_H
+#define SCRIPTWRITER_H
+
+#include "actor.h"
+#include <QProcess>
+class QElapsedTimer;
+
+class ScriptWriter : public Actor
+{
+ Q_OBJECT
+
+public:
+ ScriptWriter(QObject *parent);
+ virtual ~ScriptWriter();
+ void setScriptFile(const QString &fileName);
+ bool ready(QString &) const;
+ void start();
+ void setEnvironment(const QString &key, const QString &value);
+ void setAdditionalArgs(const QStringList &);
+
+private slots:
+ void readOutput();
+ void processFinished(int, QProcess::ExitStatus);
+ void processError(QProcess::ProcessError);
+
+private:
+ QString mScriptName;
+ QProcess mProcess;
+ QStringList mAdditionalArgs;
+ bool mDebug;
+};
+
+#endif // SCRIPTWRITER_H
diff --git a/src/b2qt-flashing-wizard/watermark.png b/src/b2qt-flashing-wizard/watermark.png
new file mode 100644
index 0000000..3ad314a
--- /dev/null
+++ b/src/b2qt-flashing-wizard/watermark.png
Binary files differ
diff --git a/src/b2qt-update-application/.gitignore b/src/b2qt-update-application/.gitignore
new file mode 100644
index 0000000..d2a78c0
--- /dev/null
+++ b/src/b2qt-update-application/.gitignore
@@ -0,0 +1,4 @@
+Makefile
+b2qt-update-application
+moc_*.cpp
+*.o
diff --git a/src/b2qt-update-application/b2qt-update-application.pro b/src/b2qt-update-application/b2qt-update-application.pro
new file mode 100644
index 0000000..51fc06f
--- /dev/null
+++ b/src/b2qt-update-application/b2qt-update-application.pro
@@ -0,0 +1,35 @@
+CONFIG += c++11
+QT = core network
+SOURCES += \
+ main.cpp \
+ tar.cpp \
+ filewrapper.cpp \
+ update.cpp
+
+HEADERS += \
+ tar.h \
+ filewrapper.h \
+ update.h
+
+LIBS += -lcrypto
+INSTALLS += target
+target.path = /usr/bin
+
+# Find out git hash
+unix:system(which git):HAS_GIT=TRUE
+win32:system(where git.exe):HAS_GIT=TRUE
+contains(HAS_GIT, TRUE) {
+ GIT_HASH=$$system(git log -1 --format=%H)
+ !system(git diff-index --quiet HEAD): GIT_HASH="$$GIT_HASH-dirty"
+ GIT_VERSION=$$system(git describe --tags --exact-match)
+ isEmpty(GIT_VERSION) : GIT_VERSION="unknown"
+} else {
+ GIT_HASH="unknown"
+ GIT_VERSION="unknown"
+}
+
+isEmpty(GIT_VERSION) : error("No suitable tag found")
+isEmpty(GIT_HASH) : error("No hash available")
+
+DEFINES+="GIT_HASH=\\\"$$GIT_HASH\\\""
+DEFINES+="GIT_VERSION=\\\"$$GIT_VERSION\\\""
diff --git a/src/b2qt-update-application/filewrapper.cpp b/src/b2qt-update-application/filewrapper.cpp
new file mode 100644
index 0000000..6b4a759
--- /dev/null
+++ b/src/b2qt-update-application/filewrapper.cpp
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "filewrapper.h"
+#include <QDebug>
+#include <QTimer>
+
+FileWrapper::FileWrapper(const QString &fileName, QObject *parent)
+ : QFile(fileName, parent)
+ , mTimer(new QTimer(this))
+{
+ mTimer->setInterval(50);
+ connect(mTimer, &QTimer::timeout, this, &FileWrapper::emitReadyRead);
+}
+
+FileWrapper::~FileWrapper()
+{
+}
+
+bool FileWrapper::open(OpenMode mode)
+{
+ bool rc = QFile::open(mode);
+ if (rc) {
+ mTimer->start();
+ }
+ return rc;
+}
+
+void FileWrapper::emitReadyRead()
+{
+ if (!atEnd()) {
+ emit readyRead();
+ } else {
+ mTimer->stop();
+ close();
+ }
+}
diff --git a/src/b2qt-update-application/filewrapper.h b/src/b2qt-update-application/filewrapper.h
new file mode 100644
index 0000000..fc8a977
--- /dev/null
+++ b/src/b2qt-update-application/filewrapper.h
@@ -0,0 +1,40 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef FILEWRAPPER_H
+#define FILEWRAPPER_H
+
+#include <QFile>
+class QTimer;
+
+class FileWrapper : public QFile
+{
+ Q_OBJECT
+
+public:
+ FileWrapper(const QString &fileName, QObject *parent = 0);
+ virtual ~FileWrapper();
+ bool open(OpenMode mode);
+
+private:
+ void emitReadyRead();
+ QTimer *mTimer;
+};
+
+#endif // FILEWRAPPER_H
diff --git a/src/b2qt-update-application/main.cpp b/src/b2qt-update-application/main.cpp
new file mode 100644
index 0000000..353edd9
--- /dev/null
+++ b/src/b2qt-update-application/main.cpp
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+#include <QProcess>
+#include <QDebug>
+#include <QFile>
+#include <QDir>
+#include <unistd.h>
+#include <sys/reboot.h>
+#include <QNetworkAccessManager>
+#include <QUrl>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include "update.h"
+#include "filewrapper.h"
+
+#define UPDATE_MOUNTPOINT "/mnt/update"
+
+QStringList mounts;
+
+void cleanup()
+{
+ foreach (const QString &m, mounts) {
+ QProcess::execute("umount", QStringList() << m);
+ }
+ mounts.clear();
+}
+
+void error(const QString &message)
+{
+ fprintf(stderr, "%s\n", message.toLocal8Bit().constData());
+ cleanup();
+ exit(1);
+}
+
+bool execute(const QString &binary, const QStringList &arguments)
+{
+ int rc = QProcess::execute(binary, arguments);
+ if (rc != 0)
+ error("Failed to execute command '" + binary + " " + arguments.join(" "));
+ return rc == 0;
+}
+
+bool mount(const QString &device, const QString &mountpoint, const QString &arguments = QString())
+{
+ QStringList tmp;
+ if (!arguments.isEmpty())
+ tmp << "-o" << arguments;
+
+ if (!execute("mount", QStringList() << tmp << device << mountpoint))
+ return false;
+
+ if (!arguments.contains("remount"))
+ mounts << mountpoint;
+ return true;
+}
+
+QByteArray readAll(const QString &fileName)
+{
+ QFile f(fileName);
+ if (!f.open(QFile::ReadOnly)) {
+ qWarning() << "Could not read" << fileName;
+ return QByteArray();
+ }
+
+ return f.readAll();
+}
+
+bool writeAll(const QString &fileName, const QByteArray &content)
+{
+ QFile f(fileName);
+ if (!f.open(QFile::WriteOnly)) {
+ qWarning() << "Could not write" << fileName;
+ return false;
+ }
+ if (f.write(content) != content.size()) {
+ qWarning() << "write size mismatch";
+ return false;
+ }
+ f.close();
+ return true;
+}
+
+QStringList find_usb_storage()
+{
+ QStringList rc;
+ QDir d("/sys/dev/block");
+
+ foreach (QString s, d.entryList()) {
+ QFileInfo fi(d.absoluteFilePath(s));
+ QString path = fi.canonicalFilePath();
+ if (path.contains("/usb"))
+ rc += path.mid(path.lastIndexOf('/'));
+ }
+ return rc;
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+ printf("Version %s, SHA1 %s\n", GIT_VERSION, GIT_HASH);
+
+ mount(QString(), "/", "remount,rw");
+ execute("mkdir", QStringList() << "-p" << "/mnt/boot");
+ execute("mkdir", QStringList() << "-p" << "/mnt/root");
+ execute("mkdir", QStringList() << "-p" << UPDATE_MOUNTPOINT);
+ mount("/dev/mmcblk0p1", "/mnt/boot", "ro");
+
+ QByteArray update_state = readAll("/mnt/boot/update/state").trimmed();
+ if (update_state.isEmpty())
+ qFatal("Update state is empty");
+
+ if (update_state == "v")
+ qFatal("Update state is 'valid', this should not happen");
+ else if (update_state == "t")
+ qDebug() << "Update state is 'testing', this should not happen";
+ else if (update_state == "u")
+ qDebug() << "Update state is 'update'";
+ else
+ qDebug() << "Unknown update state:" << update_state;
+
+ QStringList usbsticks = find_usb_storage();
+
+ qDebug() << "Found USB storage devices:" << usbsticks;
+
+ bool update_found = false;
+ foreach (QString s, usbsticks) {
+ if (QProcess::execute("mount", QStringList() << "-o" << "ro" << "/dev/" + s << UPDATE_MOUNTPOINT) == 0) {
+ mounts << UPDATE_MOUNTPOINT;
+ qDebug() << "mount successful";
+
+ if (QFile::exists(UPDATE_MOUNTPOINT "/b2qt-update")) {
+ qDebug() << "update found";
+ update_found = true;
+ break;
+ }
+ execute("umount", QStringList() << UPDATE_MOUNTPOINT);
+ mounts.removeAt(mounts.lastIndexOf(UPDATE_MOUNTPOINT));
+ qDebug() << "no update found";
+ } else {
+ qDebug() << "mount of" << s << "failed";
+ }
+ }
+
+ Update update;
+
+ if (update_found) {
+ FileWrapper *fw = new FileWrapper(UPDATE_MOUNTPOINT "/b2qt-update", &update);
+ if (!fw->open(QFile::ReadOnly))
+ qFatal("Failed to open update");
+ qDebug() << "Starting update from USB";
+ update.setDevice(fw);
+ } else {
+ QByteArray update_source = readAll("/mnt/boot/update/source").trimmed();
+ if (update_source.isEmpty()) {
+ execute("umount", QStringList() << "/mnt/boot"); // FIXME
+ mounts.removeAt(mounts.lastIndexOf("/mnt/boot"));
+ qFatal("Update source is empty");
+ }
+
+ execute("udhcpc", QStringList() << "-i" << "eth0");
+
+ QNetworkAccessManager *manager = new QNetworkAccessManager(0);
+ QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(update_source)));
+ reply->setReadBufferSize(2000000); // 2 MB
+
+ QObject::connect(reply, (void (QNetworkReply::*)(QNetworkReply::NetworkError))&QNetworkReply::error,
+ [](QNetworkReply::NetworkError e){qDebug() << "network error" << e;});
+ QObject::connect(reply, &QNetworkReply::sslErrors,
+ [](QList<QSslError>){ qDebug() << "ssl errors";});
+
+ qDebug() << "Starting update from Internet" << update_source;
+ update.setDevice(reply);
+ }
+
+ app.exec();
+
+ qDebug() << "unmount";
+ cleanup();
+
+ qDebug() << "sync";
+ sync();
+ qDebug() << "reboot; waiting 2 seconds";
+ sleep(2);
+ reboot(RB_AUTOBOOT);
+ return 0;
+}
diff --git a/src/b2qt-update-application/tar.cpp b/src/b2qt-update-application/tar.cpp
new file mode 100644
index 0000000..c0369ff
--- /dev/null
+++ b/src/b2qt-update-application/tar.cpp
@@ -0,0 +1,309 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "tar.h"
+#include <QProcess>
+#include <QDebug>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+
+static bool isZeroed(const char *ptr, unsigned int size)
+{
+ while (size) {
+ if (*ptr) {
+ return false;
+ }
+ ++ptr;
+ --size;
+ }
+ return true;
+}
+
+Tar::Tar(QIODevice *source)
+ : QObject(source)
+ , mSource(source)
+ , mSize(0)
+ , mRemainingSize(0)
+ , mRemainingFileBytes(0)
+ , mProcess(0)
+ , mState(WaitForHeader)
+ , x509(0)
+ , mdctx(0)
+{
+ // Initialize table with all digests in order to look them up by string later
+ OpenSSL_add_all_digests();
+
+ connect(source, &QIODevice::readyRead, this, &Tar::dataIncoming);
+ connect(source, &QIODevice::aboutToClose, this, &Tar::aboutToClose);
+}
+
+Tar::~Tar()
+{
+ if (x509)
+ X509_free(x509);
+ x509 = 0;
+ if (mdctx)
+ EVP_MD_CTX_destroy(mdctx);
+ mdctx = 0;
+}
+
+const QByteArray &Tar::currentContent() const
+{
+ return mContent;
+}
+
+unsigned long Tar::currentSize() const
+{
+ return mSize;
+}
+
+QString Tar::currentFileName() const
+{
+ if (mHeader.name[sizeof(mHeader.name)-1])
+ return QString::fromLatin1(mHeader.name, sizeof(mHeader.name));
+ else
+ return QString::fromLatin1(mHeader.name);
+}
+
+void Tar::dataIncoming()
+{
+ while (mSource->bytesAvailable() > 512) {
+
+ if (mState == WaitForHeader) {
+ if (mSource->bytesAvailable() < 512)
+ return;
+ if (mSource->read((char*)&mHeader, sizeof(mHeader)) != sizeof(mHeader))
+ qFatal("Tar read error");
+ if (isZeroed((char*)&mHeader, sizeof(mHeader))) {
+ qDebug() << "Zero Tar block";
+ qDebug() << "Bytes left" << mSource->bytesAvailable();
+ mSource->close();
+ return;
+ }
+ if (mHeader.name[0] == 0)
+ qFatal("Header starts with 0");
+
+ mSize = QString::fromLatin1(mHeader.size, sizeof(mHeader.size)-1).toULongLong(0, 8);
+ mRemainingFileBytes = mSize;
+ mRemainingSize = mSize;
+ if (mSize % 512) // padding to 512 byte block
+ mRemainingSize += 512 - (mSize % 512);
+
+ if (mSize == 0) {
+ qFatal("Size of file is 0");
+ }
+
+ mState = WaitForDecision;
+ qDebug() << "Started file" << currentFileName() << mSize;
+ if (x509)
+ setupMDContext();
+ emit startingFile(currentFileName());
+ } else if (mState == WaitForDecision) {
+ return;
+ } else if (mState == Extract) {
+ QByteArray ba;
+
+ quint64 bytesToHandle = mRemainingSize;
+
+ if (mSource->bytesAvailable() < mRemainingSize)
+ bytesToHandle = mSource->bytesAvailable();
+ if (bytesToHandle > 60000)
+ bytesToHandle = 60000;
+ ba = mSource->read(bytesToHandle);
+
+ updateMDContext(ba);
+ mProcess->write(ba);
+ mProcess->waitForBytesWritten();
+ mRemainingSize -= ba.size();
+ // do not return
+ } else if (mState == Receive) {
+ QByteArray ba;
+
+ if (mSource->bytesAvailable() > mRemainingSize)
+ ba = mSource->read(mRemainingSize);
+ else
+ ba = mSource->readAll();
+ updateMDContext(ba);
+ mContent += ba;
+ mRemainingSize -= ba.size();
+ if (mContent.size() > mSize)
+ mContent.resize(mSize);
+ // do not return
+ } else if (mState == Skip) {
+ QByteArray ba;
+
+ if (mSource->bytesAvailable() > mRemainingSize) {
+ ba = mSource->read(mRemainingSize);
+ } else {
+ ba = mSource->readAll();
+ }
+ updateMDContext(ba);
+ mRemainingSize -= ba.size();
+ // drop data
+ // do not return
+ } else if (mState == WaitForContinue) {
+ return;
+ } else {
+ qFatal("Unknown tar state");
+ }
+
+ if (mState != WaitForContinue && mState != WaitForHeader && mRemainingSize == 0) {
+ if (mState == Extract) {
+ qDebug() << "End tar process";
+ // please tar
+ // it needs 2 zeroed blocks
+ char buffer[sizeof(mHeader)];
+ memset(buffer, 0, sizeof(buffer));
+ mProcess->write(buffer, sizeof(buffer));
+ mProcess->write(buffer, sizeof(buffer));
+ mProcess->waitForBytesWritten();
+ mProcess->closeWriteChannel();
+ mProcess->waitForFinished(-1);
+ mProcess->deleteLater();
+ mProcess = 0;
+ }
+ mState = WaitForContinue;
+
+ emit(endingFile(currentFileName()));
+ }
+
+ }
+}
+
+void Tar::extractContent(const QString &targetDir)
+{
+ if (mState == WaitForDecision) {
+ qDebug() << Q_FUNC_INFO;
+ mState = Extract;
+ delete mProcess;
+ mProcess = new QProcess(this);
+ mProcess->setProcessChannelMode(QProcess::ForwardedChannels);
+ mProcess->start("tar", QStringList() << "xvf" << "-" << "-C" << targetDir);
+
+ // if we want to extract a file directly, the header needs to be passed to tar
+ if (!currentFileName().endsWith(".tar"))
+ mProcess->write((const char*)&mHeader, sizeof(mHeader));
+ }
+}
+
+void Tar::receiveContent()
+{
+ if (mState == WaitForDecision)
+ mState = Receive;
+}
+
+void Tar::skipContent()
+{
+ if (mState == WaitForDecision)
+ mState = Skip;
+}
+
+void Tar::continueContent()
+{
+ if (mState == WaitForContinue) {
+ mState = WaitForHeader;
+ mSize = 0;
+ mContent.clear();
+ delete mProcess;
+ mProcess = 0;
+ } else
+ qDebug() << "Called" << Q_FUNC_INFO << "in wrong state";
+}
+
+void Tar::aboutToClose()
+{
+ if (mState != WaitForHeader) {
+ qWarning() << "unexpected close";
+ }
+ emit finished();
+}
+
+bool Tar::setVerificationData(const QString &certificateFileName, const QString &mdType)
+{
+ if (x509)
+ qFatal("Setting key twice");
+
+ // Load key from file
+ BIO *i = BIO_new(BIO_s_file());
+ BIO *o = BIO_new_fp(stdout,BIO_NOCLOSE);
+
+ if ((BIO_read_filename(i, certificateFileName.toLatin1().constData()) <= 0) ||
+ ((x509 = PEM_read_bio_X509_AUX(i, NULL, NULL, NULL)) == NULL)) {
+ qWarning() << "Invalid certificate, failed to read file" << certificateFileName;
+ return false;
+ }
+ // Showing used certificate
+ X509_print_ex(o, x509, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+ BIO_free(i);
+ BIO_free(o);
+ qDebug() << "Using verification key:" << certificateFileName;
+
+ md = EVP_get_digestbyname(mdType.toLatin1().constData());
+ if (md == NULL)
+ qFatal("Digest not found");
+ return true;
+}
+
+void Tar::setupMDContext()
+{
+ if (mdctx)
+ EVP_MD_CTX_destroy(mdctx);
+ if (!(mdctx = EVP_MD_CTX_create()))
+ qFatal("EVP_MD_CTX_create failed");
+
+ EVP_PKEY *key = X509_get_pubkey(x509);
+ if (key == NULL) {
+ qFatal("X509_get_pubkey failed");
+ }
+
+ if (md == NULL)
+ qFatal("No digest set");
+
+ if (1 != EVP_DigestVerifyInit(mdctx, NULL, md, NULL, key))
+ qFatal("EVP_DigestVerifyInit failed");
+}
+
+void Tar::updateMDContext(QByteArray data)
+{
+ if (mRemainingFileBytes == 0)
+ qFatal("Trying to update MD context when no more bytes are expected");
+
+ if (mRemainingFileBytes < data.size())
+ data = data.left(mRemainingFileBytes);
+ mRemainingFileBytes -= data.size();
+
+ if (!mdctx)
+ return;
+
+ if (1 != EVP_DigestVerifyUpdate(mdctx, data.constData(), data.size()))
+ qFatal("EVP_DigestVerifyUpdate failed");
+}
+
+bool Tar::checkSignature(const QByteArray &signature)
+{
+ return 1 == EVP_DigestVerifyFinal(mdctx, (unsigned char*)signature.constData(), signature.size());
+}
+
+bool Tar::verifyCurrentContent(const QByteArray &signature)
+{
+ setupMDContext();
+ if (1 != EVP_DigestVerifyUpdate(mdctx, mContent.constData(), mContent.size()))
+ qFatal("EVP_DigestVerifyUpdate failed");
+ return checkSignature(signature);
+}
diff --git a/src/b2qt-update-application/tar.h b/src/b2qt-update-application/tar.h
new file mode 100644
index 0000000..e3ca1d2
--- /dev/null
+++ b/src/b2qt-update-application/tar.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef TAR_H
+#define TAR_H
+
+#include <QObject>
+#include <openssl/x509.h>
+class QIODevice;
+class QProcess;
+#define USTAR_HEADER_SIZE 512
+
+struct ustar_header
+{
+char name[100]; /* File name. Null-terminated if room. */
+char mode[8]; /* Permissions as octal string. */
+char uid[8]; /* User ID as octal string. */
+char gid[8]; /* Group ID as octal string. */
+char size[12]; /* File size in bytes as octal string. */
+char mtime[12]; /* Modification time in seconds from Jan 1, 1970, as octal string. */
+char chksum[8]; /* Sum of octets in header as octal string. */
+char typeflag; /* An enum ustar_type value. */
+char linkname[100]; /* Name of link target. Null-terminated if room. */
+char magic[6]; /* "ustar\0" */
+char version[2]; /* "00" */
+char uname[32]; /* User name, always null-terminated. */
+char gname[32]; /* Group name, always null-terminated. */
+char devmajor[8]; /* Device major number as octal string. */
+char devminor[8]; /* Device minor number as octal string. */
+char prefix[155]; /* Prefix to file name. Null-terminated if room. */
+char padding[12]; /* Pad to 512 bytes. */
+} __attribute__((packed));
+
+class Tar : public QObject
+{
+ Q_OBJECT
+
+public:
+ Tar(QIODevice *source);
+ virtual ~Tar();
+
+ const QByteArray &currentContent() const;
+ unsigned long currentSize() const;
+ QString currentFileName() const;
+
+ enum State { WaitForHeader, WaitForDecision, Extract, Receive, Skip, WaitForContinue};
+ bool setVerificationData(const QString &certificateFileName, const QString &md);
+ bool checkSignature(const QByteArray &signature);
+ bool verifyCurrentContent(const QByteArray &signature);
+
+signals:
+ void startingFile(const QString &name);
+ void endingFile(const QString &name);
+ void finished();
+
+public slots:
+ void extractContent(const QString &targetDir);
+ void receiveContent();
+ void skipContent();
+ void dataIncoming();
+ void continueContent();
+
+private slots:
+ void aboutToClose();
+
+private:
+ void setupMDContext();
+ void updateMDContext(QByteArray);
+
+ QIODevice *mSource;
+ qint64 mSize;
+ qint64 mRemainingSize; // File Size + Padding
+ qint64 mRemainingFileBytes; // File Size without Padding
+ ustar_header mHeader;
+ QByteArray mContent;
+ QProcess *mProcess;
+ State mState;
+ X509 *x509;
+ EVP_MD_CTX *mdctx;
+ const EVP_MD *md;
+};
+
+#endif // TAR_H
diff --git a/src/b2qt-update-application/update.cpp b/src/b2qt-update-application/update.cpp
new file mode 100644
index 0000000..79fea64
--- /dev/null
+++ b/src/b2qt-update-application/update.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "update.h"
+#include "tar.h"
+#include <QDebug>
+#include <QCoreApplication>
+#include <QProcess>
+#include <QFile>
+
+extern QStringList mounts;
+bool writeAll(const QString &fileName, const QByteArray &content);
+static QStringList allowedFiles;
+QByteArray readAll(const QString &fileName);
+bool execute(const QString &binary, const QStringList &arguments);
+bool mount(const QString &device, const QString &mountpoint, const QString &arguments = QString());
+
+QMap<QString,QString> parseMetaInfo(const QByteArray &data)
+{
+ QMap<QString,QString> rc;
+ QList<QByteArray> list = data.split('\n');
+ foreach (const QByteArray &ba, list) {
+ if (ba.isEmpty())
+ continue;
+ int i = ba.indexOf(':');
+ if (i < 0 || i == ba.size())
+ qFatal("Invalid meta.info content: No colon found or no data after colon");
+
+ QString key = ba.left(i);
+ QString value = ba.mid(i+1).trimmed();
+
+ if (rc.contains(key))
+ qFatal("Invalid meta.info content: Same key given twice");
+ rc.insert(key, value);
+ }
+ if (rc.isEmpty())
+ qFatal("Invalid meta.info content: No keys given");
+ return rc;
+}
+
+Update::Update(QObject *parent)
+ : QObject(parent)
+ , mTar(0)
+ , mSource(0)
+{
+ allowedFiles << "key.info" << "meta.info" << "keys.tar" << "uImage" << "rootfs.tar";
+}
+
+Update::~Update()
+{
+}
+
+//# openssl dgst -sha256 -sign priv.pem file.txt > file.txt.sig
+//# openssl dgst -sha256 -verify public.pem -signature file.txt.sig file.txt
+
+void Update::setDevice(QIODevice *source)
+{
+ if (!source) {
+ qFatal("Source device is NULL");
+ return;
+ }
+
+ if (mSource) {
+ qFatal("Source already set");
+ return;
+ }
+
+ mSource = source;
+ mTar = new Tar(mSource);
+
+ connect(mTar, &Tar::startingFile, this, &Update::tarStartingFile);
+ connect(mTar, &Tar::endingFile, this, &Update::tarEndingFile);
+ connect(mTar, &Tar::finished, this, &Update::tarFinished);
+}
+
+void Update::tarStartingFile(const QString &name)
+{
+ if (name == "key.info") {
+ mTar->receiveContent();
+ } else if (name.endsWith(".sig")) {
+ mTar->receiveContent();
+ mCurrentFile = name;
+ mCurrentFile.chop(4);
+
+ bool found = false;
+ while (!allowedFiles.isEmpty()) {
+ QString n = allowedFiles.takeFirst();
+ if (n == mCurrentFile) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ qFatal("File not allowed");
+
+ } else if (mCurrentFile != name) {
+ qFatal("Invalid file order");
+ } else {
+ if (mCurrentFile == "meta.info") {
+ mTar->receiveContent();
+ } else if (mCurrentFile == "uImage") {
+ mount(QString(), "/mnt/boot", "remount,rw");
+ mTar->extractContent("/mnt/boot");
+ } else if (mCurrentFile == "rootfs.tar") {
+ qDebug() << "Formatting rootfs";
+ execute("mkfs." + mMetaInfo["filesystemType"], QStringList() << mMetaInfo["rootDevice"]);
+ mount(mMetaInfo["rootDevice"], "/mnt/root");
+ mTar->extractContent("/mnt/root");
+ } else if (mCurrentFile == "keys.tar") {
+ mount(QString(), "/mnt/boot", "remount,rw");
+ execute("rm", QStringList() << "-r" << "/mnt/boot/update/keys/");
+ execute("mkdir", QStringList() << "/mnt/boot/update/keys/");
+ mTar->extractContent("/mnt/boot/update/keys");
+ } else {
+ qFatal("Unexpected file in update data");
+ }
+ }
+}
+
+void Update::tarEndingFile(const QString &name)
+{
+ if (name == "meta.info") {
+ QMap<QString,QString> map = parseMetaInfo(mTar->currentContent());
+
+ // Some rough sanity checks
+ if (!map.contains("version"))
+ qFatal("No version information found" );
+ if (map["version"] != "1")
+ qFatal("Invalid update version");
+ if (!map.contains("platform") || map["platform"].isEmpty())
+ qFatal("Platform information missing");
+ if (map["platform"] != readAll("/mnt/boot/update/platform").trimmed())
+ qFatal("Invalid platform information");
+ else
+ qDebug() << "Platform matches";
+ if (!map.contains("rootDevice") || map["rootDevice"].isEmpty())
+ qFatal("Root device information missing");
+ if (!QFile::exists(map["rootDevice"]))
+ qFatal("Root device not found");
+ if (!map.contains("key") || map["key"].isEmpty() || map["key"].contains('/'))
+ qFatal("Invalid or no key fingerprint");
+ if (!map.contains("digest") || map["digest"].isEmpty())
+ qFatal("Invalid digest type");
+ if (!map.contains("filesystemType") || map["filesystemType"].isEmpty() || map["filesystemType"].contains(' ') || map["filesystemType"].contains('/'))
+ qFatal("Invalid filesystem type");
+
+ mMetaInfo = map;
+
+ if (!mTar->setVerificationData("/mnt/boot/update/keys/" + mMetaInfo["key"], mMetaInfo["digest"]))
+ qFatal("Loading of certificate failed");
+
+ // Now verify the meta.info itself
+ if (mTar->verifyCurrentContent(mCurrentSignature)) {
+ qDebug() << "meta.info VERIFIED OK";
+ } else {
+ qDebug() << "meta.info VERIFICATION FAILED";
+ qFatal("VERIFICATION FAILED");
+ return;
+ }
+
+ } else if (name.endsWith(".sig")) {
+ mCurrentSignature = mTar->currentContent();
+ qDebug() << "Received signature with" << mCurrentSignature.size() << "bytes.";
+ } else {
+ if (mTar->checkSignature(mCurrentSignature)) {
+ qDebug() << "VERIFIED OK";
+ // FIXME: do something here
+ } else {
+ qDebug() << "VERIFICATION FAILED";
+ qFatal("VERIFICATION FAILED");
+ }
+ }
+
+ mTar->continueContent();
+}
+
+void Update::tarFinished()
+{
+ qDebug() << "Update finished";
+ mount(QString(), "/mnt/boot", "remount,rw");
+ if (!writeAll("/mnt/boot/update/state", "t"))
+ qFatal("Could not set state to testing");
+ mount(QString(), "/mnt/boot", "remount,ro");
+ qApp->exit();
+}
diff --git a/src/b2qt-update-application/update.h b/src/b2qt-update-application/update.h
new file mode 100644
index 0000000..830be56
--- /dev/null
+++ b/src/b2qt-update-application/update.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef UPDATE_H
+#define UPDATE_H
+
+#include <QObject>
+#include <QMap>
+class Tar;
+class QIODevice;
+
+class Update : public QObject
+{
+ Q_OBJECT
+
+public:
+ Update(QObject *parent = 0);
+ virtual ~Update();
+ void setDevice(QIODevice *source);
+
+private slots:
+ void tarStartingFile(const QString &name);
+ void tarEndingFile(const QString &name);
+ void tarFinished();
+
+private:
+ Tar *mTar;
+ QIODevice *mSource;
+ QString mCurrentFile;
+ QByteArray mCurrentSignature;
+ QMap<QString, QString> mMetaInfo;
+};
+
+#endif // UPDATE_H
diff --git a/src/b2qt-update-util/.gitignore b/src/b2qt-update-util/.gitignore
new file mode 100644
index 0000000..67f0098
--- /dev/null
+++ b/src/b2qt-update-util/.gitignore
@@ -0,0 +1,4 @@
+Makefile
+b2qt-update-util
+*.o
+moc_*.cpp
diff --git a/src/b2qt-update-util/b2qt-update-util.pro b/src/b2qt-update-util/b2qt-update-util.pro
new file mode 100644
index 0000000..0b5ab32
--- /dev/null
+++ b/src/b2qt-update-util/b2qt-update-util.pro
@@ -0,0 +1,23 @@
+QT = core
+SOURCES = main.cpp
+INSTALLS += target
+target.path = /usr/bin
+
+# Find out git hash
+unix:system(which git):HAS_GIT=TRUE
+win32:system(where git.exe):HAS_GIT=TRUE
+contains(HAS_GIT, TRUE) {
+ GIT_HASH=$$system(git log -1 --format=%H)
+ !system(git diff-index --quiet HEAD): GIT_HASH="$$GIT_HASH-dirty"
+ GIT_VERSION=$$system(git describe --tags --exact-match)
+ isEmpty(GIT_VERSION) : GIT_VERSION="unknown"
+} else {
+ GIT_HASH="unknown"
+ GIT_VERSION="unknown"
+}
+
+isEmpty(GIT_VERSION) : error("No suitable tag found")
+isEmpty(GIT_HASH) : error("No hash available")
+
+DEFINES+="GIT_HASH=\\\"$$GIT_HASH\\\""
+DEFINES+="GIT_VERSION=\\\"$$GIT_VERSION\\\""
diff --git a/src/b2qt-update-util/main.cpp b/src/b2qt-update-util/main.cpp
new file mode 100644
index 0000000..137cbd5
--- /dev/null
+++ b/src/b2qt-update-util/main.cpp
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+#include <sys/reboot.h>
+#include <QFile>
+#include <QDebug>
+#include <unistd.h>
+#include <QStringList>
+#include <QProcess>
+
+bool mount_boot()
+{
+ // In some cases the boot partition is already mounted somewhere else.
+ // Mounting it again is no problem but data loss will happen.
+ QProcess::execute("umount", QStringList() << "/dev/mmcblk0p1"); // Ignore return value
+ return QProcess::execute("mount", QStringList() << "/dev/mmcblk0p1" << "/boot") == 0;
+}
+
+bool umount_boot()
+{
+ return QProcess::execute("umount", QStringList() << "/boot") == 0;
+}
+
+bool finish_update()
+{
+ if (!mount_boot()) {
+ qWarning() << "Could not mount /boot";
+ return false;
+ }
+ QFile f("/boot/update/state");
+ if (!f.open(QFile::WriteOnly)) {
+ qWarning() << "Could not open file for writing";
+ return false;
+ }
+ if (f.write("v") != 1) {
+ qWarning() << "Write error";
+ return false;
+ }
+ fsync(f.handle());
+ f.close();
+ if (!umount_boot()) {
+ qWarning() << "Could not unmount /boot";
+ return false;
+ }
+ return true;
+}
+
+int start_update(const QString &source)
+{
+ if (!mount_boot()) {
+ qWarning() << "Could not mount /boot";
+ return false;
+ }
+ {
+ QFile f("/boot/update/source");
+ if (!f.open(QFile::WriteOnly)) {
+ qWarning() << "Could not open file for writing";
+ return false;
+ }
+ QByteArray ba = source.toUtf8();
+
+ if (f.write(ba) != ba.size()) {
+ qWarning() << "Write error";
+ return false;
+ }
+ fsync(f.handle());
+ f.close();
+ }
+
+ {
+ QFile f("/boot/update/state");
+ if (!f.open(QFile::WriteOnly)) {
+ qWarning() << "Could not open file for writing";
+ return false;
+ }
+ if (f.write("u") != 1) {
+ qWarning() << "Write error";
+ return false;
+ }
+ fsync(f.handle());
+ f.close();
+ }
+ if (!umount_boot()) {
+ qWarning() << "Could not unmount /boot";
+ return false;
+ }
+ reboot(RB_AUTOBOOT);
+ return true;
+}
+
+void usage()
+{
+ fprintf(stderr,
+ "b2qt-update [start|finish] [...]\n"
+ " start: For internet update provide a http URL as parameter\n"
+ " finish: An update\n"
+ );
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+ QStringList args = app.arguments();
+
+ args.removeFirst();
+ if (args.size() == 0) {
+ usage();
+ return 1;
+ }
+
+ QString arg = args.takeFirst();
+
+ if (arg == "finish")
+ return finish_update();
+ else if (arg == "start") {
+ if (args.size() == 0)
+ return start_update(QString());
+ else
+ return start_update(args.takeFirst());
+ } else if (arg == "version") {
+ printf("Version %s, SHA1 %s\n", GIT_VERSION, GIT_HASH);
+ return 0;
+ } else {
+ usage();
+ return 1;
+ }
+}
diff --git a/src/doc/config/b2qt.qdocconf b/src/doc/config/b2qt.qdocconf
index 33f4e7d..1e499dc 100644
--- a/src/doc/config/b2qt.qdocconf
+++ b/src/doc/config/b2qt.qdocconf
@@ -9,7 +9,8 @@ description = Qt Enterprise Embedded Documentation
version = 3.2.0
sourcedirs = ../src \
- ../../imports/wifi
+ ../../imports/wifi \
+ ../../imports/utils
headerdirs = ../../imports/wifi
diff --git a/src/doc/src/devices/qtee-bd-sl-imx6.qdoc b/src/doc/src/devices/qtee-bd-sl-imx6.qdoc
index 54e4b64..f81876c 100644
--- a/src/doc/src/devices/qtee-bd-sl-imx6.qdoc
+++ b/src/doc/src/devices/qtee-bd-sl-imx6.qdoc
@@ -85,12 +85,12 @@
\li \b{\B2QA}
\badcode
cd <INSTALL_DIR>
- ./Boot2Qt-3.x/generic-4.2-eAndroid/images/iMX6/deploy.sh /dev/<device_name>
+ ./Boot2Qt-4.x/generic-4.2-eAndroid/images/iMX6/deploy.sh /dev/<device_name>
\endcode
\li \b{\B2QL}
\badcode
cd <INSTALL_DIR>
- sudo ./Boot2Qt-3.x/iMX6-eLinux/images/deploy.sh /dev/<device_name>
+ sudo ./Boot2Qt-4.x/iMX6-eLinux/images/deploy.sh /dev/<device_name>
\endcode
\endlist
diff --git a/src/doc/src/devices/qtee-beagleboard-xm.qdoc b/src/doc/src/devices/qtee-beagleboard-xm.qdoc
index 3d54c15..cee898c 100644
--- a/src/doc/src/devices/qtee-beagleboard-xm.qdoc
+++ b/src/doc/src/devices/qtee-beagleboard-xm.qdoc
@@ -50,7 +50,7 @@
\badcode
cd <INSTALL_DIR>
- sudo Boot2Qt-3.x/beagleboard-eLinux/images/deploy.sh /dev/<device_name>
+ sudo Boot2Qt-4.x/beagleboard-eLinux/images/deploy.sh /dev/<device_name>
\endcode
After the image has been deployed, power on the device and check that
diff --git a/src/doc/src/devices/qtee-beaglebone-black.qdoc b/src/doc/src/devices/qtee-beaglebone-black.qdoc
index 5b0b700..8bd7e73 100644
--- a/src/doc/src/devices/qtee-beaglebone-black.qdoc
+++ b/src/doc/src/devices/qtee-beaglebone-black.qdoc
@@ -76,12 +76,12 @@
\li \b{\B2QA}
\badcode
cd <INSTALL_DIR>
- ./Boot2Qt-3.x/generic-4.2-eAndroid/images/beaglebone/deploy.sh
+ ./Boot2Qt-4.x/generic-4.2-eAndroid/images/beaglebone/deploy.sh
\endcode
\li \b{\B2QL}
\badcode
cd <INSTALL_DIR>
- sudo ./Boot2Qt-3.x/beaglebone-eLinux/images/deploy.sh /dev/<device_name>
+ sudo ./Boot2Qt-4.x/beaglebone-eLinux/images/deploy.sh /dev/<device_name>
\endcode
\endlist
diff --git a/src/doc/src/devices/qtee-nexus-7.qdoc b/src/doc/src/devices/qtee-nexus-7.qdoc
index 30cbc82..c2e7c6a 100644
--- a/src/doc/src/devices/qtee-nexus-7.qdoc
+++ b/src/doc/src/devices/qtee-nexus-7.qdoc
@@ -79,12 +79,12 @@
\li \b{Nexus 7 (2013):}
\badcode
cd <INSTALL_DIR>
- ./Boot2Qt-3.x/generic-X.Y-eAndroid/images/nexus7v2/deploy.sh
+ ./Boot2Qt-4.x/generic-X.Y-eAndroid/images/nexus7v2/deploy.sh
\endcode
\li \b{Nexus 7 (2012):}
\badcode
cd <INSTALL_DIR>
- ./Boot2Qt-3.x/generic-X.Y-eAndroid/images/nexus7/deploy.sh
+ ./Boot2Qt-4.x/generic-X.Y-eAndroid/images/nexus7/deploy.sh
\endcode
\endlist
diff --git a/src/doc/src/devices/qtee-raspberry-pi.qdoc b/src/doc/src/devices/qtee-raspberry-pi.qdoc
index 10a2b98..a4e0a69 100644
--- a/src/doc/src/devices/qtee-raspberry-pi.qdoc
+++ b/src/doc/src/devices/qtee-raspberry-pi.qdoc
@@ -54,7 +54,7 @@
\badcode
cd <INSTALL_DIR>
- sudo Boot2Qt-3.x/raspberrypi-eLinux/images/deploy.sh /dev/<device_name>
+ sudo Boot2Qt-4.x/raspberrypi-eLinux/images/deploy.sh /dev/<device_name>
\endcode
After the image has been deployed, insert the SD card, power on the device and check that
diff --git a/src/doc/src/devices/qtee-sabre-sd-imx6quad.qdoc b/src/doc/src/devices/qtee-sabre-sd-imx6quad.qdoc
index ef368d2..ad845d3 100644
--- a/src/doc/src/devices/qtee-sabre-sd-imx6quad.qdoc
+++ b/src/doc/src/devices/qtee-sabre-sd-imx6quad.qdoc
@@ -49,7 +49,7 @@
\badcode
cd <INSTALL_DIR>
- sudo Boot2Qt-3.x/imx6qsabresd-eLinux/images/deploy.sh /dev/<device_name>
+ sudo Boot2Qt-4.x/imx6qsabresd-eLinux/images/deploy.sh /dev/<device_name>
\endcode
After the image has been deployed, insert the SD card, power on the device and check that
diff --git a/src/doc/src/devices/qtee-toradex-apalis.qdoc b/src/doc/src/devices/qtee-toradex-apalis.qdoc
index df98ba3..6979253 100644
--- a/src/doc/src/devices/qtee-toradex-apalis.qdoc
+++ b/src/doc/src/devices/qtee-toradex-apalis.qdoc
@@ -49,21 +49,32 @@
\badcode
cd <INSTALL_DIR>
- sudo Boot2Qt-3.x/apalis-imx6-eLinux/images/deploy.sh /dev/<device_name>
+ sudo Boot2Qt-4.x/apalis-imx6-eLinux/images/deploy.sh /dev/<device_name>
\endcode
By default, the Toradex Apalis iMX6 boots from its internal eMMC. In order to boot from
- the external SD card, the U-Boot environment needs to be updated. Connect a serial cable
+ the external SD card, the U-Boot needs to be updated. Connect a serial cable
to the device and enter into the U-Boot environment by pressing any key before the autoboot.
- Enter following lines into U-Boot:
+ Enter following commands into U-Boot:
\badcode
- setenv bootcmd 'run sdboot ; echo sdboot failed ; run emmcboot ; echo ; echo emmcboot failed ; run nfsboot ; echo ; echo nfsboot failed ; usb start ;setenv stdout serial,vga ; setenv stdin serial,usbkbd'
- setenv sdboot 'run setup; setenv bootargs ${defargs} ${sdargs} ${setupargs} ${vidargs};echo Booting from SD card in 4bit slot...; fatload mmc 2:1 10800000 uImage && bootm 10800000'
- setenv sdargs 'ip=off root=/dev/mmcblk1p2 rw,noatime rootfstype=ext3 rootwait'
+ setenv drive 2
+ setenv setupdate 'fatload mmc ${drive}:1 ${loadaddr} flash_mmc.img; source'
+
+ run setupdate
+ run update_uboot
+ \endcode
+
+ Reset or power cycle the device to start the new U-Boot.
+ To reset the U-Boot environment to new default values, enter the following commands
+ in the U-Boot command line
+
+ \badcode
+ env default -a
saveenv
\endcode
+
New U-Boot command are now stored into the device, and you can start \B2Q. For more information
about the boot process on Toredex Apalis iMX6, see
\l{http://developer.toradex.com/software-resources/arm-family/linux/linux-booting}{Toradex Linux Booting}
diff --git a/src/doc/src/qtee-custom-embedded-linux.qdoc b/src/doc/src/qtee-custom-embedded-linux.qdoc
index bf26501..a33e41a 100644
--- a/src/doc/src/qtee-custom-embedded-linux.qdoc
+++ b/src/doc/src/qtee-custom-embedded-linux.qdoc
@@ -60,11 +60,18 @@
\section1 Setting Up Yocto Build Environment
- Run the setup script that initializes the Yocto environment:
+ Run the setup script that initializes the Yocto environment. Using Raspberry Pi as
+ an example:
\badcode
cd <BuildDir>
- <INSTALL_DIR>/Boot2Qt-3.x/sources/b2qt-yocto-meta/b2qt-init-build-env .
+ <INSTALL_DIR>/Boot2Qt-4.x/sources/b2qt-yocto-meta/b2qt-init-build-env init --device raspberrypi
+ \endcode
+
+ For more command line options, see:
+
+ \badcode
+ <INSTALL_DIR>/Boot2Qt-4.x/sources/b2qt-yocto-meta/b2qt-init-build-env help
\endcode
\section1 Building the Image and Toolchain
@@ -74,9 +81,8 @@
an example:
\badcode
- export TEMPLATECONF=meta-b2qt/conf
export MACHINE=raspberrypi
- source poky/oe-init-build-env build-raspberrypi
+ source ./setup-environment.sh
\endcode
The following table lists the \c MACHINE values for our reference platforms:
@@ -128,7 +134,7 @@
script. Using Raspberry Pi as an example:
\badcode
- <INSTALL_DIR>/Boot2Qt-3.x/sources/b2qt-build-scripts/embedded-common/init_build_env.sh <INSTALL_DIR>/Boot2Qt-3.x/sources/b2qt-build-scripts/embedded-linux/config.raspberrypi
+ <INSTALL_DIR>/Boot2Qt-4.x/sources/b2qt-build-scripts/embedded-common/init_build_env.sh <INSTALL_DIR>/Boot2Qt-4.x/sources/b2qt-build-scripts/embedded-linux/config.raspberrypi
\endcode
\note You can use the same build directory for Qt and the Yocto image.
@@ -137,12 +143,12 @@
You can use following scripts to build different parts of the \B2Q stack.
\badcode
- <INSTALL_DIR>/Boot2Qt-3.x/sources/b2qt-build-scripts/embedded-linux/build_qt.sh
- <INSTALL_DIR>/Boot2Qt-3.x/sources/b2qt-build-scripts/embedded-linux/build_extras.sh
- <INSTALL_DIR>/Boot2Qt-3.x/sources/b2qt-build-scripts/embedded-linux/build_image.sh
+ ./build_qt.sh
+ ./build_extras.sh
+ ./build_image.sh
\endcode
- After \e {embedded-linux/build_image.sh} has finished, you can flash the device with
+ After \e {build_image.sh} has finished, you can flash the device with
the updated image located in the build folder.
\section1 Configuring Qt Creator
@@ -151,7 +157,7 @@
developing for your device. The following script does this for you.
\badcode
- <INSTALL_DIR>/Boot2Qt-3.x/sources/b2qt-build-scripts/embedded-common/setup_qtcreator.sh
+ <INSTALL_DIR>/Boot2Qt-4.x/sources/b2qt-build-scripts/embedded-common/setup_qtcreator.sh
\endcode
This will set up a new kit in Qt Creator, using the toolchain and Qt from
diff --git a/src/doc/src/qtee-install-guide.qdoc b/src/doc/src/qtee-install-guide.qdoc
index 4fba4fd..6ffc8e6 100644
--- a/src/doc/src/qtee-install-guide.qdoc
+++ b/src/doc/src/qtee-install-guide.qdoc
@@ -61,9 +61,9 @@
download it from \l{https://www.virtualbox.org/wiki/Linux_Downloads} or
install it via distribution tools.
- If you have older Ubuntu versions such as 12.04 you have to install a newer
- version of VirtualBox than your distribution does provide.
- You have to add a foreign package source:
+ If you are running an older Ubuntu system such as 12.04, the version of
+ VirtualBox provided by the distribution is not recent enough. To install a
+ newer version, first add a foreign package source:
\badcode
wget -q http://download.virtualbox.org/virtualbox/debian/oracle_vbox.asc -O- | sudo apt-key add -
@@ -71,7 +71,7 @@
sudo apt-get update
\endcode
- Now install VirtualBox on your computer
+ Then, install VirtualBox on your computer:
\badcode
sudo apt-get install virtualbox-4.3
\endcode
diff --git a/src/doc/src/qtee-qml-reference.qdoc b/src/doc/src/qtee-qml-reference.qdoc
index 6ba5900..7ee86a3 100644
--- a/src/doc/src/qtee-qml-reference.qdoc
+++ b/src/doc/src/qtee-qml-reference.qdoc
@@ -27,6 +27,10 @@
\annotatedlist qtee-qmlmodules
+ \section1 B2Qt Utils Module
+
+ \annotatedlist utils-qmltypes
+
\section1 WiFi Module
\annotatedlist wifi-qmltypes
diff --git a/src/doppelganger/appops.cpp b/src/doppelganger/appops.cpp
new file mode 100644
index 0000000..adeddcc
--- /dev/null
+++ b/src/doppelganger/appops.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "appops.h"
+
+#if Q_ANDROID_VERSION_MAJOR > 4 || (Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR >= 4)
+#include <binder/IServiceManager.h>
+#include <binder/IAppOpsCallback.h>
+
+class AppOpsPrivate : public android::BnAppOpsCallback
+{
+public:
+ virtual void opChanged(int32_t, const android::String16&)
+ {
+
+ }
+
+ virtual android::status_t onTransact(uint32_t,
+ const android::Parcel&,
+ android::Parcel*,
+ uint32_t flags = 0)
+ {
+ (void)flags;
+ return android::OK;
+ }
+};
+#endif
+
+void AppOps::instantiate()
+{
+#if Q_ANDROID_VERSION_MAJOR > 4 || (Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR >= 4)
+ android::defaultServiceManager()->addService(android::String16("appops"), new AppOpsPrivate);
+#endif
+}
diff --git a/src/doppelganger/appops.h b/src/doppelganger/appops.h
new file mode 100644
index 0000000..5e765a6
--- /dev/null
+++ b/src/doppelganger/appops.h
@@ -0,0 +1,29 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#ifndef APPOPS_H
+#define APPOPS_H
+
+class AppOps
+{
+public:
+ static void instantiate();
+};
+
+#endif // APPOPS_H
diff --git a/src/doppelganger/doppelganger.pro b/src/doppelganger/doppelganger.pro
index 40c7650..fa91169 100644
--- a/src/doppelganger/doppelganger.pro
+++ b/src/doppelganger/doppelganger.pro
@@ -10,11 +10,13 @@ TEMPLATE = app
SOURCES += main.cpp \
permissioncontroller.cpp \
schedulingpolicyservice.cpp \
- powermanager.cpp
+ powermanager.cpp \
+ appops.cpp
HEADERS += \
permissioncontroller.h \
schedulingpolicyservice.h \
- powermanager.h
+ powermanager.h \
+ appops.h
load(qt_tool)
diff --git a/src/doppelganger/main.cpp b/src/doppelganger/main.cpp
index c548083..3e86a66 100644
--- a/src/doppelganger/main.cpp
+++ b/src/doppelganger/main.cpp
@@ -21,6 +21,7 @@
#include "permissioncontroller.h"
#include "schedulingpolicyservice.h"
#include "powermanager.h"
+#include "appops.h"
using namespace android;
@@ -30,5 +31,6 @@ int main(int, char *[])
SchedulingPolicyService::instantiate();
PermissionController::instantiate();
PowerManager::instantiate();
+ AppOps::instantiate();
IPCThreadState::self()->joinThreadPool();
}
diff --git a/src/imports/utils/plugin.cpp b/src/imports/utils/plugin.cpp
index cc96fb0..4012e96 100644
--- a/src/imports/utils/plugin.cpp
+++ b/src/imports/utils/plugin.cpp
@@ -16,32 +16,135 @@
** the contact form at http://www.qt.io
**
****************************************************************************/
-#include <QtDroidUtils/qdroidutils.h>
+#include <b2qtdevice.h>
#include <QtQml>
+/*!
+ \qmlmodule B2Qt.Utils 1.0
+ \title B2Qt Utils Module
+ \ingroup qtee-qmlmodules
+ \brief A collection of utility functions, accessible from QML.
+
+ Provides various utility functions for controlling an embedded
+ device, such as display brightness, IP address and hostname, and
+ device shutdown/reboot.
+
+ Import the module as follows:
+
+ \badcode
+ import B2Qt.Utils 1.0
+ \endcode
+
+ This will give you access to the singleton QML type B2QtDevice.
+
+ \note Some functions are currently only implemented for one of
+ the platforms.
+
+ \section1 QML Types
+*/
+
+/*!
+ \qmltype B2QtDevice
+ \inqmlmodule B2Qt.Utils
+ \ingroup utils-qmltypes
+ \brief Singleton QML type providing access to utility functions.
+
+ B2QtDevice QML type is the interface to various utility
+ functions.
+
+ There is no need to create an instance of this object. To use it,
+ simply import the \c {B2Qt.Utils} module:
+
+ \qml
+ import B2Qt.Utils 1.0
+
+ Text {
+ text: qsTr("IP Address:") + B2QtDevice.ipAddress
+ }
+ \endqml
+
+ \note Some functions are currently only implemented for one of
+ the platforms.
+*/
+
+/*!
+ \qmlmethod B2Qt.Utils::B2QtDevice::reboot()
+
+ Reboots the system. Does not return.
+
+ \sa powerOff()
+*/
+
+/*!
+ \qmlmethod B2Qt.Utils::B2QtDevice::powerOff()
+
+ Shuts down the system. Does not return.
+
+ \sa reboot()
+*/
+
+/*!
+ \qmlproperty int B2Qt.Utils::B2QtDevice::masterVolume
+
+ This property holds the master volume of the device.
+ The volume can range from \c 0 to \c 100 and is linear.
+ Changing the master volume will affect all audio streams.
+
+ \note Currently implemented only for \B2QA.
+*/
+
+/*!
+ \qmlproperty int B2Qt.Utils::B2QtDevice::displayBrightness
+ This property holds the display brightness (the intensity of the backlight).
+ The value is in the range from \c 0 to \c 255, where 255 is the maximum
+ brightness, and 0 is the minimum (typically, the backlight is turned off).
+
+ \note Currently implemented only for \B2QA.
+*/
+
+/*!
+ \qmlproperty string B2Qt.Utils::B2QtDevice::ipAddress
+ \readonly
+
+ This property holds the current IP address(es) of the device
+ for all active network interfaces. If multiple IP addresses are defined,
+ this property holds a comma-separated list. The localhost (loopback)
+ IP addresses are omitted.
+
+ \sa hostname
+*/
+
+/*!
+ \qmlproperty string B2Qt.Utils::B2QtDevice::hostname
+
+ This property holds the current hostname of the device.
+
+ \sa ipAddress
+*/
+
static QObject *module_api_factory(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
- QDroidUtils *api = new QDroidUtils();
+ B2QtDevice *api = new B2QtDevice();
return api;
}
-class QDroidUtilsPlugin : public QQmlExtensionPlugin
+class B2QtUtilsPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
public:
- QDroidUtilsPlugin()
+ B2QtUtilsPlugin()
{
}
void registerTypes(const char *uri)
{
- Q_ASSERT(QLatin1String(uri) == "QtDroid.Utils");
- qmlRegisterSingletonType<QDroidUtils>(uri, 1, 0, "DroidUtils", module_api_factory);
+ Q_ASSERT(QLatin1String(uri) == "B2Qt.Utils");
+ qmlRegisterSingletonType<B2QtDevice>(uri, 1, 0, "B2QtDevice", module_api_factory);
}
};
diff --git a/src/imports/utils/qmldir b/src/imports/utils/qmldir
index 6db1b0f..e5e0b31 100644
--- a/src/imports/utils/qmldir
+++ b/src/imports/utils/qmldir
@@ -1,3 +1,3 @@
-module QtDroid.Utils
-plugin qtdroidutilsplugin
+module B2Qt.Utils
+plugin b2qtutilsplugin
typeinfo plugins.qmltypes
diff --git a/src/imports/utils/utils.pro b/src/imports/utils/utils.pro
index a589b46..a580643 100644
--- a/src/imports/utils/utils.pro
+++ b/src/imports/utils/utils.pro
@@ -1,14 +1,10 @@
CXX_MODULE = qml
-TARGET = qtdroidutilsplugin
-TARGETPATH = QtDroid/Utils
+TARGET = b2qtutilsplugin
+TARGETPATH = B2Qt/Utils
IMPORT_VERSION = 1.0
-QT += qml
+QT += qml b2qtutils
SOURCES += plugin.cpp
-### kludge
-INCLUDEPATH += $$PWD/../../../include
-LIBS += -L$$PWD/../../../lib -lQt5DroidUtils
-
load(qml_plugin)
diff --git a/src/qt_hw_init/main.cpp b/src/qt_hw_init/main.cpp
index e6411ac..15cc807 100644
--- a/src/qt_hw_init/main.cpp
+++ b/src/qt_hw_init/main.cpp
@@ -16,33 +16,15 @@
** the contact form at http://www.qt.io
**
****************************************************************************/
-#include "../utils/qdroidutils.h"
-#include <QDebug>
-
-static void setMaxVolume(QDroidUtils *utils)
-{
- // Set the audio orientation to something to force the HW driver to reconfigure
- // audio routing (workaround for bug on Nexus 7)
- utils->setOrientationForAudioSystem(QDroidUtils::LandscapeAudioOrientation);
- utils->setMasterVolume(100);
- utils->setMasterMute(false);
- utils->setStreamVolume(QDroidUtils::SystemAudioStream, 100);
- utils->setStreamVolume(QDroidUtils::MusicAudioStream, 100);
- utils->setStreamVolume(QDroidUtils::NotificationAudioStream, 100);
- utils->setStreamVolume(QDroidUtils::EnforcedAudibleAudioStream, 100);
-}
-
-static void setDisplayBrightness(QDroidUtils *utils)
-{
- utils->setDisplayBrightness(255);
-}
+#include "b2qtdevice.h"
int main(int, char *[])
{
- QDroidUtils utils;
+ B2QtDevice device;
+
+ device.initAudio();
- setMaxVolume(&utils);
- setDisplayBrightness(&utils);
+ device.setDisplayBrightness(255);
return 0;
}
diff --git a/src/qt_hw_init/qt_hw_init.pro b/src/qt_hw_init/qt_hw_init.pro
index 879e450..4784926 100644
--- a/src/qt_hw_init/qt_hw_init.pro
+++ b/src/qt_hw_init/qt_hw_init.pro
@@ -1,10 +1,8 @@
TEMPLATE = app
TARGET = qt_hw_init
QT -= gui
-QT += droidutils
+QT += b2qtutils
SOURCES += main.cpp
-LIBS += -lQt5DroidUtils
-
load(qt_tool)
diff --git a/src/src.pro b/src/src.pro
index fe2f463..e8ecbaf 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -5,6 +5,7 @@ SUBDIRS += \
imports \
doc \
plugins \
+ b2qt-update-util
android: SUBDIRS += doppelganger qt_hw_init qconnectivity
diff --git a/src/utils/qdroidutils.cpp b/src/utils/b2qtdevice.cpp
index d852a2e..d7a7cd2 100644
--- a/src/utils/qdroidutils.cpp
+++ b/src/utils/b2qtdevice.cpp
@@ -16,7 +16,7 @@
** the contact form at http://www.qt.io
**
****************************************************************************/
-#include "qdroidutils.h"
+#include "b2qtdevice.h"
#include <unistd.h>
#include <QDebug>
#include <math.h>
@@ -32,61 +32,167 @@
#include <utils/String8.h>
#endif
+// When we can't query directly, at least remember what we have set it to
+static quint8 knownBrightness = 255;
+
+B2QtDevice::B2QtDevice(QObject *parent)
+ : QObject(parent)
+{
+}
+
+B2QtDevice::~B2QtDevice()
+{
+}
+
/*!
* Reboots the system. Does not return.
*
- * \sa powerOffSystem()
+ * \sa powerOff()
*/
-void QDroidUtils::rebootSystem()
+void B2QtDevice::reboot()
{
sync();
- reboot(RB_AUTOBOOT);
+ ::reboot(RB_AUTOBOOT);
qWarning("reboot returned");
}
+
/*!
* Shuts down the system. Does not return.
*
- * \sa rebootSystem()
+ * \sa reboot()
*/
-void QDroidUtils::powerOffSystem()
+void B2QtDevice::powerOff()
{
sync();
- reboot(RB_POWER_OFF);
+ ::reboot(RB_POWER_OFF);
qWarning("powerOff returned");
}
-void QDroidUtils::setOrientationForAudioSystem(AudioOrientation orientation)
+
+/*!
+ * Sets the display brightness (i.e. the intensity of the backlight)
+ * to \a value. A value of 255 requests maximum brightness, while 0 requests
+ * minimum (typically, the backlight turned off).
+ *
+ * Returns true on success.
+ */
+bool B2QtDevice::setDisplayBrightness(quint8 value)
{
#ifdef Q_OS_ANDROID_NO_SDK
- QString orientationString = QStringLiteral("undefined");
- switch (orientation) {
- case LandscapeAudioOrientation:
- orientationString = QStringLiteral("landscape");
- break;
- case PortraitAudioOrientation:
- orientationString = QStringLiteral("portrait");
- break;
- case SquareAudioOrientation:
- orientationString = QStringLiteral("square");
- break;
- default:
- break;
+ const struct hw_module_t* module = 0;
+ if (hw_get_module(LIGHTS_HARDWARE_MODULE_ID, &module))
+ return false;
+ if (!module || !module->methods || !module->methods->open)
+ return false;
+
+ struct light_device_t* device = 0;
+ if (module->methods->open(module, LIGHT_ID_BACKLIGHT, (struct hw_device_t**)&device))
+ return false;
+ if (!device || !device->set_light || !device->common.close)
+ return false;
+
+ struct light_state_t state;
+ memset(&state, 0, sizeof(light_state_t));
+ state.color = 0xff000000 | (value << 16) | (value << 8) | value;
+ if (!device->set_light(device, &state))
+ return false;
+
+ device->common.close(&device->common);
+ knownBrightness = value;
+ emit displayBrightnessChanged(value);
+ return true;
+#else
+ Q_UNUSED(value);
+ return false;
+#endif
+}
+
+
+/*!
+ * Returns the current backlight intensity.
+ * \sa setDisplayBrightness
+ */
+quint8 B2QtDevice::displayBrightness() const
+{
+ QFile sysFile(QStringLiteral("/sys/class/leds/lcd-backlight/brightness"));
+ if (sysFile.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) {
+ bool ok = false;
+ int sysVal = sysFile.read(3).simplified().toInt(&ok);
+ if (ok)
+ knownBrightness = qBound(0, sysVal, 255);
}
- android::AudioSystem::setParameters(0, android::String8(QStringLiteral("orientation=%2")
- .arg(orientationString).toLatin1().constData()));
+ return knownBrightness;
+}
+
+
+/*!
+ * Gets the current IP address(es) of the device
+ */
+QString B2QtDevice::getIPAddress() const
+{
+ QStringList addresses;
+ foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) {
+ QNetworkInterface::InterfaceFlags flags = interface.flags();
+ if (flags.testFlag(QNetworkInterface::IsRunning) && !flags.testFlag(QNetworkInterface::IsLoopBack)) {
+ foreach (const QNetworkAddressEntry &entry, interface.addressEntries())
+ addresses.append(entry.ip().toString().split('%').first());
+ }
+ }
+ return addresses.join(QStringLiteral(", "));
+}
+
+
+/*!
+ * Gets the current hostname of the device
+ */
+QString B2QtDevice::hostname() const
+{
+ QString name;
+#ifdef Q_OS_ANDROID_NO_SDK
+ char prop_value[PROPERTY_VALUE_MAX];
+ int len = property_get("net.hostname", prop_value, 0);
+ if (len)
+ name = QString::fromLocal8Bit(prop_value, len);
+#else
+ name = QHostInfo::localHostName();
#endif
+ return name;
}
+
+/*!
+ * Sets new hostname for the device
+ */
+bool B2QtDevice::setHostname(const QString &name)
+{
+#ifdef Q_OS_ANDROID_NO_SDK
+ property_set("net.hostname", name.toLocal8Bit().constData());
+#else
+ QByteArray lname = name.toLocal8Bit();
+ if (::sethostname(lname.constData(), lname.length())) {
+ qWarning("Could not set system hostname");
+ return false;
+ }
+ // Also store it for next boot:
+ QFile file(QStringLiteral("/etc/hostname"));
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning("Could not write to /etc/hostname");
+ return false;
+ }
+ file.write(lname.append('\n'));
+ file.close();
+#endif
+ emit hostnameChanged(name);
+ return true;
+}
+
+
/*!
* Sets the master volume to \a volume.
* The volume can range from 0 to 100 and is linear.
- * Changing the master volume will affect all audio streams.
- *
- * \sa setStreamVolume()
- * \sa setMasterMute()
*/
-void QDroidUtils::setMasterVolume(int volume)
+void B2QtDevice::setMasterVolume(int volume)
{
#ifdef Q_OS_ANDROID_NO_SDK
android::status_t rc;
@@ -94,28 +200,57 @@ void QDroidUtils::setMasterVolume(int volume)
rc = android::AudioSystem::setMasterVolume(android::AudioSystem::linearToLog(volume));
if (rc != android::NO_ERROR)
qWarning() << Q_FUNC_INFO << "Error while setting audio properties.";
+ else
+ emit masterVolumeChanged(volume);
+#else
+ Q_UNUSED(volume)
#endif
}
+
/*!
- * Sets the master mute to \a mute. Setting it to true will disable all
- * sounds on the device.
- *
- * \sa setMasterVolume()
- * \sa setStreamMute()
+ * Returns the current master volume.
+ * The volume can range from 0 to 100 and is linear.
*/
-void QDroidUtils::setMasterMute(bool mute)
+int B2QtDevice::masterVolume() const
{
+ float volume = 0;
#ifdef Q_OS_ANDROID_NO_SDK
android::status_t rc;
- rc = android::AudioSystem::setMasterMute(mute);
+ rc = android::AudioSystem::getMasterVolume(&volume);
if (rc != android::NO_ERROR)
- qWarning() << Q_FUNC_INFO << "Error while setting audio properties.";
+ qWarning() << Q_FUNC_INFO << "Error while getting audio properties.";
#endif
+ return qBound(0, qRound(volume), 100);
}
-/*!
- \enum QDroidUtils::AudioStreamType
+#ifdef Q_OS_ANDROID_NO_SDK
+// Android audio handling
+
+enum AudioOrientation {
+ LandscapeAudioOrientation,
+ PortraitAudioOrientation,
+ SquareAudioOrientation,
+ UndefinedAudioOrientation,
+};
+
+enum AudioStreamType {
+ DefaultAudioStream = -1,
+ VoiceCallAudioStream = 0,
+ SystemAudioStream = 1,
+ RingAudioStream = 2,
+ MusicAudioStream = 3,
+ AlarmAudioStream = 4,
+ NotificationAudioStream = 5,
+ BluetoothAudioStream = 6,
+ EnforcedAudibleAudioStream = 7,
+ DTMFAudioStream = 8,
+ TTSAudioStream = 9
+};
+
+
+/*
+ \enum AudioStreamType
\value DefaultAudioStream
The default audio stream
@@ -147,7 +282,7 @@ void QDroidUtils::setMasterMute(bool mute)
The audio stream for text-to-speech
*/
-/*!
+/*
* Sets the volume for a specific audio \a stream type to \a volume.
* The volume can range from 0 to 100 and is linear.
* All streams of the specified type will be affected.
@@ -155,172 +290,114 @@ void QDroidUtils::setMasterMute(bool mute)
* \sa setMasterVolume()
* \sa setStreamMute()
*/
-void QDroidUtils::setStreamVolume(AudioStreamType streamType, int volume)
+void setStreamVolume(AudioStreamType streamType, int volume)
{
-#ifdef Q_OS_ANDROID_NO_SDK
android::status_t rc;
volume = qBound(0, volume, 100);
rc = android::AudioSystem::setStreamVolume(audio_stream_type_t(streamType),
android::AudioSystem::linearToLog(volume), 0);
if (rc != android::NO_ERROR)
qWarning() << Q_FUNC_INFO << "Error while setting audio properties.";
-#endif
}
-/*!
+/*
* Mutes all audio \a streams of type \a streamType.
*
* \sa setStreamVolume()
* \sa setMasterMute()
*/
-void QDroidUtils::setStreamMute(AudioStreamType streamType, bool mute)
+void setStreamMute(AudioStreamType streamType, bool mute)
{
-#ifdef Q_OS_ANDROID_NO_SDK
android::status_t rc;
rc = android::AudioSystem::setStreamMute(audio_stream_type_t(streamType), mute);
if (rc != android::NO_ERROR)
qWarning() << Q_FUNC_INFO << "Error while setting audio properties.";
-#endif
}
-/*!
- * Sets the display brightness (i.e. the intensity of the backlight)
- * to \a value. A value of 255 requests maximum brightness, while 0 requests
- * minimum (typically, the backlight turned off).
- *
- * Returns true on success.
- */
-//### TBD: add the user/sensor setting as parameter!
-bool QDroidUtils::setDisplayBrightness(quint8 value)
+void setOrientationForAudioSystem(AudioOrientation orientation)
{
-#ifdef Q_OS_ANDROID_NO_SDK
- const struct hw_module_t* module = 0;
- if (hw_get_module(LIGHTS_HARDWARE_MODULE_ID, &module))
- return false;
- if (!module || !module->methods || !module->methods->open)
- return false;
-
- struct light_device_t* device = 0;
- if (module->methods->open(module, LIGHT_ID_BACKLIGHT, (struct hw_device_t**)&device))
- return false;
- if (!device || !device->set_light || !device->common.close)
- return false;
-
- struct light_state_t state;
- memset(&state, 0, sizeof(light_state_t));
- state.color = 0xff000000 | (value << 16) | (value << 8) | value;
- if (!device->set_light(device, &state))
- return false;
-
- device->common.close(&device->common);
-#else
- qDebug("QDroidUtils::setDisplayBrightness(%i)", value);
-#endif
- return true;
-}
-
-
-/*!
- * Gets the current IP address(es) of the device
- */
-QString QDroidUtils::getIPAddress()
-{
- QList<QNetworkInterface> availableInterfaces = QNetworkInterface::allInterfaces();
- if (availableInterfaces.length() > 0) {
- foreach (const QNetworkInterface &interface, availableInterfaces) {
- if (interface.flags() & QNetworkInterface::IsRunning
- && (interface.flags() & QNetworkInterface::IsLoopBack) == 0) {
- QList<QNetworkAddressEntry> entries = interface.addressEntries();
- QStringList addresses;
- foreach (const QNetworkAddressEntry &entry, entries)
- addresses.append(entry.ip().toString().split('%').first());
- return addresses.join(QStringLiteral(", "));
- }
- }
+ QString orientationString = QStringLiteral("undefined");
+ switch (orientation) {
+ case LandscapeAudioOrientation:
+ orientationString = QStringLiteral("landscape");
+ break;
+ case PortraitAudioOrientation:
+ orientationString = QStringLiteral("portrait");
+ break;
+ case SquareAudioOrientation:
+ orientationString = QStringLiteral("square");
+ break;
+ default:
+ break;
}
- return QString();
+ android::AudioSystem::setParameters(0, android::String8(QStringLiteral("orientation=%2")
+ .arg(orientationString).toLatin1().constData()));
}
-/*!
- * Gets the current hostname of the device
- */
-QString QDroidUtils::getHostname()
-{
- QString hostname;
-#ifdef Q_OS_ANDROID_NO_SDK
- char prop_value[PROPERTY_VALUE_MAX];
- int len = property_get("net.hostname", prop_value, 0);
- if (len)
- hostname = QString::fromLocal8Bit(prop_value, len);
-#else
- hostname = QHostInfo::localHostName();
-#endif
- return hostname;
-}
/*!
- * Sets new hostname for the device
+ * Sets the master mute to \a mute. Setting it to true will disable all
+ * sounds on the device.
+ *
+ * \sa setMasterVolume()
+ * \sa setStreamMute()
*/
-bool QDroidUtils::setHostname(QString hostname)
+void setMasterMute(bool mute)
{
-#ifdef Q_OS_ANDROID_NO_SDK
- property_set("net.hostname", hostname.toLocal8Bit().constData());
-#else
- QFile file("/etc/hostname");
- if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
- qWarning("Could not open hostname file");
- return false;
- }
- file.write(hostname.toLocal8Bit());
- file.close();
-#endif
- return true;
-}
-float QDroidUtils::masterVolume() const
-{
- float volume = NAN;
-#ifdef Q_OS_ANDROID_NO_SDK
android::status_t rc;
- rc = android::AudioSystem::getMasterVolume(&volume);
+ rc = android::AudioSystem::setMasterMute(mute);
if (rc != android::NO_ERROR)
- qWarning() << Q_FUNC_INFO << "Error while getting audio properties.";
-#endif
- return volume;
+ qWarning() << Q_FUNC_INFO << "Error while setting audio properties.";
}
-bool QDroidUtils::masterMute() const
+bool masterMute()
{
bool mute = false;
-#ifdef Q_OS_ANDROID_NO_SDK
android::status_t rc;
rc = android::AudioSystem::getMasterMute(&mute);
if (rc != android::NO_ERROR)
qWarning() << Q_FUNC_INFO << "Error while getting audio properties.";
-#endif
return mute;
}
-float QDroidUtils::streamVolume(AudioStreamType stream) const
+float streamVolume(AudioStreamType stream)
{
float volume = NAN;
-#ifdef Q_OS_ANDROID_NO_SDK
android::status_t rc;
rc = android::AudioSystem::getStreamVolume(audio_stream_type_t(stream), &volume, 0);
if (rc != android::NO_ERROR)
qWarning() << Q_FUNC_INFO << "Error while getting audio properties.";
-#endif
return volume;
}
-bool QDroidUtils::streamMute(AudioStreamType stream) const
+bool streamMute(AudioStreamType stream)
{
bool mute = false;
-#ifdef Q_OS_ANDROID_NO_SDK
android::status_t rc;
rc = android::AudioSystem::getStreamMute(audio_stream_type_t(stream), &mute);
if (rc != android::NO_ERROR)
qWarning() << Q_FUNC_INFO << "Error while getting audio properties.";
-#endif
return mute;
}
+
+#endif
+
+/*!
+ * Initializes the audio subsystem, setting the volume to max.
+ * This is done during system startup, so there is normally no need to call this function from applications.
+ */
+void B2QtDevice::initAudio()
+{
+#ifdef Q_OS_ANDROID_NO_SDK
+ // Set the audio orientation to something to force the HW driver to reconfigure
+ // audio routing (workaround for bug on Nexus 7)
+ setOrientationForAudioSystem(LandscapeAudioOrientation);
+ setMasterVolume(100);
+ setMasterMute(false);
+ setStreamVolume(SystemAudioStream, 100);
+ setStreamVolume(MusicAudioStream, 100);
+ setStreamVolume(NotificationAudioStream, 100);
+ setStreamVolume(EnforcedAudibleAudioStream, 100);
+#endif
+}
diff --git a/src/utils/b2qtdevice.h b/src/utils/b2qtdevice.h
new file mode 100644
index 0000000..0563e3f
--- /dev/null
+++ b/src/utils/b2qtdevice.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://www.qt.io
+**
+****************************************************************************/
+#ifndef B2QTDEVICE_H
+#define B2QTDEVICE_H
+
+#include <qobject.h>
+
+class Q_DECL_EXPORT B2QtDevice : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(quint8 displayBrightness READ displayBrightness WRITE setDisplayBrightness NOTIFY displayBrightnessChanged)
+ Q_PROPERTY(QString hostname READ hostname WRITE setHostname NOTIFY hostnameChanged)
+ Q_PROPERTY(QString ipAddress READ getIPAddress NOTIFY ipAddressChanged)
+ Q_PROPERTY(int masterVolume READ masterVolume WRITE setMasterVolume NOTIFY masterVolumeChanged)
+
+public:
+ B2QtDevice(QObject *parent = 0);
+ ~B2QtDevice();
+
+ quint8 displayBrightness() const;
+ QString hostname() const;
+ QString getIPAddress() const;
+ int masterVolume() const;
+
+ void initAudio();
+
+public Q_SLOTS:
+ void reboot();
+ void powerOff();
+
+ bool setDisplayBrightness(quint8 value);
+ bool setHostname(const QString &name);
+ void setMasterVolume(int volume);
+
+signals:
+ void displayBrightnessChanged(quint8 newValue);
+ void hostnameChanged(const QString &newName);
+ void ipAddressChanged(const QString &newAddress);
+ void masterVolumeChanged(int newVolume);
+};
+
+#endif // B2QTDEVICE_H
diff --git a/src/utils/qdroidutils.h b/src/utils/qdroidutils.h
deleted file mode 100644
index 747caad..0000000
--- a/src/utils/qdroidutils.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Digia Plc
-** All rights reserved.
-** For any questions to Digia, please use the contact form at
-** http://www.qt.io
-**
-** This file is part of Qt Enterprise Embedded.
-**
-** Licensees holding valid Qt Enterprise licenses may use this file in
-** accordance with the Qt Enterprise License Agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia.
-**
-** If you have questions regarding the use of this file, please use
-** the contact form at http://www.qt.io
-**
-****************************************************************************/
-#ifndef QDROIDUTILS_H
-#define QDROIDUTILS_H
-
-#include <qobject.h>
-
-class Q_DECL_EXPORT QDroidUtils : public QObject
-{
- Q_OBJECT
- Q_ENUMS(AudioStreamType)
-public:
- enum AudioOrientation {
- LandscapeAudioOrientation,
- PortraitAudioOrientation,
- SquareAudioOrientation,
- UndefinedAudioOrientation,
- };
-
- enum AudioStreamType {
- DefaultAudioStream = -1,
- VoiceCallAudioStream = 0,
- SystemAudioStream = 1,
- RingAudioStream = 2,
- MusicAudioStream = 3,
- AlarmAudioStream = 4,
- NotificationAudioStream = 5,
- BluetoothAudioStream = 6,
- EnforcedAudibleAudioStream = 7,
- DTMFAudioStream = 8,
- TTSAudioStream = 9
- };
-
- QDroidUtils(QObject* parent = 0) : QObject(parent)
- {
- }
- ~QDroidUtils()
- {
- }
-
- //### TBD: make an instance() method, for singleton use from C++ ?
- //e.g. connect(myobj, mysig, QDroidUtils::instance(), slot(rebootSystem());
-
-public Q_SLOTS:
- void rebootSystem();
- void powerOffSystem();
-
- void setOrientationForAudioSystem(AudioOrientation orientation);
-
- void setMasterVolume(int volume);
- void setMasterMute(bool mute);
- void setStreamVolume(AudioStreamType stream, int volume);
- void setStreamMute(AudioStreamType stream, bool mute);
-
- float masterVolume() const;
- bool masterMute() const;
- float streamVolume(AudioStreamType stream) const;
- bool streamMute(AudioStreamType stream) const;
-
- bool setDisplayBrightness(quint8 value);
-
- QString getIPAddress();
- QString getHostname();
- bool setHostname(QString hostname);
-};
-
-#endif // QDROIDUTILS_H
diff --git a/src/utils/utils.pro b/src/utils/utils.pro
index e28b111..13287d5 100644
--- a/src/utils/utils.pro
+++ b/src/utils/utils.pro
@@ -1,20 +1,18 @@
-TARGET = QtDroidUtils
-VERSION = 5.2
+load(qt_build_config)
+
+TARGET = B2QtUtils
+VERSION = 1.0
CONFIG += dll warn_on
QT = core network
-#QT = core-private gui-private qml-private quick-private
-#QT_PRIVATE = v8-private
-
-#DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES
-MODULE = droidutils
+MODULE = b2qtutils
load(qt_module)
android: LIBS += -lmedia -lhardware -lcutils -lutils
HEADERS += \
- $$PWD/qdroidutils.h
+ $$PWD/b2qtdevice.h
SOURCES += \
- $$PWD/qdroidutils.cpp
+ $$PWD/b2qtdevice.cpp
diff --git a/sync.profile b/sync.profile
index 22d148b..ede245e 100644
--- a/sync.profile
+++ b/sync.profile
@@ -1,5 +1,5 @@
%modules = ( # path to module name map
- "QtDroidUtils" => "$basedir/src/utils"
+ "B2QtUtils" => "$basedir/src/utils"
);
%moduleheaders = ( # restrict the module headers to those found in relative path
);