1 /* 2 * Copyright (C) 2014 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__ "battery_service_v1_server.c" 39 40 /** 41 * Implementation of the GATT Battery Service Server 42 * To use with your application, add `#import <battery_service.gatt>` to your .gatt file 43 */ 44 45 #include "btstack_defines.h" 46 #include "ble/att_db.h" 47 #include "ble/att_server.h" 48 #include "btstack_util.h" 49 #include "bluetooth_gatt.h" 50 #include "btstack_debug.h" 51 52 #define BS_CONNECTIONS_MAX_NUM 10 53 #include "ble/gatt-service/battery_service_v1_server.h" 54 55 #define BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED 0x0001 56 57 static btstack_linked_list_t battery_services; 58 59 60 static battery_service_v1_server_connection_t * battery_service_server_connection_for_con_handle(battery_service_v1_t * service, hci_con_handle_t con_handle){ 61 if (service == NULL){ 62 return NULL; 63 } 64 65 uint8_t i; 66 for (i = 0; i < service->connections_max_num; i++){ 67 if (service->connections[i].con_handle == con_handle){ 68 return &service->connections[i]; 69 } 70 } 71 return NULL; 72 } 73 74 static battery_service_v1_server_connection_t * battery_service_server_add_connection_for_con_handle(battery_service_v1_t * service, hci_con_handle_t con_handle){ 75 if (service == NULL){ 76 return NULL; 77 } 78 79 uint8_t i; 80 for (i = 0; i < service->connections_max_num; i++){ 81 if (service->connections[i].con_handle == HCI_CON_HANDLE_INVALID){ 82 service->connections[i].con_handle = con_handle; 83 service->connections[i].service = service; 84 return &service->connections[i]; 85 } 86 } 87 return NULL; 88 } 89 90 91 static battery_service_v1_t * battery_service_service_for_attribute_handle(uint16_t attribute_handle){ 92 btstack_linked_list_iterator_t it; 93 btstack_linked_list_iterator_init(&it, &battery_services); 94 while (btstack_linked_list_iterator_has_next(&it)){ 95 battery_service_v1_t * item = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it); 96 if (attribute_handle < item->start_handle) continue; 97 if (attribute_handle > item->end_handle) continue; 98 return item; 99 } 100 return NULL; 101 } 102 103 104 static battery_service_v1_t * battery_service_service_for_con_handle(hci_con_handle_t con_handle){ 105 btstack_linked_list_iterator_t it; 106 btstack_linked_list_iterator_init(&it, &battery_services); 107 while (btstack_linked_list_iterator_has_next(&it)){ 108 battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it); 109 uint8_t i; 110 for (i = 0; i < service->connections_max_num; i++){ 111 if (service->connections[i].con_handle == con_handle){ 112 return service; 113 } 114 } 115 } 116 return NULL; 117 } 118 119 120 static uint16_t battery_service_read_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){ 121 UNUSED(con_handle); 122 123 battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle); 124 if (service == NULL){ 125 return 0; 126 } 127 128 battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle); 129 if (connection == NULL){ 130 connection = battery_service_server_add_connection_for_con_handle(service, con_handle); 131 if (connection == NULL){ 132 return 0; 133 } 134 } 135 136 if (attribute_handle == service->battery_value_handle){ 137 return att_read_callback_handle_byte(service->battery_value, offset, buffer, buffer_size); 138 } 139 if (attribute_handle == service->battery_value_client_configuration_handle){ 140 return att_read_callback_handle_little_endian_16(connection->battery_value_client_configuration, offset, buffer, buffer_size); 141 } 142 return 0; 143 } 144 145 static int battery_service_write_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){ 146 UNUSED(offset); 147 UNUSED(buffer_size); 148 149 if (transaction_mode != ATT_TRANSACTION_MODE_NONE){ 150 return 0; 151 } 152 153 battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle); 154 if (service == NULL){ 155 return 0; 156 } 157 158 battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle); 159 if (connection == NULL){ 160 connection = battery_service_server_add_connection_for_con_handle(service, con_handle); 161 if (connection == NULL){ 162 return 0; 163 } 164 } 165 166 if (attribute_handle == service->battery_value_client_configuration_handle){ 167 connection->battery_value_client_configuration = little_endian_read_16(buffer, 0); 168 } 169 return 0; 170 } 171 172 static void battery_service_can_send_now(void * context){ 173 battery_service_v1_server_connection_t * connection = (battery_service_v1_server_connection_t *) context; 174 if (connection == NULL){ 175 return; 176 } 177 battery_service_v1_t * service = connection->service; 178 if (service == NULL){ 179 return; 180 } 181 182 if ( (connection->scheduled_tasks & BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED) > 0u){ 183 connection->scheduled_tasks &= ~BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED; 184 att_server_notify(connection->con_handle, service->battery_value_handle, &service->battery_value, 1); 185 } 186 187 if (connection->scheduled_tasks > 0u){ 188 att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle); 189 } 190 } 191 192 void battery_service_v1_server_init(void){ 193 194 } 195 196 void battery_service_v1_server_register(battery_service_v1_t *service, battery_service_v1_server_connection_t *connections, uint8_t connection_max_num){ 197 btstack_assert(service != NULL); 198 btstack_assert(connections != NULL); 199 btstack_assert(connection_max_num > 0u); 200 201 // get service handle range 202 uint16_t end_handle = 0xffff; 203 uint16_t start_handle = 0; 204 205 btstack_linked_list_iterator_t it; 206 btstack_linked_list_iterator_init(&it, &battery_services); 207 while (btstack_linked_list_iterator_has_next(&it)){ 208 battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it); 209 if (service->end_handle > start_handle){ 210 start_handle = service->end_handle + 1; 211 } 212 } 213 214 int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE, &start_handle, &end_handle); 215 btstack_assert(service_found != 0); 216 UNUSED(service_found); 217 218 service->start_handle = start_handle; 219 service->end_handle = end_handle; 220 221 // get characteristic value handle and client configuration handle 222 service->battery_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL); 223 service->battery_value_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL); 224 225 memset(connections, 0, sizeof(battery_service_v1_server_connection_t) * connection_max_num); 226 uint8_t i; 227 for (i = 0; i < connection_max_num; i++){ 228 connections[i].con_handle = HCI_CON_HANDLE_INVALID; 229 } 230 service->connections_max_num = connection_max_num; 231 service->connections = connections; 232 233 234 service->service_handler.read_callback = &battery_service_read_callback; 235 service->service_handler.write_callback = &battery_service_write_callback; 236 att_server_register_service_handler(&service->service_handler); 237 238 log_info("Found Battery Service 0x%02x-0x%02x", start_handle, end_handle); 239 240 btstack_linked_list_add(&battery_services, (btstack_linked_item_t *) service); 241 } 242 243 244 static void battery_service_set_callback(battery_service_v1_server_connection_t * connection, uint8_t task){ 245 if (connection->con_handle == HCI_CON_HANDLE_INVALID){ 246 connection->scheduled_tasks = 0; 247 return; 248 } 249 250 uint8_t scheduled_tasks = connection->scheduled_tasks; 251 connection->scheduled_tasks |= task; 252 if (scheduled_tasks == 0){ 253 connection->scheduled_tasks_callback.callback = &battery_service_can_send_now; 254 connection->scheduled_tasks_callback.context = (void*) connection; 255 att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle); 256 } 257 } 258 259 260 void battery_service_v1_server_set_battery_value(battery_service_v1_t * service, uint8_t value){ 261 btstack_assert(service != NULL); 262 if (service->battery_value == value){ 263 return; 264 } 265 266 service->battery_value = value; 267 268 uint8_t i; 269 for (i = 0; i < service->connections_max_num; i++){ 270 battery_service_v1_server_connection_t * connection = &service->connections[i]; 271 if (connection->battery_value_client_configuration != 0){ 272 battery_service_set_callback(connection, BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED); 273 } 274 } 275 } 276 277 void battery_service_v1_server_deregister(battery_service_v1_t *service){ 278 btstack_linked_list_iterator_remove((btstack_linked_item_t * )service); 279 // TODO cansel can send now 280 } 281 282 void battery_service_v1_server_deinit(void){ 283 // deregister listeners 284 // empty list 285 btstack_linked_list_iterator_t it; 286 btstack_linked_list_iterator_init(&it, &battery_services); 287 while (btstack_linked_list_iterator_has_next(&it)){ 288 battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it); 289 battery_service_v1_server_deregister(service); 290 } 291 } 292