From 8ac574d6faacb291043ffa9b8cfaefc28781cf8c Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 31 Oct 2016 21:22:16 +0100 Subject: [PATCH] att_server: allow to register handler for handle range - needed for modular service implmeentation --- src/ble/att_db.c | 128 ++++++++++++++++++++++++++--------------------- src/ble/att_db.h | 24 +++++++-- 2 files changed, 93 insertions(+), 59 deletions(-) diff --git a/src/ble/att_db.c b/src/ble/att_db.c index fe1465768..29fbd5ec8 100644 --- a/src/ble/att_db.c +++ b/src/ble/att_db.c @@ -39,9 +39,9 @@ #include #include -#include "bluetooth.h" #include "ble/att_db.h" #include "ble/core.h" +#include "bluetooth.h" #include "btstack_debug.h" #include "btstack_util.h" @@ -69,6 +69,8 @@ static att_write_callback_t att_write_callback = NULL; static uint8_t att_prepare_write_error_code = 0; static uint16_t att_prepare_write_error_handle = 0x0000; +static btstack_linked_list_t service_handlers; + // new java-style iterator typedef struct att_iterator { // private @@ -152,6 +154,30 @@ static int att_find_handle(att_iterator_t *it, uint16_t handle){ return 0; } +static att_service_handler_t * att_service_handler_for_handle(uint16_t handle){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, &service_handlers); + while (btstack_linked_list_iterator_has_next(&it)){ + att_service_handler_t * handler = (att_service_handler_t*) btstack_linked_list_iterator_next(&it); + if (handler->start_handle > handle) continue; + if (handler->end_handle < handle) continue; + return handler; + } + return NULL; +} + +static att_read_callback_t att_read_callback_for_handle(uint16_t handle){ + att_service_handler_t * handler = att_service_handler_for_handle(handle); + if (handler) return handler->read_callback; + return att_read_callback; +} + +static att_write_callback_t att_write_callback_for_handle(uint16_t handle){ + att_service_handler_t * handler = att_service_handler_for_handle(handle); + if (handler) return handler->write_callback; + return att_write_callback; +} + // experimental client API uint16_t att_uuid_for_handle(uint16_t attribute_handle){ att_iterator_t it; @@ -163,8 +189,10 @@ uint16_t att_uuid_for_handle(uint16_t attribute_handle){ // end of client API static void att_update_value_len(att_iterator_t *it, hci_con_handle_t con_handle){ - if ((it->flags & ATT_PROPERTY_DYNAMIC) == 0 || !att_read_callback) return; - it->value_len = (*att_read_callback)(con_handle, it->handle, 0, NULL, 0); + if ((it->flags & ATT_PROPERTY_DYNAMIC) == 0) return; + att_read_callback_t callback = att_read_callback_for_handle(it->handle); + if (!callback) return; + it->value_len = (*callback)(con_handle, it->handle, 0, NULL, 0); return; } @@ -172,8 +200,10 @@ static void att_update_value_len(att_iterator_t *it, hci_con_handle_t con_handle static int att_copy_value(att_iterator_t *it, uint16_t offset, uint8_t * buffer, uint16_t buffer_size, hci_con_handle_t con_handle){ // DYNAMIC - if ((it->flags & ATT_PROPERTY_DYNAMIC) && att_read_callback) { - return (*att_read_callback)(con_handle, it->handle, offset, buffer, buffer_size); + if (it->flags & ATT_PROPERTY_DYNAMIC){ + att_read_callback_t callback = att_read_callback_for_handle(it->handle); + if (!callback) return 0; + return (*callback)(con_handle, it->handle, offset, buffer, buffer_size); } // STATIC @@ -854,7 +884,8 @@ static uint16_t handle_write_request(att_connection_t * att_connection, uint8_t if (!ok) { return setup_error_invalid_handle(response_buffer, request_type, handle); } - if (!att_write_callback) { + att_write_callback_t callback = att_write_callback_for_handle(handle); + if (!callback) { return setup_error_write_not_permitted(response_buffer, request_type, handle); } if ((it.flags & ATT_PROPERTY_WRITE) == 0) { @@ -868,7 +899,7 @@ static uint16_t handle_write_request(att_connection_t * att_connection, uint8_t if (error_code) { return setup_error(response_buffer, request_type, handle, error_code); } - error_code = (*att_write_callback)(att_connection->con_handle, handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3); + error_code = (*callback)(att_connection->con_handle, handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3); if (error_code) { return setup_error(response_buffer, request_type, handle, error_code); } @@ -885,7 +916,8 @@ static uint16_t handle_prepare_write_request(att_connection_t * att_connection, uint16_t handle = little_endian_read_16(request_buffer, 1); uint16_t offset = little_endian_read_16(request_buffer, 3); - if (!att_write_callback) { + att_write_callback_t callback = att_write_callback_for_handle(handle); + if (!callback) { return setup_error_write_not_permitted(response_buffer, request_type, handle); } att_iterator_t it; @@ -905,7 +937,7 @@ static uint16_t handle_prepare_write_request(att_connection_t * att_connection, return setup_error(response_buffer, request_type, handle, error_code); } - error_code = (*att_write_callback)(att_connection->con_handle, handle, ATT_TRANSACTION_MODE_ACTIVE, offset, request_buffer + 5, request_len - 5); + error_code = (*callback)(att_connection->con_handle, handle, ATT_TRANSACTION_MODE_ACTIVE, offset, request_buffer + 5, request_len - 5); switch (error_code){ case 0: break; @@ -924,12 +956,24 @@ static uint16_t handle_prepare_write_request(att_connection_t * att_connection, return request_len; } +static void att_notify_write_callbacks(att_connection_t * att_connection, uint16_t transaction_mode){ + // notify all + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, &service_handlers); + while (btstack_linked_list_iterator_has_next(&it)){ + att_service_handler_t * handler = (att_service_handler_t*) btstack_linked_list_iterator_next(&it); + if (!handler->write_callback) continue; + (*handler->write_callback)(att_connection->con_handle, 0, transaction_mode, 0, NULL, 0); + } + if (!att_write_callback) return; + (*att_write_callback)(att_connection->con_handle, 0, transaction_mode, 0, NULL, 0); +} + /* * @brief transcation queue of prepared writes, e.g., after disconnect */ void att_clear_transaction_queue(att_connection_t * att_connection){ - if (!att_write_callback) return; - (*att_write_callback)(att_connection->con_handle, 0, ATT_TRANSACTION_MODE_CANCEL, 0, NULL, 0); + att_notify_write_callbacks(att_connection, ATT_TRANSACTION_MODE_CANCEL); } // MARK: ATT_EXECUTE_WRITE_REQUEST 0x18 @@ -938,10 +982,6 @@ static uint16_t handle_execute_write_request(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size){ uint8_t request_type = ATT_EXECUTE_WRITE_REQUEST; - - if (!att_write_callback) { - return setup_error_write_not_permitted(response_buffer, request_type, 0); - } if (request_buffer[1]) { // deliver queued errors if (att_prepare_write_error_code){ @@ -951,7 +991,7 @@ static uint16_t handle_execute_write_request(att_connection_t * att_connection, att_prepare_write_reset(); return setup_error(response_buffer, request_type, handle, error_code); } - (*att_write_callback)(att_connection->con_handle, 0, ATT_TRANSACTION_MODE_EXECUTE, 0, NULL, 0); + att_notify_write_callbacks(att_connection, ATT_TRANSACTION_MODE_EXECUTE); } else { att_clear_transaction_queue(att_connection); } @@ -965,15 +1005,17 @@ static uint16_t handle_execute_write_request(att_connection_t * att_connection, static void handle_write_command(att_connection_t * att_connection, uint8_t * request_buffer, uint16_t request_len, uint8_t * response_buffer, uint16_t response_buffer_size){ - if (!att_write_callback) return; uint16_t handle = little_endian_read_16(request_buffer, 1); + att_write_callback_t callback = att_write_callback_for_handle(handle); + if (!callback) return; + att_iterator_t it; int ok = att_find_handle(&it, handle); if (!ok) return; if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) return; if ((it.flags & ATT_PROPERTY_WRITE_WITHOUT_RESPONSE) == 0) return; if (att_validate_security(att_connection, &it)) return; - (*att_write_callback)(att_connection->con_handle, handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3); + (*callback)(att_connection->con_handle, handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3); } // MARK: helper for ATT_HANDLE_VALUE_NOTIFICATION and ATT_HANDLE_VALUE_INDICATION @@ -1068,42 +1110,16 @@ uint16_t att_handle_request(att_connection_t * att_connection, return response_len; } -#if 0 - -// test profile -#include "profile.h" - -int main(void){ - int acl_buffer_size; - uint8_t acl_buffer[27]; - att_set_db(profile_data); - att_dump_attributes(); - - uint8_t uuid_1[] = { 0x00, 0x18}; - acl_buffer_size = handle_find_by_type_value_request2(acl_buffer, 19, 0, 0xffff, 0x2800, 2, (uint8_t *) &uuid_1); - log_info_hexdump(acl_buffer, acl_buffer_size); - - uint8_t uuid_3[] = { 0x00, 0x2a}; - acl_buffer_size = handle_read_by_type_request2(acl_buffer, 19, 0, 0xffff, 2, (uint8_t *) &uuid_3); - log_info_hexdump(acl_buffer, acl_buffer_size); - - acl_buffer_size = handle_find_by_type_value_request2(acl_buffer, 19, 0, 0xffff, 0x2800, 2, (uint8_t *) &uuid_1); - log_info_hexdump(acl_buffer, acl_buffer_size); - - uint8_t uuid_4[] = { 0x00, 0x28}; - acl_buffer_size = handle_read_by_group_type_request2(acl_buffer, 20, 0, 0xffff, 2, (uint8_t *) &uuid_4); - log_info_hexdump(acl_buffer, acl_buffer_size); - - acl_buffer_size = handle_find_information_request2(acl_buffer, 20, 0, 0xffff); - log_info_hexdump(acl_buffer, acl_buffer_size); - acl_buffer_size = handle_find_information_request2(acl_buffer, 20, 3, 0xffff); - log_info_hexdump(acl_buffer, acl_buffer_size); - acl_buffer_size = handle_find_information_request2(acl_buffer, 20, 5, 0xffff); - log_info_hexdump(acl_buffer, acl_buffer_size); - - acl_buffer_size = handle_read_request2(acl_buffer, 19, 0x0003); - log_info_hexdump(acl_buffer, acl_buffer_size); - - return 0; +/** + * @brief register read/write callbacks for specific handle range + * @param att_service_handler_t + */ +void att_register_service_handler(att_service_handler_t * handler){ + if (att_service_handler_for_handle(handler->start_handle) || + att_service_handler_for_handle(handler->end_handle)){ + log_error("att_register_service_handler: handler for range 0x%04x-0x%04x already registered", handler->start_handle, handler->end_handle); + return; + } + btstack_linked_list_add(&service_handlers, (btstack_linked_item_t*) handler); } -#endif + diff --git a/src/ble/att_db.h b/src/ble/att_db.h index 2f57a00c7..3f32b1ce6 100644 --- a/src/ble/att_db.h +++ b/src/ble/att_db.h @@ -41,7 +41,8 @@ #include #include "bluetooth.h" - +#include "btstack_linked_list.h" + #if defined __cplusplus extern "C" { #endif @@ -49,7 +50,7 @@ extern "C" { // custom BTstack error codes #define ATT_ERROR_HCI_DISCONNECT_RECEIVED 0x1f -// custom BTstack ATT error coders +// custom BTstack ATT error codes #define ATT_ERROR_DATA_MISMATCH 0x7e #define ATT_ERROR_TIMEOUT 0x7F @@ -83,6 +84,16 @@ typedef uint16_t (*att_read_callback_t)(hci_con_handle_t con_handle, uint16_t at // @returns 0 if write was ok, ATT_ERROR_PREPARE_QUEUE_FULL if no space in queue, ATT_ERROR_INVALID_OFFSET if offset is larger than max buffer typedef int (*att_write_callback_t)(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size); + +// Read & Write Callbacks for handle range +typedef struct att_service_handler { + btstack_linked_item_t * item; + uint16_t start_handle; + uint16_t end_handle; + att_read_callback_t read_callback; + att_write_callback_t write_callback; +} att_service_handler_t; + // MARK: ATT Operations /* @@ -152,7 +163,14 @@ uint16_t att_prepare_handle_value_indication(att_connection_t * att_connection, */ void att_clear_transaction_queue(att_connection_t * att_connection); -// experimental client API +/** + * @brief register read/write callbacks for specific handle range + * @param att_service_handler_t + */ +void att_register_service_handler(att_service_handler_t * handler); + + + // experimental client API uint16_t att_uuid_for_handle(uint16_t attribute_handle); #if defined __cplusplus