From 8a3fac4ff5a69d65e2e5f2b915c5b5f4f92f95a1 Mon Sep 17 00:00:00 2001 From: "matthias.ringwald@gmail.com" Date: Sun, 12 Jan 2014 21:24:25 +0000 Subject: [PATCH] start timer on handle value indication, assert only on indication is active, notify client about handle value indication completion, extend ble_peripheral --- ble/att.h | 14 ++++++---- ble/att_server.c | 48 ++++++++++++++++++++++++++++++--- ble/att_server.h | 6 +++-- example/libusb/ble_peripheral.c | 26 +++++++++++++++--- include/btstack/hci_cmds.h | 8 ++++++ 5 files changed, 89 insertions(+), 13 deletions(-) diff --git a/ble/att.h b/ble/att.h index 6c283681f..00f36fe0b 100644 --- a/ble/att.h +++ b/ble/att.h @@ -73,8 +73,8 @@ extern "C" { #define ATT_EXECUTE_WRITE_RESPONSE 0x19 #define ATT_HANDLE_VALUE_NOTIFICATION 0x1b -#define ATT_HANDLE_VALUE_CONFIRMATION 0x1c #define ATT_HANDLE_VALUE_INDICATION 0x1d +#define ATT_HANDLE_VALUE_CONFIRMATION 0x1e #define ATT_WRITE_COMMAND 0x52 @@ -120,6 +120,14 @@ extern "C" { #define ATT_PROPERTY_AUTHORIZATION_REQUIRED 0x800 // Encryption key size stored in upper 4 bits, 0 == no encryption, encryption key size - 1 otherwise +// ATT Transaxtion Timeout of 30 seconds for Command/Response or Incidationc/Confirmation +#define ATT_TRANSACTION_TIMEOUT_MS 30000 + +#define ATT_TRANSACTION_MODE_NONE 0x0 +#define ATT_TRANSACTION_MODE_ACTIVE 0x1 +#define ATT_TRANSACTION_MODE_EXECUTE 0x2 +#define ATT_TRANSACTION_MODE_CANCEL 0x3 + // MARK: GATT UUIDs #define GATT_PRIMARY_SERVICE_UUID 0x2800 #define GATT_SECONDARY_SERVICE_UUID 0x2801 @@ -129,10 +137,6 @@ extern "C" { #define GAP_SERVICE_UUID 0x1800 #define GAP_DEVICE_NAME_UUID 0x2a00 -#define ATT_TRANSACTION_MODE_NONE 0x0 -#define ATT_TRANSACTION_MODE_ACTIVE 0x1 -#define ATT_TRANSACTION_MODE_EXECUTE 0x2 -#define ATT_TRANSACTION_MODE_CANCEL 0x3 typedef struct att_connection { uint16_t mtu; diff --git a/ble/att_server.c b/ble/att_server.c index b1972f7a0..053d93195 100644 --- a/ble/att_server.c +++ b/ble/att_server.c @@ -81,8 +81,31 @@ static uint8_t att_request_buffer[28]; static int att_ir_central_device_db_index = -1; static int att_ir_lookup_active = 0; +static int att_handle_value_indication_handle = 0; +static timer_source_t att_handle_value_indication_timer; + static btstack_packet_handler_t att_client_packet_handler = NULL; + +static int att_handle_value_indication_notify_client(uint8_t status, uint16_t client_handle, uint16_t attribute_handle){ + uint8_t event[7]; + int pos = 0; + event[pos++] = ATT_HANDLE_VALUE_INDICATION_COMPLETE; + event[pos++] = sizeof(event) - 2; + event[pos++] = status; + bt_store_16(event, pos, client_handle); + pos += 2; + bt_store_16(event, pos, attribute_handle); + pos += 2; + (*att_client_packet_handler)(HCI_EVENT_PACKET, 0, &event[0], sizeof(event)); +} + +static void att_handle_value_indication_timeout(timer_source_t *ts){ + uint16_t att_handle = att_handle_value_indication_handle; + att_handle_value_indication_handle = 0; + att_handle_value_indication_notify_client(ATT_HANDLE_VALUE_INDICATION_TIMEOUT, att_request_handle, att_handle); +} + static void att_event_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ switch (packet_type) { @@ -255,6 +278,15 @@ static void att_run(void){ static void att_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size){ if (packet_type != ATT_DATA_PACKET) return; + // handle value indication confirms + if (packet[0] == ATT_HANDLE_VALUE_CONFIRMATION && att_handle_value_indication_handle){ + run_loop_remove_timer(&att_handle_value_indication_timer); + uint16_t att_handle = att_handle_value_indication_handle; + att_handle_value_indication_handle = 0; + att_handle_value_indication_notify_client(0, att_request_handle, att_handle); + return; + } + // check size if (size > sizeof(att_request_buffer)) return; @@ -292,14 +324,24 @@ int att_server_can_send(){ return hci_can_send_packet_now(HCI_ACL_DATA_PACKET); } -void att_server_notify(uint16_t handle, uint8_t *value, uint16_t value_len){ +int att_server_notify(uint16_t handle, uint8_t *value, uint16_t value_len){ uint8_t packet_buffer[att_connection.mtu]; uint16_t size = att_prepare_handle_value_notification(&att_connection, handle, value, value_len, packet_buffer); - l2cap_send_connectionless(att_request_handle, L2CAP_CID_ATTRIBUTE_PROTOCOL, packet_buffer, size); + return l2cap_send_connectionless(att_request_handle, L2CAP_CID_ATTRIBUTE_PROTOCOL, packet_buffer, size); } -void att_server_indicate(uint16_t handle, uint8_t *value, uint16_t value_len){ +int att_server_indicate(uint16_t handle, uint8_t *value, uint16_t value_len){ + if (att_handle_value_indication_handle) return ATT_HANDLE_VALUE_INDICATION_IN_PORGRESS; + if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)) return BTSTACK_ACL_BUFFERS_FULL; + + // track indication + att_handle_value_indication_handle = handle; + run_loop_set_timer_handler(&att_handle_value_indication_timer, att_handle_value_indication_timeout); + run_loop_set_timer(&att_handle_value_indication_timer, ATT_TRANSACTION_TIMEOUT_MS); + run_loop_add_timer(&att_handle_value_indication_timer); + uint8_t packet_buffer[att_connection.mtu]; uint16_t size = att_prepare_handle_value_indication(&att_connection, handle, value, value_len, packet_buffer); l2cap_send_connectionless(att_request_handle, L2CAP_CID_ATTRIBUTE_PROTOCOL, packet_buffer, size); + return 0; } diff --git a/ble/att_server.h b/ble/att_server.h index b9ae9acac..bd971d316 100644 --- a/ble/att_server.h +++ b/ble/att_server.h @@ -65,13 +65,15 @@ int att_server_can_send(); /* * @brief notify client about attribute value change + * @ereturns 0 if ok, error otherwise */ -void att_server_notify(uint16_t handle, uint8_t *value, uint16_t value_len); +int att_server_notify(uint16_t handle, uint8_t *value, uint16_t value_len); /* * @brief indicate value change to client. client is supposed to reply with an indication_response + * @ereturns 0 if ok, error otherwise */ -void att_server_indicate(uint16_t handle, uint8_t *value, uint16_t value_len); +int att_server_indicate(uint16_t handle, uint8_t *value, uint16_t value_len); #if defined __cplusplus } diff --git a/example/libusb/ble_peripheral.c b/example/libusb/ble_peripheral.c index 851a32ad2..481889286 100644 --- a/example/libusb/ble_peripheral.c +++ b/example/libusb/ble_peripheral.c @@ -83,11 +83,27 @@ static void heartbeat_handler(struct timer *ts){ } static void app_run(){ - if (!client_configuration || !update_client) return; + if (!update_client) return; if (!att_server_can_send()) return; - printf("Notify value %u\n", counter); + + int result = -1; + switch (client_configuration){ + case 0x01: + printf("Notify value %u\n", counter); + result = att_server_notify(0x0f, &counter, 1); + break; + case 0x02: + printf("Indicate value %u\n", counter); + result = att_server_indicate(0x0f, &counter, 1); + break; + default: + return; + } + if (result){ + printf("Error 0x%02x\n", result); + return; + } update_client = 0; - att_server_notify(0x0f, &counter, 1); } // write requests @@ -177,6 +193,10 @@ static void app_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t * sm_authorization_grant(event->addr_type, event->address); break; } + case ATT_HANDLE_VALUE_INDICATION_COMPLETE: + printf("ATT_HANDLE_VALUE_INDICATION_COMPLETE status %u\n", packet[2]); + break; + default: break; } diff --git a/include/btstack/hci_cmds.h b/include/btstack/hci_cmds.h index 66f124d88..c0a1fb8e8 100644 --- a/include/btstack/hci_cmds.h +++ b/include/btstack/hci_cmds.h @@ -228,6 +228,9 @@ extern "C" { #define GATT_CHARACTERISTIC_QUERY_RESULT 0xA4 #define GATT_CHARACTERISTIC_QUERY_COMPLETE 0xA5 +// data: event(8), len(8), status (8), hci_handle (16), attribute_handle (16) +#define ATT_HANDLE_VALUE_INDICATION_COMPLETE 0xAF + // data: event(8), address_type(8), address (48), [number(32)] #define SM_JUST_WORKS_REQUEST 0xb0 #define SM_JUST_WORKS_CANCEL 0xb1 @@ -241,6 +244,8 @@ extern "C" { #define SM_AUTHORIZATION_REQUEST 0xb9 #define SM_AUTHORIZATION_RESULT 0xba +// ATT + // last error code in 2.1 is 0x38 - we start with 0x50 for BTstack errors #define BTSTACK_CONNECTION_TO_BTDAEMON_FAILED 0x50 @@ -278,6 +283,9 @@ extern "C" { #define SDP_HANDLE_ALREADY_REGISTERED 0x80 #define SDP_QUERY_INCOMPLETE 0x81 +#define ATT_HANDLE_VALUE_INDICATION_IN_PORGRESS 0x90 +#define ATT_HANDLE_VALUE_INDICATION_TIMEOUT 0x91 + /** * Default INQ Mode */