13deb3ec6SMatthias Ringwald /*
23deb3ec6SMatthias Ringwald * Copyright (C) 2014 BlueKitchen GmbH
33deb3ec6SMatthias Ringwald *
43deb3ec6SMatthias Ringwald * Redistribution and use in source and binary forms, with or without
53deb3ec6SMatthias Ringwald * modification, are permitted provided that the following conditions
63deb3ec6SMatthias Ringwald * are met:
73deb3ec6SMatthias Ringwald *
83deb3ec6SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright
93deb3ec6SMatthias Ringwald * notice, this list of conditions and the following disclaimer.
103deb3ec6SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright
113deb3ec6SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the
123deb3ec6SMatthias Ringwald * documentation and/or other materials provided with the distribution.
133deb3ec6SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of
143deb3ec6SMatthias Ringwald * contributors may be used to endorse or promote products derived
153deb3ec6SMatthias Ringwald * from this software without specific prior written permission.
163deb3ec6SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for
173deb3ec6SMatthias Ringwald * personal benefit and not for any commercial purpose or for
183deb3ec6SMatthias Ringwald * monetary gain.
193deb3ec6SMatthias Ringwald *
203deb3ec6SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
213deb3ec6SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
223deb3ec6SMatthias 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,
253deb3ec6SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
263deb3ec6SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
273deb3ec6SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
283deb3ec6SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
293deb3ec6SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
303deb3ec6SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
313deb3ec6SMatthias Ringwald * SUCH DAMAGE.
323deb3ec6SMatthias Ringwald *
333deb3ec6SMatthias Ringwald * Please inquire about commercial licensing options at
343deb3ec6SMatthias Ringwald * [email protected]
353deb3ec6SMatthias Ringwald *
363deb3ec6SMatthias Ringwald */
373deb3ec6SMatthias Ringwald
38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "gatt_client.c"
39ab2c6ae4SMatthias Ringwald
403deb3ec6SMatthias Ringwald #include <stdint.h>
413deb3ec6SMatthias Ringwald #include <string.h>
42a95ca902SMatthias Ringwald #include <stddef.h>
433deb3ec6SMatthias Ringwald
447907f069SMatthias Ringwald #include "btstack_config.h"
453deb3ec6SMatthias Ringwald
46296289e3SMatthias Ringwald #include "ble/att_dispatch.h"
470e2df43fSMatthias Ringwald #include "ble/att_db.h"
480e2df43fSMatthias Ringwald #include "ble/gatt_client.h"
490e2df43fSMatthias Ringwald #include "ble/le_device_db.h"
500e2df43fSMatthias Ringwald #include "ble/sm.h"
51c40517c3SMatthias Ringwald #include "bluetooth_psm.h"
5216ece135SMatthias Ringwald #include "btstack_debug.h"
530e2df43fSMatthias Ringwald #include "btstack_event.h"
543deb3ec6SMatthias Ringwald #include "btstack_memory.h"
550e2df43fSMatthias Ringwald #include "btstack_run_loop.h"
560e2df43fSMatthias Ringwald #include "btstack_util.h"
573deb3ec6SMatthias Ringwald #include "hci.h"
583deb3ec6SMatthias Ringwald #include "hci_dump.h"
5958e8c9f5SMatthias Ringwald #include "hci_event_builder.h"
603deb3ec6SMatthias Ringwald #include "l2cap.h"
611450cdc6SMatthias Ringwald #include "classic/sdp_client.h"
621450cdc6SMatthias Ringwald #include "bluetooth_gatt.h"
631450cdc6SMatthias Ringwald #include "bluetooth_sdp.h"
641450cdc6SMatthias Ringwald #include "classic/sdp_util.h"
653deb3ec6SMatthias Ringwald
664797b71aSMatthias Ringwald #if defined(ENABLE_GATT_OVER_EATT) && !defined(ENABLE_L2CAP_ENHANCED_CREDIT_BASED_FLOW_CONTROL_MODE)
674797b71aSMatthias Ringwald #error "GATT Over EATT requires support for L2CAP Enhanced CoC. Please enable ENABLE_L2CAP_ENHANCED_CREDIT_BASED_FLOW_CONTROL_MODE"
684797b71aSMatthias Ringwald #endif
694797b71aSMatthias Ringwald
70b1da4983SMatthias Ringwald // L2CAP Test Spec p35 defines a minimum of 100 ms, but PTS might indicate an error if we sent after 100 ms
71b1da4983SMatthias Ringwald #define GATT_CLIENT_COLLISION_BACKOFF_MS 150
72b1da4983SMatthias Ringwald
739c662c9bSMatthias Ringwald static btstack_linked_list_t gatt_client_connections;
749c662c9bSMatthias Ringwald static btstack_linked_list_t gatt_client_value_listeners;
751a4874dcSMatthias Ringwald static btstack_linked_list_t gatt_client_service_value_listeners;
76842492f0SMatthias Ringwald #ifdef ENABLE_GATT_CLIENT_SERVICE_CHANGED
7758e8c9f5SMatthias Ringwald static btstack_linked_list_t gatt_client_service_changed_handler;
78842492f0SMatthias Ringwald #endif
79361b1363SMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration;
80f4b33574SMatthias Ringwald static btstack_packet_callback_registration_t sm_event_callback_registration;
81544a5845SMatthias Ringwald static btstack_context_callback_registration_t gatt_client_deferred_event_emit;
82f4b33574SMatthias Ringwald
8311279da7SMatthias Ringwald // GATT Client Configuration
8411279da7SMatthias Ringwald static bool gatt_client_mtu_exchange_enabled;
8511279da7SMatthias Ringwald static gap_security_level_t gatt_client_required_security_level;
865cf6c434SJakob Krantz
873deb3ec6SMatthias Ringwald static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size);
88f4b33574SMatthias Ringwald static void gatt_client_event_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
895cf1669fSMatthias Ringwald static void gatt_client_report_error_if_pending(gatt_client_t *gatt_client, uint8_t att_error_code);
907a766ebfSMatthias Ringwald
917a766ebfSMatthias Ringwald #ifdef ENABLE_LE_SIGNED_WRITE
923deb3ec6SMatthias Ringwald static void att_signed_write_handle_cmac_result(uint8_t hash[8]);
937a766ebfSMatthias Ringwald #endif
943deb3ec6SMatthias Ringwald
9559b5e157SMatthias Ringwald #ifdef ENABLE_GATT_OVER_CLASSIC
9659b5e157SMatthias Ringwald static gatt_client_t * gatt_client_get_context_for_l2cap_cid(uint16_t l2cap_cid);
9759b5e157SMatthias Ringwald static void gatt_client_classic_handle_connected(gatt_client_t * gatt_client, uint8_t status);
9859b5e157SMatthias Ringwald static void gatt_client_classic_handle_disconnected(gatt_client_t * gatt_client);
99180cbe79SMatthias Ringwald static void gatt_client_classic_retry(btstack_timer_source_t * ts);
10059b5e157SMatthias Ringwald #endif
10159b5e157SMatthias Ringwald
10226166ecfSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
103bbb8e698SMatthias Ringwald static bool gatt_client_eatt_enabled;
1047627a0deSMatthias Ringwald static bool gatt_client_le_enhanced_handle_can_send_query(gatt_client_t * gatt_client);
1056d0f6f49SMatthias Ringwald static void gatt_client_le_enhanced_retry(btstack_timer_source_t * ts);
10626166ecfSMatthias Ringwald #endif
10726166ecfSMatthias Ringwald
gatt_client_init(void)1083deb3ec6SMatthias Ringwald void gatt_client_init(void){
1093deb3ec6SMatthias Ringwald gatt_client_connections = NULL;
1101a4874dcSMatthias Ringwald gatt_client_value_listeners = NULL;
1111a4874dcSMatthias Ringwald gatt_client_service_value_listeners = NULL;
112842492f0SMatthias Ringwald #ifdef ENABLE_GATT_CLIENT_SERVICE_CHANGED
11358e8c9f5SMatthias Ringwald gatt_client_service_changed_handler = NULL;
114842492f0SMatthias Ringwald #endif
11511279da7SMatthias Ringwald // default configuration
11611279da7SMatthias Ringwald gatt_client_mtu_exchange_enabled = true;
11711279da7SMatthias Ringwald gatt_client_required_security_level = LEVEL_0;
11811279da7SMatthias Ringwald
11911279da7SMatthias Ringwald // register for HCI Events
120f4b33574SMatthias Ringwald hci_event_callback_registration.callback = &gatt_client_event_packet_handler;
121361b1363SMatthias Ringwald hci_add_event_handler(&hci_event_callback_registration);
122361b1363SMatthias Ringwald
123f4b33574SMatthias Ringwald // register for SM Events
124f4b33574SMatthias Ringwald sm_event_callback_registration.callback = &gatt_client_event_packet_handler;
125f4b33574SMatthias Ringwald sm_add_event_handler(&sm_event_callback_registration);
126f4b33574SMatthias Ringwald
127361b1363SMatthias Ringwald // and ATT Client PDUs
1283deb3ec6SMatthias Ringwald att_dispatch_register_client(gatt_client_att_packet_handler);
129bbb8e698SMatthias Ringwald
130bbb8e698SMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
131bbb8e698SMatthias Ringwald gatt_client_eatt_enabled = true;
132bbb8e698SMatthias Ringwald #endif
1333deb3ec6SMatthias Ringwald }
1343deb3ec6SMatthias Ringwald
gatt_client_set_required_security_level(gap_security_level_t level)13511279da7SMatthias Ringwald void gatt_client_set_required_security_level(gap_security_level_t level){
13611279da7SMatthias Ringwald gatt_client_required_security_level = level;
13711279da7SMatthias Ringwald }
13811279da7SMatthias Ringwald
gatt_client_for_timer(btstack_timer_source_t * ts)139ec820d77SMatthias Ringwald static gatt_client_t * gatt_client_for_timer(btstack_timer_source_t * ts){
140665d90f2SMatthias Ringwald btstack_linked_list_iterator_t it;
141665d90f2SMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_client_connections);
142665d90f2SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
1435cf1669fSMatthias Ringwald gatt_client_t * gatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
1445cf1669fSMatthias Ringwald if (&gatt_client->gc_timeout == ts) {
1455cf1669fSMatthias Ringwald return gatt_client;
1463deb3ec6SMatthias Ringwald }
1473deb3ec6SMatthias Ringwald }
1483deb3ec6SMatthias Ringwald return NULL;
1493deb3ec6SMatthias Ringwald }
1503deb3ec6SMatthias Ringwald
gatt_client_timeout_handler(btstack_timer_source_t * timer)151ec820d77SMatthias Ringwald static void gatt_client_timeout_handler(btstack_timer_source_t * timer){
1525cf1669fSMatthias Ringwald gatt_client_t * gatt_client = gatt_client_for_timer(timer);
1535cf1669fSMatthias Ringwald if (gatt_client == NULL) return;
1545cf1669fSMatthias Ringwald log_info("GATT client timeout handle, handle 0x%02x", gatt_client->con_handle);
1555cf1669fSMatthias Ringwald gatt_client_report_error_if_pending(gatt_client, ATT_ERROR_TIMEOUT);
1563deb3ec6SMatthias Ringwald }
1573deb3ec6SMatthias Ringwald
gatt_client_timeout_start(gatt_client_t * gatt_client)1585cf1669fSMatthias Ringwald static void gatt_client_timeout_start(gatt_client_t * gatt_client){
1598ac3a0b3SMatthias Ringwald log_debug("GATT client timeout start, handle 0x%02x", gatt_client->con_handle);
1605cf1669fSMatthias Ringwald btstack_run_loop_remove_timer(&gatt_client->gc_timeout);
1615cf1669fSMatthias Ringwald btstack_run_loop_set_timer_handler(&gatt_client->gc_timeout, gatt_client_timeout_handler);
1625cf1669fSMatthias Ringwald btstack_run_loop_set_timer(&gatt_client->gc_timeout, 30000); // 30 seconds sm timeout
1635cf1669fSMatthias Ringwald btstack_run_loop_add_timer(&gatt_client->gc_timeout);
1643deb3ec6SMatthias Ringwald }
1653deb3ec6SMatthias Ringwald
gatt_client_timeout_stop(gatt_client_t * gatt_client)1665cf1669fSMatthias Ringwald static void gatt_client_timeout_stop(gatt_client_t * gatt_client){
1678ac3a0b3SMatthias Ringwald log_debug("GATT client timeout stop, handle 0x%02x", gatt_client->con_handle);
1685cf1669fSMatthias Ringwald btstack_run_loop_remove_timer(&gatt_client->gc_timeout);
1693deb3ec6SMatthias Ringwald }
1703deb3ec6SMatthias Ringwald
gatt_client_le_security_level_for_connection(hci_con_handle_t con_handle)1711dfae9c7SMatthias Ringwald static gap_security_level_t gatt_client_le_security_level_for_connection(hci_con_handle_t con_handle){
1721dfae9c7SMatthias Ringwald uint8_t encryption_key_size = gap_encryption_key_size(con_handle);
1731dfae9c7SMatthias Ringwald if (encryption_key_size == 0) return LEVEL_0;
1741dfae9c7SMatthias Ringwald
175ff3f1026SMatthias Ringwald bool authenticated = gap_authenticated(con_handle);
1761dfae9c7SMatthias Ringwald if (!authenticated) return LEVEL_2;
1771dfae9c7SMatthias Ringwald
1781dfae9c7SMatthias Ringwald return encryption_key_size == 16 ? LEVEL_4 : LEVEL_3;
1791dfae9c7SMatthias Ringwald }
1801dfae9c7SMatthias Ringwald
gatt_client_get_context_for_handle(uint16_t handle)1815cf1669fSMatthias Ringwald static gatt_client_t * gatt_client_get_context_for_handle(uint16_t handle){
182665d90f2SMatthias Ringwald btstack_linked_item_t *it;
183a0da043fSMatthias Ringwald for (it = (btstack_linked_item_t *) gatt_client_connections; it != NULL; it = it->next){
1845cf1669fSMatthias Ringwald gatt_client_t * gatt_client = (gatt_client_t *) it;
1855cf1669fSMatthias Ringwald if (gatt_client->con_handle == handle){
1865cf1669fSMatthias Ringwald return gatt_client;
1873deb3ec6SMatthias Ringwald }
1883deb3ec6SMatthias Ringwald }
1893deb3ec6SMatthias Ringwald return NULL;
1903deb3ec6SMatthias Ringwald }
1913deb3ec6SMatthias Ringwald
1923deb3ec6SMatthias Ringwald
1936b65794dSMilanka Ringwald // @return gatt_client context
1943deb3ec6SMatthias Ringwald // returns existing one, or tries to setup new one
gatt_client_provide_context_for_handle(hci_con_handle_t con_handle,gatt_client_t ** out_gatt_client)19540faeb84SMilanka Ringwald static uint8_t gatt_client_provide_context_for_handle(hci_con_handle_t con_handle, gatt_client_t ** out_gatt_client){
1965cf1669fSMatthias Ringwald gatt_client_t * gatt_client = gatt_client_get_context_for_handle(con_handle);
19740faeb84SMilanka Ringwald
19840faeb84SMilanka Ringwald if (gatt_client != NULL){
19940faeb84SMilanka Ringwald *out_gatt_client = gatt_client;
20040faeb84SMilanka Ringwald return ERROR_CODE_SUCCESS;
20140faeb84SMilanka Ringwald }
2023deb3ec6SMatthias Ringwald
203ae1d1237SMatthias Ringwald // bail if no such hci connection
2040e0bba86SMatthias Ringwald hci_connection_t * hci_connection = hci_connection_for_handle(con_handle);
2050e0bba86SMatthias Ringwald if (hci_connection == NULL){
206ae1d1237SMatthias Ringwald log_error("No connection for handle 0x%04x", con_handle);
20740faeb84SMilanka Ringwald *out_gatt_client = NULL;
20840faeb84SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
209ae1d1237SMatthias Ringwald }
21040faeb84SMilanka Ringwald
2115cf1669fSMatthias Ringwald gatt_client = btstack_memory_gatt_client_get();
21240faeb84SMilanka Ringwald if (gatt_client == NULL){
21340faeb84SMilanka Ringwald *out_gatt_client = NULL;
21440faeb84SMilanka Ringwald return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
21540faeb84SMilanka Ringwald }
2163deb3ec6SMatthias Ringwald // init state
217e9cdf30bSMatthias Ringwald gatt_client->bearer_type = ATT_BEARER_UNENHANCED_LE;
2185cf1669fSMatthias Ringwald gatt_client->con_handle = con_handle;
2195cf1669fSMatthias Ringwald gatt_client->mtu = ATT_DEFAULT_MTU;
2201dfae9c7SMatthias Ringwald gatt_client->security_level = gatt_client_le_security_level_for_connection(con_handle);
22111279da7SMatthias Ringwald if (gatt_client_mtu_exchange_enabled){
2225cf1669fSMatthias Ringwald gatt_client->mtu_state = SEND_MTU_EXCHANGE;
2235cf6c434SJakob Krantz } else {
2245cf1669fSMatthias Ringwald gatt_client->mtu_state = MTU_AUTO_EXCHANGE_DISABLED;
2255cf6c434SJakob Krantz }
226052dc82aSMatthias Ringwald gatt_client->state = P_READY;
227d9ced76fSMatthias Ringwald gatt_client->gatt_service_state = GATT_CLIENT_SERVICE_DISCOVER_W2_SEND;
228cbd76cecSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
229cbd76cecSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_IDLE;
230cbd76cecSMatthias Ringwald #endif
2315cf1669fSMatthias Ringwald btstack_linked_list_add(&gatt_client_connections, (btstack_linked_item_t*)gatt_client);
2320e0bba86SMatthias Ringwald
2330e0bba86SMatthias Ringwald // get unenhanced att bearer state
2340e0bba86SMatthias Ringwald if (hci_connection->att_connection.mtu_exchanged){
2350e0bba86SMatthias Ringwald gatt_client->mtu = hci_connection->att_connection.mtu;
2360e0bba86SMatthias Ringwald gatt_client->mtu_state = MTU_EXCHANGED;
2370e0bba86SMatthias Ringwald }
23840faeb84SMilanka Ringwald *out_gatt_client = gatt_client;
23940faeb84SMilanka Ringwald return ERROR_CODE_SUCCESS;
2403deb3ec6SMatthias Ringwald }
2413deb3ec6SMatthias Ringwald
is_ready(gatt_client_t * gatt_client)242de27733dSMatthias Ringwald static bool is_ready(gatt_client_t * gatt_client){
243052dc82aSMatthias Ringwald return gatt_client->state == P_READY;
244de27733dSMatthias Ringwald }
245de27733dSMatthias Ringwald
gatt_client_provide_context_for_request(hci_con_handle_t con_handle,gatt_client_t ** out_gatt_client)246de27733dSMatthias Ringwald static uint8_t gatt_client_provide_context_for_request(hci_con_handle_t con_handle, gatt_client_t ** out_gatt_client){
247de27733dSMatthias Ringwald gatt_client_t * gatt_client = NULL;
248de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
24940faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
25040faeb84SMilanka Ringwald return status;
25140faeb84SMilanka Ringwald }
252de27733dSMatthias Ringwald
2537627a0deSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
254bbb8e698SMatthias Ringwald if ((gatt_client->eatt_state == GATT_CLIENT_EATT_READY) && gatt_client_eatt_enabled){
2557627a0deSMatthias Ringwald btstack_linked_list_iterator_t it;
2567627a0deSMatthias Ringwald gatt_client_t * eatt_client = NULL;
2577627a0deSMatthias Ringwald // find free eatt client
2587627a0deSMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_client->eatt_clients);
2597627a0deSMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
2607627a0deSMatthias Ringwald gatt_client_t * client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
261052dc82aSMatthias Ringwald if (client->state == P_READY){
2627627a0deSMatthias Ringwald eatt_client = client;
2637627a0deSMatthias Ringwald break;
2647627a0deSMatthias Ringwald }
2657627a0deSMatthias Ringwald }
2667627a0deSMatthias Ringwald if (eatt_client == NULL){
2677627a0deSMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
2687627a0deSMatthias Ringwald }
2697627a0deSMatthias Ringwald gatt_client = eatt_client;
2707627a0deSMatthias Ringwald }
2717627a0deSMatthias Ringwald #endif
2727627a0deSMatthias Ringwald
273643a64fcSMatthias Ringwald if (is_ready(gatt_client) == false){
274de27733dSMatthias Ringwald return GATT_CLIENT_IN_WRONG_STATE;
2753deb3ec6SMatthias Ringwald }
2763deb3ec6SMatthias Ringwald
277de27733dSMatthias Ringwald gatt_client_timeout_start(gatt_client);
278de27733dSMatthias Ringwald
279de27733dSMatthias Ringwald *out_gatt_client = gatt_client;
280de27733dSMatthias Ringwald
281de27733dSMatthias Ringwald return status;
2823deb3ec6SMatthias Ringwald }
2833deb3ec6SMatthias Ringwald
gatt_client_is_ready(hci_con_handle_t con_handle)284fc64f94aSMatthias Ringwald int gatt_client_is_ready(hci_con_handle_t con_handle){
28540faeb84SMilanka Ringwald gatt_client_t * gatt_client;
28640faeb84SMilanka Ringwald uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
28740faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
28840faeb84SMilanka Ringwald return 0;
28940faeb84SMilanka Ringwald }
290aacf1b1aSMilanka Ringwald return is_ready(gatt_client) ? 1 : 0;
2913deb3ec6SMatthias Ringwald }
2923deb3ec6SMatthias Ringwald
gatt_client_mtu_enable_auto_negotiation(uint8_t enabled)2935cf6c434SJakob Krantz void gatt_client_mtu_enable_auto_negotiation(uint8_t enabled){
29411279da7SMatthias Ringwald gatt_client_mtu_exchange_enabled = enabled != 0;
2955cf6c434SJakob Krantz }
2965cf6c434SJakob Krantz
gatt_client_get_mtu(hci_con_handle_t con_handle,uint16_t * mtu)297fc64f94aSMatthias Ringwald uint8_t gatt_client_get_mtu(hci_con_handle_t con_handle, uint16_t * mtu){
29840faeb84SMilanka Ringwald gatt_client_t * gatt_client;
29940faeb84SMilanka Ringwald uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
30040faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
3016201dbadSMatthias Ringwald *mtu = 0;
30240faeb84SMilanka Ringwald return status;
30340faeb84SMilanka Ringwald }
3047d2258b3SMilanka Ringwald
3055cf1669fSMatthias Ringwald if ((gatt_client->mtu_state == MTU_EXCHANGED) || (gatt_client->mtu_state == MTU_AUTO_EXCHANGE_DISABLED)){
3065cf1669fSMatthias Ringwald *mtu = gatt_client->mtu;
3077d2258b3SMilanka Ringwald return ERROR_CODE_SUCCESS;
3083deb3ec6SMatthias Ringwald }
3093deb3ec6SMatthias Ringwald *mtu = ATT_DEFAULT_MTU;
310616edd56SMatthias Ringwald return GATT_CLIENT_IN_WRONG_STATE;
3113deb3ec6SMatthias Ringwald }
3123deb3ec6SMatthias Ringwald
gatt_client_reserve_request_buffer(gatt_client_t * gatt_client)3139228fd32SMatthias Ringwald static uint8_t *gatt_client_reserve_request_buffer(gatt_client_t *gatt_client) {
3147627a0deSMatthias Ringwald switch (gatt_client->bearer_type){
3157627a0deSMatthias Ringwald #ifdef ENABLE_GATT_OVER_CLASSIC
3167627a0deSMatthias Ringwald case ATT_BEARER_UNENHANCED_CLASSIC:
3177627a0deSMatthias Ringwald #endif
3187627a0deSMatthias Ringwald case ATT_BEARER_UNENHANCED_LE:
3199228fd32SMatthias Ringwald l2cap_reserve_packet_buffer();
3209228fd32SMatthias Ringwald return l2cap_get_outgoing_buffer();
3217627a0deSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
3227627a0deSMatthias Ringwald case ATT_BEARER_ENHANCED_LE:
3237627a0deSMatthias Ringwald return gatt_client->eatt_storage_buffer;
3247627a0deSMatthias Ringwald #endif
3257627a0deSMatthias Ringwald default:
3267627a0deSMatthias Ringwald btstack_unreachable();
3277627a0deSMatthias Ringwald break;
3287627a0deSMatthias Ringwald }
3297627a0deSMatthias Ringwald return NULL;
3309228fd32SMatthias Ringwald }
3319228fd32SMatthias Ringwald
3323deb3ec6SMatthias Ringwald // precondition: can_send_packet_now == TRUE
gatt_client_send(gatt_client_t * gatt_client,uint16_t len)3336e7b444cSMatthias Ringwald static uint8_t gatt_client_send(gatt_client_t * gatt_client, uint16_t len){
33446012949SMatthias Ringwald switch (gatt_client->bearer_type){
33546012949SMatthias Ringwald case ATT_BEARER_UNENHANCED_LE:
3366e7b444cSMatthias Ringwald return l2cap_send_prepared_connectionless(gatt_client->con_handle, L2CAP_CID_ATTRIBUTE_PROTOCOL, len);
33746012949SMatthias Ringwald #ifdef ENABLE_GATT_OVER_CLASSIC
33846012949SMatthias Ringwald case ATT_BEARER_UNENHANCED_CLASSIC:
33946012949SMatthias Ringwald return l2cap_send_prepared(gatt_client->l2cap_cid, len);
34046012949SMatthias Ringwald #endif
3417627a0deSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
3427627a0deSMatthias Ringwald case ATT_BEARER_ENHANCED_LE:
3437627a0deSMatthias Ringwald return l2cap_send(gatt_client->l2cap_cid, gatt_client->eatt_storage_buffer, len);
3447627a0deSMatthias Ringwald #endif
34546012949SMatthias Ringwald default:
34646012949SMatthias Ringwald btstack_unreachable();
34746012949SMatthias Ringwald return ERROR_CODE_HARDWARE_FAILURE;
34846012949SMatthias Ringwald }
3493deb3ec6SMatthias Ringwald }
3503deb3ec6SMatthias Ringwald
3513deb3ec6SMatthias Ringwald // precondition: can_send_packet_now == TRUE
att_confirmation(gatt_client_t * gatt_client)3526e7b444cSMatthias Ringwald static uint8_t att_confirmation(gatt_client_t * gatt_client) {
3539228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
3546e7b444cSMatthias Ringwald
3556e7b444cSMatthias Ringwald request[0] = ATT_HANDLE_VALUE_CONFIRMATION;
3566e7b444cSMatthias Ringwald
3576e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, 1);
3586e7b444cSMatthias Ringwald }
3596e7b444cSMatthias Ringwald
3606e7b444cSMatthias Ringwald // precondition: can_send_packet_now == TRUE
att_find_information_request(gatt_client_t * gatt_client,uint8_t request_type,uint16_t start_handle,uint16_t end_handle)3616e7b444cSMatthias Ringwald static uint8_t att_find_information_request(gatt_client_t *gatt_client, uint8_t request_type, uint16_t start_handle,
3626e7b444cSMatthias Ringwald uint16_t end_handle) {
3639228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
3646e7b444cSMatthias Ringwald
3653deb3ec6SMatthias Ringwald request[0] = request_type;
366f8fbdce0SMatthias Ringwald little_endian_store_16(request, 1, start_handle);
367f8fbdce0SMatthias Ringwald little_endian_store_16(request, 3, end_handle);
3683deb3ec6SMatthias Ringwald
3696e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, 5);
3703deb3ec6SMatthias Ringwald }
3713deb3ec6SMatthias Ringwald
3723deb3ec6SMatthias Ringwald // precondition: can_send_packet_now == TRUE
3736e7b444cSMatthias Ringwald static uint8_t
att_find_by_type_value_request(gatt_client_t * gatt_client,uint8_t request_type,uint16_t attribute_group_type,uint16_t start_handle,uint16_t end_handle,uint8_t * value,uint16_t value_size)3746e7b444cSMatthias Ringwald att_find_by_type_value_request(gatt_client_t *gatt_client, uint8_t request_type, uint16_t attribute_group_type,
3756e7b444cSMatthias Ringwald uint16_t start_handle, uint16_t end_handle, uint8_t *value, uint16_t value_size) {
3769228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
3773deb3ec6SMatthias Ringwald request[0] = request_type;
3789228fd32SMatthias Ringwald
379f8fbdce0SMatthias Ringwald little_endian_store_16(request, 1, start_handle);
380f8fbdce0SMatthias Ringwald little_endian_store_16(request, 3, end_handle);
381f8fbdce0SMatthias Ringwald little_endian_store_16(request, 5, attribute_group_type);
3826535961aSMatthias Ringwald (void)memcpy(&request[7], value, value_size);
3833deb3ec6SMatthias Ringwald
3846e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, 7u + value_size);
3853deb3ec6SMatthias Ringwald }
3863deb3ec6SMatthias Ringwald
3873deb3ec6SMatthias Ringwald // precondition: can_send_packet_now == TRUE
3886e7b444cSMatthias Ringwald static uint8_t
att_read_by_type_or_group_request_for_uuid16(gatt_client_t * gatt_client,uint8_t request_type,uint16_t uuid16,uint16_t start_handle,uint16_t end_handle)3896e7b444cSMatthias Ringwald att_read_by_type_or_group_request_for_uuid16(gatt_client_t *gatt_client, uint8_t request_type, uint16_t uuid16,
3906e7b444cSMatthias Ringwald uint16_t start_handle, uint16_t end_handle) {
3919228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
3926e7b444cSMatthias Ringwald
3933deb3ec6SMatthias Ringwald request[0] = request_type;
394f8fbdce0SMatthias Ringwald little_endian_store_16(request, 1, start_handle);
395f8fbdce0SMatthias Ringwald little_endian_store_16(request, 3, end_handle);
396f8fbdce0SMatthias Ringwald little_endian_store_16(request, 5, uuid16);
3973deb3ec6SMatthias Ringwald
3986e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, 7);
3993deb3ec6SMatthias Ringwald }
4003deb3ec6SMatthias Ringwald
4013deb3ec6SMatthias Ringwald // precondition: can_send_packet_now == TRUE
4026e7b444cSMatthias Ringwald static uint8_t
att_read_by_type_or_group_request_for_uuid128(gatt_client_t * gatt_client,uint8_t request_type,const uint8_t * uuid128,uint16_t start_handle,uint16_t end_handle)4036e7b444cSMatthias Ringwald att_read_by_type_or_group_request_for_uuid128(gatt_client_t *gatt_client, uint8_t request_type, const uint8_t *uuid128,
4046e7b444cSMatthias Ringwald uint16_t start_handle, uint16_t end_handle) {
4059228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
4066e7b444cSMatthias Ringwald
4073deb3ec6SMatthias Ringwald request[0] = request_type;
408f8fbdce0SMatthias Ringwald little_endian_store_16(request, 1, start_handle);
409f8fbdce0SMatthias Ringwald little_endian_store_16(request, 3, end_handle);
4109c80e4ccSMatthias Ringwald reverse_128(uuid128, &request[5]);
4113deb3ec6SMatthias Ringwald
4126e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, 21);
4133deb3ec6SMatthias Ringwald }
4143deb3ec6SMatthias Ringwald
4153deb3ec6SMatthias Ringwald // precondition: can_send_packet_now == TRUE
att_read_request(gatt_client_t * gatt_client,uint8_t request_type,uint16_t attribute_handle)4166e7b444cSMatthias Ringwald static uint8_t att_read_request(gatt_client_t *gatt_client, uint8_t request_type, uint16_t attribute_handle) {
4179228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
4186e7b444cSMatthias Ringwald
4193deb3ec6SMatthias Ringwald request[0] = request_type;
420f8fbdce0SMatthias Ringwald little_endian_store_16(request, 1, attribute_handle);
4213deb3ec6SMatthias Ringwald
4226e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, 3);
4233deb3ec6SMatthias Ringwald }
4243deb3ec6SMatthias Ringwald
4253deb3ec6SMatthias Ringwald // precondition: can_send_packet_now == TRUE
att_read_blob_request(gatt_client_t * gatt_client,uint8_t request_type,uint16_t attribute_handle,uint16_t value_offset)4266e7b444cSMatthias Ringwald static uint8_t att_read_blob_request(gatt_client_t *gatt_client, uint8_t request_type, uint16_t attribute_handle,
4276e7b444cSMatthias Ringwald uint16_t value_offset) {
4289228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
4299228fd32SMatthias Ringwald
4303deb3ec6SMatthias Ringwald request[0] = request_type;
431f8fbdce0SMatthias Ringwald little_endian_store_16(request, 1, attribute_handle);
432f8fbdce0SMatthias Ringwald little_endian_store_16(request, 3, value_offset);
4333deb3ec6SMatthias Ringwald
4346e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, 5);
4353deb3ec6SMatthias Ringwald }
4363deb3ec6SMatthias Ringwald
4376e7b444cSMatthias Ringwald static uint8_t
att_read_multiple_request_with_opcode(gatt_client_t * gatt_client,uint16_t num_value_handles,uint16_t * value_handles,uint8_t opcode)438f125a8efSMatthias Ringwald att_read_multiple_request_with_opcode(gatt_client_t *gatt_client, uint16_t num_value_handles, uint16_t *value_handles, uint8_t opcode) {
4399228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
4409228fd32SMatthias Ringwald
441f125a8efSMatthias Ringwald request[0] = opcode;
4429228fd32SMatthias Ringwald uint16_t i;
4439228fd32SMatthias Ringwald uint16_t offset = 1;
4443deb3ec6SMatthias Ringwald for (i=0;i<num_value_handles;i++){
445f8fbdce0SMatthias Ringwald little_endian_store_16(request, offset, value_handles[i]);
4463deb3ec6SMatthias Ringwald offset += 2;
4473deb3ec6SMatthias Ringwald }
44825b7c058SMilanka Ringwald
4496e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, offset);
4503deb3ec6SMatthias Ringwald }
4513deb3ec6SMatthias Ringwald
452f125a8efSMatthias Ringwald static uint8_t
att_read_multiple_request(gatt_client_t * gatt_client,uint16_t num_value_handles,uint16_t * value_handles)453f125a8efSMatthias Ringwald att_read_multiple_request(gatt_client_t *gatt_client, uint16_t num_value_handles, uint16_t *value_handles) {
454f125a8efSMatthias Ringwald return att_read_multiple_request_with_opcode(gatt_client, num_value_handles, value_handles, ATT_READ_MULTIPLE_REQUEST);
455f125a8efSMatthias Ringwald }
456f125a8efSMatthias Ringwald
457f125a8efSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
458f125a8efSMatthias Ringwald static uint8_t
att_read_multiple_variable_request(gatt_client_t * gatt_client,uint16_t num_value_handles,uint16_t * value_handles)459f125a8efSMatthias Ringwald att_read_multiple_variable_request(gatt_client_t *gatt_client, uint16_t num_value_handles, uint16_t *value_handles) {
460f125a8efSMatthias Ringwald return att_read_multiple_request_with_opcode(gatt_client, num_value_handles, value_handles, ATT_READ_MULTIPLE_VARIABLE_REQ);
461f125a8efSMatthias Ringwald }
462f125a8efSMatthias Ringwald #endif
463f125a8efSMatthias Ringwald
4647a766ebfSMatthias Ringwald #ifdef ENABLE_LE_SIGNED_WRITE
4653deb3ec6SMatthias Ringwald // precondition: can_send_packet_now == TRUE
att_signed_write_request(gatt_client_t * gatt_client,uint16_t request_type,uint16_t attribute_handle,uint16_t value_length,uint8_t * value,uint32_t sign_counter,uint8_t sgn[8])4666e7b444cSMatthias Ringwald static uint8_t att_signed_write_request(gatt_client_t *gatt_client, uint16_t request_type, uint16_t attribute_handle,
4676e7b444cSMatthias Ringwald uint16_t value_length, uint8_t *value, uint32_t sign_counter, uint8_t sgn[8]) {
4689228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
4699228fd32SMatthias Ringwald
4703deb3ec6SMatthias Ringwald request[0] = request_type;
471f8fbdce0SMatthias Ringwald little_endian_store_16(request, 1, attribute_handle);
4726535961aSMatthias Ringwald (void)memcpy(&request[3], value, value_length);
473f8fbdce0SMatthias Ringwald little_endian_store_32(request, 3 + value_length, sign_counter);
4749c80e4ccSMatthias Ringwald reverse_64(sgn, &request[3 + value_length + 4]);
47525b7c058SMilanka Ringwald
4766e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, 3 + value_length + 12);
4773deb3ec6SMatthias Ringwald }
4787a766ebfSMatthias Ringwald #endif
4793deb3ec6SMatthias Ringwald
4803deb3ec6SMatthias Ringwald // precondition: can_send_packet_now == TRUE
4816e7b444cSMatthias Ringwald static uint8_t
att_write_request(gatt_client_t * gatt_client,uint8_t request_type,uint16_t attribute_handle,uint16_t value_length,uint8_t * value)4826e7b444cSMatthias Ringwald att_write_request(gatt_client_t *gatt_client, uint8_t request_type, uint16_t attribute_handle, uint16_t value_length,
4836e7b444cSMatthias Ringwald uint8_t *value) {
4849228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
4859228fd32SMatthias Ringwald
4863deb3ec6SMatthias Ringwald request[0] = request_type;
487f8fbdce0SMatthias Ringwald little_endian_store_16(request, 1, attribute_handle);
4886535961aSMatthias Ringwald (void)memcpy(&request[3], value, value_length);
4893deb3ec6SMatthias Ringwald
4906e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, 3u + value_length);
4913deb3ec6SMatthias Ringwald }
4923deb3ec6SMatthias Ringwald
4933deb3ec6SMatthias Ringwald // precondition: can_send_packet_now == TRUE
att_execute_write_request(gatt_client_t * gatt_client,uint8_t request_type,uint8_t execute_write)4946e7b444cSMatthias Ringwald static uint8_t att_execute_write_request(gatt_client_t *gatt_client, uint8_t request_type, uint8_t execute_write) {
4959228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
4969228fd32SMatthias Ringwald
4973deb3ec6SMatthias Ringwald request[0] = request_type;
4983deb3ec6SMatthias Ringwald request[1] = execute_write;
49925b7c058SMilanka Ringwald
5006e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, 2);
5013deb3ec6SMatthias Ringwald }
5023deb3ec6SMatthias Ringwald
5033deb3ec6SMatthias Ringwald // precondition: can_send_packet_now == TRUE
att_prepare_write_request(gatt_client_t * gatt_client,uint8_t request_type,uint16_t attribute_handle,uint16_t value_offset,uint16_t blob_length,uint8_t * value)5046e7b444cSMatthias Ringwald static uint8_t att_prepare_write_request(gatt_client_t *gatt_client, uint8_t request_type, uint16_t attribute_handle,
5056e7b444cSMatthias Ringwald uint16_t value_offset, uint16_t blob_length, uint8_t *value) {
5069228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
5079228fd32SMatthias Ringwald
5083deb3ec6SMatthias Ringwald request[0] = request_type;
509f8fbdce0SMatthias Ringwald little_endian_store_16(request, 1, attribute_handle);
510f8fbdce0SMatthias Ringwald little_endian_store_16(request, 3, value_offset);
5116535961aSMatthias Ringwald (void)memcpy(&request[5], &value[value_offset], blob_length);
5123deb3ec6SMatthias Ringwald
5136e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, 5u + blob_length);
5143deb3ec6SMatthias Ringwald }
5153deb3ec6SMatthias Ringwald
att_exchange_mtu_request(gatt_client_t * gatt_client)5166e7b444cSMatthias Ringwald static uint8_t att_exchange_mtu_request(gatt_client_t *gatt_client) {
5179228fd32SMatthias Ringwald uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
5189228fd32SMatthias Ringwald
5193deb3ec6SMatthias Ringwald request[0] = ATT_EXCHANGE_MTU_REQUEST;
5209228fd32SMatthias Ringwald uint16_t mtu = l2cap_max_le_mtu();
521f8fbdce0SMatthias Ringwald little_endian_store_16(request, 1, mtu);
52225b7c058SMilanka Ringwald
5236e7b444cSMatthias Ringwald return gatt_client_send(gatt_client, 3);
5243deb3ec6SMatthias Ringwald }
5253deb3ec6SMatthias Ringwald
write_blob_length(gatt_client_t * gatt_client)5265cf1669fSMatthias Ringwald static uint16_t write_blob_length(gatt_client_t * gatt_client){
527dda77937SMatthias Ringwald uint16_t max_blob_length = gatt_client->mtu - 5u;
5285cf1669fSMatthias Ringwald if (gatt_client->attribute_offset >= gatt_client->attribute_length) {
5293deb3ec6SMatthias Ringwald return 0;
5303deb3ec6SMatthias Ringwald }
5315cf1669fSMatthias Ringwald uint16_t rest_length = gatt_client->attribute_length - gatt_client->attribute_offset;
5323deb3ec6SMatthias Ringwald if (max_blob_length > rest_length){
5333deb3ec6SMatthias Ringwald return rest_length;
5343deb3ec6SMatthias Ringwald }
5353deb3ec6SMatthias Ringwald return max_blob_length;
5363deb3ec6SMatthias Ringwald }
5373deb3ec6SMatthias Ringwald
send_gatt_services_request(gatt_client_t * gatt_client)5385cf1669fSMatthias Ringwald static void send_gatt_services_request(gatt_client_t *gatt_client){
5396e7b444cSMatthias Ringwald att_read_by_type_or_group_request_for_uuid16(gatt_client, ATT_READ_BY_GROUP_TYPE_REQUEST,
5406e7b444cSMatthias Ringwald gatt_client->uuid16, gatt_client->start_group_handle,
5416e7b444cSMatthias Ringwald gatt_client->end_group_handle);
5423deb3ec6SMatthias Ringwald }
5433deb3ec6SMatthias Ringwald
send_gatt_by_uuid_request(gatt_client_t * gatt_client,uint16_t attribute_group_type)5445cf1669fSMatthias Ringwald static void send_gatt_by_uuid_request(gatt_client_t *gatt_client, uint16_t attribute_group_type){
54552377058SMatthias Ringwald if (gatt_client->uuid16 != 0u){
5463deb3ec6SMatthias Ringwald uint8_t uuid16[2];
5475cf1669fSMatthias Ringwald little_endian_store_16(uuid16, 0, gatt_client->uuid16);
5486e7b444cSMatthias Ringwald att_find_by_type_value_request(gatt_client, ATT_FIND_BY_TYPE_VALUE_REQUEST, attribute_group_type,
5496e7b444cSMatthias Ringwald gatt_client->start_group_handle, gatt_client->end_group_handle, uuid16, 2);
5503deb3ec6SMatthias Ringwald return;
5513deb3ec6SMatthias Ringwald }
5523deb3ec6SMatthias Ringwald uint8_t uuid128[16];
5535cf1669fSMatthias Ringwald reverse_128(gatt_client->uuid128, uuid128);
5546e7b444cSMatthias Ringwald att_find_by_type_value_request(gatt_client, ATT_FIND_BY_TYPE_VALUE_REQUEST, attribute_group_type,
5556e7b444cSMatthias Ringwald gatt_client->start_group_handle, gatt_client->end_group_handle, uuid128, 16);
5563deb3ec6SMatthias Ringwald }
5573deb3ec6SMatthias Ringwald
send_gatt_services_by_uuid_request(gatt_client_t * gatt_client)5585cf1669fSMatthias Ringwald static void send_gatt_services_by_uuid_request(gatt_client_t *gatt_client){
5595cf1669fSMatthias Ringwald send_gatt_by_uuid_request(gatt_client, GATT_PRIMARY_SERVICE_UUID);
5603deb3ec6SMatthias Ringwald }
5613deb3ec6SMatthias Ringwald
send_gatt_included_service_uuid_request(gatt_client_t * gatt_client)5625cf1669fSMatthias Ringwald static void send_gatt_included_service_uuid_request(gatt_client_t *gatt_client){
5636e7b444cSMatthias Ringwald att_read_request(gatt_client, ATT_READ_REQUEST, gatt_client->query_start_handle);
5643deb3ec6SMatthias Ringwald }
5653deb3ec6SMatthias Ringwald
send_gatt_included_service_request(gatt_client_t * gatt_client)5665cf1669fSMatthias Ringwald static void send_gatt_included_service_request(gatt_client_t *gatt_client){
5676e7b444cSMatthias Ringwald att_read_by_type_or_group_request_for_uuid16(gatt_client, ATT_READ_BY_TYPE_REQUEST,
5686e7b444cSMatthias Ringwald GATT_INCLUDE_SERVICE_UUID, gatt_client->start_group_handle,
5696e7b444cSMatthias Ringwald gatt_client->end_group_handle);
5703deb3ec6SMatthias Ringwald }
5713deb3ec6SMatthias Ringwald
send_gatt_characteristic_request(gatt_client_t * gatt_client)5725cf1669fSMatthias Ringwald static void send_gatt_characteristic_request(gatt_client_t *gatt_client){
5736e7b444cSMatthias Ringwald att_read_by_type_or_group_request_for_uuid16(gatt_client, ATT_READ_BY_TYPE_REQUEST,
5746e7b444cSMatthias Ringwald GATT_CHARACTERISTICS_UUID, gatt_client->start_group_handle,
5756e7b444cSMatthias Ringwald gatt_client->end_group_handle);
5763deb3ec6SMatthias Ringwald }
5773deb3ec6SMatthias Ringwald
send_gatt_characteristic_descriptor_request(gatt_client_t * gatt_client)5785cf1669fSMatthias Ringwald static void send_gatt_characteristic_descriptor_request(gatt_client_t *gatt_client){
5796e7b444cSMatthias Ringwald att_find_information_request(gatt_client, ATT_FIND_INFORMATION_REQUEST, gatt_client->start_group_handle,
5806e7b444cSMatthias Ringwald gatt_client->end_group_handle);
5813deb3ec6SMatthias Ringwald }
5823deb3ec6SMatthias Ringwald
send_gatt_read_characteristic_value_request(gatt_client_t * gatt_client)5835cf1669fSMatthias Ringwald static void send_gatt_read_characteristic_value_request(gatt_client_t *gatt_client){
5846e7b444cSMatthias Ringwald att_read_request(gatt_client, ATT_READ_REQUEST, gatt_client->attribute_handle);
5853deb3ec6SMatthias Ringwald }
5863deb3ec6SMatthias Ringwald
send_gatt_read_by_type_request(gatt_client_t * gatt_client)5875cf1669fSMatthias Ringwald static void send_gatt_read_by_type_request(gatt_client_t * gatt_client){
58852377058SMatthias Ringwald if (gatt_client->uuid16 != 0u){
5896e7b444cSMatthias Ringwald att_read_by_type_or_group_request_for_uuid16(gatt_client, ATT_READ_BY_TYPE_REQUEST,
5906e7b444cSMatthias Ringwald gatt_client->uuid16, gatt_client->start_group_handle,
5916e7b444cSMatthias Ringwald gatt_client->end_group_handle);
5923deb3ec6SMatthias Ringwald } else {
5936e7b444cSMatthias Ringwald att_read_by_type_or_group_request_for_uuid128(gatt_client, ATT_READ_BY_TYPE_REQUEST,
5946e7b444cSMatthias Ringwald gatt_client->uuid128, gatt_client->start_group_handle,
5956e7b444cSMatthias Ringwald gatt_client->end_group_handle);
5963deb3ec6SMatthias Ringwald }
5973deb3ec6SMatthias Ringwald }
5983deb3ec6SMatthias Ringwald
send_gatt_read_blob_request(gatt_client_t * gatt_client)5995cf1669fSMatthias Ringwald static void send_gatt_read_blob_request(gatt_client_t *gatt_client){
60030952227SMilanka Ringwald if (gatt_client->attribute_offset == 0){
6016e7b444cSMatthias Ringwald att_read_request(gatt_client, ATT_READ_REQUEST, gatt_client->attribute_handle);
60230952227SMilanka Ringwald } else {
6036e7b444cSMatthias Ringwald att_read_blob_request(gatt_client, ATT_READ_BLOB_REQUEST, gatt_client->attribute_handle,
6046e7b444cSMatthias Ringwald gatt_client->attribute_offset);
6053deb3ec6SMatthias Ringwald }
60630952227SMilanka Ringwald }
6073deb3ec6SMatthias Ringwald
send_gatt_read_multiple_request(gatt_client_t * gatt_client)6085cf1669fSMatthias Ringwald static void send_gatt_read_multiple_request(gatt_client_t * gatt_client){
6096e7b444cSMatthias Ringwald att_read_multiple_request(gatt_client, gatt_client->read_multiple_handle_count, gatt_client->read_multiple_handles);
6103deb3ec6SMatthias Ringwald }
6113deb3ec6SMatthias Ringwald
612f125a8efSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
send_gatt_read_multiple_variable_request(gatt_client_t * gatt_client)613f125a8efSMatthias Ringwald static void send_gatt_read_multiple_variable_request(gatt_client_t * gatt_client){
614f125a8efSMatthias Ringwald att_read_multiple_variable_request(gatt_client, gatt_client->read_multiple_handle_count, gatt_client->read_multiple_handles);
615f125a8efSMatthias Ringwald }
616f125a8efSMatthias Ringwald #endif
617f125a8efSMatthias Ringwald
send_gatt_write_attribute_value_request(gatt_client_t * gatt_client)6185cf1669fSMatthias Ringwald static void send_gatt_write_attribute_value_request(gatt_client_t * gatt_client){
6196e7b444cSMatthias Ringwald att_write_request(gatt_client, ATT_WRITE_REQUEST, gatt_client->attribute_handle, gatt_client->attribute_length,
6206e7b444cSMatthias Ringwald gatt_client->attribute_value);
6213deb3ec6SMatthias Ringwald }
6223deb3ec6SMatthias Ringwald
send_gatt_write_client_characteristic_configuration_request(gatt_client_t * gatt_client)6235cf1669fSMatthias Ringwald static void send_gatt_write_client_characteristic_configuration_request(gatt_client_t * gatt_client){
6246e7b444cSMatthias Ringwald att_write_request(gatt_client, ATT_WRITE_REQUEST, gatt_client->client_characteristic_configuration_handle, 2,
6256e7b444cSMatthias Ringwald gatt_client->client_characteristic_configuration_value);
6263deb3ec6SMatthias Ringwald }
6273deb3ec6SMatthias Ringwald
send_gatt_prepare_write_request(gatt_client_t * gatt_client)6285cf1669fSMatthias Ringwald static void send_gatt_prepare_write_request(gatt_client_t * gatt_client){
6296e7b444cSMatthias Ringwald att_prepare_write_request(gatt_client, ATT_PREPARE_WRITE_REQUEST, gatt_client->attribute_handle,
6306e7b444cSMatthias Ringwald gatt_client->attribute_offset, write_blob_length(gatt_client),
6316e7b444cSMatthias Ringwald gatt_client->attribute_value);
6323deb3ec6SMatthias Ringwald }
6333deb3ec6SMatthias Ringwald
send_gatt_execute_write_request(gatt_client_t * gatt_client)6345cf1669fSMatthias Ringwald static void send_gatt_execute_write_request(gatt_client_t * gatt_client){
6356e7b444cSMatthias Ringwald att_execute_write_request(gatt_client, ATT_EXECUTE_WRITE_REQUEST, 1);
6363deb3ec6SMatthias Ringwald }
6373deb3ec6SMatthias Ringwald
send_gatt_cancel_prepared_write_request(gatt_client_t * gatt_client)6385cf1669fSMatthias Ringwald static void send_gatt_cancel_prepared_write_request(gatt_client_t * gatt_client){
6396e7b444cSMatthias Ringwald att_execute_write_request(gatt_client, ATT_EXECUTE_WRITE_REQUEST, 0);
6403deb3ec6SMatthias Ringwald }
6413deb3ec6SMatthias Ringwald
642*197b4dd3SMatthias Ringwald #ifndef ENABLE_GATT_FIND_INFORMATION_FOR_CCC_DISCOVERY
send_gatt_read_client_characteristic_configuration_request(gatt_client_t * gatt_client)643*197b4dd3SMatthias Ringwald static void send_gatt_read_client_characteristic_configuration_request(gatt_client_t * gatt_client){
644*197b4dd3SMatthias Ringwald att_read_by_type_or_group_request_for_uuid16(gatt_client, ATT_READ_BY_TYPE_REQUEST,
645*197b4dd3SMatthias Ringwald GATT_CLIENT_CHARACTERISTICS_CONFIGURATION,
646*197b4dd3SMatthias Ringwald gatt_client->start_group_handle, gatt_client->end_group_handle);
647*197b4dd3SMatthias Ringwald }
648*197b4dd3SMatthias Ringwald #endif
649*197b4dd3SMatthias Ringwald
send_gatt_read_characteristic_descriptor_request(gatt_client_t * gatt_client)6505cf1669fSMatthias Ringwald static void send_gatt_read_characteristic_descriptor_request(gatt_client_t * gatt_client){
6516e7b444cSMatthias Ringwald att_read_request(gatt_client, ATT_READ_REQUEST, gatt_client->attribute_handle);
6523deb3ec6SMatthias Ringwald }
6533deb3ec6SMatthias Ringwald
6547a766ebfSMatthias Ringwald #ifdef ENABLE_LE_SIGNED_WRITE
send_gatt_signed_write_request(gatt_client_t * gatt_client,uint32_t sign_counter)6555cf1669fSMatthias Ringwald static void send_gatt_signed_write_request(gatt_client_t * gatt_client, uint32_t sign_counter){
6566e7b444cSMatthias Ringwald att_signed_write_request(gatt_client, ATT_SIGNED_WRITE_COMMAND, gatt_client->attribute_handle,
6576e7b444cSMatthias Ringwald gatt_client->attribute_length, gatt_client->attribute_value, sign_counter,
6586e7b444cSMatthias Ringwald gatt_client->cmac);
6593deb3ec6SMatthias Ringwald }
6607a766ebfSMatthias Ringwald #endif
6613deb3ec6SMatthias Ringwald
get_last_result_handle_from_service_list(uint8_t * packet,uint16_t size)6623deb3ec6SMatthias Ringwald static uint16_t get_last_result_handle_from_service_list(uint8_t * packet, uint16_t size){
66339ac9711SMatthias Ringwald if (size < 2) return 0xffff;
6643deb3ec6SMatthias Ringwald uint8_t attr_length = packet[1];
66539ac9711SMatthias Ringwald if ((2 + attr_length) > size) return 0xffff;
6664ea43905SMatthias Ringwald return little_endian_read_16(packet, size - attr_length + 2u);
6673deb3ec6SMatthias Ringwald }
6683deb3ec6SMatthias Ringwald
get_last_result_handle_from_characteristics_list(uint8_t * packet,uint16_t size)6693deb3ec6SMatthias Ringwald static uint16_t get_last_result_handle_from_characteristics_list(uint8_t * packet, uint16_t size){
67039ac9711SMatthias Ringwald if (size < 2) return 0xffff;
6713deb3ec6SMatthias Ringwald uint8_t attr_length = packet[1];
67239ac9711SMatthias Ringwald if ((2 + attr_length) > size) return 0xffff;
6734ea43905SMatthias Ringwald return little_endian_read_16(packet, size - attr_length + 3u);
6743deb3ec6SMatthias Ringwald }
6753deb3ec6SMatthias Ringwald
get_last_result_handle_from_included_services_list(uint8_t * packet,uint16_t size)6763deb3ec6SMatthias Ringwald static uint16_t get_last_result_handle_from_included_services_list(uint8_t * packet, uint16_t size){
67739ac9711SMatthias Ringwald if (size < 2) return 0xffff;
6783deb3ec6SMatthias Ringwald uint8_t attr_length = packet[1];
67939ac9711SMatthias Ringwald if ((2 + attr_length) > size) return 0xffff;
680f8fbdce0SMatthias Ringwald return little_endian_read_16(packet, size - attr_length);
6813deb3ec6SMatthias Ringwald }
6823deb3ec6SMatthias Ringwald
683842492f0SMatthias Ringwald #ifdef ENABLE_GATT_CLIENT_SERVICE_CHANGED
gatt_client_service_emit_event(gatt_client_t * gatt_client,uint8_t * event,uint16_t size)68458e8c9f5SMatthias Ringwald static void gatt_client_service_emit_event(gatt_client_t * gatt_client, uint8_t * event, uint16_t size){
68558e8c9f5SMatthias Ringwald btstack_linked_list_iterator_t it;
68658e8c9f5SMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_client_service_changed_handler);
68758e8c9f5SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)) {
68858e8c9f5SMatthias Ringwald btstack_packet_callback_registration_t *callback = (btstack_packet_callback_registration_t *) btstack_linked_list_iterator_next(&it);
68958e8c9f5SMatthias Ringwald (*callback->callback)(HCI_EVENT_PACKET, (uint16_t) gatt_client->con_handle, event, size);
69058e8c9f5SMatthias Ringwald }
69158e8c9f5SMatthias Ringwald }
69258e8c9f5SMatthias Ringwald
69358e8c9f5SMatthias Ringwald static void
gatt_client_service_emit_database_hash(gatt_client_t * gatt_client,const uint8_t * value,uint16_t value_len)69458e8c9f5SMatthias Ringwald gatt_client_service_emit_database_hash(gatt_client_t *gatt_client, const uint8_t *value, uint16_t value_len) {
69558e8c9f5SMatthias Ringwald if (value_len == 16){
69658e8c9f5SMatthias Ringwald uint8_t event[21];
69758e8c9f5SMatthias Ringwald hci_event_builder_context_t context;
69858e8c9f5SMatthias Ringwald hci_event_builder_init(&context, event, sizeof(event), HCI_EVENT_GATTSERVICE_META, GATTSERVICE_SUBEVENT_GATT_DATABASE_HASH);
69958e8c9f5SMatthias Ringwald hci_event_builder_add_con_handle(&context, gatt_client->con_handle);
70058e8c9f5SMatthias Ringwald hci_event_builder_add_bytes(&context, value, 16);
70158e8c9f5SMatthias Ringwald gatt_client_service_emit_event(gatt_client, event, hci_event_builder_get_length(&context));
70258e8c9f5SMatthias Ringwald }
70358e8c9f5SMatthias Ringwald }
70458e8c9f5SMatthias Ringwald
70558e8c9f5SMatthias Ringwald static void
gatt_client_service_emit_service_changed(gatt_client_t * gatt_client,const uint8_t * value,uint16_t value_len)70658e8c9f5SMatthias Ringwald gatt_client_service_emit_service_changed(gatt_client_t *gatt_client, const uint8_t *value, uint16_t value_len) {
70758e8c9f5SMatthias Ringwald if (value_len == 4){
70858e8c9f5SMatthias Ringwald uint8_t event[9];
70958e8c9f5SMatthias Ringwald hci_event_builder_context_t context;
71058e8c9f5SMatthias Ringwald hci_event_builder_init(&context, event, sizeof(event), HCI_EVENT_GATTSERVICE_META, GATTSERVICE_SUBEVENT_GATT_SERVICE_CHANGED);
71158e8c9f5SMatthias Ringwald hci_event_builder_add_con_handle(&context, gatt_client->con_handle);
71258e8c9f5SMatthias Ringwald hci_event_builder_add_bytes(&context, value, 4);
71358e8c9f5SMatthias Ringwald gatt_client_service_emit_event(gatt_client, event, hci_event_builder_get_length(&context));
71458e8c9f5SMatthias Ringwald }
71558e8c9f5SMatthias Ringwald }
71658e8c9f5SMatthias Ringwald
gatt_client_service_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)717d9ced76fSMatthias Ringwald static void gatt_client_service_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
718e2c98440SMatthias Ringwald UNUSED(channel); // ok: handling own l2cap events
719e2c98440SMatthias Ringwald UNUSED(size); // ok: there is no channel
720e2c98440SMatthias Ringwald
721d9ced76fSMatthias Ringwald hci_con_handle_t con_handle;
722d9ced76fSMatthias Ringwald gatt_client_t *gatt_client;
723d9ced76fSMatthias Ringwald gatt_client_service_t service;
724d9ced76fSMatthias Ringwald gatt_client_characteristic_t characteristic;
725d9ced76fSMatthias Ringwald switch (packet_type) {
726d9ced76fSMatthias Ringwald case HCI_EVENT_PACKET:
727d9ced76fSMatthias Ringwald switch (hci_event_packet_get_type(packet)) {
728d9ced76fSMatthias Ringwald case GATT_EVENT_SERVICE_QUERY_RESULT:
729d9ced76fSMatthias Ringwald con_handle = gatt_event_service_query_result_get_handle(packet);
730d9ced76fSMatthias Ringwald gatt_client = gatt_client_get_context_for_handle(con_handle);
731d9ced76fSMatthias Ringwald btstack_assert(gatt_client != NULL);
732d9ced76fSMatthias Ringwald btstack_assert(gatt_client->gatt_service_state == GATT_CLIENT_SERVICE_DISCOVER_W4_DONE);
733d9ced76fSMatthias Ringwald gatt_event_service_query_result_get_service(packet, &service);
734d9ced76fSMatthias Ringwald gatt_client->gatt_service_start_group_handle = service.start_group_handle;
735d9ced76fSMatthias Ringwald gatt_client->gatt_service_end_group_handle = service.end_group_handle;
736d9ced76fSMatthias Ringwald break;
737d9ced76fSMatthias Ringwald case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
738d9ced76fSMatthias Ringwald con_handle = gatt_event_characteristic_query_result_get_handle(packet);
739d9ced76fSMatthias Ringwald gatt_client = gatt_client_get_context_for_handle(con_handle);
740d9ced76fSMatthias Ringwald btstack_assert(gatt_client != NULL);
741d9ced76fSMatthias Ringwald btstack_assert(gatt_client->gatt_service_state == GATT_CLIENT_SERVICE_DISCOVER_CHARACTERISTICS_W4_DONE);
742d9ced76fSMatthias Ringwald gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
743d9ced76fSMatthias Ringwald switch (characteristic.uuid16){
744d9ced76fSMatthias Ringwald case ORG_BLUETOOTH_CHARACTERISTIC_GATT_SERVICE_CHANGED:
745d9ced76fSMatthias Ringwald gatt_client->gatt_service_changed_value_handle = characteristic.value_handle;
746d9ced76fSMatthias Ringwald gatt_client->gatt_service_changed_end_handle = characteristic.end_handle;
747d9ced76fSMatthias Ringwald break;
748d9ced76fSMatthias Ringwald case ORG_BLUETOOTH_CHARACTERISTIC_DATABASE_HASH:
749d9ced76fSMatthias Ringwald gatt_client->gatt_service_database_hash_value_handle = characteristic.value_handle;
750d9ced76fSMatthias Ringwald gatt_client->gatt_service_database_hash_end_handle = characteristic.end_handle;
751d9ced76fSMatthias Ringwald break;
752d9ced76fSMatthias Ringwald default:
753d9ced76fSMatthias Ringwald break;
754d9ced76fSMatthias Ringwald }
755d9ced76fSMatthias Ringwald break;
756d9ced76fSMatthias Ringwald case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
757d9ced76fSMatthias Ringwald con_handle = gatt_event_characteristic_value_query_result_get_handle(packet);
758d9ced76fSMatthias Ringwald gatt_client = gatt_client_get_context_for_handle(con_handle);
759d9ced76fSMatthias Ringwald btstack_assert(gatt_client != NULL);
760d9ced76fSMatthias Ringwald btstack_assert(gatt_client->gatt_service_state == GATT_CLIENT_SERVICE_DATABASE_HASH_READ_W4_DONE);
76158e8c9f5SMatthias Ringwald gatt_client_service_emit_database_hash(gatt_client,
76258e8c9f5SMatthias Ringwald gatt_event_characteristic_value_query_result_get_value(packet),
76358e8c9f5SMatthias Ringwald gatt_event_characteristic_value_query_result_get_value_length(packet));
764d9ced76fSMatthias Ringwald break;
765d9ced76fSMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE:
766d9ced76fSMatthias Ringwald con_handle = gatt_event_query_complete_get_handle(packet);
767d9ced76fSMatthias Ringwald gatt_client = gatt_client_get_context_for_handle(con_handle);
768d9ced76fSMatthias Ringwald btstack_assert(gatt_client != NULL);
769d9ced76fSMatthias Ringwald switch (gatt_client->gatt_service_state) {
770d9ced76fSMatthias Ringwald case GATT_CLIENT_SERVICE_DISCOVER_W4_DONE:
771d9ced76fSMatthias Ringwald gatt_client->gatt_service_state = GATT_CLIENT_SERVICE_DISCOVER_CHARACTERISTICS_W2_SEND;
772d9ced76fSMatthias Ringwald break;
773d9ced76fSMatthias Ringwald case GATT_CLIENT_SERVICE_DISCOVER_CHARACTERISTICS_W4_DONE:
774d9ced76fSMatthias Ringwald gatt_client->gatt_service_state = GATT_CLIENT_SERVICE_SERVICE_CHANGED_WRITE_CCCD_W2_SEND;
775d9ced76fSMatthias Ringwald break;
776d9ced76fSMatthias Ringwald case GATT_CLIENT_SERVICE_SERVICE_CHANGED_WRITE_CCCD_W4_DONE:
777d9ced76fSMatthias Ringwald gatt_client->gatt_service_state = GATT_CLIENT_SERVICE_DATABASE_HASH_READ_W2_SEND;
778d9ced76fSMatthias Ringwald break;
779d9ced76fSMatthias Ringwald case GATT_CLIENT_SERVICE_DATABASE_HASH_READ_W4_DONE:
780d9ced76fSMatthias Ringwald gatt_client->gatt_service_state = GATT_CLIENT_SERVICE_DATABASE_HASH_WRITE_CCCD_W2_SEND;
781d9ced76fSMatthias Ringwald break;
782d9ced76fSMatthias Ringwald case GATT_CLIENT_SERVICE_DATABASE_HASH_WRITE_CCCD_W4_DONE:
783d9ced76fSMatthias Ringwald gatt_client->gatt_service_state = GATT_CLIENT_SERVICE_DONE;
784d9ced76fSMatthias Ringwald break;
785d9ced76fSMatthias Ringwald default:
786d9ced76fSMatthias Ringwald btstack_unreachable();
787d9ced76fSMatthias Ringwald break;
788d9ced76fSMatthias Ringwald }
789d9ced76fSMatthias Ringwald break;
790d9ced76fSMatthias Ringwald default:
791d9ced76fSMatthias Ringwald break;
792d9ced76fSMatthias Ringwald }
793d9ced76fSMatthias Ringwald break;
794d9ced76fSMatthias Ringwald default:
795d9ced76fSMatthias Ringwald break;
796d9ced76fSMatthias Ringwald }
797d9ced76fSMatthias Ringwald }
798842492f0SMatthias Ringwald #endif
799d9ced76fSMatthias Ringwald
gatt_client_notify_can_send_query(gatt_client_t * gatt_client)80053e9c18fSMatthias Ringwald static void gatt_client_notify_can_send_query(gatt_client_t * gatt_client){
80119c614acSMatthias Ringwald
80219c614acSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
80319c614acSMatthias Ringwald // if eatt is ready, notify all clients that can send a query
80419c614acSMatthias Ringwald if (gatt_client->eatt_state == GATT_CLIENT_EATT_READY){
80519c614acSMatthias Ringwald btstack_linked_list_iterator_t it;
80619c614acSMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_client->eatt_clients);
80719c614acSMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
80819c614acSMatthias Ringwald gatt_client_t * client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
809052dc82aSMatthias Ringwald if (client->state == P_READY){
81019c614acSMatthias Ringwald // call callback
81119c614acSMatthias Ringwald btstack_context_callback_registration_t * callback = (btstack_context_callback_registration_t *) btstack_linked_list_pop(&gatt_client->query_requests);
81219c614acSMatthias Ringwald if (callback == NULL) {
81319c614acSMatthias Ringwald return;
81419c614acSMatthias Ringwald }
81519c614acSMatthias Ringwald (*callback->callback)(callback->context);
81619c614acSMatthias Ringwald }
81719c614acSMatthias Ringwald }
81819c614acSMatthias Ringwald return;
81919c614acSMatthias Ringwald }
82019c614acSMatthias Ringwald #endif
82119c614acSMatthias Ringwald
822842492f0SMatthias Ringwald while (gatt_client->state == P_READY){
823842492f0SMatthias Ringwald bool query_sent = false;
824842492f0SMatthias Ringwald UNUSED(query_sent);
825842492f0SMatthias Ringwald
826842492f0SMatthias Ringwald #ifdef ENABLE_GATT_CLIENT_SERVICE_CHANGED
827d9ced76fSMatthias Ringwald uint8_t status = ERROR_CODE_SUCCESS;
828d9ced76fSMatthias Ringwald gatt_client_service_t gatt_service;
829d9ced76fSMatthias Ringwald gatt_client_characteristic_t characteristic;
830d9ced76fSMatthias Ringwald switch (gatt_client->gatt_service_state){
831d9ced76fSMatthias Ringwald case GATT_CLIENT_SERVICE_DISCOVER_W2_SEND:
832d9ced76fSMatthias Ringwald gatt_client->gatt_service_state = GATT_CLIENT_SERVICE_DISCOVER_W4_DONE;
833d9ced76fSMatthias Ringwald status = gatt_client_discover_primary_services_by_uuid16(&gatt_client_service_packet_handler,
834d9ced76fSMatthias Ringwald gatt_client->con_handle,
835d9ced76fSMatthias Ringwald ORG_BLUETOOTH_SERVICE_GENERIC_ATTRIBUTE);
836d9ced76fSMatthias Ringwald query_sent = true;
837d9ced76fSMatthias Ringwald break;
838d9ced76fSMatthias Ringwald case GATT_CLIENT_SERVICE_DISCOVER_CHARACTERISTICS_W2_SEND:
839d9ced76fSMatthias Ringwald if (gatt_client->gatt_service_start_group_handle != 0){
840d9ced76fSMatthias Ringwald gatt_client->gatt_service_state = GATT_CLIENT_SERVICE_DISCOVER_CHARACTERISTICS_W4_DONE;
841d9ced76fSMatthias Ringwald gatt_service.start_group_handle = gatt_client->gatt_service_start_group_handle;
842d9ced76fSMatthias Ringwald gatt_service.end_group_handle = gatt_client->gatt_service_end_group_handle;
843d9ced76fSMatthias Ringwald status = gatt_client_discover_characteristics_for_service(&gatt_client_service_packet_handler, gatt_client->con_handle, &gatt_service);
844d9ced76fSMatthias Ringwald query_sent = true;
845d9ced76fSMatthias Ringwald break;
846d9ced76fSMatthias Ringwald }
847d9ced76fSMatthias Ringwald
848d9ced76fSMatthias Ringwald /* fall through */
849d9ced76fSMatthias Ringwald
850d9ced76fSMatthias Ringwald case GATT_CLIENT_SERVICE_SERVICE_CHANGED_WRITE_CCCD_W2_SEND:
851d9ced76fSMatthias Ringwald if (gatt_client->gatt_service_changed_value_handle != 0){
852d9ced76fSMatthias Ringwald gatt_client->gatt_service_state = GATT_CLIENT_SERVICE_SERVICE_CHANGED_WRITE_CCCD_W4_DONE;
853d9ced76fSMatthias Ringwald characteristic.value_handle = gatt_client->gatt_service_changed_value_handle;
854d9ced76fSMatthias Ringwald characteristic.end_handle = gatt_client->gatt_service_changed_end_handle;
855d9ced76fSMatthias Ringwald // we assume good case. We cannot do much otherwise
856d9ced76fSMatthias Ringwald characteristic.properties = ATT_PROPERTY_INDICATE;
857d9ced76fSMatthias Ringwald status = gatt_client_write_client_characteristic_configuration(&gatt_client_service_packet_handler,
858d9ced76fSMatthias Ringwald gatt_client->con_handle, &characteristic,
859d9ced76fSMatthias Ringwald GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION);
860d9ced76fSMatthias Ringwald query_sent = true;
861d9ced76fSMatthias Ringwald break;
862d9ced76fSMatthias Ringwald }
863d9ced76fSMatthias Ringwald
864d9ced76fSMatthias Ringwald /* fall through */
865d9ced76fSMatthias Ringwald
866d9ced76fSMatthias Ringwald case GATT_CLIENT_SERVICE_DATABASE_HASH_READ_W2_SEND:
867d9ced76fSMatthias Ringwald if (gatt_client->gatt_service_database_hash_value_handle != 0){
868d9ced76fSMatthias Ringwald gatt_client->gatt_service_state = GATT_CLIENT_SERVICE_DATABASE_HASH_READ_W4_DONE;
869d9ced76fSMatthias Ringwald status = gatt_client_read_value_of_characteristics_by_uuid16(&gatt_client_service_packet_handler,
870d9ced76fSMatthias Ringwald gatt_client->con_handle,
871d9ced76fSMatthias Ringwald 0x0001, 0xffff, ORG_BLUETOOTH_CHARACTERISTIC_DATABASE_HASH);
872d9ced76fSMatthias Ringwald query_sent = true;
873d9ced76fSMatthias Ringwald break;
874d9ced76fSMatthias Ringwald }
875d9ced76fSMatthias Ringwald
876d9ced76fSMatthias Ringwald /* fall through */
877d9ced76fSMatthias Ringwald
878d9ced76fSMatthias Ringwald case GATT_CLIENT_SERVICE_DATABASE_HASH_WRITE_CCCD_W2_SEND:
879d9ced76fSMatthias Ringwald if (gatt_client->gatt_service_database_hash_value_handle != 0) {
880d9ced76fSMatthias Ringwald gatt_client->gatt_service_state = GATT_CLIENT_SERVICE_DATABASE_HASH_WRITE_CCCD_W4_DONE;
881d9ced76fSMatthias Ringwald characteristic.value_handle = gatt_client->gatt_service_database_hash_value_handle;
882d9ced76fSMatthias Ringwald characteristic.end_handle = gatt_client->gatt_service_database_hash_end_handle;
883d9ced76fSMatthias Ringwald // we assume good case. We cannot do much otherwise
884d9ced76fSMatthias Ringwald characteristic.properties = ATT_PROPERTY_INDICATE;
885d9ced76fSMatthias Ringwald status = gatt_client_write_client_characteristic_configuration(&gatt_client_service_packet_handler,
886d9ced76fSMatthias Ringwald gatt_client->con_handle,
887d9ced76fSMatthias Ringwald &characteristic,
888d9ced76fSMatthias Ringwald GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION);
889d9ced76fSMatthias Ringwald query_sent = true;
890d9ced76fSMatthias Ringwald break;
891d9ced76fSMatthias Ringwald }
892d9ced76fSMatthias Ringwald
893d9ced76fSMatthias Ringwald // DONE
894d9ced76fSMatthias Ringwald gatt_client->gatt_service_state = GATT_CLIENT_SERVICE_DONE;
895d9ced76fSMatthias Ringwald break;
896d9ced76fSMatthias Ringwald default:
897d9ced76fSMatthias Ringwald break;
898d9ced76fSMatthias Ringwald }
899d9ced76fSMatthias Ringwald btstack_assert(status == ERROR_CODE_SUCCESS);
900d9ced76fSMatthias Ringwald UNUSED(status);
901d9ced76fSMatthias Ringwald if (query_sent){
902d9ced76fSMatthias Ringwald continue;
903d9ced76fSMatthias Ringwald }
904842492f0SMatthias Ringwald #endif
905d9ced76fSMatthias Ringwald
90626166ecfSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
907d9ced76fSMatthias Ringwald query_sent = gatt_client_le_enhanced_handle_can_send_query(gatt_client);
90826166ecfSMatthias Ringwald if (query_sent){
90926166ecfSMatthias Ringwald continue;
91026166ecfSMatthias Ringwald }
91126166ecfSMatthias Ringwald #endif
91253e9c18fSMatthias Ringwald btstack_context_callback_registration_t * callback = (btstack_context_callback_registration_t *) btstack_linked_list_pop(&gatt_client->query_requests);
91309834412SMatthias Ringwald if (callback == NULL) {
91409834412SMatthias Ringwald return;
91553e9c18fSMatthias Ringwald }
91609834412SMatthias Ringwald (*callback->callback)(callback->context);
91753e9c18fSMatthias Ringwald }
91853e9c18fSMatthias Ringwald }
91953e9c18fSMatthias Ringwald
92023d583b8SMatthias Ringwald // test if notification/indication should be delivered to application (BLESA)
gatt_client_accept_server_message(gatt_client_t * gatt_client)92123d583b8SMatthias Ringwald static bool gatt_client_accept_server_message(gatt_client_t *gatt_client) {
92223d583b8SMatthias Ringwald // ignore messages until re-encryption is complete
92323d583b8SMatthias Ringwald if (gap_reconnect_security_setup_active(gatt_client->con_handle)) return false;
92423d583b8SMatthias Ringwald
92523d583b8SMatthias Ringwald // after that ignore if bonded but not encrypted
92623d583b8SMatthias Ringwald return !gap_bonded(gatt_client->con_handle) || (gap_encryption_key_size(gatt_client->con_handle) > 0);
92723d583b8SMatthias Ringwald }
92823d583b8SMatthias Ringwald
emit_event_new(btstack_packet_handler_t callback,uint8_t * packet,uint16_t size)9299c662c9bSMatthias Ringwald static void emit_event_new(btstack_packet_handler_t callback, uint8_t * packet, uint16_t size){
9309c662c9bSMatthias Ringwald if (!callback) return;
9319da9850bSMatthias Ringwald hci_dump_btstack_event(packet, size);
9329c662c9bSMatthias Ringwald (*callback)(HCI_EVENT_PACKET, 0, packet, size);
9333deb3ec6SMatthias Ringwald }
9343deb3ec6SMatthias Ringwald
emit_gatt_complete_event(gatt_client_t * gatt_client,uint8_t att_status)9355cf1669fSMatthias Ringwald static void emit_gatt_complete_event(gatt_client_t * gatt_client, uint8_t att_status){
93688371317SMatthias Ringwald // @format H122
93788371317SMatthias Ringwald uint8_t packet[9];
9386a88036eSMatthias Ringwald hci_event_builder_context_t context;
9396a88036eSMatthias Ringwald hci_event_builder_init(&context, packet, sizeof(packet), GATT_EVENT_QUERY_COMPLETE, 0);
9406a88036eSMatthias Ringwald hci_event_builder_add_con_handle(&context, gatt_client->con_handle);
94188371317SMatthias Ringwald hci_event_builder_add_16(&context, gatt_client->service_id);
94288371317SMatthias Ringwald hci_event_builder_add_16(&context, gatt_client->connection_id);
9436a88036eSMatthias Ringwald hci_event_builder_add_08(&context, att_status);
9446a88036eSMatthias Ringwald emit_event_new(gatt_client->callback, packet, hci_event_builder_get_length(&context));
9453deb3ec6SMatthias Ringwald }
9463deb3ec6SMatthias Ringwald
emit_gatt_service_query_result_event(gatt_client_t * gatt_client,uint16_t start_group_handle,uint16_t end_group_handle,const uint8_t * uuid128)947045d700dSDavid Lechner static void emit_gatt_service_query_result_event(gatt_client_t * gatt_client, uint16_t start_group_handle, uint16_t end_group_handle, const uint8_t * uuid128){
948521f5820SMatthias Ringwald // @format H22X
949521f5820SMatthias Ringwald uint8_t packet[28];
950c668d4cfSMatthias Ringwald hci_event_builder_context_t context;
951c668d4cfSMatthias Ringwald hci_event_builder_init(&context, packet, sizeof(packet), GATT_EVENT_SERVICE_QUERY_RESULT, 0);
952c668d4cfSMatthias Ringwald hci_event_builder_add_con_handle(&context, gatt_client->con_handle);
953521f5820SMatthias Ringwald hci_event_builder_add_16(&context, gatt_client->service_id);
954521f5820SMatthias Ringwald hci_event_builder_add_16(&context, gatt_client->connection_id);
955c668d4cfSMatthias Ringwald hci_event_builder_add_16(&context, start_group_handle);
956c668d4cfSMatthias Ringwald hci_event_builder_add_16(&context, end_group_handle);
957c668d4cfSMatthias Ringwald hci_event_builder_add_128(&context, uuid128);
958c668d4cfSMatthias Ringwald emit_event_new(gatt_client->callback, packet, hci_event_builder_get_length(&context));
9593deb3ec6SMatthias Ringwald }
9603deb3ec6SMatthias Ringwald
emit_gatt_included_service_query_result_event(gatt_client_t * gatt_client,uint16_t include_handle,uint16_t start_group_handle,uint16_t end_group_handle,const uint8_t * uuid128)961045d700dSDavid Lechner static void emit_gatt_included_service_query_result_event(gatt_client_t * gatt_client, uint16_t include_handle, uint16_t start_group_handle, uint16_t end_group_handle, const uint8_t * uuid128){
962d578995fSMatthias Ringwald // @format H22X
963d578995fSMatthias Ringwald uint8_t packet[30];
964d8ef101dSMatthias Ringwald hci_event_builder_context_t context;
965d8ef101dSMatthias Ringwald hci_event_builder_init(&context, packet, sizeof(packet), GATT_EVENT_INCLUDED_SERVICE_QUERY_RESULT, 0);
966d8ef101dSMatthias Ringwald hci_event_builder_add_con_handle(&context, gatt_client->con_handle);
967d578995fSMatthias Ringwald hci_event_builder_add_16(&context, gatt_client->service_id);
968d578995fSMatthias Ringwald hci_event_builder_add_16(&context, gatt_client->connection_id);
969d8ef101dSMatthias Ringwald hci_event_builder_add_16(&context, include_handle);
970d8ef101dSMatthias Ringwald hci_event_builder_add_16(&context, start_group_handle);
971d8ef101dSMatthias Ringwald hci_event_builder_add_16(&context, end_group_handle);
972d8ef101dSMatthias Ringwald hci_event_builder_add_128(&context, uuid128);
973d8ef101dSMatthias Ringwald emit_event_new(gatt_client->callback, packet, hci_event_builder_get_length(&context));
9743deb3ec6SMatthias Ringwald }
9753deb3ec6SMatthias Ringwald
emit_gatt_characteristic_query_result_event(gatt_client_t * gatt_client,uint16_t start_handle,uint16_t value_handle,uint16_t end_handle,uint16_t properties,const uint8_t * uuid128)9765cf1669fSMatthias Ringwald static void emit_gatt_characteristic_query_result_event(gatt_client_t * gatt_client, uint16_t start_handle, uint16_t value_handle, uint16_t end_handle,
977045d700dSDavid Lechner uint16_t properties, const uint8_t * uuid128){
978185497a5SMatthias Ringwald // @format H22Y
979185497a5SMatthias Ringwald uint8_t packet[32];
980b5a7d6a2SMatthias Ringwald hci_event_builder_context_t context;
981b5a7d6a2SMatthias Ringwald hci_event_builder_init(&context, packet, sizeof(packet), GATT_EVENT_CHARACTERISTIC_QUERY_RESULT, 0);
982b5a7d6a2SMatthias Ringwald hci_event_builder_add_con_handle(&context, gatt_client->con_handle);
983185497a5SMatthias Ringwald hci_event_builder_add_16(&context, gatt_client->service_id);
984185497a5SMatthias Ringwald hci_event_builder_add_16(&context, gatt_client->connection_id);
985b5a7d6a2SMatthias Ringwald hci_event_builder_add_16(&context, start_handle);
986b5a7d6a2SMatthias Ringwald hci_event_builder_add_16(&context, value_handle);
987b5a7d6a2SMatthias Ringwald hci_event_builder_add_16(&context, end_handle);
988b5a7d6a2SMatthias Ringwald hci_event_builder_add_16(&context, properties);
989b5a7d6a2SMatthias Ringwald hci_event_builder_add_128(&context, uuid128);
990b5a7d6a2SMatthias Ringwald emit_event_new(gatt_client->callback, packet, hci_event_builder_get_length(&context));
9913deb3ec6SMatthias Ringwald }
9923deb3ec6SMatthias Ringwald
emit_gatt_all_characteristic_descriptors_result_event(gatt_client_t * gatt_client,uint16_t descriptor_handle,const uint8_t * uuid128)9933deb3ec6SMatthias Ringwald static void emit_gatt_all_characteristic_descriptors_result_event(
994045d700dSDavid Lechner gatt_client_t * gatt_client, uint16_t descriptor_handle, const uint8_t * uuid128){
9955a6c9e2aSMatthias Ringwald // @format H22Z
9965a6c9e2aSMatthias Ringwald uint8_t packet[26];
9971cfd1613SMatthias Ringwald hci_event_builder_context_t context;
9981cfd1613SMatthias Ringwald hci_event_builder_init(&context, packet, sizeof(packet), GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT, 0);
9991cfd1613SMatthias Ringwald hci_event_builder_add_con_handle(&context, gatt_client->con_handle);
10005a6c9e2aSMatthias Ringwald hci_event_builder_add_16(&context, gatt_client->service_id);
10015a6c9e2aSMatthias Ringwald hci_event_builder_add_16(&context, gatt_client->connection_id);
10021cfd1613SMatthias Ringwald hci_event_builder_add_16(&context, descriptor_handle);
10031cfd1613SMatthias Ringwald hci_event_builder_add_128(&context, uuid128);
10041cfd1613SMatthias Ringwald emit_event_new(gatt_client->callback, packet, hci_event_builder_get_length(&context));
10053deb3ec6SMatthias Ringwald }
10063deb3ec6SMatthias Ringwald
emit_gatt_mtu_exchanged_result_event(gatt_client_t * gatt_client,uint16_t new_mtu)10075cf1669fSMatthias Ringwald static void emit_gatt_mtu_exchanged_result_event(gatt_client_t * gatt_client, uint16_t new_mtu){
10088f37572aSJakob Krantz // @format H2
10098f37572aSJakob Krantz uint8_t packet[6];
10108f37572aSJakob Krantz packet[0] = GATT_EVENT_MTU;
10114ea43905SMatthias Ringwald packet[1] = sizeof(packet) - 2u;
10125cf1669fSMatthias Ringwald little_endian_store_16(packet, 2, gatt_client->con_handle);
10138f37572aSJakob Krantz little_endian_store_16(packet, 4, new_mtu);
10145cf1669fSMatthias Ringwald att_dispatch_client_mtu_exchanged(gatt_client->con_handle, new_mtu);
10155cf1669fSMatthias Ringwald emit_event_new(gatt_client->callback, packet, sizeof(packet));
10168f37572aSJakob Krantz }
101723d583b8SMatthias Ringwald
101823d583b8SMatthias Ringwald // helper
gatt_client_handle_transaction_complete(gatt_client_t * gatt_client,uint8_t att_status)101923d583b8SMatthias Ringwald static void gatt_client_handle_transaction_complete(gatt_client_t *gatt_client, uint8_t att_status) {
1020052dc82aSMatthias Ringwald gatt_client->state = P_READY;
102123d583b8SMatthias Ringwald gatt_client_timeout_stop(gatt_client);
102223d583b8SMatthias Ringwald emit_gatt_complete_event(gatt_client, att_status);
102323d583b8SMatthias Ringwald gatt_client_notify_can_send_query(gatt_client);
102423d583b8SMatthias Ringwald }
102523d583b8SMatthias Ringwald
102623d583b8SMatthias Ringwald // @return packet pointer
1027f6a28e25SMatthias Ringwald // @note assume that value is part of an l2cap buffer - overwrite HCI + L2CAP packet headers + 4 pre_buffer bytes
1028f6a28e25SMatthias Ringwald #define CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE 12
10298450fbf5SMatthias Ringwald static uint8_t *
setup_characteristic_value_packet(const gatt_client_t * gatt_client,uint8_t type,uint16_t attribute_handle,uint8_t * value,uint16_t length,uint16_t service_id,uint16_t connection_id)10308450fbf5SMatthias Ringwald setup_characteristic_value_packet(const gatt_client_t *gatt_client, uint8_t type, uint16_t attribute_handle,
103101e6dd7fSMatthias Ringwald uint8_t *value, uint16_t length, uint16_t service_id, uint16_t connection_id) {
103223d583b8SMatthias Ringwald #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
103323d583b8SMatthias Ringwald // copy value into test packet for testing
103423d583b8SMatthias Ringwald static uint8_t packet[1000];
1035f51e6883SMatthias Ringwald memcpy(&packet[CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE], value, length);
103623d583b8SMatthias Ringwald #else
103723d583b8SMatthias Ringwald // before the value inside the ATT PDU
103823d583b8SMatthias Ringwald uint8_t * packet = value - CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE;
103923d583b8SMatthias Ringwald #endif
104023d583b8SMatthias Ringwald packet[0] = type;
104123d583b8SMatthias Ringwald packet[1] = CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE - 2 + length;
10428450fbf5SMatthias Ringwald little_endian_store_16(packet, 2, gatt_client->con_handle);
104301e6dd7fSMatthias Ringwald little_endian_store_16(packet, 4, service_id);
104401e6dd7fSMatthias Ringwald little_endian_store_16(packet, 6, connection_id);
1045f6a28e25SMatthias Ringwald little_endian_store_16(packet, 8, attribute_handle);
1046f6a28e25SMatthias Ringwald little_endian_store_16(packet, 10, length);
104723d583b8SMatthias Ringwald return packet;
104823d583b8SMatthias Ringwald }
104923d583b8SMatthias Ringwald
105023d583b8SMatthias Ringwald // @return packet pointer
1051b641e2afSMatthias Ringwald // @note assume that value is part of an l2cap buffer - overwrite HCI + L2CAP packet headers + 6 pre_buffer bytes
1052b641e2afSMatthias Ringwald #define LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE 14
1053f51e6883SMatthias Ringwald
1054f51e6883SMatthias Ringwald // L2CAP Header (4) + ACL Header (4) => 8 bytes
1055f51e6883SMatthias Ringwald #if !defined(HCI_INCOMING_PRE_BUFFER_SIZE) || ((HCI_INCOMING_PRE_BUFFER_SIZE < LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE - 8))
1056149e2bf9STobias Müller #error "Long Characteristic reads requires HCI_INCOMING_PRE_BUFFER_SIZE >= 6"
1057f51e6883SMatthias Ringwald #endif
1058f51e6883SMatthias Ringwald
10593e78e462SMatthias Ringwald static uint8_t *
setup_long_characteristic_value_packet(const gatt_client_t * gatt_client,uint8_t type,uint16_t attribute_handle,uint16_t offset,uint8_t * value,uint16_t length)10603e78e462SMatthias Ringwald setup_long_characteristic_value_packet(const gatt_client_t *gatt_client, uint8_t type, uint16_t attribute_handle,
10613e78e462SMatthias Ringwald uint16_t offset, uint8_t *value, uint16_t length) {
106223d583b8SMatthias Ringwald #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
106323d583b8SMatthias Ringwald // avoid using pre ATT headers.
1064464b6e7bSMatthias Ringwald // copy value into test packet for testing
1065464b6e7bSMatthias Ringwald static uint8_t packet[1000];
1066464b6e7bSMatthias Ringwald memcpy(&packet[LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE], value, length);
1067464b6e7bSMatthias Ringwald #else
106823d583b8SMatthias Ringwald // before the value inside the ATT PDU
106923d583b8SMatthias Ringwald uint8_t * packet = value - LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE;
1070464b6e7bSMatthias Ringwald #endif
107123d583b8SMatthias Ringwald packet[0] = type;
107223d583b8SMatthias Ringwald packet[1] = LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE - 2 + length;
10733e78e462SMatthias Ringwald little_endian_store_16(packet, 2, gatt_client->con_handle);
1074b641e2afSMatthias Ringwald little_endian_store_16(packet, 4, gatt_client->service_id);
1075b641e2afSMatthias Ringwald little_endian_store_16(packet, 6, gatt_client->connection_id);
1076b641e2afSMatthias Ringwald little_endian_store_16(packet, 8, attribute_handle);
1077b641e2afSMatthias Ringwald little_endian_store_16(packet, 10, offset);
1078b641e2afSMatthias Ringwald little_endian_store_16(packet, 12, length);
107923d583b8SMatthias Ringwald return packet;
108023d583b8SMatthias Ringwald }
108123d583b8SMatthias Ringwald
1082f7a42e72SMatthias Ringwald #if (LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE > CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE)
1083f7a42e72SMatthias Ringwald #define REPORT_PREBUFFER_HEADER LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE
1084f7a42e72SMatthias Ringwald #else
1085f7a42e72SMatthias Ringwald #define REPORT_PREBUFFER_HEADER CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE
1086f7a42e72SMatthias Ringwald #endif
1087f7a42e72SMatthias Ringwald
10888f37572aSJakob Krantz ///
report_gatt_services(gatt_client_t * gatt_client,uint8_t * packet,uint16_t size)10895cf1669fSMatthias Ringwald static void report_gatt_services(gatt_client_t * gatt_client, uint8_t * packet, uint16_t size){
109039ac9711SMatthias Ringwald if (size < 2) return;
10913deb3ec6SMatthias Ringwald uint8_t attr_length = packet[1];
10924ea43905SMatthias Ringwald uint8_t uuid_length = attr_length - 4u;
10933deb3ec6SMatthias Ringwald
10943deb3ec6SMatthias Ringwald int i;
109539ac9711SMatthias Ringwald for (i = 2; (i+attr_length) <= size; i += attr_length){
1096f8fbdce0SMatthias Ringwald uint16_t start_group_handle = little_endian_read_16(packet,i);
1097f8fbdce0SMatthias Ringwald uint16_t end_group_handle = little_endian_read_16(packet,i+2);
10983deb3ec6SMatthias Ringwald uint8_t uuid128[16];
10993deb3ec6SMatthias Ringwald uint16_t uuid16 = 0;
11003deb3ec6SMatthias Ringwald
11014ea43905SMatthias Ringwald if (uuid_length == 2u){
1102f8fbdce0SMatthias Ringwald uuid16 = little_endian_read_16(packet, i+4);
1103e1a125dfSMatthias Ringwald uuid_add_bluetooth_prefix((uint8_t*) &uuid128, uuid16);
110439ac9711SMatthias Ringwald } else if (uuid_length == 16u) {
11059c80e4ccSMatthias Ringwald reverse_128(&packet[i+4], uuid128);
110639ac9711SMatthias Ringwald } else {
110739ac9711SMatthias Ringwald return;
11083deb3ec6SMatthias Ringwald }
11095cf1669fSMatthias Ringwald emit_gatt_service_query_result_event(gatt_client, start_group_handle, end_group_handle, uuid128);
11103deb3ec6SMatthias Ringwald }
11113deb3ec6SMatthias Ringwald }
11123deb3ec6SMatthias Ringwald
report_gatt_characteristic_start_found(gatt_client_t * gatt_client,uint16_t start_handle,uint8_t properties,uint16_t value_handle,uint8_t * uuid,uint16_t uuid_length)111323d583b8SMatthias Ringwald static void report_gatt_characteristic_start_found(gatt_client_t * gatt_client, uint16_t start_handle, uint8_t properties, uint16_t value_handle, uint8_t * uuid, uint16_t uuid_length){
11143deb3ec6SMatthias Ringwald uint8_t uuid128[16];
11153deb3ec6SMatthias Ringwald uint16_t uuid16 = 0;
11164ea43905SMatthias Ringwald if (uuid_length == 2u){
1117f8fbdce0SMatthias Ringwald uuid16 = little_endian_read_16(uuid, 0);
1118e1a125dfSMatthias Ringwald uuid_add_bluetooth_prefix((uint8_t*) uuid128, uuid16);
11194ea43905SMatthias Ringwald } else if (uuid_length == 16u){
11209c80e4ccSMatthias Ringwald reverse_128(uuid, uuid128);
1121d73c9dbeSMilanka Ringwald } else {
1122d73c9dbeSMilanka Ringwald return;
11233deb3ec6SMatthias Ringwald }
11243deb3ec6SMatthias Ringwald
11255cf1669fSMatthias Ringwald if (gatt_client->filter_with_uuid && (memcmp(gatt_client->uuid128, uuid128, 16) != 0)) return;
11263deb3ec6SMatthias Ringwald
11275cf1669fSMatthias Ringwald gatt_client->characteristic_properties = properties;
11285cf1669fSMatthias Ringwald gatt_client->characteristic_start_handle = start_handle;
11295cf1669fSMatthias Ringwald gatt_client->attribute_handle = value_handle;
11303deb3ec6SMatthias Ringwald
11315cf1669fSMatthias Ringwald if (gatt_client->filter_with_uuid) return;
11323deb3ec6SMatthias Ringwald
11335cf1669fSMatthias Ringwald gatt_client->uuid16 = uuid16;
11345cf1669fSMatthias Ringwald (void)memcpy(gatt_client->uuid128, uuid128, 16);
11353deb3ec6SMatthias Ringwald }
11363deb3ec6SMatthias Ringwald
report_gatt_characteristic_end_found(gatt_client_t * gatt_client,uint16_t end_handle)113723d583b8SMatthias Ringwald static void report_gatt_characteristic_end_found(gatt_client_t * gatt_client, uint16_t end_handle){
11383deb3ec6SMatthias Ringwald // TODO: stop searching if filter and uuid found
11393deb3ec6SMatthias Ringwald
11405cf1669fSMatthias Ringwald if (!gatt_client->characteristic_start_handle) return;
11413deb3ec6SMatthias Ringwald
11425cf1669fSMatthias Ringwald emit_gatt_characteristic_query_result_event(gatt_client, gatt_client->characteristic_start_handle, gatt_client->attribute_handle,
11435cf1669fSMatthias Ringwald end_handle, gatt_client->characteristic_properties, gatt_client->uuid128);
11443deb3ec6SMatthias Ringwald
11455cf1669fSMatthias Ringwald gatt_client->characteristic_start_handle = 0;
11463deb3ec6SMatthias Ringwald }
11473deb3ec6SMatthias Ringwald
114823d583b8SMatthias Ringwald
report_gatt_characteristics(gatt_client_t * gatt_client,uint8_t * packet,uint16_t size)11495cf1669fSMatthias Ringwald static void report_gatt_characteristics(gatt_client_t * gatt_client, uint8_t * packet, uint16_t size){
11504ea43905SMatthias Ringwald if (size < 2u) return;
11513deb3ec6SMatthias Ringwald uint8_t attr_length = packet[1];
11524ea43905SMatthias Ringwald if ((attr_length != 7u) && (attr_length != 21u)) return;
11534ea43905SMatthias Ringwald uint8_t uuid_length = attr_length - 5u;
11543deb3ec6SMatthias Ringwald int i;
11554ea43905SMatthias Ringwald for (i = 2u; (i + attr_length) <= size; i += attr_length){
1156f8fbdce0SMatthias Ringwald uint16_t start_handle = little_endian_read_16(packet, i);
11573deb3ec6SMatthias Ringwald uint8_t properties = packet[i+2];
1158f8fbdce0SMatthias Ringwald uint16_t value_handle = little_endian_read_16(packet, i+3);
115923d583b8SMatthias Ringwald report_gatt_characteristic_end_found(gatt_client, start_handle - 1u);
116023d583b8SMatthias Ringwald report_gatt_characteristic_start_found(gatt_client, start_handle, properties, value_handle, &packet[i + 5],
116123d583b8SMatthias Ringwald uuid_length);
11623deb3ec6SMatthias Ringwald }
11633deb3ec6SMatthias Ringwald }
11643deb3ec6SMatthias Ringwald
report_gatt_included_service_uuid16(gatt_client_t * gatt_client,uint16_t include_handle,uint16_t uuid16)11655cf1669fSMatthias Ringwald static void report_gatt_included_service_uuid16(gatt_client_t * gatt_client, uint16_t include_handle, uint16_t uuid16){
11663deb3ec6SMatthias Ringwald uint8_t normalized_uuid128[16];
1167e1a125dfSMatthias Ringwald uuid_add_bluetooth_prefix(normalized_uuid128, uuid16);
11685cf1669fSMatthias Ringwald emit_gatt_included_service_query_result_event(gatt_client, include_handle, gatt_client->query_start_handle,
11695cf1669fSMatthias Ringwald gatt_client->query_end_handle, normalized_uuid128);
11703deb3ec6SMatthias Ringwald }
11713deb3ec6SMatthias Ringwald
report_gatt_included_service_uuid128(gatt_client_t * gatt_client,uint16_t include_handle,const uint8_t * uuid128)1172045d700dSDavid Lechner static void report_gatt_included_service_uuid128(gatt_client_t * gatt_client, uint16_t include_handle, const uint8_t * uuid128){
11735cf1669fSMatthias Ringwald emit_gatt_included_service_query_result_event(gatt_client, include_handle, gatt_client->query_start_handle,
11745cf1669fSMatthias Ringwald gatt_client->query_end_handle, uuid128);
11753deb3ec6SMatthias Ringwald }
11763deb3ec6SMatthias Ringwald
report_gatt_characteristic_value_change(gatt_client_t * gatt_client,uint8_t event_type,uint16_t value_handle,uint8_t * value,int length)1177d03dbc9cSMatthias Ringwald static void report_gatt_characteristic_value_change(gatt_client_t *gatt_client, uint8_t event_type, uint16_t value_handle, uint8_t *value, int length) {
11781a4874dcSMatthias Ringwald uint8_t * packet;
11792c937a66SMatthias Ringwald
11801a4874dcSMatthias Ringwald // Single Characteristic listener, setup packet with service + connection id = 0
11811a4874dcSMatthias Ringwald packet = setup_characteristic_value_packet(gatt_client, event_type, value_handle, value, length, 0, 0);
11822c937a66SMatthias Ringwald btstack_linked_list_iterator_t it;
11832c937a66SMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_client_value_listeners);
11842c937a66SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)) {
11852c937a66SMatthias Ringwald gatt_client_notification_t *notification = (gatt_client_notification_t *) btstack_linked_list_iterator_next(&it);
11862c937a66SMatthias Ringwald if ((notification->con_handle != GATT_CLIENT_ANY_CONNECTION) && (notification->con_handle != gatt_client->con_handle)) continue;
11872c937a66SMatthias Ringwald if ((notification->attribute_handle != GATT_CLIENT_ANY_VALUE_HANDLE) && (notification->attribute_handle != value_handle)) continue;
11881a4874dcSMatthias Ringwald
11891a4874dcSMatthias Ringwald (*notification->callback)(HCI_EVENT_PACKET, 0, packet, CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE + length);
11901a4874dcSMatthias Ringwald }
11911a4874dcSMatthias Ringwald
11921a4874dcSMatthias Ringwald // Service characteristics
11931a4874dcSMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_client_service_value_listeners);
11941a4874dcSMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
11951a4874dcSMatthias Ringwald const gatt_client_service_notification_t * notification = (gatt_client_service_notification_t*) btstack_linked_list_iterator_next(&it);
11961a4874dcSMatthias Ringwald if (notification->con_handle != gatt_client->con_handle) continue;
11971a4874dcSMatthias Ringwald if (notification->start_group_handle > value_handle) continue;
11981a4874dcSMatthias Ringwald if (notification->end_group_handle < value_handle) continue;
11991a4874dcSMatthias Ringwald // (re)setup value packet with service and connection id (to avoid patching event later)
12001a4874dcSMatthias Ringwald packet = setup_characteristic_value_packet(gatt_client, event_type, value_handle, value, length, notification->service_id, notification->connection_id);
12012c937a66SMatthias Ringwald (*notification->callback)(HCI_EVENT_PACKET, 0, packet, CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE + length);
12022c937a66SMatthias Ringwald }
1203d03dbc9cSMatthias Ringwald }
1204d03dbc9cSMatthias Ringwald
12053deb3ec6SMatthias Ringwald // @note assume that value is part of an l2cap buffer - overwrite parts of the HCI/L2CAP/ATT packet (4/4/3) bytes
report_gatt_notification(gatt_client_t * gatt_client,uint16_t value_handle,uint8_t * value,int length)12060fde3c5eSMatthias Ringwald static void report_gatt_notification(gatt_client_t *gatt_client, uint16_t value_handle, uint8_t *value, int length) {
12070fde3c5eSMatthias Ringwald if (!gatt_client_accept_server_message(gatt_client)) return;
1208d03dbc9cSMatthias Ringwald report_gatt_characteristic_value_change(gatt_client, GATT_EVENT_NOTIFICATION, value_handle, value, length);
12093deb3ec6SMatthias Ringwald }
12103deb3ec6SMatthias Ringwald
12113deb3ec6SMatthias Ringwald // @note assume that value is part of an l2cap buffer - overwrite parts of the HCI/L2CAP/ATT packet (4/4/3) bytes
report_gatt_indication(gatt_client_t * gatt_client,uint16_t value_handle,uint8_t * value,int length)12120fde3c5eSMatthias Ringwald static void report_gatt_indication(gatt_client_t *gatt_client, uint16_t value_handle, uint8_t *value, int length) {
12130fde3c5eSMatthias Ringwald if (!gatt_client_accept_server_message(gatt_client)) return;
1214842492f0SMatthias Ringwald #ifdef ENABLE_GATT_CLIENT_SERVICE_CHANGED
121558e8c9f5SMatthias Ringwald // Directly Handle GATT Service Changed and Database Hash indications
121658e8c9f5SMatthias Ringwald if (value_handle == gatt_client->gatt_service_database_hash_value_handle){
121758e8c9f5SMatthias Ringwald gatt_client_service_emit_database_hash(gatt_client, value, length);
121858e8c9f5SMatthias Ringwald }
121958e8c9f5SMatthias Ringwald if (value_handle == gatt_client->gatt_service_changed_value_handle){
122058e8c9f5SMatthias Ringwald gatt_client_service_emit_service_changed(gatt_client, value, length);
122158e8c9f5SMatthias Ringwald }
1222842492f0SMatthias Ringwald #endif
1223d03dbc9cSMatthias Ringwald report_gatt_characteristic_value_change(gatt_client, GATT_EVENT_INDICATION, value_handle, value, length);
12243deb3ec6SMatthias Ringwald }
12253deb3ec6SMatthias Ringwald
12263deb3ec6SMatthias Ringwald // @note assume that value is part of an l2cap buffer - overwrite parts of the HCI/L2CAP/ATT packet (4/4/3) bytes
report_gatt_characteristic_value(gatt_client_t * gatt_client,uint16_t attribute_handle,uint8_t * value,uint16_t length)12275cf1669fSMatthias Ringwald static void report_gatt_characteristic_value(gatt_client_t * gatt_client, uint16_t attribute_handle, uint8_t * value, uint16_t length){
12288450fbf5SMatthias Ringwald uint8_t * packet = setup_characteristic_value_packet(
122901e6dd7fSMatthias Ringwald gatt_client, GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT, attribute_handle, value, length, gatt_client->service_id, gatt_client->connection_id);
123023d583b8SMatthias Ringwald emit_event_new(gatt_client->callback, packet, CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE + length);
12313deb3ec6SMatthias Ringwald }
12323deb3ec6SMatthias Ringwald
12333deb3ec6SMatthias Ringwald // @note assume that value is part of an l2cap buffer - overwrite parts of the HCI/L2CAP/ATT packet (4/4/3) bytes
report_gatt_long_characteristic_value_blob(gatt_client_t * gatt_client,uint16_t attribute_handle,uint8_t * blob,uint16_t blob_length,int value_offset)12345cf1669fSMatthias Ringwald static void report_gatt_long_characteristic_value_blob(gatt_client_t * gatt_client, uint16_t attribute_handle, uint8_t * blob, uint16_t blob_length, int value_offset){
12353e78e462SMatthias Ringwald uint8_t * packet = setup_long_characteristic_value_packet(gatt_client,
12363e78e462SMatthias Ringwald GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT,
12373e78e462SMatthias Ringwald attribute_handle, value_offset,
12383e78e462SMatthias Ringwald blob, blob_length);
123923d583b8SMatthias Ringwald emit_event_new(gatt_client->callback, packet, blob_length + LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE);
12403deb3ec6SMatthias Ringwald }
12413deb3ec6SMatthias Ringwald
report_gatt_characteristic_descriptor(gatt_client_t * gatt_client,uint16_t descriptor_handle,uint8_t * value,uint16_t value_length,uint16_t value_offset)12425cf1669fSMatthias Ringwald static void report_gatt_characteristic_descriptor(gatt_client_t * gatt_client, uint16_t descriptor_handle, uint8_t *value, uint16_t value_length, uint16_t value_offset){
12439ec2630cSMatthias Ringwald UNUSED(value_offset);
12448450fbf5SMatthias Ringwald uint8_t * packet = setup_characteristic_value_packet(gatt_client, GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT,
12458450fbf5SMatthias Ringwald descriptor_handle, value,
124601e6dd7fSMatthias Ringwald value_length, gatt_client->service_id, gatt_client->connection_id);
12475cf1669fSMatthias Ringwald emit_event_new(gatt_client->callback, packet, value_length + 8u);
12483deb3ec6SMatthias Ringwald }
12493deb3ec6SMatthias Ringwald
report_gatt_long_characteristic_descriptor(gatt_client_t * gatt_client,uint16_t descriptor_handle,uint8_t * blob,uint16_t blob_length,uint16_t value_offset)12505cf1669fSMatthias Ringwald static void report_gatt_long_characteristic_descriptor(gatt_client_t * gatt_client, uint16_t descriptor_handle, uint8_t *blob, uint16_t blob_length, uint16_t value_offset){
12513e78e462SMatthias Ringwald uint8_t * packet = setup_long_characteristic_value_packet(gatt_client,
12523e78e462SMatthias Ringwald GATT_EVENT_LONG_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT,
12533e78e462SMatthias Ringwald descriptor_handle, value_offset,
12543e78e462SMatthias Ringwald blob, blob_length);
125523d583b8SMatthias Ringwald emit_event_new(gatt_client->callback, packet, blob_length + LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE);
12563deb3ec6SMatthias Ringwald }
12573deb3ec6SMatthias Ringwald
report_gatt_all_characteristic_descriptors(gatt_client_t * gatt_client,uint8_t * packet,uint16_t size,uint16_t pair_size)12585cf1669fSMatthias Ringwald static void report_gatt_all_characteristic_descriptors(gatt_client_t * gatt_client, uint8_t * packet, uint16_t size, uint16_t pair_size){
12593deb3ec6SMatthias Ringwald int i;
12604ea43905SMatthias Ringwald for (i = 0u; (i + pair_size) <= size; i += pair_size){
1261f8fbdce0SMatthias Ringwald uint16_t descriptor_handle = little_endian_read_16(packet,i);
12623deb3ec6SMatthias Ringwald uint8_t uuid128[16];
12633deb3ec6SMatthias Ringwald uint16_t uuid16 = 0;
12644ea43905SMatthias Ringwald if (pair_size == 4u){
1265f8fbdce0SMatthias Ringwald uuid16 = little_endian_read_16(packet,i+2);
1266e1a125dfSMatthias Ringwald uuid_add_bluetooth_prefix(uuid128, uuid16);
12673deb3ec6SMatthias Ringwald } else {
12689c80e4ccSMatthias Ringwald reverse_128(&packet[i+2], uuid128);
12693deb3ec6SMatthias Ringwald }
12705cf1669fSMatthias Ringwald emit_gatt_all_characteristic_descriptors_result_event(gatt_client, descriptor_handle, uuid128);
12713deb3ec6SMatthias Ringwald }
12723deb3ec6SMatthias Ringwald
12733deb3ec6SMatthias Ringwald }
12743deb3ec6SMatthias Ringwald
is_query_done(gatt_client_t * gatt_client,uint16_t last_result_handle)127552377058SMatthias Ringwald static bool is_query_done(gatt_client_t * gatt_client, uint16_t last_result_handle){
12765cf1669fSMatthias Ringwald return last_result_handle >= gatt_client->end_group_handle;
12773deb3ec6SMatthias Ringwald }
12783deb3ec6SMatthias Ringwald
trigger_next_query(gatt_client_t * gatt_client,uint16_t last_result_handle,gatt_client_state_t next_query_state)12795cf1669fSMatthias Ringwald static void trigger_next_query(gatt_client_t * gatt_client, uint16_t last_result_handle, gatt_client_state_t next_query_state){
12805cf1669fSMatthias Ringwald if (is_query_done(gatt_client, last_result_handle)){
1281526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
12823deb3ec6SMatthias Ringwald return;
12833deb3ec6SMatthias Ringwald }
12843deb3ec6SMatthias Ringwald // next
12855cf1669fSMatthias Ringwald gatt_client->start_group_handle = last_result_handle + 1u;
1286052dc82aSMatthias Ringwald gatt_client->state = next_query_state;
12873deb3ec6SMatthias Ringwald }
12883deb3ec6SMatthias Ringwald
trigger_next_included_service_query(gatt_client_t * gatt_client,uint16_t last_result_handle)12895cf1669fSMatthias Ringwald static void trigger_next_included_service_query(gatt_client_t * gatt_client, uint16_t last_result_handle){
12905cf1669fSMatthias Ringwald trigger_next_query(gatt_client, last_result_handle, P_W2_SEND_INCLUDED_SERVICE_QUERY);
12913deb3ec6SMatthias Ringwald }
12923deb3ec6SMatthias Ringwald
trigger_next_service_query(gatt_client_t * gatt_client,uint16_t last_result_handle)12935cf1669fSMatthias Ringwald static void trigger_next_service_query(gatt_client_t * gatt_client, uint16_t last_result_handle){
12945cf1669fSMatthias Ringwald trigger_next_query(gatt_client, last_result_handle, P_W2_SEND_SERVICE_QUERY);
12953deb3ec6SMatthias Ringwald }
12963deb3ec6SMatthias Ringwald
trigger_next_service_by_uuid_query(gatt_client_t * gatt_client,uint16_t last_result_handle)12975cf1669fSMatthias Ringwald static void trigger_next_service_by_uuid_query(gatt_client_t * gatt_client, uint16_t last_result_handle){
12985cf1669fSMatthias Ringwald trigger_next_query(gatt_client, last_result_handle, P_W2_SEND_SERVICE_WITH_UUID_QUERY);
12993deb3ec6SMatthias Ringwald }
13003deb3ec6SMatthias Ringwald
trigger_next_characteristic_query(gatt_client_t * gatt_client,uint16_t last_result_handle)13015cf1669fSMatthias Ringwald static void trigger_next_characteristic_query(gatt_client_t * gatt_client, uint16_t last_result_handle){
13025cf1669fSMatthias Ringwald if (is_query_done(gatt_client, last_result_handle)){
13033deb3ec6SMatthias Ringwald // report last characteristic
130423d583b8SMatthias Ringwald report_gatt_characteristic_end_found(gatt_client, gatt_client->end_group_handle);
13053deb3ec6SMatthias Ringwald }
13065cf1669fSMatthias Ringwald trigger_next_query(gatt_client, last_result_handle, P_W2_SEND_ALL_CHARACTERISTICS_OF_SERVICE_QUERY);
13073deb3ec6SMatthias Ringwald }
13083deb3ec6SMatthias Ringwald
trigger_next_characteristic_descriptor_query(gatt_client_t * gatt_client,uint16_t last_result_handle)13095cf1669fSMatthias Ringwald static void trigger_next_characteristic_descriptor_query(gatt_client_t * gatt_client, uint16_t last_result_handle){
13105cf1669fSMatthias Ringwald trigger_next_query(gatt_client, last_result_handle, P_W2_SEND_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY);
13113deb3ec6SMatthias Ringwald }
13123deb3ec6SMatthias Ringwald
trigger_next_read_by_type_query(gatt_client_t * gatt_client,uint16_t last_result_handle)13135cf1669fSMatthias Ringwald static void trigger_next_read_by_type_query(gatt_client_t * gatt_client, uint16_t last_result_handle){
13145cf1669fSMatthias Ringwald trigger_next_query(gatt_client, last_result_handle, P_W2_SEND_READ_BY_TYPE_REQUEST);
13153deb3ec6SMatthias Ringwald }
13163deb3ec6SMatthias Ringwald
trigger_next_prepare_write_query(gatt_client_t * gatt_client,gatt_client_state_t next_query_state,gatt_client_state_t done_state)13175cf1669fSMatthias Ringwald static void trigger_next_prepare_write_query(gatt_client_t * gatt_client, gatt_client_state_t next_query_state, gatt_client_state_t done_state){
13185cf1669fSMatthias Ringwald gatt_client->attribute_offset += write_blob_length(gatt_client);
13195cf1669fSMatthias Ringwald uint16_t next_blob_length = write_blob_length(gatt_client);
13203deb3ec6SMatthias Ringwald
13214ea43905SMatthias Ringwald if (next_blob_length == 0u){
1322052dc82aSMatthias Ringwald gatt_client->state = done_state;
13233deb3ec6SMatthias Ringwald return;
13243deb3ec6SMatthias Ringwald }
1325052dc82aSMatthias Ringwald gatt_client->state = next_query_state;
13263deb3ec6SMatthias Ringwald }
13273deb3ec6SMatthias Ringwald
trigger_next_blob_query(gatt_client_t * gatt_client,gatt_client_state_t next_query_state,uint16_t received_blob_length)13285cf1669fSMatthias Ringwald static void trigger_next_blob_query(gatt_client_t * gatt_client, gatt_client_state_t next_query_state, uint16_t received_blob_length){
13293deb3ec6SMatthias Ringwald
1330dda77937SMatthias Ringwald uint16_t max_blob_length = gatt_client->mtu - 1u;
13313deb3ec6SMatthias Ringwald if (received_blob_length < max_blob_length){
1332526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
13333deb3ec6SMatthias Ringwald return;
13343deb3ec6SMatthias Ringwald }
13353deb3ec6SMatthias Ringwald
13365cf1669fSMatthias Ringwald gatt_client->attribute_offset += received_blob_length;
1337052dc82aSMatthias Ringwald gatt_client->state = next_query_state;
13383deb3ec6SMatthias Ringwald }
13393deb3ec6SMatthias Ringwald
gatt_client_listen_for_characteristic_value_updates(gatt_client_notification_t * notification,btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_characteristic_t * characteristic)134023d583b8SMatthias Ringwald void gatt_client_listen_for_characteristic_value_updates(gatt_client_notification_t * notification, btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_t * characteristic){
134123d583b8SMatthias Ringwald notification->callback = callback;
134223d583b8SMatthias Ringwald notification->con_handle = con_handle;
134323d583b8SMatthias Ringwald if (characteristic == NULL){
134423d583b8SMatthias Ringwald notification->attribute_handle = GATT_CLIENT_ANY_VALUE_HANDLE;
134523d583b8SMatthias Ringwald } else {
134623d583b8SMatthias Ringwald notification->attribute_handle = characteristic->value_handle;
134723d583b8SMatthias Ringwald }
134823d583b8SMatthias Ringwald btstack_linked_list_add(&gatt_client_value_listeners, (btstack_linked_item_t*) notification);
134923d583b8SMatthias Ringwald }
135023d583b8SMatthias Ringwald
gatt_client_stop_listening_for_characteristic_value_updates(gatt_client_notification_t * notification)135123d583b8SMatthias Ringwald void gatt_client_stop_listening_for_characteristic_value_updates(gatt_client_notification_t * notification){
135223d583b8SMatthias Ringwald btstack_linked_list_remove(&gatt_client_value_listeners, (btstack_linked_item_t*) notification);
135323d583b8SMatthias Ringwald }
13543deb3ec6SMatthias Ringwald
gatt_client_listen_for_service_characteristic_value_updates(gatt_client_service_notification_t * notification,btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_service_t * service,uint16_t service_id,uint16_t connection_id)13551a4874dcSMatthias Ringwald void gatt_client_listen_for_service_characteristic_value_updates(gatt_client_service_notification_t * notification,
13561a4874dcSMatthias Ringwald btstack_packet_handler_t callback,
13571a4874dcSMatthias Ringwald hci_con_handle_t con_handle,
13581a4874dcSMatthias Ringwald gatt_client_service_t * service,
13591a4874dcSMatthias Ringwald uint16_t service_id,
13601a4874dcSMatthias Ringwald uint16_t connection_id){
13611a4874dcSMatthias Ringwald notification->callback = callback;
13621a4874dcSMatthias Ringwald notification->con_handle = con_handle;
13631a4874dcSMatthias Ringwald notification->start_group_handle = service->start_group_handle;
13641a4874dcSMatthias Ringwald notification->end_group_handle = service->end_group_handle;
1365f8b8dabaSMatthias Ringwald notification->service_id = service_id;
1366f8b8dabaSMatthias Ringwald notification->connection_id = connection_id;
1367c26f92b3SMatthias Ringwald btstack_linked_list_add(&gatt_client_service_value_listeners, (btstack_linked_item_t*) notification);
1368fd01917fSMatthias Ringwald }
13691a4874dcSMatthias Ringwald
13701a4874dcSMatthias Ringwald /**
13711a4874dcSMatthias Ringwald * @brief Stop listening to characteristic value updates for registered service with
13721a4874dcSMatthias Ringwald * the gatt_client_listen_for_characteristic_value_updates function.
13731a4874dcSMatthias Ringwald * @param notification struct used in gatt_client_listen_for_characteristic_value_updates
13741a4874dcSMatthias Ringwald */
gatt_client_stop_listening_for_service_characteristic_value_updates(gatt_client_service_notification_t * notification)13751a4874dcSMatthias Ringwald void gatt_client_stop_listening_for_service_characteristic_value_updates(gatt_client_service_notification_t * notification){
13761a4874dcSMatthias Ringwald btstack_linked_list_remove(&gatt_client_service_value_listeners, (btstack_linked_item_t*) notification);
13771a4874dcSMatthias Ringwald }
13781a4874dcSMatthias Ringwald
is_value_valid(gatt_client_t * gatt_client,uint8_t * packet,uint16_t size)137962f7b41dSMatthias Ringwald static bool is_value_valid(gatt_client_t *gatt_client, uint8_t *packet, uint16_t size){
1380f8fbdce0SMatthias Ringwald uint16_t attribute_handle = little_endian_read_16(packet, 1);
1381f8fbdce0SMatthias Ringwald uint16_t value_offset = little_endian_read_16(packet, 3);
13823deb3ec6SMatthias Ringwald
138362f7b41dSMatthias Ringwald if (gatt_client->attribute_handle != attribute_handle) return false;
138462f7b41dSMatthias Ringwald if (gatt_client->attribute_offset != value_offset) return false;
13855cf1669fSMatthias Ringwald return memcmp(&gatt_client->attribute_value[gatt_client->attribute_offset], &packet[5], size - 5u) == 0u;
13863deb3ec6SMatthias Ringwald }
13873deb3ec6SMatthias Ringwald
13880038504eSMatthias Ringwald #ifdef ENABLE_LE_SIGNED_WRITE
gatt_client_run_for_client_start_signed_write(gatt_client_t * gatt_client)138992a7335eSMatthias Ringwald static void gatt_client_run_for_client_start_signed_write(gatt_client_t *gatt_client) {
139092a7335eSMatthias Ringwald sm_key_t csrk;
139192a7335eSMatthias Ringwald le_device_db_local_csrk_get(gatt_client->le_device_index, csrk);
139292a7335eSMatthias Ringwald uint32_t sign_counter = le_device_db_local_counter_get(gatt_client->le_device_index);
1393052dc82aSMatthias Ringwald gatt_client->state = P_W4_CMAC_RESULT;
139492a7335eSMatthias Ringwald sm_cmac_signed_write_start(csrk, ATT_SIGNED_WRITE_COMMAND, gatt_client->attribute_handle, gatt_client->attribute_length, gatt_client->attribute_value, sign_counter, att_signed_write_handle_cmac_result);
139592a7335eSMatthias Ringwald }
13960038504eSMatthias Ringwald #endif
139792a7335eSMatthias Ringwald
139892a7335eSMatthias Ringwald // returns true if packet was sent
gatt_client_run_for_gatt_client(gatt_client_t * gatt_client)13991979f09cSMatthias Ringwald static bool gatt_client_run_for_gatt_client(gatt_client_t * gatt_client){
14003deb3ec6SMatthias Ringwald
1401e4d159baSMatthias Ringwald // wait until re-encryption is complete
14021979f09cSMatthias Ringwald if (gap_reconnect_security_setup_active(gatt_client->con_handle)) return false;
1403d1e1a57fSMatthias Ringwald
1404e4d159baSMatthias Ringwald // wait until re-encryption is complete
14051979f09cSMatthias Ringwald if (gatt_client->reencryption_active) return false;
14066c124bc2SMatthias Ringwald
140711279da7SMatthias Ringwald // wait until pairing complete (either reactive authentication or due to required security level)
14081979f09cSMatthias Ringwald if (gatt_client->wait_for_authentication_complete) return false;
140911279da7SMatthias Ringwald
1410052dc82aSMatthias Ringwald bool client_request_pending = gatt_client->state != P_READY;
1411fd14b205SMatthias Ringwald
14121aa9e3e8SMatthias Ringwald // verify security level for Mandatory Authentication over LE
141346012949SMatthias Ringwald bool check_security;
141446012949SMatthias Ringwald switch (gatt_client->bearer_type){
141546012949SMatthias Ringwald case ATT_BEARER_UNENHANCED_LE:
141646012949SMatthias Ringwald check_security = true;
141746012949SMatthias Ringwald break;
141846012949SMatthias Ringwald default:
14199ced96e1SMatthias Ringwald check_security = false;
142046012949SMatthias Ringwald break;
14219ced96e1SMatthias Ringwald }
14229ced96e1SMatthias Ringwald if (client_request_pending && (gatt_client_required_security_level > gatt_client->security_level) && check_security){
14231dfae9c7SMatthias Ringwald log_info("Trigger pairing, current security level %u, required %u\n", gatt_client->security_level, gatt_client_required_security_level);
14242197dbafSMatthias Ringwald gatt_client->wait_for_authentication_complete = true;
142511279da7SMatthias Ringwald // set att error code for pairing failure based on required level
142611279da7SMatthias Ringwald switch (gatt_client_required_security_level){
142711279da7SMatthias Ringwald case LEVEL_4:
142811279da7SMatthias Ringwald case LEVEL_3:
142911279da7SMatthias Ringwald gatt_client->pending_error_code = ATT_ERROR_INSUFFICIENT_AUTHENTICATION;
143011279da7SMatthias Ringwald break;
143111279da7SMatthias Ringwald default:
143211279da7SMatthias Ringwald gatt_client->pending_error_code = ATT_ERROR_INSUFFICIENT_ENCRYPTION;
143311279da7SMatthias Ringwald break;
143411279da7SMatthias Ringwald }
143511279da7SMatthias Ringwald sm_request_pairing(gatt_client->con_handle);
143611279da7SMatthias Ringwald // sm probably just sent a pdu
14371979f09cSMatthias Ringwald return true;
143811279da7SMatthias Ringwald }
1439f4b33574SMatthias Ringwald
14405cf1669fSMatthias Ringwald switch (gatt_client->mtu_state) {
1441544128c3SMatthias Ringwald case SEND_MTU_EXCHANGE:
14425cf1669fSMatthias Ringwald gatt_client->mtu_state = SENT_MTU_EXCHANGE;
14436e7b444cSMatthias Ringwald att_exchange_mtu_request(gatt_client);
14441979f09cSMatthias Ringwald return true;
14453deb3ec6SMatthias Ringwald case SENT_MTU_EXCHANGE:
14461979f09cSMatthias Ringwald return false;
14473deb3ec6SMatthias Ringwald default:
14483deb3ec6SMatthias Ringwald break;
14493deb3ec6SMatthias Ringwald }
14503deb3ec6SMatthias Ringwald
14515cf1669fSMatthias Ringwald if (gatt_client->send_confirmation){
145252377058SMatthias Ringwald gatt_client->send_confirmation = false;
14536e7b444cSMatthias Ringwald att_confirmation(gatt_client);
14541979f09cSMatthias Ringwald return true;
14553deb3ec6SMatthias Ringwald }
14563deb3ec6SMatthias Ringwald
14573deb3ec6SMatthias Ringwald // check MTU for writes
1458052dc82aSMatthias Ringwald switch (gatt_client->state){
14593deb3ec6SMatthias Ringwald case P_W2_SEND_WRITE_CHARACTERISTIC_VALUE:
14603deb3ec6SMatthias Ringwald case P_W2_SEND_WRITE_CHARACTERISTIC_DESCRIPTOR:
1461dda77937SMatthias Ringwald if (gatt_client->attribute_length <= (gatt_client->mtu - 3u)) break;
1462dda77937SMatthias Ringwald log_error("gatt_client_run: value len %u > MTU %u - 3\n", gatt_client->attribute_length,gatt_client->mtu);
1463526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH);
14641979f09cSMatthias Ringwald return false;
14653deb3ec6SMatthias Ringwald default:
14663deb3ec6SMatthias Ringwald break;
14673deb3ec6SMatthias Ringwald }
14683deb3ec6SMatthias Ringwald
14690038504eSMatthias Ringwald bool packet_sent = true;
14700038504eSMatthias Ringwald bool done = true;
1471052dc82aSMatthias Ringwald switch (gatt_client->state){
14723deb3ec6SMatthias Ringwald case P_W2_SEND_SERVICE_QUERY:
1473052dc82aSMatthias Ringwald gatt_client->state = P_W4_SERVICE_QUERY_RESULT;
14745cf1669fSMatthias Ringwald send_gatt_services_request(gatt_client);
14750038504eSMatthias Ringwald break;
14763deb3ec6SMatthias Ringwald
14773deb3ec6SMatthias Ringwald case P_W2_SEND_SERVICE_WITH_UUID_QUERY:
1478052dc82aSMatthias Ringwald gatt_client->state = P_W4_SERVICE_WITH_UUID_RESULT;
14795cf1669fSMatthias Ringwald send_gatt_services_by_uuid_request(gatt_client);
14800038504eSMatthias Ringwald break;
14813deb3ec6SMatthias Ringwald
14823deb3ec6SMatthias Ringwald case P_W2_SEND_ALL_CHARACTERISTICS_OF_SERVICE_QUERY:
1483052dc82aSMatthias Ringwald gatt_client->state = P_W4_ALL_CHARACTERISTICS_OF_SERVICE_QUERY_RESULT;
14845cf1669fSMatthias Ringwald send_gatt_characteristic_request(gatt_client);
14850038504eSMatthias Ringwald break;
14863deb3ec6SMatthias Ringwald
14873deb3ec6SMatthias Ringwald case P_W2_SEND_CHARACTERISTIC_WITH_UUID_QUERY:
1488052dc82aSMatthias Ringwald gatt_client->state = P_W4_CHARACTERISTIC_WITH_UUID_QUERY_RESULT;
14895cf1669fSMatthias Ringwald send_gatt_characteristic_request(gatt_client);
14900038504eSMatthias Ringwald break;
14913deb3ec6SMatthias Ringwald
14923deb3ec6SMatthias Ringwald case P_W2_SEND_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY:
1493052dc82aSMatthias Ringwald gatt_client->state = P_W4_CHARACTERISTIC_WITH_UUID_QUERY_RESULT;
14945cf1669fSMatthias Ringwald send_gatt_characteristic_descriptor_request(gatt_client);
14950038504eSMatthias Ringwald break;
14963deb3ec6SMatthias Ringwald
14973deb3ec6SMatthias Ringwald case P_W2_SEND_INCLUDED_SERVICE_QUERY:
1498052dc82aSMatthias Ringwald gatt_client->state = P_W4_INCLUDED_SERVICE_QUERY_RESULT;
14995cf1669fSMatthias Ringwald send_gatt_included_service_request(gatt_client);
15000038504eSMatthias Ringwald break;
15013deb3ec6SMatthias Ringwald
15023deb3ec6SMatthias Ringwald case P_W2_SEND_INCLUDED_SERVICE_WITH_UUID_QUERY:
1503052dc82aSMatthias Ringwald gatt_client->state = P_W4_INCLUDED_SERVICE_UUID_WITH_QUERY_RESULT;
15045cf1669fSMatthias Ringwald send_gatt_included_service_uuid_request(gatt_client);
15050038504eSMatthias Ringwald break;
15063deb3ec6SMatthias Ringwald
15073deb3ec6SMatthias Ringwald case P_W2_SEND_READ_CHARACTERISTIC_VALUE_QUERY:
1508052dc82aSMatthias Ringwald gatt_client->state = P_W4_READ_CHARACTERISTIC_VALUE_RESULT;
15095cf1669fSMatthias Ringwald send_gatt_read_characteristic_value_request(gatt_client);
15100038504eSMatthias Ringwald break;
15113deb3ec6SMatthias Ringwald
15123deb3ec6SMatthias Ringwald case P_W2_SEND_READ_BLOB_QUERY:
1513052dc82aSMatthias Ringwald gatt_client->state = P_W4_READ_BLOB_RESULT;
15145cf1669fSMatthias Ringwald send_gatt_read_blob_request(gatt_client);
15150038504eSMatthias Ringwald break;
15163deb3ec6SMatthias Ringwald
15173deb3ec6SMatthias Ringwald case P_W2_SEND_READ_BY_TYPE_REQUEST:
1518052dc82aSMatthias Ringwald gatt_client->state = P_W4_READ_BY_TYPE_RESPONSE;
15195cf1669fSMatthias Ringwald send_gatt_read_by_type_request(gatt_client);
15200038504eSMatthias Ringwald break;
15213deb3ec6SMatthias Ringwald
15223deb3ec6SMatthias Ringwald case P_W2_SEND_READ_MULTIPLE_REQUEST:
1523052dc82aSMatthias Ringwald gatt_client->state = P_W4_READ_MULTIPLE_RESPONSE;
15245cf1669fSMatthias Ringwald send_gatt_read_multiple_request(gatt_client);
15250038504eSMatthias Ringwald break;
15263deb3ec6SMatthias Ringwald
1527f125a8efSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
1528f125a8efSMatthias Ringwald case P_W2_SEND_READ_MULTIPLE_VARIABLE_REQUEST:
1529052dc82aSMatthias Ringwald gatt_client->state = P_W4_READ_MULTIPLE_VARIABLE_RESPONSE;
1530f125a8efSMatthias Ringwald send_gatt_read_multiple_variable_request(gatt_client);
1531f125a8efSMatthias Ringwald break;
1532f125a8efSMatthias Ringwald #endif
1533f125a8efSMatthias Ringwald
15343deb3ec6SMatthias Ringwald case P_W2_SEND_WRITE_CHARACTERISTIC_VALUE:
1535052dc82aSMatthias Ringwald gatt_client->state = P_W4_WRITE_CHARACTERISTIC_VALUE_RESULT;
15365cf1669fSMatthias Ringwald send_gatt_write_attribute_value_request(gatt_client);
15370038504eSMatthias Ringwald break;
15383deb3ec6SMatthias Ringwald
15393deb3ec6SMatthias Ringwald case P_W2_PREPARE_WRITE:
1540052dc82aSMatthias Ringwald gatt_client->state = P_W4_PREPARE_WRITE_RESULT;
15415cf1669fSMatthias Ringwald send_gatt_prepare_write_request(gatt_client);
15420038504eSMatthias Ringwald break;
15433deb3ec6SMatthias Ringwald
15443deb3ec6SMatthias Ringwald case P_W2_PREPARE_WRITE_SINGLE:
1545052dc82aSMatthias Ringwald gatt_client->state = P_W4_PREPARE_WRITE_SINGLE_RESULT;
15465cf1669fSMatthias Ringwald send_gatt_prepare_write_request(gatt_client);
15470038504eSMatthias Ringwald break;
15483deb3ec6SMatthias Ringwald
15493deb3ec6SMatthias Ringwald case P_W2_PREPARE_RELIABLE_WRITE:
1550052dc82aSMatthias Ringwald gatt_client->state = P_W4_PREPARE_RELIABLE_WRITE_RESULT;
15515cf1669fSMatthias Ringwald send_gatt_prepare_write_request(gatt_client);
15520038504eSMatthias Ringwald break;
15533deb3ec6SMatthias Ringwald
15543deb3ec6SMatthias Ringwald case P_W2_EXECUTE_PREPARED_WRITE:
1555052dc82aSMatthias Ringwald gatt_client->state = P_W4_EXECUTE_PREPARED_WRITE_RESULT;
15565cf1669fSMatthias Ringwald send_gatt_execute_write_request(gatt_client);
15570038504eSMatthias Ringwald break;
15583deb3ec6SMatthias Ringwald
15593deb3ec6SMatthias Ringwald case P_W2_CANCEL_PREPARED_WRITE:
1560052dc82aSMatthias Ringwald gatt_client->state = P_W4_CANCEL_PREPARED_WRITE_RESULT;
15615cf1669fSMatthias Ringwald send_gatt_cancel_prepared_write_request(gatt_client);
15620038504eSMatthias Ringwald break;
15633deb3ec6SMatthias Ringwald
15643deb3ec6SMatthias Ringwald case P_W2_CANCEL_PREPARED_WRITE_DATA_MISMATCH:
1565052dc82aSMatthias Ringwald gatt_client->state = P_W4_CANCEL_PREPARED_WRITE_DATA_MISMATCH_RESULT;
15665cf1669fSMatthias Ringwald send_gatt_cancel_prepared_write_request(gatt_client);
15670038504eSMatthias Ringwald break;
15683deb3ec6SMatthias Ringwald
1569*197b4dd3SMatthias Ringwald #ifdef ENABLE_GATT_FIND_INFORMATION_FOR_CCC_DISCOVERY
1570abdc9fb5SMatthias Ringwald case P_W2_SEND_FIND_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY:
1571abdc9fb5SMatthias Ringwald // use Find Information
1572a9a6bd4cSMatthias Ringwald gatt_client->state = P_W4_FIND_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY_RESULT;
15735cf1669fSMatthias Ringwald send_gatt_characteristic_descriptor_request(gatt_client);
1574*197b4dd3SMatthias Ringwald #else
1575*197b4dd3SMatthias Ringwald case P_W2_SEND_READ_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY:
1576*197b4dd3SMatthias Ringwald // Use Read By Type
1577*197b4dd3SMatthias Ringwald gatt_client->state = P_W4_READ_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY_RESULT;
1578*197b4dd3SMatthias Ringwald send_gatt_read_client_characteristic_configuration_request(gatt_client);
1579*197b4dd3SMatthias Ringwald #endif
15800038504eSMatthias Ringwald break;
15813deb3ec6SMatthias Ringwald
15823deb3ec6SMatthias Ringwald case P_W2_SEND_READ_CHARACTERISTIC_DESCRIPTOR_QUERY:
1583052dc82aSMatthias Ringwald gatt_client->state = P_W4_READ_CHARACTERISTIC_DESCRIPTOR_RESULT;
15845cf1669fSMatthias Ringwald send_gatt_read_characteristic_descriptor_request(gatt_client);
15850038504eSMatthias Ringwald break;
15863deb3ec6SMatthias Ringwald
15873deb3ec6SMatthias Ringwald case P_W2_SEND_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_QUERY:
1588052dc82aSMatthias Ringwald gatt_client->state = P_W4_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_RESULT;
15895cf1669fSMatthias Ringwald send_gatt_read_blob_request(gatt_client);
15900038504eSMatthias Ringwald break;
15913deb3ec6SMatthias Ringwald
15923deb3ec6SMatthias Ringwald case P_W2_SEND_WRITE_CHARACTERISTIC_DESCRIPTOR:
1593052dc82aSMatthias Ringwald gatt_client->state = P_W4_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT;
15945cf1669fSMatthias Ringwald send_gatt_write_attribute_value_request(gatt_client);
15950038504eSMatthias Ringwald break;
15963deb3ec6SMatthias Ringwald
15973deb3ec6SMatthias Ringwald case P_W2_WRITE_CLIENT_CHARACTERISTIC_CONFIGURATION:
1598052dc82aSMatthias Ringwald gatt_client->state = P_W4_CLIENT_CHARACTERISTIC_CONFIGURATION_RESULT;
15995cf1669fSMatthias Ringwald send_gatt_write_client_characteristic_configuration_request(gatt_client);
16000038504eSMatthias Ringwald break;
16013deb3ec6SMatthias Ringwald
16023deb3ec6SMatthias Ringwald case P_W2_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR:
1603052dc82aSMatthias Ringwald gatt_client->state = P_W4_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT;
16045cf1669fSMatthias Ringwald send_gatt_prepare_write_request(gatt_client);
16050038504eSMatthias Ringwald break;
16063deb3ec6SMatthias Ringwald
16073deb3ec6SMatthias Ringwald case P_W2_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR:
1608052dc82aSMatthias Ringwald gatt_client->state = P_W4_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT;
16095cf1669fSMatthias Ringwald send_gatt_execute_write_request(gatt_client);
16100038504eSMatthias Ringwald break;
16113deb3ec6SMatthias Ringwald
16127a766ebfSMatthias Ringwald #ifdef ENABLE_LE_SIGNED_WRITE
1613793cf6ceSMatthias Ringwald case P_W4_IDENTITY_RESOLVING:
16145cf1669fSMatthias Ringwald log_info("P_W4_IDENTITY_RESOLVING - state %x", sm_identity_resolving_state(gatt_client->con_handle));
16155cf1669fSMatthias Ringwald switch (sm_identity_resolving_state(gatt_client->con_handle)){
1616793cf6ceSMatthias Ringwald case IRK_LOOKUP_SUCCEEDED:
16175cf1669fSMatthias Ringwald gatt_client->le_device_index = sm_le_device_index(gatt_client->con_handle);
1618052dc82aSMatthias Ringwald gatt_client->state = P_W4_CMAC_READY;
161992a7335eSMatthias Ringwald if (sm_cmac_ready()){
162092a7335eSMatthias Ringwald gatt_client_run_for_client_start_signed_write(gatt_client);
162192a7335eSMatthias Ringwald }
1622793cf6ceSMatthias Ringwald break;
1623793cf6ceSMatthias Ringwald case IRK_LOOKUP_FAILED:
1624526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_BONDING_INFORMATION_MISSING);
162592a7335eSMatthias Ringwald break;
1626793cf6ceSMatthias Ringwald default:
162792a7335eSMatthias Ringwald break;
1628793cf6ceSMatthias Ringwald }
16290038504eSMatthias Ringwald packet_sent = false;
16300038504eSMatthias Ringwald break;
1631793cf6ceSMatthias Ringwald
16323deb3ec6SMatthias Ringwald case P_W4_CMAC_READY:
16333deb3ec6SMatthias Ringwald if (sm_cmac_ready()){
163492a7335eSMatthias Ringwald gatt_client_run_for_client_start_signed_write(gatt_client);
16353deb3ec6SMatthias Ringwald }
16360038504eSMatthias Ringwald packet_sent = false;
16370038504eSMatthias Ringwald break;
16383deb3ec6SMatthias Ringwald
16393deb3ec6SMatthias Ringwald case P_W2_SEND_SIGNED_WRITE: {
16404c7c987fSMatthias Ringwald gatt_client->state = P_W4_SEND_SIGNED_WRITE_DONE;
16413deb3ec6SMatthias Ringwald // bump local signing counter
16425cf1669fSMatthias Ringwald uint32_t sign_counter = le_device_db_local_counter_get(gatt_client->le_device_index);
16435cf1669fSMatthias Ringwald le_device_db_local_counter_set(gatt_client->le_device_index, sign_counter + 1);
164464b12680SMatthias Ringwald // send signed write command
16455cf1669fSMatthias Ringwald send_gatt_signed_write_request(gatt_client, sign_counter);
16463deb3ec6SMatthias Ringwald // finally, notifiy client that write is complete
1647526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
1648b68ac7a5SMatthias Ringwald break;
16493deb3ec6SMatthias Ringwald }
16507a766ebfSMatthias Ringwald #endif
16513deb3ec6SMatthias Ringwald default:
1652b68ac7a5SMatthias Ringwald done = false;
16533deb3ec6SMatthias Ringwald break;
16543deb3ec6SMatthias Ringwald }
165547181045SMatthias Ringwald
16560038504eSMatthias Ringwald if (done){
16570038504eSMatthias Ringwald return packet_sent;
16580038504eSMatthias Ringwald }
16590038504eSMatthias Ringwald
166059d34cd2SMatthias Ringwald // write without response callback
166159d34cd2SMatthias Ringwald btstack_context_callback_registration_t * callback =
166259d34cd2SMatthias Ringwald (btstack_context_callback_registration_t *) btstack_linked_list_pop(&gatt_client->write_without_response_requests);
166359d34cd2SMatthias Ringwald if (callback != NULL){
166459d34cd2SMatthias Ringwald (*callback->callback)(callback->context);
166559d34cd2SMatthias Ringwald return true;
166659d34cd2SMatthias Ringwald }
166759d34cd2SMatthias Ringwald
166859d34cd2SMatthias Ringwald // requested can send now old
1669f688bdb8SMatthias Ringwald if (gatt_client->write_without_response_callback != NULL){
16705cf1669fSMatthias Ringwald btstack_packet_handler_t packet_handler = gatt_client->write_without_response_callback;
16715cf1669fSMatthias Ringwald gatt_client->write_without_response_callback = NULL;
167247181045SMatthias Ringwald uint8_t event[4];
167347181045SMatthias Ringwald event[0] = GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE;
16744ea43905SMatthias Ringwald event[1] = sizeof(event) - 2u;
16755cf1669fSMatthias Ringwald little_endian_store_16(event, 2, gatt_client->con_handle);
16765cf1669fSMatthias Ringwald packet_handler(HCI_EVENT_PACKET, gatt_client->con_handle, event, sizeof(event));
16771979f09cSMatthias Ringwald return true; // to trigger requeueing (even if higher layer didn't sent)
167847181045SMatthias Ringwald }
167947181045SMatthias Ringwald
16801979f09cSMatthias Ringwald return false;
16813deb3ec6SMatthias Ringwald }
16823deb3ec6SMatthias Ringwald
gatt_client_run(void)1683544128c3SMatthias Ringwald static void gatt_client_run(void){
1684544128c3SMatthias Ringwald btstack_linked_item_t *it;
1685109c2548SMatthias Ringwald bool packet_sent;
16867627a0deSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
16877627a0deSMatthias Ringwald btstack_linked_list_iterator_t it_eatt;
16887627a0deSMatthias Ringwald #endif
1689a0da043fSMatthias Ringwald for (it = (btstack_linked_item_t *) gatt_client_connections; it != NULL; it = it->next){
16905cf1669fSMatthias Ringwald gatt_client_t * gatt_client = (gatt_client_t *) it;
169146012949SMatthias Ringwald switch (gatt_client->bearer_type){
169246012949SMatthias Ringwald case ATT_BEARER_UNENHANCED_LE:
16937627a0deSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
16947627a0deSMatthias Ringwald btstack_linked_list_iterator_init(&it_eatt, &gatt_client->eatt_clients);
16957627a0deSMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it_eatt)) {
16967627a0deSMatthias Ringwald gatt_client_t * eatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it_eatt);
1697052dc82aSMatthias Ringwald if (eatt_client->state != P_READY){
16984c226876SMatthias Ringwald if (att_dispatch_client_can_send_now(gatt_client->con_handle)){
16997627a0deSMatthias Ringwald gatt_client_run_for_gatt_client(eatt_client);
17007627a0deSMatthias Ringwald }
17017627a0deSMatthias Ringwald }
17027627a0deSMatthias Ringwald }
17037627a0deSMatthias Ringwald #endif
170446012949SMatthias Ringwald if (!att_dispatch_client_can_send_now(gatt_client->con_handle)) {
170546012949SMatthias Ringwald att_dispatch_client_request_can_send_now_event(gatt_client->con_handle);
170646012949SMatthias Ringwald return;
170746012949SMatthias Ringwald }
1708109c2548SMatthias Ringwald packet_sent = gatt_client_run_for_gatt_client(gatt_client);
170946012949SMatthias Ringwald if (packet_sent){
171046012949SMatthias Ringwald // request new permission
171146012949SMatthias Ringwald att_dispatch_client_request_can_send_now_event(gatt_client->con_handle);
171246012949SMatthias Ringwald // requeue client for fairness and exit
171346012949SMatthias Ringwald // note: iterator has become invalid
171446012949SMatthias Ringwald btstack_linked_list_remove(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
171546012949SMatthias Ringwald btstack_linked_list_add_tail(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
171646012949SMatthias Ringwald return;
171746012949SMatthias Ringwald }
171846012949SMatthias Ringwald break;
17191450cdc6SMatthias Ringwald #ifdef ENABLE_GATT_OVER_CLASSIC
172046012949SMatthias Ringwald case ATT_BEARER_UNENHANCED_CLASSIC:
17211450cdc6SMatthias Ringwald if (gatt_client->con_handle == HCI_CON_HANDLE_INVALID) {
17221450cdc6SMatthias Ringwald continue;
17231450cdc6SMatthias Ringwald }
17241aa9e3e8SMatthias Ringwald
17251aa9e3e8SMatthias Ringwald // handle GATT over BR/EDR
17264c226876SMatthias Ringwald if (att_dispatch_client_can_send_now(gatt_client->con_handle) == false) {
17274c226876SMatthias Ringwald att_dispatch_client_request_can_send_now_event(gatt_client->con_handle);
17281aa9e3e8SMatthias Ringwald return;
17291aa9e3e8SMatthias Ringwald }
1730109c2548SMatthias Ringwald packet_sent = gatt_client_run_for_gatt_client(gatt_client);
17311aa9e3e8SMatthias Ringwald if (packet_sent){
17321aa9e3e8SMatthias Ringwald // request new permission
17331aa9e3e8SMatthias Ringwald att_dispatch_client_request_can_send_now_event(gatt_client->con_handle);
17341aa9e3e8SMatthias Ringwald // requeue client for fairness and exit
17351aa9e3e8SMatthias Ringwald // note: iterator has become invalid
17361aa9e3e8SMatthias Ringwald btstack_linked_list_remove(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
17371aa9e3e8SMatthias Ringwald btstack_linked_list_add_tail(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
17381aa9e3e8SMatthias Ringwald return;
17391aa9e3e8SMatthias Ringwald }
174046012949SMatthias Ringwald break;
17411450cdc6SMatthias Ringwald #endif
174246012949SMatthias Ringwald default:
174346012949SMatthias Ringwald btstack_unreachable();
174446012949SMatthias Ringwald break;
1745544128c3SMatthias Ringwald }
174646012949SMatthias Ringwald
174746012949SMatthias Ringwald
1748544128c3SMatthias Ringwald }
17493deb3ec6SMatthias Ringwald }
17503deb3ec6SMatthias Ringwald
1751544a5845SMatthias Ringwald // emit complete event, used to avoid emitting event from API call
gatt_client_emit_events(void * context)1752544a5845SMatthias Ringwald static void gatt_client_emit_events(void * context){
1753544a5845SMatthias Ringwald UNUSED(context);
1754544a5845SMatthias Ringwald btstack_linked_item_t *it;
1755544a5845SMatthias Ringwald for (it = (btstack_linked_item_t *) gatt_client_connections; it != NULL; it = it->next) {
1756544a5845SMatthias Ringwald gatt_client_t *gatt_client = (gatt_client_t *) it;
1757544a5845SMatthias Ringwald if (gatt_client->state == P_W2_EMIT_QUERY_COMPLETE_EVENT){
1758544a5845SMatthias Ringwald gatt_client->state = P_READY;
1759544a5845SMatthias Ringwald emit_gatt_complete_event(gatt_client, ATT_ERROR_SUCCESS);
1760544a5845SMatthias Ringwald }
1761544a5845SMatthias Ringwald }
1762544a5845SMatthias Ringwald }
1763544a5845SMatthias Ringwald
gatt_client_report_error_if_pending(gatt_client_t * gatt_client,uint8_t att_error_code)17645cf1669fSMatthias Ringwald static void gatt_client_report_error_if_pending(gatt_client_t *gatt_client, uint8_t att_error_code) {
1765643a64fcSMatthias Ringwald if (is_ready(gatt_client)) return;
1766526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, att_error_code);
17673deb3ec6SMatthias Ringwald }
17683deb3ec6SMatthias Ringwald
gatt_client_handle_reencryption_complete(const uint8_t * packet)176978c4542aSMatthias Ringwald static void gatt_client_handle_reencryption_complete(const uint8_t * packet){
177078c4542aSMatthias Ringwald hci_con_handle_t con_handle = sm_event_reencryption_complete_get_handle(packet);
177178c4542aSMatthias Ringwald gatt_client_t * gatt_client = gatt_client_get_context_for_handle(con_handle);
177278c4542aSMatthias Ringwald if (gatt_client == NULL) return;
177378c4542aSMatthias Ringwald
177478c4542aSMatthias Ringwald // update security level
177578c4542aSMatthias Ringwald gatt_client->security_level = gatt_client_le_security_level_for_connection(con_handle);
177678c4542aSMatthias Ringwald
177778c4542aSMatthias Ringwald gatt_client->reencryption_result = sm_event_reencryption_complete_get_status(packet);
177878c4542aSMatthias Ringwald gatt_client->reencryption_active = false;
17792197dbafSMatthias Ringwald gatt_client->wait_for_authentication_complete = false;
178078c4542aSMatthias Ringwald
1781052dc82aSMatthias Ringwald if (gatt_client->state == P_READY) return;
178278c4542aSMatthias Ringwald
178378c4542aSMatthias Ringwald switch (sm_event_reencryption_complete_get_status(packet)){
178478c4542aSMatthias Ringwald case ERROR_CODE_SUCCESS:
178578c4542aSMatthias Ringwald log_info("re-encryption success, retry operation");
178678c4542aSMatthias Ringwald break;
178778c4542aSMatthias Ringwald case ERROR_CODE_AUTHENTICATION_FAILURE:
178878c4542aSMatthias Ringwald case ERROR_CODE_PIN_OR_KEY_MISSING:
178978c4542aSMatthias Ringwald #if defined(ENABLE_GATT_CLIENT_PAIRING) && !defined(ENABLE_LE_PROACTIVE_AUTHENTICATION)
179078c4542aSMatthias Ringwald if (gatt_client_required_security_level == LEVEL_0) {
179178c4542aSMatthias Ringwald // re-encryption failed for reactive authentication with pairing and we have a pending client request
179278c4542aSMatthias Ringwald // => try to resolve it by deleting bonding information if we started pairing before
179378c4542aSMatthias Ringwald // delete bonding information
179478c4542aSMatthias Ringwald int le_device_db_index = sm_le_device_index(gatt_client->con_handle);
179578c4542aSMatthias Ringwald btstack_assert(le_device_db_index >= 0);
179678c4542aSMatthias Ringwald log_info("reactive auth with pairing: delete bonding and start pairing");
179778c4542aSMatthias Ringwald #ifdef ENABLE_LE_PRIVACY_ADDRESS_RESOLUTION
179878c4542aSMatthias Ringwald hci_remove_le_device_db_entry_from_resolving_list((uint16_t) le_device_db_index);
179978c4542aSMatthias Ringwald #endif
180078c4542aSMatthias Ringwald le_device_db_remove(le_device_db_index);
180178c4542aSMatthias Ringwald // trigger pairing again
180278c4542aSMatthias Ringwald sm_request_pairing(gatt_client->con_handle);
180378c4542aSMatthias Ringwald break;
180478c4542aSMatthias Ringwald }
180578c4542aSMatthias Ringwald #endif
180678c4542aSMatthias Ringwald // report bonding information missing
1807526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_BONDING_INFORMATION_MISSING);
180878c4542aSMatthias Ringwald break;
180978c4542aSMatthias Ringwald default:
181078c4542aSMatthias Ringwald // report bonding information missing
1811526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, gatt_client->pending_error_code);
181278c4542aSMatthias Ringwald break;
181378c4542aSMatthias Ringwald }
181478c4542aSMatthias Ringwald }
181578c4542aSMatthias Ringwald
gatt_client_handle_disconnection_complete(const uint8_t * packet)181678c4542aSMatthias Ringwald static void gatt_client_handle_disconnection_complete(const uint8_t * packet){
181778c4542aSMatthias Ringwald log_info("GATT Client: HCI_EVENT_DISCONNECTION_COMPLETE");
181878c4542aSMatthias Ringwald hci_con_handle_t con_handle = little_endian_read_16(packet,3);
181978c4542aSMatthias Ringwald gatt_client_t * gatt_client = gatt_client_get_context_for_handle(con_handle);
182078c4542aSMatthias Ringwald if (gatt_client == NULL) return;
182178c4542aSMatthias Ringwald
182278c4542aSMatthias Ringwald gatt_client_report_error_if_pending(gatt_client, ATT_ERROR_HCI_DISCONNECT_RECEIVED);
182378c4542aSMatthias Ringwald gatt_client_timeout_stop(gatt_client);
182478c4542aSMatthias Ringwald btstack_linked_list_remove(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
182578c4542aSMatthias Ringwald btstack_memory_gatt_client_free(gatt_client);
182678c4542aSMatthias Ringwald }
182778c4542aSMatthias Ringwald
gatt_client_event_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)1828f4b33574SMatthias Ringwald static void gatt_client_event_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
18291ac26e06SMatthias Ringwald UNUSED(channel); // ok: handling own l2cap events
18301ac26e06SMatthias Ringwald UNUSED(size); // ok: there is no channel
18319ec2630cSMatthias Ringwald
1832361b1363SMatthias Ringwald if (packet_type != HCI_EVENT_PACKET) return;
1833361b1363SMatthias Ringwald
1834f4b33574SMatthias Ringwald hci_con_handle_t con_handle;
18355cf1669fSMatthias Ringwald gatt_client_t * gatt_client;
18360e2df43fSMatthias Ringwald switch (hci_event_packet_get_type(packet)) {
18373deb3ec6SMatthias Ringwald case HCI_EVENT_DISCONNECTION_COMPLETE:
183878c4542aSMatthias Ringwald gatt_client_handle_disconnection_complete(packet);
18393deb3ec6SMatthias Ringwald break;
1840f4b33574SMatthias Ringwald
1841f4b33574SMatthias Ringwald // Pairing complete (with/without bonding=storing of pairing information)
1842f4b33574SMatthias Ringwald case SM_EVENT_PAIRING_COMPLETE:
1843f4b33574SMatthias Ringwald con_handle = sm_event_pairing_complete_get_handle(packet);
18445cf1669fSMatthias Ringwald gatt_client = gatt_client_get_context_for_handle(con_handle);
18455cf1669fSMatthias Ringwald if (gatt_client == NULL) break;
1846f4b33574SMatthias Ringwald
18471dfae9c7SMatthias Ringwald // update security level
18481dfae9c7SMatthias Ringwald gatt_client->security_level = gatt_client_le_security_level_for_connection(con_handle);
18491dfae9c7SMatthias Ringwald
18503503dc74SMatthias Ringwald if (gatt_client->wait_for_authentication_complete){
18512197dbafSMatthias Ringwald gatt_client->wait_for_authentication_complete = false;
1852f688bdb8SMatthias Ringwald if (sm_event_pairing_complete_get_status(packet) != ERROR_CODE_SUCCESS){
18535cf1669fSMatthias Ringwald log_info("pairing failed, report previous error 0x%x", gatt_client->pending_error_code);
1854ec9babacSMatthias Ringwald gatt_client_report_error_if_pending(gatt_client, gatt_client->pending_error_code);
1855f4b33574SMatthias Ringwald } else {
1856f4b33574SMatthias Ringwald log_info("pairing success, retry operation");
18573deb3ec6SMatthias Ringwald }
1858f4b33574SMatthias Ringwald }
1859f4b33574SMatthias Ringwald break;
186011279da7SMatthias Ringwald
1861793cf6ceSMatthias Ringwald #ifdef ENABLE_LE_SIGNED_WRITE
1862793cf6ceSMatthias Ringwald // Identity Resolving completed (no code, gatt_client_run will continue)
1863793cf6ceSMatthias Ringwald case SM_EVENT_IDENTITY_RESOLVING_SUCCEEDED:
1864793cf6ceSMatthias Ringwald case SM_EVENT_IDENTITY_RESOLVING_FAILED:
1865793cf6ceSMatthias Ringwald break;
1866793cf6ceSMatthias Ringwald #endif
1867a95ca902SMatthias Ringwald
18688918bbdaSMatthias Ringwald // re-encryption started
18698918bbdaSMatthias Ringwald case SM_EVENT_REENCRYPTION_STARTED:
18708918bbdaSMatthias Ringwald con_handle = sm_event_reencryption_complete_get_handle(packet);
18718918bbdaSMatthias Ringwald gatt_client = gatt_client_get_context_for_handle(con_handle);
18728918bbdaSMatthias Ringwald if (gatt_client == NULL) break;
18738918bbdaSMatthias Ringwald
18748918bbdaSMatthias Ringwald gatt_client->reencryption_active = true;
18758918bbdaSMatthias Ringwald gatt_client->reencryption_result = ERROR_CODE_SUCCESS;
18768918bbdaSMatthias Ringwald break;
18778918bbdaSMatthias Ringwald
1878ec9babacSMatthias Ringwald // re-encryption complete
1879ec9babacSMatthias Ringwald case SM_EVENT_REENCRYPTION_COMPLETE:
188078c4542aSMatthias Ringwald gatt_client_handle_reencryption_complete(packet);
1881ec9babacSMatthias Ringwald break;
18823deb3ec6SMatthias Ringwald default:
18833deb3ec6SMatthias Ringwald break;
18843deb3ec6SMatthias Ringwald }
18853deb3ec6SMatthias Ringwald
1886361b1363SMatthias Ringwald gatt_client_run();
18873deb3ec6SMatthias Ringwald }
18883deb3ec6SMatthias Ringwald
gatt_client_handle_att_read_response(gatt_client_t * gatt_client,uint8_t * packet,uint16_t size)18892da0d963SMatthias Ringwald static void gatt_client_handle_att_read_response(gatt_client_t *gatt_client, uint8_t *packet, uint16_t size) {
1890052dc82aSMatthias Ringwald switch (gatt_client->state) {
18912da0d963SMatthias Ringwald case P_W4_INCLUDED_SERVICE_UUID_WITH_QUERY_RESULT:
18922da0d963SMatthias Ringwald if (size >= 17) {
18932da0d963SMatthias Ringwald uint8_t uuid128[16];
18942da0d963SMatthias Ringwald reverse_128(&packet[1], uuid128);
18952da0d963SMatthias Ringwald report_gatt_included_service_uuid128(gatt_client, gatt_client->start_group_handle, uuid128);
18962da0d963SMatthias Ringwald }
18972da0d963SMatthias Ringwald trigger_next_included_service_query(gatt_client, gatt_client->start_group_handle);
18985611a760SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
18993deb3ec6SMatthias Ringwald break;
19002da0d963SMatthias Ringwald
19012da0d963SMatthias Ringwald case P_W4_READ_CHARACTERISTIC_VALUE_RESULT:
19022da0d963SMatthias Ringwald report_gatt_characteristic_value(gatt_client, gatt_client->attribute_handle, &packet[1], size - 1u);
1903526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
19042da0d963SMatthias Ringwald break;
19052da0d963SMatthias Ringwald
19062da0d963SMatthias Ringwald case P_W4_READ_CHARACTERISTIC_DESCRIPTOR_RESULT:
19072da0d963SMatthias Ringwald report_gatt_characteristic_descriptor(gatt_client, gatt_client->attribute_handle, &packet[1],
19082da0d963SMatthias Ringwald size - 1u, 0u);
1909526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
19102da0d963SMatthias Ringwald break;
19112da0d963SMatthias Ringwald
19122da0d963SMatthias Ringwald // Use ATT_READ_REQUEST for first blob of Read Long Characteristic
19132da0d963SMatthias Ringwald case P_W4_READ_BLOB_RESULT:
19142da0d963SMatthias Ringwald report_gatt_long_characteristic_value_blob(gatt_client, gatt_client->attribute_handle, &packet[1],
19152da0d963SMatthias Ringwald size - 1u, gatt_client->attribute_offset);
19162da0d963SMatthias Ringwald trigger_next_blob_query(gatt_client, P_W2_SEND_READ_BLOB_QUERY, size - 1u);
19172da0d963SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
19182da0d963SMatthias Ringwald break;
19192da0d963SMatthias Ringwald
19202da0d963SMatthias Ringwald // Use ATT_READ_REQUEST for first blob of Read Long Characteristic Descriptor
19212da0d963SMatthias Ringwald case P_W4_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_RESULT:
19222da0d963SMatthias Ringwald report_gatt_long_characteristic_descriptor(gatt_client, gatt_client->attribute_handle, &packet[1],
19232da0d963SMatthias Ringwald size - 1u, gatt_client->attribute_offset);
19242da0d963SMatthias Ringwald trigger_next_blob_query(gatt_client, P_W2_SEND_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_QUERY,
19252da0d963SMatthias Ringwald size - 1u);
19262da0d963SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
19272da0d963SMatthias Ringwald break;
19282da0d963SMatthias Ringwald
19293deb3ec6SMatthias Ringwald default:
19303deb3ec6SMatthias Ringwald break;
19313deb3ec6SMatthias Ringwald }
19322da0d963SMatthias Ringwald }
19333deb3ec6SMatthias Ringwald
gatt_client_handle_att_read_by_type_response(gatt_client_t * gatt_client,uint8_t * packet,uint16_t size)19342da0d963SMatthias Ringwald static void gatt_client_handle_att_read_by_type_response(gatt_client_t *gatt_client, uint8_t *packet, uint16_t size) {
1935052dc82aSMatthias Ringwald switch (gatt_client->state) {
19363deb3ec6SMatthias Ringwald case P_W4_ALL_CHARACTERISTICS_OF_SERVICE_QUERY_RESULT:
19375cf1669fSMatthias Ringwald report_gatt_characteristics(gatt_client, packet, size);
1938cf8e5718SMatthias Ringwald trigger_next_characteristic_query(gatt_client,
1939cf8e5718SMatthias Ringwald get_last_result_handle_from_characteristics_list(packet, size));
19405611a760SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done, or by ATT_ERROR
19413deb3ec6SMatthias Ringwald break;
19423deb3ec6SMatthias Ringwald case P_W4_CHARACTERISTIC_WITH_UUID_QUERY_RESULT:
19435cf1669fSMatthias Ringwald report_gatt_characteristics(gatt_client, packet, size);
1944cf8e5718SMatthias Ringwald trigger_next_characteristic_query(gatt_client,
1945cf8e5718SMatthias Ringwald get_last_result_handle_from_characteristics_list(packet, size));
19465611a760SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done, or by ATT_ERROR
19473deb3ec6SMatthias Ringwald break;
1948cf8e5718SMatthias Ringwald case P_W4_INCLUDED_SERVICE_QUERY_RESULT: {
19494ea43905SMatthias Ringwald if (size < 2u) break;
19503deb3ec6SMatthias Ringwald uint16_t uuid16 = 0;
19513deb3ec6SMatthias Ringwald uint16_t pair_size = packet[1];
19523deb3ec6SMatthias Ringwald
19534ea43905SMatthias Ringwald if (pair_size == 6u) {
19544ea43905SMatthias Ringwald if (size < 8u) break;
19553deb3ec6SMatthias Ringwald // UUIDs not available, query first included service
19565cf1669fSMatthias Ringwald gatt_client->start_group_handle = little_endian_read_16(packet, 2); // ready for next query
19575cf1669fSMatthias Ringwald gatt_client->query_start_handle = little_endian_read_16(packet, 4);
19585cf1669fSMatthias Ringwald gatt_client->query_end_handle = little_endian_read_16(packet, 6);
1959052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_INCLUDED_SERVICE_WITH_UUID_QUERY;
19603deb3ec6SMatthias Ringwald break;
19613deb3ec6SMatthias Ringwald }
19623deb3ec6SMatthias Ringwald
19634ea43905SMatthias Ringwald if (pair_size != 8u) break;
19649f698c82SMatthias Ringwald
19659f698c82SMatthias Ringwald // UUIDs included, report all of them
19663deb3ec6SMatthias Ringwald uint16_t offset;
19674ea43905SMatthias Ringwald for (offset = 2u; (offset + 8u) <= size; offset += pair_size) {
1968f8fbdce0SMatthias Ringwald uint16_t include_handle = little_endian_read_16(packet, offset);
19695cf1669fSMatthias Ringwald gatt_client->query_start_handle = little_endian_read_16(packet, offset + 2u);
19705cf1669fSMatthias Ringwald gatt_client->query_end_handle = little_endian_read_16(packet, offset + 4u);
19714ea43905SMatthias Ringwald uuid16 = little_endian_read_16(packet, offset + 6u);
19725cf1669fSMatthias Ringwald report_gatt_included_service_uuid16(gatt_client, include_handle, uuid16);
19733deb3ec6SMatthias Ringwald }
19743deb3ec6SMatthias Ringwald
1975cf8e5718SMatthias Ringwald trigger_next_included_service_query(gatt_client,
1976cf8e5718SMatthias Ringwald get_last_result_handle_from_included_services_list(packet,
1977cf8e5718SMatthias Ringwald size));
19785611a760SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
19793deb3ec6SMatthias Ringwald break;
19803deb3ec6SMatthias Ringwald }
1981*197b4dd3SMatthias Ringwald #ifndef ENABLE_GATT_FIND_INFORMATION_FOR_CCC_DISCOVERY
1982*197b4dd3SMatthias Ringwald case P_W4_READ_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY_RESULT:
1983*197b4dd3SMatthias Ringwald gatt_client->client_characteristic_configuration_handle = little_endian_read_16(packet, 2);
1984*197b4dd3SMatthias Ringwald gatt_client->state = P_W2_WRITE_CLIENT_CHARACTERISTIC_CONFIGURATION;
1985*197b4dd3SMatthias Ringwald break;
1986*197b4dd3SMatthias Ringwald #endif
19873deb3ec6SMatthias Ringwald case P_W4_READ_BY_TYPE_RESPONSE: {
19883deb3ec6SMatthias Ringwald uint16_t pair_size = packet[1];
198939ac9711SMatthias Ringwald // set last result handle to last valid handle, only used if pair_size invalid
199039ac9711SMatthias Ringwald uint16_t last_result_handle = 0xffff;
199139ac9711SMatthias Ringwald if (pair_size > 2) {
19923deb3ec6SMatthias Ringwald uint16_t offset;
19933deb3ec6SMatthias Ringwald for (offset = 2; offset < size; offset += pair_size) {
1994f8fbdce0SMatthias Ringwald uint16_t value_handle = little_endian_read_16(packet, offset);
1995cf8e5718SMatthias Ringwald report_gatt_characteristic_value(gatt_client, value_handle, &packet[offset + 2u],
1996cf8e5718SMatthias Ringwald pair_size - 2u);
19973deb3ec6SMatthias Ringwald last_result_handle = value_handle;
19983deb3ec6SMatthias Ringwald }
199939ac9711SMatthias Ringwald }
20005cf1669fSMatthias Ringwald trigger_next_read_by_type_query(gatt_client, last_result_handle);
20013deb3ec6SMatthias Ringwald break;
20023deb3ec6SMatthias Ringwald }
20033deb3ec6SMatthias Ringwald default:
20043deb3ec6SMatthias Ringwald break;
20053deb3ec6SMatthias Ringwald }
200639ac9711SMatthias Ringwald }
2007dc13fd8dSMatthias Ringwald
gatt_client_handle_att_write_response(gatt_client_t * gatt_client)20082da0d963SMatthias Ringwald static void gatt_client_handle_att_write_response(gatt_client_t *gatt_client) {
2009052dc82aSMatthias Ringwald switch (gatt_client->state) {
20102da0d963SMatthias Ringwald case P_W4_WRITE_CHARACTERISTIC_VALUE_RESULT:
2011526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
20123deb3ec6SMatthias Ringwald break;
20132da0d963SMatthias Ringwald case P_W4_CLIENT_CHARACTERISTIC_CONFIGURATION_RESULT:
2014526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
20153deb3ec6SMatthias Ringwald break;
20162da0d963SMatthias Ringwald case P_W4_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT:
2017526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
20182da0d963SMatthias Ringwald break;
20192da0d963SMatthias Ringwald default:
20202da0d963SMatthias Ringwald break;
20212da0d963SMatthias Ringwald }
20222da0d963SMatthias Ringwald }
2023dc13fd8dSMatthias Ringwald
gatt_client_handle_att_response(gatt_client_t * gatt_client,uint8_t * packet,uint16_t size)20242da0d963SMatthias Ringwald static void gatt_client_handle_att_response(gatt_client_t * gatt_client, uint8_t * packet, uint16_t size) {
20254740230fSMatthias Ringwald uint8_t att_status;
20262da0d963SMatthias Ringwald switch (packet[0]) {
20272da0d963SMatthias Ringwald case ATT_EXCHANGE_MTU_RESPONSE: {
20282da0d963SMatthias Ringwald if (size < 3u) break;
202946012949SMatthias Ringwald bool update_gatt_server_att_mtu = false;
20302da0d963SMatthias Ringwald uint16_t remote_rx_mtu = little_endian_read_16(packet, 1);
20312da0d963SMatthias Ringwald uint16_t local_rx_mtu = l2cap_max_le_mtu();
203246012949SMatthias Ringwald switch (gatt_client->bearer_type){
203346012949SMatthias Ringwald case ATT_BEARER_UNENHANCED_LE:
203446012949SMatthias Ringwald update_gatt_server_att_mtu = true;
203546012949SMatthias Ringwald break;
20362da0d963SMatthias Ringwald #ifdef ENABLE_GATT_OVER_CLASSIC
203746012949SMatthias Ringwald case ATT_BEARER_UNENHANCED_CLASSIC:
20382da0d963SMatthias Ringwald local_rx_mtu = gatt_client->mtu;
203946012949SMatthias Ringwald break;
20402da0d963SMatthias Ringwald #endif
204146012949SMatthias Ringwald default:
204246012949SMatthias Ringwald btstack_unreachable();
204346012949SMatthias Ringwald break;
204446012949SMatthias Ringwald }
204546012949SMatthias Ringwald
20462da0d963SMatthias Ringwald uint16_t mtu = (remote_rx_mtu < local_rx_mtu) ? remote_rx_mtu : local_rx_mtu;
20472da0d963SMatthias Ringwald
20482da0d963SMatthias Ringwald // set gatt client mtu
20492da0d963SMatthias Ringwald gatt_client->mtu = mtu;
20502da0d963SMatthias Ringwald gatt_client->mtu_state = MTU_EXCHANGED;
20512da0d963SMatthias Ringwald
205246012949SMatthias Ringwald if (update_gatt_server_att_mtu){
20532da0d963SMatthias Ringwald // set per connection mtu state - for fixed channel
20542da0d963SMatthias Ringwald hci_connection_t *hci_connection = hci_connection_for_handle(gatt_client->con_handle);
20552da0d963SMatthias Ringwald hci_connection->att_connection.mtu = gatt_client->mtu;
20562da0d963SMatthias Ringwald hci_connection->att_connection.mtu_exchanged = true;
20572da0d963SMatthias Ringwald }
20582da0d963SMatthias Ringwald emit_gatt_mtu_exchanged_result_event(gatt_client, gatt_client->mtu);
20592da0d963SMatthias Ringwald break;
20602da0d963SMatthias Ringwald }
20612da0d963SMatthias Ringwald case ATT_READ_BY_GROUP_TYPE_RESPONSE:
2062052dc82aSMatthias Ringwald switch (gatt_client->state) {
20632da0d963SMatthias Ringwald case P_W4_SERVICE_QUERY_RESULT:
20642da0d963SMatthias Ringwald report_gatt_services(gatt_client, packet, size);
20652da0d963SMatthias Ringwald trigger_next_service_query(gatt_client, get_last_result_handle_from_service_list(packet, size));
206630952227SMilanka Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
206730952227SMilanka Ringwald break;
20683deb3ec6SMatthias Ringwald default:
20693deb3ec6SMatthias Ringwald break;
20703deb3ec6SMatthias Ringwald }
20713deb3ec6SMatthias Ringwald break;
20722da0d963SMatthias Ringwald case ATT_HANDLE_VALUE_NOTIFICATION:
20732da0d963SMatthias Ringwald if (size < 3u) return;
20742da0d963SMatthias Ringwald report_gatt_notification(gatt_client, little_endian_read_16(packet, 1u), &packet[3], size - 3u);
20752da0d963SMatthias Ringwald return;
2076cbba69d5SMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
2077cbba69d5SMatthias Ringwald case ATT_MULTIPLE_HANDLE_VALUE_NTF:
2078cbba69d5SMatthias Ringwald if (size >= 5u) {
2079cbba69d5SMatthias Ringwald uint16_t offset = 1;
2080cbba69d5SMatthias Ringwald while (true){
2081cbba69d5SMatthias Ringwald uint16_t value_handle = little_endian_read_16(packet, offset);
2082cbba69d5SMatthias Ringwald offset += 2;
2083cbba69d5SMatthias Ringwald uint16_t value_length = little_endian_read_16(packet, offset);
2084cbba69d5SMatthias Ringwald offset += 2;
2085cbba69d5SMatthias Ringwald if ((offset + value_length) > size) break;
2086cbba69d5SMatthias Ringwald report_gatt_notification(gatt_client, value_handle, &packet[offset], value_length);
2087cbba69d5SMatthias Ringwald offset += value_length;
2088cbba69d5SMatthias Ringwald }
2089cbba69d5SMatthias Ringwald }
2090cbba69d5SMatthias Ringwald return;
2091cbba69d5SMatthias Ringwald #endif
20922da0d963SMatthias Ringwald case ATT_HANDLE_VALUE_INDICATION:
20932da0d963SMatthias Ringwald if (size < 3u) break;
20942da0d963SMatthias Ringwald report_gatt_indication(gatt_client, little_endian_read_16(packet, 1u), &packet[3], size - 3u);
209552377058SMatthias Ringwald gatt_client->send_confirmation = true;
20962da0d963SMatthias Ringwald break;
20972da0d963SMatthias Ringwald case ATT_READ_BY_TYPE_RESPONSE:
20982da0d963SMatthias Ringwald gatt_client_handle_att_read_by_type_response(gatt_client, packet, size);
20992da0d963SMatthias Ringwald break;
21002da0d963SMatthias Ringwald case ATT_READ_RESPONSE:
21012da0d963SMatthias Ringwald gatt_client_handle_att_read_response(gatt_client, packet, size);
21022da0d963SMatthias Ringwald break;
2103cf8e5718SMatthias Ringwald case ATT_FIND_BY_TYPE_VALUE_RESPONSE: {
21043deb3ec6SMatthias Ringwald uint8_t pair_size = 4;
21053deb3ec6SMatthias Ringwald int i;
21063deb3ec6SMatthias Ringwald uint16_t start_group_handle;
21075611a760SMatthias Ringwald uint16_t end_group_handle = 0xffff; // asserts GATT_EVENT_QUERY_COMPLETE is emitted if no results
21084ea43905SMatthias Ringwald for (i = 1u; (i + pair_size) <= size; i += pair_size) {
2109f8fbdce0SMatthias Ringwald start_group_handle = little_endian_read_16(packet, i);
2110f8fbdce0SMatthias Ringwald end_group_handle = little_endian_read_16(packet, i + 2);
2111cf8e5718SMatthias Ringwald emit_gatt_service_query_result_event(gatt_client, start_group_handle, end_group_handle,
2112cf8e5718SMatthias Ringwald gatt_client->uuid128);
21133deb3ec6SMatthias Ringwald }
21145cf1669fSMatthias Ringwald trigger_next_service_by_uuid_query(gatt_client, end_group_handle);
21155611a760SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
21163deb3ec6SMatthias Ringwald break;
21173deb3ec6SMatthias Ringwald }
2118cf8e5718SMatthias Ringwald case ATT_FIND_INFORMATION_REPLY: {
21194ea43905SMatthias Ringwald if (size < 2u) break;
2120a6121b51SMilanka Ringwald
21213deb3ec6SMatthias Ringwald uint8_t pair_size = 4;
21224ea43905SMatthias Ringwald if (packet[1u] == 2u) {
21233deb3ec6SMatthias Ringwald pair_size = 18;
21243deb3ec6SMatthias Ringwald }
2125a6121b51SMilanka Ringwald uint16_t offset = 2;
2126a6121b51SMilanka Ringwald
2127a6121b51SMilanka Ringwald if (size < (pair_size + offset)) break;
2128f8fbdce0SMatthias Ringwald uint16_t last_descriptor_handle = little_endian_read_16(packet, size - pair_size);
2129abdc9fb5SMatthias Ringwald
2130*197b4dd3SMatthias Ringwald #ifdef ENABLE_GATT_FIND_INFORMATION_FOR_CCC_DISCOVERY
2131a9a6bd4cSMatthias Ringwald log_info("ENABLE_GATT_FIND_INFORMATION_FOR_CCC_DISCOVERY, state %x", gatt_client->state);
2132a9a6bd4cSMatthias Ringwald if (gatt_client->state == P_W4_FIND_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY_RESULT){
2133abdc9fb5SMatthias Ringwald // iterate over descriptors looking for CCC
2134abdc9fb5SMatthias Ringwald if (pair_size == 4){
2135a6121b51SMilanka Ringwald while ((offset + 4) <= size){
2136abdc9fb5SMatthias Ringwald uint16_t uuid16 = little_endian_read_16(packet, offset + 2);
2137abdc9fb5SMatthias Ringwald if (uuid16 == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION){
21385cf1669fSMatthias Ringwald gatt_client->client_characteristic_configuration_handle = little_endian_read_16(packet, offset);
2139a9a6bd4cSMatthias Ringwald gatt_client->state = P_W2_WRITE_CLIENT_CHARACTERISTIC_CONFIGURATION;
21405cf1669fSMatthias Ringwald log_info("CCC found %x", gatt_client->client_characteristic_configuration_handle);
2141abdc9fb5SMatthias Ringwald break;
2142abdc9fb5SMatthias Ringwald }
2143abdc9fb5SMatthias Ringwald offset += pair_size;
2144abdc9fb5SMatthias Ringwald }
2145abdc9fb5SMatthias Ringwald }
21465cf1669fSMatthias Ringwald if (is_query_done(gatt_client, last_descriptor_handle)){
2147abdc9fb5SMatthias Ringwald
2148abdc9fb5SMatthias Ringwald } else {
2149abdc9fb5SMatthias Ringwald // next
21505cf1669fSMatthias Ringwald gatt_client->start_group_handle = last_descriptor_handle + 1;
2151a9a6bd4cSMatthias Ringwald gatt_client->state = P_W2_SEND_FIND_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY;
2152abdc9fb5SMatthias Ringwald }
2153abdc9fb5SMatthias Ringwald break;
2154abdc9fb5SMatthias Ringwald }
2155*197b4dd3SMatthias Ringwald #endif
21565cf1669fSMatthias Ringwald report_gatt_all_characteristic_descriptors(gatt_client, &packet[2], size - 2u, pair_size);
21575cf1669fSMatthias Ringwald trigger_next_characteristic_descriptor_query(gatt_client, last_descriptor_handle);
21585611a760SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
21593deb3ec6SMatthias Ringwald break;
21603deb3ec6SMatthias Ringwald }
21613deb3ec6SMatthias Ringwald
21623deb3ec6SMatthias Ringwald case ATT_WRITE_RESPONSE:
21632da0d963SMatthias Ringwald gatt_client_handle_att_write_response(gatt_client);
21643deb3ec6SMatthias Ringwald break;
21653deb3ec6SMatthias Ringwald
21663deb3ec6SMatthias Ringwald case ATT_READ_BLOB_RESPONSE: {
21674ea43905SMatthias Ringwald uint16_t received_blob_length = size - 1u;
2168052dc82aSMatthias Ringwald switch (gatt_client->state) {
21693deb3ec6SMatthias Ringwald case P_W4_READ_BLOB_RESULT:
2170cf8e5718SMatthias Ringwald report_gatt_long_characteristic_value_blob(gatt_client, gatt_client->attribute_handle, &packet[1],
2171cf8e5718SMatthias Ringwald received_blob_length, gatt_client->attribute_offset);
21725cf1669fSMatthias Ringwald trigger_next_blob_query(gatt_client, P_W2_SEND_READ_BLOB_QUERY, received_blob_length);
21735611a760SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
21743deb3ec6SMatthias Ringwald break;
21753deb3ec6SMatthias Ringwald case P_W4_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_RESULT:
21765cf1669fSMatthias Ringwald report_gatt_long_characteristic_descriptor(gatt_client, gatt_client->attribute_handle,
21773deb3ec6SMatthias Ringwald &packet[1], received_blob_length,
21785cf1669fSMatthias Ringwald gatt_client->attribute_offset);
2179cf8e5718SMatthias Ringwald trigger_next_blob_query(gatt_client, P_W2_SEND_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_QUERY,
2180cf8e5718SMatthias Ringwald received_blob_length);
21815611a760SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
21823deb3ec6SMatthias Ringwald break;
21833deb3ec6SMatthias Ringwald default:
21843deb3ec6SMatthias Ringwald break;
21853deb3ec6SMatthias Ringwald }
21863deb3ec6SMatthias Ringwald break;
21873deb3ec6SMatthias Ringwald }
21883deb3ec6SMatthias Ringwald case ATT_PREPARE_WRITE_RESPONSE:
2189052dc82aSMatthias Ringwald switch (gatt_client->state) {
21903deb3ec6SMatthias Ringwald case P_W4_PREPARE_WRITE_SINGLE_RESULT:
21915cf1669fSMatthias Ringwald if (is_value_valid(gatt_client, packet, size)) {
21924740230fSMatthias Ringwald att_status = ATT_ERROR_SUCCESS;
21933deb3ec6SMatthias Ringwald } else {
21944740230fSMatthias Ringwald att_status = ATT_ERROR_DATA_MISMATCH;
21953deb3ec6SMatthias Ringwald }
2196526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, att_status);
21973deb3ec6SMatthias Ringwald break;
21983deb3ec6SMatthias Ringwald
21993deb3ec6SMatthias Ringwald case P_W4_PREPARE_WRITE_RESULT: {
22005cf1669fSMatthias Ringwald gatt_client->attribute_offset = little_endian_read_16(packet, 3);
22015cf1669fSMatthias Ringwald trigger_next_prepare_write_query(gatt_client, P_W2_PREPARE_WRITE, P_W2_EXECUTE_PREPARED_WRITE);
22025611a760SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
22033deb3ec6SMatthias Ringwald break;
22043deb3ec6SMatthias Ringwald }
22053deb3ec6SMatthias Ringwald case P_W4_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT: {
22065cf1669fSMatthias Ringwald gatt_client->attribute_offset = little_endian_read_16(packet, 3);
2207cf8e5718SMatthias Ringwald trigger_next_prepare_write_query(gatt_client, P_W2_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR,
2208cf8e5718SMatthias Ringwald P_W2_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR);
22095611a760SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
22103deb3ec6SMatthias Ringwald break;
22113deb3ec6SMatthias Ringwald }
22123deb3ec6SMatthias Ringwald case P_W4_PREPARE_RELIABLE_WRITE_RESULT: {
22135cf1669fSMatthias Ringwald if (is_value_valid(gatt_client, packet, size)) {
22145cf1669fSMatthias Ringwald gatt_client->attribute_offset = little_endian_read_16(packet, 3);
2215cf8e5718SMatthias Ringwald trigger_next_prepare_write_query(gatt_client, P_W2_PREPARE_RELIABLE_WRITE,
2216cf8e5718SMatthias Ringwald P_W2_EXECUTE_PREPARED_WRITE);
22175611a760SMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
22183deb3ec6SMatthias Ringwald break;
22193deb3ec6SMatthias Ringwald }
2220052dc82aSMatthias Ringwald gatt_client->state = P_W2_CANCEL_PREPARED_WRITE_DATA_MISMATCH;
22213deb3ec6SMatthias Ringwald break;
22223deb3ec6SMatthias Ringwald }
22233deb3ec6SMatthias Ringwald default:
22243deb3ec6SMatthias Ringwald break;
22253deb3ec6SMatthias Ringwald }
22263deb3ec6SMatthias Ringwald break;
22273deb3ec6SMatthias Ringwald
22283deb3ec6SMatthias Ringwald case ATT_EXECUTE_WRITE_RESPONSE:
2229052dc82aSMatthias Ringwald switch (gatt_client->state) {
22303deb3ec6SMatthias Ringwald case P_W4_EXECUTE_PREPARED_WRITE_RESULT:
2231526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
22323deb3ec6SMatthias Ringwald break;
22333deb3ec6SMatthias Ringwald case P_W4_CANCEL_PREPARED_WRITE_RESULT:
2234526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
22353deb3ec6SMatthias Ringwald break;
22363deb3ec6SMatthias Ringwald case P_W4_CANCEL_PREPARED_WRITE_DATA_MISMATCH_RESULT:
2237526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_DATA_MISMATCH);
22383deb3ec6SMatthias Ringwald break;
22393deb3ec6SMatthias Ringwald case P_W4_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT:
2240526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
22413deb3ec6SMatthias Ringwald break;
22423deb3ec6SMatthias Ringwald default:
22433deb3ec6SMatthias Ringwald break;
22443deb3ec6SMatthias Ringwald
22453deb3ec6SMatthias Ringwald }
22463deb3ec6SMatthias Ringwald break;
22473deb3ec6SMatthias Ringwald
22483deb3ec6SMatthias Ringwald case ATT_READ_MULTIPLE_RESPONSE:
2249052dc82aSMatthias Ringwald switch (gatt_client->state) {
22503deb3ec6SMatthias Ringwald case P_W4_READ_MULTIPLE_RESPONSE:
22515cf1669fSMatthias Ringwald report_gatt_characteristic_value(gatt_client, 0u, &packet[1], size - 1u);
2252526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
22533deb3ec6SMatthias Ringwald break;
22543deb3ec6SMatthias Ringwald default:
22553deb3ec6SMatthias Ringwald break;
22563deb3ec6SMatthias Ringwald }
22573deb3ec6SMatthias Ringwald break;
22583deb3ec6SMatthias Ringwald
2259f125a8efSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
2260f125a8efSMatthias Ringwald case ATT_READ_MULTIPLE_VARIABLE_RSP:
2261052dc82aSMatthias Ringwald switch (gatt_client->state) {
2262169b9876SMatthias Ringwald case P_W4_READ_MULTIPLE_VARIABLE_RESPONSE:
2263f125a8efSMatthias Ringwald report_gatt_characteristic_value(gatt_client, 0u, &packet[1], size - 1u);
2264f125a8efSMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
2265f125a8efSMatthias Ringwald break;
2266f125a8efSMatthias Ringwald default:
2267f125a8efSMatthias Ringwald break;
2268f125a8efSMatthias Ringwald }
2269f125a8efSMatthias Ringwald break;
2270f125a8efSMatthias Ringwald #endif
2271f125a8efSMatthias Ringwald
22723deb3ec6SMatthias Ringwald case ATT_ERROR_RESPONSE:
22734ea43905SMatthias Ringwald if (size < 5u) return;
22744740230fSMatthias Ringwald att_status = packet[4];
22754740230fSMatthias Ringwald switch (att_status) {
22763deb3ec6SMatthias Ringwald case ATT_ERROR_ATTRIBUTE_NOT_FOUND: {
2277052dc82aSMatthias Ringwald switch (gatt_client->state) {
22783deb3ec6SMatthias Ringwald case P_W4_SERVICE_QUERY_RESULT:
22793deb3ec6SMatthias Ringwald case P_W4_SERVICE_WITH_UUID_RESULT:
22803deb3ec6SMatthias Ringwald case P_W4_INCLUDED_SERVICE_QUERY_RESULT:
22813deb3ec6SMatthias Ringwald case P_W4_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
2282526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
22833deb3ec6SMatthias Ringwald break;
22843deb3ec6SMatthias Ringwald case P_W4_ALL_CHARACTERISTICS_OF_SERVICE_QUERY_RESULT:
22853deb3ec6SMatthias Ringwald case P_W4_CHARACTERISTIC_WITH_UUID_QUERY_RESULT:
228623d583b8SMatthias Ringwald report_gatt_characteristic_end_found(gatt_client, gatt_client->end_group_handle);
2287526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
22883deb3ec6SMatthias Ringwald break;
22893deb3ec6SMatthias Ringwald case P_W4_READ_BY_TYPE_RESPONSE:
22905cf1669fSMatthias Ringwald if (gatt_client->start_group_handle == gatt_client->query_start_handle) {
22914740230fSMatthias Ringwald att_status = ATT_ERROR_ATTRIBUTE_NOT_FOUND;
22923deb3ec6SMatthias Ringwald } else {
22934740230fSMatthias Ringwald att_status = ATT_ERROR_SUCCESS;
22943deb3ec6SMatthias Ringwald }
2295526859e7SMatthias Ringwald gatt_client_handle_transaction_complete(gatt_client, att_status);
22963deb3ec6SMatthias Ringwald break;
22973deb3ec6SMatthias Ringwald default:
22984740230fSMatthias Ringwald gatt_client_report_error_if_pending(gatt_client, att_status);
22993deb3ec6SMatthias Ringwald break;
23003deb3ec6SMatthias Ringwald }
23013deb3ec6SMatthias Ringwald break;
23023deb3ec6SMatthias Ringwald }
2303f4b33574SMatthias Ringwald
2304f4b33574SMatthias Ringwald #ifdef ENABLE_GATT_CLIENT_PAIRING
2305f4b33574SMatthias Ringwald
2306f4b33574SMatthias Ringwald case ATT_ERROR_INSUFFICIENT_AUTHENTICATION:
2307f4b33574SMatthias Ringwald case ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE:
2308a0e0c2beSMatthias Ringwald case ATT_ERROR_INSUFFICIENT_ENCRYPTION: {
2309a0e0c2beSMatthias Ringwald
2310f4b33574SMatthias Ringwald // security too low
23115cf1669fSMatthias Ringwald if (gatt_client->security_counter > 0) {
23124740230fSMatthias Ringwald gatt_client_report_error_if_pending(gatt_client, att_status);
2313f4b33574SMatthias Ringwald break;
2314f4b33574SMatthias Ringwald }
2315f4b33574SMatthias Ringwald // start security
23165cf1669fSMatthias Ringwald gatt_client->security_counter++;
2317f4b33574SMatthias Ringwald
2318f4b33574SMatthias Ringwald // setup action
2319f4b33574SMatthias Ringwald int retry = 1;
2320052dc82aSMatthias Ringwald switch (gatt_client->state){
2321f4b33574SMatthias Ringwald case P_W4_READ_CHARACTERISTIC_VALUE_RESULT:
2322052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_CHARACTERISTIC_VALUE_QUERY ;
2323f4b33574SMatthias Ringwald break;
2324f4b33574SMatthias Ringwald case P_W4_READ_BLOB_RESULT:
2325052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_BLOB_QUERY;
2326f4b33574SMatthias Ringwald break;
2327f4b33574SMatthias Ringwald case P_W4_READ_BY_TYPE_RESPONSE:
2328052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_BY_TYPE_REQUEST;
2329f4b33574SMatthias Ringwald break;
2330f4b33574SMatthias Ringwald case P_W4_READ_MULTIPLE_RESPONSE:
2331052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_MULTIPLE_REQUEST;
2332f4b33574SMatthias Ringwald break;
2333f125a8efSMatthias Ringwald case P_W4_READ_MULTIPLE_VARIABLE_RESPONSE:
2334052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_MULTIPLE_VARIABLE_REQUEST;
2335f125a8efSMatthias Ringwald break;
2336f4b33574SMatthias Ringwald case P_W4_WRITE_CHARACTERISTIC_VALUE_RESULT:
2337052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_WRITE_CHARACTERISTIC_VALUE;
2338f4b33574SMatthias Ringwald break;
2339f4b33574SMatthias Ringwald case P_W4_PREPARE_WRITE_RESULT:
2340052dc82aSMatthias Ringwald gatt_client->state = P_W2_PREPARE_WRITE;
2341f4b33574SMatthias Ringwald break;
2342f4b33574SMatthias Ringwald case P_W4_PREPARE_WRITE_SINGLE_RESULT:
2343052dc82aSMatthias Ringwald gatt_client->state = P_W2_PREPARE_WRITE_SINGLE;
2344f4b33574SMatthias Ringwald break;
2345f4b33574SMatthias Ringwald case P_W4_PREPARE_RELIABLE_WRITE_RESULT:
2346052dc82aSMatthias Ringwald gatt_client->state = P_W2_PREPARE_RELIABLE_WRITE;
2347f4b33574SMatthias Ringwald break;
2348f4b33574SMatthias Ringwald case P_W4_EXECUTE_PREPARED_WRITE_RESULT:
2349052dc82aSMatthias Ringwald gatt_client->state = P_W2_EXECUTE_PREPARED_WRITE;
2350f4b33574SMatthias Ringwald break;
2351f4b33574SMatthias Ringwald case P_W4_CANCEL_PREPARED_WRITE_RESULT:
2352052dc82aSMatthias Ringwald gatt_client->state = P_W2_CANCEL_PREPARED_WRITE;
2353f4b33574SMatthias Ringwald break;
2354f4b33574SMatthias Ringwald case P_W4_CANCEL_PREPARED_WRITE_DATA_MISMATCH_RESULT:
2355052dc82aSMatthias Ringwald gatt_client->state = P_W2_CANCEL_PREPARED_WRITE_DATA_MISMATCH;
2356f4b33574SMatthias Ringwald break;
2357f4b33574SMatthias Ringwald case P_W4_READ_CHARACTERISTIC_DESCRIPTOR_RESULT:
2358052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_CHARACTERISTIC_DESCRIPTOR_QUERY;
2359f4b33574SMatthias Ringwald break;
2360f4b33574SMatthias Ringwald case P_W4_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_RESULT:
2361052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_QUERY;
2362f4b33574SMatthias Ringwald break;
2363f4b33574SMatthias Ringwald case P_W4_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT:
2364052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_WRITE_CHARACTERISTIC_DESCRIPTOR;
2365f4b33574SMatthias Ringwald break;
2366f4b33574SMatthias Ringwald case P_W4_CLIENT_CHARACTERISTIC_CONFIGURATION_RESULT:
2367052dc82aSMatthias Ringwald gatt_client->state = P_W2_WRITE_CLIENT_CHARACTERISTIC_CONFIGURATION;
2368f4b33574SMatthias Ringwald break;
2369f4b33574SMatthias Ringwald case P_W4_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT:
2370052dc82aSMatthias Ringwald gatt_client->state = P_W2_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR;
2371f4b33574SMatthias Ringwald break;
2372f4b33574SMatthias Ringwald case P_W4_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT:
2373052dc82aSMatthias Ringwald gatt_client->state = P_W2_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR;
2374f4b33574SMatthias Ringwald break;
2375f4b33574SMatthias Ringwald #ifdef ENABLE_LE_SIGNED_WRITE
237630cf4543SMilanka Ringwald case P_W4_SEND_SIGNED_WRITE_DONE:
2377052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_SIGNED_WRITE;
2378f4b33574SMatthias Ringwald break;
2379f4b33574SMatthias Ringwald #endif
2380f4b33574SMatthias Ringwald default:
2381052dc82aSMatthias Ringwald log_info("retry not supported for state %x", gatt_client->state);
2382f4b33574SMatthias Ringwald retry = 0;
2383f4b33574SMatthias Ringwald break;
2384f4b33574SMatthias Ringwald }
2385f4b33574SMatthias Ringwald
2386f4b33574SMatthias Ringwald if (!retry) {
23874740230fSMatthias Ringwald gatt_client_report_error_if_pending(gatt_client, att_status);
2388f4b33574SMatthias Ringwald break;
2389f4b33574SMatthias Ringwald }
2390f4b33574SMatthias Ringwald
2391f4b33574SMatthias Ringwald log_info("security error, start pairing");
2392f4b33574SMatthias Ringwald
2393f060f108SMatthias Ringwald // start pairing for higher security level
23942197dbafSMatthias Ringwald gatt_client->wait_for_authentication_complete = true;
23954740230fSMatthias Ringwald gatt_client->pending_error_code = att_status;
23965cf1669fSMatthias Ringwald sm_request_pairing(gatt_client->con_handle);
2397f4b33574SMatthias Ringwald break;
2398a0e0c2beSMatthias Ringwald }
2399f4b33574SMatthias Ringwald #endif
2400f4b33574SMatthias Ringwald
2401f4b33574SMatthias Ringwald // nothing we can do about that
2402f4b33574SMatthias Ringwald case ATT_ERROR_INSUFFICIENT_AUTHORIZATION:
24033deb3ec6SMatthias Ringwald default:
24044740230fSMatthias Ringwald gatt_client_report_error_if_pending(gatt_client, att_status);
24053deb3ec6SMatthias Ringwald break;
24063deb3ec6SMatthias Ringwald }
24073deb3ec6SMatthias Ringwald break;
24083deb3ec6SMatthias Ringwald
24093deb3ec6SMatthias Ringwald default:
24103deb3ec6SMatthias Ringwald log_info("ATT Handler, unhandled response type 0x%02x", packet[0]);
24113deb3ec6SMatthias Ringwald break;
24123deb3ec6SMatthias Ringwald }
2413cf8e5718SMatthias Ringwald }
2414cf8e5718SMatthias Ringwald
gatt_client_att_packet_handler(uint8_t packet_type,uint16_t handle,uint8_t * packet,uint16_t size)2415cf8e5718SMatthias Ringwald static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size) {
2416cf8e5718SMatthias Ringwald gatt_client_t *gatt_client;
2417180cbe79SMatthias Ringwald #ifdef ENABLE_GATT_OVER_CLASSIC
2418b1da4983SMatthias Ringwald uint8_t status;
2419b1da4983SMatthias Ringwald hci_connection_t * hci_connection;
2420180cbe79SMatthias Ringwald hci_con_handle_t con_handle;
2421180cbe79SMatthias Ringwald #endif
2422180cbe79SMatthias Ringwald
2423cf8e5718SMatthias Ringwald if (size < 1u) return;
242459b5e157SMatthias Ringwald switch (packet_type){
242559b5e157SMatthias Ringwald case HCI_EVENT_PACKET:
242659b5e157SMatthias Ringwald switch (hci_event_packet_get_type(packet)) {
242759b5e157SMatthias Ringwald #ifdef ENABLE_GATT_OVER_CLASSIC
242859b5e157SMatthias Ringwald case L2CAP_EVENT_CHANNEL_OPENED:
242959b5e157SMatthias Ringwald status = l2cap_event_channel_opened_get_status(packet);
243059b5e157SMatthias Ringwald gatt_client = gatt_client_get_context_for_l2cap_cid(l2cap_event_channel_opened_get_local_cid(packet));
2431de5a18caSMatthias Ringwald if (gatt_client != NULL){
2432180cbe79SMatthias Ringwald con_handle = l2cap_event_channel_opened_get_handle(packet);
2433180cbe79SMatthias Ringwald hci_connection = hci_connection_for_handle(con_handle);
2434ab6f9d8fSMatthias Ringwald if (status == L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_RESOURCES){
2435ab6f9d8fSMatthias Ringwald if ((hci_connection != NULL) && hci_connection->att_server.incoming_connection_request) {
2436180cbe79SMatthias Ringwald log_info("Collision, retry in 100ms");
2437180cbe79SMatthias Ringwald gatt_client->state = P_W2_L2CAP_CONNECT;
2438180cbe79SMatthias Ringwald // set timer for retry
2439b1da4983SMatthias Ringwald btstack_run_loop_set_timer(&gatt_client->gc_timeout, GATT_CLIENT_COLLISION_BACKOFF_MS);
2440180cbe79SMatthias Ringwald btstack_run_loop_set_timer_handler(&gatt_client->gc_timeout, gatt_client_classic_retry);
2441180cbe79SMatthias Ringwald btstack_run_loop_add_timer(&gatt_client->gc_timeout);
2442180cbe79SMatthias Ringwald break;
2443180cbe79SMatthias Ringwald }
2444ab6f9d8fSMatthias Ringwald }
244559b5e157SMatthias Ringwald // if status != 0, gatt_client will be discarded
2446052dc82aSMatthias Ringwald gatt_client->state = P_READY;
244759b5e157SMatthias Ringwald gatt_client->con_handle = l2cap_event_channel_opened_get_handle(packet);
244859b5e157SMatthias Ringwald gatt_client->mtu = l2cap_event_channel_opened_get_remote_mtu(packet);
244959b5e157SMatthias Ringwald gatt_client_classic_handle_connected(gatt_client, status);
2450de5a18caSMatthias Ringwald }
245159b5e157SMatthias Ringwald break;
245259b5e157SMatthias Ringwald case L2CAP_EVENT_CHANNEL_CLOSED:
245359b5e157SMatthias Ringwald gatt_client = gatt_client_get_context_for_l2cap_cid(l2cap_event_channel_closed_get_local_cid(packet));
2454de5a18caSMatthias Ringwald if (gatt_client != NULL){
245559b5e157SMatthias Ringwald // discard gatt client object
245659b5e157SMatthias Ringwald gatt_client_classic_handle_disconnected(gatt_client);
2457de5a18caSMatthias Ringwald }
245859b5e157SMatthias Ringwald break;
245959b5e157SMatthias Ringwald #endif
2460cf8e5718SMatthias Ringwald case L2CAP_EVENT_CAN_SEND_NOW:
24613deb3ec6SMatthias Ringwald gatt_client_run();
2462cf8e5718SMatthias Ringwald break;
2463cf8e5718SMatthias Ringwald // att_server has negotiated the mtu for this connection, cache if context exists
2464cf8e5718SMatthias Ringwald case ATT_EVENT_MTU_EXCHANGE_COMPLETE:
2465cf8e5718SMatthias Ringwald if (size < 6u) break;
2466cf8e5718SMatthias Ringwald gatt_client = gatt_client_get_context_for_handle(handle);
2467de5a18caSMatthias Ringwald if (gatt_client != NULL) {
2468cf8e5718SMatthias Ringwald gatt_client->mtu = little_endian_read_16(packet, 4);
2469de5a18caSMatthias Ringwald }
2470cf8e5718SMatthias Ringwald break;
2471cf8e5718SMatthias Ringwald default:
2472cf8e5718SMatthias Ringwald break;
2473cf8e5718SMatthias Ringwald }
247459b5e157SMatthias Ringwald break;
2475cf8e5718SMatthias Ringwald
247659b5e157SMatthias Ringwald case ATT_DATA_PACKET:
2477cf8e5718SMatthias Ringwald // special cases: notifications & indications motivate creating context
2478cf8e5718SMatthias Ringwald switch (packet[0]) {
2479cf8e5718SMatthias Ringwald case ATT_HANDLE_VALUE_NOTIFICATION:
2480cf8e5718SMatthias Ringwald case ATT_HANDLE_VALUE_INDICATION:
2481cf8e5718SMatthias Ringwald gatt_client_provide_context_for_handle(handle, &gatt_client);
2482cf8e5718SMatthias Ringwald break;
2483cf8e5718SMatthias Ringwald default:
2484cf8e5718SMatthias Ringwald gatt_client = gatt_client_get_context_for_handle(handle);
2485cf8e5718SMatthias Ringwald break;
2486cf8e5718SMatthias Ringwald }
2487cf8e5718SMatthias Ringwald
2488cf8e5718SMatthias Ringwald if (gatt_client != NULL) {
2489cf8e5718SMatthias Ringwald gatt_client_handle_att_response(gatt_client, packet, size);
2490cf8e5718SMatthias Ringwald gatt_client_run();
2491cf8e5718SMatthias Ringwald }
249259b5e157SMatthias Ringwald break;
249359b5e157SMatthias Ringwald
249459b5e157SMatthias Ringwald #ifdef ENABLE_GATT_OVER_CLASSIC
249559b5e157SMatthias Ringwald case L2CAP_DATA_PACKET:
249659b5e157SMatthias Ringwald gatt_client = gatt_client_get_context_for_l2cap_cid(handle);
2497de5a18caSMatthias Ringwald if (gatt_client != NULL){
249859b5e157SMatthias Ringwald gatt_client_handle_att_response(gatt_client, packet, size);
249959b5e157SMatthias Ringwald gatt_client_run();
2500de5a18caSMatthias Ringwald }
250159b5e157SMatthias Ringwald break;
250259b5e157SMatthias Ringwald #endif
250359b5e157SMatthias Ringwald
250459b5e157SMatthias Ringwald default:
250559b5e157SMatthias Ringwald break;
250659b5e157SMatthias Ringwald }
25073deb3ec6SMatthias Ringwald }
25083deb3ec6SMatthias Ringwald
25097a766ebfSMatthias Ringwald #ifdef ENABLE_LE_SIGNED_WRITE
att_signed_write_handle_cmac_result(uint8_t hash[8])25103deb3ec6SMatthias Ringwald static void att_signed_write_handle_cmac_result(uint8_t hash[8]){
2511665d90f2SMatthias Ringwald btstack_linked_list_iterator_t it;
2512665d90f2SMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_client_connections);
2513665d90f2SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
25145cf1669fSMatthias Ringwald gatt_client_t * gatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
2515052dc82aSMatthias Ringwald if (gatt_client->state == P_W4_CMAC_RESULT){
25163deb3ec6SMatthias Ringwald // store result
25175cf1669fSMatthias Ringwald (void)memcpy(gatt_client->cmac, hash, 8);
25185cf1669fSMatthias Ringwald // reverse_64(hash, gatt_client->cmac);
2519052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_SIGNED_WRITE;
25203deb3ec6SMatthias Ringwald gatt_client_run();
25213deb3ec6SMatthias Ringwald return;
25223deb3ec6SMatthias Ringwald }
25233deb3ec6SMatthias Ringwald }
25243deb3ec6SMatthias Ringwald }
25253deb3ec6SMatthias Ringwald
gatt_client_signed_write_without_response(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t value_handle,uint16_t message_len,uint8_t * message)2526b45b7749SMilanka Ringwald uint8_t gatt_client_signed_write_without_response(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t message_len, uint8_t * message){
252740faeb84SMilanka Ringwald gatt_client_t * gatt_client;
252840faeb84SMilanka Ringwald uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
252940faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
253040faeb84SMilanka Ringwald return status;
253140faeb84SMilanka Ringwald }
253240faeb84SMilanka Ringwald if (is_ready(gatt_client) == 0){
253340faeb84SMilanka Ringwald return GATT_CLIENT_IN_WRONG_STATE;
253440faeb84SMilanka Ringwald }
25353deb3ec6SMatthias Ringwald
25365cf1669fSMatthias Ringwald gatt_client->callback = callback;
2537b45b7749SMilanka Ringwald gatt_client->attribute_handle = value_handle;
25385cf1669fSMatthias Ringwald gatt_client->attribute_length = message_len;
25395cf1669fSMatthias Ringwald gatt_client->attribute_value = message;
2540052dc82aSMatthias Ringwald gatt_client->state = P_W4_IDENTITY_RESOLVING;
25413deb3ec6SMatthias Ringwald gatt_client_run();
254225b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
25433deb3ec6SMatthias Ringwald }
25447a766ebfSMatthias Ringwald #endif
25453deb3ec6SMatthias Ringwald
gatt_client_discover_primary_services(btstack_packet_handler_t callback,hci_con_handle_t con_handle)2546711e6c80SMatthias Ringwald uint8_t gatt_client_discover_primary_services(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
254740faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2548de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
254940faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
255040faeb84SMilanka Ringwald return status;
255140faeb84SMilanka Ringwald }
25523deb3ec6SMatthias Ringwald
25535cf1669fSMatthias Ringwald gatt_client->callback = callback;
25545cf1669fSMatthias Ringwald gatt_client->start_group_handle = 0x0001;
25555cf1669fSMatthias Ringwald gatt_client->end_group_handle = 0xffff;
2556052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_SERVICE_QUERY;
25578d1f34d3SMatheus Eduardo Garbelini gatt_client->uuid16 = GATT_PRIMARY_SERVICE_UUID;
25583deb3ec6SMatthias Ringwald gatt_client_run();
255925b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
25603deb3ec6SMatthias Ringwald }
25613deb3ec6SMatthias Ringwald
gatt_client_discover_secondary_services(btstack_packet_handler_t callback,hci_con_handle_t con_handle)25628d1f34d3SMatheus Eduardo Garbelini uint8_t gatt_client_discover_secondary_services(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
256340faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2564de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
256540faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
256640faeb84SMilanka Ringwald return status;
256740faeb84SMilanka Ringwald }
25688d1f34d3SMatheus Eduardo Garbelini
25698d1f34d3SMatheus Eduardo Garbelini gatt_client->callback = callback;
25708d1f34d3SMatheus Eduardo Garbelini gatt_client->start_group_handle = 0x0001;
25718d1f34d3SMatheus Eduardo Garbelini gatt_client->end_group_handle = 0xffff;
2572052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_SERVICE_QUERY;
25738d1f34d3SMatheus Eduardo Garbelini gatt_client->uuid16 = GATT_SECONDARY_SERVICE_UUID;
25748d1f34d3SMatheus Eduardo Garbelini gatt_client_run();
25758d1f34d3SMatheus Eduardo Garbelini return ERROR_CODE_SUCCESS;
25768d1f34d3SMatheus Eduardo Garbelini }
25773deb3ec6SMatthias Ringwald
gatt_client_discover_primary_services_by_uuid16_with_context(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t uuid16,uint16_t service_id,uint16_t connection_id)257868ced5c9SMatthias Ringwald uint8_t gatt_client_discover_primary_services_by_uuid16_with_context(btstack_packet_handler_t callback, hci_con_handle_t con_handle,
257968ced5c9SMatthias Ringwald uint16_t uuid16, uint16_t service_id, uint16_t connection_id){
258040faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2581de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
258240faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
258340faeb84SMilanka Ringwald return status;
258440faeb84SMilanka Ringwald }
25853deb3ec6SMatthias Ringwald
25865cf1669fSMatthias Ringwald gatt_client->callback = callback;
258768ced5c9SMatthias Ringwald gatt_client->service_id = service_id;
258868ced5c9SMatthias Ringwald gatt_client->connection_id = connection_id;
25895cf1669fSMatthias Ringwald gatt_client->start_group_handle = 0x0001;
25905cf1669fSMatthias Ringwald gatt_client->end_group_handle = 0xffff;
2591052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_SERVICE_WITH_UUID_QUERY;
25925cf1669fSMatthias Ringwald gatt_client->uuid16 = uuid16;
25935cf1669fSMatthias Ringwald uuid_add_bluetooth_prefix((uint8_t*) &(gatt_client->uuid128), gatt_client->uuid16);
25943deb3ec6SMatthias Ringwald gatt_client_run();
259525b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
25963deb3ec6SMatthias Ringwald }
25973deb3ec6SMatthias Ringwald
gatt_client_discover_primary_services_by_uuid16(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t uuid16)259868ced5c9SMatthias Ringwald uint8_t gatt_client_discover_primary_services_by_uuid16(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t uuid16){
259968ced5c9SMatthias Ringwald return gatt_client_discover_primary_services_by_uuid16_with_context(callback, con_handle, uuid16, 0, 0);
260068ced5c9SMatthias Ringwald }
260168ced5c9SMatthias Ringwald
gatt_client_discover_primary_services_by_uuid128(btstack_packet_handler_t callback,hci_con_handle_t con_handle,const uint8_t * uuid128)2602711e6c80SMatthias Ringwald uint8_t gatt_client_discover_primary_services_by_uuid128(btstack_packet_handler_t callback, hci_con_handle_t con_handle, const uint8_t * uuid128){
260340faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2604de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
260540faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
260640faeb84SMilanka Ringwald return status;
260740faeb84SMilanka Ringwald }
26083deb3ec6SMatthias Ringwald
26095cf1669fSMatthias Ringwald gatt_client->callback = callback;
26105cf1669fSMatthias Ringwald gatt_client->start_group_handle = 0x0001;
26115cf1669fSMatthias Ringwald gatt_client->end_group_handle = 0xffff;
26125cf1669fSMatthias Ringwald gatt_client->uuid16 = 0;
26135cf1669fSMatthias Ringwald (void)memcpy(gatt_client->uuid128, uuid128, 16);
2614052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_SERVICE_WITH_UUID_QUERY;
26153deb3ec6SMatthias Ringwald gatt_client_run();
261625b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
26173deb3ec6SMatthias Ringwald }
26183deb3ec6SMatthias Ringwald
gatt_client_discover_characteristics_for_service_with_context(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_service_t * service,uint16_t service_id,uint16_t connection_id)26198183ac30SMatthias Ringwald uint8_t gatt_client_discover_characteristics_for_service_with_context(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_service_t * service,
26208183ac30SMatthias Ringwald uint16_t service_id, uint16_t connection_id){
262140faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2622de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
262340faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
262440faeb84SMilanka Ringwald return status;
262540faeb84SMilanka Ringwald }
26263deb3ec6SMatthias Ringwald
26275cf1669fSMatthias Ringwald gatt_client->callback = callback;
26288183ac30SMatthias Ringwald gatt_client->service_id = service_id;
26298183ac30SMatthias Ringwald gatt_client->connection_id = connection_id;
26305cf1669fSMatthias Ringwald gatt_client->start_group_handle = service->start_group_handle;
26315cf1669fSMatthias Ringwald gatt_client->end_group_handle = service->end_group_handle;
2632ab415e75SMatthias Ringwald gatt_client->filter_with_uuid = false;
26335cf1669fSMatthias Ringwald gatt_client->characteristic_start_handle = 0;
2634052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_ALL_CHARACTERISTICS_OF_SERVICE_QUERY;
26353deb3ec6SMatthias Ringwald gatt_client_run();
263625b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
26373deb3ec6SMatthias Ringwald }
26388183ac30SMatthias Ringwald
gatt_client_discover_characteristics_for_service(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_service_t * service)26398183ac30SMatthias Ringwald uint8_t gatt_client_discover_characteristics_for_service(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_service_t * service){
26408183ac30SMatthias Ringwald return gatt_client_discover_characteristics_for_service_with_context(callback, con_handle, service, 0, 0);
26418183ac30SMatthias Ringwald }
26428183ac30SMatthias Ringwald
gatt_client_find_included_services_for_service_with_context(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_service_t * service,uint16_t service_id,uint16_t connection_id)2643aa43543aSMatthias Ringwald uint8_t gatt_client_find_included_services_for_service_with_context(btstack_packet_handler_t callback, hci_con_handle_t con_handle,
2644aa43543aSMatthias Ringwald gatt_client_service_t * service, uint16_t service_id, uint16_t connection_id){
264540faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2646de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
264740faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
264840faeb84SMilanka Ringwald return status;
264940faeb84SMilanka Ringwald }
2650de27733dSMatthias Ringwald
26515cf1669fSMatthias Ringwald gatt_client->callback = callback;
2652aa43543aSMatthias Ringwald gatt_client->service_id = service_id;
2653aa43543aSMatthias Ringwald gatt_client->connection_id = connection_id;
26545cf1669fSMatthias Ringwald gatt_client->start_group_handle = service->start_group_handle;
26555cf1669fSMatthias Ringwald gatt_client->end_group_handle = service->end_group_handle;
2656052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_INCLUDED_SERVICE_QUERY;
26573deb3ec6SMatthias Ringwald
26583deb3ec6SMatthias Ringwald gatt_client_run();
265925b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
26603deb3ec6SMatthias Ringwald }
26613deb3ec6SMatthias Ringwald
gatt_client_find_included_services_for_service(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_service_t * service)2662aa43543aSMatthias Ringwald uint8_t gatt_client_find_included_services_for_service(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_service_t * service) {
2663aa43543aSMatthias Ringwald return gatt_client_find_included_services_for_service_with_context(callback, con_handle, service, 0, 0);
2664aa43543aSMatthias Ringwald }
2665aa43543aSMatthias Ringwald
gatt_client_discover_characteristics_for_handle_range_by_uuid16(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t start_handle,uint16_t end_handle,uint16_t uuid16)2666711e6c80SMatthias Ringwald uint8_t gatt_client_discover_characteristics_for_handle_range_by_uuid16(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t start_handle, uint16_t end_handle, uint16_t uuid16){
266740faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2668de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
266940faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
267040faeb84SMilanka Ringwald return status;
267140faeb84SMilanka Ringwald }
26723deb3ec6SMatthias Ringwald
26735cf1669fSMatthias Ringwald gatt_client->callback = callback;
26745cf1669fSMatthias Ringwald gatt_client->start_group_handle = start_handle;
26755cf1669fSMatthias Ringwald gatt_client->end_group_handle = end_handle;
2676ab415e75SMatthias Ringwald gatt_client->filter_with_uuid = true;
26775cf1669fSMatthias Ringwald gatt_client->uuid16 = uuid16;
26785cf1669fSMatthias Ringwald uuid_add_bluetooth_prefix((uint8_t*) &(gatt_client->uuid128), uuid16);
26795cf1669fSMatthias Ringwald gatt_client->characteristic_start_handle = 0;
2680052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_CHARACTERISTIC_WITH_UUID_QUERY;
26813deb3ec6SMatthias Ringwald gatt_client_run();
268225b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
26833deb3ec6SMatthias Ringwald }
26843deb3ec6SMatthias Ringwald
gatt_client_discover_characteristics_for_handle_range_by_uuid128(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t start_handle,uint16_t end_handle,const uint8_t * uuid128)2685045d700dSDavid Lechner uint8_t gatt_client_discover_characteristics_for_handle_range_by_uuid128(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128){
268640faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2687de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
268840faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
268940faeb84SMilanka Ringwald return status;
269040faeb84SMilanka Ringwald }
26913deb3ec6SMatthias Ringwald
26925cf1669fSMatthias Ringwald gatt_client->callback = callback;
26935cf1669fSMatthias Ringwald gatt_client->start_group_handle = start_handle;
26945cf1669fSMatthias Ringwald gatt_client->end_group_handle = end_handle;
2695ab415e75SMatthias Ringwald gatt_client->filter_with_uuid = true;
26965cf1669fSMatthias Ringwald gatt_client->uuid16 = 0;
26975cf1669fSMatthias Ringwald (void)memcpy(gatt_client->uuid128, uuid128, 16);
26985cf1669fSMatthias Ringwald gatt_client->characteristic_start_handle = 0;
2699052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_CHARACTERISTIC_WITH_UUID_QUERY;
27003deb3ec6SMatthias Ringwald gatt_client_run();
270125b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
27023deb3ec6SMatthias Ringwald }
27033deb3ec6SMatthias Ringwald
27043deb3ec6SMatthias Ringwald
gatt_client_discover_characteristics_for_service_by_uuid16(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_service_t * service,uint16_t uuid16)2705b45b7749SMilanka Ringwald uint8_t gatt_client_discover_characteristics_for_service_by_uuid16(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_service_t * service, uint16_t uuid16){
2706b45b7749SMilanka Ringwald return gatt_client_discover_characteristics_for_handle_range_by_uuid16(callback, con_handle, service->start_group_handle, service->end_group_handle, uuid16);
27073deb3ec6SMatthias Ringwald }
27083deb3ec6SMatthias Ringwald
gatt_client_discover_characteristics_for_service_by_uuid128(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_service_t * service,const uint8_t * uuid128)2709b45b7749SMilanka Ringwald uint8_t gatt_client_discover_characteristics_for_service_by_uuid128(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_service_t * service, const uint8_t * uuid128){
2710b45b7749SMilanka Ringwald return gatt_client_discover_characteristics_for_handle_range_by_uuid128(callback, con_handle, service->start_group_handle, service->end_group_handle, uuid128);
27113deb3ec6SMatthias Ringwald }
27123deb3ec6SMatthias Ringwald
gatt_client_discover_characteristic_descriptors_with_context(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_characteristic_t * characteristic,uint16_t service_id,uint16_t connection_id)2713a94d23eaSMatthias Ringwald uint8_t gatt_client_discover_characteristic_descriptors_with_context(btstack_packet_handler_t callback, hci_con_handle_t con_handle,
2714a94d23eaSMatthias Ringwald gatt_client_characteristic_t * characteristic, uint16_t service_id, uint16_t connection_id){
271540faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2716de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
271740faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
271840faeb84SMilanka Ringwald return status;
271940faeb84SMilanka Ringwald }
27203deb3ec6SMatthias Ringwald
2721a94d23eaSMatthias Ringwald gatt_client->service_id = service_id;
2722a94d23eaSMatthias Ringwald gatt_client->connection_id = connection_id;
2723a94d23eaSMatthias Ringwald
2724544a5845SMatthias Ringwald // check if there is space for characteristics descriptors
2725544a5845SMatthias Ringwald if (characteristic->end_handle > characteristic->value_handle){
27265cf1669fSMatthias Ringwald gatt_client->callback = callback;
27275cf1669fSMatthias Ringwald gatt_client->start_group_handle = characteristic->value_handle + 1u;
27285cf1669fSMatthias Ringwald gatt_client->end_group_handle = characteristic->end_handle;
2729052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY;
27303deb3ec6SMatthias Ringwald gatt_client_run();
2731544a5845SMatthias Ringwald } else {
2732544a5845SMatthias Ringwald // schedule gatt complete event on next run loop iteration otherwise
2733544a5845SMatthias Ringwald gatt_client->state = P_W2_EMIT_QUERY_COMPLETE_EVENT;
2734544a5845SMatthias Ringwald gatt_client_deferred_event_emit.callback = gatt_client_emit_events;
2735544a5845SMatthias Ringwald btstack_run_loop_execute_on_main_thread(&gatt_client_deferred_event_emit);
2736544a5845SMatthias Ringwald }
273725b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
27383deb3ec6SMatthias Ringwald }
27393deb3ec6SMatthias Ringwald
gatt_client_discover_characteristic_descriptors(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_characteristic_t * characteristic)2740a94d23eaSMatthias Ringwald uint8_t gatt_client_discover_characteristic_descriptors(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_t * characteristic){
2741a94d23eaSMatthias Ringwald return gatt_client_discover_characteristic_descriptors_with_context(callback, con_handle, characteristic, 0, 0);
2742a94d23eaSMatthias Ringwald }
2743a94d23eaSMatthias Ringwald
gatt_client_read_value_of_characteristic_using_value_handle_with_context(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t value_handle,uint16_t service_id,uint16_t connection_id)2744e38d764eSMatthias Ringwald uint8_t gatt_client_read_value_of_characteristic_using_value_handle_with_context(btstack_packet_handler_t callback,
2745e38d764eSMatthias Ringwald hci_con_handle_t con_handle,
2746e38d764eSMatthias Ringwald uint16_t value_handle,
2747e38d764eSMatthias Ringwald uint16_t service_id,
2748e38d764eSMatthias Ringwald uint16_t connection_id) {
274940faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2750de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
275140faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
275240faeb84SMilanka Ringwald return status;
275340faeb84SMilanka Ringwald }
27543deb3ec6SMatthias Ringwald
27555cf1669fSMatthias Ringwald gatt_client->callback = callback;
2756e38d764eSMatthias Ringwald gatt_client->service_id = service_id;
2757e38d764eSMatthias Ringwald gatt_client->connection_id = connection_id;
27585cf1669fSMatthias Ringwald gatt_client->attribute_handle = value_handle;
27595cf1669fSMatthias Ringwald gatt_client->attribute_offset = 0;
2760052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_CHARACTERISTIC_VALUE_QUERY;
27613deb3ec6SMatthias Ringwald gatt_client_run();
276225b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
27633deb3ec6SMatthias Ringwald }
27643deb3ec6SMatthias Ringwald
gatt_client_read_value_of_characteristic_using_value_handle(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t value_handle)2765e38d764eSMatthias Ringwald uint8_t gatt_client_read_value_of_characteristic_using_value_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle){
2766e38d764eSMatthias Ringwald return gatt_client_read_value_of_characteristic_using_value_handle_with_context(callback, con_handle, value_handle, 0, 0);
2767e38d764eSMatthias Ringwald
2768e38d764eSMatthias Ringwald }
2769e38d764eSMatthias Ringwald
gatt_client_read_value_of_characteristics_by_uuid16(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t start_handle,uint16_t end_handle,uint16_t uuid16)2770711e6c80SMatthias Ringwald uint8_t gatt_client_read_value_of_characteristics_by_uuid16(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t start_handle, uint16_t end_handle, uint16_t uuid16){
277140faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2772de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
277340faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
277440faeb84SMilanka Ringwald return status;
277540faeb84SMilanka Ringwald }
27763deb3ec6SMatthias Ringwald
27775cf1669fSMatthias Ringwald gatt_client->callback = callback;
27785cf1669fSMatthias Ringwald gatt_client->start_group_handle = start_handle;
27795cf1669fSMatthias Ringwald gatt_client->end_group_handle = end_handle;
27805cf1669fSMatthias Ringwald gatt_client->query_start_handle = start_handle;
27815cf1669fSMatthias Ringwald gatt_client->query_end_handle = end_handle;
27825cf1669fSMatthias Ringwald gatt_client->uuid16 = uuid16;
27835cf1669fSMatthias Ringwald uuid_add_bluetooth_prefix((uint8_t*) &(gatt_client->uuid128), uuid16);
2784052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_BY_TYPE_REQUEST;
27853deb3ec6SMatthias Ringwald gatt_client_run();
278625b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
27873deb3ec6SMatthias Ringwald }
27883deb3ec6SMatthias Ringwald
gatt_client_read_value_of_characteristics_by_uuid128(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t start_handle,uint16_t end_handle,const uint8_t * uuid128)2789045d700dSDavid Lechner uint8_t gatt_client_read_value_of_characteristics_by_uuid128(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128){
279040faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2791de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
279240faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
279340faeb84SMilanka Ringwald return status;
279440faeb84SMilanka Ringwald }
27953deb3ec6SMatthias Ringwald
27965cf1669fSMatthias Ringwald gatt_client->callback = callback;
27975cf1669fSMatthias Ringwald gatt_client->start_group_handle = start_handle;
27985cf1669fSMatthias Ringwald gatt_client->end_group_handle = end_handle;
27995cf1669fSMatthias Ringwald gatt_client->query_start_handle = start_handle;
28005cf1669fSMatthias Ringwald gatt_client->query_end_handle = end_handle;
28015cf1669fSMatthias Ringwald gatt_client->uuid16 = 0;
28025cf1669fSMatthias Ringwald (void)memcpy(gatt_client->uuid128, uuid128, 16);
2803052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_BY_TYPE_REQUEST;
28043deb3ec6SMatthias Ringwald gatt_client_run();
280525b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
28063deb3ec6SMatthias Ringwald }
28073deb3ec6SMatthias Ringwald
28083deb3ec6SMatthias Ringwald
gatt_client_read_value_of_characteristic(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_characteristic_t * characteristic)2809b45b7749SMilanka Ringwald uint8_t gatt_client_read_value_of_characteristic(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_t * characteristic){
2810b45b7749SMilanka Ringwald return gatt_client_read_value_of_characteristic_using_value_handle(callback, con_handle, characteristic->value_handle);
28113deb3ec6SMatthias Ringwald }
28123deb3ec6SMatthias Ringwald
gatt_client_read_long_value_of_characteristic_using_value_handle_with_offset(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t value_handle,uint16_t offset)2813b45b7749SMilanka Ringwald uint8_t gatt_client_read_long_value_of_characteristic_using_value_handle_with_offset(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t offset){
281440faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2815de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
281640faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
281740faeb84SMilanka Ringwald return status;
281840faeb84SMilanka Ringwald }
28193deb3ec6SMatthias Ringwald
28205cf1669fSMatthias Ringwald gatt_client->callback = callback;
2821b45b7749SMilanka Ringwald gatt_client->attribute_handle = value_handle;
28225cf1669fSMatthias Ringwald gatt_client->attribute_offset = offset;
2823052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_BLOB_QUERY;
28243deb3ec6SMatthias Ringwald gatt_client_run();
282525b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
28263deb3ec6SMatthias Ringwald }
gatt_client_read_long_value_of_characteristic_using_value_handle_with_context(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t value_handle,uint16_t service_id,uint16_t connection_id)2827691e02c2SMatthias Ringwald uint8_t gatt_client_read_long_value_of_characteristic_using_value_handle_with_context(btstack_packet_handler_t callback,
2828691e02c2SMatthias Ringwald hci_con_handle_t con_handle, uint16_t value_handle,
2829691e02c2SMatthias Ringwald uint16_t service_id, uint16_t connection_id){
2830691e02c2SMatthias Ringwald // TODO: move into gatt_client_read_long_value_of_characteristic_using_value_handle_with_offset once
2831691e02c2SMatthias Ringwald // gatt_client_read_long_value_of_characteristic_using_value_handle_with_offset_and_context exists
2832691e02c2SMatthias Ringwald gatt_client_t * gatt_client;
2833691e02c2SMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
2834691e02c2SMatthias Ringwald if (status != ERROR_CODE_SUCCESS){
2835691e02c2SMatthias Ringwald return status;
2836691e02c2SMatthias Ringwald }
2837691e02c2SMatthias Ringwald gatt_client->service_id = service_id;
2838691e02c2SMatthias Ringwald gatt_client->connection_id = connection_id;
2839691e02c2SMatthias Ringwald return gatt_client_read_long_value_of_characteristic_using_value_handle_with_offset(callback, con_handle, value_handle, 0);
2840691e02c2SMatthias Ringwald }
28413deb3ec6SMatthias Ringwald
gatt_client_read_long_value_of_characteristic_using_value_handle(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t value_handle)2842b45b7749SMilanka Ringwald uint8_t gatt_client_read_long_value_of_characteristic_using_value_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle){
2843691e02c2SMatthias Ringwald return gatt_client_read_long_value_of_characteristic_using_value_handle_with_context(callback, con_handle, value_handle, 0, 0);
28443deb3ec6SMatthias Ringwald }
28453deb3ec6SMatthias Ringwald
gatt_client_read_long_value_of_characteristic(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_characteristic_t * characteristic)2846b45b7749SMilanka Ringwald uint8_t gatt_client_read_long_value_of_characteristic(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_t * characteristic){
2847b45b7749SMilanka Ringwald return gatt_client_read_long_value_of_characteristic_using_value_handle(callback, con_handle, characteristic->value_handle);
28483deb3ec6SMatthias Ringwald }
28493deb3ec6SMatthias Ringwald
gatt_client_read_multiple_characteristic_values_with_state(btstack_packet_handler_t callback,hci_con_handle_t con_handle,int num_value_handles,uint16_t * value_handles,gatt_client_state_t state)2850f125a8efSMatthias Ringwald static uint8_t gatt_client_read_multiple_characteristic_values_with_state(btstack_packet_handler_t callback, hci_con_handle_t con_handle, int num_value_handles, uint16_t * value_handles, gatt_client_state_t state){
285140faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2852de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
285340faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
285440faeb84SMilanka Ringwald return status;
285540faeb84SMilanka Ringwald }
28563deb3ec6SMatthias Ringwald
28575cf1669fSMatthias Ringwald gatt_client->callback = callback;
28585cf1669fSMatthias Ringwald gatt_client->read_multiple_handle_count = num_value_handles;
28595cf1669fSMatthias Ringwald gatt_client->read_multiple_handles = value_handles;
2860052dc82aSMatthias Ringwald gatt_client->state = state;
28613deb3ec6SMatthias Ringwald gatt_client_run();
286225b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
28633deb3ec6SMatthias Ringwald }
28643deb3ec6SMatthias Ringwald
gatt_client_read_multiple_characteristic_values(btstack_packet_handler_t callback,hci_con_handle_t con_handle,int num_value_handles,uint16_t * value_handles)2865f125a8efSMatthias Ringwald uint8_t gatt_client_read_multiple_characteristic_values(btstack_packet_handler_t callback, hci_con_handle_t con_handle, int num_value_handles, uint16_t * value_handles){
2866f125a8efSMatthias Ringwald return gatt_client_read_multiple_characteristic_values_with_state(callback, con_handle, num_value_handles, value_handles, P_W2_SEND_READ_MULTIPLE_REQUEST);
2867f125a8efSMatthias Ringwald }
2868f125a8efSMatthias Ringwald
2869f125a8efSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
gatt_client_read_multiple_variable_characteristic_values(btstack_packet_handler_t callback,hci_con_handle_t con_handle,int num_value_handles,uint16_t * value_handles)2870f125a8efSMatthias Ringwald uint8_t gatt_client_read_multiple_variable_characteristic_values(btstack_packet_handler_t callback, hci_con_handle_t con_handle, int num_value_handles, uint16_t * value_handles){
2871f125a8efSMatthias Ringwald return gatt_client_read_multiple_characteristic_values_with_state(callback, con_handle, num_value_handles, value_handles, P_W2_SEND_READ_MULTIPLE_VARIABLE_REQUEST);
2872f125a8efSMatthias Ringwald }
2873f125a8efSMatthias Ringwald #endif
2874f125a8efSMatthias Ringwald
gatt_client_write_value_of_characteristic_without_response(hci_con_handle_t con_handle,uint16_t value_handle,uint16_t value_length,uint8_t * value)2875de9f8e94SMatthias Ringwald uint8_t gatt_client_write_value_of_characteristic_without_response(hci_con_handle_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value){
287640faeb84SMilanka Ringwald gatt_client_t * gatt_client;
287740faeb84SMilanka Ringwald uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
287840faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
287940faeb84SMilanka Ringwald return status;
288040faeb84SMilanka Ringwald }
28813deb3ec6SMatthias Ringwald
2882dda77937SMatthias Ringwald if (value_length > (gatt_client->mtu - 3u)) return GATT_CLIENT_VALUE_TOO_LONG;
28835cf1669fSMatthias Ringwald if (!att_dispatch_client_can_send_now(gatt_client->con_handle)) return GATT_CLIENT_BUSY;
28843deb3ec6SMatthias Ringwald
28856e7b444cSMatthias Ringwald return att_write_request(gatt_client, ATT_WRITE_COMMAND, value_handle, value_length, value);
28863deb3ec6SMatthias Ringwald }
gatt_client_write_value_of_characteristic_with_context(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t value_handle,uint16_t value_length,uint8_t * value,uint16_t service_id,uint16_t connection_id)2887b444aa75SMatthias Ringwald uint8_t gatt_client_write_value_of_characteristic_with_context(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle,
2888b444aa75SMatthias Ringwald uint16_t value_length, uint8_t * value, uint16_t service_id, uint16_t connection_id){
288940faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2890de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
289140faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
289240faeb84SMilanka Ringwald return status;
289340faeb84SMilanka Ringwald }
28943deb3ec6SMatthias Ringwald
28955cf1669fSMatthias Ringwald gatt_client->callback = callback;
2896b444aa75SMatthias Ringwald gatt_client->service_id = service_id;
2897b444aa75SMatthias Ringwald gatt_client->connection_id = connection_id;
28985cf1669fSMatthias Ringwald gatt_client->attribute_handle = value_handle;
28995cf1669fSMatthias Ringwald gatt_client->attribute_length = value_length;
2900b45b7749SMilanka Ringwald gatt_client->attribute_value = value;
2901052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_WRITE_CHARACTERISTIC_VALUE;
29023deb3ec6SMatthias Ringwald gatt_client_run();
290325b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
29043deb3ec6SMatthias Ringwald }
gatt_client_write_value_of_characteristic(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t value_handle,uint16_t value_length,uint8_t * value)2905b444aa75SMatthias Ringwald uint8_t gatt_client_write_value_of_characteristic(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value) {
2906b444aa75SMatthias Ringwald return gatt_client_write_value_of_characteristic_with_context(callback, con_handle, value_handle, value_length, value, 0, 0);
2907b444aa75SMatthias Ringwald }
29083deb3ec6SMatthias Ringwald
gatt_client_write_long_value_of_characteristic_with_offset(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t value_handle,uint16_t offset,uint16_t value_length,uint8_t * value)2909b45b7749SMilanka Ringwald uint8_t gatt_client_write_long_value_of_characteristic_with_offset(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t offset, uint16_t value_length, uint8_t * value){
291040faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2911de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
291240faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
291340faeb84SMilanka Ringwald return status;
291440faeb84SMilanka Ringwald }
29153deb3ec6SMatthias Ringwald
29165cf1669fSMatthias Ringwald gatt_client->callback = callback;
29175cf1669fSMatthias Ringwald gatt_client->attribute_handle = value_handle;
29185cf1669fSMatthias Ringwald gatt_client->attribute_length = value_length;
29195cf1669fSMatthias Ringwald gatt_client->attribute_offset = offset;
2920b45b7749SMilanka Ringwald gatt_client->attribute_value = value;
2921052dc82aSMatthias Ringwald gatt_client->state = P_W2_PREPARE_WRITE;
29223deb3ec6SMatthias Ringwald gatt_client_run();
292325b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
29243deb3ec6SMatthias Ringwald }
29253deb3ec6SMatthias Ringwald
gatt_client_write_long_value_of_characteristic_with_context(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t value_handle,uint16_t value_length,uint8_t * value,uint16_t service_id,uint16_t connection_id)2926b538fb89SMatthias Ringwald uint8_t gatt_client_write_long_value_of_characteristic_with_context(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value, uint16_t service_id, uint16_t connection_id){
2927b538fb89SMatthias Ringwald // TODO: move into gatt_client_write_long_value_of_characteristic_with_offset once gatt_client_write_long_value_of_characteristic_with_offset_with_context exists
2928b538fb89SMatthias Ringwald gatt_client_t * gatt_client;
2929b538fb89SMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
2930b538fb89SMatthias Ringwald if (status != ERROR_CODE_SUCCESS){
2931b538fb89SMatthias Ringwald return status;
2932b538fb89SMatthias Ringwald }
2933b538fb89SMatthias Ringwald gatt_client->service_id = service_id;
2934b538fb89SMatthias Ringwald gatt_client->connection_id = connection_id;
29359c662c9bSMatthias Ringwald return gatt_client_write_long_value_of_characteristic_with_offset(callback, con_handle, value_handle, 0, value_length, value);
29363deb3ec6SMatthias Ringwald }
29373deb3ec6SMatthias Ringwald
gatt_client_write_long_value_of_characteristic(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t value_handle,uint16_t value_length,uint8_t * value)2938b538fb89SMatthias Ringwald uint8_t gatt_client_write_long_value_of_characteristic(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value){
2939b538fb89SMatthias Ringwald return gatt_client_write_long_value_of_characteristic_with_context(callback, con_handle, value_handle, value_length, value, 0, 0);
2940b538fb89SMatthias Ringwald }
2941b538fb89SMatthias Ringwald
gatt_client_reliable_write_long_value_of_characteristic(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t value_handle,uint16_t value_length,uint8_t * value)2942711e6c80SMatthias Ringwald uint8_t gatt_client_reliable_write_long_value_of_characteristic(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value){
294340faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2944de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
294540faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
294640faeb84SMilanka Ringwald return status;
294740faeb84SMilanka Ringwald }
29483deb3ec6SMatthias Ringwald
29495cf1669fSMatthias Ringwald gatt_client->callback = callback;
29505cf1669fSMatthias Ringwald gatt_client->attribute_handle = value_handle;
29515cf1669fSMatthias Ringwald gatt_client->attribute_length = value_length;
29525cf1669fSMatthias Ringwald gatt_client->attribute_offset = 0;
29535cf1669fSMatthias Ringwald gatt_client->attribute_value = value;
2954052dc82aSMatthias Ringwald gatt_client->state = P_W2_PREPARE_RELIABLE_WRITE;
29553deb3ec6SMatthias Ringwald gatt_client_run();
295625b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
29573deb3ec6SMatthias Ringwald }
29583deb3ec6SMatthias Ringwald
gatt_client_write_client_characteristic_configuration_with_context(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_characteristic_t * characteristic,uint16_t configuration,uint16_t service_id,uint16_t connection_id)2959cb5b2b64SMatthias Ringwald uint8_t gatt_client_write_client_characteristic_configuration_with_context(btstack_packet_handler_t callback, hci_con_handle_t con_handle,
2960cb5b2b64SMatthias Ringwald gatt_client_characteristic_t * characteristic, uint16_t configuration, uint16_t service_id, uint16_t connection_id){
296140faeb84SMilanka Ringwald gatt_client_t * gatt_client;
2962de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
296340faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
296440faeb84SMilanka Ringwald return status;
296540faeb84SMilanka Ringwald }
29663deb3ec6SMatthias Ringwald
29678915696fSMilanka Ringwald if (configuration > 3){
29688915696fSMilanka Ringwald return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
29698915696fSMilanka Ringwald }
29708915696fSMilanka Ringwald
29713deb3ec6SMatthias Ringwald if ( (configuration & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION) &&
29724ea43905SMatthias Ringwald ((characteristic->properties & ATT_PROPERTY_NOTIFY) == 0u)) {
2973d8e8f12aSMatthias Ringwald log_info("gatt_client_write_client_characteristic_configuration: GATT_CLIENT_CHARACTERISTIC_NOTIFICATION_NOT_SUPPORTED");
2974616edd56SMatthias Ringwald return GATT_CLIENT_CHARACTERISTIC_NOTIFICATION_NOT_SUPPORTED;
29753deb3ec6SMatthias Ringwald } else if ( (configuration & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION) &&
29764ea43905SMatthias Ringwald ((characteristic->properties & ATT_PROPERTY_INDICATE) == 0u)){
2977d8e8f12aSMatthias Ringwald log_info("gatt_client_write_client_characteristic_configuration: GATT_CLIENT_CHARACTERISTIC_INDICATION_NOT_SUPPORTED");
2978616edd56SMatthias Ringwald return GATT_CLIENT_CHARACTERISTIC_INDICATION_NOT_SUPPORTED;
29793deb3ec6SMatthias Ringwald }
29803deb3ec6SMatthias Ringwald
29815cf1669fSMatthias Ringwald gatt_client->callback = callback;
2982cb5b2b64SMatthias Ringwald gatt_client->service_id = service_id;
2983cb5b2b64SMatthias Ringwald gatt_client->connection_id = connection_id;
29845cf1669fSMatthias Ringwald gatt_client->start_group_handle = characteristic->value_handle;
29855cf1669fSMatthias Ringwald gatt_client->end_group_handle = characteristic->end_handle;
29865cf1669fSMatthias Ringwald little_endian_store_16(gatt_client->client_characteristic_configuration_value, 0, configuration);
29873deb3ec6SMatthias Ringwald
2988*197b4dd3SMatthias Ringwald #ifdef ENABLE_GATT_FIND_INFORMATION_FOR_CCC_DISCOVERY
2989a9a6bd4cSMatthias Ringwald gatt_client->state = P_W2_SEND_FIND_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY;
2990*197b4dd3SMatthias Ringwald #else
2991*197b4dd3SMatthias Ringwald gatt_client->state = P_W2_SEND_READ_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY;
2992*197b4dd3SMatthias Ringwald #endif
29933deb3ec6SMatthias Ringwald gatt_client_run();
29949cb80b17SMilanka Ringwald return ERROR_CODE_SUCCESS;
29953deb3ec6SMatthias Ringwald }
29963deb3ec6SMatthias Ringwald
gatt_client_write_client_characteristic_configuration(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_characteristic_t * characteristic,uint16_t configuration)2997cb5b2b64SMatthias Ringwald uint8_t gatt_client_write_client_characteristic_configuration(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_t * characteristic, uint16_t configuration){
2998cb5b2b64SMatthias Ringwald return gatt_client_write_client_characteristic_configuration_with_context(callback, con_handle, characteristic, configuration, 0, 0);
2999cb5b2b64SMatthias Ringwald }
3000cb5b2b64SMatthias Ringwald
gatt_client_read_characteristic_descriptor_using_descriptor_handle(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t descriptor_handle)3001711e6c80SMatthias Ringwald uint8_t gatt_client_read_characteristic_descriptor_using_descriptor_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t descriptor_handle){
300240faeb84SMilanka Ringwald gatt_client_t * gatt_client;
3003de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
300440faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
300540faeb84SMilanka Ringwald return status;
300640faeb84SMilanka Ringwald }
30073deb3ec6SMatthias Ringwald
30085cf1669fSMatthias Ringwald gatt_client->callback = callback;
30095cf1669fSMatthias Ringwald gatt_client->attribute_handle = descriptor_handle;
30103deb3ec6SMatthias Ringwald
3011052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_CHARACTERISTIC_DESCRIPTOR_QUERY;
30123deb3ec6SMatthias Ringwald gatt_client_run();
301325b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
30143deb3ec6SMatthias Ringwald }
30153deb3ec6SMatthias Ringwald
gatt_client_read_characteristic_descriptor(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_characteristic_descriptor_t * descriptor)3016711e6c80SMatthias Ringwald uint8_t gatt_client_read_characteristic_descriptor(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_descriptor_t * descriptor){
30179c662c9bSMatthias Ringwald return gatt_client_read_characteristic_descriptor_using_descriptor_handle(callback, con_handle, descriptor->handle);
30183deb3ec6SMatthias Ringwald }
30193deb3ec6SMatthias Ringwald
gatt_client_read_long_characteristic_descriptor_using_descriptor_handle_with_offset(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t descriptor_handle,uint16_t offset)3020711e6c80SMatthias Ringwald uint8_t gatt_client_read_long_characteristic_descriptor_using_descriptor_handle_with_offset(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t descriptor_handle, uint16_t offset){
302140faeb84SMilanka Ringwald gatt_client_t * gatt_client;
3022de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
302340faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
302440faeb84SMilanka Ringwald return status;
302540faeb84SMilanka Ringwald }
30263deb3ec6SMatthias Ringwald
30275cf1669fSMatthias Ringwald gatt_client->callback = callback;
30285cf1669fSMatthias Ringwald gatt_client->attribute_handle = descriptor_handle;
30295cf1669fSMatthias Ringwald gatt_client->attribute_offset = offset;
3030052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_QUERY;
30313deb3ec6SMatthias Ringwald gatt_client_run();
303225b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
30333deb3ec6SMatthias Ringwald }
30343deb3ec6SMatthias Ringwald
gatt_client_read_long_characteristic_descriptor_using_descriptor_handle(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t descriptor_handle)3035711e6c80SMatthias Ringwald uint8_t gatt_client_read_long_characteristic_descriptor_using_descriptor_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t descriptor_handle){
30369c662c9bSMatthias Ringwald return gatt_client_read_long_characteristic_descriptor_using_descriptor_handle_with_offset(callback, con_handle, descriptor_handle, 0);
30373deb3ec6SMatthias Ringwald }
30383deb3ec6SMatthias Ringwald
gatt_client_read_long_characteristic_descriptor(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_characteristic_descriptor_t * descriptor)3039711e6c80SMatthias Ringwald uint8_t gatt_client_read_long_characteristic_descriptor(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_descriptor_t * descriptor){
30409c662c9bSMatthias Ringwald return gatt_client_read_long_characteristic_descriptor_using_descriptor_handle(callback, con_handle, descriptor->handle);
30413deb3ec6SMatthias Ringwald }
30423deb3ec6SMatthias Ringwald
gatt_client_write_characteristic_descriptor_using_descriptor_handle(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t descriptor_handle,uint16_t value_length,uint8_t * value)3043b45b7749SMilanka Ringwald uint8_t gatt_client_write_characteristic_descriptor_using_descriptor_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t descriptor_handle, uint16_t value_length, uint8_t * value){
304440faeb84SMilanka Ringwald gatt_client_t * gatt_client;
3045de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
304640faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
304740faeb84SMilanka Ringwald return status;
304840faeb84SMilanka Ringwald }
30493deb3ec6SMatthias Ringwald
30505cf1669fSMatthias Ringwald gatt_client->callback = callback;
30515cf1669fSMatthias Ringwald gatt_client->attribute_handle = descriptor_handle;
3052b45b7749SMilanka Ringwald gatt_client->attribute_length = value_length;
30535cf1669fSMatthias Ringwald gatt_client->attribute_offset = 0;
3054b45b7749SMilanka Ringwald gatt_client->attribute_value = value;
3055052dc82aSMatthias Ringwald gatt_client->state = P_W2_SEND_WRITE_CHARACTERISTIC_DESCRIPTOR;
30563deb3ec6SMatthias Ringwald gatt_client_run();
305725b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
30583deb3ec6SMatthias Ringwald }
30593deb3ec6SMatthias Ringwald
gatt_client_write_characteristic_descriptor(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_characteristic_descriptor_t * descriptor,uint16_t value_length,uint8_t * value)306048cdff9cSMilanka Ringwald uint8_t gatt_client_write_characteristic_descriptor(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_descriptor_t * descriptor, uint16_t value_length, uint8_t * value){
306148cdff9cSMilanka Ringwald return gatt_client_write_characteristic_descriptor_using_descriptor_handle(callback, con_handle, descriptor->handle, value_length, value);
30623deb3ec6SMatthias Ringwald }
30633deb3ec6SMatthias Ringwald
gatt_client_write_long_characteristic_descriptor_using_descriptor_handle_with_offset(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t descriptor_handle,uint16_t offset,uint16_t value_length,uint8_t * value)3064b45b7749SMilanka Ringwald uint8_t gatt_client_write_long_characteristic_descriptor_using_descriptor_handle_with_offset(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t descriptor_handle, uint16_t offset, uint16_t value_length, uint8_t * value){
306540faeb84SMilanka Ringwald gatt_client_t * gatt_client;
3066de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
306740faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
306840faeb84SMilanka Ringwald return status;
306940faeb84SMilanka Ringwald }
30703deb3ec6SMatthias Ringwald
30715cf1669fSMatthias Ringwald gatt_client->callback = callback;
30725cf1669fSMatthias Ringwald gatt_client->attribute_handle = descriptor_handle;
3073b45b7749SMilanka Ringwald gatt_client->attribute_length = value_length;
30745cf1669fSMatthias Ringwald gatt_client->attribute_offset = offset;
3075b45b7749SMilanka Ringwald gatt_client->attribute_value = value;
3076052dc82aSMatthias Ringwald gatt_client->state = P_W2_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR;
30773deb3ec6SMatthias Ringwald gatt_client_run();
307825b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
30793deb3ec6SMatthias Ringwald }
30803deb3ec6SMatthias Ringwald
gatt_client_write_long_characteristic_descriptor_using_descriptor_handle(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t descriptor_handle,uint16_t value_length,uint8_t * value)3081b45b7749SMilanka Ringwald uint8_t gatt_client_write_long_characteristic_descriptor_using_descriptor_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t descriptor_handle, uint16_t value_length, uint8_t * value){
3082b45b7749SMilanka Ringwald return gatt_client_write_long_characteristic_descriptor_using_descriptor_handle_with_offset(callback, con_handle, descriptor_handle, 0, value_length, value);
30833deb3ec6SMatthias Ringwald }
30843deb3ec6SMatthias Ringwald
gatt_client_write_long_characteristic_descriptor(btstack_packet_handler_t callback,hci_con_handle_t con_handle,gatt_client_characteristic_descriptor_t * descriptor,uint16_t value_length,uint8_t * value)3085b45b7749SMilanka Ringwald uint8_t gatt_client_write_long_characteristic_descriptor(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_descriptor_t * descriptor, uint16_t value_length, uint8_t * value){
3086b45b7749SMilanka Ringwald return gatt_client_write_long_characteristic_descriptor_using_descriptor_handle(callback, con_handle, descriptor->handle, value_length, value);
30873deb3ec6SMatthias Ringwald }
30883deb3ec6SMatthias Ringwald
30893deb3ec6SMatthias Ringwald /**
30903deb3ec6SMatthias Ringwald * @brief -> gatt complete event
30913deb3ec6SMatthias Ringwald */
gatt_client_prepare_write(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint16_t attribute_handle,uint16_t offset,uint16_t value_length,uint8_t * value)3092b45b7749SMilanka Ringwald uint8_t gatt_client_prepare_write(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint16_t value_length, uint8_t * value){
309340faeb84SMilanka Ringwald gatt_client_t * gatt_client;
3094de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
309540faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
309640faeb84SMilanka Ringwald return status;
309740faeb84SMilanka Ringwald }
30983deb3ec6SMatthias Ringwald
30995cf1669fSMatthias Ringwald gatt_client->callback = callback;
31005cf1669fSMatthias Ringwald gatt_client->attribute_handle = attribute_handle;
3101b45b7749SMilanka Ringwald gatt_client->attribute_length = value_length;
31025cf1669fSMatthias Ringwald gatt_client->attribute_offset = offset;
3103b45b7749SMilanka Ringwald gatt_client->attribute_value = value;
3104052dc82aSMatthias Ringwald gatt_client->state = P_W2_PREPARE_WRITE_SINGLE;
31053deb3ec6SMatthias Ringwald gatt_client_run();
310625b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
31073deb3ec6SMatthias Ringwald }
31083deb3ec6SMatthias Ringwald
31093deb3ec6SMatthias Ringwald /**
31103deb3ec6SMatthias Ringwald * @brief -> gatt complete event
31113deb3ec6SMatthias Ringwald */
gatt_client_execute_write(btstack_packet_handler_t callback,hci_con_handle_t con_handle)3112711e6c80SMatthias Ringwald uint8_t gatt_client_execute_write(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
311340faeb84SMilanka Ringwald gatt_client_t * gatt_client;
3114de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
311540faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
311640faeb84SMilanka Ringwald return status;
311740faeb84SMilanka Ringwald }
31183deb3ec6SMatthias Ringwald
31195cf1669fSMatthias Ringwald gatt_client->callback = callback;
3120052dc82aSMatthias Ringwald gatt_client->state = P_W2_EXECUTE_PREPARED_WRITE;
31213deb3ec6SMatthias Ringwald gatt_client_run();
312225b7c058SMilanka Ringwald return ERROR_CODE_SUCCESS;
31233deb3ec6SMatthias Ringwald }
31243deb3ec6SMatthias Ringwald
31253deb3ec6SMatthias Ringwald /**
31263deb3ec6SMatthias Ringwald * @brief -> gatt complete event
31273deb3ec6SMatthias Ringwald */
gatt_client_cancel_write(btstack_packet_handler_t callback,hci_con_handle_t con_handle)3128711e6c80SMatthias Ringwald uint8_t gatt_client_cancel_write(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
312940faeb84SMilanka Ringwald gatt_client_t * gatt_client;
3130de27733dSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
313140faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
313240faeb84SMilanka Ringwald return status;
313340faeb84SMilanka Ringwald }
31343deb3ec6SMatthias Ringwald
31355cf1669fSMatthias Ringwald gatt_client->callback = callback;
3136052dc82aSMatthias Ringwald gatt_client->state = P_W2_CANCEL_PREPARED_WRITE;
31373deb3ec6SMatthias Ringwald gatt_client_run();
31387d2258b3SMilanka Ringwald return ERROR_CODE_SUCCESS;
31393deb3ec6SMatthias Ringwald }
31403deb3ec6SMatthias Ringwald
gatt_client_deserialize_service(const uint8_t * packet,int offset,gatt_client_service_t * service)3141313e337bSMatthias Ringwald void gatt_client_deserialize_service(const uint8_t *packet, int offset, gatt_client_service_t * service){
31426ba2ad22SMatthias Ringwald service->start_group_handle = little_endian_read_16(packet, offset);
31436ba2ad22SMatthias Ringwald service->end_group_handle = little_endian_read_16(packet, offset + 2);
31446ba2ad22SMatthias Ringwald reverse_128(&packet[offset + 4], service->uuid128);
31456ba2ad22SMatthias Ringwald if (uuid_has_bluetooth_prefix(service->uuid128)){
31466ba2ad22SMatthias Ringwald service->uuid16 = big_endian_read_32(service->uuid128, 0);
3147c839c6f9SMatthias Ringwald } else {
3148c839c6f9SMatthias Ringwald service->uuid16 = 0;
31496ba2ad22SMatthias Ringwald }
31506ba2ad22SMatthias Ringwald }
31516ba2ad22SMatthias Ringwald
gatt_client_deserialize_characteristic(const uint8_t * packet,int offset,gatt_client_characteristic_t * characteristic)3152313e337bSMatthias Ringwald void gatt_client_deserialize_characteristic(const uint8_t * packet, int offset, gatt_client_characteristic_t * characteristic){
31536ba2ad22SMatthias Ringwald characteristic->start_handle = little_endian_read_16(packet, offset);
31546ba2ad22SMatthias Ringwald characteristic->value_handle = little_endian_read_16(packet, offset + 2);
31556ba2ad22SMatthias Ringwald characteristic->end_handle = little_endian_read_16(packet, offset + 4);
31566ba2ad22SMatthias Ringwald characteristic->properties = little_endian_read_16(packet, offset + 6);
315786c38559SMatthias Ringwald reverse_128(&packet[offset+8], characteristic->uuid128);
31586ba2ad22SMatthias Ringwald if (uuid_has_bluetooth_prefix(characteristic->uuid128)){
31596ba2ad22SMatthias Ringwald characteristic->uuid16 = big_endian_read_32(characteristic->uuid128, 0);
3160c839c6f9SMatthias Ringwald } else {
3161c839c6f9SMatthias Ringwald characteristic->uuid16 = 0;
31626ba2ad22SMatthias Ringwald }
31636ba2ad22SMatthias Ringwald }
31646ba2ad22SMatthias Ringwald
gatt_client_deserialize_characteristic_descriptor(const uint8_t * packet,int offset,gatt_client_characteristic_descriptor_t * descriptor)3165313e337bSMatthias Ringwald void gatt_client_deserialize_characteristic_descriptor(const uint8_t * packet, int offset, gatt_client_characteristic_descriptor_t * descriptor){
31666ba2ad22SMatthias Ringwald descriptor->handle = little_endian_read_16(packet, offset);
31676ba2ad22SMatthias Ringwald reverse_128(&packet[offset+2], descriptor->uuid128);
3168b4895529SJakob Krantz if (uuid_has_bluetooth_prefix(descriptor->uuid128)){
3169b4895529SJakob Krantz descriptor->uuid16 = big_endian_read_32(descriptor->uuid128, 0);
3170c839c6f9SMatthias Ringwald } else {
3171c839c6f9SMatthias Ringwald descriptor->uuid16 = 0;
3172b4895529SJakob Krantz }
31736ba2ad22SMatthias Ringwald }
31745cf6c434SJakob Krantz
gatt_client_send_mtu_negotiation(btstack_packet_handler_t callback,hci_con_handle_t con_handle)31755cf6c434SJakob Krantz void gatt_client_send_mtu_negotiation(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
317640faeb84SMilanka Ringwald gatt_client_t * gatt_client;
317740faeb84SMilanka Ringwald uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
317840faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
317940faeb84SMilanka Ringwald return;
318040faeb84SMilanka Ringwald }
318140faeb84SMilanka Ringwald if (gatt_client->mtu_state == MTU_AUTO_EXCHANGE_DISABLED){
318240faeb84SMilanka Ringwald gatt_client->callback = callback;
318340faeb84SMilanka Ringwald gatt_client->mtu_state = SEND_MTU_EXCHANGE;
31845cf6c434SJakob Krantz gatt_client_run();
31855cf6c434SJakob Krantz }
31865cf6c434SJakob Krantz }
318747181045SMatthias Ringwald
gatt_client_request_to_write_without_response(btstack_context_callback_registration_t * callback_registration,hci_con_handle_t con_handle)318859d34cd2SMatthias Ringwald uint8_t gatt_client_request_to_write_without_response(btstack_context_callback_registration_t * callback_registration, hci_con_handle_t con_handle){
318959d34cd2SMatthias Ringwald gatt_client_t * gatt_client;
319059d34cd2SMatthias Ringwald uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
319159d34cd2SMatthias Ringwald if (status != ERROR_CODE_SUCCESS){
319259d34cd2SMatthias Ringwald return status;
319359d34cd2SMatthias Ringwald }
319459d34cd2SMatthias Ringwald bool added = btstack_linked_list_add_tail(&gatt_client->write_without_response_requests, (btstack_linked_item_t*) callback_registration);
31952832a98aSMatthias Ringwald if (added == false){
319659d34cd2SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
31972832a98aSMatthias Ringwald } else {
31982832a98aSMatthias Ringwald att_dispatch_client_request_can_send_now_event(gatt_client->con_handle);
31992832a98aSMatthias Ringwald return ERROR_CODE_SUCCESS;
320059d34cd2SMatthias Ringwald }
320159d34cd2SMatthias Ringwald }
320259d34cd2SMatthias Ringwald
gatt_client_request_to_send_gatt_query(btstack_context_callback_registration_t * callback_registration,hci_con_handle_t con_handle)320353e9c18fSMatthias Ringwald uint8_t gatt_client_request_to_send_gatt_query(btstack_context_callback_registration_t * callback_registration, hci_con_handle_t con_handle){
320453e9c18fSMatthias Ringwald gatt_client_t * gatt_client;
320553e9c18fSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
320653e9c18fSMatthias Ringwald if (status != ERROR_CODE_SUCCESS){
320753e9c18fSMatthias Ringwald return status;
320853e9c18fSMatthias Ringwald }
320953e9c18fSMatthias Ringwald bool added = btstack_linked_list_add_tail(&gatt_client->query_requests, (btstack_linked_item_t*) callback_registration);
32102832a98aSMatthias Ringwald if (added == false){
321153e9c18fSMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
32122832a98aSMatthias Ringwald } else {
32132832a98aSMatthias Ringwald gatt_client_notify_can_send_query(gatt_client);
32142832a98aSMatthias Ringwald return ERROR_CODE_SUCCESS;
321553e9c18fSMatthias Ringwald }
321653e9c18fSMatthias Ringwald }
321753e9c18fSMatthias Ringwald
gatt_client_remove_gatt_query(btstack_context_callback_registration_t * callback_registration,hci_con_handle_t con_handle)321848553f67SDirk Helbig uint8_t gatt_client_remove_gatt_query(btstack_context_callback_registration_t * callback_registration, hci_con_handle_t con_handle){
321948553f67SDirk Helbig gatt_client_t * gatt_client;
322048553f67SDirk Helbig uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
322148553f67SDirk Helbig if (status != ERROR_CODE_SUCCESS){
322248553f67SDirk Helbig return status;
322348553f67SDirk Helbig }
322448553f67SDirk Helbig (void)btstack_linked_list_remove(&gatt_client->query_requests, (btstack_linked_item_t*) callback_registration);
322548553f67SDirk Helbig return ERROR_CODE_SUCCESS;
322648553f67SDirk Helbig }
322748553f67SDirk Helbig
gatt_client_request_can_write_without_response_event(btstack_packet_handler_t callback,hci_con_handle_t con_handle)322847181045SMatthias Ringwald uint8_t gatt_client_request_can_write_without_response_event(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
322940faeb84SMilanka Ringwald gatt_client_t * gatt_client;
323040faeb84SMilanka Ringwald uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
323140faeb84SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
323240faeb84SMilanka Ringwald return status;
323340faeb84SMilanka Ringwald }
323440faeb84SMilanka Ringwald if (gatt_client->write_without_response_callback != NULL){
323540faeb84SMilanka Ringwald return GATT_CLIENT_IN_WRONG_STATE;
323640faeb84SMilanka Ringwald }
323740faeb84SMilanka Ringwald gatt_client->write_without_response_callback = callback;
323840faeb84SMilanka Ringwald att_dispatch_client_request_can_send_now_event(gatt_client->con_handle);
32397d2258b3SMilanka Ringwald return ERROR_CODE_SUCCESS;
324047181045SMatthias Ringwald }
3241842492f0SMatthias Ringwald
gatt_client_att_status_to_error_code(uint8_t att_error_code)3242e80543a7SMatthias Ringwald uint8_t gatt_client_att_status_to_error_code(uint8_t att_error_code){
3243e80543a7SMatthias Ringwald switch (att_error_code){
3244e80543a7SMatthias Ringwald case ATT_ERROR_SUCCESS:
3245e80543a7SMatthias Ringwald return ERROR_CODE_SUCCESS;
3246e80543a7SMatthias Ringwald case ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH:
3247e80543a7SMatthias Ringwald return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
3248e80543a7SMatthias Ringwald default:
3249e80543a7SMatthias Ringwald log_info("ATT ERROR 0x%02x mapped to ERROR_CODE_UNSPECIFIED_ERROR", att_error_code);
3250e80543a7SMatthias Ringwald return ERROR_CODE_UNSPECIFIED_ERROR;
3251e80543a7SMatthias Ringwald }
3252e80543a7SMatthias Ringwald }
3253e80543a7SMatthias Ringwald
3254842492f0SMatthias Ringwald #ifdef ENABLE_GATT_CLIENT_SERVICE_CHANGED
gatt_client_add_service_changed_handler(btstack_packet_callback_registration_t * callback)325558e8c9f5SMatthias Ringwald void gatt_client_add_service_changed_handler(btstack_packet_callback_registration_t * callback) {
325658e8c9f5SMatthias Ringwald btstack_linked_list_add_tail(&gatt_client_service_changed_handler, (btstack_linked_item_t*) callback);
325758e8c9f5SMatthias Ringwald }
3258a6121b51SMilanka Ringwald
gatt_client_remove_service_changed_handler(btstack_packet_callback_registration_t * callback)325958e8c9f5SMatthias Ringwald void gatt_client_remove_service_changed_handler(btstack_packet_callback_registration_t * callback){
326058e8c9f5SMatthias Ringwald btstack_linked_list_remove(&gatt_client_service_changed_handler, (btstack_linked_item_t*) callback);
326158e8c9f5SMatthias Ringwald }
3262842492f0SMatthias Ringwald #endif
32637627a0deSMatthias Ringwald
32647627a0deSMatthias Ringwald #if defined(ENABLE_GATT_OVER_CLASSIC) || defined(ENABLE_GATT_OVER_EATT)
32651450cdc6SMatthias Ringwald
32661450cdc6SMatthias Ringwald #include "hci_event.h"
32671450cdc6SMatthias Ringwald
32681450cdc6SMatthias Ringwald static const hci_event_t gatt_client_connected = {
3269b2d70d58SMatthias Ringwald GATT_EVENT_CONNECTED, 0, "11BH"
32701450cdc6SMatthias Ringwald };
32711450cdc6SMatthias Ringwald
32726b4a68c3SMatthias Ringwald static const hci_event_t gatt_client_disconnected = {
32736b4a68c3SMatthias Ringwald GATT_EVENT_DISCONNECTED, 0, "H"
32746b4a68c3SMatthias Ringwald };
32751c7a19feSMatthias Ringwald
3276b2d70d58SMatthias Ringwald static void
gatt_client_emit_connected(btstack_packet_handler_t callback,uint8_t status,bd_addr_type_t addr_type,bd_addr_t addr,hci_con_handle_t con_handle)3277b2d70d58SMatthias Ringwald gatt_client_emit_connected(btstack_packet_handler_t callback, uint8_t status, bd_addr_type_t addr_type, bd_addr_t addr,
32781c7a19feSMatthias Ringwald hci_con_handle_t con_handle) {
32791c7a19feSMatthias Ringwald uint8_t buffer[20];
3280f2bbac44SMatthias Ringwald uint16_t len = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &gatt_client_connected, status, addr_type, addr, con_handle);
32811c7a19feSMatthias Ringwald (*callback)(HCI_EVENT_PACKET, 0, buffer, len);
32821c7a19feSMatthias Ringwald }
32831c7a19feSMatthias Ringwald
32847627a0deSMatthias Ringwald #endif
32857627a0deSMatthias Ringwald
32867627a0deSMatthias Ringwald #ifdef ENABLE_GATT_OVER_CLASSIC
32877627a0deSMatthias Ringwald
32887627a0deSMatthias Ringwald #include "bluetooth_psm.h"
32897627a0deSMatthias Ringwald
32907627a0deSMatthias Ringwald // single active SDP query
32917627a0deSMatthias Ringwald static gatt_client_t * gatt_client_classic_active_sdp_query;
32927627a0deSMatthias Ringwald
32937627a0deSMatthias Ringwald // macos protocol descriptor list requires 16 bytes
32947627a0deSMatthias Ringwald static uint8_t gatt_client_classic_sdp_buffer[32];
32957627a0deSMatthias Ringwald
32966b4a68c3SMatthias Ringwald
gatt_client_get_context_for_classic_addr(bd_addr_t addr)32971450cdc6SMatthias Ringwald static gatt_client_t * gatt_client_get_context_for_classic_addr(bd_addr_t addr){
32981450cdc6SMatthias Ringwald btstack_linked_item_t *it;
32991450cdc6SMatthias Ringwald for (it = (btstack_linked_item_t *) gatt_client_connections; it != NULL; it = it->next){
33001450cdc6SMatthias Ringwald gatt_client_t * gatt_client = (gatt_client_t *) it;
33011450cdc6SMatthias Ringwald if (memcmp(gatt_client->addr, addr, 6) == 0){
33021450cdc6SMatthias Ringwald return gatt_client;
33031450cdc6SMatthias Ringwald }
33041450cdc6SMatthias Ringwald }
33051450cdc6SMatthias Ringwald return NULL;
33061450cdc6SMatthias Ringwald }
33071450cdc6SMatthias Ringwald
gatt_client_get_context_for_l2cap_cid(uint16_t l2cap_cid)33081450cdc6SMatthias Ringwald static gatt_client_t * gatt_client_get_context_for_l2cap_cid(uint16_t l2cap_cid){
33091450cdc6SMatthias Ringwald btstack_linked_item_t *it;
33101450cdc6SMatthias Ringwald for (it = (btstack_linked_item_t *) gatt_client_connections; it != NULL; it = it->next){
33111450cdc6SMatthias Ringwald gatt_client_t * gatt_client = (gatt_client_t *) it;
33121450cdc6SMatthias Ringwald if (gatt_client->l2cap_cid == l2cap_cid){
33131450cdc6SMatthias Ringwald return gatt_client;
33141450cdc6SMatthias Ringwald }
33151450cdc6SMatthias Ringwald }
33161450cdc6SMatthias Ringwald return NULL;
33171450cdc6SMatthias Ringwald }
33181450cdc6SMatthias Ringwald
gatt_client_classic_handle_connected(gatt_client_t * gatt_client,uint8_t status)33191450cdc6SMatthias Ringwald static void gatt_client_classic_handle_connected(gatt_client_t * gatt_client, uint8_t status){
3320b2d70d58SMatthias Ringwald // cache peer information
33211450cdc6SMatthias Ringwald bd_addr_t addr;
3322d5529700SMatthias Ringwald // cppcheck-suppress uninitvar ; addr is reported as uninitialized although it's the destination of the memcpy
33231450cdc6SMatthias Ringwald memcpy(addr, gatt_client->addr, 6);
3324b2d70d58SMatthias Ringwald bd_addr_type_t addr_type = gatt_client->addr_type;
3325b2d70d58SMatthias Ringwald gatt_client->addr_type = BD_ADDR_TYPE_ACL;
33261450cdc6SMatthias Ringwald hci_con_handle_t con_handle = gatt_client->con_handle;
33271450cdc6SMatthias Ringwald btstack_packet_handler_t callback = gatt_client->callback;
3328b2d70d58SMatthias Ringwald
33299ed6f53cSMatthias Ringwald if (status != ERROR_CODE_SUCCESS){
33303b0e0cc5SMatthias Ringwald btstack_linked_list_remove(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
33313b0e0cc5SMatthias Ringwald btstack_memory_gatt_client_free(gatt_client);
33323b0e0cc5SMatthias Ringwald }
3333b2d70d58SMatthias Ringwald
3334b2d70d58SMatthias Ringwald gatt_client_emit_connected(callback, status, addr_type, addr, con_handle);
33351450cdc6SMatthias Ringwald }
33361450cdc6SMatthias Ringwald
gatt_client_classic_retry(btstack_timer_source_t * ts)3337180cbe79SMatthias Ringwald static void gatt_client_classic_retry(btstack_timer_source_t * ts){
3338180cbe79SMatthias Ringwald gatt_client_t * gatt_client = gatt_client_for_timer(ts);
3339180cbe79SMatthias Ringwald if (gatt_client != NULL){
3340180cbe79SMatthias Ringwald gatt_client->state = P_W4_L2CAP_CONNECTION;
3341180cbe79SMatthias Ringwald att_dispatch_classic_connect(gatt_client->addr, gatt_client->l2cap_psm, &gatt_client->l2cap_cid);
3342180cbe79SMatthias Ringwald }
3343180cbe79SMatthias Ringwald }
3344180cbe79SMatthias Ringwald
gatt_client_classic_handle_disconnected(gatt_client_t * gatt_client)33456b4a68c3SMatthias Ringwald static void gatt_client_classic_handle_disconnected(gatt_client_t * gatt_client){
33466b4a68c3SMatthias Ringwald
33476b4a68c3SMatthias Ringwald gatt_client_report_error_if_pending(gatt_client, ATT_ERROR_HCI_DISCONNECT_RECEIVED);
33486b4a68c3SMatthias Ringwald gatt_client_timeout_stop(gatt_client);
33496b4a68c3SMatthias Ringwald
33506b4a68c3SMatthias Ringwald hci_con_handle_t con_handle = gatt_client->con_handle;
33516b4a68c3SMatthias Ringwald btstack_packet_handler_t callback = gatt_client->callback;
33526b4a68c3SMatthias Ringwald btstack_linked_list_remove(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
33536b4a68c3SMatthias Ringwald btstack_memory_gatt_client_free(gatt_client);
33546b4a68c3SMatthias Ringwald
33556b4a68c3SMatthias Ringwald uint8_t buffer[20];
33566b4a68c3SMatthias Ringwald uint16_t len = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &gatt_client_disconnected, con_handle);
33576b4a68c3SMatthias Ringwald (*callback)(HCI_EVENT_PACKET, 0, buffer, len);
33586b4a68c3SMatthias Ringwald }
33596b4a68c3SMatthias Ringwald
gatt_client_handle_sdp_client_query_attribute_value(gatt_client_t * connection,uint8_t * packet)33601450cdc6SMatthias Ringwald static void gatt_client_handle_sdp_client_query_attribute_value(gatt_client_t * connection, uint8_t *packet){
33611450cdc6SMatthias Ringwald des_iterator_t des_list_it;
33621450cdc6SMatthias Ringwald des_iterator_t prot_it;
33631450cdc6SMatthias Ringwald
33641450cdc6SMatthias Ringwald if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= sizeof(gatt_client_classic_sdp_buffer)) {
33651450cdc6SMatthias Ringwald gatt_client_classic_sdp_buffer[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet);
33661450cdc6SMatthias Ringwald if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) {
33671450cdc6SMatthias Ringwald switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) {
33681450cdc6SMatthias Ringwald case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
33691450cdc6SMatthias Ringwald for (des_iterator_init(&des_list_it, gatt_client_classic_sdp_buffer); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) {
33701450cdc6SMatthias Ringwald uint8_t *des_element;
33711450cdc6SMatthias Ringwald uint8_t *element;
33721450cdc6SMatthias Ringwald uint32_t uuid;
33731450cdc6SMatthias Ringwald
33741450cdc6SMatthias Ringwald if (des_iterator_get_type(&des_list_it) != DE_DES) continue;
33751450cdc6SMatthias Ringwald
33761450cdc6SMatthias Ringwald des_element = des_iterator_get_element(&des_list_it);
33771450cdc6SMatthias Ringwald des_iterator_init(&prot_it, des_element);
33781450cdc6SMatthias Ringwald element = des_iterator_get_element(&prot_it);
33791450cdc6SMatthias Ringwald
33801450cdc6SMatthias Ringwald if (de_get_element_type(element) != DE_UUID) continue;
33811450cdc6SMatthias Ringwald
33821450cdc6SMatthias Ringwald uuid = de_get_uuid32(element);
33831450cdc6SMatthias Ringwald des_iterator_next(&prot_it);
33841450cdc6SMatthias Ringwald // we assume that the even if there are both roles supported, remote device uses the same psm and avdtp version for both
33851450cdc6SMatthias Ringwald switch (uuid){
33861450cdc6SMatthias Ringwald case BLUETOOTH_PROTOCOL_L2CAP:
33871450cdc6SMatthias Ringwald if (!des_iterator_has_more(&prot_it)) continue;
33881450cdc6SMatthias Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &connection->l2cap_psm);
33891450cdc6SMatthias Ringwald break;
33901450cdc6SMatthias Ringwald default:
33911450cdc6SMatthias Ringwald break;
33921450cdc6SMatthias Ringwald }
33931450cdc6SMatthias Ringwald }
33941450cdc6SMatthias Ringwald break;
33951450cdc6SMatthias Ringwald
33961450cdc6SMatthias Ringwald default:
33971450cdc6SMatthias Ringwald break;
33981450cdc6SMatthias Ringwald }
33991450cdc6SMatthias Ringwald }
34001450cdc6SMatthias Ringwald }
34011450cdc6SMatthias Ringwald }
34021450cdc6SMatthias Ringwald
gatt_client_classic_sdp_handler(uint8_t packet_type,uint16_t handle,uint8_t * packet,uint16_t size)34031450cdc6SMatthias Ringwald static void gatt_client_classic_sdp_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size){
34041450cdc6SMatthias Ringwald gatt_client_t * gatt_client = gatt_client_classic_active_sdp_query;
34051450cdc6SMatthias Ringwald btstack_assert(gatt_client != NULL);
34061450cdc6SMatthias Ringwald uint8_t status;
34071450cdc6SMatthias Ringwald
34081450cdc6SMatthias Ringwald // TODO: handle sdp events, get l2cap psm
34091450cdc6SMatthias Ringwald switch (hci_event_packet_get_type(packet)){
34101450cdc6SMatthias Ringwald case SDP_EVENT_QUERY_ATTRIBUTE_VALUE:
34111450cdc6SMatthias Ringwald gatt_client_handle_sdp_client_query_attribute_value(gatt_client, packet);
34121450cdc6SMatthias Ringwald // TODO:
34131450cdc6SMatthias Ringwald return;
34141450cdc6SMatthias Ringwald case SDP_EVENT_QUERY_COMPLETE:
34151450cdc6SMatthias Ringwald status = sdp_event_query_complete_get_status(packet);
34161450cdc6SMatthias Ringwald gatt_client_classic_active_sdp_query = NULL;
34171450cdc6SMatthias Ringwald log_info("l2cap psm: %0x, status %02x", gatt_client->l2cap_psm, status);
34181450cdc6SMatthias Ringwald if (status != ERROR_CODE_SUCCESS) break;
34191450cdc6SMatthias Ringwald if (gatt_client->l2cap_psm == 0) {
34201450cdc6SMatthias Ringwald status = SDP_SERVICE_NOT_FOUND;
34211450cdc6SMatthias Ringwald break;
34221450cdc6SMatthias Ringwald }
34231450cdc6SMatthias Ringwald break;
34241450cdc6SMatthias Ringwald default:
34251450cdc6SMatthias Ringwald btstack_assert(false);
34261450cdc6SMatthias Ringwald return;
34271450cdc6SMatthias Ringwald }
34281450cdc6SMatthias Ringwald
34291450cdc6SMatthias Ringwald // done
34301450cdc6SMatthias Ringwald if (status == ERROR_CODE_SUCCESS){
3431052dc82aSMatthias Ringwald gatt_client->state = P_W4_L2CAP_CONNECTION;
343259b5e157SMatthias Ringwald status = att_dispatch_classic_connect(gatt_client->addr, gatt_client->l2cap_psm, &gatt_client->l2cap_cid);
34331450cdc6SMatthias Ringwald }
34341450cdc6SMatthias Ringwald if (status != ERROR_CODE_SUCCESS) {
34351450cdc6SMatthias Ringwald gatt_client_classic_handle_connected(gatt_client, status);
34361450cdc6SMatthias Ringwald }
34371450cdc6SMatthias Ringwald }
34381450cdc6SMatthias Ringwald
gatt_client_classic_sdp_start(void * context)34391450cdc6SMatthias Ringwald static void gatt_client_classic_sdp_start(void * context){
34401450cdc6SMatthias Ringwald gatt_client_classic_active_sdp_query = (gatt_client_t *) context;
3441052dc82aSMatthias Ringwald gatt_client_classic_active_sdp_query->state = P_W4_SDP_QUERY;
34421450cdc6SMatthias Ringwald sdp_client_query_uuid16(gatt_client_classic_sdp_handler, gatt_client_classic_active_sdp_query->addr, ORG_BLUETOOTH_SERVICE_GENERIC_ATTRIBUTE);
34431450cdc6SMatthias Ringwald }
34441450cdc6SMatthias Ringwald
gatt_client_classic_emit_connected(void * context)344574daebdeSMatthias Ringwald static void gatt_client_classic_emit_connected(void * context){
344674daebdeSMatthias Ringwald gatt_client_t * gatt_client = (gatt_client_t *) context;
3447052dc82aSMatthias Ringwald gatt_client->state = P_READY;
3448b2d70d58SMatthias Ringwald gatt_client_emit_connected(gatt_client->callback, ERROR_CODE_SUCCESS, gatt_client->addr_type, gatt_client->addr, gatt_client->con_handle);
344974daebdeSMatthias Ringwald }
345074daebdeSMatthias Ringwald
gatt_client_classic_connect(btstack_packet_handler_t callback,bd_addr_t addr)34511450cdc6SMatthias Ringwald uint8_t gatt_client_classic_connect(btstack_packet_handler_t callback, bd_addr_t addr){
34521450cdc6SMatthias Ringwald gatt_client_t * gatt_client = gatt_client_get_context_for_classic_addr(addr);
34531450cdc6SMatthias Ringwald if (gatt_client != NULL){
34541450cdc6SMatthias Ringwald return ERROR_CODE_ACL_CONNECTION_ALREADY_EXISTS;
34551450cdc6SMatthias Ringwald }
34561450cdc6SMatthias Ringwald gatt_client = btstack_memory_gatt_client_get();
34571450cdc6SMatthias Ringwald if (gatt_client == NULL){
34581450cdc6SMatthias Ringwald return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
34591450cdc6SMatthias Ringwald }
34601450cdc6SMatthias Ringwald // init state
3461e9cdf30bSMatthias Ringwald gatt_client->bearer_type = ATT_BEARER_UNENHANCED_CLASSIC;
34621450cdc6SMatthias Ringwald gatt_client->con_handle = HCI_CON_HANDLE_INVALID;
34631450cdc6SMatthias Ringwald memcpy(gatt_client->addr, addr, 6);
3464b2d70d58SMatthias Ringwald gatt_client->addr_type = BD_ADDR_TYPE_ACL;
34651450cdc6SMatthias Ringwald gatt_client->mtu = ATT_DEFAULT_MTU;
34661450cdc6SMatthias Ringwald gatt_client->security_level = LEVEL_0;
34671450cdc6SMatthias Ringwald gatt_client->mtu_state = MTU_AUTO_EXCHANGE_DISABLED;
34681450cdc6SMatthias Ringwald gatt_client->callback = callback;
3469cbd76cecSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
3470cbd76cecSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_IDLE;
3471cbd76cecSMatthias Ringwald #endif
34721450cdc6SMatthias Ringwald btstack_linked_list_add(&gatt_client_connections, (btstack_linked_item_t*)gatt_client);
347374daebdeSMatthias Ringwald
347474daebdeSMatthias Ringwald // schedule emitted event if already connected, otherwise
347574daebdeSMatthias Ringwald bool already_connected = false;
347674daebdeSMatthias Ringwald hci_connection_t * hci_connection = hci_connection_for_bd_addr_and_type(addr, BD_ADDR_TYPE_ACL);
347774daebdeSMatthias Ringwald if (hci_connection != NULL){
347874daebdeSMatthias Ringwald if (hci_connection->att_server.l2cap_cid != 0){
347974daebdeSMatthias Ringwald already_connected = true;
348074daebdeSMatthias Ringwald }
348174daebdeSMatthias Ringwald }
3482052dc82aSMatthias Ringwald gatt_client->callback_request.context = gatt_client;
348374daebdeSMatthias Ringwald if (already_connected){
348474daebdeSMatthias Ringwald gatt_client->con_handle = hci_connection->con_handle;
3485052dc82aSMatthias Ringwald gatt_client->callback_request.callback = &gatt_client_classic_emit_connected;
3486052dc82aSMatthias Ringwald gatt_client->state = P_W2_EMIT_CONNECTED;
3487052dc82aSMatthias Ringwald btstack_run_loop_execute_on_main_thread(&gatt_client->callback_request);
348874daebdeSMatthias Ringwald } else {
3489052dc82aSMatthias Ringwald gatt_client->callback_request.callback = &gatt_client_classic_sdp_start;
3490052dc82aSMatthias Ringwald gatt_client->state = P_W2_SDP_QUERY;
3491052dc82aSMatthias Ringwald sdp_client_register_query_callback(&gatt_client->callback_request);
349274daebdeSMatthias Ringwald }
3493b7b03a30SMatthias Ringwald return ERROR_CODE_SUCCESS;
34941450cdc6SMatthias Ringwald }
34951450cdc6SMatthias Ringwald
gatt_client_classic_disconnect(btstack_packet_handler_t callback,hci_con_handle_t con_handle)34961450cdc6SMatthias Ringwald uint8_t gatt_client_classic_disconnect(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
34971450cdc6SMatthias Ringwald gatt_client_t * gatt_client = gatt_client_get_context_for_handle(con_handle);
34981450cdc6SMatthias Ringwald if (gatt_client == NULL){
34991450cdc6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
35001450cdc6SMatthias Ringwald }
35011450cdc6SMatthias Ringwald gatt_client->callback = callback;
35021450cdc6SMatthias Ringwald return l2cap_disconnect(gatt_client->l2cap_cid);
35031450cdc6SMatthias Ringwald }
35041450cdc6SMatthias Ringwald #endif
35051450cdc6SMatthias Ringwald
350626166ecfSMatthias Ringwald #ifdef ENABLE_GATT_OVER_EATT
350726166ecfSMatthias Ringwald
350826166ecfSMatthias Ringwald #define MAX_NR_EATT_CHANNELS 5
35097627a0deSMatthias Ringwald
35107627a0deSMatthias Ringwald static void gatt_client_le_enhanced_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
35117627a0deSMatthias Ringwald
gatt_client_le_enhanced_num_eatt_clients_in_state(gatt_client_t * gatt_client,gatt_client_state_t state)3512df0a5c68SMatthias Ringwald static uint8_t gatt_client_le_enhanced_num_eatt_clients_in_state(gatt_client_t * gatt_client, gatt_client_state_t state){
3513df0a5c68SMatthias Ringwald uint8_t num_clients = 0;
3514df0a5c68SMatthias Ringwald btstack_linked_list_iterator_t it;
3515df0a5c68SMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_client->eatt_clients);
3516df0a5c68SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
3517df0a5c68SMatthias Ringwald gatt_client_t * eatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
3518df0a5c68SMatthias Ringwald if (eatt_client->state == state){
3519df0a5c68SMatthias Ringwald num_clients++;
3520df0a5c68SMatthias Ringwald }
3521df0a5c68SMatthias Ringwald }
3522df0a5c68SMatthias Ringwald return num_clients;
3523df0a5c68SMatthias Ringwald }
3524df0a5c68SMatthias Ringwald
gatt_client_eatt_finalize(gatt_client_t * gatt_client)35257627a0deSMatthias Ringwald static void gatt_client_eatt_finalize(gatt_client_t * gatt_client) {
35267627a0deSMatthias Ringwald // free eatt clients
35277627a0deSMatthias Ringwald btstack_linked_list_iterator_t it;
35287627a0deSMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_client_connections);
35297627a0deSMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)) {
35307627a0deSMatthias Ringwald gatt_client_t *eatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
35317627a0deSMatthias Ringwald btstack_linked_list_iterator_remove(&it);
35327627a0deSMatthias Ringwald btstack_memory_gatt_client_free(eatt_client);
35337627a0deSMatthias Ringwald }
35347627a0deSMatthias Ringwald }
35357627a0deSMatthias Ringwald
35367627a0deSMatthias Ringwald // all channels connected
gatt_client_le_enhanced_handle_connected(gatt_client_t * gatt_client,uint8_t status)35377627a0deSMatthias Ringwald static void gatt_client_le_enhanced_handle_connected(gatt_client_t * gatt_client, uint8_t status) {
35387627a0deSMatthias Ringwald if (status == ERROR_CODE_SUCCESS){
3539df0a5c68SMatthias Ringwald uint8_t num_ready = gatt_client_le_enhanced_num_eatt_clients_in_state(gatt_client, P_READY);
3540df0a5c68SMatthias Ringwald if (num_ready > 0){
35417627a0deSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_READY;
3542df0a5c68SMatthias Ringwald // free unused channels
3543df0a5c68SMatthias Ringwald btstack_linked_list_iterator_t it;
3544df0a5c68SMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_client_connections);
3545df0a5c68SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)) {
3546df0a5c68SMatthias Ringwald gatt_client_t *eatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
3547df0a5c68SMatthias Ringwald if (eatt_client->state == P_L2CAP_CLOSED){
3548df0a5c68SMatthias Ringwald btstack_linked_list_iterator_remove(&it);
3549df0a5c68SMatthias Ringwald btstack_memory_gatt_client_free(eatt_client);
3550df0a5c68SMatthias Ringwald }
3551df0a5c68SMatthias Ringwald }
3552df0a5c68SMatthias Ringwald } else {
35536d0f6f49SMatthias Ringwald hci_connection_t * hci_connection = hci_connection_for_handle(gatt_client->con_handle);
35546d0f6f49SMatthias Ringwald btstack_assert(hci_connection != NULL);
35556d0f6f49SMatthias Ringwald if (hci_connection->att_server.incoming_connection_request){
35566d0f6f49SMatthias Ringwald hci_connection->att_server.incoming_connection_request = false;
35576d0f6f49SMatthias Ringwald log_info("Collision, retry in 100ms");
35586d0f6f49SMatthias Ringwald gatt_client->state = P_W2_L2CAP_CONNECT;
35596d0f6f49SMatthias Ringwald // set timer for retry
3560b1da4983SMatthias Ringwald btstack_run_loop_set_timer(&gatt_client->gc_timeout, GATT_CLIENT_COLLISION_BACKOFF_MS);
35616d0f6f49SMatthias Ringwald btstack_run_loop_set_timer_handler(&gatt_client->gc_timeout, gatt_client_le_enhanced_retry);
35626d0f6f49SMatthias Ringwald btstack_run_loop_add_timer(&gatt_client->gc_timeout);
35636d0f6f49SMatthias Ringwald return;
35646d0f6f49SMatthias Ringwald } else {
3565df0a5c68SMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_IDLE;
3566df0a5c68SMatthias Ringwald status = ERROR_CODE_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES;
3567df0a5c68SMatthias Ringwald }
35686d0f6f49SMatthias Ringwald }
35697627a0deSMatthias Ringwald } else {
35707627a0deSMatthias Ringwald gatt_client_eatt_finalize(gatt_client);
35717627a0deSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_IDLE;
35727627a0deSMatthias Ringwald }
35737627a0deSMatthias Ringwald
3574b2d70d58SMatthias Ringwald gatt_client_emit_connected(gatt_client->callback, status, gatt_client->addr_type, gatt_client->addr, gatt_client->con_handle);
35757627a0deSMatthias Ringwald }
35767627a0deSMatthias Ringwald
35777627a0deSMatthias Ringwald // single channel disconnected
gatt_client_le_enhanced_handle_ecbm_disconnected(gatt_client_t * gatt_client,gatt_client_t * eatt_client)35787627a0deSMatthias Ringwald static void gatt_client_le_enhanced_handle_ecbm_disconnected(gatt_client_t * gatt_client, gatt_client_t * eatt_client) {
35797627a0deSMatthias Ringwald
35807627a0deSMatthias Ringwald // report error
35817627a0deSMatthias Ringwald gatt_client_report_error_if_pending(eatt_client, ATT_ERROR_HCI_DISCONNECT_RECEIVED);
35827627a0deSMatthias Ringwald
35837627a0deSMatthias Ringwald // free memory
35847627a0deSMatthias Ringwald btstack_linked_list_remove(&gatt_client->eatt_clients, (btstack_linked_item_t *) eatt_client);
35857627a0deSMatthias Ringwald btstack_memory_gatt_client_free(eatt_client);
35867627a0deSMatthias Ringwald
3587570bdd2dSMatthias Ringwald // last channel
35887627a0deSMatthias Ringwald if (btstack_linked_list_empty(&gatt_client->eatt_clients)){
3589570bdd2dSMatthias Ringwald hci_connection_t * hci_connection = hci_connection_for_handle(gatt_client->con_handle);
3590570bdd2dSMatthias Ringwald hci_connection->att_server.eatt_outgoing_active = false;
3591570bdd2dSMatthias Ringwald
3592570bdd2dSMatthias Ringwald if (gatt_client->eatt_state == GATT_CLIENT_EATT_READY) {
3593570bdd2dSMatthias Ringwald // report disconnected if last channel closed
35947627a0deSMatthias Ringwald uint8_t buffer[20];
35957627a0deSMatthias Ringwald uint16_t len = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &gatt_client_disconnected, gatt_client->con_handle);
35967627a0deSMatthias Ringwald (*gatt_client->callback)(HCI_EVENT_PACKET, 0, buffer, len);
35977627a0deSMatthias Ringwald }
35987627a0deSMatthias Ringwald }
35997627a0deSMatthias Ringwald }
36007627a0deSMatthias Ringwald
gatt_client_le_enhanced_get_context_for_l2cap_cid(uint16_t l2cap_cid,gatt_client_t ** out_eatt_client)36017627a0deSMatthias Ringwald static gatt_client_t * gatt_client_le_enhanced_get_context_for_l2cap_cid(uint16_t l2cap_cid, gatt_client_t ** out_eatt_client){
36027627a0deSMatthias Ringwald btstack_linked_list_iterator_t it;
36037627a0deSMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_client_connections);
36047627a0deSMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)) {
36057627a0deSMatthias Ringwald gatt_client_t * gatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
36067627a0deSMatthias Ringwald btstack_linked_list_iterator_t it2;
36077627a0deSMatthias Ringwald btstack_linked_list_iterator_init(&it2, &gatt_client->eatt_clients);
36087627a0deSMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it2)) {
36097627a0deSMatthias Ringwald gatt_client_t * eatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it2);
36107627a0deSMatthias Ringwald if (eatt_client->l2cap_cid == l2cap_cid){
36117627a0deSMatthias Ringwald *out_eatt_client = eatt_client;
36127627a0deSMatthias Ringwald return gatt_client;
36137627a0deSMatthias Ringwald }
36147627a0deSMatthias Ringwald }
36157627a0deSMatthias Ringwald }
36167627a0deSMatthias Ringwald return NULL;
36177627a0deSMatthias Ringwald }
36187627a0deSMatthias Ringwald
gatt_client_le_enhanced_setup_l2cap_channel(gatt_client_t * gatt_client)36197627a0deSMatthias Ringwald static void gatt_client_le_enhanced_setup_l2cap_channel(gatt_client_t * gatt_client){
36207627a0deSMatthias Ringwald uint8_t num_channels = gatt_client->eatt_num_clients;
36217627a0deSMatthias Ringwald
36227627a0deSMatthias Ringwald // setup channels
36237627a0deSMatthias Ringwald uint16_t buffer_size_per_client = gatt_client->eatt_storage_size / num_channels;
3624f7a42e72SMatthias Ringwald uint16_t max_mtu = (buffer_size_per_client - REPORT_PREBUFFER_HEADER) / 2;
36257627a0deSMatthias Ringwald uint8_t * receive_buffers[MAX_NR_EATT_CHANNELS];
36267627a0deSMatthias Ringwald uint16_t new_cids[MAX_NR_EATT_CHANNELS];
36277627a0deSMatthias Ringwald memset(gatt_client->eatt_storage_buffer, 0, gatt_client->eatt_storage_size);
36287627a0deSMatthias Ringwald uint8_t i;
36297627a0deSMatthias Ringwald for (i=0;i<gatt_client->eatt_num_clients; i++){
3630f7a42e72SMatthias Ringwald receive_buffers[i] = &gatt_client->eatt_storage_buffer[REPORT_PREBUFFER_HEADER];
3631f7a42e72SMatthias Ringwald gatt_client->eatt_storage_buffer += REPORT_PREBUFFER_HEADER + max_mtu;
36327627a0deSMatthias Ringwald }
36337627a0deSMatthias Ringwald
36347627a0deSMatthias Ringwald log_info("%u EATT clients with receive buffer size %u", gatt_client->eatt_num_clients, buffer_size_per_client);
36357627a0deSMatthias Ringwald
3636f7a42e72SMatthias Ringwald uint8_t status = l2cap_ecbm_create_channels(&gatt_client_le_enhanced_packet_handler,
3637f7a42e72SMatthias Ringwald gatt_client->con_handle,
36387627a0deSMatthias Ringwald gatt_client->security_level,
3639f7a42e72SMatthias Ringwald BLUETOOTH_PSM_EATT, num_channels,
3640f7a42e72SMatthias Ringwald L2CAP_LE_AUTOMATIC_CREDITS,
36417627a0deSMatthias Ringwald buffer_size_per_client,
3642f7a42e72SMatthias Ringwald receive_buffers,
3643f7a42e72SMatthias Ringwald new_cids);
36447627a0deSMatthias Ringwald
36457627a0deSMatthias Ringwald if (status == ERROR_CODE_SUCCESS){
36467627a0deSMatthias Ringwald i = 0;
36477627a0deSMatthias Ringwald btstack_linked_list_iterator_t it;
36487627a0deSMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_client->eatt_clients);
36497627a0deSMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)) {
36507627a0deSMatthias Ringwald gatt_client_t *new_eatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
36517627a0deSMatthias Ringwald
36527627a0deSMatthias Ringwald // init state with new cid and transmit buffer
36537627a0deSMatthias Ringwald new_eatt_client->bearer_type = ATT_BEARER_ENHANCED_LE;
36547627a0deSMatthias Ringwald new_eatt_client->con_handle = gatt_client->con_handle;
36557627a0deSMatthias Ringwald new_eatt_client->mtu = 64;
36567627a0deSMatthias Ringwald new_eatt_client->security_level = LEVEL_0;
36577627a0deSMatthias Ringwald new_eatt_client->mtu_state = MTU_AUTO_EXCHANGE_DISABLED;
3658052dc82aSMatthias Ringwald new_eatt_client->state = P_W4_L2CAP_CONNECTION;
36597627a0deSMatthias Ringwald new_eatt_client->l2cap_cid = new_cids[i];
36607627a0deSMatthias Ringwald new_eatt_client->eatt_storage_buffer = gatt_client->eatt_storage_buffer;
3661f7a42e72SMatthias Ringwald gatt_client->eatt_storage_buffer += max_mtu;
36627627a0deSMatthias Ringwald i++;
36637627a0deSMatthias Ringwald }
36647627a0deSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_L2CAP_SETUP;
36657627a0deSMatthias Ringwald } else {
36667627a0deSMatthias Ringwald gatt_client_le_enhanced_handle_connected(gatt_client, status);
36677627a0deSMatthias Ringwald }
366826166ecfSMatthias Ringwald }
366926166ecfSMatthias Ringwald
gatt_client_le_enhanced_retry(btstack_timer_source_t * ts)36706d0f6f49SMatthias Ringwald static void gatt_client_le_enhanced_retry(btstack_timer_source_t * ts){
36716d0f6f49SMatthias Ringwald gatt_client_t * gatt_client = gatt_client_for_timer(ts);
36726d0f6f49SMatthias Ringwald if (gatt_client != NULL){
36736d0f6f49SMatthias Ringwald gatt_client->state = P_W4_L2CAP_CONNECTION;
36746d0f6f49SMatthias Ringwald gatt_client_le_enhanced_setup_l2cap_channel(gatt_client);
36756d0f6f49SMatthias Ringwald }
36766d0f6f49SMatthias Ringwald }
36776d0f6f49SMatthias Ringwald
gatt_client_le_enhanced_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)367826166ecfSMatthias Ringwald static void gatt_client_le_enhanced_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
36797627a0deSMatthias Ringwald gatt_client_t *gatt_client;
36807627a0deSMatthias Ringwald gatt_client_t *eatt_client;
368126166ecfSMatthias Ringwald hci_con_handle_t con_handle;
36827627a0deSMatthias Ringwald uint16_t l2cap_cid;
368326166ecfSMatthias Ringwald uint8_t status;
368426166ecfSMatthias Ringwald gatt_client_characteristic_t characteristic;
36857627a0deSMatthias Ringwald gatt_client_service_t service;
368626166ecfSMatthias Ringwald switch (packet_type) {
368726166ecfSMatthias Ringwald case HCI_EVENT_PACKET:
368826166ecfSMatthias Ringwald switch (hci_event_packet_get_type(packet)) {
36897627a0deSMatthias Ringwald case GATT_EVENT_SERVICE_QUERY_RESULT:
36907627a0deSMatthias Ringwald con_handle = gatt_event_service_query_result_get_handle(packet);
36917627a0deSMatthias Ringwald gatt_client = gatt_client_get_context_for_handle(con_handle);
36927627a0deSMatthias Ringwald btstack_assert(gatt_client != NULL);
36937627a0deSMatthias Ringwald btstack_assert(gatt_client->eatt_state == GATT_CLIENT_EATT_DISCOVER_GATT_SERVICE_W4_DONE);
36947627a0deSMatthias Ringwald gatt_event_service_query_result_get_service(packet, &service);
36957627a0deSMatthias Ringwald gatt_client->gatt_service_start_group_handle = service.start_group_handle;
36967627a0deSMatthias Ringwald gatt_client->gatt_service_end_group_handle = service.end_group_handle;
36977627a0deSMatthias Ringwald break;
369826166ecfSMatthias Ringwald case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
369926166ecfSMatthias Ringwald con_handle = gatt_event_characteristic_value_query_result_get_handle(packet);
370026166ecfSMatthias Ringwald gatt_client = gatt_client_get_context_for_handle(con_handle);
370126166ecfSMatthias Ringwald btstack_assert(gatt_client != NULL);
37027627a0deSMatthias Ringwald btstack_assert(gatt_client->eatt_state == GATT_CLIENT_EATT_READ_SERVER_SUPPORTED_FEATURES_W4_DONE);
370326166ecfSMatthias Ringwald if (gatt_event_characteristic_value_query_result_get_value_length(packet) >= 1) {
37047627a0deSMatthias Ringwald gatt_client->gatt_server_supported_features = gatt_event_characteristic_value_query_result_get_value(packet)[0];
370526166ecfSMatthias Ringwald }
370626166ecfSMatthias Ringwald break;
370726166ecfSMatthias Ringwald case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
370826166ecfSMatthias Ringwald con_handle = gatt_event_characteristic_query_result_get_handle(packet);
370926166ecfSMatthias Ringwald gatt_client = gatt_client_get_context_for_handle(con_handle);
371026166ecfSMatthias Ringwald btstack_assert(gatt_client != NULL);
37117627a0deSMatthias Ringwald btstack_assert(gatt_client->eatt_state == GATT_CLIENT_EATT_FIND_CLIENT_SUPPORTED_FEATURES_W4_DONE);
37127627a0deSMatthias Ringwald gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
371326166ecfSMatthias Ringwald gatt_client->gatt_client_supported_features_handle = characteristic.value_handle;
371426166ecfSMatthias Ringwald break;
371526166ecfSMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE:
371626166ecfSMatthias Ringwald con_handle = gatt_event_query_complete_get_handle(packet);
371726166ecfSMatthias Ringwald gatt_client = gatt_client_get_context_for_handle(con_handle);
371826166ecfSMatthias Ringwald btstack_assert(gatt_client != NULL);
37197627a0deSMatthias Ringwald switch (gatt_client->eatt_state){
37207627a0deSMatthias Ringwald case GATT_CLIENT_EATT_DISCOVER_GATT_SERVICE_W4_DONE:
37217627a0deSMatthias Ringwald if (gatt_client->gatt_service_start_group_handle == 0){
37227627a0deSMatthias Ringwald gatt_client_le_enhanced_handle_connected(gatt_client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
37237627a0deSMatthias Ringwald } else {
37247627a0deSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_READ_SERVER_SUPPORTED_FEATURES_W2_SEND;
37257627a0deSMatthias Ringwald }
37267627a0deSMatthias Ringwald break;
37277627a0deSMatthias Ringwald case GATT_CLIENT_EATT_READ_SERVER_SUPPORTED_FEATURES_W4_DONE:
37287627a0deSMatthias Ringwald if ((gatt_client->gatt_server_supported_features & 1) == 0) {
37297627a0deSMatthias Ringwald gatt_client_le_enhanced_handle_connected(gatt_client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
37307627a0deSMatthias Ringwald } else {
37317627a0deSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_FIND_CLIENT_SUPPORTED_FEATURES_W2_SEND;
37327627a0deSMatthias Ringwald }
37337627a0deSMatthias Ringwald break;
37347627a0deSMatthias Ringwald case GATT_CLIENT_EATT_FIND_CLIENT_SUPPORTED_FEATURES_W4_DONE:
37357627a0deSMatthias Ringwald if (gatt_client->gatt_client_supported_features_handle == 0){
37367627a0deSMatthias Ringwald gatt_client_le_enhanced_handle_connected(gatt_client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
37377627a0deSMatthias Ringwald } else {
37387627a0deSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_WRITE_ClIENT_SUPPORTED_FEATURES_W2_SEND;
37397627a0deSMatthias Ringwald }
37407627a0deSMatthias Ringwald break;
37417627a0deSMatthias Ringwald case GATT_CLIENT_EATT_WRITE_ClIENT_SUPPORTED_FEATURES_W4_DONE:
37427627a0deSMatthias Ringwald gatt_client_le_enhanced_setup_l2cap_channel(gatt_client);
374326166ecfSMatthias Ringwald break;
374426166ecfSMatthias Ringwald default:
374526166ecfSMatthias Ringwald break;
374626166ecfSMatthias Ringwald }
374726166ecfSMatthias Ringwald break;
37487627a0deSMatthias Ringwald case L2CAP_EVENT_ECBM_CHANNEL_OPENED:
37497627a0deSMatthias Ringwald l2cap_cid = l2cap_event_ecbm_channel_opened_get_local_cid(packet);
37507627a0deSMatthias Ringwald gatt_client = gatt_client_le_enhanced_get_context_for_l2cap_cid(l2cap_cid, &eatt_client);
37517627a0deSMatthias Ringwald
37527627a0deSMatthias Ringwald btstack_assert(gatt_client != NULL);
37537627a0deSMatthias Ringwald btstack_assert(eatt_client != NULL);
3754052dc82aSMatthias Ringwald btstack_assert(eatt_client->state == P_W4_L2CAP_CONNECTION);
37557627a0deSMatthias Ringwald
37567627a0deSMatthias Ringwald status = l2cap_event_channel_opened_get_status(packet);
37577627a0deSMatthias Ringwald if (status == ERROR_CODE_SUCCESS){
3758052dc82aSMatthias Ringwald eatt_client->state = P_READY;
37597627a0deSMatthias Ringwald eatt_client->mtu = l2cap_event_channel_opened_get_remote_mtu(packet);
37607627a0deSMatthias Ringwald } else {
3761df0a5c68SMatthias Ringwald eatt_client->state = P_L2CAP_CLOSED;
37627627a0deSMatthias Ringwald }
3763df0a5c68SMatthias Ringwald // connected if opened event for all channels received
3764df0a5c68SMatthias Ringwald if (gatt_client_le_enhanced_num_eatt_clients_in_state(gatt_client, P_W4_L2CAP_CONNECTION) == 0){
37657627a0deSMatthias Ringwald gatt_client_le_enhanced_handle_connected(gatt_client, ERROR_CODE_SUCCESS);
37667627a0deSMatthias Ringwald }
37677627a0deSMatthias Ringwald break;
37687627a0deSMatthias Ringwald case L2CAP_EVENT_CHANNEL_CLOSED:
37697627a0deSMatthias Ringwald l2cap_cid = l2cap_event_channel_closed_get_local_cid(packet);
37707627a0deSMatthias Ringwald gatt_client = gatt_client_le_enhanced_get_context_for_l2cap_cid(l2cap_cid, &eatt_client);
37717627a0deSMatthias Ringwald btstack_assert(gatt_client != NULL);
37727627a0deSMatthias Ringwald btstack_assert(eatt_client != NULL);
37737627a0deSMatthias Ringwald gatt_client_le_enhanced_handle_ecbm_disconnected(gatt_client, eatt_client);
37747627a0deSMatthias Ringwald break;
37757627a0deSMatthias Ringwald default:
37767627a0deSMatthias Ringwald break;
37777627a0deSMatthias Ringwald }
37787627a0deSMatthias Ringwald break;
37797627a0deSMatthias Ringwald case L2CAP_DATA_PACKET:
37807627a0deSMatthias Ringwald gatt_client = gatt_client_le_enhanced_get_context_for_l2cap_cid(channel, &eatt_client);
37817627a0deSMatthias Ringwald btstack_assert(gatt_client != NULL);
3782ce82ac7fSMatthias Ringwald btstack_assert(eatt_client != NULL);
3783ce82ac7fSMatthias Ringwald gatt_client_handle_att_response(eatt_client, packet, size);
37847627a0deSMatthias Ringwald gatt_client_run();
37857627a0deSMatthias Ringwald break;
378626166ecfSMatthias Ringwald default:
378726166ecfSMatthias Ringwald break;
378826166ecfSMatthias Ringwald }
378926166ecfSMatthias Ringwald }
379026166ecfSMatthias Ringwald
gatt_client_le_enhanced_handle_can_send_query(gatt_client_t * gatt_client)37917627a0deSMatthias Ringwald static bool gatt_client_le_enhanced_handle_can_send_query(gatt_client_t * gatt_client){
379226166ecfSMatthias Ringwald uint8_t status = ERROR_CODE_SUCCESS;
379326166ecfSMatthias Ringwald uint8_t gatt_client_supported_features = 0x06; // eatt + multiple value notifications
379426166ecfSMatthias Ringwald switch (gatt_client->eatt_state){
37957627a0deSMatthias Ringwald case GATT_CLIENT_EATT_DISCOVER_GATT_SERVICE_W2_SEND:
37967627a0deSMatthias Ringwald gatt_client->gatt_service_start_group_handle = 0;
37977627a0deSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_DISCOVER_GATT_SERVICE_W4_DONE;
37987627a0deSMatthias Ringwald status = gatt_client_discover_primary_services_by_uuid16(&gatt_client_le_enhanced_packet_handler,
37997627a0deSMatthias Ringwald gatt_client->con_handle,
38007627a0deSMatthias Ringwald ORG_BLUETOOTH_SERVICE_GENERIC_ATTRIBUTE);
38017627a0deSMatthias Ringwald break;
380226166ecfSMatthias Ringwald case GATT_CLIENT_EATT_READ_SERVER_SUPPORTED_FEATURES_W2_SEND:
38037627a0deSMatthias Ringwald gatt_client->gatt_server_supported_features = 0;
38047627a0deSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_READ_SERVER_SUPPORTED_FEATURES_W4_DONE;
380526166ecfSMatthias Ringwald status = gatt_client_read_value_of_characteristics_by_uuid16(&gatt_client_le_enhanced_packet_handler,
38067627a0deSMatthias Ringwald gatt_client->con_handle,
38077627a0deSMatthias Ringwald gatt_client->gatt_service_start_group_handle,
38087627a0deSMatthias Ringwald gatt_client->gatt_service_end_group_handle,
380926166ecfSMatthias Ringwald ORG_BLUETOOTH_CHARACTERISTIC_SERVER_SUPPORTED_FEATURES);
381026166ecfSMatthias Ringwald return true;
381126166ecfSMatthias Ringwald case GATT_CLIENT_EATT_FIND_CLIENT_SUPPORTED_FEATURES_W2_SEND:
38127627a0deSMatthias Ringwald gatt_client->gatt_client_supported_features_handle = 0;
38137627a0deSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_FIND_CLIENT_SUPPORTED_FEATURES_W4_DONE;
381426166ecfSMatthias Ringwald status = gatt_client_discover_characteristics_for_handle_range_by_uuid16(&gatt_client_le_enhanced_packet_handler,
38157627a0deSMatthias Ringwald gatt_client->con_handle,
38167627a0deSMatthias Ringwald gatt_client->gatt_service_start_group_handle,
38177627a0deSMatthias Ringwald gatt_client->gatt_service_end_group_handle,
38187627a0deSMatthias Ringwald ORG_BLUETOOTH_CHARACTERISTIC_CLIENT_SUPPORTED_FEATURES);
381926166ecfSMatthias Ringwald return true;
382026166ecfSMatthias Ringwald case GATT_CLIENT_EATT_WRITE_ClIENT_SUPPORTED_FEATURES_W2_SEND:
382126166ecfSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_WRITE_ClIENT_SUPPORTED_FEATURES_W4_DONE;
382226166ecfSMatthias Ringwald status = gatt_client_write_value_of_characteristic(&gatt_client_le_enhanced_packet_handler, gatt_client->con_handle,
382326166ecfSMatthias Ringwald gatt_client->gatt_client_supported_features_handle, 1,
382426166ecfSMatthias Ringwald &gatt_client_supported_features);
382526166ecfSMatthias Ringwald return true;
382626166ecfSMatthias Ringwald default:
382726166ecfSMatthias Ringwald break;
382826166ecfSMatthias Ringwald }
38297627a0deSMatthias Ringwald btstack_assert(status == ERROR_CODE_SUCCESS);
38307627a0deSMatthias Ringwald UNUSED(status);
383126166ecfSMatthias Ringwald return false;
383226166ecfSMatthias Ringwald }
383326166ecfSMatthias Ringwald
gatt_client_le_enhanced_connect(btstack_packet_handler_t callback,hci_con_handle_t con_handle,uint8_t num_channels,uint8_t * storage_buffer,uint16_t storage_size)383426166ecfSMatthias Ringwald uint8_t gatt_client_le_enhanced_connect(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint8_t num_channels, uint8_t * storage_buffer, uint16_t storage_size) {
383526166ecfSMatthias Ringwald gatt_client_t * gatt_client;
383626166ecfSMatthias Ringwald uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
383726166ecfSMatthias Ringwald if (status != ERROR_CODE_SUCCESS){
383826166ecfSMatthias Ringwald return status;
383926166ecfSMatthias Ringwald }
384026166ecfSMatthias Ringwald
384126166ecfSMatthias Ringwald if (gatt_client->eatt_state != GATT_CLIENT_EATT_IDLE){
384226166ecfSMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
384326166ecfSMatthias Ringwald }
384426166ecfSMatthias Ringwald
3845f7a42e72SMatthias Ringwald // need one buffer for sending and one for receiving. Receiving includes pre-buffer for reports
38467627a0deSMatthias Ringwald uint16_t buffer_size_per_client = storage_size / num_channels;
3847f7a42e72SMatthias Ringwald uint16_t max_mtu = (buffer_size_per_client - REPORT_PREBUFFER_HEADER) / 2;
3848f7a42e72SMatthias Ringwald if (max_mtu < 64) {
38497627a0deSMatthias Ringwald return ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS;
38507627a0deSMatthias Ringwald }
38517627a0deSMatthias Ringwald
38527627a0deSMatthias Ringwald if ((num_channels == 0) || (num_channels > MAX_NR_EATT_CHANNELS)){
38537627a0deSMatthias Ringwald return ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS;
38547627a0deSMatthias Ringwald }
38557627a0deSMatthias Ringwald
38567627a0deSMatthias Ringwald // create max num_channel eatt clients
38577627a0deSMatthias Ringwald uint8_t i;
38587627a0deSMatthias Ringwald btstack_linked_list_t eatt_clients = NULL;
38597627a0deSMatthias Ringwald for (i=0;i<num_channels;i++) {
38607627a0deSMatthias Ringwald gatt_client_t * new_gatt_client = btstack_memory_gatt_client_get();
38617627a0deSMatthias Ringwald if (new_gatt_client == NULL) {
38627627a0deSMatthias Ringwald break;
38637627a0deSMatthias Ringwald }
38647627a0deSMatthias Ringwald btstack_linked_list_add(&eatt_clients, (btstack_linked_item_t*)new_gatt_client);
38657627a0deSMatthias Ringwald }
38667627a0deSMatthias Ringwald
38677627a0deSMatthias Ringwald if (i != num_channels){
38687627a0deSMatthias Ringwald while (true){
38697627a0deSMatthias Ringwald gatt_client = (gatt_client_t *) btstack_linked_list_pop(&eatt_clients);
38707627a0deSMatthias Ringwald if (gatt_client == NULL) {
38717627a0deSMatthias Ringwald break;
38727627a0deSMatthias Ringwald }
38737627a0deSMatthias Ringwald btstack_memory_gatt_client_free(gatt_client);
38747627a0deSMatthias Ringwald }
38757627a0deSMatthias Ringwald return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
38767627a0deSMatthias Ringwald }
38777627a0deSMatthias Ringwald
3878570bdd2dSMatthias Ringwald hci_connection_t * hci_connection = hci_connection_for_handle(con_handle);
3879570bdd2dSMatthias Ringwald hci_connection->att_server.eatt_outgoing_active = true;
3880570bdd2dSMatthias Ringwald
38817627a0deSMatthias Ringwald gatt_client->callback = callback;
38827627a0deSMatthias Ringwald gatt_client->eatt_num_clients = num_channels;
38837627a0deSMatthias Ringwald gatt_client->eatt_storage_buffer = storage_buffer;
38847627a0deSMatthias Ringwald gatt_client->eatt_storage_size = storage_size;
38857627a0deSMatthias Ringwald gatt_client->eatt_clients = eatt_clients;
38867627a0deSMatthias Ringwald gatt_client->eatt_state = GATT_CLIENT_EATT_DISCOVER_GATT_SERVICE_W2_SEND;
388726166ecfSMatthias Ringwald gatt_client_notify_can_send_query(gatt_client);
388826166ecfSMatthias Ringwald
388926166ecfSMatthias Ringwald return ERROR_CODE_SUCCESS;
389026166ecfSMatthias Ringwald }
389126166ecfSMatthias Ringwald
gatt_client_le_enhanced_enable(bool enable)3892bbb8e698SMatthias Ringwald void gatt_client_le_enhanced_enable(bool enable){
3893bbb8e698SMatthias Ringwald gatt_client_eatt_enabled = enable;
3894bbb8e698SMatthias Ringwald }
3895bbb8e698SMatthias Ringwald
3896bbb8e698SMatthias Ringwald
389726166ecfSMatthias Ringwald #endif
389826166ecfSMatthias Ringwald
3899a6121b51SMilanka Ringwald #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
gatt_client_att_packet_handler_fuzz(uint8_t packet_type,uint16_t handle,uint8_t * packet,uint16_t size)3900a6121b51SMilanka Ringwald void gatt_client_att_packet_handler_fuzz(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size){
3901a6121b51SMilanka Ringwald gatt_client_att_packet_handler(packet_type, handle, packet, size);
3902a6121b51SMilanka Ringwald }
3903ae1ee62dSMilanka Ringwald
gatt_client_get_client(hci_con_handle_t con_handle,gatt_client_t ** out_gatt_client)390440faeb84SMilanka Ringwald uint8_t gatt_client_get_client(hci_con_handle_t con_handle, gatt_client_t ** out_gatt_client){
390540faeb84SMilanka Ringwald uint8_t status = gatt_client_provide_context_for_handle(con_handle, out_gatt_client);
390640faeb84SMilanka Ringwald return status;
3907ae1ee62dSMilanka Ringwald }
3908a6121b51SMilanka Ringwald #endif
3909