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__ "battery_service_client.c" 39 40 #include "btstack_config.h" 41 42 #include <stdint.h> 43 #include <string.h> 44 45 46 #include "ble/gatt-service/battery_service_client.h" 47 48 49 #include "btstack_memory.h" 50 #include "ble/att_db.h" 51 #include "ble/core.h" 52 #include "ble/gatt_client.h" 53 #include "ble/sm.h" 54 #include "bluetooth_gatt.h" 55 #include "btstack_debug.h" 56 #include "btstack_event.h" 57 #include "btstack_run_loop.h" 58 #include "gap.h" 59 60 #define BATTERY_SERVICE_INVALID_INDEX 0xFF 61 62 static btstack_linked_list_t clients; 63 static uint16_t battery_service_cid_counter = 0; 64 65 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 66 static void battery_service_poll_timer_start(battery_service_client_t * client); 67 68 static uint16_t battery_service_get_next_cid(void){ 69 if (battery_service_cid_counter == 0xffff) { 70 battery_service_cid_counter = 1; 71 } else { 72 battery_service_cid_counter++; 73 } 74 return battery_service_cid_counter; 75 } 76 77 static battery_service_client_t * battery_service_create_client(hci_con_handle_t con_handle, uint16_t cid){ 78 battery_service_client_t * client = btstack_memory_battery_service_client_get(); 79 if (!client){ 80 log_error("Not enough memory to create client"); 81 return NULL; 82 } 83 client->cid = cid; 84 client->con_handle = con_handle; 85 client->poll_interval_ms = 0; 86 client->num_instances = 0; 87 client->battery_service_index = 0; 88 client->poll_bitmap = 0; 89 client->need_poll_bitmap = 0; 90 client->polled_service_index = BATTERY_SERVICE_INVALID_INDEX; 91 client->state = BATTERY_SERVICE_CLIENT_STATE_IDLE; 92 93 btstack_linked_list_add(&clients, (btstack_linked_item_t *) client); 94 return client; 95 } 96 97 static void battery_service_finalize_client(battery_service_client_t * client){ 98 // stop listening 99 uint8_t i; 100 for (i = 0; i < client->num_instances; i++){ 101 gatt_client_stop_listening_for_characteristic_value_updates(&client->services[i].notification_listener); 102 } 103 104 // remove timer 105 btstack_run_loop_remove_timer(&client->poll_timer); 106 107 btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client); 108 btstack_memory_battery_service_client_free(client); 109 } 110 111 static battery_service_client_t * battery_service_get_client_for_con_handle(hci_con_handle_t con_handle){ 112 btstack_linked_list_iterator_t it; 113 btstack_linked_list_iterator_init(&it, &clients); 114 while (btstack_linked_list_iterator_has_next(&it)){ 115 battery_service_client_t * client = (battery_service_client_t *)btstack_linked_list_iterator_next(&it); 116 if (client->con_handle != con_handle) continue; 117 return client; 118 } 119 return NULL; 120 } 121 122 static battery_service_client_t * battery_service_get_client_for_cid(uint16_t battery_service_cid){ 123 btstack_linked_list_iterator_t it; 124 btstack_linked_list_iterator_init(&it, &clients); 125 while (btstack_linked_list_iterator_has_next(&it)){ 126 battery_service_client_t * client = (battery_service_client_t *)btstack_linked_list_iterator_next(&it); 127 if (client->cid != battery_service_cid) continue; 128 return client; 129 } 130 return NULL; 131 } 132 133 static void battery_service_emit_connection_established(battery_service_client_t * client, uint8_t status){ 134 uint8_t event[8]; 135 int pos = 0; 136 event[pos++] = HCI_EVENT_GATTSERVICE_META; 137 event[pos++] = sizeof(event) - 2; 138 event[pos++] = GATTSERVICE_SUBEVENT_BATTERY_SERVICE_CONNECTED; 139 little_endian_store_16(event, pos, client->cid); 140 pos += 2; 141 event[pos++] = status; 142 event[pos++] = client->num_instances; 143 event[pos++] = client->poll_bitmap; 144 145 (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 146 } 147 148 static void battery_service_emit_battery_level(battery_service_client_t * client, uint16_t value_handle, uint8_t att_status, uint8_t battery_level){ 149 uint8_t event[8]; 150 int pos = 0; 151 event[pos++] = HCI_EVENT_GATTSERVICE_META; 152 event[pos++] = sizeof(event) - 2; 153 event[pos++] = GATTSERVICE_SUBEVENT_BATTERY_SERVICE_LEVEL; 154 little_endian_store_16(event, pos, client->cid); 155 pos += 2; 156 157 uint8_t i; 158 for (i = 0; i < client->num_instances; i++){ 159 if (value_handle == client->services[i].value_handle){ 160 event[pos++] = i; 161 event[pos++] = att_status; 162 event[pos++] = battery_level; 163 (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 164 break; 165 } 166 } 167 } 168 169 static void battery_service_run_for_client(battery_service_client_t * client){ 170 uint8_t status; 171 uint8_t i; 172 gatt_client_characteristic_t characteristic; 173 174 switch (client->state){ 175 case BATTERY_SERVICE_CLIENT_STATE_CONNECTED: 176 for (i = 0; i < client->num_instances; i++){ 177 if ( ((client->need_poll_bitmap >> i) & 0x01) == 0x01 ){ 178 // clear bit of polled service 179 client->need_poll_bitmap &= ~(1u << i); 180 client->polled_service_index = i; 181 182 // poll value of characteristic 183 characteristic.value_handle = client->services[i].value_handle; 184 characteristic.properties = client->services[i].properties; 185 characteristic.end_handle = client->services[i].end_handle; 186 gatt_client_read_value_of_characteristic(handle_gatt_client_event, client->con_handle, &characteristic); 187 break; 188 } 189 } 190 break; 191 192 case BATTERY_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE: 193 client->state = BATTERY_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT; 194 status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE); 195 // TODO handle status 196 break; 197 198 case BATTERY_SERVICE_CLIENT_STATE_W4_REGISTER_NOTIFICATION: 199 // if there are services without notification, register pool timer, 200 // othervise register for notifications 201 characteristic.value_handle = client->services[client->battery_service_index].value_handle; 202 characteristic.properties = client->services[client->battery_service_index].properties; 203 characteristic.end_handle = client->services[client->battery_service_index].end_handle; 204 205 status = gatt_client_write_client_characteristic_configuration( 206 handle_gatt_client_event, 207 client->con_handle, 208 &characteristic, 209 GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 210 211 // notification supported, register for value updates 212 if (status == ERROR_CODE_SUCCESS){ 213 gatt_client_listen_for_characteristic_value_updates( 214 &client->services[client->battery_service_index].notification_listener, 215 handle_gatt_client_event, 216 client->con_handle, 217 &characteristic); 218 } else { 219 client->poll_bitmap |= 1u << client->battery_service_index; 220 } 221 222 if (client->battery_service_index + 1 < client->num_instances){ 223 client->battery_service_index++; 224 } else { 225 client->state = BATTERY_SERVICE_CLIENT_STATE_CONNECTED; 226 battery_service_emit_connection_established(client, ERROR_CODE_SUCCESS); 227 228 if (client->poll_interval_ms > 0){ 229 client->need_poll_bitmap = client->poll_bitmap; 230 battery_service_poll_timer_start(client); 231 } 232 } 233 break; 234 default: 235 break; 236 } 237 } 238 239 static void battery_service_poll_timer_timeout_handler(btstack_timer_source_t * timer){ 240 uint16_t battery_service_cid = (uint16_t)(uintptr_t) btstack_run_loop_get_timer_context(timer); 241 242 battery_service_client_t * client = battery_service_get_client_for_cid(battery_service_cid); 243 btstack_assert(client != NULL); 244 245 client->need_poll_bitmap = client->poll_bitmap; 246 battery_service_poll_timer_start(client); 247 battery_service_run_for_client(client); 248 } 249 250 static void battery_service_poll_timer_start(battery_service_client_t * client){ 251 btstack_run_loop_set_timer_handler(&client->poll_timer, battery_service_poll_timer_timeout_handler); 252 btstack_run_loop_set_timer_context(&client->poll_timer, (void *)(uintptr_t)client->cid); 253 254 btstack_run_loop_set_timer(&client->poll_timer, client->poll_interval_ms); 255 btstack_run_loop_add_timer(&client->poll_timer); 256 } 257 258 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 259 UNUSED(packet_type); // ok: only hci events 260 UNUSED(channel); // ok: there is no channel 261 UNUSED(size); // ok: fixed format events read from HCI buffer 262 263 battery_service_client_t * client; 264 gatt_client_service_t service; 265 gatt_client_characteristic_t characteristic; 266 uint8_t status; 267 268 switch(hci_event_packet_get_type(packet)){ 269 case GATT_EVENT_SERVICE_QUERY_RESULT: 270 client = battery_service_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet)); 271 btstack_assert(client != NULL); 272 273 if (client->state != BATTERY_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT) { 274 battery_service_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE); 275 battery_service_finalize_client(client); 276 break; 277 } 278 279 if (client->num_instances < MAX_NUM_BATTERY_SERVICES){ 280 gatt_event_service_query_result_get_service(packet, &service); 281 client->services[client->num_instances].start_handle = service.start_group_handle; 282 client->services[client->num_instances].end_handle = service.end_group_handle; 283 } 284 client->num_instances++; 285 break; 286 287 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 288 client = battery_service_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet)); 289 btstack_assert(client != NULL); 290 291 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 292 if (client->battery_service_index < client->num_instances){ 293 client->services[client->battery_service_index].value_handle = characteristic.value_handle; 294 client->services[client->battery_service_index].properties = characteristic.properties; 295 } 296 break; 297 298 case GATT_EVENT_NOTIFICATION: 299 // ignore if wrong (event type 1, length 1, handle 2, value handle 2, value length 2, value 1) 300 if (size != 9) break; 301 302 client = battery_service_get_client_for_con_handle(gatt_event_notification_get_handle(packet)); 303 btstack_assert(client != NULL); 304 305 if (gatt_event_notification_get_value_length(packet) != 1) break; 306 307 battery_service_emit_battery_level(client, 308 gatt_event_notification_get_value_handle(packet), 309 ATT_ERROR_SUCCESS, 310 gatt_event_notification_get_value(packet)[0]); 311 break; 312 313 case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT: 314 // ignore if wrong (event type 1, length 1, handle 2, value handle 2, value length 2, value 1) 315 if (size != 9) break; 316 317 client = battery_service_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet)); 318 btstack_assert(client != NULL); 319 320 if (gatt_event_characteristic_value_query_result_get_value_length(packet) != 1) break; 321 322 battery_service_emit_battery_level(client, 323 gatt_event_characteristic_value_query_result_get_value_handle(packet), 324 ATT_ERROR_SUCCESS, 325 gatt_event_characteristic_value_query_result_get_value(packet)[0]); 326 327 // call run for client function to trigger next poll 328 battery_service_run_for_client(client); 329 break; 330 331 case GATT_EVENT_QUERY_COMPLETE: 332 client = battery_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet)); 333 btstack_assert(client != NULL); 334 335 status = gatt_event_query_complete_get_att_status(packet); 336 337 switch (client->state){ 338 case GATTSERVICE_SUBEVENT_BATTERY_SERVICE_CONNECTED: 339 if (client->polled_service_index != BATTERY_SERVICE_INVALID_INDEX){ 340 if (status != ATT_ERROR_SUCCESS){ 341 battery_service_emit_battery_level(client, client->services[client->polled_service_index].value_handle, status, 0); 342 } 343 client->polled_service_index = BATTERY_SERVICE_INVALID_INDEX; 344 } 345 break; 346 case BATTERY_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT: 347 if (status != ATT_ERROR_SUCCESS){ 348 battery_service_emit_connection_established(client, status); 349 battery_service_finalize_client(client); 350 break; 351 } 352 353 if (client->num_instances == 0){ 354 battery_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 355 battery_service_finalize_client(client); 356 break; 357 } 358 359 if (client->num_instances > MAX_NUM_BATTERY_SERVICES) { 360 log_info("%d battery services found, only first %d can be stored, increase MAX_NUM_BATTERY_SERVICES", client->num_instances, MAX_NUM_BATTERY_SERVICES); 361 client->num_instances = MAX_NUM_BATTERY_SERVICES; 362 } 363 364 client->state = BATTERY_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT; 365 client->battery_service_index = 0; 366 gatt_client_discover_characteristics_for_handle_range_by_uuid16( 367 handle_gatt_client_event, 368 client->con_handle, 369 client->services[client->battery_service_index].start_handle, 370 client->services[client->battery_service_index].end_handle, 371 ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL); 372 break; 373 374 case BATTERY_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 375 if (status != ATT_ERROR_SUCCESS){ 376 battery_service_emit_connection_established(client, status); 377 battery_service_finalize_client(client); 378 break; 379 } 380 381 // check if there is another service to query 382 if ((client->battery_service_index + 1) < client->num_instances){ 383 client->battery_service_index++; 384 gatt_client_discover_characteristics_for_handle_range_by_uuid16( 385 handle_gatt_client_event, 386 client->con_handle, 387 client->services[client->battery_service_index].start_handle, 388 client->services[client->battery_service_index].end_handle, 389 ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL); 390 break; 391 } 392 393 // we are done with quering all services, wait for notification registration 394 // to send connection established event 395 client->state = BATTERY_SERVICE_CLIENT_STATE_W4_REGISTER_NOTIFICATION; 396 client->battery_service_index = 0; 397 398 battery_service_run_for_client(client); 399 break; 400 401 default: 402 break; 403 404 } 405 break; 406 407 408 default: 409 break; 410 } 411 } 412 413 414 uint8_t battery_service_client_connect(hci_con_handle_t con_handle, btstack_packet_handler_t packet_handler, uint32_t poll_interval_ms, uint16_t * battery_service_cid){ 415 btstack_assert(packet_handler != NULL); 416 417 battery_service_client_t * client = battery_service_get_client_for_con_handle(con_handle); 418 if (client != NULL){ 419 return ERROR_CODE_COMMAND_DISALLOWED; 420 } 421 422 uint16_t cid = battery_service_get_next_cid(); 423 if (battery_service_cid != NULL) { 424 *battery_service_cid = cid; 425 } 426 427 client = battery_service_create_client(con_handle, cid); 428 if (client == NULL) { 429 return BTSTACK_MEMORY_ALLOC_FAILED; 430 } 431 432 client->client_handler = packet_handler; 433 client->poll_interval_ms = poll_interval_ms; 434 client->state = BATTERY_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE; 435 battery_service_run_for_client(client); 436 return ERROR_CODE_SUCCESS; 437 } 438 439 uint8_t battery_service_client_disconnect(uint16_t battery_service_cid){ 440 battery_service_client_t * client = battery_service_get_client_for_cid(battery_service_cid); 441 if (client == NULL){ 442 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 443 } 444 // finalize connections 445 battery_service_finalize_client(client); 446 return ERROR_CODE_SUCCESS; 447 } 448 449 uint8_t battery_service_client_read_battery_level(uint16_t battery_service_cid, uint8_t service_index){ 450 battery_service_client_t * client = battery_service_get_client_for_cid(battery_service_cid); 451 if (client == NULL) { 452 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 453 } 454 if (client->state != BATTERY_SERVICE_CLIENT_STATE_CONNECTED) { 455 return GATT_CLIENT_IN_WRONG_STATE; 456 } 457 if (service_index >= client->num_instances) { 458 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 459 } 460 client->need_poll_bitmap |= (1u << service_index); 461 battery_service_run_for_client(client); 462 return ERROR_CODE_SUCCESS; 463 } 464 465 void battery_service_client_init(void){} 466 467 void battery_service_client_deinit(void){} 468 469