2009-04-30 01:45:28

by Nathan Holstein

[permalink] [raw]
Subject: [PATCH] Add support for sending enhanced L2CAP data

From: Nathan Holstein <[email protected]>

When functioning in Enhanced Retransmission or Streaming mode, the complexity
of an L2CAP SDU is greatly increased. This patch splits the control stream of
the original l2cap_sock_sendmsg() function dependent upon the socket's mode.

Previously, l2cap_do_send() did the work of copying data from an iovec into a
sk_buff. Since both Basic and Enhanced modes need this functionality, a new
function, l2cap_skbuff_fromiovec() was added to do this work.
---
net/bluetooth/l2cap.c | 131 +++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 115 insertions(+), 16 deletions(-)

diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 31b1cbc..bc7280f 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -41,6 +41,7 @@
#include <linux/list.h>
#include <linux/device.h>
#include <linux/uaccess.h>
+#include <linux/crc16.h>
#include <net/sock.h>

#include <asm/system.h>
@@ -1125,24 +1126,59 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
return 0;
}

-static inline int l2cap_do_send(struct sock *sk, struct msghdr *msg, int len)
+static inline int l2cap_do_send(struct sock *sk, struct sk_buff *skb)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ int err;
+
+ BT_DBG("sk %p, skb %p len %d", sk, skb, skb->len);
+
+ if (pi->tx_buf) {
+ err = -EAGAIN;
+ goto fail;
+ }
+
+ pi->tx_buf = skb;
+ err = hci_send_acl(pi->conn->hcon, skb, 0);
+ if (unlikely(err) < 0) {
+ pi->tx_buf = 0;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ kfree_skb(skb);
+ return err;
+}
+
+static int l2cap_skbuff_fromiovec(
+ struct sock *sk, /* the socket */
+ struct msghdr *msg, /* data to send */
+ u16 *control, /* if != 0, ptr to control value */
+ u16 sdu_len, /* if != 0, fragmented SDU */
+ int len, /* the data length in the PDU */
+ struct sk_buff **out_skb) /* the resulting sk_buff */
{
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
struct sk_buff *skb, **frag;
- int err, hlen, count, sent = 0;
+ int err, count, hlen=0, sent=0, fcs=0;
struct l2cap_hdr *lh;

BT_DBG("sk %p len %d", sk, len);

/* First fragment (with L2CAP header) */
if (sk->sk_type == SOCK_DGRAM)
- hlen = L2CAP_HDR_SIZE + 2;
- else
- hlen = L2CAP_HDR_SIZE;
-
- count = min_t(unsigned int, (conn->mtu - hlen), len);
-
- skb = bt_skb_send_alloc(sk, hlen + count,
+ hlen += 2;
+ if (control)
+ hlen += 2;
+ if (sdu_len)
+ hlen += 2;
+ if (l2cap_pi(sk)->fcs)
+ hlen += 2;
+
+ count = min_t(unsigned int, conn->mtu - hlen, len);
+ skb = bt_skb_send_alloc(sk, count + hlen + L2CAP_HDR_SIZE,
msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
return err;
@@ -1150,16 +1186,27 @@ static inline int l2cap_do_send(struct sock *sk, struct msghdr *msg, int len)
/* Create L2CAP header */
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid);
- lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
+ lh->len = cpu_to_le16(len + hlen);

if (sk->sk_type == SOCK_DGRAM)
put_unaligned(l2cap_pi(sk)->psm, (__le16 *) skb_put(skb, 2));
+ else {
+ if (control)
+ put_unaligned(*control, (__le16 *) skb_put(skb, 2));
+ if (sdu_len)
+ put_unaligned(sdu_len, (__le16 *) skb_put(skb, 2));
+ }

if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
err = -EFAULT;
goto fail;
}

+ if (l2cap_pi(sk)->fcs) {
+ fcs = crc16(0, (u8 *)lh, count + hlen + L2CAP_HDR_SIZE - 2);
+ put_unaligned(fcs, (__le16 *) skb_put(skb, 2));
+ }
+
sent += count;
len -= count;

@@ -1177,15 +1224,18 @@ static inline int l2cap_do_send(struct sock *sk, struct msghdr *msg, int len)
goto fail;
}

+ if (l2cap_pi(sk)->fcs) {
+ u16 fcs = crc16(0, skb->data, count);
+ put_unaligned(fcs, (__le16 *) skb_put(skb, 2));
+ }
+
sent += count;
len -= count;

frag = &(*frag)->next;
}
- err = hci_send_acl(conn->hcon, skb, 0);
- if (err < 0)
- goto fail;

+ *out_skb = skb;
return sent;

fail:
@@ -1193,9 +1243,36 @@ fail:
return err;
}

+static inline int l2cap_create_enhanced_sdu(struct sock *sk, struct msghdr *msg, size_t len, struct sk_buff **skb)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ u8 tx_seq = pi->tx_seq;
+ u16 control = tx_seq << L2CAP_CONTROL_TXSEQ_SHIFT;
+ u16 omtu = (pi->fcs) ? pi->omtu - 2 : pi->omtu;
+ int err;
+
+ BT_DBG("sk %p, len %d", sk, (int)len);
+
+ /* Entire SDU fits into one PDU */
+ if (len <= omtu) {
+ err = l2cap_skbuff_fromiovec(sk, msg, &control, 0, len, skb);
+ if (unlikely(err < 0))
+ return err;
+ L2CAP_SEQ_NUM_INC(pi->tx_seq);
+ bt_cb(*skb)->tx_seq = tx_seq;
+ }
+ else {
+ /* Segmentation added later */
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
int err = 0;

BT_DBG("sock %p, sk %p", sock, sk);
@@ -1208,16 +1285,38 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
return -EOPNOTSUPP;

/* Check outgoing MTU */
- if (sk->sk_type != SOCK_RAW && len > l2cap_pi(sk)->omtu)
+ if ((sk->sk_type != SOCK_RAW || pi->mode == L2CAP_MODE_BASIC) &&
+ len > l2cap_pi(sk)->omtu)
return -EINVAL;

lock_sock(sk);

- if (sk->sk_state == BT_CONNECTED)
- err = l2cap_do_send(sk, msg, len);
+ if (sk->sk_state == BT_CONNECTED) {
+ struct sk_buff *skb = 0;
+
+ switch (pi->mode) {
+ case L2CAP_MODE_BASIC:
+ err = l2cap_skbuff_fromiovec(sk, msg, 0, 0, len, &skb);
+ break;
+
+ case L2CAP_MODE_ENH_RETRANS:
+ err = l2cap_create_enhanced_sdu(sk, msg, len, &skb);
+ break;
+
+ default:
+ BT_DBG("bad state %1.1x", pi->mode);
+ err = -EINVAL;
+ }
+
+ if (err < 0)
+ goto done;
+ if (!(err = l2cap_do_send(sk, skb)))
+ err = len;
+ }
else
err = -ENOTCONN;

+done:
release_sock(sk);
return err;
}
--
1.6.0.6



2009-04-30 01:45:33

by Nathan Holstein

[permalink] [raw]
Subject: [PATCH] Check the FCS of a received L2CAP packet.

From: Nathan Holstein <[email protected]>

When in Enhanced Retransmission or Streaming modes, L2CAP supports an optional
16 bit CRC to validate a packet. This patch checks incoming SDUs for a correct
FCS.
---
net/bluetooth/l2cap.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 45 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 0a9dd65..6328fb8 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -56,6 +56,7 @@
static u32 l2cap_feat_mask =
L2CAP_FEATURE_E_RETRANS |
L2CAP_FEATURE_STREAM |
+ L2CAP_FEATURE_FCS |
L2CAP_FEATURE_FIX_CHAN;
static u8 l2cap_fixed_chan[8] = { 0x02, };

@@ -2566,6 +2567,34 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk
kfree_skb(skb);
}

+static int l2cap_check_fcs(struct l2cap_pinfo *pi, struct l2cap_hdr *lh, struct sk_buff *skb)
+{
+ u16 our_fcs = 0;
+ u16 rcv_fcs;
+
+ if (lh->len != skb->len)
+ return -1;
+
+ if (pi->fcs)
+ {
+ if (lh->len < 4)
+ return -1;
+
+ /* Remove the FCS bytes from data */
+ skb->len -= 2;
+ rcv_fcs = get_unaligned((__le16 *) skb->data + skb->len);
+ /* Include the header in the FCS */
+ our_fcs = crc16(0, skb->data - 4, skb->len);
+
+ if (our_fcs != rcv_fcs)
+ return 0;
+ }
+ else if (lh->len < 2)
+ return -1;
+
+ return 0;
+}
+
static inline int l2cap_data_channel_i_frame(struct sock *sk, u16 rx_control, struct sk_buff *skb)
{
struct l2cap_pinfo *pi = l2cap_pi(sk);
@@ -2623,6 +2652,19 @@ static inline int l2cap_data_channel_enhanced(struct sock *sk, struct l2cap_hdr

BT_DBG("sk %p skb %p", sk, skb);

+ if (l2cap_check_fcs(pi, lh, skb))
+ {
+ BT_DBG("failed fcs");
+ control = pi->req_seq | L2CAP_CONTROL_TYPE_MASK |
+ L2CAP_SUPER_REJECT;
+ if (!l2cap_send_sframe(pi, control))
+ {
+ /* TODO:
+ * set an error state, disconnect */
+ }
+ return -1;
+ }
+
control = get_unaligned((__le16 *) skb->data);
skb_pull(skb, 2);

@@ -2646,6 +2688,9 @@ static int l2cap_data_channel_streaming(struct sock *sk, struct l2cap_hdr *lh, s
struct l2cap_pinfo *pi = l2cap_pi(sk);
u16 control;

+ if (l2cap_check_fcs(pi, lh, skb))
+ return -1;
+
control = get_unaligned((__le16 *) skb->data);
skb_pull(skb, 2);

--
1.6.0.6


2009-04-30 01:45:32

by Nathan Holstein

[permalink] [raw]
Subject: [PATCH] Add support for receiving data over an enhanced L2CAP channel

From: Nathan Holstein <[email protected]>

Receiving data over L2CAP socket depends upon the mode of the connection. In
Basic mode, no processing needs to be done. In Enhanced Retransmission and
Streaming modes there are two types of packets: information and supervisory.

This patch splits the control path of l2cap_data_channel() accordingly.
---
net/bluetooth/l2cap.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 123 insertions(+), 8 deletions(-)

diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 2c694eb..0a9dd65 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -54,6 +54,8 @@
#define VERSION "2.13"

static u32 l2cap_feat_mask =
+ L2CAP_FEATURE_E_RETRANS |
+ L2CAP_FEATURE_STREAM |
L2CAP_FEATURE_FIX_CHAN;
static u8 l2cap_fixed_chan[8] = { 0x02, };

@@ -2564,7 +2566,96 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk
kfree_skb(skb);
}

-static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
+static inline int l2cap_data_channel_i_frame(struct sock *sk, u16 rx_control, struct sk_buff *skb)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ u8 tx_seq = l2cap_control_txseq(rx_control);
+ u16 tx_control;
+ int err = 0;
+
+ BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len);
+
+ if (tx_seq != pi->req_seq) {
+ tx_control = pi->req_seq << L2CAP_CONTROL_REQSEQ_SHIFT;
+ tx_control |= L2CAP_SUPER_SELECT_REJECT;
+ err = -1;
+ goto respond;
+ }
+
+ L2CAP_SEQ_NUM_INC(pi->req_seq);
+ tx_control = pi->req_seq << L2CAP_CONTROL_REQSEQ_SHIFT;
+
+ if ((err = sock_queue_rcv_skb(sk, skb))) {
+ tx_control |= L2CAP_SUPER_RCV_NOT_READY;
+ goto respond;
+ }
+
+ tx_control |= L2CAP_SUPER_RCV_READY;
+
+respond:
+ if (!l2cap_send_sframe(pi, tx_control)) {
+ /* TODO:
+ * set an error state, disconnect */
+ return -1;
+ }
+
+ return err;
+}
+
+static inline int l2cap_data_channel_s_frame(struct sock *sk, u16 rx_control, struct sk_buff *skb)
+{
+ u16 tx_control;
+ BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len);
+
+ if (rx_control & L2CAP_CONTROL_POLL_MASK) {
+ tx_control = rx_control & L2CAP_CONTROL_REQSEQ_MASK;
+ tx_control |= L2CAP_CONTROL_FINAL_MASK;
+ return l2cap_send_sframe(l2cap_pi(sk), tx_control);
+ }
+
+ return -1;
+}
+
+static inline int l2cap_data_channel_enhanced(struct sock *sk, struct l2cap_hdr *lh, struct sk_buff *skb)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ u16 control;
+
+ BT_DBG("sk %p skb %p", sk, skb);
+
+ control = get_unaligned((__le16 *) skb->data);
+ skb_pull(skb, 2);
+
+ if (l2cap_control_txseq(control) != pi->req_seq)
+ {
+ BT_DBG("wrong tx seq");
+ /* TODO:
+ * once we increase our tx window, we can queue this. For now,
+ * just silently drop it. */
+ //return -1;
+ }
+
+ if (l2cap_is_I_frame(control))
+ return l2cap_data_channel_i_frame(sk, control, skb);
+ else
+ return l2cap_data_channel_s_frame(sk, control, skb);
+}
+
+static int l2cap_data_channel_streaming(struct sock *sk, struct l2cap_hdr *lh, struct sk_buff *skb)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ u16 control;
+
+ control = get_unaligned((__le16 *) skb->data);
+ skb_pull(skb, 2);
+
+ if (!sock_queue_rcv_skb(sk, skb))
+ return -1;
+
+ return 0;
+}
+
+static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct l2cap_hdr *lh, struct sk_buff *skb)
{
struct sock *sk;

@@ -2582,13 +2673,37 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
if (l2cap_pi(sk)->imtu < skb->len)
goto drop;

- /* If socket recv buffers overflows we drop data here
- * which is *bad* because L2CAP has to be reliable.
- * But we don't have any other choice. L2CAP doesn't
- * provide flow control mechanism. */
+ switch (l2cap_pi(sk)->mode)
+ {
+ case L2CAP_MODE_BASIC:
+ /* If socket recv buffers overflows we drop data here
+ * which is *bad* because L2CAP has to be reliable.
+ * But we don't have any other choice. L2CAP doesn't
+ * provide flow control mechanism. */

- if (!sock_queue_rcv_skb(sk, skb))
- goto done;
+ if (!sock_queue_rcv_skb(sk, skb))
+ goto done;
+ break;
+
+ case L2CAP_MODE_ENH_RETRANS:
+ if (!l2cap_data_channel_enhanced(sk, lh, skb));
+ goto done;
+ break;
+
+ case L2CAP_MODE_STREAMING:
+ if (!l2cap_data_channel_streaming(sk, lh, skb));
+ goto done;
+ break;
+
+ case L2CAP_MODE_RETRANS:
+ case L2CAP_MODE_FLOWCTL:
+ default:
+ /* not implemented, no intention to implement
+ * these modes are superceded by Enhanced Retransmission
+ * and Streaming modes. */
+ BT_DBG("sk %p: bad mode 0x%2.2x", sk, l2cap_pi(sk)->mode);
+ break;
+ }

drop:
kfree_skb(skb);
@@ -2652,7 +2767,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
break;

default:
- l2cap_data_channel(conn, cid, skb);
+ l2cap_data_channel(conn, cid, lh, skb);
break;
}
}
--
1.6.0.6


2009-04-30 01:45:31

by Nathan Holstein

[permalink] [raw]
Subject: [PATCH] Add support for parsing enhanced L2CAP configuration options.

From: Nathan Holstein <[email protected]>

When negotiating for either Enhanced Retransmission or Streaming modes, both
sides of the connection must agree upon the configuration. This patch adds
support for parsing and validating the configuration receiverd from the remote.
---
include/net/bluetooth/l2cap.h | 6 +++-
net/bluetooth/l2cap.c | 55 +++++++++++++++++++++++++++++++---------
2 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 3770fb6..faf0280 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -370,8 +370,12 @@ struct l2cap_pinfo {

#define L2CAP_CONF_REQ_SENT 0x01
#define L2CAP_CONF_INPUT_DONE 0x02
-#define L2CAP_CONF_OUTPUT_DONE 0x04
+#define L2CAP_CONF_MTU_DONE 0x04
+#define L2CAP_CONF_MODE_DONE 0x04
#define L2CAP_CONF_CONNECT_PEND 0x80
+#define L2CAP_CONF_OUTPUT_DONE(state) (\
+ ((state) & L2CAP_CONF_MTU_DONE) && \
+ ((state) & L2CAP_CONF_MODE_DONE))

#define L2CAP_CONF_MAX_RETRIES 2

diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 1716969..2c694eb 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -749,6 +749,9 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)

}

+ if (pi->mode == L2CAP_MODE_BASIC)
+ pi->conf_state |= L2CAP_CONF_MODE_DONE;
+
/* Default config options */
pi->conf_len = 0;
pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
@@ -1943,6 +1946,10 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data)
memcpy(&rfc, (void *) val, olen);
break;

+ case L2CAP_CONF_FCS:
+ pi->fcs = val;
+ break;
+
default:
if (hint)
break;
@@ -1953,27 +1960,49 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data)
}
}

+ if (pi->mode != rfc.mode) {
+ result = L2CAP_CONF_UNACCEPT;
+ rfc.mode = pi->mode;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+ sizeof(rfc), (unsigned long) &rfc);
+ }
+
if (result == L2CAP_CONF_SUCCESS) {
/* Configure output options and let the other side know
* which ones we don't like. */
+ if (mtu < L2CAP_CONF_MIN_MTU)
+ result = L2CAP_CONF_UNACCEPT;
+ else {
+ pi->omtu = mtu;
+ pi->conf_state |= L2CAP_CONF_MTU_DONE;
+ }
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);

- if (rfc.mode == L2CAP_MODE_BASIC) {
- if (mtu < pi->omtu)
- result = L2CAP_CONF_UNACCEPT;
- else {
- pi->omtu = mtu;
- pi->conf_state |= L2CAP_CONF_OUTPUT_DONE;
- }
+ switch (rfc.mode) {
+ case L2CAP_MODE_BASIC:
+ pi->fcs = 0;
+ pi->conf_state |= L2CAP_CONF_MODE_DONE;
+ break;

- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
- } else {
+ case L2CAP_MODE_ENH_RETRANS:
+ case L2CAP_MODE_STREAMING:
+ pi->remote_tx_win = rfc.txwin_size;
+ pi->remote_max_tx = rfc.max_transmit;
+ pi->retrans_timeout = rfc.retrans_timeout;
+ pi->monitor_timeout = rfc.monitor_timeout;
+ pi->max_pdu_size = rfc.max_pdu_size;
+
+ pi->conf_state |= L2CAP_CONF_MODE_DONE;
+ break;
+
+ default:
result = L2CAP_CONF_UNACCEPT;

memset(&rfc, 0, sizeof(rfc));
- rfc.mode = L2CAP_MODE_BASIC;
+ rfc.mode = pi->mode;

l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
- sizeof(rfc), (unsigned long) &rfc);
+ sizeof(rfc), (unsigned long) &rfc);
}
}

@@ -2236,7 +2265,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
/* Reset config buffer. */
l2cap_pi(sk)->conf_len = 0;

- if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE))
+ if (!L2CAP_CONF_OUTPUT_DONE(l2cap_pi(sk)->conf_state))
goto unlock;

if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {
@@ -2309,7 +2338,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr

l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE;

- if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) {
+ if (L2CAP_CONF_OUTPUT_DONE(l2cap_pi(sk)->conf_state)) {
sk->sk_state = BT_CONNECTED;
l2cap_chan_ready(sk);
}
--
1.6.0.6


2009-04-30 01:45:30

by Nathan Holstein

[permalink] [raw]
Subject: [PATCH] Append RFC option when configuring an L2CAP socket.

From: Nathan Holstein <[email protected]>

When negotiating Enhanced Retransmission or Streaming modes, append our RFC
option to the configuration list.
---
net/bluetooth/l2cap.c | 24 ++++++++++++++++++++++++
1 files changed, 24 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index a68a0cf..1716969 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -1864,6 +1864,7 @@ static int l2cap_build_conf_req(struct sock *sk, void *data)
{
struct l2cap_pinfo *pi = l2cap_pi(sk);
struct l2cap_conf_req *req = data;
+ struct l2cap_conf_rfc rfc = { 0, };
void *ptr = req->data;

BT_DBG("sk %p", sk);
@@ -1871,6 +1872,29 @@ static int l2cap_build_conf_req(struct sock *sk, void *data)
if (pi->imtu != L2CAP_DEFAULT_MTU)
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);

+ switch (pi->mode) {
+ case L2CAP_MODE_ENH_RETRANS:
+ rfc.mode = L2CAP_MODE_ENH_RETRANS;
+ rfc.txwin_size = L2CAP_DEFAULT_RX_WINDOW;
+ rfc.max_transmit = L2CAP_DEFAULT_MAX_RECEIVE;
+ rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TIMEOUT);
+ rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TIMEOUT);
+ rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_RX_APDU);
+ break;
+
+ case L2CAP_MODE_STREAMING:
+ rfc.mode = L2CAP_MODE_STREAMING;
+ rfc.txwin_size = 0;
+ rfc.max_transmit = 0;
+ rfc.retrans_timeout = 0;
+ rfc.monitor_timeout = 0;
+ rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_RX_APDU);
+ break;
+ }
+ if (rfc.mode)
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc);
+
/* FIXME: Need actual value of the flush timeout */
//if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
// l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
--
1.6.0.6


2009-04-30 01:45:29

by Nathan Holstein

[permalink] [raw]
Subject: [PATCH] Add support for sending L2CAP S-frames.

From: Nathan Holstein <[email protected]>

This patch creates the function l2cap_send_sframe() which builds a sk_buff
contains the specified control field and then sends the frame over the ACL
connection.
---
net/bluetooth/l2cap.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 45 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index bc7280f..a68a0cf 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -69,6 +69,7 @@ static void l2cap_sock_kill(struct sock *sk);

static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
u8 code, u8 ident, u16 dlen, void *data);
+static struct sk_buff *l2cap_build_sframe(struct l2cap_pinfo *pi, u16 control);

/* ---- L2CAP timers ---- */
static void l2cap_sock_timeout(unsigned long arg)
@@ -333,6 +334,18 @@ static inline int l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16
return hci_send_acl(conn->hcon, skb, 0);
}

+static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control)
+{
+ struct sk_buff *skb = l2cap_build_sframe(pi, control);
+
+ BT_DBG("control 0x%2.2x", control);
+
+ if (!skb)
+ return -ENOMEM;
+
+ return hci_send_acl(pi->conn->hcon, skb, 0);
+}
+
static void l2cap_do_start(struct sock *sk)
{
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
@@ -1752,6 +1765,38 @@ fail:
return NULL;
}

+static struct sk_buff *l2cap_build_sframe(struct l2cap_pinfo *pi, u16 control)
+{
+ struct sk_buff *skb;
+ struct l2cap_hdr *lh;
+ int len;
+
+ BT_DBG("pi %p, control 0x%2.2x", pi, control);
+
+ len = L2CAP_HDR_SIZE + 2;
+ control |= L2CAP_CONTROL_TYPE_MASK;
+
+ if (pi->fcs);
+ len += 2;
+
+ skb = bt_skb_alloc(len, GFP_ATOMIC);
+ if (!skb)
+ return NULL;
+
+ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+ lh->len = cpu_to_le16(pi->fcs ? 4 : 2);
+ lh->cid = cpu_to_le16(pi->dcid);
+
+ put_unaligned(control, (__le16 *)skb_put(skb, 2));
+ if (pi->fcs)
+ {
+ u16 fcs = crc16(0, (u8 *)lh, len);
+ put_unaligned(fcs, (__le16 *)skb_put(skb, 2));
+ }
+
+ return skb;
+}
+
static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val)
{
struct l2cap_conf_opt *opt = *ptr;
--
1.6.0.6