summaryrefslogtreecommitdiffstats
path: root/libqdb
diff options
context:
space:
mode:
authorKari Oikarinen <kari.oikarinen@qt.io>2016-06-22 13:18:04 +0300
committerKari Oikarinen <kari.oikarinen@qt.io>2016-07-14 11:57:38 +0000
commit3a6284c97ee1fb22f0bafe14f0f4d4ac2d8d2acf (patch)
tree259845e8b1c150fb5bb42e7dbcbe2d77a76d6650 /libqdb
parent174aad875aa79027dae885450ddd97d59cd27426 (diff)
Import QDB prototype
The prototype is capable of: - Running a process on the device - Pushing a file to the device - Pulling a file from the device The device is connected to through USB. There is not yet management of multiple connected devices. Change-Id: Icba20e1d68dafbab9d71f44b86c20efb1df45310 Reviewed-by: Rainer Keller <Rainer.Keller@qt.io>
Diffstat (limited to 'libqdb')
-rw-r--r--libqdb/abstractconnection.cpp44
-rw-r--r--libqdb/abstractconnection.h54
-rw-r--r--libqdb/filepullcommon.h61
-rw-r--r--libqdb/filepushcommon.h61
-rw-r--r--libqdb/interruptsignalhandler.cpp82
-rw-r--r--libqdb/interruptsignalhandler.h58
-rw-r--r--libqdb/libqdb.pro42
-rw-r--r--libqdb/libqdb_global.h32
-rw-r--r--libqdb/processcommon.h61
-rw-r--r--libqdb/protocol/protocol.h31
-rw-r--r--libqdb/protocol/qdbmessage.cpp190
-rw-r--r--libqdb/protocol/qdbmessage.h78
-rw-r--r--libqdb/protocol/qdbtransport.cpp79
-rw-r--r--libqdb/protocol/qdbtransport.h52
-rw-r--r--libqdb/protocol/services.h48
-rw-r--r--libqdb/stream.cpp123
-rw-r--r--libqdb/stream.h60
-rw-r--r--libqdb/streampacket.cpp52
-rw-r--r--libqdb/streampacket.h61
-rw-r--r--libqdb/usb/usbconnection.cpp215
-rw-r--r--libqdb/usb/usbconnection.h67
-rw-r--r--libqdb/usb/usbconnectionreader.cpp56
-rw-r--r--libqdb/usb/usbconnectionreader.h45
23 files changed, 1652 insertions, 0 deletions
diff --git a/libqdb/abstractconnection.cpp b/libqdb/abstractconnection.cpp
new file mode 100644
index 0000000..7f54646
--- /dev/null
+++ b/libqdb/abstractconnection.cpp
@@ -0,0 +1,44 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#include "abstractconnection.h"
+#include "protocol/qdbtransport.h"
+
+
+AbstractConnection::AbstractConnection(QdbTransport *transport, QObject *parent)
+ : QObject{parent},
+ m_transport{transport},
+ m_outgoingMessages{},
+ m_streams{},
+ m_nextStreamId{1} // Start from 1 since stream IDs of 0 have special meaning
+{
+
+}
+
+AbstractConnection::~AbstractConnection()
+{
+
+}
+
+bool AbstractConnection::initialize()
+{
+ connect(m_transport.get(), &QdbTransport::messageAvailable, this, &AbstractConnection::handleMessage);
+ return m_transport->open();
+}
diff --git a/libqdb/abstractconnection.h b/libqdb/abstractconnection.h
new file mode 100644
index 0000000..eb25c57
--- /dev/null
+++ b/libqdb/abstractconnection.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef ABSTRACTCONNECTION_H
+#define ABSTRACTCONNECTION_H
+
+#include "protocol/qdbmessage.h"
+#include "stream.h"
+class QdbTransport;
+
+#include <QtCore/qobject.h>
+#include <QtCore/qqueue.h>
+
+#include <memory>
+#include <unordered_map>
+
+class AbstractConnection : public QObject
+{
+ Q_OBJECT
+public:
+ AbstractConnection(QdbTransport *transport, QObject *parent = 0);
+ virtual ~AbstractConnection();
+
+ virtual bool initialize();
+ virtual void enqueueMessage(const QdbMessage &message) = 0;
+
+public slots:
+ virtual void handleMessage() = 0;
+
+protected:
+ std::unique_ptr<QdbTransport> m_transport;
+ QQueue<QdbMessage> m_outgoingMessages;
+ std::unordered_map<StreamId, std::unique_ptr<Stream>> m_streams;
+ StreamId m_nextStreamId;
+};
+
+#endif // ABSTRACTCONNECTION_H
diff --git a/libqdb/filepullcommon.h b/libqdb/filepullcommon.h
new file mode 100644
index 0000000..b4cdf43
--- /dev/null
+++ b/libqdb/filepullcommon.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef FILEPULLCOMMON_H
+#define FILEPULLCOMMON_H
+
+#include <QtGlobal>
+
+#include <cstdint>
+
+enum FilePullPacketType : uint32_t
+{
+ FilePullOpen = 1,
+ FilePullOpened,
+ FilePullRead,
+ FilePullWasRead,
+ FilePullEnd,
+ FilePullError,
+};
+
+inline
+FilePullPacketType toFilePullPacketType(uint32_t x)
+{
+ switch (static_cast<FilePullPacketType>(x))
+ {
+ case FilePullOpen:
+ return FilePullOpen;
+ case FilePullOpened:
+ return FilePullOpened;
+ case FilePullRead:
+ return FilePullRead;
+ case FilePullWasRead:
+ return FilePullWasRead;
+ case FilePullEnd:
+ return FilePullEnd;
+ case FilePullError:
+ return FilePullError;
+ default:
+ Q_UNREACHABLE();
+ return FilePullError;
+ }
+}
+
+#endif // FILEPULLCOMMON_H
diff --git a/libqdb/filepushcommon.h b/libqdb/filepushcommon.h
new file mode 100644
index 0000000..35537a7
--- /dev/null
+++ b/libqdb/filepushcommon.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef FILEPUSHCOMMON_H
+#define FILEPUSHCOMMON_H
+
+#include <QtGlobal>
+
+#include <cstdint>
+
+enum FilePushPacketType : uint32_t
+{
+ FilePushOpen = 1,
+ FilePushOpened,
+ FilePushWrite,
+ FilePushWritten,
+ FilePushEnd,
+ FilePushError,
+};
+
+inline
+FilePushPacketType toFilePushPacketType(uint32_t x)
+{
+ switch (static_cast<FilePushPacketType>(x))
+ {
+ case FilePushOpen:
+ return FilePushOpen;
+ case FilePushOpened:
+ return FilePushOpened;
+ case FilePushWrite:
+ return FilePushWrite;
+ case FilePushWritten:
+ return FilePushWritten;
+ case FilePushEnd:
+ return FilePushEnd;
+ case FilePushError:
+ return FilePushError;
+ default:
+ Q_UNREACHABLE();
+ return FilePushError;
+ }
+}
+
+#endif // FILEPUSHCOMMON_H
diff --git a/libqdb/interruptsignalhandler.cpp b/libqdb/interruptsignalhandler.cpp
new file mode 100644
index 0000000..067be36
--- /dev/null
+++ b/libqdb/interruptsignalhandler.cpp
@@ -0,0 +1,82 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#include "interruptsignalhandler.h"
+
+#include "../utils/make_unique.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qsocketnotifier.h>
+
+#include <signal.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+InterruptSignalHandler::InterruptSignalHandler(QObject *parent)
+ : QObject(parent),
+ m_socketNotifier{nullptr}
+{
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, s_socketPair))
+ qFatal("Could not create socketpair for signal handling in InterruptSignalHandler");
+
+ m_socketNotifier = make_unique<QSocketNotifier>(s_socketPair[1], QSocketNotifier::Read);
+ connect(m_socketNotifier.get(), SIGNAL(activated(int)), this, SLOT(handleSigInt()));
+
+ if (!installSigIntHandler())
+ qFatal("Could not install signal handler in InterruptSignalHandler");
+}
+
+int InterruptSignalHandler::s_socketPair[2] = {0, 0};
+
+InterruptSignalHandler::~InterruptSignalHandler() = default;
+
+void InterruptSignalHandler::sigIntHandler(int signalId)
+{
+ Q_UNUSED(signalId);
+
+ char a = 1;
+ write(s_socketPair[0], &a, sizeof(a));
+}
+
+void InterruptSignalHandler::handleSigInt()
+{
+ char tmp;
+ read(s_socketPair[1], &tmp, sizeof(tmp));
+
+ // Reset signal action to allow a second Control-C to interrupt hanging qdb
+ sigaction(SIGINT, &m_oldAction, nullptr);
+
+ emit interrupted();
+}
+
+bool InterruptSignalHandler::installSigIntHandler()
+{
+ struct sigaction action;
+ action.sa_handler = &InterruptSignalHandler::sigIntHandler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = 0;
+ action.sa_flags |= SA_RESTART;
+
+ if (sigaction(SIGINT, &action, &m_oldAction))
+ return false;
+
+ return true;
+}
+
diff --git a/libqdb/interruptsignalhandler.h b/libqdb/interruptsignalhandler.h
new file mode 100644
index 0000000..4e59efe
--- /dev/null
+++ b/libqdb/interruptsignalhandler.h
@@ -0,0 +1,58 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef INTERRUPTSIGNALHANDLER_H
+#define INTERRUPTSIGNALHANDLER_H
+
+#include <QObject>
+class QSocketNotifier;
+
+#include <memory>
+
+#ifndef Q_OS_UNIX
+# error "InterruptSignalHandler only supports UNIX signals"
+#endif
+
+#include <signal.h>
+
+class InterruptSignalHandler : public QObject
+{
+ Q_OBJECT
+public:
+ explicit InterruptSignalHandler(QObject *parent = 0);
+ ~InterruptSignalHandler();
+
+ static void sigIntHandler(int signalId);
+
+signals:
+ void interrupted();
+
+public slots:
+ void handleSigInt();
+
+private:
+ bool installSigIntHandler();
+
+ static int s_socketPair[2];
+ struct sigaction m_oldAction;
+ std::unique_ptr<QSocketNotifier> m_socketNotifier;
+};
+
+#endif // INTERRUPTSIGNALHANDLER_H
diff --git a/libqdb/libqdb.pro b/libqdb/libqdb.pro
new file mode 100644
index 0000000..5e1a5c9
--- /dev/null
+++ b/libqdb/libqdb.pro
@@ -0,0 +1,42 @@
+QT -= gui
+
+TARGET = qdb
+TEMPLATE = lib
+
+DEFINES += QDB_LIBRARY
+
+SOURCES += \
+ abstractconnection.cpp \
+ interruptsignalhandler.cpp \
+ protocol/qdbmessage.cpp \
+ protocol/qdbtransport.cpp \
+ stream.cpp \
+ streampacket.cpp \
+ usb/usbconnection.cpp \
+ usb/usbconnectionreader.cpp \
+
+HEADERS += \
+ abstractconnection.h \
+ filepullcommon.h \
+ filepushcommon.h \
+ interruptsignalhandler.h \
+ libqdb_global.h \
+ processcommon.h \
+ protocol/protocol.h \
+ protocol/qdbmessage.h \
+ protocol/qdbtransport.h \
+ protocol/services.h \
+ stream.h \
+ streampacket.h \
+ usb/usbconnection.h \
+ usb/usbconnectionreader.h \
+
+INCLUDEPATH += $$PWD
+
+INCLUDEPATH += $$[QT_SYSROOT]/usr/include/libusb-1.0/
+LIBS += -lusb-1.0
+
+unix {
+ target.path = /usr/lib
+ INSTALLS += target
+}
diff --git a/libqdb/libqdb_global.h b/libqdb/libqdb_global.h
new file mode 100644
index 0000000..86e9393
--- /dev/null
+++ b/libqdb/libqdb_global.h
@@ -0,0 +1,32 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef LIBQDB_GLOBAL_H
+#define LIBQDB_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(LIBQDB_LIBRARY)
+# define LIBQDBSHARED_EXPORT Q_DECL_EXPORT
+#else
+# define LIBQDBSHARED_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // LIBQDB_GLOBAL_H
diff --git a/libqdb/processcommon.h b/libqdb/processcommon.h
new file mode 100644
index 0000000..d7dbc31
--- /dev/null
+++ b/libqdb/processcommon.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef PROCESSCOMMON_H
+#define PROCESSCOMMON_H
+
+#include <QtGlobal>
+
+#include <cstdint>
+
+enum ProcessPacketType : uint32_t
+{
+ ProcessStart = 1,
+ ProcessStarted,
+ ProcessRead,
+ ProcessWrite,
+ ProcessError,
+ ProcessFinished,
+};
+
+inline
+ProcessPacketType toProcessPacketType(uint32_t x)
+{
+ switch (static_cast<ProcessPacketType>(x))
+ {
+ case ProcessStart:
+ return ProcessStart;
+ case ProcessStarted:
+ return ProcessStarted;
+ case ProcessRead:
+ return ProcessRead;
+ case ProcessWrite:
+ return ProcessWrite;
+ case ProcessError:
+ return ProcessError;
+ case ProcessFinished:
+ return ProcessFinished;
+ default:
+ Q_UNREACHABLE(); // all possible values are covered
+ return ProcessError;
+ }
+}
+
+#endif // PROCESSCOMMON_H
diff --git a/libqdb/protocol/protocol.h b/libqdb/protocol/protocol.h
new file mode 100644
index 0000000..5878e52
--- /dev/null
+++ b/libqdb/protocol/protocol.h
@@ -0,0 +1,31 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef PROTOCOL_H
+#define PROTOCOL_H
+
+const uint8_t qdbUsbClassId = 0xff;
+const uint8_t qdbUsbSubclassId = 0x52;
+const uint8_t qdbUsbProtocolId = 0x1;
+const int qdbHeaderSize = 4*sizeof(uint32_t);
+const int qdbMessageSize = 16*1024;
+const int qdbMaxPayloadSize = qdbMessageSize - qdbHeaderSize;
+
+#endif
diff --git a/libqdb/protocol/qdbmessage.cpp b/libqdb/protocol/qdbmessage.cpp
new file mode 100644
index 0000000..9a9f26f
--- /dev/null
+++ b/libqdb/protocol/qdbmessage.cpp
@@ -0,0 +1,190 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#include "qdbmessage.h"
+
+#include <QtCore/qdatastream.h>
+#include <QtCore/qdebug.h>
+#include <QtEndian>
+
+int QdbMessage::GetDataSize(const QByteArray &header)
+{
+ const uint32_t sizeFieldBE = *(reinterpret_cast<const uint32_t *>(header.data()) + 3);
+ const uint32_t sizeField = qFromBigEndian(sizeFieldBE);
+
+ if (sizeField == 0xFFFFFFFF) {
+ // empty QByteArray
+ return 0;
+ }
+ Q_ASSERT(sizeField <= std::numeric_limits<int>::max());
+ return sizeField;
+}
+
+QdbMessage::QdbMessage()
+ : QdbMessage{Invalid, 0, 0, QByteArray{}}
+{
+
+}
+
+QdbMessage::QdbMessage(QdbMessage::CommandType command, StreamId hostStream, StreamId deviceStream)
+ : QdbMessage{command, hostStream, deviceStream, QByteArray{}}
+{
+
+}
+
+QdbMessage::QdbMessage(QdbMessage::CommandType command, StreamId hostStream, StreamId deviceStream, QByteArray data)
+ : m_command{command},
+ m_hostStream{hostStream},
+ m_deviceStream{deviceStream},
+ m_data{data}
+{
+
+}
+
+QdbMessage::QdbMessage(QdbMessage::CommandType command, StreamId hostStream, StreamId deviceStream, const char *data, int length)
+ : QdbMessage{command, hostStream, deviceStream, QByteArray{data, length}}
+{
+
+}
+
+QdbMessage::CommandType QdbMessage::command() const
+{
+ return m_command;
+}
+
+void QdbMessage::setCommand(QdbMessage::CommandType command)
+{
+ m_command = command;
+}
+
+StreamId QdbMessage::hostStream() const
+{
+ return m_hostStream;
+}
+
+void QdbMessage::setHostStream(StreamId hostStream)
+{
+ m_hostStream = hostStream;
+}
+
+StreamId QdbMessage::deviceStream() const
+{
+ return m_deviceStream;
+}
+
+void QdbMessage::setDeviceStream(StreamId deviceStream)
+{
+ m_deviceStream = deviceStream;
+}
+
+const QByteArray &QdbMessage::data() const
+{
+ return m_data;
+}
+
+void QdbMessage::setData(const QByteArray &data)
+{
+ m_data = data;
+}
+
+void QdbMessage::setData(const char *data, int length)
+{
+ m_data = QByteArray{data, length};
+}
+
+QDataStream &operator<<(QDataStream &stream, const QdbMessage &message)
+{
+ stream << message.command();
+ stream << message.hostStream() << message.deviceStream();
+ stream << message.data();
+
+ return stream;
+}
+
+QdbMessage::CommandType toCommandType(uint32_t command)
+{
+ switch (command) {
+ case static_cast<uint32_t>(QdbMessage::Connect):
+ return QdbMessage::Connect;
+ case static_cast<uint32_t>(QdbMessage::Open):
+ return QdbMessage::Open;
+ case static_cast<uint32_t>(QdbMessage::Write):
+ return QdbMessage::Write;
+ case static_cast<uint32_t>(QdbMessage::Close):
+ return QdbMessage::Close;
+ case static_cast<uint32_t>(QdbMessage::Ok):
+ return QdbMessage::Ok;
+ }
+ return QdbMessage::Invalid;
+}
+
+QDataStream &operator>>(QDataStream &stream, QdbMessage &message)
+{
+ uint32_t command;
+ stream >> command;
+ message.setCommand(toCommandType(command));
+
+ StreamId hostStream;
+ stream >> hostStream;
+ message.setHostStream(hostStream);
+
+ StreamId deviceStream;
+ stream >> deviceStream;
+ message.setDeviceStream(deviceStream);
+
+ QByteArray data;
+ stream >> data;
+ message.setData(data);
+
+ return stream;
+}
+
+QDebug &operator<<(QDebug &stream, QdbMessage::CommandType command)
+{
+ switch (command) {
+ case QdbMessage::Invalid:
+ stream << "Invalid";
+ break;
+ case QdbMessage::Connect:
+ stream << "Connect";
+ break;
+ case QdbMessage::Open:
+ stream << "Open";
+ break;
+ case QdbMessage::Write:
+ stream << "Write";
+ break;
+ case QdbMessage::Close:
+ stream << "Close";
+ break;
+ case QdbMessage::Ok:
+ stream << "Ok";
+ break;
+ }
+ return stream;
+}
+
+QDebug &operator<<(QDebug &stream, const QdbMessage &message)
+{
+ stream << message.command() << message.hostStream() << message.deviceStream() <<
+ message.data();
+
+ return stream;
+}
diff --git a/libqdb/protocol/qdbmessage.h b/libqdb/protocol/qdbmessage.h
new file mode 100644
index 0000000..6c87635
--- /dev/null
+++ b/libqdb/protocol/qdbmessage.h
@@ -0,0 +1,78 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef QDBMESSAGE_H
+#define QDBMESSAGE_H
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qmetatype.h>
+
+class QDataStream;
+
+using StreamId = uint32_t;
+
+class QdbMessage
+{
+public:
+ /*! Get the amount of bytes of data in the message payload from the message header. */
+ static int GetDataSize(const QByteArray &header);
+
+ enum CommandType : uint32_t
+ {
+ Invalid = 0, // never sent
+ Connect = 0x434e584e, // CNXN
+ Open = 0x4f50454e, // OPEN
+ Write = 0x57525445, // WRTE
+ Close = 0x434c5345, // CLSE
+ Ok = 0x4f4b4159, // OKAY
+ };
+
+ QdbMessage();
+ QdbMessage(CommandType command, StreamId hostStream, StreamId deviceStream);
+ QdbMessage(CommandType command, StreamId hostStream, StreamId deviceStream, QByteArray data);
+ QdbMessage(CommandType command, StreamId hostStream, StreamId deviceStream, const char *data, int length);
+
+ CommandType command() const;
+ void setCommand(CommandType command);
+
+ StreamId hostStream() const;
+ void setHostStream(StreamId hostStream);
+
+ StreamId deviceStream() const;
+ void setDeviceStream(StreamId deviceStream);
+
+ const QByteArray &data() const;
+ void setData(const QByteArray &data);
+ void setData(const char *data, int length);
+
+private:
+ CommandType m_command;
+ StreamId m_hostStream;
+ StreamId m_deviceStream;
+ QByteArray m_data;
+};
+Q_DECLARE_METATYPE(QdbMessage::CommandType)
+
+QDebug &operator<<(QDebug &stream, const QdbMessage &message);
+
+QDataStream &operator<<(QDataStream &stream, const QdbMessage &message);
+QDataStream &operator>>(QDataStream &stream, QdbMessage &message);
+
+#endif // QDBMESSAGE_H
diff --git a/libqdb/protocol/qdbtransport.cpp b/libqdb/protocol/qdbtransport.cpp
new file mode 100644
index 0000000..d95432b
--- /dev/null
+++ b/libqdb/protocol/qdbtransport.cpp
@@ -0,0 +1,79 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#include "qdbtransport.h"
+
+#include "protocol/protocol.h"
+
+#include <QtCore/qdatastream.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qloggingcategory.h>
+
+Q_LOGGING_CATEGORY(transportC, "transport");
+
+QdbTransport::QdbTransport(QIODevice *io)
+ : m_io{io}
+{
+
+}
+
+QdbTransport::~QdbTransport()
+{
+
+}
+
+bool QdbTransport::open()
+{
+ connect(m_io.get(), &QIODevice::readyRead, this, &QdbTransport::messageAvailable, Qt::QueuedConnection);
+ return m_io->open(QIODevice::ReadWrite | QIODevice::Unbuffered);
+}
+
+bool QdbTransport::send(const QdbMessage &message)
+{
+ int messageSize = qdbHeaderSize + message.data().size();
+ QByteArray buf{messageSize, '\0'};
+ QDataStream stream{&buf, QIODevice::WriteOnly};
+ stream << message;
+
+ int count = m_io->write(buf.constData(), messageSize);
+ if (count != messageSize) {
+ qCritical() << "QdbTransport::send() could not write entire message of" << messageSize << ", only wrote" << count;
+ return false;
+ }
+
+ qCDebug(transportC) << "TX:" << message;
+ return true;
+}
+
+QdbMessage QdbTransport::receive()
+{
+ QByteArray buf{qdbMessageSize, '\0'};
+ int count = m_io->read(buf.data(), buf.size());
+ if (count < qdbHeaderSize) {
+ qDebug() << "QdbTransport::receive() could only read" << count << "out of package header's" << qdbHeaderSize;
+ return QdbMessage{QdbMessage::Invalid, 0, 0};
+ }
+ QDataStream stream{buf};
+ QdbMessage message;
+ stream >> message;
+ qCDebug(transportC) << "RX:" << message;
+
+ return message;
+}
diff --git a/libqdb/protocol/qdbtransport.h b/libqdb/protocol/qdbtransport.h
new file mode 100644
index 0000000..1fe91e4
--- /dev/null
+++ b/libqdb/protocol/qdbtransport.h
@@ -0,0 +1,52 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef QDBTRANSPORT_H
+#define QDBTRANSPORT_H
+
+#include "protocol/qdbmessage.h"
+
+#include <QtCore/qobject.h>
+
+#include <memory>
+
+class QIODevice;
+
+class QdbTransport : public QObject
+{
+ Q_OBJECT
+public:
+ /*! QdbTransport takes ownership of the passed QIODevice. */
+ QdbTransport(QIODevice *io);
+ ~QdbTransport();
+
+ bool open();
+
+ bool send(const QdbMessage &message);
+ QdbMessage receive();
+
+signals:
+ void messageAvailable();
+
+private:
+ std::unique_ptr<QIODevice> m_io;
+};
+
+#endif // QDBTRANSPORT_H
diff --git a/libqdb/protocol/services.h b/libqdb/protocol/services.h
new file mode 100644
index 0000000..9b07d62
--- /dev/null
+++ b/libqdb/protocol/services.h
@@ -0,0 +1,48 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef SERVICES_H
+#define SERVICES_H
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qdatastream.h>
+
+#include <cstdint>
+
+enum ServiceTag : uint32_t
+{
+ EchoTag = 1,
+ ProcessTag,
+ FilePushTag,
+ FilePullTag,
+};
+
+const int fileTransferBlockSize = 4096; // in bytes
+
+inline
+QByteArray tagBuffer(ServiceTag tag, int padding = 0)
+{
+ QByteArray buffer{static_cast<int>(sizeof(ServiceTag)) + padding, '\0'};
+ QDataStream stream{&buffer, QIODevice::WriteOnly};
+ stream << tag;
+ return buffer;
+}
+
+#endif // SERVICES_H
diff --git a/libqdb/stream.cpp b/libqdb/stream.cpp
new file mode 100644
index 0000000..940d16b
--- /dev/null
+++ b/libqdb/stream.cpp
@@ -0,0 +1,123 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#include "stream.h"
+
+#include "abstractconnection.h"
+#include "protocol/protocol.h"
+
+#include <QtCore/qdatastream.h>
+#include <QtCore/qdebug.h>
+
+QByteArray wrapPacket(const StreamPacket &packet)
+{
+ QByteArray buffer{packet.size() + static_cast<int>(sizeof(quint32)), '\0'};
+ QDataStream dataStream{&buffer, QIODevice::WriteOnly};
+ dataStream << packet.buffer();
+ return buffer;
+}
+
+Stream::Stream(AbstractConnection *connection, StreamId hostId, StreamId deviceId)
+ : m_connection{connection},
+ m_hostId{hostId},
+ m_deviceId{deviceId},
+ m_partlyReceived{false},
+ m_incomingSize{0},
+ m_incomingData{}
+{
+
+}
+
+bool Stream::write(const StreamPacket &packet)
+{
+ Q_ASSERT(packet.size() > 0); // writing nothing to the stream does not make sense
+ QByteArray data = wrapPacket(packet);
+ while (data.size() > 0) {
+ const int splitSize = std::min(data.size(), qdbMaxPayloadSize);
+ m_connection->enqueueMessage(QdbMessage{QdbMessage::Write, m_hostId, m_deviceId,
+ data.left(splitSize)});
+ data = data.mid(splitSize);
+ }
+ return true;
+}
+
+StreamId Stream::hostId() const
+{
+ return m_hostId;
+}
+
+StreamId Stream::deviceId() const
+{
+ return m_deviceId;
+}
+
+void Stream::requestClose()
+{
+ m_connection->enqueueMessage(QdbMessage{QdbMessage::Close, m_hostId, m_deviceId});
+}
+
+void Stream::close()
+{
+ emit closed();
+}
+
+void Stream::receiveMessage(const QdbMessage &message)
+{
+ Q_ASSERT(message.command() == QdbMessage::Write);
+ Q_ASSERT(message.hostStream() == m_hostId);
+ Q_ASSERT(message.deviceStream() == m_deviceId);
+
+ if (m_partlyReceived) {
+ const int missing = m_incomingSize - m_incomingData.size() - message.data().size();
+ Q_ASSERT_X(missing >= 0, "Stream::receiveMessage", "One QdbMessage must only contain data from a single Stream packet");
+
+ m_incomingData.append(message.data());
+ if (missing > 0)
+ return;
+ } else {
+ int packetSize = 0;
+ {
+ QDataStream dataStream{message.data()};
+ Q_ASSERT(message.data().size() >= static_cast<int>(sizeof(uint32_t)));
+ uint32_t size;
+ dataStream >> size;
+ packetSize = int(size);
+ }
+
+ const int dataSize = message.data().size() - static_cast<int>(sizeof(uint32_t));
+ Q_ASSERT_X(dataSize <= packetSize, "Stream::receiveMessage", "One QdbMessage must only contain data from a single Stream packet");
+
+ m_incomingData = message.data().mid(sizeof(uint32_t));
+ if (dataSize < packetSize) {
+ m_partlyReceived = true;
+ m_incomingSize = packetSize;
+ return;
+ }
+ }
+
+ StreamPacket packet{m_incomingData};
+
+ m_partlyReceived = false;
+ m_incomingSize = 0;
+ m_incomingData.clear();
+
+ // Emitted last because handling of the signal may lead to closing of stream
+ emit packetAvailable(packet);
+}
diff --git a/libqdb/stream.h b/libqdb/stream.h
new file mode 100644
index 0000000..e5eb391
--- /dev/null
+++ b/libqdb/stream.h
@@ -0,0 +1,60 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef STREAM_H
+#define STREAM_H
+
+class AbstractConnection;
+#include "protocol/qdbmessage.h"
+#include "streampacket.h"
+
+#include <QtCore/qobject.h>
+
+class Stream : public QObject
+{
+ Q_OBJECT
+public:
+ Stream(AbstractConnection *connection, StreamId hostId, StreamId deviceId);
+
+ bool write(const StreamPacket &packet);
+
+ StreamId hostId() const;
+ StreamId deviceId() const;
+
+ void requestClose();
+ // Should only be called by AbstractConnection, use requestClose() instead elsewhere
+ void close();
+signals:
+ void packetAvailable(StreamPacket data);
+ void closed();
+
+public slots:
+ void receiveMessage(const QdbMessage &message);
+
+private:
+ AbstractConnection *m_connection;
+ StreamId m_hostId;
+ StreamId m_deviceId;
+ bool m_partlyReceived;
+ int m_incomingSize;
+ QByteArray m_incomingData;
+};
+
+#endif // STREAM_H
diff --git a/libqdb/streampacket.cpp b/libqdb/streampacket.cpp
new file mode 100644
index 0000000..55a9b01
--- /dev/null
+++ b/libqdb/streampacket.cpp
@@ -0,0 +1,52 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#include "streampacket.h"
+
+StreamPacket::StreamPacket()
+ : m_buffer{},
+ m_dataStream{&m_buffer, QIODevice::WriteOnly}
+{
+
+}
+
+StreamPacket::StreamPacket(const QByteArray &data)
+ : m_buffer{data},
+ m_dataStream{m_buffer}
+{
+
+}
+
+StreamPacket::StreamPacket(const StreamPacket &other)
+ : m_buffer{other.buffer()},
+ m_dataStream{m_buffer}
+{
+
+}
+
+const QByteArray &StreamPacket::buffer() const
+{
+ return m_buffer;
+}
+
+int StreamPacket::size() const
+{
+ return m_buffer.size();
+}
diff --git a/libqdb/streampacket.h b/libqdb/streampacket.h
new file mode 100644
index 0000000..42934c8
--- /dev/null
+++ b/libqdb/streampacket.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef STREAMPACKET_H
+#define STREAMPACKET_H
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qdatastream.h>
+
+class StreamPacket
+{
+public:
+ //! Create a writable StreamPacket
+ StreamPacket();
+ StreamPacket(const StreamPacket &other);
+ StreamPacket& operator=(const StreamPacket &rhs) = delete;
+ //! Create a readable StreamPacket
+ explicit StreamPacket(const QByteArray &data);
+
+
+ const QByteArray &buffer() const;
+ int size() const;
+
+ template<typename T>
+ StreamPacket &operator<<(const T &value)
+ {
+ m_dataStream << value;
+ return *this;
+ }
+
+ template<typename T>
+ StreamPacket &operator>>(T &target)
+ {
+ m_dataStream >> target;
+ return *this;
+ }
+
+private:
+ QByteArray m_buffer;
+ QDataStream m_dataStream;
+};
+Q_DECLARE_METATYPE(StreamPacket);
+
+#endif // STREAMPACKET_H
diff --git a/libqdb/usb/usbconnection.cpp b/libqdb/usb/usbconnection.cpp
new file mode 100644
index 0000000..7564f3e
--- /dev/null
+++ b/libqdb/usb/usbconnection.cpp
@@ -0,0 +1,215 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#include "usbconnection.h"
+
+#include "../utils/make_unique.h"
+#include "../utils/scopeguard.h"
+#include "protocol/protocol.h"
+#include "usbconnectionreader.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qthread.h>
+
+#include <libusb.h>
+
+static const uint16_t vendorID = 0x18d1; // Google Inc.
+static const uint16_t productID = 0x0d02; // Celkon A88
+static const int inEndpointIndex = 1;
+static const int outEndpointIndex = 0;
+
+bool isQdbInterface(const libusb_interface &interface)
+{
+ const libusb_interface_descriptor *descriptor = &interface.altsetting[0];
+ return descriptor->bInterfaceClass == qdbUsbClassId && descriptor->bInterfaceSubClass == qdbUsbSubclassId;
+}
+
+UsbConnection::UsbConnection()
+ : m_context(nullptr),
+ m_handle(nullptr),
+ m_interfaceNumber(0),
+ m_inAddress(0),
+ m_outAddress(0),
+ m_readThread{nullptr},
+ m_reader{nullptr},
+ m_reads{}
+{
+}
+
+UsbConnection::~UsbConnection()
+{
+ if (m_readThread) {
+ m_readThread->quit();
+ m_readThread->wait();
+ }
+ if (m_handle) {
+ libusb_release_interface(m_handle, m_interfaceNumber);
+ libusb_close(m_handle);
+ }
+ if (m_context)
+ libusb_exit(m_context);
+}
+
+bool UsbConnection::open(OpenMode mode)
+{
+ Q_ASSERT(mode == (QIODevice::ReadWrite | QIODevice::Unbuffered));
+ QIODevice::open(mode);
+
+ libusb_device *found = NULL;
+
+ int ret = libusb_init(&m_context);
+ if (ret) {
+ qDebug("cannot init libusb: %s\n", libusb_error_name(ret));
+ return false;
+ }
+
+ libusb_device **devices;
+ ssize_t deviceCount = libusb_get_device_list(m_context, &devices);
+ ScopeGuard deviceListGuard = [&]() {
+ libusb_free_device_list(devices, 1);
+ };
+
+ if (deviceCount <= 0) {
+ qDebug("no devices found\n");
+ return false;
+ }
+
+ for (int i = 0; i < deviceCount; ++i) {
+ libusb_device *device = devices[i];
+ struct libusb_device_descriptor desc;
+ ret = libusb_get_device_descriptor(device, &desc);
+ if (ret) {
+ qDebug("unable to get device descriptor: %s\n",
+ libusb_error_name(ret));
+ return false;
+ }
+ if (desc.idVendor == vendorID && desc.idProduct == productID) {
+ qDebug("found QDB device");
+ found = device;
+ break;
+ }
+ }
+
+ if (!found) {
+ qDebug("no QDB devices found\n");
+ return false;
+ }
+
+ libusb_config_descriptor *config;
+ ret = libusb_get_active_config_descriptor(found, &config);
+ if (ret) {
+ qDebug("could not get config descriptor: %s\n",
+ libusb_error_name(ret));
+ return false;
+ }
+ ScopeGuard configGuard = [&]() {
+ libusb_free_config_descriptor(config);
+ };
+
+ auto last = config->interface + config->bNumInterfaces;
+ auto qdbInterface = std::find_if(config->interface, last, isQdbInterface);
+ if (qdbInterface == last) {
+ qDebug("no QDB interface found in device");
+ return false;
+ }
+
+ const libusb_interface_descriptor *interface = &qdbInterface->altsetting[0];
+ m_interfaceNumber = interface->bInterfaceNumber;
+ m_inAddress = interface->endpoint[inEndpointIndex].bEndpointAddress;
+ m_outAddress = interface->endpoint[outEndpointIndex].bEndpointAddress;
+
+ qDebug("interface %d is a qdb interface", m_interfaceNumber);
+
+ ret = libusb_open(found, &m_handle);
+ if (ret) {
+ qDebug("cannot open device: %s\n", libusb_error_name(ret));
+ return false;
+ }
+
+ libusb_set_auto_detach_kernel_driver(m_handle, 1);
+
+ ret = libusb_claim_interface(m_handle, m_interfaceNumber);
+ if (ret) {
+ qDebug("cannot claim interface: %s", libusb_error_name(ret));
+ return false;
+ }
+ qDebug("claimed interface %d", m_interfaceNumber);
+
+ startReader(m_handle, m_inAddress);
+
+ return true;
+}
+
+qint64 UsbConnection::readData(char *data, qint64 maxSize)
+{
+ if (m_reads.isEmpty()) {
+ qDebug() << "UsbConnection read queue empty in readData";
+ return -1;
+ }
+ QByteArray read = m_reads.dequeue();
+ Q_ASSERT(read.size() <= maxSize); // TODO: handle too big reads
+ std::copy(read.begin(), read.end(), data);
+
+ return read.size();
+}
+
+qint64 UsbConnection::writeData(const char *data, qint64 maxSize)
+{
+ // Send header as a separate transfer to allow separate read on device side
+ int size = maxSize > qdbHeaderSize ? qdbHeaderSize : maxSize;
+
+ int transferred = 0;
+ int ret = libusb_bulk_transfer(m_handle, m_outAddress, (unsigned char*)data, size, &transferred, 0);
+ if (ret) {
+ qDebug() << "writeData error:" << libusb_error_name(ret);
+ return -1;
+ }
+ Q_ASSERT(transferred == size); // TODO: handle partial transfers of header
+ transferred = 0;
+
+ if (size < maxSize) {
+ int rest = maxSize - size;
+ int ret = libusb_bulk_transfer(m_handle, m_outAddress, (unsigned char*)data + size, rest, &transferred, 0);
+ if (ret) {
+ qDebug() << "writeData error:" << libusb_error_name(ret);
+ return -1;
+ }
+ }
+ return size + transferred;
+}
+
+void UsbConnection::dataRead(QByteArray data)
+{
+ m_reads.enqueue(data);
+ emit readyRead();
+}
+
+void UsbConnection::startReader(libusb_device_handle *handle, uint8_t inAddress)
+{
+ m_readThread = make_unique<QThread>();
+ m_reader = make_unique<UsbConnectionReader>(handle, inAddress);
+
+ connect(m_reader.get(), &UsbConnectionReader::newRead, this, &UsbConnection::dataRead);
+ connect(m_readThread.get(), &QThread::started, m_reader.get(), &UsbConnectionReader::executeRead);
+ m_reader->moveToThread(m_readThread.get());
+
+ m_readThread->setObjectName("UsbConnectionReader");
+ m_readThread->start();
+}
diff --git a/libqdb/usb/usbconnection.h b/libqdb/usb/usbconnection.h
new file mode 100644
index 0000000..bdf2670
--- /dev/null
+++ b/libqdb/usb/usbconnection.h
@@ -0,0 +1,67 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef USBMANAGER_H
+#define USBMANAGER_H
+
+#include "libqdb_global.h"
+
+class UsbConnectionReader;
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qiodevice.h>
+class QThread;
+#include <QtCore/qqueue.h>
+
+#include <memory>
+
+struct libusb_context;
+struct libusb_device_handle;
+
+class LIBQDBSHARED_EXPORT UsbConnection : public QIODevice
+{
+ Q_OBJECT
+public:
+ UsbConnection();
+ ~UsbConnection();
+
+ bool open(QIODevice::OpenMode mode) override;
+
+protected:
+ qint64 readData(char *data, qint64 maxSize) override;
+ qint64 writeData(const char *data, qint64 maxSize) override;
+
+private slots:
+ void dataRead(QByteArray data);
+
+private:
+ void startReader(libusb_device_handle *handle, uint8_t inAddress);
+
+ libusb_context *m_context;
+ libusb_device_handle *m_handle;
+ uint8_t m_interfaceNumber;
+ uint8_t m_inAddress;
+ uint8_t m_outAddress;
+ std::unique_ptr<QThread> m_readThread;
+ std::unique_ptr<UsbConnectionReader> m_reader;
+ QQueue<QByteArray> m_reads;
+};
+
+#endif // USBMANAGER_H
diff --git a/libqdb/usb/usbconnectionreader.cpp b/libqdb/usb/usbconnectionreader.cpp
new file mode 100644
index 0000000..9313514
--- /dev/null
+++ b/libqdb/usb/usbconnectionreader.cpp
@@ -0,0 +1,56 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#include "usbconnectionreader.h"
+
+#include "protocol/protocol.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qtimer.h>
+
+#include <libusb.h>
+
+// Amount of milliseconds between yielding control to the event loop of the reading thread
+static const int quitCheckingTimeout = 500;
+
+UsbConnectionReader::UsbConnectionReader(libusb_device_handle *handle, uint8_t inAddress)
+ : m_handle{handle},
+ m_inAddress{inAddress}
+{
+
+}
+
+void UsbConnectionReader::executeRead()
+{
+ QByteArray buffer{qdbMessageSize, '\0'};
+ int transferred = 0;
+ int ret = libusb_bulk_transfer(m_handle, m_inAddress, reinterpret_cast<unsigned char *>(buffer.data()),
+ buffer.size(), &transferred, quitCheckingTimeout);
+ if (ret) {
+ // TODO: report errors?
+ if (ret != LIBUSB_ERROR_TIMEOUT)
+ qDebug() << "UsbConnectionReader error:" << libusb_error_name(ret);
+ } else {
+ buffer.resize(transferred);
+ emit newRead(buffer);
+ }
+ QTimer::singleShot(0, this, &UsbConnectionReader::executeRead);
+}
diff --git a/libqdb/usb/usbconnectionreader.h b/libqdb/usb/usbconnectionreader.h
new file mode 100644
index 0000000..041deca
--- /dev/null
+++ b/libqdb/usb/usbconnectionreader.h
@@ -0,0 +1,45 @@
+/******************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Debug Bridge.
+**
+** $QT_BEGIN_LICENSE:COMM$
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#ifndef USBCONNECTIONREADER_H
+#define USBCONNECTIONREADER_H
+
+#include <QtCore/qobject.h>
+
+class libusb_device_handle;
+
+class UsbConnectionReader : public QObject
+{
+ Q_OBJECT
+public:
+ UsbConnectionReader(libusb_device_handle *handle, uint8_t inAddress);
+
+signals:
+ void newRead(QByteArray data);
+
+public slots:
+ void executeRead();
+
+private:
+ libusb_device_handle *m_handle;
+ uint8_t m_inAddress;
+};
+
+#endif // USBCONNECTIONREADER_H