xref: /btstack/src/ble/gatt-service/battery_service_client.c (revision 814908c5a4299c9f70877326a81bebd029390db4)
16bdecec7SMatthias Ringwald /*
26bdecec7SMatthias Ringwald  * Copyright (C) 2021 BlueKitchen GmbH
36bdecec7SMatthias Ringwald  *
46bdecec7SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
56bdecec7SMatthias Ringwald  * modification, are permitted provided that the following conditions
66bdecec7SMatthias Ringwald  * are met:
76bdecec7SMatthias Ringwald  *
86bdecec7SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
96bdecec7SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
106bdecec7SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
116bdecec7SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
126bdecec7SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
136bdecec7SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
146bdecec7SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
156bdecec7SMatthias Ringwald  *    from this software without specific prior written permission.
166bdecec7SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
176bdecec7SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
186bdecec7SMatthias Ringwald  *    monetary gain.
196bdecec7SMatthias Ringwald  *
206bdecec7SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
216bdecec7SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
226bdecec7SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
256bdecec7SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
266bdecec7SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
276bdecec7SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
286bdecec7SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
296bdecec7SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
306bdecec7SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
316bdecec7SMatthias Ringwald  * SUCH DAMAGE.
326bdecec7SMatthias Ringwald  *
336bdecec7SMatthias Ringwald  * Please inquire about commercial licensing options at
346bdecec7SMatthias Ringwald  * [email protected]
356bdecec7SMatthias Ringwald  *
366bdecec7SMatthias Ringwald  */
376bdecec7SMatthias Ringwald 
386bdecec7SMatthias Ringwald #define BTSTACK_FILE__ "battery_service_client.c"
396bdecec7SMatthias Ringwald 
406bdecec7SMatthias Ringwald #include "btstack_config.h"
416bdecec7SMatthias Ringwald 
4286e34143SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
4386e34143SMilanka Ringwald #include <stdio.h>
44813fc1b9SMilanka Ringwald #include <unistd.h>
4586e34143SMilanka Ringwald #endif
46813fc1b9SMilanka Ringwald 
476bdecec7SMatthias Ringwald #include <stdint.h>
486bdecec7SMatthias Ringwald #include <string.h>
496bdecec7SMatthias Ringwald 
506bdecec7SMatthias Ringwald 
516bdecec7SMatthias Ringwald #include "ble/gatt-service/battery_service_client.h"
526bdecec7SMatthias Ringwald 
536bdecec7SMatthias Ringwald #include "btstack_memory.h"
546bdecec7SMatthias Ringwald #include "ble/core.h"
556bdecec7SMatthias Ringwald #include "ble/gatt_client.h"
566bdecec7SMatthias Ringwald #include "bluetooth_gatt.h"
576bdecec7SMatthias Ringwald #include "btstack_debug.h"
586bdecec7SMatthias Ringwald #include "btstack_event.h"
596bdecec7SMatthias Ringwald #include "btstack_run_loop.h"
606bdecec7SMatthias Ringwald #include "gap.h"
616bdecec7SMatthias Ringwald 
626bdecec7SMatthias Ringwald #define BATTERY_SERVICE_INVALID_INDEX 0xFF
636bdecec7SMatthias Ringwald 
64*814908c5SMatthias Ringwald static btstack_context_callback_registration_t battery_service_handle_can_send_now;
65*814908c5SMatthias Ringwald 
666bdecec7SMatthias Ringwald static btstack_linked_list_t clients;
676bdecec7SMatthias Ringwald static uint16_t battery_service_cid_counter = 0;
68637a9965SMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration;
696bdecec7SMatthias Ringwald 
706bdecec7SMatthias Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
716bdecec7SMatthias Ringwald static void battery_service_poll_timer_start(battery_service_client_t * client);
726bdecec7SMatthias Ringwald 
battery_service_get_next_cid(void)736bdecec7SMatthias Ringwald static uint16_t battery_service_get_next_cid(void){
749eca2206SMilanka Ringwald     battery_service_cid_counter = btstack_next_cid_ignoring_zero(battery_service_cid_counter);
756bdecec7SMatthias Ringwald     return battery_service_cid_counter;
766bdecec7SMatthias Ringwald }
776bdecec7SMatthias Ringwald 
78*814908c5SMatthias Ringwald 
battery_service_client_request_send_gatt_query(battery_service_client_t * client)79*814908c5SMatthias Ringwald static uint8_t battery_service_client_request_send_gatt_query(battery_service_client_t * client){
80*814908c5SMatthias Ringwald     battery_service_handle_can_send_now.context = (void *)(uintptr_t)client->cid;
81*814908c5SMatthias Ringwald     return gatt_client_request_to_send_gatt_query(&battery_service_handle_can_send_now, client->con_handle);
82*814908c5SMatthias Ringwald }
83*814908c5SMatthias Ringwald 
battery_service_create_client(hci_con_handle_t con_handle,uint16_t cid)846bdecec7SMatthias Ringwald static battery_service_client_t * battery_service_create_client(hci_con_handle_t con_handle, uint16_t cid){
856bdecec7SMatthias Ringwald     battery_service_client_t * client = btstack_memory_battery_service_client_get();
866bdecec7SMatthias Ringwald     if (!client){
876bdecec7SMatthias Ringwald         log_error("Not enough memory to create client");
886bdecec7SMatthias Ringwald         return NULL;
896bdecec7SMatthias Ringwald     }
906bdecec7SMatthias Ringwald     client->cid = cid;
916bdecec7SMatthias Ringwald     client->con_handle = con_handle;
926bdecec7SMatthias Ringwald     client->poll_interval_ms = 0;
936bdecec7SMatthias Ringwald     client->num_instances = 0;
946d1993e0SMilanka Ringwald     client->service_index = 0;
956bdecec7SMatthias Ringwald     client->poll_bitmap = 0;
966bdecec7SMatthias Ringwald     client->need_poll_bitmap = 0;
976bdecec7SMatthias Ringwald     client->polled_service_index = BATTERY_SERVICE_INVALID_INDEX;
986bdecec7SMatthias Ringwald     client->state = BATTERY_SERVICE_CLIENT_STATE_IDLE;
996bdecec7SMatthias Ringwald 
1006bdecec7SMatthias Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
1016bdecec7SMatthias Ringwald     return client;
1026bdecec7SMatthias Ringwald }
1036bdecec7SMatthias Ringwald 
battery_service_finalize_client(battery_service_client_t * client)1046bdecec7SMatthias Ringwald static void battery_service_finalize_client(battery_service_client_t * client){
1056bdecec7SMatthias Ringwald     // stop listening
1066bdecec7SMatthias Ringwald     uint8_t i;
1076bdecec7SMatthias Ringwald     for (i = 0; i < client->num_instances; i++){
1086bdecec7SMatthias Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->services[i].notification_listener);
1096bdecec7SMatthias Ringwald     }
1106bdecec7SMatthias Ringwald 
1116bdecec7SMatthias Ringwald     // remove timer
1126bdecec7SMatthias Ringwald     btstack_run_loop_remove_timer(&client->poll_timer);
1136bdecec7SMatthias Ringwald 
1146bdecec7SMatthias Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
1156bdecec7SMatthias Ringwald     btstack_memory_battery_service_client_free(client);
1166bdecec7SMatthias Ringwald }
1176bdecec7SMatthias Ringwald 
battery_service_get_client_for_con_handle(hci_con_handle_t con_handle)1186bdecec7SMatthias Ringwald static battery_service_client_t * battery_service_get_client_for_con_handle(hci_con_handle_t con_handle){
1196bdecec7SMatthias Ringwald     btstack_linked_list_iterator_t it;
1206bdecec7SMatthias Ringwald     btstack_linked_list_iterator_init(&it, &clients);
1216bdecec7SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
1226bdecec7SMatthias Ringwald         battery_service_client_t * client = (battery_service_client_t *)btstack_linked_list_iterator_next(&it);
1236bdecec7SMatthias Ringwald         if (client->con_handle != con_handle) continue;
1246bdecec7SMatthias Ringwald         return client;
1256bdecec7SMatthias Ringwald     }
1266bdecec7SMatthias Ringwald     return NULL;
1276bdecec7SMatthias Ringwald }
1286bdecec7SMatthias Ringwald 
battery_service_get_client_for_cid(uint16_t battery_service_cid)1296bdecec7SMatthias Ringwald static battery_service_client_t * battery_service_get_client_for_cid(uint16_t battery_service_cid){
1306bdecec7SMatthias Ringwald     btstack_linked_list_iterator_t it;
1316bdecec7SMatthias Ringwald     btstack_linked_list_iterator_init(&it, &clients);
1326bdecec7SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
1336bdecec7SMatthias Ringwald         battery_service_client_t * client = (battery_service_client_t *)btstack_linked_list_iterator_next(&it);
1346bdecec7SMatthias Ringwald         if (client->cid != battery_service_cid) continue;
1356bdecec7SMatthias Ringwald         return client;
1366bdecec7SMatthias Ringwald     }
1376bdecec7SMatthias Ringwald     return NULL;
1386bdecec7SMatthias Ringwald }
1396bdecec7SMatthias Ringwald 
battery_service_emit_connection_established(battery_service_client_t * client,uint8_t status)1406bdecec7SMatthias Ringwald static void battery_service_emit_connection_established(battery_service_client_t * client, uint8_t status){
1416bdecec7SMatthias Ringwald     uint8_t event[8];
1426bdecec7SMatthias Ringwald     int pos = 0;
1436bdecec7SMatthias Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
1446bdecec7SMatthias Ringwald     event[pos++] = sizeof(event) - 2;
1456bdecec7SMatthias Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_BATTERY_SERVICE_CONNECTED;
1466bdecec7SMatthias Ringwald     little_endian_store_16(event, pos, client->cid);
1476bdecec7SMatthias Ringwald     pos += 2;
1486bdecec7SMatthias Ringwald     event[pos++] = status;
1496bdecec7SMatthias Ringwald     event[pos++] = client->num_instances;
1506bdecec7SMatthias Ringwald     event[pos++] = client->poll_bitmap;
1516bdecec7SMatthias Ringwald 
1526bdecec7SMatthias Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1536bdecec7SMatthias Ringwald }
1546bdecec7SMatthias Ringwald 
battery_service_emit_battery_level(battery_service_client_t * client,uint16_t value_handle,uint8_t att_status,uint8_t battery_level)1556bdecec7SMatthias Ringwald static void battery_service_emit_battery_level(battery_service_client_t * client, uint16_t value_handle, uint8_t att_status, uint8_t battery_level){
1566bdecec7SMatthias Ringwald     uint8_t event[8];
1576bdecec7SMatthias Ringwald     int pos = 0;
1586bdecec7SMatthias Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
1596bdecec7SMatthias Ringwald     event[pos++] = sizeof(event) - 2;
1606bdecec7SMatthias Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_BATTERY_SERVICE_LEVEL;
1616bdecec7SMatthias Ringwald     little_endian_store_16(event, pos, client->cid);
1626bdecec7SMatthias Ringwald     pos += 2;
1636bdecec7SMatthias Ringwald 
1646bdecec7SMatthias Ringwald     uint8_t i;
1656bdecec7SMatthias Ringwald     for (i = 0; i < client->num_instances; i++){
1666bdecec7SMatthias Ringwald         if (value_handle == client->services[i].value_handle){
1676bdecec7SMatthias Ringwald             event[pos++] = i;
1686bdecec7SMatthias Ringwald             event[pos++] = att_status;
1696bdecec7SMatthias Ringwald             event[pos++] = battery_level;
1706bdecec7SMatthias Ringwald             (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1716bdecec7SMatthias Ringwald             break;
1726bdecec7SMatthias Ringwald         }
1736bdecec7SMatthias Ringwald     }
1746bdecec7SMatthias Ringwald }
1756bdecec7SMatthias Ringwald 
battery_service_registered_notification(battery_service_client_t * client,uint16_t service_index)17613ca7098SMilanka Ringwald static bool battery_service_registered_notification(battery_service_client_t * client, uint16_t service_index){
17713ca7098SMilanka Ringwald     gatt_client_characteristic_t characteristic;
17813ca7098SMilanka Ringwald     // if there are services without notification, register pool timer,
179637a9965SMatthias Ringwald     // otherwise register for notifications
18013ca7098SMilanka Ringwald     characteristic.value_handle = client->services[service_index].value_handle;
18113ca7098SMilanka Ringwald     characteristic.properties   = client->services[service_index].properties;
18213ca7098SMilanka Ringwald     characteristic.end_handle   = client->services[service_index].end_handle;
18313ca7098SMilanka Ringwald 
18413ca7098SMilanka Ringwald     uint8_t status = gatt_client_write_client_characteristic_configuration(
18513ca7098SMilanka Ringwald                 &handle_gatt_client_event,
18613ca7098SMilanka Ringwald                 client->con_handle,
18713ca7098SMilanka Ringwald                 &characteristic,
18813ca7098SMilanka Ringwald                 GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
18913ca7098SMilanka Ringwald 
19013ca7098SMilanka Ringwald     // notification supported, register for value updates
19113ca7098SMilanka Ringwald     if (status == ERROR_CODE_SUCCESS){
19213ca7098SMilanka Ringwald         gatt_client_listen_for_characteristic_value_updates(
19313ca7098SMilanka Ringwald             &client->services[service_index].notification_listener,
19413ca7098SMilanka Ringwald             &handle_gatt_client_event,
19513ca7098SMilanka Ringwald             client->con_handle, &characteristic);
19613ca7098SMilanka Ringwald     } else {
19713ca7098SMilanka Ringwald         client->poll_bitmap |= 1u << client->service_index;
19813ca7098SMilanka Ringwald     }
19913ca7098SMilanka Ringwald     return status;
20013ca7098SMilanka Ringwald }
20113ca7098SMilanka Ringwald 
battery_service_is_polling_needed(battery_service_client_t * client)202*814908c5SMatthias Ringwald static bool battery_service_is_polling_needed(battery_service_client_t * client){
203*814908c5SMatthias Ringwald     return (client->poll_bitmap > 0u) && (client->poll_interval_ms > 0u);
204*814908c5SMatthias Ringwald }
205*814908c5SMatthias Ringwald 
battery_service_start_polling(battery_service_client_t * client)206*814908c5SMatthias Ringwald static void battery_service_start_polling(battery_service_client_t * client){
2072da54b5bSMilanka Ringwald     client->need_poll_bitmap = client->poll_bitmap;
2082da54b5bSMilanka Ringwald     battery_service_poll_timer_start(client);
2092da54b5bSMilanka Ringwald }
210*814908c5SMatthias Ringwald 
battery_service_send_next_query(void * context)211*814908c5SMatthias Ringwald static void battery_service_send_next_query(void * context){
212*814908c5SMatthias Ringwald     uint16_t cid = (uint16_t)(uintptr_t)context;
213*814908c5SMatthias Ringwald     battery_service_client_t * client = battery_service_get_client_for_cid(cid);
214*814908c5SMatthias Ringwald 
215*814908c5SMatthias Ringwald     if (client == NULL){
216*814908c5SMatthias Ringwald         return;
2172da54b5bSMilanka Ringwald     }
2182da54b5bSMilanka Ringwald 
2196bdecec7SMatthias Ringwald     uint8_t status;
2206bdecec7SMatthias Ringwald     uint8_t i;
2216bdecec7SMatthias Ringwald     gatt_client_characteristic_t characteristic;
2226bdecec7SMatthias Ringwald 
2236bdecec7SMatthias Ringwald     switch (client->state){
2246bdecec7SMatthias Ringwald         case BATTERY_SERVICE_CLIENT_STATE_CONNECTED:
2256bdecec7SMatthias Ringwald             for (i = 0; i < client->num_instances; i++){
2266bdecec7SMatthias Ringwald                 if ( ((client->need_poll_bitmap >> i) & 0x01) == 0x01 ){
227*814908c5SMatthias Ringwald                     client->state = BATTERY_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_VALUE_READ;
2286bdecec7SMatthias Ringwald                     // clear bit of polled service
2296bdecec7SMatthias Ringwald                     client->need_poll_bitmap &= ~(1u << i);
2306bdecec7SMatthias Ringwald                     client->polled_service_index = i;
2316bdecec7SMatthias Ringwald 
232*814908c5SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
233*814908c5SMatthias Ringwald                     printf("Poll value [%d]\n", i);
234*814908c5SMatthias Ringwald #endif
2356bdecec7SMatthias Ringwald                     // poll value of characteristic
2366bdecec7SMatthias Ringwald                     characteristic.value_handle = client->services[i].value_handle;
2376bdecec7SMatthias Ringwald                     characteristic.properties   = client->services[i].properties;
2386bdecec7SMatthias Ringwald                     characteristic.end_handle   = client->services[i].end_handle;
239813fc1b9SMilanka Ringwald                     gatt_client_read_value_of_characteristic(&handle_gatt_client_event, client->con_handle, &characteristic);
2406bdecec7SMatthias Ringwald                     break;
2416bdecec7SMatthias Ringwald                 }
2426bdecec7SMatthias Ringwald             }
2436bdecec7SMatthias Ringwald             break;
2446bdecec7SMatthias Ringwald 
2456bdecec7SMatthias Ringwald         case BATTERY_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE:
2466bdecec7SMatthias Ringwald             client->state = BATTERY_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT;
247813fc1b9SMilanka Ringwald             status = gatt_client_discover_primary_services_by_uuid16(&handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE);
2486bdecec7SMatthias Ringwald             // TODO handle status
2496bdecec7SMatthias Ringwald             break;
2506bdecec7SMatthias Ringwald 
251813fc1b9SMilanka Ringwald         case BATTERY_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS:
252813fc1b9SMilanka Ringwald             client->state = BATTERY_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
253813fc1b9SMilanka Ringwald 
254813fc1b9SMilanka Ringwald             gatt_client_discover_characteristics_for_handle_range_by_uuid16(
255813fc1b9SMilanka Ringwald                 &handle_gatt_client_event,
256813fc1b9SMilanka Ringwald                 client->con_handle,
257813fc1b9SMilanka Ringwald                 client->services[client->service_index].start_handle,
258813fc1b9SMilanka Ringwald                 client->services[client->service_index].end_handle,
259813fc1b9SMilanka Ringwald                 ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL);
260813fc1b9SMilanka Ringwald 
261813fc1b9SMilanka Ringwald             break;
262813fc1b9SMilanka Ringwald 
263813fc1b9SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
264813fc1b9SMilanka Ringwald         case BATTERY_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS:
265813fc1b9SMilanka Ringwald             client->state = BATTERY_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT;
266813fc1b9SMilanka Ringwald             // if there are services without notification, register pool timer,
267813fc1b9SMilanka Ringwald             // othervise register for notifications
268813fc1b9SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].value_handle;
269813fc1b9SMilanka Ringwald             characteristic.properties   = client->services[client->service_index].properties;
270813fc1b9SMilanka Ringwald             characteristic.end_handle   = client->services[client->service_index].end_handle;
271813fc1b9SMilanka Ringwald 
272813fc1b9SMilanka Ringwald             (void) gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
273813fc1b9SMilanka Ringwald             break;
2747594dd72SMilanka Ringwald 
2757594dd72SMilanka Ringwald         case BATTERY_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
2767594dd72SMilanka Ringwald             printf("Read client characteristic value [Service %d, handle 0x%04X]:\n",
2777594dd72SMilanka Ringwald                 client->service_index,
2787594dd72SMilanka Ringwald                 client->services[client->service_index].ccc_handle);
2797594dd72SMilanka Ringwald 
2807594dd72SMilanka Ringwald             client->state = BATTERY_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
2817594dd72SMilanka Ringwald 
2827594dd72SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
2836289ff84SMilanka Ringwald             (void) gatt_client_read_characteristic_descriptor_using_descriptor_handle(
2847594dd72SMilanka Ringwald                 &handle_gatt_client_event,
2857594dd72SMilanka Ringwald                 client->con_handle,
2867594dd72SMilanka Ringwald                 client->services[client->service_index].ccc_handle);
2877594dd72SMilanka Ringwald             break;
288813fc1b9SMilanka Ringwald #endif
289813fc1b9SMilanka Ringwald 
290813fc1b9SMilanka Ringwald         case BATTERY_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION:
291813fc1b9SMilanka Ringwald             client->state = BATTERY_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED;
2926bdecec7SMatthias Ringwald 
29313ca7098SMilanka Ringwald             for (; client->service_index < client->num_instances; client->service_index++){
29413ca7098SMilanka Ringwald                 status = battery_service_registered_notification(client, client->service_index);
29513ca7098SMilanka Ringwald                 if (status == ERROR_CODE_SUCCESS) return;
2966bdecec7SMatthias Ringwald             }
29713ca7098SMilanka Ringwald 
29813ca7098SMilanka Ringwald 
29913ca7098SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
30013ca7098SMilanka Ringwald             for (client->service_index = 0; client->service_index < client->num_instances; client->service_index++){
30198ef7bd9SMatthias Ringwald                 bool need_polling = (client->poll_bitmap & (1 << client->service_index)) != 0;
30213ca7098SMilanka Ringwald                 if ( (client->services[client->service_index].ccc_handle != 0) && !need_polling ){
30313ca7098SMilanka Ringwald                     client->state = BATTERY_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
30413ca7098SMilanka Ringwald                     break;
30513ca7098SMilanka Ringwald                 }
30613ca7098SMilanka Ringwald             }
30713ca7098SMilanka Ringwald #endif
30813ca7098SMilanka Ringwald             client->state = BATTERY_SERVICE_CLIENT_STATE_CONNECTED;
30913ca7098SMilanka Ringwald             battery_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
310*814908c5SMatthias Ringwald             if (battery_service_is_polling_needed(client)){
311*814908c5SMatthias Ringwald                 battery_service_start_polling(client);
312*814908c5SMatthias Ringwald             }
3136bdecec7SMatthias Ringwald             break;
3146bdecec7SMatthias Ringwald         default:
3156bdecec7SMatthias Ringwald             break;
3166bdecec7SMatthias Ringwald     }
3176bdecec7SMatthias Ringwald }
3186bdecec7SMatthias Ringwald 
319*814908c5SMatthias Ringwald 
battery_service_poll_timer_timeout_handler(btstack_timer_source_t * timer)3206bdecec7SMatthias Ringwald static void battery_service_poll_timer_timeout_handler(btstack_timer_source_t * timer){
3216bdecec7SMatthias Ringwald     uint16_t battery_service_cid = (uint16_t)(uintptr_t) btstack_run_loop_get_timer_context(timer);
3226bdecec7SMatthias Ringwald 
3236bdecec7SMatthias Ringwald     battery_service_client_t * client =  battery_service_get_client_for_cid(battery_service_cid);
3246bdecec7SMatthias Ringwald     btstack_assert(client != NULL);
3256bdecec7SMatthias Ringwald 
3266bdecec7SMatthias Ringwald     client->need_poll_bitmap = client->poll_bitmap;
327*814908c5SMatthias Ringwald     battery_service_client_request_send_gatt_query(client);
3286bdecec7SMatthias Ringwald }
3296bdecec7SMatthias Ringwald 
battery_service_poll_timer_start(battery_service_client_t * client)3306bdecec7SMatthias Ringwald static void battery_service_poll_timer_start(battery_service_client_t * client){
3316bdecec7SMatthias Ringwald     btstack_run_loop_set_timer_handler(&client->poll_timer,  battery_service_poll_timer_timeout_handler);
3326bdecec7SMatthias Ringwald     btstack_run_loop_set_timer_context(&client->poll_timer, (void *)(uintptr_t)client->cid);
3336bdecec7SMatthias Ringwald 
3346bdecec7SMatthias Ringwald     btstack_run_loop_set_timer(&client->poll_timer, client->poll_interval_ms);
335*814908c5SMatthias Ringwald     btstack_run_loop_remove_timer(&client->poll_timer);
3366bdecec7SMatthias Ringwald     btstack_run_loop_add_timer(&client->poll_timer);
3376bdecec7SMatthias Ringwald }
3386bdecec7SMatthias Ringwald 
battery_service_client_validate_service(battery_service_client_t * client)3394436e856SMilanka Ringwald static void battery_service_client_validate_service(battery_service_client_t * client){
3404436e856SMilanka Ringwald     // remove all services without characteristic (array in-place)
3414436e856SMilanka Ringwald     uint8_t src_index  = 0;  // next entry to check
3424436e856SMilanka Ringwald     uint8_t dest_index = 0;  // to store entry
3434436e856SMilanka Ringwald     for (src_index = 0; src_index < client->num_instances; src_index++){
3444436e856SMilanka Ringwald         if (client->services[src_index].value_handle != 0){
3454436e856SMilanka Ringwald             if (src_index != dest_index) {
3464436e856SMilanka Ringwald                 client->services[dest_index] = client->services[src_index];
3474436e856SMilanka Ringwald             }
3484436e856SMilanka Ringwald             dest_index++;
3494436e856SMilanka Ringwald         }
3504436e856SMilanka Ringwald     }
3514436e856SMilanka Ringwald     client->num_instances = dest_index;
3524436e856SMilanka Ringwald }
3534436e856SMilanka Ringwald 
3544eb38249SMatthias Ringwald // @return true if client valid / run function should be called
battery_service_client_handle_query_complete_for_connection_setup(battery_service_client_t * client,uint8_t status)355*814908c5SMatthias Ringwald static bool battery_service_client_handle_query_complete_for_connection_setup(battery_service_client_t * client, uint8_t status){
3564eb38249SMatthias Ringwald     switch (client->state){
3574eb38249SMatthias Ringwald         case BATTERY_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
3584eb38249SMatthias Ringwald             if (status != ATT_ERROR_SUCCESS){
3594eb38249SMatthias Ringwald                 battery_service_emit_connection_established(client, status);
3604eb38249SMatthias Ringwald                 battery_service_finalize_client(client);
3614eb38249SMatthias Ringwald                 return false;
3624eb38249SMatthias Ringwald             }
3634eb38249SMatthias Ringwald 
3644eb38249SMatthias Ringwald             if (client->num_instances == 0){
3654eb38249SMatthias Ringwald                 battery_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
3664eb38249SMatthias Ringwald                 battery_service_finalize_client(client);
3674eb38249SMatthias Ringwald                 return false;
3684eb38249SMatthias Ringwald             }
3694eb38249SMatthias Ringwald 
3704eb38249SMatthias Ringwald             client->service_index = 0;
3714eb38249SMatthias Ringwald             client->state = BATTERY_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
3724eb38249SMatthias Ringwald             break;
3734eb38249SMatthias Ringwald 
3744eb38249SMatthias Ringwald         case BATTERY_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
3754eb38249SMatthias Ringwald             if (status != ATT_ERROR_SUCCESS){
3764eb38249SMatthias Ringwald                 battery_service_emit_connection_established(client, status);
3774eb38249SMatthias Ringwald                 battery_service_finalize_client(client);
3784eb38249SMatthias Ringwald                 return false;
3794eb38249SMatthias Ringwald             }
3804eb38249SMatthias Ringwald 
3814eb38249SMatthias Ringwald             // check if there is another service to query
3824eb38249SMatthias Ringwald             if ((client->service_index + 1) < client->num_instances){
3834eb38249SMatthias Ringwald                 client->service_index++;
3844eb38249SMatthias Ringwald                 client->state = BATTERY_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
3854eb38249SMatthias Ringwald                 break;
3864eb38249SMatthias Ringwald             }
3874eb38249SMatthias Ringwald 
3884eb38249SMatthias Ringwald             battery_service_client_validate_service(client);
3894eb38249SMatthias Ringwald 
3904eb38249SMatthias Ringwald             if (client->num_instances == 0){
3914eb38249SMatthias Ringwald                 battery_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
3924eb38249SMatthias Ringwald                 battery_service_finalize_client(client);
3934eb38249SMatthias Ringwald                 return false;
3944eb38249SMatthias Ringwald             }
3954eb38249SMatthias Ringwald 
396637a9965SMatthias Ringwald             // we are done with querying all services
3974eb38249SMatthias Ringwald             client->service_index = 0;
3984eb38249SMatthias Ringwald 
3994eb38249SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
4004eb38249SMatthias Ringwald             client->state = BATTERY_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
4014eb38249SMatthias Ringwald #else
4024eb38249SMatthias Ringwald             // wait for notification registration
4034eb38249SMatthias Ringwald             // to send connection established event
4044eb38249SMatthias Ringwald             client->state = BATTERY_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
4054eb38249SMatthias Ringwald #endif
4064eb38249SMatthias Ringwald             break;
4074eb38249SMatthias Ringwald 
4084eb38249SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
4094eb38249SMatthias Ringwald             case BATTERY_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT:
4104eb38249SMatthias Ringwald                     if ((client->service_index + 1) < client->num_instances){
4114eb38249SMatthias Ringwald                         client->service_index++;
4124eb38249SMatthias Ringwald                         client->state = BATTERY_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
4134eb38249SMatthias Ringwald                         break;
4144eb38249SMatthias Ringwald                     }
4154eb38249SMatthias Ringwald                     client->service_index = 0;
4164eb38249SMatthias Ringwald                     client->state = BATTERY_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
4174eb38249SMatthias Ringwald                     break;
4184eb38249SMatthias Ringwald 
4194eb38249SMatthias Ringwald                 case BATTERY_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
4204eb38249SMatthias Ringwald                     client->state = BATTERY_SERVICE_CLIENT_STATE_CONNECTED;
4214eb38249SMatthias Ringwald                     battery_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
422*814908c5SMatthias Ringwald                     if (battery_service_is_polling_needed(client)){
423*814908c5SMatthias Ringwald                         battery_service_start_polling(client);
424*814908c5SMatthias Ringwald                         return false;
425*814908c5SMatthias Ringwald                     }
4264eb38249SMatthias Ringwald                     break;
4274eb38249SMatthias Ringwald #endif
4284eb38249SMatthias Ringwald         case BATTERY_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED:
4294eb38249SMatthias Ringwald             if ((client->service_index + 1) < client->num_instances){
4304eb38249SMatthias Ringwald                 client->service_index++;
4314eb38249SMatthias Ringwald                 client->state = BATTERY_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
4324eb38249SMatthias Ringwald                 break;
4334eb38249SMatthias Ringwald             }
4344eb38249SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
4354eb38249SMatthias Ringwald             for (client->service_index = 0; client->service_index < client->num_instances; client->service_index++){
4364eb38249SMatthias Ringwald                 bool need_polling = (client->poll_bitmap & (1 << client->service_index)) != 0;
4374eb38249SMatthias Ringwald                 printf("read CCC 1 0x%02x, polling %d \n", client->services[client->service_index].ccc_handle, (int) need_polling);
4384eb38249SMatthias Ringwald                 if ( (client->services[client->service_index].ccc_handle != 0) && !need_polling ) {
4394eb38249SMatthias Ringwald                     client->state = BATTERY_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
4404eb38249SMatthias Ringwald                     break;
4414eb38249SMatthias Ringwald                 }
4424eb38249SMatthias Ringwald             }
4434eb38249SMatthias Ringwald #endif
4444eb38249SMatthias Ringwald             client->state = BATTERY_SERVICE_CLIENT_STATE_CONNECTED;
4454eb38249SMatthias Ringwald             battery_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
446*814908c5SMatthias Ringwald             if (battery_service_is_polling_needed(client)){
447*814908c5SMatthias Ringwald                 battery_service_start_polling(client);
448*814908c5SMatthias Ringwald                 return false;
449*814908c5SMatthias Ringwald             }
4504eb38249SMatthias Ringwald             break;
4514eb38249SMatthias Ringwald 
452*814908c5SMatthias Ringwald         default:
453*814908c5SMatthias Ringwald             return false;
454*814908c5SMatthias Ringwald 
455*814908c5SMatthias Ringwald     }
456*814908c5SMatthias Ringwald     return true;
457*814908c5SMatthias Ringwald }
458*814908c5SMatthias Ringwald 
battery_service_client_handle_query_complete(battery_service_client_t * client,uint8_t status)459*814908c5SMatthias Ringwald static bool battery_service_client_handle_query_complete(battery_service_client_t * client, uint8_t status){
460*814908c5SMatthias Ringwald     switch (client->state){
461*814908c5SMatthias Ringwald 
462*814908c5SMatthias Ringwald         case BATTERY_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_VALUE_READ:
4634eb38249SMatthias Ringwald             if (client->polled_service_index != BATTERY_SERVICE_INVALID_INDEX){
4644eb38249SMatthias Ringwald                 if (status != ATT_ERROR_SUCCESS){
4654eb38249SMatthias Ringwald                     battery_service_emit_battery_level(client, client->services[client->polled_service_index].value_handle, status, 0);
4664eb38249SMatthias Ringwald                 }
4674eb38249SMatthias Ringwald                 client->polled_service_index = BATTERY_SERVICE_INVALID_INDEX;
4684eb38249SMatthias Ringwald             }
469*814908c5SMatthias Ringwald             client->state = BATTERY_SERVICE_CLIENT_STATE_CONNECTED;
470*814908c5SMatthias Ringwald             return (client->need_poll_bitmap != 0u);
4714eb38249SMatthias Ringwald 
4724eb38249SMatthias Ringwald         default:
4734eb38249SMatthias Ringwald             break;
4744eb38249SMatthias Ringwald 
4754eb38249SMatthias Ringwald     }
476*814908c5SMatthias Ringwald     return false;
4774eb38249SMatthias Ringwald }
4784eb38249SMatthias Ringwald 
handle_gatt_client_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)4796bdecec7SMatthias Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
480b1ad49f9SMilanka Ringwald     UNUSED(packet_type);
481b1ad49f9SMilanka Ringwald     UNUSED(channel);
48298ef7bd9SMatthias Ringwald     UNUSED(size);
4836bdecec7SMatthias Ringwald 
484813fc1b9SMilanka Ringwald     battery_service_client_t * client = NULL;
4856bdecec7SMatthias Ringwald     gatt_client_service_t service;
4866bdecec7SMatthias Ringwald     gatt_client_characteristic_t characteristic;
487*814908c5SMatthias Ringwald     bool send_next_gatt_query = false;
4886bdecec7SMatthias Ringwald 
4896bdecec7SMatthias Ringwald     switch(hci_event_packet_get_type(packet)){
4906bdecec7SMatthias Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
4916bdecec7SMatthias Ringwald             client = battery_service_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
4926bdecec7SMatthias Ringwald             btstack_assert(client != NULL);
4936bdecec7SMatthias Ringwald 
4946bdecec7SMatthias Ringwald             if (client->num_instances < MAX_NUM_BATTERY_SERVICES){
4956bdecec7SMatthias Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
4966bdecec7SMatthias Ringwald                 client->services[client->num_instances].start_handle = service.start_group_handle;
4976bdecec7SMatthias Ringwald                 client->services[client->num_instances].end_handle = service.end_group_handle;
49886e34143SMilanka Ringwald 
49986e34143SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
50086e34143SMilanka Ringwald                 printf("Battery Service: start handle 0x%04X, end handle 0x%04X\n", client->services[client->num_instances].start_handle, client->services[client->num_instances].end_handle);
50186e34143SMilanka Ringwald #endif
5026bdecec7SMatthias Ringwald                 client->num_instances++;
503dd57ce9fSMilanka Ringwald             } else {
504dd57ce9fSMilanka Ringwald                 log_info("Found more then %d, Battery Service instances. Increase MAX_NUM_BATTERY_SERVICES to store all.", MAX_NUM_BATTERY_SERVICES);
505dd57ce9fSMilanka Ringwald             }
506*814908c5SMatthias Ringwald             // for sending next query w4 GATT_EVENT_QUERY_COMPLETE
5076bdecec7SMatthias Ringwald             break;
5086bdecec7SMatthias Ringwald 
5096bdecec7SMatthias Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
5106bdecec7SMatthias Ringwald             client = battery_service_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
5116bdecec7SMatthias Ringwald             btstack_assert(client != NULL);
5126bdecec7SMatthias Ringwald 
5136bdecec7SMatthias Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
5142da54b5bSMilanka Ringwald             btstack_assert(client->service_index < client->num_instances);
515a3d5cd9fSMilanka Ringwald             btstack_assert(characteristic.uuid16 == ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL);
516a3d5cd9fSMilanka Ringwald 
5176d1993e0SMilanka Ringwald             client->services[client->service_index].value_handle = characteristic.value_handle;
5186d1993e0SMilanka Ringwald             client->services[client->service_index].properties = characteristic.properties;
519813fc1b9SMilanka Ringwald 
520813fc1b9SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
521813fc1b9SMilanka Ringwald             printf("Battery Level Characteristic:\n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d\n",
522813fc1b9SMilanka Ringwald                 // hid_characteristic_name(characteristic.uuid16),
523813fc1b9SMilanka Ringwald                 characteristic.start_handle,
524813fc1b9SMilanka Ringwald                 characteristic.properties,
525813fc1b9SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
526813fc1b9SMilanka Ringwald                 client->service_index);
527813fc1b9SMilanka Ringwald #endif
528*814908c5SMatthias Ringwald             // for sending next query w4 GATT_EVENT_QUERY_COMPLETE
5296bdecec7SMatthias Ringwald             break;
5306bdecec7SMatthias Ringwald 
5316bdecec7SMatthias Ringwald         case GATT_EVENT_NOTIFICATION:
5323e103d3cSMilanka Ringwald             if (gatt_event_notification_get_value_length(packet) != 1) break;
5336bdecec7SMatthias Ringwald 
5346bdecec7SMatthias Ringwald             client = battery_service_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
5356bdecec7SMatthias Ringwald             btstack_assert(client != NULL);
5366bdecec7SMatthias Ringwald 
5376bdecec7SMatthias Ringwald             battery_service_emit_battery_level(client,
5386bdecec7SMatthias Ringwald                 gatt_event_notification_get_value_handle(packet),
5396bdecec7SMatthias Ringwald                 ATT_ERROR_SUCCESS,
5406bdecec7SMatthias Ringwald                 gatt_event_notification_get_value(packet)[0]);
541*814908c5SMatthias Ringwald 
5426bdecec7SMatthias Ringwald             break;
5436bdecec7SMatthias Ringwald 
5446bdecec7SMatthias Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
5456bdecec7SMatthias Ringwald             client = battery_service_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
5466bdecec7SMatthias Ringwald             btstack_assert(client != NULL);
5476bdecec7SMatthias Ringwald 
5487594dd72SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
5492da54b5bSMilanka Ringwald             if (client->state == BATTERY_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT){
5507594dd72SMilanka Ringwald                 printf("    Received CCC value: ");
5517594dd72SMilanka Ringwald                 printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet),  gatt_event_characteristic_value_query_result_get_value_length(packet));
5527594dd72SMilanka Ringwald                 break;
5532da54b5bSMilanka Ringwald             }
5547594dd72SMilanka Ringwald #endif
5556bdecec7SMatthias Ringwald             if (gatt_event_characteristic_value_query_result_get_value_length(packet) != 1) break;
5566bdecec7SMatthias Ringwald 
5576bdecec7SMatthias Ringwald             battery_service_emit_battery_level(client,
5586bdecec7SMatthias Ringwald                 gatt_event_characteristic_value_query_result_get_value_handle(packet),
5596bdecec7SMatthias Ringwald                 ATT_ERROR_SUCCESS,
5606bdecec7SMatthias Ringwald                 gatt_event_characteristic_value_query_result_get_value(packet)[0]);
561*814908c5SMatthias Ringwald             // reset need_poll_bitmap in GATT_EVENT_QUERY_COMPLETE
5627594dd72SMilanka Ringwald             break;
5637594dd72SMilanka Ringwald 
564813fc1b9SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
565813fc1b9SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:{
566813fc1b9SMilanka Ringwald             gatt_client_characteristic_descriptor_t characteristic_descriptor;
567813fc1b9SMilanka Ringwald 
568813fc1b9SMilanka Ringwald             client = battery_service_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
569813fc1b9SMilanka Ringwald             btstack_assert(client != NULL);
570813fc1b9SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
571813fc1b9SMilanka Ringwald 
572813fc1b9SMilanka Ringwald             if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
5737594dd72SMilanka Ringwald                 client->services[client->service_index].ccc_handle = characteristic_descriptor.handle;
5747594dd72SMilanka Ringwald 
5757594dd72SMilanka Ringwald                 printf("    Battery Level Client Characteristic Configuration Descriptor[%d]:  Handle 0x%04X, UUID 0x%04X\n",
5767594dd72SMilanka Ringwald                     client->service_index,
577813fc1b9SMilanka Ringwald                     characteristic_descriptor.handle,
578813fc1b9SMilanka Ringwald                     characteristic_descriptor.uuid16);
579813fc1b9SMilanka Ringwald             }
580813fc1b9SMilanka Ringwald             break;
581*814908c5SMatthias Ringwald             // for sending next query w4 GATT_EVENT_QUERY_COMPLETE
582813fc1b9SMilanka Ringwald         }
583813fc1b9SMilanka Ringwald #endif
584813fc1b9SMilanka Ringwald 
5856bdecec7SMatthias Ringwald         case GATT_EVENT_QUERY_COMPLETE:
5866bdecec7SMatthias Ringwald             client = battery_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
5876bdecec7SMatthias Ringwald             btstack_assert(client != NULL);
588*814908c5SMatthias Ringwald 
589*814908c5SMatthias Ringwald             // 1. handle service establishment/notification subscription query results (client->state < BATTERY_SERVICE_CLIENT_STATE_CONNECTED)
590*814908c5SMatthias Ringwald             if (client->state < BATTERY_SERVICE_CLIENT_STATE_CONNECTED){
591*814908c5SMatthias Ringwald                 send_next_gatt_query = battery_service_client_handle_query_complete_for_connection_setup(client, gatt_event_query_complete_get_att_status(packet));
592*814908c5SMatthias Ringwald                 break;
593*814908c5SMatthias Ringwald             }
594*814908c5SMatthias Ringwald 
595*814908c5SMatthias Ringwald             // 2. handle battery value query result when devices is connected
596*814908c5SMatthias Ringwald             send_next_gatt_query = battery_service_client_handle_query_complete(client, gatt_event_query_complete_get_att_status(packet));
597*814908c5SMatthias Ringwald             if (!send_next_gatt_query){
598*814908c5SMatthias Ringwald                 // if there are no further queries, and we're connected, trigger next polling read
599*814908c5SMatthias Ringwald                 if (battery_service_is_polling_needed(client)){
600*814908c5SMatthias Ringwald                     battery_service_poll_timer_start(client);
601*814908c5SMatthias Ringwald                 }
602*814908c5SMatthias Ringwald             }
6036bdecec7SMatthias Ringwald             break;
6046bdecec7SMatthias Ringwald 
6056bdecec7SMatthias Ringwald         default:
6066bdecec7SMatthias Ringwald             break;
6076bdecec7SMatthias Ringwald     }
608813fc1b9SMilanka Ringwald 
609*814908c5SMatthias Ringwald     if (send_next_gatt_query){
610*814908c5SMatthias Ringwald         battery_service_client_request_send_gatt_query(client);
611*814908c5SMatthias Ringwald         return;
612813fc1b9SMilanka Ringwald     }
6136bdecec7SMatthias Ringwald }
6146bdecec7SMatthias Ringwald 
6156bdecec7SMatthias Ringwald 
handle_hci_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)616637a9965SMatthias Ringwald static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
617637a9965SMatthias Ringwald     UNUSED(packet_type); // ok: only hci events
618637a9965SMatthias Ringwald     UNUSED(channel);     // ok: there is no channel
619637a9965SMatthias Ringwald     UNUSED(size);        // ok: fixed format events read from HCI buffer
620637a9965SMatthias Ringwald 
621637a9965SMatthias Ringwald     hci_con_handle_t con_handle;
622637a9965SMatthias Ringwald     battery_service_client_t * client;
623637a9965SMatthias Ringwald 
624637a9965SMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
625637a9965SMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
626637a9965SMatthias Ringwald             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
627637a9965SMatthias Ringwald             client = battery_service_get_client_for_con_handle(con_handle);
628f688bdb8SMatthias Ringwald             if (client != NULL){
629637a9965SMatthias Ringwald                 // finalize
630637a9965SMatthias Ringwald                 uint16_t cid = client->cid;
631637a9965SMatthias Ringwald                 battery_service_finalize_client(client);
632637a9965SMatthias Ringwald                 // TODO: emit disconnected event
633637a9965SMatthias Ringwald                 UNUSED(cid);
634637a9965SMatthias Ringwald             }
635637a9965SMatthias Ringwald             break;
636637a9965SMatthias Ringwald         default:
637637a9965SMatthias Ringwald             break;
638637a9965SMatthias Ringwald     }
639637a9965SMatthias Ringwald }
640637a9965SMatthias Ringwald 
battery_service_client_connect(hci_con_handle_t con_handle,btstack_packet_handler_t packet_handler,uint32_t poll_interval_ms,uint16_t * battery_service_cid)6416bdecec7SMatthias Ringwald uint8_t battery_service_client_connect(hci_con_handle_t con_handle, btstack_packet_handler_t packet_handler, uint32_t poll_interval_ms, uint16_t * battery_service_cid){
6426bdecec7SMatthias Ringwald     btstack_assert(packet_handler != NULL);
6436bdecec7SMatthias Ringwald 
6446bdecec7SMatthias Ringwald     battery_service_client_t * client = battery_service_get_client_for_con_handle(con_handle);
6456bdecec7SMatthias Ringwald     if (client != NULL){
6466bdecec7SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
6476bdecec7SMatthias Ringwald     }
6486bdecec7SMatthias Ringwald 
6496bdecec7SMatthias Ringwald     uint16_t cid = battery_service_get_next_cid();
6506bdecec7SMatthias Ringwald     if (battery_service_cid != NULL) {
6516bdecec7SMatthias Ringwald         *battery_service_cid = cid;
6526bdecec7SMatthias Ringwald     }
6536bdecec7SMatthias Ringwald 
6546bdecec7SMatthias Ringwald     client = battery_service_create_client(con_handle, cid);
6556bdecec7SMatthias Ringwald     if (client == NULL) {
6566bdecec7SMatthias Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
6576bdecec7SMatthias Ringwald     }
6586bdecec7SMatthias Ringwald 
6596bdecec7SMatthias Ringwald     client->client_handler = packet_handler;
6606bdecec7SMatthias Ringwald     client->poll_interval_ms = poll_interval_ms;
6616bdecec7SMatthias Ringwald     client->state = BATTERY_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE;
662*814908c5SMatthias Ringwald 
663*814908c5SMatthias Ringwald     uint8_t status = battery_service_client_request_send_gatt_query(client);
664*814908c5SMatthias Ringwald     if (status != ERROR_CODE_SUCCESS){
665*814908c5SMatthias Ringwald         client->state = BATTERY_SERVICE_CLIENT_STATE_IDLE;
666*814908c5SMatthias Ringwald     }
667*814908c5SMatthias Ringwald     return status;
6686bdecec7SMatthias Ringwald }
6696bdecec7SMatthias Ringwald 
battery_service_client_disconnect(uint16_t battery_service_cid)6706bdecec7SMatthias Ringwald uint8_t battery_service_client_disconnect(uint16_t battery_service_cid){
6716bdecec7SMatthias Ringwald     battery_service_client_t * client = battery_service_get_client_for_cid(battery_service_cid);
6726bdecec7SMatthias Ringwald     if (client == NULL){
6736bdecec7SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
6746bdecec7SMatthias Ringwald     }
6756bdecec7SMatthias Ringwald     // finalize connections
6766bdecec7SMatthias Ringwald     battery_service_finalize_client(client);
6776bdecec7SMatthias Ringwald     return ERROR_CODE_SUCCESS;
6786bdecec7SMatthias Ringwald }
6796bdecec7SMatthias Ringwald 
680*814908c5SMatthias Ringwald 
battery_service_client_read_battery_level(uint16_t battery_service_cid,uint8_t service_index)6816bdecec7SMatthias Ringwald uint8_t battery_service_client_read_battery_level(uint16_t battery_service_cid, uint8_t service_index){
6826bdecec7SMatthias Ringwald     battery_service_client_t * client = battery_service_get_client_for_cid(battery_service_cid);
6836bdecec7SMatthias Ringwald     if (client == NULL) {
6846bdecec7SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
6856bdecec7SMatthias Ringwald     }
686*814908c5SMatthias Ringwald     if (client->state < BATTERY_SERVICE_CLIENT_STATE_CONNECTED) {
6876bdecec7SMatthias Ringwald         return GATT_CLIENT_IN_WRONG_STATE;
6886bdecec7SMatthias Ringwald     }
6896bdecec7SMatthias Ringwald     if (service_index >= client->num_instances) {
6906bdecec7SMatthias Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
6916bdecec7SMatthias Ringwald     }
6927594dd72SMilanka Ringwald 
6936bdecec7SMatthias Ringwald     client->need_poll_bitmap |= (1u << service_index);
694*814908c5SMatthias Ringwald 
695*814908c5SMatthias Ringwald     uint8_t status = battery_service_client_request_send_gatt_query(client);
696*814908c5SMatthias Ringwald     if (status != ERROR_CODE_SUCCESS){
697*814908c5SMatthias Ringwald         client->need_poll_bitmap &= ~(1u << client->polled_service_index);
698*814908c5SMatthias Ringwald     }
699*814908c5SMatthias Ringwald     return status;
7006bdecec7SMatthias Ringwald }
7016bdecec7SMatthias Ringwald 
battery_service_client_init(void)702637a9965SMatthias Ringwald void battery_service_client_init(void){
703637a9965SMatthias Ringwald     hci_event_callback_registration.callback = &handle_hci_event;
704637a9965SMatthias Ringwald     hci_add_event_handler(&hci_event_callback_registration);
705*814908c5SMatthias Ringwald     battery_service_handle_can_send_now.callback = &battery_service_send_next_query;
706637a9965SMatthias Ringwald }
7076bdecec7SMatthias Ringwald 
battery_service_client_deinit(void)7084011bbc5SMilanka Ringwald void battery_service_client_deinit(void){
7094011bbc5SMilanka Ringwald     battery_service_cid_counter = 0;
7104011bbc5SMilanka Ringwald     clients = NULL;
7114011bbc5SMilanka Ringwald }
7126bdecec7SMatthias Ringwald 
713