diff --git a/test/gatt_server/.gitignore b/test/gatt_server/.gitignore new file mode 100644 index 000000000..c064bbe29 --- /dev/null +++ b/test/gatt_server/.gitignore @@ -0,0 +1,3 @@ +gatt_client_test +le_central +profile.h diff --git a/test/gatt_server/Makefile b/test/gatt_server/Makefile new file mode 100644 index 000000000..050fd4db3 --- /dev/null +++ b/test/gatt_server/Makefile @@ -0,0 +1,49 @@ +CC = g++ + +# Requirements: cpputest.github.io + +BTSTACK_ROOT = ../.. + +CFLAGS = -DUNIT_TEST -x c++ -g -Wall -Wnarrowing -Wconversion-null -I. -I../ -I${BTSTACK_ROOT}/src +CFLAGS += -fprofile-arcs -ftest-coverage +LDFLAGS += -lCppUTest -lCppUTestExt + +VPATH += ${BTSTACK_ROOT}/src +VPATH += ${BTSTACK_ROOT}/src/ble +VPATH += ${BTSTACK_ROOT}/platform/posix + +COMMON = \ + ad_parser.c \ + att_server.c \ + att_db.c \ + att_dispatch.c \ + btstack_linked_list.c \ + btstack_memory.c \ + btstack_memory_pool.c \ + btstack_util.c \ + hci_cmd.c \ + hci_dump.c \ + le_device_db_memory.c \ + mock.c \ + btstack_tlv.c \ + +COMMON_OBJ = $(COMMON:.c=.o) + +all: gatt_server_test + +# compile .ble description +profile.h: profile.gatt + python ${BTSTACK_ROOT}/tool/compile_gatt.py $< $@ + +gatt_server_test: profile.h ${COMMON_OBJ} gatt_server_test.o + ${CC} ${COMMON_OBJ} gatt_server_test.o ${CFLAGS} ${LDFLAGS} -o $@ + +test: all + ./gatt_server_test + +clean: + rm -f gatt_server_test + rm -f *.o + rm -rf *.dSYM + rm -f *.gcno *.gcda + diff --git a/test/gatt_server/btstack_config.h b/test/gatt_server/btstack_config.h new file mode 100644 index 000000000..55c02b86a --- /dev/null +++ b/test/gatt_server/btstack_config.h @@ -0,0 +1,37 @@ +// +// btstack_config.h for most tests +// + +#ifndef __BTSTACK_CONFIG +#define __BTSTACK_CONFIG + +// Port related features +#define HAVE_MALLOC +#define HAVE_POSIX_TIME +#define HAVE_POSIX_FILE_IO +#define HAVE_BTSTACK_STDIN + +// BTstack features that can be enabled +#define ENABLE_BLE +#define ENABLE_CLASSIC +// #define ENABLE_LOG_DEBUG +#define ENABLE_LOG_ERROR +#define ENABLE_LOG_INFO +#define ENABLE_SDP_DES_DUMP +#define ENABLE_SDP_EXTRA_QUERIES +#define ENABLE_LE_SECURE_CONNECTIONS +#define ENABLE_LE_SIGNED_WRITE +#define ENABLE_LE_PERIPHERAL +#define ENABLE_LE_CENTRAL +#define ENABLE_SDP_EXTRA_QUERIES +#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE + +// BTstack configuration. buffers, sizes, ... +#define HCI_ACL_PAYLOAD_SIZE 52 +#define HCI_INCOMING_PRE_BUFFER_SIZE 4 + +#define MAX_NR_LE_DEVICE_DB_ENTRIES 4 + +#define NVM_NUM_LINK_KEYS 2 + +#endif diff --git a/test/gatt_server/gatt_server_test.c b/test/gatt_server/gatt_server_test.c new file mode 100644 index 000000000..99db94cd3 --- /dev/null +++ b/test/gatt_server/gatt_server_test.c @@ -0,0 +1,28 @@ + +// ***************************************************************************** +// +// test rfcomm query tests +// +// ***************************************************************************** + + +#include +#include +#include +#include + +#include "CppUTest/TestHarness.h" +#include "CppUTest/CommandLineTestRunner.h" + +#include "hci_cmd.h" + +#include "btstack_memory.h" +#include "hci.h" +#include "hci_dump.h" +#include "ble/att_server.h" +#include "ble/att_db.h" +#include "profile.h" + +int main (int argc, const char * argv[]){ + return CommandLineTestRunner::RunAllTests(argc, argv); +} diff --git a/test/gatt_server/mock.c b/test/gatt_server/mock.c new file mode 100644 index 000000000..b1c94d7de --- /dev/null +++ b/test/gatt_server/mock.c @@ -0,0 +1,216 @@ +#include +#include +#include +#include + +#include "hci.h" +#include "hci_dump.h" +#include "l2cap.h" + +#include "ble/att_db.h" +#include "ble/gatt_client.h" +#include "ble/sm.h" + +static btstack_packet_handler_t att_packet_handler; +static void (*registered_hci_event_handler) (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) = NULL; + +static btstack_linked_list_t connections; +static const uint16_t max_mtu = 23; +static uint8_t l2cap_stack_buffer[HCI_INCOMING_PRE_BUFFER_SIZE + 8 + max_mtu]; // pre buffer + HCI Header + L2CAP header +static uint16_t gatt_client_handle = 0x40; +static hci_connection_t hci_connection; + +uint16_t get_gatt_client_handle(void){ + return gatt_client_handle; +} + +void mock_simulate_command_complete(const hci_cmd_t *cmd){ + uint8_t packet[] = {HCI_EVENT_COMMAND_COMPLETE, 4, 1, (uint8_t) (cmd->opcode & 0xff), (uint8_t) (cmd->opcode >> 8), 0}; + registered_hci_event_handler(HCI_EVENT_PACKET, 0, (uint8_t *)&packet, sizeof(packet)); +} + +void mock_simulate_hci_state_working(void){ + uint8_t packet[3] = {BTSTACK_EVENT_STATE, 0, HCI_STATE_WORKING}; + registered_hci_event_handler(HCI_EVENT_PACKET, 0, (uint8_t *)&packet, 3); +} + +void mock_simulate_connected(void){ + uint8_t packet[] = {0x3E, 0x13, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x9B, 0x77, 0xD1, 0xF7, 0xB1, 0x34, 0x50, 0x00, 0x00, 0x00, 0xD0, 0x07, 0x05}; + registered_hci_event_handler(HCI_EVENT_PACKET, 0, (uint8_t *)&packet, sizeof(packet)); +} + +void mock_simulate_scan_response(void){ + uint8_t packet[] = {0xE2, 0x13, 0xE2, 0x01, 0x34, 0xB1, 0xF7, 0xD1, 0x77, 0x9B, 0xCC, 0x09, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + registered_hci_event_handler(HCI_EVENT_PACKET, 0, (uint8_t *)&packet, sizeof(packet)); +} + +void gap_start_scan(void){ +} +void gap_stop_scan(void){ +} +uint8_t gap_connect(bd_addr_t addr, bd_addr_type_t addr_type){ + return 0; +} +void gap_set_scan_parameters(uint8_t scan_type, uint16_t scan_interval, uint16_t scan_window){ +} + +int gap_reconnect_security_setup_active(hci_con_handle_t con_handle){ + UNUSED(con_handle); + return 0; +} + +static void att_init_connection(att_connection_t * att_connection){ + att_connection->mtu = 23; + att_connection->max_mtu = 23; + att_connection->encryption_key_size = 0; + att_connection->authenticated = 0; + att_connection->authorized = 0; +} + +int hci_can_send_acl_le_packet_now(void){ + return 1; +} + +int l2cap_can_send_connectionless_packet_now(void){ + return 1; +} + +uint8_t *l2cap_get_outgoing_buffer(void){ + // printf("l2cap_get_outgoing_buffer\n"); + return (uint8_t *)&l2cap_stack_buffer; // 8 bytes +} + +uint16_t l2cap_max_mtu(void){ + // printf("l2cap_max_mtu\n"); + return max_mtu; +} + +uint16_t l2cap_max_le_mtu(void){ + // printf("l2cap_max_mtu\n"); + return max_mtu; +} + +void l2cap_init(void){} + +void l2cap_register_fixed_channel(btstack_packet_handler_t packet_handler, uint16_t channel_id) { + att_packet_handler = packet_handler; +} + +void hci_add_event_handler(btstack_packet_callback_registration_t * callback_handler){ + registered_hci_event_handler = callback_handler->callback; +} + +int l2cap_reserve_packet_buffer(void){ + return 1; +} + +void l2cap_release_packet_buffer(void){ +} + +int l2cap_can_send_fixed_channel_packet_now(uint16_t handle, uint16_t channel_id){ + return 1; +} + +void l2cap_request_can_send_fix_channel_now_event(uint16_t handle, uint16_t channel_id){ + uint8_t event[] = { L2CAP_EVENT_CAN_SEND_NOW, 2, 1, 0}; + att_packet_handler(HCI_EVENT_PACKET, 0, (uint8_t*)event, sizeof(event)); +} + +int l2cap_send_prepared_connectionless(uint16_t handle, uint16_t cid, uint16_t len){ + att_connection_t att_connection; + att_init_connection(&att_connection); + uint8_t response[max_mtu]; + uint16_t response_len = att_handle_request(&att_connection, l2cap_get_outgoing_buffer(), len, &response[0]); + if (response_len){ + att_packet_handler(ATT_DATA_PACKET, gatt_client_handle, &response[0], response_len); + } + return 0; +} + +void sm_add_event_handler(btstack_packet_callback_registration_t * callback_handler){ +} + +int sm_cmac_ready(void){ + return 1; +} +void sm_cmac_signed_write_start(const sm_key_t key, uint8_t opcode, uint16_t attribute_handle, uint16_t message_len, const uint8_t * message, uint32_t sign_counter, void (*done_callback)(uint8_t * hash)){ + //sm_notify_client(SM_EVENT_IDENTITY_RESOLVING_SUCCEEDED, sm_central_device_addr_type, sm_central_device_address, 0, sm_central_device_matched); +} +int sm_le_device_index(uint16_t handle ){ + return -1; +} + +irk_lookup_state_t sm_identity_resolving_state(hci_con_handle_t con_handle){ + return IRK_LOOKUP_SUCCEEDED; +} + +void sm_request_pairing(hci_con_handle_t con_handle){ +} + +void btstack_run_loop_set_timer(btstack_timer_source_t *a, uint32_t timeout_in_ms){ +} + +// Set callback that will be executed when timer expires. +void btstack_run_loop_set_timer_handler(btstack_timer_source_t *ts, void (*process)(btstack_timer_source_t *_ts)){ +} + +// Add/Remove timer source. +void btstack_run_loop_add_timer(btstack_timer_source_t *timer){ +} + +int btstack_run_loop_remove_timer(btstack_timer_source_t *timer){ + return 1; +} + +void * btstack_run_loop_get_timer_context(btstack_timer_source_t *ts){ + return ts->context; +} + +// todo: +hci_connection_t * hci_connection_for_bd_addr_and_type(bd_addr_t addr, bd_addr_type_t addr_type){ + printf("hci_connection_for_bd_addr_and_type not implemented in mock backend\n"); + return NULL; +} +hci_connection_t * hci_connection_for_handle(hci_con_handle_t con_handle){ + return &hci_connection; +} +void hci_connections_get_iterator(btstack_linked_list_iterator_t *it){ + // printf("hci_connections_get_iterator not implemented in mock backend\n"); + btstack_linked_list_iterator_init(it, &connections); +} + +// int hci_send_cmd(const hci_cmd_t *cmd, ...){ +// // printf("hci_send_cmd opcode 0x%02x\n", cmd->opcode); +// return 0; +// } + +// int hci_can_send_packet_now_using_packet_buffer(uint8_t packet_type){ +// return 1; +// } + +// void hci_disconnect_security_block(hci_con_handle_t con_handle){ +// printf("hci_disconnect_security_block \n"); +// } + +// void hci_dump_log(const char * format, ...){ +// printf("hci_disconnect_security_block \n"); +// } + +void l2cap_run(void){ +} + +int gap_authenticated(hci_con_handle_t con_handle){ + return 0; +} +authorization_state_t gap_authorization_state(hci_con_handle_t con_handle){ + return AUTHORIZATION_UNKNOWN; +} +int gap_encryption_key_size(hci_con_handle_t con_handle){ + return 0; +} +int gap_secure_connection(hci_con_handle_t con_handle){ + return 0; +} +gap_connection_type_t gap_get_connection_type(hci_con_handle_t connection_handle){ + return GAP_CONNECTION_INVALID; +} diff --git a/test/gatt_server/profile.gatt b/test/gatt_server/profile.gatt new file mode 100644 index 000000000..97c7d04c7 --- /dev/null +++ b/test/gatt_server/profile.gatt @@ -0,0 +1,130 @@ +PRIMARY_SERVICE, GAP_SERVICE +CHARACTERISTIC, GAP_DEVICE_NAME, READ | WRITE | DYNAMIC, +CHARACTERISTIC, GAP_APPEARANCE, READ | WRITE | DYNAMIC, +// GAP Peripheral Privacy Flag +CHARACTERISTIC, 2A02, READ | WRITE | DYNAMIC, 00 +// GAP Reconnection Address - 6 bytes +CHARACTERISTIC, 2A03, READ | WRITE | DYNAMIC, +// GAP Peripheral Preferred Connection Parameters +CHARACTERISTIC, 2A04, READ | WRITE | DYNAMIC, + +PRIMARY_SERVICE, GATT_SERVICE +CHARACTERISTIC, GATT_SERVICE_CHANGED, READ, + +SECONDARY_SERVICE, 0000FF10-0000-1000-8000-00805F9B34FB +CHARACTERISTIC, FF10, READ | WRITE | DYNAMIC, + +SECONDARY_SERVICE, 0000FF11-0000-1000-8000-00805F9B34FB +CHARACTERISTIC, FF11, READ | WRITE | DYNAMIC, + +// +PRIMARY_SERVICE, FFFF +CHARACTERISTIC, FFFD, READ | WRITE | DYNAMIC, +CHARACTERISTIC, FFFE, READ | WRITE | DYNAMIC, +// +PRIMARY_SERVICE, FFFF +CHARACTERISTIC, FFFD, READ | WRITE | DYNAMIC, +CHARACTERISTIC, FFFE, READ | WRITE | DYNAMIC, +// +// SECONDARY_SERVICE, FFFA +// CHARACTERISTIC, FFFB, READ | WRITE | DYNAMIC, +// +// SECONDARY_SERVICE, FFF9 +// INCLUDE_SERVICE, FFFA +// +// SECONDARY_SERVICE, FFF7 +// INCLUDE_SERVICE, FFF9 +// CHARACTERISTIC, FFF8, READ | WRITE | DYNAMIC, +// +SECONDARY_SERVICE, FFF4 +// INCLUDE_SERVICE, FFF7 +// INCLUDE_SERVICE, FFFC +// INCLUDE_SERVICE, FFFF +CHARACTERISTIC, FFF5, READ | WRITE | DYNAMIC, +CHARACTERISTIC, FFF6, READ | WRITE | DYNAMIC, +// +// PRIMARY_SERVICE, FFF0 +// INCLUDE_SERVICE, FFF4 +// CHARACTERISTIC, FFF1, READ | WRITE | DYNAMIC | AUTHORIZATION_REQUIRED | ENCRYPTION_KEY_SIZE_7 | AUTHENTICATION_REQUIRED, +// CHARACTERISTIC, FFF2, READ | WRITE | DYNAMIC, +// CHARACTERISTIC, FFF3, READ | INDICATE, +// CHARACTERISTIC, 00001234-0000-1000-8000-00805F9B34FB, READ | WRITE | DYNAMIC, + +// Primary Service with 16-bit UUID, included service +PRIMARY_SERVICE, F000 +INCLUDE_SERVICE, FFF4 +// Characteristics 16 and 128 bit with different authoriztion/authentication/encryption requirements and read/write flags +// - no requirements +CHARACTERISTIC, F100, READ | WRITE | DYNAMIC | NOTIFY | INDICATE | RELIABLE_WRITE, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC, +CHARACTERISTIC, 0000F101-0000-1000-8000-00805F9B34FB, READ | WRITE | DYNAMIC | NOTIFY | INDICATE | RELIABLE_WRITE, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC, +// - neither read nor writable +CHARACTERISTIC, F102, DYNAMIC, +CHARACTERISTIC_USER_DESCRIPTION, DYNAMIC, +CHARACTERISTIC, 0000F103-0000-1000-8000-00805F9B34FB, DYNAMIC, +CHARACTERISTIC_USER_DESCRIPTION, DYNAMIC, +// - authorization required +CHARACTERISTIC, F104, READ | WRITE | DYNAMIC | AUTHORIZATION_REQUIRED, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC | AUTHORIZATION_REQUIRED, +CHARACTERISTIC, 0000F105-0000-1000-8000-00805F9B34FB, READ | WRITE | DYNAMIC | AUTHORIZATION_REQUIRED, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC | AUTHORIZATION_REQUIRED, +// - authenthication required +CHARACTERISTIC, F106, READ | WRITE | DYNAMIC | AUTHENTICATION_REQUIRED, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC | AUTHENTICATION_REQUIRED, +CHARACTERISTIC, 0000F107-0000-1000-8000-00805F9B34FB, READ | WRITE | DYNAMIC | AUTHENTICATION_REQUIRED, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC | AUTHENTICATION_REQUIRED, +// - encryptiont with 128 bit key required +CHARACTERISTIC, F108, READ | WRITE | DYNAMIC | ENCRYPTION_KEY_SIZE_16, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC | ENCRYPTION_KEY_SIZE_16, +CHARACTERISTIC, 0000F109-0000-1000-8000-00805F9B34FB, READ | WRITE | DYNAMIC | ENCRYPTION_KEY_SIZE_16, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC | ENCRYPTION_KEY_SIZE_16, +// - write only +CHARACTERISTIC, F10A, WRITE | DYNAMIC, + +// - read/write, incl. server characteristic configuration, characteristic format (uint8_t, exponent = 0, unitless, Bluetooth SIG, and characteristic aggregate format +CHARACTERISTIC, F10B, READ | WRITE | DYNAMIC, +SERVER_CHARACTERISTIC_CONFIGURATION, READ | WRITE | DYNAMIC, +CHARACTERISTIC_FORMAT, 1, 04, 0, 2700, 1, 0000 +CHARACTERISTIC_FORMAT, 2, 0c, 0, 2700, 1, 0000 +CHARACTERISTIC_AGGREGATE_FORMAT, 1, 2 + +// - read/write/write without response +CHARACTERISTIC, F10D, READ | WRITE | WRITE_WITHOUT_RESPONSE | DYNAMIC, +// - read/write without response +CHARACTERISTIC, F10C, READ | WRITE_WITHOUT_RESPONSE | DYNAMIC, +// - read/authenticated signed writes +CHARACTERISTIC, F10E, READ | WRITE | AUTHENTICATED_SIGNED_WRITE | DYNAMIC, + +// Primary Service with 128-bit UUID, included service +PRIMARY_SERVICE, 0000F001-0000-1000-8000-00805F9B34FB +INCLUDE_SERVICE, 0000FF10-0000-1000-8000-00805F9B34FB +INCLUDE_SERVICE, 0000FF11-0000-1000-8000-00805F9B34FB +// Characteristics 16 and 128 bit with different authoriztion/authentication/encryption requirements and read/write flags +// - no requirements +CHARACTERISTIC, F200, READ | WRITE | DYNAMIC | NOTIFY | INDICATE | RELIABLE_WRITE | WRITE_WITHOUT_RESPONSE, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC, +CHARACTERISTIC, 0000F201-0000-1000-8000-00805F9B34FB, READ | WRITE | DYNAMIC | NOTIFY | INDICATE | RELIABLE_WRITE, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC, +// - neither read nor writable +CHARACTERISTIC, F202, DYNAMIC, +CHARACTERISTIC_USER_DESCRIPTION, DYNAMIC, +CHARACTERISTIC, 0000F203-0000-1000-8000-00805F9B34FB, DYNAMIC, +CHARACTERISTIC_USER_DESCRIPTION, DYNAMIC, +// - authorization required +CHARACTERISTIC, F204, READ | WRITE | DYNAMIC | AUTHORIZATION_REQUIRED, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC | AUTHORIZATION_REQUIRED, +CHARACTERISTIC, 0000F205-0000-1000-8000-00805F9B34FB, READ | WRITE | DYNAMIC | AUTHORIZATION_REQUIRED, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC | AUTHORIZATION_REQUIRED, +// - authenthication required +CHARACTERISTIC, F206, READ | WRITE | DYNAMIC | AUTHENTICATION_REQUIRED, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC | AUTHENTICATION_REQUIRED, +CHARACTERISTIC, 0000F207-0000-1000-8000-00805F9B34FB, READ | WRITE | DYNAMIC | AUTHENTICATION_REQUIRED, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC | AUTHENTICATION_REQUIRED, +// - encryptiont with 128 bit key required +CHARACTERISTIC, F208, READ | WRITE | DYNAMIC | ENCRYPTION_KEY_SIZE_16, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC | ENCRYPTION_KEY_SIZE_16, +CHARACTERISTIC, 0000F209-0000-1000-8000-00805F9B34FB, READ | WRITE | DYNAMIC | ENCRYPTION_KEY_SIZE_16, +CHARACTERISTIC_USER_DESCRIPTION, READ | WRITE | DYNAMIC | ENCRYPTION_KEY_SIZE_16, + +