Commit a106e50b authored by Luiz Augusto von Dentz's avatar Luiz Augusto von Dentz
Browse files

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: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 6f7cf13e
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -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 */
+4 −1
Original line number Diff line number Diff line
@@ -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)))
+2 −0
Original line number Diff line number Diff line
@@ -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);
+98 −27
Original line number Diff line number Diff line
@@ -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) {
	if (conn && conn->state == BT_CONFIG)
		hci_connect_cfm(conn, status);
			hci_conn_drop(conn);
		}
	}

	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,
+100 −2
Original line number Diff line number Diff line
@@ -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,
	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;
}