1 /* 2 * Copyright (C) 2021 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24 * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 #define BTSTACK_FILE__ "scan_parameters_service_client.c" 39 40 #include "btstack_config.h" 41 42 #include <stdint.h> 43 #include <string.h> 44 45 #ifdef ENABLE_TESTING_SUPPORT 46 #include <stdio.h> 47 #endif 48 49 #include "scan_parameters_service_client.h" 50 51 #include "btstack_memory.h" 52 #include "ble/att_db.h" 53 #include "ble/core.h" 54 #include "ble/gatt_client.h" 55 #include "ble/sm.h" 56 #include "bluetooth_gatt.h" 57 #include "btstack_debug.h" 58 #include "btstack_event.h" 59 #include "btstack_run_loop.h" 60 #include "gap.h" 61 62 static btstack_linked_list_t clients; 63 static uint16_t scan_parameters_service_cid_counter = 0; 64 static uint16_t scan_parameters_service_scan_window = 0; 65 static uint16_t scan_parameters_service_scan_interval = 0; 66 67 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 68 static void scan_parameters_service_run_for_client(scan_parameters_service_client_t * client); 69 70 static uint16_t scan_parameters_service_get_next_cid(void){ 71 if (scan_parameters_service_cid_counter == 0xffff) { 72 scan_parameters_service_cid_counter = 1; 73 } else { 74 scan_parameters_service_cid_counter++; 75 } 76 return scan_parameters_service_cid_counter; 77 } 78 79 static scan_parameters_service_client_t * scan_parameters_service_create_client(hci_con_handle_t con_handle, uint16_t cid){ 80 scan_parameters_service_client_t * client = btstack_memory_scan_parameters_service_client_get(); 81 if (!client){ 82 log_error("Not enough memory to create client"); 83 return NULL; 84 } 85 86 client->cid = cid; 87 client->con_handle = con_handle; 88 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_IDLE; 89 90 client->start_handle = 0; 91 client->end_handle = 0; 92 93 client->scan_interval_window_value_handle = 0; 94 client->scan_interval_window_value_update = false; 95 btstack_linked_list_add(&clients, (btstack_linked_item_t *) client); 96 return client; 97 } 98 99 static void scan_parameters_service_finalize_client(scan_parameters_service_client_t * client){ 100 gatt_client_stop_listening_for_characteristic_value_updates(&client->notification_listener); 101 btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client); 102 btstack_memory_scan_parameters_service_client_free(client); 103 } 104 105 static scan_parameters_service_client_t * scan_parameters_service_get_client_for_con_handle(hci_con_handle_t con_handle){ 106 btstack_linked_list_iterator_t it; 107 btstack_linked_list_iterator_init(&it, &clients); 108 while (btstack_linked_list_iterator_has_next(&it)){ 109 scan_parameters_service_client_t * client = (scan_parameters_service_client_t *)btstack_linked_list_iterator_next(&it); 110 if (client->con_handle != con_handle) continue; 111 return client; 112 } 113 return NULL; 114 } 115 116 static scan_parameters_service_client_t * scan_parameters_service_get_client_for_cid(uint16_t scan_parameters_service_cid){ 117 btstack_linked_list_iterator_t it; 118 btstack_linked_list_iterator_init(&it, &clients); 119 while (btstack_linked_list_iterator_has_next(&it)){ 120 scan_parameters_service_client_t * client = (scan_parameters_service_client_t *)btstack_linked_list_iterator_next(&it); 121 if (client->cid != scan_parameters_service_cid) continue; 122 return client; 123 } 124 return NULL; 125 } 126 127 static void scan_parameters_service_emit_connection_established(scan_parameters_service_client_t * client, uint8_t status){ 128 uint8_t event[6]; 129 int pos = 0; 130 event[pos++] = HCI_EVENT_GATTSERVICE_META; 131 event[pos++] = sizeof(event) - 2; 132 event[pos++] = GATTSERVICE_SUBEVENT_SCAN_PARAMETERS_SERVICE_CONNECTED; 133 little_endian_store_16(event, pos, client->cid); 134 pos += 2; 135 event[pos++] = status; 136 (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, 0, event, sizeof(event)); 137 } 138 139 static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 140 UNUSED(packet_type); 141 UNUSED(channel); 142 UNUSED(size); 143 144 if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return; 145 146 scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_con_handle(gatt_event_notification_get_handle(packet)); 147 btstack_assert(client != NULL); 148 client->scan_interval_window_value_update = true; 149 scan_parameters_service_run_for_client(client); 150 } 151 152 static void scan_parameters_service_run_for_client(scan_parameters_service_client_t * client){ 153 uint8_t att_status; 154 gatt_client_service_t service; 155 156 gatt_client_characteristic_t characteristic; 157 158 switch (client->state){ 159 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE: 160 #ifdef ENABLE_TESTING_SUPPORT 161 printf("\n\nQuery Services:\n"); 162 #endif 163 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT; 164 att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_SCAN_PARAMETERS); 165 // TODO handle status 166 UNUSED(att_status); 167 break; 168 169 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC: 170 #ifdef ENABLE_TESTING_SUPPORT 171 printf("\n\nQuery Characteristics of service\n"); 172 #endif 173 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT; 174 service.start_group_handle = client->start_handle; 175 service.end_group_handle = client->end_handle; 176 att_status = gatt_client_discover_characteristics_for_service( 177 handle_gatt_client_event, client->con_handle, &service); 178 // TODO handle status 179 UNUSED(att_status); 180 break; 181 182 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED: 183 if (client->scan_interval_window_value_update){ 184 client->scan_interval_window_value_update = false; 185 186 #ifdef ENABLE_TESTING_SUPPORT 187 printf("\n\nUpdate - interval %d, window %d:\n", scan_parameters_service_scan_interval, scan_parameters_service_scan_window); 188 #endif 189 uint8_t value[4]; 190 little_endian_store_16(value, 0, scan_parameters_service_scan_interval); 191 little_endian_store_16(value, 2, scan_parameters_service_scan_window); 192 193 att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->scan_interval_window_value_handle, 4, value); 194 // TODO handle status 195 UNUSED(att_status); 196 } 197 break; 198 #ifdef ENABLE_TESTING_SUPPORT 199 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CCC: 200 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CCC; 201 202 characteristic.value_handle = client->scan_refresh_value_handle; 203 characteristic.end_handle = client->scan_refresh_end_handle; 204 205 // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT 206 att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic); 207 UNUSED(att_status); 208 break; 209 #endif 210 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS: 211 #ifdef ENABLE_TESTING_SUPPORT 212 printf(" Notification configuration enable "); 213 #endif 214 215 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED; 216 217 characteristic.value_handle = client->scan_refresh_value_handle; 218 characteristic.end_handle = client->scan_refresh_end_handle; 219 characteristic.properties = client->scan_refresh_properties; 220 221 // end of write marked in GATT_EVENT_QUERY_COMPLETE 222 223 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 224 225 if (att_status == ERROR_CODE_SUCCESS){ 226 gatt_client_listen_for_characteristic_value_updates( 227 &client->notification_listener, 228 &handle_notification_event, client->con_handle, &characteristic); 229 } 230 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED; 231 break; 232 default: 233 break; 234 } 235 } 236 237 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 238 UNUSED(packet_type); 239 UNUSED(channel); 240 UNUSED(size); 241 242 scan_parameters_service_client_t * client = NULL; 243 gatt_client_service_t service; 244 gatt_client_characteristic_t characteristic; 245 uint8_t att_status; 246 247 #ifdef ENABLE_TESTING_SUPPORT 248 gatt_client_characteristic_descriptor_t characteristic_descriptor; 249 #endif 250 251 switch(hci_event_packet_get_type(packet)){ 252 case GATT_EVENT_SERVICE_QUERY_RESULT: 253 client = scan_parameters_service_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet)); 254 btstack_assert(client != NULL); 255 256 if (client->state != SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT) { 257 scan_parameters_service_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE); 258 scan_parameters_service_finalize_client(client); 259 break; 260 } 261 262 gatt_event_service_query_result_get_service(packet, &service); 263 client->start_handle = service.start_group_handle; 264 client->end_handle = service.end_group_handle; 265 #ifdef ENABLE_TESTING_SUPPORT 266 printf("ScS Service: start handle 0x%04X, end handle 0x%04X\n", client->start_handle, client->end_handle); 267 #endif 268 break; 269 270 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 271 client = scan_parameters_service_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet)); 272 btstack_assert(client != NULL); 273 274 // found scan_interval_window_value_handle, check att_status 275 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 276 switch (characteristic.uuid16){ 277 case ORG_BLUETOOTH_CHARACTERISTIC_SCAN_INTERVAL_WINDOW: 278 client->scan_interval_window_value_handle = characteristic.value_handle; 279 280 #ifdef ENABLE_TESTING_SUPPORT 281 printf("ScS Scan Interval Characteristic: \n Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n", 282 characteristic.start_handle, 283 characteristic.properties, 284 characteristic.value_handle, characteristic.uuid16); 285 #endif 286 break; 287 case ORG_BLUETOOTH_CHARACTERISTIC_SCAN_REFRESH: 288 client->scan_refresh_value_handle = characteristic.value_handle; 289 client->scan_refresh_end_handle = characteristic.end_handle; 290 client->scan_refresh_properties = characteristic.properties; 291 292 #ifdef ENABLE_TESTING_SUPPORT 293 printf("ScS Scan Refresh Characteristic: \n Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n", 294 characteristic.start_handle, 295 characteristic.properties, 296 characteristic.value_handle, characteristic.uuid16); 297 #endif 298 break; 299 default: 300 break; 301 } 302 break; 303 304 #ifdef ENABLE_TESTING_SUPPORT 305 case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT: 306 client = scan_parameters_service_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet)); 307 btstack_assert(client != NULL); 308 309 gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor); 310 if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){ 311 printf(" Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n", 312 characteristic_descriptor.handle, 313 characteristic_descriptor.uuid16); 314 } 315 break; 316 317 case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT: 318 client = scan_parameters_service_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet)); 319 btstack_assert(client != NULL); 320 321 printf(" Received CCC value: "); 322 printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet), gatt_event_characteristic_value_query_result_get_value_length(packet)); 323 break; 324 #endif 325 326 case GATT_EVENT_QUERY_COMPLETE: 327 client = scan_parameters_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet)); 328 btstack_assert(client != NULL); 329 330 att_status = gatt_event_query_complete_get_att_status(packet); 331 332 switch (client->state){ 333 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT: 334 if (att_status != ATT_ERROR_SUCCESS){ 335 scan_parameters_service_emit_connection_established(client, att_status); 336 scan_parameters_service_finalize_client(client); 337 break; 338 } 339 340 if (client->start_handle != 0){ 341 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC; 342 break; 343 } 344 345 scan_parameters_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 346 scan_parameters_service_finalize_client(client); 347 break; 348 349 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 350 if (att_status != ATT_ERROR_SUCCESS){ 351 scan_parameters_service_emit_connection_established(client, att_status); 352 scan_parameters_service_finalize_client(client); 353 break; 354 } 355 if (client->scan_interval_window_value_handle == 0){ 356 scan_parameters_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 357 scan_parameters_service_finalize_client(client); 358 break; 359 } 360 #ifdef ENABLE_TESTING_SUPPORT 361 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CCC; 362 #else 363 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED; 364 client->scan_interval_window_value_update = true; 365 scan_parameters_service_emit_connection_established(client, ERROR_CODE_SUCCESS); 366 #endif 367 break; 368 369 #ifdef ENABLE_TESTING_SUPPORT 370 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CCC: 371 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED; 372 client->scan_interval_window_value_update = true; 373 scan_parameters_service_emit_connection_established(client, ERROR_CODE_SUCCESS); 374 break; 375 #endif 376 default: 377 break; 378 } 379 break; 380 default: 381 break; 382 } 383 384 if (client != NULL){ 385 scan_parameters_service_run_for_client(client); 386 } 387 } 388 389 void scan_parameters_service_client_set(uint16_t scan_interval, uint16_t scan_window){ 390 scan_parameters_service_scan_interval = scan_interval; 391 scan_parameters_service_scan_window = scan_window; 392 393 btstack_linked_list_iterator_t it; 394 btstack_linked_list_iterator_init(&it, &clients); 395 while (btstack_linked_list_iterator_has_next(&it)){ 396 scan_parameters_service_client_t * client = (scan_parameters_service_client_t*) btstack_linked_list_iterator_next(&it); 397 client->scan_interval_window_value_update = true; 398 scan_parameters_service_run_for_client(client); 399 } 400 } 401 402 uint8_t scan_parameters_service_client_enable_notifications(uint16_t scan_parameters_service_cid){ 403 scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(scan_parameters_service_cid); 404 405 if (client == NULL){ 406 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 407 } 408 409 if (client->state != SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED) { 410 return ERROR_CODE_COMMAND_DISALLOWED; 411 } 412 413 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS; 414 scan_parameters_service_run_for_client(client); 415 return ERROR_CODE_SUCCESS; 416 } 417 418 uint8_t scan_parameters_service_client_connect(hci_con_handle_t con_handle, btstack_packet_handler_t packet_handler, uint16_t * scan_parameters_service_cid){ 419 btstack_assert(packet_handler != NULL); 420 421 scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_con_handle(con_handle); 422 if (client != NULL){ 423 return ERROR_CODE_COMMAND_DISALLOWED; 424 } 425 426 uint16_t cid = scan_parameters_service_get_next_cid(); 427 if (scan_parameters_service_cid != NULL) { 428 *scan_parameters_service_cid = cid; 429 } 430 431 client = scan_parameters_service_create_client(con_handle, cid); 432 if (client == NULL) { 433 return BTSTACK_MEMORY_ALLOC_FAILED; 434 } 435 436 client->client_handler = packet_handler; 437 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE; 438 scan_parameters_service_run_for_client(client); 439 return ERROR_CODE_SUCCESS; 440 } 441 442 uint8_t scan_parameters_service_client_disconnect(uint16_t scan_parameters_service_cid){ 443 scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(scan_parameters_service_cid); 444 if (client == NULL){ 445 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 446 } 447 // finalize connections 448 scan_parameters_service_finalize_client(client); 449 return ERROR_CODE_SUCCESS; 450 } 451 452 void scan_parameters_service_client_init(void){} 453 454 void scan_parameters_service_client_deinit(void){} 455 456