diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 155c6fd091e..7e9f5f4f374 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010-2011 Code Aurora Forum. All rights reserved. + Copyright (c) 2000-2001, 2010-2012 Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -65,6 +65,7 @@ struct bt_security { #define BT_SECURITY_HIGH 3 #define BT_DEFER_SETUP 7 +#define BT_FLUSHABLE 8 #define BT_POWER 9 struct bt_power { diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 744eb729746..cd8585523c0 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010-2011 Code Aurora Forum. All rights reserved. + Copyright (c) 2000-2001, 2010-2012 Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -700,6 +700,12 @@ struct hci_cp_write_voice_setting { __le16 voice_setting; } __packed; +#define HCI_OP_WRITE_AUTOMATIC_FLUSH_TIMEOUT 0x0c28 +struct hci_cp_write_automatic_flush_timeout { + __le16 handle; + __le16 timeout; +} __packed; + #define HCI_OP_HOST_BUFFER_SIZE 0x0c33 struct hci_cp_host_buffer_size { __le16 acl_mtu; diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index b98af2899be..30536a28b69 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010-2011 Code Aurora Forum. All rights reserved. + Copyright (c) 2000-2001, 2010-2012 Code Aurora Forum. All rights reserved. Copyright (C) 2009-2010 Gustavo F. Padovan Copyright (C) 2010 Google Inc. @@ -32,6 +32,7 @@ #define L2CAP_DEFAULT_MIN_MTU 48 #define L2CAP_DEFAULT_MAX_SDU_SIZE 0xffff #define L2CAP_DEFAULT_FLUSH_TO 0xffff +#define L2CAP_MAX_FLUSH_TO 0x7ff #define L2CAP_DEFAULT_TX_WINDOW 63 #define L2CAP_DEFAULT_MAX_TX 3 #define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f311e107210..cf6cf592721 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010-2011 Code Aurora Forum. All rights reserved. + Copyright (c) 2000-2001, 2010-2012 Code Aurora Forum. All rights reserved. Copyright (C) 2009-2010 Gustavo F. Padovan Copyright (C) 2010 Google Inc. @@ -90,6 +90,8 @@ static int l2cap_create_cfm(struct hci_chan *chan, u8 status); static int l2cap_deaggregate(struct hci_chan *chan, struct l2cap_pinfo *pi); static void l2cap_chan_ready(struct sock *sk); static void l2cap_conn_del(struct hci_conn *hcon, int err); +static u16 l2cap_get_smallest_flushto(struct l2cap_chan_list *l); +static void l2cap_set_acl_flushto(struct hci_conn *hcon, u16 flush_to); /* ---- L2CAP channels ---- */ static struct sock *__l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, u16 cid) @@ -519,6 +521,11 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk) l2cap_pi(sk)->dcid = L2CAP_CID_SIGNALING; l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; } + + if (l2cap_get_smallest_flushto(l) > l2cap_pi(sk)->flush_to) { + /*if flush timeout of the channel is lesser than existing */ + l2cap_set_acl_flushto(conn->hcon, l2cap_pi(sk)->flush_to); + } /* Otherwise, do not set scid/dcid/omtu. These will be set up * by l2cap_fixed_channel_config() */ @@ -538,11 +545,18 @@ void l2cap_chan_del(struct sock *sk, int err) BT_DBG("sk %p, conn %p, err %d", sk, conn, err); if (conn) { + struct l2cap_chan_list *l = &conn->chan_list; /* Unlink from channel list */ - l2cap_chan_unlink(&conn->chan_list, sk); + l2cap_chan_unlink(l, sk); l2cap_pi(sk)->conn = NULL; if (!l2cap_pi(sk)->fixed_channel) hci_conn_put(conn->hcon); + + read_lock(&l->lock); + if (l2cap_pi(sk)->flush_to < l2cap_get_smallest_flushto(l)) + l2cap_set_acl_flushto(conn->hcon, + l2cap_get_smallest_flushto(l)); + read_unlock(&l->lock); } if (l2cap_pi(sk)->ampcon) { @@ -7590,6 +7604,33 @@ drop: return 0; } +static void l2cap_set_acl_flushto(struct hci_conn *hcon, u16 flush_to) +{ + struct hci_cp_write_automatic_flush_timeout flush_tm; + if (hcon && hcon->hdev) { + flush_tm.handle = hcon->handle; + if (flush_to == L2CAP_DEFAULT_FLUSH_TO) + flush_to = 0; + flush_tm.timeout = (flush_to < L2CAP_MAX_FLUSH_TO) ? + flush_to : L2CAP_MAX_FLUSH_TO; + hci_send_cmd(hcon->hdev, + HCI_OP_WRITE_AUTOMATIC_FLUSH_TIMEOUT, + 4, &(flush_tm)); + } +} + +static u16 l2cap_get_smallest_flushto(struct l2cap_chan_list *l) +{ + int ret_flush_to = L2CAP_DEFAULT_FLUSH_TO; + struct sock *s; + for (s = l->head; s; s = l2cap_pi(s)->next_c) { + if (l2cap_pi(s)->flush_to > 0 && + l2cap_pi(s)->flush_to < ret_flush_to) + ret_flush_to = l2cap_pi(s)->flush_to; + } + return ret_flush_to; +} + static int l2cap_debugfs_show(struct seq_file *f, void *p) { struct sock *sk; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 9beea7450eb..93e7b048812 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -626,6 +626,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us l2cap_pi(sk)->fcs = opts.fcs; l2cap_pi(sk)->max_tx = opts.max_tx; l2cap_pi(sk)->tx_win = opts.txwin_size; + l2cap_pi(sk)->flush_to = opts.flush_to; break; case L2CAP_LM: @@ -766,6 +767,15 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch break; + case BT_FLUSHABLE: + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + l2cap_pi(sk)->flushable = opt; + + break; + default: err = -ENOPROTOOPT; break;