summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@theqtcompany.com>2015-03-31 10:08:02 +0200
committerAlex Blasche <alexander.blasche@theqtcompany.com>2015-04-15 05:43:42 +0000
commiteceb93bda82e45dab9bb85b592533a7d7c5ccac0 (patch)
tree4cdf08b9c08187e45907944a56ce1f8b5c542e2f
parent3162d33d98a96e506a4d8dfc6813f1fb0fe44c18 (diff)
Bluez: Implement QLES::readCharacteristic()/readDescriptor()
Change-Id: I418db6ea375b8e29def136b28b4fc25154d4ffe8 Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
-rw-r--r--src/bluetooth/qlowenergycontroller_android.cpp15
-rw-r--r--src/bluetooth/qlowenergycontroller_bluez.cpp140
-rw-r--r--src/bluetooth/qlowenergycontroller_p.cpp13
-rw-r--r--src/bluetooth/qlowenergycontroller_p.h6
-rw-r--r--src/bluetooth/qlowenergyservice.cpp11
-rw-r--r--src/bluetooth/qlowenergyserviceprivate_p.h4
6 files changed, 178 insertions, 11 deletions
diff --git a/src/bluetooth/qlowenergycontroller_android.cpp b/src/bluetooth/qlowenergycontroller_android.cpp
index 65b3964e..2794fe17 100644
--- a/src/bluetooth/qlowenergycontroller_android.cpp
+++ b/src/bluetooth/qlowenergycontroller_android.cpp
@@ -235,6 +235,21 @@ void QLowEnergyControllerPrivate::writeDescriptor(
service->setError(QLowEnergyService::DescriptorWriteError);
}
+void QLowEnergyControllerPrivate::readCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> /*service*/,
+ const QLowEnergyHandle /*charHandle*/)
+{
+
+}
+
+void QLowEnergyControllerPrivate::readDescriptor(
+ const QSharedPointer<QLowEnergyServicePrivate> /*service*/,
+ const QLowEnergyHandle /*charHandle*/,
+ const QLowEnergyHandle /*descriptorHandle*/)
+{
+
+}
+
void QLowEnergyControllerPrivate::connectionUpdated(
QLowEnergyController::ControllerState newState,
QLowEnergyController::Error errorCode)
diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp
index d355b070..bb490ba9 100644
--- a/src/bluetooth/qlowenergycontroller_bluez.cpp
+++ b/src/bluetooth/qlowenergycontroller_bluez.cpp
@@ -704,6 +704,11 @@ void QLowEnergyControllerPrivate::processReply(
const QLowEnergyHandle charHandle = (handleData & 0xffff);
const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ Q_ASSERT(!service.isNull());
+ bool isServiceDiscoveryRun
+ = !(service->state == QLowEnergyService::ServiceDiscovered);
+
if (isErrorResponse) {
Q_ASSERT(!encryptionChangePending);
encryptionChangePending = increaseEncryptLevelfRequired(response.constData()[4]);
@@ -712,6 +717,12 @@ void QLowEnergyControllerPrivate::processReply(
// Retry the same command again once the change has happened
openRequests.prepend(request);
break;
+ } else if (!isServiceDiscoveryRun) {
+ // not encryption problem -> abort readCharacteristic()/readDescriptor() run
+ if (!descriptorHandle)
+ emit service->error(QLowEnergyService::CharacteristicReadError);
+ else
+ emit service->error(QLowEnergyService::DescriptorReadError);
}
} else {
if (!descriptorHandle)
@@ -721,18 +732,32 @@ void QLowEnergyControllerPrivate::processReply(
response.mid(1), NEW_VALUE);
if (response.size() == mtuSize) {
+ qCDebug(QT_BT_BLUEZ) << "Switching to blob reads for"
+ << charHandle << descriptorHandle
+ << service->characteristicList[charHandle].uuid.toString();
// Potentially more data -> switch to blob reads
readServiceValuesByOffset(handleData, mtuSize-1,
request.reference2.toBool());
break;
+ } else if (!isServiceDiscoveryRun) {
+ // readCharacteristic() or readDescriptor() ongoing
+ if (!descriptorHandle) {
+ QLowEnergyCharacteristic ch(service, charHandle);
+ emit service->characteristicRead(ch, response.mid(1));
+ } else {
+ QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
+ emit service->descriptorRead(descriptor, response.mid(1));
+ }
+ break;
}
}
- if (request.reference2.toBool()) {
+ if (request.reference2.toBool() && isServiceDiscoveryRun) {
+ // we only run into this code path during the initial service discovery
+ // and not when processing readCharacteristics() after service discovery
+
//last characteristic -> progress to descriptor discovery
//last descriptor -> service discovery is done
- QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
- Q_ASSERT(!service.isNull());
if (!descriptorHandle)
discoverServiceDescriptors(service->uuid);
else
@@ -743,13 +768,16 @@ void QLowEnergyControllerPrivate::processReply(
case ATT_OP_READ_BLOB_REQUEST: //error case
case ATT_OP_READ_BLOB_RESPONSE:
{
- //Reading characteristic or descriptor with value longer than MTU
+ //Reading characteristic or descriptor with value longer value than MTU
Q_ASSERT(request.command == ATT_OP_READ_BLOB_REQUEST);
uint handleData = request.reference.toUInt();
const QLowEnergyHandle charHandle = (handleData & 0xffff);
const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ Q_ASSERT(!service.isNull());
+
/*
* READ_BLOB does not require encryption setup code. BLOB commands
* are only issued after read request if the read request is too long
@@ -763,18 +791,33 @@ void QLowEnergyControllerPrivate::processReply(
else
length = updateValueOfDescriptor(charHandle, descriptorHandle,
response.mid(1), APPEND_VALUE);
+
if (response.size() == mtuSize) {
readServiceValuesByOffset(handleData, length,
request.reference2.toBool());
break;
+ } else if (service->state == QLowEnergyService::ServiceDiscovered) {
+ // readCharacteristic() or readDescriptor() ongoing
+ if (!descriptorHandle) {
+ QLowEnergyCharacteristic ch(service, charHandle);
+ emit service->characteristicRead(ch, ch.value());
+ } else {
+ QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
+ emit service->descriptorRead(descriptor, descriptor.value());
+ }
+ break;
}
+ } else {
+ qWarning() << "READ BLOB for char:" << charHandle
+ << "descriptor:" << descriptorHandle << "on service"
+ << service->uuid.toString() << "failed (service discovery run:"
+ << (service->state == QLowEnergyService::ServiceDiscovered) << ")";
}
if (request.reference2.toBool()) {
//last overlong characteristic -> progress to descriptor discovery
//last overlong descriptor -> service discovery is done
- QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
- Q_ASSERT(!service.isNull());
+
if (!descriptorHandle)
discoverServiceDescriptors(service->uuid);
else
@@ -1080,7 +1123,8 @@ void QLowEnergyControllerPrivate::sendReadByTypeRequest(
/*!
\internal
- Reads the value of characteristics and descriptors.
+ Reads all values of specific characteristic and descriptor. This function is
+ used during the initial service discovery process.
\a readCharacteristics determines whether we intend to read a characteristic;
otherwise we read a descriptor.
@@ -1091,10 +1135,10 @@ void QLowEnergyControllerPrivate::readServiceValues(
quint8 packet[READ_REQUEST_HEADER_SIZE];
if (QT_BT_BLUEZ().isDebugEnabled()) {
if (readCharacteristics)
- qCDebug(QT_BT_BLUEZ) << "Reading characteristic values for"
+ qCDebug(QT_BT_BLUEZ) << "Reading all characteristic values for"
<< serviceUuid.toString();
else
- qCDebug(QT_BT_BLUEZ) << "Reading descriptor values for"
+ qCDebug(QT_BT_BLUEZ) << "Reading all descriptor values for"
<< serviceUuid.toString();
}
@@ -1583,6 +1627,84 @@ void QLowEnergyControllerPrivate::writeDescriptor(
}
/*!
+ \internal
+
+ Reads the value of one specific characteristic.
+ */
+void QLowEnergyControllerPrivate::readCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle)
+{
+ Q_ASSERT(!service.isNull());
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ const QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+ if (!(charDetails.properties & QLowEnergyCharacteristic::Read)) {
+ // if this succeeds the device has a bug, char is advertised as
+ // non-readable. We try to be permissive and let the remote
+ // device answer to the read attempt
+ qCWarning(QT_BT_BLUEZ) << "Reading non-readable char" << charHandle;
+ }
+
+ quint8 packet[READ_REQUEST_HEADER_SIZE];
+ packet[0] = ATT_OP_READ_REQUEST;
+ bt_put_unaligned(htobs(charDetails.valueHandle), (quint16 *) &packet[1]);
+
+ QByteArray data(READ_REQUEST_HEADER_SIZE, Qt::Uninitialized);
+ memcpy(data.data(), packet, READ_REQUEST_HEADER_SIZE);
+
+ qCDebug(QT_BT_BLUEZ) << "Targeted reading characteristic" << hex << charHandle;
+
+ Request request;
+ request.payload = data;
+ request.command = ATT_OP_READ_REQUEST;
+ request.reference = charHandle;
+ // reference2 not really required but false prevents service discovery
+ // code from running in ATT_OP_READ_RESPONSE handler
+ request.reference2 = false;
+ openRequests.enqueue(request);
+
+ sendNextPendingRequest();
+}
+
+void QLowEnergyControllerPrivate::readDescriptor(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle)
+{
+ Q_ASSERT(!service.isNull());
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ const QLowEnergyServicePrivate::CharData &charDetails
+ = service->characteristicList[charHandle];
+ if (!charDetails.descriptorList.contains(descriptorHandle))
+ return;
+
+ quint8 packet[READ_REQUEST_HEADER_SIZE];
+ packet[0] = ATT_OP_READ_REQUEST;
+ bt_put_unaligned(htobs(descriptorHandle), (quint16 *) &packet[1]);
+
+ QByteArray data(READ_REQUEST_HEADER_SIZE, Qt::Uninitialized);
+ memcpy(data.data(), packet, READ_REQUEST_HEADER_SIZE);
+
+ qCDebug(QT_BT_BLUEZ) << "Targeted reading descriptor" << hex << descriptorHandle;
+
+ Request request;
+ request.payload = data;
+ request.command = ATT_OP_READ_REQUEST;
+ request.reference = (charHandle | (descriptorHandle << 16));
+ // reference2 not really required but false prevents service discovery
+ // code from running in ATT_OP_READ_RESPONSE handler
+ request.reference2 = false;
+ openRequests.enqueue(request);
+
+ sendNextPendingRequest();
+}
+
+/*!
* Returns true if the encryption change was successfully requested.
* The request is triggered if we got a related ATT error.
*/
diff --git a/src/bluetooth/qlowenergycontroller_p.cpp b/src/bluetooth/qlowenergycontroller_p.cpp
index 79f463ef..b3c718b5 100644
--- a/src/bluetooth/qlowenergycontroller_p.cpp
+++ b/src/bluetooth/qlowenergycontroller_p.cpp
@@ -74,6 +74,19 @@ void QLowEnergyControllerPrivate::discoverServiceDetails(const QBluetoothUuid &/
}
+void QLowEnergyControllerPrivate::readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> /*service*/,
+ const QLowEnergyHandle /*charHandle*/)
+{
+
+}
+
+void QLowEnergyControllerPrivate::readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> /*service*/,
+ const QLowEnergyHandle /*charHandle*/,
+ const QLowEnergyHandle /*descriptorHandle*/)
+{
+
+}
+
void QLowEnergyControllerPrivate::writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> /*service*/,
const QLowEnergyHandle /*charHandle*/,
const QByteArray &/*newValue*/,
diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h
index 408cc72a..9e2cedbc 100644
--- a/src/bluetooth/qlowenergycontroller_p.h
+++ b/src/bluetooth/qlowenergycontroller_p.h
@@ -112,6 +112,12 @@ public:
const QByteArray &value,
bool appendValue);
+ // read data
+ void readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle);
+ void readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle);
// write data
void writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
diff --git a/src/bluetooth/qlowenergyservice.cpp b/src/bluetooth/qlowenergyservice.cpp
index 14b9777b..0db349f1 100644
--- a/src/bluetooth/qlowenergyservice.cpp
+++ b/src/bluetooth/qlowenergyservice.cpp
@@ -339,6 +339,10 @@ QLowEnergyService::QLowEnergyService(QSharedPointer<QLowEnergyServicePrivate> p,
this, SIGNAL(characteristicWritten(QLowEnergyCharacteristic,QByteArray)));
connect(p.data(), SIGNAL(descriptorWritten(QLowEnergyDescriptor,QByteArray)),
this, SIGNAL(descriptorWritten(QLowEnergyDescriptor,QByteArray)));
+ connect(p.data(), SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray)),
+ this, SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray)));
+ connect(p.data(), SIGNAL(descriptorRead(QLowEnergyDescriptor,QByteArray)),
+ this, SIGNAL(descriptorRead(QLowEnergyDescriptor,QByteArray)));
}
/*!
@@ -574,7 +578,8 @@ void QLowEnergyService::readCharacteristic(
return;
}
- //TODO
+ d->controller->readCharacteristic(characteristic.d_ptr,
+ characteristic.attributeHandle());
}
/*!
@@ -688,7 +693,9 @@ void QLowEnergyService::readDescriptor(
return;
}
- //TODO implement QLowEnergyService::readDescriptor()
+ d->controller->readDescriptor(descriptor.d_ptr,
+ descriptor.characteristicHandle(),
+ descriptor.handle());
}
/*!
diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h
index cc9b452f..a020341f 100644
--- a/src/bluetooth/qlowenergyserviceprivate_p.h
+++ b/src/bluetooth/qlowenergyserviceprivate_p.h
@@ -80,8 +80,12 @@ signals:
void error(QLowEnergyService::ServiceError error);
void characteristicChanged(const QLowEnergyCharacteristic &characteristic,
const QByteArray &newValue);
+ void characteristicRead(const QLowEnergyCharacteristic &info,
+ const QByteArray &value);
void characteristicWritten(const QLowEnergyCharacteristic &characteristic,
const QByteArray &newValue);
+ void descriptorRead(const QLowEnergyDescriptor &info,
+ const QByteArray &value);
void descriptorWritten(const QLowEnergyDescriptor &descriptor,
const QByteArray &newValue);