1be32e7f1SMilanka Ringwald /* 2be32e7f1SMilanka Ringwald * Copyright (C) 2016 BlueKitchen GmbH 3be32e7f1SMilanka Ringwald * 4be32e7f1SMilanka Ringwald * Redistribution and use in source and binary forms, with or without 5be32e7f1SMilanka Ringwald * modification, are permitted provided that the following conditions 6be32e7f1SMilanka Ringwald * are met: 7be32e7f1SMilanka Ringwald * 8be32e7f1SMilanka Ringwald * 1. Redistributions of source code must retain the above copyright 9be32e7f1SMilanka Ringwald * notice, this list of conditions and the following disclaimer. 10be32e7f1SMilanka Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11be32e7f1SMilanka Ringwald * notice, this list of conditions and the following disclaimer in the 12be32e7f1SMilanka Ringwald * documentation and/or other materials provided with the distribution. 13be32e7f1SMilanka Ringwald * 3. Neither the name of the copyright holders nor the names of 14be32e7f1SMilanka Ringwald * contributors may be used to endorse or promote products derived 15be32e7f1SMilanka Ringwald * from this software without specific prior written permission. 16be32e7f1SMilanka Ringwald * 4. Any redistribution, use, or modification is done solely for 17be32e7f1SMilanka Ringwald * personal benefit and not for any commercial purpose or for 18be32e7f1SMilanka Ringwald * monetary gain. 19be32e7f1SMilanka Ringwald * 20be32e7f1SMilanka Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21be32e7f1SMilanka Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22be32e7f1SMilanka Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23be32e7f1SMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24be32e7f1SMilanka Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25be32e7f1SMilanka Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26be32e7f1SMilanka Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27be32e7f1SMilanka Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28be32e7f1SMilanka Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29be32e7f1SMilanka Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30be32e7f1SMilanka Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31be32e7f1SMilanka Ringwald * SUCH DAMAGE. 32be32e7f1SMilanka Ringwald * 33be32e7f1SMilanka Ringwald * Please inquire about commercial licensing options at 34be32e7f1SMilanka Ringwald * [email protected] 35be32e7f1SMilanka Ringwald * 36be32e7f1SMilanka Ringwald */ 37be32e7f1SMilanka Ringwald 38ab2c6ae4SMatthias Ringwald #define __BTSTACK_FILE__ "avrcp.c" 39ab2c6ae4SMatthias Ringwald 40be32e7f1SMilanka Ringwald #include <stdint.h> 41be32e7f1SMilanka Ringwald #include <stdio.h> 42be32e7f1SMilanka Ringwald #include <stdlib.h> 43be32e7f1SMilanka Ringwald #include <string.h> 44be32e7f1SMilanka Ringwald 45be32e7f1SMilanka Ringwald #include "btstack.h" 46be32e7f1SMilanka Ringwald #include "classic/avrcp.h" 47be32e7f1SMilanka Ringwald 48235946f1SMatthias Ringwald #define PSM_AVCTP BLUETOOTH_PROTOCOL_AVCTP 49a0f524f0SMatthias Ringwald #define PSM_AVCTP_BROWSING 0x001b 50be32e7f1SMilanka Ringwald 51be32e7f1SMilanka Ringwald /* 52be32e7f1SMilanka Ringwald Category 1: Player/Recorder 53be32e7f1SMilanka Ringwald Category 2: Monitor/Amplifier 54be32e7f1SMilanka Ringwald Category 3: Tuner 55be32e7f1SMilanka Ringwald Category 4: Menu 56be32e7f1SMilanka Ringwald */ 57be32e7f1SMilanka Ringwald 58be32e7f1SMilanka Ringwald /* controller supported features 59be32e7f1SMilanka Ringwald Bit 0 = Category 1 60be32e7f1SMilanka Ringwald Bit 1 = Category 2 61be32e7f1SMilanka Ringwald Bit 2 = Category 3 62be32e7f1SMilanka Ringwald Bit 3 = Category 4 63be32e7f1SMilanka Ringwald Bit 4-5 = RFA 64be32e7f1SMilanka Ringwald Bit 6 = Supports browsing 65be32e7f1SMilanka Ringwald Bit 7-15 = RFA 66be32e7f1SMilanka Ringwald The bits for supported categories are set to 1. Others are set to 0. 67be32e7f1SMilanka Ringwald */ 68be32e7f1SMilanka Ringwald 69be32e7f1SMilanka Ringwald /* target supported features 70be32e7f1SMilanka Ringwald Bit 0 = Category 1 71be32e7f1SMilanka Ringwald Bit 1 = Category 2 72be32e7f1SMilanka Ringwald Bit 2 = Category 3 73be32e7f1SMilanka Ringwald Bit 3 = Category 4 74be32e7f1SMilanka Ringwald Bit 4 = Player Application Settings. Bit 0 should be set for this bit to be set. 75be32e7f1SMilanka Ringwald Bit 5 = Group Navigation. Bit 0 should be set for this bit to be set. 76be32e7f1SMilanka Ringwald Bit 6 = Supports browsing*4 77be32e7f1SMilanka Ringwald Bit 7 = Supports multiple media player applications 78be32e7f1SMilanka Ringwald Bit 8-15 = RFA 79be32e7f1SMilanka Ringwald The bits for supported categories are set to 1. Others are set to 0. 80be32e7f1SMilanka Ringwald */ 81be32e7f1SMilanka Ringwald 826086246cSMilanka Ringwald static uint16_t avrcp_cid_counter = 1; 836086246cSMilanka Ringwald 84be32e7f1SMilanka Ringwald // TODO: merge with avdtp_packet_type_t 85be32e7f1SMilanka Ringwald typedef enum { 86be32e7f1SMilanka Ringwald AVRCP_SINGLE_PACKET= 0, 87be32e7f1SMilanka Ringwald AVRCP_START_PACKET , 88be32e7f1SMilanka Ringwald AVRCP_CONTINUE_PACKET , 89be32e7f1SMilanka Ringwald AVRCP_END_PACKET 90be32e7f1SMilanka Ringwald } avrcp_packet_type_t; 91be32e7f1SMilanka Ringwald 92be32e7f1SMilanka Ringwald typedef enum { 93be32e7f1SMilanka Ringwald AVRCP_COMMAND_FRAME = 0, 94be32e7f1SMilanka Ringwald AVRCP_RESPONSE_FRAME 95be32e7f1SMilanka Ringwald } avrcp_frame_type_t; 96be32e7f1SMilanka Ringwald 976086246cSMilanka Ringwald static int record_id = -1; 986086246cSMilanka Ringwald static uint8_t attribute_value[1000]; 996086246cSMilanka Ringwald static const unsigned int attribute_value_buffer_size = sizeof(attribute_value); 1006086246cSMilanka Ringwald 101be32e7f1SMilanka Ringwald static const char * default_avrcp_controller_service_name = "BTstack AVRCP Controller Service"; 102be32e7f1SMilanka Ringwald static const char * default_avrcp_controller_service_provider_name = "BTstack AVRCP Controller Service Provider"; 103be32e7f1SMilanka Ringwald static const char * default_avrcp_target_service_name = "BTstack AVRCP Target Service"; 104be32e7f1SMilanka Ringwald static const char * default_avrcp_target_service_provider_name = "BTstack AVRCP Target Service Provider"; 105be32e7f1SMilanka Ringwald 106be32e7f1SMilanka Ringwald static btstack_packet_handler_t avrcp_callback; 107be32e7f1SMilanka Ringwald 1086086246cSMilanka Ringwald static avrcp_context_t avrcp_controller_context; 1096086246cSMilanka Ringwald 110be32e7f1SMilanka Ringwald static const char * avrcp_subunit_type_name[] = { 111be32e7f1SMilanka Ringwald "MONITOR", "AUDIO", "PRINTER", "DISC", "TAPE_RECORDER_PLAYER", "TUNER", 112be32e7f1SMilanka Ringwald "CA", "CAMERA", "RESERVED", "PANEL", "BULLETIN_BOARD", "CAMERA_STORAGE", 113be32e7f1SMilanka Ringwald "VENDOR_UNIQUE", "RESERVED_FOR_ALL_SUBUNIT_TYPES", 114be32e7f1SMilanka Ringwald "EXTENDED_TO_NEXT_BYTE", "UNIT", "ERROR" 115be32e7f1SMilanka Ringwald }; 116be32e7f1SMilanka Ringwald const char * avrcp_subunit2str(uint16_t index){ 117be32e7f1SMilanka Ringwald if (index <= 11) return avrcp_subunit_type_name[index]; 118be32e7f1SMilanka Ringwald if (index >= 0x1C && index <= 0x1F) return avrcp_subunit_type_name[index - 0x10]; 119be32e7f1SMilanka Ringwald return avrcp_subunit_type_name[16]; 120be32e7f1SMilanka Ringwald } 121be32e7f1SMilanka Ringwald 122be32e7f1SMilanka Ringwald static const char * avrcp_event_name[] = { 123be32e7f1SMilanka Ringwald "ERROR", "PLAYBACK_STATUS_CHANGED", 124be32e7f1SMilanka Ringwald "TRACK_CHANGED", "TRACK_REACHED_END", "TRACK_REACHED_START", 125be32e7f1SMilanka Ringwald "PLAYBACK_POS_CHANGED", "BATT_STATUS_CHANGED", "SYSTEM_STATUS_CHANGED", 126be32e7f1SMilanka Ringwald "PLAYER_APPLICATION_SETTING_CHANGED", "NOW_PLAYING_CONTENT_CHANGED", 127be32e7f1SMilanka Ringwald "AVAILABLE_PLAYERS_CHANGED", "ADDRESSED_PLAYER_CHANGED", "UIDS_CHANGED", "VOLUME_CHANGED" 128be32e7f1SMilanka Ringwald }; 129be32e7f1SMilanka Ringwald const char * avrcp_event2str(uint16_t index){ 130be32e7f1SMilanka Ringwald if (index <= 0x0d) return avrcp_event_name[index]; 131be32e7f1SMilanka Ringwald return avrcp_event_name[0]; 132be32e7f1SMilanka Ringwald } 133be32e7f1SMilanka Ringwald 134be32e7f1SMilanka Ringwald static const char * avrcp_operation_name[] = { 135be32e7f1SMilanka Ringwald "NOT SUPPORTED", // 0x3B 136be32e7f1SMilanka Ringwald "SKIP", "NOT SUPPORTED", "NOT SUPPORTED", "NOT SUPPORTED", "NOT SUPPORTED", 137be32e7f1SMilanka Ringwald "VOLUME_UP", "VOLUME_DOWN", "MUTE", "PLAY", "STOP", "PAUSE", "NOT SUPPORTED", 138be32e7f1SMilanka Ringwald "REWIND", "FAST_FORWARD", "NOT SUPPORTED", "FORWARD", "BACKWARD" // 0x4C 139be32e7f1SMilanka Ringwald }; 140be32e7f1SMilanka Ringwald const char * avrcp_operation2str(uint8_t index){ 141be32e7f1SMilanka Ringwald if (index >= 0x3B && index <= 0x4C) return avrcp_operation_name[index - 0x3B]; 142be32e7f1SMilanka Ringwald return avrcp_operation_name[0]; 143be32e7f1SMilanka Ringwald } 144be32e7f1SMilanka Ringwald 145be32e7f1SMilanka Ringwald static const char * avrcp_media_attribute_id_name[] = { 146be32e7f1SMilanka Ringwald "NONE", "TITLE", "ARTIST", "ALBUM", "TRACK", "TOTAL TRACKS", "GENRE", "SONG LENGTH" 147be32e7f1SMilanka Ringwald }; 148be32e7f1SMilanka Ringwald const char * avrcp_attribute2str(uint8_t index){ 149be32e7f1SMilanka Ringwald if (index >= 1 && index <= 7) return avrcp_media_attribute_id_name[index]; 150be32e7f1SMilanka Ringwald return avrcp_media_attribute_id_name[0]; 151be32e7f1SMilanka Ringwald } 152be32e7f1SMilanka Ringwald 153be32e7f1SMilanka Ringwald static const char * avrcp_play_status_name[] = { 154be32e7f1SMilanka Ringwald "STOPPED", "PLAYING", "PAUSED", "FORWARD SEEK", "REVERSE SEEK", 155be32e7f1SMilanka Ringwald "ERROR" // 0xFF 156be32e7f1SMilanka Ringwald }; 157be32e7f1SMilanka Ringwald const char * avrcp_play_status2str(uint8_t index){ 158be32e7f1SMilanka Ringwald if (index >= 1 && index <= 4) return avrcp_play_status_name[index]; 159be32e7f1SMilanka Ringwald return avrcp_play_status_name[5]; 160be32e7f1SMilanka Ringwald } 161be32e7f1SMilanka Ringwald 162be32e7f1SMilanka Ringwald static const char * avrcp_ctype_name[] = { 163be32e7f1SMilanka Ringwald "CONTROL", 164be32e7f1SMilanka Ringwald "STATUS", 165be32e7f1SMilanka Ringwald "SPECIFIC_INQUIRY", 166be32e7f1SMilanka Ringwald "NOTIFY", 167be32e7f1SMilanka Ringwald "GENERAL_INQUIRY", 168be32e7f1SMilanka Ringwald "RESERVED5", 169be32e7f1SMilanka Ringwald "RESERVED6", 170be32e7f1SMilanka Ringwald "RESERVED7", 1719cc1f3ceSMilanka Ringwald "NOT IMPLEMENTED IN REMOTE", 1729cc1f3ceSMilanka Ringwald "ACCEPTED BY REMOTE", 1739cc1f3ceSMilanka Ringwald "REJECTED BY REMOTE", 174be32e7f1SMilanka Ringwald "IN_TRANSITION", 175be32e7f1SMilanka Ringwald "IMPLEMENTED_STABLE", 176be32e7f1SMilanka Ringwald "CHANGED_STABLE", 177be32e7f1SMilanka Ringwald "RESERVED", 178be32e7f1SMilanka Ringwald "INTERIM" 179be32e7f1SMilanka Ringwald }; 180be32e7f1SMilanka Ringwald const char * avrcp_ctype2str(uint8_t index){ 1813982eab9SMatthias Ringwald if (index < sizeof(avrcp_ctype_name)){ 182be32e7f1SMilanka Ringwald return avrcp_ctype_name[index]; 183be32e7f1SMilanka Ringwald } 184be32e7f1SMilanka Ringwald return "NONE"; 185be32e7f1SMilanka Ringwald } 186be32e7f1SMilanka Ringwald 187be32e7f1SMilanka Ringwald static const char * avrcp_shuffle_mode_name[] = { 188be32e7f1SMilanka Ringwald "SHUFFLE OFF", 189be32e7f1SMilanka Ringwald "SHUFFLE ALL TRACKS", 190be32e7f1SMilanka Ringwald "SHUFFLE GROUP" 191be32e7f1SMilanka Ringwald }; 192be32e7f1SMilanka Ringwald 193be32e7f1SMilanka Ringwald const char * avrcp_shuffle2str(uint8_t index){ 194be32e7f1SMilanka Ringwald if (index >= 1 && index <= 3) return avrcp_shuffle_mode_name[index-1]; 195be32e7f1SMilanka Ringwald return "NONE"; 196be32e7f1SMilanka Ringwald } 197be32e7f1SMilanka Ringwald 198be32e7f1SMilanka Ringwald static const char * avrcp_repeat_mode_name[] = { 199be32e7f1SMilanka Ringwald "REPEAT OFF", 200be32e7f1SMilanka Ringwald "REPEAT SINGLE TRACK", 201be32e7f1SMilanka Ringwald "REPEAT ALL TRACKS", 202be32e7f1SMilanka Ringwald "REPEAT GROUP" 203be32e7f1SMilanka Ringwald }; 204be32e7f1SMilanka Ringwald 205be32e7f1SMilanka Ringwald const char * avrcp_repeat2str(uint8_t index){ 206be32e7f1SMilanka Ringwald if (index >= 1 && index <= 4) return avrcp_repeat_mode_name[index-1]; 207be32e7f1SMilanka Ringwald return "NONE"; 208be32e7f1SMilanka Ringwald } 2096086246cSMilanka Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size, avrcp_context_t * context); 2106086246cSMilanka Ringwald 2116086246cSMilanka Ringwald static avrcp_sdp_query_context_t sdp_query_context; 2126086246cSMilanka Ringwald static void avrcp_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 213be32e7f1SMilanka Ringwald 214be32e7f1SMilanka Ringwald static void avrcp_create_sdp_record(uint8_t controller, uint8_t * service, uint32_t service_record_handle, uint8_t browsing, uint16_t supported_features, const char * service_name, const char * service_provider_name){ 215be32e7f1SMilanka Ringwald uint8_t* attribute; 216be32e7f1SMilanka Ringwald de_create_sequence(service); 217be32e7f1SMilanka Ringwald 218be32e7f1SMilanka Ringwald // 0x0000 "Service Record Handle" 219235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE); 220be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle); 221be32e7f1SMilanka Ringwald 222be32e7f1SMilanka Ringwald // 0x0001 "Service Class ID List" 223235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST); 224be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 225be32e7f1SMilanka Ringwald { 226be32e7f1SMilanka Ringwald if (controller){ 2276086246cSMilanka Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL); 2286086246cSMilanka Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_CONTROLLER); 229be32e7f1SMilanka Ringwald } else { 2306086246cSMilanka Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_TARGET); 231be32e7f1SMilanka Ringwald } 232be32e7f1SMilanka Ringwald } 233be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 234be32e7f1SMilanka Ringwald 235be32e7f1SMilanka Ringwald // 0x0004 "Protocol Descriptor List" 236235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST); 237be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 238be32e7f1SMilanka Ringwald { 239be32e7f1SMilanka Ringwald uint8_t* l2cpProtocol = de_push_sequence(attribute); 240be32e7f1SMilanka Ringwald { 241235946f1SMatthias Ringwald de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP); 242235946f1SMatthias Ringwald de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP); 243be32e7f1SMilanka Ringwald } 244be32e7f1SMilanka Ringwald de_pop_sequence(attribute, l2cpProtocol); 245be32e7f1SMilanka Ringwald 246be32e7f1SMilanka Ringwald uint8_t* avctpProtocol = de_push_sequence(attribute); 247be32e7f1SMilanka Ringwald { 248235946f1SMatthias Ringwald de_add_number(avctpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP); // avctpProtocol_service 249be32e7f1SMilanka Ringwald de_add_number(avctpProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version 250be32e7f1SMilanka Ringwald } 251be32e7f1SMilanka Ringwald de_pop_sequence(attribute, avctpProtocol); 252be32e7f1SMilanka Ringwald } 253be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 254be32e7f1SMilanka Ringwald 255be32e7f1SMilanka Ringwald // 0x0005 "Public Browse Group" 256235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BROWSE_GROUP_LIST); // public browse group 257be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 258be32e7f1SMilanka Ringwald { 259235946f1SMatthias Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT); 260be32e7f1SMilanka Ringwald } 261be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 262be32e7f1SMilanka Ringwald 263be32e7f1SMilanka Ringwald // 0x0009 "Bluetooth Profile Descriptor List" 264235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST); 265be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 266be32e7f1SMilanka Ringwald { 267be32e7f1SMilanka Ringwald uint8_t *avrcProfile = de_push_sequence(attribute); 268be32e7f1SMilanka Ringwald { 2696086246cSMilanka Ringwald de_add_number(avrcProfile, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL); 270be32e7f1SMilanka Ringwald de_add_number(avrcProfile, DE_UINT, DE_SIZE_16, 0x0105); 271be32e7f1SMilanka Ringwald } 272be32e7f1SMilanka Ringwald de_pop_sequence(attribute, avrcProfile); 273be32e7f1SMilanka Ringwald } 274be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 275be32e7f1SMilanka Ringwald 276a0f524f0SMatthias Ringwald // 0x000d "Additional Bluetooth Profile Descriptor List" 277a0f524f0SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS); 278a0f524f0SMatthias Ringwald attribute = de_push_sequence(service); 279a0f524f0SMatthias Ringwald if (browsing){ 280a0f524f0SMatthias Ringwald uint8_t* browsing_l2cpProtocol = de_push_sequence(attribute); 281a0f524f0SMatthias Ringwald { 282a0f524f0SMatthias Ringwald de_add_number(browsing_l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP); 283a0f524f0SMatthias Ringwald de_add_number(browsing_l2cpProtocol, DE_UINT, DE_SIZE_16, PSM_AVCTP_BROWSING); 284a0f524f0SMatthias Ringwald } 285a0f524f0SMatthias Ringwald de_pop_sequence(attribute, browsing_l2cpProtocol); 286a0f524f0SMatthias Ringwald 287a0f524f0SMatthias Ringwald uint8_t* browsing_avctpProtocol = de_push_sequence(attribute); 288a0f524f0SMatthias Ringwald { 289a0f524f0SMatthias Ringwald de_add_number(browsing_avctpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP); // browsing_avctpProtocol_service 290a0f524f0SMatthias Ringwald de_add_number(browsing_avctpProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version 291a0f524f0SMatthias Ringwald } 292a0f524f0SMatthias Ringwald de_pop_sequence(attribute, browsing_avctpProtocol); 293a0f524f0SMatthias Ringwald } 294a0f524f0SMatthias Ringwald de_pop_sequence(service, attribute); 295a0f524f0SMatthias Ringwald 296be32e7f1SMilanka Ringwald 297be32e7f1SMilanka Ringwald // 0x0100 "Service Name" 298be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); 299be32e7f1SMilanka Ringwald if (service_name){ 300be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(service_name), (uint8_t *) service_name); 301be32e7f1SMilanka Ringwald } else { 302be32e7f1SMilanka Ringwald if (controller){ 303be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_controller_service_name), (uint8_t *) default_avrcp_controller_service_name); 304be32e7f1SMilanka Ringwald } else { 305be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_target_service_name), (uint8_t *) default_avrcp_target_service_name); 306be32e7f1SMilanka Ringwald } 307be32e7f1SMilanka Ringwald } 308be32e7f1SMilanka Ringwald 309be32e7f1SMilanka Ringwald // 0x0100 "Provider Name" 310be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102); 311be32e7f1SMilanka Ringwald if (service_provider_name){ 312be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name); 313be32e7f1SMilanka Ringwald } else { 314be32e7f1SMilanka Ringwald if (controller){ 315be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_controller_service_provider_name), (uint8_t *) default_avrcp_controller_service_provider_name); 316be32e7f1SMilanka Ringwald } else { 317be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_target_service_provider_name), (uint8_t *) default_avrcp_target_service_provider_name); 318be32e7f1SMilanka Ringwald } 319be32e7f1SMilanka Ringwald } 320be32e7f1SMilanka Ringwald 321be32e7f1SMilanka Ringwald // 0x0311 "Supported Features" 322be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); 323be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); 324be32e7f1SMilanka Ringwald } 325be32e7f1SMilanka Ringwald 326be32e7f1SMilanka Ringwald void avrcp_controller_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint8_t browsing, uint16_t supported_features, const char * service_name, const char * service_provider_name){ 327be32e7f1SMilanka Ringwald avrcp_create_sdp_record(1, service, service_record_handle, browsing, supported_features, service_name, service_provider_name); 328be32e7f1SMilanka Ringwald } 329be32e7f1SMilanka Ringwald 330be32e7f1SMilanka Ringwald void avrcp_target_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint8_t browsing, uint16_t supported_features, const char * service_name, const char * service_provider_name){ 331be32e7f1SMilanka Ringwald avrcp_create_sdp_record(0, service, service_record_handle, browsing, supported_features, service_name, service_provider_name); 332be32e7f1SMilanka Ringwald } 333be32e7f1SMilanka Ringwald 3346086246cSMilanka Ringwald static void avrcp_emit_connection_established(btstack_packet_handler_t callback, uint16_t avrcp_cid, bd_addr_t addr, uint8_t status){ 335b193c45eSMilanka Ringwald if (!callback) return; 3366086246cSMilanka Ringwald uint8_t event[12]; 337b193c45eSMilanka Ringwald int pos = 0; 338b193c45eSMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 339b193c45eSMilanka Ringwald event[pos++] = sizeof(event) - 2; 340b193c45eSMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_CONNECTION_ESTABLISHED; 3416f43fcd7SMatthias Ringwald event[pos++] = status; 342b193c45eSMilanka Ringwald reverse_bd_addr(addr,&event[pos]); 343b193c45eSMilanka Ringwald pos += 6; 344b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 345b193c45eSMilanka Ringwald pos += 2; 346b193c45eSMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 347b193c45eSMilanka Ringwald } 348b193c45eSMilanka Ringwald 349d3b6a85eSMatthias Ringwald static void avrcp_emit_repeat_and_shuffle_mode(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t ctype, avrcp_repeat_mode_t repeat_mode, avrcp_shuffle_mode_t shuffle_mode){ 350be32e7f1SMilanka Ringwald if (!callback) return; 351be32e7f1SMilanka Ringwald uint8_t event[8]; 352be32e7f1SMilanka Ringwald int pos = 0; 353be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 354be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 355be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE; 356b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 357be32e7f1SMilanka Ringwald pos += 2; 358d3b6a85eSMatthias Ringwald event[pos++] = ctype; 359be32e7f1SMilanka Ringwald event[pos++] = repeat_mode; 360be32e7f1SMilanka Ringwald event[pos++] = shuffle_mode; 361be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 362be32e7f1SMilanka Ringwald } 363be32e7f1SMilanka Ringwald 364d3b6a85eSMatthias Ringwald static void avrcp_emit_operation_status(btstack_packet_handler_t callback, uint8_t subevent, uint16_t avrcp_cid, uint8_t ctype, uint8_t operation_id){ 365be32e7f1SMilanka Ringwald if (!callback) return; 366be32e7f1SMilanka Ringwald uint8_t event[7]; 367be32e7f1SMilanka Ringwald int pos = 0; 368be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 369be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 370be32e7f1SMilanka Ringwald event[pos++] = subevent; 371b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 372be32e7f1SMilanka Ringwald pos += 2; 373d3b6a85eSMatthias Ringwald event[pos++] = ctype; 374be32e7f1SMilanka Ringwald event[pos++] = operation_id; 375be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 376be32e7f1SMilanka Ringwald } 377be32e7f1SMilanka Ringwald 378b193c45eSMilanka Ringwald static void avrcp_emit_connection_closed(btstack_packet_handler_t callback, uint16_t avrcp_cid){ 379be32e7f1SMilanka Ringwald if (!callback) return; 380be32e7f1SMilanka Ringwald uint8_t event[5]; 381be32e7f1SMilanka Ringwald int pos = 0; 382be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 383be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 384be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_CONNECTION_RELEASED; 385b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 386be32e7f1SMilanka Ringwald pos += 2; 387be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 388be32e7f1SMilanka Ringwald } 389be32e7f1SMilanka Ringwald 3906086246cSMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_bd_addr(bd_addr_t addr, avrcp_context_t * context){ 391be32e7f1SMilanka Ringwald btstack_linked_list_iterator_t it; 3926086246cSMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &context->connections); 393be32e7f1SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 394be32e7f1SMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it); 395be32e7f1SMilanka Ringwald if (memcmp(addr, connection->remote_addr, 6) != 0) continue; 396be32e7f1SMilanka Ringwald return connection; 397be32e7f1SMilanka Ringwald } 398be32e7f1SMilanka Ringwald return NULL; 399be32e7f1SMilanka Ringwald } 400be32e7f1SMilanka Ringwald 4016086246cSMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_l2cap_signaling_cid(uint16_t l2cap_cid, avrcp_context_t * context){ 402be32e7f1SMilanka Ringwald btstack_linked_list_iterator_t it; 4036086246cSMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &context->connections); 404be32e7f1SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 405be32e7f1SMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it); 406be32e7f1SMilanka Ringwald if (connection->l2cap_signaling_cid != l2cap_cid) continue; 407be32e7f1SMilanka Ringwald return connection; 408be32e7f1SMilanka Ringwald } 409be32e7f1SMilanka Ringwald return NULL; 410be32e7f1SMilanka Ringwald } 411be32e7f1SMilanka Ringwald 4126086246cSMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_avrcp_cid(uint16_t l2cap_cid, avrcp_context_t * context){ 4136086246cSMilanka Ringwald btstack_linked_list_iterator_t it; 4146086246cSMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &context->connections); 4156086246cSMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 4166086246cSMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it); 4176086246cSMilanka Ringwald if (connection->avrcp_cid != l2cap_cid) continue; 4186086246cSMilanka Ringwald return connection; 4196086246cSMilanka Ringwald } 4206086246cSMilanka Ringwald return NULL; 4216086246cSMilanka Ringwald } 4226086246cSMilanka Ringwald 423be32e7f1SMilanka Ringwald static void avrcp_request_can_send_now(avrcp_connection_t * connection, uint16_t l2cap_cid){ 424be32e7f1SMilanka Ringwald connection->wait_to_send = 1; 425be32e7f1SMilanka Ringwald l2cap_request_can_send_now_event(l2cap_cid); 426be32e7f1SMilanka Ringwald } 427be32e7f1SMilanka Ringwald 428be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timeout_handler(btstack_timer_source_t * timer){ 429be32e7f1SMilanka Ringwald UNUSED(timer); 430be32e7f1SMilanka Ringwald avrcp_connection_t * connection = btstack_run_loop_get_timer_context(timer); 431be32e7f1SMilanka Ringwald btstack_run_loop_set_timer(&connection->press_and_hold_cmd_timer, 2000); // 2 seconds timeout 432be32e7f1SMilanka Ringwald btstack_run_loop_add_timer(&connection->press_and_hold_cmd_timer); 433be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_PRESS_COMMAND; 434be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 435be32e7f1SMilanka Ringwald } 436be32e7f1SMilanka Ringwald 437be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timer_start(avrcp_connection_t * connection){ 438be32e7f1SMilanka Ringwald btstack_run_loop_remove_timer(&connection->press_and_hold_cmd_timer); 439be32e7f1SMilanka Ringwald btstack_run_loop_set_timer_handler(&connection->press_and_hold_cmd_timer, avrcp_press_and_hold_timeout_handler); 440be32e7f1SMilanka Ringwald btstack_run_loop_set_timer_context(&connection->press_and_hold_cmd_timer, connection); 441be32e7f1SMilanka Ringwald btstack_run_loop_set_timer(&connection->press_and_hold_cmd_timer, 2000); // 2 seconds timeout 442be32e7f1SMilanka Ringwald btstack_run_loop_add_timer(&connection->press_and_hold_cmd_timer); 443be32e7f1SMilanka Ringwald } 444be32e7f1SMilanka Ringwald 445be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timer_stop(avrcp_connection_t * connection){ 446d1845da3SMilanka Ringwald connection->continuous_fast_forward_cmd = 0; 447be32e7f1SMilanka Ringwald btstack_run_loop_remove_timer(&connection->press_and_hold_cmd_timer); 448be32e7f1SMilanka Ringwald } 449be32e7f1SMilanka Ringwald 45031a5a745SMatthias Ringwald static uint8_t request_pass_through_release_control_cmd(avrcp_connection_t * connection){ 451be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_RELEASE_COMMAND; 452d1845da3SMilanka Ringwald if (connection->continuous_fast_forward_cmd){ 453be32e7f1SMilanka Ringwald avrcp_press_and_hold_timer_stop(connection); 454be32e7f1SMilanka Ringwald } 455be32e7f1SMilanka Ringwald connection->cmd_operands[0] = 0x80 | connection->cmd_operands[0]; 456be32e7f1SMilanka Ringwald connection->transaction_label++; 457be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 45831a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 459be32e7f1SMilanka Ringwald } 460be32e7f1SMilanka Ringwald 4616086246cSMilanka Ringwald static inline uint8_t request_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed, uint8_t continuous_fast_forward_cmd, avrcp_context_t * context){ 4626086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, context); 463be32e7f1SMilanka Ringwald if (!connection){ 464be32e7f1SMilanka Ringwald log_error("avrcp: could not find a connection."); 46531a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 466be32e7f1SMilanka Ringwald } 46731a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 468be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_PRESS_COMMAND; 4699cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_PASS_THROUGH; 470be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_CONTROL; 471be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 472d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 473adcdded9SMilanka Ringwald connection->cmd_operands_length = 0; 474be32e7f1SMilanka Ringwald 475d1845da3SMilanka Ringwald connection->continuous_fast_forward_cmd = continuous_fast_forward_cmd; 476adcdded9SMilanka Ringwald connection->cmd_operands_length = 2; 477be32e7f1SMilanka Ringwald connection->cmd_operands[0] = opid; 478be32e7f1SMilanka Ringwald if (playback_speed > 0){ 479be32e7f1SMilanka Ringwald connection->cmd_operands[2] = playback_speed; 480adcdded9SMilanka Ringwald connection->cmd_operands_length++; 481be32e7f1SMilanka Ringwald } 482adcdded9SMilanka Ringwald connection->cmd_operands[1] = connection->cmd_operands_length - 2; 483be32e7f1SMilanka Ringwald 484d1845da3SMilanka Ringwald if (connection->continuous_fast_forward_cmd){ 485be32e7f1SMilanka Ringwald avrcp_press_and_hold_timer_start(connection); 486be32e7f1SMilanka Ringwald } 487d1845da3SMilanka Ringwald 488be32e7f1SMilanka Ringwald connection->transaction_label++; 489be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 49031a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 491be32e7f1SMilanka Ringwald } 492be32e7f1SMilanka Ringwald 493d1845da3SMilanka Ringwald static uint8_t request_single_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed){ 4946086246cSMilanka Ringwald return request_pass_through_press_control_cmd(avrcp_cid, opid, playback_speed, 0, &avrcp_controller_context); 495d1845da3SMilanka Ringwald } 496d1845da3SMilanka Ringwald 497d1845da3SMilanka Ringwald static uint8_t request_continuous_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed){ 4986086246cSMilanka Ringwald return request_pass_through_press_control_cmd(avrcp_cid, opid, playback_speed, 1, &avrcp_controller_context); 499d1845da3SMilanka Ringwald } 500be32e7f1SMilanka Ringwald 501be32e7f1SMilanka Ringwald static int avrcp_send_cmd(uint16_t cid, avrcp_connection_t * connection){ 502adcdded9SMilanka Ringwald uint8_t command[30]; 503be32e7f1SMilanka Ringwald int pos = 0; 504be32e7f1SMilanka Ringwald // transport header 505be32e7f1SMilanka Ringwald // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 506be32e7f1SMilanka Ringwald command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0; 507be32e7f1SMilanka Ringwald // Profile IDentifier (PID) 5086086246cSMilanka Ringwald command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 5096086246cSMilanka Ringwald command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 510be32e7f1SMilanka Ringwald 511be32e7f1SMilanka Ringwald // command_type 512be32e7f1SMilanka Ringwald command[pos++] = connection->command_type; 513be32e7f1SMilanka Ringwald // subunit_type | subunit ID 514be32e7f1SMilanka Ringwald command[pos++] = (connection->subunit_type << 3) | connection->subunit_id; 515be32e7f1SMilanka Ringwald // opcode 5169cc1f3ceSMilanka Ringwald command[pos++] = (uint8_t)connection->command_opcode; 517be32e7f1SMilanka Ringwald // operands 518adcdded9SMilanka Ringwald memcpy(command+pos, connection->cmd_operands, connection->cmd_operands_length); 519adcdded9SMilanka Ringwald pos += connection->cmd_operands_length; 520be32e7f1SMilanka Ringwald 521be32e7f1SMilanka Ringwald return l2cap_send(cid, command, pos); 522be32e7f1SMilanka Ringwald } 523be32e7f1SMilanka Ringwald 524be32e7f1SMilanka Ringwald static int avrcp_register_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){ 525be32e7f1SMilanka Ringwald if (connection->notifications_to_deregister & (1 << event_id)) return 0; 526be32e7f1SMilanka Ringwald if (connection->notifications_enabled & (1 << event_id)) return 0; 527be32e7f1SMilanka Ringwald if (connection->notifications_to_register & (1 << event_id)) return 0; 528be32e7f1SMilanka Ringwald connection->notifications_to_register |= (1 << event_id); 529be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 530be32e7f1SMilanka Ringwald return 1; 531be32e7f1SMilanka Ringwald } 532be32e7f1SMilanka Ringwald 533be32e7f1SMilanka Ringwald static void avrcp_prepare_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){ 534be32e7f1SMilanka Ringwald connection->transaction_label++; 5359cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 536be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_NOTIFY; 537be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 538d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 539be32e7f1SMilanka Ringwald int pos = 0; 540be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 541be32e7f1SMilanka Ringwald pos += 3; 542be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_REGISTER_NOTIFICATION; 543be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; // reserved(upper 6) | packet_type -> 0 544be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 5); // parameter length 545be32e7f1SMilanka Ringwald pos += 2; 546be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = event_id; 547be32e7f1SMilanka Ringwald big_endian_store_32(connection->cmd_operands, pos, 0); 548be32e7f1SMilanka Ringwald pos += 4; 549adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 550be32e7f1SMilanka Ringwald // AVRCP_SPEC_V14.pdf 166 551be32e7f1SMilanka Ringwald // answer page 61 552be32e7f1SMilanka Ringwald } 553be32e7f1SMilanka Ringwald 5549cc1f3ceSMilanka Ringwald static uint8_t avrcp_cmd_opcode(uint8_t *packet, uint16_t size){ 5559cc1f3ceSMilanka Ringwald uint8_t cmd_opcode_index = 5; 5569cc1f3ceSMilanka Ringwald if (cmd_opcode_index > size) return AVRCP_CMD_OPCODE_UNDEFINED; 5579cc1f3ceSMilanka Ringwald return packet[cmd_opcode_index]; 558be32e7f1SMilanka Ringwald } 559be32e7f1SMilanka Ringwald 5606086246cSMilanka Ringwald static void avrcp_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 5616086246cSMilanka Ringwald if (!sdp_query_context.connection) return; 5626086246cSMilanka Ringwald if (sdp_query_context.connection->state != AVCTP_SIGNALING_W4_SDP_QUERY_COMPLETE) return; 5636086246cSMilanka Ringwald UNUSED(packet_type); 5646086246cSMilanka Ringwald UNUSED(channel); 5656086246cSMilanka Ringwald UNUSED(size); 5666086246cSMilanka Ringwald 5676086246cSMilanka Ringwald des_iterator_t des_list_it; 5686086246cSMilanka Ringwald des_iterator_t prot_it; 5696086246cSMilanka Ringwald // uint32_t avdtp_remote_uuid = 0; 5706086246cSMilanka Ringwald 5716086246cSMilanka Ringwald switch (hci_event_packet_get_type(packet)){ 5726086246cSMilanka Ringwald case SDP_EVENT_QUERY_ATTRIBUTE_VALUE: 5736086246cSMilanka Ringwald // Handle new SDP record 5746086246cSMilanka Ringwald if (sdp_event_query_attribute_byte_get_record_id(packet) != record_id) { 5756086246cSMilanka Ringwald record_id = sdp_event_query_attribute_byte_get_record_id(packet); 5766086246cSMilanka Ringwald // log_info("SDP Record: Nr: %d", record_id); 5776086246cSMilanka Ringwald } 5786086246cSMilanka Ringwald 5796086246cSMilanka Ringwald if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= attribute_value_buffer_size) { 5806086246cSMilanka Ringwald attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet); 5816086246cSMilanka Ringwald 5826086246cSMilanka Ringwald if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) { 5836086246cSMilanka Ringwald switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) { 5846086246cSMilanka Ringwald case BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST: 5856086246cSMilanka Ringwald if (de_get_element_type(attribute_value) != DE_DES) break; 5866086246cSMilanka Ringwald for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) { 5876086246cSMilanka Ringwald uint8_t * element = des_iterator_get_element(&des_list_it); 5886086246cSMilanka Ringwald if (de_get_element_type(element) != DE_UUID) continue; 5896086246cSMilanka Ringwald uint32_t uuid = de_get_uuid32(element); 5906086246cSMilanka Ringwald switch (uuid){ 5916086246cSMilanka Ringwald case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_TARGET: 592df642728SMilanka Ringwald if (sdp_query_context.avrcp_context->role == AVRCP_TARGET) { 593df642728SMilanka Ringwald sdp_query_context.role_supported = 1; 5946086246cSMilanka Ringwald break; 5956086246cSMilanka Ringwald } 5966086246cSMilanka Ringwald break; 5976086246cSMilanka Ringwald case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL: 598df642728SMilanka Ringwald case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_CONTROLLER: 599df642728SMilanka Ringwald if (sdp_query_context.avrcp_context->role == AVRCP_CONTROLLER) { 600df642728SMilanka Ringwald sdp_query_context.role_supported = 1; 6016086246cSMilanka Ringwald break; 6026086246cSMilanka Ringwald } 6036086246cSMilanka Ringwald break; 6046086246cSMilanka Ringwald default: 6056086246cSMilanka Ringwald break; 6066086246cSMilanka Ringwald } 6076086246cSMilanka Ringwald } 6086086246cSMilanka Ringwald break; 6096086246cSMilanka Ringwald 6106086246cSMilanka Ringwald case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST: { 6116086246cSMilanka Ringwald // log_info("SDP Attribute: 0x%04x", sdp_event_query_attribute_byte_get_attribute_id(packet)); 6126086246cSMilanka Ringwald for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) { 6136086246cSMilanka Ringwald uint8_t *des_element; 6146086246cSMilanka Ringwald uint8_t *element; 6156086246cSMilanka Ringwald uint32_t uuid; 6166086246cSMilanka Ringwald 6176086246cSMilanka Ringwald if (des_iterator_get_type(&des_list_it) != DE_DES) continue; 6186086246cSMilanka Ringwald 6196086246cSMilanka Ringwald des_element = des_iterator_get_element(&des_list_it); 6206086246cSMilanka Ringwald des_iterator_init(&prot_it, des_element); 6216086246cSMilanka Ringwald element = des_iterator_get_element(&prot_it); 6226086246cSMilanka Ringwald 6236086246cSMilanka Ringwald if (de_get_element_type(element) != DE_UUID) continue; 6246086246cSMilanka Ringwald 6256086246cSMilanka Ringwald uuid = de_get_uuid32(element); 6266086246cSMilanka Ringwald switch (uuid){ 6276086246cSMilanka Ringwald case BLUETOOTH_PROTOCOL_L2CAP: 6286086246cSMilanka Ringwald if (!des_iterator_has_more(&prot_it)) continue; 6296086246cSMilanka Ringwald des_iterator_next(&prot_it); 6306086246cSMilanka Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context.avrcp_l2cap_psm); 6316086246cSMilanka Ringwald break; 6326086246cSMilanka Ringwald case BLUETOOTH_PROTOCOL_AVCTP: 6336086246cSMilanka Ringwald if (!des_iterator_has_more(&prot_it)) continue; 6346086246cSMilanka Ringwald des_iterator_next(&prot_it); 6356086246cSMilanka Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context.avrcp_version); 6366086246cSMilanka Ringwald break; 6376086246cSMilanka Ringwald default: 6386086246cSMilanka Ringwald break; 6396086246cSMilanka Ringwald } 6406086246cSMilanka Ringwald } 6416086246cSMilanka Ringwald } 6426086246cSMilanka Ringwald break; 643*227d16a5SMatthias Ringwald case BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: { 644*227d16a5SMatthias Ringwald // log_info("SDP Attribute: 0x%04x", sdp_event_query_attribute_byte_get_attribute_id(packet)); 645*227d16a5SMatthias Ringwald if (de_get_element_type(attribute_value) != DE_DES) break; 646*227d16a5SMatthias Ringwald 647*227d16a5SMatthias Ringwald des_iterator_t des_list_0_it; 648*227d16a5SMatthias Ringwald uint8_t *element_0; 649*227d16a5SMatthias Ringwald 650*227d16a5SMatthias Ringwald des_iterator_init(&des_list_0_it, attribute_value); 651*227d16a5SMatthias Ringwald element_0 = des_iterator_get_element(&des_list_0_it); 652*227d16a5SMatthias Ringwald 653*227d16a5SMatthias Ringwald for (des_iterator_init(&des_list_it, element_0); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) { 654*227d16a5SMatthias Ringwald uint8_t *des_element; 655*227d16a5SMatthias Ringwald uint8_t *element; 656*227d16a5SMatthias Ringwald uint32_t uuid; 657*227d16a5SMatthias Ringwald 658*227d16a5SMatthias Ringwald if (des_iterator_get_type(&des_list_it) != DE_DES) continue; 659*227d16a5SMatthias Ringwald 660*227d16a5SMatthias Ringwald des_element = des_iterator_get_element(&des_list_it); 661*227d16a5SMatthias Ringwald des_iterator_init(&prot_it, des_element); 662*227d16a5SMatthias Ringwald element = des_iterator_get_element(&prot_it); 663*227d16a5SMatthias Ringwald 664*227d16a5SMatthias Ringwald if (de_get_element_type(element) != DE_UUID) continue; 665*227d16a5SMatthias Ringwald 666*227d16a5SMatthias Ringwald uuid = de_get_uuid32(element); 667*227d16a5SMatthias Ringwald switch (uuid){ 668*227d16a5SMatthias Ringwald case BLUETOOTH_PROTOCOL_L2CAP: 669*227d16a5SMatthias Ringwald if (!des_iterator_has_more(&prot_it)) continue; 670*227d16a5SMatthias Ringwald des_iterator_next(&prot_it); 671*227d16a5SMatthias Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context.avrcp_browsing_l2cap_psm); 672*227d16a5SMatthias Ringwald break; 673*227d16a5SMatthias Ringwald case BLUETOOTH_PROTOCOL_AVCTP: 674*227d16a5SMatthias Ringwald if (!des_iterator_has_more(&prot_it)) continue; 675*227d16a5SMatthias Ringwald des_iterator_next(&prot_it); 676*227d16a5SMatthias Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context.avrcp_browsing_version); 677*227d16a5SMatthias Ringwald break; 678*227d16a5SMatthias Ringwald default: 679*227d16a5SMatthias Ringwald break; 680*227d16a5SMatthias Ringwald } 681*227d16a5SMatthias Ringwald } 682*227d16a5SMatthias Ringwald } 683*227d16a5SMatthias Ringwald break; 6846086246cSMilanka Ringwald default: 6856086246cSMilanka Ringwald break; 6866086246cSMilanka Ringwald } 6876086246cSMilanka Ringwald } 6886086246cSMilanka Ringwald } else { 6896086246cSMilanka Ringwald log_error("SDP attribute value buffer size exceeded: available %d, required %d", attribute_value_buffer_size, sdp_event_query_attribute_byte_get_attribute_length(packet)); 6906086246cSMilanka Ringwald } 6916086246cSMilanka Ringwald break; 6926086246cSMilanka Ringwald 6936086246cSMilanka Ringwald case SDP_EVENT_QUERY_COMPLETE: 6946086246cSMilanka Ringwald log_info("General query done with status %d.", sdp_event_query_complete_get_status(packet)); 695df642728SMilanka Ringwald if (!sdp_query_context.role_supported || !sdp_query_context.avrcp_l2cap_psm){ 6966086246cSMilanka Ringwald sdp_query_context.connection->state = AVCTP_CONNECTION_IDLE; 697df642728SMilanka Ringwald avrcp_emit_connection_established(sdp_query_context.avrcp_context->avrcp_callback, sdp_query_context.connection->avrcp_cid, sdp_query_context.connection->remote_addr, SDP_SERVICE_NOT_FOUND); 6986086246cSMilanka Ringwald break; 6996086246cSMilanka Ringwald } 700*227d16a5SMatthias Ringwald log_info("AVRCP Control PSM 0x%02x, Browsing PSM 0x%02x", sdp_query_context.avrcp_l2cap_psm, sdp_query_context.avrcp_browsing_l2cap_psm); 701df642728SMilanka Ringwald 7026086246cSMilanka Ringwald sdp_query_context.connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED; 703df642728SMilanka Ringwald l2cap_create_channel(sdp_query_context.avrcp_context->packet_handler, sdp_query_context.connection->remote_addr, sdp_query_context.avrcp_l2cap_psm, l2cap_max_mtu(), NULL); 7046086246cSMilanka Ringwald break; 7056086246cSMilanka Ringwald } 7066086246cSMilanka Ringwald } 7076086246cSMilanka Ringwald 7089cc1f3ceSMilanka Ringwald static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){ 709be32e7f1SMilanka Ringwald uint8_t operands[20]; 710be32e7f1SMilanka Ringwald uint8_t opcode; 7118a2c6c7cSMilanka Ringwald int pos = 3; 712be32e7f1SMilanka Ringwald // uint8_t transport_header = packet[0]; 713be32e7f1SMilanka Ringwald // uint8_t transaction_label = transport_header >> 4; 714be32e7f1SMilanka Ringwald // uint8_t packet_type = (transport_header & 0x0F) >> 2; 715be32e7f1SMilanka Ringwald // uint8_t frame_type = (transport_header & 0x03) >> 1; 716be32e7f1SMilanka Ringwald // uint8_t ipid = transport_header & 0x01; 7178a2c6c7cSMilanka Ringwald // uint8_t byte_value = packet[2]; 718be32e7f1SMilanka Ringwald // uint16_t pid = (byte_value << 8) | packet[2]; 7198a2c6c7cSMilanka Ringwald 720d5ff3b26SMatthias Ringwald avrcp_command_type_t ctype = (avrcp_command_type_t) packet[pos++]; 7218a2c6c7cSMilanka Ringwald uint8_t byte_value = packet[pos++]; 722d5ff3b26SMatthias Ringwald avrcp_subunit_type_t subunit_type = (avrcp_subunit_type_t) (byte_value >> 3); 723d5ff3b26SMatthias Ringwald avrcp_subunit_type_t subunit_id = (avrcp_subunit_type_t) (byte_value & 0x07); 7248a2c6c7cSMilanka Ringwald opcode = packet[pos++]; 725be32e7f1SMilanka Ringwald 726be32e7f1SMilanka Ringwald // printf(" Transport header 0x%02x (transaction_label %d, packet_type %d, frame_type %d, ipid %d), pid 0x%4x\n", 727be32e7f1SMilanka Ringwald // transport_header, transaction_label, packet_type, frame_type, ipid, pid); 728be32e7f1SMilanka Ringwald // // printf_hexdump(packet+pos, size-pos); 729d5ff3b26SMatthias Ringwald 730d5ff3b26SMatthias Ringwald uint8_t pdu_id; 731d5ff3b26SMatthias Ringwald uint16_t param_length; 7329cc1f3ceSMilanka Ringwald switch (avrcp_cmd_opcode(packet,size)){ 733be32e7f1SMilanka Ringwald case AVRCP_CMD_OPCODE_UNIT_INFO:{ 734294335f1SMilanka Ringwald if (connection->state != AVCTP_W2_RECEIVE_RESPONSE) return; 7359cc1f3ceSMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 736294335f1SMilanka Ringwald 737be32e7f1SMilanka Ringwald // operands: 738be32e7f1SMilanka Ringwald memcpy(operands, packet+pos, 5); 739be32e7f1SMilanka Ringwald uint8_t unit_type = operands[1] >> 3; 740be32e7f1SMilanka Ringwald uint8_t unit = operands[1] & 0x07; 741be32e7f1SMilanka Ringwald uint32_t company_id = operands[2] << 16 | operands[3] << 8 | operands[4]; 742be32e7f1SMilanka Ringwald log_info(" UNIT INFO response: ctype 0x%02x (0C), subunit_type 0x%02x (1F), subunit_id 0x%02x (07), opcode 0x%02x (30), unit_type 0x%02x, unit %d, company_id 0x%06x", 743be32e7f1SMilanka Ringwald ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id ); 744be32e7f1SMilanka Ringwald break; 745be32e7f1SMilanka Ringwald } 746be32e7f1SMilanka Ringwald case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT: 747be32e7f1SMilanka Ringwald if (size - pos < 7) { 748be32e7f1SMilanka Ringwald log_error("avrcp: wrong packet size"); 749be32e7f1SMilanka Ringwald return; 750be32e7f1SMilanka Ringwald }; 751be32e7f1SMilanka Ringwald // operands: 752be32e7f1SMilanka Ringwald memcpy(operands, packet+pos, 7); 753be32e7f1SMilanka Ringwald pos += 7; 754be32e7f1SMilanka Ringwald // uint32_t company_id = operands[0] << 16 | operands[1] << 8 | operands[2]; 755d5ff3b26SMatthias Ringwald pdu_id = operands[3]; 756294335f1SMilanka Ringwald 757294335f1SMilanka Ringwald if (connection->state != AVCTP_W2_RECEIVE_RESPONSE && pdu_id != AVRCP_PDU_ID_REGISTER_NOTIFICATION){ 758ac98fce4SMilanka Ringwald log_info("AVRCP_CMD_OPCODE_VENDOR_DEPENDENT state %d", connection->state); 759294335f1SMilanka Ringwald return; 760294335f1SMilanka Ringwald } 761294335f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 762294335f1SMilanka Ringwald 763294335f1SMilanka Ringwald 764be32e7f1SMilanka Ringwald // uint8_t unit_type = operands[4] >> 3; 765be32e7f1SMilanka Ringwald // uint8_t unit = operands[4] & 0x07; 766d5ff3b26SMatthias Ringwald param_length = big_endian_read_16(operands, 5); 767be32e7f1SMilanka Ringwald 768be32e7f1SMilanka Ringwald // printf(" VENDOR DEPENDENT response: ctype 0x%02x (0C), subunit_type 0x%02x (1F), subunit_id 0x%02x (07), opcode 0x%02x (30), unit_type 0x%02x, unit %d, company_id 0x%06x\n", 769be32e7f1SMilanka Ringwald // ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id ); 770be32e7f1SMilanka Ringwald 771be32e7f1SMilanka Ringwald // if (ctype == AVRCP_CTYPE_RESPONSE_INTERIM) return; 772be32e7f1SMilanka Ringwald log_info(" VENDOR DEPENDENT response: pdu id 0x%02x, param_length %d, status %s", pdu_id, param_length, avrcp_ctype2str(ctype)); 773be32e7f1SMilanka Ringwald switch (pdu_id){ 774be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue:{ 775be32e7f1SMilanka Ringwald uint8_t num_attributes = packet[pos++]; 776be32e7f1SMilanka Ringwald int i; 777d5ff3b26SMatthias Ringwald avrcp_repeat_mode_t repeat_mode = AVRCP_REPEAT_MODE_INVALID; 778d5ff3b26SMatthias Ringwald avrcp_shuffle_mode_t shuffle_mode = AVRCP_SHUFFLE_MODE_INVALID; 779be32e7f1SMilanka Ringwald for (i = 0; i < num_attributes; i++){ 780be32e7f1SMilanka Ringwald uint8_t attribute_id = packet[pos++]; 7816086246cSMilanka Ringwald uint8_t value = packet[pos++]; 782be32e7f1SMilanka Ringwald switch (attribute_id){ 783be32e7f1SMilanka Ringwald case 0x02: 7846086246cSMilanka Ringwald repeat_mode = (avrcp_repeat_mode_t) value; 785be32e7f1SMilanka Ringwald break; 786be32e7f1SMilanka Ringwald case 0x03: 7876086246cSMilanka Ringwald shuffle_mode = (avrcp_shuffle_mode_t) value; 788be32e7f1SMilanka Ringwald break; 789be32e7f1SMilanka Ringwald default: 790be32e7f1SMilanka Ringwald break; 791be32e7f1SMilanka Ringwald } 792be32e7f1SMilanka Ringwald } 7936086246cSMilanka Ringwald avrcp_emit_repeat_and_shuffle_mode(avrcp_callback, connection->avrcp_cid, ctype, repeat_mode, shuffle_mode); 794be32e7f1SMilanka Ringwald break; 795be32e7f1SMilanka Ringwald } 796be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_SetPlayerApplicationSettingValue:{ 797be32e7f1SMilanka Ringwald uint8_t event[6]; 798be32e7f1SMilanka Ringwald int offset = 0; 799be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 800be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 801be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE; 8026086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 803be32e7f1SMilanka Ringwald offset += 2; 804be32e7f1SMilanka Ringwald event[offset++] = ctype; 805be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 806be32e7f1SMilanka Ringwald break; 807be32e7f1SMilanka Ringwald } 808be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME:{ 809be32e7f1SMilanka Ringwald uint8_t event[7]; 810be32e7f1SMilanka Ringwald int offset = 0; 811be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 812be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 813be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_SET_ABSOLUTE_VOLUME_RESPONSE; 8146086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 815be32e7f1SMilanka Ringwald offset += 2; 816be32e7f1SMilanka Ringwald event[offset++] = ctype; 817be32e7f1SMilanka Ringwald event[offset++] = packet[pos++]; 818be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 819be32e7f1SMilanka Ringwald break; 820be32e7f1SMilanka Ringwald } 821be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GET_CAPABILITIES:{ 822d5ff3b26SMatthias Ringwald avrcp_capability_id_t capability_id = (avrcp_capability_id_t) packet[pos++]; 823be32e7f1SMilanka Ringwald uint8_t capability_count = packet[pos++]; 824be32e7f1SMilanka Ringwald int i; 825be32e7f1SMilanka Ringwald switch (capability_id){ 826be32e7f1SMilanka Ringwald case AVRCP_CAPABILITY_ID_COMPANY: 827be32e7f1SMilanka Ringwald // log_info("Supported companies %d: ", capability_count); 828be32e7f1SMilanka Ringwald for (i = 0; i < capability_count; i++){ 829be32e7f1SMilanka Ringwald uint32_t company_id = big_endian_read_24(packet, pos); 830be32e7f1SMilanka Ringwald pos += 3; 831be32e7f1SMilanka Ringwald log_info(" 0x%06x, ", company_id); 832be32e7f1SMilanka Ringwald } 833be32e7f1SMilanka Ringwald break; 834be32e7f1SMilanka Ringwald case AVRCP_CAPABILITY_ID_EVENT: 835be32e7f1SMilanka Ringwald // log_info("Supported events %d: ", capability_count); 836be32e7f1SMilanka Ringwald for (i = 0; i < capability_count; i++){ 837be32e7f1SMilanka Ringwald uint8_t event_id = packet[pos++]; 838be32e7f1SMilanka Ringwald log_info(" 0x%02x %s", event_id, avrcp_event2str(event_id)); 839be32e7f1SMilanka Ringwald } 840be32e7f1SMilanka Ringwald break; 841be32e7f1SMilanka Ringwald } 842be32e7f1SMilanka Ringwald break; 843be32e7f1SMilanka Ringwald } 844be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GET_PLAY_STATUS:{ 845be32e7f1SMilanka Ringwald uint32_t song_length = big_endian_read_32(packet, pos); 846be32e7f1SMilanka Ringwald pos += 4; 847be32e7f1SMilanka Ringwald uint32_t song_position = big_endian_read_32(packet, pos); 848be32e7f1SMilanka Ringwald pos += 4; 849be32e7f1SMilanka Ringwald uint8_t play_status = packet[pos]; 850be32e7f1SMilanka Ringwald // log_info(" GET_PLAY_STATUS length 0x%04X, position 0x%04X, status %s", song_length, song_position, avrcp_play_status2str(play_status)); 851be32e7f1SMilanka Ringwald 852be32e7f1SMilanka Ringwald uint8_t event[15]; 853be32e7f1SMilanka Ringwald int offset = 0; 854be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 855be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 856be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_PLAY_STATUS; 8576086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 858be32e7f1SMilanka Ringwald offset += 2; 859be32e7f1SMilanka Ringwald event[offset++] = ctype; 860be32e7f1SMilanka Ringwald little_endian_store_32(event, offset, song_length); 861be32e7f1SMilanka Ringwald offset += 4; 862be32e7f1SMilanka Ringwald little_endian_store_32(event, offset, song_position); 863be32e7f1SMilanka Ringwald offset += 4; 864be32e7f1SMilanka Ringwald event[offset++] = play_status; 865be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 866be32e7f1SMilanka Ringwald break; 867be32e7f1SMilanka Ringwald } 868be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_REGISTER_NOTIFICATION:{ 869d5ff3b26SMatthias Ringwald avrcp_notification_event_id_t event_id = (avrcp_notification_event_id_t) packet[pos++]; 870be32e7f1SMilanka Ringwald uint16_t event_mask = (1 << event_id); 871be32e7f1SMilanka Ringwald uint16_t reset_event_mask = ~event_mask; 872be32e7f1SMilanka Ringwald switch (ctype){ 873be32e7f1SMilanka Ringwald case AVRCP_CTYPE_RESPONSE_INTERIM: 874be32e7f1SMilanka Ringwald // register as enabled 875be32e7f1SMilanka Ringwald connection->notifications_enabled |= event_mask; 876be32e7f1SMilanka Ringwald // printf("INTERIM notifications_enabled 0x%2x, notifications_to_register 0x%2x\n", connection->notifications_enabled, connection->notifications_to_register); 877be32e7f1SMilanka Ringwald break; 878be32e7f1SMilanka Ringwald case AVRCP_CTYPE_RESPONSE_CHANGED_STABLE: 879be32e7f1SMilanka Ringwald // received change, event is considered deregistered 880be32e7f1SMilanka Ringwald // we are re-enabling it automatically, if it is not 881be32e7f1SMilanka Ringwald // explicitly disabled 882be32e7f1SMilanka Ringwald connection->notifications_enabled &= reset_event_mask; 883be32e7f1SMilanka Ringwald if (! (connection->notifications_to_deregister & event_mask)){ 884be32e7f1SMilanka Ringwald avrcp_register_notification(connection, event_id); 885be32e7f1SMilanka Ringwald // printf("CHANGED_STABLE notifications_enabled 0x%2x, notifications_to_register 0x%2x\n", connection->notifications_enabled, connection->notifications_to_register); 886be32e7f1SMilanka Ringwald } else { 887be32e7f1SMilanka Ringwald connection->notifications_to_deregister &= reset_event_mask; 888be32e7f1SMilanka Ringwald } 889be32e7f1SMilanka Ringwald break; 890be32e7f1SMilanka Ringwald default: 891be32e7f1SMilanka Ringwald connection->notifications_to_register &= reset_event_mask; 892be32e7f1SMilanka Ringwald connection->notifications_enabled &= reset_event_mask; 893be32e7f1SMilanka Ringwald connection->notifications_to_deregister &= reset_event_mask; 894be32e7f1SMilanka Ringwald break; 895be32e7f1SMilanka Ringwald } 896be32e7f1SMilanka Ringwald 897be32e7f1SMilanka Ringwald switch (event_id){ 898be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED:{ 899be32e7f1SMilanka Ringwald uint8_t event[7]; 900be32e7f1SMilanka Ringwald int offset = 0; 901be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 902be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 903be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED; 9046086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 905be32e7f1SMilanka Ringwald offset += 2; 906be32e7f1SMilanka Ringwald event[offset++] = ctype; 907be32e7f1SMilanka Ringwald event[offset++] = packet[pos]; 908be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 909be32e7f1SMilanka Ringwald break; 910be32e7f1SMilanka Ringwald } 911be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED:{ 912ee6d17f2SMatthias Ringwald uint8_t event[6]; 913be32e7f1SMilanka Ringwald int offset = 0; 914be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 915be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 916be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED; 9176086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 918be32e7f1SMilanka Ringwald offset += 2; 919be32e7f1SMilanka Ringwald event[offset++] = ctype; 920be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 921be32e7f1SMilanka Ringwald break; 922be32e7f1SMilanka Ringwald } 923be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED:{ 924be32e7f1SMilanka Ringwald uint8_t event[6]; 925be32e7f1SMilanka Ringwald int offset = 0; 926be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 927be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 928be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED; 9296086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 930be32e7f1SMilanka Ringwald offset += 2; 931be32e7f1SMilanka Ringwald event[offset++] = ctype; 932be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 933be32e7f1SMilanka Ringwald break; 934be32e7f1SMilanka Ringwald } 935be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_AVAILABLE_PLAYERS_CHANGED:{ 936be32e7f1SMilanka Ringwald uint8_t event[6]; 937be32e7f1SMilanka Ringwald int offset = 0; 938be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 939be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 940be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED; 9416086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 942be32e7f1SMilanka Ringwald offset += 2; 943be32e7f1SMilanka Ringwald event[offset++] = ctype; 944be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 945be32e7f1SMilanka Ringwald break; 946be32e7f1SMilanka Ringwald } 947be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED:{ 948be32e7f1SMilanka Ringwald uint8_t event[7]; 949be32e7f1SMilanka Ringwald int offset = 0; 950be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 951be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 952be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED; 9536086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 954be32e7f1SMilanka Ringwald offset += 2; 955be32e7f1SMilanka Ringwald event[offset++] = ctype; 956be32e7f1SMilanka Ringwald event[offset++] = packet[pos++] & 0x7F; 957be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 958be32e7f1SMilanka Ringwald break; 959be32e7f1SMilanka Ringwald } 960be32e7f1SMilanka Ringwald // case AVRCP_NOTIFICATION_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:{ 961be32e7f1SMilanka Ringwald // uint8_t num_PlayerApplicationSettingAttributes = packet[pos++]; 962be32e7f1SMilanka Ringwald // int i; 963be32e7f1SMilanka Ringwald // for (i = 0; i < num_PlayerApplicationSettingAttributes; i++){ 964be32e7f1SMilanka Ringwald // uint8_t PlayerApplicationSetting_AttributeID = packet[pos++]; 965be32e7f1SMilanka Ringwald // uint8_t PlayerApplicationSettingValueID = packet[pos++]; 966be32e7f1SMilanka Ringwald // } 967be32e7f1SMilanka Ringwald // break; 968be32e7f1SMilanka Ringwald // } 969be32e7f1SMilanka Ringwald // case AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED: 970be32e7f1SMilanka Ringwald // uint16_t player_id = big_endian_read_16(packet, pos); 971be32e7f1SMilanka Ringwald // pos += 2; 972be32e7f1SMilanka Ringwald // uint16_t uid_counter = big_endian_read_16(packet, pos); 973be32e7f1SMilanka Ringwald // pos += 2; 974be32e7f1SMilanka Ringwald // break; 975be32e7f1SMilanka Ringwald // case AVRCP_NOTIFICATION_EVENT_UIDS_CHANGED: 976be32e7f1SMilanka Ringwald // uint16_t uid_counter = big_endian_read_16(packet, pos); 977be32e7f1SMilanka Ringwald // pos += 2; 978be32e7f1SMilanka Ringwald // break; 979be32e7f1SMilanka Ringwald default: 980be32e7f1SMilanka Ringwald log_info("avrcp: not implemented"); 981be32e7f1SMilanka Ringwald break; 982be32e7f1SMilanka Ringwald } 983be32e7f1SMilanka Ringwald if (connection->notifications_to_register != 0){ 984be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 985be32e7f1SMilanka Ringwald } 986be32e7f1SMilanka Ringwald break; 987be32e7f1SMilanka Ringwald } 988be32e7f1SMilanka Ringwald 989be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES:{ 990be32e7f1SMilanka Ringwald uint8_t num_attributes = packet[pos++]; 991be32e7f1SMilanka Ringwald int i; 992be32e7f1SMilanka Ringwald struct item { 993be32e7f1SMilanka Ringwald uint16_t len; 994be32e7f1SMilanka Ringwald uint8_t * value; 995be32e7f1SMilanka Ringwald } items[AVRCP_MEDIA_ATTR_COUNT]; 996be32e7f1SMilanka Ringwald memset(items, 0, sizeof(items)); 997be32e7f1SMilanka Ringwald 998be32e7f1SMilanka Ringwald uint16_t string_attributes_len = 0; 999be32e7f1SMilanka Ringwald uint8_t num_string_attributes = 0; 1000be32e7f1SMilanka Ringwald uint16_t total_event_payload_for_string_attributes = HCI_EVENT_PAYLOAD_SIZE-2; 1001be32e7f1SMilanka Ringwald uint16_t max_string_attribute_value_len = 0; 10024bb88fd7SMatthias Ringwald if (ctype == AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE || ctype == AVRCP_CTYPE_RESPONSE_CHANGED_STABLE){ 1003be32e7f1SMilanka Ringwald for (i = 0; i < num_attributes; i++){ 1004d5ff3b26SMatthias Ringwald avrcp_media_attribute_id_t attr_id = (avrcp_media_attribute_id_t) big_endian_read_32(packet, pos); 1005be32e7f1SMilanka Ringwald pos += 4; 1006be32e7f1SMilanka Ringwald // uint16_t character_set = big_endian_read_16(packet, pos); 1007be32e7f1SMilanka Ringwald pos += 2; 1008be32e7f1SMilanka Ringwald uint16_t attr_value_length = big_endian_read_16(packet, pos); 1009be32e7f1SMilanka Ringwald pos += 2; 1010be32e7f1SMilanka Ringwald 1011be32e7f1SMilanka Ringwald // debug - to remove later 1012be32e7f1SMilanka Ringwald uint8_t value[100]; 1013be32e7f1SMilanka Ringwald uint16_t value_len = sizeof(value) <= attr_value_length? sizeof(value) - 1 : attr_value_length; 1014be32e7f1SMilanka Ringwald memcpy(value, packet+pos, value_len); 1015be32e7f1SMilanka Ringwald value[value_len] = 0; 1016be32e7f1SMilanka Ringwald // printf("Now Playing Info %s: %s \n", attribute2str(attr_id), value); 1017be32e7f1SMilanka Ringwald // end debug 1018be32e7f1SMilanka Ringwald 1019be32e7f1SMilanka Ringwald if ((attr_id >= 1) || (attr_id <= AVRCP_MEDIA_ATTR_COUNT)) { 1020be32e7f1SMilanka Ringwald items[attr_id-1].len = attr_value_length; 1021be32e7f1SMilanka Ringwald items[attr_id-1].value = &packet[pos]; 1022be32e7f1SMilanka Ringwald switch (attr_id){ 1023be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TITLE: 1024be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ARTIST: 1025be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ALBUM: 1026be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_GENRE: 1027be32e7f1SMilanka Ringwald num_string_attributes++; 1028be32e7f1SMilanka Ringwald string_attributes_len += attr_value_length; 1029be32e7f1SMilanka Ringwald if (max_string_attribute_value_len < attr_value_length){ 1030be32e7f1SMilanka Ringwald max_string_attribute_value_len = attr_value_length; 1031be32e7f1SMilanka Ringwald } 1032be32e7f1SMilanka Ringwald break; 1033be32e7f1SMilanka Ringwald default: 1034be32e7f1SMilanka Ringwald break; 1035be32e7f1SMilanka Ringwald } 1036be32e7f1SMilanka Ringwald } 1037be32e7f1SMilanka Ringwald pos += attr_value_length; 1038be32e7f1SMilanka Ringwald } 10394bb88fd7SMatthias Ringwald } 10404bb88fd7SMatthias Ringwald 1041be32e7f1SMilanka Ringwald // subtract space for fixed fields 1042be32e7f1SMilanka Ringwald total_event_payload_for_string_attributes -= 14 + 4; // 4 for '\0' 1043be32e7f1SMilanka Ringwald 1044be32e7f1SMilanka Ringwald // @TODO optimize space by repeatedly decreasing max_string_attribute_value_len until it fits into buffer instead of crude divion 1045be32e7f1SMilanka Ringwald uint16_t max_value_len = total_event_payload_for_string_attributes > string_attributes_len? max_string_attribute_value_len : total_event_payload_for_string_attributes/(string_attributes_len+1) - 1; 1046be32e7f1SMilanka Ringwald // printf("num_string_attributes %d, string_attributes_len %d, total_event_payload_for_string_attributes %d, max_value_len %d \n", num_string_attributes, string_attributes_len, total_event_payload_for_string_attributes, max_value_len); 1047be32e7f1SMilanka Ringwald 1048be32e7f1SMilanka Ringwald const uint8_t attribute_order[] = { 1049be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_TRACK, 1050be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_TOTAL_TRACKS, 1051be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_SONG_LENGTH, 1052be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_TITLE, 1053be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_ARTIST, 1054be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_ALBUM, 1055be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_GENRE 1056be32e7f1SMilanka Ringwald }; 1057be32e7f1SMilanka Ringwald 1058be32e7f1SMilanka Ringwald uint8_t event[HCI_EVENT_BUFFER_SIZE]; 1059be32e7f1SMilanka Ringwald event[0] = HCI_EVENT_AVRCP_META; 1060be32e7f1SMilanka Ringwald pos = 2; 1061be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_NOW_PLAYING_INFO; 10626086246cSMilanka Ringwald little_endian_store_16(event, pos, connection->avrcp_cid); 1063be32e7f1SMilanka Ringwald pos += 2; 1064be32e7f1SMilanka Ringwald event[pos++] = ctype; 1065be32e7f1SMilanka Ringwald for (i = 0; i < sizeof(attribute_order); i++){ 1066d5ff3b26SMatthias Ringwald avrcp_media_attribute_id_t attr_id = (avrcp_media_attribute_id_t) attribute_order[i]; 1067be32e7f1SMilanka Ringwald uint16_t value_len = 0; 1068be32e7f1SMilanka Ringwald switch (attr_id){ 1069be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TITLE: 1070be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ARTIST: 1071be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ALBUM: 1072be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_GENRE: 1073be32e7f1SMilanka Ringwald if (items[attr_id-1].value){ 1074be32e7f1SMilanka Ringwald value_len = items[attr_id-1].len <= max_value_len ? items[attr_id-1].len : max_value_len; 1075be32e7f1SMilanka Ringwald } 1076be32e7f1SMilanka Ringwald event[pos++] = value_len + 1; 1077be32e7f1SMilanka Ringwald if (value_len){ 1078be32e7f1SMilanka Ringwald memcpy(event+pos, items[attr_id-1].value, value_len); 1079be32e7f1SMilanka Ringwald pos += value_len; 1080be32e7f1SMilanka Ringwald } 1081be32e7f1SMilanka Ringwald event[pos++] = 0; 1082be32e7f1SMilanka Ringwald break; 1083be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_SONG_LENGTH: 1084be32e7f1SMilanka Ringwald if (items[attr_id-1].value){ 1085be32e7f1SMilanka Ringwald little_endian_store_32(event, pos, btstack_atoi((char *)items[attr_id-1].value)); 1086be32e7f1SMilanka Ringwald } else { 1087be32e7f1SMilanka Ringwald little_endian_store_32(event, pos, 0); 1088be32e7f1SMilanka Ringwald } 1089be32e7f1SMilanka Ringwald pos += 4; 1090be32e7f1SMilanka Ringwald break; 1091be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TRACK: 1092be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TOTAL_TRACKS: 1093be32e7f1SMilanka Ringwald if (items[attr_id-1].value){ 1094be32e7f1SMilanka Ringwald event[pos++] = btstack_atoi((char *)items[attr_id-1].value); 1095be32e7f1SMilanka Ringwald } else { 1096be32e7f1SMilanka Ringwald event[pos++] = 0; 1097be32e7f1SMilanka Ringwald } 1098be32e7f1SMilanka Ringwald break; 1099be32e7f1SMilanka Ringwald } 1100be32e7f1SMilanka Ringwald } 1101be32e7f1SMilanka Ringwald event[1] = pos - 2; 1102be32e7f1SMilanka Ringwald // printf_hexdump(event, pos); 1103be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, pos); 1104be32e7f1SMilanka Ringwald break; 1105be32e7f1SMilanka Ringwald } 1106be32e7f1SMilanka Ringwald default: 1107be32e7f1SMilanka Ringwald break; 1108be32e7f1SMilanka Ringwald } 1109be32e7f1SMilanka Ringwald break; 1110be32e7f1SMilanka Ringwald case AVRCP_CMD_OPCODE_PASS_THROUGH:{ 1111be32e7f1SMilanka Ringwald // 0x80 | connection->cmd_operands[0] 1112be32e7f1SMilanka Ringwald uint8_t operation_id = packet[pos++]; 11139cc1f3ceSMilanka Ringwald switch (connection->state){ 11149cc1f3ceSMilanka Ringwald case AVCTP_W2_RECEIVE_PRESS_RESPONSE: 1115d1845da3SMilanka Ringwald if (connection->continuous_fast_forward_cmd){ 11169cc1f3ceSMilanka Ringwald connection->state = AVCTP_W4_STOP; 1117d1845da3SMilanka Ringwald } else { 11189cc1f3ceSMilanka Ringwald connection->state = AVCTP_W2_SEND_RELEASE_COMMAND; 11199cc1f3ceSMilanka Ringwald } 11209cc1f3ceSMilanka Ringwald break; 11219cc1f3ceSMilanka Ringwald case AVCTP_W2_RECEIVE_RESPONSE: 11229cc1f3ceSMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 11239cc1f3ceSMilanka Ringwald break; 11249cc1f3ceSMilanka Ringwald default: 11259cc1f3ceSMilanka Ringwald // check for notifications? move state transition down 1126ac98fce4SMilanka Ringwald // log_info("AVRCP_CMD_OPCODE_PASS_THROUGH state %d\n", connection->state); 11279cc1f3ceSMilanka Ringwald break; 11289cc1f3ceSMilanka Ringwald } 1129be32e7f1SMilanka Ringwald if (connection->state == AVCTP_W4_STOP){ 11306086246cSMilanka Ringwald avrcp_emit_operation_status(avrcp_callback, AVRCP_SUBEVENT_OPERATION_START, connection->avrcp_cid, ctype, operation_id); 1131be32e7f1SMilanka Ringwald } 1132be32e7f1SMilanka Ringwald if (connection->state == AVCTP_CONNECTION_OPENED) { 1133be32e7f1SMilanka Ringwald // RELEASE response 1134be32e7f1SMilanka Ringwald operation_id = operation_id & 0x7F; 11356086246cSMilanka Ringwald avrcp_emit_operation_status(avrcp_callback, AVRCP_SUBEVENT_OPERATION_COMPLETE, connection->avrcp_cid, ctype, operation_id); 1136be32e7f1SMilanka Ringwald } 1137be32e7f1SMilanka Ringwald if (connection->state == AVCTP_W2_SEND_RELEASE_COMMAND){ 1138be32e7f1SMilanka Ringwald // PRESS response 1139be32e7f1SMilanka Ringwald request_pass_through_release_control_cmd(connection); 1140be32e7f1SMilanka Ringwald } 1141be32e7f1SMilanka Ringwald break; 1142be32e7f1SMilanka Ringwald } 1143be32e7f1SMilanka Ringwald default: 1144be32e7f1SMilanka Ringwald break; 1145be32e7f1SMilanka Ringwald } 1146be32e7f1SMilanka Ringwald } 1147be32e7f1SMilanka Ringwald 1148be32e7f1SMilanka Ringwald static void avrcp_handle_can_send_now(avrcp_connection_t * connection){ 1149be32e7f1SMilanka Ringwald int i; 1150be32e7f1SMilanka Ringwald switch (connection->state){ 1151be32e7f1SMilanka Ringwald case AVCTP_W2_SEND_PRESS_COMMAND: 1152be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_RECEIVE_PRESS_RESPONSE; 1153b193c45eSMilanka Ringwald avrcp_send_cmd(connection->l2cap_signaling_cid, connection); 1154be32e7f1SMilanka Ringwald break; 1155be32e7f1SMilanka Ringwald case AVCTP_W2_SEND_COMMAND: 1156be32e7f1SMilanka Ringwald case AVCTP_W2_SEND_RELEASE_COMMAND: 1157be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_RECEIVE_RESPONSE; 1158b193c45eSMilanka Ringwald avrcp_send_cmd(connection->l2cap_signaling_cid, connection); 1159be32e7f1SMilanka Ringwald break; 1160be32e7f1SMilanka Ringwald case AVCTP_CONNECTION_OPENED: 1161be32e7f1SMilanka Ringwald if (connection->notifications_to_register != 0){ 1162d1845da3SMilanka Ringwald for (i = 1; i <= AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED; i++){ 1163be32e7f1SMilanka Ringwald if (connection->notifications_to_register & (1<<i)){ 116473264fe8SMatthias Ringwald connection->notifications_to_register &= ~ (1 << i); 1165d5ff3b26SMatthias Ringwald avrcp_prepare_notification(connection, (avrcp_notification_event_id_t) i); 1166294335f1SMilanka Ringwald connection->state = AVCTP_W2_RECEIVE_RESPONSE; 1167be32e7f1SMilanka Ringwald avrcp_send_cmd(connection->l2cap_signaling_cid, connection); 1168be32e7f1SMilanka Ringwald return; 1169be32e7f1SMilanka Ringwald } 1170be32e7f1SMilanka Ringwald } 1171be32e7f1SMilanka Ringwald } 1172be32e7f1SMilanka Ringwald return; 1173be32e7f1SMilanka Ringwald default: 1174be32e7f1SMilanka Ringwald return; 1175be32e7f1SMilanka Ringwald } 1176be32e7f1SMilanka Ringwald } 1177be32e7f1SMilanka Ringwald 11786086246cSMilanka Ringwald static avrcp_connection_t * avrcp_create_connection(bd_addr_t remote_addr, avrcp_context_t * context){ 1179be32e7f1SMilanka Ringwald avrcp_connection_t * connection = btstack_memory_avrcp_connection_get(); 1180be32e7f1SMilanka Ringwald memset(connection, 0, sizeof(avrcp_connection_t)); 1181be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_IDLE; 1182be32e7f1SMilanka Ringwald connection->transaction_label = 0xFF; 1183be32e7f1SMilanka Ringwald memcpy(connection->remote_addr, remote_addr, 6); 11846086246cSMilanka Ringwald btstack_linked_list_add(&context->connections, (btstack_linked_item_t *) connection); 1185be32e7f1SMilanka Ringwald return connection; 1186be32e7f1SMilanka Ringwald } 1187be32e7f1SMilanka Ringwald 11886086246cSMilanka Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size, avrcp_context_t * context){ 1189be32e7f1SMilanka Ringwald bd_addr_t event_addr; 1190be32e7f1SMilanka Ringwald uint16_t local_cid; 1191be32e7f1SMilanka Ringwald avrcp_connection_t * connection = NULL; 1192be32e7f1SMilanka Ringwald 1193be32e7f1SMilanka Ringwald switch (packet_type) { 1194be32e7f1SMilanka Ringwald case L2CAP_DATA_PACKET: 11956086246cSMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, context); 1196be32e7f1SMilanka Ringwald if (!connection) break; 1197be32e7f1SMilanka Ringwald avrcp_handle_l2cap_data_packet_for_signaling_connection(connection, packet, size); 1198be32e7f1SMilanka Ringwald break; 1199be32e7f1SMilanka Ringwald 1200be32e7f1SMilanka Ringwald case HCI_EVENT_PACKET: 1201be32e7f1SMilanka Ringwald switch (hci_event_packet_get_type(packet)) { 1202be32e7f1SMilanka Ringwald case L2CAP_EVENT_INCOMING_CONNECTION: 1203be32e7f1SMilanka Ringwald l2cap_event_incoming_connection_get_address(packet, event_addr); 1204be32e7f1SMilanka Ringwald local_cid = l2cap_event_incoming_connection_get_local_cid(packet); 12056086246cSMilanka Ringwald connection = avrcp_create_connection(event_addr, context); 12060f76c2d7SMatthias Ringwald if (!connection) { 12070f76c2d7SMatthias Ringwald log_error("Failed to alloc connection structure"); 12080f76c2d7SMatthias Ringwald l2cap_decline_connection(local_cid); 1209be32e7f1SMilanka Ringwald break; 1210be32e7f1SMilanka Ringwald } 12110f76c2d7SMatthias Ringwald connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED; 12120f76c2d7SMatthias Ringwald connection->l2cap_signaling_cid = local_cid; 12130f76c2d7SMatthias Ringwald l2cap_accept_connection(local_cid); 1214be32e7f1SMilanka Ringwald break; 1215be32e7f1SMilanka Ringwald 1216be32e7f1SMilanka Ringwald case L2CAP_EVENT_CHANNEL_OPENED: 1217be32e7f1SMilanka Ringwald l2cap_event_channel_opened_get_address(packet, event_addr); 1218be32e7f1SMilanka Ringwald 12196086246cSMilanka Ringwald connection = get_avrcp_connection_for_bd_addr(event_addr, context); 12200f76c2d7SMatthias Ringwald local_cid = l2cap_event_channel_opened_get_local_cid(packet); 12210f76c2d7SMatthias Ringwald 12226086246cSMilanka Ringwald if (!connection){ 12230f76c2d7SMatthias Ringwald // incoming connection 12246086246cSMilanka Ringwald connection = avrcp_create_connection(event_addr, context); 12250f76c2d7SMatthias Ringwald if (!connection) { 12260f76c2d7SMatthias Ringwald log_error("Failed to alloc connection structure"); 12270f76c2d7SMatthias Ringwald l2cap_disconnect(local_cid, 0); // reason isn't used 1228b193c45eSMilanka Ringwald break; 1229b193c45eSMilanka Ringwald } 12306086246cSMilanka Ringwald connection->l2cap_signaling_cid = local_cid; 12316086246cSMilanka Ringwald connection->avrcp_cid = avrcp_cid_counter++; 12320f76c2d7SMatthias Ringwald } 1233b193c45eSMilanka Ringwald 12346086246cSMilanka Ringwald if (l2cap_event_channel_opened_get_status(packet)){ 12356086246cSMilanka Ringwald log_info("L2CAP connection to connection %s failed. status code 0x%02x", 12366086246cSMilanka Ringwald bd_addr_to_str(event_addr), l2cap_event_channel_opened_get_status(packet)); 12376086246cSMilanka Ringwald if (connection->state == AVCTP_CONNECTION_W4_L2CAP_CONNECTED && connection->l2cap_signaling_cid == local_cid){ 12386086246cSMilanka Ringwald avrcp_emit_connection_established(avrcp_callback, connection->avrcp_cid, event_addr, l2cap_event_channel_opened_get_status(packet)); 12396086246cSMilanka Ringwald } 12406086246cSMilanka Ringwald // free connection 12416086246cSMilanka Ringwald btstack_linked_list_remove(&context->connections, (btstack_linked_item_t*) connection); 12426086246cSMilanka Ringwald btstack_memory_avrcp_connection_free(connection); 12436086246cSMilanka Ringwald break; 12446086246cSMilanka Ringwald } 1245be32e7f1SMilanka Ringwald connection->l2cap_signaling_cid = local_cid; 12466086246cSMilanka Ringwald log_info("L2CAP_EVENT_CHANNEL_OPENED avrcp_cid 0x%02x, l2cap_signaling_cid 0x%02x",connection->avrcp_cid, connection->l2cap_signaling_cid); 1247be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 12486086246cSMilanka Ringwald avrcp_emit_connection_established(avrcp_callback, connection->avrcp_cid, event_addr, ERROR_CODE_SUCCESS); 1249be32e7f1SMilanka Ringwald break; 1250be32e7f1SMilanka Ringwald 1251be32e7f1SMilanka Ringwald case L2CAP_EVENT_CAN_SEND_NOW: 12526086246cSMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, context); 1253be32e7f1SMilanka Ringwald if (!connection) break; 1254be32e7f1SMilanka Ringwald avrcp_handle_can_send_now(connection); 1255be32e7f1SMilanka Ringwald break; 1256be32e7f1SMilanka Ringwald 1257be32e7f1SMilanka Ringwald case L2CAP_EVENT_CHANNEL_CLOSED: 1258be32e7f1SMilanka Ringwald // data: event (8), len(8), channel (16) 1259be32e7f1SMilanka Ringwald local_cid = l2cap_event_channel_closed_get_local_cid(packet); 12606086246cSMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(local_cid, context); 1261be32e7f1SMilanka Ringwald if (connection){ 12626086246cSMilanka Ringwald avrcp_emit_connection_closed(avrcp_callback, connection->avrcp_cid); 12636f43fcd7SMatthias Ringwald // free connection 12646086246cSMilanka Ringwald btstack_linked_list_remove(&context->connections, (btstack_linked_item_t*) connection); 12656f43fcd7SMatthias Ringwald btstack_memory_avrcp_connection_free(connection); 1266be32e7f1SMilanka Ringwald break; 1267be32e7f1SMilanka Ringwald } 1268be32e7f1SMilanka Ringwald break; 1269be32e7f1SMilanka Ringwald default: 1270be32e7f1SMilanka Ringwald break; 1271be32e7f1SMilanka Ringwald } 1272be32e7f1SMilanka Ringwald break; 1273be32e7f1SMilanka Ringwald default: 1274be32e7f1SMilanka Ringwald break; 1275be32e7f1SMilanka Ringwald } 1276be32e7f1SMilanka Ringwald } 1277be32e7f1SMilanka Ringwald 12786086246cSMilanka Ringwald static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 12796086246cSMilanka Ringwald packet_handler(packet_type, channel, packet, size, &avrcp_controller_context); 12806086246cSMilanka Ringwald } 12816086246cSMilanka Ringwald 12826086246cSMilanka Ringwald void avrcp_controller_init(void){ 12836086246cSMilanka Ringwald avrcp_controller_context.role = AVRCP_CONTROLLER; 12846086246cSMilanka Ringwald avrcp_controller_context.connections = NULL; 12856086246cSMilanka Ringwald avrcp_controller_context.avrcp_callback = avrcp_callback; 12866086246cSMilanka Ringwald avrcp_controller_context.packet_handler = avrcp_controller_packet_handler; 1287a0f524f0SMatthias Ringwald l2cap_register_service(&avrcp_controller_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_0); 1288be32e7f1SMilanka Ringwald } 1289be32e7f1SMilanka Ringwald 1290be32e7f1SMilanka Ringwald void avrcp_register_packet_handler(btstack_packet_handler_t callback){ 1291be32e7f1SMilanka Ringwald if (callback == NULL){ 1292be32e7f1SMilanka Ringwald log_error("avrcp_register_packet_handler called with NULL callback"); 1293be32e7f1SMilanka Ringwald return; 1294be32e7f1SMilanka Ringwald } 1295be32e7f1SMilanka Ringwald avrcp_callback = callback; 1296df642728SMilanka Ringwald avrcp_controller_context.avrcp_callback = avrcp_callback; 1297be32e7f1SMilanka Ringwald } 1298be32e7f1SMilanka Ringwald 12996086246cSMilanka Ringwald uint8_t avrcp_connect(bd_addr_t bd_addr, avrcp_context_t * context, uint16_t * avrcp_cid){ 13006086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_bd_addr(bd_addr, context); 13018a2c6c7cSMilanka Ringwald if (connection){ 13028a2c6c7cSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 13038a2c6c7cSMilanka Ringwald } 13048a2c6c7cSMilanka Ringwald 13056086246cSMilanka Ringwald connection = avrcp_create_connection(bd_addr, context); 1306be32e7f1SMilanka Ringwald if (!connection){ 13078a2c6c7cSMilanka Ringwald log_error("avrcp: could not allocate connection struct."); 13088a2c6c7cSMilanka Ringwald return BTSTACK_MEMORY_ALLOC_FAILED; 1309be32e7f1SMilanka Ringwald } 13108a2c6c7cSMilanka Ringwald 13116086246cSMilanka Ringwald if (!avrcp_cid) return L2CAP_LOCAL_CID_DOES_NOT_EXIST; 13126086246cSMilanka Ringwald 13136086246cSMilanka Ringwald *avrcp_cid = avrcp_cid_counter++; 13146086246cSMilanka Ringwald connection->avrcp_cid = *avrcp_cid; 13156086246cSMilanka Ringwald 13166086246cSMilanka Ringwald connection->state = AVCTP_SIGNALING_W4_SDP_QUERY_COMPLETE; 1317df642728SMilanka Ringwald sdp_query_context.avrcp_context = context; 13186086246cSMilanka Ringwald sdp_query_context.avrcp_l2cap_psm = 0; 13196086246cSMilanka Ringwald sdp_query_context.avrcp_version = 0; 1320df642728SMilanka Ringwald sdp_query_context.connection = connection; 13216086246cSMilanka Ringwald 13226086246cSMilanka Ringwald sdp_client_query_uuid16(&avrcp_handle_sdp_client_query_result, bd_addr, BLUETOOTH_PROTOCOL_AVCTP); 13238a2c6c7cSMilanka Ringwald return ERROR_CODE_SUCCESS; 1324be32e7f1SMilanka Ringwald } 1325be32e7f1SMilanka Ringwald 13266086246cSMilanka Ringwald uint8_t avrcp_controller_connect(bd_addr_t bd_addr, uint16_t * avrcp_cid){ 13276086246cSMilanka Ringwald return avrcp_connect(bd_addr, &avrcp_controller_context, avrcp_cid); 13286086246cSMilanka Ringwald } 13296086246cSMilanka Ringwald 133031a5a745SMatthias Ringwald uint8_t avrcp_unit_info(uint16_t avrcp_cid){ 13316086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1332be32e7f1SMilanka Ringwald if (!connection){ 1333be32e7f1SMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 133431a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1335be32e7f1SMilanka Ringwald } 133631a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1337be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1338be32e7f1SMilanka Ringwald 1339be32e7f1SMilanka Ringwald connection->transaction_label++; 13409cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_UNIT_INFO; 1341be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1342be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique 1343be32e7f1SMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE; 1344adcdded9SMilanka Ringwald memset(connection->cmd_operands, 0xFF, connection->cmd_operands_length); 1345adcdded9SMilanka Ringwald connection->cmd_operands_length = 5; 1346be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 134731a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1348be32e7f1SMilanka Ringwald } 1349be32e7f1SMilanka Ringwald 135031a5a745SMatthias Ringwald static uint8_t avrcp_get_capabilities(uint16_t avrcp_cid, uint8_t capability_id){ 13516086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1352be32e7f1SMilanka Ringwald if (!connection){ 1353be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 135431a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1355be32e7f1SMilanka Ringwald } 135631a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1357be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1358be32e7f1SMilanka Ringwald 1359be32e7f1SMilanka Ringwald connection->transaction_label++; 13609cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1361be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1362be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1363d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 1364be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); 1365be32e7f1SMilanka Ringwald connection->cmd_operands[3] = AVRCP_PDU_ID_GET_CAPABILITIES; // PDU ID 1366be32e7f1SMilanka Ringwald connection->cmd_operands[4] = 0; 1367be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, 5, 1); // parameter length 1368be32e7f1SMilanka Ringwald connection->cmd_operands[7] = capability_id; // capability ID 1369adcdded9SMilanka Ringwald connection->cmd_operands_length = 8; 1370be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 137131a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1372be32e7f1SMilanka Ringwald } 1373be32e7f1SMilanka Ringwald 137431a5a745SMatthias Ringwald uint8_t avrcp_get_supported_company_ids(uint16_t avrcp_cid){ 137531a5a745SMatthias Ringwald return avrcp_get_capabilities(avrcp_cid, AVRCP_CAPABILITY_ID_COMPANY); 1376be32e7f1SMilanka Ringwald } 1377be32e7f1SMilanka Ringwald 137831a5a745SMatthias Ringwald uint8_t avrcp_get_supported_events(uint16_t avrcp_cid){ 137931a5a745SMatthias Ringwald return avrcp_get_capabilities(avrcp_cid, AVRCP_CAPABILITY_ID_EVENT); 1380be32e7f1SMilanka Ringwald } 1381be32e7f1SMilanka Ringwald 1382be32e7f1SMilanka Ringwald 138331a5a745SMatthias Ringwald uint8_t avrcp_play(uint16_t avrcp_cid){ 1384d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_PLAY, 0); 1385be32e7f1SMilanka Ringwald } 1386be32e7f1SMilanka Ringwald 138731a5a745SMatthias Ringwald uint8_t avrcp_stop(uint16_t avrcp_cid){ 1388d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_STOP, 0); 1389be32e7f1SMilanka Ringwald } 1390be32e7f1SMilanka Ringwald 139131a5a745SMatthias Ringwald uint8_t avrcp_pause(uint16_t avrcp_cid){ 1392d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_PAUSE, 0); 1393be32e7f1SMilanka Ringwald } 1394be32e7f1SMilanka Ringwald 139531a5a745SMatthias Ringwald uint8_t avrcp_forward(uint16_t avrcp_cid){ 1396d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FORWARD, 0); 1397be32e7f1SMilanka Ringwald } 1398be32e7f1SMilanka Ringwald 139931a5a745SMatthias Ringwald uint8_t avrcp_backward(uint16_t avrcp_cid){ 1400d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_BACKWARD, 0); 1401be32e7f1SMilanka Ringwald } 1402be32e7f1SMilanka Ringwald 140331a5a745SMatthias Ringwald uint8_t avrcp_start_rewind(uint16_t avrcp_cid){ 1404d1845da3SMilanka Ringwald return request_continuous_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_REWIND, 0); 1405be32e7f1SMilanka Ringwald } 1406be32e7f1SMilanka Ringwald 140731a5a745SMatthias Ringwald uint8_t avrcp_volume_up(uint16_t avrcp_cid){ 1408d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_VOLUME_UP, 0); 1409be32e7f1SMilanka Ringwald } 1410be32e7f1SMilanka Ringwald 141131a5a745SMatthias Ringwald uint8_t avrcp_volume_down(uint16_t avrcp_cid){ 1412d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_VOLUME_DOWN, 0); 1413be32e7f1SMilanka Ringwald } 1414be32e7f1SMilanka Ringwald 141531a5a745SMatthias Ringwald uint8_t avrcp_mute(uint16_t avrcp_cid){ 1416d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_MUTE, 0); 1417be32e7f1SMilanka Ringwald } 1418be32e7f1SMilanka Ringwald 141931a5a745SMatthias Ringwald uint8_t avrcp_skip(uint16_t avrcp_cid){ 1420d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_SKIP, 0); 1421be32e7f1SMilanka Ringwald } 1422be32e7f1SMilanka Ringwald 142331a5a745SMatthias Ringwald uint8_t avrcp_stop_rewind(uint16_t avrcp_cid){ 14246086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1425be32e7f1SMilanka Ringwald if (!connection){ 1426be32e7f1SMilanka Ringwald log_error("avrcp_stop_rewind: could not find a connection."); 142731a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1428be32e7f1SMilanka Ringwald } 142931a5a745SMatthias Ringwald if (connection->state != AVCTP_W4_STOP) return ERROR_CODE_COMMAND_DISALLOWED; 143031a5a745SMatthias Ringwald return request_pass_through_release_control_cmd(connection); 1431be32e7f1SMilanka Ringwald } 1432be32e7f1SMilanka Ringwald 143331a5a745SMatthias Ringwald uint8_t avrcp_start_fast_forward(uint16_t avrcp_cid){ 1434d1845da3SMilanka Ringwald return request_continuous_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FAST_FORWARD, 0); 1435be32e7f1SMilanka Ringwald } 1436be32e7f1SMilanka Ringwald 1437d1845da3SMilanka Ringwald uint8_t avrcp_fast_forward(uint16_t avrcp_cid){ 1438d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FAST_FORWARD, 0); 1439d1845da3SMilanka Ringwald } 1440d1845da3SMilanka Ringwald 1441d1845da3SMilanka Ringwald uint8_t avrcp_rewind(uint16_t avrcp_cid){ 1442d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_REWIND, 0); 1443d1845da3SMilanka Ringwald } 1444d1845da3SMilanka Ringwald 1445d1845da3SMilanka Ringwald 144631a5a745SMatthias Ringwald uint8_t avrcp_stop_fast_forward(uint16_t avrcp_cid){ 14476086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1448be32e7f1SMilanka Ringwald if (!connection){ 1449be32e7f1SMilanka Ringwald log_error("avrcp_stop_fast_forward: could not find a connection."); 145031a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1451be32e7f1SMilanka Ringwald } 145231a5a745SMatthias Ringwald if (connection->state != AVCTP_W4_STOP) return ERROR_CODE_COMMAND_DISALLOWED; 145331a5a745SMatthias Ringwald return request_pass_through_release_control_cmd(connection); 1454be32e7f1SMilanka Ringwald } 1455be32e7f1SMilanka Ringwald 145631a5a745SMatthias Ringwald uint8_t avrcp_get_play_status(uint16_t avrcp_cid){ 14576086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1458be32e7f1SMilanka Ringwald if (!connection){ 1459be32e7f1SMilanka Ringwald log_error("avrcp_get_play_status: could not find a connection."); 146031a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1461be32e7f1SMilanka Ringwald } 146231a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1463be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1464be32e7f1SMilanka Ringwald connection->transaction_label++; 14659cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1466be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1467be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1468d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 1469be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); 1470be32e7f1SMilanka Ringwald connection->cmd_operands[3] = AVRCP_PDU_ID_GET_PLAY_STATUS; 1471be32e7f1SMilanka Ringwald connection->cmd_operands[4] = 0; // reserved(upper 6) | packet_type -> 0 1472be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, 5, 0); // parameter length 1473adcdded9SMilanka Ringwald connection->cmd_operands_length = 7; 1474be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 147531a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1476be32e7f1SMilanka Ringwald } 1477be32e7f1SMilanka Ringwald 147831a5a745SMatthias Ringwald uint8_t avrcp_enable_notification(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id){ 14796086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1480be32e7f1SMilanka Ringwald if (!connection){ 1481be32e7f1SMilanka Ringwald log_error("avrcp_get_play_status: could not find a connection."); 148231a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1483be32e7f1SMilanka Ringwald } 1484be32e7f1SMilanka Ringwald avrcp_register_notification(connection, event_id); 148531a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1486be32e7f1SMilanka Ringwald } 1487be32e7f1SMilanka Ringwald 148831a5a745SMatthias Ringwald uint8_t avrcp_disable_notification(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id){ 14896086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1490be32e7f1SMilanka Ringwald if (!connection){ 1491be32e7f1SMilanka Ringwald log_error("avrcp_get_play_status: could not find a connection."); 149231a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1493be32e7f1SMilanka Ringwald } 1494be32e7f1SMilanka Ringwald connection->notifications_to_deregister |= (1 << event_id); 149531a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1496be32e7f1SMilanka Ringwald } 1497be32e7f1SMilanka Ringwald 149831a5a745SMatthias Ringwald uint8_t avrcp_get_now_playing_info(uint16_t avrcp_cid){ 14996086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1500be32e7f1SMilanka Ringwald if (!connection){ 1501be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 150231a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1503be32e7f1SMilanka Ringwald } 150431a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1505be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1506be32e7f1SMilanka Ringwald 1507be32e7f1SMilanka Ringwald connection->transaction_label++; 15089cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1509be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1510be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1511d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 1512be32e7f1SMilanka Ringwald int pos = 0; 1513be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 1514be32e7f1SMilanka Ringwald pos += 3; 1515be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES; // PDU ID 1516be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; 1517be32e7f1SMilanka Ringwald 1518be32e7f1SMilanka Ringwald // Parameter Length 1519be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 9); 1520be32e7f1SMilanka Ringwald pos += 2; 1521be32e7f1SMilanka Ringwald 1522be32e7f1SMilanka Ringwald // write 8 bytes value 1523be32e7f1SMilanka Ringwald memset(connection->cmd_operands + pos, 0, 8); // identifier: PLAYING 1524be32e7f1SMilanka Ringwald pos += 8; 1525be32e7f1SMilanka Ringwald 1526be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; // attribute count, if 0 get all attributes 1527be32e7f1SMilanka Ringwald // every attribute is 4 bytes long 1528be32e7f1SMilanka Ringwald 1529adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 1530be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 153131a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1532be32e7f1SMilanka Ringwald } 1533be32e7f1SMilanka Ringwald 153431a5a745SMatthias Ringwald uint8_t avrcp_set_absolute_volume(uint16_t avrcp_cid, uint8_t volume){ 15356086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1536be32e7f1SMilanka Ringwald if (!connection){ 1537be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 153831a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1539be32e7f1SMilanka Ringwald } 154031a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1541be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1542be32e7f1SMilanka Ringwald 1543be32e7f1SMilanka Ringwald connection->transaction_label++; 15449cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1545be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_CONTROL; 1546be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1547d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 1548be32e7f1SMilanka Ringwald int pos = 0; 1549be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 1550be32e7f1SMilanka Ringwald pos += 3; 1551be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME; // PDU ID 1552be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; 1553be32e7f1SMilanka Ringwald 1554be32e7f1SMilanka Ringwald // Parameter Length 1555be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 1); 1556be32e7f1SMilanka Ringwald pos += 2; 1557be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = volume; 1558be32e7f1SMilanka Ringwald 1559adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 1560be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 156131a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1562be32e7f1SMilanka Ringwald } 1563be32e7f1SMilanka Ringwald 156431a5a745SMatthias Ringwald uint8_t avrcp_query_shuffle_and_repeat_modes(uint16_t avrcp_cid){ 15656086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1566be32e7f1SMilanka Ringwald if (!connection){ 1567be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 156831a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1569be32e7f1SMilanka Ringwald } 157031a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1571be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1572be32e7f1SMilanka Ringwald 1573be32e7f1SMilanka Ringwald connection->transaction_label++; 15749cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1575be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1576be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1577d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 1578be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); 1579be32e7f1SMilanka Ringwald connection->cmd_operands[3] = AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue; // PDU ID 1580be32e7f1SMilanka Ringwald connection->cmd_operands[4] = 0; 1581be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, 5, 5); // parameter length 1582be32e7f1SMilanka Ringwald connection->cmd_operands[7] = 4; // NumPlayerApplicationSettingAttributeID 1583be32e7f1SMilanka Ringwald // PlayerApplicationSettingAttributeID1 AVRCP Spec, Appendix F, 133 1584be32e7f1SMilanka Ringwald connection->cmd_operands[8] = 0x01; // equalizer (1-OFF, 2-ON) 1585be32e7f1SMilanka Ringwald connection->cmd_operands[9] = 0x02; // repeat (1-off, 2-single track, 3-all tracks, 4-group repeat) 1586be32e7f1SMilanka Ringwald connection->cmd_operands[10] = 0x03; // shuffle (1-off, 2-all tracks, 3-group shuffle) 1587be32e7f1SMilanka Ringwald connection->cmd_operands[11] = 0x04; // scan (1-off, 2-all tracks, 3-group scan) 1588adcdded9SMilanka Ringwald connection->cmd_operands_length = 12; 1589be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 159031a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1591be32e7f1SMilanka Ringwald } 1592be32e7f1SMilanka Ringwald 15936086246cSMilanka Ringwald static uint8_t avrcp_set_current_player_application_setting_value(uint16_t avrcp_cid, uint8_t attr_id, uint8_t attr_value){ 15946086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1595be32e7f1SMilanka Ringwald if (!connection){ 1596be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 159731a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1598be32e7f1SMilanka Ringwald } 159931a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1600be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1601be32e7f1SMilanka Ringwald 1602be32e7f1SMilanka Ringwald connection->transaction_label++; 16039cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1604be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_CONTROL; 1605be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1606d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 1607be32e7f1SMilanka Ringwald int pos = 0; 1608be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 1609be32e7f1SMilanka Ringwald pos += 3; 1610be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_SetPlayerApplicationSettingValue; // PDU ID 1611be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; 1612be32e7f1SMilanka Ringwald // Parameter Length 1613be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 3); 1614be32e7f1SMilanka Ringwald pos += 2; 1615be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 2; 1616adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 16176086246cSMilanka Ringwald connection->cmd_operands[pos++] = attr_id; 16186086246cSMilanka Ringwald connection->cmd_operands[pos++] = attr_value; 1619adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 1620be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 162131a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1622be32e7f1SMilanka Ringwald } 1623be32e7f1SMilanka Ringwald 162431a5a745SMatthias Ringwald uint8_t avrcp_set_shuffle_mode(uint16_t avrcp_cid, avrcp_shuffle_mode_t mode){ 162531a5a745SMatthias Ringwald if (mode < AVRCP_SHUFFLE_MODE_OFF || mode > AVRCP_SHUFFLE_MODE_GROUP) return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 162631a5a745SMatthias Ringwald return avrcp_set_current_player_application_setting_value(avrcp_cid, 0x03, mode); 1627be32e7f1SMilanka Ringwald } 1628be32e7f1SMilanka Ringwald 162931a5a745SMatthias Ringwald uint8_t avrcp_set_repeat_mode(uint16_t avrcp_cid, avrcp_repeat_mode_t mode){ 163031a5a745SMatthias Ringwald if (mode < AVRCP_REPEAT_MODE_OFF || mode > AVRCP_REPEAT_MODE_GROUP) return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 163131a5a745SMatthias Ringwald return avrcp_set_current_player_application_setting_value(avrcp_cid, 0x02, mode); 1632be32e7f1SMilanka Ringwald } 1633be32e7f1SMilanka Ringwald 163431a5a745SMatthias Ringwald uint8_t avrcp_disconnect(uint16_t avrcp_cid){ 16356086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1636be32e7f1SMilanka Ringwald if (!connection){ 1637be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 163831a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1639be32e7f1SMilanka Ringwald } 164031a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1641294335f1SMilanka Ringwald l2cap_disconnect(connection->l2cap_signaling_cid, 0); 164231a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1643be32e7f1SMilanka Ringwald }