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