rewrote gatt_client state handling: API uses handles instaed of context structs (adapted examples and daemon)

This commit is contained in:
mila@ringwald.ch 2014-10-02 20:06:29 +00:00
parent 01159b07a8
commit 7c40ac80c0
6 changed files with 619 additions and 365 deletions

View File

@ -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; i<size; i+=pair_size){
@ -493,8 +553,8 @@ static void trigger_next_query(gatt_client_t * peripheral, uint16_t last_result_
return;
}
// DONE
peripheral->gatt_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);
}
}
}

View File

@ -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
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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:

View File

@ -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: