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)