17b02edb6SMatthias Ringwald /*
27b02edb6SMatthias Ringwald * Copyright (C) 2022 BlueKitchen GmbH
37b02edb6SMatthias Ringwald *
47b02edb6SMatthias Ringwald * Redistribution and use in source and binary forms, with or without
57b02edb6SMatthias Ringwald * modification, are permitted provided that the following conditions
67b02edb6SMatthias Ringwald * are met:
77b02edb6SMatthias Ringwald *
87b02edb6SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright
97b02edb6SMatthias Ringwald * notice, this list of conditions and the following disclaimer.
107b02edb6SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright
117b02edb6SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the
127b02edb6SMatthias Ringwald * documentation and/or other materials provided with the distribution.
137b02edb6SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of
147b02edb6SMatthias Ringwald * contributors may be used to endorse or promote products derived
157b02edb6SMatthias Ringwald * from this software without specific prior written permission.
167b02edb6SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for
177b02edb6SMatthias Ringwald * personal benefit and not for any commercial purpose or for
187b02edb6SMatthias Ringwald * monetary gain.
197b02edb6SMatthias Ringwald *
207b02edb6SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
217b02edb6SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
227b02edb6SMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
237b02edb6SMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
247b02edb6SMatthias Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
257b02edb6SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
267b02edb6SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
277b02edb6SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
287b02edb6SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
297b02edb6SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
307b02edb6SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
317b02edb6SMatthias Ringwald * SUCH DAMAGE.
327b02edb6SMatthias Ringwald *
337b02edb6SMatthias Ringwald * Please inquire about commercial licensing options at
347b02edb6SMatthias Ringwald * contact@bluekitchen-gmbh.com
357b02edb6SMatthias Ringwald *
367b02edb6SMatthias Ringwald */
377b02edb6SMatthias Ringwald
38db449e89SMatthias Ringwald #define BTSTACK_FILE__ "a2dp.c"
39db449e89SMatthias Ringwald
406ce6ec61SMatthias Ringwald #include <stdint.h>
416ce6ec61SMatthias Ringwald #include <string.h>
426ce6ec61SMatthias Ringwald #include "a2dp.h"
436ce6ec61SMatthias Ringwald #include "l2cap.h"
446ce6ec61SMatthias Ringwald #include "classic/sdp_util.h"
456ce6ec61SMatthias Ringwald #include "classic/avdtp_source.h"
466ce6ec61SMatthias Ringwald #include "classic/a2dp_source.h"
476ce6ec61SMatthias Ringwald #include "btstack_event.h"
486ce6ec61SMatthias Ringwald #include "bluetooth_sdp.h"
496ce6ec61SMatthias Ringwald #include "bluetooth_psm.h"
506ce6ec61SMatthias Ringwald
516ed41ce8SMatthias Ringwald #include <stddef.h>
526ed41ce8SMatthias Ringwald #include "bluetooth.h"
537b02edb6SMatthias Ringwald #include "classic/a2dp.h"
546ed41ce8SMatthias Ringwald #include "classic/avdtp_util.h"
556ed41ce8SMatthias Ringwald #include "btstack_debug.h"
566ed41ce8SMatthias Ringwald
5772237cc2SMatthias Ringwald // ENABLE_A2DP_SOURCE_EXPLICIT_CONFIG has been replaced by ENABLE_A2DP_EXPLICIT_CONFIG which is valid for both roles
5872237cc2SMatthias Ringwald #ifdef ENABLE_A2DP_SOURCE_EXPLICIT_CONFIG
5972237cc2SMatthias Ringwald #error "Please define ENABLE_A2DP_EXPLICIT_CONFIG instead of ENABLE_A2DP_SOURCE_EXPLICIT_CONFIG"
6072237cc2SMatthias Ringwald #endif
6172237cc2SMatthias Ringwald
62cb9e52f6SMatthias Ringwald #define AVDTP_MAX_SEP_NUM 10
63cb9e52f6SMatthias Ringwald #define A2DP_SET_CONFIG_DELAY_MS 200
64cb9e52f6SMatthias Ringwald
650b2fa017SMatthias Ringwald static void a2dp_config_process_discover_seps_with_next_waiting_connection(void);
66cb9e52f6SMatthias Ringwald
676ed41ce8SMatthias Ringwald // higher layer callbacks
686ed41ce8SMatthias Ringwald static btstack_packet_handler_t a2dp_source_callback;
6993aeaf99SMatthias Ringwald static btstack_packet_handler_t a2dp_sink_callback;
707b02edb6SMatthias Ringwald
71cb9e52f6SMatthias Ringwald // config process - singletons using sep_discovery_cid is used as mutex
72fe7cbb3bSMatthias Ringwald static avdtp_role_t a2dp_config_process_role;
73a2f9d3cbSMatthias Ringwald static uint16_t a2dp_config_process_sep_discovery_cid;
74a2f9d3cbSMatthias Ringwald static uint16_t a2dp_config_process_sep_discovery_count;
75a2f9d3cbSMatthias Ringwald static uint16_t a2dp_config_process_sep_discovery_index;
76a2f9d3cbSMatthias Ringwald static avdtp_sep_t a2dp_config_process_sep_discovery_seps[AVDTP_MAX_SEP_NUM];
77a2f9d3cbSMatthias Ringwald static btstack_timer_source_t a2dp_config_process_set_config_timer;
78a2f9d3cbSMatthias Ringwald static bool a2dp_config_process_set_config_timer_active;
79cb9e52f6SMatthias Ringwald
a2dp_init(void)8015ff8d31SMatthias Ringwald void a2dp_init(void) {
8115ff8d31SMatthias Ringwald }
8215ff8d31SMatthias Ringwald
a2dp_deinit(void)8315ff8d31SMatthias Ringwald void a2dp_deinit(void){
84a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_cid = 0;
85b776322eSMatthias Ringwald a2dp_source_callback = NULL;
86b776322eSMatthias Ringwald a2dp_sink_callback = NULL;
8715ff8d31SMatthias Ringwald }
886ed41ce8SMatthias Ringwald
a2dp_create_sdp_record(uint8_t * service,uint32_t service_record_handle,uint16_t service_class_uuid,uint16_t supported_features,const char * service_name,const char * service_provider_name)899f84611fSMatthias Ringwald void a2dp_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t service_class_uuid, uint16_t supported_features, const char * service_name, const char * service_provider_name){
909f84611fSMatthias Ringwald uint8_t* attribute;
919f84611fSMatthias Ringwald de_create_sequence(service);
929f84611fSMatthias Ringwald
939f84611fSMatthias Ringwald // 0x0000 "Service Record Handle"
949f84611fSMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE);
959f84611fSMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle);
969f84611fSMatthias Ringwald
979f84611fSMatthias Ringwald // 0x0001 "Service Class ID List"
989f84611fSMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST);
999f84611fSMatthias Ringwald attribute = de_push_sequence(service);
1009f84611fSMatthias Ringwald {
1019f84611fSMatthias Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, service_class_uuid);
1029f84611fSMatthias Ringwald }
1039f84611fSMatthias Ringwald de_pop_sequence(service, attribute);
1049f84611fSMatthias Ringwald
1059f84611fSMatthias Ringwald // 0x0004 "Protocol Descriptor List"
1069f84611fSMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST);
1079f84611fSMatthias Ringwald attribute = de_push_sequence(service);
1089f84611fSMatthias Ringwald {
1099f84611fSMatthias Ringwald uint8_t* l2cpProtocol = de_push_sequence(attribute);
1109f84611fSMatthias Ringwald {
1119f84611fSMatthias Ringwald de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
1129f84611fSMatthias Ringwald de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, BLUETOOTH_PSM_AVDTP);
1139f84611fSMatthias Ringwald }
1149f84611fSMatthias Ringwald de_pop_sequence(attribute, l2cpProtocol);
1159f84611fSMatthias Ringwald
1169f84611fSMatthias Ringwald uint8_t* avProtocol = de_push_sequence(attribute);
1179f84611fSMatthias Ringwald {
1189f84611fSMatthias Ringwald de_add_number(avProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVDTP); // avProtocol_service
1199f84611fSMatthias Ringwald de_add_number(avProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version
1209f84611fSMatthias Ringwald }
1219f84611fSMatthias Ringwald de_pop_sequence(attribute, avProtocol);
1229f84611fSMatthias Ringwald }
1239f84611fSMatthias Ringwald de_pop_sequence(service, attribute);
1249f84611fSMatthias Ringwald
1259f84611fSMatthias Ringwald // 0x0005 "Public Browse Group"
1269f84611fSMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BROWSE_GROUP_LIST); // public browse group
1279f84611fSMatthias Ringwald attribute = de_push_sequence(service);
1289f84611fSMatthias Ringwald {
1299f84611fSMatthias Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT);
1309f84611fSMatthias Ringwald }
1319f84611fSMatthias Ringwald de_pop_sequence(service, attribute);
1329f84611fSMatthias Ringwald
1339f84611fSMatthias Ringwald // 0x0009 "Bluetooth Profile Descriptor List"
1349f84611fSMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
1359f84611fSMatthias Ringwald attribute = de_push_sequence(service);
1369f84611fSMatthias Ringwald {
1379f84611fSMatthias Ringwald uint8_t *a2dProfile = de_push_sequence(attribute);
1389f84611fSMatthias Ringwald {
1399f84611fSMatthias Ringwald de_add_number(a2dProfile, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_ADVANCED_AUDIO_DISTRIBUTION);
1402dd31b96SMatthias Ringwald de_add_number(a2dProfile, DE_UINT, DE_SIZE_16, 0x0104);
1419f84611fSMatthias Ringwald }
1429f84611fSMatthias Ringwald de_pop_sequence(attribute, a2dProfile);
1439f84611fSMatthias Ringwald }
1449f84611fSMatthias Ringwald de_pop_sequence(service, attribute);
1459f84611fSMatthias Ringwald
1469f84611fSMatthias Ringwald
1479f84611fSMatthias Ringwald // 0x0100 "Service Name"
1481041da4bSMatthias Ringwald if (strlen(service_name) > 0){
1499f84611fSMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100);
150ab2445a0SMatthias Ringwald de_add_data(service, DE_STRING, (uint16_t) strlen(service_name), (uint8_t *) service_name);
1511041da4bSMatthias Ringwald }
1529f84611fSMatthias Ringwald
1539f84611fSMatthias Ringwald // 0x0100 "Provider Name"
1541041da4bSMatthias Ringwald if (strlen(service_provider_name) > 0) {
1559f84611fSMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102);
156ab2445a0SMatthias Ringwald de_add_data(service, DE_STRING, (uint16_t) strlen(service_provider_name), (uint8_t *) service_provider_name);
1571041da4bSMatthias Ringwald }
1589f84611fSMatthias Ringwald
1599f84611fSMatthias Ringwald // 0x0311 "Supported Features"
1609f84611fSMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311);
1619f84611fSMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, supported_features);
1629f84611fSMatthias Ringwald }
1639f84611fSMatthias Ringwald
a2dp_subevent_id_for_avdtp_subevent_id(uint8_t subevent)16493aeaf99SMatthias Ringwald uint8_t a2dp_subevent_id_for_avdtp_subevent_id(uint8_t subevent){
16593aeaf99SMatthias Ringwald switch (subevent){
16693aeaf99SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION:
16793aeaf99SMatthias Ringwald return A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION;
16893aeaf99SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_MPEG_AUDIO_CONFIGURATION:
16993aeaf99SMatthias Ringwald return A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_MPEG_AUDIO_CONFIGURATION;
17093aeaf99SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_MPEG_AAC_CONFIGURATION:
17193aeaf99SMatthias Ringwald return A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_MPEG_AAC_CONFIGURATION;
17293aeaf99SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_ATRAC_CONFIGURATION:
17393aeaf99SMatthias Ringwald return A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_ATRAC_CONFIGURATION;
17493aeaf99SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION:
17593aeaf99SMatthias Ringwald return A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION;
17693aeaf99SMatthias Ringwald default:
17793aeaf99SMatthias Ringwald btstack_unreachable();
17893aeaf99SMatthias Ringwald return 0;
17993aeaf99SMatthias Ringwald }
18093aeaf99SMatthias Ringwald }
18193aeaf99SMatthias Ringwald
a2dp_replace_subevent_id_and_emit(btstack_packet_handler_t callback,uint8_t * packet,uint16_t size,uint8_t subevent_id)18293aeaf99SMatthias Ringwald static void a2dp_replace_subevent_id_and_emit(btstack_packet_handler_t callback, uint8_t * packet, uint16_t size, uint8_t subevent_id){
18393aeaf99SMatthias Ringwald UNUSED(size);
18493aeaf99SMatthias Ringwald btstack_assert(callback != NULL);
18593aeaf99SMatthias Ringwald // cache orig event and subevent id
18693aeaf99SMatthias Ringwald uint8_t orig_event_id = packet[0];
18793aeaf99SMatthias Ringwald uint8_t orig_subevent_id = packet[2];
18893aeaf99SMatthias Ringwald // execute callback
18993aeaf99SMatthias Ringwald packet[0] = HCI_EVENT_A2DP_META;
19093aeaf99SMatthias Ringwald packet[2] = subevent_id;
19193aeaf99SMatthias Ringwald (*callback)(HCI_EVENT_PACKET, 0, packet, size);
19293aeaf99SMatthias Ringwald // restore id
19393aeaf99SMatthias Ringwald packet[0] = orig_event_id;
19493aeaf99SMatthias Ringwald packet[2] = orig_subevent_id;
19593aeaf99SMatthias Ringwald }
19693aeaf99SMatthias Ringwald
a2dp_register_source_packet_handler(btstack_packet_handler_t callback)1976ed41ce8SMatthias Ringwald void a2dp_register_source_packet_handler(btstack_packet_handler_t callback){
1986ed41ce8SMatthias Ringwald btstack_assert(callback != NULL);
1996ed41ce8SMatthias Ringwald a2dp_source_callback = callback;
2006ed41ce8SMatthias Ringwald }
2016ed41ce8SMatthias Ringwald
a2dp_register_sink_packet_handler(btstack_packet_handler_t callback)202b776322eSMatthias Ringwald void a2dp_register_sink_packet_handler(btstack_packet_handler_t callback){
203b776322eSMatthias Ringwald btstack_assert(callback != NULL);
204b776322eSMatthias Ringwald a2dp_sink_callback = callback;
205b776322eSMatthias Ringwald }
206b776322eSMatthias Ringwald
a2dp_replace_subevent_id_and_emit_source(uint8_t * packet,uint16_t size,uint8_t subevent_id)207b776322eSMatthias Ringwald void a2dp_replace_subevent_id_and_emit_source(uint8_t * packet, uint16_t size, uint8_t subevent_id) {
208b776322eSMatthias Ringwald a2dp_replace_subevent_id_and_emit(a2dp_source_callback, packet, size, subevent_id);
209b776322eSMatthias Ringwald }
210b776322eSMatthias Ringwald
a2dp_replace_subevent_id_and_emit_sink(uint8_t * packet,uint16_t size,uint8_t subevent_id)2118e4dc1feSMatthias Ringwald void a2dp_replace_subevent_id_and_emit_sink(uint8_t *packet, uint16_t size, uint8_t subevent_id) {
212b776322eSMatthias Ringwald a2dp_replace_subevent_id_and_emit(a2dp_sink_callback, packet, size, subevent_id);
213b776322eSMatthias Ringwald }
214b776322eSMatthias Ringwald
a2dp_replace_subevent_id_and_emit_for_role(avdtp_role_t role,uint8_t * packet,uint16_t size,uint8_t subevent_id)2156e55450eSMatthias Ringwald static void a2dp_replace_subevent_id_and_emit_for_role(avdtp_role_t role, uint8_t * packet, uint16_t size, uint8_t subevent_id) {
2166e55450eSMatthias Ringwald if (role == AVDTP_ROLE_SOURCE){
2176e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_source(packet, size, subevent_id);
2186e55450eSMatthias Ringwald } else {
2196e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_sink(packet, size, subevent_id);
2206e55450eSMatthias Ringwald }
2216e55450eSMatthias Ringwald }
2226e55450eSMatthias Ringwald
a2dp_emit_role(avdtp_role_t role,uint8_t * packet,uint16_t size)223b776322eSMatthias Ringwald static void a2dp_emit_role(avdtp_role_t role, uint8_t * packet, uint16_t size){
224b776322eSMatthias Ringwald if (role == AVDTP_ROLE_SOURCE){
2256ed41ce8SMatthias Ringwald (*a2dp_source_callback)(HCI_EVENT_PACKET, 0, packet, size);
226b776322eSMatthias Ringwald } else {
227b776322eSMatthias Ringwald (*a2dp_sink_callback)(HCI_EVENT_PACKET, 0, packet, size);
2286ed41ce8SMatthias Ringwald }
22993aeaf99SMatthias Ringwald }
23093aeaf99SMatthias Ringwald
a2dp_emit_stream_event_for_role(avdtp_role_t role,uint16_t cid,uint8_t local_seid,uint8_t subevent_id)2318e4dc1feSMatthias Ringwald static void a2dp_emit_stream_event_for_role(avdtp_role_t role, uint16_t cid, uint8_t local_seid, uint8_t subevent_id) {
232b776322eSMatthias Ringwald uint8_t event[6];
233b776322eSMatthias Ringwald int pos = 0;
234b776322eSMatthias Ringwald event[pos++] = HCI_EVENT_A2DP_META;
235b776322eSMatthias Ringwald event[pos++] = sizeof(event) - 2;
236b776322eSMatthias Ringwald event[pos++] = subevent_id;
237b776322eSMatthias Ringwald little_endian_store_16(event, pos, cid);
238b776322eSMatthias Ringwald pos += 2;
239b776322eSMatthias Ringwald event[pos++] = local_seid;
240b776322eSMatthias Ringwald a2dp_emit_role(role, event, sizeof(event));
241b776322eSMatthias Ringwald }
242b776322eSMatthias Ringwald
a2dp_emit_stream_reconfigured_role(avdtp_role_t role,uint16_t cid,uint8_t local_seid,uint8_t status)2438e4dc1feSMatthias Ringwald static void a2dp_emit_stream_reconfigured_role(avdtp_role_t role, uint16_t cid, uint8_t local_seid, uint8_t status){
2445f2add9cSMatthias Ringwald uint8_t event[7];
2455f2add9cSMatthias Ringwald int pos = 0;
2465f2add9cSMatthias Ringwald event[pos++] = HCI_EVENT_A2DP_META;
2475f2add9cSMatthias Ringwald event[pos++] = sizeof(event) - 2;
2485f2add9cSMatthias Ringwald event[pos++] = A2DP_SUBEVENT_STREAM_RECONFIGURED;
2495f2add9cSMatthias Ringwald little_endian_store_16(event, pos, cid);
2505f2add9cSMatthias Ringwald pos += 2;
2515f2add9cSMatthias Ringwald event[pos++] = local_seid;
2525f2add9cSMatthias Ringwald event[pos++] = status;
253b776322eSMatthias Ringwald a2dp_emit_role(role, event, sizeof(event));
2545f2add9cSMatthias Ringwald }
2555f2add9cSMatthias Ringwald
a2dp_emit_streaming_connection_failed_for_role(avdtp_role_t role,avdtp_connection_t * connection,uint8_t status)2568e4dc1feSMatthias Ringwald static void a2dp_emit_streaming_connection_failed_for_role(avdtp_role_t role, avdtp_connection_t *connection, uint8_t status) {
2575f2add9cSMatthias Ringwald uint8_t event[14];
2585f2add9cSMatthias Ringwald int pos = 0;
2595f2add9cSMatthias Ringwald event[pos++] = HCI_EVENT_A2DP_META;
2605f2add9cSMatthias Ringwald event[pos++] = sizeof(event) - 2;
2615f2add9cSMatthias Ringwald event[pos++] = A2DP_SUBEVENT_STREAM_ESTABLISHED;
2625f2add9cSMatthias Ringwald little_endian_store_16(event, pos, connection->avdtp_cid);
2635f2add9cSMatthias Ringwald pos += 2;
2645f2add9cSMatthias Ringwald reverse_bd_addr(connection->remote_addr, &event[pos]);
2655f2add9cSMatthias Ringwald pos += 6;
2665f2add9cSMatthias Ringwald event[pos++] = 0;
2675f2add9cSMatthias Ringwald event[pos++] = 0;
2685f2add9cSMatthias Ringwald event[pos++] = status;
269b776322eSMatthias Ringwald a2dp_emit_role(role, event, sizeof(event));
2705f2add9cSMatthias Ringwald }
2715f2add9cSMatthias Ringwald
a2dp_config_process_for_role(avdtp_role_t role,avdtp_connection_t * connection)272b62e4190SMatthias Ringwald static a2dp_config_process_t * a2dp_config_process_for_role(avdtp_role_t role, avdtp_connection_t *connection){
273c3df44f0SMatthias Ringwald return (role == AVDTP_ROLE_SOURCE) ? &connection->a2dp_source_config_process : &connection->a2dp_sink_config_process;
274b62e4190SMatthias Ringwald }
2754648d259SMatthias Ringwald
a2dp_config_process_timer_handler(btstack_timer_source_t * timer)276b62e4190SMatthias Ringwald static void a2dp_config_process_timer_handler(btstack_timer_source_t * timer){
277cb9e52f6SMatthias Ringwald uint16_t avdtp_cid = (uint16_t)(uintptr_t) btstack_run_loop_get_timer_context(timer);
278cb9e52f6SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(avdtp_cid);
279a2f9d3cbSMatthias Ringwald btstack_run_loop_set_timer_context(&a2dp_config_process_set_config_timer, NULL);
280a2f9d3cbSMatthias Ringwald a2dp_config_process_set_config_timer_active = false;
281cb9e52f6SMatthias Ringwald
282b62e4190SMatthias Ringwald log_info("Config timer fired, avdtp_cid 0x%02x", avdtp_cid);
283cb9e52f6SMatthias Ringwald
284cb9e52f6SMatthias Ringwald if (connection == NULL) {
2850b2fa017SMatthias Ringwald a2dp_config_process_discover_seps_with_next_waiting_connection();
286cb9e52f6SMatthias Ringwald return;
287cb9e52f6SMatthias Ringwald }
288b62e4190SMatthias Ringwald a2dp_config_process_t * config_process = a2dp_config_process_for_role(a2dp_config_process_role, connection);
289b62e4190SMatthias Ringwald if (config_process->stream_endpoint_configured) {
2900b2fa017SMatthias Ringwald a2dp_config_process_discover_seps_with_next_waiting_connection();
291b62e4190SMatthias Ringwald return;
292cb9e52f6SMatthias Ringwald }
293cb9e52f6SMatthias Ringwald
294b62e4190SMatthias Ringwald avdtp_discover_stream_endpoints(avdtp_cid);
295b62e4190SMatthias Ringwald }
296b62e4190SMatthias Ringwald
a2dp_config_process_timer_start(uint16_t avdtp_cid)297b62e4190SMatthias Ringwald static void a2dp_config_process_timer_start(uint16_t avdtp_cid){
298b62e4190SMatthias Ringwald log_info("Config timer start for cid 0%02x", avdtp_cid);
299a2f9d3cbSMatthias Ringwald a2dp_config_process_set_config_timer_active = true;
300a2f9d3cbSMatthias Ringwald btstack_run_loop_remove_timer(&a2dp_config_process_set_config_timer);
301b62e4190SMatthias Ringwald btstack_run_loop_set_timer_handler(&a2dp_config_process_set_config_timer, a2dp_config_process_timer_handler);
302a2f9d3cbSMatthias Ringwald btstack_run_loop_set_timer(&a2dp_config_process_set_config_timer, A2DP_SET_CONFIG_DELAY_MS);
303a2f9d3cbSMatthias Ringwald btstack_run_loop_set_timer_context(&a2dp_config_process_set_config_timer, (void *)(uintptr_t)avdtp_cid);
304a2f9d3cbSMatthias Ringwald btstack_run_loop_add_timer(&a2dp_config_process_set_config_timer);
305cb9e52f6SMatthias Ringwald }
306cb9e52f6SMatthias Ringwald
a2dp_config_process_timer_restart(void)307b62e4190SMatthias Ringwald static void a2dp_config_process_timer_restart(void){
308b62e4190SMatthias Ringwald log_info("Config timer restart");
309a2f9d3cbSMatthias Ringwald btstack_run_loop_remove_timer(&a2dp_config_process_set_config_timer);
310a2f9d3cbSMatthias Ringwald btstack_run_loop_set_timer(&a2dp_config_process_set_config_timer, A2DP_SET_CONFIG_DELAY_MS);
311a2f9d3cbSMatthias Ringwald btstack_run_loop_add_timer(&a2dp_config_process_set_config_timer);
312cb9e52f6SMatthias Ringwald }
313cb9e52f6SMatthias Ringwald
a2dp_config_process_timer_stop(void)314b62e4190SMatthias Ringwald static void a2dp_config_process_timer_stop(void){
315a2f9d3cbSMatthias Ringwald if (a2dp_config_process_set_config_timer_active == false) return;
316b62e4190SMatthias Ringwald log_info("Config timer stop");
317a2f9d3cbSMatthias Ringwald btstack_run_loop_remove_timer(&a2dp_config_process_set_config_timer);
318a2f9d3cbSMatthias Ringwald btstack_run_loop_set_timer_context(&a2dp_config_process_set_config_timer, NULL);
319a2f9d3cbSMatthias Ringwald a2dp_config_process_set_config_timer_active = false;
320cb9e52f6SMatthias Ringwald }
321cb9e52f6SMatthias Ringwald
322cb9e52f6SMatthias Ringwald // Discover seps, both incoming and outgoing
a2dp_config_process_start_discovering_seps(avdtp_role_t role,avdtp_connection_t * connection)3230b2fa017SMatthias Ringwald static void a2dp_config_process_start_discovering_seps(avdtp_role_t role, avdtp_connection_t *connection) {
324b62e4190SMatthias Ringwald a2dp_config_process_t * config_process = a2dp_config_process_for_role(role, connection);
325b62e4190SMatthias Ringwald
326b62e4190SMatthias Ringwald config_process->state = A2DP_DISCOVER_SEPS;
327b62e4190SMatthias Ringwald config_process->discover_seps = false;
328cb9e52f6SMatthias Ringwald
329fe7cbb3bSMatthias Ringwald a2dp_config_process_role = role;
330a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_index = 0;
331a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_count = 0;
332a2f9d3cbSMatthias Ringwald memset(a2dp_config_process_sep_discovery_seps, 0, sizeof(avdtp_sep_t) * AVDTP_MAX_SEP_NUM);
333a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_cid = connection->avdtp_cid;
334cb9e52f6SMatthias Ringwald
335cb9e52f6SMatthias Ringwald // if we initiated the connection, start config right away, else wait a bit to give remote a chance to do it first
336b62e4190SMatthias Ringwald if (config_process->outgoing_active){
337cb9e52f6SMatthias Ringwald log_info("discover seps");
338b62e4190SMatthias Ringwald avdtp_discover_stream_endpoints(connection->avdtp_cid);
339cb9e52f6SMatthias Ringwald } else {
340cb9e52f6SMatthias Ringwald log_info("wait a bit, then discover seps");
341b62e4190SMatthias Ringwald a2dp_config_process_timer_start(connection->avdtp_cid);
342cb9e52f6SMatthias Ringwald }
343cb9e52f6SMatthias Ringwald }
344cb9e52f6SMatthias Ringwald
a2dp_config_process_discover_seps_with_next_waiting_connection(void)3450b2fa017SMatthias Ringwald static void a2dp_config_process_discover_seps_with_next_waiting_connection(void){
346a2f9d3cbSMatthias Ringwald btstack_assert(a2dp_config_process_sep_discovery_cid == 0);
347cb9e52f6SMatthias Ringwald btstack_linked_list_iterator_t it;
348cb9e52f6SMatthias Ringwald btstack_linked_list_iterator_init(&it, avdtp_get_connections());
349cb9e52f6SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
350cb9e52f6SMatthias Ringwald avdtp_connection_t * next_connection = (avdtp_connection_t *)btstack_linked_list_iterator_next(&it);
351fe7cbb3bSMatthias Ringwald if (next_connection->a2dp_source_config_process.discover_seps) {
3520b2fa017SMatthias Ringwald a2dp_config_process_start_discovering_seps(AVDTP_ROLE_SOURCE, next_connection);
353fe7cbb3bSMatthias Ringwald }
354fe7cbb3bSMatthias Ringwald if (next_connection->a2dp_sink_config_process.discover_seps) {
3550b2fa017SMatthias Ringwald a2dp_config_process_start_discovering_seps(AVDTP_ROLE_SINK, next_connection);
356fe7cbb3bSMatthias Ringwald }
357cb9e52f6SMatthias Ringwald }
358cb9e52f6SMatthias Ringwald }
359cb9e52f6SMatthias Ringwald
a2dp_config_process_ready_for_sep_discovery(avdtp_role_t role,avdtp_connection_t * connection)360302e9e52SMatthias Ringwald void a2dp_config_process_ready_for_sep_discovery(avdtp_role_t role, avdtp_connection_t *connection) {
361cb9e52f6SMatthias Ringwald // start discover seps now if:
362cb9e52f6SMatthias Ringwald // - outgoing active: signaling for outgoing connection
363cb9e52f6SMatthias Ringwald // - outgoing not active: incoming connection and no sep discover ongoing
364cb9e52f6SMatthias Ringwald
365cb9e52f6SMatthias Ringwald // sep discovery active?
366a2f9d3cbSMatthias Ringwald if (a2dp_config_process_sep_discovery_cid == 0){
3670b2fa017SMatthias Ringwald a2dp_config_process_start_discovering_seps(role, connection);
368cb9e52f6SMatthias Ringwald } else {
369cb9e52f6SMatthias Ringwald // post-pone sep discovery
370b62e4190SMatthias Ringwald a2dp_config_process_t * config_process = a2dp_config_process_for_role(role, connection);
371b62e4190SMatthias Ringwald config_process->discover_seps = true;
372cb9e52f6SMatthias Ringwald }
373cb9e52f6SMatthias Ringwald }
374cb9e52f6SMatthias Ringwald
a2dp_config_process_handle_media_configuration(avdtp_role_t role,const uint8_t * packet,uint8_t local_seid)3750b2fa017SMatthias Ringwald static void a2dp_config_process_handle_media_configuration(avdtp_role_t role, const uint8_t *packet, uint8_t local_seid) {
376cb9e52f6SMatthias Ringwald uint16_t cid = avdtp_subevent_signaling_media_codec_sbc_configuration_get_avdtp_cid(packet);
3770b2fa017SMatthias Ringwald avdtp_connection_t *connection = avdtp_get_connection_for_avdtp_cid(cid);
3780b2fa017SMatthias Ringwald btstack_assert(connection != NULL);
3790b2fa017SMatthias Ringwald a2dp_config_process_t * config_process = a2dp_config_process_for_role(role, connection);
3800b2fa017SMatthias Ringwald
3810b2fa017SMatthias Ringwald config_process->local_stream_endpoint = avdtp_get_stream_endpoint_for_seid(local_seid);
382cb9e52f6SMatthias Ringwald // bail out if local seid invalid
3830b2fa017SMatthias Ringwald if (config_process->local_stream_endpoint == NULL) return;
384cb9e52f6SMatthias Ringwald
385cb9e52f6SMatthias Ringwald // stop timer
386a2f9d3cbSMatthias Ringwald if (a2dp_config_process_sep_discovery_cid == cid) {
387b62e4190SMatthias Ringwald a2dp_config_process_timer_stop();
388a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_cid = 0;
389cb9e52f6SMatthias Ringwald }
390cb9e52f6SMatthias Ringwald
3910b2fa017SMatthias Ringwald config_process->stream_endpoint_configured = true;
392cb9e52f6SMatthias Ringwald
3930b2fa017SMatthias Ringwald switch (config_process->state) {
394cb9e52f6SMatthias Ringwald case A2DP_W4_SET_CONFIGURATION:
395cb9e52f6SMatthias Ringwald // outgoing: discovery and config of remote sink sep successful, trigger stream open
3960b2fa017SMatthias Ringwald config_process->state = A2DP_W2_OPEN_STREAM_WITH_SEID;
397cb9e52f6SMatthias Ringwald break;
398cb9e52f6SMatthias Ringwald case A2DP_DISCOVER_SEPS:
399cb9e52f6SMatthias Ringwald case A2DP_GET_CAPABILITIES:
400cb9e52f6SMatthias Ringwald case A2DP_W2_GET_ALL_CAPABILITIES:
401cb9e52f6SMatthias Ringwald case A2DP_DISCOVERY_DONE:
402cb9e52f6SMatthias Ringwald case A2DP_W4_GET_CONFIGURATION:
403cb9e52f6SMatthias Ringwald // incoming: wait for stream open
4040b2fa017SMatthias Ringwald config_process->state = A2DP_W4_OPEN_STREAM_WITH_SEID;
405cb9e52f6SMatthias Ringwald break;
406cb9e52f6SMatthias Ringwald default:
407cb9e52f6SMatthias Ringwald // wait for configuration after sending reconfigure - keep state
408cb9e52f6SMatthias Ringwald break;
409cb9e52f6SMatthias Ringwald }
410cb9e52f6SMatthias Ringwald }
411cb9e52f6SMatthias Ringwald
a2dp_config_process_set_config(avdtp_role_t role,avdtp_connection_t * connection)412302e9e52SMatthias Ringwald void a2dp_config_process_set_config(avdtp_role_t role, avdtp_connection_t *connection) {
413c3df44f0SMatthias Ringwald a2dp_config_process_t * config_process = a2dp_config_process_for_role(role, connection);
4148db801acSMatthias Ringwald uint8_t local_seid = avdtp_stream_endpoint_seid(config_process->local_stream_endpoint);
415c3df44f0SMatthias Ringwald uint8_t remote_seid = config_process->local_stream_endpoint->set_config_remote_seid;
416cb9e52f6SMatthias Ringwald log_info("A2DP initiate set configuration locally and wait for response ... local seid 0x%02x, remote seid 0x%02x",
4178db801acSMatthias Ringwald local_seid, remote_seid);
4180b2fa017SMatthias Ringwald config_process->state = A2DP_W4_SET_CONFIGURATION;
4190b2fa017SMatthias Ringwald avdtp_set_configuration(connection->avdtp_cid,
4208db801acSMatthias Ringwald local_seid,
421cb9e52f6SMatthias Ringwald remote_seid,
4220b2fa017SMatthias Ringwald config_process->local_stream_endpoint->remote_configuration_bitmap,
4230b2fa017SMatthias Ringwald config_process->local_stream_endpoint->remote_configuration);
424cb9e52f6SMatthias Ringwald }
425cb9e52f6SMatthias Ringwald
4260b2fa017SMatthias Ringwald static void
a2dp_config_process_handle_media_capability(avdtp_role_t role,uint16_t cid,uint8_t a2dp_subevent_id,uint8_t * packet,uint16_t size)4270b2fa017SMatthias Ringwald a2dp_config_process_handle_media_capability(avdtp_role_t role, uint16_t cid, uint8_t a2dp_subevent_id, uint8_t *packet, uint16_t size) {
428cb9e52f6SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(cid);
429cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
4300b2fa017SMatthias Ringwald a2dp_config_process_t * config_process = a2dp_config_process_for_role(role, connection);
4310b2fa017SMatthias Ringwald if (config_process->state != A2DP_GET_CAPABILITIES) return;
4326e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size, a2dp_subevent_id);
433cb9e52f6SMatthias Ringwald }
434cb9e52f6SMatthias Ringwald
a2dp_config_process_config_init(avdtp_role_t role,avdtp_connection_t * connection,uint8_t local_seid,uint8_t remote_seid,avdtp_media_codec_type_t codec_type)435302e9e52SMatthias Ringwald uint8_t a2dp_config_process_config_init(avdtp_role_t role, avdtp_connection_t *connection, uint8_t local_seid, uint8_t remote_seid,
436cb9e52f6SMatthias Ringwald avdtp_media_codec_type_t codec_type) {
437cb9e52f6SMatthias Ringwald
438cb9e52f6SMatthias Ringwald // check state
4390b2fa017SMatthias Ringwald a2dp_config_process_t * config_process = a2dp_config_process_for_role(role, connection);
4400b2fa017SMatthias Ringwald switch (config_process->state){
441cb9e52f6SMatthias Ringwald case A2DP_DISCOVERY_DONE:
442cb9e52f6SMatthias Ringwald case A2DP_GET_CAPABILITIES:
443cb9e52f6SMatthias Ringwald break;
444cb9e52f6SMatthias Ringwald default:
445cb9e52f6SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
446cb9e52f6SMatthias Ringwald }
447cb9e52f6SMatthias Ringwald
448cb9e52f6SMatthias Ringwald // lookup local stream endpoint
449cb9e52f6SMatthias Ringwald avdtp_stream_endpoint_t * stream_endpoint = avdtp_get_stream_endpoint_for_seid(local_seid);
450cb9e52f6SMatthias Ringwald if (stream_endpoint == NULL){
451cb9e52f6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
452cb9e52f6SMatthias Ringwald }
453cb9e52f6SMatthias Ringwald
454cb9e52f6SMatthias Ringwald // lookup remote stream endpoint
455cb9e52f6SMatthias Ringwald avdtp_sep_t * remote_sep = NULL;
456cb9e52f6SMatthias Ringwald uint8_t i;
457a2f9d3cbSMatthias Ringwald for (i=0; i < a2dp_config_process_sep_discovery_count; i++){
458a2f9d3cbSMatthias Ringwald if (a2dp_config_process_sep_discovery_seps[i].seid == remote_seid){
459a2f9d3cbSMatthias Ringwald remote_sep = &a2dp_config_process_sep_discovery_seps[i];
460cb9e52f6SMatthias Ringwald }
461cb9e52f6SMatthias Ringwald }
462cb9e52f6SMatthias Ringwald if (remote_sep == NULL){
463cb9e52f6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
464cb9e52f6SMatthias Ringwald }
465cb9e52f6SMatthias Ringwald
466cb9e52f6SMatthias Ringwald // set media configuration
467cb9e52f6SMatthias Ringwald stream_endpoint->remote_configuration_bitmap = store_bit16(stream_endpoint->remote_configuration_bitmap, AVDTP_MEDIA_CODEC, 1);
468cb9e52f6SMatthias Ringwald stream_endpoint->remote_configuration.media_codec.media_type = AVDTP_AUDIO;
469cb9e52f6SMatthias Ringwald stream_endpoint->remote_configuration.media_codec.media_codec_type = codec_type;
470cb9e52f6SMatthias Ringwald // remote seid to use
471cb9e52f6SMatthias Ringwald stream_endpoint->set_config_remote_seid = remote_seid;
472cb9e52f6SMatthias Ringwald // enable delay reporting if supported
473cb9e52f6SMatthias Ringwald if (remote_sep->registered_service_categories & (1<<AVDTP_DELAY_REPORTING)){
474cb9e52f6SMatthias Ringwald stream_endpoint->remote_configuration_bitmap = store_bit16(stream_endpoint->remote_configuration_bitmap, AVDTP_DELAY_REPORTING, 1);
475cb9e52f6SMatthias Ringwald }
476cb9e52f6SMatthias Ringwald
4770b2fa017SMatthias Ringwald // suitable stream endpoint found, configure it
4780b2fa017SMatthias Ringwald config_process->local_stream_endpoint = stream_endpoint;
4790b2fa017SMatthias Ringwald config_process->have_config = true;
480cb9e52f6SMatthias Ringwald
48172237cc2SMatthias Ringwald #ifdef ENABLE_A2DP_EXPLICIT_CONFIG
4821c1afb4dSMatthias Ringwald if (config_process->state == A2DP_DISCOVERY_DONE){
483aa3e6424SMatthias Ringwald config_process->state = A2DP_SET_CONFIGURATION;
484cb9e52f6SMatthias Ringwald }
485cb9e52f6SMatthias Ringwald #endif
486cb9e52f6SMatthias Ringwald
487cb9e52f6SMatthias Ringwald return ERROR_CODE_SUCCESS;
488cb9e52f6SMatthias Ringwald }
a2dp_config_process_avdtp_event_handler(avdtp_role_t role,uint8_t * packet,uint16_t size)489302e9e52SMatthias Ringwald void a2dp_config_process_avdtp_event_handler(avdtp_role_t role, uint8_t *packet, uint16_t size) {
490cb9e52f6SMatthias Ringwald uint16_t cid;
491cb9e52f6SMatthias Ringwald avdtp_connection_t * connection;
492aa3e6424SMatthias Ringwald a2dp_config_process_t * config_process;
493cb9e52f6SMatthias Ringwald uint8_t signal_identifier;
494cb9e52f6SMatthias Ringwald uint8_t status;
495cb9e52f6SMatthias Ringwald uint8_t local_seid;
496cb9e52f6SMatthias Ringwald uint8_t remote_seid;
4976d3263ecSMatthias Ringwald bool outgoing_active;
498cb9e52f6SMatthias Ringwald
499cb9e52f6SMatthias Ringwald switch (hci_event_avdtp_meta_get_subevent_code(packet)){
500cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED:
501cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_connection_established_get_avdtp_cid(packet);
502cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
503cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
504aa3e6424SMatthias Ringwald config_process = a2dp_config_process_for_role(role, connection);
505cb9e52f6SMatthias Ringwald
506cb9e52f6SMatthias Ringwald status = avdtp_subevent_signaling_connection_established_get_status(packet);
507cb9e52f6SMatthias Ringwald if (status != ERROR_CODE_SUCCESS){
508cb9e52f6SMatthias Ringwald // notify about connection error only if we're initiator
509aa3e6424SMatthias Ringwald if (config_process->outgoing_active){
510fb301407SMatthias Ringwald log_info("A2DP signaling connection failed status 0x%02x", status);
511aa3e6424SMatthias Ringwald config_process->outgoing_active = false;
5126e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size, A2DP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED);
513fb301407SMatthias Ringwald // also emit streaming connection failed
514fb301407SMatthias Ringwald a2dp_emit_streaming_connection_failed_for_role(role, connection, status);
515cb9e52f6SMatthias Ringwald }
516cb9e52f6SMatthias Ringwald break;
517cb9e52f6SMatthias Ringwald }
518fb301407SMatthias Ringwald log_info("A2DP signaling connection established avdtp_cid 0x%02x", cid);
519aa3e6424SMatthias Ringwald config_process->state = A2DP_CONNECTED;
520cb9e52f6SMatthias Ringwald
521cb9e52f6SMatthias Ringwald // notify app
5226e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size, A2DP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED);
523cb9e52f6SMatthias Ringwald
524cb9e52f6SMatthias Ringwald // Windows 10 as Source starts SEP discovery after 1500 ms, but only if it did not get a Discover command
525cb9e52f6SMatthias Ringwald // If BTstack is configured for both roles, we need to avoid sending Discover command in Source Role for outgoing Sink connections
526cb9e52f6SMatthias Ringwald
527aa3e6424SMatthias Ringwald // For this, we trigger SEP discovery if:
528aa3e6424SMatthias Ringwald // a) this is an outgoing connection
529aa3e6424SMatthias Ringwald // b) this connection wasn't caused by an outgoing connection of the other role
530aa3e6424SMatthias Ringwald if (role == AVDTP_ROLE_SOURCE){
531cb9e52f6SMatthias Ringwald if (connection->a2dp_source_config_process.outgoing_active || !connection->a2dp_sink_config_process.outgoing_active){
532302e9e52SMatthias Ringwald a2dp_config_process_ready_for_sep_discovery(AVDTP_ROLE_SOURCE, connection);
533cb9e52f6SMatthias Ringwald }
534aa3e6424SMatthias Ringwald } else {
535aa3e6424SMatthias Ringwald if (connection->a2dp_sink_config_process.outgoing_active || !connection->a2dp_source_config_process.outgoing_active){
536aa3e6424SMatthias Ringwald a2dp_config_process_ready_for_sep_discovery(AVDTP_ROLE_SINK, connection);
537aa3e6424SMatthias Ringwald }
538aa3e6424SMatthias Ringwald }
539cb9e52f6SMatthias Ringwald break;
540cb9e52f6SMatthias Ringwald
541cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_SEP_FOUND:
542cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_sep_found_get_avdtp_cid(packet);
543cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
544cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
545aa3e6424SMatthias Ringwald config_process = a2dp_config_process_for_role(role, connection);
546cb9e52f6SMatthias Ringwald
547aa3e6424SMatthias Ringwald if (config_process->state == A2DP_DISCOVER_SEPS) {
548cb9e52f6SMatthias Ringwald avdtp_sep_t sep;
549cb9e52f6SMatthias Ringwald memset(&sep, 0, sizeof(avdtp_sep_t));
550cb9e52f6SMatthias Ringwald sep.seid = avdtp_subevent_signaling_sep_found_get_remote_seid(packet);;
551cb9e52f6SMatthias Ringwald sep.in_use = avdtp_subevent_signaling_sep_found_get_in_use(packet);
552cb9e52f6SMatthias Ringwald sep.media_type = (avdtp_media_type_t) avdtp_subevent_signaling_sep_found_get_media_type(packet);
553cb9e52f6SMatthias Ringwald sep.type = (avdtp_sep_type_t) avdtp_subevent_signaling_sep_found_get_sep_type(packet);
554cb9e52f6SMatthias Ringwald log_info("A2DP Found sep: remote seid 0x%02x, in_use %d, media type %d, sep type %s, index %d",
555cb9e52f6SMatthias Ringwald sep.seid, sep.in_use, sep.media_type, sep.type == AVDTP_SOURCE ? "source" : "sink",
556a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_count);
557aa3e6424SMatthias Ringwald avdtp_sep_type_t matching_type = (role == AVDTP_ROLE_SOURCE) ? AVDTP_SINK : AVDTP_SOURCE;
558aa3e6424SMatthias Ringwald if ((sep.type == matching_type) && (sep.in_use == false)) {
559a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_seps[a2dp_config_process_sep_discovery_count++] = sep;
560cb9e52f6SMatthias Ringwald }
561cb9e52f6SMatthias Ringwald }
562cb9e52f6SMatthias Ringwald break;
563cb9e52f6SMatthias Ringwald
564cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_SEP_DICOVERY_DONE:
565cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_sep_dicovery_done_get_avdtp_cid(packet);
566cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
567cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
568aa3e6424SMatthias Ringwald config_process = a2dp_config_process_for_role(role, connection);
569cb9e52f6SMatthias Ringwald
570aa3e6424SMatthias Ringwald if (config_process->state != A2DP_DISCOVER_SEPS) break;
571cb9e52f6SMatthias Ringwald
572a2f9d3cbSMatthias Ringwald if (a2dp_config_process_sep_discovery_count > 0){
573aa3e6424SMatthias Ringwald config_process->state = A2DP_GET_CAPABILITIES;
574a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_index = 0;
575aa3e6424SMatthias Ringwald config_process->have_config = false;
576cb9e52f6SMatthias Ringwald } else {
577aa3e6424SMatthias Ringwald if (config_process->outgoing_active){
578aa3e6424SMatthias Ringwald config_process->outgoing_active = false;
579cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
580cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
581b776322eSMatthias Ringwald a2dp_emit_streaming_connection_failed_for_role(role, connection, ERROR_CODE_CONNECTION_REJECTED_DUE_TO_NO_SUITABLE_CHANNEL_FOUND);
582cb9e52f6SMatthias Ringwald }
583cb9e52f6SMatthias Ringwald
584cb9e52f6SMatthias Ringwald // continue
585aa3e6424SMatthias Ringwald config_process->state = A2DP_CONNECTED;
586a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_cid = 0;
5870b2fa017SMatthias Ringwald a2dp_config_process_discover_seps_with_next_waiting_connection();
588cb9e52f6SMatthias Ringwald }
589cb9e52f6SMatthias Ringwald break;
590cb9e52f6SMatthias Ringwald
591cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY:
592cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_media_codec_sbc_capability_get_avdtp_cid(packet);
593cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
594cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
595aa3e6424SMatthias Ringwald config_process = a2dp_config_process_for_role(role, connection);
596cb9e52f6SMatthias Ringwald
597aa3e6424SMatthias Ringwald if (config_process->state != A2DP_GET_CAPABILITIES) break;
598cb9e52f6SMatthias Ringwald
599cb9e52f6SMatthias Ringwald // forward codec capability
6006e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size, A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY);
601cb9e52f6SMatthias Ringwald
60272237cc2SMatthias Ringwald #ifndef ENABLE_A2DP_EXPLICIT_CONFIG
603cb9e52f6SMatthias Ringwald // select SEP if none configured yet
604aa3e6424SMatthias Ringwald if (config_process->have_config == false){
605cb9e52f6SMatthias Ringwald // find SBC stream endpoint
6065f2add9cSMatthias Ringwald avdtp_sep_type_t required_sep_type = (role == AVDTP_ROLE_SOURCE) ? AVDTP_SOURCE : AVDTP_SINK;
6075f2add9cSMatthias Ringwald avdtp_stream_endpoint_t * stream_endpoint = avdtp_get_source_stream_endpoint_for_media_codec_and_type(AVDTP_CODEC_SBC, required_sep_type);
608cb9e52f6SMatthias Ringwald if (stream_endpoint != NULL){
609cb9e52f6SMatthias Ringwald // choose SBC config params
610cb9e52f6SMatthias Ringwald avdtp_configuration_sbc_t configuration;
611cb9e52f6SMatthias Ringwald configuration.sampling_frequency = avdtp_choose_sbc_sampling_frequency(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_sampling_frequency_bitmap(packet));
612cb9e52f6SMatthias Ringwald configuration.channel_mode = avdtp_choose_sbc_channel_mode(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_channel_mode_bitmap(packet));
613cb9e52f6SMatthias Ringwald configuration.block_length = avdtp_choose_sbc_block_length(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_block_length_bitmap(packet));
614cb9e52f6SMatthias Ringwald configuration.subbands = avdtp_choose_sbc_subbands(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_subbands_bitmap(packet));
615cb9e52f6SMatthias Ringwald configuration.allocation_method = avdtp_choose_sbc_allocation_method(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_allocation_method_bitmap(packet));
616cb9e52f6SMatthias Ringwald configuration.max_bitpool_value = avdtp_choose_sbc_max_bitpool_value(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_max_bitpool_value(packet));
617cb9e52f6SMatthias Ringwald configuration.min_bitpool_value = avdtp_choose_sbc_min_bitpool_value(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_min_bitpool_value(packet));
618cb9e52f6SMatthias Ringwald
619cb9e52f6SMatthias Ringwald // and pre-select this endpoint
620cb9e52f6SMatthias Ringwald local_seid = avdtp_stream_endpoint_seid(stream_endpoint);
621cb9e52f6SMatthias Ringwald remote_seid = avdtp_subevent_signaling_media_codec_sbc_capability_get_remote_seid(packet);
622c3df44f0SMatthias Ringwald a2dp_config_process_set_sbc(role, cid, local_seid, remote_seid, &configuration);
623cb9e52f6SMatthias Ringwald }
624cb9e52f6SMatthias Ringwald }
62572237cc2SMatthias Ringwald #endif
626cb9e52f6SMatthias Ringwald break;
627cb9e52f6SMatthias Ringwald // forward codec capability
628cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_MPEG_AUDIO_CAPABILITY:
629cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_media_codec_mpeg_audio_capability_get_avdtp_cid(packet);
6300b2fa017SMatthias Ringwald a2dp_config_process_handle_media_capability(role, cid,
6310b2fa017SMatthias Ringwald A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_MPEG_AUDIO_CAPABILITY,
6320b2fa017SMatthias Ringwald packet, size);
633cb9e52f6SMatthias Ringwald break;
634cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_MPEG_AAC_CAPABILITY:
635cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_media_codec_mpeg_aac_capability_get_avdtp_cid(packet);
6360b2fa017SMatthias Ringwald a2dp_config_process_handle_media_capability(role, cid,
6370b2fa017SMatthias Ringwald A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_MPEG_AAC_CAPABILITY,
6380b2fa017SMatthias Ringwald packet, size);
639cb9e52f6SMatthias Ringwald break;
640cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_ATRAC_CAPABILITY:
641cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_media_codec_atrac_capability_get_avdtp_cid(packet);
6420b2fa017SMatthias Ringwald a2dp_config_process_handle_media_capability(role, cid, A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_ATRAC_CAPABILITY,
6430b2fa017SMatthias Ringwald packet,
6440b2fa017SMatthias Ringwald size);
645cb9e52f6SMatthias Ringwald break;
646cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY:
647cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_media_codec_other_capability_get_avdtp_cid(packet);
6480b2fa017SMatthias Ringwald a2dp_config_process_handle_media_capability(role, cid, A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY,
6490b2fa017SMatthias Ringwald packet,
6500b2fa017SMatthias Ringwald size);
651cb9e52f6SMatthias Ringwald break;
652cb9e52f6SMatthias Ringwald
653cb9e52f6SMatthias Ringwald // not forwarded
654cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_TRANSPORT_CAPABILITY:
655cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_REPORTING_CAPABILITY:
656cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_RECOVERY_CAPABILITY:
657cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_CONTENT_PROTECTION_CAPABILITY:
658cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_HEADER_COMPRESSION_CAPABILITY:
659cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MULTIPLEXING_CAPABILITY:
660cb9e52f6SMatthias Ringwald break;
661cb9e52f6SMatthias Ringwald
662cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_DELAY_REPORTING_CAPABILITY:
663cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_delay_reporting_capability_get_avdtp_cid(packet);
664cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
665cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
666aa3e6424SMatthias Ringwald config_process = a2dp_config_process_for_role(role, connection);
667c3df44f0SMatthias Ringwald log_info("received AVDTP_SUBEVENT_SIGNALING_DELAY_REPORTING_CAPABILITY, cid 0x%02x, state %d", cid, config_process->state);
668cb9e52f6SMatthias Ringwald
669aa3e6424SMatthias Ringwald if (config_process->state != A2DP_GET_CAPABILITIES) break;
670cb9e52f6SMatthias Ringwald
671cb9e52f6SMatthias Ringwald // store delay reporting capability
672a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_seps[a2dp_config_process_sep_discovery_index].registered_service_categories |= 1 << AVDTP_DELAY_REPORTING;
673cb9e52f6SMatthias Ringwald
6746e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size, A2DP_SUBEVENT_SIGNALING_DELAY_REPORTING_CAPABILITY);
675cb9e52f6SMatthias Ringwald break;
676cb9e52f6SMatthias Ringwald
677cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_CAPABILITIES_DONE:
678cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_capabilities_done_get_avdtp_cid(packet);
679cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
680cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
681aa3e6424SMatthias Ringwald config_process = a2dp_config_process_for_role(role, connection);
682cb9e52f6SMatthias Ringwald
683aa3e6424SMatthias Ringwald if (config_process->state != A2DP_GET_CAPABILITIES) break;
684aa3e6424SMatthias Ringwald
685cb9e52f6SMatthias Ringwald // forward capabilities done for endpoint
6866e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size, A2DP_SUBEVENT_SIGNALING_CAPABILITIES_DONE);
687cb9e52f6SMatthias Ringwald
6885f2add9cSMatthias Ringwald // endpoint was not suitable, check next one if possible
689a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_index++;
6905f2add9cSMatthias Ringwald
691a2f9d3cbSMatthias Ringwald if (a2dp_config_process_sep_discovery_index >= a2dp_config_process_sep_discovery_count){
692cb9e52f6SMatthias Ringwald
693cb9e52f6SMatthias Ringwald // emit 'all capabilities for all seps reported'
694cb9e52f6SMatthias Ringwald uint8_t event[6];
695cb9e52f6SMatthias Ringwald uint8_t pos = 0;
696cb9e52f6SMatthias Ringwald event[pos++] = HCI_EVENT_A2DP_META;
697cb9e52f6SMatthias Ringwald event[pos++] = sizeof(event) - 2;
698cb9e52f6SMatthias Ringwald event[pos++] = A2DP_SUBEVENT_SIGNALING_CAPABILITIES_COMPLETE;
699cb9e52f6SMatthias Ringwald little_endian_store_16(event, pos, cid);
7005f2add9cSMatthias Ringwald a2dp_emit_role(role, event, sizeof(event));
701cb9e52f6SMatthias Ringwald
702cb9e52f6SMatthias Ringwald // do we have a valid config?
703aa3e6424SMatthias Ringwald if (config_process->have_config){
704aa3e6424SMatthias Ringwald config_process->state = A2DP_SET_CONFIGURATION;
705aa3e6424SMatthias Ringwald config_process->have_config = false;
706cb9e52f6SMatthias Ringwald break;
707cb9e52f6SMatthias Ringwald }
708cb9e52f6SMatthias Ringwald
70972237cc2SMatthias Ringwald #ifdef ENABLE_A2DP_EXPLICIT_CONFIG
710aa3e6424SMatthias Ringwald config_process->state = A2DP_DISCOVERY_DONE;
711cb9e52f6SMatthias Ringwald // TODO call a2dp_discover_seps_with_next_waiting_connection?
712cb9e52f6SMatthias Ringwald break;
7135f2add9cSMatthias Ringwald #endif
7145f2add9cSMatthias Ringwald
715cb9e52f6SMatthias Ringwald // we didn't find a suitable SBC stream endpoint, sorry.
716aa3e6424SMatthias Ringwald if (config_process->outgoing_active){
717aa3e6424SMatthias Ringwald config_process->outgoing_active = false;
718cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
719cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
7205f2add9cSMatthias Ringwald a2dp_emit_streaming_connection_failed_for_role(role, connection,
721cb9e52f6SMatthias Ringwald ERROR_CODE_CONNECTION_REJECTED_DUE_TO_NO_SUITABLE_CHANNEL_FOUND);
722cb9e52f6SMatthias Ringwald }
723c3df44f0SMatthias Ringwald config_process->state = A2DP_CONNECTED;
724a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_cid = 0;
7250b2fa017SMatthias Ringwald a2dp_config_process_discover_seps_with_next_waiting_connection();
726cb9e52f6SMatthias Ringwald }
727cb9e52f6SMatthias Ringwald break;
728cb9e52f6SMatthias Ringwald
729cb9e52f6SMatthias Ringwald // forward codec configuration
730cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION:
731cb9e52f6SMatthias Ringwald local_seid = avdtp_subevent_signaling_media_codec_sbc_configuration_get_local_seid(packet);
7320b2fa017SMatthias Ringwald a2dp_config_process_handle_media_configuration(role, packet, local_seid);
7336e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size,
734cb9e52f6SMatthias Ringwald A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION);
735cb9e52f6SMatthias Ringwald break;
736cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_MPEG_AUDIO_CONFIGURATION:
737cb9e52f6SMatthias Ringwald local_seid = avdtp_subevent_signaling_media_codec_mpeg_audio_configuration_get_local_seid(packet);
7380b2fa017SMatthias Ringwald a2dp_config_process_handle_media_configuration(role, packet, local_seid);
7396e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size,
740cb9e52f6SMatthias Ringwald A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_MPEG_AUDIO_CONFIGURATION);
741cb9e52f6SMatthias Ringwald break;
742cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_MPEG_AAC_CONFIGURATION:
743cb9e52f6SMatthias Ringwald local_seid = avdtp_subevent_signaling_media_codec_mpeg_aac_configuration_get_local_seid(packet);
7440b2fa017SMatthias Ringwald a2dp_config_process_handle_media_configuration(role, packet, local_seid);
7456e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size,
746cb9e52f6SMatthias Ringwald A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_MPEG_AAC_CONFIGURATION);
747cb9e52f6SMatthias Ringwald break;
748cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_ATRAC_CONFIGURATION:
749cb9e52f6SMatthias Ringwald local_seid = avdtp_subevent_signaling_media_codec_atrac_configuration_get_local_seid(packet);
7500b2fa017SMatthias Ringwald a2dp_config_process_handle_media_configuration(role, packet, local_seid);
7516e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size,
752cb9e52f6SMatthias Ringwald A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_ATRAC_CONFIGURATION);
753cb9e52f6SMatthias Ringwald break;
754cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION:
755cb9e52f6SMatthias Ringwald local_seid = avdtp_subevent_signaling_media_codec_sbc_configuration_get_local_seid(packet);
7560b2fa017SMatthias Ringwald a2dp_config_process_handle_media_configuration(role, packet, local_seid);
7576e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size,
758cb9e52f6SMatthias Ringwald A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION);
759cb9e52f6SMatthias Ringwald break;
760cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED:
761cb9e52f6SMatthias Ringwald cid = avdtp_subevent_streaming_connection_established_get_avdtp_cid(packet);
762cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
763cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
7646d3263ecSMatthias Ringwald
765aa3e6424SMatthias Ringwald config_process = a2dp_config_process_for_role(role, connection);
7666d3263ecSMatthias Ringwald outgoing_active = config_process->outgoing_active;
767aa3e6424SMatthias Ringwald config_process->outgoing_active = false;
768cb9e52f6SMatthias Ringwald status = avdtp_subevent_streaming_connection_established_get_status(packet);
769cb9e52f6SMatthias Ringwald if (status != ERROR_CODE_SUCCESS){
770cb9e52f6SMatthias Ringwald log_info("A2DP source streaming connection could not be established, avdtp_cid 0x%02x, status 0x%02x ---", cid, status);
7716d3263ecSMatthias Ringwald config_process->state = A2DP_CONNECTED;
7726d3263ecSMatthias Ringwald // suppress event if streaming wasn't requested by us
7736d3263ecSMatthias Ringwald if (outgoing_active == false){
7746d3263ecSMatthias Ringwald break;
7756d3263ecSMatthias Ringwald }
776edc66e93SMatthias Ringwald } else {
777cb9e52f6SMatthias Ringwald log_info("A2DP source streaming connection established --- avdtp_cid 0x%02x, local seid 0x%02x, remote seid 0x%02x", cid,
778cb9e52f6SMatthias Ringwald avdtp_subevent_streaming_connection_established_get_local_seid(packet),
779cb9e52f6SMatthias Ringwald avdtp_subevent_streaming_connection_established_get_remote_seid(packet));
780aa3e6424SMatthias Ringwald config_process->state = A2DP_STREAMING_OPENED;
781edc66e93SMatthias Ringwald }
7826e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size, A2DP_SUBEVENT_STREAM_ESTABLISHED);
783cb9e52f6SMatthias Ringwald break;
784cb9e52f6SMatthias Ringwald
785cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_ACCEPT:
786cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_accept_get_avdtp_cid(packet);
787cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
788cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
789aa3e6424SMatthias Ringwald config_process = a2dp_config_process_for_role(role, connection);
790cb9e52f6SMatthias Ringwald
791cb9e52f6SMatthias Ringwald // restart set config timer while remote is active for current cid
792a2f9d3cbSMatthias Ringwald if (a2dp_config_process_set_config_timer_active &&
793cb9e52f6SMatthias Ringwald (avdtp_subevent_signaling_accept_get_is_initiator(packet) == 0) &&
794a2f9d3cbSMatthias Ringwald (cid == a2dp_config_process_sep_discovery_cid)){
795cb9e52f6SMatthias Ringwald
796b62e4190SMatthias Ringwald a2dp_config_process_timer_restart();
797cb9e52f6SMatthias Ringwald break;
798cb9e52f6SMatthias Ringwald }
799cb9e52f6SMatthias Ringwald
800cb9e52f6SMatthias Ringwald signal_identifier = avdtp_subevent_signaling_accept_get_signal_identifier(packet);
801cb9e52f6SMatthias Ringwald
802aa3e6424SMatthias Ringwald log_info("A2DP cmd %s accepted, global state %d, cid 0x%02x", avdtp_si2str(signal_identifier), config_process->state, cid);
803cb9e52f6SMatthias Ringwald
804aa3e6424SMatthias Ringwald switch (config_process->state){
805cb9e52f6SMatthias Ringwald case A2DP_GET_CAPABILITIES:
806a2f9d3cbSMatthias Ringwald remote_seid = a2dp_config_process_sep_discovery_seps[a2dp_config_process_sep_discovery_index].seid;
807cb9e52f6SMatthias Ringwald log_info("A2DP get capabilities for remote seid 0x%02x", remote_seid);
808aa3e6424SMatthias Ringwald avdtp_get_all_capabilities(cid, remote_seid, role);
809cb9e52f6SMatthias Ringwald return;
810cb9e52f6SMatthias Ringwald
811cb9e52f6SMatthias Ringwald case A2DP_SET_CONFIGURATION:
812aa3e6424SMatthias Ringwald a2dp_config_process_set_config(role, connection);
813cb9e52f6SMatthias Ringwald return;
814cb9e52f6SMatthias Ringwald
815cb9e52f6SMatthias Ringwald case A2DP_W2_OPEN_STREAM_WITH_SEID:
816cb9e52f6SMatthias Ringwald log_info("A2DP open stream ... local seid 0x%02x, active remote seid 0x%02x",
817cb9e52f6SMatthias Ringwald avdtp_stream_endpoint_seid(connection->a2dp_source_config_process.local_stream_endpoint),
818c3df44f0SMatthias Ringwald config_process->local_stream_endpoint->remote_sep.seid);
819aa3e6424SMatthias Ringwald config_process->state = A2DP_W4_OPEN_STREAM_WITH_SEID;
820ff9f995dSMatthias Ringwald avdtp_open_stream(cid,
821aa3e6424SMatthias Ringwald avdtp_stream_endpoint_seid(config_process->local_stream_endpoint),
822aa3e6424SMatthias Ringwald config_process->local_stream_endpoint->remote_sep.seid);
823cb9e52f6SMatthias Ringwald break;
824cb9e52f6SMatthias Ringwald
825cb9e52f6SMatthias Ringwald case A2DP_W2_RECONFIGURE_WITH_SEID:
826cb9e52f6SMatthias Ringwald log_info("A2DP reconfigured ... local seid 0x%02x, active remote seid 0x%02x",
827aa3e6424SMatthias Ringwald avdtp_stream_endpoint_seid(config_process->local_stream_endpoint),
828aa3e6424SMatthias Ringwald config_process->local_stream_endpoint->remote_sep.seid);
8295f2add9cSMatthias Ringwald a2dp_emit_stream_reconfigured_role(role, cid, avdtp_stream_endpoint_seid(
830aa3e6424SMatthias Ringwald config_process->local_stream_endpoint), ERROR_CODE_SUCCESS);
831aa3e6424SMatthias Ringwald config_process->state = A2DP_STREAMING_OPENED;
832cb9e52f6SMatthias Ringwald break;
833cb9e52f6SMatthias Ringwald
834cb9e52f6SMatthias Ringwald case A2DP_STREAMING_OPENED:
835cb9e52f6SMatthias Ringwald switch (signal_identifier){
836cb9e52f6SMatthias Ringwald case AVDTP_SI_START:
837b776322eSMatthias Ringwald a2dp_emit_stream_event_for_role(role, cid, avdtp_stream_endpoint_seid(config_process->local_stream_endpoint),
838cb9e52f6SMatthias Ringwald A2DP_SUBEVENT_STREAM_STARTED);
839cb9e52f6SMatthias Ringwald break;
840cb9e52f6SMatthias Ringwald case AVDTP_SI_SUSPEND:
841b776322eSMatthias Ringwald a2dp_emit_stream_event_for_role(role, cid, avdtp_stream_endpoint_seid(config_process->local_stream_endpoint),
842cb9e52f6SMatthias Ringwald A2DP_SUBEVENT_STREAM_SUSPENDED);
843cb9e52f6SMatthias Ringwald break;
844cb9e52f6SMatthias Ringwald case AVDTP_SI_ABORT:
845cb9e52f6SMatthias Ringwald case AVDTP_SI_CLOSE:
846b776322eSMatthias Ringwald a2dp_emit_stream_event_for_role(role, cid, avdtp_stream_endpoint_seid(config_process->local_stream_endpoint),
847cb9e52f6SMatthias Ringwald A2DP_SUBEVENT_STREAM_STOPPED);
848cb9e52f6SMatthias Ringwald break;
849f2098d22SMatthias Ringwald #ifdef ENABLE_AVDTP_ACCEPTOR_EXPLICIT_START_STREAM_CONFIRMATION
850f2098d22SMatthias Ringwald case AVDTP_SI_ACCEPT_START:
851f2098d22SMatthias Ringwald a2dp_emit_stream_event_for_role(role, cid, avdtp_stream_endpoint_seid(config_process->local_stream_endpoint),
852f2098d22SMatthias Ringwald A2DP_SUBEVENT_START_STREAM_REQUESTED);
853f2098d22SMatthias Ringwald break;
854f2098d22SMatthias Ringwald #endif
855cb9e52f6SMatthias Ringwald default:
856cb9e52f6SMatthias Ringwald break;
857cb9e52f6SMatthias Ringwald }
858cb9e52f6SMatthias Ringwald break;
859cb9e52f6SMatthias Ringwald
860cb9e52f6SMatthias Ringwald default:
861cb9e52f6SMatthias Ringwald break;
862cb9e52f6SMatthias Ringwald }
863cb9e52f6SMatthias Ringwald break;
864cb9e52f6SMatthias Ringwald
865cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_REJECT:
866cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_reject_get_avdtp_cid(packet);
867cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
868cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
869aa3e6424SMatthias Ringwald config_process = a2dp_config_process_for_role(role, connection);
870cb9e52f6SMatthias Ringwald
871cb9e52f6SMatthias Ringwald if (avdtp_subevent_signaling_reject_get_is_initiator(packet) == 0) break;
872cb9e52f6SMatthias Ringwald
873aa3e6424SMatthias Ringwald switch (config_process->state) {
874cb9e52f6SMatthias Ringwald case A2DP_W2_RECONFIGURE_WITH_SEID:
875cb9e52f6SMatthias Ringwald log_info("A2DP reconfigure failed ... local seid 0x%02x, active remote seid 0x%02x",
876aa3e6424SMatthias Ringwald avdtp_stream_endpoint_seid(config_process->local_stream_endpoint),
877aa3e6424SMatthias Ringwald config_process->local_stream_endpoint->remote_sep.seid);
8785f2add9cSMatthias Ringwald a2dp_emit_stream_reconfigured_role(role, cid, avdtp_stream_endpoint_seid(
879aa3e6424SMatthias Ringwald config_process->local_stream_endpoint), ERROR_CODE_UNSPECIFIED_ERROR);
880aa3e6424SMatthias Ringwald config_process->state = A2DP_STREAMING_OPENED;
881cb9e52f6SMatthias Ringwald break;
882cb9e52f6SMatthias Ringwald default:
883aa3e6424SMatthias Ringwald config_process->state = A2DP_CONNECTED;
884cb9e52f6SMatthias Ringwald break;
885cb9e52f6SMatthias Ringwald }
886cb9e52f6SMatthias Ringwald
8876e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size, A2DP_SUBEVENT_COMMAND_REJECTED);
888cb9e52f6SMatthias Ringwald break;
889cb9e52f6SMatthias Ringwald
890cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_GENERAL_REJECT:
891cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_general_reject_get_avdtp_cid(packet);
892cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
893cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
894aa3e6424SMatthias Ringwald config_process = a2dp_config_process_for_role(role, connection);
895cb9e52f6SMatthias Ringwald
896cb9e52f6SMatthias Ringwald if (avdtp_subevent_signaling_general_reject_get_is_initiator(packet) == 0) break;
897cb9e52f6SMatthias Ringwald
898aa3e6424SMatthias Ringwald config_process->state = A2DP_CONNECTED;
8996e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size, A2DP_SUBEVENT_COMMAND_REJECTED);
900cb9e52f6SMatthias Ringwald break;
901cb9e52f6SMatthias Ringwald
902cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_STREAMING_CONNECTION_RELEASED:
903cb9e52f6SMatthias Ringwald cid = avdtp_subevent_streaming_connection_released_get_avdtp_cid(packet);
904cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
905cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
906aa3e6424SMatthias Ringwald config_process = a2dp_config_process_for_role(role, connection);
907cb9e52f6SMatthias Ringwald
908aa3e6424SMatthias Ringwald config_process->state = A2DP_CONFIGURED;
9096e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size, A2DP_SUBEVENT_STREAM_RELEASED);
910cb9e52f6SMatthias Ringwald break;
911cb9e52f6SMatthias Ringwald
912cb9e52f6SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_CONNECTION_RELEASED:
913cb9e52f6SMatthias Ringwald cid = avdtp_subevent_signaling_connection_released_get_avdtp_cid(packet);
914cb9e52f6SMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(cid);
915cb9e52f6SMatthias Ringwald btstack_assert(connection != NULL);
916aa3e6424SMatthias Ringwald config_process = a2dp_config_process_for_role(role, connection);
917cb9e52f6SMatthias Ringwald
918cb9e52f6SMatthias Ringwald // connect/release are passed on to app
919a2f9d3cbSMatthias Ringwald if (a2dp_config_process_sep_discovery_cid == cid){
920b62e4190SMatthias Ringwald a2dp_config_process_timer_stop();
921aa3e6424SMatthias Ringwald config_process->stream_endpoint_configured = false;
922aa3e6424SMatthias Ringwald config_process->local_stream_endpoint = NULL;
923cb9e52f6SMatthias Ringwald
924aa3e6424SMatthias Ringwald config_process->state = A2DP_IDLE;
925a2f9d3cbSMatthias Ringwald a2dp_config_process_sep_discovery_cid = 0;
926cb9e52f6SMatthias Ringwald }
9276e55450eSMatthias Ringwald a2dp_replace_subevent_id_and_emit_for_role(role, packet, size, A2DP_SUBEVENT_SIGNALING_CONNECTION_RELEASED);
928cb9e52f6SMatthias Ringwald break;
929cb9e52f6SMatthias Ringwald
930cb9e52f6SMatthias Ringwald default:
931cb9e52f6SMatthias Ringwald break;
932cb9e52f6SMatthias Ringwald }
933cb9e52f6SMatthias Ringwald }
934a6adb322SMatthias Ringwald
a2dp_config_process_set_sbc(avdtp_role_t role,uint16_t a2dp_cid,uint8_t local_seid,uint8_t remote_seid,const avdtp_configuration_sbc_t * configuration)935a6adb322SMatthias Ringwald uint8_t a2dp_config_process_set_sbc(avdtp_role_t role, uint16_t a2dp_cid, uint8_t local_seid, uint8_t remote_seid, const avdtp_configuration_sbc_t * configuration){
936a6adb322SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(a2dp_cid);
937a6adb322SMatthias Ringwald if (connection == NULL){
938a6adb322SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
939a6adb322SMatthias Ringwald }
940a6adb322SMatthias Ringwald a2dp_config_process_t * config_process = a2dp_config_process_for_role(role, connection);
941a6adb322SMatthias Ringwald
942a6adb322SMatthias Ringwald uint8_t status = a2dp_config_process_config_init(role, connection, local_seid, remote_seid, AVDTP_CODEC_SBC);
943*ea8aa208SMilanka Ringwald if (status != ERROR_CODE_SUCCESS) {
944a6adb322SMatthias Ringwald return status;
945a6adb322SMatthias Ringwald }
946*ea8aa208SMilanka Ringwald
947a6adb322SMatthias Ringwald // set config in reserved buffer
948c3df44f0SMatthias Ringwald config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information = (uint8_t *) config_process->local_stream_endpoint->media_codec_info;
949a6adb322SMatthias Ringwald config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = 4;
950*ea8aa208SMilanka Ringwald status = avdtp_config_sbc_store(config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information, configuration);
951*ea8aa208SMilanka Ringwald if (status != ERROR_CODE_SUCCESS) {
952*ea8aa208SMilanka Ringwald return status;
953*ea8aa208SMilanka Ringwald }
95472237cc2SMatthias Ringwald #ifdef ENABLE_A2DP_EXPLICIT_CONFIG
955a6adb322SMatthias Ringwald a2dp_config_process_set_config(role, connection);
9565d2ab254SMatthias Ringwald #endif
957a6adb322SMatthias Ringwald
958a6adb322SMatthias Ringwald return ERROR_CODE_SUCCESS;
959a6adb322SMatthias Ringwald }
960a6adb322SMatthias Ringwald
a2dp_config_process_set_mpeg_audio(avdtp_role_t role,uint16_t a2dp_cid,uint8_t local_seid,uint8_t remote_seid,const avdtp_configuration_mpeg_audio_t * configuration)961a6adb322SMatthias Ringwald uint8_t a2dp_config_process_set_mpeg_audio(avdtp_role_t role, uint16_t a2dp_cid, uint8_t local_seid, uint8_t remote_seid, const avdtp_configuration_mpeg_audio_t * configuration){
962a6adb322SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(a2dp_cid);
963a6adb322SMatthias Ringwald if (connection == NULL){
964a6adb322SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
965a6adb322SMatthias Ringwald }
966a6adb322SMatthias Ringwald a2dp_config_process_t * config_process = a2dp_config_process_for_role(role, connection);
967a6adb322SMatthias Ringwald
968a6adb322SMatthias Ringwald uint8_t status = a2dp_config_process_config_init(role, connection, local_seid, remote_seid, AVDTP_CODEC_MPEG_1_2_AUDIO);
969*ea8aa208SMilanka Ringwald if (status != ERROR_CODE_SUCCESS) {
970a6adb322SMatthias Ringwald return status;
971a6adb322SMatthias Ringwald }
972a6adb322SMatthias Ringwald
973a6adb322SMatthias Ringwald // set config in reserved buffer
974c3df44f0SMatthias Ringwald config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information = (uint8_t *)config_process->local_stream_endpoint->media_codec_info;
975a6adb322SMatthias Ringwald config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = 4;
976*ea8aa208SMilanka Ringwald status = avdtp_config_mpeg_audio_store(config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information, configuration);
977*ea8aa208SMilanka Ringwald if (status != ERROR_CODE_SUCCESS) {
978*ea8aa208SMilanka Ringwald return status;
979*ea8aa208SMilanka Ringwald }
980a6adb322SMatthias Ringwald
98172237cc2SMatthias Ringwald #ifdef ENABLE_A2DP_EXPLICIT_CONFIG
982a6adb322SMatthias Ringwald a2dp_config_process_set_config(role, connection);
9835d2ab254SMatthias Ringwald #endif
984a6adb322SMatthias Ringwald
985a6adb322SMatthias Ringwald return ERROR_CODE_SUCCESS;
986a6adb322SMatthias Ringwald }
987a6adb322SMatthias Ringwald
a2dp_config_process_set_mpeg_aac(avdtp_role_t role,uint16_t a2dp_cid,uint8_t local_seid,uint8_t remote_seid,const avdtp_configuration_mpeg_aac_t * configuration)988a6adb322SMatthias Ringwald uint8_t a2dp_config_process_set_mpeg_aac(avdtp_role_t role, uint16_t a2dp_cid, uint8_t local_seid, uint8_t remote_seid, const avdtp_configuration_mpeg_aac_t * configuration){
989a6adb322SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(a2dp_cid);
990a6adb322SMatthias Ringwald if (connection == NULL){
991a6adb322SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
992a6adb322SMatthias Ringwald }
993a6adb322SMatthias Ringwald a2dp_config_process_t * config_process = a2dp_config_process_for_role(role, connection);
994a6adb322SMatthias Ringwald
99500ed8f02SMatthias Ringwald uint8_t status = a2dp_config_process_config_init(role, connection, local_seid, remote_seid, AVDTP_CODEC_MPEG_2_4_AAC);
996c83b8b89SMilanka Ringwald if (status != ERROR_CODE_SUCCESS) {
997a6adb322SMatthias Ringwald return status;
998a6adb322SMatthias Ringwald }
999c3df44f0SMatthias Ringwald config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information = (uint8_t *) config_process->local_stream_endpoint->media_codec_info;
1000a6adb322SMatthias Ringwald config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = 6;
1001c83b8b89SMilanka Ringwald status = avdtp_config_mpeg_aac_store(config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information, configuration);
1002c83b8b89SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){
1003c83b8b89SMilanka Ringwald return status;
1004c83b8b89SMilanka Ringwald }
1005*ea8aa208SMilanka Ringwald
100672237cc2SMatthias Ringwald #ifdef ENABLE_A2DP_EXPLICIT_CONFIG
1007a6adb322SMatthias Ringwald a2dp_config_process_set_config(role, connection);
10085d2ab254SMatthias Ringwald #endif
1009a6adb322SMatthias Ringwald
1010a6adb322SMatthias Ringwald return ERROR_CODE_SUCCESS;
1011a6adb322SMatthias Ringwald }
1012a6adb322SMatthias Ringwald
a2dp_config_process_set_atrac(avdtp_role_t role,uint16_t a2dp_cid,uint8_t local_seid,uint8_t remote_seid,const avdtp_configuration_atrac_t * configuration)1013a6adb322SMatthias Ringwald uint8_t a2dp_config_process_set_atrac(avdtp_role_t role, uint16_t a2dp_cid, uint8_t local_seid, uint8_t remote_seid, const avdtp_configuration_atrac_t * configuration){
1014a6adb322SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(a2dp_cid);
1015a6adb322SMatthias Ringwald if (connection == NULL){
1016a6adb322SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1017a6adb322SMatthias Ringwald }
1018a6adb322SMatthias Ringwald a2dp_config_process_t * config_process = a2dp_config_process_for_role(role, connection);
1019a6adb322SMatthias Ringwald
102000ed8f02SMatthias Ringwald uint8_t status = a2dp_config_process_config_init(role, connection, local_seid, remote_seid, AVDTP_CODEC_ATRAC_FAMILY);
1021*ea8aa208SMilanka Ringwald if (status != ERROR_CODE_SUCCESS) {
1022a6adb322SMatthias Ringwald return status;
1023a6adb322SMatthias Ringwald }
1024a6adb322SMatthias Ringwald
1025c3df44f0SMatthias Ringwald config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information = (uint8_t *) config_process->local_stream_endpoint->media_codec_info;
1026a6adb322SMatthias Ringwald config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = 7;
1027*ea8aa208SMilanka Ringwald status = avdtp_config_atrac_store(config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information, configuration);
1028*ea8aa208SMilanka Ringwald if (status != ERROR_CODE_SUCCESS) {
1029*ea8aa208SMilanka Ringwald return status;
1030*ea8aa208SMilanka Ringwald }
103172237cc2SMatthias Ringwald #ifdef ENABLE_A2DP_EXPLICIT_CONFIG
1032a6adb322SMatthias Ringwald a2dp_config_process_set_config(role, connection);
10335d2ab254SMatthias Ringwald #endif
1034a6adb322SMatthias Ringwald
1035a6adb322SMatthias Ringwald return ERROR_CODE_SUCCESS;
1036a6adb322SMatthias Ringwald }
1037a6adb322SMatthias Ringwald
a2dp_config_process_set_other(avdtp_role_t role,uint16_t a2dp_cid,uint8_t local_seid,uint8_t remote_seid,const uint8_t * media_codec_information,uint8_t media_codec_information_len)1038a6adb322SMatthias Ringwald uint8_t a2dp_config_process_set_other(avdtp_role_t role, uint16_t a2dp_cid, uint8_t local_seid, uint8_t remote_seid,
1039a6adb322SMatthias Ringwald const uint8_t * media_codec_information, uint8_t media_codec_information_len){
1040a6adb322SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(a2dp_cid);
1041a6adb322SMatthias Ringwald if (connection == NULL){
1042a6adb322SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1043a6adb322SMatthias Ringwald }
1044a6adb322SMatthias Ringwald a2dp_config_process_t * config_process = a2dp_config_process_for_role(role, connection);
1045a6adb322SMatthias Ringwald
104600ed8f02SMatthias Ringwald uint8_t status = a2dp_config_process_config_init(role, connection, local_seid, remote_seid, AVDTP_CODEC_NON_A2DP);
1047a6adb322SMatthias Ringwald if (status != 0) {
1048a6adb322SMatthias Ringwald return status;
1049a6adb322SMatthias Ringwald }
1050a6adb322SMatthias Ringwald
1051a6adb322SMatthias Ringwald config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information = (uint8_t *) media_codec_information;
1052a6adb322SMatthias Ringwald config_process->local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = media_codec_information_len;
1053a6adb322SMatthias Ringwald
105472237cc2SMatthias Ringwald #ifdef ENABLE_A2DP_EXPLICIT_CONFIG
1055a6adb322SMatthias Ringwald a2dp_config_process_set_config(role, connection);
10565d2ab254SMatthias Ringwald #endif
1057a6adb322SMatthias Ringwald
1058a6adb322SMatthias Ringwald return status;
1059a6adb322SMatthias Ringwald }
1060