* [PATCH 09/19] Bluetooth: Move channel response
From: Mat Martineau @ 2012-10-15 15:33 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350315248-7690-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 6d3615e..b4c3c65 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -52,6 +52,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 ef744a9..c687cc1 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4221,6 +4221,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)
@@ -4317,6 +4324,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))
@@ -4327,8 +4335,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.3
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH 08/19] Bluetooth: Add state to hci_chan
From: Mat Martineau @ 2012-10-15 15:33 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350315248-7690-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 9fe8e2d..00abc52 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -355,6 +355,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 fe64621..6dcf452 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -959,6 +959,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.3
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH 07/19] Bluetooth: Add move channel confirm handling
From: Mat Martineau @ 2012-10-15 15:33 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350315248-7690-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 aab7f79..ef744a9 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1030,6 +1030,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 */
@@ -4297,11 +4333,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))
@@ -4312,8 +4349,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.3
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH 06/19] Bluetooth: Add new ERTM receive states for channel move
From: Mat Martineau @ 2012-10-15 15:33 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350315248-7690-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 649c06b..aab7f79 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -5207,6 +5207,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 */
@@ -5233,6 +5333,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.3
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH 05/19] Bluetooth: Channel move request handling
From: Mat Martineau @ 2012-10-15 15:33 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350315248-7690-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 a55644f..649c06b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -995,6 +995,41 @@ 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 */
@@ -4171,7 +4206,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.3
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH 04/19] Bluetooth: Lookup channel structure based on DCID
From: Mat Martineau @ 2012-10-15 15:33 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350315248-7690-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 ec2b4d9..a55644f 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -100,6 +100,22 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
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)
{
@@ -4139,6 +4155,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;
@@ -4152,9 +4169,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.3
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH 03/19] Bluetooth: Remove unnecessary intermediate function
From: Mat Martineau @ 2012-10-15 15:33 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350315248-7690-1-git-send-email-mathewm@codeaurora.org>
Resolves a conflict resolution issue in "Bluetooth: Fix L2CAP coding
style".
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
net/bluetooth/l2cap_core.c | 13 ++-----------
1 file changed, 2 insertions(+), 11 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 73ce337..ec2b4d9 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3537,7 +3537,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn,
return 0;
}
-static inline int l2cap_connect_rsp(struct l2cap_conn *conn,
+static 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;
@@ -4091,15 +4091,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)
{
@@ -4306,7 +4297,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
case L2CAP_CONN_RSP:
case L2CAP_CREATE_CHAN_RSP:
- err = l2cap_connect_rsp(conn, cmd, data);
+ err = l2cap_connect_create_rsp(conn, cmd, data);
break;
case L2CAP_CONF_REQ:
--
1.7.12.3
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH 02/19] Bluetooth: Add L2CAP create channel request handling
From: Mat Martineau @ 2012-10-15 15:33 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel, Andrei Emeltchenko
In-Reply-To: <1350315248-7690-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>
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
net/bluetooth/l2cap_core.c | 62 +++++++++++++++++++++++++++++++++++-----------
1 file changed, 47 insertions(+), 15 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index f8d78f5..73ce337 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3400,8 +3400,9 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
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;
@@ -3452,6 +3453,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;
__l2cap_chan_add(conn, chan);
@@ -3469,8 +3471,16 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
status = L2CAP_CS_AUTHOR_PEND;
chan->ops->defer(chan);
} 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 {
@@ -3516,6 +3526,8 @@ sendresp:
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
}
+
+ return chan;
}
static int l2cap_connect_req(struct l2cap_conn *conn,
@@ -4028,12 +4040,12 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn,
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))
@@ -4047,14 +4059,34 @@ 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 = __constant_cpu_to_le16(L2CAP_CR_BAD_AMP);
+ rsp.status = __constant_cpu_to_le16(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);
return 0;
}
--
1.7.12.3
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH 01/19] Bluetooth: Add new l2cap_chan struct members for high speed channels
From: Mat Martineau @ 2012-10-15 15:33 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel, Andrei Emeltchenko
In-Reply-To: <1350315248-7690-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>
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Acked-by: Marcel Holtmann <marcel@holtmann.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 6e23afd..6d3615e 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -434,6 +434,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;
@@ -477,6 +479,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;
@@ -644,6 +651,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 {
@@ -674,6 +684,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 f873619..f8d78f5 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2788,6 +2788,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.3
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH 00/19] L2CAP signaling for AMP channel create/move
From: Mat Martineau @ 2012-10-15 15:33 UTC (permalink / raw)
To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
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).
Changes:
* PATCHv1 - Rebased after AMP physical link patch set, incorporated
mailing list feedback.
* RFCv1 - Finished commit messages, fixed formatting/style issues
* RFCv0 - Initial post
Mat Martineau (19):
Bluetooth: Add new l2cap_chan struct members for high speed channels
Bluetooth: Add L2CAP create channel request handling
Bluetooth: Remove unnecessary intermediate function
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_core.h | 1 +
include/net/bluetooth/l2cap.h | 32 ++
net/bluetooth/hci_conn.c | 1 +
net/bluetooth/l2cap_core.c | 954 +++++++++++++++++++++++++++++++++++++--
net/bluetooth/l2cap_sock.c | 5 +
5 files changed, 956 insertions(+), 37 deletions(-)
--
1.7.12.3
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply
* Re: [PATCHv2 5/6] Bluetooth: AMP: Get amp_mgr reference in HS hci_conn
From: Gustavo Padovan @ 2012-10-15 15:27 UTC (permalink / raw)
To: Andrei Emeltchenko, linux-bluetooth
In-Reply-To: <20121015132114.GC2584@aemeltch-MOBL1>
Hi Andrei,
* Andrei Emeltchenko <Andrei.Emeltchenko.news@gmail.com> [2012-10-15 16:21:21 +0300]:
> Hi Gustavo,
>
> On Mon, Oct 15, 2012 at 09:49:03AM -0300, Gustavo Padovan wrote:
> > Hi Andrei,
> >
> > * Andrei Emeltchenko <Andrei.Emeltchenko.news@gmail.com> [2012-10-15 11:58:43 +0300]:
> >
> > > From: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> > >
> > > When assigning amp_mgr in hci_conn (type AMP_LINK) get also reference.
> > > In hci_conn_del those references would be put for both types.
> > >
> > > Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> > > Acked-by: Marcel Holtmann <marcel@holtmann.org>
> > > ---
> > > net/bluetooth/amp.c | 4 +++-
> > > 1 file changed, 3 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
> > > index 59da0f1..e525e23 100644
> > > --- a/net/bluetooth/amp.c
> > > +++ b/net/bluetooth/amp.c
> > > @@ -123,9 +123,11 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
> > > hcon->attempt++;
> > > hcon->handle = __next_handle(mgr);
> > > hcon->remote_id = remote_id;
> > > - hcon->amp_mgr = mgr;
> > > hcon->out = out;
> > >
> > > + hcon->amp_mgr = mgr;
> > > + amp_mgr_get(mgr);
> >
> > You did not take the comments from Marcel in this patch, please make the
> > change to amp_mgr_get() before sending this patch.
>
> I was thinking of making separate patch since this might be used in
> several places so I would change it once instead of splitting this change
> to several patches.
Sure, but just send that patch before this one.
Gustavo
^ permalink raw reply
* Is it incorrect in checking of "NO_RESET" bit in hci_core.c:hci_dev_do_close()?
From: chen kris @ 2012-10-15 15:01 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <CACKG3XmHwTgbsWBwsckZ-tA5M9xBD0TBRme7mCJo7shd90L4=Q@mail.gmail.com>
static int hci_dev_do_close(struct hci_dev *hdev)
{
...
/* Reset device */
skb_queue_purge(&hdev->cmd_q);
atomic_set(&hdev->cmd_cnt, 1);
if (!test_bit(HCI_RAW, &hdev->flags) &&
test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) {
set_bit(HCI_INIT, &hdev->flags);
__hci_request(hdev, hci_reset_req, 0,
msecs_to_jiffies(250));
clear_bit(HCI_INIT, &hdev->flags);
}
...
}
I guess the condition should be as following:
if (!test_bit(HCI_RAW, &hdev->flags) &&
*! *test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks))
it means the code should sent HCI_RESET, only if both of the following
conditions are met:
1. HCI_RAW is NOT set;
2. HCI_QUIRK_NO_RESET is *NOT* set.
thanks!
Bluefrog2012
^ permalink raw reply
* Re: [RFC obexd 02/11] build: Add --enable-fuse for gobexfuse
From: Luiz Augusto von Dentz @ 2012-10-15 14:43 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: Michał Poczwardowski, linux-bluetooth
In-Reply-To: <1349807669.27233.103.camel@aeonflux>
Hi Michal,
On Tue, Oct 9, 2012 at 8:34 PM, Marcel Holtmann <marcel@holtmann.org> wrote:
> Hi Michal,
>
>> Makefile.am | 9 +++++++++
>> configure.ac | 15 +++++++++++++++
>> 2 files changed, 24 insertions(+), 0 deletions(-)
>>
>> diff --git a/Makefile.am b/Makefile.am
>> index 724dd5d..95b99a6 100644
>> --- a/Makefile.am
>> +++ b/Makefile.am
>> @@ -132,6 +132,15 @@ client_obex_client_SOURCES = $(gdbus_sources) $(gobex_sources) \
>> client_obex_client_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@
>> endif
>>
>> +if FUSE
>> +bin_PROGRAMS = fuse/gobexfuse
>
> I rather call this obexfuse or similar. I don't want to have any "g"
> prefixes in the future anymore.
Are you looking into this? I will probably need more time to look in
detail what you have done, but at least the binary name should be
changed as Marcel suggested.
--
Luiz Augusto von Dentz
^ permalink raw reply
* Re: [PATCH BlueZ 04/16] AVRCP: Don't respond with errors when no player is registered
From: Johan Hedberg @ 2012-10-15 14:37 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <1350309936-31588-4-git-send-email-luiz.dentz@gmail.com>
Hi Luiz,
On Mon, Oct 15, 2012, Luiz Augusto von Dentz wrote:
> +static void *player_get_metadata(struct avrcp_player *player, uint32_t attr)
> +{
> + if (player != NULL)
> + return player->cb->get_metadata(attr, player->user_data);
> +
> + if (attr == AVRCP_MEDIA_ATTRIBUTE_TITLE)
> + return "";
This is quite messed up. Using void pointers like that just obfuscates
what the code is doing. After a quick look at current git it seems to me
like struct metadata_value from media.c should be made public and that
should be the return type of this function instead of doing void pointer
magic.
Johan
^ permalink raw reply
* [PATCH BlueZ 16/16] control: Add Control.Disconnect method
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1350309936-31588-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This method can be used to disconnect AVRCP.
---
audio/control.c | 31 +++++++++++++++++++++++++++++++
doc/control-api.txt | 4 ++++
2 files changed, 35 insertions(+)
diff --git a/audio/control.c b/audio/control.c
index e15e9ba..8bb085a 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -62,6 +62,7 @@ struct control {
struct avctp *session;
gboolean target;
DBusMessage *connect;
+ DBusMessage *disconnect;
};
static void state_changed(struct audio_device *dev, avctp_state_t old_state,
@@ -83,6 +84,13 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
control->connect = NULL;
}
+ if (control->disconnect) {
+ g_dbus_send_reply(conn, control->disconnect,
+ DBUS_TYPE_INVALID);
+ dbus_message_unref(control->disconnect);
+ control->disconnect = NULL;
+ }
+
if (old_state != AVCTP_STATE_CONNECTED)
break;
@@ -164,6 +172,25 @@ static DBusMessage *control_connect(DBusConnection *conn, DBusMessage *msg,
return NULL;
}
+static DBusMessage *control_disconnect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct control *control = device->control;
+
+ if (!control->session)
+ return btd_error_not_connected(msg);
+
+ if (control->disconnect)
+ return btd_error_in_progress(msg);
+
+ avctp_disconnect(control->session);
+
+ control->disconnect = dbus_message_ref(msg);
+
+ return NULL;
+}
+
static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
uint8_t op, void *data)
{
@@ -243,6 +270,7 @@ static const GDBusMethodTable control_methods[] = {
NULL, GDBUS_ARGS({ "connected", "b" }),
control_is_connected) },
{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, control_connect) },
+ { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, control_disconnect) },
{ GDBUS_METHOD("Play", NULL, NULL, control_play) },
{ GDBUS_METHOD("Pause", NULL, NULL, control_pause) },
{ GDBUS_METHOD("Stop", NULL, NULL, control_stop) },
@@ -278,6 +306,9 @@ static void path_unregister(void *data)
if (control->connect)
dbus_message_unref(control->connect);
+ if (control->disconnect)
+ dbus_message_unref(control->disconnect);
+
g_free(control);
dev->control = NULL;
}
diff --git a/doc/control-api.txt b/doc/control-api.txt
index 61069ea..5391a8a 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -25,6 +25,10 @@ Methods boolean IsConnected() {deprecated}
Connect to remote device.
+ void Disconnect()
+
+ Disconnect from remote device.
+
void Play()
Resume playback.
--
1.7.11.4
^ permalink raw reply related
* [PATCH BlueZ 15/16] control: Add Control.Connect API
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1350309936-31588-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This method can be used to manually connect AVRCP when acting as a
controller.
---
audio/control.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
doc/control-api.txt | 4 ++++
2 files changed, 49 insertions(+)
diff --git a/audio/control.c b/audio/control.c
index b77c4cb..e15e9ba 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -61,6 +61,7 @@ static unsigned int avctp_id = 0;
struct control {
struct avctp *session;
gboolean target;
+ DBusMessage *connect;
};
static void state_changed(struct audio_device *dev, avctp_state_t old_state,
@@ -73,6 +74,15 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
case AVCTP_STATE_DISCONNECTED:
control->session = NULL;
+ if (control->connect) {
+ DBusMessage *reply = btd_error_failed(control->connect,
+ "Unable to connect");
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ dbus_message_unref(control->connect);
+ control->connect = NULL;
+ }
+
if (old_state != AVCTP_STATE_CONNECTED)
break;
@@ -91,6 +101,13 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
break;
case AVCTP_STATE_CONNECTED:
+ if (control->connect) {
+ g_dbus_send_reply(conn, control->connect,
+ DBUS_TYPE_INVALID);
+ dbus_message_unref(control->connect);
+ control->connect = NULL;
+ }
+
g_dbus_emit_signal(conn, dev->path,
AUDIO_CONTROL_INTERFACE, "Connected",
DBUS_TYPE_INVALID);
@@ -123,6 +140,30 @@ static DBusMessage *control_is_connected(DBusConnection *conn,
return reply;
}
+static DBusMessage *control_connect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct control *control = device->control;
+
+ if (control->session)
+ return btd_error_already_connected(msg);
+
+ if (!control->target)
+ return btd_error_not_supported(msg);
+
+ if (control->connect)
+ return btd_error_in_progress(msg);
+
+ control->session = avctp_connect(&device->src, &device->dst);
+ if (!control->session)
+ return btd_error_failed(msg, "Unable to connect");
+
+ control->connect = dbus_message_ref(msg);
+
+ return NULL;
+}
+
static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
uint8_t op, void *data)
{
@@ -201,6 +242,7 @@ static const GDBusMethodTable control_methods[] = {
{ GDBUS_DEPRECATED_METHOD("IsConnected",
NULL, GDBUS_ARGS({ "connected", "b" }),
control_is_connected) },
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, control_connect) },
{ GDBUS_METHOD("Play", NULL, NULL, control_play) },
{ GDBUS_METHOD("Pause", NULL, NULL, control_pause) },
{ GDBUS_METHOD("Stop", NULL, NULL, control_stop) },
@@ -233,6 +275,9 @@ static void path_unregister(void *data)
if (control->session)
avctp_disconnect(control->session);
+ if (control->connect)
+ dbus_message_unref(control->connect);
+
g_free(control);
dev->control = NULL;
}
diff --git a/doc/control-api.txt b/doc/control-api.txt
index 3792dfa..61069ea 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -21,6 +21,10 @@ Methods boolean IsConnected() {deprecated}
Returns all properties for the interface. See the
properties section for available properties.
+ void Connect()
+
+ Connect to remote device.
+
void Play()
Resume playback.
--
1.7.11.4
^ permalink raw reply related
* [PATCH BlueZ 14/16] control: Add basic support for AVRCP 1.0 controller
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1350309936-31588-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
audio/control.c | 61 ++++++++++++++++++++++++++++++++++++++---------------
doc/control-api.txt | 24 +++++++++++++++++++++
2 files changed, 68 insertions(+), 17 deletions(-)
diff --git a/audio/control.c b/audio/control.c
index 926bdfb..b77c4cb 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -123,8 +123,8 @@ static DBusMessage *control_is_connected(DBusConnection *conn,
return reply;
}
-static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg,
- void *data)
+static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
+ uint8_t op, void *data)
{
struct audio_device *device = data;
struct control *control = device->control;
@@ -136,31 +136,53 @@ static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg,
if (!control->target)
return btd_error_not_supported(msg);
- err = avctp_send_passthrough(control->session, AVC_VOLUME_UP);
+ err = avctp_send_passthrough(control->session, op);
if (err < 0)
return btd_error_failed(msg, strerror(-err));
return dbus_message_new_method_return(msg);
}
-static DBusMessage *volume_down(DBusConnection *conn, DBusMessage *msg,
+static DBusMessage *control_volume_up(DBusConnection *conn, DBusMessage *msg,
void *data)
{
- struct audio_device *device = data;
- struct control *control = device->control;
- int err;
+ return key_pressed(conn, msg, AVC_VOLUME_UP, data);
+}
- if (!control->session)
- return btd_error_not_connected(msg);
+static DBusMessage *control_volume_down(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return key_pressed(conn, msg, AVC_VOLUME_DOWN, data);
+}
- if (!control->target)
- return btd_error_not_supported(msg);
+static DBusMessage *control_play(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return key_pressed(conn, msg, AVC_PLAY, data);
+}
- err = avctp_send_passthrough(control->session, AVC_VOLUME_DOWN);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
+static DBusMessage *control_pause(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return key_pressed(conn, msg, AVC_PAUSE, data);
+}
- return dbus_message_new_method_return(msg);
+static DBusMessage *control_stop(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return key_pressed(conn, msg, AVC_STOP, data);
+}
+
+static DBusMessage *control_next(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return key_pressed(conn, msg, AVC_FORWARD, data);
+}
+
+static DBusMessage *control_previous(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return key_pressed(conn, msg, AVC_BACKWARD, data);
}
static gboolean control_property_get_connected(
@@ -179,8 +201,13 @@ static const GDBusMethodTable control_methods[] = {
{ GDBUS_DEPRECATED_METHOD("IsConnected",
NULL, GDBUS_ARGS({ "connected", "b" }),
control_is_connected) },
- { GDBUS_METHOD("VolumeUp", NULL, NULL, volume_up) },
- { GDBUS_METHOD("VolumeDown", NULL, NULL, volume_down) },
+ { GDBUS_METHOD("Play", NULL, NULL, control_play) },
+ { GDBUS_METHOD("Pause", NULL, NULL, control_pause) },
+ { GDBUS_METHOD("Stop", NULL, NULL, control_stop) },
+ { GDBUS_METHOD("Next", NULL, NULL, control_next) },
+ { GDBUS_METHOD("Previous", NULL, NULL, control_previous) },
+ { GDBUS_METHOD("VolumeUp", NULL, NULL, control_volume_up) },
+ { GDBUS_METHOD("VolumeDown", NULL, NULL, control_volume_down) },
{ }
};
diff --git a/doc/control-api.txt b/doc/control-api.txt
index eacfbcd..3792dfa 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -21,6 +21,30 @@ Methods boolean IsConnected() {deprecated}
Returns all properties for the interface. See the
properties section for available properties.
+ void Play()
+
+ Resume playback.
+
+ void Pause()
+
+ Pause playback.
+
+ void Stop()
+
+ Stop playback.
+
+ void Next()
+
+ Next item.
+
+ void Previous()
+
+ Previous item.
+
+ void VolumeDown()
+
+ Adjust remote volume one step down
+
void VolumeUp()
Adjust remote volume one step up
--
1.7.11.4
^ permalink raw reply related
* [PATCH BlueZ 13/16] AVRCP: Add proper role init procedure
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1350309936-31588-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
audio/avrcp.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 103 insertions(+), 10 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 85dd241..8336831 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -104,6 +104,7 @@
#define CAP_EVENTS_SUPPORTED 0x03
#define AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH 5
+#define AVRCP_GET_CAPABILITIES_PARAM_LENGTH 1
#define AVRCP_FEATURE_CATEGORY_1 0x0001
#define AVRCP_FEATURE_CATEGORY_2 0x0002
@@ -215,7 +216,7 @@ static uint32_t company_ids[] = {
IEEEID_BTSIG,
};
-static void register_volume_notification(struct avrcp *session);
+static void register_notification(struct avrcp *session, uint8_t event);
static sdp_record_t *avrcp_ct_record(void)
{
@@ -1306,7 +1307,7 @@ static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
return NULL;
}
-static gboolean avrcp_handle_volume_changed(struct avctp *conn,
+static gboolean avrcp_handle_event(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
void *user_data)
@@ -1314,26 +1315,34 @@ static gboolean avrcp_handle_volume_changed(struct avctp *conn,
struct avrcp *session = user_data;
struct avrcp_player *player = session->player;
struct avrcp_header *pdu = (void *) operands;
+ uint8_t event;
uint8_t volume;
if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
return FALSE;
- volume = pdu->params[1] & 0x7F;
+ event = pdu->params[0];
- if (player)
- player->cb->set_volume(volume, session->dev,
+ switch (event) {
+ case AVRCP_EVENT_VOLUME_CHANGED:
+ volume = pdu->params[1] & 0x7F;
+
+ if (player)
+ player->cb->set_volume(volume, session->dev,
player->user_data);
+ break;
+ }
+
if (code == AVC_CTYPE_CHANGED) {
- register_volume_notification(session);
+ register_notification(session, event);
return FALSE;
}
return TRUE;
}
-static void register_volume_notification(struct avrcp *session)
+static void register_notification(struct avrcp *session, uint8_t event)
{
uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH];
struct avrcp_header *pdu = (void *) buf;
@@ -1344,14 +1353,89 @@ static void register_volume_notification(struct avrcp *session)
set_company_id(pdu->company_id, IEEEID_BTSIG);
pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- pdu->params[0] = AVRCP_EVENT_VOLUME_CHANGED;
+ pdu->params[0] = event;
pdu->params_len = htons(AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH);
length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
avctp_send_vendordep_req(session->conn, AVC_CTYPE_NOTIFY,
AVC_SUBUNIT_PANEL, buf, length,
- avrcp_handle_volume_changed, session);
+ avrcp_handle_event, session);
+}
+
+static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t count;
+
+ if (pdu->params[0] != CAP_EVENTS_SUPPORTED)
+ return FALSE;
+
+ count = pdu->params[1];
+
+ for (; count > 0; count--) {
+ uint8_t event = pdu->params[1 + count];
+
+ switch (event) {
+ case AVRCP_EVENT_STATUS_CHANGED:
+ case AVRCP_EVENT_TRACK_CHANGED:
+ register_notification(session, event);
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static void avrcp_get_capabilities(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_GET_CAPABILITIES_PARAM_LENGTH];
+ struct avrcp_header *pdu = (void *) buf;
+ uint8_t length;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_CAPABILITIES;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+ pdu->params[0] = CAP_EVENTS_SUPPORTED;
+ pdu->params_len = htons(AVRCP_GET_CAPABILITIES_PARAM_LENGTH);
+
+ length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, length,
+ avrcp_get_capabilities_resp,
+ session);
+}
+
+static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ return FALSE;
+}
+
+static void avrcp_get_play_status(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH];
+ struct avrcp_header *pdu = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_PLAY_STATUS;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_get_play_status_rsp,
+ session);
}
static struct avrcp *find_session(GSList *list, struct audio_device *dev)
@@ -1370,11 +1454,13 @@ static void session_tg_init(struct avrcp *session)
{
struct avrcp_server *server = session->server;
+ DBG("%p version 0x%04x", session, session->version);
+
session->player = g_slist_nth_data(server->players, 0);
session->control_handlers = tg_control_handlers;
if (session->version >= 0x0104) {
- register_volume_notification(session);
+ register_notification(session, AVRCP_EVENT_VOLUME_CHANGED);
if (session->features & AVRCP_FEATURE_BROWSING)
avctp_connect_browsing(session->conn);
}
@@ -1393,6 +1479,13 @@ static void session_ct_init(struct avrcp *session)
{
session->control_handlers = ct_control_handlers;
+ DBG("%p version 0x%04x", session, session->version);
+
+ if (session->version >= 0x0103) {
+ avrcp_get_capabilities(session);
+ avrcp_get_play_status(session);
+ }
+
session->control_id = avctp_register_pdu_handler(session->conn,
AVC_OP_VENDORDEP,
handle_vendordep_pdu,
--
1.7.11.4
^ permalink raw reply related
* [PATCH BlueZ 12/16] AVRCP: Fix handling TG PDUs as they were CT PDUs
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1350309936-31588-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
PDU handling needs to different depending on the acting role.
---
audio/avrcp.c | 81 +++++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 57 insertions(+), 24 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 8abb426..85dd241 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -188,6 +188,10 @@ struct avrcp {
uint16_t version;
int features;
+ void (*init) (struct avrcp *session);
+
+ const struct control_pdu_handler *control_handlers;
+
unsigned int control_id;
unsigned int browsing_id;
uint16_t registered_events;
@@ -196,6 +200,13 @@ struct avrcp {
struct pending_pdu *pending_pdu;
};
+struct control_pdu_handler {
+ uint8_t pdu_id;
+ uint8_t code;
+ uint8_t (*func) (struct avrcp *session, struct avrcp_header *pdu,
+ uint8_t transaction);
+};
+
static GSList *servers = NULL;
static unsigned int avctp_id = 0;
@@ -1128,12 +1139,7 @@ err:
return AVC_CTYPE_REJECTED;
}
-static struct control_pdu_handler {
- uint8_t pdu_id;
- uint8_t code;
- uint8_t (*func) (struct avrcp *session, struct avrcp_header *pdu,
- uint8_t transaction);
-} control_handlers[] = {
+static const struct control_pdu_handler tg_control_handlers[] = {
{ AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
avrcp_handle_get_capabilities },
{ AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS,
@@ -1165,6 +1171,10 @@ static struct control_pdu_handler {
{ },
};
+static const struct control_pdu_handler ct_control_handlers[] = {
+ { },
+};
+
/* handle vendordep pdu inside an avctp packet */
static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
uint8_t *code, uint8_t *subunit,
@@ -1172,7 +1182,7 @@ static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
void *user_data)
{
struct avrcp *session = user_data;
- struct control_pdu_handler *handler;
+ const struct control_pdu_handler *handler;
struct avrcp_header *pdu = (void *) operands;
uint32_t company_id = get_company_id(pdu->company_id);
@@ -1192,7 +1202,7 @@ static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
goto err_metadata;
}
- for (handler = control_handlers; handler->pdu_id; handler++) {
+ for (handler = session->control_handlers; handler->pdu_id; handler++) {
if (handler->pdu_id == pdu->pdu_id)
break;
}
@@ -1356,6 +1366,39 @@ static struct avrcp *find_session(GSList *list, struct audio_device *dev)
return NULL;
}
+static void session_tg_init(struct avrcp *session)
+{
+ struct avrcp_server *server = session->server;
+
+ session->player = g_slist_nth_data(server->players, 0);
+ session->control_handlers = tg_control_handlers;
+
+ if (session->version >= 0x0104) {
+ register_volume_notification(session);
+ if (session->features & AVRCP_FEATURE_BROWSING)
+ avctp_connect_browsing(session->conn);
+ }
+
+ session->control_id = avctp_register_pdu_handler(session->conn,
+ AVC_OP_VENDORDEP,
+ handle_vendordep_pdu,
+ session);
+ session->browsing_id = avctp_register_browsing_pdu_handler(
+ session->conn,
+ handle_browsing_pdu,
+ session);
+}
+
+static void session_ct_init(struct avrcp *session)
+{
+ session->control_handlers = ct_control_handlers;
+
+ session->control_id = avctp_register_pdu_handler(session->conn,
+ AVC_OP_VENDORDEP,
+ handle_vendordep_pdu,
+ session);
+}
+
static struct avrcp *session_create(struct avrcp_server *server,
struct audio_device *dev)
{
@@ -1368,7 +1411,6 @@ static struct avrcp *session_create(struct avrcp_server *server,
session->server = server;
session->conn = avctp_connect(&dev->src, &dev->dst);
session->dev = dev;
- session->player = g_slist_nth_data(server->players, 0);
server->sessions = g_slist_append(server->sessions, session);
@@ -1381,10 +1423,13 @@ static struct avrcp *session_create(struct avrcp_server *server,
else
session->target = FALSE;
- if (session->target)
+ if (session->target) {
+ session->init = session_tg_init;
rec = btd_device_get_record(dev->btd_dev, AVRCP_REMOTE_UUID);
- else
+ } else {
+ session->init = session_ct_init;
rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
+ }
if (rec == NULL)
return session;
@@ -1451,20 +1496,8 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
if (session == NULL)
break;
- if (session->version >= 0x0104) {
- register_volume_notification(session);
- if (session->features & AVRCP_FEATURE_BROWSING)
- avctp_connect_browsing(session->conn);
- }
+ session->init(session);
- session->control_id = avctp_register_pdu_handler(session->conn,
- AVC_OP_VENDORDEP,
- handle_vendordep_pdu,
- session);
- session->browsing_id = avctp_register_browsing_pdu_handler(
- session->conn,
- handle_browsing_pdu,
- session);
default:
return;
}
--
1.7.11.4
^ permalink raw reply related
* [PATCH BlueZ 11/16] AVRCP: Fix reading wrong profile when acting as a controller
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1350309936-31588-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
The role indicate which record should be read so we properly determine
the version and what features are supported.
---
audio/avrcp.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 7188039..8abb426 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -58,6 +58,9 @@
#include "avrcp.h"
#include "sdpd.h"
#include "dbus-common.h"
+#include "control.h"
+#include "avdtp.h"
+#include "sink.h"
/* Company IDs for vendor dependent commands */
#define IEEEID_BTSIG 0x001958
@@ -181,6 +184,7 @@ struct avrcp {
struct avctp *conn;
struct audio_device *dev;
struct avrcp_player *player;
+ gboolean target;
uint16_t version;
int features;
@@ -1368,7 +1372,20 @@ static struct avrcp *session_create(struct avrcp_server *server,
server->sessions = g_slist_append(server->sessions, session);
- rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
+ if (dev->sink && !dev->source)
+ session->target = TRUE;
+ else if (dev->source && !dev->sink)
+ session->target = FALSE;
+ else if (dev->sink && sink_is_active(dev))
+ session->target = TRUE;
+ else
+ session->target = FALSE;
+
+ if (session->target)
+ rec = btd_device_get_record(dev->btd_dev, AVRCP_REMOTE_UUID);
+ else
+ rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
+
if (rec == NULL)
return session;
--
1.7.11.4
^ permalink raw reply related
* [PATCH BlueZ 10/16] AVCTP: Add proper queueing for channels
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1350309936-31588-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Add a request queue to channels to avoid dispatching too many requests
at once as the number of transaction is quite limited (16).
---
audio/avctp.c | 343 +++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 245 insertions(+), 98 deletions(-)
diff --git a/audio/avctp.c b/audio/avctp.c
index b982a3b..0db6031 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -125,19 +125,40 @@ struct avctp_server {
GSList *sessions;
};
-struct avctp_rsp_handler {
- uint8_t id;
+struct avctp_control_req {
+ struct avctp_pending_req *p;
+ uint8_t code;
+ uint8_t subunit;
+ uint8_t op;
+ uint8_t *operands;
+ uint16_t operand_count;
avctp_rsp_cb func;
void *user_data;
};
+typedef int (*avctp_process_cb) (void *data);
+
+struct avctp_pending_req {
+ struct avctp_channel *chan;
+ uint8_t transaction;
+ guint timeout;
+ avctp_process_cb process;
+ void *data;
+ GDestroyNotify destroy;
+};
+
struct avctp_channel {
+ struct avctp *session;
GIOChannel *io;
guint watch;
uint16_t imtu;
uint16_t omtu;
uint8_t *buffer;
GSList *handlers;
+ struct avctp_pending_req *p;
+ GQueue *queue;
+ GSList *processed;
+ guint process_id;
};
struct avctp {
@@ -157,7 +178,6 @@ struct avctp {
struct avctp_channel *browsing;
uint8_t key_quirks[256];
- GSList *handlers;
};
struct avctp_pdu_handler {
@@ -190,9 +210,9 @@ static struct {
static GSList *callbacks = NULL;
static GSList *servers = NULL;
-static uint8_t id = 0;
static void auth_cb(DBusError *derr, void *user_data);
+static gboolean process_queue(gpointer user_data);
static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
{
@@ -339,6 +359,19 @@ static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode)
return NULL;
}
+static void pending_destroy(void *data)
+{
+ struct avctp_pending_req *req = data;
+
+ if (req->destroy)
+ req->destroy(req->data);
+
+ if (req->timeout > 0)
+ g_source_remove(req->timeout);
+
+ g_free(req);
+}
+
static void avctp_channel_destroy(struct avctp_channel *chan)
{
g_io_channel_shutdown(chan->io, TRUE, NULL);
@@ -347,7 +380,12 @@ static void avctp_channel_destroy(struct avctp_channel *chan)
if (chan->watch)
g_source_remove(chan->watch);
+ if (chan->process_id > 0)
+ g_source_remove(chan->process_id);
+
g_free(chan->buffer);
+ g_queue_free_full(chan->queue, pending_destroy);
+ g_slist_free_full(chan->processed, pending_destroy);
g_slist_free_full(chan->handlers, g_free);
g_free(chan);
}
@@ -383,7 +421,6 @@ static void avctp_disconnected(struct avctp *session)
server = session->server;
server->sessions = g_slist_remove(server->sessions, session);
- g_slist_free_full(session->handlers, g_free);
g_free(session);
}
@@ -423,26 +460,157 @@ static void avctp_set_state(struct avctp *session, avctp_state_t new_state)
}
}
-static void control_response(struct avctp *session, struct avctp_header *avctp,
- struct avc_header *avc, uint8_t *operands,
- size_t operand_count)
+static int avctp_send(struct avctp_channel *control, uint8_t transaction,
+ uint8_t cr, uint8_t code,
+ uint8_t subunit, uint8_t opcode,
+ uint8_t *operands, size_t operand_count)
+{
+ struct avctp_header *avctp;
+ struct avc_header *avc;
+ struct msghdr msg;
+ struct iovec iov[2];
+ int sk, err = 0;
+
+ DBG("transaction %u", transaction);
+
+ iov[0].iov_base = control->buffer;
+ iov[0].iov_len = sizeof(*avctp) + sizeof(*avc);
+ iov[1].iov_base = operands;
+ iov[1].iov_len = operand_count;
+
+ if (control->omtu < (iov[0].iov_len + iov[1].iov_len))
+ return -EOVERFLOW;
+
+ sk = g_io_channel_unix_get_fd(control->io);
+
+ memset(control->buffer, 0, iov[0].iov_len);
+
+ avctp = (void *) control->buffer;
+ avc = (void *) avctp + sizeof(*avctp);
+
+ avctp->transaction = transaction;
+ avctp->packet_type = AVCTP_PACKET_SINGLE;
+ avctp->cr = cr;
+ avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+ avc->code = code;
+ avc->subunit_type = subunit;
+ avc->opcode = opcode;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+
+ if (sendmsg(sk, &msg, 0) < 0)
+ err = -errno;
+
+ return err;
+}
+
+static void control_req_destroy(void *data)
+{
+ struct avctp_control_req *req = data;
+
+ g_free(req->operands);
+ g_free(req);
+}
+
+static gboolean req_timeout(gpointer user_data)
+{
+ struct avctp_channel *chan = user_data;
+ struct avctp_pending_req *p = chan->p;
+
+ DBG("transaction %u", p->transaction);
+
+ p->timeout = 0;
+
+ pending_destroy(p);
+ chan->p = NULL;
+
+ if (chan->process_id == 0)
+ chan->process_id = g_idle_add(process_queue, chan);
+
+ return FALSE;
+}
+
+static int process_control(void *data)
+{
+ struct avctp_control_req *req = data;
+ struct avctp_pending_req *p = req->p;
+
+ return avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code,
+ req->subunit, req->op,
+ req->operands, req->operand_count);
+}
+
+static gboolean process_queue(void *user_data)
+{
+ struct avctp_channel *chan = user_data;
+ struct avctp_pending_req *p = chan->p;
+
+ chan->process_id = 0;
+
+ if (p != NULL)
+ return FALSE;
+
+ while ((p = g_queue_pop_head(chan->queue))) {
+
+ if (p->process(p->data) == 0)
+ break;
+
+ pending_destroy(p);
+ }
+
+ if (p == NULL)
+ return FALSE;
+
+ chan->p = p;
+ p->timeout = g_timeout_add_seconds(2, req_timeout, chan);
+
+ return FALSE;
+
+}
+
+static void control_response(struct avctp_channel *control,
+ struct avctp_header *avctp,
+ struct avc_header *avc,
+ uint8_t *operands,
+ size_t operand_count)
{
+ struct avctp_pending_req *p = control->p;
+ struct avctp_control_req *req = p->data;
GSList *l;
- for (l = session->handlers; l; l = l->next) {
- struct avctp_rsp_handler *handler = l->data;
+ if (p && p->transaction == avctp->transaction) {
+ control->processed = g_slist_prepend(control->processed, p);
+
+ if (p->timeout > 0) {
+ g_source_remove(p->timeout);
+ p->timeout = 0;
+ }
+
+ control->p = NULL;
+
+ if (control->process_id == 0)
+ control->process_id = g_idle_add(process_queue,
+ control);
+ }
+
+ for (l = control->processed; l; l = l->next) {
+ p = l->data;
+ req = p->data;
- if (handler->id != avctp->transaction)
+ if (p->transaction != avctp->transaction)
continue;
- if (handler->func && handler->func(session, avc->code,
+ if (req->func && req->func(control->session, avc->code,
avc->subunit_type,
operands, operand_count,
- handler->user_data))
+ req->user_data))
return;
- session->handlers = g_slist_remove(session->handlers, handler);
- g_free(handler);
+ control->processed = g_slist_remove(control->processed, p);
+ pending_destroy(p);
return;
}
@@ -564,7 +732,7 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
avc->opcode, operand_count);
if (avctp->cr == AVCTP_RESPONSE) {
- control_response(session, avctp, avc, operands, operand_count);
+ control_response(control, avctp, avc, operands, operand_count);
return TRUE;
}
@@ -692,12 +860,15 @@ static void init_uinput(struct avctp *session)
DBG("AVRCP: uinput initialized for %s", address);
}
-static struct avctp_channel *avctp_channel_create(GIOChannel *io)
+static struct avctp_channel *avctp_channel_create(struct avctp *session,
+ GIOChannel *io)
{
struct avctp_channel *chan;
chan = g_new0(struct avctp_channel, 1);
+ chan->session = session;
chan->io = g_io_channel_ref(io);
+ chan->queue = g_queue_new();
return chan;
}
@@ -731,7 +902,7 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
DBG("AVCTP Browsing: connected to %s", address);
if (session->browsing == NULL)
- session->browsing = avctp_channel_create(chan);
+ session->browsing = avctp_channel_create(session, chan);
session->browsing->imtu = imtu;
session->browsing->omtu = omtu;
@@ -777,7 +948,7 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
DBG("AVCTP: connected to %s", address);
if (session->control == NULL)
- session->control = avctp_channel_create(chan);
+ session->control = avctp_channel_create(session, chan);
session->control->imtu = imtu;
session->control->omtu = omtu;
@@ -894,7 +1065,7 @@ static void avctp_control_confirm(struct avctp *session, GIOChannel *chan,
}
avctp_set_state(session, AVCTP_STATE_CONNECTING);
- session->control = avctp_channel_create(chan);
+ session->control = avctp_channel_create(session, chan);
session->auth_id = btd_request_authorization(&dev->src, &dev->dst,
AVRCP_TARGET_UUID,
@@ -1066,52 +1237,25 @@ void avctp_unregister(const bdaddr_t *src)
g_free(server);
}
-static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
- uint8_t code, uint8_t subunit, uint8_t opcode,
- uint8_t *operands, size_t operand_count)
+static struct avctp_pending_req *pending_create(struct avctp_channel *chan,
+ avctp_process_cb process,
+ void *data,
+ GDestroyNotify destroy)
{
- struct avctp_channel *control = session->control;
- struct avctp_header *avctp;
- struct avc_header *avc;
- struct msghdr msg;
- struct iovec iov[2];
- int sk, err = 0;
-
- if (session->state != AVCTP_STATE_CONNECTED)
- return -ENOTCONN;
-
- iov[0].iov_base = control->buffer;
- iov[0].iov_len = sizeof(*avctp) + sizeof(*avc);
- iov[1].iov_base = operands;
- iov[1].iov_len = operand_count;
-
- if (control->omtu < (iov[0].iov_len + iov[1].iov_len))
- return -EOVERFLOW;
-
- sk = g_io_channel_unix_get_fd(session->control->io);
-
- memset(control->buffer, 0, iov[0].iov_len);
-
- avctp = (void *) control->buffer;
- avc = (void *) avctp + sizeof(*avctp);
-
- avctp->transaction = transaction;
- avctp->packet_type = AVCTP_PACKET_SINGLE;
- avctp->cr = cr;
- avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
-
- avc->code = code;
- avc->subunit_type = subunit;
- avc->opcode = opcode;
+ struct avctp_pending_req *p;
+ static uint8_t transaction = 0;
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = iov;
- msg.msg_iovlen = 2;
+ p = g_new0(struct avctp_pending_req, 1);
+ p->chan = chan;
+ p->transaction = transaction;
+ p->process = process;
+ p->data = data;
+ p->destroy = destroy;
- if (sendmsg(sk, &msg, 0) < 0)
- err = -errno;
+ transaction++;
+ transaction %= 16;
- return err;
+ return p;
}
static int avctp_send_req(struct avctp *session, uint8_t code,
@@ -1119,22 +1263,30 @@ static int avctp_send_req(struct avctp *session, uint8_t code,
uint8_t *operands, size_t operand_count,
avctp_rsp_cb func, void *user_data)
{
- struct avctp_rsp_handler *handler;
- int err;
+ struct avctp_channel *control = session->control;
+ struct avctp_pending_req *p;
+ struct avctp_control_req *req;
- err = avctp_send(session, id, AVCTP_COMMAND, code, subunit,
- opcode, operands, operand_count);
- if (err < 0)
- return err;
+ if (control == NULL)
+ return -ENOTCONN;
- handler = g_new0(struct avctp_rsp_handler, 1);
- handler->id = id;
- handler->func = func;
- handler->user_data = user_data;
+ req = g_new0(struct avctp_control_req, 1);
+ req->code = code;
+ req->subunit = subunit;
+ req->op = opcode;
+ req->func = func;
+ req->operands = g_memdup(operands, operand_count);
+ req->operand_count = operand_count;
+ req->user_data = user_data;
+
+ p = pending_create(control, process_control, req, control_req_destroy);
+
+ req->p = p;
- session->handlers = g_slist_prepend(session->handlers, handler);
+ g_queue_push_tail(control->queue, p);
- id++;
+ if (control->process_id == 0)
+ control->process_id = g_idle_add(process_queue, control);
return 0;
}
@@ -1143,15 +1295,18 @@ static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
uint8_t subunit, uint8_t *operands,
size_t operand_count, void *user_data)
{
+ DBG("code %u", code);
+
if (code != AVC_CTYPE_ACCEPTED)
return FALSE;
/* Button release */
operands[0] |= 0x80;
- avctp_send(session, id, AVCTP_COMMAND, AVC_CTYPE_CONTROL,
- AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
- operands, sizeof(operand_count));
+ avctp_send_req(session, AVC_CTYPE_CONTROL,
+ AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+ operands, operand_count,
+ NULL, NULL);
return FALSE;
}
@@ -1160,6 +1315,8 @@ int avctp_send_passthrough(struct avctp *session, uint8_t op)
{
uint8_t operands[2];
+ DBG("");
+
/* Button pressed */
operands[0] = op & 0x7f;
operands[1] = 0;
@@ -1174,7 +1331,12 @@ int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count)
{
- return avctp_send(session, transaction, AVCTP_RESPONSE, code, subunit,
+ struct avctp_channel *control = session->control;
+
+ if (control == NULL)
+ return -ENOTCONN;
+
+ return avctp_send(control, transaction, AVCTP_RESPONSE, code, subunit,
AVC_OP_VENDORDEP, operands, operand_count);
}
@@ -1183,24 +1345,9 @@ int avctp_send_vendordep_req(struct avctp *session, uint8_t code,
size_t operand_count,
avctp_rsp_cb func, void *user_data)
{
- struct avctp_rsp_handler *handler;
- int err;
-
- err = avctp_send(session, id, AVCTP_COMMAND, code, subunit,
- AVC_OP_VENDORDEP, operands, operand_count);
- if (err < 0)
- return err;
-
- handler = g_new0(struct avctp_rsp_handler, 1);
- handler->id = id;
- handler->func = func;
- handler->user_data = user_data;
-
- session->handlers = g_slist_prepend(session->handlers, handler);
-
- id++;
-
- return 0;
+ return avctp_send_req(session, code, subunit, AVC_OP_VENDORDEP,
+ operands, operand_count,
+ func, user_data);
}
unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data)
@@ -1380,7 +1527,7 @@ struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst)
return NULL;
}
- session->control = avctp_channel_create(io);
+ session->control = avctp_channel_create(session, io);
g_io_channel_unref(io);
return session;
@@ -1409,7 +1556,7 @@ int avctp_connect_browsing(struct avctp *session)
return -EIO;
}
- session->browsing = avctp_channel_create(io);
+ session->browsing = avctp_channel_create(session, io);
g_io_channel_unref(io);
return 0;
--
1.7.11.4
^ permalink raw reply related
* [PATCH BlueZ 09/16] AVCTP: Add proper prefix to AVC passthrough codes
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1350309936-31588-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
audio/avctp.c | 22 +++++++++++-----------
audio/avctp.h | 24 ++++++++++++------------
audio/control.c | 4 ++--
3 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/audio/avctp.c b/audio/avctp.c
index c72d2d5..b982a3b 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -178,13 +178,13 @@ static struct {
uint8_t avc;
uint16_t uinput;
} key_map[] = {
- { "PLAY", PLAY_OP, KEY_PLAYCD },
- { "STOP", STAVC_OP_OP, KEY_STOPCD },
- { "PAUSE", PAUSE_OP, KEY_PAUSECD },
- { "FORWARD", FORWARD_OP, KEY_NEXTSONG },
- { "BACKWARD", BACKWARD_OP, KEY_PREVIOUSSONG },
- { "REWIND", REWIND_OP, KEY_REWIND },
- { "FAST FORWARD", FAST_FORWARD_OP, KEY_FASTFORWARD },
+ { "PLAY", AVC_PLAY, KEY_PLAYCD },
+ { "STOP", AVC_STOP, KEY_STOPCD },
+ { "PAUSE", AVC_PAUSE, KEY_PAUSECD },
+ { "FORWARD", AVC_FORWARD, KEY_NEXTSONG },
+ { "BACKWARD", AVC_BACKWARD, KEY_PREVIOUSSONG },
+ { "REWIND", AVC_REWIND, KEY_REWIND },
+ { "FAST FORWARD", AVC_FAST_FORWARD, KEY_FASTFORWARD },
{ NULL }
};
@@ -677,10 +677,10 @@ static void init_uinput(struct avctp *session)
device_get_name(dev->btd_dev, name, sizeof(name));
if (g_str_equal(name, "Nokia CK-20W")) {
- session->key_quirks[FORWARD_OP] |= QUIRK_NO_RELEASE;
- session->key_quirks[BACKWARD_OP] |= QUIRK_NO_RELEASE;
- session->key_quirks[PLAY_OP] |= QUIRK_NO_RELEASE;
- session->key_quirks[PAUSE_OP] |= QUIRK_NO_RELEASE;
+ session->key_quirks[AVC_FORWARD] |= QUIRK_NO_RELEASE;
+ session->key_quirks[AVC_BACKWARD] |= QUIRK_NO_RELEASE;
+ session->key_quirks[AVC_PLAY] |= QUIRK_NO_RELEASE;
+ session->key_quirks[AVC_PAUSE] |= QUIRK_NO_RELEASE;
}
ba2str(&session->dst, address);
diff --git a/audio/avctp.h b/audio/avctp.h
index 41cf6c5..c00a3fc 100644
--- a/audio/avctp.h
+++ b/audio/avctp.h
@@ -49,18 +49,18 @@
#define AVC_SUBUNIT_PANEL 0x09
/* operands in passthrough commands */
-#define VOL_UP_OP 0x41
-#define VOL_DOWN_OP 0x42
-#define MUTE_OP 0x43
-#define PLAY_OP 0x44
-#define STAVC_OP_OP 0x45
-#define PAUSE_OP 0x46
-#define RECORD_OP 0x47
-#define REWIND_OP 0x48
-#define FAST_FORWARD_OP 0x49
-#define EJECT_OP 0x4a
-#define FORWARD_OP 0x4b
-#define BACKWARD_OP 0x4c
+#define AVC_VOLUME_UP 0x41
+#define AVC_VOLUME_DOWN 0x42
+#define AVC_MUTE 0x43
+#define AVC_PLAY 0x44
+#define AVC_STOP 0x45
+#define AVC_PAUSE 0x46
+#define AVC_RECORD 0x47
+#define AVC_REWIND 0x48
+#define AVC_FAST_FORWARD 0x49
+#define AVC_EJECT 0x4a
+#define AVC_FORWARD 0x4b
+#define AVC_BACKWARD 0x4c
struct avctp;
diff --git a/audio/control.c b/audio/control.c
index 5ec8ca3..926bdfb 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -136,7 +136,7 @@ static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg,
if (!control->target)
return btd_error_not_supported(msg);
- err = avctp_send_passthrough(control->session, VOL_UP_OP);
+ err = avctp_send_passthrough(control->session, AVC_VOLUME_UP);
if (err < 0)
return btd_error_failed(msg, strerror(-err));
@@ -156,7 +156,7 @@ static DBusMessage *volume_down(DBusConnection *conn, DBusMessage *msg,
if (!control->target)
return btd_error_not_supported(msg);
- err = avctp_send_passthrough(control->session, VOL_DOWN_OP);
+ err = avctp_send_passthrough(control->session, AVC_VOLUME_DOWN);
if (err < 0)
return btd_error_failed(msg, strerror(-err));
--
1.7.11.4
^ permalink raw reply related
* [PATCH BlueZ 08/16] AVCTP: Wait confirmation to send button release
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1350309936-31588-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
audio/avctp.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 46 insertions(+), 12 deletions(-)
diff --git a/audio/avctp.c b/audio/avctp.c
index 9a62bc0..c72d2d5 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -1114,26 +1114,60 @@ static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
return err;
}
-int avctp_send_passthrough(struct avctp *session, uint8_t op)
+static int avctp_send_req(struct avctp *session, uint8_t code,
+ uint8_t subunit, uint8_t opcode,
+ uint8_t *operands, size_t operand_count,
+ avctp_rsp_cb func, void *user_data)
{
- uint8_t operands[2];
- int ret;
+ struct avctp_rsp_handler *handler;
+ int err;
- operands[0] = op & 0x7f;
- operands[1] = 0;
+ err = avctp_send(session, id, AVCTP_COMMAND, code, subunit,
+ opcode, operands, operand_count);
+ if (err < 0)
+ return err;
- ret = avctp_send(session, id, AVCTP_COMMAND, AVC_CTYPE_CONTROL,
- AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
- operands, sizeof(operands));
- if (ret < 0)
- return ret;
+ handler = g_new0(struct avctp_rsp_handler, 1);
+ handler->id = id;
+ handler->func = func;
+ handler->user_data = user_data;
+
+ session->handlers = g_slist_prepend(session->handlers, handler);
+
+ id++;
+
+ return 0;
+}
+
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+ uint8_t subunit, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ if (code != AVC_CTYPE_ACCEPTED)
+ return FALSE;
/* Button release */
operands[0] |= 0x80;
- return avctp_send(session, id, AVCTP_COMMAND, AVC_CTYPE_CONTROL,
+ avctp_send(session, id, AVCTP_COMMAND, AVC_CTYPE_CONTROL,
AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
- operands, sizeof(operands));
+ operands, sizeof(operand_count));
+
+ return FALSE;
+}
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op)
+{
+ uint8_t operands[2];
+
+ /* Button pressed */
+ operands[0] = op & 0x7f;
+ operands[1] = 0;
+
+ return avctp_send_req(session, AVC_CTYPE_CONTROL,
+ AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+ operands, sizeof(operands),
+ avctp_passthrough_rsp, NULL);
}
int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
--
1.7.11.4
^ permalink raw reply related
* [PATCH BlueZ 07/16] AVCTP: Make use of allocate buffer to send data
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1350309936-31588-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
There is no need to use the stack as the channel now have proper buffer
which can be used to store data to be send.
In addition to that make avctp_send_passthrough to use avctp_send.
---
audio/avctp.c | 83 ++++++++++++++++++++++++-----------------------------------
1 file changed, 34 insertions(+), 49 deletions(-)
diff --git a/audio/avctp.c b/audio/avctp.c
index 228d5b7..9a62bc0 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -1066,51 +1066,11 @@ void avctp_unregister(const bdaddr_t *src)
g_free(server);
}
-int avctp_send_passthrough(struct avctp *session, uint8_t op)
-{
- unsigned char buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH + 2];
- struct avctp_header *avctp = (void *) buf;
- struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH];
- uint8_t *operands = &buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH];
- int sk;
-
- if (session->state != AVCTP_STATE_CONNECTED)
- return -ENOTCONN;
-
- memset(buf, 0, sizeof(buf));
-
- avctp->transaction = id++;
- avctp->packet_type = AVCTP_PACKET_SINGLE;
- avctp->cr = AVCTP_COMMAND;
- avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
-
- avc->code = AVC_CTYPE_CONTROL;
- avc->subunit_type = AVC_SUBUNIT_PANEL;
- avc->opcode = AVC_OP_PASSTHROUGH;
-
- operands[0] = op & 0x7f;
- operands[1] = 0;
-
- sk = g_io_channel_unix_get_fd(session->control->io);
-
- if (write(sk, buf, sizeof(buf)) < 0)
- return -errno;
-
- /* Button release */
- avctp->transaction = id++;
- operands[0] |= 0x80;
-
- if (write(sk, buf, sizeof(buf)) < 0)
- return -errno;
-
- return 0;
-}
-
static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
uint8_t code, uint8_t subunit, uint8_t opcode,
uint8_t *operands, size_t operand_count)
{
- uint8_t buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH];
+ struct avctp_channel *control = session->control;
struct avctp_header *avctp;
struct avc_header *avc;
struct msghdr msg;
@@ -1120,12 +1080,20 @@ static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
if (session->state != AVCTP_STATE_CONNECTED)
return -ENOTCONN;
+ iov[0].iov_base = control->buffer;
+ iov[0].iov_len = sizeof(*avctp) + sizeof(*avc);
+ iov[1].iov_base = operands;
+ iov[1].iov_len = operand_count;
+
+ if (control->omtu < (iov[0].iov_len + iov[1].iov_len))
+ return -EOVERFLOW;
+
sk = g_io_channel_unix_get_fd(session->control->io);
- memset(buf, 0, sizeof(buf));
+ memset(control->buffer, 0, iov[0].iov_len);
- avctp = (void *) buf;
- avc = (void *) &buf[AVCTP_HEADER_LENGTH];
+ avctp = (void *) control->buffer;
+ avc = (void *) avctp + sizeof(*avctp);
avctp->transaction = transaction;
avctp->packet_type = AVCTP_PACKET_SINGLE;
@@ -1136,11 +1104,6 @@ static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
avc->subunit_type = subunit;
avc->opcode = opcode;
- iov[0].iov_base = buf;
- iov[0].iov_len = sizeof(buf);
- iov[1].iov_base = operands;
- iov[1].iov_len = operand_count;
-
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 2;
@@ -1151,6 +1114,28 @@ static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
return err;
}
+int avctp_send_passthrough(struct avctp *session, uint8_t op)
+{
+ uint8_t operands[2];
+ int ret;
+
+ operands[0] = op & 0x7f;
+ operands[1] = 0;
+
+ ret = avctp_send(session, id, AVCTP_COMMAND, AVC_CTYPE_CONTROL,
+ AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+ operands, sizeof(operands));
+ if (ret < 0)
+ return ret;
+
+ /* Button release */
+ operands[0] |= 0x80;
+
+ return avctp_send(session, id, AVCTP_COMMAND, AVC_CTYPE_CONTROL,
+ AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+ operands, sizeof(operands));
+}
+
int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count)
--
1.7.11.4
^ permalink raw reply related
* [PATCH BlueZ 06/16] AVRCP: Simplify state_changed callback
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1350309936-31588-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Move session creation and destroy to their own functions
---
audio/avrcp.c | 91 ++++++++++++++++++++++++++++++++++-------------------------
1 file changed, 53 insertions(+), 38 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index f31372b..7188039 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -1352,15 +1352,63 @@ static struct avrcp *find_session(GSList *list, struct audio_device *dev)
return NULL;
}
-static void state_changed(struct audio_device *dev, avctp_state_t old_state,
- avctp_state_t new_state, void *user_data)
+static struct avrcp *session_create(struct avrcp_server *server,
+ struct audio_device *dev)
{
- struct avrcp_server *server;
struct avrcp *session;
const sdp_record_t *rec;
sdp_list_t *list;
sdp_profile_desc_t *desc;
+ session = g_new0(struct avrcp, 1);
+ session->server = server;
+ session->conn = avctp_connect(&dev->src, &dev->dst);
+ session->dev = dev;
+ session->player = g_slist_nth_data(server->players, 0);
+
+ server->sessions = g_slist_append(server->sessions, session);
+
+ rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
+ if (rec == NULL)
+ return session;
+
+ if (sdp_get_profile_descs(rec, &list) < 0)
+ return session;
+
+ desc = list->data;
+ session->version = desc->version;
+ sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES, &session->features);
+
+ sdp_list_free(list, free);
+
+ return session;
+}
+
+static void session_destroy(struct avrcp *session)
+{
+ struct avrcp_server *server = session->server;
+ struct avrcp_player *player = session->player;
+
+ server->sessions = g_slist_remove(server->sessions, session);
+
+ if (session->control_id > 0)
+ avctp_unregister_pdu_handler(session->control_id);
+
+ if (session->browsing_id > 0)
+ avctp_unregister_browsing_pdu_handler(session->browsing_id);
+
+ if (player != NULL)
+ player->sessions = g_slist_remove(player->sessions, session);
+
+ g_free(session);
+}
+
+static void state_changed(struct audio_device *dev, avctp_state_t old_state,
+ avctp_state_t new_state, void *user_data)
+{
+ struct avrcp_server *server;
+ struct avrcp *session;
+
server = find_server(servers, &dev->src);
if (!server)
return;
@@ -1372,47 +1420,14 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
if (session == NULL)
break;
- server->sessions = g_slist_remove(server->sessions, session);
+ session_destroy(session);
- if (session->control_id > 0)
- avctp_unregister_pdu_handler(session->control_id);
-
- if (session->browsing_id > 0)
- avctp_unregister_browsing_pdu_handler(
- session->browsing_id);
-
- if (session->player != NULL)
- session->player->sessions = g_slist_remove(
- session->player->sessions,
- session);
-
- g_free(session);
break;
case AVCTP_STATE_CONNECTING:
if (session != NULL)
break;
- session = g_new0(struct avrcp, 1);
- session->server = server;
- session->conn = avctp_connect(&dev->src, &dev->dst);
- session->dev = dev;
- session->player = g_slist_nth_data(server->players, 0);
-
- server->sessions = g_slist_append(server->sessions, session);
-
- rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
- if (rec == NULL)
- return;
-
- if (sdp_get_profile_descs(rec, &list) < 0)
- return;
-
- desc = list->data;
- session->version = desc->version;
- sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES,
- &session->features);
-
- sdp_list_free(list, free);
+ session_create(server, dev);
break;
case AVCTP_STATE_CONNECTED:
--
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