Merge tag 'for-net-2025-06-27' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth
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: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -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
@@ -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
@@ -1080,7 +1080,8 @@ 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);
|
||||
hci_disable_advertising_sync(hdev);
|
||||
if (list_empty(&hdev->adv_instances))
|
||||
hci_disable_advertising_sync(hdev);
|
||||
mesh_tx = mgmt_mesh_next(hdev, NULL);
|
||||
|
||||
if (mesh_tx)
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user