]> granicus.if.org Git - esp-idf/blob - components/protocomm/src/transports/protocomm_ble.c
Merge branch 'bugfix/protocomm_sec_mult_inst' into 'master'
[esp-idf] / components / protocomm / src / transports / 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     esp_gatt_status_t status = ESP_OK;
112
113     ESP_LOGD(TAG, "Inside read w/ session - %d on param %d %d",
114              param->read.conn_id, param->read.handle, read_len);
115     if (!read_len) {
116         ESP_LOGD(TAG, "Reading attr value first time");
117         status = esp_ble_gatts_get_attr_value(param->read.handle, &read_len,  &read_buf);
118     } else {
119         ESP_LOGD(TAG, "Subsequent read request for attr value");
120     }
121
122     esp_gatt_rsp_t gatt_rsp = {0};
123     gatt_rsp.attr_value.len = MIN(read_len, (protoble_internal->gatt_mtu - 1));
124     gatt_rsp.attr_value.handle = param->read.handle;
125     gatt_rsp.attr_value.offset = param->read.offset;
126     gatt_rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
127     if (gatt_rsp.attr_value.len && read_buf) {
128         memcpy(gatt_rsp.attr_value.value,
129                 read_buf + param->read.offset,
130                 gatt_rsp.attr_value.len);
131     }
132     read_len -= gatt_rsp.attr_value.len;
133     esp_err_t err = esp_ble_gatts_send_response(gatts_if, param->read.conn_id,
134                                                 param->read.trans_id, status, &gatt_rsp);
135     if (err != ESP_OK) {
136         ESP_LOGE(TAG, "Send response error in read");
137     }
138 }
139
140 static esp_err_t prepare_write_event_env(esp_gatt_if_t gatts_if,
141                                          esp_ble_gatts_cb_param_t *param)
142 {
143     ESP_LOGD(TAG, "prepare write, handle = %d, value len = %d",
144              param->write.handle, param->write.len);
145     esp_gatt_status_t status = ESP_GATT_OK;
146     if (prepare_write_env.prepare_buf == NULL) {
147         prepare_write_env.prepare_buf = (uint8_t *) malloc(PREPARE_BUF_MAX_SIZE * sizeof(uint8_t));
148         if (prepare_write_env.prepare_buf == NULL) {
149             ESP_LOGE(TAG, "%s , failed tp allocate preparebuf", __func__);
150             status = ESP_GATT_NO_RESOURCES;
151         }
152         /* prepare_write_env.prepare_len = 0; */
153     } else {
154         if (param->write.offset > PREPARE_BUF_MAX_SIZE) {
155             status = ESP_GATT_INVALID_OFFSET;
156         } else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) {
157             status = ESP_GATT_INVALID_ATTR_LEN;
158         }
159     }
160     memcpy(prepare_write_env.prepare_buf + param->write.offset,
161            param->write.value,
162            param->write.len);
163     prepare_write_env.prepare_len += param->write.len;
164     prepare_write_env.handle = param->write.handle;
165     if (param->write.need_rsp) {
166         esp_gatt_rsp_t gatt_rsp = {0};
167         gatt_rsp.attr_value.len = param->write.len;
168         gatt_rsp.attr_value.handle = param->write.handle;
169         gatt_rsp.attr_value.offset = param->write.offset;
170         gatt_rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
171         if (gatt_rsp.attr_value.len && param->write.value) {
172             memcpy(gatt_rsp.attr_value.value, param->write.value, param->write.len);
173         }
174         esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id,
175                 param->write.trans_id, status,
176                 &gatt_rsp);
177         if (response_err != ESP_OK) {
178             ESP_LOGE(TAG, "Send response error in prep write");
179         }
180     }
181     if (status != ESP_GATT_OK) {
182         if (prepare_write_env.prepare_buf) {
183             free(prepare_write_env.prepare_buf);
184             prepare_write_env.prepare_buf = NULL;
185             prepare_write_env.prepare_len = 0;
186         }
187         return ESP_FAIL;
188     }
189     return ESP_OK;
190 }
191
192 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)
193 {
194     uint8_t *outbuf = NULL;
195     ssize_t outlen = 0;
196     esp_err_t ret;
197
198     ESP_LOGD(TAG, "Inside write with session - %d on attr handle - %d \nLen -%d IS Prep - %d",
199              param->write.conn_id, param->write.handle, param->write.len, param->write.is_prep);
200
201     if (param->write.is_prep) {
202         ret = prepare_write_event_env(gatts_if, param);
203         if (ret != ESP_OK) {
204             ESP_LOGE(TAG, "Error appending to prepare buffer");
205         }
206         return;
207     } else {
208         ESP_LOGD(TAG, "is_prep not set");
209     }
210
211     ret = protocomm_req_handle(protoble_internal->pc_ble,
212                                handle_to_handler(param->write.handle),
213                                param->write.conn_id,
214                                param->write.value,
215                                param->write.len,
216                                &outbuf, &outlen);
217     if (ret == ESP_OK) {
218         ret = esp_ble_gatts_set_attr_value(param->write.handle, outlen, outbuf);
219         if (ret != ESP_OK) {
220             ESP_LOGE(TAG, "Failed to set the session attribute value");
221         }
222         ret = esp_ble_gatts_send_response(gatts_if, param->write.conn_id,
223                                           param->write.trans_id, ESP_GATT_OK, NULL);
224         if (ret != ESP_OK) {
225             ESP_LOGE(TAG, "Send response error in write");
226         }
227         hexdump("Response from  write", outbuf, outlen);
228
229     } else {
230         ESP_LOGE(TAG, "Invalid content received, killing connection");
231         esp_ble_gatts_close(gatts_if, param->write.conn_id);
232     }
233     if (outbuf) {
234         free(outbuf);
235     }
236 }
237
238 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)
239 {
240     esp_err_t err;
241     uint8_t *outbuf = NULL;
242     ssize_t outlen = 0;
243     ESP_LOGD(TAG, "Inside exec_write w/ session - %d", param->exec_write.conn_id);
244
245     if ((param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC)
246             &&
247             prepare_write_env.prepare_buf) {
248         err = protocomm_req_handle(protoble_internal->pc_ble,
249                                    handle_to_handler(prepare_write_env.handle),
250                                    param->exec_write.conn_id,
251                                    prepare_write_env.prepare_buf,
252                                    prepare_write_env.prepare_len,
253                                    &outbuf, &outlen);
254
255         if (err != ESP_OK) {
256             ESP_LOGE(TAG, "Invalid content received, killing connection");
257             esp_ble_gatts_close(gatts_if, param->exec_write.conn_id);
258         } else {
259             hexdump("Response from exec write", outbuf, outlen);
260             esp_ble_gatts_set_attr_value(prepare_write_env.handle, outlen, outbuf);
261         }
262     }
263     if (prepare_write_env.prepare_buf) {
264         free(prepare_write_env.prepare_buf);
265         prepare_write_env.prepare_buf = NULL;
266         prepare_write_env.prepare_len = 0;
267     }
268
269     err = esp_ble_gatts_send_response(gatts_if, param->exec_write.conn_id, param->exec_write.trans_id, ESP_GATT_OK, NULL);
270     if (err != ESP_OK) {
271         ESP_LOGE(TAG, "Send response error in exec write");
272     }
273     if (outbuf) {
274         free(outbuf);
275     }
276 }
277
278 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)
279 {
280     esp_err_t ret;
281     ESP_LOGD(TAG, "Inside disconnect w/ session - %d", param->disconnect.conn_id);
282     if (protoble_internal->pc_ble->sec &&
283         protoble_internal->pc_ble->sec->close_transport_session) {
284         ret = protoble_internal->pc_ble->sec->close_transport_session(protoble_internal->pc_ble->sec_inst,
285                                                                       param->disconnect.conn_id);
286         if (ret != ESP_OK) {
287             ESP_LOGE(TAG, "error closing the session after disconnect");
288         }
289     }
290     protoble_internal->gatt_mtu = ESP_GATT_DEF_BLE_MTU_SIZE;
291 }
292
293 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)
294 {
295     esp_err_t ret;
296     ESP_LOGD(TAG, "Inside BLE connect w/ conn_id - %d", param->connect.conn_id);
297     if (protoble_internal->pc_ble->sec &&
298         protoble_internal->pc_ble->sec->new_transport_session) {
299         ret = protoble_internal->pc_ble->sec->new_transport_session(protoble_internal->pc_ble->sec_inst,
300                                                                     param->connect.conn_id);
301         if (ret != ESP_OK) {
302             ESP_LOGE(TAG, "error creating the session");
303         }
304     }
305 }
306
307 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)
308 {
309     protoble_internal->gatt_mtu = param->mtu.mtu;
310     return;
311 }
312
313 static esp_err_t protocomm_ble_add_endpoint(const char *ep_name,
314                                             protocomm_req_handler_t req_handler,
315                                             void *priv_data)
316 {
317     /* Endpoint UUID already added when protocomm_ble_start() was called */
318     return ESP_OK;
319 }
320
321 static esp_err_t protocomm_ble_remove_endpoint(const char *ep_name)
322 {
323     /* Endpoint UUID will be removed when protocomm_ble_stop() is called  */
324     return ESP_OK;
325 }
326
327 static ssize_t populate_gatt_db(esp_gatts_attr_db_t **gatt_db_generated)
328 {
329     int i;
330     /* Each endpoint requires 3 attributes:
331      * 1) for Characteristic Declaration
332      * 2) for Characteristic Value (for reading and writing to an endpoint)
333      * 3) for Characteristic User Description (endpoint name)
334      *
335      * Therefore, we need esp_gatts_attr_db_t of size 3 * number of endpoints + 1 for service
336      */
337     ssize_t gatt_db_generated_entries = 3 * protoble_internal->g_nu_lookup_count + 1;
338
339     *gatt_db_generated = (esp_gatts_attr_db_t *) malloc(sizeof(esp_gatts_attr_db_t) *
340                                                         (gatt_db_generated_entries));
341     if ((*gatt_db_generated) == NULL) {
342         ESP_LOGE(TAG, "Failed to assign memory to gatt_db");
343         return -1;
344     }
345     /* Declare service */
346     (*gatt_db_generated)[0].attr_control.auto_rsp       = ESP_GATT_RSP_BY_APP;
347
348     (*gatt_db_generated)[0].att_desc.uuid_length        = ESP_UUID_LEN_16;
349     (*gatt_db_generated)[0].att_desc.uuid_p             = (uint8_t *) &primary_service_uuid;
350     (*gatt_db_generated)[0].att_desc.perm               = ESP_GATT_PERM_READ;
351     (*gatt_db_generated)[0].att_desc.max_length         = ESP_UUID_LEN_128;
352     (*gatt_db_generated)[0].att_desc.length             = ESP_UUID_LEN_128;
353     (*gatt_db_generated)[0].att_desc.value              = protoble_internal->service_uuid;
354
355     /* Declare characteristics */
356     for (i = 1 ; i < gatt_db_generated_entries ; i++) {
357         (*gatt_db_generated)[i].attr_control.auto_rsp     = ESP_GATT_RSP_BY_APP;
358
359         if (i % 3 == 1) {
360             /* Characteristic Declaration */
361             (*gatt_db_generated)[i].att_desc.perm         = ESP_GATT_PERM_READ;
362             (*gatt_db_generated)[i].att_desc.uuid_length  = ESP_UUID_LEN_16;
363             (*gatt_db_generated)[i].att_desc.uuid_p       = (uint8_t *) &character_declaration_uuid;
364             (*gatt_db_generated)[i].att_desc.max_length   = sizeof(uint8_t);
365             (*gatt_db_generated)[i].att_desc.length       = sizeof(uint8_t);
366             (*gatt_db_generated)[i].att_desc.value        = (uint8_t *) &character_prop_read_write;
367         } else if (i % 3 == 2) {
368             /* Characteristic Value */
369             (*gatt_db_generated)[i].att_desc.perm         = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
370             (*gatt_db_generated)[i].att_desc.uuid_length  = ESP_UUID_LEN_128;
371             (*gatt_db_generated)[i].att_desc.uuid_p       = protoble_internal->g_nu_lookup[i / 3].uuid128;
372             (*gatt_db_generated)[i].att_desc.max_length   = CHAR_VAL_LEN_MAX;
373             (*gatt_db_generated)[i].att_desc.length       = 0;
374             (*gatt_db_generated)[i].att_desc.value        = NULL;
375         } else {
376             /* Characteristic User Description (for keeping endpoint names) */
377             (*gatt_db_generated)[i].att_desc.perm         = ESP_GATT_PERM_READ;
378             (*gatt_db_generated)[i].att_desc.uuid_length  = ESP_UUID_LEN_16;
379             (*gatt_db_generated)[i].att_desc.uuid_p       = (uint8_t *) &character_user_description;
380             (*gatt_db_generated)[i].att_desc.max_length   = strlen(protoble_internal->g_nu_lookup[i / 3 - 1].name);
381             (*gatt_db_generated)[i].att_desc.length       = (*gatt_db_generated)[i].att_desc.max_length;
382             (*gatt_db_generated)[i].att_desc.value        = (uint8_t *) protoble_internal->g_nu_lookup[i / 3 - 1].name;
383         }
384     }
385     return gatt_db_generated_entries;
386 }
387
388 static void protocomm_ble_cleanup(void)
389 {
390     if (protoble_internal) {
391         if (protoble_internal->g_nu_lookup) {
392             for (unsigned i = 0; i < protoble_internal->g_nu_lookup_count; i++) {
393                 if (protoble_internal->g_nu_lookup[i].name) {
394                     free((void *)protoble_internal->g_nu_lookup[i].name);
395                 }
396             }
397             free(protoble_internal->g_nu_lookup);
398         }
399         free(protoble_internal->raw_adv_data_p);
400         free(protoble_internal->raw_scan_rsp_data_p);
401         free(protoble_internal);
402         protoble_internal = NULL;
403     }
404     if (protocomm_ble_device_name) {
405         free(protocomm_ble_device_name);
406         protocomm_ble_device_name = NULL;
407     }
408 }
409
410 esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *config)
411 {
412     if (!pc || !config || !config->device_name || !config->nu_lookup) {
413         return ESP_ERR_INVALID_ARG;
414     }
415
416     if (protoble_internal) {
417         ESP_LOGE(TAG, "Protocomm BLE already started");
418         return ESP_FAIL;
419     }
420
421     /* Store BLE device name internally */
422     protocomm_ble_device_name = strdup(config->device_name);
423     if (protocomm_ble_device_name == NULL) {
424         ESP_LOGE(TAG, "Error allocating memory for storing BLE device name");
425         protocomm_ble_cleanup();
426         return ESP_ERR_NO_MEM;
427     }
428
429     protoble_internal = (_protocomm_ble_internal_t *) calloc(1, sizeof(_protocomm_ble_internal_t));
430     if (protoble_internal == NULL) {
431         ESP_LOGE(TAG, "Error allocating internal protocomm structure");
432         protocomm_ble_cleanup();
433         return ESP_ERR_NO_MEM;
434     }
435
436     protoble_internal->g_nu_lookup_count = config->nu_lookup_count;
437     protoble_internal->g_nu_lookup = malloc(config->nu_lookup_count * sizeof(name_uuid128_t));
438     if (protoble_internal->g_nu_lookup == NULL) {
439         ESP_LOGE(TAG, "Error allocating internal name UUID table");
440         protocomm_ble_cleanup();
441         return ESP_ERR_NO_MEM;
442     }
443
444     for (unsigned i = 0; i < protoble_internal->g_nu_lookup_count; i++) {
445         memcpy(protoble_internal->g_nu_lookup[i].uuid128, config->service_uuid, ESP_UUID_LEN_128);
446         memcpy((uint8_t *)uuid128_to_16(protoble_internal->g_nu_lookup[i].uuid128),
447                &config->nu_lookup[i].uuid, ESP_UUID_LEN_16);
448
449         protoble_internal->g_nu_lookup[i].name = strdup(config->nu_lookup[i].name);
450         if (protoble_internal->g_nu_lookup[i].name == NULL) {
451             ESP_LOGE(TAG, "Error allocating internal name UUID entry");
452             protocomm_ble_cleanup();
453             return ESP_ERR_NO_MEM;
454         }
455     }
456
457     pc->add_endpoint = protocomm_ble_add_endpoint;
458     pc->remove_endpoint = protocomm_ble_remove_endpoint;
459     protoble_internal->pc_ble = pc;
460     protoble_internal->gatt_mtu = ESP_GATT_DEF_BLE_MTU_SIZE;
461
462     /* The BLE advertisement data (max 31 bytes) consists of:
463      * 1) Flags -
464      *      Size : length (1 byte) + type (1 byte) + value (1 byte) = 3 bytes
465      * 2) Complete 128 bit UUID of the service -
466      *      Size : length (1 byte) + type (1 byte) + value (16 bytes) = 18 bytes
467      *
468      * Remaining 31 - (3 + 18) = 10 bytes could be used for manufacturer data
469      * or something else in the future.
470      */
471     raw_data_info_t adv_data[] = {
472         {   /* Flags */
473             .type   = ESP_BLE_AD_TYPE_FLAG,
474             .length = sizeof(ble_advertisement_flags),
475             .data_p = (uint8_t *) &ble_advertisement_flags
476         },
477         {   /* 128 bit Service UUID */
478             .type   = ESP_BLE_AD_TYPE_128SRV_CMPL,
479             .length = ESP_UUID_LEN_128,
480             .data_p = (uint8_t *) config->service_uuid
481         },
482     };
483
484     /* Get the total raw data length required for above entries */
485     uint8_t adv_data_len = 0;
486     for (uint8_t i = 0; i < (sizeof(adv_data)/sizeof(adv_data[0])); i++) {
487         /* Add extra bytes required per entry, i.e.
488          * length (1 byte) + type (1 byte) = 2 bytes */
489         adv_data_len += adv_data[i].length + 2;
490     }
491     if (adv_data_len > ESP_BLE_ADV_DATA_LEN_MAX) {
492         ESP_LOGE(TAG, "Advertisement data too long = %d bytes", adv_data_len);
493         protocomm_ble_cleanup();
494         return ESP_ERR_NO_MEM;
495     }
496
497     /* Allocate memory for the raw advertisement data */
498     protoble_internal->raw_adv_data_len = adv_data_len;
499     protoble_internal->raw_adv_data_p = malloc(adv_data_len);
500     if (protoble_internal->raw_adv_data_p == NULL) {
501         ESP_LOGE(TAG, "Error allocating memory for raw advertisement data");
502         protocomm_ble_cleanup();
503         return ESP_ERR_NO_MEM;
504     }
505
506     /* Form the raw advertisement data using above entries */
507     for (uint8_t i = 0, len = 0; i < (sizeof(adv_data)/sizeof(adv_data[0])); i++) {
508         protoble_internal->raw_adv_data_p[len++] = adv_data[i].length + 1; // + 1 byte for type
509         protoble_internal->raw_adv_data_p[len++] = adv_data[i].type;
510         memcpy(&protoble_internal->raw_adv_data_p[len],
511                adv_data[i].data_p, adv_data[i].length);
512
513         if (adv_data[i].type == ESP_BLE_AD_TYPE_128SRV_CMPL) {
514             /* Remember where the primary service UUID is kept in the
515              * raw advertisement data, so that it can be used while
516              * populating the GATT database
517              */
518             protoble_internal->service_uuid = &protoble_internal->raw_adv_data_p[len];
519         }
520
521         len += adv_data[i].length;
522     }
523
524     size_t ble_devname_len = strlen(protocomm_ble_device_name);
525     /* The BLE scan response (31 bytes) consists of:
526      * 1) Device name (complete / incomplete) -
527      *      Size : The maximum supported name length
528      *              will be 31 - 2 (length + type) = 29 bytes
529      *
530      * Any remaining space may be used for accommodating
531      * other fields in the future
532      */
533     raw_data_info_t scan_resp_data[] = {
534         {   /* If full device name can fit in the scan response then indicate
535              * that by setting type to "Complete Name", else set it to "Short Name"
536              * so that client can fetch full device name - after connecting - by
537              * reading the device name characteristic under GAP service */
538             .type   = (ble_devname_len > (ESP_BLE_SCAN_RSP_DATA_LEN_MAX - 2) ?
539                        ESP_BLE_AD_TYPE_NAME_SHORT : ESP_BLE_AD_TYPE_NAME_CMPL),
540             .length = MIN(ble_devname_len, (ESP_BLE_SCAN_RSP_DATA_LEN_MAX - 2)),
541             .data_p = (uint8_t *) protocomm_ble_device_name
542         },
543     };
544
545     /* Get the total raw scan response data length required for above entries */
546     uint8_t scan_resp_data_len = 0;
547     for (int i = 0; i < (sizeof(scan_resp_data)/sizeof(scan_resp_data[0])); i++) {
548         /* Add extra bytes required per entry, i.e.
549          * length (1 byte) + type (1 byte) = 2 bytes */
550         scan_resp_data_len += scan_resp_data[i].length + 2;
551     }
552     if (scan_resp_data_len > ESP_BLE_SCAN_RSP_DATA_LEN_MAX) {
553         ESP_LOGE(TAG, "Scan response data too long = %d bytes", scan_resp_data_len);
554         protocomm_ble_cleanup();
555         return ESP_ERR_NO_MEM;
556     }
557
558     /* Allocate memory for the raw scan response data */
559     protoble_internal->raw_scan_rsp_data_len = scan_resp_data_len;
560     protoble_internal->raw_scan_rsp_data_p = malloc(scan_resp_data_len);
561     if (protoble_internal->raw_scan_rsp_data_p == NULL) {
562         ESP_LOGE(TAG, "Error allocating memory for raw response data");
563         protocomm_ble_cleanup();
564         return ESP_ERR_NO_MEM;
565     }
566
567     /* Form the raw scan response data using above entries */
568     for (uint8_t i = 0, len = 0; i < (sizeof(scan_resp_data)/sizeof(scan_resp_data[0])); i++) {
569         protoble_internal->raw_scan_rsp_data_p[len++] = scan_resp_data[i].length + 1; // + 1 byte for type
570         protoble_internal->raw_scan_rsp_data_p[len++] = scan_resp_data[i].type;
571         memcpy(&protoble_internal->raw_scan_rsp_data_p[len],
572                scan_resp_data[i].data_p, scan_resp_data[i].length);
573         len += scan_resp_data[i].length;
574     }
575
576     simple_ble_cfg_t *ble_config = simple_ble_init();
577     if (ble_config == NULL) {
578         ESP_LOGE(TAG, "Ran out of memory for BLE config");
579         protocomm_ble_cleanup();
580         return ESP_ERR_NO_MEM;
581     }
582
583     /* Set function pointers required for simple BLE layer */
584     ble_config->read_fn         = transport_simple_ble_read;
585     ble_config->write_fn        = transport_simple_ble_write;
586     ble_config->exec_write_fn   = transport_simple_ble_exec_write;
587     ble_config->disconnect_fn   = transport_simple_ble_disconnect;
588     ble_config->connect_fn      = transport_simple_ble_connect;
589     ble_config->set_mtu_fn      = transport_simple_ble_set_mtu;
590
591     /* Set parameters required for advertising */
592     ble_config->adv_params      = adv_params;
593
594     ble_config->raw_adv_data_p        = protoble_internal->raw_adv_data_p;
595     ble_config->raw_adv_data_len      = protoble_internal->raw_adv_data_len;
596     ble_config->raw_scan_rsp_data_p   = protoble_internal->raw_scan_rsp_data_p;
597     ble_config->raw_scan_rsp_data_len = protoble_internal->raw_scan_rsp_data_len;
598
599     ble_config->device_name     = protocomm_ble_device_name;
600     ble_config->gatt_db_count   = populate_gatt_db(&ble_config->gatt_db);
601
602     if (ble_config->gatt_db_count == -1) {
603         ESP_LOGE(TAG, "Invalid GATT database count");
604         simple_ble_deinit();
605         protocomm_ble_cleanup();
606         return ESP_ERR_INVALID_STATE;
607     }
608
609     esp_err_t err = simple_ble_start(ble_config);
610     if (err != ESP_OK) {
611         ESP_LOGE(TAG, "simple_ble_start failed w/ error code 0x%x", err);
612         simple_ble_deinit();
613         protocomm_ble_cleanup();
614         return err;
615     }
616
617     prepare_write_env.prepare_buf = NULL;
618     ESP_LOGD(TAG, "Waiting for client to connect ......");
619     return ESP_OK;
620 }
621
622 esp_err_t protocomm_ble_stop(protocomm_t *pc)
623 {
624     if ((pc != NULL) &&
625         (protoble_internal != NULL ) &&
626         (pc == protoble_internal->pc_ble)) {
627         esp_err_t ret = ESP_OK;
628         ret = simple_ble_stop();
629         if (ret) {
630             ESP_LOGE(TAG, "BLE stop failed");
631         }
632         simple_ble_deinit();
633         protocomm_ble_cleanup();
634         return ret;
635     }
636     return ESP_ERR_INVALID_ARG;
637 }