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 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__ "att_db_util.c" 39 40 #include <string.h> 41 #include <stdlib.h> 42 43 #include "ble/att_db_util.h" 44 #include "ble/att_db.h" 45 #include "ble/core.h" 46 #include "btstack_util.h" 47 #include "btstack_debug.h" 48 #include "bluetooth.h" 49 50 // ATT DB Storage 51 #ifndef HAVE_MALLOC 52 #ifdef MAX_ATT_DB_SIZE 53 static uint8_t att_db_storage[MAX_ATT_DB_SIZE]; 54 #else 55 #error Neither HAVE_MALLOC nor MAX_ATT_DB_SIZE is defined. 56 #endif 57 #endif 58 59 static uint8_t * att_db; 60 static uint16_t att_db_size; 61 static uint16_t att_db_max_size; 62 static uint16_t att_db_next_handle; 63 static uint16_t att_db_hash_len; 64 65 static void att_db_util_set_end_tag(void){ 66 // end tag 67 att_db[att_db_size] = 0; 68 att_db[att_db_size+1] = 0; 69 } 70 71 void att_db_util_init(void){ 72 #ifdef HAVE_MALLOC 73 att_db = (uint8_t*) malloc(128); 74 att_db_max_size = 128; 75 #else 76 att_db = att_db_storage; 77 att_db_max_size = sizeof(att_db_storage); 78 #endif 79 // store att version 80 att_db[0] = ATT_DB_VERSION; 81 att_db_size = 1; 82 att_db_next_handle = 1; 83 att_db_hash_len = 0; 84 att_db_util_set_end_tag(); 85 } 86 87 static bool att_db_util_hash_include_with_value(uint16_t uuid16){ 88 /* «Primary Service», «Secondary Service», «Included Service», «Characteristic», or «Characteristic Extended Properties» */ 89 switch (uuid16){ 90 case GATT_PRIMARY_SERVICE_UUID: 91 case GATT_SECONDARY_SERVICE_UUID: 92 case GATT_INCLUDE_SERVICE_UUID: 93 case GATT_CHARACTERISTICS_UUID: 94 case GATT_CHARACTERISTIC_EXTENDED_PROPERTIES: 95 return true; 96 default: 97 return false; 98 } 99 } 100 101 static bool att_db_util_hash_include_without_value(uint16_t uuid16){ 102 /* «Characteristic User Description», «Client Characteristic Configuration», «Server Characteristic Configuration», 103 * «Characteristic Aggregate Format», «Characteristic Format» */ 104 switch (uuid16){ 105 case GATT_CHARACTERISTIC_USER_DESCRIPTION: 106 case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION: 107 case GATT_SERVER_CHARACTERISTICS_CONFIGURATION: 108 case GATT_CHARACTERISTIC_PRESENTATION_FORMAT: 109 case GATT_CHARACTERISTIC_AGGREGATE_FORMAT: 110 return true; 111 default: 112 return false; 113 } 114 } 115 116 uint16_t att_db_util_hash_len(void){ 117 return att_db_hash_len; 118 } 119 120 /** 121 * asserts that the requested amount of bytes can be stored in the att_db 122 * @returns TRUE if space is available 123 */ 124 static int att_db_util_assert_space(uint16_t size){ 125 size += 2; // for end tag 126 if ((att_db_size + size) <= att_db_max_size) return 1; 127 #ifdef HAVE_MALLOC 128 int new_size = att_db_size + att_db_size / 2; 129 uint8_t * new_db = (uint8_t*) realloc(att_db, new_size); 130 if (!new_db) { 131 log_error("att_db: realloc failed"); 132 return 0; 133 } 134 att_db = new_db; 135 att_db_max_size = new_size; 136 att_set_db(att_db); // Update att_db with the new db 137 return 1; 138 #else 139 log_error("att_db: out of memory"); 140 return 0; 141 #endif 142 } 143 144 // attribute size in bytes (16), flags(16), handle (16), uuid (16/128), value(...) 145 146 // db endds with 0x00 0x00 147 148 static void att_db_util_add_attribute_uuid16(uint16_t uuid16, uint16_t flags, uint8_t * data, uint16_t data_len){ 149 int size = 2 + 2 + 2 + 2 + data_len; 150 if (!att_db_util_assert_space(size)) return; 151 little_endian_store_16(att_db, att_db_size, size); 152 att_db_size += 2; 153 little_endian_store_16(att_db, att_db_size, flags); 154 att_db_size += 2; 155 little_endian_store_16(att_db, att_db_size, att_db_next_handle); 156 att_db_size += 2; 157 att_db_next_handle++; 158 little_endian_store_16(att_db, att_db_size, uuid16); 159 att_db_size += 2; 160 (void)memcpy(&att_db[att_db_size], data, data_len); 161 att_db_size += data_len; 162 att_db_util_set_end_tag(); 163 164 if (att_db_util_hash_include_with_value(uuid16)){ 165 att_db_hash_len += 4 + data_len; 166 } else if (att_db_util_hash_include_without_value(uuid16)){ 167 att_db_hash_len += 4; 168 } 169 } 170 171 static void att_db_util_add_attribute_uuid128(const uint8_t * uuid128, uint16_t flags, uint8_t * data, uint16_t data_len){ 172 int size = 2 + 2 + 2 + 16 + data_len; 173 if (!att_db_util_assert_space(size)) return; 174 flags |= ATT_PROPERTY_UUID128; 175 little_endian_store_16(att_db, att_db_size, size); 176 att_db_size += 2; 177 little_endian_store_16(att_db, att_db_size, flags); 178 att_db_size += 2; 179 little_endian_store_16(att_db, att_db_size, att_db_next_handle); 180 att_db_size += 2; 181 att_db_next_handle++; 182 reverse_128(uuid128, &att_db[att_db_size]); 183 att_db_size += 16; 184 (void)memcpy(&att_db[att_db_size], data, data_len); 185 att_db_size += data_len; 186 att_db_util_set_end_tag(); 187 } 188 189 uint16_t att_db_util_add_service_uuid16(uint16_t uuid16){ 190 uint8_t buffer[2]; 191 little_endian_store_16(buffer, 0, uuid16); 192 uint16_t service_handle = att_db_next_handle; 193 att_db_util_add_attribute_uuid16(GATT_PRIMARY_SERVICE_UUID, ATT_PROPERTY_READ, buffer, 2); 194 return service_handle; 195 } 196 197 uint16_t att_db_util_add_service_uuid128(const uint8_t * uuid128){ 198 uint8_t buffer[16]; 199 reverse_128(uuid128, buffer); 200 uint16_t service_handle = att_db_next_handle; 201 att_db_util_add_attribute_uuid16(GATT_PRIMARY_SERVICE_UUID, ATT_PROPERTY_READ, buffer, 16); 202 return service_handle; 203 } 204 205 uint16_t att_db_util_add_secondary_service_uuid16(uint16_t uuid16){ 206 uint8_t buffer[2]; 207 little_endian_store_16(buffer, 0, uuid16); 208 uint16_t service_handle = att_db_next_handle; 209 att_db_util_add_attribute_uuid16(GATT_SECONDARY_SERVICE_UUID, ATT_PROPERTY_READ, buffer, 2); 210 return service_handle; 211 } 212 213 uint16_t att_db_util_add_secondary_service_uuid128(const uint8_t * uuid128){ 214 uint8_t buffer[16]; 215 reverse_128(uuid128, buffer); 216 uint16_t service_handle = att_db_next_handle; 217 att_db_util_add_attribute_uuid16(GATT_SECONDARY_SERVICE_UUID, ATT_PROPERTY_READ, buffer, 16); 218 return service_handle; 219 } 220 221 uint16_t att_db_util_add_included_service_uuid16(uint16_t start_group_handle, uint16_t end_group_handle, uint16_t uuid16){ 222 uint8_t buffer[6]; 223 little_endian_store_16(buffer, 0, start_group_handle); 224 little_endian_store_16(buffer, 2, end_group_handle); 225 little_endian_store_16(buffer, 4, uuid16); 226 uint16_t service_handle = att_db_next_handle; 227 att_db_util_add_attribute_uuid16(GATT_SECONDARY_SERVICE_UUID, ATT_PROPERTY_READ, buffer, sizeof(buffer)); 228 return service_handle; 229 } 230 231 static void att_db_util_add_client_characteristic_configuration(uint16_t flags){ 232 uint8_t buffer[2]; 233 // drop permission for read (0xc00), keep write permissions (0x0091) 234 flags = (flags & 0x1f391) | ATT_PROPERTY_READ | ATT_PROPERTY_WRITE | ATT_PROPERTY_DYNAMIC; 235 little_endian_store_16(buffer, 0, 0); 236 att_db_util_add_attribute_uuid16(GATT_CLIENT_CHARACTERISTICS_CONFIGURATION, flags, buffer, 2); 237 } 238 239 static uint16_t att_db_util_encode_permissions(uint16_t properties, uint8_t read_permission, uint8_t write_permission){ 240 // drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Attributes (0x80) - not used for flags 241 uint16_t flags = properties & 0xfff4e; 242 // if encryption requested, set encryption key size to 16 243 if ((read_permission > ATT_SECURITY_NONE) || (write_permission > ATT_SECURITY_NONE)){ 244 flags |= 0xf000; 245 } 246 // map SC requirement 247 if (read_permission == ATT_SECURITY_AUTHENTICATED_SC){ 248 read_permission = ATT_SECURITY_AUTHENTICATED; 249 flags |= ATT_PROPERTY_READ_PERMISSION_SC; 250 } 251 if (write_permission == ATT_SECURITY_AUTHENTICATED_SC){ 252 write_permission = ATT_SECURITY_AUTHENTICATED; 253 flags |= ATT_PROPERTY_WRITE_PERMISSION_SC; 254 } 255 // encode read/write security levels 256 if (read_permission & 1){ 257 flags |= ATT_PROPERTY_READ_PERMISSION_BIT_0; 258 } 259 if (read_permission & 2){ 260 flags |= ATT_PROPERTY_READ_PERMISSION_BIT_1; 261 } 262 if (write_permission & 1){ 263 flags |= ATT_PROPERTY_WRITE_PERMISSION_BIT_0; 264 } 265 if (write_permission & 2){ 266 flags |= ATT_PROPERTY_WRITE_PERMISSION_BIT_1; 267 } 268 return flags; 269 } 270 271 uint16_t att_db_util_add_characteristic_uuid16(uint16_t uuid16, uint16_t properties, uint8_t read_permission, uint8_t write_permission, uint8_t * data, uint16_t data_len){ 272 uint8_t buffer[5]; 273 buffer[0] = properties; 274 little_endian_store_16(buffer, 1, att_db_next_handle + 1); 275 little_endian_store_16(buffer, 3, uuid16); 276 att_db_util_add_attribute_uuid16(GATT_CHARACTERISTICS_UUID, ATT_PROPERTY_READ, buffer, sizeof(buffer)); 277 uint16_t flags = att_db_util_encode_permissions(properties, read_permission, write_permission); 278 uint16_t value_handle = att_db_next_handle; 279 att_db_util_add_attribute_uuid16(uuid16, flags, data, data_len); 280 if (properties & (ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE)){ 281 att_db_util_add_client_characteristic_configuration(flags); 282 } 283 return value_handle; 284 } 285 286 uint16_t att_db_util_add_characteristic_uuid128(const uint8_t * uuid128, uint16_t properties, uint8_t read_permission, uint8_t write_permission, uint8_t * data, uint16_t data_len){ 287 uint8_t buffer[19]; 288 buffer[0] = properties; 289 little_endian_store_16(buffer, 1, att_db_next_handle + 1); 290 reverse_128(uuid128, &buffer[3]); 291 att_db_util_add_attribute_uuid16(GATT_CHARACTERISTICS_UUID, ATT_PROPERTY_READ, buffer, sizeof(buffer)); 292 uint16_t flags = att_db_util_encode_permissions(properties, read_permission, write_permission); 293 uint16_t value_handle = att_db_next_handle; 294 att_db_util_add_attribute_uuid128(uuid128, flags, data, data_len); 295 if (properties & (ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE)){ 296 att_db_util_add_client_characteristic_configuration(flags); 297 } 298 return value_handle; 299 } 300 301 uint16_t att_db_util_add_descriptor_uuid16(uint16_t uuid16, uint16_t properties, uint8_t read_permission, uint8_t write_permission, uint8_t * data, uint16_t data_len){ 302 uint16_t descriptor_handler = att_db_next_handle; 303 uint16_t flags = att_db_util_encode_permissions(properties, read_permission, write_permission); 304 att_db_util_add_attribute_uuid16(uuid16, flags, data, data_len); 305 return descriptor_handler; 306 } 307 308 uint16_t att_db_util_add_descriptor_uuid128(const uint8_t * uuid128, uint16_t properties, uint8_t read_permission, uint8_t write_permission, uint8_t * data, uint16_t data_len){ 309 uint16_t descriptor_handler = att_db_next_handle; 310 uint16_t flags = att_db_util_encode_permissions(properties, read_permission, write_permission); 311 att_db_util_add_attribute_uuid128(uuid128, flags, data, data_len); 312 return descriptor_handler; 313 } 314 315 uint8_t * att_db_util_get_address(void){ 316 return att_db; 317 } 318 319 uint16_t att_db_util_get_size(void){ 320 return att_db_size + 2; // end tag 321 } 322