mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
Bluetooth: MGMT: Fix advertising instances with Global Advertising
Global Advertising, enabled with MGMT_OP_SET_ADVERTISING, uses instance
0 and with extended advertising it has its own advertising set, but
instances were still being treated as if they shared the same set:
- Add Advertising and Add Extended Advertising Data would return
success without ever enabling the instance.
- Removing the last instance would disable all advertising sets,
stopping Global Advertising while leaving its setting enabled.
- Disabling Global Advertising would disable all sets, including
active instances.
Fix this by only overriding instances that share the advertising set of
Global Advertising, and by tracking HCI_LE_ADV_0 rather than HCI_LE_ADV
to determine the state of Global Advertising.
Fixes: 0d92808024 ("Bluetooth: HCI: Fix tracking of advertisement set/instance 0x00")
Assisted-by: OpenCode:claude-opus-5.5
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
parent
3d9526b171
commit
0ad48c88a2
|
|
@ -122,6 +122,7 @@ int hci_disable_per_advertising_sync(struct hci_dev *hdev, u8 instance);
|
|||
int hci_remove_advertising_sync(struct hci_dev *hdev, struct sock *sk,
|
||||
u8 instance, bool force);
|
||||
int hci_disable_advertising_sync(struct hci_dev *hdev);
|
||||
int hci_disable_ext_adv_legacy_instance_sync(struct hci_dev *hdev);
|
||||
int hci_clear_adv_instance_sync(struct hci_dev *hdev, struct sock *sk,
|
||||
u8 instance, bool force);
|
||||
int hci_update_passive_scan_sync(struct hci_dev *hdev);
|
||||
|
|
|
|||
|
|
@ -1686,15 +1686,22 @@ static u8 hci_cc_le_set_ext_adv_enable(struct hci_dev *hdev, void *data,
|
|||
hci_dev_clear_flag(hdev, HCI_LE_ADV_0);
|
||||
|
||||
/* If just one instance was disabled check if there are
|
||||
* any other instance enabled before clearing HCI_LE_ADV
|
||||
* any other instance enabled, including instance 0,
|
||||
* before clearing HCI_LE_ADV
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ADV_0))
|
||||
goto unlock;
|
||||
|
||||
list_for_each_entry_safe(adv, n, &hdev->adv_instances,
|
||||
list) {
|
||||
if (adv->enabled)
|
||||
goto unlock;
|
||||
}
|
||||
} else {
|
||||
/* All instances shall be considered disabled */
|
||||
/* All instances shall be considered disabled, except
|
||||
* instance 0 whose state is preserved so it can be
|
||||
* re-enabled by hci_resume_advertising_sync.
|
||||
*/
|
||||
list_for_each_entry_safe(adv, n, &hdev->adv_instances,
|
||||
list)
|
||||
adv->enabled = false;
|
||||
|
|
|
|||
|
|
@ -1158,7 +1158,7 @@ int hci_update_random_address_sync(struct hci_dev *hdev, bool require_privacy,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hci_disable_ext_adv_legacy_instance_sync(struct hci_dev *hdev)
|
||||
int hci_disable_ext_adv_legacy_instance_sync(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_cp_le_set_ext_adv_enable *cp;
|
||||
struct hci_cp_ext_adv_set *set;
|
||||
|
|
|
|||
|
|
@ -6546,7 +6546,12 @@ static void set_advertising_complete(struct hci_dev *hdev, void *data, int err)
|
|||
return;
|
||||
}
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
|
||||
/* With extended advertising global advertising (instance 0) has its
|
||||
* own advertising set so only consider its state, other instances may
|
||||
* still be enabled.
|
||||
*/
|
||||
if (ext_adv_capable(hdev) ? hci_dev_test_flag(hdev, HCI_LE_ADV_0) :
|
||||
hci_dev_test_flag(hdev, HCI_LE_ADV))
|
||||
hci_dev_set_flag(hdev, HCI_ADVERTISING);
|
||||
else
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING);
|
||||
|
|
@ -6566,6 +6571,34 @@ static void set_advertising_complete(struct hci_dev *hdev, void *data, int err)
|
|||
list_empty(&hdev->adv_instances))
|
||||
return;
|
||||
|
||||
/* With extended advertising instances are not overridden by global
|
||||
* advertising, except the ones sharing its set, so only (re)start the
|
||||
* instances that are not already enabled.
|
||||
*
|
||||
* Lookup instances by identifier while holding hdev->lock since the
|
||||
* list may change while waiting for the HCI commands to complete.
|
||||
*/
|
||||
if (ext_adv_capable(hdev)) {
|
||||
u16 i;
|
||||
|
||||
for (i = 1; i <= hdev->le_num_of_adv_sets + 1; i++) {
|
||||
bool enabled;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
adv_instance = hci_find_adv_instance(hdev, i);
|
||||
enabled = !adv_instance || adv_instance->enabled;
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
if (enabled)
|
||||
continue;
|
||||
|
||||
err = hci_schedule_adv_instance_sync(hdev, i, true);
|
||||
enable_advertising_instance(hdev, err);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
instance = hdev->cur_adv_instance;
|
||||
if (!instance) {
|
||||
adv_instance = list_first_entry_or_null(&hdev->adv_instances,
|
||||
|
|
@ -6621,6 +6654,11 @@ static int set_adv_sync(struct hci_dev *hdev, void *data)
|
|||
hci_update_scan_rsp_data_sync(hdev, 0x00);
|
||||
hci_enable_advertising_sync(hdev);
|
||||
}
|
||||
} else if (ext_adv_capable(hdev)) {
|
||||
/* Only disable global advertising (instance 0) leaving other
|
||||
* instances untouched.
|
||||
*/
|
||||
hci_disable_ext_adv_legacy_instance_sync(hdev);
|
||||
} else {
|
||||
hci_disable_advertising_sync(hdev);
|
||||
}
|
||||
|
|
@ -8952,6 +8990,21 @@ static bool adv_busy(struct hci_dev *hdev)
|
|||
return pending_find(MGMT_OP_SET_LE, hdev);
|
||||
}
|
||||
|
||||
/* Global advertising, enabled with MGMT_OP_SET_ADVERTISING, uses instance 0
|
||||
* and takes precedence over any instance that would share its advertising
|
||||
* set: with legacy advertising there is only a single set so all instances
|
||||
* are overridden, whereas with extended advertising only an instance using
|
||||
* handle 0x00 is.
|
||||
*/
|
||||
static bool adv_overridden_by_global(struct hci_dev *hdev,
|
||||
struct adv_info *adv)
|
||||
{
|
||||
if (!hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
return false;
|
||||
|
||||
return !ext_adv_capable(hdev) || !adv->handle;
|
||||
}
|
||||
|
||||
static void add_adv_complete(struct hci_dev *hdev, struct sock *sk, u8 instance,
|
||||
int err)
|
||||
{
|
||||
|
|
@ -9113,12 +9166,12 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
|||
schedule_instance = cp->instance;
|
||||
}
|
||||
|
||||
/* If the HCI_ADVERTISING flag is set or the device isn't powered or
|
||||
* there is no instance to be advertised then we have no HCI
|
||||
* communication to make. Simply return.
|
||||
/* If the instance is overridden by global advertising or the device
|
||||
* isn't powered or there is no instance to be advertised then we have
|
||||
* no HCI communication to make. Simply return.
|
||||
*/
|
||||
if (!hdev_is_powered(hdev) ||
|
||||
hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
adv_overridden_by_global(hdev, adv) ||
|
||||
!schedule_instance) {
|
||||
rp.instance = cp->instance;
|
||||
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
|
|
@ -9293,8 +9346,11 @@ static int add_ext_adv_params(struct sock *sk, struct hci_dev *hdev,
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
/* Submit request for advertising params if ext adv available */
|
||||
if (ext_adv_capable(hdev)) {
|
||||
/* Submit request for advertising params if ext adv available, unless
|
||||
* the set is in use by global advertising in which case the params
|
||||
* will be programmed once the instance is scheduled.
|
||||
*/
|
||||
if (ext_adv_capable(hdev) && !adv_overridden_by_global(hdev, adv)) {
|
||||
cmd = mgmt_pending_new(sk, MGMT_OP_ADD_EXT_ADV_PARAMS, hdev,
|
||||
data, data_len);
|
||||
if (!cmd) {
|
||||
|
|
@ -9444,11 +9500,12 @@ static int add_ext_adv_data(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
schedule_instance = cp->instance;
|
||||
}
|
||||
|
||||
/* If the HCI_ADVERTISING flag is set or there is no instance to
|
||||
* be advertised then we have no HCI communication to make.
|
||||
/* If the instance is overridden by global advertising or there is no
|
||||
* instance to be advertised then we have no HCI communication to make.
|
||||
* Simply return.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING) || !schedule_instance) {
|
||||
if (adv_overridden_by_global(hdev, adv_instance) ||
|
||||
!schedule_instance) {
|
||||
if (adv_instance->pending) {
|
||||
mgmt_advertising_added(sk, hdev, cp->instance);
|
||||
adv_instance->pending = false;
|
||||
|
|
@ -9524,7 +9581,11 @@ static int remove_advertising_sync(struct hci_dev *hdev, void *data)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
if (list_empty(&hdev->adv_instances))
|
||||
/* Don't disable advertising if global advertising (instance 0) is
|
||||
* enabled since it is not affected by removing instances.
|
||||
*/
|
||||
if (list_empty(&hdev->adv_instances) &&
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
err = hci_disable_advertising_sync(hdev);
|
||||
|
||||
return err;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user