From: Andrei Emeltchenko <[email protected]>
First draft patches applied to my previous EFS patches. Please comment.
Adds support for extended window size option (EWS) and extended control
field. Code partly based on Atheros patches sent a year ago and Qualcomm
code git://codeaurora.org/kernel/msm.git.
To decode EWS option and extended control field please apply patch to hcidump
which I sent to linux-bluetooth.
Andrei Emeltchenko (5):
Bluetooth: extended window size option support
Bluetooth: l2cap extended control field support
Bluetooth: EWS: support extended seq numbers
Bluetooth: remove magic numbers in l2cap
Bluetooth: prevent unneeded fragmentation in l2cap
include/net/bluetooth/l2cap.h | 304 ++++++++++++++++++++++----
net/bluetooth/l2cap_core.c | 481 +++++++++++++++++++++++++----------------
net/bluetooth/l2cap_sock.c | 11 +-
3 files changed, 562 insertions(+), 234 deletions(-)
--
1.7.4.1
On Tue, 30 Aug 2011, Marcel Holtmann wrote:
> Hi Mat,
>
>>> First draft patches applied to my previous EFS patches. Please comment.
>>>
>>> Adds support for extended window size option (EWS) and extended control
>>> field. Code partly based on Atheros patches sent a year ago and Qualcomm
>>> code git://codeaurora.org/kernel/msm.git.
>>>
>>> To decode EWS option and extended control field please apply patch to hcidump
>>> which I sent to linux-bluetooth.
>>>
>>> Andrei Emeltchenko (5):
>>> Bluetooth: extended window size option support
>>> Bluetooth: l2cap extended control field support
>>> Bluetooth: EWS: support extended seq numbers
>>> Bluetooth: remove magic numbers in l2cap
>>> Bluetooth: prevent unneeded fragmentation in l2cap
>>>
>>> include/net/bluetooth/l2cap.h | 304 ++++++++++++++++++++++----
>>> net/bluetooth/l2cap_core.c | 481 +++++++++++++++++++++++++----------------
>>> net/bluetooth/l2cap_sock.c | 11 +-
>>> 3 files changed, 562 insertions(+), 234 deletions(-)
>>>
>>> --
>>> 1.7.4.1
>>
>> Extended window size is next on my list to upstream. As you probably
>> noticed, the git://codeaurora.org/kernel/msm.git code has a slightly
>> different approach to handling the two control field types. The
>> fields are handled in a struct that's part of the bt_cb control block,
>> and only read/written in wire format when the frame is first received
>> or about to be sent. Are there any issues with this method (other
>> than the fact that I haven't upstreamed it yet)? Some of the
>> subsequent L2CAP state machine changes depend on this.
>
> in general using bt_cb is fine, but it depends a little bit on the
> details. The space of the skb cb is limited. So we need to be cautious
> here.
Yes, I've been keeping that limited space in mind. Some of the data
already in bt_cb was control field data, so I removed the redundant
entries.
> We are also currently making bt_cb global for all protocols. Maybe one
> way is to have l2cap_cb for this. We need to see what makes more sense
> and what is more efficient.
>
>> Please let me know if there are specific features from the
>> git://codeaurora.org/kernel/msm.git code base that you would like to
>> prioritize for upstreaming - there's a lot to do to bring in those
>> L2CAP and AMP changes, and I'll have more time to devote to it in
>> mid-September. I think we can gain a lot by reviewing each other's
>> patches and coordinating our efforts.
>
> Maybe just for reference, you should post a set of patches to the
> mailing list so they are archived and seen by others. Digging into a
> generic msm.git tree is not really efficient.
I'll take a look at that - I'd have to split up the commits in that
git repository in order for them to be suitable for the mailing list.
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Mat
On Tue, Aug 30, 2011 at 7:10 PM, Mat Martineau <[email protected]> wrote:
>
> Hi Andrei -
>
> On Tue, 30 Aug 2011, Emeltchenko Andrei wrote:
>
>> From: Andrei Emeltchenko <[email protected]>
>>
>> First draft patches applied to my previous EFS patches. Please comment.
>>
>> Adds support for extended window size option (EWS) and extended control
>> field. Code partly based on Atheros patches sent a year ago and Qualcomm
>> code git://codeaurora.org/kernel/msm.git.
>>
>> To decode EWS option and extended control field please apply patch to
>> hcidump
>> which I sent to linux-bluetooth.
>>
>> Andrei Emeltchenko (5):
>> ?Bluetooth: extended window size option support
>> ?Bluetooth: l2cap extended control field support
>> ?Bluetooth: EWS: support extended seq numbers
>> ?Bluetooth: remove magic numbers in l2cap
>> ?Bluetooth: prevent unneeded fragmentation in l2cap
>>
>> include/net/bluetooth/l2cap.h | ?304 ++++++++++++++++++++++----
>> net/bluetooth/l2cap_core.c ? ?| ?481
>> +++++++++++++++++++++++++----------------
>> net/bluetooth/l2cap_sock.c ? ?| ? 11 +-
>> 3 files changed, 562 insertions(+), 234 deletions(-)
>>
>> --
>> 1.7.4.1
>
> Extended window size is next on my list to upstream. ?As you probably
> noticed, the git://codeaurora.org/kernel/msm.git code has a slightly
> different approach to handling the two control field types. ?The fields are
> handled in a struct that's part of the bt_cb control block, and only
> read/written in wire format when the frame is first received or about to be
> sent. ?Are there any issues with this method (other than the fact that I
> haven't upstreamed it yet)? ?Some of the subsequent L2CAP state machine
> changes depend on this.
AFAIK this was discussed about one year ago:
http://www.spinics.net/lists/linux-arm-msm/msg01022.html
> Please let me know if there are specific features from the
> git://codeaurora.org/kernel/msm.git code base that you would like to
> prioritize for upstreaming - there's a lot to do to bring in those L2CAP and
> AMP changes, and I'll have more time to devote to it in mid-September. ?I
> think we can gain a lot by reviewing each other's patches and coordinating
> our efforts.
I think the same. We will come up with our priority list soon.
Regards,
Andrei
Hi Mat,
> > First draft patches applied to my previous EFS patches. Please comment.
> >
> > Adds support for extended window size option (EWS) and extended control
> > field. Code partly based on Atheros patches sent a year ago and Qualcomm
> > code git://codeaurora.org/kernel/msm.git.
> >
> > To decode EWS option and extended control field please apply patch to hcidump
> > which I sent to linux-bluetooth.
> >
> > Andrei Emeltchenko (5):
> > Bluetooth: extended window size option support
> > Bluetooth: l2cap extended control field support
> > Bluetooth: EWS: support extended seq numbers
> > Bluetooth: remove magic numbers in l2cap
> > Bluetooth: prevent unneeded fragmentation in l2cap
> >
> > include/net/bluetooth/l2cap.h | 304 ++++++++++++++++++++++----
> > net/bluetooth/l2cap_core.c | 481 +++++++++++++++++++++++++----------------
> > net/bluetooth/l2cap_sock.c | 11 +-
> > 3 files changed, 562 insertions(+), 234 deletions(-)
> >
> > --
> > 1.7.4.1
>
> Extended window size is next on my list to upstream. As you probably
> noticed, the git://codeaurora.org/kernel/msm.git code has a slightly
> different approach to handling the two control field types. The
> fields are handled in a struct that's part of the bt_cb control block,
> and only read/written in wire format when the frame is first received
> or about to be sent. Are there any issues with this method (other
> than the fact that I haven't upstreamed it yet)? Some of the
> subsequent L2CAP state machine changes depend on this.
in general using bt_cb is fine, but it depends a little bit on the
details. The space of the skb cb is limited. So we need to be cautious
here.
We are also currently making bt_cb global for all protocols. Maybe one
way is to have l2cap_cb for this. We need to see what makes more sense
and what is more efficient.
> Please let me know if there are specific features from the
> git://codeaurora.org/kernel/msm.git code base that you would like to
> prioritize for upstreaming - there's a lot to do to bring in those
> L2CAP and AMP changes, and I'll have more time to devote to it in
> mid-September. I think we can gain a lot by reviewing each other's
> patches and coordinating our efforts.
Maybe just for reference, you should post a set of patches to the
mailing list so they are archived and seen by others. Digging into a
generic msm.git tree is not really efficient.
Regards
Marcel
Hi Andrei -
On Tue, 30 Aug 2011, Emeltchenko Andrei wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> First draft patches applied to my previous EFS patches. Please comment.
>
> Adds support for extended window size option (EWS) and extended control
> field. Code partly based on Atheros patches sent a year ago and Qualcomm
> code git://codeaurora.org/kernel/msm.git.
>
> To decode EWS option and extended control field please apply patch to hcidump
> which I sent to linux-bluetooth.
>
> Andrei Emeltchenko (5):
> Bluetooth: extended window size option support
> Bluetooth: l2cap extended control field support
> Bluetooth: EWS: support extended seq numbers
> Bluetooth: remove magic numbers in l2cap
> Bluetooth: prevent unneeded fragmentation in l2cap
>
> include/net/bluetooth/l2cap.h | 304 ++++++++++++++++++++++----
> net/bluetooth/l2cap_core.c | 481 +++++++++++++++++++++++++----------------
> net/bluetooth/l2cap_sock.c | 11 +-
> 3 files changed, 562 insertions(+), 234 deletions(-)
>
> --
> 1.7.4.1
Extended window size is next on my list to upstream. As you probably
noticed, the git://codeaurora.org/kernel/msm.git code has a slightly
different approach to handling the two control field types. The
fields are handled in a struct that's part of the bt_cb control block,
and only read/written in wire format when the frame is first received
or about to be sent. Are there any issues with this method (other
than the fact that I haven't upstreamed it yet)? Some of the
subsequent L2CAP state machine changes depend on this.
Please let me know if there are specific features from the
git://codeaurora.org/kernel/msm.git code base that you would like to
prioritize for upstreaming - there's a lot to do to bring in those
L2CAP and AMP changes, and I'll have more time to devote to it in
mid-September. I think we can gain a lot by reviewing each other's
patches and coordinating our efforts.
Regards,
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
From: Andrei Emeltchenko <[email protected]>
Adds support for extended sequence numbers found in
extended control fields.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 17 +++++++++++
net/bluetooth/l2cap_core.c | 63 +++++++++++++++++++---------------------
net/bluetooth/l2cap_sock.c | 1 +
3 files changed, 48 insertions(+), 33 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index e2906c8..97df6a0 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -384,6 +384,7 @@ struct l2cap_chan {
__u8 fcs;
__u16 tx_win;
+ __u16 tx_win_max;
__u8 max_tx;
__u16 retrans_timeout;
__u16 monitor_timeout;
@@ -548,6 +549,22 @@ enum {
L2CAP_DEFAULT_ACK_TO);
#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer)
+static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2)
+{
+ int offset;
+
+ offset = (seq1 - seq2) % (chan->tx_win_max + 1);
+ if (offset < 0)
+ offset += (chan->tx_win_max + 1);
+
+ return offset;
+}
+
+static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
+{
+ return (seq + 1) % (chan->tx_win_max + 1);
+}
+
static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
{
int sub;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 820a2b0..7e67f9b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1305,7 +1305,7 @@ static void l2cap_streaming_send(struct l2cap_chan *chan)
l2cap_do_send(chan, skb);
- chan->next_tx_seq = (chan->next_tx_seq + 1) % 64;
+ chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
}
}
@@ -1401,7 +1401,8 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
__set_retrans_timer(chan);
bt_cb(skb)->tx_seq = chan->next_tx_seq;
- chan->next_tx_seq = (chan->next_tx_seq + 1) % 64;
+
+ chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
if (bt_cb(skb)->retries == 1)
chan->unacked_frames++;
@@ -1954,13 +1955,16 @@ static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
__l2cap_ews_supported(chan)) {
/* use extended control field */
chan->ext_ctrl = 1;
+ chan->tx_win_max = L2CAP_DEFAULT_MAX_EXT_WINDOW;
+
if (chan->tx_win > L2CAP_DEFAULT_MAX_EXT_WINDOW)
chan->tx_win = L2CAP_DEFAULT_MAX_EXT_WINDOW;
} else {
+ chan->ext_ctrl = 0;
+ chan->tx_win_max = L2CAP_DEFAULT_MAX_TX_WINDOW;
+
if (chan->tx_win > L2CAP_DEFAULT_MAX_TX_WINDOW)
chan->tx_win = L2CAP_DEFAULT_MAX_TX_WINDOW;
-
- chan->ext_ctrl = 0;
}
}
@@ -2185,6 +2189,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
case L2CAP_CONF_EWS:
chan->ext_ctrl = 1;
chan->remote_tx_win = val;
+ chan->tx_win_max = L2CAP_DEFAULT_MAX_EXT_WINDOW;
set_bit(CONF_EWS_RECV, &chan->conf_state);
break;
@@ -3384,18 +3389,14 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
return 0;
}
- tx_seq_offset = (tx_seq - chan->buffer_seq) % 64;
- if (tx_seq_offset < 0)
- tx_seq_offset += 64;
+ tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
do {
if (bt_cb(next_skb)->tx_seq == tx_seq)
return -EINVAL;
- next_tx_seq_offset = (bt_cb(next_skb)->tx_seq -
- chan->buffer_seq) % 64;
- if (next_tx_seq_offset < 0)
- next_tx_seq_offset += 64;
+ next_tx_seq_offset = __seq_offset(chan,
+ bt_cb(next_skb)->tx_seq, chan->buffer_seq);
if (next_tx_seq_offset > tx_seq_offset) {
__skb_queue_before(&chan->srej_q, next_skb, skb);
@@ -3585,9 +3586,8 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq)
break;
}
- chan->buffer_seq_srej =
- (chan->buffer_seq_srej + 1) % 64;
- tx_seq = (tx_seq + 1) % 64;
+ chan->buffer_seq_srej = __next_seq(chan, chan->buffer_seq_srej);
+ tx_seq = __next_seq(chan, tx_seq);
}
}
@@ -3622,10 +3622,13 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
new->tx_seq = chan->expected_tx_seq;
- chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
+
+ chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
+
list_add_tail(&new->list, &chan->srej_l);
}
- chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
+
+ chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
}
static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb)
@@ -3651,9 +3654,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
chan->expected_ack_seq = req_seq;
l2cap_drop_acked_frames(chan);
- tx_seq_offset = (tx_seq - chan->buffer_seq) % 64;
- if (tx_seq_offset < 0)
- tx_seq_offset += 64;
+ tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
/* invalid tx_seq */
if (tx_seq_offset >= chan->tx_win) {
@@ -3701,10 +3702,8 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
l2cap_send_srejframe(chan, tx_seq);
}
} else {
- expected_tx_seq_offset =
- (chan->expected_tx_seq - chan->buffer_seq) % 64;
- if (expected_tx_seq_offset < 0)
- expected_tx_seq_offset += 64;
+ expected_tx_seq_offset = __seq_offset(chan,
+ chan->expected_tx_seq, chan->buffer_seq);
/* duplicated tx_seq */
if (tx_seq_offset < expected_tx_seq_offset)
@@ -3729,7 +3728,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
return 0;
expected:
- chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
+ 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;
@@ -3739,7 +3738,8 @@ expected:
}
err = l2cap_reassemble_sdu(chan, skb, rx_control);
- chan->buffer_seq = (chan->buffer_seq + 1) % 64;
+ chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
+
if (err < 0) {
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
return err;
@@ -3954,14 +3954,11 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
}
req_seq = __get_reqseq(chan->ext_ctrl, control);
- req_seq_offset = (req_seq - chan->expected_ack_seq) % 64;
- if (req_seq_offset < 0)
- req_seq_offset += 64;
- next_tx_seq_offset =
- (chan->next_tx_seq - chan->expected_ack_seq) % 64;
- if (next_tx_seq_offset < 0)
- next_tx_seq_offset += 64;
+ req_seq_offset = __seq_offset(chan, req_seq, chan->expected_ack_seq);
+
+ next_tx_seq_offset = __seq_offset(chan, chan->next_tx_seq,
+ chan->expected_ack_seq);
/* check for invalid req-seq */
if (req_seq_offset > next_tx_seq_offset) {
@@ -4067,7 +4064,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
/* TODO: Notify userland of missing data */
}
- chan->expected_tx_seq = (tx_seq + 1) % 64;
+ chan->expected_tx_seq = __next_seq(chan, tx_seq);
if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE)
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index d6a4f63..bf343aa 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -969,6 +969,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
/* Default config options */
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+ chan->tx_win_max = L2CAP_DEFAULT_MAX_TX_WINDOW;
chan->data = sk;
chan->ops = &l2cap_chan_ops;
--
1.7.4.1
From: Andrei Emeltchenko <[email protected]>
Remove magic numbers for FCS and SDU LEN
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/l2cap_core.c | 40 +++++++++++++++++++++++-----------------
1 files changed, 23 insertions(+), 17 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 7e67f9b..274b60c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -599,7 +599,7 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control)
hlen = L2CAP_ENHANCED_HDR_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16)
- hlen += 2;
+ hlen += L2CAP_FCS_SIZE;
BT_DBG("chan %p, control 0x%8.8x", chan, control);
@@ -624,8 +624,8 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control)
__put_control_put(chan->ext_ctrl, control, skb);
if (chan->fcs == L2CAP_FCS_CRC16) {
- u16 fcs = crc16(0, (u8 *)lh, count - 2);
- put_unaligned_le16(fcs, skb_put(skb, 2));
+ u16 fcs = crc16(0, (u8 *)lh, count - L2CAP_FCS_SIZE);
+ put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
}
if (lmp_no_flush_capable(conn->hcon->hdev))
@@ -1299,8 +1299,10 @@ static void l2cap_streaming_send(struct l2cap_chan *chan)
skb->data + L2CAP_HDR_SIZE);
if (chan->fcs == L2CAP_FCS_CRC16) {
- fcs = crc16(0, (u8 *)skb->data, skb->len - 2);
- put_unaligned_le16(fcs, skb->data + skb->len - 2);
+ fcs = crc16(0, (u8 *)skb->data,
+ skb->len - L2CAP_FCS_SIZE);
+ put_unaligned_le16(fcs,
+ skb->data + skb->len - L2CAP_FCS_SIZE);
}
l2cap_do_send(chan, skb);
@@ -1349,8 +1351,10 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
__put_control(chan->ext_ctrl, control, tx_skb->data + L2CAP_HDR_SIZE);
if (chan->fcs == L2CAP_FCS_CRC16) {
- fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2);
- put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2);
+ fcs = crc16(0, (u8 *)tx_skb->data,
+ tx_skb->len - L2CAP_FCS_SIZE);
+ put_unaligned_le16(fcs,
+ tx_skb->data + tx_skb->len - L2CAP_FCS_SIZE);
}
l2cap_do_send(chan, tx_skb);
@@ -1392,8 +1396,10 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
tx_skb->data + L2CAP_HDR_SIZE);
if (chan->fcs == L2CAP_FCS_CRC16) {
- fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2);
- put_unaligned_le16(fcs, skb->data + tx_skb->len - 2);
+ fcs = crc16(0, (u8 *)skb->data,
+ tx_skb->len - L2CAP_FCS_SIZE);
+ put_unaligned_le16(fcs, skb->data +
+ tx_skb->len - L2CAP_FCS_SIZE);
}
l2cap_do_send(chan, tx_skb);
@@ -1576,10 +1582,10 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct m
hlen = L2CAP_ENHANCED_HDR_SIZE;
if (sdulen)
- hlen += 2;
+ hlen += L2CAP_SDULEN_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16)
- hlen += 2;
+ hlen += L2CAP_FCS_SIZE;
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = bt_skb_send_alloc(sk, count + hlen,
@@ -1595,7 +1601,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct m
__put_control_put(chan->ext_ctrl, control, skb);
if (sdulen)
- put_unaligned_le16(sdulen, skb_put(skb, 2));
+ put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
if (unlikely(err < 0)) {
@@ -1604,7 +1610,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct m
}
if (chan->fcs == L2CAP_FCS_CRC16)
- put_unaligned_le16(0, skb_put(skb, 2));
+ put_unaligned_le16(0, skb_put(skb, L2CAP_FCS_SIZE));
bt_cb(skb)->retries = 0;
return skb;
@@ -3339,7 +3345,7 @@ static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb)
hdr_size = L2CAP_ENHANCED_HDR_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16) {
- skb_trim(skb, skb->len - 2);
+ skb_trim(skb, skb->len - L2CAP_FCS_SIZE);
rcv_fcs = get_unaligned_le16(skb->data + skb->len);
our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size);
@@ -3449,7 +3455,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u3
break;
chan->sdu_len = get_unaligned_le16(skb->data);
- skb_pull(skb, 2);
+ skb_pull(skb, L2CAP_SDULEN_SIZE);
if (chan->sdu_len > chan->imtu) {
err = -EMSGSIZE;
@@ -3946,7 +3952,7 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
len -= L2CAP_SDULEN_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16)
- len -= 2;
+ len -= L2CAP_FCS_SIZE;
if (len > chan->mps) {
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
@@ -4046,7 +4052,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
len -= L2CAP_SDULEN_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16)
- len -= 2;
+ len -= L2CAP_FCS_SIZE;
if (len > chan->mps || len < 0 || __is_sframe(chan->ext_ctrl,
control))
--
1.7.4.1
From: Andrei Emeltchenko <[email protected]>
Adds support for parsing extended control field. Extended control
field may be used for ERTM and streaming mode (if EWS specified).
u32 control is used for standard, enhanced and extended control
fields. Flag in l2cap chan specifies which format is in use.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 276 ++++++++++++++++++++++++++++++++------
net/bluetooth/l2cap_core.c | 299 +++++++++++++++++++++++------------------
2 files changed, 405 insertions(+), 170 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index b586a55..e2906c8 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -27,6 +27,8 @@
#ifndef __L2CAP_H
#define __L2CAP_H
+#include <asm/unaligned.h>
+
/* L2CAP defaults */
#define L2CAP_DEFAULT_MTU 672
#define L2CAP_DEFAULT_MIN_MTU 48
@@ -113,36 +115,52 @@ struct l2cap_conninfo {
#define L2CAP_FCS_CRC16 0x01
/* L2CAP Control Field bit masks */
-#define L2CAP_CTRL_SAR 0xC000
-#define L2CAP_CTRL_REQSEQ 0x3F00
-#define L2CAP_CTRL_TXSEQ 0x007E
-#define L2CAP_CTRL_RETRANS 0x0080
-#define L2CAP_CTRL_FINAL 0x0080
-#define L2CAP_CTRL_POLL 0x0010
-#define L2CAP_CTRL_SUPERVISE 0x000C
-#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */
-
-#define L2CAP_CTRL_TXSEQ_SHIFT 1
-#define L2CAP_CTRL_REQSEQ_SHIFT 8
-#define L2CAP_CTRL_SAR_SHIFT 14
+#define L2CAP_CTRL_SAR_MASK 0xC000
+#define L2CAP_CTRL_REQSEQ_MASK 0x3F00
+#define L2CAP_CTRL_TXSEQ_MASK 0x007E
+#define L2CAP_CTRL_SUPERVISE_MASK 0x000C
+
+#define L2CAP_CTRL_RETRANS 0x0080
+#define L2CAP_CTRL_FINAL 0x0080
+#define L2CAP_CTRL_POLL 0x0010
+#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */
+
+#define L2CAP_CTRL_TXSEQ_SHIFT 1
+#define L2CAP_CTRL_SUPER_SHIFT 2
+#define L2CAP_CTRL_REQSEQ_SHIFT 8
+#define L2CAP_CTRL_SAR_SHIFT 14
+
+/* L2CAP Extended Control Field bit mask */
+#define L2CAP_EXT_CTRL_TXSEQ_MASK 0xFFFC0000
+#define L2CAP_EXT_CTRL_SAR_MASK 0x00030000
+#define L2CAP_EXT_CTRL_SUPERVISE_MASK 0x00030000
+#define L2CAP_EXT_CTRL_REQSEQ_MASK 0x0000FFFC
+
+#define L2CAP_EXT_CTRL_POLL 0x00040000
+#define L2CAP_EXT_CTRL_FINAL 0x00000002
+#define L2CAP_EXT_CTRL_FRAME_TYPE 0x00000001 /* I- or S-Frame */
+
+#define L2CAP_EXT_CTRL_REQSEQ_SHIFT 2
+#define L2CAP_EXT_CTRL_SAR_SHIFT 16
+#define L2CAP_EXT_CTRL_SUPER_SHIFT 16
+#define L2CAP_EXT_CTRL_TXSEQ_SHIFT 18
/* L2CAP Supervisory Function */
-#define L2CAP_SUPER_RCV_READY 0x0000
-#define L2CAP_SUPER_REJECT 0x0004
-#define L2CAP_SUPER_RCV_NOT_READY 0x0008
-#define L2CAP_SUPER_SELECT_REJECT 0x000C
+#define L2CAP_SUPER_RR 0x00
+#define L2CAP_SUPER_REJ 0x01
+#define L2CAP_SUPER_RNR 0x02
+#define L2CAP_SUPER_SREJ 0x03
/* L2CAP Segmentation and Reassembly */
-#define L2CAP_SDU_UNSEGMENTED 0x0000
-#define L2CAP_SDU_START 0x4000
-#define L2CAP_SDU_END 0x8000
-#define L2CAP_SDU_CONTINUE 0xC000
+#define L2CAP_SAR_UNSEGMENTED 0x00
+#define L2CAP_SAR_START 0x01
+#define L2CAP_SAR_END 0x02
+#define L2CAP_SAR_CONTINUE 0x03
/* L2CAP Command rej. reasons */
-#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000
-#define L2CAP_REJ_MTU_EXCEEDED 0x0001
-#define L2CAP_REJ_INVALID_CID 0x0002
-
+#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000
+#define L2CAP_REJ_MTU_EXCEEDED 0x0001
+#define L2CAP_REJ_INVALID_CID 0x0002
/* L2CAP structures */
struct l2cap_hdr {
@@ -150,6 +168,11 @@ struct l2cap_hdr {
__le16 cid;
} __packed;
#define L2CAP_HDR_SIZE 4
+#define L2CAP_ENHANCED_HDR_SIZE 6
+#define L2CAP_EXTENDED_HDR_SIZE 8
+
+#define L2CAP_FCS_SIZE 2
+#define L2CAP_SDULEN_SIZE 2
struct l2cap_cmd_hdr {
__u8 code;
@@ -320,7 +343,7 @@ struct l2cap_conn_param_update_rsp {
/* ----- L2CAP channels and connections ----- */
struct srej_list {
- __u8 tx_seq;
+ __u16 tx_seq;
struct list_head list;
};
@@ -369,16 +392,16 @@ struct l2cap_chan {
unsigned long conf_state;
unsigned long conn_state;
- __u8 next_tx_seq;
- __u8 expected_ack_seq;
- __u8 expected_tx_seq;
- __u8 buffer_seq;
- __u8 buffer_seq_srej;
- __u8 srej_save_reqseq;
- __u8 frames_sent;
- __u8 unacked_frames;
+ __u16 next_tx_seq;
+ __u16 expected_ack_seq;
+ __u16 expected_tx_seq;
+ __u16 buffer_seq;
+ __u16 buffer_seq_srej;
+ __u16 srej_save_reqseq;
+ __u16 frames_sent;
+ __u16 unacked_frames;
__u8 retry_count;
- __u8 num_acked;
+ __u16 num_acked;
__u16 sdu_len;
struct sk_buff *sdu;
struct sk_buff *sdu_last_frag;
@@ -537,11 +560,186 @@ static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
return sub == ch->remote_tx_win;
}
-#define __get_txseq(ctrl) (((ctrl) & L2CAP_CTRL_TXSEQ) >> 1)
-#define __get_reqseq(ctrl) (((ctrl) & L2CAP_CTRL_REQSEQ) >> 8)
-#define __is_iframe(ctrl) (!((ctrl) & L2CAP_CTRL_FRAME_TYPE))
-#define __is_sframe(ctrl) ((ctrl) & L2CAP_CTRL_FRAME_TYPE)
-#define __is_sar_start(ctrl) (((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START)
+static inline __u16 __get_reqseq(__u8 ext_ctrl, __u32 ctrl)
+{
+ if (ext_ctrl)
+ return (ctrl & L2CAP_EXT_CTRL_REQSEQ_MASK) >>
+ L2CAP_EXT_CTRL_REQSEQ_SHIFT;
+ else
+ return (ctrl & L2CAP_CTRL_REQSEQ_MASK) >>
+ L2CAP_CTRL_REQSEQ_SHIFT;
+}
+
+static inline __u32 __set_reqseq(__u8 ext_ctrl, __u32 reqseq)
+{
+ if (ext_ctrl)
+ return (reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT) &
+ L2CAP_EXT_CTRL_REQSEQ_MASK;
+ else
+ return (reqseq << L2CAP_CTRL_REQSEQ_SHIFT) &
+ L2CAP_CTRL_REQSEQ_MASK;
+}
+
+static inline __u16 __get_txseq(__u8 ext_ctrl, __u32 ctrl)
+{
+ if (ext_ctrl)
+ return (ctrl & L2CAP_EXT_CTRL_TXSEQ_MASK) >>
+ L2CAP_EXT_CTRL_TXSEQ_SHIFT;
+ else
+ return (ctrl & L2CAP_CTRL_TXSEQ_MASK) >>
+ L2CAP_CTRL_TXSEQ_SHIFT;
+}
+
+static inline __u32 __set_txseq(__u8 ext_ctrl, __u32 txseq)
+{
+ if (ext_ctrl)
+ return (txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT) &
+ L2CAP_EXT_CTRL_TXSEQ_MASK;
+ else
+ return (txseq << L2CAP_CTRL_TXSEQ_SHIFT) &
+ L2CAP_CTRL_TXSEQ_MASK;
+}
+
+static inline bool __is_sframe(__u8 ext_ctrl, __u32 ctrl)
+{
+ if (ext_ctrl)
+ return ctrl & L2CAP_EXT_CTRL_FRAME_TYPE;
+ else
+ return ctrl & L2CAP_CTRL_FRAME_TYPE;
+}
+
+static inline __u32 __set_sframe(__u8 ext_ctrl)
+{
+ if (ext_ctrl)
+ return L2CAP_EXT_CTRL_FRAME_TYPE;
+ else
+ return L2CAP_CTRL_FRAME_TYPE;
+}
+
+static inline __u8 __get_ctrl_sar(__u8 ext_ctrl, __u32 ctrl)
+{
+ if (ext_ctrl)
+ return (ctrl & L2CAP_EXT_CTRL_SAR_MASK) >>
+ L2CAP_EXT_CTRL_SAR_SHIFT;
+ else
+ return (ctrl & L2CAP_CTRL_SAR_MASK) >>
+ L2CAP_CTRL_SAR_SHIFT;
+}
+
+static inline __u32 __set_ctrl_sar(__u8 ext_ctrl, __u32 sar)
+{
+ if (ext_ctrl)
+ return (sar << L2CAP_EXT_CTRL_SAR_SHIFT) &
+ L2CAP_EXT_CTRL_SAR_MASK;
+ else
+ return (sar << L2CAP_CTRL_SAR_SHIFT) &
+ L2CAP_CTRL_SAR_MASK;
+}
+
+static inline bool __is_sar_start(__u8 ext_ctrl, __u32 ctrl)
+{
+ return __get_ctrl_sar(ext_ctrl, ctrl) == L2CAP_SAR_START;
+}
+
+static inline __u32 __get_sar_mask(__u8 ext_ctrl)
+{
+ if (ext_ctrl)
+ return L2CAP_EXT_CTRL_SAR_MASK;
+ else
+ return L2CAP_CTRL_SAR_MASK;
+}
+
+static inline __u8 __get_ctrl_super(__u8 ext_ctrl, __u32 ctrl)
+{
+ if (ext_ctrl)
+ return (ctrl & L2CAP_EXT_CTRL_SUPERVISE_MASK) >>
+ L2CAP_EXT_CTRL_SUPER_SHIFT;
+ else
+ return (ctrl & L2CAP_CTRL_SUPERVISE_MASK) >>
+ L2CAP_CTRL_SUPER_SHIFT;
+}
+
+static inline __u32 __set_ctrl_super(__u8 ext_ctrl, __u32 super)
+{
+ if (ext_ctrl)
+ return (super << L2CAP_EXT_CTRL_SUPER_SHIFT) &
+ L2CAP_EXT_CTRL_SUPERVISE_MASK;
+ else
+ return (super << L2CAP_CTRL_SUPER_SHIFT) &
+ L2CAP_CTRL_SUPERVISE_MASK;
+}
+
+static inline __u32 __set_ctrl_final(__u8 ext_ctrl)
+{
+ if (ext_ctrl)
+ return L2CAP_EXT_CTRL_FINAL;
+ else
+ return L2CAP_CTRL_FINAL;
+}
+
+static inline bool __is_ctrl_final(__u8 ext_ctrl, __u32 ctrl)
+{
+ if (ext_ctrl)
+ return ctrl & L2CAP_EXT_CTRL_FINAL;
+ else
+ return ctrl & L2CAP_CTRL_FINAL;
+}
+
+static inline __u32 __set_ctrl_poll(__u8 ext_ctrl)
+{
+ if (ext_ctrl)
+ return L2CAP_EXT_CTRL_POLL;
+ else
+ return L2CAP_CTRL_POLL;
+}
+
+static inline bool __is_ctrl_poll(__u8 ext_ctrl, __u32 ctrl)
+{
+ if (ext_ctrl)
+ return ctrl & L2CAP_EXT_CTRL_POLL;
+ else
+ return ctrl & L2CAP_CTRL_POLL;
+}
+
+static inline __u32 __get_control(__u8 ext_ctrl, void *p)
+{
+ if (ext_ctrl)
+ return get_unaligned_le32(p);
+ else
+ return get_unaligned_le16(p);
+}
+
+static inline __u32 __get_control_pull(__u8 ext_ctrl,
+ struct sk_buff *skb, void *p)
+{
+ __u32 ctrl;
+
+ if (ext_ctrl) {
+ ctrl = get_unaligned_le32(p);
+ skb_pull(skb, 4);
+ } else {
+ ctrl = get_unaligned_le16(p);
+ skb_pull(skb, 2);
+ }
+
+ return ctrl;
+}
+
+static inline void __put_control(__u8 ext_ctrl, __u32 control, void *p)
+{
+ if (ext_ctrl)
+ return put_unaligned_le32(control, p);
+ else
+ return put_unaligned_le16(control, p);
+}
+
+static inline void __put_control_put(__u8 ext_ctrl, __u32 control, void *p)
+{
+ if (ext_ctrl)
+ return put_unaligned_le32(control, skb_put(p, 4));
+ else
+ return put_unaligned_le16(control, skb_put(p, 2));
+}
extern int disable_ertm;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 0d62b05..820a2b0 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -582,30 +582,36 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
hci_send_acl(conn->hcon, skb, flags);
}
-static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
+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 = L2CAP_HDR_SIZE + 2;
+ int count, hlen;
u8 flags;
if (chan->state != BT_CONNECTED)
return;
+ if (chan->ext_ctrl)
+ hlen = L2CAP_EXTENDED_HDR_SIZE;
+ else
+ hlen = L2CAP_ENHANCED_HDR_SIZE;
+
if (chan->fcs == L2CAP_FCS_CRC16)
hlen += 2;
- BT_DBG("chan %p, control 0x%2.2x", chan, control);
+ BT_DBG("chan %p, control 0x%8.8x", chan, control);
count = min_t(unsigned int, conn->mtu, hlen);
- control |= L2CAP_CTRL_FRAME_TYPE;
+
+ control |= __set_sframe(chan->ext_ctrl);
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
- control |= L2CAP_CTRL_FINAL;
+ control |= __set_ctrl_final(chan->ext_ctrl);
if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state))
- control |= L2CAP_CTRL_POLL;
+ control |= __set_ctrl_poll(chan->ext_ctrl);
skb = bt_skb_alloc(count, GFP_ATOMIC);
if (!skb)
@@ -614,7 +620,8 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
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_unaligned_le16(control, skb_put(skb, 2));
+
+ __put_control_put(chan->ext_ctrl, control, skb);
if (chan->fcs == L2CAP_FCS_CRC16) {
u16 fcs = crc16(0, (u8 *)lh, count - 2);
@@ -631,15 +638,15 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
hci_send_acl(chan->conn->hcon, skb, flags);
}
-static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control)
+static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control)
{
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
- control |= L2CAP_SUPER_RCV_NOT_READY;
+ control |= __set_ctrl_super(chan->ext_ctrl, L2CAP_SUPER_RNR);
set_bit(CONN_RNR_SENT, &chan->conn_state);
} else
- control |= L2CAP_SUPER_RCV_READY;
+ control |= __set_ctrl_super(chan->ext_ctrl, L2CAP_SUPER_RR);
- control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control |= __set_reqseq(chan->ext_ctrl, chan->buffer_seq);
l2cap_send_sframe(chan, control);
}
@@ -1281,12 +1288,15 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
static void l2cap_streaming_send(struct l2cap_chan *chan)
{
struct sk_buff *skb;
- u16 control, fcs;
+ u32 control;
+ u16 fcs;
while ((skb = skb_dequeue(&chan->tx_q))) {
- control = get_unaligned_le16(skb->data + L2CAP_HDR_SIZE);
- control |= chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT;
- put_unaligned_le16(control, skb->data + L2CAP_HDR_SIZE);
+ control = __get_control(chan->ext_ctrl,
+ skb->data + L2CAP_HDR_SIZE);
+ control |= __set_txseq(chan->ext_ctrl, chan->next_tx_seq);
+ __put_control(chan->ext_ctrl, control,
+ skb->data + L2CAP_HDR_SIZE);
if (chan->fcs == L2CAP_FCS_CRC16) {
fcs = crc16(0, (u8 *)skb->data, skb->len - 2);
@@ -1299,10 +1309,11 @@ static void l2cap_streaming_send(struct l2cap_chan *chan)
}
}
-static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
+static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
{
struct sk_buff *skb, *tx_skb;
- u16 control, fcs;
+ u16 fcs;
+ u32 control;
skb = skb_peek(&chan->tx_q);
if (!skb)
@@ -1325,16 +1336,17 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
tx_skb = skb_clone(skb, GFP_ATOMIC);
bt_cb(skb)->retries++;
- control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
- control &= L2CAP_CTRL_SAR;
+
+ control = __get_control(chan->ext_ctrl, tx_skb->data + L2CAP_HDR_SIZE);
+ control &= __get_sar_mask(chan->ext_ctrl);
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
- control |= L2CAP_CTRL_FINAL;
+ control |= __set_ctrl_final(chan->ext_ctrl);
- control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
- | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
+ control |= __set_reqseq(chan->ext_ctrl, chan->buffer_seq);
+ control |= __set_txseq(chan->ext_ctrl, tx_seq);
- put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
+ __put_control(chan->ext_ctrl, control, tx_skb->data + L2CAP_HDR_SIZE);
if (chan->fcs == L2CAP_FCS_CRC16) {
fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2);
@@ -1347,7 +1359,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
static int l2cap_ertm_send(struct l2cap_chan *chan)
{
struct sk_buff *skb, *tx_skb;
- u16 control, fcs;
+ u16 fcs;
+ u32 control;
int nsent = 0;
if (chan->state != BT_CONNECTED)
@@ -1365,16 +1378,18 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
bt_cb(skb)->retries++;
- control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
- control &= L2CAP_CTRL_SAR;
+ control = __get_control(chan->ext_ctrl,
+ tx_skb->data + L2CAP_HDR_SIZE);
+ control &= __get_sar_mask(chan->ext_ctrl);
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
- control |= L2CAP_CTRL_FINAL;
+ control |= __set_ctrl_final(chan->ext_ctrl);
- control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
- | (chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
- put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
+ control |= __set_reqseq(chan->ext_ctrl, chan->buffer_seq);
+ control |= __set_txseq(chan->ext_ctrl, chan->next_tx_seq);
+ __put_control(chan->ext_ctrl, control,
+ tx_skb->data + L2CAP_HDR_SIZE);
if (chan->fcs == L2CAP_FCS_CRC16) {
fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2);
@@ -1418,12 +1433,12 @@ static int l2cap_retransmit_frames(struct l2cap_chan *chan)
static void l2cap_send_ack(struct l2cap_chan *chan)
{
- u16 control = 0;
+ u32 control = 0;
- control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control |= __set_reqseq(chan->ext_ctrl, chan->buffer_seq);
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
- control |= L2CAP_SUPER_RCV_NOT_READY;
+ control |= __set_ctrl_super(chan->ext_ctrl, L2CAP_SUPER_RNR);
set_bit(CONN_RNR_SENT, &chan->conn_state);
l2cap_send_sframe(chan, control);
return;
@@ -1432,20 +1447,20 @@ static void l2cap_send_ack(struct l2cap_chan *chan)
if (l2cap_ertm_send(chan) > 0)
return;
- control |= L2CAP_SUPER_RCV_READY;
+ control |= __set_ctrl_super(chan->ext_ctrl, L2CAP_SUPER_RR);
l2cap_send_sframe(chan, control);
}
static void l2cap_send_srejtail(struct l2cap_chan *chan)
{
struct srej_list *tail;
- u16 control;
+ u32 control;
- control = L2CAP_SUPER_SELECT_REJECT;
- control |= L2CAP_CTRL_FINAL;
+ control = __set_ctrl_super(chan->ext_ctrl, L2CAP_SUPER_SREJ);
+ control |= __set_ctrl_final(chan->ext_ctrl);
tail = list_entry((&chan->srej_l)->prev, struct srej_list, list);
- control |= tail->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control |= __set_reqseq(chan->ext_ctrl, tail->tx_seq);
l2cap_send_sframe(chan, control);
}
@@ -1541,12 +1556,12 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct ms
return skb;
}
-static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u16 control, u16 sdulen)
+static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u32 control, u16 sdulen)
{
struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
- int err, count, hlen = L2CAP_HDR_SIZE + 2;
+ int err, count, hlen;
struct l2cap_hdr *lh;
BT_DBG("sk %p len %d", sk, (int)len);
@@ -1554,6 +1569,11 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct m
if (!conn)
return ERR_PTR(-ENOTCONN);
+ if (chan->ext_ctrl)
+ hlen = L2CAP_EXTENDED_HDR_SIZE;
+ else
+ hlen = L2CAP_ENHANCED_HDR_SIZE;
+
if (sdulen)
hlen += 2;
@@ -1570,7 +1590,9 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct m
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_le16(control, skb_put(skb, 2));
+
+ __put_control_put(chan->ext_ctrl, control, skb);
+
if (sdulen)
put_unaligned_le16(sdulen, skb_put(skb, 2));
@@ -1591,11 +1613,11 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si
{
struct sk_buff *skb;
struct sk_buff_head sar_queue;
- u16 control;
+ u32 control;
size_t size = 0;
skb_queue_head_init(&sar_queue);
- control = L2CAP_SDU_START;
+ control = __set_ctrl_sar(chan->ext_ctrl, L2CAP_SAR_START);
skb = l2cap_create_iframe_pdu(chan, msg, chan->remote_mps, control, len);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -1608,10 +1630,12 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si
size_t buflen;
if (len > chan->remote_mps) {
- control = L2CAP_SDU_CONTINUE;
+ control = __set_ctrl_sar(chan->ext_ctrl,
+ L2CAP_SAR_CONTINUE);
buflen = chan->remote_mps;
} else {
- control = L2CAP_SDU_END;
+ control = __set_ctrl_sar(chan->ext_ctrl,
+ L2CAP_SAR_END);
buflen = len;
}
@@ -1635,7 +1659,7 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
{
struct sk_buff *skb;
- u16 control;
+ u32 control;
int err;
/* Connectionless channel */
@@ -1667,7 +1691,8 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
case L2CAP_MODE_STREAMING:
/* Entire SDU fits into one PDU */
if (len <= chan->remote_mps) {
- control = L2CAP_SDU_UNSEGMENTED;
+ control = __set_ctrl_sar(chan->ext_ctrl,
+ L2CAP_SAR_UNSEGMENTED);
skb = l2cap_create_iframe_pdu(chan, msg, len, control,
0);
if (IS_ERR(skb))
@@ -2007,8 +2032,11 @@ done:
rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
l2cap_txwin_setup(chan);
- rfc.txwin_size = chan->tx_win > L2CAP_DEFAULT_MAX_TX_WINDOW?
- L2CAP_DEFAULT_MAX_TX_WINDOW : chan->tx_win;
+
+ if (chan->ext_ctrl)
+ rfc.txwin_size = L2CAP_DEFAULT_MAX_TX_WINDOW;
+ else
+ rfc.txwin_size = chan->tx_win;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
(unsigned long) &rfc);
@@ -3298,7 +3326,12 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb)
{
u16 our_fcs, rcv_fcs;
- int hdr_size = L2CAP_HDR_SIZE + 2;
+ int hdr_size;
+
+ if (chan->ext_ctrl)
+ hdr_size = L2CAP_EXTENDED_HDR_SIZE;
+ else
+ hdr_size = L2CAP_ENHANCED_HDR_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16) {
skb_trim(skb, skb->len - 2);
@@ -3313,14 +3346,14 @@ static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb)
static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
{
- u16 control = 0;
+ u32 control = 0;
chan->frames_sent = 0;
- control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control |= __set_reqseq(chan->ext_ctrl, chan->buffer_seq);
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
- control |= L2CAP_SUPER_RCV_NOT_READY;
+ control |= __set_ctrl_super(chan->ext_ctrl, L2CAP_SUPER_RNR);
l2cap_send_sframe(chan, control);
set_bit(CONN_RNR_SENT, &chan->conn_state);
}
@@ -3332,12 +3365,12 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
chan->frames_sent == 0) {
- control |= L2CAP_SUPER_RCV_READY;
+ control |= __set_ctrl_super(chan->ext_ctrl, L2CAP_SUPER_RR);
l2cap_send_sframe(chan, control);
}
}
-static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u8 tx_seq, u8 sar)
+static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u16 tx_seq, u8 sar)
{
struct sk_buff *next_skb;
int tx_seq_offset, next_tx_seq_offset;
@@ -3398,19 +3431,19 @@ static void append_skb_frag(struct sk_buff *skb,
skb->truesize += new_frag->truesize;
}
-static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
+static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u32 control)
{
int err = -EINVAL;
- switch (control & L2CAP_CTRL_SAR) {
- case L2CAP_SDU_UNSEGMENTED:
+ switch (__get_ctrl_sar(chan->ext_ctrl, control)) {
+ case L2CAP_SAR_UNSEGMENTED:
if (chan->sdu)
break;
err = chan->ops->recv(chan->data, skb);
break;
- case L2CAP_SDU_START:
+ case L2CAP_SAR_START:
if (chan->sdu)
break;
@@ -3432,7 +3465,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1
err = 0;
break;
- case L2CAP_SDU_CONTINUE:
+ case L2CAP_SAR_CONTINUE:
if (!chan->sdu)
break;
@@ -3446,7 +3479,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1
err = 0;
break;
- case L2CAP_SDU_END:
+ case L2CAP_SAR_END:
if (!chan->sdu)
break;
@@ -3481,14 +3514,14 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1
static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
{
- u16 control;
+ u32 control;
BT_DBG("chan %p, Enter local busy", chan);
set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
- control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- control |= L2CAP_SUPER_RCV_NOT_READY;
+ control = __set_reqseq(chan->ext_ctrl, chan->buffer_seq);
+ control |= __set_ctrl_super(chan->ext_ctrl, L2CAP_SUPER_RNR);
l2cap_send_sframe(chan, control);
set_bit(CONN_RNR_SENT, &chan->conn_state);
@@ -3498,13 +3531,14 @@ static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan)
{
- u16 control;
+ u32 control;
if (!test_bit(CONN_RNR_SENT, &chan->conn_state))
goto done;
- control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- control |= L2CAP_SUPER_RCV_READY | L2CAP_CTRL_POLL;
+ control = __set_reqseq(chan->ext_ctrl, chan->buffer_seq);
+ control |= __set_ctrl_super(chan->ext_ctrl, L2CAP_SUPER_RR);
+ control |= __set_ctrl_poll(chan->ext_ctrl);
l2cap_send_sframe(chan, control);
chan->retry_count = 1;
@@ -3530,10 +3564,10 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
}
}
-static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
+static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq)
{
struct sk_buff *skb;
- u16 control;
+ u32 control;
while ((skb = skb_peek(&chan->srej_q)) &&
!test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
@@ -3543,7 +3577,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
break;
skb = skb_dequeue(&chan->srej_q);
- control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
+ control = __set_ctrl_sar(chan->ext_ctrl, bt_cb(skb)->sar);
err = l2cap_reassemble_sdu(chan, skb, control);
if (err < 0) {
@@ -3557,10 +3591,10 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
}
}
-static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq)
+static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq)
{
struct srej_list *l, *tmp;
- u16 control;
+ u32 control;
list_for_each_entry_safe(l, tmp, &chan->srej_l, list) {
if (l->tx_seq == tx_seq) {
@@ -3568,22 +3602,22 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq)
kfree(l);
return;
}
- control = L2CAP_SUPER_SELECT_REJECT;
- control |= l->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control = __set_ctrl_super(chan->ext_ctrl, L2CAP_SUPER_SREJ);
+ control |= __set_reqseq(chan->ext_ctrl, l->tx_seq);
l2cap_send_sframe(chan, control);
list_del(&l->list);
list_add_tail(&l->list, &chan->srej_l);
}
}
-static void l2cap_send_srejframe(struct l2cap_chan *chan, u8 tx_seq)
+static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
{
struct srej_list *new;
- u16 control;
+ u32 control;
while (tx_seq != chan->expected_tx_seq) {
- control = L2CAP_SUPER_SELECT_REJECT;
- control |= chan->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control = __set_ctrl_super(chan->ext_ctrl, L2CAP_SUPER_SREJ);
+ control |= __set_reqseq(chan->ext_ctrl, chan->expected_tx_seq);
l2cap_send_sframe(chan, control);
new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
@@ -3594,19 +3628,19 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u8 tx_seq)
chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
}
-static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb)
+static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb)
{
- u8 tx_seq = __get_txseq(rx_control);
- u8 req_seq = __get_reqseq(rx_control);
- u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT;
+ u16 tx_seq = __get_txseq(chan->ext_ctrl, rx_control);
+ u16 req_seq = __get_reqseq(chan->ext_ctrl, rx_control);
+ u8 sar = __get_ctrl_sar(chan->ext_ctrl, rx_control);
int tx_seq_offset, expected_tx_seq_offset;
int num_to_ack = (chan->tx_win/6) + 1;
int err = 0;
- BT_DBG("chan %p len %d tx_seq %d rx_control 0x%4.4x", chan, skb->len,
+ BT_DBG("chan %p len %d tx_seq %d rx_control 0x%8.8x", chan, skb->len,
tx_seq, rx_control);
- if (L2CAP_CTRL_FINAL & rx_control &&
+ if (__is_ctrl_final(chan->ext_ctrl, rx_control) &&
test_bit(CONN_WAIT_F, &chan->conn_state)) {
__clear_monitor_timer(chan);
if (chan->unacked_frames > 0)
@@ -3711,7 +3745,7 @@ expected:
return err;
}
- if (rx_control & L2CAP_CTRL_FINAL) {
+ if (__is_ctrl_final(chan->ext_ctrl, rx_control)) {
if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
l2cap_retransmit_frames(chan);
}
@@ -3729,15 +3763,16 @@ drop:
return 0;
}
-static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_control)
+static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u32 rx_control)
{
- BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, __get_reqseq(rx_control),
- rx_control);
+ BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x",
+ chan, __get_reqseq(chan->ext_ctrl, rx_control),
+ rx_control);
- chan->expected_ack_seq = __get_reqseq(rx_control);
+ chan->expected_ack_seq = __get_reqseq(chan->ext_ctrl, rx_control);
l2cap_drop_acked_frames(chan);
- if (rx_control & L2CAP_CTRL_POLL) {
+ if (__is_ctrl_poll(chan->ext_ctrl, rx_control)) {
set_bit(CONN_SEND_FBIT, &chan->conn_state);
if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
@@ -3750,7 +3785,7 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co
l2cap_send_i_or_rr_or_rnr(chan);
}
- } else if (rx_control & L2CAP_CTRL_FINAL) {
+ } else if (__is_ctrl_final(chan->ext_ctrl, rx_control)) {
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
@@ -3769,9 +3804,9 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co
}
}
-static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_control)
+static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u32 rx_control)
{
- u8 tx_seq = __get_reqseq(rx_control);
+ u16 tx_seq = __get_reqseq(chan->ext_ctrl, rx_control);
BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
@@ -3780,7 +3815,7 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c
chan->expected_ack_seq = tx_seq;
l2cap_drop_acked_frames(chan);
- if (rx_control & L2CAP_CTRL_FINAL) {
+ if (__is_ctrl_final(chan->ext_ctrl, rx_control)) {
if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
l2cap_retransmit_frames(chan);
} else {
@@ -3790,15 +3825,15 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c
set_bit(CONN_REJ_ACT, &chan->conn_state);
}
}
-static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control)
+static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u32 rx_control)
{
- u8 tx_seq = __get_reqseq(rx_control);
+ u16 tx_seq = __get_reqseq(chan->ext_ctrl, rx_control);
- BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
+ BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control);
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
- if (rx_control & L2CAP_CTRL_POLL) {
+ if (__is_ctrl_poll(chan->ext_ctrl, rx_control)) {
chan->expected_ack_seq = tx_seq;
l2cap_drop_acked_frames(chan);
@@ -3811,7 +3846,7 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_
chan->srej_save_reqseq = tx_seq;
set_bit(CONN_SREJ_ACT, &chan->conn_state);
}
- } else if (rx_control & L2CAP_CTRL_FINAL) {
+ } else if (__is_ctrl_final(chan->ext_ctrl, rx_control)) {
if (test_bit(CONN_SREJ_ACT, &chan->conn_state) &&
chan->srej_save_reqseq == tx_seq)
clear_bit(CONN_SREJ_ACT, &chan->conn_state);
@@ -3826,37 +3861,39 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_
}
}
-static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_control)
+static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u32 rx_control)
{
- u8 tx_seq = __get_reqseq(rx_control);
+ u16 tx_seq = __get_reqseq(chan->ext_ctrl, rx_control);
- BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
+ BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control);
set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
chan->expected_ack_seq = tx_seq;
l2cap_drop_acked_frames(chan);
- if (rx_control & L2CAP_CTRL_POLL)
+ if (__is_ctrl_poll(chan->ext_ctrl, rx_control))
set_bit(CONN_SEND_FBIT, &chan->conn_state);
if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
__clear_retrans_timer(chan);
- if (rx_control & L2CAP_CTRL_POLL)
+ if (__is_ctrl_poll(chan->ext_ctrl, rx_control))
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL);
return;
}
- if (rx_control & L2CAP_CTRL_POLL)
+ if (__is_ctrl_poll(chan->ext_ctrl, rx_control))
l2cap_send_srejtail(chan);
- else
- l2cap_send_sframe(chan, L2CAP_SUPER_RCV_READY);
+ else {
+ rx_control = __set_ctrl_super(chan->ext_ctrl, L2CAP_SUPER_RR);
+ l2cap_send_sframe(chan, rx_control);
+ }
}
-static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb)
+static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb)
{
- BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len);
+ BT_DBG("chan %p rx_control 0x%8.8x len %d", chan, rx_control, skb->len);
- if (L2CAP_CTRL_FINAL & rx_control &&
+ if (__is_ctrl_final(chan->ext_ctrl, rx_control) &&
test_bit(CONN_WAIT_F, &chan->conn_state)) {
__clear_monitor_timer(chan);
if (chan->unacked_frames > 0)
@@ -3864,20 +3901,20 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont
clear_bit(CONN_WAIT_F, &chan->conn_state);
}
- switch (rx_control & L2CAP_CTRL_SUPERVISE) {
- case L2CAP_SUPER_RCV_READY:
+ switch (__get_ctrl_super(chan->ext_ctrl, rx_control)) {
+ case L2CAP_SUPER_RR:
l2cap_data_channel_rrframe(chan, rx_control);
break;
- case L2CAP_SUPER_REJECT:
+ case L2CAP_SUPER_REJ:
l2cap_data_channel_rejframe(chan, rx_control);
break;
- case L2CAP_SUPER_SELECT_REJECT:
+ case L2CAP_SUPER_SREJ:
l2cap_data_channel_srejframe(chan, rx_control);
break;
- case L2CAP_SUPER_RCV_NOT_READY:
+ case L2CAP_SUPER_RNR:
l2cap_data_channel_rnrframe(chan, rx_control);
break;
}
@@ -3889,12 +3926,11 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont
static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
{
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
- u16 control;
- u8 req_seq;
+ u32 control;
+ u16 req_seq;
int len, next_tx_seq_offset, req_seq_offset;
- control = get_unaligned_le16(skb->data);
- skb_pull(skb, 2);
+ control = __get_control_pull(chan->ext_ctrl, skb, skb->data);
len = skb->len;
/*
@@ -3905,8 +3941,9 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
if (l2cap_check_fcs(chan, skb))
goto drop;
- if (__is_sar_start(control) && __is_iframe(control))
- len -= 2;
+ if (__is_sar_start(chan->ext_ctrl, control) &&
+ !__is_sframe(chan->ext_ctrl, control))
+ len -= L2CAP_SDULEN_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16)
len -= 2;
@@ -3916,7 +3953,7 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
goto drop;
}
- req_seq = __get_reqseq(control);
+ req_seq = __get_reqseq(chan->ext_ctrl, control);
req_seq_offset = (req_seq - chan->expected_ack_seq) % 64;
if (req_seq_offset < 0)
req_seq_offset += 64;
@@ -3932,7 +3969,7 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
goto drop;
}
- if (__is_iframe(control)) {
+ if (!__is_sframe(chan->ext_ctrl, control)) {
if (len < 0) {
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
goto drop;
@@ -3960,8 +3997,8 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
{
struct l2cap_chan *chan;
struct sock *sk = NULL;
- u16 control;
- u8 tx_seq;
+ u32 control;
+ u16 tx_seq;
int len;
chan = l2cap_get_chan_by_scid(conn, cid);
@@ -4002,23 +4039,23 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
goto done;
case L2CAP_MODE_STREAMING:
- control = get_unaligned_le16(skb->data);
- skb_pull(skb, 2);
+ control = __get_control_pull(chan->ext_ctrl, skb, skb->data);
len = skb->len;
if (l2cap_check_fcs(chan, skb))
goto drop;
- if (__is_sar_start(control))
- len -= 2;
+ if (__is_sar_start(chan->ext_ctrl, control))
+ len -= L2CAP_SDULEN_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16)
len -= 2;
- if (len > chan->mps || len < 0 || __is_sframe(control))
+ if (len > chan->mps || len < 0 || __is_sframe(chan->ext_ctrl,
+ control))
goto drop;
- tx_seq = __get_txseq(control);
+ tx_seq = __get_txseq(chan->ext_ctrl, control);
if (chan->expected_tx_seq != tx_seq) {
/* Frame(s) missing - must discard partial SDU */
--
1.7.4.1
From: Andrei Emeltchenko <[email protected]>
Calculate pdu length before creating I-frame. Otherwise if
conn->mtu - (headers) < remote_mps we get fragmented packets in
create_iframe_pdu function.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/l2cap_core.c | 25 +++++++++++++++++--------
1 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 274b60c..4bb70d2 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1571,8 +1571,6 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct m
int err, count, hlen;
struct l2cap_hdr *lh;
- BT_DBG("sk %p len %d", sk, (int)len);
-
if (!conn)
return ERR_PTR(-ENOTCONN);
@@ -1587,6 +1585,9 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct m
if (chan->fcs == L2CAP_FCS_CRC16)
hlen += L2CAP_FCS_SIZE;
+ BT_DBG("sk %p, msg %p, len %d, sdulen %d, hlen %d",
+ sk, msg, (int)len, (int)sdulen, hlen);
+
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = bt_skb_send_alloc(sk, count + hlen,
msg->msg_flags & MSG_DONTWAIT, &err);
@@ -1622,24 +1623,30 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si
struct sk_buff_head sar_queue;
u32 control;
size_t size = 0;
+ size_t pdu_len;
skb_queue_head_init(&sar_queue);
control = __set_ctrl_sar(chan->ext_ctrl, L2CAP_SAR_START);
- skb = l2cap_create_iframe_pdu(chan, msg, chan->remote_mps, control, len);
+
+ pdu_len = chan->conn->mtu;
+ pdu_len -= L2CAP_EXTENDED_HDR_SIZE + L2CAP_FCS_SIZE + L2CAP_SDULEN_SIZE;
+ pdu_len = min_t(size_t, pdu_len, chan->remote_mps);
+
+ skb = l2cap_create_iframe_pdu(chan, msg, pdu_len, control, len);
if (IS_ERR(skb))
return PTR_ERR(skb);
__skb_queue_tail(&sar_queue, skb);
- len -= chan->remote_mps;
- size += chan->remote_mps;
+ len -= pdu_len;
+ size += pdu_len;
while (len > 0) {
size_t buflen;
- if (len > chan->remote_mps) {
+ if (len > pdu_len) {
control = __set_ctrl_sar(chan->ext_ctrl,
L2CAP_SAR_CONTINUE);
- buflen = chan->remote_mps;
+ buflen = pdu_len;
} else {
control = __set_ctrl_sar(chan->ext_ctrl,
L2CAP_SAR_END);
@@ -3349,8 +3356,10 @@ static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb)
rcv_fcs = get_unaligned_le16(skb->data + skb->len);
our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size);
- if (our_fcs != rcv_fcs)
+ if (our_fcs != rcv_fcs) {
+ BT_DBG("FCS check failed");
return -EBADMSG;
+ }
}
return 0;
}
--
1.7.4.1
From: Andrei Emeltchenko <[email protected]>
Adds support for EWS option. Code partly based on Qualcomm and Atheros
patches sent upstream a year ago.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 11 +++++--
net/bluetooth/l2cap_core.c | 60 +++++++++++++++++++++++++++++++++++++++-
net/bluetooth/l2cap_sock.c | 10 +++---
3 files changed, 71 insertions(+), 10 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 2350057..b586a55 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -31,7 +31,7 @@
#define L2CAP_DEFAULT_MTU 672
#define L2CAP_DEFAULT_MIN_MTU 48
#define L2CAP_DEFAULT_FLUSH_TO 0xffff
-#define L2CAP_DEFAULT_TX_WINDOW 63
+#define L2CAP_DEFAULT_MAX_TX_WINDOW 63
#define L2CAP_DEFAULT_MAX_TX 3
#define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */
#define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */
@@ -42,6 +42,8 @@
#define L2CAP_DEFAULT_SDU_ARRIVAL_TIME 0xFFFFFFFF
#define L2CAP_DEFAULT_ACCESS_LATENCY 0xFFFFFFFF
+#define L2CAP_DEFAULT_MAX_EXT_WINDOW 0x3FFF
+
#define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */
#define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */
@@ -240,6 +242,7 @@ struct l2cap_conf_opt {
#define L2CAP_CONF_RFC 0x04
#define L2CAP_CONF_FCS 0x05
#define L2CAP_CONF_EFS 0x06
+#define L2CAP_CONF_EWS 0x07
#define L2CAP_CONF_MAX_SIZE 22
@@ -357,7 +360,7 @@ struct l2cap_chan {
__u8 fcs;
- __u8 tx_win;
+ __u16 tx_win;
__u8 max_tx;
__u16 retrans_timeout;
__u16 monitor_timeout;
@@ -380,7 +383,7 @@ struct l2cap_chan {
struct sk_buff *sdu;
struct sk_buff *sdu_last_frag;
- __u8 remote_tx_win;
+ __u16 remote_tx_win;
__u8 remote_max_tx;
__u16 remote_mps;
@@ -399,6 +402,7 @@ struct l2cap_chan {
__u32 remote_flush_to;
__u8 ext_flowspec_enable;
+ __u8 ext_ctrl;
struct timer_list chan_timer;
struct timer_list retrans_timer;
@@ -491,6 +495,7 @@ enum {
CONF_STATE2_DEVICE,
CONF_LOCAL_PEND,
CONF_REMOTE_PEND,
+ CONF_EWS_RECV,
};
#define L2CAP_CONF_MAX_CONF_REQ 2
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index de9995d..0d62b05 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -58,6 +58,7 @@
int disable_ertm;
int disable_flowspec = 1;
+int disable_extwindow = 1;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
static u8 l2cap_fixed_chan[8] = { 0x02, };
@@ -1916,6 +1917,28 @@ static inline bool __l2cap_efs_supported(struct l2cap_chan *chan)
chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
}
+static inline bool __l2cap_ews_supported(struct l2cap_chan *chan)
+{
+ return !disable_extwindow &&
+ chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
+}
+
+static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
+{
+ if (chan->tx_win > L2CAP_DEFAULT_MAX_TX_WINDOW &&
+ __l2cap_ews_supported(chan)) {
+ /* use extended control field */
+ chan->ext_ctrl = 1;
+ if (chan->tx_win > L2CAP_DEFAULT_MAX_EXT_WINDOW)
+ chan->tx_win = L2CAP_DEFAULT_MAX_EXT_WINDOW;
+ } else {
+ if (chan->tx_win > L2CAP_DEFAULT_MAX_TX_WINDOW)
+ chan->tx_win = L2CAP_DEFAULT_MAX_TX_WINDOW;
+
+ chan->ext_ctrl = 0;
+ }
+}
+
static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
{
struct l2cap_conf_req *req = data;
@@ -1976,7 +1999,6 @@ done:
case L2CAP_MODE_ERTM:
rfc.mode = L2CAP_MODE_ERTM;
- rfc.txwin_size = chan->tx_win;
rfc.max_transmit = chan->max_tx;
rfc.retrans_timeout = 0;
rfc.monitor_timeout = 0;
@@ -1984,6 +2006,10 @@ done:
if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10)
rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
+ l2cap_txwin_setup(chan);
+ rfc.txwin_size = chan->tx_win > L2CAP_DEFAULT_MAX_TX_WINDOW?
+ L2CAP_DEFAULT_MAX_TX_WINDOW : chan->tx_win;
+
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
(unsigned long) &rfc);
@@ -2023,6 +2049,10 @@ done:
sizeof(efs), (unsigned long) &efs);
}
+ if (__l2cap_ews_supported(chan) && chan->ext_ctrl)
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
+ chan->tx_win);
+
break;
case L2CAP_MODE_STREAMING:
@@ -2061,6 +2091,9 @@ done:
sizeof(efs), (unsigned long) &efs);
}
+ if (__l2cap_ews_supported(chan) && chan->ext_ctrl)
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, 0);
+
break;
}
@@ -2121,6 +2154,12 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
memcpy(&efs, (void *) val, olen);
break;
+ case L2CAP_CONF_EWS:
+ chan->ext_ctrl = 1;
+ chan->remote_tx_win = val;
+ set_bit(CONF_EWS_RECV, &chan->conf_state);
+ break;
+
default:
if (hint)
break;
@@ -2217,7 +2256,11 @@ done:
break;
case L2CAP_MODE_ERTM:
- chan->remote_tx_win = rfc.txwin_size;
+ if (!test_bit(CONF_EWS_RECV, &chan->conf_state))
+ chan->remote_tx_win = rfc.txwin_size;
+ else
+ rfc.txwin_size = L2CAP_DEFAULT_MAX_TX_WINDOW;
+
chan->remote_max_tx = rfc.max_transmit;
if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10)
@@ -2339,6 +2382,13 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
sizeof(efs), (unsigned long) &efs);
break;
+
+ case L2CAP_CONF_EWS:
+ chan->tx_win = val < L2CAP_DEFAULT_MAX_EXT_WINDOW ?
+ val : L2CAP_DEFAULT_MAX_EXT_WINDOW;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS,
+ 2, chan->tx_win);
+ break;
}
}
@@ -2980,6 +3030,9 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
if (!disable_flowspec)
feat_mask |= L2CAP_FEAT_EXT_FLOW;
+ if (!disable_extwindow)
+ feat_mask |= L2CAP_FEAT_EXT_WINDOW;
+
put_unaligned_le32(feat_mask, rsp->data);
l2cap_send_cmd(conn, cmd->ident,
L2CAP_INFO_RSP, sizeof(buf), buf);
@@ -4504,3 +4557,6 @@ MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode");
module_param(disable_flowspec, bool, 0644);
MODULE_PARM_DESC(disable_flowspec, "Disable Extended Flow Specification");
+
+module_param(disable_extwindow, bool, 0644);
+MODULE_PARM_DESC(disable_extwindow, "Disable Extended Window Size");
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 61f1f62..d6a4f63 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -331,7 +331,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
opts.mode = chan->mode;
opts.fcs = chan->fcs;
opts.max_tx = chan->max_tx;
- opts.txwin_size = (__u16)chan->tx_win;
+ opts.txwin_size = chan->tx_win;
len = min_t(unsigned int, len, sizeof(opts));
if (copy_to_user(optval, (char *) &opts, len))
@@ -500,7 +500,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
opts.mode = chan->mode;
opts.fcs = chan->fcs;
opts.max_tx = chan->max_tx;
- opts.txwin_size = (__u16)chan->tx_win;
+ opts.txwin_size = chan->tx_win;
len = min_t(unsigned int, sizeof(opts), optlen);
if (copy_from_user((char *) &opts, optval, len)) {
@@ -508,7 +508,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
break;
}
- if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) {
+ if (opts.txwin_size > L2CAP_DEFAULT_MAX_EXT_WINDOW) {
err = -EINVAL;
break;
}
@@ -532,7 +532,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
chan->omtu = opts.omtu;
chan->fcs = opts.fcs;
chan->max_tx = opts.max_tx;
- chan->tx_win = (__u8)opts.txwin_size;
+ chan->tx_win = opts.txwin_size;
break;
case L2CAP_LM:
@@ -958,7 +958,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
}
chan->max_tx = L2CAP_DEFAULT_MAX_TX;
chan->fcs = L2CAP_FCS_CRC16;
- chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
+ chan->tx_win = L2CAP_DEFAULT_MAX_TX_WINDOW;
chan->sec_level = BT_SECURITY_LOW;
chan->role_switch = 0;
chan->force_reliable = 0;
--
1.7.4.1