[Bluetooth] Read local version information on device init

The local version information are needed to identify certain feature
sets of devices. They must be read on device init and stored for later
use. It is also possible to access them through the device model.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 263e42b..7451a9c 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -72,6 +72,9 @@
 	__u8		type;
 	bdaddr_t	bdaddr;
 	__u8		features[8];
+	__u8		hci_ver;
+	__u16		hci_rev;
+	__u16		manufacturer;
 	__u16		voice_setting;
 
 	__u16		pkt_type;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 5ed4742..338ae97 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -206,6 +206,9 @@
 	/* Read Local Supported Features */
 	hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES, 0, NULL);
 
+	/* Read Local Version */
+	hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION, 0, NULL);
+
 	/* Read Buffer Size (ACL mtu, max pkt, etc.) */
 	hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE, 0, NULL);
 
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index c6cd243..7518bdb 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -298,6 +298,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_version *lv;
 	struct hci_rp_read_local_features *lf;
 	struct hci_rp_read_buffer_size *bs;
 	struct hci_rp_read_bd_addr *ba;
@@ -305,6 +306,23 @@
 	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
 
 	switch (ocf) {
+	case OCF_READ_LOCAL_VERSION:
+		lv = (struct hci_rp_read_loc_version *) skb->data;
+
+		if (lv->status) {
+			BT_DBG("%s READ_LOCAL_VERSION failed %d", hdev->name, lf->status);
+			break;
+		}
+
+		hdev->hci_ver = lv->hci_ver;
+		hdev->hci_rev = btohs(lv->hci_rev);
+		hdev->manufacturer = btohs(lv->manufacturer);
+
+		BT_DBG("%s: manufacturer %d hci_ver %d hci_rev %d", hdev->name,
+				hdev->manufacturer, hdev->hci_ver, hdev->hci_rev);
+
+		break;
+
 	case OCF_READ_LOCAL_FEATURES:
 		lf = (struct hci_rp_read_local_features *) skb->data;
 
@@ -329,7 +347,8 @@
 		if (hdev->features[1] & LMP_HV3)
 			hdev->pkt_type |= (HCI_HV3);
 
-		BT_DBG("%s: features 0x%x 0x%x 0x%x", hdev->name, lf->features[0], lf->features[1], lf->features[2]);
+		BT_DBG("%s: features 0x%x 0x%x 0x%x", hdev->name,
+				lf->features[0], lf->features[1], lf->features[2]);
 
 		break;
 
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 09c6161..a5c4804 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -49,6 +49,24 @@
 	return sprintf(buf, "%s\n", batostr(&bdaddr));
 }
 
+static ssize_t show_manufacturer(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct hci_dev *hdev = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", hdev->manufacturer);
+}
+
+static ssize_t show_hci_version(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct hci_dev *hdev = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", hdev->hci_ver);
+}
+
+static ssize_t show_hci_revision(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct hci_dev *hdev = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", hdev->hci_rev);
+}
+
 static ssize_t show_inquiry_cache(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct hci_dev *hdev = dev_get_drvdata(dev);
@@ -153,6 +171,9 @@
 
 static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
 static DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
+static DEVICE_ATTR(manufacturer, S_IRUGO, show_manufacturer, NULL);
+static DEVICE_ATTR(hci_version, S_IRUGO, show_hci_version, NULL);
+static DEVICE_ATTR(hci_revision, S_IRUGO, show_hci_revision, NULL);
 static DEVICE_ATTR(inquiry_cache, S_IRUGO, show_inquiry_cache, NULL);
 
 static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR,
@@ -165,6 +186,9 @@
 static struct device_attribute *bt_attrs[] = {
 	&dev_attr_type,
 	&dev_attr_address,
+	&dev_attr_manufacturer,
+	&dev_attr_hci_version,
+	&dev_attr_hci_revision,
 	&dev_attr_inquiry_cache,
 	&dev_attr_idle_timeout,
 	&dev_attr_sniff_max_interval,