added att_db_util to construct GATT DB at runtime

This commit is contained in:
Matthias Ringwald 2015-05-23 23:35:37 +02:00
parent 45c4c64843
commit 3ec0d11b8e
6 changed files with 481 additions and 0 deletions

190
ble/att_db_util.c Normal file
View File

@ -0,0 +1,190 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#include <stdlib.h>
#include <string.h>
#include "att_db_util.h"
#include "att.h"
#include <btstack/utils.h>
#include "debug.h"
// ATT DB Storage
#ifndef HAVE_MALLOC
#ifdef MAX_ATT_DB_SIZE
static uint8_t att_db_storage[MAX_ATT_DB_SIZE];
#else
#error Neither HAVE_MALLOC] nor MAX_ATT_DB_SIZE is defined.
#endif
#endif
static uint8_t * att_db;
static uint16_t att_db_size;
static uint16_t att_db_max_size;
static uint16_t att_db_next_handle;
static void att_db_util_set_end_tag(void){
// end tag
att_db[att_db_size] = 0;
att_db[att_db_size+1] = 0;
}
void att_db_util_init(void){
#ifdef HAVE_MALLOC
att_db = (uint8_t*) malloc(128);
att_db_max_size = 128;
#else
att_db = att_db_storage;
att_db_max_size = sizeof(att_db_storage);
#endif
att_db_size = 0;
att_db_next_handle = 1;
att_db_util_set_end_tag();
}
/**
* asserts that the requested amount of bytes can be stored in the att_db
* @returns TRUE if space is available
*/
static int att_db_util_assert_space(uint16_t size){
size += 2; // for end tag
if (att_db_size + size <= att_db_max_size) return true;
#ifdef HAVE_MALLOC
int new_size = att_db_size + att_db_size / 2;
att_db = (uint8_t*) realloc(att_db, new_size);
if (!att_db) {
log_error("att_db: realloc failed");
return false;
}
att_db_max_size = new_size;
return true;
#else
log_error("att_db: out of memory");
return false;
#endif
}
// attribute size in bytes (16), flags(16), handle (16), uuid (16/128), value(...)
// db endds with 0x00 0x00
static void att_db_util_add_attribute_uuid16(uint16_t uuid16, uint16_t flags, uint8_t * data, uint16_t data_len){
int size = 2 + 2 + 2 + 2 + data_len;
if (!att_db_util_assert_space(size)) return;
bt_store_16(att_db, att_db_size, size);
att_db_size += 2;
bt_store_16(att_db, att_db_size, flags);
att_db_size += 2;
bt_store_16(att_db, att_db_size, att_db_next_handle);
att_db_size += 2;
att_db_next_handle++;
bt_store_16(att_db, att_db_size, uuid16);
att_db_size += 2;
memcpy(&att_db[att_db_size], data, data_len);
att_db_size += data_len;
att_db_util_set_end_tag();
}
static void att_db_util_add_attribute_uuid128(uint8_t * uuid128, uint16_t flags, uint8_t * data, uint16_t data_len){
int size = 2 + 2 + 2 + 16 + data_len;
if (!att_db_util_assert_space(size)) return;
flags |= ATT_PROPERTY_UUID128;
bt_store_16(att_db, att_db_size, size);
att_db_size += 2;
bt_store_16(att_db, att_db_size, flags);
att_db_size += 2;
bt_store_16(att_db, att_db_size, att_db_next_handle);
att_db_size += 2;
att_db_next_handle++;
swap128(uuid128, &att_db[att_db_size]);
att_db_size += 16;
memcpy(&att_db[att_db_size], data, data_len);
att_db_size += data_len;
att_db_util_set_end_tag();
}
void att_db_util_add_service_uuid16(uint16_t uuid16){
uint8_t buffer[2];
bt_store_16(buffer, 0, uuid16);
att_db_util_add_attribute_uuid16(GATT_PRIMARY_SERVICE_UUID, ATT_PROPERTY_READ, buffer, 2);
}
void att_db_util_add_service_uuid128(uint8_t * uuid128){
uint8_t buffer[16];
swap128(uuid128, buffer);
att_db_util_add_attribute_uuid16(GATT_PRIMARY_SERVICE_UUID, ATT_PROPERTY_READ, buffer, 16);
}
uint16_t att_db_util_add_characteristic_uuid16(uint16_t uuid16, uint16_t properties, uint8_t * data, uint16_t data_len){
uint8_t buffer[5];
buffer[0] = properties;
bt_store_16(buffer, 1, att_db_next_handle + 1);
bt_store_16(buffer, 3, uuid16);
att_db_util_add_attribute_uuid16(GATT_CHARACTERISTICS_UUID, ATT_PROPERTY_READ, buffer, sizeof(buffer));
uint16_t value_handle = att_db_next_handle;
att_db_util_add_attribute_uuid16(uuid16, properties, data, data_len);
if (properties & (ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE)){
uint16_t flags = ATT_PROPERTY_READ | ATT_PROPERTY_WRITE | ATT_PROPERTY_DYNAMIC;
bt_store_16(buffer, 0, 0);
att_db_util_add_attribute_uuid16(GATT_CLIENT_CHARACTERISTICS_CONFIGURATION, flags, buffer, 2);
}
return value_handle;
}
uint16_t att_db_util_add_characteristic_uuid128(uint8_t * uuid128, uint16_t properties, uint8_t * data, uint16_t data_len){
uint8_t buffer[19];
buffer[0] = properties;
bt_store_16(buffer, 1, att_db_next_handle + 1);
swap128(uuid128, &buffer[3]);
att_db_util_add_attribute_uuid16(GATT_CHARACTERISTICS_UUID, ATT_PROPERTY_READ, buffer, sizeof(buffer));
uint16_t value_handle = att_db_next_handle;
att_db_util_add_attribute_uuid128(uuid128, properties, data, data_len);
if (properties & (ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE)){
uint16_t flags = ATT_PROPERTY_READ | ATT_PROPERTY_WRITE | ATT_PROPERTY_DYNAMIC;
bt_store_16(buffer, 0, 0);
att_db_util_add_attribute_uuid16(GATT_CLIENT_CHARACTERISTICS_CONFIGURATION, flags, buffer, 2);
}
return value_handle;
}
uint8_t * att_db_util_get_address(void){
return att_db;
}
uint16_t att_db_util_get_size(void){
return att_db_size + 2; // end tag
}

100
ble/att_db_util.h Normal file
View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
/**
* Helper to construct ATT DB at runtime
* (BTstack GATT Compiler is not used)
*/
#ifndef __ATT_DB_UTIL
#define __ATT_DB_UTIL
#include "btstack-config.h"
#
#ifdef HAVE_MALLOC
#else
#endif
#include <stdint.h>
#if defined __cplusplus
extern "C" {
#endif
/**
* @brief Init ATT DB storage
*/
void att_db_util_init(void);
/**
* @brief Add primary service for 16-bit UUID
*/
void att_db_util_add_service_uuid16(uint16_t udid16);
/**
* @brief Add primary service for 128-bit UUID
*/
void att_db_util_add_service_uuid128(uint8_t * udid128);
/**
* @brief Add Characteristic with 16-bit UUID, properties, and data
* @returns attribute value handle
* @see ATT_PROPERTY_* in ble/att.h
*/
uint16_t att_db_util_add_characteristic_uuid16(uint16_t udid16, uint16_t properties, uint8_t * data, uint16_t data_len);
/**
* @brief Add Characteristic with 128-bit UUID, properties, and data
* @returns attribute value handle
* @see ATT_PROPERTY_* in ble/att.h
*/
uint16_t att_db_util_add_characteristic_uuid128(uint8_t * udid128, uint16_t properties, uint8_t * data, uint16_t data_len);
/**
* @brief Get address of constructed ATT DB
*/
uint8_t * att_db_util_get_address(void);
/**
* @brief Get size of constructed ATT DB
*/
uint16_t att_db_util_get_size(void);
#if defined __cplusplus
}
#endif
#endif

View File

@ -2,6 +2,7 @@
# Makefile to build and run all tests
SUBDIRS = \
att_db_util \
ble_client \
des_iterator \
gatt_client \

34
test/att_db/Makefile Normal file
View File

@ -0,0 +1,34 @@
CC = g++
# Requirements: http://www.cpputest.org/ should be placed in btstack/test
BTSTACK_ROOT = ../..
CPPUTEST_HOME = ${BTSTACK_ROOT}/test/cpputest
CFLAGS = -g -Wall -I.. -I${BTSTACK_ROOT}/example/libusb -I${BTSTACK_ROOT}/src -I${BTSTACK_ROOT}/ble -I${BTSTACK_ROOT}/include -I$(CPPUTEST_HOME)/include
# -L$(CPPUTEST_HOME)/lib
LDFLAGS += -lCppUTest -lCppUTestExt
VPATH += ${BTSTACK_ROOT}/ble
VPATH += ${BTSTACK_ROOT}/src
VPATH += ${BTSTACK_ROOT}/platforms/posix/src
COMMON = \
utils.c \
att_db_util.c \
COMMON_OBJ = $(COMMON:.c=.o)
all: att_db_util_test
att_db_util_test: ${COMMON_OBJ} att_db_util_test.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
test:
./att_db_util_test
clean:
rm -f att_db_util_test
rm -f *.o
rm -rf *.dSYM

View File

@ -0,0 +1,110 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
// *****************************************************************************
//
// test rfcomm query tests
//
// *****************************************************************************
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "CppUTest/TestHarness.h"
#include "CppUTest/CommandLineTestRunner.h"
#include "att.h"
#include "att_db_util.h"
#include "le_counter.h"
#include <btstack/utils.h>
#if 0
PRIMARY_SERVICE, GAP_SERVICE
CHARACTERISTIC, GAP_DEVICE_NAME, READ, "SPP+LE Counter"
PRIMARY_SERVICE, GATT_SERVICE
CHARACTERISTIC, GATT_SERVICE_CHANGED, READ,
// Counter Service
PRIMARY_SERVICE, 0000FF10-0000-1000-8000-00805F9B34FB
// Counter Characteristic, with read and notify
CHARACTERISTIC, 0000FF11-0000-1000-8000-00805F9B34FB, READ | NOTIFY | DYNAMIC,
#endif
// 0000FF10-0000-1000-8000-00805F9B34FB
uint8_t counter_service_uuid[] = { 0x00, 0x00, 0xFF, 0x10, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
// 0000FF11-0000-1000-8000-00805F9B34FB
uint8_t counter_characteristic_uuid[] = { 0x00, 0x00, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
void CHECK_EQUAL_ARRAY(const uint8_t * expected, uint8_t * actual, int size){
for (int i=0; i<size; i++){
// printf("%03u: %02x - %02x\n", i, expected[i], actual[i]);
BYTES_EQUAL(expected[i], actual[i]);
}
}
TEST_GROUP(AttDbUtil){
void setup(void){
att_db_util_init();
}
};
TEST(AttDbUtil, LeCounterDb){
att_db_util_add_service_uuid16(GAP_SERVICE_UUID);
att_db_util_add_characteristic_uuid16(GAP_DEVICE_NAME_UUID, ATT_PROPERTY_READ, (uint8_t*)"SPP+LE Counter", 14);
att_db_util_add_service_uuid16(0x1801);
att_db_util_add_characteristic_uuid16(0x2a05, ATT_PROPERTY_READ, NULL, 0);
att_db_util_add_service_uuid128(counter_service_uuid);
att_db_util_add_characteristic_uuid128(counter_characteristic_uuid, ATT_PROPERTY_READ | ATT_PROPERTY_NOTIFY | ATT_PROPERTY_DYNAMIC, NULL, 0);
uint8_t * addr = att_db_util_get_address();
uint16_t size = att_db_util_get_size();
// hexdumpf(addr, size);
CHECK_EQUAL(size, sizeof(profile_data));
CHECK_EQUAL_ARRAY(profile_data, addr, size);
}
int main (int argc, const char * argv[]){
return CommandLineTestRunner::RunAllTests(argc, argv);
}

46
test/att_db/le_counter.h Normal file
View File

@ -0,0 +1,46 @@
// le_counter.h generated from ../../example/embedded/le_counter.gatt for BTstack
// binary representation
// attribute size in bytes (16), flags(16), handle (16), uuid (16/128), value(...)
#include <stdint.h>
const uint8_t profile_data[] =
{
// 0x0001 PRIMARY_SERVICE-GAP_SERVICE
0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28, 0x00, 0x18,
// 0x0002 CHARACTERISTIC-GAP_DEVICE_NAME-READ
0x0d, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x28, 0x02, 0x03, 0x00, 0x00, 0x2a,
// 0x0003 VALUE-GAP_DEVICE_NAME-READ-'SPP+LE Counter'
0x16, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x2a, 0x53, 0x50, 0x50, 0x2b, 0x4c, 0x45, 0x20, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
// 0x0004 PRIMARY_SERVICE-GATT_SERVICE
0x0a, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x28, 0x01, 0x18,
// 0x0005 CHARACTERISTIC-GATT_SERVICE_CHANGED-READ
0x0d, 0x00, 0x02, 0x00, 0x05, 0x00, 0x03, 0x28, 0x02, 0x06, 0x00, 0x05, 0x2a,
// 0x0006 VALUE-GATT_SERVICE_CHANGED-READ-''
0x08, 0x00, 0x02, 0x00, 0x06, 0x00, 0x05, 0x2a,
// Counter Service
// 0x0007 PRIMARY_SERVICE-0000FF10-0000-1000-8000-00805F9B34FB
0x18, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x28, 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x10, 0xff, 0x00, 0x00,
// Counter Characteristic, with read and notify
// 0x0008 CHARACTERISTIC-0000FF11-0000-1000-8000-00805F9B34FB-READ | NOTIFY | DYNAMIC
0x1b, 0x00, 0x02, 0x00, 0x08, 0x00, 0x03, 0x28, 0x12, 0x09, 0x00, 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x11, 0xff, 0x00, 0x00,
// 0x0009 VALUE-0000FF11-0000-1000-8000-00805F9B34FB-READ | NOTIFY | DYNAMIC-''
0x16, 0x00, 0x12, 0x03, 0x09, 0x00, 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x11, 0xff, 0x00, 0x00,
// 0x000a CLIENT_CHARACTERISTIC_CONFIGURATION
0x0a, 0x00, 0x0a, 0x01, 0x0a, 0x00, 0x02, 0x29, 0x00, 0x00,
// END
0x00, 0x00,
}; // total size 99 bytes
//
// list mapping between characteristics and handles
//
#define ATT_CHARACTERISTIC_GAP_DEVICE_NAME_01_VALUE_HANDLE 0x0003
#define ATT_CHARACTERISTIC_GATT_SERVICE_CHANGED_01_VALUE_HANDLE 0x0006
#define ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE 0x0009
#define ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_CLIENT_CONFIGURATION_HANDLE 0x000a