[Bluetooth] Make use of the default link policy settings

The Bluetooth specification supports the default link policy settings
on a per host controller basis. For every new connection the link
manager would then use these settings. It is better to use this instead
of bothering the controller on every connection setup to overwrite the
default settings.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index f1dc174..efc8c55 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -404,6 +404,17 @@
 	__le16   handle;
 } __attribute__ ((packed));
 
+#define HCI_OP_READ_DEF_LINK_POLICY	0x080e
+struct hci_rp_read_def_link_policy {
+	__u8     status;
+	__le16   policy;
+} __attribute__ ((packed));
+
+#define HCI_OP_WRITE_DEF_LINK_POLICY	0x080f
+struct hci_cp_write_def_link_policy {
+	__le16   policy;
+} __attribute__ ((packed));
+
 #define HCI_OP_SNIFF_SUBRATE		0x0811
 struct hci_cp_sniff_subrate {
 	__le16   handle;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 69c64ce..6175ce8 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -65,6 +65,8 @@
 
 	conn->attempt++;
 
+	conn->link_policy = hdev->link_policy;
+
 	memset(&cp, 0, sizeof(cp));
 	bacpy(&cp.bdaddr, &conn->dst);
 	cp.pscan_rep_mode = 0x02;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index aec6929..69b2c1a 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -279,10 +279,20 @@
 
 	BT_DBG("%s %x", hdev->name, encrypt);
 
-	/* Authentication */
+	/* Encryption */
 	hci_send_cmd(hdev, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt);
 }
 
+static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt)
+{
+	__le16 policy = cpu_to_le16(opt);
+
+	BT_DBG("%s %x", hdev->name, opt);
+
+	/* Default link policy */
+	hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy);
+}
+
 /* Get HCI device by index.
  * Device is held on return. */
 struct hci_dev *hci_dev_get(int index)
@@ -694,32 +704,35 @@
 					msecs_to_jiffies(HCI_INIT_TIMEOUT));
 		break;
 
+	case HCISETLINKPOL:
+		err = hci_request(hdev, hci_linkpol_req, dr.dev_opt,
+					msecs_to_jiffies(HCI_INIT_TIMEOUT));
+		break;
+
+	case HCISETLINKMODE:
+		hdev->link_mode = ((__u16) dr.dev_opt) &
+					(HCI_LM_MASTER | HCI_LM_ACCEPT);
+		break;
+
 	case HCISETPTYPE:
 		hdev->pkt_type = (__u16) dr.dev_opt;
 		break;
 
-	case HCISETLINKPOL:
-		hdev->link_policy = (__u16) dr.dev_opt;
-		break;
-
-	case HCISETLINKMODE:
-		hdev->link_mode = ((__u16) dr.dev_opt) & (HCI_LM_MASTER | HCI_LM_ACCEPT);
-		break;
-
 	case HCISETACLMTU:
-		hdev->acl_mtu  = *((__u16 *)&dr.dev_opt + 1);
-		hdev->acl_pkts = *((__u16 *)&dr.dev_opt + 0);
+		hdev->acl_mtu  = *((__u16 *) &dr.dev_opt + 1);
+		hdev->acl_pkts = *((__u16 *) &dr.dev_opt + 0);
 		break;
 
 	case HCISETSCOMTU:
-		hdev->sco_mtu  = *((__u16 *)&dr.dev_opt + 1);
-		hdev->sco_pkts = *((__u16 *)&dr.dev_opt + 0);
+		hdev->sco_mtu  = *((__u16 *) &dr.dev_opt + 1);
+		hdev->sco_pkts = *((__u16 *) &dr.dev_opt + 0);
 		break;
 
 	default:
 		err = -EINVAL;
 		break;
 	}
+
 	hci_dev_put(hdev);
 	return err;
 }
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index d4d2dcc..9af181a 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -110,6 +110,25 @@
 	hci_dev_unlock(hdev);
 }
 
+static void hci_cc_read_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_rp_read_link_policy *rp = (void *) skb->data;
+	struct hci_conn *conn;
+
+	BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+	if (rp->status)
+		return;
+
+	hci_dev_lock(hdev);
+
+	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
+	if (conn)
+		conn->link_policy = __le16_to_cpu(rp->policy);
+
+	hci_dev_unlock(hdev);
+}
+
 static void hci_cc_write_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_rp_write_link_policy *rp = (void *) skb->data;
@@ -128,13 +147,41 @@
 	hci_dev_lock(hdev);
 
 	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
-	if (conn) {
+	if (conn)
 		conn->link_policy = get_unaligned_le16(sent + 2);
-	}
 
 	hci_dev_unlock(hdev);
 }
 
+static void hci_cc_read_def_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_rp_read_def_link_policy *rp = (void *) skb->data;
+
+	BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+	if (rp->status)
+		return;
+
+	hdev->link_policy = __le16_to_cpu(rp->policy);
+}
+
+static void hci_cc_write_def_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	__u8 status = *((__u8 *) skb->data);
+	void *sent;
+
+	BT_DBG("%s status 0x%x", hdev->name, status);
+
+	sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_DEF_LINK_POLICY);
+	if (!sent)
+		return;
+
+	if (!status)
+		hdev->link_policy = get_unaligned_le16(sent);
+
+	hci_req_complete(hdev, status);
+}
+
 static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	__u8 status = *((__u8 *) skb->data);
@@ -347,8 +394,8 @@
 		return;
 
 	hdev->hci_ver = rp->hci_ver;
-	hdev->hci_rev = btohs(rp->hci_rev);
-	hdev->manufacturer = btohs(rp->manufacturer);
+	hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
+	hdev->manufacturer = __le16_to_cpu(rp->manufacturer);
 
 	BT_DBG("%s manufacturer %d hci ver %d:%d", hdev->name,
 					hdev->manufacturer,
@@ -690,14 +737,6 @@
 			hci_send_cmd(hdev, HCI_OP_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, HCI_OP_WRITE_LINK_POLICY, sizeof(cp), &cp);
-		}
-
 		/* Set packet type for incoming connection */
 		if (!conn->out && hdev->hci_ver < 3) {
 			struct hci_cp_change_conn_ptype cp;
@@ -974,10 +1013,22 @@
 		hci_cc_role_discovery(hdev, skb);
 		break;
 
+	case HCI_OP_READ_LINK_POLICY:
+		hci_cc_read_link_policy(hdev, skb);
+		break;
+
 	case HCI_OP_WRITE_LINK_POLICY:
 		hci_cc_write_link_policy(hdev, skb);
 		break;
 
+	case HCI_OP_READ_DEF_LINK_POLICY:
+		hci_cc_read_def_link_policy(hdev, skb);
+		break;
+
+	case HCI_OP_WRITE_DEF_LINK_POLICY:
+		hci_cc_write_def_link_policy(hdev, skb);
+		break;
+
 	case HCI_OP_RESET:
 		hci_cc_reset(hdev, skb);
 		break;