summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTimur Pocheptsov <Timur.Pocheptsov@digia.com>2014-11-25 16:11:48 +0100
committerTimur Pocheptsov <Timur.Pocheptsov@digia.com>2014-12-01 12:10:36 +0100
commit63a158b2d53a4f18f118a95796c0386911e1e5fa (patch)
tree0c39dafd71c760fd4fdc6a1fbfc4063aeb000553 /src
parentae3d26661aeaa2917ed35e5892648dea4caced04 (diff)
QLowEnergyController - writeDescriptor (OS X/iOS)
Core Bluetooth - based implementation. Change-Id: Ie642a13ae9a4d75401dd10648fac6eeee4123a3b Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'src')
-rw-r--r--src/bluetooth/osx/osxbtcentralmanager.mm117
-rw-r--r--src/bluetooth/osx/osxbtcentralmanager_p.h27
-rw-r--r--src/bluetooth/qlowenergycontroller_osx.mm89
-rw-r--r--src/bluetooth/qlowenergycontroller_osx_p.h10
-rw-r--r--src/bluetooth/qlowenergydescriptor.h1
-rw-r--r--src/bluetooth/qlowenergyservice_osx.mm26
6 files changed, 241 insertions, 29 deletions
diff --git a/src/bluetooth/osx/osxbtcentralmanager.mm b/src/bluetooth/osx/osxbtcentralmanager.mm
index da4e6c34..35a284c2 100644
--- a/src/bluetooth/osx/osxbtcentralmanager.mm
+++ b/src/bluetooth/osx/osxbtcentralmanager.mm
@@ -99,6 +99,7 @@ using namespace QT_NAMESPACE;
- (void)discoverIncludedServices;
- (void)readCharacteristics:(CBService *)service;
- (void)serviceDetailsDiscoveryFinished:(CBService *)service;
+- (void)performNextWriteRequest;
// Aux. functions.
- (CBService *)serviceForUUID:(const QBluetoothUuid &)qtUuid;
@@ -110,6 +111,9 @@ using namespace QT_NAMESPACE;
- (CBDescriptor *)nextDescriptorForCharacteristic:(CBCharacteristic *)characteristic
startingFrom:(CBDescriptor *)descriptor;
+// TODO: check _what_ exactly I have to reset ...
+- (void)reset;
+
@end
@implementation QT_MANGLE_NAMESPACE(OSXBTCentralManager)
@@ -126,6 +130,7 @@ using namespace QT_NAMESPACE;
delegate = aDelegate;
currentService = 0;
lastValidHandle = 0;
+ writePending = false;
}
return self;
@@ -272,7 +277,7 @@ using namespace QT_NAMESPACE;
- (void)disconnectFromDevice
{
- servicesToDiscoverDetails.clear();
+ [self reset];
if (managerState == OSXBluetooth::CentralManagerUpdating) {
disconnectPending = true;
@@ -306,11 +311,6 @@ using namespace QT_NAMESPACE;
//
// ... but we'd like to have them all:
- lastValidHandle = 0;
- serviceMap.clear();
- charMap.clear();
- descMap.clear();
-
[peripheral setDelegate:self];
managerState = OSXBluetooth::CentralManagerDiscovering;
[peripheral discoverServices:nil];
@@ -546,6 +546,41 @@ using namespace QT_NAMESPACE;
delegate->serviceDetailsDiscoveryFinished(qtService);
}
+- (void)performNextWriteRequest
+{
+ using namespace OSXBluetooth;
+
+ Q_ASSERT_X(peripheral, "-performNextWriteRequest",
+ "invalid peripheral (nil)");
+
+ if (writePending || !writeQueue.size())
+ return;
+
+ LEWriteRequest request(writeQueue.dequeue());
+
+ if (request.isDescriptor) {
+ if (!descMap.contains(request.handle)) {
+ qCWarning(QT_BT_OSX) << "-performNextWriteRequest, descriptor with "
+ "handle: " << request.handle << " not found";
+ [self performNextWriteRequest];
+ }
+
+ CBDescriptor *const d = descMap[request.handle];
+ ObjCStrongReference<NSData> data(data_from_bytearray(request.value));
+ if (!data) {
+ // Even if qtData.size() == 0, we still need NSData object.
+ qCWarning(QT_BT_OSX) << "-write:descHandle:, failed "
+ "to allocate an NSData object";
+ [self performNextWriteRequest];
+ }
+
+ writePending = true;
+ return [peripheral writeValue:data.data() forDescriptor:d];
+ } else {
+ // TODO: characteristics write requests.
+ }
+}
+
- (bool)write:(const QT_PREPEND_NAMESPACE(QByteArray) &)value
charHandle:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle
withResponse:(bool)withResponse
@@ -562,7 +597,7 @@ using namespace QT_NAMESPACE;
return false;
}
- CBCharacteristic *const ch = charMap.value(charHandle);
+ CBCharacteristic *const ch = charMap[charHandle];
Q_ASSERT_X(ch, "-write:charHandle:withResponse:", "invalid characteristic (nil) for a give handle");
Q_ASSERT_X(peripheral, "-write:charHandle:withResponse:", "invalid peripheral (nil)");
@@ -581,6 +616,32 @@ using namespace QT_NAMESPACE;
return true;
}
+- (bool)write:(const QByteArray &)value descHandle:(QLowEnergyHandle)descHandle
+{
+ using namespace OSXBluetooth;
+
+ Q_ASSERT_X(descHandle, "-write:descHandle:",
+ "invalid descriptor handle (0)");
+
+ if (!descMap.contains(descHandle)) {
+ qCWarning(QT_BT_OSX) << "-write:descHandle:, descriptor with "
+ "handle: " << descHandle << " not found";
+ return false;
+ }
+
+ LEWriteRequest request;
+ request.isDescriptor = true;
+ request.handle = descHandle;
+ request.value = value;
+
+ writeQueue.enqueue(request);
+ [self performNextWriteRequest];
+ // TODO: this is quite ugly: true value can be returned after
+ // write actually. If I have any problems with the order later,
+ // I'll use performSelector afterDelay with some delay.
+ return true;
+}
+
// Aux. methods:
- (CBService *)serviceForUUID:(const QBluetoothUuid &)qtUuid
@@ -740,6 +801,17 @@ using namespace QT_NAMESPACE;
return [ds objectAtIndex:1];
}
+- (void)reset
+{
+ writePending = false;
+ writeQueue.clear();
+ servicesToDiscoverDetails.clear();
+ lastValidHandle = 0;
+ serviceMap.clear();
+ charMap.clear();
+ descMap.clear();
+}
+
// CBCentralManagerDelegate (the real one).
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
@@ -1162,7 +1234,6 @@ using namespace QT_NAMESPACE;
Q_UNUSED(aPeripheral)
Q_UNUSED(characteristic)
- Q_UNUSED(error)
Q_ASSERT_X(delegate, "-peripheral:didWriteValueForCharacteristic:error",
"invalid delegate (null)");
@@ -1181,4 +1252,34 @@ using namespace QT_NAMESPACE;
}
}
+- (void)peripheral:(CBPeripheral *)aPeripheral
+ didWriteValueForDescriptor:(CBDescriptor *)descriptor
+ error:(NSError *)error
+{
+ Q_UNUSED(aPeripheral)
+
+ using namespace OSXBluetooth;
+
+ writePending = false;
+
+ if (error) {
+ // NSLog to log the actual NSError:
+ NSLog(@"-peripheral:didWriteValueForDescriptor:error:, failed with error %@",
+ error);
+ // TODO: this error function is not good at all - it takes charHandle,
+ // which is noop at the moment and ... we actually work with a descriptor handle
+ // here.
+ delegate->error(qt_uuid(descriptor.characteristic.service.UUID), 0,
+ QLowEnergyService::DescriptorWriteError);
+ } else {
+ // We know that keys are unique, so can find a key for a given descriptor.
+ const QLowEnergyHandle dHandle = descMap.key(descriptor);
+ Q_ASSERT_X(dHandle, "-peripheral:didWriteValueForDescriptor:error:",
+ "invalid descriptor, not found in a descMap");
+ delegate->descriptorWriteNotification(dHandle, qt_bytearray(descriptor.value));
+ }
+
+ [self performNextWriteRequest];
+}
+
@end
diff --git a/src/bluetooth/osx/osxbtcentralmanager_p.h b/src/bluetooth/osx/osxbtcentralmanager_p.h
index f5692c38..2bdea3c0 100644
--- a/src/bluetooth/osx/osxbtcentralmanager_p.h
+++ b/src/bluetooth/osx/osxbtcentralmanager_p.h
@@ -47,7 +47,9 @@
#include "qbluetoothuuid.h"
#include "osxbtutility_p.h"
+#include <QtCore/qbytearray.h>
#include <QtCore/qglobal.h>
+#include <QtCore/qqueue.h>
#include <QtCore/qhash.h>
// Foundation.h must be included before corebluetoothwrapper_p.h -
@@ -61,7 +63,6 @@
QT_BEGIN_NAMESPACE
class QLowEnergyServicePrivate;
-class QByteArray;
namespace OSXBluetooth {
@@ -80,6 +81,8 @@ public:
virtual void serviceDiscoveryFinished(LEServices services) = 0;
virtual void serviceDetailsDiscoveryFinished(LEService service) = 0;
virtual void characteristicWriteNotification(LECharacteristic ch) = 0;
+ virtual void descriptorWriteNotification(QLowEnergyHandle descHandle,
+ const QByteArray &value) = 0;
virtual void disconnected() = 0;
// General errors.
@@ -114,6 +117,19 @@ typedef QHash<QLowEnergyHandle, CBService *> ServiceHash;
typedef QHash<QLowEnergyHandle, CBCharacteristic *> CharHash;
typedef QHash<QLowEnergyHandle, CBDescriptor *> DescHash;
+// Descriptor write request - we have to serialize 'concurrent' write requests.
+struct LEWriteRequest
+{
+ LEWriteRequest() : isDescriptor(false), handle(0)
+ {}
+
+ bool isDescriptor;
+ QLowEnergyHandle handle;
+ QByteArray value;
+};
+
+typedef QQueue<LEWriteRequest> WriteQueue;
+
}
QT_END_NAMESPACE
@@ -145,7 +161,10 @@ QT_END_NAMESPACE
QT_PREPEND_NAMESPACE(OSXBluetooth)::CharHash charMap;
QT_PREPEND_NAMESPACE(OSXBluetooth)::DescHash descMap;
- QLowEnergyHandle lastValidHandle;
+ QT_PREPEND_NAMESPACE(QLowEnergyHandle) lastValidHandle;
+
+ bool writePending;
+ QT_PREPEND_NAMESPACE(OSXBluetooth)::WriteQueue writeQueue;
}
- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::CentralManagerDelegate *)aDelegate;
@@ -159,12 +178,12 @@ QT_END_NAMESPACE
- (void)discoverServices;
- (bool)discoverServiceDetails:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid;
-// Characteristic's handle here is a 'relative' == valueHandle - service->startHandle
-// to simplify mapping between Qt's handles and Core Bluetooth's data structures.
- (bool)write:(const QT_PREPEND_NAMESPACE(QByteArray) &)value
charHandle:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle
withResponse:(bool)writeWithResponse;
+- (bool)write:(const QT_PREPEND_NAMESPACE(QByteArray) &)value
+ descHandle:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))descHandle;
@end
#endif
diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm
index 88ec6421..7a938ae9 100644
--- a/src/bluetooth/qlowenergycontroller_osx.mm
+++ b/src/bluetooth/qlowenergycontroller_osx.mm
@@ -123,6 +123,7 @@ UUIDList qt_servicesUuids(NSArray *services)
return uuids;
}
+// TODO: get rid of this.
QLowEnergyHandle qt_findCharacteristicHandle(QLowEnergyHandle serviceHandle,
CBService *service, CBCharacteristic *ch)
{
@@ -130,12 +131,9 @@ QLowEnergyHandle qt_findCharacteristicHandle(QLowEnergyHandle serviceHandle,
// but duplicating data structures (CB char-tree, Qt char-tree, etc.)
// is even more annoying.
- Q_ASSERT_X(serviceHandle, "qt_findCharacteristicHandle",
- "invalid service handle (0)");
- Q_ASSERT_X(service, "qt_findCharacteristicHandle",
- "invalid service (nil)");
- Q_ASSERT_X(ch, "qt_findCharacteristicHandle",
- "invalid characteristic (nil)");
+ Q_ASSERT_X(serviceHandle, "qt_findCharacteristicHandle", "invalid service handle (0)");
+ Q_ASSERT_X(service, "qt_findCharacteristicHandle", "invalid service (nil)");
+ Q_ASSERT_X(ch, "qt_findCharacteristicHandle", "invalid characteristic (nil)");
NSArray *const chars = service.characteristics;
if (!chars || !chars.count)
@@ -374,12 +372,28 @@ void QLowEnergyControllerPrivateOSX::characteristicWriteNotification(LECharacter
return;
}
- // TODO: check that this 'value' is what we need!
const QByteArray data(OSXBluetooth::qt_bytearray([ch value]));
updateValueOfCharacteristic(charHandle, data, false);
emit service->characteristicWritten(characteristic, data);
}
+void QLowEnergyControllerPrivateOSX::descriptorWriteNotification(QLowEnergyHandle dHandle, const QByteArray &value)
+{
+ Q_ASSERT_X(dHandle, "descriptorWriteNotification", "invalid descriptor handle (0)");
+
+ const QLowEnergyDescriptor qtDescriptor(descriptorForHandle(dHandle));
+ if (!qtDescriptor.isValid()) {
+ qCWarning(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::descriptorWriteNotification(), "
+ "unknown descriptor " << dHandle;
+ return;
+ }
+
+ ServicePrivate service(serviceForHandle(qtDescriptor.characteristicHandle()));
+ // TODO: test if this data is what we expected.
+ updateValueOfDescriptor(qtDescriptor.characteristicHandle(), dHandle, value, false);
+ emit service->descriptorWritten(qtDescriptor, value);
+}
+
void QLowEnergyControllerPrivateOSX::disconnected()
{
controllerState = QLowEnergyController::UnconnectedState;
@@ -547,13 +561,13 @@ void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEner
// otherwise we can not write anything at all.
if (!discoveredServices.contains(service->uuid)) {
qCWarning(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::writeCharacteristic(), "
- "no service with uuid: " << service << "found";
+ "no service with uuid: " << service->uuid << " found";
return;
}
if (!service->characteristicList.contains(charHandle)) {
qCDebug(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::writeCharacteristic(), "
- "no characteristic with handle: " << charHandle << "found";
+ "no characteristic with handle: " << charHandle << " found";
return;
}
@@ -582,13 +596,46 @@ quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHa
}
void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service,
- QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle,
+ QLowEnergyHandle descriptorHandle,
const QByteArray &newValue)
{
- Q_UNUSED(service)
- Q_UNUSED(charHandle)
- Q_UNUSED(descriptorHandle)
- Q_UNUSED(newValue)
+ Q_ASSERT_X(!service.isNull(), "writeDescriptor", "invalid service (null)");
+
+ if (!isValid()) {
+ qCWarning(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::writeDescriptor(), "
+ "invalid controller";
+ return;
+ }
+
+ // We can work only with services found on a given peripheral
+ // (== created by the given LE controller),
+ // otherwise we can not write anything at all.
+ if (!discoveredServices.contains(service->uuid)) {
+ qCWarning(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::writeDescriptor(), "
+ "no service with uuid: " << service->uuid << " found";
+ return;
+ }
+
+ if (![centralManager write:newValue descHandle:descriptorHandle])
+ service->setError(QLowEnergyService::DescriptorWriteError);
+}
+
+quint16 QLowEnergyControllerPrivateOSX::updateValueOfDescriptor(QLowEnergyHandle charHandle, QLowEnergyHandle descHandle,
+ const QByteArray &value, bool appendValue)
+{
+ ServicePrivate service(serviceForHandle(charHandle));
+ if (service.isNull() || !service->characteristicList.contains(charHandle))
+ return 0;
+
+ if (!service->characteristicList[charHandle].descriptorList.contains(descHandle))
+ return 0;
+
+ if (appendValue)
+ service->characteristicList[charHandle].descriptorList[descHandle].value += value;
+ else
+ service->characteristicList[charHandle].descriptorList[descHandle].value = value;
+
+ return service->characteristicList[charHandle].descriptorList[descHandle].value.size();
}
QSharedPointer<QLowEnergyServicePrivate> QLowEnergyControllerPrivateOSX::serviceForHandle(QLowEnergyHandle handle)
@@ -628,6 +675,20 @@ QLowEnergyCharacteristic QLowEnergyControllerPrivateOSX::characteristicForHandle
return QLowEnergyCharacteristic();
}
+QLowEnergyDescriptor QLowEnergyControllerPrivateOSX::descriptorForHandle(QLowEnergyHandle descriptorHandle)
+{
+ const QLowEnergyCharacteristic ch(characteristicForHandle(descriptorHandle));
+ if (!ch.isValid())
+ return QLowEnergyDescriptor();
+
+ const QLowEnergyServicePrivate::CharData charData = ch.d_ptr->characteristicList[ch.attributeHandle()];
+
+ if (charData.descriptorList.contains(descriptorHandle))
+ return QLowEnergyDescriptor(ch.d_ptr, ch.attributeHandle(), descriptorHandle);
+
+ return QLowEnergyDescriptor();
+}
+
void QLowEnergyControllerPrivateOSX::setErrorDescription(QLowEnergyController::Error errorCode)
{
// This function does not emit!
diff --git a/src/bluetooth/qlowenergycontroller_osx_p.h b/src/bluetooth/qlowenergycontroller_osx_p.h
index 2aec6200..df25e88a 100644
--- a/src/bluetooth/qlowenergycontroller_osx_p.h
+++ b/src/bluetooth/qlowenergycontroller_osx_p.h
@@ -73,6 +73,8 @@ private:
void serviceDiscoveryFinished(LEServices services) Q_DECL_OVERRIDE;
void serviceDetailsDiscoveryFinished(LEService service) Q_DECL_OVERRIDE;
void characteristicWriteNotification(LECharacteristic ch) Q_DECL_OVERRIDE;
+ void descriptorWriteNotification(QLowEnergyHandle descHandle,
+ const QByteArray &newValue) Q_DECL_OVERRIDE;
void disconnected() Q_DECL_OVERRIDE;
void error(QLowEnergyController::Error errorCode) Q_DECL_OVERRIDE;
void error(const QBluetoothUuid &serviceUuid,
@@ -97,13 +99,19 @@ private:
bool appendValue);
void writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service,
- QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle,
+ QLowEnergyHandle descriptorHandle,
const QByteArray &newValue);
+ quint16 updateValueOfDescriptor(QLowEnergyHandle charHandle,
+ QLowEnergyHandle descHandle,
+ const QByteArray &value,
+ bool appendValue);
+
// 'Lookup' functions:
QSharedPointer<QLowEnergyServicePrivate> serviceForHandle(QLowEnergyHandle serviceHandle);
QLowEnergyCharacteristic characteristicForHandle(QLowEnergyHandle charHandle);
+ QLowEnergyDescriptor descriptorForHandle(QLowEnergyHandle descriptorHandle);
void setErrorDescription(QLowEnergyController::Error errorCode);
void invalidateServices();
diff --git a/src/bluetooth/qlowenergydescriptor.h b/src/bluetooth/qlowenergydescriptor.h
index edbac253..eb0ae54f 100644
--- a/src/bluetooth/qlowenergydescriptor.h
+++ b/src/bluetooth/qlowenergydescriptor.h
@@ -73,6 +73,7 @@ protected:
friend class QLowEnergyCharacteristic;
friend class QLowEnergyService;
friend class QLowEnergyControllerPrivate;
+ friend class QLowEnergyControllerPrivateOSX;
QLowEnergyDescriptorPrivate *data;
QLowEnergyDescriptor(QSharedPointer<QLowEnergyServicePrivate> p,
diff --git a/src/bluetooth/qlowenergyservice_osx.mm b/src/bluetooth/qlowenergyservice_osx.mm
index f9c124e4..996de64b 100644
--- a/src/bluetooth/qlowenergyservice_osx.mm
+++ b/src/bluetooth/qlowenergyservice_osx.mm
@@ -221,8 +221,30 @@ bool QLowEnergyService::contains(const QLowEnergyDescriptor &descriptor) const
void QLowEnergyService::writeDescriptor(const QLowEnergyDescriptor &descriptor,
const QByteArray &newValue)
{
- Q_UNUSED(descriptor)
- Q_UNUSED(newValue)
+ if (!contains(descriptor))
+ return;
+
+ QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr);
+ if (!controller)
+ return;
+
+ if (state() != ServiceDiscovered) {
+ d_ptr->setError(OperationError);
+ return;
+ }
+
+ if (descriptor.uuid() == QBluetoothUuid::ClientCharacteristicConfiguration) {
+ // Core Bluetooth:
+ //
+ // "You cannot use this method to write the value of a client configuration descriptor
+ // (represented by the CBUUIDClientCharacteristicConfigurationString constant),
+ // which describes how notification or indications are configured for a
+ // characteristic’s value with respect to a client. If you want to manage
+ // notifications or indications for a characteristic’s value, you must
+ // use the setNotifyValue:forCharacteristic: method instead."
+ } else {
+ controller->writeDescriptor(descriptor.d_ptr, descriptor.handle(), newValue);
+ }
}
QT_END_NAMESPACE