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