Bluetooth: Controller: Fix Extended Scan Duplicate Filtering
Fix Extended Scan Duplicate Filtering to consider different
advertising mode and multiple advertising set id from same
advertiser Bluetooth device address.
Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
diff --git a/subsys/bluetooth/controller/Kconfig b/subsys/bluetooth/controller/Kconfig
index bd2d1be..4d3ade9 100644
--- a/subsys/bluetooth/controller/Kconfig
+++ b/subsys/bluetooth/controller/Kconfig
@@ -161,6 +161,15 @@
Set the number of unique BLE addresses that can be filtered as
duplicates while scanning.
+config BT_CTLR_DUP_FILTER_ADV_SET_MAX
+ int "Number of Extended Advertising Sets in the scan duplicate filter"
+ depends on BT_OBSERVER && (BT_CTLR_DUP_FILTER_LEN > 0)
+ range 1 16
+ default 1
+ help
+ Set the number of unique Advertising Set ID per Bluetooth Low Energy
+ addresses that can be filtered as duplicates while Extended Scanning.
+
config BT_CTLR_MESH_SCAN_FILTERS
int "Number of Mesh scan filters"
depends on BT_HCI_MESH_EXT
diff --git a/subsys/bluetooth/controller/hci/hci.c b/subsys/bluetooth/controller/hci/hci.c
index f514854..eb7de20 100644
--- a/subsys/bluetooth/controller/hci/hci.c
+++ b/subsys/bluetooth/controller/hci/hci.c
@@ -84,21 +84,36 @@
static uint16_t _opcode;
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
-/* Scan duplicate filter */
-struct dup {
- uint8_t mask;
+#define DUP_EXT_ADV_MODE_MAX 4
+#define DUP_EXT_ADV_MODE_COUNT 4
+
+/* Duplicate filter entries, one per Bluetooth address */
+static struct dup_entry {
bt_addr_le_t addr;
+ /* Mask to accumulate advertising PDU type as bitmask */
+ uint8_t mask;
+
#if defined(CONFIG_BT_CTLR_ADV_EXT)
- uint8_t adv_mode:2;
- uint8_t data_cmplt:1;
- struct pdu_adv_adi adi;
+ struct dup_ext_adv_mode {
+ uint16_t set_count:5;
+ uint16_t set_curr:5;
+ struct dup_ext_adv_set {
+ uint8_t data_cmplt:1;
+ struct pdu_adv_adi adi;
+ } set[CONFIG_BT_CTLR_DUP_FILTER_ADV_SET_MAX];
+ } adv_mode[DUP_EXT_ADV_MODE_MAX];
#endif
-};
-static struct dup dup_filter[CONFIG_BT_CTLR_DUP_FILTER_LEN];
+} dup_filter[CONFIG_BT_CTLR_DUP_FILTER_LEN];
+
+/* Duplicate filtering is disabled if count value is set to negative integer */
+#define DUP_FILTER_DISABLED (-1)
+
+/* Duplicate filtering array entry count, filtering disabled if negative */
static int32_t dup_count;
+/* Duplicate filtering current free entry, overwrites entries after rollover */
static uint32_t dup_curr;
-#endif
+#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
#if defined(CONFIG_BT_HCI_MESH_EXT)
struct scan_filter {
@@ -331,8 +346,8 @@
#endif
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
- dup_count = -1;
-#endif
+ dup_count = DUP_FILTER_DISABLED;
+#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
/* reset event masks */
event_mask = DEFAULT_EVENT_MASK;
@@ -1611,14 +1626,14 @@
}
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
- /* initialize duplicate filtering */
+ /* Initialize duplicate filtering */
if (cmd->enable && cmd->filter_dup) {
dup_count = 0;
dup_curr = 0U;
} else {
- dup_count = -1;
+ dup_count = DUP_FILTER_DISABLED;
}
-#endif
+#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
#if defined(CONFIG_BT_CTLR_ADV_EXT)
status = ll_scan_enable(cmd->enable, 0, 0);
@@ -3301,14 +3316,14 @@
}
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
- /* initialize duplicate filtering */
+ /* Initialize duplicate filtering */
if (cmd->enable && cmd->filter_dup) {
dup_count = 0;
dup_curr = 0U;
} else {
- dup_count = -1;
+ dup_count = DUP_FILTER_DISABLED;
}
-#endif
+#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
status = ll_scan_enable(cmd->enable, cmd->duration, cmd->period);
@@ -4663,49 +4678,102 @@
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
#if defined(CONFIG_BT_CTLR_ADV_EXT)
-static void store_adi(int i, const struct pdu_adv_adi *adi)
+static void dup_ext_adv_adi_store(struct dup_ext_adv_mode *dup_mode,
+ const struct pdu_adv_adi *adi,
+ uint8_t data_status)
{
+ struct dup_ext_adv_set *adv_set;
+
+ adv_set = &dup_mode->set[dup_mode->set_curr];
+
+ adv_set->data_cmplt = !data_status;
+
if (adi) {
- memcpy(&dup_filter[i].adi, adi, sizeof(*adi));
+ (void)memcpy(&adv_set->adi, adi, sizeof(*adi));
} else {
- memset(&dup_filter[i].adi, 0, sizeof(*adi));
+ (void)memset(&adv_set->adi, 0U, sizeof(*adi));
+ }
+
+ if (dup_mode->set_count < CONFIG_BT_CTLR_DUP_FILTER_ADV_SET_MAX) {
+ dup_mode->set_count++;
+ dup_mode->set_curr = dup_mode->set_count;
+ } else {
+ dup_mode->set_curr++;
+ }
+
+ if (dup_mode->set_curr == CONFIG_BT_CTLR_DUP_FILTER_ADV_SET_MAX) {
+ dup_mode->set_curr = 0U;
+ }
+}
+
+static void dup_ext_adv_mode_reset(struct dup_ext_adv_mode *dup_adv_mode)
+{
+ uint8_t adv_mode;
+
+ for (adv_mode = 0U; adv_mode < DUP_EXT_ADV_MODE_COUNT;
+ adv_mode++) {
+ struct dup_ext_adv_mode *dup_mode;
+
+ dup_mode = &dup_adv_mode[adv_mode];
+ dup_mode->set_count = 0U;
+ dup_mode->set_curr = 0U;
}
}
#endif /* CONFIG_BT_CTLR_ADV_EXT */
-static inline bool is_dup_or_update(int i, uint8_t adv_type, uint8_t adv_mode,
+static inline bool is_dup_or_update(struct dup_entry *dup, uint8_t adv_type,
+ uint8_t adv_mode,
const struct pdu_adv_adi *adi,
uint8_t data_status)
{
- if (!(dup_filter[i].mask & BIT(adv_type))) {
+ if (!(dup->mask & BIT(adv_type))) {
/* report different adv types */
- dup_filter[i].mask |= BIT(adv_type);
+ dup->mask |= BIT(adv_type);
#if defined(CONFIG_BT_CTLR_ADV_EXT)
- dup_filter[i].adv_mode = adv_mode;
- dup_filter[i].data_cmplt = !data_status;
- store_adi(i, adi);
+ dup_ext_adv_adi_store(&dup->adv_mode[adv_mode], adi,
+ data_status);
return false;
- } else if (dup_filter[i].adv_mode != adv_mode) {
- /* report different adv mode */
- dup_filter[i].adv_mode = adv_mode;
-
- dup_filter[i].data_cmplt = !data_status;
- store_adi(i, adi);
-
+ } else if (adv_type != PDU_ADV_TYPE_EXT_IND) {
+ /* drop duplicate legacy advertising */
+ return true;
+ } else if (dup->adv_mode[adv_mode].set_count == 0U) {
+ /* report different extended adv mode */
+ dup_ext_adv_adi_store(&dup->adv_mode[adv_mode], adi,
+ data_status);
return false;
- } else if (adi && ((dup_filter[i].adi.sid != adi->sid) ||
- (dup_filter[i].adi.did != adi->did))) {
- /* report different adi */
- store_adi(i, adi);
+ } else if (adi) {
+ struct dup_ext_adv_mode *dup_mode;
+ uint8_t j;
- dup_filter[i].data_cmplt = !data_status;
+ dup_mode = &dup->adv_mode[adv_mode];
+ for (j = 0; j < dup_mode->set_count; j++) {
+ struct dup_ext_adv_set *adv_set;
- return false;
- } else if (!dup_filter[i].data_cmplt && !data_status) {
- /* report data complete */
- dup_filter[i].data_cmplt = !data_status;
+ adv_set = &dup_mode->set[j];
+ if (adv_set->adi.sid != adi->sid) {
+ continue;
+ }
+
+ if (adv_set->adi.did != adi->did) {
+ /* report different DID */
+ adv_set->adi.did = adi->did;
+ return false;
+ } else if (!adv_set->data_cmplt && !data_status) {
+ /* report data complete */
+ adv_set->data_cmplt = !data_status;
+ return false;
+ } else if (!adv_set->data_cmplt) {
+ /* report partial and incomplete data */
+ return false;
+ }
+
+ return true;
+ }
+
+ dup_ext_adv_adi_store(&dup->adv_mode[adv_mode], adi,
+ data_status);
#endif /* CONFIG_BT_CTLR_ADV_EXT */
return false;
@@ -4720,31 +4788,38 @@
{
/* check for duplicate filtering */
if (dup_count >= 0) {
+ struct dup_entry *dup;
int i;
+#if defined(CONFIG_BT_CTLR_ADV_EXT)
+ __ASSERT((adv_mode < ARRAY_SIZE(dup_filter[0].adv_mode)),
+ "adv_mode index out-of-bound");
+#endif /* CONFIG_BT_CTLR_ADV_EXT */
+
/* find for existing entry and update if changed */
for (i = 0; i < dup_count; i++) {
- if (memcmp(addr, &dup_filter[i].addr.a.val[0],
+ dup = &dup_filter[i];
+ if (memcmp(addr, &dup->addr.a.val[0],
sizeof(bt_addr_t)) ||
- (addr_type != dup_filter[i].addr.type)) {
+ (addr_type != dup->addr.type)) {
continue;
}
/* still duplicate or update entry with change */
- return is_dup_or_update(i, adv_type, adv_mode, adi,
+ return is_dup_or_update(dup, adv_type, adv_mode, adi,
data_status);
}
/* insert into the duplicate filter */
- memcpy(&dup_filter[dup_curr].addr.a.val[0], addr,
- sizeof(bt_addr_t));
- dup_filter[dup_curr].addr.type = addr_type;
- dup_filter[dup_curr].mask = BIT(adv_type);
+ dup = &dup_filter[dup_curr];
+ (void)memcpy(&dup->addr.a.val[0], addr, sizeof(bt_addr_t));
+ dup->addr.type = addr_type;
+ dup->mask = BIT(adv_type);
#if defined(CONFIG_BT_CTLR_ADV_EXT)
- dup_filter[dup_curr].adv_mode = adv_mode;
- dup_filter[i].data_cmplt = !data_status;
- store_adi(dup_curr, adi);
+ dup_ext_adv_mode_reset(dup->adv_mode);
+ dup_ext_adv_adi_store(&dup->adv_mode[adv_mode], adi,
+ data_status);
#endif /* CONFIG_BT_CTLR_ADV_EXT */
if (dup_count < CONFIG_BT_CTLR_DUP_FILTER_LEN) {