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" 2775c806868SMatthias Ringwald if (browsing){ 278a0f524f0SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS); 279a0f524f0SMatthias Ringwald attribute = de_push_sequence(service); 2805c806868SMatthias Ringwald { 2815c806868SMatthias Ringwald uint8_t * des = de_push_sequence(attribute); 2825c806868SMatthias Ringwald { 2835c806868SMatthias Ringwald uint8_t* browsing_l2cpProtocol = de_push_sequence(des); 284a0f524f0SMatthias Ringwald { 285a0f524f0SMatthias Ringwald de_add_number(browsing_l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP); 286a0f524f0SMatthias Ringwald de_add_number(browsing_l2cpProtocol, DE_UINT, DE_SIZE_16, PSM_AVCTP_BROWSING); 287a0f524f0SMatthias Ringwald } 2885c806868SMatthias Ringwald de_pop_sequence(des, browsing_l2cpProtocol); 289a0f524f0SMatthias Ringwald 2905c806868SMatthias Ringwald uint8_t* browsing_avctpProtocol = de_push_sequence(des); 291a0f524f0SMatthias Ringwald { 292a0f524f0SMatthias Ringwald de_add_number(browsing_avctpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP); // browsing_avctpProtocol_service 293a0f524f0SMatthias Ringwald de_add_number(browsing_avctpProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version 294a0f524f0SMatthias Ringwald } 2955c806868SMatthias Ringwald de_pop_sequence(des, browsing_avctpProtocol); 2965c806868SMatthias Ringwald } 2975c806868SMatthias Ringwald de_pop_sequence(attribute, des); 298a0f524f0SMatthias Ringwald } 299a0f524f0SMatthias Ringwald de_pop_sequence(service, attribute); 3005c806868SMatthias Ringwald } 301a0f524f0SMatthias Ringwald 302be32e7f1SMilanka Ringwald 303be32e7f1SMilanka Ringwald // 0x0100 "Service Name" 304be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); 305be32e7f1SMilanka Ringwald if (service_name){ 306be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(service_name), (uint8_t *) service_name); 307be32e7f1SMilanka Ringwald } else { 308be32e7f1SMilanka Ringwald if (controller){ 309be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_controller_service_name), (uint8_t *) default_avrcp_controller_service_name); 310be32e7f1SMilanka Ringwald } else { 311be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_target_service_name), (uint8_t *) default_avrcp_target_service_name); 312be32e7f1SMilanka Ringwald } 313be32e7f1SMilanka Ringwald } 314be32e7f1SMilanka Ringwald 315be32e7f1SMilanka Ringwald // 0x0100 "Provider Name" 316be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102); 317be32e7f1SMilanka Ringwald if (service_provider_name){ 318be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name); 319be32e7f1SMilanka Ringwald } else { 320be32e7f1SMilanka Ringwald if (controller){ 321be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_controller_service_provider_name), (uint8_t *) default_avrcp_controller_service_provider_name); 322be32e7f1SMilanka Ringwald } else { 323be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_target_service_provider_name), (uint8_t *) default_avrcp_target_service_provider_name); 324be32e7f1SMilanka Ringwald } 325be32e7f1SMilanka Ringwald } 326be32e7f1SMilanka Ringwald 327be32e7f1SMilanka Ringwald // 0x0311 "Supported Features" 328be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); 329be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); 330be32e7f1SMilanka Ringwald } 331be32e7f1SMilanka Ringwald 332be32e7f1SMilanka 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){ 333be32e7f1SMilanka Ringwald avrcp_create_sdp_record(1, service, service_record_handle, browsing, supported_features, service_name, service_provider_name); 334be32e7f1SMilanka Ringwald } 335be32e7f1SMilanka Ringwald 336be32e7f1SMilanka 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){ 337be32e7f1SMilanka Ringwald avrcp_create_sdp_record(0, service, service_record_handle, browsing, supported_features, service_name, service_provider_name); 338be32e7f1SMilanka Ringwald } 339be32e7f1SMilanka Ringwald 3406086246cSMilanka Ringwald static void avrcp_emit_connection_established(btstack_packet_handler_t callback, uint16_t avrcp_cid, bd_addr_t addr, uint8_t status){ 341b193c45eSMilanka Ringwald if (!callback) return; 3426086246cSMilanka Ringwald uint8_t event[12]; 343b193c45eSMilanka Ringwald int pos = 0; 344b193c45eSMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 345b193c45eSMilanka Ringwald event[pos++] = sizeof(event) - 2; 346b193c45eSMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_CONNECTION_ESTABLISHED; 3476f43fcd7SMatthias Ringwald event[pos++] = status; 348b193c45eSMilanka Ringwald reverse_bd_addr(addr,&event[pos]); 349b193c45eSMilanka Ringwald pos += 6; 350b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 351b193c45eSMilanka Ringwald pos += 2; 352b193c45eSMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 353b193c45eSMilanka Ringwald } 354b193c45eSMilanka Ringwald 355d3b6a85eSMatthias 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){ 356be32e7f1SMilanka Ringwald if (!callback) return; 357be32e7f1SMilanka Ringwald uint8_t event[8]; 358be32e7f1SMilanka Ringwald int pos = 0; 359be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 360be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 361be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE; 362b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 363be32e7f1SMilanka Ringwald pos += 2; 364d3b6a85eSMatthias Ringwald event[pos++] = ctype; 365be32e7f1SMilanka Ringwald event[pos++] = repeat_mode; 366be32e7f1SMilanka Ringwald event[pos++] = shuffle_mode; 367be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 368be32e7f1SMilanka Ringwald } 369be32e7f1SMilanka Ringwald 370d3b6a85eSMatthias 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){ 371be32e7f1SMilanka Ringwald if (!callback) return; 372be32e7f1SMilanka Ringwald uint8_t event[7]; 373be32e7f1SMilanka Ringwald int pos = 0; 374be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 375be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 376be32e7f1SMilanka Ringwald event[pos++] = subevent; 377b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 378be32e7f1SMilanka Ringwald pos += 2; 379d3b6a85eSMatthias Ringwald event[pos++] = ctype; 380be32e7f1SMilanka Ringwald event[pos++] = operation_id; 381be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 382be32e7f1SMilanka Ringwald } 383be32e7f1SMilanka Ringwald 384b193c45eSMilanka Ringwald static void avrcp_emit_connection_closed(btstack_packet_handler_t callback, uint16_t avrcp_cid){ 385be32e7f1SMilanka Ringwald if (!callback) return; 386be32e7f1SMilanka Ringwald uint8_t event[5]; 387be32e7f1SMilanka Ringwald int pos = 0; 388be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 389be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 390be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_CONNECTION_RELEASED; 391b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 392be32e7f1SMilanka Ringwald pos += 2; 393be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 394be32e7f1SMilanka Ringwald } 395be32e7f1SMilanka Ringwald 3966086246cSMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_bd_addr(bd_addr_t addr, avrcp_context_t * context){ 397be32e7f1SMilanka Ringwald btstack_linked_list_iterator_t it; 3986086246cSMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &context->connections); 399be32e7f1SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 400be32e7f1SMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it); 401be32e7f1SMilanka Ringwald if (memcmp(addr, connection->remote_addr, 6) != 0) continue; 402be32e7f1SMilanka Ringwald return connection; 403be32e7f1SMilanka Ringwald } 404be32e7f1SMilanka Ringwald return NULL; 405be32e7f1SMilanka Ringwald } 406be32e7f1SMilanka Ringwald 4076086246cSMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_l2cap_signaling_cid(uint16_t l2cap_cid, avrcp_context_t * context){ 408be32e7f1SMilanka Ringwald btstack_linked_list_iterator_t it; 4096086246cSMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &context->connections); 410be32e7f1SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 411be32e7f1SMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it); 412be32e7f1SMilanka Ringwald if (connection->l2cap_signaling_cid != l2cap_cid) continue; 413be32e7f1SMilanka Ringwald return connection; 414be32e7f1SMilanka Ringwald } 415be32e7f1SMilanka Ringwald return NULL; 416be32e7f1SMilanka Ringwald } 417be32e7f1SMilanka Ringwald 4186086246cSMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_avrcp_cid(uint16_t l2cap_cid, avrcp_context_t * context){ 4196086246cSMilanka Ringwald btstack_linked_list_iterator_t it; 4206086246cSMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &context->connections); 4216086246cSMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 4226086246cSMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it); 4236086246cSMilanka Ringwald if (connection->avrcp_cid != l2cap_cid) continue; 4246086246cSMilanka Ringwald return connection; 4256086246cSMilanka Ringwald } 4266086246cSMilanka Ringwald return NULL; 4276086246cSMilanka Ringwald } 4286086246cSMilanka Ringwald 429be32e7f1SMilanka Ringwald static void avrcp_request_can_send_now(avrcp_connection_t * connection, uint16_t l2cap_cid){ 430be32e7f1SMilanka Ringwald connection->wait_to_send = 1; 431be32e7f1SMilanka Ringwald l2cap_request_can_send_now_event(l2cap_cid); 432be32e7f1SMilanka Ringwald } 433be32e7f1SMilanka Ringwald 434be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timeout_handler(btstack_timer_source_t * timer){ 435be32e7f1SMilanka Ringwald UNUSED(timer); 436be32e7f1SMilanka Ringwald avrcp_connection_t * connection = btstack_run_loop_get_timer_context(timer); 437be32e7f1SMilanka Ringwald btstack_run_loop_set_timer(&connection->press_and_hold_cmd_timer, 2000); // 2 seconds timeout 438be32e7f1SMilanka Ringwald btstack_run_loop_add_timer(&connection->press_and_hold_cmd_timer); 439be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_PRESS_COMMAND; 440be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 441be32e7f1SMilanka Ringwald } 442be32e7f1SMilanka Ringwald 443be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timer_start(avrcp_connection_t * connection){ 444be32e7f1SMilanka Ringwald btstack_run_loop_remove_timer(&connection->press_and_hold_cmd_timer); 445be32e7f1SMilanka Ringwald btstack_run_loop_set_timer_handler(&connection->press_and_hold_cmd_timer, avrcp_press_and_hold_timeout_handler); 446be32e7f1SMilanka Ringwald btstack_run_loop_set_timer_context(&connection->press_and_hold_cmd_timer, connection); 447be32e7f1SMilanka Ringwald btstack_run_loop_set_timer(&connection->press_and_hold_cmd_timer, 2000); // 2 seconds timeout 448be32e7f1SMilanka Ringwald btstack_run_loop_add_timer(&connection->press_and_hold_cmd_timer); 449be32e7f1SMilanka Ringwald } 450be32e7f1SMilanka Ringwald 451be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timer_stop(avrcp_connection_t * connection){ 452d1845da3SMilanka Ringwald connection->continuous_fast_forward_cmd = 0; 453be32e7f1SMilanka Ringwald btstack_run_loop_remove_timer(&connection->press_and_hold_cmd_timer); 454be32e7f1SMilanka Ringwald } 455be32e7f1SMilanka Ringwald 45631a5a745SMatthias Ringwald static uint8_t request_pass_through_release_control_cmd(avrcp_connection_t * connection){ 457be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_RELEASE_COMMAND; 458d1845da3SMilanka Ringwald if (connection->continuous_fast_forward_cmd){ 459be32e7f1SMilanka Ringwald avrcp_press_and_hold_timer_stop(connection); 460be32e7f1SMilanka Ringwald } 461be32e7f1SMilanka Ringwald connection->cmd_operands[0] = 0x80 | connection->cmd_operands[0]; 462be32e7f1SMilanka Ringwald connection->transaction_label++; 463be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 46431a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 465be32e7f1SMilanka Ringwald } 466be32e7f1SMilanka Ringwald 4676086246cSMilanka 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){ 4686086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, context); 469be32e7f1SMilanka Ringwald if (!connection){ 470be32e7f1SMilanka Ringwald log_error("avrcp: could not find a connection."); 47131a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 472be32e7f1SMilanka Ringwald } 47331a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 474be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_PRESS_COMMAND; 4759cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_PASS_THROUGH; 476be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_CONTROL; 477be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 478d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 479adcdded9SMilanka Ringwald connection->cmd_operands_length = 0; 480be32e7f1SMilanka Ringwald 481d1845da3SMilanka Ringwald connection->continuous_fast_forward_cmd = continuous_fast_forward_cmd; 482adcdded9SMilanka Ringwald connection->cmd_operands_length = 2; 483be32e7f1SMilanka Ringwald connection->cmd_operands[0] = opid; 484be32e7f1SMilanka Ringwald if (playback_speed > 0){ 485be32e7f1SMilanka Ringwald connection->cmd_operands[2] = playback_speed; 486adcdded9SMilanka Ringwald connection->cmd_operands_length++; 487be32e7f1SMilanka Ringwald } 488adcdded9SMilanka Ringwald connection->cmd_operands[1] = connection->cmd_operands_length - 2; 489be32e7f1SMilanka Ringwald 490d1845da3SMilanka Ringwald if (connection->continuous_fast_forward_cmd){ 491be32e7f1SMilanka Ringwald avrcp_press_and_hold_timer_start(connection); 492be32e7f1SMilanka Ringwald } 493d1845da3SMilanka Ringwald 494be32e7f1SMilanka Ringwald connection->transaction_label++; 495be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 49631a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 497be32e7f1SMilanka Ringwald } 498be32e7f1SMilanka Ringwald 499d1845da3SMilanka Ringwald static uint8_t request_single_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed){ 5006086246cSMilanka Ringwald return request_pass_through_press_control_cmd(avrcp_cid, opid, playback_speed, 0, &avrcp_controller_context); 501d1845da3SMilanka Ringwald } 502d1845da3SMilanka Ringwald 503d1845da3SMilanka Ringwald static uint8_t request_continuous_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed){ 5046086246cSMilanka Ringwald return request_pass_through_press_control_cmd(avrcp_cid, opid, playback_speed, 1, &avrcp_controller_context); 505d1845da3SMilanka Ringwald } 506be32e7f1SMilanka Ringwald 507be32e7f1SMilanka Ringwald static int avrcp_send_cmd(uint16_t cid, avrcp_connection_t * connection){ 508adcdded9SMilanka Ringwald uint8_t command[30]; 509be32e7f1SMilanka Ringwald int pos = 0; 510be32e7f1SMilanka Ringwald // transport header 511be32e7f1SMilanka Ringwald // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 512be32e7f1SMilanka Ringwald command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0; 513be32e7f1SMilanka Ringwald // Profile IDentifier (PID) 5146086246cSMilanka Ringwald command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 5156086246cSMilanka Ringwald command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 516be32e7f1SMilanka Ringwald 517be32e7f1SMilanka Ringwald // command_type 518be32e7f1SMilanka Ringwald command[pos++] = connection->command_type; 519be32e7f1SMilanka Ringwald // subunit_type | subunit ID 520be32e7f1SMilanka Ringwald command[pos++] = (connection->subunit_type << 3) | connection->subunit_id; 521be32e7f1SMilanka Ringwald // opcode 5229cc1f3ceSMilanka Ringwald command[pos++] = (uint8_t)connection->command_opcode; 523be32e7f1SMilanka Ringwald // operands 524adcdded9SMilanka Ringwald memcpy(command+pos, connection->cmd_operands, connection->cmd_operands_length); 525adcdded9SMilanka Ringwald pos += connection->cmd_operands_length; 526be32e7f1SMilanka Ringwald 527be32e7f1SMilanka Ringwald return l2cap_send(cid, command, pos); 528be32e7f1SMilanka Ringwald } 529be32e7f1SMilanka Ringwald 530be32e7f1SMilanka Ringwald static int avrcp_register_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){ 531be32e7f1SMilanka Ringwald if (connection->notifications_to_deregister & (1 << event_id)) return 0; 532be32e7f1SMilanka Ringwald if (connection->notifications_enabled & (1 << event_id)) return 0; 533be32e7f1SMilanka Ringwald if (connection->notifications_to_register & (1 << event_id)) return 0; 534be32e7f1SMilanka Ringwald connection->notifications_to_register |= (1 << event_id); 535be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 536be32e7f1SMilanka Ringwald return 1; 537be32e7f1SMilanka Ringwald } 538be32e7f1SMilanka Ringwald 539be32e7f1SMilanka Ringwald static void avrcp_prepare_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){ 540be32e7f1SMilanka Ringwald connection->transaction_label++; 5419cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 542be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_NOTIFY; 543be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 544d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 545be32e7f1SMilanka Ringwald int pos = 0; 546be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 547be32e7f1SMilanka Ringwald pos += 3; 548be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_REGISTER_NOTIFICATION; 549be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; // reserved(upper 6) | packet_type -> 0 550be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 5); // parameter length 551be32e7f1SMilanka Ringwald pos += 2; 552be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = event_id; 553be32e7f1SMilanka Ringwald big_endian_store_32(connection->cmd_operands, pos, 0); 554be32e7f1SMilanka Ringwald pos += 4; 555adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 556be32e7f1SMilanka Ringwald // AVRCP_SPEC_V14.pdf 166 557be32e7f1SMilanka Ringwald // answer page 61 558be32e7f1SMilanka Ringwald } 559be32e7f1SMilanka Ringwald 5609cc1f3ceSMilanka Ringwald static uint8_t avrcp_cmd_opcode(uint8_t *packet, uint16_t size){ 5619cc1f3ceSMilanka Ringwald uint8_t cmd_opcode_index = 5; 5629cc1f3ceSMilanka Ringwald if (cmd_opcode_index > size) return AVRCP_CMD_OPCODE_UNDEFINED; 5639cc1f3ceSMilanka Ringwald return packet[cmd_opcode_index]; 564be32e7f1SMilanka Ringwald } 565be32e7f1SMilanka Ringwald 5666086246cSMilanka Ringwald static void avrcp_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 5676086246cSMilanka Ringwald if (!sdp_query_context.connection) return; 5686086246cSMilanka Ringwald if (sdp_query_context.connection->state != AVCTP_SIGNALING_W4_SDP_QUERY_COMPLETE) return; 5696086246cSMilanka Ringwald UNUSED(packet_type); 5706086246cSMilanka Ringwald UNUSED(channel); 5716086246cSMilanka Ringwald UNUSED(size); 5726086246cSMilanka Ringwald 5736086246cSMilanka Ringwald des_iterator_t des_list_it; 5746086246cSMilanka Ringwald des_iterator_t prot_it; 5756086246cSMilanka Ringwald // uint32_t avdtp_remote_uuid = 0; 5766086246cSMilanka Ringwald 5776086246cSMilanka Ringwald switch (hci_event_packet_get_type(packet)){ 5786086246cSMilanka Ringwald case SDP_EVENT_QUERY_ATTRIBUTE_VALUE: 5796086246cSMilanka Ringwald // Handle new SDP record 5806086246cSMilanka Ringwald if (sdp_event_query_attribute_byte_get_record_id(packet) != record_id) { 5816086246cSMilanka Ringwald record_id = sdp_event_query_attribute_byte_get_record_id(packet); 5826086246cSMilanka Ringwald // log_info("SDP Record: Nr: %d", record_id); 5836086246cSMilanka Ringwald } 5846086246cSMilanka Ringwald 5856086246cSMilanka Ringwald if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= attribute_value_buffer_size) { 5866086246cSMilanka Ringwald attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet); 5876086246cSMilanka Ringwald 5886086246cSMilanka Ringwald if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) { 5896086246cSMilanka Ringwald switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) { 5906086246cSMilanka Ringwald case BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST: 5916086246cSMilanka Ringwald if (de_get_element_type(attribute_value) != DE_DES) break; 5926086246cSMilanka Ringwald for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) { 5936086246cSMilanka Ringwald uint8_t * element = des_iterator_get_element(&des_list_it); 5946086246cSMilanka Ringwald if (de_get_element_type(element) != DE_UUID) continue; 5956086246cSMilanka Ringwald uint32_t uuid = de_get_uuid32(element); 5966086246cSMilanka Ringwald switch (uuid){ 5976086246cSMilanka Ringwald case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_TARGET: 5984b1ac813SMilanka Ringwald if (sdp_query_context.avrcp_context->role == AVRCP_CONTROLLER) { 599df642728SMilanka Ringwald sdp_query_context.role_supported = 1; 6006086246cSMilanka Ringwald break; 6016086246cSMilanka Ringwald } 6026086246cSMilanka Ringwald break; 6036086246cSMilanka Ringwald case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL: 604df642728SMilanka Ringwald case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_CONTROLLER: 6054b1ac813SMilanka Ringwald if (sdp_query_context.avrcp_context->role == AVRCP_TARGET) { 606df642728SMilanka Ringwald sdp_query_context.role_supported = 1; 6076086246cSMilanka Ringwald break; 6086086246cSMilanka Ringwald } 6096086246cSMilanka Ringwald break; 6106086246cSMilanka Ringwald default: 6116086246cSMilanka Ringwald break; 6126086246cSMilanka Ringwald } 6136086246cSMilanka Ringwald } 6146086246cSMilanka Ringwald break; 6156086246cSMilanka Ringwald 6166086246cSMilanka Ringwald case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST: { 6176086246cSMilanka Ringwald // log_info("SDP Attribute: 0x%04x", sdp_event_query_attribute_byte_get_attribute_id(packet)); 6186086246cSMilanka Ringwald for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) { 6196086246cSMilanka Ringwald uint8_t *des_element; 6206086246cSMilanka Ringwald uint8_t *element; 6216086246cSMilanka Ringwald uint32_t uuid; 6226086246cSMilanka Ringwald 6236086246cSMilanka Ringwald if (des_iterator_get_type(&des_list_it) != DE_DES) continue; 6246086246cSMilanka Ringwald 6256086246cSMilanka Ringwald des_element = des_iterator_get_element(&des_list_it); 6266086246cSMilanka Ringwald des_iterator_init(&prot_it, des_element); 6276086246cSMilanka Ringwald element = des_iterator_get_element(&prot_it); 6286086246cSMilanka Ringwald 6296086246cSMilanka Ringwald if (de_get_element_type(element) != DE_UUID) continue; 6306086246cSMilanka Ringwald 6316086246cSMilanka Ringwald uuid = de_get_uuid32(element); 6326086246cSMilanka Ringwald switch (uuid){ 6336086246cSMilanka Ringwald case BLUETOOTH_PROTOCOL_L2CAP: 6346086246cSMilanka Ringwald if (!des_iterator_has_more(&prot_it)) continue; 6356086246cSMilanka Ringwald des_iterator_next(&prot_it); 6366086246cSMilanka Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context.avrcp_l2cap_psm); 6376086246cSMilanka Ringwald break; 6386086246cSMilanka Ringwald case BLUETOOTH_PROTOCOL_AVCTP: 6396086246cSMilanka Ringwald if (!des_iterator_has_more(&prot_it)) continue; 6406086246cSMilanka Ringwald des_iterator_next(&prot_it); 6416086246cSMilanka Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context.avrcp_version); 6426086246cSMilanka Ringwald break; 6436086246cSMilanka Ringwald default: 6446086246cSMilanka Ringwald break; 6456086246cSMilanka Ringwald } 6466086246cSMilanka Ringwald } 6476086246cSMilanka Ringwald } 6486086246cSMilanka Ringwald break; 649227d16a5SMatthias Ringwald case BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: { 650227d16a5SMatthias Ringwald // log_info("SDP Attribute: 0x%04x", sdp_event_query_attribute_byte_get_attribute_id(packet)); 651227d16a5SMatthias Ringwald if (de_get_element_type(attribute_value) != DE_DES) break; 652227d16a5SMatthias Ringwald 653227d16a5SMatthias Ringwald des_iterator_t des_list_0_it; 654227d16a5SMatthias Ringwald uint8_t *element_0; 655227d16a5SMatthias Ringwald 656227d16a5SMatthias Ringwald des_iterator_init(&des_list_0_it, attribute_value); 657227d16a5SMatthias Ringwald element_0 = des_iterator_get_element(&des_list_0_it); 658227d16a5SMatthias Ringwald 659227d16a5SMatthias Ringwald for (des_iterator_init(&des_list_it, element_0); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) { 660227d16a5SMatthias Ringwald uint8_t *des_element; 661227d16a5SMatthias Ringwald uint8_t *element; 662227d16a5SMatthias Ringwald uint32_t uuid; 663227d16a5SMatthias Ringwald 664227d16a5SMatthias Ringwald if (des_iterator_get_type(&des_list_it) != DE_DES) continue; 665227d16a5SMatthias Ringwald 666227d16a5SMatthias Ringwald des_element = des_iterator_get_element(&des_list_it); 667227d16a5SMatthias Ringwald des_iterator_init(&prot_it, des_element); 668227d16a5SMatthias Ringwald element = des_iterator_get_element(&prot_it); 669227d16a5SMatthias Ringwald 670227d16a5SMatthias Ringwald if (de_get_element_type(element) != DE_UUID) continue; 671227d16a5SMatthias Ringwald 672227d16a5SMatthias Ringwald uuid = de_get_uuid32(element); 673227d16a5SMatthias Ringwald switch (uuid){ 674227d16a5SMatthias Ringwald case BLUETOOTH_PROTOCOL_L2CAP: 675227d16a5SMatthias Ringwald if (!des_iterator_has_more(&prot_it)) continue; 676227d16a5SMatthias Ringwald des_iterator_next(&prot_it); 677227d16a5SMatthias Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context.avrcp_browsing_l2cap_psm); 678227d16a5SMatthias Ringwald break; 679227d16a5SMatthias Ringwald case BLUETOOTH_PROTOCOL_AVCTP: 680227d16a5SMatthias Ringwald if (!des_iterator_has_more(&prot_it)) continue; 681227d16a5SMatthias Ringwald des_iterator_next(&prot_it); 682227d16a5SMatthias Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context.avrcp_browsing_version); 683227d16a5SMatthias Ringwald break; 684227d16a5SMatthias Ringwald default: 685227d16a5SMatthias Ringwald break; 686227d16a5SMatthias Ringwald } 687227d16a5SMatthias Ringwald } 688227d16a5SMatthias Ringwald } 689227d16a5SMatthias Ringwald break; 6906086246cSMilanka Ringwald default: 6916086246cSMilanka Ringwald break; 6926086246cSMilanka Ringwald } 6936086246cSMilanka Ringwald } 6946086246cSMilanka Ringwald } else { 6956086246cSMilanka 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)); 6966086246cSMilanka Ringwald } 6976086246cSMilanka Ringwald break; 6986086246cSMilanka Ringwald 6996086246cSMilanka Ringwald case SDP_EVENT_QUERY_COMPLETE: 7004b1ac813SMilanka Ringwald log_info("General query done with status %d, role supported %d.\n", sdp_event_query_complete_get_status(packet), sdp_query_context.role_supported); 701df642728SMilanka Ringwald if (!sdp_query_context.role_supported || !sdp_query_context.avrcp_l2cap_psm){ 7026086246cSMilanka Ringwald sdp_query_context.connection->state = AVCTP_CONNECTION_IDLE; 703df642728SMilanka 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); 7046086246cSMilanka Ringwald break; 7056086246cSMilanka Ringwald } 706227d16a5SMatthias 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); 707df642728SMilanka Ringwald 7086086246cSMilanka Ringwald sdp_query_context.connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED; 709*34b22aacSMilanka Ringwald printf("sdp_query_context.packet_handler %p\n",sdp_query_context.avrcp_context->packet_handler); 710*34b22aacSMilanka Ringwald 711df642728SMilanka 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); 7126086246cSMilanka Ringwald break; 7136086246cSMilanka Ringwald } 7146086246cSMilanka Ringwald } 7156086246cSMilanka Ringwald 7169cc1f3ceSMilanka Ringwald static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){ 717be32e7f1SMilanka Ringwald uint8_t operands[20]; 718be32e7f1SMilanka Ringwald uint8_t opcode; 7198a2c6c7cSMilanka Ringwald int pos = 3; 720be32e7f1SMilanka Ringwald // uint8_t transport_header = packet[0]; 721be32e7f1SMilanka Ringwald // uint8_t transaction_label = transport_header >> 4; 722be32e7f1SMilanka Ringwald // uint8_t packet_type = (transport_header & 0x0F) >> 2; 723be32e7f1SMilanka Ringwald // uint8_t frame_type = (transport_header & 0x03) >> 1; 724be32e7f1SMilanka Ringwald // uint8_t ipid = transport_header & 0x01; 7258a2c6c7cSMilanka Ringwald // uint8_t byte_value = packet[2]; 726be32e7f1SMilanka Ringwald // uint16_t pid = (byte_value << 8) | packet[2]; 7278a2c6c7cSMilanka Ringwald 728d5ff3b26SMatthias Ringwald avrcp_command_type_t ctype = (avrcp_command_type_t) packet[pos++]; 7298a2c6c7cSMilanka Ringwald uint8_t byte_value = packet[pos++]; 730d5ff3b26SMatthias Ringwald avrcp_subunit_type_t subunit_type = (avrcp_subunit_type_t) (byte_value >> 3); 731d5ff3b26SMatthias Ringwald avrcp_subunit_type_t subunit_id = (avrcp_subunit_type_t) (byte_value & 0x07); 7328a2c6c7cSMilanka Ringwald opcode = packet[pos++]; 733be32e7f1SMilanka Ringwald 734be32e7f1SMilanka Ringwald // printf(" Transport header 0x%02x (transaction_label %d, packet_type %d, frame_type %d, ipid %d), pid 0x%4x\n", 735be32e7f1SMilanka Ringwald // transport_header, transaction_label, packet_type, frame_type, ipid, pid); 736be32e7f1SMilanka Ringwald // // printf_hexdump(packet+pos, size-pos); 737d5ff3b26SMatthias Ringwald 738d5ff3b26SMatthias Ringwald uint8_t pdu_id; 739d5ff3b26SMatthias Ringwald uint16_t param_length; 7409cc1f3ceSMilanka Ringwald switch (avrcp_cmd_opcode(packet,size)){ 741be32e7f1SMilanka Ringwald case AVRCP_CMD_OPCODE_UNIT_INFO:{ 742294335f1SMilanka Ringwald if (connection->state != AVCTP_W2_RECEIVE_RESPONSE) return; 7439cc1f3ceSMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 744294335f1SMilanka Ringwald 745be32e7f1SMilanka Ringwald // operands: 746be32e7f1SMilanka Ringwald memcpy(operands, packet+pos, 5); 747be32e7f1SMilanka Ringwald uint8_t unit_type = operands[1] >> 3; 748be32e7f1SMilanka Ringwald uint8_t unit = operands[1] & 0x07; 749be32e7f1SMilanka Ringwald uint32_t company_id = operands[2] << 16 | operands[3] << 8 | operands[4]; 750be32e7f1SMilanka 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", 751be32e7f1SMilanka Ringwald ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id ); 752be32e7f1SMilanka Ringwald break; 753be32e7f1SMilanka Ringwald } 754be32e7f1SMilanka Ringwald case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT: 755be32e7f1SMilanka Ringwald if (size - pos < 7) { 756be32e7f1SMilanka Ringwald log_error("avrcp: wrong packet size"); 757be32e7f1SMilanka Ringwald return; 758be32e7f1SMilanka Ringwald }; 759be32e7f1SMilanka Ringwald // operands: 760be32e7f1SMilanka Ringwald memcpy(operands, packet+pos, 7); 761be32e7f1SMilanka Ringwald pos += 7; 762be32e7f1SMilanka Ringwald // uint32_t company_id = operands[0] << 16 | operands[1] << 8 | operands[2]; 763d5ff3b26SMatthias Ringwald pdu_id = operands[3]; 764294335f1SMilanka Ringwald 765294335f1SMilanka Ringwald if (connection->state != AVCTP_W2_RECEIVE_RESPONSE && pdu_id != AVRCP_PDU_ID_REGISTER_NOTIFICATION){ 766ac98fce4SMilanka Ringwald log_info("AVRCP_CMD_OPCODE_VENDOR_DEPENDENT state %d", connection->state); 767294335f1SMilanka Ringwald return; 768294335f1SMilanka Ringwald } 769294335f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 770294335f1SMilanka Ringwald 771294335f1SMilanka Ringwald 772be32e7f1SMilanka Ringwald // uint8_t unit_type = operands[4] >> 3; 773be32e7f1SMilanka Ringwald // uint8_t unit = operands[4] & 0x07; 774d5ff3b26SMatthias Ringwald param_length = big_endian_read_16(operands, 5); 775be32e7f1SMilanka Ringwald 776be32e7f1SMilanka 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", 777be32e7f1SMilanka Ringwald // ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id ); 778be32e7f1SMilanka Ringwald 779be32e7f1SMilanka Ringwald // if (ctype == AVRCP_CTYPE_RESPONSE_INTERIM) return; 780be32e7f1SMilanka Ringwald log_info(" VENDOR DEPENDENT response: pdu id 0x%02x, param_length %d, status %s", pdu_id, param_length, avrcp_ctype2str(ctype)); 781be32e7f1SMilanka Ringwald switch (pdu_id){ 782be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue:{ 783be32e7f1SMilanka Ringwald uint8_t num_attributes = packet[pos++]; 784be32e7f1SMilanka Ringwald int i; 785d5ff3b26SMatthias Ringwald avrcp_repeat_mode_t repeat_mode = AVRCP_REPEAT_MODE_INVALID; 786d5ff3b26SMatthias Ringwald avrcp_shuffle_mode_t shuffle_mode = AVRCP_SHUFFLE_MODE_INVALID; 787be32e7f1SMilanka Ringwald for (i = 0; i < num_attributes; i++){ 788be32e7f1SMilanka Ringwald uint8_t attribute_id = packet[pos++]; 7896086246cSMilanka Ringwald uint8_t value = packet[pos++]; 790be32e7f1SMilanka Ringwald switch (attribute_id){ 791be32e7f1SMilanka Ringwald case 0x02: 7926086246cSMilanka Ringwald repeat_mode = (avrcp_repeat_mode_t) value; 793be32e7f1SMilanka Ringwald break; 794be32e7f1SMilanka Ringwald case 0x03: 7956086246cSMilanka Ringwald shuffle_mode = (avrcp_shuffle_mode_t) value; 796be32e7f1SMilanka Ringwald break; 797be32e7f1SMilanka Ringwald default: 798be32e7f1SMilanka Ringwald break; 799be32e7f1SMilanka Ringwald } 800be32e7f1SMilanka Ringwald } 8016086246cSMilanka Ringwald avrcp_emit_repeat_and_shuffle_mode(avrcp_callback, connection->avrcp_cid, ctype, repeat_mode, shuffle_mode); 802be32e7f1SMilanka Ringwald break; 803be32e7f1SMilanka Ringwald } 804be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_SetPlayerApplicationSettingValue:{ 805be32e7f1SMilanka Ringwald uint8_t event[6]; 806be32e7f1SMilanka Ringwald int offset = 0; 807be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 808be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 809be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE; 8106086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 811be32e7f1SMilanka Ringwald offset += 2; 812be32e7f1SMilanka Ringwald event[offset++] = ctype; 813be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 814be32e7f1SMilanka Ringwald break; 815be32e7f1SMilanka Ringwald } 816be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME:{ 817be32e7f1SMilanka Ringwald uint8_t event[7]; 818be32e7f1SMilanka Ringwald int offset = 0; 819be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 820be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 821be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_SET_ABSOLUTE_VOLUME_RESPONSE; 8226086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 823be32e7f1SMilanka Ringwald offset += 2; 824be32e7f1SMilanka Ringwald event[offset++] = ctype; 825be32e7f1SMilanka Ringwald event[offset++] = packet[pos++]; 826be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 827be32e7f1SMilanka Ringwald break; 828be32e7f1SMilanka Ringwald } 829be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GET_CAPABILITIES:{ 830d5ff3b26SMatthias Ringwald avrcp_capability_id_t capability_id = (avrcp_capability_id_t) packet[pos++]; 831be32e7f1SMilanka Ringwald uint8_t capability_count = packet[pos++]; 832be32e7f1SMilanka Ringwald int i; 833be32e7f1SMilanka Ringwald switch (capability_id){ 834be32e7f1SMilanka Ringwald case AVRCP_CAPABILITY_ID_COMPANY: 835be32e7f1SMilanka Ringwald // log_info("Supported companies %d: ", capability_count); 836be32e7f1SMilanka Ringwald for (i = 0; i < capability_count; i++){ 837be32e7f1SMilanka Ringwald uint32_t company_id = big_endian_read_24(packet, pos); 838be32e7f1SMilanka Ringwald pos += 3; 839be32e7f1SMilanka Ringwald log_info(" 0x%06x, ", company_id); 840be32e7f1SMilanka Ringwald } 841be32e7f1SMilanka Ringwald break; 842be32e7f1SMilanka Ringwald case AVRCP_CAPABILITY_ID_EVENT: 843be32e7f1SMilanka Ringwald // log_info("Supported events %d: ", capability_count); 844be32e7f1SMilanka Ringwald for (i = 0; i < capability_count; i++){ 845be32e7f1SMilanka Ringwald uint8_t event_id = packet[pos++]; 846be32e7f1SMilanka Ringwald log_info(" 0x%02x %s", event_id, avrcp_event2str(event_id)); 847be32e7f1SMilanka Ringwald } 848be32e7f1SMilanka Ringwald break; 849be32e7f1SMilanka Ringwald } 850be32e7f1SMilanka Ringwald break; 851be32e7f1SMilanka Ringwald } 852be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GET_PLAY_STATUS:{ 853be32e7f1SMilanka Ringwald uint32_t song_length = big_endian_read_32(packet, pos); 854be32e7f1SMilanka Ringwald pos += 4; 855be32e7f1SMilanka Ringwald uint32_t song_position = big_endian_read_32(packet, pos); 856be32e7f1SMilanka Ringwald pos += 4; 857be32e7f1SMilanka Ringwald uint8_t play_status = packet[pos]; 858be32e7f1SMilanka Ringwald // log_info(" GET_PLAY_STATUS length 0x%04X, position 0x%04X, status %s", song_length, song_position, avrcp_play_status2str(play_status)); 859be32e7f1SMilanka Ringwald 860be32e7f1SMilanka Ringwald uint8_t event[15]; 861be32e7f1SMilanka Ringwald int offset = 0; 862be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 863be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 864be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_PLAY_STATUS; 8656086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 866be32e7f1SMilanka Ringwald offset += 2; 867be32e7f1SMilanka Ringwald event[offset++] = ctype; 868be32e7f1SMilanka Ringwald little_endian_store_32(event, offset, song_length); 869be32e7f1SMilanka Ringwald offset += 4; 870be32e7f1SMilanka Ringwald little_endian_store_32(event, offset, song_position); 871be32e7f1SMilanka Ringwald offset += 4; 872be32e7f1SMilanka Ringwald event[offset++] = play_status; 873be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 874be32e7f1SMilanka Ringwald break; 875be32e7f1SMilanka Ringwald } 876be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_REGISTER_NOTIFICATION:{ 877d5ff3b26SMatthias Ringwald avrcp_notification_event_id_t event_id = (avrcp_notification_event_id_t) packet[pos++]; 878be32e7f1SMilanka Ringwald uint16_t event_mask = (1 << event_id); 879be32e7f1SMilanka Ringwald uint16_t reset_event_mask = ~event_mask; 880be32e7f1SMilanka Ringwald switch (ctype){ 881be32e7f1SMilanka Ringwald case AVRCP_CTYPE_RESPONSE_INTERIM: 882be32e7f1SMilanka Ringwald // register as enabled 883be32e7f1SMilanka Ringwald connection->notifications_enabled |= event_mask; 884be32e7f1SMilanka Ringwald // printf("INTERIM notifications_enabled 0x%2x, notifications_to_register 0x%2x\n", connection->notifications_enabled, connection->notifications_to_register); 885be32e7f1SMilanka Ringwald break; 886be32e7f1SMilanka Ringwald case AVRCP_CTYPE_RESPONSE_CHANGED_STABLE: 887be32e7f1SMilanka Ringwald // received change, event is considered deregistered 888be32e7f1SMilanka Ringwald // we are re-enabling it automatically, if it is not 889be32e7f1SMilanka Ringwald // explicitly disabled 890be32e7f1SMilanka Ringwald connection->notifications_enabled &= reset_event_mask; 891be32e7f1SMilanka Ringwald if (! (connection->notifications_to_deregister & event_mask)){ 892be32e7f1SMilanka Ringwald avrcp_register_notification(connection, event_id); 893be32e7f1SMilanka Ringwald // printf("CHANGED_STABLE notifications_enabled 0x%2x, notifications_to_register 0x%2x\n", connection->notifications_enabled, connection->notifications_to_register); 894be32e7f1SMilanka Ringwald } else { 895be32e7f1SMilanka Ringwald connection->notifications_to_deregister &= reset_event_mask; 896be32e7f1SMilanka Ringwald } 897be32e7f1SMilanka Ringwald break; 898be32e7f1SMilanka Ringwald default: 899be32e7f1SMilanka Ringwald connection->notifications_to_register &= reset_event_mask; 900be32e7f1SMilanka Ringwald connection->notifications_enabled &= reset_event_mask; 901be32e7f1SMilanka Ringwald connection->notifications_to_deregister &= reset_event_mask; 902be32e7f1SMilanka Ringwald break; 903be32e7f1SMilanka Ringwald } 904be32e7f1SMilanka Ringwald 905be32e7f1SMilanka Ringwald switch (event_id){ 906be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED:{ 907be32e7f1SMilanka Ringwald uint8_t event[7]; 908be32e7f1SMilanka Ringwald int offset = 0; 909be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 910be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 911be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED; 9126086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 913be32e7f1SMilanka Ringwald offset += 2; 914be32e7f1SMilanka Ringwald event[offset++] = ctype; 915be32e7f1SMilanka Ringwald event[offset++] = packet[pos]; 916be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 917be32e7f1SMilanka Ringwald break; 918be32e7f1SMilanka Ringwald } 919be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED:{ 920ee6d17f2SMatthias Ringwald uint8_t event[6]; 921be32e7f1SMilanka Ringwald int offset = 0; 922be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 923be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 924be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED; 9256086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 926be32e7f1SMilanka Ringwald offset += 2; 927be32e7f1SMilanka Ringwald event[offset++] = ctype; 928be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 929be32e7f1SMilanka Ringwald break; 930be32e7f1SMilanka Ringwald } 931be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED:{ 932be32e7f1SMilanka Ringwald uint8_t event[6]; 933be32e7f1SMilanka Ringwald int offset = 0; 934be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 935be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 936be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED; 9376086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 938be32e7f1SMilanka Ringwald offset += 2; 939be32e7f1SMilanka Ringwald event[offset++] = ctype; 940be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 941be32e7f1SMilanka Ringwald break; 942be32e7f1SMilanka Ringwald } 943be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_AVAILABLE_PLAYERS_CHANGED:{ 944be32e7f1SMilanka Ringwald uint8_t event[6]; 945be32e7f1SMilanka Ringwald int offset = 0; 946be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 947be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 948be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED; 9496086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 950be32e7f1SMilanka Ringwald offset += 2; 951be32e7f1SMilanka Ringwald event[offset++] = ctype; 952be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 953be32e7f1SMilanka Ringwald break; 954be32e7f1SMilanka Ringwald } 955be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED:{ 956be32e7f1SMilanka Ringwald uint8_t event[7]; 957be32e7f1SMilanka Ringwald int offset = 0; 958be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 959be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 960be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED; 9616086246cSMilanka Ringwald little_endian_store_16(event, offset, connection->avrcp_cid); 962be32e7f1SMilanka Ringwald offset += 2; 963be32e7f1SMilanka Ringwald event[offset++] = ctype; 964be32e7f1SMilanka Ringwald event[offset++] = packet[pos++] & 0x7F; 965be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 966be32e7f1SMilanka Ringwald break; 967be32e7f1SMilanka Ringwald } 968be32e7f1SMilanka Ringwald // case AVRCP_NOTIFICATION_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:{ 969be32e7f1SMilanka Ringwald // uint8_t num_PlayerApplicationSettingAttributes = packet[pos++]; 970be32e7f1SMilanka Ringwald // int i; 971be32e7f1SMilanka Ringwald // for (i = 0; i < num_PlayerApplicationSettingAttributes; i++){ 972be32e7f1SMilanka Ringwald // uint8_t PlayerApplicationSetting_AttributeID = packet[pos++]; 973be32e7f1SMilanka Ringwald // uint8_t PlayerApplicationSettingValueID = packet[pos++]; 974be32e7f1SMilanka Ringwald // } 975be32e7f1SMilanka Ringwald // break; 976be32e7f1SMilanka Ringwald // } 977be32e7f1SMilanka Ringwald // case AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED: 978be32e7f1SMilanka Ringwald // uint16_t player_id = big_endian_read_16(packet, pos); 979be32e7f1SMilanka Ringwald // pos += 2; 980be32e7f1SMilanka Ringwald // uint16_t uid_counter = big_endian_read_16(packet, pos); 981be32e7f1SMilanka Ringwald // pos += 2; 982be32e7f1SMilanka Ringwald // break; 983be32e7f1SMilanka Ringwald // case AVRCP_NOTIFICATION_EVENT_UIDS_CHANGED: 984be32e7f1SMilanka Ringwald // uint16_t uid_counter = big_endian_read_16(packet, pos); 985be32e7f1SMilanka Ringwald // pos += 2; 986be32e7f1SMilanka Ringwald // break; 987be32e7f1SMilanka Ringwald default: 988be32e7f1SMilanka Ringwald log_info("avrcp: not implemented"); 989be32e7f1SMilanka Ringwald break; 990be32e7f1SMilanka Ringwald } 991be32e7f1SMilanka Ringwald if (connection->notifications_to_register != 0){ 992be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 993be32e7f1SMilanka Ringwald } 994be32e7f1SMilanka Ringwald break; 995be32e7f1SMilanka Ringwald } 996be32e7f1SMilanka Ringwald 997be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES:{ 998be32e7f1SMilanka Ringwald uint8_t num_attributes = packet[pos++]; 999be32e7f1SMilanka Ringwald int i; 1000be32e7f1SMilanka Ringwald struct item { 1001be32e7f1SMilanka Ringwald uint16_t len; 1002be32e7f1SMilanka Ringwald uint8_t * value; 1003be32e7f1SMilanka Ringwald } items[AVRCP_MEDIA_ATTR_COUNT]; 1004be32e7f1SMilanka Ringwald memset(items, 0, sizeof(items)); 1005be32e7f1SMilanka Ringwald 1006be32e7f1SMilanka Ringwald uint16_t string_attributes_len = 0; 1007be32e7f1SMilanka Ringwald uint8_t num_string_attributes = 0; 1008be32e7f1SMilanka Ringwald uint16_t total_event_payload_for_string_attributes = HCI_EVENT_PAYLOAD_SIZE-2; 1009be32e7f1SMilanka Ringwald uint16_t max_string_attribute_value_len = 0; 10104bb88fd7SMatthias Ringwald if (ctype == AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE || ctype == AVRCP_CTYPE_RESPONSE_CHANGED_STABLE){ 1011be32e7f1SMilanka Ringwald for (i = 0; i < num_attributes; i++){ 1012d5ff3b26SMatthias Ringwald avrcp_media_attribute_id_t attr_id = (avrcp_media_attribute_id_t) big_endian_read_32(packet, pos); 1013be32e7f1SMilanka Ringwald pos += 4; 1014be32e7f1SMilanka Ringwald // uint16_t character_set = big_endian_read_16(packet, pos); 1015be32e7f1SMilanka Ringwald pos += 2; 1016be32e7f1SMilanka Ringwald uint16_t attr_value_length = big_endian_read_16(packet, pos); 1017be32e7f1SMilanka Ringwald pos += 2; 1018be32e7f1SMilanka Ringwald 1019be32e7f1SMilanka Ringwald // debug - to remove later 1020be32e7f1SMilanka Ringwald uint8_t value[100]; 1021be32e7f1SMilanka Ringwald uint16_t value_len = sizeof(value) <= attr_value_length? sizeof(value) - 1 : attr_value_length; 1022be32e7f1SMilanka Ringwald memcpy(value, packet+pos, value_len); 1023be32e7f1SMilanka Ringwald value[value_len] = 0; 1024be32e7f1SMilanka Ringwald // printf("Now Playing Info %s: %s \n", attribute2str(attr_id), value); 1025be32e7f1SMilanka Ringwald // end debug 1026be32e7f1SMilanka Ringwald 1027be32e7f1SMilanka Ringwald if ((attr_id >= 1) || (attr_id <= AVRCP_MEDIA_ATTR_COUNT)) { 1028be32e7f1SMilanka Ringwald items[attr_id-1].len = attr_value_length; 1029be32e7f1SMilanka Ringwald items[attr_id-1].value = &packet[pos]; 1030be32e7f1SMilanka Ringwald switch (attr_id){ 1031be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TITLE: 1032be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ARTIST: 1033be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ALBUM: 1034be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_GENRE: 1035be32e7f1SMilanka Ringwald num_string_attributes++; 1036be32e7f1SMilanka Ringwald string_attributes_len += attr_value_length; 1037be32e7f1SMilanka Ringwald if (max_string_attribute_value_len < attr_value_length){ 1038be32e7f1SMilanka Ringwald max_string_attribute_value_len = attr_value_length; 1039be32e7f1SMilanka Ringwald } 1040be32e7f1SMilanka Ringwald break; 1041be32e7f1SMilanka Ringwald default: 1042be32e7f1SMilanka Ringwald break; 1043be32e7f1SMilanka Ringwald } 1044be32e7f1SMilanka Ringwald } 1045be32e7f1SMilanka Ringwald pos += attr_value_length; 1046be32e7f1SMilanka Ringwald } 10474bb88fd7SMatthias Ringwald } 10484bb88fd7SMatthias Ringwald 1049be32e7f1SMilanka Ringwald // subtract space for fixed fields 1050be32e7f1SMilanka Ringwald total_event_payload_for_string_attributes -= 14 + 4; // 4 for '\0' 1051be32e7f1SMilanka Ringwald 1052be32e7f1SMilanka Ringwald // @TODO optimize space by repeatedly decreasing max_string_attribute_value_len until it fits into buffer instead of crude divion 1053be32e7f1SMilanka 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; 1054be32e7f1SMilanka 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); 1055be32e7f1SMilanka Ringwald 1056be32e7f1SMilanka Ringwald const uint8_t attribute_order[] = { 1057be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_TRACK, 1058be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_TOTAL_TRACKS, 1059be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_SONG_LENGTH, 1060be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_TITLE, 1061be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_ARTIST, 1062be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_ALBUM, 1063be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_GENRE 1064be32e7f1SMilanka Ringwald }; 1065be32e7f1SMilanka Ringwald 1066be32e7f1SMilanka Ringwald uint8_t event[HCI_EVENT_BUFFER_SIZE]; 1067be32e7f1SMilanka Ringwald event[0] = HCI_EVENT_AVRCP_META; 1068be32e7f1SMilanka Ringwald pos = 2; 1069be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_NOW_PLAYING_INFO; 10706086246cSMilanka Ringwald little_endian_store_16(event, pos, connection->avrcp_cid); 1071be32e7f1SMilanka Ringwald pos += 2; 1072be32e7f1SMilanka Ringwald event[pos++] = ctype; 1073be32e7f1SMilanka Ringwald for (i = 0; i < sizeof(attribute_order); i++){ 1074d5ff3b26SMatthias Ringwald avrcp_media_attribute_id_t attr_id = (avrcp_media_attribute_id_t) attribute_order[i]; 1075be32e7f1SMilanka Ringwald uint16_t value_len = 0; 1076be32e7f1SMilanka Ringwald switch (attr_id){ 1077be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TITLE: 1078be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ARTIST: 1079be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ALBUM: 1080be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_GENRE: 1081be32e7f1SMilanka Ringwald if (items[attr_id-1].value){ 1082be32e7f1SMilanka Ringwald value_len = items[attr_id-1].len <= max_value_len ? items[attr_id-1].len : max_value_len; 1083be32e7f1SMilanka Ringwald } 1084be32e7f1SMilanka Ringwald event[pos++] = value_len + 1; 1085be32e7f1SMilanka Ringwald if (value_len){ 1086be32e7f1SMilanka Ringwald memcpy(event+pos, items[attr_id-1].value, value_len); 1087be32e7f1SMilanka Ringwald pos += value_len; 1088be32e7f1SMilanka Ringwald } 1089be32e7f1SMilanka Ringwald event[pos++] = 0; 1090be32e7f1SMilanka Ringwald break; 1091be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_SONG_LENGTH: 1092be32e7f1SMilanka Ringwald if (items[attr_id-1].value){ 1093be32e7f1SMilanka Ringwald little_endian_store_32(event, pos, btstack_atoi((char *)items[attr_id-1].value)); 1094be32e7f1SMilanka Ringwald } else { 1095be32e7f1SMilanka Ringwald little_endian_store_32(event, pos, 0); 1096be32e7f1SMilanka Ringwald } 1097be32e7f1SMilanka Ringwald pos += 4; 1098be32e7f1SMilanka Ringwald break; 1099be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TRACK: 1100be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TOTAL_TRACKS: 1101be32e7f1SMilanka Ringwald if (items[attr_id-1].value){ 1102be32e7f1SMilanka Ringwald event[pos++] = btstack_atoi((char *)items[attr_id-1].value); 1103be32e7f1SMilanka Ringwald } else { 1104be32e7f1SMilanka Ringwald event[pos++] = 0; 1105be32e7f1SMilanka Ringwald } 1106be32e7f1SMilanka Ringwald break; 1107be32e7f1SMilanka Ringwald } 1108be32e7f1SMilanka Ringwald } 1109be32e7f1SMilanka Ringwald event[1] = pos - 2; 1110be32e7f1SMilanka Ringwald // printf_hexdump(event, pos); 1111be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, pos); 1112be32e7f1SMilanka Ringwald break; 1113be32e7f1SMilanka Ringwald } 1114be32e7f1SMilanka Ringwald default: 1115be32e7f1SMilanka Ringwald break; 1116be32e7f1SMilanka Ringwald } 1117be32e7f1SMilanka Ringwald break; 1118be32e7f1SMilanka Ringwald case AVRCP_CMD_OPCODE_PASS_THROUGH:{ 1119be32e7f1SMilanka Ringwald // 0x80 | connection->cmd_operands[0] 1120be32e7f1SMilanka Ringwald uint8_t operation_id = packet[pos++]; 11219cc1f3ceSMilanka Ringwald switch (connection->state){ 11229cc1f3ceSMilanka Ringwald case AVCTP_W2_RECEIVE_PRESS_RESPONSE: 1123d1845da3SMilanka Ringwald if (connection->continuous_fast_forward_cmd){ 11249cc1f3ceSMilanka Ringwald connection->state = AVCTP_W4_STOP; 1125d1845da3SMilanka Ringwald } else { 11269cc1f3ceSMilanka Ringwald connection->state = AVCTP_W2_SEND_RELEASE_COMMAND; 11279cc1f3ceSMilanka Ringwald } 11289cc1f3ceSMilanka Ringwald break; 11299cc1f3ceSMilanka Ringwald case AVCTP_W2_RECEIVE_RESPONSE: 11309cc1f3ceSMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 11319cc1f3ceSMilanka Ringwald break; 11329cc1f3ceSMilanka Ringwald default: 11339cc1f3ceSMilanka Ringwald // check for notifications? move state transition down 1134ac98fce4SMilanka Ringwald // log_info("AVRCP_CMD_OPCODE_PASS_THROUGH state %d\n", connection->state); 11359cc1f3ceSMilanka Ringwald break; 11369cc1f3ceSMilanka Ringwald } 1137be32e7f1SMilanka Ringwald if (connection->state == AVCTP_W4_STOP){ 11386086246cSMilanka Ringwald avrcp_emit_operation_status(avrcp_callback, AVRCP_SUBEVENT_OPERATION_START, connection->avrcp_cid, ctype, operation_id); 1139be32e7f1SMilanka Ringwald } 1140be32e7f1SMilanka Ringwald if (connection->state == AVCTP_CONNECTION_OPENED) { 1141be32e7f1SMilanka Ringwald // RELEASE response 1142be32e7f1SMilanka Ringwald operation_id = operation_id & 0x7F; 11436086246cSMilanka Ringwald avrcp_emit_operation_status(avrcp_callback, AVRCP_SUBEVENT_OPERATION_COMPLETE, connection->avrcp_cid, ctype, operation_id); 1144be32e7f1SMilanka Ringwald } 1145be32e7f1SMilanka Ringwald if (connection->state == AVCTP_W2_SEND_RELEASE_COMMAND){ 1146be32e7f1SMilanka Ringwald // PRESS response 1147be32e7f1SMilanka Ringwald request_pass_through_release_control_cmd(connection); 1148be32e7f1SMilanka Ringwald } 1149be32e7f1SMilanka Ringwald break; 1150be32e7f1SMilanka Ringwald } 1151be32e7f1SMilanka Ringwald default: 1152be32e7f1SMilanka Ringwald break; 1153be32e7f1SMilanka Ringwald } 1154be32e7f1SMilanka Ringwald } 1155be32e7f1SMilanka Ringwald 1156be32e7f1SMilanka Ringwald static void avrcp_handle_can_send_now(avrcp_connection_t * connection){ 1157be32e7f1SMilanka Ringwald int i; 1158be32e7f1SMilanka Ringwald switch (connection->state){ 1159be32e7f1SMilanka Ringwald case AVCTP_W2_SEND_PRESS_COMMAND: 1160be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_RECEIVE_PRESS_RESPONSE; 1161b193c45eSMilanka Ringwald avrcp_send_cmd(connection->l2cap_signaling_cid, connection); 1162be32e7f1SMilanka Ringwald break; 1163be32e7f1SMilanka Ringwald case AVCTP_W2_SEND_COMMAND: 1164be32e7f1SMilanka Ringwald case AVCTP_W2_SEND_RELEASE_COMMAND: 1165be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_RECEIVE_RESPONSE; 1166b193c45eSMilanka Ringwald avrcp_send_cmd(connection->l2cap_signaling_cid, connection); 1167be32e7f1SMilanka Ringwald break; 1168be32e7f1SMilanka Ringwald case AVCTP_CONNECTION_OPENED: 1169be32e7f1SMilanka Ringwald if (connection->notifications_to_register != 0){ 1170d1845da3SMilanka Ringwald for (i = 1; i <= AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED; i++){ 1171be32e7f1SMilanka Ringwald if (connection->notifications_to_register & (1<<i)){ 117273264fe8SMatthias Ringwald connection->notifications_to_register &= ~ (1 << i); 1173d5ff3b26SMatthias Ringwald avrcp_prepare_notification(connection, (avrcp_notification_event_id_t) i); 1174294335f1SMilanka Ringwald connection->state = AVCTP_W2_RECEIVE_RESPONSE; 1175be32e7f1SMilanka Ringwald avrcp_send_cmd(connection->l2cap_signaling_cid, connection); 1176be32e7f1SMilanka Ringwald return; 1177be32e7f1SMilanka Ringwald } 1178be32e7f1SMilanka Ringwald } 1179be32e7f1SMilanka Ringwald } 1180be32e7f1SMilanka Ringwald return; 1181be32e7f1SMilanka Ringwald default: 1182be32e7f1SMilanka Ringwald return; 1183be32e7f1SMilanka Ringwald } 1184be32e7f1SMilanka Ringwald } 1185be32e7f1SMilanka Ringwald 11866086246cSMilanka Ringwald static avrcp_connection_t * avrcp_create_connection(bd_addr_t remote_addr, avrcp_context_t * context){ 1187be32e7f1SMilanka Ringwald avrcp_connection_t * connection = btstack_memory_avrcp_connection_get(); 1188be32e7f1SMilanka Ringwald memset(connection, 0, sizeof(avrcp_connection_t)); 1189be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_IDLE; 1190be32e7f1SMilanka Ringwald connection->transaction_label = 0xFF; 1191be32e7f1SMilanka Ringwald memcpy(connection->remote_addr, remote_addr, 6); 11926086246cSMilanka Ringwald btstack_linked_list_add(&context->connections, (btstack_linked_item_t *) connection); 1193be32e7f1SMilanka Ringwald return connection; 1194be32e7f1SMilanka Ringwald } 1195be32e7f1SMilanka Ringwald 11966086246cSMilanka Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size, avrcp_context_t * context){ 1197be32e7f1SMilanka Ringwald bd_addr_t event_addr; 1198be32e7f1SMilanka Ringwald uint16_t local_cid; 1199be32e7f1SMilanka Ringwald avrcp_connection_t * connection = NULL; 1200be32e7f1SMilanka Ringwald 1201be32e7f1SMilanka Ringwald switch (packet_type) { 1202be32e7f1SMilanka Ringwald case L2CAP_DATA_PACKET: 12036086246cSMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, context); 1204be32e7f1SMilanka Ringwald if (!connection) break; 1205be32e7f1SMilanka Ringwald avrcp_handle_l2cap_data_packet_for_signaling_connection(connection, packet, size); 1206be32e7f1SMilanka Ringwald break; 1207be32e7f1SMilanka Ringwald 1208be32e7f1SMilanka Ringwald case HCI_EVENT_PACKET: 1209be32e7f1SMilanka Ringwald switch (hci_event_packet_get_type(packet)) { 1210*34b22aacSMilanka Ringwald case HCI_EVENT_DISCONNECTION_COMPLETE: 1211*34b22aacSMilanka Ringwald // connection closed -> quit test app 1212*34b22aacSMilanka Ringwald // status = hci_event_disconnection_complete_get_status(packet); 1213*34b22aacSMilanka Ringwald avrcp_emit_connection_closed(avrcp_callback, 0); 1214*34b22aacSMilanka Ringwald break; 1215be32e7f1SMilanka Ringwald case L2CAP_EVENT_INCOMING_CONNECTION: 1216be32e7f1SMilanka Ringwald l2cap_event_incoming_connection_get_address(packet, event_addr); 1217be32e7f1SMilanka Ringwald local_cid = l2cap_event_incoming_connection_get_local_cid(packet); 12186086246cSMilanka Ringwald connection = avrcp_create_connection(event_addr, context); 12190f76c2d7SMatthias Ringwald if (!connection) { 12200f76c2d7SMatthias Ringwald log_error("Failed to alloc connection structure"); 12210f76c2d7SMatthias Ringwald l2cap_decline_connection(local_cid); 1222be32e7f1SMilanka Ringwald break; 1223be32e7f1SMilanka Ringwald } 12240f76c2d7SMatthias Ringwald connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED; 12250f76c2d7SMatthias Ringwald connection->l2cap_signaling_cid = local_cid; 12260f76c2d7SMatthias Ringwald l2cap_accept_connection(local_cid); 1227be32e7f1SMilanka Ringwald break; 1228be32e7f1SMilanka Ringwald 1229be32e7f1SMilanka Ringwald case L2CAP_EVENT_CHANNEL_OPENED: 1230be32e7f1SMilanka Ringwald l2cap_event_channel_opened_get_address(packet, event_addr); 1231be32e7f1SMilanka Ringwald 12326086246cSMilanka Ringwald connection = get_avrcp_connection_for_bd_addr(event_addr, context); 12330f76c2d7SMatthias Ringwald local_cid = l2cap_event_channel_opened_get_local_cid(packet); 12340f76c2d7SMatthias Ringwald 12356086246cSMilanka Ringwald if (!connection){ 12360f76c2d7SMatthias Ringwald // incoming connection 12376086246cSMilanka Ringwald connection = avrcp_create_connection(event_addr, context); 12380f76c2d7SMatthias Ringwald if (!connection) { 12390f76c2d7SMatthias Ringwald log_error("Failed to alloc connection structure"); 12400f76c2d7SMatthias Ringwald l2cap_disconnect(local_cid, 0); // reason isn't used 1241b193c45eSMilanka Ringwald break; 1242b193c45eSMilanka Ringwald } 12436086246cSMilanka Ringwald connection->l2cap_signaling_cid = local_cid; 12446086246cSMilanka Ringwald connection->avrcp_cid = avrcp_cid_counter++; 12450f76c2d7SMatthias Ringwald } 1246b193c45eSMilanka Ringwald 12476086246cSMilanka Ringwald if (l2cap_event_channel_opened_get_status(packet)){ 12486086246cSMilanka Ringwald log_info("L2CAP connection to connection %s failed. status code 0x%02x", 12496086246cSMilanka Ringwald bd_addr_to_str(event_addr), l2cap_event_channel_opened_get_status(packet)); 12506086246cSMilanka Ringwald if (connection->state == AVCTP_CONNECTION_W4_L2CAP_CONNECTED && connection->l2cap_signaling_cid == local_cid){ 12516086246cSMilanka Ringwald avrcp_emit_connection_established(avrcp_callback, connection->avrcp_cid, event_addr, l2cap_event_channel_opened_get_status(packet)); 12526086246cSMilanka Ringwald } 12536086246cSMilanka Ringwald // free connection 12546086246cSMilanka Ringwald btstack_linked_list_remove(&context->connections, (btstack_linked_item_t*) connection); 12556086246cSMilanka Ringwald btstack_memory_avrcp_connection_free(connection); 12566086246cSMilanka Ringwald break; 12576086246cSMilanka Ringwald } 1258be32e7f1SMilanka Ringwald connection->l2cap_signaling_cid = local_cid; 12596086246cSMilanka Ringwald log_info("L2CAP_EVENT_CHANNEL_OPENED avrcp_cid 0x%02x, l2cap_signaling_cid 0x%02x",connection->avrcp_cid, connection->l2cap_signaling_cid); 1260be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 12616086246cSMilanka Ringwald avrcp_emit_connection_established(avrcp_callback, connection->avrcp_cid, event_addr, ERROR_CODE_SUCCESS); 12625c806868SMatthias Ringwald 12635c806868SMatthias Ringwald // hack 12645c806868SMatthias Ringwald // l2cap_create_channel(avrcp_controller_context.packet_handler, connection->remote_addr, 0x001b, l2cap_max_mtu(), NULL); 1265be32e7f1SMilanka Ringwald break; 1266be32e7f1SMilanka Ringwald 1267be32e7f1SMilanka Ringwald case L2CAP_EVENT_CAN_SEND_NOW: 12686086246cSMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, context); 1269be32e7f1SMilanka Ringwald if (!connection) break; 1270be32e7f1SMilanka Ringwald avrcp_handle_can_send_now(connection); 1271be32e7f1SMilanka Ringwald break; 1272be32e7f1SMilanka Ringwald 1273be32e7f1SMilanka Ringwald case L2CAP_EVENT_CHANNEL_CLOSED: 1274be32e7f1SMilanka Ringwald // data: event (8), len(8), channel (16) 1275be32e7f1SMilanka Ringwald local_cid = l2cap_event_channel_closed_get_local_cid(packet); 12766086246cSMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(local_cid, context); 1277be32e7f1SMilanka Ringwald if (connection){ 1278*34b22aacSMilanka Ringwald avrcp_emit_connection_closed(avrcp_callback, local_cid); 12796f43fcd7SMatthias Ringwald // free connection 12806086246cSMilanka Ringwald btstack_linked_list_remove(&context->connections, (btstack_linked_item_t*) connection); 12816f43fcd7SMatthias Ringwald btstack_memory_avrcp_connection_free(connection); 1282be32e7f1SMilanka Ringwald break; 1283be32e7f1SMilanka Ringwald } 1284be32e7f1SMilanka Ringwald break; 1285be32e7f1SMilanka Ringwald default: 1286be32e7f1SMilanka Ringwald break; 1287be32e7f1SMilanka Ringwald } 1288be32e7f1SMilanka Ringwald break; 1289be32e7f1SMilanka Ringwald default: 1290be32e7f1SMilanka Ringwald break; 1291be32e7f1SMilanka Ringwald } 1292be32e7f1SMilanka Ringwald } 1293be32e7f1SMilanka Ringwald 12946086246cSMilanka Ringwald static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 12956086246cSMilanka Ringwald packet_handler(packet_type, channel, packet, size, &avrcp_controller_context); 12966086246cSMilanka Ringwald } 12976086246cSMilanka Ringwald 12986086246cSMilanka Ringwald void avrcp_controller_init(void){ 12996086246cSMilanka Ringwald avrcp_controller_context.role = AVRCP_CONTROLLER; 13006086246cSMilanka Ringwald avrcp_controller_context.connections = NULL; 13016086246cSMilanka Ringwald avrcp_controller_context.avrcp_callback = avrcp_callback; 13026086246cSMilanka Ringwald avrcp_controller_context.packet_handler = avrcp_controller_packet_handler; 1303a0f524f0SMatthias Ringwald l2cap_register_service(&avrcp_controller_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_0); 1304be32e7f1SMilanka Ringwald } 1305be32e7f1SMilanka Ringwald 1306be32e7f1SMilanka Ringwald void avrcp_register_packet_handler(btstack_packet_handler_t callback){ 1307be32e7f1SMilanka Ringwald if (callback == NULL){ 1308be32e7f1SMilanka Ringwald log_error("avrcp_register_packet_handler called with NULL callback"); 1309be32e7f1SMilanka Ringwald return; 1310be32e7f1SMilanka Ringwald } 1311be32e7f1SMilanka Ringwald avrcp_callback = callback; 1312df642728SMilanka Ringwald avrcp_controller_context.avrcp_callback = avrcp_callback; 1313be32e7f1SMilanka Ringwald } 1314be32e7f1SMilanka Ringwald 13156086246cSMilanka Ringwald uint8_t avrcp_connect(bd_addr_t bd_addr, avrcp_context_t * context, uint16_t * avrcp_cid){ 13166086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_bd_addr(bd_addr, context); 13178a2c6c7cSMilanka Ringwald if (connection){ 13188a2c6c7cSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 13198a2c6c7cSMilanka Ringwald } 13208a2c6c7cSMilanka Ringwald 13216086246cSMilanka Ringwald connection = avrcp_create_connection(bd_addr, context); 1322be32e7f1SMilanka Ringwald if (!connection){ 13238a2c6c7cSMilanka Ringwald log_error("avrcp: could not allocate connection struct."); 13248a2c6c7cSMilanka Ringwald return BTSTACK_MEMORY_ALLOC_FAILED; 1325be32e7f1SMilanka Ringwald } 13268a2c6c7cSMilanka Ringwald 13276086246cSMilanka Ringwald if (!avrcp_cid) return L2CAP_LOCAL_CID_DOES_NOT_EXIST; 13286086246cSMilanka Ringwald 13296086246cSMilanka Ringwald *avrcp_cid = avrcp_cid_counter++; 13306086246cSMilanka Ringwald connection->avrcp_cid = *avrcp_cid; 13316086246cSMilanka Ringwald 13326086246cSMilanka Ringwald connection->state = AVCTP_SIGNALING_W4_SDP_QUERY_COMPLETE; 1333df642728SMilanka Ringwald sdp_query_context.avrcp_context = context; 13346086246cSMilanka Ringwald sdp_query_context.avrcp_l2cap_psm = 0; 13356086246cSMilanka Ringwald sdp_query_context.avrcp_version = 0; 1336df642728SMilanka Ringwald sdp_query_context.connection = connection; 13376086246cSMilanka Ringwald 13386086246cSMilanka Ringwald sdp_client_query_uuid16(&avrcp_handle_sdp_client_query_result, bd_addr, BLUETOOTH_PROTOCOL_AVCTP); 13398a2c6c7cSMilanka Ringwald return ERROR_CODE_SUCCESS; 1340be32e7f1SMilanka Ringwald } 1341be32e7f1SMilanka Ringwald 13426086246cSMilanka Ringwald uint8_t avrcp_controller_connect(bd_addr_t bd_addr, uint16_t * avrcp_cid){ 13436086246cSMilanka Ringwald return avrcp_connect(bd_addr, &avrcp_controller_context, avrcp_cid); 13446086246cSMilanka Ringwald } 13456086246cSMilanka Ringwald 134631a5a745SMatthias Ringwald uint8_t avrcp_unit_info(uint16_t avrcp_cid){ 13476086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1348be32e7f1SMilanka Ringwald if (!connection){ 1349be32e7f1SMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 135031a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1351be32e7f1SMilanka Ringwald } 135231a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1353be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1354be32e7f1SMilanka Ringwald 1355be32e7f1SMilanka Ringwald connection->transaction_label++; 13569cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_UNIT_INFO; 1357be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1358be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique 1359be32e7f1SMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE; 1360adcdded9SMilanka Ringwald memset(connection->cmd_operands, 0xFF, connection->cmd_operands_length); 1361adcdded9SMilanka Ringwald connection->cmd_operands_length = 5; 1362be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 136331a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1364be32e7f1SMilanka Ringwald } 1365be32e7f1SMilanka Ringwald 136631a5a745SMatthias Ringwald static uint8_t avrcp_get_capabilities(uint16_t avrcp_cid, uint8_t capability_id){ 13676086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1368be32e7f1SMilanka Ringwald if (!connection){ 1369be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 137031a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1371be32e7f1SMilanka Ringwald } 137231a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1373be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1374be32e7f1SMilanka Ringwald 1375be32e7f1SMilanka Ringwald connection->transaction_label++; 13769cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1377be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1378be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1379d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 1380be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); 1381be32e7f1SMilanka Ringwald connection->cmd_operands[3] = AVRCP_PDU_ID_GET_CAPABILITIES; // PDU ID 1382be32e7f1SMilanka Ringwald connection->cmd_operands[4] = 0; 1383be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, 5, 1); // parameter length 1384be32e7f1SMilanka Ringwald connection->cmd_operands[7] = capability_id; // capability ID 1385adcdded9SMilanka Ringwald connection->cmd_operands_length = 8; 1386be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 138731a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1388be32e7f1SMilanka Ringwald } 1389be32e7f1SMilanka Ringwald 139031a5a745SMatthias Ringwald uint8_t avrcp_get_supported_company_ids(uint16_t avrcp_cid){ 139131a5a745SMatthias Ringwald return avrcp_get_capabilities(avrcp_cid, AVRCP_CAPABILITY_ID_COMPANY); 1392be32e7f1SMilanka Ringwald } 1393be32e7f1SMilanka Ringwald 139431a5a745SMatthias Ringwald uint8_t avrcp_get_supported_events(uint16_t avrcp_cid){ 139531a5a745SMatthias Ringwald return avrcp_get_capabilities(avrcp_cid, AVRCP_CAPABILITY_ID_EVENT); 1396be32e7f1SMilanka Ringwald } 1397be32e7f1SMilanka Ringwald 1398be32e7f1SMilanka Ringwald 139931a5a745SMatthias Ringwald uint8_t avrcp_play(uint16_t avrcp_cid){ 1400d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_PLAY, 0); 1401be32e7f1SMilanka Ringwald } 1402be32e7f1SMilanka Ringwald 140331a5a745SMatthias Ringwald uint8_t avrcp_stop(uint16_t avrcp_cid){ 1404d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_STOP, 0); 1405be32e7f1SMilanka Ringwald } 1406be32e7f1SMilanka Ringwald 140731a5a745SMatthias Ringwald uint8_t avrcp_pause(uint16_t avrcp_cid){ 1408d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_PAUSE, 0); 1409be32e7f1SMilanka Ringwald } 1410be32e7f1SMilanka Ringwald 141131a5a745SMatthias Ringwald uint8_t avrcp_forward(uint16_t avrcp_cid){ 1412d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FORWARD, 0); 1413be32e7f1SMilanka Ringwald } 1414be32e7f1SMilanka Ringwald 141531a5a745SMatthias Ringwald uint8_t avrcp_backward(uint16_t avrcp_cid){ 1416d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_BACKWARD, 0); 1417be32e7f1SMilanka Ringwald } 1418be32e7f1SMilanka Ringwald 141931a5a745SMatthias Ringwald uint8_t avrcp_start_rewind(uint16_t avrcp_cid){ 1420d1845da3SMilanka Ringwald return request_continuous_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_REWIND, 0); 1421be32e7f1SMilanka Ringwald } 1422be32e7f1SMilanka Ringwald 142331a5a745SMatthias Ringwald uint8_t avrcp_volume_up(uint16_t avrcp_cid){ 1424d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_VOLUME_UP, 0); 1425be32e7f1SMilanka Ringwald } 1426be32e7f1SMilanka Ringwald 142731a5a745SMatthias Ringwald uint8_t avrcp_volume_down(uint16_t avrcp_cid){ 1428d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_VOLUME_DOWN, 0); 1429be32e7f1SMilanka Ringwald } 1430be32e7f1SMilanka Ringwald 143131a5a745SMatthias Ringwald uint8_t avrcp_mute(uint16_t avrcp_cid){ 1432d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_MUTE, 0); 1433be32e7f1SMilanka Ringwald } 1434be32e7f1SMilanka Ringwald 143531a5a745SMatthias Ringwald uint8_t avrcp_skip(uint16_t avrcp_cid){ 1436d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_SKIP, 0); 1437be32e7f1SMilanka Ringwald } 1438be32e7f1SMilanka Ringwald 143931a5a745SMatthias Ringwald uint8_t avrcp_stop_rewind(uint16_t avrcp_cid){ 14406086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1441be32e7f1SMilanka Ringwald if (!connection){ 1442be32e7f1SMilanka Ringwald log_error("avrcp_stop_rewind: could not find a connection."); 144331a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1444be32e7f1SMilanka Ringwald } 144531a5a745SMatthias Ringwald if (connection->state != AVCTP_W4_STOP) return ERROR_CODE_COMMAND_DISALLOWED; 144631a5a745SMatthias Ringwald return request_pass_through_release_control_cmd(connection); 1447be32e7f1SMilanka Ringwald } 1448be32e7f1SMilanka Ringwald 144931a5a745SMatthias Ringwald uint8_t avrcp_start_fast_forward(uint16_t avrcp_cid){ 1450d1845da3SMilanka Ringwald return request_continuous_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FAST_FORWARD, 0); 1451be32e7f1SMilanka Ringwald } 1452be32e7f1SMilanka Ringwald 1453d1845da3SMilanka Ringwald uint8_t avrcp_fast_forward(uint16_t avrcp_cid){ 1454d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FAST_FORWARD, 0); 1455d1845da3SMilanka Ringwald } 1456d1845da3SMilanka Ringwald 1457d1845da3SMilanka Ringwald uint8_t avrcp_rewind(uint16_t avrcp_cid){ 1458d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_REWIND, 0); 1459d1845da3SMilanka Ringwald } 1460d1845da3SMilanka Ringwald 1461d1845da3SMilanka Ringwald 146231a5a745SMatthias Ringwald uint8_t avrcp_stop_fast_forward(uint16_t avrcp_cid){ 14636086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1464be32e7f1SMilanka Ringwald if (!connection){ 1465be32e7f1SMilanka Ringwald log_error("avrcp_stop_fast_forward: could not find a connection."); 146631a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1467be32e7f1SMilanka Ringwald } 146831a5a745SMatthias Ringwald if (connection->state != AVCTP_W4_STOP) return ERROR_CODE_COMMAND_DISALLOWED; 146931a5a745SMatthias Ringwald return request_pass_through_release_control_cmd(connection); 1470be32e7f1SMilanka Ringwald } 1471be32e7f1SMilanka Ringwald 147231a5a745SMatthias Ringwald uint8_t avrcp_get_play_status(uint16_t avrcp_cid){ 14736086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1474be32e7f1SMilanka Ringwald if (!connection){ 1475be32e7f1SMilanka Ringwald log_error("avrcp_get_play_status: could not find a connection."); 147631a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1477be32e7f1SMilanka Ringwald } 147831a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1479be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1480be32e7f1SMilanka Ringwald connection->transaction_label++; 14819cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1482be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1483be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1484d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 1485be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); 1486be32e7f1SMilanka Ringwald connection->cmd_operands[3] = AVRCP_PDU_ID_GET_PLAY_STATUS; 1487be32e7f1SMilanka Ringwald connection->cmd_operands[4] = 0; // reserved(upper 6) | packet_type -> 0 1488be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, 5, 0); // parameter length 1489adcdded9SMilanka Ringwald connection->cmd_operands_length = 7; 1490be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 149131a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1492be32e7f1SMilanka Ringwald } 1493be32e7f1SMilanka Ringwald 149431a5a745SMatthias Ringwald uint8_t avrcp_enable_notification(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id){ 14956086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1496be32e7f1SMilanka Ringwald if (!connection){ 1497be32e7f1SMilanka Ringwald log_error("avrcp_get_play_status: could not find a connection."); 149831a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1499be32e7f1SMilanka Ringwald } 1500be32e7f1SMilanka Ringwald avrcp_register_notification(connection, event_id); 150131a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1502be32e7f1SMilanka Ringwald } 1503be32e7f1SMilanka Ringwald 150431a5a745SMatthias Ringwald uint8_t avrcp_disable_notification(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id){ 15056086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1506be32e7f1SMilanka Ringwald if (!connection){ 1507be32e7f1SMilanka Ringwald log_error("avrcp_get_play_status: could not find a connection."); 150831a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1509be32e7f1SMilanka Ringwald } 1510be32e7f1SMilanka Ringwald connection->notifications_to_deregister |= (1 << event_id); 151131a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1512be32e7f1SMilanka Ringwald } 1513be32e7f1SMilanka Ringwald 151431a5a745SMatthias Ringwald uint8_t avrcp_get_now_playing_info(uint16_t avrcp_cid){ 15156086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1516be32e7f1SMilanka Ringwald if (!connection){ 1517be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 151831a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1519be32e7f1SMilanka Ringwald } 152031a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1521be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1522be32e7f1SMilanka Ringwald 1523be32e7f1SMilanka Ringwald connection->transaction_label++; 15249cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1525be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1526be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1527d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 1528be32e7f1SMilanka Ringwald int pos = 0; 1529be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 1530be32e7f1SMilanka Ringwald pos += 3; 1531be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES; // PDU ID 1532be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; 1533be32e7f1SMilanka Ringwald 1534be32e7f1SMilanka Ringwald // Parameter Length 1535be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 9); 1536be32e7f1SMilanka Ringwald pos += 2; 1537be32e7f1SMilanka Ringwald 1538be32e7f1SMilanka Ringwald // write 8 bytes value 1539be32e7f1SMilanka Ringwald memset(connection->cmd_operands + pos, 0, 8); // identifier: PLAYING 1540be32e7f1SMilanka Ringwald pos += 8; 1541be32e7f1SMilanka Ringwald 1542be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; // attribute count, if 0 get all attributes 1543be32e7f1SMilanka Ringwald // every attribute is 4 bytes long 1544be32e7f1SMilanka Ringwald 1545adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 1546be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 154731a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1548be32e7f1SMilanka Ringwald } 1549be32e7f1SMilanka Ringwald 155031a5a745SMatthias Ringwald uint8_t avrcp_set_absolute_volume(uint16_t avrcp_cid, uint8_t volume){ 15516086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1552be32e7f1SMilanka Ringwald if (!connection){ 1553be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 155431a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1555be32e7f1SMilanka Ringwald } 155631a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1557be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1558be32e7f1SMilanka Ringwald 1559be32e7f1SMilanka Ringwald connection->transaction_label++; 15609cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1561be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_CONTROL; 1562be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1563d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 1564be32e7f1SMilanka Ringwald int pos = 0; 1565be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 1566be32e7f1SMilanka Ringwald pos += 3; 1567be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME; // PDU ID 1568be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; 1569be32e7f1SMilanka Ringwald 1570be32e7f1SMilanka Ringwald // Parameter Length 1571be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 1); 1572be32e7f1SMilanka Ringwald pos += 2; 1573be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = volume; 1574be32e7f1SMilanka Ringwald 1575adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 1576be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 157731a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1578be32e7f1SMilanka Ringwald } 1579be32e7f1SMilanka Ringwald 158031a5a745SMatthias Ringwald uint8_t avrcp_query_shuffle_and_repeat_modes(uint16_t avrcp_cid){ 15816086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1582be32e7f1SMilanka Ringwald if (!connection){ 1583be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 158431a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1585be32e7f1SMilanka Ringwald } 158631a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1587be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1588be32e7f1SMilanka Ringwald 1589be32e7f1SMilanka Ringwald connection->transaction_label++; 15909cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1591be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1592be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1593d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 1594be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); 1595be32e7f1SMilanka Ringwald connection->cmd_operands[3] = AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue; // PDU ID 1596be32e7f1SMilanka Ringwald connection->cmd_operands[4] = 0; 1597be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, 5, 5); // parameter length 1598be32e7f1SMilanka Ringwald connection->cmd_operands[7] = 4; // NumPlayerApplicationSettingAttributeID 1599be32e7f1SMilanka Ringwald // PlayerApplicationSettingAttributeID1 AVRCP Spec, Appendix F, 133 1600be32e7f1SMilanka Ringwald connection->cmd_operands[8] = 0x01; // equalizer (1-OFF, 2-ON) 1601be32e7f1SMilanka Ringwald connection->cmd_operands[9] = 0x02; // repeat (1-off, 2-single track, 3-all tracks, 4-group repeat) 1602be32e7f1SMilanka Ringwald connection->cmd_operands[10] = 0x03; // shuffle (1-off, 2-all tracks, 3-group shuffle) 1603be32e7f1SMilanka Ringwald connection->cmd_operands[11] = 0x04; // scan (1-off, 2-all tracks, 3-group scan) 1604adcdded9SMilanka Ringwald connection->cmd_operands_length = 12; 1605be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 160631a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1607be32e7f1SMilanka Ringwald } 1608be32e7f1SMilanka Ringwald 16096086246cSMilanka Ringwald static uint8_t avrcp_set_current_player_application_setting_value(uint16_t avrcp_cid, uint8_t attr_id, uint8_t attr_value){ 16106086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1611be32e7f1SMilanka Ringwald if (!connection){ 1612be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 161331a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1614be32e7f1SMilanka Ringwald } 161531a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1616be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1617be32e7f1SMilanka Ringwald 1618be32e7f1SMilanka Ringwald connection->transaction_label++; 16199cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1620be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_CONTROL; 1621be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1622d5ff3b26SMatthias Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 1623be32e7f1SMilanka Ringwald int pos = 0; 1624be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 1625be32e7f1SMilanka Ringwald pos += 3; 1626be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_SetPlayerApplicationSettingValue; // PDU ID 1627be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; 1628be32e7f1SMilanka Ringwald // Parameter Length 1629be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 3); 1630be32e7f1SMilanka Ringwald pos += 2; 1631be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 2; 1632adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 16336086246cSMilanka Ringwald connection->cmd_operands[pos++] = attr_id; 16346086246cSMilanka Ringwald connection->cmd_operands[pos++] = attr_value; 1635adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 1636be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 163731a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1638be32e7f1SMilanka Ringwald } 1639be32e7f1SMilanka Ringwald 164031a5a745SMatthias Ringwald uint8_t avrcp_set_shuffle_mode(uint16_t avrcp_cid, avrcp_shuffle_mode_t mode){ 164131a5a745SMatthias Ringwald if (mode < AVRCP_SHUFFLE_MODE_OFF || mode > AVRCP_SHUFFLE_MODE_GROUP) return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 164231a5a745SMatthias Ringwald return avrcp_set_current_player_application_setting_value(avrcp_cid, 0x03, mode); 1643be32e7f1SMilanka Ringwald } 1644be32e7f1SMilanka Ringwald 164531a5a745SMatthias Ringwald uint8_t avrcp_set_repeat_mode(uint16_t avrcp_cid, avrcp_repeat_mode_t mode){ 164631a5a745SMatthias Ringwald if (mode < AVRCP_REPEAT_MODE_OFF || mode > AVRCP_REPEAT_MODE_GROUP) return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 164731a5a745SMatthias Ringwald return avrcp_set_current_player_application_setting_value(avrcp_cid, 0x02, mode); 1648be32e7f1SMilanka Ringwald } 1649be32e7f1SMilanka Ringwald 165031a5a745SMatthias Ringwald uint8_t avrcp_disconnect(uint16_t avrcp_cid){ 16516086246cSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); 1652be32e7f1SMilanka Ringwald if (!connection){ 1653be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 165431a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1655be32e7f1SMilanka Ringwald } 165631a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1657294335f1SMilanka Ringwald l2cap_disconnect(connection->l2cap_signaling_cid, 0); 165831a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1659be32e7f1SMilanka Ringwald }