protocomm_nimble.c
1 // Copyright 2019 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 <string.h> 18 #include "nvs_flash.h" 19 20 #include <protocomm.h> 21 #include <protocomm_ble.h> 22 #include "protocomm_priv.h" 23 24 /* NimBLE */ 25 #include "esp_nimble_hci.h" 26 #include "nimble/nimble_port.h" 27 #include "nimble/nimble_port_freertos.h" 28 #include "host/ble_hs.h" 29 #include "host/ble_uuid.h" 30 #include "host/util/util.h" 31 #include "services/gap/ble_svc_gap.h" 32 33 static const char *TAG = "protocomm_nimble"; 34 35 int ble_uuid_flat(const ble_uuid_t *, void *); 36 static uint8_t ble_uuid_base[BLE_UUID128_VAL_LENGTH]; 37 static int num_chr_dsc; 38 39 /* Standard 16 bit UUID for characteristic User Description*/ 40 #define BLE_GATT_UUID_CHAR_DSC 0x2901 41 42 /******************************************************** 43 * Maintain database for Attribute specific data * 44 ********************************************************/ 45 struct data_mbuf { 46 SLIST_ENTRY(data_mbuf) node; 47 uint8_t *outbuf; 48 ssize_t outlen; 49 uint16_t attr_handle; 50 }; 51 52 static SLIST_HEAD(data_mbuf_head, data_mbuf) data_mbuf_list = 53 SLIST_HEAD_INITIALIZER(data_mbuf_list); 54 55 static struct data_mbuf *find_attr_with_handle(uint16_t attr_handle) 56 { 57 struct data_mbuf *cur; 58 SLIST_FOREACH(cur, &data_mbuf_list, node) { 59 if (cur->attr_handle == attr_handle) { 60 return cur; 61 } 62 } 63 return NULL; 64 } 65 /************************************************************** 66 * Initialize GAP, protocomm parameters * 67 **************************************************************/ 68 static int simple_ble_gap_event(struct ble_gap_event *event, void *arg); 69 static uint8_t own_addr_type; 70 void ble_store_config_init(void); 71 72 typedef struct _protocomm_ble { 73 protocomm_t *pc_ble; 74 protocomm_ble_name_uuid_t *g_nu_lookup; 75 ssize_t g_nu_lookup_count; 76 uint16_t gatt_mtu; 77 } _protocomm_ble_internal_t; 78 79 static _protocomm_ble_internal_t *protoble_internal; 80 static struct ble_gap_adv_params adv_params; 81 static char *protocomm_ble_device_name; 82 static struct ble_hs_adv_fields adv_data, resp_data; 83 84 /********************************************************************** 85 * Maintain database of uuid_name addresses to free memory afterwards * 86 **********************************************************************/ 87 struct uuid128_name_buf { 88 SLIST_ENTRY(uuid128_name_buf) link; 89 ble_uuid128_t *uuid128_name_table; 90 }; 91 92 static SLIST_HEAD(uuid128_name_buf_head, uuid128_name_buf) uuid128_name_list = 93 SLIST_HEAD_INITIALIZER(uuid128_name_list); 94 95 /********************************************************************** 96 * Initialize simple BLE parameters, advertisement, scan response etc * 97 **********************************************************************/ 98 static int 99 gatt_svr_chr_access(uint16_t conn_handle, uint16_t attr_handle, 100 struct ble_gatt_access_ctxt *ctxt, 101 void *arg); 102 static int 103 gatt_svr_dsc_access(uint16_t conn_handle, uint16_t attr_handle, 104 struct ble_gatt_access_ctxt *ctxt, 105 void *arg); 106 107 void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); 108 109 typedef void (simple_ble_cb_t)(struct ble_gap_event *event, void *arg); 110 static void transport_simple_ble_connect(struct ble_gap_event *event, void *arg); 111 static void transport_simple_ble_disconnect(struct ble_gap_event *event, void *arg); 112 static void transport_simple_ble_set_mtu(struct ble_gap_event *event, void *arg); 113 114 typedef struct { 115 /** Name to be displayed to devices scanning for ESP32 */ 116 const char *device_name; 117 /** Advertising data content, according to "Supplement to the Bluetooth Core Specification" */ 118 struct ble_hs_adv_fields adv_data; 119 /** Parameters to configure the nature of advertising */ 120 struct ble_gap_adv_params adv_params; 121 /** Descriptor table which consists the configuration required by services and characteristics */ 122 struct ble_gatt_svc_def *gatt_db; 123 /** Client disconnect callback */ 124 simple_ble_cb_t *disconnect_fn; 125 /** Client connect callback */ 126 simple_ble_cb_t *connect_fn; 127 /** MTU set callback */ 128 simple_ble_cb_t *set_mtu_fn; 129 } simple_ble_cfg_t; 130 131 static simple_ble_cfg_t *ble_cfg_p; 132 133 /************************************************************ 134 * Functions to set and get attr value based on attr Handle * 135 ************************************************************/ 136 static int simple_ble_gatts_set_attr_value(uint16_t attr_handle, ssize_t outlen, 137 uint8_t *outbuf) 138 { 139 struct data_mbuf *attr_mbuf = find_attr_with_handle(attr_handle); 140 if (!attr_mbuf) { 141 attr_mbuf = calloc(1, sizeof(struct data_mbuf)); 142 if (!attr_mbuf) { 143 ESP_LOGE(TAG, "Failed to allocate memory for storing outbuf and outlen"); 144 return ESP_ERR_NO_MEM; 145 } 146 SLIST_INSERT_HEAD(&data_mbuf_list, attr_mbuf, node); 147 attr_mbuf->attr_handle = attr_handle; 148 } else { 149 free(attr_mbuf->outbuf); 150 } 151 attr_mbuf->outbuf = outbuf; 152 attr_mbuf->outlen = outlen; 153 return ESP_OK; 154 } 155 156 static int simple_ble_gatts_get_attr_value(uint16_t attr_handle, ssize_t 157 *outlen, uint8_t **outbuf) 158 { 159 struct data_mbuf *attr_mbuf = find_attr_with_handle(attr_handle); 160 if (!attr_mbuf) { 161 ESP_LOGE(TAG, "Outbuf with handle %d not found", attr_handle); 162 return ESP_ERR_NOT_FOUND; 163 } 164 *outbuf = attr_mbuf->outbuf; 165 *outlen = attr_mbuf->outlen; 166 return ESP_OK; 167 } 168 169 /*****************************************************************************************/ 170 /* SIMPLE BLE INTEGRATION */ 171 /*****************************************************************************************/ 172 static void 173 simple_ble_advertise(void) 174 { 175 int rc; 176 177 adv_data.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); 178 adv_data.num_uuids128 = 1; 179 adv_data.uuids128_is_complete = 1; 180 181 rc = ble_gap_adv_set_fields(&adv_data); 182 if (rc != 0) { 183 ESP_LOGE(TAG, "Error setting advertisement data; rc = %d", rc); 184 return; 185 } 186 187 rc = ble_gap_adv_rsp_set_fields((const struct ble_hs_adv_fields *) &resp_data); 188 if (rc != 0) { 189 ESP_LOGE(TAG, "Error in setting scan response; rc = %d", rc); 190 return; 191 } 192 193 adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; 194 adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; 195 adv_params.itvl_min = 0x100; 196 adv_params.itvl_max = 0x100; 197 198 rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, 199 &adv_params, simple_ble_gap_event, NULL); 200 if (rc != 0) { 201 /* If BLE Host is disabled, it probably means device is already 202 * provisioned in previous session. Avoid error prints for this case.*/ 203 if (rc == BLE_HS_EDISABLED) { 204 ESP_LOGD(TAG, "BLE Host is disabled !!"); 205 } else { 206 ESP_LOGE(TAG, "Error enabling advertisement; rc = %d", rc); 207 } 208 return; 209 } 210 /* Take note of free heap space */ 211 ESP_LOGD(TAG, "Minimum free heap size = %d, free Heap size = %d", 212 esp_get_minimum_free_heap_size(), esp_get_free_heap_size()); 213 } 214 215 static int 216 simple_ble_gap_event(struct ble_gap_event *event, void *arg) 217 { 218 struct ble_gap_conn_desc desc; 219 int rc; 220 221 switch (event->type) { 222 case BLE_GAP_EVENT_CONNECT: 223 /* A new connection was established or a connection attempt failed. */ 224 if (event->connect.status == 0) { 225 transport_simple_ble_connect(event, arg); 226 rc = ble_gap_conn_find(event->connect.conn_handle, &desc); 227 if (rc != 0) { 228 ESP_LOGE(TAG, "No open connection with the specified handle"); 229 return rc; 230 } 231 } else { 232 /* Connection failed; resume advertising. */ 233 simple_ble_advertise(); 234 } 235 return 0; 236 237 case BLE_GAP_EVENT_DISCONNECT: 238 ESP_LOGD(TAG, "disconnect; reason=%d ", event->disconnect.reason); 239 transport_simple_ble_disconnect(event, arg); 240 241 /* Connection terminated; resume advertising. */ 242 simple_ble_advertise(); 243 return 0; 244 245 case BLE_GAP_EVENT_ADV_COMPLETE: 246 simple_ble_advertise(); 247 return 0; 248 249 case BLE_GAP_EVENT_MTU: 250 ESP_LOGI(TAG, "mtu update event; conn_handle=%d cid=%d mtu=%d\n", 251 event->mtu.conn_handle, 252 event->mtu.channel_id, 253 event->mtu.value); 254 transport_simple_ble_set_mtu(event, arg); 255 return 0; 256 } 257 return 0; 258 } 259 260 /* Gets `g_nu_lookup name handler` from 128 bit UUID */ 261 static const char *uuid128_to_handler(uint8_t *uuid) 262 { 263 /* Use it to convert 128 bit UUID to 16 bit UUID.*/ 264 uint8_t *uuid16 = uuid + 12; 265 for (int i = 0; i < protoble_internal->g_nu_lookup_count; i++) { 266 if (protoble_internal->g_nu_lookup[i].uuid == *(uint16_t *)uuid16 ) { 267 ESP_LOGD(TAG, "UUID (0x%x) matched with proto-name = %s", *uuid16, protoble_internal->g_nu_lookup[i].name); 268 return protoble_internal->g_nu_lookup[i].name; 269 } else { 270 ESP_LOGD(TAG, "UUID did not match... %x", *uuid16); 271 } 272 } 273 return NULL; 274 } 275 276 /* Callback to handle GATT characteristic descriptor read */ 277 static int 278 gatt_svr_dsc_access(uint16_t conn_handle, uint16_t attr_handle, struct 279 ble_gatt_access_ctxt *ctxt, void *arg) 280 { 281 if (ctxt->op != BLE_GATT_ACCESS_OP_READ_DSC) { 282 ESP_LOGE(TAG, "Invalid operation on Read-only Descriptor"); 283 return BLE_ATT_ERR_UNLIKELY; 284 } 285 286 int rc; 287 char *temp_outbuf = strdup(ctxt->dsc->arg); 288 if (temp_outbuf == NULL) { 289 ESP_LOGE(TAG, "Error duplicating user description of characteristic"); 290 return BLE_ATT_ERR_INSUFFICIENT_RES; 291 } 292 293 ssize_t temp_outlen = strlen(temp_outbuf); 294 rc = os_mbuf_append(ctxt->om, temp_outbuf, temp_outlen); 295 free(temp_outbuf); 296 return rc; 297 } 298 299 /* Callback to handle GATT characteristic value Read & Write */ 300 static int 301 gatt_svr_chr_access(uint16_t conn_handle, uint16_t attr_handle, 302 struct ble_gatt_access_ctxt *ctxt, 303 void *arg) 304 { 305 int rc; 306 esp_err_t ret; 307 char buf[BLE_UUID_STR_LEN]; 308 ssize_t temp_outlen = 0; 309 uint8_t *temp_outbuf = NULL; 310 uint8_t *uuid = NULL; 311 uint8_t *data_buf = NULL; 312 uint16_t data_len = 0; 313 uint16_t data_buf_len = 0; 314 315 switch (ctxt->op) { 316 case BLE_GATT_ACCESS_OP_READ_CHR: 317 ESP_LOGD(TAG, "Read attempeted for Characterstic UUID = %s, attr_handle = %d", 318 ble_uuid_to_str(ctxt->chr->uuid, buf), attr_handle); 319 320 rc = simple_ble_gatts_get_attr_value(attr_handle, &temp_outlen, 321 &temp_outbuf); 322 if (rc != 0) { 323 ESP_LOGE(TAG, "Failed to read characteristic with attr_handle = %d", attr_handle); 324 return rc; 325 } 326 327 rc = os_mbuf_append(ctxt->om, temp_outbuf, temp_outlen); 328 return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; 329 330 case BLE_GATT_ACCESS_OP_WRITE_CHR: 331 uuid = (uint8_t *) calloc(BLE_UUID128_VAL_LENGTH, sizeof(uint8_t)); 332 if (!uuid) { 333 ESP_LOGE(TAG, "Error allocating memory for 128 bit UUID"); 334 return BLE_ATT_ERR_INSUFFICIENT_RES; 335 } 336 337 rc = ble_uuid_flat(ctxt->chr->uuid, uuid); 338 if (rc != 0) { 339 free(uuid); 340 ESP_LOGE(TAG, "Error fetching Characteristic UUID128"); 341 return rc; 342 } 343 344 /* Save the length of entire data */ 345 data_len = OS_MBUF_PKTLEN(ctxt->om); 346 ESP_LOGD(TAG, "Write attempt for uuid = %s, attr_handle = %d, data_len = %d", 347 ble_uuid_to_str(ctxt->chr->uuid, buf), attr_handle, data_len); 348 349 data_buf = calloc(1, data_len); 350 if (data_buf == NULL) { 351 ESP_LOGE(TAG, "Error allocating memory for characteristic value"); 352 return BLE_ATT_ERR_INSUFFICIENT_RES; 353 } 354 355 rc = ble_hs_mbuf_to_flat(ctxt->om, data_buf, data_len, &data_buf_len); 356 if (rc != 0) { 357 ESP_LOGE(TAG, "Error getting data from memory buffers"); 358 return BLE_ATT_ERR_UNLIKELY; 359 } 360 361 ret = protocomm_req_handle(protoble_internal->pc_ble, 362 uuid128_to_handler(uuid), 363 conn_handle, 364 data_buf, 365 data_buf_len, 366 &temp_outbuf, &temp_outlen); 367 /* Release the 16 bytes allocated for uuid*/ 368 free(uuid); 369 free(data_buf); 370 if (ret == ESP_OK) { 371 372 /* Save data address and length outbuf and outlen internally */ 373 rc = simple_ble_gatts_set_attr_value(attr_handle, temp_outlen, 374 temp_outbuf); 375 if (rc != 0) { 376 ESP_LOGE(TAG, "Failed to set outbuf for characteristic with attr_handle = %d", 377 attr_handle); 378 free(temp_outbuf); 379 } 380 381 return rc; 382 } else { 383 ESP_LOGE(TAG, "Invalid content received, killing connection"); 384 return BLE_ATT_ERR_INVALID_PDU; 385 } 386 387 default: 388 return BLE_ATT_ERR_UNLIKELY; 389 } 390 } 391 392 void 393 gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) 394 { 395 char buf[BLE_UUID_STR_LEN]; 396 397 switch (ctxt->op) { 398 case BLE_GATT_REGISTER_OP_SVC: 399 ESP_LOGD(TAG, "registering service %s with handle=%d TYPE =%d", 400 ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), 401 ctxt->svc.handle, ctxt->svc.svc_def->uuid->type); 402 break; 403 404 case BLE_GATT_REGISTER_OP_CHR: 405 ESP_LOGD(TAG, "registering characteristic %s with " 406 "def_handle=%d val_handle=%d , TYPE = %d", 407 ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), 408 ctxt->chr.def_handle, 409 ctxt->chr.val_handle, ctxt->chr.chr_def->uuid->type); 410 break; 411 412 case BLE_GATT_REGISTER_OP_DSC: 413 ESP_LOGD(TAG, "registering descriptor %s with handle=%d", 414 ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), 415 ctxt->dsc.handle); 416 break; 417 418 default: 419 assert(0); 420 break; 421 } 422 } 423 424 int 425 gatt_svr_init(const simple_ble_cfg_t *config) 426 { 427 int rc; 428 rc = ble_gatts_count_cfg(config->gatt_db); 429 if (rc != 0) { 430 return rc; 431 } 432 433 rc = ble_gatts_add_svcs(config->gatt_db); 434 if (rc != 0) { 435 return rc; 436 } 437 438 return 0; 439 } 440 441 static void 442 simple_ble_on_reset(int reason) 443 { 444 ESP_LOGE(TAG, "Resetting state; reason=%d\n", reason); 445 } 446 447 static void 448 simple_ble_on_sync(void) 449 { 450 int rc; 451 452 rc = ble_hs_util_ensure_addr(0); 453 if (rc != 0) { 454 ESP_LOGE(TAG, "Error loading address"); 455 return; 456 } 457 458 /* Figure out address to use while advertising (no privacy for now) */ 459 rc = ble_hs_id_infer_auto(0, &own_addr_type); 460 if (rc != 0) { 461 ESP_LOGE(TAG, "error determining address type; rc=%d\n", rc); 462 return; 463 } 464 465 /* Begin advertising. */ 466 simple_ble_advertise(); 467 } 468 469 void 470 nimble_host_task(void *param) 471 { 472 /* This function will return only when nimble_port_stop() is executed */ 473 ESP_LOGI(TAG, "BLE Host Task Started"); 474 nimble_port_run(); 475 476 nimble_port_freertos_deinit(); 477 } 478 479 static int simple_ble_start(const simple_ble_cfg_t *cfg) 480 { 481 ble_cfg_p = (void *)cfg; 482 int rc; 483 ESP_LOGD(TAG, "Free mem at start of simple_ble_init %d", esp_get_free_heap_size()); 484 485 ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init()); 486 nimble_port_init(); 487 488 /* Initialize the NimBLE host configuration. */ 489 ble_hs_cfg.reset_cb = simple_ble_on_reset; 490 ble_hs_cfg.sync_cb = simple_ble_on_sync; 491 ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; 492 493 rc = gatt_svr_init(cfg); 494 if (rc != 0) { 495 ESP_LOGE(TAG, "Error initializing GATT server"); 496 return rc; 497 } 498 499 /* Set device name, configure response data to be sent while advertising */ 500 rc = ble_svc_gap_device_name_set(cfg->device_name); 501 if (rc != 0) { 502 ESP_LOGE(TAG, "Error setting device name"); 503 return rc; 504 } 505 506 resp_data.name = (void *) ble_svc_gap_device_name(); 507 if (resp_data.name != NULL) { 508 resp_data.name_len = strlen(ble_svc_gap_device_name()); 509 resp_data.name_is_complete = 1; 510 } 511 512 /* XXX Need to have template for store */ 513 ble_store_config_init(); 514 nimble_port_freertos_init(nimble_host_task); 515 516 return 0; 517 } 518 519 /* transport_simple BLE Fn */ 520 static void transport_simple_ble_disconnect(struct ble_gap_event *event, void *arg) 521 { 522 esp_err_t ret; 523 ESP_LOGD(TAG, "Inside disconnect w/ session - %d", 524 event->disconnect.conn.conn_handle); 525 if (protoble_internal->pc_ble->sec && 526 protoble_internal->pc_ble->sec->close_transport_session) { 527 ret = 528 protoble_internal->pc_ble->sec->close_transport_session(protoble_internal->pc_ble->sec_inst, event->disconnect.conn.conn_handle); 529 if (ret != ESP_OK) { 530 ESP_LOGE(TAG, "error closing the session after disconnect"); 531 } 532 } 533 protoble_internal->gatt_mtu = BLE_ATT_MTU_DFLT; 534 } 535 536 static void transport_simple_ble_connect(struct ble_gap_event *event, void *arg) 537 { 538 esp_err_t ret; 539 ESP_LOGD(TAG, "Inside BLE connect w/ conn_id - %d", event->connect.conn_handle); 540 if (protoble_internal->pc_ble->sec && 541 protoble_internal->pc_ble->sec->new_transport_session) { 542 ret = 543 protoble_internal->pc_ble->sec->new_transport_session(protoble_internal->pc_ble->sec_inst, event->connect.conn_handle); 544 if (ret != ESP_OK) { 545 ESP_LOGE(TAG, "error creating the session"); 546 } 547 } 548 } 549 550 static void transport_simple_ble_set_mtu(struct ble_gap_event *event, void *arg) 551 { 552 protoble_internal->gatt_mtu = event->mtu.value; 553 return; 554 } 555 556 static esp_err_t protocomm_ble_add_endpoint(const char *ep_name, 557 protocomm_req_handler_t req_handler, 558 void *priv_data) 559 { 560 /* Endpoint UUID already added when protocomm_ble_start() was called */ 561 return ESP_OK; 562 } 563 564 static esp_err_t protocomm_ble_remove_endpoint(const char *ep_name) 565 { 566 /* Endpoint UUID will be removed when protocomm_ble_stop() is called */ 567 return ESP_OK; 568 } 569 570 /* Function to add descriptor to characteristic. The value of descriptor is 571 * filled with corresponding protocomm endpoint names. Characteristic address, 572 * its serial no. and XXX 16 bit standard UUID for descriptor to be provided as 573 * input parameters. Returns 0 on success and returns ESP_ERR_NO_MEM on 574 * failure. */ 575 static int 576 ble_gatt_add_char_dsc(struct ble_gatt_chr_def *characteristics, int idx, uint16_t dsc_uuid) 577 { 578 ble_uuid_t *uuid16 = BLE_UUID16_DECLARE(dsc_uuid); 579 580 /* Allocate memory for 2 descriptors, the 2nd descriptor shall be all NULLs 581 * to indicate End of Descriptors. */ 582 (characteristics + idx)->descriptors = (struct ble_gatt_dsc_def *) calloc(2, 583 sizeof(struct ble_gatt_dsc_def)); 584 if ((characteristics + idx)->descriptors == NULL) { 585 ESP_LOGE(TAG, "Error while allocating memory for characteristic descriptor"); 586 return ESP_ERR_NO_MEM; 587 } 588 589 (characteristics + idx)->descriptors[0].uuid = (ble_uuid_t *) calloc(1, 590 sizeof(ble_uuid16_t)); 591 if ((characteristics + idx)->descriptors[0].uuid == NULL) { 592 ESP_LOGE(TAG, "Error while allocating memory for characteristic descriptor"); 593 return ESP_ERR_NO_MEM; 594 } 595 memcpy((void *)(characteristics + idx)->descriptors[0].uuid, uuid16, 596 sizeof(ble_uuid16_t)); 597 (characteristics + idx)->descriptors[0].att_flags = BLE_ATT_F_READ; 598 (characteristics + idx)->descriptors[0].access_cb = gatt_svr_dsc_access; 599 (characteristics + idx)->descriptors[0].arg = (void *) 600 protoble_internal->g_nu_lookup[idx].name; 601 602 return 0; 603 } 604 605 /* Function to add characteristics to the service. For simplicity the 606 * flags and access callbacks are same for all the characteristics. The Fn 607 * requires pointer to characteristic of service and index of characteristic, 608 * depending upon the index no. individual characteristics can be handled in 609 * future. The fn also assumes that the required memory for all characteristics 610 * is already allocated while adding corresponding service. Returns 0 on 611 * success and returns ESP_ERR_NO_MEM on failure to add characteristic. */ 612 static int 613 ble_gatt_add_characteristics(struct ble_gatt_chr_def *characteristics, int idx) 614 { 615 /* Prepare 128 bit UUID of characteristics using custom base 128 616 * bit UUID and replacing byte 12 and 13 with corresponding protocom 617 * endpoint 16 bit UUID value. */ 618 ble_uuid128_t temp_uuid128_name = {0}; 619 temp_uuid128_name.u.type = BLE_UUID_TYPE_128; 620 memcpy(temp_uuid128_name.value, ble_uuid_base, BLE_UUID128_VAL_LENGTH); 621 memcpy(&temp_uuid128_name.value[12], &protoble_internal->g_nu_lookup[idx].uuid, 2); 622 623 (characteristics + idx)->flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE; 624 (characteristics + idx)->access_cb = gatt_svr_chr_access; 625 626 /* Out of 128 bit UUID, 16 bits from g_nu_lookup table. Currently 627 * g_nu_lookup table has 16 bit UUID, XXX this can be changed to 128 bit UUID 628 * in future. For time being continue using 16 bit UUID on top of base 128 629 * bit service UUID */ 630 (characteristics + idx)->uuid = (ble_uuid_t *)calloc(1, 631 sizeof(ble_uuid128_t)); 632 if ((characteristics + idx)->uuid == NULL) { 633 ESP_LOGE(TAG, "Error allocating memory for characteristic UUID"); 634 return ESP_ERR_NO_MEM; 635 } 636 memcpy((void *)(characteristics + idx)->uuid, &temp_uuid128_name, 637 sizeof(ble_uuid128_t)); 638 639 return 0; 640 } 641 642 /* Function to add primary service. It also allocates memory for the 643 * characteristics. Returns 0 on success, returns ESP_ERR_NO_MEM on failure to 644 * add service. */ 645 static int 646 ble_gatt_add_primary_svcs(struct ble_gatt_svc_def *gatt_db_svcs, int char_count) 647 { 648 /* Remember the count of characteristics here, as it will be used to free 649 * memory afterwards */ 650 num_chr_dsc = char_count; 651 gatt_db_svcs->type = BLE_GATT_SVC_TYPE_PRIMARY; 652 653 /* Allocate (number of characteristics + 1) memory for characteristics, the 654 * addtional characteristic consist of all 0s indicating end of 655 * characteristics */ 656 gatt_db_svcs->characteristics = (struct ble_gatt_chr_def *) calloc((char_count + 1), 657 sizeof(struct ble_gatt_chr_def)); 658 if (gatt_db_svcs->characteristics == NULL) { 659 ESP_LOGE(TAG, "Memory allocation for GATT characteristics failed"); 660 return ESP_ERR_NO_MEM; 661 } 662 return 0; 663 } 664 665 static int 666 populate_gatt_db(struct ble_gatt_svc_def **gatt_db_svcs, const protocomm_ble_config_t *config) 667 { 668 /* Allocate memory for 2 services, 2nd to be all NULL indicating end of 669 * services */ 670 *gatt_db_svcs = (struct ble_gatt_svc_def *) calloc(2, sizeof(struct ble_gatt_svc_def)); 671 if (*gatt_db_svcs == NULL) { 672 ESP_LOGE(TAG, "Error allocating memory for GATT services"); 673 return ESP_ERR_NO_MEM; 674 } 675 676 /* Allocate space for 1st service UUID as well, assume length = 128 bit */ 677 (*gatt_db_svcs)->uuid = (ble_uuid_t *) calloc(1, sizeof(ble_uuid128_t)); 678 if ((*gatt_db_svcs)->uuid == NULL) { 679 ESP_LOGE(TAG, "Error allocating memory for GATT service UUID"); 680 return ESP_ERR_NO_MEM; 681 } 682 683 /* Prepare 128 bit UUID for primary service from config service UUID. */ 684 ble_uuid128_t uuid128 = {0}; 685 uuid128.u.type = BLE_UUID_TYPE_128; 686 memcpy(uuid128.value, config->service_uuid, BLE_UUID128_VAL_LENGTH); 687 memcpy((void *) (*gatt_db_svcs)->uuid, &uuid128, sizeof(ble_uuid128_t)); 688 689 /* GATT: Add primary service. */ 690 int rc = ble_gatt_add_primary_svcs(*gatt_db_svcs, config->nu_lookup_count); 691 if (rc != 0) { 692 ESP_LOGE(TAG, "Error adding primary service !!!"); 693 return rc; 694 } 695 696 for (int i = 0 ; i < config->nu_lookup_count; i++) { 697 698 /* GATT: Add characteristics to the service at index no. i*/ 699 rc = ble_gatt_add_characteristics((void *) (*gatt_db_svcs)->characteristics, i); 700 if (rc != 0) { 701 ESP_LOGE(TAG, "Error adding GATT characteristic !!!"); 702 return rc; 703 } 704 /* GATT: Add user description to characteristic no. i*/ 705 rc = ble_gatt_add_char_dsc((void *) (*gatt_db_svcs)->characteristics, 706 i, BLE_GATT_UUID_CHAR_DSC); 707 if (rc != 0) { 708 ESP_LOGE(TAG, "Error adding GATT Discriptor !!"); 709 return rc; 710 } 711 } 712 return 0; 713 } 714 715 static void protocomm_ble_cleanup(void) 716 { 717 if (protoble_internal) { 718 if (protoble_internal->g_nu_lookup) { 719 for (unsigned i = 0; i < protoble_internal->g_nu_lookup_count; i++) { 720 if (protoble_internal->g_nu_lookup[i].name) { 721 free((void *)protoble_internal->g_nu_lookup[i].name); 722 } 723 } 724 free(protoble_internal->g_nu_lookup); 725 } 726 free(protoble_internal); 727 protoble_internal = NULL; 728 } 729 if (protocomm_ble_device_name) { 730 free(protocomm_ble_device_name); 731 protocomm_ble_device_name = NULL; 732 } 733 } 734 735 static void free_gatt_ble_misc_memory(simple_ble_cfg_t *ble_config) 736 { 737 /* Free up gatt_db memory if exists */ 738 if (ble_config->gatt_db->characteristics) { 739 for (int i = 0; i < num_chr_dsc; i++) { 740 if ((ble_config->gatt_db->characteristics + i)->descriptors) { 741 free((void *)(ble_config->gatt_db->characteristics + i)->descriptors->uuid); 742 free((ble_config->gatt_db->characteristics + i)->descriptors); 743 } 744 free((void *)(ble_config->gatt_db->characteristics + i)->uuid); 745 } 746 free((void *)(ble_config->gatt_db->characteristics)); 747 } 748 749 if (ble_config->gatt_db) { 750 free((void *)ble_config->gatt_db->uuid); 751 free(ble_config->gatt_db); 752 } 753 754 if (ble_config) { 755 free(ble_config); 756 } 757 ble_config = NULL; 758 759 /* Free the uuid_name_table struct list if exists */ 760 struct uuid128_name_buf *cur; 761 while (!SLIST_EMPTY(&uuid128_name_list)) { 762 cur = SLIST_FIRST(&uuid128_name_list); 763 SLIST_REMOVE_HEAD(&uuid128_name_list, link); 764 if (cur->uuid128_name_table) { 765 free(cur->uuid128_name_table); 766 } 767 free(cur); 768 } 769 770 /* Free the data_mbuf list if exists */ 771 struct data_mbuf *curr; 772 while (!SLIST_EMPTY(&data_mbuf_list)) { 773 curr = SLIST_FIRST(&data_mbuf_list); 774 SLIST_REMOVE_HEAD(&data_mbuf_list, node); 775 free(curr->outbuf); 776 free(curr); 777 } 778 } 779 780 esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *config) 781 { 782 /* copy the 128 bit service UUID into local buffer to use as base 128 bit 783 * UUID. */ 784 memcpy(ble_uuid_base, config->service_uuid, BLE_UUID128_VAL_LENGTH); 785 786 if (!pc || !config || !config->device_name || !config->nu_lookup) { 787 return ESP_ERR_INVALID_ARG; 788 } 789 790 if (protoble_internal) { 791 ESP_LOGE(TAG, "Protocomm BLE already started"); 792 return ESP_FAIL; 793 } 794 795 /* Store 128 bit service UUID internally. */ 796 ble_uuid128_t *svc_uuid128 = (ble_uuid128_t *) 797 calloc(1, sizeof(ble_uuid128_t)); 798 if (svc_uuid128 == NULL) { 799 ESP_LOGE(TAG, "Error while allocating memory for 128 bit UUID"); 800 return ESP_ERR_NO_MEM; 801 } 802 svc_uuid128->u.type = BLE_UUID_TYPE_128; 803 memcpy(svc_uuid128->value, config->service_uuid, BLE_UUID128_VAL_LENGTH); 804 adv_data.uuids128 = (void *)svc_uuid128; 805 806 /* Store service uuid128 in SLIST, to free it afterwards */ 807 struct uuid128_name_buf *temp_uuid128_name_buf = (struct uuid128_name_buf *) 808 calloc(1, sizeof(struct uuid128_name_buf)); 809 810 if (temp_uuid128_name_buf == NULL) { 811 ESP_LOGE(TAG, "Error allocating memory for UUID128 address database"); 812 return ESP_ERR_NO_MEM; 813 } 814 SLIST_INSERT_HEAD(&uuid128_name_list, temp_uuid128_name_buf, link); 815 temp_uuid128_name_buf->uuid128_name_table = svc_uuid128; 816 817 if (adv_data.uuids128 == NULL) { 818 ESP_LOGE(TAG, "Error allocating memory for storing service UUID"); 819 protocomm_ble_cleanup(); 820 return ESP_ERR_NO_MEM; 821 } 822 823 /* Store BLE device name internally */ 824 protocomm_ble_device_name = strdup(config->device_name); 825 826 if (protocomm_ble_device_name == NULL) { 827 ESP_LOGE(TAG, "Error allocating memory for storing BLE device name"); 828 protocomm_ble_cleanup(); 829 return ESP_ERR_NO_MEM; 830 } 831 832 protoble_internal = (_protocomm_ble_internal_t *) calloc(1, sizeof(_protocomm_ble_internal_t)); 833 if (protoble_internal == NULL) { 834 ESP_LOGE(TAG, "Error allocating internal protocomm structure"); 835 protocomm_ble_cleanup(); 836 return ESP_ERR_NO_MEM; 837 } 838 839 protoble_internal->g_nu_lookup_count = config->nu_lookup_count; 840 protoble_internal->g_nu_lookup = malloc(config->nu_lookup_count * sizeof(protocomm_ble_name_uuid_t)); 841 if (protoble_internal->g_nu_lookup == NULL) { 842 ESP_LOGE(TAG, "Error allocating internal name UUID table"); 843 protocomm_ble_cleanup(); 844 return ESP_ERR_NO_MEM; 845 } 846 847 for (unsigned i = 0; i < protoble_internal->g_nu_lookup_count; i++) { 848 protoble_internal->g_nu_lookup[i].uuid = config->nu_lookup[i].uuid; 849 protoble_internal->g_nu_lookup[i].name = strdup(config->nu_lookup[i].name); 850 if (protoble_internal->g_nu_lookup[i].name == NULL) { 851 ESP_LOGE(TAG, "Error allocating internal name UUID entry"); 852 protocomm_ble_cleanup(); 853 return ESP_ERR_NO_MEM; 854 } 855 } 856 857 pc->add_endpoint = protocomm_ble_add_endpoint; 858 pc->remove_endpoint = protocomm_ble_remove_endpoint; 859 protoble_internal->pc_ble = pc; 860 protoble_internal->gatt_mtu = BLE_ATT_MTU_DFLT; 861 862 simple_ble_cfg_t *ble_config = (simple_ble_cfg_t *) calloc(1, sizeof(simple_ble_cfg_t)); 863 if (ble_config == NULL) { 864 ESP_LOGE(TAG, "Ran out of memory for BLE config"); 865 protocomm_ble_cleanup(); 866 return ESP_ERR_NO_MEM; 867 } 868 869 /* Set function pointers required for simple BLE layer */ 870 ble_config->connect_fn = transport_simple_ble_connect; 871 ble_config->disconnect_fn = transport_simple_ble_disconnect; 872 ble_config->set_mtu_fn = transport_simple_ble_set_mtu; 873 874 /* Set parameters required for advertising */ 875 ble_config->adv_data = adv_data; 876 ble_config->adv_params = adv_params; 877 878 ble_config->device_name = protocomm_ble_device_name; 879 880 if (populate_gatt_db(&ble_config->gatt_db, config) != 0) { 881 ESP_LOGE(TAG, "Error populating GATT Database"); 882 free_gatt_ble_misc_memory(ble_config); 883 return ESP_ERR_NO_MEM; 884 } 885 886 esp_err_t err = simple_ble_start(ble_config); 887 ESP_LOGD(TAG, "Free Heap size after simple_ble_start= %d", esp_get_free_heap_size()); 888 889 if (err != ESP_OK) { 890 ESP_LOGE(TAG, "simple_ble_start failed w/ error code 0x%x", err); 891 free_gatt_ble_misc_memory(ble_config); 892 protocomm_ble_cleanup(); 893 return err; 894 } 895 896 ESP_LOGV(TAG, "Waiting for client to connect ......"); 897 return ESP_OK; 898 } 899 900 esp_err_t protocomm_ble_stop(protocomm_t *pc) 901 { 902 ESP_LOGD(TAG, "protocomm_ble_stop called here..."); 903 if ((pc != NULL) && 904 (protoble_internal != NULL ) && 905 (pc == protoble_internal->pc_ble)) { 906 esp_err_t ret = ESP_OK; 907 908 esp_err_t rc = ble_gap_adv_stop(); 909 if (rc) { 910 ESP_LOGD(TAG, "Error in stopping advertisement with err code = %d", 911 rc); 912 } 913 914 ret = nimble_port_stop(); 915 if (ret == 0) { 916 nimble_port_deinit(); 917 ret = esp_nimble_hci_and_controller_deinit(); 918 if (ret != ESP_OK) { 919 ESP_LOGE(TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret); 920 } 921 } 922 923 free_gatt_ble_misc_memory(ble_cfg_p); 924 protocomm_ble_cleanup(); 925 return ret; 926 } 927 return ESP_ERR_INVALID_ARG; 928 }