summaryrefslogtreecommitdiffstats
path: root/src/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'src/bluetooth')
-rw-r--r--src/bluetooth/bluetooth.pro21
-rw-r--r--src/bluetooth/qbluetooth.cpp2
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent.h1
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_p.h45
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp564
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_p.h24
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_win.cpp262
-rw-r--r--src/bluetooth/qbluetoothserver_win.cpp109
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent.h5
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_p.h30
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_win.cpp452
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_win.cpp69
-rw-r--r--src/bluetooth/qbluetoothsocket.cpp4
-rw-r--r--src/bluetooth/qbluetoothsocket.h1
-rw-r--r--src/bluetooth/qbluetoothsocket_win.cpp587
-rw-r--r--src/bluetooth/qbluetoothsocket_win_p.h123
-rw-r--r--src/bluetooth/qbluetoothutils_win.cpp2
-rw-r--r--src/bluetooth/qlowenergycharacteristic.h1
-rw-r--r--src/bluetooth/qlowenergycontroller.cpp4
-rw-r--r--src/bluetooth/qlowenergycontroller_p.h1
-rw-r--r--src/bluetooth/qlowenergycontroller_win.cpp1346
-rw-r--r--src/bluetooth/qlowenergycontroller_win_p.h185
-rw-r--r--src/bluetooth/qlowenergydescriptor.h1
-rw-r--r--src/bluetooth/qlowenergyserviceprivate_p.h9
-rw-r--r--src/bluetooth/windows/qwinlowenergybluetooth_p.h231
-rw-r--r--src/bluetooth/windows/windows.pri2
26 files changed, 4076 insertions, 5 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index 2bcdee12..eedd4864 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -245,6 +245,27 @@ qtConfig(bluez) {
DEFINES += QT_WINRT_LIMITED_SERVICEDISCOVERY
DEFINES += QT_UCRTVERSION=$$WINDOWS_SDK_VERSION
}
+} else:win32 {
+ QT_PRIVATE = concurrent
+ DEFINES += QT_WIN_BLUETOOTH
+ LIBS += -lbthprops -lws2_32 -lsetupapi
+
+ include(windows/windows.pri)
+
+ # remove dummy warning once platform port is complete
+ include(dummy/dummy.pri)
+
+ SOURCES += \
+ qbluetoothdevicediscoveryagent_win.cpp \
+ qbluetoothlocaldevice_win.cpp \
+ qbluetoothserviceinfo_win.cpp \
+ qbluetoothservicediscoveryagent_win.cpp \
+ qbluetoothsocket_win.cpp \
+ qbluetoothserver_win.cpp \
+ qlowenergycontroller_win.cpp
+
+ PRIVATE_HEADERS += qlowenergycontroller_win_p.h \
+ qbluetoothsocket_win_p.h
} else {
message("Unsupported Bluetooth platform, will not build a working QtBluetooth library.")
message("Either no Qt D-Bus found or no BlueZ headers available.")
diff --git a/src/bluetooth/qbluetooth.cpp b/src/bluetooth/qbluetooth.cpp
index 6eab11fb..7b5fd266 100644
--- a/src/bluetooth/qbluetooth.cpp
+++ b/src/bluetooth/qbluetooth.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -101,6 +102,7 @@ namespace QBluetooth {
Q_LOGGING_CATEGORY(QT_BT, "qt.bluetooth")
Q_LOGGING_CATEGORY(QT_BT_ANDROID, "qt.bluetooth.android")
Q_LOGGING_CATEGORY(QT_BT_BLUEZ, "qt.bluetooth.bluez")
+Q_LOGGING_CATEGORY(QT_BT_WINDOWS, "qt.bluetooth.windows")
Q_LOGGING_CATEGORY(QT_BT_WINRT, "qt.bluetooth.winrt")
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.h b/src/bluetooth/qbluetoothdevicediscoveryagent.h
index 59a8b456..e566d895 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent.h
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
index ce31392f..97beced3 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -78,7 +79,20 @@ class QDBusVariant;
QT_END_NAMESPACE
#endif
-#ifdef QT_WINRT_BLUETOOTH
+#ifdef QT_WIN_BLUETOOTH
+QT_BEGIN_NAMESPACE
+class QThread;
+
+class ThreadWorkerDeviceDiscovery : public QObject
+{
+ Q_OBJECT
+signals:
+ void discoveryCompleted(const QVariant res);
+};
+
+QT_END_NAMESPACE
+
+#elif defined(QT_WINRT_BLUETOOTH)
#include <QtCore/QPointer>
#include <QtCore/QTimer>
#endif
@@ -90,7 +104,7 @@ class QWinRTBluetoothDeviceDiscoveryWorker;
#endif
class QBluetoothDeviceDiscoveryAgentPrivate
-#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH)
+#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) || defined(QT_WIN_BLUETOOTH)
: public QObject
{
Q_OBJECT
@@ -164,6 +178,33 @@ private:
QTimer extendedDiscoveryTimer;
#endif
+#ifdef QT_WIN_BLUETOOTH
+public:
+ static QString discoveredLeDeviceSystemPath(const QBluetoothAddress &deviceAddress);
+
+private:
+ void cancelDiscovery();
+ void restartDiscovery();
+ void finishDiscovery(QBluetoothDeviceDiscoveryAgent::Error errorCode, const QString &errorText);
+
+ void startLeDevicesDiscovery();
+ void completeLeDevicesDiscovery(const QVariant &res);
+ void startClassicDevicesDiscovery(Qt::HANDLE hSearch = nullptr);
+ void completeClassicDevicesDiscovery(const QVariant &res);
+
+ void processDiscoveredDevice(const QBluetoothDeviceInfo &foundDevice);
+
+ QBluetoothAddress adapterAddress;
+ bool pendingCancel;
+ bool pendingStart;
+ bool active;
+
+ QThread *threadLE = nullptr;
+ QThread *threadClassic = nullptr;
+ ThreadWorkerDeviceDiscovery *threadWorkerLE = nullptr;
+ ThreadWorkerDeviceDiscovery *threadWorkerClassic = nullptr;
+#endif
+
#ifdef QT_WINRT_BLUETOOTH
private slots:
void registerDevice(const QBluetoothDeviceInfo &info);
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp
new file mode 100644
index 00000000..7509c5f6
--- /dev/null
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_win.cpp
@@ -0,0 +1,564 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothdevicediscoveryagent.h"
+#include "qbluetoothuuid.h"
+#include "qbluetoothdevicediscoveryagent_p.h"
+#include "qbluetoothlocaldevice_p.h"
+
+#include <QtCore/qmutex.h>
+#include <QtCore/QThread>
+#include <QtCore/QLoggingCategory>
+
+#include <qt_windows.h>
+#include <setupapi.h>
+#include <bluetoothapis.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+struct LeDeviceEntry {
+ QString devicePath;
+ QBluetoothAddress deviceAddress;
+};
+
+Q_GLOBAL_STATIC(QVector<LeDeviceEntry>, cachedLeDeviceEntries)
+Q_GLOBAL_STATIC(QMutex, cachedLeDeviceEntriesGuard)
+
+static QString devicePropertyString(
+ HDEVINFO hDeviceInfo,
+ const PSP_DEVINFO_DATA deviceInfoData,
+ DWORD registryProperty)
+{
+ DWORD propertyRegDataType = 0;
+ DWORD propertyBufferSize = 0;
+ QByteArray propertyBuffer;
+
+ while (!::SetupDiGetDeviceRegistryProperty(
+ hDeviceInfo,
+ deviceInfoData,
+ registryProperty,
+ &propertyRegDataType,
+ propertyBuffer.isEmpty() ? nullptr : reinterpret_cast<PBYTE>(propertyBuffer.data()),
+ propertyBuffer.size(),
+ &propertyBufferSize)) {
+
+ const DWORD error = ::GetLastError();
+ if (error != ERROR_INSUFFICIENT_BUFFER
+ || (propertyRegDataType != REG_SZ
+ && propertyRegDataType != REG_EXPAND_SZ)) {
+ return QString();
+ }
+
+ // add +2 byte to allow to successfully convert to qstring
+ propertyBuffer.fill(0, propertyBufferSize + sizeof(wchar_t));
+ }
+
+ return QString::fromWCharArray(reinterpret_cast<const wchar_t *>(
+ propertyBuffer.constData()));
+}
+
+static QString deviceName(HDEVINFO hDeviceInfo, PSP_DEVINFO_DATA deviceInfoData)
+{
+ return devicePropertyString(hDeviceInfo, deviceInfoData, SPDRP_FRIENDLYNAME);
+}
+
+static QString deviceSystemPath(const PSP_INTERFACE_DEVICE_DETAIL_DATA detailData)
+{
+ return QString::fromWCharArray(detailData->DevicePath);
+}
+
+static QBluetoothAddress deviceAddress(const QString &devicePath)
+{
+ const int firstbound = devicePath.indexOf(QStringLiteral("dev_"));
+ const int lastbound = devicePath.indexOf(QLatin1Char('#'), firstbound);
+ const QString hex = devicePath.mid(firstbound + 4, lastbound - firstbound - 4);
+ bool ok = false;
+ return QBluetoothAddress(hex.toULongLong(&ok, 16));
+}
+
+static QBluetoothDeviceInfo createClassicDeviceInfo(const BLUETOOTH_DEVICE_INFO &foundDevice)
+{
+ QBluetoothDeviceInfo deviceInfo(
+ QBluetoothAddress(foundDevice.Address.ullLong),
+ QString::fromWCharArray(foundDevice.szName),
+ foundDevice.ulClassofDevice);
+
+ deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration);
+
+ if (foundDevice.fRemembered)
+ deviceInfo.setCached(true);
+ return deviceInfo;
+}
+
+static QBluetoothDeviceInfo findFirstClassicDevice(
+ DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND *hSearch)
+{
+ BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = {0};
+ searchParams.dwSize = sizeof(searchParams);
+ searchParams.cTimeoutMultiplier = 10; // 12.8 sec
+ searchParams.fIssueInquiry = TRUE;
+ searchParams.fReturnAuthenticated = TRUE;
+ searchParams.fReturnConnected = TRUE;
+ searchParams.fReturnRemembered = TRUE;
+ searchParams.fReturnUnknown = TRUE;
+ searchParams.hRadio = nullptr;
+
+ BLUETOOTH_DEVICE_INFO deviceInfo = {0};
+ deviceInfo.dwSize = sizeof(deviceInfo);
+
+ const HBLUETOOTH_DEVICE_FIND hFind = ::BluetoothFindFirstDevice(
+ &searchParams, &deviceInfo);
+
+ QBluetoothDeviceInfo foundDevice;
+ if (hFind) {
+ *hSearch = hFind;
+ *systemErrorCode = NO_ERROR;
+ foundDevice = createClassicDeviceInfo(deviceInfo);
+ } else {
+ *systemErrorCode = ::GetLastError();
+ }
+
+ return foundDevice;
+}
+
+static QBluetoothDeviceInfo findNextClassicDevice(
+ DWORD *systemErrorCode, HBLUETOOTH_DEVICE_FIND hSearch)
+{
+ BLUETOOTH_DEVICE_INFO deviceInfo = {0};
+ deviceInfo.dwSize = sizeof(deviceInfo);
+
+ QBluetoothDeviceInfo foundDevice;
+ if (!::BluetoothFindNextDevice(hSearch, &deviceInfo)) {
+ *systemErrorCode = ::GetLastError();
+ } else {
+ *systemErrorCode = NO_ERROR;
+ foundDevice = createClassicDeviceInfo(deviceInfo);
+ }
+
+ return foundDevice;
+}
+
+static void closeClassicSearch(HBLUETOOTH_DEVICE_FIND hSearch)
+{
+ if (hSearch)
+ ::BluetoothFindDeviceClose(hSearch);
+}
+
+static QVector<QBluetoothDeviceInfo> enumerateLeDevices(
+ DWORD *systemErrorCode)
+{
+ const QUuid deviceInterfaceGuid("781aee18-7733-4ce4-add0-91f41c67b592");
+ const HDEVINFO hDeviceInfo = ::SetupDiGetClassDevs(
+ reinterpret_cast<const GUID *>(&deviceInterfaceGuid),
+ nullptr,
+ nullptr,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+
+ if (hDeviceInfo == INVALID_HANDLE_VALUE) {
+ *systemErrorCode = ::GetLastError();
+ return QVector<QBluetoothDeviceInfo>();
+ }
+
+ QVector<QBluetoothDeviceInfo> foundDevices;
+ DWORD index = 0;
+
+ QVector<LeDeviceEntry> cachedEntries;
+
+ for (;;) {
+ SP_DEVICE_INTERFACE_DATA deviceInterfaceData = {0};
+ deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
+
+ if (!::SetupDiEnumDeviceInterfaces(
+ hDeviceInfo,
+ nullptr,
+ reinterpret_cast<const GUID *>(&deviceInterfaceGuid),
+ index++,
+ &deviceInterfaceData)) {
+ *systemErrorCode = ::GetLastError();
+ break;
+ }
+
+ DWORD deviceInterfaceDetailDataSize = 0;
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ hDeviceInfo,
+ &deviceInterfaceData,
+ nullptr,
+ deviceInterfaceDetailDataSize,
+ &deviceInterfaceDetailDataSize,
+ nullptr)) {
+ if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ *systemErrorCode = ::GetLastError();
+ break;
+ }
+ }
+
+ SP_DEVINFO_DATA deviceInfoData = {0};
+ deviceInfoData.cbSize = sizeof(deviceInfoData);
+
+ QByteArray deviceInterfaceDetailDataBuffer(
+ deviceInterfaceDetailDataSize, 0);
+
+ PSP_INTERFACE_DEVICE_DETAIL_DATA deviceInterfaceDetailData =
+ reinterpret_cast<PSP_INTERFACE_DEVICE_DETAIL_DATA>
+ (deviceInterfaceDetailDataBuffer.data());
+
+ deviceInterfaceDetailData->cbSize =
+ sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
+
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ hDeviceInfo,
+ &deviceInterfaceData,
+ deviceInterfaceDetailData,
+ deviceInterfaceDetailDataBuffer.size(),
+ &deviceInterfaceDetailDataSize,
+ &deviceInfoData)) {
+ *systemErrorCode = ::GetLastError();
+ break;
+ }
+
+ const QString systemPath = deviceSystemPath(deviceInterfaceDetailData);
+ const QBluetoothAddress address = deviceAddress(systemPath);
+ if (address.isNull())
+ continue;
+ const QString name = deviceName(hDeviceInfo, &deviceInfoData);
+
+ QBluetoothDeviceInfo deviceInfo(address, name, QBluetoothDeviceInfo::MiscellaneousDevice);
+ deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
+ deviceInfo.setCached(true);
+
+ foundDevices << deviceInfo;
+ cachedEntries << LeDeviceEntry{systemPath, address};
+ }
+
+ QMutexLocker locker(cachedLeDeviceEntriesGuard());
+ cachedLeDeviceEntries()->swap(cachedEntries);
+
+ ::SetupDiDestroyDeviceInfoList(hDeviceInfo);
+ return foundDevices;
+}
+
+struct DiscoveryResult {
+ QVector<QBluetoothDeviceInfo> devices;
+ DWORD systemErrorCode;
+ HBLUETOOTH_DEVICE_FIND hSearch; // Used only for classic devices
+};
+
+QString QBluetoothDeviceDiscoveryAgentPrivate::discoveredLeDeviceSystemPath(
+ const QBluetoothAddress &deviceAddress)
+{
+ // update LE devices cache
+ DWORD dummyErrorCode;
+ enumerateLeDevices(&dummyErrorCode);
+
+ QMutexLocker locker(cachedLeDeviceEntriesGuard());
+ for (const LeDeviceEntry &e: *cachedLeDeviceEntries) {
+ if (e.deviceAddress == deviceAddress)
+ return e.devicePath;
+ }
+ return QString();
+}
+
+QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(
+ const QBluetoothAddress &deviceAdapter,
+ QBluetoothDeviceDiscoveryAgent *parent)
+ : inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry)
+ , lastError(QBluetoothDeviceDiscoveryAgent::NoError)
+ , adapterAddress(deviceAdapter)
+ , pendingCancel(false)
+ , pendingStart(false)
+ , active(false)
+ , lowEnergySearchTimeout(-1) // remains -1 -> timeout not supported
+ , q_ptr(parent)
+{
+ threadLE = new QThread;
+ threadWorkerLE = new ThreadWorkerDeviceDiscovery;
+ threadWorkerLE->moveToThread(threadLE);
+ connect(threadWorkerLE, &ThreadWorkerDeviceDiscovery::discoveryCompleted, this, &QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery);
+ connect(threadLE, &QThread::finished, threadWorkerLE, &ThreadWorkerDeviceDiscovery::deleteLater);
+ connect(threadLE, &QThread::finished, threadLE, &QThread::deleteLater);
+ threadLE->start();
+
+ threadClassic = new QThread;
+ threadWorkerClassic = new ThreadWorkerDeviceDiscovery;
+ threadWorkerClassic->moveToThread(threadClassic);
+ connect(threadWorkerClassic, &ThreadWorkerDeviceDiscovery::discoveryCompleted, this, &QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery);
+ connect(threadClassic, &QThread::finished, threadWorkerClassic, &ThreadWorkerDeviceDiscovery::deleteLater);
+ connect(threadClassic, &QThread::finished, threadClassic, &QThread::deleteLater);
+ threadClassic->start();
+}
+
+QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
+{
+ if (active)
+ stop();
+ if (threadLE)
+ threadLE->quit();
+ if (threadClassic)
+ threadClassic->quit();
+}
+
+bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const
+{
+ if (pendingStart)
+ return true;
+ if (pendingCancel)
+ return false;
+ return active;
+}
+
+QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods()
+{
+ return (LowEnergyMethod | ClassicMethod);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods)
+{
+ requestedMethods = methods;
+
+ if (pendingCancel == true) {
+ pendingStart = true;
+ return;
+ }
+
+ const QList<QBluetoothHostInfo> foundLocalAdapters =
+ QBluetoothLocalDevicePrivate::localAdapters();
+
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+
+ if (foundLocalAdapters.isEmpty()) {
+ qCWarning(QT_BT_WINDOWS) << "Device does not support Bluetooth";
+ lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError;
+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Device does not support Bluetooth");
+ emit q->error(lastError);
+ return;
+ }
+
+ // Check for the local adapter address.
+ auto equals = [this](const QBluetoothHostInfo &adapterInfo) {
+ return adapterAddress == QBluetoothAddress()
+ || adapterAddress == adapterInfo.address();
+ };
+ const auto end = foundLocalAdapters.cend();
+ const auto it = std::find_if(foundLocalAdapters.cbegin(), end, equals);
+ if (it == end) {
+ qCWarning(QT_BT_WINDOWS) << "Incorrect local adapter passed.";
+ lastError = QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError;
+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Passed address is not a local device.");
+ emit q->error(lastError);
+ return;
+ }
+
+ discoveredDevices.clear();
+ active = true;
+
+ // We run LE search first, as it is fast on windows.
+ if (requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)
+ startLeDevicesDiscovery();
+ else if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod)
+ startClassicDevicesDiscovery();
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::stop()
+{
+ if (!active)
+ return;
+
+ pendingCancel = true;
+ pendingStart = false;
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::cancelDiscovery()
+{
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+ active = false;
+ pendingCancel = false;
+ emit q->canceled();
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::restartDiscovery()
+{
+ pendingStart = false;
+ pendingCancel = false;
+ start(requestedMethods);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::finishDiscovery(QBluetoothDeviceDiscoveryAgent::Error errorCode, const QString &errorText)
+{
+ active = false;
+ pendingStart = false;
+ pendingCancel = false;
+ lastError = errorCode;
+ errorString = errorText;
+
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+ if (errorCode == QBluetoothDeviceDiscoveryAgent::NoError)
+ emit q->finished();
+ else
+ emit q->error(lastError);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::startLeDevicesDiscovery()
+{
+ const auto threadWorker = threadWorkerLE;
+ QMetaObject::invokeMethod(threadWorkerLE, [threadWorker]()
+ {
+ DiscoveryResult result; // Do not use hSearch here!
+ result.systemErrorCode = NO_ERROR;
+ result.devices = enumerateLeDevices(&result.systemErrorCode);
+ emit threadWorker->discoveryCompleted(QVariant::fromValue(result));
+ }, Qt::QueuedConnection);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::completeLeDevicesDiscovery(const QVariant &res)
+{
+ if (pendingCancel && !pendingStart) {
+ cancelDiscovery();
+ } else if (pendingStart) {
+ restartDiscovery();
+ } else {
+ const DiscoveryResult result = res.value<DiscoveryResult>();
+ if (result.systemErrorCode == NO_ERROR || result.systemErrorCode == ERROR_NO_MORE_ITEMS) {
+ for (const QBluetoothDeviceInfo &device : result.devices)
+ processDiscoveredDevice(device);
+
+ // We run classic search at second, as it is slow on windows.
+ if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod)
+ startClassicDevicesDiscovery();
+ else
+ finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, qt_error_string(NO_ERROR));
+ } else {
+ const QBluetoothDeviceDiscoveryAgent::Error error = (result.systemErrorCode == ERROR_INVALID_HANDLE)
+ ? QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError
+ : QBluetoothDeviceDiscoveryAgent::InputOutputError;
+ finishDiscovery(error, qt_error_string(result.systemErrorCode));
+ }
+ }
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::startClassicDevicesDiscovery(Qt::HANDLE hSearch)
+{
+ const auto threadWorker = threadWorkerClassic;
+ QMetaObject::invokeMethod(threadWorker, [threadWorker, hSearch]()
+ {
+ DiscoveryResult result;
+ result.hSearch = hSearch;
+ result.systemErrorCode = NO_ERROR;
+
+ const QBluetoothDeviceInfo device = hSearch
+ ? findNextClassicDevice(&result.systemErrorCode, result.hSearch)
+ : findFirstClassicDevice(&result.systemErrorCode, &result.hSearch);
+
+ result.devices.append(device);
+ emit threadWorker->discoveryCompleted(QVariant::fromValue(result));
+ }, Qt::QueuedConnection);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::completeClassicDevicesDiscovery(const QVariant &res)
+{
+ const DiscoveryResult result = res.value<DiscoveryResult>();
+ if (pendingCancel && !pendingStart) {
+ closeClassicSearch(result.hSearch);
+ cancelDiscovery();
+ } else if (pendingStart) {
+ closeClassicSearch(result.hSearch);
+ restartDiscovery();
+ } else {
+ if (result.systemErrorCode == ERROR_NO_MORE_ITEMS) {
+ closeClassicSearch(result.hSearch);
+ finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, QString());
+ } else if (result.systemErrorCode == NO_ERROR) {
+ if (result.hSearch) {
+ for (const QBluetoothDeviceInfo &device : result.devices)
+ processDiscoveredDevice(device);
+
+ startClassicDevicesDiscovery(result.hSearch);
+ } else {
+ finishDiscovery(QBluetoothDeviceDiscoveryAgent::NoError, QString());
+ }
+ } else {
+ closeClassicSearch(result.hSearch);
+ const QBluetoothDeviceDiscoveryAgent::Error error = (result.systemErrorCode == ERROR_INVALID_HANDLE)
+ ? QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError
+ : QBluetoothDeviceDiscoveryAgent::InputOutputError;
+ finishDiscovery(error, qt_error_string(result.systemErrorCode));
+ }
+ }
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevice(
+ const QBluetoothDeviceInfo &foundDevice)
+{
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+
+ auto equalAddress = [foundDevice](const QBluetoothDeviceInfo &targetDevice) {
+ return foundDevice.address() == targetDevice.address(); };
+ auto end = discoveredDevices.end();
+ auto deviceIt = std::find_if(discoveredDevices.begin(), end, equalAddress);
+ if (deviceIt == end) {
+ qCDebug(QT_BT_WINDOWS) << "Emit: " << foundDevice.address();
+ discoveredDevices.append(foundDevice);
+ emit q->deviceDiscovered(foundDevice);
+ } else if (*deviceIt == foundDevice
+ || deviceIt->coreConfigurations() == foundDevice.coreConfigurations()) {
+ qCDebug(QT_BT_WINDOWS) << "Duplicate: " << foundDevice.address();
+ } else {
+ // We assume that if the existing device it is low energy, it means that
+ // the found device should be as classic, because it is impossible to get
+ // same low energy device.
+ if (deviceIt->coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration)
+ *deviceIt = foundDevice;
+
+ // We assume that it is impossible to have multiple devices with same core
+ // configurations, which have one address. This possible only in case a device
+ // provided both low energy and classic features at the same time.
+ deviceIt->setCoreConfigurations(QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration);
+ deviceIt->setCached(foundDevice.isCached());
+
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+ qCDebug(QT_BT_WINDOWS) << "Updated: " << deviceIt->address();
+ emit q->deviceDiscovered(*deviceIt);
+ }
+}
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(DiscoveryResult)
diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h
index 98c62151..e18169f9 100644
--- a/src/bluetooth/qbluetoothlocaldevice_p.h
+++ b/src/bluetooth/qbluetoothlocaldevice_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -208,6 +209,29 @@ private:
void initializeAdapter();
void initializeAdapterBluez5();
};
+
+#elif defined(QT_WIN_BLUETOOTH)
+
+class QBluetoothLocalDevicePrivate : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PUBLIC(QBluetoothLocalDevice)
+public:
+ QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q,
+ const QBluetoothAddress &address = QBluetoothAddress());
+
+ ~QBluetoothLocalDevicePrivate();
+ bool isValid() const;
+ void initialize(const QBluetoothAddress &address);
+
+ static QList<QBluetoothHostInfo> localAdapters();
+
+ QBluetoothAddress deviceAddress;
+ QString deviceName;
+ bool deviceValid;
+private:
+ QBluetoothLocalDevice *q_ptr;
+};
#elif !defined(QT_OSX_BLUETOOTH) // winrt and dummy backend
class QBluetoothLocalDevicePrivate : public QObject
{
diff --git a/src/bluetooth/qbluetoothlocaldevice_win.cpp b/src/bluetooth/qbluetoothlocaldevice_win.cpp
new file mode 100644
index 00000000..a3f3c339
--- /dev/null
+++ b/src/bluetooth/qbluetoothlocaldevice_win.cpp
@@ -0,0 +1,262 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothlocaldevice.h"
+#include "qbluetoothaddress.h"
+
+#include "qbluetoothlocaldevice_p.h"
+
+#include <QtCore/QLoggingCategory>
+
+#include <qt_windows.h>
+#include <bluetoothapis.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) :
+ QObject(parent),
+ d_ptr(new QBluetoothLocalDevicePrivate(this))
+{
+}
+
+QBluetoothLocalDevice::QBluetoothLocalDevice(
+ const QBluetoothAddress &address, QObject *parent) :
+ QObject(parent),
+ d_ptr(new QBluetoothLocalDevicePrivate(this, address))
+{
+}
+
+QString QBluetoothLocalDevice::name() const
+{
+ Q_D(const QBluetoothLocalDevice);
+ return d->deviceName;
+}
+
+QBluetoothAddress QBluetoothLocalDevice::address() const
+{
+ Q_D(const QBluetoothLocalDevice);
+ return d->deviceAddress;
+}
+
+void QBluetoothLocalDevice::powerOn()
+{
+ if (hostMode() != HostPoweredOff)
+ return;
+
+ setHostMode(HostConnectable);
+}
+
+void QBluetoothLocalDevice::setHostMode(
+ QBluetoothLocalDevice::HostMode requestedMode)
+{
+ if (!isValid()) {
+ qCWarning(QT_BT_WINDOWS) << "The local device is not initialized correctly";
+ return;
+ }
+
+ if (requestedMode == HostDiscoverableLimitedInquiry)
+ requestedMode = HostDiscoverable;
+
+ if (requestedMode == hostMode())
+ return;
+
+ if (requestedMode == QBluetoothLocalDevice::HostPoweredOff) {
+ if (::BluetoothIsDiscoverable(NULL)
+ && !::BluetoothEnableDiscovery(NULL, FALSE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to disable the discoverable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ if (::BluetoothIsConnectable(NULL)
+ && !::BluetoothEnableIncomingConnections(NULL, FALSE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to disable the connectable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ } else if (requestedMode == QBluetoothLocalDevice::HostConnectable) {
+ if (::BluetoothIsDiscoverable(NULL)) {
+ if (!::BluetoothEnableDiscovery(NULL, FALSE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to disable the discoverable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ } else if (!::BluetoothEnableIncomingConnections(NULL, TRUE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to enable the connectable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ } else if (requestedMode == QBluetoothLocalDevice::HostDiscoverable
+ || requestedMode == QBluetoothLocalDevice::HostDiscoverableLimitedInquiry) {
+ if (!::BluetoothIsConnectable(NULL)
+ && !::BluetoothEnableIncomingConnections(NULL, TRUE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to enable the connectable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ if (!::BluetoothEnableDiscovery(NULL, TRUE)) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to enable the discoverable mode";
+ emit error(QBluetoothLocalDevice::UnknownError);
+ return;
+ }
+ }
+
+ emit hostModeStateChanged(requestedMode);
+}
+
+QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const
+{
+ if (!isValid()) {
+ qCWarning(QT_BT_WINDOWS) << "The local device is not initialized correctly";
+ return HostPoweredOff;
+ }
+
+ if (::BluetoothIsDiscoverable(NULL))
+ return HostDiscoverable;
+ if (::BluetoothIsConnectable(NULL))
+ return HostConnectable;
+ return HostPoweredOff;
+}
+
+QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
+{
+ return QList<QBluetoothAddress>();
+}
+
+QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
+{
+ return QBluetoothLocalDevicePrivate::localAdapters();
+}
+
+void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing)
+{
+ Q_UNUSED(address);
+ Q_UNUSED(pairing);
+}
+
+QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(
+ const QBluetoothAddress &address) const
+{
+ Q_UNUSED(address);
+ return Unpaired;
+}
+
+void QBluetoothLocalDevice::pairingConfirmation(bool confirmation)
+{
+ Q_UNUSED(confirmation);
+}
+
+QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(
+ QBluetoothLocalDevice *q, const QBluetoothAddress &address)
+ : deviceValid(false)
+ , q_ptr(q)
+{
+ initialize(address);
+}
+
+QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate()
+{
+}
+
+bool QBluetoothLocalDevicePrivate::isValid() const
+{
+ return deviceValid;
+}
+
+void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address)
+{
+ Q_Q(QBluetoothLocalDevice);
+
+ const QList<QBluetoothHostInfo> adapterInfos = QBluetoothLocalDevicePrivate::localAdapters();
+ for (const QBluetoothHostInfo &adapterInfo : adapterInfos) {
+ if (address == QBluetoothAddress()
+ || address == adapterInfo.address()) {
+ deviceAddress = adapterInfo.address();
+ deviceName = adapterInfo.name();
+ deviceValid = true;
+ return;
+ }
+ }
+
+ qCWarning(QT_BT_WINDOWS) << "Unable to find classic local radio: " << address;
+ QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection,
+ Q_ARG(QBluetoothLocalDevice::Error,
+ QBluetoothLocalDevice::UnknownError));
+}
+
+QList<QBluetoothHostInfo> QBluetoothLocalDevicePrivate::localAdapters()
+{
+ BLUETOOTH_FIND_RADIO_PARAMS params;
+ ::ZeroMemory(&params, sizeof(params));
+ params.dwSize = sizeof(params);
+
+ QList<QBluetoothHostInfo> foundAdapters;
+
+ HANDLE hRadio = 0;
+ if (const HBLUETOOTH_RADIO_FIND hSearch = ::BluetoothFindFirstRadio(&params, &hRadio)) {
+ for (;;) {
+ BLUETOOTH_RADIO_INFO radio;
+ ::ZeroMemory(&radio, sizeof(radio));
+ radio.dwSize = sizeof(radio);
+
+ const DWORD retval = ::BluetoothGetRadioInfo(hRadio, &radio);
+ ::CloseHandle(hRadio);
+
+ if (retval != ERROR_SUCCESS)
+ break;
+
+ QBluetoothHostInfo adapterInfo;
+ adapterInfo.setAddress(QBluetoothAddress(radio.address.ullLong));
+ adapterInfo.setName(QString::fromWCharArray(radio.szName));
+
+ foundAdapters << adapterInfo;
+
+ if (!::BluetoothFindNextRadio(hSearch, &hRadio))
+ break;
+ }
+
+ ::BluetoothFindRadioClose(hSearch);
+ }
+
+ return foundAdapters;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothserver_win.cpp b/src/bluetooth/qbluetoothserver_win.cpp
new file mode 100644
index 00000000..a9f8659e
--- /dev/null
+++ b/src/bluetooth/qbluetoothserver_win.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothserver.h"
+#include "qbluetoothserver_p.h"
+#include "qbluetoothsocket.h"
+
+QT_BEGIN_NAMESPACE
+
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType)
+ : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError)
+{
+ if (sType == QBluetoothServiceInfo::RfcommProtocol)
+ socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
+ else
+ socket = new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol);
+}
+
+QBluetoothServerPrivate::~QBluetoothServerPrivate()
+{
+ delete socket;
+}
+
+void QBluetoothServer::close()
+{
+}
+
+bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
+{
+ Q_UNUSED(address);
+ Q_UNUSED(port);
+ Q_D(QBluetoothServer);
+ d->m_lastError = UnsupportedProtocolError;
+ emit error(d->m_lastError);
+ return false;
+}
+
+void QBluetoothServer::setMaxPendingConnections(int numConnections)
+{
+ Q_UNUSED(numConnections);
+}
+
+bool QBluetoothServer::hasPendingConnections() const
+{
+ return false;
+}
+
+QBluetoothSocket *QBluetoothServer::nextPendingConnection()
+{
+ return 0;
+}
+
+QBluetoothAddress QBluetoothServer::serverAddress() const
+{
+ return QBluetoothAddress();
+}
+
+quint16 QBluetoothServer::serverPort() const
+{
+ return 0;
+}
+
+void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security)
+{
+ Q_UNUSED(security);
+}
+
+QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const
+{
+ return QBluetooth::NoSecurity;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.h b/src/bluetooth/qbluetoothservicediscoveryagent.h
index 1db05f55..8533ab72 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent.h
@@ -43,6 +43,7 @@
#include <QtBluetooth/qtbluetoothglobal.h>
#include <QtCore/QObject>
+#include <QtCore/QVariant>
#include <QtBluetooth/QBluetoothServiceInfo>
#include <QtBluetooth/QBluetoothUuid>
@@ -110,6 +111,10 @@ Q_SIGNALS:
private:
QBluetoothServiceDiscoveryAgentPrivate *d_ptr;
+
+#ifdef QT_WIN_BLUETOOTH
+ Q_PRIVATE_SLOT(d_func(), void _q_nextSdpScan(QVariant input))
+#endif
};
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
index dbf8b1d4..4a522826 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
@@ -72,7 +72,21 @@ class QXmlStreamReader;
QT_END_NAMESPACE
#endif
-#ifdef QT_WINRT_BLUETOOTH
+#ifdef QT_WIN_BLUETOOTH
+#include <QtCore/QVariant>
+
+QT_BEGIN_NAMESPACE
+class QThread;
+
+class ThreadWorkerFind : public QObject
+{
+ Q_OBJECT
+signals:
+ void findFinished(QVariant result);
+};
+QT_END_NAMESPACE
+
+#elif defined(QT_WINRT_BLUETOOTH)
#include <QtCore/QPointer>
#endif
@@ -91,7 +105,7 @@ class QWinRTBluetoothServiceDiscoveryWorker;
#endif
class QBluetoothServiceDiscoveryAgentPrivate
-#if defined QT_WINRT_BLUETOOTH
+#if defined QT_WINRT_BLUETOOTH || defined QT_WIN_BLUETOOTH
: public QObject
{
Q_OBJECT
@@ -149,6 +163,9 @@ public:
void _q_fetchUuidsTimeout();
void _q_hostModeStateChanged(QBluetoothLocalDevice::HostMode state);
#endif
+#ifdef QT_WIN_BLUETOOTH
+ void _q_nextSdpScan(const QVariant &input);
+#endif
private:
void start(const QBluetoothAddress &address);
@@ -200,6 +217,15 @@ private:
QMap<QBluetoothAddress,QPair<QBluetoothDeviceInfo,QList<QBluetoothUuid> > > sdpCache;
#endif
+#ifdef QT_WIN_BLUETOOTH
+private:
+ bool pendingStop;
+ bool pendingFinish;
+
+ QThread *threadFind = nullptr;
+ ThreadWorkerFind *threadWorkerFind = nullptr;
+#endif
+
#ifdef QT_WINRT_BLUETOOTH
private slots:
void processFoundService(quint64 deviceAddress, const QBluetoothServiceInfo &info);
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp
new file mode 100644
index 00000000..e76dd7b6
--- /dev/null
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_win.cpp
@@ -0,0 +1,452 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothservicediscoveryagent.h"
+#include "qbluetoothservicediscoveryagent_p.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QLibrary>
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QThread>
+#include <QtCore/QUrl>
+
+#include <initguid.h>
+#include <winsock2.h>
+#include <qt_windows.h>
+#include <bluetoothapis.h>
+#include <ws2bth.h>
+
+Q_GLOBAL_STATIC(QLibrary, bluetoothapis)
+
+#define DEFINEFUNC(ret, func, ...) \
+ typedef ret (WINAPI *fp_##func)(__VA_ARGS__); \
+ static fp_##func p##func;
+
+#define RESOLVEFUNC(func) \
+ p##func = (fp_##func)resolveFunction(library, #func); \
+ if (!p##func) \
+ return false;
+
+DEFINEFUNC(DWORD, BluetoothSdpGetElementData, LPBYTE, ULONG, PSDP_ELEMENT_DATA)
+
+QT_BEGIN_NAMESPACE
+
+struct FindServiceResult {
+ QBluetoothServiceInfo info;
+ Qt::HANDLE hSearch;
+ int systemError;
+};
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+static inline QFunctionPointer resolveFunction(QLibrary *library, const char *func)
+{
+ const QFunctionPointer symbolFunctionPointer = library->resolve(func);
+ if (!symbolFunctionPointer)
+ qWarning("Cannot resolve '%s' in '%s'.", func, qPrintable(library->fileName()));
+ return symbolFunctionPointer;
+}
+
+static inline bool resolveFunctions(QLibrary *library)
+{
+ if (!library->isLoaded()) {
+ library->setFileName(QStringLiteral("bluetoothapis"));
+ if (!library->load()) {
+ qWarning("Unable to load '%s' library.", qPrintable(library->fileName()));
+ return false;
+ }
+ }
+
+ RESOLVEFUNC(BluetoothSdpGetElementData)
+
+ return true;
+}
+
+static QList<QVariant> spdContainerToVariantList(LPBYTE containerStream, ULONG containerLength);
+
+static QVariant spdElementToVariant(const SDP_ELEMENT_DATA &element)
+{
+ QVariant variant;
+
+ switch (element.type) {
+ case SDP_TYPE_UINT: {
+ switch (element.specificType) {
+ case SDP_ST_UINT128:
+ //Not supported!!!
+ break;
+ case SDP_ST_UINT64:
+ variant = QVariant::fromValue<quint64>(element.data.uint64);
+ break;
+ case SDP_ST_UINT32:
+ variant = QVariant::fromValue<quint32>(element.data.uint32);
+ break;
+ case SDP_ST_UINT16:
+ variant = QVariant::fromValue<quint16>(element.data.uint16);
+ break;
+ case SDP_ST_UINT8:
+ variant = QVariant::fromValue<quint8>(element.data.uint8);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case SDP_TYPE_INT: {
+ switch (element.specificType) {
+ case SDP_ST_INT128: {
+ //Not supported!!!
+ break;
+ }
+ case SDP_ST_INT64:
+ variant = QVariant::fromValue<qint64>(element.data.int64);
+ break;
+ case SDP_ST_INT32:
+ variant = QVariant::fromValue<qint32>(element.data.int32);
+ break;
+ case SDP_ST_INT16:
+ variant = QVariant::fromValue<qint16>(element.data.int16);
+ break;
+ case SDP_ST_INT8:
+ variant = QVariant::fromValue<qint8>(element.data.int8);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case SDP_TYPE_UUID: {
+ switch (element.specificType) {
+ case SDP_ST_UUID128: {
+ //Not sure if this will work, might be evil
+ Q_ASSERT(sizeof(element.data.uuid128) == sizeof(quint128));
+
+ quint128 uuid128;
+ memcpy(&uuid128, &(element.data.uuid128), sizeof(quint128));
+
+ variant = QVariant::fromValue(QBluetoothUuid(uuid128));
+ }
+ case SDP_ST_UUID32:
+ variant = QVariant::fromValue(QBluetoothUuid(quint32(element.data.uuid32)));
+ case SDP_ST_UUID16:
+ variant = QVariant::fromValue(QBluetoothUuid(quint16(element.data.uuid16)));
+ default:
+ break;
+ }
+ break;
+ }
+ case SDP_TYPE_STRING: {
+ const QByteArray stringBuffer(reinterpret_cast<const char*>(element.data.string.value), element.data.string.length);
+ variant = QVariant::fromValue<QString>(QString::fromLocal8Bit(stringBuffer));
+ break;
+ }
+ case SDP_TYPE_URL: {
+ const QString urlString = QString::fromLocal8Bit(reinterpret_cast<const char*>(element.data.url.value),
+ (int)element.data.url.length);
+ const QUrl url(urlString);
+ if (url.isValid())
+ variant = QVariant::fromValue<QUrl>(url);
+ break;
+ }
+ case SDP_TYPE_SEQUENCE: {
+ const QList<QVariant> sequenceList = spdContainerToVariantList(element.data.sequence.value,
+ element.data.sequence.length);
+ const QBluetoothServiceInfo::Sequence sequence(sequenceList);
+ variant = QVariant::fromValue(sequence);
+ break;
+ }
+ case SDP_TYPE_ALTERNATIVE: {
+ const QList<QVariant> alternativeList = spdContainerToVariantList(element.data.alternative.value,
+ element.data.alternative.length);
+ const QBluetoothServiceInfo::Alternative alternative(alternativeList);
+ variant = QVariant::fromValue(alternative);
+ break;
+ }
+ case SDP_TYPE_BOOLEAN:
+ variant = QVariant::fromValue<bool>((bool)element.data.booleanVal);
+ break;
+ case SDP_TYPE_NIL:
+ break;
+ default:
+ break;
+ }
+
+ return variant;
+}
+
+static QList<QVariant> spdContainerToVariantList(LPBYTE containerStream, ULONG containerLength)
+{
+ HBLUETOOTH_CONTAINER_ELEMENT iter = NULL;
+ SDP_ELEMENT_DATA element;
+
+ QList<QVariant> sequence;
+
+ for (;;) {
+ const DWORD result = BluetoothSdpGetContainerElementData(containerStream,
+ containerLength,
+ &iter,
+ &element);
+
+ if (result == ERROR_SUCCESS) {
+ const QVariant variant = spdElementToVariant(element);
+ sequence.append(variant);
+ } else if (result == ERROR_NO_MORE_ITEMS) {
+ break;
+ } else if (result == ERROR_INVALID_PARAMETER) {
+ break;
+ }
+ }
+
+ return sequence;
+}
+
+#if defined(Q_CC_MINGW)
+# define SDP_CALLBACK
+#else
+# define SDP_CALLBACK QT_WIN_CALLBACK
+#endif
+static BOOL SDP_CALLBACK bluetoothSdpCallback(ULONG attributeId, LPBYTE valueStream, ULONG streamSize, LPVOID param)
+{
+ QBluetoothServiceInfo *result = static_cast<QBluetoothServiceInfo*>(param);
+
+ SDP_ELEMENT_DATA element;
+
+ if (pBluetoothSdpGetElementData(valueStream, streamSize, &element) == ERROR_SUCCESS)
+ switch (element.type) {
+ case SDP_TYPE_UINT:
+ case SDP_TYPE_INT:
+ case SDP_TYPE_UUID:
+ case SDP_TYPE_STRING:
+ case SDP_TYPE_URL:
+ case SDP_TYPE_BOOLEAN:
+ case SDP_TYPE_SEQUENCE:
+ case SDP_TYPE_ALTERNATIVE: {
+ const QVariant variant = spdElementToVariant(element);
+ result->setAttribute(attributeId, variant);
+ break;
+ }
+ case SDP_TYPE_NIL:
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+enum {
+ WSAControlFlags = LUP_FLUSHCACHE
+ | LUP_RETURN_NAME
+ | LUP_RETURN_TYPE
+ | LUP_RETURN_ADDR
+ | LUP_RETURN_BLOB
+ | LUP_RETURN_COMMENT
+};
+
+static FindServiceResult findNextService(HANDLE hSearch)
+{
+ FindServiceResult result;
+ result.systemError = NO_ERROR;
+ result.hSearch = hSearch;
+
+ QByteArray resultBuffer(2048, 0);
+ WSAQUERYSET *resultQuery = reinterpret_cast<WSAQUERYSET*>(resultBuffer.data());
+ DWORD resultBufferSize = DWORD(resultBuffer.size());
+ const int resultCode = WSALookupServiceNext(hSearch,
+ WSAControlFlags,
+ &resultBufferSize,
+ resultQuery);
+
+ if (resultCode == SOCKET_ERROR) {
+ result.systemError = ::WSAGetLastError();
+ if (result.systemError == WSA_E_NO_MORE)
+ WSALookupServiceEnd(hSearch);
+ return result;
+ }
+
+ if (resultQuery->lpBlob
+ && BluetoothSdpEnumAttributes(resultQuery->lpBlob->pBlobData,
+ resultQuery->lpBlob->cbSize,
+ bluetoothSdpCallback,
+ &result.info)) {
+ return result;
+ } else {
+ result.systemError = GetLastError();
+ }
+ return result;
+}
+
+static FindServiceResult findFirstService(const QBluetoothAddress &address)
+{
+ //### should we try for 2.2 on all platforms ??
+ WSAData wsadata;
+ FindServiceResult result;
+ result.hSearch = INVALID_HANDLE_VALUE;
+
+ // IPv6 requires Winsock v2.0 or better.
+ if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) {
+ result.systemError = ::WSAGetLastError();
+ return result;
+ }
+
+ const QString addressAsString = QStringLiteral("(%1)").arg(address.toString());
+
+ QVector<WCHAR> addressAsWChar(addressAsString.size());
+ addressAsString.toWCharArray(addressAsWChar.data());
+
+ GUID protocol = L2CAP_PROTOCOL_UUID; //Search for L2CAP and RFCOMM services
+
+ WSAQUERYSET serviceQuery = {0};
+ serviceQuery.dwSize = sizeof(WSAQUERYSET); //As specified by the windows documentation
+ serviceQuery.lpServiceClassId = &protocol; //The protocal of the service what is being queried
+ serviceQuery.dwNameSpace = NS_BTH; //As specified by the windows documentation
+ serviceQuery.dwNumberOfCsAddrs = 0; //As specified by the windows documentation
+ serviceQuery.lpszContext = addressAsWChar.data(); //The remote address that query will run on
+
+ HANDLE hSearch;
+ const int resultCode = WSALookupServiceBegin(&serviceQuery,
+ WSAControlFlags,
+ &hSearch);
+ if (resultCode == SOCKET_ERROR) {
+ result.systemError = ::WSAGetLastError();
+ WSALookupServiceEnd(hSearch);
+ return result;
+ }
+ return findNextService(hSearch);
+}
+
+QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
+ QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter)
+ : error(QBluetoothServiceDiscoveryAgent::NoError),
+ state(Inactive),
+ deviceDiscoveryAgent(0),
+ mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery),
+ singleDevice(false),
+ pendingStop(false),
+ pendingFinish(false),
+ q_ptr(qp)
+{
+ Q_UNUSED(deviceAdapter);
+
+ resolveFunctions(bluetoothapis());
+
+ threadFind = new QThread;
+ threadWorkerFind = new ThreadWorkerFind;
+ threadWorkerFind->moveToThread(threadFind);
+ connect(threadWorkerFind, &ThreadWorkerFind::findFinished, this, &QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan);
+ connect(threadFind, &QThread::finished, threadWorkerFind, &ThreadWorkerFind::deleteLater);
+ connect(threadFind, &QThread::finished, threadFind, &QThread::deleteLater);
+ threadFind->start();
+}
+
+QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate()
+{
+ if (pendingFinish) {
+ stop();
+ }
+ if (threadFind)
+ threadFind->quit();
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &address)
+{
+ if (!pendingFinish) {
+ pendingFinish = true;
+ pendingStop = false;
+
+ const auto threadWorker = threadWorkerFind;
+ QMetaObject::invokeMethod(threadWorkerFind, [threadWorker, address]()
+ {
+ FindServiceResult result = findFirstService(address);
+ emit threadWorker->findFinished(QVariant::fromValue(result));
+ }, Qt::QueuedConnection);
+ }
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::stop()
+{
+ pendingStop = true;
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::_q_nextSdpScan(const QVariant &input)
+{
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+ auto result = input.value<FindServiceResult>();
+
+ if (pendingStop) {
+ WSALookupServiceEnd(result.hSearch);
+ pendingStop = false;
+ pendingFinish = false;
+ emit q->canceled();
+ } else {
+ if (result.systemError == WSA_E_NO_MORE) {
+ result.systemError = NO_ERROR;
+ } else if (result.systemError != NO_ERROR) {
+ if (result.hSearch != INVALID_HANDLE_VALUE)
+ WSALookupServiceEnd(result.hSearch);
+ error = (result.systemError == ERROR_INVALID_HANDLE) ?
+ QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError
+ : QBluetoothServiceDiscoveryAgent::InputOutputError;
+ errorString = qt_error_string(result.systemError);
+ qCWarning(QT_BT_WINDOWS) << errorString;
+ emit q->error(this->error);
+ } else {
+ result.info.setDevice(discoveredDevices.at(0));
+ if (result.info.isValid()) {
+ if (!isDuplicatedService(result.info)) {
+ discoveredServices.append(result.info);
+ emit q->serviceDiscovered(result.info);
+ }
+ }
+ const auto threadWorker = threadWorkerFind;
+ const auto hSearch = result.hSearch;
+ QMetaObject::invokeMethod(threadWorkerFind, [threadWorker, hSearch]()
+ {
+ FindServiceResult result = findNextService(hSearch);
+ emit threadWorker->findFinished(QVariant::fromValue(result));
+ }, Qt::QueuedConnection);
+ return;
+ }
+ pendingFinish = false;
+ _q_serviceDiscoveryFinished();
+ }
+}
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(FindServiceResult)
diff --git a/src/bluetooth/qbluetoothserviceinfo_win.cpp b/src/bluetooth/qbluetoothserviceinfo_win.cpp
new file mode 100644
index 00000000..6629e610
--- /dev/null
+++ b/src/bluetooth/qbluetoothserviceinfo_win.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothserviceinfo.h"
+#include "qbluetoothserviceinfo_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate()
+{
+}
+
+QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate()
+{
+}
+
+bool QBluetoothServiceInfoPrivate::isRegistered() const
+{
+ return false;
+}
+
+bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &localAdapter)
+{
+ Q_UNUSED(localAdapter);
+ return false;
+}
+
+bool QBluetoothServiceInfoPrivate::unregisterService()
+{
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothsocket.cpp b/src/bluetooth/qbluetoothsocket.cpp
index e6e1d955..54eb6024 100644
--- a/src/bluetooth/qbluetoothsocket.cpp
+++ b/src/bluetooth/qbluetoothsocket.cpp
@@ -47,6 +47,8 @@
#include "qbluetoothsocket_android_p.h"
#elif defined(QT_WINRT_BLUETOOTH)
#include "qbluetoothsocket_winrt_p.h"
+#elif defined(QT_WIN_BLUETOOTH)
+#include "qbluetoothsocket_win_p.h"
#else
#include "qbluetoothsocket_dummy_p.h"
#endif
@@ -267,6 +269,8 @@ static QBluetoothSocketBasePrivate *createSocketPrivate()
return new QBluetoothSocketPrivateAndroid();
#elif defined(QT_WINRT_BLUETOOTH)
return new QBluetoothSocketPrivateWinRT();
+#elif defined(QT_WIN_BLUETOOTH)
+ return new QBluetoothSocketPrivateWin();
#else
return new QBluetoothSocketPrivateDummy();
#endif
diff --git a/src/bluetooth/qbluetoothsocket.h b/src/bluetooth/qbluetoothsocket.h
index d2535544..eefcd2ad 100644
--- a/src/bluetooth/qbluetoothsocket.h
+++ b/src/bluetooth/qbluetoothsocket.h
@@ -75,6 +75,7 @@ class Q_BLUETOOTH_EXPORT QBluetoothSocket : public QIODevice
friend class QBluetoothSocketPrivateBluez;
friend class QBluetoothSocketPrivateBluezDBus;
friend class QBluetoothSocketPrivateDummy;
+ friend class QBluetoothSocketPrivateWin;
friend class QBluetoothSocketPrivateWinRT;
public:
diff --git a/src/bluetooth/qbluetoothsocket_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp
new file mode 100644
index 00000000..14c780d1
--- /dev/null
+++ b/src/bluetooth/qbluetoothsocket_win.cpp
@@ -0,0 +1,587 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothsocket.h"
+#include "qbluetoothsocket_win_p.h"
+
+#include <QtCore/qloggingcategory.h>
+
+#include <QtBluetooth/qbluetoothdeviceinfo.h>
+#include <QtBluetooth/QBluetoothLocalDevice>
+#include <QtCore/QSocketNotifier>
+
+#include <winsock2.h>
+#include <ws2bth.h>
+#include <bluetoothapis.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+QBluetoothSocketPrivateWin::QBluetoothSocketPrivateWin()
+ : QBluetoothSocketBasePrivate()
+{
+}
+
+QBluetoothSocketPrivateWin::~QBluetoothSocketPrivateWin()
+{
+ abort();
+}
+
+bool QBluetoothSocketPrivateWin::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (socket != INVALID_SOCKET) {
+ if (socketType == type)
+ return true;
+ abort();
+ }
+
+ socketType = type;
+
+ switch (type) {
+ case QBluetoothServiceInfo::RfcommProtocol:
+ socket = ::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
+ break;
+ default:
+ socket = INVALID_SOCKET;
+ errorString = QBluetoothSocket::tr("Unsupported protocol. Win32 only supports RFCOMM sockets");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return false;
+ }
+
+ if (socket == INVALID_SOCKET) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Failed to create socket:" << error << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Failed to create socket");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return false;
+ }
+
+ if (!createNotifiers())
+ return false;
+
+ return true;
+}
+
+void QBluetoothSocketPrivateWin::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (socket == INVALID_SOCKET && !ensureNativeSocket(socketType))
+ return;
+
+ if (!configureSecurity())
+ return;
+
+ SOCKADDR_BTH addr = {0};
+ addr.addressFamily = AF_BTH;
+ addr.port = port;
+ addr.btAddr = address.toUInt64();
+
+ switch (socketType) {
+ case QBluetoothServiceInfo::RfcommProtocol:
+ addr.serviceClassId = RFCOMM_PROTOCOL_UUID;
+ break;
+ default:
+ errorString = QBluetoothSocket::tr("Socket type not handled: %1").arg(socketType);
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ connectWriteNotifier->setEnabled(true);
+ readNotifier->setEnabled(true);
+ exceptNotifier->setEnabled(true);
+
+ const int result = ::connect(socket, reinterpret_cast<sockaddr *>(&addr), sizeof(addr));
+
+ const int error = ::WSAGetLastError();
+ if (result != SOCKET_ERROR || error == WSAEWOULDBLOCK) {
+ q->setSocketState(QBluetoothSocket::ConnectingState);
+ q->setOpenMode(openMode);
+ } else {
+ errorString = qt_error_string(error);
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ }
+}
+
+void QBluetoothSocketPrivateWin::connectToService(
+ const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (q->state() != QBluetoothSocket::UnconnectedState
+ && q->state() != QBluetoothSocket::ServiceLookupState) {
+ //qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWIN::connectToService called on busy socket";
+ errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ // we are checking the service protocol and not socketType()
+ // socketType will change in ensureNativeSocket()
+ if (service.socketProtocol() == QBluetoothServiceInfo::UnknownProtocol) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocket::connectToService cannot "
+ "connect with 'UnknownProtocol' (type provided by given service)";
+ errorString = QBluetoothSocket::tr("Socket type not supported");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ if (service.protocolServiceMultiplexer() > 0) {
+ Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::L2capProtocol);
+
+ if (!ensureNativeSocket(QBluetoothServiceInfo::L2capProtocol)) {
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return;
+ }
+ connectToServiceHelper(service.device().address(), service.protocolServiceMultiplexer(),
+ openMode);
+ } else if (service.serverChannel() > 0) {
+ Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol);
+
+ if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return;
+ }
+ connectToServiceHelper(service.device().address(), service.serverChannel(), openMode);
+ } else {
+ // try doing service discovery to see if we can find the socket
+ if (service.serviceUuid().isNull()
+ && !service.serviceClassUuids().contains(QBluetoothUuid::SerialPort)) {
+ qCWarning(QT_BT_WINDOWS) << "No port, no PSM, and no UUID provided. Unable to connect";
+ return;
+ }
+ qCDebug(QT_BT_WINDOWS) << "Need a port/psm, doing discovery";
+ q->doDeviceDiscovery(service, openMode);
+ }
+}
+void QBluetoothSocketPrivateWin::_q_writeNotify()
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state == QBluetoothSocket::ConnectingState) {
+ updateAddressesAndPorts();
+ q->setSocketState(QBluetoothSocket::ConnectedState);
+ emit q->connected();
+ connectWriteNotifier->setEnabled(false);
+ } else {
+ if (txBuffer.isEmpty()) {
+ connectWriteNotifier->setEnabled(false);
+ return;
+ }
+
+ char buf[1024];
+ const int size = txBuffer.read(&buf[0], sizeof(buf));
+ const int writtenBytes = ::send(socket, &buf[0], size, 0);
+ if (writtenBytes == SOCKET_ERROR) {
+ // every other case returns error
+ const int error = ::WSAGetLastError();
+ errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(error));
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ } else if (writtenBytes <= size) {
+ // add remainder back to buffer
+ char *remainder = &buf[writtenBytes];
+ txBuffer.ungetBlock(remainder, size - writtenBytes);
+ if (writtenBytes > 0)
+ emit q->bytesWritten(writtenBytes);
+ } else {
+ errorString = QBluetoothSocket::tr("Logic error: more bytes sent than passed to ::send");
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ }
+
+ if (!txBuffer.isEmpty()) {
+ connectWriteNotifier->setEnabled(true);
+ } else if (state == QBluetoothSocket::ClosingState) {
+ connectWriteNotifier->setEnabled(false);
+ this->close();
+ }
+ }
+}
+
+void QBluetoothSocketPrivateWin::_q_readNotify()
+{
+ Q_Q(QBluetoothSocket);
+
+ char *writePointer = buffer.reserve(QPRIVATELINEARBUFFER_BUFFERSIZE);
+ const int bytesRead = ::recv(socket, writePointer, QPRIVATELINEARBUFFER_BUFFERSIZE, 0);
+ if (bytesRead == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ buffer.chop(QPRIVATELINEARBUFFER_BUFFERSIZE);
+ readNotifier->setEnabled(false);
+ connectWriteNotifier->setEnabled(false);
+ errorString = qt_error_string(error);
+ qCWarning(QT_BT_WINDOWS) << Q_FUNC_INFO << socket << "error:" << error << errorString;
+ switch (error) {
+ case WSAEHOSTDOWN:
+ q->setSocketError(QBluetoothSocket::HostNotFoundError);
+ break;
+ case WSAECONNRESET:
+ q->setSocketError(QBluetoothSocket::RemoteHostClosedError);
+ break;
+ default:
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ break;
+ }
+
+ q->disconnectFromService();
+ } else {
+ const int unusedBytes = QPRIVATELINEARBUFFER_BUFFERSIZE - bytesRead;
+ buffer.chop(unusedBytes);
+ if (bytesRead > 0)
+ emit q->readyRead();
+ }
+}
+
+void QBluetoothSocketPrivateWin::_q_exceptNotify()
+{
+ Q_Q(QBluetoothSocket);
+
+ const int error = ::WSAGetLastError();
+ errorString = qt_error_string(error);
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+
+ if (state == QBluetoothSocket::ConnectingState) {
+ abort();
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ emit q->disconnected();
+ }
+}
+
+void QBluetoothSocketPrivateWin::connectToService(
+ const QBluetoothAddress &address, const QBluetoothUuid &uuid,
+ QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (q->state() != QBluetoothSocket::UnconnectedState) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService called on busy socket";
+ errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService cannot "
+ "connect with 'UnknownProtocol' (type provided by given service)";
+ errorString = QBluetoothSocket::tr("Socket type not supported");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ QBluetoothServiceInfo service;
+ QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice);
+ service.setDevice(device);
+ service.setServiceUuid(uuid);
+ q->doDeviceDiscovery(service, openMode);
+}
+
+void QBluetoothSocketPrivateWin::connectToService(
+ const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService cannot "
+ "connect with 'UnknownProtocol' (type provided by given service)";
+ errorString = QBluetoothSocket::tr("Socket type not supported");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ if (q->state() != QBluetoothSocket::UnconnectedState) {
+ qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService called on busy socket";
+ errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ q->setOpenMode(openMode);
+ connectToServiceHelper(address, port, openMode);
+}
+
+void QBluetoothSocketPrivateWin::abort()
+{
+ delete readNotifier;
+ readNotifier = nullptr;
+ delete connectWriteNotifier;
+ connectWriteNotifier = nullptr;
+ delete exceptNotifier;
+ exceptNotifier = nullptr;
+
+ m_localAddress.clear();
+ m_localPort = 0;
+ m_peerAddress.clear();
+ m_peerPort = 0;
+
+ // We don't transition through Closing for abort, so
+ // we don't call disconnectFromService or QBluetoothSocket::close
+ ::closesocket(socket);
+ socket = INVALID_SOCKET;
+}
+
+QString QBluetoothSocketPrivateWin::localName() const
+{
+ const QBluetoothLocalDevice device(m_localAddress);
+ return device.name();
+}
+
+QBluetoothAddress QBluetoothSocketPrivateWin::localAddress() const
+{
+ return m_localAddress;
+}
+
+quint16 QBluetoothSocketPrivateWin::localPort() const
+{
+ return m_localPort;
+}
+
+QString QBluetoothSocketPrivateWin::peerName() const
+{
+ if (socket == INVALID_SOCKET)
+ return {};
+ BLUETOOTH_DEVICE_INFO bdi = {0};
+ bdi.dwSize = sizeof(bdi);
+ bdi.Address.ullLong = m_peerAddress.toUInt64();
+ const DWORD res = ::BluetoothGetDeviceInfo(nullptr, &bdi);
+ if (res == ERROR_SUCCESS)
+ return QString::fromWCharArray(&bdi.szName[0]);
+ qCWarning(QT_BT_WINDOWS) << "Error calling BluetoothGetDeviceInfo" << res << qt_error_string(res);
+ return {};
+}
+
+QBluetoothAddress QBluetoothSocketPrivateWin::peerAddress() const
+{
+ return m_peerAddress;
+}
+
+quint16 QBluetoothSocketPrivateWin::peerPort() const
+{
+ return m_peerPort;
+}
+
+qint64 QBluetoothSocketPrivateWin::writeData(const char *data, qint64 maxSize)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QBluetoothSocket::tr("Cannot write while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ if (q->openMode() & QIODevice::Unbuffered) {
+ const int bytesWritten = ::send(socket, data, maxSize, 0);
+
+ if (bytesWritten == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(error));
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ }
+
+ if (bytesWritten > 0)
+ emit q->bytesWritten(bytesWritten);
+
+ return bytesWritten;
+ } else {
+
+ if (!connectWriteNotifier)
+ return -1;
+
+ if (txBuffer.isEmpty())
+ connectWriteNotifier->setEnabled(true);
+
+ char *txbuf = txBuffer.reserve(maxSize);
+ ::memcpy(txbuf, data, maxSize);
+
+ return maxSize;
+ }
+}
+
+qint64 QBluetoothSocketPrivateWin::readData(char *data, qint64 maxSize)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QBluetoothSocket::tr("Cannot read while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ const int bytesRead = buffer.read(data, maxSize);
+ return bytesRead;
+}
+
+void QBluetoothSocketPrivateWin::close()
+{
+ if (txBuffer.isEmpty())
+ abort();
+ else
+ connectWriteNotifier->setEnabled(true);
+}
+
+bool QBluetoothSocketPrivateWin::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType_,
+ QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ abort();
+
+ socketType = socketType_;
+ socket = socketDescriptor;
+
+ if (!createNotifiers())
+ return false;
+ updateAddressesAndPorts();
+ q->setSocketState(socketState);
+ q->setOpenMode(openMode);
+ if (socketState == QBluetoothSocket::ConnectedState) {
+ connectWriteNotifier->setEnabled(true);
+ readNotifier->setEnabled(true);
+ exceptNotifier->setEnabled(true);
+ }
+
+ return true;
+}
+
+qint64 QBluetoothSocketPrivateWin::bytesAvailable() const
+{
+ return buffer.size();
+}
+
+bool QBluetoothSocketPrivateWin::canReadLine() const
+{
+ return buffer.canReadLine();
+}
+
+qint64 QBluetoothSocketPrivateWin::bytesToWrite() const
+{
+ return txBuffer.size();
+}
+
+bool QBluetoothSocketPrivateWin::createNotifiers()
+{
+ Q_Q(QBluetoothSocket);
+
+ ULONG mode = 1; // 1 to enable non-blocking socket
+ const int result = ::ioctlsocket(socket, FIONBIO, &mode);
+
+ if (result == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ errorString = qt_error_string(error);
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ qCWarning(QT_BT_WINDOWS) << "Error setting socket to non-blocking" << error << errorString;
+ abort();
+ return false;
+ }
+ readNotifier = new QSocketNotifier(socket, QSocketNotifier::Read);
+ QObject::connect(readNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_readNotify);
+ connectWriteNotifier = new QSocketNotifier(socket, QSocketNotifier::Write, q);
+ QObject::connect(connectWriteNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_writeNotify);
+ exceptNotifier = new QSocketNotifier(socket, QSocketNotifier::Exception, q);
+ QObject::connect(exceptNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_exceptNotify);
+
+ connectWriteNotifier->setEnabled(false);
+ readNotifier->setEnabled(false);
+ exceptNotifier->setEnabled(false);
+ return true;
+}
+
+void QBluetoothSocketPrivateWin::updateAddressesAndPorts()
+{
+ SOCKADDR_BTH localAddr = {0};
+ int localAddrLength = sizeof(localAddr);
+ const int localResult = ::getsockname(socket, reinterpret_cast<sockaddr *>(&localAddr), &localAddrLength);
+ if (localResult != SOCKET_ERROR) {
+ m_localAddress = QBluetoothAddress(localAddr.btAddr);
+ m_localPort = localAddr.port;
+ } else {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Error getting local address and port" << error << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Cannot get socket's local address and port");
+ }
+
+ SOCKADDR_BTH peerAddr = {0};
+ int peerAddrLength = sizeof(peerAddr);
+ const int peerResult = ::getpeername(socket, reinterpret_cast<sockaddr *>(&peerAddr), &peerAddrLength);
+ if (peerResult != SOCKET_ERROR) {
+ m_peerAddress = QBluetoothAddress(peerAddr.btAddr);
+ m_peerPort = peerAddr.port;
+ } else {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Error getting peer address and port" << error << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Cannot get socket's peer address and port");
+ }
+}
+
+bool QBluetoothSocketPrivateWin::configureSecurity()
+{
+ Q_Q(QBluetoothSocket);
+
+ if (secFlags & QBluetooth::Authorization) {
+ ULONG authenticate = TRUE;
+ const int result = ::setsockopt(socket, SOL_RFCOMM, SO_BTH_AUTHENTICATE, reinterpret_cast<const char*>(&authenticate), sizeof(authenticate));
+ if (result == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Failed to set socket option, closing socket for safety" << error;
+ qCWarning(QT_BT_WINDOWS) << "Error: " << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Cannot set connection security level");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return false;
+ }
+ }
+
+ if (secFlags & QBluetooth::Encryption) {
+ ULONG encrypt = TRUE;
+ const int result = ::setsockopt(socket, SOL_RFCOMM, SO_BTH_ENCRYPT, reinterpret_cast<const char*>(&encrypt), sizeof(encrypt));
+ if (result == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Failed to set socket option, closing socket for safety" << error;
+ qCWarning(QT_BT_WINDOWS) << "Error: " << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Cannot set connection security level");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return false;
+ }
+ }
+ return true;
+}
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothsocket_win_p.h b/src/bluetooth/qbluetoothsocket_win_p.h
new file mode 100644
index 00000000..fe0fc99f
--- /dev/null
+++ b/src/bluetooth/qbluetoothsocket_win_p.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBLUETOOTHSOCKET_WIN_H
+#define QBLUETOOTHSOCKET_WIN_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qbluetoothsocket.h"
+#include "qbluetoothsocketbase_p.h"
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+
+class QBluetoothSocketPrivateWin final : public QBluetoothSocketBasePrivate
+{
+ Q_OBJECT
+ friend class QBluetoothServerPrivate;
+
+public:
+ QBluetoothSocketPrivateWin();
+ ~QBluetoothSocketPrivateWin() override;
+
+ void connectToServiceHelper(const QBluetoothAddress &address,
+ quint16 port,
+ QIODevice::OpenMode openMode) override;
+
+ void connectToService(const QBluetoothServiceInfo &service,
+ QIODevice::OpenMode openMode) override;
+ void connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid,
+ QIODevice::OpenMode openMode) override;
+ void connectToService(const QBluetoothAddress &address, quint16 port,
+ QIODevice::OpenMode openMode) override;
+
+ bool ensureNativeSocket(QBluetoothServiceInfo::Protocol type) override;
+
+ QString localName() const override;
+ QBluetoothAddress localAddress() const override;
+ quint16 localPort() const override;
+
+ QString peerName() const override;
+ QBluetoothAddress peerAddress() const override;
+ quint16 peerPort() const override;
+
+ void abort() override;
+ void close() override;
+
+ qint64 writeData(const char *data, qint64 maxSize) override;
+ qint64 readData(char *data, qint64 maxSize) override;
+
+ bool setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
+ QBluetoothSocket::SocketState socketState = QBluetoothSocket::ConnectedState,
+ QBluetoothSocket::OpenMode openMode = QBluetoothSocket::ReadWrite) override;
+
+ qint64 bytesAvailable() const override;
+ bool canReadLine() const override;
+ qint64 bytesToWrite() const override;
+
+private slots:
+ void _q_readNotify();
+ void _q_writeNotify();
+ void _q_exceptNotify();
+
+private:
+ bool createNotifiers();
+ void updateAddressesAndPorts();
+ bool configureSecurity();
+
+ QSocketNotifier *exceptNotifier = nullptr;
+ QBluetoothAddress m_localAddress;
+ quint16 m_localPort = 0;
+ QBluetoothAddress m_peerAddress;
+ quint16 m_peerPort = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QBLUETOOTHSOCKET_WIN_H
diff --git a/src/bluetooth/qbluetoothutils_win.cpp b/src/bluetooth/qbluetoothutils_win.cpp
index fa3127cb..a5151d82 100644
--- a/src/bluetooth/qbluetoothutils_win.cpp
+++ b/src/bluetooth/qbluetoothutils_win.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
diff --git a/src/bluetooth/qlowenergycharacteristic.h b/src/bluetooth/qlowenergycharacteristic.h
index 9b27d621..bb6487c4 100644
--- a/src/bluetooth/qlowenergycharacteristic.h
+++ b/src/bluetooth/qlowenergycharacteristic.h
@@ -102,6 +102,7 @@ protected:
friend class QLowEnergyControllerPrivateBluezDBus;
friend class QLowEnergyControllerPrivateCommon;
friend class QLowEnergyControllerPrivateOSX;
+ friend class QLowEnergyControllerPrivateWin32;
friend class QLowEnergyControllerPrivateWinRT;
friend class QLowEnergyControllerPrivateWinRTNew;
QLowEnergyCharacteristicPrivate *data = nullptr;
diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp
index 8fc044fb..c3742b76 100644
--- a/src/bluetooth/qlowenergycontroller.cpp
+++ b/src/bluetooth/qlowenergycontroller.cpp
@@ -60,6 +60,8 @@
#if QT_CONFIG(winrt_btle_no_pairing)
#include "qlowenergycontroller_winrt_new_p.h"
#endif
+#elif defined(QT_WIN_BLUETOOTH)
+#include "qlowenergycontroller_win_p.h"
#else
#include "qlowenergycontroller_p.h"
#endif
@@ -321,6 +323,8 @@ static QLowEnergyControllerPrivate *privateController(QLowEnergyController::Role
qCDebug(QT_BT_WINRT) << "Using pre 15063 low energy controller";
return new QLowEnergyControllerPrivateWinRT();
#endif
+#elif defined(QT_WIN_BLUETOOTH)
+ return new QLowEnergyControllerPrivateWin32();
#else
Q_UNUSED(role);
return new QLowEnergyControllerPrivateCommon();
diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h
index 54a49b53..fe1da852 100644
--- a/src/bluetooth/qlowenergycontroller_p.h
+++ b/src/bluetooth/qlowenergycontroller_p.h
@@ -104,6 +104,7 @@ public:
const QByteArray &newValue) override;
void addToGenericAttributeList(const QLowEnergyServiceData &service,
+
QLowEnergyHandle startHandle) override;
};
diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp
new file mode 100644
index 00000000..cc848d1e
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller_win.cpp
@@ -0,0 +1,1346 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlowenergycontroller_win_p.h"
+#include "qbluetoothdevicediscoveryagent_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QIODevice> // for open modes
+#include <QtCore/QEvent>
+#include <QtCore/QMutex>
+#include <QtCore/QThread>
+#include <QtCore/QDataStream>
+#include <QtCore/QCoreApplication>
+
+#include <algorithm> // for std::max
+
+#include <setupapi.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
+
+Q_GLOBAL_STATIC(QLibrary, bluetoothapis)
+
+Q_GLOBAL_STATIC(QVector<QLowEnergyControllerPrivateWin32 *>, qControllers)
+static QMutex controllersGuard(QMutex::NonRecursive);
+
+const QEvent::Type CharactericticValueEventType = static_cast<QEvent::Type>(QEvent::User + 1);
+
+class CharactericticValueEvent : public QEvent
+{
+public:
+ explicit CharactericticValueEvent(const PBLUETOOTH_GATT_VALUE_CHANGED_EVENT gattValueChangedEvent)
+ : QEvent(CharactericticValueEventType)
+ , m_handle(0)
+ {
+ if (!gattValueChangedEvent || gattValueChangedEvent->CharacteristicValueDataSize == 0)
+ return;
+
+ m_handle = gattValueChangedEvent->ChangedAttributeHandle;
+
+ const PBTH_LE_GATT_CHARACTERISTIC_VALUE gattValue = gattValueChangedEvent->CharacteristicValue;
+ if (!gattValue)
+ return;
+
+ m_value = QByteArray(reinterpret_cast<const char *>(&gattValue->Data[0]),
+ gattValue->DataSize);
+ }
+
+ QByteArray m_value;
+ QLowEnergyHandle m_handle;
+};
+
+// Bit masks of ClientCharacteristicConfiguration value, see btle spec.
+namespace ClientCharacteristicConfigurationValue {
+enum { UseNotifications = 0x1, UseIndications = 0x2 };
+}
+
+static bool gattFunctionsResolved = false;
+
+static QBluetoothAddress getDeviceAddress(const QString &servicePath)
+{
+ const int firstbound = servicePath.lastIndexOf(QStringLiteral("_"));
+ const int lastbound = servicePath.indexOf(QLatin1Char('#'), firstbound);
+ const QString hex = servicePath.mid(firstbound + 1, lastbound - firstbound - 1);
+ bool ok = false;
+ return QBluetoothAddress(hex.toULongLong(&ok, 16));
+}
+
+static QString getServiceSystemPath(const QBluetoothAddress &deviceAddress,
+ const QBluetoothUuid &serviceUuid, int *systemErrorCode)
+{
+ const HDEVINFO deviceInfoSet = ::SetupDiGetClassDevs(
+ reinterpret_cast<const GUID *>(&serviceUuid),
+ NULL,
+ 0,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+
+ if (deviceInfoSet == INVALID_HANDLE_VALUE) {
+ *systemErrorCode = ::GetLastError();
+ return QString();
+ }
+
+ QString foundSystemPath;
+ DWORD index = 0;
+
+ for (;;) {
+ SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
+ ::ZeroMemory(&deviceInterfaceData, sizeof(deviceInterfaceData));
+ deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
+
+ if (!::SetupDiEnumDeviceInterfaces(
+ deviceInfoSet,
+ NULL,
+ reinterpret_cast<const GUID *>(&serviceUuid),
+ index++,
+ &deviceInterfaceData)) {
+ *systemErrorCode = ::GetLastError();
+ break;
+ }
+
+ DWORD deviceInterfaceDetailDataSize = 0;
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ deviceInfoSet,
+ &deviceInterfaceData,
+ NULL,
+ deviceInterfaceDetailDataSize,
+ &deviceInterfaceDetailDataSize,
+ NULL)) {
+ const DWORD error = ::GetLastError();
+ if (error != ERROR_INSUFFICIENT_BUFFER) {
+ *systemErrorCode = error;
+ break;
+ }
+ }
+
+ SP_DEVINFO_DATA deviceInfoData;
+ ::ZeroMemory(&deviceInfoData, sizeof(deviceInfoData));
+ deviceInfoData.cbSize = sizeof(deviceInfoData);
+
+ QByteArray deviceInterfaceDetailDataBuffer(
+ deviceInterfaceDetailDataSize, 0);
+
+ PSP_INTERFACE_DEVICE_DETAIL_DATA deviceInterfaceDetailData =
+ reinterpret_cast<PSP_INTERFACE_DEVICE_DETAIL_DATA>
+ (deviceInterfaceDetailDataBuffer.data());
+
+ deviceInterfaceDetailData->cbSize =
+ sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
+
+ if (!::SetupDiGetDeviceInterfaceDetail(
+ deviceInfoSet,
+ &deviceInterfaceData,
+ deviceInterfaceDetailData,
+ deviceInterfaceDetailDataBuffer.size(),
+ &deviceInterfaceDetailDataSize,
+ &deviceInfoData)) {
+ *systemErrorCode = ::GetLastError();
+ break;
+ }
+
+ // We need to check on required device address which contains in a
+ // system path. As it is not enough to use only service UUID for this.
+ const auto candidateSystemPath = QString::fromWCharArray(deviceInterfaceDetailData->DevicePath);
+ const auto candidateDeviceAddress = getDeviceAddress(candidateSystemPath);
+ if (candidateDeviceAddress == deviceAddress) {
+ foundSystemPath = candidateSystemPath;
+ *systemErrorCode = NO_ERROR;
+ break;
+ }
+ }
+
+ ::SetupDiDestroyDeviceInfoList(deviceInfoSet);
+ return foundSystemPath;
+}
+
+static HANDLE openSystemDevice(
+ const QString &systemPath, QIODevice::OpenMode openMode, int *systemErrorCode)
+{
+ DWORD desiredAccess = 0;
+ DWORD shareMode = 0;
+
+ if (openMode & QIODevice::ReadOnly) {
+ desiredAccess |= GENERIC_READ;
+ shareMode |= FILE_SHARE_READ;
+ }
+
+ if (openMode & QIODevice::WriteOnly) {
+ desiredAccess |= GENERIC_WRITE;
+ shareMode |= FILE_SHARE_WRITE;
+ }
+
+ const HANDLE hDevice = ::CreateFile(
+ reinterpret_cast<const wchar_t *>(systemPath.utf16()),
+ desiredAccess,
+ shareMode,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+
+ *systemErrorCode = (INVALID_HANDLE_VALUE == hDevice)
+ ? ::GetLastError() : NO_ERROR;
+ return hDevice;
+}
+
+static HANDLE openSystemService(const QBluetoothAddress &deviceAddress,
+ const QBluetoothUuid &service, QIODevice::OpenMode openMode, int *systemErrorCode)
+{
+ const QString serviceSystemPath = getServiceSystemPath(
+ deviceAddress, service, systemErrorCode);
+
+ if (*systemErrorCode != NO_ERROR)
+ return INVALID_HANDLE_VALUE;
+
+ const HANDLE hService = openSystemDevice(
+ serviceSystemPath, openMode, systemErrorCode);
+
+ if (*systemErrorCode != NO_ERROR)
+ return INVALID_HANDLE_VALUE;
+
+ return hService;
+}
+
+static void closeSystemDevice(HANDLE hDevice)
+{
+ if (hDevice && hDevice != INVALID_HANDLE_VALUE)
+ ::CloseHandle(hDevice);
+}
+
+static QVector<BTH_LE_GATT_SERVICE> enumeratePrimaryGattServices(
+ HANDLE hDevice, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QVector<BTH_LE_GATT_SERVICE>();
+ }
+
+ QVector<BTH_LE_GATT_SERVICE> foundServices;
+ USHORT servicesCount = 0;
+ for (;;) {
+ const HRESULT hr = ::BluetoothGATTGetServices(
+ hDevice,
+ servicesCount,
+ foundServices.isEmpty() ? NULL : &foundServices[0],
+ &servicesCount,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return foundServices;
+ } else {
+ const DWORD error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ foundServices.resize(servicesCount);
+ } else {
+ *systemErrorCode = error;
+ return QVector<BTH_LE_GATT_SERVICE>();
+ }
+ }
+ }
+}
+
+static QVector<BTH_LE_GATT_CHARACTERISTIC> enumerateGattCharacteristics(
+ HANDLE hService, PBTH_LE_GATT_SERVICE gattService, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QVector<BTH_LE_GATT_CHARACTERISTIC>();
+ }
+
+ QVector<BTH_LE_GATT_CHARACTERISTIC> foundCharacteristics;
+ USHORT characteristicsCount = 0;
+ for (;;) {
+ const HRESULT hr = ::BluetoothGATTGetCharacteristics(
+ hService,
+ gattService,
+ characteristicsCount,
+ foundCharacteristics.isEmpty() ? NULL : &foundCharacteristics[0],
+ &characteristicsCount,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return foundCharacteristics;
+ } else {
+ const DWORD error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ foundCharacteristics.resize(characteristicsCount);
+ } else {
+ *systemErrorCode = error;
+ return QVector<BTH_LE_GATT_CHARACTERISTIC>();
+ }
+ }
+ }
+}
+
+static QByteArray getGattCharacteristicValue(
+ HANDLE hService, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QByteArray();
+ }
+
+ QByteArray valueBuffer;
+ USHORT valueBufferSize = 0;
+ for (;;) {
+ const auto valuePtr = valueBuffer.isEmpty()
+ ? NULL
+ : reinterpret_cast<PBTH_LE_GATT_CHARACTERISTIC_VALUE>(valueBuffer.data());
+
+ const HRESULT hr = ::BluetoothGATTGetCharacteristicValue(
+ hService,
+ gattCharacteristic,
+ valueBufferSize,
+ valuePtr,
+ &valueBufferSize,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return QByteArray(reinterpret_cast<const char *>(&valuePtr->Data[0]),
+ valuePtr->DataSize);
+ } else {
+ const DWORD error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ valueBuffer.resize(valueBufferSize);
+ } else {
+ *systemErrorCode = error;
+ return QByteArray();
+ }
+ }
+ }
+}
+
+static void setGattCharacteristicValue(
+ HANDLE hService, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic,
+ const QByteArray &value, DWORD flags, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return;
+ }
+
+ QByteArray valueBuffer;
+ QDataStream out(&valueBuffer, QIODevice::WriteOnly);
+ ULONG dataSize = value.size();
+ out.writeRawData(reinterpret_cast<const char *>(&dataSize), sizeof(dataSize));
+ out.writeRawData(value.constData(), value.size());
+
+ BTH_LE_GATT_RELIABLE_WRITE_CONTEXT reliableWriteContext = 0;
+
+ const HRESULT hr = ::BluetoothGATTSetCharacteristicValue(
+ hService,
+ gattCharacteristic,
+ reinterpret_cast<PBTH_LE_GATT_CHARACTERISTIC_VALUE>(valueBuffer.data()),
+ reliableWriteContext,
+ flags);
+
+ if (SUCCEEDED(hr))
+ *systemErrorCode = NO_ERROR;
+ else
+ *systemErrorCode = WIN32_FROM_HRESULT(hr);
+}
+
+static QVector<BTH_LE_GATT_DESCRIPTOR> enumerateGattDescriptors(
+ HANDLE hService, PBTH_LE_GATT_CHARACTERISTIC gattCharacteristic, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QVector<BTH_LE_GATT_DESCRIPTOR>();
+ }
+
+ QVector<BTH_LE_GATT_DESCRIPTOR> foundDescriptors;
+ USHORT descriptorsCount = 0;
+ for (;;) {
+ const HRESULT hr = ::BluetoothGATTGetDescriptors(
+ hService,
+ gattCharacteristic,
+ descriptorsCount,
+ foundDescriptors.isEmpty() ? NULL : &foundDescriptors[0],
+ &descriptorsCount,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return foundDescriptors;
+ } else {
+ const DWORD error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ foundDescriptors.resize(descriptorsCount);
+ } else {
+ *systemErrorCode = error;
+ return QVector<BTH_LE_GATT_DESCRIPTOR>();
+ }
+ }
+ }
+}
+
+static QByteArray getGattDescriptorValue(
+ HANDLE hService, PBTH_LE_GATT_DESCRIPTOR gattDescriptor, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return QByteArray();
+ }
+
+ QByteArray valueBuffer;
+ USHORT valueBufferSize = 0;
+ for (;;) {
+ const auto valuePtr = valueBuffer.isEmpty()
+ ? NULL
+ : reinterpret_cast<PBTH_LE_GATT_DESCRIPTOR_VALUE>(valueBuffer.data());
+
+ const HRESULT hr = ::BluetoothGATTGetDescriptorValue(
+ hService,
+ gattDescriptor,
+ valueBufferSize,
+ valuePtr,
+ &valueBufferSize,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr)) {
+ *systemErrorCode = NO_ERROR;
+ return QByteArray(reinterpret_cast<const char *>(&valuePtr->Data[0]),
+ valuePtr->DataSize);
+ } else {
+ const DWORD error = WIN32_FROM_HRESULT(hr);
+ if (error == ERROR_MORE_DATA) {
+ valueBuffer.resize(valueBufferSize);
+ } else {
+ *systemErrorCode = error;
+ return QByteArray();
+ }
+ }
+ }
+}
+
+static void setGattDescriptorValue(
+ HANDLE hService, PBTH_LE_GATT_DESCRIPTOR gattDescriptor,
+ QByteArray value, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return;
+ }
+
+ const int requiredValueBufferSize = sizeof(BTH_LE_GATT_DESCRIPTOR_VALUE)
+ + value.size();
+
+ QByteArray valueBuffer(requiredValueBufferSize, 0);
+
+ PBTH_LE_GATT_DESCRIPTOR_VALUE gattValue = reinterpret_cast<
+ PBTH_LE_GATT_DESCRIPTOR_VALUE>(valueBuffer.data());
+
+ gattValue->DescriptorType = gattDescriptor->DescriptorType;
+
+ if (gattValue->DescriptorType == ClientCharacteristicConfiguration) {
+ QDataStream in(value);
+ quint8 u;
+ in >> u;
+
+ // We need to setup appropriate fields that allow to subscribe for events.
+ gattValue->ClientCharacteristicConfiguration.IsSubscribeToNotification =
+ bool(u & ClientCharacteristicConfigurationValue::UseNotifications);
+ gattValue->ClientCharacteristicConfiguration.IsSubscribeToIndication =
+ bool(u & ClientCharacteristicConfigurationValue::UseIndications);
+ }
+
+ gattValue->DataSize = ULONG(value.size());
+ ::memcpy(gattValue->Data, value.constData(), value.size());
+
+ const HRESULT hr = ::BluetoothGATTSetDescriptorValue(
+ hService,
+ gattDescriptor,
+ gattValue,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr))
+ *systemErrorCode = NO_ERROR;
+ else
+ *systemErrorCode = WIN32_FROM_HRESULT(hr);
+}
+
+static void WINAPI eventChangedCallbackEntry(
+ BTH_LE_GATT_EVENT_TYPE eventType, PVOID eventOutParameter, PVOID context)
+{
+ if ((eventType != CharacteristicValueChangedEvent) || !eventOutParameter || !context)
+ return;
+
+ QMutexLocker locker(&controllersGuard);
+ const auto target = static_cast<QLowEnergyControllerPrivateWin32 *>(context);
+ if (!qControllers->contains(target))
+ return;
+
+ CharactericticValueEvent *e = new CharactericticValueEvent(
+ reinterpret_cast<const PBLUETOOTH_GATT_VALUE_CHANGED_EVENT>(eventOutParameter));
+
+ QCoreApplication::postEvent(target, e);
+}
+
+static HANDLE registerEvent(
+ HANDLE hService, BTH_LE_GATT_CHARACTERISTIC gattCharacteristic,
+ PVOID context, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return INVALID_HANDLE_VALUE;
+ }
+
+ HANDLE hEvent = INVALID_HANDLE_VALUE;
+
+ BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION registration;
+ ::ZeroMemory(&registration, sizeof(registration));
+ registration.NumCharacteristics = 1;
+ registration.Characteristics[0] = gattCharacteristic;
+
+ const HRESULT hr = ::BluetoothGATTRegisterEvent(
+ hService,
+ CharacteristicValueChangedEvent,
+ &registration,
+ eventChangedCallbackEntry,
+ context,
+ &hEvent,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr))
+ *systemErrorCode = NO_ERROR;
+ else
+ *systemErrorCode = WIN32_FROM_HRESULT(hr);
+
+ return hEvent;
+}
+
+static void unregisterEvent(HANDLE hEvent, int *systemErrorCode)
+{
+ if (!gattFunctionsResolved) {
+ *systemErrorCode = ERROR_NOT_SUPPORTED;
+ return;
+ }
+
+ const HRESULT hr = ::BluetoothGATTUnregisterEvent(
+ hEvent,
+ BLUETOOTH_GATT_FLAG_NONE);
+
+ if (SUCCEEDED(hr))
+ *systemErrorCode = NO_ERROR;
+ else
+ *systemErrorCode = WIN32_FROM_HRESULT(hr);
+}
+
+static QBluetoothUuid qtBluetoothUuidFromNativeLeUuid(const BTH_LE_UUID &uuid)
+{
+ return uuid.IsShortUuid ? QBluetoothUuid(uuid.Value.ShortUuid)
+ : QBluetoothUuid(uuid.Value.LongUuid);
+}
+
+static BTH_LE_UUID nativeLeUuidFromQtBluetoothUuid(const QBluetoothUuid &uuid)
+{
+ BTH_LE_UUID gattUuid;
+ ::ZeroMemory(&gattUuid, sizeof(gattUuid));
+ if (uuid.minimumSize() == 2) {
+ gattUuid.IsShortUuid = TRUE;
+ gattUuid.Value.ShortUuid = uuid.data1; // other fields should be empty!
+ } else {
+ gattUuid.Value.LongUuid = uuid;
+ }
+ return gattUuid;
+}
+
+static BTH_LE_GATT_CHARACTERISTIC recoverNativeLeGattCharacteristic(
+ QLowEnergyHandle serviceHandle, QLowEnergyHandle characteristicHandle,
+ const QLowEnergyServicePrivate::CharData &characteristicData)
+{
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic;
+
+ gattCharacteristic.ServiceHandle = serviceHandle;
+ gattCharacteristic.AttributeHandle = characteristicHandle;
+ gattCharacteristic.CharacteristicValueHandle = characteristicData.valueHandle;
+
+ gattCharacteristic.CharacteristicUuid = nativeLeUuidFromQtBluetoothUuid(
+ characteristicData.uuid);
+
+ gattCharacteristic.HasExtendedProperties = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::ExtendedProperty);
+ gattCharacteristic.IsBroadcastable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Broadcasting);
+ gattCharacteristic.IsIndicatable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Indicate);
+ gattCharacteristic.IsNotifiable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Notify);
+ gattCharacteristic.IsReadable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Read);
+ gattCharacteristic.IsSignedWritable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::WriteSigned);
+ gattCharacteristic.IsWritable = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::Write);
+ gattCharacteristic.IsWritableWithoutResponse = bool(characteristicData.properties
+ & QLowEnergyCharacteristic::WriteNoResponse);
+
+ return gattCharacteristic;
+}
+
+static BTH_LE_GATT_DESCRIPTOR_TYPE nativeLeGattDescriptorTypeFromUuid(
+ const QBluetoothUuid &uuid)
+{
+ switch (uuid.toUInt16()) {
+ case QBluetoothUuid::CharacteristicExtendedProperties:
+ return CharacteristicExtendedProperties;
+ case QBluetoothUuid::CharacteristicUserDescription:
+ return CharacteristicUserDescription;
+ case QBluetoothUuid::ClientCharacteristicConfiguration:
+ return ClientCharacteristicConfiguration;
+ case QBluetoothUuid::ServerCharacteristicConfiguration:
+ return ServerCharacteristicConfiguration;
+ case QBluetoothUuid::CharacteristicPresentationFormat:
+ return CharacteristicFormat;
+ case QBluetoothUuid::CharacteristicAggregateFormat:
+ return CharacteristicAggregateFormat;
+ default:
+ return CustomDescriptor;
+ }
+}
+
+static BTH_LE_GATT_DESCRIPTOR recoverNativeLeGattDescriptor(
+ QLowEnergyHandle serviceHandle, QLowEnergyHandle characteristicHandle,
+ QLowEnergyHandle descriptorHandle,
+ const QLowEnergyServicePrivate::DescData &descriptorData)
+{
+ BTH_LE_GATT_DESCRIPTOR gattDescriptor;
+
+ gattDescriptor.ServiceHandle = serviceHandle;
+ gattDescriptor.CharacteristicHandle = characteristicHandle;
+ gattDescriptor.AttributeHandle = descriptorHandle;
+
+ gattDescriptor.DescriptorUuid = nativeLeUuidFromQtBluetoothUuid(
+ descriptorData.uuid);
+
+ gattDescriptor.DescriptorType = nativeLeGattDescriptorTypeFromUuid
+ (descriptorData.uuid);
+
+ return gattDescriptor;
+}
+
+void QLowEnergyControllerPrivateWin32::customEvent(QEvent *e)
+{
+ if (e->type() != CharactericticValueEventType)
+ return;
+
+ const CharactericticValueEvent *characteristicEvent
+ = static_cast<CharactericticValueEvent *>(e);
+
+ updateValueOfCharacteristic(characteristicEvent->m_handle,
+ characteristicEvent->m_value, false);
+
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(
+ characteristicEvent->m_handle);
+ if (service.isNull())
+ return;
+
+ const QLowEnergyCharacteristic ch(service, characteristicEvent->m_handle);
+ emit service->characteristicChanged(ch, characteristicEvent->m_value);
+}
+
+QLowEnergyControllerPrivateWin32::QLowEnergyControllerPrivateWin32()
+ : QLowEnergyControllerPrivate()
+{
+ QMutexLocker locker(&controllersGuard);
+ qControllers()->append(this);
+
+ gattFunctionsResolved = resolveFunctions(bluetoothapis());
+ if (!gattFunctionsResolved) {
+ qCWarning(QT_BT_WINDOWS) << "LE is not supported on this OS";
+ return;
+ }
+}
+
+QLowEnergyControllerPrivateWin32::~QLowEnergyControllerPrivateWin32()
+{
+ QMutexLocker locker(&controllersGuard);
+ qControllers()->removeAll(this);
+}
+
+void QLowEnergyControllerPrivateWin32::init()
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWin32::connectToDevice()
+{
+ // required to pass unit test on default backend
+ if (remoteDevice.isNull()) {
+ qWarning() << "Invalid/null remote device address";
+ setError(QLowEnergyController::UnknownRemoteDeviceError);
+ return;
+ }
+
+ if (!deviceSystemPath.isEmpty()) {
+ qCDebug(QT_BT_WINDOWS) << "Already is connected";
+ return;
+ }
+
+ setState(QLowEnergyController::ConnectingState);
+
+ deviceSystemPath =
+ QBluetoothDeviceDiscoveryAgentPrivate::discoveredLeDeviceSystemPath(
+ remoteDevice);
+
+ if (deviceSystemPath.isEmpty()) {
+ qCWarning(QT_BT_WINDOWS) << qt_error_string(ERROR_PATH_NOT_FOUND);
+ setError(QLowEnergyController::UnknownRemoteDeviceError);
+ setState(QLowEnergyController::UnconnectedState);
+ return;
+ }
+
+ setState(QLowEnergyController::ConnectedState);
+
+ 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();
+
+ Q_Q(QLowEnergyController);
+ emit q->connected();
+}
+
+void QLowEnergyControllerPrivateWin32::disconnectFromDevice()
+{
+ if (deviceSystemPath.isEmpty()) {
+ qCDebug(QT_BT_WINDOWS) << "Already is disconnected";
+ return;
+ }
+
+ setState(QLowEnergyController::ClosingState);
+ deviceSystemPath.clear();
+ setState(QLowEnergyController::UnconnectedState);
+
+ if (thread) {
+ disconnect(threadWorker, &ThreadWorker::jobFinished, this, &QLowEnergyControllerPrivateWin32::jobFinished);
+ thread->quit();
+ thread = nullptr;
+ }
+
+ for (const QSharedPointer<QLowEnergyServicePrivate> servicePrivate: serviceList)
+ closeSystemDevice(servicePrivate->hService);
+
+ Q_Q(QLowEnergyController);
+ emit q->disconnected();
+}
+
+void QLowEnergyControllerPrivateWin32::discoverServices()
+{
+ int systemErrorCode = NO_ERROR;
+
+ const HANDLE hDevice = openSystemDevice(
+ deviceSystemPath, QIODevice::ReadOnly, &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << qt_error_string(systemErrorCode);
+ setError(QLowEnergyController::NetworkError);
+ setState(QLowEnergyController::ConnectedState);
+ return;
+ }
+
+ const QVector<BTH_LE_GATT_SERVICE> foundServices =
+ enumeratePrimaryGattServices(hDevice, &systemErrorCode);
+
+ closeSystemDevice(hDevice);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << qt_error_string(systemErrorCode);
+ setError(QLowEnergyController::NetworkError);
+ setState(QLowEnergyController::ConnectedState);
+ return;
+ }
+
+ setState(QLowEnergyController::DiscoveringState);
+
+ Q_Q(QLowEnergyController);
+
+ for (const BTH_LE_GATT_SERVICE &service : foundServices) {
+ const QBluetoothUuid uuid = qtBluetoothUuidFromNativeLeUuid(
+ service.ServiceUuid);
+ qCDebug(QT_BT_WINDOWS) << "Found uuid:" << uuid;
+
+ QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate();
+ priv->uuid = uuid;
+ priv->type = QLowEnergyService::PrimaryService;
+ priv->startHandle = service.AttributeHandle;
+ priv->setController(this);
+
+ QSharedPointer<QLowEnergyServicePrivate> pointer(priv);
+ serviceList.insert(uuid, pointer);
+
+ emit q->serviceDiscovered(uuid);
+ }
+
+ setState(QLowEnergyController::DiscoveredState);
+ emit q->discoveryFinished();
+}
+
+void QLowEnergyControllerPrivateWin32::discoverServiceDetails(
+ const QBluetoothUuid &service)
+{
+ if (!serviceList.contains(service)) {
+ qCWarning(QT_BT_WINDOWS) << "Discovery of unknown service" << service.toString()
+ << "not possible";
+ return;
+ }
+
+ const QSharedPointer<QLowEnergyServicePrivate> servicePrivate =
+ serviceList.value(service);
+
+ int systemErrorCode = NO_ERROR;
+
+ // Only open a service once and close it in the QLowEnergyServicePrivate destructor
+ if (!servicePrivate->hService || servicePrivate->hService == INVALID_HANDLE_VALUE) {
+ servicePrivate->hService = openSystemService(remoteDevice, service,
+ QIODevice::ReadOnly | QIODevice::WriteOnly,
+ &systemErrorCode);
+ }
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ servicePrivate->setError(QLowEnergyService::UnknownError);
+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired);
+ return;
+ }
+
+ // We assume that the service does not have any characteristics with descriptors.
+ servicePrivate->endHandle = servicePrivate->startHandle;
+
+ const QVector<BTH_LE_GATT_CHARACTERISTIC> foundCharacteristics =
+ enumerateGattCharacteristics(servicePrivate->hService, NULL, &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get characteristics for service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ servicePrivate->setError(QLowEnergyService::CharacteristicReadError);
+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired);
+ return;
+ }
+
+ for (const BTH_LE_GATT_CHARACTERISTIC &gattCharacteristic : foundCharacteristics) {
+ const QLowEnergyHandle characteristicHandle = gattCharacteristic.AttributeHandle;
+
+ QLowEnergyServicePrivate::CharData detailsData;
+
+ detailsData.hValueChangeEvent = NULL;
+
+ detailsData.uuid = qtBluetoothUuidFromNativeLeUuid(
+ gattCharacteristic.CharacteristicUuid);
+ detailsData.valueHandle = gattCharacteristic.CharacteristicValueHandle;
+
+ QLowEnergyCharacteristic::PropertyTypes properties = QLowEnergyCharacteristic::Unknown;
+ if (gattCharacteristic.HasExtendedProperties)
+ properties |= QLowEnergyCharacteristic::ExtendedProperty;
+ if (gattCharacteristic.IsBroadcastable)
+ properties |= QLowEnergyCharacteristic::Broadcasting;
+ if (gattCharacteristic.IsIndicatable)
+ properties |= QLowEnergyCharacteristic::Indicate;
+ if (gattCharacteristic.IsNotifiable)
+ properties |= QLowEnergyCharacteristic::Notify;
+ if (gattCharacteristic.IsReadable)
+ properties |= QLowEnergyCharacteristic::Read;
+ if (gattCharacteristic.IsSignedWritable)
+ properties |= QLowEnergyCharacteristic::WriteSigned;
+ if (gattCharacteristic.IsWritable)
+ properties |= QLowEnergyCharacteristic::Write;
+ if (gattCharacteristic.IsWritableWithoutResponse)
+ properties |= QLowEnergyCharacteristic::WriteNoResponse;
+
+ detailsData.properties = properties;
+ detailsData.value = getGattCharacteristicValue(
+ servicePrivate->hService, const_cast<PBTH_LE_GATT_CHARACTERISTIC>(
+ &gattCharacteristic), &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ // We do not interrupt enumerating of characteristics
+ // if value can not be read
+ qCWarning(QT_BT_WINDOWS) << "Unable to get value for characteristic"
+ << detailsData.uuid.toString()
+ << "of the service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ }
+
+ // We assume that the characteristic has no any descriptors. So, the
+ // biggest characteristic + 1 will indicate an end handle of service.
+ servicePrivate->endHandle = std::max(
+ servicePrivate->endHandle,
+ QLowEnergyHandle(gattCharacteristic.AttributeHandle + 1));
+
+ const QVector<BTH_LE_GATT_DESCRIPTOR> foundDescriptors = enumerateGattDescriptors(
+ servicePrivate->hService, const_cast<PBTH_LE_GATT_CHARACTERISTIC>(
+ &gattCharacteristic), &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ if (systemErrorCode != ERROR_NOT_FOUND) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get descriptor for characteristic"
+ << detailsData.uuid.toString()
+ << "of the service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ servicePrivate->setError(QLowEnergyService::DescriptorReadError);
+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired);
+ return;
+ }
+ }
+
+ for (const BTH_LE_GATT_DESCRIPTOR &gattDescriptor : foundDescriptors) {
+ const QLowEnergyHandle descriptorHandle = gattDescriptor.AttributeHandle;
+
+ QLowEnergyServicePrivate::DescData data;
+ data.uuid = qtBluetoothUuidFromNativeLeUuid(
+ gattDescriptor.DescriptorUuid);
+
+ data.value = getGattDescriptorValue(servicePrivate->hService, const_cast<PBTH_LE_GATT_DESCRIPTOR>(
+ &gattDescriptor), &systemErrorCode);
+
+ if (systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get value for descriptor"
+ << data.uuid.toString()
+ << "for characteristic"
+ << detailsData.uuid.toString()
+ << "of the service" << service.toString()
+ << ":" << qt_error_string(systemErrorCode);
+ servicePrivate->setError(QLowEnergyService::DescriptorReadError);
+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired);
+ return;
+ }
+
+ // Biggest descriptor will contain an end handle of service.
+ servicePrivate->endHandle = std::max(
+ servicePrivate->endHandle,
+ QLowEnergyHandle(gattDescriptor.AttributeHandle));
+
+ detailsData.descriptorList.insert(descriptorHandle, data);
+ }
+
+ servicePrivate->characteristicList.insert(characteristicHandle, detailsData);
+ }
+
+ servicePrivate->setState(QLowEnergyService::ServiceDiscovered);
+}
+
+void QLowEnergyControllerPrivateWin32::startAdvertising(const QLowEnergyAdvertisingParameters &, const QLowEnergyAdvertisingData &, const QLowEnergyAdvertisingData &)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWin32::stopAdvertising()
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWin32::requestConnectionUpdate(const QLowEnergyConnectionParameters &)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QLowEnergyControllerPrivateWin32::readCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle)
+{
+ Q_ASSERT(!service.isNull());
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ const QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+ if (!(charDetails.properties & QLowEnergyCharacteristic::Read)) {
+ // if this succeeds the device has a bug, char is advertised as
+ // non-readable. We try to be permissive and let the remote
+ // device answer to the read attempt
+ qCWarning(QT_BT_WINDOWS) << "Reading non-readable char" << charHandle;
+ }
+
+ ReadCharData data;
+ data.systemErrorCode = NO_ERROR;
+ data.hService = service->hService;
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicReadError);
+ return;
+ }
+
+ data.gattCharacteristic = recoverNativeLeGattCharacteristic(
+ service->startHandle, charHandle, charDetails);
+
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::ReadChar;
+ job.data = QVariant::fromValue(data);
+
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
+
+void QLowEnergyControllerPrivateWin32::writeCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QByteArray &newValue,
+ QLowEnergyService::WriteMode mode)
+{
+ Q_ASSERT(!service.isNull());
+
+ if (!service->characteristicList.contains(charHandle)) {
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
+
+ WriteCharData data;
+ data.systemErrorCode = NO_ERROR;
+ data.hService = service->hService;
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
+
+ const QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+
+ data.gattCharacteristic = recoverNativeLeGattCharacteristic(
+ service->startHandle, charHandle, charDetails);
+
+ data.flags = (mode == QLowEnergyService::WriteWithResponse)
+ ? BLUETOOTH_GATT_FLAG_NONE
+ : BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE;
+
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::WriteChar;
+ data.newValue = newValue;
+ data.mode = mode;
+ job.data = QVariant::fromValue(data);
+
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
+
+void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job)
+{
+ switch (job.operation) {
+ case ThreadWorkerJob::WriteChar:
+ {
+ const WriteCharData data = job.data.value<WriteCharData>();
+ 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 (data.mode == QLowEnergyService::WriteWithResponse) {
+ const QLowEnergyCharacteristic ch = characteristicForHandle(charHandle);
+ emit service->characteristicWritten(ch, data.newValue);
+ }
+ }
+ break;
+ case ThreadWorkerJob::ReadChar:
+ {
+ const ReadCharData data = job.data.value<ReadCharData>();
+ 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 get value for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicReadError);
+ return;
+ }
+
+ updateValueOfCharacteristic(charHandle, data.value, false);
+
+ const QLowEnergyCharacteristic ch(service, charHandle);
+ emit service->characteristicRead(ch, data.value);
+ }
+ break;
+ case ThreadWorkerJob::WriteDescr:
+ {
+ WriteDescData data = job.data.value<WriteDescData>();
+ const QLowEnergyHandle descriptorHandle = static_cast<QLowEnergyHandle>(data.gattDescriptor.AttributeHandle);
+ const QLowEnergyHandle charHandle = static_cast<QLowEnergyHandle>(data.gattDescriptor.CharacteristicHandle);
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle];
+ const QLowEnergyServicePrivate::DescData &dscrDetails = charDetails.descriptorList[descriptorHandle];
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to set value for descriptor"
+ << dscrDetails.uuid.toString()
+ << "for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+
+ if (data.gattDescriptor.DescriptorType == ClientCharacteristicConfiguration) {
+
+ QDataStream in(data.newValue);
+ quint8 u;
+ in >> u;
+
+ if (u & ClientCharacteristicConfigurationValue::UseNotifications
+ || u & ClientCharacteristicConfigurationValue::UseIndications) {
+ if (!charDetails.hValueChangeEvent) {
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic = recoverNativeLeGattCharacteristic(
+ service->startHandle, charHandle, charDetails);
+
+ // note: if the service handle is closed the event registration is no longer valid.
+ charDetails.hValueChangeEvent = registerEvent(
+ data.hService, gattCharacteristic, this, &data.systemErrorCode);
+ }
+ } else {
+ if (charDetails.hValueChangeEvent) {
+ unregisterEvent(charDetails.hValueChangeEvent, &data.systemErrorCode);
+ charDetails.hValueChangeEvent = NULL;
+ }
+ }
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to subscribe events for descriptor"
+ << dscrDetails.uuid.toString()
+ << "for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+ }
+
+ updateValueOfDescriptor(charHandle, descriptorHandle, data.newValue, false);
+
+ const QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle);
+ emit service->descriptorWritten(dscr, data.newValue);
+ }
+ break;
+ case ThreadWorkerJob::ReadDescr:
+ {
+ ReadDescData data = job.data.value<ReadDescData>();
+ const QLowEnergyHandle descriptorHandle = static_cast<QLowEnergyHandle>(data.gattDescriptor.AttributeHandle);
+ const QLowEnergyHandle charHandle = static_cast<QLowEnergyHandle>(data.gattDescriptor.CharacteristicHandle);
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle];
+ const QLowEnergyServicePrivate::DescData &dscrDetails = charDetails.descriptorList[descriptorHandle];
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to get value for descriptor"
+ << dscrDetails.uuid.toString()
+ << "for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorReadError);
+ return;
+ }
+
+ updateValueOfDescriptor(charHandle, descriptorHandle, data.value, false);
+
+ QLowEnergyDescriptor dscr(service, charHandle, descriptorHandle);
+ emit service->descriptorRead(dscr, data.value);
+ }
+ break;
+ }
+
+ QMetaObject::invokeMethod(threadWorker, "runPendingJob", Qt::QueuedConnection);
+}
+
+void QLowEnergyControllerPrivateWin32::readDescriptor(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle)
+{
+ Q_ASSERT(!service.isNull());
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ const QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+ if (!charDetails.descriptorList.contains(descriptorHandle))
+ return;
+
+ ReadDescData data;
+ data.systemErrorCode = NO_ERROR;
+ data.hService = service->hService;
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorReadError);
+ return;
+ }
+
+ const QLowEnergyServicePrivate::DescData &dscrDetails
+ = charDetails.descriptorList[descriptorHandle];
+
+ data.gattDescriptor = recoverNativeLeGattDescriptor(
+ service->startHandle, charHandle, descriptorHandle, dscrDetails);
+
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::ReadDescr;
+ job.data = QVariant::fromValue(data);
+
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
+
+void QLowEnergyControllerPrivateWin32::writeDescriptor(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue)
+{
+ Q_ASSERT(!service.isNull());
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+ if (!charDetails.descriptorList.contains(descriptorHandle))
+ return;
+
+ WriteDescData data;
+ data.systemErrorCode = NO_ERROR;
+ data.newValue = newValue;
+ data.hService = service->hService;
+
+ if (data.systemErrorCode != NO_ERROR) {
+ qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+
+ const QLowEnergyServicePrivate::DescData &dscrDetails
+ = charDetails.descriptorList[descriptorHandle];
+
+ data.gattDescriptor = recoverNativeLeGattDescriptor(
+ service->startHandle, charHandle, descriptorHandle, dscrDetails);
+
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::WriteDescr;
+ job.data = QVariant::fromValue(data);
+
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
+
+void QLowEnergyControllerPrivateWin32::addToGenericAttributeList(const QLowEnergyServiceData &, QLowEnergyHandle)
+{
+ 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:
+ {
+ ReadCharData data = job.data.value<ReadCharData>();
+ data.value = getGattCharacteristicValue(
+ data.hService, &data.gattCharacteristic, &data.systemErrorCode);
+ job.data = QVariant::fromValue(data);
+ }
+ break;
+ case ThreadWorkerJob::WriteDescr:
+ {
+ WriteDescData data = job.data.value<WriteDescData>();
+ setGattDescriptorValue(data.hService, &data.gattDescriptor,
+ data.newValue, &data.systemErrorCode);
+ job.data = QVariant::fromValue(data);
+ }
+ break;
+ case ThreadWorkerJob::ReadDescr:
+ {
+ ReadDescData data = job.data.value<ReadDescData>();
+ data.value = getGattDescriptorValue(
+ data.hService,
+ const_cast<PBTH_LE_GATT_DESCRIPTOR>(&data.gattDescriptor),
+ &data.systemErrorCode);
+ job.data = QVariant::fromValue(data);
+ }
+ 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
new file mode 100644
index 00000000..f8e3b10a
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller_win_p.h
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOWENERGYCONTROLLERPRIVATE_WIN32_P_H
+#define QLOWENERGYCONTROLLERPRIVATE_WIN32_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include <QtCore/QQueue>
+#include <QtCore/QVector>
+#include <QtBluetooth/qbluetooth.h>
+#include <QtBluetooth/qlowenergycharacteristic.h>
+#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;
+};
+
+struct WriteCharData
+{
+ QByteArray newValue;
+ QLowEnergyService::WriteMode mode;
+ HANDLE hService;
+ DWORD flags;
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic;
+ int systemErrorCode;
+};
+
+struct ReadCharData
+{
+ QByteArray value;
+ HANDLE hService;
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic;
+ int systemErrorCode;
+};
+
+struct WriteDescData
+{
+ QByteArray newValue;
+ HANDLE hService;
+ BTH_LE_GATT_DESCRIPTOR gattDescriptor;
+ int systemErrorCode;
+};
+
+struct ReadDescData
+{
+ QByteArray value;
+ HANDLE hService;
+ BTH_LE_GATT_DESCRIPTOR gattDescriptor;
+ int systemErrorCode;
+};
+
+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();
+
+class QLowEnergyControllerPrivateWin32 : public QLowEnergyControllerPrivate
+{
+ Q_OBJECT
+public:
+ QLowEnergyControllerPrivateWin32();
+ ~QLowEnergyControllerPrivateWin32();
+
+ void init() override;
+
+ void connectToDevice() override;
+ void disconnectFromDevice() override;
+
+ void discoverServices() override;
+ void discoverServiceDetails(const QBluetoothUuid &service) override;
+
+ void startAdvertising(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData) override;
+ void stopAdvertising() override;
+
+ void requestConnectionUpdate(const QLowEnergyConnectionParameters &params) override;
+
+ // read data
+ void readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle) override;
+ void readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle) override;
+
+ // write data
+ void writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QByteArray &newValue, QLowEnergyService::WriteMode mode) override;
+ void writeDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue) override;
+
+ 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;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(ThreadWorkerJob)
+Q_DECLARE_METATYPE(WriteCharData)
+Q_DECLARE_METATYPE(ReadCharData)
+Q_DECLARE_METATYPE(WriteDescData)
+Q_DECLARE_METATYPE(ReadDescData)
+
+#endif // QLOWENERGYCONTROLLERPRIVATE_WIN32__P_H
diff --git a/src/bluetooth/qlowenergydescriptor.h b/src/bluetooth/qlowenergydescriptor.h
index 62ca5fd3..adfe1203 100644
--- a/src/bluetooth/qlowenergydescriptor.h
+++ b/src/bluetooth/qlowenergydescriptor.h
@@ -84,6 +84,7 @@ protected:
friend class QLowEnergyControllerPrivateBluezDBus;
friend class QLowEnergyControllerPrivateCommon;
friend class QLowEnergyControllerPrivateOSX;
+ friend class QLowEnergyControllerPrivateWin32;
friend class QLowEnergyControllerPrivateWinRT;
friend class QLowEnergyControllerPrivateWinRTNew;
QLowEnergyDescriptorPrivate *data = nullptr;
diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h
index fb4163a0..226af145 100644
--- a/src/bluetooth/qlowenergyserviceprivate_p.h
+++ b/src/bluetooth/qlowenergyserviceprivate_p.h
@@ -60,6 +60,9 @@
#if defined(QT_ANDROID_BLUETOOTH)
#include <QtAndroidExtras/QAndroidJniObject>
#endif
+#if defined(QT_WIN_BLUETOOTH)
+#include <qt_windows.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -83,6 +86,9 @@ public:
QLowEnergyCharacteristic::PropertyTypes properties;
QByteArray value;
QHash<QLowEnergyHandle, DescData> descriptorList;
+#ifdef QT_WIN_BLUETOOTH
+ Qt::HANDLE hValueChangeEvent;
+#endif
};
enum GattAttributeTypes {
@@ -128,6 +134,9 @@ public:
// reference to the BluetoothGattService object
QAndroidJniObject androidService;
#endif
+#if defined(QT_WIN_BLUETOOTH)
+ Qt::HANDLE hService = nullptr;
+#endif
};
diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h
new file mode 100644
index 00000000..39b88a5f
--- /dev/null
+++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINLOWENERGYBLUETOOTH_P_H
+#define QWINLOWENERGYBLUETOOTH_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qlibrary.h>
+
+#include <qt_windows.h>
+
+#define WIN32_FROM_HRESULT(hr) \
+ (SUCCEEDED(hr) ? ERROR_SUCCESS : \
+ (HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : (hr)))
+
+#define BLUETOOTH_GATT_FLAG_NONE 0x00000000
+#define BLUETOOTH_GATT_FLAG_CONNECTION_ENCRYPTED 0x00000001
+#define BLUETOOTH_GATT_FLAG_CONNECTION_AUTHENTICATED 0x00000002
+#define BLUETOOTH_GATT_FLAG_FORCE_READ_FROM_DEVICE 0x00000004
+#define BLUETOOTH_GATT_FLAG_FORCE_READ_FROM_CACHE 0x00000008
+#define BLUETOOTH_GATT_FLAG_SIGNED_WRITE 0x00000010
+#define BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE 0x00000020
+#define BLUETOOTH_GATT_FLAG_RETURN_ALL 0x00000040
+
+typedef enum _BTH_LE_GATT_DESCRIPTOR_TYPE {
+ CharacteristicExtendedProperties,
+ CharacteristicUserDescription,
+ ClientCharacteristicConfiguration,
+ ServerCharacteristicConfiguration,
+ CharacteristicFormat,
+ CharacteristicAggregateFormat,
+ CustomDescriptor
+} BTH_LE_GATT_DESCRIPTOR_TYPE, *PBTH_LE_GATT_DESCRIPTOR_TYPE;
+
+typedef enum _BTH_LE_GATT_EVENT_TYPE {
+ CharacteristicValueChangedEvent
+} BTH_LE_GATT_EVENT_TYPE;
+
+typedef struct _BTH_LE_UUID {
+ BOOLEAN IsShortUuid;
+ union {
+ USHORT ShortUuid;
+ GUID LongUuid;
+ } Value;
+} BTH_LE_UUID, *PBTH_LE_UUID;
+
+typedef struct _BTH_LE_GATT_SERVICE {
+ BTH_LE_UUID ServiceUuid;
+ USHORT AttributeHandle;
+} BTH_LE_GATT_SERVICE, *PBTH_LE_GATT_SERVICE;
+
+typedef struct _BTH_LE_GATT_CHARACTERISTIC {
+ USHORT ServiceHandle;
+ BTH_LE_UUID CharacteristicUuid;
+ USHORT AttributeHandle;
+ USHORT CharacteristicValueHandle;
+ BOOLEAN IsBroadcastable;
+ BOOLEAN IsReadable;
+ BOOLEAN IsWritable;
+ BOOLEAN IsWritableWithoutResponse;
+ BOOLEAN IsSignedWritable;
+ BOOLEAN IsNotifiable;
+ BOOLEAN IsIndicatable;
+ BOOLEAN HasExtendedProperties;
+} BTH_LE_GATT_CHARACTERISTIC, *PBTH_LE_GATT_CHARACTERISTIC;
+
+typedef struct _BTH_LE_GATT_CHARACTERISTIC_VALUE {
+ ULONG DataSize;
+ UCHAR Data[1];
+} BTH_LE_GATT_CHARACTERISTIC_VALUE, *PBTH_LE_GATT_CHARACTERISTIC_VALUE;
+
+typedef struct _BTH_LE_GATT_DESCRIPTOR {
+ USHORT ServiceHandle;
+ USHORT CharacteristicHandle;
+ BTH_LE_GATT_DESCRIPTOR_TYPE DescriptorType;
+ BTH_LE_UUID DescriptorUuid;
+ USHORT AttributeHandle;
+} BTH_LE_GATT_DESCRIPTOR, *PBTH_LE_GATT_DESCRIPTOR;
+
+typedef struct _BTH_LE_GATT_DESCRIPTOR_VALUE {
+ BTH_LE_GATT_DESCRIPTOR_TYPE DescriptorType;
+ BTH_LE_UUID DescriptorUuid;
+ union {
+ struct {
+ BOOLEAN IsReliableWriteEnabled;
+ BOOLEAN IsAuxiliariesWritable;
+ } CharacteristicExtendedProperties;
+ struct {
+ BOOLEAN IsSubscribeToNotification;
+ BOOLEAN IsSubscribeToIndication;
+ } ClientCharacteristicConfiguration;
+ struct {
+ BOOLEAN IsBroadcast;
+ } ServerCharacteristicConfiguration;
+ struct {
+ UCHAR Format;
+ UCHAR Exponent;
+ BTH_LE_UUID Unit;
+ UCHAR NameSpace;
+ BTH_LE_UUID Description;
+ } CharacteristicFormat;
+ };
+ ULONG DataSize;
+ UCHAR Data[1];
+} BTH_LE_GATT_DESCRIPTOR_VALUE, *PBTH_LE_GATT_DESCRIPTOR_VALUE;
+
+typedef struct _BLUETOOTH_GATT_VALUE_CHANGED_EVENT {
+ USHORT ChangedAttributeHandle;
+ size_t CharacteristicValueDataSize;
+ PBTH_LE_GATT_CHARACTERISTIC_VALUE CharacteristicValue;
+} BLUETOOTH_GATT_VALUE_CHANGED_EVENT, *PBLUETOOTH_GATT_VALUE_CHANGED_EVENT;
+
+typedef struct _BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION {
+ USHORT NumCharacteristics;
+ BTH_LE_GATT_CHARACTERISTIC Characteristics[1];
+} BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION, *PBLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION;
+
+typedef VOID (CALLBACK *PFNBLUETOOTH_GATT_EVENT_CALLBACK)(
+ BTH_LE_GATT_EVENT_TYPE EventType,
+ PVOID EventOutParameter,
+ PVOID Context
+ );
+
+typedef ULONG64 BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, *PBTH_LE_GATT_RELIABLE_WRITE_CONTEXT;
+
+#define DEFINEFUNC(ret, func, ...) \
+ typedef ret (WINAPI *fp_##func)(__VA_ARGS__); \
+ static fp_##func func;
+
+#define RESOLVEFUNC(func) \
+ func = (fp_##func)resolveFunction(library, #func); \
+ if (!func) \
+ return false;
+
+DEFINEFUNC(HRESULT, BluetoothGATTGetServices, HANDLE, USHORT, PBTH_LE_GATT_SERVICE, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetIncludedServices, HANDLE, PBTH_LE_GATT_SERVICE, USHORT, PBTH_LE_GATT_SERVICE, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetCharacteristics, HANDLE, PBTH_LE_GATT_SERVICE, USHORT, PBTH_LE_GATT_CHARACTERISTIC, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetDescriptors, HANDLE, PBTH_LE_GATT_CHARACTERISTIC, USHORT, PBTH_LE_GATT_DESCRIPTOR, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetCharacteristicValue, HANDLE, PBTH_LE_GATT_CHARACTERISTIC, ULONG, PBTH_LE_GATT_CHARACTERISTIC_VALUE, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTGetDescriptorValue, HANDLE, PBTH_LE_GATT_DESCRIPTOR, ULONG, PBTH_LE_GATT_DESCRIPTOR_VALUE, PUSHORT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTBeginReliableWrite, HANDLE, PBTH_LE_GATT_RELIABLE_WRITE_CONTEXT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTEndReliableWrite, HANDLE, BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTAbortReliableWrite, HANDLE, BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTSetCharacteristicValue, HANDLE, PBTH_LE_GATT_CHARACTERISTIC, PBTH_LE_GATT_CHARACTERISTIC_VALUE, BTH_LE_GATT_RELIABLE_WRITE_CONTEXT, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTSetDescriptorValue, HANDLE, PBTH_LE_GATT_DESCRIPTOR, PBTH_LE_GATT_DESCRIPTOR_VALUE, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTRegisterEvent, HANDLE, BTH_LE_GATT_EVENT_TYPE, PVOID, PFNBLUETOOTH_GATT_EVENT_CALLBACK, PVOID, PHANDLE, ULONG)
+DEFINEFUNC(HRESULT, BluetoothGATTUnregisterEvent, HANDLE, ULONG)
+
+static inline QFunctionPointer resolveFunction(QLibrary *library, const char *func)
+{
+ QFunctionPointer symbolFunctionPointer = library->resolve(func);
+ if (!symbolFunctionPointer)
+ qWarning("Cannot resolve '%s' in '%s'.", func, qPrintable(library->fileName()));
+ return symbolFunctionPointer;
+}
+
+static inline bool resolveFunctions(QLibrary *library)
+{
+ if (!library->isLoaded()) {
+ library->setFileName(QStringLiteral("bluetoothapis"));
+ if (!library->load()) {
+ qWarning("Unable to load '%s' library.", qPrintable(library->fileName()));
+ return false;
+ }
+ }
+
+ RESOLVEFUNC(BluetoothGATTGetServices)
+ RESOLVEFUNC(BluetoothGATTGetIncludedServices)
+ RESOLVEFUNC(BluetoothGATTGetCharacteristics)
+ RESOLVEFUNC(BluetoothGATTGetDescriptors)
+ RESOLVEFUNC(BluetoothGATTGetCharacteristicValue)
+ RESOLVEFUNC(BluetoothGATTGetDescriptorValue)
+ RESOLVEFUNC(BluetoothGATTBeginReliableWrite)
+ RESOLVEFUNC(BluetoothGATTEndReliableWrite)
+ RESOLVEFUNC(BluetoothGATTAbortReliableWrite)
+ RESOLVEFUNC(BluetoothGATTSetCharacteristicValue)
+ RESOLVEFUNC(BluetoothGATTSetDescriptorValue)
+ RESOLVEFUNC(BluetoothGATTRegisterEvent)
+ RESOLVEFUNC(BluetoothGATTUnregisterEvent)
+
+ return true;
+}
+
+#endif // QWINLOWENERGYBLUETOOTH_P_H
diff --git a/src/bluetooth/windows/windows.pri b/src/bluetooth/windows/windows.pri
new file mode 100644
index 00000000..bf35eaa4
--- /dev/null
+++ b/src/bluetooth/windows/windows.pri
@@ -0,0 +1,2 @@
+PRIVATE_HEADERS += \
+ windows/qwinlowenergybluetooth_p.h