diff options
Diffstat (limited to 'gatoperipheral.cpp')
-rw-r--r-- | gatoperipheral.cpp | 834 |
1 files changed, 834 insertions, 0 deletions
diff --git a/gatoperipheral.cpp b/gatoperipheral.cpp new file mode 100644 index 0000000..faec8cd --- /dev/null +++ b/gatoperipheral.cpp @@ -0,0 +1,834 @@ +/* + * libgato - A GATT/ATT library for use with Bluez + * + * Copyright (C) 2013 Javier S. Pedro <maemo@javispedro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <QtCore/QDebug> + +#include <assert.h> +#include <bluetooth/bluetooth.h> + +#include "gatoperipheral_p.h" +#include "gatoaddress.h" +#include "gatouuid.h" +#include "helpers.h" + +enum EIRDataFields { + EIRFlags = 0x01, + EIRIncompleteUUID16List = 0x02, + EIRCompleteUUID16List = 0x03, + EIRIncompleteUUID32List = 0x04, + EIRCompleteUUID32List = 0x05, + EIRIncompleteUUID128List = 0x06, + EIRCompleteUUID128List = 0x07, + EIRIncompleteLocalName = 0x08, + EIRCompleteLocalName = 0x09, + EIRTxPowerLevel = 0x0A, + EIRDeviceClass = 0x0D, + EIRSecurityManagerTKValue = 0x10, + EIRSecurityManagerOutOfBandFlags = 0x11 +}; + +GatoPeripheral::GatoPeripheral(const GatoAddress &addr, QObject *parent) : + QObject(parent), d_ptr(new GatoPeripheralPrivate(this)) +{ + Q_D(GatoPeripheral); + d->addr = addr; + d->att = new GatoAtt(this); + + connect(d->att, SIGNAL(connected()), d, SLOT(handleAttConnected())); + connect(d->att, SIGNAL(disconnected()), d, SLOT(handleAttDisconnected())); + connect(d->att, SIGNAL(attributeUpdated(GatoHandle,QByteArray,bool)), d, SLOT(handleAttAttributeUpdated(GatoHandle,QByteArray,bool))); +} + +GatoPeripheral::~GatoPeripheral() +{ + if (state() != StateDisconnected) { + disconnect(); + } + delete d_ptr; +} + +GatoPeripheral::State GatoPeripheral::state() const +{ + Q_D(const GatoPeripheral); + return static_cast<State>(d->att->state()); +} + +GatoAddress GatoPeripheral::address() const +{ + Q_D(const GatoPeripheral); + return d->addr; +} + +QString GatoPeripheral::name() const +{ + Q_D(const GatoPeripheral); + return d->name; +} + +QList<GatoService> GatoPeripheral::services() const +{ + Q_D(const GatoPeripheral); + return d->services.values(); +} + +void GatoPeripheral::parseEIR(quint8 data[], int len) +{ + Q_D(GatoPeripheral); + + int pos = 0; + while (pos < len) { + int item_len = data[pos]; + pos++; + if (item_len == 0) break; + int type = data[pos]; + assert(pos + item_len <= len); + switch (type) { + case EIRFlags: + d->parseEIRFlags(&data[pos + 1], item_len - 1); + break; + case EIRIncompleteUUID16List: + d->parseEIRUUIDs(16/8, false, &data[pos + 1], item_len - 1); + break; + case EIRCompleteUUID16List: + d->parseEIRUUIDs(16/8, true, &data[pos + 1], item_len - 1); + break; + case EIRIncompleteUUID32List: + d->parseEIRUUIDs(32/8, false, &data[pos + 1], item_len - 1); + break; + case EIRCompleteUUID32List: + d->parseEIRUUIDs(32/8, true, &data[pos + 1], item_len - 1); + break; + case EIRIncompleteUUID128List: + d->parseEIRUUIDs(128/8, false, &data[pos + 1], item_len - 1); + break; + case EIRCompleteUUID128List: + d->parseEIRUUIDs(128/8, true, &data[pos + 1], item_len - 1); + break; + case EIRIncompleteLocalName: + d->parseName(false, &data[pos + 1], item_len - 1); + break; + case EIRCompleteLocalName: + d->parseName(true, &data[pos + 1], item_len - 1); + break; + default: + qWarning() << "Unknown EIR data type" << type; + break; + } + + pos += item_len; + } + + assert(pos == len); +} + +void GatoPeripheral::connectPeripheral() +{ + Q_D(GatoPeripheral); + if (d->att->state() != GatoSocket::StateDisconnected) { + qDebug() << "Already connecting"; + return; + } + + d->att->connectTo(d->addr); +} + +void GatoPeripheral::disconnectPeripheral() +{ + Q_D(GatoPeripheral); + + d->att->close(); +} + +void GatoPeripheral::discoverServices() +{ + Q_D(GatoPeripheral); + if (!d->complete_services && state() == StateConnected) { + d->clearServices(); + d->att->requestReadByGroupType(0x0001, 0xFFFF, GatoUUID::GattPrimaryService, + d, SLOT(handlePrimary(QList<GatoAtt::AttributeGroupData>))); + } else { + qWarning() << "Not connected"; + } +} + +void GatoPeripheral::discoverServices(const QList<GatoUUID> &serviceUUIDs) +{ + Q_D(GatoPeripheral); + if (serviceUUIDs.isEmpty()) return; + if (state() == StateConnected) { + foreach (const GatoUUID& uuid, serviceUUIDs) { + QByteArray value = gatouuid_to_bytearray(uuid, true, false); + uint req = d->att->requestFindByTypeValue(0x0001, 0xFFFF, GatoUUID::GattPrimaryService, value, + d, SLOT(handlePrimaryForService(uint,QList<GatoAtt::HandleInformation>))); + d->pending_primary_reqs.insert(req, uuid); + } + } else { + qWarning() << "Not connected"; + } +} + +void GatoPeripheral::discoverCharacteristics(const GatoService &service) +{ + Q_D(GatoPeripheral); + + if (!d->services.contains(service.startHandle())) { + qWarning() << "Unknown service for this peripheral"; + return; + } + + GatoService &our_service = d->services[service.startHandle()]; + + if (our_service.startHandle() != service.startHandle() || + our_service.endHandle() != service.endHandle() || + our_service.uuid() != service.uuid()) { + qWarning() << "Unknown service for this peripheral"; + return; + } + + if (state() == StateConnected) { + GatoHandle start = our_service.startHandle(); + GatoHandle end = our_service.endHandle(); + + d->clearServiceCharacteristics(&our_service); + + uint req = d->att->requestReadByType(start, end, GatoUUID::GattCharacteristic, + d, SLOT(handleCharacteristic(QList<GatoAtt::AttributeData>))); + d->pending_characteristic_reqs.insert(req, start); + } else { + qWarning() << "Not connected"; + } +} + +void GatoPeripheral::discoverCharacteristics(const GatoService &service, const QList<GatoUUID> &characteristicUUIDs) +{ + // TODO There seems to be no way to ask for the peripheral to filter by uuid + Q_UNUSED(characteristicUUIDs); + discoverCharacteristics(service); +} + +void GatoPeripheral::discoverDescriptors(const GatoCharacteristic &characteristic) +{ + Q_D(GatoPeripheral); + + GatoHandle char_handle = characteristic.startHandle(); + GatoHandle service_handle = d->characteristic_to_service.value(char_handle); + + if (!service_handle) { + qWarning() << "Unknown characteristic for this peripheral"; + return; + } + + GatoService &our_service = d->services[service_handle]; + Q_ASSERT(our_service.containsCharacteristic(char_handle)); + GatoCharacteristic our_char = our_service.getCharacteristic(char_handle); + Q_ASSERT(our_char.startHandle() == char_handle); + + if (state() == StateConnected) { + d->clearCharacteristicDescriptors(&our_char); + our_service.addCharacteristic(our_char); // Update service with empty descriptors list + uint req = d->att->requestFindInformation(our_char.startHandle() + 1, our_char.endHandle(), + d, SLOT(handleDescriptors(uint,QList<GatoAtt::InformationData>))); + d->pending_descriptor_reqs.insert(req, char_handle); + } else { + qWarning() << "Not connected"; + } +} + +void GatoPeripheral::readValue(const GatoCharacteristic &characteristic) +{ + Q_D(GatoPeripheral); + + GatoHandle char_handle = characteristic.startHandle(); + GatoHandle service_handle = d->characteristic_to_service.value(char_handle); + + if (!service_handle) { + qWarning() << "Unknown characteristic for this peripheral"; + return; + } + + GatoService &our_service = d->services[service_handle]; + Q_ASSERT(our_service.containsCharacteristic(char_handle)); + + if (state() == StateConnected) { + uint req = d->att->requestRead(characteristic.valueHandle(), + d, SLOT(handleCharacteristicRead(uint,QByteArray))); + d->pending_characteristic_read_reqs.insert(req, char_handle); + } else { + qWarning() << "Not connected"; + } +} + +void GatoPeripheral::readValue(const GatoDescriptor &descriptor) +{ + Q_D(GatoPeripheral); + + GatoHandle desc_handle = descriptor.handle(); + GatoHandle char_handle = d->descriptor_to_characteristic.value(desc_handle); + + if (!char_handle) { + qWarning() << "Unknown descriptor for this peripheral"; + return; + } + + GatoHandle service_handle = d->characteristic_to_service.value(char_handle); + Q_ASSERT(service_handle); + + GatoService &our_service = d->services[service_handle]; + Q_ASSERT(our_service.containsCharacteristic(char_handle)); + + if (state() == StateConnected) { + uint req = d->att->requestRead(descriptor.handle(), + d, SLOT(handleDescriptorRead(uint,QByteArray))); + d->pending_descriptor_read_reqs.insert(req, char_handle); + } else { + qWarning() << "Not connected"; + } +} + +void GatoPeripheral::writeValue(const GatoCharacteristic &characteristic, const QByteArray &data) +{ + Q_D(GatoPeripheral); + + GatoHandle char_handle = characteristic.startHandle(); + GatoHandle service_handle = d->characteristic_to_service.value(char_handle); + + if (!service_handle) { + qWarning() << "Unknown characteristic for this peripheral"; + return; + } + + GatoService &our_service = d->services[service_handle]; + Q_ASSERT(our_service.containsCharacteristic(char_handle)); + + if (state() == StateConnected) { + d->att->requestWrite(characteristic.valueHandle(), data, + d, SLOT(handleCharacteristicRead(uint,QByteArray))); + } else { + qWarning() << "Not connected"; + } +} + +void GatoPeripheral::writeValue(const GatoDescriptor &descriptor, const QByteArray &data) +{ + Q_D(GatoPeripheral); + + GatoHandle desc_handle = descriptor.handle(); + GatoHandle char_handle = d->descriptor_to_characteristic.value(desc_handle); + + if (!char_handle) { + qWarning() << "Unknown descriptor for this peripheral"; + return; + } + + GatoHandle service_handle = d->characteristic_to_service.value(char_handle); + Q_ASSERT(service_handle); + + GatoService &our_service = d->services[service_handle]; + Q_ASSERT(our_service.containsCharacteristic(char_handle)); + + if (state() == StateConnected) { + d->att->requestWrite(descriptor.handle(), data, + d, SLOT(handleDescriptorWrite(uint,bool))); + } else { + qWarning() << "Not connected"; + } +} + +void GatoPeripheral::setNotification(const GatoCharacteristic &characteristic, bool enabled) +{ + Q_D(GatoPeripheral); + + GatoHandle char_handle = characteristic.startHandle(); + GatoHandle service_handle = d->characteristic_to_service.value(char_handle); + + if (!service_handle) { + qWarning() << "Unknown characteristic for this peripheral"; + return; + } + + GatoService &our_service = d->services[service_handle]; + Q_ASSERT(our_service.containsCharacteristic(char_handle)); + GatoCharacteristic our_char = our_service.getCharacteristic(char_handle); + + if (!(our_char.properties() & GatoCharacteristic::PropertyNotify)) { + qWarning() << "Characteristic does not support notifications"; + return; + } + + if (state() != StateConnected) { + qWarning() << "Not connected"; + return; + } + + const GatoUUID uuid(GatoUUID::GattClientCharacteristicConfiguration); + if (our_char.containsDescriptor(uuid)) { + GatoDescriptor desc = our_char.getDescriptor(uuid); + d->pending_set_notify.remove(char_handle); + writeValue(characteristic, d->genClientCharConfiguration(true, false)); + } else { + d->pending_set_notify[char_handle] = enabled; + discoverDescriptors(our_char); // May need to find appropiate descriptor + } +} + +GatoPeripheralPrivate::GatoPeripheralPrivate(GatoPeripheral *parent) + : QObject(parent), q_ptr(parent), + complete_name(false), complete_services(false) +{ +} + +GatoPeripheralPrivate::~GatoPeripheralPrivate() +{ + delete att; +} + +void GatoPeripheralPrivate::parseEIRFlags(quint8 data[], int len) +{ + Q_UNUSED(data); + Q_UNUSED(len); + // Nothing to do for now. +} + +void GatoPeripheralPrivate::parseEIRUUIDs(int size, bool complete, quint8 data[], int len) +{ + Q_UNUSED(complete); + + for (int pos = 0; pos < len; pos += size) { + GatoUUID uuid; + switch (size) { + case 16/8: + uuid = GatoUUID(qFromLittleEndian<quint16>(&data[pos])); + break; + case 32/8: + uuid = GatoUUID(qFromLittleEndian<quint32>(&data[pos])); + break; + case 128/8: + uuid = GatoUUID(qFromLittleEndian<gatouint128>(&data[pos])); + break; + } + + service_uuids.insert(uuid); + } +} + +void GatoPeripheralPrivate::parseName(bool complete, quint8 data[], int len) +{ + Q_Q(GatoPeripheral); + if (complete || !complete_name) { + name = QString::fromAscii(reinterpret_cast<char*>(data), len); + complete_name = complete; + emit q->nameChanged(); + } +} + +GatoCharacteristic GatoPeripheralPrivate::parseCharacteristicValue(const QByteArray &ba) +{ + GatoCharacteristic characteristic; + const char *data = ba.constData(); + + quint8 properties = data[0]; + characteristic.setProperties(GatoCharacteristic::Properties(properties)); + + GatoHandle handle = read_le<quint16>(&data[1]); + characteristic.setValueHandle(handle); + + GatoUUID uuid = bytearray_to_gatouuid(ba.mid(3)); + characteristic.setUuid(uuid); + + return characteristic; +} + +QByteArray GatoPeripheralPrivate::genClientCharConfiguration(bool notification, bool indication) +{ + QByteArray ba; + ba.resize(sizeof(quint16)); + + quint16 val = 0; + if (notification) + val |= 0x1; + if (indication) + val |= 0x2; + + write_le<quint16>(val, ba.data()); + + return ba; +} + +void GatoPeripheralPrivate::clearServices() +{ + characteristic_to_service.clear(); + value_to_characteristic.clear(); + descriptor_to_characteristic.clear(); + services.clear(); +} + +void GatoPeripheralPrivate::clearServiceCharacteristics(GatoService *service) +{ + QList<GatoCharacteristic> chars = service->characteristics(); + QList<GatoCharacteristic>::iterator it; + for (it = chars.begin(); it != chars.end(); ++it) { + clearCharacteristicDescriptors(&*it); + characteristic_to_service.remove(it->startHandle()); + value_to_characteristic.remove(it->valueHandle()); + } + service->clearCharacteristics(); +} + +void GatoPeripheralPrivate::clearCharacteristicDescriptors(GatoCharacteristic *characteristic) +{ + QList<GatoDescriptor> descs = characteristic->descriptors(); + foreach (const GatoDescriptor& d, descs) { + descriptor_to_characteristic.remove(d.handle()); + } + characteristic->clearDescriptors(); +} + +void GatoPeripheralPrivate::finishSetNotifyOperations(const GatoCharacteristic &characteristic) +{ + Q_Q(GatoPeripheral); + + GatoHandle handle = characteristic.startHandle(); + + if (pending_set_notify.contains(handle)) { + const GatoUUID uuid(GatoUUID::GattClientCharacteristicConfiguration); + bool notify = pending_set_notify.value(handle); + + foreach (const GatoDescriptor &descriptor, characteristic.descriptors()) { + if (descriptor.uuid() == uuid) { + q->writeValue(descriptor, genClientCharConfiguration(notify, false)); + } + } + + pending_set_notify.remove(handle); + } +} + +void GatoPeripheralPrivate::handleAttConnected() +{ + Q_Q(GatoPeripheral); + + emit q->connected(); +} + +void GatoPeripheralPrivate::handleAttDisconnected() +{ + Q_Q(GatoPeripheral); + + // Forget about all pending requests + pending_primary_reqs.clear(); + pending_characteristic_reqs.clear(); + pending_characteristic_read_reqs.clear(); + pending_descriptor_reqs.clear(); + pending_descriptor_read_reqs.clear(); + + emit q->disconnected(); +} + +void GatoPeripheralPrivate::handleAttAttributeUpdated(GatoHandle handle, const QByteArray &value, bool confirmed) +{ + Q_Q(GatoPeripheral); + Q_UNUSED(confirmed); + + // Let's see if this is a handle we know about. + if (value_to_characteristic.contains(handle)) { + // Ok, it's a characteristic value. + GatoHandle char_handle = value_to_characteristic.value(handle); + GatoHandle service_handle = characteristic_to_service.value(char_handle); + if (!service_handle) { + qWarning() << "Got a notification for a characteristic I don't know about"; + return; + } + + GatoService &service = services[service_handle]; + GatoCharacteristic characteristic = service.getCharacteristic(char_handle); + + emit q->valueUpdated(characteristic, value); + } +} + +void GatoPeripheralPrivate::handlePrimary(uint req, const QList<GatoAtt::AttributeGroupData> &list) +{ + Q_Q(GatoPeripheral); + Q_UNUSED(req); + + if (list.isEmpty()) { + complete_services = true; + emit q->servicesDiscovered(); + } else { + GatoHandle last_handle = 0; + + foreach (const GatoAtt::AttributeGroupData &data, list) { + GatoUUID uuid = bytearray_to_gatouuid(data.value); + GatoService service; + + service.setUuid(uuid); + service.setStartHandle(data.start); + service.setEndHandle(data.end); + + services.insert(data.start, service); + service_uuids.insert(uuid); + + last_handle = data.end; + } + + // Fetch following attributes + att->requestReadByGroupType(last_handle + 1, 0xFFFF, GatoUUID::GattPrimaryService, + this, SLOT(handlePrimary(uint,QList<GatoAtt::AttributeGroupData>))); + } +} + +void GatoPeripheralPrivate::handlePrimaryForService(uint req, const QList<GatoAtt::HandleInformation> &list) +{ + Q_Q(GatoPeripheral); + + GatoUUID uuid = pending_primary_reqs.value(req, GatoUUID()); + if (uuid.isNull()) { + qDebug() << "Got primary for service response for a request I did not make"; + return; + } + pending_primary_reqs.remove(req); + + if (list.isEmpty()) { + if (pending_primary_reqs.isEmpty()) { + emit q->servicesDiscovered(); + } + } else { + GatoHandle last_handle = 0; + + foreach (const GatoAtt::HandleInformation &data, list) { + GatoService service; + + service.setUuid(uuid); + service.setStartHandle(data.start); + service.setEndHandle(data.end); + + services.insert(data.start, service); + service_uuids.insert(uuid); + + last_handle = data.end; + } + + // Fetch following attributes + QByteArray value = gatouuid_to_bytearray(uuid, true, false); + uint req = att->requestFindByTypeValue(last_handle + 1, 0xFFFF, GatoUUID::GattPrimaryService, value, + this, SLOT(handlePrimaryForService(uint,QList<GatoAtt::HandleInformation>))); + pending_primary_reqs.insert(req, uuid); + } +} + +void GatoPeripheralPrivate::handleCharacteristic(uint req, const QList<GatoAtt::AttributeData> &list) +{ + Q_Q(GatoPeripheral); + + GatoHandle service_start = pending_characteristic_reqs.value(req, 0); + if (!service_start) { + qDebug() << "Got characteristics for a request I did not make"; + return; + } + pending_characteristic_reqs.remove(req); + + Q_ASSERT(services.contains(service_start)); + GatoService &service = services[service_start]; + Q_ASSERT(service.startHandle() == service_start); + + if (list.isEmpty()) { + emit q->characteristicsDiscovered(service); + } else { + GatoHandle last_handle = 0; + + // If we are continuing a characteristic list, this means the + // last service we discovered in the previous iteration was not + // the last one, so we have to reduce its endHandle! + QList<GatoCharacteristic> cur_chars = service.characteristics(); + if (!cur_chars.isEmpty()) { + GatoCharacteristic &last = cur_chars.back(); + last.setEndHandle(list.front().handle - 1); + service.addCharacteristic(last); + } + + for (int i = 0; i < list.size(); i++) { + const GatoAtt::AttributeData &data = list.at(i); + GatoCharacteristic characteristic = parseCharacteristicValue(data.value); + + characteristic.setStartHandle(data.handle); + if (i + 1 < list.size()) { + characteristic.setEndHandle(list.at(i + 1).handle - 1); + } else { + characteristic.setEndHandle(service.endHandle()); + } + + service.addCharacteristic(characteristic); + characteristic_to_service.insert(data.handle, service_start); + value_to_characteristic.insert(characteristic.valueHandle(), data.handle); + + last_handle = data.handle; + } + + if (last_handle >= service.endHandle()) { + // Already finished, no need to send another request + emit q->characteristicsDiscovered(service); + return; + } + + // Fetch following attributes + uint req = att->requestReadByType(last_handle + 1, service.endHandle(), GatoUUID::GattCharacteristic, + this, SLOT(handleCharacteristic(uint,QList<GatoAtt::AttributeData>))); + pending_characteristic_reqs.insert(req, service.startHandle()); + } +} + +void GatoPeripheralPrivate::handleDescriptors(uint req, const QList<GatoAtt::InformationData> &list) +{ + Q_Q(GatoPeripheral); + + GatoHandle char_handle = pending_descriptor_reqs.value(req); + if (!char_handle) { + qDebug() << "Got descriptor for a request I did not make"; + return; + } + pending_descriptor_reqs.remove(req); + GatoHandle service_handle = characteristic_to_service.value(char_handle); + if (!service_handle) { + qWarning() << "Unknown characteristic during descriptor discovery: " << char_handle; + return; + } + + Q_ASSERT(services.contains(service_handle)); + GatoService &service = services[service_handle]; + Q_ASSERT(service.startHandle() == service_handle); + + Q_ASSERT(service.containsCharacteristic(char_handle)); + GatoCharacteristic characteristic = service.getCharacteristic(char_handle); + + if (list.isEmpty()) { + finishSetNotifyOperations(characteristic); + emit q->descriptorsDiscovered(characteristic); + } else { + GatoHandle last_handle = 0; + + foreach (const GatoAtt::InformationData &data, list) { + // Skip the value attribute itself. + if (data.handle == characteristic.valueHandle()) continue; + + GatoDescriptor descriptor; + + descriptor.setHandle(data.handle); + descriptor.setUuid(data.uuid); + + characteristic.addDescriptor(descriptor); + + service.addCharacteristic(characteristic); + descriptor_to_characteristic.insert(data.handle, char_handle); + + last_handle = data.handle; + } + + service.addCharacteristic(characteristic); + + if (last_handle >= characteristic.endHandle()) { + // Already finished, no need to send another request + finishSetNotifyOperations(characteristic); + emit q->descriptorsDiscovered(characteristic); + return; + } + + // Fetch following attributes + uint req = att->requestFindInformation(last_handle + 1, characteristic.endHandle(), + this, SLOT(handleDescriptors(uint,QList<GatoAtt::InformationData>))); + pending_descriptor_reqs.insert(req, char_handle); + + } +} + +void GatoPeripheralPrivate::handleCharacteristicRead(uint req, const QByteArray &value) +{ + Q_Q(GatoPeripheral); + + GatoHandle char_handle = pending_characteristic_read_reqs.value(req); + if (!char_handle) { + qDebug() << "Got characteristics for a request I did not make"; + return; + } + pending_characteristic_read_reqs.remove(req); + GatoHandle service_handle = characteristic_to_service.value(char_handle); + if (!service_handle) { + qWarning() << "Unknown characteristic during read: " << char_handle; + return; + } + + Q_ASSERT(services.contains(service_handle)); + GatoService &service = services[service_handle]; + Q_ASSERT(service.startHandle() == service_handle); + + Q_ASSERT(service.containsCharacteristic(char_handle)); + GatoCharacteristic characteristic = service.getCharacteristic(char_handle); + + emit q->valueUpdated(characteristic, value); +} + +void GatoPeripheralPrivate::handleDescriptorRead(uint req, const QByteArray &value) +{ + Q_Q(GatoPeripheral); + + GatoHandle desc_handle = pending_descriptor_read_reqs.value(req); + if (!desc_handle) { + qDebug() << "Got characteristics for a request I did not make"; + return; + } + pending_descriptor_read_reqs.remove(req); + GatoHandle char_handle = descriptor_to_characteristic.value(desc_handle); + if (!char_handle) { + qWarning() << "Unknown characteristic during read: " << char_handle; + return; + } + GatoHandle service_handle = characteristic_to_service.value(char_handle); + if (!service_handle) { + qWarning() << "Unknown characteristic during read: " << char_handle; + return; + } + + Q_ASSERT(services.contains(service_handle)); + GatoService &service = services[service_handle]; + Q_ASSERT(service.startHandle() == service_handle); + + Q_ASSERT(service.containsCharacteristic(char_handle)); + GatoCharacteristic characteristic = service.getCharacteristic(char_handle); + + Q_ASSERT(characteristic.containsDescriptor(desc_handle)); + GatoDescriptor descriptor = characteristic.getDescriptor(desc_handle); + + emit q->descriptorValueUpdated(descriptor, value); +} + +void GatoPeripheralPrivate::handleCharacteristicWrite(uint req, bool ok) +{ + Q_UNUSED(req); + if (!ok) { + qWarning() << "Failed to write some characteristic"; + } +} + +void GatoPeripheralPrivate::handleDescriptorWrite(uint req, bool ok) +{ + Q_UNUSED(req); + if (!ok) { + qWarning() << "Failed to write some characteristic"; + } +} |