*v2: Rebased remaining patches.
*v1: Addressed comments by jamuraa and vudentz:
- Now passing bt_att instead of bdaddr_t in gatt_db callbacks and functions.
I have not run the tests on the android side here, so I would appreciate it
if you can run them.
- Renamed src/gatt-server to src/gatt-database for now. Added TODO item for
refactoring this later.
- Updated the TODO items for GATT.
This patch set includes patches that rewrite the local GATT server using
shared/gatt. This in effect invalidates the existing src/attrib-server in
favor of a new src/gatt-server.
Arman Uguray (8):
core: adapter: Initialize GATT database
core: Attach gatt-server to bt_att
core: Add GATT UUIDs to Adapter1.UUIDs
core: device: Add getter for GATT server
core: gatt: Send not/ind to devices
core: adapter: Send UUIDs changed for GATT services
shared/gatt: Don't incorrectly terminate discovery
TODO: Update GATT items.
TODO | 54 ++++++++++++++++++---------------
src/adapter.c | 77 +++++++++++++++++++++++++++++++++++++++++-------
src/adapter.h | 2 ++
src/device.c | 55 ++++++++++++++++++++++++----------
src/device.h | 1 +
src/gatt-database.c | 25 ++++++++++++++--
src/shared/gatt-client.c | 3 +-
7 files changed, 162 insertions(+), 55 deletions(-)
--
2.2.0.rc0.207.ga3a616c
Hi Arman,
On Wed, Feb 18, 2015 at 10:18 AM, Arman Uguray <[email protected]> wrote:
> *v2: Rebased remaining patches.
>
> *v1: Addressed comments by jamuraa and vudentz:
> - Now passing bt_att instead of bdaddr_t in gatt_db callbacks and functions.
> I have not run the tests on the android side here, so I would appreciate it
> if you can run them.
> - Renamed src/gatt-server to src/gatt-database for now. Added TODO item for
> refactoring this later.
> - Updated the TODO items for GATT.
>
> This patch set includes patches that rewrite the local GATT server using
> shared/gatt. This in effect invalidates the existing src/attrib-server in
> favor of a new src/gatt-server.
>
>
> Arman Uguray (8):
> core: adapter: Initialize GATT database
> core: Attach gatt-server to bt_att
> core: Add GATT UUIDs to Adapter1.UUIDs
> core: device: Add getter for GATT server
> core: gatt: Send not/ind to devices
> core: adapter: Send UUIDs changed for GATT services
> shared/gatt: Don't incorrectly terminate discovery
> TODO: Update GATT items.
>
> TODO | 54 ++++++++++++++++++---------------
> src/adapter.c | 77 +++++++++++++++++++++++++++++++++++++++++-------
> src/adapter.h | 2 ++
> src/device.c | 55 ++++++++++++++++++++++++----------
> src/device.h | 1 +
> src/gatt-database.c | 25 ++++++++++++++--
> src/shared/gatt-client.c | 3 +-
> 7 files changed, 162 insertions(+), 55 deletions(-)
>
> --
> 2.2.0.rc0.207.ga3a616c
Applied, note that I did add support for BR/EDR that was missing, but
now we need to fix the following problems:
bluetoothd[28074]: plugins/gatt-example.c:gatt_example_adapter_probe()
Battery service could not be registered
bluetoothd[28074]: gatt-example-adapter-driver: Input/output error (5)
bluetoothd[28074]: Not enough free handles to register service
bluetoothd[28074]: Error adding Link Loss service
bluetoothd[28074]: Not enough free handles to register service
bluetoothd[28074]: Not enough free handles to register service
bluetoothd[28074]:
profiles/proximity/reporter.c:reporter_adapter_probe() Proximity
Reporter for adapter 0x5e86af0
bluetoothd[28074]: profiles/time/server.c:time_server_init() path
/org/bluez/hci0
bluetoothd[28074]: Not enough free handles to register service
bluetoothd[28074]: Current Time Service could not be registered
bluetoothd[28074]: gatt-time-server: Input/output error (5)
bluetoothd[28074]: Not enough free handles to register service
bluetoothd[28074]: Not enough free handles to register service
I guess it might be possible to leave attrib-server.h API but rewrite
the internals so that it actually uses bt_gatt_server and gatt_db, or
we can just rewrite the plugins and remove attrib-server altogether.
--
Luiz Augusto von Dentz
Updated the GATT/ATT related TODO items:
- Removed item about disconnect handling as this is already done via
bt_att.
- Long-term client caching is currently done in memory. Updated this
to mean peristent.
- GAttrib has already been turned into a shim around bt_att. Updated
the related item to involve updating profiles only.
- GATT client D-Bus API has been implemented.
- Added item for missing portion of Service Changed support.
- Added item for GATT related refactors discussed over IRC.
- Added item for supporting the Includes property in GATT client.
---
TODO | 54 ++++++++++++++++++++++++++++++------------------------
1 file changed, 30 insertions(+), 24 deletions(-)
diff --git a/TODO b/TODO
index e489221..db80b95 100644
--- a/TODO
+++ b/TODO
@@ -136,41 +136,19 @@ ATT/GATT (new shared stack)
Priority: Medium
Complexity: C1
-- Introduce a handler interface to shared/gatt-client which can be used by the
- upper layer to determine when the link has been disconnected or an ATT
- protocol request times out.
+- Persist client attribute cache across reboots.
Priority: Medium
- Complexity: C2
-
-- Introduce long-term caching of attributes to shared/gatt-client, such that the
- services, characteristics, and descriptors obtained from a peripheral are
- remembered in the case of bonding. This may involve storing data about GATT
- services to disk.
-
- Priority: Low
Complexity: C4
- Move all daemon plugins and profiles that are GATT based to use
shared/gatt-client instead of attrib/*. This is a complicated task that
potentially needs a new plugin/profile probing interface and a lot of
- rewriting that can cause regressions in existing functionality. The biggest
- challenge here is that an instance of bt_att (shared/att) and GAttrib
- (attrib/gattrib) cannot coexist on the same file descriptor, since they will
- cause ATT protocol violations by breaking the sequential request-response
- structure. A special shared/gatt-client-gattrib implementation may be
- necessary to move each profile/plugin to the new API before actually switching
- to the shared/att based implementation.
+ rewriting that can cause regressions in existing functionality.
Priority: Medium
Complexity: C4
-- Implement the client portion of doc/gatt-api.txt using shared/gatt-client once
- plugin/profile code uses it.
-
- Priority: Medium
- Complexity: C2
-
- Introduce a way for shared/gatt-server to check security permissions on the
current connection through bt_att.
@@ -190,6 +168,34 @@ ATT/GATT (new shared stack)
Priority: Medium
Complexity: C4
+- Send out indications from the "Service Changed" characteristic upon
+ reconnection if a bonded device is not connected when the local database is
+ modified.
+
+ Priority: High
+ Complexity: C2
+
+- Unify the GATT server and client D-Bus implementations into a single module.
+ While these don't share a lot of code, keeping them all in src/gatt-dbus seems
+ to make more sense from an organizational perspective.
+
+ Priority: Low
+ Complexity: C1
+
+- Isolate all GATT code inside the daemon into its own module and perform
+ interaction with other modules (e.g. src/device.c) via callbacks. This
+ includes client/server management, tracking incoming/outgoing connections for
+ ATT, and callbacks to perform profile probing.
+
+ Priority: Low
+ Complexity: C4
+
+- Support included services in the GATT D-Bus client API.
+
+ Priority: Medium
+ Complexity: C1
+
+
ATT/GATT (old/outdated)
=======================
--
2.2.0.rc0.207.ga3a616c
bt_gatt_client terminates discovery if no primary services are found
within the given range. This behavior is incorrect, as the given
handle range may contain secondary services and those should be
discovered regardless.
---
src/shared/gatt-client.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 5a3939b..90866df 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -914,7 +914,7 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
util_debug(client->debug_callback, client->debug_data,
"Primary service discovery failed."
" ATT ECODE: 0x%02x", att_ecode);
- goto done;
+ goto secondary;
}
if (!result || !bt_gatt_iter_init(&iter, result)) {
@@ -947,6 +947,7 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
queue_push_tail(op->pending_svcs, attr);
}
+secondary:
/* Discover secondary services */
if (bt_gatt_discover_secondary_services(client->att, NULL,
op->start, op->end,
--
2.2.0.rc0.207.ga3a616c
btd_adapter now sends a PropertiesChanged signal for the "UUIDs"
property when its associated gatt_db is modified.
---
src/adapter.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/src/adapter.c b/src/adapter.c
index 03e359b..fdf7bc5 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -229,6 +229,8 @@ struct btd_adapter {
unsigned int pair_device_id;
guint pair_device_timeout;
+ unsigned int db_id; /* Service event handler for GATT db */
+
bool is_default; /* true if adapter is default one */
};
@@ -4593,6 +4595,7 @@ static struct btd_adapter *btd_adapter_new(uint16_t index)
static void adapter_remove(struct btd_adapter *adapter)
{
GSList *l;
+ struct gatt_db *db;
DBG("Removing adapter %s", adapter->path);
@@ -4618,6 +4621,11 @@ static void adapter_remove(struct btd_adapter *adapter)
adapter->devices = NULL;
unload_drivers(adapter);
+
+ db = btd_gatt_database_get_db(adapter->database);
+ gatt_db_unregister(db, adapter->db_id);
+ adapter->db_id = 0;
+
btd_gatt_database_destroy(adapter->database);
g_slist_free(adapter->pin_callbacks);
@@ -6627,9 +6635,18 @@ static int set_did(struct btd_adapter *adapter, uint16_t vendor,
return -EIO;
}
+static void services_modified(struct gatt_db_attribute *attrib, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "UUIDs");
+}
+
static int adapter_register(struct btd_adapter *adapter)
{
struct agent *agent;
+ struct gatt_db *db;
if (powering_down)
return -EBUSY;
@@ -6664,6 +6681,11 @@ static int adapter_register(struct btd_adapter *adapter)
if (!adapter->database)
error("Failed to create GATT database for adapter");
+ db = btd_gatt_database_get_db(adapter->database);
+ adapter->db_id = gatt_db_register(db, services_modified,
+ services_modified,
+ adapter, NULL);
+
load_config(adapter);
fix_storage(adapter);
load_drivers(adapter);
--
2.2.0.rc0.207.ga3a616c
This patch adds code that sends out characteristic value
notification/indication PDUs by obtaining the bt_gatt_server
from each device that has the associated CCC configured.
---
src/gatt-database.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 1a77e6b..710f277 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -496,6 +496,11 @@ struct not_data {
bool indicate;
};
+static void conf_cb(void *user_data)
+{
+ DBG("GATT server received confirmation");
+}
+
static void send_notification_to_device(void *data, void *user_data)
{
struct device_state *device_state = data;
@@ -517,8 +522,24 @@ static void send_notification_to_device(void *data, void *user_data)
return;
/*
- * TODO: Notify device via bt_gatt_server
+ * TODO: If the device is not connected but bonded, send the
+ * notification/indication when it becomes connected.
*/
+ if (!not_data->indicate) {
+ DBG("GATT server sending notification");
+ bt_gatt_server_send_notification(
+ btd_device_get_gatt_server(device),
+ not_data->handle, not_data->value,
+ not_data->len);
+ return;
+ }
+
+ DBG("GATT server sending indication");
+ bt_gatt_server_send_indication(btd_device_get_gatt_server(device),
+ not_data->handle,
+ not_data->value,
+ not_data->len, conf_cb,
+ NULL, NULL);
}
static void send_notification_to_devices(struct btd_gatt_database *database,
--
2.2.0.rc0.207.ga3a616c
Added btd_device_get_gatt_server function.
---
src/device.c | 8 ++++++++
src/device.h | 1 +
2 files changed, 9 insertions(+)
diff --git a/src/device.c b/src/device.c
index 678a6ac..3ce5897 100644
--- a/src/device.c
+++ b/src/device.c
@@ -5268,6 +5268,14 @@ struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device)
return device->client;
}
+struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device)
+{
+ if (!device)
+ return NULL;
+
+ return device->server;
+}
+
void btd_device_gatt_set_service_changed(struct btd_device *device,
uint16_t start, uint16_t end)
{
diff --git a/src/device.h b/src/device.h
index f250bfa..0387409 100644
--- a/src/device.h
+++ b/src/device.h
@@ -71,6 +71,7 @@ struct gatt_primary *btd_device_get_primary(struct btd_device *device,
GSList *btd_device_get_primaries(struct btd_device *device);
struct gatt_db *btd_device_get_gatt_db(struct btd_device *device);
struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device);
+struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device);
void btd_device_gatt_set_service_changed(struct btd_device *device,
uint16_t start, uint16_t end);
bool device_attach_att(struct btd_device *dev, GIOChannel *io);
--
2.2.0.rc0.207.ga3a616c
Modified src/adapter so that the UUIDs property includes UUIDs that
were added to the local GATT database.
---
src/adapter.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/src/adapter.c b/src/adapter.c
index 8886045..03e359b 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -53,6 +53,9 @@
#include "lib/mgmt.h"
#include "src/shared/mgmt.h"
#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
#include "hcid.h"
#include "sdpd.h"
@@ -2200,16 +2203,37 @@ static gboolean property_get_discovering(const GDBusPropertyTable *property,
return TRUE;
}
+static void add_gatt_uuid(struct gatt_db_attribute *attrib, void *user_data)
+{
+ DBusMessageIter *iter = user_data;
+ bt_uuid_t uuid, u128;
+ char uuidstr[MAX_LEN_UUID_STR + 1];
+ const char *ptr = uuidstr;
+
+ if (!gatt_db_service_get_active(attrib))
+ return;
+
+ if (!gatt_db_attribute_get_service_uuid(attrib, &uuid))
+ return;
+
+ bt_uuid_to_uuid128(&uuid, &u128);
+ bt_uuid_to_string(&u128, uuidstr, sizeof(uuidstr));
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
+}
+
static gboolean property_get_uuids(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
struct btd_adapter *adapter = user_data;
DBusMessageIter entry;
sdp_list_t *l;
+ struct gatt_db *db;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING_AS_STRING, &entry);
+ /* SDP records */
for (l = adapter->services; l != NULL; l = l->next) {
sdp_record_t *rec = l->data;
char *uuid;
@@ -2223,6 +2247,11 @@ static gboolean property_get_uuids(const GDBusPropertyTable *property,
free(uuid);
}
+ /* GATT services */
+ db = btd_gatt_database_get_db(adapter->database);
+ if (db)
+ gatt_db_foreach_service(db, NULL, add_gatt_uuid, &entry);
+
dbus_message_iter_close_container(iter, &entry);
return TRUE;
--
2.2.0.rc0.207.ga3a616c
With this patch, btd_device now creates a bt_gatt_server and attaches
it to the ATT transport which coexists with a bt_gatt_client.
---
src/device.c | 47 +++++++++++++++++++++++++++++++----------------
src/gatt-database.c | 2 --
2 files changed, 31 insertions(+), 18 deletions(-)
diff --git a/src/device.c b/src/device.c
index 05a4bd9..678a6ac 100644
--- a/src/device.c
+++ b/src/device.c
@@ -51,11 +51,13 @@
#include "src/shared/queue.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-client.h"
+#include "src/shared/gatt-server.h"
#include "btio/btio.h"
#include "lib/mgmt.h"
#include "attrib/att.h"
#include "hcid.h"
#include "adapter.h"
+#include "gatt-database.h"
#include "attrib/gattrib.h"
#include "attio.h"
#include "device.h"
@@ -211,7 +213,6 @@ struct btd_device {
GAttrib *attrib;
GSList *attios;
GSList *attios_offline;
- guint attachid; /* Attrib server attach */
struct bt_att *att; /* The new ATT transport */
uint16_t att_mtu; /* The ATT MTU */
@@ -224,6 +225,7 @@ struct btd_device {
*/
struct gatt_db *db; /* GATT db cache */
struct bt_gatt_client *client; /* GATT client instance */
+ struct bt_gatt_server *server; /* GATT server instance */
struct btd_gatt_client *client_dbus;
@@ -538,6 +540,15 @@ static void gatt_client_cleanup(struct btd_device *device)
gatt_db_clear(device->db);
}
+static void gatt_server_cleanup(struct btd_device *device)
+{
+ if (!device->server)
+ return;
+
+ bt_gatt_server_unref(device->server);
+ device->server = NULL;
+}
+
static void attio_cleanup(struct btd_device *device)
{
if (device->att_disconn_id)
@@ -551,6 +562,7 @@ static void attio_cleanup(struct btd_device *device)
}
gatt_client_cleanup(device);
+ gatt_server_cleanup(device);
if (device->att) {
bt_att_unref(device->att);
@@ -561,14 +573,6 @@ static void attio_cleanup(struct btd_device *device)
GAttrib *attrib = device->attrib;
device->attrib = NULL;
-
- if (device->attachid) {
- guint attachid = device->attachid;
-
- device->attachid = 0;
- attrib_channel_detach(attrib, attachid);
- }
-
g_attrib_cancel_all(attrib);
g_attrib_unref(attrib);
}
@@ -4123,6 +4127,20 @@ static void gatt_client_init(struct btd_device *device)
}
}
+static void gatt_server_init(struct btd_device *device, struct gatt_db *db)
+{
+ if (!db) {
+ error("No local GATT database exists for this adapter");
+ return;
+ }
+
+ gatt_server_cleanup(device);
+
+ device->server = bt_gatt_server_new(db, device->att, device->att_mtu);
+ if (!device->server)
+ error("Failed to initialize bt_gatt_server");
+}
+
bool device_attach_att(struct btd_device *dev, GIOChannel *io)
{
GError *gerr = NULL;
@@ -4130,6 +4148,7 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
BtIOSecLevel sec_level;
uint16_t mtu;
uint16_t cid;
+ struct btd_gatt_database *database;
bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level,
BT_IO_OPT_IMTU, &mtu,
@@ -4162,13 +4181,6 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
return false;
}
- dev->attachid = attrib_channel_attach(attrib);
- if (dev->attachid == 0) {
- g_attrib_unref(attrib);
- error("Attribute server attach failure!");
- return false;
- }
-
dev->attrib = attrib;
dev->att = g_attrib_get_att(attrib);
@@ -4178,7 +4190,10 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
att_disconnected_cb, dev, NULL);
bt_att_set_close_on_unref(dev->att, true);
+ database = btd_adapter_get_database(dev->adapter);
+
gatt_client_init(dev);
+ gatt_server_init(dev, btd_gatt_database_get_db(database));
/*
* Remove the device from the connect_list and give the passive
diff --git a/src/gatt-database.c b/src/gatt-database.c
index c69de15..1a77e6b 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -234,8 +234,6 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
if (!device)
return;
- /* TODO: create bt_gatt_server instance */
-
device_attach_att(device, io);
}
--
2.2.0.rc0.207.ga3a616c
This patch adds code that initializes a btd_gatt_database for a
btd_adapter. Also added is the btd_adapter_get_database getter
function.
---
src/adapter.c | 26 +++++++++++++++-----------
src/adapter.h | 2 ++
2 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index 5a6b45e..8886045 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -68,6 +68,7 @@
#include "attrib/att.h"
#include "attrib/gatt.h"
#include "attrib-server.h"
+#include "gatt-database.h"
#include "eir.h"
#define ADAPTER_INTERFACE "org.bluez.Adapter1"
@@ -204,6 +205,8 @@ struct btd_adapter {
struct btd_device *connect_le; /* LE device waiting to be connected */
sdp_list_t *services; /* Services associated to adapter */
+ struct btd_gatt_database *database;
+
gboolean initialized;
GSList *pin_callbacks;
@@ -279,7 +282,6 @@ static void dev_class_changed_callback(uint16_t index, uint16_t length,
{
struct btd_adapter *adapter = user_data;
const struct mgmt_cod *rp = param;
- uint8_t appearance[3];
uint32_t dev_class;
if (length < sizeof(*rp)) {
@@ -298,13 +300,6 @@ static void dev_class_changed_callback(uint16_t index, uint16_t length,
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Class");
-
- appearance[0] = rp->val[0];
- appearance[1] = rp->val[1] & 0x1f; /* removes service class */
- appearance[2] = rp->val[2];
-
- /* TODO: Do this through btd_gatt_database instead */
- attrib_gap_set(adapter, GATT_CHARAC_APPEARANCE, appearance, 2);
}
static void set_dev_class_complete(uint8_t status, uint16_t length,
@@ -3176,6 +3171,14 @@ bool btd_adapter_get_connectable(struct btd_adapter *adapter)
return false;
}
+struct btd_gatt_database *btd_adapter_get_database(struct btd_adapter *adapter)
+{
+ if (!adapter)
+ return NULL;
+
+ return adapter->database;
+}
+
uint32_t btd_adapter_get_class(struct btd_adapter *adapter)
{
return adapter->dev_class;
@@ -4586,7 +4589,7 @@ static void adapter_remove(struct btd_adapter *adapter)
adapter->devices = NULL;
unload_drivers(adapter);
- btd_adapter_gatt_server_stop(adapter);
+ btd_gatt_database_destroy(adapter->database);
g_slist_free(adapter->pin_callbacks);
adapter->pin_callbacks = NULL;
@@ -6628,8 +6631,9 @@ static int adapter_register(struct btd_adapter *adapter)
agent_unref(agent);
}
- /* TODO: Migrate to use btd_gatt_database */
- btd_adapter_gatt_server_start(adapter);
+ adapter->database = btd_gatt_database_new(adapter);
+ if (!adapter->database)
+ error("Failed to create GATT database for adapter");
load_config(adapter);
fix_storage(adapter);
diff --git a/src/adapter.h b/src/adapter.h
index 737479f..867f4e3 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -79,6 +79,8 @@ bool btd_adapter_get_pairable(struct btd_adapter *adapter);
bool btd_adapter_get_powered(struct btd_adapter *adapter);
bool btd_adapter_get_connectable(struct btd_adapter *adapter);
+struct btd_gatt_database *btd_adapter_get_database(struct btd_adapter *adapter);
+
uint32_t btd_adapter_get_class(struct btd_adapter *adapter);
const char *btd_adapter_get_name(struct btd_adapter *adapter);
void btd_adapter_remove_device(struct btd_adapter *adapter,
--
2.2.0.rc0.207.ga3a616c