xref: /btstack/src/ble/gatt_service_client.c (revision bee7a0d93a1fd5b88a1f3784d1ffa9fce6d8c9bc)
112879e75SMatthias Ringwald /*
212879e75SMatthias Ringwald  * Copyright (C) 2023 BlueKitchen GmbH
312879e75SMatthias Ringwald  *
412879e75SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
512879e75SMatthias Ringwald  * modification, are permitted provided that the following conditions
612879e75SMatthias Ringwald  * are met:
712879e75SMatthias Ringwald  *
812879e75SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
912879e75SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
1012879e75SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
1112879e75SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
1212879e75SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
1312879e75SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
1412879e75SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
1512879e75SMatthias Ringwald  *    from this software without specific prior written permission.
1612879e75SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
1712879e75SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
1812879e75SMatthias Ringwald  *    monetary gain.
1912879e75SMatthias Ringwald  *
2012879e75SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
2112879e75SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2212879e75SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2312879e75SMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
2412879e75SMatthias Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2512879e75SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2612879e75SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
2712879e75SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2812879e75SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2912879e75SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
3012879e75SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3112879e75SMatthias Ringwald  * SUCH DAMAGE.
3212879e75SMatthias Ringwald  *
3312879e75SMatthias Ringwald  * Please inquire about commercial licensing options at
3412879e75SMatthias Ringwald  * [email protected]
3512879e75SMatthias Ringwald  *
3612879e75SMatthias Ringwald  */
3712879e75SMatthias Ringwald 
3812879e75SMatthias Ringwald #define BTSTACK_FILE__ "gatt_service_client_helper.c"
3912879e75SMatthias Ringwald 
4012879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
4112879e75SMatthias Ringwald #include <stdio.h>
4212879e75SMatthias Ringwald #endif
4312879e75SMatthias Ringwald 
4412879e75SMatthias Ringwald #include <stdint.h>
4512879e75SMatthias Ringwald #include <string.h>
4612879e75SMatthias Ringwald 
4712879e75SMatthias Ringwald #include "ble/gatt_service_client.h"
4812879e75SMatthias Ringwald 
4912879e75SMatthias Ringwald #include "bluetooth_gatt.h"
5012879e75SMatthias Ringwald #include "btstack_debug.h"
5112879e75SMatthias Ringwald #include "btstack_event.h"
5212879e75SMatthias Ringwald 
5312879e75SMatthias Ringwald static void gatt_service_client_gatt_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
5412879e75SMatthias Ringwald 
5512879e75SMatthias Ringwald static bool gatt_service_client_intitialized = false;
5612879e75SMatthias Ringwald static uint16_t gatt_service_client_service_cid;
5712879e75SMatthias Ringwald static btstack_linked_list_t gatt_service_clients;
5812879e75SMatthias Ringwald static btstack_packet_callback_registration_t gatt_service_client_hci_callback_registration;
5912879e75SMatthias Ringwald 
6012879e75SMatthias Ringwald // LE Audio Service Client helper functions
gatt_service_client_finalize_connection(gatt_service_client_t * client,gatt_service_client_connection_t * connection)6112879e75SMatthias Ringwald static void gatt_service_client_finalize_connection(gatt_service_client_t * client, gatt_service_client_connection_t * connection){
6212879e75SMatthias Ringwald     btstack_assert(client != NULL);
6312879e75SMatthias Ringwald     btstack_linked_list_remove(&client->connections, (btstack_linked_item_t*) connection);
6412879e75SMatthias Ringwald }
6512879e75SMatthias Ringwald 
gatt_service_client_get_service_client_for_id(uint16_t service_cid)6612879e75SMatthias Ringwald static gatt_service_client_t * gatt_service_client_get_service_client_for_id(uint16_t service_cid){
6712879e75SMatthias Ringwald     btstack_linked_list_iterator_t it;
6812879e75SMatthias Ringwald     btstack_linked_list_iterator_init(&it,  &gatt_service_clients);
6912879e75SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
7012879e75SMatthias Ringwald         gatt_service_client_t * service_client = (gatt_service_client_t *)btstack_linked_list_iterator_next(&it);
7112879e75SMatthias Ringwald         if (service_client->service_id == service_cid) {
7212879e75SMatthias Ringwald             return service_client;
7312879e75SMatthias Ringwald         };
7412879e75SMatthias Ringwald     }
7512879e75SMatthias Ringwald     return NULL;
7612879e75SMatthias Ringwald }
7712879e75SMatthias Ringwald 
gatt_service_client_get_connection_for_con_handle_and_service_index(const gatt_service_client_t * client,hci_con_handle_t con_handle,uint8_t service_index)7812879e75SMatthias Ringwald static gatt_service_client_connection_t * gatt_service_client_get_connection_for_con_handle_and_service_index(const gatt_service_client_t * client, hci_con_handle_t con_handle, uint8_t service_index){
7912879e75SMatthias Ringwald     btstack_linked_list_iterator_t it;
8012879e75SMatthias Ringwald     btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections);
8112879e75SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
8212879e75SMatthias Ringwald         gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it);
8312879e75SMatthias Ringwald         if (connection->con_handle != con_handle) continue;
8412879e75SMatthias Ringwald         if (connection->service_index != service_index) continue;
8512879e75SMatthias Ringwald         return connection;
8612879e75SMatthias Ringwald     }
8712879e75SMatthias Ringwald     return NULL;
8812879e75SMatthias Ringwald }
8912879e75SMatthias Ringwald 
gatt_service_client_get_connection_for_con_handle_and_attribute_handle(const gatt_service_client_t * client,hci_con_handle_t con_handle,uint16_t attribute_handle)90*bee7a0d9SMatthias Ringwald static gatt_service_client_connection_t * gatt_service_client_get_connection_for_con_handle_and_attribute_handle(const gatt_service_client_t * client, hci_con_handle_t con_handle, uint16_t attribute_handle){
91*bee7a0d9SMatthias Ringwald     btstack_linked_list_iterator_t it;
92*bee7a0d9SMatthias Ringwald     btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections);
93*bee7a0d9SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
94*bee7a0d9SMatthias Ringwald         gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it);
95*bee7a0d9SMatthias Ringwald         if (connection->con_handle != con_handle) continue;
96*bee7a0d9SMatthias Ringwald         if (attribute_handle < connection->start_handle) continue;
97*bee7a0d9SMatthias Ringwald         if (attribute_handle > connection->end_handle) continue;
98*bee7a0d9SMatthias Ringwald         return connection;
99*bee7a0d9SMatthias Ringwald     }
100*bee7a0d9SMatthias Ringwald     return NULL;
101*bee7a0d9SMatthias Ringwald }
102*bee7a0d9SMatthias Ringwald 
gatt_service_client_get_connection_for_cid(const gatt_service_client_t * client,uint16_t connection_cid)10312879e75SMatthias Ringwald static gatt_service_client_connection_t * gatt_service_client_get_connection_for_cid(
10412879e75SMatthias Ringwald         const gatt_service_client_t *client, uint16_t connection_cid){
10512879e75SMatthias Ringwald     btstack_linked_list_iterator_t it;
10612879e75SMatthias Ringwald     btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections);
10712879e75SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
10812879e75SMatthias Ringwald         gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it);
10912879e75SMatthias Ringwald         if (connection->cid != connection_cid) continue;
11012879e75SMatthias Ringwald         return connection;
11112879e75SMatthias Ringwald     }
11212879e75SMatthias Ringwald     return NULL;
11312879e75SMatthias Ringwald }
11412879e75SMatthias Ringwald 
gatt_service_client_get_mtu(const gatt_service_client_connection_t * connection)11512879e75SMatthias Ringwald uint16_t gatt_service_client_get_mtu(const gatt_service_client_connection_t *connection) {
11612879e75SMatthias Ringwald     uint16_t mtu = 0;
11712879e75SMatthias Ringwald     gatt_client_get_mtu(connection->con_handle, &mtu);
11812879e75SMatthias Ringwald     return mtu;
11912879e75SMatthias Ringwald }
12012879e75SMatthias Ringwald 
gatt_service_client_get_connection_id(const gatt_service_client_connection_t * connection)12112879e75SMatthias Ringwald uint16_t gatt_service_client_get_connection_id(const gatt_service_client_connection_t * connection){
12212879e75SMatthias Ringwald     return connection->cid;
12312879e75SMatthias Ringwald 
12412879e75SMatthias Ringwald }
12512879e75SMatthias Ringwald 
gatt_service_client_get_con_handle(const gatt_service_client_connection_t * connection)12612879e75SMatthias Ringwald hci_con_handle_t gatt_service_client_get_con_handle(const gatt_service_client_connection_t * connection){
12712879e75SMatthias Ringwald     return connection->con_handle;
12812879e75SMatthias Ringwald }
12912879e75SMatthias Ringwald 
gatt_service_client_get_service_index(const gatt_service_client_connection_t * connection)13012879e75SMatthias Ringwald uint8_t gatt_service_client_get_service_index(const gatt_service_client_connection_t * connection){
13112879e75SMatthias Ringwald     return connection->service_index;
13212879e75SMatthias Ringwald }
13312879e75SMatthias Ringwald 
gatt_service_client_characteristic_uuid16_for_index(const gatt_service_client_t * client,uint8_t characteristic_index)13412879e75SMatthias Ringwald uint16_t gatt_service_client_characteristic_uuid16_for_index(const gatt_service_client_t * client, uint8_t characteristic_index){
13512879e75SMatthias Ringwald     if (characteristic_index < client->characteristics_desc16_num){
13612879e75SMatthias Ringwald         return client->characteristics_desc16[characteristic_index];
13712879e75SMatthias Ringwald     } else {
13812879e75SMatthias Ringwald         return 0;
13912879e75SMatthias Ringwald     }
14012879e75SMatthias Ringwald }
14112879e75SMatthias Ringwald 
gatt_service_client_characteristic_value_handle_for_index(const gatt_service_client_connection_t * connection,uint8_t characteristic_index)14212879e75SMatthias Ringwald uint16_t gatt_service_client_characteristic_value_handle_for_index(const gatt_service_client_connection_t *connection,
14312879e75SMatthias Ringwald                                                                    uint8_t characteristic_index) {
14412879e75SMatthias Ringwald     return connection->characteristics[characteristic_index].value_handle;
14512879e75SMatthias Ringwald }
14612879e75SMatthias Ringwald 
gatt_service_client_characteristic_index_for_value_handle(const gatt_service_client_connection_t * connection,uint16_t value_handle)14712879e75SMatthias Ringwald uint8_t gatt_service_client_characteristic_index_for_value_handle(const gatt_service_client_connection_t *connection,
14812879e75SMatthias Ringwald                                                                   uint16_t value_handle) {
14912879e75SMatthias Ringwald     for (int i = 0; i < connection->client->characteristics_desc16_num; i++){
15012879e75SMatthias Ringwald         if (connection->characteristics[i].value_handle == value_handle) {
15112879e75SMatthias Ringwald             return i;
15212879e75SMatthias Ringwald         }
15312879e75SMatthias Ringwald     }
15412879e75SMatthias Ringwald     return GATT_SERVICE_CLIENT_INVALID_INDEX;
15512879e75SMatthias Ringwald }
15612879e75SMatthias Ringwald 
15712879e75SMatthias Ringwald 
gatt_service_client_emit_connected(btstack_packet_handler_t event_callback,hci_con_handle_t con_handle,uint16_t cid,uint8_t status)15812879e75SMatthias Ringwald static void gatt_service_client_emit_connected(btstack_packet_handler_t event_callback, hci_con_handle_t con_handle, uint16_t cid, uint8_t status){
15912879e75SMatthias Ringwald     btstack_assert(event_callback != NULL);
16012879e75SMatthias Ringwald 
16112879e75SMatthias Ringwald     log_info("GATT Client Helper, connected 0x%04x, status 0x%02x", con_handle, status);
16212879e75SMatthias Ringwald 
16312879e75SMatthias Ringwald     uint8_t event[9];
16412879e75SMatthias Ringwald     int pos = 0;
16512879e75SMatthias Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
16612879e75SMatthias Ringwald     event[pos++] = sizeof(event) - 2;
16712879e75SMatthias Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_CLIENT_CONNECTED;
16812879e75SMatthias Ringwald     little_endian_store_16(event, pos, con_handle);
16912879e75SMatthias Ringwald     pos += 2;
17012879e75SMatthias Ringwald     little_endian_store_16(event, pos, cid);
17112879e75SMatthias Ringwald     pos += 2;
17212879e75SMatthias Ringwald     event[pos++] = 0; // num included services
17312879e75SMatthias Ringwald     event[pos++] = status;
17412879e75SMatthias Ringwald     (*event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
17512879e75SMatthias Ringwald }
17612879e75SMatthias Ringwald 
gatt_service_client_emit_disconnected(btstack_packet_handler_t event_callback,hci_con_handle_t con_handle,uint16_t cid)17712879e75SMatthias Ringwald static void gatt_service_client_emit_disconnected(btstack_packet_handler_t event_callback, hci_con_handle_t con_handle, uint16_t cid){
17812879e75SMatthias Ringwald     btstack_assert(event_callback != NULL);
17912879e75SMatthias Ringwald 
18012879e75SMatthias Ringwald     uint8_t event[7];
18112879e75SMatthias Ringwald     int pos = 0;
18212879e75SMatthias Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
18312879e75SMatthias Ringwald     event[pos++] = sizeof(event) - 2;
18412879e75SMatthias Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_CLIENT_DISCONNECTED;
18512879e75SMatthias Ringwald     little_endian_store_16(event, pos, con_handle);
18612879e75SMatthias Ringwald     pos += 2;
18712879e75SMatthias Ringwald     little_endian_store_16(event, pos, cid);
18812879e75SMatthias Ringwald     pos += 2;
18912879e75SMatthias Ringwald     (*event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
19012879e75SMatthias Ringwald }
19112879e75SMatthias Ringwald 
gatt_service_client_get_next_cid(gatt_service_client_t * client)19212879e75SMatthias Ringwald static uint16_t gatt_service_client_get_next_cid(gatt_service_client_t * client){
19312879e75SMatthias Ringwald     client->cid_counter = btstack_next_cid_ignoring_zero(client->cid_counter);
19412879e75SMatthias Ringwald     return client->cid_counter;
19512879e75SMatthias Ringwald }
19612879e75SMatthias Ringwald 
gatt_service_client_more_descriptor_queries(const gatt_service_client_t * client,gatt_service_client_connection_t * connection)19712879e75SMatthias Ringwald static bool gatt_service_client_more_descriptor_queries(const gatt_service_client_t * client, gatt_service_client_connection_t * connection) {
19812879e75SMatthias Ringwald     bool next_query_found = false;
19912879e75SMatthias Ringwald     while (!next_query_found && (connection->characteristic_index < client->characteristics_desc16_num)) {
20012879e75SMatthias Ringwald         uint16_t notify_or_indicate = ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE;
20112879e75SMatthias Ringwald         if ((connection->characteristics[connection->characteristic_index].properties & notify_or_indicate) != 0u){
20212879e75SMatthias Ringwald             next_query_found = true;
20312879e75SMatthias Ringwald             break;
20412879e75SMatthias Ringwald         }
20512879e75SMatthias Ringwald         connection->characteristic_index++;
20612879e75SMatthias Ringwald     }
20712879e75SMatthias Ringwald     return next_query_found;
20812879e75SMatthias Ringwald }
20912879e75SMatthias Ringwald 
gatt_service_client_have_more_notifications_to_enable(const gatt_service_client_t * client,gatt_service_client_connection_t * connection)21012879e75SMatthias Ringwald static bool gatt_service_client_have_more_notifications_to_enable(const gatt_service_client_t * client, gatt_service_client_connection_t * connection) {
21112879e75SMatthias Ringwald     bool next_query_found = false;
21212879e75SMatthias Ringwald     while (!next_query_found && (connection->characteristic_index < client->characteristics_desc16_num)) {
21312879e75SMatthias Ringwald         if (connection->characteristics[connection->characteristic_index].client_configuration_handle != 0) {
21412879e75SMatthias Ringwald             next_query_found = true;
21512879e75SMatthias Ringwald             break;
21612879e75SMatthias Ringwald         }
21712879e75SMatthias Ringwald         connection->characteristic_index++;
21812879e75SMatthias Ringwald     }
21912879e75SMatthias Ringwald     return next_query_found;
22012879e75SMatthias Ringwald }
22112879e75SMatthias Ringwald 
gatt_service_client_register_notification(gatt_service_client_t * client,gatt_service_client_connection_t * connection)22212879e75SMatthias Ringwald static uint8_t gatt_service_client_register_notification(gatt_service_client_t * client,
22312879e75SMatthias Ringwald                                                          gatt_service_client_connection_t *connection) {
22412879e75SMatthias Ringwald     gatt_client_characteristic_t characteristic;
22512879e75SMatthias Ringwald     uint8_t status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
22612879e75SMatthias Ringwald 
22712879e75SMatthias Ringwald     if (connection->characteristics[connection->characteristic_index].client_configuration_handle != 0){
22812879e75SMatthias Ringwald         characteristic.value_handle = connection->characteristics[connection->characteristic_index].value_handle;
22912879e75SMatthias Ringwald         characteristic.end_handle = connection->characteristics[connection->characteristic_index].end_handle;
23012879e75SMatthias Ringwald         characteristic.properties = connection->characteristics[connection->characteristic_index].properties;
23112879e75SMatthias Ringwald 
23212879e75SMatthias Ringwald         if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_INDICATE) != 0u){
23312879e75SMatthias Ringwald             status = gatt_client_write_client_characteristic_configuration_with_context(
23412879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
23512879e75SMatthias Ringwald                     connection->con_handle,
23612879e75SMatthias Ringwald                     &characteristic,
23712879e75SMatthias Ringwald                     GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION,
23812879e75SMatthias Ringwald                     client->service_id,
23912879e75SMatthias Ringwald                     connection->cid);
24012879e75SMatthias Ringwald         } else if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY) != 0u){
24112879e75SMatthias Ringwald             status = gatt_client_write_client_characteristic_configuration_with_context(
24212879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
24312879e75SMatthias Ringwald                     connection->con_handle,
24412879e75SMatthias Ringwald                     &characteristic,
24512879e75SMatthias Ringwald                     GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION,
24612879e75SMatthias Ringwald                     client->service_id,
24712879e75SMatthias Ringwald                     connection->cid);
24812879e75SMatthias Ringwald         }
24912879e75SMatthias Ringwald     }
25012879e75SMatthias Ringwald     return status;
25112879e75SMatthias Ringwald }
25212879e75SMatthias Ringwald 
25312879e75SMatthias Ringwald 
gatt_service_client_run_for_client(gatt_service_client_t * client,gatt_service_client_connection_t * connection)25412879e75SMatthias Ringwald static void gatt_service_client_run_for_client(gatt_service_client_t * client, gatt_service_client_connection_t * connection){
25512879e75SMatthias Ringwald     uint8_t status = ATT_ERROR_SUCCESS;
25612879e75SMatthias Ringwald     gatt_client_service_t service;
25712879e75SMatthias Ringwald     gatt_client_characteristic_t characteristic;
25812879e75SMatthias Ringwald 
25912879e75SMatthias Ringwald     switch (connection->state){
26012879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W2_QUERY_PRIMARY_SERVICE:
26112879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT;
26212879e75SMatthias Ringwald             status = gatt_client_discover_primary_services_by_uuid16_with_context(
26312879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
26412879e75SMatthias Ringwald                     connection->con_handle,
26512879e75SMatthias Ringwald                     connection->service_uuid16,
26612879e75SMatthias Ringwald                     client->service_id,
26712879e75SMatthias Ringwald                     connection->cid);
26812879e75SMatthias Ringwald             break;
26912879e75SMatthias Ringwald 
27012879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS:
27112879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
27212879e75SMatthias Ringwald             printf("Read characteristics [service 0x%04x]:\n", connection->service_uuid16);
27312879e75SMatthias Ringwald #endif
27412879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
27512879e75SMatthias Ringwald 
27612879e75SMatthias Ringwald             service.start_group_handle = connection->start_handle;
27712879e75SMatthias Ringwald             service.end_group_handle = connection->end_handle;
27812879e75SMatthias Ringwald             service.uuid16 = connection->service_uuid16;
27912879e75SMatthias Ringwald 
28012879e75SMatthias Ringwald             status = gatt_client_discover_characteristics_for_service_with_context(
28112879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
28212879e75SMatthias Ringwald                     connection->con_handle,
28312879e75SMatthias Ringwald                     &service,
28412879e75SMatthias Ringwald                     client->service_id,
28512879e75SMatthias Ringwald                     connection->cid);
28612879e75SMatthias Ringwald             break;
28712879e75SMatthias Ringwald 
28812879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS:
28912879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
29012879e75SMatthias Ringwald             printf("Read client characteristic descriptors for characteristic[%u, uuid16 0x%04x, value_handle 0x%04x]:\n",
29112879e75SMatthias Ringwald                 connection->characteristic_index,
29212879e75SMatthias Ringwald                 client->characteristics_desc16[connection->characteristic_index],
29312879e75SMatthias Ringwald                 connection->characteristics[connection->characteristic_index].value_handle);
29412879e75SMatthias Ringwald #endif
29512879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT;
29612879e75SMatthias Ringwald             characteristic.value_handle = connection->characteristics[connection->characteristic_index].value_handle;
29712879e75SMatthias Ringwald             characteristic.properties   = connection->characteristics[connection->characteristic_index].properties;
29812879e75SMatthias Ringwald             characteristic.end_handle   = connection->characteristics[connection->characteristic_index].end_handle;
29912879e75SMatthias Ringwald 
30012879e75SMatthias Ringwald             (void) gatt_client_discover_characteristic_descriptors_with_context(
30112879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
30212879e75SMatthias Ringwald                     connection->con_handle,
30312879e75SMatthias Ringwald                     &characteristic,
30412879e75SMatthias Ringwald                     client->service_id,
30512879e75SMatthias Ringwald                     connection->cid);
30612879e75SMatthias Ringwald             break;
30712879e75SMatthias Ringwald 
30812879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION:
30912879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
31012879e75SMatthias Ringwald             if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY) != 0u){
31112879e75SMatthias Ringwald                 printf("Register notification for characteristic");
31212879e75SMatthias Ringwald             } else {
31312879e75SMatthias Ringwald                 printf("Register indication for characteristic");
31412879e75SMatthias Ringwald             }
31512879e75SMatthias Ringwald 
31612879e75SMatthias Ringwald             printf("[%u, uuid16 0x%04x, ccd handle 0x%04x]\n",
31712879e75SMatthias Ringwald                 connection->characteristic_index,
31812879e75SMatthias Ringwald                 client->characteristics_desc16[connection->characteristic_index],
31912879e75SMatthias Ringwald                 connection->characteristics[connection->characteristic_index].client_configuration_handle);
32012879e75SMatthias Ringwald #endif
32112879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED;
32212879e75SMatthias Ringwald             status = gatt_service_client_register_notification(client, connection);
32312879e75SMatthias Ringwald             connection->characteristic_index++;
32412879e75SMatthias Ringwald 
32512879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
32612879e75SMatthias Ringwald             if (status != ERROR_CODE_SUCCESS) {
32712879e75SMatthias Ringwald                 printf("Notification not supported, status 0%02X\n.", status);
32812879e75SMatthias Ringwald             }
32912879e75SMatthias Ringwald #else
33012879e75SMatthias Ringwald             UNUSED(status);
33112879e75SMatthias Ringwald #endif
33212879e75SMatthias Ringwald             return;
33312879e75SMatthias Ringwald 
33412879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_CONNECTED:
33512879e75SMatthias Ringwald             // TODO
33612879e75SMatthias Ringwald             break;
33712879e75SMatthias Ringwald         default:
33812879e75SMatthias Ringwald             break;
33912879e75SMatthias Ringwald     }
34012879e75SMatthias Ringwald 
34112879e75SMatthias Ringwald     if (status != ATT_ERROR_SUCCESS){
34213a624a6SMatthias Ringwald         gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, gatt_client_att_status_to_error_code(status));
34312879e75SMatthias Ringwald         gatt_service_client_finalize_connection(client, connection);
34412879e75SMatthias Ringwald     }
34512879e75SMatthias Ringwald }
34612879e75SMatthias Ringwald 
34712879e75SMatthias Ringwald // @return true if client valid / run function should be called
gatt_service_client_handle_query_complete(gatt_service_client_t * client,gatt_service_client_connection_t * connection,uint8_t status)34812879e75SMatthias Ringwald static bool gatt_service_client_handle_query_complete(gatt_service_client_t *client,
34912879e75SMatthias Ringwald                                                       gatt_service_client_connection_t *connection,
35012879e75SMatthias Ringwald                                                       uint8_t status) {
35112879e75SMatthias Ringwald     btstack_assert(client != NULL);
35212879e75SMatthias Ringwald     btstack_assert(connection != NULL);
35312879e75SMatthias Ringwald 
35412879e75SMatthias Ringwald     if (status != ATT_ERROR_SUCCESS){
35512879e75SMatthias Ringwald         switch (connection->state){
35612879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
35712879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
35812879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT:
35912879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED:
36013a624a6SMatthias Ringwald                 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, gatt_client_att_status_to_error_code(status));
36112879e75SMatthias Ringwald                 gatt_service_client_finalize_connection(client, connection);
36212879e75SMatthias Ringwald                 return false;
36312879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_CONNECTED:
36412879e75SMatthias Ringwald                 break;
36512879e75SMatthias Ringwald             default:
36612879e75SMatthias Ringwald                 btstack_unreachable();
36712879e75SMatthias Ringwald                 break;
36812879e75SMatthias Ringwald         }
36912879e75SMatthias Ringwald     }
37012879e75SMatthias Ringwald 
37112879e75SMatthias Ringwald     switch (connection->state){
37212879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
37312879e75SMatthias Ringwald             if (connection->service_instances_num == 0){
37412879e75SMatthias Ringwald                 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
37512879e75SMatthias Ringwald                 gatt_service_client_finalize_connection(client, connection);
37612879e75SMatthias Ringwald                 return false;
37712879e75SMatthias Ringwald             }
37812879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
37912879e75SMatthias Ringwald             connection->characteristic_index = 0;
38012879e75SMatthias Ringwald             break;
38112879e75SMatthias Ringwald 
38212879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
38312879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
38412879e75SMatthias Ringwald             connection->characteristic_index = 0;
38512879e75SMatthias Ringwald             break;
38612879e75SMatthias Ringwald 
38712879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT:
38812879e75SMatthias Ringwald             if (gatt_service_client_more_descriptor_queries(client, connection)){
38912879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
39012879e75SMatthias Ringwald                 break;
39112879e75SMatthias Ringwald             }
39212879e75SMatthias Ringwald 
39312879e75SMatthias Ringwald             connection->characteristic_index = 0;
39412879e75SMatthias Ringwald             if (gatt_service_client_have_more_notifications_to_enable(client, connection)){
39512879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
39612879e75SMatthias Ringwald             } else {
39712879e75SMatthias Ringwald                 connection->characteristic_index = 0;
39812879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_CONNECTED;
39912879e75SMatthias Ringwald                 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_SUCCESS);
40012879e75SMatthias Ringwald             }
40112879e75SMatthias Ringwald             break;
40212879e75SMatthias Ringwald 
40312879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED:
40412879e75SMatthias Ringwald             if (gatt_service_client_have_more_notifications_to_enable(client, connection)){
40512879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
40612879e75SMatthias Ringwald             } else {
40712879e75SMatthias Ringwald                 // all notifications registered, start listening
40812879e75SMatthias Ringwald                 gatt_client_service_t service;
40912879e75SMatthias Ringwald                 service.start_group_handle = connection->start_handle;
41012879e75SMatthias Ringwald                 service.end_group_handle = connection->end_handle;
41112879e75SMatthias Ringwald 
41212879e75SMatthias Ringwald                 gatt_client_listen_for_service_characteristic_value_updates(&connection->notification_listener, client->packet_handler,
41312879e75SMatthias Ringwald                                                                             connection->con_handle, &service, client->service_id, connection->cid);
41412879e75SMatthias Ringwald 
41512879e75SMatthias Ringwald                 connection->characteristic_index = 0;
41612879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_CONNECTED;
41712879e75SMatthias Ringwald                 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_SUCCESS);
41812879e75SMatthias Ringwald             }
41912879e75SMatthias Ringwald 
42012879e75SMatthias Ringwald             break;
42112879e75SMatthias Ringwald 
42212879e75SMatthias Ringwald         default:
42312879e75SMatthias Ringwald             break;
42412879e75SMatthias Ringwald 
42512879e75SMatthias Ringwald     }
42612879e75SMatthias Ringwald     // TODO run_for_client
42712879e75SMatthias Ringwald     return true;
42812879e75SMatthias Ringwald }
42912879e75SMatthias Ringwald 
gatt_service_client_get_uninitialized_characteristic_index_for_uuid16(gatt_service_client_t * client,gatt_service_client_connection_t * connection,uint16_t uuid16)43012879e75SMatthias Ringwald static uint8_t gatt_service_client_get_uninitialized_characteristic_index_for_uuid16(
43112879e75SMatthias Ringwald         gatt_service_client_t * client,
43212879e75SMatthias Ringwald         gatt_service_client_connection_t * connection,
43312879e75SMatthias Ringwald         uint16_t uuid16){
43412879e75SMatthias Ringwald 
43512879e75SMatthias Ringwald         uint8_t index = 0xff;
43612879e75SMatthias Ringwald 
43712879e75SMatthias Ringwald         uint8_t i;
43812879e75SMatthias Ringwald 
43912879e75SMatthias Ringwald         for (i = 0; i < client->characteristics_desc16_num; i++){
44012879e75SMatthias Ringwald             if (client->characteristics_desc16[i] == uuid16){
44112879e75SMatthias Ringwald                 // allow for more then one instance of the same characteristic (as in OTS client)
44212879e75SMatthias Ringwald                 if (connection->characteristics[i].value_handle != 0){
44312879e75SMatthias Ringwald                    continue;
44412879e75SMatthias Ringwald                 }
44512879e75SMatthias Ringwald                 index = i;
44612879e75SMatthias Ringwald                 break;
44712879e75SMatthias Ringwald             }
44812879e75SMatthias Ringwald         }
44912879e75SMatthias Ringwald         return index;
45012879e75SMatthias Ringwald }
45112879e75SMatthias Ringwald 
gatt_service_client_handle_disconnect(hci_con_handle_t con_handle)45212879e75SMatthias Ringwald static void gatt_service_client_handle_disconnect(hci_con_handle_t con_handle){
45312879e75SMatthias Ringwald     btstack_linked_list_iterator_t service_it;
45412879e75SMatthias Ringwald     btstack_linked_list_iterator_init(&service_it, &gatt_service_clients);
45512879e75SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&service_it)){
45612879e75SMatthias Ringwald         gatt_service_client_t * client = (gatt_service_client_t *)btstack_linked_list_iterator_next(&service_it);
45712879e75SMatthias Ringwald         btstack_linked_list_iterator_t connection_it;
45812879e75SMatthias Ringwald         btstack_linked_list_iterator_init(&connection_it, &client->connections);
45912879e75SMatthias Ringwald         while (btstack_linked_list_iterator_has_next(&connection_it)){
46012879e75SMatthias Ringwald             gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&connection_it);
46112879e75SMatthias Ringwald             if (connection->con_handle == con_handle) {
46212879e75SMatthias Ringwald                 gatt_service_client_emit_disconnected(client->packet_handler, connection->con_handle, connection->cid);
46312879e75SMatthias Ringwald                 gatt_service_client_finalize_connection(client, connection);
46412879e75SMatthias Ringwald             }
46512879e75SMatthias Ringwald         }
46612879e75SMatthias Ringwald     }
46712879e75SMatthias Ringwald }
46812879e75SMatthias Ringwald 
46912879e75SMatthias Ringwald static void
gatt_service_client_gatt_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)47012879e75SMatthias Ringwald gatt_service_client_gatt_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
47112879e75SMatthias Ringwald     UNUSED(channel);
47212879e75SMatthias Ringwald     UNUSED(size);
47312879e75SMatthias Ringwald 
47412879e75SMatthias Ringwald     if (packet_type != HCI_EVENT_PACKET) return;
47512879e75SMatthias Ringwald 
47612879e75SMatthias Ringwald     gatt_service_client_t * client;
47712879e75SMatthias Ringwald     gatt_service_client_connection_t * connection;
47812879e75SMatthias Ringwald     gatt_client_service_t service;
47912879e75SMatthias Ringwald     gatt_client_characteristic_t characteristic;
48012879e75SMatthias Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
48112879e75SMatthias Ringwald     uint8_t characteristic_index;
48212879e75SMatthias Ringwald 
48312879e75SMatthias Ringwald     bool call_run = true;
48412879e75SMatthias Ringwald     switch (hci_event_packet_get_type(packet)){
48512879e75SMatthias Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
48612879e75SMatthias Ringwald             client = gatt_service_client_get_service_client_for_id(gatt_event_service_query_result_get_service_id(packet));
48712879e75SMatthias Ringwald             btstack_assert(client != NULL);
48812879e75SMatthias Ringwald             connection = gatt_service_client_get_connection_for_cid(client, gatt_event_service_query_result_get_connection_id(packet));
48912879e75SMatthias Ringwald             btstack_assert(connection != NULL);
49012879e75SMatthias Ringwald 
49112879e75SMatthias Ringwald             if (connection->service_instances_num < 1){
49212879e75SMatthias Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
49312879e75SMatthias Ringwald                 connection->start_handle = service.start_group_handle;
49412879e75SMatthias Ringwald                 connection->end_handle   = service.end_group_handle;
49512879e75SMatthias Ringwald 
49612879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
49712879e75SMatthias Ringwald                 printf("Service: start handle 0x%04X, end handle 0x%04X\n", connection->start_handle, connection->end_handle);
49812879e75SMatthias Ringwald #endif
49912879e75SMatthias Ringwald                 connection->service_instances_num++;
50012879e75SMatthias Ringwald             } else {
50112879e75SMatthias Ringwald                 log_info("Found more then one Service instance.");
50212879e75SMatthias Ringwald             }
50312879e75SMatthias Ringwald             break;
50412879e75SMatthias Ringwald 
50512879e75SMatthias Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
50612879e75SMatthias Ringwald             client = gatt_service_client_get_service_client_for_id(gatt_event_characteristic_query_result_get_service_id(packet));
50712879e75SMatthias Ringwald             btstack_assert(client != NULL);
50812879e75SMatthias Ringwald             connection = gatt_service_client_get_connection_for_cid(client, gatt_event_characteristic_query_result_get_connection_id(packet));
50912879e75SMatthias Ringwald             btstack_assert(connection != NULL);
51012879e75SMatthias Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
51112879e75SMatthias Ringwald 
51212879e75SMatthias Ringwald             characteristic_index = gatt_service_client_get_uninitialized_characteristic_index_for_uuid16(client, connection, characteristic.uuid16);
51312879e75SMatthias Ringwald             if (characteristic_index < client->characteristics_desc16_num){
51412879e75SMatthias Ringwald                 connection->characteristics[characteristic_index].value_handle = characteristic.value_handle;
51512879e75SMatthias Ringwald                 connection->characteristics[characteristic_index].properties = characteristic.properties;
51612879e75SMatthias Ringwald                 connection->characteristics[characteristic_index].end_handle = characteristic.end_handle;
51712879e75SMatthias Ringwald 
51812879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
51912879e75SMatthias Ringwald                 printf("    [%u] Attribute Handle 0x%04X, Properties 0x%02X, Value Handle 0x%04X, UUID 0x%04X\n",
52012879e75SMatthias Ringwald                        characteristic_index, characteristic.start_handle,
52112879e75SMatthias Ringwald                        characteristic.properties, characteristic.value_handle, characteristic.uuid16);
52212879e75SMatthias Ringwald #endif
52312879e75SMatthias Ringwald             }
52412879e75SMatthias Ringwald             break;
52512879e75SMatthias Ringwald 
52612879e75SMatthias Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
52712879e75SMatthias Ringwald             client = gatt_service_client_get_service_client_for_id(gatt_event_all_characteristic_descriptors_query_result_get_service_id(packet));
52812879e75SMatthias Ringwald             btstack_assert(client != NULL);
52912879e75SMatthias Ringwald             connection = gatt_service_client_get_connection_for_cid(client, gatt_event_all_characteristic_descriptors_query_result_get_connection_id(packet));
53012879e75SMatthias Ringwald             btstack_assert(connection != NULL);
53112879e75SMatthias Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
53212879e75SMatthias Ringwald 
53312879e75SMatthias Ringwald             if (characteristic_descriptor.uuid16 != ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
53412879e75SMatthias Ringwald                 break;
53512879e75SMatthias Ringwald             }
53612879e75SMatthias Ringwald             btstack_assert(connection->state == GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT);
53712879e75SMatthias Ringwald 
53812879e75SMatthias Ringwald             if ( ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY)   != 0u) ||
53912879e75SMatthias Ringwald                  ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_INDICATE) != 0u)
54012879e75SMatthias Ringwald                ){
54112879e75SMatthias Ringwald                 connection->characteristics[connection->characteristic_index].client_configuration_handle = characteristic_descriptor.handle;
54212879e75SMatthias Ringwald             } else {
54312879e75SMatthias Ringwald                 connection->characteristics[connection->characteristic_index].client_configuration_handle = 0;
54412879e75SMatthias Ringwald             }
54512879e75SMatthias Ringwald             connection->characteristic_index++;
54612879e75SMatthias Ringwald 
54712879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
54812879e75SMatthias Ringwald             printf("    Characteristic Configuration Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
54912879e75SMatthias Ringwald                 characteristic_descriptor.handle,
55012879e75SMatthias Ringwald                 characteristic_descriptor.uuid16);
55112879e75SMatthias Ringwald #endif
55212879e75SMatthias Ringwald             break;
55312879e75SMatthias Ringwald 
55412879e75SMatthias Ringwald         case GATT_EVENT_QUERY_COMPLETE:
55512879e75SMatthias Ringwald             client = gatt_service_client_get_service_client_for_id(gatt_event_query_complete_get_service_id(packet));
55612879e75SMatthias Ringwald             btstack_assert(client != NULL);
55712879e75SMatthias Ringwald             connection = gatt_service_client_get_connection_for_cid(client, gatt_event_query_complete_get_connection_id(packet));
55812879e75SMatthias Ringwald             btstack_assert(connection != NULL);
55912879e75SMatthias Ringwald             call_run = gatt_service_client_handle_query_complete(client, connection, gatt_event_query_complete_get_att_status(packet));
56012879e75SMatthias Ringwald             break;
56112879e75SMatthias Ringwald 
56212879e75SMatthias Ringwald         default:
56312879e75SMatthias Ringwald             call_run = false;
56412879e75SMatthias Ringwald             break;
56512879e75SMatthias Ringwald     }
56612879e75SMatthias Ringwald 
56712879e75SMatthias Ringwald     if (call_run){
56812879e75SMatthias Ringwald         gatt_service_client_run_for_client(client, connection);
56912879e75SMatthias Ringwald     }
57012879e75SMatthias Ringwald }
57112879e75SMatthias Ringwald 
gatt_service_client_hci_event_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)57212879e75SMatthias Ringwald static void gatt_service_client_hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
57312879e75SMatthias Ringwald     UNUSED(channel);
57412879e75SMatthias Ringwald     UNUSED(size);
57512879e75SMatthias Ringwald     UNUSED(packet);
57612879e75SMatthias Ringwald     UNUSED(size);
57712879e75SMatthias Ringwald 
57812879e75SMatthias Ringwald     if (packet_type != HCI_EVENT_PACKET) return;
57912879e75SMatthias Ringwald 
58012879e75SMatthias Ringwald     hci_con_handle_t con_handle;
58112879e75SMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
58212879e75SMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
58312879e75SMatthias Ringwald             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
58412879e75SMatthias Ringwald             gatt_service_client_handle_disconnect(con_handle);
58512879e75SMatthias Ringwald             break;
58612879e75SMatthias Ringwald         default:
58712879e75SMatthias Ringwald             break;
58812879e75SMatthias Ringwald     }
58912879e75SMatthias Ringwald }
59012879e75SMatthias Ringwald 
59112879e75SMatthias Ringwald /* API */
59212879e75SMatthias Ringwald 
gatt_service_client_init(void)59312879e75SMatthias Ringwald void gatt_service_client_init(void){
59412879e75SMatthias Ringwald     if (false == gatt_service_client_intitialized){
59512879e75SMatthias Ringwald         gatt_service_client_hci_callback_registration.callback = gatt_service_client_hci_event_handler;
59612879e75SMatthias Ringwald         hci_add_event_handler(&gatt_service_client_hci_callback_registration);
59712879e75SMatthias Ringwald         gatt_service_client_intitialized = true;
59812879e75SMatthias Ringwald     }
59912879e75SMatthias Ringwald }
60012879e75SMatthias Ringwald 
gatt_service_client_register_client(gatt_service_client_t * client,btstack_packet_handler_t packet_handler,const uint16_t * characteristic_uuid16s,uint16_t characteristic_uuid16s_num)60112879e75SMatthias Ringwald void gatt_service_client_register_client(gatt_service_client_t *client, btstack_packet_handler_t packet_handler,
60212879e75SMatthias Ringwald                                          const uint16_t *characteristic_uuid16s, uint16_t characteristic_uuid16s_num) {
60312879e75SMatthias Ringwald 
60412879e75SMatthias Ringwald     btstack_assert(gatt_service_client_intitialized);
60512879e75SMatthias Ringwald 
60612879e75SMatthias Ringwald     gatt_service_client_service_cid = btstack_next_cid_ignoring_zero(gatt_service_client_service_cid);
60712879e75SMatthias Ringwald     client->service_id =gatt_service_client_service_cid;
60812879e75SMatthias Ringwald     client->cid_counter = 0;
60912879e75SMatthias Ringwald     client->characteristics_desc16_num = 0;
61012879e75SMatthias Ringwald     client->packet_handler = packet_handler;
61141c44321SMatthias Ringwald     client->characteristics_desc16 = characteristic_uuid16s;
61241c44321SMatthias Ringwald     client->characteristics_desc16_num = characteristic_uuid16s_num;
61312879e75SMatthias Ringwald     btstack_linked_list_add(&gatt_service_clients, &client->item);
61412879e75SMatthias Ringwald }
61512879e75SMatthias Ringwald 
61612879e75SMatthias Ringwald uint8_t
gatt_service_client_connect_primary_service_with_uuid16(hci_con_handle_t con_handle,gatt_service_client_t * client,gatt_service_client_connection_t * connection,uint16_t service_uuid16,uint8_t service_index,gatt_service_client_characteristic_t * characteristics,uint8_t characteristics_num)61712879e75SMatthias Ringwald gatt_service_client_connect_primary_service_with_uuid16(hci_con_handle_t con_handle, gatt_service_client_t *client,
61812879e75SMatthias Ringwald                                                         gatt_service_client_connection_t *connection,
61912879e75SMatthias Ringwald                                                         uint16_t service_uuid16, uint8_t service_index,
62012879e75SMatthias Ringwald                                                         gatt_service_client_characteristic_t *characteristics,
62112879e75SMatthias Ringwald                                                         uint8_t characteristics_num) {
62212879e75SMatthias Ringwald 
62312879e75SMatthias Ringwald     btstack_assert(client          != NULL);
62412879e75SMatthias Ringwald     btstack_assert(connection      != NULL);
62512879e75SMatthias Ringwald     btstack_assert(characteristics != NULL);
62612879e75SMatthias Ringwald 
62712879e75SMatthias Ringwald     if (gatt_service_client_get_connection_for_con_handle_and_service_index(client, con_handle, service_index) != NULL){
62812879e75SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
62912879e75SMatthias Ringwald     }
63012879e75SMatthias Ringwald 
63112879e75SMatthias Ringwald     if (characteristics_num < client->characteristics_desc16_num){
63212879e75SMatthias Ringwald         log_info("At least %u characteristics needed", client->characteristics_desc16_num);
63312879e75SMatthias Ringwald         return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
63412879e75SMatthias Ringwald     }
63512879e75SMatthias Ringwald 
63612879e75SMatthias Ringwald     connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_PRIMARY_SERVICE;
63712879e75SMatthias Ringwald     connection->client              = client;
63812879e75SMatthias Ringwald     connection->cid                 = gatt_service_client_get_next_cid(client);
63912879e75SMatthias Ringwald     connection->con_handle          = con_handle;
64012879e75SMatthias Ringwald     connection->service_uuid16      = service_uuid16;
64112879e75SMatthias Ringwald     connection->service_index       = service_index;
64212879e75SMatthias Ringwald     connection->characteristics     = characteristics;
64312879e75SMatthias Ringwald     btstack_linked_list_add(&client->connections, (btstack_linked_item_t *) connection);
64412879e75SMatthias Ringwald 
64512879e75SMatthias Ringwald     gatt_service_client_run_for_client(client, connection);
64612879e75SMatthias Ringwald     return ERROR_CODE_SUCCESS;
64712879e75SMatthias Ringwald }
64812879e75SMatthias Ringwald 
64912879e75SMatthias Ringwald uint8_t
gatt_service_client_connect_secondary_service_with_uuid16(hci_con_handle_t con_handle,gatt_service_client_t * client,gatt_service_client_connection_t * connection,uint16_t service_uuid16,uint8_t service_index,uint16_t service_start_handle,uint16_t service_end_handle,gatt_service_client_characteristic_t * characteristics,uint8_t characteristics_num)65012879e75SMatthias Ringwald gatt_service_client_connect_secondary_service_with_uuid16(hci_con_handle_t con_handle, gatt_service_client_t *client,
65112879e75SMatthias Ringwald                                                           gatt_service_client_connection_t *connection,
65212879e75SMatthias Ringwald                                                           uint16_t service_uuid16, uint8_t service_index,
65312879e75SMatthias Ringwald                                                           uint16_t service_start_handle, uint16_t service_end_handle,
65412879e75SMatthias Ringwald                                                           gatt_service_client_characteristic_t *characteristics,
65512879e75SMatthias Ringwald                                                           uint8_t characteristics_num) {
65612879e75SMatthias Ringwald 
65712879e75SMatthias Ringwald     btstack_assert(client != NULL);
65812879e75SMatthias Ringwald     btstack_assert(connection != NULL);
65912879e75SMatthias Ringwald     btstack_assert(characteristics != NULL);
66012879e75SMatthias Ringwald     btstack_assert(characteristics_num >= client->characteristics_desc16_num);
66112879e75SMatthias Ringwald 
662*bee7a0d9SMatthias Ringwald     if (gatt_service_client_get_connection_for_con_handle_and_attribute_handle(client, con_handle, service_start_handle) != NULL){
663*bee7a0d9SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
664*bee7a0d9SMatthias Ringwald     }
665*bee7a0d9SMatthias Ringwald     if (gatt_service_client_get_connection_for_con_handle_and_attribute_handle(client, con_handle, service_end_handle) != NULL){
66612879e75SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
66712879e75SMatthias Ringwald     }
66812879e75SMatthias Ringwald 
66912879e75SMatthias Ringwald     uint16_t cid = gatt_service_client_get_next_cid(client);
67012879e75SMatthias Ringwald 
67112879e75SMatthias Ringwald     connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
67212879e75SMatthias Ringwald     connection->client              = client;
67312879e75SMatthias Ringwald     connection->cid                 = cid;
67412879e75SMatthias Ringwald     connection->con_handle          = con_handle;
67512879e75SMatthias Ringwald     connection->service_uuid16      = service_uuid16;
67612879e75SMatthias Ringwald     connection->service_index       = service_index;
67712879e75SMatthias Ringwald     connection->start_handle        = service_start_handle;
67812879e75SMatthias Ringwald     connection->end_handle          = service_end_handle;
67912879e75SMatthias Ringwald     connection->characteristics     = characteristics;
68012879e75SMatthias Ringwald     btstack_linked_list_add(&client->connections, (btstack_linked_item_t *) connection);
68112879e75SMatthias Ringwald 
68212879e75SMatthias Ringwald     gatt_service_client_run_for_client(client, connection);
68312879e75SMatthias Ringwald     return ERROR_CODE_SUCCESS;
68412879e75SMatthias Ringwald }
68512879e75SMatthias Ringwald 
68612879e75SMatthias Ringwald uint8_t
gatt_service_client_can_query_characteristic(const gatt_service_client_connection_t * connection,uint8_t characteristic_index)68712879e75SMatthias Ringwald gatt_service_client_can_query_characteristic(const gatt_service_client_connection_t *connection,
68812879e75SMatthias Ringwald                                              uint8_t characteristic_index) {
68912879e75SMatthias Ringwald     if (connection->state != GATT_SERVICE_CLIENT_STATE_CONNECTED){
69012879e75SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
69112879e75SMatthias Ringwald     }
69212879e75SMatthias Ringwald 
69312879e75SMatthias Ringwald     if (connection->characteristics[characteristic_index].value_handle == 0){
69412879e75SMatthias Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
69512879e75SMatthias Ringwald     }
69612879e75SMatthias Ringwald     return ERROR_CODE_SUCCESS;
69712879e75SMatthias Ringwald }
69812879e75SMatthias Ringwald 
gatt_service_client_disconnect(gatt_service_client_connection_t * connection)69912879e75SMatthias Ringwald uint8_t gatt_service_client_disconnect(gatt_service_client_connection_t *connection) {
70012879e75SMatthias Ringwald     // finalize connections
70112879e75SMatthias Ringwald     gatt_service_client_emit_disconnected(connection->client->packet_handler, connection->con_handle, connection->cid);
70212879e75SMatthias Ringwald     gatt_service_client_finalize_connection(connection->client, connection);
70312879e75SMatthias Ringwald     return ERROR_CODE_SUCCESS;
70412879e75SMatthias Ringwald }
70512879e75SMatthias Ringwald 
gatt_service_client_unregister_client(gatt_service_client_t * client)70612879e75SMatthias Ringwald void gatt_service_client_unregister_client(gatt_service_client_t * client){
70712879e75SMatthias Ringwald     btstack_assert(client != NULL);
70812879e75SMatthias Ringwald 
70912879e75SMatthias Ringwald     client->packet_handler = NULL;
71012879e75SMatthias Ringwald 
71112879e75SMatthias Ringwald     client->cid_counter = 0;
71212879e75SMatthias Ringwald     client->characteristics_desc16_num = 0;
71312879e75SMatthias Ringwald 
71412879e75SMatthias Ringwald     btstack_linked_list_iterator_t it;
71512879e75SMatthias Ringwald     btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections);
71612879e75SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
71712879e75SMatthias Ringwald         gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it);
71812879e75SMatthias Ringwald         gatt_service_client_finalize_connection(client, connection);
71912879e75SMatthias Ringwald     }
72012879e75SMatthias Ringwald 
72112879e75SMatthias Ringwald     btstack_linked_list_remove(&gatt_service_clients, &client->item);
72212879e75SMatthias Ringwald }
72312879e75SMatthias Ringwald 
gatt_service_client_dump_characteristic_value_handles(const gatt_service_client_connection_t * connection,const char ** characteristic_names)72412879e75SMatthias Ringwald void gatt_service_client_dump_characteristic_value_handles(const gatt_service_client_connection_t *connection,
72512879e75SMatthias Ringwald                                                            const char **characteristic_names) {
72612879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
72712879e75SMatthias Ringwald     uint8_t i;
72812879e75SMatthias Ringwald     for (i = 0; i < connection->client->characteristics_desc16_num; i++) {
72912879e75SMatthias Ringwald         printf("0x%04X %s\n", connection->characteristics[i].value_handle, characteristic_names[i]);
73012879e75SMatthias Ringwald     }
73112879e75SMatthias Ringwald #else
73212879e75SMatthias Ringwald     UNUSED(connection);
73312879e75SMatthias Ringwald     UNUSED(characteristic_names);
73412879e75SMatthias Ringwald #endif
73512879e75SMatthias Ringwald }
73612879e75SMatthias Ringwald 
gatt_service_client_deinit(void)73712879e75SMatthias Ringwald void gatt_service_client_deinit(void){
73812879e75SMatthias Ringwald     gatt_service_client_service_cid = 0;
73912879e75SMatthias Ringwald     gatt_service_clients = NULL;
74012879e75SMatthias Ringwald     gatt_service_client_intitialized = false;
74112879e75SMatthias Ringwald }
74212879e75SMatthias Ringwald 
743