* [RFCv1 13/20] Bluetooth: Handle physical link completion
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
Several different actions may be taken when an AMP physical link
becomes available. A channel being created on an AMP controller must
continue the connection process. A channel being moved needs to
either send a move request or a move response. A failed physical link
will revert to using a BR/EDR controller if possible.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
net/bluetooth/l2cap_core.c | 166 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 166 insertions(+)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 0d57ab3..9d7fab0 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -977,6 +977,19 @@ static void l2cap_send_conn_req(struct l2cap_chan *chan)
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
}
+static void l2cap_send_create_chan_req(struct l2cap_chan *chan, u8 amp_id)
+{
+ struct l2cap_create_chan_req req;
+ req.scid = cpu_to_le16(chan->scid);
+ req.psm = chan->psm;
+ req.amp_id = amp_id;
+
+ chan->ident = l2cap_get_ident(chan->conn);
+
+ l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_REQ,
+ sizeof(req), &req);
+}
+
static void l2cap_move_setup(struct l2cap_chan *chan)
{
struct sk_buff *skb;
@@ -4128,6 +4141,23 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
return 0;
}
+static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id)
+{
+ struct l2cap_move_chan_req req;
+ u8 ident;
+
+ BT_DBG("chan %p, dest_amp_id %d", chan, dest_amp_id);
+
+ ident = l2cap_get_ident(chan->conn);
+ chan->ident = ident;
+
+ req.icid = cpu_to_le16(chan->scid);
+ req.dest_amp_id = dest_amp_id;
+
+ l2cap_send_cmd(chan->conn, ident, L2CAP_MOVE_CHAN_REQ, sizeof(req),
+ &req);
+}
+
static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident,
u16 icid, u16 result)
{
@@ -4290,6 +4320,142 @@ static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
}
}
+static void l2cap_do_create(struct l2cap_chan *chan, int result,
+ u8 local_amp_id, u8 remote_amp_id)
+{
+ if (!test_bit(CONF_CONNECT_PEND, &chan->conf_state)) {
+ struct l2cap_conn_rsp rsp;
+ char buf[128];
+ rsp.scid = cpu_to_le16(chan->dcid);
+ rsp.dcid = cpu_to_le16(chan->scid);
+
+ /* Incoming channel on AMP */
+ if (result == L2CAP_CR_SUCCESS) {
+ /* Send successful response */
+ rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
+ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
+ } else {
+ /* Send negative response */
+ rsp.result = cpu_to_le16(L2CAP_CR_NO_MEM);
+ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
+ }
+
+ l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_RSP,
+ sizeof(rsp), &rsp);
+
+ if (result == L2CAP_CR_SUCCESS) {
+ __l2cap_state_change(chan, BT_CONFIG);
+ set_bit(CONF_REQ_SENT, &chan->conf_state);
+ l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn),
+ L2CAP_CONF_REQ,
+ l2cap_build_conf_req(chan, buf), buf);
+ chan->num_conf_req++;
+ }
+ } else {
+ /* Outgoing channel on AMP */
+ if (result == L2CAP_CR_SUCCESS) {
+ chan->local_amp_id = local_amp_id;
+ l2cap_send_create_chan_req(chan, remote_amp_id);
+ } else {
+ /* Revert to BR/EDR connect */
+ l2cap_send_conn_req(chan);
+ }
+ }
+}
+
+static void l2cap_do_move_initiate(struct l2cap_chan *chan, u8 local_amp_id,
+ u8 remote_amp_id)
+{
+ l2cap_move_setup(chan);
+ chan->move_id = local_amp_id;
+ chan->move_state = L2CAP_MOVE_WAIT_RSP;
+
+ l2cap_send_move_chan_req(chan, remote_amp_id);
+ __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+}
+
+static void l2cap_do_move_respond(struct l2cap_chan *chan, int result)
+{
+ struct hci_chan *hchan = NULL;
+
+ /* Placeholder - get hci_chan for logical link */
+
+ if (hchan) {
+ if (hchan->state == BT_CONNECTED) {
+ /* Logical link is ready to go */
+ chan->hs_hcon = hchan->conn;
+ chan->hs_hcon->l2cap_data = chan->conn;
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+ l2cap_send_move_chan_rsp(chan->conn, chan->ident,
+ chan->dcid, L2CAP_MR_SUCCESS);
+
+ l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS);
+ } else {
+ /* Wait for logical link to be ready */
+ chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
+ }
+ } else {
+ /* Logical link not available */
+ l2cap_send_move_chan_rsp(chan->conn, chan->ident, chan->dcid,
+ L2CAP_MR_NOT_ALLOWED);
+ }
+}
+
+static void l2cap_do_move_cancel(struct l2cap_chan *chan, int result)
+{
+ if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) {
+ u8 rsp_result;
+ if (result == -EINVAL)
+ rsp_result = L2CAP_MR_BAD_ID;
+ else
+ rsp_result = L2CAP_MR_NOT_ALLOWED;
+
+ l2cap_send_move_chan_rsp(chan->conn, chan->ident, chan->dcid,
+ rsp_result);
+ }
+
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+ chan->move_state = L2CAP_MOVE_STABLE;
+
+ /* Restart data transmission */
+ l2cap_ertm_send(chan);
+}
+
+void l2cap_physical_cfm(struct l2cap_chan *chan, int result, u8 local_amp_id,
+ u8 remote_amp_id)
+{
+ BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d",
+ chan, result, local_amp_id, remote_amp_id);
+
+ l2cap_chan_lock(chan);
+
+ if (chan->state == BT_DISCONN || chan->state == BT_CLOSED) {
+ l2cap_chan_unlock(chan);
+ return;
+ }
+
+ if (chan->state != BT_CONNECTED) {
+ l2cap_do_create(chan, result, local_amp_id, remote_amp_id);
+ } else if (result != L2CAP_MR_SUCCESS) {
+ l2cap_do_move_cancel(chan, result);
+ } else {
+ switch (chan->move_role) {
+ case L2CAP_MOVE_ROLE_INITIATOR:
+ l2cap_do_move_initiate(chan, local_amp_id,
+ remote_amp_id);
+ break;
+ case L2CAP_MOVE_ROLE_RESPONDER:
+ l2cap_do_move_respond(chan, result);
+ break;
+ default:
+ l2cap_do_move_cancel(chan, result);
+ break;
+ }
+ }
+
+ l2cap_chan_unlock(chan);
+}
+
static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u16 cmd_len, void *data)
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 12/20] Bluetooth: Add move confirm response handling
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
The move confirm response concludes the channel move command sequence.
Receipt of this command indicates that data may begin to flow again.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
net/bluetooth/l2cap_core.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 4c5191e..0d57ab3 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4581,6 +4581,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
u16 cmd_len, void *data)
{
struct l2cap_move_chan_cfm_rsp *rsp = data;
+ struct l2cap_chan *chan;
u16 icid;
if (cmd_len != sizeof(*rsp))
@@ -4590,6 +4591,31 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
BT_DBG("icid 0x%4.4x", icid);
+ chan = l2cap_get_chan_by_scid(conn, icid);
+ if (!chan)
+ return 0;
+
+ __clear_chan_timer(chan);
+
+ if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) {
+ chan->move_state = L2CAP_MOVE_STABLE;
+ chan->local_amp_id = chan->move_id;
+
+ if (!chan->local_amp_id && chan->hs_hchan) {
+ /* Have moved off of AMP, free the channel */
+ chan->hs_hchan = NULL;
+ chan->hs_hcon = NULL;
+
+ /* Placeholder - free the logical link */
+ }
+
+ l2cap_move_success(chan);
+
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+ }
+
+ l2cap_chan_unlock(chan);
+
return 0;
}
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 11/20] Bluetooth: Add logical link confirm
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
The logical link confirm callback is executed when the AMP controller
completes its logical link setup. During a channel move, a newly
formed logical link allows a move responder to send a move channel
response. A move initiator will send a move channel confirm. A
failed logical link will end the channel move and send an appropriate
response or confirm command indicating a failure.
If the channel is being created on an AMP controller, L2CAP
configuration is started after the logical link is set up.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
net/bluetooth/l2cap_core.c | 117 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 115 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 37f3bf8..4c5191e 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3725,6 +3725,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
goto unlock;
}
+ chan->ident = cmd->ident;
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
chan->num_conf_rsp++;
@@ -4170,11 +4171,123 @@ static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp);
}
+/* Call with chan locked */
static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
u8 status)
{
- /* Placeholder */
- return;
+ BT_DBG("chan %p, hchan %p, status %d", chan, hchan, status);
+
+ if (chan->state != BT_CONNECTED && !chan->local_amp_id)
+ return;
+
+ if (!status && chan->state != BT_CONNECTED) {
+ struct l2cap_conf_rsp rsp;
+ u8 code;
+
+ /* Create channel complete */
+ chan->hs_hcon = hchan->conn;
+ chan->hs_hcon->l2cap_data = chan->conn;
+
+ code = l2cap_build_conf_rsp(chan, &rsp,
+ L2CAP_CONF_SUCCESS, 0);
+ l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CONF_RSP, code,
+ &rsp);
+ set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
+
+ if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) {
+ int err = 0;
+
+ set_default_fcs(chan);
+
+ if (chan->mode == L2CAP_MODE_ERTM ||
+ chan->mode == L2CAP_MODE_STREAMING)
+ err = l2cap_ertm_init(chan);
+
+ if (err < 0)
+ l2cap_send_disconn_req(chan->conn, chan, -err);
+ else
+ l2cap_chan_ready(chan);
+ }
+ } else if (chan && !status) {
+ /* Channel move */
+ chan->hs_hcon = hchan->conn;
+ chan->hs_hcon->l2cap_data = chan->conn;
+
+ BT_DBG("move_state %d", chan->move_state);
+
+ if (chan->state != BT_CONNECTED)
+ return;
+
+ switch (chan->move_state) {
+ case L2CAP_MOVE_WAIT_LOGICAL_COMP:
+ /* Move confirm will be sent after a success
+ * response is received
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
+ break;
+ case L2CAP_MOVE_WAIT_LOGICAL_CFM:
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+ chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+ } else if (chan->move_role ==
+ L2CAP_MOVE_ROLE_INITIATOR) {
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP;
+ l2cap_send_move_chan_cfm(chan->conn, chan,
+ chan->scid,
+ L2CAP_MR_SUCCESS);
+ __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+ } else if (chan->move_role ==
+ L2CAP_MOVE_ROLE_RESPONDER) {
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+ l2cap_send_move_chan_rsp(chan->conn,
+ chan->ident,
+ chan->dcid,
+ L2CAP_MR_SUCCESS);
+ }
+ break;
+ default:
+ /* Move was not in expected state, free the channel */
+ chan->hs_hchan = NULL;
+ chan->hs_hcon = NULL;
+
+ /* Placeholder - free the logical link */
+
+ chan->move_state = L2CAP_MOVE_STABLE;
+ }
+ } else {
+ /* Logical link setup failed. */
+
+ if (chan->state != BT_CONNECTED)
+ l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+ else if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) {
+ l2cap_move_revert(chan);
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+ chan->move_state = L2CAP_MOVE_STABLE;
+ l2cap_send_move_chan_rsp(chan->conn, chan->ident,
+ chan->dcid, L2CAP_MR_NOT_SUPP);
+ } else if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) {
+ if (chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_COMP ||
+ chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_CFM) {
+ /* Remote has only sent pending or
+ * success responses, clean up
+ */
+ l2cap_move_revert(chan);
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+ chan->move_state = L2CAP_MOVE_STABLE;
+ }
+
+ /* Other amp move states imply that the move
+ * has already aborted
+ */
+ l2cap_send_move_chan_cfm(chan->conn, chan, chan->scid,
+ L2CAP_MC_UNCONFIRMED);
+ __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+ }
+
+ chan->hs_hchan = NULL;
+ chan->hs_hcon = NULL;
+
+ /* Placeholder - free logical link */
+ }
}
static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 10/20] Bluetooth: Move channel response
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
The move response command includes a result code indicationg
"pending", "success", or "failure" status. A pending result is
received when the remote address is still setting up a physical link,
and will be followed by success or failure. On success, logical link
setup will proceed. On failure, the move is stopped. The receiver of
a move channel response must always follow up by sending a move
channel confirm command.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
include/net/bluetooth/l2cap.h | 2 +
net/bluetooth/l2cap_core.c | 137 +++++++++++++++++++++++++++++++++++++++++-
2 files changed, 137 insertions(+), 2 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 6b6fb27..cfbde8f 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -51,6 +51,8 @@
#define L2CAP_ENC_TIMEOUT msecs_to_jiffies(5000)
#define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000)
#define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000)
+#define L2CAP_MOVE_TIMEOUT msecs_to_jiffies(4000)
+#define L2CAP_MOVE_ERTX_TIMEOUT msecs_to_jiffies(60000)
#define L2CAP_A2MP_DEFAULT_MTU 670
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index df761ab..37f3bf8 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4170,6 +4170,13 @@ static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp);
}
+static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
+ u8 status)
+{
+ /* Placeholder */
+ return;
+}
+
static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u16 cmd_len, void *data)
@@ -4266,6 +4273,7 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
u16 cmd_len, void *data)
{
struct l2cap_move_chan_rsp *rsp = data;
+ struct l2cap_chan *chan = NULL;
u16 icid, result;
if (cmd_len != sizeof(*rsp))
@@ -4276,8 +4284,133 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
- /* Placeholder: Always unconfirmed */
- l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED);
+ switch (result) {
+ case L2CAP_MR_SUCCESS:
+ case L2CAP_MR_PEND:
+ chan = l2cap_get_chan_by_scid(conn, icid);
+ if (!chan) {
+ l2cap_send_move_chan_cfm(conn, NULL, icid,
+ L2CAP_MC_UNCONFIRMED);
+ break;
+ }
+
+ __clear_chan_timer(chan);
+ if (result == L2CAP_MR_PEND)
+ __set_chan_timer(chan, L2CAP_MOVE_ERTX_TIMEOUT);
+
+ if (chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_COMP) {
+ /* Move confirm will be sent when logical link
+ * is complete.
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
+ } else if (chan->move_state == L2CAP_MOVE_WAIT_RSP_SUCCESS) {
+ if (result == L2CAP_MR_PEND) {
+ break;
+ } else if (test_bit(CONN_LOCAL_BUSY,
+ &chan->conn_state)) {
+ chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+ } else {
+ /* Logical link is up or moving to BR/EDR,
+ * proceed with move */
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP;
+ l2cap_send_move_chan_cfm(conn, chan, chan->scid,
+ L2CAP_MC_CONFIRMED);
+ __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+ }
+ } else if (chan->move_state == L2CAP_MOVE_WAIT_RSP) {
+ struct hci_chan *hchan = NULL;
+ /* Moving to AMP */
+ if (result == L2CAP_MR_SUCCESS) {
+ /* Remote is ready, send confirm immediately
+ * after logical link is ready
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
+ } else {
+ /* Both logical link and move success
+ * are required to confirm
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_COMP;
+ }
+
+ /* Placeholder - get hci_chan for logical link */
+ if (!hchan) {
+ /* Logical link not available */
+ l2cap_send_move_chan_cfm(conn, chan, chan->scid,
+ L2CAP_MC_UNCONFIRMED);
+ break;
+ }
+
+ /* If the logical link is not yet connected, do not
+ * send confirmation.
+ */
+ if (hchan->state != BT_CONNECTED)
+ break;
+
+ /* Logical link is already ready to go */
+
+ chan->hs_hcon = hchan->conn;
+ chan->hs_hcon->l2cap_data = chan->conn;
+
+ if (result == L2CAP_MR_SUCCESS) {
+ /* Can confirm now */
+ l2cap_send_move_chan_cfm(conn, chan, chan->scid,
+ L2CAP_MC_CONFIRMED);
+ } else {
+ /* Now only need move success
+ * to confirm
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
+ }
+
+ l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS);
+ } else {
+ /* Any other amp move state means the move failed. */
+ chan->move_id = chan->local_amp_id;
+ chan->move_state = L2CAP_MOVE_STABLE;
+ l2cap_move_revert(chan);
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+ l2cap_send_move_chan_cfm(conn, chan, chan->scid,
+ L2CAP_MC_UNCONFIRMED);
+ __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+ }
+ break;
+ default:
+ /* Failed (including collision case) */
+ mutex_lock(&conn->chan_lock);
+ chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
+ if (chan)
+ l2cap_chan_lock(chan);
+ mutex_unlock(&conn->chan_lock);
+
+ if (!chan) {
+ /* Could not locate channel, icid is best guess */
+ l2cap_send_move_chan_cfm(conn, NULL, icid,
+ L2CAP_MC_UNCONFIRMED);
+ break;
+ }
+
+ __clear_chan_timer(chan);
+
+ if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) {
+ if (result == L2CAP_MR_COLLISION) {
+ chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
+ } else {
+ /* Cleanup - cancel move */
+ chan->move_id = chan->local_amp_id;
+ chan->move_state = L2CAP_MOVE_STABLE;
+ l2cap_move_revert(chan);
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+ }
+ }
+
+ l2cap_send_move_chan_cfm(conn, chan, chan->scid,
+ L2CAP_MC_UNCONFIRMED);
+ __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+ break;
+ }
+
+ if (chan)
+ l2cap_chan_unlock(chan);
return 0;
}
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 09/20] Bluetooth: Add state to hci_chan
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
On an AMP controller, hci_chan maps to a logical link. When a channel
is being moved, the logical link may or may not be connected already.
The hci_chan->state is used to determine the existance of a useable
logical link so the link can be either used or requested.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_conn.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 6a3337e..ac645c3 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -341,6 +341,7 @@ struct hci_chan {
struct hci_conn *conn;
struct sk_buff_head data_q;
unsigned int sent;
+ __u8 state;
};
extern struct list_head hci_dev_list;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 59f0344..537a104 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -929,6 +929,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
chan->conn = conn;
skb_queue_head_init(&chan->data_q);
+ chan->state = BT_CONNECTED;
list_add_rcu(&chan->list, &conn->chan_list);
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 08/20] Bluetooth: Add move channel confirm handling
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
After sending a move channel response, a move responder waits for a
move channel confirm command. If the received command has a
"confirmed" result the move is proceeding, and "unconfirmed" means the
move has failed and the channel will not change controllers.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
net/bluetooth/l2cap_core.c | 70 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 67 insertions(+), 3 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 5bc14ca..df761ab 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1012,6 +1012,42 @@ static void l2cap_move_setup(struct l2cap_chan *chan)
set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
}
+static void l2cap_move_success(struct l2cap_chan *chan)
+{
+ BT_DBG("chan %p", chan);
+
+ if (chan->mode != L2CAP_MODE_ERTM)
+ return;
+
+ switch (chan->move_role) {
+ case L2CAP_MOVE_ROLE_INITIATOR:
+ l2cap_tx(chan, NULL, NULL, L2CAP_EV_EXPLICIT_POLL);
+ chan->rx_state = L2CAP_RX_STATE_WAIT_F;
+ break;
+ case L2CAP_MOVE_ROLE_RESPONDER:
+ chan->rx_state = L2CAP_RX_STATE_WAIT_P;
+ break;
+ }
+}
+
+static void l2cap_move_revert(struct l2cap_chan *chan)
+{
+ BT_DBG("chan %p", chan);
+
+ if (chan->mode != L2CAP_MODE_ERTM)
+ return;
+
+ switch (chan->move_role) {
+ case L2CAP_MOVE_ROLE_INITIATOR:
+ l2cap_tx(chan, NULL, NULL, L2CAP_EV_EXPLICIT_POLL);
+ chan->rx_state = L2CAP_RX_STATE_WAIT_F;
+ break;
+ case L2CAP_MOVE_ROLE_RESPONDER:
+ chan->rx_state = L2CAP_RX_STATE_WAIT_P;
+ break;
+ }
+}
+
static void l2cap_chan_ready(struct l2cap_chan *chan)
{
/* This clears all conf flags, including CONF_NOT_COMPLETE */
@@ -4246,11 +4282,12 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
return 0;
}
-static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd,
- u16 cmd_len, void *data)
+static int l2cap_move_channel_confirm(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd,
+ u16 cmd_len, void *data)
{
struct l2cap_move_chan_cfm *cfm = data;
+ struct l2cap_chan *chan;
u16 icid, result;
if (cmd_len != sizeof(*cfm))
@@ -4261,8 +4298,35 @@ static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn,
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
+ chan = l2cap_get_chan_by_dcid(conn, icid);
+ if (!chan)
+ goto send_move_confirm_response;
+
+ if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) {
+ chan->move_state = L2CAP_MOVE_STABLE;
+ if (result == L2CAP_MC_CONFIRMED) {
+ chan->local_amp_id = chan->move_id;
+ if (!chan->local_amp_id) {
+ /* Have moved off of AMP, free the channel */
+ chan->hs_hchan = NULL;
+ chan->hs_hcon = NULL;
+
+ /* Placeholder - free the logical link */
+ }
+ l2cap_move_success(chan);
+ } else {
+ chan->move_id = chan->local_amp_id;
+ l2cap_move_revert(chan);
+ }
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+ }
+
+send_move_confirm_response:
l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
+ if (chan)
+ l2cap_chan_unlock(chan);
+
return 0;
}
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 07/20] Bluetooth: Add new ERTM receive states for channel move
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
Two new states are required to implement channel moves with the ERTM
receive state machine.
The "WAIT_P" state is used by a move responder to wait for a "poll"
flag after a move is completed (success or failure). "WAIT_F" is
similarly used by a move initiator to wait for a "final" flag when the
move is completing. In either state, the reqseq value in the
poll/final frame tells the state machine exactly which frame should be
expected next.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
net/bluetooth/l2cap_core.c | 106 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 106 insertions(+)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 8097b75..5bc14ca 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -5153,6 +5153,106 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
return err;
}
+static int l2cap_finish_move(struct l2cap_chan *chan)
+{
+ int err = 0;
+
+ BT_DBG("chan %p", chan);
+
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+ chan->rx_state = L2CAP_RX_STATE_RECV;
+
+ if (chan->hs_hcon)
+ chan->conn->mtu = chan->hs_hcon->hdev->acl_mtu;
+ else
+ chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
+
+ /* Placeholder - resegment based on new conn->mtu */
+ /*err = l2cap_resegment(chan);*/
+
+ return err;
+}
+
+static int l2cap_rx_state_wait_p(struct l2cap_chan *chan,
+ struct l2cap_ctrl *control,
+ struct sk_buff *skb, u8 event)
+{
+ int err = -EPROTO;
+
+ BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
+ event);
+
+ if (!control->poll)
+ return err;
+
+ l2cap_process_reqseq(chan, control->reqseq);
+
+ if (!skb_queue_empty(&chan->tx_q))
+ chan->tx_send_head = skb_peek(&chan->tx_q);
+ else
+ chan->tx_send_head = NULL;
+
+ /* Rewind next_tx_seq to the point expected
+ * by the receiver.
+ */
+ chan->next_tx_seq = control->reqseq;
+ chan->unacked_frames = 0;
+
+ err = l2cap_finish_move(chan);
+
+ if (err)
+ return err;
+
+ set_bit(CONN_SEND_FBIT, &chan->conn_state);
+ l2cap_send_i_or_rr_or_rnr(chan);
+
+ if (event == L2CAP_EV_RECV_IFRAME)
+ err = -EPROTO;
+ else
+ err = l2cap_rx_state_recv(chan, control, NULL, event);
+
+ return err;
+}
+
+static int l2cap_rx_state_wait_f(struct l2cap_chan *chan,
+ struct l2cap_ctrl *control,
+ struct sk_buff *skb, u8 event)
+{
+ int err = -EPROTO;
+
+ if (control->final) {
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+
+ chan->rx_state = L2CAP_RX_STATE_RECV;
+ l2cap_process_reqseq(chan, control->reqseq);
+
+ if (!skb_queue_empty(&chan->tx_q))
+ chan->tx_send_head = skb_peek(&chan->tx_q);
+ else
+ chan->tx_send_head = NULL;
+
+ /* Rewind next_tx_seq to the point expected
+ * by the receiver.
+ */
+ chan->next_tx_seq = control->reqseq;
+ chan->unacked_frames = 0;
+
+ if (chan->hs_hcon)
+ chan->conn->mtu = chan->hs_hcon->hdev->acl_mtu;
+ else
+ chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
+
+ /* Placeholder - resegment based on new conn->mtu */
+ /*err = l2cap_resegment(chan);*/
+
+ if (!err)
+ err = l2cap_rx_state_recv(chan, control, skb, event);
+ }
+
+ return err;
+}
+
static bool __valid_reqseq(struct l2cap_chan *chan, u16 reqseq)
{
/* Make sure reqseq is for a packet that has been sent but not acked */
@@ -5179,6 +5279,12 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
err = l2cap_rx_state_srej_sent(chan, control, skb,
event);
break;
+ case L2CAP_RX_STATE_WAIT_P:
+ err = l2cap_rx_state_wait_p(chan, control, skb, event);
+ break;
+ case L2CAP_RX_STATE_WAIT_F:
+ err = l2cap_rx_state_wait_f(chan, control, skb, event);
+ break;
default:
/* shut it down */
break;
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 06/20] Bluetooth: Channel move request handling
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
On receipt of a channel move request, the request must be validated
based on the L2CAP mode, connection state, and controller
capabilities. ERTM channels must have their state machines cleared
and transmission paused while the channel move takes place.
If the channel is being moved to an AMP controller then
an AMP physical link must be prepared. Moving the channel back to
BR/EDR proceeds immediately.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
net/bluetooth/l2cap_core.c | 98 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 97 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index cc62f70..8097b75 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -977,6 +977,41 @@ static void l2cap_send_conn_req(struct l2cap_chan *chan)
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
}
+static void l2cap_move_setup(struct l2cap_chan *chan)
+{
+ struct sk_buff *skb;
+
+ BT_DBG("chan %p", chan);
+
+ if (chan->mode != L2CAP_MODE_ERTM)
+ return;
+
+ __clear_retrans_timer(chan);
+ __clear_monitor_timer(chan);
+ __clear_ack_timer(chan);
+
+ chan->retry_count = 0;
+ skb_queue_walk(&chan->tx_q, skb) {
+ if (bt_cb(skb)->control.retries)
+ bt_cb(skb)->control.retries = 1;
+ else
+ break;
+ }
+
+ chan->expected_tx_seq = chan->buffer_seq;
+
+ clear_bit(CONN_REJ_ACT, &chan->conn_state);
+ clear_bit(CONN_SREJ_ACT, &chan->conn_state);
+ l2cap_seq_list_clear(&chan->retrans_list);
+ l2cap_seq_list_clear(&chan->srej_list);
+ skb_queue_purge(&chan->srej_q);
+
+ chan->tx_state = L2CAP_TX_STATE_XMIT;
+ chan->rx_state = L2CAP_RX_STATE_MOVE;
+
+ set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+}
+
static void l2cap_chan_ready(struct l2cap_chan *chan)
{
/* This clears all conf flags, including CONF_NOT_COMPLETE */
@@ -4120,7 +4155,68 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
chan = l2cap_get_chan_by_dcid(conn, icid);
- /* Placeholder: Always refuse */
+ if (!chan || chan->scid < L2CAP_CID_DYN_START ||
+ (chan->mode != L2CAP_MODE_ERTM &&
+ chan->mode != L2CAP_MODE_STREAMING)) {
+ result = L2CAP_MR_NOT_ALLOWED;
+ goto send_move_response;
+ }
+
+ if (chan->local_amp_id == req->dest_amp_id) {
+ result = L2CAP_MR_SAME_ID;
+ goto send_move_response;
+ }
+
+ if (req->dest_amp_id) {
+ struct hci_dev *hdev;
+ hdev = hci_dev_get(req->dest_amp_id);
+ if (!hdev || hdev->dev_type != HCI_AMP ||
+ !test_bit(HCI_UP, &hdev->flags)) {
+ if (hdev)
+ hci_dev_put(hdev);
+
+ result = L2CAP_MR_BAD_ID;
+ goto send_move_response;
+ }
+ hci_dev_put(hdev);
+ }
+
+ if (((chan->move_state != L2CAP_MOVE_STABLE &&
+ chan->move_state != L2CAP_MOVE_WAIT_PREPARE) ||
+ chan->move_role != L2CAP_MOVE_ROLE_NONE) &&
+ bacmp(conn->src, conn->dst) > 0) {
+ result = L2CAP_MR_COLLISION;
+ goto send_move_response;
+ }
+
+ if (chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY) {
+ result = L2CAP_MR_NOT_ALLOWED;
+ goto send_move_response;
+ }
+
+ chan->ident = cmd->ident;
+ chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
+ l2cap_move_setup(chan);
+ chan->move_id = req->dest_amp_id;
+ icid = chan->dcid;
+
+ if (req->dest_amp_id == 0) {
+ /* Moving to BR/EDR */
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+ chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+ result = L2CAP_MR_PEND;
+ } else {
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+ result = L2CAP_MR_SUCCESS;
+ }
+ } else {
+ chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
+ /* Placeholder - uncomment when amp functions are available */
+ /*amp_accept_physical(chan, req->dest_amp_id);*/
+ result = L2CAP_MR_PEND;
+ }
+
+send_move_response:
l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result);
if (chan)
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 05/20] Bluetooth: Lookup channel structure based on DCID
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
Processing a move channel request involves getting the channel
structure using the destination channel ID. Previous code could only
look up using the source channel ID.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
net/bluetooth/l2cap_core.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index a042bcf..cc62f70 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -97,6 +97,22 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 ci
return c;
}
+/* Find channel with given DCID.
+ * Returns locked channel. */
+static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
+ u16 cid)
+{
+ struct l2cap_chan *c;
+
+ mutex_lock(&conn->chan_lock);
+ c = __l2cap_get_chan_by_dcid(conn, cid);
+ if (c)
+ l2cap_chan_lock(c);
+ mutex_unlock(&conn->chan_lock);
+
+ return c;
+}
+
static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
{
struct l2cap_chan *c;
@@ -4088,6 +4104,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
u16 cmd_len, void *data)
{
struct l2cap_move_chan_req *req = data;
+ struct l2cap_chan *chan;
u16 icid = 0;
u16 result = L2CAP_MR_NOT_ALLOWED;
@@ -4101,9 +4118,14 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
if (!enable_hs)
return -EINVAL;
+ chan = l2cap_get_chan_by_dcid(conn, icid);
+
/* Placeholder: Always refuse */
l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result);
+ if (chan)
+ l2cap_chan_unlock(chan);
+
return 0;
}
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 04/20] Bluetooth: Process create response and connect response identically
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
This removes an intermediate function for sending a create response
which added no functionality.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
net/bluetooth/l2cap_core.c | 18 ++++--------------
1 file changed, 4 insertions(+), 14 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 17e02d9..a042bcf 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3501,7 +3501,8 @@ static int l2cap_connect_req(struct l2cap_conn *conn,
return 0;
}
-static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_connect_create_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u8 *data)
{
struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
u16 scid, dcid, result, status;
@@ -4039,14 +4040,6 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
return 0;
}
-static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd, void *data)
-{
- BT_DBG("conn %p", conn);
-
- return l2cap_connect_rsp(conn, cmd, data);
-}
-
static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident,
u16 icid, u16 result)
{
@@ -4250,7 +4243,8 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
break;
case L2CAP_CONN_RSP:
- err = l2cap_connect_rsp(conn, cmd, data);
+ case L2CAP_CREATE_CHAN_RSP:
+ err = l2cap_connect_create_rsp(conn, cmd, data);
break;
case L2CAP_CONF_REQ:
@@ -4288,10 +4282,6 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
err = l2cap_create_channel_req(conn, cmd, cmd_len, data);
break;
- case L2CAP_CREATE_CHAN_RSP:
- err = l2cap_create_channel_rsp(conn, cmd, data);
- break;
-
case L2CAP_MOVE_CHAN_REQ:
err = l2cap_move_channel_req(conn, cmd, cmd_len, data);
break;
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 03/20] Bluetooth: Add L2CAP create channel request handling
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
The L2CAP create channel request is very similar to an L2CAP connect
request, but it has an additional parameter for the controller ID. If
the controller id is 0, the channel is set up on the BR/EDR controller
(just like a connect request). Using a valid high speed controller ID
will cause the channel to be initially created on that high speed
controller. While the L2CAP data will be initially routed over the
AMP controller, the L2CAP fixed signaling channel only uses BR/EDR.
When a create channel request is received for a high speed controller,
a pending response is always sent first. After the high speed
physical and logical links are complete a success response will be
sent.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
net/bluetooth/l2cap_core.c | 70 +++++++++++++++++++++++++++++++++++-----------
1 file changed, 54 insertions(+), 16 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 166382c..17e02d9 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3362,8 +3362,9 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
return 0;
}
-static void __l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
- u8 *data, u8 rsp_code, u8 amp_id)
+static struct l2cap_chan *__l2cap_connect(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd,
+ u8 *data, u8 rsp_code, u8 amp_id)
{
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp;
@@ -3390,7 +3391,7 @@ static void __l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
/* Check if the ACL is secure enough (if not SDP) */
if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) &&
- !hci_conn_check_link_mode(conn->hcon)) {
+ !hci_conn_check_link_mode(conn->hcon)) {
conn->disc_reason = HCI_ERROR_AUTH_FAILURE;
result = L2CAP_CR_SEC_BLOCK;
goto response;
@@ -3414,6 +3415,7 @@ static void __l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
bacpy(&bt_sk(sk)->dst, conn->dst);
chan->psm = psm;
chan->dcid = scid;
+ chan->local_amp_id = amp_id;
bt_accept_enqueue(parent, sk);
@@ -3433,8 +3435,16 @@ static void __l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
status = L2CAP_CS_AUTHOR_PEND;
parent->sk_data_ready(parent, 0);
} else {
- __l2cap_state_change(chan, BT_CONFIG);
- result = L2CAP_CR_SUCCESS;
+ /* Force pending result for AMP controllers.
+ * The connection will succeed after the
+ * physical link is up. */
+ if (amp_id) {
+ __l2cap_state_change(chan, BT_CONNECT2);
+ result = L2CAP_CR_PEND;
+ } else {
+ __l2cap_state_change(chan, BT_CONFIG);
+ result = L2CAP_CR_SUCCESS;
+ }
status = L2CAP_CS_NO_INFO;
}
} else {
@@ -3480,6 +3490,8 @@ sendresp:
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
}
+
+ return chan;
}
static int l2cap_connect_req(struct l2cap_conn *conn,
@@ -3970,12 +3982,12 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
return 0;
}
-static inline int l2cap_create_channel_req(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd, u16 cmd_len,
- void *data)
+static int l2cap_create_channel_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ void *data)
{
struct l2cap_create_chan_req *req = data;
- struct l2cap_create_chan_rsp rsp;
+ struct l2cap_chan *chan;
u16 psm, scid;
if (cmd_len != sizeof(*req))
@@ -3989,14 +4001,40 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn,
BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id);
- /* Placeholder: Always reject */
- rsp.dcid = 0;
- rsp.scid = cpu_to_le16(scid);
- rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM);
- rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+ if (req->amp_id) {
+ struct hci_dev *hdev;
- l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
- sizeof(rsp), &rsp);
+ /* Validate AMP controller id */
+ hdev = hci_dev_get(req->amp_id);
+ if (!hdev || hdev->dev_type != HCI_AMP ||
+ !test_bit(HCI_UP, &hdev->flags)) {
+ struct l2cap_create_chan_rsp rsp;
+
+ rsp.dcid = 0;
+ rsp.scid = cpu_to_le16(scid);
+ rsp.result = L2CAP_CR_BAD_AMP;
+ rsp.status = L2CAP_CS_NO_INFO;
+
+ l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
+ sizeof(rsp), &rsp);
+
+ if (hdev)
+ hci_dev_put(hdev);
+
+ return 0;
+ }
+
+ hci_dev_put(hdev);
+ }
+
+ chan = __l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
+ req->amp_id);
+
+ /* Placeholder - uncomment when amp functions are available
+ if (chan && req->amp_id &&
+ (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
+ amp_accept_physical(conn, req->amp_id, chan);
+ */
return 0;
}
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 02/20] Bluetooth: Factor out common L2CAP connection code
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
L2CAP connect requests and create channel requests share a significant
amount of code. This change moves common code to a new function.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
net/bluetooth/l2cap_core.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 111e716..166382c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3362,7 +3362,8 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
return 0;
}
-static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static void __l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
+ u8 *data, u8 rsp_code, u8 amp_id)
{
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp;
@@ -3456,7 +3457,7 @@ sendresp:
rsp.dcid = cpu_to_le16(dcid);
rsp.result = cpu_to_le16(result);
rsp.status = cpu_to_le16(status);
- l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ l2cap_send_cmd(conn, cmd->ident, rsp_code, sizeof(rsp), &rsp);
if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) {
struct l2cap_info_req info;
@@ -3479,7 +3480,12 @@ sendresp:
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
}
+}
+static int l2cap_connect_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u8 *data)
+{
+ __l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0);
return 0;
}
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 01/20] Bluetooth: Add new l2cap_chan struct members for high speed channels
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
In-Reply-To: <1347387691-5285-1-git-send-email-mathewm@codeaurora.org>
An L2CAP channel using high speed continues to be associated with a
BR/EDR l2cap_conn, while also tracking an additional hci_conn
(representing a physical link on a high speed controller) and hci_chan
(representing a logical link). There may only be one physical link
between two high speed controllers. Each physical link may contain
several logical links, with each logical link representing a channel
with specific quality of service.
During a channel move, the destination channel id, current move state,
and role (initiator vs. responder) are tracked and used by the channel
move state machine. The ident value associated with a move request
must also be stored in order to use it in later move responses.
The active channel is stored in local_amp_id.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
include/net/bluetooth/l2cap.h | 29 +++++++++++++++++++++++++++++
net/bluetooth/l2cap_core.c | 5 +++++
2 files changed, 34 insertions(+)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 7ed8e35..6b6fb27 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -433,6 +433,8 @@ struct l2cap_chan {
struct sock *sk;
struct l2cap_conn *conn;
+ struct hci_conn *hs_hcon;
+ struct hci_chan *hs_hchan;
struct kref kref;
__u8 state;
@@ -476,6 +478,11 @@ struct l2cap_chan {
unsigned long conn_state;
unsigned long flags;
+ __u8 local_amp_id;
+ __u8 move_id;
+ __u8 move_state;
+ __u8 move_role;
+
__u16 next_tx_seq;
__u16 expected_ack_seq;
__u16 expected_tx_seq;
@@ -640,6 +647,9 @@ enum {
enum {
L2CAP_RX_STATE_RECV,
L2CAP_RX_STATE_SREJ_SENT,
+ L2CAP_RX_STATE_MOVE,
+ L2CAP_RX_STATE_WAIT_P,
+ L2CAP_RX_STATE_WAIT_F,
};
enum {
@@ -670,6 +680,25 @@ enum {
L2CAP_EV_RECV_FRAME,
};
+enum {
+ L2CAP_MOVE_ROLE_NONE,
+ L2CAP_MOVE_ROLE_INITIATOR,
+ L2CAP_MOVE_ROLE_RESPONDER,
+};
+
+enum {
+ L2CAP_MOVE_STABLE,
+ L2CAP_MOVE_WAIT_REQ,
+ L2CAP_MOVE_WAIT_RSP,
+ L2CAP_MOVE_WAIT_RSP_SUCCESS,
+ L2CAP_MOVE_WAIT_CONFIRM,
+ L2CAP_MOVE_WAIT_CONFIRM_RSP,
+ L2CAP_MOVE_WAIT_LOGICAL_COMP,
+ L2CAP_MOVE_WAIT_LOGICAL_CFM,
+ L2CAP_MOVE_WAIT_LOCAL_BUSY,
+ L2CAP_MOVE_WAIT_PREPARE,
+};
+
void l2cap_chan_hold(struct l2cap_chan *c);
void l2cap_chan_put(struct l2cap_chan *c);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 9732f03..111e716 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2747,6 +2747,11 @@ int l2cap_ertm_init(struct l2cap_chan *chan)
skb_queue_head_init(&chan->tx_q);
+ chan->local_amp_id = 0;
+ chan->move_id = 0;
+ chan->move_state = L2CAP_MOVE_STABLE;
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+
if (chan->mode != L2CAP_MODE_ERTM)
return 0;
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply related
* [RFCv1 00/20] L2CAP signaling for AMP channel create/move
From: Mat Martineau @ 2012-09-11 18:21 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk
Here are the changes that process commands on the L2CAP signaling
channel for setting up AMP channels. There's still a lot of
integration to do as other AMP functionality is implemented. I've
marked places that require this integration with "Placeholder"
comments (look for that string).
This patch set has some minor conflicts with Andrei's "Create AMP
physical link" patch set, and will need to be rebased after that is
merged.
Changes:
* RFCv1 - Finished commit messages, fixed formatting/style issues
* RFCv0 - Initial post
Mat Martineau (20):
Bluetooth: Add new l2cap_chan struct members for high speed channels
Bluetooth: Factor out common L2CAP connection code
Bluetooth: Add L2CAP create channel request handling
Bluetooth: Process create response and connect response identically
Bluetooth: Lookup channel structure based on DCID
Bluetooth: Channel move request handling
Bluetooth: Add new ERTM receive states for channel move
Bluetooth: Add move channel confirm handling
Bluetooth: Add state to hci_chan
Bluetooth: Move channel response
Bluetooth: Add logical link confirm
Bluetooth: Add move confirm response handling
Bluetooth: Handle physical link completion
Bluetooth: Flag ACL frames as complete for AMP controllers
Bluetooth: Do not send data during channel move
Bluetooth: Configure appropriate timeouts for AMP controllers
Bluetooth: Ignore BR/EDR packet size constraints when fragmenting for
AMP
Bluetooth: Send create channel request instead of connect for AMP
Bluetooth: Do not retransmit data during a channel move
Bluetooth: Start channel move when socket option is changed
include/net/bluetooth/hci.h | 1 +
include/net/bluetooth/hci_core.h | 1 +
include/net/bluetooth/l2cap.h | 32 ++
net/bluetooth/hci_conn.c | 1 +
net/bluetooth/l2cap_core.c | 1019 ++++++++++++++++++++++++++++++++++++--
net/bluetooth/l2cap_sock.c | 5 +
6 files changed, 1010 insertions(+), 49 deletions(-)
--
1.7.12
--
Mat Martineau
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply
* Re: [PATCH 01/10] battery: Add generic device battery documentation
From: Joao Paulo Rechi Vita @ 2012-09-11 15:34 UTC (permalink / raw)
To: chen.ganir; +Cc: linux-bluetooth
In-Reply-To: <1347349100-24228-2-git-send-email-chen.ganir@ti.com>
Hello Chen,
On Tue, Sep 11, 2012 at 4:38 AM, <chen.ganir@ti.com> wrote:
> From: Chen Ganir <chen.ganir@ti.com>
>
> Add documentation for the generic battery D-Bus interface.
> ---
> doc/battery-api.txt | 33 +++++++++++++++++++++++++++++++++
> doc/device-api.txt | 5 +++++
> 2 files changed, 38 insertions(+)
> create mode 100644 doc/battery-api.txt
>
> diff --git a/doc/battery-api.txt b/doc/battery-api.txt
> new file mode 100644
> index 0000000..d5ef5ed
> --- /dev/null
> +++ b/doc/battery-api.txt
> @@ -0,0 +1,33 @@
> +BlueZ D-Bus Battery API description
> +****************************************
> +
> + Texas Instruments, Inc. <chen.ganir@ti.com>
> +
> +Device Battery hierarchy
> +=====================================
> +
> +Service org.bluez
> +Interface org.bluez.Battery
> +Object path [variable prefix]/{hci0,..}/dev_XX_XX_XX_XX_XX_XX/BATTYYYY
> +YYYY is numeric value between 0 and 9999.
> +
> +Methods dict GetProperties()
> +
> + Returns all properties for the interface. See the
> + Properties section for the available properties.
> +
> + void Refresh()
> +
> + Refresh the batterty level. If the battery level changed, the
> + PropertyChanged signal will be sent with the new value.
> +
> +Signals PropertyChanged(string name, variant value)
> +
> + This signal indicates a changed value of the given
> + property.
> +
> +Properties uint16 Level [readonly]
> +
> + Battery level (0-100).
> +
> +
Can you please remove these two blank lines on the end of the file?
> diff --git a/doc/device-api.txt b/doc/device-api.txt
> index 1f0dc96..c98d539 100644
> --- a/doc/device-api.txt
> +++ b/doc/device-api.txt
> @@ -179,3 +179,8 @@ Properties string Address [readonly]
> Note that this property can exhibit false-positives
> in the case of Bluetooth 2.1 (or newer) devices that
> have disabled Extended Inquiry Response support.
> +
> + array{object} Batteries [readonly]
> +
> + List of device battery object paths that represents the available
> + batteries on the remote device.
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
João Paulo Rechi Vita
Openbossa Labs - INdT
^ permalink raw reply
* Re: [PATCH] Add support for Logitech Harmony Adapter for PS3
From: Luiz Augusto von Dentz @ 2012-09-11 14:21 UTC (permalink / raw)
To: Bastien Nocera; +Cc: David Dillow, linux-bluetooth
In-Reply-To: <CABBYNZ+n9obON7iNcv66x2ZVQCeTs_JoJUDXZFwxJJ6C4cb4YQ@mail.gmail.com>
Hi Bastien,
On Mon, Sep 10, 2012 at 10:03 PM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> Hi Bastien,
>
> On Mon, Sep 10, 2012 at 4:58 PM, Luiz Augusto von Dentz
> <luiz.dentz@gmail.com> wrote:
>> Hi Bastien,
>>
>> On Mon, Sep 10, 2012 at 4:47 PM, Bastien Nocera <hadess@hadess.net> wrote:
>>> Em Mon, 2012-09-10 às 16:11 +0300, Luiz Augusto von Dentz escreveu:
>>>> Hi David, Bastien,
>>>>
>>>> So we are plannin to rid of the fakehid.c in favor of implementing it
>>>> properly inside the kernel similarly to what was done to wiimote, so
>>>> is there any obstacle for doing that?
>>>>
>>>> The kernel seems to already have some support for sixaxis in
>>>> drivers/hid/hid-sony.c, so I suppose the following would enable us to
>>>> use it:
>>>
>>> It won't. They're not the same hardware.
>>
>> What hardware is that then? And why wouldn't the kernel be able to
>> support even if it is a different driver?
>
> So what exactly are the difference between 0x0268 and 0x0306? And why
> sixpair.c save as 0x0268 while fakeinput.c uses 0x0306?
>
> Also, after fixing sixpair.c to be able to compile it does add to the
> storage but it does not create any object until bluetoothd is
> restarted and even after restart it does not allow the device to
> connect because it does has the record (although this can be fixed by
> automatically add the UUID once we find out it is attempt to connect).
So apparently the 0x0306 device is the br remote controller, not the
sixaxis joystick, sorry about the confusion. Anyway regardless of
being a different device I thing we should move this code to kernel as
it was done for wiimote.
--
Luiz Augusto von Dentz
^ permalink raw reply
* [PATCH v0 4/4] audio: Drop audio-specific authorization mechanism
From: Mikel Astiz @ 2012-09-11 13:55 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1347371730-14933-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Remove the audio-specific service authorization mechanism in favor of
using the conventional one.
The main difference is that audio profiles will be authorized
independently. Therefore a single connection might result in several
profile authorization requests to the agent (i.e. HFP, A2DP and AVRCP).
This removes any internal policy that would skip the authorization
procedure, making it simpler and more convenient for IVI use-cases.
Agents interested in simulating the old behavior are encouraged to
either set the device as trusted or just reply to the additional
authorization requests automatically without user intervention.
---
audio/avctp.c | 29 ++++------
audio/avdtp.c | 25 ++++++---
audio/device.c | 150 -------------------------------------------------------
audio/device.h | 12 +----
audio/manager.c | 18 ++++---
5 files changed, 40 insertions(+), 194 deletions(-)
diff --git a/audio/avctp.c b/audio/avctp.c
index 4e0e8dc..40e85ed 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -139,6 +139,7 @@ struct avctp {
GIOChannel *io;
guint io_id;
+ int service_auth_id;
uint16_t mtu;
@@ -337,13 +338,9 @@ static void avctp_disconnected(struct avctp *session)
g_source_remove(session->io_id);
session->io_id = 0;
- if (session->state == AVCTP_STATE_CONNECTING) {
- struct audio_device *dev;
-
- dev = manager_get_device(&session->server->src,
- &session->dst, FALSE);
- audio_device_cancel_authorization(dev, auth_cb,
- session);
+ if (session->service_auth_id > 0) {
+ btd_cancel_authorization(session->service_auth_id);
+ session->service_auth_id = 0;
}
}
@@ -386,15 +383,7 @@ static void avctp_set_state(struct avctp *session, avctp_state_t new_state)
switch (new_state) {
case AVCTP_STATE_DISCONNECTED:
DBG("AVCTP Disconnected");
-
avctp_disconnected(session);
-
- if (old_state != AVCTP_STATE_CONNECTED)
- break;
-
- if (!audio_device_is_active(dev, NULL))
- audio_device_set_authorized(dev, FALSE);
-
break;
case AVCTP_STATE_CONNECTING:
DBG("AVCTP Connecting");
@@ -657,6 +646,8 @@ static void auth_cb(DBusError *derr, void *user_data)
struct avctp *session = user_data;
GError *err = NULL;
+ session->service_auth_id = 0;
+
if (session->io_id) {
g_source_remove(session->io_id);
session->io_id = 0;
@@ -736,6 +727,7 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
struct audio_device *dev;
char address[18];
bdaddr_t src, dst;
+ int auth_id;
GError *err = NULL;
bt_io_get(chan, &err,
@@ -780,12 +772,15 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
avctp_set_state(session, AVCTP_STATE_CONNECTING);
session->io = g_io_channel_ref(chan);
- if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID,
- auth_cb, session) < 0)
+ auth_id = btd_request_authorization(&dev->src, &dev->dst,
+ AVRCP_TARGET_UUID,
+ auth_cb, session);
+ if (auth_id < 0)
goto drop;
session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
session_cb, session);
+ session->service_auth_id = auth_id;
return;
drop:
diff --git a/audio/avdtp.c b/audio/avdtp.c
index d44c504..3137094 100644
--- a/audio/avdtp.c
+++ b/audio/avdtp.c
@@ -400,6 +400,8 @@ struct avdtp {
/* True if the entire device is being disconnected */
gboolean device_disconnect;
+ int service_auth_id;
+
GIOChannel *io;
guint io_id;
@@ -1129,16 +1131,21 @@ static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
static int avdtp_cancel_authorization(struct avdtp *session)
{
- struct audio_device *dev;
+ int err;
if (session->state != AVDTP_SESSION_STATE_CONNECTING)
return 0;
- dev = manager_get_device(&session->server->src, &session->dst, FALSE);
- if (dev == NULL)
- return -ENODEV;
+ if (!session->service_auth_id)
+ return 0;
- return audio_device_cancel_authorization(dev, auth_cb, session);
+ err = btd_cancel_authorization(session->service_auth_id);
+ if (err < 0)
+ return err;
+
+ session->service_auth_id = 0;
+
+ return 0;
}
static void connection_lost(struct avdtp *session, int err)
@@ -2507,7 +2514,7 @@ static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
struct audio_device *dev;
char address[18];
bdaddr_t src, dst;
- int perr;
+ int auth_id;
GError *err = NULL;
bt_io_get(chan, &err,
@@ -2566,15 +2573,17 @@ static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) session_cb, session);
- perr = audio_device_request_authorization(dev, ADVANCED_AUDIO_UUID,
+ auth_id = btd_request_authorization(&dev->src, &dev->dst,
+ ADVANCED_AUDIO_UUID,
auth_cb, session);
- if (perr < 0) {
+ if (auth_id < 0) {
avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
avdtp_unref(session);
goto drop;
}
dev->auto_connect = auto_connect;
+ session->service_auth_id = auth_id;
return;
diff --git a/audio/device.c b/audio/device.c
index ca64f14..072b0ce 100644
--- a/audio/device.c
+++ b/audio/device.c
@@ -71,19 +71,12 @@ typedef enum {
AUDIO_STATE_CONNECTED,
} audio_state_t;
-struct service_auth {
- service_auth_cb cb;
- void *user_data;
-};
-
struct dev_priv {
audio_state_t state;
headset_state_t hs_state;
sink_state_t sink_state;
avctp_state_t avctp_state;
- GSList *auths;
- int auth_id;
DBusMessage *conn_req;
DBusMessage *dc_req;
@@ -94,8 +87,6 @@ struct dev_priv {
guint dc_id;
gboolean disconnecting;
- gboolean authorized;
- guint auth_idle_id;
};
static unsigned int sink_callback_id = 0;
@@ -113,8 +104,6 @@ static void device_free(struct audio_device *dev)
btd_device_unref(dev->btd_dev);
if (priv) {
- if (priv->auths)
- audio_device_cancel_authorization(dev, NULL, NULL);
if (priv->control_timer)
g_source_remove(priv->control_timer);
if (priv->avdtp_timer)
@@ -240,8 +229,6 @@ static void device_set_state(struct audio_device *dev, audio_state_t new_state)
return;
if (new_state == AUDIO_STATE_DISCONNECTED) {
- priv->authorized = FALSE;
-
if (priv->dc_id) {
device_remove_disconnect_watch(dev->btd_dev,
priv->dc_id);
@@ -736,140 +723,3 @@ void audio_device_unregister(struct audio_device *device)
device_free(device);
}
-
-static void auth_cb(DBusError *derr, void *user_data)
-{
- struct audio_device *dev = user_data;
- struct dev_priv *priv = dev->priv;
-
- priv->auth_id = 0;
-
- if (derr == NULL)
- priv->authorized = TRUE;
-
- while (priv->auths) {
- struct service_auth *auth = priv->auths->data;
-
- auth->cb(derr, auth->user_data);
- priv->auths = g_slist_remove(priv->auths, auth);
- g_free(auth);
- }
-}
-
-static gboolean auth_idle_cb(gpointer user_data)
-{
- struct audio_device *dev = user_data;
- struct dev_priv *priv = dev->priv;
-
- priv->auth_idle_id = 0;
-
- auth_cb(NULL, dev);
-
- return FALSE;
-}
-
-static gboolean audio_device_is_connected(struct audio_device *dev)
-{
- if (dev->headset) {
- headset_state_t state = headset_get_state(dev);
-
- if (state == HEADSET_STATE_CONNECTED ||
- state == HEADSET_STATE_PLAY_IN_PROGRESS ||
- state == HEADSET_STATE_PLAYING)
- return TRUE;
- }
-
- if (dev->sink) {
- sink_state_t state = sink_get_state(dev);
-
- if (state == SINK_STATE_CONNECTED ||
- state == SINK_STATE_PLAYING)
- return TRUE;
- }
-
- if (dev->source) {
- source_state_t state = source_get_state(dev);
-
- if (state == SOURCE_STATE_CONNECTED ||
- state == SOURCE_STATE_PLAYING)
- return TRUE;
- }
-
- return FALSE;
-}
-
-int audio_device_request_authorization(struct audio_device *dev,
- const char *uuid, service_auth_cb cb,
- void *user_data)
-{
- struct dev_priv *priv = dev->priv;
- struct service_auth *auth;
- int err;
-
- auth = g_try_new0(struct service_auth, 1);
- if (!auth)
- return -ENOMEM;
-
- auth->cb = cb;
- auth->user_data = user_data;
-
- priv->auths = g_slist_append(priv->auths, auth);
- if (g_slist_length(priv->auths) > 1)
- return 0;
-
- if (priv->authorized || audio_device_is_connected(dev)) {
- priv->auth_idle_id = g_idle_add(auth_idle_cb, dev);
- return 0;
- }
-
- err = btd_request_authorization(&dev->src, &dev->dst, uuid, auth_cb,
- dev);
- if (err < 0) {
- priv->auths = g_slist_remove(priv->auths, auth);
- g_free(auth);
- } else
- priv->auth_id = err;
-
- return err;
-}
-
-int audio_device_cancel_authorization(struct audio_device *dev,
- authorization_cb cb, void *user_data)
-{
- struct dev_priv *priv = dev->priv;
- GSList *l, *next;
-
- for (l = priv->auths; l != NULL; l = next) {
- struct service_auth *auth = l->data;
-
- next = g_slist_next(l);
-
- if (cb && auth->cb != cb)
- continue;
-
- if (user_data && auth->user_data != user_data)
- continue;
-
- priv->auths = g_slist_remove(priv->auths, auth);
- g_free(auth);
- }
-
- if (g_slist_length(priv->auths) == 0) {
- if (priv->auth_idle_id > 0) {
- g_source_remove(priv->auth_idle_id);
- priv->auth_idle_id = 0;
- } else {
- btd_cancel_authorization(priv->auth_id);
- priv->auth_id = 0;
- }
- }
-
- return 0;
-}
-
-void audio_device_set_authorized(struct audio_device *dev, gboolean auth)
-{
- struct dev_priv *priv = dev->priv;
-
- priv->authorized = auth;
-}
diff --git a/audio/device.h b/audio/device.h
index 75f1da9..49c6829 100644
--- a/audio/device.h
+++ b/audio/device.h
@@ -48,6 +48,7 @@ struct audio_device {
struct target *target;
guint hs_preauth_id;
+ int hs_service_auth_id;
struct dev_priv *priv;
};
@@ -61,14 +62,3 @@ void audio_device_unregister(struct audio_device *device);
gboolean audio_device_is_active(struct audio_device *dev,
const char *interface);
-
-typedef void (*authorization_cb) (DBusError *derr, void *user_data);
-
-int audio_device_cancel_authorization(struct audio_device *dev,
- authorization_cb cb, void *user_data);
-
-int audio_device_request_authorization(struct audio_device *dev,
- const char *uuid, authorization_cb cb,
- void *user_data);
-
-void audio_device_set_authorized(struct audio_device *dev, gboolean auth);
diff --git a/audio/manager.c b/audio/manager.c
index 5c81957..0222d67 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -328,7 +328,7 @@ static gboolean hs_preauth_cb(GIOChannel *chan, GIOCondition cond,
DBG("Headset disconnected during authorization");
- audio_device_cancel_authorization(device, headset_auth_cb, device);
+ btd_cancel_authorization(device->hs_service_auth_id);
headset_set_state(device, HEADSET_STATE_DISCONNECTED);
@@ -343,7 +343,7 @@ static void ag_confirm(GIOChannel *chan, gpointer data)
struct audio_device *device;
gboolean hfp_active;
bdaddr_t src, dst;
- int perr;
+ int auth_id;
GError *err = NULL;
uint8_t ch;
@@ -398,10 +398,11 @@ static void ag_confirm(GIOChannel *chan, gpointer data)
headset_set_state(device, HEADSET_STATE_CONNECTING);
- perr = audio_device_request_authorization(device, server_uuid,
- headset_auth_cb, device);
- if (perr < 0) {
- DBG("Authorization denied: %s", strerror(-perr));
+ auth_id = btd_request_authorization(&device->src, &device->dst,
+ server_uuid, headset_auth_cb,
+ device);
+ if (auth_id < 0) {
+ DBG("Authorization denied: %s", strerror(-auth_id));
headset_set_state(device, HEADSET_STATE_DISCONNECTED);
return;
}
@@ -480,8 +481,9 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
goto drop;
}
- perr = audio_device_request_authorization(device, server_uuid,
- gateway_auth_cb, device);
+ perr = btd_request_authorization(&device->src, &device->dst,
+ server_uuid, gateway_auth_cb,
+ device);
if (perr < 0) {
DBG("Authorization denied: %s", strerror(-perr));
gateway_set_state(device, GATEWAY_STATE_DISCONNECTED);
--
1.7.7.6
^ permalink raw reply related
* [PATCH v0 3/4] adapter: Queue parallel authorization requests
From: Mikel Astiz @ 2012-09-11 13:55 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1347371730-14933-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Remote device could try to connect several profiles in parallel, or
several devices could be trying to connect services. Instead of
returning EBUSY, this patch adds a queue to each adapter such that the
authorization requests will be queued and processed sequentially.
---
src/adapter.c | 150 ++++++++++++++++++++++++++++++--------------------------
1 files changed, 80 insertions(+), 70 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index de5d1c9..14fe02a 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -103,8 +103,10 @@ struct service_auth {
int id;
service_auth_cb cb;
void *user_data;
+ const char *uuid;
struct btd_device *device;
struct btd_adapter *adapter;
+ struct agent *agent; /* NULL for queued auths */
};
struct btd_adapter {
@@ -131,8 +133,8 @@ struct btd_adapter {
GSList *found_devices;
GSList *oor_devices; /* out of range device list */
struct agent *agent; /* For the new API */
- guint auth_idle_id; /* Ongoing authorization (trusted) */
- struct service_auth *auth; /* Ongoing authorization */
+ guint auth_idle_id; /* Pending authorization dequeue */
+ GQueue *auths; /* Ongoing and pending auths */
GSList *connections; /* Connected devices */
GSList *devices; /* Devices structure pointers */
GSList *mode_sessions; /* Request Mode sessions */
@@ -160,6 +162,8 @@ struct btd_adapter {
GSList *profiles;
};
+static gboolean process_auth_queue(gpointer user_data);
+
static void dev_info_free(void *data)
{
struct remote_dev_info *dev = data;
@@ -971,16 +975,34 @@ static struct btd_device *adapter_create_device(DBusConnection *conn,
return device;
}
+static void service_auth_cancel(struct service_auth *auth)
+{
+ DBusError derr;
+
+ dbus_error_init(&derr);
+ dbus_set_error_const(&derr, "org.bluez.Error.Canceled", NULL);
+
+ auth->cb(&derr, auth->user_data);
+
+ dbus_error_free(&derr);
+
+ if (auth->agent != NULL)
+ agent_cancel(auth->agent);
+
+ service_auth_free(auth);
+}
+
void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter,
struct btd_device *device,
gboolean remove_storage)
{
const gchar *dev_path = device_get_path(device);
- struct agent *agent;
adapter->devices = g_slist_remove(adapter->devices, device);
adapter->connections = g_slist_remove(adapter->connections, device);
+ g_queue_foreach(adapter->auths, (GFunc) service_auth_cancel, NULL);
+
adapter_update_devices(adapter);
g_dbus_emit_signal(conn, adapter->path,
@@ -988,13 +1010,6 @@ void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter,
DBUS_TYPE_OBJECT_PATH, &dev_path,
DBUS_TYPE_INVALID);
- agent = device_get_agent(device);
-
- if (agent && adapter->auth && adapter->auth->device == device) {
- adapter->auth = NULL;
- agent_cancel(agent);
- }
-
device_remove(device, remove_storage);
}
@@ -2426,8 +2441,8 @@ static void adapter_free(gpointer user_data)
if (adapter->auth_idle_id)
g_source_remove(adapter->auth_idle_id);
- if (adapter->auth)
- service_auth_free(adapter->auth);
+ g_queue_foreach(adapter->auths, (GFunc) service_auth_free, NULL);
+ g_queue_free(adapter->auths);
if (adapter->off_timer)
off_timer_remove(adapter);
@@ -2525,6 +2540,7 @@ struct btd_adapter *adapter_create(DBusConnection *conn, int id)
}
adapter->dev_id = id;
+ adapter->auths = g_queue_new();
snprintf(path, sizeof(path), "%s/hci%d", base_path, id);
adapter->path = g_strdup(path);
@@ -3173,24 +3189,60 @@ static void agent_auth_cb(struct agent *agent, DBusError *derr,
void *user_data)
{
struct btd_adapter *adapter = user_data;
- struct service_auth *auth = adapter->auth;
+ struct service_auth *auth = adapter->auths->head->data;
- adapter->auth = NULL;
+ g_queue_pop_head(adapter->auths);
auth->cb(derr, auth->user_data);
service_auth_free(auth);
+
+ adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter);
}
-static gboolean auth_idle_cb(gpointer user_data)
+static gboolean process_auth_queue(gpointer user_data)
{
struct btd_adapter *adapter = user_data;
- struct service_auth *auth = adapter->auth;
+ DBusError err;
- adapter->auth = NULL;
adapter->auth_idle_id = 0;
- auth->cb(NULL, auth->user_data);
+ dbus_error_init(&err);
+ dbus_set_error_const(&err, "org.bluez.Error.Rejected", NULL);
+
+ while (!g_queue_is_empty(adapter->auths)) {
+ struct service_auth *auth = adapter->auths->head->data;
+ struct btd_device *device = auth->device;
+ const gchar *dev_path;
+
+ if (device_is_trusted(device) == TRUE) {
+ auth->cb(NULL, auth->user_data);
+ goto next;
+ }
+
+ auth->agent = device_get_agent(device);
+ if (auth->agent == NULL) {
+ warn("Can't find device agent");
+ auth->cb(&err, auth->user_data);
+ goto next;
+ }
+
+ dev_path = device_get_path(device);
+
+ if (agent_authorize(auth->agent, dev_path, auth->uuid,
+ agent_auth_cb, adapter, NULL) < 0) {
+ auth->cb(&err, auth->user_data);
+ goto next;
+ }
+
+ break;
+
+next:
+ service_auth_free(auth);
+ g_queue_pop_head(adapter->auths);
+ }
+
+ dbus_error_free(&err);
return FALSE;
}
@@ -3201,10 +3253,7 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
{
struct service_auth *auth;
struct btd_device *device;
- struct agent *agent;
char address[18];
- const gchar *dev_path;
- int err;
ba2str(dst, address);
device = adapter_find_device(adapter, address);
@@ -3215,46 +3264,29 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
if (!g_slist_find(adapter->connections, device))
error("Authorization request for non-connected device!?");
- if (adapter->auth)
- return -EBUSY;
-
auth = g_try_new0(struct service_auth, 1);
if (!auth)
return -ENOMEM;
auth->cb = cb;
auth->user_data = user_data;
+ auth->uuid = uuid;
auth->device = device;
auth->adapter = adapter;
auth->id = service_auth_id++;
g_hash_table_insert(service_auths, GUINT_TO_POINTER(auth->id), auth);
- if (device_is_trusted(device) == TRUE) {
- adapter->auth_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
- auth_idle_cb, adapter,
- NULL);
- goto done;
- }
+ g_queue_push_tail(adapter->auths, auth);
- agent = device_get_agent(device);
- if (!agent) {
- warn("Can't find device agent");
- service_auth_free(auth);
- return -EPERM;
- }
+ if (adapter->auths->length != 1)
+ return auth->id;
- dev_path = device_get_path(device);
+ if (adapter->auth_idle_id != 0)
+ return auth->id;
- err = agent_authorize(agent, dev_path, uuid, agent_auth_cb, adapter,
- NULL);
- if (err < 0) {
- service_auth_free(auth);
- return err;
- }
+ adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter);
-done:
- adapter->auth = auth;
return auth->id;
}
@@ -3289,9 +3321,6 @@ int btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
int btd_cancel_authorization(int auth_id)
{
struct service_auth *auth;
- struct btd_adapter *adapter;
- struct agent *agent;
- int err;
if (!service_auths)
return -EPERM;
@@ -3300,30 +3329,11 @@ int btd_cancel_authorization(int auth_id)
if (!auth)
return -EPERM;
- adapter = auth->adapter;
-
- if (adapter->auth_idle_id) {
- g_source_remove(adapter->auth_idle_id);
- adapter->auth_idle_id = 0;
- adapter->auth = NULL;
- return 0;
- }
-
- /*
- * FIXME: Cancel fails if authorization is requested to adapter's
- * agent and in the meanwhile CreatePairedDevice is called.
- */
-
- agent = device_get_agent(auth->device);
- if (!agent)
- return -EPERM;
-
- err = agent_cancel(agent);
+ g_queue_remove(auth->adapter->auths, auth);
- if (err == 0)
- adapter->auth = NULL;
+ service_auth_cancel(auth);
- return err;
+ return 0;
}
static gchar *adapter_any_path = NULL;
--
1.7.7.6
^ permalink raw reply related
* [PATCH v0 2/4] adapter: Use authorization id for cancelling
From: Mikel Astiz @ 2012-09-11 13:55 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1347371730-14933-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Return a request id in btd_request_authorization() in order to be used
when the request needs to be cancelled. This id alone will be enough to
use btd_cancel_authorization().
---
audio/device.c | 12 +++++++--
plugins/service.c | 18 ++++++++-----
profiles/input/server.c | 2 +-
src/adapter.c | 62 +++++++++++++++++++++++++++++++----------------
src/adapter.h | 2 +-
5 files changed, 63 insertions(+), 33 deletions(-)
diff --git a/audio/device.c b/audio/device.c
index 4952b57..ca64f14 100644
--- a/audio/device.c
+++ b/audio/device.c
@@ -83,6 +83,7 @@ struct dev_priv {
sink_state_t sink_state;
avctp_state_t avctp_state;
GSList *auths;
+ int auth_id;
DBusMessage *conn_req;
DBusMessage *dc_req;
@@ -741,6 +742,8 @@ static void auth_cb(DBusError *derr, void *user_data)
struct audio_device *dev = user_data;
struct dev_priv *priv = dev->priv;
+ priv->auth_id = 0;
+
if (derr == NULL)
priv->authorized = TRUE;
@@ -824,7 +827,8 @@ int audio_device_request_authorization(struct audio_device *dev,
if (err < 0) {
priv->auths = g_slist_remove(priv->auths, auth);
g_free(auth);
- }
+ } else
+ priv->auth_id = err;
return err;
}
@@ -854,8 +858,10 @@ int audio_device_cancel_authorization(struct audio_device *dev,
if (priv->auth_idle_id > 0) {
g_source_remove(priv->auth_idle_id);
priv->auth_idle_id = 0;
- } else
- btd_cancel_authorization(&dev->src, &dev->dst);
+ } else {
+ btd_cancel_authorization(priv->auth_id);
+ priv->auth_id = 0;
+ }
}
return 0;
diff --git a/plugins/service.c b/plugins/service.c
index f16abe7..506a0cd 100644
--- a/plugins/service.c
+++ b/plugins/service.c
@@ -67,6 +67,7 @@ struct pending_auth {
char *sender;
bdaddr_t dst;
char uuid[MAX_LEN_UUID_STR];
+ int auth_id;
};
struct service_adapter {
@@ -560,8 +561,9 @@ done:
else
bacpy(&src, BDADDR_ANY);
- btd_request_authorization(&src, &auth->dst,
- auth->uuid, auth_cb, serv_adapter);
+ auth->auth_id = btd_request_authorization(&src, &auth->dst,
+ auth->uuid, auth_cb,
+ serv_adapter);
}
static DBusMessage *request_authorization(DBusConnection *conn,
@@ -637,8 +639,9 @@ static DBusMessage *request_authorization(DBusConnection *conn,
else
bacpy(&src, BDADDR_ANY);
- if (btd_request_authorization(&src, &auth->dst, auth->uuid, auth_cb,
- serv_adapter) < 0) {
+ auth->auth_id = btd_request_authorization(&src, &auth->dst, auth->uuid,
+ auth_cb, serv_adapter);
+ if (auth->auth_id < 0) {
serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
auth);
g_free(auth);
@@ -668,7 +671,7 @@ static DBusMessage *cancel_authorization(DBusConnection *conn,
else
bacpy(&src, BDADDR_ANY);
- btd_cancel_authorization(&src, &auth->dst);
+ btd_cancel_authorization(auth->auth_id);
reply = btd_error_not_authorized(auth->msg);
dbus_message_unref(auth->msg);
@@ -689,8 +692,9 @@ static DBusMessage *cancel_authorization(DBusConnection *conn,
else
bacpy(&src, BDADDR_ANY);
- btd_request_authorization(&src, &auth->dst,
- auth->uuid, auth_cb, serv_adapter);
+ auth->auth_id = btd_request_authorization(&src, &auth->dst,
+ auth->uuid, auth_cb,
+ serv_adapter);
done:
return dbus_message_new_method_return(msg);
diff --git a/profiles/input/server.c b/profiles/input/server.c
index 0464c4a..0385012 100644
--- a/profiles/input/server.c
+++ b/profiles/input/server.c
@@ -175,7 +175,7 @@ static void confirm_event_cb(GIOChannel *chan, gpointer user_data)
ret = btd_request_authorization(&src, &dst, HID_UUID,
auth_callback, server);
- if (ret == 0)
+ if (ret >= 0)
return;
ba2str(&src, addr);
diff --git a/src/adapter.c b/src/adapter.c
index 14c5322..de5d1c9 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -85,6 +85,8 @@
static DBusConnection *connection = NULL;
static GSList *adapter_drivers = NULL;
+static int service_auth_id = 0;
+static GHashTable *service_auths = NULL;
struct session_req {
struct btd_adapter *adapter;
@@ -98,6 +100,7 @@ struct session_req {
};
struct service_auth {
+ int id;
service_auth_cb cb;
void *user_data;
struct btd_device *device;
@@ -208,6 +211,15 @@ static uint8_t get_mode(const bdaddr_t *bdaddr, const char *mode)
return MODE_UNKNOWN;
}
+static void service_auth_free(gpointer data)
+{
+ struct service_auth *auth = data;
+
+ g_hash_table_remove(service_auths, GUINT_TO_POINTER(auth->id));
+
+ g_free(auth);
+}
+
static struct session_req *session_ref(struct session_req *req)
{
req->refcount++;
@@ -2415,7 +2427,7 @@ static void adapter_free(gpointer user_data)
g_source_remove(adapter->auth_idle_id);
if (adapter->auth)
- g_free(adapter->auth);
+ service_auth_free(adapter->auth);
if (adapter->off_timer)
off_timer_remove(adapter);
@@ -2429,6 +2441,8 @@ static void adapter_free(gpointer user_data)
g_free(adapter->path);
g_free(adapter->name);
g_free(adapter);
+
+ g_hash_table_unref(service_auths);
}
struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter)
@@ -2523,6 +2537,11 @@ struct btd_adapter *adapter_create(DBusConnection *conn, int id)
return NULL;
}
+ if (!service_auths)
+ service_auths = g_hash_table_new(g_direct_hash, g_direct_equal);
+ else
+ g_hash_table_ref(service_auths);
+
return btd_adapter_ref(adapter);
}
@@ -3160,7 +3179,7 @@ static void agent_auth_cb(struct agent *agent, DBusError *derr,
auth->cb(derr, auth->user_data);
- g_free(auth);
+ service_auth_free(auth);
}
static gboolean auth_idle_cb(gpointer user_data)
@@ -3207,6 +3226,9 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
auth->user_data = user_data;
auth->device = device;
auth->adapter = adapter;
+ auth->id = service_auth_id++;
+
+ g_hash_table_insert(service_auths, GUINT_TO_POINTER(auth->id), auth);
if (device_is_trusted(device) == TRUE) {
adapter->auth_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
@@ -3218,7 +3240,7 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
agent = device_get_agent(device);
if (!agent) {
warn("Can't find device agent");
- g_free(auth);
+ service_auth_free(auth);
return -EPERM;
}
@@ -3227,13 +3249,13 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
err = agent_authorize(agent, dev_path, uuid, agent_auth_cb, adapter,
NULL);
if (err < 0) {
- g_free(auth);
+ service_auth_free(auth);
return err;
}
done:
adapter->auth = auth;
- return 0;
+ return auth->id;
}
int btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
@@ -3252,49 +3274,47 @@ int btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
}
for (l = manager_get_adapters(); l != NULL; l = g_slist_next(l)) {
- int err;
+ int auth_id;
adapter = l->data;
- err = adapter_authorize(adapter, dst, uuid, cb, user_data);
- if (err == 0)
- return 0;
+ auth_id = adapter_authorize(adapter, dst, uuid, cb, user_data);
+ if (auth_id >= 0)
+ return auth_id;
}
return -EPERM;
}
-int btd_cancel_authorization(const bdaddr_t *src, const bdaddr_t *dst)
+int btd_cancel_authorization(int auth_id)
{
- struct btd_adapter *adapter = manager_find_adapter(src);
- struct btd_device *device;
+ struct service_auth *auth;
+ struct btd_adapter *adapter;
struct agent *agent;
- char address[18];
int err;
- if (!adapter)
+ if (!service_auths)
return -EPERM;
- ba2str(dst, address);
- device = adapter_find_device(adapter, address);
- if (!device)
+ auth = g_hash_table_lookup(service_auths, GUINT_TO_POINTER(auth_id));
+ if (!auth)
return -EPERM;
+ adapter = auth->adapter;
+
if (adapter->auth_idle_id) {
g_source_remove(adapter->auth_idle_id);
adapter->auth_idle_id = 0;
+ adapter->auth = NULL;
return 0;
}
- if (!adapter->auth || adapter->auth->device != device)
- return -EPERM;
-
/*
* FIXME: Cancel fails if authorization is requested to adapter's
* agent and in the meanwhile CreatePairedDevice is called.
*/
- agent = device_get_agent(device);
+ agent = device_get_agent(auth->device);
if (!agent)
return -EPERM;
diff --git a/src/adapter.h b/src/adapter.h
index cd37b15..2993715 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -149,7 +149,7 @@ int btd_register_adapter_driver(struct btd_adapter_driver *driver);
void btd_unregister_adapter_driver(struct btd_adapter_driver *driver);
int btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
const char *uuid, service_auth_cb cb, void *user_data);
-int btd_cancel_authorization(const bdaddr_t *src, const bdaddr_t *dst);
+int btd_cancel_authorization(int auth_id);
const char *adapter_any_get_path(void);
--
1.7.7.6
^ permalink raw reply related
* [PATCH v0 1/4] adapter: Replace device authorizing flag
From: Mikel Astiz @ 2012-09-11 13:55 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1347371730-14933-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Refactor code to drop the device authorizing flag by replacing it with a
private authorization pointer in btd_adapter. After all, no more than
one authorization can be ongoing, so the code is easier to follow if
this is made explicit.
---
src/adapter.c | 48 ++++++++++++++++++++++++++++++++----------------
src/device.c | 11 -----------
src/device.h | 2 --
3 files changed, 32 insertions(+), 29 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index 4b675e8..14c5322 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -128,7 +128,8 @@ struct btd_adapter {
GSList *found_devices;
GSList *oor_devices; /* out of range device list */
struct agent *agent; /* For the new API */
- guint auth_idle_id; /* Ongoing authorization */
+ guint auth_idle_id; /* Ongoing authorization (trusted) */
+ struct service_auth *auth; /* Ongoing authorization */
GSList *connections; /* Connected devices */
GSList *devices; /* Devices structure pointers */
GSList *mode_sessions; /* Request Mode sessions */
@@ -977,8 +978,10 @@ void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter,
agent = device_get_agent(device);
- if (agent && device_is_authorizing(device))
+ if (agent && adapter->auth && adapter->auth->device == device) {
+ adapter->auth = NULL;
agent_cancel(agent);
+ }
device_remove(device, remove_storage);
}
@@ -2411,6 +2414,9 @@ static void adapter_free(gpointer user_data)
if (adapter->auth_idle_id)
g_source_remove(adapter->auth_idle_id);
+ if (adapter->auth)
+ g_free(adapter->auth);
+
if (adapter->off_timer)
off_timer_remove(adapter);
@@ -3147,18 +3153,22 @@ void btd_unregister_adapter_driver(struct btd_adapter_driver *driver)
static void agent_auth_cb(struct agent *agent, DBusError *derr,
void *user_data)
{
- struct service_auth *auth = user_data;
+ struct btd_adapter *adapter = user_data;
+ struct service_auth *auth = adapter->auth;
- device_set_authorizing(auth->device, FALSE);
+ adapter->auth = NULL;
auth->cb(derr, auth->user_data);
+
+ g_free(auth);
}
static gboolean auth_idle_cb(gpointer user_data)
{
- struct service_auth *auth = user_data;
- struct btd_adapter *adapter = auth->adapter;
+ struct btd_adapter *adapter = user_data;
+ struct service_auth *auth = adapter->auth;
+ adapter->auth = NULL;
adapter->auth_idle_id = 0;
auth->cb(NULL, auth->user_data);
@@ -3186,7 +3196,7 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
if (!g_slist_find(adapter->connections, device))
error("Authorization request for non-connected device!?");
- if (adapter->auth_idle_id)
+ if (adapter->auth)
return -EBUSY;
auth = g_try_new0(struct service_auth, 1);
@@ -3200,9 +3210,9 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
if (device_is_trusted(device) == TRUE) {
adapter->auth_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
- auth_idle_cb, auth,
- g_free);
- return 0;
+ auth_idle_cb, adapter,
+ NULL);
+ goto done;
}
agent = device_get_agent(device);
@@ -3214,13 +3224,16 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
dev_path = device_get_path(device);
- err = agent_authorize(agent, dev_path, uuid, agent_auth_cb, auth, g_free);
- if (err < 0)
+ err = agent_authorize(agent, dev_path, uuid, agent_auth_cb, adapter,
+ NULL);
+ if (err < 0) {
g_free(auth);
- else
- device_set_authorizing(device, TRUE);
+ return err;
+ }
- return err;
+done:
+ adapter->auth = auth;
+ return 0;
}
int btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
@@ -3273,6 +3286,9 @@ int btd_cancel_authorization(const bdaddr_t *src, const bdaddr_t *dst)
return 0;
}
+ if (!adapter->auth || adapter->auth->device != device)
+ return -EPERM;
+
/*
* FIXME: Cancel fails if authorization is requested to adapter's
* agent and in the meanwhile CreatePairedDevice is called.
@@ -3285,7 +3301,7 @@ int btd_cancel_authorization(const bdaddr_t *src, const bdaddr_t *dst)
err = agent_cancel(agent);
if (err == 0)
- device_set_authorizing(device, FALSE);
+ adapter->auth = NULL;
return err;
}
diff --git a/src/device.c b/src/device.c
index 02ef35e..fd0b7f9 100644
--- a/src/device.c
+++ b/src/device.c
@@ -164,7 +164,6 @@ struct btd_device {
gboolean bonded;
gboolean auto_connect;
- gboolean authorizing;
gint ref;
GIOChannel *att_io;
@@ -2963,16 +2962,6 @@ gboolean device_is_authenticating(struct btd_device *device)
return (device->authr != NULL);
}
-gboolean device_is_authorizing(struct btd_device *device)
-{
- return device->authorizing;
-}
-
-void device_set_authorizing(struct btd_device *device, gboolean auth)
-{
- device->authorizing = auth;
-}
-
void device_register_services(DBusConnection *conn, struct btd_device *device,
GSList *prim_list, int psm)
{
diff --git a/src/device.h b/src/device.h
index f1d95c6..cb3a982 100644
--- a/src/device.h
+++ b/src/device.h
@@ -131,8 +131,6 @@ int device_notify_pincode(struct btd_device *device, gboolean secure,
const char *pincode, void *cb);
void device_cancel_authentication(struct btd_device *device, gboolean aborted);
gboolean device_is_authenticating(struct btd_device *device);
-gboolean device_is_authorizing(struct btd_device *device);
-void device_set_authorizing(struct btd_device *device, gboolean auth);
void device_add_connection(struct btd_device *device, DBusConnection *conn);
void device_remove_connection(struct btd_device *device, DBusConnection *conn);
void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
--
1.7.7.6
^ permalink raw reply related
* [PATCH v0 0/4] Audio profile authorization
From: Mikel Astiz @ 2012-09-11 13:55 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
The agent-based profile authorization makes a special consideration for audio profiles: they are processed as if they all belonged to one single profile. This includes several internal policies that are inconvenient for IVI use-cases.
This patchset removes this exception by making use of the conventional authorization mechanism also for audio profiles.
The new approach is not straightforward since devices might send several connection requets in parallel (i.e. HFP, A2DP, AVRCP). This cannot be neither automatically rejected (EBUSY) nor forwarded in parallel to the agent, so a queue was added to store the pending authorization requests. These will be sent to the agent sequentially.
Mikel Astiz (4):
adapter: Replace device authorizing flag
adapter: Use authorization id for cancelling
adapter: Queue parallel authorization requests
audio: Drop audio-specific authorization mechanism
audio/avctp.c | 29 +++----
audio/avdtp.c | 25 ++++--
audio/device.c | 144 ------------------------------------
audio/device.h | 12 +---
audio/manager.c | 18 +++--
plugins/service.c | 18 +++--
profiles/input/server.c | 2 +-
src/adapter.c | 188 +++++++++++++++++++++++++++++------------------
src/adapter.h | 2 +-
src/device.c | 11 ---
src/device.h | 2 -
11 files changed, 170 insertions(+), 281 deletions(-)
--
1.7.7.6
^ permalink raw reply
* Re: [PATCH BlueZ v5 01/14] core: Control connections based on adapter state
From: Joao Paulo Rechi Vita @ 2012-09-11 13:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <20120907095932.GA7057@x220>
On Fri, Sep 7, 2012 at 6:59 AM, Johan Hedberg <johan.hedberg@gmail.com> wrote:
> Hi,
>
> On Tue, Sep 04, 2012, João Paulo Rechi Vita wrote:
>> +static void set_auto_connect(gpointer data, gpointer user_data)
>> +{
>> + struct btd_device *device = data;
>> + gboolean enable = GPOINTER_TO_INT(user_data);
>> +
>> + device_set_auto_connect(device, enable);
>> +}
>> +
>> static void call_adapter_powered_callbacks(struct btd_adapter *adapter,
>> gboolean powered)
>> {
>> @@ -2125,7 +2133,10 @@ 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,
>> + GINT_TO_POINTER(powered));
>
> Instead of this GINT_TO_POINTER magic on a variable that isn't even a
> gint couldn't you just pass &powered to g_slist_foreach and then in the
> callback do:
>
> gboolean enable = *(gboolean *) user_data;
>
> or
>
> gboolean *enable = user_data;
>
> device_set_auto_connect(device, *enable);
>
I'm sending an updated version now.
--
João Paulo Rechi Vita
Openbossa Labs - INdT
^ permalink raw reply
* [PATCH BlueZ v6 11/11] core: Suspend scanning before connect on pairing
From: João Paulo Rechi Vita @ 2012-09-11 13:33 UTC (permalink / raw)
To: linux-bluetooth; +Cc: João Paulo Rechi Vita
In-Reply-To: <1347370387-17670-1-git-send-email-jprvita@openbossa.org>
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
^ permalink raw reply related
* [PATCH BlueZ v6 10/11] mgmt: Add address type to bonding debug message
From: João Paulo Rechi Vita @ 2012-09-11 13:33 UTC (permalink / raw)
To: linux-bluetooth; +Cc: João Paulo Rechi Vita
In-Reply-To: <1347370387-17670-1-git-send-email-jprvita@openbossa.org>
---
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
^ permalink raw reply related
* [PATCH BlueZ v6 09/11] core: Re-connect for ECONNRESET or ECONNABORTED
From: João Paulo Rechi Vita @ 2012-09-11 13:33 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Claudio Takahasi
In-Reply-To: <1347370387-17670-1-git-send-email-jprvita@openbossa.org>
From: Claudio Takahasi <claudio.takahasi@openbossa.org>
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
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox