Bluetooth: Add API to set LE Connection Parameters

Include auto-setting connection parameters during Pairing for
low latenecy, and default (high latency) settings for non-Pairing
connections.

Change-Id: Ie8e3f65da52213451dcc01fce38d95ae07d1b13b
Signed-off-by: Brian Gix <bgix@codeaurora.org>
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 127c7ca..ce7ecf1 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -219,6 +219,15 @@
 
 #define MGMT_OP_SET_LIMIT_DISCOVERABLE	0x001F
 
+#define MGMT_OP_SET_CONNECTION_PARAMS	0x0020
+struct mgmt_cp_set_connection_params {
+	bdaddr_t bdaddr;
+	__le16 interval_min;
+	__le16 interval_max;
+	__le16 slave_latency;
+	__le16 timeout_multiplier;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	__le16 opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 77845f2..094bfdb 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1699,6 +1699,48 @@
 	return err;
 }
 
+static int set_connection_params(struct sock *sk, u16 index,
+				unsigned char *data, u16 len)
+{
+	struct mgmt_cp_set_connection_params *cp = (void *) data;
+	struct hci_dev *hdev;
+	struct hci_conn *conn;
+	int err;
+
+	BT_DBG("");
+
+	if (len != sizeof(*cp))
+		return cmd_status(sk, index, MGMT_OP_SET_CONNECTION_PARAMS,
+									EINVAL);
+
+	hdev = hci_dev_get(index);
+	if (!hdev)
+		return cmd_status(sk, index, MGMT_OP_SET_CONNECTION_PARAMS,
+									ENODEV);
+
+	hci_dev_lock(hdev);
+
+	conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
+	if (!conn) {
+		err = cmd_status(sk, index, MGMT_OP_SET_CONNECTION_PARAMS,
+								ENOTCONN);
+		goto failed;
+	}
+
+	hci_le_conn_update(conn, le16_to_cpu(cp->interval_min),
+				le16_to_cpu(cp->interval_max),
+				le16_to_cpu(cp->slave_latency),
+				le16_to_cpu(cp->timeout_multiplier));
+
+	err = cmd_status(sk, index, MGMT_OP_SET_CONNECTION_PARAMS, 0);
+
+failed:
+	hci_dev_unlock(hdev);
+	hci_dev_put(hdev);
+
+	return err;
+}
+
 static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
 								u16 len)
 {
@@ -2262,6 +2304,9 @@
 	case MGMT_OP_RESOLVE_NAME:
 		err = resolve_name(sk, index, buf + sizeof(*hdr), len);
 		break;
+	case MGMT_OP_SET_CONNECTION_PARAMS:
+		err = set_connection_params(sk, index, buf + sizeof(*hdr), len);
+		break;
 	case MGMT_OP_READ_LOCAL_OOB_DATA:
 		err = read_local_oob_data(sk, index);
 		break;
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index e725148..a88f4a5 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -31,6 +31,11 @@
 
 #define SMP_TIMEOUT 30000 /* 30 seconds */
 
+#define SMP_MIN_CONN_INTERVAL	40	/* 50ms (40 * 1.25ms) */
+#define SMP_MAX_CONN_INTERVAL	56	/* 70ms (56 * 1.25ms) */
+#define SMP_MAX_CONN_LATENCY	0	/* 0ms (0 * 1.25ms) */
+#define SMP_SUPERVISION_TIMEOUT	500	/* 5 seconds (500 * 10ms) */
+
 #ifndef FALSE
 #define FALSE 0
 #define TRUE (!FALSE)
@@ -696,6 +701,10 @@
 invalid_key:
 	hcon->sec_req = FALSE;
 
+	/* Switch to Pairing Connection Parameters */
+	hci_le_conn_update(hcon, SMP_MIN_CONN_INTERVAL, SMP_MAX_CONN_INTERVAL,
+			SMP_MAX_CONN_LATENCY, SMP_SUPERVISION_TIMEOUT);
+
 	skb_pull(skb, sizeof(*rp));
 
 	memset(&cp, 0, sizeof(cp));
@@ -763,6 +772,11 @@
 	if (hcon->link_mode & HCI_LM_MASTER) {
 		struct smp_cmd_pairing cp;
 
+		/* Switch to Pairing Connection Parameters */
+		hci_le_conn_update(hcon, SMP_MIN_CONN_INTERVAL,
+				SMP_MAX_CONN_INTERVAL, SMP_MAX_CONN_LATENCY,
+				SMP_SUPERVISION_TIMEOUT);
+
 		build_pairing_cmd(conn, &cp, NULL, authreq);
 		hcon->preq[0] = SMP_CMD_PAIRING_REQ;
 		memcpy(&hcon->preq[1], &cp, sizeof(cp));