Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH v1] Bluetooth: btintel_pcie: Reset controller before configuring MSI-X
From: Kiran K @ 2026-05-11 13:27 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: ravishankar.srivatsa, chethan.tumkur.narayan, aluvala.sai.teja,
	Kiran K

From: Sai Teja Aluvala <aluvala.sai.teja@intel.com>

Perform the shared hardware reset in btintel_pcie_probe() before
configuring MSI-X so the controller starts from a known clean state.

While here, move btintel_pcie_config_msix() out of
btintel_pcie_config_pcie() and into the probe sequence, and propagate
errors from btintel_pcie_reset_bt() so probe fails cleanly if the
shared HW reset does not complete.

Signed-off-by: Sai Teja Aluvala <aluvala.sai.teja@intel.com>
Signed-off-by: Kiran K <kiran.k@intel.com>
---
 drivers/bluetooth/btintel_pcie.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index fda474406003..6ff08de9ec78 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -1646,9 +1646,6 @@ static int btintel_pcie_config_pcie(struct pci_dev *pdev,
 	if (err)
 		return err;
 
-	/* Configure MSI-X with causes list */
-	btintel_pcie_config_msix(data);
-
 	return 0;
 }
 
@@ -2659,6 +2656,14 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
 	if (err)
 		goto exit_error;
 
+	err = btintel_pcie_reset_bt(data);
+	if (err) {
+		dev_err(&pdev->dev, "Bluetooth shared HW reset failed (%d)\n", err);
+		goto exit_error;
+	}
+
+	/* Configure MSI-X with causes list */
+	btintel_pcie_config_msix(data);
 	pci_set_drvdata(pdev, data);
 
 	err = btintel_pcie_alloc(data);
-- 
2.53.0


^ permalink raw reply related

* Re: net: convert remaining bluetooth socket families to getsockopt_iter
From: Breno Leitao @ 2026-05-11 12:29 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <6a01b70b.050a0220.366c6d.5746@mx.google.com>

On Mon, May 11, 2026 at 04:01:31AM -0700, bluez.test.bot@gmail.com wrote:
> This is an automated email and please do not reply to this email.
> 
> Dear Submitter,
> 
> Thank you for submitting the patches to the linux bluetooth mailing list.
> While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository.

What is the HEAD of the repository? This should apply against
linux-next-alike branch.

^ permalink raw reply

* [PATCH] Bluetooth: L2CAP: ecred_reconfigure: send packed pdu, not stack pointer
From: Michael Bommarito @ 2026-05-11 12:26 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, Johan Hedberg,
	linux-bluetooth
  Cc: Gustavo A. R. Silva, stable, linux-kernel

Commit 1c08108f3014 ("Bluetooth: L2CAP: Avoid -Wflex-array-member-not-at-end
warnings") converted the on-stack request PDU in l2cap_ecred_reconfigure()
from an explicit packed struct to DEFINE_RAW_FLEX(), but did not adjust the
size and source-pointer arguments to l2cap_send_cmd():

  -    struct {
  -            struct l2cap_ecred_reconf_req req;
  -            __le16 scid;
  -    } pdu;
  +    DEFINE_RAW_FLEX(struct l2cap_ecred_reconf_req, pdu, scid, 1);
       ...
       l2cap_send_cmd(conn, chan->ident, L2CAP_ECRED_RECONF_REQ,
                      sizeof(pdu), &pdu);

After the conversion, DEFINE_RAW_FLEX() expands to declare an anonymous
union pdu_u plus a local pointer "pdu" pointing at it. Therefore:

  - sizeof(pdu) is now sizeof(struct l2cap_ecred_reconf_req *) = 8 on
    64-bit (4 on 32-bit), not the 6 bytes of (mtu, mps, scid[1]).
  - &pdu is the address of the local pointer's stack storage, not the
    address of the request payload.

l2cap_send_cmd() forwards (data, count) to l2cap_build_cmd(), which calls
skb_put_data(skb, data, count). The L2CAP_ECRED_RECONFIGURE_REQ packet
body therefore contains 8 bytes copied from the kernel stack starting at
&pdu -- the 8 bytes overlap the pdu pointer's value, leaking a kernel
stack address to the paired Bluetooth peer. The intended (mtu, mps, scid)
fields are not transmitted at all, so the peer rejects the request as
malformed and the L2CAP_ECRED_RECONFIGURE feature itself has been broken
for the local-side initiator since the introducing commit landed.

The sibling site l2cap_ecred_conn_req() in the same commit was converted
correctly (sizeof(*pdu) + len, pdu); only this site was missed.

Restore the original semantics: pass the full flex-struct size via
struct_size(pdu, scid, 1) and the pdu pointer (the struct address) as
the source.

Validated on a stock 7.0-based host kernel via the real call path:
setsockopt(SOL_BLUETOOTH, BT_RCVMTU, ...) on a BT_CONNECTED
L2CAP_MODE_EXT_FLOWCTL socket emits an L2CAP_ECRED_RECONFIGURE_REQ
whose body is 8 bytes (the on-stack pdu local's value) rather than
the expected 6. Three captures from fresh socket / fresh hciemu peer
on the same host -- low bytes vary per call, high 0xffff confirms a
kernel virtual address (KASLR-randomised stack slot, not a fixed
string):

  RECONF_REQ body (ident=0x02 len=8): 42 fb 54 af 0e ca ff ff
  RECONF_REQ body (ident=0x02 len=8): 52 3d 2e af 0e ca ff ff
  RECONF_REQ body (ident=0x02 len=8): b2 fc 5b af 0e ca ff ff

After this patch the body is 6 bytes carrying the expected
little-endian (mtu, mps, scid).

Cc: stable@vger.kernel.org
Fixes: 1c08108f3014 ("Bluetooth: L2CAP: Avoid -Wflex-array-member-not-at-end warnings")
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 net/bluetooth/l2cap_core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 77dec104a9c3..4773a453b145 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -7282,7 +7282,7 @@ static void l2cap_ecred_reconfigure(struct l2cap_chan *chan)
 	chan->ident = l2cap_get_ident(conn);
 
 	l2cap_send_cmd(conn, chan->ident, L2CAP_ECRED_RECONF_REQ,
-		       sizeof(pdu), &pdu);
+		       struct_size(pdu, scid, 1), pdu);
 }
 
 int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu)
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH v2 4/9] power: sequencing: pcie-m2: Create serdev for PCI devices present before probe
From: Bartosz Golaszewski @ 2026-05-11 11:45 UTC (permalink / raw)
  To: manivannan.sadhasivam
  Cc: Manivannan Sadhasivam via B4 Relay, linux-pm, linux-kernel,
	linux-pci, linux-arm-msm, linux-bluetooth, Wei Deng,
	Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Marcel Holtmann, Luiz Augusto von Dentz,
	Shuai Zhang
In-Reply-To: <20260507-pwrseq-m2-bt-v2-4-1740bd478539@oss.qualcomm.com>

On Thu, 7 May 2026 18:06:12 +0200, Manivannan Sadhasivam via B4 Relay
<devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org> said:
> From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
>
> So far, the driver is registering a notifier to create serdev for the PCI
> devices that are going to be attached after probe. But it doesn't handle
> the devices present before probe. Due to this, serdev is not getting
> created for those existing devices.
>
> Hence, create serdev for PCI devices available before probe as well.
>
> Note that the serdev for available devices are created before
> registering the notifier. There is a small window where a device could
> appear after pwrseq_pcie_m2_create_serdev(), before notifier registration.
> But since M.2 cards are fixed to a slot, they are mostly added either
> before booting the host or after using hotplug. So this window is mostly
> theoretical.
>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---
>  drivers/power/sequencing/pwrseq-pcie-m2.c | 83 ++++++++++++++++++++++++++-----
>  1 file changed, 70 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c
> index 038271207a27..0a37a375a89d 100644
> --- a/drivers/power/sequencing/pwrseq-pcie-m2.c
> +++ b/drivers/power/sequencing/pwrseq-pcie-m2.c
> @@ -236,7 +236,7 @@ static int pwrseq_pcie_m2_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx,
>  	return ret;
>  }
>
> -static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx,
> +static int __pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx,
>  					struct pci_dev *pdev)

It may just be a personal preference but I dislike the __ prefix that doesn't
really indicate how the function is different from the one without it. I prefer
the name to reflect that - if it's because the function expects a mutex to be
already taken then it should be called something_something_unlocked() etc.

Maybe call it pwrseq_pcie_m2_create_serdev_for_pci()? Or at least:
pwrseq_pcie_m2_do_create_serdev()?

>  {
>  	struct serdev_controller *serdev_ctrl;
> @@ -259,6 +259,16 @@ static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx,
>  		return 0;
>  	}
>
> +	/* Bail out if the serdev device was already created for the PCI dev */
> +	mutex_lock(&ctx->list_lock);
> +	list_for_each_entry(pci_dev, &ctx->pci_devices, list) {
> +		if (pci_dev->pdev == pdev) {
> +			mutex_unlock(&ctx->list_lock);
> +			return 0;
> +		}
> +	}
> +	mutex_unlock(&ctx->list_lock);

Is there any reason to not use guard() here?

Bart

^ permalink raw reply

* Re: [PATCH v2 9/9] Bluetooth: hci_qca: Set 'bt_en_available' based on W_DISABLE2# presence in M.2 connector
From: Bartosz Golaszewski @ 2026-05-11 11:36 UTC (permalink / raw)
  To: manivannan.sadhasivam
  Cc: Manivannan Sadhasivam via B4 Relay, linux-pm, linux-kernel,
	linux-pci, linux-arm-msm, linux-bluetooth, Wei Deng,
	Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Marcel Holtmann, Luiz Augusto von Dentz,
	Shuai Zhang
In-Reply-To: <20260507-pwrseq-m2-bt-v2-9-1740bd478539@oss.qualcomm.com>

On Thu, 7 May 2026 18:06:17 +0200, Manivannan Sadhasivam via B4 Relay
<devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org> said:
> From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
>
> Check if the M.2 connector supports the W_DISABLE2# property or not by
> querying the pwrseq provider's DT node. If not available, then set
> 'bt_en_available' flag to 'false'. This flag is used to set the
> HCI_QUIRK_NON_PERSISTENT_SETUP HCI quirk, which informs the HCI layer
> whether the shutdown() callback for the device can be triggered or not.
>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---
>  drivers/bluetooth/hci_qca.c | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
> index 3e71a72ea7c7..b5439b9956cf 100644
> --- a/drivers/bluetooth/hci_qca.c
> +++ b/drivers/bluetooth/hci_qca.c
> @@ -2449,10 +2449,17 @@ static int qca_serdev_probe(struct serdev_device *serdev)
>  		 * the M.2 Key E connector.
>  		 */
>  		if (of_graph_is_present(dev_of_node(&serdev->ctrl->dev))) {
> +			struct device *dev;
> +
>  			qcadev->bt_power->pwrseq = devm_pwrseq_get(&serdev->ctrl->dev,
>  								   "uart");
>  			if (IS_ERR(qcadev->bt_power->pwrseq))
>  				return PTR_ERR(qcadev->bt_power->pwrseq);
> +
> +			dev = pwrseq_to_device(qcadev->bt_power->pwrseq);
> +			if (!device_property_present(dev, "w-disable2-gpios"))
> +				bt_en_available = false;
> +
>  			break;
>  		}
>
>

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

Just one nit: I'd switch the order of patches 7 and 8 in this series so that
I can queue the pwrseq patches in an immutable branch and provide it to the
bluetooth tree for v7.2

Bart

^ permalink raw reply

* [BlueZ 3/3] mesh: Fix const qualifier dropping when using strchr()
From: Bastien Nocera @ 2026-05-11 11:35 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <20260511113511.1217887-1-hadess@hadess.net>

strchr() with a const string returns a const string, we don't change
that string or "next", so make both const and get rid of the warning.

mesh/util.c: In function ‘create_dir’:
mesh/util.c:108:14: error: assignment discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
  108 |         prev = strchr(dir_name, '/');
      |              ^
---
 mesh/util.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/mesh/util.c b/mesh/util.c
index 348401ae5582..a21882a2d9f0 100644
--- a/mesh/util.c
+++ b/mesh/util.c
@@ -95,7 +95,8 @@ size_t hex2str(const uint8_t *in, size_t in_len, char *out, size_t out_len)
 int create_dir(const char *dir_name)
 {
 	struct stat st;
-	char dir[PATH_MAX + 1], *prev, *next;
+	const char *prev, *next;
+	char dir[PATH_MAX + 1];
 	int err;
 
 	err = stat(dir_name, &st);
-- 
2.54.0


^ permalink raw reply related

* [BlueZ 2/3] mesh: Fix str{r,}chr usage
From: Bastien Nocera @ 2026-05-11 11:35 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <20260511113511.1217887-1-hadess@hadess.net>

Fix the code manipulating "const char *" return values from
json_object_to_json_string_ext() to modify it for printing, we're
not allowed to do that.

tools/mesh/mesh-db.c: In function ‘mesh_db_finish_export’:
tools/mesh/mesh-db.c:2598:13: error: assignment discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
 2598 |         pos = strrchr(hdr, '}');
      |             ^
tools/mesh/mesh-db.c:2604:13: error: assignment discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
 2604 |         pos = strrchr(hdr, '"');
      |             ^
tools/mesh/mesh-db.c:2613:13: error: assignment discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
 2613 |         pos = strchr(str, '{');
      |             ^
---
 tools/mesh/mesh-db.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index fb9c436d1d06..7bb8ab53ecbb 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -2547,7 +2547,9 @@ void *mesh_db_prepare_export(void)
 bool mesh_db_finish_export(bool is_error, void *expt_cfg, const char *fname)
 {
 	FILE *outfile = NULL;
-	const char *str, *hdr;
+	const char *str_s, *hdr_s;
+	char *str = NULL;
+	char *hdr = NULL;
 	json_object *jhdr = NULL;
 	bool result = false;
 	char *pos;
@@ -2581,15 +2583,18 @@ bool mesh_db_finish_export(bool is_error, void *expt_cfg, const char *fname)
 	if (!add_string(jhdr, "version", schema_version))
 		goto done;
 
-	hdr = json_object_to_json_string_ext(jhdr, JSON_C_TO_STRING_PRETTY |
+	hdr_s = json_object_to_json_string_ext(jhdr, JSON_C_TO_STRING_PRETTY |
 						JSON_C_TO_STRING_NOSLASHESCAPE);
 
-	str = json_object_to_json_string_ext(expt_cfg, JSON_C_TO_STRING_PRETTY |
+	str_s = json_object_to_json_string_ext(expt_cfg, JSON_C_TO_STRING_PRETTY |
 						JSON_C_TO_STRING_NOSLASHESCAPE);
 
-	if (!hdr || !str)
+	if (!hdr_s || !str_s)
 		goto done;
 
+	hdr = strdup(hdr_s);
+	str = strdup(str_s);
+
 	/*
 	 * Write two strings to the output while stripping closing "}" from the
 	 * header string and opening "{" from the config object.
@@ -2624,6 +2629,10 @@ bool mesh_db_finish_export(bool is_error, void *expt_cfg, const char *fname)
 	result = true;
 
 done:
+	if (hdr != NULL)
+		free(hdr);
+	if (str != NULL)
+		free(str);
 	if (outfile)
 		fclose(outfile);
 
-- 
2.54.0


^ permalink raw reply related

* [BlueZ 1/3] mesh: Remove unused but set variable
From: Bastien Nocera @ 2026-05-11 11:35 UTC (permalink / raw)
  To: linux-bluetooth

We played around with the bits, but didn't do anything with it.

mesh/net.c: In function ‘ack_received’:
mesh/net.c:1569:18: error: variable ‘ack_copy’ set but not used [-Werror=unused-but-set-variable=]
 1569 |         uint32_t ack_copy = ack_flag;
      |                  ^~~~~~~~
---
 mesh/net.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/mesh/net.c b/mesh/net.c
index b29e24f5d4a9..2c1be1f2cc63 100644
--- a/mesh/net.c
+++ b/mesh/net.c
@@ -1566,7 +1566,6 @@ static void ack_received(struct mesh_net *net, bool timeout,
 {
 	struct mesh_sar *outgoing;
 	uint32_t seg_flag = 0x00000001;
-	uint32_t ack_copy = ack_flag;
 	uint16_t i;
 
 	l_debug("ACK Rxed (%x) (to:%d): %8.8x", seq0, timeout, ack_flag);
@@ -1599,8 +1598,6 @@ static void ack_received(struct mesh_net *net, bool timeout,
 
 	outgoing->last_nak |= ack_flag;
 
-	ack_copy &= outgoing->flags;
-
 	for (i = 0; i <= SEG_MAX(true, outgoing->len); i++, seg_flag <<= 1) {
 		if (seg_flag & ack_flag) {
 			l_debug("Skipping Seg %d of %d",
@@ -1608,8 +1605,6 @@ static void ack_received(struct mesh_net *net, bool timeout,
 			continue;
 		}
 
-		ack_copy |= seg_flag;
-
 		l_debug("Resend Seg %d net:%p dst:%x app_idx:%3.3x",
 				i, net, outgoing->remote, outgoing->app_idx);
 
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v2 7/9] Bluetooth: hci_qca: Rename 'power_ctrl_enabled' to 'bt_en_available'
From: Bartosz Golaszewski @ 2026-05-11 11:34 UTC (permalink / raw)
  To: manivannan.sadhasivam
  Cc: Manivannan Sadhasivam via B4 Relay, linux-pm, linux-kernel,
	linux-pci, linux-arm-msm, linux-bluetooth, Wei Deng,
	Luiz Augusto von Dentz, Dmitry Baryshkov, Bartosz Golaszewski,
	Manivannan Sadhasivam, Marcel Holtmann, Luiz Augusto von Dentz,
	Shuai Zhang
In-Reply-To: <20260507-pwrseq-m2-bt-v2-7-1740bd478539@oss.qualcomm.com>

On Thu, 7 May 2026 18:06:15 +0200, Manivannan Sadhasivam via B4 Relay
<devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org> said:
> From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
>
> 'power_ctrl_enabled' flag is used to indicate the availability of the BT_EN
> GPIO in devicetree. But the naming causes confusion with the new pwrctrl
> framework.
>
> So rename it to 'bt_en_available' to make it clear and explicit.
>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

^ permalink raw reply

* Re: [PATCH v2 8/9] power: sequencing: Add an API to return the pwrseq device's 'dev' pointer
From: Bartosz Golaszewski @ 2026-05-11 11:34 UTC (permalink / raw)
  To: manivannan.sadhasivam
  Cc: Manivannan Sadhasivam via B4 Relay, linux-pm, linux-kernel,
	linux-pci, linux-arm-msm, linux-bluetooth, Wei Deng,
	Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Marcel Holtmann, Luiz Augusto von Dentz,
	Shuai Zhang
In-Reply-To: <20260507-pwrseq-m2-bt-v2-8-1740bd478539@oss.qualcomm.com>

On Thu, 7 May 2026 18:06:16 +0200, Manivannan Sadhasivam via B4 Relay
<devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org> said:
> From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
>
> The consumer drivers can make use of the pwrseq device's 'dev' pointer to
> query the pwrseq provider's DT node to check for existence of specific
> properties.
>
> Hence, add an API to return the pwrseq device's 'dev' pointer to consumers.
>
> Note that since pwrseq_get() would've increased the pwrseq refcount, there
> is no need to increase the refcount in this API again.
>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---

Thanks, I like this approach much more.

Bart

^ permalink raw reply

* Re: [PATCH v2 8/8] arm64: dts: qcom: arduino-imola: Describe NVMEM layout for WiFi/BT addresses
From: Bartosz Golaszewski @ 2026-05-11 11:30 UTC (permalink / raw)
  To: Loic Poulain
  Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
	linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
	Konrad Dybcio, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio, Jens Axboe,
	Johannes Berg, Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
	Russell King, Saravana Kannan
In-Reply-To: <20260507-block-as-nvmem-v2-8-bf17edd5134e@oss.qualcomm.com>

On Thu, 7 May 2026 17:24:43 +0200, Loic Poulain
<loic.poulain@oss.qualcomm.com> said:
> On Arduino Uno-Q, the eMMC boot1 partition is factory provisioned
> with device-specific information such as the WiFi MAC address
> and the Bluetooth BD address. This partition can serve as an
> alternative to additional non-volatile memory, such as a
> dedicated EEPROM.
>
> The eMMC boot partitions are typically good candidates, as they
> are relatively small, read-only by default (and can be enforced
> as hardware read-only), and are not affected by board reflashing
> procedures, which generally target the eMMC user or GP partitions.
>
> Describe the corresponding nvmem-layout for the WiFi and Bluetooth
> addresses, and point the WiFi and Bluetooth nodes to the appropriate
> NVMEM cells to retrieve them.
>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

^ permalink raw reply

* Re: [PATCH v2 7/8] Bluetooth: qca: Set NVMEM BD address quirks when address is invalid
From: Bartosz Golaszewski @ 2026-05-11 11:29 UTC (permalink / raw)
  To: Loic Poulain
  Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
	linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
	Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
	Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
	Russell King, Saravana Kannan
In-Reply-To: <20260507-block-as-nvmem-v2-7-bf17edd5134e@oss.qualcomm.com>

On Thu, 7 May 2026 17:24:42 +0200, Loic Poulain
<loic.poulain@oss.qualcomm.com> said:
> When the controller BD address is invalid (zero or default),
> set the NVMEM quirks to allow retrieving the address from a
> 'local-bd-address' NVMEM cell. The BD address is often stored
> alongside the WiFi MAC address in big-endian format, so also
> set the big-endian quirk.
>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
>  drivers/bluetooth/btqca.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
> index dda76365726f0bfe0e80e05fe04859fa4f0592e1..df33eacfd29fa680f393f90215150743e6001d5b 100644
> --- a/drivers/bluetooth/btqca.c
> +++ b/drivers/bluetooth/btqca.c
> @@ -721,8 +721,11 @@ static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *co
>  	}
>
>  	bda = (struct hci_rp_read_bd_addr *)skb->data;
> -	if (!bacmp(&bda->bdaddr, &config->bdaddr))
> +	if (!bacmp(&bda->bdaddr, &config->bdaddr)) {
>  		hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
> +		hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
> +		hci_set_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE);
> +	}
>
>  	kfree_skb(skb);
>
>
> --
> 2.34.1
>
>

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

^ permalink raw reply

* Re: [PATCH v2 4/8] block: implement NVMEM provider
From: Bartosz Golaszewski @ 2026-05-11 11:27 UTC (permalink / raw)
  To: Loic Poulain
  Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
	linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
	Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
	Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
	Russell King, Saravana Kannan
In-Reply-To: <20260507-block-as-nvmem-v2-4-bf17edd5134e@oss.qualcomm.com>

On Thu, 7 May 2026 17:24:39 +0200, Loic Poulain
<loic.poulain@oss.qualcomm.com> said:
> From: Daniel Golle <daniel@makrotopia.org>
>
> On embedded devices using an eMMC it is common that one or more partitions
> on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
> data. Allow referencing the partition in device tree for the kernel and
> Wi-Fi drivers accessing it via the NVMEM layer.
>
> To safely defer the freeing of the provider private data until all
> consumers have released their cells, a nvmem_dev() accessor is added to
> the NVMEM core to expose the struct device embedded in struct nvmem_device.
> This allows registering a devm action on the nvmem device itself, ensuring
> the private data outlives any active cell references.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> Co-developed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---

...

> diff --git a/block/blk-nvmem.c b/block/blk-nvmem.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..96c0ffc51b1862a75644f3f94add35d59577d86b
> --- /dev/null
> +++ b/block/blk-nvmem.c
> @@ -0,0 +1,188 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * block device NVMEM provider
> + *
> + * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
> + *
> + * Useful on devices using a partition on an eMMC for MAC addresses or
> + * Wi-Fi calibration EEPROM data.
> + */
> +
> +#include "blk.h"

Local includes typically go after system-wide ones. I thought that maybe it's
a block subsystem thing but no, it too goes after here.

> +#include <linux/nvmem-provider.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/pagemap.h>
> +#include <linux/property.h>
> +
> +static void blk_nvmem_free(void *data)
> +{
> +	kfree(data);
> +}
> +
> +/* List of all NVMEM devices */
> +static LIST_HEAD(nvmem_devices);
> +static DEFINE_MUTEX(devices_mutex);
> +
> +struct blk_nvmem {
> +	struct nvmem_device	*nvmem;
> +	dev_t			devt;
> +	bool			removed;
> +	struct list_head	list;
> +};
> +
> +static int blk_nvmem_reg_read(void *priv, unsigned int from,
> +			      void *val, size_t bytes)
> +{
> +	blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
> +	unsigned long offs = from & ~PAGE_MASK, to_read;
> +	pgoff_t f_index = from >> PAGE_SHIFT;
> +	struct blk_nvmem *bnv = priv;
> +	size_t bytes_left = bytes;
> +	struct file *bdev_file;
> +	struct folio *folio;
> +	void *p;
> +	int ret = 0;
> +
> +	if (bnv->removed)
> +		return -ENODEV;
> +
> +	bdev_file = bdev_file_open_by_dev(bnv->devt, mode, priv, NULL);
> +	if (!bdev_file)
> +		return -ENODEV;
> +
> +	if (IS_ERR(bdev_file))
> +		return PTR_ERR(bdev_file);
> +
> +	while (bytes_left) {
> +		folio = read_mapping_folio(bdev_file->f_mapping, f_index++, NULL);
> +		if (IS_ERR(folio)) {
> +			ret = PTR_ERR(folio);
> +			goto err_release_bdev;
> +		}
> +		to_read = min_t(unsigned long, bytes_left, PAGE_SIZE - offs);
> +		p = folio_address(folio) + offset_in_folio(folio, offs);
> +		memcpy(val, p, to_read);
> +		offs = 0;
> +		bytes_left -= to_read;
> +		val += to_read;
> +		folio_put(folio);
> +	}
> +
> +err_release_bdev:
> +	fput(bdev_file);
> +
> +	return ret;
> +}
> +
> +static int blk_nvmem_register(struct device *dev)
> +{
> +	struct device_node *np = dev_of_node(dev);
> +	struct block_device *bdev = dev_to_bdev(dev);
> +	struct nvmem_config config = {};
> +	struct blk_nvmem *bnv;
> +
> +	/* skip devices which do not have a device tree node */
> +	if (!np)
> +		return 0;
> +
> +	/* skip devices without an nvmem layout defined */
> +	if (!of_get_child_by_name(np, "nvmem-layout"))
> +		return 0;
> +
> +	/*
> +	 * skip block device too large to be represented as NVMEM devices
> +	 * which are using an 'int' as address
> +	 */
> +	if (bdev_nr_bytes(bdev) > INT_MAX)
> +		return -EFBIG;
> +
> +	bnv = kzalloc_obj(*bnv);
> +	if (!bnv)
> +		return -ENOMEM;
> +
> +	config.id = NVMEM_DEVID_NONE;
> +	config.dev = &bdev->bd_device;
> +	config.name = dev_name(&bdev->bd_device);
> +	config.owner = THIS_MODULE;
> +	config.priv = bnv;
> +	config.reg_read = blk_nvmem_reg_read;
> +	config.size = bdev_nr_bytes(bdev);
> +	config.word_size = 1;
> +	config.stride = 1;
> +	config.read_only = true;
> +	config.root_only = true;
> +	config.ignore_wp = true;
> +	config.of_node = to_of_node(dev->fwnode);
> +
> +	bnv->devt = bdev->bd_device.devt;
> +	bnv->nvmem = nvmem_register(&config);
> +	if (IS_ERR(bnv->nvmem)) {
> +		dev_err_probe(&bdev->bd_device, PTR_ERR(bnv->nvmem),
> +			      "Failed to register NVMEM device\n");
> +
> +		kfree(bnv);
> +		return PTR_ERR(bnv->nvmem);
> +	}
> +
> +	/*
> +	 * Free bnv only when the nvmem device is fully released (i.e. when
> +	 * its kref hits zero), not at unregister time. This prevents a
> +	 * use-after-free if a consumer still holds an nvmem_cell reference
> +	 * when the block device is removed: nvmem_unregister() only does a
> +	 * kref_put(), so reg_read could still be called with bnv as priv
> +	 * until the last consumer drops its cell.
> +	 */
> +	if (devm_add_action(nvmem_dev(bnv->nvmem), blk_nvmem_free, bnv)) {
> +		nvmem_unregister(bnv->nvmem);
> +		kfree(bnv);
> +		return -ENOMEM;
> +	}

Please take a look at the series[1] I sent, it seems to address this issue at
the nvmem core level. Help reviewing it is appreciated. :)

In any case: I don't think it will work the way you intend it to: the devres
action will be executed when the nvmem device is "unbound" not when it's
"released". Not only that but the device you retrieve here is not the "parent"
device that's actually bound to the driver but the "logical" nvmem device, the
nvmem core creates to back the struct nvmem_device's functionality and reference
counting. In other words: I believe the devres action will never be called.

In general, it's a very bad idea to schedule devres actions on devices you
don't control and in context different than at probe() time.

> +
> +	mutex_lock(&devices_mutex);
> +	list_add_tail(&bnv->list, &nvmem_devices);
> +	mutex_unlock(&devices_mutex);
> +
> +	return 0;
> +}
> +
> +static void blk_nvmem_unregister(struct device *dev)
> +{
> +	struct blk_nvmem *bnv_c, *bnv = NULL;
> +
> +	mutex_lock(&devices_mutex);
> +	list_for_each_entry(bnv_c, &nvmem_devices, list) {
> +		if (bnv_c->devt == dev_to_bdev(dev)->bd_device.devt) {
> +			bnv = bnv_c;
> +			break;
> +		}
> +	}
> +
> +	if (!bnv) {
> +		mutex_unlock(&devices_mutex);
> +		return;
> +	}
> +
> +	list_del(&bnv->list);
> +	mutex_unlock(&devices_mutex);
> +	bnv->removed = true;
> +	nvmem_unregister(bnv->nvmem);
> +}
> +
> +static struct class_interface blk_nvmem_bus_interface __refdata = {
> +	.class = &block_class,
> +	.add_dev = &blk_nvmem_register,
> +	.remove_dev = &blk_nvmem_unregister,
> +};
> +
> +static int __init blk_nvmem_init(void)
> +{
> +	int ret;
> +
> +	ret = class_interface_register(&blk_nvmem_bus_interface);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +device_initcall(blk_nvmem_init);
> diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
> index 311cb2e5a5c02d2c6979d7c9bbb7f94abdfbdad1..ee3481229c20b3063c86d0dd66aabbf6b5e29169 100644
> --- a/drivers/nvmem/core.c
> +++ b/drivers/nvmem/core.c
> @@ -2154,6 +2154,19 @@ const char *nvmem_dev_name(struct nvmem_device *nvmem)
>  }
>  EXPORT_SYMBOL_GPL(nvmem_dev_name);
>
> +/**
> + * nvmem_dev() - Get the struct device of a given nvmem device.
> + *
> + * @nvmem: nvmem device.
> + *
> + * Return: pointer to the struct device of the nvmem device.
> + */
> +struct device *nvmem_dev(struct nvmem_device *nvmem)
> +{
> +	return &nvmem->dev;
> +}
> +EXPORT_SYMBOL_GPL(nvmem_dev);
> +

That should be done in a separate patch but see my response above.

>  /**
>   * nvmem_dev_size() - Get the size of a given nvmem device.
>   *
> diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h
> index 34c0e58dfa26636d2804fcc7e0bc4a875ee73dae..ce387c89dc8e4bc1241f3b6f36be8c6c95e282ed 100644
> --- a/include/linux/nvmem-consumer.h
> +++ b/include/linux/nvmem-consumer.h
> @@ -82,6 +82,7 @@ int nvmem_device_cell_write(struct nvmem_device *nvmem,
>
>  const char *nvmem_dev_name(struct nvmem_device *nvmem);
>  size_t nvmem_dev_size(struct nvmem_device *nvmem);
> +struct device *nvmem_dev(struct nvmem_device *nvmem);
>
>  void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries,
>  			    size_t nentries);
> @@ -220,6 +221,11 @@ static inline const char *nvmem_dev_name(struct nvmem_device *nvmem)
>  	return NULL;
>  }
>
> +static inline struct device *nvmem_dev(struct nvmem_device *nvmem)
> +{
> +	return NULL;
> +}
> +
>  static inline void
>  nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) {}
>  static inline void
>
> --
> 2.34.1
>
>

Thanks,
Bartosz

[1] https://lore.kernel.org/all/20260429-nvmem-unbind-v3-0-2a694f95395b@oss.qualcomm.com/

^ permalink raw reply

* RE: Bluetooth: btmtk: set HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN for MT6639
From: bluez.test.bot @ 2026-05-11 11:26 UTC (permalink / raw)
  To: linux-bluetooth, silviu.sandulache
In-Reply-To: <20260511104607.382060-1-silviu.sandulache@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 882 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=1092685

---Test result---

Test Summary:
CheckPatch                    PASS      0.55 seconds
GitLint                       PASS      0.23 seconds
SubjectPrefix                 PASS      0.08 seconds
BuildKernel                   PASS      25.33 seconds
CheckAllWarning               PASS      28.30 seconds
CheckSparse                   PASS      28.08 seconds
BuildKernel32                 PASS      25.05 seconds
TestRunnerSetup               PASS      549.88 seconds
IncrementalBuild              PASS      25.94 seconds



https://github.com/bluez/bluetooth-next/pull/164

---
Regards,
Linux Bluetooth


^ permalink raw reply

* RE: net: convert remaining bluetooth socket families to getsockopt_iter
From: bluez.test.bot @ 2026-05-11 11:01 UTC (permalink / raw)
  To: linux-bluetooth, leitao
In-Reply-To: <20260511-getsock_three-v1-1-1461fa8786ab@debian.org>

[-- Attachment #1: Type: text/plain, Size: 592 bytes --]

This is an automated email and please do not reply to this email.

Dear Submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository.

----- Output -----

error: patch failed: tools/testing/selftests/net/getsockopt_iter.c:297
error: tools/testing/selftests/net/getsockopt_iter.c: patch does not apply
hint: Use 'git am --show-current-patch' to see the failed patch

Please resolve the issue and submit the patches again.


---
Regards,
Linux Bluetooth


^ permalink raw reply

* [PATCH BlueZ 1/1] bap: set QOS state when CIS is lost while the state is streaming/enabling
From: raghu447 @ 2026-05-11 10:58 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: raghu447
In-Reply-To: <20260511105845.8008-1-raghavendra.rao@collabora.com>

This is used to Pass PTS tests BAP/USR/SCC/BV-167-C abd BV-168-C.
---
 src/shared/bap.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/src/shared/bap.c b/src/shared/bap.c
index 78ba22259..2edd4b249 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -6670,9 +6670,38 @@ bool bt_bap_match_bcast_sink_stream(const void *data, const void *user_data)
 	return stream->lpac->type == BT_BAP_BCAST_SINK;
 }
 
+static void stream_io_qos_disconnect(struct bt_bap_stream *stream,
+					struct bt_bap_stream_io *io)
+{
+	uint8_t state;
+
+	if (!stream || !stream->ep || stream->io != io)
+		return;
+
+	state = stream->ep->state;
+
+	DBG(stream->bap, "CIS disconnected for stream %p state %u", stream,
+								state);
+
+	if (state != BT_ASCS_ASE_STATE_ENABLING &&
+			state != BT_ASCS_ASE_STATE_STREAMING)
+		return;
+
+	DBG(stream->bap, "Moving ASE %u to QoS Configured after CIS loss",
+							stream->ep->id);
+
+	stream_set_state(stream, BT_BAP_STREAM_STATE_QOS);
+}
+
+static void stream_link_io_qos_disconnect(void *data, void *user_data)
+{
+	stream_io_qos_disconnect(data, user_data);
+}
+
 static bool stream_io_disconnected(struct io *io, void *user_data)
 {
 	struct bt_bap_stream *stream = user_data;
+	struct bt_bap_stream_io *sio;
 
 	DBG(stream->bap, "stream %p io disconnected", stream);
 
@@ -6685,6 +6714,12 @@ static bool stream_io_disconnected(struct io *io, void *user_data)
 		return false;
 	}
 
+	sio = stream->io;
+	if (sio) {
+		stream_io_qos_disconnect(stream, sio);
+		queue_foreach(stream->links, stream_link_io_qos_disconnect, sio);
+	}
+
 	if (stream->ep->state == BT_ASCS_ASE_STATE_RELEASING)
 		stream_set_state(stream, BT_BAP_STREAM_STATE_CONFIG);
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH BlueZ 0/1] /bap: Handle CIS loss during streaming
From: raghu447 @ 2026-05-11 10:58 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: raghu447

This patch sets the QOS state when CIS is lost during the streaming.

This is used to pass the PTS tests BAP/USR/SCC/BV-167-C abd BV-168-C.

raghu447 (1):
  bap: set QOS state when CIS is lost while the state is
    streaming/enabling

 src/shared/bap.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

-- 
2.43.0


^ permalink raw reply

* [PATCH] Bluetooth: btmtk: set HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN for MT6639
From: Silviu Sandulache @ 2026-05-11 10:46 UTC (permalink / raw)
  To: Luiz Augusto von Dentz, Marcel Holtmann, Johan Hedberg
  Cc: linux-bluetooth, linux-kernel, Sean Wang, Javier Tia

The MediaTek MT6639 Bluetooth controller (the BT half of the MT7925/MT7927
WiFi 7 combo silicon shipped on motherboards such as the ASUS ProArt
X870E-CREATOR WIFI) advertises support for HCI Enhanced Setup Synchronous
Connection (opcode 0x043D) in its supported-commands bitmap, but rejects
the command at runtime.

This breaks HFP wideband-speech (mSBC): when a Bluetooth headset is
connected and the kernel attempts to set up an mSBC eSCO link, the
controller responds with an error and the kernel logs:

  Bluetooth: hciN: HCI Enhanced Setup Synchronous Connection command \
      is advertised, but not supported.

User-visible symptom: the headset microphone captures pure silence in
HFP mode, while A2DP playback works normally. This reproduces on every
MT6639-based machine seen so far.

A Bluetooth HCI trace captured on Windows on the same hardware (ASUS
X870E-CREATOR with the MediaTek WHQL driver, recorded via the ETW
BTHPORT provider during an HFP wideband recording session) shows that
the Windows driver works around the same firmware bug by issuing the
classic Setup Synchronous Connection command (opcode 0x0428) with
Transparent air-mode parameters. The classic command sets up an mSBC
eSCO link successfully, and the headset streams 16 kHz mono wideband
audio over it as expected.

HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN exists in the BR/EDR HCI core
for exactly this case: when set, hci_setup_sync_conn() falls back to
the classic Setup Synchronous Connection command with the same
parameters. Setting the quirk for MT6639 makes Linux do what Windows
does, restoring wideband HFP.

The change is scoped to dev_id == 0x6639 to keep it hardware-confirmed
on the chip where the bug has been reproduced and the Windows fallback
has been observed. Sibling MT79xx variants (MT7921 / MT7922 / MT7925 /
MT7961) exhibit the same dmesg signature in community bug reports; if
the same fallback proves correct for them, extending the quirk to
those chip IDs is straightforward follow-up.

Verification on an ASUS ProArt X870E-CREATOR WIFI:

  - Before patch: profile switch to headset-head-unit (mSBC) is
    accepted by PipeWire, but the kernel issues HCI_OP_ENHANCED_SETUP_
    SYNC_CONN (0x043D), the controller refuses it, no eSCO link is
    established, the mic captures pure silence.
  - After patch: the kernel issues HCI_OP_SETUP_SYNC_CONN (0x0428)
    with Air Coding Format = Transparent. The controller responds
    Synchronous Connect Complete: Status Success, Link type eSCO,
    Air mode Transparent. SCO data flows over the link.

Signed-off-by: Silviu Sandulache <silviu.sandulache@gmail.com>
---
 drivers/bluetooth/btmtk.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index ab34f1d..4bd2e6c 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -1417,6 +1417,19 @@ int btmtk_usb_setup(struct hci_dev *hdev)
 		hci_set_msft_opcode(hdev, 0xFD30);
 		hci_set_aosp_capable(hdev);
 
+		/* MT6639 firmware advertises the HCI Enhanced Setup
+		 * Synchronous Connection command (opcode 0x043D) in its
+		 * supported-commands bitmap, but actually rejects it at
+		 * runtime, breaking mSBC wideband HFP (the headset mic
+		 * captures pure silence). The Windows MediaTek driver
+		 * works around this by falling back to the classic Setup
+		 * Synchronous Connection command (opcode 0x0428); setting
+		 * this quirk makes the BR/EDR core do the same fallback.
+		 */
+		if (dev_id == 0x6639)
+			hci_set_quirk(hdev,
+				      HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN);
+
 		/* Clear BTMTK_FIRMWARE_DL_RETRY if setup successfully */
 		test_and_clear_bit(BTMTK_FIRMWARE_DL_RETRY, &btmtk_data->flags);
 
-- 
2.54.0


^ permalink raw reply related

* [PATCH net-next 7/7] Bluetooth: SCO: convert to getsockopt_iter
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Shuah Khan
  Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
	Breno Leitao, kernel-team
In-Reply-To: <20260511-getsock_three-v1-0-1461fa8786ab@debian.org>

Convert SCO socket's getsockopt implementation to use the new
getsockopt_iter callback with sockopt_t.

Key changes:
- Replace (char __user *optval, int __user *optlen) with sockopt_t *opt
- Use opt->optlen for buffer length (input) and returned size (output)
- Use copy_to_iter() instead of put_user()/copy_to_user()
- Drop the open-coded ptr cursor in BT_CODEC; iter_out advances on
  every copy_to_iter() naturally
- Add linux/uio.h for copy_to_iter()

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 net/bluetooth/sco.c | 59 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 31 insertions(+), 28 deletions(-)

diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index eba44525d41d9..37dcba9edfab6 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -28,6 +28,7 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/sched/signal.h>
+#include <linux/uio.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -1072,7 +1073,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
 }
 
 static int sco_sock_getsockopt_old(struct socket *sock, int optname,
-				   char __user *optval, int __user *optlen)
+				   sockopt_t *opt)
 {
 	struct sock *sk = sock->sk;
 	struct sco_options opts;
@@ -1082,8 +1083,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname,
 
 	BT_DBG("sk %p", sk);
 
-	if (get_user(len, optlen))
-		return -EFAULT;
+	len = opt->optlen;
 
 	lock_sock(sk);
 
@@ -1101,7 +1101,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname,
 		BT_DBG("mtu %u", opts.mtu);
 
 		len = min(len, sizeof(opts));
-		if (copy_to_user(optval, (char *)&opts, len))
+		if (copy_to_iter(&opts, len, &opt->iter_out) != len)
 			err = -EFAULT;
 
 		break;
@@ -1119,7 +1119,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname,
 		memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3);
 
 		len = min(len, sizeof(cinfo));
-		if (copy_to_user(optval, (char *)&cinfo, len))
+		if (copy_to_iter(&cinfo, len, &opt->iter_out) != len)
 			err = -EFAULT;
 
 		break;
@@ -1134,15 +1134,15 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname,
 }
 
 static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
-			       char __user *optval, int __user *optlen)
+			       sockopt_t *opt)
 {
 	struct sock *sk = sock->sk;
-	int len, err = 0;
+	int len, val, err = 0;
 	struct bt_voice voice;
 	u32 phys;
 	int buf_len;
 	struct codec_list *c;
-	u8 num_codecs, i, __user *ptr;
+	u8 num_codecs, i;
 	struct hci_dev *hdev;
 	struct hci_codec_caps *caps;
 	struct bt_codec codec;
@@ -1150,10 +1150,9 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
 	BT_DBG("sk %p", sk);
 
 	if (level == SOL_SCO)
-		return sco_sock_getsockopt_old(sock, optname, optval, optlen);
+		return sco_sock_getsockopt_old(sock, optname, opt);
 
-	if (get_user(len, optlen))
-		return -EFAULT;
+	len = opt->optlen;
 
 	lock_sock(sk);
 
@@ -1165,8 +1164,9 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
 			break;
 		}
 
-		if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
-			     (u32 __user *)optval))
+		val = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+		if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
+		    sizeof(val))
 			err = -EFAULT;
 
 		break;
@@ -1175,7 +1175,7 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
 		voice.setting = sco_pi(sk)->setting;
 
 		len = min_t(unsigned int, len, sizeof(voice));
-		if (copy_to_user(optval, (char *)&voice, len))
+		if (copy_to_iter(&voice, len, &opt->iter_out) != len)
 			err = -EFAULT;
 
 		break;
@@ -1188,13 +1188,15 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
 
 		phys = hci_conn_get_phy(sco_pi(sk)->conn->hcon);
 
-		if (put_user(phys, (u32 __user *) optval))
+		if (copy_to_iter(&phys, sizeof(phys), &opt->iter_out) !=
+		    sizeof(phys))
 			err = -EFAULT;
 		break;
 
 	case BT_PKT_STATUS:
-		if (put_user(test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags),
-			     (int __user *)optval))
+		val = test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags);
+		if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
+		    sizeof(val))
 			err = -EFAULT;
 		break;
 
@@ -1205,7 +1207,9 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
 			break;
 		}
 
-		if (put_user(sco_pi(sk)->conn->mtu, (u32 __user *)optval))
+		val = sco_pi(sk)->conn->mtu;
+		if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
+		    sizeof(val))
 			err = -EFAULT;
 		break;
 
@@ -1252,13 +1256,12 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
 			hci_dev_put(hdev);
 			return -ENOBUFS;
 		}
-		ptr = optval;
 
-		if (put_user(num_codecs, ptr)) {
+		if (copy_to_iter(&num_codecs, sizeof(num_codecs),
+				 &opt->iter_out) != sizeof(num_codecs)) {
 			hci_dev_put(hdev);
 			return -EFAULT;
 		}
-		ptr += sizeof(num_codecs);
 
 		/* Iterate all the codecs supported over SCO and populate
 		 * codec data
@@ -1275,11 +1278,11 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
 			if (err < 0)
 				break;
 			codec.num_caps = c->num_caps;
-			if (copy_to_user(ptr, &codec, sizeof(codec))) {
+			if (copy_to_iter(&codec, sizeof(codec), &opt->iter_out)
+			    != sizeof(codec)) {
 				err = -EFAULT;
 				break;
 			}
-			ptr += sizeof(codec);
 
 			/* find codec capabilities data length */
 			len = 0;
@@ -1289,11 +1292,11 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
 			}
 
 			/* copy codec capabilities data */
-			if (len && copy_to_user(ptr, c->caps, len)) {
+			if (len &&
+			    copy_to_iter(c->caps, len, &opt->iter_out) != len) {
 				err = -EFAULT;
 				break;
 			}
-			ptr += len;
 		}
 
 		hci_dev_unlock(hdev);
@@ -1301,8 +1304,8 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
 
 		lock_sock(sk);
 
-		if (!err && put_user(buf_len, optlen))
-			err = -EFAULT;
+		if (!err)
+			opt->optlen = buf_len;
 
 		break;
 
@@ -1577,7 +1580,7 @@ static const struct proto_ops sco_sock_ops = {
 	.socketpair	= sock_no_socketpair,
 	.shutdown	= sco_sock_shutdown,
 	.setsockopt	= sco_sock_setsockopt,
-	.getsockopt	= sco_sock_getsockopt
+	.getsockopt_iter = sco_sock_getsockopt
 };
 
 static const struct net_proto_family sco_sock_family_ops = {

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next 6/7] selftests: net: getsockopt_iter: cover SCO BT_CODEC conversion
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Shuah Khan
  Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
	Breno Leitao, kernel-team
In-Reply-To: <20260511-getsock_three-v1-0-1461fa8786ab@debian.org>

Add a single test for the SCO conversion that replaced an open-coded
ptr cursor in BT_CODEC with plain copy_to_iter() calls. The test
pre-fills the user buffer with a sentinel byte and:

  - asserts the buffer is unchanged when the kernel returns -EBADFD
    or -EOPNOTSUPP (the common case in CI, where there is no
    controller exposing HCI_OFFLOAD_CODECS_ENABLED + a
    get_data_path_id op), or
  - asserts at least one byte changed when the call succeeds
    (capable controller present), validating the converted
    copy_to_iter() write path.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 tools/testing/selftests/net/getsockopt_iter.c | 65 +++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/tools/testing/selftests/net/getsockopt_iter.c b/tools/testing/selftests/net/getsockopt_iter.c
index 209569354d0e3..b3cac4be4af0d 100644
--- a/tools/testing/selftests/net/getsockopt_iter.c
+++ b/tools/testing/selftests/net/getsockopt_iter.c
@@ -11,6 +11,13 @@
  *   that always reports the required buffer length back via optlen,
  *   even when the user buffer is too small to receive any group bits.
  * - vsock:   SO_VM_SOCKETS_BUFFER_SIZE covers the u64 path.
+ * - bluetooth/sco: BT_CODEC exercises the SCO conversion that
+ *   replaced an open-coded ptr cursor with copy_to_iter() — the
+ *   test pre-fills the user buffer with a sentinel and asserts that
+ *   the error-return paths (hit when no controller / no codec
+ *   offload is available, the common case in CI) leave the buffer
+ *   untouched. If a capable controller is present, the success
+ *   path's copy_to_iter() write is validated instead.
  *
  * Author: Breno Leitao <leitao@debian.org>
  */
@@ -31,6 +38,15 @@
 #define AF_VSOCK 40
 #endif
 
+#ifndef AF_BLUETOOTH
+#define AF_BLUETOOTH	31
+#endif
+
+#define BTPROTO_SCO		2
+#define SOL_BLUETOOTH		274
+#define BT_CODEC		19
+#define BT_SENTINEL		0xa5
+
 /* ---------- netlink ---------- */
 
 FIXTURE(netlink)
@@ -297,4 +313,53 @@ TEST_F(vsock, connect_timeout_old_exact)
 	ASSERT_EQ(sizeof(tv), optlen);
 }
 
+/* ---------- bluetooth ---------- */
+
+/* sco: BT_CODEC. The SCO conversion replaced an open-coded ptr
+ * cursor in this option with plain copy_to_iter() calls. Without a
+ * controller exposing HCI_OFFLOAD_CODECS_ENABLED + a get_data_path_id
+ * op, the kernel returns -EBADFD / -EOPNOTSUPP from an early check
+ * and must NOT touch the user buffer. With such a controller the
+ * call succeeds and the buffer is filled by copy_to_iter().
+ */
+TEST(bt_sco_codec)
+{
+	socklen_t optlen;
+	uint8_t buf[256];
+	int fd, ret;
+
+	fd = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+	if (fd < 0)
+		SKIP(return, "AF_BLUETOOTH/SCO: %s", strerror(errno));
+
+	memset(buf, BT_SENTINEL, sizeof(buf));
+	optlen = sizeof(buf);
+	ret = getsockopt(fd, SOL_BLUETOOTH, BT_CODEC, buf, &optlen);
+
+	if (ret < 0) {
+		size_t i, changed = 0;
+
+		ASSERT_TRUE(errno == EBADFD || errno == EOPNOTSUPP)
+			TH_LOG("unexpected errno %d (%s)", errno,
+			       strerror(errno));
+		for (i = 0; i < sizeof(buf); i++)
+			if (buf[i] != BT_SENTINEL)
+				changed++;
+		ASSERT_EQ(0, changed)
+			TH_LOG("error path modified %zu byte(s)", changed);
+	} else {
+		size_t i, changed = 0;
+
+		ASSERT_GT(optlen, 0);
+		ASSERT_LE(optlen, sizeof(buf));
+		for (i = 0; i < optlen; i++)
+			if (buf[i] != BT_SENTINEL)
+				changed++;
+		ASSERT_GT(changed, 0)
+			TH_LOG("success path left buffer untouched");
+	}
+
+	close(fd);
+}
+
 TEST_HARNESS_MAIN

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next 5/7] Bluetooth: L2CAP: convert to getsockopt_iter
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Shuah Khan
  Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
	Breno Leitao, kernel-team
In-Reply-To: <20260511-getsock_three-v1-0-1461fa8786ab@debian.org>

Convert L2CAP socket's getsockopt implementation to use the new
getsockopt_iter callback with sockopt_t.

Key changes:
- Replace (char __user *optval, int __user *optlen) with sockopt_t *sopt
- Use sopt->optlen for buffer length (input)
- Use copy_to_iter() instead of put_user()/copy_to_user()
- Add linux/uio.h for copy_to_iter()

The sockopt_t parameter is named sopt rather than opt to avoid
collision with the existing local u32 opt used by L2CAP_LM. The same
naming is reused for the new u32 helper in l2cap_sock_getsockopt(),
with mtu and mval helpers covering the u16 and u8 cases.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 net/bluetooth/l2cap_sock.c | 61 ++++++++++++++++++++++++++++------------------
 1 file changed, 37 insertions(+), 24 deletions(-)

diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index cf590a67d3641..dede550d60319 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -31,6 +31,7 @@
 #include <linux/export.h>
 #include <linux/filter.h>
 #include <linux/sched/signal.h>
+#include <linux/uio.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -433,7 +434,7 @@ static int l2cap_get_mode(struct l2cap_chan *chan)
 }
 
 static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
-				     char __user *optval, int __user *optlen)
+				     sockopt_t *sopt)
 {
 	struct sock *sk = sock->sk;
 	struct l2cap_chan *chan = l2cap_pi(sk)->chan;
@@ -445,8 +446,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
 
 	BT_DBG("sk %p", sk);
 
-	if (get_user(len, optlen))
-		return -EFAULT;
+	len = sopt->optlen;
 
 	lock_sock(sk);
 
@@ -488,7 +488,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
 		BT_DBG("mode 0x%2.2x", chan->mode);
 
 		len = min(len, sizeof(opts));
-		if (copy_to_user(optval, (char *) &opts, len))
+		if (copy_to_iter(&opts, len, &sopt->iter_out) != len)
 			err = -EFAULT;
 
 		break;
@@ -520,7 +520,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
 		if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
 			opt |= L2CAP_LM_RELIABLE;
 
-		if (put_user(opt, (u32 __user *) optval))
+		if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+		    sizeof(opt))
 			err = -EFAULT;
 
 		break;
@@ -538,7 +539,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
 		memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3);
 
 		len = min(len, sizeof(cinfo));
-		if (copy_to_user(optval, (char *) &cinfo, len))
+		if (copy_to_iter(&cinfo, len, &sopt->iter_out) != len)
 			err = -EFAULT;
 
 		break;
@@ -553,25 +554,26 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
 }
 
 static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
-				 char __user *optval, int __user *optlen)
+				 sockopt_t *sopt)
 {
 	struct sock *sk = sock->sk;
 	struct l2cap_chan *chan = l2cap_pi(sk)->chan;
 	struct bt_security sec;
 	struct bt_power pwr;
-	u32 phys;
 	int len, mode, err = 0;
+	u32 opt;
+	u16 mtu;
+	u8 mval;
 
 	BT_DBG("sk %p", sk);
 
 	if (level == SOL_L2CAP)
-		return l2cap_sock_getsockopt_old(sock, optname, optval, optlen);
+		return l2cap_sock_getsockopt_old(sock, optname, sopt);
 
 	if (level != SOL_BLUETOOTH)
 		return -ENOPROTOOPT;
 
-	if (get_user(len, optlen))
-		return -EFAULT;
+	len = sopt->optlen;
 
 	lock_sock(sk);
 
@@ -595,7 +597,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
 		}
 
 		len = min_t(unsigned int, len, sizeof(sec));
-		if (copy_to_user(optval, (char *) &sec, len))
+		if (copy_to_iter(&sec, len, &sopt->iter_out) != len)
 			err = -EFAULT;
 
 		break;
@@ -606,15 +608,17 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
 			break;
 		}
 
-		if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
-			     (u32 __user *) optval))
+		opt = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+		if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+		    sizeof(opt))
 			err = -EFAULT;
 
 		break;
 
 	case BT_FLUSHABLE:
-		if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags),
-			     (u32 __user *) optval))
+		opt = test_bit(FLAG_FLUSHABLE, &chan->flags);
+		if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+		    sizeof(opt))
 			err = -EFAULT;
 
 		break;
@@ -629,13 +633,15 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
 		pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
 
 		len = min_t(unsigned int, len, sizeof(pwr));
-		if (copy_to_user(optval, (char *) &pwr, len))
+		if (copy_to_iter(&pwr, len, &sopt->iter_out) != len)
 			err = -EFAULT;
 
 		break;
 
 	case BT_CHANNEL_POLICY:
-		if (put_user(chan->chan_policy, (u32 __user *) optval))
+		opt = chan->chan_policy;
+		if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+		    sizeof(opt))
 			err = -EFAULT;
 		break;
 
@@ -650,7 +656,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
 			break;
 		}
 
-		if (put_user(chan->omtu, (u16 __user *) optval))
+		mtu = chan->omtu;
+		if (copy_to_iter(&mtu, sizeof(mtu), &sopt->iter_out) !=
+		    sizeof(mtu))
 			err = -EFAULT;
 		break;
 
@@ -660,7 +668,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
 			break;
 		}
 
-		if (put_user(chan->imtu, (u16 __user *) optval))
+		mtu = chan->imtu;
+		if (copy_to_iter(&mtu, sizeof(mtu), &sopt->iter_out) !=
+		    sizeof(mtu))
 			err = -EFAULT;
 		break;
 
@@ -670,9 +680,10 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
 			break;
 		}
 
-		phys = hci_conn_get_phy(chan->conn->hcon);
+		opt = hci_conn_get_phy(chan->conn->hcon);
 
-		if (put_user(phys, (u32 __user *) optval))
+		if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+		    sizeof(opt))
 			err = -EFAULT;
 		break;
 
@@ -693,7 +704,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
 			break;
 		}
 
-		if (put_user(mode, (u8 __user *) optval))
+		mval = mode;
+		if (copy_to_iter(&mval, sizeof(mval), &sopt->iter_out) !=
+		    sizeof(mval))
 			err = -EFAULT;
 		break;
 
@@ -2000,7 +2013,7 @@ static const struct proto_ops l2cap_sock_ops = {
 	.socketpair	= sock_no_socketpair,
 	.shutdown	= l2cap_sock_shutdown,
 	.setsockopt	= l2cap_sock_setsockopt,
-	.getsockopt	= l2cap_sock_getsockopt
+	.getsockopt_iter = l2cap_sock_getsockopt
 };
 
 static const struct net_proto_family l2cap_sock_family_ops = {

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next 4/7] Bluetooth: RFCOMM: convert to getsockopt_iter
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Shuah Khan
  Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
	Breno Leitao, kernel-team
In-Reply-To: <20260511-getsock_three-v1-0-1461fa8786ab@debian.org>

Convert RFCOMM socket's getsockopt implementation to use the new
getsockopt_iter callback with sockopt_t.

Key changes:
- Replace (char __user *optval, int __user *optlen) with sockopt_t *sopt
- Use sopt->optlen for buffer length (input)
- Use copy_to_iter() instead of put_user()/copy_to_user()
- Add linux/uio.h for copy_to_iter()

The sockopt_t parameter is named sopt rather than opt to avoid
collision with the existing local u32 opt used by RFCOMM_LM.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 net/bluetooth/rfcomm/sock.c | 30 +++++++++++++++++-------------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index be6639cd6f590..9b22e4240ed14 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -28,6 +28,7 @@
 #include <linux/export.h>
 #include <linux/debugfs.h>
 #include <linux/sched/signal.h>
+#include <linux/uio.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -723,7 +724,8 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname,
 	return err;
 }
 
-static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
+static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname,
+				      sockopt_t *sopt)
 {
 	struct sock *sk = sock->sk;
 	struct sock *l2cap_sk;
@@ -735,8 +737,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
 
 	BT_DBG("sk %p", sk);
 
-	if (get_user(len, optlen))
-		return -EFAULT;
+	len = sopt->optlen;
 
 	lock_sock(sk);
 
@@ -765,7 +766,8 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
 		if (rfcomm_pi(sk)->role_switch)
 			opt |= RFCOMM_LM_MASTER;
 
-		if (put_user(opt, (u32 __user *) optval))
+		if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+		    sizeof(opt))
 			err = -EFAULT;
 
 		break;
@@ -785,7 +787,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
 		memcpy(cinfo.dev_class, conn->hcon->dev_class, 3);
 
 		len = min(len, sizeof(cinfo));
-		if (copy_to_user(optval, (char *) &cinfo, len))
+		if (copy_to_iter(&cinfo, len, &sopt->iter_out) != len)
 			err = -EFAULT;
 
 		break;
@@ -799,23 +801,24 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
 	return err;
 }
 
-static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
+static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname,
+				  sockopt_t *sopt)
 {
 	struct sock *sk = sock->sk;
 	struct bt_security sec;
 	int err = 0;
 	size_t len;
+	u32 opt;
 
 	BT_DBG("sk %p", sk);
 
 	if (level == SOL_RFCOMM)
-		return rfcomm_sock_getsockopt_old(sock, optname, optval, optlen);
+		return rfcomm_sock_getsockopt_old(sock, optname, sopt);
 
 	if (level != SOL_BLUETOOTH)
 		return -ENOPROTOOPT;
 
-	if (get_user(len, optlen))
-		return -EFAULT;
+	len = sopt->optlen;
 
 	lock_sock(sk);
 
@@ -830,7 +833,7 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c
 		sec.key_size = 0;
 
 		len = min(len, sizeof(sec));
-		if (copy_to_user(optval, (char *) &sec, len))
+		if (copy_to_iter(&sec, len, &sopt->iter_out) != len)
 			err = -EFAULT;
 
 		break;
@@ -841,8 +844,9 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c
 			break;
 		}
 
-		if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
-			     (u32 __user *) optval))
+		opt = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+		if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+		    sizeof(opt))
 			err = -EFAULT;
 
 		break;
@@ -1014,7 +1018,7 @@ static const struct proto_ops rfcomm_sock_ops = {
 	.recvmsg	= rfcomm_sock_recvmsg,
 	.shutdown	= rfcomm_sock_shutdown,
 	.setsockopt	= rfcomm_sock_setsockopt,
-	.getsockopt	= rfcomm_sock_getsockopt,
+	.getsockopt_iter = rfcomm_sock_getsockopt,
 	.ioctl		= rfcomm_sock_ioctl,
 	.gettstamp	= sock_gettstamp,
 	.poll		= bt_sock_poll,

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next 3/7] Bluetooth: ISO: convert to getsockopt_iter
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Shuah Khan
  Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
	Breno Leitao, kernel-team
In-Reply-To: <20260511-getsock_three-v1-0-1461fa8786ab@debian.org>

Convert ISO socket's getsockopt implementation to use the new
getsockopt_iter callback with sockopt_t.

Key changes:
- Replace (char __user *optval, int __user *optlen) with sockopt_t *opt
- Use opt->optlen for buffer length (input) and returned size (output)
- Use copy_to_iter() instead of put_user()/copy_to_user()
- Add linux/uio.h for copy_to_iter()

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 net/bluetooth/iso.c | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 7cb2864fe8724..0d792d36bb5ac 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -10,6 +10,7 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/sched/signal.h>
+#include <linux/uio.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -1842,18 +1843,17 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
 }
 
 static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
-			       char __user *optval, int __user *optlen)
+			       sockopt_t *opt)
 {
 	struct sock *sk = sock->sk;
-	int len, err = 0;
 	struct bt_iso_qos *qos;
+	int len, val, err = 0;
 	u8 base_len;
 	u8 *base;
 
 	BT_DBG("sk %p", sk);
 
-	if (get_user(len, optlen))
-		return -EFAULT;
+	len = opt->optlen;
 
 	lock_sock(sk);
 
@@ -1864,15 +1864,17 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
 			break;
 		}
 
-		if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
-			     (u32 __user *)optval))
+		val = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+		if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
+		    sizeof(val))
 			err = -EFAULT;
 
 		break;
 
 	case BT_PKT_STATUS:
-		if (put_user(test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags),
-			     (int __user *)optval))
+		val = test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags);
+		if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
+		    sizeof(val))
 			err = -EFAULT;
 		break;
 
@@ -1880,7 +1882,7 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
 		qos = iso_sock_get_qos(sk);
 
 		len = min_t(unsigned int, len, sizeof(*qos));
-		if (copy_to_user(optval, qos, len))
+		if (copy_to_iter(qos, len, &opt->iter_out) != len)
 			err = -EFAULT;
 
 		break;
@@ -1896,9 +1898,8 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
 		}
 
 		len = min_t(unsigned int, len, base_len);
-		if (copy_to_user(optval, base, len))
-			err = -EFAULT;
-		if (put_user(len, optlen))
+		opt->optlen = len;
+		if (copy_to_iter(base, len, &opt->iter_out) != len)
 			err = -EFAULT;
 
 		break;
@@ -2660,7 +2661,7 @@ static const struct proto_ops iso_sock_ops = {
 	.socketpair	= sock_no_socketpair,
 	.shutdown	= iso_sock_shutdown,
 	.setsockopt	= iso_sock_setsockopt,
-	.getsockopt	= iso_sock_getsockopt
+	.getsockopt_iter = iso_sock_getsockopt
 };
 
 static const struct net_proto_family iso_sock_family_ops = {

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next 2/7] Bluetooth: hci_sock: convert to getsockopt_iter
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Shuah Khan
  Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
	Breno Leitao, kernel-team
In-Reply-To: <20260511-getsock_three-v1-0-1461fa8786ab@debian.org>

Convert HCI socket's getsockopt implementation to use the new
getsockopt_iter callback with sockopt_t.

Key changes:
- Replace (char __user *optval, int __user *optlen) with sockopt_t *sopt
- Use sopt->optlen for buffer length (input)
- Use copy_to_iter() instead of put_user()/copy_to_user()
- Add linux/uio.h for copy_to_iter()

The sockopt_t parameter is named sopt rather than opt to avoid
collision with the existing local int opt used by HCI_DATA_DIR and
HCI_TIME_STAMP.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 net/bluetooth/hci_sock.c | 26 +++++++++++++++-----------
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 1823c06ba8940..61fec674a2078 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -27,6 +27,7 @@
 #include <linux/export.h>
 #include <linux/utsname.h>
 #include <linux/sched.h>
+#include <linux/uio.h>
 #include <linux/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
@@ -2063,7 +2064,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
 }
 
 static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
-				   char __user *optval, int __user *optlen)
+				   sockopt_t *sopt)
 {
 	struct hci_ufilter uf;
 	struct sock *sk = sock->sk;
@@ -2071,8 +2072,7 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
 
 	BT_DBG("sk %p, opt %d", sk, optname);
 
-	if (get_user(len, optlen))
-		return -EFAULT;
+	len = sopt->optlen;
 
 	lock_sock(sk);
 
@@ -2088,7 +2088,8 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
 		else
 			opt = 0;
 
-		if (put_user(opt, (int __user *)optval))
+		if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+		    sizeof(opt))
 			err = -EFAULT;
 		break;
 
@@ -2098,7 +2099,8 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
 		else
 			opt = 0;
 
-		if (put_user(opt, (int __user *)optval))
+		if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
+		    sizeof(opt))
 			err = -EFAULT;
 		break;
 
@@ -2114,7 +2116,7 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
 		}
 
 		len = min_t(unsigned int, len, sizeof(uf));
-		if (copy_to_user(optval, &uf, len))
+		if (copy_to_iter(&uf, len, &sopt->iter_out) != len)
 			err = -EFAULT;
 		break;
 
@@ -2129,16 +2131,16 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
 }
 
 static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
-			       char __user *optval, int __user *optlen)
+			       sockopt_t *sopt)
 {
 	struct sock *sk = sock->sk;
 	int err = 0;
+	u16 mtu;
 
 	BT_DBG("sk %p, opt %d", sk, optname);
 
 	if (level == SOL_HCI)
-		return hci_sock_getsockopt_old(sock, level, optname, optval,
-					       optlen);
+		return hci_sock_getsockopt_old(sock, level, optname, sopt);
 
 	if (level != SOL_BLUETOOTH)
 		return -ENOPROTOOPT;
@@ -2148,7 +2150,9 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
 	switch (optname) {
 	case BT_SNDMTU:
 	case BT_RCVMTU:
-		if (put_user(hci_pi(sk)->mtu, (u16 __user *)optval))
+		mtu = hci_pi(sk)->mtu;
+		if (copy_to_iter(&mtu, sizeof(mtu), &sopt->iter_out) !=
+		    sizeof(mtu))
 			err = -EFAULT;
 		break;
 
@@ -2185,7 +2189,7 @@ static const struct proto_ops hci_sock_ops = {
 	.listen		= sock_no_listen,
 	.shutdown	= sock_no_shutdown,
 	.setsockopt	= hci_sock_setsockopt,
-	.getsockopt	= hci_sock_getsockopt,
+	.getsockopt_iter = hci_sock_getsockopt,
 	.connect	= sock_no_connect,
 	.socketpair	= sock_no_socketpair,
 	.accept		= sock_no_accept,

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next 0/7] net: convert remaining bluetooth socket families to getsockopt_iter
From: Breno Leitao @ 2026-05-11 10:41 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Shuah Khan
  Cc: linux-bluetooth, linux-kernel, netdev, linux-kselftest,
	Breno Leitao, kernel-team

Continue the conversion to .getsockopt_iter for the Bluetooth socket
families: hci_sock, ISO, RFCOMM, SCO and L2CAP. The first patch is a
small precursor that fixes a long-standing 1-byte put_user write in
hci_sock_getsockopt_old() so the subsequent conversion stays mechanical.

The riskiest change in this series is the SCO BT_CODEC conversion: it
is the only one that drops an open-coded ptr cursor in favour of
relying on iter_out advancing naturally on every copy_to_iter() call.
Every other socket option is a near-mechanical s/copy_to_user/
copy_to_iter/ rewrite, but BT_CODEC walks a variable-length list of
codecs + capabilities and previously tracked its own write offset by
hand. Getting the cursor semantics wrong here would silently truncate
or misalign user-visible codec data.

To guard against regressions in that specific path, a patch (previous to
the change) extends tools/testing/selftests/net/getsockopt_iter.c with
a focused BT_CODEC test that pre-fills the user buffer with a sentinel
and asserts:

  - the buffer is unchanged on the early error returns (-EBADFD /
    -EOPNOTSUPP, the common case in CI without a controller exposing
    HCI_OFFLOAD_CODECS_ENABLED + a get_data_path_id op), and
  - at least one byte changed on the success path (when a capable
    controller is present), proving the converted copy_to_iter() walk
    actually wrote into user memory.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
Breno Leitao (7):
      Bluetooth: hci_sock: write the full optval for getsockopt
      Bluetooth: hci_sock: convert to getsockopt_iter
      Bluetooth: ISO: convert to getsockopt_iter
      Bluetooth: RFCOMM: convert to getsockopt_iter
      Bluetooth: L2CAP: convert to getsockopt_iter
      selftests: net: getsockopt_iter: cover SCO BT_CODEC conversion
      Bluetooth: SCO: convert to getsockopt_iter

 net/bluetooth/hci_sock.c                      | 26 ++++++-----
 net/bluetooth/iso.c                           | 27 +++++------
 net/bluetooth/l2cap_sock.c                    | 61 +++++++++++++++----------
 net/bluetooth/rfcomm/sock.c                   | 30 +++++++------
 net/bluetooth/sco.c                           | 59 ++++++++++++------------
 tools/testing/selftests/net/getsockopt_iter.c | 65 +++++++++++++++++++++++++++
 6 files changed, 179 insertions(+), 89 deletions(-)
---
base-commit: 63751099502d10f0aa6bb35273e56c5800cc4e3a
change-id: 20260511-getsock_three-d0d7f1b2629e

Best regards,
--  
Breno Leitao <leitao@debian.org>


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox