Implement start_discovery hciops callback
diff --git a/plugins/hciops.c b/plugins/hciops.c
index 965e9de..23e8915 100644
--- a/plugins/hciops.c
+++ b/plugins/hciops.c
@@ -56,11 +56,26 @@
#define DISCOV_INQ 1
#define DISCOV_SCAN 2
+#define TIMEOUT_BR_LE_SCAN 5120 /* TGAP(100)/2 */
+#define TIMEOUT_LE_SCAN 10240 /* TGAP(gen_disc_scan_min) */
+
+#define LENGTH_BR_INQ 0x08
+#define LENGTH_BR_LE_INQ 0x04
+
+static int hciops_start_scanning(int index, int timeout);
+
static int child_pipe[2] = { -1, -1 };
static guint child_io_id = 0;
static guint ctl_io_id = 0;
+enum adapter_type {
+ BR_EDR,
+ LE_ONLY,
+ BR_EDR_LE,
+ UNKNOWN,
+};
+
/* Commands sent by kernel on starting an adapter */
enum {
PENDING_BDADDR,
@@ -151,27 +166,74 @@
return dev->discov_state;
}
+static inline gboolean is_resolvname_enabled(void)
+{
+ return main_opts.name_resolv ? TRUE : FALSE;
+}
+
static void set_state(int index, int state)
{
+ struct btd_adapter *adapter;
struct dev_info *dev = &devs[index];
if (dev->discov_state == state)
return;
+ adapter = manager_find_adapter_by_id(index);
+ if (!adapter) {
+ error("No matching adapter found");
+ return;
+ }
+
dev->discov_state = state;
DBG("hci%d: new state %d", index, dev->discov_state);
switch (dev->discov_state) {
case DISCOV_HALTED:
+ if (adapter_get_state(adapter) == STATE_SUSPENDED)
+ return;
+
+ if (is_resolvname_enabled() &&
+ adapter_has_discov_sessions(adapter))
+ adapter_set_state(adapter, STATE_RESOLVNAME);
+ else
+ adapter_set_state(adapter, STATE_IDLE);
break;
case DISCOV_INQ:
- break;
case DISCOV_SCAN:
+ adapter_set_state(adapter, STATE_DISCOV);
break;
}
}
+static inline gboolean is_le_capable(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ return (main_opts.le && dev->features[4] & LMP_LE &&
+ dev->extfeatures[0] & LMP_HOST_LE) ? TRUE : FALSE;
+}
+
+static inline gboolean is_bredr_capable(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ return (dev->features[4] & LMP_NO_BREDR) == 0 ? TRUE : FALSE;
+}
+
+static int get_adapter_type(int index)
+{
+ if (is_le_capable(index) && is_bredr_capable(index))
+ return BR_EDR_LE;
+ else if (is_le_capable(index))
+ return LE_ONLY;
+ else if (is_bredr_capable(index))
+ return BR_EDR;
+
+ return UNKNOWN;
+}
+
static int ignore_device(struct hci_dev_info *di)
{
return hci_test_bit(HCI_RAW, &di->flags) || di->type >> 4 != HCI_BREDR;
@@ -1831,12 +1893,30 @@
static inline void inquiry_complete_evt(int index, uint8_t status)
{
+ int adapter_type;
+ struct btd_adapter *adapter;
+
if (status) {
error("Inquiry Failed with status 0x%02x", status);
return;
}
- set_state(index, DISCOV_HALTED);
+ adapter = manager_find_adapter_by_id(index);
+ if (!adapter) {
+ error("No matching adapter found");
+ return;
+ }
+
+ adapter_type = get_adapter_type(index);
+
+ if (adapter_type == BR_EDR_LE &&
+ adapter_has_discov_sessions(adapter)) {
+ int err = hciops_start_scanning(index, TIMEOUT_BR_LE_SCAN);
+ if (err < 0)
+ set_state(index, DISCOV_HALTED);
+ } else {
+ set_state(index, DISCOV_HALTED);
+ }
}
static inline void cc_inquiry_cancel(int index, uint8_t status)
@@ -3159,8 +3239,18 @@
static int hciops_start_discovery(int index)
{
- DBG("index %d", index);
- return -ENOSYS;
+ int adapter_type = get_adapter_type(index);
+
+ switch (adapter_type) {
+ case BR_EDR_LE:
+ return hciops_start_inquiry(index, LENGTH_BR_LE_INQ, FALSE);
+ case BR_EDR:
+ return hciops_start_inquiry(index, LENGTH_BR_INQ, FALSE);
+ case LE_ONLY:
+ return hciops_start_scanning(index, TIMEOUT_LE_SCAN);
+ default:
+ return -EINVAL;
+ }
}
static int hciops_stop_discovery(int index)