Bluetooth: EWS: handling different Control fields

There are three different Control Field formats: the Standard Control
Field, the Enhanced Control Field, and the Extended Control Field.
Patch adds function to handle all those fields seamlessly.

Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
This commit is contained in:
Andrei Emeltchenko 2011-10-17 12:19:56 +03:00 committed by Gustavo F. Padovan
parent e5b82e5892
commit 88843ab06b
2 changed files with 79 additions and 44 deletions

View File

@ -27,6 +27,8 @@
#ifndef __L2CAP_H #ifndef __L2CAP_H
#define __L2CAP_H #define __L2CAP_H
#include <asm/unaligned.h>
/* L2CAP defaults */ /* L2CAP defaults */
#define L2CAP_DEFAULT_MTU 672 #define L2CAP_DEFAULT_MTU 672
#define L2CAP_DEFAULT_MIN_MTU 48 #define L2CAP_DEFAULT_MIN_MTU 48
@ -684,6 +686,32 @@ static inline bool __is_ctrl_poll(struct l2cap_chan *chan, __u32 ctrl)
else else
return ctrl & L2CAP_CTRL_POLL; return ctrl & L2CAP_CTRL_POLL;
} }
static inline __u32 __get_control(struct l2cap_chan *chan, void *p)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return get_unaligned_le32(p);
else
return get_unaligned_le16(p);
}
static inline void __put_control(struct l2cap_chan *chan, __u32 control,
void *p)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return put_unaligned_le32(control, p);
else
return put_unaligned_le16(control, p);
}
static inline __u8 __ctrl_size(struct l2cap_chan *chan)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return L2CAP_EXT_HDR_SIZE - L2CAP_HDR_SIZE;
else
return L2CAP_ENH_HDR_SIZE - L2CAP_HDR_SIZE;
}
extern int disable_ertm; extern int disable_ertm;
int l2cap_init_sockets(void); int l2cap_init_sockets(void);

View File

@ -568,7 +568,7 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
hci_send_acl(conn->hcon, skb, flags); 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 sk_buff *skb;
struct l2cap_hdr *lh; struct l2cap_hdr *lh;
@ -587,7 +587,7 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
if (chan->fcs == L2CAP_FCS_CRC16) if (chan->fcs == L2CAP_FCS_CRC16)
hlen += 2; 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); count = min_t(unsigned int, conn->mtu, hlen);
@ -606,7 +606,8 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE); lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid); lh->cid = cpu_to_le16(chan->dcid);
put_unaligned_le16(control, skb_put(skb, 2));
__put_control(chan, control, skb_put(skb, __ctrl_size(chan)));
if (chan->fcs == L2CAP_FCS_CRC16) { if (chan->fcs == L2CAP_FCS_CRC16) {
u16 fcs = crc16(0, (u8 *)lh, count - 2); u16 fcs = crc16(0, (u8 *)lh, count - 2);
@ -623,7 +624,7 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
hci_send_acl(chan->conn->hcon, skb, flags); 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)) { if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
@ -1279,12 +1280,13 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
static void l2cap_streaming_send(struct l2cap_chan *chan) static void l2cap_streaming_send(struct l2cap_chan *chan)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u16 control, fcs; u32 control;
u16 fcs;
while ((skb = skb_dequeue(&chan->tx_q))) { while ((skb = skb_dequeue(&chan->tx_q))) {
control = get_unaligned_le16(skb->data + L2CAP_HDR_SIZE); control = __get_control(chan, skb->data + L2CAP_HDR_SIZE);
control |= __set_txseq(chan, chan->next_tx_seq); control |= __set_txseq(chan, chan->next_tx_seq);
put_unaligned_le16(control, skb->data + L2CAP_HDR_SIZE); __put_control(chan, control, skb->data + L2CAP_HDR_SIZE);
if (chan->fcs == L2CAP_FCS_CRC16) { if (chan->fcs == L2CAP_FCS_CRC16) {
fcs = crc16(0, (u8 *)skb->data, skb->len - 2); fcs = crc16(0, (u8 *)skb->data, skb->len - 2);
@ -1300,7 +1302,8 @@ static void l2cap_streaming_send(struct l2cap_chan *chan)
static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
{ {
struct sk_buff *skb, *tx_skb; struct sk_buff *skb, *tx_skb;
u16 control, fcs; u16 fcs;
u32 control;
skb = skb_peek(&chan->tx_q); skb = skb_peek(&chan->tx_q);
if (!skb) if (!skb)
@ -1323,7 +1326,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
tx_skb = skb_clone(skb, GFP_ATOMIC); tx_skb = skb_clone(skb, GFP_ATOMIC);
bt_cb(skb)->retries++; bt_cb(skb)->retries++;
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE);
control &= __get_sar_mask(chan); control &= __get_sar_mask(chan);
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
@ -1332,7 +1336,7 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
control |= __set_reqseq(chan, chan->buffer_seq); control |= __set_reqseq(chan, chan->buffer_seq);
control |= __set_txseq(chan, tx_seq); control |= __set_txseq(chan, tx_seq);
put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE);
if (chan->fcs == L2CAP_FCS_CRC16) { if (chan->fcs == L2CAP_FCS_CRC16) {
fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2); fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2);
@ -1345,7 +1349,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
static int l2cap_ertm_send(struct l2cap_chan *chan) static int l2cap_ertm_send(struct l2cap_chan *chan)
{ {
struct sk_buff *skb, *tx_skb; struct sk_buff *skb, *tx_skb;
u16 control, fcs; u16 fcs;
u32 control;
int nsent = 0; int nsent = 0;
if (chan->state != BT_CONNECTED) if (chan->state != BT_CONNECTED)
@ -1363,7 +1368,7 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
bt_cb(skb)->retries++; bt_cb(skb)->retries++;
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE);
control &= __get_sar_mask(chan); control &= __get_sar_mask(chan);
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
@ -1371,8 +1376,8 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
control |= __set_reqseq(chan, chan->buffer_seq); control |= __set_reqseq(chan, chan->buffer_seq);
control |= __set_txseq(chan, chan->next_tx_seq); control |= __set_txseq(chan, chan->next_tx_seq);
put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
__put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE);
if (chan->fcs == L2CAP_FCS_CRC16) { if (chan->fcs == L2CAP_FCS_CRC16) {
fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2); fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2);
@ -1416,7 +1421,7 @@ static int l2cap_retransmit_frames(struct l2cap_chan *chan)
static void l2cap_send_ack(struct l2cap_chan *chan) static void l2cap_send_ack(struct l2cap_chan *chan)
{ {
u16 control = 0; u32 control = 0;
control |= __set_reqseq(chan, chan->buffer_seq); control |= __set_reqseq(chan, chan->buffer_seq);
@ -1437,7 +1442,7 @@ static void l2cap_send_ack(struct l2cap_chan *chan)
static void l2cap_send_srejtail(struct l2cap_chan *chan) static void l2cap_send_srejtail(struct l2cap_chan *chan)
{ {
struct srej_list *tail; struct srej_list *tail;
u16 control; u32 control;
control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
control |= __set_ctrl_final(chan); control |= __set_ctrl_final(chan);
@ -1541,7 +1546,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct ms
static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len, struct msghdr *msg, size_t len,
u16 control, u16 sdulen) u32 control, u16 sdulen)
{ {
struct sock *sk = chan->sk; struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn; struct l2cap_conn *conn = chan->conn;
@ -1575,7 +1580,9 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid); lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
put_unaligned_le16(control, skb_put(skb, 2));
__put_control(chan, control, skb_put(skb, __ctrl_size(chan)));
if (sdulen) if (sdulen)
put_unaligned_le16(sdulen, skb_put(skb, 2)); put_unaligned_le16(sdulen, skb_put(skb, 2));
@ -1596,7 +1603,7 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si
{ {
struct sk_buff *skb; struct sk_buff *skb;
struct sk_buff_head sar_queue; struct sk_buff_head sar_queue;
u16 control; u32 control;
size_t size = 0; size_t size = 0;
skb_queue_head_init(&sar_queue); skb_queue_head_init(&sar_queue);
@ -1640,7 +1647,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) int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u16 control; u32 control;
int err; int err;
/* Connectionless channel */ /* Connectionless channel */
@ -3180,7 +3187,7 @@ 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) static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
{ {
u16 control = 0; u32 control = 0;
chan->frames_sent = 0; chan->frames_sent = 0;
@ -3265,7 +3272,7 @@ static void append_skb_frag(struct sk_buff *skb,
skb->truesize += new_frag->truesize; 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; int err = -EINVAL;
@ -3348,7 +3355,7 @@ 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) static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
{ {
u16 control; u32 control;
BT_DBG("chan %p, Enter local busy", chan); BT_DBG("chan %p, Enter local busy", chan);
@ -3365,7 +3372,7 @@ static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
static void l2cap_ertm_exit_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)) if (!test_bit(CONN_RNR_SENT, &chan->conn_state))
goto done; goto done;
@ -3401,7 +3408,7 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq) static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u16 control; u32 control;
while ((skb = skb_peek(&chan->srej_q)) && while ((skb = skb_peek(&chan->srej_q)) &&
!test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { !test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
@ -3428,7 +3435,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq)
static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq) static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq)
{ {
struct srej_list *l, *tmp; struct srej_list *l, *tmp;
u16 control; u32 control;
list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { list_for_each_entry_safe(l, tmp, &chan->srej_l, list) {
if (l->tx_seq == tx_seq) { if (l->tx_seq == tx_seq) {
@ -3447,7 +3454,7 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq)
static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq) static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
{ {
struct srej_list *new; struct srej_list *new;
u16 control; u32 control;
while (tx_seq != chan->expected_tx_seq) { while (tx_seq != chan->expected_tx_seq) {
control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
@ -3462,7 +3469,7 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; 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)
{ {
u16 tx_seq = __get_txseq(chan, rx_control); u16 tx_seq = __get_txseq(chan, rx_control);
u16 req_seq = __get_reqseq(chan, rx_control); u16 req_seq = __get_reqseq(chan, rx_control);
@ -3471,7 +3478,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
int num_to_ack = (chan->tx_win/6) + 1; int num_to_ack = (chan->tx_win/6) + 1;
int err = 0; 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); tx_seq, rx_control);
if (__is_ctrl_final(chan, rx_control) && if (__is_ctrl_final(chan, rx_control) &&
@ -3597,9 +3604,9 @@ drop:
return 0; 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, BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan,
__get_reqseq(chan, rx_control), rx_control); __get_reqseq(chan, rx_control), rx_control);
chan->expected_ack_seq = __get_reqseq(chan, rx_control); chan->expected_ack_seq = __get_reqseq(chan, rx_control);
@ -3637,11 +3644,11 @@ 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)
{ {
u16 tx_seq = __get_reqseq(chan, rx_control); u16 tx_seq = __get_reqseq(chan, 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); clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
@ -3658,11 +3665,11 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c
set_bit(CONN_REJ_ACT, &chan->conn_state); 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)
{ {
u16 tx_seq = __get_reqseq(chan, rx_control); u16 tx_seq = __get_reqseq(chan, 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); clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
@ -3694,11 +3701,11 @@ 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)
{ {
u16 tx_seq = __get_reqseq(chan, rx_control); u16 tx_seq = __get_reqseq(chan, 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); set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
chan->expected_ack_seq = tx_seq; chan->expected_ack_seq = tx_seq;
@ -3722,9 +3729,9 @@ static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_c
} }
} }
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 (__is_ctrl_final(chan, rx_control) && if (__is_ctrl_final(chan, rx_control) &&
test_bit(CONN_WAIT_F, &chan->conn_state)) { test_bit(CONN_WAIT_F, &chan->conn_state)) {
@ -3759,12 +3766,12 @@ 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) static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
{ {
struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct l2cap_chan *chan = l2cap_pi(sk)->chan;
u16 control; u32 control;
u16 req_seq; u16 req_seq;
int len, next_tx_seq_offset, req_seq_offset; int len, next_tx_seq_offset, req_seq_offset;
control = get_unaligned_le16(skb->data); control = __get_control(chan, skb->data);
skb_pull(skb, 2); skb_pull(skb, __ctrl_size(chan));
len = skb->len; len = skb->len;
/* /*
@ -3830,7 +3837,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
{ {
struct l2cap_chan *chan; struct l2cap_chan *chan;
struct sock *sk = NULL; struct sock *sk = NULL;
u16 control; u32 control;
u16 tx_seq; u16 tx_seq;
int len; int len;
@ -3872,8 +3879,8 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
goto done; goto done;
case L2CAP_MODE_STREAMING: case L2CAP_MODE_STREAMING:
control = get_unaligned_le16(skb->data); control = __get_control(chan, skb->data);
skb_pull(skb, 2); skb_pull(skb, __ctrl_size(chan));
len = skb->len; len = skb->len;
if (l2cap_check_fcs(chan, skb)) if (l2cap_check_fcs(chan, skb))