From fcafde2e6d81aa7901f9b10e6a097592f0637b79 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 22 Dec 2009 15:58:08 +0200 Subject: Bluetooth: Remove double free of SKB pointer in L2CAP Trivial fix for double free of SKB pointer with kfree_skb to make code simplier and cleaner. Remove unused variable err. Signed-off-by: Andrei Emeltchenko Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 1120cf14a548..8acc19ed9c4d 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3518,7 +3518,6 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk struct l2cap_pinfo *pi; u16 control, len; u8 tx_seq; - int err; sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); if (!sk) { @@ -3570,13 +3569,11 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk goto drop; if (__is_iframe(control)) - err = l2cap_data_channel_iframe(sk, control, skb); + l2cap_data_channel_iframe(sk, control, skb); else - err = l2cap_data_channel_sframe(sk, control, skb); + l2cap_data_channel_sframe(sk, control, skb); - if (!err) - goto done; - break; + goto done; case L2CAP_MODE_STREAMING: control = get_unaligned_le16(skb->data); @@ -3602,7 +3599,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk else pi->expected_tx_seq = tx_seq + 1; - err = l2cap_sar_reassembly_sdu(sk, skb, control); + l2cap_sar_reassembly_sdu(sk, skb, control); goto done; -- cgit v1.2.2 From e420aba331f44de0eed6871441293a6124d566d1 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 23 Dec 2009 13:07:14 +0200 Subject: Bluetooth: Fix memory leak in L2CAP Move skb_clone after error confition check so it is not going potentially out of the scope. Signed-off-by: Andrei Emeltchenko Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 8acc19ed9c4d..400efa26ddba 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1368,7 +1368,6 @@ static int l2cap_ertm_send(struct sock *sk) while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk)) && !(pi->conn_state & L2CAP_CONN_REMOTE_BUSY)) { - tx_skb = skb_clone(skb, GFP_ATOMIC); if (pi->remote_max_tx && bt_cb(skb)->retries == pi->remote_max_tx) { @@ -1376,6 +1375,8 @@ static int l2cap_ertm_send(struct sock *sk) break; } + tx_skb = skb_clone(skb, GFP_ATOMIC); + bt_cb(skb)->retries++; control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); -- cgit v1.2.2 From 28812fe11a21826ba4c97c6c7971a619987cd912 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 5 Jan 2010 12:48:07 +0100 Subject: driver-core: Add attribute argument to class_attribute show/store Passing the attribute to the low level IO functions allows all kinds of cleanups, by sharing low level IO code without requiring an own function for every piece of data. Also drivers can extend the attributes with own data fields and use that in the low level function. This makes the class attributes the same as sysdev_class attributes and plain attributes. This will allow further cleanups in drivers. Full tree sweep converting all users. Signed-off-by: Andi Kleen Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/l2cap.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 400efa26ddba..4db7ae2fe07d 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3937,7 +3937,9 @@ drop: return 0; } -static ssize_t l2cap_sysfs_show(struct class *dev, char *buf) +static ssize_t l2cap_sysfs_show(struct class *dev, + struct class_attribute *attr, + char *buf) { struct sock *sk; struct hlist_node *node; -- cgit v1.2.2 From 101545f6fef4a0a3ea8daf0b5b880df2c6a92a69 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 15 Mar 2010 14:12:58 -0700 Subject: Bluetooth: Fix potential bad memory access with sysfs files When creating a high number of Bluetooth sockets (L2CAP, SCO and RFCOMM) it is possible to scribble repeatedly on arbitrary pages of memory. Ensure that the content of these sysfs files is always less than one page. Even if this means truncating. The files in question are scheduled to be moved over to debugfs in the future anyway. Based on initial patches from Neil Brown and Linus Torvalds Reported-by: Neil Brown Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 4db7ae2fe07d..27551820741e 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3944,16 +3944,24 @@ static ssize_t l2cap_sysfs_show(struct class *dev, struct sock *sk; struct hlist_node *node; char *str = buf; + int size = PAGE_SIZE; read_lock_bh(&l2cap_sk_list.lock); sk_for_each(sk, node, &l2cap_sk_list.head) { struct l2cap_pinfo *pi = l2cap_pi(sk); + int len; - str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n", + len = snprintf(str, size, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n", batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), sk->sk_state, __le16_to_cpu(pi->psm), pi->scid, pi->dcid, pi->imtu, pi->omtu, pi->sec_level); + + size -= len; + if (size <= 0) + break; + + str += len; } read_unlock_bh(&l2cap_sk_list.lock); -- cgit v1.2.2 From aef7d97cc604309b66f6f45cce02cd734934cd4e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 21 Mar 2010 05:27:45 +0100 Subject: Bluetooth: Convert debug files to actually use debugfs instead of sysfs Some of the debug files ended up wrongly in sysfs, because at that point of time, debugfs didn't exist. Convert these files to use debugfs and also seq_file. This patch converts all of these files at once and then removes the exported symbol for the Bluetooth sysfs class. Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 51 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 21 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 27551820741e..43e17f7d7ecd 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include #include #include @@ -3937,39 +3939,42 @@ drop: return 0; } -static ssize_t l2cap_sysfs_show(struct class *dev, - struct class_attribute *attr, - char *buf) +static int l2cap_debugfs_show(struct seq_file *f, void *p) { struct sock *sk; struct hlist_node *node; - char *str = buf; - int size = PAGE_SIZE; read_lock_bh(&l2cap_sk_list.lock); sk_for_each(sk, node, &l2cap_sk_list.head) { struct l2cap_pinfo *pi = l2cap_pi(sk); - int len; - - len = snprintf(str, size, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n", - batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), - sk->sk_state, __le16_to_cpu(pi->psm), pi->scid, - pi->dcid, pi->imtu, pi->omtu, pi->sec_level); - - size -= len; - if (size <= 0) - break; - str += len; + seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n", + batostr(&bt_sk(sk)->src), + batostr(&bt_sk(sk)->dst), + sk->sk_state, __le16_to_cpu(pi->psm), + pi->scid, pi->dcid, + pi->imtu, pi->omtu, pi->sec_level); } read_unlock_bh(&l2cap_sk_list.lock); - return str - buf; + return 0; } -static CLASS_ATTR(l2cap, S_IRUGO, l2cap_sysfs_show, NULL); +static int l2cap_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, l2cap_debugfs_show, inode->i_private); +} + +static const struct file_operations l2cap_debugfs_fops = { + .open = l2cap_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *l2cap_debugfs; static const struct proto_ops l2cap_sock_ops = { .family = PF_BLUETOOTH, @@ -4029,8 +4034,12 @@ static int __init l2cap_init(void) goto error; } - if (class_create_file(bt_class, &class_attr_l2cap) < 0) - BT_ERR("Failed to create L2CAP info file"); + if (bt_debugfs) { + l2cap_debugfs = debugfs_create_file("l2cap", 0444, + bt_debugfs, NULL, &l2cap_debugfs_fops); + if (!l2cap_debugfs) + BT_ERR("Failed to create L2CAP debug file"); + } BT_INFO("L2CAP ver %s", VERSION); BT_INFO("L2CAP socket layer initialized"); @@ -4044,7 +4053,7 @@ error: static void __exit l2cap_exit(void) { - class_remove_file(bt_class, &class_attr_l2cap); + debugfs_remove(l2cap_debugfs); if (bt_sock_unregister(BTPROTO_L2CAP) < 0) BT_ERR("L2CAP socket unregistration failed"); -- cgit v1.2.2 From c2c77ec83bdad17fb688557b5b3fdc36661dd1c6 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 19 Mar 2010 10:26:28 +0200 Subject: Bluetooth: Fix kernel crash on L2CAP stress tests Added very simple check that req buffer has enough space to fit configuration parameters. Shall be enough to reject packets with configuration size more than req buffer. Crash trace below [ 6069.659393] Unable to handle kernel paging request at virtual address 02000205 [ 6069.673034] Internal error: Oops: 805 [#1] PREEMPT ... [ 6069.727172] PC is at l2cap_add_conf_opt+0x70/0xf0 [l2cap] [ 6069.732604] LR is at l2cap_recv_frame+0x1350/0x2e78 [l2cap] ... [ 6070.030303] Backtrace: [ 6070.032806] [] (l2cap_add_conf_opt+0x0/0xf0 [l2cap]) from [] (l2cap_recv_frame+0x1350/0x2e78 [l2cap]) [ 6070.043823] r8:dc5d3100 r7:df2a91d6 r6:00000001 r5:df2a8000 r4:00000200 [ 6070.050659] [] (l2cap_recv_frame+0x0/0x2e78 [l2cap]) from [] (l2cap_recv_acldata+0x2bc/0x350 [l2cap]) [ 6070.061798] [] (l2cap_recv_acldata+0x0/0x350 [l2cap]) from [] (hci_rx_task+0x244/0x478 [bluetooth]) [ 6070.072631] r6:dc647700 r5:00000001 r4:df2ab740 [ 6070.077362] [] (hci_rx_task+0x0/0x478 [bluetooth]) from [] (tasklet_action+0x78/0xd8) [ 6070.087005] [] (tasklet_action+0x0/0xd8) from [] Signed-off-by: Andrei Emeltchenko Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 43e17f7d7ecd..7794a2e2adce 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2832,6 +2832,11 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr int len = cmd->len - sizeof(*rsp); char req[64]; + if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) { + l2cap_send_disconn_req(conn, sk); + goto done; + } + /* throw out any old stored conf requests */ result = L2CAP_CONF_SUCCESS; len = l2cap_parse_conf_rsp(sk, rsp->data, -- cgit v1.2.2 From 6503d96168f891ffa3b70ae6c9698a1a722025a0 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Wed, 31 Mar 2010 22:58:26 +0000 Subject: net: check the length of the socket address passed to connect(2) check the length of the socket address passed to connect(2). Check the length of the socket address passed to connect(2). If the length is invalid, -EINVAL will be returned. Signed-off-by: Changli Gao ---- net/bluetooth/l2cap.c | 3 ++- net/bluetooth/rfcomm/sock.c | 3 ++- net/bluetooth/sco.c | 3 ++- net/can/bcm.c | 3 +++ net/ieee802154/af_ieee802154.c | 3 +++ net/ipv4/af_inet.c | 5 +++++ net/netlink/af_netlink.c | 3 +++ 7 files changed, 20 insertions(+), 3 deletions(-) Signed-off-by: David S. Miller --- net/bluetooth/l2cap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 7794a2e2adce..99d68c34e4f1 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1002,7 +1002,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al BT_DBG("sk %p", sk); - if (!addr || addr->sa_family != AF_BLUETOOTH) + if (!addr || alen < sizeof(addr->sa_family) || + addr->sa_family != AF_BLUETOOTH) return -EINVAL; memset(&la, 0, sizeof(la)); -- cgit v1.2.2 From aa395145165cb06a0d0885221bbe0ce4a564391d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 20 Apr 2010 13:03:51 +0000 Subject: net: sk_sleep() helper Define a new function to return the waitqueue of a "struct sock". static inline wait_queue_head_t *sk_sleep(struct sock *sk) { return sk->sk_sleep; } Change all read occurrences of sk_sleep by a call to this function. Needed for a future RCU conversion. sk_sleep wont be a field directly available. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/bluetooth/l2cap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 99d68c34e4f1..c1e60eed5a97 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1147,7 +1147,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int fl BT_DBG("sk %p timeo %ld", sk, timeo); /* Wait for an incoming connection. (wake-one). */ - add_wait_queue_exclusive(sk->sk_sleep, &wait); + add_wait_queue_exclusive(sk_sleep(sk), &wait); while (!(nsk = bt_accept_dequeue(sk, newsock))) { set_current_state(TASK_INTERRUPTIBLE); if (!timeo) { @@ -1170,7 +1170,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int fl } } set_current_state(TASK_RUNNING); - remove_wait_queue(sk->sk_sleep, &wait); + remove_wait_queue(sk_sleep(sk), &wait); if (err) goto done; -- cgit v1.2.2 From 477fffb082920476cc26f238d65538ccb8d601e1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 21 Apr 2010 23:52:01 +0000 Subject: bluetooth: handle l2cap_create_connless_pdu() errors l2cap_create_connless_pdu() can sometimes return ERR_PTR(-ENOMEM) or ERR_PTR(-EFAULT). Signed-off-by: Dan Carpenter Acked-by: Marcel Holtmann Signed-off-by: David S. Miller --- net/bluetooth/l2cap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 99d68c34e4f1..9753b690a8b3 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1626,7 +1626,10 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms /* Connectionless channel */ if (sk->sk_type == SOCK_DGRAM) { skb = l2cap_create_connless_pdu(sk, msg, len); - err = l2cap_do_send(sk, skb); + if (IS_ERR(skb)) + err = PTR_ERR(skb); + else + err = l2cap_do_send(sk, skb); goto done; } -- cgit v1.2.2 From c69163e9ed5048407cc84f439cbfecc53f6f7131 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:35 -0300 Subject: Bluetooth: Move specific Basic Mode code to the right place MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inside "case L2CAP_MODE_BASIC:" we don't need to check for sk_type and L2CAP mode. So only the length check is fine. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 864c76f4a678..c9a848d3ef94 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1611,11 +1611,6 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; - /* Check outgoing MTU */ - if (sk->sk_type == SOCK_SEQPACKET && pi->mode == L2CAP_MODE_BASIC && - len > pi->omtu) - return -EINVAL; - lock_sock(sk); if (sk->sk_state != BT_CONNECTED) { @@ -1635,6 +1630,12 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms switch (pi->mode) { case L2CAP_MODE_BASIC: + /* Check outgoing MTU */ + if (len > pi->omtu) { + err = -EINVAL; + goto done; + } + /* Create a basic PDU */ skb = l2cap_create_basic_pdu(sk, msg, len); if (IS_ERR(skb)) { -- cgit v1.2.2 From faaebd192ec9c3febcab98149d1309199a5b886c Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:35 -0300 Subject: Bluetooth: Fix memory leak of S-frames into L2CAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit l2cap_data_channel do not free the S-frame, so we free it here. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index c9a848d3ef94..46f22640a337 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3522,6 +3522,7 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str break; } + kfree_skb(skb); return 0; } -- cgit v1.2.2 From 7dffe4210233a2860c3f41477c40b3252edf2b7d Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:36 -0300 Subject: Bluetooth: Fix expected_tx_seq calculation on L2CAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All operation related to the txWindow should be modulo 64. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 46f22640a337..401011a53c73 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3611,7 +3611,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (pi->expected_tx_seq == tx_seq) pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; else - pi->expected_tx_seq = tx_seq + 1; + pi->expected_tx_seq = (tx_seq + 1) % 64; l2cap_sar_reassembly_sdu(sk, skb, control); -- cgit v1.2.2 From d1daa091e8612f3aab14d28b5836375fafe155e1 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:36 -0300 Subject: Bluetooth: Fix ACL MTU issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ERTM and Streaming Modes was having problems when the ACL MTU is lower than MPS. The 'minus 10' is to take in account the header and fcs lenghts. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 401011a53c73..99cf1772b481 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2267,6 +2267,8 @@ done: rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); + if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10) + rfc.max_pdu_size = pi->conn->mtu - 10; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -2288,6 +2290,8 @@ done: rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); + if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10) + rfc.max_pdu_size = pi->conn->mtu - 10; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); -- cgit v1.2.2 From e8235c6bdd1c7ffbaa7eb8dcdbb46c51f1e5d72e Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:36 -0300 Subject: Bluetooth: Use a l2cap_pinfo struct instead l2cap_pi() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivial clean up. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 99cf1772b481..a9c152a09f0b 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1291,7 +1291,7 @@ static int l2cap_streaming_send(struct sock *sk) control |= pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); - if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) { + if (pi->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); } @@ -1344,7 +1344,7 @@ static int l2cap_retransmit_frame(struct sock *sk, u8 tx_seq) | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); - if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) { + if (pi->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); } @@ -1388,7 +1388,7 @@ static int l2cap_ertm_send(struct sock *sk) put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); - if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) { + if (pi->fcs == L2CAP_FCS_CRC16) { fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2); put_unaligned_le16(fcs, skb->data + tx_skb->len - 2); } @@ -3518,10 +3518,10 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str pi->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(sk); - del_timer(&l2cap_pi(sk)->retrans_timer); + del_timer(&pi->retrans_timer); if (rx_control & L2CAP_CTRL_POLL) { u16 control = L2CAP_CTRL_FINAL; - l2cap_send_rr_or_rnr(l2cap_pi(sk), control); + l2cap_send_rr_or_rnr(pi, control); } break; } @@ -3622,7 +3622,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk goto done; default: - BT_DBG("sk %p: bad mode 0x%2.2x", sk, l2cap_pi(sk)->mode); + BT_DBG("sk %p: bad mode 0x%2.2x", sk, pi->mode); break; } -- cgit v1.2.2 From d5392c8f1e9faef089bb7cb66c3314da8bddd1fe Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:36 -0300 Subject: Bluetooth: Implement 'Send IorRRorRNR' event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After receive a RR with P bit set ERTM shall use this funcion to choose what type of frame to reply with F bit = 1. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index a9c152a09f0b..06687e264703 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1383,6 +1383,10 @@ static int l2cap_ertm_send(struct sock *sk) bt_cb(skb)->retries++; control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + if (pi->conn_state & L2CAP_CONN_SEND_FBIT) { + control |= L2CAP_CTRL_FINAL; + pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; + } control |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) | (pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); @@ -1404,6 +1408,7 @@ static int l2cap_ertm_send(struct sock *sk) pi->next_tx_seq = (pi->next_tx_seq + 1) % 64; pi->unacked_frames++; + pi->frames_sent++; if (skb_queue_is_last(TX_QUEUE(sk), skb)) sk->sk_send_head = NULL; @@ -2191,6 +2196,7 @@ static inline void l2cap_ertm_init(struct sock *sk) l2cap_pi(sk)->unacked_frames = 0; l2cap_pi(sk)->buffer_seq = 0; l2cap_pi(sk)->num_to_ack = 0; + l2cap_pi(sk)->frames_sent = 0; setup_timer(&l2cap_pi(sk)->retrans_timer, l2cap_retrans_timeout, (unsigned long) sk); @@ -3148,6 +3154,38 @@ static int l2cap_check_fcs(struct l2cap_pinfo *pi, struct sk_buff *skb) return 0; } +static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + u16 control = 0; + + pi->frames_sent = 0; + pi->conn_state |= L2CAP_CONN_SEND_FBIT; + + control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + + if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + control |= L2CAP_SUPER_RCV_NOT_READY | L2CAP_CTRL_FINAL; + l2cap_send_sframe(pi, control); + pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; + } + + if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY && pi->unacked_frames > 0) + __mod_retrans_timer(); + + l2cap_ertm_send(sk); + + if (!(pi->conn_state & L2CAP_CONN_LOCAL_BUSY) && + pi->frames_sent == 0) { + control |= L2CAP_SUPER_RCV_READY; + if (pi->conn_state & L2CAP_CONN_SEND_FBIT) { + control |= L2CAP_CTRL_FINAL; + pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; + } + l2cap_send_sframe(pi, control); + } +} + static void l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_seq, u8 sar) { struct sk_buff *next_skb; @@ -3418,10 +3456,7 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str switch (rx_control & L2CAP_CTRL_SUPERVISE) { case L2CAP_SUPER_RCV_READY: if (rx_control & L2CAP_CTRL_POLL) { - u16 control = L2CAP_CTRL_FINAL; - control |= L2CAP_SUPER_RCV_READY | - (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT); - l2cap_send_sframe(l2cap_pi(sk), control); + l2cap_send_i_or_rr_or_rnr(sk); pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; } else if (rx_control & L2CAP_CTRL_FINAL) { -- cgit v1.2.2 From 1d8f5d16913d74e428950ee02fe9ff7e6391c120 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:37 -0300 Subject: Bluetooth: Support case with F bit set under WAIT_F state. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On receipt of a F=1 under WAIT_F state ERTM shall stop monitor timer and start retransmission timer (if there are unacked frames). Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 06687e264703..36cd4e4e6ad1 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3364,6 +3364,13 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); + if (L2CAP_CTRL_FINAL & rx_control) { + del_timer(&pi->monitor_timer); + if (pi->unacked_frames > 0) + __mod_retrans_timer(); + pi->conn_state &= ~L2CAP_CONN_WAIT_F; + } + pi->expected_ack_seq = req_seq; l2cap_drop_acked_frames(sk); @@ -3453,6 +3460,13 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); + if (L2CAP_CTRL_FINAL & rx_control) { + del_timer(&pi->monitor_timer); + if (pi->unacked_frames > 0) + __mod_retrans_timer(); + pi->conn_state &= ~L2CAP_CONN_WAIT_F; + } + switch (rx_control & L2CAP_CTRL_SUPERVISE) { case L2CAP_SUPER_RCV_READY: if (rx_control & L2CAP_CTRL_POLL) { @@ -3472,14 +3486,6 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str l2cap_ertm_send(sk); } - if (!(pi->conn_state & L2CAP_CONN_WAIT_F)) - break; - - pi->conn_state &= ~L2CAP_CONN_WAIT_F; - del_timer(&pi->monitor_timer); - - if (pi->unacked_frames > 0) - __mod_retrans_timer(); } else { pi->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(sk); -- cgit v1.2.2 From 277ffbe362823d18a17792fbd8e507010e666299 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:37 -0300 Subject: Bluetooth: Check the minimum {I,S}-frame size into L2CAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All packets with size fewer than the minimum specified is dropped. Note that the size of the l2cap basic header, FCS and SAR fields are already subtracted of len at the moment of the size check. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 36cd4e4e6ad1..ac00f5fac2d2 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3627,10 +3627,17 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (l2cap_check_fcs(pi, skb)) goto drop; - if (__is_iframe(control)) + if (__is_iframe(control)) { + if (len < 4) + goto drop; + l2cap_data_channel_iframe(sk, control, skb); - else + } else { + if (len != 0) + goto drop; + l2cap_data_channel_sframe(sk, control, skb); + } goto done; @@ -3645,7 +3652,8 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (pi->fcs == L2CAP_FCS_CRC16) len -= 2; - if (len > L2CAP_DEFAULT_MAX_PDU_SIZE || __is_sframe(control)) + if (len > L2CAP_DEFAULT_MAX_PDU_SIZE || len < 4 + || __is_sframe(control)) goto drop; if (l2cap_check_fcs(pi, skb)) -- cgit v1.2.2 From 36f2fd585f43199f006a3b5ff84e95815102cd31 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:37 -0300 Subject: Bluetooth: Check if SDU size is greater than MTU on L2CAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After reassembly the SDU we need to check his size. It can't overflow the MTU size. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index ac00f5fac2d2..2e354d29f102 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3277,15 +3277,19 @@ static int l2cap_sar_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 co pi->conn_state &= ~L2CAP_CONN_SAR_SDU; pi->partial_sdu_len += skb->len; + if (pi->partial_sdu_len > pi->imtu) + goto drop; + if (pi->partial_sdu_len == pi->sdu_len) { _skb = skb_clone(pi->sdu, GFP_ATOMIC); err = sock_queue_rcv_skb(sk, _skb); if (err < 0) kfree_skb(_skb); } - kfree_skb(pi->sdu); err = 0; +drop: + kfree_skb(pi->sdu); break; } -- cgit v1.2.2 From 9e917af13d59182f95bbb5483dc0c4254dfb7944 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:37 -0300 Subject: Bluetooth: Implement SendAck() Action on ERTM. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shall be used to ack received frames, It must decide type of acknowledgment between a RR frame, a RNR frame or transmission of pending I-frames. It also modifies l2cap_ertm_send() to report the number of frames sent. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 2e354d29f102..0a739ef167c2 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -352,6 +352,11 @@ static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) count = min_t(unsigned int, conn->mtu, hlen); control |= L2CAP_CTRL_FRAME_TYPE; + if (pi->conn_state & L2CAP_CONN_SEND_FBIT) { + control |= L2CAP_CTRL_FINAL; + pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; + } + skb = bt_skb_alloc(count, GFP_ATOMIC); if (!skb) return -ENOMEM; @@ -1364,7 +1369,7 @@ static int l2cap_ertm_send(struct sock *sk) struct sk_buff *skb, *tx_skb; struct l2cap_pinfo *pi = l2cap_pi(sk); u16 control, fcs; - int err; + int err, nsent = 0; if (pi->conn_state & L2CAP_CONN_WAIT_F) return 0; @@ -1414,8 +1419,27 @@ static int l2cap_ertm_send(struct sock *sk) sk->sk_send_head = NULL; else sk->sk_send_head = skb_queue_next(TX_QUEUE(sk), skb); + + nsent++; } + return nsent; +} + +static int l2cap_send_ack(struct l2cap_pinfo *pi) +{ + struct sock *sk = (struct sock *)pi; + u16 control = 0; + + control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + + if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + control |= L2CAP_SUPER_RCV_NOT_READY; + return l2cap_send_sframe(pi, control); + } else if (l2cap_ertm_send(sk) == 0) { + control |= L2CAP_SUPER_RCV_READY; + return l2cap_send_sframe(pi, control); + } return 0; } @@ -1678,7 +1702,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms else err = l2cap_ertm_send(sk); - if (!err) + if (err >= 0) err = len; break; @@ -3178,10 +3202,6 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) if (!(pi->conn_state & L2CAP_CONN_LOCAL_BUSY) && pi->frames_sent == 0) { control |= L2CAP_SUPER_RCV_READY; - if (pi->conn_state & L2CAP_CONN_SEND_FBIT) { - control |= L2CAP_CTRL_FINAL; - pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; - } l2cap_send_sframe(pi, control); } } @@ -3362,7 +3382,6 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str struct l2cap_pinfo *pi = l2cap_pi(sk); u8 tx_seq = __get_txseq(rx_control); u8 req_seq = __get_reqseq(rx_control); - u16 tx_control = 0; u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT; int err = 0; @@ -3449,11 +3468,9 @@ expected: return err; pi->num_to_ack = (pi->num_to_ack + 1) % L2CAP_DEFAULT_NUM_TO_ACK; - if (pi->num_to_ack == L2CAP_DEFAULT_NUM_TO_ACK - 1) { - tx_control |= L2CAP_SUPER_RCV_READY; - tx_control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - l2cap_send_sframe(pi, tx_control); - } + if (pi->num_to_ack == L2CAP_DEFAULT_NUM_TO_ACK - 1) + l2cap_send_ack(pi); + return 0; } -- cgit v1.2.2 From f0946ccfc7da403a46b7ff7cb2e3deffac108742 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:37 -0300 Subject: Bluetooth: Move set of P-bit to l2cap_send_sframe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Abstract the send of of P-bit and avoids code duplication like we did with the setting of F-bit. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 0a739ef167c2..852c1400d069 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -357,6 +357,11 @@ static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; } + if (pi->conn_state & L2CAP_CONN_SEND_PBIT) { + control |= L2CAP_CTRL_POLL; + pi->conn_state &= ~L2CAP_CONN_SEND_PBIT; + } + skb = bt_skb_alloc(count, GFP_ATOMIC); if (!skb) return -ENOMEM; @@ -3364,10 +3369,6 @@ static void l2cap_send_srejframe(struct sock *sk, u8 tx_seq) while (tx_seq != pi->expected_tx_seq) { control = L2CAP_SUPER_SELECT_REJECT; control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; - if (pi->conn_state & L2CAP_CONN_SEND_PBIT) { - control |= L2CAP_CTRL_POLL; - pi->conn_state &= ~L2CAP_CONN_SEND_PBIT; - } l2cap_send_sframe(pi, control); new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); -- cgit v1.2.2 From 73edaa9933219e25d6733b78d1e2c881025705e2 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:38 -0300 Subject: Bluetooth: Add Recv RR (P=0)(F=0) for SREJ_SENT state on ERTM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This finishes the implementation of Recv RR (P=0)(F=0) for the Enhanced Retransmission Mode on L2CAP. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 852c1400d069..e5cd64ac6fb2 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3517,7 +3517,10 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str __mod_retrans_timer(); pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - l2cap_ertm_send(sk); + if (pi->conn_state & L2CAP_CONN_SREJ_SENT) + l2cap_send_ack(pi); + else + l2cap_ertm_send(sk); } break; -- cgit v1.2.2 From e072745f4adb01b909bd08a0cfc8f79348f4d2c6 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:38 -0300 Subject: Bluetooth: Split l2cap_data_channel_sframe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a function for each type fo S-frame and avoid a lot of nested code. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 204 ++++++++++++++++++++++++++++---------------------- 1 file changed, 115 insertions(+), 89 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index e5cd64ac6fb2..068edf7704bf 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3475,120 +3475,146 @@ expected: return 0; } -static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) +static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) { struct l2cap_pinfo *pi = l2cap_pi(sk); u8 tx_seq = __get_reqseq(rx_control); - BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); - - if (L2CAP_CTRL_FINAL & rx_control) { - del_timer(&pi->monitor_timer); - if (pi->unacked_frames > 0) - __mod_retrans_timer(); - pi->conn_state &= ~L2CAP_CONN_WAIT_F; - } + if (rx_control & L2CAP_CTRL_POLL) { + l2cap_send_i_or_rr_or_rnr(sk); + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - switch (rx_control & L2CAP_CTRL_SUPERVISE) { - case L2CAP_SUPER_RCV_READY: - if (rx_control & L2CAP_CTRL_POLL) { - l2cap_send_i_or_rr_or_rnr(sk); - pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - - } else if (rx_control & L2CAP_CTRL_FINAL) { - pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - pi->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(sk); - - if (pi->conn_state & L2CAP_CONN_REJ_ACT) - pi->conn_state &= ~L2CAP_CONN_REJ_ACT; - else { - sk->sk_send_head = TX_QUEUE(sk)->next; - pi->next_tx_seq = pi->expected_ack_seq; - l2cap_ertm_send(sk); - } + } else if (rx_control & L2CAP_CTRL_FINAL) { + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + pi->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(sk); - } else { - pi->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(sk); + if (pi->conn_state & L2CAP_CONN_REJ_ACT) + pi->conn_state &= ~L2CAP_CONN_REJ_ACT; + else { + sk->sk_send_head = TX_QUEUE(sk)->next; + pi->next_tx_seq = pi->expected_ack_seq; + l2cap_ertm_send(sk); + } - if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && - (pi->unacked_frames > 0)) - __mod_retrans_timer(); + } else { + pi->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(sk); - pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - if (pi->conn_state & L2CAP_CONN_SREJ_SENT) - l2cap_send_ack(pi); - else - l2cap_ertm_send(sk); - } - break; + if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && + (pi->unacked_frames > 0)) + __mod_retrans_timer(); - case L2CAP_SUPER_REJECT: pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + if (pi->conn_state & L2CAP_CONN_SREJ_SENT) + l2cap_send_ack(pi); + else + l2cap_ertm_send(sk); + } +} - pi->expected_ack_seq = __get_reqseq(rx_control); - l2cap_drop_acked_frames(sk); +static inline void l2cap_data_channel_rejframe(struct sock *sk, u16 rx_control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + u8 tx_seq = __get_reqseq(rx_control); - if (rx_control & L2CAP_CTRL_FINAL) { - if (pi->conn_state & L2CAP_CONN_REJ_ACT) - pi->conn_state &= ~L2CAP_CONN_REJ_ACT; - else { - sk->sk_send_head = TX_QUEUE(sk)->next; - pi->next_tx_seq = pi->expected_ack_seq; - l2cap_ertm_send(sk); - } - } else { + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + + pi->expected_ack_seq = __get_reqseq(rx_control); + l2cap_drop_acked_frames(sk); + + if (rx_control & L2CAP_CTRL_FINAL) { + if (pi->conn_state & L2CAP_CONN_REJ_ACT) + pi->conn_state &= ~L2CAP_CONN_REJ_ACT; + else { sk->sk_send_head = TX_QUEUE(sk)->next; pi->next_tx_seq = pi->expected_ack_seq; l2cap_ertm_send(sk); - - if (pi->conn_state & L2CAP_CONN_WAIT_F) { - pi->srej_save_reqseq = tx_seq; - pi->conn_state |= L2CAP_CONN_REJ_ACT; - } } + } else { + sk->sk_send_head = TX_QUEUE(sk)->next; + pi->next_tx_seq = pi->expected_ack_seq; + l2cap_ertm_send(sk); - break; + if (pi->conn_state & L2CAP_CONN_WAIT_F) { + pi->srej_save_reqseq = tx_seq; + pi->conn_state |= L2CAP_CONN_REJ_ACT; + } + } +} +static inline void l2cap_data_channel_srejframe(struct sock *sk, u16 rx_control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + u8 tx_seq = __get_reqseq(rx_control); - case L2CAP_SUPER_SELECT_REJECT: - pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - if (rx_control & L2CAP_CTRL_POLL) { - pi->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(sk); - l2cap_retransmit_frame(sk, tx_seq); - l2cap_ertm_send(sk); - if (pi->conn_state & L2CAP_CONN_WAIT_F) { - pi->srej_save_reqseq = tx_seq; - pi->conn_state |= L2CAP_CONN_SREJ_ACT; - } - } else if (rx_control & L2CAP_CTRL_FINAL) { - if ((pi->conn_state & L2CAP_CONN_SREJ_ACT) && - pi->srej_save_reqseq == tx_seq) - pi->conn_state &= ~L2CAP_CONN_SREJ_ACT; - else - l2cap_retransmit_frame(sk, tx_seq); + if (rx_control & L2CAP_CTRL_POLL) { + pi->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(sk); + l2cap_retransmit_frame(sk, tx_seq); + l2cap_ertm_send(sk); + if (pi->conn_state & L2CAP_CONN_WAIT_F) { + pi->srej_save_reqseq = tx_seq; + pi->conn_state |= L2CAP_CONN_SREJ_ACT; } - else { + } else if (rx_control & L2CAP_CTRL_FINAL) { + if ((pi->conn_state & L2CAP_CONN_SREJ_ACT) && + pi->srej_save_reqseq == tx_seq) + pi->conn_state &= ~L2CAP_CONN_SREJ_ACT; + else l2cap_retransmit_frame(sk, tx_seq); - if (pi->conn_state & L2CAP_CONN_WAIT_F) { - pi->srej_save_reqseq = tx_seq; - pi->conn_state |= L2CAP_CONN_SREJ_ACT; - } + } else { + l2cap_retransmit_frame(sk, tx_seq); + if (pi->conn_state & L2CAP_CONN_WAIT_F) { + pi->srej_save_reqseq = tx_seq; + pi->conn_state |= L2CAP_CONN_SREJ_ACT; } + } +} + +static inline void l2cap_data_channel_rnrframe(struct sock *sk, u16 rx_control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + u8 tx_seq = __get_reqseq(rx_control); + + pi->conn_state |= L2CAP_CONN_REMOTE_BUSY; + pi->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(sk); + + del_timer(&pi->retrans_timer); + if (rx_control & L2CAP_CTRL_POLL) { + u16 control = L2CAP_CTRL_FINAL; + l2cap_send_rr_or_rnr(pi, control); + } +} + +static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) +{ + BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); + + if (L2CAP_CTRL_FINAL & rx_control) { + del_timer(&l2cap_pi(sk)->monitor_timer); + if (l2cap_pi(sk)->unacked_frames > 0) + __mod_retrans_timer(); + l2cap_pi(sk)->conn_state &= ~L2CAP_CONN_WAIT_F; + } + + switch (rx_control & L2CAP_CTRL_SUPERVISE) { + case L2CAP_SUPER_RCV_READY: + l2cap_data_channel_rrframe(sk, rx_control); break; - case L2CAP_SUPER_RCV_NOT_READY: - pi->conn_state |= L2CAP_CONN_REMOTE_BUSY; - pi->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(sk); + case L2CAP_SUPER_REJECT: + l2cap_data_channel_rejframe(sk, rx_control); + break; - del_timer(&pi->retrans_timer); - if (rx_control & L2CAP_CTRL_POLL) { - u16 control = L2CAP_CTRL_FINAL; - l2cap_send_rr_or_rnr(pi, control); - } + case L2CAP_SUPER_SELECT_REJECT: + l2cap_data_channel_srejframe(sk, rx_control); + break; + + case L2CAP_SUPER_RCV_NOT_READY: + l2cap_data_channel_rnrframe(sk, rx_control); break; } -- cgit v1.2.2 From 99b0d4b7b09edeacf4542bced5c01239375b51a9 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:38 -0300 Subject: Bluetooth: Handle all cases of receipt of RNR-frames into L2CAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We weren't handling the receipt under SREJ_SENT state table. It also introduce l2cap_send_srejtail(). It will be used in the nexts commits too. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 068edf7704bf..8937a842347a 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1448,6 +1448,22 @@ static int l2cap_send_ack(struct l2cap_pinfo *pi) return 0; } +static int l2cap_send_srejtail(struct sock *sk) +{ + struct srej_list *tail; + u16 control; + + control = L2CAP_SUPER_SELECT_REJECT; + control |= L2CAP_CTRL_FINAL; + + tail = list_entry(SREJ_LIST(sk)->prev, struct srej_list, list); + control |= tail->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + + l2cap_send_sframe(l2cap_pi(sk), control); + + return 0; +} + static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, int len, int count, struct sk_buff *skb) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; @@ -3582,11 +3598,19 @@ static inline void l2cap_data_channel_rnrframe(struct sock *sk, u16 rx_control) pi->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(sk); - del_timer(&pi->retrans_timer); - if (rx_control & L2CAP_CTRL_POLL) { - u16 control = L2CAP_CTRL_FINAL; - l2cap_send_rr_or_rnr(pi, control); + if (!(pi->conn_state & L2CAP_CONN_SREJ_SENT)) { + del_timer(&pi->retrans_timer); + if (rx_control & L2CAP_CTRL_POLL) { + u16 control = L2CAP_CTRL_FINAL; + l2cap_send_rr_or_rnr(pi, control); + } + return; } + + if (rx_control & L2CAP_CTRL_POLL) + l2cap_send_srejtail(sk); + else + l2cap_send_sframe(pi, L2CAP_SUPER_RCV_READY); } static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) -- cgit v1.2.2 From 6e3a59819fac19006fe4255b87928e5a12c54532 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:38 -0300 Subject: Bluetooth: Group the ack of I-frames into l2cap_data_channel_rrframe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It also fix a bug: we weren't acknowledging I-frames when P=1. Note that when F=1 we are acknowledging packets before setting RemoteBusy to False. The spec says we should do that in the opposite order, but acknowledment of packets doesn't care about RemoteBusy flag so we can do that in the order we want. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 8937a842347a..d096c7c11ab5 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3494,7 +3494,9 @@ expected: static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) { struct l2cap_pinfo *pi = l2cap_pi(sk); - u8 tx_seq = __get_reqseq(rx_control); + + pi->expected_ack_seq = __get_reqseq(rx_control); + l2cap_drop_acked_frames(sk); if (rx_control & L2CAP_CTRL_POLL) { l2cap_send_i_or_rr_or_rnr(sk); @@ -3502,8 +3504,6 @@ static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) } else if (rx_control & L2CAP_CTRL_FINAL) { pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - pi->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(sk); if (pi->conn_state & L2CAP_CONN_REJ_ACT) pi->conn_state &= ~L2CAP_CONN_REJ_ACT; @@ -3514,9 +3514,6 @@ static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) } } else { - pi->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(sk); - if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && (pi->unacked_frames > 0)) __mod_retrans_timer(); -- cgit v1.2.2 From 8abb52ee00c4b3f857269eb6b7145991bab869bf Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:38 -0300 Subject: Bluetooth: Remove duplicate use of __get_reqseq() macro on L2CAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tx_seq var already has the value of __get_reqseq(). Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index d096c7c11ab5..e9ac9fb11917 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3533,7 +3533,7 @@ static inline void l2cap_data_channel_rejframe(struct sock *sk, u16 rx_control) pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - pi->expected_ack_seq = __get_reqseq(rx_control); + pi->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(sk); if (rx_control & L2CAP_CTRL_FINAL) { -- cgit v1.2.2 From 05fbd89dd4153341717b33d9e8ae8bd29db6c1c8 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:39 -0300 Subject: Bluetooth: Finish implementation for Rec RR (P=1) on ERTM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now the code handles the case under SREJ_SENT state. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index e9ac9fb11917..f3869857ee9f 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3499,8 +3499,17 @@ static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) l2cap_drop_acked_frames(sk); if (rx_control & L2CAP_CTRL_POLL) { - l2cap_send_i_or_rr_or_rnr(sk); - pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { + if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && + (pi->unacked_frames > 0)) + __mod_retrans_timer(); + + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + l2cap_send_srejtail(sk); + } else { + l2cap_send_i_or_rr_or_rnr(sk); + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + } } else if (rx_control & L2CAP_CTRL_FINAL) { pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; -- cgit v1.2.2 From c1b4f43be01c2a363be021485dd18cca33cfab8a Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:39 -0300 Subject: Bluetooth: Add timer to Acknowledge I-frames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We ack I-frames on each txWindow/5 I-frames received, but if the sender stop to send I-frames and it's not a txWindow multiple we can leave some frames unacked. So I added a timer to ack I-frames on this case. The timer expires in 200ms. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index f3869857ee9f..03006568f8a1 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2235,6 +2235,15 @@ static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val) *ptr += L2CAP_CONF_OPT_SIZE + len; } +static void l2cap_ack_timeout(unsigned long arg) +{ + struct sock *sk = (void *) arg; + + bh_lock_sock(sk); + l2cap_send_ack(l2cap_pi(sk)); + bh_unlock_sock(sk); +} + static inline void l2cap_ertm_init(struct sock *sk) { l2cap_pi(sk)->expected_ack_seq = 0; @@ -2247,6 +2256,8 @@ static inline void l2cap_ertm_init(struct sock *sk) l2cap_retrans_timeout, (unsigned long) sk); setup_timer(&l2cap_pi(sk)->monitor_timer, l2cap_monitor_timeout, (unsigned long) sk); + setup_timer(&l2cap_pi(sk)->ack_timer, + l2cap_ack_timeout, (unsigned long) sk); __skb_queue_head_init(SREJ_QUEUE(sk)); } @@ -2975,6 +2986,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd skb_queue_purge(SREJ_QUEUE(sk)); del_timer(&l2cap_pi(sk)->retrans_timer); del_timer(&l2cap_pi(sk)->monitor_timer); + del_timer(&l2cap_pi(sk)->ack_timer); } l2cap_chan_del(sk, ECONNRESET); @@ -3005,6 +3017,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd skb_queue_purge(SREJ_QUEUE(sk)); del_timer(&l2cap_pi(sk)->retrans_timer); del_timer(&l2cap_pi(sk)->monitor_timer); + del_timer(&l2cap_pi(sk)->ack_timer); } l2cap_chan_del(sk, 0); @@ -3484,6 +3497,8 @@ expected: if (err < 0) return err; + __mod_ack_timer(); + pi->num_to_ack = (pi->num_to_ack + 1) % L2CAP_DEFAULT_NUM_TO_ACK; if (pi->num_to_ack == L2CAP_DEFAULT_NUM_TO_ACK - 1) l2cap_send_ack(pi); -- cgit v1.2.2 From 2fb862e215e53630066c677e06d7551fa38bf235 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:39 -0300 Subject: Bluetooth: Ignore Tx Window value with Streaming mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tx Window value shall not be used with Streaming Mode and the receiver of the config Request shall ignore its value. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 03006568f8a1..f604405fe667 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2495,7 +2495,6 @@ done: break; case L2CAP_MODE_STREAMING: - pi->remote_tx_win = rfc.txwin_size; pi->max_pdu_size = rfc.max_pdu_size; pi->conf_state |= L2CAP_CONF_MODE_DONE; -- cgit v1.2.2 From 7b1c0049be3aabc18831ada339dbcf41ba8c81fd Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:39 -0300 Subject: Bluetooth: Read RFC conf option on a successful Conf RSP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Enhanced Retransmission Mode and Streaming Mode a entity can send, on a successful Conf RSP, new values for the RFC fields. For example, the entity can send txWindow and MPS values less than the value received on a Conf REQ. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index f604405fe667..c50c05738fb6 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2602,6 +2602,42 @@ static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 fla return ptr - data; } +static void l2cap_conf_rfc_get(struct sock *sk, void *rsp, int len) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + int type, olen; + unsigned long val; + struct l2cap_conf_rfc rfc; + + BT_DBG("sk %p, rsp %p, len %d", sk, rsp, len); + + if ((pi->mode != L2CAP_MODE_ERTM) && (pi->mode != L2CAP_MODE_STREAMING)) + return; + + while (len >= L2CAP_CONF_OPT_SIZE) { + len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); + + switch (type) { + case L2CAP_CONF_RFC: + if (olen == sizeof(rfc)) + memcpy(&rfc, (void *)val, olen); + goto done; + } + } + +done: + switch (rfc.mode) { + case L2CAP_MODE_ERTM: + pi->remote_tx_win = rfc.txwin_size; + pi->retrans_timeout = rfc.retrans_timeout; + pi->monitor_timeout = rfc.monitor_timeout; + pi->mps = le16_to_cpu(rfc.max_pdu_size); + break; + case L2CAP_MODE_STREAMING: + pi->mps = le16_to_cpu(rfc.max_pdu_size); + } +} + static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_cmd_rej *rej = (struct l2cap_cmd_rej *) data; @@ -2881,6 +2917,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data; u16 scid, flags, result; struct sock *sk; + int len = cmd->len - sizeof(*rsp); scid = __le16_to_cpu(rsp->scid); flags = __le16_to_cpu(rsp->flags); @@ -2895,11 +2932,11 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr switch (result) { case L2CAP_CONF_SUCCESS: + l2cap_conf_rfc_get(sk, rsp->data, len); break; case L2CAP_CONF_UNACCEPT: if (l2cap_pi(sk)->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) { - int len = cmd->len - sizeof(*rsp); char req[64]; if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) { -- cgit v1.2.2 From 1c7621596d11b9c3e19eb88a818758dee4901c95 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:40 -0300 Subject: Bluetooth: Fix configuration of the MPS value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were accepting values bigger than we can accept. This was leading ERTM to drop packets because of wrong FCS checks. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index c50c05738fb6..94be5dbb2569 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1606,21 +1606,21 @@ static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, siz __skb_queue_head_init(&sar_queue); control = L2CAP_SDU_START; - skb = l2cap_create_iframe_pdu(sk, msg, pi->max_pdu_size, control, len); + skb = l2cap_create_iframe_pdu(sk, msg, pi->remote_mps, control, len); if (IS_ERR(skb)) return PTR_ERR(skb); __skb_queue_tail(&sar_queue, skb); - len -= pi->max_pdu_size; - size +=pi->max_pdu_size; + len -= pi->remote_mps; + size += pi->remote_mps; control = 0; while (len > 0) { size_t buflen; - if (len > pi->max_pdu_size) { + if (len > pi->remote_mps) { control |= L2CAP_SDU_CONTINUE; - buflen = pi->max_pdu_size; + buflen = pi->remote_mps; } else { control |= L2CAP_SDU_END; buflen = len; @@ -1701,7 +1701,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: /* Entire SDU fits into one PDU */ - if (len <= pi->max_pdu_size) { + if (len <= pi->remote_mps) { control = L2CAP_SDU_UNSEGMENTED; skb = l2cap_create_iframe_pdu(sk, msg, len, control, 0); if (IS_ERR(skb)) { @@ -2330,7 +2330,7 @@ done: rfc.monitor_timeout = 0; rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10) - rfc.max_pdu_size = pi->conn->mtu - 10; + rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -2353,7 +2353,7 @@ done: rfc.monitor_timeout = 0; rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10) - rfc.max_pdu_size = pi->conn->mtu - 10; + rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -2482,7 +2482,10 @@ done: case L2CAP_MODE_ERTM: pi->remote_tx_win = rfc.txwin_size; pi->remote_max_tx = rfc.max_transmit; - pi->max_pdu_size = rfc.max_pdu_size; + if (rfc.max_pdu_size > pi->conn->mtu - 10) + rfc.max_pdu_size = le16_to_cpu(pi->conn->mtu - 10); + + pi->remote_mps = le16_to_cpu(rfc.max_pdu_size); rfc.retrans_timeout = L2CAP_DEFAULT_RETRANS_TO; rfc.monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; @@ -2495,7 +2498,10 @@ done: break; case L2CAP_MODE_STREAMING: - pi->max_pdu_size = rfc.max_pdu_size; + if (rfc.max_pdu_size > pi->conn->mtu - 10) + rfc.max_pdu_size = le16_to_cpu(pi->conn->mtu - 10); + + pi->remote_mps = le16_to_cpu(rfc.max_pdu_size); pi->conf_state |= L2CAP_CONF_MODE_DONE; @@ -2574,11 +2580,10 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, pi->remote_tx_win = rfc.txwin_size; pi->retrans_timeout = rfc.retrans_timeout; pi->monitor_timeout = rfc.monitor_timeout; - pi->max_pdu_size = le16_to_cpu(rfc.max_pdu_size); + pi->mps = le16_to_cpu(rfc.max_pdu_size); break; case L2CAP_MODE_STREAMING: - pi->max_pdu_size = le16_to_cpu(rfc.max_pdu_size); - break; + pi->mps = le16_to_cpu(rfc.max_pdu_size); } } @@ -3753,7 +3758,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk * Receiver will miss it and start proper recovery * procedures and ask retransmission. */ - if (len > L2CAP_DEFAULT_MAX_PDU_SIZE) + if (len > pi->mps) goto drop; if (l2cap_check_fcs(pi, skb)) @@ -3784,8 +3789,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (pi->fcs == L2CAP_FCS_CRC16) len -= 2; - if (len > L2CAP_DEFAULT_MAX_PDU_SIZE || len < 4 - || __is_sframe(control)) + if (len > pi->mps || len < 4 || __is_sframe(control)) goto drop; if (l2cap_check_fcs(pi, skb)) -- cgit v1.2.2 From 10467e9e9b89272b25b56688bb276d0830e9ab9a Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:40 -0300 Subject: Bluetooth: Add le16 macro to Retransmission and Monitor Timeouts values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a possible problem with Big Endian machines. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 94be5dbb2569..0889949b6896 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2487,8 +2487,10 @@ done: pi->remote_mps = le16_to_cpu(rfc.max_pdu_size); - rfc.retrans_timeout = L2CAP_DEFAULT_RETRANS_TO; - rfc.monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; + rfc.retrans_timeout = + le16_to_cpu(L2CAP_DEFAULT_RETRANS_TO); + rfc.monitor_timeout = + le16_to_cpu(L2CAP_DEFAULT_MONITOR_TO); pi->conf_state |= L2CAP_CONF_MODE_DONE; @@ -2578,8 +2580,8 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, switch (rfc.mode) { case L2CAP_MODE_ERTM: pi->remote_tx_win = rfc.txwin_size; - pi->retrans_timeout = rfc.retrans_timeout; - pi->monitor_timeout = rfc.monitor_timeout; + pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); + pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); pi->mps = le16_to_cpu(rfc.max_pdu_size); break; case L2CAP_MODE_STREAMING: @@ -2634,8 +2636,8 @@ done: switch (rfc.mode) { case L2CAP_MODE_ERTM: pi->remote_tx_win = rfc.txwin_size; - pi->retrans_timeout = rfc.retrans_timeout; - pi->monitor_timeout = rfc.monitor_timeout; + pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); + pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); pi->mps = le16_to_cpu(rfc.max_pdu_size); break; case L2CAP_MODE_STREAMING: -- cgit v1.2.2 From 052897ca5019d9157ae09e5e84eee2a9ef5dccc6 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:40 -0300 Subject: Bluetooth: Check the SDU size against the MTU value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the SDU size is greater than the MTU something is wrong, so report an error. Signed-off-by: Gustavo F. Padovan [jprvita@profusion.mobi: set err to appropriate errno value] Signed-off-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 0889949b6896..e936913c921e 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3338,6 +3338,11 @@ static int l2cap_sar_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 co pi->sdu_len = get_unaligned_le16(skb->data); skb_pull(skb, 2); + if (pi->sdu_len > pi->imtu) { + err = -EMSGSIZE; + break; + } + pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC); if (!pi->sdu) { err = -ENOMEM; -- cgit v1.2.2 From 855666cccc939d392316de17512e17a08b2fa05a Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:40 -0300 Subject: Bluetooth: Send Ack after clear the SREJ list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As specified by Bluetooth 3.0 spec we shall send an acknowledgment using the Send-Ack() after clear the SREJ list. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index e936913c921e..c6bc1b9ed657 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3493,6 +3493,7 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str if (list_empty(SREJ_LIST(sk))) { pi->buffer_seq = pi->buffer_seq_srej; pi->conn_state &= ~L2CAP_CONN_SREJ_SENT; + l2cap_send_ack(pi); } } else { struct srej_list *l; -- cgit v1.2.2 From 14b5aa71ec506f4e38ca6a1dc02ecd668ecfd902 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:40 -0300 Subject: Bluetooth: Add sockopt configuration for txWindow on L2CAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we can set/get Transmission Window size via sockopt. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index c6bc1b9ed657..530079649b43 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -782,6 +782,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->omtu = l2cap_pi(parent)->omtu; pi->mode = l2cap_pi(parent)->mode; pi->fcs = l2cap_pi(parent)->fcs; + pi->tx_win = l2cap_pi(parent)->tx_win; pi->sec_level = l2cap_pi(parent)->sec_level; pi->role_switch = l2cap_pi(parent)->role_switch; pi->force_reliable = l2cap_pi(parent)->force_reliable; @@ -790,6 +791,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->omtu = 0; pi->mode = L2CAP_MODE_BASIC; pi->fcs = L2CAP_FCS_CRC16; + pi->tx_win = L2CAP_DEFAULT_TX_WINDOW; pi->sec_level = BT_SECURITY_LOW; pi->role_switch = 0; pi->force_reliable = 0; @@ -1782,6 +1784,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us opts.flush_to = l2cap_pi(sk)->flush_to; opts.mode = l2cap_pi(sk)->mode; opts.fcs = l2cap_pi(sk)->fcs; + opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; len = min_t(unsigned int, sizeof(opts), optlen); if (copy_from_user((char *) &opts, optval, len)) { @@ -1793,6 +1796,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us l2cap_pi(sk)->omtu = opts.omtu; l2cap_pi(sk)->mode = opts.mode; l2cap_pi(sk)->fcs = opts.fcs; + l2cap_pi(sk)->tx_win = (__u8)opts.txwin_size; break; case L2CAP_LM: @@ -1907,6 +1911,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us opts.flush_to = l2cap_pi(sk)->flush_to; opts.mode = l2cap_pi(sk)->mode; opts.fcs = l2cap_pi(sk)->fcs; + opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *) &opts, len)) @@ -2324,7 +2329,7 @@ done: case L2CAP_MODE_ERTM: rfc.mode = L2CAP_MODE_ERTM; - rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW; + rfc.txwin_size = pi->tx_win; rfc.max_transmit = max_transmit; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; -- cgit v1.2.2 From 803020c6fa63aa738cfda3329c9675b42023e9d2 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:41 -0300 Subject: Bluetooth: Change acknowledgement to use the value of txWindow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we can set the txWindow we need to change the acknowledgement procedure to ack after each (pi->txWindow/6 + 1). The plus 1 is to avoid the zero value. It also renames pi->num_to_ack to a better name: pi->num_acked. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 530079649b43..0b0b237bb786 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2254,7 +2254,7 @@ static inline void l2cap_ertm_init(struct sock *sk) l2cap_pi(sk)->expected_ack_seq = 0; l2cap_pi(sk)->unacked_frames = 0; l2cap_pi(sk)->buffer_seq = 0; - l2cap_pi(sk)->num_to_ack = 0; + l2cap_pi(sk)->num_acked = 0; l2cap_pi(sk)->frames_sent = 0; setup_timer(&l2cap_pi(sk)->retrans_timer, @@ -3466,6 +3466,7 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str u8 tx_seq = __get_txseq(rx_control); u8 req_seq = __get_reqseq(rx_control); u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT; + int num_to_ack = (pi->tx_win/6) + 1; int err = 0; BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); @@ -3553,8 +3554,8 @@ expected: __mod_ack_timer(); - pi->num_to_ack = (pi->num_to_ack + 1) % L2CAP_DEFAULT_NUM_TO_ACK; - if (pi->num_to_ack == L2CAP_DEFAULT_NUM_TO_ACK - 1) + pi->num_acked = (pi->num_acked + 1) % num_to_ack; + if (pi->num_acked == num_to_ack - 1) l2cap_send_ack(pi); return 0; -- cgit v1.2.2 From 369ba30264826f38eefc61b93688100be8adbd4d Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:41 -0300 Subject: Bluetooth: Add module parameter for txWindow size on L2CAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Very useful for testing purposes. Signed-off-by: Gustavo F. Padovan [jprvita@profusion.mobi: improved parameter description] Signed-off-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 0b0b237bb786..ff1466b21580 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -57,6 +57,7 @@ static int enable_ertm = 0; static int max_transmit = L2CAP_DEFAULT_MAX_TX; +static int tx_window = L2CAP_DEFAULT_TX_WINDOW; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; static u8 l2cap_fixed_chan[8] = { 0x02, }; @@ -791,7 +792,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->omtu = 0; pi->mode = L2CAP_MODE_BASIC; pi->fcs = L2CAP_FCS_CRC16; - pi->tx_win = L2CAP_DEFAULT_TX_WINDOW; + pi->tx_win = tx_window; pi->sec_level = BT_SECURITY_LOW; pi->role_switch = 0; pi->force_reliable = 0; @@ -4296,6 +4297,9 @@ MODULE_PARM_DESC(enable_ertm, "Enable enhanced retransmission mode"); module_param(max_transmit, uint, 0644); MODULE_PARM_DESC(max_transmit, "Max transmit value (default = 3)"); +module_param(tx_window, uint, 0644); +MODULE_PARM_DESC(tx_window, "Transmission window size value (default = 63)"); + MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION); MODULE_VERSION(VERSION); -- cgit v1.2.2 From 68d7f0ce911e41e463c45911be031cdf6a096fe8 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:41 -0300 Subject: Bluetooth: Enable option to configure Max Transmission value via sockopt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the sockopt extension we can set a per-channel MaxTx value. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index ff1466b21580..f9e4da2677af 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -783,6 +783,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->omtu = l2cap_pi(parent)->omtu; pi->mode = l2cap_pi(parent)->mode; pi->fcs = l2cap_pi(parent)->fcs; + pi->max_tx = l2cap_pi(parent)->max_tx; pi->tx_win = l2cap_pi(parent)->tx_win; pi->sec_level = l2cap_pi(parent)->sec_level; pi->role_switch = l2cap_pi(parent)->role_switch; @@ -791,6 +792,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->imtu = L2CAP_DEFAULT_MTU; pi->omtu = 0; pi->mode = L2CAP_MODE_BASIC; + pi->max_tx = max_transmit; pi->fcs = L2CAP_FCS_CRC16; pi->tx_win = tx_window; pi->sec_level = BT_SECURITY_LOW; @@ -1785,6 +1787,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us opts.flush_to = l2cap_pi(sk)->flush_to; opts.mode = l2cap_pi(sk)->mode; opts.fcs = l2cap_pi(sk)->fcs; + opts.max_tx = l2cap_pi(sk)->max_tx; opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; len = min_t(unsigned int, sizeof(opts), optlen); @@ -1797,6 +1800,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us l2cap_pi(sk)->omtu = opts.omtu; l2cap_pi(sk)->mode = opts.mode; l2cap_pi(sk)->fcs = opts.fcs; + l2cap_pi(sk)->max_tx = opts.max_tx; l2cap_pi(sk)->tx_win = (__u8)opts.txwin_size; break; @@ -1912,6 +1916,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us opts.flush_to = l2cap_pi(sk)->flush_to; opts.mode = l2cap_pi(sk)->mode; opts.fcs = l2cap_pi(sk)->fcs; + opts.max_tx = l2cap_pi(sk)->max_tx; opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; len = min_t(unsigned int, len, sizeof(opts)); @@ -2331,7 +2336,7 @@ done: case L2CAP_MODE_ERTM: rfc.mode = L2CAP_MODE_ERTM; rfc.txwin_size = pi->tx_win; - rfc.max_transmit = max_transmit; + rfc.max_transmit = pi->max_tx; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); -- cgit v1.2.2 From f6e6b16823de0aff31cb8ee8c098383e3aceec58 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:41 -0300 Subject: Bluetooth: Fix bug when retransmitting I-frames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there is no frames to retransmit l2cap was crashing the kernel, now we check if the queue is empty first. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index f9e4da2677af..4c7b2d22faa5 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3546,7 +3546,8 @@ expected: if (pi->conn_state & L2CAP_CONN_REJ_ACT) pi->conn_state &= ~L2CAP_CONN_REJ_ACT; else { - sk->sk_send_head = TX_QUEUE(sk)->next; + if (!skb_queue_empty(TX_QUEUE(sk))) + sk->sk_send_head = TX_QUEUE(sk)->next; pi->next_tx_seq = pi->expected_ack_seq; l2cap_ertm_send(sk); } @@ -3593,7 +3594,8 @@ static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) if (pi->conn_state & L2CAP_CONN_REJ_ACT) pi->conn_state &= ~L2CAP_CONN_REJ_ACT; else { - sk->sk_send_head = TX_QUEUE(sk)->next; + if (!skb_queue_empty(TX_QUEUE(sk))) + sk->sk_send_head = TX_QUEUE(sk)->next; pi->next_tx_seq = pi->expected_ack_seq; l2cap_ertm_send(sk); } @@ -3625,12 +3627,14 @@ static inline void l2cap_data_channel_rejframe(struct sock *sk, u16 rx_control) if (pi->conn_state & L2CAP_CONN_REJ_ACT) pi->conn_state &= ~L2CAP_CONN_REJ_ACT; else { - sk->sk_send_head = TX_QUEUE(sk)->next; + if (!skb_queue_empty(TX_QUEUE(sk))) + sk->sk_send_head = TX_QUEUE(sk)->next; pi->next_tx_seq = pi->expected_ack_seq; l2cap_ertm_send(sk); } } else { - sk->sk_send_head = TX_QUEUE(sk)->next; + if (!skb_queue_empty(TX_QUEUE(sk))) + sk->sk_send_head = TX_QUEUE(sk)->next; pi->next_tx_seq = pi->expected_ack_seq; l2cap_ertm_send(sk); -- cgit v1.2.2 From 0ee0d20855ae9271de3f6695f4cafc08ab2533bb Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:41 -0300 Subject: Bluetooth: Fix crash when monitor timeout expires MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code was crashing due to a invalid access to hci_conn after the channel disconnect. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 4c7b2d22faa5..2f9bbad42887 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1568,6 +1568,9 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *m BT_DBG("sk %p len %d", sk, (int)len); + if (!conn) + return ERR_PTR(-ENOTCONN); + if (sdulen) hlen += 2; -- cgit v1.2.2 From 812e737e29a1d559e7bfbea675fdcfcbad9f5e1f Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:42 -0300 Subject: Bluetooth: Fix drop of acked packets on ERTM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit l2cap_drop_acked_frames() was droping not sent packets, causing them to be not transmitted. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 2f9bbad42887..042fd967e79c 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1262,7 +1262,8 @@ static void l2cap_drop_acked_frames(struct sock *sk) { struct sk_buff *skb; - while ((skb = skb_peek(TX_QUEUE(sk)))) { + while ((skb = skb_peek(TX_QUEUE(sk))) && + l2cap_pi(sk)->unacked_frames) { if (bt_cb(skb)->tx_seq == l2cap_pi(sk)->expected_ack_seq) break; -- cgit v1.2.2 From 3b1a9f3fa6ad842991538da2c3b2e29e047b131f Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:42 -0300 Subject: Bluetooth: Optimize SREJ_QUEUE append MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the I-frame received is the expected, i.e., its tx_seq is equal to expected_tx_seq and we are under a SREJ, we can just add it to the tail of the list. Doing that we change the complexity from O(n) to O(1). Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 042fd967e79c..187f46dd8309 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3542,7 +3542,9 @@ expected: pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { - l2cap_add_to_srej_queue(sk, skb, tx_seq, sar); + bt_cb(skb)->tx_seq = tx_seq; + bt_cb(skb)->sar = sar; + __skb_queue_tail(SREJ_QUEUE(sk), skb); return 0; } -- cgit v1.2.2 From 84fb0a6334af0ccad3544f6972c055d90fbb9fbe Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:42 -0300 Subject: Bluetooth: Add Kconfig option for L2CAP Extended Features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The L2CAP Extended Features are still unstable and under development, so we are adding them under the EXPERIMENTAL flag to get more feedback on them. L2CAP Extended Features includes the Enhanced Retransmission and Streaming Modes, Frame Check Sequence (FCS), and Segmentation and Reassemby (SAR). Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 187f46dd8309..4c007203d66b 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -55,7 +55,11 @@ #define VERSION "2.14" +#ifdef CONFIG_BT_L2CAP_EXT_FEATURES +static int enable_ertm = 1; +#else static int enable_ertm = 0; +#endif static int max_transmit = L2CAP_DEFAULT_MAX_TX; static int tx_window = L2CAP_DEFAULT_TX_WINDOW; -- cgit v1.2.2 From bd3c9e255e76ae232d417e3914ca5d80ca3e9485 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:42 -0300 Subject: Bluetooth: Add SOCK_STREAM support to L2CAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit if enable_ertm is true and we have SOCK_STREAM the default mode will be ERTM, otherwise Basic Mode. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 4c007203d66b..1a32562adf46 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -224,7 +224,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so l2cap_pi(sk)->conn = conn; - if (sk->sk_type == SOCK_SEQPACKET) { + if (sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) { /* Alloc CID for connection-oriented socket */ l2cap_pi(sk)->scid = l2cap_alloc_cid(l); } else if (sk->sk_type == SOCK_DGRAM) { @@ -452,7 +452,8 @@ static void l2cap_conn_start(struct l2cap_conn *conn) for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { bh_lock_sock(sk); - if (sk->sk_type != SOCK_SEQPACKET) { + if (sk->sk_type != SOCK_SEQPACKET && + sk->sk_type != SOCK_STREAM) { bh_unlock_sock(sk); continue; } @@ -512,7 +513,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { bh_lock_sock(sk); - if (sk->sk_type != SOCK_SEQPACKET) { + if (sk->sk_type != SOCK_SEQPACKET && + sk->sk_type != SOCK_STREAM) { l2cap_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); @@ -721,7 +723,8 @@ static void __l2cap_sock_close(struct sock *sk, int reason) case BT_CONNECTED: case BT_CONFIG: - if (sk->sk_type == SOCK_SEQPACKET) { + if (sk->sk_type == SOCK_SEQPACKET || + sk->sk_type == SOCK_STREAM) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; sk->sk_state = BT_DISCONN; @@ -732,7 +735,8 @@ static void __l2cap_sock_close(struct sock *sk, int reason) break; case BT_CONNECT2: - if (sk->sk_type == SOCK_SEQPACKET) { + if (sk->sk_type == SOCK_SEQPACKET || + sk->sk_type == SOCK_STREAM) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct l2cap_conn_rsp rsp; __u16 result; @@ -795,7 +799,10 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) } else { pi->imtu = L2CAP_DEFAULT_MTU; pi->omtu = 0; - pi->mode = L2CAP_MODE_BASIC; + if (enable_ertm && sk->sk_type == SOCK_STREAM) + pi->mode = L2CAP_MODE_ERTM; + else + pi->mode = L2CAP_MODE_BASIC; pi->max_tx = max_transmit; pi->fcs = L2CAP_FCS_CRC16; pi->tx_win = tx_window; @@ -852,7 +859,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, sock->state = SS_UNCONNECTED; - if (sock->type != SOCK_SEQPACKET && + if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; @@ -1000,7 +1007,8 @@ static int l2cap_do_connect(struct sock *sk) l2cap_sock_set_timer(sk, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { - if (sk->sk_type != SOCK_SEQPACKET) { + if (sk->sk_type != SOCK_SEQPACKET && + sk->sk_type != SOCK_STREAM) { l2cap_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; } else @@ -1034,7 +1042,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al lock_sock(sk); - if (sk->sk_type == SOCK_SEQPACKET && !la.l2_psm) { + if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) + && !la.l2_psm) { err = -EINVAL; goto done; } @@ -1098,7 +1107,8 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) lock_sock(sk); - if (sk->sk_state != BT_BOUND || sock->type != SOCK_SEQPACKET) { + if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) + || sk->sk_state != BT_BOUND) { err = -EBADFD; goto done; } @@ -1857,7 +1867,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_RAW) { + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM + && sk->sk_type != SOCK_RAW) { err = -EINVAL; break; } @@ -2007,7 +2018,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_RAW) { + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM + && sk->sk_type != SOCK_RAW) { err = -EINVAL; break; } @@ -2314,7 +2326,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 = { .mode = L2CAP_MODE_BASIC }; + struct l2cap_conf_rfc rfc = { .mode = pi->mode }; void *ptr = req->data; BT_DBG("sk %p", sk); @@ -3997,7 +4009,7 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) static inline void l2cap_check_encryption(struct sock *sk, u8 encrypt) { - if (sk->sk_type != SOCK_SEQPACKET) + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM) return; if (encrypt == 0x00) { -- cgit v1.2.2 From 0041ecfa3025d7612fdaab12b2f07c9c3c09f42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Sat, 1 May 2010 16:15:42 -0300 Subject: Bluetooth: Check if mode is supported on getsockopt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add this check to getsockopt makes possible to fail early instead of waiting until listen / connect. Signed-off-by: João Paulo Rechi Vita Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 1a32562adf46..bf5bb7dc6abf 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1814,9 +1814,22 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us break; } + l2cap_pi(sk)->mode = opts.mode; + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + break; + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + if (enable_ertm) + break; + /* fall through */ + default: + err = -EINVAL; + break; + } + l2cap_pi(sk)->imtu = opts.imtu; l2cap_pi(sk)->omtu = opts.omtu; - l2cap_pi(sk)->mode = opts.mode; l2cap_pi(sk)->fcs = opts.fcs; l2cap_pi(sk)->max_tx = opts.max_tx; l2cap_pi(sk)->tx_win = (__u8)opts.txwin_size; -- cgit v1.2.2 From afefdbc4cf3b9d409d07e1e5264e7ff88bc48711 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:43 -0300 Subject: Bluetooth: Fix SDU reassembly under SREJ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code was reusing the control var without its reinitialization. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index bf5bb7dc6abf..478def700c7c 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3446,14 +3446,14 @@ drop: static void l2cap_check_srej_gap(struct sock *sk, u8 tx_seq) { struct sk_buff *skb; - u16 control = 0; + u16 control; while((skb = skb_peek(SREJ_QUEUE(sk)))) { if (bt_cb(skb)->tx_seq != tx_seq) break; skb = skb_dequeue(SREJ_QUEUE(sk)); - control |= bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; + control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; l2cap_sar_reassembly_sdu(sk, skb, control); l2cap_pi(sk)->buffer_seq_srej = (l2cap_pi(sk)->buffer_seq_srej + 1) % 64; -- cgit v1.2.2 From 01760bdde9a92413b7fff928d08e19352bf09d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Sat, 1 May 2010 16:15:43 -0300 Subject: Bluetooth: Close L2CAP channel on invalid ReqSeq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Paulo Rechi Vita Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 478def700c7c..31514d8faa66 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3772,7 +3772,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk struct sock *sk; struct l2cap_pinfo *pi; u16 control, len; - u8 tx_seq; + u8 tx_seq, req_seq, next_tx_seq_offset, req_seq_offset; sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); if (!sk) { @@ -3823,6 +3823,22 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (l2cap_check_fcs(pi, skb)) goto drop; + req_seq = __get_reqseq(control); + req_seq_offset = (req_seq - pi->expected_ack_seq) % 64; + if (req_seq_offset < 0) + req_seq_offset += 64; + + next_tx_seq_offset = + (pi->next_tx_seq - pi->expected_ack_seq) % 64; + if (next_tx_seq_offset < 0) + next_tx_seq_offset += 64; + + /* check for invalid req-seq */ + if (req_seq_offset > next_tx_seq_offset) { + l2cap_send_disconn_req(pi->conn, sk); + goto drop; + } + if (__is_iframe(control)) { if (len < 4) goto drop; -- cgit v1.2.2 From 44651b85cc3a076147af5d181fc4833ef8debc59 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:43 -0300 Subject: Bluetooth: Don't set control bits to zero first MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can set the SAR bits in the control field directly. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 31514d8faa66..cfb18cd97564 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1636,16 +1636,15 @@ static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, siz __skb_queue_tail(&sar_queue, skb); len -= pi->remote_mps; size += pi->remote_mps; - control = 0; while (len > 0) { size_t buflen; if (len > pi->remote_mps) { - control |= L2CAP_SDU_CONTINUE; + control = L2CAP_SDU_CONTINUE; buflen = pi->remote_mps; } else { - control |= L2CAP_SDU_END; + control = L2CAP_SDU_END; buflen = len; } @@ -1658,7 +1657,6 @@ static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, siz __skb_queue_tail(&sar_queue, skb); len -= buflen; size += buflen; - control = 0; } skb_queue_splice_tail(&sar_queue, TX_QUEUE(sk)); if (sk->sk_send_head == NULL) -- cgit v1.2.2 From 59203a21a56c53afeb6f45e059299e6f1437f30f Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:43 -0300 Subject: Bluetooth: Fix errors reported by checkpatch.pl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index cfb18cd97564..2a981de071df 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1400,7 +1400,7 @@ static int l2cap_ertm_send(struct sock *sk) return 0; while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk)) && - !(pi->conn_state & L2CAP_CONN_REMOTE_BUSY)) { + !(pi->conn_state & L2CAP_CONN_REMOTE_BUSY)) { if (pi->remote_max_tx && bt_cb(skb)->retries == pi->remote_max_tx) { @@ -1490,9 +1490,8 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in struct sk_buff **frag; int err, sent = 0; - if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { + if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) return -EFAULT; - } sent += count; len -= count; @@ -3347,7 +3346,7 @@ static void l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_ if (skb_queue_is_last(SREJ_QUEUE(sk), next_skb)) break; - } while((next_skb = skb_queue_next(SREJ_QUEUE(sk), next_skb))); + } while ((next_skb = skb_queue_next(SREJ_QUEUE(sk), next_skb))); __skb_queue_tail(SREJ_QUEUE(sk), skb); } @@ -3446,7 +3445,7 @@ static void l2cap_check_srej_gap(struct sock *sk, u8 tx_seq) struct sk_buff *skb; u16 control; - while((skb = skb_peek(SREJ_QUEUE(sk)))) { + while ((skb = skb_peek(SREJ_QUEUE(sk)))) { if (bt_cb(skb)->tx_seq != tx_seq) break; @@ -3465,7 +3464,7 @@ static void l2cap_resend_srejframe(struct sock *sk, u8 tx_seq) struct srej_list *l, *tmp; u16 control; - list_for_each_entry_safe(l,tmp, SREJ_LIST(sk), list) { + list_for_each_entry_safe(l, tmp, SREJ_LIST(sk), list) { if (l->tx_seq == tx_seq) { list_del(&l->list); kfree(l); -- cgit v1.2.2 From 0301ef04b5f49a95681694fc0d75af9441faa919 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 5 May 2010 20:56:43 -0300 Subject: Bluetooth: Remove set of SrejSaveReqSeq under receipt of REJ frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That action is not specified by the ERTM spec, so removing it. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 2a981de071df..d0d03302e14d 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3670,10 +3670,8 @@ static inline void l2cap_data_channel_rejframe(struct sock *sk, u16 rx_control) pi->next_tx_seq = pi->expected_ack_seq; l2cap_ertm_send(sk); - if (pi->conn_state & L2CAP_CONN_WAIT_F) { - pi->srej_save_reqseq = tx_seq; + if (pi->conn_state & L2CAP_CONN_WAIT_F) pi->conn_state |= L2CAP_CONN_REJ_ACT; - } } } static inline void l2cap_data_channel_srejframe(struct sock *sk, u16 rx_control) -- cgit v1.2.2 From a2e12a2a312f816d5970b0c809d43b399fbfe90c Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 5 May 2010 19:58:27 -0300 Subject: Bluetooth: Remove unneeded control vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivial clean up. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index d0d03302e14d..5a5203f03642 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1239,7 +1239,6 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l static void l2cap_monitor_timeout(unsigned long arg) { struct sock *sk = (void *) arg; - u16 control; bh_lock_sock(sk); if (l2cap_pi(sk)->retry_count >= l2cap_pi(sk)->remote_max_tx) { @@ -1251,15 +1250,13 @@ static void l2cap_monitor_timeout(unsigned long arg) l2cap_pi(sk)->retry_count++; __mod_monitor_timer(); - control = L2CAP_CTRL_POLL; - l2cap_send_rr_or_rnr(l2cap_pi(sk), control); + l2cap_send_rr_or_rnr(l2cap_pi(sk), L2CAP_CTRL_POLL); bh_unlock_sock(sk); } static void l2cap_retrans_timeout(unsigned long arg) { struct sock *sk = (void *) arg; - u16 control; bh_lock_sock(sk); l2cap_pi(sk)->retry_count = 1; @@ -1267,8 +1264,7 @@ static void l2cap_retrans_timeout(unsigned long arg) l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F; - control = L2CAP_CTRL_POLL; - l2cap_send_rr_or_rnr(l2cap_pi(sk), control); + l2cap_send_rr_or_rnr(l2cap_pi(sk), L2CAP_CTRL_POLL); bh_unlock_sock(sk); } @@ -3716,10 +3712,8 @@ static inline void l2cap_data_channel_rnrframe(struct sock *sk, u16 rx_control) if (!(pi->conn_state & L2CAP_CONN_SREJ_SENT)) { del_timer(&pi->retrans_timer); - if (rx_control & L2CAP_CTRL_POLL) { - u16 control = L2CAP_CTRL_FINAL; - l2cap_send_rr_or_rnr(pi, control); - } + if (rx_control & L2CAP_CTRL_POLL) + l2cap_send_rr_or_rnr(pi, L2CAP_CTRL_FINAL); return; } -- cgit v1.2.2 From 9b16dc6551cbde65d0ac525af3c46efab53a2c46 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 5 May 2010 20:05:57 -0300 Subject: Bluetooth: Check if we really are in WAIT_F when F bit comes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F-bit set should be processed only if we are in the WAIT_F state. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 5a5203f03642..eb5cb29115a7 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3503,7 +3503,8 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); - if (L2CAP_CTRL_FINAL & rx_control) { + if (L2CAP_CTRL_FINAL & rx_control && + l2cap_pi(sk)->conn_state & L2CAP_CONN_WAIT_F) { del_timer(&pi->monitor_timer); if (pi->unacked_frames > 0) __mod_retrans_timer(); @@ -3727,7 +3728,8 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str { BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); - if (L2CAP_CTRL_FINAL & rx_control) { + if (L2CAP_CTRL_FINAL & rx_control && + l2cap_pi(sk)->conn_state & L2CAP_CONN_WAIT_F) { del_timer(&l2cap_pi(sk)->monitor_timer); if (l2cap_pi(sk)->unacked_frames > 0) __mod_retrans_timer(); -- cgit v1.2.2 From ff12fd643334071084b6145cad3793bb6c956638 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 5 May 2010 22:09:15 -0300 Subject: Bluetooth: Fix lockdep annotation on ERTM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A spin_lock_init() call was missing. :) Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index eb5cb29115a7..6b08f4d7c873 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1622,7 +1622,7 @@ static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, siz u16 control; size_t size = 0; - __skb_queue_head_init(&sar_queue); + skb_queue_head_init(&sar_queue); control = L2CAP_SDU_START; skb = l2cap_create_iframe_pdu(sk, msg, pi->remote_mps, control, len); if (IS_ERR(skb)) -- cgit v1.2.2 From 9a9c6a34416b3743c09c00f3d6708d9df3c21629 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:43 -0300 Subject: Bluetooth: Make hci_send_acl() void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hci_send_acl can't fail, so we can make it void. This patch changes that and all the funcions that use hci_send_acl(). That change exposed a bug on sending connectionless data. We were not reporting the lenght send back to the user space. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 73 +++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 46 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 6b08f4d7c873..7e74d5be16e3 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -330,19 +330,19 @@ static inline u8 l2cap_get_ident(struct l2cap_conn *conn) return id; } -static inline int l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) +static inline void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) { struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); BT_DBG("code 0x%2.2x", code); if (!skb) - return -ENOMEM; + return; - return hci_send_acl(conn->hcon, skb, 0); + hci_send_acl(conn->hcon, skb, 0); } -static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) +static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) { struct sk_buff *skb; struct l2cap_hdr *lh; @@ -369,7 +369,7 @@ static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) skb = bt_skb_alloc(count, GFP_ATOMIC); if (!skb) - return -ENOMEM; + return; lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE); @@ -381,10 +381,10 @@ static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) put_unaligned_le16(fcs, skb_put(skb, 2)); } - return hci_send_acl(pi->conn->hcon, skb, 0); + hci_send_acl(pi->conn->hcon, skb, 0); } -static inline int l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control) +static inline void l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control) { if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) control |= L2CAP_SUPER_RCV_NOT_READY; @@ -393,7 +393,7 @@ static inline int l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control) control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - return l2cap_send_sframe(pi, control); + l2cap_send_sframe(pi, control); } static void l2cap_do_start(struct sock *sk) @@ -1289,18 +1289,13 @@ static void l2cap_drop_acked_frames(struct sock *sk) return; } -static inline int l2cap_do_send(struct sock *sk, struct sk_buff *skb) +static inline void 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); - err = hci_send_acl(pi->conn->hcon, skb, 0); - if (err < 0) - kfree_skb(skb); - - return err; + hci_send_acl(pi->conn->hcon, skb, 0); } static int l2cap_streaming_send(struct sock *sk) @@ -1308,7 +1303,6 @@ static int l2cap_streaming_send(struct sock *sk) struct sk_buff *skb, *tx_skb; struct l2cap_pinfo *pi = l2cap_pi(sk); u16 control, fcs; - int err; while ((skb = sk->sk_send_head)) { tx_skb = skb_clone(skb, GFP_ATOMIC); @@ -1322,11 +1316,7 @@ static int l2cap_streaming_send(struct sock *sk) put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2); } - err = l2cap_do_send(sk, tx_skb); - if (err < 0) { - l2cap_send_disconn_req(pi->conn, sk); - return err; - } + l2cap_do_send(sk, tx_skb); pi->next_tx_seq = (pi->next_tx_seq + 1) % 64; @@ -1346,7 +1336,6 @@ static int l2cap_retransmit_frame(struct sock *sk, u8 tx_seq) struct l2cap_pinfo *pi = l2cap_pi(sk); struct sk_buff *skb, *tx_skb; u16 control, fcs; - int err; skb = skb_peek(TX_QUEUE(sk)); do { @@ -1375,11 +1364,7 @@ static int l2cap_retransmit_frame(struct sock *sk, u8 tx_seq) put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2); } - err = l2cap_do_send(sk, tx_skb); - if (err < 0) { - l2cap_send_disconn_req(pi->conn, sk); - return err; - } + l2cap_do_send(sk, tx_skb); break; } while(1); return 0; @@ -1390,7 +1375,7 @@ static int l2cap_ertm_send(struct sock *sk) struct sk_buff *skb, *tx_skb; struct l2cap_pinfo *pi = l2cap_pi(sk); u16 control, fcs; - int err, nsent = 0; + int nsent = 0; if (pi->conn_state & L2CAP_CONN_WAIT_F) return 0; @@ -1423,11 +1408,8 @@ static int l2cap_ertm_send(struct sock *sk) put_unaligned_le16(fcs, skb->data + tx_skb->len - 2); } - err = l2cap_do_send(sk, tx_skb); - if (err < 0) { - l2cap_send_disconn_req(pi->conn, sk); - return err; - } + l2cap_do_send(sk, tx_skb); + __mod_retrans_timer(); bt_cb(skb)->tx_seq = pi->next_tx_seq; @@ -1447,7 +1429,7 @@ static int l2cap_ertm_send(struct sock *sk) return nsent; } -static int l2cap_send_ack(struct l2cap_pinfo *pi) +static void l2cap_send_ack(struct l2cap_pinfo *pi) { struct sock *sk = (struct sock *)pi; u16 control = 0; @@ -1456,15 +1438,15 @@ static int l2cap_send_ack(struct l2cap_pinfo *pi) if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { control |= L2CAP_SUPER_RCV_NOT_READY; - return l2cap_send_sframe(pi, control); + l2cap_send_sframe(pi, control); + return; } else if (l2cap_ertm_send(sk) == 0) { control |= L2CAP_SUPER_RCV_READY; - return l2cap_send_sframe(pi, control); + l2cap_send_sframe(pi, control); } - return 0; } -static int l2cap_send_srejtail(struct sock *sk) +static void l2cap_send_srejtail(struct sock *sk) { struct srej_list *tail; u16 control; @@ -1476,8 +1458,6 @@ static int l2cap_send_srejtail(struct sock *sk) control |= tail->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; l2cap_send_sframe(l2cap_pi(sk), control); - - return 0; } static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, int len, int count, struct sk_buff *skb) @@ -1687,10 +1667,12 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms /* Connectionless channel */ if (sk->sk_type == SOCK_DGRAM) { skb = l2cap_create_connless_pdu(sk, msg, len); - if (IS_ERR(skb)) + if (IS_ERR(skb)) { err = PTR_ERR(skb); - else - err = l2cap_do_send(sk, skb); + } else { + l2cap_do_send(sk, skb); + err = len; + } goto done; } @@ -1709,9 +1691,8 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms goto done; } - err = l2cap_do_send(sk, skb); - if (!err) - err = len; + l2cap_do_send(sk, skb); + err = len; break; case L2CAP_MODE_ERTM: -- cgit v1.2.2 From f11d676da4059c7888efca810ab300b931736a26 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:44 -0300 Subject: Bluetooth: Refactor l2cap_retransmit_frame() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the code flow cleaner and changes the function to void. It also fixes a potential NULL dereference with skb. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 53 ++++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 7e74d5be16e3..1c35c328181d 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1331,43 +1331,44 @@ static int l2cap_streaming_send(struct sock *sk) return 0; } -static int l2cap_retransmit_frame(struct sock *sk, u8 tx_seq) +static void l2cap_retransmit_frame(struct sock *sk, u8 tx_seq) { struct l2cap_pinfo *pi = l2cap_pi(sk); struct sk_buff *skb, *tx_skb; u16 control, fcs; skb = skb_peek(TX_QUEUE(sk)); - do { - if (bt_cb(skb)->tx_seq != tx_seq) { - if (skb_queue_is_last(TX_QUEUE(sk), skb)) - break; - skb = skb_queue_next(TX_QUEUE(sk), skb); - continue; - } + if (!skb) + return; - if (pi->remote_max_tx && - bt_cb(skb)->retries == pi->remote_max_tx) { - l2cap_send_disconn_req(pi->conn, sk); + do { + if (bt_cb(skb)->tx_seq == tx_seq) break; - } - tx_skb = skb_clone(skb, GFP_ATOMIC); - bt_cb(skb)->retries++; - control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); - control |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) - | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); - put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + if (skb_queue_is_last(TX_QUEUE(sk), skb)) + return; - if (pi->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); - } + } while ((skb = skb_queue_next(TX_QUEUE(sk), skb))); - l2cap_do_send(sk, tx_skb); - break; - } while(1); - return 0; + if (pi->remote_max_tx && + bt_cb(skb)->retries == pi->remote_max_tx) { + l2cap_send_disconn_req(pi->conn, sk); + return; + } + + tx_skb = skb_clone(skb, GFP_ATOMIC); + bt_cb(skb)->retries++; + control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + control |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) + | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); + put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + + if (pi->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); + } + + l2cap_do_send(sk, tx_skb); } static int l2cap_ertm_send(struct sock *sk) -- cgit v1.2.2 From 18778a63ddc83bc89bda3b119fb02eb121512a66 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:44 -0300 Subject: Bluetooth: Implement missing parts of the Invalid Frame Detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a plenty of situation where ERTM shall close the channel, this commit treats the cases regarding Invalid Frame Detection. It create one reassembly SDU function for ERTM and other for Streaming Mode to make the Invalid Frame Detection handling less complex. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 112 insertions(+), 7 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 1c35c328181d..cfd672419315 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3329,12 +3329,111 @@ static void l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_ __skb_queue_tail(SREJ_QUEUE(sk), skb); } -static int l2cap_sar_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) +static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *_skb; + int err = 0; + + switch (control & L2CAP_CTRL_SAR) { + case L2CAP_SDU_UNSEGMENTED: + if (pi->conn_state & L2CAP_CONN_SAR_SDU) + goto drop; + + err = sock_queue_rcv_skb(sk, skb); + if (!err) + return err; + + break; + + case L2CAP_SDU_START: + if (pi->conn_state & L2CAP_CONN_SAR_SDU) + goto drop; + + pi->sdu_len = get_unaligned_le16(skb->data); + skb_pull(skb, 2); + + if (pi->sdu_len > pi->imtu) + goto disconnect; + + pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC); + if (!pi->sdu) { + err = -ENOMEM; + break; + } + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + + pi->conn_state |= L2CAP_CONN_SAR_SDU; + pi->partial_sdu_len = skb->len; + break; + + case L2CAP_SDU_CONTINUE: + if (!(pi->conn_state & L2CAP_CONN_SAR_SDU)) + goto disconnect; + + if (!pi->sdu) + goto disconnect; + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + + pi->partial_sdu_len += skb->len; + if (pi->partial_sdu_len > pi->sdu_len) + goto drop; + + break; + + case L2CAP_SDU_END: + if (!(pi->conn_state & L2CAP_CONN_SAR_SDU)) + goto disconnect; + + if (!pi->sdu) + goto disconnect; + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + + pi->conn_state &= ~L2CAP_CONN_SAR_SDU; + pi->partial_sdu_len += skb->len; + + if (pi->partial_sdu_len > pi->imtu) + goto drop; + + if (pi->partial_sdu_len != pi->sdu_len) + goto drop; + + _skb = skb_clone(pi->sdu, GFP_ATOMIC); + err = sock_queue_rcv_skb(sk, _skb); + if (err < 0) + kfree_skb(_skb); + + kfree_skb(pi->sdu); + break; + } + + kfree_skb(skb); + return err; + +drop: + kfree_skb(pi->sdu); + pi->sdu = NULL; + +disconnect: + l2cap_send_disconn_req(pi->conn, sk); + kfree_skb(skb); + return 0; +} + +static int l2cap_streaming_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) { struct l2cap_pinfo *pi = l2cap_pi(sk); struct sk_buff *_skb; int err = -EINVAL; + /* + * TODO: We have to notify the userland if some data is lost with the + * Streaming Mode. + */ + switch (control & L2CAP_CTRL_SAR) { case L2CAP_SDU_UNSEGMENTED: if (pi->conn_state & L2CAP_CONN_SAR_SDU) { @@ -3429,7 +3528,7 @@ static void l2cap_check_srej_gap(struct sock *sk, u8 tx_seq) skb = skb_dequeue(SREJ_QUEUE(sk)); control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; - l2cap_sar_reassembly_sdu(sk, skb, control); + l2cap_ertm_reassembly_sdu(sk, skb, control); l2cap_pi(sk)->buffer_seq_srej = (l2cap_pi(sk)->buffer_seq_srej + 1) % 64; tx_seq++; @@ -3566,7 +3665,7 @@ expected: pi->buffer_seq = (pi->buffer_seq + 1) % 64; - err = l2cap_sar_reassembly_sdu(sk, skb, rx_control); + err = l2cap_ertm_reassembly_sdu(sk, skb, rx_control); if (err < 0) return err; @@ -3790,8 +3889,10 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk * Receiver will miss it and start proper recovery * procedures and ask retransmission. */ - if (len > pi->mps) + if (len > pi->mps) { + l2cap_send_disconn_req(pi->conn, sk); goto drop; + } if (l2cap_check_fcs(pi, skb)) goto drop; @@ -3813,13 +3914,17 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk } if (__is_iframe(control)) { - if (len < 4) + if (len < 4) { + l2cap_send_disconn_req(pi->conn, sk); goto drop; + } l2cap_data_channel_iframe(sk, control, skb); } else { - if (len != 0) + if (len != 0) { + l2cap_send_disconn_req(pi->conn, sk); goto drop; + } l2cap_data_channel_sframe(sk, control, skb); } @@ -3850,7 +3955,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk else pi->expected_tx_seq = (tx_seq + 1) % 64; - l2cap_sar_reassembly_sdu(sk, skb, control); + l2cap_streaming_reassembly_sdu(sk, skb, control); goto done; -- cgit v1.2.2 From 9b53350d3cf5b330c3261d89b5e62a2dc25c5653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Sat, 1 May 2010 16:15:44 -0300 Subject: Bluetooth: Completes the I-frame tx_seq check logic on RECV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add checks for invalid tx_seq and fixes the duplicated tx_seq check. Signed-off-by: João Paulo Rechi Vita Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index cfd672419315..481cec22ef96 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3302,7 +3302,7 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) } } -static void l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_seq, u8 sar) +static int l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_seq, u8 sar) { struct sk_buff *next_skb; @@ -3312,13 +3312,16 @@ static void l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_ next_skb = skb_peek(SREJ_QUEUE(sk)); if (!next_skb) { __skb_queue_tail(SREJ_QUEUE(sk), skb); - return; + return 0; } do { + if (bt_cb(next_skb)->tx_seq == tx_seq) + return -EINVAL; + if (bt_cb(next_skb)->tx_seq > tx_seq) { __skb_queue_before(SREJ_QUEUE(sk), next_skb, skb); - return; + return 0; } if (skb_queue_is_last(SREJ_QUEUE(sk), next_skb)) @@ -3327,6 +3330,8 @@ static void l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_ } while ((next_skb = skb_queue_next(SREJ_QUEUE(sk), next_skb))); __skb_queue_tail(SREJ_QUEUE(sk), skb); + + return 0; } static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) @@ -3579,6 +3584,7 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str u8 tx_seq = __get_txseq(rx_control); u8 req_seq = __get_reqseq(rx_control); u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT; + u8 tx_seq_offset, expected_tx_seq_offset; int num_to_ack = (pi->tx_win/6) + 1; int err = 0; @@ -3598,6 +3604,16 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str if (tx_seq == pi->expected_tx_seq) goto expected; + tx_seq_offset = (tx_seq - pi->buffer_seq) % 64; + if (tx_seq_offset < 0) + tx_seq_offset += 64; + + /* invalid tx_seq */ + if (tx_seq_offset >= pi->tx_win) { + l2cap_send_disconn_req(pi->conn, sk); + goto drop; + } + if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { struct srej_list *first; @@ -3617,7 +3633,10 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str } } else { struct srej_list *l; - l2cap_add_to_srej_queue(sk, skb, tx_seq, sar); + + /* duplicated tx_seq */ + if (l2cap_add_to_srej_queue(sk, skb, tx_seq, sar) < 0) + goto drop; list_for_each_entry(l, SREJ_LIST(sk), list) { if (l->tx_seq == tx_seq) { @@ -3628,6 +3647,15 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str l2cap_send_srejframe(sk, tx_seq); } } else { + expected_tx_seq_offset = + (pi->expected_tx_seq - pi->buffer_seq) % 64; + if (expected_tx_seq_offset < 0) + expected_tx_seq_offset += 64; + + /* duplicated tx_seq */ + if (tx_seq_offset < expected_tx_seq_offset) + goto drop; + pi->conn_state |= L2CAP_CONN_SREJ_SENT; INIT_LIST_HEAD(SREJ_LIST(sk)); @@ -3676,6 +3704,10 @@ expected: l2cap_send_ack(pi); return 0; + +drop: + kfree_skb(skb); + return 0; } static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) -- cgit v1.2.2 From 1890d36bb556a27684ad29654a9898ab9a5f57ee Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:44 -0300 Subject: Bluetooth: Implement Local Busy Condition handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supports Local Busy condition handling through a waitqueue that wake ups each 200ms and try to push the packets to the upper layer. If it can push all the queue then it leaves the Local Busy state. The patch modifies the behaviour of l2cap_ertm_reassembly_sdu() to support retry of the push operation. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 187 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 166 insertions(+), 21 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 481cec22ef96..103e4b54a86a 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -68,10 +68,14 @@ static u8 l2cap_fixed_chan[8] = { 0x02, }; static const struct proto_ops l2cap_sock_ops; +static struct workqueue_struct *_busy_wq; + static struct bt_sock_list l2cap_sk_list = { .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock) }; +static void l2cap_busy_work(struct work_struct *work); + static void __l2cap_sock_close(struct sock *sk, int reason); static void l2cap_sock_close(struct sock *sk); static void l2cap_sock_kill(struct sock *sk); @@ -386,9 +390,10 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) static inline void l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control) { - if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) + if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { control |= L2CAP_SUPER_RCV_NOT_READY; - else + pi->conn_state |= L2CAP_CONN_RNR_SENT; + } else control |= L2CAP_SUPER_RCV_READY; control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; @@ -816,6 +821,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; skb_queue_head_init(TX_QUEUE(sk)); skb_queue_head_init(SREJ_QUEUE(sk)); + skb_queue_head_init(BUSY_QUEUE(sk)); INIT_LIST_HEAD(SREJ_LIST(sk)); } @@ -1439,6 +1445,7 @@ static void l2cap_send_ack(struct l2cap_pinfo *pi) if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { control |= L2CAP_SUPER_RCV_NOT_READY; + pi->conn_state |= L2CAP_CONN_RNR_SENT; l2cap_send_sframe(pi, control); return; } else if (l2cap_ertm_send(sk) == 0) { @@ -2279,6 +2286,9 @@ static inline void l2cap_ertm_init(struct sock *sk) l2cap_ack_timeout, (unsigned long) sk); __skb_queue_head_init(SREJ_QUEUE(sk)); + __skb_queue_head_init(BUSY_QUEUE(sk)); + + INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work); } static int l2cap_mode_supported(__u8 mode, __u32 feat_mask) @@ -3046,6 +3056,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { skb_queue_purge(SREJ_QUEUE(sk)); + skb_queue_purge(BUSY_QUEUE(sk)); del_timer(&l2cap_pi(sk)->retrans_timer); del_timer(&l2cap_pi(sk)->monitor_timer); del_timer(&l2cap_pi(sk)->ack_timer); @@ -3077,6 +3088,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { skb_queue_purge(SREJ_QUEUE(sk)); + skb_queue_purge(BUSY_QUEUE(sk)); del_timer(&l2cap_pi(sk)->retrans_timer); del_timer(&l2cap_pi(sk)->monitor_timer); del_timer(&l2cap_pi(sk)->ack_timer); @@ -3287,6 +3299,7 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { control |= L2CAP_SUPER_RCV_NOT_READY | L2CAP_CTRL_FINAL; l2cap_send_sframe(pi, control); + pi->conn_state |= L2CAP_CONN_RNR_SENT; pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; } @@ -3338,7 +3351,7 @@ static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 c { struct l2cap_pinfo *pi = l2cap_pi(sk); struct sk_buff *_skb; - int err = 0; + int err; switch (control & L2CAP_CTRL_SAR) { case L2CAP_SDU_UNSEGMENTED: @@ -3356,16 +3369,18 @@ static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 c goto drop; pi->sdu_len = get_unaligned_le16(skb->data); - skb_pull(skb, 2); if (pi->sdu_len > pi->imtu) goto disconnect; pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC); - if (!pi->sdu) { - err = -ENOMEM; - break; - } + if (!pi->sdu) + return -ENOMEM; + + /* pull sdu_len bytes only after alloc, because of Local Busy + * condition we have to be sure that this will be executed + * only once, i.e., when alloc does not fail */ + skb_pull(skb, 2); memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); @@ -3395,28 +3410,40 @@ static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 c if (!pi->sdu) goto disconnect; - memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + if (!(pi->conn_state & L2CAP_CONN_SAR_RETRY)) { + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); - pi->conn_state &= ~L2CAP_CONN_SAR_SDU; - pi->partial_sdu_len += skb->len; + pi->partial_sdu_len += skb->len; - if (pi->partial_sdu_len > pi->imtu) - goto drop; + if (pi->partial_sdu_len > pi->imtu) + goto drop; - if (pi->partial_sdu_len != pi->sdu_len) - goto drop; + if (pi->partial_sdu_len != pi->sdu_len) + goto drop; + } _skb = skb_clone(pi->sdu, GFP_ATOMIC); + if (!_skb) { + pi->conn_state |= L2CAP_CONN_SAR_RETRY; + return -ENOMEM; + } + err = sock_queue_rcv_skb(sk, _skb); - if (err < 0) + if (err < 0) { kfree_skb(_skb); + pi->conn_state |= L2CAP_CONN_SAR_RETRY; + return err; + } + + pi->conn_state &= ~L2CAP_CONN_SAR_RETRY; + pi->conn_state &= ~L2CAP_CONN_SAR_SDU; kfree_skb(pi->sdu); break; } kfree_skb(skb); - return err; + return 0; drop: kfree_skb(pi->sdu); @@ -3428,6 +3455,115 @@ disconnect: return 0; } +static void l2cap_busy_work(struct work_struct *work) +{ + DECLARE_WAITQUEUE(wait, current); + struct l2cap_pinfo *pi = + container_of(work, struct l2cap_pinfo, busy_work); + struct sock *sk = (struct sock *)pi; + int n_tries = 0, timeo = HZ/5, err; + struct sk_buff *skb; + u16 control; + + lock_sock(sk); + + add_wait_queue(sk->sk_sleep, &wait); + while ((skb = skb_peek(BUSY_QUEUE(sk)))) { + set_current_state(TASK_INTERRUPTIBLE); + + if (n_tries++ > L2CAP_LOCAL_BUSY_TRIES) { + err = -EBUSY; + l2cap_send_disconn_req(pi->conn, sk); + goto done; + } + + if (!timeo) + timeo = HZ/5; + + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + goto done; + } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + + err = sock_error(sk); + if (err) + goto done; + + while ((skb = skb_dequeue(BUSY_QUEUE(sk)))) { + control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; + err = l2cap_ertm_reassembly_sdu(sk, skb, control); + if (err < 0) { + skb_queue_head(BUSY_QUEUE(sk), skb); + break; + } + + pi->buffer_seq = (pi->buffer_seq + 1) % 64; + } + + if (!skb) + break; + } + + if (!(pi->conn_state & L2CAP_CONN_RNR_SENT)) + goto done; + + control = pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= L2CAP_SUPER_RCV_READY | L2CAP_CTRL_POLL; + l2cap_send_sframe(pi, control); + l2cap_pi(sk)->retry_count = 1; + + del_timer(&pi->retrans_timer); + __mod_monitor_timer(); + + l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F; + +done: + pi->conn_state &= ~L2CAP_CONN_LOCAL_BUSY; + pi->conn_state &= ~L2CAP_CONN_RNR_SENT; + + set_current_state(TASK_RUNNING); + remove_wait_queue(sk->sk_sleep, &wait); + + release_sock(sk); +} + +static int l2cap_push_rx_skb(struct sock *sk, struct sk_buff *skb, u16 control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + int sctrl, err; + + if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; + __skb_queue_tail(BUSY_QUEUE(sk), skb); + return -EBUSY; + } + + err = l2cap_ertm_reassembly_sdu(sk, skb, control); + if (err >= 0) { + pi->buffer_seq = (pi->buffer_seq + 1) % 64; + return err; + } + + /* Busy Condition */ + pi->conn_state |= L2CAP_CONN_LOCAL_BUSY; + bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; + __skb_queue_tail(BUSY_QUEUE(sk), skb); + + sctrl = pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + sctrl |= L2CAP_SUPER_RCV_NOT_READY; + l2cap_send_sframe(pi, sctrl); + + pi->conn_state |= L2CAP_CONN_RNR_SENT; + + queue_work(_busy_wq, &pi->busy_work); + + return err; +} + static int l2cap_streaming_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) { struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -3614,6 +3750,9 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str goto drop; } + if (pi->conn_state == L2CAP_CONN_LOCAL_BUSY) + goto drop; + if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { struct srej_list *first; @@ -3662,6 +3801,7 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str pi->buffer_seq_srej = pi->buffer_seq; __skb_queue_head_init(SREJ_QUEUE(sk)); + __skb_queue_head_init(BUSY_QUEUE(sk)); l2cap_add_to_srej_queue(sk, skb, tx_seq, sar); pi->conn_state |= L2CAP_CONN_SEND_PBIT; @@ -3691,11 +3831,9 @@ expected: } } - pi->buffer_seq = (pi->buffer_seq + 1) % 64; - - err = l2cap_ertm_reassembly_sdu(sk, skb, rx_control); + err = l2cap_push_rx_skb(sk, skb, rx_control); if (err < 0) - return err; + return 0; __mod_ack_timer(); @@ -4406,6 +4544,10 @@ static int __init l2cap_init(void) if (err < 0) return err; + _busy_wq = create_singlethread_workqueue("l2cap"); + if (!_busy_wq) + goto error; + err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops); if (err < 0) { BT_ERR("L2CAP socket registration failed"); @@ -4440,6 +4582,9 @@ static void __exit l2cap_exit(void) { debugfs_remove(l2cap_debugfs); + flush_workqueue(_busy_wq); + destroy_workqueue(_busy_wq); + if (bt_sock_unregister(BTPROTO_L2CAP) < 0) BT_ERR("L2CAP socket unregistration failed"); -- cgit v1.2.2 From 6161c0382bbab883a634d284f7367a88bbe88534 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:44 -0300 Subject: Bluetooth: Add wait_queue to wait ack of all sent packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To guarantee that all packets we sent were received we need to wait for theirs ack before shutdown the socket. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 103e4b54a86a..9d514f9dbc0f 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1242,6 +1242,37 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l return 0; } +static int __l2cap_wait_ack(struct sock *sk) +{ + DECLARE_WAITQUEUE(wait, current); + int err = 0; + int timeo = HZ/5; + + add_wait_queue(sk->sk_sleep, &wait); + while ((l2cap_pi(sk)->unacked_frames > 0 && l2cap_pi(sk)->conn)) { + set_current_state(TASK_INTERRUPTIBLE); + + if (!timeo) + timeo = HZ/5; + + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + break; + } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + + err = sock_error(sk); + if (err) + break; + } + set_current_state(TASK_RUNNING); + remove_wait_queue(sk->sk_sleep, &wait); + return err; +} + static void l2cap_monitor_timeout(unsigned long arg) { struct sock *sk = (void *) arg; @@ -2059,6 +2090,9 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) lock_sock(sk); if (!sk->sk_shutdown) { + if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) + err = __l2cap_wait_ack(sk); + sk->sk_shutdown = SHUTDOWN_MASK; l2cap_sock_clear_timer(sk); __l2cap_sock_close(sk, 0); -- cgit v1.2.2 From dfc909befbfe967bd7f46ef33b6969c1b7f3cf42 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:45 -0300 Subject: Bluetooth: Fix race condition on l2cap_ertm_send() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit l2cap_ertm_send() can be called both from user context and bottom half context. The socket locks for that contexts are different, the user context uses a mutex(which can sleep) and the second one uses a spinlock_bh. That creates a race condition when we have interruptions on both contexts at the same time. The better way to solve this is to add a new spinlock to lock l2cap_ertm_send() and the vars it access. The other solution was to defer l2cap_ertm_send() with a workqueue, but we the sending process already has one defer on the hci layer. It's not a good idea add another one. The patch refactor the code to create l2cap_retransmit_frames(), then we encapulate the lock of l2cap_ertm_send() for some call. It also changes l2cap_retransmit_frame() to l2cap_retransmit_one_frame() to avoid confusion Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 99 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 33 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 9d514f9dbc0f..fe663e9c6684 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1368,7 +1368,7 @@ static int l2cap_streaming_send(struct sock *sk) return 0; } -static void l2cap_retransmit_frame(struct sock *sk, u8 tx_seq) +static void l2cap_retransmit_one_frame(struct sock *sk, u8 tx_seq) { struct l2cap_pinfo *pi = l2cap_pi(sk); struct sk_buff *skb, *tx_skb; @@ -1467,10 +1467,29 @@ static int l2cap_ertm_send(struct sock *sk) return nsent; } +static int l2cap_retransmit_frames(struct sock *sk) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + int ret; + + spin_lock_bh(&pi->send_lock); + + if (!skb_queue_empty(TX_QUEUE(sk))) + sk->sk_send_head = TX_QUEUE(sk)->next; + + pi->next_tx_seq = pi->expected_ack_seq; + ret = l2cap_ertm_send(sk); + + spin_unlock_bh(&pi->send_lock); + + return ret; +} + static void l2cap_send_ack(struct l2cap_pinfo *pi) { struct sock *sk = (struct sock *)pi; u16 control = 0; + int nframes; control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; @@ -1479,10 +1498,17 @@ static void l2cap_send_ack(struct l2cap_pinfo *pi) pi->conn_state |= L2CAP_CONN_RNR_SENT; l2cap_send_sframe(pi, control); return; - } else if (l2cap_ertm_send(sk) == 0) { - control |= L2CAP_SUPER_RCV_READY; - l2cap_send_sframe(pi, control); } + + spin_lock_bh(&pi->send_lock); + nframes = l2cap_ertm_send(sk); + spin_unlock_bh(&pi->send_lock); + + if (nframes > 0) + return; + + control |= L2CAP_SUPER_RCV_READY; + l2cap_send_sframe(pi, control); } static void l2cap_send_srejtail(struct sock *sk) @@ -1673,8 +1699,10 @@ static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, siz size += buflen; } skb_queue_splice_tail(&sar_queue, TX_QUEUE(sk)); + spin_lock_bh(&pi->send_lock); if (sk->sk_send_head == NULL) sk->sk_send_head = sar_queue.next; + spin_unlock_bh(&pi->send_lock); return size; } @@ -1745,8 +1773,15 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms goto done; } __skb_queue_tail(TX_QUEUE(sk), skb); + + if (pi->mode == L2CAP_MODE_ERTM) + spin_lock_bh(&pi->send_lock); + if (sk->sk_send_head == NULL) sk->sk_send_head = skb; + + if (pi->mode == L2CAP_MODE_ERTM) + spin_unlock_bh(&pi->send_lock); } else { /* Segment SDU into multiples PDUs */ err = l2cap_sar_segment_sdu(sk, msg, len); @@ -1754,10 +1789,13 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms goto done; } - if (pi->mode == L2CAP_MODE_STREAMING) + if (pi->mode == L2CAP_MODE_STREAMING) { err = l2cap_streaming_send(sk); - else + } else { + spin_lock_bh(&pi->send_lock); err = l2cap_ertm_send(sk); + spin_unlock_bh(&pi->send_lock); + } if (err >= 0) err = len; @@ -2321,6 +2359,7 @@ static inline void l2cap_ertm_init(struct sock *sk) __skb_queue_head_init(SREJ_QUEUE(sk)); __skb_queue_head_init(BUSY_QUEUE(sk)); + spin_lock_init(&l2cap_pi(sk)->send_lock); INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work); } @@ -3340,7 +3379,9 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY && pi->unacked_frames > 0) __mod_retrans_timer(); + spin_lock_bh(&pi->send_lock); l2cap_ertm_send(sk); + spin_unlock_bh(&pi->send_lock); if (!(pi->conn_state & L2CAP_CONN_LOCAL_BUSY) && pi->frames_sent == 0) { @@ -3857,12 +3898,8 @@ expected: if (rx_control & L2CAP_CTRL_FINAL) { if (pi->conn_state & L2CAP_CONN_REJ_ACT) pi->conn_state &= ~L2CAP_CONN_REJ_ACT; - else { - if (!skb_queue_empty(TX_QUEUE(sk))) - sk->sk_send_head = TX_QUEUE(sk)->next; - pi->next_tx_seq = pi->expected_ack_seq; - l2cap_ertm_send(sk); - } + else + l2cap_retransmit_frames(sk); } err = l2cap_push_rx_skb(sk, skb, rx_control); @@ -3907,12 +3944,8 @@ static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) if (pi->conn_state & L2CAP_CONN_REJ_ACT) pi->conn_state &= ~L2CAP_CONN_REJ_ACT; - else { - if (!skb_queue_empty(TX_QUEUE(sk))) - sk->sk_send_head = TX_QUEUE(sk)->next; - pi->next_tx_seq = pi->expected_ack_seq; - l2cap_ertm_send(sk); - } + else + l2cap_retransmit_frames(sk); } else { if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && @@ -3920,10 +3953,13 @@ static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) __mod_retrans_timer(); pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - if (pi->conn_state & L2CAP_CONN_SREJ_SENT) + if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { l2cap_send_ack(pi); - else + } else { + spin_lock_bh(&pi->send_lock); l2cap_ertm_send(sk); + spin_unlock_bh(&pi->send_lock); + } } } @@ -3940,17 +3976,10 @@ static inline void l2cap_data_channel_rejframe(struct sock *sk, u16 rx_control) if (rx_control & L2CAP_CTRL_FINAL) { if (pi->conn_state & L2CAP_CONN_REJ_ACT) pi->conn_state &= ~L2CAP_CONN_REJ_ACT; - else { - if (!skb_queue_empty(TX_QUEUE(sk))) - sk->sk_send_head = TX_QUEUE(sk)->next; - pi->next_tx_seq = pi->expected_ack_seq; - l2cap_ertm_send(sk); - } + else + l2cap_retransmit_frames(sk); } else { - if (!skb_queue_empty(TX_QUEUE(sk))) - sk->sk_send_head = TX_QUEUE(sk)->next; - pi->next_tx_seq = pi->expected_ack_seq; - l2cap_ertm_send(sk); + l2cap_retransmit_frames(sk); if (pi->conn_state & L2CAP_CONN_WAIT_F) pi->conn_state |= L2CAP_CONN_REJ_ACT; @@ -3966,8 +3995,12 @@ static inline void l2cap_data_channel_srejframe(struct sock *sk, u16 rx_control) if (rx_control & L2CAP_CTRL_POLL) { pi->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(sk); - l2cap_retransmit_frame(sk, tx_seq); + l2cap_retransmit_one_frame(sk, tx_seq); + + spin_lock_bh(&pi->send_lock); l2cap_ertm_send(sk); + spin_unlock_bh(&pi->send_lock); + if (pi->conn_state & L2CAP_CONN_WAIT_F) { pi->srej_save_reqseq = tx_seq; pi->conn_state |= L2CAP_CONN_SREJ_ACT; @@ -3977,9 +4010,9 @@ static inline void l2cap_data_channel_srejframe(struct sock *sk, u16 rx_control) pi->srej_save_reqseq == tx_seq) pi->conn_state &= ~L2CAP_CONN_SREJ_ACT; else - l2cap_retransmit_frame(sk, tx_seq); + l2cap_retransmit_one_frame(sk, tx_seq); } else { - l2cap_retransmit_frame(sk, tx_seq); + l2cap_retransmit_one_frame(sk, tx_seq); if (pi->conn_state & L2CAP_CONN_WAIT_F) { pi->srej_save_reqseq = tx_seq; pi->conn_state |= L2CAP_CONN_SREJ_ACT; -- cgit v1.2.2 From 4178ba462a3e8ab5094e69606f01d9e95f2d5ea6 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 1 May 2010 16:15:45 -0300 Subject: Bluetooth: Prevents buffer overflow on l2cap_ertm_reassembly_sdu() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The checks should be done before the the memcpy to avoid buffer overflow. Reported-by: João Paulo Rechi Vita Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index fe663e9c6684..9ef01c32b3a2 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3470,12 +3470,12 @@ static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 c if (!pi->sdu) goto disconnect; - memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); - pi->partial_sdu_len += skb->len; if (pi->partial_sdu_len > pi->sdu_len) goto drop; + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + break; case L2CAP_SDU_END: @@ -3486,8 +3486,6 @@ static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 c goto disconnect; if (!(pi->conn_state & L2CAP_CONN_SAR_RETRY)) { - memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); - pi->partial_sdu_len += skb->len; if (pi->partial_sdu_len > pi->imtu) @@ -3495,6 +3493,8 @@ static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 c if (pi->partial_sdu_len != pi->sdu_len) goto drop; + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); } _skb = skb_clone(pi->sdu, GFP_ATOMIC); -- cgit v1.2.2 From 844c0972427ee5f661158160aaca10b22b3dda60 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 4 May 2010 23:16:01 -0300 Subject: Bluetooth: Fix spec error in the RemoteBusy Logic On the receipt of an RR(P=1) under RemoteBusy set to TRUE(on the RECV state table) we have to call sendIorRRorRNR(F=1) and just after set RemoteBusy to False. This leads to a freeze in the sending process since it's not allowed send data with RemoteBusy set to true and no one call SendPending-I-Frames after set RemoteBusy to false(The last action for that event). Actually sendIorRRorRNR() calls SendPending-I-Frames but at that moment RemoteBusy is still True and we cannot send any frame, after, no one calls SendPending-I-Frames again and the sending process stops. The solution here is to set RemoteBusy to false inside SendPending-I-Frames just before call SendPending-I-Frames. That will make SendPending-I-Frames able to send frames. This solution is similar to what RR(P=0)(F=0) on the RECV table and RR(P=1) on the SREJ_SENT table do. Actually doesn't make any sense call SendPending-I-Frames if we can send any frame, i. e., RemoteBusy is True. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 9ef01c32b3a2..ba49f9a3579e 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3379,6 +3379,8 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY && pi->unacked_frames > 0) __mod_retrans_timer(); + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + spin_lock_bh(&pi->send_lock); l2cap_ertm_send(sk); spin_unlock_bh(&pi->send_lock); @@ -3936,7 +3938,6 @@ static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) l2cap_send_srejtail(sk); } else { l2cap_send_i_or_rr_or_rnr(sk); - pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; } } else if (rx_control & L2CAP_CTRL_FINAL) { -- cgit v1.2.2 From 2b0b05ddc04b6d45e71cd36405df512075786f1e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 10 May 2010 11:33:10 +0200 Subject: Bluetooth: Fix issues where sk_sleep() helper is needed now There were some left-overs that used sk->sk_sleep instead of the new sk_sleep() helper. Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index ba49f9a3579e..673a36886716 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1248,7 +1248,7 @@ static int __l2cap_wait_ack(struct sock *sk) int err = 0; int timeo = HZ/5; - add_wait_queue(sk->sk_sleep, &wait); + add_wait_queue(sk_sleep(sk), &wait); while ((l2cap_pi(sk)->unacked_frames > 0 && l2cap_pi(sk)->conn)) { set_current_state(TASK_INTERRUPTIBLE); @@ -1269,7 +1269,7 @@ static int __l2cap_wait_ack(struct sock *sk) break; } set_current_state(TASK_RUNNING); - remove_wait_queue(sk->sk_sleep, &wait); + remove_wait_queue(sk_sleep(sk), &wait); return err; } @@ -3544,7 +3544,7 @@ static void l2cap_busy_work(struct work_struct *work) lock_sock(sk); - add_wait_queue(sk->sk_sleep, &wait); + add_wait_queue(sk_sleep(sk), &wait); while ((skb = skb_peek(BUSY_QUEUE(sk)))) { set_current_state(TASK_INTERRUPTIBLE); @@ -3603,7 +3603,7 @@ done: pi->conn_state &= ~L2CAP_CONN_RNR_SENT; set_current_state(TASK_RUNNING); - remove_wait_queue(sk->sk_sleep, &wait); + remove_wait_queue(sk_sleep(sk), &wait); release_sock(sk); } -- cgit v1.2.2 From 3fa21e07e6acefa31f974d57fba2b6920a7ebd1a Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 17 May 2010 23:08:21 -0700 Subject: net: Remove unnecessary returns from void function()s This patch removes from net/ (but not any netfilter files) all the unnecessary return; statements that precede the last closing brace of void functions. It does not remove the returns that are immediately preceded by a label as gcc doesn't like that. Done via: $ grep -rP --include=*.[ch] -l "return;\n}" net/ | \ xargs perl -i -e 'local $/ ; while (<>) { s/\n[ \t\n]+return;\n}/\n}/g; print; }' Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- net/bluetooth/l2cap.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 673a36886716..1b682a5aa061 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1322,8 +1322,6 @@ static void l2cap_drop_acked_frames(struct sock *sk) if (!l2cap_pi(sk)->unacked_frames) del_timer(&l2cap_pi(sk)->retrans_timer); - - return; } static inline void l2cap_do_send(struct sock *sk, struct sk_buff *skb) @@ -4667,7 +4665,6 @@ void l2cap_load(void) /* Dummy function to trigger automatic L2CAP module loading by * other modules that use L2CAP sockets but don't use any other * symbols from it. */ - return; } EXPORT_SYMBOL(l2cap_load); -- cgit v1.2.2 From e501d0553a7580fcc6654d7f58a5f061d31d00af Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 8 Jul 2010 12:14:41 +0300 Subject: Bluetooth: Check L2CAP pending status before sending connect request Due to race condition in L2CAP state machine L2CAP Connection Request may be sent twice for SDP with the same source channel id. Problems reported connecting to Apple products, some carkit, Blackberry phones. ... 2010-06-07 21:18:03.651031 < ACL data: handle 1 flags 0x02 dlen 12 L2CAP(s): Connect req: psm 1 scid 0x0040 2010-06-07 21:18:03.653473 > HCI Event: Number of Completed Packets (0x13) plen 5 handle 1 packets 1 2010-06-07 21:18:03.653808 > HCI Event: Auth Complete (0x06) plen 3 status 0x00 handle 1 2010-06-07 21:18:03.653869 < ACL data: handle 1 flags 0x02 dlen 12 L2CAP(s): Connect req: psm 1 scid 0x0040 ... Patch uses L2CAP_CONF_CONNECT_PEND flag to mark that L2CAP Connection Request has been sent already. Modified version of patch from Ville Tervo. Signed-off-by: Andrei Emeltchenko Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 1b682a5aa061..cf3c4073a8a6 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -401,6 +401,11 @@ static inline void l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control) l2cap_send_sframe(pi, control); } +static inline int __l2cap_no_conn_pending(struct sock *sk) +{ + return !(l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND); +} + static void l2cap_do_start(struct sock *sk) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; @@ -409,12 +414,13 @@ static void l2cap_do_start(struct sock *sk) if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)) return; - if (l2cap_check_security(sk)) { + if (l2cap_check_security(sk) && __l2cap_no_conn_pending(sk)) { struct l2cap_conn_req req; req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; l2cap_pi(sk)->ident = l2cap_get_ident(conn); + l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_REQ, sizeof(req), &req); @@ -464,12 +470,14 @@ static void l2cap_conn_start(struct l2cap_conn *conn) } if (sk->sk_state == BT_CONNECT) { - if (l2cap_check_security(sk)) { + if (l2cap_check_security(sk) && + __l2cap_no_conn_pending(sk)) { struct l2cap_conn_req req; req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; l2cap_pi(sk)->ident = l2cap_get_ident(conn); + l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_REQ, sizeof(req), &req); @@ -2912,7 +2920,6 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd l2cap_pi(sk)->ident = 0; l2cap_pi(sk)->dcid = dcid; l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; - l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_CONNECT_PEND; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, @@ -4404,6 +4411,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) req.psm = l2cap_pi(sk)->psm; l2cap_pi(sk)->ident = l2cap_get_ident(conn); + l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_REQ, sizeof(req), &req); -- cgit v1.2.2 From fd059b9bd0af2e217f29d5a34e1c71039d26af8c Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 10 May 2010 14:22:56 -0300 Subject: Bluetooth: Remove max_tx and tx_window module paramenters from L2CAP We don't need these parameters anymore since we have socket options for them. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index cf3c4073a8a6..fd507cbc7932 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -60,8 +60,6 @@ static int enable_ertm = 1; #else static int enable_ertm = 0; #endif -static int max_transmit = L2CAP_DEFAULT_MAX_TX; -static int tx_window = L2CAP_DEFAULT_TX_WINDOW; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; static u8 l2cap_fixed_chan[8] = { 0x02, }; @@ -816,9 +814,9 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->mode = L2CAP_MODE_ERTM; else pi->mode = L2CAP_MODE_BASIC; - pi->max_tx = max_transmit; + pi->max_tx = L2CAP_DEFAULT_MAX_TX; pi->fcs = L2CAP_FCS_CRC16; - pi->tx_win = tx_window; + pi->tx_win = L2CAP_DEFAULT_TX_WINDOW; pi->sec_level = BT_SECURITY_LOW; pi->role_switch = 0; pi->force_reliable = 0; @@ -4682,12 +4680,6 @@ module_exit(l2cap_exit); module_param(enable_ertm, bool, 0644); MODULE_PARM_DESC(enable_ertm, "Enable enhanced retransmission mode"); -module_param(max_transmit, uint, 0644); -MODULE_PARM_DESC(max_transmit, "Max transmit value (default = 3)"); - -module_param(tx_window, uint, 0644); -MODULE_PARM_DESC(tx_window, "Transmission window size value (default = 63)"); - MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION); MODULE_VERSION(VERSION); -- cgit v1.2.2 From 0b31c85ce78d3646ac1e90d62969e7cd8cfd8b15 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 10 May 2010 14:26:11 -0300 Subject: Bluetooth: Remove L2CAP Extended Features from Kconfig This reverts commit 84fb0a6334af0ccad3544f6972c055d90fbb9fbe which adds the L2CAP Extended Features to the Kconfig, that is actually not needed. One can use other mechanisms to enable L2CAP Extended Features. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index fd507cbc7932..6c5462b2a244 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -55,11 +55,7 @@ #define VERSION "2.14" -#ifdef CONFIG_BT_L2CAP_EXT_FEATURES -static int enable_ertm = 1; -#else static int enable_ertm = 0; -#endif static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; static u8 l2cap_fixed_chan[8] = { 0x02, }; -- cgit v1.2.2 From f6337c771126420c348b702e012262cfb5f0d56e Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 10 May 2010 18:32:04 -0300 Subject: Bluetooth: Fix drop of packets with invalid req_seq/tx_seq We shall not use an unsigned var since we are expecting negatives value there. Using unsigned causes ERTM connection to close due to invalid ReqSeq numbers. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 6c5462b2a244..7e39d9ee0b9c 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3796,7 +3796,7 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str u8 tx_seq = __get_txseq(rx_control); u8 req_seq = __get_reqseq(rx_control); u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT; - u8 tx_seq_offset, expected_tx_seq_offset; + int tx_seq_offset, expected_tx_seq_offset; int num_to_ack = (pi->tx_win/6) + 1; int err = 0; @@ -4081,7 +4081,8 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk struct sock *sk; struct l2cap_pinfo *pi; u16 control, len; - u8 tx_seq, req_seq, next_tx_seq_offset, req_seq_offset; + u8 tx_seq, req_seq; + int next_tx_seq_offset, req_seq_offset; sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); if (!sk) { -- cgit v1.2.2 From 8ff50ec04a7ecdbba6e0a5423cf6f2c5f7fc605e Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 10 May 2010 19:34:11 -0300 Subject: Bluetooth: Fix bug with ERTM vars increment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All ERTM operations regarding the txWindow should be modulo 64, otherwise we confuse the ERTM logic and connections will break. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 7e39d9ee0b9c..a9fdfe401f5b 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3748,7 +3748,7 @@ static void l2cap_check_srej_gap(struct sock *sk, u8 tx_seq) l2cap_ertm_reassembly_sdu(sk, skb, control); l2cap_pi(sk)->buffer_seq_srej = (l2cap_pi(sk)->buffer_seq_srej + 1) % 64; - tx_seq++; + tx_seq = (tx_seq + 1) % 64; } } @@ -3784,10 +3784,11 @@ static void l2cap_send_srejframe(struct sock *sk, u8 tx_seq) l2cap_send_sframe(pi, control); new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); - new->tx_seq = pi->expected_tx_seq++; + new->tx_seq = pi->expected_tx_seq; + pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; list_add_tail(&new->list, SREJ_LIST(sk)); } - pi->expected_tx_seq++; + pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; } static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) -- cgit v1.2.2 From bc1b1f8bee63966649dd5ac7d10d31a6556bf19b Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 11 May 2010 22:14:00 -0300 Subject: Bluetooth: Only check SAR bits if frame is an I-frame The SAR bits doesn't make sense for an S-frame. It doesn't use SAR. Checking SAR for a S-frames can lead to L2CAP errors, it could close the channel with an invalid packet length, since we was removing the 2 of the of any frame that match SAR start bits, without check if it is an I-frame. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index a9fdfe401f5b..108c2f290ac5 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -4117,7 +4117,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk skb_pull(skb, 2); len = skb->len; - if (__is_sar_start(control)) + if (__is_sar_start(control) && __is_iframe(control)) len -= 2; if (pi->fcs == L2CAP_FCS_CRC16) -- cgit v1.2.2 From 6e2b6722abaa3f6042357e11f465488b7c12f94c Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 1 Jun 2010 18:52:58 -0300 Subject: Bluetooth: Fix bug in l2cap_ertm_send() behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch makes l2cap_ertm_send() similar to the Send-Data action of the ERTM spec. We shall not check for RemoteBusy or WAIT_F state inside l2cap_ertm_send(). Such checks were causing a bug in the retransmission logic of ERTM and making ERTM stalls until the ACL is dropped. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 108c2f290ac5..69f098d98141 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1415,11 +1415,8 @@ static int l2cap_ertm_send(struct sock *sk) u16 control, fcs; int nsent = 0; - if (pi->conn_state & L2CAP_CONN_WAIT_F) - return 0; - while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk)) && - !(pi->conn_state & L2CAP_CONN_REMOTE_BUSY)) { + while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk))) { if (pi->remote_max_tx && bt_cb(skb)->retries == pi->remote_max_tx) { @@ -1792,6 +1789,11 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms if (pi->mode == L2CAP_MODE_STREAMING) { err = l2cap_streaming_send(sk); } else { + if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY && + pi->conn_state && L2CAP_CONN_WAIT_F) { + err = len; + break; + } spin_lock_bh(&pi->send_lock); err = l2cap_ertm_send(sk); spin_unlock_bh(&pi->send_lock); @@ -3378,8 +3380,6 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY && pi->unacked_frames > 0) __mod_retrans_timer(); - pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - spin_lock_bh(&pi->send_lock); l2cap_ertm_send(sk); spin_unlock_bh(&pi->send_lock); -- cgit v1.2.2 From bfbacc11550a785caf082f3ccfcd7ecf882e09a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Mon, 31 May 2010 18:35:44 -0300 Subject: Bluetooth: Fix SREJ_QUEUE corruption in L2CAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since all TxSeq values are modulo, we shall not compare them directly. We have to compare their offset inside the TxWindow instead. Signed-off-by: João Paulo Rechi Vita Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 69f098d98141..b89762134e4e 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3394,6 +3394,8 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) static int l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_seq, u8 sar) { struct sk_buff *next_skb; + struct l2cap_pinfo *pi = l2cap_pi(sk); + int tx_seq_offset, next_tx_seq_offset; bt_cb(skb)->tx_seq = tx_seq; bt_cb(skb)->sar = sar; @@ -3404,11 +3406,20 @@ static int l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_s return 0; } + tx_seq_offset = (tx_seq - pi->buffer_seq) % 64; + if (tx_seq_offset < 0) + tx_seq_offset += 64; + do { if (bt_cb(next_skb)->tx_seq == tx_seq) return -EINVAL; - if (bt_cb(next_skb)->tx_seq > tx_seq) { + next_tx_seq_offset = (bt_cb(next_skb)->tx_seq - + pi->buffer_seq) % 64; + if (next_tx_seq_offset < 0) + next_tx_seq_offset += 64; + + if (next_tx_seq_offset > tx_seq_offset) { __skb_queue_before(SREJ_QUEUE(sk), next_skb, skb); return 0; } -- cgit v1.2.2 From 51893f88dd916efead5e24a212c907b2cd35e160 Mon Sep 17 00:00:00 2001 From: Nathan Holstein Date: Wed, 9 Jun 2010 15:46:25 -0400 Subject: Bluetooth: Fix bug with ERTM minimum packet length ERTM and streaming mode L2CAP sockets have no minimum packet length. Only basic mode connections have minimum length. Instead, validate the packet containing all necessary control, FCS, and SAR fields. The patch fixes the drop of valid packets with length lower than 4. Signed-off-by: Nathan Holstein Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index b89762134e4e..4af8fc0d512c 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -4092,9 +4092,9 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk { struct sock *sk; struct l2cap_pinfo *pi; - u16 control, len; + u16 control; u8 tx_seq, req_seq; - int next_tx_seq_offset, req_seq_offset; + int len, next_tx_seq_offset, req_seq_offset; sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); if (!sk) { @@ -4164,7 +4164,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk } if (__is_iframe(control)) { - if (len < 4) { + if (len < 0) { l2cap_send_disconn_req(pi->conn, sk); goto drop; } @@ -4192,7 +4192,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (pi->fcs == L2CAP_FCS_CRC16) len -= 2; - if (len > pi->mps || len < 4 || __is_sframe(control)) + if (len > pi->mps || len < 0 || __is_sframe(control)) goto drop; if (l2cap_check_fcs(pi, skb)) -- cgit v1.2.2 From c13ffa620f15cb28d18268a773464cf51550fa9f Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 13 May 2010 20:50:12 -0300 Subject: Bluetooth: Proper shutdown ERTM when closing the channel Fix a crash regarding the Monitor Timeout, it was running even after the shutdown of the ACL connection, which doesn't make sense. The same code also fixes another issue, before this patch L2CAP was sending many Disconnections Requests while we have to send only one. The issues are related to each other, a expired Monitor Timeout can trigger a Disconnection Request and then we may have a crash if the link was already deleted. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 59 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 22 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 4af8fc0d512c..4415eb48c6ae 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -272,6 +272,24 @@ static void l2cap_chan_del(struct sock *sk, int err) parent->sk_data_ready(parent, 0); } else sk->sk_state_change(sk); + + skb_queue_purge(TX_QUEUE(sk)); + + if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { + struct srej_list *l, *tmp; + + del_timer(&l2cap_pi(sk)->retrans_timer); + del_timer(&l2cap_pi(sk)->monitor_timer); + del_timer(&l2cap_pi(sk)->ack_timer); + + skb_queue_purge(SREJ_QUEUE(sk)); + skb_queue_purge(BUSY_QUEUE(sk)); + + list_for_each_entry_safe(l, tmp, SREJ_LIST(sk), list) { + list_del(&l->list); + kfree(l); + } + } } /* Service level security */ @@ -345,8 +363,12 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) struct sk_buff *skb; struct l2cap_hdr *lh; struct l2cap_conn *conn = pi->conn; + struct sock *sk = (struct sock *)pi; int count, hlen = L2CAP_HDR_SIZE + 2; + if (sk->sk_state != BT_CONNECTED) + return; + if (pi->fcs == L2CAP_FCS_CRC16) hlen += 2; @@ -438,10 +460,23 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk) { struct l2cap_disconn_req req; + if (!conn) + return; + + skb_queue_purge(TX_QUEUE(sk)); + + if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { + del_timer(&l2cap_pi(sk)->retrans_timer); + del_timer(&l2cap_pi(sk)->monitor_timer); + del_timer(&l2cap_pi(sk)->ack_timer); + } + req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); req.scid = cpu_to_le16(l2cap_pi(sk)->scid); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ, sizeof(req), &req); + + sk->sk_state = BT_DISCONN; } /* ---- L2CAP connections ---- */ @@ -734,7 +769,6 @@ static void __l2cap_sock_close(struct sock *sk, int reason) sk->sk_type == SOCK_STREAM) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; - sk->sk_state = BT_DISCONN; l2cap_sock_set_timer(sk, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, sk); } else @@ -1415,6 +1449,8 @@ static int l2cap_ertm_send(struct sock *sk) u16 control, fcs; int nsent = 0; + if (sk->sk_state != BT_CONNECTED) + return -ENOTCONN; while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk))) { @@ -3072,7 +3108,6 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr } default: - sk->sk_state = BT_DISCONN; sk->sk_err = ECONNRESET; l2cap_sock_set_timer(sk, HZ * 5); l2cap_send_disconn_req(conn, sk); @@ -3126,16 +3161,6 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd sk->sk_shutdown = SHUTDOWN_MASK; - skb_queue_purge(TX_QUEUE(sk)); - - if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { - skb_queue_purge(SREJ_QUEUE(sk)); - skb_queue_purge(BUSY_QUEUE(sk)); - del_timer(&l2cap_pi(sk)->retrans_timer); - del_timer(&l2cap_pi(sk)->monitor_timer); - del_timer(&l2cap_pi(sk)->ack_timer); - } - l2cap_chan_del(sk, ECONNRESET); bh_unlock_sock(sk); @@ -3158,16 +3183,6 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd if (!sk) return 0; - skb_queue_purge(TX_QUEUE(sk)); - - if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { - skb_queue_purge(SREJ_QUEUE(sk)); - skb_queue_purge(BUSY_QUEUE(sk)); - del_timer(&l2cap_pi(sk)->retrans_timer); - del_timer(&l2cap_pi(sk)->monitor_timer); - del_timer(&l2cap_pi(sk)->ack_timer); - } - l2cap_chan_del(sk, 0); bh_unlock_sock(sk); -- cgit v1.2.2 From 95ffa97827371ede501615d9bd048eb5b49e8fe1 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 18 Jun 2010 20:37:33 -0300 Subject: Bluetooth: Fix L2CAP control bit field corruption When resending an I-frame, ERTM was reusing the control bits from the last time it was sent, that was causing a corruption in the new control field due to it dirty fields. This patches extracts only the SAR bits from the old field and reuse it to resend the packet, the others bits should be reset and receive the updated value. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 4415eb48c6ae..c2fb26d9286c 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1430,6 +1430,8 @@ static void l2cap_retransmit_one_frame(struct sock *sk, 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 |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); @@ -1465,6 +1467,8 @@ static int l2cap_ertm_send(struct sock *sk) bt_cb(skb)->retries++; control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + control &= L2CAP_CTRL_SAR; + if (pi->conn_state & L2CAP_CONN_SEND_FBIT) { control |= L2CAP_CTRL_FINAL; pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; -- cgit v1.2.2 From 7fe9b298c98fdfecf3b0efb4c971b7696d091ae9 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 12 May 2010 18:32:04 -0300 Subject: Bluetooth: Stop ack_timer if ERTM enters in Local Busy or SREJ_SENT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ack_timer is implemation specific, disabling it in such situation avoids some potencial errors in the ERTM protocol. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index c2fb26d9286c..40cf67c11d20 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3665,6 +3665,8 @@ static int l2cap_push_rx_skb(struct sock *sk, struct sk_buff *skb, u16 control) pi->conn_state |= L2CAP_CONN_RNR_SENT; + del_timer(&pi->ack_timer); + queue_work(_busy_wq, &pi->busy_work); return err; @@ -3914,6 +3916,8 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str pi->conn_state |= L2CAP_CONN_SEND_PBIT; l2cap_send_srejframe(sk, tx_seq); + + del_timer(&pi->ack_timer); } return 0; -- cgit v1.2.2 From 2ece3684b4037ad2394de795d67abbe412ab5e2f Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 16 Jun 2010 17:21:44 -0300 Subject: Bluetooth: Update buffer_seq before retransmit frames Updating buffer_seq first make us able to ack the last I-frame received. This is also a requirement of the Profile Tuning Suite software. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 40cf67c11d20..ecf3e3dcefe3 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3931,6 +3931,10 @@ expected: return 0; } + err = l2cap_push_rx_skb(sk, skb, rx_control); + if (err < 0) + return 0; + if (rx_control & L2CAP_CTRL_FINAL) { if (pi->conn_state & L2CAP_CONN_REJ_ACT) pi->conn_state &= ~L2CAP_CONN_REJ_ACT; @@ -3938,10 +3942,6 @@ expected: l2cap_retransmit_frames(sk); } - err = l2cap_push_rx_skb(sk, skb, rx_control); - if (err < 0) - return 0; - __mod_ack_timer(); pi->num_acked = (pi->num_acked + 1) % num_to_ack; -- cgit v1.2.2 From 3cb123d1c03a0510d3c325bfaa971ce4df1e050d Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 29 May 2010 02:24:35 -0300 Subject: Bluetooth: Fix handle of received P-bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ERTM spec mandates that after receive a P-bit we shall send an F-bit in response. This patch fixes this for retransmitted packets, on retransmitting we were missing to check for a pending F-bit to be sent. Also we were missing some annotation to send a F-bit. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index ecf3e3dcefe3..f0441b0b0335 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1430,10 +1430,15 @@ static void l2cap_retransmit_one_frame(struct sock *sk, 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; + + if (pi->conn_state & L2CAP_CONN_SEND_FBIT) { + control |= L2CAP_CTRL_FINAL; + pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; + } control |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); + put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); if (pi->fcs == L2CAP_FCS_CRC16) { @@ -3385,7 +3390,6 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) u16 control = 0; pi->frames_sent = 0; - pi->conn_state |= L2CAP_CONN_SEND_FBIT; control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; @@ -3963,6 +3967,7 @@ static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) l2cap_drop_acked_frames(sk); if (rx_control & L2CAP_CTRL_POLL) { + pi->conn_state |= L2CAP_CONN_SEND_FBIT; if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && (pi->unacked_frames > 0)) @@ -4030,6 +4035,8 @@ static inline void l2cap_data_channel_srejframe(struct sock *sk, u16 rx_control) if (rx_control & L2CAP_CTRL_POLL) { pi->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(sk); + + pi->conn_state |= L2CAP_CONN_SEND_FBIT; l2cap_retransmit_one_frame(sk, tx_seq); spin_lock_bh(&pi->send_lock); @@ -4064,6 +4071,9 @@ static inline void l2cap_data_channel_rnrframe(struct sock *sk, u16 rx_control) pi->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(sk); + if (rx_control & L2CAP_CTRL_POLL) + pi->conn_state |= L2CAP_CONN_SEND_FBIT; + if (!(pi->conn_state & L2CAP_CONN_SREJ_SENT)) { del_timer(&pi->retrans_timer); if (rx_control & L2CAP_CTRL_POLL) -- cgit v1.2.2 From 45d65c46acc39945219eeb3752367ee80ed82799 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 7 Jun 2010 19:21:30 -0300 Subject: Bluetooth: Check the tx_window size on setsockopt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have to check if the proposed tx_window value is not greater that maximum value supported. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index f0441b0b0335..6094870d5d2a 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1912,6 +1912,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us break; } + if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) { + err = -EINVAL; + break; + } + l2cap_pi(sk)->mode = opts.mode; switch (l2cap_pi(sk)->mode) { case L2CAP_MODE_BASIC: -- cgit v1.2.2 From 260000896750690b774d4343294ae5cbff1423e5 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 11 May 2010 22:02:00 -0300 Subject: Bluetooth: Check packet FCS earlier This way, if FCS is enabled and the packet is corrupted, we just drop it without read it len, which could be corrupted. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 6094870d5d2a..8c9f577dd46d 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -4166,25 +4166,25 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk skb_pull(skb, 2); len = skb->len; + /* + * We can just drop the corrupted I-frame here. + * Receiver will miss it and start proper recovery + * procedures and ask retransmission. + */ + if (l2cap_check_fcs(pi, skb)) + goto drop; + if (__is_sar_start(control) && __is_iframe(control)) len -= 2; if (pi->fcs == L2CAP_FCS_CRC16) len -= 2; - /* - * We can just drop the corrupted I-frame here. - * Receiver will miss it and start proper recovery - * procedures and ask retransmission. - */ if (len > pi->mps) { l2cap_send_disconn_req(pi->conn, sk); goto drop; } - if (l2cap_check_fcs(pi, skb)) - goto drop; - req_seq = __get_reqseq(control); req_seq_offset = (req_seq - pi->expected_ack_seq) % 64; if (req_seq_offset < 0) @@ -4224,6 +4224,9 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk skb_pull(skb, 2); len = skb->len; + if (l2cap_check_fcs(pi, skb)) + goto drop; + if (__is_sar_start(control)) len -= 2; @@ -4233,9 +4236,6 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (len > pi->mps || len < 0 || __is_sframe(control)) goto drop; - if (l2cap_check_fcs(pi, skb)) - goto drop; - tx_seq = __get_txseq(control); if (pi->expected_tx_seq == tx_seq) -- cgit v1.2.2 From 4ea727ef9d507413f15da0de401d8a50b125649a Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 3 Jun 2010 16:34:20 -0300 Subject: Bluetooth: Fix missing retransmission action with RR(P=1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Bluetooth SIG Profile Tuning Suite Software uses the CSA1 spec to run the L2CAP tests. The new 3.0 spec has a missing Retransmit-I-Frames action when the Remote side is Busy. We still start the retransmission timer if Remote is Busy and unacked frames > 0. We do everything we did before this change plus the Retransmission of I-frames. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 8c9f577dd46d..7c2273732909 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3405,8 +3405,8 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; } - if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY && pi->unacked_frames > 0) - __mod_retrans_timer(); + if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY) + l2cap_retransmit_frames(sk); spin_lock_bh(&pi->send_lock); l2cap_ertm_send(sk); -- cgit v1.2.2 From 9b108fc0cf4e79c34a7d5626f5c2c4c529ef6d3f Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 20 May 2010 16:21:53 -0300 Subject: Bluetooth: Fix ERTM error reporting to the userspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If any error occurs during transfers we have to tell userspace that something wrong happened. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 7c2273732909..1e39d72b80af 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -456,7 +456,7 @@ static void l2cap_do_start(struct sock *sk) } } -static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk) +static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err) { struct l2cap_disconn_req req; @@ -477,6 +477,7 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk) L2CAP_DISCONN_REQ, sizeof(req), &req); sk->sk_state = BT_DISCONN; + sk->sk_err = err; } /* ---- L2CAP connections ---- */ @@ -770,7 +771,7 @@ static void __l2cap_sock_close(struct sock *sk, int reason) struct l2cap_conn *conn = l2cap_pi(sk)->conn; l2cap_sock_set_timer(sk, sk->sk_sndtimeo); - l2cap_send_disconn_req(conn, sk); + l2cap_send_disconn_req(conn, sk, reason); } else l2cap_chan_del(sk, reason); break; @@ -1315,7 +1316,7 @@ static void l2cap_monitor_timeout(unsigned long arg) bh_lock_sock(sk); if (l2cap_pi(sk)->retry_count >= l2cap_pi(sk)->remote_max_tx) { - l2cap_send_disconn_req(l2cap_pi(sk)->conn, sk); + l2cap_send_disconn_req(l2cap_pi(sk)->conn, sk, ECONNABORTED); bh_unlock_sock(sk); return; } @@ -1423,7 +1424,7 @@ static void l2cap_retransmit_one_frame(struct sock *sk, u8 tx_seq) if (pi->remote_max_tx && bt_cb(skb)->retries == pi->remote_max_tx) { - l2cap_send_disconn_req(pi->conn, sk); + l2cap_send_disconn_req(pi->conn, sk, ECONNABORTED); return; } @@ -1463,7 +1464,7 @@ static int l2cap_ertm_send(struct sock *sk) if (pi->remote_max_tx && bt_cb(skb)->retries == pi->remote_max_tx) { - l2cap_send_disconn_req(pi->conn, sk); + l2cap_send_disconn_req(pi->conn, sk, ECONNABORTED); break; } @@ -2191,6 +2192,10 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); } + + if (!err && sk->sk_err) + err = -sk->sk_err; + release_sock(sk); return err; } @@ -2462,7 +2467,7 @@ static int l2cap_build_conf_req(struct sock *sk, void *data) case L2CAP_MODE_ERTM: pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask)) - l2cap_send_disconn_req(pi->conn, sk); + l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); break; default: pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask); @@ -3030,7 +3035,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr /* Complete config. */ len = l2cap_parse_conf_req(sk, rsp); if (len < 0) { - l2cap_send_disconn_req(conn, sk); + l2cap_send_disconn_req(conn, sk, ECONNRESET); goto unlock; } @@ -3100,7 +3105,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr char req[64]; if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) { - l2cap_send_disconn_req(conn, sk); + l2cap_send_disconn_req(conn, sk, ECONNRESET); goto done; } @@ -3109,7 +3114,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr len = l2cap_parse_conf_rsp(sk, rsp->data, len, req, &result); if (len < 0) { - l2cap_send_disconn_req(conn, sk); + l2cap_send_disconn_req(conn, sk, ECONNRESET); goto done; } @@ -3124,7 +3129,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr default: sk->sk_err = ECONNRESET; l2cap_sock_set_timer(sk, HZ * 5); - l2cap_send_disconn_req(conn, sk); + l2cap_send_disconn_req(conn, sk, ECONNRESET); goto done; } @@ -3565,7 +3570,7 @@ drop: pi->sdu = NULL; disconnect: - l2cap_send_disconn_req(pi->conn, sk); + l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); kfree_skb(skb); return 0; } @@ -3588,7 +3593,7 @@ static void l2cap_busy_work(struct work_struct *work) if (n_tries++ > L2CAP_LOCAL_BUSY_TRIES) { err = -EBUSY; - l2cap_send_disconn_req(pi->conn, sk); + l2cap_send_disconn_req(pi->conn, sk, EBUSY); goto done; } @@ -3864,7 +3869,7 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str /* invalid tx_seq */ if (tx_seq_offset >= pi->tx_win) { - l2cap_send_disconn_req(pi->conn, sk); + l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); goto drop; } @@ -4181,7 +4186,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk len -= 2; if (len > pi->mps) { - l2cap_send_disconn_req(pi->conn, sk); + l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); goto drop; } @@ -4197,20 +4202,20 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk /* check for invalid req-seq */ if (req_seq_offset > next_tx_seq_offset) { - l2cap_send_disconn_req(pi->conn, sk); + l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); goto drop; } if (__is_iframe(control)) { if (len < 0) { - l2cap_send_disconn_req(pi->conn, sk); + l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); goto drop; } l2cap_data_channel_iframe(sk, control, skb); } else { if (len != 0) { - l2cap_send_disconn_req(pi->conn, sk); + l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); goto drop; } -- cgit v1.2.2 From 0e98958d4f827f814444757e0376546b462dfe6f Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 19 Apr 2010 14:45:38 -0300 Subject: Bluetooth: Add debug output to ERTM code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the dynamic debug to output info about ERTM protocol stuff. The following script can be used to enable debug for ERTM: DEBUGFS="/sys/kernel/debug/dynamic_debug/control" echo -n 'func l2cap_send_disconn_req +p' > $DEBUGFS echo -n 'func l2cap_monitor_timeout +p' > $DEBUGFS echo -n 'func l2cap_retrans_timeout +p' > $DEBUGFS echo -n 'func l2cap_busy_work +p' > $DEBUGFS echo -n 'func l2cap_push_rx_skb +p' > $DEBUGFS echo -n 'func l2cap_data_channel_iframe +p' > $DEBUGFS echo -n 'func l2cap_data_channel_rrframe +p' > $DEBUGFS echo -n 'func l2cap_data_channel_rejframe +p' > $DEBUGFS echo -n 'func l2cap_data_channel_srejframe +p' > $DEBUGFS echo -n 'func l2cap_data_channel_rnrframe +p' > $DEBUGFS Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 1e39d72b80af..6e8a0512a9c9 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1314,6 +1314,8 @@ static void l2cap_monitor_timeout(unsigned long arg) { struct sock *sk = (void *) arg; + BT_DBG("sk %p", sk); + bh_lock_sock(sk); if (l2cap_pi(sk)->retry_count >= l2cap_pi(sk)->remote_max_tx) { l2cap_send_disconn_req(l2cap_pi(sk)->conn, sk, ECONNABORTED); @@ -1332,6 +1334,8 @@ static void l2cap_retrans_timeout(unsigned long arg) { struct sock *sk = (void *) arg; + BT_DBG("sk %p", sk); + bh_lock_sock(sk); l2cap_pi(sk)->retry_count = 1; __mod_monitor_timer(); @@ -3645,6 +3649,8 @@ done: pi->conn_state &= ~L2CAP_CONN_LOCAL_BUSY; pi->conn_state &= ~L2CAP_CONN_RNR_SENT; + BT_DBG("sk %p, Exit local busy", sk); + set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); @@ -3669,6 +3675,8 @@ static int l2cap_push_rx_skb(struct sock *sk, struct sk_buff *skb, u16 control) } /* Busy Condition */ + BT_DBG("sk %p, Enter local busy", sk); + pi->conn_state |= L2CAP_CONN_LOCAL_BUSY; bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; __skb_queue_tail(BUSY_QUEUE(sk), skb); @@ -3847,7 +3855,8 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str int num_to_ack = (pi->tx_win/6) + 1; int err = 0; - BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); + BT_DBG("sk %p len %d tx_seq %d rx_control 0x%4.4x", sk, skb->len, tx_seq, + rx_control); if (L2CAP_CTRL_FINAL & rx_control && l2cap_pi(sk)->conn_state & L2CAP_CONN_WAIT_F) { @@ -3892,6 +3901,7 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str pi->buffer_seq = pi->buffer_seq_srej; pi->conn_state &= ~L2CAP_CONN_SREJ_SENT; l2cap_send_ack(pi); + BT_DBG("sk %p, Exit SREJ_SENT", sk); } } else { struct srej_list *l; @@ -3920,6 +3930,8 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str pi->conn_state |= L2CAP_CONN_SREJ_SENT; + BT_DBG("sk %p, Enter SREJ", sk); + INIT_LIST_HEAD(SREJ_LIST(sk)); pi->buffer_seq_srej = pi->buffer_seq; @@ -3973,6 +3985,9 @@ static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) { struct l2cap_pinfo *pi = l2cap_pi(sk); + BT_DBG("sk %p, req_seq %d ctrl 0x%4.4x", sk, __get_reqseq(rx_control), + rx_control); + pi->expected_ack_seq = __get_reqseq(rx_control); l2cap_drop_acked_frames(sk); @@ -4018,6 +4033,8 @@ static inline void l2cap_data_channel_rejframe(struct sock *sk, u16 rx_control) struct l2cap_pinfo *pi = l2cap_pi(sk); u8 tx_seq = __get_reqseq(rx_control); + BT_DBG("sk %p, req_seq %d ctrl 0x%4.4x", sk, tx_seq, rx_control); + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; pi->expected_ack_seq = tx_seq; @@ -4040,6 +4057,8 @@ static inline void l2cap_data_channel_srejframe(struct sock *sk, u16 rx_control) struct l2cap_pinfo *pi = l2cap_pi(sk); u8 tx_seq = __get_reqseq(rx_control); + BT_DBG("sk %p, req_seq %d ctrl 0x%4.4x", sk, tx_seq, rx_control); + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; if (rx_control & L2CAP_CTRL_POLL) { @@ -4077,6 +4096,8 @@ static inline void l2cap_data_channel_rnrframe(struct sock *sk, u16 rx_control) struct l2cap_pinfo *pi = l2cap_pi(sk); u8 tx_seq = __get_reqseq(rx_control); + BT_DBG("sk %p, req_seq %d ctrl 0x%4.4x", sk, tx_seq, rx_control); + pi->conn_state |= L2CAP_CONN_REMOTE_BUSY; pi->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(sk); -- cgit v1.2.2 From 64988868637304330f7df20d08b965592312c531 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 10 May 2010 14:54:14 -0300 Subject: Bluetooth: Tweaks to l2cap_send_i_or_rr_or_rnr() flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit l2cap_send_sframe() already set the F-bit if we set L2CAP_CONN_SEND_FBIT and unset L2CAP_CONN_SEND_FBIT after send the F-bit. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 6e8a0512a9c9..65c6a989449a 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3408,10 +3408,9 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { - control |= L2CAP_SUPER_RCV_NOT_READY | L2CAP_CTRL_FINAL; + control |= L2CAP_SUPER_RCV_NOT_READY; l2cap_send_sframe(pi, control); pi->conn_state |= L2CAP_CONN_RNR_SENT; - pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; } if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY) -- cgit v1.2.2 From 85eb53c6f719523dde9e0658823dffd2664d4d1c Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 3 Jun 2010 18:43:28 -0300 Subject: Bluetooth: Change the way we set ERTM mode as mandatory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the socket type is SOCK_STREAM we set Enhanced Retransmisson Mode or Streaming Mode as mandatory. That means that we will close the channel if the other side doesn't support or request the the mandatory mode. Basic mode can't be set as mandatory. Signed-off-by: Gustavo F. Padovan Reviewed-by: João Paulo Rechi Vita Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 65c6a989449a..e78a7504d09c 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -831,6 +831,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->imtu = l2cap_pi(parent)->imtu; pi->omtu = l2cap_pi(parent)->omtu; + pi->conf_state = l2cap_pi(parent)->conf_state; pi->mode = l2cap_pi(parent)->mode; pi->fcs = l2cap_pi(parent)->fcs; pi->max_tx = l2cap_pi(parent)->max_tx; @@ -841,10 +842,12 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) } else { pi->imtu = L2CAP_DEFAULT_MTU; pi->omtu = 0; - if (enable_ertm && sk->sk_type == SOCK_STREAM) + if (enable_ertm && sk->sk_type == SOCK_STREAM) { pi->mode = L2CAP_MODE_ERTM; - else + pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; + } else { pi->mode = L2CAP_MODE_BASIC; + } pi->max_tx = L2CAP_DEFAULT_MAX_TX; pi->fcs = L2CAP_FCS_CRC16; pi->tx_win = L2CAP_DEFAULT_TX_WINDOW; @@ -1925,6 +1928,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us l2cap_pi(sk)->mode = opts.mode; switch (l2cap_pi(sk)->mode) { case L2CAP_MODE_BASIC: + l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_STATE2_DEVICE; break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -2469,7 +2473,12 @@ static int l2cap_build_conf_req(struct sock *sk, void *data) switch (pi->mode) { case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: - pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; + if (!(pi->conf_state & L2CAP_CONF_STATE2_DEVICE)) { + pi->mode = l2cap_select_mode(rfc.mode, + pi->conn->feat_mask); + break; + } + if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask)) l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); break; @@ -2602,7 +2611,12 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) switch (pi->mode) { case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: - pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; + if (!(pi->conf_state & L2CAP_CONF_STATE2_DEVICE)) { + pi->mode = l2cap_select_mode(rfc.mode, + pi->conn->feat_mask); + break; + } + if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask)) return -ECONNREFUSED; break; -- cgit v1.2.2 From 742e519b0db4a470008118b48f0baea6126e2122 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 8 Jun 2010 19:09:48 -0300 Subject: Bluetooth: Disconnect the channel if we don't want the proposed mode If the device is a STATE 2 then it should disconnect the channel if the remote device propose a mode different from its mandatory mode. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index e78a7504d09c..03e9125dd74f 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2617,8 +2617,9 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) break; } - if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask)) + if (pi->mode != rfc.mode) return -ECONNREFUSED; + break; default: pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask); -- cgit v1.2.2 From ae12d52efd492ee6634c34322302ac754ff8cde8 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 8 Jun 2010 19:29:00 -0300 Subject: Bluetooth: Prefer Basic Mode on receipt of ConfigReq If we choose to use Basic Mode then we have to refuse the received mode and propose Basic Mode again. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 03e9125dd74f..ceed5df43fee 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2621,9 +2621,6 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) return -ECONNREFUSED; break; - default: - pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask); - break; } done: -- cgit v1.2.2 From 625477523b4e656fbcc5ec2a8ca7a1beb39b1caf Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 8 Jun 2010 20:05:31 -0300 Subject: Bluetooth: Actively send request for Basic Mode The Profile Tuning Suite requires that we send a RFC containing the Basic Mode configuration when requesting Basic Mode. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index ceed5df43fee..ca9bab225777 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2492,6 +2492,14 @@ done: case L2CAP_MODE_BASIC: if (pi->imtu != L2CAP_DEFAULT_MTU) l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu); + + rfc.mode = L2CAP_MODE_BASIC; + rfc.txwin_size = 0; + rfc.max_transmit = 0; + rfc.retrans_timeout = 0; + rfc.monitor_timeout = 0; + rfc.max_pdu_size = 0; + break; case L2CAP_MODE_ERTM: @@ -2504,9 +2512,6 @@ done: if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10) rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); - if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS)) break; @@ -2527,9 +2532,6 @@ done: if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10) rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); - if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS)) break; @@ -2541,6 +2543,9 @@ done: break; } + 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); -- cgit v1.2.2 From 6c2ea7a8f5fea67fa20e5825401b8fce5a78dbf6 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 8 Jun 2010 20:08:49 -0300 Subject: Bluetooth: Refuse ConfigRsp with different mode If our mode is Basic Mode we have to refuse any ConfigRsp that proposes a different mode. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index ca9bab225777..c5904082392c 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2747,7 +2747,6 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, rfc.mode != pi->mode) return -ECONNREFUSED; - pi->mode = rfc.mode; pi->fcs = 0; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, @@ -2756,6 +2755,11 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, } } + if (pi->mode == L2CAP_MODE_BASIC && pi->mode != rfc.mode) + return -ECONNREFUSED; + + pi->mode = rfc.mode; + if (*result == L2CAP_CONF_SUCCESS) { switch (rfc.mode) { case L2CAP_MODE_ERTM: -- cgit v1.2.2 From 2ba13ed678775195e8255b4e503c59d48b615bd8 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 9 Jun 2010 16:39:05 -0300 Subject: Bluetooth: Remove check for supported mode Since now we have checks for the supported mode before on l2cap_info_rsp we can remove the check for it here. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index c5904082392c..2fb45c481762 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2473,15 +2473,10 @@ static int l2cap_build_conf_req(struct sock *sk, void *data) switch (pi->mode) { case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: - if (!(pi->conf_state & L2CAP_CONF_STATE2_DEVICE)) { - pi->mode = l2cap_select_mode(rfc.mode, - pi->conn->feat_mask); + if (pi->conf_state & L2CAP_CONF_STATE2_DEVICE) break; - } - if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask)) - l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); - break; + /* fall through */ default: pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask); break; -- cgit v1.2.2 From cf6c2c0b9f47ee3cd12684b905725c8376d52135 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 7 Jun 2010 20:54:45 -0300 Subject: Bluetooth: Disconnect early if mode is not supported When mode is mandatory we shall not send connect request and report this to the userspace as well. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 56 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 16 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 2fb45c481762..6a33d269389e 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -456,6 +456,22 @@ static void l2cap_do_start(struct sock *sk) } } +static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) +{ + u32 local_feat_mask = l2cap_feat_mask; + if (enable_ertm) + local_feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING; + + switch (mode) { + case L2CAP_MODE_ERTM: + return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask; + case L2CAP_MODE_STREAMING: + return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask; + default: + return 0x00; + } +} + static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err) { struct l2cap_disconn_req req; @@ -484,10 +500,13 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int static void l2cap_conn_start(struct l2cap_conn *conn) { struct l2cap_chan_list *l = &conn->chan_list; + struct sock_del_list del, *tmp1, *tmp2; struct sock *sk; BT_DBG("conn %p", conn); + INIT_LIST_HEAD(&del.list); + read_lock(&l->lock); for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { @@ -503,6 +522,19 @@ static void l2cap_conn_start(struct l2cap_conn *conn) if (l2cap_check_security(sk) && __l2cap_no_conn_pending(sk)) { struct l2cap_conn_req req; + + if (!l2cap_mode_supported(l2cap_pi(sk)->mode, + conn->feat_mask) + && l2cap_pi(sk)->conf_state & + L2CAP_CONF_STATE2_DEVICE) { + tmp1 = kzalloc(sizeof(struct srej_list), + GFP_ATOMIC); + tmp1->sk = sk; + list_add_tail(&tmp1->list, &del.list); + bh_unlock_sock(sk); + continue; + } + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; @@ -542,6 +574,14 @@ static void l2cap_conn_start(struct l2cap_conn *conn) } read_unlock(&l->lock); + + list_for_each_entry_safe(tmp1, tmp2, &del.list, list) { + bh_lock_sock(tmp1->sk); + __l2cap_sock_close(tmp1->sk, ECONNRESET); + bh_unlock_sock(tmp1->sk); + list_del(&tmp1->list); + kfree(tmp1); + } } static void l2cap_conn_ready(struct l2cap_conn *conn) @@ -2429,22 +2469,6 @@ static inline void l2cap_ertm_init(struct sock *sk) INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work); } -static int l2cap_mode_supported(__u8 mode, __u32 feat_mask) -{ - u32 local_feat_mask = l2cap_feat_mask; - if (enable_ertm) - local_feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING; - - switch (mode) { - case L2CAP_MODE_ERTM: - return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask; - case L2CAP_MODE_STREAMING: - return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask; - default: - return 0x00; - } -} - static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) { switch (mode) { -- cgit v1.2.2 From 8cb8e6f1684be13b51f8429b15f39c140326b327 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 14 Jun 2010 02:26:15 -0300 Subject: Bluetooth: Don't accept ConfigReq if we aren't in the BT_CONFIG state If such event happens we shall reply with a Command Reject, because we are not expecting any configure request. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 6a33d269389e..f6e46fdddd2b 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3052,8 +3052,14 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (!sk) return -ENOENT; - if (sk->sk_state == BT_DISCONN) + if (sk->sk_state != BT_CONFIG) { + struct l2cap_cmd_rej rej; + + rej.reason = cpu_to_le16(0x0002); + l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ, + sizeof(rej), &rej); goto unlock; + } /* Reject if config buffer is too small. */ len = cmd_len - sizeof(*req); -- cgit v1.2.2 From e0f66218b3a7d0bcf37ca95186123c257fda0ba5 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 21 Jun 2010 18:50:49 -0300 Subject: Bluetooth: Remove the send_lock spinlock from ERTM Using a lock to deal with the ERTM race condition - interruption with new data from the hci layer - is wrong. We should use the native skb backlog queue. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index f6e46fdddd2b..dc8601fc2404 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1562,16 +1562,11 @@ static int l2cap_retransmit_frames(struct sock *sk) struct l2cap_pinfo *pi = l2cap_pi(sk); int ret; - spin_lock_bh(&pi->send_lock); - if (!skb_queue_empty(TX_QUEUE(sk))) sk->sk_send_head = TX_QUEUE(sk)->next; pi->next_tx_seq = pi->expected_ack_seq; ret = l2cap_ertm_send(sk); - - spin_unlock_bh(&pi->send_lock); - return ret; } @@ -1579,7 +1574,6 @@ static void l2cap_send_ack(struct l2cap_pinfo *pi) { struct sock *sk = (struct sock *)pi; u16 control = 0; - int nframes; control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; @@ -1590,11 +1584,7 @@ static void l2cap_send_ack(struct l2cap_pinfo *pi) return; } - spin_lock_bh(&pi->send_lock); - nframes = l2cap_ertm_send(sk); - spin_unlock_bh(&pi->send_lock); - - if (nframes > 0) + if (l2cap_ertm_send(sk) > 0) return; control |= L2CAP_SUPER_RCV_READY; @@ -1789,10 +1779,8 @@ static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, siz size += buflen; } skb_queue_splice_tail(&sar_queue, TX_QUEUE(sk)); - spin_lock_bh(&pi->send_lock); if (sk->sk_send_head == NULL) sk->sk_send_head = sar_queue.next; - spin_unlock_bh(&pi->send_lock); return size; } @@ -1864,14 +1852,9 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms } __skb_queue_tail(TX_QUEUE(sk), skb); - if (pi->mode == L2CAP_MODE_ERTM) - spin_lock_bh(&pi->send_lock); - if (sk->sk_send_head == NULL) sk->sk_send_head = skb; - if (pi->mode == L2CAP_MODE_ERTM) - spin_unlock_bh(&pi->send_lock); } else { /* Segment SDU into multiples PDUs */ err = l2cap_sar_segment_sdu(sk, msg, len); @@ -1887,9 +1870,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms err = len; break; } - spin_lock_bh(&pi->send_lock); err = l2cap_ertm_send(sk); - spin_unlock_bh(&pi->send_lock); } if (err >= 0) @@ -2464,7 +2445,6 @@ static inline void l2cap_ertm_init(struct sock *sk) __skb_queue_head_init(SREJ_QUEUE(sk)); __skb_queue_head_init(BUSY_QUEUE(sk)); - spin_lock_init(&l2cap_pi(sk)->send_lock); INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work); } @@ -3462,9 +3442,7 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY) l2cap_retransmit_frames(sk); - spin_lock_bh(&pi->send_lock); l2cap_ertm_send(sk); - spin_unlock_bh(&pi->send_lock); if (!(pi->conn_state & L2CAP_CONN_LOCAL_BUSY) && pi->frames_sent == 0) { @@ -4066,9 +4044,7 @@ static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { l2cap_send_ack(pi); } else { - spin_lock_bh(&pi->send_lock); l2cap_ertm_send(sk); - spin_unlock_bh(&pi->send_lock); } } } @@ -4113,9 +4089,7 @@ static inline void l2cap_data_channel_srejframe(struct sock *sk, u16 rx_control) pi->conn_state |= L2CAP_CONN_SEND_FBIT; l2cap_retransmit_one_frame(sk, tx_seq); - spin_lock_bh(&pi->send_lock); l2cap_ertm_send(sk); - spin_unlock_bh(&pi->send_lock); if (pi->conn_state & L2CAP_CONN_WAIT_F) { pi->srej_save_reqseq = tx_seq; -- cgit v1.2.2 From 218bb9dfd21472128f86b38ad2eab123205c2991 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 21 Jun 2010 18:53:22 -0300 Subject: Bluetooth: Add backlog queue to ERTM code backlog queue is the canonical mechanism to avoid race conditions due interrupts in bottom half context. After the socket lock is released the net core take care of push all skb in its backlog queue. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 132 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 79 insertions(+), 53 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index dc8601fc2404..cf4481f7f566 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -77,6 +77,8 @@ 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 int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb); + /* ---- L2CAP timers ---- */ static void l2cap_sock_timeout(unsigned long arg) { @@ -2447,6 +2449,8 @@ static inline void l2cap_ertm_init(struct sock *sk) __skb_queue_head_init(BUSY_QUEUE(sk)); INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work); + + sk->sk_backlog_rcv = l2cap_ertm_data_rcv; } static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) @@ -4171,13 +4175,83 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str return 0; } +static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + u16 control; + u8 req_seq; + int len, next_tx_seq_offset, req_seq_offset; + + control = get_unaligned_le16(skb->data); + skb_pull(skb, 2); + len = skb->len; + + /* + * We can just drop the corrupted I-frame here. + * Receiver will miss it and start proper recovery + * procedures and ask retransmission. + */ + if (l2cap_check_fcs(pi, skb)) + goto drop; + + if (__is_sar_start(control) && __is_iframe(control)) + len -= 2; + + if (pi->fcs == L2CAP_FCS_CRC16) + len -= 2; + + if (len > pi->mps) { + l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); + goto drop; + } + + req_seq = __get_reqseq(control); + req_seq_offset = (req_seq - pi->expected_ack_seq) % 64; + if (req_seq_offset < 0) + req_seq_offset += 64; + + next_tx_seq_offset = + (pi->next_tx_seq - pi->expected_ack_seq) % 64; + if (next_tx_seq_offset < 0) + next_tx_seq_offset += 64; + + /* check for invalid req-seq */ + if (req_seq_offset > next_tx_seq_offset) { + l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); + goto drop; + } + + if (__is_iframe(control)) { + if (len < 0) { + l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); + goto drop; + } + + l2cap_data_channel_iframe(sk, control, skb); + } else { + if (len != 0) { + BT_ERR("%d", len); + l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); + goto drop; + } + + l2cap_data_channel_sframe(sk, control, skb); + } + + return 0; + +drop: + kfree_skb(skb); + return 0; +} + static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) { struct sock *sk; struct l2cap_pinfo *pi; u16 control; - u8 tx_seq, req_seq; - int len, next_tx_seq_offset, req_seq_offset; + u8 tx_seq; + int len; sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); if (!sk) { @@ -4207,59 +4281,11 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk break; case L2CAP_MODE_ERTM: - control = get_unaligned_le16(skb->data); - skb_pull(skb, 2); - len = skb->len; - - /* - * We can just drop the corrupted I-frame here. - * Receiver will miss it and start proper recovery - * procedures and ask retransmission. - */ - if (l2cap_check_fcs(pi, skb)) - goto drop; - - if (__is_sar_start(control) && __is_iframe(control)) - len -= 2; - - if (pi->fcs == L2CAP_FCS_CRC16) - len -= 2; - - if (len > pi->mps) { - l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); - goto drop; - } - - req_seq = __get_reqseq(control); - req_seq_offset = (req_seq - pi->expected_ack_seq) % 64; - if (req_seq_offset < 0) - req_seq_offset += 64; - - next_tx_seq_offset = - (pi->next_tx_seq - pi->expected_ack_seq) % 64; - if (next_tx_seq_offset < 0) - next_tx_seq_offset += 64; - - /* check for invalid req-seq */ - if (req_seq_offset > next_tx_seq_offset) { - l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); - goto drop; - } - - if (__is_iframe(control)) { - if (len < 0) { - l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); - goto drop; - } - - l2cap_data_channel_iframe(sk, control, skb); + if (!sock_owned_by_user(sk)) { + l2cap_ertm_data_rcv(sk, skb); } else { - if (len != 0) { - l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); + if (sk_add_backlog(sk, skb)) goto drop; - } - - l2cap_data_channel_sframe(sk, control, skb); } goto done; -- cgit v1.2.2 From 712132eb541e4a76afad97898dc0ce6b6c0032d8 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 21 Jun 2010 19:39:50 -0300 Subject: Bluetooth: Improve ERTM local busy handling Now we also check if can push skb userspace just after receive a new skb instead of only wait the l2cap_busy_work wake up from time to time to check the local busy condition. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 83 +++++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 36 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index cf4481f7f566..6b839d682143 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3606,6 +3606,46 @@ disconnect: return 0; } +static int l2cap_try_push_rx_skb(struct sock *sk) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *skb; + u16 control; + int err; + + while ((skb = skb_dequeue(BUSY_QUEUE(sk)))) { + control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; + err = l2cap_ertm_reassembly_sdu(sk, skb, control); + if (err < 0) { + skb_queue_head(BUSY_QUEUE(sk), skb); + return -EBUSY; + } + + pi->buffer_seq = (pi->buffer_seq + 1) % 64; + } + + if (!(pi->conn_state & L2CAP_CONN_RNR_SENT)) + goto done; + + control = pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= L2CAP_SUPER_RCV_READY | L2CAP_CTRL_POLL; + l2cap_send_sframe(pi, control); + l2cap_pi(sk)->retry_count = 1; + + del_timer(&pi->retrans_timer); + __mod_monitor_timer(); + + l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F; + +done: + pi->conn_state &= ~L2CAP_CONN_LOCAL_BUSY; + pi->conn_state &= ~L2CAP_CONN_RNR_SENT; + + BT_DBG("sk %p, Exit local busy", sk); + + return 0; +} + static void l2cap_busy_work(struct work_struct *work) { DECLARE_WAITQUEUE(wait, current); @@ -3614,7 +3654,6 @@ static void l2cap_busy_work(struct work_struct *work) struct sock *sk = (struct sock *)pi; int n_tries = 0, timeo = HZ/5, err; struct sk_buff *skb; - u16 control; lock_sock(sk); @@ -3625,7 +3664,7 @@ static void l2cap_busy_work(struct work_struct *work) if (n_tries++ > L2CAP_LOCAL_BUSY_TRIES) { err = -EBUSY; l2cap_send_disconn_req(pi->conn, sk, EBUSY); - goto done; + break; } if (!timeo) @@ -3633,7 +3672,7 @@ static void l2cap_busy_work(struct work_struct *work) if (signal_pending(current)) { err = sock_intr_errno(timeo); - goto done; + break; } release_sock(sk); @@ -3642,42 +3681,12 @@ static void l2cap_busy_work(struct work_struct *work) err = sock_error(sk); if (err) - goto done; - - while ((skb = skb_dequeue(BUSY_QUEUE(sk)))) { - control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; - err = l2cap_ertm_reassembly_sdu(sk, skb, control); - if (err < 0) { - skb_queue_head(BUSY_QUEUE(sk), skb); - break; - } - - pi->buffer_seq = (pi->buffer_seq + 1) % 64; - } + break; - if (!skb) + if (l2cap_try_push_rx_skb(sk) == 0) break; } - if (!(pi->conn_state & L2CAP_CONN_RNR_SENT)) - goto done; - - control = pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - control |= L2CAP_SUPER_RCV_READY | L2CAP_CTRL_POLL; - l2cap_send_sframe(pi, control); - l2cap_pi(sk)->retry_count = 1; - - del_timer(&pi->retrans_timer); - __mod_monitor_timer(); - - l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F; - -done: - pi->conn_state &= ~L2CAP_CONN_LOCAL_BUSY; - pi->conn_state &= ~L2CAP_CONN_RNR_SENT; - - BT_DBG("sk %p, Exit local busy", sk); - set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); @@ -3692,7 +3701,9 @@ static int l2cap_push_rx_skb(struct sock *sk, struct sk_buff *skb, u16 control) if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; __skb_queue_tail(BUSY_QUEUE(sk), skb); - return -EBUSY; + return l2cap_try_push_rx_skb(sk); + + } err = l2cap_ertm_reassembly_sdu(sk, skb, control); -- cgit v1.2.2 From 8b0dc6dc827fb71efd6c9dfb5c4172b7b5c96cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Tue, 22 Jun 2010 13:56:22 -0300 Subject: Bluetooth: Fix l2cap_sock_connect error return. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Return a proper error value if socket is already connected. Signed-off-by: João Paulo Rechi Vita Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 6b839d682143..e322be8ff948 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1158,6 +1158,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al case BT_CONNECTED: /* Already connected */ + err = -EISCONN; goto done; case BT_OPEN: -- cgit v1.2.2 From 305682e8377b0f560d4b885c169a72e6a62331e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Tue, 22 Jun 2010 13:56:23 -0300 Subject: Bluetooth: Make l2cap_streaming_send() void. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn't make sense to have a return value since we always set it to 0. Signed-off-by: João Paulo Rechi Vita Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index e322be8ff948..33e134b0d402 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1420,7 +1420,7 @@ static inline void l2cap_do_send(struct sock *sk, struct sk_buff *skb) hci_send_acl(pi->conn->hcon, skb, 0); } -static int l2cap_streaming_send(struct sock *sk) +static void l2cap_streaming_send(struct sock *sk) { struct sk_buff *skb, *tx_skb; struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -1450,7 +1450,6 @@ static int l2cap_streaming_send(struct sock *sk) skb = skb_dequeue(TX_QUEUE(sk)); kfree_skb(skb); } - return 0; } static void l2cap_retransmit_one_frame(struct sock *sk, u8 tx_seq) @@ -1866,7 +1865,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms } if (pi->mode == L2CAP_MODE_STREAMING) { - err = l2cap_streaming_send(sk); + l2cap_streaming_send(sk); } else { if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY && pi->conn_state && L2CAP_CONN_WAIT_F) { -- cgit v1.2.2 From f9dd11b03c5c3cd6bdf2e503400bbc922c898974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Tue, 22 Jun 2010 13:56:24 -0300 Subject: Bluetooth: Fix error return value on sendmsg. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we try to send a message bigger than the outgoing MTU value EMSGSIZE (message too long) should be returned. Signed-off-by: João Paulo Rechi Vita Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 33e134b0d402..884b840081ae 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1827,7 +1827,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms case L2CAP_MODE_BASIC: /* Check outgoing MTU */ if (len > pi->omtu) { - err = -EINVAL; + err = -EMSGSIZE; goto done; } -- cgit v1.2.2 From bc766db2ef3700ae74bdfc88d74b771b97971a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Tue, 22 Jun 2010 13:56:25 -0300 Subject: Bluetooth: Fix error return value on sendmsg. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the socket is in a bad state EBADFD is more appropriate then EINVAL. Signed-off-by: João Paulo Rechi Vita Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 884b840081ae..ac25952538fd 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1881,7 +1881,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms default: BT_DBG("bad state %1.1x", pi->mode); - err = -EINVAL; + err = -EBADFD; } done: -- cgit v1.2.2 From 57d3b22bf56579bb1ab2d6f5020d372c99a7afae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Tue, 22 Jun 2010 13:56:26 -0300 Subject: Bluetooth: Fix error return for l2cap_connect_rsp(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Paulo Rechi Vita Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index ac25952538fd..58c81cbb4040 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2986,11 +2986,11 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd if (scid) { sk = l2cap_get_chan_by_scid(&conn->chan_list, scid); if (!sk) - return 0; + return -EFAULT; } else { sk = l2cap_get_chan_by_ident(&conn->chan_list, cmd->ident); if (!sk) - return 0; + return -EFAULT; } switch (result) { -- cgit v1.2.2 From 7a560e5c99dc5f03e2c0dbe05ed20008af5d0bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Tue, 22 Jun 2010 13:56:27 -0300 Subject: Bluetooth: Fix error value for wrong FCS. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Paulo Rechi Vita Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 58c81cbb4040..de545f1687d8 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3423,7 +3423,7 @@ static int l2cap_check_fcs(struct l2cap_pinfo *pi, struct sk_buff *skb) our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size); if (our_fcs != rcv_fcs) - return -EINVAL; + return -EBADMSG; } return 0; } -- cgit v1.2.2 From 963cf687e825f7a59817f145a1ea19bdc224a18f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Tue, 22 Jun 2010 13:56:28 -0300 Subject: Bluetooth: Fix error return on L2CAP-HCI interface. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L2CAP only deals with ACL links. EINVAL should be returned otherwise. Signed-off-by: João Paulo Rechi Vita Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index de545f1687d8..a3dfee97ab9d 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -4415,7 +4415,7 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) struct hlist_node *node; if (type != ACL_LINK) - return 0; + return -EINVAL; BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); @@ -4448,7 +4448,7 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status) BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); if (hcon->type != ACL_LINK) - return 0; + return -EINVAL; if (!status) { conn = l2cap_conn_add(hcon, status); @@ -4477,7 +4477,7 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) BT_DBG("hcon %p reason %d", hcon, reason); if (hcon->type != ACL_LINK) - return 0; + return -EINVAL; l2cap_conn_del(hcon, bt_err(reason)); -- cgit v1.2.2 From e9aeb2ddd441f0c8699ff04c499d7213730a0f04 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 8 Jul 2010 20:08:18 -0300 Subject: Bluetooth: Send ConfigReq after send a ConnectionRsp The extended L2CAP features requires that one should initiate a ConfigReq after send the ConnectionRsp. This patch changes the behaviour of the configuration process of our stack. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index a3dfee97ab9d..e366be0792cb 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -74,6 +74,7 @@ static void __l2cap_sock_close(struct sock *sk, int reason); static void l2cap_sock_close(struct sock *sk); static void l2cap_sock_kill(struct sock *sk); +static int l2cap_build_conf_req(struct sock *sk, void *data); static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data); @@ -548,6 +549,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) } } else if (sk->sk_state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; + char buf[128]; rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); @@ -570,6 +572,17 @@ static void l2cap_conn_start(struct l2cap_conn *conn) l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); + + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT || + rsp.result != L2CAP_CR_SUCCESS) { + bh_unlock_sock(sk); + continue; + } + + l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, + l2cap_build_conf_req(sk, buf), buf); + l2cap_pi(sk)->num_conf_req++; } bh_unlock_sock(sk); @@ -1897,6 +1910,8 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) { struct l2cap_conn_rsp rsp; + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + u8 buf[128]; sk->sk_state = BT_CONFIG; @@ -1907,6 +1922,16 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) { + release_sock(sk); + return 0; + } + + l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, + l2cap_build_conf_req(sk, buf), buf); + l2cap_pi(sk)->num_conf_req++; + release_sock(sk); return 0; } @@ -2613,7 +2638,7 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) } } - if (pi->num_conf_rsp || pi->num_conf_req) + if (pi->num_conf_rsp || pi->num_conf_req > 1) goto done; switch (pi->mode) { @@ -2857,7 +2882,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd struct l2cap_chan_list *list = &conn->chan_list; struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; struct l2cap_conn_rsp rsp; - struct sock *sk, *parent; + struct sock *parent, *uninitialized_var(sk); int result, status = L2CAP_CS_NO_INFO; u16 dcid = 0, scid = __le16_to_cpu(req->scid); @@ -2966,6 +2991,15 @@ sendresp: L2CAP_INFO_REQ, sizeof(info), &info); } + if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) && + result == L2CAP_CR_SUCCESS) { + u8 buf[128]; + l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, + l2cap_build_conf_req(sk, buf), buf); + l2cap_pi(sk)->num_conf_req++; + } + return 0; } @@ -2998,9 +3032,13 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd sk->sk_state = BT_CONFIG; l2cap_pi(sk)->ident = 0; l2cap_pi(sk)->dcid = dcid; - l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_CONNECT_PEND; + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) + break; + + l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req); l2cap_pi(sk)->num_conf_req++; -- cgit v1.2.2 From 89746b856c88af9e5019e84615d88e002fb54dc3 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 9 Jul 2010 16:38:34 -0300 Subject: Bluetooth: Fix bug in kzalloc allocation size Probably a typo error. We were using the wrong struct to get size. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index e366be0792cb..419e2c3306aa 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -530,7 +530,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) conn->feat_mask) && l2cap_pi(sk)->conf_state & L2CAP_CONF_STATE2_DEVICE) { - tmp1 = kzalloc(sizeof(struct srej_list), + tmp1 = kzalloc(sizeof(struct sock_del_list), GFP_ATOMIC); tmp1->sk = sk; list_add_tail(&tmp1->list, &del.list); -- cgit v1.2.2 From 47731de789749c9ed3c54751db28fd9c9eeaf019 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 9 Jul 2010 16:38:35 -0300 Subject: Bluetooth: Keep code under column 80 Purely a cosmetic change, it doesn't change the code flow. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 419e2c3306aa..449cbdd4ddbb 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -522,31 +522,35 @@ static void l2cap_conn_start(struct l2cap_conn *conn) } if (sk->sk_state == BT_CONNECT) { - if (l2cap_check_security(sk) && - __l2cap_no_conn_pending(sk)) { - struct l2cap_conn_req req; + struct l2cap_conn_req req; - if (!l2cap_mode_supported(l2cap_pi(sk)->mode, - conn->feat_mask) - && l2cap_pi(sk)->conf_state & - L2CAP_CONF_STATE2_DEVICE) { - tmp1 = kzalloc(sizeof(struct sock_del_list), - GFP_ATOMIC); - tmp1->sk = sk; - list_add_tail(&tmp1->list, &del.list); - bh_unlock_sock(sk); - continue; - } + if (!l2cap_check_security(sk) || + !__l2cap_no_conn_pending(sk)) { + bh_unlock_sock(sk); + continue; + } - req.scid = cpu_to_le16(l2cap_pi(sk)->scid); - req.psm = l2cap_pi(sk)->psm; + if (!l2cap_mode_supported(l2cap_pi(sk)->mode, + conn->feat_mask) + && l2cap_pi(sk)->conf_state & + L2CAP_CONF_STATE2_DEVICE) { + tmp1 = kzalloc(sizeof(struct sock_del_list), + GFP_ATOMIC); + tmp1->sk = sk; + list_add_tail(&tmp1->list, &del.list); + bh_unlock_sock(sk); + continue; + } - l2cap_pi(sk)->ident = l2cap_get_ident(conn); - l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); + req.psm = l2cap_pi(sk)->psm; + + l2cap_pi(sk)->ident = l2cap_get_ident(conn); + l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; + + l2cap_send_cmd(conn, l2cap_pi(sk)->ident, + L2CAP_CONN_REQ, sizeof(req), &req); - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, - L2CAP_CONN_REQ, sizeof(req), &req); - } } else if (sk->sk_state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; char buf[128]; -- cgit v1.2.2 From ce5706bd69be6b25715ed6cd48a210b5080032bc Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 13 Jul 2010 11:57:11 -0300 Subject: Bluetooth: Add Copyright notice to L2CAP Copyright for the time I worked on L2CAP during the Google Summer of Code program. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 449cbdd4ddbb..67a6f59873aa 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1,6 +1,7 @@ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated + Copyright (C) 2009-2010 Gustavo F. Padovan Written 2000,2001 by Maxim Krasnyansky -- cgit v1.2.2 From dd135240e8e10295f7e7cdf347800df6e1841437 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 13 Jul 2010 11:57:12 -0300 Subject: Bluetooth: Update L2CAP version information We did some changes on the L2CAP configuration process and its behaviour is bit different now. That justifies a updated on the L2CAP version. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 67a6f59873aa..cdd608d72741 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -54,7 +54,7 @@ #include #include -#define VERSION "2.14" +#define VERSION "2.15" static int enable_ertm = 0; -- cgit v1.2.2 From 5d8868ff3d11e3fc5a5c07477f281a16c71714a3 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 16 Jul 2010 16:18:39 -0300 Subject: Bluetooth: Add Google's copyright to L2CAP Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index cdd608d72741..d175cc262833 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2,6 +2,7 @@ BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Copyright (C) 2009-2010 Gustavo F. Padovan + Copyright (C) 2010 Google Inc. Written 2000,2001 by Maxim Krasnyansky -- cgit v1.2.2 From d1c4a17d58a6dfacb48935aa430aa986559a885f Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sun, 18 Jul 2010 16:25:54 -0300 Subject: Bluetooth: Enable L2CAP Extended features by default Change the enable_ertm param to disable_ertm and default value to 0. That means that L2CAP Extended features are enabled by default now. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index d175cc262833..9ba1e8eee37c 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -57,7 +57,7 @@ #define VERSION "2.15" -static int enable_ertm = 0; +static int disable_ertm = 0; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; static u8 l2cap_fixed_chan[8] = { 0x02, }; @@ -464,7 +464,7 @@ static void l2cap_do_start(struct sock *sk) static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) { u32 local_feat_mask = l2cap_feat_mask; - if (enable_ertm) + if (!disable_ertm) local_feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING; switch (mode) { @@ -903,7 +903,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) } else { pi->imtu = L2CAP_DEFAULT_MTU; pi->omtu = 0; - if (enable_ertm && sk->sk_type == SOCK_STREAM) { + if (!disable_ertm && sk->sk_type == SOCK_STREAM) { pi->mode = L2CAP_MODE_ERTM; pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; } else { @@ -1160,7 +1160,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: - if (enable_ertm) + if (!disable_ertm) break; /* fall through */ default: @@ -1226,7 +1226,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: - if (enable_ertm) + if (!disable_ertm) break; /* fall through */ default: @@ -1986,7 +1986,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: - if (enable_ertm) + if (!disable_ertm) break; /* fall through */ default: @@ -3302,7 +3302,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK); rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); - if (enable_ertm) + if (!disable_ertm) feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING | L2CAP_FEAT_FCS; put_unaligned_le32(feat_mask, rsp->data); @@ -4850,8 +4850,8 @@ EXPORT_SYMBOL(l2cap_load); module_init(l2cap_init); module_exit(l2cap_exit); -module_param(enable_ertm, bool, 0644); -MODULE_PARM_DESC(enable_ertm, "Enable enhanced retransmission mode"); +module_param(disable_ertm, bool, 0644); +MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode"); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION); -- cgit v1.2.2 From 6340650400525a9ca8d86b1b4501cc50670dce0d Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 3 Aug 2010 23:49:29 -0300 Subject: Bluetooth: Don't send RFC for Basic Mode if only it is supported If the remote side doesn't support Enhanced Retransmission Mode neither Streaming Mode, we shall not send the RFC option. Some devices that only supports Basic Mode do not understanding the RFC option. This patch fixes the regression found with these devices. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 9ba1e8eee37c..0f34e1275147 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2527,6 +2527,10 @@ done: if (pi->imtu != L2CAP_DEFAULT_MTU) l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu); + if (!(pi->conn->feat_mask & L2CAP_FEAT_ERTM) && + !(pi->conn->feat_mask & L2CAP_FEAT_STREAMING)) + break; + rfc.mode = L2CAP_MODE_BASIC; rfc.txwin_size = 0; rfc.max_transmit = 0; @@ -2534,6 +2538,8 @@ done: rfc.monitor_timeout = 0; rfc.max_pdu_size = 0; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), + (unsigned long) &rfc); break; case L2CAP_MODE_ERTM: @@ -2546,6 +2552,9 @@ done: if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10) rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), + (unsigned long) &rfc); + if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS)) break; @@ -2566,6 +2575,9 @@ done: if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10) rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), + (unsigned long) &rfc); + if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS)) break; @@ -2577,9 +2589,6 @@ done: break; } - 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); -- cgit v1.2.2 From adb08edea0119f7a5484a9f6a385fbcecdf85a63 Mon Sep 17 00:00:00 2001 From: Ville Tervo Date: Wed, 4 Aug 2010 09:43:33 +0300 Subject: Bluetooth: Check result code of L2CAP information response Check result code of L2CAP information response. Otherwise it would read invalid feature mask and access invalid memory. Signed-off-by: Ville Tervo Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 0f34e1275147..3e3cd9d4e52c 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3348,6 +3348,15 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm del_timer(&conn->info_timer); + if (result != L2CAP_IR_SUCCESS) { + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; + + l2cap_conn_start(conn); + + return 0; + } + if (type == L2CAP_IT_FEAT_MASK) { conn->feat_mask = get_unaligned_le32(rsp->data); -- cgit v1.2.2 From 86b1b26326279299c93ddb11ab4782d3896bf84c Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Thu, 5 Aug 2010 15:54:22 -0700 Subject: Bluetooth: Fix endianness issue with L2CAP MPS configuration Incoming configuration values must be converted to native CPU order before use. This fixes a bug where a little-endian MPS value is compared to a native CPU value. On big-endian processors, this can cause ERTM and streaming mode segmentation to produce PDUs that are larger than the remote stack is expecting, or that would produce fragmented skbs that the current FCS code cannot handle. Signed-off-by: Mat Martineau Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 3e3cd9d4e52c..9d1f1544d9d3 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2705,8 +2705,9 @@ done: case L2CAP_MODE_ERTM: pi->remote_tx_win = rfc.txwin_size; pi->remote_max_tx = rfc.max_transmit; - if (rfc.max_pdu_size > pi->conn->mtu - 10) - rfc.max_pdu_size = le16_to_cpu(pi->conn->mtu - 10); + + if (le16_to_cpu(rfc.max_pdu_size) > pi->conn->mtu - 10) + rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); pi->remote_mps = le16_to_cpu(rfc.max_pdu_size); @@ -2723,8 +2724,8 @@ done: break; case L2CAP_MODE_STREAMING: - if (rfc.max_pdu_size > pi->conn->mtu - 10) - rfc.max_pdu_size = le16_to_cpu(pi->conn->mtu - 10); + if (le16_to_cpu(rfc.max_pdu_size) > pi->conn->mtu - 10) + rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); pi->remote_mps = le16_to_cpu(rfc.max_pdu_size); -- cgit v1.2.2 From cff70fae111efba80c27023772ce5265797fb514 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Thu, 5 Aug 2010 15:54:23 -0700 Subject: Bluetooth: Fix incorrect setting of remote_tx_win for L2CAP ERTM remote_tx_win is intended to be set on receipt of an L2CAP configuration request. The value is used to determine the size of the transmit window on the remote side of an ERTM connection, so L2CAP can stop sending frames when that remote window is full. An incorrect remote_tx_win value will cause the stack to not fully utilize the tx window (performance impact), or to overfill the remote tx window (causing dropped frames or a disconnect). This patch removes an extra setting of remote_tx_win when a configuration response is received. The transmit window has a different meaning in a response - it is an informational value less than or equal to the local tx_win. Signed-off-by: Mat Martineau Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 9d1f1544d9d3..fadf26b4ed7c 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2807,7 +2807,6 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, if (*result == L2CAP_CONF_SUCCESS) { switch (rfc.mode) { case L2CAP_MODE_ERTM: - pi->remote_tx_win = rfc.txwin_size; pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); pi->mps = le16_to_cpu(rfc.max_pdu_size); @@ -2863,7 +2862,6 @@ static void l2cap_conf_rfc_get(struct sock *sk, void *rsp, int len) done: switch (rfc.mode) { case L2CAP_MODE_ERTM: - pi->remote_tx_win = rfc.txwin_size; pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); pi->mps = le16_to_cpu(rfc.max_pdu_size); -- cgit v1.2.2 From 8c462b6047da80491b8cb6be878e8bf9313ac3e1 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 24 Aug 2010 15:35:42 -0700 Subject: Bluetooth: Only enable L2CAP FCS for ERTM or streaming This fixes a bug which caused the FCS setting to show L2CAP_FCS_CRC16 with L2CAP modes other than ERTM or streaming. At present, this only affects the FCS value shown with getsockopt() for basic mode. Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index fadf26b4ed7c..c7847035562b 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3071,6 +3071,17 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd return 0; } +static inline void set_default_fcs(struct l2cap_pinfo *pi) +{ + /* FCS is enabled only in ERTM or streaming mode, if one or both + * sides request it. + */ + if (pi->mode != L2CAP_MODE_ERTM && pi->mode != L2CAP_MODE_STREAMING) + pi->fcs = L2CAP_FCS_NONE; + else if (!(pi->conf_state & L2CAP_CONF_NO_FCS_RECV)) + pi->fcs = L2CAP_FCS_CRC16; +} + static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) { struct l2cap_conf_req *req = (struct l2cap_conf_req *) data; @@ -3135,9 +3146,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr goto unlock; if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) { - if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_NO_FCS_RECV) || - l2cap_pi(sk)->fcs != L2CAP_FCS_NONE) - l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16; + set_default_fcs(l2cap_pi(sk)); sk->sk_state = BT_CONNECTED; @@ -3225,9 +3234,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_pi(sk)->conf_state & L2CAP_CONF_NO_FCS_RECV) || - l2cap_pi(sk)->fcs != L2CAP_FCS_NONE) - l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16; + set_default_fcs(l2cap_pi(sk)); sk->sk_state = BT_CONNECTED; l2cap_pi(sk)->next_tx_seq = 0; -- cgit v1.2.2 From 8183b775bc5b79b6b1e250019c9dd930554dfa94 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 1 Sep 2010 15:17:25 +0300 Subject: Bluetooth: fix MTU L2CAP configuration parameter When receiving L2CAP negative configuration response with respect to MTU parameter we modify wrong field. MTU here means proposed value of MTU that the remote device intends to transmit. So for local L2CAP socket it is pi->imtu. Signed-off-by: Andrei Emeltchenko Acked-by: Ville Tervo Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index c7847035562b..9fad312e53f2 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2771,10 +2771,10 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, case L2CAP_CONF_MTU: if (val < L2CAP_DEFAULT_MIN_MTU) { *result = L2CAP_CONF_UNACCEPT; - pi->omtu = L2CAP_DEFAULT_MIN_MTU; + pi->imtu = L2CAP_DEFAULT_MIN_MTU; } else - pi->omtu = val; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu); + pi->imtu = val; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu); break; case L2CAP_CONF_FLUSH_TO: -- cgit v1.2.2 From ccbb84af28594e19fd4bf27ff2828c80d03b6081 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 30 Aug 2010 18:44:44 -0300 Subject: Bluetooth: Simplify L2CAP Streaming mode sending As we don't have any error control on the Streaming mode, i.e., we don't need to keep a copy of the skb for later resending we don't need to call skb_clone() on it. Then we can go one further here, and dequeue the skb before sending it, that also means we don't need to look to sk->sk_send_head anymore. The patch saves memory and time when sending Streaming mode data, so it is good to mainline. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 9fad312e53f2..f2062aace406 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1441,33 +1441,23 @@ static inline void l2cap_do_send(struct sock *sk, struct sk_buff *skb) static void l2cap_streaming_send(struct sock *sk) { - struct sk_buff *skb, *tx_skb; + struct sk_buff *skb; struct l2cap_pinfo *pi = l2cap_pi(sk); u16 control, fcs; - while ((skb = sk->sk_send_head)) { - tx_skb = skb_clone(skb, GFP_ATOMIC); - - control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + while ((skb = skb_dequeue(TX_QUEUE(sk)))) { + control = get_unaligned_le16(skb->data + L2CAP_HDR_SIZE); control |= pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; - put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + put_unaligned_le16(control, skb->data + L2CAP_HDR_SIZE); if (pi->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 *)skb->data, skb->len - 2); + put_unaligned_le16(fcs, skb->data + skb->len - 2); } - l2cap_do_send(sk, tx_skb); + l2cap_do_send(sk, skb); pi->next_tx_seq = (pi->next_tx_seq + 1) % 64; - - if (skb_queue_is_last(TX_QUEUE(sk), skb)) - sk->sk_send_head = NULL; - else - sk->sk_send_head = skb_queue_next(TX_QUEUE(sk), skb); - - skb = skb_dequeue(TX_QUEUE(sk)); - kfree_skb(skb); } } -- cgit v1.2.2 From b0239c80fe89d5832a68a0f3121a9d5ec9fb763e Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 8 Sep 2010 14:59:44 -0300 Subject: Revert "Bluetooth: Don't accept ConfigReq if we aren't in the BT_CONFIG state" This reverts commit 8cb8e6f1684be13b51f8429b15f39c140326b327. That commit introduced a regression with the Bluetooth Profile Tuning Suite(PTS), Reverting this make sure that L2CAP is in a qualificable state. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index f2062aace406..44a8fb0d6c29 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3089,14 +3089,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (!sk) return -ENOENT; - if (sk->sk_state != BT_CONFIG) { - struct l2cap_cmd_rej rej; - - rej.reason = cpu_to_le16(0x0002); - l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ, - sizeof(rej), &rej); + if (sk->sk_state == BT_DISCONN) goto unlock; - } /* Reject if config buffer is too small. */ len = cmd_len - sizeof(*req); -- cgit v1.2.2 From eaa71b318c5ed0cd1ac3182a533471dc5edf372d Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 4 Oct 2010 19:28:52 -0300 Subject: Bluetooth: Disallow to change L2CAP_OPTIONS values when connected L2CAP doesn't permit change like MTU, FCS, TxWindow values while the connection is alive, we can only set that before the connection/configuration process. That can lead to bugs in the L2CAP operation. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'net/bluetooth/l2cap.c') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 44a8fb0d6c29..0b54b7dd8401 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1950,6 +1950,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us switch (optname) { case L2CAP_OPTIONS: + if (sk->sk_state == BT_CONNECTED) { + err = -EINVAL; + break; + } + opts.imtu = l2cap_pi(sk)->imtu; opts.omtu = l2cap_pi(sk)->omtu; opts.flush_to = l2cap_pi(sk)->flush_to; -- cgit v1.2.2