Merge branch 'master' into 'master'

bluez5/backend-native: Add HFP/HSP hardware offload data path configuration

See merge request pipewire/pipewire!2667
This commit is contained in:
Mengshi Wu 2026-01-25 17:26:21 +00:00
commit 2c6bf5f67a
3 changed files with 53 additions and 1 deletions

View file

@ -1171,6 +1171,15 @@ in a platform-specific way. See `tests/examples/bt-pinephone.lua` in WirePlumber
Do not enable this setting if you don't know what all this means, as it won't work. Do not enable this setting if you don't know what all this means, as it won't work.
\endparblock \endparblock
@PAR@ monitor-prop bluez5.hw-offload-datapath # integer
\parblock
HFP/HSP hardware offload data path ID (default: 0).
This feature configures the SCO hardwareoffload data path for HFP/HSP using the Bluetooth
SIGspecified procedure. It is intended for advanced setups and vendor integrations. Do not
edit this unless required; incorrect values can disable SCO offload.
\endparblock
@PAR@ monitor-prop bluez5.a2dp.opus.pro.channels = 3 # integer @PAR@ monitor-prop bluez5.a2dp.opus.pro.channels = 3 # integer
PipeWire Opus Pro audio profile channel count. PipeWire Opus Pro audio profile channel count.

View file

@ -63,6 +63,9 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.native");
#define RFCOMM_MESSAGE_MAX_LENGTH 256 #define RFCOMM_MESSAGE_MAX_LENGTH 256
#define BT_CODEC_CVSD 0x02
#define BT_CODEC_MSBC 0x05
enum { enum {
HFP_AG_INITIAL_CODEC_SETUP_NONE = 0, HFP_AG_INITIAL_CODEC_SETUP_NONE = 0,
HFP_AG_INITIAL_CODEC_SETUP_SEND, HFP_AG_INITIAL_CODEC_SETUP_SEND,
@ -112,6 +115,7 @@ struct impl {
int hfp_default_speaker_volume; int hfp_default_speaker_volume;
struct spa_source sco; struct spa_source sco;
unsigned int hfphsp_sco_datapath;
const struct spa_bt_quirks *quirks; const struct spa_bt_quirks *quirks;
@ -297,6 +301,33 @@ static const struct media_codec *codec_list_best(struct impl *backend, struct sp
return NULL; return NULL;
} }
static void sco_offload_btcodec(struct impl *backend, int sock, bool msbc)
{
int err;
char buffer[255];
struct bt_codecs *codecs;
if (backend->hfphsp_sco_datapath == HFP_SCO_DEFAULT_DATAPATH)
return;
spa_log_info(backend->log, "sock(%d) msbc(%d)", sock, msbc);
memset(buffer, 0, sizeof(buffer));
codecs = (void *)buffer;
if (msbc)
codecs->codecs[0].id = BT_CODEC_MSBC;
else
codecs->codecs[0].id = BT_CODEC_CVSD;
codecs->num_codecs = 1;
codecs->codecs[0].data_path_id = backend->hfphsp_sco_datapath;
err = setsockopt(sock, SOL_BLUETOOTH, BT_CODEC, codecs, sizeof(buffer));
if (err < 0)
spa_log_error(backend->log, "ERROR: %s (%d)", strerror(errno), errno);
else
spa_log_info(backend->log, "set offload codec succeeded");
}
static DBusHandlerResult profile_release(DBusConnection *conn, DBusMessage *m, void *userdata) static DBusHandlerResult profile_release(DBusConnection *conn, DBusMessage *m, void *userdata)
{ {
if (!reply_with_error(conn, m, BLUEZ_PROFILE_INTERFACE ".Error.NotImplemented", "Method not implemented")) if (!reply_with_error(conn, m, BLUEZ_PROFILE_INTERFACE ".Error.NotImplemented", "Method not implemented"))
@ -2595,6 +2626,8 @@ static int sco_create_socket(struct impl *backend, struct spa_bt_adapter *adapte
} }
} }
sco_offload_btcodec(backend, sock, transparent);
return spa_steal_fd(sock); return spa_steal_fd(sock);
} }
@ -4101,6 +4134,14 @@ static void parse_hfp_default_volumes(struct impl *backend, const struct spa_dic
backend->hfp_default_speaker_volume = SPA_BT_VOLUME_HS_MAX; backend->hfp_default_speaker_volume = SPA_BT_VOLUME_HS_MAX;
} }
static void parse_sco_datapath(struct impl *backend, const struct spa_dict *info)
{
backend->hfphsp_sco_datapath = HFP_SCO_DEFAULT_DATAPATH;
spa_atou32(spa_dict_lookup(info, "bluez5.hw-offload-datapath"),
&backend->hfphsp_sco_datapath, 10);
}
static const struct spa_bt_backend_implementation backend_impl = { static const struct spa_bt_backend_implementation backend_impl = {
SPA_VERSION_BT_BACKEND_IMPLEMENTATION, SPA_VERSION_BT_BACKEND_IMPLEMENTATION,
.free = backend_native_free, .free = backend_native_free,
@ -4163,6 +4204,7 @@ struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor,
parse_hfp_disable_nrec(backend, info); parse_hfp_disable_nrec(backend, info);
parse_hfp_default_volumes(backend, info); parse_hfp_default_volumes(backend, info);
parse_hfp_pts(backend, info); parse_hfp_pts(backend, info);
parse_sco_datapath(backend, info);
#ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE #ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE
if (!dbus_connection_register_object_path(backend->conn, if (!dbus_connection_register_object_path(backend->conn,

View file

@ -135,7 +135,8 @@ extern "C" {
#define PROFILE_HFP_AG "/Profile/HFPAG" #define PROFILE_HFP_AG "/Profile/HFPAG"
#define PROFILE_HFP_HF "/Profile/HFPHF" #define PROFILE_HFP_HF "/Profile/HFPHF"
#define HSP_HS_DEFAULT_CHANNEL 3 #define HSP_HS_DEFAULT_CHANNEL 3
#define HFP_SCO_DEFAULT_DATAPATH 0
#define SOURCE_ID_BLUETOOTH 0x1 /* Bluetooth SIG */ #define SOURCE_ID_BLUETOOTH 0x1 /* Bluetooth SIG */
#define SOURCE_ID_USB 0x2 /* USB Implementer's Forum */ #define SOURCE_ID_USB 0x2 /* USB Implementer's Forum */