sa

Fork of nRF51822 by Nordic Semiconductor

Files at this revision

API Documentation at this revision

Comitter:
vcoubard
Date:
Mon Jan 11 10:19:19 2016 +0000
Parent:
566:e425ad9e5d6e
Child:
568:13b23a4b1f58
Commit message:
Synchronized with git rev 59ced0b4
Author: Vincent Coubard
rename remainingCharacteristic member, now it is named
discoveredCharacteristic. Add doc to the discovery process and the
rationale behind discoveredCharacteristic member.

Changed in this revision

source/btle/btle.cpp Show annotated file Show diff for this revision Revisions of this file
source/btle/btle_discovery.cpp Show annotated file Show diff for this revision Revisions of this file
source/nRF5xCharacteristicDescriptorDiscoverer.cpp Show annotated file Show diff for this revision Revisions of this file
source/nRF5xCharacteristicDescriptorDiscoverer.h Show annotated file Show diff for this revision Revisions of this file
source/nRF5xDiscoveredCharacteristic.h Show annotated file Show diff for this revision Revisions of this file
source/nRF5xGap.cpp Show annotated file Show diff for this revision Revisions of this file
source/nRF5xGattClient.cpp Show annotated file Show diff for this revision Revisions of this file
source/nRF5xGattClient.h Show annotated file Show diff for this revision Revisions of this file
source/nRF5xServiceDiscovery.cpp Show annotated file Show diff for this revision Revisions of this file
source/nRF5xServiceDiscovery.h Show annotated file Show diff for this revision Revisions of this file
--- a/source/btle/btle.cpp	Mon Jan 11 10:19:18 2016 +0000
+++ b/source/btle/btle.cpp	Mon Jan 11 10:19:19 2016 +0000
@@ -41,6 +41,10 @@
 #include "ble_hci.h"
 #include "btle_discovery.h"
 
+#include "nRF5xGattClient.h"
+#include "nRF5xServiceDiscovery.h"
+#include "nRF5xCharacteristicDescriptorDiscoverer.h"
+
 extern "C" void assert_nrf_callback(uint16_t line_num, const uint8_t *p_file_name);
 void            app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t *p_file_name);
 
@@ -154,8 +158,8 @@
             const ble_gap_addr_t *own  = &p_ble_evt->evt.gap_evt.params.connected.own_addr;
             nRF5xGap::getInstance().processConnectionEvent(handle,
                                                            role,
-                                                           static_cast<BLEProtocol::AddressType::Type>(peer->addr_type), peer->addr,
-                                                           static_cast<BLEProtocol::AddressType::Type>(own->addr_type),  own->addr,
+                                                           static_cast<Gap::AddressType_t>(peer->addr_type), peer->addr,
+                                                           static_cast<Gap::AddressType_t>(own->addr_type),  own->addr,
                                                            params);
             break;
         }
@@ -183,6 +187,12 @@
                     reason = static_cast<Gap::DisconnectionReason_t>(p_ble_evt->evt.gap_evt.params.disconnected.reason);
                     break;
             }
+
+            // Close all pending discoveries for this connection
+            nRF5xGattClient& gattClient = nRF5xGattClient::getInstance();
+            gattClient.characteristicDescriptorDiscoverer().terminate(handle, BLE_ERROR_INVALID_STATE);
+            gattClient.discovery().terminate(handle);
+
             nRF5xGap::getInstance().processDisconnectionEvent(handle, reason);
             break;
         }
--- a/source/btle/btle_discovery.cpp	Mon Jan 11 10:19:18 2016 +0000
+++ b/source/btle/btle_discovery.cpp	Mon Jan 11 10:19:19 2016 +0000
@@ -15,12 +15,15 @@
  */
 
 #include "nRF5xServiceDiscovery.h"
+#include "nRF5xCharacteristicDescriptorDiscoverer.h"
 #include "nRF5xGattClient.h"
 
 #if !defined(TARGET_MCU_NRF51_16K_S110) && !defined(TARGET_MCU_NRF51_32K_S110)
 void bleGattcEventHandler(const ble_evt_t *p_ble_evt)
 {
-    nRF5xServiceDiscovery &sdSingleton = nRF5xGattClient::getInstance().discovery;
+    nRF5xServiceDiscovery &sdSingleton = nRF5xGattClient::getInstance().discovery();
+    nRF5xCharacteristicDescriptorDiscoverer &characteristicDescriptorDiscoverer = 
+        nRF5xGattClient::getInstance().characteristicDescriptorDiscoverer();
 
     switch (p_ble_evt->header.evt_id) {
         case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP:
@@ -44,7 +47,7 @@
 
                 case BLE_GATT_STATUS_ATTERR_ATTRIBUTE_NOT_FOUND:
                 default:
-                    sdSingleton.terminateCharacteristicDiscovery();
+                    sdSingleton.terminateCharacteristicDiscovery(BLE_ERROR_NONE);
                     break;
             }
             break;
@@ -91,6 +94,28 @@
                 nRF5xGattClient::getInstance().processHVXEvent(&params);
             }
             break;
+
+        case BLE_GATTC_EVT_DESC_DISC_RSP: {
+            uint16_t conn_handle = p_ble_evt->evt.gattc_evt.conn_handle;
+            uint16_t status = p_ble_evt->evt.gattc_evt.gatt_status;
+            const ble_gattc_evt_desc_disc_rsp_t& discovered_descriptors = p_ble_evt->evt.gattc_evt.params.desc_disc_rsp;
+
+            switch(status) { 
+                case BLE_GATT_STATUS_SUCCESS:
+                    characteristicDescriptorDiscoverer.process(
+                        conn_handle, 
+                        discovered_descriptors
+                    );                
+                    break;
+                case BLE_GATT_STATUS_ATTERR_ATTRIBUTE_NOT_FOUND:
+                    // end of discovery 
+                    characteristicDescriptorDiscoverer.terminate(conn_handle, BLE_ERROR_NONE);
+                    break;
+                default:
+                    characteristicDescriptorDiscoverer.terminate(conn_handle, BLE_ERROR_UNSPECIFIED);
+                    break;
+            }
+        }   break;
     }
 
     sdSingleton.progressCharacteristicDiscovery();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nRF5xCharacteristicDescriptorDiscoverer.cpp	Mon Jan 11 10:19:19 2016 +0000
@@ -0,0 +1,208 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 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 "nRF5xCharacteristicDescriptorDiscoverer.h"
+#include "ble_err.h"
+#include "mbed-drivers/mbed_error.h"
+#include "ble/DiscoveredCharacteristicDescriptor.h"
+
+namespace { 
+    void emptyDiscoveryCallback(const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t*) { }
+    void emptyTerminationCallback(const CharacteristicDescriptorDiscovery::TerminationCallbackParams_t*) { }
+}
+
+nRF5xCharacteristicDescriptorDiscoverer::nRF5xCharacteristicDescriptorDiscoverer(size_t concurrentConnectionsCount) : 
+    maximumConcurrentConnectionsCount(concurrentConnectionsCount), 
+    discoveryRunning(new Discovery[concurrentConnectionsCount]) {
+
+}
+
+nRF5xCharacteristicDescriptorDiscoverer::~nRF5xCharacteristicDescriptorDiscoverer() { 
+    delete [] discoveryRunning;
+}
+
+ble_error_t nRF5xCharacteristicDescriptorDiscoverer::launch(
+    const DiscoveredCharacteristic& characteristic, 
+    const CharacteristicDescriptorDiscovery::DiscoveryCallback_t& discoveryCallback,
+    const CharacteristicDescriptorDiscovery::TerminationCallback_t& terminationCallback
+) {
+    Gap::Handle_t connHandle = characteristic.getConnectionHandle();
+    // it is ok to deduce that the start handle for descriptors is after 
+    // the characteristic declaration and the characteristic value declaration
+    // see BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] (3.3)
+    Gap::Handle_t descriptorStartHandle = characteristic.getDeclHandle() + 2;
+    Gap::Handle_t descriptorEndHandle = characteristic.getLastHandle();
+
+    // check if their is any descriptor to discover
+    if (descriptorEndHandle < descriptorStartHandle) { 
+        CharacteristicDescriptorDiscovery::TerminationCallbackParams_t termParams = { 
+            characteristic,
+            BLE_ERROR_NONE
+        };
+        terminationCallback.call(&termParams);
+        return BLE_ERROR_NONE;
+    }
+
+    // check if we can run this discovery
+    if (isConnectionInUse(connHandle)) { 
+        return BLE_STACK_BUSY;
+    }
+
+    // get a new discovery slot, if none are available, just return
+    Discovery* discovery = getAvailableDiscoverySlot();
+    if(discovery == NULL) {
+        return BLE_STACK_BUSY; 
+    }
+
+    // try to launch the discovery 
+    ble_error_t err = gattc_descriptors_discover(connHandle, descriptorStartHandle, descriptorEndHandle);
+    if(!err) { 
+        // commit the new discovery to its slot
+        *discovery = Discovery(characteristic, discoveryCallback, terminationCallback);
+    }
+
+    return err;
+}
+
+bool nRF5xCharacteristicDescriptorDiscoverer::isActive(const DiscoveredCharacteristic& characteristic) const {
+    return findRunningDiscovery(characteristic) != NULL;
+}
+
+void nRF5xCharacteristicDescriptorDiscoverer::requestTerminate(const DiscoveredCharacteristic& characteristic) {
+    Discovery* discovery = findRunningDiscovery(characteristic);
+    if(discovery) { 
+        discovery->onDiscovery = emptyDiscoveryCallback;
+        // call terminate anyway
+        discovery->terminate(BLE_ERROR_NONE);
+        discovery->onTerminate = emptyTerminationCallback;
+    }
+}
+
+void nRF5xCharacteristicDescriptorDiscoverer::process(uint16_t connectionHandle, const ble_gattc_evt_desc_disc_rsp_t& descriptors) {
+    Discovery* discovery = findRunningDiscovery(connectionHandle);
+    if(!discovery) { 
+        error("logic error in nRF5xCharacteristicDescriptorDiscoverer::process !!!");
+    }
+
+    for (uint16_t i = 0; i < descriptors.count; ++i) {
+        discovery->process(
+            descriptors.descs[i].handle, UUID(descriptors.descs[i].uuid.uuid)
+        );
+    }
+
+    // prepare the next discovery request (if needed)
+    uint16_t startHandle = descriptors.descs[descriptors.count - 1].handle + 1;
+    uint16_t endHandle = discovery->characteristic.getLastHandle();
+
+    if(startHandle > endHandle || 
+      (discovery->onDiscovery == emptyDiscoveryCallback && discovery->onTerminate == emptyTerminationCallback)) { 
+        terminate(connectionHandle, BLE_ERROR_NONE);
+        return;
+    }
+
+    ble_error_t err = gattc_descriptors_discover(connectionHandle, startHandle, endHandle);
+    if(err) { 
+        terminate(connectionHandle, err);
+        return;
+    }
+}
+
+void nRF5xCharacteristicDescriptorDiscoverer::terminate(uint16_t handle, ble_error_t err) {
+    Discovery* discovery = findRunningDiscovery(handle);
+    if(!discovery) { 
+        error("logic error in nRF5xCharacteristicDescriptorDiscoverer::process !!!");
+    }
+
+    Discovery tmp = *discovery;
+    *discovery = Discovery();
+    tmp.terminate(err);
+}
+
+nRF5xCharacteristicDescriptorDiscoverer::Discovery* 
+nRF5xCharacteristicDescriptorDiscoverer::findRunningDiscovery(const DiscoveredCharacteristic& characteristic) {
+    for(size_t i = 0; i < maximumConcurrentConnectionsCount; ++i) { 
+        if(discoveryRunning[i].characteristic == characteristic) { 
+            return &discoveryRunning[i];
+        }
+    }
+    return NULL;
+}
+
+nRF5xCharacteristicDescriptorDiscoverer::Discovery* 
+nRF5xCharacteristicDescriptorDiscoverer::findRunningDiscovery(const DiscoveredCharacteristic& characteristic) const {
+    for(size_t i = 0; i < maximumConcurrentConnectionsCount; ++i) { 
+        if(discoveryRunning[i].characteristic == characteristic) { 
+            return &discoveryRunning[i];
+        }
+    }
+    return NULL;
+}
+
+nRF5xCharacteristicDescriptorDiscoverer::Discovery* 
+nRF5xCharacteristicDescriptorDiscoverer::findRunningDiscovery(uint16_t handle) {
+    for(size_t i = 0; i < maximumConcurrentConnectionsCount; ++i) {
+        if(discoveryRunning[i].characteristic.getConnectionHandle() == handle && 
+           discoveryRunning[i] != Discovery()) {
+            return &discoveryRunning[i];
+        }
+    }
+    return NULL;
+}     
+
+void nRF5xCharacteristicDescriptorDiscoverer::removeDiscovery(Discovery* discovery) {
+    for(size_t i = 0; i < maximumConcurrentConnectionsCount; ++i) { 
+        if(&discoveryRunning[i] == discovery) { 
+            discoveryRunning[i] = Discovery();
+        }
+    }
+}
+
+nRF5xCharacteristicDescriptorDiscoverer::Discovery* 
+nRF5xCharacteristicDescriptorDiscoverer::getAvailableDiscoverySlot() {
+    for(size_t i = 0; i < maximumConcurrentConnectionsCount; ++i) { 
+        if(discoveryRunning[i] == Discovery()) {
+            return &discoveryRunning[i];
+        }
+    }     
+    return NULL;
+}
+
+bool nRF5xCharacteristicDescriptorDiscoverer::isConnectionInUse(uint16_t connHandle) {
+     return findRunningDiscovery(connHandle) != NULL;
+}
+
+ble_error_t nRF5xCharacteristicDescriptorDiscoverer::gattc_descriptors_discover(
+    uint16_t connection_handle, uint16_t start_handle, uint16_t end_handle) { 
+
+    ble_gattc_handle_range_t discoveryRange = {
+        start_handle,
+        end_handle
+    };
+    uint32_t err = sd_ble_gattc_descriptors_discover(connection_handle, &discoveryRange);
+
+    switch(err) { 
+        case NRF_SUCCESS:
+            return BLE_ERROR_NONE;            
+        case BLE_ERROR_INVALID_CONN_HANDLE:
+            return BLE_ERROR_INVALID_PARAM;
+        case NRF_ERROR_INVALID_ADDR:
+            return BLE_ERROR_PARAM_OUT_OF_RANGE;
+        case NRF_ERROR_BUSY:
+            return BLE_STACK_BUSY;
+        default:
+            return BLE_ERROR_UNSPECIFIED;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/nRF5xCharacteristicDescriptorDiscoverer.h	Mon Jan 11 10:19:19 2016 +0000
@@ -0,0 +1,140 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 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.
+ */
+
+#ifndef __NRF_CHARACTERISTIC_DESCRIPTOR_DISCOVERY_H__
+#define __NRF_CHARACTERISTIC_DESCRIPTOR_DISCOVERY_H__
+
+#include "ble/Gap.h"
+#include "ble/DiscoveredCharacteristic.h"
+#include "ble/CharacteristicDescriptorDiscovery.h"
+#include "ble/GattClient.h"
+#include "ble_gattc.h"
+
+/**
+ * @brief Manage the discovery of Characteristic descriptors 
+ * @details is a bridge beetween BLE API and nordic stack regarding Characteristic
+ * Descriptor discovery. The BLE API can launch, monitorate and ask for termination
+ * of a discovery. The nordic stack will provide new descriptors and indicate when
+ * the discovery is done
+ */
+class nRF5xCharacteristicDescriptorDiscoverer
+{
+    typedef CharacteristicDescriptorDiscovery::DiscoveryCallback_t DiscoveryCallback_t;
+    typedef CharacteristicDescriptorDiscovery::TerminationCallback_t TerminationCallback_t;
+
+public:
+    nRF5xCharacteristicDescriptorDiscoverer(size_t concurrentConnectionsCount = 3);
+
+    ~nRF5xCharacteristicDescriptorDiscoverer();
+
+    /**
+     * Launch a new characteristic descriptor discovery for a given 
+     * DiscoveredCharacteristic.
+     * @note: this will be called by BLE API side
+     */
+    ble_error_t launch(
+        const DiscoveredCharacteristic& characteristic, 
+        const DiscoveryCallback_t& callback,
+        const TerminationCallback_t& terminationCallback
+    );
+
+    /**
+     * @brief indicate if a characteristic descriptor discovery is active for a 
+     * given DiscoveredCharacteristic
+     * @note: this will be called by BLE API side
+     */
+    bool isActive(const DiscoveredCharacteristic& characteristic) const;
+
+    /**
+     * @brief reauest the termination of characteristic descriptor discovery 
+     * for a give DiscoveredCharacteristic
+     * @note: this will be called by BLE API side
+     */
+    void requestTerminate(const DiscoveredCharacteristic& characteristic);
+
+    /**
+     * @brief process descriptors discovered from the nordic stack
+     */
+    void process(uint16_t handle, const ble_gattc_evt_desc_disc_rsp_t& descriptors);
+
+    /**
+     * @brief Called by the nordic stack when the discovery is over.
+     */
+    void terminate(uint16_t handle, ble_error_t err);
+
+private:
+    nRF5xCharacteristicDescriptorDiscoverer(const nRF5xCharacteristicDescriptorDiscoverer&);
+    nRF5xCharacteristicDescriptorDiscoverer& operator=(const nRF5xCharacteristicDescriptorDiscoverer&);
+
+    struct Discovery {
+        Discovery() : characteristic(), onDiscovery(), onTerminate() { }
+
+        Discovery(const DiscoveredCharacteristic& c, const DiscoveryCallback_t& dCb, const TerminationCallback_t& tCb) : 
+            characteristic(c), 
+            onDiscovery(dCb), 
+            onTerminate(tCb) { 
+        }
+
+        DiscoveredCharacteristic characteristic;
+        DiscoveryCallback_t onDiscovery;
+        TerminationCallback_t onTerminate;
+
+        void process(GattAttribute::Handle_t handle, const UUID& uuid) { 
+            CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t params = { 
+                characteristic,
+                DiscoveredCharacteristicDescriptor(
+                    characteristic.getGattClient(),
+                    characteristic.getConnectionHandle(),
+                    handle,
+                    uuid
+                )
+            };
+            onDiscovery.call(&params);
+        }
+
+        void terminate(ble_error_t err) { 
+            CharacteristicDescriptorDiscovery::TerminationCallbackParams_t params = {
+                characteristic,
+                err
+            };
+            onTerminate.call(&params);
+        }
+
+        friend bool operator==(const Discovery& lhs, const Discovery& rhs) {
+            return lhs.characteristic == rhs.characteristic &&
+                lhs.onDiscovery == rhs.onDiscovery &&
+                lhs.onTerminate == rhs.onTerminate;
+        }
+
+        friend bool operator!=(const Discovery& lhs, const Discovery& rhs) {
+            return !(lhs == rhs);
+        }
+    };
+
+    Discovery* findRunningDiscovery(const DiscoveredCharacteristic& characteristic);
+    Discovery* findRunningDiscovery(const DiscoveredCharacteristic& characteristic) const;
+    Discovery* findRunningDiscovery(uint16_t handle);
+    void removeDiscovery(Discovery* discovery);
+    Discovery* getAvailableDiscoverySlot(); 
+    bool isConnectionInUse(uint16_t connHandle);
+    static ble_error_t gattc_descriptors_discover(uint16_t connection_handle, uint16_t start_handle, uint16_t end_handle); 
+
+
+    size_t maximumConcurrentConnectionsCount;
+    Discovery *discoveryRunning;
+};
+
+#endif /*__NRF_CHARACTERISTIC_DESCRIPTOR_DISCOVERY_H__*/
\ No newline at end of file
--- a/source/nRF5xDiscoveredCharacteristic.h	Mon Jan 11 10:19:18 2016 +0000
+++ b/source/nRF5xDiscoveredCharacteristic.h	Mon Jan 11 10:19:19 2016 +0000
@@ -36,6 +36,10 @@
                ble_gatt_char_props_t    propsIn,
                GattAttribute::Handle_t  declHandleIn,
                GattAttribute::Handle_t  valueHandleIn);
+
+    void setLastHandle(GattAttribute::Handle_t last) {
+      lastHandle = last;
+    }
 };
 
 #endif /* __NRF_DISCOVERED_CHARACTERISTIC_H__ */
\ No newline at end of file
--- a/source/nRF5xGap.cpp	Mon Jan 11 10:19:18 2016 +0000
+++ b/source/nRF5xGap.cpp	Mon Jan 11 10:19:19 2016 +0000
@@ -367,7 +367,7 @@
     @code
 
     uint8_t device_address[6] = { 0xca, 0xfe, 0xf0, 0xf0, 0xf0, 0xf0 };
-    nrf.getGap().setAddress(Gap::BLEProtocol::AddressType::RANDOM_STATIC, device_address);
+    nrf.getGap().setAddress(Gap::ADDR_TYPE_RANDOM_STATIC, device_address);
 
     @endcode
 */
@@ -381,12 +381,12 @@
        When using Random Private addresses, the cycle mode must be Auto.
        In auto mode, the given address is ignored.
     */
-    if ((type == BLEProtocol::AddressType::PUBLIC) || (type == BLEProtocol::AddressType::RANDOM_STATIC))
+    if ((type == ADDR_TYPE_PUBLIC) || (type == ADDR_TYPE_RANDOM_STATIC))
     {
         cycle_mode = BLE_GAP_ADDR_CYCLE_MODE_NONE;
         memcpy(dev_addr.addr, address, ADDR_LEN);
     }
-    else if ((type == BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE) || (type == BLEProtocol::AddressType::RANDOM_PRIVATE_NON_RESOLVABLE))
+    else if ((type == ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE) || (type == ADDR_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE))
     {
         cycle_mode = BLE_GAP_ADDR_CYCLE_MODE_AUTO;
         // address is ignored when in auto mode
--- a/source/nRF5xGattClient.cpp	Mon Jan 11 10:19:18 2016 +0000
+++ b/source/nRF5xGattClient.cpp	Mon Jan 11 10:19:19 2016 +0000
@@ -33,6 +33,27 @@
                                         const UUID                                 &matchingServiceUUIDIn,
                                         const UUID                                 &matchingCharacteristicUUIDIn)
 {
-    return discovery.launch(connectionHandle, sc, cc, matchingServiceUUIDIn, matchingCharacteristicUUIDIn);
+    return _discovery.launch(connectionHandle, sc, cc, matchingServiceUUIDIn, matchingCharacteristicUUIDIn);
 }
+
+ble_error_t nRF5xGattClient::discoverCharacteristicDescriptors(
+    const DiscoveredCharacteristic& characteristic,
+    const CharacteristicDescriptorDiscovery::DiscoveryCallback_t& discoveryCallback,
+    const CharacteristicDescriptorDiscovery::TerminationCallback_t& terminationCallback)
+{
+    return _characteristicDescriptorDiscoverer.launch(
+        characteristic, 
+        discoveryCallback, 
+        terminationCallback
+    );
+}
+
+bool nRF5xGattClient::isCharacteristicDescriptorsDiscoveryActive(const DiscoveredCharacteristic& characteristic) const {
+    return _characteristicDescriptorDiscoverer.isActive(characteristic);   
+}
+
+void nRF5xGattClient::terminateCharacteristicDescriptorsDiscovery(const DiscoveredCharacteristic& characteristic) { 
+    return _characteristicDescriptorDiscoverer.requestTerminate(characteristic);
+}
+
 #endif
\ No newline at end of file
--- a/source/nRF5xGattClient.h	Mon Jan 11 10:19:18 2016 +0000
+++ b/source/nRF5xGattClient.h	Mon Jan 11 10:19:19 2016 +0000
@@ -19,6 +19,7 @@
 
 #include "ble/GattClient.h"
 #include "nRF5xServiceDiscovery.h"
+#include "nRF5xCharacteristicDescriptorDiscoverer.h"
 
 class nRF5xGattClient : public GattClient
 {
@@ -87,14 +88,14 @@
                                                const UUID                                 &matchingCharacteristicUUIDIn = UUID::ShortUUIDBytes_t(BLE_UUID_UNKNOWN));
 
     virtual void onServiceDiscoveryTermination(ServiceDiscovery::TerminationCallback_t callback) {
-        discovery.onTermination(callback);
+        _discovery.onTermination(callback);
     }
 
     /**
      * Is service-discovery currently active?
      */
     virtual bool isServiceDiscoveryActive(void) const {
-        return discovery.isActive();
+        return _discovery.isActive();
     }
 
     /**
@@ -102,9 +103,31 @@
      * invocation of the TerminationCallback if service-discovery is active.
      */
     virtual void terminateServiceDiscovery(void) {
-        discovery.terminate();
+        _discovery.terminate();
     }
 
+    /**
+     * @brief Implementation of GattClient::discoverCharacteristicDescriptors 
+     * @see GattClient::discoverCharacteristicDescriptors 
+     */
+    virtual ble_error_t discoverCharacteristicDescriptors(
+        const DiscoveredCharacteristic& characteristic,
+        const CharacteristicDescriptorDiscovery::DiscoveryCallback_t& discoveryCallback,
+        const CharacteristicDescriptorDiscovery::TerminationCallback_t& terminationCallback
+    );
+
+    /**
+     * @brief Implementation of GattClient::isCharacteristicDiscoveryActive 
+     * @see GattClient::isCharacteristicDiscoveryActive 
+     */
+    virtual bool isCharacteristicDescriptorsDiscoveryActive(const DiscoveredCharacteristic& characteristic) const;
+
+    /**
+     * @brief Implementation of GattClient::terminateCharacteristicDiscovery 
+     * @see GattClient::terminateCharacteristicDiscovery 
+     */
+    virtual void terminateCharacteristicDescriptorsDiscovery(const DiscoveredCharacteristic& characteristic);
+
     virtual ble_error_t read(Gap::Handle_t connHandle, GattAttribute::Handle_t attributeHandle, uint16_t offset) const {
         uint32_t rc = sd_ble_gattc_read(connHandle, attributeHandle, offset);
         if (rc == NRF_SUCCESS) {
@@ -148,18 +171,25 @@
     }
 
 public:
-    nRF5xGattClient() : discovery(this) {
+    nRF5xGattClient() : _discovery(this) {
         /* empty */
     }
 
-    friend void bleGattcEventHandler(const ble_evt_t *p_ble_evt);
+    nRF5xServiceDiscovery& discovery() { 
+        return _discovery;
+    }
+
+    nRF5xCharacteristicDescriptorDiscoverer& characteristicDescriptorDiscoverer() { 
+        return _characteristicDescriptorDiscoverer;
+    }
 
 private:
     nRF5xGattClient(const nRF5xGattClient &);
     const nRF5xGattClient& operator=(const nRF5xGattClient &);
 
 private:
-    nRF5xServiceDiscovery discovery;
+    nRF5xServiceDiscovery _discovery;
+    nRF5xCharacteristicDescriptorDiscoverer _characteristicDescriptorDiscoverer;
 
 #endif // if !S110
 };
--- a/source/nRF5xServiceDiscovery.cpp	Mon Jan 11 10:19:18 2016 +0000
+++ b/source/nRF5xServiceDiscovery.cpp	Mon Jan 11 10:19:19 2016 +0000
@@ -27,22 +27,32 @@
         .start_handle = startHandle,
         .end_handle   = endHandle
     };
-    uint32_t rc;
-    if ((rc = sd_ble_gattc_characteristics_discover(connectionHandle, &handleRange)) != NRF_SUCCESS) {
-        terminateCharacteristicDiscovery();
-        switch (rc) {
-            case BLE_ERROR_INVALID_CONN_HANDLE:
-            case NRF_ERROR_INVALID_ADDR:
-                return BLE_ERROR_INVALID_PARAM;
-            case NRF_ERROR_BUSY:
-                return BLE_STACK_BUSY;
-            default:
-            case NRF_ERROR_INVALID_STATE:
-                return BLE_ERROR_INVALID_STATE;
-        }
+    uint32_t rc = sd_ble_gattc_characteristics_discover(connectionHandle, &handleRange);
+    ble_error_t err = BLE_ERROR_NONE;
+
+    switch (rc) {
+        case NRF_SUCCESS:
+            err = BLE_ERROR_NONE;
+            break;
+        case BLE_ERROR_INVALID_CONN_HANDLE:
+        case NRF_ERROR_INVALID_ADDR:
+            err = BLE_ERROR_INVALID_PARAM;
+            break;
+        case NRF_ERROR_BUSY:
+            err = BLE_STACK_BUSY;
+            break;
+        case NRF_ERROR_INVALID_STATE:
+            err = BLE_ERROR_INVALID_STATE;
+            break;
+        default:
+            err = BLE_ERROR_UNSPECIFIED;
+            break;
     }
 
-    return BLE_ERROR_NONE;
+    if (err) {
+        terminateCharacteristicDiscovery(err);
+    }
+    return err;
 }
 
 void
@@ -78,7 +88,6 @@
 void
 nRF5xServiceDiscovery::setupDiscoveredCharacteristics(const ble_gattc_evt_char_disc_rsp_t *response)
 {
-    characteristicIndex = 0;
     numCharacteristics  = response->count;
 
     /* Account for the limitation on the number of discovered characteristics we can handle at a time. */
@@ -114,38 +123,62 @@
 void
 nRF5xServiceDiscovery::progressCharacteristicDiscovery(void)
 {
-    /* Iterate through the previously discovered characteristics cached in characteristics[]. */
-    while ((state == CHARACTERISTIC_DISCOVERY_ACTIVE) && (characteristicIndex < numCharacteristics)) {
+    if (state != CHARACTERISTIC_DISCOVERY_ACTIVE) {
+        return;
+    }
+
+    if ((discoveredCharacteristic != nRF5xDiscoveredCharacteristic()) && (numCharacteristics > 0)) {
+        discoveredCharacteristic.setLastHandle(characteristics[0].getDeclHandle() - 1);
+
         if ((matchingCharacteristicUUID == UUID::ShortUUIDBytes_t(BLE_UUID_UNKNOWN)) ||
-            ((matchingCharacteristicUUID == characteristics[characteristicIndex].getUUID()) &&
+            ((matchingCharacteristicUUID == discoveredCharacteristic.getUUID()) &&
              (matchingServiceUUID != UUID::ShortUUIDBytes_t(BLE_UUID_UNKNOWN)))) {
             if (characteristicCallback) {
-                characteristicCallback(&characteristics[characteristicIndex]);
+                characteristicCallback(&discoveredCharacteristic);
             }
         }
-
-        characteristicIndex++;
     }
 
-    /* Relaunch discovery of new characteristics beyond the last entry cached in characteristics[]. */
-    if (state == CHARACTERISTIC_DISCOVERY_ACTIVE) {
-        /* Determine the ending handle of the last cached characteristic. */
-        Gap::Handle_t startHandle = characteristics[characteristicIndex - 1].getValueHandle() + 1;
-        Gap::Handle_t endHandle   = services[serviceIndex].getEndHandle();
-        resetDiscoveredCharacteristics(); /* Note: resetDiscoveredCharacteristics() must come after fetching start and end Handles. */
+    for (uint8_t i = 0; i < numCharacteristics; ++i) {
+        if (state != CHARACTERISTIC_DISCOVERY_ACTIVE) {
+            return;
+        }
+
+        if (i == (numCharacteristics - 1)) {
+            discoveredCharacteristic = characteristics[i];
+            break;
+        } else {
+            characteristics[i].setLastHandle(characteristics[i + 1].getDeclHandle() - 1);
+        }
 
-        if (startHandle < endHandle) {
-            ble_gattc_handle_range_t handleRange = {
-                .start_handle = startHandle,
-                .end_handle   = endHandle
-            };
-            if (sd_ble_gattc_characteristics_discover(connHandle, &handleRange) != NRF_SUCCESS) {
-                terminateCharacteristicDiscovery();
+        if ((matchingCharacteristicUUID == UUID::ShortUUIDBytes_t(BLE_UUID_UNKNOWN)) ||
+            ((matchingCharacteristicUUID == characteristics[i].getUUID()) &&
+             (matchingServiceUUID != UUID::ShortUUIDBytes_t(BLE_UUID_UNKNOWN)))) {
+            if (characteristicCallback) {
+                characteristicCallback(&characteristics[i]);
             }
-        } else {
-            terminateCharacteristicDiscovery();
         }
     }
+
+    if (state != CHARACTERISTIC_DISCOVERY_ACTIVE) {
+        return;
+    }
+
+    Gap::Handle_t startHandle = (numCharacteristics > 0) ? characteristics[numCharacteristics - 1].getValueHandle() + 1 : SRV_DISC_END_HANDLE;
+    Gap::Handle_t endHandle   = services[serviceIndex].getEndHandle();
+    resetDiscoveredCharacteristics(); /* Note: resetDiscoveredCharacteristics() must come after fetching start and end Handles. */
+
+    if (startHandle < endHandle) {
+        ble_gattc_handle_range_t handleRange = {
+            .start_handle = startHandle,
+            .end_handle   = endHandle
+        };
+        if (sd_ble_gattc_characteristics_discover(connHandle, &handleRange) != NRF_SUCCESS) {
+            terminateCharacteristicDiscovery(BLE_ERROR_UNSPECIFIED);
+        }
+    } else {
+        terminateCharacteristicDiscovery(BLE_ERROR_NONE);
+    }
 }
 
 void
--- a/source/nRF5xServiceDiscovery.h	Mon Jan 11 10:19:18 2016 +0000
+++ b/source/nRF5xServiceDiscovery.h	Mon Jan 11 10:19:19 2016 +0000
@@ -41,7 +41,6 @@
         gattc(gattcIn),
         serviceIndex(0),
         numServices(0),
-        characteristicIndex(0),
         numCharacteristics(0),
         state(INACTIVE),
         services(),
@@ -95,6 +94,12 @@
         terminateServiceDiscovery();
     }
 
+    void terminate(Gap::Handle_t connectionHandle) {
+        if(connHandle == connectionHandle) {
+            terminate();
+        }
+    }
+
     virtual void onTermination(ServiceDiscovery::TerminationCallback_t callback) {
         onTerminationCallback = callback;
     }
@@ -111,6 +116,8 @@
     void removeFirstServiceNeedingUUIDDiscovery(void);
 
     void terminateServiceDiscovery(void) {
+        discoveredCharacteristic = nRF5xDiscoveredCharacteristic();
+
         bool wasActive = isActive();
         state = INACTIVE;
 
@@ -119,8 +126,24 @@
         }
     }
 
-    void terminateCharacteristicDiscovery(void) {
+    void terminateCharacteristicDiscovery(ble_error_t err) {
         if (state == CHARACTERISTIC_DISCOVERY_ACTIVE) {
+            if(discoveredCharacteristic != nRF5xDiscoveredCharacteristic()) {
+               if(err == BLE_ERROR_NONE) {
+                    // fullfill the last characteristic
+                    discoveredCharacteristic.setLastHandle(services[serviceIndex].getEndHandle());
+
+                    if ((matchingCharacteristicUUID == UUID::ShortUUIDBytes_t(BLE_UUID_UNKNOWN)) ||
+                        ((matchingCharacteristicUUID == discoveredCharacteristic.getUUID()) &&
+                         (matchingServiceUUID != UUID::ShortUUIDBytes_t(BLE_UUID_UNKNOWN)))) {
+                        if (characteristicCallback) {
+                            characteristicCallback(&discoveredCharacteristic);
+                        }
+                    }
+               }
+               discoveredCharacteristic = nRF5xDiscoveredCharacteristic();
+            }
+
             state = SERVICE_DISCOVERY_ACTIVE;
         }
         serviceIndex++; /* Progress service index to keep discovery alive. */
@@ -134,7 +157,6 @@
 
     void resetDiscoveredCharacteristics(void) {
         numCharacteristics  = 0;
-        characteristicIndex = 0;
     }
 
 private:
@@ -281,7 +303,6 @@
 private:
     uint8_t  serviceIndex;        /**< Index of the current service being discovered. This is intended for internal use during service discovery.*/
     uint8_t  numServices;         /**< Number of services at the peers GATT database.*/
-    uint8_t  characteristicIndex; /**< Index of the current characteristic being discovered. This is intended for internal use during service discovery.*/
     uint8_t  numCharacteristics;  /**< Number of characteristics within the service.*/
 
     enum State_t {
@@ -300,6 +321,19 @@
     CharUUIDDiscoveryQueue      charUUIDDiscoveryQueue;
 
     TerminationCallback_t       onTerminationCallback;
+
+    /*
+     * The currently discovered characteristic. Discovery of a characteristic
+     * is a two phase process.
+     * First, declaration handle is fetched, it provide the UUID, the value handle and
+     * the properties of a characteristic.
+     * Second, the next declaration handle is fetched, with its declaration handle, it is
+     * possible to compute the last handle of the discovered characteristic and fill the
+     * missing part of the object.
+     * If there is no remaining characteristic to discover, the last handle of the
+     * discovered characteristic will be set to the last handle of its enclosing service.
+     */
+    nRF5xDiscoveredCharacteristic discoveredCharacteristic;
 };
 
 #endif /*__NRF_SERVICE_DISCOVERY_H__*/
\ No newline at end of file