* [PATCH BlueZ 0/1] Add configurable default LE PHY policy
@ 2026-05-24 22:14 Tarjei Bitustøyl
2026-05-24 22:14 ` [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
` (3 more replies)
0 siblings, 4 replies; 14+ messages in thread
From: Tarjei Bitustøyl @ 2026-05-24 22:14 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Tarjei Bitustøyl
This patch adds an opt-in bluetoothd configuration for the adapter's
default LE PHY policy.
It is motivated by a reproduced AX210 controller failure with a
Frostbay BLE device. In that setup, LE discovery fails unless the
adapter is limited to LE 1M TX/RX. A btmon trace shows the peer
returning a valid ATT Exchange MTU Response, but the controller/kernel
path reports it with the wrong ACL packet boundary flag, which leaves
service resolution incomplete.
Rather than adding a peer-specific quirk, this patch exposes a
generic [LE] DefaultPHYs setting in main.conf and applies it through
the existing mgmt PHY configuration interface.
The setting is adapter-wide, so it affects LE scanning and connection
establishment initiated through bluetoothd, including desktop clients
such as GNOME. Only configurable LE PHY bits are changed, and
mandatory controller-selected PHYs are preserved.
This may also be relevant to unresolved Intel-controller LE
interoperability reports such as BlueZ issues #1193 and #1557, but
those have not been re-tested with this patch and are not claimed as
fixed here.
Tarjei Bitustøyl (1):
adapter: Add configurable default LE PHYs
src/adapter.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/btd.h | 2 ++
src/main.c | 61 +++++++++++++++++++++++++++++++++++++++++
src/main.conf | 8 ++++++
4 files changed, 147 insertions(+)
--
2.43.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs
2026-05-24 22:14 [PATCH BlueZ 0/1] Add configurable default LE PHY policy Tarjei Bitustøyl
@ 2026-05-24 22:14 ` Tarjei Bitustøyl
2026-05-24 23:07 ` Add configurable default LE PHY policy bluez.test.bot
2026-05-26 14:27 ` [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs Luiz Augusto von Dentz
2026-05-25 6:50 ` [PATCH BlueZ v2 0/1] Add configurable default LE PHY policy Tarjei Bitustøyl
` (2 subsequent siblings)
3 siblings, 2 replies; 14+ messages in thread
From: Tarjei Bitustøyl @ 2026-05-24 22:14 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Tarjei Bitustøyl
Some controllers mis-handle LE procedures on specific PHYs with
certain peers. On an Intel AX210-class controller, connecting to a
Frostbay BLE device can fail during early ATT/GATT setup unless the
adapter is limited to LE 1M TX/RX.
Add an opt-in [LE] DefaultPHYs setting to bluetoothd and apply it at
adapter startup using MGMT_OP_GET/SET_PHY_CONFIGURATION while
preserving non-configurable PHY bits.
This provides a generic, adapter-wide workaround for controller-
specific LE PHY interoperability problems affecting scanning and
connection establishment, without adding device-specific quirks.
---
src/adapter.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/btd.h | 2 ++
src/main.c | 61 +++++++++++++++++++++++++++++++++++++++++
src/main.conf | 8 ++++++
4 files changed, 147 insertions(+)
diff --git a/src/adapter.c b/src/adapter.c
index 20f7c3e03..fcbb65e38 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -4972,6 +4972,81 @@ done:
mgmt_tlv_list_free(list);
}
+static void set_default_le_phys_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ if (status != MGMT_STATUS_SUCCESS)
+ btd_error(adapter->dev_id,
+ "Failed to set default LE PHYs for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+}
+
+static void get_default_le_phys_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const struct mgmt_rp_get_phy_confguration *rp = param;
+ struct mgmt_cp_set_phy_confguration cp;
+ uint32_t configurable_phys;
+ uint32_t selected_phys;
+ uint32_t next_phys;
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ btd_error(adapter->dev_id,
+ "Failed to read PHY configuration for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ return;
+ }
+
+ if (length < sizeof(*rp)) {
+ btd_error(adapter->dev_id,
+ "Too small get PHY configuration response for hci%u",
+ adapter->dev_id);
+ return;
+ }
+
+ configurable_phys = btohl(rp->configurable_phys);
+ selected_phys = btohl(rp->selected_phys);
+
+ configurable_phys &= MGMT_PHY_LE_TX_MASK | MGMT_PHY_LE_RX_MASK;
+ next_phys = selected_phys & ~configurable_phys;
+ next_phys |= btd_opts.default_le_phys & configurable_phys;
+
+ if (next_phys == selected_phys)
+ return;
+
+ cp.selected_phys = cpu_to_le32(next_phys);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_PHY_CONFIGURATION,
+ adapter->dev_id, sizeof(cp), &cp,
+ set_default_le_phys_complete, adapter, NULL) > 0)
+ return;
+
+ btd_error(adapter->dev_id,
+ "Failed to set default LE PHYs for hci%u",
+ adapter->dev_id);
+}
+
+static void load_default_le_phys(struct btd_adapter *adapter)
+{
+ if (!btd_opts.default_le_phys_configured)
+ return;
+
+ if (!(adapter->supported_settings & MGMT_SETTING_LE))
+ return;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_GET_PHY_CONFIGURATION,
+ adapter->dev_id, 0, NULL,
+ get_default_le_phys_complete, adapter, NULL) > 0)
+ return;
+
+ btd_error(adapter->dev_id,
+ "Failed to read PHY configuration for hci%u",
+ adapter->dev_id);
+}
+
static void load_devices(struct btd_adapter *adapter)
{
char dirname[PATH_MAX];
@@ -9455,6 +9530,7 @@ load:
btd_profile_foreach(probe_profile, adapter);
clear_blocked(adapter);
load_defaults(adapter);
+ load_default_le_phys(adapter);
load_devices(adapter);
/* restore Service Changed CCC value for bonded devices */
diff --git a/src/btd.h b/src/btd.h
index db2e81239..59f44dc8c 100644
--- a/src/btd.h
+++ b/src/btd.h
@@ -140,6 +140,8 @@ struct btd_opts {
bool device_privacy;
uint32_t name_request_retry_delay;
uint8_t secure_conn;
+ uint32_t default_le_phys;
+ bool default_le_phys_configured;
struct btd_defaults defaults;
diff --git a/src/main.c b/src/main.c
index 8aa19a3e3..97c64845b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -32,6 +32,7 @@
#include <dbus/dbus.h>
#include "bluetooth/bluetooth.h"
+#include "bluetooth/mgmt.h"
#include "bluetooth/sdp.h"
#include "gdbus/gdbus.h"
@@ -132,6 +133,7 @@ static const char *le_options[] = {
"Autoconnecttimeout",
"AdvMonAllowlistScanDuration",
"AdvMonNoFilterScanDuration",
+ "DefaultPHYs",
"EnableAdvMonInterleaveScan",
NULL
};
@@ -145,6 +147,8 @@ static const char *policy_options[] = {
NULL
};
+static void parse_default_le_phys(GKeyFile *config);
+
static const char *gatt_options[] = {
"Cache",
"KeySize",
@@ -751,6 +755,7 @@ static void parse_le_config(GKeyFile *config)
return;
parse_mode_config(config, "LE", params, ARRAY_SIZE(params));
+ parse_default_le_phys(config);
}
static bool match_experimental(const void *data, const void *match_data)
@@ -966,6 +971,62 @@ static void parse_repairing(GKeyFile *config)
g_free(str);
}
+struct phy_config_entry {
+ const char *name;
+ uint32_t bit;
+};
+
+static const struct phy_config_entry le_phy_configs[] = {
+ { "LE1MTX", MGMT_PHY_LE_1M_TX },
+ { "LE1MRX", MGMT_PHY_LE_1M_RX },
+ { "LE2MTX", MGMT_PHY_LE_2M_TX },
+ { "LE2MRX", MGMT_PHY_LE_2M_RX },
+ { "LECODEDTX", MGMT_PHY_LE_CODED_TX },
+ { "LECODEDRX", MGMT_PHY_LE_CODED_RX },
+};
+
+static void parse_default_le_phys(GKeyFile *config)
+{
+ char *str = NULL;
+ char **tokens;
+ uint32_t phys = 0;
+ bool valid = false;
+ int i;
+
+ if (!parse_config_string(config, "LE", "DefaultPHYs", &str))
+ return;
+
+ tokens = g_strsplit_set(str, ", \t", -1);
+
+ for (i = 0; tokens[i]; i++) {
+ const char *token = tokens[i];
+ size_t j;
+
+ if (!token[0])
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(le_phy_configs); j++) {
+ if (strcasecmp(le_phy_configs[j].name, token) != 0)
+ continue;
+
+ phys |= le_phy_configs[j].bit;
+ valid = true;
+ break;
+ }
+
+ if (j == ARRAY_SIZE(le_phy_configs))
+ warn("Invalid DefaultPHYs token: %s", token);
+ }
+
+ if (valid) {
+ btd_opts.default_le_phys = phys;
+ btd_opts.default_le_phys_configured = true;
+ }
+
+ g_strfreev(tokens);
+ g_free(str);
+}
+
static bool parse_config_hex(GKeyFile *config, char *group,
const char *key, uint32_t *val)
{
diff --git a/src/main.conf b/src/main.conf
index 5846ef92d..ed955897e 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -247,6 +247,14 @@
# Default: 500
#AdvMonNoFilterScanDuration=
+# Configure the controller's default LE PHY policy used for scanning and
+# connection establishment. Only configurable LE PHYs are changed; mandatory
+# PHYs remain selected automatically.
+# Possible values: comma or space separated list of LE1MTX, LE1MRX, LE2MTX,
+# LE2MRX, LECODEDTX, LECODEDRX.
+# Example: keep LE on 1M only.
+#DefaultPHYs = LE1MTX LE1MRX
+
# Enable/Disable Advertisement Monitor interleave scan for power saving.
# 0: disable
# 1: enable
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* RE: Add configurable default LE PHY policy
2026-05-24 22:14 ` [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
@ 2026-05-24 23:07 ` bluez.test.bot
2026-05-26 14:27 ` [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs Luiz Augusto von Dentz
1 sibling, 0 replies; 14+ messages in thread
From: bluez.test.bot @ 2026-05-24 23:07 UTC (permalink / raw)
To: linux-bluetooth, tarjeib
[-- Attachment #1: Type: text/plain, Size: 993 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1100107
---Test result---
Test Summary:
CheckPatch PASS 0.39 seconds
GitLint PASS 0.22 seconds
BuildEll PASS 15.83 seconds
BluezMake PASS 497.32 seconds
CheckSmatch WARNING 252.29 seconds
bluezmakeextell PASS 127.97 seconds
IncrementalBuild PASS 492.03 seconds
ScanBuild PASS 721.90 seconds
Details
##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
src/main.c: note: in included file (through src/device.h):
https://github.com/bluez/bluez/pull/2148
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH BlueZ v2 0/1] Add configurable default LE PHY policy
2026-05-24 22:14 [PATCH BlueZ 0/1] Add configurable default LE PHY policy Tarjei Bitustøyl
2026-05-24 22:14 ` [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
@ 2026-05-25 6:50 ` Tarjei Bitustøyl
2026-05-25 6:50 ` [PATCH BlueZ v2 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
2026-05-25 11:37 ` [PATCH BlueZ v3 0/1] " Tarjei Bitustøyl
2026-05-25 14:50 ` [PATCH BlueZ v4 0/1] " Tarjei Bitustøyl
3 siblings, 1 reply; 14+ messages in thread
From: Tarjei Bitustøyl @ 2026-05-25 6:50 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Tarjei Bitustøyl
This adds an opt-in bluetoothd setting for the adapter's default LE PHY
policy.
v2:
- Check MGMT_SETTING_PHY_CONFIGURATION before sending PHY
configuration commands.
Tarjei Bitustøyl (1):
adapter: Add configurable default LE PHYs
src/adapter.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/btd.h | 2 ++
src/main.c | 61 +++++++++++++++++++++++++++++++++++++++
src/main.conf | 8 ++++++
4 files changed, 150 insertions(+)
--
2.43.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH BlueZ v2 1/1] adapter: Add configurable default LE PHYs
2026-05-25 6:50 ` [PATCH BlueZ v2 0/1] Add configurable default LE PHY policy Tarjei Bitustøyl
@ 2026-05-25 6:50 ` Tarjei Bitustøyl
2026-05-25 11:04 ` Add configurable default LE PHY policy bluez.test.bot
0 siblings, 1 reply; 14+ messages in thread
From: Tarjei Bitustøyl @ 2026-05-25 6:50 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Tarjei Bitustøyl
Some controllers mis-handle LE procedures on specific PHYs with
certain peers. On an Intel AX210-class controller, connecting to a
Frostbay BLE device can fail during early ATT/GATT setup unless the
adapter is limited to LE 1M TX/RX.
Add an opt-in [LE] DefaultPHYs setting to bluetoothd and apply it at
adapter startup using MGMT_OP_GET/SET_PHY_CONFIGURATION while
preserving non-configurable PHY bits.
This provides a generic, adapter-wide workaround for controller-
specific LE PHY interoperability problems affecting scanning and
connection establishment, without adding device-specific quirks.
v2:
- Check MGMT_SETTING_PHY_CONFIGURATION before sending PHY
configuration commands.
---
src/adapter.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/btd.h | 2 ++
src/main.c | 61 +++++++++++++++++++++++++++++++++++++++
src/main.conf | 8 ++++++
4 files changed, 150 insertions(+)
diff --git a/src/adapter.c b/src/adapter.c
index 20f7c3e03..46df362c5 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -4972,6 +4972,84 @@ done:
mgmt_tlv_list_free(list);
}
+static void set_default_le_phys_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ if (status != MGMT_STATUS_SUCCESS)
+ btd_error(adapter->dev_id,
+ "Failed to set default LE PHYs for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+}
+
+static void get_default_le_phys_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const struct mgmt_rp_get_phy_confguration *rp = param;
+ struct mgmt_cp_set_phy_confguration cp;
+ uint32_t configurable_phys;
+ uint32_t selected_phys;
+ uint32_t next_phys;
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ btd_error(adapter->dev_id,
+ "Failed to read PHY configuration for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ return;
+ }
+
+ if (length < sizeof(*rp)) {
+ btd_error(adapter->dev_id,
+ "Too small get PHY configuration response for hci%u",
+ adapter->dev_id);
+ return;
+ }
+
+ configurable_phys = btohl(rp->configurable_phys);
+ selected_phys = btohl(rp->selected_phys);
+
+ configurable_phys &= MGMT_PHY_LE_TX_MASK | MGMT_PHY_LE_RX_MASK;
+ next_phys = selected_phys & ~configurable_phys;
+ next_phys |= btd_opts.default_le_phys & configurable_phys;
+
+ if (next_phys == selected_phys)
+ return;
+
+ cp.selected_phys = cpu_to_le32(next_phys);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_PHY_CONFIGURATION,
+ adapter->dev_id, sizeof(cp), &cp,
+ set_default_le_phys_complete, adapter, NULL) > 0)
+ return;
+
+ btd_error(adapter->dev_id,
+ "Failed to set default LE PHYs for hci%u",
+ adapter->dev_id);
+}
+
+static void load_default_le_phys(struct btd_adapter *adapter)
+{
+ if (!btd_opts.default_le_phys_configured)
+ return;
+
+ if (!(adapter->supported_settings & MGMT_SETTING_LE))
+ return;
+
+ if (!(adapter->supported_settings & MGMT_SETTING_PHY_CONFIGURATION))
+ return;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_GET_PHY_CONFIGURATION,
+ adapter->dev_id, 0, NULL,
+ get_default_le_phys_complete, adapter, NULL) > 0)
+ return;
+
+ btd_error(adapter->dev_id,
+ "Failed to read PHY configuration for hci%u",
+ adapter->dev_id);
+}
+
static void load_devices(struct btd_adapter *adapter)
{
char dirname[PATH_MAX];
@@ -9455,6 +9533,7 @@ load:
btd_profile_foreach(probe_profile, adapter);
clear_blocked(adapter);
load_defaults(adapter);
+ load_default_le_phys(adapter);
load_devices(adapter);
/* restore Service Changed CCC value for bonded devices */
diff --git a/src/btd.h b/src/btd.h
index db2e81239..59f44dc8c 100644
--- a/src/btd.h
+++ b/src/btd.h
@@ -140,6 +140,8 @@ struct btd_opts {
bool device_privacy;
uint32_t name_request_retry_delay;
uint8_t secure_conn;
+ uint32_t default_le_phys;
+ bool default_le_phys_configured;
struct btd_defaults defaults;
diff --git a/src/main.c b/src/main.c
index 8aa19a3e3..97c64845b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -32,6 +32,7 @@
#include <dbus/dbus.h>
#include "bluetooth/bluetooth.h"
+#include "bluetooth/mgmt.h"
#include "bluetooth/sdp.h"
#include "gdbus/gdbus.h"
@@ -132,6 +133,7 @@ static const char *le_options[] = {
"Autoconnecttimeout",
"AdvMonAllowlistScanDuration",
"AdvMonNoFilterScanDuration",
+ "DefaultPHYs",
"EnableAdvMonInterleaveScan",
NULL
};
@@ -145,6 +147,8 @@ static const char *policy_options[] = {
NULL
};
+static void parse_default_le_phys(GKeyFile *config);
+
static const char *gatt_options[] = {
"Cache",
"KeySize",
@@ -751,6 +755,7 @@ static void parse_le_config(GKeyFile *config)
return;
parse_mode_config(config, "LE", params, ARRAY_SIZE(params));
+ parse_default_le_phys(config);
}
static bool match_experimental(const void *data, const void *match_data)
@@ -966,6 +971,62 @@ static void parse_repairing(GKeyFile *config)
g_free(str);
}
+struct phy_config_entry {
+ const char *name;
+ uint32_t bit;
+};
+
+static const struct phy_config_entry le_phy_configs[] = {
+ { "LE1MTX", MGMT_PHY_LE_1M_TX },
+ { "LE1MRX", MGMT_PHY_LE_1M_RX },
+ { "LE2MTX", MGMT_PHY_LE_2M_TX },
+ { "LE2MRX", MGMT_PHY_LE_2M_RX },
+ { "LECODEDTX", MGMT_PHY_LE_CODED_TX },
+ { "LECODEDRX", MGMT_PHY_LE_CODED_RX },
+};
+
+static void parse_default_le_phys(GKeyFile *config)
+{
+ char *str = NULL;
+ char **tokens;
+ uint32_t phys = 0;
+ bool valid = false;
+ int i;
+
+ if (!parse_config_string(config, "LE", "DefaultPHYs", &str))
+ return;
+
+ tokens = g_strsplit_set(str, ", \t", -1);
+
+ for (i = 0; tokens[i]; i++) {
+ const char *token = tokens[i];
+ size_t j;
+
+ if (!token[0])
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(le_phy_configs); j++) {
+ if (strcasecmp(le_phy_configs[j].name, token) != 0)
+ continue;
+
+ phys |= le_phy_configs[j].bit;
+ valid = true;
+ break;
+ }
+
+ if (j == ARRAY_SIZE(le_phy_configs))
+ warn("Invalid DefaultPHYs token: %s", token);
+ }
+
+ if (valid) {
+ btd_opts.default_le_phys = phys;
+ btd_opts.default_le_phys_configured = true;
+ }
+
+ g_strfreev(tokens);
+ g_free(str);
+}
+
static bool parse_config_hex(GKeyFile *config, char *group,
const char *key, uint32_t *val)
{
diff --git a/src/main.conf b/src/main.conf
index 5846ef92d..ed955897e 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -247,6 +247,14 @@
# Default: 500
#AdvMonNoFilterScanDuration=
+# Configure the controller's default LE PHY policy used for scanning and
+# connection establishment. Only configurable LE PHYs are changed; mandatory
+# PHYs remain selected automatically.
+# Possible values: comma or space separated list of LE1MTX, LE1MRX, LE2MTX,
+# LE2MRX, LECODEDTX, LECODEDRX.
+# Example: keep LE on 1M only.
+#DefaultPHYs = LE1MTX LE1MRX
+
# Enable/Disable Advertisement Monitor interleave scan for power saving.
# 0: disable
# 1: enable
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* RE: Add configurable default LE PHY policy
2026-05-25 6:50 ` [PATCH BlueZ v2 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
@ 2026-05-25 11:04 ` bluez.test.bot
0 siblings, 0 replies; 14+ messages in thread
From: bluez.test.bot @ 2026-05-25 11:04 UTC (permalink / raw)
To: linux-bluetooth, tarjeib
[-- Attachment #1: Type: text/plain, Size: 993 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1100262
---Test result---
Test Summary:
CheckPatch PASS 0.58 seconds
GitLint PASS 0.33 seconds
BuildEll PASS 20.00 seconds
BluezMake PASS 621.23 seconds
CheckSmatch WARNING 323.73 seconds
bluezmakeextell PASS 164.72 seconds
IncrementalBuild PASS 622.45 seconds
ScanBuild PASS 920.21 seconds
Details
##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
src/main.c: note: in included file (through src/device.h):
https://github.com/bluez/bluez/pull/2150
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH BlueZ v3 0/1] Add configurable default LE PHY policy
2026-05-24 22:14 [PATCH BlueZ 0/1] Add configurable default LE PHY policy Tarjei Bitustøyl
2026-05-24 22:14 ` [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
2026-05-25 6:50 ` [PATCH BlueZ v2 0/1] Add configurable default LE PHY policy Tarjei Bitustøyl
@ 2026-05-25 11:37 ` Tarjei Bitustøyl
2026-05-25 11:37 ` [PATCH BlueZ v3 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
2026-05-25 14:50 ` [PATCH BlueZ v4 0/1] " Tarjei Bitustøyl
3 siblings, 1 reply; 14+ messages in thread
From: Tarjei Bitustøyl @ 2026-05-25 11:37 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Tarjei Bitustøyl
This adds an opt-in bluetoothd setting for the adapter's default LE PHY
policy.
v3:
- Match the queue header include spelling in main.c with device.h to
avoid the Sparse redefinition warning seen in CI.
v2:
- Check MGMT_SETTING_PHY_CONFIGURATION before sending PHY
configuration commands.
Tarjei Bitustøyl (1):
adapter: Add configurable default LE PHYs
src/adapter.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/btd.h | 2 ++
src/main.c | 63 +++++++++++++++++++++++++++++++++++++++-
src/main.conf | 8 ++++++
4 files changed, 151 insertions(+), 1 deletion(-)
--
2.43.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH BlueZ v3 1/1] adapter: Add configurable default LE PHYs
2026-05-25 11:37 ` [PATCH BlueZ v3 0/1] " Tarjei Bitustøyl
@ 2026-05-25 11:37 ` Tarjei Bitustøyl
2026-05-25 14:33 ` Add configurable default LE PHY policy bluez.test.bot
0 siblings, 1 reply; 14+ messages in thread
From: Tarjei Bitustøyl @ 2026-05-25 11:37 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Tarjei Bitustøyl
Some controllers mis-handle LE procedures on specific PHYs with
certain peers. On an Intel AX210-class controller, connecting to a
Frostbay BLE device can fail during early ATT/GATT setup unless the
adapter is limited to LE 1M TX/RX.
Add an opt-in [LE] DefaultPHYs setting to bluetoothd and apply it at
adapter startup using MGMT_OP_GET/SET_PHY_CONFIGURATION while
preserving non-configurable PHY bits.
This provides a generic, adapter-wide workaround for controller-
specific LE PHY interoperability problems affecting scanning and
connection establishment, without adding device-specific quirks.
v3:
- Match the queue header include spelling in main.c with device.h to
avoid the Sparse redefinition warning seen in CI.
v2:
- Check MGMT_SETTING_PHY_CONFIGURATION before sending PHY
configuration commands.
---
src/adapter.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/btd.h | 2 ++
src/main.c | 63 +++++++++++++++++++++++++++++++++++++++-
src/main.conf | 8 ++++++
4 files changed, 151 insertions(+), 1 deletion(-)
diff --git a/src/adapter.c b/src/adapter.c
index 20f7c3e03..46df362c5 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -4972,6 +4972,84 @@ done:
mgmt_tlv_list_free(list);
}
+static void set_default_le_phys_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ if (status != MGMT_STATUS_SUCCESS)
+ btd_error(adapter->dev_id,
+ "Failed to set default LE PHYs for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+}
+
+static void get_default_le_phys_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const struct mgmt_rp_get_phy_confguration *rp = param;
+ struct mgmt_cp_set_phy_confguration cp;
+ uint32_t configurable_phys;
+ uint32_t selected_phys;
+ uint32_t next_phys;
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ btd_error(adapter->dev_id,
+ "Failed to read PHY configuration for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ return;
+ }
+
+ if (length < sizeof(*rp)) {
+ btd_error(adapter->dev_id,
+ "Too small get PHY configuration response for hci%u",
+ adapter->dev_id);
+ return;
+ }
+
+ configurable_phys = btohl(rp->configurable_phys);
+ selected_phys = btohl(rp->selected_phys);
+
+ configurable_phys &= MGMT_PHY_LE_TX_MASK | MGMT_PHY_LE_RX_MASK;
+ next_phys = selected_phys & ~configurable_phys;
+ next_phys |= btd_opts.default_le_phys & configurable_phys;
+
+ if (next_phys == selected_phys)
+ return;
+
+ cp.selected_phys = cpu_to_le32(next_phys);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_PHY_CONFIGURATION,
+ adapter->dev_id, sizeof(cp), &cp,
+ set_default_le_phys_complete, adapter, NULL) > 0)
+ return;
+
+ btd_error(adapter->dev_id,
+ "Failed to set default LE PHYs for hci%u",
+ adapter->dev_id);
+}
+
+static void load_default_le_phys(struct btd_adapter *adapter)
+{
+ if (!btd_opts.default_le_phys_configured)
+ return;
+
+ if (!(adapter->supported_settings & MGMT_SETTING_LE))
+ return;
+
+ if (!(adapter->supported_settings & MGMT_SETTING_PHY_CONFIGURATION))
+ return;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_GET_PHY_CONFIGURATION,
+ adapter->dev_id, 0, NULL,
+ get_default_le_phys_complete, adapter, NULL) > 0)
+ return;
+
+ btd_error(adapter->dev_id,
+ "Failed to read PHY configuration for hci%u",
+ adapter->dev_id);
+}
+
static void load_devices(struct btd_adapter *adapter)
{
char dirname[PATH_MAX];
@@ -9455,6 +9533,7 @@ load:
btd_profile_foreach(probe_profile, adapter);
clear_blocked(adapter);
load_defaults(adapter);
+ load_default_le_phys(adapter);
load_devices(adapter);
/* restore Service Changed CCC value for bonded devices */
diff --git a/src/btd.h b/src/btd.h
index db2e81239..59f44dc8c 100644
--- a/src/btd.h
+++ b/src/btd.h
@@ -140,6 +140,8 @@ struct btd_opts {
bool device_privacy;
uint32_t name_request_retry_delay;
uint8_t secure_conn;
+ uint32_t default_le_phys;
+ bool default_le_phys_configured;
struct btd_defaults defaults;
diff --git a/src/main.c b/src/main.c
index 8aa19a3e3..83be19be3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -32,6 +32,7 @@
#include <dbus/dbus.h>
#include "bluetooth/bluetooth.h"
+#include "bluetooth/mgmt.h"
#include "bluetooth/sdp.h"
#include "gdbus/gdbus.h"
@@ -43,7 +44,7 @@
#include "shared/att-types.h"
#include "shared/mainloop.h"
#include "shared/timeout.h"
-#include "shared/queue.h"
+#include "src/shared/queue.h"
#include "shared/crypto.h"
#include "bluetooth/uuid.h"
#include "shared/util.h"
@@ -132,6 +133,7 @@ static const char *le_options[] = {
"Autoconnecttimeout",
"AdvMonAllowlistScanDuration",
"AdvMonNoFilterScanDuration",
+ "DefaultPHYs",
"EnableAdvMonInterleaveScan",
NULL
};
@@ -145,6 +147,8 @@ static const char *policy_options[] = {
NULL
};
+static void parse_default_le_phys(GKeyFile *config);
+
static const char *gatt_options[] = {
"Cache",
"KeySize",
@@ -751,6 +755,7 @@ static void parse_le_config(GKeyFile *config)
return;
parse_mode_config(config, "LE", params, ARRAY_SIZE(params));
+ parse_default_le_phys(config);
}
static bool match_experimental(const void *data, const void *match_data)
@@ -966,6 +971,62 @@ static void parse_repairing(GKeyFile *config)
g_free(str);
}
+struct phy_config_entry {
+ const char *name;
+ uint32_t bit;
+};
+
+static const struct phy_config_entry le_phy_configs[] = {
+ { "LE1MTX", MGMT_PHY_LE_1M_TX },
+ { "LE1MRX", MGMT_PHY_LE_1M_RX },
+ { "LE2MTX", MGMT_PHY_LE_2M_TX },
+ { "LE2MRX", MGMT_PHY_LE_2M_RX },
+ { "LECODEDTX", MGMT_PHY_LE_CODED_TX },
+ { "LECODEDRX", MGMT_PHY_LE_CODED_RX },
+};
+
+static void parse_default_le_phys(GKeyFile *config)
+{
+ char *str = NULL;
+ char **tokens;
+ uint32_t phys = 0;
+ bool valid = false;
+ int i;
+
+ if (!parse_config_string(config, "LE", "DefaultPHYs", &str))
+ return;
+
+ tokens = g_strsplit_set(str, ", \t", -1);
+
+ for (i = 0; tokens[i]; i++) {
+ const char *token = tokens[i];
+ size_t j;
+
+ if (!token[0])
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(le_phy_configs); j++) {
+ if (strcasecmp(le_phy_configs[j].name, token) != 0)
+ continue;
+
+ phys |= le_phy_configs[j].bit;
+ valid = true;
+ break;
+ }
+
+ if (j == ARRAY_SIZE(le_phy_configs))
+ warn("Invalid DefaultPHYs token: %s", token);
+ }
+
+ if (valid) {
+ btd_opts.default_le_phys = phys;
+ btd_opts.default_le_phys_configured = true;
+ }
+
+ g_strfreev(tokens);
+ g_free(str);
+}
+
static bool parse_config_hex(GKeyFile *config, char *group,
const char *key, uint32_t *val)
{
diff --git a/src/main.conf b/src/main.conf
index 5846ef92d..ed955897e 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -247,6 +247,14 @@
# Default: 500
#AdvMonNoFilterScanDuration=
+# Configure the controller's default LE PHY policy used for scanning and
+# connection establishment. Only configurable LE PHYs are changed; mandatory
+# PHYs remain selected automatically.
+# Possible values: comma or space separated list of LE1MTX, LE1MRX, LE2MTX,
+# LE2MRX, LECODEDTX, LECODEDRX.
+# Example: keep LE on 1M only.
+#DefaultPHYs = LE1MTX LE1MRX
+
# Enable/Disable Advertisement Monitor interleave scan for power saving.
# 0: disable
# 1: enable
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* RE: Add configurable default LE PHY policy
2026-05-25 11:37 ` [PATCH BlueZ v3 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
@ 2026-05-25 14:33 ` bluez.test.bot
0 siblings, 0 replies; 14+ messages in thread
From: bluez.test.bot @ 2026-05-25 14:33 UTC (permalink / raw)
To: linux-bluetooth, tarjeib
[-- Attachment #1: Type: text/plain, Size: 1144 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1100433
---Test result---
Test Summary:
CheckPatch PASS 0.39 seconds
GitLint FAIL 0.21 seconds
BuildEll PASS 19.92 seconds
BluezMake PASS 646.41 seconds
CheckSmatch PASS 354.93 seconds
bluezmakeextell PASS 182.02 seconds
IncrementalBuild PASS 665.89 seconds
ScanBuild PASS 1045.30 seconds
Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,v3,1/1] adapter: Add configurable default LE PHYs
18: B3 Line contains hard tab characters (\t): " avoid the Sparse redefinition warning seen in CI."
22: B3 Line contains hard tab characters (\t): " configuration commands."
https://github.com/bluez/bluez/pull/2152
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH BlueZ v4 0/1] Add configurable default LE PHY policy
2026-05-24 22:14 [PATCH BlueZ 0/1] Add configurable default LE PHY policy Tarjei Bitustøyl
` (2 preceding siblings ...)
2026-05-25 11:37 ` [PATCH BlueZ v3 0/1] " Tarjei Bitustøyl
@ 2026-05-25 14:50 ` Tarjei Bitustøyl
2026-05-25 14:50 ` [PATCH BlueZ v4 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
3 siblings, 1 reply; 14+ messages in thread
From: Tarjei Bitustøyl @ 2026-05-25 14:50 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Tarjei Bitustøyl
This adds an opt-in bluetoothd setting for the adapter's default LE PHY
policy.
v4:
- Replace the hard tabs in the changelog continuation lines with spaces
to satisfy GitLint.
v3:
- Match the queue header include spelling in main.c with device.h to
avoid the Sparse redefinition warning seen in CI.
v2:
- Check MGMT_SETTING_PHY_CONFIGURATION before sending PHY
configuration commands.
Tarjei Bitustøyl (1):
adapter: Add configurable default LE PHYs
src/adapter.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/btd.h | 2 ++
src/main.c | 63 +++++++++++++++++++++++++++++++++++++++-
src/main.conf | 8 ++++++
4 files changed, 151 insertions(+), 1 deletion(-)
--
2.43.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH BlueZ v4 1/1] adapter: Add configurable default LE PHYs
2026-05-25 14:50 ` [PATCH BlueZ v4 0/1] " Tarjei Bitustøyl
@ 2026-05-25 14:50 ` Tarjei Bitustøyl
2026-05-25 16:55 ` Add configurable default LE PHY policy bluez.test.bot
0 siblings, 1 reply; 14+ messages in thread
From: Tarjei Bitustøyl @ 2026-05-25 14:50 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Tarjei Bitustøyl
Some controllers mis-handle LE procedures on specific PHYs with
certain peers. On an Intel AX210-class controller, connecting to a
Frostbay BLE device can fail during early ATT/GATT setup unless the
adapter is limited to LE 1M TX/RX.
Add an opt-in [LE] DefaultPHYs setting to bluetoothd and apply it at
adapter startup using MGMT_OP_GET/SET_PHY_CONFIGURATION while
preserving non-configurable PHY bits.
This provides a generic, adapter-wide workaround for controller-
specific LE PHY interoperability problems affecting scanning and
connection establishment, without adding device-specific quirks.
v4:
- Replace the hard tabs in the changelog continuation lines with spaces
to satisfy GitLint.
v3:
- Match the queue header include spelling in main.c with device.h to
avoid the Sparse redefinition warning seen in CI.
v2:
- Check MGMT_SETTING_PHY_CONFIGURATION before sending PHY
configuration commands.
---
src/adapter.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/btd.h | 2 ++
src/main.c | 63 +++++++++++++++++++++++++++++++++++++++-
src/main.conf | 8 ++++++
4 files changed, 151 insertions(+), 1 deletion(-)
diff --git a/src/adapter.c b/src/adapter.c
index 20f7c3e03..46df362c5 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -4972,6 +4972,84 @@ done:
mgmt_tlv_list_free(list);
}
+static void set_default_le_phys_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ if (status != MGMT_STATUS_SUCCESS)
+ btd_error(adapter->dev_id,
+ "Failed to set default LE PHYs for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+}
+
+static void get_default_le_phys_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const struct mgmt_rp_get_phy_confguration *rp = param;
+ struct mgmt_cp_set_phy_confguration cp;
+ uint32_t configurable_phys;
+ uint32_t selected_phys;
+ uint32_t next_phys;
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ btd_error(adapter->dev_id,
+ "Failed to read PHY configuration for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ return;
+ }
+
+ if (length < sizeof(*rp)) {
+ btd_error(adapter->dev_id,
+ "Too small get PHY configuration response for hci%u",
+ adapter->dev_id);
+ return;
+ }
+
+ configurable_phys = btohl(rp->configurable_phys);
+ selected_phys = btohl(rp->selected_phys);
+
+ configurable_phys &= MGMT_PHY_LE_TX_MASK | MGMT_PHY_LE_RX_MASK;
+ next_phys = selected_phys & ~configurable_phys;
+ next_phys |= btd_opts.default_le_phys & configurable_phys;
+
+ if (next_phys == selected_phys)
+ return;
+
+ cp.selected_phys = cpu_to_le32(next_phys);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_PHY_CONFIGURATION,
+ adapter->dev_id, sizeof(cp), &cp,
+ set_default_le_phys_complete, adapter, NULL) > 0)
+ return;
+
+ btd_error(adapter->dev_id,
+ "Failed to set default LE PHYs for hci%u",
+ adapter->dev_id);
+}
+
+static void load_default_le_phys(struct btd_adapter *adapter)
+{
+ if (!btd_opts.default_le_phys_configured)
+ return;
+
+ if (!(adapter->supported_settings & MGMT_SETTING_LE))
+ return;
+
+ if (!(adapter->supported_settings & MGMT_SETTING_PHY_CONFIGURATION))
+ return;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_GET_PHY_CONFIGURATION,
+ adapter->dev_id, 0, NULL,
+ get_default_le_phys_complete, adapter, NULL) > 0)
+ return;
+
+ btd_error(adapter->dev_id,
+ "Failed to read PHY configuration for hci%u",
+ adapter->dev_id);
+}
+
static void load_devices(struct btd_adapter *adapter)
{
char dirname[PATH_MAX];
@@ -9455,6 +9533,7 @@ load:
btd_profile_foreach(probe_profile, adapter);
clear_blocked(adapter);
load_defaults(adapter);
+ load_default_le_phys(adapter);
load_devices(adapter);
/* restore Service Changed CCC value for bonded devices */
diff --git a/src/btd.h b/src/btd.h
index db2e81239..59f44dc8c 100644
--- a/src/btd.h
+++ b/src/btd.h
@@ -140,6 +140,8 @@ struct btd_opts {
bool device_privacy;
uint32_t name_request_retry_delay;
uint8_t secure_conn;
+ uint32_t default_le_phys;
+ bool default_le_phys_configured;
struct btd_defaults defaults;
diff --git a/src/main.c b/src/main.c
index 8aa19a3e3..83be19be3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -32,6 +32,7 @@
#include <dbus/dbus.h>
#include "bluetooth/bluetooth.h"
+#include "bluetooth/mgmt.h"
#include "bluetooth/sdp.h"
#include "gdbus/gdbus.h"
@@ -43,7 +44,7 @@
#include "shared/att-types.h"
#include "shared/mainloop.h"
#include "shared/timeout.h"
-#include "shared/queue.h"
+#include "src/shared/queue.h"
#include "shared/crypto.h"
#include "bluetooth/uuid.h"
#include "shared/util.h"
@@ -132,6 +133,7 @@ static const char *le_options[] = {
"Autoconnecttimeout",
"AdvMonAllowlistScanDuration",
"AdvMonNoFilterScanDuration",
+ "DefaultPHYs",
"EnableAdvMonInterleaveScan",
NULL
};
@@ -145,6 +147,8 @@ static const char *policy_options[] = {
NULL
};
+static void parse_default_le_phys(GKeyFile *config);
+
static const char *gatt_options[] = {
"Cache",
"KeySize",
@@ -751,6 +755,7 @@ static void parse_le_config(GKeyFile *config)
return;
parse_mode_config(config, "LE", params, ARRAY_SIZE(params));
+ parse_default_le_phys(config);
}
static bool match_experimental(const void *data, const void *match_data)
@@ -966,6 +971,62 @@ static void parse_repairing(GKeyFile *config)
g_free(str);
}
+struct phy_config_entry {
+ const char *name;
+ uint32_t bit;
+};
+
+static const struct phy_config_entry le_phy_configs[] = {
+ { "LE1MTX", MGMT_PHY_LE_1M_TX },
+ { "LE1MRX", MGMT_PHY_LE_1M_RX },
+ { "LE2MTX", MGMT_PHY_LE_2M_TX },
+ { "LE2MRX", MGMT_PHY_LE_2M_RX },
+ { "LECODEDTX", MGMT_PHY_LE_CODED_TX },
+ { "LECODEDRX", MGMT_PHY_LE_CODED_RX },
+};
+
+static void parse_default_le_phys(GKeyFile *config)
+{
+ char *str = NULL;
+ char **tokens;
+ uint32_t phys = 0;
+ bool valid = false;
+ int i;
+
+ if (!parse_config_string(config, "LE", "DefaultPHYs", &str))
+ return;
+
+ tokens = g_strsplit_set(str, ", \t", -1);
+
+ for (i = 0; tokens[i]; i++) {
+ const char *token = tokens[i];
+ size_t j;
+
+ if (!token[0])
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(le_phy_configs); j++) {
+ if (strcasecmp(le_phy_configs[j].name, token) != 0)
+ continue;
+
+ phys |= le_phy_configs[j].bit;
+ valid = true;
+ break;
+ }
+
+ if (j == ARRAY_SIZE(le_phy_configs))
+ warn("Invalid DefaultPHYs token: %s", token);
+ }
+
+ if (valid) {
+ btd_opts.default_le_phys = phys;
+ btd_opts.default_le_phys_configured = true;
+ }
+
+ g_strfreev(tokens);
+ g_free(str);
+}
+
static bool parse_config_hex(GKeyFile *config, char *group,
const char *key, uint32_t *val)
{
diff --git a/src/main.conf b/src/main.conf
index 5846ef92d..ed955897e 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -247,6 +247,14 @@
# Default: 500
#AdvMonNoFilterScanDuration=
+# Configure the controller's default LE PHY policy used for scanning and
+# connection establishment. Only configurable LE PHYs are changed; mandatory
+# PHYs remain selected automatically.
+# Possible values: comma or space separated list of LE1MTX, LE1MRX, LE2MTX,
+# LE2MRX, LECODEDTX, LECODEDRX.
+# Example: keep LE on 1M only.
+#DefaultPHYs = LE1MTX LE1MRX
+
# Enable/Disable Advertisement Monitor interleave scan for power saving.
# 0: disable
# 1: enable
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* RE: Add configurable default LE PHY policy
2026-05-25 14:50 ` [PATCH BlueZ v4 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
@ 2026-05-25 16:55 ` bluez.test.bot
0 siblings, 0 replies; 14+ messages in thread
From: bluez.test.bot @ 2026-05-25 16:55 UTC (permalink / raw)
To: linux-bluetooth, tarjeib
[-- Attachment #1: Type: text/plain, Size: 825 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1100512
---Test result---
Test Summary:
CheckPatch PASS 0.58 seconds
GitLint PASS 0.33 seconds
BuildEll PASS 20.48 seconds
BluezMake PASS 632.11 seconds
CheckSmatch PASS 328.33 seconds
bluezmakeextell PASS 167.60 seconds
IncrementalBuild PASS 620.54 seconds
ScanBuild PASS 923.33 seconds
https://github.com/bluez/bluez/pull/2153
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs
2026-05-24 22:14 ` [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
2026-05-24 23:07 ` Add configurable default LE PHY policy bluez.test.bot
@ 2026-05-26 14:27 ` Luiz Augusto von Dentz
2026-05-26 18:58 ` Tarjei Bitustøyl
1 sibling, 1 reply; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2026-05-26 14:27 UTC (permalink / raw)
To: Tarjei Bitustøyl; +Cc: linux-bluetooth
Hi Tarjei,
On Sun, May 24, 2026 at 6:14 PM Tarjei Bitustøyl <tarjeib@gmail.com> wrote:
>
> Some controllers mis-handle LE procedures on specific PHYs with
> certain peers. On an Intel AX210-class controller, connecting to a
> Frostbay BLE device can fail during early ATT/GATT setup unless the
> adapter is limited to LE 1M TX/RX.
Perhaps there should be a GitHub issue explaining exactly what the
problem is with btmon logs, etc, Then we can evaluate if this needs a
workaround like this or if we should detect that certain PHYs should
not be used.
> Add an opt-in [LE] DefaultPHYs setting to bluetoothd and apply it at
> adapter startup using MGMT_OP_GET/SET_PHY_CONFIGURATION while
> preserving non-configurable PHY bits.
>
> This provides a generic, adapter-wide workaround for controller-
> specific LE PHY interoperability problems affecting scanning and
> connection establishment, without adding device-specific quirks.
Well it is not really controller specific though, since it applies to
any controller on the system. Also, I believe this could be a device
specific problem so you might be taking away 2M support entirely when
it could actually be supported with another device.
> ---
> src/adapter.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++
> src/btd.h | 2 ++
> src/main.c | 61 +++++++++++++++++++++++++++++++++++++++++
> src/main.conf | 8 ++++++
> 4 files changed, 147 insertions(+)
>
> diff --git a/src/adapter.c b/src/adapter.c
> index 20f7c3e03..fcbb65e38 100644
> --- a/src/adapter.c
> +++ b/src/adapter.c
> @@ -4972,6 +4972,81 @@ done:
> mgmt_tlv_list_free(list);
> }
>
> +static void set_default_le_phys_complete(uint8_t status, uint16_t length,
> + const void *param, void *user_data)
> +{
> + struct btd_adapter *adapter = user_data;
> +
> + if (status != MGMT_STATUS_SUCCESS)
> + btd_error(adapter->dev_id,
> + "Failed to set default LE PHYs for hci%u: %s (0x%02x)",
> + adapter->dev_id, mgmt_errstr(status), status);
> +}
> +
> +static void get_default_le_phys_complete(uint8_t status, uint16_t length,
> + const void *param, void *user_data)
> +{
> + struct btd_adapter *adapter = user_data;
> + const struct mgmt_rp_get_phy_confguration *rp = param;
> + struct mgmt_cp_set_phy_confguration cp;
> + uint32_t configurable_phys;
> + uint32_t selected_phys;
> + uint32_t next_phys;
> +
> + if (status != MGMT_STATUS_SUCCESS) {
> + btd_error(adapter->dev_id,
> + "Failed to read PHY configuration for hci%u: %s (0x%02x)",
> + adapter->dev_id, mgmt_errstr(status), status);
> + return;
> + }
> +
> + if (length < sizeof(*rp)) {
> + btd_error(adapter->dev_id,
> + "Too small get PHY configuration response for hci%u",
> + adapter->dev_id);
> + return;
> + }
> +
> + configurable_phys = btohl(rp->configurable_phys);
> + selected_phys = btohl(rp->selected_phys);
> +
> + configurable_phys &= MGMT_PHY_LE_TX_MASK | MGMT_PHY_LE_RX_MASK;
> + next_phys = selected_phys & ~configurable_phys;
> + next_phys |= btd_opts.default_le_phys & configurable_phys;
> +
> + if (next_phys == selected_phys)
> + return;
> +
> + cp.selected_phys = cpu_to_le32(next_phys);
> +
> + if (mgmt_send(adapter->mgmt, MGMT_OP_SET_PHY_CONFIGURATION,
> + adapter->dev_id, sizeof(cp), &cp,
> + set_default_le_phys_complete, adapter, NULL) > 0)
> + return;
> +
> + btd_error(adapter->dev_id,
> + "Failed to set default LE PHYs for hci%u",
> + adapter->dev_id);
> +}
> +
> +static void load_default_le_phys(struct btd_adapter *adapter)
> +{
> + if (!btd_opts.default_le_phys_configured)
> + return;
> +
> + if (!(adapter->supported_settings & MGMT_SETTING_LE))
> + return;
> +
> + if (mgmt_send(adapter->mgmt, MGMT_OP_GET_PHY_CONFIGURATION,
> + adapter->dev_id, 0, NULL,
> + get_default_le_phys_complete, adapter, NULL) > 0)
> + return;
> +
> + btd_error(adapter->dev_id,
> + "Failed to read PHY configuration for hci%u",
> + adapter->dev_id);
> +}
> +
> static void load_devices(struct btd_adapter *adapter)
> {
> char dirname[PATH_MAX];
> @@ -9455,6 +9530,7 @@ load:
> btd_profile_foreach(probe_profile, adapter);
> clear_blocked(adapter);
> load_defaults(adapter);
> + load_default_le_phys(adapter);
> load_devices(adapter);
>
> /* restore Service Changed CCC value for bonded devices */
> diff --git a/src/btd.h b/src/btd.h
> index db2e81239..59f44dc8c 100644
> --- a/src/btd.h
> +++ b/src/btd.h
> @@ -140,6 +140,8 @@ struct btd_opts {
> bool device_privacy;
> uint32_t name_request_retry_delay;
> uint8_t secure_conn;
> + uint32_t default_le_phys;
> + bool default_le_phys_configured;
>
> struct btd_defaults defaults;
>
> diff --git a/src/main.c b/src/main.c
> index 8aa19a3e3..97c64845b 100644
> --- a/src/main.c
> +++ b/src/main.c
> @@ -32,6 +32,7 @@
> #include <dbus/dbus.h>
>
> #include "bluetooth/bluetooth.h"
> +#include "bluetooth/mgmt.h"
> #include "bluetooth/sdp.h"
>
> #include "gdbus/gdbus.h"
> @@ -132,6 +133,7 @@ static const char *le_options[] = {
> "Autoconnecttimeout",
> "AdvMonAllowlistScanDuration",
> "AdvMonNoFilterScanDuration",
> + "DefaultPHYs",
> "EnableAdvMonInterleaveScan",
> NULL
> };
> @@ -145,6 +147,8 @@ static const char *policy_options[] = {
> NULL
> };
>
> +static void parse_default_le_phys(GKeyFile *config);
> +
> static const char *gatt_options[] = {
> "Cache",
> "KeySize",
> @@ -751,6 +755,7 @@ static void parse_le_config(GKeyFile *config)
> return;
>
> parse_mode_config(config, "LE", params, ARRAY_SIZE(params));
> + parse_default_le_phys(config);
> }
>
> static bool match_experimental(const void *data, const void *match_data)
> @@ -966,6 +971,62 @@ static void parse_repairing(GKeyFile *config)
> g_free(str);
> }
>
> +struct phy_config_entry {
> + const char *name;
> + uint32_t bit;
> +};
> +
> +static const struct phy_config_entry le_phy_configs[] = {
> + { "LE1MTX", MGMT_PHY_LE_1M_TX },
> + { "LE1MRX", MGMT_PHY_LE_1M_RX },
> + { "LE2MTX", MGMT_PHY_LE_2M_TX },
> + { "LE2MRX", MGMT_PHY_LE_2M_RX },
> + { "LECODEDTX", MGMT_PHY_LE_CODED_TX },
> + { "LECODEDRX", MGMT_PHY_LE_CODED_RX },
> +};
> +
> +static void parse_default_le_phys(GKeyFile *config)
> +{
> + char *str = NULL;
> + char **tokens;
> + uint32_t phys = 0;
> + bool valid = false;
> + int i;
> +
> + if (!parse_config_string(config, "LE", "DefaultPHYs", &str))
> + return;
> +
> + tokens = g_strsplit_set(str, ", \t", -1);
> +
> + for (i = 0; tokens[i]; i++) {
> + const char *token = tokens[i];
> + size_t j;
> +
> + if (!token[0])
> + continue;
> +
> + for (j = 0; j < ARRAY_SIZE(le_phy_configs); j++) {
> + if (strcasecmp(le_phy_configs[j].name, token) != 0)
> + continue;
> +
> + phys |= le_phy_configs[j].bit;
> + valid = true;
> + break;
> + }
> +
> + if (j == ARRAY_SIZE(le_phy_configs))
> + warn("Invalid DefaultPHYs token: %s", token);
> + }
> +
> + if (valid) {
> + btd_opts.default_le_phys = phys;
> + btd_opts.default_le_phys_configured = true;
> + }
> +
> + g_strfreev(tokens);
> + g_free(str);
> +}
> +
> static bool parse_config_hex(GKeyFile *config, char *group,
> const char *key, uint32_t *val)
> {
> diff --git a/src/main.conf b/src/main.conf
> index 5846ef92d..ed955897e 100644
> --- a/src/main.conf
> +++ b/src/main.conf
> @@ -247,6 +247,14 @@
> # Default: 500
> #AdvMonNoFilterScanDuration=
>
> +# Configure the controller's default LE PHY policy used for scanning and
> +# connection establishment. Only configurable LE PHYs are changed; mandatory
> +# PHYs remain selected automatically.
> +# Possible values: comma or space separated list of LE1MTX, LE1MRX, LE2MTX,
> +# LE2MRX, LECODEDTX, LECODEDRX.
> +# Example: keep LE on 1M only.
> +#DefaultPHYs = LE1MTX LE1MRX
> +
> # Enable/Disable Advertisement Monitor interleave scan for power saving.
> # 0: disable
> # 1: enable
> --
> 2.43.0
>
>
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs
2026-05-26 14:27 ` [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs Luiz Augusto von Dentz
@ 2026-05-26 18:58 ` Tarjei Bitustøyl
0 siblings, 0 replies; 14+ messages in thread
From: Tarjei Bitustøyl @ 2026-05-26 18:58 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
Yea you're right, I assumed too much here. I have only identified it
with my Frostbay device, not any other device. I've made a GitHub
issue with btmon logs and others that should show the issue.
I do still suspect that this is an issue for other BLE devices on the
Intel AX210 though.
Issue is 2155.
Regards,
Tarjei
tir. 26. mai 2026 kl. 16:27 skrev Luiz Augusto von Dentz <luiz.dentz@gmail.com>:
>
> Hi Tarjei,
>
> On Sun, May 24, 2026 at 6:14 PM Tarjei Bitustøyl <tarjeib@gmail.com> wrote:
> >
> > Some controllers mis-handle LE procedures on specific PHYs with
> > certain peers. On an Intel AX210-class controller, connecting to a
> > Frostbay BLE device can fail during early ATT/GATT setup unless the
> > adapter is limited to LE 1M TX/RX.
>
> Perhaps there should be a GitHub issue explaining exactly what the
> problem is with btmon logs, etc, Then we can evaluate if this needs a
> workaround like this or if we should detect that certain PHYs should
> not be used.
>
> > Add an opt-in [LE] DefaultPHYs setting to bluetoothd and apply it at
> > adapter startup using MGMT_OP_GET/SET_PHY_CONFIGURATION while
> > preserving non-configurable PHY bits.
> >
> > This provides a generic, adapter-wide workaround for controller-
> > specific LE PHY interoperability problems affecting scanning and
> > connection establishment, without adding device-specific quirks.
>
> Well it is not really controller specific though, since it applies to
> any controller on the system. Also, I believe this could be a device
> specific problem so you might be taking away 2M support entirely when
> it could actually be supported with another device.
>
> > ---
> > src/adapter.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++
> > src/btd.h | 2 ++
> > src/main.c | 61 +++++++++++++++++++++++++++++++++++++++++
> > src/main.conf | 8 ++++++
> > 4 files changed, 147 insertions(+)
> >
> > diff --git a/src/adapter.c b/src/adapter.c
> > index 20f7c3e03..fcbb65e38 100644
> > --- a/src/adapter.c
> > +++ b/src/adapter.c
> > @@ -4972,6 +4972,81 @@ done:
> > mgmt_tlv_list_free(list);
> > }
> >
> > +static void set_default_le_phys_complete(uint8_t status, uint16_t length,
> > + const void *param, void *user_data)
> > +{
> > + struct btd_adapter *adapter = user_data;
> > +
> > + if (status != MGMT_STATUS_SUCCESS)
> > + btd_error(adapter->dev_id,
> > + "Failed to set default LE PHYs for hci%u: %s (0x%02x)",
> > + adapter->dev_id, mgmt_errstr(status), status);
> > +}
> > +
> > +static void get_default_le_phys_complete(uint8_t status, uint16_t length,
> > + const void *param, void *user_data)
> > +{
> > + struct btd_adapter *adapter = user_data;
> > + const struct mgmt_rp_get_phy_confguration *rp = param;
> > + struct mgmt_cp_set_phy_confguration cp;
> > + uint32_t configurable_phys;
> > + uint32_t selected_phys;
> > + uint32_t next_phys;
> > +
> > + if (status != MGMT_STATUS_SUCCESS) {
> > + btd_error(adapter->dev_id,
> > + "Failed to read PHY configuration for hci%u: %s (0x%02x)",
> > + adapter->dev_id, mgmt_errstr(status), status);
> > + return;
> > + }
> > +
> > + if (length < sizeof(*rp)) {
> > + btd_error(adapter->dev_id,
> > + "Too small get PHY configuration response for hci%u",
> > + adapter->dev_id);
> > + return;
> > + }
> > +
> > + configurable_phys = btohl(rp->configurable_phys);
> > + selected_phys = btohl(rp->selected_phys);
> > +
> > + configurable_phys &= MGMT_PHY_LE_TX_MASK | MGMT_PHY_LE_RX_MASK;
> > + next_phys = selected_phys & ~configurable_phys;
> > + next_phys |= btd_opts.default_le_phys & configurable_phys;
> > +
> > + if (next_phys == selected_phys)
> > + return;
> > +
> > + cp.selected_phys = cpu_to_le32(next_phys);
> > +
> > + if (mgmt_send(adapter->mgmt, MGMT_OP_SET_PHY_CONFIGURATION,
> > + adapter->dev_id, sizeof(cp), &cp,
> > + set_default_le_phys_complete, adapter, NULL) > 0)
> > + return;
> > +
> > + btd_error(adapter->dev_id,
> > + "Failed to set default LE PHYs for hci%u",
> > + adapter->dev_id);
> > +}
> > +
> > +static void load_default_le_phys(struct btd_adapter *adapter)
> > +{
> > + if (!btd_opts.default_le_phys_configured)
> > + return;
> > +
> > + if (!(adapter->supported_settings & MGMT_SETTING_LE))
> > + return;
> > +
> > + if (mgmt_send(adapter->mgmt, MGMT_OP_GET_PHY_CONFIGURATION,
> > + adapter->dev_id, 0, NULL,
> > + get_default_le_phys_complete, adapter, NULL) > 0)
> > + return;
> > +
> > + btd_error(adapter->dev_id,
> > + "Failed to read PHY configuration for hci%u",
> > + adapter->dev_id);
> > +}
> > +
> > static void load_devices(struct btd_adapter *adapter)
> > {
> > char dirname[PATH_MAX];
> > @@ -9455,6 +9530,7 @@ load:
> > btd_profile_foreach(probe_profile, adapter);
> > clear_blocked(adapter);
> > load_defaults(adapter);
> > + load_default_le_phys(adapter);
> > load_devices(adapter);
> >
> > /* restore Service Changed CCC value for bonded devices */
> > diff --git a/src/btd.h b/src/btd.h
> > index db2e81239..59f44dc8c 100644
> > --- a/src/btd.h
> > +++ b/src/btd.h
> > @@ -140,6 +140,8 @@ struct btd_opts {
> > bool device_privacy;
> > uint32_t name_request_retry_delay;
> > uint8_t secure_conn;
> > + uint32_t default_le_phys;
> > + bool default_le_phys_configured;
> >
> > struct btd_defaults defaults;
> >
> > diff --git a/src/main.c b/src/main.c
> > index 8aa19a3e3..97c64845b 100644
> > --- a/src/main.c
> > +++ b/src/main.c
> > @@ -32,6 +32,7 @@
> > #include <dbus/dbus.h>
> >
> > #include "bluetooth/bluetooth.h"
> > +#include "bluetooth/mgmt.h"
> > #include "bluetooth/sdp.h"
> >
> > #include "gdbus/gdbus.h"
> > @@ -132,6 +133,7 @@ static const char *le_options[] = {
> > "Autoconnecttimeout",
> > "AdvMonAllowlistScanDuration",
> > "AdvMonNoFilterScanDuration",
> > + "DefaultPHYs",
> > "EnableAdvMonInterleaveScan",
> > NULL
> > };
> > @@ -145,6 +147,8 @@ static const char *policy_options[] = {
> > NULL
> > };
> >
> > +static void parse_default_le_phys(GKeyFile *config);
> > +
> > static const char *gatt_options[] = {
> > "Cache",
> > "KeySize",
> > @@ -751,6 +755,7 @@ static void parse_le_config(GKeyFile *config)
> > return;
> >
> > parse_mode_config(config, "LE", params, ARRAY_SIZE(params));
> > + parse_default_le_phys(config);
> > }
> >
> > static bool match_experimental(const void *data, const void *match_data)
> > @@ -966,6 +971,62 @@ static void parse_repairing(GKeyFile *config)
> > g_free(str);
> > }
> >
> > +struct phy_config_entry {
> > + const char *name;
> > + uint32_t bit;
> > +};
> > +
> > +static const struct phy_config_entry le_phy_configs[] = {
> > + { "LE1MTX", MGMT_PHY_LE_1M_TX },
> > + { "LE1MRX", MGMT_PHY_LE_1M_RX },
> > + { "LE2MTX", MGMT_PHY_LE_2M_TX },
> > + { "LE2MRX", MGMT_PHY_LE_2M_RX },
> > + { "LECODEDTX", MGMT_PHY_LE_CODED_TX },
> > + { "LECODEDRX", MGMT_PHY_LE_CODED_RX },
> > +};
> > +
> > +static void parse_default_le_phys(GKeyFile *config)
> > +{
> > + char *str = NULL;
> > + char **tokens;
> > + uint32_t phys = 0;
> > + bool valid = false;
> > + int i;
> > +
> > + if (!parse_config_string(config, "LE", "DefaultPHYs", &str))
> > + return;
> > +
> > + tokens = g_strsplit_set(str, ", \t", -1);
> > +
> > + for (i = 0; tokens[i]; i++) {
> > + const char *token = tokens[i];
> > + size_t j;
> > +
> > + if (!token[0])
> > + continue;
> > +
> > + for (j = 0; j < ARRAY_SIZE(le_phy_configs); j++) {
> > + if (strcasecmp(le_phy_configs[j].name, token) != 0)
> > + continue;
> > +
> > + phys |= le_phy_configs[j].bit;
> > + valid = true;
> > + break;
> > + }
> > +
> > + if (j == ARRAY_SIZE(le_phy_configs))
> > + warn("Invalid DefaultPHYs token: %s", token);
> > + }
> > +
> > + if (valid) {
> > + btd_opts.default_le_phys = phys;
> > + btd_opts.default_le_phys_configured = true;
> > + }
> > +
> > + g_strfreev(tokens);
> > + g_free(str);
> > +}
> > +
> > static bool parse_config_hex(GKeyFile *config, char *group,
> > const char *key, uint32_t *val)
> > {
> > diff --git a/src/main.conf b/src/main.conf
> > index 5846ef92d..ed955897e 100644
> > --- a/src/main.conf
> > +++ b/src/main.conf
> > @@ -247,6 +247,14 @@
> > # Default: 500
> > #AdvMonNoFilterScanDuration=
> >
> > +# Configure the controller's default LE PHY policy used for scanning and
> > +# connection establishment. Only configurable LE PHYs are changed; mandatory
> > +# PHYs remain selected automatically.
> > +# Possible values: comma or space separated list of LE1MTX, LE1MRX, LE2MTX,
> > +# LE2MRX, LECODEDTX, LECODEDRX.
> > +# Example: keep LE on 1M only.
> > +#DefaultPHYs = LE1MTX LE1MRX
> > +
> > # Enable/Disable Advertisement Monitor interleave scan for power saving.
> > # 0: disable
> > # 1: enable
> > --
> > 2.43.0
> >
> >
>
>
> --
> Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-05-26 18:58 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-24 22:14 [PATCH BlueZ 0/1] Add configurable default LE PHY policy Tarjei Bitustøyl
2026-05-24 22:14 ` [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
2026-05-24 23:07 ` Add configurable default LE PHY policy bluez.test.bot
2026-05-26 14:27 ` [PATCH BlueZ 1/1] adapter: Add configurable default LE PHYs Luiz Augusto von Dentz
2026-05-26 18:58 ` Tarjei Bitustøyl
2026-05-25 6:50 ` [PATCH BlueZ v2 0/1] Add configurable default LE PHY policy Tarjei Bitustøyl
2026-05-25 6:50 ` [PATCH BlueZ v2 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
2026-05-25 11:04 ` Add configurable default LE PHY policy bluez.test.bot
2026-05-25 11:37 ` [PATCH BlueZ v3 0/1] " Tarjei Bitustøyl
2026-05-25 11:37 ` [PATCH BlueZ v3 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
2026-05-25 14:33 ` Add configurable default LE PHY policy bluez.test.bot
2026-05-25 14:50 ` [PATCH BlueZ v4 0/1] " Tarjei Bitustøyl
2026-05-25 14:50 ` [PATCH BlueZ v4 1/1] adapter: Add configurable default LE PHYs Tarjei Bitustøyl
2026-05-25 16:55 ` Add configurable default LE PHY policy bluez.test.bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox