gatt-service/bas-client: use request to send

This commit is contained in:
Matthias Ringwald 2024-09-25 15:42:09 +02:00
parent b60c168034
commit 814908c5a4
2 changed files with 106 additions and 34 deletions

View File

@ -61,6 +61,8 @@
#define BATTERY_SERVICE_INVALID_INDEX 0xFF #define BATTERY_SERVICE_INVALID_INDEX 0xFF
static btstack_context_callback_registration_t battery_service_handle_can_send_now;
static btstack_linked_list_t clients; static btstack_linked_list_t clients;
static uint16_t battery_service_cid_counter = 0; static uint16_t battery_service_cid_counter = 0;
static btstack_packet_callback_registration_t hci_event_callback_registration; static btstack_packet_callback_registration_t hci_event_callback_registration;
@ -73,6 +75,12 @@ static uint16_t battery_service_get_next_cid(void){
return battery_service_cid_counter; return battery_service_cid_counter;
} }
static uint8_t battery_service_client_request_send_gatt_query(battery_service_client_t * client){
battery_service_handle_can_send_now.context = (void *)(uintptr_t)client->cid;
return gatt_client_request_to_send_gatt_query(&battery_service_handle_can_send_now, client->con_handle);
}
static battery_service_client_t * battery_service_create_client(hci_con_handle_t con_handle, uint16_t cid){ static battery_service_client_t * battery_service_create_client(hci_con_handle_t con_handle, uint16_t cid){
battery_service_client_t * client = btstack_memory_battery_service_client_get(); battery_service_client_t * client = btstack_memory_battery_service_client_get();
if (!client){ if (!client){
@ -191,14 +199,23 @@ static bool battery_service_registered_notification(battery_service_client_t * c
return status; return status;
} }
static void battery_service_start_polling_if_needed(battery_service_client_t * client){ static bool battery_service_is_polling_needed(battery_service_client_t * client){
if (client->poll_interval_ms > 0){ return (client->poll_bitmap > 0u) && (client->poll_interval_ms > 0u);
client->need_poll_bitmap = client->poll_bitmap;
battery_service_poll_timer_start(client);
}
} }
static void battery_service_run_for_client(battery_service_client_t * client){ static void battery_service_start_polling(battery_service_client_t * client){
client->need_poll_bitmap = client->poll_bitmap;
battery_service_poll_timer_start(client);
}
static void battery_service_send_next_query(void * context){
uint16_t cid = (uint16_t)(uintptr_t)context;
battery_service_client_t * client = battery_service_get_client_for_cid(cid);
if (client == NULL){
return;
}
uint8_t status; uint8_t status;
uint8_t i; uint8_t i;
gatt_client_characteristic_t characteristic; gatt_client_characteristic_t characteristic;
@ -207,10 +224,14 @@ static void battery_service_run_for_client(battery_service_client_t * client){
case BATTERY_SERVICE_CLIENT_STATE_CONNECTED: case BATTERY_SERVICE_CLIENT_STATE_CONNECTED:
for (i = 0; i < client->num_instances; i++){ for (i = 0; i < client->num_instances; i++){
if ( ((client->need_poll_bitmap >> i) & 0x01) == 0x01 ){ if ( ((client->need_poll_bitmap >> i) & 0x01) == 0x01 ){
client->state = BATTERY_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_VALUE_READ;
// clear bit of polled service // clear bit of polled service
client->need_poll_bitmap &= ~(1u << i); client->need_poll_bitmap &= ~(1u << i);
client->polled_service_index = i; client->polled_service_index = i;
#ifdef ENABLE_TESTING_SUPPORT
printf("Poll value [%d]\n", i);
#endif
// poll value of characteristic // poll value of characteristic
characteristic.value_handle = client->services[i].value_handle; characteristic.value_handle = client->services[i].value_handle;
characteristic.properties = client->services[i].properties; characteristic.properties = client->services[i].properties;
@ -286,13 +307,16 @@ static void battery_service_run_for_client(battery_service_client_t * client){
#endif #endif
client->state = BATTERY_SERVICE_CLIENT_STATE_CONNECTED; client->state = BATTERY_SERVICE_CLIENT_STATE_CONNECTED;
battery_service_emit_connection_established(client, ERROR_CODE_SUCCESS); battery_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
battery_service_start_polling_if_needed(client); if (battery_service_is_polling_needed(client)){
battery_service_start_polling(client);
}
break; break;
default: default:
break; break;
} }
} }
static void battery_service_poll_timer_timeout_handler(btstack_timer_source_t * timer){ static void battery_service_poll_timer_timeout_handler(btstack_timer_source_t * timer){
uint16_t battery_service_cid = (uint16_t)(uintptr_t) btstack_run_loop_get_timer_context(timer); uint16_t battery_service_cid = (uint16_t)(uintptr_t) btstack_run_loop_get_timer_context(timer);
@ -300,15 +324,15 @@ static void battery_service_poll_timer_timeout_handler(btstack_timer_source_t *
btstack_assert(client != NULL); btstack_assert(client != NULL);
client->need_poll_bitmap = client->poll_bitmap; client->need_poll_bitmap = client->poll_bitmap;
battery_service_poll_timer_start(client); battery_service_client_request_send_gatt_query(client);
battery_service_run_for_client(client);
} }
static void battery_service_poll_timer_start(battery_service_client_t * client){ static void battery_service_poll_timer_start(battery_service_client_t * client){
btstack_run_loop_set_timer_handler(&client->poll_timer, battery_service_poll_timer_timeout_handler); btstack_run_loop_set_timer_handler(&client->poll_timer, battery_service_poll_timer_timeout_handler);
btstack_run_loop_set_timer_context(&client->poll_timer, (void *)(uintptr_t)client->cid); btstack_run_loop_set_timer_context(&client->poll_timer, (void *)(uintptr_t)client->cid);
btstack_run_loop_set_timer(&client->poll_timer, client->poll_interval_ms); btstack_run_loop_set_timer(&client->poll_timer, client->poll_interval_ms);
btstack_run_loop_remove_timer(&client->poll_timer);
btstack_run_loop_add_timer(&client->poll_timer); btstack_run_loop_add_timer(&client->poll_timer);
} }
@ -328,7 +352,7 @@ static void battery_service_client_validate_service(battery_service_client_t * c
} }
// @return true if client valid / run function should be called // @return true if client valid / run function should be called
static bool battery_service_client_handle_query_complete(battery_service_client_t * client, uint8_t status){ static bool battery_service_client_handle_query_complete_for_connection_setup(battery_service_client_t * client, uint8_t status){
switch (client->state){ switch (client->state){
case BATTERY_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT: case BATTERY_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
if (status != ATT_ERROR_SUCCESS){ if (status != ATT_ERROR_SUCCESS){
@ -395,7 +419,10 @@ static bool battery_service_client_handle_query_complete(battery_service_client_
case BATTERY_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT: case BATTERY_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
client->state = BATTERY_SERVICE_CLIENT_STATE_CONNECTED; client->state = BATTERY_SERVICE_CLIENT_STATE_CONNECTED;
battery_service_emit_connection_established(client, ERROR_CODE_SUCCESS); battery_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
battery_service_start_polling_if_needed(client); if (battery_service_is_polling_needed(client)){
battery_service_start_polling(client);
return false;
}
break; break;
#endif #endif
case BATTERY_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED: case BATTERY_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED:
@ -406,33 +433,47 @@ static bool battery_service_client_handle_query_complete(battery_service_client_
} }
#ifdef ENABLE_TESTING_SUPPORT #ifdef ENABLE_TESTING_SUPPORT
for (client->service_index = 0; client->service_index < client->num_instances; client->service_index++){ for (client->service_index = 0; client->service_index < client->num_instances; client->service_index++){
bool need_polling = (client->poll_bitmap & (1 << client->service_index)) != 0; bool need_polling = (client->poll_bitmap & (1 << client->service_index)) != 0;
printf("read CCC 1 0x%02x, polling %d \n", client->services[client->service_index].ccc_handle, (int) need_polling); printf("read CCC 1 0x%02x, polling %d \n", client->services[client->service_index].ccc_handle, (int) need_polling);
if ( (client->services[client->service_index].ccc_handle != 0) && !need_polling ) { if ( (client->services[client->service_index].ccc_handle != 0) && !need_polling ) {
client->state = BATTERY_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION; client->state = BATTERY_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
break; break;
} }
} }
#endif #endif
client->state = BATTERY_SERVICE_CLIENT_STATE_CONNECTED; client->state = BATTERY_SERVICE_CLIENT_STATE_CONNECTED;
battery_service_emit_connection_established(client, ERROR_CODE_SUCCESS); battery_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
battery_service_start_polling_if_needed(client); if (battery_service_is_polling_needed(client)){
battery_service_start_polling(client);
return false;
}
break; break;
case BATTERY_SERVICE_CLIENT_STATE_CONNECTED: default:
return false;
}
return true;
}
static bool battery_service_client_handle_query_complete(battery_service_client_t * client, uint8_t status){
switch (client->state){
case BATTERY_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_VALUE_READ:
if (client->polled_service_index != BATTERY_SERVICE_INVALID_INDEX){ if (client->polled_service_index != BATTERY_SERVICE_INVALID_INDEX){
if (status != ATT_ERROR_SUCCESS){ if (status != ATT_ERROR_SUCCESS){
battery_service_emit_battery_level(client, client->services[client->polled_service_index].value_handle, status, 0); battery_service_emit_battery_level(client, client->services[client->polled_service_index].value_handle, status, 0);
} }
client->polled_service_index = BATTERY_SERVICE_INVALID_INDEX; client->polled_service_index = BATTERY_SERVICE_INVALID_INDEX;
} }
break; client->state = BATTERY_SERVICE_CLIENT_STATE_CONNECTED;
return (client->need_poll_bitmap != 0u);
default: default:
break; break;
} }
return true; return false;
} }
static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
@ -443,7 +484,7 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
battery_service_client_t * client = NULL; battery_service_client_t * client = NULL;
gatt_client_service_t service; gatt_client_service_t service;
gatt_client_characteristic_t characteristic; gatt_client_characteristic_t characteristic;
bool call_run = true; bool send_next_gatt_query = false;
switch(hci_event_packet_get_type(packet)){ switch(hci_event_packet_get_type(packet)){
case GATT_EVENT_SERVICE_QUERY_RESULT: case GATT_EVENT_SERVICE_QUERY_RESULT:
@ -462,6 +503,7 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
} else { } else {
log_info("Found more then %d, Battery Service instances. Increase MAX_NUM_BATTERY_SERVICES to store all.", MAX_NUM_BATTERY_SERVICES); log_info("Found more then %d, Battery Service instances. Increase MAX_NUM_BATTERY_SERVICES to store all.", MAX_NUM_BATTERY_SERVICES);
} }
// for sending next query w4 GATT_EVENT_QUERY_COMPLETE
break; break;
case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
@ -483,6 +525,7 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
characteristic.value_handle, characteristic.uuid16, characteristic.value_handle, characteristic.uuid16,
client->service_index); client->service_index);
#endif #endif
// for sending next query w4 GATT_EVENT_QUERY_COMPLETE
break; break;
case GATT_EVENT_NOTIFICATION: case GATT_EVENT_NOTIFICATION:
@ -495,6 +538,7 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
gatt_event_notification_get_value_handle(packet), gatt_event_notification_get_value_handle(packet),
ATT_ERROR_SUCCESS, ATT_ERROR_SUCCESS,
gatt_event_notification_get_value(packet)[0]); gatt_event_notification_get_value(packet)[0]);
break; break;
case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT: case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
@ -514,6 +558,7 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
gatt_event_characteristic_value_query_result_get_value_handle(packet), gatt_event_characteristic_value_query_result_get_value_handle(packet),
ATT_ERROR_SUCCESS, ATT_ERROR_SUCCESS,
gatt_event_characteristic_value_query_result_get_value(packet)[0]); gatt_event_characteristic_value_query_result_get_value(packet)[0]);
// reset need_poll_bitmap in GATT_EVENT_QUERY_COMPLETE
break; break;
#ifdef ENABLE_TESTING_SUPPORT #ifdef ENABLE_TESTING_SUPPORT
@ -533,21 +578,37 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint
characteristic_descriptor.uuid16); characteristic_descriptor.uuid16);
} }
break; break;
// for sending next query w4 GATT_EVENT_QUERY_COMPLETE
} }
#endif #endif
case GATT_EVENT_QUERY_COMPLETE: case GATT_EVENT_QUERY_COMPLETE:
client = battery_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet)); client = battery_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
btstack_assert(client != NULL); btstack_assert(client != NULL);
call_run = battery_service_client_handle_query_complete(client, gatt_event_query_complete_get_att_status(packet));
// 1. handle service establishment/notification subscription query results (client->state < BATTERY_SERVICE_CLIENT_STATE_CONNECTED)
if (client->state < BATTERY_SERVICE_CLIENT_STATE_CONNECTED){
send_next_gatt_query = battery_service_client_handle_query_complete_for_connection_setup(client, gatt_event_query_complete_get_att_status(packet));
break;
}
// 2. handle battery value query result when devices is connected
send_next_gatt_query = battery_service_client_handle_query_complete(client, gatt_event_query_complete_get_att_status(packet));
if (!send_next_gatt_query){
// if there are no further queries, and we're connected, trigger next polling read
if (battery_service_is_polling_needed(client)){
battery_service_poll_timer_start(client);
}
}
break; break;
default: default:
break; break;
} }
if (call_run && (client != NULL)){ if (send_next_gatt_query){
battery_service_run_for_client(client); battery_service_client_request_send_gatt_query(client);
return;
} }
} }
@ -598,8 +659,12 @@ uint8_t battery_service_client_connect(hci_con_handle_t con_handle, btstack_pack
client->client_handler = packet_handler; client->client_handler = packet_handler;
client->poll_interval_ms = poll_interval_ms; client->poll_interval_ms = poll_interval_ms;
client->state = BATTERY_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE; client->state = BATTERY_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE;
battery_service_run_for_client(client);
return ERROR_CODE_SUCCESS; uint8_t status = battery_service_client_request_send_gatt_query(client);
if (status != ERROR_CODE_SUCCESS){
client->state = BATTERY_SERVICE_CLIENT_STATE_IDLE;
}
return status;
} }
uint8_t battery_service_client_disconnect(uint16_t battery_service_cid){ uint8_t battery_service_client_disconnect(uint16_t battery_service_cid){
@ -612,12 +677,13 @@ uint8_t battery_service_client_disconnect(uint16_t battery_service_cid){
return ERROR_CODE_SUCCESS; return ERROR_CODE_SUCCESS;
} }
uint8_t battery_service_client_read_battery_level(uint16_t battery_service_cid, uint8_t service_index){ uint8_t battery_service_client_read_battery_level(uint16_t battery_service_cid, uint8_t service_index){
battery_service_client_t * client = battery_service_get_client_for_cid(battery_service_cid); battery_service_client_t * client = battery_service_get_client_for_cid(battery_service_cid);
if (client == NULL) { if (client == NULL) {
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
} }
if (client->state != BATTERY_SERVICE_CLIENT_STATE_CONNECTED) { if (client->state < BATTERY_SERVICE_CLIENT_STATE_CONNECTED) {
return GATT_CLIENT_IN_WRONG_STATE; return GATT_CLIENT_IN_WRONG_STATE;
} }
if (service_index >= client->num_instances) { if (service_index >= client->num_instances) {
@ -625,13 +691,18 @@ uint8_t battery_service_client_read_battery_level(uint16_t battery_service_cid,
} }
client->need_poll_bitmap |= (1u << service_index); client->need_poll_bitmap |= (1u << service_index);
battery_service_run_for_client(client);
return ERROR_CODE_SUCCESS; uint8_t status = battery_service_client_request_send_gatt_query(client);
if (status != ERROR_CODE_SUCCESS){
client->need_poll_bitmap &= ~(1u << client->polled_service_index);
}
return status;
} }
void battery_service_client_init(void){ void battery_service_client_init(void){
hci_event_callback_registration.callback = &handle_hci_event; hci_event_callback_registration.callback = &handle_hci_event;
hci_add_event_handler(&hci_event_callback_registration); hci_add_event_handler(&hci_event_callback_registration);
battery_service_handle_can_send_now.callback = &battery_service_send_next_query;
} }
void battery_service_client_deinit(void){ void battery_service_client_deinit(void){

View File

@ -83,6 +83,7 @@ typedef enum {
BATTERY_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION, BATTERY_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION,
BATTERY_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED, BATTERY_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED,
BATTERY_SERVICE_CLIENT_STATE_CONNECTED, BATTERY_SERVICE_CLIENT_STATE_CONNECTED,
BATTERY_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_VALUE_READ,
#ifdef ENABLE_TESTING_SUPPORT #ifdef ENABLE_TESTING_SUPPORT
BATTERY_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION, BATTERY_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION,
@ -142,7 +143,7 @@ void battery_service_client_init(void);
* If notifications are not supported by remote Battery Service, the client will poll battery level * If notifications are not supported by remote Battery Service, the client will poll battery level
* If poll_interval_ms is 0, polling is disabled, and only notifications will be received. * If poll_interval_ms is 0, polling is disabled, and only notifications will be received.
* In either case, the battery level is received via GATTSERVICE_SUBEVENT_BATTERY_SERVICE_LEVEL event. * In either case, the battery level is received via GATTSERVICE_SUBEVENT_BATTERY_SERVICE_LEVEL event.
* The battery level is reported as percentage, i.e. 100 = full and it is valid if the ATTT status is equal to ATT_ERROR_SUCCESS, * The battery level is reported as percentage, i.e. 100 = full and it is valid if the ATT status is equal to ATT_ERROR_SUCCESS,
* see ATT errors (see bluetooth.h) for other values. * see ATT errors (see bluetooth.h) for other values.
* *
* For manual polling, see battery_service_client_read_battery_level below. * For manual polling, see battery_service_client_read_battery_level below.