diff --git a/src/btstack_memory.h b/src/btstack_memory.h index be1203da8..f3d865f91 100644 --- a/src/btstack_memory.h +++ b/src/btstack_memory.h @@ -64,8 +64,8 @@ extern "C" { #include "classic/btstack_link_key_db_memory.h" #include "classic/rfcomm.h" #include "classic/sdp_server.h" -#include "../test/avdtp/avdtp_sink.h" -#include "../test/avdtp/avdtp_source.h" +#include "classic/avdtp_sink.h" +#include "classic/avdtp_source.h" // BLE #ifdef ENABLE_BLE diff --git a/src/classic/avdtp.h b/src/classic/avdtp.h new file mode 100644 index 000000000..341d26a33 --- /dev/null +++ b/src/classic/avdtp.h @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * avdtp.h + * + * Audio/Video Distribution Transport Protocol + * + * This protocol defines A/V stream negotiation, establishment, and transmission + * procedures. Also specified are the message formats that are exchanged between + * such devices to transport their A/V streams in A/V distribution applications. + * + * Media packets are unidirectional, they travel downstream from AVDTP Source to AVDTP Sink. + */ + +#ifndef __AVDTP_H +#define __AVDTP_H + +#include +#include "hci.h" + +#if defined __cplusplus +extern "C" { +#endif + +#define MAX_NUM_SEPS 10 + +// protocols +#define PSM_AVCTP 0x0017 +#define PSM_AVDTP 0x0019 + +// service classes +#define AUDIO_SOURCE_GROUP 0x110A +#define AUDIO_SINK_GROUP 0x110B +#define AV_REMOTE_CONTROL_TARGET 0X110C +#define ADVANCED_AUDIO_DISTRIBUTION 0X110D +#define AV_REMOTE_CONTROL 0X110E +#define AV_REMOTE_CONTROL_CONTROLER 0X110F + +#define MAX_CSRC_NUM 15 + +// Supported Features +#define AVDTP_SOURCE_SF_Player 0x0001 +#define AVDTP_SOURCE_SF_Microphone 0x0002 +#define AVDTP_SOURCE_SF_Tuner 0x0004 +#define AVDTP_SOURCE_SF_Mixer 0x0008 + +#define AVDTP_SINK_SF_Headphone 0x0001 +#define AVDTP_SINK_SF_Speaker 0x0002 +#define AVDTP_SINK_SF_Recorder 0x0004 +#define AVDTP_SINK_SF_Amplifier 0x0008 + +// ACP to INT, Signal Response Header Error Codes +#define BAD_HEADER_FORMAT 0x01 + +// ACP to INT, Signal Response Payload Format Error Codes +#define BAD_LENGTH 0x11 +#define BAD_ACP_SEID 0x12 +#define SEP_IN_USE 0x13 +#define SEP_NOT_IN_USE 0x14 +#define BAD_SERV_CATEGORY 0x17 +#define BAD_PAYLOAD_FORMAT 0x18 +#define NOT_SUPPORTED_COMMAND 0x19 +#define INVALID_CAPABILITIES 0x1A + +// ACP to INT, Signal Response Transport Service Capabilities Error Codes +#define BAD_RECOVERY_TYPE 0x22 +#define BAD_MEDIA_TRANSPORT_FORMAT 0x23 +#define BAD_RECOVERY_FORMAT 0x25 +#define BAD_ROHC_FORMAT 0x26 +#define BAD_CP_FORMAT 0x27 +#define BAD_MULTIPLEXING_FORMAT 0x28 +#define UNSUPPORTED_CONFIGURATION 0x29 + +// ACP to INT, Procedure Error Codes +#define BAD_STATE 0x31 +// Signal Identifier fields +typedef enum { + AVDTP_SI_DISCOVER = 0x01, + AVDTP_SI_GET_CAPABILITIES, + AVDTP_SI_SET_CONFIGURATION, + AVDTP_SI_GET_CONFIGURATION, + AVDTP_SI_RECONFIGURE, //5 + AVDTP_SI_OPEN, //6 + AVDTP_SI_START, //7 + AVDTP_SI_CLOSE, + AVDTP_SI_SUSPEND, + AVDTP_SI_ABORT, //10 + AVDTP_SI_SECURITY_CONTROL, + AVDTP_SI_GET_ALL_CAPABILITIES, //12 + AVDTP_SI_DELAYREPORT +} avdtp_signal_identifier_t; + +typedef enum { + AVDTP_SINGLE_PACKET= 0, + AVDTP_START_PACKET , + AVDTP_CONTINUE_PACKET , + AVDTP_END_PACKET +} avdtp_packet_type_t; + +typedef enum { + AVDTP_CMD_MSG = 0, + AVDTP_GENERAL_REJECT_MSG , + AVDTP_RESPONSE_ACCEPT_MSG , + AVDTP_RESPONSE_REJECT_MSG +} avdtp_message_type_t; + +typedef enum{ + AVDTP_AUDIO = 0, + AVDTP_VIDEO, + AVDTP_MULTIMEDIA +} avdtp_media_type_t; + +typedef enum{ + AVDTP_CODEC_SBC = 0x00, + AVDTP_CODEC_MPEG_1_2_AUDIO = 0x01, + AVDTP_CODEC_MPEG_2_4_AAC = 0x02, + AVDTP_CODEC_ATRAC_FAMILY = 0x04, + AVDTP_CODEC_NON_A2DP = 0xFF +} avdtp_media_codec_type_t; + +typedef enum{ + AVDTP_CONTENT_PROTECTION_DTCP = 0x0001, + AVDTP_CONTENT_PROTECTION_SCMS_T = 0x0002 +} avdtp_content_protection_type_t; + +typedef enum{ + AVDTP_SOURCE = 0, + AVDTP_SINK +} avdtp_sep_type_t; + +typedef enum { + AVDTP_SERVICE_CATEGORY_INVALID_0 = 0x00, + AVDTP_MEDIA_TRANSPORT = 0X01, + AVDTP_REPORTING, + AVDTP_RECOVERY, + AVDTP_CONTENT_PROTECTION, //4 + AVDTP_HEADER_COMPRESSION, //5 + AVDTP_MULTIPLEXING, //6 + AVDTP_MEDIA_CODEC, //7 + AVDTP_DELAY_REPORTING, //8 + AVDTP_SERVICE_CATEGORY_INVALID_FF = 0xFF +} avdtp_service_category_t; + +typedef struct { + uint8_t recovery_type; // 0x01 = RFC2733 + uint8_t maximum_recovery_window_size; // 0x01 to 0x18, for a Transport Packet + uint8_t maximum_number_media_packets; // 0x01 to 0x18, The maximum number of media packets a specific parity code covers +} avdtp_recovery_capabilities_t; + +typedef struct { + avdtp_media_type_t media_type; + avdtp_media_codec_type_t media_codec_type; + uint16_t media_codec_information_len; + const uint8_t * media_codec_information; +} adtvp_media_codec_capabilities_t; + +typedef struct { + uint16_t cp_type; + uint16_t cp_type_value_len; + const uint8_t * cp_type_value; +} adtvp_content_protection_t; + +typedef struct{ + uint8_t back_ch; // byte0 - bit 8; 0=Not Available/Not Used; 1=Available/In Use + uint8_t media; // byte0 - bit 7 + uint8_t recovery; // byte0 - bit 6 +} avdtp_header_compression_capabilities_t; + +typedef struct{ + uint8_t fragmentation; // byte0 - bit 8, Allow Adaptation Layer Fragmentation, 0 no, 1 yes + // Request/indicate value of the Transport Session Identifier for a media, reporting, or recovery transport sessions, respectively + uint8_t transport_identifiers_num; + uint8_t transport_session_identifiers[3]; // byte1, upper 5bits, 0x01 to 0x1E + // Request/indicate value for TCID for a media, reporting, or transport session + uint8_t tcid[3]; // byte2 0x01 to 0x1E +} avdtp_multiplexing_mode_capabilities_t; + +typedef struct{ + avdtp_recovery_capabilities_t recovery; + adtvp_media_codec_capabilities_t media_codec; + adtvp_content_protection_t content_protection; + avdtp_header_compression_capabilities_t header_compression; + avdtp_multiplexing_mode_capabilities_t multiplexing_mode; +} avdtp_capabilities_t; + +typedef enum{ + AVDTP_SBC_48000 = 1, + AVDTP_SBC_44100 = 2, + AVDTP_SBC_32000 = 4, + AVDTP_SBC_16000 = 8 +} avdtp_sbc_sampling_frequency_t; + +typedef enum{ + AVDTP_SBC_JOINT_STEREO = 1, + AVDTP_SBC_STEREO = 2, + AVDTP_SBC_DUAL_CHANNEL = 4, + AVDTP_SBC_MONO = 8 +} avdtp_sbc_channel_mode_t; + +typedef enum{ + AVDTP_SBC_BLOCK_LENGTH_16 = 1, + AVDTP_SBC_BLOCK_LENGTH_12 = 2, + AVDTP_SBC_BLOCK_LENGTH_8 = 4, + AVDTP_SBC_BLOCK_LENGTH_4 = 8 +} avdtp_sbc_block_length_t; + +typedef enum{ + AVDTP_SBC_SUBBANDS_8 = 1, + AVDTP_SBC_SUBBANDS_4 = 2 +} avdtp_sbc_subbands_t; + +typedef enum{ + AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS = 1, + AVDTP_SBC_ALLOCATION_METHOD_SNR = 2 +} avdtp_sbc_allocation_method_t; + +typedef struct { + uint8_t fragmentation; + uint8_t starting_packet; // of fragmented SBC frame + uint8_t last_packet; // of fragmented SBC frame + uint8_t num_frames; +} avdtp_sbc_codec_header_t; + +// typedef struct { +// uint8_t transaction_label; +// avdtp_packet_type_t packet_type; +// avdtp_message_type_t message_type; +// uint8_t signal_identifier; +// } avdtp_signaling_packet_header_t; + +typedef struct { + uint8_t version; + uint8_t padding; + uint8_t extension; + uint8_t csrc_count; + uint8_t marker; + uint8_t payload_type; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t synchronization_source; + + uint32_t csrc_list[MAX_CSRC_NUM]; +} avdtp_media_packet_header_t; + +typedef enum { + AVDTP_BASIC_SERVICE_MODE, + AVDTP_MULTIPLEXING_SERVICE_MODE +} avdtp_service_mode_t; + +typedef enum { + AVDTP_STREAM_ENDPOINT_IDLE, + AVDTP_STREAM_ENDPOINT_CONFIGURATION_SUBSTATEMACHINE, + AVDTP_STREAM_ENDPOINT_CONFIGURED, + AVDTP_STREAM_ENDPOINT_W4_L2CAP_FOR_MEDIA_CONNECTED, + AVDTP_STREAM_ENDPOINT_OPENED, + AVDTP_STREAM_ENDPOINT_STREAMING, + AVDTP_STREAM_ENDPOINT_CLOSING, + AVDTP_STREAM_ENDPOINT_ABORTING, + AVDTP_STREAM_ENDPOINT_W4_L2CAP_FOR_MEDIA_DISCONNECTED +} avdtp_stream_endpoint_state_t; + +typedef enum { + AVDTP_INITIATOR_STREAM_CONFIG_IDLE, + AVDTP_INITIATOR_W2_SET_CONFIGURATION, + AVDTP_INITIATOR_W2_SUSPEND_STREAM_WITH_SEID, + AVDTP_INITIATOR_W2_RECONFIGURE_STREAM_WITH_SEID, + AVDTP_INITIATOR_W2_MEDIA_CONNECT, + AVDTP_INITIATOR_W2_STREAMING_START, + AVDTP_INITIATOR_W2_STREAMING_STOP, + AVDTP_INITIATOR_W2_STREAMING_ABORT, + AVDTP_INITIATOR_FRAGMENTATED_COMMAND, + AVDTP_INITIATOR_W4_ANSWER +} avdtp_initiator_stream_endpoint_state_t; + +typedef enum { + AVDTP_ACCEPTOR_STREAM_CONFIG_IDLE, + AVDTP_ACCEPTOR_W2_ANSWER_GET_CAPABILITIES, + AVDTP_ACCEPTOR_W2_ANSWER_GET_ALL_CAPABILITIES, + AVDTP_ACCEPTOR_W2_ANSWER_SET_CONFIGURATION, + AVDTP_ACCEPTOR_W2_ANSWER_RECONFIGURE, + AVDTP_ACCEPTOR_W2_ANSWER_GET_CONFIGURATION, + AVDTP_ACCEPTOR_W2_ANSWER_OPEN_STREAM, + + AVDTP_ACCEPTOR_W4_L2CAP_FOR_MEDIA_CONNECTED, + + AVDTP_ACCEPTOR_W2_ANSWER_START_STREAM, + AVDTP_ACCEPTOR_W2_ANSWER_CLOSE_STREAM, + AVDTP_ACCEPTOR_W2_ANSWER_ABORT_STREAM, + AVDTP_ACCEPTOR_W2_SUSPEND_STREAM_WITH_SEID, + AVDTP_ACCEPTOR_W2_ANSWER_SUSPEND_STREAM, + AVDTP_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE, + AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE, + AVDTP_ACCEPTOR_W2_REJECT_UNKNOWN_CMD, + AVDTP_ACCEPTOR_STREAMING +} avdtp_acceptor_stream_endpoint_state_t; + +typedef struct { + uint8_t seid; // 0x01 – 0x3E, 6bit + uint8_t in_use; // 1 bit, 0 - not in use, 1 - in use + avdtp_media_type_t media_type; // 4 bit + avdtp_sep_type_t type; // 1 bit, 0 - SRC, 1 - SNK + + uint16_t registered_service_categories; + avdtp_capabilities_t capabilities; + + uint16_t configured_service_categories; + avdtp_capabilities_t configuration; +} avdtp_sep_t; + + +typedef enum { + AVDTP_SIGNALING_CONNECTION_IDLE, + AVDTP_SIGNALING_CONNECTION_W4_L2CAP_CONNECTED, + AVDTP_SIGNALING_CONNECTION_OPENED, + AVDTP_SIGNALING_CONNECTION_W4_L2CAP_DISCONNECTED +} avdtp_connection_state_t; + +typedef enum { + AVDTP_SIGNALING_CONNECTION_ACCEPTOR_IDLE, + AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_ANSWER_DISCOVER_SEPS, + AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE, + AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE, + AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_GENERAL_REJECT_WITH_ERROR_CODE +} avdtp_acceptor_connection_state_t; + +typedef enum { + AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE, + AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS, + AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CAPABILITIES, + AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_ALL_CAPABILITIES, + AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CONFIGURATION, + AVDTP_SIGNALING_CONNECTION_INITIATOR_W4_ANSWER +} avdtp_initiator_connection_state_t; + +typedef struct { + uint8_t command[200]; + uint16_t size; + uint16_t offset; + avdtp_signal_identifier_t signal_identifier; + avdtp_message_type_t message_type; + avdtp_packet_type_t packet_type; + uint8_t acp_seid; + uint8_t int_seid; + uint16_t transaction_label; + uint16_t num_packets; +} avdtp_signaling_packet_t; + +typedef struct { + btstack_linked_item_t item; + + bd_addr_t remote_addr; + hci_con_handle_t con_handle; + uint16_t l2cap_signaling_cid; + avdtp_service_mode_t service_mode; + + avdtp_connection_state_t state; + avdtp_acceptor_connection_state_t acceptor_connection_state; + avdtp_initiator_connection_state_t initiator_connection_state; + + // used for fragmentation + // avdtp_signaling_packet_header_t signaling_header; + avdtp_signaling_packet_t signaling_packet; + + uint8_t disconnect; + uint8_t initiator_transaction_label; + uint8_t acceptor_transaction_label; + uint8_t query_seid; + uint8_t int_seid; + uint8_t acp_seid; + + avdtp_capabilities_t remote_capabilities; + uint16_t remote_capabilities_bitmap; + + uint8_t wait_to_send_acceptor; + uint8_t wait_to_send_initiator; + uint8_t wait_to_send_self; + + uint8_t suspended_seids[MAX_NUM_SEPS]; + uint8_t num_suspended_seids; + + uint8_t reject_service_category; + avdtp_signal_identifier_t reject_signal_identifier; + uint8_t error_code; +} avdtp_connection_t; + + +typedef struct avdtp_stream_endpoint { + btstack_linked_item_t item; + + // original capabilities + avdtp_sep_t sep; + uint16_t l2cap_media_cid; + uint16_t l2cap_reporting_cid; + uint16_t l2cap_recovery_cid; + + avdtp_stream_endpoint_state_t state; + avdtp_acceptor_stream_endpoint_state_t acceptor_config_state; + avdtp_initiator_stream_endpoint_state_t initiator_config_state; + + // active connection + avdtp_connection_t * connection; + // store configurations with remote seps + avdtp_sep_t remote_seps[MAX_NUM_SEPS]; + uint8_t remote_seps_num; + + // currently active remote seid + uint8_t remote_sep_index; + // register request for media L2cap connection release + uint8_t media_disconnect; + uint8_t media_connect; +} avdtp_stream_endpoint_t; + +#if defined __cplusplus +} +#endif + +#endif // __AVDTP_H \ No newline at end of file diff --git a/src/classic/avdtp_acceptor.c b/src/classic/avdtp_acceptor.c new file mode 100644 index 000000000..a840565c2 --- /dev/null +++ b/src/classic/avdtp_acceptor.c @@ -0,0 +1,611 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + + +#include +#include +#include +#include +#include + +#include "btstack.h" +#include "avdtp.h" +#include "avdtp_util.h" +#include "avdtp_acceptor.h" + + +static int avdtp_acceptor_send_accept_response(uint16_t cid, uint8_t transaction_label, avdtp_signal_identifier_t identifier){ + uint8_t command[2]; + command[0] = avdtp_header(transaction_label, AVDTP_SINGLE_PACKET, AVDTP_RESPONSE_ACCEPT_MSG); + command[1] = (uint8_t)identifier; + return l2cap_send(cid, command, sizeof(command)); +} + +static int avdtp_acceptor_process_chunk(avdtp_signaling_packet_t * signaling_packet, uint8_t * packet, uint16_t size){ + memcpy(signaling_packet->command + signaling_packet->size, packet, size); + signaling_packet->size += size; + return signaling_packet->packet_type == AVDTP_SINGLE_PACKET || signaling_packet->packet_type == AVDTP_END_PACKET; +} + +static int avdtp_acceptor_validate_msg_length(avdtp_signal_identifier_t signal_identifier, uint16_t msg_size){ + int minimal_msg_lenght = 2; + switch (signal_identifier){ + case AVDTP_SI_GET_CAPABILITIES: + case AVDTP_SI_GET_ALL_CAPABILITIES: + case AVDTP_SI_SET_CONFIGURATION: + case AVDTP_SI_GET_CONFIGURATION: + case AVDTP_SI_START: + case AVDTP_SI_CLOSE: + case AVDTP_SI_ABORT: + case AVDTP_SI_RECONFIGURE: + case AVDTP_SI_OPEN: + minimal_msg_lenght = 3; + break; + default: + break; + } + return msg_size >= minimal_msg_lenght; +} + +void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t * packet, uint16_t size, int offset){ + avdtp_stream_endpoint_t * stream_endpoint; + connection->acceptor_transaction_label = connection->signaling_packet.transaction_label; + + if (!avdtp_acceptor_validate_msg_length(connection->signaling_packet.signal_identifier, size)) { + connection->error_code = BAD_LENGTH; + connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE; + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + avdtp_sink_request_can_send_now_acceptor(connection, connection->l2cap_signaling_cid); + return; + } + + switch (connection->signaling_packet.signal_identifier){ + case AVDTP_SI_DISCOVER: + if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; + printf(" ACP: AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_ANSWER_DISCOVER_SEPS\n"); + connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_ANSWER_DISCOVER_SEPS; + avdtp_sink_request_can_send_now_acceptor(connection, connection->l2cap_signaling_cid); + return; + case AVDTP_SI_GET_CAPABILITIES: + case AVDTP_SI_GET_ALL_CAPABILITIES: + case AVDTP_SI_SET_CONFIGURATION: + case AVDTP_SI_GET_CONFIGURATION: + case AVDTP_SI_START: + case AVDTP_SI_CLOSE: + case AVDTP_SI_ABORT: + case AVDTP_SI_OPEN: + case AVDTP_SI_RECONFIGURE: + connection->query_seid = packet[offset++] >> 2; + stream_endpoint = get_avdtp_stream_endpoint_with_seid(connection->query_seid); + if (!stream_endpoint){ + printf(" ACP: cmd %d - RESPONSE REJECT\n", connection->signaling_packet.signal_identifier); + connection->error_code = BAD_ACP_SEID; + if (connection->signaling_packet.signal_identifier == AVDTP_SI_OPEN){ + connection->error_code = BAD_STATE; + } + + connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE; + if (connection->signaling_packet.signal_identifier == AVDTP_SI_RECONFIGURE){ + connection->reject_service_category = connection->query_seid; + connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; + } + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + avdtp_sink_request_can_send_now_acceptor(connection, connection->l2cap_signaling_cid); + return; + } + break; + + case AVDTP_SI_SUSPEND:{ + int i; + printf(" ACP: AVDTP_SI_SUSPEND seids: "); + connection->num_suspended_seids = 0; + + for (i = offset; i < size; i++){ + connection->suspended_seids[connection->num_suspended_seids] = packet[i] >> 2; + offset++; + printf("%d, \n", connection->suspended_seids[connection->num_suspended_seids]); + connection->num_suspended_seids++; + } + + if (connection->num_suspended_seids == 0) { + printf(" ACP: CATEGORY RESPONSE REJECT BAD_ACP_SEID\n"); + connection->error_code = BAD_ACP_SEID; + connection->reject_service_category = connection->query_seid; + connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + avdtp_sink_request_can_send_now_acceptor(connection, connection->l2cap_signaling_cid); + return; + } + // deal with first susspended seid + connection->query_seid = connection->suspended_seids[0]; + stream_endpoint = get_avdtp_stream_endpoint_with_seid(connection->query_seid); + if (!stream_endpoint){ + printf(" ACP: stream_endpoint not found, CATEGORY RESPONSE REJECT BAD_ACP_SEID\n"); + connection->error_code = BAD_ACP_SEID; + connection->reject_service_category = connection->query_seid; + connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + connection->num_suspended_seids = 0; + avdtp_sink_request_can_send_now_acceptor(connection, connection->l2cap_signaling_cid); + return; + } + break; + } + default: + connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_GENERAL_REJECT_WITH_ERROR_CODE; + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + printf("AVDTP_CMD_MSG signal %d not implemented, general reject\n", connection->signaling_packet.signal_identifier); + avdtp_sink_request_can_send_now_acceptor(connection, connection->l2cap_signaling_cid); + return; + } + + if (!stream_endpoint) { + return; + } + + if (!avdtp_acceptor_process_chunk(&connection->signaling_packet, packet, size)) return; + + uint16_t packet_size = connection->signaling_packet.size; + connection->signaling_packet.size = 0; + + int request_to_send = 1; + switch (stream_endpoint->acceptor_config_state){ + case AVDTP_ACCEPTOR_STREAM_CONFIG_IDLE: + switch (connection->signaling_packet.signal_identifier){ + case AVDTP_SI_GET_ALL_CAPABILITIES: + printf(" ACP: AVDTP_SI_GET_ALL_CAPABILITIES\n"); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_GET_ALL_CAPABILITIES; + break; + case AVDTP_SI_GET_CAPABILITIES: + printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_GET_CAPABILITIES\n"); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_GET_CAPABILITIES; + break; + case AVDTP_SI_SET_CONFIGURATION:{ + printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_SET_CONFIGURATION \n"); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_SET_CONFIGURATION; + connection->reject_service_category = 0; + + avdtp_sep_t sep; + sep.seid = connection->signaling_packet.command[offset++] >> 2; + sep.configured_service_categories = avdtp_unpack_service_capabilities(connection, &sep.configuration, connection->signaling_packet.command+offset, packet_size-offset); + sep.in_use = 1; + + if (connection->error_code){ + printf("fire configuration parsing errors \n"); + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; + break; + } + // find or add sep + int i; + stream_endpoint->remote_sep_index = 0xFF; + for (i=0; i < stream_endpoint->remote_seps_num; i++){ + if (stream_endpoint->remote_seps[i].seid == sep.seid){ + stream_endpoint->remote_sep_index = i; + } + } + printf(" ACP .. seid %d, index %d\n", sep.seid, stream_endpoint->remote_sep_index); + + if (stream_endpoint->remote_sep_index != 0xFF){ + if (stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].in_use){ + // reject if already configured + connection->error_code = SEP_IN_USE; + // find first registered category and fire the error + connection->reject_service_category = 0; + for (i = 1; i < 9; i++){ + if (get_bit16(sep.configured_service_categories, i)){ + connection->reject_service_category = i; + break; + } + } + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; + } else { + stream_endpoint->remote_seps[stream_endpoint->remote_sep_index] = sep; + printf(" ACP: update seid %d, to %p\n", stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); + } + } else { + // add new + printf(" ACP: seid %d not found in %p\n", sep.seid, stream_endpoint); + stream_endpoint->remote_sep_index = stream_endpoint->remote_seps_num; + stream_endpoint->remote_seps_num++; + stream_endpoint->remote_seps[stream_endpoint->remote_sep_index] = sep; + printf(" ACP: add seid %d, to %p\n", stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); + } + + if (get_bit16(sep.configured_service_categories, AVDTP_MEDIA_CODEC)){ + switch (sep.configuration.media_codec.media_codec_type){ + case AVDTP_CODEC_SBC: + avdtp_signaling_emit_media_codec_sbc_configuration(avdtp_sink_callback, connection->con_handle, sep.configuration.media_codec); + break; + default: + avdtp_signaling_emit_media_codec_other_configuration(avdtp_sink_callback, connection->con_handle, sep.configuration.media_codec); + break; + } + } + avdtp_signaling_emit_accept(avdtp_sink_callback, connection->con_handle, connection->signaling_packet.signal_identifier, 0); + break; + } + case AVDTP_SI_RECONFIGURE:{ + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_RECONFIGURE; + connection->reject_service_category = 0; + + avdtp_sep_t sep; + sep.seid = connection->query_seid; + printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_RECONFIGURE seid %d\n", sep.seid); + // printf_hexdump(connection->signaling_packet.command, packet_size); + + sep.configured_service_categories = avdtp_unpack_service_capabilities(connection, &sep.configuration, connection->signaling_packet.command+offset, packet_size-offset); + + if (connection->error_code){ + // fire configuration parsing errors + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; + break; + } + + // find sep or raise error + int i; + stream_endpoint->remote_sep_index = 0xFF; + for (i = 0; i < stream_endpoint->remote_seps_num; i++){ + if (stream_endpoint->remote_seps[i].seid == sep.seid){ + stream_endpoint->remote_sep_index = i; + } + } + + if (stream_endpoint->remote_sep_index == 0xFF){ + printf(" ACP: REJECT AVDTP_SI_RECONFIGURE, BAD_ACP_SEID\n"); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; + connection->error_code = BAD_ACP_SEID; + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + break; + } + stream_endpoint->remote_seps[stream_endpoint->remote_sep_index] = sep; + printf(" ACP: update seid %d, to %p\n", stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); + + if (get_bit16(sep.configured_service_categories, AVDTP_MEDIA_CODEC)){ + switch (sep.capabilities.media_codec.media_codec_type){ + case AVDTP_CODEC_SBC: + avdtp_signaling_emit_media_codec_sbc_reconfiguration(avdtp_sink_callback, connection->con_handle, sep.capabilities.media_codec); + break; + default: + avdtp_signaling_emit_media_codec_other_reconfiguration(avdtp_sink_callback, connection->con_handle, sep.capabilities.media_codec); + break; + } + } + avdtp_signaling_emit_accept(avdtp_sink_callback, connection->con_handle, connection->signaling_packet.signal_identifier, 0); + break; + } + + case AVDTP_SI_GET_CONFIGURATION: + printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_GET_CONFIGURATION\n"); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_GET_CONFIGURATION; + break; + case AVDTP_SI_OPEN: + if (stream_endpoint->state != AVDTP_STREAM_ENDPOINT_CONFIGURED){ + printf(" ACP: REJECT AVDTP_SI_OPEN, BAD_STATE\n"); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE; + connection->error_code = BAD_STATE; + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + break; + } + printf(" ACP: AVDTP_STREAM_ENDPOINT_W2_ANSWER_OPEN_STREAM\n"); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_OPEN_STREAM; + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_W4_L2CAP_FOR_MEDIA_CONNECTED; + break; + case AVDTP_SI_START: + if (stream_endpoint->state != AVDTP_STREAM_ENDPOINT_OPENED){ + printf(" ACP: REJECT AVDTP_SI_OPEN, BAD_STATE\n"); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; + connection->error_code = BAD_STATE; + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + break; + } + printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_START_STREAM\n"); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_START_STREAM; + break; + case AVDTP_SI_CLOSE: + switch (stream_endpoint->state){ + case AVDTP_STREAM_ENDPOINT_OPENED: + case AVDTP_STREAM_ENDPOINT_STREAMING: + printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_CLOSE_STREAM\n"); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_CLOSING; + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_CLOSE_STREAM; + break; + default: + printf(" ACP: AVDTP_SI_CLOSE, bad state %d \n", stream_endpoint->state); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE; + connection->error_code = BAD_STATE; + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + break; + } + break; + case AVDTP_SI_ABORT: + switch (stream_endpoint->state){ + case AVDTP_STREAM_ENDPOINT_CONFIGURED: + case AVDTP_STREAM_ENDPOINT_CLOSING: + case AVDTP_STREAM_ENDPOINT_OPENED: + case AVDTP_STREAM_ENDPOINT_STREAMING: + printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_ABORT_STREAM\n"); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_ABORTING; + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_ABORT_STREAM; + break; + default: + printf(" ACP: AVDTP_SI_ABORT, bad state %d \n", stream_endpoint->state); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE; + connection->error_code = BAD_STATE; + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + break; + } + break; + case AVDTP_SI_SUSPEND: + printf(" entering AVDTP_SI_SUSPEND\n"); + switch (stream_endpoint->state){ + case AVDTP_STREAM_ENDPOINT_OPENED: + case AVDTP_STREAM_ENDPOINT_STREAMING: + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_OPENED; + connection->num_suspended_seids--; + if (connection->num_suspended_seids <= 0){ + printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_SUSPEND_STREAM\n"); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_SUSPEND_STREAM; + } + break; + default: + printf(" ACP: AVDTP_SI_SUSPEND, bad state \n"); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; + connection->error_code = BAD_STATE; + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + break; + } + + //stream_endpoint->state = AVDTP_STREAM_ENDPOINT_SUSPENDING; + //stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_SUSPEND_STREAM; + break; + default: + printf(" ACP: NOT IMPLEMENTED, Reject signal_identifier %02x\n", connection->signaling_packet.signal_identifier); + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_UNKNOWN_CMD; + connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; + break; + } + break; + default: + return; + } + + if (!request_to_send){ + printf(" ACP: NOT IMPLEMENTED\n"); + } + avdtp_sink_request_can_send_now_acceptor(connection, connection->l2cap_signaling_cid); +} + +static int avdtp_acceptor_send_seps_response(uint16_t cid, uint8_t transaction_label, avdtp_stream_endpoint_t * endpoints){ + uint8_t command[2+2*MAX_NUM_SEPS]; + int pos = 0; + command[pos++] = avdtp_header(transaction_label, AVDTP_SINGLE_PACKET, AVDTP_RESPONSE_ACCEPT_MSG); + command[pos++] = (uint8_t)AVDTP_SI_DISCOVER; + + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) endpoints); + while (btstack_linked_list_iterator_has_next(&it)){ + avdtp_stream_endpoint_t * stream_endpoint = (avdtp_stream_endpoint_t *)btstack_linked_list_iterator_next(&it); + command[pos++] = (stream_endpoint->sep.seid << 2) | (stream_endpoint->sep.in_use<<1); + command[pos++] = (stream_endpoint->sep.media_type << 4) | (stream_endpoint->sep.type << 3); + } + return l2cap_send(cid, command, pos); +} + +static int avdtp_acceptor_send_response_reject_service_category(uint16_t cid, avdtp_signal_identifier_t identifier, uint8_t category, uint8_t error_code, uint8_t transaction_label){ + uint8_t command[4]; + command[0] = avdtp_header(transaction_label, AVDTP_SINGLE_PACKET, AVDTP_RESPONSE_REJECT_MSG); + command[1] = (uint8_t)identifier; + command[2] = category; + command[3] = error_code; + return l2cap_send(cid, command, sizeof(command)); +} + +static int avdtp_acceptor_send_response_general_reject(uint16_t cid, avdtp_signal_identifier_t identifier, uint8_t transaction_label){ + uint8_t command[2]; + command[0] = avdtp_header(transaction_label, AVDTP_SINGLE_PACKET, AVDTP_GENERAL_REJECT_MSG); + command[1] = (uint8_t)identifier; + return l2cap_send(cid, command, sizeof(command)); +} + +static int avdtp_acceptor_send_response_reject(uint16_t cid, avdtp_signal_identifier_t identifier, uint8_t transaction_label){ + uint8_t command[2]; + command[0] = avdtp_header(transaction_label, AVDTP_SINGLE_PACKET, AVDTP_RESPONSE_REJECT_MSG); + command[1] = (uint8_t)identifier; + return l2cap_send(cid, command, sizeof(command)); +} + +static int avdtp_acceptor_send_response_reject_with_error_code(uint16_t cid, avdtp_signal_identifier_t identifier, uint8_t error_code, uint8_t transaction_label){ + uint8_t command[3]; + command[0] = avdtp_header(transaction_label, AVDTP_SINGLE_PACKET, AVDTP_RESPONSE_REJECT_MSG); + command[1] = (uint8_t)identifier; + command[2] = error_code; + return l2cap_send(cid, command, sizeof(command)); +} + +void avdtp_acceptor_stream_config_subsm_run(avdtp_connection_t * connection){ + int sent = 1; + + switch (connection->acceptor_connection_state){ + case AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_ANSWER_DISCOVER_SEPS: + printf(" -> AVDTP_SIGNALING_CONNECTION_OPENED\n"); + connection->state = AVDTP_SIGNALING_CONNECTION_OPENED; + connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_IDLE; + avdtp_acceptor_send_seps_response(connection->l2cap_signaling_cid, connection->acceptor_transaction_label, (avdtp_stream_endpoint_t *)&stream_endpoints); + break; + case AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE: + connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_IDLE; + avdtp_acceptor_send_response_reject_with_error_code(connection->l2cap_signaling_cid, connection->reject_signal_identifier, connection->error_code, connection->acceptor_transaction_label); + break; + case AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE: + connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_IDLE; + avdtp_acceptor_send_response_reject_service_category(connection->l2cap_signaling_cid, connection->reject_signal_identifier, connection->reject_service_category, connection->error_code, connection->acceptor_transaction_label); + break; + case AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_GENERAL_REJECT_WITH_ERROR_CODE: + connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_IDLE; + avdtp_acceptor_send_response_general_reject(connection->l2cap_signaling_cid, connection->reject_signal_identifier, connection->acceptor_transaction_label); + default: + sent = 0; + break; + } + if (sent) return; + + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_for_seid(connection->query_seid); + if (!stream_endpoint) return; + + uint8_t reject_service_category = connection->reject_service_category; + avdtp_signal_identifier_t reject_signal_identifier = connection->reject_signal_identifier; + uint8_t error_code = connection->error_code; + uint16_t cid = stream_endpoint->connection ? stream_endpoint->connection->l2cap_signaling_cid : connection->l2cap_signaling_cid; + uint8_t trid = stream_endpoint->connection ? stream_endpoint->connection->acceptor_transaction_label : connection->acceptor_transaction_label; + + avdtp_acceptor_stream_endpoint_state_t acceptor_config_state = stream_endpoint->acceptor_config_state; + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_STREAM_CONFIG_IDLE; + uint8_t * out_buffer; + uint16_t pos; + + int status = 0; + switch (acceptor_config_state){ + case AVDTP_ACCEPTOR_STREAM_CONFIG_IDLE: + break; + case AVDTP_ACCEPTOR_W2_ANSWER_GET_CAPABILITIES: + avdtp_prepare_capabilities(&connection->signaling_packet, trid, stream_endpoint->sep.registered_service_categories, stream_endpoint->sep.capabilities, AVDTP_SI_GET_CAPABILITIES); + l2cap_reserve_packet_buffer(); + out_buffer = l2cap_get_outgoing_buffer(); + pos = avdtp_signaling_create_fragment(cid, &connection->signaling_packet, out_buffer); + if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ + stream_endpoint->acceptor_config_state = acceptor_config_state; + printf(" ACP: fragmented\n"); + } + l2cap_send_prepared(cid, pos); + break; + case AVDTP_ACCEPTOR_W2_ANSWER_GET_ALL_CAPABILITIES: + avdtp_prepare_capabilities(&connection->signaling_packet, trid, stream_endpoint->sep.registered_service_categories, stream_endpoint->sep.capabilities, AVDTP_SI_GET_ALL_CAPABILITIES); + l2cap_reserve_packet_buffer(); + out_buffer = l2cap_get_outgoing_buffer(); + pos = avdtp_signaling_create_fragment(cid, &connection->signaling_packet, out_buffer); + if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ + stream_endpoint->acceptor_config_state = acceptor_config_state; + printf(" ACP: fragmented\n"); + } + l2cap_send_prepared(cid, pos); + break; + case AVDTP_ACCEPTOR_W2_ANSWER_SET_CONFIGURATION: + printf(" ACP: DONE\n"); + printf(" -> AVDTP_STREAM_ENDPOINT_CONFIGURED\n"); + stream_endpoint->connection = connection; + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_CONFIGURED; + avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_SET_CONFIGURATION); + break; + case AVDTP_ACCEPTOR_W2_ANSWER_RECONFIGURE: + printf(" ACP: DONE \n"); + avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_RECONFIGURE); + break; + + case AVDTP_ACCEPTOR_W2_ANSWER_GET_CONFIGURATION:{ + avdtp_sep_t sep = stream_endpoint->remote_seps[stream_endpoint->remote_sep_index]; + avdtp_prepare_capabilities(&connection->signaling_packet, trid, sep.configured_service_categories, sep.configuration, AVDTP_SI_GET_CONFIGURATION); + l2cap_reserve_packet_buffer(); + out_buffer = l2cap_get_outgoing_buffer(); + pos = avdtp_signaling_create_fragment(cid, &connection->signaling_packet, out_buffer); + if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ + stream_endpoint->acceptor_config_state = acceptor_config_state; + printf(" ACP: fragmented\n"); + } + l2cap_send_prepared(cid, pos); + break; + } + case AVDTP_ACCEPTOR_W4_L2CAP_FOR_MEDIA_CONNECTED: + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W4_L2CAP_FOR_MEDIA_CONNECTED; + break; + case AVDTP_ACCEPTOR_W2_ANSWER_OPEN_STREAM: + printf(" ACP: DONE\n"); + avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_OPEN); + break; + case AVDTP_ACCEPTOR_W2_ANSWER_START_STREAM: + printf(" ACP: DONE \n"); + printf(" -> AVDTP_STREAM_ENDPOINT_STREAMING \n"); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_STREAMING; + avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_START); + break; + case AVDTP_ACCEPTOR_W2_ANSWER_CLOSE_STREAM: + printf(" ACP: DONE\n"); + avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_CLOSE); + break; + case AVDTP_ACCEPTOR_W2_ANSWER_ABORT_STREAM: + printf(" ACP: DONE\n"); + avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_ABORT); + break; + case AVDTP_ACCEPTOR_W2_ANSWER_SUSPEND_STREAM: + printf(" ACP: DONE\n"); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_OPENED; + avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_SUSPEND); + break; + case AVDTP_ACCEPTOR_W2_REJECT_UNKNOWN_CMD: + status = 1; + printf(" ACP: DONE REJECT\n"); + connection->reject_signal_identifier = 0; + avdtp_acceptor_send_response_reject(cid, reject_signal_identifier, trid); + break; + case AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE: + status = 1; + printf(" ACP: DONE REJECT CATEGORY\n"); + connection->reject_service_category = 0; + avdtp_acceptor_send_response_reject_service_category(cid, reject_signal_identifier, reject_service_category, error_code, trid); + break; + + case AVDTP_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE: + status = 1; + printf(" ACP: DONE REJECT\n"); + connection->reject_signal_identifier = 0; + connection->error_code = 0; + avdtp_acceptor_send_response_reject_with_error_code(cid, reject_signal_identifier, error_code, trid); + break; + default: + status = 0; + printf(" ACP: NOT IMPLEMENTED\n"); + sent = 0; + } + avdtp_signaling_emit_accept(avdtp_sink_callback, connection->con_handle, connection->signaling_packet.signal_identifier, status); + + // check fragmentation + if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ + avdtp_sink_request_can_send_now_acceptor(connection, connection->l2cap_signaling_cid); + } +} diff --git a/src/classic/avdtp_acceptor.h b/src/classic/avdtp_acceptor.h new file mode 100644 index 000000000..80d703020 --- /dev/null +++ b/src/classic/avdtp_acceptor.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * avdtp_acp.h + * + * Audio/Video Distribution Transport Protocol - Acceptor Role + * + */ + +#ifndef __AVDTP_ACP_H +#define __AVDTP_ACP_H + +#include + +#if defined __cplusplus +extern "C" { +#endif + +void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t *packet, uint16_t size, int offset); +void avdtp_acceptor_stream_config_subsm_run(avdtp_connection_t * connection); + +#if defined __cplusplus +} +#endif + +#endif // __AVDTP_ACP_H \ No newline at end of file diff --git a/src/classic/avdtp_initiator.c b/src/classic/avdtp_initiator.c new file mode 100644 index 000000000..61b5da2e0 --- /dev/null +++ b/src/classic/avdtp_initiator.c @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + + +#include +#include +#include +#include +#include + +#include "btstack.h" +#include "avdtp.h" +#include "avdtp_util.h" +#include "avdtp_initiator.h" + + +static int avdtp_initiator_send_signaling_cmd(uint16_t cid, avdtp_signal_identifier_t identifier, uint8_t transaction_label){ + uint8_t command[2]; + command[0] = avdtp_header(transaction_label, AVDTP_SINGLE_PACKET, AVDTP_CMD_MSG); + command[1] = (uint8_t)identifier; + return l2cap_send(cid, command, sizeof(command)); +} + +static int avdtp_initiator_send_signaling_cmd_with_seid(uint16_t cid, avdtp_signal_identifier_t identifier, uint8_t transaction_label, uint8_t sep_id){ + uint8_t command[3]; + command[0] = avdtp_header(transaction_label, AVDTP_SINGLE_PACKET, AVDTP_CMD_MSG); + command[1] = (uint8_t)identifier; + command[2] = sep_id << 2; + return l2cap_send(cid, command, sizeof(command)); +} + +static void avdtp_signaling_emit_media_codec_capability(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_sep_t sep){ + if (get_bit16(sep.registered_service_categories, AVDTP_MEDIA_CODEC)){ + switch (sep.capabilities.media_codec.media_codec_type){ + case AVDTP_CODEC_SBC: + avdtp_signaling_emit_media_codec_sbc_capability(callback, con_handle, sep.capabilities.media_codec); + break; + default: + avdtp_signaling_emit_media_codec_other_capability(callback, con_handle, sep.capabilities.media_codec); + break; + } + } +} + +static void avdtp_signaling_emit_media_codec_configuration(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_sep_t sep){ + if (get_bit16(sep.registered_service_categories, AVDTP_MEDIA_CODEC)){ + switch (sep.capabilities.media_codec.media_codec_type){ + case AVDTP_CODEC_SBC: + avdtp_signaling_emit_media_codec_sbc_configuration(callback, con_handle, sep.capabilities.media_codec); + break; + default: + avdtp_signaling_emit_media_codec_other_configuration(callback, con_handle, sep.capabilities.media_codec); + break; + } + } +} + +void avdtp_initiator_stream_config_subsm(avdtp_connection_t * connection, uint8_t *packet, uint16_t size, int offset){ + int status = 0; + avdtp_stream_endpoint_t * stream_endpoint = NULL; + uint8_t remote_sep_index; + avdtp_sep_t sep; + if (connection->initiator_connection_state == AVDTP_SIGNALING_CONNECTION_INITIATOR_W4_ANSWER) { + connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE; + } else { + stream_endpoint = get_avdtp_stream_endpoint_associated_with_acp_seid(connection->acp_seid); + if (!stream_endpoint){ + stream_endpoint = get_avdtp_stream_endpoint_with_seid(connection->int_seid); + } + if (!stream_endpoint) return; + sep.seid = connection->acp_seid; + + printf("avdtp_initiator_stream_config_subsm int seid %d, acp seid %d, ident %d \n", connection->int_seid, connection->acp_seid, connection->signaling_packet.signal_identifier); + if (stream_endpoint->initiator_config_state != AVDTP_INITIATOR_W4_ANSWER) return; + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_STREAM_CONFIG_IDLE; + } + + switch (connection->signaling_packet.message_type){ + case AVDTP_RESPONSE_ACCEPT_MSG: + printf(" INT: AVDTP_RESPONSE_ACCEPT_MSG: "); + switch (connection->signaling_packet.signal_identifier){ + case AVDTP_SI_DISCOVER:{ + printf("AVDTP_SI_DISCOVER\n"); + if (connection->signaling_packet.transaction_label != connection->initiator_transaction_label){ + printf(" unexpected transaction label, got %d, expected %d\n", connection->signaling_packet.transaction_label, connection->initiator_transaction_label); + status = BAD_HEADER_FORMAT; + break; + } + + if (size == 3){ + printf(" ERROR code %02x\n", packet[offset]); + status = packet[offset]; + break; + } + + int i; + for (i = offset; i < size; i += 2){ + sep.seid = packet[i] >> 2; + offset++; + if (sep.seid < 0x01 || sep.seid > 0x3E){ + printf(" invalid sep id\n"); + status = BAD_ACP_SEID; + break; + } + sep.in_use = (packet[i] >> 1) & 0x01; + sep.media_type = (avdtp_media_type_t)(packet[i+1] >> 4); + sep.type = (avdtp_sep_type_t)((packet[i+1] >> 3) & 0x01); + avdtp_signaling_emit_sep(avdtp_sink_callback, connection->con_handle, sep); + } + break; + } + + case AVDTP_SI_GET_CAPABILITIES: + case AVDTP_SI_GET_ALL_CAPABILITIES: + printf("AVDTP_SI_GET(_ALL)_CAPABILITIES\n"); + sep.registered_service_categories = avdtp_unpack_service_capabilities(connection, &sep.capabilities, packet+offset, size-offset); + avdtp_signaling_emit_media_codec_capability(avdtp_sink_callback, connection->con_handle, sep); + break; + + case AVDTP_SI_GET_CONFIGURATION: + printf("AVDTP_SI_GET_CONFIGURATION\n"); + sep.configured_service_categories = avdtp_unpack_service_capabilities(connection, &sep.configuration, packet+offset, size-offset); + + avdtp_signaling_emit_media_codec_configuration(avdtp_sink_callback, connection->con_handle, sep); + break; + + case AVDTP_SI_RECONFIGURE: + printf("AVDTP_SI_RECONFIGURE\n"); + sep.configured_service_categories = avdtp_unpack_service_capabilities(connection, &sep.configuration, connection->signaling_packet.command+4, connection->signaling_packet.size-4); + // TODO check if configuration is supported + + remote_sep_index = avdtp_get_index_of_remote_stream_endpoint_with_seid(stream_endpoint, sep.seid); + if (remote_sep_index != 0xFF){ + stream_endpoint->remote_sep_index = remote_sep_index; + stream_endpoint->remote_seps[stream_endpoint->remote_sep_index] = sep; + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_CONFIGURED; + printf(" INT: update seid %d, to %p\n", stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); + } + break; + + case AVDTP_SI_SET_CONFIGURATION:{ + printf("AVDTP_SI_SET_CONFIGURATION\n"); + sep.configured_service_categories = connection->remote_capabilities_bitmap; + sep.configuration = connection->remote_capabilities; + sep.in_use = 1; + // TODO check if configuration is supported + + // find or add sep + remote_sep_index = avdtp_get_index_of_remote_stream_endpoint_with_seid(stream_endpoint, sep.seid); + if (remote_sep_index != 0xFF){ + stream_endpoint->remote_sep_index = remote_sep_index; + } else { + stream_endpoint->remote_sep_index = stream_endpoint->remote_seps_num; + stream_endpoint->remote_seps_num++; + } + stream_endpoint->remote_seps[stream_endpoint->remote_sep_index] = sep; + printf(" INT: configured seid %d, to %p\n", stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_CONFIGURED; + break; + } + + case AVDTP_SI_OPEN: + printf("AVDTP_SI_OPEN\n"); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_OPENED; + break; + case AVDTP_SI_START: + printf("AVDTP_SI_START\n"); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_STREAMING; + break; + case AVDTP_SI_SUSPEND: + printf("AVDTP_SI_SUSPEND\n"); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_OPENED; + break; + case AVDTP_SI_CLOSE: + printf("AVDTP_SI_CLOSE\n"); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_CLOSING; + break; + case AVDTP_SI_ABORT: + printf("AVDTP_SI_ABORT\n"); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_ABORTING; + break; + default: + status = 1; + printf(" AVDTP_RESPONSE_ACCEPT_MSG, signal %d not implemented\n", connection->signaling_packet.signal_identifier); + break; + } + break; + case AVDTP_RESPONSE_REJECT_MSG: + printf(" AVDTP_RESPONSE_REJECT_MSG signal %d\n", connection->signaling_packet.signal_identifier); + avdtp_signaling_emit_reject(avdtp_sink_callback, connection->con_handle, connection->signaling_packet.signal_identifier); + return; + case AVDTP_GENERAL_REJECT_MSG: + printf(" AVDTP_GENERAL_REJECT_MSG signal %d\n", connection->signaling_packet.signal_identifier); + avdtp_signaling_emit_general_reject(avdtp_sink_callback, connection->con_handle, connection->signaling_packet.signal_identifier); + return; + default: + break; + } + connection->initiator_transaction_label++; + connection->int_seid = 0; + connection->acp_seid = 0; + avdtp_signaling_emit_accept(avdtp_sink_callback, connection->con_handle, connection->signaling_packet.signal_identifier, status); +} + +void avdtp_initiator_stream_config_subsm_run(avdtp_connection_t * connection){ + int sent = 1; + switch (connection->initiator_connection_state){ + case AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS: + printf(" INT: AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS\n"); + connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W4_ANSWER; + avdtp_initiator_send_signaling_cmd(connection->l2cap_signaling_cid, AVDTP_SI_DISCOVER, connection->initiator_transaction_label); + break; + case AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CAPABILITIES: + printf(" INT: AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CAPABILITIES\n"); + connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W4_ANSWER; + avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_GET_CAPABILITIES, connection->initiator_transaction_label, connection->acp_seid); + break; + case AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_ALL_CAPABILITIES: + printf(" INT: AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_ALL_CAPABILITIES\n"); + connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W4_ANSWER; + avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_GET_ALL_CAPABILITIES, connection->initiator_transaction_label, connection->acp_seid); + break; + case AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CONFIGURATION: + printf(" INT: AVDTP_INITIATOR_W4_GET_CONFIGURATION\n"); + connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W4_ANSWER; + avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_GET_CONFIGURATION, connection->initiator_transaction_label, connection->acp_seid); + break; + default: + sent = 0; + break; + } + + if (sent) return; + sent = 1; + avdtp_stream_endpoint_t * stream_endpoint = NULL; + + printf(" run int seid %d, acp seid %d\n", connection->int_seid, connection->acp_seid); + + stream_endpoint = get_avdtp_stream_endpoint_associated_with_acp_seid(connection->acp_seid); + if (!stream_endpoint){ + stream_endpoint = get_avdtp_stream_endpoint_with_seid(connection->int_seid); + } + if (!stream_endpoint) return; + + avdtp_initiator_stream_endpoint_state_t stream_endpoint_state = stream_endpoint->initiator_config_state; + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W4_ANSWER; + + switch (stream_endpoint_state){ + case AVDTP_INITIATOR_W2_SET_CONFIGURATION: + case AVDTP_INITIATOR_W2_RECONFIGURE_STREAM_WITH_SEID:{ + printf(" INT: AVDTP_INITIATOR_W2_(RE)CONFIGURATION bitmap, int seid %d, acp seid %d\n", connection->int_seid, connection->acp_seid); + printf_hexdump( connection->remote_capabilities.media_codec.media_codec_information, connection->remote_capabilities.media_codec.media_codec_information_len); + connection->signaling_packet.acp_seid = connection->acp_seid; + connection->signaling_packet.int_seid = connection->int_seid; + + connection->signaling_packet.signal_identifier = AVDTP_SI_SET_CONFIGURATION; + + if (stream_endpoint_state == AVDTP_INITIATOR_W2_RECONFIGURE_STREAM_WITH_SEID){ + connection->signaling_packet.signal_identifier = AVDTP_SI_RECONFIGURE; + } + + avdtp_prepare_capabilities(&connection->signaling_packet, connection->initiator_transaction_label, connection->remote_capabilities_bitmap, connection->remote_capabilities, connection->signaling_packet.signal_identifier); + l2cap_reserve_packet_buffer(); + uint8_t * out_buffer = l2cap_get_outgoing_buffer(); + uint16_t pos = avdtp_signaling_create_fragment(connection->l2cap_signaling_cid, &connection->signaling_packet, out_buffer); + if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_FRAGMENTATED_COMMAND; + printf(" INT: fragmented\n"); + } + l2cap_send_prepared(connection->l2cap_signaling_cid, pos); + break; + } + case AVDTP_INITIATOR_FRAGMENTATED_COMMAND:{ + l2cap_reserve_packet_buffer(); + uint8_t * out_buffer = l2cap_get_outgoing_buffer(); + uint16_t pos = avdtp_signaling_create_fragment(connection->l2cap_signaling_cid, &connection->signaling_packet, out_buffer); + if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_FRAGMENTATED_COMMAND; + printf(" INT: fragmented\n"); + } + l2cap_send_prepared(connection->l2cap_signaling_cid, pos); + break; + } + case AVDTP_INITIATOR_W2_MEDIA_CONNECT: + printf(" INT: AVDTP_INITIATOR_W4_L2CAP_FOR_MEDIA_CONNECTED\n"); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_W4_L2CAP_FOR_MEDIA_CONNECTED; + avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_OPEN, connection->initiator_transaction_label, connection->acp_seid); + break; + case AVDTP_INITIATOR_W2_SUSPEND_STREAM_WITH_SEID: + printf(" INT: AVDTP_INITIATOR_W4_SUSPEND_STREAM_WITH_SEID\n"); + avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_SUSPEND, connection->initiator_transaction_label, connection->acp_seid); + break; + case AVDTP_INITIATOR_W2_STREAMING_START: + printf(" INT: AVDTP_INITIATOR_W4_STREAMING_START\n"); + avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_START, connection->initiator_transaction_label, connection->acp_seid); + break; + case AVDTP_INITIATOR_W2_STREAMING_STOP: + printf(" INT: AVDTP_INITIATOR_W4_STREAMING_STOP\n"); + avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_CLOSE, connection->initiator_transaction_label, connection->acp_seid); + break; + case AVDTP_INITIATOR_W2_STREAMING_ABORT: + printf(" INT: AVDTP_INITIATOR_W4_STREAMING_ABORT\n"); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_ABORTING; + avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_ABORT, connection->initiator_transaction_label, connection->acp_seid); + break; + default: + break; + } + + // check fragmentation + if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ + avdtp_sink_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); + } +} diff --git a/src/classic/avdtp_initiator.h b/src/classic/avdtp_initiator.h new file mode 100644 index 000000000..1ad211be7 --- /dev/null +++ b/src/classic/avdtp_initiator.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * avdtp_int.h + * + * Audio/Video Distribution Transport Protocol - Initiator Role + * + */ + +#ifndef __AVDTP_INT_H +#define __AVDTP_INT_H + +#include + +#if defined __cplusplus +extern "C" { +#endif + +void avdtp_initiator_stream_config_subsm(avdtp_connection_t * connection, uint8_t *packet, uint16_t size, int offset); +void avdtp_initiator_stream_config_subsm_run(avdtp_connection_t * connection); + +#if defined __cplusplus +} +#endif + +#endif // __AVDTP_INT_H \ No newline at end of file diff --git a/src/classic/avdtp_sink.c b/src/classic/avdtp_sink.c new file mode 100644 index 000000000..e724ab086 --- /dev/null +++ b/src/classic/avdtp_sink.c @@ -0,0 +1,901 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + + +#include +#include +#include +#include +#include + +#include "btstack.h" +#include "avdtp.h" +#include "avdtp_sink.h" +#include "avdtp_util.h" +#include "avdtp_initiator.h" +#include "avdtp_acceptor.h" + +static const char * default_avdtp_sink_service_name = "BTstack AVDTP Sink Service"; +static const char * default_avdtp_sink_service_provider_name = "BTstack AVDTP Sink Service Provider"; + +// TODO list of devices +static btstack_linked_list_t avdtp_connections; +static uint16_t stream_endpoints_id_counter; + +btstack_linked_list_t stream_endpoints; +btstack_packet_handler_t avdtp_sink_callback; + + +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); + +static void (*handle_media_data)(avdtp_stream_endpoint_t * stream_endpoint, uint8_t *packet, uint16_t size); + +void a2dp_sink_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name){ + uint8_t* attribute; + de_create_sequence(service); + + // 0x0000 "Service Record Handle" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceRecordHandle); + de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle); + + // 0x0001 "Service Class ID List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceClassIDList); + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, AUDIO_SINK_GROUP); + } + de_pop_sequence(service, attribute); + + // 0x0004 "Protocol Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ProtocolDescriptorList); + attribute = de_push_sequence(service); + { + uint8_t* l2cpProtocol = de_push_sequence(attribute); + { + de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, SDP_L2CAPProtocol); + de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, PSM_AVDTP); + } + de_pop_sequence(attribute, l2cpProtocol); + + uint8_t* avProtocol = de_push_sequence(attribute); + { + de_add_number(avProtocol, DE_UUID, DE_SIZE_16, PSM_AVDTP); // avProtocol_service + de_add_number(avProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version + } + de_pop_sequence(attribute, avProtocol); + } + de_pop_sequence(service, attribute); + + // 0x0005 "Public Browse Group" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BrowseGroupList); // public browse group + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, SDP_PublicBrowseGroup); + } + de_pop_sequence(service, attribute); + + // 0x0009 "Bluetooth Profile Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BluetoothProfileDescriptorList); + attribute = de_push_sequence(service); + { + uint8_t *a2dProfile = de_push_sequence(attribute); + { + de_add_number(a2dProfile, DE_UUID, DE_SIZE_16, ADVANCED_AUDIO_DISTRIBUTION); + de_add_number(a2dProfile, DE_UINT, DE_SIZE_16, 0x0103); + } + de_pop_sequence(attribute, a2dProfile); + } + de_pop_sequence(service, attribute); + + + // 0x0100 "Service Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); + if (service_name){ + de_add_data(service, DE_STRING, strlen(service_name), (uint8_t *) service_name); + } else { + de_add_data(service, DE_STRING, strlen(default_avdtp_sink_service_name), (uint8_t *) default_avdtp_sink_service_name); + } + + // 0x0100 "Provider Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102); + if (service_provider_name){ + de_add_data(service, DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name); + } else { + de_add_data(service, DE_STRING, strlen(default_avdtp_sink_service_provider_name), (uint8_t *) default_avdtp_sink_service_provider_name); + } + + // 0x0311 "Supported Features" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); + de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); +} + + +static avdtp_connection_t * avdtp_sink_create_connection(bd_addr_t remote_addr){ + avdtp_connection_t * connection = btstack_memory_avdtp_connection_get(); + memset(connection, 0, sizeof(avdtp_connection_t)); + connection->state = AVDTP_SIGNALING_CONNECTION_IDLE; + connection->initiator_transaction_label++; + memcpy(connection->remote_addr, remote_addr, 6); + btstack_linked_list_add(&avdtp_connections, (btstack_linked_item_t *) connection); + return connection; +} + +avdtp_stream_endpoint_t * avdtp_sink_create_stream_endpoint(avdtp_sep_type_t sep_type, avdtp_media_type_t media_type){ + avdtp_stream_endpoint_t * stream_endpoint = btstack_memory_avdtp_stream_endpoint_get(); + memset(stream_endpoint, 0, sizeof(avdtp_stream_endpoint_t)); + stream_endpoints_id_counter++; + stream_endpoint->sep.seid = stream_endpoints_id_counter; + stream_endpoint->sep.media_type = media_type; + stream_endpoint->sep.type = sep_type; + btstack_linked_list_add(&stream_endpoints, (btstack_linked_item_t *) stream_endpoint); + return stream_endpoint; +} + +void avdtp_sink_register_media_transport_category(uint8_t seid){ + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_for_seid(seid); + if (!stream_endpoint){ + log_error("avdtp_sink_register_media_transport_category: stream endpoint with seid %d is not registered", seid); + return; + } + uint16_t bitmap = store_bit16(stream_endpoint->sep.registered_service_categories, AVDTP_MEDIA_TRANSPORT, 1); + stream_endpoint->sep.registered_service_categories = bitmap; + printf("registered services AVDTP_MEDIA_TRANSPORT(%d) %02x\n", AVDTP_MEDIA_TRANSPORT, stream_endpoint->sep.registered_service_categories); +} + +void avdtp_sink_register_reporting_category(uint8_t seid){ + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_for_seid(seid); + if (!stream_endpoint){ + log_error("avdtp_sink_register_media_transport_category: stream endpoint with seid %d is not registered", seid); + return; + } + uint16_t bitmap = store_bit16(stream_endpoint->sep.registered_service_categories, AVDTP_REPORTING, 1); + stream_endpoint->sep.registered_service_categories = bitmap; +} + +void avdtp_sink_register_delay_reporting_category(uint8_t seid){ + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_for_seid(seid); + if (!stream_endpoint){ + log_error("avdtp_sink_register_media_transport_category: stream endpoint with seid %d is not registered", seid); + return; + } + uint16_t bitmap = store_bit16(stream_endpoint->sep.registered_service_categories, AVDTP_DELAY_REPORTING, 1); + stream_endpoint->sep.registered_service_categories = bitmap; +} + +void avdtp_sink_register_recovery_category(uint8_t seid, uint8_t maximum_recovery_window_size, uint8_t maximum_number_media_packets){ + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_for_seid(seid); + if (!stream_endpoint){ + log_error("avdtp_sink_register_media_transport_category: stream endpoint with seid %d is not registered", seid); + return; + } + uint16_t bitmap = store_bit16(stream_endpoint->sep.registered_service_categories, AVDTP_RECOVERY, 1); + stream_endpoint->sep.registered_service_categories = bitmap; + stream_endpoint->sep.capabilities.recovery.recovery_type = 0x01; // 0x01 = RFC2733 + stream_endpoint->sep.capabilities.recovery.maximum_recovery_window_size = maximum_recovery_window_size; + stream_endpoint->sep.capabilities.recovery.maximum_number_media_packets = maximum_number_media_packets; +} + +void avdtp_sink_register_content_protection_category(uint8_t seid, uint16_t cp_type, const uint8_t * cp_type_value, uint8_t cp_type_value_len){ + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_for_seid(seid); + if (!stream_endpoint){ + log_error("avdtp_sink_register_media_transport_category: stream endpoint with seid %d is not registered", seid); + return; + } + uint16_t bitmap = store_bit16(stream_endpoint->sep.registered_service_categories, AVDTP_CONTENT_PROTECTION, 1); + stream_endpoint->sep.registered_service_categories = bitmap; + stream_endpoint->sep.capabilities.content_protection.cp_type = cp_type; + stream_endpoint->sep.capabilities.content_protection.cp_type_value = cp_type_value; + stream_endpoint->sep.capabilities.content_protection.cp_type_value_len = cp_type_value_len; +} + +void avdtp_sink_register_header_compression_category(uint8_t seid, uint8_t back_ch, uint8_t media, uint8_t recovery){ + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_for_seid(seid); + if (!stream_endpoint){ + log_error("avdtp_sink_register_media_transport_category: stream endpoint with seid %d is not registered", seid); + return; + } + uint16_t bitmap = store_bit16(stream_endpoint->sep.registered_service_categories, AVDTP_HEADER_COMPRESSION, 1); + stream_endpoint->sep.registered_service_categories = bitmap; + stream_endpoint->sep.capabilities.header_compression.back_ch = back_ch; + stream_endpoint->sep.capabilities.header_compression.media = media; + stream_endpoint->sep.capabilities.header_compression.recovery = recovery; +} + +void avdtp_sink_register_media_codec_category(uint8_t seid, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, const uint8_t * media_codec_info, uint16_t media_codec_info_len){ + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_for_seid(seid); + if (!stream_endpoint){ + log_error("avdtp_sink_register_media_transport_category: stream endpoint with seid %d is not registered", seid); + return; + } + uint16_t bitmap = store_bit16(stream_endpoint->sep.registered_service_categories, AVDTP_MEDIA_CODEC, 1); + stream_endpoint->sep.registered_service_categories = bitmap; + printf("registered services AVDTP_MEDIA_CODEC(%d) %02x\n", AVDTP_MEDIA_CODEC, stream_endpoint->sep.registered_service_categories); + stream_endpoint->sep.capabilities.media_codec.media_type = media_type; + stream_endpoint->sep.capabilities.media_codec.media_codec_type = media_codec_type; + stream_endpoint->sep.capabilities.media_codec.media_codec_information = media_codec_info; + stream_endpoint->sep.capabilities.media_codec.media_codec_information_len = media_codec_info_len; +} + +void avdtp_sink_register_multiplexing_category(uint8_t seid, uint8_t fragmentation){ + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_for_seid(seid); + if (!stream_endpoint){ + log_error("avdtp_sink_register_media_transport_category: stream endpoint with seid %d is not registered", seid); + return; + } + uint16_t bitmap = store_bit16(stream_endpoint->sep.registered_service_categories, AVDTP_MULTIPLEXING, 1); + stream_endpoint->sep.registered_service_categories = bitmap; + stream_endpoint->sep.capabilities.multiplexing_mode.fragmentation = fragmentation; +} + +// // media, reporting. recovery +// void avdtp_sink_register_media_transport_identifier_for_multiplexing_category(uint8_t seid, uint8_t fragmentation){ + +// } + + +static avdtp_connection_t * get_avdtp_connection_for_bd_addr(bd_addr_t addr){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avdtp_connections); + while (btstack_linked_list_iterator_has_next(&it)){ + avdtp_connection_t * connection = (avdtp_connection_t *)btstack_linked_list_iterator_next(&it); + if (memcmp(addr, connection->remote_addr, 6) != 0) continue; + return connection; + } + return NULL; +} + +static avdtp_connection_t * get_avdtp_connection_for_con_handle(hci_con_handle_t con_handle){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avdtp_connections); + while (btstack_linked_list_iterator_has_next(&it)){ + avdtp_connection_t * connection = (avdtp_connection_t *)btstack_linked_list_iterator_next(&it); + if (connection->con_handle != con_handle) continue; + return connection; + } + return NULL; +} + +static avdtp_connection_t * get_avdtp_connection_for_l2cap_signaling_cid(uint16_t l2cap_cid){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avdtp_connections); + while (btstack_linked_list_iterator_has_next(&it)){ + avdtp_connection_t * connection = (avdtp_connection_t *)btstack_linked_list_iterator_next(&it); + if (connection->l2cap_signaling_cid != l2cap_cid) continue; + return connection; + } + return NULL; +} + +static avdtp_stream_endpoint_t * get_avdtp_stream_endpoint_for_l2cap_cid(uint16_t l2cap_cid){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &stream_endpoints); + while (btstack_linked_list_iterator_has_next(&it)){ + avdtp_stream_endpoint_t * stream_endpoint = (avdtp_stream_endpoint_t *)btstack_linked_list_iterator_next(&it); + if (stream_endpoint->l2cap_media_cid == l2cap_cid){ + return stream_endpoint; + } + if (stream_endpoint->l2cap_reporting_cid == l2cap_cid){ + return stream_endpoint; + } + if (stream_endpoint->l2cap_recovery_cid == l2cap_cid){ + return stream_endpoint; + } + } + return NULL; +} + +/* START: tracking can send now requests pro l2cap cid */ +static void avdtp_sink_handle_can_send_now(avdtp_connection_t * connection, uint16_t l2cap_cid){ + if (connection->wait_to_send_acceptor){ + connection->wait_to_send_acceptor = 0; + avdtp_acceptor_stream_config_subsm_run(connection); + } else if (connection->wait_to_send_initiator){ + connection->wait_to_send_initiator = 0; + avdtp_initiator_stream_config_subsm_run(connection); + } else if (connection->wait_to_send_self){ + connection->wait_to_send_self = 0; + if (connection->disconnect){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &stream_endpoints); + while (btstack_linked_list_iterator_has_next(&it)){ + avdtp_stream_endpoint_t * stream_endpoint = (avdtp_stream_endpoint_t *)btstack_linked_list_iterator_next(&it); + if (stream_endpoint->connection == connection){ + if (stream_endpoint->state >= AVDTP_STREAM_ENDPOINT_OPENED && stream_endpoint->state != AVDTP_STREAM_ENDPOINT_W4_L2CAP_FOR_MEDIA_DISCONNECTED){ + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_W4_L2CAP_FOR_MEDIA_DISCONNECTED; + avdtp_sink_request_can_send_now_self(connection, connection->l2cap_signaling_cid); + l2cap_disconnect(stream_endpoint->l2cap_media_cid, 0); + return; + } + } + } + connection->disconnect = 0; + connection->state = AVDTP_SIGNALING_CONNECTION_W4_L2CAP_DISCONNECTED; + l2cap_disconnect(connection->l2cap_signaling_cid, 0); + return; + } + } + + // re-register + int more_to_send = connection->wait_to_send_acceptor || connection->wait_to_send_initiator || connection->wait_to_send_self; + if (more_to_send){ + l2cap_request_can_send_now_event(l2cap_cid); + } +} + + +/* END: tracking can send now requests pro l2cap cid */ +// TODO remove +static void handle_l2cap_data_packet_for_signaling_connection(avdtp_connection_t * connection, uint8_t *packet, uint16_t size){ + int offset = avdtp_read_signaling_header(&connection->signaling_packet, packet, size); + switch (connection->signaling_packet.message_type){ + case AVDTP_CMD_MSG: + avdtp_acceptor_stream_config_subsm(connection, packet, size, offset); + break; + default: + avdtp_initiator_stream_config_subsm(connection, packet, size, offset); + break; + } +} + +static void stream_endpoint_state_machine(avdtp_connection_t * connection, avdtp_stream_endpoint_t * stream_endpoint, uint8_t packet_type, uint8_t event, uint8_t *packet, uint16_t size){ + uint16_t local_cid; + switch (packet_type){ + case L2CAP_DATA_PACKET:{ + int offset = avdtp_read_signaling_header(&connection->signaling_packet, packet, size); + if (connection->signaling_packet.message_type == AVDTP_CMD_MSG){ + avdtp_acceptor_stream_config_subsm(connection, packet, size, offset); + } else { + avdtp_initiator_stream_config_subsm(connection, packet, size, offset); + } + break; + } + case HCI_EVENT_PACKET: + switch (event){ + case L2CAP_EVENT_CHANNEL_OPENED: + if (stream_endpoint->l2cap_media_cid == 0){ + if (stream_endpoint->state != AVDTP_STREAM_ENDPOINT_W4_L2CAP_FOR_MEDIA_CONNECTED) return; + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_OPENED; + stream_endpoint->connection = connection; + stream_endpoint->l2cap_media_cid = l2cap_event_channel_opened_get_local_cid(packet);; + printf(" -> AVDTP_STREAM_ENDPOINT_OPENED, stream endpoint %p, connection %p\n", stream_endpoint, connection); + break; + } + break; + case L2CAP_EVENT_CHANNEL_CLOSED: + local_cid = l2cap_event_channel_closed_get_local_cid(packet); + if (stream_endpoint->l2cap_media_cid == local_cid){ + stream_endpoint->l2cap_media_cid = 0; + printf(" -> L2CAP_EVENT_CHANNEL_CLOSED: AVDTP_STREAM_ENDPOINT_IDLE\n"); + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_IDLE; + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_STREAM_CONFIG_IDLE; + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_STREAM_CONFIG_IDLE; + stream_endpoint->remote_seps_num = 0; + memset(stream_endpoint->remote_seps, 0, sizeof(avdtp_sep_t)*MAX_NUM_SEPS); + stream_endpoint->remote_sep_index = 0; + break; + } + if (stream_endpoint->l2cap_recovery_cid == local_cid){ + log_info(" -> L2CAP_EVENT_CHANNEL_CLOSED recovery cid 0x%0x", local_cid); + stream_endpoint->l2cap_recovery_cid = 0; + break; + } + + if (stream_endpoint->l2cap_reporting_cid == local_cid){ + log_info("L2CAP_EVENT_CHANNEL_CLOSED reporting cid 0x%0x", local_cid); + stream_endpoint->l2cap_reporting_cid = 0; + break; + } + break; + default: + break; + } + break; + default: + break; + } +} + +static void avdtp_initialize_stream_endpoint(avdtp_stream_endpoint_t * stream_endpoint){ + stream_endpoint->connection = NULL; + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_IDLE; + stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_STREAM_CONFIG_IDLE; + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_STREAM_CONFIG_IDLE; + stream_endpoint->remote_sep_index = 0; + stream_endpoint->media_disconnect = 0; + stream_endpoint->remote_seps_num = 0; + stream_endpoint->sep.in_use = 0; + memset(stream_endpoint->remote_seps, 0, sizeof(stream_endpoint->remote_seps)); + stream_endpoint->remote_sep_index = 0; +} + +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + bd_addr_t event_addr; + hci_con_handle_t con_handle; + uint16_t psm; + uint16_t local_cid; + avdtp_stream_endpoint_t * stream_endpoint = NULL; + avdtp_connection_t * connection = NULL; + + switch (packet_type) { + case L2CAP_DATA_PACKET: + connection = get_avdtp_connection_for_l2cap_signaling_cid(channel); + if (connection){ + handle_l2cap_data_packet_for_signaling_connection(connection, packet, size); + break; + } + + stream_endpoint = get_avdtp_stream_endpoint_for_l2cap_cid(channel); + if (!stream_endpoint){ + if (!connection) break; + handle_l2cap_data_packet_for_signaling_connection(connection, packet, size); + break; + } + + if (channel == stream_endpoint->connection->l2cap_signaling_cid){ + stream_endpoint_state_machine(stream_endpoint->connection, stream_endpoint, L2CAP_DATA_PACKET, 0, packet, size); + break; + } + + if (channel == stream_endpoint->l2cap_media_cid){ + (*handle_media_data)(stream_endpoint, packet, size); + break; + } + + if (channel == stream_endpoint->l2cap_reporting_cid){ + // TODO + printf("L2CAP_DATA_PACKET for reporting: NOT IMPLEMENTED\n"); + } else if (channel == stream_endpoint->l2cap_recovery_cid){ + // TODO + printf("L2CAP_DATA_PACKET for recovery: NOT IMPLEMENTED\n"); + } else { + log_error("avdtp packet handler L2CAP_DATA_PACKET: local cid 0x%02x not found", channel); + } + break; + + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + case L2CAP_EVENT_INCOMING_CONNECTION: + l2cap_event_incoming_connection_get_address(packet, event_addr); + local_cid = l2cap_event_incoming_connection_get_local_cid(packet); + + connection = get_avdtp_connection_for_bd_addr(event_addr); + if (!connection){ + connection = avdtp_sink_create_connection(event_addr); + connection->state = AVDTP_SIGNALING_CONNECTION_W4_L2CAP_CONNECTED; + l2cap_accept_connection(local_cid); + break; + } + + stream_endpoint = get_avdtp_stream_endpoint_for_seid(connection->query_seid); + if (!stream_endpoint) { + printf("L2CAP_EVENT_INCOMING_CONNECTION no streamendpoint found for seid %d\n", connection->query_seid); + break; + } + + if (stream_endpoint->l2cap_media_cid == 0){ + if (stream_endpoint->state != AVDTP_STREAM_ENDPOINT_W4_L2CAP_FOR_MEDIA_CONNECTED) break; + l2cap_accept_connection(local_cid); + break; + } + break; + + case L2CAP_EVENT_CHANNEL_OPENED: + // inform about new l2cap connection + l2cap_event_channel_opened_get_address(packet, event_addr); + + if (l2cap_event_channel_opened_get_status(packet)){ + log_error("L2CAP connection to connection %s failed. status code 0x%02x", + bd_addr_to_str(event_addr), l2cap_event_channel_opened_get_status(packet)); + break; + } + psm = l2cap_event_channel_opened_get_psm(packet); + if (psm != PSM_AVDTP){ + log_error("unexpected PSM - Not implemented yet, avdtp sink: L2CAP_EVENT_CHANNEL_OPENED"); + return; + } + + con_handle = l2cap_event_channel_opened_get_handle(packet); + local_cid = l2cap_event_channel_opened_get_local_cid(packet); + + // printf("L2CAP_EVENT_CHANNEL_OPENED: Channel successfully opened: %s, handle 0x%02x, psm 0x%02x, local cid 0x%02x, remote cid 0x%02x\n", + // bd_addr_to_str(event_addr), con_handle, psm, local_cid, l2cap_event_channel_opened_get_remote_cid(packet)); + + if (psm != PSM_AVDTP) break; + + connection = get_avdtp_connection_for_bd_addr(event_addr); + if (!connection) break; + + if (connection->l2cap_signaling_cid == 0) { + if (connection->state != AVDTP_SIGNALING_CONNECTION_W4_L2CAP_CONNECTED) break; + connection->l2cap_signaling_cid = local_cid; + connection->con_handle = con_handle; + connection->query_seid = 0; + connection->state = AVDTP_SIGNALING_CONNECTION_OPENED; + printf(" -> AVDTP_SIGNALING_CONNECTION_OPENED, connection %p\n", connection); + avdtp_signaling_emit_connection_established(avdtp_sink_callback, con_handle, event_addr, 0); + break; + } + + stream_endpoint = get_avdtp_stream_endpoint_for_seid(connection->query_seid); + if (!stream_endpoint){ + printf("L2CAP_EVENT_CHANNEL_OPENED: stream_endpoint not found"); + return; + } + stream_endpoint_state_machine(connection, stream_endpoint, HCI_EVENT_PACKET, L2CAP_EVENT_CHANNEL_OPENED, packet, size); + break; + + case L2CAP_EVENT_CHANNEL_CLOSED: + // data: event (8), len(8), channel (16) + local_cid = l2cap_event_channel_closed_get_local_cid(packet); + connection = get_avdtp_connection_for_l2cap_signaling_cid(local_cid); + printf(" -> L2CAP_EVENT_CHANNEL_CLOSED signaling cid 0x%0x\n", local_cid); + + if (connection){ + printf(" -> AVDTP_STREAM_ENDPOINT_IDLE, connection closed\n"); + btstack_linked_list_remove(&avdtp_connections, (btstack_linked_item_t*) connection); + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &stream_endpoints); + while (btstack_linked_list_iterator_has_next(&it)){ + avdtp_stream_endpoint_t * _stream_endpoint = (avdtp_stream_endpoint_t *)btstack_linked_list_iterator_next(&it); + avdtp_initialize_stream_endpoint(_stream_endpoint); + } + break; + } + + stream_endpoint = get_avdtp_stream_endpoint_for_l2cap_cid(local_cid); + if (!stream_endpoint) return; + + stream_endpoint_state_machine(connection, stream_endpoint, HCI_EVENT_PACKET, L2CAP_EVENT_CHANNEL_CLOSED, packet, size); + break; + + case HCI_EVENT_DISCONNECTION_COMPLETE: + break; + + case L2CAP_EVENT_CAN_SEND_NOW: + connection = get_avdtp_connection_for_l2cap_signaling_cid(channel); + if (!connection) { + stream_endpoint = get_avdtp_stream_endpoint_for_l2cap_cid(channel); + if (!stream_endpoint->connection) break; + connection = stream_endpoint->connection; + } + avdtp_sink_handle_can_send_now(connection, channel); + break; + default: + printf("unknown HCI event type %02x\n", hci_event_packet_get_type(packet)); + break; + } + break; + + default: + // other packet type + break; + } +} + +// TODO: find out which security level is needed, and replace LEVEL_0 in avdtp_sink_init +void avdtp_sink_init(void){ + stream_endpoints = NULL; + avdtp_connections = NULL; + stream_endpoints_id_counter = 0; + l2cap_register_service(&packet_handler, PSM_AVDTP, 0xffff, LEVEL_0); +} + +void avdtp_sink_register_media_handler(void (*callback)(avdtp_stream_endpoint_t * stream_endpoint, uint8_t *packet, uint16_t size)){ + if (callback == NULL){ + log_error("avdtp_sink_register_media_handler called with NULL callback"); + return; + } + handle_media_data = callback; +} + +void avdtp_sink_register_packet_handler(btstack_packet_handler_t callback){ + if (callback == NULL){ + log_error("avdtp_sink_register_packet_handler called with NULL callback"); + return; + } + avdtp_sink_callback = callback; +} + +void avdtp_sink_connect(bd_addr_t bd_addr){ + avdtp_connection_t * connection = get_avdtp_connection_for_bd_addr(bd_addr); + if (!connection){ + connection = avdtp_sink_create_connection(bd_addr); + } + if (connection->state != AVDTP_SIGNALING_CONNECTION_IDLE) return; + connection->state = AVDTP_SIGNALING_CONNECTION_W4_L2CAP_CONNECTED; + l2cap_create_channel(packet_handler, connection->remote_addr, PSM_AVDTP, 0xffff, NULL); +} + +void avdtp_sink_disconnect(uint16_t con_handle){ + avdtp_connection_t * connection = get_avdtp_connection_for_con_handle(con_handle); + if (connection->state == AVDTP_SIGNALING_CONNECTION_IDLE) return; + if (connection->state == AVDTP_SIGNALING_CONNECTION_W4_L2CAP_DISCONNECTED) return; + + connection->disconnect = 1; + avdtp_sink_request_can_send_now_self(connection, connection->l2cap_signaling_cid); +} + +void avdtp_sink_open_stream(uint16_t con_handle, uint8_t acp_seid){ + avdtp_connection_t * connection = get_avdtp_connection_for_con_handle(con_handle); + if (!connection){ + printf("avdtp_sink_media_connect: no connection for handle 0x%02x found\n", con_handle); + return; + } + + if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) { + printf("avdtp_sink_media_connect: wrong connection state %d\n", connection->state); + return; + } + + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_associated_with_acp_seid(acp_seid); + if (!stream_endpoint) { + printf("avdtp_sink_media_connect: no stream_endpoint with acp seid %d found\n", acp_seid); + return; + } + + if (stream_endpoint->state < AVDTP_STREAM_ENDPOINT_CONFIGURED) return; + if (stream_endpoint->remote_sep_index == 0xFF) return; + + printf(" AVDTP_INITIATOR_W2_MEDIA_CONNECT \n"); + connection->initiator_transaction_label++; + connection->acp_seid = acp_seid; + connection->int_seid = stream_endpoint->sep.seid; + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_MEDIA_CONNECT; + avdtp_sink_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); +} + +void avdtp_sink_start_stream(uint16_t con_handle, uint8_t acp_seid){ + avdtp_connection_t * connection = get_avdtp_connection_for_con_handle(con_handle); + if (!connection){ + printf("avdtp_sink_media_connect: no connection for handle 0x%02x found\n", con_handle); + return; + } + + if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) { + printf("avdtp_sink_media_connect: wrong connection state %d\n", connection->state); + return; + } + + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_associated_with_acp_seid(acp_seid); + if (!stream_endpoint) { + printf("avdtp_sink_media_connect: no stream_endpoint with acp_seid %d found\n", acp_seid); + return; + } + if (stream_endpoint->remote_sep_index == 0xFF) return; + if (stream_endpoint->state < AVDTP_STREAM_ENDPOINT_OPENED) return; + printf(" AVDTP_INITIATOR_W2_STREAMING_START \n"); + connection->initiator_transaction_label++; + connection->acp_seid = acp_seid; + connection->int_seid = stream_endpoint->sep.seid; + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_STREAMING_START; + avdtp_sink_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); +} + +void avdtp_sink_stop_stream(uint16_t con_handle, uint8_t acp_seid){ + avdtp_connection_t * connection = get_avdtp_connection_for_con_handle(con_handle); + if (!connection){ + printf("avdtp_sink_stop_stream: no connection for handle 0x%02x found\n", con_handle); + return; + } + if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) { + printf("avdtp_sink_stop_stream: wrong connection state %d\n", connection->state); + return; + } + + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_associated_with_acp_seid(acp_seid); + if (!stream_endpoint) { + printf("avdtp_sink_stop_stream: no stream_endpoint with acp seid %d found\n", acp_seid); + return; + } + if (stream_endpoint->remote_sep_index == 0xFF) return; + switch (stream_endpoint->state){ + case AVDTP_STREAM_ENDPOINT_OPENED: + case AVDTP_STREAM_ENDPOINT_STREAMING: + printf(" AVDTP_INITIATOR_W2_STREAMING_STOP \n"); + connection->initiator_transaction_label++; + connection->acp_seid = acp_seid; + connection->int_seid = stream_endpoint->sep.seid; + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_STREAMING_STOP; + avdtp_sink_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); + break; + default: + break; + } +} + +void avdtp_sink_abort_stream(uint16_t con_handle, uint8_t acp_seid){ + avdtp_connection_t * connection = get_avdtp_connection_for_con_handle(con_handle); + if (!connection){ + printf("avdtp_sink_abort_stream: no connection for handle 0x%02x found\n", con_handle); + return; + } + + if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) { + printf("avdtp_sink_abort_stream: wrong connection state %d\n", connection->state); + return; + } + + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_associated_with_acp_seid(acp_seid); + if (!stream_endpoint) { + printf("avdtp_sink_abort_stream: no stream_endpoint for seid %d found\n", acp_seid); + return; + } + if (stream_endpoint->remote_sep_index == 0xFF) return; + switch (stream_endpoint->state){ + case AVDTP_STREAM_ENDPOINT_CONFIGURED: + case AVDTP_STREAM_ENDPOINT_CLOSING: + case AVDTP_STREAM_ENDPOINT_OPENED: + case AVDTP_STREAM_ENDPOINT_STREAMING: + printf(" AVDTP_INITIATOR_W2_STREAMING_ABORT \n"); + connection->initiator_transaction_label++; + connection->acp_seid = acp_seid; + connection->int_seid = stream_endpoint->sep.seid; + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_STREAMING_ABORT; + avdtp_sink_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); + break; + default: + break; + } +} + +void avdtp_sink_discover_stream_endpoints(uint16_t con_handle){ + avdtp_connection_t * connection = get_avdtp_connection_for_con_handle(con_handle); + if (!connection){ + printf("avdtp_sink_discover_stream_endpoints: no connection for handle 0x%02x found\n", con_handle); + return; + } + if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; + + switch (connection->initiator_connection_state){ + case AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE: + connection->initiator_transaction_label++; + connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS; + avdtp_sink_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); + break; + default: + printf("avdtp_sink_discover_stream_endpoints: wrong state\n"); + break; + } +} + + +void avdtp_sink_get_capabilities(uint16_t con_handle, uint8_t acp_seid){ + avdtp_connection_t * connection = get_avdtp_connection_for_con_handle(con_handle); + if (!connection){ + printf("avdtp_sink_get_capabilities: no connection for handle 0x%02x found\n", con_handle); + return; + } + if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; + if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return; + connection->initiator_transaction_label++; + connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CAPABILITIES; + connection->acp_seid = acp_seid; + avdtp_sink_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); +} + + +void avdtp_sink_get_all_capabilities(uint16_t con_handle, uint8_t acp_seid){ + avdtp_connection_t * connection = get_avdtp_connection_for_con_handle(con_handle); + if (!connection){ + printf("avdtp_sink_get_all_capabilities: no connection for handle 0x%02x found\n", con_handle); + return; + } + if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; + if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return; + connection->initiator_transaction_label++; + connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_ALL_CAPABILITIES; + connection->acp_seid = acp_seid; + avdtp_sink_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); +} + +void avdtp_sink_get_configuration(uint16_t con_handle, uint8_t acp_seid){ + avdtp_connection_t * connection = get_avdtp_connection_for_con_handle(con_handle); + if (!connection){ + printf("avdtp_sink_get_configuration: no connection for handle 0x%02x found\n", con_handle); + return; + } + if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; + if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return; + connection->initiator_transaction_label++; + connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CONFIGURATION; + connection->acp_seid = acp_seid; + avdtp_sink_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); +} + +void avdtp_sink_set_configuration(uint16_t con_handle, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ + avdtp_connection_t * connection = get_avdtp_connection_for_con_handle(con_handle); + if (!connection){ + printf("avdtp_sink_set_configuration: no connection for handle 0x%02x found\n", con_handle); + return; + } + if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; + if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return; + + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_for_seid(int_seid); + if (!stream_endpoint) { + printf("avdtp_sink_set_configuration: no initiator stream endpoint for seid %d\n", int_seid); + return; + } + printf("avdtp_sink_set_configuration int seid %d, acp seid %d\n", int_seid, acp_seid); + + connection->initiator_transaction_label++; + connection->acp_seid = acp_seid; + connection->int_seid = int_seid; + connection->remote_capabilities_bitmap = configured_services_bitmap; + connection->remote_capabilities = configuration; + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_SET_CONFIGURATION; + avdtp_sink_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); +} + +void avdtp_sink_reconfigure(uint16_t con_handle, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ + avdtp_connection_t * connection = get_avdtp_connection_for_con_handle(con_handle); + if (!connection){ + printf("avdtp_sink_reconfigure: no connection for handle 0x%02x found\n", con_handle); + return; + } + //TODO: if opened only app capabilities, enable reconfigure for not opened + if (connection->state < AVDTP_SIGNALING_CONNECTION_OPENED) return; + if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return; + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_associated_with_acp_seid(acp_seid); + if (!stream_endpoint) return; + if (stream_endpoint->remote_sep_index == 0xFF) return; + connection->initiator_transaction_label++; + connection->acp_seid = acp_seid; + connection->int_seid = stream_endpoint->sep.seid; + connection->remote_capabilities_bitmap = configured_services_bitmap; + connection->remote_capabilities = configuration; + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_RECONFIGURE_STREAM_WITH_SEID; + avdtp_sink_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); +} + +void avdtp_sink_suspend(uint16_t con_handle, uint8_t acp_seid){ + avdtp_connection_t * connection = get_avdtp_connection_for_con_handle(con_handle); + if (!connection){ + printf("avdtp_sink_suspend: no connection for handle 0x%02x found\n", con_handle); + return; + } + if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; + if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return; + avdtp_stream_endpoint_t * stream_endpoint = get_avdtp_stream_endpoint_associated_with_acp_seid(acp_seid); + if (!stream_endpoint) return; + if (stream_endpoint->remote_sep_index == 0xFF) return; + connection->initiator_transaction_label++; + connection->acp_seid = acp_seid; + connection->int_seid = stream_endpoint->sep.seid; + stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_SUSPEND_STREAM_WITH_SEID; + avdtp_sink_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); +} \ No newline at end of file diff --git a/src/classic/avdtp_sink.h b/src/classic/avdtp_sink.h new file mode 100644 index 000000000..6048e7145 --- /dev/null +++ b/src/classic/avdtp_sink.h @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * avdtp_sink.h + * + * Audio/Video Distribution Transport Protocol (AVDTP) Sink + * + * AVDTP Sink is a device that accepts streamed media data. + */ + +#ifndef __AVDTP_SINK_H +#define __AVDTP_SINK_H + +#include +#include "hci.h" +#include "avdtp.h" + +#if defined __cplusplus +extern "C" { +#endif + +/* API_START */ +/** + * @brief AVDTP Sink service record. + * @param service + * @param service_record_handle + * @param supported_features 16-bit bitmap, see AVDTP_SINK_SF_* values in avdtp.h + * @param service_name + * @param service_provider_name + */ +void a2dp_sink_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name); + +/** + * @brief Set up AVDTP Sink device. + */ +void avdtp_sink_init(void); + +// returns avdtp_stream_endpoint_t * +avdtp_stream_endpoint_t * avdtp_sink_create_stream_endpoint(avdtp_sep_type_t sep_type, avdtp_media_type_t media_type); + +void avdtp_sink_register_media_transport_category(uint8_t seid); +void avdtp_sink_register_reporting_category(uint8_t seid); +void avdtp_sink_register_delay_reporting_category(uint8_t seid); +void avdtp_sink_register_recovery_category(uint8_t seid, uint8_t maximum_recovery_window_size, uint8_t maximum_number_media_packets); +void avdtp_sink_register_header_compression_category(uint8_t seid, uint8_t back_ch, uint8_t media, uint8_t recovery); +void avdtp_sink_register_multiplexing_category(uint8_t seid, uint8_t fragmentation); + +void avdtp_sink_register_media_codec_category(uint8_t seid, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, const uint8_t * media_codec_info, uint16_t media_codec_info_len); +void avdtp_sink_register_content_protection_category(uint8_t seid, uint16_t cp_type, const uint8_t * cp_type_value, uint8_t cp_type_value_len); + +/** + * @brief Register callback for the AVDTP Sink client. + * @param callback + */ +void avdtp_sink_register_packet_handler(btstack_packet_handler_t callback); + +/** + * @brief Connect to device with a bluetooth address. (and perform configuration?) + * @param bd_addr + */ +void avdtp_sink_connect(bd_addr_t bd_addr); + +void avdtp_sink_register_media_handler(void (*callback)(avdtp_stream_endpoint_t * stream_endpoint, uint8_t *packet, uint16_t size)); +/** + * @brief Disconnect from device with connection handle. + * @param con_handle + */ +void avdtp_sink_disconnect(uint16_t con_handle); + +/** + * @brief Discover stream endpoints + * @param con_handle + */ +void avdtp_sink_discover_stream_endpoints(uint16_t con_handle); + +/** + * @brief Get capabilities + * @param con_handle + */ +void avdtp_sink_get_capabilities(uint16_t con_handle, uint8_t acp_seid); + +/** + * @brief Get all capabilities + * @param con_handle + */ +void avdtp_sink_get_all_capabilities(uint16_t con_handle, uint8_t acp_seid); + +/** + * @brief Set configuration + * @param con_handle + */ +void avdtp_sink_set_configuration(uint16_t con_handle, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); + +/** + * @brief Reconfigure stream + * @param con_handle + * @param seid + */ +void avdtp_sink_reconfigure(uint16_t con_handle, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); + +/** + * @brief Get configuration + * @param con_handle + */ +void avdtp_sink_get_configuration(uint16_t con_handle, uint8_t acp_seid); + +/** + * @brief Suspend stream + * @param con_handle + * @param seid + */ +void avdtp_sink_suspend(uint16_t con_handle, uint8_t acp_seid); + + +/** + * @brief Open stream + * @param con_handle + * @param seid + */ +void avdtp_sink_open_stream(uint16_t con_handle, uint8_t acp_seid); + +/** + * @brief Start stream + * @param con_handle + * @param seid + */ +void avdtp_sink_start_stream(uint16_t con_handle, uint8_t acp_seid); + +/** + * @brief Start stream + * @param con_handle + * @param seid + */ +void avdtp_sink_abort_stream(uint16_t con_handle, uint8_t acp_seid); + +/** + * @brief Start stream + * @param con_handle + * @param seid + */ +void avdtp_sink_stop_stream(uint16_t con_handle, uint8_t acp_seid); + +/* API_END */ + +extern btstack_packet_handler_t avdtp_sink_callback; +extern btstack_linked_list_t stream_endpoints; + +#if defined __cplusplus +} +#endif + +#endif // __AVDTP_SINK_H \ No newline at end of file diff --git a/src/classic/avdtp_source.c b/src/classic/avdtp_source.c new file mode 100644 index 000000000..6242947bd --- /dev/null +++ b/src/classic/avdtp_source.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + + +#include +#include +#include +#include +#include + +#include "btstack.h" +#include "avdtp.h" +#include "avdtp_source.h" + + +static const char * default_avdtp_sink_service_name = "BTstack AVDTP Sink Service"; +static const char * default_avdtp_sink_service_provider_name = "BTstack AVDTP Sink Service Provider"; + +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){ + uint8_t* attribute; + de_create_sequence(service); + + // 0x0000 "Service Record Handle" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceRecordHandle); + de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle); + + // 0x0001 "Service Class ID List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceClassIDList); + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, AUDIO_SOURCE_GROUP); + } + de_pop_sequence(service, attribute); + + // 0x0004 "Protocol Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ProtocolDescriptorList); + attribute = de_push_sequence(service); + { + uint8_t* l2cpProtocol = de_push_sequence(attribute); + { + de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, SDP_L2CAPProtocol); + de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, PSM_AVDTP); + } + de_pop_sequence(attribute, l2cpProtocol); + + uint8_t* avProtocol = de_push_sequence(attribute); + { + de_add_number(avProtocol, DE_UUID, DE_SIZE_16, PSM_AVDTP); // avProtocol_service + de_add_number(avProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version + } + de_pop_sequence(attribute, avProtocol); + } + de_pop_sequence(service, attribute); + + // 0x0005 "Public Browse Group" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BrowseGroupList); // public browse group + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, SDP_PublicBrowseGroup); + } + de_pop_sequence(service, attribute); + + // 0x0009 "Bluetooth Profile Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BluetoothProfileDescriptorList); + attribute = de_push_sequence(service); + { + uint8_t *a2dProfile = de_push_sequence(attribute); + { + de_add_number(a2dProfile, DE_UUID, DE_SIZE_16, ADVANCED_AUDIO_DISTRIBUTION); + de_add_number(a2dProfile, DE_UINT, DE_SIZE_16, 0x0103); + } + de_pop_sequence(attribute, a2dProfile); + } + de_pop_sequence(service, attribute); + + + // 0x0100 "Service Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); + if (service_name){ + de_add_data(service, DE_STRING, strlen(service_name), (uint8_t *) service_name); + } else { + de_add_data(service, DE_STRING, strlen(default_avdtp_sink_service_name), (uint8_t *) default_avdtp_sink_service_name); + } + + // 0x0100 "Provider Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102); + if (service_provider_name){ + de_add_data(service, DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name); + } else { + de_add_data(service, DE_STRING, strlen(default_avdtp_sink_service_provider_name), (uint8_t *) default_avdtp_sink_service_provider_name); + } + + // 0x0311 "Supported Features" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); + de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); +} + + + + diff --git a/src/classic/avdtp_source.h b/src/classic/avdtp_source.h new file mode 100644 index 000000000..24de81549 --- /dev/null +++ b/src/classic/avdtp_source.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * avdtp_source.h + * + * Audio/Video Distribution Transport Protocol Source + * + * AVDTP Source is a device that streames media data. + */ + +#ifndef __AVDTP_SOURCE_H +#define __AVDTP_SOURCE_H + +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* API_START */ + +/** + * @brief AVDTP Source service record. + * @param service + * @param service_record_handle + * @param supported_features 16-bit bitmap, see AVDTP_SOURCE_SF_* values in avdtp.h + * @param service_name + * @param service_provider_name + */ +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); +/* API_END */ + +#if defined __cplusplus +} +#endif + +#endif // __AVDTP_SOURCE_H \ No newline at end of file diff --git a/src/classic/avdtp_util.c b/src/classic/avdtp_util.c new file mode 100644 index 000000000..18c41aeb0 --- /dev/null +++ b/src/classic/avdtp_util.c @@ -0,0 +1,694 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + + +#include +#include +#include +#include +#include + +#include "btstack.h" +#include "avdtp.h" +#include "avdtp_util.h" + +inline uint8_t avdtp_header(uint8_t tr_label, avdtp_packet_type_t packet_type, avdtp_message_type_t msg_type){ + return (tr_label<<4) | ((uint8_t)packet_type<<2) | (uint8_t)msg_type; +} + +avdtp_stream_endpoint_t * get_avdtp_stream_endpoint_with_seid(uint8_t seid){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &stream_endpoints); + while (btstack_linked_list_iterator_has_next(&it)){ + avdtp_stream_endpoint_t * stream_endpoint = (avdtp_stream_endpoint_t *)btstack_linked_list_iterator_next(&it); + if (stream_endpoint->sep.seid == seid){ + return stream_endpoint; + } + } + return NULL; +} + +avdtp_stream_endpoint_t * get_avdtp_stream_endpoint_associated_with_acp_seid(uint16_t acp_seid){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &stream_endpoints); + while (btstack_linked_list_iterator_has_next(&it)){ + avdtp_stream_endpoint_t * stream_endpoint = (avdtp_stream_endpoint_t *)btstack_linked_list_iterator_next(&it); + if (stream_endpoint->remote_sep_index >= 0 && stream_endpoint->remote_sep_index < MAX_NUM_SEPS){ + if (stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid == acp_seid){ + return stream_endpoint; + } + } + } + return NULL; +} + +int get_bit16(uint16_t bitmap, int position){ + return (bitmap >> position) & 1; +} + +uint8_t store_bit16(uint16_t bitmap, int position, uint8_t value){ + if (value){ + bitmap |= 1 << position; + } else { + bitmap &= ~ (1 << position); + } + return bitmap; +} + +int avdtp_read_signaling_header(avdtp_signaling_packet_t * signaling_header, uint8_t * packet, uint16_t size){ + int pos = 0; + if (size < 2) return pos; + signaling_header->transaction_label = packet[pos] >> 4; + signaling_header->packet_type = (avdtp_packet_type_t)((packet[pos] >> 2) & 0x03); + signaling_header->message_type = (avdtp_message_type_t) (packet[pos] & 0x03); + pos++; + memset(signaling_header->command, 0, sizeof(signaling_header->command)); + switch (signaling_header->packet_type){ + case AVDTP_SINGLE_PACKET: + signaling_header->num_packets = 0; + signaling_header->offset = 0; + signaling_header->size = 0; + break; + case AVDTP_END_PACKET: + signaling_header->num_packets = 0; + break; + case AVDTP_START_PACKET: + signaling_header->num_packets = packet[pos++]; + signaling_header->size = 0; + signaling_header->offset = 0; + break; + case AVDTP_CONTINUE_PACKET: + if (signaling_header->num_packets <= 0) { + printf(" ERROR: wrong num fragmented packets\n"); + break; + } + signaling_header->num_packets--; + break; + } + signaling_header->signal_identifier = packet[pos++] & 0x3f; + return pos; +} + +int avdtp_pack_service_capabilities(uint8_t * buffer, int size, avdtp_capabilities_t caps, avdtp_service_category_t category, uint8_t pack_all_capabilities){ + UNUSED(size); + + int i; + // pos = 0 reserved for length + int pos = 1; + switch(category){ + case AVDTP_MEDIA_TRANSPORT: + case AVDTP_REPORTING: + break; + case AVDTP_DELAY_REPORTING: + if (!pack_all_capabilities) break; + break; + case AVDTP_RECOVERY: + buffer[pos++] = caps.recovery.recovery_type; // 0x01=RFC2733 + buffer[pos++] = caps.recovery.maximum_recovery_window_size; + buffer[pos++] = caps.recovery.maximum_number_media_packets; + break; + case AVDTP_CONTENT_PROTECTION: + buffer[pos++] = caps.content_protection.cp_type_value_len + 2; + big_endian_store_16(buffer, pos, caps.content_protection.cp_type); + pos += 2; + memcpy(buffer+pos, caps.content_protection.cp_type_value, caps.content_protection.cp_type_value_len); + break; + case AVDTP_HEADER_COMPRESSION: + buffer[pos++] = (caps.header_compression.back_ch << 7) | (caps.header_compression.media << 6) | (caps.header_compression.recovery << 5); + break; + case AVDTP_MULTIPLEXING: + buffer[pos++] = caps.multiplexing_mode.fragmentation << 7; + for (i=0; ierror_code = 0; + + if (category == AVDTP_SERVICE_CATEGORY_INVALID_0 || + (category == AVDTP_SERVICE_CATEGORY_INVALID_FF && connection->signaling_packet.signal_identifier == AVDTP_SI_RECONFIGURE)){ + printf(" ERROR: BAD SERVICE CATEGORY %d\n", category); + connection->reject_service_category = category; + connection->error_code = BAD_SERV_CATEGORY; + return 1; + } + + if (connection->signaling_packet.signal_identifier == AVDTP_SI_RECONFIGURE){ + if (category != AVDTP_CONTENT_PROTECTION && category != AVDTP_MEDIA_CODEC){ + printf(" ERROR: REJECT CATEGORY, INVALID_CAPABILITIES\n"); + connection->reject_service_category = category; + connection->error_code = INVALID_CAPABILITIES; + return 1; + } + } + + switch(category){ + case AVDTP_MEDIA_TRANSPORT: + if (cap_len != 0){ + printf(" ERROR: REJECT CATEGORY, BAD_MEDIA_TRANSPORT\n"); + connection->reject_service_category = category; + connection->error_code = BAD_MEDIA_TRANSPORT_FORMAT; + return 1; + } + break; + case AVDTP_REPORTING: + case AVDTP_DELAY_REPORTING: + if (cap_len != 0){ + printf(" ERROR: REJECT CATEGORY, BAD_LENGTH\n"); + connection->reject_service_category = category; + connection->error_code = BAD_LENGTH; + return 1; + } + break; + case AVDTP_RECOVERY: + if (cap_len < 3){ + printf(" ERROR: REJECT CATEGORY, BAD_MEDIA_TRANSPORT\n"); + connection->reject_service_category = category; + connection->error_code = BAD_RECOVERY_FORMAT; + return 1; + } + break; + case AVDTP_CONTENT_PROTECTION: + if (cap_len < 2){ + printf(" ERROR: REJECT CATEGORY, BAD_CP_FORMAT\n"); + connection->reject_service_category = category; + connection->error_code = BAD_CP_FORMAT; + return 1; + } + break; + case AVDTP_HEADER_COMPRESSION: + break; + case AVDTP_MULTIPLEXING: + break; + case AVDTP_MEDIA_CODEC: + break; + default: + break; + } + return 0; +} + +uint16_t avdtp_unpack_service_capabilities(avdtp_connection_t * connection, avdtp_capabilities_t * caps, uint8_t * packet, uint16_t size){ + if (size == 0) return 0; + + uint16_t registered_service_categories = 0; + int pos = 0; + int i; + avdtp_service_category_t category = (avdtp_service_category_t)packet[pos++]; + uint8_t cap_len = packet[pos++]; + + if (avdtp_unpack_service_capabilities_has_errors(connection, category, cap_len)) return 0; + int processed_cap_len = 0; + int rfa = 0; + //printf(" size %d, cat size %d\n", size, cap_len); + + while (pos < size){ + if (cap_len > size - pos){ + connection->reject_service_category = category; + connection->error_code = BAD_LENGTH; + return 0; + } + rfa = 0; + processed_cap_len = pos; + switch(category){ + case AVDTP_RECOVERY: + caps->recovery.recovery_type = packet[pos++]; + caps->recovery.maximum_recovery_window_size = packet[pos++]; + caps->recovery.maximum_number_media_packets = packet[pos++]; + break; + case AVDTP_CONTENT_PROTECTION: + caps->content_protection.cp_type = big_endian_read_16(packet, pos); + pos+=2; + + caps->content_protection.cp_type_value_len = cap_len - 2; + pos += caps->content_protection.cp_type_value_len; + + // connection->reject_service_category = category; + // connection->error_code = UNSUPPORTED_CONFIGURATION; + // support for content protection goes here + break; + + case AVDTP_HEADER_COMPRESSION: + caps->header_compression.back_ch = packet[pos] >> 7; + caps->header_compression.media = packet[pos] >> 6; + caps->header_compression.recovery = packet[pos] >> 5; + pos++; + break; + case AVDTP_MULTIPLEXING: + caps->multiplexing_mode.fragmentation = packet[pos++] >> 7; + // read [tsid, tcid] for media, reporting. recovery respectively + caps->multiplexing_mode.transport_identifiers_num = 3; + for (i=0; imultiplexing_mode.transport_identifiers_num; i++){ + caps->multiplexing_mode.transport_session_identifiers[i] = packet[pos++] >> 7; + caps->multiplexing_mode.tcid[i] = packet[pos++] >> 7; + } + break; + case AVDTP_MEDIA_CODEC: + caps->media_codec.media_type = packet[pos++] >> 4; + caps->media_codec.media_codec_type = packet[pos++]; + caps->media_codec.media_codec_information_len = cap_len - 2; + caps->media_codec.media_codec_information = &packet[pos]; + pos += caps->media_codec.media_codec_information_len; + break; + case AVDTP_MEDIA_TRANSPORT: + case AVDTP_REPORTING: + case AVDTP_DELAY_REPORTING: + pos += cap_len; + break; + default: + pos += cap_len; + rfa = 1; + break; + } + processed_cap_len = pos - processed_cap_len; + + if (cap_len == processed_cap_len){ + if (!rfa) { + registered_service_categories = store_bit16(registered_service_categories, category, 1); + } + if (pos < size-2){ + //int old_pos = pos; + category = (avdtp_service_category_t)packet[pos++]; + cap_len = packet[pos++]; + if (avdtp_unpack_service_capabilities_has_errors(connection, category, cap_len)) return 0; + //printf("category %d, pos %d + 2 + %d -> %d\n", category, old_pos, cap_len, pos + cap_len); + //printf_hexdump(packet+old_pos, size-old_pos); + } + } + } + return registered_service_categories; +} + +void avdtp_prepare_capabilities(avdtp_signaling_packet_t * signaling_packet, uint8_t transaction_label, uint16_t registered_service_categories, avdtp_capabilities_t capabilities, uint8_t identifier){ + if (signaling_packet->offset) return; + uint8_t pack_all_capabilities = 1; + signaling_packet->message_type = AVDTP_RESPONSE_ACCEPT_MSG; + signaling_packet->size = 0; + int i; + signaling_packet->command[signaling_packet->size++] = signaling_packet->acp_seid << 2; + + switch (identifier) { + case AVDTP_SI_GET_CAPABILITIES: + pack_all_capabilities = 0; + break; + case AVDTP_SI_GET_ALL_CAPABILITIES: + pack_all_capabilities = 1; + break; + case AVDTP_SI_SET_CONFIGURATION: + signaling_packet->command[signaling_packet->size++] = signaling_packet->int_seid << 2; + signaling_packet->message_type = AVDTP_CMD_MSG; + break; + case AVDTP_SI_RECONFIGURE: + signaling_packet->message_type = AVDTP_CMD_MSG; + break; + default: + log_error("avdtp_prepare_capabilities wrong identifier %d", identifier); + break; + } + + for (i = 1; i < 9; i++){ + if (get_bit16(registered_service_categories, i)){ + // service category + signaling_packet->command[signaling_packet->size++] = i; + signaling_packet->size += avdtp_pack_service_capabilities(signaling_packet->command+signaling_packet->size, sizeof(signaling_packet->command)-signaling_packet->size, capabilities, (avdtp_service_category_t)i, pack_all_capabilities); + } + } + // signaling_packet->command[signaling_packet->size++] = 0x04; + // signaling_packet->command[signaling_packet->size++] = 0x02; + // signaling_packet->command[signaling_packet->size++] = 0x02; + // signaling_packet->command[signaling_packet->size++] = 0x00; + + signaling_packet->signal_identifier = identifier; + signaling_packet->transaction_label = transaction_label; +} + +int avdtp_signaling_create_fragment(uint16_t cid, avdtp_signaling_packet_t * signaling_packet, uint8_t * out_buffer) { + int mtu = l2cap_get_remote_mtu_for_local_cid(cid); + // hack for test + // int mtu = 6; + int data_len = 0; + + uint16_t offset = signaling_packet->offset; + uint16_t pos = 1; + // printf(" avdtp_signaling_create_fragment offset %d, packet type %d\n", signaling_packet->offset, signaling_packet->packet_type); + + if (offset == 0){ + if (signaling_packet->size <= mtu - 2){ + // printf(" AVDTP_SINGLE_PACKET\n"); + signaling_packet->packet_type = AVDTP_SINGLE_PACKET; + out_buffer[pos++] = signaling_packet->signal_identifier; + data_len = signaling_packet->size; + } else { + signaling_packet->packet_type = AVDTP_START_PACKET; + out_buffer[pos++] = (mtu + signaling_packet->size)/ (mtu-1); + out_buffer[pos++] = signaling_packet->signal_identifier; + data_len = mtu - 3; + signaling_packet->offset = data_len; + // printf(" AVDTP_START_PACKET len %d, offset %d\n", signaling_packet->size, signaling_packet->offset); + } + } else { + int remaining_bytes = signaling_packet->size - offset; + if (remaining_bytes <= mtu - 1){ + //signaling_packet->fragmentation = 1; + signaling_packet->packet_type = AVDTP_END_PACKET; + data_len = remaining_bytes; + signaling_packet->offset = 0; + // printf(" AVDTP_END_PACKET len %d, offset %d\n", signaling_packet->size, signaling_packet->offset); + } else{ + signaling_packet->packet_type = AVDTP_CONTINUE_PACKET; + data_len = mtu - 1; + signaling_packet->offset += data_len; + // printf(" AVDTP_CONTINUE_PACKET len %d, offset %d\n", signaling_packet->size, signaling_packet->offset); + } + } + out_buffer[0] = avdtp_header(signaling_packet->transaction_label, signaling_packet->packet_type, signaling_packet->message_type); + memcpy(out_buffer+pos, signaling_packet->command + offset, data_len); + pos += data_len; + return pos; +} + + +void avdtp_signaling_emit_connection_established(btstack_packet_handler_t callback, uint16_t con_handle, bd_addr_t addr, uint8_t status){ + if (!callback) return; + uint8_t event[12]; + int pos = 0; + event[pos++] = HCI_EVENT_AVDTP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED; + little_endian_store_16(event, pos, con_handle); + pos += 2; + reverse_bd_addr(addr,&event[pos]); + pos += 6; + event[pos++] = status; + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +void avdtp_signaling_emit_sep(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_sep_t sep){ + if (!callback) return; + uint8_t event[9]; + int pos = 0; + event[pos++] = HCI_EVENT_AVDTP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = AVDTP_SUBEVENT_SIGNALING_SEP_FOUND; + little_endian_store_16(event, pos, con_handle); + pos += 2; + event[pos++] = sep.seid; + event[pos++] = sep.in_use; + event[pos++] = sep.media_type; + event[pos++] = sep.type; + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +void avdtp_signaling_emit_accept(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_signal_identifier_t identifier, uint8_t status){ + if (!callback) return; + uint8_t event[7]; + int pos = 0; + event[pos++] = HCI_EVENT_AVDTP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = AVDTP_SUBEVENT_SIGNALING_ACCEPT; + little_endian_store_16(event, pos, con_handle); + pos += 2; + event[pos++] = identifier; + event[pos++] = status; + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +void avdtp_signaling_emit_reject(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_signal_identifier_t identifier){ + if (!callback) return; + uint8_t event[6]; + int pos = 0; + event[pos++] = HCI_EVENT_AVDTP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = AVDTP_SUBEVENT_SIGNALING_REJECT; + little_endian_store_16(event, pos, con_handle); + pos += 2; + event[pos++] = identifier; + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +void avdtp_signaling_emit_general_reject(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_signal_identifier_t identifier){ + if (!callback) return; + uint8_t event[6]; + int pos = 0; + event[pos++] = HCI_EVENT_AVDTP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = AVDTP_SUBEVENT_SIGNALING_GENERAL_REJECT; + little_endian_store_16(event, pos, con_handle); + pos += 2; + event[pos++] = identifier; + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +void avdtp_signaling_emit_media_codec_sbc_capability(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec){ + if (!callback) return; + uint8_t event[13]; + int pos = 0; + event[pos++] = HCI_EVENT_AVDTP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY; + little_endian_store_16(event, pos, con_handle); + pos += 2; + event[pos++] = media_codec.media_type; + event[pos++] = media_codec.media_codec_information[0] >> 4; + event[pos++] = media_codec.media_codec_information[0] & 0x0F; + event[pos++] = media_codec.media_codec_information[1] >> 4; + event[pos++] = (media_codec.media_codec_information[1] & 0x0F) >> 2; + event[pos++] = media_codec.media_codec_information[1] & 0x03; + event[pos++] = media_codec.media_codec_information[2]; + event[pos++] = media_codec.media_codec_information[3]; + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +void avdtp_signaling_emit_media_codec_other_capability(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec){ + if (!callback) return; + uint8_t event[109]; + int pos = 0; + event[pos++] = HCI_EVENT_AVDTP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY; + little_endian_store_16(event, pos, con_handle); + pos += 2; + event[pos++] = media_codec.media_type; + little_endian_store_16(event, pos, media_codec.media_codec_type); + pos += 2; + little_endian_store_16(event, pos, media_codec.media_codec_information_len); + pos += 2; + if (media_codec.media_codec_information_len < 100){ + memcpy(event+pos, media_codec.media_codec_information, media_codec.media_codec_information_len); + } else { + memcpy(event+pos, media_codec.media_codec_information, 100); + } + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +static inline void avdtp_signaling_emit_media_codec_sbc(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec, uint8_t reconfigure){ + if (!callback) return; + uint8_t event[14+2]; + int pos = 0; + event[pos++] = HCI_EVENT_AVDTP_META; + event[pos++] = sizeof(event) - 2; + + event[pos++] = AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION; + little_endian_store_16(event, pos, con_handle); + pos += 2; + event[pos++] = reconfigure; + + uint8_t num_channels = 0; + uint16_t sampling_frequency = 0; + uint8_t subbands = 0; + uint8_t block_length = 0; + + uint8_t sampling_frequency_bitmap = media_codec.media_codec_information[0] >> 4; + uint8_t channel_mode_bitmap = media_codec.media_codec_information[0] & 0x0F; + uint8_t block_length_bitmap = media_codec.media_codec_information[1] >> 4; + uint8_t subbands_bitmap = (media_codec.media_codec_information[1] & 0x0F) >> 2; + + if (channel_mode_bitmap & AVDTP_SBC_MONO){ + num_channels = 1; + } + if ( (channel_mode_bitmap & AVDTP_SBC_JOINT_STEREO) || + (channel_mode_bitmap & AVDTP_SBC_STEREO) || + (channel_mode_bitmap & AVDTP_SBC_DUAL_CHANNEL) ){ + num_channels = 2; + } + + if (sampling_frequency_bitmap & AVDTP_SBC_16000){ + sampling_frequency = 16000; + } + if (sampling_frequency_bitmap & AVDTP_SBC_32000){ + sampling_frequency = 32000; + } + if (sampling_frequency_bitmap & AVDTP_SBC_44100){ + sampling_frequency = 44100; + } + if (sampling_frequency_bitmap & AVDTP_SBC_48000){ + sampling_frequency = 48000; + } + + if (subbands_bitmap & AVDTP_SBC_SUBBANDS_4){ + subbands = 4; + } + if (subbands_bitmap & AVDTP_SBC_SUBBANDS_8){ + subbands = 8; + } + + if (block_length_bitmap & AVDTP_SBC_BLOCK_LENGTH_4){ + block_length = 4; + } + if (block_length_bitmap & AVDTP_SBC_BLOCK_LENGTH_8){ + block_length = 8; + } + if (block_length_bitmap & AVDTP_SBC_BLOCK_LENGTH_12){ + block_length = 12; + } + if (block_length_bitmap & AVDTP_SBC_BLOCK_LENGTH_16){ + block_length = 16; + } + + event[pos++] = media_codec.media_type; + little_endian_store_16(event, pos, sampling_frequency); + pos += 2; + + event[pos++] = channel_mode_bitmap; + event[pos++] = num_channels; + event[pos++] = block_length; + event[pos++] = subbands; + event[pos++] = media_codec.media_codec_information[1] & 0x03; + event[pos++] = media_codec.media_codec_information[2]; + event[pos++] = media_codec.media_codec_information[3]; + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +void avdtp_signaling_emit_media_codec_sbc_configuration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec){ + if (!callback) return; + avdtp_signaling_emit_media_codec_sbc(callback, con_handle, media_codec, 0); +} + +void avdtp_signaling_emit_media_codec_sbc_reconfiguration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec){ + if (!callback) return; + avdtp_signaling_emit_media_codec_sbc(callback, con_handle, media_codec, 1); +} + +static inline void avdtp_signaling_emit_media_codec_other(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec, uint8_t reconfigure){ + uint8_t event[110]; + int pos = 0; + event[pos++] = HCI_EVENT_AVDTP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION; + little_endian_store_16(event, pos, con_handle); + pos += 2; + + event[pos++] = reconfigure; + + event[pos++] = media_codec.media_type; + little_endian_store_16(event, pos, media_codec.media_codec_type); + pos += 2; + little_endian_store_16(event, pos, media_codec.media_codec_information_len); + pos += 2; + + if (media_codec.media_codec_information_len < 100){ + memcpy(event+pos, media_codec.media_codec_information, media_codec.media_codec_information_len); + } else { + memcpy(event+pos, media_codec.media_codec_information, 100); + } + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +void avdtp_signaling_emit_media_codec_other_configuration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec){ + if (!callback) return; + avdtp_signaling_emit_media_codec_other(callback, con_handle, media_codec, 0); +} + +void avdtp_signaling_emit_media_codec_other_reconfiguration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec){ + if (!callback) return; + avdtp_signaling_emit_media_codec_other(callback, con_handle, media_codec, 1); +} + + +void avdtp_sink_request_can_send_now_acceptor(avdtp_connection_t * connection, uint16_t l2cap_cid){ + connection->wait_to_send_acceptor = 1; + l2cap_request_can_send_now_event(l2cap_cid); +} +void avdtp_sink_request_can_send_now_initiator(avdtp_connection_t * connection, uint16_t l2cap_cid){ + connection->wait_to_send_initiator = 1; + l2cap_request_can_send_now_event(l2cap_cid); +} +void avdtp_sink_request_can_send_now_self(avdtp_connection_t * connection, uint16_t l2cap_cid){ + connection->wait_to_send_self = 1; + l2cap_request_can_send_now_event(l2cap_cid); +} + +avdtp_stream_endpoint_t * get_avdtp_stream_endpoint_for_seid(uint16_t seid){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &stream_endpoints); + while (btstack_linked_list_iterator_has_next(&it)){ + avdtp_stream_endpoint_t * stream_endpoint = (avdtp_stream_endpoint_t *)btstack_linked_list_iterator_next(&it); + if (stream_endpoint->sep.seid == seid){ + return stream_endpoint; + } + } + return NULL; +} + +uint8_t avdtp_get_index_of_remote_stream_endpoint_with_seid(avdtp_stream_endpoint_t * stream_endpoint, uint16_t seid){ + if (stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid == seid){ + return stream_endpoint->remote_sep_index; + } + int i; + for (i=0; i < stream_endpoint->remote_seps_num; i++){ + if (stream_endpoint->remote_seps[i].seid == seid){ + return i; + } + } + return 0xFF; +} diff --git a/src/classic/avdtp_util.h b/src/classic/avdtp_util.h new file mode 100644 index 000000000..55bf723d7 --- /dev/null +++ b/src/classic/avdtp_util.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * avdtp_util.h + * + * Audio/Video Distribution Transport Protocol - Utils + * + */ + +#ifndef __AVDTP_UITL_H +#define __AVDTP_UITL_H + +#include +#include "avdtp.h" + +#if defined __cplusplus +extern "C" { +#endif + +avdtp_stream_endpoint_t * get_avdtp_stream_endpoint_with_seid(uint8_t seid); +avdtp_stream_endpoint_t * get_avdtp_stream_endpoint_associated_with_acp_seid(uint16_t acp_seid); + +uint8_t avdtp_header(uint8_t tr_label, avdtp_packet_type_t packet_type, avdtp_message_type_t msg_type); +int avdtp_read_signaling_header(avdtp_signaling_packet_t * signaling_header, uint8_t * packet, uint16_t size); + +uint8_t store_bit16(uint16_t bitmap, int position, uint8_t value); +int get_bit16(uint16_t bitmap, int position); + +int avdtp_pack_service_capabilities(uint8_t * buffer, int size, avdtp_capabilities_t caps, avdtp_service_category_t category, uint8_t pack_all_capabilities); +uint16_t avdtp_unpack_service_capabilities(avdtp_connection_t * connection, avdtp_capabilities_t * caps, uint8_t * packet, uint16_t size); + +void avdtp_prepare_capabilities(avdtp_signaling_packet_t * signaling_packet, uint8_t transaction_label, uint16_t registered_service_categories, avdtp_capabilities_t configuration, uint8_t identifier); +int avdtp_signaling_create_fragment(uint16_t cid, avdtp_signaling_packet_t * signaling_packet, uint8_t * out_buffer); + +void avdtp_signaling_emit_connection_established(btstack_packet_handler_t callback, uint16_t con_handle, bd_addr_t addr, uint8_t status); +void avdtp_signaling_emit_sep(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_sep_t sep); +void avdtp_signaling_emit_accept(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_signal_identifier_t identifier, uint8_t status); +void avdtp_signaling_emit_general_reject(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_signal_identifier_t identifier); +void avdtp_signaling_emit_reject(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_signal_identifier_t identifier); + + +void avdtp_signaling_emit_media_codec_sbc_capability(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec); +void avdtp_signaling_emit_media_codec_other_capability(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec); +void avdtp_signaling_emit_media_codec_sbc_configuration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec); +void avdtp_signaling_emit_media_codec_other_configuration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec); +void avdtp_signaling_emit_media_codec_sbc_reconfiguration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec); +void avdtp_signaling_emit_media_codec_other_reconfiguration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec); + +void avdtp_sink_request_can_send_now_acceptor(avdtp_connection_t * connection, uint16_t l2cap_cid); +void avdtp_sink_request_can_send_now_initiator(avdtp_connection_t * connection, uint16_t l2cap_cid); +void avdtp_sink_request_can_send_now_self(avdtp_connection_t * connection, uint16_t l2cap_cid); + +uint8_t avdtp_get_index_of_remote_stream_endpoint_with_seid(avdtp_stream_endpoint_t * stream_endpoint, uint16_t acp_seid); + +avdtp_stream_endpoint_t * get_avdtp_stream_endpoint_for_seid(uint16_t seid); + +#if defined __cplusplus +} +#endif + +#endif // __AVDTP_UITL_H \ No newline at end of file diff --git a/test/avdtp/Makefile b/test/avdtp/Makefile index da9524f84..99ff3882e 100644 --- a/test/avdtp/Makefile +++ b/test/avdtp/Makefile @@ -62,6 +62,13 @@ SBC_ENCODER += \ ${BTSTACK_ROOT}/src/classic/btstack_sbc_bludroid.c \ ${BTSTACK_ROOT}/src/classic/hfp_msbc.c \ +AVDTP_SINK += \ + avdtp_util.c \ + avdtp_initiator.c \ + avdtp_acceptor.c \ + avdtp_source.c \ + avdtp_sink.c \ + btstack_ring_buffer.c \ AVDTP_TESTS = avdtp_test portaudio_test @@ -69,10 +76,11 @@ CORE_OBJ = $(CORE:.c=.o) COMMON_OBJ = $(COMMON:.c=.o) SBC_DECODER_OBJ = $(SBC_DECODER:.c=.o) SBC_ENCODER_OBJ = $(SBC_ENCODER:.c=.o) +AVDTP_SINK_OBJ = $(AVDTP_SINK:.c=.o) all: ${AVDTP_TESTS} -avdtp_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} btstack_ring_buffer.o avdtp_util.o avdtp_sink.o avdtp_acceptor.o avdtp_initiator.o avdtp_test.o +avdtp_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_SINK_OBJ} avdtp_test.o ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ portaudio_test: btstack_util.o hci_dump.o wav_util.o btstack_ring_buffer.o portaudio_test.c