diff options
author | Denis Shienkov <denis.shienkov@gmail.com> | 2014-12-08 19:47:03 +0300 |
---|---|---|
committer | Denis Shienkov <denis.shienkov@gmail.com> | 2014-12-10 13:42:47 +0100 |
commit | fd96b0aa5ae37188f63e6bd1503024c1b5b3cf60 (patch) | |
tree | 63fdd9a4c000fe44dab13b58f361709a902f13a1 /src | |
parent | ee9a7a91abda21d66a3162da1273bbe1e69adf23 (diff) |
Add discovering of primary GATT services on Windows
It is still partially implemented where are discovered
only the primary services UUID's.
Change-Id: I2765a8f83be4716cfe875a2f7a3593ba1af211a4
Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/bluetooth/bluetooth.pro | 2 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_p.h | 17 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_win.cpp | 249 | ||||
-rw-r--r-- | src/bluetooth/windows/qwinlowenergybluetooth.cpp | 31 | ||||
-rw-r--r-- | src/bluetooth/windows/qwinlowenergybluetooth_p.h | 9 |
5 files changed, 307 insertions, 1 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index db8d2e21..334ff600 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -207,7 +207,7 @@ config_bluez:qtHaveModule(dbus) { qbluetoothservicediscoveryagent_win.cpp \ qbluetoothsocket_win.cpp \ qbluetoothserver_win.cpp \ - qlowenergycontroller_p.cpp + qlowenergycontroller_win.cpp LIBS += -lbthprops -lsetupapi diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h index 6234b57f..273ccf84 100644 --- a/src/bluetooth/qlowenergycontroller_p.h +++ b/src/bluetooth/qlowenergycontroller_p.h @@ -65,6 +65,12 @@ QT_END_NAMESPACE #include "android/lowenergynotificationhub_p.h" #endif +#if defined(Q_OS_WIN32) +#include <QtConcurrent> +#include <QtBluetooth/qbluetoothuuid.h> +#include "windows/qwinlowenergybluetooth_p.h" +#endif + QT_BEGIN_NAMESPACE #if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE) @@ -211,6 +217,17 @@ private slots: void descriptorWritten(int descHandle, const QByteArray &data, QLowEnergyService::ServiceError errorCode); void characteristicChanged(int charHandle, const QByteArray &data); + +#elif defined(Q_OS_WIN32) +private slots: + void primaryServicesDiscoveryCompleted(); + +private: + void startDiscoveryOfPrimaryServices(); + bool isConnected() const; + + HANDLE hRemoteDevice; + QFutureWatcher<WinLowEnergyBluetooth::ServicesDiscoveryResult> *primaryServicesDiscoveryWatcher; #endif private: QLowEnergyController *q_ptr; diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp new file mode 100644 index 00000000..508e8e53 --- /dev/null +++ b/src/bluetooth/qlowenergycontroller_win.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlowenergycontroller_p.h" +#include <QtCore/QLoggingCategory> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) + +QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() + : QObject(), + state(QLowEnergyController::UnconnectedState), + error(QLowEnergyController::NoError), + hRemoteDevice(INVALID_HANDLE_VALUE), + primaryServicesDiscoveryWatcher(0) +{ +} + +QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate() +{ +} + +void QLowEnergyControllerPrivate::connectToDevice() +{ + Q_Q(QLowEnergyController); + + // required to pass unit test on default backend + if (remoteDevice.isNull()) { + qWarning() << "Invalid/null remote device address"; + setError(QLowEnergyController::UnknownRemoteDeviceError); + return; + } + + if (!WinLowEnergyBluetooth::isSupported()) { + qWarning() << "Low energy is not supported by OS"; + setError(QLowEnergyController::UnknownError); + return; + } + + if (isConnected()) { + qCDebug(QT_BT_WINDOWS) << "Already is connected"; + return; + } + + setState(QLowEnergyController::ConnectingState); + + const WinLowEnergyBluetooth::DeviceDiscoveryResult result = + WinLowEnergyBluetooth::startDiscoveryOfRemoteDevices(); + + if (result.error != NO_ERROR + && result.error != ERROR_NO_MORE_ITEMS) { + qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); + setError(QLowEnergyController::UnknownRemoteDeviceError); + setState(QLowEnergyController::UnconnectedState); + return; + } + + foreach (const WinLowEnergyBluetooth::DeviceInfo &info, result.devices) { + + if (info.address != remoteDevice) + continue; + + const DWORD desiredAccess = GENERIC_READ | GENERIC_WRITE; + const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + + hRemoteDevice = ::CreateFile( + reinterpret_cast<const wchar_t *>(info.systemPath.utf16()), + desiredAccess, + shareMode, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (hRemoteDevice == INVALID_HANDLE_VALUE) { + qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + return; + } + + setState(QLowEnergyController::ConnectedState); + emit q->connected(); + return; + } + + qCWarning(QT_BT_WINDOWS) << qt_error_string(ERROR_FILE_NOT_FOUND); + setError(QLowEnergyController::UnknownRemoteDeviceError); + setState(QLowEnergyController::UnconnectedState); +} + +void QLowEnergyControllerPrivate::disconnectFromDevice() +{ + Q_Q(QLowEnergyController); + + if (!WinLowEnergyBluetooth::isSupported()) { + qWarning() << "Low energy is not supported by OS"; + setError(QLowEnergyController::UnknownError); + return; + } + + if (!isConnected()) { + qCDebug(QT_BT_WINDOWS) << "Already is disconnected"; + return; + } + + setState(QLowEnergyController::ClosingState); + + if (!::CloseHandle(hRemoteDevice)) + qCWarning(QT_BT_WINDOWS) << qt_error_string(::GetLastError()); + + hRemoteDevice = INVALID_HANDLE_VALUE; + setState(QLowEnergyController::UnconnectedState); + emit q->disconnected(); +} + +void QLowEnergyControllerPrivate::discoverServices() +{ + if (!WinLowEnergyBluetooth::isSupported()) { + qWarning() << "Low energy is not supported by OS"; + setError(QLowEnergyController::UnknownError); + return; + } + + startDiscoveryOfPrimaryServices(); +} + +void QLowEnergyControllerPrivate::discoverServiceDetails( + const QBluetoothUuid &/*service*/) +{ + +} + +void QLowEnergyControllerPrivate::writeCharacteristic( + const QSharedPointer<QLowEnergyServicePrivate> /*service*/, + const QLowEnergyHandle /*charHandle*/, + const QByteArray &/*newValue*/, + bool /*writeWithResponse*/) +{ + +} + +void QLowEnergyControllerPrivate::writeDescriptor( + const QSharedPointer<QLowEnergyServicePrivate> /*service*/, + const QLowEnergyHandle /*charHandle*/, + const QLowEnergyHandle /*descriptorHandle*/, + const QByteArray &/*newValue*/) +{ + +} + +void QLowEnergyControllerPrivate::primaryServicesDiscoveryCompleted() +{ + Q_Q(QLowEnergyController); + + const WinLowEnergyBluetooth::ServicesDiscoveryResult result = + primaryServicesDiscoveryWatcher->result(); + + if (result.error != NO_ERROR) { + qCWarning(QT_BT_WINDOWS) << qt_error_string(result.error); + setError(QLowEnergyController::UnknownError); + return; + } + + foreach (const WinLowEnergyBluetooth::BTH_LE_GATT_SERVICE &service, + result.services) { + + const QBluetoothUuid uuid( + service.ServiceUuid.IsShortUuid + ? QBluetoothUuid(service.ServiceUuid.Value.ShortUuid) + : QBluetoothUuid(service.ServiceUuid.Value.LongUuid)); + + 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 QLowEnergyControllerPrivate::startDiscoveryOfPrimaryServices() +{ + if (!primaryServicesDiscoveryWatcher) { + primaryServicesDiscoveryWatcher = new QFutureWatcher< + WinLowEnergyBluetooth::ServicesDiscoveryResult>(this); + + QObject::connect(primaryServicesDiscoveryWatcher, SIGNAL(finished()), + this, SLOT(primaryServicesDiscoveryCompleted())); + } + + if (primaryServicesDiscoveryWatcher->isRunning()) + return; + + const QFuture<WinLowEnergyBluetooth::ServicesDiscoveryResult> future = + QtConcurrent::run( + WinLowEnergyBluetooth::startDiscoveryOfPrimaryServices, + hRemoteDevice); + + primaryServicesDiscoveryWatcher->setFuture(future); +} + +bool QLowEnergyControllerPrivate::isConnected() const +{ + return hRemoteDevice && (hRemoteDevice != INVALID_HANDLE_VALUE); +} + +QT_END_NAMESPACE diff --git a/src/bluetooth/windows/qwinlowenergybluetooth.cpp b/src/bluetooth/windows/qwinlowenergybluetooth.cpp index 275c5f64..5fe4e16b 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth.cpp +++ b/src/bluetooth/windows/qwinlowenergybluetooth.cpp @@ -182,6 +182,11 @@ DeviceDiscoveryResult::DeviceDiscoveryResult() { } +ServicesDiscoveryResult::ServicesDiscoveryResult() + : error(NO_ERROR) +{ +} + static DeviceDiscoveryResult availableSystemInterfaces( const GUID &deviceInterface) { @@ -312,6 +317,32 @@ DeviceDiscoveryResult startDiscoveryOfRemoteDevices() QUuid("781aee18-7733-4ce4-add0-91f41c67b592")); } +ServicesDiscoveryResult startDiscoveryOfPrimaryServices( + HANDLE hDevice) +{ + ServicesDiscoveryResult result; + USHORT servicesCount = 0; + forever { + const HRESULT hr = BluetoothGATTGetServices( + hDevice, + result.services.count(), + result.services.isEmpty() ? NULL : &result.services[0], + &servicesCount, + BLUETOOTH_GATT_FLAG_NONE); + + if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) { + result.services.resize(servicesCount); + } else if (hr == S_OK) { + break; + } else { + result.error = ::GetLastError(); + result.services.clear(); + break; + } + } + return result; +} + bool isSupported() { static bool resolved = resolveFunctions(bluetoothapis()); diff --git a/src/bluetooth/windows/qwinlowenergybluetooth_p.h b/src/bluetooth/windows/qwinlowenergybluetooth_p.h index d7987966..cf83f331 100644 --- a/src/bluetooth/windows/qwinlowenergybluetooth_p.h +++ b/src/bluetooth/windows/qwinlowenergybluetooth_p.h @@ -36,6 +36,7 @@ #define QWINLOWENERGYBLUETOOTH_P_H #include <QtCore/qstringlist.h> +#include <QtCore/qvector.h> #include <QtBluetooth/qbluetoothaddress.h> @@ -173,10 +174,18 @@ struct DeviceDiscoveryResult DWORD error; }; +struct ServicesDiscoveryResult +{ + ServicesDiscoveryResult(); + QVector<BTH_LE_GATT_SERVICE> services; + DWORD error; +}; + bool isSupported(); bool hasLocalRadio(); DeviceDiscoveryResult startDiscoveryOfRemoteDevices(); +ServicesDiscoveryResult startDiscoveryOfPrimaryServices(HANDLE hDevice); } // namespace WinLowEnergyBluetooth |