protocomm_ble.c
1 // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <sys/param.h> 16 #include <esp_log.h> 17 #include <esp_gatt_common_api.h> 18 #include <esp_gap_bt_api.h> 19 20 #include <protocomm.h> 21 #include <protocomm_ble.h> 22 23 #include "protocomm_priv.h" 24 #include "simple_ble.h" 25 26 #define CHAR_VAL_LEN_MAX (256 + 1) 27 #define PREPARE_BUF_MAX_SIZE CHAR_VAL_LEN_MAX 28 29 static const char *TAG = "protocomm_ble"; 30 31 /* BLE specific configuration parameters */ 32 static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE; 33 static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE; 34 static const uint16_t character_user_description = ESP_GATT_UUID_CHAR_DESCRIPTION; 35 static const uint8_t character_prop_read_write = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE; 36 static const uint8_t ble_advertisement_flags = ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT; 37 38 typedef struct { 39 uint8_t type; 40 uint8_t length; 41 uint8_t *data_p; 42 } raw_data_info_t; 43 44 typedef struct { 45 uint8_t *prepare_buf; 46 int prepare_len; 47 uint16_t handle; 48 } prepare_type_env_t; 49 50 static prepare_type_env_t prepare_write_env; 51 52 typedef struct name_uuid128 { 53 const char *name; 54 uint8_t uuid128[ESP_UUID_LEN_128]; 55 } name_uuid128_t; 56 57 typedef struct _protocomm_ble { 58 protocomm_t *pc_ble; 59 name_uuid128_t *g_nu_lookup; 60 ssize_t g_nu_lookup_count; 61 uint16_t gatt_mtu; 62 uint8_t *service_uuid; 63 uint8_t *raw_adv_data_p; 64 uint8_t raw_adv_data_len; 65 uint8_t *raw_scan_rsp_data_p; 66 uint8_t raw_scan_rsp_data_len; 67 } _protocomm_ble_internal_t; 68 69 static _protocomm_ble_internal_t *protoble_internal; 70 71 static esp_ble_adv_params_t adv_params = { 72 .adv_int_min = 0x100, 73 .adv_int_max = 0x100, 74 .adv_type = ADV_TYPE_IND, 75 .own_addr_type = BLE_ADDR_TYPE_PUBLIC, 76 .channel_map = ADV_CHNL_ALL, 77 .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, 78 }; 79 80 static char* protocomm_ble_device_name = NULL; 81 82 static void hexdump(const char *msg, uint8_t *buf, int len) 83 { 84 ESP_LOGD(TAG, "%s:", msg); 85 ESP_LOG_BUFFER_HEX_LEVEL(TAG, buf, len, ESP_LOG_DEBUG); 86 } 87 88 static const uint16_t *uuid128_to_16(const uint8_t *uuid128) 89 { 90 return (const uint16_t *) &uuid128[12]; 91 } 92 93 static const char *handle_to_handler(uint16_t handle) 94 { 95 const uint8_t *uuid128 = simple_ble_get_uuid128(handle); 96 if (!uuid128) { 97 return NULL; 98 } 99 for (int i = 0; i < protoble_internal->g_nu_lookup_count; i++) { 100 if (*uuid128_to_16(protoble_internal->g_nu_lookup[i].uuid128) == *uuid128_to_16(uuid128)) { 101 return protoble_internal->g_nu_lookup[i].name; 102 } 103 } 104 return NULL; 105 } 106 107 static void transport_simple_ble_read(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) 108 { 109 static const uint8_t *read_buf = NULL; 110 static uint16_t read_len = 0; 111 static uint16_t max_read_len = 0; 112 esp_gatt_status_t status = ESP_OK; 113 114 ESP_LOGD(TAG, "Inside read w/ session - %d on param %d %d", 115 param->read.conn_id, param->read.handle, read_len); 116 if (!read_len && !param->read.offset) { 117 ESP_LOGD(TAG, "Reading attr value first time"); 118 status = esp_ble_gatts_get_attr_value(param->read.handle, &read_len, &read_buf); 119 max_read_len = read_len; 120 } else if ((read_len + param->read.offset) > max_read_len) { 121 status = ESP_GATT_INVALID_OFFSET; 122 } else { 123 ESP_LOGD(TAG, "Subsequent read request for attr value"); 124 } 125 126 esp_gatt_rsp_t gatt_rsp = {0}; 127 gatt_rsp.attr_value.handle = param->read.handle; 128 gatt_rsp.attr_value.offset = param->read.offset; 129 130 if (status == ESP_GATT_OK) { 131 gatt_rsp.attr_value.len = MIN(read_len, (protoble_internal->gatt_mtu - 1)); 132 gatt_rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; 133 if (gatt_rsp.attr_value.len && read_buf) { 134 memcpy(gatt_rsp.attr_value.value, 135 read_buf + param->read.offset, 136 gatt_rsp.attr_value.len); 137 } 138 read_len -= gatt_rsp.attr_value.len; 139 } else { 140 read_len = 0; 141 max_read_len = 0; 142 read_buf = NULL; 143 } 144 esp_err_t err = esp_ble_gatts_send_response(gatts_if, param->read.conn_id, 145 param->read.trans_id, status, &gatt_rsp); 146 if (err != ESP_OK) { 147 ESP_LOGE(TAG, "Send response error in read"); 148 } 149 } 150 151 static esp_err_t prepare_write_event_env(esp_gatt_if_t gatts_if, 152 esp_ble_gatts_cb_param_t *param) 153 { 154 ESP_LOGD(TAG, "prepare write, handle = %d, value len = %d, offset = %d", 155 param->write.handle, param->write.len, param->write.offset); 156 esp_gatt_status_t status = ESP_GATT_OK; 157 158 /* Ensure that write data is not larger than max attribute length */ 159 if (param->write.offset > PREPARE_BUF_MAX_SIZE) { 160 status = ESP_GATT_INVALID_OFFSET; 161 } else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) { 162 status = ESP_GATT_INVALID_ATTR_LEN; 163 } else { 164 /* If prepare buffer is not allocated, then allocate it */ 165 if (prepare_write_env.prepare_buf == NULL) { 166 prepare_write_env.prepare_len = 0; 167 prepare_write_env.prepare_buf = (uint8_t *) malloc(PREPARE_BUF_MAX_SIZE * sizeof(uint8_t)); 168 if (prepare_write_env.prepare_buf == NULL) { 169 ESP_LOGE(TAG, "%s , failed to allocate prepare buf", __func__); 170 status = ESP_GATT_NO_RESOURCES; 171 } 172 } 173 } 174 175 /* If prepare buffer is allocated copy incoming data into it */ 176 if (status == ESP_GATT_OK) { 177 memcpy(prepare_write_env.prepare_buf + param->write.offset, 178 param->write.value, 179 param->write.len); 180 prepare_write_env.prepare_len += param->write.len; 181 prepare_write_env.handle = param->write.handle; 182 } 183 184 /* Send write response if needed */ 185 if (param->write.need_rsp) { 186 esp_err_t response_err; 187 /* If data was successfully appended to prepare buffer 188 * only then have it reflected in the response */ 189 if (status == ESP_GATT_OK) { 190 esp_gatt_rsp_t gatt_rsp = {0}; 191 gatt_rsp.attr_value.len = param->write.len; 192 gatt_rsp.attr_value.handle = param->write.handle; 193 gatt_rsp.attr_value.offset = param->write.offset; 194 gatt_rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; 195 if (gatt_rsp.attr_value.len && param->write.value) { 196 memcpy(gatt_rsp.attr_value.value, param->write.value, param->write.len); 197 } 198 response_err = esp_ble_gatts_send_response(gatts_if, 199 param->write.conn_id, param->write.trans_id, status, &gatt_rsp); 200 } else { 201 response_err = esp_ble_gatts_send_response(gatts_if, 202 param->write.conn_id, param->write.trans_id, status, NULL); 203 } 204 if (response_err != ESP_OK) { 205 ESP_LOGE(TAG, "Send response error in prep write"); 206 } 207 } 208 if (status != ESP_GATT_OK) { 209 if (prepare_write_env.prepare_buf) { 210 free(prepare_write_env.prepare_buf); 211 prepare_write_env.prepare_buf = NULL; 212 prepare_write_env.prepare_len = 0; 213 } 214 return ESP_FAIL; 215 } 216 return ESP_OK; 217 } 218 219 static void transport_simple_ble_write(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) 220 { 221 uint8_t *outbuf = NULL; 222 ssize_t outlen = 0; 223 esp_err_t ret; 224 225 ESP_LOGD(TAG, "Inside write with session - %d on attr handle = %d \nlen = %d, is_prep = %d", 226 param->write.conn_id, param->write.handle, param->write.len, param->write.is_prep); 227 228 if (param->write.is_prep) { 229 ret = prepare_write_event_env(gatts_if, param); 230 if (ret != ESP_OK) { 231 ESP_LOGE(TAG, "Error appending to prepare buffer"); 232 } 233 return; 234 } else { 235 ESP_LOGD(TAG, "is_prep not set"); 236 } 237 238 ret = protocomm_req_handle(protoble_internal->pc_ble, 239 handle_to_handler(param->write.handle), 240 param->write.conn_id, 241 param->write.value, 242 param->write.len, 243 &outbuf, &outlen); 244 if (ret == ESP_OK) { 245 ret = esp_ble_gatts_set_attr_value(param->write.handle, outlen, outbuf); 246 if (ret != ESP_OK) { 247 ESP_LOGE(TAG, "Failed to set the session attribute value"); 248 } 249 ret = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, 250 param->write.trans_id, ESP_GATT_OK, NULL); 251 if (ret != ESP_OK) { 252 ESP_LOGE(TAG, "Send response error in write"); 253 } 254 hexdump("Response from write", outbuf, outlen); 255 256 } else { 257 ESP_LOGE(TAG, "Invalid content received, killing connection"); 258 esp_ble_gatts_close(gatts_if, param->write.conn_id); 259 } 260 if (outbuf) { 261 free(outbuf); 262 } 263 } 264 265 static void transport_simple_ble_exec_write(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) 266 { 267 esp_err_t err; 268 uint8_t *outbuf = NULL; 269 ssize_t outlen = 0; 270 ESP_LOGD(TAG, "Inside exec_write w/ session - %d", param->exec_write.conn_id); 271 272 if ((param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) 273 && 274 prepare_write_env.prepare_buf) { 275 err = protocomm_req_handle(protoble_internal->pc_ble, 276 handle_to_handler(prepare_write_env.handle), 277 param->exec_write.conn_id, 278 prepare_write_env.prepare_buf, 279 prepare_write_env.prepare_len, 280 &outbuf, &outlen); 281 282 if (err != ESP_OK) { 283 ESP_LOGE(TAG, "Invalid content received, killing connection"); 284 esp_ble_gatts_close(gatts_if, param->exec_write.conn_id); 285 } else { 286 hexdump("Response from exec write", outbuf, outlen); 287 esp_ble_gatts_set_attr_value(prepare_write_env.handle, outlen, outbuf); 288 } 289 } 290 if (prepare_write_env.prepare_buf) { 291 free(prepare_write_env.prepare_buf); 292 prepare_write_env.prepare_buf = NULL; 293 prepare_write_env.prepare_len = 0; 294 } 295 296 err = esp_ble_gatts_send_response(gatts_if, param->exec_write.conn_id, param->exec_write.trans_id, ESP_GATT_OK, NULL); 297 if (err != ESP_OK) { 298 ESP_LOGE(TAG, "Send response error in exec write"); 299 } 300 if (outbuf) { 301 free(outbuf); 302 } 303 } 304 305 static void transport_simple_ble_disconnect(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) 306 { 307 esp_err_t ret; 308 ESP_LOGD(TAG, "Inside disconnect w/ session - %d", param->disconnect.conn_id); 309 if (protoble_internal->pc_ble->sec && 310 protoble_internal->pc_ble->sec->close_transport_session) { 311 ret = protoble_internal->pc_ble->sec->close_transport_session(protoble_internal->pc_ble->sec_inst, 312 param->disconnect.conn_id); 313 if (ret != ESP_OK) { 314 ESP_LOGE(TAG, "error closing the session after disconnect"); 315 } 316 } 317 protoble_internal->gatt_mtu = ESP_GATT_DEF_BLE_MTU_SIZE; 318 } 319 320 static void transport_simple_ble_connect(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) 321 { 322 esp_err_t ret; 323 ESP_LOGD(TAG, "Inside BLE connect w/ conn_id - %d", param->connect.conn_id); 324 if (protoble_internal->pc_ble->sec && 325 protoble_internal->pc_ble->sec->new_transport_session) { 326 ret = protoble_internal->pc_ble->sec->new_transport_session(protoble_internal->pc_ble->sec_inst, 327 param->connect.conn_id); 328 if (ret != ESP_OK) { 329 ESP_LOGE(TAG, "error creating the session"); 330 } 331 } 332 } 333 334 static void transport_simple_ble_set_mtu(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) 335 { 336 protoble_internal->gatt_mtu = param->mtu.mtu; 337 return; 338 } 339 340 static esp_err_t protocomm_ble_add_endpoint(const char *ep_name, 341 protocomm_req_handler_t req_handler, 342 void *priv_data) 343 { 344 /* Endpoint UUID already added when protocomm_ble_start() was called */ 345 return ESP_OK; 346 } 347 348 static esp_err_t protocomm_ble_remove_endpoint(const char *ep_name) 349 { 350 /* Endpoint UUID will be removed when protocomm_ble_stop() is called */ 351 return ESP_OK; 352 } 353 354 static ssize_t populate_gatt_db(esp_gatts_attr_db_t **gatt_db_generated) 355 { 356 int i; 357 /* Each endpoint requires 3 attributes: 358 * 1) for Characteristic Declaration 359 * 2) for Characteristic Value (for reading and writing to an endpoint) 360 * 3) for Characteristic User Description (endpoint name) 361 * 362 * Therefore, we need esp_gatts_attr_db_t of size 3 * number of endpoints + 1 for service 363 */ 364 ssize_t gatt_db_generated_entries = 3 * protoble_internal->g_nu_lookup_count + 1; 365 366 *gatt_db_generated = (esp_gatts_attr_db_t *) malloc(sizeof(esp_gatts_attr_db_t) * 367 (gatt_db_generated_entries)); 368 if ((*gatt_db_generated) == NULL) { 369 ESP_LOGE(TAG, "Failed to assign memory to gatt_db"); 370 return -1; 371 } 372 /* Declare service */ 373 (*gatt_db_generated)[0].attr_control.auto_rsp = ESP_GATT_RSP_BY_APP; 374 375 (*gatt_db_generated)[0].att_desc.uuid_length = ESP_UUID_LEN_16; 376 (*gatt_db_generated)[0].att_desc.uuid_p = (uint8_t *) &primary_service_uuid; 377 (*gatt_db_generated)[0].att_desc.perm = ESP_GATT_PERM_READ; 378 (*gatt_db_generated)[0].att_desc.max_length = ESP_UUID_LEN_128; 379 (*gatt_db_generated)[0].att_desc.length = ESP_UUID_LEN_128; 380 (*gatt_db_generated)[0].att_desc.value = protoble_internal->service_uuid; 381 382 /* Declare characteristics */ 383 for (i = 1 ; i < gatt_db_generated_entries ; i++) { 384 (*gatt_db_generated)[i].attr_control.auto_rsp = ESP_GATT_RSP_BY_APP; 385 386 if (i % 3 == 1) { 387 /* Characteristic Declaration */ 388 (*gatt_db_generated)[i].att_desc.perm = ESP_GATT_PERM_READ; 389 (*gatt_db_generated)[i].att_desc.uuid_length = ESP_UUID_LEN_16; 390 (*gatt_db_generated)[i].att_desc.uuid_p = (uint8_t *) &character_declaration_uuid; 391 (*gatt_db_generated)[i].att_desc.max_length = sizeof(uint8_t); 392 (*gatt_db_generated)[i].att_desc.length = sizeof(uint8_t); 393 (*gatt_db_generated)[i].att_desc.value = (uint8_t *) &character_prop_read_write; 394 } else if (i % 3 == 2) { 395 /* Characteristic Value */ 396 (*gatt_db_generated)[i].att_desc.perm = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; 397 (*gatt_db_generated)[i].att_desc.uuid_length = ESP_UUID_LEN_128; 398 (*gatt_db_generated)[i].att_desc.uuid_p = protoble_internal->g_nu_lookup[i / 3].uuid128; 399 (*gatt_db_generated)[i].att_desc.max_length = CHAR_VAL_LEN_MAX; 400 (*gatt_db_generated)[i].att_desc.length = 0; 401 (*gatt_db_generated)[i].att_desc.value = NULL; 402 } else { 403 /* Characteristic User Description (for keeping endpoint names) */ 404 (*gatt_db_generated)[i].att_desc.perm = ESP_GATT_PERM_READ; 405 (*gatt_db_generated)[i].att_desc.uuid_length = ESP_UUID_LEN_16; 406 (*gatt_db_generated)[i].att_desc.uuid_p = (uint8_t *) &character_user_description; 407 (*gatt_db_generated)[i].att_desc.max_length = strlen(protoble_internal->g_nu_lookup[i / 3 - 1].name); 408 (*gatt_db_generated)[i].att_desc.length = (*gatt_db_generated)[i].att_desc.max_length; 409 (*gatt_db_generated)[i].att_desc.value = (uint8_t *) protoble_internal->g_nu_lookup[i / 3 - 1].name; 410 } 411 } 412 return gatt_db_generated_entries; 413 } 414 415 static void protocomm_ble_cleanup(void) 416 { 417 if (protoble_internal) { 418 if (protoble_internal->g_nu_lookup) { 419 for (unsigned i = 0; i < protoble_internal->g_nu_lookup_count; i++) { 420 if (protoble_internal->g_nu_lookup[i].name) { 421 free((void *)protoble_internal->g_nu_lookup[i].name); 422 } 423 } 424 free(protoble_internal->g_nu_lookup); 425 } 426 free(protoble_internal->raw_adv_data_p); 427 free(protoble_internal->raw_scan_rsp_data_p); 428 free(protoble_internal); 429 protoble_internal = NULL; 430 } 431 if (protocomm_ble_device_name) { 432 free(protocomm_ble_device_name); 433 protocomm_ble_device_name = NULL; 434 } 435 } 436 437 esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *config) 438 { 439 if (!pc || !config || !config->device_name || !config->nu_lookup) { 440 return ESP_ERR_INVALID_ARG; 441 } 442 443 if (protoble_internal) { 444 ESP_LOGE(TAG, "Protocomm BLE already started"); 445 return ESP_FAIL; 446 } 447 448 /* Store BLE device name internally */ 449 protocomm_ble_device_name = strdup(config->device_name); 450 if (protocomm_ble_device_name == NULL) { 451 ESP_LOGE(TAG, "Error allocating memory for storing BLE device name"); 452 protocomm_ble_cleanup(); 453 return ESP_ERR_NO_MEM; 454 } 455 456 protoble_internal = (_protocomm_ble_internal_t *) calloc(1, sizeof(_protocomm_ble_internal_t)); 457 if (protoble_internal == NULL) { 458 ESP_LOGE(TAG, "Error allocating internal protocomm structure"); 459 protocomm_ble_cleanup(); 460 return ESP_ERR_NO_MEM; 461 } 462 463 protoble_internal->g_nu_lookup_count = config->nu_lookup_count; 464 protoble_internal->g_nu_lookup = malloc(config->nu_lookup_count * sizeof(name_uuid128_t)); 465 if (protoble_internal->g_nu_lookup == NULL) { 466 ESP_LOGE(TAG, "Error allocating internal name UUID table"); 467 protocomm_ble_cleanup(); 468 return ESP_ERR_NO_MEM; 469 } 470 471 for (unsigned i = 0; i < protoble_internal->g_nu_lookup_count; i++) { 472 memcpy(protoble_internal->g_nu_lookup[i].uuid128, config->service_uuid, ESP_UUID_LEN_128); 473 memcpy((uint8_t *)uuid128_to_16(protoble_internal->g_nu_lookup[i].uuid128), 474 &config->nu_lookup[i].uuid, ESP_UUID_LEN_16); 475 476 protoble_internal->g_nu_lookup[i].name = strdup(config->nu_lookup[i].name); 477 if (protoble_internal->g_nu_lookup[i].name == NULL) { 478 ESP_LOGE(TAG, "Error allocating internal name UUID entry"); 479 protocomm_ble_cleanup(); 480 return ESP_ERR_NO_MEM; 481 } 482 } 483 484 pc->add_endpoint = protocomm_ble_add_endpoint; 485 pc->remove_endpoint = protocomm_ble_remove_endpoint; 486 protoble_internal->pc_ble = pc; 487 protoble_internal->gatt_mtu = ESP_GATT_DEF_BLE_MTU_SIZE; 488 489 /* The BLE advertisement data (max 31 bytes) consists of: 490 * 1) Flags - 491 * Size : length (1 byte) + type (1 byte) + value (1 byte) = 3 bytes 492 * 2) Complete 128 bit UUID of the service - 493 * Size : length (1 byte) + type (1 byte) + value (16 bytes) = 18 bytes 494 * 495 * Remaining 31 - (3 + 18) = 10 bytes could be used for manufacturer data 496 * or something else in the future. 497 */ 498 raw_data_info_t adv_data[] = { 499 { /* Flags */ 500 .type = ESP_BLE_AD_TYPE_FLAG, 501 .length = sizeof(ble_advertisement_flags), 502 .data_p = (uint8_t *) &ble_advertisement_flags 503 }, 504 { /* 128 bit Service UUID */ 505 .type = ESP_BLE_AD_TYPE_128SRV_CMPL, 506 .length = ESP_UUID_LEN_128, 507 .data_p = (uint8_t *) config->service_uuid 508 }, 509 }; 510 511 /* Get the total raw data length required for above entries */ 512 uint8_t adv_data_len = 0; 513 for (uint8_t i = 0; i < (sizeof(adv_data)/sizeof(adv_data[0])); i++) { 514 /* Add extra bytes required per entry, i.e. 515 * length (1 byte) + type (1 byte) = 2 bytes */ 516 adv_data_len += adv_data[i].length + 2; 517 } 518 if (adv_data_len > ESP_BLE_ADV_DATA_LEN_MAX) { 519 ESP_LOGE(TAG, "Advertisement data too long = %d bytes", adv_data_len); 520 protocomm_ble_cleanup(); 521 return ESP_ERR_NO_MEM; 522 } 523 524 /* Allocate memory for the raw advertisement data */ 525 protoble_internal->raw_adv_data_len = adv_data_len; 526 protoble_internal->raw_adv_data_p = malloc(adv_data_len); 527 if (protoble_internal->raw_adv_data_p == NULL) { 528 ESP_LOGE(TAG, "Error allocating memory for raw advertisement data"); 529 protocomm_ble_cleanup(); 530 return ESP_ERR_NO_MEM; 531 } 532 533 /* Form the raw advertisement data using above entries */ 534 for (uint8_t i = 0, len = 0; i < (sizeof(adv_data)/sizeof(adv_data[0])); i++) { 535 protoble_internal->raw_adv_data_p[len++] = adv_data[i].length + 1; // + 1 byte for type 536 protoble_internal->raw_adv_data_p[len++] = adv_data[i].type; 537 memcpy(&protoble_internal->raw_adv_data_p[len], 538 adv_data[i].data_p, adv_data[i].length); 539 540 if (adv_data[i].type == ESP_BLE_AD_TYPE_128SRV_CMPL) { 541 /* Remember where the primary service UUID is kept in the 542 * raw advertisement data, so that it can be used while 543 * populating the GATT database 544 */ 545 protoble_internal->service_uuid = &protoble_internal->raw_adv_data_p[len]; 546 } 547 548 len += adv_data[i].length; 549 } 550 551 size_t ble_devname_len = strlen(protocomm_ble_device_name); 552 /* The BLE scan response (31 bytes) consists of: 553 * 1) Device name (complete / incomplete) - 554 * Size : The maximum supported name length 555 * will be 31 - 2 (length + type) = 29 bytes 556 * 557 * Any remaining space may be used for accommodating 558 * other fields in the future 559 */ 560 raw_data_info_t scan_resp_data[] = { 561 { /* If full device name can fit in the scan response then indicate 562 * that by setting type to "Complete Name", else set it to "Short Name" 563 * so that client can fetch full device name - after connecting - by 564 * reading the device name characteristic under GAP service */ 565 .type = (ble_devname_len > (ESP_BLE_SCAN_RSP_DATA_LEN_MAX - 2) ? 566 ESP_BLE_AD_TYPE_NAME_SHORT : ESP_BLE_AD_TYPE_NAME_CMPL), 567 .length = MIN(ble_devname_len, (ESP_BLE_SCAN_RSP_DATA_LEN_MAX - 2)), 568 .data_p = (uint8_t *) protocomm_ble_device_name 569 }, 570 }; 571 572 /* Get the total raw scan response data length required for above entries */ 573 uint8_t scan_resp_data_len = 0; 574 for (int i = 0; i < (sizeof(scan_resp_data)/sizeof(scan_resp_data[0])); i++) { 575 /* Add extra bytes required per entry, i.e. 576 * length (1 byte) + type (1 byte) = 2 bytes */ 577 scan_resp_data_len += scan_resp_data[i].length + 2; 578 } 579 if (scan_resp_data_len > ESP_BLE_SCAN_RSP_DATA_LEN_MAX) { 580 ESP_LOGE(TAG, "Scan response data too long = %d bytes", scan_resp_data_len); 581 protocomm_ble_cleanup(); 582 return ESP_ERR_NO_MEM; 583 } 584 585 /* Allocate memory for the raw scan response data */ 586 protoble_internal->raw_scan_rsp_data_len = scan_resp_data_len; 587 protoble_internal->raw_scan_rsp_data_p = malloc(scan_resp_data_len); 588 if (protoble_internal->raw_scan_rsp_data_p == NULL) { 589 ESP_LOGE(TAG, "Error allocating memory for raw response data"); 590 protocomm_ble_cleanup(); 591 return ESP_ERR_NO_MEM; 592 } 593 594 /* Form the raw scan response data using above entries */ 595 for (uint8_t i = 0, len = 0; i < (sizeof(scan_resp_data)/sizeof(scan_resp_data[0])); i++) { 596 protoble_internal->raw_scan_rsp_data_p[len++] = scan_resp_data[i].length + 1; // + 1 byte for type 597 protoble_internal->raw_scan_rsp_data_p[len++] = scan_resp_data[i].type; 598 memcpy(&protoble_internal->raw_scan_rsp_data_p[len], 599 scan_resp_data[i].data_p, scan_resp_data[i].length); 600 len += scan_resp_data[i].length; 601 } 602 603 simple_ble_cfg_t *ble_config = simple_ble_init(); 604 if (ble_config == NULL) { 605 ESP_LOGE(TAG, "Ran out of memory for BLE config"); 606 protocomm_ble_cleanup(); 607 return ESP_ERR_NO_MEM; 608 } 609 610 /* Set function pointers required for simple BLE layer */ 611 ble_config->read_fn = transport_simple_ble_read; 612 ble_config->write_fn = transport_simple_ble_write; 613 ble_config->exec_write_fn = transport_simple_ble_exec_write; 614 ble_config->disconnect_fn = transport_simple_ble_disconnect; 615 ble_config->connect_fn = transport_simple_ble_connect; 616 ble_config->set_mtu_fn = transport_simple_ble_set_mtu; 617 618 /* Set parameters required for advertising */ 619 ble_config->adv_params = adv_params; 620 621 ble_config->raw_adv_data_p = protoble_internal->raw_adv_data_p; 622 ble_config->raw_adv_data_len = protoble_internal->raw_adv_data_len; 623 ble_config->raw_scan_rsp_data_p = protoble_internal->raw_scan_rsp_data_p; 624 ble_config->raw_scan_rsp_data_len = protoble_internal->raw_scan_rsp_data_len; 625 626 ble_config->device_name = protocomm_ble_device_name; 627 ble_config->gatt_db_count = populate_gatt_db(&ble_config->gatt_db); 628 629 if (ble_config->gatt_db_count == -1) { 630 ESP_LOGE(TAG, "Invalid GATT database count"); 631 simple_ble_deinit(); 632 protocomm_ble_cleanup(); 633 return ESP_ERR_INVALID_STATE; 634 } 635 636 esp_err_t err = simple_ble_start(ble_config); 637 if (err != ESP_OK) { 638 ESP_LOGE(TAG, "simple_ble_start failed w/ error code 0x%x", err); 639 simple_ble_deinit(); 640 protocomm_ble_cleanup(); 641 return err; 642 } 643 644 prepare_write_env.prepare_buf = NULL; 645 ESP_LOGD(TAG, "Waiting for client to connect ......"); 646 return ESP_OK; 647 } 648 649 esp_err_t protocomm_ble_stop(protocomm_t *pc) 650 { 651 if ((pc != NULL) && 652 (protoble_internal != NULL ) && 653 (pc == protoble_internal->pc_ble)) { 654 esp_err_t ret = ESP_OK; 655 ret = simple_ble_stop(); 656 if (ret) { 657 ESP_LOGE(TAG, "BLE stop failed"); 658 } 659 simple_ble_deinit(); 660 protocomm_ble_cleanup(); 661 return ret; 662 } 663 return ESP_ERR_INVALID_ARG; 664 }