* [PATCH BlueZ v5 15/16] test: functional: add some Agent1 interface tests
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Add test
test/functional/test_agent.py::test_agent_pair_bredr
---
test/functional/test_agent.py | 46 +++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
create mode 100644 test/functional/test_agent.py
diff --git a/test/functional/test_agent.py b/test/functional/test_agent.py
new file mode 100644
index 000000000..24593090b
--- /dev/null
+++ b/test/functional/test_agent.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""
+Tests for bluetoothctl using VM instances
+"""
+import sys
+import re
+import pytest
+import subprocess
+import tempfile
+
+import time
+import logging
+
+
+from pytest_bluezenv import host_config, Agent, wait_until
+
+pytestmark = [pytest.mark.vm]
+
+
+@host_config([Agent()], [Agent()])
+@pytest.mark.parametrize("success", [True, False], ids=["accept", "reject"])
+def test_agent_pair_bredr(hosts, success):
+ host0, host1 = hosts
+
+ host0.agent.adapter_method("StartDiscovery")
+ host0.agent.expect("org.bluez.Adapter1.StartDiscovery:reply")
+
+ host1.agent.adapter_set("Pairable", True)
+ host1.agent.adapter_set("Discoverable", True)
+
+ wait_until(host0.agent.has_device, host1.bdaddr)
+
+ host0.agent.device_method(host1.bdaddr, "Pair")
+
+ confirm_0 = host0.agent.expect("org.bluez.Agent1.RequestConfirmation")
+ confirm_1 = host1.agent.expect("org.bluez.Agent1.RequestConfirmation")
+ assert confirm_0.passkey == confirm_1.passkey
+ host0.agent.reply()
+
+ if success:
+ host1.agent.reply()
+ host0.agent.expect("org.bluez.Device1.Pair:reply")
+ else:
+ host1.agent.reply_error()
+ host0.agent.expect("org.bluez.Device1.Pair:error")
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 16/16] test: functional: add basic obex file transfer tests
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Add tests for Obex DBus API and obexctl
test/functional/test_obex.py::test_obex_ftp_get
test/functional/test_obex.py::test_obex_ftp_list
test/functional/test_obex.py::test_obexctl_list
---
test/functional/test_obex.py | 285 +++++++++++++++++++++++++++++++++++
1 file changed, 285 insertions(+)
create mode 100644 test/functional/test_obex.py
diff --git a/test/functional/test_obex.py b/test/functional/test_obex.py
new file mode 100644
index 000000000..fcb9105e5
--- /dev/null
+++ b/test/functional/test_obex.py
@@ -0,0 +1,285 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: LGPL-2.1-or-later
+"""
+Tests for Obex
+"""
+import sys
+import os
+import re
+import pytest
+import subprocess
+import tempfile
+import time
+import logging
+import json
+import dbus
+import threading
+from pathlib import Path
+
+import pytest
+
+from pytest_bluezenv import (
+ HostPlugin,
+ Agent,
+ host_config,
+ find_exe,
+ Bluetoothd,
+ Bluetoothctl,
+ Obexd,
+ LogStream,
+ wait_until,
+ mainloop_wrap,
+ mainloop_assert,
+ Event,
+ EventPluginMixin,
+ dbus_service_event_method,
+ Pexpect,
+ utils,
+)
+
+pytestmark = [pytest.mark.vm]
+
+log = logging.getLogger(__name__)
+
+
+BUS_NAME = "org.bluez.obex"
+PATH = "/org/bluez/obex"
+AGENT_MANAGER_INTERFACE = "org.bluez.obex.AgentManager1"
+AGENT_INTERFACE = "org.bluez.obex.Agent1"
+CLIENT_INTERFACE = "org.bluez.obex.Client1"
+SESSION_INTERFACE = "org.bluez.obex.Session1"
+FILE_TRANSFER_INTERFACE = "org.bluez.obex.FileTransfer1"
+TRANSFER_INTERFACE = "org.bluez.obex.Transfer1"
+
+FTP_UUID = "00001106-0000-1000-8000-00805f9b34fb"
+
+
+class ObexAgent(HostPlugin, EventPluginMixin):
+ depends = [Bluetoothd()]
+ name = "obex_agent"
+
+ def __init__(self, path="/obexagent"):
+ self.path = path
+
+ @mainloop_wrap
+ def setup(self, impl):
+ EventPluginMixin.setup(self, impl)
+
+ self.bus = dbus.SessionBus()
+ self.bus.set_exit_on_disconnect(False)
+
+ self.agent = ObexAgentObject(self.bus, self.path, self.events)
+
+ bluez = self.bus.get_object(BUS_NAME, PATH)
+ self.manager = dbus.Interface(bluez, AGENT_MANAGER_INTERFACE)
+ self.manager.RegisterAgent(self.path)
+
+ log.info("Obex agent registered")
+
+ def cleanup(self):
+ path = Path("/run/obex")
+ for f in path.iterdir():
+ f.unlink()
+
+
+def agent_method(*a, **kw):
+ return dbus_service_event_method(AGENT_INTERFACE, *a, **kw)
+
+
+class ObexAgentObject(dbus.service.Object):
+ @mainloop_assert
+ def __init__(self, bus, path, events):
+ self.events = events
+ super().__init__(bus, path)
+
+ AuthorizePush = agent_method("AuthorizePush", ("path",), "o", "s", sync=False)
+ Cancel = agent_method("Cancel")
+
+
+def write_obex_file(name, content):
+ with open(f"/run/obex/{name}", "w") as f:
+ f.write(content)
+
+
+def read_file(name):
+ with open(name, "r") as f:
+ return f.read()
+
+
+#
+# Direct Obex Python client API tests
+#
+
+
+class ObexClient(HostPlugin, EventPluginMixin):
+ name = "obex"
+
+ @mainloop_wrap
+ def setup(self, impl):
+ EventPluginMixin.setup(self, impl)
+
+ self.transferred = 0
+ self.transfer_path = None
+ self.transfer_size = 0
+
+ self.bus = dbus.SessionBus()
+ self.bus.set_exit_on_disconnect(False)
+ self.log = logging.getLogger(self.name)
+ self.client = dbus.Interface(
+ self.bus.get_object(BUS_NAME, PATH), CLIENT_INTERFACE
+ )
+
+ self.bus.add_signal_receiver(
+ self.properties_changed,
+ dbus_interface="org.freedesktop.DBus.Properties",
+ signal_name="PropertiesChanged",
+ path_keyword="path",
+ )
+
+ @mainloop_wrap
+ def connect(self, bdaddr):
+ def reply(path):
+ obj = self.bus.get_object(BUS_NAME, path)
+ self.session = dbus.Interface(obj, SESSION_INTERFACE)
+ self.ftp = dbus.Interface(obj, FILE_TRANSFER_INTERFACE)
+
+ self._object_method(
+ self.client, "CreateSession", bdaddr, {"Target": "ftp"}, reply_handler=reply
+ )
+
+ @mainloop_assert
+ def properties_changed(self, interface, properties, invalidated, path):
+ if path != self.transfer_path:
+ return
+
+ if "Status" in properties and (
+ properties["Status"] == "complete" or properties["Status"] == "error"
+ ):
+ self.events.put(
+ Event(
+ f"{FILE_TRANSFER_INTERFACE}:{properties['Status']}",
+ properties=properties,
+ )
+ )
+ self.log.debug(f"Transfer {properties['Status']}")
+
+ if "Transferred" not in properties:
+ return
+
+ value = properties["Transferred"]
+ speed = (value - self.transferred) / 1000
+ self.log.debug(
+ f"Transfer progress {value}/{self.transfer_size} at {speed} kBps"
+ )
+ self.transferred = value
+
+ @mainloop_wrap
+ def ftp_list_folder(self):
+ return self.ftp.ListFolder()
+
+ @mainloop_wrap
+ def ftp_get_file(self, dst, src):
+ path, properties = self.ftp.GetFile(dst, src)
+ self.transfer_path = path
+ self.transfer_size = properties["Size"]
+ return properties["Filename"]
+
+
+@pytest.fixture
+def paired_hosts(hosts):
+ from .test_agent import test_agent_pair_bredr
+
+ if hosts[0].agent.has_device(hosts[1].bdaddr):
+ return hosts
+
+ test_agent_pair_bredr(hosts, True)
+ return hosts
+
+
+obex_host_config = host_config(
+ [Agent(), Obexd(), ObexClient(), Pexpect()],
+ [Agent(), Obexd(), ObexAgent()],
+ reuse=True,
+)
+
+
+@pytest.fixture
+def obex_hosts(paired_hosts):
+ host0, host1 = paired_hosts
+
+ if hasattr(host0, "session"):
+ return paired_hosts
+
+ host0.obex.connect(host1.bdaddr)
+
+ service = host1.agent.expect("org.bluez.Agent1.AuthorizeService")
+ assert service.uuid == FTP_UUID
+ host1.agent.reply()
+
+ host0.obex.expect("org.bluez.obex.Client1.CreateSession:reply")
+
+ yield paired_hosts
+
+ host1.obex_agent.cleanup()
+
+
+@obex_host_config
+def test_obex_ftp_list(obex_hosts):
+ host0, host1 = obex_hosts
+
+ host1.call(write_obex_file, "test", "1234")
+
+ (item,) = host0.obex.ftp_list_folder()
+ assert item["Type"] == "file"
+ assert item["Name"] == "test"
+ assert item["Size"] == 4
+
+
+@obex_host_config
+def test_obex_ftp_get(obex_hosts):
+ host0, host1 = obex_hosts
+
+ host1.call(write_obex_file, "test", "1234")
+
+ filename = host0.obex.ftp_get_file("", "test")
+ host0.obex.expect("org.bluez.obex.FileTransfer1:complete")
+ assert host0.call(read_file, filename) == "1234"
+
+
+#
+# obexctl tests
+#
+
+
+@pytest.fixture
+def obexctl(obex_hosts):
+ host0, host1 = obex_hosts
+
+ exe = find_exe("tools", "obexctl")
+ obexctl = host0.pexpect.spawn([exe])
+
+ obexctl.expect("Client /org/bluez/obex")
+ obexctl.send(f"connect {host1.bdaddr} {FTP_UUID}\n")
+
+ service = host1.agent.expect("org.bluez.Agent1.AuthorizeService")
+ assert service.uuid == FTP_UUID
+ host1.agent.reply()
+
+ obexctl.expect("Connection successful")
+ obexctl.send(f"select /org/bluez/obex/client/session1\n")
+
+ yield obexctl
+
+ obexctl.close()
+
+
+@obex_host_config
+def test_obexctl_list(obex_hosts, obexctl):
+ host0, host1 = obex_hosts
+
+ host1.call(write_obex_file, "test", "1234")
+
+ obexctl.send(f"ls\n")
+ obexctl.expect(f"Type: file")
+ obexctl.expect(f"Name: test")
+ obexctl.expect(f"Size: 4")
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 14/16] test: functional: impose Python code formatting
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Check Python code formatting of the functional test suite.
---
test/functional/test_tests.py | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 test/functional/test_tests.py
diff --git a/test/functional/test_tests.py b/test/functional/test_tests.py
new file mode 100644
index 000000000..561b04703
--- /dev/null
+++ b/test/functional/test_tests.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""
+Tests for the test suite itself
+"""
+import sys
+import subprocess
+import warnings
+from pathlib import Path
+
+import pytest
+
+
+def test_formatting():
+ pytest.importorskip("black")
+
+ result = subprocess.run(
+ [sys.executable, "-mblack", "--check", "--diff", "-q", Path(__file__).parent],
+ stdout=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ if result.returncode != 0:
+ warnings.warn(f"Formatting incorrect:\n{result.stdout}")
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 13/16] build: add functional testing target
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
This adds check-functional: target that runs the functional test suite.
Also add a --enable-functional-testing=<kernel-image> argument for
configure that can be used to include it in the check: make target,
possibly with a predefined kernel image.
---
Makefile.am | 10 ++++++++++
configure.ac | 22 ++++++++++++++++++++++
2 files changed, 32 insertions(+)
diff --git a/Makefile.am b/Makefile.am
index 76c4ab5d4..7920cae68 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -812,6 +812,16 @@ endif
TESTS = $(unit_tests)
AM_TESTS_ENVIRONMENT = MALLOC_CHECK_=3 MALLOC_PERTURB_=69
+check-functional: all
+ python3 -m pytest "$(srcdir)/test/functional" -v \
+ --kernel="$(FUNCTIONAL_TESTING_KERNEL)" \
+ --bluez-build-dir="$(top_builddir)" \
+ --bluez-src-dir="$(srcdir)"
+
+if FUNCTIONAL_TESTING
+check: check-functional
+endif
+
if DBUS_RUN_SESSION
AM_TESTS_ENVIRONMENT += dbus-run-session --
endif
diff --git a/configure.ac b/configure.ac
index 52de7d665..254d90318 100644
--- a/configure.ac
+++ b/configure.ac
@@ -405,6 +405,28 @@ if (test "${enable_testing}" = "yes"); then
#include <linux/net_tstamp.h>]])
fi
+AC_ARG_ENABLE(functional-testing, AS_HELP_STRING([--enable-functional-testing],
+ [enable functional testing tools]),
+ [enable_functional_testing=yes; functional_testing_kernel=${enableval}],
+ [enable_functional_testing=no])
+AM_CONDITIONAL(FUNCTIONAL_TESTING, test "${enable_functional_testing}" = "yes")
+AC_ARG_VAR(FUNCTIONAL_TESTING_KERNEL, [vmlinux image to use for functional testing])
+FUNCTIONAL_TESTING_KERNEL=${functional_testing_kernel}
+
+if (test "${enable_functional_testing}" = "yes"); then
+ if (test "${enable_client}" = "no" || \
+ test "${enable_tools}" != "yes" || \
+ test "${enable_testing}" != "yes"); then
+ AC_MSG_ERROR([--enable-functional-testing requires --enable-client --enable-tools --enable-testing])
+ fi
+ AC_MSG_CHECKING([pytest and dependencies])
+ python3 -m pip install --dry-run --no-index -r "${srcdir}/test/functional/requirements.txt" >/dev/null
+ if (test "$?" != "0"); then
+ AC_MSG_ERROR([pytest or dependencies missing])
+ fi
+ AC_MSG_RESULT([ok])
+fi
+
AC_ARG_ENABLE(experimental, AS_HELP_STRING([--enable-experimental],
[enable experimental tools]),
[enable_experimental=${enableval}])
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 12/16] test: add functional/integration testing framework
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Add framework for writing tests simulating "real" environments where
BlueZ and other parts of the stack run on different virtual machine
hosts that communicate with each other.
Add some smoke tests for bluetoothctl and btmgmt.
The implementation for the VM setup is maintained separately in the
pytest-bluezenv plugin, https://pypi.org/project/pytest-bluezenv
Implements:
- RPC communication with tester instances running each of the VM hosts,
so that tests can be written on the parent host which coordinates the
execution.
- Extensible way to add stateful test-specific code inside the VM
instances
- Logging control: output from different processes running inside the VM
are separated and can be filtered.
- Test runner framework with Pytest, factored into a pytest plugin
- Grouping tests to minimize VM reboots
- Redirecting USB controllers to use for testing
There is no requirement that the tests spawn VM instances.
---
test/functional/__init__.py | 2 +
test/functional/conftest.py | 48 ++++++++
test/functional/requirements.txt | 2 +
test/functional/test_bluetoothctl_vm.py | 152 ++++++++++++++++++++++++
test/functional/test_btmgmt_vm.py | 30 +++++
test/pytest.ini | 17 +++
test/test-functional | 21 ++++
test/test-functional-attach | 7 ++
8 files changed, 279 insertions(+)
create mode 100644 test/functional/__init__.py
create mode 100644 test/functional/conftest.py
create mode 100644 test/functional/requirements.txt
create mode 100644 test/functional/test_bluetoothctl_vm.py
create mode 100644 test/functional/test_btmgmt_vm.py
create mode 100644 test/pytest.ini
create mode 100755 test/test-functional
create mode 100755 test/test-functional-attach
diff --git a/test/functional/__init__.py b/test/functional/__init__.py
new file mode 100644
index 000000000..fe1c85178
--- /dev/null
+++ b/test/functional/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
diff --git a/test/functional/conftest.py b/test/functional/conftest.py
new file mode 100644
index 000000000..196afa08d
--- /dev/null
+++ b/test/functional/conftest.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+import os
+import re
+from pathlib import Path
+
+
+def pytest_addoption(parser):
+ parser.addoption(
+ "--list",
+ action="store_true",
+ default=None,
+ help=("List tests"),
+ )
+
+
+def pytest_configure(config):
+ if config.option.list:
+ config.option.reportchars = "A"
+ config.option.no_header = True
+ config.option.verbose = -2
+
+
+COLLECT_ERRORS = []
+
+
+def pytest_collectreport(report):
+ if report.outcome != "passed":
+ COLLECT_ERRORS.append((report.outcome, report.fspath))
+
+
+def pytest_collection_finish(session):
+ if session.config.option.list:
+ cwd = Path(".").resolve()
+ root = session.config.rootpath.absolute()
+
+ regex = re.compile(r"\[.*")
+ names = set(
+ (root.joinpath(item.location[0]), regex.sub("", item.location[2]))
+ for item in session.items
+ )
+
+ for path, name in sorted(names):
+ print(f"{path.resolve().relative_to(cwd, walk_up=True)}::{name}")
+ for outcome, name in COLLECT_ERRORS:
+ print(f"{outcome.upper()} {name}")
+ print()
+ os._exit(0)
diff --git a/test/functional/requirements.txt b/test/functional/requirements.txt
new file mode 100644
index 000000000..98b0c31d4
--- /dev/null
+++ b/test/functional/requirements.txt
@@ -0,0 +1,2 @@
+pytest>=8
+pytest-bluezenv==0.1.5
diff --git a/test/functional/test_bluetoothctl_vm.py b/test/functional/test_bluetoothctl_vm.py
new file mode 100644
index 000000000..0ba75e9a9
--- /dev/null
+++ b/test/functional/test_bluetoothctl_vm.py
@@ -0,0 +1,152 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""
+Tests for bluetoothctl using VM instances
+"""
+import sys
+import re
+import pytest
+import subprocess
+import tempfile
+import warnings
+
+import time
+import logging
+
+
+from pytest_bluezenv import host_config, find_exe, run, Bluetoothd, Bluetoothctl
+
+pytestmark = [pytest.mark.vm]
+
+bluetoothctl = find_exe("client", "bluetoothctl")
+
+bluetoothd_reuse_config = host_config([Bluetoothd()], reuse=True)
+
+
+@host_config(
+ [Bluetoothctl()],
+ [Bluetoothctl()],
+)
+def test_bluetoothctl_pair_bredr(hosts):
+ host0, host1 = hosts
+
+ host0.bluetoothctl.send("scan on\n")
+ host0.bluetoothctl.expect(f"Controller {host0.bdaddr.upper()} Discovering: yes")
+
+ host1.bluetoothctl.send("pairable on\n")
+ host1.bluetoothctl.expect("Changing pairable on succeeded")
+ host1.bluetoothctl.send("discoverable on\n")
+ host1.bluetoothctl.expect(f"Controller {host1.bdaddr.upper()} Discoverable: yes")
+
+ host0.bluetoothctl.expect(f"Device {host1.bdaddr.upper()}")
+ host0.bluetoothctl.send(f"pair {host1.bdaddr}\n")
+
+ idx, m = host0.bluetoothctl.expect(r"Confirm passkey (\d+).*:")
+ key = m[0].decode("utf-8")
+
+ host1.bluetoothctl.expect(f"Confirm passkey {key}")
+
+ host0.bluetoothctl.send("yes\n")
+ host1.bluetoothctl.send("yes\n")
+
+ host0.bluetoothctl.expect("Pairing successful")
+
+
+@host_config(
+ [Bluetoothd(conf="[General]\nControllerMode = le\n"), Bluetoothctl()],
+ [Bluetoothd(conf="[General]\nControllerMode = le\n"), Bluetoothctl()],
+)
+def test_bluetoothctl_pair_le(hosts):
+ host0, host1 = hosts
+
+ host0.bluetoothctl.send("scan on\n")
+ host0.bluetoothctl.expect(f"Controller {host0.bdaddr.upper()} Discovering: yes")
+
+ host1.bluetoothctl.send("advertise on\n")
+ host1.bluetoothctl.expect("Advertising object registered")
+
+ host0.bluetoothctl.expect(f"Device {host1.bdaddr.upper()}")
+ host0.bluetoothctl.send(f"pair {host1.bdaddr.upper()}\n")
+
+ # BUG!: if controller is power cycled off/on at boot (before bluetoothd)
+ # BUG!: which is what the tester here does,
+ # BUG!: bluetoothd MGMT command to enable Secure Connections Host Support
+ # BUG!: fails and we are left with legacy passkey. It seems we get randomly
+ # BUG!: one of these depending on what state controller/kernel were before
+ # BUG!: btmgmt power off/on
+
+ idx, m = host0.bluetoothctl.expect(
+ [r"\[agent\].*Passkey:.*m(\d+)", r"Confirm passkey (\d+).*:"]
+ )
+ key = m[0].decode("utf-8")
+
+ if idx == 0:
+ warnings.warn(
+ "BUG: we got passkey authentication, bluetoothd/kernel should be fixed"
+ )
+ host1.bluetoothctl.expect(r"\[agent\] Enter passkey \(number in 0-999999\):")
+ host1.bluetoothctl.send(f"{key}\n")
+ else:
+ host1.bluetoothctl.expect(f"Confirm passkey {key}")
+
+ host0.bluetoothctl.send("yes\n")
+ host1.bluetoothctl.send("yes\n")
+
+ host0.bluetoothctl.expect("Pairing successful")
+
+
+def run_bluetoothctl(*args):
+ return run(
+ [bluetoothctl] + list(args),
+ stdout=subprocess.PIPE,
+ stdin=subprocess.DEVNULL,
+ encoding="utf-8",
+ )
+
+
+def run_bluetoothctl_script(script):
+ with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as f:
+ f.write(script)
+ f.write("\nquit")
+ f.flush()
+ return run_bluetoothctl("--init-script", f.name)
+
+
+@bluetoothd_reuse_config
+def test_bluetoothctl_show(hosts):
+ (host,) = hosts
+
+ result = host.call(run_bluetoothctl, f"show")
+ assert result.returncode == 0
+ assert f"Controller {host.bdaddr.upper()}" in result.stdout
+ assert "Powered: " in result.stdout
+ assert "Discoverable: no" in result.stdout
+
+
+@bluetoothd_reuse_config
+def test_bluetoothctl_list(hosts):
+ (host,) = hosts
+
+ result = host.call(run_bluetoothctl, "list")
+ assert result.returncode == 0
+ assert re.search(rf"{host.bdaddr.upper()}.*\[default\]", result.stdout)
+
+
+@bluetoothd_reuse_config
+def test_bluetoothctl_script_show(hosts):
+ (host,) = hosts
+
+ result = host.call(run_bluetoothctl_script, f"show")
+ assert result.returncode == 0
+ assert f"Controller {host.bdaddr.upper()}" in result.stdout
+ assert "Powered: " in result.stdout
+ assert "Discoverable: no" in result.stdout
+
+
+@bluetoothd_reuse_config
+def test_bluetoothctl_script_list(hosts):
+ (host,) = hosts
+
+ result = host.call(run_bluetoothctl_script, f"list")
+ assert result.returncode == 0
+ assert re.search(rf"{host.bdaddr.upper()}.*\[default\]", result.stdout)
diff --git a/test/functional/test_btmgmt_vm.py b/test/functional/test_btmgmt_vm.py
new file mode 100644
index 000000000..3d11d7616
--- /dev/null
+++ b/test/functional/test_btmgmt_vm.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""
+Tests for btmgmt using VM instances
+"""
+import sys
+import pytest
+import subprocess
+import tempfile
+
+from pytest_bluezenv import host_config, find_exe, run
+
+pytestmark = [pytest.mark.vm]
+
+btmgmt = find_exe("tools", "btmgmt")
+
+
+@host_config([])
+def test_btmgmt_info(hosts):
+ (host,) = hosts
+
+ result = host.call(
+ run,
+ [btmgmt, "--index", "0", "info"],
+ stdout=subprocess.PIPE,
+ stdin=subprocess.DEVNULL,
+ encoding="utf-8",
+ )
+ assert result.returncode == 0
+ assert f"addr {host.bdaddr.upper()}" in result.stdout
diff --git a/test/pytest.ini b/test/pytest.ini
new file mode 100644
index 000000000..0d4e48d69
--- /dev/null
+++ b/test/pytest.ini
@@ -0,0 +1,17 @@
+[pytest]
+log_format = %(asctime)s %(levelname)-6s %(name)-20s: %(message)s
+log_date_format = %Y-%m-%d %H:%M:%S.%f
+log_level = 0
+log_file = test-functional.log
+markers =
+ vm: tests requiring VM image
+
+addopts =
+ -p pytest_bluezenv
+
+# Default timeout
+vm_timeout = 30
+
+# Default sources for kernel-build when requested
+kernel_upstream = https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git/
+kernel_branch = master
diff --git a/test/test-functional b/test/test-functional
new file mode 100755
index 000000000..9f90207b2
--- /dev/null
+++ b/test/test-functional
@@ -0,0 +1,21 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# See doc/test-functional.rst
+#
+TESTDIR="$(dirname "$0")"
+SRCDIR="$TESTDIR/.."
+
+BUILDDIR=
+for d in "$SRCDIR" "$SRCDIR/build" "$SRCDIR/builddir"; do
+ if [ -f "$d/src/bluetoothd" ]; then
+ BUILDDIR="$d"
+ break
+ fi
+done
+
+if [ -n "$BUILDDIR" -a -d "$BUILDDIR" ]; then
+ exec python3 -m pytest "$TESTDIR/functional" --bluez-src-dir "$SRCDIR" --bluez-build-dir "$BUILDDIR" "$@"
+else
+ exec python3 -m pytest "$TESTDIR/functional" --bluez-src-dir "$SRCDIR" "$@"
+fi
diff --git a/test/test-functional-attach b/test/test-functional-attach
new file mode 100755
index 000000000..6e65464f7
--- /dev/null
+++ b/test/test-functional-attach
@@ -0,0 +1,7 @@
+#!/bin/sh
+#
+# test-functional-attach
+#
+# Start Tmux and connect to active test-functional VM hosts.
+#
+exec python3 -mpytest_bluezenv attach "$@"
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 11/16] doc: add functional/integration testing documentation
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Add documentation for functional/integration test suite.
---
doc/test-functional.rst | 299 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 299 insertions(+)
create mode 100644 doc/test-functional.rst
diff --git a/doc/test-functional.rst b/doc/test-functional.rst
new file mode 100644
index 000000000..bda7366e0
--- /dev/null
+++ b/doc/test-functional.rst
@@ -0,0 +1,299 @@
+===============
+test-functional
+===============
+
+**test-functional** [*OPTIONS*]
+
+DESCRIPTION
+===========
+
+**test-functional(1)** is used for functional testing of BlueZ and
+kernel using multiple virtual machine environments, connected by real
+or virtual controllers.
+
+It uses https://pypi.org/project/pytest-bluezenv as VM-based test
+framework. For details, see its documentation.
+
+QUICK EXAMPLE
+=============
+
+Install `qemu-system-x86_64` first. Then,
+
+.. code-block::
+
+ $ ./configure --enable-functional-testing --enable-testing --enable-tools
+ $ make -j8
+ $ python3 -mpip install -r test/functional/requirements.txt
+ $ test/test-functional --kernel-build -v
+
+OPTIONS
+=======
+
+The `test-functional` script simply runs `Pytest
+<https://pytest.org>`__ which can take the following options:
+https://docs.pytest.org/en/stable/reference/reference.html#command-line-flags
+
+The following additional options apply:
+
+:--kernel=<image>: Kernel image (or built Linux source tree root) to
+ use. See **test-runner(1)** and `tester.config` for required
+ kernel config.
+
+ If not provided, value from `FUNCTIONAL_TESTING_KERNEL`
+ environment variable is used. If none, no image is used.
+
+:--usb=hci0,hci1: USB controllers to use in tests that require use of
+ real controllers.
+
+ If not provided, value from `FUNCTIONAL_TESTING_CONTROLLERS`
+ environment variable is used. If none, all USB controllers
+ with suitable permissions are considered.
+
+:--btmon: Launch btmon on all hosts to log events, and dump traffic to
+ test-functional-\*.btsnoop
+
+:--force-usb: Force tests to use USB controllers instead of `btvirt`.
+
+:--vm-timeout=<seconds>: Specify timeout for communication with VM hosts.
+
+:--log-filter=[+-]<pattern>,[+-]<pattern>,...: Allow/deny lists
+ for filtering logging output. The pattern is a shell glob matching
+ to the logger names.
+
+:--build-dir=<path>: Path to build directory where to search for BlueZ
+ executables.
+
+:--list: Output brief lists of existing tests.
+
+:--kernel-build=no/use/auto/force: Build a suitable kernel image from source.
+
+:--kernel-upstream=<GIT_URL>: URL for Git clone of kernel sources.
+
+:--kernel-branch=<GIT_BRANCH>: Git branch to build from.
+
+Tests that require kernel image or USB controllers are skipped if none
+are available. Normally, tests use `btvirt`.
+
+VM instances share a directory ``/run/shared`` with host machine,
+located on host usually in ``/tmp/bluez-func-test-*/shared-*``. Core
+dumps etc. are copied out from it before test instance is shut down.
+
+REQUIREMENTS
+============
+
+General
+-------
+
+The following are needed:
+
+- QEmu (x86_64)
+- ``dbus-daemon`` available
+
+Recommended:
+
+- KVM-enabled x86_64 host system
+- Preferably built BlueZ source tree
+- ``chronyd`` available
+- ``util-linux`` tools available
+- ``agetty`` available
+
+Python
+------
+
+The following Python packages are required:
+
+.. code-block::
+
+ pytest>=8
+ pytest-bluezenv==0.1.5
+
+To install them via pip::
+
+ python3 -m pip install -r test/functional/requirements.txt
+
+On Fedora / RHEL, the dependencies aside from `pytest-bluezenv` can be
+installed via::
+
+ sudo dnf install python3-pytest python3-pexpect python3-dbus
+
+Kernel
+------
+
+The **test-functional(1)** tool requires a kernel image with similar
+config as **test-runner(1)**. If given `--kernel-build` option, a
+suitable image is built from sources downloaded under
+`test/.pytest_cache`.
+
+Simplest setup is
+
+.. code-block::
+
+ cp ../bluez/doc/tester.config .config
+ make olddefconfig
+ make -j8
+
+To get log timestamps right, the kernel should have the following
+configuration enabled:
+
+.. code-block::
+
+ CONFIG_HYPERVISOR_GUEST=y
+ CONFIG_PARAVIRT=y
+ CONFIG_KVM_GUEST=y
+
+ CONFIG_PTP_1588_CLOCK=y
+ CONFIG_PTP_1588_CLOCK_KVM=y
+ CONFIG_PTP_1588_CLOCK_VMCLOCK=y
+
+USB
+---
+
+Some tests may require a hardware controller instead of the virtual `btvirt` one.
+
+EXAMPLES
+========
+
+Run all tests
+-------------
+
+.. code-block::
+
+ $ test/test-functional --kernel=/pathto/bzImage
+
+ $ export FUNCTIONAL_TESTING_KERNEL=/pathto/bzImage
+ $ test/test-functional
+
+Show output during run
+----------------------
+
+.. code-block::
+
+ $ test/test-functional --log-cli-level=0
+
+Show only specific loggers:
+
+.. code-block::
+
+ $ test/test-functional --log-cli-level=0 --log-filter=rpc,host
+
+ $ test/test-functional --log-cli-level=0 --log-filter=*.bluetoothctl
+
+Filter out loggers:
+
+.. code-block::
+
+ $ test/test-functional --log-cli-level=0 --log-filter=-host
+
+ $ test/test-functional --log-cli-level=0 --log-filter=host,-host.*.1
+
+Run selected tests
+------------------
+
+.. code-block::
+
+ $ test/test-functional test/functional/test_cli_simple.py::test_bluetoothctl_script_show
+
+ $ test/test-functional -k test_bluetoothctl_script_show
+
+ $ test/test-functional -k 'test_btmgmt or test_bluetoothctl'
+
+Don't run tests with a given marker:
+
+.. code-block::
+
+ $ test/test-functional -m "not pipewire"
+
+Don't run known-failing tests:
+
+.. code-block::
+
+ $ test/test-functional -m "not xfail"
+
+Note that otherwise known-failing tests would be run, but with
+failures suppressed.
+
+Run previously failed and stop on failure
+-----------------------------------------
+
+.. code-block::
+
+ $ test/test-functional -x --ff
+
+List all tests
+--------------
+
+.. code-block::
+
+ $ test/test-functional --list
+
+Show errors from know-failing test
+----------------------------------
+
+.. code-block::
+
+ $ test/test-functional --runxfail -k test_btmgmt_info
+
+Redirect USB devices
+--------------------
+
+.. code-block::
+
+ $ test/test-functional --usb=hci0,hci1
+
+ $ export FUNCTIONAL_TESTING_CONTROLLERS=hci0,hci1
+ $ test/test-functional -vv
+
+This does not require running as root. Changing device permissions is
+sufficient. In verbose mode (``-vv``) some instructions are printed.
+
+Run all tests using the USB controllers:
+
+.. code-block::
+
+ $ test/test-functional --usb=hci0,hci1 --force-usb
+
+Run tests in parallel
+---------------------
+
+pytest-xdist is required for parallel execution. To run:
+
+.. code-block::
+
+ $ test/test-functional -n auto
+
+To reduce VM setup/teardowns:
+
+.. code-block::
+
+ $ test/test-functional -n auto --dist loadgroup
+
+Logging in to a test VM instance
+--------------------------------
+
+While test is running:
+
+.. code-block::
+
+ $ test/test-functional-attach
+
+For this to be useful, usually, you need to pause the test
+e.g. by running with ``--trace`` option.
+
+To do it manually, when starting the tester will log a line like::
+
+ TTY: socat /tmp/bluez-func-test-q658swgi/bluez-func-test-tty-0 STDIO,rawer
+
+with the location of the socket where the serial is connected to.
+
+WRITING TESTS
+=============
+
+The functional tests are written in files (test modules) names
+`test/functional/test_*.py`. They are written using standard Pytest
+style. See https://docs.pytest.org/en/stable/getting-started.html
+
+See https://pypi.org/project/pytest-bluezenv/ for documentation of
+how to write VM-using tests.
+
+Use `Black <https://black.readthedocs.io/en/stable/>`__ to autoformat
+Python test code.
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 10/16] doc: enable KVM paravirtualization & clock support in tester kernel config
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Enable KVM guest and PTP options in tester kernel config.
This allows synchronizing tester VM guest with host clock, needed for
testers that want to compare timestamps outside the VM guest.
---
doc/ci.config | 8 ++++++++
doc/test-runner.rst | 16 ++++++++++++++++
doc/tester.config | 8 ++++++++
3 files changed, 32 insertions(+)
diff --git a/doc/ci.config b/doc/ci.config
index a48c1af9d..bb3cb221f 100644
--- a/doc/ci.config
+++ b/doc/ci.config
@@ -8,6 +8,14 @@ CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_CONSOLE=y
+CONFIG_HYPERVISOR_GUEST=y
+CONFIG_PARAVIRT=y
+CONFIG_KVM_GUEST=y
+
+CONFIG_PTP_1588_CLOCK=y
+CONFIG_PTP_1588_CLOCK_KVM=y
+CONFIG_PTP_1588_CLOCK_VMCLOCK=y
+
CONFIG_NET=y
CONFIG_INET=y
diff --git a/doc/test-runner.rst b/doc/test-runner.rst
index d030787a4..60f18683c 100644
--- a/doc/test-runner.rst
+++ b/doc/test-runner.rst
@@ -122,6 +122,22 @@ options may be useful:
CONFIG_DEBUG_MUTEXES=y
CONFIG_KASAN=y
+Other
+-----
+
+For tests requiring accurate time inside the VM, possible with KVM:
+
+.. code-block::
+
+ CONFIG_HYPERVISOR_GUEST=y
+ CONFIG_PARAVIRT=y
+ CONFIG_KVM_GUEST=y
+
+ CONFIG_PTP_1588_CLOCK=y
+ CONFIG_PTP_1588_CLOCK_KVM=y
+ CONFIG_PTP_1588_CLOCK_VMCLOCK=y
+
+
EXAMPLES
========
diff --git a/doc/tester.config b/doc/tester.config
index 015e7cc1a..0cf5e2723 100644
--- a/doc/tester.config
+++ b/doc/tester.config
@@ -3,6 +3,14 @@ CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_CONSOLE=y
+CONFIG_HYPERVISOR_GUEST=y
+CONFIG_PARAVIRT=y
+CONFIG_KVM_GUEST=y
+
+CONFIG_PTP_1588_CLOCK=y
+CONFIG_PTP_1588_CLOCK_KVM=y
+CONFIG_PTP_1588_CLOCK_VMCLOCK=y
+
CONFIG_NET=y
CONFIG_INET=y
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 09/16] doc: enable CONFIG_VIRTIO_CONSOLE in tester config
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Enable kernel option that allows using -device virtserialport in qemu.
This is easier to make work reliably than pci-serial channel.
---
doc/ci.config | 1 +
doc/test-runner.rst | 1 +
doc/tester.config | 1 +
3 files changed, 3 insertions(+)
diff --git a/doc/ci.config b/doc/ci.config
index 31e49ba96..a48c1af9d 100644
--- a/doc/ci.config
+++ b/doc/ci.config
@@ -6,6 +6,7 @@
CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
+CONFIG_VIRTIO_CONSOLE=y
CONFIG_NET=y
CONFIG_INET=y
diff --git a/doc/test-runner.rst b/doc/test-runner.rst
index 64715e2e7..d030787a4 100644
--- a/doc/test-runner.rst
+++ b/doc/test-runner.rst
@@ -45,6 +45,7 @@ option (like the Bluetooth subsystem) can be enabled on top of this.
CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
+ CONFIG_VIRTIO_CONSOLE=y
CONFIG_NET=y
CONFIG_INET=y
diff --git a/doc/tester.config b/doc/tester.config
index 4ee306405..015e7cc1a 100644
--- a/doc/tester.config
+++ b/doc/tester.config
@@ -1,6 +1,7 @@
CONFIG_PCI=y
CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
+CONFIG_VIRTIO_CONSOLE=y
CONFIG_NET=y
CONFIG_INET=y
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 05/16] test-runner: enable path argument for --unix
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Allow specifying the path for the controller socket to be used.
---
tools/test-runner.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/tools/test-runner.c b/tools/test-runner.c
index 48b7c1589..331cb6eb1 100644
--- a/tools/test-runner.c
+++ b/tools/test-runner.c
@@ -54,6 +54,7 @@ static bool start_monitor = false;
static bool qemu_host_cpu = false;
static int num_devs = 0;
static int num_emulator = 0;
+static const char *device_path = "/tmp/bt-server-bredr";
static const char *qemu_binary = NULL;
static const char *kernel_image = NULL;
static char *audio_server;
@@ -313,11 +314,10 @@ static void start_qemu(void)
argv[pos++] = (char *) cmdline;
for (i = 0; i < num_devs; i++) {
- const char *path = "/tmp/bt-server-bredr";
char *chrdev, *serdev;
- chrdev = alloca(48 + strlen(path));
- sprintf(chrdev, "socket,path=%s,id=bt%d", path, i);
+ chrdev = alloca(48 + strlen(device_path));
+ sprintf(chrdev, "socket,path=%s,id=bt%d", device_path, i);
serdev = alloca(48);
sprintf(serdev, "pci-serial,chardev=bt%d", i);
@@ -1198,7 +1198,7 @@ static void usage(void)
"\t-m, --monitor Start btmon\n"
"\t-l, --emulator[=num] Start btvirt\n"
"\t-A, --audio[=path] Start audio server\n"
- "\t-u, --unix [path] Provide serial device\n"
+ "\t-u, --unix[=path] Provide serial device\n"
"\t-U, --usb [qemu_args] Provide USB device\n"
"\t-q, --qemu <path> QEMU binary\n"
"\t-H, --qemu-host-cpu Use host CPU (requires KVM support)\n"
@@ -1211,7 +1211,7 @@ static const struct option main_options[] = {
{ "auto", no_argument, NULL, 'a' },
{ "dbus", no_argument, NULL, 'b' },
{ "dbus-session", no_argument, NULL, 's' },
- { "unix", no_argument, NULL, 'u' },
+ { "unix", optional_argument, NULL, 'u' },
{ "daemon", no_argument, NULL, 'd' },
{ "emulator", no_argument, NULL, 'l' },
{ "monitor", no_argument, NULL, 'm' },
@@ -1239,7 +1239,7 @@ int main(int argc, char *argv[])
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "aubdsl::mq:Hk:A::U:vh",
+ opt = getopt_long(argc, argv, "au::bdsl::mq:Hk:A::U:vh",
main_options, NULL);
if (opt < 0)
break;
@@ -1250,6 +1250,8 @@ int main(int argc, char *argv[])
break;
case 'u':
num_devs = 1;
+ if (optarg)
+ device_path = optarg;
break;
case 'b':
start_dbus = true;
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 06/16] test-runner: Add -o/--option option
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Allow passing arbitrary arguments to QEMU.
---
tools/test-runner.c | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/tools/test-runner.c b/tools/test-runner.c
index 331cb6eb1..3de3a9d74 100644
--- a/tools/test-runner.c
+++ b/tools/test-runner.c
@@ -41,6 +41,7 @@
#endif
#define CMDLINE_MAX (2048 * 10)
+#define EXTRA_OPT_MAX 64
static const char *own_binary;
static char **test_argv;
@@ -59,6 +60,8 @@ static const char *qemu_binary = NULL;
static const char *kernel_image = NULL;
static char *audio_server;
static char *usb_dev;
+static char *extra_opts[EXTRA_OPT_MAX];
+static int num_extra_opts;
static const char *qemu_table[] = {
"qemu-system-x86_64",
@@ -291,7 +294,8 @@ static void start_qemu(void)
argv = alloca(sizeof(qemu_argv) +
(sizeof(char *) * (6 + (num_devs * 4))) +
- (sizeof(char *) * (usb_dev ? 4 : 0)));
+ (sizeof(char *) * (usb_dev ? 4 : 0)) +
+ (sizeof(char *) * num_extra_opts));
memcpy(argv, qemu_argv, sizeof(qemu_argv));
pos = (sizeof(qemu_argv) / sizeof(char *)) - 1;
@@ -335,6 +339,9 @@ static void start_qemu(void)
argv[pos++] = usb_dev;
}
+ for (i = 0; i < num_extra_opts; ++i)
+ argv[pos++] = extra_opts[i];
+
argv[pos] = NULL;
execve(argv[0], argv, qemu_envp);
@@ -1199,10 +1206,11 @@ static void usage(void)
"\t-l, --emulator[=num] Start btvirt\n"
"\t-A, --audio[=path] Start audio server\n"
"\t-u, --unix[=path] Provide serial device\n"
- "\t-U, --usb [qemu_args] Provide USB device\n"
+ "\t-U, --usb <qemu_args> Provide USB device\n"
"\t-q, --qemu <path> QEMU binary\n"
"\t-H, --qemu-host-cpu Use host CPU (requires KVM support)\n"
"\t-k, --kernel <image> Kernel image (bzImage)\n"
+ "\t-o, --option <opt> Additional argument passed to QEMU\n"
"\t-h, --help Show help options\n");
}
@@ -1220,6 +1228,7 @@ static const struct option main_options[] = {
{ "kernel", required_argument, NULL, 'k' },
{ "audio", optional_argument, NULL, 'A' },
{ "usb", required_argument, NULL, 'U' },
+ { "option", required_argument, NULL, 'o' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ }
@@ -1239,7 +1248,7 @@ int main(int argc, char *argv[])
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "au::bdsl::mq:Hk:A::U:vh",
+ opt = getopt_long(argc, argv, "au::bdsl::mq:Hk:A::U:o:vh",
main_options, NULL);
if (opt < 0)
break;
@@ -1284,6 +1293,13 @@ int main(int argc, char *argv[])
case 'U':
usb_dev = optarg;
break;
+ case 'o':
+ if (num_extra_opts >= EXTRA_OPT_MAX) {
+ fprintf(stderr, "Too many -o\n");
+ return EXIT_FAILURE;
+ }
+ extra_opts[num_extra_opts++] = optarg;
+ break;
case 'v':
printf("%s\n", VERSION);
return EXIT_SUCCESS;
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 07/16] test-runner: allow source tree root for -k
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Allow passing source tree root for -k option, look up kernel below it.
---
tools/test-runner.c | 42 ++++++++++++++++++++++++++++--------------
1 file changed, 28 insertions(+), 14 deletions(-)
diff --git a/tools/test-runner.c b/tools/test-runner.c
index 3de3a9d74..b3e0b0cfe 100644
--- a/tools/test-runner.c
+++ b/tools/test-runner.c
@@ -93,18 +93,31 @@ static const char *kernel_table[] = {
NULL
};
-static const char *find_kernel(void)
+static bool find_kernel(const char *root, char path[PATH_MAX])
{
+ struct stat st;
int i;
- for (i = 0; kernel_table[i]; i++) {
- struct stat st;
-
- if (!stat(kernel_table[i], &st))
- return kernel_table[i];
+ if (root) {
+ snprintf(path, PATH_MAX, "%s", root);
+ if (stat(path, &st))
+ return false;
+ if (!(st.st_mode & S_IFDIR))
+ return true;
}
- return NULL;
+ for (i = 0; kernel_table[i]; i++) {
+ if (root)
+ snprintf(path, PATH_MAX, "%s/%s", root,
+ kernel_table[i]);
+ else
+ snprintf(path, PATH_MAX, "%s",
+ kernel_table[i]);
+ if (!stat(path, &st))
+ return true;
+ }
+
+ return false;
}
static const struct {
@@ -1209,7 +1222,7 @@ static void usage(void)
"\t-U, --usb <qemu_args> Provide USB device\n"
"\t-q, --qemu <path> QEMU binary\n"
"\t-H, --qemu-host-cpu Use host CPU (requires KVM support)\n"
- "\t-k, --kernel <image> Kernel image (bzImage)\n"
+ "\t-k, --kernel <image> Kernel bzImage or source tree path\n"
"\t-o, --option <opt> Additional argument passed to QEMU\n"
"\t-h, --help Show help options\n");
}
@@ -1236,6 +1249,8 @@ static const struct option main_options[] = {
int main(int argc, char *argv[])
{
+ char kernel_path[PATH_MAX];
+
if (getpid() == 1 && getppid() == 0) {
prepare_sandbox();
run_tests();
@@ -1335,14 +1350,13 @@ int main(int argc, char *argv[])
}
}
- if (!kernel_image) {
- kernel_image = find_kernel();
- if (!kernel_image) {
- fprintf(stderr, "No default kernel image found\n");
- return EXIT_FAILURE;
- }
+ if (!find_kernel(kernel_image, kernel_path)) {
+ fprintf(stderr, "No kernel image found\n");
+ return EXIT_FAILURE;
}
+ kernel_image = kernel_path;
+
printf("Using QEMU binary %s\n", qemu_binary);
printf("Using kernel image %s\n", kernel_image);
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 08/16] test-runner: use virtio-serial for implementing -u device forwarding
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Using pci-serial to forward eg. btvirt sockets is unreliable, as qemu or
kernel seems to be sometimes dropping part of the sent data or insert
spurious \0 bytes, leading to sporadic errors like:
kernel: Bluetooth: hci0: command 0x0c52 tx timeout
kernel: Bluetooth: hci0: Opcode 0x0c52 failed: -110
btvirt: packet error, unknown type: 0
This appears to occur most often when host system is under load, e.g.
due to multiple test-runners running at the same time. The problem is
not specific to btvirt, but seems to be in the qemu serial device layer
vs. kernel interaction.
Change test-runner to use virtserialport to forward the btvirt
connection inside the VM, as virtio-serial doesn't appear to have these
problems.
---
tools/test-runner.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/tools/test-runner.c b/tools/test-runner.c
index b3e0b0cfe..0e3bfb8b7 100644
--- a/tools/test-runner.c
+++ b/tools/test-runner.c
@@ -306,7 +306,7 @@ static void start_qemu(void)
testargs);
argv = alloca(sizeof(qemu_argv) +
- (sizeof(char *) * (6 + (num_devs * 4))) +
+ (sizeof(char *) * (8 + (num_devs * 4))) +
(sizeof(char *) * (usb_dev ? 4 : 0)) +
(sizeof(char *) * num_extra_opts));
memcpy(argv, qemu_argv, sizeof(qemu_argv));
@@ -330,14 +330,19 @@ static void start_qemu(void)
argv[pos++] = "-append";
argv[pos++] = (char *) cmdline;
+ if (num_devs) {
+ argv[pos++] = "-device";
+ argv[pos++] = "virtio-serial";
+ }
+
for (i = 0; i < num_devs; i++) {
char *chrdev, *serdev;
chrdev = alloca(48 + strlen(device_path));
sprintf(chrdev, "socket,path=%s,id=bt%d", device_path, i);
- serdev = alloca(48);
- sprintf(serdev, "pci-serial,chardev=bt%d", i);
+ serdev = alloca(64);
+ sprintf(serdev, "virtconsole,chardev=bt%d,name=bt.%d", i, i);
argv[pos++] = "-chardev";
argv[pos++] = chrdev;
@@ -910,7 +915,7 @@ static void run_command(char *cmdname, char *home)
}
if (num_devs) {
- const char *node = "/dev/ttyS1";
+ const char *node = "/dev/hvc0";
unsigned int basic_flags, extra_flags;
printf("Attaching BR/EDR controller to %s\n", node);
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 01/16] emulator: btvirt: check pkt lengths, don't get stuck on malformed
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Don't try to parse packet before whole header is received.
If received data has unknown packet type, reset buffer so that we don't
get stuck.
---
emulator/server.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/emulator/server.c b/emulator/server.c
index fa2bc07be..f14e14cd2 100644
--- a/emulator/server.c
+++ b/emulator/server.c
@@ -136,12 +136,20 @@ again:
client->pkt_len = 0;
break;
case HCI_ACLDATA_PKT:
+ if (count < HCI_ACL_HDR_SIZE + 1) {
+ client->pkt_offset += len;
+ return;
+ }
acl_hdr = (hci_acl_hdr*)(ptr + 1);
client->pkt_expect = HCI_ACL_HDR_SIZE + acl_hdr->dlen + 1;
client->pkt_data = malloc(client->pkt_expect);
client->pkt_len = 0;
break;
case HCI_ISODATA_PKT:
+ if (count < HCI_ISO_HDR_SIZE + 1) {
+ client->pkt_offset += len;
+ return;
+ }
iso_hdr = (hci_iso_hdr *)(ptr + 1);
client->pkt_expect = HCI_ISO_HDR_SIZE +
iso_hdr->dlen + 1;
@@ -151,6 +159,7 @@ again:
default:
printf("packet error, unknown type: %d\n",
client->pkt_type);
+ client->pkt_offset = 0;
return;
}
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 04/16] emulator: btdev: clear more state on Reset
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
On controller Reset command, initialize most fields in struct btdev to
zero, similarly to the state just after btdev_create().
This excludes some fields like command bitmasks, which hciemu may have
adjusted.
To make this easier, add struct_group() macro similar to what kernel
uses.
---
emulator/btdev.c | 117 ++++++++++++++++++++++++++++-------------------
1 file changed, 70 insertions(+), 47 deletions(-)
diff --git a/emulator/btdev.c b/emulator/btdev.c
index 3a295b679..ad2e025d1 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -50,6 +50,12 @@
#define has_bredr(btdev) (!((btdev)->features[4] & 0x20))
#define has_le(btdev) (!!((btdev)->features[4] & 0x40))
+#define struct_group(NAME, MEMBERS...) \
+ union { \
+ struct { MEMBERS }; \
+ struct { MEMBERS } NAME; \
+ }
+
#define ACL_HANDLE BIT(0)
#define SCO_HANDLE BIT(8)
#define CIS_HANDLE SCO_HANDLE
@@ -149,15 +155,6 @@ struct btdev {
struct queue *conns;
- bool auth_init;
- uint8_t link_key[16];
- uint16_t pin[16];
- uint8_t pin_len;
- uint8_t io_cap;
- uint8_t auth_req;
- bool ssp_auth_complete;
- uint8_t ssp_status;
-
btdev_command_func command_handler;
void *command_data;
@@ -196,6 +193,18 @@ struct btdev {
const struct btdev_cmd *emu_cmds;
bool aosp_capable;
+ /* State zeroed on reset */
+ struct_group(reset_group,
+
+ bool auth_init;
+ uint8_t link_key[16];
+ uint16_t pin[16];
+ uint8_t pin_len;
+ uint8_t io_cap;
+ uint8_t auth_req;
+ bool ssp_auth_complete;
+ uint8_t ssp_status;
+
uint16_t default_link_policy;
uint8_t event_mask[8];
uint8_t event_mask_page2[8];
@@ -249,25 +258,26 @@ struct btdev {
struct le_cig le_cig[CIG_SIZE];
uint8_t le_iso_path[2];
- /* Real time length of AL array */
- uint8_t le_al_len;
- /* Real time length of RL array */
- uint8_t le_rl_len;
- struct btdev_al le_al[AL_SIZE];
- struct btdev_rl le_rl[RL_SIZE];
uint8_t le_rl_enable;
- uint16_t le_rl_timeout;
struct pending_conn pending_conn[MAX_PENDING_CONN];
- uint8_t le_local_sk256[32];
-
uint16_t sync_train_interval;
uint32_t sync_train_timeout;
uint8_t sync_train_service_data;
uint16_t le_ext_adv_type;
+ ); /* reset_group */
+
+ /* Real time length of AL array */
+ uint8_t le_al_len;
+ /* Real time length of RL array */
+ uint8_t le_rl_len;
+ struct btdev_al le_al[AL_SIZE];
+ struct btdev_rl le_rl[RL_SIZE];
+ uint16_t le_rl_timeout;
+
struct queue *le_ext_adv;
struct queue *le_per_adv;
struct queue *le_big;
@@ -617,15 +627,52 @@ static void le_big_free(void *data)
free(big);
}
+static void btdev_init_param(struct btdev *btdev)
+{
+ unsigned int i;
+
+ btdev->page_scan_interval = 0x0800;
+ btdev->page_scan_window = 0x0012;
+ btdev->page_scan_type = 0x00;
+
+ btdev->sync_train_interval = 0x0080;
+ btdev->sync_train_timeout = 0x0002ee00;
+ btdev->sync_train_service_data = 0x00;
+
+ btdev->acl_mtu = 192;
+ btdev->acl_max_pkt = 1;
+
+ btdev->sco_mtu = 72;
+ btdev->sco_max_pkt = 1;
+
+ btdev->iso_mtu = 251;
+ btdev->iso_max_pkt = 1;
+
+ for (i = 0; i < ARRAY_SIZE(btdev->le_cig); ++i)
+ btdev->le_cig[i].params.cig_id = 0xff;
+
+ btdev->country_code = 0x00;
+}
+
static void btdev_reset(struct btdev *btdev)
{
/* FIXME: include here clearing of all states that should be
* cleared upon HCI_Reset
*/
- btdev->le_scan_enable = 0x00;
- btdev->le_adv_enable = 0x00;
- btdev->le_pa_enable = 0x00;
+ if (btdev->inquiry_id > 0) {
+ timeout_remove(btdev->inquiry_id);
+ btdev->inquiry_id = 0;
+ }
+
+ queue_remove_all(btdev->conns, NULL, NULL, conn_remove);
+ queue_remove_all(btdev->le_ext_adv, NULL, NULL, le_ext_adv_free);
+ queue_remove_all(btdev->le_per_adv, NULL, NULL, free);
+ queue_remove_all(btdev->le_big, NULL, NULL, le_big_free);
+
+ memset(&btdev->reset_group, 0, sizeof(btdev->reset_group));
+
+ btdev_init_param(btdev);
al_clear(btdev);
rl_clear(btdev);
@@ -633,10 +680,7 @@ static void btdev_reset(struct btdev *btdev)
btdev->le_al_len = AL_SIZE;
btdev->le_rl_len = RL_SIZE;
- queue_remove_all(btdev->conns, NULL, NULL, conn_remove);
- queue_remove_all(btdev->le_ext_adv, NULL, NULL, le_ext_adv_free);
- queue_remove_all(btdev->le_per_adv, NULL, NULL, free);
- queue_remove_all(btdev->le_big, NULL, NULL, le_big_free);
+ btdev->le_rl_timeout = 0x0384;
}
static int cmd_reset(struct btdev *dev, const void *data, uint8_t len)
@@ -8130,7 +8174,6 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id)
{
struct btdev *btdev;
int index;
- unsigned int i;
btdev = malloc(sizeof(*btdev));
if (!btdev)
@@ -8195,27 +8238,7 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id)
break;
}
- btdev->page_scan_interval = 0x0800;
- btdev->page_scan_window = 0x0012;
- btdev->page_scan_type = 0x00;
-
- btdev->sync_train_interval = 0x0080;
- btdev->sync_train_timeout = 0x0002ee00;
- btdev->sync_train_service_data = 0x00;
-
- btdev->acl_mtu = 192;
- btdev->acl_max_pkt = 1;
-
- btdev->sco_mtu = 72;
- btdev->sco_max_pkt = 1;
-
- btdev->iso_mtu = 251;
- btdev->iso_max_pkt = 1;
-
- for (i = 0; i < ARRAY_SIZE(btdev->le_cig); ++i)
- btdev->le_cig[i].params.cig_id = 0xff;
-
- btdev->country_code = 0x00;
+ btdev_init_param(btdev);
index = add_btdev(btdev);
if (index < 0) {
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 02/16] emulator: btvirt: allow specifying where server unix sockets are made
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Make --server to take optional path name where to create the various
server sockets.
---
emulator/main.c | 37 ++++++++++++++++++++++++-------------
1 file changed, 24 insertions(+), 13 deletions(-)
diff --git a/emulator/main.c b/emulator/main.c
index 456fcd98e..09d6e9adb 100644
--- a/emulator/main.c
+++ b/emulator/main.c
@@ -18,6 +18,7 @@
#include <stdbool.h>
#include <getopt.h>
#include <sys/uio.h>
+#include <limits.h>
#include "src/shared/mainloop.h"
#include "src/shared/util.h"
@@ -46,7 +47,7 @@ static void usage(void)
printf("options:\n"
"\t-d Enable debug\n"
"\t-S Create local serial port\n"
- "\t-s Create local server sockets\n"
+ "\t-s[path=/tmp] Create local server sockets\n"
"\t-t[port=45550] Create a TCP server\n"
"\t-l[num] Number of local controllers\n"
"\t-L Create LE only controller\n"
@@ -60,7 +61,7 @@ static void usage(void)
static const struct option main_options[] = {
{ "debug", no_argument, NULL, 'd' },
{ "serial", no_argument, NULL, 'S' },
- { "server", no_argument, NULL, 's' },
+ { "server", optional_argument, NULL, 's' },
{ "tcp", optional_argument, NULL, 't' },
{ "local", optional_argument, NULL, 'l' },
{ "le", no_argument, NULL, 'L' },
@@ -88,6 +89,7 @@ int main(int argc, char *argv[])
struct server *server5;
bool debug_enabled = false;
bool server_enabled = false;
+ const char *server_path = "/tmp";
uint16_t tcp_port = 0;
bool serial_enabled = false;
int letest_count = 0;
@@ -100,7 +102,7 @@ int main(int argc, char *argv[])
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "dSst::l::LBAU::T::vh",
+ opt = getopt_long(argc, argv, "dSs::t::l::LBAU::T::vh",
main_options, NULL);
if (opt < 0)
break;
@@ -114,6 +116,8 @@ int main(int argc, char *argv[])
break;
case 's':
server_enabled = true;
+ if (optarg)
+ server_path = optarg;
break;
case 't':
if (optarg)
@@ -196,28 +200,35 @@ int main(int argc, char *argv[])
}
if (server_enabled) {
- server1 = server_open_unix(SERVER_TYPE_BREDRLE,
- "/tmp/bt-server-bredrle");
+ char path[PATH_MAX];
+
+ snprintf(path, sizeof(path), "%s/%s", server_path,
+ "bt-server-bredrle");
+ server1 = server_open_unix(SERVER_TYPE_BREDRLE, path);
if (!server1)
fprintf(stderr, "Failed to open BR/EDR/LE server\n");
- server2 = server_open_unix(SERVER_TYPE_BREDR,
- "/tmp/bt-server-bredr");
+ snprintf(path, sizeof(path), "%s/%s", server_path,
+ "bt-server-bredr");
+ server2 = server_open_unix(SERVER_TYPE_BREDR, path);
if (!server2)
fprintf(stderr, "Failed to open BR/EDR server\n");
- server3 = server_open_unix(SERVER_TYPE_AMP,
- "/tmp/bt-server-amp");
+ snprintf(path, sizeof(path), "%s/%s", server_path,
+ "bt-server-amp");
+ server3 = server_open_unix(SERVER_TYPE_AMP, path);
if (!server3)
fprintf(stderr, "Failed to open AMP server\n");
- server4 = server_open_unix(SERVER_TYPE_LE,
- "/tmp/bt-server-le");
+ snprintf(path, sizeof(path), "%s/%s", server_path,
+ "bt-server-le");
+ server4 = server_open_unix(SERVER_TYPE_LE, path);
if (!server4)
fprintf(stderr, "Failed to open LE server\n");
- server5 = server_open_unix(SERVER_TYPE_MONITOR,
- "/tmp/bt-server-mon");
+ snprintf(path, sizeof(path), "%s/%s", server_path,
+ "bt-server-mon");
+ server5 = server_open_unix(SERVER_TYPE_MONITOR, path);
if (!server5)
fprintf(stderr, "Failed to open monitor server\n");
}
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 03/16] emulator: btvirt: support SCO data packets
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
In-Reply-To: <cover.1778688966.git.pav@iki.fi>
Support also SCO data packets in btvirt.
---
emulator/server.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/emulator/server.c b/emulator/server.c
index f14e14cd2..7790867b7 100644
--- a/emulator/server.c
+++ b/emulator/server.c
@@ -119,6 +119,7 @@ again:
hci_command_hdr *cmd_hdr;
hci_acl_hdr *acl_hdr;
hci_iso_hdr *iso_hdr;
+ hci_sco_hdr *sco_hdr;
if (!client->pkt_data) {
client->pkt_type = ptr[0];
@@ -156,6 +157,17 @@ again:
client->pkt_data = malloc(client->pkt_expect);
client->pkt_len = 0;
break;
+ case HCI_SCODATA_PKT:
+ if (count < HCI_SCO_HDR_SIZE + 1) {
+ client->pkt_offset += len;
+ return;
+ }
+ sco_hdr = (hci_sco_hdr *)(ptr + 1);
+ client->pkt_expect = HCI_SCO_HDR_SIZE +
+ sco_hdr->dlen + 1;
+ client->pkt_data = malloc(client->pkt_expect);
+ client->pkt_len = 0;
+ break;
default:
printf("packet error, unknown type: %d\n",
client->pkt_type);
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ v5 00/16] Functional/integration testing
From: Pauli Virtanen @ 2026-05-13 16:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
Add framework for writing tests simulating "real" environments where
BlueZ and other parts of the stack run on different virtual machine
hosts that communicate with each other.
*** v5 ***
https://github.com/pv/bluez/compare/func-test-v4-r..func-test-v5
* Factor out the pytest-bluezenv plugin, to be maintained separately.
https://pypi.org/project/pytest-bluezenv/
It could in principle be moved under the BlueZ organization, but
there's no particular reason why it should be in bluez repository.
Generally, it's better to have the pytest plugin separate so it's
easier to reuse and can have its own version cycle.
* Pipewire tests are moved to pipewire repository where they probably
belong to.
They can be run easily vs. given BlueZ build dir.
We are currently running them in Pipewire CI, but at frozen
kernel/BlueZ version, so it is not testing BlueZ/kernel upstream
development.
* No changes in the emulator/test-runner patches since v4
They are stand-alone bug fixes / improvements, and make sense
also separately from the rest.
*** v4 ***
https://github.com/pv/bluez/compare/func-test-v3-r..func-test-v4
* Use virtconsole for simpler HCI forwarding to the vm
* Fix typoed vm_module -> vm_once
* Skip tests for some pipewire versions
*** v3 ***
https://github.com/pv/bluez/compare/func-test-v2-r..func-test-v3
* fix configure.ac openpty() detection to match TOOLS conditional,
to fix make distcheck
* properly retry virtio RPC connection if it fails initially
* properly restart VM if previous test hangs
* allow custom parent host side proxy objects, use them for pexpect
* improve --list with out-of-tree test files
* fix missing bus.set_exit_on_disconnect(False) for obex tests
* have --vm-timeout etc. change values also on VM host side
* use larger-memory VM instances for Pipewire, in case ASAN enabled
* set reasonable inside-VM ASAN_OPTION default values
* don't run btvirt under stdbuf, since not compatible with ASAN
*** v2 ***
https://github.com/pv/bluez/compare/func-test-v1-r..func-test-v2
* move unit/func_test -> test/functional & test/pytest_bluez
The pytest_bluez plugin is in principle reusable for other projects,
so we can eg. have more complete Pipewire integration tests that can
live in Pipewire repository.
* openpty() is in -lutil on some platforms, detect this in autoconf
* more emulator adjustments:
- fix SCO data packet support in btvirt
- more complete Reset command
* improve logging: get timestamps from kernel, and reorder logs
to timestamp order, so that lines from different hosts, btmon,
and parent tester appear in right order regardless of whether
VM console / btmon is lagging
- this requires accurate clock sync in the VM, so enable KVM PTP in
config and run chronyd inside the VMs
- use virtio port instead of qemu console to export logs, since the
console has fixed baud rate and is too slow
* add --btmon & export btsnoop dumps from VM hosts
* fix compatibility with older Python versions
* add parametrized_host_config()
* split Pipewire test to A2DP/BAP/HFP and really stream audio.
These catch the 5.86 regression fixed in 066a164a524e498 and
the 5.84 one in 6b0a08776a
* add support for tests that reuse tester environment, so they can run
faster without needing Bluetoothd teardown/setup in between
* add HostPlugin.presetup (mainly for test skipping)
* deal with RPC virtio port buffer possibly containing unflushed
commands from previous failed test
* add some Agent1 interface tests
* add basic Obex file transfer tests
* add support for logging in to a running test instance (for gdb etc)
* export any core dumps out from test environ
Some bells & whistles:
* add --kernel-build for kernel image build
* test suite Python code formatting checks
***
Implements:
- RPC communication with tester instances running each of the VM hosts.
Tests run on parent host, which instructs VM hosts what to do.
- Extensible way to add stateful test-specific code inside the VM
instances
- Logging control: output from different processes running inside the VM
are separated and can be filtered.
- Test runner framework with Pytest (more convenient than Python/unittest)
- Automatic grouping of tests to minimize VM reboots
- Redirecting USB controllers to use for testing in addition to btvirt
- Fairly straightforward, ~1600 sloc for the framework
There is no requirement that the tests spawn VM instances, the test
runner can be used for any tests written in Python.
See doc/test-functional.rst for various examples.
Also test/functional/test_bluetoothctl_vm.py has some simple cases, and
test/functional/test_pipewire.py for a more complicated setup
host0(qemu): Pipewire <-> BlueZ <-> kernel
<-> btvirt
host1(qemu): kernel <-> BlueZ <-> Pipewire
The framework allows easily passing any data and code between the parent
and VM hosts, so writing tests is straightforward.
***
Some examples:
$ test/test-functional --list -q
test/functional/lib/tests/test_rpc.py::test_basic
test/functional/test_bluetoothctl_vm.py::test_bluetoothctl_pair[hosts0-vm2]
test/functional/test_bluetoothctl_vm.py::test_bluetoothctl_script_show[hosts1-vm1]
test/functional/test_btmgmt_vm.py::test_btmgmt_info[hosts2-vm1]
test/functional/test_pipewire.py::test_pipewire[hosts3-vm2]
$ test/test-functional -v --no-header
======================================= test session starts ========================================
collected 5 items
test/functional/lib/tests/test_rpc.py::test_basic PASSED [ 20%]
test/functional/test_bluetoothctl_vm.py::test_bluetoothctl_script_show[hosts1-vm1] SKIPPED [ 40%]
test/functional/test_btmgmt_vm.py::test_btmgmt_info[hosts2-vm1] SKIPPED (No kernel image) [ 60%]
test/functional/test_bluetoothctl_vm.py::test_bluetoothctl_pair[hosts0-vm2] SKIPPED (No k...) [ 80%]
test/functional/test_pipewire.py::test_pipewire[hosts3-vm2] SKIPPED (No kernel image) [100%]
=================================== 1 passed, 4 skipped in 0.19s ===================================
$ test/test-functional --kernel=../linux
============================= test session starts ==============================
platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0
rootdir: /home/pauli/prj/external/bluez/unit
configfile: pytest.ini
plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1
collected 5 items
test/functional/lib/tests/test_rpc.py . [ 20%]
test/functional/test_bluetoothctl_vm.py . [ 40%]
test/functional/test_btmgmt_vm.py . [ 60%]
test/functional/test_bluetoothctl_vm.py . [ 80%]
test/functional/test_pipewire.py . [100%]
============================== 5 passed in 41.92s ==============================
$ test/test-functional --kernel=../linux -k test_btmgmt
============================= test session starts ==============================
platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0
rootdir: /home/pauli/prj/external/bluez/unit
configfile: pytest.ini
plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1
collected 5 items / 4 deselected / 1 selected
test/functional/test_btmgmt_vm.py . [100%]
======================= 1 passed, 4 deselected in 9.15s ========================
$ grep btmgmt test-functional.log
13:15:42 INFO rpc.host.0.0 : client: call_plugin ('call', '__call__', <function run at 0x7f27b81ce140>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'}
13:15:42 INFO host.0.0.rpc : server: call_plugin ('call', '__call__', <function run at 0x7fd5e35a1010>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'}
13:15:42 INFO host.0.0.run : $ /home/pauli/prj/external/bluez/build/tools/btmgmt --index 0 info
$ test/test-functional --kernel=../linux -k test_btmgmt --log-cli-level=0
============================= test session starts ==============================
platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0
rootdir: /home/pauli/prj/external/bluez/unit
configfile: pytest.ini
plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1
collected 5 items / 4 deselected / 1 selected
test/functional/test_btmgmt_vm.py::test_btmgmt_info[hosts2-vm1]
-------------------------------- live log setup --------------------------------
13:00:31 INFO func_test.lib.env : Starting btvirt: /usr/bin/stdbuf -o L -e L /home/pauli/prj/external/bluez/build/emulator/btvirt --server=/tmp/bluez-func-test-8t6ychy8
13:00:31 OUT btvirt : Bluetooth emulator ver 5.86
13:00:31 INFO func_test.lib.env : Starting host: /home/pauli/prj/external/bluez/build/tools/test-runner --kernel=../linux/arch/x86/boot/bzImage -u/tmp/bluez-func-test-8t6ychy8/bt-server-bredrle -o -chardev -o socket,id=ser0,path=/tmp/bluez-func-test-8t6ychy8/bluez-func-test-rpc-0,server=on,wait=off -o -device -o virtio-serial -o -device -o virtserialport,chardev=ser0,name=bluez-func-test-rpc -H -- /usr/bin/python3 -P /home/pauli/prj/external/bluez/test/functional/lib/runner.py /dev/ttyS2
13:00:31 OUT btvirt : Request for /tmp/bluez-func-test-8t6ychy8/bt-server-bredrle
13:00:32 OUT host.0.0 : early console in extract_kernel
13:00:32 OUT host.0.0 : input_data: 0x000000000425c2c4
...
13:00:39 INFO rpc.host.0.0 : client: call_plugin ('call', '__call__', <function run at 0x7f7547472140>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'}
13:00:39 DEBUG host.0.0.rpc : server: done
13:00:39 INFO host.0.0.rpc : server: call_plugin ('call', '__call__', <function run at 0x7f77dcc81010>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'}
13:00:39 INFO host.0.0.run : $ /home/pauli/prj/external/bluez/build/tools/btmgmt --index 0 info
13:00:40 OUT host.0.0.run.out : hci0: Primary controller
13:00:40 OUT host.0.0.run.out : addr 00:AA:01:00:00:42 version 11 manufacturer 1521 class 0x000000
13:00:40 OUT host.0.0.run.out : supported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver
13:00:40 OUT host.0.0.run.out : current settings: br/edr
13:00:40 OUT host.0.0.run.out : name
13:00:40 OUT host.0.0.run.out : short name
13:00:40 INFO host.0.0.run : (return code 0)
13:00:40 DEBUG rpc.host.0.0 : client-reply
PASSED [100%]
13:00:40 OUT host.0.0 : qemu-system-x86_64: terminating on signal 15 from pid 149047 (python3)
======================= 1 passed, 4 deselected in 8.84s ========================
$ test/test-functional --kernel=../linux -k test_bluetoothctl_pair --log-cli-level=0 --log-filter=*.bluetoothctl,rpc.* --force-usb
============================= test session starts ==============================
platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0
rootdir: /home/pauli/prj/external/bluez/unit
configfile: pytest.ini
plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1
collected 5 items / 4 deselected / 1 selected
test/functional/test_bluetoothctl_vm.py::test_bluetoothctl_pair[hosts0-vm2]
-------------------------------- live log setup --------------------------------
13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Bdaddr object at 0x7f268712d160>,) {}
13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Call object at 0x7f268712d2b0>,) {}
13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.DbusSystem object at 0x7f2687aa30e0>,) {}
13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Bluetoothd object at 0x7f2687aa3230>,) {}
13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Bluetoothctl object at 0x7f268712d010>,) {}
13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Bdaddr object at 0x7f26871542d0>,) {}
13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Call object at 0x7f2687154410>,) {}
13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.DbusSystem object at 0x7f2687aa30e0>,) {}
13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Bluetoothd object at 0x7f2687aa3230>,) {}
13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Bluetoothctl object at 0x7f2687154190>,) {}
13:03:20 INFO rpc.host.0.0 : client: wait_load () {}
13:03:21 DEBUG rpc.host.0.0 : client-reply
13:03:21 INFO rpc.host.0.1 : client: wait_load () {}
13:03:21 DEBUG rpc.host.0.1 : client-reply
-------------------------------- live log call ---------------------------------
13:03:21 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'send', 'show\n') {}
13:03:21 DEBUG rpc.host.0.0 : client-reply
13:03:21 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'expect', 'Powered: yes') {}
...
13:03:23 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'send', 'pair 70:1a:b8:73:99:bb\n') {}
13:03:23 OUT host.0.0.bluetoothctl: pair 70:1a:b8:73:99:bb
13:03:23 DEBUG rpc.host.0.0 : client-reply
13:03:23 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'expect', 'Confirm passkey (\\d+).*:') {}
13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> pair 70:1a:b8:73:99:bb
13:03:23 OUT host.0.0.bluetoothctl: Attempting to pair with 70:1A:B8:73:99:BB
13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> hci0 device_flags_changed: 70:1A:B8:73:99:BB (BR/EDR)
13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> supp: 0x00000007 curr: 0x00000000
13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> hci0 type 7 discovering off
13:03:25 OUT host.0.0.bluetoothctl: [bluetoothctl]> hci0 70:1A:B8:73:99:BB type BR/EDR connected eir_len 12
13:03:25 OUT host.0.0.bluetoothctl: [bluetoothctl]> [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB Connected: yes
13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> Request confirmation
13:03:25 DEBUG rpc.host.0.0 : client-reply
13:03:25 INFO rpc.host.0.1 : client: call_plugin ('bluetoothctl', 'expect', 'Confirm passkey 237345') {}
13:03:25 OUT host.0.1.bluetoothctl: [bluetoothctl]> hci0 84:5C:F3:77:31:19 type BR/EDR connected eir_len 12
13:03:25 OUT host.0.1.bluetoothctl: [bluetoothctl]> [NEW] Device 84:5C:F3:77:31:19 BlueZ 5.86
13:03:25 DEBUG rpc.host.0.1 : client-reply
13:03:25 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'send', 'yes\n') {}
13:03:25 OUT host.0.1.bluetoothctl: [bluetoothctl]> [BlueZ 5.86]> Request confirmation
13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [agent] Confirm passkey 237345 (yes/no): yes
13:03:25 DEBUG rpc.host.0.0 : client-reply
13:03:25 INFO rpc.host.0.1 : client: call_plugin ('bluetoothctl', 'send', 'yes\n') {}
13:03:25 OUT host.0.1.bluetoothctl: [BlueZ 5.86]> [agent] Confirm passkey 237345 (yes/no): yes
13:03:25 DEBUG rpc.host.0.1 : client-reply
13:03:25 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'expect', 'Pairing successful') {}
13:03:25 OUT host.0.0.bluetoothctl: yes
13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> hci0 new_link_key 70:1A:B8:73:99:BB type 0x08 pin_len 0 store_hint 1
13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB Bonded: yes
13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB AddressType: public
13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB UUIDs: 0000110c-0000-1000-8000-00805f9b34fb
13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB UUIDs: 0000110e-0000-1000-8000-00805f9b34fb
13:03:26 DEBUG rpc.host.0.0 : client-reply
PASSED [100%]
------------------------------ live log teardown -------------------------------
13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:98:FF:qemu-system-x86_64: terminating on signal 15 from pid 149357 (python3)
======================= 1 passed, 4 deselected in 13.22s =======================
$ test/test-functional -k test_btmgmt --kernel=../linux --trace
============================= test session starts ==============================
platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0
rootdir: /home/pauli/prj/external/bluez/unit
configfile: pytest.ini
plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1
collected 5 items / 4 deselected / 1 selected
test/functional/test_btmgmt_vm.py
>>>>>>>>>>>>>>>>>>>> PDB runcall (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>
> /home/pauli/prj/external/bluez/test/functional/test_btmgmt_vm.py(19)test_btmgmt_info()
-> (host,) = hosts
(Pdb) n
> /home/pauli/prj/external/bluez/test/functional/test_btmgmt_vm.py(21)test_btmgmt_info()
-> result = host.call(
(Pdb) p host.bdaddr
'00:aa:01:00:00:42'
(Pdb) n
> /home/pauli/prj/external/bluez/test/functional/test_btmgmt_vm.py(22)test_btmgmt_info()
-> run,
(Pdb) n
> /home/pauli/prj/external/bluez/test/functional/test_btmgmt_vm.py(23)test_btmgmt_info()
-> [btmgmt, "--index", "0", "info"],
(Pdb) n
> /home/pauli/prj/external/bluez/test/functional/test_btmgmt_vm.py(24)test_btmgmt_info()
-> stdout=subprocess.PIPE,
(Pdb) n
> /home/pauli/prj/external/bluez/test/functional/test_btmgmt_vm.py(25)test_btmgmt_info()
-> stdin=subprocess.DEVNULL,
(Pdb) n
> /home/pauli/prj/external/bluez/test/functional/test_btmgmt_vm.py(26)test_btmgmt_info()
-> encoding="utf-8",
(Pdb) n
> /home/pauli/prj/external/bluez/test/functional/test_btmgmt_vm.py(21)test_btmgmt_info()
-> result = host.call(
(Pdb) n
> /home/pauli/prj/external/bluez/test/functional/test_btmgmt_vm.py(28)test_btmgmt_info()
-> assert result.returncode == 0
(Pdb) p result
CompletedProcess(args=['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info'], returncode=0, stdout='hci0:\tPrimary controller\n\taddr 00:AA:01:00:00:42 version 11 manufacturer 1521 class 0x000000\n\tsupported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver \n\tcurrent settings: br/edr \n\tname \n\tshort name \n')
(Pdb) print(result.stdout)
hci0: Primary controller
addr 00:AA:01:00:00:42 version 11 manufacturer 1521 class 0x000000
supported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver
current settings: br/edr
name
short name
(Pdb) q
!!!!!!!!!!!!!!!!!!! _pytest.outcomes.Exit: Quitting debugger !!!!!!!!!!!!!!!!!!!
======================= 4 deselected in 75.91s (0:01:15) =======================
Pauli Virtanen (16):
emulator: btvirt: check pkt lengths, don't get stuck on malformed
emulator: btvirt: allow specifying where server unix sockets are made
emulator: btvirt: support SCO data packets
emulator: btdev: clear more state on Reset
test-runner: enable path argument for --unix
test-runner: Add -o/--option option
test-runner: allow source tree root for -k
test-runner: use virtio-serial for implementing -u device forwarding
doc: enable CONFIG_VIRTIO_CONSOLE in tester config
doc: enable KVM paravirtualization & clock support in tester kernel
config
doc: add functional/integration testing documentation
test: add functional/integration testing framework
build: add functional testing target
test: functional: impose Python code formatting
test: functional: add some Agent1 interface tests
test: functional: add basic obex file transfer tests
Makefile.am | 10 +
configure.ac | 22 ++
doc/ci.config | 9 +
doc/test-functional.rst | 299 ++++++++++++++++++++++++
doc/test-runner.rst | 17 ++
doc/tester.config | 9 +
emulator/btdev.c | 117 ++++++----
emulator/main.c | 37 +--
emulator/server.c | 21 ++
test/functional/__init__.py | 2 +
test/functional/conftest.py | 48 ++++
test/functional/requirements.txt | 2 +
test/functional/test_agent.py | 46 ++++
test/functional/test_bluetoothctl_vm.py | 152 ++++++++++++
test/functional/test_btmgmt_vm.py | 30 +++
test/functional/test_obex.py | 285 ++++++++++++++++++++++
test/functional/test_tests.py | 23 ++
test/pytest.ini | 17 ++
test/test-functional | 21 ++
test/test-functional-attach | 7 +
tools/test-runner.c | 89 ++++---
21 files changed, 1177 insertions(+), 86 deletions(-)
create mode 100644 doc/test-functional.rst
create mode 100644 test/functional/__init__.py
create mode 100644 test/functional/conftest.py
create mode 100644 test/functional/requirements.txt
create mode 100644 test/functional/test_agent.py
create mode 100644 test/functional/test_bluetoothctl_vm.py
create mode 100644 test/functional/test_btmgmt_vm.py
create mode 100644 test/functional/test_obex.py
create mode 100644 test/functional/test_tests.py
create mode 100644 test/pytest.ini
create mode 100755 test/test-functional
create mode 100755 test/test-functional-attach
--
2.54.0
^ permalink raw reply
* [Bug 110901] Macbook8,1 12-inch (Early 2015) Bluetooth not working
From: bugzilla-daemon @ 2026-05-13 16:08 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <bug-110901-62941@https.bugzilla.kernel.org/>
https://bugzilla.kernel.org/show_bug.cgi?id=110901
--- Comment #50 from Andy Shevchenko (andy.shevchenko@gmail.com) ---
(In reply to Jonas from comment #49)
> (In reply to Leif Liddy from comment #46)
> Are these patches still required or have they eventually been merged into
> the kernel eventually?
I just checked the currect driver (as in v7.1-rcX) and I believe the support on
MBP machines is in upstream for a long time (the hci_ldisc.c patch was
upstreamed same, 2017 year, the rest was redone by Hans de Goede — it uses
serdev framework now and the PM runtime support is done accordingly).
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are the assignee for the bug.
^ permalink raw reply
* [bluez/bluez]
From: BluezTestBot @ 2026-05-13 15:23 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1081435
Home: https://github.com/bluez/bluez
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* [bluez/bluez] c47895: monitor: Add decoding for Microsoft defined event
From: apusaka @ 2026-05-13 15:22 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/master
Home: https://github.com/bluez/bluez
Commit: c4789506455f639a330ece6016912b93c9a419b8
https://github.com/bluez/bluez/commit/c4789506455f639a330ece6016912b93c9a419b8
Author: Archie Pusaka <apusaka@chromium.org>
Date: 2026-05-13 (Wed, 13 May 2026)
Changed paths:
M monitor/msft.c
M monitor/msft.h
M monitor/packet.c
Log Message:
-----------
monitor: Add decoding for Microsoft defined event
This adds decoders to MSFT LE monitor device event
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* Re: [PATCH BlueZ] monitor: Add decoding for Microsoft defined event
From: patchwork-bot+bluetooth @ 2026-05-13 14:00 UTC (permalink / raw)
To: Archie Pusaka
Cc: linux-bluetooth, luiz.dentz, chromeos-bluetooth-upstreaming,
apusaka
In-Reply-To: <20260415073940.739683-1-apusaka@google.com>
Hello:
This patch was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Wed, 15 Apr 2026 15:36:45 +0800 you wrote:
> From: Archie Pusaka <apusaka@chromium.org>
>
> This adds decoders to MSFT LE monitor device event
> ---
>
> monitor/msft.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++-
> monitor/msft.h | 1 +
> monitor/packet.c | 44 ++++++++++++++++++++++-----
> 3 files changed, 115 insertions(+), 8 deletions(-)
Here is the summary with links:
- [BlueZ] monitor: Add decoding for Microsoft defined event
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=c4789506455f
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* RE: [v5] Bluetooth: btrtl: Add firmware format v3 support
From: bluez.test.bot @ 2026-05-13 10:31 UTC (permalink / raw)
To: linux-bluetooth, hildawu
In-Reply-To: <20260513092423.2789433-1-hildawu@realtek.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=1094070
---Test result---
Test Summary:
CheckPatch PASS 1.75 seconds
GitLint PASS 0.34 seconds
SubjectPrefix PASS 0.13 seconds
BuildKernel PASS 24.61 seconds
CheckAllWarning PASS 27.06 seconds
CheckSparse PASS 25.95 seconds
BuildKernel32 PASS 24.65 seconds
TestRunnerSetup PASS 519.47 seconds
IncrementalBuild PASS 23.66 seconds
https://github.com/bluez/bluetooth-next/pull/183
---
Regards,
Linux Bluetooth
^ permalink raw reply
* [Bug 221511] New: MT7925 7.1 rc does not work, but it works in kernel 7.0
From: bugzilla-daemon @ 2026-05-13 9:51 UTC (permalink / raw)
To: linux-bluetooth
https://bugzilla.kernel.org/show_bug.cgi?id=221511
Bug ID: 221511
Summary: MT7925 7.1 rc does not work, but it works in kernel
7.0
Product: Drivers
Version: 2.5
Hardware: AMD
OS: Linux
Status: NEW
Severity: high
Priority: P3
Component: Bluetooth
Assignee: linux-bluetooth@vger.kernel.org
Reporter: erpumper@gmail.com
Regression: No
7.1 rc does not work, but it works in kernel 7.0
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are the assignee for the bug.
^ permalink raw reply
* [bluetooth-next:master] BUILD SUCCESS ff2e1268ee45f471bb06d4231dc284d5112f1b5c
From: kernel test robot @ 2026-05-13 9:38 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master
branch HEAD: ff2e1268ee45f471bb06d4231dc284d5112f1b5c Bluetooth: SCO: convert to getsockopt_iter
elapsed time: 927m
configs tested: 182
configs skipped: 2
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-15.2.0
alpha allyesconfig gcc-15.2.0
alpha defconfig gcc-15.2.0
arc allmodconfig clang-16
arc allmodconfig gcc-15.2.0
arc allnoconfig gcc-15.2.0
arc allyesconfig clang-23
arc allyesconfig gcc-15.2.0
arc defconfig gcc-15.2.0
arc randconfig-001-20260513 gcc-14.3.0
arc randconfig-002-20260513 gcc-14.3.0
arm allnoconfig clang-23
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-16
arm allyesconfig gcc-15.2.0
arm defconfig gcc-15.2.0
arm gemini_defconfig clang-20
arm randconfig-001-20260513 gcc-14.3.0
arm randconfig-002-20260513 gcc-14.3.0
arm randconfig-003-20260513 gcc-14.3.0
arm randconfig-004-20260513 gcc-14.3.0
arm64 allmodconfig clang-19
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-15.2.0
arm64 randconfig-001 clang-23
arm64 randconfig-001-20260513 clang-23
arm64 randconfig-001-20260513 gcc-12.5.0
arm64 randconfig-002 clang-23
arm64 randconfig-002-20260513 clang-23
arm64 randconfig-002-20260513 gcc-12.5.0
arm64 randconfig-003 clang-23
arm64 randconfig-003-20260513 clang-23
arm64 randconfig-003-20260513 gcc-12.5.0
arm64 randconfig-004 clang-23
arm64 randconfig-004-20260513 clang-23
arm64 randconfig-004-20260513 gcc-12.5.0
csky allmodconfig gcc-15.2.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-15.2.0
csky randconfig-001 clang-23
csky randconfig-001-20260513 clang-23
csky randconfig-001-20260513 gcc-12.5.0
csky randconfig-002 clang-23
csky randconfig-002-20260513 clang-23
csky randconfig-002-20260513 gcc-12.5.0
hexagon allmodconfig clang-17
hexagon allmodconfig gcc-15.2.0
hexagon allnoconfig clang-23
hexagon allnoconfig gcc-15.2.0
hexagon defconfig gcc-15.2.0
hexagon randconfig-001-20260513 gcc-8.5.0
hexagon randconfig-002-20260513 gcc-8.5.0
i386 allmodconfig clang-20
i386 allmodconfig gcc-14
i386 allnoconfig gcc-14
i386 allnoconfig gcc-15.2.0
i386 allyesconfig clang-20
i386 allyesconfig gcc-14
i386 buildonly-randconfig-001-20260513 clang-20
i386 buildonly-randconfig-002-20260513 clang-20
i386 buildonly-randconfig-003-20260513 clang-20
i386 buildonly-randconfig-004-20260513 clang-20
i386 buildonly-randconfig-005-20260513 clang-20
i386 buildonly-randconfig-006-20260513 clang-20
i386 defconfig gcc-15.2.0
i386 randconfig-001-20260513 clang-20
i386 randconfig-002-20260513 clang-20
i386 randconfig-003-20260513 clang-20
i386 randconfig-004-20260513 clang-20
i386 randconfig-005-20260513 clang-20
i386 randconfig-006-20260513 clang-20
i386 randconfig-007-20260513 clang-20
loongarch allmodconfig clang-19
loongarch allmodconfig clang-23
loongarch allnoconfig clang-23
loongarch allnoconfig gcc-15.2.0
loongarch randconfig-001-20260513 gcc-8.5.0
loongarch randconfig-002-20260513 gcc-8.5.0
m68k allmodconfig gcc-15.2.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-16
m68k allyesconfig gcc-15.2.0
m68k atari_defconfig gcc-15.2.0
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
nios2 allmodconfig clang-23
nios2 allmodconfig gcc-11.5.0
nios2 allnoconfig clang-23
nios2 allnoconfig gcc-11.5.0
nios2 randconfig-001-20260513 gcc-8.5.0
nios2 randconfig-002-20260513 gcc-8.5.0
openrisc allmodconfig clang-23
openrisc allmodconfig gcc-15.2.0
openrisc allnoconfig clang-23
openrisc allnoconfig gcc-15.2.0
openrisc defconfig gcc-15.2.0
parisc allmodconfig gcc-15.2.0
parisc allnoconfig clang-23
parisc allnoconfig gcc-15.2.0
parisc allyesconfig clang-19
parisc allyesconfig gcc-15.2.0
parisc defconfig gcc-15.2.0
parisc randconfig-001-20260513 gcc-8.5.0
parisc randconfig-002-20260513 gcc-8.5.0
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-23
powerpc allnoconfig gcc-15.2.0
powerpc randconfig-001-20260513 gcc-8.5.0
powerpc randconfig-002-20260513 gcc-8.5.0
powerpc64 randconfig-001-20260513 gcc-8.5.0
powerpc64 randconfig-002-20260513 gcc-8.5.0
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allnoconfig gcc-15.2.0
riscv allyesconfig clang-16
riscv defconfig gcc-15.2.0
riscv randconfig-001-20260513 gcc-15.2.0
riscv randconfig-002-20260513 gcc-15.2.0
s390 allmodconfig clang-18
s390 allmodconfig clang-19
s390 allnoconfig clang-23
s390 allyesconfig gcc-15.2.0
s390 defconfig gcc-15.2.0
s390 randconfig-001-20260513 gcc-15.2.0
s390 randconfig-002-20260513 gcc-15.2.0
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-23
sh allnoconfig gcc-15.2.0
sh allyesconfig clang-19
sh allyesconfig gcc-15.2.0
sh randconfig-001-20260513 gcc-15.2.0
sh randconfig-002-20260513 gcc-15.2.0
sparc allnoconfig clang-23
sparc allnoconfig gcc-15.2.0
sparc defconfig gcc-15.2.0
sparc randconfig-001-20260513 gcc-11.5.0
sparc randconfig-002-20260513 gcc-11.5.0
sparc64 allmodconfig clang-23
sparc64 randconfig-001-20260513 gcc-11.5.0
sparc64 randconfig-002-20260513 gcc-11.5.0
um allmodconfig clang-19
um allnoconfig clang-23
um allyesconfig gcc-14
um allyesconfig gcc-15.2.0
um randconfig-001-20260513 gcc-11.5.0
um randconfig-002-20260513 gcc-11.5.0
x86_64 allmodconfig clang-20
x86_64 allnoconfig clang-20
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-20
x86_64 buildonly-randconfig-001-20260513 gcc-12
x86_64 buildonly-randconfig-002-20260513 gcc-12
x86_64 buildonly-randconfig-003-20260513 gcc-12
x86_64 buildonly-randconfig-004-20260513 gcc-12
x86_64 buildonly-randconfig-005-20260513 gcc-12
x86_64 buildonly-randconfig-006-20260513 gcc-12
x86_64 randconfig-011-20260513 gcc-14
x86_64 randconfig-012-20260513 gcc-14
x86_64 randconfig-013-20260513 gcc-14
x86_64 randconfig-014-20260513 gcc-14
x86_64 randconfig-015-20260513 gcc-14
x86_64 randconfig-016-20260513 gcc-14
x86_64 randconfig-071-20260513 gcc-14
x86_64 randconfig-072-20260513 gcc-14
x86_64 randconfig-073-20260513 gcc-14
x86_64 randconfig-074-20260513 gcc-14
x86_64 randconfig-075-20260513 gcc-14
x86_64 randconfig-076-20260513 gcc-14
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-20
xtensa allnoconfig clang-23
xtensa allnoconfig gcc-15.2.0
xtensa allyesconfig clang-23
xtensa allyesconfig gcc-15.2.0
xtensa randconfig-001-20260513 gcc-11.5.0
xtensa randconfig-002-20260513 gcc-11.5.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [PATCH v5] Bluetooth: btrtl: Add firmware format v3 support
From: Hilda Wu @ 2026-05-13 9:24 UTC (permalink / raw)
To: marcel
Cc: luiz.dentz, linux-bluetooth, linux-kernel, alex_lu, jason_mao,
zoey_zhou, max.chou
Realtek updated its Bluetooth firmware format to v3.
This patch extends the btrtl driver to recognise and parse the new v3 file
format, including:
- New signature string and image ID definitions
- Extension of btrtl_device_info to store v3-specific metadata
- Logic to extract and load firmware data out of v3 images
- Maintains compatibility with existing v2 firmware format
This is required for future Realtek Bluetooth chips that ship with
v3 firmware.
The RTL8922D is the first IC to use firmware format V3, so the following
example uses the RTL8922D's log as expected fw format v3 output:
Bluetooth: btrtl_read_chip_id() hci0: RTL: chip_id status=0x00 id=0x37
Bluetooth: btrtl_initialize() hci0: RTL: examining hci_ver=0d
hci_rev=000d lmp_ver=0d lmp_subver=8922
Bluetooth: rtl_read_rom_version() hci0: RTL: rom_version status=0 version=1
Bluetooth: btrtl_initialize() hci0: RTL: btrtl_initialize: key id 0
Bluetooth: rtl_load_file() hci0: RTL: loading rtl_bt/rtl8922du_fw.bin
Bluetooth: rtl_load_file() hci0: RTL: loading rtl_bt/rtl8922du_config.bin
Bluetooth: rtlbt_parse_firmware_v3() hci0: RTL: key id 0
Bluetooth: rtlbt_parse_section_v3() hci0: RTL: image (f000:00), chip id
55, cut 0x02, len 00007185
Bluetooth: rtlbt_parse_section_v3() hci0: RTL: image version: 35fd7908
Bluetooth: rtlbt_parse_config() hci0: RTL: config file:
rtl_bt/rtl8922du_config_f000.bin
Bluetooth: rtlbt_parse_section_v3() hci0: RTL: image (f002:00), chip id
55, cut 0x02, len 000078f5
Bluetooth: rtlbt_parse_section_v3() hci0: RTL: image version: 47b6874d
Bluetooth: rtlbt_parse_config() hci0: RTL: config file:
rtl_bt/rtl8922du_config_f002.bin
Bluetooth: rtlbt_parse_firmware_v3() hci0: RTL: image payload total len:
0x0000ea7a
Bluetooth: rtl_finalize_download() hci0: RTL: Watchdog reset status 00
Bluetooth: rtl_finalize_download() hci0: RTL: fw version 0x47b6874d
Signed-off-by: Alex Lu <alex_lu@realsil.com.cn>
Signed-off-by: Zoey Zhou <zoey_zhou@realsil.com.cn>
Signed-off-by: Hilda Wu <hildawu@realtek.com>
---
Change in V5:
- Independent support for 8922D section
- Define relevant macros to increase readability
- Added format v3 description and differences
- Adjusted according to the recommendations
- Adjust to use kzalloc_obj
Change in V4:
- Modify access to skb->data and add descriptions
- Fix hidden issues
Change in V3:
- Fixed cocci warning
Change in V2:
- Fill in the missing symbols
- Fix build warnings
---
drivers/bluetooth/btrtl.c | 698 +++++++++++++++++++++++++++++++++++++-
drivers/bluetooth/btrtl.h | 102 ++++++
drivers/bluetooth/btusb.c | 3 +
3 files changed, 786 insertions(+), 17 deletions(-)
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 49ecb18fea45..450b32bcfa63 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -22,6 +22,12 @@
#define RTL_CHIP_8723CS_XX 5
#define RTL_EPATCH_SIGNATURE "Realtech"
#define RTL_EPATCH_SIGNATURE_V2 "RTBTCore"
+#define RTL_EPATCH_SIGNATURE_V3 "BTNIC003"
+#define RTL_PATCH_V3_1 0x01
+#define RTL_PATCH_V3_2 0x02
+#define IMAGE_ID_F000 0xf000
+#define IMAGE_ID_F001 0xf001
+#define IMAGE_ID_F002 0xf002
#define RTL_ROM_LMP_8703B 0x8703
#define RTL_ROM_LMP_8723A 0x1200
#define RTL_ROM_LMP_8723B 0x8723
@@ -33,7 +39,14 @@
#define RTL_ROM_LMP_8922A 0x8922
#define RTL_CONFIG_MAGIC 0x8723ab55
-#define RTL_VSC_OP_COREDUMP 0xfcff
+#define RTL_VSC_OP_DOWNLOAD_CMD 0xfc20
+#define RTL_VSC_OP_READ_VENDER 0xfc61
+#define RTL_VSC_OP_WRITE_VENDOR 0xfc62
+#define RTL_VSC_OP_READ_ROM_VER 0xfc6d
+#define RTL_VSC_OP_READ_CHIP_ID 0xfc6f
+#define RTL_VSC_OP_COREDUMP 0xfcff
+#define RTL_VSC_OP_CHECK_DOWNLOAD_STATE 0xfdcf
+#define RTL_VSC_OP_WDG_RESET_CMD 0xfc8e
#define IC_MATCH_FL_LMPSUBV (1 << 0)
#define IC_MATCH_FL_HCIREV (1 << 1)
@@ -50,12 +63,16 @@
#define RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}})
#define RTL_CHIP_REV (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}})
-#define RTL_SEC_PROJ (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0xAD, 0x00, 0xb0}})
+#define RTL_SEC_PROJ_V2 (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0xAD, 0x00, 0xb0}})
+#define RTL_SEC_PROJ_V3 (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0x0D, 0x01, 0xa0}})
#define RTL_PATCH_SNIPPETS 0x01
#define RTL_PATCH_DUMMY_HEADER 0x02
#define RTL_PATCH_SECURITY_HEADER 0x03
+#define CHIP_ID_V3_BASE 55
+#define RTL_VENDOR_WRITE_TYPE 0x21
+
enum btrtl_chip_id {
CHIP_ID_8723A,
CHIP_ID_8723B,
@@ -99,8 +116,11 @@ struct btrtl_device_info {
int cfg_len;
bool drop_fw;
int project_id;
+ u32 opcode;
+ u8 fw_type;
u8 key_id;
struct list_head patch_subsecs;
+ struct list_head patch_images;
};
static const struct id_table ic_id_table[] = {
@@ -371,6 +391,33 @@ static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
return &ic_id_table[i];
}
+static int btrtl_read_chip_id(struct hci_dev *hdev, u8 *chip_id)
+{
+ struct rtl_rp_read_chip_id *rp;
+ struct sk_buff *skb;
+ int ret = 0;
+
+ skb = __hci_cmd_sync(hdev, RTL_VSC_OP_READ_CHIP_ID, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ rp = skb_pull_data(skb, sizeof(*rp));
+ if (!rp) {
+ ret = -EIO;
+ goto out;
+ }
+
+ rtl_dev_info(hdev, "chip_id status=0x%02x id=0x%02x",
+ rp->status, rp->chip_id);
+
+ if (chip_id)
+ *chip_id = rp->chip_id;
+
+out:
+ kfree_skb(skb);
+ return ret;
+}
+
static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev)
{
struct sk_buff *skb;
@@ -397,8 +444,7 @@ static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
struct rtl_rom_version_evt *rom_version;
struct sk_buff *skb;
- /* Read RTL ROM version command */
- skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT);
+ skb = __hci_cmd_sync(hdev, RTL_VSC_OP_READ_ROM_VER, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
rtl_dev_err(hdev, "Read ROM version failed (%ld)",
PTR_ERR(skb));
@@ -427,7 +473,7 @@ static int btrtl_vendor_read_reg16(struct hci_dev *hdev,
struct sk_buff *skb;
int err = 0;
- skb = __hci_cmd_sync(hdev, 0xfc61, sizeof(*cmd), cmd,
+ skb = __hci_cmd_sync(hdev, RTL_VSC_OP_READ_VENDER, sizeof(*cmd), cmd,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
@@ -449,6 +495,26 @@ static int btrtl_vendor_read_reg16(struct hci_dev *hdev,
return 0;
}
+static int btrtl_vendor_write_mem(struct hci_dev *hdev, u32 addr, u32 val)
+{
+ struct rtl_vendor_write_cmd cp;
+ struct sk_buff *skb;
+ int err = 0;
+
+ cp.type = RTL_VENDOR_WRITE_TYPE;
+ cp.addr = cpu_to_le32(addr);
+ cp.val = cpu_to_le32(val);
+ skb = __hci_cmd_sync(hdev, RTL_VSC_OP_WRITE_VENDOR, sizeof(cp), &cp, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ bt_dev_err(hdev, "RTL: Write mem32 failed (%d)", err);
+ return err;
+ }
+
+ kfree_skb(skb);
+ return 0;
+}
+
static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len)
{
void *data = iov->data;
@@ -462,6 +528,31 @@ static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len)
return data;
}
+
+static void btrtl_insert_ordered_patch_image(struct rtl_section_patch_image *image,
+ struct btrtl_device_info *btrtl_dev)
+{
+ struct list_head *pos;
+ struct list_head *next;
+ struct rtl_section_patch_image *node;
+
+ list_for_each_safe(pos, next, &btrtl_dev->patch_images) {
+ node = list_entry(pos, struct rtl_section_patch_image, list);
+
+ if (node->image_id > image->image_id) {
+ __list_add(&image->list, pos->prev, pos);
+ return;
+ }
+
+ if (node->image_id == image->image_id &&
+ node->index > image->index) {
+ __list_add(&image->list, pos->prev, pos);
+ return;
+ }
+ }
+ __list_add(&image->list, pos->prev, pos);
+}
+
static void btrtl_insert_ordered_subsec(struct rtl_subsection *node,
struct btrtl_device_info *btrtl_dev)
{
@@ -633,6 +724,279 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
}
*_buf = ptr;
+ btrtl_dev->fw_type = FW_TYPE_V2;
+ return len;
+}
+
+static int rtlbt_parse_config(struct hci_dev *hdev,
+ struct rtl_section_patch_image *patch_image,
+ struct btrtl_device_info *btrtl_dev)
+{
+ const struct id_table *ic_info = NULL;
+ const struct firmware *fw;
+ char tmp_name[32];
+ char filename[64];
+ u8 *cfg_buf;
+ char *str;
+ char *p;
+ size_t len;
+ int ret;
+
+ if (btrtl_dev && btrtl_dev->ic_info)
+ ic_info = btrtl_dev->ic_info;
+
+ if (!ic_info)
+ return -EINVAL;
+
+ str = ic_info->cfg_name;
+ if (btrtl_dev->fw_type == FW_TYPE_V3_1) {
+ if (!patch_image->image_id && !patch_image->index) {
+ snprintf(filename, sizeof(filename), "%s.bin", str);
+ goto load_fw;
+ }
+ goto done;
+ }
+
+ len = strlen(str);
+ if (len > sizeof(tmp_name) - 1)
+ len = sizeof(tmp_name) - 1;
+ memcpy(tmp_name, str, len);
+ tmp_name[len] = '\0';
+
+ str = tmp_name;
+ p = strsep(&str, ".");
+
+ ret = snprintf(filename, sizeof(filename), "%s", p);
+ if (patch_image->config_rule && patch_image->need_config) {
+ switch (patch_image->image_id) {
+ case IMAGE_ID_F000:
+ case IMAGE_ID_F001:
+ case IMAGE_ID_F002:
+ ret += snprintf(filename + ret, sizeof(filename) - ret,
+ "_%04x", patch_image->image_id);
+ break;
+ default:
+ goto done;
+ }
+ } else {
+ goto done;
+ }
+
+ snprintf(filename + ret, sizeof(filename) - ret, ".%s", str ? str : "bin");
+
+load_fw:
+ rtl_dev_info(hdev, "config file: %s", filename);
+ ret = request_firmware(&fw, filename, &hdev->dev);
+ if (ret < 0) {
+ if (btrtl_dev->fw_type == FW_TYPE_V3_2) {
+ len = 4;
+ cfg_buf = kvmalloc(len, GFP_KERNEL);
+ if (!cfg_buf)
+ return -ENOMEM;
+
+ memset(cfg_buf, 0xff, len);
+ patch_image->cfg_buf = cfg_buf;
+ patch_image->cfg_len = len;
+ return 0;
+ }
+ goto err_req_fw;
+ }
+ rtl_dev_info(hdev, "config file: %s found", filename);
+ cfg_buf = kvmalloc(fw->size, GFP_KERNEL);
+ if (!cfg_buf) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ memcpy(cfg_buf, fw->data, fw->size);
+ len = fw->size;
+ release_firmware(fw);
+
+ patch_image->cfg_buf = cfg_buf;
+ patch_image->cfg_len = len;
+done:
+ return 0;
+err:
+ release_firmware(fw);
+err_req_fw:
+ rtl_dev_info(hdev, "config file: [%s] not found", filename);
+ return ret;
+}
+
+static int rtlbt_parse_section_v3(struct hci_dev *hdev,
+ struct btrtl_device_info *btrtl_dev,
+ u32 opcode, u8 *data, u32 len)
+{
+ struct rtl_section_patch_image *patch_image;
+ struct rtl_patch_image_hdr *hdr;
+ u16 image_id;
+ u16 chip_id;
+ size_t patch_image_len;
+ u8 *ptr;
+ int ret = 0;
+ size_t i;
+ struct rtl_iovec iov = {
+ .data = data,
+ .len = len,
+ };
+
+ hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
+ if (!hdr)
+ return -EINVAL;
+
+ if (btrtl_dev->opcode && btrtl_dev->opcode != opcode) {
+ rtl_dev_err(hdev, "invalid opcode 0x%02x", opcode);
+ return -EINVAL;
+ }
+
+ if (!btrtl_dev->opcode) {
+ btrtl_dev->opcode = opcode;
+ switch (btrtl_dev->opcode) {
+ case RTL_PATCH_V3_1:
+ btrtl_dev->fw_type = FW_TYPE_V3_1;
+ break;
+ case RTL_PATCH_V3_2:
+ btrtl_dev->fw_type = FW_TYPE_V3_2;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ patch_image_len = (u32)le64_to_cpu(hdr->patch_image_len);
+ chip_id = le16_to_cpu(hdr->chip_id);
+ image_id = le16_to_cpu(hdr->image_id);
+ rtl_dev_info(hdev, "image (%04x:%02x), chip id %u, cut 0x%02x, len %08zx"
+ , image_id, hdr->index, chip_id, hdr->ic_cut,
+ patch_image_len);
+
+ if (btrtl_dev->key_id != hdr->key_id) {
+ rtl_dev_err(hdev, "invalid key_id (%u, %u)", hdr->key_id,
+ btrtl_dev->key_id);
+ return -EINVAL;
+ }
+
+ if (hdr->ic_cut != btrtl_dev->rom_version + 1) {
+ rtl_dev_info(hdev, "unused ic_cut (%u, %u)", hdr->ic_cut,
+ btrtl_dev->rom_version + 1);
+ return -EINVAL;
+ }
+
+ if (btrtl_dev->fw_type == FW_TYPE_V3_1 && !btrtl_dev->project_id)
+ btrtl_dev->project_id = chip_id;
+
+ if (btrtl_dev->fw_type == FW_TYPE_V3_2 &&
+ chip_id != btrtl_dev->project_id) {
+ rtl_dev_err(hdev, "invalid chip_id (%u, %d)", chip_id,
+ btrtl_dev->project_id);
+ return -EINVAL;
+ }
+
+ ptr = rtl_iov_pull_data(&iov, patch_image_len);
+ if (!ptr)
+ return -ENODATA;
+
+ patch_image = kzalloc_obj(*patch_image);
+ if (!patch_image)
+ return -ENOMEM;
+ patch_image->index = hdr->index;
+ patch_image->image_id = image_id;
+ patch_image->config_rule = hdr->config_rule;
+ patch_image->need_config = hdr->need_config;
+
+ for (i = 0; i < DL_FIX_ADDR_MAX; i++) {
+ patch_image->fix[i].addr =
+ (u32)le64_to_cpu(hdr->addr_fix[i * 2]);
+ patch_image->fix[i].value =
+ (u32)le64_to_cpu(hdr->addr_fix[i * 2 + 1]);
+ }
+
+ patch_image->image_len = patch_image_len;
+ patch_image->image_data = kvmalloc(patch_image_len, GFP_KERNEL);
+ if (!patch_image->image_data) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ memcpy(patch_image->image_data, ptr, patch_image_len);
+ patch_image->image_ver =
+ get_unaligned_le32(ptr + patch_image->image_len - 4);
+ rtl_dev_info(hdev, "image version: %08x", patch_image->image_ver);
+
+ rtlbt_parse_config(hdev, patch_image, btrtl_dev);
+
+ ret = patch_image->image_len;
+
+ btrtl_insert_ordered_patch_image(patch_image, btrtl_dev);
+
+ return ret;
+err:
+ kfree(patch_image);
+ return ret;
+}
+
+static int rtlbt_parse_firmware_v3(struct hci_dev *hdev,
+ struct btrtl_device_info *btrtl_dev)
+{
+ struct rtl_epatch_header_v3 *hdr;
+ int rc;
+ u32 num_sections;
+ struct rtl_section_v3 *section;
+ u32 section_len;
+ u32 opcode;
+ int len = 0;
+ int i;
+ u8 *ptr;
+ struct rtl_iovec iov = {
+ .data = btrtl_dev->fw_data,
+ .len = btrtl_dev->fw_len,
+ };
+
+ rtl_dev_info(hdev, "key id %u", btrtl_dev->key_id);
+
+ hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
+ if (!hdr)
+ return -EINVAL;
+ num_sections = le32_to_cpu(hdr->num_sections);
+
+ rtl_dev_dbg(hdev, "timpstamp %08x-%08x", *((u32 *)hdr->timestamp),
+ *((u32 *)(hdr->timestamp + 4)));
+
+ for (i = 0; i < num_sections; i++) {
+ section = rtl_iov_pull_data(&iov, sizeof(*section));
+ if (!section)
+ break;
+
+ section_len = (u32)le64_to_cpu(section->len);
+ opcode = le32_to_cpu(section->opcode);
+
+ rtl_dev_dbg(hdev, "opcode 0x%04x", section->opcode);
+
+ ptr = rtl_iov_pull_data(&iov, section_len);
+ if (!ptr)
+ break;
+
+ rc = 0;
+ switch (opcode) {
+ case RTL_PATCH_V3_1:
+ case RTL_PATCH_V3_2:
+ rc = rtlbt_parse_section_v3(hdev, btrtl_dev, opcode,
+ ptr, section_len);
+ break;
+ default:
+ rtl_dev_warn(hdev, "Unknown opcode %08x", opcode);
+ break;
+ }
+ if (rc < 0) {
+ rtl_dev_err(hdev, "Parse section (%u) err (%d)",
+ opcode, rc);
+ continue;
+ }
+ len += rc;
+ }
+
+ rtl_dev_info(hdev, "image payload total len: 0x%08x", len);
+ if (!len)
+ return -ENODATA;
+
return len;
}
@@ -678,6 +1042,9 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
if (btrtl_dev->fw_len <= 8)
return -EINVAL;
+ if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V3, 8))
+ return rtlbt_parse_firmware_v3(hdev, btrtl_dev);
+
if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8))
min_size = sizeof(struct rtl_epatch_header) +
sizeof(extension_sig) + 3;
@@ -813,10 +1180,11 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4);
*_buf = buf;
+ btrtl_dev->fw_type = FW_TYPE_V1;
return len;
}
-static int rtl_download_firmware(struct hci_dev *hdev,
+static int rtl_download_firmware(struct hci_dev *hdev, u8 fw_type,
const unsigned char *data, int fw_len)
{
struct rtl_download_cmd *dl_cmd;
@@ -827,6 +1195,13 @@ static int rtl_download_firmware(struct hci_dev *hdev,
int j = 0;
struct sk_buff *skb;
struct hci_rp_read_local_version *rp;
+ u8 dl_rp_len = sizeof(struct rtl_download_response);
+
+ if (is_v3_fw(fw_type)) {
+ j = 1;
+ if (fw_type == FW_TYPE_V3_2)
+ dl_rp_len++;
+ }
dl_cmd = kmalloc_obj(*dl_cmd);
if (!dl_cmd)
@@ -840,15 +1215,15 @@ static int rtl_download_firmware(struct hci_dev *hdev,
j = 1;
if (i == (frag_num - 1)) {
- dl_cmd->index |= 0x80; /* data end */
+ if (!is_v3_fw(fw_type))
+ dl_cmd->index |= 0x80; /* data end */
frag_len = fw_len % RTL_FRAG_LEN;
}
rtl_dev_dbg(hdev, "download fw (%d/%d). index = %d", i,
frag_num, dl_cmd->index);
memcpy(dl_cmd->data, data, frag_len);
- /* Send download command */
- skb = __hci_cmd_sync(hdev, 0xfc20, frag_len + 1, dl_cmd,
+ skb = __hci_cmd_sync(hdev, RTL_VSC_OP_DOWNLOAD_CMD, frag_len + 1, dl_cmd,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
rtl_dev_err(hdev, "download fw command failed (%ld)",
@@ -857,7 +1232,7 @@ static int rtl_download_firmware(struct hci_dev *hdev,
goto out;
}
- if (skb->len != sizeof(struct rtl_download_response)) {
+ if (skb->len != dl_rp_len) {
rtl_dev_err(hdev, "download fw event length mismatch");
kfree_skb(skb);
ret = -EIO;
@@ -868,6 +1243,9 @@ static int rtl_download_firmware(struct hci_dev *hdev,
data += RTL_FRAG_LEN;
}
+ if (is_v3_fw(fw_type))
+ goto out;
+
skb = btrtl_read_local_version(hdev);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
@@ -885,6 +1263,237 @@ static int rtl_download_firmware(struct hci_dev *hdev,
return ret;
}
+static int rtl_check_download_state(struct hci_dev *hdev,
+ struct btrtl_device_info *btrtl_dev)
+{
+ struct sk_buff *skb;
+ int ret = 0;
+ u8 *state;
+
+ skb = __hci_cmd_sync(hdev, RTL_VSC_OP_CHECK_DOWNLOAD_STATE, 0, NULL, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ rtl_dev_err(hdev, "write tb error %lu", PTR_ERR(skb));
+ return -EIO;
+ }
+
+ /* Other driver might be downloading the combined firmware. */
+ state = skb_pull_data(skb, sizeof(*state));
+ if (state && *state == 0x03) {
+ btrealtek_set_flag(hdev, REALTEK_DOWNLOADING);
+ ret = btrealtek_wait_on_flag_timeout(hdev, REALTEK_DOWNLOADING,
+ TASK_INTERRUPTIBLE,
+ msecs_to_jiffies(5000));
+ if (ret == -EINTR) {
+ bt_dev_err(hdev, "Firmware loading interrupted");
+ goto out;
+ }
+
+ if (ret) {
+ bt_dev_err(hdev, "Firmware loading timeout");
+ ret = -ETIMEDOUT;
+ } else {
+ ret = -EALREADY;
+ }
+
+ }
+
+out:
+ kfree_skb(skb);
+ return ret;
+}
+
+static int rtl_finalize_download(struct hci_dev *hdev,
+ struct btrtl_device_info *btrtl_dev)
+{
+ struct hci_rp_read_local_version *rp_ver;
+ u8 params[2] = { 0x03, 0xb2 };
+ struct sk_buff *skb;
+ int ret = 0;
+ u16 opcode;
+ u32 len;
+ u8 *p;
+
+ opcode = RTL_VSC_OP_WDG_RESET_CMD;
+ len = 2;
+ if (btrtl_dev->opcode == RTL_PATCH_V3_1) {
+ opcode = RTL_VSC_OP_DOWNLOAD_CMD;
+ params[0] = 0x80;
+ len = 1;
+ }
+ skb = __hci_cmd_sync(hdev, opcode, len, params, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ rtl_dev_err(hdev, "Watchdog reset err (%ld)", PTR_ERR(skb));
+ return -EIO;
+ }
+ p = skb_pull_data(skb, 1);
+ if (!p) {
+ ret = -ENODATA;
+ goto out;
+ }
+ rtl_dev_info(hdev, "Watchdog reset status %02x", *p);
+ kfree_skb(skb);
+
+ skb = btrtl_read_local_version(hdev);
+ if (IS_ERR(skb)) {
+ ret = PTR_ERR(skb);
+ rtl_dev_err(hdev, "read local version failed (%d)", ret);
+ return ret;
+ }
+
+ rp_ver = skb_pull_data(skb, sizeof(*rp_ver));
+ if (rp_ver)
+ rtl_dev_info(hdev, "fw version 0x%04x%04x",
+ __le16_to_cpu(rp_ver->hci_rev),
+ __le16_to_cpu(rp_ver->lmp_subver));
+out:
+ kfree_skb(skb);
+ return ret;
+}
+
+static int rtl_security_check(struct hci_dev *hdev,
+ struct btrtl_device_info *btrtl_dev)
+{
+ struct rtl_section_patch_image *tmp = NULL;
+ struct rtl_section_patch_image *image = NULL;
+ u32 val;
+ int ret;
+
+ list_for_each_entry_reverse(tmp, &btrtl_dev->patch_images, list) {
+ /* Check security hdr */
+ if (!tmp->fix[DL_FIX_SEC_HDR_ADDR].value ||
+ !tmp->fix[DL_FIX_SEC_HDR_ADDR].addr ||
+ tmp->fix[DL_FIX_SEC_HDR_ADDR].addr == 0xffffffff)
+ continue;
+ rtl_dev_info(hdev, "addr 0x%08x, value 0x%08x",
+ tmp->fix[DL_FIX_SEC_HDR_ADDR].addr,
+ tmp->fix[DL_FIX_SEC_HDR_ADDR].value);
+ image = tmp;
+ break;
+ }
+
+ if (!image)
+ return 0;
+
+ rtl_dev_info(hdev, "sec image (%04x:%02x)", image->image_id,
+ image->index);
+ val = image->fix[DL_FIX_PATCH_ADDR].value + image->image_len -
+ image->fix[DL_FIX_SEC_HDR_ADDR].value;
+ ret = btrtl_vendor_write_mem(hdev, image->fix[DL_FIX_PATCH_ADDR].addr,
+ val);
+ if (ret) {
+ rtl_dev_err(hdev, "write sec reg failed (%d)", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int rtl_download_firmware_v3(struct hci_dev *hdev,
+ struct btrtl_device_info *btrtl_dev)
+{
+ struct rtl_section_patch_image *image, *tmp;
+ struct rtl_rp_dl_v3 *rp;
+ struct sk_buff *skb;
+ u8 *fw_data;
+ int fw_len;
+ int ret = 0;
+ u8 i;
+
+ if (btrtl_dev->fw_type == FW_TYPE_V3_2) {
+ ret = rtl_check_download_state(hdev, btrtl_dev);
+ if (ret) {
+ if (ret == -EALREADY)
+ return 0;
+ return ret;
+ }
+ }
+
+ list_for_each_entry_safe(image, tmp, &btrtl_dev->patch_images, list) {
+ rtl_dev_dbg(hdev, "image (%04x:%02x)", image->image_id,
+ image->index);
+
+ for (i = DL_FIX_CI_ID; i < DL_FIX_ADDR_MAX; i++) {
+ if (!image->fix[i].addr ||
+ image->fix[i].addr == 0xffffffff) {
+ rtl_dev_dbg(hdev, "no need to write addr %08x",
+ image->fix[i].addr);
+ continue;
+ }
+ rtl_dev_dbg(hdev, "write addr and val, 0x%08x, 0x%08x",
+ image->fix[i].addr, image->fix[i].value);
+ if (btrtl_vendor_write_mem(hdev, image->fix[i].addr,
+ image->fix[i].value)) {
+ rtl_dev_err(hdev, "write reg failed");
+ ret = -EIO;
+ goto done;
+ }
+ }
+
+ fw_len = image->image_len + image->cfg_len;
+ fw_data = kvmalloc(fw_len, GFP_KERNEL);
+ if (!fw_data) {
+ rtl_dev_err(hdev, "Couldn't alloc buf for image data");
+ ret = -ENOMEM;
+ goto done;
+ }
+ memcpy(fw_data, image->image_data, image->image_len);
+ if (image->cfg_len > 0)
+ memcpy(fw_data + image->image_len, image->cfg_buf,
+ image->cfg_len);
+
+ rtl_dev_dbg(hdev, "patch image (%04x:%02x). len: %d",
+ image->image_id, image->index, fw_len);
+ rtl_dev_dbg(hdev, "fw_data %p, image buf %p, len %u", fw_data,
+ image->image_data, image->image_len);
+
+ ret = rtl_download_firmware(hdev, btrtl_dev->fw_type, fw_data,
+ fw_len);
+ kvfree(fw_data);
+ if (ret < 0) {
+ rtl_dev_err(hdev, "download firmware failed (%d)", ret);
+ goto done;
+ }
+
+ if (image->list.next != &btrtl_dev->patch_images &&
+ image->image_id == tmp->image_id)
+ continue;
+
+ if (btrtl_dev->fw_type == FW_TYPE_V3_1)
+ continue;
+
+ i = 0x80;
+ skb = __hci_cmd_sync(hdev, RTL_VSC_OP_DOWNLOAD_CMD, 1, &i, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ ret = -EIO;
+ rtl_dev_err(hdev, "Failed to issue last cmd fc20, %ld",
+ PTR_ERR(skb));
+ goto done;
+ }
+ ret = 2;
+ rp = skb_pull_data(skb, sizeof(*rp));
+ if (rp)
+ ret = rp->err;
+ kfree_skb(skb);
+ if (ret == 2) {
+ /* Verification failure */
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ if (btrtl_dev->fw_type == FW_TYPE_V3_1) {
+ ret = rtl_security_check(hdev, btrtl_dev);
+ if (ret) {
+ rtl_dev_err(hdev, "Security check failed (%d)", ret);
+ goto done;
+ }
+ }
+
+ ret = rtl_finalize_download(hdev, btrtl_dev);
+
+done:
+ return ret;
+}
+
static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff)
{
const struct firmware *fw;
@@ -918,7 +1527,7 @@ static int btrtl_setup_rtl8723a(struct hci_dev *hdev,
return -EINVAL;
}
- return rtl_download_firmware(hdev, btrtl_dev->fw_data,
+ return rtl_download_firmware(hdev, FW_TYPE_V0, btrtl_dev->fw_data,
btrtl_dev->fw_len);
}
@@ -933,7 +1542,7 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
if (ret < 0)
goto out;
- if (btrtl_dev->cfg_len > 0) {
+ if (!is_v3_fw(btrtl_dev->fw_type) && btrtl_dev->cfg_len > 0) {
tbuff = kvzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL);
if (!tbuff) {
ret = -ENOMEM;
@@ -949,9 +1558,14 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
fw_data = tbuff;
}
+ if (is_v3_fw(btrtl_dev->fw_type)) {
+ ret = rtl_download_firmware_v3(hdev, btrtl_dev);
+ goto out;
+ }
+
rtl_dev_info(hdev, "cfg_sz %d, total sz %d", btrtl_dev->cfg_len, ret);
- ret = rtl_download_firmware(hdev, fw_data, ret);
+ ret = rtl_download_firmware(hdev, btrtl_dev->fw_type, fw_data, ret);
out:
kvfree(fw_data);
@@ -1021,7 +1635,7 @@ static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type)
const unsigned char cmd_buf[] = {0x00, 0x94, 0xa0, 0x00, 0xb0};
/* Read RTL chip type command */
- skb = __hci_cmd_sync(hdev, 0xfc61, 5, cmd_buf, HCI_INIT_TIMEOUT);
+ skb = __hci_cmd_sync(hdev, RTL_VSC_OP_READ_VENDER, 5, cmd_buf, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
rtl_dev_err(hdev, "Read chip type failed (%ld)",
PTR_ERR(skb));
@@ -1047,6 +1661,7 @@ static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type)
void btrtl_free(struct btrtl_device_info *btrtl_dev)
{
struct rtl_subsection *entry, *tmp;
+ struct rtl_section_patch_image *image, *next;
kvfree(btrtl_dev->fw_data);
kvfree(btrtl_dev->cfg_data);
@@ -1056,6 +1671,13 @@ void btrtl_free(struct btrtl_device_info *btrtl_dev)
kfree(entry);
}
+ list_for_each_entry_safe(image, next, &btrtl_dev->patch_images, list) {
+ list_del(&image->list);
+ kvfree(image->image_data);
+ kvfree(image->cfg_buf);
+ kfree(image);
+ }
+
kfree(btrtl_dev);
}
EXPORT_SYMBOL_GPL(btrtl_free);
@@ -1063,7 +1685,7 @@ EXPORT_SYMBOL_GPL(btrtl_free);
struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
const char *postfix)
{
- struct btrealtek_data *coredump_info = hci_get_priv(hdev);
+ struct btrealtek_data *btrtl_data = hci_get_priv(hdev);
struct btrtl_device_info *btrtl_dev;
struct sk_buff *skb;
struct hci_rp_read_local_version *resp;
@@ -1072,6 +1694,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
char cfg_name[40];
u16 hci_rev, lmp_subver;
u8 hci_ver, lmp_ver, chip_type = 0;
+ u8 chip_id = 0;
int ret;
int rc;
u8 key_id;
@@ -1084,8 +1707,15 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
}
INIT_LIST_HEAD(&btrtl_dev->patch_subsecs);
+ INIT_LIST_HEAD(&btrtl_dev->patch_images);
check_version:
+ ret = btrtl_read_chip_id(hdev, &chip_id);
+ if (!ret && chip_id >= CHIP_ID_V3_BASE) {
+ btrtl_dev->project_id = chip_id;
+ goto read_local_ver;
+ }
+
ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_SUBVER, reg_val);
if (ret < 0)
goto err_free;
@@ -1108,6 +1738,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
}
}
+read_local_ver:
skb = btrtl_read_local_version(hdev);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
@@ -1185,7 +1816,11 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
goto err_free;
}
- rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
+ if (btrtl_dev->project_id >= CHIP_ID_V3_BASE)
+ rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ_V3, reg_val);
+ else
+ rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ_V2, reg_val);
+
if (rc < 0)
goto err_free;
@@ -1243,7 +1878,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
hci_set_msft_opcode(hdev, 0xFCF0);
if (btrtl_dev->ic_info)
- coredump_info->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
+ btrtl_data->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
return btrtl_dev;
@@ -1416,6 +2051,35 @@ int btrtl_shutdown_realtek(struct hci_dev *hdev)
}
EXPORT_SYMBOL_GPL(btrtl_shutdown_realtek);
+
+int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+ struct hci_event_hdr *hdr;
+ u8 *p;
+
+ if (!clone)
+ goto out;
+
+ hdr = skb_pull_data(clone, sizeof(*hdr));
+ if (!hdr || hdr->evt != HCI_VENDOR_PKT)
+ goto out;
+
+ p = skb_pull_data(clone, 1);
+ if (!p)
+ goto out;
+ switch (*p) {
+ case 0x77:
+ if (btrealtek_test_and_clear_flag(hdev, REALTEK_DOWNLOADING))
+ btrealtek_wake_up_flag(hdev, REALTEK_DOWNLOADING);
+ break;
+ }
+out:
+ consume_skb(clone);
+ return hci_recv_frame(hdev, skb);
+}
+EXPORT_SYMBOL_GPL(btrtl_recv_event);
+
static unsigned int btrtl_convert_baudrate(u32 device_baudrate)
{
switch (device_baudrate) {
diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h
index a2d9d34f9fb0..dd4acdf29b2f 100644
--- a/drivers/bluetooth/btrtl.h
+++ b/drivers/bluetooth/btrtl.h
@@ -12,6 +12,19 @@
#define rtl_dev_info(dev, fmt, ...) bt_dev_info(dev, "RTL: " fmt, ##__VA_ARGS__)
#define rtl_dev_dbg(dev, fmt, ...) bt_dev_dbg(dev, "RTL: " fmt, ##__VA_ARGS__)
+#define FW_TYPE_V0 0
+#define FW_TYPE_V1 1
+#define FW_TYPE_V2 2
+#define FW_TYPE_V3_1 3
+#define FW_TYPE_V3_2 4
+#define is_v3_fw(type) (type == FW_TYPE_V3_1 || type == FW_TYPE_V3_2)
+
+#define DL_FIX_CI_ID 0
+#define DL_FIX_CI_ADDR 1
+#define DL_FIX_PATCH_ADDR 2
+#define DL_FIX_SEC_HDR_ADDR 3
+#define DL_FIX_ADDR_MAX 4
+
struct btrtl_device_info;
struct rtl_chip_type_evt {
@@ -103,8 +116,79 @@ struct rtl_vendor_cmd {
__u8 param[5];
} __packed;
+struct rtl_vendor_write_cmd {
+ u8 type;
+ __le32 addr;
+ __le32 val;
+} __packed;
+
+struct rtl_rp_read_chip_id {
+ __u8 status;
+ __u8 chip_id;
+} __packed;
+
+struct rtl_rp_dl_v3 {
+ __u8 status;
+ __u8 index;
+ __u8 err;
+} __packed;
+
+struct rtl_epatch_header_v3 {
+ __u8 signature[8];
+ __u8 timestamp[8];
+ __le32 ver_rsvd;
+ __le32 num_sections;
+} __packed;
+
+struct rtl_section_v3 {
+ __le32 opcode;
+ __le64 len;
+ u8 data[];
+} __packed;
+
+struct rtl_addr_fix {
+ u32 addr;
+ u32 value;
+};
+
+struct rtl_section_patch_image {
+ u16 image_id;
+ u8 index;
+ u8 config_rule;
+ u8 need_config;
+
+ struct rtl_addr_fix fix[DL_FIX_ADDR_MAX];
+
+ u32 image_len;
+ u8 *image_data;
+ u32 image_ver;
+
+ u8 *cfg_buf;
+ u16 cfg_len;
+
+ struct list_head list;
+};
+
+struct rtl_patch_image_hdr {
+ __le16 chip_id;
+ u8 ic_cut;
+ u8 key_id;
+ u8 enable_ota;
+ __le16 image_id;
+ u8 config_rule;
+ u8 need_config;
+ u8 rsv[950];
+
+ __le64 addr_fix[DL_FIX_ADDR_MAX * 2];
+ u8 index;
+
+ __le64 patch_image_len;
+ __u8 data[];
+} __packed;
+
enum {
REALTEK_ALT6_CONTINUOUS_TX_CHIP,
+ REALTEK_DOWNLOADING,
__REALTEK_NUM_FLAGS,
};
@@ -130,8 +214,20 @@ struct btrealtek_data {
#define btrealtek_get_flag(hdev) \
(((struct btrealtek_data *)hci_get_priv(hdev))->flags)
+#define btrealtek_wake_up_flag(hdev, nr) \
+ do { \
+ struct btrealtek_data *rtl = hci_get_priv((hdev)); \
+ wake_up_bit(rtl->flags, (nr)); \
+ } while (0)
+
#define btrealtek_test_flag(hdev, nr) test_bit((nr), btrealtek_get_flag(hdev))
+#define btrealtek_test_and_clear_flag(hdev, nr) \
+ test_and_clear_bit((nr), btrealtek_get_flag(hdev))
+
+#define btrealtek_wait_on_flag_timeout(hdev, nr, m, to) \
+ wait_on_bit_timeout(btrealtek_get_flag(hdev), (nr), m, to)
+
#if IS_ENABLED(CONFIG_BT_RTL)
struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
@@ -148,6 +244,7 @@ int btrtl_get_uart_settings(struct hci_dev *hdev,
unsigned int *controller_baudrate,
u32 *device_baudrate, bool *flow_control);
void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name);
+int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
#else
@@ -157,6 +254,11 @@ static inline struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
return ERR_PTR(-EOPNOTSUPP);
}
+static inline int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ return -EOPNOTSUPP;
+}
+
static inline void btrtl_free(struct btrtl_device_info *btrtl_dev)
{
}
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 572091e601f9..96fcf09e0b7f 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -2787,6 +2787,9 @@ static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
return 0;
}
+ if (skb->data[0] == HCI_VENDOR_PKT)
+ return btrtl_recv_event(hdev, skb);
+
return hci_recv_frame(hdev, skb);
}
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox