diff --git a/doc/zephyr.doxyfile.in b/doc/zephyr.doxyfile.in index 6ceebb6669b..83f9573f57a 100644 --- a/doc/zephyr.doxyfile.in +++ b/doc/zephyr.doxyfile.in @@ -1977,6 +1977,7 @@ PREDEFINED = "CONFIG_SYS_CLOCK_EXISTS=y" \ "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 7c761bbb395..a7ee9b38e7f 100644 --- a/include/bluetooth/conn.h +++ b/include/bluetooth/conn.h @@ -163,7 +163,6 @@ enum { }; /** @brief Connection Info Structure - * * * @param type Connection Type * @param role Connection Role @@ -185,6 +184,50 @@ struct bt_conn_info { }; }; +/** 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 @@ struct bt_conn_info { */ 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 @@ struct bt_conn_cb { 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 c7210357970..bad45101a8e 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -261,6 +261,22 @@ config BT_AUTO_PHY_UPDATE 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 2f9ea93c8ea..cac08419a4a 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -154,6 +154,27 @@ static void notify_disconnected(struct bt_conn *conn) } } +#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 @@ void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state) 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 @@ int bt_conn_get_info(const struct bt_conn *conn, struct bt_conn_info *info) 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 b792603d954..f4a02d9217b 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -33,6 +33,7 @@ enum { 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 { 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 @@ void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state); 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 d963e5a290c..1b82f16d9c2 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -967,6 +967,33 @@ static int hci_le_read_remote_features(struct bt_conn *conn) 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 @@ static void conn_auto_initiate(struct bt_conn *conn) } } + 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 @@ static void le_remote_feat_complete(struct net_buf *buf) } 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 @@ static void hci_encrypt_key_refresh_complete(struct net_buf *buf) } #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 @@ static const struct event_handler normal_events[] = { 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)