[Bluetooth] Track status of Simple Pairing mode

The Simple Pairing feature is optional and needs to be enabled by the
host stack first. The Linux kernel relies on the Bluetooth daemon to
either enable or disable it, but at any time it needs to know the
current state of the Simple Pairing mode. So track any changes made
by external entities and store the current mode in the HCI device
structure.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 79629ff..6d0c04a 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -514,6 +514,17 @@
 	__le16   sco_max_pkt;
 } __attribute__ ((packed));
 
+#define HCI_OP_READ_SSP_MODE		0x0c55
+struct hci_rp_read_ssp_mode {
+	__u8     status;
+	__u8     mode;
+} __attribute__ ((packed));
+
+#define HCI_OP_WRITE_SSP_MODE		0x0c56
+struct hci_cp_write_ssp_mode {
+	__u8     mode;
+} __attribute__ ((packed));
+
 #define HCI_OP_READ_LOCAL_VERSION	0x1001
 struct hci_rp_read_local_version {
 	__u8     status;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 6424d63..b85754e 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -75,6 +75,7 @@
 	__u8		dev_class[3];
 	__u8		features[8];
 	__u8		commands[64];
+	__u8		ssp_mode;
 	__u8		hci_ver;
 	__u16		hci_rev;
 	__u16		manufacturer;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 7c9ac01..6077a65 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -391,6 +391,35 @@
 	hci_req_complete(hdev, status);
 }
 
+static void hci_cc_read_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_rp_read_ssp_mode *rp = (void *) skb->data;
+
+	BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+	if (rp->status)
+		return;
+
+	hdev->ssp_mode = rp->mode;
+}
+
+static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	__u8 status = *((__u8 *) skb->data);
+	void *sent;
+
+	BT_DBG("%s status 0x%x", hdev->name, status);
+
+	if (status)
+		return;
+
+	sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_MODE);
+	if (!sent)
+		return;
+
+	hdev->ssp_mode = *((__u8 *) sent);
+}
+
 static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_rp_read_local_version *rp = (void *) skb->data;
@@ -1084,6 +1113,14 @@
 		hci_cc_host_buffer_size(hdev, skb);
 		break;
 
+	case HCI_OP_READ_SSP_MODE:
+		hci_cc_read_ssp_mode(hdev, skb);
+		break;
+
+	case HCI_OP_WRITE_SSP_MODE:
+		hci_cc_write_ssp_mode(hdev, skb);
+		break;
+
 	case HCI_OP_READ_LOCAL_VERSION:
 		hci_cc_read_local_version(hdev, skb);
 		break;