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