[Bluetooth] Add automatic sniff mode support

This patch introduces the automatic sniff mode feature. This allows
the host to switch idle connections into sniff mode to safe power.

Signed-off-by: Ulisses Furquim <ulissesf@gmail.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 51f8670..729461f 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -48,7 +48,7 @@
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "2.8"
+#define VERSION "2.9"
 
 /* Bluetooth sockets */
 #define BT_MAX_PROTO	8
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 5c0c2b1..420ed4d 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -115,8 +115,8 @@
 
 static void hci_conn_timeout(unsigned long arg)
 {
-	struct hci_conn *conn = (void *)arg;
-	struct hci_dev  *hdev = conn->hdev;
+	struct hci_conn *conn = (void *) arg;
+	struct hci_dev *hdev = conn->hdev;
 
 	BT_DBG("conn %p state %d", conn, conn->state);
 
@@ -132,11 +132,13 @@
 	return;
 }
 
-static void hci_conn_init_timer(struct hci_conn *conn)
+static void hci_conn_idle(unsigned long arg)
 {
-	init_timer(&conn->timer);
-	conn->timer.function = hci_conn_timeout;
-	conn->timer.data = (unsigned long)conn;
+	struct hci_conn *conn = (void *) arg;
+
+	BT_DBG("conn %p mode %d", conn, conn->mode);
+
+	hci_conn_enter_sniff_mode(conn);
 }
 
 struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
@@ -145,17 +147,27 @@
 
 	BT_DBG("%s dst %s", hdev->name, batostr(dst));
 
-	if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC)))
+	conn = kzalloc(sizeof(struct hci_conn), GFP_ATOMIC);
+	if (!conn)
 		return NULL;
-	memset(conn, 0, sizeof(struct hci_conn));
 
 	bacpy(&conn->dst, dst);
-	conn->type   = type;
 	conn->hdev   = hdev;
+	conn->type   = type;
+	conn->mode   = HCI_CM_ACTIVE;
 	conn->state  = BT_OPEN;
 
+	conn->power_save = 1;
+
 	skb_queue_head_init(&conn->data_q);
-	hci_conn_init_timer(conn);
+
+	init_timer(&conn->disc_timer);
+	conn->disc_timer.function = hci_conn_timeout;
+	conn->disc_timer.data = (unsigned long) conn;
+
+	init_timer(&conn->idle_timer);
+	conn->idle_timer.function = hci_conn_idle;
+	conn->idle_timer.data = (unsigned long) conn;
 
 	atomic_set(&conn->refcnt, 0);
 
@@ -178,7 +190,9 @@
 
 	BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
 
-	hci_conn_del_timer(conn);
+	del_timer(&conn->idle_timer);
+
+	del_timer(&conn->disc_timer);
 
 	if (conn->type == SCO_LINK) {
 		struct hci_conn *acl = conn->link;
@@ -364,6 +378,70 @@
 }
 EXPORT_SYMBOL(hci_conn_switch_role);
 
+/* Enter active mode */
+void hci_conn_enter_active_mode(struct hci_conn *conn)
+{
+	struct hci_dev *hdev = conn->hdev;
+
+	BT_DBG("conn %p mode %d", conn, conn->mode);
+
+	if (test_bit(HCI_RAW, &hdev->flags))
+		return;
+
+	if (conn->mode != HCI_CM_SNIFF || !conn->power_save)
+		goto timer;
+
+	if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+		struct hci_cp_exit_sniff_mode cp;
+		cp.handle = __cpu_to_le16(conn->handle);
+		hci_send_cmd(hdev, OGF_LINK_POLICY,
+				OCF_EXIT_SNIFF_MODE, sizeof(cp), &cp);
+	}
+
+timer:
+	if (hdev->idle_timeout > 0)
+		mod_timer(&conn->idle_timer,
+			jiffies + msecs_to_jiffies(hdev->idle_timeout));
+}
+
+/* Enter sniff mode */
+void hci_conn_enter_sniff_mode(struct hci_conn *conn)
+{
+	struct hci_dev *hdev = conn->hdev;
+
+	BT_DBG("conn %p mode %d", conn, conn->mode);
+
+	if (test_bit(HCI_RAW, &hdev->flags))
+		return;
+
+	if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn))
+		return;
+
+	if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF))
+		return;
+
+	if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) {
+		struct hci_cp_sniff_subrate cp;
+		cp.handle             = __cpu_to_le16(conn->handle);
+		cp.max_latency        = __constant_cpu_to_le16(0);
+		cp.min_remote_timeout = __constant_cpu_to_le16(0);
+		cp.min_local_timeout  = __constant_cpu_to_le16(0);
+		hci_send_cmd(hdev, OGF_LINK_POLICY,
+				OCF_SNIFF_SUBRATE, sizeof(cp), &cp);
+	}
+
+	if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+		struct hci_cp_sniff_mode cp;
+		cp.handle       = __cpu_to_le16(conn->handle);
+		cp.max_interval = __cpu_to_le16(hdev->sniff_max_interval);
+		cp.min_interval = __cpu_to_le16(hdev->sniff_min_interval);
+		cp.attempt      = __constant_cpu_to_le16(4);
+		cp.timeout      = __constant_cpu_to_le16(1);
+		hci_send_cmd(hdev, OGF_LINK_POLICY,
+				OCF_SNIFF_MODE, sizeof(cp), &cp);
+	}
+}
+
 /* Drop all connection on the device */
 void hci_conn_hash_flush(struct hci_dev *hdev)
 {
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index f67240b..3f9f156 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -411,7 +411,7 @@
 	}
 	hci_dev_unlock_bh(hdev);
 
-	timeo = ir.length * 2 * HZ;
+	timeo = ir.length * msecs_to_jiffies(2000);
 	if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
 		goto done;
 
@@ -479,7 +479,8 @@
 		set_bit(HCI_INIT, &hdev->flags);
 
 		//__hci_request(hdev, hci_reset_req, 0, HZ);
-		ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
+		ret = __hci_request(hdev, hci_init_req, 0,
+					msecs_to_jiffies(HCI_INIT_TIMEOUT));
 
 		clear_bit(HCI_INIT, &hdev->flags);
 	}
@@ -546,7 +547,8 @@
 	atomic_set(&hdev->cmd_cnt, 1);
 	if (!test_bit(HCI_RAW, &hdev->flags)) {
 		set_bit(HCI_INIT, &hdev->flags);
-		__hci_request(hdev, hci_reset_req, 0, HZ/4);
+		__hci_request(hdev, hci_reset_req, 0,
+					msecs_to_jiffies(250));
 		clear_bit(HCI_INIT, &hdev->flags);
 	}
 
@@ -619,7 +621,8 @@
 	hdev->acl_cnt = 0; hdev->sco_cnt = 0;
 
 	if (!test_bit(HCI_RAW, &hdev->flags))
-		ret = __hci_request(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT);
+		ret = __hci_request(hdev, hci_reset_req, 0,
+					msecs_to_jiffies(HCI_INIT_TIMEOUT));
 
 done:
 	tasklet_enable(&hdev->tx_task);
@@ -657,7 +660,8 @@
 
 	switch (cmd) {
 	case HCISETAUTH:
-		err = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+		err = hci_request(hdev, hci_auth_req, dr.dev_opt,
+					msecs_to_jiffies(HCI_INIT_TIMEOUT));
 		break;
 
 	case HCISETENCRYPT:
@@ -668,18 +672,19 @@
 
 		if (!test_bit(HCI_AUTH, &hdev->flags)) {
 			/* Auth must be enabled first */
-			err = hci_request(hdev, hci_auth_req,
-					dr.dev_opt, HCI_INIT_TIMEOUT);
+			err = hci_request(hdev, hci_auth_req, dr.dev_opt,
+					msecs_to_jiffies(HCI_INIT_TIMEOUT));
 			if (err)
 				break;
 		}
 
-		err = hci_request(hdev, hci_encrypt_req,
-					dr.dev_opt, HCI_INIT_TIMEOUT);
+		err = hci_request(hdev, hci_encrypt_req, dr.dev_opt,
+					msecs_to_jiffies(HCI_INIT_TIMEOUT));
 		break;
 
 	case HCISETSCAN:
-		err = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+		err = hci_request(hdev, hci_scan_req, dr.dev_opt,
+					msecs_to_jiffies(HCI_INIT_TIMEOUT));
 		break;
 
 	case HCISETPTYPE:
@@ -848,6 +853,10 @@
 	hdev->pkt_type  = (HCI_DM1 | HCI_DH1 | HCI_HV1);
 	hdev->link_mode = (HCI_LM_ACCEPT);
 
+	hdev->idle_timeout = 0;
+	hdev->sniff_max_interval = 800;
+	hdev->sniff_min_interval = 80;
+
 	tasklet_init(&hdev->cmd_task, hci_cmd_task,(unsigned long) hdev);
 	tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
 	tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev);
@@ -1220,6 +1229,9 @@
 	while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
 		while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
 			BT_DBG("skb %p len %d", skb, skb->len);
+
+			hci_conn_enter_active_mode(conn);
+
 			hci_send_frame(skb);
 			hdev->acl_last_tx = jiffies;
 
@@ -1298,6 +1310,8 @@
 	if (conn) {
 		register struct hci_proto *hp;
 
+		hci_conn_enter_active_mode(conn);
+
 		/* Send to upper protocol */
 		if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) {
 			hp->recv_acldata(conn, skb, flags);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index f41cf1a..3896dab 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -83,6 +83,8 @@
 {
 	struct hci_conn *conn;
 	struct hci_rp_role_discovery *rd;
+	struct hci_rp_write_link_policy *lp;
+	void *sent;
 
 	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
 
@@ -106,6 +108,27 @@
 		hci_dev_unlock(hdev);
 		break;
 
+	case OCF_WRITE_LINK_POLICY:
+		sent = hci_sent_cmd_data(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY);
+		if (!sent)
+			break;
+
+		lp = (struct hci_rp_write_link_policy *) skb->data;
+
+		if (lp->status)
+			break;
+
+		hci_dev_lock(hdev);
+
+		conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(lp->handle));
+		if (conn) {
+			__le16 policy = get_unaligned((__le16 *) (sent + 2));
+			conn->link_policy = __le16_to_cpu(policy);
+		}
+
+		hci_dev_unlock(hdev);
+		break;
+
 	default:
 		BT_DBG("%s: Command complete: ogf LINK_POLICY ocf %x", 
 				hdev->name, ocf);
@@ -274,7 +297,7 @@
 /* Command Complete OGF INFO_PARAM  */
 static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
 {
-	struct hci_rp_read_loc_features *lf;
+	struct hci_rp_read_local_features *lf;
 	struct hci_rp_read_buffer_size *bs;
 	struct hci_rp_read_bd_addr *ba;
 
@@ -282,7 +305,7 @@
 
 	switch (ocf) {
 	case OCF_READ_LOCAL_FEATURES:
-		lf = (struct hci_rp_read_loc_features *) skb->data;
+		lf = (struct hci_rp_read_local_features *) skb->data;
 
 		if (lf->status) {
 			BT_DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status);
@@ -447,8 +470,46 @@
 	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
 
 	switch (ocf) {
+	case OCF_SNIFF_MODE:
+		if (status) {
+			struct hci_conn *conn;
+			struct hci_cp_sniff_mode *cp = hci_sent_cmd_data(hdev, OGF_LINK_POLICY, OCF_SNIFF_MODE);
+
+			if (!cp)
+				break;
+
+			hci_dev_lock(hdev);
+
+			conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+			if (conn) {
+				clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend);
+			}
+
+			hci_dev_unlock(hdev);
+		}
+		break;
+
+	case OCF_EXIT_SNIFF_MODE:
+		if (status) {
+			struct hci_conn *conn;
+			struct hci_cp_exit_sniff_mode *cp = hci_sent_cmd_data(hdev, OGF_LINK_POLICY, OCF_EXIT_SNIFF_MODE);
+
+			if (!cp)
+				break;
+
+			hci_dev_lock(hdev);
+
+			conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+			if (conn) {
+				clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend);
+			}
+
+			hci_dev_unlock(hdev);
+		}
+		break;
+
 	default:
-		BT_DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf);
+		BT_DBG("%s Command status: ogf LINK_POLICY ocf %x", hdev->name, ocf);
 		break;
 	}
 }
@@ -630,14 +691,16 @@
 		else
 			cp.role = 0x01; /* Remain slave */
 
-		hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ, sizeof(cp), &cp);
+		hci_send_cmd(hdev, OGF_LINK_CTL,
+				OCF_ACCEPT_CONN_REQ, sizeof(cp), &cp);
 	} else {
 		/* Connection rejected */
 		struct hci_cp_reject_conn_req cp;
 
 		bacpy(&cp.bdaddr, &ev->bdaddr);
 		cp.reason = 0x0f;
-		hci_send_cmd(hdev, OGF_LINK_CTL, OCF_REJECT_CONN_REQ, sizeof(cp), &cp);
+		hci_send_cmd(hdev, OGF_LINK_CTL,
+				OCF_REJECT_CONN_REQ, sizeof(cp), &cp);
 	}
 }
 
@@ -645,7 +708,7 @@
 static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_ev_conn_complete *ev = (struct hci_ev_conn_complete *) skb->data;
-	struct hci_conn *conn = NULL;
+	struct hci_conn *conn;
 
 	BT_DBG("%s", hdev->name);
 
@@ -667,12 +730,21 @@
 		if (test_bit(HCI_ENCRYPT, &hdev->flags))
 			conn->link_mode |= HCI_LM_ENCRYPT;
 
+		/* Get remote features */
+		if (conn->type == ACL_LINK) {
+			struct hci_cp_read_remote_features cp;
+			cp.handle = ev->handle;
+			hci_send_cmd(hdev, OGF_LINK_CTL,
+				OCF_READ_REMOTE_FEATURES, sizeof(cp), &cp);
+		}
+
 		/* Set link policy */
 		if (conn->type == ACL_LINK && hdev->link_policy) {
 			struct hci_cp_write_link_policy cp;
 			cp.handle = ev->handle;
 			cp.policy = __cpu_to_le16(hdev->link_policy);
-			hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY, sizeof(cp), &cp);
+			hci_send_cmd(hdev, OGF_LINK_POLICY,
+				OCF_WRITE_LINK_POLICY, sizeof(cp), &cp);
 		}
 
 		/* Set packet type for incoming connection */
@@ -683,7 +755,8 @@
 				__cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK):
 				__cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
 
-			hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_PTYPE, sizeof(cp), &cp);
+			hci_send_cmd(hdev, OGF_LINK_CTL,
+				OCF_CHANGE_CONN_PTYPE, sizeof(cp), &cp);
 		}
 	} else
 		conn->state = BT_CLOSED;
@@ -711,8 +784,7 @@
 static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_ev_disconn_complete *ev = (struct hci_ev_disconn_complete *) skb->data;
-	struct hci_conn *conn = NULL;
-	__u16 handle = __le16_to_cpu(ev->handle);
+	struct hci_conn *conn;
 
 	BT_DBG("%s status %d", hdev->name, ev->status);
 
@@ -721,7 +793,7 @@
 
 	hci_dev_lock(hdev);
 
-	conn = hci_conn_hash_lookup_handle(hdev, handle);
+	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
 	if (conn) {
 		conn->state = BT_CLOSED;
 		hci_proto_disconn_ind(conn, ev->reason);
@@ -778,7 +850,7 @@
 static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_ev_role_change *ev = (struct hci_ev_role_change *) skb->data;
-	struct hci_conn *conn = NULL;
+	struct hci_conn *conn;
 
 	BT_DBG("%s status %d", hdev->name, ev->status);
 
@@ -801,18 +873,43 @@
 	hci_dev_unlock(hdev);
 }
 
-/* Authentication Complete */
-static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+/* Mode Change */
+static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
-	struct hci_ev_auth_complete *ev = (struct hci_ev_auth_complete *) skb->data;
-	struct hci_conn *conn = NULL;
-	__u16 handle = __le16_to_cpu(ev->handle);
+	struct hci_ev_mode_change *ev = (struct hci_ev_mode_change *) skb->data;
+	struct hci_conn *conn;
 
 	BT_DBG("%s status %d", hdev->name, ev->status);
 
 	hci_dev_lock(hdev);
 
-	conn = hci_conn_hash_lookup_handle(hdev, handle);
+	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+	if (conn) {
+		conn->mode = ev->mode;
+		conn->interval = __le16_to_cpu(ev->interval);
+
+		if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+			if (conn->mode == HCI_CM_ACTIVE)
+				conn->power_save = 1;
+			else
+				conn->power_save = 0;
+		}
+	}
+
+	hci_dev_unlock(hdev);
+}
+
+/* Authentication Complete */
+static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_ev_auth_complete *ev = (struct hci_ev_auth_complete *) skb->data;
+	struct hci_conn *conn;
+
+	BT_DBG("%s status %d", hdev->name, ev->status);
+
+	hci_dev_lock(hdev);
+
+	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
 	if (conn) {
 		if (!ev->status)
 			conn->link_mode |= HCI_LM_AUTH;
@@ -827,8 +924,7 @@
 				cp.handle  = __cpu_to_le16(conn->handle);
 				cp.encrypt = 1;
 				hci_send_cmd(conn->hdev, OGF_LINK_CTL,
-						OCF_SET_CONN_ENCRYPT,
-						sizeof(cp), &cp);
+					OCF_SET_CONN_ENCRYPT, sizeof(cp), &cp);
 			} else {
 				clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
 				hci_encrypt_cfm(conn, ev->status, 0x00);
@@ -843,14 +939,13 @@
 static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_ev_encrypt_change *ev = (struct hci_ev_encrypt_change *) skb->data;
-	struct hci_conn *conn = NULL;
-	__u16 handle = __le16_to_cpu(ev->handle);
+	struct hci_conn *conn;
 
 	BT_DBG("%s status %d", hdev->name, ev->status);
 
 	hci_dev_lock(hdev);
 
-	conn = hci_conn_hash_lookup_handle(hdev, handle);
+	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
 	if (conn) {
 		if (!ev->status) {
 			if (ev->encrypt)
@@ -871,14 +966,13 @@
 static inline void hci_change_conn_link_key_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_ev_change_conn_link_key_complete *ev = (struct hci_ev_change_conn_link_key_complete *) skb->data;
-	struct hci_conn *conn = NULL;
-	__u16 handle = __le16_to_cpu(ev->handle);
+	struct hci_conn *conn;
 
 	BT_DBG("%s status %d", hdev->name, ev->status);
 
 	hci_dev_lock(hdev);
 
-	conn = hci_conn_hash_lookup_handle(hdev, handle);
+	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
 	if (conn) {
 		if (!ev->status)
 			conn->link_mode |= HCI_LM_SECURE;
@@ -906,18 +1000,35 @@
 {
 }
 
-/* Clock Offset */
-static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb)
+/* Remote Features */
+static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
-	struct hci_ev_clock_offset *ev = (struct hci_ev_clock_offset *) skb->data;
-	struct hci_conn *conn = NULL;
-	__u16 handle = __le16_to_cpu(ev->handle);
+	struct hci_ev_remote_features *ev = (struct hci_ev_remote_features *) skb->data;
+	struct hci_conn *conn;
 
 	BT_DBG("%s status %d", hdev->name, ev->status);
 
 	hci_dev_lock(hdev);
 
-	conn = hci_conn_hash_lookup_handle(hdev, handle);
+	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+	if (conn && !ev->status) {
+		memcpy(conn->features, ev->features, sizeof(conn->features));
+	}
+
+	hci_dev_unlock(hdev);
+}
+
+/* Clock Offset */
+static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_ev_clock_offset *ev = (struct hci_ev_clock_offset *) skb->data;
+	struct hci_conn *conn;
+
+	BT_DBG("%s status %d", hdev->name, ev->status);
+
+	hci_dev_lock(hdev);
+
+	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
 	if (conn && !ev->status) {
 		struct inquiry_entry *ie;
 
@@ -948,6 +1059,23 @@
 	hci_dev_unlock(hdev);
 }
 
+/* Sniff Subrate */
+static inline void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_ev_sniff_subrate *ev = (struct hci_ev_sniff_subrate *) skb->data;
+	struct hci_conn *conn;
+
+	BT_DBG("%s status %d", hdev->name, ev->status);
+
+	hci_dev_lock(hdev);
+
+	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+	if (conn) {
+	}
+
+	hci_dev_unlock(hdev);
+}
+
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_event_hdr *hdr = (struct hci_event_hdr *) skb->data;
@@ -996,6 +1124,10 @@
 		hci_role_change_evt(hdev, skb);
 		break;
 
+	case HCI_EV_MODE_CHANGE:
+		hci_mode_change_evt(hdev, skb);
+		break;
+
 	case HCI_EV_AUTH_COMPLETE:
 		hci_auth_complete_evt(hdev, skb);
 		break;
@@ -1020,6 +1152,10 @@
 		hci_link_key_notify_evt(hdev, skb);
 		break;
 
+	case HCI_EV_REMOTE_FEATURES:
+		hci_remote_features_evt(hdev, skb);
+		break;
+
 	case HCI_EV_CLOCK_OFFSET:
 		hci_clock_offset_evt(hdev, skb);
 		break;
@@ -1028,6 +1164,10 @@
 		hci_pscan_rep_mode_evt(hdev, skb);
 		break;
 
+	case HCI_EV_SNIFF_SUBRATE:
+		hci_sniff_subrate_evt(hdev, skb);
+		break;
+
 	case HCI_EV_CMD_STATUS:
 		cs = (struct hci_ev_cmd_status *) skb->data;
 		skb_pull(skb, sizeof(cs));
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 19b234c..89918d2 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -61,18 +61,106 @@
 	return n;
 }
 
+static ssize_t show_idle_timeout(struct class_device *cdev, char *buf)
+{
+	struct hci_dev *hdev = class_get_devdata(cdev);
+	return sprintf(buf, "%d\n", hdev->idle_timeout);
+}
+
+static ssize_t store_idle_timeout(struct class_device *cdev, const char *buf, size_t count)
+{
+	struct hci_dev *hdev = class_get_devdata(cdev);
+	char *ptr;
+	__u32 val;
+
+	val = simple_strtoul(buf, &ptr, 10);
+	if (ptr == buf)
+		return -EINVAL;
+
+	if (val != 0 && (val < 500 || val > 3600000))
+		return -EINVAL;
+
+	hdev->idle_timeout = val;
+
+	return count;
+}
+
+static ssize_t show_sniff_max_interval(struct class_device *cdev, char *buf)
+{
+	struct hci_dev *hdev = class_get_devdata(cdev);
+	return sprintf(buf, "%d\n", hdev->sniff_max_interval);
+}
+
+static ssize_t store_sniff_max_interval(struct class_device *cdev, const char *buf, size_t count)
+{
+	struct hci_dev *hdev = class_get_devdata(cdev);
+	char *ptr;
+	__u16 val;
+
+	val = simple_strtoul(buf, &ptr, 10);
+	if (ptr == buf)
+		return -EINVAL;
+
+	if (val < 0x0002 || val > 0xFFFE || val % 2)
+		return -EINVAL;
+
+	if (val < hdev->sniff_min_interval)
+		return -EINVAL;
+
+	hdev->sniff_max_interval = val;
+
+	return count;
+}
+
+static ssize_t show_sniff_min_interval(struct class_device *cdev, char *buf)
+{
+	struct hci_dev *hdev = class_get_devdata(cdev);
+	return sprintf(buf, "%d\n", hdev->sniff_min_interval);
+}
+
+static ssize_t store_sniff_min_interval(struct class_device *cdev, const char *buf, size_t count)
+{
+	struct hci_dev *hdev = class_get_devdata(cdev);
+	char *ptr;
+	__u16 val;
+
+	val = simple_strtoul(buf, &ptr, 10);
+	if (ptr == buf)
+		return -EINVAL;
+
+	if (val < 0x0002 || val > 0xFFFE || val % 2)
+		return -EINVAL;
+
+	if (val > hdev->sniff_max_interval)
+		return -EINVAL;
+
+	hdev->sniff_min_interval = val;
+
+	return count;
+}
+
 static CLASS_DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
 static CLASS_DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
 static CLASS_DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
 static CLASS_DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
 static CLASS_DEVICE_ATTR(inquiry_cache, S_IRUGO, show_inquiry_cache, NULL);
 
+static CLASS_DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR,
+				show_idle_timeout, store_idle_timeout);
+static CLASS_DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR,
+				show_sniff_max_interval, store_sniff_max_interval);
+static CLASS_DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR,
+				show_sniff_min_interval, store_sniff_min_interval);
+
 static struct class_device_attribute *bt_attrs[] = {
 	&class_device_attr_name,
 	&class_device_attr_type,
 	&class_device_attr_address,
 	&class_device_attr_flags,
 	&class_device_attr_inquiry_cache,
+	&class_device_attr_idle_timeout,
+	&class_device_attr_sniff_max_interval,
+	&class_device_attr_sniff_min_interval,
 	NULL
 };