xref: /btstack/src/classic/a2dp_source.c (revision ea8aa2085ae3e25931720f7e568374a0866ebf9b)
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 
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)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 
a2dp_source_packet_handler_internal(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)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 }
a2dp_source_register_packet_handler(btstack_packet_handler_t callback)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 
a2dp_source_init(void)114274391e8SMilanka Ringwald void a2dp_source_init(void){
11515ff8d31SMatthias Ringwald     a2dp_init();
11677092f3eSMatthias Ringwald     avdtp_source_init();
117274391e8SMilanka Ringwald }
118274391e8SMilanka Ringwald 
a2dp_source_deinit(void)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 
a2dp_source_create_stream_endpoint(avdtp_media_type_t media_type,avdtp_media_codec_type_t media_codec_type,const uint8_t * codec_capabilities,uint16_t codec_capabilities_len,uint8_t * codec_configuration,uint16_t codec_configuration_len)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 
a2dp_source_finalize_stream_endpoint(avdtp_stream_endpoint_t * stream_endpoint)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 
a2dp_source_establish_stream(bd_addr_t remote_addr,uint16_t * avdtp_cid)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 
a2dp_source_disconnect(uint16_t avdtp_cid)19087d9d508SMilanka Ringwald uint8_t a2dp_source_disconnect(uint16_t avdtp_cid){
19187d9d508SMilanka Ringwald     return avdtp_disconnect(avdtp_cid);
192ba155c22SMilanka Ringwald }
193ba155c22SMilanka Ringwald 
a2dp_source_start_stream(uint16_t avdtp_cid,uint8_t local_seid)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 
a2dp_source_pause_stream(uint16_t avdtp_cid,uint8_t local_seid)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 
a2dp_source_stream_endpoint_request_can_send_now(uint16_t avdtp_cid,uint8_t local_seid)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 
a2dp_max_media_payload_size(uint16_t avdtp_cid,uint8_t local_seid)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 
21042780893SMatthias Ringwald uint8_t
a2dp_source_stream_send_media_payload_rtp(uint16_t a2dp_cid,uint8_t local_seid,uint8_t marker,uint32_t timestamp,uint8_t * payload,uint16_t payload_size)21142780893SMatthias Ringwald a2dp_source_stream_send_media_payload_rtp(uint16_t a2dp_cid, uint8_t local_seid, uint8_t marker, uint32_t timestamp,
21242780893SMatthias Ringwald                                           uint8_t *payload, uint16_t payload_size) {
21342780893SMatthias Ringwald     return avdtp_source_stream_send_media_payload_rtp(a2dp_cid, local_seid, marker, timestamp, payload, payload_size);
2148869c787SBjoern Hartmann }
2158869c787SBjoern Hartmann 
a2dp_source_stream_send_media_packet(uint16_t a2dp_cid,uint8_t local_seid,const uint8_t * packet,uint16_t size)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 
a2dp_source_set_config_sbc(uint16_t a2dp_cid,uint8_t local_seid,uint8_t remote_seid,const avdtp_configuration_sbc_t * configuration)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 
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)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 
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)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 
a2dp_source_set_config_atrac(uint16_t a2dp_cid,uint8_t local_seid,uint8_t remote_seid,const avdtp_configuration_atrac_t * configuration)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 
a2dp_source_set_config_other(uint16_t a2dp_cid,uint8_t local_seid,uint8_t remote_seid,const uint8_t * media_codec_information,uint8_t media_codec_information_len)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 
a2dp_source_reconfigure_stream_sampling_frequency(uint16_t avdtp_cid,uint32_t sampling_frequency)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;
258*ea8aa208SMilanka Ringwald     uint8_t status;
25980555d91SMatthias Ringwald     switch (codec_type){
26080555d91SMatthias Ringwald         case AVDTP_CODEC_SBC:
26180555d91SMatthias Ringwald             codec_info_len = 4;
262ffc20ab4SMatthias 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);
263*ea8aa208SMilanka Ringwald             status = avdtp_config_sbc_set_sampling_frequency(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, sampling_frequency);
26433553e7eSMatthias Ringwald             break;
26580555d91SMatthias Ringwald         case AVDTP_CODEC_MPEG_1_2_AUDIO:
26680555d91SMatthias Ringwald             codec_info_len = 4;
267ffc20ab4SMatthias 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);
268*ea8aa208SMilanka Ringwald             status = avdtp_config_mpeg_audio_set_sampling_frequency(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, sampling_frequency);
26933553e7eSMatthias Ringwald             break;
27080555d91SMatthias Ringwald         case AVDTP_CODEC_MPEG_2_4_AAC:
27180555d91SMatthias Ringwald             codec_info_len = 6;
272ffc20ab4SMatthias 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);
273*ea8aa208SMilanka Ringwald             status = avdtp_config_mpeg_aac_set_sampling_frequency(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, sampling_frequency);
27433553e7eSMatthias Ringwald             break;
27580555d91SMatthias Ringwald         case AVDTP_CODEC_ATRAC_FAMILY:
27680555d91SMatthias Ringwald             codec_info_len = 7;
277ffc20ab4SMatthias 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);
278*ea8aa208SMilanka Ringwald             status = avdtp_config_atrac_set_sampling_frequency(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, sampling_frequency);
27933553e7eSMatthias Ringwald             break;
2805bfc74a2SMatthias Ringwald         default:
28133553e7eSMatthias Ringwald             return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
28233553e7eSMatthias Ringwald     }
283*ea8aa208SMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
284*ea8aa208SMilanka Ringwald         return status;
285*ea8aa208SMilanka Ringwald     }
28633553e7eSMatthias Ringwald     avdtp_capabilities_t new_configuration;
28733553e7eSMatthias Ringwald     new_configuration.media_codec.media_type = AVDTP_AUDIO;
28880555d91SMatthias Ringwald     new_configuration.media_codec.media_codec_type = codec_type;
28980555d91SMatthias Ringwald     new_configuration.media_codec.media_codec_information_len = codec_info_len;
290ffc20ab4SMatthias Ringwald     new_configuration.media_codec.media_codec_information = connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info;
29133553e7eSMatthias Ringwald 
29233553e7eSMatthias Ringwald     // start reconfigure
293ffc20ab4SMatthias Ringwald     connection->a2dp_source_config_process.state = A2DP_W2_RECONFIGURE_WITH_SEID;
29433553e7eSMatthias Ringwald 
29533553e7eSMatthias Ringwald     return avdtp_source_reconfigure(
29633553e7eSMatthias Ringwald             avdtp_cid,
297ffc20ab4SMatthias Ringwald             avdtp_stream_endpoint_seid(connection->a2dp_source_config_process.local_stream_endpoint),
298ffc20ab4SMatthias Ringwald             connection->a2dp_source_config_process.local_stream_endpoint->remote_sep.seid,
29933553e7eSMatthias Ringwald             1 << AVDTP_MEDIA_CODEC,
30033553e7eSMatthias Ringwald             new_configuration
30133553e7eSMatthias Ringwald     );
30233553e7eSMatthias Ringwald }
303a95794ceSMatthias Ringwald 
a2dp_source_media_config_validator_callback(const avdtp_stream_endpoint_t * stream_endpoint,const uint8_t * event,uint16_t size)304a95794ceSMatthias Ringwald static uint8_t a2dp_source_media_config_validator_callback(const avdtp_stream_endpoint_t * stream_endpoint, const uint8_t * event, uint16_t size){
305a95794ceSMatthias Ringwald     uint8_t error = 0;
30659dedcb9SMatthias Ringwald     if (a2dp_source_media_config_validator != NULL) {
307a95794ceSMatthias Ringwald         // update subevent id and call validator
308a95794ceSMatthias Ringwald         uint8_t avdtp_subevent_id = event[2];
309a95794ceSMatthias Ringwald         uint8_t a2dp_subevent_id = a2dp_subevent_id_for_avdtp_subevent_id(avdtp_subevent_id);
310a95794ceSMatthias Ringwald         uint8_t * subevent_field = (uint8_t *) &event[2];
311a95794ceSMatthias Ringwald         *subevent_field = a2dp_subevent_id;
312a95794ceSMatthias Ringwald         error = (*a2dp_source_media_config_validator)(stream_endpoint, event, size);
313a95794ceSMatthias Ringwald         *subevent_field = avdtp_subevent_id;
314a95794ceSMatthias Ringwald     }
315a95794ceSMatthias Ringwald     return error;
316a95794ceSMatthias Ringwald }
317a95794ceSMatthias Ringwald 
a2dp_source_register_media_config_validator(uint8_t (* callback)(const avdtp_stream_endpoint_t * stream_endpoint,const uint8_t * event,uint16_t size))3183421998fSMatthias 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)){
319a95794ceSMatthias Ringwald     a2dp_source_media_config_validator = callback;
320a95794ceSMatthias Ringwald     avdtp_source_register_media_config_validator(&a2dp_source_media_config_validator_callback);
321a95794ceSMatthias Ringwald }
322a95794ceSMatthias Ringwald 
323