1b442c9e6SMilanka Ringwald 2b442c9e6SMilanka Ringwald /* 3b442c9e6SMilanka Ringwald * Copyright (C) 2016 BlueKitchen GmbH 4b442c9e6SMilanka Ringwald * 5b442c9e6SMilanka Ringwald * Redistribution and use in source and binary forms, with or without 6b442c9e6SMilanka Ringwald * modification, are permitted provided that the following conditions 7b442c9e6SMilanka Ringwald * are met: 8b442c9e6SMilanka Ringwald * 9b442c9e6SMilanka Ringwald * 1. Redistributions of source code must retain the above copyright 10b442c9e6SMilanka Ringwald * notice, this list of conditions and the following disclaimer. 11b442c9e6SMilanka Ringwald * 2. Redistributions in binary form must reproduce the above copyright 12b442c9e6SMilanka Ringwald * notice, this list of conditions and the following disclaimer in the 13b442c9e6SMilanka Ringwald * documentation and/or other materials provided with the distribution. 14b442c9e6SMilanka Ringwald * 3. Neither the name of the copyright holders nor the names of 15b442c9e6SMilanka Ringwald * contributors may be used to endorse or promote products derived 16b442c9e6SMilanka Ringwald * from this software without specific prior written permission. 17b442c9e6SMilanka Ringwald * 4. Any redistribution, use, or modification is done solely for 18b442c9e6SMilanka Ringwald * personal benefit and not for any commercial purpose or for 19b442c9e6SMilanka Ringwald * monetary gain. 20b442c9e6SMilanka Ringwald * 21b442c9e6SMilanka Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 22b442c9e6SMilanka Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23b442c9e6SMilanka Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 242fca4dadSMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 252fca4dadSMilanka Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26b442c9e6SMilanka Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27b442c9e6SMilanka Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 28b442c9e6SMilanka Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29b442c9e6SMilanka Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30b442c9e6SMilanka Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 31b442c9e6SMilanka Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32b442c9e6SMilanka Ringwald * SUCH DAMAGE. 33b442c9e6SMilanka Ringwald * 34b442c9e6SMilanka Ringwald * Please inquire about commercial licensing options at 35b442c9e6SMilanka Ringwald * [email protected] 36b442c9e6SMilanka Ringwald * 37b442c9e6SMilanka Ringwald */ 38b442c9e6SMilanka Ringwald 39696f1abaSMatthias Ringwald /** 40696f1abaSMatthias Ringwald * Supported use cases: 41696f1abaSMatthias Ringwald * - single incoming connection: sep discovery starts and stream will get setup if remote sink sep with SBC is found 42696f1abaSMatthias Ringwald * - single outgoing connection: see above 43696f1abaSMatthias Ringwald * - outgoing and incoming connection to same device: 44696f1abaSMatthias Ringwald * - if outgoing is triggered first, incoming will get ignored. 45696f1abaSMatthias Ringwald * - if incoming starts first, start ougoing will fail, but incoming will succeed. 46696f1abaSMatthias Ringwald * - outgoing and incoming connections to different devices: 47696f1abaSMatthias Ringwald * - if outgoing is first, incoming gets ignored. 48696f1abaSMatthias Ringwald * - if incoming starts first SEP discovery will get stopped and outgoing will succeed. 49696f1abaSMatthias Ringwald */ 50696f1abaSMatthias Ringwald 51e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "a2dp_source.c" 52b442c9e6SMilanka Ringwald 53b442c9e6SMilanka Ringwald #include <stdint.h> 54b442c9e6SMilanka Ringwald #include <string.h> 55b442c9e6SMilanka Ringwald 5684e3541eSMilanka Ringwald #include "bluetooth_psm.h" 5784e3541eSMilanka Ringwald #include "bluetooth_sdp.h" 5884e3541eSMilanka Ringwald #include "btstack_debug.h" 5984e3541eSMilanka Ringwald #include "btstack_event.h" 6015ff8d31SMatthias Ringwald #include "classic/a2dp.h" 614cb889a5SMilanka Ringwald #include "classic/a2dp_source.h" 6284e3541eSMilanka Ringwald #include "classic/avdtp_source.h" 6384e3541eSMilanka Ringwald #include "classic/avdtp_util.h" 6484e3541eSMilanka Ringwald #include "classic/sdp_util.h" 6584e3541eSMilanka Ringwald #include "l2cap.h" 666ce6ec61SMatthias Ringwald #include "a2dp.h" 67b442c9e6SMilanka Ringwald 689f84611fSMatthias Ringwald static const char * a2dp_source_default_service_name = "BTstack A2DP Source Service"; 69137e2954SMatthias Ringwald static const char * a2dp_default_source_service_provider_name = "BTstack A2DP Source Service Provider"; 70cc85b8eaSMatthias Ringwald 71a95794ceSMatthias Ringwald static uint8_t (*a2dp_source_media_config_validator)(const avdtp_stream_endpoint_t * stream_endpoint, const uint8_t * event, uint16_t size); 720d176b65SMilanka Ringwald 73b442c9e6SMilanka Ringwald void a2dp_source_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name){ 749f84611fSMatthias Ringwald if (service_provider_name == NULL){ 759f84611fSMatthias Ringwald service_provider_name = a2dp_default_source_service_provider_name; 76b442c9e6SMilanka Ringwald } 779f84611fSMatthias Ringwald if (service_name == NULL){ 789f84611fSMatthias Ringwald service_name = a2dp_source_default_service_name; 79b442c9e6SMilanka Ringwald } 809f84611fSMatthias Ringwald a2dp_create_sdp_record(service, service_record_handle, BLUETOOTH_SERVICE_CLASS_AUDIO_SOURCE, 819f84611fSMatthias Ringwald supported_features, service_name, service_provider_name); 82b442c9e6SMilanka Ringwald } 83b442c9e6SMilanka Ringwald 842f9a3826SMatthias Ringwald static void a2dp_source_packet_handler_internal(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 852f9a3826SMatthias Ringwald UNUSED(channel); 862f9a3826SMatthias Ringwald UNUSED(size); 872f9a3826SMatthias Ringwald 882f9a3826SMatthias Ringwald if (packet_type != HCI_EVENT_PACKET) return; 892f9a3826SMatthias Ringwald if (hci_event_packet_get_type(packet) != HCI_EVENT_AVDTP_META) return; 902f9a3826SMatthias Ringwald 912f9a3826SMatthias Ringwald switch (hci_event_avdtp_meta_get_subevent_code(packet)){ 922f9a3826SMatthias Ringwald 932f9a3826SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_DELAY_REPORT: 942f9a3826SMatthias Ringwald a2dp_replace_subevent_id_and_emit_source(packet, size, A2DP_SUBEVENT_SIGNALING_DELAY_REPORT); 952f9a3826SMatthias Ringwald break; 962f9a3826SMatthias Ringwald 972f9a3826SMatthias Ringwald case AVDTP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW: 982f9a3826SMatthias Ringwald a2dp_replace_subevent_id_and_emit_source(packet, size, A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW); 992f9a3826SMatthias Ringwald break; 1002f9a3826SMatthias Ringwald 1012f9a3826SMatthias Ringwald default: 1022e01864aSMatthias Ringwald // forward events to config process 103302e9e52SMatthias Ringwald a2dp_config_process_avdtp_event_handler(AVDTP_ROLE_SOURCE, packet, size); 1042f9a3826SMatthias Ringwald break; 1052f9a3826SMatthias Ringwald } 106ba155c22SMilanka Ringwald } 107274391e8SMilanka Ringwald void a2dp_source_register_packet_handler(btstack_packet_handler_t callback){ 108cf95ebe8SMatthias Ringwald btstack_assert(callback != NULL); 109cf95ebe8SMatthias Ringwald 11038106e95SMatthias Ringwald avdtp_source_register_packet_handler(&a2dp_source_packet_handler_internal); 1116ed41ce8SMatthias Ringwald a2dp_register_source_packet_handler(callback); 112274391e8SMilanka Ringwald } 113274391e8SMilanka Ringwald 114274391e8SMilanka Ringwald void a2dp_source_init(void){ 11515ff8d31SMatthias Ringwald a2dp_init(); 11677092f3eSMatthias Ringwald avdtp_source_init(); 117274391e8SMilanka Ringwald } 118274391e8SMilanka Ringwald 1197569dc61SMatthias Ringwald void a2dp_source_deinit(void){ 12015ff8d31SMatthias Ringwald a2dp_deinit(); 1217569dc61SMatthias Ringwald avdtp_source_deinit(); 122137e2954SMatthias Ringwald a2dp_source_media_config_validator = NULL; 1237569dc61SMatthias Ringwald } 1247569dc61SMatthias Ringwald 1257078e434SMilanka Ringwald avdtp_stream_endpoint_t * a2dp_source_create_stream_endpoint(avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, 1263e6cf581SMatthias Ringwald const uint8_t *codec_capabilities, uint16_t codec_capabilities_len, 12782767773SMatthias Ringwald uint8_t * codec_configuration, uint16_t codec_configuration_len){ 128cc85b8eaSMatthias Ringwald avdtp_stream_endpoint_t * stream_endpoint = avdtp_source_create_stream_endpoint(AVDTP_SOURCE, media_type); 129cc85b8eaSMatthias Ringwald if (!stream_endpoint){ 1307078e434SMilanka Ringwald return NULL; 1314567cc17SMilanka Ringwald } 132cc85b8eaSMatthias Ringwald avdtp_source_register_media_transport_category(avdtp_stream_endpoint_seid(stream_endpoint)); 133cc85b8eaSMatthias Ringwald avdtp_source_register_media_codec_category(avdtp_stream_endpoint_seid(stream_endpoint), media_type, media_codec_type, 134ba155c22SMilanka Ringwald codec_capabilities, codec_capabilities_len); 135cc85b8eaSMatthias Ringwald avdtp_source_register_delay_reporting_category(avdtp_stream_endpoint_seid(stream_endpoint)); 13682767773SMatthias Ringwald 13782767773SMatthias Ringwald // store user codec configuration buffer 138636a58caSBjoern Hartmann stream_endpoint->media_codec_type = media_codec_type; 139cc85b8eaSMatthias Ringwald stream_endpoint->media_codec_configuration_info = codec_configuration; 140cc85b8eaSMatthias Ringwald stream_endpoint->media_codec_configuration_len = codec_configuration_len; 14182767773SMatthias Ringwald 142cc85b8eaSMatthias Ringwald return stream_endpoint; 143274391e8SMilanka Ringwald } 144ba155c22SMilanka Ringwald 14517ddf501SMatthias Ringwald void a2dp_source_finalize_stream_endpoint(avdtp_stream_endpoint_t * stream_endpoint){ 14617ddf501SMatthias Ringwald avdtp_source_finalize_stream_endpoint(stream_endpoint); 14717ddf501SMatthias Ringwald } 14817ddf501SMatthias Ringwald 1494c5b56ffSMatthias Ringwald uint8_t a2dp_source_establish_stream(bd_addr_t remote_addr, uint16_t *avdtp_cid) { 150cd94cb8eSMatthias Ringwald 151696f1abaSMatthias Ringwald uint16_t outgoing_cid; 152696f1abaSMatthias Ringwald 153cd0bbc03SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_bd_addr(remote_addr); 154cd0bbc03SMatthias Ringwald if (connection == NULL){ 155cd94cb8eSMatthias Ringwald uint8_t status = avdtp_source_connect(remote_addr, &outgoing_cid); 156cd94cb8eSMatthias Ringwald if (status != ERROR_CODE_SUCCESS) { 157696f1abaSMatthias Ringwald // if there's already a connection for for remote addr, avdtp_source_connect fails, 158696f1abaSMatthias Ringwald // but the stream will get set-up nevertheless 159cd94cb8eSMatthias Ringwald return status; 160cd94cb8eSMatthias Ringwald } 161ff43929dSMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(outgoing_cid); 162ff43929dSMatthias Ringwald btstack_assert(connection != NULL); 16387d9d508SMilanka Ringwald 164696f1abaSMatthias Ringwald // setup state 165ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.outgoing_active = true; 166ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.state = A2DP_W4_CONNECTED; 167a4ec7672SMilanka Ringwald *avdtp_cid = outgoing_cid; 168696f1abaSMatthias Ringwald 169cd0bbc03SMatthias Ringwald } else { 170ffc20ab4SMatthias Ringwald if (connection->a2dp_source_config_process.outgoing_active || connection->a2dp_source_config_process.stream_endpoint_configured) { 171b7eb14d4SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 172b7eb14d4SMatthias Ringwald } 173b7eb14d4SMatthias Ringwald 174cd0bbc03SMatthias Ringwald // check state 175ffc20ab4SMatthias Ringwald switch (connection->a2dp_source_config_process.state){ 176cd0bbc03SMatthias Ringwald case A2DP_IDLE: 177cd0bbc03SMatthias Ringwald case A2DP_CONNECTED: 178cd0bbc03SMatthias Ringwald // restart process e.g. if there no suitable stream endpoints or they had been in use 179ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.outgoing_active = true; 180cd0bbc03SMatthias Ringwald *avdtp_cid = connection->avdtp_cid; 181302e9e52SMatthias Ringwald a2dp_config_process_ready_for_sep_discovery(AVDTP_ROLE_SOURCE, connection); 182cd0bbc03SMatthias Ringwald break; 183cd0bbc03SMatthias Ringwald default: 184cd0bbc03SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 185cd0bbc03SMatthias Ringwald } 186cd0bbc03SMatthias Ringwald } 18787d9d508SMilanka Ringwald return ERROR_CODE_SUCCESS; 188ba155c22SMilanka Ringwald } 189ba155c22SMilanka Ringwald 19087d9d508SMilanka Ringwald uint8_t a2dp_source_disconnect(uint16_t avdtp_cid){ 19187d9d508SMilanka Ringwald return avdtp_disconnect(avdtp_cid); 192ba155c22SMilanka Ringwald } 193ba155c22SMilanka Ringwald 19487d9d508SMilanka Ringwald uint8_t a2dp_source_start_stream(uint16_t avdtp_cid, uint8_t local_seid){ 19587d9d508SMilanka Ringwald return avdtp_start_stream(avdtp_cid, local_seid); 196b548dda6SMilanka Ringwald } 197b548dda6SMilanka Ringwald 19887d9d508SMilanka Ringwald uint8_t a2dp_source_pause_stream(uint16_t avdtp_cid, uint8_t local_seid){ 19987d9d508SMilanka Ringwald return avdtp_suspend_stream(avdtp_cid, local_seid); 20046e6b063SMilanka Ringwald } 20146e6b063SMilanka Ringwald 20287d9d508SMilanka Ringwald void a2dp_source_stream_endpoint_request_can_send_now(uint16_t avdtp_cid, uint8_t local_seid){ 20387d9d508SMilanka Ringwald avdtp_source_stream_endpoint_request_can_send_now(avdtp_cid, local_seid); 20446e6b063SMilanka Ringwald } 20546e6b063SMilanka Ringwald 20687d9d508SMilanka Ringwald int a2dp_max_media_payload_size(uint16_t avdtp_cid, uint8_t local_seid){ 20787d9d508SMilanka Ringwald return avdtp_max_media_payload_size(avdtp_cid, local_seid); 2081f6397ceSMilanka Ringwald } 2091f6397ceSMilanka Ringwald 210*42780893SMatthias Ringwald uint8_t 211*42780893SMatthias Ringwald a2dp_source_stream_send_media_payload_rtp(uint16_t a2dp_cid, uint8_t local_seid, uint8_t marker, uint32_t timestamp, 212*42780893SMatthias Ringwald uint8_t *payload, uint16_t payload_size) { 213*42780893SMatthias Ringwald return avdtp_source_stream_send_media_payload_rtp(a2dp_cid, local_seid, marker, timestamp, payload, payload_size); 2148869c787SBjoern Hartmann } 2158869c787SBjoern Hartmann 2165695c5ccSMatthias Ringwald uint8_t a2dp_source_stream_send_media_packet(uint16_t a2dp_cid, uint8_t local_seid, const uint8_t * packet, uint16_t size){ 2175695c5ccSMatthias Ringwald return avdtp_source_stream_send_media_packet(a2dp_cid, local_seid, packet, size); 2185695c5ccSMatthias Ringwald } 2195695c5ccSMatthias Ringwald 220cbfa0fc6SMatthias Ringwald 2218691a66cSMatthias Ringwald uint8_t a2dp_source_set_config_sbc(uint16_t a2dp_cid, uint8_t local_seid, uint8_t remote_seid, const avdtp_configuration_sbc_t * configuration){ 222a6adb322SMatthias Ringwald return a2dp_config_process_set_sbc(AVDTP_ROLE_SOURCE, a2dp_cid, local_seid, remote_seid, configuration); 223cbfa0fc6SMatthias Ringwald } 2240fa4346cSMatthias Ringwald 2258691a66cSMatthias Ringwald uint8_t a2dp_source_set_config_mpeg_audio(uint16_t a2dp_cid, uint8_t local_seid, uint8_t remote_seid, const avdtp_configuration_mpeg_audio_t * configuration){ 226a6adb322SMatthias Ringwald return a2dp_config_process_set_mpeg_audio(AVDTP_ROLE_SOURCE, a2dp_cid, local_seid, remote_seid, configuration); 227cbfa0fc6SMatthias Ringwald } 228cbfa0fc6SMatthias Ringwald 2298691a66cSMatthias Ringwald uint8_t a2dp_source_set_config_mpeg_aac(uint16_t a2dp_cid, uint8_t local_seid, uint8_t remote_seid, const avdtp_configuration_mpeg_aac_t * configuration){ 230a6adb322SMatthias Ringwald return a2dp_config_process_set_mpeg_aac(AVDTP_ROLE_SOURCE, a2dp_cid, local_seid, remote_seid, configuration); 231cbfa0fc6SMatthias Ringwald } 232cbfa0fc6SMatthias Ringwald 2338691a66cSMatthias Ringwald uint8_t a2dp_source_set_config_atrac(uint16_t a2dp_cid, uint8_t local_seid, uint8_t remote_seid, const avdtp_configuration_atrac_t * configuration){ 234a6adb322SMatthias Ringwald return a2dp_config_process_set_atrac(AVDTP_ROLE_SOURCE, a2dp_cid, local_seid, remote_seid, configuration); 235cbfa0fc6SMatthias Ringwald } 236cbfa0fc6SMatthias Ringwald 237cbfa0fc6SMatthias Ringwald uint8_t a2dp_source_set_config_other(uint16_t a2dp_cid, uint8_t local_seid, uint8_t remote_seid, 238cbfa0fc6SMatthias Ringwald const uint8_t * media_codec_information, uint8_t media_codec_information_len){ 239a6adb322SMatthias Ringwald return a2dp_config_process_set_other(AVDTP_ROLE_SOURCE, a2dp_cid, local_seid, remote_seid, media_codec_information, media_codec_information_len); 2400fa4346cSMatthias Ringwald } 24133553e7eSMatthias Ringwald 24233553e7eSMatthias Ringwald uint8_t a2dp_source_reconfigure_stream_sampling_frequency(uint16_t avdtp_cid, uint32_t sampling_frequency){ 24333553e7eSMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(avdtp_cid); 24433553e7eSMatthias Ringwald if (connection == NULL){ 24533553e7eSMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 24633553e7eSMatthias Ringwald } 24733553e7eSMatthias Ringwald 248ffc20ab4SMatthias Ringwald if (connection->a2dp_source_config_process.state != A2DP_STREAMING_OPENED) { 24933553e7eSMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 25033553e7eSMatthias Ringwald } 25133553e7eSMatthias Ringwald 252ffc20ab4SMatthias Ringwald btstack_assert(connection->a2dp_source_config_process.local_stream_endpoint != NULL); 253cc85b8eaSMatthias Ringwald 25433553e7eSMatthias Ringwald log_info("Reconfigure avdtp_cid 0x%02x", avdtp_cid); 25533553e7eSMatthias Ringwald 256ffc20ab4SMatthias Ringwald avdtp_media_codec_type_t codec_type = connection->a2dp_source_config_process.local_stream_endpoint->sep.capabilities.media_codec.media_codec_type; 25780555d91SMatthias Ringwald uint8_t codec_info_len; 25880555d91SMatthias Ringwald switch (codec_type){ 25980555d91SMatthias Ringwald case AVDTP_CODEC_SBC: 26080555d91SMatthias Ringwald codec_info_len = 4; 261ffc20ab4SMatthias Ringwald (void)memcpy(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, connection->a2dp_source_config_process.local_stream_endpoint->remote_sep.configuration.media_codec.media_codec_information, codec_info_len); 262ffc20ab4SMatthias Ringwald avdtp_config_sbc_set_sampling_frequency(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, sampling_frequency); 26333553e7eSMatthias Ringwald break; 26480555d91SMatthias Ringwald case AVDTP_CODEC_MPEG_1_2_AUDIO: 26580555d91SMatthias Ringwald codec_info_len = 4; 266ffc20ab4SMatthias Ringwald (void)memcpy(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, connection->a2dp_source_config_process.local_stream_endpoint->remote_sep.configuration.media_codec.media_codec_information, codec_info_len); 267ffc20ab4SMatthias Ringwald avdtp_config_mpeg_audio_set_sampling_frequency(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, sampling_frequency); 26833553e7eSMatthias Ringwald break; 26980555d91SMatthias Ringwald case AVDTP_CODEC_MPEG_2_4_AAC: 27080555d91SMatthias Ringwald codec_info_len = 6; 271ffc20ab4SMatthias Ringwald (void)memcpy(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, connection->a2dp_source_config_process.local_stream_endpoint->remote_sep.configuration.media_codec.media_codec_information, codec_info_len); 272ffc20ab4SMatthias Ringwald avdtp_config_mpeg_aac_set_sampling_frequency(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, sampling_frequency); 27333553e7eSMatthias Ringwald break; 27480555d91SMatthias Ringwald case AVDTP_CODEC_ATRAC_FAMILY: 27580555d91SMatthias Ringwald codec_info_len = 7; 276ffc20ab4SMatthias Ringwald (void)memcpy(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, connection->a2dp_source_config_process.local_stream_endpoint->remote_sep.configuration.media_codec.media_codec_information, codec_info_len); 277ffc20ab4SMatthias Ringwald avdtp_config_atrac_set_sampling_frequency(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, sampling_frequency); 27833553e7eSMatthias Ringwald break; 2795bfc74a2SMatthias Ringwald default: 28033553e7eSMatthias Ringwald return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 28133553e7eSMatthias Ringwald } 28233553e7eSMatthias Ringwald 28333553e7eSMatthias Ringwald avdtp_capabilities_t new_configuration; 28433553e7eSMatthias Ringwald new_configuration.media_codec.media_type = AVDTP_AUDIO; 28580555d91SMatthias Ringwald new_configuration.media_codec.media_codec_type = codec_type; 28680555d91SMatthias Ringwald new_configuration.media_codec.media_codec_information_len = codec_info_len; 287ffc20ab4SMatthias Ringwald new_configuration.media_codec.media_codec_information = connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info; 28833553e7eSMatthias Ringwald 28933553e7eSMatthias Ringwald // start reconfigure 290ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.state = A2DP_W2_RECONFIGURE_WITH_SEID; 29133553e7eSMatthias Ringwald 29233553e7eSMatthias Ringwald return avdtp_source_reconfigure( 29333553e7eSMatthias Ringwald avdtp_cid, 294ffc20ab4SMatthias Ringwald avdtp_stream_endpoint_seid(connection->a2dp_source_config_process.local_stream_endpoint), 295ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.local_stream_endpoint->remote_sep.seid, 29633553e7eSMatthias Ringwald 1 << AVDTP_MEDIA_CODEC, 29733553e7eSMatthias Ringwald new_configuration 29833553e7eSMatthias Ringwald ); 29933553e7eSMatthias Ringwald } 300a95794ceSMatthias Ringwald 301a95794ceSMatthias Ringwald static uint8_t a2dp_source_media_config_validator_callback(const avdtp_stream_endpoint_t * stream_endpoint, const uint8_t * event, uint16_t size){ 302a95794ceSMatthias Ringwald uint8_t error = 0; 30359dedcb9SMatthias Ringwald if (a2dp_source_media_config_validator != NULL) { 304a95794ceSMatthias Ringwald // update subevent id and call validator 305a95794ceSMatthias Ringwald uint8_t avdtp_subevent_id = event[2]; 306a95794ceSMatthias Ringwald uint8_t a2dp_subevent_id = a2dp_subevent_id_for_avdtp_subevent_id(avdtp_subevent_id); 307a95794ceSMatthias Ringwald uint8_t * subevent_field = (uint8_t *) &event[2]; 308a95794ceSMatthias Ringwald *subevent_field = a2dp_subevent_id; 309a95794ceSMatthias Ringwald error = (*a2dp_source_media_config_validator)(stream_endpoint, event, size); 310a95794ceSMatthias Ringwald *subevent_field = avdtp_subevent_id; 311a95794ceSMatthias Ringwald } 312a95794ceSMatthias Ringwald return error; 313a95794ceSMatthias Ringwald } 314a95794ceSMatthias Ringwald 3153421998fSMatthias Ringwald void a2dp_source_register_media_config_validator(uint8_t (*callback)(const avdtp_stream_endpoint_t * stream_endpoint, const uint8_t * event, uint16_t size)){ 316a95794ceSMatthias Ringwald a2dp_source_media_config_validator = callback; 317a95794ceSMatthias Ringwald avdtp_source_register_media_config_validator(&a2dp_source_media_config_validator_callback); 318a95794ceSMatthias Ringwald } 319a95794ceSMatthias Ringwald 320