Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions doc/nrf-bm/release_notes/release_notes_changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ Libraries
* An issue where the :c:func:`ble_conn_params_phy_radio_mode_get` function would incorrectly return the PHY mode mask of a pending update rather than the currently active PHY mode if a PHY update initiated by the :c:func:`ble_conn_params_phy_radio_mode_set` function was still in progress.
* An issue where the SoftDevice define :c:macro:`BLE_GAP_PHYS_SUPPORTED` was used instead of the PHY preferences set with Kconfig when initiating or responding to a PHY update procedure.

* :ref:`lib_ble_adv` library:

* Added the :c:func:`ble_adv_data_manufacturer_data_find` function to locate manufacturer-specific data in an advertising payload and prefix-match it against a target value.

* :ref:`lib_ble_scan` library:

* Added:

* Support for filtering by manufacturer-specific data using the :c:macro:`BLE_SCAN_MANUFACTURER_DATA_FILTER` filter type.
* The :kconfig:option:`CONFIG_BLE_SCAN_MANUFACTURER_DATA_COUNT` and :kconfig:option:`CONFIG_BLE_SCAN_MANUFACTURER_DATA_MAX_LEN` Kconfig options to configure the manufacturer data filter capacity and maximum payload length.

Bluetooth LE Services
---------------------

Expand Down
17 changes: 17 additions & 0 deletions include/bm/bluetooth/ble_adv_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,23 @@ bool ble_adv_data_uuid_find(const uint8_t *buf, uint16_t len, const ble_uuid_t *
*/
bool ble_adv_data_appearance_find(const uint8_t *buf, uint16_t len, const uint16_t *appearance);

/**
* @brief Search encoded Advertising data for manufacturer-specific data.
*
* @param[in] buf Encoded advertising data.
* @param[in] len Buffer length.
* @param[in] target_data Target manufacturer data to match (prefix). Starts with the
* 2-byte little-endian company identifier followed by optional
* vendor payload.
* @param[in] target_data_len Length of the target manufacturer data.
*
* @retval true If manufacturer-specific data in which the first @p target_data_len bytes matches
* @p target_data was found among @p buf.
* @retval false If no match was found among @p buf, or if @p buf or @p target_data was @c NULL.
*/
bool ble_adv_data_manufacturer_data_find(const uint8_t *buf, uint16_t len,
const uint8_t *target_data, uint8_t target_data_len);

#ifdef __cplusplus
}
#endif
Expand Down
36 changes: 34 additions & 2 deletions include/bm/bluetooth/ble_scan.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ enum ble_scan_evt_type {
#define BLE_SCAN_APPEARANCE_FILTER (0x08)
/** Filters the device short name. */
#define BLE_SCAN_SHORT_NAME_FILTER (0x10)
/** Filters the manufacturer data. */
#define BLE_SCAN_MANUFACTURER_DATA_FILTER (0x20)
/** @} */

/**
Expand Down Expand Up @@ -136,6 +138,13 @@ struct ble_scan_filter_data {
/** Minimum length of the short name to be matched. */
uint8_t short_name_min_len;
} short_name_filter;
/** Manufacturer filter data */
struct {
/** Pointer to the manufacturer data. */
uint8_t *data;
/** Manufacturer data length. */
uint8_t data_len;
} manufacturer_data_filter;
};
};

Expand All @@ -153,6 +162,8 @@ struct ble_scan_filter_match {
uint8_t appearance_filter_match: 1;
/** Set to 1 if short name filter is matched. */
uint8_t short_name_filter_match: 1;
/** Set to 1 if manufacturer data filter is matched. */
uint8_t manufacturer_data_filter_match: 1;
};

/**
Expand Down Expand Up @@ -291,6 +302,22 @@ struct ble_scan_appearance_filter {
bool appearance_filter_enabled;
};

/** Scan manufacturer data filter. */
struct ble_scan_manufacturer_data_filter {
struct {
/** Manufacturer data that the main application will scan for, and that will be
* advertised by the peripherals.
*/
uint8_t data[CONFIG_BLE_SCAN_MANUFACTURER_DATA_MAX_LEN];
/** Length of the manufacturer data. */
uint8_t data_len;
} manufacturer_data[CONFIG_BLE_SCAN_MANUFACTURER_DATA_COUNT];
/** Number of manufacturer data in list. */
uint8_t manufacturer_data_cnt;
/** Flag to inform about enabling or disabling this filter. */
bool manufacturer_data_filter_enabled;
};

/**
* @brief Filter data.
*
Expand Down Expand Up @@ -320,6 +347,10 @@ struct ble_scan_filters {
#if CONFIG_BLE_SCAN_APPEARANCE_COUNT > 0
/** Appearance filter data. */
struct ble_scan_appearance_filter appearance_filter;
#endif
#if CONFIG_BLE_SCAN_MANUFACTURER_DATA_COUNT > 0
/** Manufacturer filter data. */
struct ble_scan_manufacturer_data_filter manufacturer_data_filter;
#endif
/** Filter mode. If true, all set filters must be matched to generate an event. */
bool all_filters_mode;
Expand Down Expand Up @@ -517,8 +548,9 @@ uint32_t ble_scan_filter_get(const struct ble_scan *scan, struct ble_scan_filter
* This function adds a new filter by type @ref ble_scan_filter_type.
* The filter will be added if the number of filters of a given type does not exceed @ref
* CONFIG_BLE_SCAN_UUID_COUNT, @ref CONFIG_BLE_SCAN_NAME_COUNT, @ref
* CONFIG_BLE_SCAN_ADDRESS_COUNT, or @ref CONFIG_BLE_SCAN_APPEARANCE_COUNT, depending on
* the filter type, and if the same filter has not already been set.
* CONFIG_BLE_SCAN_ADDRESS_COUNT, @ref CONFIG_BLE_SCAN_APPEARANCE_COUNT,
* or @ref BLE_SCAN_MANUFACTURER_DATA_COUNT, depending on the filter type,
* and if the same filter has not already been set.
*
* @param[in,out] scan Scan library instance.
* @param[in] type Filter type.
Expand Down
29 changes: 29 additions & 0 deletions lib/bluetooth/ble_adv/ble_adv_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -742,3 +742,32 @@ bool ble_adv_data_appearance_find(const uint8_t *data, uint16_t data_len,

return false;
}

bool ble_adv_data_manufacturer_data_find(const uint8_t *data, uint16_t data_len,
const uint8_t *target_data, uint8_t target_data_len)
{
uint16_t data_offset;
uint16_t parsed_len;

if (!data || !target_data || target_data_len == 0) {
return false;
}

data_offset = 0;
parsed_len = ble_adv_data_search(data, data_len, &data_offset,
BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA);

if (data_offset == 0 || parsed_len == 0) {
return false;
}

if (target_data_len > parsed_len) {
Comment thread
eivindj-nordic marked this conversation as resolved.
return false;
}

if (memcmp(&data[data_offset], target_data, target_data_len) == 0) {
return true;
}

return false;
}
12 changes: 12 additions & 0 deletions lib/bluetooth/ble_scan/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ config BLE_SCAN_SHORT_NAME_MAX_LEN
help
Maximum size of the short name to search for in the advertisement report.

config BLE_SCAN_MANUFACTURER_DATA_MAX_LEN
int "Scan manufacturer data maximum length"
default 32
help
Maximum size for the manufacturer data to search in the advertisement report.

config BLE_SCAN_FILTER
bool "Scan filter"
default y
Expand Down Expand Up @@ -68,6 +74,12 @@ config BLE_SCAN_UUID_COUNT
help
Maximum number of filters for UUIDs.

config BLE_SCAN_MANUFACTURER_DATA_COUNT
int "Scan manufacturer data count"
default 0
help
Maximum number of manufacturer data filters.

endif # BLE_SCAN_FILTER

config BLE_SCAN_INTERVAL
Expand Down
112 changes: 109 additions & 3 deletions lib/bluetooth/ble_scan/ble_scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,68 @@ static int appearance_filter_add(struct ble_scan *scan, const struct ble_scan_fi
}
#endif /* CONFIG_BLE_SCAN_APPEARANCE_COUNT */

#if (CONFIG_BLE_SCAN_MANUFACTURER_DATA_COUNT > 0)
static bool adv_manufacturer_data_compare(const struct ble_scan *scan,
uint8_t *data, uint16_t len)
{
const struct ble_scan_manufacturer_data_filter *md_filter =
&scan->scan_filters.manufacturer_data_filter;

/* Match the adv packet against each configured manufacturer data filter. */
for (uint8_t i = 0; i < md_filter->manufacturer_data_cnt; i++) {
if (ble_adv_data_manufacturer_data_find(data, len,
md_filter->manufacturer_data[i].data,
md_filter->manufacturer_data[i].data_len)) {
return true;
}
}
return false;
}

static int manufacturer_data_filter_add(struct ble_scan *scan,
const struct ble_scan_filter_data *data)
{
struct ble_scan_manufacturer_data_filter *md_filter =
&scan->scan_filters.manufacturer_data_filter;
uint8_t *counter = &md_filter->manufacturer_data_cnt;
uint8_t md_len = data->manufacturer_data_filter.data_len;
uint8_t *md_data = data->manufacturer_data_filter.data;

/* Validate length. */
if ((md_len == 0) || (md_len > CONFIG_BLE_SCAN_MANUFACTURER_DATA_MAX_LEN)) {
return NRF_ERROR_DATA_SIZE;
}

/* Validate md_data. */
if (md_data == NULL) {
return NRF_ERROR_NULL;
}

/* Check for duplicated filter. */
for (uint8_t i = 0; i < *counter; i++) {
if ((md_filter->manufacturer_data[i].data_len == md_len) &&
(memcmp(md_filter->manufacturer_data[i].data, md_data, md_len) == 0)) {
return NRF_SUCCESS;
}
}

/* Check for free slot. */
if (*counter >= CONFIG_BLE_SCAN_MANUFACTURER_DATA_COUNT) {
return NRF_ERROR_NO_MEM;
}

/* Add manufacturer data to filter. */
memcpy(md_filter->manufacturer_data[*counter].data, md_data, md_len);
md_filter->manufacturer_data[*counter].data_len = md_len;
(*counter)++;

LOG_DBG("Added manufacturer data filter");
LOG_HEXDUMP_DBG(md_data, md_len, "Manufacturer data");

return NRF_SUCCESS;
}
#endif /* CONFIG_BLE_SCAN_MANUFACTURER_DATA_COUNT */

uint32_t ble_scan_filter_add(struct ble_scan *scan, uint8_t type,
const struct ble_scan_filter_data *data)
{
Expand Down Expand Up @@ -400,6 +462,12 @@ uint32_t ble_scan_filter_add(struct ble_scan *scan, uint8_t type,
}
#endif

#if (CONFIG_BLE_SCAN_MANUFACTURER_DATA_COUNT > 0)
case BLE_SCAN_MANUFACTURER_DATA_FILTER: {
return manufacturer_data_filter_add(scan, data);
}
#endif

default:
return NRF_ERROR_INVALID_PARAM;
}
Expand Down Expand Up @@ -444,6 +512,14 @@ uint32_t ble_scan_all_filter_remove(struct ble_scan *scan)
appearance_filter->appearance_cnt = 0;
#endif

#if (CONFIG_BLE_SCAN_MANUFACTURER_DATA_COUNT > 0)
struct ble_scan_manufacturer_data_filter *md_filter =
&scan->scan_filters.manufacturer_data_filter;

memset(md_filter->manufacturer_data, 0, sizeof(md_filter->manufacturer_data));
md_filter->manufacturer_data_cnt = 0;
#endif

return NRF_SUCCESS;
}

Expand All @@ -461,7 +537,8 @@ uint32_t ble_scan_filters_enable(struct ble_scan *scan, uint8_t mode, bool match
(!(mode & BLE_SCAN_NAME_FILTER)) &&
(!(mode & BLE_SCAN_UUID_FILTER)) &&
(!(mode & BLE_SCAN_SHORT_NAME_FILTER)) &&
(!(mode & BLE_SCAN_APPEARANCE_FILTER))) {
(!(mode & BLE_SCAN_APPEARANCE_FILTER)) &&
(!(mode & BLE_SCAN_MANUFACTURER_DATA_FILTER))) {
return NRF_ERROR_INVALID_PARAM;
}

Expand Down Expand Up @@ -502,6 +579,12 @@ uint32_t ble_scan_filters_enable(struct ble_scan *scan, uint8_t mode, bool match
}
#endif

#if (CONFIG_BLE_SCAN_MANUFACTURER_DATA_COUNT > 0)
if (mode & BLE_SCAN_MANUFACTURER_DATA_FILTER) {
filters->manufacturer_data_filter.manufacturer_data_filter_enabled = true;
}
#endif

/* Select the filter mode. */
filters->all_filters_mode = match_all;

Expand Down Expand Up @@ -539,6 +622,10 @@ uint32_t ble_scan_filters_disable(struct ble_scan *scan)
filters->appearance_filter.appearance_filter_enabled = false;
#endif

#if (CONFIG_BLE_SCAN_MANUFACTURER_DATA_COUNT > 0)
filters->manufacturer_data_filter.manufacturer_data_filter_enabled = false;
#endif

return NRF_SUCCESS;
}

Expand Down Expand Up @@ -683,8 +770,12 @@ static void ble_scan_on_adv_report(struct ble_scan *scan,
LOG_INF("Incomplete, expecting more data!");
}

/* If active scanning where we need to match all we need the scan response for the same
* address before processing the filters.
/* Active scanning with match_all mode: filter data may be split between the ADV packet and
* the SCAN_RSP packet, so we need both before evaluating filters.
*
* Cache this ADV and swap scan buffers so the next report the SoftDevice delivers
* (the SCAN_RSP) is written into a different buffer, leaving adv_data valid until we can
* evaluate the filters.
*/
if (active_match_all && !adv_report->type.scan_response) {
/* Store what we have as advertising data and continue for the scan response. */
Expand Down Expand Up @@ -767,6 +858,21 @@ static void ble_scan_on_adv_report(struct ble_scan *scan,
}
#endif /* CONFIG_BLE_SCAN_APPEARANCE_COUNT */

#if (CONFIG_BLE_SCAN_MANUFACTURER_DATA_COUNT > 0)
/* Check the manufacturer data filter. */
if (scan->scan_filters.manufacturer_data_filter.manufacturer_data_filter_enabled) {
filter_cnt++;
if (adv_manufacturer_data_compare(scan, adv_report->data.p_data,
adv_report->data.len) ||
(active_match_all &&
adv_manufacturer_data_compare(scan, adv_data, adv_data_len))) {
filter_match_cnt++;

scan_evt.filter_match.filter_match.manufacturer_data_filter_match = true;
}
}
#endif /* CONFIG_BLE_SCAN_MANUFACTURER_DATA_COUNT */

/* In the multifilter mode, the number of the active filters must equal the number of the
* filters matched to generate the notification.
*/
Expand Down
Loading
Loading