diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bluetooth/qlowenergycontroller_win.cpp | 126 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_win_p.h | 43 |
2 files changed, 140 insertions, 29 deletions
diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp index f7a1611f..27bfb7c7 100644 --- a/src/bluetooth/qlowenergycontroller_win.cpp +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -45,11 +45,10 @@ #include <QtCore/QIODevice> // for open modes #include <QtCore/QEvent> #include <QtCore/QMutex> +#include <QtCore/QThread> #include <algorithm> // for std::max -#include <windows/qwinlowenergybluetooth_p.h> - #include <setupapi.h> QT_BEGIN_NAMESPACE @@ -690,12 +689,22 @@ QLowEnergyControllerPrivateWin32::QLowEnergyControllerPrivateWin32() qCWarning(QT_BT_WINDOWS) << "LE is not supported on this OS"; return; } + + thread = new QThread; + threadWorker = new ThreadWorker; + threadWorker->moveToThread(thread); + connect(threadWorker, &ThreadWorker::jobFinished, this, &QLowEnergyControllerPrivateWin32::jobFinished); + connect(thread, &QThread::finished, threadWorker, &ThreadWorker::deleteLater); + connect(thread, &QThread::finished, thread, &QThread::deleteLater); + thread->start(); } QLowEnergyControllerPrivateWin32::~QLowEnergyControllerPrivateWin32() { QMutexLocker locker(&controllersGuard); qControllers()->removeAll(this); + if (thread) + thread->quit(); } void QLowEnergyControllerPrivateWin32::init() @@ -1020,17 +1029,20 @@ void QLowEnergyControllerPrivateWin32::writeCharacteristic( QLowEnergyService::WriteMode mode) { Q_ASSERT(!service.isNull()); - if (!service->characteristicList.contains(charHandle)) - return; - int systemErrorCode = NO_ERROR; + if (!service->characteristicList.contains(charHandle)) { + service->setError(QLowEnergyService::CharacteristicWriteError); + return; + } - const HANDLE hService = openSystemService( - remoteDevice, service->uuid, QIODevice::ReadWrite, &systemErrorCode); + WriteCharData data; + data.systemErrorCode = NO_ERROR; + data.hService = openSystemService( + remoteDevice, service->uuid, QIODevice::ReadWrite, &data.systemErrorCode); - if (systemErrorCode != NO_ERROR) { + if (data.systemErrorCode != NO_ERROR) { qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString() - << ":" << qt_error_string(systemErrorCode); + << ":" << qt_error_string(data.systemErrorCode); service->setError(QLowEnergyService::CharacteristicWriteError); return; } @@ -1038,35 +1050,58 @@ void QLowEnergyControllerPrivateWin32::writeCharacteristic( const QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle]; - BTH_LE_GATT_CHARACTERISTIC gattCharacteristic = recoverNativeLeGattCharacteristic( + data.gattCharacteristic = recoverNativeLeGattCharacteristic( service->startHandle, charHandle, charDetails); - const DWORD flags = (mode == QLowEnergyService::WriteWithResponse) + data.flags = (mode == QLowEnergyService::WriteWithResponse) ? BLUETOOTH_GATT_FLAG_NONE : BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE; - // TODO: If a device is not connected, this function will block - // for some time. So, need to re-implement of writeCharacteristic() - // with use QFutureWatcher. - setGattCharacteristicValue(hService, &gattCharacteristic, - newValue, flags, &systemErrorCode); - closeSystemDevice(hService); + ThreadWorkerJob job; + job.operation = ThreadWorkerJob::WriteChar; + data.newValue = newValue; + data.mode = mode; + job.data = QVariant::fromValue(data); - if (systemErrorCode != NO_ERROR) { - qCWarning(QT_BT_WINDOWS) << "Unable to set value for characteristic" - << charDetails.uuid.toString() - << "of the service" << service->uuid.toString() - << ":" << qt_error_string(systemErrorCode); - service->setError(QLowEnergyService::CharacteristicWriteError); - return; - } + QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection, + Q_ARG(ThreadWorkerJob, job)); +} - updateValueOfCharacteristic(charHandle, newValue, false); +void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job) +{ + switch (job.operation) { + case ThreadWorkerJob::WriteChar: + { + const WriteCharData data = job.data.value<WriteCharData>(); + closeSystemDevice(data.hService); + const QLowEnergyHandle charHandle = static_cast<QLowEnergyHandle>(data.gattCharacteristic.AttributeHandle); + const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle); + + if (data.systemErrorCode != NO_ERROR) { + const QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle]; + qCWarning(QT_BT_WINDOWS) << "Unable to set value for characteristic" + << charDetails.uuid.toString() + << "of the service" << service->uuid.toString() + << ":" << qt_error_string(data.systemErrorCode); + service->setError(QLowEnergyService::CharacteristicWriteError); + return; + } + + updateValueOfCharacteristic(charHandle, data.newValue, false); - if (mode == QLowEnergyService::WriteWithResponse) { - const QLowEnergyCharacteristic ch(service, charHandle); - emit service->characteristicWritten(ch, newValue); + if (data.mode == QLowEnergyService::WriteWithResponse) { + const QLowEnergyCharacteristic ch = characteristicForHandle(charHandle); + emit service->characteristicWritten(ch, data.newValue); + } + } + break; + case ThreadWorkerJob::ReadChar: + case ThreadWorkerJob::WriteDescr: + case ThreadWorkerJob::ReadDescr: + break; } + + QMetaObject::invokeMethod(threadWorker, "runPendingJob", Qt::QueuedConnection); } void QLowEnergyControllerPrivateWin32::readDescriptor( @@ -1219,4 +1254,37 @@ void QLowEnergyControllerPrivateWin32::addToGenericAttributeList(const QLowEnerg Q_UNIMPLEMENTED(); } +void ThreadWorker::putJob(const ThreadWorkerJob &job) +{ + m_jobs.append(job); + if (m_jobs.count() == 1) + runPendingJob(); +} + +void ThreadWorker::runPendingJob() +{ + if (!m_jobs.count()) + return; + + ThreadWorkerJob job = m_jobs.first(); + + switch (job.operation) { + case ThreadWorkerJob::WriteChar: + { + WriteCharData data = job.data.value<WriteCharData>(); + setGattCharacteristicValue(data.hService, &data.gattCharacteristic, + data.newValue, data.flags, &data.systemErrorCode); + job.data = QVariant::fromValue(data); + } + break; + case ThreadWorkerJob::ReadChar: + case ThreadWorkerJob::WriteDescr: + case ThreadWorkerJob::ReadDescr: + break; + } + + m_jobs.removeFirst(); + emit jobFinished(job); +} + QT_END_NAMESPACE diff --git a/src/bluetooth/qlowenergycontroller_win_p.h b/src/bluetooth/qlowenergycontroller_win_p.h index 9c2d4f92..64f824e6 100644 --- a/src/bluetooth/qlowenergycontroller_win_p.h +++ b/src/bluetooth/qlowenergycontroller_win_p.h @@ -59,8 +59,47 @@ #include "qlowenergycontroller.h" #include "qlowenergycontrollerbase_p.h" +#include <windows/qwinlowenergybluetooth_p.h> + QT_BEGIN_NAMESPACE +class QThread; +class QLowEnergyControllerPrivateWin32; + +class ThreadWorkerJob +{ +public: + enum Operation { WriteChar, ReadChar, WriteDescr, ReadDescr }; + Operation operation; + QVariant data; +}; + +Q_DECLARE_METATYPE(ThreadWorkerJob) + +struct WriteCharData +{ + QByteArray newValue; + QLowEnergyService::WriteMode mode; + HANDLE hService; + DWORD flags; + BTH_LE_GATT_CHARACTERISTIC gattCharacteristic; + int systemErrorCode; +}; + +Q_DECLARE_METATYPE(WriteCharData) + +class ThreadWorker : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE void putJob(const ThreadWorkerJob &job); + Q_INVOKABLE void runPendingJob(); +signals: + void jobFinished(const ThreadWorkerJob &job); +private: + QVector<ThreadWorkerJob> m_jobs; +}; + class QLowEnergyServiceData; extern void registerQLowEnergyControllerMetaType(); @@ -105,9 +144,13 @@ public: void addToGenericAttributeList(const QLowEnergyServiceData &service, QLowEnergyHandle startHandle) override; +public slots: + void jobFinished(const ThreadWorkerJob &job); protected: void customEvent(QEvent *e); private: + QThread *thread = nullptr; + ThreadWorker *threadWorker = nullptr; QString deviceSystemPath; }; |