Bluetooth: controller: Implement scan duplicate filter

Implement the controller's ability to drop duplicate advertising reports
when instructed by the Host.

Jira: ZEP-1246

Change-Id: I2d1b7abf1ed950dde705e5df30a858c595f3834c
Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
diff --git a/subsys/bluetooth/controller/Kconfig b/subsys/bluetooth/controller/Kconfig
index 01fa344..513e8a0 100644
--- a/subsys/bluetooth/controller/Kconfig
+++ b/subsys/bluetooth/controller/Kconfig
@@ -82,6 +82,14 @@
 	int
 	default 320
 
+config BLUETOOTH_CONTROLLER_DUP_FILTER_LEN
+	prompt "Number of addresses in the scan duplicate filter"
+	int
+	default 16
+	help
+	  Set the number of unique BLE addresses that can be filtered as
+	  duplicates while scanning.
+
 comment "BLE Controller features"
 
 config BLUETOOTH_CONTROLLER_LE_PING
diff --git a/subsys/bluetooth/controller/hci/hci.c b/subsys/bluetooth/controller/hci/hci.c
index 76ca6dd..34d8fb2 100644
--- a/subsys/bluetooth/controller/hci/hci.c
+++ b/subsys/bluetooth/controller/hci/hci.c
@@ -35,6 +35,17 @@
  */
 static uint16_t _opcode;
 
+#if CONFIG_BLUETOOTH_CONTROLLER_DUP_FILTER_LEN > 0
+/* Scan duplicate filter */
+struct dup {
+		uint8_t mask;
+		bt_addr_le_t addr;
+};
+static struct dup dup_filter[CONFIG_BLUETOOTH_CONTROLLER_DUP_FILTER_LEN];
+static int32_t dup_count;
+static uint32_t dup_curr;
+#endif
+
 static void evt_create(struct net_buf *buf, uint8_t evt, uint8_t len)
 {
 	struct bt_hci_evt_hdr *hdr;
@@ -143,6 +154,10 @@
 
 	ll_reset();
 
+#if CONFIG_BLUETOOTH_CONTROLLER_DUP_FILTER_LEN > 0
+	dup_count = -1;
+#endif
+
 	ccst = cmd_complete(evt, sizeof(*ccst));
 	ccst->status = 0x00;
 }
@@ -411,6 +426,15 @@
 	struct bt_hci_evt_cc_status *ccst;
 	uint32_t status;
 
+#if CONFIG_BLUETOOTH_CONTROLLER_DUP_FILTER_LEN > 0
+	/* initialize duplicate filtering */
+	if (cmd->enable && cmd->filter_dup) {
+		dup_count = 0;
+		dup_curr = 0;
+	} else {
+		dup_count = -1;
+	}
+#endif
 	status = ll_scan_enable(cmd->enable);
 
 	ccst = cmd_complete(evt, sizeof(*ccst));
@@ -973,6 +997,46 @@
 	uint8_t data_len;
 	uint8_t *rssi;
 	uint8_t info_len;
+	int i;
+
+#if CONFIG_BLUETOOTH_CONTROLLER_DUP_FILTER_LEN > 0
+	/* check for duplicate filtering */
+	if (dup_count >= 0) {
+		for (i = 0; i < dup_count; i++) {
+			if (!memcmp(&adv->payload.adv_ind.addr[0],
+				    &dup_filter[i].addr.a.val[0],
+				    sizeof(bt_addr_t)) &&
+			    adv->tx_addr == dup_filter[i].addr.type) {
+
+				if (dup_filter[i].mask & BIT(adv->type)) {
+					/* duplicate found */
+					return;
+				}
+				/* report different adv types */
+				dup_filter[i].mask |= BIT(adv->type);
+				goto fill_report;
+			}
+		}
+
+		/* insert into the duplicate filter */
+		memcpy(&dup_filter[dup_curr].addr.a.val[0],
+		       &adv->payload.adv_ind.addr[0], sizeof(bt_addr_t));
+		dup_filter[dup_curr].addr.type = adv->tx_addr;
+		dup_filter[dup_curr].mask = BIT(adv->type);
+
+		if (dup_count < CONFIG_BLUETOOTH_CONTROLLER_DUP_FILTER_LEN) {
+			dup_count++;
+			dup_curr = dup_count;
+		} else {
+			dup_curr++;
+		}
+
+		if (dup_curr == CONFIG_BLUETOOTH_CONTROLLER_DUP_FILTER_LEN) {
+			dup_curr = 0;
+		}
+	}
+fill_report:
+#endif
 
 	if (adv->type != PDU_ADV_TYPE_DIRECT_IND) {
 		data_len = (adv->len - BDADDR_SIZE);
diff --git a/tests/bluetooth/shell/src/main.c b/tests/bluetooth/shell/src/main.c
index dc04ef9..b5a00a6 100644
--- a/tests/bluetooth/shell/src/main.c
+++ b/tests/bluetooth/shell/src/main.c
@@ -566,11 +566,20 @@
 	return 0;
 }
 
-static void cmd_active_scan_on(void)
+static void cmd_active_scan_on(int dups)
 {
 	int err;
+	struct bt_le_scan_param param = {
+			.type       = BT_HCI_LE_SCAN_PASSIVE,
+			.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE,
+			.interval   = BT_GAP_SCAN_FAST_INTERVAL,
+			.window     = BT_GAP_SCAN_FAST_WINDOW };
 
-	err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, device_found);
+	if (dups >= 0) {
+		param.filter_dup = dups;
+	}
+
+	err = bt_le_scan_start(&param, device_found);
 	if (err) {
 		printk("Bluetooth set active scan failed (err %d)\n", err);
 	} else {
@@ -578,7 +587,7 @@
 	}
 }
 
-static void cmd_passive_scan_on(void)
+static void cmd_passive_scan_on(int dups)
 {
 	struct bt_le_scan_param param = {
 			.type       = BT_HCI_LE_SCAN_PASSIVE,
@@ -587,6 +596,10 @@
 			.window     = 0x10 };
 	int err;
 
+	if (dups >= 0) {
+		param.filter_dup = dups;
+	}
+
 	err = bt_le_scan_start(&param, device_found);
 	if (err) {
 		printk("Bluetooth set passive scan failed (err %d)\n", err);
@@ -610,18 +623,32 @@
 static int cmd_scan(int argc, char *argv[])
 {
 	const char *action;
+	int dups = -1;
 
 	if (argc < 2) {
 		return -EINVAL;
 	}
 
+	/* Parse duplicate filtering data */
+	if (argc >= 3) {
+		const char *dup_filter = argv[2];
+
+		if (!strcmp(dup_filter, "dups")) {
+			dups = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE;
+		} else if (!strcmp(dup_filter, "nodups")) {
+			dups = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE;
+		} else {
+			return -EINVAL;
+		}
+	}
+
 	action = argv[1];
 	if (!strcmp(action, "on")) {
-		cmd_active_scan_on();
+		cmd_active_scan_on(dups);
 	} else if (!strcmp(action, "off")) {
 		cmd_scan_off();
 	} else if (!strcmp(action, "passive")) {
-		cmd_passive_scan_on();
+		cmd_passive_scan_on(dups);
 	} else {
 		return -EINVAL;
 	}
@@ -2494,7 +2521,7 @@
 
 static const struct shell_cmd commands[] = {
 	{ "init", cmd_init, HELP_ADDR_LE },
-	{ "scan", cmd_scan, "<value: on, off>" },
+	{ "scan", cmd_scan, "<value: on, off> <dup filter: dups, nodups>" },
 	{ "advertise", cmd_advertise,
 	"<type: off, on, scan, nconn> <mode: discov, non_discov>"  },
 	{ "connect", cmd_connect_le, HELP_ADDR_LE },