Commit 72fb8373 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
Luiz Augusto von Dentz says:

====================
bluetooth pull request for net:

 - MGMT: set_mesh: update LE scan interval and window
 - MGMT: mesh_send: check instances prior disabling advertising
 - hci_sync: revert some mesh modifications
 - hci_sync: Set extended advertising data synchronously
 - hci_sync: Prevent unintended pause by checking if advertising is active

* tag 'for-net-2025-06-27' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: HCI: Set extended advertising data synchronously
  Bluetooth: MGMT: mesh_send: check instances prior disabling advertising
  Bluetooth: MGMT: set_mesh: update LE scan interval and window
  Bluetooth: hci_sync: revert some mesh modifications
  Bluetooth: Prevent unintended pause by checking if advertising is active
====================

Link: https://patch.msgid.link/20250627181601.520435-1-luiz.dentz@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 3b2c45cb 89fb8acc
Loading
Loading
Loading
Loading
+0 −36
Original line number Diff line number Diff line
@@ -2150,40 +2150,6 @@ static u8 hci_cc_set_adv_param(struct hci_dev *hdev, void *data,
	return rp->status;
}

static u8 hci_cc_set_ext_adv_param(struct hci_dev *hdev, void *data,
				   struct sk_buff *skb)
{
	struct hci_rp_le_set_ext_adv_params *rp = data;
	struct hci_cp_le_set_ext_adv_params *cp;
	struct adv_info *adv_instance;

	bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);

	if (rp->status)
		return rp->status;

	cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS);
	if (!cp)
		return rp->status;

	hci_dev_lock(hdev);
	hdev->adv_addr_type = cp->own_addr_type;
	if (!cp->handle) {
		/* Store in hdev for instance 0 */
		hdev->adv_tx_power = rp->tx_power;
	} else {
		adv_instance = hci_find_adv_instance(hdev, cp->handle);
		if (adv_instance)
			adv_instance->tx_power = rp->tx_power;
	}
	/* Update adv data as tx power is known now */
	hci_update_adv_data(hdev, cp->handle);

	hci_dev_unlock(hdev);

	return rp->status;
}

static u8 hci_cc_read_rssi(struct hci_dev *hdev, void *data,
			   struct sk_buff *skb)
{
@@ -4164,8 +4130,6 @@ static const struct hci_cc {
	HCI_CC(HCI_OP_LE_READ_NUM_SUPPORTED_ADV_SETS,
	       hci_cc_le_read_num_adv_sets,
	       sizeof(struct hci_rp_le_read_num_supported_adv_sets)),
	HCI_CC(HCI_OP_LE_SET_EXT_ADV_PARAMS, hci_cc_set_ext_adv_param,
	       sizeof(struct hci_rp_le_set_ext_adv_params)),
	HCI_CC_STATUS(HCI_OP_LE_SET_EXT_ADV_ENABLE,
		      hci_cc_le_set_ext_adv_enable),
	HCI_CC_STATUS(HCI_OP_LE_SET_ADV_SET_RAND_ADDR,
+138 −89
Original line number Diff line number Diff line
@@ -1205,9 +1205,126 @@ static int hci_set_adv_set_random_addr_sync(struct hci_dev *hdev, u8 instance,
				     sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

static int
hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv,
			    const struct hci_cp_le_set_ext_adv_params *cp,
			    struct hci_rp_le_set_ext_adv_params *rp)
{
	struct sk_buff *skb;

	skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp),
			     cp, HCI_CMD_TIMEOUT);

	/* If command return a status event, skb will be set to -ENODATA */
	if (skb == ERR_PTR(-ENODATA))
		return 0;

	if (IS_ERR(skb)) {
		bt_dev_err(hdev, "Opcode 0x%4.4x failed: %ld",
			   HCI_OP_LE_SET_EXT_ADV_PARAMS, PTR_ERR(skb));
		return PTR_ERR(skb);
	}

	if (skb->len != sizeof(*rp)) {
		bt_dev_err(hdev, "Invalid response length for 0x%4.4x: %u",
			   HCI_OP_LE_SET_EXT_ADV_PARAMS, skb->len);
		kfree_skb(skb);
		return -EIO;
	}

	memcpy(rp, skb->data, sizeof(*rp));
	kfree_skb(skb);

	if (!rp->status) {
		hdev->adv_addr_type = cp->own_addr_type;
		if (!cp->handle) {
			/* Store in hdev for instance 0 */
			hdev->adv_tx_power = rp->tx_power;
		} else if (adv) {
			adv->tx_power = rp->tx_power;
		}
	}

	return rp->status;
}

static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)
{
	DEFINE_FLEX(struct hci_cp_le_set_ext_adv_data, pdu, data, length,
		    HCI_MAX_EXT_AD_LENGTH);
	u8 len;
	struct adv_info *adv = NULL;
	int err;

	if (instance) {
		adv = hci_find_adv_instance(hdev, instance);
		if (!adv || !adv->adv_data_changed)
			return 0;
	}

	len = eir_create_adv_data(hdev, instance, pdu->data,
				  HCI_MAX_EXT_AD_LENGTH);

	pdu->length = len;
	pdu->handle = adv ? adv->handle : instance;
	pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
	pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;

	err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA,
				    struct_size(pdu, data, len), pdu,
				    HCI_CMD_TIMEOUT);
	if (err)
		return err;

	/* Update data if the command succeed */
	if (adv) {
		adv->adv_data_changed = false;
	} else {
		memcpy(hdev->adv_data, pdu->data, len);
		hdev->adv_data_len = len;
	}

	return 0;
}

static int hci_set_adv_data_sync(struct hci_dev *hdev, u8 instance)
{
	struct hci_cp_le_set_adv_data cp;
	u8 len;

	memset(&cp, 0, sizeof(cp));

	len = eir_create_adv_data(hdev, instance, cp.data, sizeof(cp.data));

	/* There's nothing to do if the data hasn't changed */
	if (hdev->adv_data_len == len &&
	    memcmp(cp.data, hdev->adv_data, len) == 0)
		return 0;

	memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
	hdev->adv_data_len = len;

	cp.length = len;

	return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_DATA,
				     sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

int hci_update_adv_data_sync(struct hci_dev *hdev, u8 instance)
{
	if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
		return 0;

	if (ext_adv_capable(hdev))
		return hci_set_ext_adv_data_sync(hdev, instance);

	return hci_set_adv_data_sync(hdev, instance);
}

int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
{
	struct hci_cp_le_set_ext_adv_params cp;
	struct hci_rp_le_set_ext_adv_params rp;
	bool connectable;
	u32 flags;
	bdaddr_t random_addr;
@@ -1316,8 +1433,12 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
		cp.secondary_phy = HCI_ADV_PHY_1M;
	}

	err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS,
				    sizeof(cp), &cp, HCI_CMD_TIMEOUT);
	err = hci_set_ext_adv_params_sync(hdev, adv, &cp, &rp);
	if (err)
		return err;

	/* Update adv data as tx power is known now */
	err = hci_set_ext_adv_data_sync(hdev, cp.handle);
	if (err)
		return err;

@@ -1822,79 +1943,6 @@ int hci_le_terminate_big_sync(struct hci_dev *hdev, u8 handle, u8 reason)
				     sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)
{
	DEFINE_FLEX(struct hci_cp_le_set_ext_adv_data, pdu, data, length,
		    HCI_MAX_EXT_AD_LENGTH);
	u8 len;
	struct adv_info *adv = NULL;
	int err;

	if (instance) {
		adv = hci_find_adv_instance(hdev, instance);
		if (!adv || !adv->adv_data_changed)
			return 0;
	}

	len = eir_create_adv_data(hdev, instance, pdu->data,
				  HCI_MAX_EXT_AD_LENGTH);

	pdu->length = len;
	pdu->handle = adv ? adv->handle : instance;
	pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
	pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;

	err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA,
				    struct_size(pdu, data, len), pdu,
				    HCI_CMD_TIMEOUT);
	if (err)
		return err;

	/* Update data if the command succeed */
	if (adv) {
		adv->adv_data_changed = false;
	} else {
		memcpy(hdev->adv_data, pdu->data, len);
		hdev->adv_data_len = len;
	}

	return 0;
}

static int hci_set_adv_data_sync(struct hci_dev *hdev, u8 instance)
{
	struct hci_cp_le_set_adv_data cp;
	u8 len;

	memset(&cp, 0, sizeof(cp));

	len = eir_create_adv_data(hdev, instance, cp.data, sizeof(cp.data));

	/* There's nothing to do if the data hasn't changed */
	if (hdev->adv_data_len == len &&
	    memcmp(cp.data, hdev->adv_data, len) == 0)
		return 0;

	memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
	hdev->adv_data_len = len;

	cp.length = len;

	return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_DATA,
				     sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

int hci_update_adv_data_sync(struct hci_dev *hdev, u8 instance)
{
	if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
		return 0;

	if (ext_adv_capable(hdev))
		return hci_set_ext_adv_data_sync(hdev, instance);

	return hci_set_adv_data_sync(hdev, instance);
}

int hci_schedule_adv_instance_sync(struct hci_dev *hdev, u8 instance,
				   bool force)
{
@@ -1970,13 +2018,10 @@ static int hci_clear_adv_sets_sync(struct hci_dev *hdev, struct sock *sk)
static int hci_clear_adv_sync(struct hci_dev *hdev, struct sock *sk, bool force)
{
	struct adv_info *adv, *n;
	int err = 0;

	if (ext_adv_capable(hdev))
		/* Remove all existing sets */
		err = hci_clear_adv_sets_sync(hdev, sk);
	if (ext_adv_capable(hdev))
		return err;
		return hci_clear_adv_sets_sync(hdev, sk);

	/* This is safe as long as there is no command send while the lock is
	 * held.
@@ -2004,13 +2049,11 @@ static int hci_clear_adv_sync(struct hci_dev *hdev, struct sock *sk, bool force)
static int hci_remove_adv_sync(struct hci_dev *hdev, u8 instance,
			       struct sock *sk)
{
	int err = 0;
	int err;

	/* If we use extended advertising, instance has to be removed first. */
	if (ext_adv_capable(hdev))
		err = hci_remove_ext_adv_instance_sync(hdev, instance, sk);
	if (ext_adv_capable(hdev))
		return err;
		return hci_remove_ext_adv_instance_sync(hdev, instance, sk);

	/* This is safe as long as there is no command send while the lock is
	 * held.
@@ -2109,16 +2152,13 @@ int hci_read_tx_power_sync(struct hci_dev *hdev, __le16 handle, u8 type)
int hci_disable_advertising_sync(struct hci_dev *hdev)
{
	u8 enable = 0x00;
	int err = 0;

	/* If controller is not advertising we are done. */
	if (!hci_dev_test_flag(hdev, HCI_LE_ADV))
		return 0;

	if (ext_adv_capable(hdev))
		err = hci_disable_ext_adv_instance_sync(hdev, 0x00);
	if (ext_adv_capable(hdev))
		return err;
		return hci_disable_ext_adv_instance_sync(hdev, 0x00);

	return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_ENABLE,
				     sizeof(enable), &enable, HCI_CMD_TIMEOUT);
@@ -2481,6 +2521,10 @@ static int hci_pause_advertising_sync(struct hci_dev *hdev)
	int err;
	int old_state;

	/* If controller is not advertising we are done. */
	if (!hci_dev_test_flag(hdev, HCI_LE_ADV))
		return 0;

	/* If already been paused there is nothing to do. */
	if (hdev->advertising_paused)
		return 0;
@@ -6277,6 +6321,7 @@ static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev,
						struct hci_conn *conn)
{
	struct hci_cp_le_set_ext_adv_params cp;
	struct hci_rp_le_set_ext_adv_params rp;
	int err;
	bdaddr_t random_addr;
	u8 own_addr_type;
@@ -6318,8 +6363,12 @@ static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev,
	if (err)
		return err;

	err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS,
				    sizeof(cp), &cp, HCI_CMD_TIMEOUT);
	err = hci_set_ext_adv_params_sync(hdev, NULL, &cp, &rp);
	if (err)
		return err;

	/* Update adv data as tx power is known now */
	err = hci_set_ext_adv_data_sync(hdev, cp.handle);
	if (err)
		return err;

+24 −1
Original line number Diff line number Diff line
@@ -1080,6 +1080,7 @@ static int mesh_send_done_sync(struct hci_dev *hdev, void *data)
	struct mgmt_mesh_tx *mesh_tx;

	hci_dev_clear_flag(hdev, HCI_MESH_SENDING);
	if (list_empty(&hdev->adv_instances))
		hci_disable_advertising_sync(hdev);
	mesh_tx = mgmt_mesh_next(hdev, NULL);

@@ -2153,6 +2154,9 @@ static int set_mesh_sync(struct hci_dev *hdev, void *data)
	else
		hci_dev_clear_flag(hdev, HCI_MESH);

	hdev->le_scan_interval = __le16_to_cpu(cp->period);
	hdev->le_scan_window = __le16_to_cpu(cp->window);

	len -= sizeof(*cp);

	/* If filters don't fit, forward all adv pkts */
@@ -2167,6 +2171,7 @@ static int set_mesh(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
{
	struct mgmt_cp_set_mesh *cp = data;
	struct mgmt_pending_cmd *cmd;
	__u16 period, window;
	int err = 0;

	bt_dev_dbg(hdev, "sock %p", sk);
@@ -2180,6 +2185,23 @@ static int set_mesh(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_MESH_RECEIVER,
				       MGMT_STATUS_INVALID_PARAMS);

	/* Keep allowed ranges in sync with set_scan_params() */
	period = __le16_to_cpu(cp->period);

	if (period < 0x0004 || period > 0x4000)
		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_MESH_RECEIVER,
				       MGMT_STATUS_INVALID_PARAMS);

	window = __le16_to_cpu(cp->window);

	if (window < 0x0004 || window > 0x4000)
		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_MESH_RECEIVER,
				       MGMT_STATUS_INVALID_PARAMS);

	if (window > period)
		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_MESH_RECEIVER,
				       MGMT_STATUS_INVALID_PARAMS);

	hci_dev_lock(hdev);

	cmd = mgmt_pending_add(sk, MGMT_OP_SET_MESH_RECEIVER, hdev, data, len);
@@ -6432,6 +6454,7 @@ static int set_scan_params(struct sock *sk, struct hci_dev *hdev,
		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
				       MGMT_STATUS_NOT_SUPPORTED);

	/* Keep allowed ranges in sync with set_mesh() */
	interval = __le16_to_cpu(cp->interval);

	if (interval < 0x0004 || interval > 0x4000)