summaryrefslogtreecommitdiffstats
path: root/src/runonphone/symbianutils
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit50123887ba0f33cf47520bee7c419d68742af2d1 (patch)
tree0eb8679b9e4e4370e59b44bfdcae616816e39aca /src/runonphone/symbianutils
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/runonphone/symbianutils')
-rw-r--r--src/runonphone/symbianutils/bluetoothlistener.cpp224
-rw-r--r--src/runonphone/symbianutils/bluetoothlistener.h103
-rw-r--r--src/runonphone/symbianutils/bluetoothlistener_gui.cpp111
-rw-r--r--src/runonphone/symbianutils/bluetoothlistener_gui.h89
-rw-r--r--src/runonphone/symbianutils/callback.h160
-rw-r--r--src/runonphone/symbianutils/communicationstarter.cpp251
-rw-r--r--src/runonphone/symbianutils/communicationstarter.h160
-rw-r--r--src/runonphone/symbianutils/json.cpp490
-rw-r--r--src/runonphone/symbianutils/json.h149
-rw-r--r--src/runonphone/symbianutils/launcher.cpp1036
-rw-r--r--src/runonphone/symbianutils/launcher.h212
-rw-r--r--src/runonphone/symbianutils/symbiandevicemanager.cpp489
-rw-r--r--src/runonphone/symbianutils/symbiandevicemanager.h178
-rw-r--r--src/runonphone/symbianutils/symbianutils.pri35
-rw-r--r--src/runonphone/symbianutils/symbianutils_global.h55
-rw-r--r--src/runonphone/symbianutils/tcftrkdevice.cpp929
-rw-r--r--src/runonphone/symbianutils/tcftrkdevice.h295
-rw-r--r--src/runonphone/symbianutils/tcftrkmessage.cpp562
-rw-r--r--src/runonphone/symbianutils/tcftrkmessage.h296
-rw-r--r--src/runonphone/symbianutils/trkdevice.cpp1184
-rw-r--r--src/runonphone/symbianutils/trkdevice.h143
-rw-r--r--src/runonphone/symbianutils/trkutils.cpp603
-rw-r--r--src/runonphone/symbianutils/trkutils.h261
-rw-r--r--src/runonphone/symbianutils/trkutils_p.h62
24 files changed, 8077 insertions, 0 deletions
diff --git a/src/runonphone/symbianutils/bluetoothlistener.cpp b/src/runonphone/symbianutils/bluetoothlistener.cpp
new file mode 100644
index 000000000..be232db04
--- /dev/null
+++ b/src/runonphone/symbianutils/bluetoothlistener.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "bluetoothlistener.h"
+#include "trkdevice.h"
+
+#include <QtCore/QDebug>
+
+#ifdef Q_OS_UNIX
+# include <unistd.h>
+# include <signal.h>
+#else
+# include <windows.h>
+#endif
+
+// Process id helpers.
+#ifdef Q_OS_WIN
+inline DWORD processId(const QProcess &p)
+{
+ if (const Q_PID processInfoStruct = p.pid())
+ return processInfoStruct->dwProcessId;
+ return 0;
+}
+#else
+inline Q_PID processId(const QProcess &p)
+{
+ return p.pid();
+}
+#endif
+
+
+enum { debug = 0 };
+
+namespace trk {
+
+struct BluetoothListenerPrivate {
+ BluetoothListenerPrivate();
+ QString device;
+ QProcess process;
+#ifdef Q_OS_WIN
+ DWORD pid;
+#else
+ Q_PID pid;
+#endif
+ bool printConsoleMessages;
+ BluetoothListener::Mode mode;
+};
+
+BluetoothListenerPrivate::BluetoothListenerPrivate() :
+ pid(0),
+ printConsoleMessages(false),
+ mode(BluetoothListener::Listen)
+{
+}
+
+BluetoothListener::BluetoothListener(QObject *parent) :
+ QObject(parent),
+ d(new BluetoothListenerPrivate)
+{
+ d->process.setProcessChannelMode(QProcess::MergedChannels);
+
+ connect(&d->process, SIGNAL(readyReadStandardError()),
+ this, SLOT(slotStdError()));
+ connect(&d->process, SIGNAL(readyReadStandardOutput()),
+ this, SLOT(slotStdOutput()));
+ connect(&d->process, SIGNAL(finished(int, QProcess::ExitStatus)),
+ this, SLOT(slotProcessFinished(int,QProcess::ExitStatus)));
+ connect(&d->process, SIGNAL(error(QProcess::ProcessError)),
+ this, SLOT(slotProcessError(QProcess::ProcessError)));
+}
+
+BluetoothListener::~BluetoothListener()
+{
+ const int trc = terminateProcess();
+ if (debug)
+ qDebug() << "~BluetoothListener: terminated" << trc;
+ delete d;
+}
+
+BluetoothListener::Mode BluetoothListener::mode() const
+{
+ return d->mode;
+}
+
+void BluetoothListener::setMode(Mode m)
+{
+ d->mode = m;
+}
+
+bool BluetoothListener::printConsoleMessages() const
+{
+ return d->printConsoleMessages;
+}
+
+void BluetoothListener::setPrintConsoleMessages(bool p)
+{
+ d->printConsoleMessages = p;
+}
+
+int BluetoothListener::terminateProcess()
+{
+ enum { TimeOutMS = 200 };
+ if (debug)
+ qDebug() << "terminateProcess" << d->process.pid() << d->process.state();
+ if (d->process.state() == QProcess::NotRunning)
+ return -1;
+ emitMessage(tr("%1: Stopping listener %2...").arg(d->device).arg(processId(d->process)));
+ // When listening, the process should terminate by itself after closing the connection
+ if (mode() == Listen && d->process.waitForFinished(TimeOutMS))
+ return 0;
+#ifdef Q_OS_UNIX
+ kill(d->process.pid(), SIGHUP); // Listens for SIGHUP
+ if (d->process.waitForFinished(TimeOutMS))
+ return 1;
+#endif
+ d->process.terminate();
+ if (d->process.waitForFinished(TimeOutMS))
+ return 2;
+ d->process.kill();
+ return 3;
+}
+
+bool BluetoothListener::start(const QString &device, QString *errorMessage)
+{
+ if (d->process.state() != QProcess::NotRunning) {
+ *errorMessage = QLatin1String("Internal error: Still running.");
+ return false;
+ }
+ d->device = device;
+ const QString binary = QLatin1String("rfcomm");
+ QStringList arguments;
+ arguments << QLatin1String("-r")
+ << (d->mode == Listen ? QLatin1String("listen") : QLatin1String("watch"))
+ << device << QString(QLatin1Char('1'));
+ if (debug)
+ qDebug() << binary << arguments;
+ emitMessage(tr("%1: Starting Bluetooth listener %2...").arg(device, binary));
+ d->pid = 0;
+ d->process.start(binary, arguments);
+ if (!d->process.waitForStarted()) {
+ *errorMessage = tr("Unable to run '%1': %2").arg(binary, d->process.errorString());
+ return false;
+ }
+ d->pid = processId(d->process); // Forgets it after crash/termination
+ emitMessage(tr("%1: Bluetooth listener running (%2).").arg(device).arg(processId(d->process)));
+ return true;
+}
+
+void BluetoothListener::slotStdOutput()
+{
+ emitMessage(QString::fromLocal8Bit(d->process.readAllStandardOutput()));
+}
+
+void BluetoothListener::emitMessage(const QString &m)
+{
+ if (d->printConsoleMessages || debug)
+ qDebug("%s\n", qPrintable(m));
+ emit message(m);
+}
+
+void BluetoothListener::slotStdError()
+{
+ emitMessage(QString::fromLocal8Bit(d->process.readAllStandardError()));
+}
+
+void BluetoothListener::slotProcessFinished(int ex, QProcess::ExitStatus state)
+{
+ switch (state) {
+ case QProcess::NormalExit:
+ emitMessage(tr("%1: Process %2 terminated with exit code %3.")
+ .arg(d->device).arg(d->pid).arg(ex));
+ break;
+ case QProcess::CrashExit:
+ emitMessage(tr("%1: Process %2 crashed.").arg(d->device).arg(d->pid));
+ break;
+ }
+ emit terminated();
+}
+
+void BluetoothListener::slotProcessError(QProcess::ProcessError error)
+{
+ emitMessage(tr("%1: Process error %2: %3")
+ .arg(d->device).arg(error).arg(d->process.errorString()));
+}
+
+} // namespace trk
diff --git a/src/runonphone/symbianutils/bluetoothlistener.h b/src/runonphone/symbianutils/bluetoothlistener.h
new file mode 100644
index 000000000..38757275e
--- /dev/null
+++ b/src/runonphone/symbianutils/bluetoothlistener.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BLUETOOTHLISTENER_H
+#define BLUETOOTHLISTENER_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QProcess>
+
+namespace trk {
+struct BluetoothListenerPrivate;
+
+/* BluetoothListener: Starts a helper process watching connections on a
+ * Bluetooth device, Linux only:
+ * The rfcomm command is used. It process can be started in the background
+ * while connection attempts (TrkDevice::open()) are made in the foreground. */
+
+class SYMBIANUTILS_EXPORT BluetoothListener : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(BluetoothListener)
+public:
+ // The Mode property must be set before calling start().
+ enum Mode {
+ Listen, /* Terminate after client closed (read: Trk app
+ * on the phone terminated or disconnected).*/
+ Watch // Keep running, watch for next connection from client
+ };
+
+ explicit BluetoothListener(QObject *parent = 0);
+ virtual ~BluetoothListener();
+
+ Mode mode() const;
+ void setMode(Mode m);
+
+ bool start(const QString &device, QString *errorMessage);
+
+ // Print messages on the console.
+ bool printConsoleMessages() const;
+ void setPrintConsoleMessages(bool p);
+
+signals:
+ void terminated();
+ void message(const QString &);
+
+public slots:
+ void emitMessage(const QString &m); // accessed by starter
+
+private slots:
+ void slotStdOutput();
+ void slotStdError();
+ void slotProcessFinished(int, QProcess::ExitStatus);
+ void slotProcessError(QProcess::ProcessError error);
+
+private:
+ int terminateProcess();
+
+ BluetoothListenerPrivate *d;
+};
+
+} // namespace trk
+
+#endif // BLUETOOTHLISTENER_H
diff --git a/src/runonphone/symbianutils/bluetoothlistener_gui.cpp b/src/runonphone/symbianutils/bluetoothlistener_gui.cpp
new file mode 100644
index 000000000..c3f36b23a
--- /dev/null
+++ b/src/runonphone/symbianutils/bluetoothlistener_gui.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "bluetoothlistener_gui.h"
+#include "bluetoothlistener.h"
+#include "communicationstarter.h"
+
+#include <QtGui/QMessageBox>
+#include <QtGui/QPushButton>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+
+namespace trk {
+
+SYMBIANUTILS_EXPORT PromptStartCommunicationResult
+ promptStartCommunication(BaseCommunicationStarter &starter,
+ const QString &msgBoxTitle,
+ const QString &msgBoxText,
+ QWidget *msgBoxParent,
+ QString *errorMessage)
+{
+ errorMessage->clear();
+ // Initial connection attempt.
+ switch (starter.start()) {
+ case BaseCommunicationStarter::Started:
+ break;
+ case BaseCommunicationStarter::ConnectionSucceeded:
+ return PromptStartCommunicationConnected;
+ case BaseCommunicationStarter::StartError:
+ *errorMessage = starter.errorString();
+ return PromptStartCommunicationError;
+ }
+ // Run the starter with the event loop of a message box, have the box
+ // closed by the signals of the starter.
+ QMessageBox messageBox(QMessageBox::Information, msgBoxTitle, msgBoxText, QMessageBox::Cancel, msgBoxParent);
+ QObject::connect(&starter, SIGNAL(connected()), &messageBox, SLOT(close()));
+ QObject::connect(&starter, SIGNAL(timeout()), &messageBox, SLOT(close()));
+ messageBox.exec();
+ // Only starter.state() is reliable here to obtain the state.
+ switch (starter.state()) {
+ case AbstractBluetoothStarter::Running:
+ *errorMessage = QCoreApplication::translate("trk::promptStartCommunication", "Connection on %1 canceled.").arg(starter.device());
+ return PromptStartCommunicationCanceled;
+ case AbstractBluetoothStarter::TimedOut:
+ *errorMessage = starter.errorString();
+ return PromptStartCommunicationError;
+ case AbstractBluetoothStarter::Connected:
+ break;
+ }
+ return PromptStartCommunicationConnected;
+}
+
+SYMBIANUTILS_EXPORT PromptStartCommunicationResult
+ promptStartSerial(BaseCommunicationStarter &starter,
+ QWidget *msgBoxParent,
+ QString *errorMessage)
+{
+ const QString title = QCoreApplication::translate("trk::promptStartCommunication", "Waiting for App TRK");
+ const QString message = QCoreApplication::translate("trk::promptStartCommunication", "Waiting for App TRK to start on %1...").arg(starter.device());
+ return promptStartCommunication(starter, title, message, msgBoxParent, errorMessage);
+}
+
+SYMBIANUTILS_EXPORT PromptStartCommunicationResult
+ promptStartBluetooth(BaseCommunicationStarter &starter,
+ QWidget *msgBoxParent,
+ QString *errorMessage)
+{
+ const QString title = QCoreApplication::translate("trk::promptStartCommunication", "Waiting for Bluetooth Connection");
+ const QString message = QCoreApplication::translate("trk::promptStartCommunication", "Connecting to %1...").arg(starter.device());
+ return promptStartCommunication(starter, title, message, msgBoxParent, errorMessage);
+}
+
+} // namespace trk
diff --git a/src/runonphone/symbianutils/bluetoothlistener_gui.h b/src/runonphone/symbianutils/bluetoothlistener_gui.h
new file mode 100644
index 000000000..d0edc7a26
--- /dev/null
+++ b/src/runonphone/symbianutils/bluetoothlistener_gui.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BLUETOOTHLISTENER_GUI_H
+#define BLUETOOTHLISTENER_GUI_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QWidget;
+QT_END_NAMESPACE
+
+namespace trk {
+class BaseCommunicationStarter;
+
+/* promptStartCommunication(): Convenience functions that
+ * prompt the user to start a communication (launching or
+ * connecting TRK) using a modal message box in which they can cancel.
+ * Pass in the starter with device and parameters set up. */
+
+enum PromptStartCommunicationResult {
+ PromptStartCommunicationConnected,
+ PromptStartCommunicationCanceled,
+ PromptStartCommunicationError
+};
+
+SYMBIANUTILS_EXPORT PromptStartCommunicationResult
+ promptStartCommunication(BaseCommunicationStarter &starter,
+ const QString &msgBoxTitle,
+ const QString &msgBoxText,
+ QWidget *msgBoxParent,
+ QString *errorMessage);
+
+// Convenience to start a serial connection (messages prompting
+// to launch Trk).
+SYMBIANUTILS_EXPORT PromptStartCommunicationResult
+ promptStartSerial(BaseCommunicationStarter &starter,
+ QWidget *msgBoxParent,
+ QString *errorMessage);
+
+// Convenience to start blue tooth connection (messages
+// prompting to connect).
+SYMBIANUTILS_EXPORT PromptStartCommunicationResult
+ promptStartBluetooth(BaseCommunicationStarter &starter,
+ QWidget *msgBoxParent,
+ QString *errorMessage);
+} // namespace trk
+
+#endif // BLUETOOTHLISTENER_GUI_H
diff --git a/src/runonphone/symbianutils/callback.h b/src/runonphone/symbianutils/callback.h
new file mode 100644
index 000000000..4ee77ea31
--- /dev/null
+++ b/src/runonphone/symbianutils/callback.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DEBUGGER_CALLBACK_H
+#define DEBUGGER_CALLBACK_H
+
+#include "symbianutils_global.h"
+
+namespace trk {
+namespace Internal {
+
+/* Helper class for the 1-argument functor:
+ * Cloneable base class for the implementation which is
+ * invokeable with the argument. */
+template <class Argument>
+class CallbackImplBase
+{
+ Q_DISABLE_COPY(CallbackImplBase)
+public:
+ CallbackImplBase() {}
+ virtual CallbackImplBase *clone() const = 0;
+ virtual void invoke(Argument a) = 0;
+ virtual ~CallbackImplBase() {}
+};
+
+/* Helper class for the 1-argument functor: Implementation for
+ * a class instance with a member function pointer. */
+template <class Class, class Argument>
+class CallbackMemberPtrImpl : public CallbackImplBase<Argument>
+{
+public:
+ typedef void (Class::*MemberFuncPtr)(Argument);
+
+ CallbackMemberPtrImpl(Class *instance,
+ MemberFuncPtr memberFunc) :
+ m_instance(instance),
+ m_memberFunc(memberFunc) {}
+
+ virtual CallbackImplBase<Argument> *clone() const
+ {
+ return new CallbackMemberPtrImpl<Class, Argument>(m_instance, m_memberFunc);
+ }
+
+ virtual void invoke(Argument a)
+ { (m_instance->*m_memberFunc)(a); }
+private:
+ Class *m_instance;
+ MemberFuncPtr m_memberFunc;
+};
+
+} // namespace Internal
+
+/* Default-constructible, copyable 1-argument functor providing an
+ * operator()(Argument) that invokes a member function of a class:
+ * \code
+class Foo {
+public:
+ void print(const std::string &);
+};
+...
+Foo foo;
+Callback<const std::string &> f1(&foo, &Foo::print);
+f1("test");
+\endcode */
+
+template <class Argument>
+class Callback
+{
+public:
+ Callback() : m_impl(0) {}
+
+ template <class Class>
+ Callback(Class *instance, void (Class::*memberFunc)(Argument)) :
+ m_impl(new Internal::CallbackMemberPtrImpl<Class,Argument>(instance, memberFunc))
+ {}
+
+ ~Callback()
+ {
+ clean();
+ }
+
+ Callback(const Callback &rhs) :
+ m_impl(0)
+ {
+ if (rhs.m_impl)
+ m_impl = rhs.m_impl->clone();
+ }
+
+ Callback &operator=(const Callback &rhs)
+ {
+ if (this != &rhs) {
+ clean();
+ if (rhs.m_impl)
+ m_impl = rhs.m_impl->clone();
+ }
+ return *this;
+ }
+
+ bool isNull() const { return m_impl == 0; }
+ operator bool() const { return !isNull(); }
+
+ void operator()(Argument a)
+ {
+ if (m_impl)
+ m_impl->invoke(a);
+ }
+
+private:
+ void clean()
+ {
+ if (m_impl) {
+ delete m_impl;
+ m_impl = 0;
+ }
+ }
+
+ Internal::CallbackImplBase<Argument> *m_impl;
+};
+
+} // namespace trk
+
+#endif // DEBUGGER_CALLBACK_H
diff --git a/src/runonphone/symbianutils/communicationstarter.cpp b/src/runonphone/symbianutils/communicationstarter.cpp
new file mode 100644
index 000000000..602786b61
--- /dev/null
+++ b/src/runonphone/symbianutils/communicationstarter.cpp
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "communicationstarter.h"
+#include "bluetoothlistener.h"
+#include "trkdevice.h"
+
+#include <QtCore/QTimer>
+#include <QtCore/QEventLoop>
+
+namespace trk {
+
+// --------------- AbstractBluetoothStarter
+struct BaseCommunicationStarterPrivate {
+ explicit BaseCommunicationStarterPrivate(const BaseCommunicationStarter::TrkDevicePtr &d);
+
+ const BaseCommunicationStarter::TrkDevicePtr trkDevice;
+ BluetoothListener *listener;
+ QTimer *timer;
+ int intervalMS;
+ int attempts;
+ int n;
+ QString errorString;
+ BaseCommunicationStarter::State state;
+};
+
+BaseCommunicationStarterPrivate::BaseCommunicationStarterPrivate(const BaseCommunicationStarter::TrkDevicePtr &d) :
+ trkDevice(d),
+ listener(0),
+ timer(0),
+ intervalMS(1000),
+ attempts(-1),
+ n(0),
+ state(BaseCommunicationStarter::TimedOut)
+{
+}
+
+BaseCommunicationStarter::BaseCommunicationStarter(const TrkDevicePtr &trkDevice, QObject *parent) :
+ QObject(parent),
+ d(new BaseCommunicationStarterPrivate(trkDevice))
+{
+}
+
+BaseCommunicationStarter::~BaseCommunicationStarter()
+{
+ stopTimer();
+ delete d;
+}
+
+void BaseCommunicationStarter::stopTimer()
+{
+ if (d->timer && d->timer->isActive())
+ d->timer->stop();
+}
+
+bool BaseCommunicationStarter::initializeStartupResources(QString *errorMessage)
+{
+ errorMessage->clear();
+ return true;
+}
+
+BaseCommunicationStarter::StartResult BaseCommunicationStarter::start()
+{
+ if (state() == Running) {
+ d->errorString = QLatin1String("Internal error, attempt to re-start BaseCommunicationStarter.\n");
+ return StartError;
+ }
+ // Before we instantiate timers, and such, try to open the device,
+ // which should succeed if another listener is already running in
+ // 'Watch' mode
+ if (d->trkDevice->open(&(d->errorString)))
+ return ConnectionSucceeded;
+ // Pull up resources for next attempt
+ d->n = 0;
+ if (!initializeStartupResources(&(d->errorString)))
+ return StartError;
+ // Start timer
+ if (!d->timer) {
+ d->timer = new QTimer;
+ connect(d->timer, SIGNAL(timeout()), this, SLOT(slotTimer()));
+ }
+ d->timer->setInterval(d->intervalMS);
+ d->timer->setSingleShot(false);
+ d->timer->start();
+ d->state = Running;
+ return Started;
+}
+
+BaseCommunicationStarter::State BaseCommunicationStarter::state() const
+{
+ return d->state;
+}
+
+int BaseCommunicationStarter::intervalMS() const
+{
+ return d->intervalMS;
+}
+
+void BaseCommunicationStarter::setIntervalMS(int i)
+{
+ d->intervalMS = i;
+ if (d->timer)
+ d->timer->setInterval(i);
+}
+
+int BaseCommunicationStarter::attempts() const
+{
+ return d->attempts;
+}
+
+void BaseCommunicationStarter::setAttempts(int a)
+{
+ d->attempts = a;
+}
+
+QString BaseCommunicationStarter::device() const
+{
+ return d->trkDevice->port();
+}
+
+QString BaseCommunicationStarter::errorString() const
+{
+ return d->errorString;
+}
+
+void BaseCommunicationStarter::slotTimer()
+{
+ ++d->n;
+ // Check for timeout
+ if (d->attempts >= 0 && d->n >= d->attempts) {
+ stopTimer();
+ d->errorString = tr("%1: timed out after %n attempts using an interval of %2ms.", 0, d->n)
+ .arg(d->trkDevice->port()).arg(d->intervalMS);
+ d->state = TimedOut;
+ emit timeout();
+ } else {
+ // Attempt n to connect?
+ if (d->trkDevice->open(&(d->errorString))) {
+ stopTimer();
+ const QString msg = tr("%1: Connection attempt %2 succeeded.").arg(d->trkDevice->port()).arg(d->n);
+ emit message(msg);
+ d->state = Connected;
+ emit connected();
+ } else {
+ const QString msg = tr("%1: Connection attempt %2 failed: %3 (retrying)...")
+ .arg(d->trkDevice->port()).arg(d->n).arg(d->errorString);
+ emit message(msg);
+ }
+ }
+}
+
+// --------------- AbstractBluetoothStarter
+
+AbstractBluetoothStarter::AbstractBluetoothStarter(const TrkDevicePtr &trkDevice, QObject *parent) :
+ BaseCommunicationStarter(trkDevice, parent)
+{
+}
+
+bool AbstractBluetoothStarter::initializeStartupResources(QString *errorMessage)
+{
+ // Create the listener and forward messages to it.
+ BluetoothListener *listener = createListener();
+ connect(this, SIGNAL(message(QString)), listener, SLOT(emitMessage(QString)));
+ return listener->start(device(), errorMessage);
+}
+
+// -------- ConsoleBluetoothStarter
+ConsoleBluetoothStarter::ConsoleBluetoothStarter(const TrkDevicePtr &trkDevice,
+ QObject *listenerParent,
+ QObject *parent) :
+AbstractBluetoothStarter(trkDevice, parent),
+m_listenerParent(listenerParent)
+{
+}
+
+BluetoothListener *ConsoleBluetoothStarter::createListener()
+{
+ BluetoothListener *rc = new BluetoothListener(m_listenerParent);
+ rc->setMode(BluetoothListener::Listen);
+ rc->setPrintConsoleMessages(true);
+ return rc;
+}
+
+bool ConsoleBluetoothStarter::startBluetooth(const TrkDevicePtr &trkDevice,
+ QObject *listenerParent,
+ int attempts,
+ QString *errorMessage)
+{
+ // Set up a console starter to print to stdout.
+ ConsoleBluetoothStarter starter(trkDevice, listenerParent);
+ starter.setAttempts(attempts);
+ switch (starter.start()) {
+ case Started:
+ break;
+ case ConnectionSucceeded:
+ return true;
+ case StartError:
+ *errorMessage = starter.errorString();
+ return false;
+ }
+ // Run the starter with an event loop. @ToDo: Implement
+ // some asynchronous keypress read to cancel.
+ QEventLoop eventLoop;
+ connect(&starter, SIGNAL(connected()), &eventLoop, SLOT(quit()));
+ connect(&starter, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
+ eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
+ if (starter.state() != AbstractBluetoothStarter::Connected) {
+ *errorMessage = starter.errorString();
+ return false;
+ }
+ return true;
+}
+} // namespace trk
diff --git a/src/runonphone/symbianutils/communicationstarter.h b/src/runonphone/symbianutils/communicationstarter.h
new file mode 100644
index 000000000..9fec5893c
--- /dev/null
+++ b/src/runonphone/symbianutils/communicationstarter.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef COMMUNICATIONSTARTER_H
+#define COMMUNICATIONSTARTER_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QSharedPointer>
+#include <QtCore/QObject>
+
+namespace trk {
+class TrkDevice;
+class BluetoothListener;
+struct BaseCommunicationStarterPrivate;
+
+/* BaseCommunicationStarter: A QObject that repeatedly tries to open a
+ * trk device until a connection succeeds or a timeout occurs (emitting
+ * signals), allowing to do something else in the foreground (local event loop
+ * [say QMessageBox] or some asynchronous operation). If the initial
+ * connection attempt in start() fails, the
+ * virtual initializeStartupResources() is called to initialize resources
+ * required to pull up the communication (namely Bluetooth listeners).
+ * The base class can be used as is to prompt the user to launch App TRK for a
+ * serial communication as this requires no further resource setup. */
+
+class SYMBIANUTILS_EXPORT BaseCommunicationStarter : public QObject {
+ Q_OBJECT
+ Q_DISABLE_COPY(BaseCommunicationStarter)
+public:
+ typedef QSharedPointer<TrkDevice> TrkDevicePtr;
+
+ enum State { Running, Connected, TimedOut };
+
+ explicit BaseCommunicationStarter(const TrkDevicePtr& trkDevice, QObject *parent = 0);
+ virtual ~BaseCommunicationStarter();
+
+ int intervalMS() const;
+ void setIntervalMS(int i);
+
+ int attempts() const;
+ void setAttempts(int a);
+
+ QString device() const;
+
+ State state() const;
+ QString errorString() const;
+
+ enum StartResult {
+ Started, // Starter is now running.
+ ConnectionSucceeded, /* Initial connection attempt succeeded,
+ * no need to keep running. */
+ StartError // Error occurred during start.
+ };
+
+ StartResult start();
+
+signals:
+ void connected();
+ void timeout();
+ void message(const QString &);
+
+private slots:
+ void slotTimer();
+
+protected:
+ virtual bool initializeStartupResources(QString *errorMessage);
+
+private:
+ inline void stopTimer();
+
+ BaseCommunicationStarterPrivate *d;
+};
+
+/* AbstractBluetoothStarter: Repeatedly tries to open a trk Bluetooth
+ * device. Note that in case a Listener is already running mode, the
+ * connection will succeed immediately.
+ * initializeStartupResources() is implemented to fire up the listener.
+ * Introduces a new virtual createListener() that derived classes must
+ * implement as a factory function that creates and sets up the
+ * listener (mode, message connection, etc). */
+
+class SYMBIANUTILS_EXPORT AbstractBluetoothStarter : public BaseCommunicationStarter {
+ Q_OBJECT
+ Q_DISABLE_COPY(AbstractBluetoothStarter)
+public:
+
+protected:
+ explicit AbstractBluetoothStarter(const TrkDevicePtr& trkDevice, QObject *parent = 0);
+
+ // Implemented to fire up the listener.
+ virtual bool initializeStartupResources(QString *errorMessage);
+ // New virtual: Overwrite to create and parametrize the listener.
+ virtual BluetoothListener *createListener() = 0;
+};
+
+/* ConsoleBluetoothStarter: Convenience class for console processes. Creates a
+ * listener in "Listen" mode with the messages redirected to standard output. */
+
+class SYMBIANUTILS_EXPORT ConsoleBluetoothStarter : public AbstractBluetoothStarter {
+ Q_OBJECT
+ Q_DISABLE_COPY(ConsoleBluetoothStarter)
+public:
+ static bool startBluetooth(const TrkDevicePtr& trkDevice,
+ QObject *listenerParent,
+ int attempts,
+ QString *errorMessage);
+
+protected:
+ virtual BluetoothListener *createListener();
+
+private:
+ explicit ConsoleBluetoothStarter(const TrkDevicePtr& trkDevice,
+ QObject *listenerParent,
+ QObject *parent = 0);
+
+ QObject *m_listenerParent;
+};
+
+} // namespace trk
+
+#endif // COMMUNICATIONSTARTER_H
diff --git a/src/runonphone/symbianutils/json.cpp b/src/runonphone/symbianutils/json.cpp
new file mode 100644
index 000000000..755df5e1d
--- /dev/null
+++ b/src/runonphone/symbianutils/json.cpp
@@ -0,0 +1,490 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "json.h"
+
+#ifdef TODO_USE_CREATOR
+#include <utils/qtcassert.h>
+#endif // TODO_USE_CREATOR
+
+#include <QtCore/QByteArray>
+#include <QtCore/QTextStream>
+#include <QtCore/QDebug>
+#include <QtCore/QStringList>
+
+#include <ctype.h>
+
+//#define DEBUG_JASON
+#ifdef DEBUG_JASON
+#define JDEBUG(s) qDebug() << s
+#else
+#define JDEBUG(s)
+#endif
+
+namespace tcftrk {
+
+static void skipSpaces(const char *&from, const char *to)
+{
+ while (from != to && isspace(*from))
+ ++from;
+}
+
+QTextStream &operator<<(QTextStream &os, const JsonValue &mi)
+{
+ return os << mi.toString();
+}
+
+void JsonValue::parsePair(const char *&from, const char *to)
+{
+ skipSpaces(from, to);
+ JDEBUG("parsePair: " << QByteArray(from, to - from));
+ m_name = parseCString(from, to);
+ skipSpaces(from, to);
+ while (from < to && *from != ':') {
+ JDEBUG("not a colon" << *from);
+ ++from;
+ }
+ ++from;
+ parseValue(from, to);
+ skipSpaces(from, to);
+}
+
+QByteArray JsonValue::parseNumber(const char *&from, const char *to)
+{
+ QByteArray result;
+ if (from < to && *from == '-') // Leading '-'.
+ result.append(*from++);
+ while (from < to && *from >= '0' && *from <= '9')
+ result.append(*from++);
+ return result;
+}
+
+QByteArray JsonValue::parseCString(const char *&from, const char *to)
+{
+ QByteArray result;
+ JDEBUG("parseCString: " << QByteArray(from, to - from));
+ if (*from != '"') {
+ qDebug() << "JSON Parse Error, double quote expected";
+ ++from; // So we don't hang
+ return QByteArray();
+ }
+ const char *ptr = from;
+ ++ptr;
+ while (ptr < to) {
+ if (*ptr == '"') {
+ ++ptr;
+ result = QByteArray(from + 1, ptr - from - 2);
+ break;
+ }
+ if (*ptr == '\\') {
+ ++ptr;
+ if (ptr == to) {
+ qDebug() << "JSON Parse Error, unterminated backslash escape";
+ from = ptr; // So we don't hang
+ return QByteArray();
+ }
+ }
+ ++ptr;
+ }
+ from = ptr;
+
+ int idx = result.indexOf('\\');
+ if (idx >= 0) {
+ char *dst = result.data() + idx;
+ const char *src = dst + 1, *end = result.data() + result.length();
+ do {
+ char c = *src++;
+ switch (c) {
+ case 'a': *dst++ = '\a'; break;
+ case 'b': *dst++ = '\b'; break;
+ case 'f': *dst++ = '\f'; break;
+ case 'n': *dst++ = '\n'; break;
+ case 'r': *dst++ = '\r'; break;
+ case 't': *dst++ = '\t'; break;
+ case 'v': *dst++ = '\v'; break;
+ case '"': *dst++ = '"'; break;
+ case '\\': *dst++ = '\\'; break;
+ default:
+ {
+ int chars = 0;
+ uchar prod = 0;
+ forever {
+ if (c < '0' || c > '7') {
+ --src;
+ break;
+ }
+ prod = prod * 8 + c - '0';
+ if (++chars == 3 || src == end)
+ break;
+ c = *src++;
+ }
+ if (!chars) {
+ qDebug() << "JSON Parse Error, unrecognized backslash escape";
+ return QByteArray();
+ }
+ *dst++ = prod;
+ }
+ }
+ while (src != end) {
+ char c = *src++;
+ if (c == '\\')
+ break;
+ *dst++ = c;
+ }
+ } while (src != end);
+ *dst = 0;
+ result.truncate(dst - result.data());
+ }
+
+ JDEBUG("parseCString, got " << result);
+ return result;
+}
+
+
+
+void JsonValue::parseValue(const char *&from, const char *to)
+{
+ JDEBUG("parseValue: " << QByteArray(from, to - from));
+ switch (*from) {
+ case '{':
+ parseObject(from, to);
+ break;
+ case 't':
+ if (to - from >= 4 && qstrncmp(from, "true", 4) == 0) {
+ m_data = QByteArray(from, 4);
+ from += m_data.size();
+ m_type = Boolean;
+ }
+ break;
+ case 'f':
+ if (to - from >= 5 && qstrncmp(from, "false", 5) == 0) {
+ m_data = QByteArray(from, 5);
+ from += m_data.size();
+ m_type = Boolean;
+ }
+ break;
+ case 'n':
+ if (to - from >= 4 && qstrncmp(from, "null", 4) == 0) {
+ m_data = QByteArray(from, 4);
+ from += m_data.size();
+ m_type = NullObject;
+ }
+ break;
+ case '[':
+ parseArray(from, to);
+ break;
+ case '"':
+ m_type = String;
+ m_data = parseCString(from, to);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '-':
+ m_type = Number;
+ m_data = parseNumber(from, to);
+ default:
+ break;
+ }
+}
+
+void JsonValue::parseObject(const char *&from, const char *to)
+{
+ JDEBUG("parseObject: " << QByteArray(from, to - from));
+#ifdef TODO_USE_CREATOR
+ QTC_ASSERT(*from == '{', /**/);
+#endif
+ ++from;
+ m_type = Object;
+ while (from < to) {
+ if (*from == '}') {
+ ++from;
+ break;
+ }
+ JsonValue child;
+ child.parsePair(from, to);
+ if (!child.isValid())
+ return;
+ m_children += child;
+ if (*from == ',')
+ ++from;
+ }
+}
+
+void JsonValue::parseArray(const char *&from, const char *to)
+{
+ JDEBUG("parseArray: " << QByteArray(from, to - from));
+#ifdef TODO_USE_CREATOR
+ QTC_ASSERT(*from == '[', /**/);
+#endif
+ ++from;
+ m_type = Array;
+ while (from < to) {
+ if (*from == ']') {
+ ++from;
+ break;
+ }
+ JsonValue child;
+ child.parseValue(from, to);
+ if (child.isValid())
+ m_children += child;
+ if (*from == ',')
+ ++from;
+ }
+}
+
+void JsonValue::setStreamOutput(const QByteArray &name, const QByteArray &content)
+{
+ if (content.isEmpty())
+ return;
+ JsonValue child;
+ child.m_type = String;
+ child.m_name = name;
+ child.m_data = content;
+ m_children += child;
+ if (m_type == Invalid)
+ m_type = Object;
+}
+
+static QByteArray ind(int indent)
+{
+ return QByteArray(2 * indent, ' ');
+}
+
+void JsonValue::dumpChildren(QByteArray * str, bool multiline, int indent) const
+{
+ for (int i = 0; i < m_children.size(); ++i) {
+ if (i != 0) {
+ *str += ',';
+ if (multiline)
+ *str += '\n';
+ }
+ if (multiline)
+ *str += ind(indent);
+ *str += m_children.at(i).toString(multiline, indent);
+ }
+}
+
+class MyString : public QString {
+public:
+ ushort at(int i) const { return constData()[i].unicode(); }
+};
+
+template<class ST, typename CT>
+inline ST escapeCStringTpl(const ST &ba)
+{
+ ST ret;
+ ret.reserve(ba.length() * 2);
+ for (int i = 0; i < ba.length(); ++i) {
+ CT c = ba.at(i);
+ switch (c) {
+ case '\\': ret += "\\\\"; break;
+ case '\a': ret += "\\a"; break;
+ case '\b': ret += "\\b"; break;
+ case '\f': ret += "\\f"; break;
+ case '\n': ret += "\\n"; break;
+ case '\r': ret += "\\r"; break;
+ case '\t': ret += "\\t"; break;
+ case '\v': ret += "\\v"; break;
+ case '"': ret += "\\\""; break;
+ default:
+ if (c < 32 || c == 127) {
+ ret += '\\';
+ ret += '0' + (c >> 6);
+ ret += '0' + ((c >> 3) & 7);
+ ret += '0' + (c & 7);
+ } else {
+ ret += c;
+ }
+ }
+ }
+ return ret;
+}
+
+QString JsonValue::escapeCString(const QString &ba)
+{
+ return escapeCStringTpl<MyString, ushort>(static_cast<const MyString &>(ba));
+}
+
+QByteArray JsonValue::escapeCString(const QByteArray &ba)
+{
+ return escapeCStringTpl<QByteArray, uchar>(ba);
+}
+
+QByteArray JsonValue::toString(bool multiline, int indent) const
+{
+ QByteArray result;
+ switch (m_type) {
+ case Invalid:
+ if (multiline)
+ result += ind(indent) + "Invalid\n";
+ else
+ result += "Invalid";
+ break;
+ case String:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ result += '"' + escapeCString(m_data) + '"';
+ break;
+ case Number:
+ if (!m_name.isEmpty())
+ result += '"' + m_name + "\":";
+ result += m_data;
+ break;
+ case Boolean:
+ case NullObject:
+ if (!m_name.isEmpty())
+ result += '"' + m_name + "\":";
+ result += m_data;
+ break;
+ case Object:
+ if (!m_name.isEmpty())
+ result += m_name + '=';
+ if (multiline) {
+ result += "{\n";
+ dumpChildren(&result, multiline, indent + 1);
+ result += '\n' + ind(indent) + "}";
+ } else {
+ result += "{";
+ dumpChildren(&result, multiline, indent + 1);
+ result += "}";
+ }
+ break;
+ case Array:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ if (multiline) {
+ result += "[\n";
+ dumpChildren(&result, multiline, indent + 1);
+ result += '\n' + ind(indent) + "]";
+ } else {
+ result += "[";
+ dumpChildren(&result, multiline, indent + 1);
+ result += "]";
+ }
+ break;
+ }
+ return result;
+}
+
+void JsonValue::fromString(const QByteArray &ba)
+{
+ const char *from = ba.constBegin();
+ const char *to = ba.constEnd();
+ parseValue(from, to);
+}
+
+JsonValue JsonValue::findChild(const char *name) const
+{
+ for (int i = 0; i < m_children.size(); ++i)
+ if (m_children.at(i).m_name == name)
+ return m_children.at(i);
+ return JsonValue();
+}
+
+void JsonInputStream::appendCString(const char *s)
+{
+ m_target.append('"');
+ for (const char *p = s; *p; p++) {
+ if (*p == '"' || *p == '\\')
+ m_target.append('\\');
+ m_target.append(*p);
+ }
+ m_target.append('"');
+}
+
+void JsonInputStream::appendString(const QString &in)
+{
+ if (in.isEmpty()) {
+ m_target.append("\"\"");
+ return;
+ }
+
+ const QChar doubleQuote('"');
+ const QChar backSlash('\\');
+ QString rc;
+ const int inSize = in.size();
+ rc.reserve(in.size() + 5);
+ rc.append(doubleQuote);
+ for (int i = 0; i < inSize; i++) {
+ const QChar c = in.at(i);
+ if (c == doubleQuote || c == backSlash)
+ rc.append(backSlash);
+ rc.append(c);
+ }
+ rc.append(doubleQuote);
+ m_target.append(rc.toUtf8());
+ return;
+}
+
+JsonInputStream &JsonInputStream::operator<<(const QStringList &in)
+{
+ m_target.append('[');
+ const int count = in.size();
+ for (int i = 0 ; i < count; i++) {
+ if (i)
+ m_target.append(',');
+ appendString(in.at(i));
+ }
+ m_target.append(']');
+ return *this;
+}
+
+JsonInputStream &JsonInputStream::operator<<(const QVector<QByteArray> &ba)
+{
+ m_target.append('[');
+ const int count = ba.size();
+ for (int i = 0 ; i < count; i++) {
+ if (i)
+ m_target.append(',');
+ appendCString(ba.at(i).constData());
+ }
+ m_target.append(']');
+ return *this;
+}
+
+JsonInputStream &JsonInputStream::operator<<(bool b)
+{
+ m_target.append(b ? "true" : "false");
+ return *this;
+}
+
+} // namespace tcftrk
+
diff --git a/src/runonphone/symbianutils/json.h b/src/runonphone/symbianutils/json.h
new file mode 100644
index 000000000..a9190f812
--- /dev/null
+++ b/src/runonphone/symbianutils/json.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMBIANUTILS_JSON_H
+#define SYMBIANUTILS_JSON_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+namespace tcftrk {
+
+class SYMBIANUTILS_EXPORT JsonValue
+{
+public:
+ JsonValue() : m_type(Invalid) {}
+ explicit JsonValue(const QByteArray &str) { fromString(str); }
+
+ QByteArray m_name;
+ QByteArray m_data;
+ QList<JsonValue> m_children;
+
+ enum Type {
+ Invalid,
+ String,
+ Number,
+ Boolean,
+ Object,
+ NullObject,
+ Array,
+ };
+
+ Type m_type;
+
+ inline Type type() const { return m_type; }
+ inline QByteArray name() const { return m_name; }
+ inline bool hasName(const char *name) const { return m_name == name; }
+
+ inline bool isValid() const { return m_type != Invalid; }
+ inline bool isNumber() const { return m_type == Number; }
+ inline bool isString() const { return m_type == String; }
+ inline bool isObject() const { return m_type == Object; }
+ inline bool isArray() const { return m_type == Array; }
+
+
+ inline QByteArray data() const { return m_data; }
+ inline const QList<JsonValue> &children() const { return m_children; }
+ inline int childCount() const { return m_children.size(); }
+
+ const JsonValue &childAt(int index) const { return m_children[index]; }
+ JsonValue &childAt(int index) { return m_children[index]; }
+ JsonValue findChild(const char *name) const;
+
+ QByteArray toString(bool multiline = false, int indent = 0) const;
+ void fromString(const QByteArray &str);
+ void setStreamOutput(const QByteArray &name, const QByteArray &content);
+
+private:
+ static QByteArray parseCString(const char *&from, const char *to);
+ static QByteArray parseNumber(const char *&from, const char *to);
+ static QByteArray escapeCString(const QByteArray &ba);
+ static QString escapeCString(const QString &ba);
+ void parsePair(const char *&from, const char *to);
+ void parseValue(const char *&from, const char *to);
+ void parseObject(const char *&from, const char *to);
+ void parseArray(const char *&from, const char *to);
+
+ void dumpChildren(QByteArray *str, bool multiline, int indent) const;
+};
+
+/* Thin wrapper around QByteArray for formatting JSON input. Use as in:
+ * JsonInputStream(byteArray) << '{' << "bla" << ':' << "blup" << '}';
+ * Note that strings get double quotes and JSON-escaping, characters should be
+ * used for the array/hash delimiters.
+ * */
+class SYMBIANUTILS_EXPORT JsonInputStream {
+public:
+ explicit JsonInputStream(QByteArray &a) : m_target(a) {}
+
+ JsonInputStream &operator<<(char c) { m_target.append(c); return *this; }
+ JsonInputStream &operator<<(const char *c) { appendCString(c); return *this; }
+ JsonInputStream &operator<<(const QByteArray &a) { appendCString(a.constData()); return *this; }
+ JsonInputStream &operator<<(const QString &c) { appendString(c); return *this; }
+
+ // Format as array
+ JsonInputStream &operator<<(const QStringList &c);
+
+ // Format as array
+ JsonInputStream &operator<<(const QVector<QByteArray> &ba);
+
+ JsonInputStream &operator<<(bool b);
+
+ JsonInputStream &operator<<(int i)
+ { m_target.append(QByteArray::number(i)); return *this; }
+ JsonInputStream &operator<<(unsigned i)
+ { m_target.append(QByteArray::number(i)); return *this; }
+ JsonInputStream &operator<<(quint64 i)
+ { m_target.append(QByteArray::number(i)); return *this; }
+
+private:
+ void appendString(const QString &);
+ void appendCString(const char *c);
+
+ QByteArray &m_target;
+};
+
+} // namespace tcftrk
+
+#endif // SYMBIANUTILS_JSON_H
diff --git a/src/runonphone/symbianutils/launcher.cpp b/src/runonphone/symbianutils/launcher.cpp
new file mode 100644
index 000000000..a5d3d68ae
--- /dev/null
+++ b/src/runonphone/symbianutils/launcher.cpp
@@ -0,0 +1,1036 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "launcher.h"
+#include "trkutils.h"
+#include "trkutils_p.h"
+#include "trkdevice.h"
+#include "bluetoothlistener.h"
+#include "symbiandevicemanager.h"
+
+#include <QtCore/QTimer>
+#include <QtCore/QDateTime>
+#include <QtCore/QVariant>
+#include <QtCore/QDebug>
+#include <QtCore/QQueue>
+#include <QtCore/QFile>
+#include <QtCore/QScopedPointer>
+
+#include <cstdio>
+
+namespace trk {
+
+struct CrashReportState {
+ CrashReportState();
+ void clear();
+
+ typedef uint Thread;
+ typedef QList<Thread> Threads;
+ Threads threads;
+
+ QList<uint> registers;
+ QByteArray stack;
+ uint sp;
+ uint fetchingStackPID;
+ uint fetchingStackTID;
+};
+
+CrashReportState::CrashReportState()
+{
+ clear();
+}
+
+void CrashReportState::clear()
+{
+ threads.clear();
+ stack.clear();
+ sp = fetchingStackPID = fetchingStackTID = 0;
+}
+
+struct LauncherPrivate {
+ struct CopyState {
+ QString sourceFileName;
+ QString destinationFileName;
+ uint copyFileHandle;
+ QScopedPointer<QByteArray> data;
+ qint64 position;
+ QScopedPointer<QFile> localFile;
+ };
+
+ explicit LauncherPrivate(const TrkDevicePtr &d);
+
+ TrkDevicePtr m_device;
+ QByteArray m_trkReadBuffer;
+ Launcher::State m_state;
+
+ void logMessage(const QString &msg);
+ // Debuggee state
+ Session m_session; // global-ish data (process id, target information)
+
+ CopyState m_copyState;
+ CopyState m_downloadState;
+ QString m_fileName;
+ QStringList m_commandLineArgs;
+ QString m_installFileName;
+ int m_verbose;
+ Launcher::Actions m_startupActions;
+ bool m_closeDevice;
+ CrashReportState m_crashReportState;
+};
+
+LauncherPrivate::LauncherPrivate(const TrkDevicePtr &d) :
+ m_device(d),
+ m_state(Launcher::Disconnected),
+ m_verbose(0),
+ m_closeDevice(true)
+{
+ if (m_device.isNull())
+ m_device = TrkDevicePtr(new TrkDevice);
+}
+
+Launcher::Launcher(Actions startupActions,
+ const TrkDevicePtr &dev,
+ QObject *parent) :
+ QObject(parent),
+ d(new LauncherPrivate(dev))
+{
+ d->m_startupActions = startupActions;
+ connect(d->m_device.data(), SIGNAL(messageReceived(trk::TrkResult)), this, SLOT(handleResult(trk::TrkResult)));
+}
+
+Launcher::~Launcher()
+{
+ // Destroyed before protocol was through: Close
+ if (d->m_closeDevice && d->m_device->isOpen())
+ d->m_device->close();
+ emit destroyed(d->m_device->port());
+ logMessage("Shutting down.\n");
+ delete d;
+}
+
+Launcher::State Launcher::state() const
+{
+ return d->m_state;
+}
+
+void Launcher::setState(State s)
+{
+ if (s != d->m_state) {
+ d->m_state = s;
+ emit stateChanged(s);
+ }
+}
+
+void Launcher::addStartupActions(trk::Launcher::Actions startupActions)
+{
+ d->m_startupActions = Actions(d->m_startupActions | startupActions);
+}
+
+void Launcher::setTrkServerName(const QString &name)
+{
+ d->m_device->setPort(name);
+}
+
+QString Launcher::trkServerName() const
+{
+ return d->m_device->port();
+}
+
+TrkDevicePtr Launcher::trkDevice() const
+{
+ return d->m_device;
+}
+
+void Launcher::setFileName(const QString &name)
+{
+ d->m_fileName = name;
+}
+
+void Launcher::setCopyFileName(const QString &srcName, const QString &dstName)
+{
+ d->m_copyState.sourceFileName = srcName;
+ d->m_copyState.destinationFileName = dstName;
+}
+
+void Launcher::setDownloadFileName(const QString &srcName, const QString &dstName)
+{
+ d->m_downloadState.sourceFileName = srcName;
+ d->m_downloadState.destinationFileName = dstName;
+}
+
+void Launcher::setInstallFileName(const QString &name)
+{
+ d->m_installFileName = name;
+}
+
+void Launcher::setCommandLineArgs(const QStringList &args)
+{
+ d->m_commandLineArgs = args;
+}
+
+void Launcher::setSerialFrame(bool b)
+{
+ d->m_device->setSerialFrame(b);
+}
+
+bool Launcher::serialFrame() const
+{
+ return d->m_device->serialFrame();
+}
+
+
+bool Launcher::closeDevice() const
+{
+ return d->m_closeDevice;
+}
+
+void Launcher::setCloseDevice(bool c)
+{
+ d->m_closeDevice = c;
+}
+
+bool Launcher::startServer(QString *errorMessage)
+{
+ errorMessage->clear();
+ d->m_crashReportState.clear();
+ if (d->m_verbose) {
+ QString msg;
+ QTextStream str(&msg);
+ str.setIntegerBase(16);
+ str << "Actions=0x" << d->m_startupActions;
+ str.setIntegerBase(10);
+ str << " Port=" << trkServerName();
+ if (!d->m_fileName.isEmpty())
+ str << " Executable=" << d->m_fileName;
+ if (!d->m_commandLineArgs.isEmpty())
+ str << " Arguments= " << d->m_commandLineArgs.join(QString(QLatin1Char(' ')));
+ if (!d->m_copyState.sourceFileName.isEmpty())
+ str << " Package/Source=" << d->m_copyState.sourceFileName;
+ if (!d->m_copyState.destinationFileName.isEmpty())
+ str << " Remote Package/Destination=" << d->m_copyState.destinationFileName;
+ if (!d->m_downloadState.sourceFileName.isEmpty())
+ str << " Source=" << d->m_downloadState.sourceFileName;
+ if (!d->m_downloadState.destinationFileName.isEmpty())
+ str << " Destination=" << d->m_downloadState.destinationFileName;
+ if (!d->m_installFileName.isEmpty())
+ str << " Install file=" << d->m_installFileName;
+ logMessage(msg);
+ }
+ if (d->m_startupActions & ActionCopy) {
+ if (d->m_copyState.sourceFileName.isEmpty()) {
+ qWarning("No local filename given for copying package.");
+ return false;
+ } else if (d->m_copyState.destinationFileName.isEmpty()) {
+ qWarning("No remote filename given for copying package.");
+ return false;
+ }
+ }
+ if (d->m_startupActions & ActionInstall && d->m_installFileName.isEmpty()) {
+ qWarning("No package name given for installing.");
+ return false;
+ }
+ if (d->m_startupActions & ActionRun && d->m_fileName.isEmpty()) {
+ qWarning("No remote executable given for running.");
+ return false;
+ }
+ if (!d->m_device->isOpen() && !d->m_device->open(errorMessage))
+ return false;
+ setState(Connecting);
+ // Set up the temporary 'waiting' state if we do not get immediate connection
+ QTimer::singleShot(1000, this, SLOT(slotWaitingForTrk()));
+ d->m_device->sendTrkInitialPing();
+ d->m_device->sendTrkMessage(TrkDisconnect); // Disconnect, as trk might be still connected
+ d->m_device->sendTrkMessage(TrkSupported, TrkCallback(this, &Launcher::handleSupportMask));
+ d->m_device->sendTrkMessage(TrkCpuType, TrkCallback(this, &Launcher::handleCpuType));
+ d->m_device->sendTrkMessage(TrkVersions, TrkCallback(this, &Launcher::handleTrkVersion));
+ if (d->m_startupActions != ActionPingOnly)
+ d->m_device->sendTrkMessage(TrkConnect, TrkCallback(this, &Launcher::handleConnect));
+ return true;
+}
+
+void Launcher::slotWaitingForTrk()
+{
+ // Set temporary state if we are still in connected state
+ if (state() == Connecting)
+ setState(WaitingForTrk);
+}
+
+void Launcher::handleConnect(const TrkResult &result)
+{
+ if (result.errorCode()) {
+ emit canNotConnect(result.errorString());
+ return;
+ }
+ setState(Connected);
+ if (d->m_startupActions & ActionCopy)
+ copyFileToRemote();
+ else if (d->m_startupActions & ActionInstall)
+ installRemotePackageSilently();
+ else if (d->m_startupActions & ActionRun)
+ startInferiorIfNeeded();
+ else if (d->m_startupActions & ActionDownload)
+ copyFileFromRemote();
+}
+
+void Launcher::setVerbose(int v)
+{
+ d->m_verbose = v;
+ d->m_device->setVerbose(v);
+}
+
+void Launcher::logMessage(const QString &msg)
+{
+ if (d->m_verbose)
+ qDebug() << "LAUNCHER: " << qPrintable(msg);
+}
+
+void Launcher::handleFinished()
+{
+ if (d->m_closeDevice)
+ d->m_device->close();
+ emit finished();
+}
+
+void Launcher::terminate()
+{
+ switch (state()) {
+ case DeviceDescriptionReceived:
+ case Connected:
+ if (d->m_session.pid) {
+ QByteArray ba;
+ appendShort(&ba, 0x0000, TargetByteOrder);
+ appendInt(&ba, d->m_session.pid, TargetByteOrder);
+ d->m_device->sendTrkMessage(TrkDeleteItem, TrkCallback(this, &Launcher::handleRemoteProcessKilled), ba);
+ return;
+ }
+ if (d->m_copyState.copyFileHandle)
+ closeRemoteFile(true);
+ disconnectTrk();
+ break;
+ case Disconnected:
+ break;
+ case Connecting:
+ case WaitingForTrk:
+ setState(Disconnected);
+ handleFinished();
+ break;
+ }
+}
+
+void Launcher::handleRemoteProcessKilled(const TrkResult &result)
+{
+ Q_UNUSED(result)
+ disconnectTrk();
+}
+
+QString Launcher::msgStopped(uint pid, uint tid, uint address, const QString &why)
+{
+ return QString::fromLatin1("Process %1, thread %2 stopped at 0x%3: %4").
+ arg(pid).arg(tid).arg(address, 0, 16).
+ arg(why.isEmpty() ? QString::fromLatin1("<Unknown reason>") : why);
+}
+
+bool Launcher::parseNotifyStopped(const QByteArray &dataBA,
+ uint *pid, uint *tid, uint *address,
+ QString *why /* = 0 */)
+{
+ if (why)
+ why->clear();
+ *address = *pid = *tid = 0;
+ if (dataBA.size() < 12)
+ return false;
+ const char *data = dataBA.data();
+ *address = extractInt(data);
+ *pid = extractInt(data + 4);
+ *tid = extractInt(data + 8);
+ if (why && dataBA.size() >= 14) {
+ const unsigned short len = extractShort(data + 12);
+ if (len > 0)
+ *why = QString::fromLatin1(data + 14, len);
+ }
+ return true;
+}
+
+void Launcher::handleResult(const TrkResult &result)
+{
+ QByteArray prefix = "READ BUF: ";
+ QByteArray str = result.toString().toUtf8();
+ if (result.isDebugOutput) { // handle application output
+ QString msg;
+ if (result.multiplex == MuxTextTrace) {
+ if (result.data.length() > 8) {
+ quint64 timestamp = extractInt64(result.data) & 0x0FFFFFFFFFFFFFFFULL;
+ quint64 secs = timestamp / 1000000000;
+ quint64 ns = timestamp % 1000000000;
+ msg = QString("[%1.%2] %3").arg(secs).arg(ns).arg(QString(result.data.mid(8)));
+ logMessage("TEXT TRACE: " + msg);
+ }
+ } else {
+ logMessage("APPLICATION OUTPUT: " + stringFromArray(result.data));
+ msg = result.data;
+ }
+ msg.replace("\r\n", "\n");
+ if(!msg.endsWith('\n')) msg.append('\n');
+ emit applicationOutputReceived(msg);
+ return;
+ }
+ switch (result.code) {
+ case TrkNotifyAck:
+ break;
+ case TrkNotifyNak: { // NAK
+ logMessage(prefix + "NAK: " + str);
+ //logMessage(prefix << "TOKEN: " << result.token);
+ logMessage(prefix + "ERROR: " + errorMessage(result.data.at(0)));
+ break;
+ }
+ case TrkNotifyStopped: { // Notified Stopped
+ QString reason;
+ uint pc;
+ uint pid;
+ uint tid;
+ parseNotifyStopped(result.data, &pid, &tid, &pc, &reason);
+ logMessage(prefix + msgStopped(pid, tid, pc, reason));
+ emit(processStopped(pc, pid, tid, reason));
+ d->m_device->sendTrkAck(result.token);
+ break;
+ }
+ case TrkNotifyException: { // Notify Exception (obsolete)
+ logMessage(prefix + "NOTE: EXCEPTION " + str);
+ d->m_device->sendTrkAck(result.token);
+ break;
+ }
+ case TrkNotifyInternalError: { //
+ logMessage(prefix + "NOTE: INTERNAL ERROR: " + str);
+ d->m_device->sendTrkAck(result.token);
+ break;
+ }
+
+ // target->host OS notification
+ case TrkNotifyCreated: { // Notify Created
+
+ if (result.data.size() < 10)
+ break;
+ const char *data = result.data.constData();
+ const byte error = result.data.at(0);
+ Q_UNUSED(error)
+ const byte type = result.data.at(1); // type: 1 byte; for dll item, this value is 2.
+ const uint tid = extractInt(data + 6); //threadID: 4 bytes
+ Q_UNUSED(tid)
+ if (type == kDSOSDLLItem && result.data.size() >=20) {
+ const Library lib = Library(result);
+ d->m_session.libraries.push_back(lib);
+ emit libraryLoaded(lib);
+ }
+ QByteArray ba;
+ ba.append(result.data.mid(2, 8));
+ d->m_device->sendTrkMessage(TrkContinue, TrkCallback(), ba, "CONTINUE");
+ break;
+ }
+ case TrkNotifyDeleted: { // NotifyDeleted
+ const ushort itemType = (unsigned char)result.data.at(1);
+ const uint pid = result.data.size() >= 6 ? extractShort(result.data.constData() + 2) : 0;
+ const uint tid = result.data.size() >= 10 ? extractShort(result.data.constData() + 6) : 0;
+ Q_UNUSED(tid)
+ const ushort len = result.data.size() > 12 ? extractShort(result.data.constData() + 10) : ushort(0);
+ const QString name = len ? QString::fromAscii(result.data.mid(12, len)) : QString();
+ logMessage(QString::fromLatin1("%1 %2 UNLOAD: %3").
+ arg(QString::fromAscii(prefix)).arg(itemType ? QLatin1String("LIB") : QLatin1String("PROCESS")).
+ arg(name));
+ d->m_device->sendTrkAck(result.token);
+ if (itemType == kDSOSProcessItem // process
+ && result.data.size() >= 10
+ && d->m_session.pid == extractInt(result.data.data() + 6)) {
+ if (d->m_startupActions & ActionDownload)
+ copyFileFromRemote();
+ else
+ disconnectTrk();
+ }
+ else if (itemType == kDSOSDLLItem && len) {
+ // Remove libraries of process.
+ for (QList<Library>::iterator it = d->m_session.libraries.begin(); it != d->m_session.libraries.end(); ) {
+ if ((*it).pid == pid && (*it).name == name) {
+ emit libraryUnloaded(*it);
+ it = d->m_session.libraries.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ break;
+ }
+ case TrkNotifyProcessorStarted: { // NotifyProcessorStarted
+ logMessage(prefix + "NOTE: PROCESSOR STARTED: " + str);
+ d->m_device->sendTrkAck(result.token);
+ break;
+ }
+ case TrkNotifyProcessorStandBy: { // NotifyProcessorStandby
+ logMessage(prefix + "NOTE: PROCESSOR STANDBY: " + str);
+ d->m_device->sendTrkAck(result.token);
+ break;
+ }
+ case TrkNotifyProcessorReset: { // NotifyProcessorReset
+ logMessage(prefix + "NOTE: PROCESSOR RESET: " + str);
+ d->m_device->sendTrkAck(result.token);
+ break;
+ }
+ default: {
+ logMessage(prefix + "INVALID: " + str);
+ break;
+ }
+ }
+}
+
+QString Launcher::deviceDescription(unsigned verbose) const
+{
+ return d->m_session.deviceDescription(verbose);
+}
+
+void Launcher::handleTrkVersion(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 5) {
+ if (d->m_startupActions == ActionPingOnly) {
+ setState(Disconnected);
+ handleFinished();
+ }
+ return;
+ }
+ d->m_session.trkAppVersion.trkMajor = result.data.at(1);
+ d->m_session.trkAppVersion.trkMinor = result.data.at(2);
+ d->m_session.trkAppVersion.protocolMajor = result.data.at(3);
+ d->m_session.trkAppVersion.protocolMinor = result.data.at(4);
+ setState(DeviceDescriptionReceived);
+ const QString msg = deviceDescription();
+ emit deviceDescriptionReceived(trkServerName(), msg);
+ // Ping mode: Log & Terminate
+ if (d->m_startupActions == ActionPingOnly) {
+ qWarning("%s", qPrintable(msg));
+ setState(Disconnected);
+ handleFinished();
+ }
+}
+
+static inline QString msgCannotOpenRemoteFile(const QString &fileName, const QString &message)
+{
+ return Launcher::tr("Cannot open remote file '%1': %2").arg(fileName, message);
+}
+
+static inline QString msgCannotOpenLocalFile(const QString &fileName, const QString &message)
+{
+ return Launcher::tr("Cannot open '%1': %2").arg(fileName, message);
+}
+
+void Launcher::handleFileCreation(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 6) {
+ const QString msg = msgCannotOpenRemoteFile(d->m_copyState.destinationFileName, result.errorString());
+ logMessage(msg);
+ emit canNotCreateFile(d->m_copyState.destinationFileName, msg);
+ disconnectTrk();
+ return;
+ }
+ const char *data = result.data.data();
+ d->m_copyState.copyFileHandle = extractInt(data + 2);
+ const QString localFileName = d->m_copyState.sourceFileName;
+ QFile file(localFileName);
+ d->m_copyState.position = 0;
+ if (!file.open(QIODevice::ReadOnly)) {
+ const QString msg = msgCannotOpenLocalFile(localFileName, file.errorString());
+ logMessage(msg);
+ emit canNotOpenLocalFile(localFileName, msg);
+ closeRemoteFile(true);
+ disconnectTrk();
+ return;
+ }
+ d->m_copyState.data.reset(new QByteArray(file.readAll()));
+ file.close();
+ continueCopying();
+}
+
+void Launcher::handleFileOpened(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 6) {
+ const QString msg = msgCannotOpenRemoteFile(d->m_downloadState.sourceFileName, result.errorString());
+ logMessage(msg);
+ emit canNotOpenFile(d->m_downloadState.sourceFileName, msg);
+ disconnectTrk();
+ return;
+ }
+ d->m_downloadState.position = 0;
+ const QString localFileName = d->m_downloadState.destinationFileName;
+ bool opened = false;
+ if (localFileName == QLatin1String("-")) {
+ d->m_downloadState.localFile.reset(new QFile);
+ opened = d->m_downloadState.localFile->open(stdout, QFile::WriteOnly);
+ } else {
+ d->m_downloadState.localFile.reset(new QFile(localFileName));
+ opened = d->m_downloadState.localFile->open(QFile::WriteOnly | QFile::Truncate);
+ }
+ if (!opened) {
+ const QString msg = msgCannotOpenLocalFile(localFileName, d->m_downloadState.localFile->errorString());
+ logMessage(msg);
+ emit canNotOpenLocalFile(localFileName, msg);
+ closeRemoteFile(true);
+ disconnectTrk();
+ }
+ continueReading();
+}
+
+void Launcher::continueReading()
+{
+ QByteArray ba;
+ appendInt(&ba, d->m_downloadState.copyFileHandle, TargetByteOrder);
+ appendShort(&ba, 2048, TargetByteOrder);
+ d->m_device->sendTrkMessage(TrkReadFile, TrkCallback(this, &Launcher::handleRead), ba);
+}
+
+void Launcher::handleRead(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 4) {
+ d->m_downloadState.localFile->close();
+ closeRemoteFile(true);
+ disconnectTrk();
+ } else {
+ int length = extractShort(result.data.data() + 2);
+ //TRK doesn't tell us the file length, so we need to keep reading until it returns 0 length
+ if (length > 0) {
+ d->m_downloadState.localFile->write(result.data.data() + 4, length);
+ continueReading();
+ } else {
+ closeRemoteFile(true);
+ disconnectTrk();
+ }
+ }
+}
+
+void Launcher::handleCopy(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 4) {
+ closeRemoteFile(true);
+ emit canNotWriteFile(d->m_copyState.destinationFileName, result.errorString());
+ disconnectTrk();
+ } else {
+ continueCopying(extractShort(result.data.data() + 2));
+ }
+}
+
+void Launcher::continueCopying(uint lastCopiedBlockSize)
+{
+ qint64 size = d->m_copyState.data->length();
+ d->m_copyState.position += lastCopiedBlockSize;
+ if (size == 0)
+ emit copyProgress(100);
+ else {
+ const qint64 hundred = 100;
+ const qint64 percent = qMin( (d->m_copyState.position * hundred) / size, hundred);
+ emit copyProgress(static_cast<int>(percent));
+ }
+ if (d->m_copyState.position < size) {
+ QByteArray ba;
+ appendInt(&ba, d->m_copyState.copyFileHandle, TargetByteOrder);
+ appendString(&ba, d->m_copyState.data->mid(d->m_copyState.position, 2048), TargetByteOrder, false);
+ d->m_device->sendTrkMessage(TrkWriteFile, TrkCallback(this, &Launcher::handleCopy), ba);
+ } else {
+ closeRemoteFile();
+ }
+}
+
+void Launcher::closeRemoteFile(bool failed)
+{
+ QByteArray ba;
+ appendInt(&ba, d->m_copyState.copyFileHandle, TargetByteOrder);
+ appendDateTime(&ba, QDateTime::currentDateTime(), TargetByteOrder);
+ d->m_device->sendTrkMessage(TrkCloseFile,
+ failed ? TrkCallback() : TrkCallback(this, &Launcher::handleFileCopied),
+ ba);
+ d->m_copyState.data.reset();
+ d->m_copyState.copyFileHandle = 0;
+ d->m_copyState.position = 0;
+}
+
+void Launcher::handleFileCopied(const TrkResult &result)
+{
+ if (result.errorCode())
+ emit canNotCloseFile(d->m_copyState.destinationFileName, result.errorString());
+ if (d->m_startupActions & ActionInstall)
+ installRemotePackageSilently();
+ else if (d->m_startupActions & ActionRun)
+ startInferiorIfNeeded();
+ else if (d->m_startupActions & ActionDownload)
+ copyFileFromRemote();
+ else
+ disconnectTrk();
+}
+
+void Launcher::handleCpuType(const TrkResult &result)
+{
+ logMessage("HANDLE CPU TYPE: " + result.toString());
+ if(result.errorCode() || result.data.size() < 7)
+ return;
+ //---TRK------------------------------------------------------
+ // Command: 0x80 Acknowledge
+ // Error: 0x00
+ // [80 03 00 04 00 00 04 00 00 00]
+ d->m_session.cpuMajor = result.data.at(1);
+ d->m_session.cpuMinor = result.data.at(2);
+ d->m_session.bigEndian = result.data.at(3);
+ d->m_session.defaultTypeSize = result.data.at(4);
+ d->m_session.fpTypeSize = result.data.at(5);
+ d->m_session.extended1TypeSize = result.data.at(6);
+ //d->m_session.extended2TypeSize = result.data[6];
+}
+
+void Launcher::handleCreateProcess(const TrkResult &result)
+{
+ if (result.errorCode()) {
+ emit canNotRun(result.errorString());
+ disconnectTrk();
+ return;
+ }
+ // 40 00 00]
+ //logMessage(" RESULT: " + result.toString());
+ // [80 08 00 00 00 01 B5 00 00 01 B6 78 67 40 00 00 40 00 00]
+ const char *data = result.data.data();
+ d->m_session.pid = extractInt(data + 1);
+ d->m_session.tid = extractInt(data + 5);
+ d->m_session.codeseg = extractInt(data + 9);
+ d->m_session.dataseg = extractInt(data + 13);
+ if (d->m_verbose) {
+ const QString msg = QString::fromLatin1("Process id: %1 Thread id: %2 code: 0x%3 data: 0x%4").
+ arg(d->m_session.pid).arg(d->m_session.tid).arg(d->m_session.codeseg, 0, 16).
+ arg(d->m_session.dataseg, 0 ,16);
+ logMessage(msg);
+ }
+ emit applicationRunning(d->m_session.pid);
+ //create a "library" entry for the executable which launched the process
+ Library lib;
+ lib.pid = d->m_session.pid;
+ lib.codeseg = d->m_session.codeseg;
+ lib.dataseg = d->m_session.dataseg;
+ lib.name = d->m_fileName.toUtf8();
+ d->m_session.libraries << lib;
+ emit libraryLoaded(lib);
+
+ QByteArray ba;
+ appendInt(&ba, d->m_session.pid);
+ appendInt(&ba, d->m_session.tid);
+ d->m_device->sendTrkMessage(TrkContinue, TrkCallback(), ba, "CONTINUE");
+}
+
+void Launcher::handleWaitForFinished(const TrkResult &result)
+{
+ logMessage(" FINISHED: " + stringFromArray(result.data));
+ setState(Disconnected);
+ handleFinished();
+}
+
+void Launcher::handleSupportMask(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 32)
+ return;
+ const char *data = result.data.data() + 1;
+
+ if (d->m_verbose > 1) {
+ QString str = QLatin1String("SUPPORTED: ");
+ for (int i = 0; i < 32; ++i) {
+ for (int j = 0; j < 8; ++j) {
+ if (data[i] & (1 << j)) {
+ str.append(QString::number(i * 8 + j, 16));
+ str.append(QLatin1Char(' '));
+ }
+ }
+ }
+ logMessage(str);
+ }
+}
+
+void Launcher::cleanUp()
+{
+ //
+ //---IDE------------------------------------------------------
+ // Command: 0x41 Delete Item
+ // Sub Cmd: Delete Process
+ //ProcessID: 0x0000071F (1823)
+ // [41 24 00 00 00 00 07 1F]
+ QByteArray ba(2, char(0));
+ appendInt(&ba, d->m_session.pid);
+ d->m_device->sendTrkMessage(TrkDeleteItem, TrkCallback(), ba, "Delete process");
+
+ //---TRK------------------------------------------------------
+ // Command: 0x80 Acknowledge
+ // Error: 0x00
+ // [80 24 00]
+
+ //---IDE------------------------------------------------------
+ // Command: 0x1C Clear Break
+ // [1C 25 00 00 00 0A 78 6A 43 40]
+
+ //---TRK------------------------------------------------------
+ // Command: 0xA1 Notify Deleted
+ // [A1 09 00 00 00 00 00 00 00 00 07 1F]
+ //---IDE------------------------------------------------------
+ // Command: 0x80 Acknowledge
+ // Error: 0x00
+ // [80 09 00]
+
+ //---TRK------------------------------------------------------
+ // Command: 0x80 Acknowledge
+ // Error: 0x00
+ // [80 25 00]
+
+ //---IDE------------------------------------------------------
+ // Command: 0x1C Clear Break
+ // [1C 26 00 00 00 0B 78 6A 43 70]
+ //---TRK------------------------------------------------------
+ // Command: 0x80 Acknowledge
+ // Error: 0x00
+ // [80 26 00]
+
+
+ //---IDE------------------------------------------------------
+ // Command: 0x02 Disconnect
+ // [02 27]
+// sendTrkMessage(0x02, TrkCallback(this, &Launcher::handleDisconnect));
+ //---TRK------------------------------------------------------
+ // Command: 0x80 Acknowledge
+ // Error: 0x00
+}
+
+void Launcher::disconnectTrk()
+{
+ d->m_device->sendTrkMessage(TrkDisconnect, TrkCallback(this, &Launcher::handleWaitForFinished));
+}
+
+void Launcher::copyFileToRemote()
+{
+ emit copyingStarted();
+ QByteArray ba;
+ ba.append(char(10)); //kDSFileOpenWrite | kDSFileOpenBinary
+ appendString(&ba, d->m_copyState.destinationFileName.toLocal8Bit(), TargetByteOrder, false);
+ d->m_device->sendTrkMessage(TrkOpenFile, TrkCallback(this, &Launcher::handleFileCreation), ba);
+}
+
+void Launcher::copyFileFromRemote()
+{
+ emit copyingStarted();
+ QByteArray ba;
+ ba.append(char(9)); //kDSFileOpenRead | kDSFileOpenBinary
+ appendString(&ba, d->m_downloadState.sourceFileName.toLocal8Bit(), TargetByteOrder, false);
+ d->m_device->sendTrkMessage(TrkOpenFile, TrkCallback(this, &Launcher::handleFileOpened), ba);
+}
+
+void Launcher::installRemotePackageSilently()
+{
+ emit installingStarted();
+ QByteArray ba;
+ ba.append('C');
+ appendString(&ba, d->m_installFileName.toLocal8Bit(), TargetByteOrder, false);
+ d->m_device->sendTrkMessage(TrkInstallFile, TrkCallback(this, &Launcher::handleInstallPackageFinished), ba);
+}
+
+void Launcher::handleInstallPackageFinished(const TrkResult &result)
+{
+ if (result.errorCode()) {
+ emit canNotInstall(d->m_installFileName, result.errorString());
+ disconnectTrk();
+ return;
+ } else {
+ emit installingFinished();
+ }
+ if (d->m_startupActions & ActionRun) {
+ startInferiorIfNeeded();
+ } else if (d->m_startupActions & ActionDownload) {
+ copyFileFromRemote();
+ } else {
+ disconnectTrk();
+ }
+}
+
+QByteArray Launcher::startProcessMessage(const QString &executable,
+ const QStringList &arguments)
+{
+ // It's not started yet
+ QByteArray ba;
+ appendShort(&ba, 0, TargetByteOrder); // create new process (kDSOSProcessItem)
+ ba.append(char(0)); // options - currently unused
+ // One string consisting of binary terminated by '\0' and arguments terminated by '\0'
+ QByteArray commandLineBa = executable.toLocal8Bit();
+ commandLineBa.append(char(0));
+ if (!arguments.isEmpty())
+ commandLineBa.append(arguments.join(QString(QLatin1Char(' '))).toLocal8Bit());
+ appendString(&ba, commandLineBa, TargetByteOrder, true);
+ return ba;
+}
+
+QByteArray Launcher::readMemoryMessage(uint pid, uint tid, uint from, uint len)
+{
+ QByteArray ba;
+ ba.reserve(11);
+ ba.append(char(0x8)); // Options, FIXME: why?
+ appendShort(&ba, len);
+ appendInt(&ba, from);
+ appendInt(&ba, pid);
+ appendInt(&ba, tid);
+ return ba;
+}
+
+QByteArray Launcher::readRegistersMessage(uint pid, uint tid)
+{
+ QByteArray ba;
+ ba.reserve(15);
+ ba.append(char(0)); // Register set, only 0 supported
+ appendShort(&ba, 0); //R0
+ appendShort(&ba, 16); // last register CPSR
+ appendInt(&ba, pid);
+ appendInt(&ba, tid);
+ return ba;
+}
+
+void Launcher::startInferiorIfNeeded()
+{
+ emit startingApplication();
+ if (d->m_session.pid != 0) {
+ logMessage("Process already 'started'");
+ return;
+ }
+ d->m_device->sendTrkMessage(TrkCreateItem, TrkCallback(this, &Launcher::handleCreateProcess),
+ startProcessMessage(d->m_fileName, d->m_commandLineArgs)); // Create Item
+}
+
+void Launcher::resumeProcess(uint pid, uint tid)
+{
+ QByteArray ba;
+ appendInt(&ba, pid, BigEndian);
+ appendInt(&ba, tid, BigEndian);
+ d->m_device->sendTrkMessage(TrkContinue, TrkCallback(), ba, "CONTINUE");
+}
+
+// Acquire a device from SymbianDeviceManager, return 0 if not available.
+Launcher *Launcher::acquireFromDeviceManager(const QString &serverName,
+ QObject *parent,
+ QString *errorMessage)
+{
+ SymbianUtils::SymbianDeviceManager *sdm = SymbianUtils::SymbianDeviceManager::instance();
+ const QSharedPointer<trk::TrkDevice> device = sdm->acquireDevice(serverName);
+ if (device.isNull()) {
+ *errorMessage = tr("Unable to acquire a device for port '%1'. It appears to be in use.").arg(serverName);
+ return 0;
+ }
+ // Wire release signal.
+ Launcher *rc = new Launcher(trk::Launcher::ActionPingOnly, device, parent);
+ connect(rc, SIGNAL(deviceDescriptionReceived(QString,QString)),
+ sdm, SLOT(setAdditionalInformation(QString,QString)));
+ connect(rc, SIGNAL(destroyed(QString)), sdm, SLOT(releaseDevice(QString)));
+ return rc;
+}
+
+// Preliminary release of device, disconnecting the signal.
+void Launcher::releaseToDeviceManager(Launcher *launcher)
+{
+ SymbianUtils::SymbianDeviceManager *sdm = SymbianUtils::SymbianDeviceManager::instance();
+ // Disentangle launcher and its device, remove connection from destroyed
+ launcher->setCloseDevice(false);
+ TrkDevice *device = launcher->trkDevice().data();
+ launcher->disconnect(device);
+ device->disconnect(launcher);
+ launcher->disconnect(sdm);
+ sdm->releaseDevice(launcher->trkServerName());
+}
+
+void Launcher::getRegistersAndCallStack(uint pid, uint tid)
+{
+ d->m_device->sendTrkMessage(TrkReadRegisters,
+ TrkCallback(this, &Launcher::handleReadRegisters),
+ Launcher::readRegistersMessage(pid, tid));
+ d->m_crashReportState.fetchingStackPID = pid;
+ d->m_crashReportState.fetchingStackTID = tid;
+}
+
+void Launcher::handleReadRegisters(const TrkResult &result)
+{
+ if(result.errorCode() || result.data.size() < (17*4)) {
+ terminate();
+ return;
+ }
+ const char* data = result.data.constData() + 1;
+ d->m_crashReportState.registers.clear();
+ d->m_crashReportState.stack.clear();
+ for (int i=0;i<17;i++) {
+ uint r = extractInt(data);
+ data += 4;
+ d->m_crashReportState.registers.append(r);
+ }
+ d->m_crashReportState.sp = d->m_crashReportState.registers.at(13);
+
+ const ushort len = 1024 - (d->m_crashReportState.sp % 1024); //read to 1k boundary first
+ const QByteArray ba = Launcher::readMemoryMessage(d->m_crashReportState.fetchingStackPID,
+ d->m_crashReportState.fetchingStackTID,
+ d->m_crashReportState.sp,
+ len);
+ d->m_device->sendTrkMessage(TrkReadMemory, TrkCallback(this, &Launcher::handleReadStack), ba);
+ d->m_crashReportState.sp += len;
+}
+
+void Launcher::handleReadStack(const TrkResult &result)
+{
+ if (result.errorCode()) {
+ //error implies memory fault when reaching end of stack
+ emit registersAndCallStackReadComplete(d->m_crashReportState.registers, d->m_crashReportState.stack);
+ return;
+ }
+
+ const uint len = extractShort(result.data.constData() + 1);
+ d->m_crashReportState.stack.append(result.data.mid(3, len));
+
+ if (d->m_crashReportState.sp - d->m_crashReportState.registers.at(13) > 0x10000) {
+ //read enough stack, stop here
+ emit registersAndCallStackReadComplete(d->m_crashReportState.registers, d->m_crashReportState.stack);
+ return;
+ }
+ //read 1k more
+ const QByteArray ba = Launcher::readMemoryMessage(d->m_crashReportState.fetchingStackPID,
+ d->m_crashReportState.fetchingStackTID,
+ d->m_crashReportState.sp,
+ 1024);
+ d->m_device->sendTrkMessage(TrkReadMemory, TrkCallback(this, &Launcher::handleReadStack), ba);
+ d->m_crashReportState.sp += 1024;
+}
+
+} // namespace trk
diff --git a/src/runonphone/symbianutils/launcher.h b/src/runonphone/symbianutils/launcher.h
new file mode 100644
index 000000000..fac65b569
--- /dev/null
+++ b/src/runonphone/symbianutils/launcher.h
@@ -0,0 +1,212 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef LAUNCHER_H
+#define LAUNCHER_H
+
+#include "trkdevice.h"
+#include "trkutils.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QVariant>
+#include <QtCore/QSharedPointer>
+
+namespace trk {
+
+struct TrkResult;
+struct TrkMessage;
+struct LauncherPrivate;
+
+typedef QSharedPointer<TrkDevice> TrkDevicePtr;
+
+class SYMBIANUTILS_EXPORT Launcher : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(Launcher)
+public:
+ typedef void (Launcher::*TrkCallBack)(const TrkResult &);
+
+ enum Actions {
+ ActionPingOnly = 0x0,
+ ActionCopy = 0x1,
+ ActionInstall = 0x2,
+ ActionCopyInstall = ActionCopy | ActionInstall,
+ ActionRun = 0x4,
+ ActionDownload = 0x8,
+ ActionCopyRun = ActionCopy | ActionRun,
+ ActionInstallRun = ActionInstall | ActionRun,
+ ActionCopyInstallRun = ActionCopy | ActionInstall | ActionRun
+ };
+
+ enum State { Disconnected, Connecting, Connected,
+ WaitingForTrk, // This occurs only if the initial ping times out after
+ // a reasonable timeout, indicating that Trk is not
+ // running. Note that this will never happen with
+ // Bluetooth as communication immediately starts
+ // after connecting.
+ DeviceDescriptionReceived };
+
+ explicit Launcher(trk::Launcher::Actions startupActions = trk::Launcher::ActionPingOnly,
+ const TrkDevicePtr &trkDevice = TrkDevicePtr(),
+ QObject *parent = 0);
+ ~Launcher();
+
+ State state() const;
+
+ void addStartupActions(trk::Launcher::Actions startupActions);
+ void setTrkServerName(const QString &name);
+ QString trkServerName() const;
+ void setFileName(const QString &name);
+ void setCopyFileName(const QString &srcName, const QString &dstName);
+ void setDownloadFileName(const QString &srcName, const QString &dstName);
+ void setInstallFileName(const QString &name);
+ void setCommandLineArgs(const QStringList &args);
+ bool startServer(QString *errorMessage);
+ void setVerbose(int v);
+ void setSerialFrame(bool b);
+ bool serialFrame() const;
+ // Close device or leave it open
+ bool closeDevice() const;
+ void setCloseDevice(bool c);
+
+ TrkDevicePtr trkDevice() const;
+
+ // becomes valid after successful execution of ActionPingOnly
+ QString deviceDescription(unsigned verbose = 0u) const;
+
+ // Acquire a device from SymbianDeviceManager, return 0 if not available.
+ // The device will be released on destruction.
+ static Launcher *acquireFromDeviceManager(const QString &serverName,
+ QObject *parent,
+ QString *errorMessage);
+ // Preliminary release of device, disconnecting the signal.
+ static void releaseToDeviceManager(Launcher *l);
+
+ // Create Trk message to start a process.
+ static QByteArray startProcessMessage(const QString &executable,
+ const QStringList &arguments);
+ // Create Trk message to read memory
+ static QByteArray readMemoryMessage(uint pid, uint tid, uint from, uint len);
+ static QByteArray readRegistersMessage(uint pid, uint tid);
+ // Parse a TrkNotifyStopped message
+ static bool parseNotifyStopped(const QByteArray &a,
+ uint *pid, uint *tid, uint *address,
+ QString *why = 0);
+ // Helper message
+ static QString msgStopped(uint pid, uint tid, uint address, const QString &why);
+
+signals:
+ void deviceDescriptionReceived(const QString &port, const QString &description);
+ void copyingStarted();
+ void canNotConnect(const QString &errorMessage);
+ void canNotCreateFile(const QString &filename, const QString &errorMessage);
+ void canNotOpenFile(const QString &filename, const QString &errorMessage);
+ void canNotOpenLocalFile(const QString &filename, const QString &errorMessage);
+ void canNotWriteFile(const QString &filename, const QString &errorMessage);
+ void canNotCloseFile(const QString &filename, const QString &errorMessage);
+ void installingStarted();
+ void canNotInstall(const QString &packageFilename, const QString &errorMessage);
+ void installingFinished();
+ void startingApplication();
+ void applicationRunning(uint pid);
+ void canNotRun(const QString &errorMessage);
+ void finished();
+ void applicationOutputReceived(const QString &output);
+ void copyProgress(int percent);
+ void stateChanged(int);
+ void processStopped(uint pc, uint pid, uint tid, const QString& reason);
+ void processResumed(uint pid, uint tid);
+ void libraryLoaded(const trk::Library &lib);
+ void libraryUnloaded(const trk::Library &lib);
+ void registersAndCallStackReadComplete(const QList<uint>& registers, const QByteArray& stack);
+ // Emitted by the destructor, for releasing devices of SymbianDeviceManager by name
+ void destroyed(const QString &serverName);
+
+public slots:
+ void terminate();
+ void resumeProcess(uint pid, uint tid);
+ //can be used to obtain traceback after a breakpoint / exception
+ void getRegistersAndCallStack(uint pid, uint tid);
+
+private slots:
+ void handleResult(const trk::TrkResult &data);
+ void slotWaitingForTrk();
+
+private:
+ // kill process and breakpoints
+ void cleanUp();
+ void disconnectTrk();
+
+ void handleRemoteProcessKilled(const TrkResult &result);
+ void handleConnect(const TrkResult &result);
+ void handleFileCreation(const TrkResult &result);
+ void handleFileOpened(const TrkResult &result);
+ void handleCopy(const TrkResult &result);
+ void handleRead(const TrkResult &result);
+ void continueCopying(uint lastCopiedBlockSize = 0);
+ void continueReading();
+ void closeRemoteFile(bool failed = false);
+ void handleFileCopied(const TrkResult &result);
+ void handleInstallPackageFinished(const TrkResult &result);
+ void handleCpuType(const TrkResult &result);
+ void handleCreateProcess(const TrkResult &result);
+ void handleWaitForFinished(const TrkResult &result);
+ void handleStop(const TrkResult &result);
+ void handleSupportMask(const TrkResult &result);
+ void handleTrkVersion(const TrkResult &result);
+ void handleReadRegisters(const TrkResult &result);
+ void handleReadStack(const TrkResult &result);
+
+ void copyFileToRemote();
+ void copyFileFromRemote();
+ void installRemotePackageSilently();
+ void startInferiorIfNeeded();
+ void handleFinished();
+
+ void logMessage(const QString &msg);
+ void setState(State s);
+
+ LauncherPrivate *d;
+};
+
+} // namespace Trk
+
+#endif // LAUNCHER_H
diff --git a/src/runonphone/symbianutils/symbiandevicemanager.cpp b/src/runonphone/symbianutils/symbiandevicemanager.cpp
new file mode 100644
index 000000000..8d65d182c
--- /dev/null
+++ b/src/runonphone/symbianutils/symbiandevicemanager.cpp
@@ -0,0 +1,489 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "symbiandevicemanager.h"
+#include "trkdevice.h"
+
+#include <QtCore/QSettings>
+#include <QtCore/QStringList>
+#include <QtCore/QFileInfo>
+#include <QtCore/QtDebug>
+#include <QtCore/QTextStream>
+#include <QtCore/QSharedData>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QSignalMapper>
+
+namespace SymbianUtils {
+
+enum { debug = 0 };
+
+static const char REGKEY_CURRENT_CONTROL_SET[] = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet";
+static const char USBSER[] = "Services/usbser/Enum";
+
+const char *SymbianDeviceManager::linuxBlueToothDeviceRootC = "/dev/rfcomm";
+
+// ------------- SymbianDevice
+class SymbianDeviceData : public QSharedData {
+public:
+ SymbianDeviceData();
+ ~SymbianDeviceData();
+
+ inline bool isOpen() const { return !device.isNull() && device->isOpen(); }
+ void forcedClose();
+
+ QString portName;
+ QString friendlyName;
+ QString deviceDesc;
+ QString manufacturer;
+ QString additionalInformation;
+
+ DeviceCommunicationType type;
+ QSharedPointer<trk::TrkDevice> device;
+ bool deviceAcquired;
+};
+
+SymbianDeviceData::SymbianDeviceData() :
+ type(SerialPortCommunication),
+ deviceAcquired(false)
+{
+}
+
+SymbianDeviceData::~SymbianDeviceData()
+{
+ forcedClose();
+}
+
+void SymbianDeviceData::forcedClose()
+{
+ // Close the device when unplugging. Should devices be in 'acquired' state,
+ // their owners should hit on write failures.
+ // Apart from the <shared item> destructor, also called by the devicemanager
+ // to ensure it also happens if other shared instances are still around.
+ if (isOpen()) {
+ if (deviceAcquired)
+ qWarning("Device on '%s' unplugged while an operation is in progress.",
+ qPrintable(portName));
+ device->close();
+ }
+}
+
+SymbianDevice::SymbianDevice(SymbianDeviceData *data) :
+ m_data(data)
+{
+}
+
+SymbianDevice::SymbianDevice() :
+ m_data(new SymbianDeviceData)
+{
+}
+SymbianDevice::SymbianDevice(const SymbianDevice &rhs) :
+ m_data(rhs.m_data)
+{
+}
+
+SymbianDevice &SymbianDevice::operator=(const SymbianDevice &rhs)
+{
+ if (this != &rhs)
+ m_data = rhs.m_data;
+ return *this;
+}
+
+SymbianDevice::~SymbianDevice()
+{
+}
+
+void SymbianDevice::forcedClose()
+{
+ m_data->forcedClose();
+}
+
+QString SymbianDevice::portName() const
+{
+ return m_data->portName;
+}
+
+QString SymbianDevice::friendlyName() const
+{
+ return m_data->friendlyName;
+}
+
+QString SymbianDevice::additionalInformation() const
+{
+ return m_data->additionalInformation;
+}
+
+void SymbianDevice::setAdditionalInformation(const QString &a)
+{
+ m_data->additionalInformation = a;
+}
+
+SymbianDevice::TrkDevicePtr SymbianDevice::acquireDevice()
+{
+ if (debug)
+ qDebug() << "SymbianDevice::acquireDevice" << m_data->portName
+ << "acquired: " << m_data->deviceAcquired << " open: " << isOpen();
+ if (isNull() || m_data->deviceAcquired)
+ return TrkDevicePtr();
+ if (m_data->device.isNull()) {
+ m_data->device = TrkDevicePtr(new trk::TrkDevice);
+ m_data->device->setPort(m_data->portName);
+ m_data->device->setSerialFrame(m_data->type == SerialPortCommunication);
+ }
+ m_data->deviceAcquired = true;
+ return m_data->device;
+}
+
+void SymbianDevice::releaseDevice(TrkDevicePtr *ptr /* = 0 */)
+{
+ if (debug)
+ qDebug() << "SymbianDevice::releaseDevice" << m_data->portName
+ << " open: " << isOpen();
+ if (m_data->deviceAcquired) {
+ if (m_data->device->isOpen())
+ m_data->device->clearWriteQueue();
+ // Release if a valid pointer was passed in.
+ if (ptr && !ptr->isNull()) {
+ ptr->data()->disconnect();
+ *ptr = TrkDevicePtr();
+ }
+ m_data->deviceAcquired = false;
+ } else {
+ qWarning("Internal error: Attempt to release device that is not acquired.");
+ }
+}
+
+QString SymbianDevice::deviceDesc() const
+{
+ return m_data->deviceDesc;
+}
+
+QString SymbianDevice::manufacturer() const
+{
+ return m_data->manufacturer;
+}
+
+DeviceCommunicationType SymbianDevice::type() const
+{
+ return m_data->type;
+}
+
+bool SymbianDevice::isNull() const
+{
+ return m_data->portName.isEmpty();
+}
+
+bool SymbianDevice::isOpen() const
+{
+ return m_data->isOpen();
+}
+
+QString SymbianDevice::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ format(str);
+ return rc;
+}
+
+void SymbianDevice::format(QTextStream &str) const
+{
+ str << (m_data->type == BlueToothCommunication ? "Bluetooth: " : "Serial: ")
+ << m_data->portName;
+ if (!m_data->friendlyName.isEmpty()) {
+ str << " (" << m_data->friendlyName;
+ if (!m_data->deviceDesc.isEmpty())
+ str << " / " << m_data->deviceDesc;
+ str << ')';
+ }
+ if (!m_data->manufacturer.isEmpty())
+ str << " [" << m_data->manufacturer << ']';
+}
+
+// Compare by port and friendly name
+int SymbianDevice::compare(const SymbianDevice &rhs) const
+{
+ if (const int prc = m_data->portName.compare(rhs.m_data->portName))
+ return prc;
+ if (const int frc = m_data->friendlyName.compare(rhs.m_data->friendlyName))
+ return frc;
+ return 0;
+}
+
+SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDevice &cd)
+{
+ d.nospace() << cd.toString();
+ return d;
+}
+
+// ------------- SymbianDeviceManagerPrivate
+struct SymbianDeviceManagerPrivate {
+ SymbianDeviceManagerPrivate() : m_initialized(false), m_destroyReleaseMapper(0) {}
+
+ bool m_initialized;
+ SymbianDeviceManager::SymbianDeviceList m_devices;
+ QSignalMapper *m_destroyReleaseMapper;
+};
+
+SymbianDeviceManager::SymbianDeviceManager(QObject *parent) :
+ QObject(parent),
+ d(new SymbianDeviceManagerPrivate)
+{
+}
+
+SymbianDeviceManager::~SymbianDeviceManager()
+{
+ delete d;
+}
+
+SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::devices() const
+{
+ ensureInitialized();
+ return d->m_devices;
+}
+
+QString SymbianDeviceManager::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << d->m_devices.size() << " devices:\n";
+ const int count = d->m_devices.size();
+ for (int i = 0; i < count; i++) {
+ str << '#' << i << ' ';
+ d->m_devices.at(i).format(str);
+ str << '\n';
+ }
+ return rc;
+}
+
+int SymbianDeviceManager::findByPortName(const QString &p) const
+{
+ ensureInitialized();
+ const int count = d->m_devices.size();
+ for (int i = 0; i < count; i++)
+ if (d->m_devices.at(i).portName() == p)
+ return i;
+ return -1;
+}
+
+QString SymbianDeviceManager::friendlyNameForPort(const QString &port) const
+{
+ const int idx = findByPortName(port);
+ return idx == -1 ? QString() : d->m_devices.at(idx).friendlyName();
+}
+
+SymbianDeviceManager::TrkDevicePtr
+ SymbianDeviceManager::acquireDevice(const QString &port)
+{
+ ensureInitialized();
+ const int idx = findByPortName(port);
+ if (idx == -1) {
+ qWarning("Attempt to acquire device '%s' that does not exist.", qPrintable(port));
+ if (debug)
+ qDebug() << *this;
+ return TrkDevicePtr();
+ }
+ const TrkDevicePtr rc = d->m_devices[idx].acquireDevice();
+ if (debug)
+ qDebug() << "SymbianDeviceManager::acquireDevice" << port << " returns " << !rc.isNull();
+ return rc;
+}
+
+void SymbianDeviceManager::update()
+{
+ update(true);
+}
+
+void SymbianDeviceManager::releaseDevice(const QString &port)
+{
+ const int idx = findByPortName(port);
+ if (debug)
+ qDebug() << "SymbianDeviceManager::releaseDevice" << port << idx << sender();
+ if (idx != -1) {
+ d->m_devices[idx].releaseDevice();
+ } else {
+ qWarning("Attempt to release non-existing device %s.", qPrintable(port));
+ }
+}
+
+void SymbianDeviceManager::setAdditionalInformation(const QString &port, const QString &ai)
+{
+ const int idx = findByPortName(port);
+ if (idx != -1)
+ d->m_devices[idx].setAdditionalInformation(ai);
+}
+
+void SymbianDeviceManager::ensureInitialized() const
+{
+ if (!d->m_initialized) // Flag is set in update()
+ const_cast<SymbianDeviceManager*>(this)->update(false);
+}
+
+void SymbianDeviceManager::update(bool emitSignals)
+{
+ static int n = 0;
+ typedef SymbianDeviceList::iterator SymbianDeviceListIterator;
+
+ if (debug)
+ qDebug(">SerialDeviceLister::update(#%d, signals=%d)\n%s", n++, int(emitSignals),
+ qPrintable(toString()));
+
+ d->m_initialized = true;
+ // Get ordered new list
+ SymbianDeviceList newDevices = serialPorts() + blueToothDevices();
+ if (newDevices.size() > 1)
+ qStableSort(newDevices.begin(), newDevices.end());
+ if (d->m_devices == newDevices) { // Happy, nothing changed.
+ if (debug)
+ qDebug("<SerialDeviceLister::update: unchanged\n");
+ return;
+ }
+ // Merge the lists and emit the respective added/removed signals, assuming
+ // no one can plug a different device on the same port at the speed of lightning
+ if (!d->m_devices.isEmpty()) {
+ // Find deleted devices
+ for (SymbianDeviceListIterator oldIt = d->m_devices.begin(); oldIt != d->m_devices.end(); ) {
+ if (newDevices.contains(*oldIt)) {
+ ++oldIt;
+ } else {
+ SymbianDevice toBeDeleted = *oldIt;
+ toBeDeleted.forcedClose();
+ oldIt = d->m_devices.erase(oldIt);
+ if (emitSignals)
+ emit deviceRemoved(toBeDeleted);
+ }
+ }
+ }
+ if (!newDevices.isEmpty()) {
+ // Find new devices and insert in order
+ foreach(const SymbianDevice &newDevice, newDevices) {
+ if (!d->m_devices.contains(newDevice)) {
+ d->m_devices.append(newDevice);
+ if (emitSignals)
+ emit deviceAdded(newDevice);
+ }
+ }
+ if (d->m_devices.size() > 1)
+ qStableSort(d->m_devices.begin(), d->m_devices.end());
+ }
+ if (emitSignals)
+ emit updated();
+
+ if (debug)
+ qDebug("<SerialDeviceLister::update\n%s\n", qPrintable(toString()));
+}
+
+SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::serialPorts() const
+{
+ SymbianDeviceList rc;
+#ifdef Q_OS_WIN
+ const QSettings registry(REGKEY_CURRENT_CONTROL_SET, QSettings::NativeFormat);
+ const QString usbSerialRootKey = QLatin1String(USBSER) + QLatin1Char('/');
+ const int count = registry.value(usbSerialRootKey + QLatin1String("Count")).toInt();
+ for (int i = 0; i < count; ++i) {
+ QString driver = registry.value(usbSerialRootKey + QString::number(i)).toString();
+ if (driver.contains(QLatin1String("JAVACOMM"))) {
+ driver.replace(QLatin1Char('\\'), QLatin1Char('/'));
+ const QString driverRootKey = QLatin1String("Enum/") + driver + QLatin1Char('/');
+ if (debug > 1)
+ qDebug() << "SerialDeviceLister::serialPorts(): Checking " << i << count
+ << REGKEY_CURRENT_CONTROL_SET << usbSerialRootKey << driverRootKey;
+ QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
+ device->type = SerialPortCommunication;
+ device->friendlyName = registry.value(driverRootKey + QLatin1String("FriendlyName")).toString();
+ device->portName = registry.value(driverRootKey + QLatin1String("Device Parameters/PortName")).toString();
+ device->deviceDesc = registry.value(driverRootKey + QLatin1String("DeviceDesc")).toString();
+ device->manufacturer = registry.value(driverRootKey + QLatin1String("Mfg")).toString();
+ rc.append(SymbianDevice(device.take()));
+ }
+ }
+#endif
+ return rc;
+}
+
+SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::blueToothDevices() const
+{
+ SymbianDeviceList rc;
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
+ // Bluetooth devices are created on connection. List the existing ones
+ // or at least the first one.
+ const QString prefix = QLatin1String(linuxBlueToothDeviceRootC);
+ const QString blueToothfriendlyFormat = QLatin1String("Bluetooth device (%1)");
+ for (int d = 0; d < 4; d++) {
+ QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
+ device->type = BlueToothCommunication;
+ device->portName = prefix + QString::number(d);
+ if (d == 0 || QFileInfo(device->portName).exists()) {
+ device->friendlyName = blueToothfriendlyFormat.arg(device->portName);
+ rc.push_back(SymbianDevice(device.take()));
+ }
+ }
+ // New kernel versions support /dev/ttyUSB0, /dev/ttyUSB1. Trk responds
+ // on the latter (usually), try first.
+ static const char *usbTtyDevices[] = { "/dev/ttyUSB1", "/dev/ttyUSB0" };
+ const int usbTtyCount = sizeof(usbTtyDevices)/sizeof(const char *);
+ for (int d = 0; d < usbTtyCount; d++) {
+ const QString ttyUSBDevice = QLatin1String(usbTtyDevices[d]);
+ if (QFileInfo(ttyUSBDevice).exists()) {
+ SymbianDeviceData *device = new SymbianDeviceData;
+ device->type = SerialPortCommunication;
+ device->portName = ttyUSBDevice;
+ device->friendlyName = QString::fromLatin1("USB/Serial device (%1)").arg(device->portName);
+ rc.push_back(SymbianDevice(device));
+ }
+ }
+#endif
+ return rc;
+}
+
+Q_GLOBAL_STATIC(SymbianDeviceManager, symbianDeviceManager)
+
+SymbianDeviceManager *SymbianDeviceManager::instance()
+{
+ return symbianDeviceManager();
+}
+
+SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDeviceManager &sdm)
+{
+ d.nospace() << sdm.toString();
+ return d;
+}
+
+} // namespace SymbianUtilsInternal
diff --git a/src/runonphone/symbianutils/symbiandevicemanager.h b/src/runonphone/symbianutils/symbiandevicemanager.h
new file mode 100644
index 000000000..b4edd7884
--- /dev/null
+++ b/src/runonphone/symbianutils/symbiandevicemanager.h
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMBIANDEVICEMANAGER_H
+#define SYMBIANDEVICEMANAGER_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QExplicitlySharedDataPointer>
+#include <QtCore/QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace trk {
+ class TrkDevice;
+}
+
+namespace SymbianUtils {
+
+struct SymbianDeviceManagerPrivate;
+class SymbianDeviceData;
+
+enum DeviceCommunicationType {
+ SerialPortCommunication = 0,
+ BlueToothCommunication = 1
+};
+
+// SymbianDevice: Explicitly shared device data and a TrkDevice
+// instance that can be acquired (exclusively) for use.
+// A device removal from the manager will result in the
+// device being closed.
+class SYMBIANUTILS_EXPORT SymbianDevice {
+ explicit SymbianDevice(SymbianDeviceData *data);
+ friend class SymbianDeviceManager;
+public:
+ typedef QSharedPointer<trk::TrkDevice> TrkDevicePtr;
+
+ SymbianDevice();
+ SymbianDevice(const SymbianDevice &rhs);
+ SymbianDevice &operator=(const SymbianDevice &rhs);
+ ~SymbianDevice();
+ int compare(const SymbianDevice &rhs) const;
+
+ DeviceCommunicationType type() const;
+ bool isNull() const;
+ QString portName() const;
+ QString friendlyName() const;
+ QString additionalInformation() const;
+ void setAdditionalInformation(const QString &);
+
+ // Acquire: Mark the device as 'out' and return a shared pointer
+ // unless it is already in use by another owner. The result should not
+ // be passed on further.
+ TrkDevicePtr acquireDevice();
+ // Give back a device and mark it as 'free'.
+ void releaseDevice(TrkDevicePtr *ptr = 0);
+
+ bool isOpen() const;
+
+ // Windows only.
+ QString deviceDesc() const;
+ QString manufacturer() const;
+
+ void format(QTextStream &str) const;
+ QString toString() const;
+
+private:
+ void forcedClose();
+
+ QExplicitlySharedDataPointer<SymbianDeviceData> m_data;
+};
+
+SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDevice &);
+
+inline bool operator==(const SymbianDevice &d1, const SymbianDevice &d2)
+ { return d1.compare(d2) == 0; }
+inline bool operator!=(const SymbianDevice &d1, const SymbianDevice &d2)
+ { return d1.compare(d2) != 0; }
+inline bool operator<(const SymbianDevice &d1, const SymbianDevice &d2)
+ { return d1.compare(d2) < 0; }
+
+/* SymbianDeviceManager: Singleton that maintains a list of Symbian devices.
+ * and emits change signals.
+ * On Windows, the update slot must be connected to a [delayed] signal
+ * emitted from an event handler listening for WM_DEVICECHANGE.
+ * Device removal will result in the device being closed. */
+class SYMBIANUTILS_EXPORT SymbianDeviceManager : public QObject
+{
+ Q_OBJECT
+public:
+ typedef QList<SymbianDevice> SymbianDeviceList;
+ typedef QSharedPointer<trk::TrkDevice> TrkDevicePtr;
+
+ static const char *linuxBlueToothDeviceRootC;
+
+ // Do not use this constructor, it is just public for Q_GLOBAL_STATIC
+ explicit SymbianDeviceManager(QObject *parent = 0);
+ virtual ~SymbianDeviceManager();
+
+ // Singleton access.
+ static SymbianDeviceManager *instance();
+
+ SymbianDeviceList devices() const;
+ QString toString() const;
+
+ // Acquire a device for use. See releaseDevice().
+ TrkDevicePtr acquireDevice(const QString &port);
+
+ int findByPortName(const QString &p) const;
+ QString friendlyNameForPort(const QString &port) const;
+
+public slots:
+ void update();
+ // Relase a device, make it available for further use.
+ void releaseDevice(const QString &port);
+ void setAdditionalInformation(const QString &port, const QString &ai);
+
+signals:
+ void deviceRemoved(const SymbianUtils::SymbianDevice &d);
+ void deviceAdded(const SymbianUtils::SymbianDevice &d);
+ void updated();
+
+private:
+ void ensureInitialized() const;
+ void update(bool emitSignals);
+ SymbianDeviceList serialPorts() const;
+ SymbianDeviceList blueToothDevices() const;
+
+ SymbianDeviceManagerPrivate *d;
+};
+
+SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDeviceManager &);
+
+} // namespace SymbianUtils
+
+#endif // SYMBIANDEVICEMANAGER_H
diff --git a/src/runonphone/symbianutils/symbianutils.pri b/src/runonphone/symbianutils/symbianutils.pri
new file mode 100644
index 000000000..f07e494ed
--- /dev/null
+++ b/src/runonphone/symbianutils/symbianutils.pri
@@ -0,0 +1,35 @@
+INCLUDEPATH *= $$PWD
+
+QT += network
+
+# Input
+HEADERS += $$PWD/symbianutils_global.h \
+ $$PWD/callback.h \
+ $$PWD/trkutils.h \
+ $$PWD/trkutils_p.h \
+ $$PWD/trkdevice.h \
+ $$PWD/launcher.h \
+ $$PWD/bluetoothlistener.h \
+ $$PWD/communicationstarter.h \
+ $$PWD/symbiandevicemanager.h \
+ $$PWD/tcftrkdevice.h \
+ $$PWD/tcftrkmessage.h \
+ $$PWD/json.h
+
+SOURCES += $$PWD/trkutils.cpp \
+ $$PWD/trkdevice.cpp \
+ $$PWD/launcher.cpp \
+ $$PWD/bluetoothlistener.cpp \
+ $$PWD/communicationstarter.cpp \
+ $$PWD/symbiandevicemanager.cpp \
+ $$PWD/tcftrkdevice.cpp \
+ $$PWD/tcftrkmessage.cpp \
+ $$PWD/json.cpp
+
+# Tests/trklauncher is a console application
+contains(QT, gui) {
+ HEADERS += $$PWD/bluetoothlistener_gui.h
+ SOURCES += $$PWD/bluetoothlistener_gui.cpp
+} else {
+ message(Trk: Console ...)
+}
diff --git a/src/runonphone/symbianutils/symbianutils_global.h b/src/runonphone/symbianutils/symbianutils_global.h
new file mode 100644
index 000000000..d7acabffd
--- /dev/null
+++ b/src/runonphone/symbianutils/symbianutils_global.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMBIANUTILS_GLOBAL_H
+#define SYMBIANUTILS_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(SYMBIANUTILS_BUILD_LIB)
+# define SYMBIANUTILS_EXPORT Q_DECL_EXPORT
+#elif defined(SYMBIANUTILS_BUILD_STATIC_LIB) || defined(SYMBIANUTILS_INCLUDE_PRI)
+# define SYMBIANUTILS_EXPORT
+#else
+# define SYMBIANUTILS_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // SYMBIANUTILS_GLOBAL_H
diff --git a/src/runonphone/symbianutils/tcftrkdevice.cpp b/src/runonphone/symbianutils/tcftrkdevice.cpp
new file mode 100644
index 000000000..5b923ee90
--- /dev/null
+++ b/src/runonphone/symbianutils/tcftrkdevice.cpp
@@ -0,0 +1,929 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "tcftrkdevice.h"
+#include "json.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 messageTerminatorC[] = "\003\001";
+
+namespace tcftrk {
+// ------------- TcfTrkCommandError
+
+TcfTrkCommandError::TcfTrkCommandError() : timeMS(0), code(0), alternativeCode(0)
+{
+}
+
+void TcfTrkCommandError::clear()
+{
+ timeMS = 0;
+ code = alternativeCode = 0;
+ format.clear();
+ alternativeOrganization.clear();
+}
+
+void TcfTrkCommandError::write(QTextStream &str) const
+{
+ if (timeMS) {
+ const QDateTime time(QDate(1970, 1, 1));
+ str << time.addMSecs(timeMS).toString(Qt::ISODate) << ": Error code: " << code
+ << " '" << format << '\'';
+ if (!alternativeOrganization.isEmpty())
+ str << " ('" << alternativeOrganization << "', code: " << alternativeCode << ')';
+ } else{
+ str << "<No error>";
+ }
+}
+
+QString TcfTrkCommandError::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ write(str);
+ return rc;
+}
+
+/* {"Time":1277459762255,"Code":1,"AltCode":-6,"AltOrg":"POSIX","Format":"Unknown error: -6"} */
+bool TcfTrkCommandError::parse(const QVector<JsonValue> &values)
+{
+ // Parse an arbitrary hash (that could as well be a command response)
+ // and check for error elements.
+ unsigned errorKeyCount = 0;
+ clear();
+ do {
+ if (values.isEmpty() || values.front().type() != JsonValue::Object)
+ break;
+ foreach (const JsonValue &c, values.front().children()) {
+ if (c.name() == "Time") {
+ timeMS = c.data().toULongLong();
+ errorKeyCount++;
+ } else if (c.name() == "Code") {
+ code = c.data().toInt();
+ errorKeyCount++;
+ } else if (c.name() == "Format") {
+ format = c.data();
+ errorKeyCount++;
+ } else if (c.name() == "AltCode") {
+ alternativeCode = c.data().toInt();
+ 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() << "TcfTrkCommandError::parse: Found error: " << errorFound;
+ if (!values.isEmpty())
+ qDebug() << values.front().toString();
+ }
+ return errorFound;
+}
+
+// ------------ TcfTrkCommandResult
+
+TcfTrkCommandResult::TcfTrkCommandResult(Type t) :
+ type(t), service(LocatorService)
+{
+}
+
+TcfTrkCommandResult::TcfTrkCommandResult(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 TcfTrkCommandResult::errorString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+
+ switch (type) {
+ case SuccessReply:
+ case ProgressReply:
+ str << "<No error>";
+ return rc;
+ case FailReply:
+ str << "NAK";
+ case CommandErrorReply:
+ commandError.write(str);
+ break;
+ }
+ // Append the failed command for reference
+ str << " (Command was: '";
+ QByteArray printableRequest = request;
+ printableRequest.replace('\0', '|');
+ str << printableRequest << "')";
+ return rc;
+}
+
+QString TcfTrkCommandResult::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;
+}
+
+struct TcfTrkSendQueueEntry
+{
+ typedef TcfTrkDevice::MessageType MessageType;
+
+ explicit TcfTrkSendQueueEntry(MessageType mt,
+ int tok,
+ Services s,
+ const QByteArray &d,
+ const TcfTrkCallback &cb= TcfTrkCallback(),
+ 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;
+ TcfTrkCallback callback;
+};
+
+struct TcfTrkDevicePrivate {
+ typedef TcfTrkDevice::IODevicePtr IODevicePtr;
+ typedef QHash<int, TcfTrkSendQueueEntry> TokenWrittenMessageMap;
+
+ TcfTrkDevicePrivate();
+
+ const QByteArray m_messageTerminator;
+
+ IODevicePtr m_device;
+ unsigned m_verbose;
+ QByteArray m_readBuffer;
+ int m_token;
+ QQueue<TcfTrkSendQueueEntry> m_sendQueue;
+ TokenWrittenMessageMap m_writtenMessages;
+ QVector<QByteArray> m_registerNames;
+};
+
+TcfTrkDevicePrivate::TcfTrkDevicePrivate() :
+ m_messageTerminator(messageTerminatorC),
+ m_verbose(0), m_token(0)
+{
+}
+
+TcfTrkDevice::TcfTrkDevice(QObject *parent) :
+ QObject(parent), d(new TcfTrkDevicePrivate)
+{
+}
+
+TcfTrkDevice::~TcfTrkDevice()
+{
+ delete d;
+}
+
+QVector<QByteArray> TcfTrkDevice::registerNames() const
+{
+ return d->m_registerNames;
+}
+
+void TcfTrkDevice::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);
+ }
+}
+
+TcfTrkDevice::IODevicePtr TcfTrkDevice::device() const
+{
+ return d->m_device;
+}
+
+TcfTrkDevice::IODevicePtr TcfTrkDevice::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 TcfTrkDevice::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 TcfTrkDevice::slotDeviceError()
+{
+ const QString message = d->m_device->errorString();
+ emitLogMessage(message);
+ emit error(message);
+}
+
+void TcfTrkDevice::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)
+{
+ message.replace('\0', '|');
+ const QString messageS = QString::fromLatin1(message);
+ return prefix ?
+ (QLatin1String(prefix) + messageS) : messageS;
+}
+
+void TcfTrkDevice::slotDeviceReadyRead()
+{
+ d->m_readBuffer += d->m_device->readAll();
+ // Take complete message off front of readbuffer.
+ do {
+ const int messageEndPos = d->m_readBuffer.indexOf(d->m_messageTerminator);
+ if (messageEndPos == -1)
+ break;
+ const QByteArray message = d->m_readBuffer.left(messageEndPos);
+ if (debug)
+ qDebug("Read:\n%s", qPrintable(formatData(message)));
+ if (const int errorCode = parseMessage(message)) {
+ emitLogMessage(QString::fromLatin1("Parse error %1 for: %2").arg(errorCode).arg(debugMessage(message)));
+ }
+ d->m_readBuffer.remove(0, messageEndPos + d->m_messageTerminator.size());
+ } while (!d->m_readBuffer.isEmpty());
+ checkSendQueue(); // Send off further 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 TcfTrkDevice::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 TcfTrkDevice::parseTcfCommandReply(char type, const QVector<QByteArray> &tokens)
+{
+ typedef TcfTrkDevicePrivate::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("TcfTrkDevice: Internal error: token %d not found for '%s'",
+ token, qPrintable(joinByteArrays(tokens)));
+ return 236;
+ }
+ // No callback: remove entry from map, happy
+ if (!it.value().callback) {
+ 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.
+ TcfTrkCallback callback = it.value().callback;
+ TcfTrkCommandResult result(type, it.value().service, it.value().data,
+ values, it.value().cookie);
+ d->m_writtenMessages.erase(it);
+ callback(result);
+ return 0;
+}
+
+static const char locatorAnswerC[] = "E\0Locator\0Hello\0[\"Locator\"]";
+
+int TcfTrkDevice::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<TcfTrkEvent> knownEvent(TcfTrkEvent::parseEvent(service, tokens.at(1), values));
+ if (!knownEvent.isNull()) {
+ // Answer hello event.
+ if (knownEvent->type() == TcfTrkEvent::LocatorHello)
+ 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 TcfTrkDevice::verbose() const
+{
+ return d->m_verbose;
+}
+
+void TcfTrkDevice::setVerbose(unsigned v)
+{
+ d->m_verbose = v;
+}
+
+void TcfTrkDevice::emitLogMessage(const QString &m)
+{
+ if (debug)
+ qWarning("%s", qPrintable(m));
+ emit logMessage(m);
+}
+
+bool TcfTrkDevice::checkOpen()
+{
+ if (d->m_device.isNull()) {
+ emitLogMessage(QLatin1String("Internal error: No device set on TcfTrkDevice."));
+ return false;
+ }
+ if (!d->m_device->isOpen()) {
+ emitLogMessage(QLatin1String("Internal error: Device not open in TcfTrkDevice."));
+ return false;
+ }
+ return true;
+}
+
+void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+ const char *commandParameters, int commandParametersLength,
+ const TcfTrkCallback &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 TcfTrkSendQueueEntry entry(mt, token, service, data, callBack, cookie);
+ d->m_sendQueue.enqueue(entry);
+ checkSendQueue();
+}
+
+void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+ const QByteArray &commandParameters,
+ const TcfTrkCallback &callBack,
+ const QVariant &cookie)
+{
+ sendTcfTrkMessage(mt, service, command, commandParameters.constData(), commandParameters.size(),
+ callBack, cookie);
+}
+
+// Enclose in message frame and write.
+void TcfTrkDevice::writeMessage(QByteArray data)
+{
+ if (!checkOpen())
+ return;
+
+ if (d->m_verbose)
+ emitLogMessage(debugMessage(data, "TCF <-"));
+
+ // Ensure \0-termination which easily gets lost in QByteArray CT.
+ if (!data.endsWith('\0'))
+ data.append('\0');
+ data += d->m_messageTerminator;
+
+ if (debug > 1)
+ qDebug("Writing:\n%s", qPrintable(formatData(data)));
+
+ d->m_device->write(data);
+ if (QAbstractSocket *as = qobject_cast<QAbstractSocket *>(d->m_device.data()))
+ as->flush();
+}
+
+void TcfTrkDevice::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;
+ TcfTrkSendQueueEntry 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) {
+ TcfTrkCommandResult noopResult(TcfTrkCommandResult::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 TcfTrkDevice::sendProcessStartCommand(const TcfTrkCallback &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);
+ const QString binaryDirectory = slashPos == -1 ? sysBin : binaryIn.left(slashPos);
+ const QString binary = fixFileName(binaryDirectory + QLatin1Char('/') + binaryFileName);
+
+ // Fixup: Does argv[0] convention exist on Symbian?
+ arguments.push_front(binary);
+ if (workingDirectory.isEmpty())
+ workingDirectory = sysBin;
+
+ // Format settings with empty dummy parameter
+ QByteArray setData;
+ JsonInputStream setStr(setData);
+ setStr << "" << '\0'
+ << '[' << "exeToLaunch" << ',' << "addExecutables" << ',' << "addLibraries" << ']'
+ << '\0' << '['
+ << binary << ','
+ << '{' << binaryFileName << ':' << QString::number(uid, 16) << '}' << ','
+ << additionalLibraries
+ << ']';
+ sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData);
+
+ QByteArray startData;
+ JsonInputStream startStr(startData);
+ startStr << fixFileName(workingDirectory)
+ << '\0' << binary << '\0' << arguments << '\0'
+ << QStringList() << '\0' // Env is an array ["PATH=value"] (non-standard)
+ << debugControl;
+ sendTcfTrkMessage(MessageWithReply, ProcessesService, "start", startData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendProcessTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendTcfTrkMessage(MessageWithReply, ProcessesService, "terminate", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendTcfTrkMessage(MessageWithReply, RunControlService, "terminate", data, callBack, cookie);
+}
+
+// Non-standard: Remove executable from settings
+void TcfTrkDevice::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
+ << ']';
+ sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData, TcfTrkCallback(), cookie);
+}
+
+void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &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;
+ }
+ sendTcfTrkMessage(MessageWithReply, RunControlService, "resume", resumeData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlSuspendCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendTcfTrkMessage(MessageWithReply, RunControlService, "suspend", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ sendRunControlResumeCommand(callBack, id, RM_RESUME, 1, 0, 0, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsAddCommand(const TcfTrkCallback &callBack,
+ const Breakpoint &bp,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << bp;
+ sendTcfTrkMessage(MessageWithReply, BreakpointsService, "add", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ sendBreakpointsRemoveCommand(callBack, QVector<QByteArray>(1, id), cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << ids;
+ sendTcfTrkMessage(MessageWithReply, BreakpointsService, "remove", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ bool enable,
+ const QVariant &cookie)
+{
+ sendBreakpointsEnableCommand(callBack, QVector<QByteArray>(1, id), enable, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &ids,
+ bool enable,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << ids;
+ sendTcfTrkMessage(MessageWithReply, BreakpointsService,
+ enable ? "enable" : "disable",
+ data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendMemorySetCommand(const TcfTrkCallback &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();
+ sendTcfTrkMessage(MessageWithReply, MemoryService, "set", getData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendMemoryGetCommand(const TcfTrkCallback &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;
+ sendTcfTrkMessage(MessageWithReply, MemoryService, "get", data, callBack, cookie);
+}
+
+QByteArray TcfTrkDevice::parseMemoryGet(const TcfTrkCommandResult &r)
+{
+ if (r.type != TcfTrkCommandResult::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("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("TcfTrkDevice::parseMemoryGet: received %d bytes", memory.size());
+ return memory;
+}
+
+void TcfTrkDevice::sendRegistersGetMCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie)
+{
+ // TODO: use "Registers" (which uses base64-encoded values)
+ QByteArray data;
+ JsonInputStream str(data);
+ str << contextId << '\0' << ids;
+ sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "get", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned start, unsigned count)
+{
+ const unsigned end = start + count;
+ if (end > (unsigned)d->m_registerNames.size()) {
+ qWarning("TcfTrkDevice: 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 TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QByteArray &id,
+ unsigned value,
+ const QVariant &cookie)
+{
+ // TODO: use "Registers" (which uses base64-encoded values)
+ QByteArray data;
+ JsonInputStream str(data);
+ str << contextId << '\0' << QVector<QByteArray>(1, id)
+ << '\0' << QVector<QByteArray>(1, QByteArray::number(value, 16));
+ sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "set", data, callBack, cookie);
+}
+
+// Set register
+void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned registerNumber,
+ unsigned value,
+ const QVariant &cookie)
+{
+ if (registerNumber >= (unsigned)d->m_registerNames.size()) {
+ qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", registerNumber, d->m_registerNames.size());
+ return;
+ }
+ sendRegistersSetCommand(callBack, contextId,
+ d->m_registerNames[registerNumber],
+ value, cookie);
+}
+
+} // namespace tcftrk
diff --git a/src/runonphone/symbianutils/tcftrkdevice.h b/src/runonphone/symbianutils/tcftrkdevice.h
new file mode 100644
index 000000000..d42f3f3cb
--- /dev/null
+++ b/src/runonphone/symbianutils/tcftrkdevice.h
@@ -0,0 +1,295 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TCFTRKENGINE_H
+#define TCFTRKENGINE_H
+
+#include "symbianutils_global.h"
+#include "tcftrkmessage.h"
+#include "callback.h"
+#include "json.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QVector>
+#include <QtCore/QVariant>
+#include <QtCore/QStringList>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace tcftrk {
+
+struct TcfTrkDevicePrivate;
+struct Breakpoint;
+
+/* Command error handling in TCF:
+ * 1) 'Severe' errors (JSON format, parameter format): Trk emits a
+ * nonstandard message (\3\2 error parameters) and closes the connection.
+ * 2) Protocol errors: 'N' without error message is returned.
+ * 3) Errors in command execution: 'R' with a TCF error hash is returned
+ * (see TcfTrkCommandError). */
+
+/* Error code return in 'R' reply to command
+ * (see top of 'Services' documentation). */
+struct SYMBIANUTILS_EXPORT TcfTrkCommandError {
+ TcfTrkCommandError();
+ void clear();
+ operator bool() const { return timeMS != 0; }
+ QString toString() const;
+ void write(QTextStream &str) const;
+ bool parse(const QVector<JsonValue> &values);
+
+ quint64 timeMS; // Since 1.1.1970
+ int code;
+ QByteArray format; // message
+ // 'Alternative' meaning, like altOrg="POSIX"/altCode=<some errno>
+ QByteArray alternativeOrganization;
+ int alternativeCode;
+};
+
+/* Answer to a Tcf command passed to the callback. */
+struct SYMBIANUTILS_EXPORT TcfTrkCommandResult {
+ enum Type {
+ SuccessReply, // 'R' and no error -> all happy.
+ CommandErrorReply, // 'R' with TcfTrkCommandError received
+ ProgressReply, // 'P', progress indicator
+ FailReply // 'N' Protocol NAK, severe error
+ };
+
+ explicit TcfTrkCommandResult(Type t = SuccessReply);
+ explicit TcfTrkCommandResult(char typeChar, Services service,
+ const QByteArray &request,
+ const QVector<JsonValue> &values,
+ const QVariant &cookie);
+
+ QString toString() const;
+ QString errorString() const;
+ operator bool() const { return type == SuccessReply || type == ProgressReply; }
+
+ Type type;
+ Services service;
+ QByteArray request;
+ TcfTrkCommandError commandError;
+ QVector<JsonValue> values;
+ QVariant cookie;
+};
+
+typedef trk::Callback<const TcfTrkCommandResult &> TcfTrkCallback;
+
+/* TcfTrkDevice: TCF communication helper using an asynchronous QIODevice
+ * implementing the TCF protocol according to:
+http://dev.eclipse.org/svnroot/dsdp/org.eclipse.tm.tcf/trunk/docs/TCF%20Specification.html
+http://dev.eclipse.org/svnroot/dsdp/org.eclipse.tm.tcf/trunk/docs/TCF%20Services.html
+ * Commands can be sent along with callbacks that are passed a
+ * TcfTrkCommandResult and an opaque QVariant cookie. In addition, events are emitted.
+*/
+
+class SYMBIANUTILS_EXPORT TcfTrkDevice : public QObject
+{
+ Q_PROPERTY(unsigned verbose READ verbose WRITE setVerbose)
+ Q_OBJECT
+public:
+ enum MessageType { MessageWithReply,
+ MessageWithoutReply, /* Non-standard: "Settings:set" command does not reply */
+ NoopMessage };
+
+ typedef QSharedPointer<QIODevice> IODevicePtr;
+
+ explicit TcfTrkDevice(QObject *parent = 0);
+ virtual ~TcfTrkDevice();
+
+ unsigned verbose() const;
+
+ // Mapping of register names for indices
+ QVector<QByteArray> registerNames() const;
+ void setRegisterNames(const QVector<QByteArray>& n);
+
+ IODevicePtr device() const;
+ IODevicePtr takeDevice();
+ void setDevice(const IODevicePtr &dp);
+
+ void sendTcfTrkMessage(MessageType mt, Services service,
+ const char *command,
+ const char *commandParameters, int commandParametersLength,
+ const TcfTrkCallback &callBack = TcfTrkCallback(),
+ const QVariant &cookie = QVariant());
+
+ void sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+ const QByteArray &commandParameters,
+ const TcfTrkCallback &callBack = TcfTrkCallback(),
+ const QVariant &cookie = QVariant());
+
+ // Convenience messages: Start a process
+ void sendProcessStartCommand(const TcfTrkCallback &callBack,
+ const QString &binary,
+ unsigned uid,
+ QStringList arguments = QStringList(),
+ QString workingDirectory = QString(),
+ bool debugControl = true,
+ const QStringList &additionalLibraries = QStringList(),
+ const QVariant &cookie = QVariant());
+
+ // Preferred over Processes:Terminate by TCF TRK.
+ void sendRunControlTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendProcessTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ // Non-standard: Remove executable from settings.
+ // Probably needs to be called after stopping. This command has no response.
+ void sendSettingsRemoveExecutableCommand(const QString &binaryIn,
+ unsigned uid,
+ const QStringList &additionalLibraries = QStringList(),
+ const QVariant &cookie = QVariant());
+
+ void sendRunControlSuspendCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ // Resume / Step (see RunControlResumeMode).
+ void sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ RunControlResumeMode mode,
+ unsigned count /* = 1, currently ignored. */,
+ quint64 rangeStart, quint64 rangeEnd,
+ const QVariant &cookie = QVariant());
+
+ // Convenience to resume a suspended process
+ void sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsAddCommand(const TcfTrkCallback &callBack,
+ const Breakpoint &b,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ bool enable,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &id,
+ bool enable,
+ const QVariant &cookie = QVariant());
+
+
+ void sendMemoryGetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, quint64 size,
+ const QVariant &cookie = QVariant());
+
+ void sendMemorySetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, const QByteArray& data,
+ const QVariant &cookie = QVariant());
+
+ // Reply is an array of hexvalues
+ void sendRegistersGetMCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie = QVariant());
+
+ // Convenience to get a range of register "R0" .. "R<n>".
+ // Cookie will be an int containing "start".
+ void sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned start, unsigned count);
+
+ // Set register
+ void sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QByteArray &ids,
+ unsigned value,
+ const QVariant &cookie = QVariant());
+ // Set register
+ void sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned registerNumber,
+ unsigned value,
+ const QVariant &cookie = QVariant());
+
+ static QByteArray parseMemoryGet(const TcfTrkCommandResult &r);
+
+signals:
+ void genericTcfEvent(int service, const QByteArray &name, const QVector<tcftrk::JsonValue> &value);
+ void tcfEvent(const tcftrk::TcfTrkEvent &knownEvent);
+
+ void logMessage(const QString &);
+ void error(const QString &);
+
+public slots:
+ void setVerbose(unsigned v);
+
+private slots:
+ void slotDeviceError();
+ void slotDeviceSocketStateChanged();
+ void slotDeviceReadyRead();
+
+private:
+ bool checkOpen();
+ void checkSendQueue();
+ void writeMessage(QByteArray data);
+ void emitLogMessage(const QString &);
+ int parseMessage(const QByteArray &);
+ int parseTcfCommandReply(char type, const QVector<QByteArray> &tokens);
+ int parseTcfEvent(const QVector<QByteArray> &tokens);
+
+ TcfTrkDevicePrivate *d;
+};
+
+} // namespace tcftrk
+
+#endif // TCFTRKENGINE_H
diff --git a/src/runonphone/symbianutils/tcftrkmessage.cpp b/src/runonphone/symbianutils/tcftrkmessage.cpp
new file mode 100644
index 000000000..8d62dece2
--- /dev/null
+++ b/src/runonphone/symbianutils/tcftrkmessage.cpp
@@ -0,0 +1,562 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "tcftrkmessage.h"
+#include "json.h"
+
+#include <QtCore/QString>
+#include <QtCore/QTextStream>
+
+// Names matching the enum
+static const char *serviceNamesC[] =
+{ "Locator", "RunControl", "Processes", "Memory", "Settings", "Breakpoints",
+ "Registers", "SimpleRegisters",
+ "UnknownService"};
+
+namespace tcftrk {
+
+SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep)
+{
+ QString rc;
+ const int count = a.size();
+ for (int i = 0; i < count; i++) {
+ if (i)
+ rc += QLatin1Char(sep);
+ rc += QString::fromUtf8(a.at(i));
+ }
+ return rc;
+}
+
+static inline bool jsonToBool(const JsonValue& js)
+{
+ return js.type() == JsonValue::Boolean && js.data() == "true";
+}
+
+SYMBIANUTILS_EXPORT const char *serviceName(Services s)
+{
+ return serviceNamesC[s];
+}
+
+SYMBIANUTILS_EXPORT Services serviceFromName(const char *n)
+{
+ const int count = sizeof(serviceNamesC)/sizeof(char *);
+ for (int i = 0; i < count; i++)
+ if (!qstrcmp(serviceNamesC[i], n))
+ return static_cast<Services>(i);
+ return UnknownService;
+}
+
+SYMBIANUTILS_EXPORT QString formatData(const QByteArray &a)
+{
+ const int columns = 16;
+ QString rc;
+ QTextStream str(&rc);
+ str.setIntegerBase(16);
+ str.setPadChar(QLatin1Char('0'));
+ const unsigned char *start = reinterpret_cast<const unsigned char *>(a.constData());
+ const unsigned char *end = start + a.size();
+ for (const unsigned char *p = start; p < end ; ) {
+ str << "0x";
+ str.setFieldWidth(4);
+ str << (p - start);
+ str.setFieldWidth(0);
+ str << ' ';
+ QString asc;
+ int c = 0;
+ for ( ; c < columns && p < end; c++, p++) {
+ const unsigned u = *p;
+ str.setFieldWidth(2);
+ str << u;
+ str.setFieldWidth(0);
+ str << ' ';
+ switch (u) {
+ case '\n':
+ asc += QLatin1String("\\n");
+ break;
+ case '\r':
+ asc += QLatin1String("\\r");
+ break;
+ case '\t':
+ asc += QLatin1String("\\t");
+ break;
+ default:
+ if (u >= 32 && u < 128) {
+ asc += QLatin1Char(' ');
+ asc += QLatin1Char(u);
+ } else {
+ asc += QLatin1String(" .");
+ }
+ break;
+ }
+ }
+ if (const int remainder = columns - c)
+ str << QString(3 * remainder, QLatin1Char(' '));
+ str << ' ' << asc << '\n';
+ }
+ return rc;
+}
+
+// ----------- RunControlContext
+RunControlContext::RunControlContext() :
+ flags(0), resumeFlags(0)
+{
+}
+
+void RunControlContext::clear()
+{
+ flags =0;
+ resumeFlags = 0;
+ id.clear();
+ osid.clear();
+ parentId.clear();
+}
+
+RunControlContext::Type RunControlContext::typeFromTcfId(const QByteArray &id)
+{
+ // "p12" or "p12.t34"?
+ return id.contains(".t") ? Thread : Process;
+}
+
+unsigned RunControlContext::processId() const
+{
+ return processIdFromTcdfId(id);
+}
+
+unsigned RunControlContext::threadId() const
+{
+ return threadIdFromTcdfId(id);
+}
+
+unsigned RunControlContext::processIdFromTcdfId(const QByteArray &id)
+{
+ // Cut out process id from "p12" or "p12.t34"?
+ if (!id.startsWith('p'))
+ return 0;
+ const int dotPos = id.indexOf('.');
+ const int pLen = dotPos == -1 ? id.size() : dotPos;
+ return id.mid(1, pLen - 1).toUInt();
+}
+
+unsigned RunControlContext::threadIdFromTcdfId(const QByteArray &id)
+{
+ const int tPos = id.indexOf(".t");
+ return tPos != -1 ? id.mid(tPos + 2).toUInt() : uint(0);
+}
+
+QByteArray RunControlContext::tcfId(unsigned processId, unsigned threadId /* = 0 */)
+{
+ QByteArray rc("p");
+ rc += QByteArray::number(processId);
+ if (threadId) {
+ rc += ".t";
+ rc += QByteArray::number(threadId);
+ }
+ return rc;
+}
+
+RunControlContext::Type RunControlContext::type() const
+{
+ return RunControlContext::typeFromTcfId(id);
+}
+
+bool RunControlContext::parse(const JsonValue &val)
+{
+ clear();
+ if (val.type() != JsonValue::Object)
+ return false;
+ foreach(const JsonValue &c, val.children()) {
+ if (c.name() == "ID") {
+ id = c.data();
+ } else if (c.name() == "OSID") {
+ osid = c.data();
+ } else if (c.name() == "ParentID") {
+ parentId = c.data();
+ } else if (c.name() == "IsContainer") {
+ if (jsonToBool(c))
+ flags |= Container;
+ } else if (c.name() == "CanTerminate") {
+ if (jsonToBool(c))
+ flags |= CanTerminate;
+ } else if (c.name() == "CanResume") {
+ resumeFlags = c.data().toUInt();
+ } else if (c.name() == "HasState") {
+ if (jsonToBool(c))
+ flags |= HasState;
+ } else if (c.name() == "CanSuspend") {
+ if (jsonToBool(c))
+ flags |= CanSuspend;
+ }
+ }
+ return true;
+}
+
+QString RunControlContext::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ format(str);
+ return rc;
+}
+
+void RunControlContext::format(QTextStream &str) const
+{
+ str << " id='" << id << "' osid='" << osid
+ << "' parentId='" << parentId <<"' ";
+ if (flags & Container)
+ str << "[container] ";
+ if (flags & HasState)
+ str << "[has state] ";
+ if (flags & CanSuspend)
+ str << "[can suspend] ";
+ if (flags & CanSuspend)
+ str << "[can terminate] ";
+ str.setIntegerBase(16);
+ str << " resume_flags: 0x" << resumeFlags;
+ str.setIntegerBase(10);
+}
+
+// ------ ModuleLoadEventInfo
+ModuleLoadEventInfo::ModuleLoadEventInfo() :
+ loaded(false), codeAddress(0), dataAddress(0), requireResume(false)
+{
+}
+
+void ModuleLoadEventInfo::clear()
+{
+ loaded = requireResume = false;
+ codeAddress = dataAddress =0;
+}
+
+bool ModuleLoadEventInfo::parse(const JsonValue &val)
+{
+ clear();
+ if (val.type() != JsonValue::Object)
+ return false;
+ foreach(const JsonValue &c, val.children()) {
+ if (c.name() == "Name") {
+ name = c.data();
+ } else if (c.name() == "File") {
+ file = c.data();
+ } else if (c.name() == "CodeAddress") {
+ codeAddress = c.data().toULongLong();
+ } else if (c.name() == "DataAddress") {
+ dataAddress = c.data().toULongLong();
+ } else if (c.name() == "Loaded") {
+ loaded = jsonToBool(c);
+ } else if (c.name() == "RequireResume") {
+ requireResume =jsonToBool(c);
+ }
+ }
+ return true;
+}
+void ModuleLoadEventInfo::format(QTextStream &str) const
+{
+ str << "name='" << name << "' file='" << file << "' " <<
+ (loaded ? "[loaded] " : "[not loaded] ");
+ if (requireResume)
+ str << "[requires resume] ";
+ str.setIntegerBase(16);
+ str << " code: 0x" << codeAddress << " data: 0x" << dataAddress;
+ str.setIntegerBase(10);
+}
+
+// ---------------------- Breakpoint
+
+// Types matching enum
+static const char *breakPointTypesC[] = {"Software", "Hardware", "Auto"};
+
+Breakpoint::Breakpoint(quint64 loc) :
+ type(Auto), enabled(true), ignoreCount(0), location(loc), size(1), thumb(true)
+{
+ if (loc)
+ id = idFromLocation(location);
+}
+
+void Breakpoint::setContextId(unsigned processId, unsigned threadId)
+{
+ contextIds = QVector<QByteArray>(1, RunControlContext::tcfId(processId, threadId));
+}
+
+QByteArray Breakpoint::idFromLocation(quint64 loc)
+{
+ return QByteArray("BP_0x") + QByteArray::number(loc, 16);
+}
+
+QString Breakpoint::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str.setIntegerBase(16);
+ str << "Breakpoint '" << id << "' " << breakPointTypesC[type] << " for contexts '"
+ << joinByteArrays(contextIds, ',') << "' at 0x" << location;
+ str.setIntegerBase(10);
+ str << " size " << size;
+ if (enabled)
+ str << " [enabled]";
+ if (thumb)
+ str << " [thumb]";
+ if (ignoreCount)
+ str << " IgnoreCount " << ignoreCount;
+ return rc;
+}
+
+JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b)
+{
+ if (b.contextIds.isEmpty())
+ qWarning("tcftrk::Breakpoint: No context ids specified");
+
+ str << '{' << "ID" << ':' << QString::fromUtf8(b.id) << ','
+ << "BreakpointType" << ':' << breakPointTypesC[b.type] << ','
+ << "Enabled" << ':' << b.enabled << ','
+ << "IgnoreCount" << ':' << b.ignoreCount << ','
+ << "ContextIds" << ':' << b.contextIds << ','
+ << "Location" << ':' << QString::number(b.location) << ','
+ << "Size" << ':' << b.size << ','
+ << "THUMB_BREAKPOINT" << ':' << b.thumb
+ << '}';
+ return str;
+}
+
+// --- Events
+TcfTrkEvent::TcfTrkEvent(Type type) : m_type(type)
+{
+}
+
+TcfTrkEvent::~TcfTrkEvent()
+{
+}
+
+TcfTrkEvent::Type TcfTrkEvent::type() const
+{
+ return m_type;
+}
+
+QString TcfTrkEvent::toString() const
+{
+ return QString();
+}
+
+static const char sharedLibrarySuspendReasonC[] = "Shared Library";
+
+TcfTrkEvent *TcfTrkEvent::parseEvent(Services s, const QByteArray &nameBA, const QVector<JsonValue> &values)
+{
+ switch (s) {
+ case LocatorService:
+ if (nameBA == "Hello" && values.size() == 1 && values.front().type() == JsonValue::Array) {
+ QStringList services;
+ foreach (const JsonValue &jv, values.front().children())
+ services.push_back(QString::fromUtf8(jv.data()));
+ return new TcfTrkLocatorHelloEvent(services);
+ }
+ break;
+ case RunControlService:
+ if (values.empty())
+ return 0;
+ // "id/PC/Reason/Data"
+ if (nameBA == "contextSuspended" && values.size() == 4) {
+ const QByteArray idBA = values.at(0).data();
+ const quint64 pc = values.at(1).data().toULongLong();
+ const QByteArray reasonBA = values.at(2).data();
+ // Module load: Special
+ if (reasonBA == sharedLibrarySuspendReasonC) {
+ ModuleLoadEventInfo info;
+ if (!info.parse(values.at(3)))
+ return 0;
+ return new TcfTrkRunControlModuleLoadContextSuspendedEvent(idBA, reasonBA, pc, info);
+ }
+ return new TcfTrkRunControlContextSuspendedEvent(idBA, reasonBA, pc);
+ } // "contextSuspended"
+ if (nameBA == "contextAdded")
+ return TcfTrkRunControlContextAddedEvent::parseEvent(values);
+ if (nameBA == "contextRemoved" && values.front().type() == JsonValue::Array) {
+ QVector<QByteArray> ids;
+ foreach(const JsonValue &c, values.front().children())
+ ids.push_back(c.data());
+ return new TcfTrkRunControlContextRemovedEvent(ids);
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+// -------------- TcfTrkServiceHelloEvent
+TcfTrkLocatorHelloEvent::TcfTrkLocatorHelloEvent(const QStringList &s) :
+ TcfTrkEvent(LocatorHello),
+ m_services(s)
+{
+}
+
+QString TcfTrkLocatorHelloEvent::toString() const
+{
+ return QLatin1String("ServiceHello: ") + m_services.join(QLatin1String(", "));
+}
+
+// -------------- TcfTrkIdEvent
+TcfTrkIdEvent::TcfTrkIdEvent(Type t, const QByteArray &id) :
+ TcfTrkEvent(t), m_id(id)
+{
+}
+
+// ---------- TcfTrkIdsEvent
+TcfTrkIdsEvent::TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids) :
+ TcfTrkEvent(t), m_ids(ids)
+{
+}
+
+QString TcfTrkIdsEvent::joinedIdString(const char sep) const
+{
+ return joinByteArrays(m_ids, sep);
+}
+
+// ---------------- TcfTrkRunControlContextAddedEvent
+TcfTrkRunControlContextAddedEvent::TcfTrkRunControlContextAddedEvent(const RunControlContexts &c) :
+ TcfTrkEvent(RunControlContextAdded), m_contexts(c)
+{
+}
+
+TcfTrkRunControlContextAddedEvent
+ *TcfTrkRunControlContextAddedEvent::parseEvent(const QVector<JsonValue> &values)
+{
+ // Parse array of contexts
+ if (values.size() < 1 || values.front().type() != JsonValue::Array)
+ return 0;
+
+ RunControlContexts contexts;
+ foreach (const JsonValue &v, values.front().children()) {
+ RunControlContext context;
+ if (context.parse(v))
+ contexts.push_back(context);
+ }
+ return new TcfTrkRunControlContextAddedEvent(contexts);
+}
+
+QString TcfTrkRunControlContextAddedEvent::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << "RunControl: " << m_contexts.size() << " context(s) "
+ << (type() == RunControlContextAdded ? "added" : "removed")
+ << '\n';
+ foreach (const RunControlContext &c, m_contexts) {
+ c.format(str);
+ str << '\n';
+ }
+ return rc;
+}
+
+// --------------- TcfTrkRunControlContextRemovedEvent
+TcfTrkRunControlContextRemovedEvent::TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &ids) :
+ TcfTrkIdsEvent(RunControlContextRemoved, ids)
+{
+}
+
+QString TcfTrkRunControlContextRemovedEvent::toString() const
+{
+ return QLatin1String("RunControl: Removed contexts '") + joinedIdString() + ("'.");
+}
+
+// --------------- TcfTrkRunControlContextSuspendedEvent
+TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc) :
+ TcfTrkIdEvent(RunControlSuspended, id), m_pc(pc), m_reason(reason)
+{
+}
+
+TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(Type t,
+ const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc) :
+ TcfTrkIdEvent(t, id), m_pc(pc), m_reason(reason)
+{
+}
+
+void TcfTrkRunControlContextSuspendedEvent::format(QTextStream &str) const
+{
+ str.setIntegerBase(16);
+ str << "RunControl: '" << idString() << "' suspended at 0x"
+ << m_pc << ": '" << m_reason << "'.";
+ str.setIntegerBase(10);
+}
+
+QString TcfTrkRunControlContextSuspendedEvent::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ format(str);
+ return rc;
+}
+
+TcfTrkRunControlContextSuspendedEvent::Reason TcfTrkRunControlContextSuspendedEvent::reason() const
+{
+ if (m_reason == sharedLibrarySuspendReasonC)
+ return ModuleLoad;
+ if (m_reason == "Breakpoint")
+ return BreakPoint;
+ // 'Data abort exception'/'Thread has panicked' ... unfortunately somewhat unspecific.
+ if (m_reason.contains("exception") || m_reason.contains("panick"))
+ return Crash;
+ return Other;
+}
+
+TcfTrkRunControlModuleLoadContextSuspendedEvent::TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc,
+ const ModuleLoadEventInfo &mi) :
+ TcfTrkRunControlContextSuspendedEvent(RunControlModuleLoadSuspended, id, reason, pc),
+ m_mi(mi)
+{
+}
+
+QString TcfTrkRunControlModuleLoadContextSuspendedEvent::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ TcfTrkRunControlContextSuspendedEvent::format(str);
+ str << ' ';
+ m_mi.format(str);
+ return rc;
+}
+
+
+} // namespace tcftrk
diff --git a/src/runonphone/symbianutils/tcftrkmessage.h b/src/runonphone/symbianutils/tcftrkmessage.h
new file mode 100644
index 000000000..f2c8022e3
--- /dev/null
+++ b/src/runonphone/symbianutils/tcftrkmessage.h
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TRCFTRKMESSAGE_H
+#define TRCFTRKMESSAGE_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+QT_BEGIN_NAMESPACE
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace tcftrk {
+
+class JsonValue;
+class JsonInputStream;
+
+enum Services {
+ LocatorService,
+ RunControlService,
+ ProcessesService,
+ MemoryService,
+ SettingsService, // non-standard, trk specific
+ BreakpointsService,
+ RegistersService,
+ SimpleRegistersService, // non-standard, trk specific
+ UnknownService
+}; // Note: Check string array 'serviceNamesC' of same size when modifying this.
+
+// Modes of RunControl/'Resume' (see EDF documentation).
+// As of 24.6.2010, RM_RESUME, RM_STEP_OVER, RM_STEP_INTO,
+// RM_STEP_OVER_RANGE, RM_STEP_INTO_RANGE are supported with
+// RANG_START/RANGE_END parameters.
+enum RunControlResumeMode {
+ RM_RESUME = 0,
+ RM_STEP_OVER = 1, RM_STEP_INTO = 2,
+ RM_STEP_OVER_LINE = 3, RM_STEP_INTO_LINE = 4,
+ RM_STEP_OUT = 5, RM_REVERSE_RESUME = 6,
+ RM_REVERSE_STEP_OVER = 7, RM_REVERSE_STEP_INTO = 8,
+ RM_REVERSE_STEP_OVER_LINE = 9, RM_REVERSE_STEP_INTO_LINE = 10,
+ RM_REVERSE_STEP_OUT = 11, RM_STEP_OVER_RANGE = 12,
+ RM_STEP_INTO_RANGE = 13, RM_REVERSE_STEP_OVER_RANGE = 14,
+ RM_REVERSE_STEP_INTO_RANGE = 15
+};
+
+SYMBIANUTILS_EXPORT const char *serviceName(Services s);
+SYMBIANUTILS_EXPORT Services serviceFromName(const char *);
+
+// Debug helpers
+SYMBIANUTILS_EXPORT QString formatData(const QByteArray &a);
+SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep = ',');
+
+// Context used in 'RunControl contextAdded' events and in reply
+// to 'Processes start'. Could be thread or process.
+struct SYMBIANUTILS_EXPORT RunControlContext {
+ enum Flags {
+ Container = 0x1, HasState = 0x2, CanSuspend = 0x4,
+ CanTerminate = 0x8
+ };
+ enum Type { Process, Thread };
+
+ RunControlContext();
+ Type type() const;
+ unsigned processId() const;
+ unsigned threadId() const;
+
+ void clear();
+ bool parse(const JsonValue &v);
+ void format(QTextStream &str) const;
+ QString toString() const;
+
+ // Helper for converting the TCF ids ("p12" or "p12.t34")
+ static Type typeFromTcfId(const QByteArray &id);
+ static unsigned processIdFromTcdfId(const QByteArray &id);
+ static unsigned threadIdFromTcdfId(const QByteArray &id);
+ static QByteArray tcfId(unsigned processId, unsigned threadId = 0);
+
+ unsigned flags;
+ unsigned resumeFlags;
+ QByteArray id; // "p434.t699"
+ QByteArray osid; // Non-standard: Process or thread id
+ QByteArray parentId; // Parent process id of a thread.
+};
+
+// Module load information occurring with 'RunControl contextSuspended' events
+struct SYMBIANUTILS_EXPORT ModuleLoadEventInfo {
+ ModuleLoadEventInfo();
+ void clear();
+ bool parse(const JsonValue &v);
+ void format(QTextStream &str) const;
+
+ QByteArray name;
+ QByteArray file;
+ bool loaded;
+ quint64 codeAddress;
+ quint64 dataAddress;
+ bool requireResume;
+};
+
+// Breakpoint as supported by TcfTrk source June 2010
+// TODO: Add watchpoints,etc once they are implemented
+struct SYMBIANUTILS_EXPORT Breakpoint {
+ enum Type { Software, Hardware, Auto };
+
+ explicit Breakpoint(quint64 loc = 0);
+ void setContextId(unsigned processId, unsigned threadId = 0);
+ QString toString() const;
+
+ static QByteArray idFromLocation(quint64 loc); // Automagically determine from location
+
+ Type type;
+ bool enabled;
+ int ignoreCount;
+ QVector<QByteArray> contextIds; // Process or thread ids.
+ QByteArray id; // Id of the breakpoint;
+ quint64 location;
+ unsigned size;
+ bool thumb;
+};
+
+SYMBIANUTILS_EXPORT JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b);
+
+// Event hierarchy
+class SYMBIANUTILS_EXPORT TcfTrkEvent {
+ Q_DISABLE_COPY(TcfTrkEvent)
+public:
+ enum Type { None,
+ LocatorHello,
+ RunControlContextAdded,
+ RunControlContextRemoved,
+ RunControlSuspended,
+ RunControlBreakpointSuspended,
+ RunControlModuleLoadSuspended,
+ RunControlResumed
+ };
+
+ virtual ~TcfTrkEvent();
+
+ Type type() const;
+ virtual QString toString() const;
+
+ static TcfTrkEvent *parseEvent(Services s, const QByteArray &name, const QVector<JsonValue> &val);
+
+protected:
+ explicit TcfTrkEvent(Type type = None);
+
+private:
+ const Type m_type;
+};
+
+// ServiceHello
+class SYMBIANUTILS_EXPORT TcfTrkLocatorHelloEvent : public TcfTrkEvent {
+public:
+ explicit TcfTrkLocatorHelloEvent(const QStringList &);
+
+ const QStringList &services() { return m_services; }
+ virtual QString toString() const;
+
+private:
+ QStringList m_services;
+};
+
+// Base for events that just have one id as parameter
+// (simple suspend)
+class SYMBIANUTILS_EXPORT TcfTrkIdEvent : public TcfTrkEvent {
+protected:
+ explicit TcfTrkIdEvent(Type t, const QByteArray &id);
+public:
+ QByteArray id() const { return m_id; }
+ QString idString() const { return QString::fromUtf8(m_id); }
+
+private:
+ const QByteArray m_id;
+};
+
+// Base for events that just have some ids as parameter
+// (context removed)
+class SYMBIANUTILS_EXPORT TcfTrkIdsEvent : public TcfTrkEvent {
+protected:
+ explicit TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids);
+
+public:
+ QVector<QByteArray> ids() const { return m_ids; }
+ QString joinedIdString(const char sep = ',') const;
+
+private:
+ const QVector<QByteArray> m_ids;
+};
+
+// RunControlContextAdded
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextAddedEvent : public TcfTrkEvent {
+public:
+ typedef QVector<RunControlContext> RunControlContexts;
+
+ explicit TcfTrkRunControlContextAddedEvent(const RunControlContexts &c);
+
+ const RunControlContexts &contexts() const { return m_contexts; }
+ virtual QString toString() const;
+
+ static TcfTrkRunControlContextAddedEvent *parseEvent(const QVector<JsonValue> &val);
+
+private:
+ const RunControlContexts m_contexts;
+};
+
+// RunControlContextRemoved
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextRemovedEvent : public TcfTrkIdsEvent {
+public:
+ explicit TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &id);
+ virtual QString toString() const;
+};
+
+// Simple RunControlContextSuspended (process/thread)
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextSuspendedEvent : public TcfTrkIdEvent {
+public:
+ enum Reason { BreakPoint, ModuleLoad, Crash, Other } ;
+
+ explicit TcfTrkRunControlContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc = 0);
+ virtual QString toString() const;
+
+ quint64 pc() const { return m_pc; }
+ QByteArray reasonID() const { return m_reason; }
+ Reason reason() const;
+
+protected:
+ explicit TcfTrkRunControlContextSuspendedEvent(Type t,
+ const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc = 0);
+ void format(QTextStream &str) const;
+
+private:
+ const quint64 m_pc;
+ const QByteArray m_reason;
+};
+
+// RunControlContextSuspended due to module load
+class SYMBIANUTILS_EXPORT TcfTrkRunControlModuleLoadContextSuspendedEvent : public TcfTrkRunControlContextSuspendedEvent {
+public:
+ explicit TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc,
+ const ModuleLoadEventInfo &mi);
+
+ virtual QString toString() const;
+ const ModuleLoadEventInfo &info() const { return m_mi; }
+
+private:
+ const ModuleLoadEventInfo m_mi;
+};
+
+} // namespace tcftrk
+#endif // TRCFTRKMESSAGE_H
diff --git a/src/runonphone/symbianutils/trkdevice.cpp b/src/runonphone/symbianutils/trkdevice.cpp
new file mode 100644
index 000000000..2825b5f9b
--- /dev/null
+++ b/src/runonphone/symbianutils/trkdevice.cpp
@@ -0,0 +1,1184 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "trkdevice.h"
+#include "trkutils.h"
+#include "trkutils_p.h"
+
+#include <QtCore/QString>
+#include <QtCore/QDebug>
+#include <QtCore/QQueue>
+#include <QtCore/QHash>
+#include <QtCore/QMap>
+#include <QtCore/QThread>
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QMetaType>
+
+#ifdef Q_OS_WIN
+# include <windows.h>
+#else
+# include <QtCore/QFile>
+
+# include <stdio.h>
+# include <sys/ioctl.h>
+# include <sys/types.h>
+# include <termios.h>
+# include <errno.h>
+# include <string.h>
+# include <unistd.h>
+/* Required headers for select() according to POSIX.1-2001 */
+# include <sys/select.h>
+/* Required headers for select() according to earlier standards:
+ #include <sys/time.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+*/
+#endif
+
+#ifdef Q_OS_WIN
+
+// Format windows error from GetLastError() value:
+// TODO: Use the one provided by the utils lib.
+QString winErrorMessage(unsigned long error)
+{
+ QString rc = QString::fromLatin1("#%1: ").arg(error);
+ ushort *lpMsgBuf;
+
+ const int len = FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
+ if (len) {
+ rc = QString::fromUtf16(lpMsgBuf, len);
+ LocalFree(lpMsgBuf);
+ } else {
+ rc += QString::fromLatin1("<unknown error>");
+ }
+ return rc;
+}
+
+#endif
+
+enum { verboseTrk = 0 };
+
+static inline QString msgAccessingClosedDevice(const QString &msg)
+{
+ return QString::fromLatin1("Error: Attempt to access device '%1', which is closed.").arg(msg);
+}
+
+namespace trk {
+
+///////////////////////////////////////////////////////////////////////
+//
+// TrkMessage
+//
+///////////////////////////////////////////////////////////////////////
+
+/* A message to be send to TRK, triggering a callback on receipt
+ * of the answer. */
+struct TrkMessage
+{
+ explicit TrkMessage(byte code = 0u, byte token = 0u,
+ TrkCallback callback = TrkCallback());
+
+ byte code;
+ byte token;
+ QByteArray data;
+ QVariant cookie;
+ TrkCallback callback;
+};
+
+TrkMessage::TrkMessage(byte c, byte t, TrkCallback cb) :
+ code(c),
+ token(t),
+ callback(cb)
+{
+}
+
+QDebug operator<<(QDebug d, const TrkMessage &msg)
+{
+ return d << "Message: Code: " << msg.code
+ << " Token: " << msg.token << " " << msg.data.toHex();
+}
+
+} // namespace trk
+
+Q_DECLARE_METATYPE(trk::TrkMessage)
+Q_DECLARE_METATYPE(trk::TrkResult)
+
+namespace trk {
+
+///////////////////////////////////////////////////////////////////////
+//
+// TrkWriteQueue: Mixin class that manages a write queue of Trk messages.
+// pendingMessage()/notifyWriteResult() should be called from a worked/timer
+// that writes the messages. The class does not take precautions for multithreading.
+// A no-op message is simply taken off the queue. The calling class
+// can use the helper invokeNoopMessage() to trigger its callback.
+//
+///////////////////////////////////////////////////////////////////////
+
+class TrkWriteQueue
+{
+ Q_DISABLE_COPY(TrkWriteQueue)
+public:
+ explicit TrkWriteQueue();
+ void clear();
+
+ // Enqueue messages.
+ void queueTrkMessage(byte code, TrkCallback callback,
+ const QByteArray &data, const QVariant &cookie);
+ void queueTrkInitialPing();
+
+ // Call this from the device read notification with the results.
+ void slotHandleResult(const TrkResult &result, QMutex *mutex = 0);
+
+ // pendingMessage() can be called periodically in a timer to retrieve
+ // the pending messages to be sent.
+ enum PendingMessageResult {
+ NoMessage, // No message in queue.
+ PendingMessage, /* There is a queued message. The calling class
+ * can write it out and use notifyWriteResult()
+ * to notify about the result. */
+ NoopMessageDequeued // A no-op message has been dequeued. see invokeNoopMessage().
+ };
+
+ PendingMessageResult pendingMessage(TrkMessage *message);
+ // Notify the queue about the success of the write operation
+ // after taking the pendingMessage off.
+ enum WriteResult {
+ WriteOk,
+ WriteFailedDiscard, // Discard failed message
+ WriteFailedKeep, // Keep failed message
+ };
+ void notifyWriteResult(WriteResult ok);
+
+ // Helper function that invokes the callback of a no-op message
+ static void invokeNoopMessage(trk::TrkMessage);
+
+private:
+ typedef QMap<byte, TrkMessage> TokenMessageMap;
+
+ byte nextTrkWriteToken();
+
+ byte m_trkWriteToken;
+ QQueue<TrkMessage> m_trkWriteQueue;
+ TokenMessageMap m_writtenTrkMessages;
+ bool m_trkWriteBusy;
+};
+
+TrkWriteQueue::TrkWriteQueue() :
+ m_trkWriteToken(0),
+ m_trkWriteBusy(false)
+{
+}
+
+void TrkWriteQueue::clear()
+{
+ m_trkWriteToken = 0;
+ m_trkWriteBusy = false;
+ m_trkWriteQueue.clear();
+ const int discarded = m_writtenTrkMessages.size();
+ m_writtenTrkMessages.clear();
+ if (verboseTrk)
+ qDebug() << "TrkWriteQueue::clear: discarded " << discarded;
+}
+
+byte TrkWriteQueue::nextTrkWriteToken()
+{
+ ++m_trkWriteToken;
+ if (m_trkWriteToken == 0)
+ ++m_trkWriteToken;
+ if (verboseTrk)
+ qDebug() << "nextTrkWriteToken:" << m_trkWriteToken;
+ return m_trkWriteToken;
+}
+
+void TrkWriteQueue::queueTrkMessage(byte code, TrkCallback callback,
+ const QByteArray &data, const QVariant &cookie)
+{
+ const byte token = code == TRK_WRITE_QUEUE_NOOP_CODE ?
+ byte(0) : nextTrkWriteToken();
+ TrkMessage msg(code, token, callback);
+ msg.data = data;
+ msg.cookie = cookie;
+ m_trkWriteQueue.append(msg);
+}
+
+TrkWriteQueue::PendingMessageResult TrkWriteQueue::pendingMessage(TrkMessage *message)
+{
+ // Invoked from timer, try to flush out message queue
+ if (m_trkWriteBusy || m_trkWriteQueue.isEmpty())
+ return NoMessage;
+ // Handle the noop message, just invoke CB in slot (ower thread)
+ if (m_trkWriteQueue.front().code == TRK_WRITE_QUEUE_NOOP_CODE) {
+ *message = m_trkWriteQueue.dequeue();
+ return NoopMessageDequeued;
+ }
+ // Insert into map fir answers (as reading threads might get an
+ // answer before notifyWriteResult(true)) is called.
+ *message = m_trkWriteQueue.front();
+ m_writtenTrkMessages.insert(message->token, *message);
+ m_trkWriteBusy = true;
+ return PendingMessage;
+}
+
+void TrkWriteQueue::invokeNoopMessage(trk::TrkMessage noopMessage)
+{
+ TrkResult result;
+ result.code = noopMessage.code;
+ result.token = noopMessage.token;
+ result.data = noopMessage.data;
+ result.cookie = noopMessage.cookie;
+ noopMessage.callback(result);
+}
+
+void TrkWriteQueue::notifyWriteResult(WriteResult wr)
+{
+ // On success, dequeue message and await result
+ const byte token = m_trkWriteQueue.front().token;
+ switch (wr) {
+ case WriteOk:
+ m_trkWriteQueue.dequeue();
+ break;
+ case WriteFailedKeep:
+ case WriteFailedDiscard:
+ m_writtenTrkMessages.remove(token);
+ m_trkWriteBusy = false;
+ if (wr == WriteFailedDiscard)
+ m_trkWriteQueue.dequeue();
+ break;
+ }
+}
+
+void TrkWriteQueue::slotHandleResult(const TrkResult &result, QMutex *mutex)
+{
+ // Find which request the message belongs to and invoke callback
+ // if ACK or on NAK if desired.
+ if (mutex)
+ mutex->lock();
+ m_trkWriteBusy = false;
+ const TokenMessageMap::iterator it = m_writtenTrkMessages.find(result.token);
+ if (it == m_writtenTrkMessages.end()) {
+ if (mutex)
+ mutex->unlock();
+ return;
+ }
+ TrkCallback callback = it.value().callback;
+ const QVariant cookie = it.value().cookie;
+ m_writtenTrkMessages.erase(it);
+ if (mutex)
+ mutex->unlock();
+ // Invoke callback
+ if (callback) {
+ TrkResult result1 = result;
+ result1.cookie = cookie;
+ callback(result1);
+ }
+}
+
+void TrkWriteQueue::queueTrkInitialPing()
+{
+ // Ping, reset sequence count
+ m_trkWriteToken = 0;
+ m_trkWriteQueue.append(TrkMessage(TrkPing, 0));
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// DeviceContext to be shared between threads
+//
+///////////////////////////////////////////////////////////////////////
+
+struct DeviceContext {
+ DeviceContext();
+#ifdef Q_OS_WIN
+ HANDLE device;
+ OVERLAPPED readOverlapped;
+ OVERLAPPED writeOverlapped;
+#else
+ QFile file;
+#endif
+ bool serialFrame;
+ QMutex mutex;
+};
+
+DeviceContext::DeviceContext() :
+#ifdef Q_OS_WIN
+ device(INVALID_HANDLE_VALUE),
+#endif
+ serialFrame(true)
+{
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// TrkWriterThread: A thread operating a TrkWriteQueue.
+// with exception of the handling of the TRK_WRITE_QUEUE_NOOP_CODE
+// synchronization message. The invocation of the callback is then
+// done by the thread owning the TrkWriteQueue, while pendingMessage() is called
+// from another thread. This happens via a Qt::BlockingQueuedConnection.
+
+///////////////////////////////////////////////////////////////////////
+
+class WriterThread : public QThread
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(WriterThread)
+public:
+ explicit WriterThread(const QSharedPointer<DeviceContext> &context);
+
+ // Enqueue messages.
+ void queueTrkMessage(byte code, TrkCallback callback,
+ const QByteArray &data, const QVariant &cookie);
+ void queueTrkInitialPing();
+
+ void clearWriteQueue();
+
+ // Call this from the device read notification with the results.
+ void slotHandleResult(const TrkResult &result);
+
+ virtual void run();
+
+signals:
+ void error(const QString &);
+ void internalNoopMessageDequeued(const trk::TrkMessage&);
+
+public slots:
+ bool trkWriteRawMessage(const TrkMessage &msg);
+ void terminate();
+ void tryWrite();
+
+private slots:
+ void invokeNoopMessage(const trk::TrkMessage &);
+
+private:
+ bool write(const QByteArray &data, QString *errorMessage);
+ inline int writePendingMessage();
+
+ const QSharedPointer<DeviceContext> m_context;
+ QMutex m_dataMutex;
+ QMutex m_waitMutex;
+ QWaitCondition m_waitCondition;
+ TrkWriteQueue m_queue;
+ bool m_terminate;
+};
+
+WriterThread::WriterThread(const QSharedPointer<DeviceContext> &context) :
+ m_context(context),
+ m_terminate(false)
+{
+ static const int trkMessageMetaId = qRegisterMetaType<trk::TrkMessage>();
+ Q_UNUSED(trkMessageMetaId)
+ connect(this, SIGNAL(internalNoopMessageDequeued(trk::TrkMessage)),
+ this, SLOT(invokeNoopMessage(trk::TrkMessage)), Qt::BlockingQueuedConnection);
+}
+
+void WriterThread::run()
+{
+ while (writePendingMessage() == 0) ;
+}
+
+int WriterThread::writePendingMessage()
+{
+ enum { MaxAttempts = 100, RetryIntervalMS = 200 };
+
+ // Wait. Use a timeout in case something is already queued before we
+ // start up or some weird hanging exit condition
+ m_waitMutex.lock();
+ m_waitCondition.wait(&m_waitMutex, 100);
+ m_waitMutex.unlock();
+ if (m_terminate)
+ return 1;
+
+ // Send off message
+ m_dataMutex.lock();
+ TrkMessage message;
+ const TrkWriteQueue::PendingMessageResult pr = m_queue.pendingMessage(&message);
+ m_dataMutex.unlock();
+
+ switch (pr) {
+ case TrkWriteQueue::NoMessage:
+ break;
+ case TrkWriteQueue::PendingMessage: {
+ //qDebug() << "Write pending message " << message;
+ // Untested: try to re-send a few times
+ bool success = false;
+ for (int r = 0; !success && (r < MaxAttempts); r++) {
+ success = trkWriteRawMessage(message);
+ if (!success) {
+ emit error(QString::fromLatin1("Write failure, attempt %1 of %2.").arg(r).arg(int(MaxAttempts)));
+ if (m_terminate)
+ return 1;
+ QThread::msleep(RetryIntervalMS);
+ }
+ }
+ // Notify queue. If still failed, give up.
+ m_dataMutex.lock();
+ m_queue.notifyWriteResult(success ? TrkWriteQueue::WriteOk : TrkWriteQueue::WriteFailedDiscard);
+ m_dataMutex.unlock();
+ }
+ break;
+ case TrkWriteQueue::NoopMessageDequeued:
+ // Sync with thread that owns us via a blocking signal
+ if (verboseTrk)
+ qDebug() << "Noop message dequeued" << message;
+ emit internalNoopMessageDequeued(message);
+ break;
+ } // switch
+ return 0;
+}
+
+void WriterThread::invokeNoopMessage(const trk::TrkMessage &msg)
+{
+ TrkWriteQueue::invokeNoopMessage(msg);
+}
+
+void WriterThread::terminate()
+{
+ m_terminate = true;
+ m_waitCondition.wakeAll();
+ wait();
+ m_terminate = false;
+ m_queue.clear();
+}
+
+#ifdef Q_OS_WIN
+
+static inline QString msgTerminated(int size)
+{
+ return QString::fromLatin1("Terminated with %1 bytes pending.").arg(size);
+}
+
+// Interruptible synchronous write function.
+static inline bool overlappedSyncWrite(HANDLE file,
+ const bool &terminateFlag,
+ const char *data,
+ DWORD size, DWORD *charsWritten,
+ OVERLAPPED *overlapped,
+ QString *errorMessage)
+{
+ if (WriteFile(file, data, size, charsWritten, overlapped))
+ return true;
+ const DWORD writeError = GetLastError();
+ if (writeError != ERROR_IO_PENDING) {
+ *errorMessage = QString::fromLatin1("WriteFile failed: %1").arg(winErrorMessage(writeError));
+ return false;
+ }
+ // Wait for written or thread terminated
+ const DWORD timeoutMS = 200;
+ const unsigned maxAttempts = 20;
+ DWORD wr = WaitForSingleObject(overlapped->hEvent, timeoutMS);
+ for (unsigned n = 0; wr == WAIT_TIMEOUT && n < maxAttempts && !terminateFlag;
+ wr = WaitForSingleObject(overlapped->hEvent, timeoutMS), n++);
+ if (terminateFlag) {
+ *errorMessage = msgTerminated(size);
+ return false;
+ }
+ switch (wr) {
+ case WAIT_OBJECT_0:
+ break;
+ case WAIT_TIMEOUT:
+ *errorMessage = QString::fromLatin1("Write timed out.");
+ return false;
+ default:
+ *errorMessage = QString::fromLatin1("Error while waiting for WriteFile results: %1").arg(winErrorMessage(GetLastError()));
+ return false;
+ }
+ if (!GetOverlappedResult(file, overlapped, charsWritten, TRUE)) {
+ *errorMessage = QString::fromLatin1("Error writing %1 bytes: %2").arg(size).arg(winErrorMessage(GetLastError()));
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool WriterThread::write(const QByteArray &data, QString *errorMessage)
+{
+ if (verboseTrk)
+ qDebug() << "Write raw data: " << stringFromArray(data).toLatin1();
+ QMutexLocker locker(&m_context->mutex);
+#ifdef Q_OS_WIN
+ DWORD charsWritten;
+ if (!overlappedSyncWrite(m_context->device, m_terminate, data.data(), data.size(), &charsWritten, &m_context->writeOverlapped, errorMessage)) {
+ return false;
+ }
+ FlushFileBuffers(m_context->device);
+ return true;
+#else
+ if (m_context->file.write(data) == -1 || !m_context->file.flush()) {
+ *errorMessage = QString::fromLatin1("Cannot write: %1").arg(m_context->file.errorString());
+ return false;
+ }
+ return true;
+#endif
+}
+
+bool WriterThread::trkWriteRawMessage(const TrkMessage &msg)
+{
+ const QByteArray ba = frameMessage(msg.code, msg.token, msg.data, m_context->serialFrame);
+ QString errorMessage;
+ const bool rc = write(ba, &errorMessage);
+ if (!rc) {
+ qWarning("%s\n", qPrintable(errorMessage));
+ emit error(errorMessage);
+ }
+ return rc;
+}
+
+void WriterThread::tryWrite()
+{
+ m_waitCondition.wakeAll();
+}
+
+void WriterThread::queueTrkMessage(byte code, TrkCallback callback,
+ const QByteArray &data, const QVariant &cookie)
+{
+ m_dataMutex.lock();
+ m_queue.queueTrkMessage(code, callback, data, cookie);
+ m_dataMutex.unlock();
+ tryWrite();
+}
+
+void WriterThread::clearWriteQueue()
+{
+ m_dataMutex.lock();
+ m_queue.clear();
+ m_dataMutex.unlock();
+}
+
+void WriterThread::queueTrkInitialPing()
+{
+ m_dataMutex.lock();
+ m_queue.queueTrkInitialPing();
+ m_dataMutex.unlock();
+ tryWrite();
+}
+
+// Call this from the device read notification with the results.
+void WriterThread::slotHandleResult(const TrkResult &result)
+{
+ m_queue.slotHandleResult(result, &m_dataMutex);
+ tryWrite(); // Have messages been enqueued in-between?
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// ReaderThreadBase: Base class for a thread that reads data from
+// the device, decodes the messages and emit signals for the messages.
+// A Qt::BlockingQueuedConnection should be used for the message signal
+// to ensure messages are processed in the correct sequence.
+//
+///////////////////////////////////////////////////////////////////////
+
+class ReaderThreadBase : public QThread
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ReaderThreadBase)
+public:
+
+ int bytesPending() const { return m_trkReadBuffer.size(); }
+
+signals:
+ void messageReceived(const trk::TrkResult &result, const QByteArray &rawData);
+
+protected:
+ explicit ReaderThreadBase(const QSharedPointer<DeviceContext> &context);
+ void processData(const QByteArray &a);
+ void processData(char c);
+
+ const QSharedPointer<DeviceContext> m_context;
+
+private:
+ void readMessages();
+
+ QByteArray m_trkReadBuffer;
+ bool linkEstablishmentMode;
+};
+
+ReaderThreadBase::ReaderThreadBase(const QSharedPointer<DeviceContext> &context) :
+ m_context(context), linkEstablishmentMode(true)
+{
+ static const int trkResultMetaId = qRegisterMetaType<trk::TrkResult>();
+ Q_UNUSED(trkResultMetaId)
+}
+
+void ReaderThreadBase::processData(const QByteArray &a)
+{
+ m_trkReadBuffer += a;
+ readMessages();
+}
+
+void ReaderThreadBase::processData(char c)
+{
+ m_trkReadBuffer += c;
+ if (m_trkReadBuffer.size() > 1)
+ readMessages();
+}
+
+void ReaderThreadBase::readMessages()
+{
+ TrkResult r;
+ QByteArray rawData;
+ while (extractResult(&m_trkReadBuffer, m_context->serialFrame, &r, linkEstablishmentMode, &rawData)) {
+ emit messageReceived(r, rawData);
+ }
+}
+
+#ifdef Q_OS_WIN
+///////////////////////////////////////////////////////////////////////
+//
+// WinReaderThread: A thread reading from the device using Windows API.
+// Waits on an overlapped I/O handle and an event that tells the thread to
+// terminate.
+//
+///////////////////////////////////////////////////////////////////////
+
+class WinReaderThread : public ReaderThreadBase
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(WinReaderThread)
+public:
+ explicit WinReaderThread(const QSharedPointer<DeviceContext> &context);
+ ~WinReaderThread();
+
+ virtual void run();
+
+signals:
+ void error(const QString &);
+
+public slots:
+ void terminate();
+
+private:
+ enum Handles { FileHandle, TerminateEventHandle, HandleCount };
+
+ inline int tryRead();
+
+ HANDLE m_handles[HandleCount];
+};
+
+WinReaderThread::WinReaderThread(const QSharedPointer<DeviceContext> &context) :
+ ReaderThreadBase(context)
+{
+ m_handles[FileHandle] = NULL;
+ m_handles[TerminateEventHandle] = CreateEvent(NULL, FALSE, FALSE, NULL);
+}
+
+WinReaderThread::~WinReaderThread()
+{
+ CloseHandle(m_handles[TerminateEventHandle]);
+}
+
+// Return 0 to continue or error code
+int WinReaderThread::tryRead()
+{
+ enum { BufSize = 1024 };
+ char buffer[BufSize];
+ // Check if there are already bytes waiting. If not, wait for first byte
+ COMSTAT comStat;
+ if (!ClearCommError(m_context->device, NULL, &comStat)){
+ emit error(QString::fromLatin1("ClearCommError failed: %1").arg(winErrorMessage(GetLastError())));
+ return -7;
+ }
+ const DWORD bytesToRead = qMax(DWORD(1), qMin(comStat.cbInQue, DWORD(BufSize)));
+ // Trigger read
+ DWORD bytesRead = 0;
+ if (ReadFile(m_context->device, &buffer, bytesToRead, &bytesRead, &m_context->readOverlapped)) {
+ if (bytesRead == 1) {
+ processData(buffer[0]);
+ } else {
+ processData(QByteArray(buffer, bytesRead));
+ }
+ return 0;
+ }
+ const DWORD readError = GetLastError();
+ if (readError != ERROR_IO_PENDING) {
+ emit error(QString::fromLatin1("Read error: %1").arg(winErrorMessage(readError)));
+ return -1;
+ }
+ // Wait for either termination or data
+ const DWORD wr = WaitForMultipleObjects(HandleCount, m_handles, false, INFINITE);
+ if (wr == WAIT_FAILED) {
+ emit error(QString::fromLatin1("Wait failed: %1").arg(winErrorMessage(GetLastError())));
+ return -2;
+ }
+ if (wr - WAIT_OBJECT_0 == TerminateEventHandle) {
+ return 1; // Terminate
+ }
+ // Check data
+ if (!GetOverlappedResult(m_context->device, &m_context->readOverlapped, &bytesRead, true)) {
+ emit error(QString::fromLatin1("GetOverlappedResult failed: %1").arg(winErrorMessage(GetLastError())));
+ return -3;
+ }
+ if (bytesRead == 1) {
+ processData(buffer[0]);
+ } else {
+ processData(QByteArray(buffer, bytesRead));
+ }
+ return 0;
+}
+
+void WinReaderThread::run()
+{
+ m_handles[FileHandle] = m_context->readOverlapped.hEvent;
+ while ( tryRead() == 0) ;
+}
+
+void WinReaderThread::terminate()
+{
+ SetEvent(m_handles[TerminateEventHandle]);
+ wait();
+}
+
+typedef WinReaderThread ReaderThread;
+
+#else
+
+///////////////////////////////////////////////////////////////////////
+//
+// UnixReaderThread: A thread reading from the device.
+// Uses select() to wait and a special ioctl() to find out the number
+// of bytes queued. For clean termination, the self-pipe trick is used.
+// The class maintains a pipe, on whose read end the select waits besides
+// the device file handle. To terminate, a byte is written to the pipe.
+//
+///////////////////////////////////////////////////////////////////////
+
+static inline QString msgUnixCallFailedErrno(const char *func, int errorNumber)
+{
+ return QString::fromLatin1("Call to %1() failed: %2").arg(QLatin1String(func), QString::fromLocal8Bit(strerror(errorNumber)));
+}
+
+class UnixReaderThread : public ReaderThreadBase {
+ Q_OBJECT
+ Q_DISABLE_COPY(UnixReaderThread)
+public:
+ explicit UnixReaderThread(const QSharedPointer<DeviceContext> &context);
+ ~UnixReaderThread();
+
+ virtual void run();
+
+signals:
+ void error(const QString &);
+
+public slots:
+ void terminate();
+
+private:
+ inline int tryRead();
+
+ int m_terminatePipeFileDescriptors[2];
+};
+
+UnixReaderThread::UnixReaderThread(const QSharedPointer<DeviceContext> &context) :
+ ReaderThreadBase(context)
+{
+ m_terminatePipeFileDescriptors[0] = m_terminatePipeFileDescriptors[1] = -1;
+ // Set up pipes for termination. Should not fail
+ if (pipe(m_terminatePipeFileDescriptors) < 0)
+ qWarning("%s\n", qPrintable(msgUnixCallFailedErrno("pipe", errno)));
+}
+
+UnixReaderThread::~UnixReaderThread()
+{
+ close(m_terminatePipeFileDescriptors[0]);
+ close(m_terminatePipeFileDescriptors[1]);
+}
+
+int UnixReaderThread::tryRead()
+{
+ fd_set readSet, tempReadSet, tempExceptionSet;
+ struct timeval timeOut;
+ const int fileDescriptor = m_context->file.handle();
+ FD_ZERO(&readSet);
+ FD_SET(fileDescriptor, &readSet);
+ FD_SET(m_terminatePipeFileDescriptors[0], &readSet);
+ const int maxFileDescriptor = qMax(m_terminatePipeFileDescriptors[0], fileDescriptor);
+ int result = 0;
+ do {
+ memcpy(&tempReadSet, &readSet, sizeof(fd_set));
+ memcpy(&tempExceptionSet, &readSet, sizeof(fd_set));
+ timeOut.tv_sec = 1;
+ timeOut.tv_usec = 0;
+ result = select(maxFileDescriptor + 1, &tempReadSet, NULL, &tempExceptionSet, &timeOut);
+ } while ( result < 0 && errno == EINTR );
+ // Timeout?
+ if (result == 0)
+ return 0;
+ // Something wrong?
+ if (result < 0) {
+ emit error(msgUnixCallFailedErrno("select", errno));
+ return -1;
+ }
+ // Did the exception set trigger on the device?
+ if (FD_ISSET(fileDescriptor,&tempExceptionSet)) {
+ emit error(QLatin1String("An Exception occurred on the device."));
+ return -2;
+ }
+ // Check termination pipe.
+ if (FD_ISSET(m_terminatePipeFileDescriptors[0], &tempReadSet)
+ || FD_ISSET(m_terminatePipeFileDescriptors[0], &tempExceptionSet))
+ return 1;
+
+ // determine number of pending bytes and read
+ int numBytes;
+ if (ioctl(fileDescriptor, FIONREAD, &numBytes) < 0) {
+ emit error(msgUnixCallFailedErrno("ioctl", errno));
+ return -1;
+ }
+ m_context->mutex.lock();
+ const QByteArray data = m_context->file.read(numBytes);
+ m_context->mutex.unlock();
+ processData(data);
+ return 0;
+}
+
+void UnixReaderThread::run()
+{
+ // Read loop
+ while (tryRead() == 0)
+ ;
+}
+
+void UnixReaderThread::terminate()
+{
+ // Trigger select() by writing to the pipe
+ char c = 0;
+ const int written = write(m_terminatePipeFileDescriptors[1], &c, 1);
+ Q_UNUSED(written)
+ wait();
+}
+
+typedef UnixReaderThread ReaderThread;
+
+#endif
+
+///////////////////////////////////////////////////////////////////////
+//
+// TrkDevicePrivate
+//
+///////////////////////////////////////////////////////////////////////
+
+struct TrkDevicePrivate
+{
+ TrkDevicePrivate();
+
+ QSharedPointer<DeviceContext> deviceContext;
+ QScopedPointer<WriterThread> writerThread;
+ QScopedPointer<ReaderThread> readerThread;
+
+ QByteArray trkReadBuffer;
+ int verbose;
+ QString errorString;
+ QString port;
+};
+
+///////////////////////////////////////////////////////////////////////
+//
+// TrkDevice
+//
+///////////////////////////////////////////////////////////////////////
+
+TrkDevicePrivate::TrkDevicePrivate() :
+ deviceContext(new DeviceContext),
+ verbose(0)
+{
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// TrkDevice
+//
+///////////////////////////////////////////////////////////////////////
+
+TrkDevice::TrkDevice(QObject *parent) :
+ QObject(parent),
+ d(new TrkDevicePrivate)
+{}
+
+TrkDevice::~TrkDevice()
+{
+ close();
+ delete d;
+}
+
+bool TrkDevice::open(QString *errorMessage)
+{
+ if (d->verbose || verboseTrk)
+ qDebug() << "Opening" << port() << "is open: " << isOpen() << " serialFrame=" << serialFrame();
+ if (isOpen())
+ return true;
+ if (d->port.isEmpty()) {
+ *errorMessage = QLatin1String("Internal error: No port set on TrkDevice");
+ return false;
+ }
+#ifdef Q_OS_WIN
+ const QString fullPort = QLatin1String("\\\\.\\") + d->port;
+ d->deviceContext->device = CreateFile(reinterpret_cast<const WCHAR*>(fullPort.utf16()),
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING|FILE_FLAG_OVERLAPPED,
+ NULL);
+
+ if (INVALID_HANDLE_VALUE == d->deviceContext->device) {
+ *errorMessage = QString::fromLatin1("Could not open device '%1': %2").arg(port(), winErrorMessage(GetLastError()));
+ return false;
+ }
+ memset(&d->deviceContext->readOverlapped, 0, sizeof(OVERLAPPED));
+ d->deviceContext->readOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ memset(&d->deviceContext->writeOverlapped, 0, sizeof(OVERLAPPED));
+ d->deviceContext->writeOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (d->deviceContext->readOverlapped.hEvent == NULL || d->deviceContext->writeOverlapped.hEvent == NULL) {
+ *errorMessage = QString::fromLatin1("Failed to create events: %1").arg(winErrorMessage(GetLastError()));
+ return false;
+ }
+#else
+ d->deviceContext->file.setFileName(d->port);
+ if (!d->deviceContext->file.open(QIODevice::ReadWrite|QIODevice::Unbuffered)) {
+ *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(d->port, d->deviceContext->file.errorString());
+ return false;
+ }
+
+ struct termios termInfo;
+ if (tcgetattr(d->deviceContext->file.handle(), &termInfo) < 0) {
+ *errorMessage = QString::fromLatin1("Unable to retrieve terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno)));
+ return false;
+ }
+ // Turn off terminal echo as not get messages back, among other things
+ termInfo.c_cflag |= CREAD|CLOCAL;
+ termInfo.c_lflag &= (~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|ISIG));
+ termInfo.c_iflag &= (~(INPCK|IGNPAR|PARMRK|ISTRIP|ICRNL|IXANY));
+ termInfo.c_oflag &= (~OPOST);
+ termInfo.c_cc[VMIN] = 0;
+ termInfo.c_cc[VINTR] = _POSIX_VDISABLE;
+ termInfo.c_cc[VQUIT] = _POSIX_VDISABLE;
+ termInfo.c_cc[VSTART] = _POSIX_VDISABLE;
+ termInfo.c_cc[VSTOP] = _POSIX_VDISABLE;
+ termInfo.c_cc[VSUSP] = _POSIX_VDISABLE;
+ if (tcsetattr(d->deviceContext->file.handle(), TCSAFLUSH, &termInfo) < 0) {
+ *errorMessage = QString::fromLatin1("Unable to apply terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno)));
+ return false;
+ }
+#endif
+ d->readerThread.reset(new ReaderThread(d->deviceContext));
+ connect(d->readerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)),
+ Qt::QueuedConnection);
+ connect(d->readerThread.data(), SIGNAL(messageReceived(trk::TrkResult,QByteArray)),
+ this, SLOT(slotMessageReceived(trk::TrkResult,QByteArray)),
+ Qt::QueuedConnection);
+ d->readerThread->start();
+
+ d->writerThread.reset(new WriterThread(d->deviceContext));
+ connect(d->writerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)),
+ Qt::QueuedConnection);
+ d->writerThread->start();
+
+ if (d->verbose || verboseTrk)
+ qDebug() << "Opened" << d->port << d->readerThread.data() << d->writerThread.data();
+ return true;
+}
+
+void TrkDevice::close()
+{
+ if (verboseTrk)
+ qDebug() << "close" << d->port << " is open: " << isOpen()
+ << " read pending " << (d->readerThread.isNull() ? 0 : d->readerThread->bytesPending())
+ << sender();
+ if (!isOpen())
+ return;
+ if (d->readerThread)
+ d->readerThread->terminate();
+ if (d->writerThread)
+ d->writerThread->terminate();
+#ifdef Q_OS_WIN
+ CloseHandle(d->deviceContext->device);
+ d->deviceContext->device = INVALID_HANDLE_VALUE;
+ CloseHandle(d->deviceContext->readOverlapped.hEvent);
+ CloseHandle(d->deviceContext->writeOverlapped.hEvent);
+ d->deviceContext->readOverlapped.hEvent = d->deviceContext->writeOverlapped.hEvent = NULL;
+#else
+ d->deviceContext->file.close();
+#endif
+
+ if (d->verbose)
+ emitLogMessage("Close");
+}
+
+bool TrkDevice::isOpen() const
+{
+#ifdef Q_OS_WIN
+ return d->deviceContext->device != INVALID_HANDLE_VALUE;
+#else
+ return d->deviceContext->file.isOpen();
+#endif
+}
+
+QString TrkDevice::port() const
+{
+ return d->port;
+}
+
+void TrkDevice::setPort(const QString &p)
+{
+ if (verboseTrk)
+ qDebug() << "setPort" << p;
+ d->port = p;
+}
+
+QString TrkDevice::errorString() const
+{
+ return d->errorString;
+}
+
+bool TrkDevice::serialFrame() const
+{
+ return d->deviceContext->serialFrame;
+}
+
+void TrkDevice::setSerialFrame(bool f)
+{
+ if (verboseTrk)
+ qDebug() << "setSerialFrame" << f;
+ d->deviceContext->serialFrame = f;
+}
+
+int TrkDevice::verbose() const
+{
+ return d->verbose;
+}
+
+void TrkDevice::setVerbose(int b)
+{
+ d->verbose = b;
+}
+
+void TrkDevice::slotMessageReceived(const trk::TrkResult &result, const QByteArray &rawData)
+{
+ if (isOpen()) { // Might receive bytes after closing due to queued connections.
+ d->writerThread->slotHandleResult(result);
+ if (d->verbose > 1)
+ qDebug() << "Received: " << result.toString();
+ emit messageReceived(result);
+ if (!rawData.isEmpty())
+ emit rawDataReceived(rawData);
+ }
+}
+
+void TrkDevice::emitError(const QString &s)
+{
+ d->errorString = s;
+ qWarning("%s\n", qPrintable(s));
+ emit error(s);
+}
+
+void TrkDevice::clearWriteQueue()
+{
+ if (isOpen())
+ d->writerThread->clearWriteQueue();
+}
+
+void TrkDevice::sendTrkMessage(byte code, TrkCallback callback,
+ const QByteArray &data, const QVariant &cookie)
+{
+ if (!isOpen()) {
+ emitError(msgAccessingClosedDevice(d->port));
+ return;
+ }
+ if (!d->writerThread.isNull()) {
+ if (d->verbose > 1) {
+ QByteArray msg = "Sending: 0x";
+ msg += QByteArray::number(code, 16);
+ msg += ": ";
+ msg += stringFromArray(data).toLatin1();
+ if (cookie.isValid())
+ msg += " Cookie: " + cookie.toString().toLatin1();
+ qDebug("%s", msg.data());
+ }
+ d->writerThread->queueTrkMessage(code, callback, data, cookie);
+ }
+}
+
+void TrkDevice::sendTrkInitialPing()
+{
+ if (!isOpen()) {
+ emitError(msgAccessingClosedDevice(d->port));
+ return;
+ }
+ if (!d->writerThread.isNull())
+ d->writerThread->queueTrkInitialPing();
+}
+
+bool TrkDevice::sendTrkAck(byte token)
+{
+ if (!isOpen()) {
+ emitError(msgAccessingClosedDevice(d->port));
+ return false;
+ }
+ if (d->writerThread.isNull())
+ return false;
+ // The acknowledgement must not be queued!
+ TrkMessage msg(0x80, token);
+ msg.token = token;
+ msg.data.append('\0');
+ if (verboseTrk)
+ qDebug() << "Write synchroneous message: " << msg;
+ return d->writerThread->trkWriteRawMessage(msg);
+ // 01 90 00 07 7e 80 01 00 7d 5e 7e
+}
+
+void TrkDevice::emitLogMessage(const QString &msg)
+{
+ if (d->verbose)
+ qDebug("%s\n", qPrintable(msg));
+ emit logMessage(msg);
+}
+
+} // namespace trk
+
+#include "trkdevice.moc"
diff --git a/src/runonphone/symbianutils/trkdevice.h b/src/runonphone/symbianutils/trkdevice.h
new file mode 100644
index 000000000..bb54be520
--- /dev/null
+++ b/src/runonphone/symbianutils/trkdevice.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TRKDEVICE_H
+#define TRKDEVICE_H
+
+#include "symbianutils_global.h"
+#include "callback.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QVariant>
+#include <QtCore/QByteArray>
+#include <QtCore/QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+QT_END_NAMESPACE
+
+namespace trk {
+
+struct TrkResult;
+struct TrkMessage;
+struct TrkDevicePrivate;
+
+/* TrkDevice: Implements a Windows COM or Linux device for
+ * Trk communications. Provides synchronous write and asynchronous
+ * read operation.
+ * The serialFrames property specifies whether packets are encapsulated in
+ * "0x90 <length>" frames, which is currently the case for serial ports.
+ * Contains a write message queue allowing
+ * for queueing messages with a notification callback. If the message receives
+ * an ACK, the callback is invoked.
+ * The special message TRK_WRITE_QUEUE_NOOP_CODE code can be used for synchronization.
+ * The respective message will not be sent, the callback is just invoked.
+ * Note that calling open/close in quick succession can cause crashes
+ * due to the use of queused signals. */
+
+enum { TRK_WRITE_QUEUE_NOOP_CODE = 0x7f };
+
+typedef trk::Callback<const TrkResult &> TrkCallback;
+
+class SYMBIANUTILS_EXPORT TrkDevice : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool serialFrame READ serialFrame WRITE setSerialFrame)
+ Q_PROPERTY(bool verbose READ verbose WRITE setVerbose)
+ Q_PROPERTY(QString port READ port WRITE setPort)
+public:
+ explicit TrkDevice(QObject *parent = 0);
+ virtual ~TrkDevice();
+
+ bool open(QString *errorMessage);
+ bool isOpen() const;
+
+ QString port() const;
+ void setPort(const QString &p);
+
+ QString errorString() const;
+
+ bool serialFrame() const;
+ void setSerialFrame(bool f);
+
+ int verbose() const;
+ void setVerbose(int b);
+
+ // Enqueue a message with a notification callback.
+ void sendTrkMessage(unsigned char code,
+ TrkCallback callBack = TrkCallback(),
+ const QByteArray &data = QByteArray(),
+ const QVariant &cookie = QVariant());
+
+ // Enqeue an initial ping
+ void sendTrkInitialPing();
+
+ // Send an Ack synchronously, bypassing the queue
+ bool sendTrkAck(unsigned char token);
+
+public slots:
+ void clearWriteQueue();
+
+signals:
+ void messageReceived(const trk::TrkResult &result);
+ // Emitted with the contents of messages enclosed in 07e, not for log output
+ void rawDataReceived(const QByteArray &data);
+ void error(const QString &msg);
+ void logMessage(const QString &msg);
+
+private slots:
+ void slotMessageReceived(const trk::TrkResult &result, const QByteArray &a);
+
+protected slots:
+ void emitError(const QString &msg);
+ void emitLogMessage(const QString &msg);
+
+public slots:
+ void close();
+
+private:
+ void readMessages();
+ TrkDevicePrivate *d;
+};
+
+} // namespace trk
+
+#endif // TRKDEVICE_H
diff --git a/src/runonphone/symbianutils/trkutils.cpp b/src/runonphone/symbianutils/trkutils.cpp
new file mode 100644
index 000000000..168f53960
--- /dev/null
+++ b/src/runonphone/symbianutils/trkutils.cpp
@@ -0,0 +1,603 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "trkutils.h"
+#include <ctype.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QDate>
+#include <QtCore/QDateTime>
+#include <QtCore/QTime>
+
+#define logMessage(s) do { qDebug() << "TRKCLIENT: " << s; } while (0)
+
+namespace trk {
+
+Library::Library() : codeseg(0), dataseg(0), pid(0)
+{
+}
+
+Library::Library(const TrkResult &result) : codeseg(0), dataseg(0), pid(0)
+{
+ if (result.data.size() < 20) {
+ qWarning("Invalid trk creation notification received.");
+ return;
+ }
+
+ const char *data = result.data.constData();
+ pid = extractInt(data + 2);
+ codeseg = extractInt(data + 10);
+ dataseg = extractInt(data + 14);
+ const uint len = extractShort(data + 18);
+ name = result.data.mid(20, len);
+}
+
+TrkAppVersion::TrkAppVersion()
+{
+ reset();
+}
+
+void TrkAppVersion::reset()
+{
+ trkMajor = trkMinor= protocolMajor = protocolMinor = 0;
+}
+
+Session::Session()
+{
+ reset();
+}
+
+void Session::reset()
+{
+ cpuMajor = 0;
+ cpuMinor = 0;
+ bigEndian = 0;
+ defaultTypeSize = 0;
+ fpTypeSize = 0;
+ extended1TypeSize = 0;
+ extended2TypeSize = 0;
+ pid = 0;
+ mainTid = 0;
+ tid = 0;
+ codeseg = 0;
+ dataseg = 0;
+
+ libraries.clear();
+ trkAppVersion.reset();
+}
+
+static QString formatCpu(int major, int minor)
+{
+ //: CPU description of an S60 device
+ //: %1 major verison, %2 minor version
+ //: %3 real name of major verison, %4 real name of minor version
+ const QString str = QCoreApplication::translate("trk::Session", "CPU: v%1.%2%3%4");
+ QString majorStr;
+ QString minorStr;
+ switch (major) {
+ case 0x04:
+ majorStr = " ARM";
+ break;
+ }
+ switch (minor) {
+ case 0x00:
+ minorStr = " 920T";
+ break;
+ }
+ return str.arg(major).arg(minor).arg(majorStr).arg(minorStr);
+ }
+
+QString formatTrkVersion(const TrkAppVersion &version)
+{
+ QString str = QCoreApplication::translate("trk::Session",
+ "App TRK: v%1.%2 TRK protocol: v%3.%4");
+ str = str.arg(version.trkMajor).arg(version.trkMinor);
+ return str.arg(version.protocolMajor).arg(version.protocolMinor);
+}
+
+QString Session::deviceDescription(unsigned verbose) const
+{
+ if (!cpuMajor)
+ return QString();
+
+ //: s60description
+ //: description of an S60 device
+ //: %1 CPU description, %2 endianness
+ //: %3 default type size (if any), %4 float size (if any)
+ //: %5 TRK version
+ QString msg = QCoreApplication::translate("trk::Session", "%1, %2%3%4, %5");
+ QString endianness = bigEndian
+ ? QCoreApplication::translate("trk::Session", "big endian")
+ : QCoreApplication::translate("trk::Session", "little endian");
+ msg = msg.arg(formatCpu(cpuMajor, cpuMinor)).arg(endianness);
+ //: The separator in a list of strings
+ QString defaultTypeSizeStr;
+ QString fpTypeSizeStr;
+ if (verbose && defaultTypeSize)
+ //: will be inserted into s60description
+ defaultTypeSizeStr = QCoreApplication::translate("trk::Session", ", type size: %1").arg(defaultTypeSize);
+ if (verbose && fpTypeSize)
+ //: will be inserted into s60description
+ fpTypeSizeStr = QCoreApplication::translate("trk::Session", ", float size: %1").arg(fpTypeSize);
+ msg = msg.arg(defaultTypeSizeStr).arg(fpTypeSizeStr);
+ return msg.arg(formatTrkVersion(trkAppVersion));
+}
+
+QByteArray Session::gdbLibraryList() const
+{
+ const int count = libraries.size();
+ QByteArray response = "l<library-list>";
+ for (int i = 0; i != count; ++i) {
+ const trk::Library &lib = libraries.at(i);
+ response += "<library name=\"";
+ response += lib.name;
+ response += "\">";
+ response += "<section address=\"0x";
+ response += trk::hexNumber(lib.codeseg);
+ response += "\"/>";
+ response += "<section address=\"0x";
+ response += trk::hexNumber(lib.dataseg);
+ response += "\"/>";
+ response += "<section address=\"0x";
+ response += trk::hexNumber(lib.dataseg);
+ response += "\"/>";
+ response += "</library>";
+ }
+ response += "</library-list>";
+ return response;
+}
+
+QByteArray Session::gdbQsDllInfo(int start, int count) const
+{
+ // Happens with gdb 6.4.50.20060226-cvs / CodeSourcery.
+ // Never made it into FSF gdb that got qXfer:libraries:read instead.
+ // http://sourceware.org/ml/gdb/2007-05/msg00038.html
+ // Name=hexname,TextSeg=textaddr[,DataSeg=dataaddr]
+ const int libraryCount = libraries.size();
+ const int end = count < 0 ? libraryCount : qMin(libraryCount, start + count);
+ QByteArray response(1, end == libraryCount ? 'l' : 'm');
+ for (int i = start; i < end; ++i) {
+ if (i != start)
+ response += ';';
+ const Library &lib = libraries.at(i);
+ response += "Name=";
+ response += lib.name.toHex();
+ response += ",TextSeg=";
+ response += hexNumber(lib.codeseg);
+ response += ",DataSeg=";
+ response += hexNumber(lib.dataseg);
+ }
+ return response;
+}
+
+QString Session::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << "Session: " << deviceDescription(false) << '\n'
+ << "pid: " << pid << "main thread: " << mainTid
+ << " current thread: " << tid << ' ';
+ str.setIntegerBase(16);
+ str << " code: 0x" << codeseg << " data: 0x" << dataseg << '\n';
+ if (const int libCount = libraries.size()) {
+ str << "Libraries:\n";
+ for (int i = 0; i < libCount; i++)
+ str << " #" << i << ' ' << libraries.at(i).name
+ << " code: 0x" << libraries.at(i).codeseg
+ << " data: 0x" << libraries.at(i).dataseg << '\n';
+ }
+ if (const int moduleCount = modules.size()) {
+ str << "Modules:\n";
+ for (int i = 0; i < moduleCount; i++)
+ str << " #" << i << ' ' << modules.at(i) << '\n';
+ }
+ str.setIntegerBase(10);
+ if (!addressToBP.isEmpty()) {
+ typedef QHash<uint, uint>::const_iterator BP_ConstIterator;
+ str << "Breakpoints:\n";
+ const BP_ConstIterator cend = addressToBP.constEnd();
+ for (BP_ConstIterator it = addressToBP.constBegin(); it != cend; ++it) {
+ str.setIntegerBase(16);
+ str << " 0x" << it.key();
+ str.setIntegerBase(10);
+ str << ' ' << it.value() << '\n';
+ }
+ }
+
+ return rc;
+}
+
+// --------------
+
+QByteArray decode7d(const QByteArray &ba)
+{
+ QByteArray res;
+ res.reserve(ba.size());
+ for (int i = 0; i < ba.size(); ++i) {
+ byte c = byte(ba.at(i));
+ if (c == 0x7d) {
+ ++i;
+ c = 0x20 ^ byte(ba.at(i));
+ }
+ res.append(c);
+ }
+ return res;
+}
+
+QByteArray encode7d(const QByteArray &ba)
+{
+ QByteArray res;
+ res.reserve(ba.size() + 2);
+ for (int i = 0; i < ba.size(); ++i) {
+ byte c = byte(ba.at(i));
+ if (c == 0x7e || c == 0x7d) {
+ res.append(0x7d);
+ res.append(0x20 ^ c);
+ } else {
+ res.append(c);
+ }
+ }
+ return res;
+}
+
+// FIXME: Use the QByteArray based version below?
+static inline QString stringFromByte(byte c)
+{
+ return QString::fromLatin1("%1").arg(c, 2, 16, QChar('0'));
+}
+
+SYMBIANUTILS_EXPORT QString stringFromArray(const QByteArray &ba, int maxLen)
+{
+ QString str;
+ QString ascii;
+ const int size = maxLen == -1 ? ba.size() : qMin(ba.size(), maxLen);
+ for (int i = 0; i < size; ++i) {
+ const int c = byte(ba.at(i));
+ str += QString::fromAscii("%1 ").arg(c, 2, 16, QChar('0'));
+ ascii += QChar(c).isPrint() ? QChar(c) : QChar('.');
+ }
+ if (size != ba.size()) {
+ str += QLatin1String("...");
+ ascii += QLatin1String("...");
+ }
+ return str + QLatin1String(" ") + ascii;
+}
+
+SYMBIANUTILS_EXPORT QByteArray hexNumber(uint n, int digits)
+{
+ QByteArray ba = QByteArray::number(n, 16);
+ if (digits == 0 || ba.size() == digits)
+ return ba;
+ return QByteArray(digits - ba.size(), '0') + ba;
+}
+
+SYMBIANUTILS_EXPORT QByteArray hexxNumber(uint n, int digits)
+{
+ return "0x" + hexNumber(n, digits);
+}
+
+TrkResult::TrkResult() :
+ code(0),
+ token(0),
+ isDebugOutput(false)
+{
+}
+
+void TrkResult::clear()
+{
+ code = token= 0;
+ isDebugOutput = false;
+ data.clear();
+ cookie = QVariant();
+}
+
+QString TrkResult::toString() const
+{
+ QString res = stringFromByte(code);
+ res += QLatin1String(" [");
+ res += stringFromByte(token);
+ res += QLatin1Char(']');
+ res += QLatin1Char(' ');
+ res += stringFromArray(data);
+ return res;
+}
+
+QByteArray frameMessage(byte command, byte token, const QByteArray &data, bool serialFrame)
+{
+ byte s = command + token;
+ for (int i = 0; i != data.size(); ++i)
+ s += data.at(i);
+ byte checksum = 255 - (s & 0xff);
+ //int x = s + ~s;
+ //logMessage("check: " << s << checksum << x;
+
+ QByteArray response;
+ response.reserve(data.size() + 3);
+ response.append(char(command));
+ response.append(char(token));
+ response.append(data);
+ response.append(char(checksum));
+
+ QByteArray encodedData = encode7d(response);
+
+ QByteArray ba;
+ ba.reserve(encodedData.size() + 6);
+ if (serialFrame) {
+ ba.append(char(0x01));
+ ba.append(char(0x90));
+ const ushort encodedSize = encodedData.size() + 2; // 2 x 0x7e
+ appendShort(&ba, encodedSize, BigEndian);
+ }
+ ba.append(char(0x7e));
+ ba.append(encodedData);
+ ba.append(char(0x7e));
+
+ return ba;
+}
+
+/* returns 0 if array doesn't represent a result,
+otherwise returns the length of the result data */
+ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame, ushort& mux)
+{
+ if (serialFrame) {
+ // Serial protocol with length info
+ if (buffer.length() < 4)
+ return 0;
+ mux = extractShort(buffer.data());
+ const ushort len = extractShort(buffer.data() + 2);
+ return (buffer.size() >= len + 4) ? len : ushort(0);
+ }
+ // Frameless protocol without length info
+ const char delimiter = char(0x7e);
+ const int firstDelimiterPos = buffer.indexOf(delimiter);
+ // Regular message delimited by 0x7e..0x7e
+ if (firstDelimiterPos == 0) {
+ mux = MuxTrk;
+ const int endPos = buffer.indexOf(delimiter, firstDelimiterPos + 1);
+ return endPos != -1 ? endPos + 1 - firstDelimiterPos : 0;
+ }
+ // Some ASCII log message up to first delimiter or all
+ return firstDelimiterPos != -1 ? firstDelimiterPos : buffer.size();
+}
+
+bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, bool &linkEstablishmentMode, QByteArray *rawData)
+{
+ result->clear();
+ if(rawData)
+ rawData->clear();
+ ushort len = isValidTrkResult(*buffer, serialFrame, result->multiplex);
+ // handle receiving application output, which is not a regular command
+ const int delimiterPos = serialFrame ? 4 : 0;
+ if (linkEstablishmentMode) {
+ //when "hot connecting" a device, we can receive partial frames.
+ //this code resyncs by discarding data until a TRK frame is found
+ while (buffer->length() > delimiterPos
+ && result->multiplex != MuxTextTrace
+ && !(result->multiplex == MuxTrk && buffer->at(delimiterPos) == 0x7e)) {
+ buffer->remove(0,1);
+ len = isValidTrkResult(*buffer, serialFrame, result->multiplex);
+ }
+ }
+ if (!len)
+ return false;
+ if (buffer->at(delimiterPos) != 0x7e) {
+ result->isDebugOutput = true;
+ result->data = buffer->mid(delimiterPos, len);
+ buffer->remove(0, delimiterPos + len);
+ return true;
+ }
+ // FIXME: what happens if the length contains 0xfe?
+ // Assume for now that it passes unencoded!
+ const QByteArray data = decode7d(buffer->mid(delimiterPos + 1, len - 2));
+ if(rawData)
+ *rawData = data;
+ buffer->remove(0, delimiterPos + len);
+
+ byte sum = 0;
+ for (int i = 0; i < data.size(); ++i) // 3 = 2 * 0xfe + sum
+ sum += byte(data.at(i));
+ if (sum != 0xff)
+ logMessage("*** CHECKSUM ERROR: " << byte(sum));
+
+ result->code = data.at(0);
+ result->token = data.at(1);
+ result->data = data.mid(2, data.size() - 3);
+ //logMessage(" REST BUF: " << stringFromArray(*buffer));
+ //logMessage(" CURR DATA: " << stringFromArray(data));
+ //QByteArray prefix = "READ BUF: ";
+ //logMessage((prefix + "HEADER: " + stringFromArray(header).toLatin1()).data());
+ linkEstablishmentMode = false; //have received a good TRK packet, therefore in sync
+ return true;
+}
+
+SYMBIANUTILS_EXPORT ushort extractShort(const char *data)
+{
+ return byte(data[0]) * 256 + byte(data[1]);
+}
+
+SYMBIANUTILS_EXPORT uint extractInt(const char *data)
+{
+ uint res = byte(data[0]);
+ res *= 256; res += byte(data[1]);
+ res *= 256; res += byte(data[2]);
+ res *= 256; res += byte(data[3]);
+ return res;
+}
+
+SYMBIANUTILS_EXPORT quint64 extractInt64(const char *data)
+{
+ quint64 res = byte(data[0]);
+ res <<= 8; res += byte(data[1]);
+ res <<= 8; res += byte(data[2]);
+ res <<= 8; res += byte(data[3]);
+ res <<= 8; res += byte(data[4]);
+ res <<= 8; res += byte(data[5]);
+ res <<= 8; res += byte(data[6]);
+ res <<= 8; res += byte(data[7]);
+ return res;
+}
+
+SYMBIANUTILS_EXPORT QString quoteUnprintableLatin1(const QByteArray &ba)
+{
+ QString res;
+ char buf[10];
+ for (int i = 0, n = ba.size(); i != n; ++i) {
+ const byte c = ba.at(i);
+ if (isprint(c)) {
+ res += c;
+ } else {
+ qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
+ res += buf;
+ }
+ }
+ return res;
+}
+
+SYMBIANUTILS_EXPORT void appendShort(QByteArray *ba, ushort s, Endianness endian)
+{
+ if (endian == BigEndian) {
+ ba->append(s / 256);
+ ba->append(s % 256);
+ } else {
+ ba->append(s % 256);
+ ba->append(s / 256);
+ }
+}
+
+SYMBIANUTILS_EXPORT void appendInt(QByteArray *ba, uint i, Endianness endian)
+{
+ const uchar b3 = i % 256; i /= 256;
+ const uchar b2 = i % 256; i /= 256;
+ const uchar b1 = i % 256; i /= 256;
+ const uchar b0 = i;
+ ba->reserve(ba->size() + 4);
+ if (endian == BigEndian) {
+ ba->append(b0);
+ ba->append(b1);
+ ba->append(b2);
+ ba->append(b3);
+ } else {
+ ba->append(b3);
+ ba->append(b2);
+ ba->append(b1);
+ ba->append(b0);
+ }
+}
+
+void appendString(QByteArray *ba, const QByteArray &str, Endianness endian, bool appendNullTerminator)
+{
+ const int fullSize = str.size() + (appendNullTerminator ? 1 : 0);
+ appendShort(ba, fullSize, endian); // count the terminating \0
+ ba->append(str);
+ if (appendNullTerminator)
+ ba->append('\0');
+}
+
+void appendDateTime(QByteArray *ba, QDateTime dateTime, Endianness endian)
+{
+ // convert the QDateTime to UTC and append its representation to QByteArray
+ // format is the same as in FAT file system
+ dateTime = dateTime.toUTC();
+ const QTime utcTime = dateTime.time();
+ const QDate utcDate = dateTime.date();
+ uint fatDateTime = (utcTime.hour() << 11 | utcTime.minute() << 5 | utcTime.second()/2) << 16;
+ fatDateTime |= (utcDate.year()-1980) << 9 | utcDate.month() << 5 | utcDate.day();
+ appendInt(ba, fatDateTime, endian);
+}
+
+QByteArray errorMessage(byte code)
+{
+ switch (code) {
+ case 0x00: return "No error";
+ case 0x01: return "Generic error in CWDS message";
+ case 0x02: return "Unexpected packet size in send msg";
+ case 0x03: return "Internal error occurred in CWDS";
+ case 0x04: return "Escape followed by frame flag";
+ case 0x05: return "Bad FCS in packet";
+ case 0x06: return "Packet too long";
+ case 0x07: return "Sequence ID not expected (gap in sequence)";
+
+ case 0x10: return "Command not supported";
+ case 0x11: return "Command param out of range";
+ case 0x12: return "An option was not supported";
+ case 0x13: return "Read/write to invalid memory";
+ case 0x14: return "Read/write invalid registers";
+ case 0x15: return "Exception occurred in CWDS";
+ case 0x16: return "Targeted system or thread is running";
+ case 0x17: return "Breakpoint resources (HW or SW) exhausted";
+ case 0x18: return "Requested breakpoint conflicts with existing one";
+
+ case 0x20: return "General OS-related error";
+ case 0x21: return "Request specified invalid process";
+ case 0x22: return "Request specified invalid thread";
+ }
+ return "Unknown error";
+}
+
+uint swapEndian(uint in)
+{
+ return (in>>24) | ((in<<8) & 0x00FF0000) | ((in>>8) & 0x0000FF00) | (in<<24);
+}
+
+int TrkResult::errorCode() const
+{
+ // NAK means always error, else data sized 1 with a non-null element
+ const bool isNAK = code == 0xff;
+ if (data.size() != 1 && !isNAK)
+ return 0;
+ if (const int errorCode = data.at(0))
+ return errorCode;
+ return isNAK ? 0xff : 0;
+}
+
+QString TrkResult::errorString() const
+{
+ // NAK means always error, else data sized 1 with a non-null element
+ if (code == 0xff)
+ return "NAK";
+ if (data.size() < 1)
+ return "Unknown error packet";
+ return errorMessage(data.at(0));
+}
+
+} // namespace trk
+
diff --git a/src/runonphone/symbianutils/trkutils.h b/src/runonphone/symbianutils/trkutils.h
new file mode 100644
index 000000000..c22df865a
--- /dev/null
+++ b/src/runonphone/symbianutils/trkutils.h
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DEBUGGER_TRK_UTILS
+#define DEBUGGER_TRK_UTILS
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QHash>
+#include <QtCore/QStringList>
+#include <QtCore/QVariant>
+
+QT_BEGIN_NAMESPACE
+class QDateTime;
+QT_END_NAMESPACE
+
+namespace trk {
+
+typedef unsigned char byte;
+struct TrkResult;
+
+enum Command {
+ //meta commands
+ TrkPing = 0x00,
+ TrkConnect = 0x01,
+ TrkDisconnect = 0x02,
+ TrkReset = 0x03,
+ TrkVersions = 0x04,
+ TrkSupported = 0x05,
+ TrkCpuType = 0x06,
+ TrkConfigTransport = 0x07,
+ TrkVersions2 = 0x08,
+ TrkHostVersions = 0x09,
+
+ //state commands
+ TrkReadMemory = 0x10,
+ TrkWriteMemory = 0x11,
+ TrkReadRegisters = 0x12,
+ TrkWriteRegisters = 0x13,
+ TrkFillMemory = 0x14,
+ TrkCopyMemory = 0x15,
+ TrkFlushCache = 0x16,
+
+ //execution commands
+ TrkContinue = 0x18,
+ TrkStep = 0x19,
+ TrkStop = 0x1a,
+ TrkSetBreak = 0x1b,
+ TrkClearBreak = 0x1c,
+ TrkDownload = 0x1d,
+ TrkModifyBreakThread = 0x1e,
+
+ //host -> target IO management
+ TrkNotifyFileInput = 0x20,
+ TrkBlockFileIo = 0x21,
+
+ //host -> target os commands
+ TrkCreateItem = 0x40,
+ TrkDeleteItem = 0x41,
+ TrkReadInfo = 0x42,
+ TrkWriteInfo = 0x43,
+
+ TrkWriteFile = 0x48,
+ TrkReadFile = 0x49,
+ TrkOpenFile = 0x4a,
+ TrkCloseFile = 0x4b,
+ TrkPositionFile = 0x4c,
+ TrkInstallFile = 0x4d,
+ TrkInstallFile2 = 0x4e,
+
+ TrkPhoneSwVersion = 0x4f,
+ TrkPhoneName = 0x50,
+ TrkVersions3 = 0x51,
+
+ //replies
+ TrkNotifyAck = 0x80,
+ TrkNotifyNak = 0xff,
+
+ //target -> host notification
+ TrkNotifyStopped = 0x90,
+ TrkNotifyException = 0x91,
+ TrkNotifyInternalError = 0x92,
+ TrkNotifyStopped2 = 0x94,
+
+ //target -> host OS notification
+ TrkNotifyCreated = 0xa0,
+ TrkNotifyDeleted = 0xa1,
+ TrkNotifyProcessorStarted = 0xa2,
+ TrkNotifyProcessorStandBy = 0xa6,
+ TrkNotifyProcessorReset = 0xa7,
+
+ //target -> host support commands (these are defined but not implemented in TRK)
+ TrkDSWriteFile = 0xd0,
+ TrkDSReadFile = 0xd1,
+ TrkDSOpenFile = 0xd2,
+ TrkDSCloseFile = 0xd3,
+ TrkDSPositionFile = 0xd4
+};
+
+enum DSOSItemTypes {
+ kDSOSProcessItem = 0x0000,
+ kDSOSThreadItem = 0x0001,
+ kDSOSDLLItem = 0x0002,
+ kDSOSAppItem = 0x0003,
+ kDSOSMemBlockItem = 0x0004,
+ kDSOSProcAttachItem = 0x0005,
+ kDSOSThreadAttachItem = 0x0006,
+ kDSOSProcAttach2Item = 0x0007,
+ kDSOSProcRunItem = 0x0008,
+ /* 0x0009 - 0x00ff reserved for general expansion */
+ /* 0x0100 - 0xffff available for target-specific use */
+};
+
+enum SerialMultiplexor {
+ MuxRaw = 0,
+ MuxTextTrace = 0x0102,
+ MuxTrk = 0x0190
+};
+
+inline byte extractByte(const char *data) { return *data; }
+SYMBIANUTILS_EXPORT ushort extractShort(const char *data);
+SYMBIANUTILS_EXPORT uint extractInt(const char *data);
+SYMBIANUTILS_EXPORT quint64 extractInt64(const char *data);
+
+SYMBIANUTILS_EXPORT QString quoteUnprintableLatin1(const QByteArray &ba);
+
+// produces "xx xx xx "
+SYMBIANUTILS_EXPORT QString stringFromArray(const QByteArray &ba, int maxLen = - 1);
+
+enum Endianness
+{
+ LittleEndian,
+ BigEndian,
+ TargetByteOrder = BigEndian,
+};
+
+SYMBIANUTILS_EXPORT void appendShort(QByteArray *ba, ushort s, Endianness = TargetByteOrder);
+SYMBIANUTILS_EXPORT void appendInt(QByteArray *ba, uint i, Endianness = TargetByteOrder);
+SYMBIANUTILS_EXPORT void appendString(QByteArray *ba, const QByteArray &str, Endianness = TargetByteOrder, bool appendNullTerminator = true);
+
+struct SYMBIANUTILS_EXPORT Library
+{
+ Library();
+ explicit Library(const TrkResult &r);
+
+ QByteArray name;
+ uint codeseg;
+ uint dataseg;
+ //library addresses are valid for a given process (depending on memory model, they might be loaded at the same address in all processes or not)
+ uint pid;
+};
+
+struct SYMBIANUTILS_EXPORT TrkAppVersion
+{
+ TrkAppVersion();
+ void reset();
+
+ int trkMajor;
+ int trkMinor;
+ int protocolMajor;
+ int protocolMinor;
+};
+
+struct SYMBIANUTILS_EXPORT Session
+{
+ Session();
+ void reset();
+ QString deviceDescription(unsigned verbose) const;
+ QString toString() const;
+ // Answer to qXfer::libraries
+ QByteArray gdbLibraryList() const;
+ // Answer to qsDllInfo, can be sent chunk-wise.
+ QByteArray gdbQsDllInfo(int start = 0, int count = -1) const;
+
+ // Trk feedback
+ byte cpuMajor;
+ byte cpuMinor;
+ byte bigEndian;
+ byte defaultTypeSize;
+ byte fpTypeSize;
+ byte extended1TypeSize;
+ byte extended2TypeSize;
+ TrkAppVersion trkAppVersion;
+ uint pid;
+ uint mainTid;
+ uint tid;
+ uint codeseg;
+ uint dataseg;
+ QHash<uint, uint> addressToBP;
+
+ typedef QList<Library> Libraries;
+ Libraries libraries;
+
+ // Gdb request
+ QStringList modules;
+};
+
+struct SYMBIANUTILS_EXPORT TrkResult
+{
+ TrkResult();
+ void clear();
+ QString toString() const;
+ // 0 for no error.
+ int errorCode() const;
+ QString errorString() const;
+
+ ushort multiplex;
+ byte code;
+ byte token;
+ QByteArray data;
+ QVariant cookie;
+ bool isDebugOutput;
+};
+
+SYMBIANUTILS_EXPORT QByteArray errorMessage(byte code);
+SYMBIANUTILS_EXPORT QByteArray hexNumber(uint n, int digits = 0);
+SYMBIANUTILS_EXPORT QByteArray hexxNumber(uint n, int digits = 0); // prepends '0x', too
+SYMBIANUTILS_EXPORT uint swapEndian(uint in);
+
+} // namespace trk
+
+#endif // DEBUGGER_TRK_UTILS
diff --git a/src/runonphone/symbianutils/trkutils_p.h b/src/runonphone/symbianutils/trkutils_p.h
new file mode 100644
index 000000000..df9504c5e
--- /dev/null
+++ b/src/runonphone/symbianutils/trkutils_p.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DEBUGGER_TRK_PRIVATE_UTILS
+#define DEBUGGER_TRK_PRIVATE_UTILS
+
+#include "trkutils.h"
+#include "symbianutils_global.h"
+
+QT_BEGIN_NAMESPACE
+class QDateTime;
+QT_END_NAMESPACE
+
+namespace trk {
+
+void appendDateTime(QByteArray *ba, QDateTime dateTime, Endianness = TargetByteOrder);
+// returns a QByteArray containing optionally
+// the serial frame [0x01 0x90 <len>] and 0x7e encoded7d(ba) 0x7e
+QByteArray frameMessage(byte command, byte token, const QByteArray &data, bool serialFrame);
+bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *r, bool& linkEstablishmentMode, QByteArray *rawData = 0);
+
+} // namespace trk
+
+#endif // DEBUGGER_TRK_PRIVATE_UTILS