1 /* 2 * Copyright (C) 2024 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 BLUEKITCHEN 24 * GMBH 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__ "link_loss_service_client.c" 39 40 #include "btstack_config.h" 41 42 #ifdef ENABLE_TESTING_SUPPORT 43 #include <stdio.h> 44 #include <unistd.h> 45 #endif 46 47 #include <stdint.h> 48 #include <string.h> 49 50 #include "ble/gatt_service_client.h" 51 #include "ble/gatt-service/link_loss_service_client.h" 52 53 #include "bluetooth_gatt.h" 54 #include "btstack_debug.h" 55 #include "btstack_event.h" 56 57 // LLS Client 58 static gatt_service_client_t lls_client; 59 static btstack_linked_list_t lls_connections; 60 61 static btstack_context_callback_registration_t lls_client_handle_can_send_now; 62 63 static void lls_client_packet_handler_internal(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 64 static void lls_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 65 static void lls_client_run_for_connection(void * context); 66 67 // list of uuids 68 static const uint16_t lls_uuid16s[LINK_LOSS_SERVICE_CLIENT_NUM_CHARACTERISTICS] = { 69 ORG_BLUETOOTH_CHARACTERISTIC_ALERT_LEVEL 70 }; 71 72 typedef enum { 73 LLS_CLIENT_CHARACTERISTIC_INDEX_ALERT_LEVEL = 0, 74 LLS_CLIENT_CHARACTERISTIC_INDEX_RFU 75 } lls_client_characteristic_index_t; 76 77 #ifdef ENABLE_TESTING_SUPPORT 78 static char * lls_client_characteristic_name[] = { 79 "ALERT_LEVEL", 80 "RFU" 81 }; 82 #endif 83 84 static lls_client_connection_t * lls_client_get_connection_for_cid(uint16_t connection_cid){ 85 btstack_linked_list_iterator_t it; 86 btstack_linked_list_iterator_init(&it, &lls_connections); 87 while (btstack_linked_list_iterator_has_next(&it)){ 88 lls_client_connection_t * connection = (lls_client_connection_t *)btstack_linked_list_iterator_next(&it); 89 if (gatt_service_client_get_connection_id(&connection->basic_connection) == connection_cid) { 90 return connection; 91 } 92 } 93 return NULL; 94 } 95 96 static void lls_client_add_connection(lls_client_connection_t * connection){ 97 btstack_linked_list_add(&lls_connections, (btstack_linked_item_t*) connection); 98 } 99 100 static void lls_client_finalize_connection(lls_client_connection_t * connection){ 101 btstack_linked_list_remove(&lls_connections, (btstack_linked_item_t*) connection); 102 } 103 104 static void lls_client_replace_subevent_id_and_emit(btstack_packet_handler_t callback, uint8_t * packet, uint16_t size, uint8_t subevent_id){ 105 UNUSED(size); 106 btstack_assert(callback != NULL); 107 // execute callback 108 packet[2] = subevent_id; 109 (*callback)(HCI_EVENT_PACKET, 0, packet, size); 110 } 111 112 static void lls_client_connected(lls_client_connection_t * connection, uint8_t status, uint8_t * packet, uint16_t size) { 113 if (status == ERROR_CODE_SUCCESS){ 114 connection->state = LINK_LOSS_SERVICE_CLIENT_STATE_READY; 115 } else { 116 connection->state = LINK_LOSS_SERVICE_CLIENT_STATE_IDLE; 117 } 118 lls_client_replace_subevent_id_and_emit(connection->packet_handler, packet, size, 119 GATTSERVICE_SUBEVENT_LLS_CLIENT_CONNECTED); 120 } 121 122 static void lls_client_emit_uint8(uint16_t cid, btstack_packet_handler_t event_callback, uint8_t subevent, const uint8_t * data, uint16_t data_size){ 123 UNUSED(data_size); 124 btstack_assert(event_callback != NULL); 125 126 if (data_size != 1){ 127 return; 128 } 129 130 uint8_t event[6]; 131 uint16_t pos = 0; 132 event[pos++] = HCI_EVENT_GATTSERVICE_META; 133 event[pos++] = 4; 134 event[pos++] = subevent; 135 little_endian_store_16(event, pos, cid); 136 pos+= 2; 137 event[pos++] = data[0]; 138 139 (*event_callback)(HCI_EVENT_PACKET, 0, event, pos); 140 } 141 142 143 static uint16_t lls_client_value_handle_for_index(lls_client_connection_t * connection){ 144 return connection->basic_connection.characteristics[connection->characteristic_index].value_handle; 145 } 146 147 static void lls_client_emit_done_event(lls_client_connection_t * connection, uint8_t index, uint8_t status){ 148 btstack_assert(connection->packet_handler != NULL); 149 150 uint16_t cid = connection->basic_connection.cid; 151 uint16_t characteristic_uuid16 = gatt_service_client_characteristic_uuid16_for_index(&lls_client, index); 152 153 uint8_t event[8]; 154 uint16_t pos = 0; 155 event[pos++] = HCI_EVENT_GATTSERVICE_META; 156 event[pos++] = sizeof(event) - 2; 157 event[pos++] = GATTSERVICE_SUBEVENT_LLS_CLIENT_WRITE_DONE; 158 159 little_endian_store_16(event, pos, cid); 160 pos+= 2; 161 little_endian_store_16(event, pos, characteristic_uuid16); 162 pos+= 2; 163 event[pos++] = status; 164 (*connection->packet_handler)(HCI_EVENT_PACKET, 0, event, pos); 165 } 166 167 168 static void lls_client_emit_read_event(lls_client_connection_t * connection, uint8_t index, uint8_t status, const uint8_t * data, uint16_t data_size){ 169 UNUSED(status); 170 171 if ((data_size > 0) && (data == NULL)){ 172 return; 173 } 174 175 uint16_t characteristic_uuid16 = gatt_service_client_characteristic_uuid16_for_index(&lls_client, index); 176 switch (characteristic_uuid16){ 177 case ORG_BLUETOOTH_CHARACTERISTIC_ALERT_LEVEL: 178 lls_client_emit_uint8(connection->basic_connection.cid, connection->packet_handler, GATTSERVICE_SUBEVENT_LLS_CLIENT_ALERT_LEVEL, data, data_size); 179 break; 180 default: 181 btstack_assert(false); 182 break; 183 } 184 } 185 186 static uint8_t lls_client_can_query_characteristic(lls_client_connection_t * connection, lls_client_characteristic_index_t characteristic_index){ 187 uint8_t status = gatt_service_client_can_query_characteristic(&connection->basic_connection, 188 (uint8_t) characteristic_index); 189 if (status != ERROR_CODE_SUCCESS){ 190 return status; 191 } 192 return connection->state == LINK_LOSS_SERVICE_CLIENT_STATE_READY ? ERROR_CODE_SUCCESS : ERROR_CODE_CONTROLLER_BUSY; 193 } 194 195 static uint8_t lls_client_request_send_gatt_query(lls_client_connection_t * connection, lls_client_characteristic_index_t characteristic_index){ 196 connection->characteristic_index = characteristic_index; 197 198 lls_client_handle_can_send_now.context = (void *)(uintptr_t)connection->basic_connection.cid; 199 uint8_t status = gatt_client_request_to_send_gatt_query(&lls_client_handle_can_send_now, connection->basic_connection.con_handle); 200 if (status != ERROR_CODE_SUCCESS){ 201 connection->state = LINK_LOSS_SERVICE_CLIENT_STATE_READY; 202 } 203 return status; 204 } 205 206 static uint8_t lls_client_request_read_characteristic(uint16_t lls_cid, lls_client_characteristic_index_t characteristic_index){ 207 lls_client_connection_t * connection = lls_client_get_connection_for_cid(lls_cid); 208 if (connection == NULL){ 209 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 210 } 211 uint8_t status = lls_client_can_query_characteristic(connection, characteristic_index); 212 if (status != ERROR_CODE_SUCCESS){ 213 return status; 214 } 215 216 connection->state = LINK_LOSS_SERVICE_CLIENT_STATE_W2_READ_CHARACTERISTIC_VALUE; 217 return lls_client_request_send_gatt_query(connection, characteristic_index); 218 } 219 220 static uint8_t lls_client_request_write_characteristic(lls_client_connection_t * connection, lls_client_characteristic_index_t characteristic_index){ 221 connection->state = LINK_LOSS_SERVICE_CLIENT_STATE_W2_WRITE_CHARACTERISTIC_VALUE; 222 return lls_client_request_send_gatt_query(connection, characteristic_index); 223 } 224 225 static void lls_client_packet_handler_internal(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 226 UNUSED(channel); 227 UNUSED(size); 228 229 if (packet_type != HCI_EVENT_PACKET) return; 230 lls_client_connection_t * connection; 231 uint16_t cid; 232 uint8_t status; 233 234 switch(hci_event_packet_get_type(packet)){ 235 case HCI_EVENT_GATTSERVICE_META: 236 switch (hci_event_gattservice_meta_get_subevent_code(packet)) { 237 case GATTSERVICE_SUBEVENT_CLIENT_CONNECTED: 238 cid = gattservice_subevent_client_connected_get_cid(packet); 239 connection = lls_client_get_connection_for_cid(cid); 240 btstack_assert(connection != NULL); 241 242 #ifdef ENABLE_TESTING_SUPPORT 243 { 244 uint8_t i; 245 for (i = LLS_CLIENT_CHARACTERISTIC_INDEX_ALERT_LEVEL; 246 i < LLS_CLIENT_CHARACTERISTIC_INDEX_RFU; i++) { 247 printf("0x%04X %s\n", connection->basic_connection.characteristics[i].value_handle, 248 lls_client_characteristic_name[i]); 249 } 250 }; 251 #endif 252 status = gattservice_subevent_client_connected_get_status(packet); 253 lls_client_connected(connection, status, packet, size); 254 break; 255 256 case GATTSERVICE_SUBEVENT_CLIENT_DISCONNECTED: 257 cid = gattservice_subevent_client_disconnected_get_cid(packet); 258 connection = lls_client_get_connection_for_cid(cid); 259 btstack_assert(connection != NULL); 260 lls_client_finalize_connection(connection); 261 lls_client_replace_subevent_id_and_emit(connection->packet_handler, packet, size, 262 GATTSERVICE_SUBEVENT_LLS_CLIENT_DISCONNECTED); 263 break; 264 265 default: 266 break; 267 } 268 break; 269 270 default: 271 break; 272 } 273 } 274 275 static void lls_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 276 UNUSED(packet_type); 277 UNUSED(channel); 278 UNUSED(size); 279 280 lls_client_connection_t * connection = NULL; 281 uint16_t connection_id; 282 283 switch(hci_event_packet_get_type(packet)){ 284 case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT: 285 connection_id = gatt_event_characteristic_value_query_result_get_connection_id(packet); 286 connection = lls_client_get_connection_for_cid(connection_id); 287 btstack_assert(connection != NULL); 288 289 lls_client_emit_read_event(connection, connection->characteristic_index, ATT_ERROR_SUCCESS, 290 gatt_event_characteristic_value_query_result_get_value(packet), 291 gatt_event_characteristic_value_query_result_get_value_length(packet)); 292 293 connection->state = LINK_LOSS_SERVICE_CLIENT_STATE_READY; 294 break; 295 296 case GATT_EVENT_QUERY_COMPLETE: 297 connection_id = gatt_event_query_complete_get_connection_id(packet); 298 connection = lls_client_get_connection_for_cid(connection_id); 299 btstack_assert(connection != NULL); 300 301 connection->state = LINK_LOSS_SERVICE_CLIENT_STATE_READY; 302 lls_client_emit_done_event(connection, connection->characteristic_index, gatt_event_query_complete_get_att_status(packet)); 303 break; 304 305 default: 306 break; 307 } 308 } 309 310 static uint16_t lls_client_serialize_characteristic_value_for_write(lls_client_connection_t * connection, uint8_t ** out_value){ 311 uint16_t characteristic_uuid16 = gatt_service_client_characteristic_uuid16_for_index(&lls_client, 312 connection->characteristic_index); 313 *out_value = (uint8_t *)connection->write_buffer; 314 315 switch (characteristic_uuid16){ 316 case ORG_BLUETOOTH_CHARACTERISTIC_ALERT_LEVEL: 317 return 1; 318 default: 319 btstack_assert(false); 320 break; 321 } 322 return 0; 323 } 324 325 static void lls_client_run_for_connection(void * context){ 326 uint16_t connection_id = (uint16_t)(uintptr_t)context; 327 lls_client_connection_t * connection = lls_client_get_connection_for_cid(connection_id); 328 329 btstack_assert(connection != NULL); 330 uint16_t value_length; 331 uint8_t * value; 332 333 switch (connection->state){ 334 case LINK_LOSS_SERVICE_CLIENT_STATE_W2_READ_CHARACTERISTIC_VALUE: 335 connection->state = LINK_LOSS_SERVICE_CLIENT_STATE_W4_READ_CHARACTERISTIC_VALUE_RESULT; 336 337 (void) gatt_client_read_value_of_characteristic_using_value_handle_with_context( 338 &lls_client_handle_gatt_client_event, connection->basic_connection.cid, 339 lls_client_value_handle_for_index(connection), lls_client.service_id, connection->basic_connection.cid); 340 break; 341 342 case LINK_LOSS_SERVICE_CLIENT_STATE_W2_WRITE_CHARACTERISTIC_VALUE: 343 connection->state = LINK_LOSS_SERVICE_CLIENT_STATE_W4_WRITE_CHARACTERISTIC_VALUE_RESULT; 344 345 value_length = lls_client_serialize_characteristic_value_for_write(connection, &value); 346 (void) gatt_client_write_value_of_characteristic_with_context( 347 &lls_client_handle_gatt_client_event, connection->basic_connection.cid, 348 lls_client_value_handle_for_index(connection), 349 value_length, value, lls_client.service_id, connection->basic_connection.cid); 350 351 break; 352 353 default: 354 break; 355 } 356 } 357 358 void link_loss_service_client_init(void){ 359 gatt_service_client_register_client(&lls_client, &lls_client_packet_handler_internal, lls_uuid16s, sizeof(lls_uuid16s)/sizeof(uint16_t)); 360 361 lls_client_handle_can_send_now.callback = &lls_client_run_for_connection; 362 } 363 364 uint8_t link_loss_service_client_connect(hci_con_handle_t con_handle, 365 btstack_packet_handler_t packet_handler, 366 lls_client_connection_t * lls_connection, 367 gatt_service_client_characteristic_t * lls_storage_for_characteristics, uint8_t lls_characteristics_num, 368 uint16_t * lls_cid 369 ){ 370 371 btstack_assert(packet_handler != NULL); 372 btstack_assert(lls_connection != NULL); 373 btstack_assert(lls_characteristics_num == LINK_LOSS_SERVICE_CLIENT_NUM_CHARACTERISTICS); 374 375 *lls_cid = 0; 376 377 lls_connection->state = LINK_LOSS_SERVICE_CLIENT_STATE_W4_CONNECTION; 378 lls_connection->packet_handler = packet_handler; 379 380 uint8_t status = gatt_service_client_connect_primary_service_with_uuid16(con_handle, 381 &lls_client, 382 &lls_connection->basic_connection, 383 ORG_BLUETOOTH_SERVICE_LINK_LOSS, 0, 384 lls_storage_for_characteristics, 385 lls_characteristics_num); 386 387 if (status == ERROR_CODE_SUCCESS){ 388 lls_client_add_connection(lls_connection); 389 *lls_cid = lls_connection->basic_connection.cid; 390 } 391 392 return status; 393 } 394 395 uint8_t link_loss_service_client_read_alert_level(uint16_t lls_cid){ 396 return lls_client_request_read_characteristic(lls_cid, LLS_CLIENT_CHARACTERISTIC_INDEX_ALERT_LEVEL); 397 } 398 399 uint8_t link_loss_service_client_write_alert_level(uint16_t lls_cid, lls_alert_level_t alert_level){ 400 lls_client_connection_t * connection = lls_client_get_connection_for_cid(lls_cid); 401 if (connection == NULL){ 402 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 403 } 404 if (alert_level >= LLS_ALERT_LEVEL_RFU){ 405 return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE; 406 } 407 408 lls_client_characteristic_index_t index = LLS_CLIENT_CHARACTERISTIC_INDEX_ALERT_LEVEL; 409 410 uint8_t status = lls_client_can_query_characteristic(connection, index); 411 if (status != ERROR_CODE_SUCCESS){ 412 return status; 413 } 414 415 connection->write_buffer[0] = (uint8_t)alert_level; 416 return lls_client_request_write_characteristic(connection, index); 417 } 418 419 uint8_t link_loss_service_client_disconnect(uint16_t lls_cid){ 420 lls_client_connection_t * connection = lls_client_get_connection_for_cid(lls_cid); 421 if (connection == NULL){ 422 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 423 } 424 return gatt_service_client_disconnect(&connection->basic_connection); 425 } 426 427 void link_loss_service_client_deinit(void){ 428 gatt_service_client_unregister_client(&lls_client); 429 } 430 431