* [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
* 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
* [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
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