2012-11-13 18:20:24

by Frédéric DALLEAU

[permalink] [raw]
Subject: [RFC] sco: BT_DEFER_SETUP for SCO sockets

Hi,

This is a first draft about what can be done to implement DEFER_SETUP on SCO
sockets. I couldn't test it today I just pulled bluetooth-next and build
failed, so I plan to go further tomorrow.

hci layer get some changes since previous behavior was to accept all SCO
connections.

Some questions are open :
Should we be able to defer non eSCO capable devices? This would remove a few
lines of code.
hci_proto_defered is ugly: What about a flag similar to LM_ACCEPT called
LM_DEFER that would indicate that accepting is defered.

Let me know what you think.

Best regards,
Frédéric


Frédéric Dalleau (1):
Bluetooth: Add BT_DEFER_SETUP option to sco socket

include/net/bluetooth/hci_core.h | 15 +++++++
net/bluetooth/hci_event.c | 47 +++++++++++++++++++-
net/bluetooth/sco.c | 88 ++++++++++++++++++++++++++++++++++++--
3 files changed, 145 insertions(+), 5 deletions(-)

--
1.7.9.5



2012-11-16 12:10:30

by Frédéric DALLEAU

[permalink] [raw]
Subject: Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket

Hi Gustavo,

On 11/15/2012 09:30 PM, Gustavo Padovan wrote:
>> mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
>> + defered = hci_proto_defered(hdev, &ev->bdaddr, ev->link_type);
>
> I'm not really happy with this hci_proto thing. Weed need to think more on
> this.

Since hci_proto_connect_ind returns an int, what about using 16 bits for
HCI_LM_* flags, and 16 bits for additional flags (from hci_core.h),
including one to indicate deferred setup.

Fr?d?ric

2012-11-15 20:30:29

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket

Hi Fr?d?ric,

* Fr?d?ric Dalleau <[email protected]> [2012-11-13 19:20:25 +0100]:

> In order to authenticate and later configure an incoming SCO connection, the
> BT_DEFER_SETUP option is added.
> When an connection is requested, the listening socket is unblocked but the
> effective connection setup happens only on first recv.
> Any send between accept and recv fails with -ENOTCONN.
> ---
> include/net/bluetooth/hci_core.h | 15 +++++++
> net/bluetooth/hci_event.c | 47 +++++++++++++++++++-
> net/bluetooth/sco.c | 88 ++++++++++++++++++++++++++++++++++++--
> 3 files changed, 145 insertions(+), 5 deletions(-)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index ef5b85d..2ee0ecb 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -377,6 +377,7 @@ extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb,
> u16 flags);
>
> extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
> +extern int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr);
> extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
> extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
> extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
> @@ -577,6 +578,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
> int hci_conn_del(struct hci_conn *conn);
> void hci_conn_hash_flush(struct hci_dev *hdev);
> void hci_conn_check_pending(struct hci_dev *hdev);
> +void hci_conn_accept(struct hci_conn *conn, int mask);
>
> struct hci_chan *hci_chan_create(struct hci_conn *conn);
> void hci_chan_del(struct hci_chan *chan);
> @@ -796,6 +798,19 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
> }
> }
>
> +static inline int hci_proto_defered(struct hci_dev *hdev, bdaddr_t *bdaddr,
> + __u8 type)
> +{
> + switch (type) {
> + case SCO_LINK:
> + case ESCO_LINK:
> + return sco_defer(hdev, bdaddr);
> +
> + default:
> + return 0;
> + }
> +}
> +
> static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
> {
> switch (conn->type) {
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index 9f5c5f2..167eca0 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -2047,15 +2047,54 @@ unlock:
> hci_conn_check_pending(hdev);
> }
>
> +void hci_conn_accept(struct hci_conn *conn, int mask)
> +{
> + struct hci_dev *hdev = conn->hdev;
> +
> + BT_DBG("conn %p", conn);
> +
> + if (!lmp_esco_capable(hdev)) {
> + struct hci_cp_accept_conn_req cp;
> +
> + conn->state = BT_CONNECT;
> + bacpy(&cp.bdaddr, &conn->dst);
> +
> + if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
> + cp.role = 0x00; /* Become master */
> + else
> + cp.role = 0x01; /* Remain slave */
> +
> + hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
> + &cp);
> + } else /* lmp_esco_capable(hdev)) */ {
> + struct hci_cp_accept_sync_conn_req cp;
> +
> + conn->state = BT_CONNECT;
> + bacpy(&cp.bdaddr, &conn->dst);
> + cp.pkt_type = cpu_to_le16(conn->pkt_type);
> +
> + cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
> + cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
> + cp.max_latency = __constant_cpu_to_le16(0xffff);
> + cp.content_format = cpu_to_le16(hdev->voice_setting);
> + cp.retrans_effort = 0xff;
> +
> + hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
> + sizeof(cp), &cp);
> + }
> +}
> +
> static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
> {
> struct hci_ev_conn_request *ev = (void *) skb->data;
> int mask = hdev->link_mode;
> + int defered;
>
> BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
> ev->link_type);
>
> mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
> + defered = hci_proto_defered(hdev, &ev->bdaddr, ev->link_type);

I'm not really happy with this hci_proto thing. Weed need to think more on
this.

>
> if ((mask & HCI_LM_ACCEPT) &&
> !hci_blacklist_lookup(hdev, &ev->bdaddr)) {
> @@ -2085,7 +2124,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
>
> hci_dev_unlock(hdev);
>
> - if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) {
> + if (ev->link_type == ACL_LINK ||
> + (!defered && !lmp_esco_capable(hdev))) {
> struct hci_cp_accept_conn_req cp;
>
> bacpy(&cp.bdaddr, &ev->bdaddr);
> @@ -2097,7 +2137,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
>
> hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
> &cp);
> - } else {
> + } else if (!defered) {
> struct hci_cp_accept_sync_conn_req cp;
>
> bacpy(&cp.bdaddr, &ev->bdaddr);
> @@ -2111,6 +2151,9 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
>
> hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
> sizeof(cp), &cp);
> + } else {
> + hci_proto_connect_cfm(conn, 0);
> + hci_conn_put(conn);
> }
> } else {
> /* Connection rejected */
> diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
> index 450cdcd..416801a 100644
> --- a/net/bluetooth/sco.c
> +++ b/net/bluetooth/sco.c
> @@ -662,16 +662,57 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
> return err;
> }
>
> +static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
> + struct msghdr *msg, size_t len, int flags)
> +{
> + struct sock *sk = sock->sk;
> + struct sco_pinfo *pi = sco_pi(sk);
> +
> + lock_sock(sk);
> +
> + if (sk->sk_state == BT_CONNECT &&

I think you want to use BT_CONNECT2 here...

> + test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
> + hci_conn_accept(pi->conn->hcon, 0);
> + sk->sk_state = BT_CONNECT2;

and BT_CONFIG here.

> +
> + release_sock(sk);
> + return 0;
> + }
> +
> + release_sock(sk);
> +
> + return bt_sock_recvmsg(iocb, sock, msg, len, flags);
> +}
> +
> static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
> {
> struct sock *sk = sock->sk;
> int err = 0;
> + u32 opt;
>
> BT_DBG("sk %p", sk);
>
> lock_sock(sk);
>
> switch (optname) {
> +
> + case BT_DEFER_SETUP:
> + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (get_user(opt, (u32 __user *) optval)) {
> + err = -EFAULT;
> + break;
> + }
> +
> + if (opt)
> + set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
> + else
> + clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
> + break;
> +

Please move the set/getsockopt to a separated patch and put it first in your
series.

> default:
> err = -ENOPROTOOPT;
> break;
> @@ -753,6 +794,19 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
> lock_sock(sk);
>
> switch (optname) {
> +
> + case BT_DEFER_SETUP:
> + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
> + (u32 __user *) optval))
> + err = -EFAULT;
> +
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;
> @@ -874,7 +928,10 @@ static void sco_conn_ready(struct sco_conn *conn)
> hci_conn_hold(conn->hcon);
> __sco_chan_add(conn, sk, parent);
>
> - sk->sk_state = BT_CONNECTED;
> + if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
> + sk->sk_state = BT_CONNECT;

Let's use BT_CONNECT2 here as this a incoming connection.

Gustavo

2012-11-14 14:39:47

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket

Hi Fred,

> > My idea is to define (I'm not sure if this is exactly the same as what you
> > suggested yourself) a bit, LM_DEFER, that e.g. sco_connect_ind() may return
> > and propagate this bit to hci_conn_request_evt(). This way all policing of
> > accepting SCO connections is handled from a single entry point into sco.c.
>
> You detailed it much further than I did, but yes it is the same idea. I
> fully support it! If everybody agrees, then I can update the patch.
>
> hci.h would receive the folowing line:
>
> + #define HCI_LM_DEFER 0x4000

can we actually not that that. This is an old public API and I rather
not extend it.

Regards

Marcel



2012-11-14 10:33:44

by Arnaud Mouiche

[permalink] [raw]
Subject: Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket

What is your plan once the kernel will gain the defer capability.
- is it up to bluetoothd/audio to accept and configure ?
- is it the external HFP apps (for the HFP case) to do the job ?

arnaud

On 11/14/2012 11:18 AM, Frédéric Dalleau wrote:
> Hi Arnaud,
>
> On 11/14/2012 11:09 AM, Arnaud Mouiche wrote:
>> Hello,
>> I will try to find some time to try your patch.
>
> It is a bit early since I haven't tested it myself, should be in much
> better shape before end of the week.
>
> Regards,
> Frédéric


2012-11-14 10:21:07

by Michael Knudsen

[permalink] [raw]
Subject: RE: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket

> - can't we take the opportunity to configure the whole set
> of eSCO parameters (bandwith / latency / retransmission effort)
> instead of only getting the possibility to change the voice
> settings

I am currently looking into this as mentioned in my mail last
week:

<[email protected]>

If anyone has any input for this, I'm all ears.

-m.



2012-11-14 10:18:52

by Frédéric DALLEAU

[permalink] [raw]
Subject: Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket

Hi Arnaud,

On 11/14/2012 11:09 AM, Arnaud Mouiche wrote:
> Hello,
> I will try to find some time to try your patch.

It is a bit early since I haven't tested it myself, should be in much
better shape before end of the week.

Regards,
Frédéric

2012-11-14 10:10:14

by Frédéric DALLEAU

[permalink] [raw]
Subject: Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket

Hi Michael,

On 11/14/2012 09:26 AM, Michael Knudsen wrote:

> My idea is to define (I'm not sure if this is exactly the same as what you
> suggested yourself) a bit, LM_DEFER, that e.g. sco_connect_ind() may return
> and propagate this bit to hci_conn_request_evt(). This way all policing of
> accepting SCO connections is handled from a single entry point into sco.c.

You detailed it much further than I did, but yes it is the same idea. I
fully support it! If everybody agrees, then I can update the patch.

hci.h would receive the folowing line:

+ #define HCI_LM_DEFER 0x4000

Best regards,
Frédéric

2012-11-14 10:09:21

by Arnaud Mouiche

[permalink] [raw]
Subject: Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket

Hello,

First thank you for this RFC. Defer (or something similar) is a must
have for mSBC vs CVSD connection accept from multiple device management,
at least for HFP case.
some comments / ideas:
- can't we take the opportunity to configure the whole set of eSCO
parameters (bandwith / latency / retransmission effort) instead of only
getting the possibility to change the voice settings
- can we introduce a better way to change the voice settings, instead of
modifying the adapter config (hdev->voice_setting)
- any possibility to reject the eSCO creating (for 'connection rejected
due to limited resources' reason)

I will try to find some time to try your patch.

regards,
arnaud



On 11/13/2012 07:20 PM, Frédéric Dalleau wrote:
> In order to authenticate and later configure an incoming SCO connection, the
> BT_DEFER_SETUP option is added.
> When an connection is requested, the listening socket is unblocked but the
> effective connection setup happens only on first recv.
> Any send between accept and recv fails with -ENOTCONN.
> ---
> include/net/bluetooth/hci_core.h | 15 +++++++
> net/bluetooth/hci_event.c | 47 +++++++++++++++++++-
> net/bluetooth/sco.c | 88 ++++++++++++++++++++++++++++++++++++--
> 3 files changed, 145 insertions(+), 5 deletions(-)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index ef5b85d..2ee0ecb 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -377,6 +377,7 @@ extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb,
> u16 flags);
>
> extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
> +extern int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr);
> extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
> extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
> extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
> @@ -577,6 +578,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
> int hci_conn_del(struct hci_conn *conn);
> void hci_conn_hash_flush(struct hci_dev *hdev);
> void hci_conn_check_pending(struct hci_dev *hdev);
> +void hci_conn_accept(struct hci_conn *conn, int mask);
>
> struct hci_chan *hci_chan_create(struct hci_conn *conn);
> void hci_chan_del(struct hci_chan *chan);
> @@ -796,6 +798,19 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
> }
> }
>
> +static inline int hci_proto_defered(struct hci_dev *hdev, bdaddr_t *bdaddr,
> + __u8 type)
> +{
> + switch (type) {
> + case SCO_LINK:
> + case ESCO_LINK:
> + return sco_defer(hdev, bdaddr);
> +
> + default:
> + return 0;
> + }
> +}
> +
> static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
> {
> switch (conn->type) {
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index 9f5c5f2..167eca0 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -2047,15 +2047,54 @@ unlock:
> hci_conn_check_pending(hdev);
> }
>
> +void hci_conn_accept(struct hci_conn *conn, int mask)
> +{
> + struct hci_dev *hdev = conn->hdev;
> +
> + BT_DBG("conn %p", conn);
> +
> + if (!lmp_esco_capable(hdev)) {
> + struct hci_cp_accept_conn_req cp;
> +
> + conn->state = BT_CONNECT;
> + bacpy(&cp.bdaddr, &conn->dst);
> +
> + if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
> + cp.role = 0x00; /* Become master */
> + else
> + cp.role = 0x01; /* Remain slave */
> +
> + hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
> + &cp);
> + } else /* lmp_esco_capable(hdev)) */ {
> + struct hci_cp_accept_sync_conn_req cp;
> +
> + conn->state = BT_CONNECT;
> + bacpy(&cp.bdaddr, &conn->dst);
> + cp.pkt_type = cpu_to_le16(conn->pkt_type);
> +
> + cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
> + cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
> + cp.max_latency = __constant_cpu_to_le16(0xffff);
> + cp.content_format = cpu_to_le16(hdev->voice_setting);
> + cp.retrans_effort = 0xff;
> +
> + hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
> + sizeof(cp), &cp);
> + }
> +}
> +
> static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
> {
> struct hci_ev_conn_request *ev = (void *) skb->data;
> int mask = hdev->link_mode;
> + int defered;
>
> BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
> ev->link_type);
>
> mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
> + defered = hci_proto_defered(hdev, &ev->bdaddr, ev->link_type);
>
> if ((mask & HCI_LM_ACCEPT) &&
> !hci_blacklist_lookup(hdev, &ev->bdaddr)) {
> @@ -2085,7 +2124,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
>
> hci_dev_unlock(hdev);
>
> - if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) {
> + if (ev->link_type == ACL_LINK ||
> + (!defered && !lmp_esco_capable(hdev))) {
> struct hci_cp_accept_conn_req cp;
>
> bacpy(&cp.bdaddr, &ev->bdaddr);
> @@ -2097,7 +2137,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
>
> hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
> &cp);
> - } else {
> + } else if (!defered) {
> struct hci_cp_accept_sync_conn_req cp;
>
> bacpy(&cp.bdaddr, &ev->bdaddr);
> @@ -2111,6 +2151,9 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
>
> hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
> sizeof(cp), &cp);
> + } else {
> + hci_proto_connect_cfm(conn, 0);
> + hci_conn_put(conn);
> }
> } else {
> /* Connection rejected */
> diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
> index 450cdcd..416801a 100644
> --- a/net/bluetooth/sco.c
> +++ b/net/bluetooth/sco.c
> @@ -662,16 +662,57 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
> return err;
> }
>
> +static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
> + struct msghdr *msg, size_t len, int flags)
> +{
> + struct sock *sk = sock->sk;
> + struct sco_pinfo *pi = sco_pi(sk);
> +
> + lock_sock(sk);
> +
> + if (sk->sk_state == BT_CONNECT &&
> + test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
> + hci_conn_accept(pi->conn->hcon, 0);
> + sk->sk_state = BT_CONNECT2;
> +
> + release_sock(sk);
> + return 0;
> + }
> +
> + release_sock(sk);
> +
> + return bt_sock_recvmsg(iocb, sock, msg, len, flags);
> +}
> +
> static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
> {
> struct sock *sk = sock->sk;
> int err = 0;
> + u32 opt;
>
> BT_DBG("sk %p", sk);
>
> lock_sock(sk);
>
> switch (optname) {
> +
> + case BT_DEFER_SETUP:
> + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (get_user(opt, (u32 __user *) optval)) {
> + err = -EFAULT;
> + break;
> + }
> +
> + if (opt)
> + set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
> + else
> + clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;
> @@ -753,6 +794,19 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
> lock_sock(sk);
>
> switch (optname) {
> +
> + case BT_DEFER_SETUP:
> + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
> + (u32 __user *) optval))
> + err = -EFAULT;
> +
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;
> @@ -874,7 +928,10 @@ static void sco_conn_ready(struct sco_conn *conn)
> hci_conn_hold(conn->hcon);
> __sco_chan_add(conn, sk, parent);
>
> - sk->sk_state = BT_CONNECTED;
> + if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
> + sk->sk_state = BT_CONNECT;
> + else
> + sk->sk_state = BT_CONNECTED;
>
> /* Wake up parent */
> parent->sk_data_ready(parent, 1);
> @@ -912,6 +969,32 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
> return lm;
> }
>
> +int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr)
> +{
> + struct sock *sk;
> + struct hlist_node *node;
> + int defer = 0;
> +
> + BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr);
> +
> + /* Find listening sockets */
> + read_lock(&sco_sk_list.lock);
> + sk_for_each(sk, node, &sco_sk_list.head) {
> + if (sk->sk_state != BT_LISTEN)
> + continue;
> +
> + if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
> + !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
> + if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
> + defer = 1;
> + break;
> + }
> + }
> + read_unlock(&sco_sk_list.lock);
> +
> + return defer;
> +}
> +
> void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
> {
> BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
> @@ -992,7 +1075,7 @@ static const struct proto_ops sco_sock_ops = {
> .accept = sco_sock_accept,
> .getname = sco_sock_getname,
> .sendmsg = sco_sock_sendmsg,
> - .recvmsg = bt_sock_recvmsg,
> + .recvmsg = sco_sock_recvmsg,
> .poll = bt_sock_poll,
> .ioctl = bt_sock_ioctl,
> .mmap = sock_no_mmap,
> @@ -1036,7 +1119,6 @@ int __init sco_init(void)
> BT_ERR("Failed to create SCO debug file");
> }
>
> - BT_INFO("SCO socket layer initialized");
>
> return 0;
>


2012-11-14 09:17:41

by Andrei Emeltchenko

[permalink] [raw]
Subject: Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket

Hi Fr?d?ric,

On Tue, Nov 13, 2012 at 07:20:25PM +0100, Fr?d?ric Dalleau wrote:
> In order to authenticate and later configure an incoming SCO connection, the
> BT_DEFER_SETUP option is added.
> When an connection is requested, the listening socket is unblocked but the
> effective connection setup happens only on first recv.
> Any send between accept and recv fails with -ENOTCONN.
> ---

Apart from other comments..

> +static inline int hci_proto_defered(struct hci_dev *hdev, bdaddr_t *bdaddr,

I think we do not use "inline" anymore.

> + __u8 type)
> +{
> + switch (type) {
> + case SCO_LINK:
> + case ESCO_LINK:
> + return sco_defer(hdev, bdaddr);
> +
> + default:
> + return 0;
> + }
> +}
> +
> static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
> {
> switch (conn->type) {
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index 9f5c5f2..167eca0 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -2047,15 +2047,54 @@ unlock:
> hci_conn_check_pending(hdev);
> }
>
> +void hci_conn_accept(struct hci_conn *conn, int mask)
> +{
> + struct hci_dev *hdev = conn->hdev;
> +
> + BT_DBG("conn %p", conn);
> +
> + if (!lmp_esco_capable(hdev)) {
> + struct hci_cp_accept_conn_req cp;
> +
> + conn->state = BT_CONNECT;
> + bacpy(&cp.bdaddr, &conn->dst);
> +
> + if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
> + cp.role = 0x00; /* Become master */
> + else
> + cp.role = 0x01; /* Remain slave */
> +
> + hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
> + &cp);
> + } else /* lmp_esco_capable(hdev)) */ {
> + struct hci_cp_accept_sync_conn_req cp;
> +
> + conn->state = BT_CONNECT;
> + bacpy(&cp.bdaddr, &conn->dst);
> + cp.pkt_type = cpu_to_le16(conn->pkt_type);
> +
> + cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
> + cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
> + cp.max_latency = __constant_cpu_to_le16(0xffff);

maybe some comments about those numbers

> + cp.content_format = cpu_to_le16(hdev->voice_setting);
> + cp.retrans_effort = 0xff;
> +
> + hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
> + sizeof(cp), &cp);
> + }
> +}
> +

...

> static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
> {
> struct sock *sk = sock->sk;
> int err = 0;
> + u32 opt;
>
> BT_DBG("sk %p", sk);
>
> lock_sock(sk);
>
> switch (optname) {
> +
> + case BT_DEFER_SETUP:
> + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (get_user(opt, (u32 __user *) optval)) {
> + err = -EFAULT;
> + break;
> + }
> +
> + if (opt)
> + set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
> + else
> + clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;
> @@ -753,6 +794,19 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
> lock_sock(sk);
>
> switch (optname) {
> +
> + case BT_DEFER_SETUP:
> + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
> + (u32 __user *) optval))
> + err = -EFAULT;
> +
> + break;
> +

I think set/get sockopt might be implemented in a separate patch.
Maybe you can also break even into more logical chunks so this would be
easier to understand.

Best regards
Andrei Emeltchenko


2012-11-14 08:26:55

by Michael Knudsen

[permalink] [raw]
Subject: Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket

On 2012-11-13 19:20, Frédéric Dalleau wrote:
> In order to authenticate and later configure an incoming SCO connection, the
> BT_DEFER_SETUP option is added.
> When an connection is requested, the listening socket is unblocked but the
> effective connection setup happens only on first recv.
> Any send between accept and recv fails with -ENOTCONN.

I have a few comments, please see below.

> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index ef5b85d..2ee0ecb 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -377,6 +377,7 @@ extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb,
> u16 flags);
>
> extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
> +extern int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr);

I would prefer to make sco_defer() static and call it from sco_connect_ind()
instead of as a separate step. More on this below.

> +static inline int hci_proto_defered(struct hci_dev *hdev, bdaddr_t *bdaddr,
> + __u8 type)
> +{
> + switch (type) {
> + case SCO_LINK:
> + case ESCO_LINK:
> + return sco_defer(hdev, bdaddr);
> +
> + default:
> + return 0;
> + }
> +}

My idea is to define (I'm not sure if this is exactly the same as what you
suggested yourself) a bit, LM_DEFER, that e.g. sco_connect_ind() may return
and propagate this bit to hci_conn_request_evt(). This way all policing of
accepting SCO connections is handled from a single entry point into sco.c.

> static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
> {
> switch (conn->type) {

> static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
> {
> struct hci_ev_conn_request *ev = (void *) skb->data;
> int mask = hdev->link_mode;
> + int defered;
>
> BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
> ev->link_type);
>
> mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
> + defered = hci_proto_defered(hdev, &ev->bdaddr, ev->link_type);

Given that these two take exactly the same parameters, I would much
rather see hci_proto_defered() folded into hci_proto_connect_ind()
and set (LM_ACCEPT | LM_DEFERRED) for deferred cases. This would
also make sure we only do one iteration over the SCO socket list and
that the same SCO sockets are taken into account. With your approach
there is a risk that one of the loops is changed and not the other.

-m.

2012-11-13 18:45:14

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket

Hi Fr?d?ric,

On Tue, Nov 13, 2012 at 8:20 PM, Fr?d?ric Dalleau
<[email protected]> wrote:
> In order to authenticate and later configure an incoming SCO connection, the
> BT_DEFER_SETUP option is added.
> When an connection is requested, the listening socket is unblocked but the
> effective connection setup happens only on first recv.
> Any send between accept and recv fails with -ENOTCONN.

I think you should detail when exactly we do defer, we need to defer
before the audio settings negotiation takes place because latter once
we add support for passing the settings this happens during this stage
before accepting.

> ---
> include/net/bluetooth/hci_core.h | 15 +++++++
> net/bluetooth/hci_event.c | 47 +++++++++++++++++++-
> net/bluetooth/sco.c | 88 ++++++++++++++++++++++++++++++++++++--
> 3 files changed, 145 insertions(+), 5 deletions(-)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index ef5b85d..2ee0ecb 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -377,6 +377,7 @@ extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb,
> u16 flags);
>
> extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
> +extern int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr);
> extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
> extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
> extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
> @@ -577,6 +578,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
> int hci_conn_del(struct hci_conn *conn);
> void hci_conn_hash_flush(struct hci_dev *hdev);
> void hci_conn_check_pending(struct hci_dev *hdev);
> +void hci_conn_accept(struct hci_conn *conn, int mask);
>
> struct hci_chan *hci_chan_create(struct hci_conn *conn);
> void hci_chan_del(struct hci_chan *chan);
> @@ -796,6 +798,19 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
> }
> }
>
> +static inline int hci_proto_defered(struct hci_dev *hdev, bdaddr_t *bdaddr,
> + __u8 type)
> +{
> + switch (type) {
> + case SCO_LINK:
> + case ESCO_LINK:
> + return sco_defer(hdev, bdaddr);
> +
> + default:
> + return 0;
> + }
> +}
> +
> static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
> {
> switch (conn->type) {
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index 9f5c5f2..167eca0 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -2047,15 +2047,54 @@ unlock:
> hci_conn_check_pending(hdev);
> }
>
> +void hci_conn_accept(struct hci_conn *conn, int mask)
> +{
> + struct hci_dev *hdev = conn->hdev;
> +
> + BT_DBG("conn %p", conn);
> +
> + if (!lmp_esco_capable(hdev)) {
> + struct hci_cp_accept_conn_req cp;
> +
> + conn->state = BT_CONNECT;
> + bacpy(&cp.bdaddr, &conn->dst);
> +
> + if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
> + cp.role = 0x00; /* Become master */
> + else
> + cp.role = 0x01; /* Remain slave */
> +
> + hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
> + &cp);
> + } else /* lmp_esco_capable(hdev)) */ {
> + struct hci_cp_accept_sync_conn_req cp;
> +
> + conn->state = BT_CONNECT;
> + bacpy(&cp.bdaddr, &conn->dst);
> + cp.pkt_type = cpu_to_le16(conn->pkt_type);
> +
> + cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
> + cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
> + cp.max_latency = __constant_cpu_to_le16(0xffff);
> + cp.content_format = cpu_to_le16(hdev->voice_setting);
> + cp.retrans_effort = 0xff;
> +
> + hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
> + sizeof(cp), &cp);
> + }
> +}
> +
> static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
> {
> struct hci_ev_conn_request *ev = (void *) skb->data;
> int mask = hdev->link_mode;
> + int defered;
>
> BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
> ev->link_type);
>
> mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
> + defered = hci_proto_defered(hdev, &ev->bdaddr, ev->link_type);
>
> if ((mask & HCI_LM_ACCEPT) &&
> !hci_blacklist_lookup(hdev, &ev->bdaddr)) {
> @@ -2085,7 +2124,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
>
> hci_dev_unlock(hdev);
>
> - if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) {
> + if (ev->link_type == ACL_LINK ||
> + (!defered && !lmp_esco_capable(hdev))) {
> struct hci_cp_accept_conn_req cp;
>
> bacpy(&cp.bdaddr, &ev->bdaddr);
> @@ -2097,7 +2137,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
>
> hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
> &cp);
> - } else {
> + } else if (!defered) {
> struct hci_cp_accept_sync_conn_req cp;
>
> bacpy(&cp.bdaddr, &ev->bdaddr);
> @@ -2111,6 +2151,9 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
>
> hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
> sizeof(cp), &cp);
> + } else {
> + hci_proto_connect_cfm(conn, 0);
> + hci_conn_put(conn);
> }
> } else {
> /* Connection rejected */
> diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
> index 450cdcd..416801a 100644
> --- a/net/bluetooth/sco.c
> +++ b/net/bluetooth/sco.c
> @@ -662,16 +662,57 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
> return err;
> }
>
> +static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
> + struct msghdr *msg, size_t len, int flags)
> +{
> + struct sock *sk = sock->sk;
> + struct sco_pinfo *pi = sco_pi(sk);
> +
> + lock_sock(sk);
> +
> + if (sk->sk_state == BT_CONNECT &&
> + test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
> + hci_conn_accept(pi->conn->hcon, 0);
> + sk->sk_state = BT_CONNECT2;
> +
> + release_sock(sk);
> + return 0;
> + }
> +
> + release_sock(sk);
> +
> + return bt_sock_recvmsg(iocb, sock, msg, len, flags);
> +}
> +
> static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
> {
> struct sock *sk = sock->sk;
> int err = 0;
> + u32 opt;
>
> BT_DBG("sk %p", sk);
>
> lock_sock(sk);
>
> switch (optname) {
> +
> + case BT_DEFER_SETUP:
> + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (get_user(opt, (u32 __user *) optval)) {
> + err = -EFAULT;
> + break;
> + }
> +
> + if (opt)
> + set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
> + else
> + clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;
> @@ -753,6 +794,19 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
> lock_sock(sk);
>
> switch (optname) {
> +
> + case BT_DEFER_SETUP:
> + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
> + (u32 __user *) optval))
> + err = -EFAULT;
> +
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;
> @@ -874,7 +928,10 @@ static void sco_conn_ready(struct sco_conn *conn)
> hci_conn_hold(conn->hcon);
> __sco_chan_add(conn, sk, parent);
>
> - sk->sk_state = BT_CONNECTED;
> + if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
> + sk->sk_state = BT_CONNECT;
> + else
> + sk->sk_state = BT_CONNECTED;
>
> /* Wake up parent */
> parent->sk_data_ready(parent, 1);
> @@ -912,6 +969,32 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
> return lm;
> }
>
> +int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr)
> +{
> + struct sock *sk;
> + struct hlist_node *node;
> + int defer = 0;
> +
> + BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr);
> +
> + /* Find listening sockets */
> + read_lock(&sco_sk_list.lock);
> + sk_for_each(sk, node, &sco_sk_list.head) {
> + if (sk->sk_state != BT_LISTEN)
> + continue;
> +
> + if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
> + !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
> + if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
> + defer = 1;
> + break;
> + }
> + }
> + read_unlock(&sco_sk_list.lock);
> +
> + return defer;
> +}
> +
> void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
> {
> BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
> @@ -992,7 +1075,7 @@ static const struct proto_ops sco_sock_ops = {
> .accept = sco_sock_accept,
> .getname = sco_sock_getname,
> .sendmsg = sco_sock_sendmsg,
> - .recvmsg = bt_sock_recvmsg,
> + .recvmsg = sco_sock_recvmsg,
> .poll = bt_sock_poll,
> .ioctl = bt_sock_ioctl,
> .mmap = sock_no_mmap,
> @@ -1036,7 +1119,6 @@ int __init sco_init(void)
> BT_ERR("Failed to create SCO debug file");
> }
>
> - BT_INFO("SCO socket layer initialized");

There doesn't seems to be any need to remove the line above, could you
please fix that.

--
Luiz Augusto von Dentz

2012-11-13 18:20:25

by Frédéric DALLEAU

[permalink] [raw]
Subject: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket

In order to authenticate and later configure an incoming SCO connection, the
BT_DEFER_SETUP option is added.
When an connection is requested, the listening socket is unblocked but the
effective connection setup happens only on first recv.
Any send between accept and recv fails with -ENOTCONN.
---
include/net/bluetooth/hci_core.h | 15 +++++++
net/bluetooth/hci_event.c | 47 +++++++++++++++++++-
net/bluetooth/sco.c | 88 ++++++++++++++++++++++++++++++++++++--
3 files changed, 145 insertions(+), 5 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index ef5b85d..2ee0ecb 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -377,6 +377,7 @@ extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb,
u16 flags);

extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
+extern int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr);
extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
@@ -577,6 +578,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
int hci_conn_del(struct hci_conn *conn);
void hci_conn_hash_flush(struct hci_dev *hdev);
void hci_conn_check_pending(struct hci_dev *hdev);
+void hci_conn_accept(struct hci_conn *conn, int mask);

struct hci_chan *hci_chan_create(struct hci_conn *conn);
void hci_chan_del(struct hci_chan *chan);
@@ -796,6 +798,19 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
}
}

+static inline int hci_proto_defered(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ __u8 type)
+{
+ switch (type) {
+ case SCO_LINK:
+ case ESCO_LINK:
+ return sco_defer(hdev, bdaddr);
+
+ default:
+ return 0;
+ }
+}
+
static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
{
switch (conn->type) {
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 9f5c5f2..167eca0 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2047,15 +2047,54 @@ unlock:
hci_conn_check_pending(hdev);
}

+void hci_conn_accept(struct hci_conn *conn, int mask)
+{
+ struct hci_dev *hdev = conn->hdev;
+
+ BT_DBG("conn %p", conn);
+
+ if (!lmp_esco_capable(hdev)) {
+ struct hci_cp_accept_conn_req cp;
+
+ conn->state = BT_CONNECT;
+ bacpy(&cp.bdaddr, &conn->dst);
+
+ if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
+ cp.role = 0x00; /* Become master */
+ else
+ cp.role = 0x01; /* Remain slave */
+
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
+ &cp);
+ } else /* lmp_esco_capable(hdev)) */ {
+ struct hci_cp_accept_sync_conn_req cp;
+
+ conn->state = BT_CONNECT;
+ bacpy(&cp.bdaddr, &conn->dst);
+ cp.pkt_type = cpu_to_le16(conn->pkt_type);
+
+ cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
+ cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
+ cp.max_latency = __constant_cpu_to_le16(0xffff);
+ cp.content_format = cpu_to_le16(hdev->voice_setting);
+ cp.retrans_effort = 0xff;
+
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
+ sizeof(cp), &cp);
+ }
+}
+
static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_conn_request *ev = (void *) skb->data;
int mask = hdev->link_mode;
+ int defered;

BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
ev->link_type);

mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
+ defered = hci_proto_defered(hdev, &ev->bdaddr, ev->link_type);

if ((mask & HCI_LM_ACCEPT) &&
!hci_blacklist_lookup(hdev, &ev->bdaddr)) {
@@ -2085,7 +2124,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)

hci_dev_unlock(hdev);

- if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) {
+ if (ev->link_type == ACL_LINK ||
+ (!defered && !lmp_esco_capable(hdev))) {
struct hci_cp_accept_conn_req cp;

bacpy(&cp.bdaddr, &ev->bdaddr);
@@ -2097,7 +2137,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)

hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
&cp);
- } else {
+ } else if (!defered) {
struct hci_cp_accept_sync_conn_req cp;

bacpy(&cp.bdaddr, &ev->bdaddr);
@@ -2111,6 +2151,9 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)

hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
sizeof(cp), &cp);
+ } else {
+ hci_proto_connect_cfm(conn, 0);
+ hci_conn_put(conn);
}
} else {
/* Connection rejected */
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 450cdcd..416801a 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -662,16 +662,57 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
return err;
}

+static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sco_pinfo *pi = sco_pi(sk);
+
+ lock_sock(sk);
+
+ if (sk->sk_state == BT_CONNECT &&
+ test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
+ hci_conn_accept(pi->conn->hcon, 0);
+ sk->sk_state = BT_CONNECT2;
+
+ release_sock(sk);
+ return 0;
+ }
+
+ release_sock(sk);
+
+ return bt_sock_recvmsg(iocb, sock, msg, len, flags);
+}
+
static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
int err = 0;
+ u32 opt;

BT_DBG("sk %p", sk);

lock_sock(sk);

switch (optname) {
+
+ case BT_DEFER_SETUP:
+ if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (get_user(opt, (u32 __user *) optval)) {
+ err = -EFAULT;
+ break;
+ }
+
+ if (opt)
+ set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+ else
+ clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -753,6 +794,19 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
lock_sock(sk);

switch (optname) {
+
+ case BT_DEFER_SETUP:
+ if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
+ (u32 __user *) optval))
+ err = -EFAULT;
+
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -874,7 +928,10 @@ static void sco_conn_ready(struct sco_conn *conn)
hci_conn_hold(conn->hcon);
__sco_chan_add(conn, sk, parent);

- sk->sk_state = BT_CONNECTED;
+ if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
+ sk->sk_state = BT_CONNECT;
+ else
+ sk->sk_state = BT_CONNECTED;

/* Wake up parent */
parent->sk_data_ready(parent, 1);
@@ -912,6 +969,32 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
return lm;
}

+int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+ struct sock *sk;
+ struct hlist_node *node;
+ int defer = 0;
+
+ BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr);
+
+ /* Find listening sockets */
+ read_lock(&sco_sk_list.lock);
+ sk_for_each(sk, node, &sco_sk_list.head) {
+ if (sk->sk_state != BT_LISTEN)
+ continue;
+
+ if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
+ !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
+ if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
+ defer = 1;
+ break;
+ }
+ }
+ read_unlock(&sco_sk_list.lock);
+
+ return defer;
+}
+
void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
{
BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
@@ -992,7 +1075,7 @@ static const struct proto_ops sco_sock_ops = {
.accept = sco_sock_accept,
.getname = sco_sock_getname,
.sendmsg = sco_sock_sendmsg,
- .recvmsg = bt_sock_recvmsg,
+ .recvmsg = sco_sock_recvmsg,
.poll = bt_sock_poll,
.ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap,
@@ -1036,7 +1119,6 @@ int __init sco_init(void)
BT_ERR("Failed to create SCO debug file");
}

- BT_INFO("SCO socket layer initialized");

return 0;

--
1.7.9.5