aboutsummaryrefslogtreecommitdiff
path: root/net/bluetooth/l2cap_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/bluetooth/l2cap_core.c')
-rw-r--r--net/bluetooth/l2cap_core.c121
1 files changed, 81 insertions, 40 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 68843a28a7a..8c3499bec89 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -504,8 +504,10 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
if (conn->hcon->type == LE_LINK) {
/* LE connection */
chan->omtu = L2CAP_DEFAULT_MTU;
- chan->scid = L2CAP_CID_LE_DATA;
- chan->dcid = L2CAP_CID_LE_DATA;
+ if (chan->dcid == L2CAP_CID_ATT)
+ chan->scid = L2CAP_CID_ATT;
+ else
+ chan->scid = l2cap_alloc_cid(conn);
} else {
/* Alloc CID for connection-oriented socket */
chan->scid = l2cap_alloc_cid(conn);
@@ -543,6 +545,8 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
l2cap_chan_hold(chan);
+ hci_conn_hold(conn->hcon);
+
list_add(&chan->list, &conn->chan_l);
}
@@ -1338,17 +1342,21 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
{
- struct sock *parent, *sk;
+ struct sock *parent;
struct l2cap_chan *chan, *pchan;
BT_DBG("");
/* Check if we have socket listening on cid */
- pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA,
+ pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
conn->src, conn->dst);
if (!pchan)
return;
+ /* Client ATT sockets should override the server one */
+ if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT))
+ return;
+
parent = pchan->sk;
lock_sock(parent);
@@ -1357,17 +1365,12 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
if (!chan)
goto clean;
- sk = chan->sk;
-
- hci_conn_hold(conn->hcon);
- conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
-
- bacpy(&bt_sk(sk)->src, conn->src);
- bacpy(&bt_sk(sk)->dst, conn->dst);
+ chan->dcid = L2CAP_CID_ATT;
- l2cap_chan_add(conn, chan);
+ bacpy(&bt_sk(chan->sk)->src, conn->src);
+ bacpy(&bt_sk(chan->sk)->dst, conn->dst);
- l2cap_chan_ready(chan);
+ __l2cap_chan_add(conn, chan);
clean:
release_sock(parent);
@@ -1380,14 +1383,17 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
BT_DBG("conn %p", conn);
- if (!hcon->out && hcon->type == LE_LINK)
- l2cap_le_conn_ready(conn);
-
+ /* For outgoing pairing which doesn't necessarily have an
+ * associated socket (e.g. mgmt_pair_device).
+ */
if (hcon->out && hcon->type == LE_LINK)
smp_conn_security(hcon, hcon->pending_sec_level);
mutex_lock(&conn->chan_lock);
+ if (hcon->type == LE_LINK)
+ l2cap_le_conn_ready(conn);
+
list_for_each_entry(chan, &conn->chan_l, list) {
l2cap_chan_lock(chan);
@@ -1792,7 +1798,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
auth_type = l2cap_get_auth_type(chan);
- if (chan->dcid == L2CAP_CID_LE_DATA)
+ if (bdaddr_type_is_le(dst_type))
hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
chan->sec_level, auth_type);
else
@@ -1811,16 +1817,10 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
goto done;
}
- if (hcon->type == LE_LINK) {
- err = 0;
-
- if (!list_empty(&conn->chan_l)) {
- err = -EBUSY;
- hci_conn_drop(hcon);
- }
-
- if (err)
- goto done;
+ if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
+ hci_conn_drop(hcon);
+ err = -EBUSY;
+ goto done;
}
/* Update source addr of the socket */
@@ -1830,6 +1830,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
l2cap_chan_add(conn, chan);
l2cap_chan_lock(chan);
+ /* l2cap_chan_add takes its own ref so we can drop this one */
+ hci_conn_drop(hcon);
+
l2cap_state_change(chan, BT_CONNECT);
__set_chan_timer(chan, sk->sk_sndtimeo);
@@ -3751,8 +3754,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
sk = chan->sk;
- hci_conn_hold(conn->hcon);
-
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
chan->psm = psm;
@@ -5292,6 +5293,51 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
}
}
+static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
+ struct sk_buff *skb)
+{
+ u8 *data = skb->data;
+ int len = skb->len;
+ struct l2cap_cmd_hdr cmd;
+ int err;
+
+ l2cap_raw_recv(conn, skb);
+
+ while (len >= L2CAP_CMD_HDR_SIZE) {
+ u16 cmd_len;
+ memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
+ data += L2CAP_CMD_HDR_SIZE;
+ len -= L2CAP_CMD_HDR_SIZE;
+
+ cmd_len = le16_to_cpu(cmd.len);
+
+ BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len,
+ cmd.ident);
+
+ if (cmd_len > len || !cmd.ident) {
+ BT_DBG("corrupted command");
+ break;
+ }
+
+ err = l2cap_le_sig_cmd(conn, &cmd, data);
+ if (err) {
+ struct l2cap_cmd_rej_unk rej;
+
+ BT_ERR("Wrong link type (%d)", err);
+
+ /* FIXME: Map err to a valid reason */
+ rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
+ l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
+ sizeof(rej), &rej);
+ }
+
+ data += cmd_len;
+ len -= cmd_len;
+ }
+
+ kfree_skb(skb);
+}
+
static inline void l2cap_sig_channel(struct l2cap_conn *conn,
struct sk_buff *skb)
{
@@ -5318,11 +5364,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
break;
}
- if (conn->hcon->type == LE_LINK)
- err = l2cap_le_sig_cmd(conn, &cmd, data);
- else
- err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
-
+ err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
if (err) {
struct l2cap_cmd_rej_unk rej;
@@ -6356,16 +6398,13 @@ static void l2cap_att_channel(struct l2cap_conn *conn,
{
struct l2cap_chan *chan;
- chan = l2cap_global_chan_by_scid(0, L2CAP_CID_LE_DATA,
+ chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT,
conn->src, conn->dst);
if (!chan)
goto drop;
BT_DBG("chan %p, len %d", chan, skb->len);
- if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
- goto drop;
-
if (chan->imtu < skb->len)
goto drop;
@@ -6395,6 +6434,8 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
switch (cid) {
case L2CAP_CID_LE_SIGNALING:
+ l2cap_le_sig_channel(conn, skb);
+ break;
case L2CAP_CID_SIGNALING:
l2cap_sig_channel(conn, skb);
break;
@@ -6405,7 +6446,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
l2cap_conless_channel(conn, psm, skb);
break;
- case L2CAP_CID_LE_DATA:
+ case L2CAP_CID_ATT:
l2cap_att_channel(conn, skb);
break;
@@ -6531,7 +6572,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
continue;
}
- if (chan->scid == L2CAP_CID_LE_DATA) {
+ if (chan->scid == L2CAP_CID_ATT) {
if (!status && encrypt) {
chan->sec_level = hcon->sec_level;
l2cap_chan_ready(chan);