mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-03-26 07:58:03 -04:00
bluez5/backend-native: Add HFP/HSP hardware offload datapath configuration
Add support for configuring the SCO hardware offload data path for HFP/HSP profiles using the Bluetooth SIG-specified procedure. This enables vendor-specific SCO offload integrations. Changes: - Add `bluez5.hw-offload-datapath` configuration property (default: 0) - Implement `sco_offload_btcodec()` to set BT_CODEC socket option - Add `SPA_BT_FEATURE_HW_OFFLOAD` quirk feature flag - Apply offload configuration when creating SCO sockets if quirk enabled - Document new property in pipewire-props.7.md The datapath ID is configurable via device parameters and only applied when the hardware offload feature flag is set in quirks, allowing platform-specific SCO offload implementations.
This commit is contained in:
parent
35817c0d85
commit
db7c74a042
4 changed files with 64 additions and 1 deletions
|
|
@ -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@ device-param bluez5.hw-offload-datapath # integer
|
||||||
|
\parblock
|
||||||
|
HFP/HSP hardware offload data path ID (default: 0).
|
||||||
|
|
||||||
|
This feature configures the SCO hardware‑offload data path for HFP/HSP using the Bluetooth
|
||||||
|
SIG–specified 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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,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 +298,32 @@ static const struct media_codec *codec_list_best(struct impl *backend, struct sp
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sco_offload_btcodec(struct impl *backend, int sock, bool msbc)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
char buffer[255];
|
||||||
|
struct bt_codecs *codecs;
|
||||||
|
|
||||||
|
spa_log_info(backend->log, "%s: sock(%d) msbc(%d)", __func__, sock, msbc);
|
||||||
|
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
codecs = (void *)buffer;
|
||||||
|
if (msbc)
|
||||||
|
codecs->codecs[0].id = 0x05;
|
||||||
|
else
|
||||||
|
codecs->codecs[0].id = 0x02;
|
||||||
|
codecs->num_codecs = 1;
|
||||||
|
codecs->codecs[0].data_path_id = backend->hfphsp_sco_datapath;
|
||||||
|
codecs->codecs[0].num_caps = 0x00;
|
||||||
|
|
||||||
|
err = setsockopt(sock, SOL_BLUETOOTH, BT_CODEC, codecs, sizeof(buffer));
|
||||||
|
if (err < 0)
|
||||||
|
spa_log_error(backend->log, "%s: ERROR: %s (%d)", __func__, strerror(errno), errno);
|
||||||
|
else
|
||||||
|
spa_log_info(backend->log, "%s: set offload codec succeeded", __func__);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
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"))
|
||||||
|
|
@ -2564,6 +2591,7 @@ static int sco_create_socket(struct impl *backend, struct spa_bt_adapter *adapte
|
||||||
struct sockaddr_sco addr;
|
struct sockaddr_sco addr;
|
||||||
socklen_t len;
|
socklen_t len;
|
||||||
bdaddr_t src;
|
bdaddr_t src;
|
||||||
|
uint32_t bt_features;
|
||||||
|
|
||||||
spa_autoclose int sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK, BTPROTO_SCO);
|
spa_autoclose int sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK, BTPROTO_SCO);
|
||||||
if (sock < 0) {
|
if (sock < 0) {
|
||||||
|
|
@ -2595,6 +2623,11 @@ static int sco_create_socket(struct impl *backend, struct spa_bt_adapter *adapte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (backend->quirks &&
|
||||||
|
(spa_bt_quirks_get_features(backend->quirks, NULL, NULL, &bt_features) == 0) &&
|
||||||
|
((bt_features & (SPA_BT_FEATURE_HW_OFFLOAD)) != 0))
|
||||||
|
sco_offload_btcodec(backend, sock, transparent);
|
||||||
|
|
||||||
return spa_steal_fd(sock);
|
return spa_steal_fd(sock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4101,6 +4134,18 @@ 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)
|
||||||
|
{
|
||||||
|
uint32_t tmp;
|
||||||
|
const char *str;
|
||||||
|
|
||||||
|
backend->hfphsp_sco_datapath = HFP_SCO_DEFAULT_DATAPATH;
|
||||||
|
|
||||||
|
if ((str = spa_dict_lookup(info, "bluez5.hw-offload-datapath")) != NULL &&
|
||||||
|
(tmp = atoi(str)) > 0)
|
||||||
|
backend->hfphsp_sco_datapath = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
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 +4208,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,
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
@ -809,6 +810,7 @@ enum spa_bt_feature {
|
||||||
SPA_BT_FEATURE_SBC_XQ = (1 << 5),
|
SPA_BT_FEATURE_SBC_XQ = (1 << 5),
|
||||||
SPA_BT_FEATURE_FASTSTREAM = (1 << 6),
|
SPA_BT_FEATURE_FASTSTREAM = (1 << 6),
|
||||||
SPA_BT_FEATURE_A2DP_DUPLEX = (1 << 7),
|
SPA_BT_FEATURE_A2DP_DUPLEX = (1 << 7),
|
||||||
|
SPA_BT_FEATURE_HW_OFFLOAD = (1 << 8),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct spa_bt_quirks;
|
struct spa_bt_quirks;
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ struct spa_bt_quirks {
|
||||||
int force_sbc_xq;
|
int force_sbc_xq;
|
||||||
int force_faststream;
|
int force_faststream;
|
||||||
int force_a2dp_duplex;
|
int force_a2dp_duplex;
|
||||||
|
int force_hw_offload;
|
||||||
|
|
||||||
char *device_rules;
|
char *device_rules;
|
||||||
char *adapter_rules;
|
char *adapter_rules;
|
||||||
|
|
@ -69,6 +70,7 @@ static enum spa_bt_feature parse_feature(const char *str)
|
||||||
{ "sbc-xq", SPA_BT_FEATURE_SBC_XQ },
|
{ "sbc-xq", SPA_BT_FEATURE_SBC_XQ },
|
||||||
{ "faststream", SPA_BT_FEATURE_FASTSTREAM },
|
{ "faststream", SPA_BT_FEATURE_FASTSTREAM },
|
||||||
{ "a2dp-duplex", SPA_BT_FEATURE_A2DP_DUPLEX },
|
{ "a2dp-duplex", SPA_BT_FEATURE_A2DP_DUPLEX },
|
||||||
|
{ "hw-offload", SPA_BT_FEATURE_HW_OFFLOAD },
|
||||||
};
|
};
|
||||||
SPA_FOR_EACH_ELEMENT_VAR(feature_keys, f) {
|
SPA_FOR_EACH_ELEMENT_VAR(feature_keys, f) {
|
||||||
if (spa_streq(str, f->key))
|
if (spa_streq(str, f->key))
|
||||||
|
|
@ -228,6 +230,7 @@ struct spa_bt_quirks *spa_bt_quirks_create(const struct spa_dict *info, struct s
|
||||||
this->force_hw_volume = parse_force_flag(info, "bluez5.enable-hw-volume");
|
this->force_hw_volume = parse_force_flag(info, "bluez5.enable-hw-volume");
|
||||||
this->force_faststream = parse_force_flag(info, "bluez5.enable-faststream");
|
this->force_faststream = parse_force_flag(info, "bluez5.enable-faststream");
|
||||||
this->force_a2dp_duplex = parse_force_flag(info, "bluez5.enable-a2dp-duplex");
|
this->force_a2dp_duplex = parse_force_flag(info, "bluez5.enable-a2dp-duplex");
|
||||||
|
this->force_hw_offload = parse_force_flag(info, "bluez5.hw-offload-sco");
|
||||||
|
|
||||||
if ((str = spa_dict_lookup(info, "bluez5.hardware-database")) != NULL) {
|
if ((str = spa_dict_lookup(info, "bluez5.hardware-database")) != NULL) {
|
||||||
spa_log_debug(this->log, "loading session manager provided data");
|
spa_log_debug(this->log, "loading session manager provided data");
|
||||||
|
|
@ -385,6 +388,9 @@ static int get_features(const struct spa_bt_quirks *this,
|
||||||
if (this->force_a2dp_duplex != -1)
|
if (this->force_a2dp_duplex != -1)
|
||||||
SPA_FLAG_UPDATE(*features, SPA_BT_FEATURE_A2DP_DUPLEX, this->force_a2dp_duplex);
|
SPA_FLAG_UPDATE(*features, SPA_BT_FEATURE_A2DP_DUPLEX, this->force_a2dp_duplex);
|
||||||
|
|
||||||
|
if (this->force_hw_offload != -1)
|
||||||
|
SPA_FLAG_UPDATE(*features, SPA_BT_FEATURE_HW_OFFLOAD, this->force_hw_offload);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue