a2dp source: fix crash on outgoing connection after previous incoming one

The pointer to the user media codec configuration buffer was stored in stream_endpoint.remote_configuration, which was set to zero in avdtp_reset_stream_endpoint. The user buffer is now stored separately and it is updateds when the configuration gets set/updated. The field media_codec_sbc_info is used by a2dp configure and reconfigure.
This commit is contained in:
Matthias Ringwald 2020-10-13 10:59:55 +02:00
parent 44e638f3c1
commit 8276777316
7 changed files with 91 additions and 67 deletions

View File

@ -56,8 +56,6 @@ static bool outgoing_active = false;
static uint16_t a2dp_sink_cid;
static bool stream_endpoint_configured = false;
static avdtp_stream_endpoint_context_t sc;
static btstack_packet_handler_t a2dp_sink_packet_handler_user;
static void a2dp_sink_packet_handler_internal(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
@ -164,9 +162,9 @@ void a2dp_sink_init(void){
avdtp_sink_init();
}
avdtp_stream_endpoint_t * a2dp_sink_create_stream_endpoint(avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type,
uint8_t * codec_capabilities, uint16_t codec_capabilities_len,
uint8_t * media_codec_info, uint16_t media_codec_info_len){
avdtp_stream_endpoint_t * a2dp_sink_create_stream_endpoint(avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type,
uint8_t * codec_capabilities, uint16_t codec_capabilities_len,
uint8_t * codec_configuration, uint16_t codec_configuration_len){
avdtp_stream_endpoint_t * local_stream_endpoint = avdtp_sink_create_stream_endpoint(AVDTP_SINK, media_type);
if (!local_stream_endpoint){
return NULL;
@ -174,9 +172,12 @@ avdtp_stream_endpoint_t * a2dp_sink_create_stream_endpoint(avdtp_media_type_t me
avdtp_sink_register_media_transport_category(avdtp_stream_endpoint_seid(local_stream_endpoint));
avdtp_sink_register_media_codec_category(avdtp_stream_endpoint_seid(local_stream_endpoint), media_type, media_codec_type,
codec_capabilities, codec_capabilities_len);
local_stream_endpoint->remote_configuration.media_codec.media_codec_information = media_codec_info;
local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = media_codec_info_len;
avdtp_sink_register_delay_reporting_category(avdtp_stream_endpoint_seid(local_stream_endpoint));
avdtp_sink_register_delay_reporting_category(avdtp_stream_endpoint_seid(local_stream_endpoint));
// store user codec configuration buffer
local_stream_endpoint->media_codec_configuration_info = codec_configuration;
local_stream_endpoint->media_codec_configuration_len = codec_configuration_len;
return local_stream_endpoint;
}
@ -185,8 +186,8 @@ void a2dp_sink_finalize_stream_endpoint(avdtp_stream_endpoint_t * stream_endpoin
}
uint8_t a2dp_sink_establish_stream(bd_addr_t bd_addr, uint8_t local_seid, uint16_t * avdtp_cid){
sc.local_stream_endpoint = avdtp_get_stream_endpoint_for_seid(local_seid);
if (!sc.local_stream_endpoint){
avdtp_stream_endpoint_t * stream_endpoint = avdtp_get_stream_endpoint_for_seid(local_seid);
if (stream_endpoint == NULL){
log_info("No local_stream_endpoint for seid %d", local_seid);
return ERROR_CODE_COMMAND_DISALLOWED;
}

View File

@ -373,6 +373,7 @@ static void a2dp_source_packet_handler_internal(uint8_t packet_type, uint16_t ch
connection = avdtp_get_connection_for_avdtp_cid(cid);
btstack_assert(connection != NULL);
// choose SBC config params
uint8_t sampling_frequency = avdtp_choose_sbc_sampling_frequency(sc.local_stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_sampling_frequency_bitmap(packet));
uint8_t channel_mode = avdtp_choose_sbc_channel_mode(sc.local_stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_channel_mode_bitmap(packet));
uint8_t block_length = avdtp_choose_sbc_block_length(sc.local_stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_block_length_bitmap(packet));
@ -382,16 +383,21 @@ static void a2dp_source_packet_handler_internal(uint8_t packet_type, uint16_t ch
uint8_t max_bitpool_value = avdtp_choose_sbc_max_bitpool_value(sc.local_stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_max_bitpool_value(packet));
uint8_t min_bitpool_value = avdtp_choose_sbc_min_bitpool_value(sc.local_stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_min_bitpool_value(packet));
// set media configuration
sc.local_stream_endpoint->remote_configuration_bitmap = store_bit16(sc.local_stream_endpoint->remote_configuration_bitmap, AVDTP_MEDIA_CODEC, 1);
sc.local_stream_endpoint->remote_configuration.media_codec.media_type = AVDTP_AUDIO;
sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_type = AVDTP_CODEC_SBC;
// select reserved SBC config buffer
sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_information = sc.local_stream_endpoint->media_codec_sbc_info;
sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = 4;
// store SBC configuration in reserved field
sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_information[0] = (sampling_frequency << 4) | channel_mode;
sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_information[1] = (block_length << 4) | (subbands << 2) | allocation_method;
sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_information[2] = min_bitpool_value;
sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_information[3] = max_bitpool_value;
sc.local_stream_endpoint->remote_configuration_bitmap = store_bit16(sc.local_stream_endpoint->remote_configuration_bitmap, AVDTP_MEDIA_CODEC, 1);
sc.local_stream_endpoint->remote_configuration.media_codec.media_type = AVDTP_AUDIO;
sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_type = AVDTP_CODEC_SBC;
// suitable Sink SEP found, configure SEP
sep_found_w2_set_configuration = true;
connection->supported_codecs_bitmap |= (1 << AVDTP_CODEC_SBC);
@ -635,9 +641,9 @@ void a2dp_source_init(void){
avdtp_source_init();
}
avdtp_stream_endpoint_t * a2dp_source_create_stream_endpoint(avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type,
uint8_t * codec_capabilities, uint16_t codec_capabilities_len,
uint8_t * media_codec_info, uint16_t media_codec_info_len){
avdtp_stream_endpoint_t * a2dp_source_create_stream_endpoint(avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type,
uint8_t * codec_capabilities, uint16_t codec_capabilities_len,
uint8_t * codec_configuration, uint16_t codec_configuration_len){
avdtp_stream_endpoint_t * local_stream_endpoint = avdtp_source_create_stream_endpoint(AVDTP_SOURCE, media_type);
if (!local_stream_endpoint){
return NULL;
@ -645,11 +651,13 @@ avdtp_stream_endpoint_t * a2dp_source_create_stream_endpoint(avdtp_media_type_t
avdtp_source_register_media_transport_category(avdtp_stream_endpoint_seid(local_stream_endpoint));
avdtp_source_register_media_codec_category(avdtp_stream_endpoint_seid(local_stream_endpoint), media_type, media_codec_type,
codec_capabilities, codec_capabilities_len);
local_stream_endpoint->remote_configuration.media_codec.media_codec_information = media_codec_info;
local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = media_codec_info_len;
sc.local_stream_endpoint = local_stream_endpoint;
avdtp_source_register_delay_reporting_category(avdtp_stream_endpoint_seid(local_stream_endpoint));
avdtp_source_register_delay_reporting_category(avdtp_stream_endpoint_seid(local_stream_endpoint));
// store user codec configuration buffer
local_stream_endpoint->media_codec_configuration_info = codec_configuration;
local_stream_endpoint->media_codec_configuration_len = codec_configuration_len;
sc.local_stream_endpoint = local_stream_endpoint;
return local_stream_endpoint;
}
@ -717,12 +725,12 @@ uint8_t a2dp_source_reconfigure_stream_sampling_frequency(uint16_t avdtp_cid, ui
log_info("Reconfigure avdtp_cid 0x%02x", avdtp_cid);
(void)memcpy(sc.local_stream_endpoint->reconfigure_media_codec_sbc_info,
(void)memcpy(sc.local_stream_endpoint->media_codec_sbc_info,
sc.local_stream_endpoint->remote_sep.configuration.media_codec.media_codec_information,
4);
// update sampling frequency
uint8_t config = sc.local_stream_endpoint->reconfigure_media_codec_sbc_info[0] & 0x0f;
uint8_t config = sc.local_stream_endpoint->media_codec_sbc_info[0] & 0x0f;
switch (sampling_frequency){
case 48000:
config |= (AVDTP_SBC_48000 << 4);
@ -740,13 +748,13 @@ uint8_t a2dp_source_reconfigure_stream_sampling_frequency(uint16_t avdtp_cid, ui
log_error("Unsupported sampling frequency %u", sampling_frequency);
return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
sc.local_stream_endpoint->reconfigure_media_codec_sbc_info[0] = config;
sc.local_stream_endpoint->media_codec_sbc_info[0] = config;
avdtp_capabilities_t new_configuration;
new_configuration.media_codec.media_type = AVDTP_AUDIO;
new_configuration.media_codec.media_codec_type = AVDTP_CODEC_SBC;
new_configuration.media_codec.media_codec_information_len = 4;
new_configuration.media_codec.media_codec_information = sc.local_stream_endpoint->reconfigure_media_codec_sbc_info;
new_configuration.media_codec.media_codec_information = sc.local_stream_endpoint->media_codec_sbc_info;
// start reconfigure
a2dp_source_state = A2DP_W2_RECONFIGURE_WITH_SEID;

View File

@ -1299,13 +1299,6 @@ uint8_t avdtp_set_configuration(uint16_t avdtp_cid, uint8_t local_seid, uint8_t
log_debug("SE %p, initiator_config_state: 0x%02x", stream_endpoint, stream_endpoint->initiator_config_state);
// cache media codec information for SBC
stream_endpoint->media_codec_type = configuration.media_codec.media_codec_type;
if (configuration.media_codec.media_codec_type == AVDTP_CODEC_SBC){
stream_endpoint->media_type = configuration.media_codec.media_type;
(void)memcpy(stream_endpoint->media_codec_sbc_info,
configuration.media_codec.media_codec_information, 4);
}
return avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid);
}

View File

@ -486,8 +486,13 @@ typedef struct {
typedef struct avdtp_stream_endpoint {
btstack_linked_item_t item;
// original capabilities
// original capabilities configured via avdtp_register_x_category
avdtp_sep_t sep;
// media codec configuration
uint16_t media_codec_configuration_len;
uint8_t * media_codec_configuration_info;
avdtp_sep_t remote_sep;
hci_con_handle_t media_con_handle;
uint16_t l2cap_media_cid;
@ -508,14 +513,11 @@ typedef struct avdtp_stream_endpoint {
uint16_t remote_configuration_bitmap;
avdtp_capabilities_t remote_configuration;
// temporary SBC config
// temporary SBC config used by A2DP Source
avdtp_media_codec_type_t media_codec_type;
avdtp_media_type_t media_type;
uint8_t media_codec_sbc_info[4];
// temporary reconfigure SBC config used by A2DP
uint8_t reconfigure_media_codec_sbc_info[4];
// preferred sampling frequency
uint32_t preferred_sampling_frequency;

View File

@ -132,8 +132,14 @@ avdtp_acceptor_handle_configuration_command(avdtp_connection_t *connection, int
log_info("add remote seid %d", stream_endpoint->remote_sep.seid);
}
avdtp_signaling_emit_configuration(stream_endpoint, connection->avdtp_cid, &sep.configuration,
sep.configured_service_categories);
// if media codec configuration set, copy configuration and emit event
if ((sep.configured_service_categories & (1 << AVDTP_MEDIA_CODEC)) != 0){
if (stream_endpoint->media_codec_configuration_len == sep.configuration.media_codec.media_codec_information_len){
(void) memcpy(stream_endpoint->media_codec_configuration_info, sep.configuration.media_codec.media_codec_information, stream_endpoint->media_codec_configuration_len);
}
avdtp_signaling_emit_configuration(stream_endpoint, connection->avdtp_cid, &sep.configuration, sep.configured_service_categories);
}
avdtp_signaling_emit_accept(connection->avdtp_cid, avdtp_local_seid(stream_endpoint),
connection->acceptor_signaling_packet.signal_identifier, false);
}
@ -314,8 +320,13 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t *connection, uint8_t
log_info("update active remote seid %d", stream_endpoint->remote_sep.seid);
avdtp_signaling_emit_configuration(stream_endpoint, connection->avdtp_cid, &sep.configuration,
sep.configured_service_categories);
// if media codec configuration updated, copy configuration and emit event
if ((sep.configured_service_categories & (1 << AVDTP_MEDIA_CODEC)) != 0){
if (stream_endpoint->media_codec_configuration_len == sep.configuration.media_codec.media_codec_information_len){
(void) memcpy(stream_endpoint->media_codec_configuration_info, sep.configuration.media_codec.media_codec_information, stream_endpoint->media_codec_configuration_len);
}
avdtp_signaling_emit_configuration(stream_endpoint, connection->avdtp_cid, &sep.configuration, sep.configured_service_categories);
}
break;
}

View File

@ -152,17 +152,18 @@ void avdtp_initiator_stream_config_subsm(avdtp_connection_t *connection, uint8_t
break;
}
stream_endpoint_for_event = stream_endpoint;
// copy sbc media codec info
stream_endpoint->remote_sep.configured_service_categories |= stream_endpoint->remote_configuration_bitmap;
stream_endpoint->remote_sep.configuration = stream_endpoint->remote_configuration;
(void)memcpy(stream_endpoint->media_codec_sbc_info,
stream_endpoint->remote_configuration.media_codec.media_codec_information,
4);
stream_endpoint->remote_sep.configuration.media_codec.media_codec_information = stream_endpoint->media_codec_sbc_info;
stream_endpoint->state = AVDTP_STREAM_ENDPOINT_OPENED;
// copy media codec configuration if reconfigured
if ((stream_endpoint->remote_configuration_bitmap & (1 << AVDTP_MEDIA_CODEC)) != 0){
btstack_assert(stream_endpoint->remote_configuration.media_codec.media_codec_information_len == stream_endpoint->media_codec_configuration_len);
(void)memcpy(stream_endpoint->media_codec_configuration_info, stream_endpoint->remote_configuration.media_codec.media_codec_information, stream_endpoint->media_codec_configuration_len);
}
break;
case AVDTP_SI_SET_CONFIGURATION:{
case AVDTP_SI_SET_CONFIGURATION:
if (!stream_endpoint){
log_error("AVDTP_SI_SET_CONFIGURATION: stream endpoint is null");
break;
@ -181,24 +182,32 @@ void avdtp_initiator_stream_config_subsm(avdtp_connection_t *connection, uint8_t
log_info("configured remote seid %d", stream_endpoint->remote_sep.seid);
switch (stream_endpoint->media_codec_type){
case AVDTP_CODEC_SBC:
avdtp_signaling_emit_media_codec_sbc_configuration(
stream_endpoint,
connection->avdtp_cid,
stream_endpoint->media_type,
stream_endpoint->media_codec_sbc_info);
break;
default:
// TODO: we don\t have codec info to emit config
avdtp_signaling_emit_media_codec_other_configuration(stream_endpoint,
connection->avdtp_cid,
&sep.configuration.media_codec);
break;
}
// copy media codec configuration if configured
if ((stream_endpoint->remote_configuration_bitmap & (1 << AVDTP_MEDIA_CODEC)) != 0) {
btstack_assert(stream_endpoint->remote_configuration.media_codec.media_codec_information_len ==
stream_endpoint->media_codec_configuration_len);
(void) memcpy(stream_endpoint->media_codec_configuration_info,
stream_endpoint->remote_configuration.media_codec.media_codec_information,
stream_endpoint->media_codec_configuration_len);
switch (stream_endpoint->media_codec_type) {
case AVDTP_CODEC_SBC:
avdtp_signaling_emit_media_codec_sbc_configuration(
stream_endpoint,
connection->avdtp_cid,
stream_endpoint->media_type,
stream_endpoint->media_codec_configuration_info);
break;
default:
// TODO: we don't have codec info to emit config
avdtp_signaling_emit_media_codec_other_configuration(stream_endpoint,
connection->avdtp_cid,
&sep.configuration.media_codec);
break;
}
}
break;
}
case AVDTP_SI_OPEN:
if (!stream_endpoint){
log_error("AVDTP_SI_OPEN: stream endpoint is null");

View File

@ -124,8 +124,8 @@ void avdtp_reset_stream_endpoint(avdtp_stream_endpoint_t * stream_endpoint){
stream_endpoint->remote_configuration_bitmap = 0;
memset(&stream_endpoint->remote_configuration, 0, sizeof(avdtp_capabilities_t));
// temporary reconfigure SBC config used by A2DP
memset(stream_endpoint->reconfigure_media_codec_sbc_info, 0, 4);
// temporary SBC config used by A2DP Source
memset(stream_endpoint->media_codec_sbc_info, 0, 4);
stream_endpoint->media_disconnect = 0;
stream_endpoint->media_connect = 0;