Bluetooth: improve readability of l2cap_seq_list code
[deliverable/linux.git] / net / bluetooth / l2cap_core.c
index ce93dcf0c2db9af91e33a134a374679a5153c3ce..1e12d6d58e84cbb388d4c5a844c94b970055f7d6 100644 (file)
@@ -4,6 +4,7 @@
    Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org>
    Copyright (C) 2010 Google Inc.
    Copyright (C) 2011 ProFUSION Embedded Systems
+   Copyright (c) 2012 Code Aurora Forum.  All rights reserved.
 
    Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
 
@@ -120,17 +121,6 @@ static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8
        return NULL;
 }
 
-static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
-{
-       struct l2cap_chan *c;
-
-       mutex_lock(&conn->chan_lock);
-       c = __l2cap_get_chan_by_ident(conn, ident);
-       mutex_unlock(&conn->chan_lock);
-
-       return c;
-}
-
 static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
 {
        struct l2cap_chan *c;
@@ -320,14 +310,16 @@ static inline u16 l2cap_seq_list_pop(struct l2cap_seq_list *seq_list)
 
 static void l2cap_seq_list_clear(struct l2cap_seq_list *seq_list)
 {
-       if (seq_list->head != L2CAP_SEQ_LIST_CLEAR) {
-               u16 i;
-               for (i = 0; i <= seq_list->mask; i++)
-                       seq_list->list[i] = L2CAP_SEQ_LIST_CLEAR;
+       u16 i;
 
-               seq_list->head = L2CAP_SEQ_LIST_CLEAR;
-               seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
-       }
+       if (seq_list->head == L2CAP_SEQ_LIST_CLEAR)
+               return;
+
+       for (i = 0; i <= seq_list->mask; i++)
+               seq_list->list[i] = L2CAP_SEQ_LIST_CLEAR;
+
+       seq_list->head = L2CAP_SEQ_LIST_CLEAR;
+       seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
 }
 
 static void l2cap_seq_list_append(struct l2cap_seq_list *seq_list, u16 seq)
@@ -336,15 +328,16 @@ static void l2cap_seq_list_append(struct l2cap_seq_list *seq_list, u16 seq)
 
        /* All appends happen in constant time */
 
-       if (seq_list->list[seq & mask] == L2CAP_SEQ_LIST_CLEAR) {
-               if (seq_list->tail == L2CAP_SEQ_LIST_CLEAR)
-                       seq_list->head = seq;
-               else
-                       seq_list->list[seq_list->tail & mask] = seq;
+       if (seq_list->list[seq & mask] != L2CAP_SEQ_LIST_CLEAR)
+               return;
 
-               seq_list->tail = seq;
-               seq_list->list[seq & mask] = L2CAP_SEQ_LIST_TAIL;
-       }
+       if (seq_list->tail == L2CAP_SEQ_LIST_CLEAR)
+               seq_list->head = seq;
+       else
+               seq_list->list[seq_list->tail & mask] = seq;
+
+       seq_list->tail = seq;
+       seq_list->list[seq & mask] = L2CAP_SEQ_LIST_TAIL;
 }
 
 static void l2cap_chan_timeout(struct work_struct *work)
@@ -724,87 +717,6 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
        hci_send_acl(chan->conn->hchan, skb, flags);
 }
 
-static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control)
-{
-       struct sk_buff *skb;
-       struct l2cap_hdr *lh;
-       struct l2cap_conn *conn = chan->conn;
-       int count, hlen;
-
-       if (chan->state != BT_CONNECTED)
-               return;
-
-       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-               hlen = L2CAP_EXT_HDR_SIZE;
-       else
-               hlen = L2CAP_ENH_HDR_SIZE;
-
-       if (chan->fcs == L2CAP_FCS_CRC16)
-               hlen += L2CAP_FCS_SIZE;
-
-       BT_DBG("chan %p, control 0x%8.8x", chan, control);
-
-       count = min_t(unsigned int, conn->mtu, hlen);
-
-       control |= __set_sframe(chan);
-
-       if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
-               control |= __set_ctrl_final(chan);
-
-       if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state))
-               control |= __set_ctrl_poll(chan);
-
-       skb = bt_skb_alloc(count, GFP_ATOMIC);
-       if (!skb)
-               return;
-
-       lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
-       lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE);
-       lh->cid = cpu_to_le16(chan->dcid);
-
-       __put_control(chan, control, skb_put(skb, __ctrl_size(chan)));
-
-       if (chan->fcs == L2CAP_FCS_CRC16) {
-               u16 fcs = crc16(0, (u8 *)lh, count - L2CAP_FCS_SIZE);
-               put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
-       }
-
-       skb->priority = HCI_PRIO_MAX;
-       l2cap_do_send(chan, skb);
-}
-
-static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control)
-{
-       if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
-               control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
-               set_bit(CONN_RNR_SENT, &chan->conn_state);
-       } else
-               control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
-
-       control |= __set_reqseq(chan, chan->buffer_seq);
-
-       l2cap_send_sframe(chan, control);
-}
-
-static u16 __pack_enhanced_control(struct l2cap_ctrl *control)
-{
-       u16 packed;
-
-       packed = control->reqseq << L2CAP_CTRL_REQSEQ_SHIFT;
-       packed |= control->final << L2CAP_CTRL_FINAL_SHIFT;
-
-       if (control->sframe) {
-               packed |= control->poll << L2CAP_CTRL_POLL_SHIFT;
-               packed |= control->super << L2CAP_CTRL_SUPER_SHIFT;
-               packed |= L2CAP_CTRL_FRAME_TYPE;
-       } else {
-               packed |= control->sar << L2CAP_CTRL_SAR_SHIFT;
-               packed |= control->txseq << L2CAP_CTRL_TXSEQ_SHIFT;
-       }
-
-       return packed;
-}
-
 static void __unpack_enhanced_control(u16 enh, struct l2cap_ctrl *control)
 {
        control->reqseq = (enh & L2CAP_CTRL_REQSEQ) >> L2CAP_CTRL_REQSEQ_SHIFT;
@@ -829,25 +741,6 @@ static void __unpack_enhanced_control(u16 enh, struct l2cap_ctrl *control)
        }
 }
 
-static u32 __pack_extended_control(struct l2cap_ctrl *control)
-{
-       u32 packed;
-
-       packed = control->reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT;
-       packed |= control->final << L2CAP_EXT_CTRL_FINAL_SHIFT;
-
-       if (control->sframe) {
-               packed |= control->poll << L2CAP_EXT_CTRL_POLL_SHIFT;
-               packed |= control->super << L2CAP_EXT_CTRL_SUPER_SHIFT;
-               packed |= L2CAP_EXT_CTRL_FRAME_TYPE;
-       } else {
-               packed |= control->sar << L2CAP_EXT_CTRL_SAR_SHIFT;
-               packed |= control->txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT;
-       }
-
-       return packed;
-}
-
 static void __unpack_extended_control(u32 ext, struct l2cap_ctrl *control)
 {
        control->reqseq = (ext & L2CAP_EXT_CTRL_REQSEQ) >> L2CAP_EXT_CTRL_REQSEQ_SHIFT;
@@ -884,6 +777,44 @@ static inline void __unpack_control(struct l2cap_chan *chan,
        }
 }
 
+static u32 __pack_extended_control(struct l2cap_ctrl *control)
+{
+       u32 packed;
+
+       packed = control->reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT;
+       packed |= control->final << L2CAP_EXT_CTRL_FINAL_SHIFT;
+
+       if (control->sframe) {
+               packed |= control->poll << L2CAP_EXT_CTRL_POLL_SHIFT;
+               packed |= control->super << L2CAP_EXT_CTRL_SUPER_SHIFT;
+               packed |= L2CAP_EXT_CTRL_FRAME_TYPE;
+       } else {
+               packed |= control->sar << L2CAP_EXT_CTRL_SAR_SHIFT;
+               packed |= control->txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT;
+       }
+
+       return packed;
+}
+
+static u16 __pack_enhanced_control(struct l2cap_ctrl *control)
+{
+       u16 packed;
+
+       packed = control->reqseq << L2CAP_CTRL_REQSEQ_SHIFT;
+       packed |= control->final << L2CAP_CTRL_FINAL_SHIFT;
+
+       if (control->sframe) {
+               packed |= control->poll << L2CAP_CTRL_POLL_SHIFT;
+               packed |= control->super << L2CAP_CTRL_SUPER_SHIFT;
+               packed |= L2CAP_CTRL_FRAME_TYPE;
+       } else {
+               packed |= control->sar << L2CAP_CTRL_SAR_SHIFT;
+               packed |= control->txseq << L2CAP_CTRL_TXSEQ_SHIFT;
+       }
+
+       return packed;
+}
+
 static inline void __pack_control(struct l2cap_chan *chan,
                                  struct l2cap_ctrl *control,
                                  struct sk_buff *skb)
@@ -897,6 +828,68 @@ static inline void __pack_control(struct l2cap_chan *chan,
        }
 }
 
+static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control)
+{
+       struct sk_buff *skb;
+       struct l2cap_hdr *lh;
+       struct l2cap_conn *conn = chan->conn;
+       int count, hlen;
+
+       if (chan->state != BT_CONNECTED)
+               return;
+
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               hlen = L2CAP_EXT_HDR_SIZE;
+       else
+               hlen = L2CAP_ENH_HDR_SIZE;
+
+       if (chan->fcs == L2CAP_FCS_CRC16)
+               hlen += L2CAP_FCS_SIZE;
+
+       BT_DBG("chan %p, control 0x%8.8x", chan, control);
+
+       count = min_t(unsigned int, conn->mtu, hlen);
+
+       control |= __set_sframe(chan);
+
+       if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
+               control |= __set_ctrl_final(chan);
+
+       if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state))
+               control |= __set_ctrl_poll(chan);
+
+       skb = bt_skb_alloc(count, GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+       lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE);
+       lh->cid = cpu_to_le16(chan->dcid);
+
+       __put_control(chan, control, skb_put(skb, __ctrl_size(chan)));
+
+       if (chan->fcs == L2CAP_FCS_CRC16) {
+               u16 fcs = crc16(0, (u8 *)lh, count - L2CAP_FCS_SIZE);
+               put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
+       }
+
+       skb->priority = HCI_PRIO_MAX;
+       l2cap_do_send(chan, skb);
+}
+
+static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control)
+{
+       if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+               control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
+               set_bit(CONN_RNR_SENT, &chan->conn_state);
+       } else
+               control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
+
+       control |= __set_reqseq(chan, chan->buffer_seq);
+
+       l2cap_send_sframe(chan, control);
+}
+
 static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
 {
        return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
@@ -917,10 +910,38 @@ static void l2cap_send_conn_req(struct l2cap_chan *chan)
        l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
 }
 
+static void l2cap_chan_ready(struct l2cap_chan *chan)
+{
+       struct sock *sk = chan->sk;
+       struct sock *parent;
+
+       lock_sock(sk);
+
+       parent = bt_sk(sk)->parent;
+
+       BT_DBG("sk %p, parent %p", sk, parent);
+
+       chan->conf_state = 0;
+       __clear_chan_timer(chan);
+
+       __l2cap_state_change(chan, BT_CONNECTED);
+       sk->sk_state_change(sk);
+
+       if (parent)
+               parent->sk_data_ready(parent, 0);
+
+       release_sock(sk);
+}
+
 static void l2cap_do_start(struct l2cap_chan *chan)
 {
        struct l2cap_conn *conn = chan->conn;
 
+       if (conn->hcon->type == LE_LINK) {
+               l2cap_chan_ready(chan);
+               return;
+       }
+
        if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
                if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
                        return;
@@ -1066,11 +1087,12 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
        mutex_unlock(&conn->chan_lock);
 }
 
-/* Find socket with cid and source bdaddr.
+/* Find socket with cid and source/destination bdaddr.
  * Returns closest match, locked.
  */
 static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
-                                                   bdaddr_t *src)
+                                                   bdaddr_t *src,
+                                                   bdaddr_t *dst)
 {
        struct l2cap_chan *c, *c1 = NULL;
 
@@ -1083,14 +1105,22 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
                        continue;
 
                if (c->scid == cid) {
+                       int src_match, dst_match;
+                       int src_any, dst_any;
+
                        /* Exact match. */
-                       if (!bacmp(&bt_sk(sk)->src, src)) {
+                       src_match = !bacmp(&bt_sk(sk)->src, src);
+                       dst_match = !bacmp(&bt_sk(sk)->dst, dst);
+                       if (src_match && dst_match) {
                                read_unlock(&chan_list_lock);
                                return c;
                        }
 
                        /* Closest match */
-                       if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
+                       src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY);
+                       dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY);
+                       if ((src_match && dst_any) || (src_any && dst_match) ||
+                           (src_any && dst_any))
                                c1 = c;
                }
        }
@@ -1109,7 +1139,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 
        /* Check if we have socket listening on cid */
        pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA,
-                                                       conn->src);
+                                         conn->src, conn->dst);
        if (!pchan)
                return;
 
@@ -1147,29 +1177,6 @@ clean:
        release_sock(parent);
 }
 
-static void l2cap_chan_ready(struct l2cap_chan *chan)
-{
-       struct sock *sk = chan->sk;
-       struct sock *parent;
-
-       lock_sock(sk);
-
-       parent = bt_sk(sk)->parent;
-
-       BT_DBG("sk %p, parent %p", sk, parent);
-
-       chan->conf_state = 0;
-       __clear_chan_timer(chan);
-
-       __l2cap_state_change(chan, BT_CONNECTED);
-       sk->sk_state_change(sk);
-
-       if (parent)
-               parent->sk_data_ready(parent, 0);
-
-       release_sock(sk);
-}
-
 static void l2cap_conn_ready(struct l2cap_conn *conn)
 {
        struct l2cap_chan *chan;
@@ -1253,6 +1260,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
 
        /* Kill channels */
        list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
+               l2cap_chan_hold(chan);
                l2cap_chan_lock(chan);
 
                l2cap_chan_del(chan, err);
@@ -1260,6 +1268,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
                l2cap_chan_unlock(chan);
 
                chan->ops->close(chan->data);
+               l2cap_chan_put(chan);
        }
 
        mutex_unlock(&conn->chan_lock);
@@ -1337,10 +1346,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
 
 /* ---- Socket interface ---- */
 
-/* Find socket with psm and source bdaddr.
+/* Find socket with psm and source / destination bdaddr.
  * Returns closest match.
  */
-static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr_t *src)
+static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
+                                                  bdaddr_t *src,
+                                                  bdaddr_t *dst)
 {
        struct l2cap_chan *c, *c1 = NULL;
 
@@ -1353,14 +1364,22 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr
                        continue;
 
                if (c->psm == psm) {
+                       int src_match, dst_match;
+                       int src_any, dst_any;
+
                        /* Exact match. */
-                       if (!bacmp(&bt_sk(sk)->src, src)) {
+                       src_match = !bacmp(&bt_sk(sk)->src, src);
+                       dst_match = !bacmp(&bt_sk(sk)->dst, dst);
+                       if (src_match && dst_match) {
                                read_unlock(&chan_list_lock);
                                return c;
                        }
 
                        /* Closest match */
-                       if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
+                       src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY);
+                       dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY);
+                       if ((src_match && dst_any) || (src_any && dst_match) ||
+                           (src_any && dst_any))
                                c1 = c;
                }
        }
@@ -1370,7 +1389,8 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr
        return c1;
 }
 
-int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst)
+int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
+                      bdaddr_t *dst, u8 dst_type)
 {
        struct sock *sk = chan->sk;
        bdaddr_t *src = &bt_sk(sk)->src;
@@ -1380,8 +1400,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d
        __u8 auth_type;
        int err;
 
-       BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst),
-              __le16_to_cpu(chan->psm));
+       BT_DBG("%s -> %s (type %u) psm 0x%2.2x", batostr(src), batostr(dst),
+              dst_type, __le16_to_cpu(chan->psm));
 
        hdev = hci_get_route(dst, src);
        if (!hdev)
@@ -1455,11 +1475,11 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d
        auth_type = l2cap_get_auth_type(chan);
 
        if (chan->dcid == L2CAP_CID_LE_DATA)
-               hcon = hci_connect(hdev, LE_LINK, dst,
-                                       chan->sec_level, auth_type);
+               hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
+                                  chan->sec_level, auth_type);
        else
-               hcon = hci_connect(hdev, ACL_LINK, dst,
-                                       chan->sec_level, auth_type);
+               hcon = hci_connect(hdev, ACL_LINK, dst, dst_type,
+                                  chan->sec_level, auth_type);
 
        if (IS_ERR(hcon)) {
                err = PTR_ERR(hcon);
@@ -1473,6 +1493,18 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d
                goto done;
        }
 
+       if (hcon->type == LE_LINK) {
+               err = 0;
+
+               if (!list_empty(&conn->chan_l)) {
+                       err = -EBUSY;
+                       hci_conn_put(hcon);
+               }
+
+               if (err)
+                       goto done;
+       }
+
        /* Update source addr of the socket */
        bacpy(src, conn->src);
 
@@ -1583,7 +1615,7 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
 
        while ((skb = skb_peek(&chan->tx_q)) &&
                        chan->unacked_frames) {
-               if (bt_cb(skb)->tx_seq == chan->expected_ack_seq)
+               if (bt_cb(skb)->control.txseq == chan->expected_ack_seq)
                        break;
 
                skb = skb_dequeue(&chan->tx_q);
@@ -1605,6 +1637,7 @@ static void l2cap_streaming_send(struct l2cap_chan *chan)
        while ((skb = skb_dequeue(&chan->tx_q))) {
                control = __get_control(chan, skb->data + L2CAP_HDR_SIZE);
                control |= __set_txseq(chan, chan->next_tx_seq);
+               control |= __set_ctrl_sar(chan, bt_cb(skb)->control.sar);
                __put_control(chan, control, skb->data + L2CAP_HDR_SIZE);
 
                if (chan->fcs == L2CAP_FCS_CRC16) {
@@ -1630,21 +1663,21 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
        if (!skb)
                return;
 
-       while (bt_cb(skb)->tx_seq != tx_seq) {
+       while (bt_cb(skb)->control.txseq != tx_seq) {
                if (skb_queue_is_last(&chan->tx_q, skb))
                        return;
 
                skb = skb_queue_next(&chan->tx_q, skb);
        }
 
-       if (chan->remote_max_tx &&
-                       bt_cb(skb)->retries == chan->remote_max_tx) {
+       if (bt_cb(skb)->control.retries == chan->remote_max_tx &&
+           chan->remote_max_tx) {
                l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
                return;
        }
 
        tx_skb = skb_clone(skb, GFP_ATOMIC);
-       bt_cb(skb)->retries++;
+       bt_cb(skb)->control.retries++;
 
        control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE);
        control &= __get_sar_mask(chan);
@@ -1677,17 +1710,20 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
        if (chan->state != BT_CONNECTED)
                return -ENOTCONN;
 
+       if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
+               return 0;
+
        while ((skb = chan->tx_send_head) && (!l2cap_tx_window_full(chan))) {
 
-               if (chan->remote_max_tx &&
-                               bt_cb(skb)->retries == chan->remote_max_tx) {
+               if (bt_cb(skb)->control.retries == chan->remote_max_tx &&
+                   chan->remote_max_tx) {
                        l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
                        break;
                }
 
                tx_skb = skb_clone(skb, GFP_ATOMIC);
 
-               bt_cb(skb)->retries++;
+               bt_cb(skb)->control.retries++;
 
                control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE);
                control &= __get_sar_mask(chan);
@@ -1697,6 +1733,7 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
 
                control |= __set_reqseq(chan, chan->buffer_seq);
                control |= __set_txseq(chan, chan->next_tx_seq);
+               control |= __set_ctrl_sar(chan, bt_cb(skb)->control.sar);
 
                __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE);
 
@@ -1711,11 +1748,11 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
 
                __set_retrans_timer(chan);
 
-               bt_cb(skb)->tx_seq = chan->next_tx_seq;
+               bt_cb(skb)->control.txseq = chan->next_tx_seq;
 
                chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
 
-               if (bt_cb(skb)->retries == 1) {
+               if (bt_cb(skb)->control.retries == 1) {
                        chan->unacked_frames++;
 
                        if (!nsent++)
@@ -1802,13 +1839,17 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
        /* Continuation fragments (no L2CAP header) */
        frag = &skb_shinfo(skb)->frag_list;
        while (len) {
+               struct sk_buff *tmp;
+
                count = min_t(unsigned int, conn->mtu, len);
 
-               *frag = chan->ops->alloc_skb(chan, count,
-                                            msg->msg_flags & MSG_DONTWAIT);
+               tmp = chan->ops->alloc_skb(chan, count,
+                                          msg->msg_flags & MSG_DONTWAIT);
+               if (IS_ERR(tmp))
+                       return PTR_ERR(tmp);
+
+               *frag = tmp;
 
-               if (IS_ERR(*frag))
-                       return PTR_ERR(*frag);
                if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count))
                        return -EFAULT;
 
@@ -1817,6 +1858,9 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
                sent += count;
                len  -= count;
 
+               skb->len += (*frag)->len;
+               skb->data_len += (*frag)->len;
+
                frag = &(*frag)->next;
        }
 
@@ -1846,8 +1890,8 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
        /* Create L2CAP header */
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->cid = cpu_to_le16(chan->dcid);
-       lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
-       put_unaligned(chan->psm, skb_put(skb, 2));
+       lh->len = cpu_to_le16(len + L2CAP_PSMLEN_SIZE);
+       put_unaligned(chan->psm, skb_put(skb, L2CAP_PSMLEN_SIZE));
 
        err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
        if (unlikely(err < 0)) {
@@ -1863,14 +1907,14 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
 {
        struct l2cap_conn *conn = chan->conn;
        struct sk_buff *skb;
-       int err, count, hlen = L2CAP_HDR_SIZE;
+       int err, count;
        struct l2cap_hdr *lh;
 
        BT_DBG("chan %p len %d", chan, (int)len);
 
-       count = min_t(unsigned int, (conn->mtu - hlen), len);
+       count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len);
 
-       skb = chan->ops->alloc_skb(chan, count + hlen,
+       skb = chan->ops->alloc_skb(chan, count + L2CAP_HDR_SIZE,
                                   msg->msg_flags & MSG_DONTWAIT);
        if (IS_ERR(skb))
                return skb;
@@ -1880,7 +1924,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
        /* Create L2CAP header */
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->cid = cpu_to_le16(chan->dcid);
-       lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
+       lh->len = cpu_to_le16(len);
 
        err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
        if (unlikely(err < 0)) {
@@ -1892,7 +1936,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
 
 static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
                                                struct msghdr *msg, size_t len,
-                                               u32 control, u16 sdulen)
+                                               u16 sdulen)
 {
        struct l2cap_conn *conn = chan->conn;
        struct sk_buff *skb;
@@ -1927,7 +1971,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
        lh->cid = cpu_to_le16(chan->dcid);
        lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
 
-       __put_control(chan, control, skb_put(skb, __ctrl_size(chan)));
+       __put_control(chan, 0, skb_put(skb, __ctrl_size(chan)));
 
        if (sdulen)
                put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
@@ -1941,61 +1985,82 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
        if (chan->fcs == L2CAP_FCS_CRC16)
                put_unaligned_le16(0, skb_put(skb, L2CAP_FCS_SIZE));
 
-       bt_cb(skb)->retries = 0;
+       bt_cb(skb)->control.retries = 0;
        return skb;
 }
 
-static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+static int l2cap_segment_sdu(struct l2cap_chan *chan,
+                            struct sk_buff_head *seg_queue,
+                            struct msghdr *msg, size_t len)
 {
        struct sk_buff *skb;
-       struct sk_buff_head sar_queue;
-       u32 control;
-       size_t size = 0;
+       u16 sdu_len;
+       size_t pdu_len;
+       int err = 0;
+       u8 sar;
 
-       skb_queue_head_init(&sar_queue);
-       control = __set_ctrl_sar(chan, L2CAP_SAR_START);
-       skb = l2cap_create_iframe_pdu(chan, msg, chan->remote_mps, control, len);
-       if (IS_ERR(skb))
-               return PTR_ERR(skb);
+       BT_DBG("chan %p, msg %p, len %d", chan, msg, (int)len);
+
+       /* It is critical that ERTM PDUs fit in a single HCI fragment,
+        * so fragmented skbs are not used.  The HCI layer's handling
+        * of fragmented skbs is not compatible with ERTM's queueing.
+        */
 
-       __skb_queue_tail(&sar_queue, skb);
-       len -= chan->remote_mps;
-       size += chan->remote_mps;
+       /* PDU size is derived from the HCI MTU */
+       pdu_len = chan->conn->mtu;
 
-       while (len > 0) {
-               size_t buflen;
+       pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD);
 
-               if (len > chan->remote_mps) {
-                       control = __set_ctrl_sar(chan, L2CAP_SAR_CONTINUE);
-                       buflen = chan->remote_mps;
-               } else {
-                       control = __set_ctrl_sar(chan, L2CAP_SAR_END);
-                       buflen = len;
-               }
+       /* Adjust for largest possible L2CAP overhead. */
+       pdu_len -= L2CAP_EXT_HDR_SIZE + L2CAP_FCS_SIZE;
+
+       /* Remote device may have requested smaller PDUs */
+       pdu_len = min_t(size_t, pdu_len, chan->remote_mps);
+
+       if (len <= pdu_len) {
+               sar = L2CAP_SAR_UNSEGMENTED;
+               sdu_len = 0;
+               pdu_len = len;
+       } else {
+               sar = L2CAP_SAR_START;
+               sdu_len = len;
+               pdu_len -= L2CAP_SDULEN_SIZE;
+       }
+
+       while (len > 0) {
+               skb = l2cap_create_iframe_pdu(chan, msg, pdu_len, sdu_len);
 
-               skb = l2cap_create_iframe_pdu(chan, msg, buflen, control, 0);
                if (IS_ERR(skb)) {
-                       skb_queue_purge(&sar_queue);
+                       __skb_queue_purge(seg_queue);
                        return PTR_ERR(skb);
                }
 
-               __skb_queue_tail(&sar_queue, skb);
-               len -= buflen;
-               size += buflen;
+               bt_cb(skb)->control.sar = sar;
+               __skb_queue_tail(seg_queue, skb);
+
+               len -= pdu_len;
+               if (sdu_len) {
+                       sdu_len = 0;
+                       pdu_len += L2CAP_SDULEN_SIZE;
+               }
+
+               if (len <= pdu_len) {
+                       sar = L2CAP_SAR_END;
+                       pdu_len = len;
+               } else {
+                       sar = L2CAP_SAR_CONTINUE;
+               }
        }
-       skb_queue_splice_tail(&sar_queue, &chan->tx_q);
-       if (chan->tx_send_head == NULL)
-               chan->tx_send_head = sar_queue.next;
 
-       return size;
+       return err;
 }
 
 int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
                                                                u32 priority)
 {
        struct sk_buff *skb;
-       u32 control;
        int err;
+       struct sk_buff_head seg_queue;
 
        /* Connectionless channel */
        if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
@@ -2024,42 +2089,47 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
 
        case L2CAP_MODE_ERTM:
        case L2CAP_MODE_STREAMING:
-               /* Entire SDU fits into one PDU */
-               if (len <= chan->remote_mps) {
-                       control = __set_ctrl_sar(chan, L2CAP_SAR_UNSEGMENTED);
-                       skb = l2cap_create_iframe_pdu(chan, msg, len, control,
-                                                                       0);
-                       if (IS_ERR(skb))
-                               return PTR_ERR(skb);
+               /* Check outgoing MTU */
+               if (len > chan->omtu) {
+                       err = -EMSGSIZE;
+                       break;
+               }
 
-                       __skb_queue_tail(&chan->tx_q, skb);
+               __skb_queue_head_init(&seg_queue);
 
-                       if (chan->tx_send_head == NULL)
-                               chan->tx_send_head = skb;
+               /* Do segmentation before calling in to the state machine,
+                * since it's possible to block while waiting for memory
+                * allocation.
+                */
+               err = l2cap_segment_sdu(chan, &seg_queue, msg, len);
 
-               } else {
-                       /* Segment SDU into multiples PDUs */
-                       err = l2cap_sar_segment_sdu(chan, msg, len);
-                       if (err < 0)
-                               return err;
+               /* The channel could have been closed while segmenting,
+                * check that it is still connected.
+                */
+               if (chan->state != BT_CONNECTED) {
+                       __skb_queue_purge(&seg_queue);
+                       err = -ENOTCONN;
                }
 
-               if (chan->mode == L2CAP_MODE_STREAMING) {
-                       l2cap_streaming_send(chan);
-                       err = len;
+               if (err)
                        break;
-               }
 
-               if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
-                               test_bit(CONN_WAIT_F, &chan->conn_state)) {
-                       err = len;
-                       break;
-               }
+               if (chan->mode == L2CAP_MODE_ERTM && chan->tx_send_head == NULL)
+                       chan->tx_send_head = seg_queue.next;
+               skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
+
+               if (chan->mode == L2CAP_MODE_ERTM)
+                       err = l2cap_ertm_send(chan);
+               else
+                       l2cap_streaming_send(chan);
 
-               err = l2cap_ertm_send(chan);
                if (err >= 0)
                        err = len;
 
+               /* If the skbs were not queued for sending, they'll still be in
+                * seg_queue and need to be purged.
+                */
+               __skb_queue_purge(&seg_queue);
                break;
 
        default:
@@ -2277,11 +2347,25 @@ static inline int l2cap_ertm_init(struct l2cap_chan *chan)
 {
        int err;
 
+       chan->next_tx_seq = 0;
+       chan->expected_tx_seq = 0;
        chan->expected_ack_seq = 0;
        chan->unacked_frames = 0;
        chan->buffer_seq = 0;
        chan->num_acked = 0;
        chan->frames_sent = 0;
+       chan->last_acked_seq = 0;
+       chan->sdu = NULL;
+       chan->sdu_last_frag = NULL;
+       chan->sdu_len = 0;
+
+       skb_queue_head_init(&chan->tx_q);
+
+       if (chan->mode != L2CAP_MODE_ERTM)
+               return 0;
+
+       chan->rx_state = L2CAP_RX_STATE_RECV;
+       chan->tx_state = L2CAP_TX_STATE_XMIT;
 
        INIT_DELAYED_WORK(&chan->retrans_timer, l2cap_retrans_timeout);
        INIT_DELAYED_WORK(&chan->monitor_timer, l2cap_monitor_timeout);
@@ -2887,7 +2971,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
        BT_DBG("psm 0x%2.2x scid 0x%4.4x", __le16_to_cpu(psm), scid);
 
        /* Check if we have socket listening on psm */
-       pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src);
+       pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src, conn->dst);
        if (!pchan) {
                result = L2CAP_CR_BAD_PSM;
                goto sendresp;
@@ -3155,10 +3239,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
 
                l2cap_state_change(chan, BT_CONNECTED);
 
-               chan->next_tx_seq = 0;
-               chan->expected_tx_seq = 0;
-               skb_queue_head_init(&chan->tx_q);
-               if (chan->mode == L2CAP_MODE_ERTM)
+               if (chan->mode == L2CAP_MODE_ERTM ||
+                   chan->mode == L2CAP_MODE_STREAMING)
                        err = l2cap_ertm_init(chan);
 
                if (err < 0)
@@ -3290,10 +3372,8 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
                set_default_fcs(chan);
 
                l2cap_state_change(chan, BT_CONNECTED);
-               chan->next_tx_seq = 0;
-               chan->expected_tx_seq = 0;
-               skb_queue_head_init(&chan->tx_q);
-               if (chan->mode ==  L2CAP_MODE_ERTM)
+               if (chan->mode == L2CAP_MODE_ERTM ||
+                   chan->mode == L2CAP_MODE_STREAMING)
                        err = l2cap_ertm_init(chan);
 
                if (err < 0)
@@ -3340,11 +3420,13 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
        sk->sk_shutdown = SHUTDOWN_MASK;
        release_sock(sk);
 
+       l2cap_chan_hold(chan);
        l2cap_chan_del(chan, ECONNRESET);
 
        l2cap_chan_unlock(chan);
 
        chan->ops->close(chan->data);
+       l2cap_chan_put(chan);
 
        mutex_unlock(&conn->chan_lock);
 
@@ -3372,11 +3454,13 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
 
        l2cap_chan_lock(chan);
 
+       l2cap_chan_hold(chan);
        l2cap_chan_del(chan, 0);
 
        l2cap_chan_unlock(chan);
 
        chan->ops->close(chan->data);
+       l2cap_chan_put(chan);
 
        mutex_unlock(&conn->chan_lock);
 
@@ -3913,19 +3997,19 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
        struct sk_buff *next_skb;
        int tx_seq_offset, next_tx_seq_offset;
 
-       bt_cb(skb)->tx_seq = tx_seq;
-       bt_cb(skb)->sar = sar;
+       bt_cb(skb)->control.txseq = tx_seq;
+       bt_cb(skb)->control.sar = sar;
 
        next_skb = skb_peek(&chan->srej_q);
 
        tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
 
        while (next_skb) {
-               if (bt_cb(next_skb)->tx_seq == tx_seq)
+               if (bt_cb(next_skb)->control.txseq == tx_seq)
                        return -EINVAL;
 
                next_tx_seq_offset = __seq_offset(chan,
-                               bt_cb(next_skb)->tx_seq, chan->buffer_seq);
+                       bt_cb(next_skb)->control.txseq, chan->buffer_seq);
 
                if (next_tx_seq_offset > tx_seq_offset) {
                        __skb_queue_before(&chan->srej_q, next_skb, skb);
@@ -4097,11 +4181,11 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq)
                        !test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
                int err;
 
-               if (bt_cb(skb)->tx_seq != tx_seq)
+               if (bt_cb(skb)->control.txseq != tx_seq)
                        break;
 
                skb = skb_dequeue(&chan->srej_q);
-               control = __set_ctrl_sar(chan, bt_cb(skb)->sar);
+               control = __set_ctrl_sar(chan, bt_cb(skb)->control.sar);
                err = l2cap_reassemble_sdu(chan, skb, control);
 
                if (err < 0) {
@@ -4272,8 +4356,8 @@ expected:
        chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
 
        if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
-               bt_cb(skb)->tx_seq = tx_seq;
-               bt_cb(skb)->sar = sar;
+               bt_cb(skb)->control.txseq = tx_seq;
+               bt_cb(skb)->control.sar = sar;
                __skb_queue_tail(&chan->srej_q, skb);
                return 0;
        }
@@ -4627,7 +4711,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str
 {
        struct l2cap_chan *chan;
 
-       chan = l2cap_global_chan_by_psm(0, psm, conn->src);
+       chan = l2cap_global_chan_by_psm(0, psm, conn->src, conn->dst);
        if (!chan)
                goto drop;
 
@@ -4653,7 +4737,7 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, u16 cid,
 {
        struct l2cap_chan *chan;
 
-       chan = l2cap_global_chan_by_scid(0, cid, conn->src);
+       chan = l2cap_global_chan_by_scid(0, cid, conn->src, conn->dst);
        if (!chan)
                goto drop;
 
@@ -4842,6 +4926,11 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
 
                if (!status && (chan->state == BT_CONNECTED ||
                                                chan->state == BT_CONFIG)) {
+                       struct sock *sk = chan->sk;
+
+                       bt_sk(sk)->suspended = false;
+                       sk->sk_state_change(sk);
+
                        l2cap_check_encryption(chan, encrypt);
                        l2cap_chan_unlock(chan);
                        continue;
This page took 0.041145 seconds and 5 git commands to generate.