/ components / protocomm / src / transports / protocomm_nimble.c
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  }