summaryrefslogtreecommitdiffstats
path: root/tools/runonphone/symbianutils/codadevice.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/runonphone/symbianutils/codadevice.cpp')
-rw-r--r--tools/runonphone/symbianutils/codadevice.cpp1501
1 files changed, 1501 insertions, 0 deletions
diff --git a/tools/runonphone/symbianutils/codadevice.cpp b/tools/runonphone/symbianutils/codadevice.cpp
new file mode 100644
index 0000000..751f84e
--- /dev/null
+++ b/tools/runonphone/symbianutils/codadevice.cpp
@@ -0,0 +1,1501 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "codadevice.h"
+#include "json.h"
+#include "trkutils.h"
+
+#include <QtNetwork/QAbstractSocket>
+#include <QtCore/QDebug>
+#include <QtCore/QVector>
+#include <QtCore/QQueue>
+#include <QtCore/QTextStream>
+#include <QtCore/QDateTime>
+#include <QtCore/QFileInfo>
+
+enum { debug = 0 };
+
+static const char tcpMessageTerminatorC[] = "\003\001";
+
+// Serial Ping: 0xfc,0x1f
+static const char serialPingC[] = "\xfc\x1f";
+// Serial Pong: 0xfc,0xf1, followed by version info
+static const char serialPongC[] = "\xfc\xf1";
+
+static const char locatorAnswerC[] = "E\0Locator\0Hello\0[\"Locator\"]";
+
+/* Serial messages > (1K - 2) have to chunked in order to pass the USB
+ * router as '0xfe char(chunkCount - 1) data' ... '0x0 char(chunkCount - 2) data'
+ * ... '0x0 0x0 last-data' */
+static const unsigned serialChunkLength = 0x400; // 1K max USB router
+static const int maxSerialMessageLength = 0x10000; // given chunking scheme
+
+static const char validProtocolIdStart = (char)0x90;
+static const char validProtocolIdEnd = (char)0x95;
+static const char codaProtocolId = (char)0x92;
+static const unsigned char serialChunkingStart = 0xfe;
+static const unsigned char serialChunkingContinuation = 0x0;
+enum { SerialChunkHeaderSize = 2 };
+
+// Create USB router frame
+static inline void encodeSerialFrame(const QByteArray &data, QByteArray *target, char protocolId)
+{
+ target->append(char(0x01));
+ target->append(protocolId);
+ appendShort(target, ushort(data.size()), trk::BigEndian);
+ target->append(data);
+}
+
+// Split in chunks of 1K according to CODA protocol chunking
+static inline QByteArray encodeUsbSerialMessage(const QByteArray &dataIn)
+{
+ // Reserve 2 header bytes
+ static const int chunkSize = serialChunkLength - SerialChunkHeaderSize;
+ const int size = dataIn.size();
+ QByteArray frame;
+ // Do we need to split?
+ if (size < chunkSize) { // Nope, all happy.
+ frame.reserve(size + 4);
+ encodeSerialFrame(dataIn, &frame, codaProtocolId);
+ return frame;
+ }
+ // Split.
+ unsigned chunkCount = size / chunkSize;
+ if (size % chunkSize)
+ chunkCount++;
+ if (debug)
+ qDebug("Serial: Splitting message of %d bytes into %u chunks of %d", size, chunkCount, chunkSize);
+
+ frame.reserve((4 + serialChunkLength) * chunkCount);
+ int pos = 0;
+ for (unsigned c = chunkCount - 1; pos < size ; c--) {
+ QByteArray chunk; // chunk with long message start/continuation code
+ chunk.reserve(serialChunkLength);
+ chunk.append(pos ? serialChunkingContinuation : serialChunkingStart);
+ chunk.append(char(static_cast<unsigned char>(c))); // Avoid any signedness issues.
+ const int chunkEnd = qMin(pos + chunkSize, size);
+ chunk.append(dataIn.mid(pos, chunkEnd - pos));
+ encodeSerialFrame(chunk, &frame, codaProtocolId);
+ pos = chunkEnd;
+ }
+ if (debug > 1)
+ qDebug("Serial chunked:\n%s", qPrintable(Coda::formatData(frame)));
+ return frame;
+}
+
+namespace Coda {
+// ------------- CodaCommandError
+
+CodaCommandError::CodaCommandError() : timeMS(0), code(0), alternativeCode(0)
+{
+}
+
+void CodaCommandError::clear()
+{
+ timeMS = 0;
+ code = alternativeCode = 0;
+ format.clear();
+ alternativeOrganization.clear();
+}
+
+QDateTime CodaCommandResult::tcfTimeToQDateTime(quint64 tcfTimeMS)
+{
+ const QDateTime time(QDate(1970, 1, 1));
+ return time.addMSecs(tcfTimeMS);
+}
+
+void CodaCommandError::write(QTextStream &str) const
+{
+ if (isError()) {
+ if (debug && timeMS)
+ str << CodaCommandResult::tcfTimeToQDateTime(timeMS).toString(Qt::ISODate) << ": ";
+ str << "'" << format << '\'' //for symbian the format is the real error message
+ << " Code: " << code;
+ if (!alternativeOrganization.isEmpty())
+ str << " ('" << alternativeOrganization << "', code: " << alternativeCode << ')';
+ } else{
+ str << "<No error>";
+ }
+}
+
+QString CodaCommandError::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ write(str);
+ return rc;
+}
+
+bool CodaCommandError::isError() const
+{
+ return timeMS != 0 || code != 0 || !format.isEmpty() || alternativeCode != 0;
+}
+
+/* {"Time":1277459762255,"Code":1,"AltCode":-6,"AltOrg":"POSIX","Format":"Unknown error: -6"} */
+bool CodaCommandError::parse(const QVector<JsonValue> &values)
+{
+ // Parse an arbitrary hash (that could as well be a command response)
+ // and check for error elements. It looks like sometimes errors are appended
+ // to other values.
+ unsigned errorKeyCount = 0;
+ clear();
+ do {
+ if (values.isEmpty())
+ break;
+ // Errors are mostly appended, except for FileSystem::open, in which case
+ // a string "null" file handle (sic!) follows the error.
+ const int last = values.size() - 1;
+ const int checkIndex = last == 1 && values.at(last).data() == "null" ?
+ last - 1 : last;
+ if (values.at(checkIndex).type() != JsonValue::Object)
+ break;
+ foreach (const JsonValue &c, values.at(checkIndex).children()) {
+ if (c.name() == "Time") {
+ timeMS = c.data().toULongLong();
+ errorKeyCount++;
+ } else if (c.name() == "Code") {
+ code = c.data().toLongLong();
+ errorKeyCount++;
+ } else if (c.name() == "Format") {
+ format = c.data();
+ errorKeyCount++;
+ } else if (c.name() == "AltCode") {
+ alternativeCode = c.data().toULongLong();
+ errorKeyCount++;
+ } else if (c.name() == "AltOrg") {
+ alternativeOrganization = c.data();
+ errorKeyCount++;
+ }
+ }
+ } while (false);
+ const bool errorFound = errorKeyCount >= 2u; // Should be at least 'Time', 'Code'.
+ if (!errorFound)
+ clear();
+ if (debug) {
+ qDebug("CodaCommandError::parse: Found error %d (%u): ", errorFound, errorKeyCount);
+ if (!values.isEmpty())
+ qDebug() << values.back().toString();
+ }
+ return errorFound;
+}
+
+// ------------ CodaCommandResult
+
+CodaCommandResult::CodaCommandResult(Type t) :
+ type(t), service(LocatorService)
+{
+}
+
+CodaCommandResult::CodaCommandResult(char typeChar, Services s,
+ const QByteArray &r,
+ const QVector<JsonValue> &v,
+ const QVariant &ck) :
+ type(FailReply), service(s), request(r), values(v), cookie(ck)
+{
+ switch (typeChar) {
+ case 'N':
+ type = FailReply;
+ break;
+ case 'P':
+ type = ProgressReply;
+ break;
+ case 'R':
+ type = commandError.parse(values) ? CommandErrorReply : SuccessReply;
+ break;
+ default:
+ qWarning("Unknown TCF reply type '%c'", typeChar);
+ }
+}
+
+QString CodaCommandResult::errorString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+
+ switch (type) {
+ case SuccessReply:
+ case ProgressReply:
+ str << "<No error>";
+ return rc;
+ case FailReply:
+ str << "NAK";
+ return rc;
+ case CommandErrorReply:
+ commandError.write(str);
+ break;
+ }
+ if (debug) {
+ // Append the failed command for reference
+ str << " (Command was: '";
+ QByteArray printableRequest = request;
+ printableRequest.replace('\0', '|');
+ str << printableRequest << "')";
+ }
+ return rc;
+}
+
+QString CodaCommandResult::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << "Command answer ";
+ switch (type) {
+ case SuccessReply:
+ str << "[success]";
+ break;
+ case CommandErrorReply:
+ str << "[command error]";
+ break;
+ case FailReply:
+ str << "[fail (NAK)]";
+ break;
+ case ProgressReply:
+ str << "[progress]";
+ break;
+ }
+ str << ", " << values.size() << " values(s) to request: '";
+ QByteArray printableRequest = request;
+ printableRequest.replace('\0', '|');
+ str << printableRequest << "' ";
+ if (cookie.isValid())
+ str << " cookie: " << cookie.toString();
+ str << '\n';
+ for (int i = 0, count = values.size(); i < count; i++)
+ str << '#' << i << ' ' << values.at(i).toString() << '\n';
+ if (type == CommandErrorReply)
+ str << "Error: " << errorString();
+ return rc;
+}
+
+CodaStatResponse::CodaStatResponse() : size(0)
+{
+}
+
+struct CodaSendQueueEntry
+{
+ typedef CodaDevice::MessageType MessageType;
+
+ explicit CodaSendQueueEntry(MessageType mt,
+ int tok,
+ Services s,
+ const QByteArray &d,
+ const CodaCallback &cb= CodaCallback(),
+ const QVariant &ck = QVariant()) :
+ messageType(mt), service(s), data(d), token(tok), cookie(ck), callback(cb) {}
+
+ MessageType messageType;
+ Services service;
+ QByteArray data;
+ int token;
+ QVariant cookie;
+ CodaCallback callback;
+ unsigned specialHandling;
+};
+
+struct CodaDevicePrivate {
+ typedef CodaDevice::IODevicePtr IODevicePtr;
+ typedef QHash<int, CodaSendQueueEntry> TokenWrittenMessageMap;
+
+ CodaDevicePrivate();
+
+ const QByteArray m_tcpMessageTerminator;
+
+ IODevicePtr m_device;
+ unsigned m_verbose;
+ QByteArray m_readBuffer;
+ QByteArray m_serialBuffer; // for chunked messages
+ int m_token;
+ QQueue<CodaSendQueueEntry> m_sendQueue;
+ TokenWrittenMessageMap m_writtenMessages;
+ QVector<QByteArray> m_registerNames;
+ QVector<QByteArray> m_fakeGetMRegisterValues;
+ bool m_serialFrame;
+ bool m_serialPingOnly;
+};
+
+CodaDevicePrivate::CodaDevicePrivate() :
+ m_tcpMessageTerminator(tcpMessageTerminatorC),
+ m_verbose(0), m_token(0), m_serialFrame(false), m_serialPingOnly(false)
+{
+}
+
+CodaDevice::CodaDevice(QObject *parent) :
+ QObject(parent), d(new CodaDevicePrivate)
+{
+ if (debug) setVerbose(true);
+}
+
+CodaDevice::~CodaDevice()
+{
+ delete d;
+}
+
+QVector<QByteArray> CodaDevice::registerNames() const
+{
+ return d->m_registerNames;
+}
+
+void CodaDevice::setRegisterNames(const QVector<QByteArray>& n)
+{
+ d->m_registerNames = n;
+ if (d->m_verbose) {
+ QString msg;
+ QTextStream str(&msg);
+ const int count = n.size();
+ str << "Registers (" << count << "): ";
+ for (int i = 0; i < count; i++)
+ str << '#' << i << '=' << n.at(i) << ' ';
+ emitLogMessage(msg);
+ }
+}
+
+CodaDevice::IODevicePtr CodaDevice::device() const
+{
+ return d->m_device;
+}
+
+CodaDevice::IODevicePtr CodaDevice::takeDevice()
+{
+ const IODevicePtr old = d->m_device;
+ if (!old.isNull()) {
+ old.data()->disconnect(this);
+ d->m_device = IODevicePtr();
+ }
+ d->m_readBuffer.clear();
+ d->m_token = 0;
+ d->m_sendQueue.clear();
+ return old;
+}
+
+void CodaDevice::setDevice(const IODevicePtr &dp)
+{
+ if (dp.data() == d->m_device.data())
+ return;
+ if (dp.isNull()) {
+ emitLogMessage(QLatin1String("Internal error: Attempt to set NULL device."));
+ return;
+ }
+ takeDevice();
+ d->m_device = dp;
+ connect(dp.data(), SIGNAL(readyRead()), this, SLOT(slotDeviceReadyRead()));
+ if (QAbstractSocket *s = qobject_cast<QAbstractSocket *>(dp.data())) {
+ connect(s, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotDeviceError()));
+ connect(s, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(slotDeviceSocketStateChanged()));
+ }
+}
+
+void CodaDevice::slotDeviceError()
+{
+ const QString message = d->m_device->errorString();
+ emitLogMessage(message);
+ emit error(message);
+}
+
+void CodaDevice::slotDeviceSocketStateChanged()
+{
+ if (const QAbstractSocket *s = qobject_cast<const QAbstractSocket *>(d->m_device.data())) {
+ const QAbstractSocket::SocketState st = s->state();
+ switch (st) {
+ case QAbstractSocket::UnconnectedState:
+ emitLogMessage(QLatin1String("Unconnected"));
+ break;
+ case QAbstractSocket::HostLookupState:
+ emitLogMessage(QLatin1String("HostLookupState"));
+ break;
+ case QAbstractSocket::ConnectingState:
+ emitLogMessage(QLatin1String("Connecting"));
+ break;
+ case QAbstractSocket::ConnectedState:
+ emitLogMessage(QLatin1String("Connected"));
+ break;
+ case QAbstractSocket::ClosingState:
+ emitLogMessage(QLatin1String("Closing"));
+ break;
+ default:
+ emitLogMessage(QString::fromLatin1("State %1").arg(st));
+ break;
+ }
+ }
+}
+
+static inline QString debugMessage(QByteArray message, const char *prefix = 0)
+{
+ const bool isBinary = !message.isEmpty() && message.at(0) < 0;
+ if (isBinary) {
+ message = message.toHex(); // Some serial special message
+ } else {
+ message.replace('\0', '|');
+ }
+ const QString messageS = QString::fromLatin1(message);
+ return prefix ?
+ (QLatin1String(prefix) + messageS) : messageS;
+}
+
+void CodaDevice::slotDeviceReadyRead()
+{
+ const QByteArray newData = d->m_device->readAll();
+ d->m_readBuffer += newData;
+ if (debug)
+ qDebug("ReadBuffer: %s", qPrintable(trk::stringFromArray(newData)));
+ if (d->m_serialFrame) {
+ deviceReadyReadSerial();
+ } else {
+ deviceReadyReadTcp();
+ }
+}
+
+// Find a serial header in input stream '0x1', '0x92', 'lenH', 'lenL'
+// and return message position and size.
+QPair<int, int> CodaDevice::findSerialHeader(QByteArray &in)
+{
+ static const char header1 = 0x1;
+ // Header should in theory always be at beginning of
+ // buffer. Warn if there are bogus data in-between.
+
+ while (in.size() >= 4) {
+ if (in.at(0) == header1 && in.at(1) == codaProtocolId) {
+ // Good packet
+ const int length = trk::extractShort(in.constData() + 2);
+ return QPair<int, int>(4, length);
+ } else if (in.at(0) == header1 && in.at(1) >= validProtocolIdStart && in.at(1) <= validProtocolIdEnd) {
+ // We recognise it but it's not a TCF message - emit it for any interested party to handle
+ const int length = trk::extractShort(in.constData() + 2);
+ if (4 + length <= in.size()) {
+ // We have all the data
+ QByteArray data(in.mid(4, length));
+ emit unknownEvent(in.at(1), data);
+ in.remove(0, 4+length);
+ // and continue
+ } else {
+ // If we don't have all this packet, there can't be any data following it, so return now
+ // and wait for more data
+ return QPair<int, int>(-1, -1);
+ }
+ } else {
+ // Bad data - log it, remove it, and go round again
+ int nextHeader = in.indexOf(header1, 1);
+ QByteArray bad = in.mid(0, nextHeader);
+ qWarning("Bogus data received on serial line: %s\n"
+ "Frame Header at: %d", qPrintable(trk::stringFromArray(bad)), nextHeader);
+ in.remove(0, bad.length());
+ // and continue
+ }
+ }
+ return QPair<int, int>(-1, -1); // No more data, or not enough for a complete header
+}
+
+void CodaDevice::deviceReadyReadSerial()
+{
+ do {
+ // Extract message (pos,len)
+ const QPair<int, int> messagePos = findSerialHeader(d->m_readBuffer);
+ if (messagePos.first < 0)
+ break;
+ // Do we have the complete message?
+ const int messageEnd = messagePos.first + messagePos.second;
+ if (messageEnd > d->m_readBuffer.size())
+ break;
+ processSerialMessage(d->m_readBuffer.mid(messagePos.first, messagePos.second));
+ d->m_readBuffer.remove(0, messageEnd);
+ } while (!d->m_readBuffer.isEmpty());
+ checkSendQueue(); // Send off further messages
+}
+
+void CodaDevice::processSerialMessage(const QByteArray &message)
+{
+ if (debug > 1)
+ qDebug("Serial message: %s",qPrintable(trk::stringFromArray(message)));
+ if (message.isEmpty())
+ return;
+ // Is thing a ping/pong response
+ const int size = message.size();
+ if (message.startsWith(serialPongC)) {
+ const QString version = QString::fromLatin1(message.mid(sizeof(serialPongC) - 1));
+ emitLogMessage(QString::fromLatin1("Serial connection from '%1'").arg(version));
+ emit serialPong(version);
+ // Answer with locator.
+ if (!d->m_serialPingOnly)
+ writeMessage(QByteArray(locatorAnswerC, sizeof(locatorAnswerC)));
+ return;
+ }
+ // Check for long message (see top, '0xfe #number, data' or '0x0 #number, data')
+ // TODO: This is currently untested.
+ const unsigned char *dataU = reinterpret_cast<const unsigned char *>(message.constData());
+ const bool isLongMessageStart = size > SerialChunkHeaderSize
+ && *dataU == serialChunkingStart;
+ const bool isLongMessageContinuation = size > SerialChunkHeaderSize
+ && *dataU == serialChunkingContinuation;
+ if (isLongMessageStart || isLongMessageContinuation) {
+ const unsigned chunkNumber = *++dataU;
+ if (isLongMessageStart) { // Start new buffer
+ d->m_serialBuffer.clear();
+ d->m_serialBuffer.reserve( (chunkNumber + 1) * serialChunkLength);
+ }
+ d->m_serialBuffer.append(message.mid(SerialChunkHeaderSize, size - SerialChunkHeaderSize));
+ // Last chunk? - Process
+ if (!chunkNumber) {
+ processMessage(d->m_serialBuffer);
+ d->m_serialBuffer.clear();
+ d->m_serialBuffer.squeeze();
+ }
+ } else {
+ processMessage(message); // Normal, unchunked message
+ }
+}
+
+void CodaDevice::deviceReadyReadTcp()
+{
+ // Take complete message off front of readbuffer.
+ do {
+ const int messageEndPos = d->m_readBuffer.indexOf(d->m_tcpMessageTerminator);
+ if (messageEndPos == -1)
+ break;
+ if (messageEndPos == 0) {
+ // TCF TRK 4.0.5 emits empty messages on errors.
+ emitLogMessage(QString::fromLatin1("An empty TCF TRK message has been received."));
+ } else {
+ processMessage(d->m_readBuffer.left(messageEndPos));
+ }
+ d->m_readBuffer.remove(0, messageEndPos + d->m_tcpMessageTerminator.size());
+ } while (!d->m_readBuffer.isEmpty());
+ checkSendQueue(); // Send off further messages
+}
+
+void CodaDevice::processMessage(const QByteArray &message)
+{
+ if (debug)
+ qDebug("Read %d bytes:\n%s", message.size(), qPrintable(formatData(message)));
+ if (const int errorCode = parseMessage(message)) {
+ emitLogMessage(QString::fromLatin1("Parse error %1 : %2").
+ arg(errorCode).arg(debugMessage(message)));
+ if (debug)
+ qDebug("Parse error %d for %d bytes:\n%s", errorCode,
+ message.size(), qPrintable(formatData(message)));
+ }
+}
+
+// Split \0-terminated message into tokens, skipping the initial type character
+static inline QVector<QByteArray> splitMessage(const QByteArray &message)
+{
+ QVector<QByteArray> tokens;
+ tokens.reserve(7);
+ const int messageSize = message.size();
+ for (int pos = 2; pos < messageSize; ) {
+ const int nextPos = message.indexOf('\0', pos);
+ if (nextPos == -1)
+ break;
+ tokens.push_back(message.mid(pos, nextPos - pos));
+ pos = nextPos + 1;
+ }
+ return tokens;
+}
+
+int CodaDevice::parseMessage(const QByteArray &message)
+{
+ if (d->m_verbose)
+ emitLogMessage(debugMessage(message, "TCF ->"));
+ // Special JSON parse error message or protocol format error.
+ // The port is usually closed after receiving it.
+ // "\3\2{"Time":1276096098255,"Code":3,"Format": "Protocol format error"}"
+ if (message.startsWith("\003\002")) {
+ QByteArray text = message.mid(2);
+ const QString errorMessage = QString::fromLatin1("Parse error received: %1").arg(QString::fromAscii(text));
+ emit error(errorMessage);
+ return 0;
+ }
+ if (message.size() < 4 || message.at(1) != '\0')
+ return 1;
+ // Split into tokens
+ const char type = message.at(0);
+ const QVector<QByteArray> tokens = splitMessage(message);
+ switch (type) {
+ case 'E':
+ return parseTcfEvent(tokens);
+ case 'R': // Command replies
+ case 'N':
+ case 'P':
+ return parseTcfCommandReply(type, tokens);
+ default:
+ emitLogMessage(QString::fromLatin1("Unhandled message type: %1").arg(debugMessage(message)));
+ return 756;
+ }
+ return 0;
+}
+
+int CodaDevice::parseTcfCommandReply(char type, const QVector<QByteArray> &tokens)
+{
+ typedef CodaDevicePrivate::TokenWrittenMessageMap::iterator TokenWrittenMessageMapIterator;
+ // Find the corresponding entry in the written messages hash.
+ const int tokenCount = tokens.size();
+ if (tokenCount < 1)
+ return 234;
+ bool tokenOk;
+ const int token = tokens.at(0).toInt(&tokenOk);
+ if (!tokenOk)
+ return 235;
+ const TokenWrittenMessageMapIterator it = d->m_writtenMessages.find(token);
+ if (it == d->m_writtenMessages.end()) {
+ qWarning("CodaDevice: Internal error: token %d not found for '%s'",
+ token, qPrintable(joinByteArrays(tokens)));
+ return 236;
+ }
+ // No callback: remove entry from map, happy
+ const unsigned specialHandling = it.value().specialHandling;
+ if (!it.value().callback && specialHandling == 0u) {
+ d->m_writtenMessages.erase(it);
+ return 0;
+ }
+ // Parse values into JSON
+ QVector<JsonValue> values;
+ values.reserve(tokenCount);
+ for (int i = 1; i < tokenCount; i++) {
+ if (!tokens.at(i).isEmpty()) { // Strange: Empty tokens occur.
+ const JsonValue value(tokens.at(i));
+ if (value.isValid()) {
+ values.push_back(value);
+ } else {
+ qWarning("JSON parse error for reply to command token %d: #%d '%s'",
+ token, i, tokens.at(i).constData());
+ d->m_writtenMessages.erase(it);
+ return -1;
+ }
+ }
+ }
+ // Construct result and invoke callback, remove entry from map.
+ CodaCommandResult result(type, it.value().service, it.value().data,
+ values, it.value().cookie);
+
+ if (it.value().callback)
+ it.value().callback(result);
+ d->m_writtenMessages.erase(it);
+ return 0;
+}
+
+int CodaDevice::parseTcfEvent(const QVector<QByteArray> &tokens)
+{
+ // Event: Ignore the periodical heartbeat event, answer 'Hello',
+ // emit signal for the rest
+ if (tokens.size() < 3)
+ return 433;
+ const Services service = serviceFromName(tokens.at(0).constData());
+ if (service == LocatorService && tokens.at(1) == "peerHeartBeat")
+ return 0;
+ QVector<JsonValue> values;
+ for (int i = 2; i < tokens.size(); i++) {
+ const JsonValue value(tokens.at(i));
+ if (!value.isValid())
+ return 434;
+ values.push_back(value);
+ }
+ // Parse known events, emit signals
+ QScopedPointer<CodaEvent> knownEvent(CodaEvent::parseEvent(service, tokens.at(1), values));
+ if (!knownEvent.isNull()) {
+ // Answer hello event (WLAN)
+ if (knownEvent->type() == CodaEvent::LocatorHello)
+ if (!d->m_serialFrame)
+ writeMessage(QByteArray(locatorAnswerC, sizeof(locatorAnswerC)));
+ emit tcfEvent(*knownEvent);
+ }
+ emit genericTcfEvent(service, tokens.at(1), values);
+
+ if (debug || d->m_verbose) {
+ QString msg;
+ QTextStream str(&msg);
+ if (knownEvent.isNull()) {
+ str << "Event: " << tokens.at(0) << ' ' << tokens.at(1) << '\n';
+ foreach(const JsonValue &val, values)
+ str << " " << val.toString() << '\n';
+ } else {
+ str << knownEvent->toString();
+ }
+ emitLogMessage(msg);
+ }
+
+ return 0;
+}
+
+unsigned CodaDevice::verbose() const
+{
+ return d->m_verbose;
+}
+
+bool CodaDevice::serialFrame() const
+{
+ return d->m_serialFrame;
+}
+
+void CodaDevice::setSerialFrame(bool s)
+{
+ d->m_serialFrame = s;
+}
+
+void CodaDevice::setVerbose(unsigned v)
+{
+ d->m_verbose = v;
+}
+
+void CodaDevice::emitLogMessage(const QString &m)
+{
+ if (debug)
+ qWarning("%s", qPrintable(m));
+ emit logMessage(m);
+}
+
+bool CodaDevice::checkOpen()
+{
+ if (d->m_device.isNull()) {
+ emitLogMessage(QLatin1String("Internal error: No device set on CodaDevice."));
+ return false;
+ }
+ if (!d->m_device->isOpen()) {
+ emitLogMessage(QLatin1String("Internal error: Device not open in CodaDevice."));
+ return false;
+ }
+ return true;
+}
+
+void CodaDevice::sendSerialPing(bool pingOnly)
+{
+ if (!checkOpen())
+ return;
+
+ d->m_serialPingOnly = pingOnly;
+ setSerialFrame(true);
+ writeMessage(QByteArray(serialPingC, qstrlen(serialPingC)), false);
+ if (d->m_verbose)
+ emitLogMessage(QLatin1String("Ping..."));
+}
+
+void CodaDevice::sendCodaMessage(MessageType mt, Services service, const char *command,
+ const char *commandParameters, // may contain '\0'
+ int commandParametersLength,
+ const CodaCallback &callBack,
+ const QVariant &cookie)
+
+{
+ if (!checkOpen())
+ return;
+ // Format the message
+ const int token = d->m_token++;
+ QByteArray data;
+ data.reserve(30 + commandParametersLength);
+ data.append('C');
+ data.append('\0');
+ data.append(QByteArray::number(token));
+ data.append('\0');
+ data.append(serviceName(service));
+ data.append('\0');
+ data.append(command);
+ data.append('\0');
+ if (commandParametersLength)
+ data.append(commandParameters, commandParametersLength);
+ const CodaSendQueueEntry entry(mt, token, service, data, callBack, cookie);
+ d->m_sendQueue.enqueue(entry);
+ checkSendQueue();
+}
+
+void CodaDevice::sendCodaMessage(MessageType mt, Services service, const char *command,
+ const QByteArray &commandParameters,
+ const CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ sendCodaMessage(mt, service, command, commandParameters.constData(), commandParameters.size(),
+ callBack, cookie);
+}
+
+// Enclose in message frame and write.
+void CodaDevice::writeMessage(QByteArray data, bool ensureTerminating0)
+{
+ if (!checkOpen())
+ return;
+
+ if (d->m_serialFrame && data.size() > maxSerialMessageLength) {
+ qCritical("Attempt to send large message (%d bytes) exceeding the "
+ "limit of %d bytes over serial channel. Skipping.",
+ data.size(), maxSerialMessageLength);
+ return;
+ }
+
+ if (d->m_verbose)
+ emitLogMessage(debugMessage(data, "TCF <-"));
+
+ // Ensure \0-termination which easily gets lost in QByteArray CT.
+ if (ensureTerminating0 && !data.endsWith('\0'))
+ data.append('\0');
+ if (d->m_serialFrame) {
+ data = encodeUsbSerialMessage(data);
+ } else {
+ data += d->m_tcpMessageTerminator;
+ }
+
+ if (debug > 1)
+ qDebug("Writing:\n%s", qPrintable(formatData(data)));
+
+ int result = d->m_device->write(data);
+ if (result < data.length())
+ qWarning("Failed to write all data! result=%d", result);
+ if (QAbstractSocket *as = qobject_cast<QAbstractSocket *>(d->m_device.data()))
+ as->flush();
+}
+
+void CodaDevice::writeCustomData(char protocolId, const QByteArray &data)
+{
+ if (!checkOpen())
+ return;
+
+ if (!d->m_serialFrame) {
+ qWarning("Ignoring request to send data to non-serial CodaDevice");
+ return;
+ }
+ if (data.length() > 0xFFFF) {
+ qWarning("Ignoring request to send too large packet, of size %d", data.length());
+ return;
+ }
+ QByteArray framedData;
+ encodeSerialFrame(data, &framedData, protocolId);
+ device()->write(framedData);
+}
+
+void CodaDevice::checkSendQueue()
+{
+ // Fire off messages or invoke noops until a message with reply is found
+ // and an entry to writtenMessages is made.
+ while (d->m_writtenMessages.empty()) {
+ if (d->m_sendQueue.isEmpty())
+ break;
+ CodaSendQueueEntry entry = d->m_sendQueue.dequeue();
+ switch (entry.messageType) {
+ case MessageWithReply:
+ d->m_writtenMessages.insert(entry.token, entry);
+ writeMessage(entry.data);
+ break;
+ case MessageWithoutReply:
+ writeMessage(entry.data);
+ break;
+ case NoopMessage: // Invoke the noop-callback for synchronization
+ if (entry.callback) {
+ CodaCommandResult noopResult(CodaCommandResult::SuccessReply);
+ noopResult.cookie = entry.cookie;
+ entry.callback(noopResult);
+ }
+ break;
+ }
+ }
+}
+
+// Fix slashes
+static inline QString fixFileName(QString in)
+{
+ in.replace(QLatin1Char('/'), QLatin1Char('\\'));
+ return in;
+}
+
+// Start a process (consisting of a non-reply setSettings and start).
+void CodaDevice::sendProcessStartCommand(const CodaCallback &callBack,
+ const QString &binaryIn,
+ unsigned uid,
+ QStringList arguments,
+ QString workingDirectory,
+ bool debugControl,
+ const QStringList &additionalLibraries,
+ const QVariant &cookie)
+{
+ // Obtain the bin directory, expand by c:/sys/bin if missing
+ const QChar backSlash('\\');
+ int slashPos = binaryIn.lastIndexOf(QLatin1Char('/'));
+ if (slashPos == -1)
+ slashPos = binaryIn.lastIndexOf(backSlash);
+ const QString sysBin = QLatin1String("c:/sys/bin");
+ const QString binaryFileName = slashPos == -1 ? binaryIn : binaryIn.mid(slashPos + 1);
+
+ if (workingDirectory.isEmpty())
+ workingDirectory = sysBin;
+
+ // Format settings with empty dummy parameter
+ QByteArray setData;
+ JsonInputStream setStr(setData);
+ setStr << "" << '\0'
+ << '[' << "exeToLaunch" << ',' << "addExecutables" << ',' << "addLibraries" << ',' << "logUserTraces" << ']'
+ << '\0' << '['
+ << binaryFileName << ','
+ << '{' << binaryFileName << ':' << QString::number(uid, 16) << '}' << ','
+ << additionalLibraries << ',' << true
+ << ']';
+ sendCodaMessage(
+#if 1
+ MessageWithReply, // TCF TRK 4.0.5 onwards
+#else
+ MessageWithoutReply, // TCF TRK 4.0.2
+#endif
+ SettingsService, "set", setData);
+
+ QByteArray startData;
+ JsonInputStream startStr(startData);
+ startStr << "" //We don't really know the drive of the working dir
+ << '\0' << binaryFileName << '\0' << arguments << '\0'
+ << QStringList() << '\0' // Env is an array ["PATH=value"] (non-standard)
+ << debugControl;
+ sendCodaMessage(MessageWithReply, ProcessesService, "start", startData, callBack, cookie);
+}
+
+void CodaDevice::sendRunProcessCommand(const CodaCallback &callBack,
+ const QString &processName,
+ QStringList arguments,
+ const QVariant &cookie)
+{
+ QByteArray startData;
+ JsonInputStream startStr(startData);
+ startStr << "" //We don't really know the drive of the working dir
+ << '\0' << processName << '\0' << arguments << '\0'
+ << QStringList() << '\0' // Env is an array ["PATH=value"] (non-standard)
+ << false; // Don't attach debugger
+ sendCodaMessage(MessageWithReply, ProcessesService, "start", startData, callBack, cookie);
+}
+
+void CodaDevice::sendSettingsEnableLogCommand()
+{
+
+ QByteArray setData;
+ JsonInputStream setStr(setData);
+ setStr << "" << '\0'
+ << '[' << "logUserTraces" << ']'
+ << '\0' << '['
+ << true
+ << ']';
+ sendCodaMessage(
+#if 1
+ MessageWithReply, // TCF TRK 4.0.5 onwards
+#else
+ MessageWithoutReply, // TCF TRK 4.0.2
+#endif
+ SettingsService, "set", setData);
+}
+
+void CodaDevice::sendProcessTerminateCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendCodaMessage(MessageWithReply, ProcessesService, "terminate", data, callBack, cookie);
+}
+
+void CodaDevice::sendRunControlTerminateCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendCodaMessage(MessageWithReply, RunControlService, "terminate", data, callBack, cookie);
+}
+
+// Non-standard: Remove executable from settings
+void CodaDevice::sendSettingsRemoveExecutableCommand(const QString &binaryIn,
+ unsigned uid,
+ const QStringList &additionalLibraries,
+ const QVariant &cookie)
+{
+ QByteArray setData;
+ JsonInputStream setStr(setData);
+ setStr << "" << '\0'
+ << '[' << "removedExecutables" << ',' << "removedLibraries" << ']'
+ << '\0' << '['
+ << '{' << QFileInfo(binaryIn).fileName() << ':' << QString::number(uid, 16) << '}' << ','
+ << additionalLibraries
+ << ']';
+ sendCodaMessage(MessageWithoutReply, SettingsService, "set", setData, CodaCallback(), cookie);
+}
+
+void CodaDevice::sendRunControlResumeCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ RunControlResumeMode mode,
+ unsigned count,
+ quint64 rangeStart,
+ quint64 rangeEnd,
+ const QVariant &cookie)
+{
+ QByteArray resumeData;
+ JsonInputStream str(resumeData);
+ str << id << '\0' << int(mode) << '\0' << count;
+ switch (mode) {
+ case RM_STEP_OVER_RANGE:
+ case RM_STEP_INTO_RANGE:
+ case RM_REVERSE_STEP_OVER_RANGE:
+ case RM_REVERSE_STEP_INTO_RANGE:
+ str << '\0' << '{' << "RANGE_START" << ':' << rangeStart
+ << ',' << "RANGE_END" << ':' << rangeEnd << '}';
+ break;
+ default:
+ break;
+ }
+ sendCodaMessage(MessageWithReply, RunControlService, "resume", resumeData, callBack, cookie);
+}
+
+void CodaDevice::sendRunControlSuspendCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendCodaMessage(MessageWithReply, RunControlService, "suspend", data, callBack, cookie);
+}
+
+void CodaDevice::sendRunControlResumeCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ sendRunControlResumeCommand(callBack, id, RM_RESUME, 1, 0, 0, cookie);
+}
+
+void CodaDevice::sendBreakpointsAddCommand(const CodaCallback &callBack,
+ const Breakpoint &bp,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << bp;
+ sendCodaMessage(MessageWithReply, BreakpointsService, "add", data, callBack, cookie);
+}
+
+void CodaDevice::sendBreakpointsRemoveCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ sendBreakpointsRemoveCommand(callBack, QVector<QByteArray>(1, id), cookie);
+}
+
+void CodaDevice::sendBreakpointsRemoveCommand(const CodaCallback &callBack,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << ids;
+ sendCodaMessage(MessageWithReply, BreakpointsService, "remove", data, callBack, cookie);
+}
+
+void CodaDevice::sendBreakpointsEnableCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ bool enable,
+ const QVariant &cookie)
+{
+ sendBreakpointsEnableCommand(callBack, QVector<QByteArray>(1, id), enable, cookie);
+}
+
+void CodaDevice::sendBreakpointsEnableCommand(const CodaCallback &callBack,
+ const QVector<QByteArray> &ids,
+ bool enable,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << ids;
+ sendCodaMessage(MessageWithReply, BreakpointsService,
+ enable ? "enable" : "disable",
+ data, callBack, cookie);
+}
+
+void CodaDevice::sendMemorySetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, const QByteArray& data,
+ const QVariant &cookie)
+{
+ QByteArray getData;
+ JsonInputStream str(getData);
+ // start/word size/mode. Mode should ideally be 1 (continue on error?)
+ str << contextId << '\0' << start << '\0' << 1 << '\0' << data.size() << '\0' << 1
+ << '\0' << data.toBase64();
+ sendCodaMessage(MessageWithReply, MemoryService, "set", getData, callBack, cookie);
+}
+
+void CodaDevice::sendMemoryGetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, quint64 size,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ // start/word size/mode. Mode should ideally be 1 (continue on error?)
+ str << contextId << '\0' << start << '\0' << 1 << '\0' << size << '\0' << 1;
+ sendCodaMessage(MessageWithReply, MemoryService, "get", data, callBack, cookie);
+}
+
+QByteArray CodaDevice::parseMemoryGet(const CodaCommandResult &r)
+{
+ if (r.type != CodaCommandResult::SuccessReply || r.values.size() < 1)
+ return QByteArray();
+ const JsonValue &memoryV = r.values.front();
+
+ if (memoryV.type() != JsonValue::String || memoryV.data().size() < 2
+ || !memoryV.data().endsWith('='))
+ return QByteArray();
+ // Catch errors reported as hash:
+ // R.4."TlVMTA==".{"Time":1276786871255,"Code":1,"AltCode":-38,"AltOrg":"POSIX","Format":"BadDescriptor"}
+ // Not sure what to make of it.
+ if (r.values.size() >= 2 && r.values.at(1).type() == JsonValue::Object)
+ qWarning("CodaDevice::parseMemoryGet(): Error retrieving memory: %s", r.values.at(1).toString(false).constData());
+ // decode
+ const QByteArray memory = QByteArray::fromBase64(memoryV.data());
+ if (memory.isEmpty())
+ qWarning("Base64 decoding of %s failed.", memoryV.data().constData());
+ if (debug)
+ qDebug("CodaDevice::parseMemoryGet: received %d bytes", memory.size());
+ return memory;
+}
+
+// Parse register children (array of names)
+QVector<QByteArray> CodaDevice::parseRegisterGetChildren(const CodaCommandResult &r)
+{
+ QVector<QByteArray> rc;
+ if (!r || r.values.size() < 1 || r.values.front().type() != JsonValue::Array)
+ return rc;
+ const JsonValue &front = r.values.front();
+ rc.reserve(front.childCount());
+ foreach(const JsonValue &v, front.children())
+ rc.push_back(v.data());
+ return rc;
+}
+
+CodaStatResponse CodaDevice::parseStat(const CodaCommandResult &r)
+{
+ CodaStatResponse rc;
+ if (!r || r.values.size() < 1 || r.values.front().type() != JsonValue::Object)
+ return rc;
+ foreach(const JsonValue &v, r.values.front().children()) {
+ if (v.name() == "Size") {
+ rc.size = v.data().toULongLong();
+ } else if (v.name() == "ATime") {
+ if (const quint64 atime = v.data().toULongLong())
+ rc.accessTime = CodaCommandResult::tcfTimeToQDateTime(atime);
+ } else if (v.name() == "MTime") {
+ if (const quint64 mtime = v.data().toULongLong())
+ rc.modTime = CodaCommandResult::tcfTimeToQDateTime(mtime);
+ }
+ }
+ return rc;
+}
+
+void CodaDevice::sendRegistersGetChildrenCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << contextId;
+ sendCodaMessage(MessageWithReply, RegistersService, "getChildren", data, callBack, cookie);
+}
+
+// Format id of register get request (needs contextId containing process and thread)
+static inline QByteArray registerId(const QByteArray &contextId, QByteArray id)
+{
+ QByteArray completeId = contextId;
+ if (!completeId.isEmpty())
+ completeId.append('.');
+ completeId.append(id);
+ return completeId;
+}
+
+// Format parameters of register get request
+static inline QByteArray registerGetData(const QByteArray &contextId, QByteArray id)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << registerId(contextId, id);
+ return data;
+}
+
+void CodaDevice::sendRegistersGetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ QByteArray id,
+ const QVariant &cookie)
+{
+ sendCodaMessage(MessageWithReply, RegistersService, "get",
+ registerGetData(contextId, id), callBack, cookie);
+}
+
+void CodaDevice::sendRegistersGetMCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie)
+{
+ // Format the register ids as a JSON list
+ QByteArray data;
+ JsonInputStream str(data);
+ str << '[';
+ const int count = ids.size();
+ for (int r = 0; r < count; r++) {
+ if (r)
+ str << ',';
+ // TODO: When 8-byte floating-point registers are supported, query for register length based on register id
+ str << '[' << registerId(contextId, ids.at(r)) << ',' << '0' << ',' << '4' << ']';
+ }
+ str << ']';
+ sendCodaMessage(MessageWithReply, RegistersService, "getm", data, callBack, cookie);
+}
+
+void CodaDevice::sendRegistersGetMRangeCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ unsigned start, unsigned count)
+{
+ const unsigned end = start + count;
+ if (end > (unsigned)d->m_registerNames.size()) {
+ qWarning("CodaDevice: No register name set for index %u (size: %d).", end, d->m_registerNames.size());
+ return;
+ }
+
+ QVector<QByteArray> ids;
+ ids.reserve(count);
+ for (unsigned i = start; i < end; ++i)
+ ids.push_back(d->m_registerNames.at(i));
+ sendRegistersGetMCommand(callBack, contextId, ids, QVariant(start));
+}
+
+// Set register
+void CodaDevice::sendRegistersSetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ QByteArray id,
+ const QByteArray &value,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ if (!contextId.isEmpty()) {
+ id.prepend('.');
+ id.prepend(contextId);
+ }
+ str << id << '\0' << value.toBase64();
+ sendCodaMessage(MessageWithReply, RegistersService, "set", data, callBack, cookie);
+}
+
+// Set register
+void CodaDevice::sendRegistersSetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ unsigned registerNumber,
+ const QByteArray &value,
+ const QVariant &cookie)
+{
+ if (registerNumber >= (unsigned)d->m_registerNames.size()) {
+ qWarning("CodaDevice: No register name set for index %u (size: %d).", registerNumber, d->m_registerNames.size());
+ return;
+ }
+ sendRegistersSetCommand(callBack, contextId,
+ d->m_registerNames[registerNumber],
+ value, cookie);
+}
+
+static const char outputListenerIDC[] = "ProgramOutputConsoleLogger";
+
+void CodaDevice::sendLoggingAddListenerCommand(const CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << outputListenerIDC;
+ sendCodaMessage(MessageWithReply, LoggingService, "addListener", data, callBack, cookie);
+}
+
+void CodaDevice::sendSymbianUninstallCommand(const Coda::CodaCallback &callBack,
+ const quint32 package,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ QString string = QString::number(package, 16);
+ str << string;
+ sendCodaMessage(MessageWithReply, SymbianInstallService, "uninstall", data, callBack, cookie);
+}
+
+void CodaDevice::sendSymbianOsDataGetThreadsCommand(const CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ sendCodaMessage(MessageWithReply, SymbianOSData, "getThreads", data, callBack, cookie);
+}
+
+void CodaDevice::sendSymbianOsDataFindProcessesCommand(const CodaCallback &callBack,
+ const QByteArray &processName,
+ const QByteArray &uid,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << processName << '\0' << uid;
+ sendCodaMessage(MessageWithReply, SymbianOSData, "findRunningProcesses", data, callBack, cookie);
+}
+
+void CodaDevice::sendSymbianOsDataGetQtVersionCommand(const CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ sendCodaMessage(MessageWithReply, SymbianOSData, "getQtVersion", QByteArray(), callBack, cookie);
+}
+
+void CodaDevice::sendSymbianOsDataGetRomInfoCommand(const CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ sendCodaMessage(MessageWithReply, SymbianOSData, "getRomInfo", QByteArray(), callBack, cookie);
+}
+
+void CodaDevice::sendSymbianOsDataGetHalInfoCommand(const CodaCallback &callBack,
+ const QStringList &keys,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << '[';
+ for (int i = 0; i < keys.count(); ++i) {
+ if (i)
+ str << ',';
+ str << keys[i];
+ }
+ str << ']';
+ sendCodaMessage(MessageWithReply, SymbianOSData, "getHalInfo", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendFileSystemOpenCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &name,
+ unsigned flags,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << name << '\0' << flags << '\0' << '{' << '}';
+ sendCodaMessage(MessageWithReply, FileSystemService, "open", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendFileSystemFstatCommand(const CodaCallback &callBack,
+ const QByteArray &handle,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << handle;
+ sendCodaMessage(MessageWithReply, FileSystemService, "fstat", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendFileSystemReadCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &handle,
+ unsigned int offset,
+ unsigned int size,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << handle << '\0' << offset << '\0' << size;
+ sendCodaMessage(MessageWithReply, FileSystemService, "read", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendFileSystemWriteCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &handle,
+ const QByteArray &dataIn,
+ unsigned offset,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << handle << '\0' << offset << '\0' << dataIn.toBase64();
+ sendCodaMessage(MessageWithReply, FileSystemService, "write", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendFileSystemCloseCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &handle,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << handle;
+ sendCodaMessage(MessageWithReply, FileSystemService, "close", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendSymbianInstallSilentInstallCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &file,
+ const QByteArray &targetDrive,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << file << '\0' << targetDrive;
+ sendCodaMessage(MessageWithReply, SymbianInstallService, "install", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendSymbianInstallUIInstallCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &file,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << file;
+ sendCodaMessage(MessageWithReply, SymbianInstallService, "installWithUI", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendSymbianInstallGetPackageInfoCommand(const Coda::CodaCallback &callBack,
+ const QList<quint32> &packages,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << '[';
+ for (int i = 0; i < packages.count(); ++i) {
+ if (i)
+ str << ',';
+ QString pkgString;
+ pkgString.setNum(packages[i], 16);
+ str << pkgString;
+ }
+ str << ']';
+ sendCodaMessage(MessageWithReply, SymbianInstallService, "getPackageInfo", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendDebugSessionControlSessionStartCommand(const Coda::CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ sendCodaMessage(MessageWithReply, DebugSessionControl, "sessionStart", QByteArray(), callBack, cookie);
+}
+
+void Coda::CodaDevice::sendDebugSessionControlSessionEndCommand(const Coda::CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ sendCodaMessage(MessageWithReply, DebugSessionControl, "sessionEnd ", QByteArray(), callBack, cookie);
+}
+
+} // namespace Coda