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 #ifdef ENABLE_TESTING_SUPPORT 157 gatt_client_characteristic_t characteristic; 158 #endif 159 160 switch (client->state){ 161 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE: 162 #ifdef ENABLE_TESTING_SUPPORT 163 printf("\n\nQuery Services:\n"); 164 #endif 165 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT; 166 att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_SCAN_PARAMETERS); 167 // TODO handle status 168 UNUSED(att_status); 169 break; 170 171 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC: 172 #ifdef ENABLE_TESTING_SUPPORT 173 printf("\n\nQuery Characteristics of service\n"); 174 #endif 175 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT; 176 service.start_group_handle = client->start_handle; 177 service.end_group_handle = client->end_handle; 178 att_status = gatt_client_discover_characteristics_for_service( 179 handle_gatt_client_event, client->con_handle, &service); 180 // TODO handle status 181 UNUSED(att_status); 182 break; 183 184 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED: 185 if (client->scan_interval_window_value_update){ 186 client->scan_interval_window_value_update = false; 187 188 #ifdef ENABLE_TESTING_SUPPORT 189 printf("\n\nUpdate - interval %d, window %d:\n", scan_parameters_service_scan_interval, scan_parameters_service_scan_window); 190 #endif 191 uint8_t value[4]; 192 little_endian_store_16(value, 0, scan_parameters_service_scan_interval); 193 little_endian_store_16(value, 2, scan_parameters_service_scan_window); 194 195 att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->scan_interval_window_value_handle, 4, value); 196 // TODO handle status 197 UNUSED(att_status); 198 } 199 break; 200 #ifdef ENABLE_TESTING_SUPPORT 201 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CCC: 202 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CCC; 203 204 characteristic.value_handle = client->scan_refresh_value_handle; 205 characteristic.end_handle = client->scan_refresh_end_handle; 206 207 // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT 208 att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic); 209 UNUSED(att_status); 210 break; 211 #endif 212 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS: 213 #ifdef ENABLE_TESTING_SUPPORT 214 printf(" Notification configuration enable "); 215 #endif 216 217 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED; 218 219 characteristic.value_handle = client->scan_refresh_value_handle; 220 characteristic.end_handle = client->scan_refresh_end_handle; 221 characteristic.properties = client->scan_refresh_properties; 222 223 // end of write marked in GATT_EVENT_QUERY_COMPLETE 224 225 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 226 227 if (att_status == ERROR_CODE_SUCCESS){ 228 gatt_client_listen_for_characteristic_value_updates( 229 &client->notification_listener, 230 &handle_notification_event, client->con_handle, &characteristic); 231 } 232 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED; 233 break; 234 default: 235 break; 236 } 237 } 238 239 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 240 UNUSED(packet_type); 241 UNUSED(channel); 242 UNUSED(size); 243 244 scan_parameters_service_client_t * client = NULL; 245 gatt_client_service_t service; 246 gatt_client_characteristic_t characteristic; 247 uint8_t att_status; 248 249 #ifdef ENABLE_TESTING_SUPPORT 250 gatt_client_characteristic_descriptor_t characteristic_descriptor; 251 #endif 252 253 switch(hci_event_packet_get_type(packet)){ 254 case GATT_EVENT_SERVICE_QUERY_RESULT: 255 client = scan_parameters_service_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet)); 256 btstack_assert(client != NULL); 257 258 if (client->state != SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT) { 259 scan_parameters_service_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE); 260 scan_parameters_service_finalize_client(client); 261 break; 262 } 263 264 gatt_event_service_query_result_get_service(packet, &service); 265 client->start_handle = service.start_group_handle; 266 client->end_handle = service.end_group_handle; 267 #ifdef ENABLE_TESTING_SUPPORT 268 printf("ScS Service: start handle 0x%04X, end handle 0x%04X\n", client->start_handle, client->end_handle); 269 #endif 270 break; 271 272 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 273 client = scan_parameters_service_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet)); 274 btstack_assert(client != NULL); 275 276 // found scan_interval_window_value_handle, check att_status 277 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 278 switch (characteristic.uuid16){ 279 case ORG_BLUETOOTH_CHARACTERISTIC_SCAN_INTERVAL_WINDOW: 280 client->scan_interval_window_value_handle = characteristic.value_handle; 281 282 #ifdef ENABLE_TESTING_SUPPORT 283 printf("ScS Scan Interval Characteristic: \n Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n", 284 characteristic.start_handle, 285 characteristic.properties, 286 characteristic.value_handle, characteristic.uuid16); 287 #endif 288 break; 289 case ORG_BLUETOOTH_CHARACTERISTIC_SCAN_REFRESH: 290 client->scan_refresh_value_handle = characteristic.value_handle; 291 client->scan_refresh_end_handle = characteristic.end_handle; 292 client->scan_refresh_properties = characteristic.properties; 293 294 #ifdef ENABLE_TESTING_SUPPORT 295 printf("ScS Scan Refresh Characteristic: \n Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n", 296 characteristic.start_handle, 297 characteristic.properties, 298 characteristic.value_handle, characteristic.uuid16); 299 #endif 300 break; 301 default: 302 break; 303 } 304 break; 305 306 #ifdef ENABLE_TESTING_SUPPORT 307 case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT: 308 client = scan_parameters_service_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet)); 309 btstack_assert(client != NULL); 310 311 gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor); 312 if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){ 313 printf(" Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n", 314 characteristic_descriptor.handle, 315 characteristic_descriptor.uuid16); 316 } 317 break; 318 319 case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT: 320 client = scan_parameters_service_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet)); 321 btstack_assert(client != NULL); 322 323 printf(" Received CCC value: "); 324 printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet), gatt_event_characteristic_value_query_result_get_value_length(packet)); 325 break; 326 #endif 327 328 case GATT_EVENT_QUERY_COMPLETE: 329 client = scan_parameters_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet)); 330 btstack_assert(client != NULL); 331 332 att_status = gatt_event_query_complete_get_att_status(packet); 333 334 switch (client->state){ 335 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT: 336 if (att_status != ATT_ERROR_SUCCESS){ 337 scan_parameters_service_emit_connection_established(client, att_status); 338 scan_parameters_service_finalize_client(client); 339 break; 340 } 341 342 if (client->start_handle != 0){ 343 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC; 344 break; 345 } 346 347 scan_parameters_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 348 scan_parameters_service_finalize_client(client); 349 break; 350 351 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 352 if (att_status != ATT_ERROR_SUCCESS){ 353 scan_parameters_service_emit_connection_established(client, att_status); 354 scan_parameters_service_finalize_client(client); 355 break; 356 } 357 if (client->scan_interval_window_value_handle == 0){ 358 scan_parameters_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 359 scan_parameters_service_finalize_client(client); 360 break; 361 } 362 #ifdef ENABLE_TESTING_SUPPORT 363 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CCC; 364 #else 365 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED; 366 client->scan_interval_window_value_update = true; 367 scan_parameters_service_emit_connection_established(client, ERROR_CODE_SUCCESS); 368 #endif 369 break; 370 371 #ifdef ENABLE_TESTING_SUPPORT 372 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CCC: 373 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED; 374 client->scan_interval_window_value_update = true; 375 scan_parameters_service_emit_connection_established(client, ERROR_CODE_SUCCESS); 376 break; 377 #endif 378 default: 379 break; 380 } 381 break; 382 default: 383 break; 384 } 385 386 if (client != NULL){ 387 scan_parameters_service_run_for_client(client); 388 } 389 } 390 391 void scan_parameters_service_client_set(uint16_t scan_interval, uint16_t scan_window){ 392 scan_parameters_service_scan_interval = scan_interval; 393 scan_parameters_service_scan_window = scan_window; 394 395 btstack_linked_list_iterator_t it; 396 btstack_linked_list_iterator_init(&it, &clients); 397 while (btstack_linked_list_iterator_has_next(&it)){ 398 scan_parameters_service_client_t * client = (scan_parameters_service_client_t*) btstack_linked_list_iterator_next(&it); 399 client->scan_interval_window_value_update = true; 400 scan_parameters_service_run_for_client(client); 401 } 402 } 403 404 uint8_t scan_parameters_service_client_enable_notifications(uint16_t scan_parameters_service_cid){ 405 scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(scan_parameters_service_cid); 406 407 if (client == NULL){ 408 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 409 } 410 411 if (client->state != SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED) { 412 return ERROR_CODE_COMMAND_DISALLOWED; 413 } 414 415 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS; 416 scan_parameters_service_run_for_client(client); 417 return ERROR_CODE_SUCCESS; 418 } 419 420 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){ 421 btstack_assert(packet_handler != NULL); 422 423 scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_con_handle(con_handle); 424 if (client != NULL){ 425 return ERROR_CODE_COMMAND_DISALLOWED; 426 } 427 428 uint16_t cid = scan_parameters_service_get_next_cid(); 429 if (scan_parameters_service_cid != NULL) { 430 *scan_parameters_service_cid = cid; 431 } 432 433 client = scan_parameters_service_create_client(con_handle, cid); 434 if (client == NULL) { 435 return BTSTACK_MEMORY_ALLOC_FAILED; 436 } 437 438 client->client_handler = packet_handler; 439 client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE; 440 scan_parameters_service_run_for_client(client); 441 return ERROR_CODE_SUCCESS; 442 } 443 444 uint8_t scan_parameters_service_client_disconnect(uint16_t scan_parameters_service_cid){ 445 scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(scan_parameters_service_cid); 446 if (client == NULL){ 447 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 448 } 449 // finalize connections 450 scan_parameters_service_finalize_client(client); 451 return ERROR_CODE_SUCCESS; 452 } 453 454 void scan_parameters_service_client_init(void){} 455 456 void scan_parameters_service_client_deinit(void){} 457 458