Bluetooth: host: Add calling of read_remote_version

Make remote features and remote version accesible to the application
through the bt_conn_get_remote_info object. The host will auto initiate
the procedures. If the procedures have not finished with the application
calls bt_conn_get_remote_info then EBUSY will be returned.
The procedures should finish during the first 10 connection intervals.

Signed-off-by: Sverre Storvold <Sverre.Storvold@nordicsemi.no>
Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
diff --git a/doc/zephyr.doxyfile.in b/doc/zephyr.doxyfile.in
index 6ceebb6..83f9573 100644
--- a/doc/zephyr.doxyfile.in
+++ b/doc/zephyr.doxyfile.in
@@ -1977,6 +1977,7 @@
                          "CONFIG_SYS_POWER_MANAGEMENT=y" \
                          "CONFIG_DEVICE_POWER_MANAGEMENT=y" \
                          "CONFIG_BT_SMP=y" \
+                         "CONFIG_BT_REMOTE_INFO=y" \
                          "CONFIG_USERSPACE=y" \
                          "CONFIG_BT_BREDR=y" \
                          "CONFIG_FLASH_PAGE_LAYOUT=y" \
diff --git a/include/bluetooth/conn.h b/include/bluetooth/conn.h
index 7c761bb..a7ee9b3 100644
--- a/include/bluetooth/conn.h
+++ b/include/bluetooth/conn.h
@@ -164,7 +164,6 @@
 
 /** @brief Connection Info Structure
  *
- *
  *  @param type Connection Type
  *  @param role Connection Role
  *  @param id Which local identity the connection was created with
@@ -185,6 +184,50 @@
 	};
 };
 
+/** LE Connection Remote Info Structure */
+struct bt_conn_le_remote_info {
+
+	/** Remote LE feature set (bitmask). */
+	const u8_t *features;
+};
+
+/** BR/EDR Connection Remote Info structure */
+struct bt_conn_br_remote_info {
+
+	/** Remote feature set (pages of bitmasks). */
+	const u8_t *features;
+
+	/** Number of pages in the remote feature set. */
+	u8_t num_pages;
+};
+
+/** @brief Connection Remote Info Structure
+ *
+ *  @note The version, manufacturer and subversion fields will only contain
+ *        valid data if :option:`CONFIG_BT_REMOTE_VERSION` is enabled.
+ */
+struct bt_conn_remote_info {
+	/* Connection Type */
+	u8_t  type;
+
+	/* Remote Link Layer version */
+	u8_t  version;
+
+	/* Remote manufacturer identifier */
+	u16_t manufacturer;
+
+	/* Per-manufacturer unique revision */
+	u16_t subversion;
+
+	union {
+		/* LE connection remote info */
+		struct bt_conn_le_remote_info le;
+
+		/* BR/EDR connection remote info */
+		struct bt_conn_br_remote_info br;
+	};
+};
+
 /** @brief Get connection info
  *
  *  @param conn Connection object.
@@ -194,6 +237,24 @@
  */
 int bt_conn_get_info(const struct bt_conn *conn, struct bt_conn_info *info);
 
+/** @brief Get connection info for the remote device.
+ *
+ *  @param conn Connection object.
+ *  @param remote_info Connection remote info object.
+ *
+ *  @note In order to retrieve the remote version (version, manufacturer
+ *  and subversion) :option:`CONFIG_BT_REMOTE_VERSION` must be enabled
+ *
+ *  @note The remote information is exchanged directly after the connection has
+ *  been established. The application can be notified about when the remote
+ *  information is available through the remote_info_available callback.
+ *
+ *  @return Zero on success or (negative) error code on failure.
+ *  @return -EBUSY The remote information is not yet available.
+ */
+int bt_conn_get_remote_info(struct bt_conn *conn,
+			    struct bt_conn_remote_info *remote_info);
+
 /** @brief Update the connection parameters.
  *
  *  @param conn Connection object.
@@ -494,6 +555,17 @@
 	void (*security_changed)(struct bt_conn *conn, bt_security_t level,
 				 enum bt_security_err err);
 #endif /* defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) */
+
+#if defined(CONFIG_BT_REMOTE_INFO)
+	/** @brief Remote information procedures has completed.
+	 *
+	 *  This callback notifies the application that the remote information
+	 *  has been retrieved from the remote peer.
+	 */
+	void (*remote_info_available)(struct bt_conn *conn,
+				      struct bt_conn_remote_info *remote_info);
+#endif /* defined(CONFIG_BT_REMOTE_INFO) */
+
 	struct bt_conn_cb *_next;
 };
 
diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig
index c721035..bad4510 100644
--- a/subsys/bluetooth/host/Kconfig
+++ b/subsys/bluetooth/host/Kconfig
@@ -261,6 +261,22 @@
 	  want to rely on remote device to initiate the procedure at its
 	  discretion.
 
+config BT_REMOTE_INFO
+	bool "Enable application access to remote information"
+	help
+	  Enable application access to the remote information available in the
+	  stack. The remote information is retrieved once a connection has been
+	  established and the application will be notified when this information
+	  is available through the remote_version_available connection callback.
+
+config BT_REMOTE_VERSION
+	bool "Enable remote version information"
+	depends on BT_REMOTE_INFO
+	help
+	  Enable this to get access to the remote version through
+	  the remote_version_available callback. The host will automatically ask
+	  the remote device after the connection has been established.
+
 config BT_SMP
 	bool "Security Manager Protocol support"
 	select TINYCRYPT
diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c
index 2f9ea93..cac0841 100644
--- a/subsys/bluetooth/host/conn.c
+++ b/subsys/bluetooth/host/conn.c
@@ -154,6 +154,27 @@
 	}
 }
 
+#if defined(CONFIG_BT_REMOTE_INFO)
+void notify_remote_info(struct bt_conn *conn)
+{
+	struct bt_conn_remote_info remote_info;
+	struct bt_conn_cb *cb;
+	int err;
+
+	err = bt_conn_get_remote_info(conn, &remote_info);
+	if (err) {
+		BT_DBG("Notify remote info failed %d", err);
+		return;
+	}
+
+	for (cb = callback_list; cb; cb = cb->_next) {
+		if (cb->remote_info_available) {
+			cb->remote_info_available(conn, &remote_info);
+		}
+	}
+}
+#endif /* defined(CONFIG_BT_REMOTE_INFO) */
+
 void notify_le_param_updated(struct bt_conn *conn)
 {
 	struct bt_conn_cb *cb;
@@ -1638,6 +1659,7 @@
 			bt_conn_unref(conn);
 			break;
 		}
+
 		/* Notify disconnection and queue a dummy buffer to wake
 		 * up and stop the tx thread for states where it was
 		 * running.
@@ -1902,6 +1924,42 @@
 	return -EINVAL;
 }
 
+int bt_conn_get_remote_info(struct bt_conn *conn,
+			    struct bt_conn_remote_info *remote_info)
+{
+	if (!atomic_test_bit(conn->flags, BT_CONN_AUTO_FEATURE_EXCH) ||
+	    (IS_ENABLED(CONFIG_BT_REMOTE_VERSION) &&
+	     !atomic_test_bit(conn->flags, BT_CONN_AUTO_VERSION_INFO))) {
+		return -EBUSY;
+	}
+
+	remote_info->type = conn->type;
+#if defined(CONFIG_BT_REMOTE_VERSION)
+	/* The conn->rv values will be just zeroes if the operation failed */
+	remote_info->version = conn->rv.version;
+	remote_info->manufacturer = conn->rv.manufacturer;
+	remote_info->subversion = conn->rv.subversion;
+#else
+	remote_info->version = 0;
+	remote_info->manufacturer = 0;
+	remote_info->subversion = 0;
+#endif
+
+	switch (conn->type) {
+	case BT_CONN_TYPE_LE:
+		remote_info->le.features = conn->le.features;
+		return 0;
+#if defined(CONFIG_BT_BREDR)
+	case BT_CONN_TYPE_BR:
+		/* TODO: Make sure the HCI commands to read br features and
+		*  extended features has finished. */
+		return -ENOTSUP;
+#endif
+	default:
+		return -EINVAL;
+	}
+}
+
 static int bt_hci_disconnect(struct bt_conn *conn, u8_t reason)
 {
 	struct net_buf *buf;
diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h
index b792603..f4a02d9 100644
--- a/subsys/bluetooth/host/conn_internal.h
+++ b/subsys/bluetooth/host/conn_internal.h
@@ -33,6 +33,7 @@
 
 	BT_CONN_AUTO_PHY_COMPLETE,      /* Auto-initiated PHY procedure done */
 	BT_CONN_AUTO_FEATURE_EXCH,	/* Auto-initiated LE Feat done */
+	BT_CONN_AUTO_VERSION_INFO,      /* Auto-initiated LE version done */
 
 	/* Total number of flags - must be at the end of the enum */
 	BT_CONN_NUM_FLAGS,
@@ -146,6 +147,14 @@
 		struct bt_conn_sco	sco;
 #endif
 	};
+
+#if defined(CONFIG_BT_REMOTE_VERSION)
+	struct bt_conn_rv {
+		u8_t  version;
+		u16_t manufacturer;
+		u16_t subversion;
+	} rv;
+#endif
 };
 
 /* Process incoming data for a connection */
@@ -212,6 +221,8 @@
 int bt_conn_le_conn_update(struct bt_conn *conn,
 			   const struct bt_le_conn_param *param);
 
+void notify_remote_info(struct bt_conn *conn);
+
 void notify_le_param_updated(struct bt_conn *conn);
 
 bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param);
diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c
index d963e5a..1b82f16 100644
--- a/subsys/bluetooth/host/hci_core.c
+++ b/subsys/bluetooth/host/hci_core.c
@@ -967,6 +967,33 @@
 	return 0;
 }
 
+static int hci_read_remote_version(struct bt_conn *conn)
+{
+	struct bt_hci_cp_read_remote_version_info *cp;
+	struct net_buf *buf;
+
+	if (conn->state != BT_CONN_CONNECTED) {
+		return -ENOTCONN;
+	}
+
+	/* Remote version cannot change. */
+	if (atomic_test_bit(conn->flags, BT_CONN_AUTO_VERSION_INFO)) {
+		return 0;
+	}
+
+	buf = bt_hci_cmd_create(BT_HCI_OP_READ_REMOTE_VERSION_INFO,
+				sizeof(*cp));
+	if (!buf) {
+		return -ENOBUFS;
+	}
+
+	cp = net_buf_add(buf, sizeof(*cp));
+	cp->handle = sys_cpu_to_le16(conn->handle);
+
+	return bt_hci_cmd_send_sync(BT_HCI_OP_READ_REMOTE_VERSION_INFO, buf,
+				   NULL);
+}
+
 /* LE Data Length Change Event is optional so this function just ignore
  * error and stack will continue to use default values.
  */
@@ -1099,6 +1126,14 @@
 		}
 	}
 
+	if (IS_ENABLED(CONFIG_BT_REMOTE_VERSION) &&
+	    !atomic_test_bit(conn->flags, BT_CONN_AUTO_VERSION_INFO)) {
+		err = hci_read_remote_version(conn);
+		if (!err) {
+			return;
+		}
+	}
+
 	if (IS_ENABLED(CONFIG_BT_AUTO_PHY_UPDATE) &&
 	    !atomic_test_bit(conn->flags, BT_CONN_AUTO_PHY_COMPLETE) &&
 	    BT_FEAT_LE_PHY_2M(bt_dev.le.features)) {
@@ -1372,6 +1407,12 @@
 	}
 
 	atomic_set_bit(conn->flags, BT_CONN_AUTO_FEATURE_EXCH);
+
+	if (IS_ENABLED(CONFIG_BT_REMOTE_INFO) &&
+	    !IS_ENABLED(CONFIG_BT_REMOTE_VERSION)) {
+		notify_remote_info(conn);
+	}
+
 	/* Continue with auto-initiated procedures */
 	conn_auto_initiate(conn);
 
@@ -3231,6 +3272,39 @@
 }
 #endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */
 
+#if defined(CONFIG_BT_REMOTE_VERSION)
+static void bt_hci_evt_read_remote_version_complete(struct net_buf *buf)
+{
+	struct bt_hci_evt_remote_version_info *evt;
+	struct bt_conn *conn;
+
+	evt = net_buf_pull_mem(buf, sizeof(*evt));
+	conn = bt_conn_lookup_handle(evt->handle);
+	if (!conn) {
+		BT_ERR("No connection for handle %u", evt->handle);
+		return;
+	}
+
+	if (!evt->status) {
+		conn->rv.version = evt->version;
+		conn->rv.manufacturer = sys_le16_to_cpu(evt->manufacturer);
+		conn->rv.subversion = sys_le16_to_cpu(evt->subversion);
+	}
+
+	atomic_set_bit(conn->flags, BT_CONN_AUTO_VERSION_INFO);
+
+	if (IS_ENABLED(CONFIG_BT_REMOTE_INFO)) {
+		/* Remote features is already present */
+		notify_remote_info(conn);
+	}
+
+	/* Continue with auto-initiated procedures */
+	conn_auto_initiate(conn);
+
+	bt_conn_unref(conn);
+}
+#endif /* CONFIG_BT_REMOTE_VERSION */
+
 #if defined(CONFIG_BT_SMP)
 static void le_ltk_neg_reply(u16_t handle)
 {
@@ -3795,6 +3869,11 @@
 		      hci_encrypt_key_refresh_complete,
 		      sizeof(struct bt_hci_evt_encrypt_key_refresh_complete)),
 #endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */
+#if defined(CONFIG_BT_REMOTE_VERSION)
+	EVENT_HANDLER(BT_HCI_EVT_REMOTE_VERSION_INFO,
+		      bt_hci_evt_read_remote_version_complete,
+		      sizeof(struct bt_hci_evt_remote_version_info)),
+#endif /* CONFIG_BT_REMOTE_VERSION */
 };
 
 static void hci_event(struct net_buf *buf)