From: Claudio Takahasi <[email protected]>
This patch disable automatic ATTIO connections when the adapter is
powered down and enable automatic connection when the adapter is powered
on.
---
src/adapter.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index 4b675e8..6b8435e 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -2113,6 +2113,14 @@ static int get_pairable_timeout(const char *src)
return main_opts.pairto;
}
+static void set_auto_connect(gpointer data, gpointer user_data)
+{
+ struct btd_device *device = data;
+ gboolean *enable = user_data;
+
+ device_set_auto_connect(device, *enable);
+}
+
static void call_adapter_powered_callbacks(struct btd_adapter *adapter,
gboolean powered)
{
@@ -2122,7 +2130,9 @@ static void call_adapter_powered_callbacks(struct btd_adapter *adapter,
btd_adapter_powered_cb cb = l->data;
cb(adapter, powered);
- }
+ }
+
+ g_slist_foreach(adapter->devices, set_auto_connect, &powered);
}
static void emit_device_disappeared(gpointer data, gpointer user_data)
@@ -3401,15 +3411,10 @@ static gboolean disable_auto(gpointer user_data)
return FALSE;
}
-static void set_auto_connect(gpointer data, gpointer user_data)
-{
- struct btd_device *device = data;
-
- device_set_auto_connect(device, TRUE);
-}
-
void btd_adapter_enable_auto_connect(struct btd_adapter *adapter)
{
+ gboolean enable = TRUE;
+
if (!adapter->up)
return;
@@ -3418,7 +3423,7 @@ void btd_adapter_enable_auto_connect(struct btd_adapter *adapter)
if (adapter->auto_timeout_id)
return;
- g_slist_foreach(adapter->devices, set_auto_connect, NULL);
+ g_slist_foreach(adapter->devices, set_auto_connect, &enable);
adapter->auto_timeout_id = g_timeout_add_seconds(main_opts.autoto,
disable_auto, adapter);
--
1.7.11.4
On Thu, Sep 20, 2012 at 8:43 AM, Johan Hedberg <[email protected]> wrote:
(...)
>
> As a general note (not relating specifically to this patch) I'd like to
> see a clearer split and naming of LE/GATT-specific functionality and
> variables (function names, struct btd_adapter/device member naming &
> grouping, etc.). Right now these seem to be sprinkled all over the place
> which can be confusing for someone looking at the code from a
> BR/EDR-only or LE-only perspective.
>
Any suggestions on how to improve that? I've tried to improve naming
for quite a while before sending these for the first time to the list,
and neither I nor anyone else from INdT could come up with better
naming.
I'm sending an updated version now, than you can comment about other
improvements on top of that.
--
João Paulo Rechi Vita
Openbossa Labs - INdT
Hi,
On Tue, Sep 11, 2012, Jo?o Paulo Rechi Vita wrote:
> Since controllers don't support more than one ongoing connection
> procedure at the same time, new connection attempts needs to yield if
> there is an ongoing connection procedure already.
> ---
> src/adapter.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++--------
> src/device.c | 6 +++---
> src/device.h | 2 +-
> 3 files changed, 57 insertions(+), 12 deletions(-)
Couple of coding style things I'd like to get fixed in this one. Patches
1/11 and 2/11 have been already applied though so no need to resend
them.
> + if (g_slist_length(adapter->connect_list))
> + mgmt_start_discovery(adapter->dev_id);
Since you're not testing for a boolean I'd rather have a clear > 0 here.
> const char *path = adapter->path;
> + guint connect_list_size;
Call this conn_list_len (to make it shorter and to match what the
function you get this value from is called)
> + if (!adapter_has_discov_sessions(adapter) && !connect_list_size)
> return;
Again since conn_list_len is not a boolean I'd rather have a clear == 0.
> - DBG("hci%u restarting discovery, disc_sessions %u", adapter->dev_id,
> - g_slist_length(adapter->disc_sessions));
> + DBG("hci%u restarting discovery: disc_sessions %u, connect_list size "
> + "%u", adapter->dev_id, g_slist_length(adapter->disc_sessions),
Splitting strings like this is something that we really should try to
avoid. If you do the variable rename as I suggested you can get this on
the same line.
> +static gboolean clean_connecting_state(GIOChannel *io, GIOCondition cond, gpointer user_data)
Looks like a too long line to me.
> + if (adapter->waiting_to_connect == 0 &&
> + g_slist_length(adapter->connect_list))
> + mgmt_start_discovery(adapter->dev_id);
Again > 0 here (which is especially strange that you didn't do it from
the start as in the first test you do use == 0).
> + btd_device_unref(device);
> + return FALSE;
> +}
Empty line before the return statement please.
> + btd_device_unref(device);
> return FALSE;
> }
Same here.
As a general note (not relating specifically to this patch) I'd like to
see a clearer split and naming of LE/GATT-specific functionality and
variables (function names, struct btd_adapter/device member naming &
grouping, etc.). Right now these seem to be sprinkled all over the place
which can be confusing for someone looking at the code from a
BR/EDR-only or LE-only perspective.
Johan
If there is a disconnected bonded device there will be a scanning
procedure active due to the General Connection Establishment Procedure.
This scan have to be suspended before trying to connect to the remote
device for pairing.
---
src/device.c | 150 ++++++++++++++++++++++++++++++-----------------------------
1 file changed, 77 insertions(+), 73 deletions(-)
diff --git a/src/device.c b/src/device.c
index 04ed96f..726f95b 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1675,8 +1675,8 @@ send_reply:
else if (dbus_message_is_method_call(req->msg, ADAPTER_INTERFACE,
"CreateDevice")) {
if (err < 0) {
- DBusMessage *reply;
- reply = btd_error_failed(req->msg, strerror(-err));
+ DBusMessage *reply = btd_error_failed(req->msg,
+ strerror(-err));
g_dbus_send_message(req->conn, reply);
goto cleanup;
}
@@ -1888,6 +1888,38 @@ done:
browse_request_free(req);
}
+static void bonding_request_free(struct bonding_req *bonding)
+{
+ struct btd_device *device;
+
+ if (!bonding)
+ return;
+
+ if (bonding->listener_id)
+ g_dbus_remove_watch(bonding->conn, bonding->listener_id);
+
+ if (bonding->msg)
+ dbus_message_unref(bonding->msg);
+
+ if (bonding->conn)
+ dbus_connection_unref(bonding->conn);
+
+ device = bonding->device;
+ g_free(bonding);
+
+ if (!device)
+ return;
+
+ device->bonding = NULL;
+
+ if (!device->agent)
+ return;
+
+ agent_cancel(device->agent);
+ agent_free(device->agent);
+ device->agent = NULL;
+}
+
static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
{
struct att_callbacks *attcb = user_data;
@@ -1917,6 +1949,21 @@ static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
if (attcb->success)
attcb->success(user_data);
+
+ if (device->bonding) {
+ /* this is a LE device during pairing */
+ int err = adapter_create_bonding(device->adapter,
+ &device->bdaddr, device->bdaddr_type,
+ agent_get_io_capability(device->agent));
+ if (err < 0) {
+ DBusMessage *reply = btd_error_failed(
+ device->bonding->msg, strerror(-err));
+ g_dbus_send_message(device->bonding->conn, reply);
+ bonding_request_cancel(device->bonding);
+ bonding_request_free(device->bonding);
+ }
+ }
+
done:
g_free(attcb);
}
@@ -1968,16 +2015,30 @@ GIOChannel *device_att_connect(gpointer user_data)
attcb->user_data = device;
if (device_is_bredr(device)) {
- io = bt_io_connect(att_connect_cb,
- attcb, NULL, &gerr,
+ io = bt_io_connect(att_connect_cb, attcb, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, &sba,
BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
BT_IO_OPT_PSM, ATT_PSM,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_INVALID);
+ } else if (device->bonding) {
+ /* this is a LE device during pairing, using low sec level */
+ io = bt_io_connect(att_connect_cb, attcb, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &sba,
+ BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+ BT_IO_OPT_DEST_TYPE, device->bdaddr_type,
+ BT_IO_OPT_CID, ATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (io == NULL) {
+ DBusMessage *reply = btd_error_failed(
+ device->bonding->msg, gerr->message);
+ g_dbus_send_message(device->bonding->conn, reply);
+ bonding_request_cancel(device->bonding);
+ bonding_request_free(device->bonding);
+ }
} else {
- io = bt_io_connect(att_connect_cb,
- attcb, NULL, &gerr,
+ io = bt_io_connect(att_connect_cb, attcb, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, &sba,
BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
BT_IO_OPT_DEST_TYPE, device->bdaddr_type,
@@ -2303,38 +2364,6 @@ static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status)
}
}
-static void bonding_request_free(struct bonding_req *bonding)
-{
- struct btd_device *device;
-
- if (!bonding)
- return;
-
- if (bonding->listener_id)
- g_dbus_remove_watch(bonding->conn, bonding->listener_id);
-
- if (bonding->msg)
- dbus_message_unref(bonding->msg);
-
- if (bonding->conn)
- dbus_connection_unref(bonding->conn);
-
- device = bonding->device;
- g_free(bonding);
-
- if (!device)
- return;
-
- device->bonding = NULL;
-
- if (!device->agent)
- return;
-
- agent_cancel(device->agent);
- agent_free(device->agent);
- device->agent = NULL;
-}
-
void device_set_paired(struct btd_device *device, gboolean value)
{
DBusConnection *conn = get_dbus_connection();
@@ -2428,41 +2457,6 @@ DBusMessage *device_create_bonding(struct btd_device *device,
if (device_is_bonded(device))
return btd_error_already_exists(msg);
- if (device_is_le(device)) {
- struct att_callbacks *attcb;
- GError *gerr = NULL;
- bdaddr_t sba;
-
- adapter_get_address(adapter, &sba);
-
- attcb = g_new0(struct att_callbacks, 1);
- attcb->user_data = device;
-
- device->att_io = bt_io_connect(att_connect_cb,
- attcb, NULL, &gerr,
- BT_IO_OPT_SOURCE_BDADDR, &sba,
- BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
- BT_IO_OPT_DEST_TYPE, device->bdaddr_type,
- BT_IO_OPT_CID, ATT_CID,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
- BT_IO_OPT_INVALID);
-
- if (device->att_io == NULL) {
- DBusMessage *reply = btd_error_failed(msg,
- gerr->message);
-
- error("Bonding bt_io_connect(): %s", gerr->message);
- g_error_free(gerr);
- g_free(attcb);
- return reply;
- }
- }
-
- err = adapter_create_bonding(adapter, &device->bdaddr,
- device->bdaddr_type, capability);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
-
bonding = bonding_request_new(conn, msg, device, agent_path,
capability);
@@ -2474,6 +2468,16 @@ DBusMessage *device_create_bonding(struct btd_device *device,
device->bonding = bonding;
bonding->device = device;
+ if (device_is_le(device)) {
+ adapter_connect_list_add(adapter, device);
+ return NULL;
+ }
+
+ err = adapter_create_bonding(adapter, &device->bdaddr,
+ device->bdaddr_type, capability);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
return NULL;
}
--
1.7.11.4
---
src/mgmt.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/mgmt.c b/src/mgmt.c
index db1eaed..bba5a89 100644
--- a/src/mgmt.c
+++ b/src/mgmt.c
@@ -2371,7 +2371,8 @@ int mgmt_create_bonding(int index, bdaddr_t *bdaddr, uint8_t addr_type, uint8_t
char addr[18];
ba2str(bdaddr, addr);
- DBG("hci%d bdaddr %s io_cap 0x%02x", index, addr, io_cap);
+ DBG("hci%d bdaddr %s type %d io_cap 0x%02x",
+ index, addr, addr_type, io_cap);
memset(buf, 0, sizeof(buf));
hdr->opcode = htobs(MGMT_OP_PAIR_DEVICE);
--
1.7.11.4
From: Claudio Takahasi <[email protected]>
This patch keeps scanning and re-connections active if the disconnection
reason is ECONNRESET(Remote Initiated Disconnection).
Re-connection is a behaviour determined by Profiles or by the upper
layer(user actions). For instance, HoG requires re-connection always
active, no matter if the previous disconnection reason was page timeout
or remote initiated disconnection (ECONNRESET). Some devices disconnects
after some idle time, connectable advertises are sent by the peripheral
when commanded by the user(eg: key pressed). Disconnection can be also
triggered by the local host (ECONNABORTED) using command line tools or
Disconnect method in the Device interface.
The peripheral dictates the re-connection controlling the connectable
advertises, BlueZ(central) needs to keep the scanning always active to
able to detect the advertises and trigger the connection.
---
src/device.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/device.c b/src/device.c
index e46ea2b..04ed96f 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1818,10 +1818,18 @@ static gboolean attrib_disconnected_cb(GIOChannel *io, GIOCondition cond,
g_slist_foreach(device->attios, attio_disconnected, NULL);
- if (device->auto_connect == FALSE || err != ETIMEDOUT)
+ if (device->auto_connect == FALSE) {
+ DBG("Automatic connection disabled");
goto done;
+ }
- adapter_connect_list_add(device_get_adapter(device), device);
+ /*
+ * Keep scanning/re-connection active if disconnection reason
+ * is page timeout, remote user terminated connection or local
+ * initiated disconnection.
+ */
+ if (err == ETIMEDOUT || err == ECONNRESET || err == ECONNABORTED)
+ adapter_connect_list_add(device_get_adapter(device), device);
done:
attio_cleanup(device);
--
1.7.11.4
From: Paulo Alcantara <[email protected]>
BlueZ host disconnects the link when encryption fails. ECONNABORTED
error is returned by the kernel when the connection is terminated by the
local host. This scenario commonly happens when authentication fails due
PIN or Key Missing.
---
src/device.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/device.c b/src/device.c
index 96afc37..e46ea2b 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1918,6 +1918,9 @@ static void att_error_cb(const GError *gerr, gpointer user_data)
struct att_callbacks *attcb = user_data;
struct btd_device *device = attcb->user_data;
+ if (g_error_matches(gerr, BT_IO_ERROR, ECONNABORTED))
+ return;
+
if (device->auto_connect == FALSE)
return;
--
1.7.11.4
From: Claudio Takahasi <[email protected]>
This patch manages BR/EDR inquiry and BLE scanning discovery sessions.
A scanning session is added in the discovery session list when there is
a bonded device which requires re-connection.
bluetoothd decides if interleaved or scanning needs to be executed based
on the queued discovery sessions. Interleaved discovery has higher
priority, scanning only is executed when there is only a scanning
session active.
---
src/adapter.c | 67 +++++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 47 insertions(+), 20 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index 62f92c6..8ee6a50 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -133,6 +133,7 @@ struct btd_adapter {
GSList *devices; /* Devices structure pointers */
GSList *mode_sessions; /* Request Mode sessions */
GSList *disc_sessions; /* Discovery sessions */
+ struct session_req *scanning_session;
GSList *connect_list; /* Devices to connect when found */
guint discov_id; /* Discovery timer */
gboolean discovering; /* Discovery active */
@@ -222,18 +223,19 @@ static struct session_req *create_session(struct btd_adapter *adapter,
DBusConnection *conn, DBusMessage *msg,
uint8_t mode, GDBusWatchFunction cb)
{
- const char *sender = dbus_message_get_sender(msg);
+ const char *sender;
struct session_req *req;
req = g_new0(struct session_req, 1);
req->adapter = adapter;
- req->conn = dbus_connection_ref(conn);
- req->msg = dbus_message_ref(msg);
req->mode = mode;
- if (cb == NULL)
+ if (conn == NULL || cb == NULL || msg == NULL)
return session_ref(req);
+ req->conn = dbus_connection_ref(conn);
+ req->msg = dbus_message_ref(msg);
+ sender = dbus_message_get_sender(msg);
req->owner = g_strdup(sender);
req->id = g_dbus_add_disconnect_watch(conn, sender, cb, req, NULL);
@@ -445,7 +447,9 @@ static struct session_req *find_session(GSList *list, const char *sender)
for (; list; list = list->next) {
struct session_req *req = list->data;
- if (g_str_equal(req->owner, sender))
+ /* req->owner may be NULL if the session has been added by the
+ * daemon itself, so we use g_strcmp0 instead of g_str_equal */
+ if (g_strcmp0(req->owner, sender) == 0)
return req;
}
@@ -520,7 +524,7 @@ static void session_remove(struct session_req *req)
struct btd_adapter *adapter = req->adapter;
/* Ignore set_mode session */
- if (req->owner == NULL)
+ if (req->owner == NULL && adapter->pending_mode)
return;
DBG("%s session %p with %s deactivated",
@@ -1009,7 +1013,12 @@ static gboolean discovery_cb(gpointer user_data)
struct btd_adapter *adapter = user_data;
adapter->discov_id = 0;
- mgmt_start_discovery(adapter->dev_id);
+
+ if (adapter->scanning_session &&
+ (g_slist_length(adapter->disc_sessions) == 1))
+ mgmt_start_scanning(adapter->dev_id);
+ else
+ mgmt_start_discovery(adapter->dev_id);
return FALSE;
}
@@ -2204,6 +2213,8 @@ const char *btd_adapter_get_name(struct btd_adapter *adapter)
void adapter_connect_list_add(struct btd_adapter *adapter,
struct btd_device *device)
{
+ struct session_req *req;
+
if (g_slist_find(adapter->connect_list, device)) {
DBG("ignoring already added device %s",
device_get_path(device));
@@ -2215,10 +2226,21 @@ void adapter_connect_list_add(struct btd_adapter *adapter,
DBG("%s added to %s's connect_list", device_get_path(device),
adapter->name);
- if (adapter->disc_sessions)
+ if (!adapter->up)
+ return;
+
+ if (adapter->off_requested)
+ return;
+
+ if (adapter->scanning_session)
return;
- mgmt_start_scanning(adapter->dev_id);
+ if (adapter->disc_sessions == NULL)
+ adapter->discov_id = g_idle_add(discovery_cb, adapter);
+
+ req = create_session(adapter, NULL, NULL, 0, NULL);
+ adapter->disc_sessions = g_slist_append(adapter->disc_sessions, req);
+ adapter->scanning_session = req;
}
void adapter_connect_list_remove(struct btd_adapter *adapter,
@@ -2238,6 +2260,7 @@ void adapter_connect_list_remove(struct btd_adapter *adapter,
void btd_adapter_start(struct btd_adapter *adapter)
{
+ struct session_req *req;
char address[18];
gboolean powered;
@@ -2264,8 +2287,15 @@ void btd_adapter_start(struct btd_adapter *adapter)
info("Adapter %s has been enabled", adapter->path);
- if (g_slist_length(adapter->connect_list))
- mgmt_start_scanning(adapter->dev_id);
+ if (g_slist_length(adapter->connect_list) == 0 ||
+ adapter->disc_sessions)
+ return;
+
+ req = create_session(adapter, NULL, NULL, 0, NULL);
+ adapter->disc_sessions = g_slist_append(adapter->disc_sessions, req);
+ adapter->scanning_session = req;
+
+ adapter->discov_id = g_idle_add(discovery_cb, adapter);
}
static void reply_pending_requests(struct btd_adapter *adapter)
@@ -2609,6 +2639,11 @@ void adapter_set_discovering(struct btd_adapter *adapter,
connect_list_size = g_slist_length(adapter->connect_list);
+ if (connect_list_size == 0 && adapter->scanning_session) {
+ session_unref(adapter->scanning_session);
+ adapter->scanning_session = NULL;
+ }
+
if (adapter_has_discov_sessions(adapter)) {
adapter->discov_id = g_idle_add(discovery_cb, adapter);
@@ -2617,14 +2652,6 @@ void adapter_set_discovering(struct btd_adapter *adapter,
g_slist_length(adapter->disc_sessions));
return;
}
-
- if (connect_list_size) {
- mgmt_start_scanning(adapter->dev_id);
-
- DBG("hci%u restarting scanning connect_list size %u",
- adapter->dev_id, connect_list_size);
- return;
- }
}
static void suspend_discovery(struct btd_adapter *adapter)
@@ -2939,7 +2966,7 @@ static gboolean clean_connecting_state(GIOChannel *io, GIOCondition cond, gpoint
if (adapter->waiting_to_connect == 0 &&
g_slist_length(adapter->connect_list))
- mgmt_start_scanning(adapter->dev_id);
+ adapter->discov_id = g_idle_add(discovery_cb, adapter);
btd_device_unref(device);
return FALSE;
--
1.7.11.4
From: Claudio Takahasi <[email protected]>
This patch enables the LE scanning when a device requires connection and
there isn't discovery sessions, triggering the General Connection
Establishment Procedure.
---
src/adapter.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/adapter.c b/src/adapter.c
index 2bba1ac..62f92c6 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -2214,6 +2214,11 @@ void adapter_connect_list_add(struct btd_adapter *adapter,
btd_device_ref(device));
DBG("%s added to %s's connect_list", device_get_path(device),
adapter->name);
+
+ if (adapter->disc_sessions)
+ return;
+
+ mgmt_start_scanning(adapter->dev_id);
}
void adapter_connect_list_remove(struct btd_adapter *adapter,
--
1.7.11.4
From: Claudio Takahasi <[email protected]>
This patches replaces the interleaved discovery by LE scanning when LE
re-connection is required.
---
src/adapter.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index 9dbd306..2bba1ac 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -2260,7 +2260,7 @@ void btd_adapter_start(struct btd_adapter *adapter)
info("Adapter %s has been enabled", adapter->path);
if (g_slist_length(adapter->connect_list))
- mgmt_start_discovery(adapter->dev_id);
+ mgmt_start_scanning(adapter->dev_id);
}
static void reply_pending_requests(struct btd_adapter *adapter)
@@ -2604,14 +2604,22 @@ void adapter_set_discovering(struct btd_adapter *adapter,
connect_list_size = g_slist_length(adapter->connect_list);
- if (!adapter_has_discov_sessions(adapter) && !connect_list_size)
+ if (adapter_has_discov_sessions(adapter)) {
+ adapter->discov_id = g_idle_add(discovery_cb, adapter);
+
+ DBG("hci%u restarting discovery: disc_sessions %u",
+ adapter->dev_id,
+ g_slist_length(adapter->disc_sessions));
return;
+ }
- DBG("hci%u restarting discovery: disc_sessions %u, connect_list size "
- "%u", adapter->dev_id, g_slist_length(adapter->disc_sessions),
- connect_list_size);
+ if (connect_list_size) {
+ mgmt_start_scanning(adapter->dev_id);
- adapter->discov_id = g_idle_add(discovery_cb, adapter);
+ DBG("hci%u restarting scanning connect_list size %u",
+ adapter->dev_id, connect_list_size);
+ return;
+ }
}
static void suspend_discovery(struct btd_adapter *adapter)
@@ -2926,7 +2934,7 @@ static gboolean clean_connecting_state(GIOChannel *io, GIOCondition cond, gpoint
if (adapter->waiting_to_connect == 0 &&
g_slist_length(adapter->connect_list))
- mgmt_start_discovery(adapter->dev_id);
+ mgmt_start_scanning(adapter->dev_id);
btd_device_unref(device);
return FALSE;
--
1.7.11.4
From: Claudio Takahasi <[email protected]>
This patch adds a new callback to allow the adapter to control LE
scanning. The current approach uses the active scanning with default
windows and intervals defined by the core spec without any filtering.
---
src/mgmt.c | 34 ++++++++++++++++++++++++++++++++++
src/mgmt.h | 1 +
2 files changed, 35 insertions(+)
diff --git a/src/mgmt.c b/src/mgmt.c
index 3f83fdf..db1eaed 100644
--- a/src/mgmt.c
+++ b/src/mgmt.c
@@ -2043,6 +2043,40 @@ int mgmt_start_discovery(int index)
return 0;
}
+int mgmt_start_scanning(int index)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_start_discovery)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_start_discovery *cp = (void *) &buf[sizeof(*hdr)];
+ struct controller_info *info = &controllers[index];
+
+ DBG("index %d", index);
+
+ if (!mgmt_low_energy(info->current_settings)) {
+ error("scanning failed: Low Energy not enabled/supported");
+ return -ENOTSUP;
+ }
+
+ info->discov_type = 0;
+ hci_set_bit(BDADDR_LE_PUBLIC, &info->discov_type);
+ hci_set_bit(BDADDR_LE_RANDOM, &info->discov_type);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_START_DISCOVERY);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ cp->type = info->discov_type;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0) {
+ int err = -errno;
+ error("failed to write to MGMT socket: %s", strerror(-err));
+ return err;
+ }
+
+ return 0;
+}
+
int mgmt_stop_discovery(int index)
{
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_start_discovery)];
diff --git a/src/mgmt.h b/src/mgmt.h
index 95245d2..eb7434a 100644
--- a/src/mgmt.h
+++ b/src/mgmt.h
@@ -33,6 +33,7 @@ int mgmt_set_dev_class(int index, uint8_t major, uint8_t minor);
int mgmt_set_fast_connectable(int index, gboolean enable);
int mgmt_start_discovery(int index);
+int mgmt_start_scanning(int index);
int mgmt_stop_discovery(int index);
int mgmt_read_clock(int index, bdaddr_t *bdaddr, int which, int timeout,
--
1.7.11.4
Since controllers don't support more than one ongoing connection
procedure at the same time, new connection attempts needs to yield if
there is an ongoing connection procedure already.
---
src/adapter.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++--------
src/device.c | 6 +++---
src/device.h | 2 +-
3 files changed, 57 insertions(+), 12 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index e3994a8..9dbd306 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -136,6 +136,8 @@ struct btd_adapter {
GSList *connect_list; /* Devices to connect when found */
guint discov_id; /* Discovery timer */
gboolean discovering; /* Discovery active */
+ gboolean connecting; /* Connect active */
+ guint waiting_to_connect; /* # of devices waiting to connect */
gboolean discov_suspended; /* Discovery suspended */
guint auto_timeout_id; /* Automatic connections timeout */
sdp_list_t *services; /* Services associated to adapter */
@@ -2256,6 +2258,9 @@ void btd_adapter_start(struct btd_adapter *adapter)
call_adapter_powered_callbacks(adapter, TRUE);
info("Adapter %s has been enabled", adapter->path);
+
+ if (g_slist_length(adapter->connect_list))
+ mgmt_start_discovery(adapter->dev_id);
}
static void reply_pending_requests(struct btd_adapter *adapter)
@@ -2579,6 +2584,7 @@ void adapter_set_discovering(struct btd_adapter *adapter,
gboolean discovering)
{
const char *path = adapter->path;
+ guint connect_list_size;
adapter->discovering = discovering;
@@ -2593,11 +2599,17 @@ void adapter_set_discovering(struct btd_adapter *adapter,
g_slist_free_full(adapter->oor_devices, dev_info_free);
adapter->oor_devices = g_slist_copy(adapter->found_devices);
- if (!adapter_has_discov_sessions(adapter) || adapter->discov_suspended)
+ if (adapter->discov_suspended)
+ return;
+
+ connect_list_size = g_slist_length(adapter->connect_list);
+
+ if (!adapter_has_discov_sessions(adapter) && !connect_list_size)
return;
- DBG("hci%u restarting discovery, disc_sessions %u", adapter->dev_id,
- g_slist_length(adapter->disc_sessions));
+ DBG("hci%u restarting discovery: disc_sessions %u, connect_list size "
+ "%u", adapter->dev_id, g_slist_length(adapter->disc_sessions),
+ connect_list_size);
adapter->discov_id = g_idle_add(discovery_cb, adapter);
}
@@ -2905,18 +2917,44 @@ static char *read_stored_data(bdaddr_t *local, bdaddr_t *peer,
return textfile_get(filename, key);
}
+static gboolean clean_connecting_state(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+ struct btd_device *device = user_data;
+ struct btd_adapter *adapter = device_get_adapter(device);
+
+ adapter->connecting = FALSE;
+
+ if (adapter->waiting_to_connect == 0 &&
+ g_slist_length(adapter->connect_list))
+ mgmt_start_discovery(adapter->dev_id);
+
+ btd_device_unref(device);
+ return FALSE;
+}
+
static gboolean connect_pending_cb(gpointer user_data)
{
struct btd_device *device = user_data;
struct btd_adapter *adapter = device_get_adapter(device);
+ GIOChannel *io;
/* in the future we may want to check here if the controller supports
* scanning and connecting at the same time */
if (adapter->discovering)
return TRUE;
- device_att_connect(device);
+ if (adapter->connecting)
+ return TRUE;
+
+ adapter->connecting = TRUE;
+ adapter->waiting_to_connect--;
+ io = device_att_connect(device);
+ g_io_add_watch(io, G_IO_OUT | G_IO_ERR, clean_connecting_state,
+ btd_device_ref(device));
+ g_io_channel_unref(io);
+
+ btd_device_unref(device);
return FALSE;
}
@@ -3008,12 +3046,19 @@ void adapter_update_found_devices(struct btd_adapter *adapter,
if (bdaddr_type == BDADDR_LE_PUBLIC ||
bdaddr_type == BDADDR_LE_RANDOM) {
+ struct btd_device *device;
+
l = g_slist_find_custom(adapter->connect_list, bdaddr,
(GCompareFunc) device_bdaddr_cmp);
- if (l) {
- g_idle_add(connect_pending_cb, l->data);
- stop_discovery(adapter);
- }
+ if (!l)
+ goto done;
+
+ device = l->data;
+ adapter_connect_list_remove(adapter, device);
+
+ g_idle_add(connect_pending_cb, btd_device_ref(device));
+ stop_discovery(adapter);
+ adapter->waiting_to_connect++;
}
done:
diff --git a/src/device.c b/src/device.c
index c69b578..96afc37 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1936,7 +1936,7 @@ static void att_success_cb(gpointer user_data)
g_slist_foreach(device->attios, attio_connected, device->attrib);
}
-gboolean device_att_connect(gpointer user_data)
+GIOChannel *device_att_connect(gpointer user_data)
{
struct btd_device *device = user_data;
struct btd_adapter *adapter = device->adapter;
@@ -1979,12 +1979,12 @@ gboolean device_att_connect(gpointer user_data)
error("ATT bt_io_connect(%s): %s", addr, gerr->message);
g_error_free(gerr);
g_free(attcb);
- return FALSE;
+ return NULL;
}
device->att_io = io;
- return FALSE;
+ return g_io_channel_ref(io);
}
static void att_browse_error_cb(const GError *gerr, gpointer user_data)
diff --git a/src/device.h b/src/device.h
index ab70f90..1b5be4e 100644
--- a/src/device.h
+++ b/src/device.h
@@ -158,4 +158,4 @@ int device_unblock(DBusConnection *conn, struct btd_device *device,
void device_set_pnpid(struct btd_device *device, uint8_t vendor_id_src,
uint16_t vendor_id, uint16_t product_id,
uint16_t product_ver);
-gboolean device_att_connect(gpointer user_data);
+GIOChannel *device_att_connect(gpointer user_data);
--
1.7.11.4
When a connection is needed for a LE device it is added to the adapter
connect list instead of directly connecting the ATT io channel.
---
src/adapter.c | 2 +-
src/device.c | 45 +++++++++++----------------------------------
src/device.h | 1 +
3 files changed, 13 insertions(+), 35 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index 6b8435e..e3994a8 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -2915,7 +2915,7 @@ static gboolean connect_pending_cb(gpointer user_data)
if (adapter->discovering)
return TRUE;
- /* TODO: call device connect callback */
+ device_att_connect(device);
return FALSE;
}
diff --git a/src/device.c b/src/device.c
index 02ef35e..c69b578 100644
--- a/src/device.c
+++ b/src/device.c
@@ -68,8 +68,6 @@
#define DISCONNECT_TIMER 2
#define DISCOVERY_TIMER 2
-#define AUTO_CONNECTION_INTERVAL 5 /* Next connection attempt */
-
struct btd_disconnect_data {
guint id;
disconnect_watch watch;
@@ -1804,15 +1802,6 @@ static void attio_disconnected(gpointer data, gpointer user_data)
attio->dcfunc(attio->user_data);
}
-static void att_connect_dispatched(gpointer user_data)
-{
- struct btd_device *device = user_data;
-
- device->auto_id = 0;
-}
-
-static gboolean att_connect(gpointer user_data);
-
static gboolean attrib_disconnected_cb(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
@@ -1832,10 +1821,7 @@ static gboolean attrib_disconnected_cb(GIOChannel *io, GIOCondition cond,
if (device->auto_connect == FALSE || err != ETIMEDOUT)
goto done;
- device->auto_id = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT_IDLE,
- AUTO_CONNECTION_INTERVAL,
- att_connect, device,
- att_connect_dispatched);
+ adapter_connect_list_add(device_get_adapter(device), device);
done:
attio_cleanup(device);
@@ -1935,11 +1921,7 @@ static void att_error_cb(const GError *gerr, gpointer user_data)
if (device->auto_connect == FALSE)
return;
- device->auto_id = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT_IDLE,
- AUTO_CONNECTION_INTERVAL,
- att_connect, device,
- att_connect_dispatched);
-
+ adapter_connect_list_add(device_get_adapter(device), device);
DBG("Enabling automatic connections");
}
@@ -1954,7 +1936,7 @@ static void att_success_cb(gpointer user_data)
g_slist_foreach(device->attios, attio_connected, device->attrib);
}
-static gboolean att_connect(gpointer user_data)
+gboolean device_att_connect(gpointer user_data)
{
struct btd_device *device = user_data;
struct btd_adapter *adapter = device->adapter;
@@ -2216,6 +2198,9 @@ void device_set_temporary(struct btd_device *device, gboolean temporary)
DBG("temporary %d", temporary);
+ if (temporary)
+ adapter_connect_list_remove(device_get_adapter(device), device);
+
device->temporary = temporary;
}
@@ -2231,6 +2216,7 @@ void device_set_bonded(struct btd_device *device, gboolean bonded)
void device_set_auto_connect(struct btd_device *device, gboolean enable)
{
+ struct btd_adapter *adapter = device_get_adapter(device);
char addr[18];
if (!device)
@@ -2244,15 +2230,10 @@ void device_set_auto_connect(struct btd_device *device, gboolean enable)
/* Disabling auto connect */
if (enable == FALSE) {
- if (device->auto_id)
- g_source_remove(device->auto_id);
+ adapter_connect_list_remove(adapter, device);
return;
}
- /* Enabling auto connect */
- if (device->auto_id != 0)
- return;
-
if (device->attrib) {
DBG("Already connected");
return;
@@ -2261,9 +2242,8 @@ void device_set_auto_connect(struct btd_device *device, gboolean enable)
if (device->attios == NULL && device->attios_offline == NULL)
return;
- device->auto_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
- att_connect, device,
- att_connect_dispatched);
+ /* Enabling auto connect */
+ adapter_connect_list_add(adapter, device);
}
static gboolean start_discovery(gpointer user_data)
@@ -3164,10 +3144,7 @@ guint btd_device_add_attio_callback(struct btd_device *device,
device->attios = g_slist_append(device->attios, attio);
- if (device->auto_id == 0)
- device->auto_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
- att_connect, device,
- att_connect_dispatched);
+ adapter_connect_list_add(device_get_adapter(device), device);
return attio->id;
}
diff --git a/src/device.h b/src/device.h
index f1d95c6..ab70f90 100644
--- a/src/device.h
+++ b/src/device.h
@@ -158,3 +158,4 @@ int device_unblock(DBusConnection *conn, struct btd_device *device,
void device_set_pnpid(struct btd_device *device, uint8_t vendor_id_src,
uint16_t vendor_id, uint16_t product_id,
uint16_t product_ver);
+gboolean device_att_connect(gpointer user_data);
--
1.7.11.4