text: /* mbed Microcontroller Library
   * Copyright (c) 2017-2017 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 "BLERoles.h"
  
  #include <algorithm>
  #include "CordioBLE.h"
  #include "CordioGattServer.h"
  #include "source/GattServer.tpp"
  #include "mbed.h"
  #include "wsf_types.h"
  #include "att_api.h"
  
  template class ble::interface::GattServer<ble::vendor::cordio::GattServer>;
  
  namespace ble {
  namespace vendor {
  namespace cordio {
  
  namespace {
  
  static UUID CCCD_UUID(BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG);
  static const uint16_t CCCD_SIZE = sizeof(uint16_t);
  
  static const unsigned int READ_PROPERTY =
      GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ;
  static const unsigned int WRITE_PROPERTY =
      GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE;
  static const unsigned int WRITE_WITHOUT_RESPONSE_PROPERTY =
      GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE;
  static const unsigned int SIGNED_WRITE_PROPERTY =
      GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_AUTHENTICATED_SIGNED_WRITES;
  static const unsigned int NOTIFY_PROPERTY =
      GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY;
  static const unsigned int INDICATE_PROPERTY =
      GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE;
  
  static const uint8_t WRITABLE_PROPERTIES =
      WRITE_PROPERTY |
      WRITE_WITHOUT_RESPONSE_PROPERTY |
      SIGNED_WRITE_PROPERTY;
  
  static const uint8_t UPDATE_PROPERTIES =
      NOTIFY_PROPERTY |
      INDICATE_PROPERTY;
  
  static const uint16_t CONNECTION_ID_LIMIT = 0x100;
  
  } // end of anonymous namespace
  
  GattServer &GattServer::getInstance()
  {
      static GattServer m_instance;
      return m_instance;
  }
  
  void GattServer::initialize()
  {
  #if BLE_FEATURE_SECURITY
      AttsAuthorRegister(atts_auth_cb);
  #endif
      add_default_services();
  }
  
  void GattServer::add_default_services()
  {
      if (!default_services_added) {
          default_services_added = true;
          add_generic_access_service();
          add_generic_attribute_service();
      }
  }
  
  ble_error_t GattServer::addService_(GattService &service)
  {
      add_default_services();
      // create and fill the service structure
      internal_service_t *att_service = new internal_service_t;
      att_service->attGroup.pNext = NULL;
      att_service->attGroup.readCback = atts_read_cb;
      att_service->attGroup.writeCback = atts_write_cb;
  
      // Determine the attribute list length
      uint16_t attributes_count = compute_attributes_count(service);
  
      // Create cordio attribute list
      att_service->attGroup.pAttr =
          (attsAttr_t*) alloc_block(attributes_count * sizeof(attsAttr_t));
      if (att_service->attGroup.pAttr == NULL) {
          delete att_service;
          return BLE_ERROR_BUFFER_OVERFLOW;
      }
  
      // insert every element in the iterator
      attsAttr_t *attribute_it = att_service->attGroup.pAttr;
  
      /* Service */
      insert_service_attribute(service, attribute_it);
      att_service->attGroup.startHandle = currentHandle;
      service.setHandle(currentHandle);
  
      /* Add characteristics to the service */
      for (int i = 0; i < service.getCharacteristicCount(); i++) {
          ble_error_t err = insert_characteristic(
              service.getCharacteristic(i),
              attribute_it
          );
          if (err) {
              // FIXME: proper cleanup of data structure:
              //   - att_service->attGroup.pAttr
              //   - blocks allocated for characteristics value
              // NOTE: those are rightfully released when reset() is called.
              delete att_service;
              return err;
          }
      }
      att_service->attGroup.endHandle = currentHandle;
  
      // add the service to the list of registered services
      if (registered_service) {
          att_service->next = registered_service;
      } else {
          att_service->next = NULL;
      }
  
      registered_service = att_service;
  
      // register services and update cccds
      AttsAddGroup(&att_service->attGroup);
      AttsCccRegister(cccd_cnt, (attsCccSet_t*)cccds, cccd_cb);
      return BLE_ERROR_NONE;
  }
  
  uint16_t GattServer::compute_attributes_count(GattService& service)
  {
      // start at 1, one attribute is required for the service itself
      uint16_t attributes_count = 1;
      for (int i = 0; i < service.getCharacteristicCount(); i++) {
          attributes_count += 2;
          GattCharacteristic *p_char = service.getCharacteristic(i);
          attributes_count += p_char->getDescriptorCount();
          if (p_char->getProperties() & UPDATE_PROPERTIES) {
              // add a CCCD
              ++attributes_count;
  
              // verify that it hasn't been counted twice
              for (size_t j = 0; j < p_char->getDescriptorCount(); ++j) {
                  if (p_char->getDescriptor(j)->getUUID() ==
                      UUID(BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG)
                  ) {
                      --attributes_count;
                      break;
                  }
              }
          }
      }
  
      return attributes_count;
  }
  
  void GattServer::insert_service_attribute(
      GattService& service,
      attsAttr_t *&attribute_it
  ) {
      ++currentHandle;
      const UUID& service_uuid = service.getUUID();
  
      attribute_it->pUuid = attPrimSvcUuid;
      if (service_uuid.shortOrLong() == UUID::UUID_TYPE_LONG) {
          attribute_it->maxLen = UUID::LENGTH_OF_LONG_UUID;
      } else {
          attribute_it->maxLen = sizeof(UUID::ShortUUIDBytes_t);
      }
      attribute_it->pValue = (uint8_t*) alloc_block(attribute_it->maxLen);
      memcpy(attribute_it->pValue, service_uuid.getBaseUUID(), attribute_it->maxLen);
      attribute_it->pLen = &attribute_it->maxLen;
      attribute_it->settings = 0;
      attribute_it->permissions = ATTS_PERMIT_READ;
  
      ++attribute_it;
  }
  
  ble_error_t GattServer::insert_characteristic(
      GattCharacteristic *characteristic,
      attsAttr_t *&attribute_it
  ) {
      bool valid = is_characteristic_valid(characteristic);
      if (!valid) {
          return BLE_ERROR_INVALID_PARAM;
      }
  
      uint8_t properties = characteristic->getProperties();
  
      // Create Characteristic Declaration Attribute
      insert_characteristic_declaration_attribute(characteristic, attribute_it);
      ble_error_t err = insert_characteristic_value_attribute(characteristic, attribute_it);
      if (err) {
          return err;
      }
  
  
      // insert descriptors
      bool cccd_created = false;
      for (size_t i = 0; i < characteristic->getDescriptorCount(); i++) {
          err = insert_descriptor(
              characteristic,
              characteristic->getDescriptor(i),
              attribute_it,
              cccd_created
          );
          if (err) {
              return err;
          }
      }
  
      // insert implicit CCCD
      if ((properties & UPDATE_PROPERTIES) && (cccd_created == false)) {
          err = insert_cccd(characteristic, attribute_it);
          if (err) {
              return err;
          }
      }
  
      return BLE_ERROR_NONE;
  }
  
  bool GattServer::is_characteristic_valid(GattCharacteristic *characteristic) {
      uint8_t properties = characteristic->getProperties();
  
      // nothing to read while the characteristic is flagged as readable
      if ((characteristic->getValueAttribute().getValuePtr() == NULL) &&
          (characteristic->getValueAttribute().getMaxLength() == 0) &&
          (properties == READ_PROPERTY) &&
          (characteristic->isReadAuthorizationEnabled() == false)
      ) {
          return false;
      }
  
      // nothing to write while the characteristic is flagged as writable
      if ((characteristic->getValueAttribute().getValuePtr() == NULL) &&
          (characteristic->getValueAttribute().getMaxLength() == 0) &&
          (properties & WRITABLE_PROPERTIES) &&
          (characteristic->isWriteAuthorizationEnabled() == false)
      ) {
          return false;
      }
  
  #if BLE_FEATURE_SIGNING
      // check for invalid permissions
      if ((properties == SIGNED_WRITE_PROPERTY) &&
          (characteristic->getWriteSecurityRequirement() == att_security_requirement_t::NONE
  #if BLE_FEATURE_SECURE_CONNECTIONS
           || characteristic->getWriteSecurityRequirement() == att_security_requirement_t::SC_AUTHENTICATED
  
  #endif // BLE_FEATURE_SECURE_CONNECTIONS
          )
      ) {
          return false;
      }
  #endif // BLE_FEATURE_SIGNING
  
      return true;
  }
  
  void GattServer::insert_characteristic_declaration_attribute(
      GattCharacteristic *characteristic,
      attsAttr_t *&attribute_it
  ) {
      const UUID& value_uuid = characteristic->getValueAttribute().getUUID();
  
      // move the current handle to point to the value handle
      currentHandle += 2;
      characteristic->getValueAttribute().setHandle(currentHandle);
  
      // fill the cordio attribute
      attribute_it->pUuid = attChUuid;
      attribute_it->maxLen = 1 + sizeof(currentHandle) + value_uuid.getLen();
      attribute_it->pLen = &attribute_it->maxLen;
      attribute_it->pValue = (uint8_t*) alloc_block(attribute_it->maxLen);
      attribute_it->settings = 0;
      attribute_it->permissions = ATTS_PERMIT_READ;
  
      // set the attribute value
      uint8_t *value_it = attribute_it->pValue;
      *value_it++ = characteristic->getProperties();
      memcpy(value_it, &currentHandle, sizeof(currentHandle));
      value_it += sizeof(currentHandle);
      memcpy(value_it, value_uuid.getBaseUUID(), value_uuid.getLen());
  
      ++attribute_it;
  }
  
  ble_error_t GattServer::insert_characteristic_value_attribute(
      GattCharacteristic *characteristic,
      attsAttr_t *&attribute_it
  ) {
      GattAttribute &value_attribute = characteristic->getValueAttribute();
      uint8_t properties = characteristic->getProperties();
  
      // note: currentHandle has been already moved to the correct value
  
      // Create Value Attribute
      attribute_it->pUuid = value_attribute.getUUID().getBaseUUID();
      attribute_it->maxLen = characteristic->getValueAttribute().getMaxLength();
      attribute_it->pLen = (uint16_t*) alloc_block(attribute_it->maxLen + sizeof(uint16_t));
      *attribute_it->pLen = value_attribute.getLength();
      attribute_it->pValue = (uint8_t*) ((uint16_t*)attribute_it->pLen + 1);
      memcpy(attribute_it->pValue, value_attribute.getValuePtr(), *attribute_it->pLen);
      memset(attribute_it->pValue + *attribute_it->pLen, 0, attribute_it->maxLen - *attribute_it->pLen);
  
      // Set value attribute settings
      attribute_it->settings = 0;
  
      if (properties & READ_PROPERTY) {
          attribute_it->settings |= ATTS_SET_READ_CBACK;
      }
      if (properties & WRITABLE_PROPERTIES) {
          attribute_it->settings |= ATTS_SET_WRITE_CBACK;
      }
      if (value_attribute.getUUID().shortOrLong() == UUID::UUID_TYPE_LONG) {
          attribute_it->settings |= ATTS_SET_UUID_128;
      }
      if (value_attribute.hasVariableLength()) {
          attribute_it->settings |= ATTS_SET_VARIABLE_LEN;
      }
      if (properties & SIGNED_WRITE_PROPERTY) {
          attribute_it->settings |= ATTS_SET_ALLOW_SIGNED;
      }
  
      // setup permissions
      attribute_it->permissions = 0;
  
      // configure read permission
      if (properties & READ_PROPERTY) {
          attribute_it->permissions |= ATTS_PERMIT_READ;
          switch (characteristic->getReadSecurityRequirement().value()) {
              case att_security_requirement_t::NONE:
                  break;
  #if BLE_FEATURE_SECURITY
              case att_security_requirement_t::UNAUTHENTICATED:
                  attribute_it->permissions |= ATTS_PERMIT_READ_ENC;
                  break;
              case att_security_requirement_t::AUTHENTICATED:
                  attribute_it->permissions |=
                      ATTS_PERMIT_READ_ENC |
                      ATTS_PERMIT_READ_AUTH;
                  break;
  #if BLE_FEATURE_SECURE_CONNECTIONS
              case att_security_requirement_t::SC_AUTHENTICATED:
                  // Note: check done in the cordio stack doesn't cover LESC
                  // so this one is done in attsAuthorCback
                  attribute_it->permissions |=
                      ATTS_PERMIT_READ_ENC |
                      ATTS_PERMIT_READ_AUTH |
                      ATTS_PERMIT_READ_AUTHORIZ;
                  break;
  #endif // BLE_FEATURE_SECURE_CONNECTIONS
  #endif // BLE_FEATURE_SECURITY
          }
      }
  
      // configure write permission
      if (properties & WRITABLE_PROPERTIES) {
          attribute_it->permissions |= ATTS_PERMIT_WRITE;
          switch (characteristic->getWriteSecurityRequirement().value()) {
              case att_security_requirement_t::NONE:
                  break;
  #if BLE_FEATURE_SECURITY
              case att_security_requirement_t::UNAUTHENTICATED:
                  attribute_it->permissions |= ATTS_PERMIT_WRITE_ENC;
                  break;
              case att_security_requirement_t::AUTHENTICATED:
                  attribute_it->permissions |=
                      ATTS_PERMIT_WRITE_ENC |
                      ATTS_PERMIT_WRITE_AUTH;
                  break;
  #if BLE_FEATURE_SECURE_CONNECTIONS
              case att_security_requirement_t::SC_AUTHENTICATED:
                  // Note: check done in the cordio stack doesn't cover LESC
                  // so this one is done in attsAuthorCback
                  attribute_it->permissions |=
                      ATTS_PERMIT_WRITE_ENC |
                      ATTS_PERMIT_WRITE_AUTH |
                      ATTS_PERMIT_WRITE_AUTHORIZ;
                  break;
  #endif // BLE_FEATURE_SECURE_CONNECTIONS
  #endif // BLE_FEATURE_SECURITY
          }
      }
  
      // Register characteristic in authorisation list
      // Note: ATTS_PERMIT_*_AUTHORIZ is uniquely used to check if security
      // requirements are met according to the security requirements set in the
      // characteristic.
      // User defined security authorisation does not impact this flag
      if ((attribute_it->permissions & (ATTS_PERMIT_READ_AUTHORIZ | ATTS_PERMIT_WRITE_AUTHORIZ)) ||
          (properties & UPDATE_PROPERTIES) ||
          characteristic->isReadAuthorizationEnabled() ||
          characteristic->isWriteAuthorizationEnabled()
      ) {
          if ( _auth_char_count >= MAX_CHARACTERISTIC_AUTHORIZATION_CNT) {
              return BLE_ERROR_NO_MEM;
          }
          _auth_char[_auth_char_count] = characteristic;
          ++_auth_char_count;
      }
  
      ++attribute_it;
  
      return BLE_ERROR_NONE;
  }
  
  ble_error_t GattServer::insert_descriptor(
      GattCharacteristic *characteristic,
      GattAttribute* descriptor,
      attsAttr_t *&attribute_it,
      bool& cccd_created
  ) {
      uint8_t properties = characteristic->getProperties();
  
      currentHandle++;
  
      descriptor->setHandle(currentHandle);
  
      attribute_it->pUuid = descriptor->getUUID().getBaseUUID();
      attribute_it->maxLen = descriptor->getMaxLength();
      attribute_it->pLen = (uint16_t*) alloc_block(attribute_it->maxLen + sizeof(uint16_t));
      *attribute_it->pLen = descriptor->getLength();
      attribute_it->pValue = (uint8_t*) ((uint16_t*)attribute_it->pLen + 1);
      memcpy(attribute_it->pValue, descriptor->getValuePtr(), *attribute_it->pLen);
      memset(attribute_it->pValue + *attribute_it->pLen, 0, attribute_it->maxLen - *attribute_it->pLen);
  
      attribute_it->settings = 0;
  
      if (descriptor->getUUID().shortOrLong() == UUID::UUID_TYPE_LONG) {
          attribute_it->settings |= ATTS_SET_UUID_128;
      }
  
      // handle the special case of a CCCD
      if (descriptor->getUUID() == UUID(BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG)) {
          if (cccd_cnt >= MAX_CCCD_CNT) {
              return BLE_ERROR_NO_MEM;
          }
  
          if (descriptor->isReadAllowed() == false ||
              descriptor->getReadSecurityRequirement() != att_security_requirement_t::NONE
          ) {
              return BLE_ERROR_INVALID_PARAM;
          }
  
          cccd_created = true;
          attribute_it->settings |= ATTS_SET_CCC;
  
          cccds[cccd_cnt].handle = currentHandle;
          cccds[cccd_cnt].valueRange = 0;
          if (properties & NOTIFY_PROPERTY) {
              cccds[cccd_cnt].valueRange |= ATT_CLIENT_CFG_NOTIFY;
          }
          if (properties & INDICATE_PROPERTY) {
              cccds[cccd_cnt].valueRange |= ATT_CLIENT_CFG_INDICATE;
          }
          cccd_handles[cccd_cnt] = characteristic->getValueAttribute().getHandle();
          cccd_cnt++;
      }
  
      if (descriptor->hasVariableLength()) {
          attribute_it->settings |= ATTS_SET_VARIABLE_LEN;
      }
  
      // setup permissions
      attribute_it->permissions = 0;
  
      // configure read permission
      if (descriptor->isReadAllowed()) {
          attribute_it->permissions |= ATTS_PERMIT_READ;
          switch (descriptor->getReadSecurityRequirement().value()) {
              case att_security_requirement_t::NONE:
                  break;
  #if BLE_FEATURE_SECURITY
              case att_security_requirement_t::UNAUTHENTICATED:
                  attribute_it->permissions |= ATTS_PERMIT_READ_ENC;
                  break;
              case att_security_requirement_t::AUTHENTICATED:
                  attribute_it->permissions |=
                      ATTS_PERMIT_READ_ENC |
                      ATTS_PERMIT_READ_AUTH;
                  break;
  #if BLE_FEATURE_SECURE_CONNECTIONS
              case att_security_requirement_t::SC_AUTHENTICATED:
                  // Note: check done in the cordio stack doesn't cover LESC
                  // so this one is done in attsAuthorCback
                  attribute_it->permissions |=
                      ATTS_PERMIT_READ_ENC |
                      ATTS_PERMIT_READ_AUTH |
                      ATTS_PERMIT_READ_AUTHORIZ;
                  break;
  #endif // BLE_FEATURE_SECURE_CONNECTIONS
  #endif // BLE_FEATURE_SECURITY
          }
  
          if (properties & READ_PROPERTY) {
              attribute_it->settings |= ATTS_SET_READ_CBACK;
          }
      }
  
      // configure write permission
      if (descriptor->isWriteAllowed()) {
          attribute_it->permissions |= ATTS_PERMIT_WRITE;
          switch (descriptor->getWriteSecurityRequirement().value()) {
              case att_security_requirement_t::NONE:
                  break;
  #if BLE_FEATURE_SECURITY
              case att_security_requirement_t::UNAUTHENTICATED:
                  attribute_it->permissions |= ATTS_PERMIT_WRITE_ENC;
                  break;
              case att_security_requirement_t::AUTHENTICATED:
                  attribute_it->permissions |=
                      ATTS_PERMIT_WRITE_ENC |
                      ATTS_PERMIT_WRITE_AUTH;
                  break;
  #if BLE_FEATURE_SECURE_CONNECTIONS
              case att_security_requirement_t::SC_AUTHENTICATED:
                  // Note: check done in the cordio stack doesn't cover LESC
                  // so this one is done in attsAuthorCback
                  attribute_it->permissions |=
                      ATTS_PERMIT_WRITE_ENC |
                      ATTS_PERMIT_WRITE_AUTH |
                      ATTS_PERMIT_WRITE_AUTHORIZ;
                  break;
  #endif // BLE_FEATURE_SECURE_CONNECTIONS
  #endif // BLE_FEATURE_SECURITY
          }
  
          if (properties & WRITABLE_PROPERTIES) {
              attribute_it->settings |= ATTS_SET_WRITE_CBACK;
          }
      }
  
      attribute_it++;
      return BLE_ERROR_NONE;
  }
  
  ble_error_t GattServer::insert_cccd(
      GattCharacteristic *characteristic,
      attsAttr_t *&attribute_it
  ) {
      if (cccd_cnt >= MAX_CCCD_CNT) {
          return BLE_ERROR_NO_MEM;
      }
  
      uint8_t properties = characteristic->getProperties();
  
      currentHandle++;
  
      attribute_it->pUuid = CCCD_UUID.getBaseUUID();
      attribute_it->pValue = (uint8_t*) &cccd_values[cccd_cnt];
      attribute_it->maxLen = CCCD_SIZE;
      attribute_it->pLen = &attribute_it->maxLen;
      attribute_it->settings = ATTS_SET_CCC;
      attribute_it->permissions = (ATTS_PERMIT_READ | ATTS_PERMIT_WRITE);
  
      cccds[cccd_cnt].handle = currentHandle;
      cccds[cccd_cnt].valueRange = 0;
      if (properties & NOTIFY_PROPERTY) {
          cccds[cccd_cnt].valueRange |= ATT_CLIENT_CFG_NOTIFY;
      }
      if (properties & INDICATE_PROPERTY) {
          cccds[cccd_cnt].valueRange |= ATT_CLIENT_CFG_INDICATE;
      }
      cccds[cccd_cnt].secLevel = characteristic->getUpdateSecurityRequirement().value();
      cccd_handles[cccd_cnt] = characteristic->getValueAttribute().getHandle();
  
      cccd_cnt++;
      attribute_it++;
  
      return BLE_ERROR_NONE;
  }
  
  ble_error_t GattServer::read_(
      GattAttribute::Handle_t att_handle,
      uint8_t buffer[],
      uint16_t * buffer_length
  ) {
      uint16_t att_length = 0;
      uint8_t* att_value = NULL;
  
      if (AttsGetAttr(att_handle, &att_length, &att_value) != ATT_SUCCESS) {
          return BLE_ERROR_PARAM_OUT_OF_RANGE;
      }
  
      if (buffer) {
          memcpy(buffer, att_value, std::min(*buffer_length, att_length));
      }
  
      *buffer_length = att_length;
  
      return BLE_ERROR_NONE;
  }
  
  ble_error_t GattServer::read_(
      connection_handle_t connection,
      GattAttribute::Handle_t att_handle,
      uint8_t buffer[],
      uint16_t *buffer_length
  ) {
      // Check to see if this is a CCCD
      uint8_t cccd_index;
      if (get_cccd_index_by_cccd_handle(att_handle, cccd_index)) {
          if (connection == DM_CONN_ID_NONE) {
              return BLE_ERROR_PARAM_OUT_OF_RANGE;
          }
          uint16_t cccd_value = AttsCccGet(connection, cccd_index);
          uint16_t cccd_length = sizeof(cccd_value);
  
          if (buffer) {
              memcpy(buffer, &cccd_value, std::min(*buffer_length, cccd_length));
          }
  
          *buffer_length = cccd_length;
          return BLE_ERROR_NONE;
      }
  
      // This is not a CCCD. Use the non-connection specific update method.
      return read(att_handle, buffer, buffer_length);
  }
  
  ble_error_t GattServer::write_(
      GattAttribute::Handle_t att_handle,
      const uint8_t buffer[],
      uint16_t len,
      bool local_only
  ) {
      // Check to see if this is a CCCD, if it is the case update the value for all
      // connections
      uint8_t cccd_index;
      if (get_cccd_index_by_cccd_handle(att_handle, cccd_index)) {
          if (len != sizeof(uint16_t)) {
              return BLE_ERROR_INVALID_PARAM;
          }
  
          uint16_t cccd_value;
          memcpy(&cccd_value, buffer, sizeof(cccd_value));
  
          for (dmConnId_t conn_id = DM_CONN_MAX; conn_id > DM_CONN_ID_NONE; --conn_id) {
              if (DmConnInUse(conn_id) == true) {
                  AttsCccSet(conn_id, cccd_index, cccd_value);
              }
          }
  
          return BLE_ERROR_NONE;
      }
  
      // write the value to the attribute handle
      if (AttsSetAttr(att_handle, len, (uint8_t*)buffer) != ATT_SUCCESS) {
          return BLE_ERROR_PARAM_OUT_OF_RANGE;
      }
  
      // return if the update does not have to be propagated to peers
      if (local_only || !get_cccd_index_by_value_handle(att_handle, cccd_index)) {
          return BLE_ERROR_NONE;
      }
  
      // This characteristic has a CCCD attribute. Handle notifications and
      // indications for all active connections if the authentication is
      // successful
      size_t updates_sent = 0;
  
  #if BLE_FEATURE_SECURITY
      for (dmConnId_t conn_id = DM_CONN_MAX; conn_id > DM_CONN_ID_NONE; --conn_id) {
          if (DmConnInUse(conn_id) == true) {
              if (is_update_authorized(conn_id, att_handle)) {
                  uint16_t cccd_config = AttsCccEnabled(conn_id, cccd_index);
                  if (cccd_config & ATT_CLIENT_CFG_NOTIFY) {
                      AttsHandleValueNtf(conn_id, att_handle, len, (uint8_t*)buffer);
                      updates_sent++;
                  }
                  if (cccd_config & ATT_CLIENT_CFG_INDICATE) {
                      AttsHandleValueInd(conn_id, att_handle, len, (uint8_t*)buffer);
                      updates_sent++;
                  }
              }
          }
      }
  #endif // BLE_FEATURE_SECURITY
  
      if (updates_sent) {
          handleDataSentEvent(updates_sent);
      }
  
      return BLE_ERROR_NONE;
  }
  
  ble_error_t GattServer::write_(
      connection_handle_t connection,
      GattAttribute::Handle_t att_handle,
      const uint8_t buffer[],
      uint16_t len,
      bool local_only
  ) {
      // Check to see if this is a CCCD
      uint8_t cccd_index;
      if (get_cccd_index_by_cccd_handle(att_handle, cccd_index)) {
          if ((connection == DM_CONN_ID_NONE) || (len != 2)) { // CCCDs are always 16 bits
              return BLE_ERROR_PARAM_OUT_OF_RANGE;
          }
  
          uint16_t cccd_value;
          memcpy(&cccd_value, buffer, sizeof(cccd_value));
          AttsCccSet(connection, cccd_index, cccd_value);
          return BLE_ERROR_NONE;
      }
  
      // write the value to the attribute handle
      if (AttsSetAttr(att_handle, len, (uint8_t*)buffer) != ATT_SUCCESS) {
          return BLE_ERROR_PARAM_OUT_OF_RANGE;
      }
  
      // return if the update does not have to be propagated to peers
      if (local_only || !get_cccd_index_by_value_handle(att_handle, cccd_index)) {
          return BLE_ERROR_NONE;
      }
  
      // This characteristic has a CCCD attribute. Handle notifications and indications.
      size_t updates_sent = 0;
  
  #if BLE_FEATURE_SECURITY
      if (is_update_authorized(connection, att_handle)) {
          uint16_t cccEnabled = AttsCccEnabled(connection, cccd_index);
          if (cccEnabled & ATT_CLIENT_CFG_NOTIFY) {
              AttsHandleValueNtf(connection, att_handle, len, (uint8_t*)buffer);
              updates_sent++;
          }
          if (cccEnabled & ATT_CLIENT_CFG_INDICATE) {
              AttsHandleValueInd(connection, att_handle, len, (uint8_t*)buffer);
              updates_sent++;
          }
      }
  #endif // BLE_FEATURE_SECURITY
  
      if (updates_sent) {
          handleDataSentEvent(updates_sent);
      }
  
      return BLE_ERROR_NONE;
  }
  
  ble_error_t GattServer::areUpdatesEnabled_(
      const GattCharacteristic &characteristic,
      bool *enabled
  ) {
      for (size_t idx = 0; idx < cccd_cnt; idx++) {
          if (characteristic.getValueHandle() == cccd_handles[idx]) {
              for (dmConnId_t conn_id = DM_CONN_MAX; conn_id > DM_CONN_ID_NONE; --conn_id) {
                  if (DmConnInUse(conn_id) == true) {
                      uint16_t cccd_value = AttsCccGet(conn_id, idx);
                      if (cccd_value & (ATT_CLIENT_CFG_NOTIFY | ATT_CLIENT_CFG_INDICATE)) {
                          *enabled = true;
                          return BLE_ERROR_NONE;
                      }
  
                  }
              }
              *enabled = false;
              return BLE_ERROR_NONE;
          }
      }
  
      return BLE_ERROR_PARAM_OUT_OF_RANGE;
  }
  
  ble_error_t GattServer::areUpdatesEnabled_(
      connection_handle_t connectionHandle,
      const GattCharacteristic &characteristic,
      bool *enabled
  ) {
      if (connectionHandle == DM_CONN_ID_NONE) {
          return BLE_ERROR_INVALID_PARAM;
      }
  
      for (uint8_t idx = 0; idx < cccd_cnt; idx++) {
          if (characteristic.getValueHandle() == cccd_handles[idx]) {
              uint16_t cccd_value = AttsCccGet(connectionHandle, idx);
              if (cccd_value & (ATT_CLIENT_CFG_NOTIFY | ATT_CLIENT_CFG_INDICATE)) {
                  *enabled = true;
              } else {
                  *enabled = false;
              }
              return BLE_ERROR_NONE;
          }
      }
      return BLE_ERROR_PARAM_OUT_OF_RANGE;
  }
  
  bool GattServer::isOnDataReadAvailable_() const
  {
      return true;
  }
  
  ::Gap::ConnectionParams_t GattServer::getPreferredConnectionParams()
  {
      ::Gap::ConnectionParams_t params = { 0 };
      memcpy(&params.minConnectionInterval, generic_access_service.ppcp, 2);
      memcpy(&params.maxConnectionInterval, generic_access_service.ppcp + 2, 2);
      memcpy(&params.slaveLatency, generic_access_service.ppcp + 4, 2);
      memcpy(&params.connectionSupervisionTimeout, generic_access_service.ppcp + 6, 2);
      return params;
  }
  
  void GattServer::setPreferredConnectionParams(const ::Gap::ConnectionParams_t& params)
  {
      memcpy(generic_access_service.ppcp, &params.minConnectionInterval, 2);
      memcpy(generic_access_service.ppcp + 2, &params.maxConnectionInterval, 2);
      memcpy(generic_access_service.ppcp + 4, &params.slaveLatency, 2);
      memcpy(generic_access_service.ppcp + 6, &params.connectionSupervisionTimeout, 2);
  }
  
  ble_error_t GattServer::setDeviceName(const uint8_t *deviceName)
  {
      size_t length = 0;
  
      if (deviceName != NULL) {
          length = strlen((const char*)deviceName);
      }
  
      if (length == 0) {
          free(generic_access_service.device_name_value());
      } else {
          uint8_t* res = (uint8_t*) realloc(generic_access_service.device_name_value(), length);
          if (res == NULL) {
              return BLE_ERROR_NO_MEM;
          }
  
          generic_access_service.device_name_value() = res;
          memcpy(res, deviceName, length);
      }
  
      generic_access_service.device_name_length = length;
  
      return BLE_ERROR_NONE;
  }
  
  void GattServer::getDeviceName(const uint8_t*& name, uint16_t& length)
  {
      length = generic_access_service.device_name_length;
      name = generic_access_service.device_name_value();
  }
  
  void GattServer::setAppearance(GapAdvertisingData::Appearance appearance)
  {
      generic_access_service.appearance = appearance;
  }
  
  GapAdvertisingData::Appearance GattServer::getAppearance()
  {
      return (GapAdvertisingData::Appearance) generic_access_service.appearance;
  }
  
  ble_error_t GattServer::reset_(void)
  {
      Base::reset_();
  
      while (registered_service) {
          internal_service_t* s = registered_service;
          registered_service = s->next;
          AttsRemoveGroup(s->attGroup.startHandle);
          delete s;
      }
  
      while (allocated_blocks) {
          alloc_block_t* b = allocated_blocks;
          allocated_blocks = b->next;
          free(b);
      }
  
      AttsRemoveGroup(generic_access_service.service.startHandle);
      AttsRemoveGroup(generic_attribute_service.service.startHandle);
      free(generic_access_service.device_name_value());
  
      currentHandle = 0;
      cccd_cnt = 0;
  
      _auth_char_count = 0;
  
      AttsCccRegister(cccd_cnt, (attsCccSet_t*)cccds, cccd_cb);
  
      return BLE_ERROR_NONE;
  }
  
  void GattServer::cccd_cb(attsCccEvt_t *evt)
  {
      GattServerEvents::gattEvent_t evt_type =
          evt->value & (ATT_CLIENT_CFG_NOTIFY | ATT_CLIENT_CFG_INDICATE) ?
              GattServerEvents::GATT_EVENT_UPDATES_ENABLED :
              GattServerEvents::GATT_EVENT_UPDATES_DISABLED;
  
      getInstance().handleEvent(evt_type, evt->handle);
  }
  
  void GattServer::att_cb(const attEvt_t *evt)
  {
      if (evt->hdr.status == ATT_SUCCESS && evt->hdr.event == ATT_MTU_UPDATE_IND) {
          ::GattServer::EventHandler *handler = getInstance().getEventHandler();
          if (handler) {
              handler->onAttMtuChange(evt->hdr.param, evt->mtu);
          }
      } else if (evt->hdr.status == ATT_SUCCESS && evt->hdr.event == ATTS_HANDLE_VALUE_CNF) {
          getInstance().handleEvent(GattServerEvents::GATT_EVENT_DATA_SENT, evt->handle);
      }
  }
  
  uint8_t GattServer::atts_read_cb(
      dmConnId_t connId,
      uint16_t handle,
      uint8_t operation,
      uint16_t offset,
      attsAttr_t *pAttr
  ) {
      GattCharacteristic* auth_char = getInstance().get_auth_char(handle);
      if (auth_char && auth_char->isReadAuthorizationEnabled()) {
          GattReadAuthCallbackParams read_auth_params = {
              connId,
              handle,
              offset,
              /* len */ 0,
              /* data */ NULL,
              AUTH_CALLBACK_REPLY_SUCCESS
          };
  
          GattAuthCallbackReply_t ret = auth_char->authorizeRead(&read_auth_params);
          if (ret != AUTH_CALLBACK_REPLY_SUCCESS) {
              return ret & 0xFF;
          }
  
          pAttr->pValue = read_auth_params.data;
          *pAttr->pLen = read_auth_params.len;
      }
  
      GattReadCallbackParams read_params = {
          connId,
          handle,
          offset,
          *pAttr->pLen,
          pAttr->pValue,
          /* status */ BLE_ERROR_NONE,
      };
      getInstance().handleDataReadEvent(&read_params);
  
      return ATT_SUCCESS;
  }
  
  uint8_t GattServer::atts_write_cb(
      dmConnId_t connId,
      uint16_t handle,
      uint8_t operation,
      uint16_t offset,
      uint16_t len,
      uint8_t *pValue,
      attsAttr_t *pAttr
  ) {
      uint8_t err;
  
      /* TODO: offset is not handled properly */
      if ((err = AttsSetAttr(handle, len, pValue)) != ATT_SUCCESS) {
          return err;
      }
  
      GattWriteCallbackParams::WriteOp_t writeOp;
      switch (operation) {
          case ATT_PDU_WRITE_REQ:
              writeOp = GattWriteCallbackParams::OP_WRITE_REQ;
              break;
          case ATT_PDU_WRITE_CMD:
              writeOp = GattWriteCallbackParams::OP_WRITE_CMD;
              break;
  #if BLE_FEATURE_SIGNING
          case ATT_PDU_SIGNED_WRITE_CMD:
              if (getInstance()._signing_event_handler) {
                  getInstance()._signing_event_handler->on_signed_write_received(
                      connId,
                      AttsGetSignCounter(connId)
                  );
              }
              writeOp = GattWriteCallbackParams::OP_SIGN_WRITE_CMD;
              break;
  #endif // BLE_FEATURE_SIGNING
          case ATT_PDU_PREP_WRITE_REQ:
              writeOp = GattWriteCallbackParams::OP_PREP_WRITE_REQ;
              break;
          case ATT_PDU_EXEC_WRITE_REQ:
              writeOp = GattWriteCallbackParams::OP_EXEC_WRITE_REQ_NOW;
              break;
          default:
              writeOp = GattWriteCallbackParams::OP_INVALID;
              break;
      }
  
      GattCharacteristic* auth_char = getInstance().get_auth_char(handle);
      if (auth_char && auth_char->isWriteAuthorizationEnabled()) {
          GattWriteAuthCallbackParams write_auth_params = {
              connId,
              handle,
              offset,
              len,
              pValue,
              AUTH_CALLBACK_REPLY_SUCCESS
          };
  
          GattAuthCallbackReply_t ret = auth_char->authorizeWrite(&write_auth_params);
          if (ret!= AUTH_CALLBACK_REPLY_SUCCESS) {
              return ret & 0xFF;
          }
      }
  
      GattWriteCallbackParams write_params = {
          connId,
          handle,
          writeOp,
          offset,
          len,
          pValue
      };
      getInstance().handleDataWrittenEvent(&write_params);
  
      return ATT_SUCCESS;
  }
  
  
  uint8_t GattServer::atts_auth_cb(dmConnId_t connId, uint8_t permit, uint16_t handle)
  {
  #if BLE_FEATURE_SECURITY
      // this CB is triggered when read or write of an attribute (either a value
      // handle or a descriptor) requires secure connection security.
      SecurityManager& security_manager = BLE::deviceInstance().getSecurityManager();
  
      link_encryption_t encryption(link_encryption_t::NOT_ENCRYPTED);
      ble_error_t err = security_manager.getLinkEncryption(connId, &encryption);
      if (err) {
          return ATT_ERR_AUTH;
      }
  
      if (encryption != link_encryption_t::ENCRYPTED_WITH_SC_AND_MITM) {
          return ATT_ERR_AUTH;
      }
  
      return ATT_SUCCESS;
  #else
      return ATT_ERR_AUTH;
  #endif
  }
  
  
  void GattServer::add_generic_access_service()
  {
      ++currentHandle;
      generic_access_service.service.pNext = NULL;
      generic_access_service.service.startHandle = currentHandle;
      generic_access_service.service.readCback = atts_read_cb;
      generic_access_service.service.writeCback = atts_write_cb;
  
      // bind attributes to the service
      generic_access_service.service.pAttr = generic_access_service.attributes;
  
      attsAttr_t* current_attribute = generic_access_service.attributes;
  
      // service attribute
      current_attribute->pUuid = attPrimSvcUuid;
      current_attribute->pValue = (uint8_t*) attGapSvcUuid;
      current_attribute->maxLen = sizeof(attGapSvcUuid);
      current_attribute->pLen = &current_attribute->maxLen;
      current_attribute->settings = 0;
      current_attribute->permissions = ATTS_PERMIT_READ;
  
      // device name declaration
      currentHandle += 2; // note: incremented by two to get a pointer to the value handle
      ++current_attribute;
  
      // set properties
      generic_access_service.device_name_declaration_value[0] = ATT_PROP_READ;
      // set value handle
      memcpy(generic_access_service.device_name_declaration_value + 1, &currentHandle, sizeof(currentHandle));
      // set the characteristic UUID
      memcpy(generic_access_service.device_name_declaration_value + 3, attDnChUuid, sizeof(attDnChUuid));
  
      current_attribute->pUuid = attChUuid;
      current_attribute->pValue = generic_access_service.device_name_declaration_value;
      current_attribute->maxLen = sizeof(generic_access_service.device_name_declaration_value);
      current_attribute->pLen = &current_attribute->maxLen;
      current_attribute->settings = 0;
      current_attribute->permissions = ATTS_PERMIT_READ;
  
      // device name value
      ++current_attribute;
      generic_access_service.device_name_length = 0;
      current_attribute->pUuid = attDnChUuid;
      current_attribute->maxLen = 248;
      current_attribute->pLen = &generic_access_service.device_name_length;
      current_attribute->pValue = NULL;
      current_attribute->settings = ATTS_SET_VARIABLE_LEN;
      current_attribute->permissions = ATTS_PERMIT_READ;
  
      // appearance declaration
      currentHandle += 2; // note: incremented by two to get a pointer to the value handle
      ++current_attribute;
  
      // set properties
      generic_access_service.appearance_declaration_value[0] = ATT_PROP_READ;
      // set value handle
      memcpy(generic_access_service.appearance_declaration_value + 1, &currentHandle, sizeof(currentHandle));
      // set the characteristic UUID
      memcpy(generic_access_service.appearance_declaration_value + 3, attApChUuid, sizeof(attApChUuid));
  
      current_attribute->pUuid = attChUuid;
      current_attribute->pValue = generic_access_service.appearance_declaration_value;
      current_attribute->maxLen = sizeof(generic_access_service.appearance_declaration_value);
      current_attribute->pLen = &current_attribute->maxLen;
      current_attribute->settings = 0;
      current_attribute->permissions = ATTS_PERMIT_READ;
  
      // appearance value
      ++current_attribute;
      generic_access_service.appearance = 0; // unknown appearance
      current_attribute->pUuid = attApChUuid;
      current_attribute->maxLen = sizeof(generic_access_service.appearance);
      current_attribute->pLen = &current_attribute->maxLen;
      current_attribute->pValue = (uint8_t*) &generic_access_service.appearance;
      current_attribute->settings = 0;
      current_attribute->permissions = ATTS_PERMIT_READ;
  
  
      // peripheral prefered connection parameters declaration
      currentHandle += 2; // note: incremented by two to get a pointer to the value handle
      ++current_attribute;
  
      // set properties
      generic_access_service.ppcp_declaration_value[0] = ATT_PROP_READ;
      // set value handle
      memcpy(generic_access_service.ppcp_declaration_value + 1, &currentHandle, sizeof(currentHandle));
      // set the characteristic UUID
      memcpy(generic_access_service.ppcp_declaration_value + 3, attPpcpChUuid, sizeof(attPpcpChUuid));
  
      current_attribute->pUuid = attChUuid;
      current_attribute->pValue = generic_access_service.ppcp_declaration_value;
      current_attribute->maxLen = sizeof(generic_access_service.ppcp_declaration_value);
      current_attribute->pLen = &current_attribute->maxLen;
      current_attribute->settings = 0;
      current_attribute->permissions = ATTS_PERMIT_READ;
  
      // peripheral prefered connection parameters value
      ++current_attribute;
      const uint8_t default_ppcp_value[] = {
          0xFF, 0xFF, // no specific min connection interval
          0xFF, 0xFF, // no specific max connection interval
          0x00, 0x00, // no slave latency
          0xFF, 0xFF // no specific connection supervision timeout
      };
      memcpy(&generic_access_service.ppcp, default_ppcp_value, sizeof(default_ppcp_value));
      current_attribute->pUuid = attPpcpChUuid;
      current_attribute->maxLen = sizeof(generic_access_service.ppcp);
      current_attribute->pLen = &current_attribute->maxLen;
      current_attribute->pValue = generic_access_service.ppcp;
      current_attribute->settings = 0;
      current_attribute->permissions = ATTS_PERMIT_READ;
  
      generic_access_service.service.endHandle = currentHandle;
      AttsAddGroup(&generic_access_service.service);
  }
  
  void GattServer::add_generic_attribute_service()
  {
      ++currentHandle;
      generic_attribute_service.service.pNext = NULL;
      generic_attribute_service.service.startHandle = currentHandle;
      generic_attribute_service.service.readCback = atts_read_cb;
      generic_attribute_service.service.writeCback = atts_write_cb;
  
      // bind attributes to the service
      generic_attribute_service.service.pAttr = generic_attribute_service.attributes;
  
      attsAttr_t* current_attribute = generic_attribute_service.attributes;
  
      // service attribute
      current_attribute->pUuid = attPrimSvcUuid;
      current_attribute->pValue = (uint8_t*) attGattSvcUuid;
      current_attribute->maxLen = sizeof(attGattSvcUuid);
      current_attribute->pLen = &current_attribute->maxLen;
      current_attribute->settings = 0;
      current_attribute->permissions = ATTS_PERMIT_READ;
  
      // service changed declaration
      currentHandle += 2; // note: incremented by two to get a pointer to the value handle
      ++current_attribute;
  
      // set properties
      generic_attribute_service.service_changed_declaration[0] = ATT_PROP_INDICATE;
      // set value handle
      memcpy(generic_attribute_service.service_changed_declaration + 1, &currentHandle, sizeof(currentHandle));
      // set the characteristic UUID
      memcpy(generic_attribute_service.service_changed_declaration + 3, attScChUuid, sizeof(attScChUuid));
  
      current_attribute->pUuid = attChUuid;
      current_attribute->pValue = generic_attribute_service.service_changed_declaration;
      current_attribute->maxLen = sizeof(generic_attribute_service.service_changed_declaration);
      current_attribute->pLen = &current_attribute->maxLen;
      current_attribute->settings = 0;
      current_attribute->permissions = ATTS_PERMIT_READ;
  
      // service changed value
      ++current_attribute;
      current_attribute->pUuid = attScChUuid;
      current_attribute->maxLen = 0;
      current_attribute->pLen = &current_attribute->maxLen;
      current_attribute->pValue = NULL;
      current_attribute->settings = 0;
      current_attribute->permissions = 0;
  
      // CCCD
      ++current_attribute;
      current_attribute->pUuid = attCliChCfgUuid;
      current_attribute->pValue = (uint8_t*)&cccd_values[cccd_cnt];
      current_attribute->maxLen = 2;
      current_attribute->pLen = &current_attribute->maxLen;
      current_attribute->settings = ATTS_SET_CCC;
      current_attribute->permissions = (ATTS_PERMIT_READ | ATTS_PERMIT_WRITE);
  
      cccds[cccd_cnt].handle = currentHandle;
      cccds[cccd_cnt].valueRange = ATT_CLIENT_CFG_INDICATE;
      cccds[cccd_cnt].secLevel = DM_SEC_LEVEL_NONE;
      cccd_handles[cccd_cnt] = currentHandle - 1;
      cccd_cnt++;
  
      generic_attribute_service.service.endHandle = currentHandle;
      AttsAddGroup(&generic_attribute_service.service);
      AttsCccRegister(cccd_cnt, (attsCccSet_t*)cccds, cccd_cb);
  }
  
  void* GattServer::alloc_block(size_t block_size) {
      alloc_block_t* block = (alloc_block_t*) malloc(sizeof(alloc_block_t) + block_size);
      if (block == NULL) {
          return NULL;
      }
  
      if (allocated_blocks) {
          block->next = allocated_blocks;
      } else {
          block->next = NULL;
      }
  
      allocated_blocks = block;
  
      return block->data;
  }
  
  GattCharacteristic* GattServer::get_auth_char(uint16_t value_handle)
  {
      for (size_t i = 0; i < _auth_char_count; ++i) {
          if (_auth_char[i]->getValueHandle() == value_handle) {
              return _auth_char[i];
          }
      }
      return NULL;
  }
  
  bool GattServer::get_cccd_index_by_cccd_handle(GattAttribute::Handle_t cccd_handle, uint8_t& idx) const
  {
      for (idx = 0; idx < cccd_cnt; idx++) {
          if (cccd_handle == cccds[idx].handle) {
              return true;
          }
      }
      return false;
  }
  
  bool GattServer::get_cccd_index_by_value_handle(GattAttribute::Handle_t char_handle, uint8_t& idx) const
  {
      for (idx = 0; idx < cccd_cnt; ++idx) {
          if (char_handle == cccd_handles[idx]) {
              return true;
          }
      }
      return false;
  }
  
  bool GattServer::is_update_authorized(
      connection_handle_t connection,
      GattAttribute::Handle_t value_handle
  ) {
      GattCharacteristic* auth_char = get_auth_char(value_handle);
      if (!auth_char) {
          return true;
      }
  
      att_security_requirement_t sec_req =
          auth_char->getUpdateSecurityRequirement();
  
      if (sec_req == att_security_requirement_t::NONE) {
          return true;
      }
  
  #if BLE_FEATURE_SECURITY
      SecurityManager& security_manager = BLE::deviceInstance().getSecurityManager();
      link_encryption_t encryption(link_encryption_t::NOT_ENCRYPTED);
      ble_error_t err = security_manager.getLinkEncryption(connection, &encryption);
      if (err) {
          return false;
      }
  #endif // BLE_FEATURE_SECURITY
  
      switch (sec_req.value()) {
  #if BLE_FEATURE_SECURITY
          case att_security_requirement_t::UNAUTHENTICATED:
              if (encryption < link_encryption_t::ENCRYPTED) {
                  return false;
              }
              return true;
  
          case att_security_requirement_t::AUTHENTICATED:
              if (encryption < link_encryption_t::ENCRYPTED_WITH_MITM) {
                  return false;
              }
              return true;
  #if BLE_FEATURE_SECURE_CONNECTIONS
          case att_security_requirement_t::SC_AUTHENTICATED:
              if (encryption != link_encryption_t::ENCRYPTED_WITH_SC_AND_MITM) {
                  return false;
              }
              return true;
  #endif // BLE_FEATURE_SECURE_CONNECTIONS
  #endif // BLE_FEATURE_SECURITY
          default:
              return false;
      }
  }
  
  GattServer::GattServer() :
      _signing_event_handler(NULL),
      cccds(),
      cccd_values(),
      cccd_handles(),
      cccd_cnt(0),
      _auth_char(),
      _auth_char_count(0),
      generic_access_service(),
      generic_attribute_service(),
      registered_service(NULL),
      allocated_blocks(NULL),
      currentHandle(0),
      default_services_added(false)
  {
  }
  
  } // namespace cordio
  } // namespace vendor
  } // namespace ble
  