summaryrefslogtreecommitdiff
path: root/daemon
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2014-12-07 23:39:29 +0100
committerJavier <dev.git@javispedro.com>2014-12-07 23:39:29 +0100
commita60c1cb3c4afd6dfd305115ec4c52e993172fa7d (patch)
tree1a12cfaed45b923ed511de388d556ccc705c9e6a /daemon
parent49c20eb7e2933ae6e9e4337fc0fe9b49a39efde8 (diff)
ability to upload apps
Diffstat (limited to 'daemon')
-rw-r--r--daemon/bankmanager.cpp134
-rw-r--r--daemon/bankmanager.h14
-rw-r--r--daemon/daemon.pro8
-rw-r--r--daemon/manager.cpp14
-rw-r--r--daemon/manager.h3
-rw-r--r--daemon/stm32crc.cpp119
-rw-r--r--daemon/stm32crc.h24
-rw-r--r--daemon/uploadmanager.cpp266
-rw-r--r--daemon/uploadmanager.h65
-rw-r--r--daemon/watchconnector.h16
10 files changed, 639 insertions, 24 deletions
diff --git a/daemon/bankmanager.cpp b/daemon/bankmanager.cpp
index 194ec77..fe5dc21 100644
--- a/daemon/bankmanager.cpp
+++ b/daemon/bankmanager.cpp
@@ -1,11 +1,19 @@
+#include <QFile>
+#include <QDir>
#include "unpacker.h"
#include "packer.h"
#include "bankmanager.h"
-BankManager::BankManager(WatchConnector *watch, AppManager *apps, QObject *parent) :
- QObject(parent), watch(watch), apps(apps)
+BankManager::BankManager(WatchConnector *watch, UploadManager *upload, AppManager *apps, QObject *parent) :
+ QObject(parent), watch(watch), upload(upload), apps(apps), _refresh(new QTimer(this))
{
- connect(watch, &WatchConnector::connectedChanged, this, &BankManager::handleWatchConnected);
+ connect(watch, &WatchConnector::connectedChanged,
+ this, &BankManager::handleWatchConnected);
+
+ _refresh->setInterval(0);
+ _refresh->setSingleShot(true);
+ connect(_refresh, &QTimer::timeout,
+ this, &BankManager::refresh);
}
int BankManager::numSlots() const
@@ -32,9 +40,82 @@ bool BankManager::uploadApp(const QUuid &uuid, int slot)
return false;
}
- // TODO
+ QDir appDir(info.path());
+
+ logger()->debug() << "about to install app from" << appDir.absolutePath() << "into slot" << slot;
+
+ QFile *binaryFile = new QFile(appDir.absoluteFilePath("pebble-app.bin"), this);
+ if (!binaryFile->open(QIODevice::ReadOnly)) {
+ logger()->warn() << "failed to open" << binaryFile->fileName() << ":" << binaryFile->errorString();
+ delete binaryFile;
+ return false;
+ }
+
+ logger()->debug() << "binary file size is" << binaryFile->size();
+
+ QFile *resourceFile = 0;
+ if (appDir.exists("app_resources.pbpack")) {
+ resourceFile = new QFile(appDir.absoluteFilePath("app_resources.pbpack"), this);
+ if (!resourceFile->open(QIODevice::ReadOnly)) {
+ logger()->warn() << "failed to open" << resourceFile->fileName() << ":" << resourceFile->errorString();
+ delete resourceFile;
+ return false;
+ }
+ }
+
+ // Mark the slot as used, but without any app, just in case.
+ _slots[slot].used = true;
+ _slots[slot].name.clear();
+ _slots[slot].uuid = QUuid();
+
+ upload->upload(WatchConnector::uploadBINARY, slot, binaryFile, -1,
+ [this, binaryFile, resourceFile, slot]() {
+ logger()->debug() << "app binary upload succesful";
+ delete binaryFile;
+
+ // Proceed to upload the resource file
+ if (resourceFile) {
+ upload->upload(WatchConnector::uploadRESOURCES, slot, resourceFile, -1,
+ [this, resourceFile, slot]() {
+ logger()->debug() << "app resources upload succesful";
+ delete resourceFile;
+
+ // Upload succesful
+ // Tell the watch to reload the slot
+ refreshWatchApp(slot, [this]() {
+ logger()->debug() << "app refresh succesful";
+ _refresh->start();
+ }, [this](int code) {
+ logger()->warn() << "app refresh failed" << code;
+ _refresh->start();
+ });
+ }, [this, resourceFile](int code) {
+ logger()->warn() << "app resources upload failed" << code;
+ delete resourceFile;
+
+ _refresh->start();
+ });
+
+ } else {
+ // No resource file
+ // Tell the watch to reload the slot
+ refreshWatchApp(slot, [this]() {
+ logger()->debug() << "app refresh succesful";
+ _refresh->start();
+ }, [this](int code) {
+ logger()->warn() << "app refresh failed" << code;
+ _refresh->start();
+ });
+ }
+ }, [this, binaryFile, resourceFile](int code) {
+ logger()->warn() << "app binary upload failed" << code;
+ delete binaryFile;
+ delete resourceFile;
+
+ _refresh->start();
+ });
- return false;
+ return true;
}
bool BankManager::unloadApp(int slot)
@@ -76,7 +157,7 @@ bool BankManager::unloadApp(int slot)
break;
}
- QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection);
+ _refresh->start();
return true;
});
@@ -169,24 +250,43 @@ int BankManager::findUnusedSlot() const
return -1;
}
+void BankManager::refreshWatchApp(int slot, std::function<void ()> successCallback, std::function<void (int)> errorCallback)
+{
+ QByteArray msg;
+ Packer p(&msg);
+ p.write<quint8>(WatchConnector::appmgrREFRESH_APP);
+ p.write<quint32>(slot);
+
+ watch->sendMessage(WatchConnector::watchAPP_MANAGER, msg,
+ [this, successCallback, errorCallback](const QByteArray &data) {
+ Unpacker u(data);
+ if (u.read<quint8>() != WatchConnector::appmgrREFRESH_APP) {
+ return false;
+ }
+ int code = u.read<quint32>();
+ if (code == Success) {
+ if (successCallback) {
+ successCallback();
+ }
+ } else {
+ if (errorCallback) {
+ errorCallback(code);
+ }
+ }
+
+ return true;
+ });
+}
+
void BankManager::handleWatchConnected()
{
if (watch->isConnected()) {
- refresh();
+ _refresh->start();
}
}
#if 0
-
-void WatchConnector::getAppbankStatus(const std::function<void(const QString &s)>& callback)
-{
- sendMessage(watchAPP_MANAGER, QByteArray(1, appmgrGET_APPBANK_STATUS),
- [this, callback](const QByteArray &data) {
-
- });
-}
-
-void WatchConnector::getAppbankUuids(const function<void(const QList<QUuid> &)>& callback)
+void BankManager::getAppbankUuids(const function<void(const QList<QUuid> &)>& callback)
{
sendMessage(watchAPP_MANAGER, QByteArray(1, appmgrGET_APPBANK_UUIDS),
[this, callback](const QByteArray &data) {
diff --git a/daemon/bankmanager.h b/daemon/bankmanager.h
index 28729b9..6abedc8 100644
--- a/daemon/bankmanager.h
+++ b/daemon/bankmanager.h
@@ -2,6 +2,7 @@
#define BANKMANAGER_H
#include "watchconnector.h"
+#include "uploadmanager.h"
#include "appmanager.h"
class BankManager : public QObject
@@ -10,11 +11,10 @@ class BankManager : public QObject
LOG4QT_DECLARE_QCLASS_LOGGER
public:
- explicit BankManager(WatchConnector *watch, AppManager *apps, QObject *parent = 0);
+ explicit BankManager(WatchConnector *watch, UploadManager *upload, AppManager *apps, QObject *parent = 0);
int numSlots() const;
-
signals:
void slotsChanged();
@@ -26,6 +26,7 @@ public slots:
private:
int findUnusedSlot() const;
+ void refreshWatchApp(int slot, std::function<void()> successCallback, std::function<void(int)> errorCallback);
private slots:
@@ -33,8 +34,16 @@ private slots:
private:
WatchConnector *watch;
+ UploadManager *upload;
AppManager *apps;
+ enum ResultCodes {
+ Success = 1,
+ BankInUse = 2,
+ InvalidCommand = 3,
+ GeneralFailure = 4
+ };
+
struct SlotInfo {
bool used;
quint32 id;
@@ -46,6 +55,7 @@ private:
};
QVector<SlotInfo> _slots;
+ QTimer *_refresh;
};
#endif // BANKMANAGER_H
diff --git a/daemon/daemon.pro b/daemon/daemon.pro
index 21ebffa..6eea288 100644
--- a/daemon/daemon.pro
+++ b/daemon/daemon.pro
@@ -29,7 +29,9 @@ SOURCES += \
appinfo.cpp \
jskitobjects.cpp \
packer.cpp \
- bankmanager.cpp
+ bankmanager.cpp \
+ uploadmanager.cpp \
+ stm32crc.cpp
HEADERS += \
manager.h \
@@ -48,7 +50,9 @@ HEADERS += \
appinfo.h \
jskitobjects.h \
packer.h \
- bankmanager.h
+ bankmanager.h \
+ uploadmanager.h \
+ stm32crc.h
OTHER_FILES += \
../log4qt-debug.conf \
diff --git a/daemon/manager.cpp b/daemon/manager.cpp
index 27bb870..136d7f3 100644
--- a/daemon/manager.cpp
+++ b/daemon/manager.cpp
@@ -10,8 +10,9 @@ Manager::Manager(Settings *settings, QObject *parent) :
proxy(new PebbledProxy(this)),
watch(new WatchConnector(this)),
dbus(new DBusConnector(this)),
+ upload(new UploadManager(watch, this)),
apps(new AppManager(this)),
- bank(new BankManager(watch, apps, this)),
+ bank(new BankManager(watch, upload, apps, this)),
voice(new VoiceCallManager(settings, this)),
notifications(new NotificationManager(settings, this)),
music(new MusicManager(watch, this)),
@@ -510,3 +511,14 @@ void PebbledProxy::UnloadApp(uint slot)
"Cannot unload application");
}
}
+
+void PebbledProxy::UploadApp(const QString &uuid, uint slot)
+{
+ Q_ASSERT(calledFromDBus());
+ const QDBusMessage msg = message();
+
+ if (!manager()->bank->uploadApp(QUuid(uuid), slot)) {
+ sendErrorReply(msg.interface() + ".Error.CannotUpload",
+ "Cannot upload application");
+ }
+}
diff --git a/daemon/manager.h b/daemon/manager.h
index 0e55190..f27da98 100644
--- a/daemon/manager.h
+++ b/daemon/manager.h
@@ -3,6 +3,7 @@
#include "watchconnector.h"
#include "dbusconnector.h"
+#include "uploadmanager.h"
#include "voicecallmanager.h"
#include "notificationmanager.h"
#include "musicmanager.h"
@@ -45,6 +46,7 @@ class Manager : public QObject, protected QDBusContext
WatchConnector *watch;
DBusConnector *dbus;
+ UploadManager *upload;
AppManager *apps;
BankManager *bank;
VoiceCallManager *voice;
@@ -146,6 +148,7 @@ public slots:
void SendAppConfigurationData(const QString &uuid, const QString &data);
void UnloadApp(uint slot);
+ void UploadApp(const QString &uuid, uint slot);
signals:
void NameChanged();
diff --git a/daemon/stm32crc.cpp b/daemon/stm32crc.cpp
new file mode 100644
index 0000000..dd09f38
--- /dev/null
+++ b/daemon/stm32crc.cpp
@@ -0,0 +1,119 @@
+#include "stm32crc.h"
+
+/** Precomputed CRC polynomial
+ * Generated by pycrc v0.8.2, http://www.tty1.net/pycrc/
+ * using the configuration:
+ * Width = 32
+ * Poly = 0x04c11db7
+ * XorIn = 0xffffffff
+ * ReflectIn = False
+ * XorOut = 0xffffffff
+ * ReflectOut = False
+ * Algorithm = table-driven
+ * Modified to use STM32-like word size
+ *****************************************************************************/
+static const quint32 crc_table[256] = {
+ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
+ 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
+ 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+ 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+ 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
+ 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+ 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
+ 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
+ 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
+ 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
+ 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+ 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
+ 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+ 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+ 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
+ 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
+ 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+ 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+ 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
+ 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+ 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
+ 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
+ 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
+ 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
+ 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+ 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
+ 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+ 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+ 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
+ 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
+ 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+ 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+ 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
+ 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+ 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
+ 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
+ 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
+ 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
+ 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+ 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
+ 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+ 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+ 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
+ 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
+ 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+ 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+ 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
+ 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+ 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
+ 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
+ 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
+ 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
+ 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+ 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
+ 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+ 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+ 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
+ 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
+ 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+ 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+Stm32Crc::Stm32Crc()
+{
+ reset();
+}
+
+void Stm32Crc::reset()
+{
+ crc = 0xFFFFFFFFU;
+}
+
+void Stm32Crc::addData(const char *data, int length)
+{
+ const int word_len = sizeof(quint32);
+ int i = 0;
+
+ Q_ASSERT(length % word_len == 0);
+ Q_ASSERT(quintptr(data) % word_len == 0);
+
+ for (; i < (length/word_len)*word_len; i+=word_len) {
+ const quint32 *word = reinterpret_cast<const quint32*>(&data[i]);
+
+ crc ^= *word;
+ crc = (crc << 8) ^ crc_table[(crc >> 24) & 0xFF];
+ crc = (crc << 8) ^ crc_table[(crc >> 24) & 0xFF];
+ crc = (crc << 8) ^ crc_table[(crc >> 24) & 0xFF];
+ crc = (crc << 8) ^ crc_table[(crc >> 24) & 0xFF];
+ }
+}
+
+void Stm32Crc::addData(const QByteArray &data)
+{
+ addData(data.constData(), data.length());
+}
+
+quint32 Stm32Crc::result() const
+{
+ return crc;
+}
diff --git a/daemon/stm32crc.h b/daemon/stm32crc.h
new file mode 100644
index 0000000..1361de3
--- /dev/null
+++ b/daemon/stm32crc.h
@@ -0,0 +1,24 @@
+#ifndef STM32CRC_H
+#define STM32CRC_H
+
+#include <QByteArray>
+
+class Stm32Crc
+{
+public:
+ Stm32Crc();
+
+ void reset();
+
+ // Data size must be multiple of 4, data must be aligned to 4.
+
+ void addData(const char *data, int length);
+ void addData(const QByteArray &data);
+
+ quint32 result() const;
+
+private:
+ quint32 crc;
+};
+
+#endif // STM32CRC_H
diff --git a/daemon/uploadmanager.cpp b/daemon/uploadmanager.cpp
new file mode 100644
index 0000000..89d70f7
--- /dev/null
+++ b/daemon/uploadmanager.cpp
@@ -0,0 +1,266 @@
+#include "uploadmanager.h"
+#include "unpacker.h"
+#include "packer.h"
+#include "stm32crc.h"
+
+static const int CHUNK_SIZE = 2000;
+using std::function;
+
+UploadManager::UploadManager(WatchConnector *watch, QObject *parent) :
+ QObject(parent), watch(watch), _lastUploadId(0), _state(StateNotStarted)
+{
+ watch->setEndpointHandler(WatchConnector::watchPUTBYTES,
+ [this](const QByteArray &msg) {
+ if (_pending.empty()) {
+ logger()->warn() << "putbytes message, but queue is empty!";
+ return false;
+ }
+ handleMessage(msg);
+ return true;
+ });
+}
+
+uint UploadManager::upload(WatchConnector::UploadType type, int index, QIODevice *device, int size,
+ function<void()> successCallback, function<void(int)> errorCallback)
+{
+ PendingUpload upload;
+ upload.id = ++_lastUploadId;
+ upload.type = type;
+ upload.index = index;
+ upload.device = device;
+ if (size < 0) {
+ upload.remaining = device->size();
+ } else {
+ upload.remaining = size;
+ }
+ upload.successCallback = successCallback;
+ upload.errorCallback = errorCallback;
+
+ if (upload.remaining <= 0) {
+ logger()->warn() << "upload is empty";
+ if (errorCallback) {
+ errorCallback(-1);
+ return -1;
+ }
+ }
+
+ _pending.enqueue(upload);
+
+ if (_pending.size() == 1) {
+ startNextUpload();
+ }
+
+ return upload.id;
+}
+
+void UploadManager::cancel(uint id, int code)
+{
+ if (_pending.empty()) {
+ logger()->warn() << "cannot cancel, empty queue";
+ return;
+ }
+
+ if (id == _pending.head().id) {
+ PendingUpload upload = _pending.dequeue();
+ logger()->debug() << "aborting current upload" << id << "(code:" << code << ")";
+
+ if (_state != StateNotStarted && _state != StateWaitForToken && _state != StateComplete) {
+ QByteArray msg;
+ Packer p(&msg);
+ p.write<quint8>(WatchConnector::putbytesABORT);
+ p.write<quint32>(_token);
+
+ logger()->debug() << "sending abort for upload" << id;
+
+ watch->sendMessage(WatchConnector::watchPUTBYTES, msg);
+ }
+
+ _state = StateNotStarted;
+ _token = 0;
+
+ if (upload.errorCallback) {
+ upload.errorCallback(code);
+ }
+
+ if (!_pending.empty()) {
+ startNextUpload();
+ }
+ } else {
+ for (int i = 1; i < _pending.size(); ++i) {
+ if (_pending[i].id == id) {
+ logger()->debug() << "cancelling upload" << id << "(code:" << code << ")";
+ if (_pending[i].errorCallback) {
+ _pending[i].errorCallback(code);
+ }
+ _pending.removeAt(i);
+ return;
+ }
+ }
+ logger()->warn() << "cannot cancel, id" << id << "not found";
+ }
+}
+
+void UploadManager::startNextUpload()
+{
+ Q_ASSERT(!_pending.empty());
+ Q_ASSERT(_state == StateNotStarted);
+
+ PendingUpload &upload = _pending.head();
+ QByteArray msg;
+ Packer p(&msg);
+ p.write<quint8>(WatchConnector::putbytesINIT);
+ p.write<quint32>(upload.remaining);
+ p.write<quint8>(upload.type);
+ p.write<quint8>(upload.index);
+
+ _state = StateWaitForToken;
+ watch->sendMessage(WatchConnector::watchPUTBYTES, msg);
+}
+
+void UploadManager::handleMessage(const QByteArray &msg)
+{
+ Q_ASSERT(!_pending.empty());
+ PendingUpload &upload = _pending.head();
+
+ logger()->debug() << "get message" << msg.toHex();
+
+ Unpacker u(msg);
+ int status = u.read<quint8>();
+
+ if (u.bad() || status != 1) {
+ logger()->warn() << "upload" << upload.id << "got error code=" << status;
+ cancel(upload.id, status);
+ return;
+ }
+
+ quint32 recv_token = u.read<quint32>();
+
+ if (u.bad()) {
+ logger()->warn() << "upload" << upload.id << ": could not read the token";
+ cancel(upload.id, -1);
+ return;
+ }
+
+ if (_state != StateNotStarted && _state != StateWaitForToken && _state != StateComplete) {
+ if (recv_token != _token) {
+ logger()->warn() << "upload" << upload.id << ": invalid token";
+ cancel(upload.id, -1);
+ return;
+ }
+ }
+
+ switch (_state) {
+ case StateNotStarted:
+ logger()->warn() << "got packet when upload is not started";
+ break;
+ case StateWaitForToken:
+ logger()->debug() << "token received";
+ _token = recv_token;
+ _state = StateInProgress;
+
+ /* fallthrough */
+ case StateInProgress:
+ logger()->debug() << "moving to the next chunk";
+ if (upload.remaining > 0) {
+ if (!uploadNextChunk(upload)) {
+ cancel(upload.id, -1);
+ return;
+ }
+ } else {
+ logger()->debug() << "no additional chunks, commit";
+ _state = StateCommit;
+ if (!commit(upload)) {
+ cancel(upload.id, -1);
+ return;
+ }
+ }
+ break;
+ case StateCommit:
+ logger()->debug() << "commited succesfully";
+ _state = StateComplete;
+ if (!complete(upload)) {
+ cancel(upload.id, -1);
+ return;
+ }
+ break;
+ case StateComplete:
+ logger()->debug() << "upload" << upload.id << "succesful, invoking callback";
+ if (upload.successCallback) {
+ upload.successCallback();
+ }
+ _pending.dequeue();
+ _token = 0;
+ _state = StateNotStarted;
+ if (!_pending.empty()) {
+ startNextUpload();
+ }
+ break;
+ }
+}
+
+bool UploadManager::uploadNextChunk(PendingUpload &upload)
+{
+ QByteArray chunk = upload.device->read(qMin<int>(upload.remaining, CHUNK_SIZE));
+
+ if (upload.remaining < CHUNK_SIZE && chunk.size() < upload.remaining) {
+ // Short read!
+ logger()->warn() << "short read during upload" << upload.id;
+ return false;
+ }
+
+ Q_ASSERT(!chunk.isEmpty());
+ Q_ASSERT(_state = StateInProgress);
+
+ QByteArray msg;
+ Packer p(&msg);
+ p.write<quint8>(WatchConnector::putbytesSEND);
+ p.write<quint32>(_token);
+ p.write<quint32>(chunk.size());
+ msg.append(chunk);
+
+ logger()->debug() << "sending a chunk of" << chunk.size() << "bytes";
+
+ watch->sendMessage(WatchConnector::watchPUTBYTES, msg);
+
+ upload.remaining -= chunk.size();
+ upload.crc.addData(chunk);
+
+ logger()->debug() << "remaining" << upload.remaining << "bytes";
+
+ return true;
+}
+
+bool UploadManager::commit(PendingUpload &upload)
+{
+ Q_ASSERT(_state == StateCommit);
+ Q_ASSERT(upload.remaining == 0);
+
+ QByteArray msg;
+ Packer p(&msg);
+ p.write<quint8>(WatchConnector::putbytesCOMMIT);
+ p.write<quint32>(_token);
+ p.write<quint32>(upload.crc.result());
+
+ logger()->debug() << "commiting upload" << upload.id
+ << "with crc" << qPrintable(QString("0x%1").arg(upload.crc.result(), 0, 16));
+
+ watch->sendMessage(WatchConnector::watchPUTBYTES, msg);
+
+ return true;
+}
+
+bool UploadManager::complete(PendingUpload &upload)
+{
+ Q_ASSERT(_state == StateComplete);
+
+ QByteArray msg;
+ Packer p(&msg);
+ p.write<quint8>(WatchConnector::putbytesCOMPLETE);
+ p.write<quint32>(_token);
+
+ logger()->debug() << "completing upload" << upload.id;
+
+ watch->sendMessage(WatchConnector::watchPUTBYTES, msg);
+
+ return true;
+}
diff --git a/daemon/uploadmanager.h b/daemon/uploadmanager.h
new file mode 100644
index 0000000..1d42237
--- /dev/null
+++ b/daemon/uploadmanager.h
@@ -0,0 +1,65 @@
+#ifndef UPLOADMANAGER_H
+#define UPLOADMANAGER_H
+
+#include <functional>
+#include <QQueue>
+#include "watchconnector.h"
+#include "stm32crc.h"
+
+class UploadManager : public QObject
+{
+ Q_OBJECT
+ LOG4QT_DECLARE_QCLASS_LOGGER
+
+public:
+ explicit UploadManager(WatchConnector *watch, QObject *parent = 0);
+
+ typedef std::function<void()> Callback;
+
+ uint upload(WatchConnector::UploadType type, int index, QIODevice *device, int size = -1,
+ std::function<void()> successCallback = std::function<void()>(),
+ std::function<void(int)> errorCallback = std::function<void(int)>());
+ void cancel(uint id, int code = 0);
+
+signals:
+
+public slots:
+
+
+private:
+ enum State {
+ StateNotStarted,
+ StateWaitForToken,
+ StateInProgress,
+ StateCommit,
+ StateComplete
+ };
+
+ struct PendingUpload {
+ uint id;
+
+ WatchConnector::UploadType type;
+ int index;
+ QIODevice *device;
+ int remaining;
+ Stm32Crc crc;
+
+ std::function<void()> successCallback;
+ std::function<void(int)> errorCallback;
+ };
+
+ void startNextUpload();
+ void handleMessage(const QByteArray &msg);
+ bool uploadNextChunk(PendingUpload &upload);
+ bool commit(PendingUpload &upload);
+ bool complete(PendingUpload &upload);
+
+private:
+ WatchConnector *watch;
+ QQueue<PendingUpload> _pending;
+ uint _lastUploadId;
+ State _state;
+ quint32 _token;
+};
+
+#endif // UPLOADMANAGER_H
diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h
index a5fe1ea..1aaf39d 100644
--- a/daemon/watchconnector.h
+++ b/daemon/watchconnector.h
@@ -108,6 +108,7 @@ public:
enum AppManager {
appmgrGET_APPBANK_STATUS = 1,
appmgrREMOVE_APP = 2,
+ appmgrREFRESH_APP = 3,
appmgrGET_APPBANK_UUIDS = 5
};
enum AppMessage {
@@ -154,8 +155,19 @@ public:
osLINUX = 4,
osWINDOWS = 5
};
- enum {
- DEFAULT_TIMEOUT_MSECS = 100
+ enum UploadType {
+ uploadFIRMWARE = 1,
+ uploadRECOVERY = 2,
+ uploadSYS_RESOURCES = 3,
+ uploadRESOURCES = 4,
+ uploadBINARY = 5
+ };
+ enum PutBytesCommand {
+ putbytesINIT = 1,
+ putbytesSEND = 2,
+ putbytesCOMMIT = 3,
+ putbytesABORT = 4,
+ putbytesCOMPLETE = 5
};
typedef QMap<int, QVariant> Dict;