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 
hexdump(const char * msg,uint8_t * buf,int len)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 
uuid128_to_16(const uint8_t * uuid128)88 static const uint16_t *uuid128_to_16(const uint8_t *uuid128)
89 {
90     return (const uint16_t *) &uuid128[12];
91 }
92 
handle_to_handler(uint16_t handle)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 
transport_simple_ble_read(esp_gatts_cb_event_t event,esp_gatt_if_t gatts_if,esp_ble_gatts_cb_param_t * param)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 
prepare_write_event_env(esp_gatt_if_t gatts_if,esp_ble_gatts_cb_param_t * param)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 
transport_simple_ble_write(esp_gatts_cb_event_t event,esp_gatt_if_t gatts_if,esp_ble_gatts_cb_param_t * param)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 
transport_simple_ble_exec_write(esp_gatts_cb_event_t event,esp_gatt_if_t gatts_if,esp_ble_gatts_cb_param_t * param)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 
transport_simple_ble_disconnect(esp_gatts_cb_event_t event,esp_gatt_if_t gatts_if,esp_ble_gatts_cb_param_t * param)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 
transport_simple_ble_connect(esp_gatts_cb_event_t event,esp_gatt_if_t gatts_if,esp_ble_gatts_cb_param_t * param)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 
transport_simple_ble_set_mtu(esp_gatts_cb_event_t event,esp_gatt_if_t gatts_if,esp_ble_gatts_cb_param_t * param)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 
protocomm_ble_add_endpoint(const char * ep_name,protocomm_req_handler_t req_handler,void * priv_data)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 
protocomm_ble_remove_endpoint(const char * ep_name)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 
populate_gatt_db(esp_gatts_attr_db_t ** gatt_db_generated)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 
protocomm_ble_cleanup(void)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 
protocomm_ble_start(protocomm_t * pc,const protocomm_ble_config_t * config)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 
protocomm_ble_stop(protocomm_t * pc)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 }
665