summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDenis Shienkov <denis.shienkov@gmail.com>2014-12-08 19:47:03 +0300
committerDenis Shienkov <denis.shienkov@gmail.com>2014-12-10 13:42:47 +0100
commitfd96b0aa5ae37188f63e6bd1503024c1b5b3cf60 (patch)
tree63fdd9a4c000fe44dab13b58f361709a902f13a1 /src
parentee9a7a91abda21d66a3162da1273bbe1e69adf23 (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.pro2
-rw-r--r--src/bluetooth/qlowenergycontroller_p.h17
-rw-r--r--src/bluetooth/qlowenergycontroller_win.cpp249
-rw-r--r--src/bluetooth/windows/qwinlowenergybluetooth.cpp31
-rw-r--r--src/bluetooth/windows/qwinlowenergybluetooth_p.h9
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