diff --git a/ble/CCCDSetter.h b/ble/CCCDSetter.h new file mode 100644 index 0000000..57173bb --- /dev/null +++ b/ble/CCCDSetter.h @@ -0,0 +1,198 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FunctionPointerWithContext.h" +#include "GattClient.h" +#include "GattAttribute.h" +#include "DiscoveredCharacteristic.h" +#include "CharacteristicDescriptorDiscovery.h" + +#ifndef __CCCDSETTER_H__ +#define __CCCDSETTER_H__ + +/** + * This class encapsulates the data needed to: + * - Discover Characteristic Descriptors + * - Identify the Client Configuration Characteristic Descriptor (CCCD) handle. + * - Intitiate the write procedure to the CCCD. + * + * The object can be instanciated by using the launch() function with the + * required arguments. The user is able to register a write callback that will + * be executed at the end of the CCCD write procedure with the final outcome of + * the operation. + * + * @note This object will automatically release any resources acquired during + * its lifetime. + */ +class CCCDSetter { +public: + /** + * Create a new instance of the object and launch the Characteristic + * Descriptor Discovery procedure to find the CCCD handle. + * + * @param[in] client + * A pointer to the GattClient used for the operation. + * @param[in] characteristic + * The characteristic that contains the CCCD to write. + * @param[in] callback + * The callback executed at the end of the CCCD write procedure. + * @param[in] cccdData + * The new data to write to the CCCD. + */ + static void launch(GattClient* client, + const DiscoveredCharacteristic &characteristic, + const GattClient::WriteCallback_t& callback, + uint16_t cccdData) { + CCCDSetter* cccdSetterPtr = new CCCDSetter(client, characteristic, callback, cccdData); + cccdSetterPtr->attach(); + } + +private: + /** + * Construct a new instance of this object. + * + * @param[in] client + * A pointer to the GattClient used for the operation. + * @param[in] characteristic + * The characteristic that contains the CCCD to write. + * @param[in] callback + * The callback executed at the end of the CCCD write procedure. + * @param[in] cccdData + * The new data to write to the CCCD. + */ + CCCDSetter(GattClient* client, + const DiscoveredCharacteristic &characteristic, + const GattClient::WriteCallback_t& callback, + uint16_t cccdData) : + _client(client), + _characteristic(characteristic), + _callback(callback), + _cccdData(cccdData), + _cccdFound(false) { } + + /** + * Start the Characteristic Descriptor Discovery procedure to find the CCCD + * handle and register callbacks for descriptor discovered and descriptor + * discovery termination events. + */ + void attach(void) { + /* Launch descriptor discovery procedure */ + FunctionPointerWithContext descriptorDiscoveredCallback(this, &CCCDSetter::descriptorDiscovered); + FunctionPointerWithContext descriptorDiscoveryTerminationCallback(this, &CCCDSetter::descriptorDiscoveryTermination); + _characteristic.discoverDescriptors(descriptorDiscoveredCallback, descriptorDiscoveryTerminationCallback); + } + + /** + * Callback that will be executed when a characteristic descriptor discovery + * termination event occurs. If the CCCD was not previously found, then this + * callback will call the user-registered write callback with failure values. + * + * @note This callback release all resources previously acquired by this + * object. + */ + void descriptorDiscoveryTermination( + const CharacteristicDescriptorDiscovery::TerminationCallbackParams_t *) { + /* + * We do not check params->status because we really only care that the + * CCCD is found and that the write procedure succeeded/failed and not + * the discovery procedure + */ + if (_cccdFound) { + /* The CCCD was found and write was triggered */ + delete this; + return; + } + + /* The CCCD was not found post a callback to the user to report failure */ + GattWriteCallbackParams callbackParams; + callbackParams.connHandle = _characteristic.getConnectionHandle(); + callbackParams.handle = GattAttribute::INVALID_HANDLE; + callbackParams.writeOp = GattWriteCallbackParams::OP_INVALID; + callbackParams.offset = 0; + callbackParams.len = 0; + callbackParams.data = NULL; + _callback.call(&callbackParams); + + delete this; + } + + /** + * Callback that will be executed when a characteristic descriptor is + * discovered. If the descriptor is the CCCD, then the characteristic + * descriptor discovery procedure is terminated and a write procedure is + * started to update the CCCD. + * + * @param[in] params + * Information about the discovered characteristric descriptor. + */ + void descriptorDiscovered( + const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t *params) { + UUID cccdUuid(BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG); + if (params->descriptor.getUUID() == cccdUuid) { + /* This is the CCCD that we need to write */ + /* Update setter config information */ + _cccdFound = true; + /* Terminate the characteristic descriptor discovery */ + _client->terminateCharacteristicDescriptorDiscovery(params->characteristic); + /* + * Write the descriptor with the new value, this will trigger a + * callback to user code that will report the final status of the write + */ + ble_error_t error = _client->write( + GattClient::GATT_OP_WRITE_REQ, + _characteristic.getConnectionHandle(), + params->descriptor.getAttributeHandle(), + sizeof(CCCDSetter::_cccdData), + (const uint8_t*) &_cccdData + ); + if (error != BLE_ERROR_NONE) { + /* Report the error and terminate */ + GattWriteCallbackParams callbackParams; + callbackParams.connHandle = _characteristic.getConnectionHandle(); + callbackParams.handle = params->descriptor.getAttributeHandle(); + callbackParams.writeOp = GattWriteCallbackParams::OP_WRITE_REQ; + callbackParams.offset = 0; + callbackParams.len = 0; + callbackParams.data = NULL; + _callback.call(&callbackParams); + } + } + } + + /** + * The GattClient instance that created this object. + */ + GattClient* _client; + /** + * The discovered characteristic that contains the CCCD to write. + */ + DiscoveredCharacteristic _characteristic; + /** + * The registered callback that will be executed when the write procedure + * terminates. + */ + GattClient::WriteCallback_t _callback; + /** + * The data to write to the CCCD. + */ + uint16_t _cccdData; + /** + * Flag when the CCCD has been found. + */ + bool _cccdFound; +}; + +#endif /* __CCCDSETTER_H__ */ diff --git a/ble/DiscoveredCharacteristic.h b/ble/DiscoveredCharacteristic.h index 3529c36..3cc3ac0 100644 --- a/ble/DiscoveredCharacteristic.h +++ b/ble/DiscoveredCharacteristic.h @@ -265,6 +265,26 @@ class DiscoveredCharacteristic { uuid.setupLong(longUUID, order); } + /** + * Write the characteristic's Client Characteristic Configuration Descriptor + * (CCCD) to enable/disable indications and notifications. + * + * @param[in] indicationValue + * The new value for the indications bit. + * @param[in] notificationValue + * The new value for the notifications bit. + * @param[in] writeCallback + * Callback executed at the end of the procedure. + * + * @retval BLE_ERROR_NONE if the procedure was correctly started. + * @retval BLE_ERROR_OPERATION_NOT_PERMITTED if the indication + * characteristic does not allow updates to the notification or + * indications value. In the case that one of them is locked setting + * the respective parameter (i.e. @p indicationValue or + * @p notificationValue) to false clears this error. + */ + ble_error_t setCCCD(bool indicationValue, bool notificationValue, const GattClient::WriteCallback_t &writeCallback) const; + public: /** * @brief Get the UUID of the discovered characteristic diff --git a/ble/DiscoveredCharacteristicDescriptor.h b/ble/DiscoveredCharacteristicDescriptor.h index 07aa127..1baa8a0 100644 --- a/ble/DiscoveredCharacteristicDescriptor.h +++ b/ble/DiscoveredCharacteristicDescriptor.h @@ -22,6 +22,7 @@ #include "GattAttribute.h" #include "GattClient.h" #include "CharacteristicDescriptorDiscovery.h" +#include "OneShotWriteCallback.h" /** * @brief Representation of a descriptor discovered during a GattClient @@ -101,6 +102,34 @@ class DiscoveredCharacteristicDescriptor { return _gattHandle; } + /** + * @brief Initiate the write procedure of this characteristic descriptor. + * + * @param[in] lenght + * Length in bytes of the data to write. + * @param[in] value + * Pointer to the data to write. + * @param[in] callback + * The callback executed at the end of the write procedure. + * + * @return BLE_ERROR_NONE if the write procedure was successfully started. + */ + ble_error_t write(uint16_t length, const uint8_t *value, const GattClient::WriteCallback_t& callback) const { + ble_error_t error = _client->write( + GattClient::GATT_OP_WRITE_REQ, + _connectionHandle, + _gattHandle, + length, + value + ); + + if (error == BLE_ERROR_NONE) { + OneShotWriteCallback::launch(_client, _connectionHandle, _gattHandle, callback); + } + + return error; + } + private: GattClient *_client; Gap::Handle_t _connectionHandle; diff --git a/ble/OneShotReadCallback.h b/ble/OneShotReadCallback.h new file mode 100644 index 0000000..d0b1f7b --- /dev/null +++ b/ble/OneShotReadCallback.h @@ -0,0 +1,185 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BLE.h" +#include "GattClient.h" +#include "Gap.h" +#include "GattAttribute.h" + +#ifndef __ONESHOTREADCALLBACK_H__ +#define __ONESHOTREADCALLBACK_H__ + +/** + * This class encapsulates the data needed to keep track a read procedure to + * a GATT Attribute in the peer's ATT table. The object can be instanciated by + * using the launch() function with the required arguments. The user is able to + * register a read callback that will be executed at the end of the read + * procedure with the final outcome of the operation. + * + * @note This object will automatically release any resources acquired during + * its lifetime. + */ +class OneShotReadCallback { +public: + /** + * Create a new instance of the object and launch the read procedure with + * the specified arguments. + * + * @param[in] client + * A pointer to the GattClient used to trigger the read + * procedure. + * @param[in] connHandle + * The ID of the connection to execute the read procedure. + * @param[in] handle + * The attribute handle of the value read in the peer's + * ATT table. + * @param[in] cb + * The callback executed at the end of the read procedure. + */ + static void launch(GattClient* client, Gap::Handle_t connHandle, + GattAttribute::Handle_t handle, const GattClient::ReadCallback_t& cb) { + OneShotReadCallback* oneShot = new OneShotReadCallback(client, connHandle, handle, cb); + oneShot->attach(); + /* Delete will be made when this callback is called */ + } + +private: + /** + * Construct a new instance of this object. + * + * @param[in] client + * A pointer to the GattClient used to trigger the read + * procedure. + * @param[in] connHandle + * The ID of the connection to execute the read procedure. + * @param[in] handle + * The attribute handle of the value read in the peer's + * ATT table. + * @param[in] cb + * The callback executed at the end of the read procedure. + */ + OneShotReadCallback(GattClient* client, Gap::Handle_t connHandle, + GattAttribute::Handle_t handle, const GattClient::ReadCallback_t& cb) : + _client(client), + _connHandle(connHandle), + _handle(handle), + _callback(cb) { } + + /** + * Attach callbacks to the GattClient and Gap to receive shutdown, data + * read and disconnection events. + */ + void attach() { + _client->onDataRead(makeFunctionPointer(this, &OneShotReadCallback::call)); + _client->onShutdown(this, &OneShotReadCallback::shutdownCallback); + BLE::Instance().gap().onDisconnection(this, &OneShotReadCallback::disconnectionCallback); + } + + /** + * This callback will be executed in case the user calls BLE::shutdown() + * before the read procedure completes. In this case, the call() callback + * will never be executed, but the resources acquired by this object need + * to be released. + * + * @param[in] client + * A pointer to the GattClient used to trigger the read + * procedure. + */ + void shutdownCallback(const GattClient *client) { + /* Avoid compiler warnings */ + (void) client; + + /* + * Free resources allocated to this object. Pending read callbacks + * will no longer be executed. + */ + delete this; + } + + /** + * This callback will be executed when a disconnection even occurs before + * the read procedure completes. In this case, the call() callback + * will never be executed, but the resources acquired by this object need + * to be released. + * + * @param[in] params + * The disconnection parameters. + */ + void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) { + if (params->handle == _connHandle) { + /* Tell the user that the operation failed */ + GattReadCallbackParams callbackParams; + callbackParams.connHandle = _connHandle; + callbackParams.handle = _handle; + callbackParams.offset = 0; + callbackParams.len = 0; + callbackParams.data = NULL; + _callback(&callbackParams); + + /* + * Free resources allocated to this object. Pending write callbacks + * will no longer be executed. + */ + _client->onDataRead().detach(makeFunctionPointer(this, &OneShotReadCallback::call)); + _client->onShutdown().detach(makeFunctionPointer(this, &OneShotReadCallback::shutdownCallback)); + BLE::Instance().gap().onDisconnection().detach(makeFunctionPointer(this, &OneShotReadCallback::disconnectionCallback)); + delete this; + } + } + + /** + * Registered callback that will be executed at the end of the read + * procedure. If this callback is called with the correct connection and + * attribute handles, then it will do the following: + * - Call the user-registered read callback. + * - Detach its callbacks registered for disconnection, shutdown and + * data read events. + * - Release its resources. + * + * @param[in] param + * Parameters specifying the result of the write operation. + */ + void call(const GattReadCallbackParams* params) { + /* Verifiy that it is the right characteristic on the right connection */ + if (params->connHandle == _connHandle && params->handle == _handle) { + _callback(params); + _client->onDataRead().detach(makeFunctionPointer(this, &OneShotReadCallback::call)); + _client->onShutdown().detach(makeFunctionPointer(this, &OneShotReadCallback::shutdownCallback)); + BLE::Instance().gap().onDisconnection().detach(makeFunctionPointer(this, &OneShotReadCallback::disconnectionCallback)); + delete this; + } + } + + /** + * The GattClient instance that created this object. + */ + GattClient* _client; + /** + * The connection ID that used to execute the read procedure. + */ + Gap::Handle_t _connHandle; + /** + * The attribute handle of the value read in the peer's ATT table. + */ + GattAttribute::Handle_t _handle; + /** + * The registered callback that will be executed when the read procedure + * terminates. + */ + GattClient::ReadCallback_t _callback; +}; + +#endif /* __ONESHOTREADCALLBACK_H__ */ diff --git a/ble/OneShotWriteCallback.h b/ble/OneShotWriteCallback.h new file mode 100644 index 0000000..cafd74b --- /dev/null +++ b/ble/OneShotWriteCallback.h @@ -0,0 +1,186 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BLE.h" +#include "GattClient.h" +#include "Gap.h" +#include "GattAttribute.h" + +#ifndef __ONESHOTWRITECALLBACK_H__ +#define __ONESHOTWRITECALLBACK_H__ + +/** + * This class encapsulates the data needed to keep track a write procedure to + * a GATT Attribute in the peer's ATT table. The object can be instanciated by + * using the launch() function with the required arguments. The user is able to + * register a write callback that will be executed at the end of the write + * procedure with the final outcome of the operation. + * + * @note This object will automatically release any resources acquired during + * its lifetime. + */ +class OneShotWriteCallback { +public: + /** + * Create a new instance of the object and launch the write procedure with + * the specified arguments. + * + * @param[in] client + * A pointer to the GattClient used to trigger the write + * procedure. + * @param[in] connHandle + * The ID of the connection to execute the write procedure. + * @param[in] handle + * The attribute handle of the value written in the peer's + * ATT table. + * @param[in] cb + * The callback executed at the end of the write procedure. + */ + static void launch(GattClient* client, Gap::Handle_t connHandle, + GattAttribute::Handle_t handle, const GattClient::WriteCallback_t& cb) { + OneShotWriteCallback* oneShot = new OneShotWriteCallback(client, connHandle, handle, cb); + oneShot->attach(); + /* Delete will be made when this callback is called */ + } + +private: + /** + * Construct a new instance of this object. + * + * @param[in] client + * A pointer to the GattClient used to trigger the write + * procedure. + * @param[in] connHandle + * The ID of the connection to execute the write procedure. + * @param[in] handle + * The attribute handle of the value written in the peer's + * ATT table. + * @param[in] cb + * The callback executed at the end of the write procedure. + */ + OneShotWriteCallback(GattClient* client, Gap::Handle_t connHandle, + GattAttribute::Handle_t handle, const GattClient::WriteCallback_t& cb) : + _client(client), + _connHandle(connHandle), + _handle(handle), + _callback(cb) { } + + /** + * Attach callbacks to the GattClient and Gap to receive shutdown, data + * written and disconnection events. + */ + void attach(void) { + _client->onDataWritten(makeFunctionPointer(this, &OneShotWriteCallback::call)); + _client->onShutdown(this, &OneShotWriteCallback::shutdownCallback); + BLE::Instance().gap().onDisconnection(this, &OneShotWriteCallback::disconnectionCallback); + } + + /** + * This callback will be executed in case the user calls BLE::shutdown() + * before the write procedure completes. In this case, the call() callback + * will never be executed, but the resources acquired by this object need + * to be released. + * + * @param[in] client + * A pointer to the GattClient used to trigger the write + * procedure. + */ + void shutdownCallback(const GattClient *client) { + /* Avoid compiler warnings */ + (void) client; + + /* + * Free resources allocated to this object. Pending write callbacks + * will no longer be executed. + */ + delete this; + } + + /** + * This callback will be executed when a disconnection even occurs before + * the write procedure completes. In this case, the call() callback + * will never be executed, but the resources acquired by this object need + * to be released. + * + * @param[in] params + * The disconnection parameters. + */ + void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) { + if (params->handle == _connHandle) { + /* Tell the user that the operation failed */ + GattWriteCallbackParams callbackParams; + callbackParams.connHandle = _connHandle; + callbackParams.handle = _handle; + callbackParams.writeOp = GattWriteCallbackParams::OP_INVALID; + callbackParams.offset = 0; + callbackParams.len = 0; + callbackParams.data = NULL; + _callback(&callbackParams); + + /* + * Free resources allocated to this object. Pending write callbacks + * will no longer be executed. + */ + _client->onDataWritten().detach(makeFunctionPointer(this, &OneShotWriteCallback::call)); + _client->onShutdown().detach(makeFunctionPointer(this, &OneShotWriteCallback::shutdownCallback)); + BLE::Instance().gap().onDisconnection().detach(makeFunctionPointer(this, &OneShotWriteCallback::disconnectionCallback)); + delete this; + } + } + + /** + * Registered callback that will be executed at the end of the write + * procedure. If this callback is called with the correct connection and + * attribute handles, then it will do the following: + * - Call the user-registered write callback. + * - Detach its callbacks registered for disconnection, shutdown and + * data written events. + * - Release its resources. + * + * @param[in] param + * Parameters specifying the result of the write operation. + */ + void call(const GattWriteCallbackParams* params) { + /* Verifiy that it is the right characteristic on the right connection */ + if (params->connHandle == _connHandle && params->handle == _handle) { + _callback(params); + _client->onDataWritten().detach(makeFunctionPointer(this, &OneShotWriteCallback::call)); + _client->onShutdown().detach(makeFunctionPointer(this, &OneShotWriteCallback::shutdownCallback)); + BLE::Instance().gap().onDisconnection().detach(makeFunctionPointer(this, &OneShotWriteCallback::disconnectionCallback)); + delete this; + } + } + + /** + * The GattClient instance that created this object. + */ + GattClient* _client; + /** + * The connection ID that used to execute the write procedure. + */ + Gap::Handle_t _connHandle; + /** + * The attribute handle of the value written in the peer's ATT table. + */ + GattAttribute::Handle_t _handle; + /** + * The registered callback that will be executed when the write procedure + * terminates. + */ + GattClient::WriteCallback_t _callback; +}; + +#endif /* __ONESHOTWRITECALLBACK_H__ */ diff --git a/ble/UUID.h b/ble/UUID.h index 2379412..cde3ada 100644 --- a/ble/UUID.h +++ b/ble/UUID.h @@ -298,6 +298,37 @@ class UUID { return true; } + /* + * If one UUID is short and the other long check whether the short + * can be expanded to equal the long one. + */ + if (((this->type == UUID_TYPE_LONG) && (other.type == UUID_TYPE_SHORT)) || + ((this->type == UUID_TYPE_SHORT) && (other.type == UUID_TYPE_LONG))) { + const uint8_t *thisExpandedUUID; + const uint8_t *otherExpandedUUID; + UUID expandedUUID; + + if (this->type == UUID_TYPE_SHORT) { + expandedUUID = *this; + expandedUUID.from16To128BitUUID(); + thisExpandedUUID = expandedUUID.baseUUID; + } else { + thisExpandedUUID = this->baseUUID; + } + + if (other.type == UUID_TYPE_SHORT) { + expandedUUID = other; + expandedUUID.from16To128BitUUID(); + otherExpandedUUID = expandedUUID.baseUUID; + } else { + otherExpandedUUID = other.baseUUID; + } + + if (memcmp(thisExpandedUUID, otherExpandedUUID, LENGTH_OF_LONG_UUID) == 0) { + return true; + } + } + return false; } @@ -313,6 +344,28 @@ class UUID { return !(*this == other); } +private: + /** + * @brief Expand a 16-bit UUID into a 128-bit UUID. + * + * @details To reconstruct the full 128-bit UUID from the shortened version, insert + * the 16-bit short value (indicated by xxxx, including leading zeros) into + * the Bluetooth Base UUID (in MSB format): + * + * 0000xxxx-0000-1000-8000-00805F9B34FB + */ + void from16To128BitUUID(void) { + if (type == UUID_TYPE_LONG) { + return; + } + uint8_t sig[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }; + sig[2] = (shortUUID >> 8) & 0x00FF; + sig[3] = shortUUID & 0x00FF; + setupLong(sig, UUID::MSB); + type = UUID_TYPE_LONG; + } + private: /** * The UUID type. Refer to UUID_Type_t. diff --git a/source/DiscoveredCharacteristic.cpp b/source/DiscoveredCharacteristic.cpp index e2a6849..9f65690 100644 --- a/source/DiscoveredCharacteristic.cpp +++ b/source/DiscoveredCharacteristic.cpp @@ -16,6 +16,9 @@ #include "ble/DiscoveredCharacteristic.h" #include "ble/GattClient.h" +#include "ble/OneShotWriteCallback.h" +#include "ble/OneShotReadCallback.h" +#include "ble/CCCDSetter.h" ble_error_t DiscoveredCharacteristic::read(uint16_t offset) const @@ -31,41 +34,6 @@ DiscoveredCharacteristic::read(uint16_t offset) const return gattc->read(connHandle, valueHandle, offset); } -struct OneShotReadCallback { - static void launch(GattClient* client, Gap::Handle_t connHandle, - GattAttribute::Handle_t handle, const GattClient::ReadCallback_t& cb) { - OneShotReadCallback* oneShot = new OneShotReadCallback(client, connHandle, handle, cb); - oneShot->attach(); - // delete will be made when this callback is called - } - -private: - OneShotReadCallback(GattClient* client, Gap::Handle_t connHandle, - GattAttribute::Handle_t handle, const GattClient::ReadCallback_t& cb) : - _client(client), - _connHandle(connHandle), - _handle(handle), - _callback(cb) { } - - void attach() { - _client->onDataRead(makeFunctionPointer(this, &OneShotReadCallback::call)); - } - - void call(const GattReadCallbackParams* params) { - // verifiy that it is the right characteristic on the right connection - if (params->connHandle == _connHandle && params->handle == _handle) { - _callback(params); - _client->onDataRead().detach(makeFunctionPointer(this, &OneShotReadCallback::call)); - delete this; - } - } - - GattClient* _client; - Gap::Handle_t _connHandle; - GattAttribute::Handle_t _handle; - GattClient::ReadCallback_t _callback; -}; - ble_error_t DiscoveredCharacteristic::read(uint16_t offset, const GattClient::ReadCallback_t& onRead) const { ble_error_t error = read(offset); if (error) { @@ -105,41 +73,6 @@ DiscoveredCharacteristic::writeWoResponse(uint16_t length, const uint8_t *value) return gattc->write(GattClient::GATT_OP_WRITE_CMD, connHandle, valueHandle, length, value); } -struct OneShotWriteCallback { - static void launch(GattClient* client, Gap::Handle_t connHandle, - GattAttribute::Handle_t handle, const GattClient::WriteCallback_t& cb) { - OneShotWriteCallback* oneShot = new OneShotWriteCallback(client, connHandle, handle, cb); - oneShot->attach(); - // delete will be made when this callback is called - } - -private: - OneShotWriteCallback(GattClient* client, Gap::Handle_t connHandle, - GattAttribute::Handle_t handle, const GattClient::WriteCallback_t& cb) : - _client(client), - _connHandle(connHandle), - _handle(handle), - _callback(cb) { } - - void attach() { - _client->onDataWritten(makeFunctionPointer(this, &OneShotWriteCallback::call)); - } - - void call(const GattWriteCallbackParams* params) { - // verifiy that it is the right characteristic on the right connection - if (params->connHandle == _connHandle && params->handle == _handle) { - _callback(params); - _client->onDataWritten().detach(makeFunctionPointer(this, &OneShotWriteCallback::call)); - delete this; - } - } - - GattClient* _client; - Gap::Handle_t _connHandle; - GattAttribute::Handle_t _handle; - GattClient::WriteCallback_t _callback; -}; - ble_error_t DiscoveredCharacteristic::write(uint16_t length, const uint8_t *value, const GattClient::WriteCallback_t& onRead) const { ble_error_t error = write(length, value); if (error) { @@ -152,7 +85,7 @@ ble_error_t DiscoveredCharacteristic::write(uint16_t length, const uint8_t *valu } ble_error_t DiscoveredCharacteristic::discoverDescriptors( - const CharacteristicDescriptorDiscovery::DiscoveryCallback_t& onCharacteristicDiscovered, + const CharacteristicDescriptorDiscovery::DiscoveryCallback_t& onCharacteristicDiscovered, const CharacteristicDescriptorDiscovery::TerminationCallback_t& onTermination) const { if(!gattc) { @@ -165,3 +98,20 @@ ble_error_t DiscoveredCharacteristic::discoverDescriptors( return err; } + +ble_error_t DiscoveredCharacteristic::setCCCD( + bool indicationValue, + bool notificationValue, + const GattClient::WriteCallback_t &writeCallback) const { + if (!props.notify() && notificationValue) { + return BLE_ERROR_OPERATION_NOT_PERMITTED; + } + if (!props.indicate() && indicationValue) { + return BLE_ERROR_OPERATION_NOT_PERMITTED; + } + + /* Configure the internal structure to prepare for descriptor update */ + CCCDSetter::launch(gattc, *this, writeCallback, notificationValue | (indicationValue << 1)); + + return BLE_ERROR_NONE; +}