The A2DP specification defines error codes that shall be used if the codec capabilities contain improper settings. This change allows clients to trigger the sending of these specific error codes by returning the corresponding error messages from MediaEndpoint1.SetConfiguration. This update is fully backwards compatible: clients passing other error messages will continue to receive the default error code as before. On older BlueZ versions, these new errors will also result in the default error code, enabling clients to implement support for the new errors without breaking compatibility. This change enables passing A2DP/SNK/AVP/* and A2DP/SRC/AVP/* qualification tests. --- profiles/audio/a2dp.c | 78 ++++++++++++++++++++++++++++++++++++++---- profiles/audio/a2dp.h | 43 ++++++++++++++++++++++- profiles/audio/avdtp.c | 4 +-- profiles/audio/media.c | 20 ++++++----- 4 files changed, 128 insertions(+), 17 deletions(-) diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c index c0a53eae9..661843a89 100644 --- a/profiles/audio/a2dp.c +++ b/profiles/audio/a2dp.c @@ -157,6 +157,73 @@ static GSList *servers = NULL; static GSList *setups = NULL; static unsigned int cb_id = 0; +struct a2dp_error { + const char *error_name; + uint8_t error_code; +}; + +#define A2DP_ERROR_PREFIX ERROR_INTERFACE ".A2DP." + +static struct a2dp_error config_errors[] = { + {"InvalidCodecType", A2DP_INVALID_CODEC_TYPE}, + {"NotSupportedCodecType", A2DP_NOT_SUPPORTED_CODEC_TYPE}, + {"InvalidSamplingFrequency", A2DP_INVALID_SAMPLING_FREQUENCY}, + {"NotSupportedSamplingFrequency", + A2DP_NOT_SUPPORTED_SAMPLING_FREQUENCY}, + {"InvalidChannelMode", A2DP_INVALID_CHANNEL_MODE}, + {"NotSupportedChannelMode", A2DP_NOT_SUPPORTED_CHANNEL_MODE}, + {"InvalidSubbands", A2DP_INVALID_SUBBANDS}, + {"NotSupportedSubbands", A2DP_NOT_SUPPORTED_SUBBANDS}, + {"InvalidAllocationMethod", A2DP_INVALID_ALLOCATION_METHOD}, + {"NotSupportedAllocationMethod", A2DP_NOT_SUPPORTED_ALLOCATION_METHOD}, + {"InvalidMinimumBitpoolValue", + A2DP_INVALID_MINIMUM_BITPOOL_VALUE}, + {"NotSupportedMinimumBitpoolValue", + A2DP_NOT_SUPPORTED_MINIMUM_BITPOOL_VALUE}, + {"InvalidMaximumBitpoolValue", A2DP_INVALID_MAXIMUM_BITPOOL_VALUE}, + {"NotSupportedMaximumBitpoolValue", + A2DP_NOT_SUPPORTED_MAXIMUM_BITPOOL_VALUE}, + {"InvalidInvalidLayer", A2DP_INVALID_INVALID_LAYER}, + {"NotSupportedLayer", A2DP_NOT_SUPPORTED_LAYER}, + {"NotSupporterdCRC", A2DP_NOT_SUPPORTERD_CRC}, + {"NotSupporterdMPF", A2DP_NOT_SUPPORTERD_MPF}, + {"NotSupporterdVBR", A2DP_NOT_SUPPORTERD_VBR}, + {"InvalidBitRate", A2DP_INVALID_BIT_RATE}, + {"NotSupportedBitRate", A2DP_NOT_SUPPORTED_BIT_RATE}, + {"InvalidObjectType", A2DP_INVALID_OBJECT_TYPE}, + {"NotSupportedObjectType", A2DP_NOT_SUPPORTED_OBJECT_TYPE}, + {"InvalidChannels", A2DP_INVALID_CHANNELS}, + {"NotSupportedChannels", A2DP_NOT_SUPPORTED_CHANNELS}, + {"InvalidVersion", A2DP_INVALID_VERSION}, + {"NotSupportedVersion", A2DP_NOT_SUPPORTED_VERSION}, + {"NotSupportedMaximumSUL", A2DP_NOT_SUPPORTED_MAXIMUM_SUL}, + {"InvalidBlockLength", A2DP_INVALID_BLOCK_LENGTH}, + {"InvalidCPType", A2DP_INVALID_CP_TYPE}, + {"InvalidCPFormat", A2DP_INVALID_CP_FORMAT}, + {"InvalidCodecParameter", A2DP_INVALID_CODEC_PARAMETER}, + {"NotSupportedCodecParameter", A2DP_NOT_SUPPORTED_CODEC_PARAMETER}, + {"InvalidDRC", A2DP_INVALID_DRC}, + {"NotSupportedDRC", A2DP_NOT_SUPPORTED_DRC} +}; + +uint8_t a2dp_parse_config_error(const char *error_name) +{ + size_t prefix_length; + size_t i; + + prefix_length = strlen(A2DP_ERROR_PREFIX); + if (strncmp(A2DP_ERROR_PREFIX, error_name, prefix_length)) + return AVDTP_UNSUPPORTED_CONFIGURATION; + + error_name += prefix_length; + for (i = 0; i < ARRAY_SIZE(config_errors); i++) { + if (strcmp(config_errors[i].error_name, error_name) == 0) + return config_errors[i].error_code; + } + + return AVDTP_UNSUPPORTED_CONFIGURATION; +} + static struct a2dp_setup *setup_ref(struct a2dp_setup *setup) { setup->ref++; @@ -688,11 +755,10 @@ done: return FALSE; } -static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret) +static void endpoint_setconf_cb(struct a2dp_setup *setup, uint8_t error_code) { - if (ret == FALSE) - setup_error_init(setup, AVDTP_MEDIA_CODEC, - AVDTP_UNSUPPORTED_CONFIGURATION); + if (error_code != 0) + setup_error_init(setup, AVDTP_MEDIA_CODEC, error_code); auto_config(setup); setup_unref(setup); @@ -865,11 +931,11 @@ static gboolean endpoint_getcap_ind(struct avdtp *session, return TRUE; } -static void endpoint_open_cb(struct a2dp_setup *setup, gboolean ret) +static void endpoint_open_cb(struct a2dp_setup *setup, uint8_t error_code) { int err = error_to_errno(setup->err); - if (ret == FALSE) { + if (error_code != 0) { setup->stream = NULL; finalize_setup_errno(setup, -EPERM, finalize_config, NULL); goto done; diff --git a/profiles/audio/a2dp.h b/profiles/audio/a2dp.h index c698bc983..bcdb4d43f 100644 --- a/profiles/audio/a2dp.h +++ b/profiles/audio/a2dp.h @@ -15,7 +15,8 @@ struct a2dp_setup; typedef void (*a2dp_endpoint_select_t) (struct a2dp_setup *setup, void *ret, int size); -typedef void (*a2dp_endpoint_config_t) (struct a2dp_setup *setup, gboolean ret); +typedef void (*a2dp_endpoint_config_t) (struct a2dp_setup *setup, + uint8_t error_code); struct a2dp_endpoint { const char *(*get_name) (struct a2dp_sep *sep, void *user_data); @@ -70,6 +71,8 @@ unsigned int a2dp_select_capabilities(struct avdtp *session, unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep, a2dp_config_cb_t cb, GSList *caps, void *user_data); +uint8_t a2dp_parse_config_error(const char *error_name); + unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep, a2dp_stream_cb_t cb, void *user_data); unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep, @@ -83,3 +86,41 @@ struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep, struct btd_device *a2dp_setup_get_device(struct a2dp_setup *setup); const char *a2dp_setup_remote_path(struct a2dp_setup *setup); struct avdtp *a2dp_avdtp_get(struct btd_device *device); + +enum a2dp_error_codes { + A2DP_INVALID_CODEC_TYPE = 0xc1, + A2DP_NOT_SUPPORTED_CODEC_TYPE = 0xc2, + A2DP_INVALID_SAMPLING_FREQUENCY = 0xc3, + A2DP_NOT_SUPPORTED_SAMPLING_FREQUENCY = 0xc4, + A2DP_INVALID_CHANNEL_MODE = 0xc5, + A2DP_NOT_SUPPORTED_CHANNEL_MODE = 0xc6, + A2DP_INVALID_SUBBANDS = 0xc7, + A2DP_NOT_SUPPORTED_SUBBANDS = 0xc8, + A2DP_INVALID_ALLOCATION_METHOD = 0xc9, + A2DP_NOT_SUPPORTED_ALLOCATION_METHOD = 0xca, + A2DP_INVALID_MINIMUM_BITPOOL_VALUE = 0xcb, + A2DP_NOT_SUPPORTED_MINIMUM_BITPOOL_VALUE = 0xcc, + A2DP_INVALID_MAXIMUM_BITPOOL_VALUE = 0xcd, + A2DP_NOT_SUPPORTED_MAXIMUM_BITPOOL_VALUE = 0xce, + A2DP_INVALID_INVALID_LAYER = 0xcf, + A2DP_NOT_SUPPORTED_LAYER = 0xd0, + A2DP_NOT_SUPPORTERD_CRC = 0xd1, + A2DP_NOT_SUPPORTERD_MPF = 0xd2, + A2DP_NOT_SUPPORTERD_VBR = 0xd3, + A2DP_INVALID_BIT_RATE = 0xd4, + A2DP_NOT_SUPPORTED_BIT_RATE = 0xd5, + A2DP_INVALID_OBJECT_TYPE = 0xd6, + A2DP_NOT_SUPPORTED_OBJECT_TYPE = 0xd7, + A2DP_INVALID_CHANNELS = 0xd8, + A2DP_NOT_SUPPORTED_CHANNELS = 0xd9, + A2DP_INVALID_VERSION = 0xda, + A2DP_NOT_SUPPORTED_VERSION = 0xdb, + A2DP_NOT_SUPPORTED_MAXIMUM_SUL = 0xdc, + A2DP_INVALID_BLOCK_LENGTH = 0xdd, + A2DP_INVALID_CP_TYPE = 0xe0, + A2DP_INVALID_CP_FORMAT = 0xe1, + A2DP_INVALID_CODEC_PARAMETER = 0xe2, + A2DP_NOT_SUPPORTED_CODEC_PARAMETER = 0xe3, + A2DP_INVALID_DRC = 0xe4, + A2DP_NOT_SUPPORTED_DRC = 0xe5, +}; diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c index 30648251f..ed4e22b26 100644 --- a/profiles/audio/avdtp.c +++ b/profiles/audio/avdtp.c @@ -1494,8 +1494,8 @@ static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream, struct conf_rej rej; if (err != NULL) { - rej.error = AVDTP_UNSUPPORTED_CONFIGURATION; - rej.category = err->err.error_code; + rej.error = err->err.error_code; + rej.category = AVDTP_UNSUPPORTED_CONFIGURATION; avdtp_send(session, session->in_cmd.transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION, &rej, sizeof(rej)); diff --git a/profiles/audio/media.c b/profiles/audio/media.c index 9b3042c18..332f643bb 100644 --- a/profiles/audio/media.c +++ b/profiles/audio/media.c @@ -333,7 +333,7 @@ static void endpoint_reply(DBusPendingCall *call, void *user_data) DBusMessage *reply; DBusMessageIter args, props; DBusError err; - gboolean value; + uint8_t error_code; void *ret = NULL; int size = -1; @@ -356,8 +356,12 @@ static void endpoint_reply(DBusPendingCall *call, void *user_data) if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE, - "SetConfiguration")) + "SetConfiguration")) { endpoint_remove_transport(endpoint, request->transport); + error_code = a2dp_parse_config_error(err.name); + ret = &error_code; + size = 1; + } dbus_error_free(&err); goto done; @@ -390,8 +394,8 @@ static void endpoint_reply(DBusPendingCall *call, void *user_data) } size = 1; - value = TRUE; - ret = &value; + error_code = 0; + ret = &error_code; done: dbus_message_unref(reply); @@ -634,9 +638,9 @@ static void config_cb(struct media_endpoint *endpoint, void *ret, int size, void *user_data) { struct a2dp_config_data *data = user_data; - gboolean *ret_value = ret; + uint8_t *ret_value = ret; - data->cb(data->setup, ret_value ? *ret_value : FALSE); + data->cb(data->setup, ret_value ? *ret_value : 1); } static int set_config(struct a2dp_sep *sep, uint8_t *configuration, @@ -1098,7 +1102,7 @@ static void pac_config_cb(struct media_endpoint *endpoint, void *ret, int size, void *user_data) { struct pac_config_data *data = user_data; - gboolean *ret_value = ret; + uint8_t *error_code = ret; struct media_transport *transport; /* If transport was cleared, configuration was cancelled */ @@ -1106,7 +1110,7 @@ static void pac_config_cb(struct media_endpoint *endpoint, void *ret, int size, if (!transport) return; - data->cb(data->stream, ret_value ? 0 : -EINVAL); + data->cb(data->stream, error_code == 0 ? 0 : -EINVAL); } static struct media_transport *pac_ucast_config(struct bt_bap_stream *stream, -- 2.43.0