summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@digia.com>2014-06-24 09:46:01 +0200
committerAlex Blasche <alexander.blasche@digia.com>2014-06-26 16:36:18 +0200
commitb9909f15e8760e1587f67d9ff028bf730a92d044 (patch)
treee1cc704a97057d8fa9390ff19a02a68c82631e5d /src
parent3c9c77c94d747a568a22421c0b0ada086463dbad (diff)
Discover all descriptor handles and uuid.
The descriptor information is not yet exposed via the public API. Change-Id: I7247ba2ede81fec95bae7ea10313a9123565211a Reviewed-by: Fabian Bumberger <fbumberger@rim.com>
Diffstat (limited to 'src')
-rw-r--r--src/bluetooth/qlowenergycontrollernew_bluez.cpp169
-rw-r--r--src/bluetooth/qlowenergycontrollernew_p.h13
-rw-r--r--src/bluetooth/qlowenergyserviceprivate_p.h8
3 files changed, 183 insertions, 7 deletions
diff --git a/src/bluetooth/qlowenergycontrollernew_bluez.cpp b/src/bluetooth/qlowenergycontrollernew_bluez.cpp
index 1b5ea1db..4e5f5270 100644
--- a/src/bluetooth/qlowenergycontrollernew_bluez.cpp
+++ b/src/bluetooth/qlowenergycontrollernew_bluez.cpp
@@ -56,6 +56,8 @@
// GATT commands
#define ATT_OP_ERROR_RESPONSE 0x1
+#define ATT_OP_FIND_INFORMATION_REQUEST 0x4 //discover individual attribute info
+#define ATT_OP_FIND_INFORMATION_RESPONSE 0x5
#define ATT_OP_READ_BY_TYPE_REQUEST 0x8 //discover characteristics
#define ATT_OP_READ_BY_TYPE_RESPONSE 0x9
#define ATT_OP_READ_REQUEST 0xA //read characteristic value
@@ -230,7 +232,7 @@ void QLowEnergyControllerNewPrivate::l2cpReadyRead()
{
requestPending = false;
const QByteArray reply = l2cpSocket->readAll();
- qDebug() << reply.size() << "data:" << reply.toHex();
+ //qDebug() << reply.size() << "data:" << reply.toHex();
if (reply.isEmpty())
return;
@@ -399,12 +401,110 @@ void QLowEnergyControllerNewPrivate::processReply(
}
QLowEnergyHandle charHandle = (QLowEnergyHandle) request.reference.toUInt();
- QByteArray a = response.mid(1);
updateValueOfCharacteristic(charHandle, response.mid(1).toHex());
if (request.reference2.toBool()) {
QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
Q_ASSERT(!service.isNull());
- service->setState(QLowEnergyService::ServiceDiscovered);
+ discoverServiceDescriptors(service->uuid);
+ }
+ }
+ break;
+ case ATT_OP_FIND_INFORMATION_REQUEST: //error case
+ case ATT_OP_FIND_INFORMATION_RESPONSE:
+ {
+ Q_ASSERT(request.command == ATT_OP_FIND_INFORMATION_REQUEST);
+
+ /* packet format:
+ * <opcode><format>[<handle><descriptor_uuid>]+
+ *
+ * The uuid can be 16 or 128 bit which is indicated by format.
+ */
+
+ if (isErrorResponse) {
+ //TODO handle error
+ break;
+ }
+
+ QList<QLowEnergyHandle> keys = request.reference.value<QList<QLowEnergyHandle> >();
+ if (keys.isEmpty()) {
+ qCWarning(QT_BT_BLUEZ) << "Descriptor discovery for unknown characteristic received";
+ break;
+ }
+ QLowEnergyHandle charHandle = keys.first();
+
+ QSharedPointer<QLowEnergyServicePrivate> p =
+ serviceForHandle(charHandle);
+ Q_ASSERT(!p.isNull());
+
+
+ const quint8 format = response[1];
+ quint16 elementLength;
+ switch (format) {
+ case 0x01:
+ elementLength = 2 + 2; //sizeof(QLowEnergyHandle) + 16bit uuid
+ break;
+ case 0x02:
+ elementLength = 2 + 16; //sizeof(QLowEnergyHandle) + 128bit uuid
+ break;
+ default:
+ qCWarning(QT_BT_BLUEZ) << "Unknown format in FIND_INFORMATION_RESPONSE";
+ return;
+ }
+
+ const quint16 numElements = (response.size() - 2) / elementLength;
+
+ quint16 offset = 2;
+ QLowEnergyHandle descriptorHandle;
+ QBluetoothUuid uuid;
+ const char *data = response.constData();
+ for (int i = 0; i < numElements; i++) {
+ descriptorHandle = bt_get_le16(&data[offset]);
+
+ if (format == 0x01)
+ uuid = QBluetoothUuid(bt_get_le16(&data[offset+2]));
+ else if (format == 0x02)
+ uuid = convert_uuid128((uint128_t *)&data[offset+2]);
+
+ offset += elementLength;
+
+ // ignore all attributes which are not of type descriptor
+ // examples are the characteristics value or
+ bool ok = false;
+ quint16 shortUuid = uuid.toUInt16(&ok);
+ if (ok && shortUuid >= QLowEnergyServicePrivate::PrimaryService
+ && shortUuid <= QLowEnergyServicePrivate::Characteristic)
+ continue;
+
+ // ignore value handle
+ if (descriptorHandle == p->characteristicList[charHandle].valueHandle)
+ continue;
+
+ p->characteristicList[charHandle].descriptorList.insert(
+ descriptorHandle, uuid);
+
+ qCDebug(QT_BT_BLUEZ) << "Descriptor found, uuid:"
+ << uuid.toString()
+ << "descriptor handle:" << descriptorHandle;
+ }
+
+ QLowEnergyHandle nextBorderHandle = 0;
+ if (keys.count() == 1) { // processing last characteristic
+ nextBorderHandle = p->endHandle;
+ } else {
+ nextBorderHandle = keys[1];
+ }
+
+ // have we reached next border?
+ if (descriptorHandle + 1 >= nextBorderHandle)
+ // go to next characteristic
+ keys.removeFirst();
+
+ if (keys.isEmpty()) {
+ // descriptor for last characteristic found
+ p->setState(QLowEnergyService::ServiceDiscovered);
+ } else {
+ // service has more descriptors to be found
+ discoverNextDescriptor(p, keys, descriptorHandle + 1);
}
}
break;
@@ -520,14 +620,73 @@ void QLowEnergyControllerNewPrivate::readServiceCharacteristicValues(
request.payload = data;
request.command = ATT_OP_READ_REQUEST;
request.reference = charHandle;
- //last entry?
+ // last entry?
request.reference2 = QVariant((bool)(i + 1 == keys.count()));
openRequests.enqueue(request);
oneReadRequested = true;
}
- if (!oneReadRequested)
+
+ if (!oneReadRequested) {
+ // none of the characteristics is readable
+ // -> continue with descriptor discovery
+ discoverServiceDescriptors(service->uuid);
+ return;
+ }
+
+ sendNextPendingRequest();
+}
+
+void QLowEnergyControllerNewPrivate::discoverServiceDescriptors(
+ const QBluetoothUuid &serviceUuid)
+{
+ qCDebug(QT_BT_BLUEZ) << "Discovering descriptor values for"
+ << serviceUuid.toString();
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceList.value(serviceUuid);
+ // start handle of all known characteristics
+ QList<QLowEnergyHandle> keys = service->characteristicList.keys();
+
+ if (keys.isEmpty()) { // service has no characteristics -> very unlikely
service->setState(QLowEnergyService::ServiceDiscovered);
+ return;
+ }
+
+ std::sort(keys.begin(), keys.end());
+
+ discoverNextDescriptor(service, keys, keys[0]);
+}
+
+void QLowEnergyControllerNewPrivate::discoverNextDescriptor(
+ QSharedPointer<QLowEnergyServicePrivate> serviceData,
+ const QList<QLowEnergyHandle> pendingCharHandles,
+ const QLowEnergyHandle startingHandle)
+{
+ Q_ASSERT(!pendingCharHandles.isEmpty());
+ Q_ASSERT(!serviceData.isNull());
+
+#define FIND_INFO_REQUEST_SIZE 5
+ quint8 packet[FIND_INFO_REQUEST_SIZE];
+ packet[0] = ATT_OP_FIND_INFORMATION_REQUEST;
+
+ QLowEnergyHandle charStartHandle = startingHandle;
+ QLowEnergyHandle charEndHandle = 0;
+ if (pendingCharHandles.count() == 1) //single characteristic
+ charEndHandle = serviceData->endHandle;
+ else
+ charEndHandle = pendingCharHandles[1] - 1;
+
+ bt_put_unaligned(htobs(charStartHandle), (quint16 *) &packet[1]);
+ bt_put_unaligned(htobs(charEndHandle), (quint16 *) &packet[3]);
+
+ QByteArray data(FIND_INFO_REQUEST_SIZE, Qt::Uninitialized);
+ memcpy(data.data(), packet, FIND_INFO_REQUEST_SIZE);
+
+ Request request;
+ request.payload = data;
+ request.command = ATT_OP_FIND_INFORMATION_REQUEST;
+ request.reference = QVariant::fromValue<QList<QLowEnergyHandle> >(pendingCharHandles);
+ request.reference2 = startingHandle;
+ openRequests.enqueue(request);
sendNextPendingRequest();
}
diff --git a/src/bluetooth/qlowenergycontrollernew_p.h b/src/bluetooth/qlowenergycontrollernew_p.h
index f6bc06e5..50ff7a62 100644
--- a/src/bluetooth/qlowenergycontrollernew_p.h
+++ b/src/bluetooth/qlowenergycontrollernew_p.h
@@ -68,7 +68,11 @@ public:
#if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE)
, l2cpSocket(0)
#endif
- {}
+ {
+#if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE)
+ qRegisterMetaType<QList<QLowEnergyHandle> >();
+#endif
+ }
void setError(QLowEnergyControllerNew::Error newError);
bool isValidLocalAdapter();
@@ -85,7 +89,7 @@ public:
// misc lookup helpers
QSharedPointer<QLowEnergyServicePrivate> serviceForHandle(
- QLowEnergyHandle handle);
+ QLowEnergyHandle handle);
void updateValueOfCharacteristic(QLowEnergyHandle charHandle,
const QByteArray &value);
@@ -123,6 +127,11 @@ private:
QLowEnergyHandle nextHandle);
void readServiceCharacteristicValues(const QBluetoothUuid &service);
+ void discoverServiceDescriptors(const QBluetoothUuid &serviceUuid);
+ void discoverNextDescriptor(QSharedPointer<QLowEnergyServicePrivate> serviceData,
+ const QList<QLowEnergyHandle> pendingCharHandles,
+ QLowEnergyHandle startingHandle);
+
private slots:
void l2cpConnected();
void l2cpDisconnected();
diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h
index 8f946a46..16b1ea31 100644
--- a/src/bluetooth/qlowenergyserviceprivate_p.h
+++ b/src/bluetooth/qlowenergyserviceprivate_p.h
@@ -64,6 +64,14 @@ public:
QBluetoothUuid uuid;
QLowEnergyCharacteristic::PropertyTypes properties;
QByteArray value;
+ QHash<QLowEnergyHandle, QBluetoothUuid> descriptorList;
+ };
+
+ enum GattAttributeTypes {
+ PrimaryService = 0x2800,
+ SecondaryService = 0x2801,
+ IncludeAttribute = 0x2802,
+ Characteristic = 0x2803
};
void setController(QLowEnergyControllerNewPrivate* control);