1b442c9e6SMilanka Ringwald 2b442c9e6SMilanka Ringwald /* 3b442c9e6SMilanka Ringwald * Copyright (C) 2016 BlueKitchen GmbH 4b442c9e6SMilanka Ringwald * 5b442c9e6SMilanka Ringwald * Redistribution and use in source and binary forms, with or without 6b442c9e6SMilanka Ringwald * modification, are permitted provided that the following conditions 7b442c9e6SMilanka Ringwald * are met: 8b442c9e6SMilanka Ringwald * 9b442c9e6SMilanka Ringwald * 1. Redistributions of source code must retain the above copyright 10b442c9e6SMilanka Ringwald * notice, this list of conditions and the following disclaimer. 11b442c9e6SMilanka Ringwald * 2. Redistributions in binary form must reproduce the above copyright 12b442c9e6SMilanka Ringwald * notice, this list of conditions and the following disclaimer in the 13b442c9e6SMilanka Ringwald * documentation and/or other materials provided with the distribution. 14b442c9e6SMilanka Ringwald * 3. Neither the name of the copyright holders nor the names of 15b442c9e6SMilanka Ringwald * contributors may be used to endorse or promote products derived 16b442c9e6SMilanka Ringwald * from this software without specific prior written permission. 17b442c9e6SMilanka Ringwald * 4. Any redistribution, use, or modification is done solely for 18b442c9e6SMilanka Ringwald * personal benefit and not for any commercial purpose or for 19b442c9e6SMilanka Ringwald * monetary gain. 20b442c9e6SMilanka Ringwald * 21b442c9e6SMilanka Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 22b442c9e6SMilanka Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23b442c9e6SMilanka Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 242fca4dadSMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 252fca4dadSMilanka Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26b442c9e6SMilanka Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27b442c9e6SMilanka Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 28b442c9e6SMilanka Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29b442c9e6SMilanka Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30b442c9e6SMilanka Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 31b442c9e6SMilanka Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32b442c9e6SMilanka Ringwald * SUCH DAMAGE. 33b442c9e6SMilanka Ringwald * 34b442c9e6SMilanka Ringwald * Please inquire about commercial licensing options at 35b442c9e6SMilanka Ringwald * [email protected] 36b442c9e6SMilanka Ringwald * 37b442c9e6SMilanka Ringwald */ 38b442c9e6SMilanka Ringwald 39696f1abaSMatthias Ringwald /** 40696f1abaSMatthias Ringwald * Supported use cases: 41696f1abaSMatthias Ringwald * - single incoming connection: sep discovery starts and stream will get setup if remote sink sep with SBC is found 42696f1abaSMatthias Ringwald * - single outgoing connection: see above 43696f1abaSMatthias Ringwald * - outgoing and incoming connection to same device: 44696f1abaSMatthias Ringwald * - if outgoing is triggered first, incoming will get ignored. 45696f1abaSMatthias Ringwald * - if incoming starts first, start ougoing will fail, but incoming will succeed. 46696f1abaSMatthias Ringwald * - outgoing and incoming connections to different devices: 47696f1abaSMatthias Ringwald * - if outgoing is first, incoming gets ignored. 48696f1abaSMatthias Ringwald * - if incoming starts first SEP discovery will get stopped and outgoing will succeed. 49696f1abaSMatthias Ringwald */ 50696f1abaSMatthias Ringwald 51e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "a2dp_source.c" 52b442c9e6SMilanka Ringwald 53b442c9e6SMilanka Ringwald #include <stdint.h> 54b442c9e6SMilanka Ringwald #include <string.h> 55b442c9e6SMilanka Ringwald 5684e3541eSMilanka Ringwald #include "bluetooth_psm.h" 5784e3541eSMilanka Ringwald #include "bluetooth_sdp.h" 5884e3541eSMilanka Ringwald #include "btstack_debug.h" 5984e3541eSMilanka Ringwald #include "btstack_event.h" 6015ff8d31SMatthias Ringwald #include "classic/a2dp.h" 614cb889a5SMilanka Ringwald #include "classic/a2dp_source.h" 6284e3541eSMilanka Ringwald #include "classic/avdtp_source.h" 6384e3541eSMilanka Ringwald #include "classic/avdtp_util.h" 6484e3541eSMilanka Ringwald #include "classic/sdp_util.h" 6584e3541eSMilanka Ringwald #include "l2cap.h" 666ce6ec61SMatthias Ringwald #include "a2dp.h" 67b442c9e6SMilanka Ringwald 689f84611fSMatthias Ringwald static const char * a2dp_source_default_service_name = "BTstack A2DP Source Service"; 69137e2954SMatthias Ringwald static const char * a2dp_default_source_service_provider_name = "BTstack A2DP Source Service Provider"; 70cc85b8eaSMatthias Ringwald 71a95794ceSMatthias Ringwald static uint8_t (*a2dp_source_media_config_validator)(const avdtp_stream_endpoint_t * stream_endpoint, const uint8_t * event, uint16_t size); 720d176b65SMilanka Ringwald 73b442c9e6SMilanka Ringwald void a2dp_source_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name){ 749f84611fSMatthias Ringwald if (service_provider_name == NULL){ 759f84611fSMatthias Ringwald service_provider_name = a2dp_default_source_service_provider_name; 76b442c9e6SMilanka Ringwald } 779f84611fSMatthias Ringwald if (service_name == NULL){ 789f84611fSMatthias Ringwald service_name = a2dp_source_default_service_name; 79b442c9e6SMilanka Ringwald } 809f84611fSMatthias Ringwald a2dp_create_sdp_record(service, service_record_handle, BLUETOOTH_SERVICE_CLASS_AUDIO_SOURCE, 819f84611fSMatthias Ringwald supported_features, service_name, service_provider_name); 82b442c9e6SMilanka Ringwald } 83b442c9e6SMilanka Ringwald 842f9a3826SMatthias Ringwald static void a2dp_source_packet_handler_internal(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 852f9a3826SMatthias Ringwald UNUSED(channel); 862f9a3826SMatthias Ringwald UNUSED(size); 872f9a3826SMatthias Ringwald 882f9a3826SMatthias Ringwald if (packet_type != HCI_EVENT_PACKET) return; 892f9a3826SMatthias Ringwald if (hci_event_packet_get_type(packet) != HCI_EVENT_AVDTP_META) return; 902f9a3826SMatthias Ringwald 912f9a3826SMatthias Ringwald switch (hci_event_avdtp_meta_get_subevent_code(packet)){ 922f9a3826SMatthias Ringwald 932f9a3826SMatthias Ringwald case AVDTP_SUBEVENT_SIGNALING_DELAY_REPORT: 942f9a3826SMatthias Ringwald a2dp_replace_subevent_id_and_emit_source(packet, size, A2DP_SUBEVENT_SIGNALING_DELAY_REPORT); 952f9a3826SMatthias Ringwald break; 962f9a3826SMatthias Ringwald 972f9a3826SMatthias Ringwald case AVDTP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW: 982f9a3826SMatthias Ringwald a2dp_replace_subevent_id_and_emit_source(packet, size, A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW); 992f9a3826SMatthias Ringwald break; 1002f9a3826SMatthias Ringwald 1012f9a3826SMatthias Ringwald default: 1022e01864aSMatthias Ringwald // forward events to config process 103*302e9e52SMatthias Ringwald a2dp_config_process_avdtp_event_handler(AVDTP_ROLE_SOURCE, packet, size); 1042f9a3826SMatthias Ringwald break; 1052f9a3826SMatthias Ringwald } 106ba155c22SMilanka Ringwald } 107274391e8SMilanka Ringwald void a2dp_source_register_packet_handler(btstack_packet_handler_t callback){ 108cf95ebe8SMatthias Ringwald btstack_assert(callback != NULL); 109cf95ebe8SMatthias Ringwald 11038106e95SMatthias Ringwald avdtp_source_register_packet_handler(&a2dp_source_packet_handler_internal); 1116ed41ce8SMatthias Ringwald a2dp_register_source_packet_handler(callback); 112274391e8SMilanka Ringwald } 113274391e8SMilanka Ringwald 114274391e8SMilanka Ringwald void a2dp_source_init(void){ 11515ff8d31SMatthias Ringwald a2dp_init(); 11677092f3eSMatthias Ringwald avdtp_source_init(); 117274391e8SMilanka Ringwald } 118274391e8SMilanka Ringwald 1197569dc61SMatthias Ringwald void a2dp_source_deinit(void){ 12015ff8d31SMatthias Ringwald a2dp_deinit(); 1217569dc61SMatthias Ringwald avdtp_source_deinit(); 122137e2954SMatthias Ringwald a2dp_source_media_config_validator = NULL; 1237569dc61SMatthias Ringwald } 1247569dc61SMatthias Ringwald 1257078e434SMilanka Ringwald avdtp_stream_endpoint_t * a2dp_source_create_stream_endpoint(avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, 1263e6cf581SMatthias Ringwald const uint8_t *codec_capabilities, uint16_t codec_capabilities_len, 12782767773SMatthias Ringwald uint8_t * codec_configuration, uint16_t codec_configuration_len){ 128cc85b8eaSMatthias Ringwald avdtp_stream_endpoint_t * stream_endpoint = avdtp_source_create_stream_endpoint(AVDTP_SOURCE, media_type); 129cc85b8eaSMatthias Ringwald if (!stream_endpoint){ 1307078e434SMilanka Ringwald return NULL; 1314567cc17SMilanka Ringwald } 132cc85b8eaSMatthias Ringwald avdtp_source_register_media_transport_category(avdtp_stream_endpoint_seid(stream_endpoint)); 133cc85b8eaSMatthias Ringwald avdtp_source_register_media_codec_category(avdtp_stream_endpoint_seid(stream_endpoint), media_type, media_codec_type, 134ba155c22SMilanka Ringwald codec_capabilities, codec_capabilities_len); 135cc85b8eaSMatthias Ringwald avdtp_source_register_delay_reporting_category(avdtp_stream_endpoint_seid(stream_endpoint)); 13682767773SMatthias Ringwald 13782767773SMatthias Ringwald // store user codec configuration buffer 138636a58caSBjoern Hartmann stream_endpoint->media_codec_type = media_codec_type; 139cc85b8eaSMatthias Ringwald stream_endpoint->media_codec_configuration_info = codec_configuration; 140cc85b8eaSMatthias Ringwald stream_endpoint->media_codec_configuration_len = codec_configuration_len; 14182767773SMatthias Ringwald 142cc85b8eaSMatthias Ringwald return stream_endpoint; 143274391e8SMilanka Ringwald } 144ba155c22SMilanka Ringwald 14517ddf501SMatthias Ringwald void a2dp_source_finalize_stream_endpoint(avdtp_stream_endpoint_t * stream_endpoint){ 14617ddf501SMatthias Ringwald avdtp_source_finalize_stream_endpoint(stream_endpoint); 14717ddf501SMatthias Ringwald } 14817ddf501SMatthias Ringwald 1494c5b56ffSMatthias Ringwald uint8_t a2dp_source_establish_stream(bd_addr_t remote_addr, uint16_t *avdtp_cid) { 150cd94cb8eSMatthias Ringwald 151696f1abaSMatthias Ringwald uint16_t outgoing_cid; 152696f1abaSMatthias Ringwald 153cd0bbc03SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_bd_addr(remote_addr); 154cd0bbc03SMatthias Ringwald if (connection == NULL){ 155cd94cb8eSMatthias Ringwald uint8_t status = avdtp_source_connect(remote_addr, &outgoing_cid); 156cd94cb8eSMatthias Ringwald if (status != ERROR_CODE_SUCCESS) { 157696f1abaSMatthias Ringwald // if there's already a connection for for remote addr, avdtp_source_connect fails, 158696f1abaSMatthias Ringwald // but the stream will get set-up nevertheless 159cd94cb8eSMatthias Ringwald return status; 160cd94cb8eSMatthias Ringwald } 161ff43929dSMatthias Ringwald connection = avdtp_get_connection_for_avdtp_cid(outgoing_cid); 162ff43929dSMatthias Ringwald btstack_assert(connection != NULL); 16387d9d508SMilanka Ringwald 164696f1abaSMatthias Ringwald // setup state 165ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.outgoing_active = true; 166ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.state = A2DP_W4_CONNECTED; 167a4ec7672SMilanka Ringwald *avdtp_cid = outgoing_cid; 168696f1abaSMatthias Ringwald 169cd0bbc03SMatthias Ringwald } else { 170ffc20ab4SMatthias Ringwald if (connection->a2dp_source_config_process.outgoing_active || connection->a2dp_source_config_process.stream_endpoint_configured) { 171b7eb14d4SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 172b7eb14d4SMatthias Ringwald } 173b7eb14d4SMatthias Ringwald 174cd0bbc03SMatthias Ringwald // check state 175ffc20ab4SMatthias Ringwald switch (connection->a2dp_source_config_process.state){ 176cd0bbc03SMatthias Ringwald case A2DP_IDLE: 177cd0bbc03SMatthias Ringwald case A2DP_CONNECTED: 178cd0bbc03SMatthias Ringwald // restart process e.g. if there no suitable stream endpoints or they had been in use 179ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.outgoing_active = true; 180cd0bbc03SMatthias Ringwald *avdtp_cid = connection->avdtp_cid; 181*302e9e52SMatthias Ringwald a2dp_config_process_ready_for_sep_discovery(AVDTP_ROLE_SOURCE, connection); 182cd0bbc03SMatthias Ringwald break; 183cd0bbc03SMatthias Ringwald default: 184cd0bbc03SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 185cd0bbc03SMatthias Ringwald } 186cd0bbc03SMatthias Ringwald } 18787d9d508SMilanka Ringwald return ERROR_CODE_SUCCESS; 188ba155c22SMilanka Ringwald } 189ba155c22SMilanka Ringwald 19087d9d508SMilanka Ringwald uint8_t a2dp_source_disconnect(uint16_t avdtp_cid){ 19187d9d508SMilanka Ringwald return avdtp_disconnect(avdtp_cid); 192ba155c22SMilanka Ringwald } 193ba155c22SMilanka Ringwald 19487d9d508SMilanka Ringwald uint8_t a2dp_source_start_stream(uint16_t avdtp_cid, uint8_t local_seid){ 19587d9d508SMilanka Ringwald return avdtp_start_stream(avdtp_cid, local_seid); 196b548dda6SMilanka Ringwald } 197b548dda6SMilanka Ringwald 19887d9d508SMilanka Ringwald uint8_t a2dp_source_pause_stream(uint16_t avdtp_cid, uint8_t local_seid){ 19987d9d508SMilanka Ringwald return avdtp_suspend_stream(avdtp_cid, local_seid); 20046e6b063SMilanka Ringwald } 20146e6b063SMilanka Ringwald 20287d9d508SMilanka Ringwald void a2dp_source_stream_endpoint_request_can_send_now(uint16_t avdtp_cid, uint8_t local_seid){ 20387d9d508SMilanka Ringwald avdtp_source_stream_endpoint_request_can_send_now(avdtp_cid, local_seid); 20446e6b063SMilanka Ringwald } 20546e6b063SMilanka Ringwald 20687d9d508SMilanka Ringwald int a2dp_max_media_payload_size(uint16_t avdtp_cid, uint8_t local_seid){ 20787d9d508SMilanka Ringwald return avdtp_max_media_payload_size(avdtp_cid, local_seid); 2081f6397ceSMilanka Ringwald } 2091f6397ceSMilanka Ringwald 21087d9d508SMilanka Ringwald int a2dp_source_stream_send_media_payload(uint16_t avdtp_cid, uint8_t local_seid, uint8_t * storage, int num_bytes_to_copy, uint8_t num_frames, uint8_t marker){ 21187d9d508SMilanka Ringwald return avdtp_source_stream_send_media_payload(avdtp_cid, local_seid, storage, num_bytes_to_copy, num_frames, marker); 21246e6b063SMilanka Ringwald } 2130fa4346cSMatthias Ringwald 2148869c787SBjoern Hartmann uint8_t a2dp_source_stream_send_media_payload_rtp(uint16_t a2dp_cid, uint8_t local_seid, uint8_t marker, uint8_t * payload, uint16_t payload_size){ 2158869c787SBjoern Hartmann return avdtp_source_stream_send_media_payload_rtp(a2dp_cid, local_seid, marker, payload, payload_size); 2168869c787SBjoern Hartmann } 2178869c787SBjoern Hartmann 2185695c5ccSMatthias Ringwald uint8_t a2dp_source_stream_send_media_packet(uint16_t a2dp_cid, uint8_t local_seid, const uint8_t * packet, uint16_t size){ 2195695c5ccSMatthias Ringwald return avdtp_source_stream_send_media_packet(a2dp_cid, local_seid, packet, size); 2205695c5ccSMatthias Ringwald } 2215695c5ccSMatthias Ringwald 222cbfa0fc6SMatthias Ringwald 2238691a66cSMatthias 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){ 2240fa4346cSMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(a2dp_cid); 2250fa4346cSMatthias Ringwald if (connection == NULL){ 2260fa4346cSMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 2270fa4346cSMatthias Ringwald } 2280fa4346cSMatthias Ringwald 229*302e9e52SMatthias Ringwald uint8_t status = a2dp_config_process_config_init(AVDTP_ROLE_SOURCE, connection, local_seid, remote_seid, 230*302e9e52SMatthias Ringwald AVDTP_CODEC_SBC); 231cc85b8eaSMatthias Ringwald if (status != 0) { 232cc85b8eaSMatthias Ringwald return status; 233cc85b8eaSMatthias Ringwald } 234cc85b8eaSMatthias Ringwald // set config in reserved buffer 235ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information = (uint8_t *) connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info; 236ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = 4; 237ffc20ab4SMatthias Ringwald avdtp_config_sbc_store(connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information, configuration); 2380fa4346cSMatthias Ringwald 2390d4e1353SMatthias Ringwald #ifdef ENABLE_A2DP_SOURCE_EXPLICIT_CONFIG 240*302e9e52SMatthias Ringwald a2dp_config_process_set_config(AVDTP_ROLE_SOURCE, connection); 2410d4e1353SMatthias Ringwald #endif 2420d4e1353SMatthias Ringwald 243cbfa0fc6SMatthias Ringwald return ERROR_CODE_SUCCESS; 244cbfa0fc6SMatthias Ringwald } 2450fa4346cSMatthias Ringwald 2468691a66cSMatthias 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){ 247cbfa0fc6SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(a2dp_cid); 248cbfa0fc6SMatthias Ringwald if (connection == NULL){ 249cbfa0fc6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 250cbfa0fc6SMatthias Ringwald } 251cbfa0fc6SMatthias Ringwald 252*302e9e52SMatthias Ringwald uint8_t status = a2dp_config_process_config_init(AVDTP_ROLE_SOURCE, connection, local_seid, remote_seid, 253*302e9e52SMatthias Ringwald AVDTP_CODEC_MPEG_1_2_AUDIO); 254cc85b8eaSMatthias Ringwald if (status != 0) { 255cc85b8eaSMatthias Ringwald return status; 256cc85b8eaSMatthias Ringwald } 257cc85b8eaSMatthias Ringwald 258cc85b8eaSMatthias Ringwald // set config in reserved buffer 259ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information = (uint8_t *)connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info; 260ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = 4; 261ffc20ab4SMatthias Ringwald avdtp_config_mpeg_audio_store(connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information, configuration); 262cbfa0fc6SMatthias Ringwald 2630d4e1353SMatthias Ringwald #ifdef ENABLE_A2DP_SOURCE_EXPLICIT_CONFIG 264*302e9e52SMatthias Ringwald a2dp_config_process_set_config(AVDTP_ROLE_SOURCE, connection); 2650d4e1353SMatthias Ringwald #endif 2660d4e1353SMatthias Ringwald 267cbfa0fc6SMatthias Ringwald return ERROR_CODE_SUCCESS; 268cbfa0fc6SMatthias Ringwald } 269cbfa0fc6SMatthias Ringwald 2708691a66cSMatthias 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){ 271cbfa0fc6SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(a2dp_cid); 272cbfa0fc6SMatthias Ringwald if (connection == NULL){ 273cbfa0fc6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 274cbfa0fc6SMatthias Ringwald } 275cbfa0fc6SMatthias Ringwald 276*302e9e52SMatthias Ringwald uint8_t status = a2dp_config_process_config_init(AVDTP_ROLE_SOURCE, connection, local_seid, remote_seid, 277*302e9e52SMatthias Ringwald AVDTP_CODEC_MPEG_2_4_AAC); 278cc85b8eaSMatthias Ringwald if (status != 0) { 279cc85b8eaSMatthias Ringwald return status; 280cc85b8eaSMatthias Ringwald } 281ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information = (uint8_t *) connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info; 282ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = 6; 283ffc20ab4SMatthias Ringwald avdtp_config_mpeg_aac_store(connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information, configuration); 284cbfa0fc6SMatthias Ringwald 2850d4e1353SMatthias Ringwald #ifdef ENABLE_A2DP_SOURCE_EXPLICIT_CONFIG 286*302e9e52SMatthias Ringwald a2dp_config_process_set_config(AVDTP_ROLE_SOURCE, connection); 2870d4e1353SMatthias Ringwald #endif 2880d4e1353SMatthias Ringwald 289cbfa0fc6SMatthias Ringwald return ERROR_CODE_SUCCESS; 290cbfa0fc6SMatthias Ringwald } 291cbfa0fc6SMatthias Ringwald 2928691a66cSMatthias 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){ 293cbfa0fc6SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(a2dp_cid); 294cbfa0fc6SMatthias Ringwald if (connection == NULL){ 295cbfa0fc6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 296cbfa0fc6SMatthias Ringwald } 297cc85b8eaSMatthias Ringwald 298*302e9e52SMatthias Ringwald uint8_t status = a2dp_config_process_config_init(AVDTP_ROLE_SOURCE, connection, local_seid, remote_seid, 299*302e9e52SMatthias Ringwald AVDTP_CODEC_ATRAC_FAMILY); 300cc85b8eaSMatthias Ringwald if (status != 0) { 301cc85b8eaSMatthias Ringwald return status; 302cc85b8eaSMatthias Ringwald } 303cc85b8eaSMatthias Ringwald 304ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information = (uint8_t *) connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info; 305ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = 7; 306ffc20ab4SMatthias Ringwald avdtp_config_atrac_store(connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information, configuration); 307cbfa0fc6SMatthias Ringwald 3080d4e1353SMatthias Ringwald #ifdef ENABLE_A2DP_SOURCE_EXPLICIT_CONFIG 309*302e9e52SMatthias Ringwald a2dp_config_process_set_config(AVDTP_ROLE_SOURCE, connection); 3100d4e1353SMatthias Ringwald #endif 3110d4e1353SMatthias Ringwald 312cbfa0fc6SMatthias Ringwald return ERROR_CODE_SUCCESS; 313cbfa0fc6SMatthias Ringwald } 314cbfa0fc6SMatthias Ringwald 315cbfa0fc6SMatthias Ringwald uint8_t a2dp_source_set_config_other(uint16_t a2dp_cid, uint8_t local_seid, uint8_t remote_seid, 316cbfa0fc6SMatthias Ringwald const uint8_t * media_codec_information, uint8_t media_codec_information_len){ 317cbfa0fc6SMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(a2dp_cid); 318cbfa0fc6SMatthias Ringwald if (connection == NULL){ 319cbfa0fc6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 320cbfa0fc6SMatthias Ringwald } 321cbfa0fc6SMatthias Ringwald 322*302e9e52SMatthias Ringwald uint8_t status = a2dp_config_process_config_init(AVDTP_ROLE_SOURCE, connection, local_seid, remote_seid, 323*302e9e52SMatthias Ringwald AVDTP_CODEC_NON_A2DP); 324cc85b8eaSMatthias Ringwald if (status != 0) { 325cc85b8eaSMatthias Ringwald return status; 326cc85b8eaSMatthias Ringwald } 3270fa4346cSMatthias Ringwald 328ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information = (uint8_t *) media_codec_information; 329ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = media_codec_information_len; 330cc85b8eaSMatthias Ringwald 3310d4e1353SMatthias Ringwald #ifdef ENABLE_A2DP_SOURCE_EXPLICIT_CONFIG 332*302e9e52SMatthias Ringwald a2dp_config_process_set_config(AVDTP_ROLE_SOURCE, connection); 3330d4e1353SMatthias Ringwald #endif 3340d4e1353SMatthias Ringwald 335cc85b8eaSMatthias Ringwald return status; 3360fa4346cSMatthias Ringwald } 33733553e7eSMatthias Ringwald 33833553e7eSMatthias Ringwald uint8_t a2dp_source_reconfigure_stream_sampling_frequency(uint16_t avdtp_cid, uint32_t sampling_frequency){ 33933553e7eSMatthias Ringwald avdtp_connection_t * connection = avdtp_get_connection_for_avdtp_cid(avdtp_cid); 34033553e7eSMatthias Ringwald if (connection == NULL){ 34133553e7eSMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 34233553e7eSMatthias Ringwald } 34333553e7eSMatthias Ringwald 344ffc20ab4SMatthias Ringwald if (connection->a2dp_source_config_process.state != A2DP_STREAMING_OPENED) { 34533553e7eSMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 34633553e7eSMatthias Ringwald } 34733553e7eSMatthias Ringwald 348ffc20ab4SMatthias Ringwald btstack_assert(connection->a2dp_source_config_process.local_stream_endpoint != NULL); 349cc85b8eaSMatthias Ringwald 35033553e7eSMatthias Ringwald log_info("Reconfigure avdtp_cid 0x%02x", avdtp_cid); 35133553e7eSMatthias Ringwald 352ffc20ab4SMatthias Ringwald avdtp_media_codec_type_t codec_type = connection->a2dp_source_config_process.local_stream_endpoint->sep.capabilities.media_codec.media_codec_type; 35380555d91SMatthias Ringwald uint8_t codec_info_len; 35480555d91SMatthias Ringwald switch (codec_type){ 35580555d91SMatthias Ringwald case AVDTP_CODEC_SBC: 35680555d91SMatthias Ringwald codec_info_len = 4; 357ffc20ab4SMatthias 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); 358ffc20ab4SMatthias Ringwald avdtp_config_sbc_set_sampling_frequency(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, sampling_frequency); 35933553e7eSMatthias Ringwald break; 36080555d91SMatthias Ringwald case AVDTP_CODEC_MPEG_1_2_AUDIO: 36180555d91SMatthias Ringwald codec_info_len = 4; 362ffc20ab4SMatthias 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); 363ffc20ab4SMatthias Ringwald avdtp_config_mpeg_audio_set_sampling_frequency(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, sampling_frequency); 36433553e7eSMatthias Ringwald break; 36580555d91SMatthias Ringwald case AVDTP_CODEC_MPEG_2_4_AAC: 36680555d91SMatthias Ringwald codec_info_len = 6; 367ffc20ab4SMatthias 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); 368ffc20ab4SMatthias Ringwald avdtp_config_mpeg_aac_set_sampling_frequency(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, sampling_frequency); 36933553e7eSMatthias Ringwald break; 37080555d91SMatthias Ringwald case AVDTP_CODEC_ATRAC_FAMILY: 37180555d91SMatthias Ringwald codec_info_len = 7; 372ffc20ab4SMatthias 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); 373ffc20ab4SMatthias Ringwald avdtp_config_atrac_set_sampling_frequency(connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info, sampling_frequency); 37433553e7eSMatthias Ringwald break; 3755bfc74a2SMatthias Ringwald default: 37633553e7eSMatthias Ringwald return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 37733553e7eSMatthias Ringwald } 37833553e7eSMatthias Ringwald 37933553e7eSMatthias Ringwald avdtp_capabilities_t new_configuration; 38033553e7eSMatthias Ringwald new_configuration.media_codec.media_type = AVDTP_AUDIO; 38180555d91SMatthias Ringwald new_configuration.media_codec.media_codec_type = codec_type; 38280555d91SMatthias Ringwald new_configuration.media_codec.media_codec_information_len = codec_info_len; 383ffc20ab4SMatthias Ringwald new_configuration.media_codec.media_codec_information = connection->a2dp_source_config_process.local_stream_endpoint->media_codec_info; 38433553e7eSMatthias Ringwald 38533553e7eSMatthias Ringwald // start reconfigure 386ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.state = A2DP_W2_RECONFIGURE_WITH_SEID; 38733553e7eSMatthias Ringwald 38833553e7eSMatthias Ringwald return avdtp_source_reconfigure( 38933553e7eSMatthias Ringwald avdtp_cid, 390ffc20ab4SMatthias Ringwald avdtp_stream_endpoint_seid(connection->a2dp_source_config_process.local_stream_endpoint), 391ffc20ab4SMatthias Ringwald connection->a2dp_source_config_process.local_stream_endpoint->remote_sep.seid, 39233553e7eSMatthias Ringwald 1 << AVDTP_MEDIA_CODEC, 39333553e7eSMatthias Ringwald new_configuration 39433553e7eSMatthias Ringwald ); 39533553e7eSMatthias Ringwald } 396a95794ceSMatthias Ringwald 397a95794ceSMatthias Ringwald static uint8_t a2dp_source_media_config_validator_callback(const avdtp_stream_endpoint_t * stream_endpoint, const uint8_t * event, uint16_t size){ 398a95794ceSMatthias Ringwald uint8_t error = 0; 39959dedcb9SMatthias Ringwald if (a2dp_source_media_config_validator != NULL) { 400a95794ceSMatthias Ringwald // update subevent id and call validator 401a95794ceSMatthias Ringwald uint8_t avdtp_subevent_id = event[2]; 402a95794ceSMatthias Ringwald uint8_t a2dp_subevent_id = a2dp_subevent_id_for_avdtp_subevent_id(avdtp_subevent_id); 403a95794ceSMatthias Ringwald uint8_t * subevent_field = (uint8_t *) &event[2]; 404a95794ceSMatthias Ringwald *subevent_field = a2dp_subevent_id; 405a95794ceSMatthias Ringwald error = (*a2dp_source_media_config_validator)(stream_endpoint, event, size); 406a95794ceSMatthias Ringwald *subevent_field = avdtp_subevent_id; 407a95794ceSMatthias Ringwald } 408a95794ceSMatthias Ringwald return error; 409a95794ceSMatthias Ringwald } 410a95794ceSMatthias Ringwald 4113421998fSMatthias 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)){ 412a95794ceSMatthias Ringwald a2dp_source_media_config_validator = callback; 413a95794ceSMatthias Ringwald avdtp_source_register_media_config_validator(&a2dp_source_media_config_validator_callback); 414a95794ceSMatthias Ringwald } 415a95794ceSMatthias Ringwald 416