mirror of https://github.com/torvalds/linux.git
Bluetooth: HCI: Add support for LL Extended Feature Set
This adds support for emulating LL Extended Feature Set introduced in 6.0 that adds the following: Commands: - HCI_LE_Read_All_Local_Supported_Features(0x2087)(Feature:47,1) - HCI_LE_Read_All_Remote_Features(0x2088)(Feature:47,2) Events: - HCI_LE_Read_All_Remote_Features_Complete(0x2b)(Mask bit:42) Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
parent
6f7cf13ef6
commit
a106e50be7
|
|
@ -653,6 +653,7 @@ enum {
|
|||
#define HCI_LE_CIS_PERIPHERAL 0x20
|
||||
#define HCI_LE_ISO_BROADCASTER 0x40
|
||||
#define HCI_LE_ISO_SYNC_RECEIVER 0x80
|
||||
#define HCI_LE_LL_EXT_FEATURE 0x80
|
||||
|
||||
/* Connection modes */
|
||||
#define HCI_CM_ACTIVE 0x0000
|
||||
|
|
@ -2255,6 +2256,19 @@ struct hci_cp_le_set_host_feature {
|
|||
__u8 bit_value;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_READ_ALL_LOCAL_FEATURES 0x2087
|
||||
struct hci_rp_le_read_all_local_features {
|
||||
__u8 status;
|
||||
__u8 page;
|
||||
__u8 features[248];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_READ_ALL_REMOTE_FEATURES 0x2088
|
||||
struct hci_cp_le_read_all_remote_features {
|
||||
__le16 handle;
|
||||
__u8 pages;
|
||||
} __packed;
|
||||
|
||||
/* ---- HCI Events ---- */
|
||||
struct hci_ev_status {
|
||||
__u8 status;
|
||||
|
|
@ -2937,6 +2951,15 @@ struct hci_evt_le_big_info_adv_report {
|
|||
__u8 encryption;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE 0x2b
|
||||
struct hci_evt_le_read_all_remote_features_complete {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
__u8 max_pages;
|
||||
__u8 valid_pages;
|
||||
__u8 features[248];
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_VENDOR 0xff
|
||||
|
||||
/* Internal events generated by Bluetooth stack */
|
||||
|
|
|
|||
|
|
@ -378,7 +378,7 @@ struct hci_dev {
|
|||
__u8 minor_class;
|
||||
__u8 max_page;
|
||||
__u8 features[HCI_MAX_PAGES][8];
|
||||
__u8 le_features[8];
|
||||
__u8 le_features[248];
|
||||
__u8 le_accept_list_size;
|
||||
__u8 le_resolv_list_size;
|
||||
__u8 le_num_of_adv_sets;
|
||||
|
|
@ -702,6 +702,7 @@ struct hci_conn {
|
|||
__u8 attempt;
|
||||
__u8 dev_class[3];
|
||||
__u8 features[HCI_MAX_PAGES][8];
|
||||
__u8 le_features[248];
|
||||
__u16 pkt_type;
|
||||
__u16 link_policy;
|
||||
__u8 key_type;
|
||||
|
|
@ -2067,6 +2068,8 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
|||
(le_enabled(dev) && past_receiver_capable(dev))
|
||||
#define past_enabled(dev) \
|
||||
(past_sender_enabled(dev) || past_receiver_enabled(dev))
|
||||
#define ll_ext_feature_capable(dev) \
|
||||
((dev)->le_features[7] & HCI_LE_LL_EXT_FEATURE)
|
||||
|
||||
#define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \
|
||||
(!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG)))
|
||||
|
|
|
|||
|
|
@ -189,3 +189,5 @@ int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
|||
int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
int hci_past_sync(struct hci_conn *conn, struct hci_conn *le);
|
||||
|
||||
int hci_le_read_remote_features(struct hci_conn *conn);
|
||||
|
|
|
|||
|
|
@ -2886,12 +2886,8 @@ static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status)
|
|||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
||||
if (conn) {
|
||||
if (conn->state == BT_CONFIG) {
|
||||
hci_connect_cfm(conn, status);
|
||||
hci_conn_drop(conn);
|
||||
}
|
||||
}
|
||||
if (conn && conn->state == BT_CONFIG)
|
||||
hci_connect_cfm(conn, status);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
|
@ -3915,11 +3911,49 @@ static u8 hci_cc_le_setup_iso_path(struct hci_dev *hdev, void *data,
|
|||
return rp->status;
|
||||
}
|
||||
|
||||
static u8 hci_cc_le_read_all_local_features(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_read_all_local_features *rp = data;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return rp->status;
|
||||
|
||||
memcpy(hdev->le_features, rp->features, 248);
|
||||
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
static void hci_cs_le_create_big(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
}
|
||||
|
||||
static void hci_cs_le_read_all_remote_features(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
struct hci_cp_le_read_remote_features *cp;
|
||||
struct hci_conn *conn;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
|
||||
if (!status)
|
||||
return;
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_READ_ALL_REMOTE_FEATURES);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
||||
if (conn && conn->state == BT_CONFIG)
|
||||
hci_connect_cfm(conn, status);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static u8 hci_cc_set_per_adv_param(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
|
@ -4171,6 +4205,9 @@ static const struct hci_cc {
|
|||
sizeof(struct hci_rp_le_set_cig_params), HCI_MAX_EVENT_SIZE),
|
||||
HCI_CC(HCI_OP_LE_SETUP_ISO_PATH, hci_cc_le_setup_iso_path,
|
||||
sizeof(struct hci_rp_le_setup_iso_path)),
|
||||
HCI_CC(HCI_OP_LE_READ_ALL_LOCAL_FEATURES,
|
||||
hci_cc_le_read_all_local_features,
|
||||
sizeof(struct hci_rp_le_read_all_local_features)),
|
||||
};
|
||||
|
||||
static u8 hci_cc_func(struct hci_dev *hdev, const struct hci_cc *cc,
|
||||
|
|
@ -4325,6 +4362,8 @@ static const struct hci_cs {
|
|||
HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn),
|
||||
HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis),
|
||||
HCI_CS(HCI_OP_LE_CREATE_BIG, hci_cs_le_create_big),
|
||||
HCI_CS(HCI_OP_LE_READ_ALL_REMOTE_FEATURES,
|
||||
hci_cs_le_read_all_remote_features),
|
||||
};
|
||||
|
||||
static void hci_cmd_status_evt(struct hci_dev *hdev, void *data,
|
||||
|
|
@ -5645,6 +5684,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
|||
struct hci_conn *conn;
|
||||
struct smp_irk *irk;
|
||||
u8 addr_type;
|
||||
int err;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
|
|
@ -5775,26 +5815,8 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
|||
hci_debugfs_create_conn(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
|
||||
/* The remote features procedure is defined for central
|
||||
* role only. So only in case of an initiated connection
|
||||
* request the remote features.
|
||||
*
|
||||
* If the local controller supports peripheral-initiated features
|
||||
* exchange, then requesting the remote features in peripheral
|
||||
* role is possible. Otherwise just transition into the
|
||||
* connected state without requesting the remote features.
|
||||
*/
|
||||
if (conn->out ||
|
||||
(hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) {
|
||||
struct hci_cp_le_read_remote_features cp;
|
||||
|
||||
cp.handle = __cpu_to_le16(conn->handle);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_READ_REMOTE_FEATURES,
|
||||
sizeof(cp), &cp);
|
||||
|
||||
hci_conn_hold(conn);
|
||||
} else {
|
||||
err = hci_le_read_remote_features(conn);
|
||||
if (err) {
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_connect_cfm(conn, status);
|
||||
}
|
||||
|
|
@ -6608,7 +6630,6 @@ static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data,
|
|||
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_connect_cfm(conn, status);
|
||||
hci_conn_drop(conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7186,6 +7207,50 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data,
|
|||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_le_read_all_remote_features_evt(struct hci_dev *hdev,
|
||||
void *data, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_evt_le_read_all_remote_features_complete *ev = data;
|
||||
struct hci_conn *conn;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
if (!ev->status)
|
||||
memcpy(conn->le_features, ev->features, 248);
|
||||
|
||||
if (conn->state == BT_CONFIG) {
|
||||
__u8 status;
|
||||
|
||||
/* If the local controller supports peripheral-initiated
|
||||
* features exchange, but the remote controller does
|
||||
* not, then it is possible that the error code 0x1a
|
||||
* for unsupported remote feature gets returned.
|
||||
*
|
||||
* In this specific case, allow the connection to
|
||||
* transition into connected state and mark it as
|
||||
* successful.
|
||||
*/
|
||||
if (!conn->out &&
|
||||
ev->status == HCI_ERROR_UNSUPPORTED_REMOTE_FEATURE &&
|
||||
(hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES))
|
||||
status = 0x00;
|
||||
else
|
||||
status = ev->status;
|
||||
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_connect_cfm(conn, status);
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
#define HCI_LE_EV_VL(_op, _func, _min_len, _max_len) \
|
||||
[_op] = { \
|
||||
.func = _func, \
|
||||
|
|
@ -7291,6 +7356,12 @@ static const struct hci_le_ev {
|
|||
hci_le_big_info_adv_report_evt,
|
||||
sizeof(struct hci_evt_le_big_info_adv_report),
|
||||
HCI_MAX_EVENT_SIZE),
|
||||
/* [0x2b = HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE] */
|
||||
HCI_LE_EV_VL(HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE,
|
||||
hci_le_read_all_remote_features_evt,
|
||||
sizeof(struct
|
||||
hci_evt_le_read_all_remote_features_complete),
|
||||
HCI_MAX_EVENT_SIZE),
|
||||
};
|
||||
|
||||
static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
|
||||
|
|
|
|||
|
|
@ -4011,8 +4011,19 @@ static int hci_le_read_buffer_size_sync(struct hci_dev *hdev)
|
|||
/* Read LE Local Supported Features */
|
||||
static int hci_le_read_local_features_sync(struct hci_dev *hdev)
|
||||
{
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_LOCAL_FEATURES,
|
||||
0, NULL, HCI_CMD_TIMEOUT);
|
||||
int err;
|
||||
|
||||
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_LOCAL_FEATURES,
|
||||
0, NULL, HCI_CMD_TIMEOUT);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (ll_ext_feature_capable(hdev) && hdev->commands[47] & BIT(2))
|
||||
return __hci_cmd_sync_status(hdev,
|
||||
HCI_OP_LE_READ_ALL_LOCAL_FEATURES,
|
||||
0, NULL, HCI_CMD_TIMEOUT);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Read LE Supported States */
|
||||
|
|
@ -7320,3 +7331,90 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
|
|||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void le_read_features_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
if (err == -ECANCELED)
|
||||
return;
|
||||
|
||||
hci_conn_drop(conn);
|
||||
}
|
||||
|
||||
static int hci_le_read_all_remote_features_sync(struct hci_dev *hdev,
|
||||
void *data)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
struct hci_cp_le_read_all_remote_features cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.pages = 10; /* Attempt to read all pages */
|
||||
|
||||
/* Wait for HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE event otherwise
|
||||
* hci_conn_drop may run prematurely causing a disconnection.
|
||||
*/
|
||||
return __hci_cmd_sync_status_sk(hdev,
|
||||
HCI_OP_LE_READ_ALL_REMOTE_FEATURES,
|
||||
sizeof(cp), &cp,
|
||||
HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE,
|
||||
HCI_CMD_TIMEOUT, NULL);
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_ALL_REMOTE_FEATURES,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_le_read_remote_features_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
struct hci_cp_le_read_remote_features cp;
|
||||
|
||||
if (!hci_conn_valid(hdev, conn))
|
||||
return -ECANCELED;
|
||||
|
||||
/* Check if LL Extended Feature Set is supported and
|
||||
* HCI_OP_LE_READ_ALL_REMOTE_FEATURES is supported then use that to read
|
||||
* all features.
|
||||
*/
|
||||
if (ll_ext_feature_capable(hdev) && hdev->commands[47] & BIT(3))
|
||||
return hci_le_read_all_remote_features_sync(hdev, data);
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
|
||||
/* Wait for HCI_EV_LE_REMOTE_FEAT_COMPLETE event otherwise
|
||||
* hci_conn_drop may run prematurely causing a disconnection.
|
||||
*/
|
||||
return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_READ_REMOTE_FEATURES,
|
||||
sizeof(cp), &cp,
|
||||
HCI_EV_LE_REMOTE_FEAT_COMPLETE,
|
||||
HCI_CMD_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
int hci_le_read_remote_features(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
int err;
|
||||
|
||||
/* The remote features procedure is defined for central
|
||||
* role only. So only in case of an initiated connection
|
||||
* request the remote features.
|
||||
*
|
||||
* If the local controller supports peripheral-initiated features
|
||||
* exchange, then requesting the remote features in peripheral
|
||||
* role is possible. Otherwise just transition into the
|
||||
* connected state without requesting the remote features.
|
||||
*/
|
||||
if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES))
|
||||
err = hci_cmd_sync_queue_once(hdev,
|
||||
hci_le_read_remote_features_sync,
|
||||
hci_conn_hold(conn),
|
||||
le_read_features_complete);
|
||||
else
|
||||
err = -EOPNOTSUPP;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue