hcitool: add discovery procedure to lescan command

This patch adds the option [--discovery=g|l] to lescan command. Use
this option to enable the general or limited discovery procedure.
If discovery option is not given scanning will display all results
ignoring the AD flags.
diff --git a/tools/hcitool.c b/tools/hcitool.c
index 8c147bf..c8829a4 100644
--- a/tools/hcitool.c
+++ b/tools/hcitool.c
@@ -49,6 +49,10 @@
 /* Unofficial value, might still change */
 #define LE_LINK		0x03
 
+#define FLAGS_AD_TYPE 0x01
+#define FLAGS_LIMITED_MODE_BIT 0x01
+#define FLAGS_GENERAL_MODE_BIT 0x02
+
 #define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, NULL)) != -1)
 
 static void usage(void);
@@ -2287,7 +2291,62 @@
 	hci_close_dev(dd);
 }
 
-static int print_advertising_devices(int dd)
+static int read_flags(uint8_t *flags, const uint8_t *data, size_t size)
+{
+	unsigned int offset;
+
+	if (!flags || !data)
+		return -EINVAL;
+
+	offset = 0;
+	while (offset < size) {
+		uint8_t len = data[offset];
+		uint8_t type = data[offset + 1];
+
+		/* Check if it is the end of the significant part */
+		if (len == 0)
+			break;
+
+		if (type == FLAGS_AD_TYPE) {
+			*flags = data[offset + 2];
+			return 0;
+		}
+
+		offset += 1 + len;
+	}
+
+	return -ENOENT;
+}
+
+static int check_report_filter(uint8_t procedure, le_advertising_info *info)
+{
+	uint8_t flags;
+
+	/* If no discovery procedure is set, all reports are treat as valid */
+	if (procedure == 0)
+		return 1;
+
+	/* Read flags AD type value from the advertising report if it exists */
+	if (read_flags(&flags, info->data, info->length))
+		return 0;
+
+	switch (procedure) {
+	case 'l': /* Limited Discovery Procedure */
+		if (flags & FLAGS_LIMITED_MODE_BIT)
+			return 1;
+		break;
+	case 'g': /* General Discovery Procedure */
+		if (flags & (FLAGS_LIMITED_MODE_BIT | FLAGS_GENERAL_MODE_BIT))
+			return 1;
+		break;
+	default:
+		fprintf(stderr, "Unknown discovery procedure\n");
+	}
+
+	return 0;
+}
+
+static int print_advertising_devices(int dd, uint8_t filter_type)
 {
 	unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
 	struct hci_filter nf, of;
@@ -2334,8 +2393,10 @@
 
 		/* Ignoring multiple reports */
 		info = (le_advertising_info *) (meta->data + 1);
-		ba2str(&info->bdaddr, addr);
-		printf("%s\n", addr);
+		if (check_report_filter(filter_type, info)) {
+			ba2str(&info->bdaddr, addr);
+			printf("%s\n", addr);
+		}
 	}
 
 done:
@@ -2351,19 +2412,23 @@
 	{ "help",	0, 0, 'h' },
 	{ "privacy",	0, 0, 'p' },
 	{ "passive",	0, 0, 'P' },
+	{ "discovery",	1, 0, 'd' },
 	{ 0, 0, 0, 0 }
 };
 
 static const char *lescan_help =
 	"Usage:\n"
 	"\tlescan [--privacy] enable privacy\n"
-	"\tlescan [--passive] set scan type passive (default active)\n";
+	"\tlescan [--passive] set scan type passive (default active)\n"
+	"\tlescan [--discovery=g|l] enable general or limited discovery"
+		"procedure\n";
 
 static void cmd_lescan(int dev_id, int argc, char **argv)
 {
 	int err, opt, dd;
 	uint8_t own_type = 0x00;
 	uint8_t scan_type = 0x01;
+	uint8_t filter_type = 0;
 
 	for_each_opt(opt, lescan_options, NULL) {
 		switch (opt) {
@@ -2373,6 +2438,13 @@
 		case 'P':
 			scan_type = 0x00; /* Passive */
 			break;
+		case 'd':
+			filter_type = optarg[0];
+			if (filter_type != 'g' && filter_type != 'l') {
+				fprintf(stderr, "Unknown discovery procedure\n");
+				exit(1);
+			}
+			break;
 		default:
 			printf("%s", lescan_help);
 			return;
@@ -2404,7 +2476,7 @@
 
 	printf("LE Scan ...\n");
 
-	err = print_advertising_devices(dd);
+	err = print_advertising_devices(dd, filter_type);
 	if (err < 0) {
 		perror("Could not receive advertising events");
 		exit(1);