2014-03-12 14:43:51

by Jakub Tyszkowski

[permalink] [raw]
Subject: [PATCH 0/4] Support LE scan in GATT

This patch set exposes LE scan functionality in Core Bluetooth API to other
profiles i.e. GATT. Simple callbacks were added to notify GATT about device
discovery events. Last patch mekes use of these extensions to perform LE device
scan.

Jakub Tyszkowski (4):
android/bluetooth: Refactor start/stop discovery
android/bluetooth: Add GATT notifications on LE discovery
android/bluetooth: Expose LE scan in bluetooth API
android/gatt: Use Core profile for LE scan

android/bluetooth.c | 103 ++++++++++++++++++++++++++++++++++++++++++----------
android/bluetooth.h | 10 +++++
android/gatt.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 186 insertions(+), 22 deletions(-)

--
1.9.0



2014-03-12 14:43:54

by Jakub Tyszkowski

[permalink] [raw]
Subject: [PATCH 3/4] android/bluetooth: Expose LE scan in bluetooth API

Allow GATT to use Core scan functionality instead doubling this
functionality in GATT.
---
android/bluetooth.c | 18 ++++++++++++++++++
android/bluetooth.h | 2 ++
2 files changed, 20 insertions(+)

diff --git a/android/bluetooth.c b/android/bluetooth.c
index 5430c8a..7ab5184 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -2506,6 +2506,24 @@ void bt_set_le_discovering_cb(bt_le_discovering_state_cb_t cb)
le_discovering_cb = cb;
}

+bool bt_write_le_scan(bool do_scan)
+{
+ if (!(adapter.current_settings & MGMT_SETTING_POWERED))
+ return false;
+
+ if (adapter.le_discovering == do_scan)
+ return true;
+
+ /* if core is discovering its interleaved scan, don't bother */
+ if (adapter.discovering)
+ return true;
+
+ if (do_scan)
+ return start_discovery(SCAN_TYPE_LE);
+
+ return stop_discovery(SCAN_TYPE_LE);
+}
+
static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len)
{
const uint8_t *mode = buf;
diff --git a/android/bluetooth.h b/android/bluetooth.h
index 127b9db..9855544 100644
--- a/android/bluetooth.h
+++ b/android/bluetooth.h
@@ -42,3 +42,5 @@ void bt_set_le_device_found_cb(bt_le_device_found_cb_t cb);

typedef void (*bt_le_discovering_state_cb_t)(uint8_t state);
void bt_set_le_discovering_cb(bt_le_discovering_state_cb_t cb);
+
+bool bt_write_le_scan(bool do_scan);
--
1.9.0


2014-03-12 14:43:55

by Jakub Tyszkowski

[permalink] [raw]
Subject: [PATCH 4/4] android/gatt: Use Core profile for LE scan

This makes gatt capable of triggering LE scan using functionality
exposed by Core API. GATT registers its own callbacks for discovering
events.
---
android/gatt.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 93 insertions(+), 2 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index a874737..27cc98d 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -32,10 +32,12 @@

#include "ipc.h"
#include "ipc-common.h"
-#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "bluetooth.h"
#include "gatt.h"
#include "src/log.h"
#include "hal-msg.h"
+#include "utils.h"
#include "src/shared/util.h"

struct gatt_client {
@@ -46,6 +48,7 @@ struct gatt_client {
static struct ipc *hal_ipc = NULL;
static bdaddr_t adapter_addr;
static GSList *gatt_clients = NULL;
+static GSList *scan_clients = NULL;

static int find_client_by_uuid(gconstpointer data, gconstpointer user_data)
{
@@ -107,6 +110,50 @@ failed:
HAL_OP_GATT_CLIENT_REGISTER, status);
}

+static bool set_client_scan(int32_t client_if, bool do_start)
+{
+ bool expected_on_scanlist = !do_start;
+ gpointer found_if;
+ bool is_registered = false;
+
+ /* TODO: Consider ignoring not registered clients unlike bluedroid. */
+ if (g_slist_find_custom(gatt_clients, INT_TO_PTR(client_if),
+ find_client_by_id))
+ is_registered = true;
+
+ /*
+ * Ignore scan requests from already scanning interfaces, end ignore
+ * stop requests from not scanning ones.
+ */
+ found_if = g_slist_find(scan_clients, INT_TO_PTR(client_if));
+
+ if (!!found_if != expected_on_scanlist) {
+ error("gatt: Scan already %sed", do_start ? "start" : "stopp");
+ return true;
+ }
+
+ if (bt_write_le_scan(do_start)) {
+ /*
+ * Keep track of registered clients only. We won't stop scanning
+ * till all registered scanning clients requested to stop.
+ */
+ if (!is_registered)
+ return true;
+
+ if (do_start)
+ scan_clients = g_slist_prepend(scan_clients,
+ INT_TO_PTR(client_if));
+ else
+ scan_clients = g_slist_remove(scan_clients,
+ INT_TO_PTR(client_if));
+
+ return true;
+ }
+
+ error("gatt: LE scan switch failed");
+ return false;
+}
+
static void handle_client_unregister(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_unregister *cmd = buf;
@@ -124,6 +171,7 @@ static void handle_client_unregister(const void *buf, uint16_t len)
}

gatt_clients = g_slist_remove(gatt_clients, l->data);
+ set_client_scan(cmd->client_if, false);

status = HAL_STATUS_SUCCESS;

@@ -132,12 +180,49 @@ failed:
HAL_OP_GATT_CLIENT_UNREGISTER, status);
}

+static void le_device_found_handler(bdaddr_t *addr, uint8_t addr_type, int rssi,
+ uint16_t len, const void *data)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
+ char bda[18];
+
+ if (!g_slist_length(scan_clients))
+ return;
+
+ ba2str(addr, bda);
+ DBG("gatt: LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi,
+ data ? true : false);
+
+ bdaddr2android(addr, ev->bda);
+ ev->rssi = rssi;
+ ev->len = len;
+
+ memcpy(ev->adv_data, data, ev->len);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_SCAN_RESULT, sizeof(ev) + len, ev);
+}
+
+static void le_discovering_handler(uint8_t discovering)
+{
+ /* resume scanning if there are any clients left */
+ if (!discovering && g_slist_length(scan_clients))
+ bt_write_le_scan(true);
+}
+
static void handle_client_scan(const void *buf, uint16_t len)
{
+ const struct hal_cmd_gatt_client_scan *cmd = buf;
+ uint8_t status;
+
DBG("");

+ status = set_client_scan(cmd->client_if, cmd->start) ?
+ HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
- HAL_STATUS_FAILED);
+ status);
}

static void handle_client_connect(const void *buf, uint16_t len)
@@ -524,6 +609,9 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers,
G_N_ELEMENTS(cmd_handlers));

+ bt_set_le_device_found_cb(le_device_found_handler);
+ bt_set_le_discovering_cb(le_discovering_handler);
+
return true;
}

@@ -533,4 +621,7 @@ void bt_gatt_unregister(void)

ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT);
hal_ipc = NULL;
+
+ bt_set_le_device_found_cb(NULL);
+ bt_set_le_discovering_cb(NULL);
}
--
1.9.0


2014-03-12 14:43:53

by Jakub Tyszkowski

[permalink] [raw]
Subject: [PATCH 2/4] android/bluetooth: Add GATT notifications on LE discovery

This allows GATT to be notified about scan events.
---
android/bluetooth.c | 48 +++++++++++++++++++++++++++++++++++++++++++-----
android/bluetooth.h | 8 ++++++++
2 files changed, 51 insertions(+), 5 deletions(-)

diff --git a/android/bluetooth.c b/android/bluetooth.c
index e614883..5430c8a 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -121,6 +121,7 @@ static struct {
uint32_t current_settings;

bool discovering;
+ bool le_discovering;
uint32_t discoverable_timeout;

GSList *uuids;
@@ -130,6 +131,7 @@ static struct {
.name = NULL,
.current_settings = 0,
.discovering = false,
+ .le_discovering = false,
.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT,
.uuids = NULL,
};
@@ -147,6 +149,9 @@ static struct mgmt *mgmt_if = NULL;
static GSList *bonded_devices = NULL;
static GSList *cached_devices = NULL;

+bt_le_device_found_cb_t le_device_found_cb = NULL;
+bt_le_discovering_state_cb_t le_discovering_cb = NULL;
+
/* This list contains addresses which are asked for records */
static GSList *browse_reqs;

@@ -999,6 +1004,8 @@ static void mgmt_discovering_event(uint16_t index, uint16_t length,
{
const struct mgmt_ev_discovering *ev = param;
struct hal_ev_discovery_state_changed cp;
+ bool notify = false;
+ bool notify_le = false;

if (length < sizeof(*ev)) {
error("Too small discovering event");
@@ -1008,10 +1015,20 @@ static void mgmt_discovering_event(uint16_t index, uint16_t length,
DBG("hci%u type %u discovering %u", index, ev->type,
ev->discovering);

- if (adapter.discovering == !!ev->discovering)
- return;
+ if ((ev->type & SCAN_TYPE_BREDR) &&
+ (adapter.discovering != !!ev->discovering)) {
+ adapter.discovering = !!ev->discovering;
+ notify = true;
+ }
+
+ if ((ev->type & SCAN_TYPE_LE) &&
+ (adapter.le_discovering != !!ev->discovering)) {
+ adapter.le_discovering = !!ev->discovering;
+ notify_le = true;
+ }

- adapter.discovering = !!ev->discovering;
+ if (!(notify || notify_le))
+ return;

DBG("new discovering state %u", ev->discovering);

@@ -1023,8 +1040,13 @@ static void mgmt_discovering_event(uint16_t index, uint16_t length,
cp.state = HAL_DISCOVERY_STATE_STOPPED;
}

- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
- HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(cp), &cp);
+ if (notify)
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+ HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(cp),
+ &cp);
+
+ if (notify_le && le_discovering_cb)
+ le_discovering_cb(adapter.le_discovering);
}

static void confirm_device_name(const bdaddr_t *addr, uint8_t addr_type,
@@ -1121,6 +1143,12 @@ static void update_new_device(struct device *dev, int8_t rssi,

ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_DEVICE_FOUND,
size, buf);
+
+ /* Notify Gatt if its registered for LE events */
+ if (le_device_found_cb && adapter.le_discovering)
+ if (dev->bdaddr_type & (BDADDR_LE_PUBLIC | BDADDR_LE_RANDOM))
+ le_device_found_cb(&dev->bdaddr, dev->bdaddr_type,
+ dev->rssi, sizeof(*eir), eir);
}

static void update_device(struct device *dev, int8_t rssi,
@@ -2468,6 +2496,16 @@ static bool stop_discovery(uint8_t type)
return false;
}

+void bt_set_le_device_found_cb(bt_le_device_found_cb_t cb)
+{
+ le_device_found_cb = cb;
+}
+
+void bt_set_le_discovering_cb(bt_le_discovering_state_cb_t cb)
+{
+ le_discovering_cb = cb;
+}
+
static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len)
{
const uint8_t *mode = buf;
diff --git a/android/bluetooth.h b/android/bluetooth.h
index f436178..127b9db 100644
--- a/android/bluetooth.h
+++ b/android/bluetooth.h
@@ -34,3 +34,11 @@ void bt_bluetooth_unregister(void);

int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint);
void bt_adapter_remove_record(uint32_t handle);
+
+typedef void (*bt_le_device_found_cb_t)(bdaddr_t *addr, uint8_t addr_type,
+ int rssi, uint16_t len,
+ const void *data);
+void bt_set_le_device_found_cb(bt_le_device_found_cb_t cb);
+
+typedef void (*bt_le_discovering_state_cb_t)(uint8_t state);
+void bt_set_le_discovering_cb(bt_le_discovering_state_cb_t cb);
--
1.9.0


2014-03-12 14:43:52

by Jakub Tyszkowski

[permalink] [raw]
Subject: [PATCH 1/4] android/bluetooth: Refactor start/stop discovery

Pass discovery type to discovery stop/start routine so it can be used
also for LE only scan.
---
android/bluetooth.c | 37 ++++++++++++++++++++++---------------
1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/android/bluetooth.c b/android/bluetooth.c
index f2b9321..e614883 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -80,6 +80,10 @@
#define BASELEN_REMOTE_DEV_PROP (sizeof(struct hal_ev_remote_device_props) \
+ sizeof(struct hal_property))

+#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
+#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
+#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
+
struct device {
bdaddr_t bdaddr;
uint8_t bdaddr_type;
@@ -2417,17 +2421,26 @@ static void get_adapter_properties(void)
get_adapter_discoverable_timeout();
}

-static bool start_discovery(void)
+static uint8_t get_adapter_discovering_type()
{
- struct mgmt_cp_start_discovery cp;
+ uint8_t type;

if (adapter.current_settings & MGMT_SETTING_BREDR)
- cp.type = 1 << BDADDR_BREDR;
+ type = SCAN_TYPE_BREDR;
else
- cp.type = 0;
+ type = 0;

if (adapter.current_settings & MGMT_SETTING_LE)
- cp.type |= (1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM);
+ type |= SCAN_TYPE_LE;
+
+ return type;
+}
+
+static bool start_discovery(uint8_t type)
+{
+ struct mgmt_cp_start_discovery cp;
+
+ cp.type = get_adapter_discovering_type() & type;

DBG("type=0x%x", cp.type);

@@ -2439,17 +2452,11 @@ static bool start_discovery(void)
return false;
}

-static bool stop_discovery(void)
+static bool stop_discovery(uint8_t type)
{
struct mgmt_cp_stop_discovery cp;

- if (adapter.current_settings & MGMT_SETTING_BREDR)
- cp.type = 1 << BDADDR_BREDR;
- else
- cp.type = 0;
-
- if (adapter.current_settings & MGMT_SETTING_LE)
- cp.type |= (1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM);
+ cp.type = get_adapter_discovering_type() & type;

DBG("type=0x%x", cp.type);

@@ -3140,7 +3147,7 @@ static void handle_start_discovery_cmd(const void *buf, uint16_t len)
goto reply;
}

- if (!start_discovery()) {
+ if (!start_discovery(SCAN_TYPE_DUAL)) {
status = HAL_STATUS_FAILED;
goto reply;
}
@@ -3165,7 +3172,7 @@ static void handle_cancel_discovery_cmd(const void *buf, uint16_t len)
goto reply;
}

- if (!stop_discovery()) {
+ if (!stop_discovery(SCAN_TYPE_DUAL)) {
status = HAL_STATUS_FAILED;
goto reply;
}
--
1.9.0