* Re: [PATCHv3 06/18] Bluetooth: Add new ERTM receive states for channel move
From: Andrei Emeltchenko @ 2012-10-19 8:06 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth, gustavo, sunnyk, marcel
In-Reply-To: <1350583130-3241-7-git-send-email-mathewm@codeaurora.org>
Hi Mat,
On Thu, Oct 18, 2012 at 10:58:38AM -0700, Mat Martineau wrote:
> 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>
> Acked-by: Marcel Holtmann <marcel@holtmann.org>
Acked-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> net/bluetooth/l2cap_core.c | 104 +++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 104 insertions(+)
>
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index 42e20ee..ff25bf4 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -4706,6 +4706,12 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb,
> return err;
> }
>
> +static int l2cap_resegment(struct l2cap_chan *chan)
> +{
> + /* Placeholder */
> + return 0;
> +}
> +
> void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
> {
> u8 event;
> @@ -5211,6 +5217,98 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
> return err;
> }
>
> +static int l2cap_finish_move(struct l2cap_chan *chan)
> +{
> + 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->block_mtu;
> + else
> + chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
> +
> + return l2cap_resegment(chan);
> +}
> +
> +static int l2cap_rx_state_wait_p(struct l2cap_chan *chan,
> + struct l2cap_ctrl *control,
> + struct sk_buff *skb, u8 event)
> +{
> + int err;
> +
> + BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
> + event);
> +
> + if (!control->poll)
> + return -EPROTO;
> +
> + 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)
> + return -EPROTO;
> +
> + return l2cap_rx_state_recv(chan, control, NULL, event);
> +}
> +
> +static int l2cap_rx_state_wait_f(struct l2cap_chan *chan,
> + struct l2cap_ctrl *control,
> + struct sk_buff *skb, u8 event)
> +{
> + int err;
> +
> + if (!control->final)
> + return -EPROTO;
> +
> + 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->block_mtu;
> + else
> + chan->conn->mtu = chan->conn->hcon->hdev->acl_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 */
> @@ -5237,6 +5335,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
* Re: [PATCHv3 07/18] Bluetooth: Add move channel confirm handling
From: Andrei Emeltchenko @ 2012-10-19 8:07 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth, gustavo, sunnyk, marcel
In-Reply-To: <1350583130-3241-8-git-send-email-mathewm@codeaurora.org>
Hi Mat,
On Thu, Oct 18, 2012 at 10:58:39AM -0700, Mat Martineau wrote:
> 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>
Acked-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> net/bluetooth/l2cap_core.c | 71 ++++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 68 insertions(+), 3 deletions(-)
>
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index ff25bf4..2fa1bb5 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -1036,6 +1036,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 */
> @@ -4301,11 +4337,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))
> @@ -4316,8 +4353,36 @@ 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) {
> + /* Spec requires a response even if the icid was not found */
> + l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
> + return 0;
> + }
> +
> + 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;
> + }
> +
> l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
>
> + 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
* Re: [PATCHv3 08/18] Bluetooth: Add state to hci_chan
From: Andrei Emeltchenko @ 2012-10-19 8:08 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth, gustavo, sunnyk, marcel
In-Reply-To: <1350583130-3241-9-git-send-email-mathewm@codeaurora.org>
Hi Mat,
On Thu, Oct 18, 2012 at 10:58:40AM -0700, Mat Martineau wrote:
> 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>
> Acked-by: Marcel Holtmann <marcel@holtmann.org>
Acked-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> 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
* Re: [PATCHv3 11/18] Bluetooth: Add move confirm response handling
From: Andrei Emeltchenko @ 2012-10-19 8:25 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth, gustavo, sunnyk, marcel
In-Reply-To: <1350583130-3241-12-git-send-email-mathewm@codeaurora.org>
Hi Mat,
On Thu, Oct 18, 2012 at 10:58:43AM -0700, Mat Martineau wrote:
> 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>
> Acked-by: Marcel Holtmann <marcel@holtmann.org>
Acked-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> 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 8e50685..f315530 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -4661,6 +4661,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))
> @@ -4670,6 +4671,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.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: [PATCHv3 12/18] Bluetooth: Handle physical link completion
From: Andrei Emeltchenko @ 2012-10-19 8:32 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth, gustavo, sunnyk, marcel
In-Reply-To: <1350583130-3241-13-git-send-email-mathewm@codeaurora.org>
Hi Mat,
On Thu, Oct 18, 2012 at 10:58:44AM -0700, Mat Martineau wrote:
> 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>
> Acked-by: Marcel Holtmann <marcel@holtmann.org>
Acked-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> 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 f315530..d83faa9 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -1015,6 +1015,19 @@ 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;
> @@ -4199,6 +4212,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)
> {
> @@ -4364,6 +4394,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.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: [PATCHv3 13/18] Bluetooth: Flag ACL frames as complete for AMP controllers
From: Andrei Emeltchenko @ 2012-10-19 8:44 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth, gustavo, sunnyk, marcel
In-Reply-To: <1350583130-3241-14-git-send-email-mathewm@codeaurora.org>
Hi Mat,
On Thu, Oct 18, 2012 at 10:58:45AM -0700, Mat Martineau wrote:
> AMP controllers expect to transmit only "complete" ACL frames. These
> frames have both the "start" and "cont" bits set. AMP does not allow
> fragmented ACLs.
>
> Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
> Acked-by: Marcel Holtmann <marcel@holtmann.org>
Acked-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> net/bluetooth/l2cap_core.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index d83faa9..a8fc10d 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -762,6 +762,15 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
> BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len,
> skb->priority);
>
> + if (chan->hs_hcon && !__chan_is_moving(chan)) {
> + if (chan->hs_hchan)
> + hci_send_acl(chan->hs_hchan, skb, ACL_COMPLETE);
> + else
> + kfree_skb(skb);
> +
> + return;
> + }
> +
> if (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
> lmp_no_flush_capable(hcon->hdev))
> flags = ACL_START_NO_FLUSH;
> --
> 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: [PATCHv3 14/18] Bluetooth: Do not send data during channel move
From: Andrei Emeltchenko @ 2012-10-19 8:45 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth, gustavo, sunnyk, marcel
In-Reply-To: <1350583130-3241-15-git-send-email-mathewm@codeaurora.org>
Hi Mat,
On Thu, Oct 18, 2012 at 10:58:46AM -0700, Mat Martineau wrote:
> Outgoing ERTM data is queued during a channel move. The ERTM state
> machine is partially reset at the start of a move, and must be
> resynchronized with the remote state machine at the end of the move.
> Data is not sent so that there are no state transitions between the
> partial reset and the resync.
>
> Streaming mode frames are dropped during a move.
>
> Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
> Acked-by: Marcel Holtmann <marcel@holtmann.org>
Acked-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> net/bluetooth/l2cap_core.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index a8fc10d..ab2bfc8 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -946,6 +946,9 @@ static void l2cap_send_sframe(struct l2cap_chan *chan,
> if (!control->sframe)
> return;
>
> + if (__chan_is_moving(chan))
> + return;
> +
> if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state) &&
> !control->poll)
> control->final = 1;
> @@ -1824,6 +1827,9 @@ static void l2cap_streaming_send(struct l2cap_chan *chan,
>
> BT_DBG("chan %p, skbs %p", chan, skbs);
>
> + if (__chan_is_moving(chan))
> + return;
> +
> skb_queue_splice_tail_init(skbs, &chan->tx_q);
>
> while (!skb_queue_empty(&chan->tx_q)) {
> @@ -1866,6 +1872,9 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
> if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
> return 0;
>
> + if (__chan_is_moving(chan))
> + return 0;
> +
> while (chan->tx_send_head &&
> chan->unacked_frames < chan->remote_tx_win &&
> chan->tx_state == L2CAP_TX_STATE_XMIT) {
> @@ -1931,6 +1940,9 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan)
> if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
> return;
>
> + if (__chan_is_moving(chan))
> + return;
> +
> while (chan->retrans_list.head != L2CAP_SEQ_LIST_CLEAR) {
> seq = l2cap_seq_list_pop(&chan->retrans_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
* Re: [PATCHv3 17/18] Bluetooth: Do not retransmit data during a channel move
From: Andrei Emeltchenko @ 2012-10-19 8:48 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth, gustavo, sunnyk, marcel
In-Reply-To: <1350583130-3241-18-git-send-email-mathewm@codeaurora.org>
On Thu, Oct 18, 2012 at 10:58:49AM -0700, Mat Martineau wrote:
> Do not retransmit previously-sent data when a "receiver ready" s-frame
> with the "final" flag is received during a move.
>
> The ERTM state machines will resynchronize at the end of a channel
> move, and the state machine needs to avoid state changes during a
> move.
>
> Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
> Acked-by: Marcel Holtmann <marcel@holtmann.org>
Acked-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> net/bluetooth/l2cap_core.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index e89af62..8fa46de 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -5612,8 +5612,8 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
> if (control->final) {
> clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
>
> - if (!test_and_clear_bit(CONN_REJ_ACT,
> - &chan->conn_state)) {
> + if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state) &&
> + !__chan_is_moving(chan)) {
> control->final = 0;
> l2cap_retransmit_all(chan, control);
> }
> --
> 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: [PATCH v6 01/16] doc: Add settings storage documentation
From: Frederic Danis @ 2012-10-19 9:33 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: linux-bluetooth
In-Reply-To: <1350602001.2026.35.camel@aeonflux>
Hello Marcel,
On 19/10/2012 01:13, Marcel Holtmann wrote:
<snip>
>> +Cache directory files
>> +======================
>> +
>> +Each files, named by remote device address, contains 1 [General] group:
>> + [General]
>> + Name=device name a
>
> Is this the only thing we are storing in these files? Or are we planning
> to store additional information at some point.
>
>> +Device directory files
>> +======================
>> +
>> +Remote device info file will include a [General] group with device info,
>> +[DeviceID], [LinkKey] and [LongTermKey] groups with related info:
>> + [General]
>> + Alias=
>
> Are we just doing the Alias here? We are not keeping the Name? In theory
> we have a short name and full name actually. If we ever only got the
> short name, we need to know that. Then we have to request the long name
> next time.
Name is already stored in cache file. So I think we do not need to save
it also in device info file.
We may store both short and long names in the cache file.
Regards
Fred
--
Frederic Danis Open Source Technology Center
frederic.danis@intel.com Intel Corporation
^ permalink raw reply
* Re: [PATCHv3 18/18] Bluetooth: Start channel move when socket option is changed
From: Andrei Emeltchenko @ 2012-10-19 9:37 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth, gustavo, sunnyk, marcel
In-Reply-To: <1350583130-3241-19-git-send-email-mathewm@codeaurora.org>
Hi Mat,
On Thu, Oct 18, 2012 at 10:58:50AM -0700, Mat Martineau wrote:
> Channel moves are triggered by changes to the BT_CHANNEL_POLICY
> sockopt when an ERTM or streaming-mode channel is connected.
>
> Moves are only started if enable_hs is true.
>
> Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
> Acked-by: Marcel Holtmann <marcel@holtmann.org>
> ---
> include/net/bluetooth/l2cap.h | 1 +
> net/bluetooth/l2cap_core.c | 20 ++++++++++++++++++++
> net/bluetooth/l2cap_sock.c | 5 +++++
> 3 files changed, 26 insertions(+)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index b4c3c65..49783e9 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -809,5 +809,6 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
> void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
> void l2cap_chan_del(struct l2cap_chan *chan, int err);
> void l2cap_send_conn_req(struct l2cap_chan *chan);
> +void l2cap_move_start(struct l2cap_chan *chan);
>
> #endif /* __L2CAP_H */
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index 8fa46de..b3d3f4f 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -4452,6 +4452,26 @@ static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
> }
> }
>
> +void l2cap_move_start(struct l2cap_chan *chan)
> +{
> + BT_DBG("chan %p", chan);
> +
> + if (chan->local_amp_id == 0) {
I would rather use "if (!chan->local_amp_id)" or event better if compare to
use "if (chan->local_amp_id == HCI_BRDER_ID)
> + if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED)
> + return;
> + chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
> + chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
Isn't it a bit earlier to start move? We should first to query remote AMP
controllers to find out AMP id, etc. Or how this supposed to work? Where
do you move?
Best regards
Andrei Emeltchenko
> + /* Placeholder - start physical link setup */
> + } else {
> + chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
> + chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
> + chan->move_id = 0;
> + l2cap_move_start(chan);
> + l2cap_send_move_chan_req(chan, 0);
> + __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
> + }
> +}
> +
> static void l2cap_do_create(struct l2cap_chan *chan, int result,
> u8 local_amp_id, u8 remote_amp_id)
> {
> diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
> index 5fae2bd..7cb4d73 100644
> --- a/net/bluetooth/l2cap_sock.c
> +++ b/net/bluetooth/l2cap_sock.c
> @@ -735,6 +735,11 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
> }
>
> chan->chan_policy = (u8) opt;
> +
> + if (sk->sk_state == BT_CONNECTED &&
> + chan->move_role == L2CAP_MOVE_ROLE_NONE)
> + l2cap_move_start(chan);
> +
> break;
>
> default:
> --
> 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
* [PATCH BlueZ v2] AVDTP: Do not keep a internal reference
From: Luiz Augusto von Dentz @ 2012-10-19 9:59 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Don't initialize reference with 1, instead always start disconnect timer
when reference drops to 0, so in case nobody reclaims the session it
automatically disconnect after 1 second and frees the memory.
---
v2: Fix with stream_setup flag being ignored in disconnect_timeout
audio/avdtp.c | 217 +++++++++++++++++++++++++---------------------------------
1 file changed, 94 insertions(+), 123 deletions(-)
diff --git a/audio/avdtp.c b/audio/avdtp.c
index bd91cb6..bca4809 100644
--- a/audio/avdtp.c
+++ b/audio/avdtp.c
@@ -387,8 +387,7 @@ struct avdtp_stream {
/* Structure describing an AVDTP connection between two devices */
struct avdtp {
- int ref;
- int free_lock;
+ unsigned int ref;
uint16_t version;
@@ -657,50 +656,6 @@ static gboolean stream_open_timeout(gpointer user_data)
return FALSE;
}
-static gboolean disconnect_timeout(gpointer user_data)
-{
- struct avdtp *session = user_data;
- struct audio_device *dev;
- gboolean stream_setup;
-
- session->dc_timer = 0;
- stream_setup = session->stream_setup;
- session->stream_setup = FALSE;
-
- dev = manager_get_device(&session->server->src, &session->dst, FALSE);
-
- if (dev && dev->sink && stream_setup)
- sink_setup_stream(dev->sink, session);
- else if (dev && dev->source && stream_setup)
- source_setup_stream(dev->source, session);
- else
- connection_lost(session, ETIMEDOUT);
-
- return FALSE;
-}
-
-static void remove_disconnect_timer(struct avdtp *session)
-{
- g_source_remove(session->dc_timer);
- session->dc_timer = 0;
- session->stream_setup = FALSE;
-}
-
-static void set_disconnect_timer(struct avdtp *session)
-{
- if (session->dc_timer)
- remove_disconnect_timer(session);
-
- if (session->device_disconnect) {
- session->dc_timer = g_idle_add(disconnect_timeout, session);
- return;
- }
-
- session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
- disconnect_timeout,
- session);
-}
-
void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id)
{
err->category = category;
@@ -780,8 +735,9 @@ static void avdtp_set_state(struct avdtp *session,
}
}
-static void stream_free(struct avdtp_stream *stream)
+static void stream_free(void *data)
{
+ struct avdtp_stream *stream = data;
struct avdtp_remote_sep *rsep;
stream->lsep->info.inuse = 0;
@@ -1144,37 +1100,42 @@ static int avdtp_cancel_authorization(struct avdtp *session)
return err;
session->auth_id = 0;
+ avdtp_unref(session);
return 0;
}
-static void connection_lost(struct avdtp *session, int err)
+static void sep_free(gpointer data)
{
- char address[18];
+ struct avdtp_remote_sep *sep = data;
- ba2str(&session->dst, address);
- DBG("Disconnected from %s", address);
+ g_slist_free_full(sep->caps, g_free);
+ g_free(sep);
+}
- if (err != EACCES)
- avdtp_cancel_authorization(session);
+static void remove_disconnect_timer(struct avdtp *session)
+{
+ g_source_remove(session->dc_timer);
+ session->dc_timer = 0;
+ session->stream_setup = FALSE;
+}
- session->free_lock = 1;
+static void avdtp_free(void *data)
+{
+ struct avdtp *session = data;
- finalize_discovery(session, err);
+ DBG("%p", session);
- g_slist_foreach(session->streams, (GFunc) release_stream, session);
- session->streams = NULL;
+ g_slist_free_full(session->streams, stream_free);
- session->free_lock = 0;
+ if (session->state != AVDTP_SESSION_STATE_DISCONNECTED)
+ avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
if (session->io) {
g_io_channel_shutdown(session->io, FALSE, NULL);
g_io_channel_unref(session->io);
- session->io = NULL;
}
- avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
-
if (session->io_id) {
g_source_remove(session->io_id);
session->io_id = 0;
@@ -1183,69 +1144,92 @@ static void connection_lost(struct avdtp *session, int err)
if (session->dc_timer)
remove_disconnect_timer(session);
- if (session->ref != 1)
- error("connection_lost: ref count not 1 after all callbacks");
- else
- avdtp_unref(session);
+ avdtp_cancel_authorization(session);
+
+ if (session->req)
+ pending_req_free(session->req);
+
+ g_slist_free_full(session->seps, sep_free);
+
+ g_free(session->buf);
+
+ g_free(session);
}
-static void sep_free(gpointer data)
+static gboolean disconnect_timeout(gpointer user_data)
{
- struct avdtp_remote_sep *sep = data;
+ struct avdtp *session = user_data;
+ struct audio_device *dev;
+ gboolean stream_setup;
- g_slist_free_full(sep->caps, g_free);
- g_free(sep);
+ session->dc_timer = 0;
+
+ stream_setup = session->stream_setup;
+ session->stream_setup = FALSE;
+ dev = manager_get_device(&session->server->src, &session->dst, FALSE);
+
+ if (dev && dev->sink && stream_setup)
+ sink_setup_stream(dev->sink, session);
+ else if (dev && dev->source && stream_setup)
+ source_setup_stream(dev->source, session);
+ else if (session->ref > 0)
+ connection_lost(session, ETIMEDOUT);
+ else {
+ struct avdtp_server *server = session->server;
+
+ server->sessions = g_slist_remove(server->sessions, session);
+ avdtp_free(session);
+ }
+
+ return FALSE;
}
-void avdtp_unref(struct avdtp *session)
+static void set_disconnect_timer(struct avdtp *session)
{
- struct avdtp_server *server;
+ if (session->dc_timer)
+ remove_disconnect_timer(session);
- if (!session)
+ if (session->device_disconnect) {
+ session->dc_timer = g_idle_add(disconnect_timeout, session);
return;
+ }
- session->ref--;
-
- DBG("%p: ref=%d", session, session->ref);
+ session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
+ disconnect_timeout,
+ session);
+}
- if (session->ref == 1) {
- if (session->state == AVDTP_SESSION_STATE_CONNECTING &&
- session->io) {
- avdtp_cancel_authorization(session);
- g_io_channel_shutdown(session->io, TRUE, NULL);
- g_io_channel_unref(session->io);
- session->io = NULL;
- avdtp_set_state(session,
- AVDTP_SESSION_STATE_DISCONNECTED);
- }
+static void connection_lost(struct avdtp *session, int err)
+{
+ char address[18];
- if (session->io)
- set_disconnect_timer(session);
- else if (!session->free_lock) /* Drop the local ref if we
- aren't connected */
- session->ref--;
- }
+ ba2str(&session->dst, address);
+ DBG("Disconnected from %s", address);
- if (session->ref > 0)
- return;
+ if (err != EACCES)
+ avdtp_cancel_authorization(session);
- server = session->server;
+ g_slist_foreach(session->streams, (GFunc) release_stream, session);
+ session->streams = NULL;
- DBG("%p: freeing session and removing from list", session);
+ finalize_discovery(session, err);
- if (session->dc_timer)
- remove_disconnect_timer(session);
+ avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
+}
- server->sessions = g_slist_remove(server->sessions, session);
+void avdtp_unref(struct avdtp *session)
+{
+ if (!session)
+ return;
- if (session->req)
- pending_req_free(session->req);
+ session->ref--;
- g_slist_free_full(session->seps, sep_free);
+ DBG("%p: ref=%d", session, session->ref);
- g_free(session->buf);
+ if (session->ref > 0)
+ return;
- g_free(session);
+ set_disconnect_timer(session);
}
struct avdtp *avdtp_ref(struct avdtp *session)
@@ -2231,12 +2215,6 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
goto failed;
}
- if (session->ref == 1 && !session->streams && !session->req)
- set_disconnect_timer(session);
-
- if (session->streams && session->dc_timer)
- remove_disconnect_timer(session);
-
if (session->req && session->req->collided) {
DBG("Collision detected");
goto next;
@@ -2383,7 +2361,7 @@ static struct avdtp *avdtp_get_internal(const bdaddr_t *src, const bdaddr_t *dst
session->server = server;
bacpy(&session->dst, dst);
- session->ref = 1;
+ set_disconnect_timer(session);
/* We don't use avdtp_set_state() here since this isn't a state change
* but just setting of the initial state */
session->state = AVDTP_SESSION_STATE_DISCONNECTED;
@@ -2490,6 +2468,8 @@ static void auth_cb(DBusError *derr, void *user_data)
struct avdtp *session = user_data;
GError *err = NULL;
+ avdtp_unref(session);
+
if (derr && dbus_error_is_set(derr)) {
error("Access denied: %s", derr->message);
connection_lost(session, EACCES);
@@ -2578,10 +2558,12 @@ static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
auth_cb, session);
if (session->auth_id == 0) {
avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
- avdtp_unref(session);
goto drop;
}
+ /* Disable disconnect timer while authorizing */
+ avdtp_ref(session);
+
dev->auto_connect = auto_connect;
return;
@@ -3949,23 +3931,12 @@ proceed:
void avdtp_exit(const bdaddr_t *src)
{
struct avdtp_server *server;
- GSList *l;
server = find_server(servers, src);
if (!server)
return;
- l = server->sessions;
- while (l) {
- struct avdtp *session = l->data;
-
- l = l->next;
- /* value of l pointer should be updated before invoking
- * connection_lost since it internally uses avdtp_unref
- * which operates on server->session list as well
- */
- connection_lost(session, -ECONNABORTED);
- }
+ g_slist_free_full(server->sessions, avdtp_free);
servers = g_slist_remove(servers, server);
--
1.7.11.4
^ permalink raw reply related
* [RFC v0 00/15] WIP: btd_profile connect and disconnect
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
This RFC is work-in-progress: the patches are not intended to be applied, but instead submitted as a base for discussion.
The patchset proposes three inter-related changes:
(1) Splitting of individual btd_profile instantes per role. Some profiles do already have such separation (see HFP-Gateway) but some others have all roles merged into one (see A2DP or PAN).
(2) Exposure of internal APIs to connect/disconnect devices, as proposed by the btd_profile approach (.connect and .disconnect). The goal would be not only to support Device.Connect, but also to be able to implement connection-handling plugins such as the IVI case.
(3) The exposure of a D-Bus API to connect/disconnect profiles, and report the current state (disconnected, connecting, or connected). The goal here would be to be able to implement Bluetooth-centric UIs, which typically show which profiles have been connected per device. Additionally, this would allow implementing connection-handling policies externally, instead of in a plugin.
Each of this point requires some discussion. For example, in point (2), there are different possible way to expose an internal API to connect/disconnect profiles. The patchset, in fact, proposes different approaches for HID (patch 08/15), where it's btd_profile-aware, or networking (patch 03/15), where the internal API has nothing to do with the btd_profile infrastructure.
The D-Bus API could have several variants as well, one of them being the currently existing one in BlueZ 4.x. This first RFC proposes a completely different one, based on a new D-Bus interface (org.bluez.Profile, see patch 15/15), which is admittedly ugly.
I hope some of this ideas can make sense for BlueZ 5.
Any feedback will be welcome,
Mikel
Mikel Astiz (15):
network: Specify id while registering server
network: Trivial function rename
network: Expose internal connection API
network: Split Network into three btd_profile
network: Add network .connect and .disconnect
audio: Split A2DP into three btd_profile
audio: Trivial function rename
source: Expose internal connection API
source: Add profile .connect and .disconnect
input: Trivial function rename
input: Expose internal disconnection API
input: Add profile .disconnect
profile: Rename org.bluez.Profile->ProfileAgent
profile: Add object to represent device-profile
profile: Add new org.bluez.Profile
Makefile.am | 1 +
audio/a2dp.c | 88 ++++++-------
audio/a2dp.h | 4 +-
audio/device.h | 3 +
audio/manager.c | 185 ++++++++++++++++++++++++---
audio/sink.c | 12 +-
audio/source.c | 158 +++++++++++++----------
audio/source.h | 2 +
doc/device-api.txt | 23 ----
doc/manager-api.txt | 3 +-
doc/profile-api.txt | 59 ++++-----
doc/profileagent-api.txt | 53 ++++++++
profiles/input/device.c | 39 ++++--
profiles/input/device.h | 6 +-
profiles/input/manager.c | 1 +
profiles/network/connection.c | 160 +++++++++++++++--------
profiles/network/connection.h | 8 ++
profiles/network/manager.c | 205 +++++++++++++++++++++++++++---
profiles/network/server.c | 9 +-
profiles/network/server.h | 4 +-
src/bluetooth.conf | 2 +-
src/device-profile.c | 288 ++++++++++++++++++++++++++++++++++++++++++
src/device-profile.h | 43 +++++++
src/device.c | 110 +++++++++++-----
src/manager.c | 2 +-
src/profile.c | 56 ++++----
src/profile.h | 11 +-
test/test-profile | 8 +-
28 files changed, 1191 insertions(+), 352 deletions(-)
create mode 100644 doc/profileagent-api.txt
create mode 100644 src/device-profile.c
create mode 100644 src/device-profile.h
--
1.7.11.7
^ permalink raw reply
* [RFC v0 01/15] network: Specify id while registering server
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Only one type of server is being installed (BNEP_SVC_NAP), but the
server API actually supports having several of them. Therefore, it is
more accurate to specify BNEP_SVC_NAP in the manager's side.
---
profiles/network/manager.c | 4 ++--
profiles/network/server.c | 9 ++++-----
profiles/network/server.h | 4 ++--
3 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/profiles/network/manager.c b/profiles/network/manager.c
index aa564bd..fe3324b 100644
--- a/profiles/network/manager.c
+++ b/profiles/network/manager.c
@@ -102,7 +102,7 @@ static int network_server_probe(struct btd_profile *p,
DBG("path %s", path);
- return server_register(adapter);
+ return server_register(adapter, BNEP_SVC_NAP);
}
static void network_server_remove(struct btd_profile *p,
@@ -112,7 +112,7 @@ static void network_server_remove(struct btd_profile *p,
DBG("path %s", path);
- server_unregister(adapter);
+ server_unregister(adapter, BNEP_SVC_NAP);
}
static struct btd_profile network_profile = {
diff --git a/profiles/network/server.c b/profiles/network/server.c
index ccf6e69..7b4460d 100644
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -766,7 +766,7 @@ static struct network_adapter *create_adapter(struct btd_adapter *adapter)
return na;
}
-int server_register(struct btd_adapter *adapter)
+int server_register(struct btd_adapter *adapter, uint16_t id)
{
struct network_adapter *na;
struct network_server *ns;
@@ -780,7 +780,7 @@ int server_register(struct btd_adapter *adapter)
adapters = g_slist_append(adapters, na);
}
- ns = find_server(na->servers, BNEP_SVC_NAP);
+ ns = find_server(na->servers, id);
if (ns)
return 0;
@@ -802,7 +802,7 @@ int server_register(struct btd_adapter *adapter)
}
bacpy(&ns->src, adapter_get_address(adapter));
- ns->id = BNEP_SVC_NAP;
+ ns->id = id;
ns->na = na;
ns->record_id = 0;
na->servers = g_slist_append(na->servers, ns);
@@ -812,11 +812,10 @@ int server_register(struct btd_adapter *adapter)
return 0;
}
-int server_unregister(struct btd_adapter *adapter)
+int server_unregister(struct btd_adapter *adapter, uint16_t id)
{
struct network_adapter *na;
struct network_server *ns;
- uint16_t id = BNEP_SVC_NAP;
na = find_adapter(adapters, adapter);
if (!na)
diff --git a/profiles/network/server.h b/profiles/network/server.h
index 4c3ab85..2edd342 100644
--- a/profiles/network/server.h
+++ b/profiles/network/server.h
@@ -23,5 +23,5 @@
int server_init(gboolean secure);
void server_exit(void);
-int server_register(struct btd_adapter *adapter);
-int server_unregister(struct btd_adapter *adapter);
+int server_register(struct btd_adapter *adapter, uint16_t id);
+int server_unregister(struct btd_adapter *adapter, uint16_t id);
--
1.7.11.7
^ permalink raw reply related
* [RFC v0 02/15] network: Trivial function rename
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Avoid the connection_ prefix for local functions implementing the D-Bus
API.
---
profiles/network/connection.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/profiles/network/connection.c b/profiles/network/connection.c
index 8564bca..afa8a6b 100644
--- a/profiles/network/connection.c
+++ b/profiles/network/connection.c
@@ -397,7 +397,7 @@ failed:
}
/* Connect and initiate BNEP session */
-static DBusMessage *connection_connect(DBusConnection *conn,
+static DBusMessage *local_connect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct network_peer *peer = data;
@@ -465,7 +465,7 @@ static DBusMessage *connection_cancel(DBusConnection *conn,
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
-static DBusMessage *connection_disconnect(DBusConnection *conn,
+static DBusMessage *local_disconnect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct network_peer *peer = data;
@@ -483,7 +483,7 @@ static DBusMessage *connection_disconnect(DBusConnection *conn,
return btd_error_not_connected(msg);
}
-static DBusMessage *connection_get_properties(DBusConnection *conn,
+static DBusMessage *local_get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct network_peer *peer = data;
@@ -568,12 +568,12 @@ static const GDBusMethodTable connection_methods[] = {
{ GDBUS_ASYNC_METHOD("Connect",
GDBUS_ARGS({"uuid", "s"}),
GDBUS_ARGS({"interface", "s"}),
- connection_connect) },
+ local_connect) },
{ GDBUS_METHOD("Disconnect",
- NULL, NULL, connection_disconnect) },
+ NULL, NULL, local_disconnect) },
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
- connection_get_properties) },
+ local_get_properties) },
{ }
};
--
1.7.11.7
^ permalink raw reply related
* [RFC v0 03/15] network: Expose internal connection API
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Separate the D-Bus code from the internal connection handling code,
exposing an internal API in case some internal codepath/plugin is
interested in using it.
---
profiles/network/connection.c | 148 ++++++++++++++++++++++++++++--------------
profiles/network/connection.h | 8 +++
2 files changed, 108 insertions(+), 48 deletions(-)
diff --git a/profiles/network/connection.c b/profiles/network/connection.c
index afa8a6b..abcbee8 100644
--- a/profiles/network/connection.c
+++ b/profiles/network/connection.c
@@ -64,11 +64,13 @@ struct network_peer {
};
struct network_conn {
- DBusMessage *msg;
+ btd_connection_cb cb;
+ void *cb_data;
char dev[16]; /* Interface name */
uint16_t id; /* Role: Service Class Identifier */
conn_state state;
GIOChannel *io;
+ char *owner; /* Connection initiator D-Bus client */
guint watch; /* Disconnect watch */
guint dc_id;
struct network_peer *peer;
@@ -141,10 +143,9 @@ static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
return FALSE;
}
-static void cancel_connection(struct network_conn *nc, const char *err_msg)
+static void cancel_connection(struct network_conn *nc, int err)
{
DBusConnection *conn = btd_get_dbus_connection();
- DBusMessage *reply;
if (nc->timeout_source > 0) {
g_source_remove(nc->timeout_source);
@@ -156,14 +157,8 @@ static void cancel_connection(struct network_conn *nc, const char *err_msg)
nc->watch = 0;
}
- if (nc->msg) {
- if (err_msg) {
- reply = btd_error_failed(nc->msg, err_msg);
- g_dbus_send_message(conn, reply);
- }
- dbus_message_unref(nc->msg);
- nc->msg = NULL;
- }
+ if (nc->cb)
+ nc->cb(nc->peer->device, err, NULL, nc->cb_data);
g_io_channel_shutdown(nc->io, TRUE, NULL);
g_io_channel_unref(nc->io);
@@ -180,7 +175,12 @@ static void connection_destroy(DBusConnection *conn, void *user_data)
bnep_if_down(nc->dev);
bnep_kill_connection(device_get_address(nc->peer->device));
} else if (nc->io)
- cancel_connection(nc, NULL);
+ cancel_connection(nc, -EIO);
+
+ if (nc->owner) {
+ g_free(nc->owner);
+ nc->owner = NULL;
+ }
}
static void disconnect_cb(struct btd_device *device, gboolean removal,
@@ -270,11 +270,8 @@ static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond,
pdev = nc->dev;
uuid = bnep_uuid(nc->id);
- g_dbus_send_reply(btd_get_dbus_connection(), nc->msg,
- DBUS_TYPE_STRING, &pdev,
- DBUS_TYPE_INVALID);
- dbus_message_unref(nc->msg);
- nc->msg = NULL;
+ if (nc->cb)
+ nc->cb(nc->peer->device, 0, pdev, nc->cb_data);
path = device_get_path(nc->peer->device);
@@ -303,7 +300,7 @@ static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond,
return FALSE;
failed:
- cancel_connection(nc, "bnep setup failed");
+ cancel_connection(nc, -EIO);
return FALSE;
}
@@ -349,7 +346,7 @@ static gboolean bnep_conn_req_to(gpointer user_data)
return TRUE;
}
- cancel_connection(nc, "bnep setup failed");
+ cancel_connection(nc, -ETIMEDOUT);
return FALSE;
}
@@ -393,32 +390,74 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
return;
failed:
- cancel_connection(nc, err_msg);
+ cancel_connection(nc, -EIO);
+}
+
+static void local_connect_cb(struct btd_device *device, int err,
+ const char *pdev, void *data)
+{
+ DBusMessage *msg = data;
+ DBusMessage *reply;
+
+ if (err < 0) {
+ reply = btd_error_failed(msg, strerror(-err));
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ dbus_message_unref(msg);
+ return;
+ }
+
+ g_dbus_send_reply(btd_get_dbus_connection(), msg,
+ DBUS_TYPE_STRING, &pdev,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_unref(msg);
}
-/* Connect and initiate BNEP session */
static DBusMessage *local_connect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct network_peer *peer = data;
- struct network_conn *nc;
const char *svc;
uint16_t id;
- GError *err = NULL;
- const bdaddr_t *src;
- const bdaddr_t *dst;
+ int err;
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &svc,
DBUS_TYPE_INVALID) == FALSE)
return btd_error_invalid_args(msg);
id = bnep_service_id(svc);
+
+ err = connection_connect(peer->device, id, dbus_message_get_sender(msg),
+ local_connect_cb, msg);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ dbus_message_ref(msg);
+
+ return NULL;
+}
+
+/* Connect and initiate BNEP session */
+int connection_connect(struct btd_device *device, uint16_t id,
+ const char *owner,
+ btd_connection_cb cb, void *data)
+{
+ struct network_peer *peer;
+ struct network_conn *nc;
+ GError *err = NULL;
+ const bdaddr_t *src;
+ const bdaddr_t *dst;
+
+ peer = find_peer(peers, device);
+ if (!peer)
+ return -ENOENT;
+
nc = find_connection(peer->connections, id);
if (!nc)
- return btd_error_not_supported(msg);
+ return -ENOTSUP;
if (nc->state != DISCONNECTED)
- return btd_error_already_connected(msg);
+ return -EALREADY;
src = adapter_get_address(device_get_adapter(peer->device));
dst = device_get_address(peer->device);
@@ -432,52 +471,65 @@ static DBusMessage *local_connect(DBusConnection *conn,
BT_IO_OPT_OMTU, BNEP_MTU,
BT_IO_OPT_IMTU, BNEP_MTU,
BT_IO_OPT_INVALID);
- if (!nc->io) {
- DBusMessage *reply;
- error("%s", err->message);
- reply = btd_error_failed(msg, err->message);
- g_error_free(err);
- return reply;
- }
+ if (!nc->io)
+ return -EIO;
nc->state = CONNECTING;
- nc->msg = dbus_message_ref(msg);
- nc->watch = g_dbus_add_disconnect_watch(conn,
- dbus_message_get_sender(msg),
- connection_destroy,
+ nc->owner = g_strdup(owner);
+
+ if (owner)
+ nc->watch = g_dbus_add_disconnect_watch(
+ btd_get_dbus_connection(),
+ owner, connection_destroy,
nc, NULL);
- return NULL;
+ return 0;
}
-static DBusMessage *connection_cancel(DBusConnection *conn,
- DBusMessage *msg, void *data)
+int connection_disconnect(struct btd_device *device, uint16_t id,
+ const char *caller)
{
- struct network_conn *nc = data;
- const char *owner = dbus_message_get_sender(nc->msg);
- const char *caller = dbus_message_get_sender(msg);
+ struct network_peer *peer;
+ struct network_conn *nc;
+
+ peer = find_peer(peers, device);
+ if (!peer)
+ return -ENOENT;
+
+ nc = find_connection(peer->connections, id);
+ if (!nc)
+ return -ENOTSUP;
+
+ if (nc->state == DISCONNECTED)
+ return 0;
- if (!g_str_equal(owner, caller))
- return btd_error_not_authorized(msg);
+ if (!g_str_equal(nc->owner, caller))
+ return -EPERM;
connection_destroy(NULL, nc);
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ return 0;
}
static DBusMessage *local_disconnect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct network_peer *peer = data;
+ const char *caller = dbus_message_get_sender(msg);
GSList *l;
for (l = peer->connections; l; l = l->next) {
struct network_conn *nc = l->data;
+ int err;
if (nc->state == DISCONNECTED)
continue;
- return connection_cancel(conn, msg, nc);
+ err = connection_disconnect(peer->device, nc->id, caller);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
return btd_error_not_connected(msg);
diff --git a/profiles/network/connection.h b/profiles/network/connection.h
index 50c0774..e67b0ec 100644
--- a/profiles/network/connection.h
+++ b/profiles/network/connection.h
@@ -21,5 +21,13 @@
*
*/
+typedef void (*btd_connection_cb)(struct btd_device *device, int err,
+ const char *pdev, void *data);
+
int connection_register(struct btd_device *device, uint16_t id);
void connection_unregister(struct btd_device *device);
+int connection_connect(struct btd_device *device, uint16_t id,
+ const char *owner,
+ btd_connection_cb cb, void *data);
+int connection_disconnect(struct btd_device *device, uint16_t id,
+ const char *caller);
--
1.7.11.7
^ permalink raw reply related
* [RFC v0 04/15] network: Split Network into three btd_profile
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Split the possible roles into three different btd_profile instances, one
role each, in accordance with the rest of the existing profiles.
---
profiles/network/manager.c | 105 +++++++++++++++++++++++++++++++++++++--------
1 file changed, 86 insertions(+), 19 deletions(-)
diff --git a/profiles/network/manager.c b/profiles/network/manager.c
index fe3324b..eda0bbe 100644
--- a/profiles/network/manager.c
+++ b/profiles/network/manager.c
@@ -73,19 +73,12 @@ done:
conf_security ? "true" : "false");
}
-static int network_probe(struct btd_profile *p, struct btd_device *device,
+static int panu_probe(struct btd_profile *p, struct btd_device *device,
GSList *uuids)
{
DBG("path %s", device_get_path(device));
- if (g_slist_find_custom(uuids, PANU_UUID, bt_uuid_strcmp))
- connection_register(device, BNEP_SVC_PANU);
- if (g_slist_find_custom(uuids, GN_UUID, bt_uuid_strcmp))
- connection_register(device, BNEP_SVC_GN);
- if (g_slist_find_custom(uuids, NAP_UUID, bt_uuid_strcmp))
- connection_register(device, BNEP_SVC_NAP);
-
- return 0;
+ return connection_register(device, BNEP_SVC_PANU);
}
static void network_remove(struct btd_profile *p, struct btd_device *device)
@@ -95,17 +88,70 @@ static void network_remove(struct btd_profile *p, struct btd_device *device)
connection_unregister(device);
}
-static int network_server_probe(struct btd_profile *p,
+static int panu_server_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+ const gchar *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ return server_register(adapter, BNEP_SVC_PANU);
+}
+
+static void panu_server_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ const gchar *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ server_unregister(adapter, BNEP_SVC_PANU);
+}
+
+static int gn_probe(struct btd_profile *p, struct btd_device *device,
+ GSList *uuids)
+{
+ DBG("path %s", device_get_path(device));
+
+ return connection_register(device, BNEP_SVC_GN);
+}
+
+static int gn_server_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+ const gchar *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ return server_register(adapter, BNEP_SVC_GN);
+}
+
+static void gn_server_remove(struct btd_profile *p,
struct btd_adapter *adapter)
{
const gchar *path = adapter_get_path(adapter);
DBG("path %s", path);
+ server_unregister(adapter, BNEP_SVC_GN);
+}
+
+static int nap_probe(struct btd_profile *p, struct btd_device *device,
+ GSList *uuids)
+{
+ DBG("path %s", device_get_path(device));
+
+ return connection_register(device, BNEP_SVC_NAP);
+}
+
+static int nap_server_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+ const gchar *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
return server_register(adapter, BNEP_SVC_NAP);
}
-static void network_server_remove(struct btd_profile *p,
+static void nap_server_remove(struct btd_profile *p,
struct btd_adapter *adapter)
{
const gchar *path = adapter_get_path(adapter);
@@ -115,14 +161,31 @@ static void network_server_remove(struct btd_profile *p,
server_unregister(adapter, BNEP_SVC_NAP);
}
-static struct btd_profile network_profile = {
- .name = "network",
- .remote_uuids = BTD_UUIDS(PANU_UUID, GN_UUID, NAP_UUID),
- .device_probe = network_probe,
+static struct btd_profile panu_profile = {
+ .name = "network-panu",
+ .remote_uuids = BTD_UUIDS(PANU_UUID),
+ .device_probe = panu_probe,
+ .device_remove = network_remove,
+ .adapter_probe = panu_server_probe,
+ .adapter_remove = panu_server_remove,
+};
+
+static struct btd_profile gn_profile = {
+ .name = "network-gn",
+ .remote_uuids = BTD_UUIDS(GN_UUID),
+ .device_probe = gn_probe,
.device_remove = network_remove,
+ .adapter_probe = gn_server_probe,
+ .adapter_remove = gn_server_remove,
+};
- .adapter_probe = network_server_probe,
- .adapter_remove = network_server_remove,
+static struct btd_profile nap_profile = {
+ .name = "network-nap",
+ .remote_uuids = BTD_UUIDS(NAP_UUID),
+ .device_probe = nap_probe,
+ .device_remove = network_remove,
+ .adapter_probe = nap_server_probe,
+ .adapter_remove = nap_server_remove,
};
int network_manager_init(void)
@@ -144,7 +207,9 @@ int network_manager_init(void)
if (server_init(conf_security) < 0)
return -1;
- btd_profile_register(&network_profile);
+ btd_profile_register(&panu_profile);
+ btd_profile_register(&gn_profile);
+ btd_profile_register(&nap_profile);
return 0;
}
@@ -153,7 +218,9 @@ void network_manager_exit(void)
{
server_exit();
- btd_profile_unregister(&network_profile);
+ btd_profile_unregister(&panu_profile);
+ btd_profile_unregister(&gn_profile);
+ btd_profile_unregister(&nap_profile);
bnep_cleanup();
}
--
1.7.11.7
^ permalink raw reply related
* [RFC v0 05/15] network: Add network .connect and .disconnect
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Add the btd_profile hooks to connect and disconnect all three network
roles.
---
profiles/network/manager.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 99 insertions(+)
diff --git a/profiles/network/manager.c b/profiles/network/manager.c
index eda0bbe..87d263a 100644
--- a/profiles/network/manager.c
+++ b/profiles/network/manager.c
@@ -47,6 +47,12 @@
static gboolean conf_security = TRUE;
+struct connect_req {
+ struct btd_device *device;
+ struct btd_profile *profile;
+ btd_profile_cb cb;
+};
+
static void read_config(const char *file)
{
GKeyFile *keyfile;
@@ -73,6 +79,57 @@ done:
conf_security ? "true" : "false");
}
+static void connect_profile_cb(struct btd_device *device, int err,
+ const char *pdev, void *data)
+{
+ struct connect_req *req = data;
+
+ req->cb(req->profile, req->device, err);
+
+ g_free(req);
+}
+
+static int connect_profile(struct btd_device *dev, struct btd_profile *profile,
+ uint16_t id, btd_profile_cb cb)
+{
+ struct connect_req *req;
+ int err;
+
+ DBG("path %s id %u", device_get_path(dev), id);
+
+ req = g_new0(struct connect_req, 1);
+ req->device = dev;
+ req->profile = profile;
+ req->cb = cb;
+
+ err = connection_connect(dev, BNEP_SVC_PANU, NULL, connect_profile_cb,
+ req);
+ if (err < 0) {
+ g_free(req);
+ return err;
+ }
+
+ return 0;
+}
+
+static int disconnect_profile(struct btd_device *dev,
+ struct btd_profile *profile,
+ uint16_t id, btd_profile_cb cb)
+{
+ int err;
+
+ DBG("path %s id %u", device_get_path(dev), id);
+
+ err = connection_disconnect(dev, id, NULL);
+ if (err < 0)
+ return err;
+
+ if (cb)
+ cb(profile, dev, 0);
+
+ return 0;
+}
+
static int panu_probe(struct btd_profile *p, struct btd_device *device,
GSList *uuids)
{
@@ -88,6 +145,18 @@ static void network_remove(struct btd_profile *p, struct btd_device *device)
connection_unregister(device);
}
+static int panu_connect(struct btd_device *dev, struct btd_profile *profile,
+ btd_profile_cb cb)
+{
+ return connect_profile(dev, profile, BNEP_SVC_PANU, cb);
+}
+
+static int panu_disconnect(struct btd_device *dev, struct btd_profile *profile,
+ btd_profile_cb cb)
+{
+ return disconnect_profile(dev, profile, BNEP_SVC_PANU, cb);
+}
+
static int panu_server_probe(struct btd_profile *p, struct btd_adapter *adapter)
{
const gchar *path = adapter_get_path(adapter);
@@ -115,6 +184,18 @@ static int gn_probe(struct btd_profile *p, struct btd_device *device,
return connection_register(device, BNEP_SVC_GN);
}
+static int gn_connect(struct btd_device *dev, struct btd_profile *profile,
+ btd_profile_cb cb)
+{
+ return connect_profile(dev, profile, BNEP_SVC_GN, cb);
+}
+
+static int gn_disconnect(struct btd_device *dev, struct btd_profile *profile,
+ btd_profile_cb cb)
+{
+ return disconnect_profile(dev, profile, BNEP_SVC_GN, cb);
+}
+
static int gn_server_probe(struct btd_profile *p, struct btd_adapter *adapter)
{
const gchar *path = adapter_get_path(adapter);
@@ -142,6 +223,18 @@ static int nap_probe(struct btd_profile *p, struct btd_device *device,
return connection_register(device, BNEP_SVC_NAP);
}
+static int nap_connect(struct btd_device *dev, struct btd_profile *profile,
+ btd_profile_cb cb)
+{
+ return connect_profile(dev, profile, BNEP_SVC_NAP, cb);
+}
+
+static int nap_disconnect(struct btd_device *dev, struct btd_profile *profile,
+ btd_profile_cb cb)
+{
+ return disconnect_profile(dev, profile, BNEP_SVC_NAP, cb);
+}
+
static int nap_server_probe(struct btd_profile *p, struct btd_adapter *adapter)
{
const gchar *path = adapter_get_path(adapter);
@@ -166,6 +259,8 @@ static struct btd_profile panu_profile = {
.remote_uuids = BTD_UUIDS(PANU_UUID),
.device_probe = panu_probe,
.device_remove = network_remove,
+ .connect = panu_connect,
+ .disconnect = panu_disconnect,
.adapter_probe = panu_server_probe,
.adapter_remove = panu_server_remove,
};
@@ -175,6 +270,8 @@ static struct btd_profile gn_profile = {
.remote_uuids = BTD_UUIDS(GN_UUID),
.device_probe = gn_probe,
.device_remove = network_remove,
+ .connect = gn_connect,
+ .disconnect = gn_disconnect,
.adapter_probe = gn_server_probe,
.adapter_remove = gn_server_remove,
};
@@ -184,6 +281,8 @@ static struct btd_profile nap_profile = {
.remote_uuids = BTD_UUIDS(NAP_UUID),
.device_probe = nap_probe,
.device_remove = network_remove,
+ .connect = nap_connect,
+ .disconnect = nap_disconnect,
.adapter_probe = nap_server_probe,
.adapter_remove = nap_server_remove,
};
--
1.7.11.7
^ permalink raw reply related
* [RFC v0 06/15] audio: Split A2DP into three btd_profile
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Merging the three audio profiles (AVDTP, A2DP sink and A2DP source)
into one was convenient in the past was doesn't fit very well the new
btd_profile approach. The split is also more consistent with the
approach of the HFP/HSP profile.
---
audio/a2dp.c | 88 ++++++++++++++++++++++-------------------------
audio/a2dp.h | 4 ++-
audio/manager.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++---------
3 files changed, 133 insertions(+), 64 deletions(-)
diff --git a/audio/a2dp.c b/audio/a2dp.c
index 7799420..6bfc8c2 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -1167,73 +1167,67 @@ static struct a2dp_server *find_server(GSList *list, const bdaddr_t *src)
return NULL;
}
-int a2dp_register(const bdaddr_t *src, GKeyFile *config)
+int a2dp_server_register(const bdaddr_t *src, GKeyFile *config)
{
- gboolean source = TRUE, sink = FALSE;
gboolean delay_reporting = FALSE;
- char *str;
- GError *err = NULL;
struct a2dp_server *server;
+ int av_err;
- if (!config)
- goto proceed;
+ server = find_server(servers, src);
+ if (server)
+ return 0;
- str = g_key_file_get_string(config, "General", "Enable", &err);
+ server = g_new0(struct a2dp_server, 1);
- if (err) {
- DBG("audio.conf: %s", err->message);
- g_clear_error(&err);
- } else {
- if (strstr(str, "Sink"))
- source = TRUE;
- if (strstr(str, "Source"))
- sink = TRUE;
- g_free(str);
+ av_err = avdtp_init(src, config, &server->version);
+ if (av_err < 0) {
+ g_free(server);
+ return av_err;
}
- str = g_key_file_get_string(config, "General", "Disable", &err);
+ bacpy(&server->src, src);
+ servers = g_slist_append(servers, server);
- if (err) {
- DBG("audio.conf: %s", err->message);
- g_clear_error(&err);
- } else {
- if (strstr(str, "Sink"))
- source = FALSE;
- if (strstr(str, "Source"))
- sink = FALSE;
- g_free(str);
- }
+ if (config)
+ delay_reporting = g_key_file_get_boolean(config, "A2DP",
+ "DelayReporting", NULL);
-proceed:
+ if (delay_reporting)
+ server->version = 0x0103;
+ else
+ server->version = 0x0102;
+
+ return 0;
+}
+
+int a2dp_source_register(const bdaddr_t *src, GKeyFile *config)
+{
+ struct a2dp_server *server;
server = find_server(servers, src);
if (!server) {
- int av_err;
+ DBG("AVDTP not registered");
+ return -EPROTONOSUPPORT;
- server = g_new0(struct a2dp_server, 1);
+ }
- av_err = avdtp_init(src, config, &server->version);
- if (av_err < 0) {
- g_free(server);
- return av_err;
- }
+ server->source_enabled = TRUE;
- bacpy(&server->src, src);
- servers = g_slist_append(servers, server);
- }
+ return 0;
+}
- if (config)
- delay_reporting = g_key_file_get_boolean(config, "A2DP",
- "DelayReporting", NULL);
+int a2dp_sink_register(const bdaddr_t *src, GKeyFile *config)
+{
+ struct a2dp_server *server;
- if (delay_reporting)
- server->version = 0x0103;
- else
- server->version = 0x0102;
+ server = find_server(servers, src);
+ if (!server) {
+ DBG("AVDTP not registered");
+ return -EPROTONOSUPPORT;
- server->source_enabled = source;
+ }
- server->sink_enabled = sink;
+ server->sink_enabled = TRUE;
return 0;
}
diff --git a/audio/a2dp.h b/audio/a2dp.h
index 736bc66..c471499 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -64,7 +64,9 @@ typedef void (*a2dp_stream_cb_t) (struct avdtp *session,
struct avdtp_error *err,
void *user_data);
-int a2dp_register(const bdaddr_t *src, GKeyFile *config);
+int a2dp_server_register(const bdaddr_t *src, GKeyFile *config);
+int a2dp_source_register(const bdaddr_t *src, GKeyFile *config);
+int a2dp_sink_register(const bdaddr_t *src, GKeyFile *config);
void a2dp_unregister(const bdaddr_t *src);
struct a2dp_sep *a2dp_add_sep(const bdaddr_t *src, uint8_t type,
diff --git a/audio/manager.c b/audio/manager.c
index 4ea61bf..c5d295e 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -722,7 +722,7 @@ static int ag_probe(struct btd_profile *p, struct btd_device *device,
return 0;
}
-static int a2dp_probe(struct btd_profile *p, struct btd_device *device,
+static int a2dp_source_probe(struct btd_profile *p, struct btd_device *device,
GSList *uuids)
{
struct audio_device *audio_dev;
@@ -733,13 +733,23 @@ static int a2dp_probe(struct btd_profile *p, struct btd_device *device,
return -1;
}
- if (g_slist_find_custom(uuids, A2DP_SINK_UUID, bt_uuid_strcmp) &&
- audio_dev->sink == NULL)
- audio_dev->sink = sink_init(audio_dev);
+ audio_dev->source = source_init(audio_dev);
- if (g_slist_find_custom(uuids, A2DP_SOURCE_UUID, bt_uuid_strcmp) &&
- audio_dev->source == NULL)
- audio_dev->source = source_init(audio_dev);
+ return 0;
+}
+
+static int a2dp_sink_probe(struct btd_profile *p, struct btd_device *device,
+ GSList *uuids)
+{
+ struct audio_device *audio_dev;
+
+ audio_dev = get_audio_dev(device);
+ if (!audio_dev) {
+ DBG("unable to get a device object");
+ return -1;
+ }
+
+ audio_dev->sink = sink_init(audio_dev);
return 0;
}
@@ -975,7 +985,7 @@ static int a2dp_server_probe(struct btd_profile *p,
if (!adp)
return -EINVAL;
- err = a2dp_register(adapter_get_address(adapter), config);
+ err = a2dp_server_register(adapter_get_address(adapter), config);
if (err < 0)
audio_adapter_unref(adp);
@@ -998,6 +1008,40 @@ static void a2dp_server_remove(struct btd_profile *p,
audio_adapter_unref(adp);
}
+static int a2dp_source_server_probe(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ adp = audio_adapter_get(adapter);
+ if (!adp)
+ return -EINVAL;
+
+ audio_adapter_unref(adp); /* Referenced by a2dp server */
+
+ return a2dp_source_register(adapter_get_address(adapter), config);
+}
+
+static int a2dp_sink_server_probe(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ adp = audio_adapter_get(adapter);
+ if (!adp)
+ return -EINVAL;
+
+ audio_adapter_unref(adp); /* Referenced by a2dp server */
+
+ return a2dp_sink_register(adapter_get_address(adapter), config);
+}
+
static int avrcp_server_probe(struct btd_profile *p,
struct btd_adapter *adapter)
{
@@ -1090,18 +1134,35 @@ static struct btd_profile gateway_profile = {
.adapter_remove = gateway_server_remove,
};
-static struct btd_profile a2dp_profile = {
- .name = "audio-a2dp",
+static struct btd_profile avdtp_profile = {
+ .name = "audio-avdtp",
- .remote_uuids = BTD_UUIDS(A2DP_SOURCE_UUID, A2DP_SINK_UUID,
- ADVANCED_AUDIO_UUID),
- .device_probe = a2dp_probe,
- .device_remove = audio_remove,
+ .remote_uuids = BTD_UUIDS(ADVANCED_AUDIO_UUID),
.adapter_probe = a2dp_server_probe,
.adapter_remove = a2dp_server_remove,
};
+static struct btd_profile a2dp_source_profile = {
+ .name = "audio-source",
+
+ .remote_uuids = BTD_UUIDS(A2DP_SOURCE_UUID),
+ .device_probe = a2dp_source_probe,
+ .device_remove = audio_remove,
+
+ .adapter_probe = a2dp_source_server_probe,
+};
+
+static struct btd_profile a2dp_sink_profile = {
+ .name = "audio-sink",
+
+ .remote_uuids = BTD_UUIDS(A2DP_SINK_UUID),
+ .device_probe = a2dp_sink_probe,
+ .device_remove = audio_remove,
+
+ .adapter_probe = a2dp_sink_server_probe,
+};
+
static struct btd_profile avrcp_profile = {
.name = "audio-avrcp",
@@ -1194,7 +1255,13 @@ proceed:
btd_profile_register(&gateway_profile);
if (enabled.source || enabled.sink)
- btd_profile_register(&a2dp_profile);
+ btd_profile_register(&avdtp_profile);
+
+ if (enabled.source)
+ btd_profile_register(&a2dp_source_profile);
+
+ if (enabled.sink)
+ btd_profile_register(&a2dp_sink_profile);
if (enabled.control)
btd_profile_register(&avrcp_profile);
@@ -1219,8 +1286,14 @@ void audio_manager_exit(void)
if (enabled.gateway)
btd_profile_unregister(&gateway_profile);
+ if (enabled.source)
+ btd_profile_unregister(&a2dp_source_profile);
+
+ if (enabled.sink)
+ btd_profile_unregister(&a2dp_sink_profile);
+
if (enabled.source || enabled.sink)
- btd_profile_unregister(&a2dp_profile);
+ btd_profile_unregister(&avdtp_profile);
if (enabled.control)
btd_profile_unregister(&avrcp_profile);
--
1.7.11.7
^ permalink raw reply related
* [RFC v0 07/15] audio: Trivial function rename
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Use the local_ name prefix for local functions implementing the D-Bus
API.
---
audio/sink.c | 12 ++++++------
audio/source.c | 12 ++++++------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/audio/sink.c b/audio/sink.c
index 2e63579..b9e4fe0 100644
--- a/audio/sink.c
+++ b/audio/sink.c
@@ -390,7 +390,7 @@ gboolean sink_setup_stream(struct sink *sink, struct avdtp *session)
return TRUE;
}
-static DBusMessage *sink_connect(DBusConnection *conn,
+static DBusMessage *local_connect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct audio_device *dev = data;
@@ -423,7 +423,7 @@ static DBusMessage *sink_connect(DBusConnection *conn,
return NULL;
}
-static DBusMessage *sink_disconnect(DBusConnection *conn,
+static DBusMessage *local_disconnect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct audio_device *device = data;
@@ -457,7 +457,7 @@ static DBusMessage *sink_disconnect(DBusConnection *conn,
return NULL;
}
-static DBusMessage *sink_get_properties(DBusConnection *conn,
+static DBusMessage *local_get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct audio_device *device = data;
@@ -489,11 +489,11 @@ static DBusMessage *sink_get_properties(DBusConnection *conn,
}
static const GDBusMethodTable sink_methods[] = {
- { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, sink_connect) },
- { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, sink_disconnect) },
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, local_connect) },
+ { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, local_disconnect) },
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
- sink_get_properties) },
+ local_get_properties) },
{ }
};
diff --git a/audio/source.c b/audio/source.c
index 1d0c74a..04971d9 100644
--- a/audio/source.c
+++ b/audio/source.c
@@ -379,7 +379,7 @@ gboolean source_setup_stream(struct source *source, struct avdtp *session)
return TRUE;
}
-static DBusMessage *source_connect(DBusConnection *conn,
+static DBusMessage *local_connect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct audio_device *dev = data;
@@ -412,7 +412,7 @@ static DBusMessage *source_connect(DBusConnection *conn,
return NULL;
}
-static DBusMessage *source_disconnect(DBusConnection *conn,
+static DBusMessage *local_disconnect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct audio_device *device = data;
@@ -446,7 +446,7 @@ static DBusMessage *source_disconnect(DBusConnection *conn,
return NULL;
}
-static DBusMessage *source_get_properties(DBusConnection *conn,
+static DBusMessage *local_get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct audio_device *device = data;
@@ -478,11 +478,11 @@ static DBusMessage *source_get_properties(DBusConnection *conn,
}
static const GDBusMethodTable source_methods[] = {
- { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, source_connect) },
- { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, source_disconnect) },
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, local_connect) },
+ { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, local_disconnect) },
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
- source_get_properties) },
+ local_get_properties) },
{ }
};
--
1.7.11.7
^ permalink raw reply related
* [RFC v0 08/15] source: Expose internal connection API
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Separate the D-Bus code from the internal connection handling code,
exposing an internal API in case some internal codepath/plugin is
interested in using it.
---
audio/device.h | 3 ++
audio/source.c | 146 ++++++++++++++++++++++++++++++++-------------------------
audio/source.h | 2 +
3 files changed, 88 insertions(+), 63 deletions(-)
diff --git a/audio/device.h b/audio/device.h
index 1e2cac1..41e5635 100644
--- a/audio/device.h
+++ b/audio/device.h
@@ -22,6 +22,7 @@
*
*/
+struct audio_device;
struct source;
struct control;
struct target;
@@ -30,6 +31,8 @@ struct headset;
struct gateway;
struct dev_priv;
+typedef void (*audio_device_cb) (struct audio_device *dev, int err, void *data);
+
struct audio_device {
struct btd_device *btd_dev;
diff --git a/audio/source.c b/audio/source.c
index 04971d9..e48e308 100644
--- a/audio/source.c
+++ b/audio/source.c
@@ -53,7 +53,8 @@
#define STREAM_SETUP_RETRY_TIMER 2
struct pending_request {
- DBusMessage *msg;
+ audio_device_cb cb;
+ void *cb_data;
unsigned int id;
};
@@ -152,10 +153,12 @@ static void avdtp_state_callback(struct audio_device *dev,
}
static void pending_request_free(struct audio_device *dev,
- struct pending_request *pending)
+ struct pending_request *pending,
+ int err)
{
- if (pending->msg)
- dbus_message_unref(pending->msg);
+ if (pending->cb)
+ pending->cb(dev, err, pending->cb_data);
+
if (pending->id)
a2dp_cancel(dev, pending->id);
@@ -177,15 +180,12 @@ static void stream_state_changed(struct avdtp_stream *stream,
switch (new_state) {
case AVDTP_STATE_IDLE:
if (source->disconnect) {
- DBusMessage *reply;
struct pending_request *p;
p = source->disconnect;
source->disconnect = NULL;
- reply = dbus_message_new_method_return(p->msg);
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- pending_request_free(dev, p);
+ pending_request_free(dev, p, 0);
}
if (source->session) {
@@ -211,35 +211,24 @@ static void stream_state_changed(struct avdtp_stream *stream,
source->stream_state = new_state;
}
-static void error_failed(DBusMessage *msg,
- const char *desc)
-{
- DBusMessage *reply = btd_error_failed(msg, desc);
- g_dbus_send_message(btd_get_dbus_connection(), reply);
-}
-
static gboolean stream_setup_retry(gpointer user_data)
{
struct source *source = user_data;
struct pending_request *pending = source->connect;
+ int err;
source->retry_id = 0;
if (source->stream_state >= AVDTP_STATE_OPEN) {
DBG("Stream successfully created, after XCASE connect:connect");
- if (pending->msg) {
- DBusMessage *reply;
- reply = dbus_message_new_method_return(pending->msg);
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- }
+ err = 0;
} else {
DBG("Stream setup failed, after XCASE connect:connect");
- if (pending->msg)
- error_failed(pending->msg, "Stream setup failed");
+ err = -EIO;
}
source->connect = NULL;
- pending_request_free(source->dev, pending);
+ pending_request_free(source->dev, pending, err);
return FALSE;
}
@@ -258,14 +247,8 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
if (stream) {
DBG("Stream successfully created");
- if (pending->msg) {
- DBusMessage *reply;
- reply = dbus_message_new_method_return(pending->msg);
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- }
-
source->connect = NULL;
- pending_request_free(source->dev, pending);
+ pending_request_free(source->dev, pending, 0);
return;
}
@@ -279,10 +262,8 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
stream_setup_retry,
source);
} else {
- if (pending->msg)
- error_failed(pending->msg, "Stream setup failed");
source->connect = NULL;
- pending_request_free(source->dev, pending);
+ pending_request_free(source->dev, pending, -EIO);
DBG("Stream setup failed : %s", avdtp_strerror(err));
}
}
@@ -309,9 +290,7 @@ static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
return;
failed:
- if (pending->msg)
- error_failed(pending->msg, "Stream setup failed");
- pending_request_free(source->dev, pending);
+ pending_request_free(source->dev, pending, -EIO);
source->connect = NULL;
avdtp_unref(source->session);
source->session = NULL;
@@ -352,9 +331,7 @@ static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp
return;
failed:
- if (pending->msg)
- error_failed(pending->msg, "Stream setup failed");
- pending_request_free(source->dev, pending);
+ pending_request_free(source->dev, pending, -EIO);
source->connect = NULL;
avdtp_unref(source->session);
source->session = NULL;
@@ -379,71 +356,114 @@ gboolean source_setup_stream(struct source *source, struct avdtp *session)
return TRUE;
}
+static void generic_cb(struct audio_device *dev, int err, void *data)
+{
+ DBusMessage *msg = data;
+ DBusMessage *reply;
+
+ if (err < 0) {
+ reply = btd_error_failed(msg, strerror(-err));
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ dbus_message_unref(msg);
+ return;
+ }
+
+ g_dbus_send_reply(btd_get_dbus_connection(), msg, DBUS_TYPE_INVALID);
+
+ dbus_message_unref(msg);
+}
+
static DBusMessage *local_connect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct audio_device *dev = data;
+ int err;
+
+ err = source_connect(dev, generic_cb, msg);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ dbus_message_ref(msg);
+
+ return NULL;
+}
+
+int source_connect(struct audio_device *dev, audio_device_cb cb, void *data)
+{
struct source *source = dev->source;
struct pending_request *pending;
if (!source->session)
source->session = avdtp_get(&dev->src, &dev->dst);
- if (!source->session)
- return btd_error_failed(msg, "Unable to get a session");
+ if (!source->session) {
+ DBG("Unable to get a session");
+ return -EIO;
+ }
if (source->connect || source->disconnect)
- return btd_error_busy(msg);
+ return -EBUSY;
if (source->stream_state >= AVDTP_STATE_OPEN)
- return btd_error_already_connected(msg);
+ return -EALREADY;
- if (!source_setup_stream(source, NULL))
- return btd_error_failed(msg, "Failed to create a stream");
+ if (!source_setup_stream(source, NULL)) {
+ DBG("Failed to create a stream");
+ return -EIO;
+ }
dev->auto_connect = FALSE;
pending = source->connect;
-
- pending->msg = dbus_message_ref(msg);
+ pending->cb = cb;
+ pending->cb_data = data;
DBG("stream creation in progress");
- return NULL;
+ return 0;
}
static DBusMessage *local_disconnect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
+ struct audio_device *dev = data;
+ int err;
+
+ err = source_disconnect(dev, generic_cb, msg);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ dbus_message_ref(msg);
+
+ return NULL;
+}
+
+int source_disconnect(struct audio_device *dev, audio_device_cb cb, void *data)
+{
struct audio_device *device = data;
struct source *source = device->source;
struct pending_request *pending;
int err;
if (!source->session)
- return btd_error_not_connected(msg);
+ return -ENOTCONN;
if (source->connect || source->disconnect)
- return btd_error_busy(msg);
+ return -EBUSY;
- if (source->stream_state < AVDTP_STATE_OPEN) {
- DBusMessage *reply = dbus_message_new_method_return(msg);
- if (!reply)
- return NULL;
- avdtp_unref(source->session);
- source->session = NULL;
- return reply;
- }
+ if (source->stream_state < AVDTP_STATE_OPEN)
+ return -ENOTCONN;
err = avdtp_close(source->session, source->stream, FALSE);
if (err < 0)
- return btd_error_failed(msg, strerror(-err));
+ return err;
pending = g_new0(struct pending_request, 1);
- pending->msg = dbus_message_ref(msg);
+ pending->cb = cb;
+ pending->cb_data = data;
source->disconnect = pending;
- return NULL;
+ return 0;
}
static DBusMessage *local_get_properties(DBusConnection *conn,
@@ -504,10 +524,10 @@ static void source_free(struct audio_device *dev)
avdtp_unref(source->session);
if (source->connect)
- pending_request_free(dev, source->connect);
+ pending_request_free(dev, source->connect, -ECANCELED);
if (source->disconnect)
- pending_request_free(dev, source->disconnect);
+ pending_request_free(dev, source->disconnect, -ECANCELED);
if (source->retry_id)
g_source_remove(source->retry_id);
diff --git a/audio/source.h b/audio/source.h
index 695bded..dadb392 100644
--- a/audio/source.h
+++ b/audio/source.h
@@ -44,6 +44,8 @@ struct source *source_init(struct audio_device *dev);
void source_unregister(struct audio_device *dev);
gboolean source_is_active(struct audio_device *dev);
source_state_t source_get_state(struct audio_device *dev);
+int source_connect(struct audio_device *dev, audio_device_cb cb, void *data);
+int source_disconnect(struct audio_device *dev, audio_device_cb cb, void *data);
gboolean source_new_stream(struct audio_device *dev, struct avdtp *session,
struct avdtp_stream *stream);
gboolean source_setup_stream(struct source *source, struct avdtp *session);
--
1.7.11.7
^ permalink raw reply related
* [RFC v0 09/15] source: Add profile .connect and .disconnect
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Add the connection and disconnection hooks to the a2dp_source
btd_profile.
---
audio/manager.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 82 insertions(+)
diff --git a/audio/manager.c b/audio/manager.c
index c5d295e..77e3540 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -83,6 +83,12 @@ struct audio_adapter {
gint ref;
};
+struct profile_req {
+ struct btd_device *device;
+ struct btd_profile *profile;
+ btd_profile_cb cb;
+};
+
static gboolean auto_connect = TRUE;
static int max_connected_headsets = 1;
static GKeyFile *config = NULL;
@@ -776,6 +782,79 @@ static int avrcp_probe(struct btd_profile *p, struct btd_device *device,
return 0;
}
+static struct profile_req *new_profile_request(struct btd_device *dev,
+ struct btd_profile *profile,
+ btd_profile_cb cb)
+{
+ struct profile_req *req;
+
+ req = g_new0(struct profile_req, 1);
+ req->device = dev;
+ req->profile = profile;
+ req->cb = cb;
+
+ return req;
+}
+
+static void profile_cb(struct audio_device *dev, int err, void *data)
+{
+ struct profile_req *req = data;
+
+ req->cb(req->profile, req->device, err);
+
+ g_free(req);
+}
+
+static int a2dp_source_connect(struct btd_device *dev,
+ struct btd_profile *profile,
+ btd_profile_cb cb)
+{
+ struct audio_device *audio_dev;
+ struct profile_req *req;
+ int err;
+
+ audio_dev = get_audio_dev(dev);
+ if (!audio_dev) {
+ DBG("unable to get a device object");
+ return -1;
+ }
+
+ req = new_profile_request(dev, profile, cb);
+
+ err = source_connect(audio_dev, profile_cb, req);
+ if (err < 0) {
+ g_free(req);
+ return err;
+ }
+
+ return 0;
+}
+
+static int a2dp_source_disconnect(struct btd_device *dev,
+ struct btd_profile *profile,
+ btd_profile_cb cb)
+{
+ struct audio_device *audio_dev;
+ struct profile_req *req;
+ int err;
+
+ audio_dev = get_audio_dev(dev);
+ if (!audio_dev) {
+ DBG("unable to get a device object");
+ return -1;
+ }
+
+ req = new_profile_request(dev, profile, cb);
+
+ err = source_disconnect(audio_dev, profile_cb, req);
+ if (err < 0) {
+ g_free(req);
+ return err;
+ }
+
+ return 0;
+}
+
static struct audio_adapter *audio_adapter_ref(struct audio_adapter *adp)
{
adp->ref++;
@@ -1150,6 +1229,9 @@ static struct btd_profile a2dp_source_profile = {
.device_probe = a2dp_source_probe,
.device_remove = audio_remove,
+ .connect = a2dp_source_connect,
+ .disconnect = a2dp_source_disconnect,
+
.adapter_probe = a2dp_source_server_probe,
};
--
1.7.11.7
^ permalink raw reply related
* [RFC v0 10/15] input: Trivial function rename
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Use the local_ name prefix for functions implementing the D-Bus API.
---
profiles/input/device.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/profiles/input/device.c b/profiles/input/device.c
index 9dd8002..7df92e2 100644
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -700,7 +700,7 @@ static DBusMessage *local_connect(DBusConnection *conn, DBusMessage *msg,
return NULL;
}
-static DBusMessage *input_device_disconnect(DBusConnection *conn,
+static DBusMessage *local_disconnect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct input_device *idev = data;
@@ -742,7 +742,7 @@ static const GDBusMethodTable device_methods[] = {
{ GDBUS_ASYNC_METHOD("Connect",
NULL, NULL, local_connect) },
{ GDBUS_METHOD("Disconnect",
- NULL, NULL, input_device_disconnect) },
+ NULL, NULL, local_disconnect) },
{ }
};
--
1.7.11.7
^ permalink raw reply related
* [RFC v0 11/15] input: Expose internal disconnection API
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Expose the same API exposed in D-Bus to disconnect devices.
---
profiles/input/device.c | 20 ++++++++++++++++++++
profiles/input/device.h | 2 ++
2 files changed, 22 insertions(+)
diff --git a/profiles/input/device.c b/profiles/input/device.c
index 7df92e2..fbc3d6f 100644
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -700,6 +700,26 @@ static DBusMessage *local_connect(DBusConnection *conn, DBusMessage *msg,
return NULL;
}
+int input_device_disconnect(struct btd_device *dev, struct btd_profile *profile,
+ btd_profile_cb cb)
+{
+ struct input_device *idev;
+ int err;
+
+ idev = find_device_by_path(devices, device_get_path(dev));
+ if (!idev)
+ return -ENOENT;
+
+ err = connection_disconnect(idev, 0);
+ if (err < 0)
+ return err;
+
+ if (cb)
+ cb(profile, dev, 0);
+
+ return 0;
+}
+
static DBusMessage *local_disconnect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
diff --git a/profiles/input/device.h b/profiles/input/device.h
index be1f830..d68798c 100644
--- a/profiles/input/device.h
+++ b/profiles/input/device.h
@@ -38,3 +38,5 @@ int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst);
int input_device_connect(struct btd_device *dev, struct btd_profile *profile,
btd_profile_cb cb);
+int input_device_disconnect(struct btd_device *dev, struct btd_profile *profile,
+ btd_profile_cb cb);
--
1.7.11.7
^ permalink raw reply related
* [RFC v0 12/15] input: Add profile .disconnect
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
Add the disconnect hook to the btd_profile.
---
profiles/input/manager.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/profiles/input/manager.c b/profiles/input/manager.c
index a2690b5..71fd48f 100644
--- a/profiles/input/manager.c
+++ b/profiles/input/manager.c
@@ -90,6 +90,7 @@ static struct btd_profile input_profile = {
.auto_connect = true,
.connect = input_device_connect,
+ .disconnect = input_device_disconnect,
.device_probe = hid_device_probe,
.device_remove = hid_device_remove,
--
1.7.11.7
^ permalink raw reply related
* [RFC v0 13/15] profile: Rename org.bluez.Profile->ProfileAgent
From: Mikel Astiz @ 2012-10-19 15:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1350661172-18125-1-git-send-email-mikel.astiz.oss@gmail.com>
From: Mikel Astiz <mikel.astiz@bmw-carit.de>
The interface represents an external component supporting a specific
profile by implementing an agent. Therefore ProfileAgent is a more
accurate name for the D-Bus interface.
---
doc/manager-api.txt | 3 ++-
doc/profile-api.txt | 53 ------------------------------------------------
doc/profileagent-api.txt | 53 ++++++++++++++++++++++++++++++++++++++++++++++++
src/bluetooth.conf | 2 +-
src/manager.c | 2 +-
src/profile.c | 11 +++++-----
test/test-profile | 8 ++++----
7 files changed, 67 insertions(+), 65 deletions(-)
delete mode 100644 doc/profile-api.txt
create mode 100644 doc/profileagent-api.txt
diff --git a/doc/manager-api.txt b/doc/manager-api.txt
index fe50556..16fe4ee 100644
--- a/doc/manager-api.txt
+++ b/doc/manager-api.txt
@@ -29,7 +29,8 @@ Object path /
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.NoSuchAdapter
- void RegisterProfile(object profile, string uuid, dict options)
+ void RegisterProfileAgent(object profile, string uuid,
+ dict options)
This registers a profile implementation.
diff --git a/doc/profile-api.txt b/doc/profile-api.txt
deleted file mode 100644
index 639202f..0000000
--- a/doc/profile-api.txt
+++ /dev/null
@@ -1,53 +0,0 @@
-BlueZ D-Bus Profile API description
-***********************************
-
-Copyright (C) 2012 Intel Corporation. All rights reserved.
-
-
-Profile hierarchy
-=================
-
-Service unique name
-Interface org.bluez.Profile
-Object path freely definable
-
-Methods void Release()
-
- This method gets called when the service daemon
- unregisters the profile. A profile can use it to do
- cleanup tasks. There is no need to unregister the
- profile, because when this method gets called it has
- already been unregistered.
-
- void NewConnection(object device, fd)
-
- This method gets called when a new service level
- connection has been made and authorized.
-
- Possible errors: org.bluez.Error.Rejected
- org.bluez.Error.Canceled
-
- void RequestDisconnection(object device)
-
- This method gets called when a profile gets
- disconnected.
-
- The file descriptor is no longer owned by the service
- daemon and the profile implementation needs to take
- care of cleaning up all connections.
-
- If multiple file descriptors are indicated via
- NewConnection, it is expected that all of them
- are disconnected before returning from this
- method call.
-
- Possible errors: org.bluez.Error.Rejected
- org.bluez.Error.Canceled
-
- void Cancel()
-
- This method gets called to indicate that the profile
- request failed before a reply was returned.
-
- All request are queued and there will be only one
- pending pequest at a time per profile.
diff --git a/doc/profileagent-api.txt b/doc/profileagent-api.txt
new file mode 100644
index 0000000..8a27a01
--- /dev/null
+++ b/doc/profileagent-api.txt
@@ -0,0 +1,53 @@
+BlueZ D-Bus Profile API description
+***********************************
+
+Copyright (C) 2012 Intel Corporation. All rights reserved.
+
+
+Profile hierarchy
+=================
+
+Service unique name
+Interface org.bluez.ProfileAgent
+Object path freely definable
+
+Methods void Release()
+
+ This method gets called when the service daemon
+ unregisters the profile. A profile can use it to do
+ cleanup tasks. There is no need to unregister the
+ profile, because when this method gets called it has
+ already been unregistered.
+
+ void NewConnection(object device, fd)
+
+ This method gets called when a new service level
+ connection has been made and authorized.
+
+ Possible errors: org.bluez.Error.Rejected
+ org.bluez.Error.Canceled
+
+ void RequestDisconnection(object device)
+
+ This method gets called when a profile gets
+ disconnected.
+
+ The file descriptor is no longer owned by the service
+ daemon and the profile implementation needs to take
+ care of cleaning up all connections.
+
+ If multiple file descriptors are indicated via
+ NewConnection, it is expected that all of them
+ are disconnected before returning from this
+ method call.
+
+ Possible errors: org.bluez.Error.Rejected
+ org.bluez.Error.Canceled
+
+ void Cancel()
+
+ This method gets called to indicate that the profile
+ request failed before a reply was returned.
+
+ All request are queued and there will be only one
+ pending pequest at a time per profile.
diff --git a/src/bluetooth.conf b/src/bluetooth.conf
index 2db43d9..49dafc8 100644
--- a/src/bluetooth.conf
+++ b/src/bluetooth.conf
@@ -17,7 +17,7 @@
<allow send_interface="org.bluez.Watcher"/>
<allow send_interface="org.bluez.ThermometerWatcher"/>
<allow send_interface="org.bluez.AlertAgent"/>
- <allow send_interface="org.bluez.Profile"/>
+ <allow send_interface="org.bluez.ProfileAgent"/>
<allow send_interface="org.bluez.HeartRateWatcher"/>
</policy>
diff --git a/src/manager.c b/src/manager.c
index a96115b..1dc1c54 100644
--- a/src/manager.c
+++ b/src/manager.c
@@ -163,7 +163,7 @@ static const GDBusMethodTable manager_methods[] = {
GDBUS_ARGS({ "pattern", "s" }),
GDBUS_ARGS({ "adapter", "o" }),
find_adapter) },
- { GDBUS_METHOD("RegisterProfile",
+ { GDBUS_METHOD("RegisterProfileAgent",
GDBUS_ARGS({ "profile", "o"}, { "UUID", "s" },
{ "options", "a{sv}" }),
NULL, btd_profile_reg_ext) },
diff --git a/src/profile.c b/src/profile.c
index eb63e1e..7e7c945 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -157,7 +157,8 @@ static void ext_cancel(struct ext_profile *ext)
DBusMessage *msg;
msg = dbus_message_new_method_call(ext->owner, ext->path,
- "org.bluez.Profile", "Cancel");
+ "org.bluez.ProfileAgent",
+ "Cancel");
if (msg)
g_dbus_send_message(btd_get_dbus_connection(), msg);
}
@@ -274,8 +275,8 @@ static bool send_new_connection(struct ext_profile *ext, struct ext_io *conn,
int fd;
msg = dbus_message_new_method_call(ext->owner, ext->path,
- "org.bluez.Profile",
- "NewConnection");
+ "org.bluez.ProfileAgent",
+ "NewConnection");
if (!msg) {
error("Unable to create NewConnection call for %s", ext->name);
return false;
@@ -1108,8 +1109,8 @@ void btd_profile_cleanup(void)
ext->conns = NULL;
msg = dbus_message_new_method_call(ext->owner, ext->path,
- "org.bluez.Profile",
- "Release");
+ "org.bluez.ProfileAgent",
+ "Release");
if (msg)
g_dbus_send_message(conn, msg);
diff --git a/test/test-profile b/test/test-profile
index 2d66444..cfc9331 100755
--- a/test/test-profile
+++ b/test/test-profile
@@ -12,18 +12,18 @@ import dbus.mainloop.glib
from optparse import OptionParser, make_option
class Profile(dbus.service.Object):
- @dbus.service.method("org.bluez.Profile",
+ @dbus.service.method("org.bluez.ProfileAgent",
in_signature="", out_signature="")
def Release(self):
print("Release")
mainloop.quit()
- @dbus.service.method("org.bluez.Profile",
+ @dbus.service.method("org.bluez.ProfileAgent",
in_signature="", out_signature="")
def Cancel(self):
print("Cancel")
- @dbus.service.method("org.bluez.Profile",
+ @dbus.service.method("org.bluez.ProfileAgent",
in_signature="oh", out_signature="")
def NewConnection(self, path, fd):
fd = fd.take()
@@ -84,6 +84,6 @@ if __name__ == '__main__':
if (options.channel):
opts["Channel"] = dbus.UInt16(options.channel)
- manager.RegisterProfile(options.path, options.uuid, opts)
+ manager.RegisterProfileAgent(options.path, options.uuid, opts)
mainloop.run()
--
1.7.11.7
^ 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