diff options
author | Alex Blasche <alexander.blasche@digia.com> | 2014-07-22 16:41:44 +0200 |
---|---|---|
committer | Alex Blasche <alexander.blasche@digia.com> | 2014-08-13 13:47:41 +0200 |
commit | 0e367adbfe8e0971930315d1c58b4ef0bf80a9ba (patch) | |
tree | a7d491849bac13398968dccb8be0f21cf5d5d4a0 /src | |
parent | 199bd8306541c3987cc4aae7d7f93af31717d3b5 (diff) |
Implement support for QLowEnergyService include hierarchies.
Change-Id: Id11d2dcb06bd04bc1d911d746002fbbc53326ffa
Reviewed-by: Fabian Bumberger <fbumberger@rim.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/bluetooth/qlowenergycontroller.cpp | 24 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_bluez.cpp | 148 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_p.h | 5 | ||||
-rw-r--r-- | src/bluetooth/qlowenergyservice.cpp | 20 | ||||
-rw-r--r-- | src/bluetooth/qlowenergyservice.h | 3 | ||||
-rw-r--r-- | src/bluetooth/qlowenergyserviceprivate_p.h | 1 |
6 files changed, 149 insertions, 52 deletions
diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp index 32fbd37e..c8ffc802 100644 --- a/src/bluetooth/qlowenergycontroller.cpp +++ b/src/bluetooth/qlowenergycontroller.cpp @@ -286,11 +286,35 @@ void QLowEnergyController::discoverServices() d->discoverServices(); } +/*! + Returns the list of services offered by the remote device. + + The list contains all primary and secondary services. + + \sa createServiceObject() + */ QList<QBluetoothUuid> QLowEnergyController::services() const { return d_ptr->serviceList.keys(); } +/*! + Creates an instance of the service represented by \a serviceUuid. + The \a serviceUuid parameter must have been obtained via + \l services(). + + The caller takes ownership of the returned pointer and may pass + a \a parent parameter as default owner. + + This function returns a null pointer if no service with + \a serviceUUid can be found on the remote device. + + This function can return instances for secondary services + too. The include relationships between services can be expressed + via \l QLowEnergyService::includedServices(). + + \sa services() + */ QLowEnergyService *QLowEnergyController::createServiceObject( const QBluetoothUuid &serviceUuid, QObject *parent) { diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp index 795bdfb8..f92d188d 100644 --- a/src/bluetooth/qlowenergycontroller_bluez.cpp +++ b/src/bluetooth/qlowenergycontroller_bluez.cpp @@ -57,6 +57,7 @@ #define GATT_PRIMARY_SERVICE 0x2800 #define GATT_SECONDARY_SERVICE 0x2801 +#define GATT_INCLUDED_SERVICE 0x2802 #define GATT_CHARACTERISTIC 0x2803 // GATT commands @@ -357,6 +358,57 @@ void QLowEnergyControllerPrivate::sendNextPendingRequest() sendCommand(request.payload); } +QLowEnergyHandle parseReadByTypeCharDiscovery( + QLowEnergyServicePrivate::CharData *charData, + const char *data, quint16 elementLength) +{ + Q_ASSERT(charData); + Q_ASSERT(data); + + QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]); + charData->properties = + (QLowEnergyCharacteristic::PropertyTypes)data[2]; + charData->valueHandle = bt_get_le16(&data[3]); + + if (elementLength == 7) // 16 bit uuid + charData->uuid = QBluetoothUuid(bt_get_le16(&data[5])); + else + charData->uuid = convert_uuid128((uint128_t *)&data[5]); + + qCDebug(QT_BT_BLUEZ) << "Found handle:" << hex << attributeHandle + << "properties:" << charData->properties + << "value handle:" << charData->valueHandle + << "uuid:" << charData->uuid.toString(); + + return attributeHandle; +} + +QLowEnergyHandle parseReadByTypeIncludeDiscovery( + QList<QBluetoothUuid> *foundServices, + const char *data, quint16 elementLength) +{ + Q_ASSERT(foundServices); + Q_ASSERT(data); + + QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]); + + // the next 2 elements are not required as we have discovered + // all (primary/secondary) services already. Now we are only + // interested in their relationship to each other + // data[2] -> included service start handle + // data[4] -> included service end handle + + if (elementLength == 8) //16 bit uuid + foundServices->append(QBluetoothUuid(bt_get_le16(&data[6]))); + else + foundServices->append(convert_uuid128((uint128_t *) &data[6])); + + qCDebug(QT_BT_BLUEZ) << "Found included service: " << hex + << attributeHandle << "uuid:" << *foundServices; + + return attributeHandle; +} + void QLowEnergyControllerPrivate::processReply( const Request &request, const QByteArray &response) { @@ -461,63 +513,70 @@ void QLowEnergyControllerPrivate::processReply( QSharedPointer<QLowEnergyServicePrivate> p = request.reference.value<QSharedPointer<QLowEnergyServicePrivate> >(); + const quint16 attributeType = request.reference2.toUInt(); if (isErrorResponse) { - // we reached end of service handle - // just finished up characteristic discovery - // continue with values of characteristics - if (!p->characteristicList.isEmpty()) { - readServiceValues(p->uuid, true); - } else { - // discovery finished since the service doesn't have any - // characteristics - p->setState(QLowEnergyService::ServiceDiscovered); + if (attributeType == GATT_CHARACTERISTIC) { + // we reached end of service handle + // just finished up characteristic discovery + // continue with values of characteristics + if (!p->characteristicList.isEmpty()) { + readServiceValues(p->uuid, true); + } else { + // discovery finished since the service doesn't have any + // characteristics + p->setState(QLowEnergyService::ServiceDiscovered); + } + } else if (attributeType == GATT_INCLUDED_SERVICE) { + // finished up include discovery + // continue with characteristic discovery + sendReadByTypeRequest(p, p->startHandle, GATT_CHARACTERISTIC); } break; } /* packet format: - * <opcode><elementLength>[<handle><property><charHandle><uuid>]+ + * if GATT_CHARACTERISTIC discovery + * <opcode><elementLength> + * [<handle><property><charHandle><uuid>]+ + * + * if GATT_INCLUDE discovery + * <opcode><elementLength> + * [<handle><startHandle_included><endHandle_included><uuid>]+ * * The uuid can be 16 or 128 bit. */ - QLowEnergyHandle startHandle, valueHandle; + QLowEnergyHandle lastHandle; const quint16 elementLength = response.constData()[1]; const quint16 numElements = (response.size() - 2) / elementLength; quint16 offset = 2; const char *data = response.constData(); for (int i = 0; i < numElements; i++) { - startHandle = bt_get_le16(&data[offset]); - QLowEnergyCharacteristic::PropertyTypes flags = - (QLowEnergyCharacteristic::PropertyTypes)data[offset+2]; - valueHandle = bt_get_le16(&data[offset+3]); - QBluetoothUuid uuid; + if (attributeType == GATT_CHARACTERISTIC) { + QLowEnergyServicePrivate::CharData characteristic; + lastHandle = parseReadByTypeCharDiscovery( + &characteristic, &data[offset], elementLength); + p->characteristicList[lastHandle] = characteristic; + } else if (attributeType == GATT_INCLUDED_SERVICE) { + QList<QBluetoothUuid> includedServices; + lastHandle = parseReadByTypeIncludeDiscovery( + &includedServices, &data[offset], elementLength); + p->includedServices = includedServices; + foreach (const QBluetoothUuid &uuid, includedServices) { + if (serviceList.contains(uuid)) + serviceList[uuid]->type |= QLowEnergyService::IncludedService; + } + } + } - if (elementLength == 7) // 16 bit uuid - uuid = QBluetoothUuid(bt_get_le16(&data[offset+5])); + if (lastHandle + 1 < p->endHandle) { // more chars to discover + sendReadByTypeRequest(p, lastHandle + 1, attributeType); + } else { + if (attributeType == GATT_INCLUDED_SERVICE) + sendReadByTypeRequest(p, p->startHandle, GATT_CHARACTERISTIC); else - uuid = convert_uuid128((uint128_t *)&data[offset+5]); - - offset += elementLength; - - QLowEnergyServicePrivate::CharData characteristic; - characteristic.properties = flags; - characteristic.valueHandle = valueHandle; - characteristic.uuid = uuid; - - p->characteristicList[startHandle] = characteristic; - - - qCDebug(QT_BT_BLUEZ) << "Found handle:" << hex << startHandle - << "properties:" << flags - << "value handle:" << valueHandle - << "uuid:" << uuid.toString(); + readServiceValues(p->uuid, true); } - - if (startHandle + 1 < p->endHandle) // more chars to discover - sendReadByTypeRequest(p, startHandle + 1); - else - readServiceValues(p->uuid, true); } break; case ATT_OP_READ_REQUEST: //error case @@ -718,7 +777,7 @@ void QLowEnergyControllerPrivate::sendReadByGroupRequest( QByteArray data(GRP_TYPE_REQ_SIZE, Qt::Uninitialized); memcpy(data.data(), packet, GRP_TYPE_REQ_SIZE); qCDebug(QT_BT_BLUEZ) << "Sending read_by_group_type request, startHandle:" << hex - << start << "endHandle:" << end; + << start << "endHandle:" << end << type; Request request; request.payload = data; @@ -739,30 +798,31 @@ void QLowEnergyControllerPrivate::discoverServiceDetails(const QBluetoothUuid &s QSharedPointer<QLowEnergyServicePrivate> serviceData = serviceList.value(service); serviceData->characteristicList.clear(); - sendReadByTypeRequest(serviceData, serviceData->startHandle); + sendReadByTypeRequest(serviceData, serviceData->startHandle, GATT_INCLUDED_SERVICE); } void QLowEnergyControllerPrivate::sendReadByTypeRequest( QSharedPointer<QLowEnergyServicePrivate> serviceData, - QLowEnergyHandle nextHandle) + QLowEnergyHandle nextHandle, quint16 attributeType) { quint8 packet[READ_BY_TYPE_REQ_SIZE]; packet[0] = ATT_OP_READ_BY_TYPE_REQUEST; bt_put_unaligned(htobs(nextHandle), (quint16 *) &packet[1]); bt_put_unaligned(htobs(serviceData->endHandle), (quint16 *) &packet[3]); - bt_put_unaligned(htobs(GATT_CHARACTERISTIC), (quint16 *) &packet[5]); + bt_put_unaligned(htobs(attributeType), (quint16 *) &packet[5]); QByteArray data(READ_BY_TYPE_REQ_SIZE, Qt::Uninitialized); memcpy(data.data(), packet, READ_BY_TYPE_REQ_SIZE); qCDebug(QT_BT_BLUEZ) << "Sending read_by_type request, startHandle:" << hex << nextHandle << "endHandle:" << serviceData->endHandle - << "packet:" << data.toHex(); + << "type:" << attributeType << "packet:" << data.toHex(); Request request; request.payload = data; request.command = ATT_OP_READ_BY_TYPE_REQUEST; request.reference = QVariant::fromValue(serviceData); + request.reference2 = attributeType; openRequests.enqueue(request); sendNextPendingRequest(); diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h index 38178028..c3743320 100644 --- a/src/bluetooth/qlowenergycontroller_p.h +++ b/src/bluetooth/qlowenergycontroller_p.h @@ -109,11 +109,10 @@ public: QLowEnergyController::Error error; QString errorString; - -private: // list of all found service uuids ServiceDataMap serviceList; +private: #if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE) QBluetoothSocket *l2cpSocket; struct Request { @@ -135,7 +134,7 @@ private: void sendReadByGroupRequest(QLowEnergyHandle start, QLowEnergyHandle end, quint16 type); void sendReadByTypeRequest(QSharedPointer<QLowEnergyServicePrivate> serviceData, - QLowEnergyHandle nextHandle); + QLowEnergyHandle nextHandle, quint16 attributeType); void sendReadValueRequest(QLowEnergyHandle attributeHandle, bool isDescriptor); void readServiceValues(const QBluetoothUuid &service, bool readCharacteristics); diff --git a/src/bluetooth/qlowenergyservice.cpp b/src/bluetooth/qlowenergyservice.cpp index 0bc9dc55..725c82e9 100644 --- a/src/bluetooth/qlowenergyservice.cpp +++ b/src/bluetooth/qlowenergyservice.cpp @@ -92,11 +92,23 @@ QLowEnergyService::~QLowEnergyService() { } -QList<QSharedPointer<QLowEnergyService> > QLowEnergyService::includedServices() const +/*! + Returns the uuids of all services which are included by the + current service. + + It is possible that an included service contains yet another service. Such + second level includes have to be obtained via their relevant first level + QLowEnergyService instance. Technically it is possible that this can create + a circular dependency. + + \l {QLowEnergyController::createServiceObject} should be used to obtain + service instances for each of the uuids. + + \sa createServiceObject() + */ +QList<QBluetoothUuid> QLowEnergyService::includedServices() const { - QList<QSharedPointer<QLowEnergyService > > results; - //TODO implement secondary service support - return results; + return d_ptr->includedServices; } QLowEnergyService::ServiceState QLowEnergyService::state() const diff --git a/src/bluetooth/qlowenergyservice.h b/src/bluetooth/qlowenergyservice.h index 541d6c61..d7033224 100644 --- a/src/bluetooth/qlowenergyservice.h +++ b/src/bluetooth/qlowenergyservice.h @@ -76,7 +76,8 @@ public: ~QLowEnergyService(); - QList<QSharedPointer<QLowEnergyService> > includedServices() const; + QList<QBluetoothUuid> includedServices() const; + QLowEnergyService::ServiceTypes type() const; QLowEnergyService::ServiceState state() const; diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h index ab19aa95..cc0fafee 100644 --- a/src/bluetooth/qlowenergyserviceprivate_p.h +++ b/src/bluetooth/qlowenergyserviceprivate_p.h @@ -96,6 +96,7 @@ public: QLowEnergyHandle endHandle; QBluetoothUuid uuid; + QList<QBluetoothUuid> includedServices; QLowEnergyService::ServiceTypes type; QLowEnergyService::ServiceState state; QLowEnergyService::ServiceError lastError; |