diff options
author | Kari Oikarinen <kari.oikarinen@qt.io> | 2016-06-22 13:18:04 +0300 |
---|---|---|
committer | Kari Oikarinen <kari.oikarinen@qt.io> | 2016-07-14 11:57:38 +0000 |
commit | 3a6284c97ee1fb22f0bafe14f0f4d4ac2d8d2acf (patch) | |
tree | 259845e8b1c150fb5bb42e7dbcbe2d77a76d6650 /libqdb | |
parent | 174aad875aa79027dae885450ddd97d59cd27426 (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.cpp | 44 | ||||
-rw-r--r-- | libqdb/abstractconnection.h | 54 | ||||
-rw-r--r-- | libqdb/filepullcommon.h | 61 | ||||
-rw-r--r-- | libqdb/filepushcommon.h | 61 | ||||
-rw-r--r-- | libqdb/interruptsignalhandler.cpp | 82 | ||||
-rw-r--r-- | libqdb/interruptsignalhandler.h | 58 | ||||
-rw-r--r-- | libqdb/libqdb.pro | 42 | ||||
-rw-r--r-- | libqdb/libqdb_global.h | 32 | ||||
-rw-r--r-- | libqdb/processcommon.h | 61 | ||||
-rw-r--r-- | libqdb/protocol/protocol.h | 31 | ||||
-rw-r--r-- | libqdb/protocol/qdbmessage.cpp | 190 | ||||
-rw-r--r-- | libqdb/protocol/qdbmessage.h | 78 | ||||
-rw-r--r-- | libqdb/protocol/qdbtransport.cpp | 79 | ||||
-rw-r--r-- | libqdb/protocol/qdbtransport.h | 52 | ||||
-rw-r--r-- | libqdb/protocol/services.h | 48 | ||||
-rw-r--r-- | libqdb/stream.cpp | 123 | ||||
-rw-r--r-- | libqdb/stream.h | 60 | ||||
-rw-r--r-- | libqdb/streampacket.cpp | 52 | ||||
-rw-r--r-- | libqdb/streampacket.h | 61 | ||||
-rw-r--r-- | libqdb/usb/usbconnection.cpp | 215 | ||||
-rw-r--r-- | libqdb/usb/usbconnection.h | 67 | ||||
-rw-r--r-- | libqdb/usb/usbconnectionreader.cpp | 56 | ||||
-rw-r--r-- | libqdb/usb/usbconnectionreader.h | 45 |
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 |