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__ "tx_power_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/tx_power_service_client.h" 52 53 #include "bluetooth_gatt.h" 54 #include "btstack_debug.h" 55 #include "btstack_event.h" 56 57 // IAS Client 58 static gatt_service_client_t txps_client; 59 static btstack_linked_list_t txps_connections; 60 61 static btstack_context_callback_registration_t txps_client_handle_can_send_now; 62 63 static void txps_client_packet_handler_internal(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 64 static void txps_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 65 static void txps_client_run_for_connection(void * context); 66 67 // list of uuids 68 static const uint16_t txps_uuid16s[TX_POWER_SERVICE_CLIENT_NUM_CHARACTERISTICS] = { 69 ORG_BLUETOOTH_CHARACTERISTIC_TX_POWER_LEVEL 70 }; 71 72 typedef enum { 73 TXPS_CLIENT_CHARACTERISTIC_INDEX_TX_POWER_LEVEL = 0, 74 TXPS_CLIENT_CHARACTERISTIC_INDEX_RFU 75 } txps_client_characteristic_index_t; 76 77 #ifdef ENABLE_TESTING_SUPPORT 78 static const char * txps_characteristic_names[] = { 79 "TX_POWER_LEVEL", 80 "RFU" 81 }; 82 #endif 83 84 85 static txps_client_connection_t * txps_client_get_connection_for_cid(uint16_t connection_cid){ 86 btstack_linked_list_iterator_t it; 87 btstack_linked_list_iterator_init(&it, &txps_connections); 88 while (btstack_linked_list_iterator_has_next(&it)){ 89 txps_client_connection_t * connection = (txps_client_connection_t *)btstack_linked_list_iterator_next(&it); 90 if (gatt_service_client_get_connection_id(&connection->basic_connection) == connection_cid) { 91 return connection; 92 } 93 } 94 return NULL; 95 } 96 97 static void txps_client_add_connection(txps_client_connection_t * connection){ 98 btstack_linked_list_add(&txps_connections, (btstack_linked_item_t*) connection); 99 } 100 101 static void txps_client_finalize_connection(txps_client_connection_t * connection){ 102 btstack_linked_list_remove(&txps_connections, (btstack_linked_item_t*) connection); 103 } 104 105 106 static void txps_client_replace_subevent_id_and_emit(btstack_packet_handler_t callback, uint8_t * packet, uint16_t size, uint8_t subevent_id){ 107 UNUSED(size); 108 btstack_assert(callback != NULL); 109 // execute callback 110 packet[2] = subevent_id; 111 (*callback)(HCI_EVENT_PACKET, 0, packet, size); 112 } 113 114 static void txps_client_connected(txps_client_connection_t * connection, uint8_t status, uint8_t * packet, uint16_t size) { 115 if (status == ERROR_CODE_SUCCESS){ 116 connection->state = TX_POWER_SERVICE_CLIENT_STATE_READY; 117 } else { 118 connection->state = TX_POWER_SERVICE_CLIENT_STATE_IDLE; 119 } 120 txps_client_replace_subevent_id_and_emit(connection->packet_handler, packet, size, 121 GATTSERVICE_SUBEVENT_TXPS_CLIENT_CONNECTED); 122 } 123 124 static void txps_client_emit_uint8(uint16_t cid, btstack_packet_handler_t event_callback, uint8_t subevent, const uint8_t * data, uint16_t data_size){ 125 UNUSED(data_size); 126 btstack_assert(event_callback != NULL); 127 128 if (data_size != 1){ 129 return; 130 } 131 132 uint8_t event[6]; 133 uint16_t pos = 0; 134 event[pos++] = HCI_EVENT_GATTSERVICE_META; 135 event[pos++] = 4; 136 event[pos++] = subevent; 137 little_endian_store_16(event, pos, cid); 138 pos+= 2; 139 event[pos++] = data[0]; 140 141 (*event_callback)(HCI_EVENT_PACKET, 0, event, pos); 142 } 143 144 static uint16_t txps_client_value_handle_for_index(txps_client_connection_t * connection){ 145 return connection->basic_connection.characteristics[connection->characteristic_index].value_handle; 146 } 147 148 149 static void txps_client_emit_read_event(txps_client_connection_t * connection, uint8_t index, uint8_t status, const uint8_t * data, uint16_t data_size){ 150 UNUSED(status); 151 152 if ((data_size > 0) && (data == NULL)){ 153 return; 154 } 155 156 uint16_t characteristic_uuid16 = gatt_service_client_characteristic_uuid16_for_index(&txps_client, index); 157 switch (characteristic_uuid16){ 158 case ORG_BLUETOOTH_CHARACTERISTIC_TX_POWER_LEVEL: 159 txps_client_emit_uint8(connection->basic_connection.cid, connection->packet_handler, GATTSERVICE_SUBEVENT_TXPS_CLIENT_TX_POWER_LEVEL, data, data_size); 160 break; 161 default: 162 btstack_assert(false); 163 break; 164 } 165 } 166 167 static uint8_t txps_client_can_query_characteristic(txps_client_connection_t * connection, txps_client_characteristic_index_t characteristic_index){ 168 uint8_t status = gatt_service_client_can_query_characteristic(&connection->basic_connection, 169 (uint8_t) characteristic_index); 170 if (status != ERROR_CODE_SUCCESS){ 171 return status; 172 } 173 return connection->state == TX_POWER_SERVICE_CLIENT_STATE_READY ? ERROR_CODE_SUCCESS : ERROR_CODE_CONTROLLER_BUSY; 174 } 175 176 static uint8_t txps_client_request_send_gatt_query(txps_client_connection_t * connection, txps_client_characteristic_index_t characteristic_index){ 177 connection->characteristic_index = characteristic_index; 178 179 txps_client_handle_can_send_now.context = (void *)(uintptr_t)connection->basic_connection.cid; 180 uint8_t status = gatt_client_request_to_send_gatt_query(&txps_client_handle_can_send_now, connection->basic_connection.con_handle); 181 if (status != ERROR_CODE_SUCCESS){ 182 connection->state = TX_POWER_SERVICE_CLIENT_STATE_READY; 183 } 184 return status; 185 } 186 187 static uint8_t txps_client_request_read_characteristic(uint16_t cid, txps_client_characteristic_index_t characteristic_index){ 188 txps_client_connection_t * connection = txps_client_get_connection_for_cid(cid); 189 if (connection == NULL){ 190 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 191 } 192 uint8_t status = txps_client_can_query_characteristic(connection, characteristic_index); 193 if (status != ERROR_CODE_SUCCESS){ 194 return status; 195 } 196 197 connection->state = TX_POWER_SERVICE_CLIENT_STATE_W2_READ_CHARACTERISTIC_VALUE; 198 return txps_client_request_send_gatt_query(connection, characteristic_index); 199 } 200 201 static void txps_client_packet_handler_internal(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 202 UNUSED(channel); 203 UNUSED(size); 204 205 if (packet_type != HCI_EVENT_PACKET) return; 206 txps_client_connection_t * connection; 207 uint16_t cid; 208 uint8_t status; 209 210 switch(hci_event_packet_get_type(packet)){ 211 case HCI_EVENT_GATTSERVICE_META: 212 switch (hci_event_gattservice_meta_get_subevent_code(packet)) { 213 case GATTSERVICE_SUBEVENT_CLIENT_CONNECTED: 214 cid = gattservice_subevent_client_connected_get_cid(packet); 215 connection = txps_client_get_connection_for_cid(cid); 216 btstack_assert(connection != NULL); 217 218 #ifdef ENABLE_TESTING_SUPPORT 219 gatt_service_client_dump_characteristic_value_handles(&connection->basic_connection, 220 txps_characteristic_names); 221 #endif 222 status = gattservice_subevent_client_connected_get_status(packet); 223 txps_client_connected(connection, status, packet, size); 224 break; 225 226 case GATTSERVICE_SUBEVENT_CLIENT_DISCONNECTED: 227 // TODO reset client 228 cid = gattservice_subevent_client_disconnected_get_cid(packet); 229 connection = txps_client_get_connection_for_cid(cid); 230 btstack_assert(connection != NULL); 231 txps_client_finalize_connection(connection); 232 txps_client_replace_subevent_id_and_emit(connection->packet_handler, packet, size, 233 GATTSERVICE_SUBEVENT_TXPS_CLIENT_DISCONNECTED); 234 break; 235 236 default: 237 break; 238 } 239 break; 240 241 default: 242 break; 243 } 244 } 245 246 static void txps_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 247 UNUSED(packet_type); 248 UNUSED(channel); 249 UNUSED(size); 250 251 txps_client_connection_t * connection = NULL; 252 uint16_t connection_id; 253 254 switch(hci_event_packet_get_type(packet)){ 255 case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT: 256 connection_id = gatt_event_characteristic_value_query_result_get_connection_id(packet); 257 connection = txps_client_get_connection_for_cid(connection_id); 258 btstack_assert(connection != NULL); 259 260 txps_client_emit_read_event(connection, connection->characteristic_index, ATT_ERROR_SUCCESS, 261 gatt_event_characteristic_value_query_result_get_value(packet), 262 gatt_event_characteristic_value_query_result_get_value_length(packet)); 263 264 connection->state = TX_POWER_SERVICE_CLIENT_STATE_READY; 265 break; 266 267 case GATT_EVENT_QUERY_COMPLETE: 268 connection_id = gatt_event_query_complete_get_connection_id(packet); 269 connection = txps_client_get_connection_for_cid(connection_id); 270 btstack_assert(connection != NULL); 271 272 connection->state = TX_POWER_SERVICE_CLIENT_STATE_READY; 273 break; 274 275 default: 276 break; 277 } 278 } 279 280 static void txps_client_run_for_connection(void * context){ 281 uint16_t connection_id = (hci_con_handle_t)(uintptr_t)context; 282 txps_client_connection_t * connection = txps_client_get_connection_for_cid(connection_id); 283 284 btstack_assert(connection != NULL); 285 286 switch (connection->state){ 287 case TX_POWER_SERVICE_CLIENT_STATE_W2_READ_CHARACTERISTIC_VALUE: 288 connection->state = TX_POWER_SERVICE_CLIENT_STATE_W4_READ_CHARACTERISTIC_VALUE_RESULT; 289 290 (void) gatt_client_read_value_of_characteristic_using_value_handle_with_context( 291 &txps_client_handle_gatt_client_event, connection->basic_connection.cid, 292 txps_client_value_handle_for_index(connection), txps_client.service_id, connection->basic_connection.cid); 293 break; 294 295 default: 296 break; 297 } 298 } 299 300 void tx_power_service_client_init(void){ 301 gatt_service_client_register_client(&txps_client, &txps_client_packet_handler_internal, txps_uuid16s, sizeof(txps_uuid16s)/sizeof(uint16_t)); 302 303 txps_client_handle_can_send_now.callback = &txps_client_run_for_connection; 304 } 305 306 uint8_t tx_power_service_client_connect(hci_con_handle_t con_handle, 307 btstack_packet_handler_t packet_handler, 308 txps_client_connection_t * txps_connection, 309 gatt_service_client_characteristic_t * txps_storage_for_characteristics, uint8_t txps_characteristics_num, 310 uint16_t * txps_cid 311 ){ 312 313 btstack_assert(packet_handler != NULL); 314 btstack_assert(txps_connection != NULL); 315 btstack_assert(txps_characteristics_num == TX_POWER_SERVICE_CLIENT_NUM_CHARACTERISTICS); 316 317 *txps_cid = 0; 318 319 txps_connection->state = TX_POWER_SERVICE_CLIENT_STATE_W4_CONNECTION; 320 txps_connection->packet_handler = packet_handler; 321 322 uint8_t status = gatt_service_client_connect_primary_service_with_uuid16(con_handle, 323 &txps_client, 324 &txps_connection->basic_connection, 325 ORG_BLUETOOTH_SERVICE_TX_POWER, 0, 326 txps_storage_for_characteristics, 327 txps_characteristics_num); 328 if (status == ERROR_CODE_SUCCESS){ 329 txps_client_add_connection(txps_connection); 330 *txps_cid = txps_connection->basic_connection.cid; 331 } 332 333 return status; 334 } 335 336 uint8_t tx_power_service_client_read_tx_power_level(uint16_t txps_cid){ 337 return txps_client_request_read_characteristic(txps_cid, TXPS_CLIENT_CHARACTERISTIC_INDEX_TX_POWER_LEVEL); 338 } 339 340 void tx_power_service_client_deinit(void){ 341 gatt_service_client_unregister_client(&txps_client); 342 } 343 344