mirror of https://github.com/ARMmbed/mbed-os.git
1533 lines
49 KiB
C++
1533 lines
49 KiB
C++
/* mbed Microcontroller Library
|
|
* Copyright (c) 2006-2020 ARM Limited
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* 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 "GattServerImpl.h"
|
|
#include "source/BLEInstanceBase.h"
|
|
#include "wsf_types.h"
|
|
#include "att_api.h"
|
|
|
|
namespace ble {
|
|
namespace impl {
|
|
|
|
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 = nullptr;
|
|
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 == nullptr) {
|
|
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 = nullptr;
|
|
}
|
|
|
|
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() == nullptr) &&
|
|
(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() == nullptr) &&
|
|
(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, ¤tHandle, 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;
|
|
attribute_it->settings |= ATTS_SET_ALLOW_OFFSET;
|
|
}
|
|
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 >= MBED_CONF_BLE_API_IMPLEMENTATION_MAX_CHARACTERISTIC_AUTHORISATION_COUNT) {
|
|
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 >= MBED_CONF_BLE_API_IMPLEMENTATION_MAX_CCCD_COUNT) {
|
|
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 (!(attribute_it->settings & ATTS_SET_CCC)) {
|
|
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 (!(attribute_it->settings & ATTS_SET_CCC)) {
|
|
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 >= MBED_CONF_BLE_API_IMPLEMENTATION_MAX_CCCD_COUNT) {
|
|
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 = nullptr;
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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;
|
|
}
|
|
|
|
ble::Gap::PreferredConnectionParams_t GattServer::getPreferredConnectionParams()
|
|
{
|
|
ble::Gap::PreferredConnectionParams_t params = {0};
|
|
memcpy(¶ms.minConnectionInterval, generic_access_service.ppcp, 2);
|
|
memcpy(¶ms.maxConnectionInterval, generic_access_service.ppcp + 2, 2);
|
|
memcpy(¶ms.slaveLatency, generic_access_service.ppcp + 4, 2);
|
|
memcpy(¶ms.connectionSupervisionTimeout, generic_access_service.ppcp + 6, 2);
|
|
return params;
|
|
}
|
|
|
|
void GattServer::setPreferredConnectionParams(const ble::Gap::PreferredConnectionParams_t ¶ms)
|
|
{
|
|
memcpy(generic_access_service.ppcp, ¶ms.minConnectionInterval, 2);
|
|
memcpy(generic_access_service.ppcp + 2, ¶ms.maxConnectionInterval, 2);
|
|
memcpy(generic_access_service.ppcp + 4, ¶ms.slaveLatency, 2);
|
|
memcpy(generic_access_service.ppcp + 6, ¶ms.connectionSupervisionTimeout, 2);
|
|
}
|
|
|
|
#if 0 // Disabled until reworked and reintroduced to GattServer API
|
|
|
|
ble_error_t GattServer::setDeviceName(const uint8_t *deviceName)
|
|
{
|
|
size_t length = 0;
|
|
|
|
if (deviceName != nullptr) {
|
|
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 == nullptr) {
|
|
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;
|
|
}
|
|
|
|
#endif // Disabled until reworked and reintroduced to GattServer API
|
|
|
|
ble_error_t GattServer::reset(ble::GattServer* server)
|
|
{
|
|
/* Notify that the instance is about to shutdown */
|
|
shutdownCallChain.call(server);
|
|
shutdownCallChain.clear();
|
|
|
|
serviceCount = 0;
|
|
characteristicCount = 0;
|
|
|
|
dataSentCallChain.clear();
|
|
dataWrittenCallChain.clear();
|
|
dataReadCallChain.clear();
|
|
updatesEnabledCallback = nullptr;
|
|
updatesDisabledCallback = nullptr;
|
|
confirmationReceivedCallback = nullptr;
|
|
|
|
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) {
|
|
ble::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 */ nullptr,
|
|
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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* we don't write anything during the prepare phase */
|
|
bool write_happened = (operation != ATT_PDU_PREP_WRITE_REQ);
|
|
|
|
MBED_ASSERT(len + offset <= pAttr->maxLen);
|
|
|
|
if (write_happened) {
|
|
WsfTaskLock();
|
|
|
|
memcpy((pAttr->pValue + offset), pValue, len);
|
|
|
|
/* write the length if variable length attribute */
|
|
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0) {
|
|
*(pAttr->pLen) = offset + len;
|
|
}
|
|
|
|
WsfTaskUnlock();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (write_happened) {
|
|
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.
|
|
ble::SecurityManager &security_manager = BLEInstanceBase::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 = nullptr;
|
|
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 = ¤t_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, ¤tHandle, 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 = ¤t_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 = nullptr;
|
|
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, ¤tHandle, 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 = ¤t_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 = ¤t_attribute->maxLen;
|
|
current_attribute->pValue = (uint8_t *) &generic_access_service.appearance;
|
|
current_attribute->settings = 0;
|
|
current_attribute->permissions = ATTS_PERMIT_READ;
|
|
|
|
|
|
// peripheral preferred 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, ¤tHandle, 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 = ¤t_attribute->maxLen;
|
|
current_attribute->settings = 0;
|
|
current_attribute->permissions = ATTS_PERMIT_READ;
|
|
|
|
// peripheral preferred 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 = ¤t_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 = nullptr;
|
|
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 = ¤t_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, ¤tHandle, 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 = ¤t_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 = ¤t_attribute->maxLen;
|
|
current_attribute->pValue = nullptr;
|
|
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 = ¤t_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 == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (allocated_blocks) {
|
|
block->next = allocated_blocks;
|
|
} else {
|
|
block->next = nullptr;
|
|
}
|
|
|
|
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 nullptr;
|
|
}
|
|
|
|
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
|
|
ble::SecurityManager &security_manager = BLEInstanceBase::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() :
|
|
eventHandler(nullptr),
|
|
serviceCount(0),
|
|
characteristicCount(0),
|
|
dataSentCallChain(),
|
|
dataWrittenCallChain(),
|
|
dataReadCallChain(),
|
|
updatesEnabledCallback(nullptr),
|
|
updatesDisabledCallback(nullptr),
|
|
confirmationReceivedCallback(nullptr),
|
|
_signing_event_handler(nullptr),
|
|
cccds(),
|
|
cccd_values(),
|
|
cccd_handles(),
|
|
cccd_cnt(0),
|
|
_auth_char(),
|
|
_auth_char_count(0),
|
|
generic_access_service(),
|
|
generic_attribute_service(),
|
|
registered_service(nullptr),
|
|
allocated_blocks(nullptr),
|
|
currentHandle(0),
|
|
default_services_added(false)
|
|
{
|
|
}
|
|
|
|
void GattServer::set_signing_event_handler(
|
|
PalSigningMonitorEventHandler *signing_event_handler
|
|
)
|
|
{
|
|
_signing_event_handler = signing_event_handler;
|
|
}
|
|
|
|
void GattServer::onDataSent(const DataSentCallback_t &callback)
|
|
{
|
|
dataSentCallChain.add(callback);
|
|
}
|
|
|
|
ble::GattServer::DataSentCallbackChain_t &GattServer::onDataSent()
|
|
{
|
|
return dataSentCallChain;
|
|
}
|
|
|
|
ble::GattServer::DataWrittenCallbackChain_t &GattServer::onDataWritten()
|
|
{
|
|
return dataWrittenCallChain;
|
|
}
|
|
|
|
ble_error_t GattServer::onDataRead(const DataReadCallback_t &callback)
|
|
{
|
|
if (!isOnDataReadAvailable()) {
|
|
return BLE_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
dataReadCallChain.add(callback);
|
|
return BLE_ERROR_NONE;
|
|
}
|
|
|
|
ble::GattServer::DataReadCallbackChain_t &GattServer::onDataRead()
|
|
{
|
|
return dataReadCallChain;
|
|
}
|
|
|
|
void GattServer::onShutdown(const GattServerShutdownCallback_t &callback)
|
|
{
|
|
shutdownCallChain.add(callback);
|
|
}
|
|
|
|
GattServer::GattServerShutdownCallbackChain_t &GattServer::onShutdown()
|
|
{
|
|
return shutdownCallChain;
|
|
}
|
|
|
|
void GattServer::onUpdatesEnabled(EventCallback_t callback)
|
|
{
|
|
updatesEnabledCallback = callback;
|
|
}
|
|
|
|
void GattServer::onUpdatesDisabled(EventCallback_t callback)
|
|
{
|
|
updatesDisabledCallback = callback;
|
|
}
|
|
|
|
void GattServer::onConfirmationReceived(EventCallback_t callback)
|
|
{
|
|
confirmationReceivedCallback = callback;
|
|
}
|
|
|
|
void GattServer::setEventHandler(EventHandler *handler)
|
|
{
|
|
eventHandler = handler;
|
|
}
|
|
|
|
GattServer::EventHandler *GattServer::getEventHandler()
|
|
{
|
|
return eventHandler;
|
|
}
|
|
|
|
void GattServer::handleDataWrittenEvent(const GattWriteCallbackParams *params)
|
|
{
|
|
dataWrittenCallChain.call(params);
|
|
}
|
|
|
|
void GattServer::handleDataReadEvent(const GattReadCallbackParams *params)
|
|
{
|
|
dataReadCallChain.call(params);
|
|
}
|
|
|
|
void GattServer::handleEvent(
|
|
GattServerEvents::gattEvent_e type,
|
|
GattAttribute::Handle_t attributeHandle
|
|
)
|
|
{
|
|
switch (type) {
|
|
case GattServerEvents::GATT_EVENT_UPDATES_ENABLED:
|
|
if (updatesEnabledCallback) {
|
|
updatesEnabledCallback(attributeHandle);
|
|
}
|
|
break;
|
|
case GattServerEvents::GATT_EVENT_UPDATES_DISABLED:
|
|
if (updatesDisabledCallback) {
|
|
updatesDisabledCallback(attributeHandle);
|
|
}
|
|
break;
|
|
case GattServerEvents::GATT_EVENT_CONFIRMATION_RECEIVED:
|
|
if (confirmationReceivedCallback) {
|
|
confirmationReceivedCallback(attributeHandle);
|
|
}
|
|
break;
|
|
|
|
case GattServerEvents::GATT_EVENT_DATA_SENT:
|
|
// Called every time a notification or indication has been sent
|
|
handleDataSentEvent(1);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GattServer::handleDataSentEvent(unsigned count)
|
|
{
|
|
dataSentCallChain.call(count);
|
|
}
|
|
|
|
} // namespace impl
|
|
} // namespace ble
|