diff --git a/components/protocomm/include/transports/protocomm_ble.h b/components/protocomm/include/transports/protocomm_ble.h index b30d5707b6..a9e7b6a209 100644 --- a/components/protocomm/include/transports/protocomm_ble.h +++ b/components/protocomm/include/transports/protocomm_ble.h @@ -26,6 +26,14 @@ extern "C" { */ #define MAX_BLE_DEVNAME_LEN 29 #define BLE_UUID128_VAL_LENGTH 16 + /** + * Theoretically, the limit for max manufacturer length remains same as BLE + * device name i.e. 31 bytes (max scan response size) - 1 byte (length) - 1 + * byte (type) = 29 bytes + * However, manufacturer data goes along with BLE device name in scan response. + * So, it is important to understand the actual length should be smaller than + * (29 - (BLE device name length) - 2). */ +#define MAX_BLE_MANUFACTURER_DATA_LEN 29 /** * @brief This structure maps handler required by protocomm layer to @@ -59,6 +67,16 @@ typedef struct protocomm_ble_config { */ uint8_t service_uuid[BLE_UUID128_VAL_LENGTH]; + /** + * BLE device manufacturer data pointer in advertisement + */ + uint8_t *manufacturer_data; + + /** + * BLE device manufacturer data length in advertisement + */ + ssize_t manufacturer_data_len; + /** * Number of entries in the Name-UUID lookup table */ diff --git a/components/protocomm/src/transports/protocomm_ble.c b/components/protocomm/src/transports/protocomm_ble.c index 053b37f1dd..93494273d5 100644 --- a/components/protocomm/src/transports/protocomm_ble.c +++ b/components/protocomm/src/transports/protocomm_ble.c @@ -77,7 +77,9 @@ static esp_ble_adv_params_t adv_params = { .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, }; -static char* protocomm_ble_device_name = NULL; +static char *protocomm_ble_device_name = NULL; +static uint8_t *protocomm_ble_mfg_data = NULL; +static size_t protocomm_ble_mfg_data_len; static void hexdump(const char *msg, uint8_t *buf, int len) { @@ -132,8 +134,8 @@ static void transport_simple_ble_read(esp_gatts_cb_event_t event, esp_gatt_if_t gatt_rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; if (gatt_rsp.attr_value.len && read_buf) { memcpy(gatt_rsp.attr_value.value, - read_buf + param->read.offset, - gatt_rsp.attr_value.len); + read_buf + param->read.offset, + gatt_rsp.attr_value.len); } read_len -= gatt_rsp.attr_value.len; } else { @@ -142,14 +144,14 @@ static void transport_simple_ble_read(esp_gatts_cb_event_t event, esp_gatt_if_t read_buf = NULL; } esp_err_t err = esp_ble_gatts_send_response(gatts_if, param->read.conn_id, - param->read.trans_id, status, &gatt_rsp); + param->read.trans_id, status, &gatt_rsp); if (err != ESP_OK) { ESP_LOGE(TAG, "Send response error in read"); } } static esp_err_t prepare_write_event_env(esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) + esp_ble_gatts_cb_param_t *param) { ESP_LOGD(TAG, "prepare write, handle = %d, value len = %d, offset = %d", param->write.handle, param->write.len, param->write.offset); @@ -196,10 +198,10 @@ static esp_err_t prepare_write_event_env(esp_gatt_if_t gatts_if, memcpy(gatt_rsp.attr_value.value, param->write.value, param->write.len); } response_err = esp_ble_gatts_send_response(gatts_if, - param->write.conn_id, param->write.trans_id, status, &gatt_rsp); + param->write.conn_id, param->write.trans_id, status, &gatt_rsp); } else { response_err = esp_ble_gatts_send_response(gatts_if, - param->write.conn_id, param->write.trans_id, status, NULL); + param->write.conn_id, param->write.trans_id, status, NULL); } if (response_err != ESP_OK) { ESP_LOGE(TAG, "Send response error in prep write"); @@ -307,9 +309,9 @@ static void transport_simple_ble_disconnect(esp_gatts_cb_event_t event, esp_gatt esp_err_t ret; ESP_LOGD(TAG, "Inside disconnect w/ session - %d", param->disconnect.conn_id); if (protoble_internal->pc_ble->sec && - protoble_internal->pc_ble->sec->close_transport_session) { + protoble_internal->pc_ble->sec->close_transport_session) { ret = protoble_internal->pc_ble->sec->close_transport_session(protoble_internal->pc_ble->sec_inst, - param->disconnect.conn_id); + param->disconnect.conn_id); if (ret != ESP_OK) { ESP_LOGE(TAG, "error closing the session after disconnect"); } @@ -322,9 +324,9 @@ static void transport_simple_ble_connect(esp_gatts_cb_event_t event, esp_gatt_if esp_err_t ret; ESP_LOGD(TAG, "Inside BLE connect w/ conn_id - %d", param->connect.conn_id); if (protoble_internal->pc_ble->sec && - protoble_internal->pc_ble->sec->new_transport_session) { + protoble_internal->pc_ble->sec->new_transport_session) { ret = protoble_internal->pc_ble->sec->new_transport_session(protoble_internal->pc_ble->sec_inst, - param->connect.conn_id); + param->connect.conn_id); if (ret != ESP_OK) { ESP_LOGE(TAG, "error creating the session"); } @@ -338,8 +340,8 @@ static void transport_simple_ble_set_mtu(esp_gatts_cb_event_t event, esp_gatt_if } static esp_err_t protocomm_ble_add_endpoint(const char *ep_name, - protocomm_req_handler_t req_handler, - void *priv_data) + protocomm_req_handler_t req_handler, + void *priv_data) { /* Endpoint UUID already added when protocomm_ble_start() was called */ return ESP_OK; @@ -364,7 +366,7 @@ static ssize_t populate_gatt_db(esp_gatts_attr_db_t **gatt_db_generated) ssize_t gatt_db_generated_entries = 3 * protoble_internal->g_nu_lookup_count + 1; *gatt_db_generated = (esp_gatts_attr_db_t *) malloc(sizeof(esp_gatts_attr_db_t) * - (gatt_db_generated_entries)); + (gatt_db_generated_entries)); if ((*gatt_db_generated) == NULL) { ESP_LOGE(TAG, "Failed to assign memory to gatt_db"); return -1; @@ -432,6 +434,11 @@ static void protocomm_ble_cleanup(void) free(protocomm_ble_device_name); protocomm_ble_device_name = NULL; } + if (protocomm_ble_mfg_data) { + free(protocomm_ble_mfg_data); + protocomm_ble_mfg_data = NULL; + protocomm_ble_mfg_data_len = 0; + } } esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *config) @@ -453,6 +460,12 @@ esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *con return ESP_ERR_NO_MEM; } + /* Store BLE manufacturer data pointer */ + if (config->manufacturer_data != NULL) { + protocomm_ble_mfg_data = config->manufacturer_data; + protocomm_ble_mfg_data_len = config->manufacturer_data_len; + } + protoble_internal = (_protocomm_ble_internal_t *) calloc(1, sizeof(_protocomm_ble_internal_t)); if (protoble_internal == NULL) { ESP_LOGE(TAG, "Error allocating internal protocomm structure"); @@ -510,7 +523,7 @@ esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *con /* Get the total raw data length required for above entries */ uint8_t adv_data_len = 0; - for (uint8_t i = 0; i < (sizeof(adv_data)/sizeof(adv_data[0])); i++) { + for (uint8_t i = 0; i < (sizeof(adv_data) / sizeof(adv_data[0])); i++) { /* Add extra bytes required per entry, i.e. * length (1 byte) + type (1 byte) = 2 bytes */ adv_data_len += adv_data[i].length + 2; @@ -531,7 +544,7 @@ esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *con } /* Form the raw advertisement data using above entries */ - for (uint8_t i = 0, len = 0; i < (sizeof(adv_data)/sizeof(adv_data[0])); i++) { + for (uint8_t i = 0, len = 0; i < (sizeof(adv_data) / sizeof(adv_data[0])); i++) { protoble_internal->raw_adv_data_p[len++] = adv_data[i].length + 1; // + 1 byte for type protoble_internal->raw_adv_data_p[len++] = adv_data[i].type; memcpy(&protoble_internal->raw_adv_data_p[len], @@ -556,7 +569,12 @@ esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *con * * Any remaining space may be used for accommodating * other fields in the future + * + * 2) Manufacturer Data (To be truncated depending upon available size) + * Size : The maximum supported manufacturer data size + * will be 31 - 2 (length + type) - ble_devname_len - 2 (length + type) */ + raw_data_info_t scan_resp_data[] = { { /* If full device name can fit in the scan response then indicate * that by setting type to "Complete Name", else set it to "Short Name" @@ -567,11 +585,20 @@ esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *con .length = MIN(ble_devname_len, (ESP_BLE_SCAN_RSP_DATA_LEN_MAX - 2)), .data_p = (uint8_t *) protocomm_ble_device_name }, + { + 0, + }, }; + if (protocomm_ble_mfg_data_len > 0) { + scan_resp_data[1].type = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; + scan_resp_data[1].length = protocomm_ble_mfg_data_len; + scan_resp_data[1].data_p = (uint8_t *) protocomm_ble_mfg_data; + } + /* Get the total raw scan response data length required for above entries */ uint8_t scan_resp_data_len = 0; - for (int i = 0; i < (sizeof(scan_resp_data)/sizeof(scan_resp_data[0])); i++) { + for (int i = 0; i < (sizeof(scan_resp_data) / sizeof(scan_resp_data[0])); i++) { /* Add extra bytes required per entry, i.e. * length (1 byte) + type (1 byte) = 2 bytes */ scan_resp_data_len += scan_resp_data[i].length + 2; @@ -592,7 +619,7 @@ esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *con } /* Form the raw scan response data using above entries */ - for (uint8_t i = 0, len = 0; i < (sizeof(scan_resp_data)/sizeof(scan_resp_data[0])); i++) { + for (uint8_t i = 0, len = 0; i < (sizeof(scan_resp_data) / sizeof(scan_resp_data[0])); i++) { protoble_internal->raw_scan_rsp_data_p[len++] = scan_resp_data[i].length + 1; // + 1 byte for type protoble_internal->raw_scan_rsp_data_p[len++] = scan_resp_data[i].type; memcpy(&protoble_internal->raw_scan_rsp_data_p[len], @@ -649,8 +676,8 @@ esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *con esp_err_t protocomm_ble_stop(protocomm_t *pc) { if ((pc != NULL) && - (protoble_internal != NULL ) && - (pc == protoble_internal->pc_ble)) { + (protoble_internal != NULL ) && + (pc == protoble_internal->pc_ble)) { esp_err_t ret = ESP_OK; ret = simple_ble_stop(); if (ret) { diff --git a/components/protocomm/src/transports/protocomm_nimble.c b/components/protocomm/src/transports/protocomm_nimble.c index bf0697ba5b..8c9fe6bf5b 100644 --- a/components/protocomm/src/transports/protocomm_nimble.c +++ b/components/protocomm/src/transports/protocomm_nimble.c @@ -81,6 +81,9 @@ static struct ble_gap_adv_params adv_params; static char *protocomm_ble_device_name; static struct ble_hs_adv_fields adv_data, resp_data; +static uint8_t *protocomm_ble_mfg_data; +static size_t protocomm_ble_mfg_data_len; + /********************************************************************** * Maintain database of uuid_name addresses to free memory afterwards * **********************************************************************/ @@ -314,7 +317,7 @@ gatt_svr_chr_access(uint16_t conn_handle, uint16_t attr_handle, switch (ctxt->op) { case BLE_GATT_ACCESS_OP_READ_CHR: - ESP_LOGD(TAG, "Read attempeted for Characterstic UUID = %s, attr_handle = %d", + ESP_LOGD(TAG, "Read attempted for characteristic UUID = %s, attr_handle = %d", ble_uuid_to_str(ctxt->chr->uuid, buf), attr_handle); rc = simple_ble_gatts_get_attr_value(attr_handle, &temp_outlen, @@ -480,7 +483,7 @@ static int simple_ble_start(const simple_ble_cfg_t *cfg) { ble_cfg_p = (void *)cfg; int rc; - ESP_LOGD(TAG, "Free mem at start of simple_ble_init %d", esp_get_free_heap_size()); + ESP_LOGD(TAG, "Free memory at start of simple_ble_init %d", esp_get_free_heap_size()); ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init()); nimble_port_init(); @@ -509,6 +512,13 @@ static int simple_ble_start(const simple_ble_cfg_t *cfg) resp_data.name_is_complete = 1; } + /* Set manufacturer data if protocomm_ble_mfg_data points to valid data */ + if (protocomm_ble_mfg_data != NULL) { + resp_data.mfg_data = protocomm_ble_mfg_data; + resp_data.mfg_data_len = protocomm_ble_mfg_data_len; + ESP_LOGD(TAG, "Custom manufacturer data length = %d", protocomm_ble_mfg_data_len); + } + /* XXX Need to have template for store */ ble_store_config_init(); nimble_port_freertos_init(nimble_host_task); @@ -726,10 +736,17 @@ static void protocomm_ble_cleanup(void) free(protoble_internal); protoble_internal = NULL; } + if (protocomm_ble_device_name) { free(protocomm_ble_device_name); protocomm_ble_device_name = NULL; } + + if (protocomm_ble_mfg_data) { + free(protocomm_ble_mfg_data); + protocomm_ble_mfg_data = NULL; + protocomm_ble_mfg_data_len = 0; + } } static void free_gatt_ble_misc_memory(simple_ble_cfg_t *ble_config) @@ -781,7 +798,9 @@ esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *con { /* copy the 128 bit service UUID into local buffer to use as base 128 bit * UUID. */ - memcpy(ble_uuid_base, config->service_uuid, BLE_UUID128_VAL_LENGTH); + if (config->service_uuid != NULL) { + memcpy(ble_uuid_base, config->service_uuid, BLE_UUID128_VAL_LENGTH); + } if (!pc || !config || !config->device_name || !config->nu_lookup) { return ESP_ERR_INVALID_ARG; @@ -822,13 +841,18 @@ esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *con /* Store BLE device name internally */ protocomm_ble_device_name = strdup(config->device_name); - if (protocomm_ble_device_name == NULL) { ESP_LOGE(TAG, "Error allocating memory for storing BLE device name"); protocomm_ble_cleanup(); return ESP_ERR_NO_MEM; } + /* Store BLE manufacturer data pointer */ + if (config->manufacturer_data != NULL) { + protocomm_ble_mfg_data = config->manufacturer_data; + protocomm_ble_mfg_data_len = config->manufacturer_data_len; + } + protoble_internal = (_protocomm_ble_internal_t *) calloc(1, sizeof(_protocomm_ble_internal_t)); if (protoble_internal == NULL) { ESP_LOGE(TAG, "Error allocating internal protocomm structure"); diff --git a/components/wifi_provisioning/include/wifi_provisioning/scheme_ble.h b/components/wifi_provisioning/include/wifi_provisioning/scheme_ble.h index ed30d81815..fc19e16b17 100644 --- a/components/wifi_provisioning/include/wifi_provisioning/scheme_ble.h +++ b/components/wifi_provisioning/include/wifi_provisioning/scheme_ble.h @@ -77,6 +77,29 @@ void wifi_prov_scheme_ble_event_cb_free_bt (void *user_data, wifi_prov_cb_event */ esp_err_t wifi_prov_scheme_ble_set_service_uuid(uint8_t *uuid128); +/** + * @brief Set manufacturer specific data in scan response + * + * This must be called before starting provisioning, i.e. before + * making a call to wifi_prov_mgr_start_provisioning(). + * + * @note It is important to understand that length of custom manufacturer + * data should be within limits. The manufacturer data goes into scan + * response along with BLE device name. By default, BLE device name + * length is of 11 Bytes, however it can vary as per application use + * case. So, one has to honour the scan response data size limits i.e. + * (mfg_data_len + 2) < 31 - (device_name_length + 2 ). If the + * mfg_data length exceeds this limit, the length will be truncated. + * + * @param[in] mfg_data Custom manufacturer data + * @param[in] mfg_data_len Manufacturer data length + * + * @return + * - ESP_OK : Success + * - ESP_ERR_INVALID_ARG : Null argument + */ +esp_err_t wifi_prov_scheme_ble_set_mfg_data(uint8_t *mfg_data, ssize_t mfg_data_len); + #ifdef __cplusplus } #endif diff --git a/components/wifi_provisioning/src/scheme_ble.c b/components/wifi_provisioning/src/scheme_ble.c index 6faf49bd2d..9a1fd48940 100644 --- a/components/wifi_provisioning/src/scheme_ble.c +++ b/components/wifi_provisioning/src/scheme_ble.c @@ -29,6 +29,9 @@ extern const wifi_prov_scheme_t wifi_prov_scheme_ble; static uint8_t *custom_service_uuid; +static uint8_t *custom_manufacturer_data; +static size_t custom_manufacturer_data_len; + static esp_err_t prov_start(protocomm_t *pc, void *config) { if (!pc) { @@ -60,6 +63,23 @@ esp_err_t wifi_prov_scheme_ble_set_service_uuid(uint8_t *uuid128) return ESP_OK; } +esp_err_t wifi_prov_scheme_ble_set_mfg_data(uint8_t *mfg_data, ssize_t mfg_data_len) +{ + if (!mfg_data || !mfg_data_len) { + return ESP_ERR_INVALID_ARG; + } + + custom_manufacturer_data = (uint8_t *) malloc(mfg_data_len); + if (custom_manufacturer_data == NULL) { + ESP_LOGE(TAG, "Error allocating memory for mfg_data"); + return ESP_ERR_NO_MEM; + } + + custom_manufacturer_data_len = mfg_data_len; + memcpy(custom_manufacturer_data, mfg_data, mfg_data_len); + return ESP_OK; +} + static void *new_config(void) { protocomm_ble_config_t *ble_config = calloc(1, sizeof(protocomm_ble_config_t)); @@ -114,6 +134,26 @@ static esp_err_t set_config_service(void *config, const char *service_name, cons if (custom_service_uuid) { memcpy(ble_config->service_uuid, custom_service_uuid, sizeof(ble_config->service_uuid)); } + /* Set manufacturer data if it is provided by app */ + if (custom_manufacturer_data) { + size_t mfg_data_len = custom_manufacturer_data_len; + /* Manufacturer Data Length + 2 Byte header + BLE Device name + 2 Byte + * header <= 31 Bytes */ + if (mfg_data_len > (MAX_BLE_MANUFACTURER_DATA_LEN - sizeof(ble_config->device_name) - 2)) { + ESP_LOGE(TAG, "Manufacturer data length is more than the max allowed size; expect truncated mfg_data "); + /* XXX Does it even make any sense to set truncated mfg_data ? The + * only reason to not return failure from here is provisioning + * should continue as it is with error prints for mfg_data length */ + mfg_data_len = MAX_BLE_MANUFACTURER_DATA_LEN - sizeof(ble_config->device_name) - 2; + } + + ble_config->manufacturer_data = custom_manufacturer_data; + ble_config->manufacturer_data_len = mfg_data_len; + } else { + ble_config->manufacturer_data = NULL; + ble_config->manufacturer_data_len = 0; + } + return ESP_OK; }