@@ -317,6 +317,7 @@ struct bt_sock {
struct list_head accept_q;
struct sock *parent;
unsigned long flags;
+ u16 avdtp_handle;
void (*skb_msg_name)(struct sk_buff *, void *, int *);
void (*skb_put_cmsg)(struct sk_buff *, struct msghdr *, struct sock *);
};
@@ -324,6 +325,7 @@ struct bt_sock {
enum {
BT_SK_DEFER_SETUP,
BT_SK_SUSPEND,
+ BT_SK_AVDTP_PEND,
};
struct bt_sock_list {
@@ -346,6 +348,7 @@ __poll_t bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait);
int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
int bt_sock_wait_ready(struct sock *sk, unsigned long flags);
+int bt_sock_wait_for_avdtp_hndl(struct sock *sk, unsigned long timeo);
void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh);
void bt_accept_unlink(struct sock *sk);
@@ -348,6 +348,7 @@ enum {
#define HCI_POWER_OFF_TIMEOUT msecs_to_jiffies(5000) /* 5 seconds */
#define HCI_LE_CONN_TIMEOUT msecs_to_jiffies(20000) /* 20 seconds */
#define HCI_LE_AUTOCONN_TIMEOUT msecs_to_jiffies(4000) /* 4 seconds */
+#define MSFT_AVDTP_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
/* HCI data types */
#define HCI_COMMAND_PKT 0x01
@@ -669,6 +669,8 @@ struct l2cap_ops {
unsigned long len, int nb);
int (*filter) (struct l2cap_chan * chan,
struct sk_buff *skb);
+ void (*avdtp_wakeup) (struct l2cap_chan *chan,
+ u8 status, u16 handle);
};
struct l2cap_conn {
@@ -1001,6 +1003,8 @@ void __l2cap_physical_cfm(struct l2cap_chan *chan, int result);
struct l2cap_conn *l2cap_conn_get(struct l2cap_conn *conn);
void l2cap_conn_put(struct l2cap_conn *conn);
+void l2cap_avdtp_wakeup(struct l2cap_conn *conn, u16 dcid, u8 status,
+ u16 handle);
int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user);
void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user);
@@ -607,6 +607,42 @@ int bt_sock_wait_ready(struct sock *sk, unsigned long flags)
}
EXPORT_SYMBOL(bt_sock_wait_ready);
+/* This function expects the sk lock to be held when called */
+int bt_sock_wait_for_avdtp_hndl(struct sock *sk, unsigned long timeo)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int err = 0;
+
+ BT_DBG("sk %p", sk);
+
+ add_wait_queue(sk_sleep(sk), &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (test_bit(BT_SK_AVDTP_PEND, &bt_sk(sk)->flags)) {
+ if (!timeo) {
+ err = -EINPROGRESS;
+ break;
+ }
+
+ if (signal_pending(current)) {
+ err = sock_intr_errno(timeo);
+ break;
+ }
+
+ release_sock(sk);
+ timeo = schedule_timeout(timeo);
+ lock_sock(sk);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ err = sock_error(sk);
+ if (err)
+ break;
+ }
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(sk_sleep(sk), &wait);
+ return err;
+}
+EXPORT_SYMBOL(bt_sock_wait_for_avdtp_hndl);
+
#ifdef CONFIG_PROC_FS
static void *bt_seq_start(struct seq_file *seq, loff_t *pos)
__acquires(seq->private->l->lock)
@@ -8507,6 +8507,21 @@ int __init l2cap_init(void)
return 0;
}
+void l2cap_avdtp_wakeup(struct l2cap_conn *conn, u16 cid, u8 status,
+ u16 handle)
+{
+ struct l2cap_chan *chan;
+
+ chan = l2cap_get_chan_by_dcid(conn, cid);
+
+ if (!chan)
+ return;
+
+ chan->ops->avdtp_wakeup(chan, status, handle);
+
+ l2cap_chan_unlock(chan);
+}
+
void l2cap_exit(void)
{
debugfs_remove(l2cap_debugfs);
@@ -1167,7 +1167,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
}
- err = msft_avdtp_cmd(hdev, chan, optval, optlen);
+ err = msft_avdtp_cmd(hdev, chan, optval, optlen, sk);
hci_dev_put(hdev);
break;
@@ -1758,6 +1758,19 @@ static int l2cap_sock_filter(struct l2cap_chan *chan, struct sk_buff *skb)
return 0;
}
+static void l2cap_sock_avdtp_wakeup(struct l2cap_chan *chan, u8 status,
+ u16 handle)
+{
+ struct sock *sk = chan->data;
+
+ if (test_bit(BT_SK_AVDTP_PEND, &bt_sk(sk)->flags)) {
+ bt_sk(sk)->avdtp_handle = handle;
+ sk->sk_err = -bt_to_errno(status);
+ clear_bit(BT_SK_AVDTP_PEND, &bt_sk(sk)->flags);
+ sk->sk_state_change(sk);
+ }
+}
+
static const struct l2cap_ops l2cap_chan_ops = {
.name = "L2CAP Socket Interface",
.new_connection = l2cap_sock_new_connection_cb,
@@ -1774,6 +1787,7 @@ static const struct l2cap_ops l2cap_chan_ops = {
.get_peer_pid = l2cap_sock_get_peer_pid_cb,
.alloc_skb = l2cap_sock_alloc_skb_cb,
.filter = l2cap_sock_filter,
+ .avdtp_wakeup = l2cap_sock_avdtp_wakeup,
};
static void l2cap_sock_destruct(struct sock *sk)
@@ -844,7 +844,8 @@ bool msft_curve_validity(struct hci_dev *hdev)
static int msft_avdtp_open(struct hci_dev *hdev,
struct l2cap_chan *chan,
- struct msft_cp_avdtp *cmd)
+ struct msft_cp_avdtp *cmd,
+ struct sock *sk)
{
struct msft_cp_avdtp_open *open_cmd;
struct hci_media_service_caps *caps;
@@ -874,14 +875,18 @@ static int msft_avdtp_open(struct hci_dev *hdev,
hci_send_cmd(hdev, MSFT_OP_AVDTP, sizeof(*open_cmd) + cmd->len,
open_cmd);
+ set_bit(BT_SK_AVDTP_PEND, &bt_sk(sk)->flags);
/* wait until we get avdtp handle or timeout */
+ err = bt_sock_wait_for_avdtp_hndl(sk, MSFT_AVDTP_TIMEOUT);
+
fail:
kfree(open_cmd);
return err;
}
int msft_avdtp_cmd(struct hci_dev *hdev, struct l2cap_chan *chan,
- sockptr_t optval, int optlen)
+ sockptr_t optval, int optlen,
+ struct sock *sk)
{
int err = 0;
struct msft_cp_avdtp *cmd;
@@ -910,7 +915,7 @@ int msft_avdtp_cmd(struct hci_dev *hdev, struct l2cap_chan *chan,
err = -EINVAL;
break;
}
- err = msft_avdtp_open(hdev, chan, cmd);
+ err = msft_avdtp_open(hdev, chan, cmd, sk);
break;
default:
@@ -928,6 +933,7 @@ static void msft_cc_avdtp_open(struct hci_dev *hdev, struct sk_buff *skb)
struct msft_rp_avdtp_open *rp;
struct msft_cp_avdtp_open *sent;
struct hci_conn *hconn;
+ struct l2cap_conn *conn;
if (skb->len < sizeof(*rp))
return;
@@ -941,7 +947,11 @@ static void msft_cc_avdtp_open(struct hci_dev *hdev, struct sk_buff *skb)
if (!hconn)
return;
+ conn = hconn->l2cap_data;
+
/* wake up the task waiting on avdtp handle */
+ l2cap_avdtp_wakeup(conn, le16_to_cpu(sent->dcid), rp->status,
+ rp->status ? 0 : __le16_to_cpu(rp->avdtp_handle));
}
void msft_cc_avdtp(struct hci_dev *hdev, struct sk_buff *skb)
@@ -81,5 +81,5 @@ static inline bool msft_curve_validity(struct hci_dev *hdev)
#endif
int msft_avdtp_cmd(struct hci_dev *hdev, struct l2cap_chan *chan,
- sockptr_t optval, int optlen);
+ sockptr_t optval, int optlen, struct sock *sk);
void msft_cc_avdtp(struct hci_dev *hdev, struct sk_buff *skb);