From 7c40ac80c04a837be6ff465851ddc96576b95bee Mon Sep 17 00:00:00 2001 From: "mila@ringwald.ch" Date: Thu, 2 Oct 2014 20:06:29 +0000 Subject: [PATCH] rewrote gatt_client state handling: API uses handles instaed of context structs (adapted examples and daemon) --- ble/gatt_client.c | 375 ++++++++++++++-------- ble/gatt_client.h | 81 ++--- example/libusb/ancs_client_lib.c | 22 +- example/libusb/gatt_battery_query.c | 15 +- example/libusb/gatt_browser.c | 16 +- platforms/posix/src/daemon.c | 475 +++++++++++++++++++--------- 6 files changed, 619 insertions(+), 365 deletions(-) diff --git a/ble/gatt_client.c b/ble/gatt_client.c index 1a593d484..04b5a5865 100644 --- a/ble/gatt_client.c +++ b/ble/gatt_client.c @@ -58,9 +58,41 @@ static linked_list_t gatt_client_connections = NULL; static uint16_t att_client_start_handle = 0x0001; static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size); +static void gatt_client_report_error_if_pending(gatt_client_t *peripheral, uint8_t error_code); void (*gatt_client_callback)(le_event_t * event); +static gatt_client_t * gatt_client_for_timer(timer_source_t * ts){ + linked_list_iterator_t it; + linked_list_iterator_init(&it, &gatt_client_connections); + while (linked_list_iterator_has_next(&it)){ + gatt_client_t * peripheral = (gatt_client_t *) linked_list_iterator_next(&it); + if ( &peripheral->gc_timeout == ts) { + return peripheral; + } + } + return NULL; +} + +static void gatt_client_timeout_handler(timer_source_t * timer){ + gatt_client_t * peripheral = gatt_client_for_timer(timer); + if (!peripheral) return; + log_info("GATT client timeout handle, handle 0x%02x", peripheral->handle); + gatt_client_report_error_if_pending(peripheral, ATT_ERROR_TIMEOUT); +} + +static void gatt_client_timeout_start(gatt_client_t * peripheral){ + log_info("GATT client timeout start, handle 0x%02x", peripheral->handle); + run_loop_remove_timer(&peripheral->gc_timeout); + run_loop_set_timer_handler(&peripheral->gc_timeout, gatt_client_timeout_handler); + run_loop_set_timer(&peripheral->gc_timeout, 30000); // 30 seconds sm timeout + run_loop_add_timer(&peripheral->gc_timeout); +} + +static void gatt_client_timeout_stop(gatt_client_t * peripheral){ + log_info("GATT client timeout stop, handle 0x%02x", peripheral->handle); + run_loop_remove_timer(&peripheral->gc_timeout); +} void gatt_client_init(){ att_client_start_handle = 0x0000; @@ -77,24 +109,46 @@ void gatt_client_register_packet_handler(void (*gatt_callback)(le_event_t* event gatt_client_callback = gatt_callback; } -// gatt client API -// start/stop gatt client -void gatt_client_start(gatt_client_t *context, uint16_t handle){ - memset(context, 0, sizeof(gatt_client_t)); - context->handle = handle; +static gatt_client_t * get_gatt_client_context_for_handle(uint16_t handle){ + linked_item_t *it; + for (it = (linked_item_t *) gatt_client_connections; it ; it = it->next){ + gatt_client_t * peripheral = (gatt_client_t *) it; + if (peripheral->handle == handle){ + return peripheral; + } + } + return NULL; +} + + +// @returns context +// returns existing one, or tries to setup new one +static gatt_client_t * provide_context_for_conn_handle(uint16_t con_handle){ + gatt_client_t * context = get_gatt_client_context_for_handle(con_handle); + if (context) return context; + + context = btstack_memory_gatt_client_get(); + if (!context) return NULL; + // init state + context->handle = con_handle; context->mtu_state = SEND_MTU_EXCHANGE; + context->gatt_client_state = P_READY; + gatt_client_timeout_start(context); linked_list_add(&gatt_client_connections, (linked_item_t*)context); + return context; } -void gatt_client_stop(gatt_client_t *context){ - linked_list_remove(&gatt_client_connections, (linked_item_t*) context); -} - -int gatt_client_is_ready(gatt_client_t *context){ +static int is_ready(gatt_client_t * context){ return context->gatt_client_state == P_READY; } +int gatt_client_is_ready(uint16_t handle){ + gatt_client_t * context = provide_context_for_conn_handle(handle); + if (!context) return 0; + return is_ready(context); +} + // precondition: can_send_packet_now == TRUE static void att_confirmation(uint16_t peripheral_handle){ l2cap_reserve_packet_buffer(); @@ -303,10 +357,16 @@ static uint16_t get_last_result_handle(uint8_t * packet, uint16_t size){ return READ_BT_16(packet, size - attr_length + handle_offset); } -static inline void send_gatt_complete_event(gatt_client_t * peripheral, uint8_t status){ +static void gatt_client_handle_transaction_complete(gatt_client_t * peripheral){ + peripheral->gatt_client_state = P_READY; + gatt_client_timeout_stop(peripheral); +} + +static inline void emit_gatt_complete_event(gatt_client_t * peripheral, uint8_t status){ gatt_complete_event_t event; event.type = GATT_QUERY_COMPLETE; - event.client = peripheral; + event.handle = peripheral->handle; + event.attribute_handle = peripheral->attribute_handle; event.status = status; (*gatt_client_callback)((le_event_t*)&event); } @@ -318,7 +378,7 @@ static void report_gatt_services(gatt_client_t * peripheral, uint8_t * packet, le_service_t service; le_service_event_t event; event.type = GATT_SERVICE_QUERY_RESULT; - event.client = peripheral; + event.handle = peripheral->handle; int i; for (i = 2; i < size; i += attr_length){ @@ -370,7 +430,7 @@ static void characteristic_end_found(gatt_client_t * peripheral, uint16_t end_ha le_characteristic_event_t event; event.type = GATT_CHARACTERISTIC_QUERY_RESULT; - event.client = peripheral; + event.handle = peripheral->handle; event.characteristic.start_handle = peripheral->characteristic_start_handle; event.characteristic.value_handle = peripheral->attribute_handle; event.characteristic.end_handle = end_handle; @@ -412,15 +472,15 @@ static void report_gatt_included_service(gatt_client_t * peripheral, uint8_t *uu le_service_event_t event; event.type = GATT_INCLUDED_SERVICE_QUERY_RESULT; event.service = service; - event.client = peripheral; + event.handle = peripheral->handle; (*gatt_client_callback)((le_event_t*)&event); } -static void send_characteristic_value_event(gatt_client_t * peripheral, uint16_t handle, uint8_t * value, uint16_t length, uint16_t offset, uint8_t event_type){ +static void send_characteristic_value_event(gatt_client_t * peripheral, uint16_t value_handle, uint8_t * value, uint16_t length, uint16_t offset, uint8_t event_type){ le_characteristic_value_event_t event; event.type = event_type; - event.client = peripheral; - event.value_handle = handle; + event.handle = peripheral->handle; + event.value_handle = value_handle; event.value_offset = offset; event.blob_length = length; event.blob = value; @@ -446,7 +506,7 @@ static void report_gatt_characteristic_value(gatt_client_t * peripheral, uint16_ static void report_gatt_characteristic_descriptor(gatt_client_t * peripheral, uint16_t handle, uint8_t *value, uint16_t value_length, uint16_t value_offset, uint8_t event_type){ le_characteristic_descriptor_event_t event; event.type = event_type; - event.client = peripheral; + event.handle = peripheral->handle; le_characteristic_descriptor_t descriptor; descriptor.handle = handle; @@ -466,7 +526,7 @@ static void report_gatt_all_characteristic_descriptors(gatt_client_t * periphera le_characteristic_descriptor_t descriptor; le_characteristic_descriptor_event_t event; event.type = GATT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT; - event.client = peripheral; + event.handle = peripheral->handle; int i; for (i = 0; igatt_client_state = P_READY; - send_gatt_complete_event(peripheral, 0); + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, 0); } @@ -533,8 +593,8 @@ static inline void trigger_next_blob_query(gatt_client_t * peripheral, gatt_clie uint16_t max_blob_length = peripheral->mtu - 1; if (received_blob_length < max_blob_length){ - peripheral->gatt_client_state = P_READY; - send_gatt_complete_event(peripheral, 0); + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, 0); return; } @@ -552,18 +612,6 @@ static int is_value_valid(gatt_client_t *peripheral, uint8_t *packet, uint16_t s } -gatt_client_t * get_gatt_client_context_for_handle(uint16_t handle){ - linked_item_t *it; - for (it = (linked_item_t *) gatt_client_connections; it ; it = it->next){ - gatt_client_t * peripheral = (gatt_client_t *) it; - if (peripheral->handle == handle){ - return peripheral; - } - } - return NULL; -} - - static void gatt_client_run(){ linked_item_t *it; @@ -604,8 +652,8 @@ static void gatt_client_run(){ case P_W2_SEND_WRITE_CHARACTERISTIC_VALUE: case P_W2_SEND_WRITE_CHARACTERISTIC_DESCRIPTOR: if (peripheral->attribute_length < peripheral->mtu - 3) break; - peripheral->gatt_client_state = P_READY; - send_gatt_complete_event(peripheral, ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH); + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH); return; default: break; @@ -726,20 +774,26 @@ static void gatt_client_run(){ } static void gatt_client_report_error_if_pending(gatt_client_t *peripheral, uint8_t error_code) { - if (gatt_client_is_ready(peripheral)) return; - peripheral->gatt_client_state = P_READY; - send_gatt_complete_event(peripheral, error_code); + if (is_ready(peripheral)) return; + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, error_code); } static void gatt_client_hci_event_packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){ switch (packet[0]) { case HCI_EVENT_DISCONNECTION_COMPLETE: { - uint16_t handle = READ_BT_16(packet,3); - gatt_client_t * peripheral = get_gatt_client_context_for_handle(handle); + log_info("gatt client: HCI_EVENT_DISCONNECTION_COMPLETE gatt_client_hci_event_packet_handler "); + uint16_t con_handle = READ_BT_16(packet,3); + gatt_client_t * peripheral = get_gatt_client_context_for_handle(con_handle); if (!peripheral) break; gatt_client_report_error_if_pending(peripheral, ATT_ERROR_HCI_DISCONNECT_RECEIVED); + linked_list_remove(&gatt_client_connections, (linked_item_t *) peripheral); + btstack_memory_gatt_client_free(peripheral); + + // HACK: forward disconnect event to client, needed by the daemon currently + (*gatt_client_callback)((le_event_t *) packet); break; } default: @@ -841,12 +895,12 @@ static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, break; } case P_W4_READ_CHARACTERISTIC_VALUE_RESULT: - peripheral->gatt_client_state = P_READY; + gatt_client_handle_transaction_complete(peripheral); report_gatt_characteristic_value(peripheral, peripheral->attribute_handle, &packet[1], size-1); break; case P_W4_READ_CHARACTERISTIC_DESCRIPTOR_RESULT:{ - peripheral->gatt_client_state = P_READY; + gatt_client_handle_transaction_complete(peripheral); report_gatt_characteristic_descriptor(peripheral, peripheral->attribute_handle, &packet[1], size-1, 0, GATT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT); break; } @@ -869,7 +923,7 @@ static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, memcpy(service.uuid128, peripheral->uuid128, 16); service.uuid16 = peripheral->uuid16; event.service = service; - event.client = peripheral; + event.handle = peripheral->handle; (*gatt_client_callback)((le_event_t*)&event); } @@ -893,16 +947,16 @@ static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, case ATT_WRITE_RESPONSE: switch (peripheral->gatt_client_state){ case P_W4_WRITE_CHARACTERISTIC_VALUE_RESULT: - peripheral->gatt_client_state = P_READY; - send_gatt_complete_event(peripheral, 0); + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, 0); break; case P_W4_CLIENT_CHARACTERISTIC_CONFIGURATION_RESULT: - peripheral->gatt_client_state = P_READY; - send_gatt_complete_event(peripheral, 0); + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, 0); break; case P_W4_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT: - peripheral->gatt_client_state = P_READY; - send_gatt_complete_event(peripheral, 0); + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, 0); break; default: break; @@ -957,16 +1011,16 @@ static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, case ATT_EXECUTE_WRITE_RESPONSE: switch (peripheral->gatt_client_state){ case P_W4_EXECUTE_PREPARED_WRITE_RESULT: - peripheral->gatt_client_state = P_READY; - send_gatt_complete_event(peripheral, 0); + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, 0); break; case P_W4_CANCEL_PREPARED_WRITE_RESULT: - peripheral->gatt_client_state = P_READY; - send_gatt_complete_event(peripheral, 1); + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, 0); break; case P_W4_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT: - peripheral->gatt_client_state = P_READY; - send_gatt_complete_event(peripheral, 0); + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, 0); break; default: break; @@ -975,6 +1029,7 @@ static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, break; case ATT_ERROR_RESPONSE: + switch (packet[4]){ case ATT_ERROR_ATTRIBUTE_NOT_FOUND: { switch(peripheral->gatt_client_state){ @@ -982,14 +1037,14 @@ static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, case P_W4_SERVICE_WITH_UUID_RESULT: case P_W4_INCLUDED_SERVICE_QUERY_RESULT: case P_W4_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT: - peripheral->gatt_client_state = P_READY; - send_gatt_complete_event(peripheral, 0); + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, 0); break; case P_W4_ALL_CHARACTERISTICS_OF_SERVICE_QUERY_RESULT: case P_W4_CHARACTERISTIC_WITH_UUID_QUERY_RESULT: characteristic_end_found(peripheral, peripheral->end_group_handle); - peripheral->gatt_client_state = P_READY; - send_gatt_complete_event(peripheral, 0); + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, 0); break; default: gatt_client_report_error_if_pending(peripheral, packet[4]); @@ -1016,9 +1071,8 @@ static void att_signed_write_handle_cmac_result(uint8_t hash[8]){ while (linked_list_iterator_has_next(&it)){ gatt_client_t * peripheral = (gatt_client_t *) linked_list_iterator_next(&it); if (peripheral->gatt_client_state == P_W4_CMAC){ - peripheral->gatt_client_state = P_READY; + gatt_client_handle_transaction_complete(peripheral); memcpy(peripheral->cmac, hash, 8); - att_signed_write_request(ATT_SIGNED_WRITE_COMMAND, peripheral->handle, peripheral->attribute_handle, peripheral->attribute_length, peripheral->attribute_value, peripheral->sign_counter, peripheral->cmac); return; } @@ -1026,8 +1080,9 @@ static void att_signed_write_handle_cmac_result(uint8_t hash[8]){ } -le_command_status_t gatt_client_signed_write_without_response(gatt_client_t * peripheral, uint16_t handle, uint16_t message_len, uint8_t * message, sm_key_t csrk, uint32_t sign_counter){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_signed_write_without_response(uint16_t con_handle, uint16_t handle, uint16_t message_len, uint8_t * message, sm_key_t csrk, uint32_t sign_counter){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; if (!sm_cmac_ready()) { log_info("ATT Signed Write, sm_cmac engine not ready. Abort"); return BLE_PERIPHERAL_IN_WRONG_STATE; @@ -1045,8 +1100,11 @@ le_command_status_t gatt_client_signed_write_without_response(gatt_client_t * pe return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_discover_primary_services(gatt_client_t *peripheral){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_discover_primary_services(uint16_t handle){ + gatt_client_t * peripheral = provide_context_for_conn_handle(handle); + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->start_group_handle = 0x0001; peripheral->end_group_handle = 0xffff; peripheral->gatt_client_state = P_W2_SEND_SERVICE_QUERY; @@ -1056,8 +1114,12 @@ le_command_status_t gatt_client_discover_primary_services(gatt_client_t *periphe } -le_command_status_t gatt_client_discover_primary_services_by_uuid16(gatt_client_t *peripheral, uint16_t uuid16){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_discover_primary_services_by_uuid16(uint16_t con_handle, uint16_t uuid16){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->start_group_handle = 0x0001; peripheral->end_group_handle = 0xffff; peripheral->gatt_client_state = P_W2_SEND_SERVICE_WITH_UUID_QUERY; @@ -1067,21 +1129,27 @@ le_command_status_t gatt_client_discover_primary_services_by_uuid16(gatt_client_ return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_discover_primary_services_by_uuid128(gatt_client_t *peripheral, const uint8_t * uuid128){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_discover_primary_services_by_uuid128(uint16_t con_handle, const uint8_t * uuid128){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->start_group_handle = 0x0001; peripheral->end_group_handle = 0xffff; peripheral->uuid16 = 0; memcpy(peripheral->uuid128, uuid128, 16); - peripheral->gatt_client_state = P_W2_SEND_SERVICE_WITH_UUID_QUERY; - gatt_client_run(); return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_discover_characteristics_for_service(gatt_client_t *peripheral, le_service_t *service){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_discover_characteristics_for_service(uint16_t con_handle, le_service_t *service){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->start_group_handle = service->start_group_handle; peripheral->end_group_handle = service->end_group_handle; peripheral->filter_with_uuid = 0; @@ -1091,8 +1159,12 @@ le_command_status_t gatt_client_discover_characteristics_for_service(gatt_client return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_find_included_services_for_service(gatt_client_t *peripheral, le_service_t *service){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_find_included_services_for_service(uint16_t con_handle, le_service_t *service){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->start_group_handle = service->start_group_handle; peripheral->end_group_handle = service->end_group_handle; peripheral->gatt_client_state = P_W2_SEND_INCLUDED_SERVICE_QUERY; @@ -1101,8 +1173,12 @@ le_command_status_t gatt_client_find_included_services_for_service(gatt_client_t return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_discover_characteristics_for_handle_range_by_uuid16(gatt_client_t *peripheral, uint16_t start_handle, uint16_t end_handle, uint16_t uuid16){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_discover_characteristics_for_handle_range_by_uuid16(uint16_t con_handle, uint16_t start_handle, uint16_t end_handle, uint16_t uuid16){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->start_group_handle = start_handle; peripheral->end_group_handle = end_handle; peripheral->filter_with_uuid = 1; @@ -1115,8 +1191,12 @@ le_command_status_t gatt_client_discover_characteristics_for_handle_range_by_uui return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_discover_characteristics_for_handle_range_by_uuid128(gatt_client_t *peripheral, uint16_t start_handle, uint16_t end_handle, uint8_t * uuid128){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_discover_characteristics_for_handle_range_by_uuid128(uint16_t con_handle, uint16_t start_handle, uint16_t end_handle, uint8_t * uuid128){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->start_group_handle = start_handle; peripheral->end_group_handle = end_handle; peripheral->filter_with_uuid = 1; @@ -1130,18 +1210,22 @@ le_command_status_t gatt_client_discover_characteristics_for_handle_range_by_uui } -le_command_status_t gatt_client_discover_characteristics_for_service_by_uuid16 (gatt_client_t *peripheral, le_service_t *service, uint16_t uuid16){ - return gatt_client_discover_characteristics_for_handle_range_by_uuid16(peripheral, service->start_group_handle, service->end_group_handle, uuid16); +le_command_status_t gatt_client_discover_characteristics_for_service_by_uuid16(uint16_t handle, le_service_t *service, uint16_t uuid16){ + return gatt_client_discover_characteristics_for_handle_range_by_uuid16(handle, service->start_group_handle, service->end_group_handle, uuid16); } -le_command_status_t gatt_client_discover_characteristics_for_service_by_uuid128(gatt_client_t *peripheral, le_service_t *service, uint8_t * uuid128){ - return gatt_client_discover_characteristics_for_handle_range_by_uuid128(peripheral, service->start_group_handle, service->end_group_handle, uuid128); +le_command_status_t gatt_client_discover_characteristics_for_service_by_uuid128(uint16_t handle, le_service_t *service, uint8_t * uuid128){ + return gatt_client_discover_characteristics_for_handle_range_by_uuid128(handle, service->start_group_handle, service->end_group_handle, uuid128); } -le_command_status_t gatt_client_discover_characteristic_descriptors(gatt_client_t *peripheral, le_characteristic_t *characteristic){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_discover_characteristic_descriptors(uint16_t con_handle, le_characteristic_t *characteristic){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + if (characteristic->value_handle == characteristic->end_handle){ - send_gatt_complete_event(peripheral, 0); + emit_gatt_complete_event(peripheral, 0); return BLE_PERIPHERAL_OK; } peripheral->start_group_handle = characteristic->value_handle + 1; @@ -1152,8 +1236,12 @@ le_command_status_t gatt_client_discover_characteristic_descriptors(gatt_client_ return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_read_value_of_characteristic_using_value_handle(gatt_client_t *peripheral, uint16_t value_handle){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_read_value_of_characteristic_using_value_handle(uint16_t con_handle, uint16_t value_handle){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->attribute_handle = value_handle; peripheral->attribute_offset = 0; peripheral->gatt_client_state = P_W2_SEND_READ_CHARACTERISTIC_VALUE_QUERY; @@ -1161,13 +1249,17 @@ le_command_status_t gatt_client_read_value_of_characteristic_using_value_handle( return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_read_value_of_characteristic(gatt_client_t *peripheral, le_characteristic_t *characteristic){ - return gatt_client_read_value_of_characteristic_using_value_handle(peripheral, characteristic->value_handle); +le_command_status_t gatt_client_read_value_of_characteristic(uint16_t handle, le_characteristic_t *characteristic){ + return gatt_client_read_value_of_characteristic_using_value_handle(handle, characteristic->value_handle); } -le_command_status_t gatt_client_read_long_value_of_characteristic_using_value_handle(gatt_client_t *peripheral, uint16_t value_handle){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_read_long_value_of_characteristic_using_value_handle(uint16_t con_handle, uint16_t value_handle){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->attribute_handle = value_handle; peripheral->attribute_offset = 0; peripheral->gatt_client_state = P_W2_SEND_READ_BLOB_QUERY; @@ -1175,23 +1267,28 @@ le_command_status_t gatt_client_read_long_value_of_characteristic_using_value_ha return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_read_long_value_of_characteristic(gatt_client_t *peripheral, le_characteristic_t *characteristic){ - return gatt_client_read_long_value_of_characteristic_using_value_handle(peripheral, characteristic->value_handle); +le_command_status_t gatt_client_read_long_value_of_characteristic(uint16_t handle, le_characteristic_t *characteristic){ + return gatt_client_read_long_value_of_characteristic_using_value_handle(handle, characteristic->value_handle); } -le_command_status_t gatt_client_write_value_of_characteristic_without_response(gatt_client_t *peripheral, uint16_t value_handle, uint16_t value_length, uint8_t * value){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_write_value_of_characteristic_without_response(uint16_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + if (value_length >= peripheral->mtu - 3) return BLE_VALUE_TOO_LONG; att_write_request(ATT_WRITE_COMMAND, peripheral->handle, value_handle, value_length, value); return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_write_value_of_characteristic(gatt_client_t *peripheral, uint16_t value_handle, uint16_t value_length, uint8_t * value){ - if (!gatt_client_is_ready(peripheral)) { - hci_dump_log("gatt client not ready"); - return BLE_PERIPHERAL_IN_WRONG_STATE; - } +le_command_status_t gatt_client_write_value_of_characteristic(uint16_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->attribute_handle = value_handle; peripheral->attribute_length = value_length; peripheral->attribute_value = value; @@ -1200,8 +1297,12 @@ le_command_status_t gatt_client_write_value_of_characteristic(gatt_client_t *per return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_write_long_value_of_characteristic(gatt_client_t *peripheral, uint16_t value_handle, uint16_t value_length, uint8_t * value){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_write_long_value_of_characteristic(uint16_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->attribute_handle = value_handle; peripheral->attribute_length = value_length; peripheral->attribute_offset = 0; @@ -1211,8 +1312,12 @@ le_command_status_t gatt_client_write_long_value_of_characteristic(gatt_client_t return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_reliable_write_long_value_of_characteristic(gatt_client_t *peripheral, uint16_t value_handle, uint16_t value_length, uint8_t * value){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_reliable_write_long_value_of_characteristic(uint16_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->attribute_handle = value_handle; peripheral->attribute_length = value_length; peripheral->attribute_offset = 0; @@ -1222,8 +1327,12 @@ le_command_status_t gatt_client_reliable_write_long_value_of_characteristic(gatt return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_write_client_characteristic_configuration(gatt_client_t *peripheral, le_characteristic_t * characteristic, uint16_t configuration){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_write_client_characteristic_configuration(uint16_t con_handle, le_characteristic_t * characteristic, uint16_t configuration){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + if ( (configuration & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION) && (characteristic->properties & ATT_PROPERTY_NOTIFY) == 0) { log_info("le_central_write_client_characteristic_configuration: BLE_CHARACTERISTIC_NOTIFICATION_NOT_SUPPORTED"); @@ -1243,10 +1352,13 @@ le_command_status_t gatt_client_write_client_characteristic_configuration(gatt_c return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_read_characteristic_descriptor(gatt_client_t *peripheral, le_characteristic_descriptor_t * descriptor){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; - peripheral->attribute_handle = descriptor->handle; +le_command_status_t gatt_client_read_characteristic_descriptor(uint16_t con_handle, le_characteristic_descriptor_t * descriptor){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + + peripheral->attribute_handle = descriptor->handle; peripheral->uuid16 = descriptor->uuid16; if (!descriptor->uuid16){ memcpy(peripheral->uuid128, descriptor->uuid128, 16); @@ -1257,8 +1369,12 @@ le_command_status_t gatt_client_read_characteristic_descriptor(gatt_client_t *pe return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_read_long_characteristic_descriptor(gatt_client_t *peripheral, le_characteristic_descriptor_t * descriptor){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_read_long_characteristic_descriptor(uint16_t con_handle, le_characteristic_descriptor_t * descriptor){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->attribute_handle = descriptor->handle; peripheral->attribute_offset = 0; peripheral->gatt_client_state = P_W2_SEND_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_QUERY; @@ -1266,39 +1382,34 @@ le_command_status_t gatt_client_read_long_characteristic_descriptor(gatt_client_ return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_write_characteristic_descriptor(gatt_client_t *peripheral, le_characteristic_descriptor_t * descriptor, uint16_t length, uint8_t * value){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_write_characteristic_descriptor(uint16_t con_handle, le_characteristic_descriptor_t * descriptor, uint16_t length, uint8_t * value){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->attribute_handle = descriptor->handle; peripheral->attribute_length = length; peripheral->attribute_offset = 0; peripheral->attribute_value = value; - peripheral->gatt_client_state = P_W2_SEND_WRITE_CHARACTERISTIC_DESCRIPTOR; gatt_client_run(); return BLE_PERIPHERAL_OK; } -le_command_status_t gatt_client_write_long_characteristic_descriptor(gatt_client_t *peripheral, le_characteristic_descriptor_t * descriptor, uint16_t length, uint8_t * value){ - if (!gatt_client_is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; +le_command_status_t gatt_client_write_long_characteristic_descriptor(uint16_t con_handle, le_characteristic_descriptor_t * descriptor, uint16_t length, uint8_t * value){ + gatt_client_t * peripheral = provide_context_for_conn_handle(con_handle); + + if (!peripheral) return BTSTACK_MEMORY_ALLOC_FAILED; + if (!is_ready(peripheral)) return BLE_PERIPHERAL_IN_WRONG_STATE; + peripheral->attribute_handle = descriptor->handle; peripheral->attribute_length = length; peripheral->attribute_offset = 0; peripheral->attribute_value = value; - peripheral->gatt_client_state = P_W2_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR; gatt_client_run(); return BLE_PERIPHERAL_OK; } -// used by daemon -void gatt_client_disconnect_connection(void * connection){ - if (!connection) return; - linked_item_t *it; - for (it = (linked_item_t *) gatt_client_connections; it ; it = it->next){ - gatt_client_t * client = (gatt_client_t *) it; - if (client->context == connection){ - gap_disconnect(client->handle); - } - } -} diff --git a/ble/gatt_client.h b/ble/gatt_client.h index 7ebd01168..7e5349a8b 100644 --- a/ble/gatt_client.h +++ b/ble/gatt_client.h @@ -150,16 +150,18 @@ typedef struct gatt_client{ sm_key_t csrk; uint32_t sign_counter; uint8_t cmac[8]; + timer_source_t gc_timeout; } gatt_client_t; typedef struct le_event { uint8_t type; - gatt_client_t * client; + uint16_t handle; } le_event_t; typedef struct gatt_complete_event{ uint8_t type; - gatt_client_t * client; + uint16_t handle; + uint16_t attribute_handle; uint8_t status; } gatt_complete_event_t; @@ -172,7 +174,7 @@ typedef struct le_service{ typedef struct le_service_event{ uint8_t type; - gatt_client_t * client; + uint16_t handle; le_service_t service; } le_service_event_t; @@ -187,13 +189,13 @@ typedef struct le_characteristic{ typedef struct le_characteristic_event{ uint8_t type; - gatt_client_t * client; + uint16_t handle; le_characteristic_t characteristic; } le_characteristic_event_t; typedef struct le_characteristic_value_event{ - uint8_t type; - gatt_client_t * client; + uint8_t type; + uint16_t handle; uint16_t value_handle; uint16_t value_offset; uint16_t blob_length; @@ -209,7 +211,7 @@ typedef struct le_characteristic_descriptor{ typedef struct le_characteristic_descriptor_event{ uint8_t type; - gatt_client_t * client; + uint16_t handle; le_characteristic_descriptor_t characteristic_descriptor; uint16_t value_length; uint16_t value_offset; @@ -217,37 +219,24 @@ typedef struct le_characteristic_descriptor_event{ } le_characteristic_descriptor_event_t; //TODO: define uuid type - // used by daemon -void gatt_client_disconnect_connection(void * connection); - // Set up GATT client. void gatt_client_init(); // Register packet handler. void gatt_client_register_packet_handler(void (*le_callback)(le_event_t * event)); -// To query a remote GATT Server, the application needs to provide a -// gatt client context structure and an active connection handle. -// This function initializes the provided gatt client context and -// adds it to the list of active clients. -void gatt_client_start(gatt_client_t *context, uint16_t handle); - -// Removes the gatt client context from the list of the list of -// active clients. -void gatt_client_stop(gatt_client_t *context); - // Returns the GATT client context for the specified handle. -gatt_client_t * get_gatt_client_context_for_handle(uint16_t handle); +// gatt_client_t * get_gatt_client_context_for_handle(uint16_t con_handle); // Returns if the gatt client is ready to receive a query. It is used with daemon. -int gatt_client_is_ready(gatt_client_t *context); +int gatt_client_is_ready(uint16_t handle); // Discovers all primary services. For each found service, an // le_service_event_t with type set to GATT_SERVICE_QUERY_RESULT // will be generated and passed to the registered callback. // The gatt_complete_event_t, with type set to GATT_QUERY_COMPLETE, // marks the end of discovery. -le_command_status_t gatt_client_discover_primary_services(gatt_client_t *context); +le_command_status_t gatt_client_discover_primary_services(uint16_t con_handle); // Discovers a specific primary service given its UUID. This service // may exist multiple times. For each found service, an @@ -255,8 +244,8 @@ le_command_status_t gatt_client_discover_primary_services(gatt_client_t *context // will be generated and passed to the registered callback. // The gatt_complete_event_t, with type set to GATT_QUERY_COMPLETE, // marks the end of discovery. -le_command_status_t gatt_client_discover_primary_services_by_uuid16(gatt_client_t *context, uint16_t uuid16); -le_command_status_t gatt_client_discover_primary_services_by_uuid128(gatt_client_t *context, const uint8_t * uuid); +le_command_status_t gatt_client_discover_primary_services_by_uuid16(uint16_t con_handle, uint16_t uuid16); +le_command_status_t gatt_client_discover_primary_services_by_uuid128(uint16_t con_handle, const uint8_t * uuid); // Finds included services within the specified service. For each // found included service, an le_service_event_t with type set to @@ -269,14 +258,14 @@ le_command_status_t gatt_client_discover_primary_services_by_uuid128(gatt_client // for the returned start group handle (returning the handle and // the UUID for primary or secondary service) or by comparing the // service to the list of all primary services. -le_command_status_t gatt_client_find_included_services_for_service(gatt_client_t *context, le_service_t *service); +le_command_status_t gatt_client_find_included_services_for_service(uint16_t con_handle, le_service_t *service); // Discovers all characteristics within the specified service. For // each found characteristic, an le_characteristics_event_t with // type set to GATT_CHARACTERISTIC_QUERY_RESULT will be generated // and passed to the registered callback. The gatt_complete_event_t // with type set to GATT_QUERY_COMPLETE, marks the end of discovery. -le_command_status_t gatt_client_discover_characteristics_for_service(gatt_client_t *context, le_service_t *service); +le_command_status_t gatt_client_discover_characteristics_for_service(uint16_t con_handle, le_service_t *service); // The following four functions are used to discover all // characteristics within the specified service or handle range, and @@ -285,10 +274,10 @@ le_command_status_t gatt_client_discover_characteristics_for_service(gatt_client // GATT_CHARACTERISTIC_QUERY_RESULT will be generated and passed to // the registered callback. The gatt_complete_event_t with type set // to GATT_QUERY_COMPLETE, marks the end of discovery. -le_command_status_t gatt_client_discover_characteristics_for_handle_range_by_uuid16(gatt_client_t *context, uint16_t start_handle, uint16_t end_handle, uint16_t uuid16); -le_command_status_t gatt_client_discover_characteristics_for_handle_range_by_uuid128(gatt_client_t *context, uint16_t start_handle, uint16_t end_handle, uint8_t * uuid); -le_command_status_t gatt_client_discover_characteristics_for_service_by_uuid16 (gatt_client_t *context, le_service_t *service, uint16_t uuid16); -le_command_status_t gatt_client_discover_characteristics_for_service_by_uuid128(gatt_client_t *context, le_service_t *service, uint8_t * uuid128); +le_command_status_t gatt_client_discover_characteristics_for_handle_range_by_uuid16(uint16_t con_handle, uint16_t start_handle, uint16_t end_handle, uint16_t uuid16); +le_command_status_t gatt_client_discover_characteristics_for_handle_range_by_uuid128(uint16_t con_handle, uint16_t start_handle, uint16_t end_handle, uint8_t * uuid); +le_command_status_t gatt_client_discover_characteristics_for_service_by_uuid16 (uint16_t con_handle, le_service_t *service, uint16_t uuid16); +le_command_status_t gatt_client_discover_characteristics_for_service_by_uuid128(uint16_t con_handle, le_service_t *service, uint8_t * uuid128); // Discovers attribute handle and UUID of a characteristic // descriptor within the specified characteristic. For each found @@ -297,7 +286,7 @@ le_command_status_t gatt_client_discover_characteristics_for_service_by_uuid128( // generated and passed to the registered callback. The // gatt_complete_event_t with type set to GATT_QUERY_COMPLETE, marks // the end of discovery. -le_command_status_t gatt_client_discover_characteristic_descriptors(gatt_client_t *context, le_characteristic_t *characteristic); +le_command_status_t gatt_client_discover_characteristic_descriptors(uint16_t con_handle, le_characteristic_t *characteristic); // Reads the characteristic value using the characteristic's value // handle. If the characteristic value is found, an @@ -305,8 +294,8 @@ le_command_status_t gatt_client_discover_characteristic_descriptors(gatt_client_ // GATT_CHARACTERISTIC_VALUE_QUERY_RESULT will be generated and // passed to the registered callback. The gatt_complete_event_t // with type set to GATT_QUERY_COMPLETE, marks the end of read. -le_command_status_t gatt_client_read_value_of_characteristic(gatt_client_t *context, le_characteristic_t *characteristic); -le_command_status_t gatt_client_read_value_of_characteristic_using_value_handle(gatt_client_t *context, uint16_t characteristic_value_handle); +le_command_status_t gatt_client_read_value_of_characteristic(uint16_t con_handle, le_characteristic_t *characteristic); +le_command_status_t gatt_client_read_value_of_characteristic_using_value_handle(uint16_t con_handle, uint16_t characteristic_value_handle); // Reads the long characteristic value using the characteristic's // value handle. The value will be returned in several blobs. For @@ -315,25 +304,25 @@ le_command_status_t gatt_client_read_value_of_characteristic_using_value_handle( // will be generated and passed to the registered callback. The // gatt_complete_event_t with type set to GATT_QUERY_COMPLETE, marks // the end of read. -le_command_status_t gatt_client_read_long_value_of_characteristic(gatt_client_t *context, le_characteristic_t *characteristic); -le_command_status_t gatt_client_read_long_value_of_characteristic_using_value_handle(gatt_client_t *context, uint16_t characteristic_value_handle); +le_command_status_t gatt_client_read_long_value_of_characteristic(uint16_t con_handle, le_characteristic_t *characteristic); +le_command_status_t gatt_client_read_long_value_of_characteristic_using_value_handle(uint16_t con_handle, uint16_t characteristic_value_handle); // Writes the characteristic value using the characteristic's value // handle without an acknowledgement that the write was successfully // performed. -le_command_status_t gatt_client_write_value_of_characteristic_without_response(gatt_client_t *context, uint16_t characteristic_value_handle, uint16_t length, uint8_t * data); +le_command_status_t gatt_client_write_value_of_characteristic_without_response(uint16_t con_handle, uint16_t characteristic_value_handle, uint16_t length, uint8_t * data); // Writes the authenticated characteristic value using the // characteristic's value handle without an acknowledgement // that the write was successfully performed. -le_command_status_t gatt_client_signed_write_without_response(gatt_client_t * context, uint16_t handle, uint16_t message_len, uint8_t * message, sm_key_t csrk, uint32_t sgn_counter); +le_command_status_t gatt_client_signed_write_without_response(uint16_t con_handle, uint16_t handle, uint16_t message_len, uint8_t * message, sm_key_t csrk, uint32_t sgn_counter); // Writes the characteristic value using the characteristic's value // handle. The gatt_complete_event_t with type set to // GATT_QUERY_COMPLETE, marks the end of write. The write is // successfully performed, if the event's status field is set to 0. -le_command_status_t gatt_client_write_value_of_characteristic(gatt_client_t *context, uint16_t characteristic_value_handle, uint16_t length, uint8_t * data); -le_command_status_t gatt_client_write_long_value_of_characteristic(gatt_client_t *context, uint16_t characteristic_value_handle, uint16_t length, uint8_t * data); +le_command_status_t gatt_client_write_value_of_characteristic(uint16_t con_handle, uint16_t characteristic_value_handle, uint16_t length, uint8_t * data); +le_command_status_t gatt_client_write_long_value_of_characteristic(uint16_t con_handle, uint16_t characteristic_value_handle, uint16_t length, uint8_t * data); // Writes of the long characteristic value using the // characteristic's value handle. It uses server response to @@ -341,7 +330,7 @@ le_command_status_t gatt_client_write_long_value_of_characteristic(gatt_client_t // The gatt_complete_event_t with type set to GATT_QUERY_COMPLETE // marks the end of write. The write is successfully performed, if // the event's status field is set to 0. -le_command_status_t gatt_client_reliable_write_long_value_of_characteristic(gatt_client_t *context, uint16_t characteristic_value_handle, uint16_t length, uint8_t * data); +le_command_status_t gatt_client_reliable_write_long_value_of_characteristic(uint16_t con_handle, uint16_t characteristic_value_handle, uint16_t length, uint8_t * data); // Reads the characteristic descriptor using its handle. If the // characteristic descriptor is found, an @@ -349,7 +338,7 @@ le_command_status_t gatt_client_reliable_write_long_value_of_characteristic(gatt // GATT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT will be generated // and passed to the registered callback. The gatt_complete_event_t // with type set to GATT_QUERY_COMPLETE, marks the end of read. -le_command_status_t gatt_client_read_characteristic_descriptor(gatt_client_t *context, le_characteristic_descriptor_t * descriptor); +le_command_status_t gatt_client_read_characteristic_descriptor(uint16_t con_handle, le_characteristic_descriptor_t * descriptor); // Reads the long characteristic descriptor using its handle. It // will be returned in several blobs. For each blob, an @@ -357,14 +346,14 @@ le_command_status_t gatt_client_read_characteristic_descriptor(gatt_client_t *co // GATT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT will be generated // and passed to the registered callback. The gatt_complete_event_t // with type set to GATT_QUERY_COMPLETE, marks the end of read. -le_command_status_t gatt_client_read_long_characteristic_descriptor(gatt_client_t *context, le_characteristic_descriptor_t * descriptor); +le_command_status_t gatt_client_read_long_characteristic_descriptor(uint16_t con_handle, le_characteristic_descriptor_t * descriptor); // Writes the characteristic descriptor using its handle. // The gatt_complete_event_t with type set to // GATT_QUERY_COMPLETE, marks the end of write. The write is // successfully performed, if the event's status field is set to 0. -le_command_status_t gatt_client_write_characteristic_descriptor(gatt_client_t *context, le_characteristic_descriptor_t * descriptor, uint16_t length, uint8_t * data); -le_command_status_t gatt_client_write_long_characteristic_descriptor(gatt_client_t *context, le_characteristic_descriptor_t * descriptor, uint16_t length, uint8_t * data); +le_command_status_t gatt_client_write_characteristic_descriptor(uint16_t con_handle, le_characteristic_descriptor_t * descriptor, uint16_t length, uint8_t * data); +le_command_status_t gatt_client_write_long_characteristic_descriptor(uint16_t con_handle, le_characteristic_descriptor_t * descriptor, uint16_t length, uint8_t * data); // Writes the client characteristic configuration of the specified // characteristic. It is used to subscribe for notifications or @@ -373,7 +362,7 @@ le_command_status_t gatt_client_write_long_characteristic_descriptor(gatt_client // GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION resp. // GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION // as configuration value. -le_command_status_t gatt_client_write_client_characteristic_configuration(gatt_client_t *context, le_characteristic_t * characteristic, uint16_t configuration); +le_command_status_t gatt_client_write_client_characteristic_configuration(uint16_t con_handle, le_characteristic_t * characteristic, uint16_t configuration); #if defined __cplusplus } diff --git a/example/libusb/ancs_client_lib.c b/example/libusb/ancs_client_lib.c index e9d0a7a39..7122ef6e0 100644 --- a/example/libusb/ancs_client_lib.c +++ b/example/libusb/ancs_client_lib.c @@ -90,8 +90,7 @@ static const uint8_t ancs_control_point_uuid[] = {0x69,0xD1,0xD8,0xF3,0x45 static const uint8_t ancs_data_source_uuid[] = {0x22,0xEA,0xC6,0xE9,0x24,0xD6,0x4B,0xB5,0xBE,0x44,0xB3,0x6A,0xCE,0x7C,0x7B,0xFB}; static uint32_t ancs_notification_uid; -static uint16_t handle; -static gatt_client_t ancs_client_context; +static uint16_t gc_handle; static int ancs_service_found; static le_service_t ancs_service; static le_characteristic_t ancs_notification_source_characteristic; @@ -182,7 +181,7 @@ static void handle_gatt_client_event(le_event_t * event){ } tc_state = TC_W4_CHARACTERISTIC_RESULT; printf("ANCS Client - Discover characteristics for ANCS SERVICE \n"); - gatt_client_discover_characteristics_for_service(&ancs_client_context, &ancs_service); + gatt_client_discover_characteristics_for_service(gc_handle, &ancs_service); break; default: break; @@ -215,7 +214,7 @@ static void handle_gatt_client_event(le_event_t * event){ case GATT_QUERY_COMPLETE: printf("ANCS Characteristcs count %u\n", ancs_characteristcs); tc_state = TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED; - gatt_client_write_client_characteristic_configuration(&ancs_client_context, &ancs_notification_source_characteristic, + gatt_client_write_client_characteristic_configuration(gc_handle, &ancs_notification_source_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); break; default: @@ -227,7 +226,7 @@ static void handle_gatt_client_event(le_event_t * event){ case GATT_QUERY_COMPLETE: printf("ANCS Notification Source subscribed\n"); tc_state = TC_W4_DATA_SOURCE_SUBSCRIBED; - gatt_client_write_client_characteristic_configuration(&ancs_client_context, &ancs_data_source_characteristic, + gatt_client_write_client_characteristic_configuration(gc_handle, &ancs_data_source_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); break; default: @@ -261,7 +260,7 @@ static void handle_gatt_client_event(le_event_t * event){ bt_store_32(get_notification_attributes, 1, ancs_notification_uid); ancs_notification_uid = 0; ancs_chunk_parser_init(); - gatt_client_write_value_of_characteristic(&ancs_client_context, ancs_control_point_characteristic.value_handle, + gatt_client_write_value_of_characteristic(gc_handle, ancs_control_point_characteristic.value_handle, sizeof(get_notification_attributes), get_notification_attributes); } else { printf("Unknown Source: "); @@ -284,8 +283,8 @@ void ancs_client_hci_event_handler (uint8_t packet_type, uint16_t channel, uint8 case HCI_EVENT_LE_META: switch (packet[2]) { case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: - handle = READ_BT_16(packet, 4); - printf("Connection handle 0x%04x\n", handle); + gc_handle = READ_BT_16(packet, 4); + printf("Connection ghandle 0x%04x\n", gc_handle); // we need to be paired to enable notifications tc_state = TC_W4_ENCRYPTED_CONNECTION; @@ -298,7 +297,7 @@ void ancs_client_hci_event_handler (uint8_t packet_type, uint16_t channel, uint8 break; case HCI_EVENT_ENCRYPTION_CHANGE: - if (handle != READ_BT_16(packet, 3)) break; + if (gc_handle != READ_BT_16(packet, 3)) break; connection_encrypted = packet[5]; log_info("Encryption state change: %u", connection_encrypted); if (!connection_encrypted) break; @@ -307,8 +306,7 @@ void ancs_client_hci_event_handler (uint8_t packet_type, uint16_t channel, uint8 // let's start printf("\nANCS Client - CONNECTED, discover ANCS service\n"); tc_state = TC_W4_SERVICE_RESULT; - gatt_client_start(&ancs_client_context, handle); - gatt_client_discover_primary_services_by_uuid128(&ancs_client_context, ancs_service_uuid); + gatt_client_discover_primary_services_by_uuid128(gc_handle, ancs_service_uuid); break; case HCI_EVENT_DISCONNECTION_COMPLETE: @@ -322,5 +320,5 @@ void ancs_client_hci_event_handler (uint8_t packet_type, uint16_t channel, uint8 } void ancs_client_init(){ - gatt_client_register_packet_handler(&handle_gatt_client_event); + gatt_client_register_packet_handler(&gc_handle_gatt_client_event); } diff --git a/example/libusb/gatt_battery_query.c b/example/libusb/gatt_battery_query.c index 901137494..6128d09d0 100644 --- a/example/libusb/gatt_battery_query.c +++ b/example/libusb/gatt_battery_query.c @@ -85,8 +85,8 @@ typedef enum { static bd_addr_t cmdline_addr = { }; static int cmdline_addr_found = 0; -static gatt_client_t gc_context; +uint16_t gc_handle; static uint16_t battery_service_uuid = 0x180F; static uint16_t battery_level_characteristic_uuid = 0x2a19; static le_service_t battery_service; @@ -150,7 +150,7 @@ void handle_gatt_client_event(le_event_t * event){ case GATT_QUERY_COMPLETE: state = TC_W4_CHARACTERISTIC_RESULT; printf("\nSearch for battery level characteristic in battery service. "); - gatt_client_discover_characteristics_for_service_by_uuid16(&gc_context, &battery_service, battery_level_characteristic_uuid); + gatt_client_discover_characteristics_for_service_by_uuid16(gc_handle, &battery_service, battery_level_characteristic_uuid); break; default: break; @@ -167,7 +167,7 @@ void handle_gatt_client_event(le_event_t * event){ case GATT_QUERY_COMPLETE: state = TC_W4_BATTERY_DATA; printf("\nConfigure battery level characteristic for notify."); - gatt_client_write_client_characteristic_configuration(&gc_context, &config_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); + gatt_client_write_client_characteristic_configuration(gc_handle, &config_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); break; default: break; @@ -213,8 +213,6 @@ static void fill_advertising_report_from_packet(advertising_report_t * report, u static void handle_hci_event(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ if (packet_type != HCI_EVENT_PACKET) return; advertising_report_t report; - uint16_t gc_handle; - uint8_t event = packet[0]; switch (event) { case BTSTACK_EVENT_STATE: @@ -237,6 +235,7 @@ static void handle_hci_event(void * connection, uint8_t packet_type, uint16_t ch // stop scanning, and connect to the device state = TC_W4_CONNECT; le_central_stop_scan(); + printf("Stop scan. Start connect to device with addr %s.\n", bd_addr_to_str(report.address)); le_central_connect(&report.address,report.address_type); break; case HCI_EVENT_LE_META: @@ -245,15 +244,13 @@ static void handle_hci_event(void * connection, uint8_t packet_type, uint16_t ch if (state != TC_W4_CONNECT) return; gc_handle = READ_BT_16(packet, 4); // initialize gatt client context with handle, and add it to the list of active clients - gatt_client_start(&gc_context, gc_handle); // query primary services printf("\nSearch for battery service. "); state = TC_W4_SERVICE_RESULT; - gatt_client_discover_primary_services_by_uuid16(&gc_context, battery_service_uuid); + gatt_client_discover_primary_services_by_uuid16(gc_handle, battery_service_uuid); break; case HCI_EVENT_DISCONNECTION_COMPLETE: printf("\nDISCONNECTED\n"); - gatt_client_stop(&gc_context); exit(0); break; default: @@ -268,7 +265,7 @@ void setup(void){ run_loop_init(RUN_LOOP_POSIX); // use logger: format HCI_DUMP_PACKETLOGGER, HCI_DUMP_BLUEZ or HCI_DUMP_STDOUT - hci_dump_open("/tmp/gatt_browser.pklg", HCI_DUMP_PACKETLOGGER); + hci_dump_open("/tmp/hci_dump.pklg", HCI_DUMP_PACKETLOGGER); // init HCI remote_device_db_t * remote_db = (remote_device_db_t *) &remote_device_db_memory; diff --git a/example/libusb/gatt_browser.c b/example/libusb/gatt_browser.c index b8efa5350..f938befd8 100644 --- a/example/libusb/gatt_browser.c +++ b/example/libusb/gatt_browser.c @@ -86,8 +86,8 @@ typedef enum { static bd_addr_t cmdline_addr = { }; static int cmdline_addr_found = 0; -static gatt_client_t gc_context; +uint16_t gc_handle; static le_service_t services[40]; static int service_count = 0; static int service_index = 0; @@ -138,7 +138,7 @@ void handle_gatt_client_event(le_event_t * event){ printf("\ntest client - CHARACTERISTIC for SERVICE "); printUUID128(service.uuid128); printf("\n"); - gatt_client_discover_characteristics_for_service(&gc_context, &services[service_index]); + gatt_client_discover_characteristics_for_service(gc_handle, &services[service_index]); break; default: break; @@ -159,12 +159,12 @@ void handle_gatt_client_event(le_event_t * event){ printUUID128(service.uuid128); printf(", [0x%04x-0x%04x]\n", service.start_group_handle, service.end_group_handle); - gatt_client_discover_characteristics_for_service(&gc_context, &service); + gatt_client_discover_characteristics_for_service(gc_handle, &service); break; } state = TC_W4_DISCONNECT; service_index = 0; - gap_disconnect(gc_context.handle); + gap_disconnect(gc_handle); break; default: break; @@ -196,8 +196,7 @@ static void fill_advertising_report_from_packet(advertising_report_t * report, u static void handle_hci_event(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ if (packet_type != HCI_EVENT_PACKET) return; advertising_report_t report; - uint16_t gc_handle; - + uint8_t event = packet[0]; switch (event) { case BTSTACK_EVENT_STATE: @@ -227,15 +226,12 @@ static void handle_hci_event(void * connection, uint8_t packet_type, uint16_t ch if (packet[2] != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break; if (state != TC_W4_CONNECT) return; gc_handle = READ_BT_16(packet, 4); - // initialize gatt client context with handle, and add it to the list of active clients - gatt_client_start(&gc_context, gc_handle); // query primary services state = TC_W4_SERVICE_RESULT; - gatt_client_discover_primary_services(&gc_context); + gatt_client_discover_primary_services(gc_handle); break; case HCI_EVENT_DISCONNECTION_COMPLETE: printf("\ntest client - DISCONNECTED\n"); - gatt_client_stop(&gc_context); exit(0); break; default: diff --git a/platforms/posix/src/daemon.c b/platforms/posix/src/daemon.c index 967750b27..e22958ba0 100644 --- a/platforms/posix/src/daemon.c +++ b/platforms/posix/src/daemon.c @@ -124,8 +124,7 @@ typedef struct { linked_list_t l2cap_cids; linked_list_t l2cap_psms; linked_list_t sdp_record_handles; - linked_list_t gatt_client_handles; - + linked_list_t gatt_con_handles; // power mode HCI_POWER_MODE power_mode; @@ -139,9 +138,19 @@ typedef struct linked_list_uint32 { uint32_t value; } linked_list_uint32_t; -typedef struct gatt_client_helper { +typedef struct linked_list_connection { + linked_item_t item; + connection_t * connection; +} linked_list_connection_t; + +typedef struct linked_list_gatt_client_helper{ + linked_item_t item; + uint16_t con_handle; + connection_t * active_connection; // the one that started the current query + linked_list_t all_connections; // list of all connections that ever used this helper uint16_t characteristic_length; -} gatt_client_helper_t; + uint8_t characteristic_buffer[ATT_MAX_LONG_ATTRIBUTE_SIZE]; +} linked_list_gatt_client_helper_t; // MARK: prototypes static void handle_sdp_rfcomm_service_result(sdp_query_event_t * event, void * context); @@ -162,7 +171,9 @@ static hci_uart_config_t config; static timer_source_t timeout; static uint8_t timeout_active = 0; static int power_management_sleep = 0; -static linked_list_t clients = NULL; // list of connected clients +static linked_list_t clients = NULL; // list of connected clients ` +static linked_list_t gatt_client_helpers = NULL; // list of used gatt client (helpers) + static void (*bluetooth_status_handler)(BLUETOOTH_STATE state) = dummy_bluetooth_status_handler; static int global_enable = 0; @@ -188,6 +199,7 @@ static void daemon_no_connections_timeout(struct timer *ts){ hci_power_control(HCI_POWER_OFF); } + static void add_uint32_to_list(linked_list_t *list, uint32_t value){ linked_list_uint32_t * item = malloc(sizeof(linked_list_uint32_t)); if (!item) return; @@ -266,22 +278,169 @@ static void daemon_remove_client_sdp_service_record_handle(connection_t * connec remove_and_free_uint32_from_list(&client_state->sdp_record_handles, handle); } + static void daemon_add_gatt_client_handle(connection_t * connection, uint32_t handle){ client_state_t * client_state = client_for_connection(connection); if (!client_state) return; - add_uint32_to_list(&client_state->gatt_client_handles, handle); + + // check if handle already exists in the gatt_con_handles list + linked_list_iterator_t it; + int handle_found = 0; + linked_list_iterator_init(&it, &client_state->gatt_con_handles); + while (linked_list_iterator_has_next(&it)){ + linked_list_uint32_t * item = (linked_list_uint32_t*) linked_list_iterator_next(&it); + if (item->value == handle){ + handle_found = 1; + break; + } + } + // if handle doesn't exist add it to gatt_con_handles + if (!handle_found){ + add_uint32_to_list(&client_state->gatt_con_handles, handle); + } + + // check if there is a helper with given handle + linked_list_gatt_client_helper_t * gatt_helper = NULL; + linked_list_iterator_init(&it, &gatt_client_helpers); + while (linked_list_iterator_has_next(&it)){ + linked_list_gatt_client_helper_t * item = (linked_list_gatt_client_helper_t*) linked_list_iterator_next(&it); + if (item->con_handle == handle){ + gatt_helper = item; + break; + } + } + + // if gatt_helper doesn't exist, create it and add it to gatt_client_helpers list + if (!gatt_helper){ + gatt_helper = malloc(sizeof(linked_list_gatt_client_helper_t)); + if (!gatt_helper) return; + memset(gatt_helper, 0, sizeof(linked_list_gatt_client_helper_t)); + gatt_helper->con_handle = handle; + linked_list_add(&gatt_client_helpers, (linked_item_t *) gatt_helper); + } + + // check if connection exists + int connection_found = 0; + linked_list_iterator_init(&it, &gatt_helper->all_connections); + while (linked_list_iterator_has_next(&it)){ + linked_list_connection_t * item = (linked_list_connection_t*) linked_list_iterator_next(&it); + if (item->connection == connection){ + connection_found = 1; + break; + } + } + + // if connection is not found, add it to the all_connections, and set it as active connection + if (!connection_found){ + linked_list_connection_t * con = malloc(sizeof(linked_list_connection_t)); + if (!con) return; + memset(con, 0, sizeof(linked_list_connection_t)); + con->connection = connection; + linked_list_add(&gatt_helper->all_connections, (linked_item_t *)con); + } + + // remember connection responsible for this request + gatt_helper->active_connection = connection; } + static void daemon_remove_gatt_client_handle(connection_t * connection, uint32_t handle){ + // PART 1 - uses connection & handle + // might be extracted or vanish totally client_state_t * client_state = client_for_connection(connection); if (!client_state) return; - remove_and_free_uint32_from_list(&client_state->gatt_client_handles, handle); + + linked_list_iterator_t it; + // remove handle from gatt_con_handles list + linked_list_iterator_init(&it, &client_state->gatt_con_handles); + while (linked_list_iterator_has_next(&it)){ + linked_list_uint32_t * item = (linked_list_uint32_t*) linked_list_iterator_next(&it); + if (item->value == handle){ + linked_list_remove(&client_state->gatt_con_handles, (linked_item_t *) item); + free(item); + } + } + + // PART 2 - only uses handle + + // find helper with given handle + linked_list_gatt_client_helper_t * helper = NULL; + linked_list_iterator_init(&it, &gatt_client_helpers); + while (linked_list_iterator_has_next(&it)){ + linked_list_gatt_client_helper_t * item = (linked_list_gatt_client_helper_t*) linked_list_iterator_next(&it); + if (item->con_handle == handle){ + helper = item; + break; + } + } + + if (!helper) return; + // remove connection from helper + linked_list_iterator_init(&it, &helper->all_connections); + while (linked_list_iterator_has_next(&it)){ + linked_list_connection_t * item = (linked_list_connection_t*) linked_list_iterator_next(&it); + if (item->connection == connection){ + linked_list_remove(&helper->all_connections, (linked_item_t *) item); + free(item); + break; + } + } + + if (helper->active_connection == connection){ + helper->active_connection = NULL; + } + // if helper has no more connections, call disconnect + if (helper->all_connections == NULL){ + gap_disconnect((hci_con_handle_t) helper->con_handle); + } } -static void daemon_rfcomm_close_connection(linked_list_t *rfcomm_services, linked_list_t *rfcomm_cids){ - linked_list_iterator_t it; +static void daemon_remove_gatt_client_helper(uint32_t con_handle){ + linked_list_iterator_t it, cl; + // find helper with given handle + linked_list_gatt_client_helper_t * helper = NULL; + linked_list_iterator_init(&it, &gatt_client_helpers); + while (linked_list_iterator_has_next(&it)){ + linked_list_gatt_client_helper_t * item = (linked_list_gatt_client_helper_t*) linked_list_iterator_next(&it); + if (item->con_handle == con_handle){ + helper = item; + break; + } + } + if (!helper) return; + + // remove all connection from helper + linked_list_iterator_init(&it, &helper->all_connections); + while (linked_list_iterator_has_next(&it)){ + linked_list_connection_t * item = (linked_list_connection_t*) linked_list_iterator_next(&it); + linked_list_remove(&helper->all_connections, (linked_item_t *) item); + free(item); + } + + free(helper); + + linked_list_iterator_init(&cl, &clients); + while (linked_list_iterator_has_next(&cl)){ + client_state_t * client_state = (client_state_t *) linked_list_iterator_next(&cl); + linked_list_iterator_init(&it, &client_state->gatt_con_handles); + while (linked_list_iterator_has_next(&it)){ + linked_list_uint32_t * item = (linked_list_uint32_t*) linked_list_iterator_next(&it); + if (item->value == con_handle){ + linked_list_remove(&client_state->gatt_con_handles, (linked_item_t *) item); + free(item); + } + } + } +} + + +static void daemon_rfcomm_close_connection(client_state_t * gatt_client){ + linked_list_iterator_t it; + linked_list_t *rfcomm_services = &gatt_client->rfcomm_services; + linked_list_t *rfcomm_cids = &gatt_client->rfcomm_cids; + linked_list_iterator_init(&it, rfcomm_services); while (linked_list_iterator_has_next(&it)){ linked_list_uint32_t * item = (linked_list_uint32_t*) linked_list_iterator_next(&it); @@ -299,9 +458,12 @@ static void daemon_rfcomm_close_connection(linked_list_t *rfcomm_services, linke } } -static void daemon_l2cap_close_connection(linked_list_t *l2cap_psms, linked_list_t *l2cap_cids){ - linked_list_iterator_t it; +static void daemon_l2cap_close_connection(client_state_t * gatt_client){ + linked_list_iterator_t it; + linked_list_t *l2cap_psms = &gatt_client->l2cap_psms; + linked_list_t *l2cap_cids = &gatt_client->l2cap_cids; + linked_list_iterator_init(&it, l2cap_psms); while (linked_list_iterator_has_next(&it)){ linked_list_uint32_t * item = (linked_list_uint32_t*) linked_list_iterator_next(&it); @@ -319,25 +481,27 @@ static void daemon_l2cap_close_connection(linked_list_t *l2cap_psms, linked_list } } -static void daemon_sdp_close_connection(void *connection, linked_list_t *sdp_handles){ +static void daemon_sdp_close_connection(client_state_t * gatt_client){ + linked_list_t * list = &gatt_client->sdp_record_handles; linked_list_iterator_t it; - linked_list_iterator_init(&it, sdp_handles); + linked_list_iterator_init(&it, list); while (linked_list_iterator_has_next(&it)){ linked_list_uint32_t * item = (linked_list_uint32_t*) linked_list_iterator_next(&it); - sdp_unregister_service_internal(connection, item->value); - linked_list_remove(sdp_handles, (linked_item_t *) item); + sdp_unregister_service_internal(&gatt_client->connection, item->value); + linked_list_remove(list, (linked_item_t *) item); free(item); } } -static void daemon_gatt_client_close_connection(linked_list_t *gatt_client_handles){ - linked_list_iterator_t it; - linked_list_iterator_init(&it, gatt_client_handles); +static void daemon_gatt_client_close_connection(connection_t * connection){ + client_state_t * client = client_for_connection(connection); + if (!client) return; + + linked_list_iterator_t it; + linked_list_iterator_init(&it, &client->gatt_con_handles); while (linked_list_iterator_has_next(&it)){ linked_list_uint32_t * item = (linked_list_uint32_t*) linked_list_iterator_next(&it); - gap_disconnect((hci_con_handle_t) item->value); - linked_list_remove(gatt_client_handles, (linked_item_t *) item); - free(item); + daemon_remove_gatt_client_handle(connection, item->value); } } @@ -347,13 +511,13 @@ static void daemon_disconnect_client(connection_t * connection){ client_state_t * client = client_for_connection(connection); if (!client) return; - daemon_sdp_close_connection(connection, &client->sdp_record_handles); - daemon_rfcomm_close_connection(&client->rfcomm_services, &client->rfcomm_cids); - daemon_l2cap_close_connection(&client->l2cap_psms, &client->l2cap_cids); + daemon_sdp_close_connection(client); + daemon_rfcomm_close_connection(client); + daemon_l2cap_close_connection(client); #ifdef HAVE_BLE // NOTE: experimental - disconnect all LE connections where GATT Client was used // gatt_client_disconnect_connection(connection); - daemon_gatt_client_close_connection(&client->gatt_client_handles); + daemon_gatt_client_close_connection(connection); #endif linked_list_remove(&clients, (linked_item_t *) client); @@ -362,29 +526,17 @@ static void daemon_disconnect_client(connection_t * connection){ #ifdef HAVE_BLE -static gatt_client_t * daemon_provide_gatt_client_context_for_handle(void * connection, uint16_t handle){ - gatt_client_t *context; - context = get_gatt_client_context_for_handle(handle); - if (context) return context; - - context = (gatt_client_t*)malloc(sizeof(gatt_client_t) + sizeof(gatt_client_helper_t) + ATT_MAX_LONG_ATTRIBUTE_SIZE); - if (!context) return NULL; - - gatt_client_start(context, handle); - daemon_add_gatt_client_handle(connection, handle); - return context; -} - -gatt_client_helper_t * daemon_get_gatt_helper(gatt_client_t * context){ - return (gatt_client_helper_t*) (((uint8_t *) context) + sizeof(gatt_client_t)); -} - -uint8_t * daemon_get_data_buffer(gatt_client_t *context) { - return ((uint8_t *) context) + sizeof(gatt_client_t) + sizeof(gatt_client_helper_t); -} - -gatt_client_helper_t * daemon_get_gatt_client_helper(gatt_client_t *context) { - return (gatt_client_helper_t *) (((uint8_t *) context) + sizeof(gatt_client_t)); +linked_list_gatt_client_helper_t * daemon_get_gatt_client_helper(uint16_t handle) { + linked_list_iterator_t it; + linked_list_iterator_init(&it, &gatt_client_helpers); + while (linked_list_iterator_has_next(&it)){ + linked_list_gatt_client_helper_t * item = (linked_list_gatt_client_helper_t*) linked_list_iterator_next(&it); + if (item->con_handle == handle){ + return item; + } + } + log_info("daemon_get_gatt_client_helper for handle 0x%02x is NULL.", handle); + return NULL; } static void send_gatt_query_complete(connection_t * connection, uint16_t handle, uint8_t status){ @@ -397,28 +549,32 @@ static void send_gatt_query_complete(connection_t * connection, uint16_t handle, socket_connection_send_packet(connection, HCI_EVENT_PACKET, 0, event, sizeof(event)); } -gatt_client_t * daemon_prepare_gatt_client_context(connection_t *connection, uint8_t *packet) { - hci_con_handle_t handle = READ_BT_16(packet, 3); - +linked_list_gatt_client_helper_t * daemon_setup_gatt_client_request(connection_t *connection, uint8_t *packet) { + hci_con_handle_t handle = READ_BT_16(packet, 3); + log_info("daemon_setup_gatt_client_request for handle 0x%02x", handle); hci_connection_t * hci_con = hci_connection_for_handle(handle); if ((hci_con == NULL) || (hci_con->state != OPEN)){ send_gatt_query_complete(connection, handle, GATT_CLIENT_NOT_CONNECTED); return NULL; } - gatt_client_t *context = daemon_provide_gatt_client_context_for_handle(connection, handle); - if (!context) { - send_gatt_query_complete(connection, handle, BTSTACK_MEMORY_ALLOC_FAILED); - return NULL; - } - // check state - if (!gatt_client_is_ready(context)){ + linked_list_gatt_client_helper_t * helper = daemon_get_gatt_client_helper(handle); + + if (!helper){ + helper = malloc(sizeof(linked_list_gatt_client_helper_t)); + if (!helper) return NULL; + memset(helper, 0, sizeof(linked_list_gatt_client_helper_t)); + helper->con_handle = handle; + linked_list_add(&gatt_client_helpers, (linked_item_t *) helper); + } + + if (helper->active_connection){ send_gatt_query_complete(connection, handle, GATT_CLIENT_BUSY); return NULL; } - context->context = connection; - return context; + daemon_add_gatt_client_handle(connection, handle); + return helper; } // (de)serialize structs from/to HCI commands/events @@ -467,7 +623,7 @@ void daemon_setup_service_event(le_event_t *le_event, uint8_t* event) { le_service_event_t * service_event = (le_service_event_t *) le_event; event[0] = le_event->type; event[1] = SERVICE_LENGTH; - bt_store_16(event, 2, service_event->client->handle); + bt_store_16(event, 2, service_event->handle); daemon_gatt_serialize_service(&service_event->service, event, 4); } @@ -475,7 +631,7 @@ void daemon_gatt_setup_characteristic_event(le_event_t *le_event, uint8_t* event le_characteristic_event_t * characteristic_event = (le_characteristic_event_t *) le_event; event[0] = le_event->type; event[1] = CHARACTERISTIC_LENGTH; - bt_store_16(event, 2, characteristic_event->client->handle); + bt_store_16(event, 2, characteristic_event->handle); daemon_gatt_serialize_characteristic(&characteristic_event->characteristic, event, 4); } @@ -483,7 +639,7 @@ void daemon_setup_characteristic_descriptor_event(le_event_t *le_event, uint8_t* le_characteristic_descriptor_event_t * descriptor_event = (le_characteristic_descriptor_event_t *) le_event; event[0] = le_event->type; event[1] = CHARACTERISTIC_DESCRIPTOR_LENGTH; - bt_store_16(event, 2, descriptor_event->client->handle); + bt_store_16(event, 2, descriptor_event->handle); daemon_gatt_serialize_characteristic_descriptor(&descriptor_event->characteristic_descriptor, event, 4); } @@ -500,7 +656,7 @@ void daemon_setup_characteristic_value_event(le_event_t *le_event, uint8_t* even le_characteristic_value_event_t * cvalue_event = (le_characteristic_value_event_t *) le_event; event[0] = le_event->type; event[1] = 2 + (2 + 2 + cvalue_event->blob_length); - bt_store_16(event, 2, cvalue_event->client->handle); + bt_store_16(event, 2, cvalue_event->handle); bt_store_16(event, 4, cvalue_event->value_handle); bt_store_16(event, 6, cvalue_event->blob_length); memcpy(&event[8], cvalue_event->blob, cvalue_event->blob_length); @@ -524,13 +680,13 @@ static int btstack_command_handler(connection_t *connection, uint8_t *packet, ui client_state_t *client; #if defined(HAVE_MALLOC) && defined(HAVE_BLE) - gatt_client_t * context; uint8_t uuid128[16]; le_service_t service; le_characteristic_t characteristic; le_characteristic_descriptor_t descriptor; uint16_t data_length; uint8_t * data; + linked_list_gatt_client_helper_t * gatt_helper; #endif uint16_t serviceSearchPatternLen; @@ -762,134 +918,134 @@ static int btstack_command_handler(connection_t *connection, uint8_t *packet, ui break; #if defined(HAVE_MALLOC) && defined(HAVE_BLE) case GATT_DISCOVER_ALL_PRIMARY_SERVICES: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; - gatt_client_discover_primary_services(context); + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; + gatt_client_discover_primary_services(gatt_helper->con_handle); break; case GATT_DISCOVER_PRIMARY_SERVICES_BY_UUID16: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; - gatt_client_discover_primary_services_by_uuid16(context, READ_BT_16(packet, 5)); + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; + gatt_client_discover_primary_services_by_uuid16(gatt_helper->con_handle, READ_BT_16(packet, 5)); break; case GATT_DISCOVER_PRIMARY_SERVICES_BY_UUID128: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; swap128(&packet[5], uuid128); - gatt_client_discover_primary_services_by_uuid128(context, uuid128); + gatt_client_discover_primary_services_by_uuid128(gatt_helper->con_handle, uuid128); break; case GATT_FIND_INCLUDED_SERVICES_FOR_SERVICE: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_service(packet, 5, &service); - gatt_client_find_included_services_for_service(context, &service); + gatt_client_find_included_services_for_service(gatt_helper->con_handle, &service); break; case GATT_DISCOVER_CHARACTERISTICS_FOR_SERVICE: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_service(packet, 5, &service); - gatt_client_discover_characteristics_for_service(context, &service); + gatt_client_discover_characteristics_for_service(gatt_helper->con_handle, &service); break; case GATT_DISCOVER_CHARACTERISTICS_FOR_SERVICE_BY_UUID128: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_service(packet, 5, &service); swap128(&packet[5 + SERVICE_LENGTH], uuid128); - gatt_client_discover_characteristics_for_service_by_uuid128(context, &service, uuid128); + gatt_client_discover_characteristics_for_service_by_uuid128(gatt_helper->con_handle, &service, uuid128); break; case GATT_DISCOVER_CHARACTERISTIC_DESCRIPTORS: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_characteristic(packet, 5, &characteristic); - gatt_client_discover_characteristic_descriptors(context, &characteristic); + gatt_client_discover_characteristic_descriptors(gatt_helper->con_handle, &characteristic); break; case GATT_READ_VALUE_OF_CHARACTERISTIC: - // context = daemon_prepare_gatt_client_context(connection, packet, GATT_CHARACTERISTIC_VALUE_QUERY_RESULT); - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_characteristic(packet, 5, &characteristic); - gatt_client_read_value_of_characteristic(context, &characteristic); + gatt_client_read_value_of_characteristic(gatt_helper->con_handle, &characteristic); break; case GATT_READ_LONG_VALUE_OF_CHARACTERISTIC: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_characteristic(packet, 5, &characteristic); - gatt_client_read_long_value_of_characteristic(context, &characteristic); + gatt_client_read_long_value_of_characteristic(gatt_helper->con_handle, &characteristic); break; case GATT_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_characteristic(packet, 5, &characteristic); data_length = READ_BT_16(packet, 5 + CHARACTERISTIC_LENGTH); - data = daemon_get_data_buffer(context); + data = gatt_helper->characteristic_buffer; memcpy(data, &packet[7 + CHARACTERISTIC_LENGTH], data_length); - gatt_client_write_value_of_characteristic_without_response(context, characteristic.value_handle, data_length, data); + gatt_client_write_value_of_characteristic_without_response(gatt_helper->con_handle, characteristic.value_handle, data_length, data); break; case GATT_WRITE_VALUE_OF_CHARACTERISTIC: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_characteristic(packet, 5, &characteristic); data_length = READ_BT_16(packet, 5 + CHARACTERISTIC_LENGTH); - data = daemon_get_data_buffer(context); + data = gatt_helper->characteristic_buffer; memcpy(data, &packet[7 + CHARACTERISTIC_LENGTH], data_length); - gatt_client_write_value_of_characteristic(context, characteristic.value_handle, data_length, data); + gatt_client_write_value_of_characteristic(gatt_helper->con_handle, characteristic.value_handle, data_length, data); break; case GATT_WRITE_LONG_VALUE_OF_CHARACTERISTIC: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_characteristic(packet, 5, &characteristic); data_length = READ_BT_16(packet, 5 + CHARACTERISTIC_LENGTH); - data = daemon_get_data_buffer(context); + data = gatt_helper->characteristic_buffer; memcpy(data, &packet[7 + CHARACTERISTIC_LENGTH], data_length); - gatt_client_write_long_value_of_characteristic(context, characteristic.value_handle, data_length, data); + gatt_client_write_long_value_of_characteristic(gatt_helper->con_handle, characteristic.value_handle, data_length, data); break; case GATT_RELIABLE_WRITE_LONG_VALUE_OF_CHARACTERISTIC: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_characteristic(packet, 5, &characteristic); data_length = READ_BT_16(packet, 5 + CHARACTERISTIC_LENGTH); - data = daemon_get_data_buffer(context); + data = gatt_helper->characteristic_buffer; memcpy(data, &packet[7 + CHARACTERISTIC_LENGTH], data_length); - gatt_client_write_long_value_of_characteristic(context, characteristic.value_handle, data_length, data); + gatt_client_write_long_value_of_characteristic(gatt_helper->con_handle, characteristic.value_handle, data_length, data); break; case GATT_READ_CHARACTERISTIC_DESCRIPTOR: - // context = daemon_prepare_gatt_client_context(connection, packet, GATT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT); - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; + handle = READ_BT_16(packet, 3); daemon_gatt_deserialize_characteristic_descriptor(packet, 5, &descriptor); - gatt_client_read_characteristic_descriptor(context, &descriptor); + gatt_client_read_characteristic_descriptor(gatt_helper->con_handle, &descriptor); break; case GATT_READ_LONG_CHARACTERISTIC_DESCRIPTOR: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_characteristic_descriptor(packet, 5, &descriptor); - gatt_client_read_long_characteristic_descriptor(context, &descriptor); + gatt_client_read_long_characteristic_descriptor(gatt_helper->con_handle, &descriptor); break; case GATT_WRITE_CHARACTERISTIC_DESCRIPTOR: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_characteristic_descriptor(packet, 5, &descriptor); - data = daemon_get_data_buffer(context); + data = gatt_helper->characteristic_buffer; data_length = READ_BT_16(packet, 5 + CHARACTERISTIC_DESCRIPTOR_LENGTH); - gatt_client_write_characteristic_descriptor(context, &descriptor, data_length, data); + gatt_client_write_characteristic_descriptor(gatt_helper->con_handle, &descriptor, data_length, data); break; case GATT_WRITE_LONG_CHARACTERISTIC_DESCRIPTOR: - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; daemon_gatt_deserialize_characteristic_descriptor(packet, 5, &descriptor); - data = daemon_get_data_buffer(context); + data = gatt_helper->characteristic_buffer; data_length = READ_BT_16(packet, 5 + CHARACTERISTIC_DESCRIPTOR_LENGTH); - gatt_client_write_long_characteristic_descriptor(context, &descriptor, data_length, data); + gatt_client_write_long_characteristic_descriptor(gatt_helper->con_handle, &descriptor, data_length, data); break; case GATT_WRITE_CLIENT_CHARACTERISTIC_CONFIGURATION:{ uint16_t configuration = READ_BT_16(packet, 5 + CHARACTERISTIC_LENGTH); - context = daemon_prepare_gatt_client_context(connection, packet); - if (!context) break; + gatt_helper = daemon_setup_gatt_client_request(connection, packet); + if (!gatt_helper) break; + data = gatt_helper->characteristic_buffer; daemon_gatt_deserialize_characteristic(packet, 5, &characteristic); - gatt_client_write_client_characteristic_configuration(context, &characteristic, configuration); + gatt_client_write_client_characteristic_configuration(gatt_helper->con_handle, &characteristic, configuration); break; } #endif @@ -1147,19 +1303,11 @@ static void daemon_packet_handler(void * connection, uint8_t packet_type, uint16 daemon_add_client_l2cap_service(connection, READ_BT_16(packet, 3)); break; #if defined(HAVE_BLE) && defined(HAVE_MALLOC) - case HCI_EVENT_DISCONNECTION_COMPLETE:{ - // re-enable advertisements - // todos = ENABLE_ADVERTISEMENTS; - - uint16_t handle = READ_BT_16(packet, 3); - daemon_remove_gatt_client_handle(connection, handle); - - gatt_client_t *context = get_gatt_client_context_for_handle(handle); - if (!context) break; - gatt_client_stop(context); - free(context); + case HCI_EVENT_DISCONNECTION_COMPLETE: + log_info("daemon : ignore HCI_EVENT_DISCONNECTION_COMPLETE ingnoring."); + // note: moved to gatt_client_handler because it's received here prematurely + // daemon_remove_gatt_client_helper(READ_BT_16(packet, 3)); break; - } #endif default: break; @@ -1286,8 +1434,6 @@ static void daemon_sigint_handler(int param){ exit(0); } - - // MARK: manage power off timer #define USE_POWER_OFF_TIMER @@ -1377,12 +1523,30 @@ static void * run_loop_thread(void *context){ #ifdef HAVE_BLE static void handle_gatt_client_event(le_event_t * le_event){ - connection_t * context = (connection_t *)le_event->client->context; + + // hack: handle disconnection_complete_here instead of main hci event packet handler + // we receive a HCI event packet in disguise + if (le_event->type == HCI_EVENT_DISCONNECTION_COMPLETE){ + log_info("daemon hack: handle disconnection_complete in handle_gatt_client_event instead of main hci event packet handler"); + uint8_t * packet = (uint8_t*) le_event; + uint16_t handle = READ_BT_16(packet, 3); + daemon_remove_gatt_client_helper(handle); + return; + } + gatt_complete_event_t * complete_event = (gatt_complete_event_t *) le_event; - + + linked_list_gatt_client_helper_t * gatt_client_helper = daemon_get_gatt_client_helper(le_event->handle); + if (!gatt_client_helper){ + log_info("daemon handle_gatt_client_event: gc helper for handle 0x%2x is NULL.", le_event->handle); + return; + } + + connection_t *connection = gatt_client_helper->active_connection; + if (!connection) return; + #if defined(HAVE_MALLOC) uint8_t * data; - gatt_client_helper_t * gatt_client_helper; uint8_t gatt_chunk = 0; #endif @@ -1393,14 +1557,14 @@ static void handle_gatt_client_event(le_event_t * le_event){ uint8_t event[4 + SERVICE_LENGTH]; daemon_setup_service_event(le_event, event); hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); - socket_connection_send_packet(context, HCI_EVENT_PACKET, 0, event, sizeof(event)); + socket_connection_send_packet(connection, HCI_EVENT_PACKET, 0, event, sizeof(event)); break; } case GATT_CHARACTERISTIC_QUERY_RESULT:{ uint8_t event[4 + CHARACTERISTIC_LENGTH]; daemon_gatt_setup_characteristic_event(le_event, event); hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); - socket_connection_send_packet(context, HCI_EVENT_PACKET, 0, event, sizeof(event)); + socket_connection_send_packet(connection, HCI_EVENT_PACKET, 0, event, sizeof(event)); break; } @@ -1410,7 +1574,7 @@ static void handle_gatt_client_event(le_event_t * le_event){ uint8_t event[4 + CHARACTERISTIC_DESCRIPTOR_LENGTH]; daemon_setup_characteristic_descriptor_event(le_event, event); hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); - socket_connection_send_packet(context, HCI_EVENT_PACKET, 0, event, sizeof(event)); + socket_connection_send_packet(connection, HCI_EVENT_PACKET, 0, event, sizeof(event)); break; } @@ -1418,7 +1582,7 @@ static void handle_gatt_client_event(le_event_t * le_event){ uint8_t event[4 + 2 + 1 + ATT_MAX_ATTRIBUTE_SIZE]; // (type, len, handle), handle, len, data daemon_setup_characteristic_value_event(le_event, event); hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); - socket_connection_send_packet(context, HCI_EVENT_PACKET, 0, event, sizeof(event)); + socket_connection_send_packet(connection, HCI_EVENT_PACKET, 0, event, sizeof(event)); break; } @@ -1426,8 +1590,7 @@ static void handle_gatt_client_event(le_event_t * le_event){ #if defined(HAVE_MALLOC) gatt_chunk = 1; le_characteristic_value_event_t * cvalue_event = (le_characteristic_value_event_t *) le_event; - data = daemon_get_data_buffer(le_event->client); - gatt_client_helper = daemon_get_gatt_client_helper(le_event->client); + data = gatt_client_helper->characteristic_buffer; gatt_client_helper->characteristic_length = cvalue_event->value_offset + cvalue_event->blob_length; memcpy(&data[cvalue_event->value_offset], cvalue_event->blob, cvalue_event->blob_length); #endif @@ -1447,17 +1610,17 @@ static void handle_gatt_client_event(le_event_t * le_event){ break; } case GATT_QUERY_COMPLETE:{ + gatt_client_helper->active_connection = NULL; if (gatt_chunk){ - gatt_client_helper = daemon_get_gatt_client_helper(le_event->client); - data = daemon_get_data_buffer(le_event->client); + data = gatt_client_helper->characteristic_buffer; uint8_t event[4 + 2 + 1 + gatt_client_helper->characteristic_length]; - daemon_setup_long_characteristic_value_event(event, complete_event->client->handle, - complete_event->client->attribute_handle, + daemon_setup_long_characteristic_value_event(event, complete_event->handle, + complete_event->attribute_handle, gatt_client_helper->characteristic_length, data); - socket_connection_send_packet(context, HCI_EVENT_PACKET, 0, event, sizeof(event)); + socket_connection_send_packet(connection, HCI_EVENT_PACKET, 0, event, sizeof(event)); gatt_chunk = 0; } - send_gatt_query_complete(context, complete_event->client->handle, complete_event->status); + send_gatt_query_complete(connection, complete_event->handle, complete_event->status); break; } default: