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 #include <unistd.h> 45be32e7f1SMilanka Ringwald 46be32e7f1SMilanka Ringwald #include "btstack.h" 47be32e7f1SMilanka Ringwald #include "classic/avrcp.h" 48be32e7f1SMilanka Ringwald 49be32e7f1SMilanka Ringwald #define AV_REMOTE_CONTROL_TARGET 0x110C 50be32e7f1SMilanka Ringwald #define AV_REMOTE_CONTROL 0x110E 51be32e7f1SMilanka Ringwald #define AV_REMOTE_CONTROL_CONTROLLER 0x110F 52be32e7f1SMilanka Ringwald 53*235946f1SMatthias Ringwald #define PSM_AVCTP BLUETOOTH_PROTOCOL_AVCTP 54be32e7f1SMilanka Ringwald #define PSM_AVCTP_BROWSING 0xFF17 55be32e7f1SMilanka Ringwald 56be32e7f1SMilanka Ringwald /* 57be32e7f1SMilanka Ringwald Category 1: Player/Recorder 58be32e7f1SMilanka Ringwald Category 2: Monitor/Amplifier 59be32e7f1SMilanka Ringwald Category 3: Tuner 60be32e7f1SMilanka Ringwald Category 4: Menu 61be32e7f1SMilanka Ringwald */ 62be32e7f1SMilanka Ringwald 63be32e7f1SMilanka Ringwald /* controller supported features 64be32e7f1SMilanka Ringwald Bit 0 = Category 1 65be32e7f1SMilanka Ringwald Bit 1 = Category 2 66be32e7f1SMilanka Ringwald Bit 2 = Category 3 67be32e7f1SMilanka Ringwald Bit 3 = Category 4 68be32e7f1SMilanka Ringwald Bit 4-5 = RFA 69be32e7f1SMilanka Ringwald Bit 6 = Supports browsing 70be32e7f1SMilanka Ringwald Bit 7-15 = RFA 71be32e7f1SMilanka Ringwald The bits for supported categories are set to 1. Others are set to 0. 72be32e7f1SMilanka Ringwald */ 73be32e7f1SMilanka Ringwald 74be32e7f1SMilanka Ringwald /* target supported features 75be32e7f1SMilanka Ringwald Bit 0 = Category 1 76be32e7f1SMilanka Ringwald Bit 1 = Category 2 77be32e7f1SMilanka Ringwald Bit 2 = Category 3 78be32e7f1SMilanka Ringwald Bit 3 = Category 4 79be32e7f1SMilanka Ringwald Bit 4 = Player Application Settings. Bit 0 should be set for this bit to be set. 80be32e7f1SMilanka Ringwald Bit 5 = Group Navigation. Bit 0 should be set for this bit to be set. 81be32e7f1SMilanka Ringwald Bit 6 = Supports browsing*4 82be32e7f1SMilanka Ringwald Bit 7 = Supports multiple media player applications 83be32e7f1SMilanka Ringwald Bit 8-15 = RFA 84be32e7f1SMilanka Ringwald The bits for supported categories are set to 1. Others are set to 0. 85be32e7f1SMilanka Ringwald */ 86be32e7f1SMilanka Ringwald 87be32e7f1SMilanka Ringwald // TODO: merge with avdtp_packet_type_t 88be32e7f1SMilanka Ringwald typedef enum { 89be32e7f1SMilanka Ringwald AVRCP_SINGLE_PACKET= 0, 90be32e7f1SMilanka Ringwald AVRCP_START_PACKET , 91be32e7f1SMilanka Ringwald AVRCP_CONTINUE_PACKET , 92be32e7f1SMilanka Ringwald AVRCP_END_PACKET 93be32e7f1SMilanka Ringwald } avrcp_packet_type_t; 94be32e7f1SMilanka Ringwald 95be32e7f1SMilanka Ringwald typedef enum { 96be32e7f1SMilanka Ringwald AVRCP_COMMAND_FRAME = 0, 97be32e7f1SMilanka Ringwald AVRCP_RESPONSE_FRAME 98be32e7f1SMilanka Ringwald } avrcp_frame_type_t; 99be32e7f1SMilanka Ringwald 100be32e7f1SMilanka Ringwald static const char * default_avrcp_controller_service_name = "BTstack AVRCP Controller Service"; 101be32e7f1SMilanka Ringwald static const char * default_avrcp_controller_service_provider_name = "BTstack AVRCP Controller Service Provider"; 102be32e7f1SMilanka Ringwald static const char * default_avrcp_target_service_name = "BTstack AVRCP Target Service"; 103be32e7f1SMilanka Ringwald static const char * default_avrcp_target_service_provider_name = "BTstack AVRCP Target Service Provider"; 104be32e7f1SMilanka Ringwald 105be32e7f1SMilanka Ringwald static btstack_linked_list_t avrcp_connections; 106be32e7f1SMilanka Ringwald static btstack_packet_handler_t avrcp_callback; 107be32e7f1SMilanka Ringwald 108be32e7f1SMilanka Ringwald static const char * avrcp_subunit_type_name[] = { 109be32e7f1SMilanka Ringwald "MONITOR", "AUDIO", "PRINTER", "DISC", "TAPE_RECORDER_PLAYER", "TUNER", 110be32e7f1SMilanka Ringwald "CA", "CAMERA", "RESERVED", "PANEL", "BULLETIN_BOARD", "CAMERA_STORAGE", 111be32e7f1SMilanka Ringwald "VENDOR_UNIQUE", "RESERVED_FOR_ALL_SUBUNIT_TYPES", 112be32e7f1SMilanka Ringwald "EXTENDED_TO_NEXT_BYTE", "UNIT", "ERROR" 113be32e7f1SMilanka Ringwald }; 114be32e7f1SMilanka Ringwald const char * avrcp_subunit2str(uint16_t index){ 115be32e7f1SMilanka Ringwald if (index <= 11) return avrcp_subunit_type_name[index]; 116be32e7f1SMilanka Ringwald if (index >= 0x1C && index <= 0x1F) return avrcp_subunit_type_name[index - 0x10]; 117be32e7f1SMilanka Ringwald return avrcp_subunit_type_name[16]; 118be32e7f1SMilanka Ringwald } 119be32e7f1SMilanka Ringwald 120be32e7f1SMilanka Ringwald static const char * avrcp_event_name[] = { 121be32e7f1SMilanka Ringwald "ERROR", "PLAYBACK_STATUS_CHANGED", 122be32e7f1SMilanka Ringwald "TRACK_CHANGED", "TRACK_REACHED_END", "TRACK_REACHED_START", 123be32e7f1SMilanka Ringwald "PLAYBACK_POS_CHANGED", "BATT_STATUS_CHANGED", "SYSTEM_STATUS_CHANGED", 124be32e7f1SMilanka Ringwald "PLAYER_APPLICATION_SETTING_CHANGED", "NOW_PLAYING_CONTENT_CHANGED", 125be32e7f1SMilanka Ringwald "AVAILABLE_PLAYERS_CHANGED", "ADDRESSED_PLAYER_CHANGED", "UIDS_CHANGED", "VOLUME_CHANGED" 126be32e7f1SMilanka Ringwald }; 127be32e7f1SMilanka Ringwald const char * avrcp_event2str(uint16_t index){ 128be32e7f1SMilanka Ringwald if (index <= 0x0d) return avrcp_event_name[index]; 129be32e7f1SMilanka Ringwald return avrcp_event_name[0]; 130be32e7f1SMilanka Ringwald } 131be32e7f1SMilanka Ringwald 132be32e7f1SMilanka Ringwald static const char * avrcp_operation_name[] = { 133be32e7f1SMilanka Ringwald "NOT SUPPORTED", // 0x3B 134be32e7f1SMilanka Ringwald "SKIP", "NOT SUPPORTED", "NOT SUPPORTED", "NOT SUPPORTED", "NOT SUPPORTED", 135be32e7f1SMilanka Ringwald "VOLUME_UP", "VOLUME_DOWN", "MUTE", "PLAY", "STOP", "PAUSE", "NOT SUPPORTED", 136be32e7f1SMilanka Ringwald "REWIND", "FAST_FORWARD", "NOT SUPPORTED", "FORWARD", "BACKWARD" // 0x4C 137be32e7f1SMilanka Ringwald }; 138be32e7f1SMilanka Ringwald const char * avrcp_operation2str(uint8_t index){ 139be32e7f1SMilanka Ringwald if (index >= 0x3B && index <= 0x4C) return avrcp_operation_name[index - 0x3B]; 140be32e7f1SMilanka Ringwald return avrcp_operation_name[0]; 141be32e7f1SMilanka Ringwald } 142be32e7f1SMilanka Ringwald 143be32e7f1SMilanka Ringwald static const char * avrcp_media_attribute_id_name[] = { 144be32e7f1SMilanka Ringwald "NONE", "TITLE", "ARTIST", "ALBUM", "TRACK", "TOTAL TRACKS", "GENRE", "SONG LENGTH" 145be32e7f1SMilanka Ringwald }; 146be32e7f1SMilanka Ringwald const char * avrcp_attribute2str(uint8_t index){ 147be32e7f1SMilanka Ringwald if (index >= 1 && index <= 7) return avrcp_media_attribute_id_name[index]; 148be32e7f1SMilanka Ringwald return avrcp_media_attribute_id_name[0]; 149be32e7f1SMilanka Ringwald } 150be32e7f1SMilanka Ringwald 151be32e7f1SMilanka Ringwald static const char * avrcp_play_status_name[] = { 152be32e7f1SMilanka Ringwald "STOPPED", "PLAYING", "PAUSED", "FORWARD SEEK", "REVERSE SEEK", 153be32e7f1SMilanka Ringwald "ERROR" // 0xFF 154be32e7f1SMilanka Ringwald }; 155be32e7f1SMilanka Ringwald const char * avrcp_play_status2str(uint8_t index){ 156be32e7f1SMilanka Ringwald if (index >= 1 && index <= 4) return avrcp_play_status_name[index]; 157be32e7f1SMilanka Ringwald return avrcp_play_status_name[5]; 158be32e7f1SMilanka Ringwald } 159be32e7f1SMilanka Ringwald 160be32e7f1SMilanka Ringwald static const char * avrcp_ctype_name[] = { 161be32e7f1SMilanka Ringwald "CONTROL", 162be32e7f1SMilanka Ringwald "STATUS", 163be32e7f1SMilanka Ringwald "SPECIFIC_INQUIRY", 164be32e7f1SMilanka Ringwald "NOTIFY", 165be32e7f1SMilanka Ringwald "GENERAL_INQUIRY", 166be32e7f1SMilanka Ringwald "RESERVED5", 167be32e7f1SMilanka Ringwald "RESERVED6", 168be32e7f1SMilanka Ringwald "RESERVED7", 169be32e7f1SMilanka Ringwald "NOT_IMPLEMENTED", 170be32e7f1SMilanka Ringwald "ACCEPTED", 171be32e7f1SMilanka Ringwald "REJECTED", 172be32e7f1SMilanka Ringwald "IN_TRANSITION", 173be32e7f1SMilanka Ringwald "IMPLEMENTED_STABLE", 174be32e7f1SMilanka Ringwald "CHANGED_STABLE", 175be32e7f1SMilanka Ringwald "RESERVED", 176be32e7f1SMilanka Ringwald "INTERIM" 177be32e7f1SMilanka Ringwald }; 178be32e7f1SMilanka Ringwald const char * avrcp_ctype2str(uint8_t index){ 179be32e7f1SMilanka Ringwald if (index >= 0 && index < sizeof(avrcp_ctype_name)){ 180be32e7f1SMilanka Ringwald return avrcp_ctype_name[index]; 181be32e7f1SMilanka Ringwald } 182be32e7f1SMilanka Ringwald return "NONE"; 183be32e7f1SMilanka Ringwald } 184be32e7f1SMilanka Ringwald 185be32e7f1SMilanka Ringwald static const char * avrcp_shuffle_mode_name[] = { 186be32e7f1SMilanka Ringwald "SHUFFLE OFF", 187be32e7f1SMilanka Ringwald "SHUFFLE ALL TRACKS", 188be32e7f1SMilanka Ringwald "SHUFFLE GROUP" 189be32e7f1SMilanka Ringwald }; 190be32e7f1SMilanka Ringwald 191be32e7f1SMilanka Ringwald const char * avrcp_shuffle2str(uint8_t index){ 192be32e7f1SMilanka Ringwald if (index >= 1 && index <= 3) return avrcp_shuffle_mode_name[index-1]; 193be32e7f1SMilanka Ringwald return "NONE"; 194be32e7f1SMilanka Ringwald } 195be32e7f1SMilanka Ringwald 196be32e7f1SMilanka Ringwald static const char * avrcp_repeat_mode_name[] = { 197be32e7f1SMilanka Ringwald "REPEAT OFF", 198be32e7f1SMilanka Ringwald "REPEAT SINGLE TRACK", 199be32e7f1SMilanka Ringwald "REPEAT ALL TRACKS", 200be32e7f1SMilanka Ringwald "REPEAT GROUP" 201be32e7f1SMilanka Ringwald }; 202be32e7f1SMilanka Ringwald 203be32e7f1SMilanka Ringwald const char * avrcp_repeat2str(uint8_t index){ 204be32e7f1SMilanka Ringwald if (index >= 1 && index <= 4) return avrcp_repeat_mode_name[index-1]; 205be32e7f1SMilanka Ringwald return "NONE"; 206be32e7f1SMilanka Ringwald } 207be32e7f1SMilanka Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 208be32e7f1SMilanka Ringwald 209be32e7f1SMilanka 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){ 210be32e7f1SMilanka Ringwald uint8_t* attribute; 211be32e7f1SMilanka Ringwald de_create_sequence(service); 212be32e7f1SMilanka Ringwald 213be32e7f1SMilanka Ringwald // 0x0000 "Service Record Handle" 214*235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE); 215be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle); 216be32e7f1SMilanka Ringwald 217be32e7f1SMilanka Ringwald // 0x0001 "Service Class ID List" 218*235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST); 219be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 220be32e7f1SMilanka Ringwald { 221be32e7f1SMilanka Ringwald if (controller){ 222be32e7f1SMilanka Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, AV_REMOTE_CONTROL); 223be32e7f1SMilanka Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, AV_REMOTE_CONTROL_CONTROLLER); 224be32e7f1SMilanka Ringwald } else { 225be32e7f1SMilanka Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, AV_REMOTE_CONTROL_TARGET); 226be32e7f1SMilanka Ringwald } 227be32e7f1SMilanka Ringwald } 228be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 229be32e7f1SMilanka Ringwald 230be32e7f1SMilanka Ringwald // 0x0004 "Protocol Descriptor List" 231*235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST); 232be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 233be32e7f1SMilanka Ringwald { 234be32e7f1SMilanka Ringwald uint8_t* l2cpProtocol = de_push_sequence(attribute); 235be32e7f1SMilanka Ringwald { 236*235946f1SMatthias Ringwald de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP); 237*235946f1SMatthias Ringwald de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP); 238be32e7f1SMilanka Ringwald } 239be32e7f1SMilanka Ringwald de_pop_sequence(attribute, l2cpProtocol); 240be32e7f1SMilanka Ringwald 241be32e7f1SMilanka Ringwald uint8_t* avctpProtocol = de_push_sequence(attribute); 242be32e7f1SMilanka Ringwald { 243*235946f1SMatthias Ringwald de_add_number(avctpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP); // avctpProtocol_service 244be32e7f1SMilanka Ringwald de_add_number(avctpProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version 245be32e7f1SMilanka Ringwald } 246be32e7f1SMilanka Ringwald de_pop_sequence(attribute, avctpProtocol); 247be32e7f1SMilanka Ringwald 248be32e7f1SMilanka Ringwald if (browsing){ 249*235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST); 250be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 251be32e7f1SMilanka Ringwald { 252be32e7f1SMilanka Ringwald uint8_t* browsing_l2cpProtocol = de_push_sequence(attribute); 253be32e7f1SMilanka Ringwald { 254*235946f1SMatthias Ringwald de_add_number(browsing_l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP); 255be32e7f1SMilanka Ringwald de_add_number(browsing_l2cpProtocol, DE_UINT, DE_SIZE_16, PSM_AVCTP_BROWSING); 256be32e7f1SMilanka Ringwald } 257be32e7f1SMilanka Ringwald de_pop_sequence(attribute, browsing_l2cpProtocol); 258be32e7f1SMilanka Ringwald 259be32e7f1SMilanka Ringwald uint8_t* browsing_avctpProtocol = de_push_sequence(attribute); 260be32e7f1SMilanka Ringwald { 261*235946f1SMatthias Ringwald de_add_number(browsing_avctpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP); // browsing_avctpProtocol_service 262be32e7f1SMilanka Ringwald de_add_number(browsing_avctpProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version 263be32e7f1SMilanka Ringwald } 264be32e7f1SMilanka Ringwald de_pop_sequence(attribute, browsing_avctpProtocol); 265be32e7f1SMilanka Ringwald } 266be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 267be32e7f1SMilanka Ringwald } 268be32e7f1SMilanka Ringwald } 269be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 270be32e7f1SMilanka Ringwald 271be32e7f1SMilanka Ringwald // 0x0005 "Public Browse Group" 272*235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BROWSE_GROUP_LIST); // public browse group 273be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 274be32e7f1SMilanka Ringwald { 275*235946f1SMatthias Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT); 276be32e7f1SMilanka Ringwald } 277be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 278be32e7f1SMilanka Ringwald 279be32e7f1SMilanka Ringwald // 0x0009 "Bluetooth Profile Descriptor List" 280*235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST); 281be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 282be32e7f1SMilanka Ringwald { 283be32e7f1SMilanka Ringwald uint8_t *avrcProfile = de_push_sequence(attribute); 284be32e7f1SMilanka Ringwald { 285be32e7f1SMilanka Ringwald de_add_number(avrcProfile, DE_UUID, DE_SIZE_16, AV_REMOTE_CONTROL); 286be32e7f1SMilanka Ringwald de_add_number(avrcProfile, DE_UINT, DE_SIZE_16, 0x0105); 287be32e7f1SMilanka Ringwald } 288be32e7f1SMilanka Ringwald de_pop_sequence(attribute, avrcProfile); 289be32e7f1SMilanka Ringwald } 290be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 291be32e7f1SMilanka Ringwald 292be32e7f1SMilanka Ringwald 293be32e7f1SMilanka Ringwald // 0x0100 "Service Name" 294be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); 295be32e7f1SMilanka Ringwald if (service_name){ 296be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(service_name), (uint8_t *) service_name); 297be32e7f1SMilanka Ringwald } else { 298be32e7f1SMilanka Ringwald if (controller){ 299be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_controller_service_name), (uint8_t *) default_avrcp_controller_service_name); 300be32e7f1SMilanka Ringwald } else { 301be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_target_service_name), (uint8_t *) default_avrcp_target_service_name); 302be32e7f1SMilanka Ringwald } 303be32e7f1SMilanka Ringwald } 304be32e7f1SMilanka Ringwald 305be32e7f1SMilanka Ringwald // 0x0100 "Provider Name" 306be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102); 307be32e7f1SMilanka Ringwald if (service_provider_name){ 308be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name); 309be32e7f1SMilanka Ringwald } else { 310be32e7f1SMilanka Ringwald if (controller){ 311be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_controller_service_provider_name), (uint8_t *) default_avrcp_controller_service_provider_name); 312be32e7f1SMilanka Ringwald } else { 313be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_target_service_provider_name), (uint8_t *) default_avrcp_target_service_provider_name); 314be32e7f1SMilanka Ringwald } 315be32e7f1SMilanka Ringwald } 316be32e7f1SMilanka Ringwald 317be32e7f1SMilanka Ringwald // 0x0311 "Supported Features" 318be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); 319be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); 320be32e7f1SMilanka Ringwald } 321be32e7f1SMilanka Ringwald 322be32e7f1SMilanka 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){ 323be32e7f1SMilanka Ringwald avrcp_create_sdp_record(1, service, service_record_handle, browsing, supported_features, service_name, service_provider_name); 324be32e7f1SMilanka Ringwald } 325be32e7f1SMilanka Ringwald 326be32e7f1SMilanka 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){ 327be32e7f1SMilanka Ringwald avrcp_create_sdp_record(0, service, service_record_handle, browsing, supported_features, service_name, service_provider_name); 328be32e7f1SMilanka Ringwald } 329be32e7f1SMilanka Ringwald 330be32e7f1SMilanka Ringwald static void avrcp_emit_repeat_and_shuffle_mode(btstack_packet_handler_t callback, uint16_t con_handle, uint8_t status, avrcp_repeat_mode_t repeat_mode, avrcp_shuffle_mode_t shuffle_mode){ 331be32e7f1SMilanka Ringwald if (!callback) return; 332be32e7f1SMilanka Ringwald uint8_t event[8]; 333be32e7f1SMilanka Ringwald int pos = 0; 334be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 335be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 336be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE; 337be32e7f1SMilanka Ringwald little_endian_store_16(event, pos, con_handle); 338be32e7f1SMilanka Ringwald pos += 2; 339be32e7f1SMilanka Ringwald event[pos++] = status; 340be32e7f1SMilanka Ringwald event[pos++] = repeat_mode; 341be32e7f1SMilanka Ringwald event[pos++] = shuffle_mode; 342be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 343be32e7f1SMilanka Ringwald } 344be32e7f1SMilanka Ringwald 345be32e7f1SMilanka Ringwald static void avrcp_emit_connection_established(btstack_packet_handler_t callback, uint16_t con_handle, uint8_t status, uint16_t local_cid, bd_addr_t addr){ 346be32e7f1SMilanka Ringwald if (!callback) return; 347be32e7f1SMilanka Ringwald uint8_t event[14]; 348be32e7f1SMilanka Ringwald int pos = 0; 349be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 350be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 351be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_CONNECTION_ESTABLISHED; 352be32e7f1SMilanka Ringwald little_endian_store_16(event, pos, con_handle); 353be32e7f1SMilanka Ringwald pos += 2; 354be32e7f1SMilanka Ringwald event[pos++] = status; 355be32e7f1SMilanka Ringwald little_endian_store_16(event, pos, local_cid); 356be32e7f1SMilanka Ringwald pos += 2; 357be32e7f1SMilanka Ringwald reverse_bd_addr(addr,&event[pos]); 358be32e7f1SMilanka Ringwald pos += 6; 359be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 360be32e7f1SMilanka Ringwald } 361be32e7f1SMilanka Ringwald 362be32e7f1SMilanka Ringwald static void avrcp_emit_operation_status(btstack_packet_handler_t callback, uint8_t subevent, uint16_t con_handle, uint8_t status, uint8_t operation_id){ 363be32e7f1SMilanka Ringwald if (!callback) return; 364be32e7f1SMilanka Ringwald uint8_t event[7]; 365be32e7f1SMilanka Ringwald int pos = 0; 366be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 367be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 368be32e7f1SMilanka Ringwald event[pos++] = subevent; 369be32e7f1SMilanka Ringwald little_endian_store_16(event, pos, con_handle); 370be32e7f1SMilanka Ringwald pos += 2; 371be32e7f1SMilanka Ringwald event[pos++] = status; 372be32e7f1SMilanka Ringwald event[pos++] = operation_id; 373be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 374be32e7f1SMilanka Ringwald } 375be32e7f1SMilanka Ringwald 376be32e7f1SMilanka Ringwald static void avrcp_emit_connection_closed(btstack_packet_handler_t callback, uint16_t con_handle){ 377be32e7f1SMilanka Ringwald if (!callback) return; 378be32e7f1SMilanka Ringwald uint8_t event[5]; 379be32e7f1SMilanka Ringwald int pos = 0; 380be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 381be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 382be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_CONNECTION_RELEASED; 383be32e7f1SMilanka Ringwald little_endian_store_16(event, pos, con_handle); 384be32e7f1SMilanka Ringwald pos += 2; 385be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 386be32e7f1SMilanka Ringwald } 387be32e7f1SMilanka Ringwald 388be32e7f1SMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_bd_addr(bd_addr_t addr){ 389be32e7f1SMilanka Ringwald btstack_linked_list_iterator_t it; 390be32e7f1SMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_connections); 391be32e7f1SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 392be32e7f1SMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it); 393be32e7f1SMilanka Ringwald if (memcmp(addr, connection->remote_addr, 6) != 0) continue; 394be32e7f1SMilanka Ringwald return connection; 395be32e7f1SMilanka Ringwald } 396be32e7f1SMilanka Ringwald return NULL; 397be32e7f1SMilanka Ringwald } 398be32e7f1SMilanka Ringwald 399be32e7f1SMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_con_handle(hci_con_handle_t con_handle){ 400be32e7f1SMilanka Ringwald btstack_linked_list_iterator_t it; 401be32e7f1SMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_connections); 402be32e7f1SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 403be32e7f1SMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it); 404be32e7f1SMilanka Ringwald if (connection->con_handle != con_handle) continue; 405be32e7f1SMilanka Ringwald return connection; 406be32e7f1SMilanka Ringwald } 407be32e7f1SMilanka Ringwald return NULL; 408be32e7f1SMilanka Ringwald } 409be32e7f1SMilanka Ringwald 410be32e7f1SMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_l2cap_signaling_cid(uint16_t l2cap_cid){ 411be32e7f1SMilanka Ringwald btstack_linked_list_iterator_t it; 412be32e7f1SMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_connections); 413be32e7f1SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 414be32e7f1SMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it); 415be32e7f1SMilanka Ringwald if (connection->l2cap_signaling_cid != l2cap_cid) continue; 416be32e7f1SMilanka Ringwald return connection; 417be32e7f1SMilanka Ringwald } 418be32e7f1SMilanka Ringwald return NULL; 419be32e7f1SMilanka Ringwald } 420be32e7f1SMilanka Ringwald 421be32e7f1SMilanka Ringwald static void avrcp_request_can_send_now(avrcp_connection_t * connection, uint16_t l2cap_cid){ 422be32e7f1SMilanka Ringwald connection->wait_to_send = 1; 423be32e7f1SMilanka Ringwald l2cap_request_can_send_now_event(l2cap_cid); 424be32e7f1SMilanka Ringwald } 425be32e7f1SMilanka Ringwald 426be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timeout_handler(btstack_timer_source_t * timer){ 427be32e7f1SMilanka Ringwald UNUSED(timer); 428be32e7f1SMilanka Ringwald avrcp_connection_t * connection = btstack_run_loop_get_timer_context(timer); 429be32e7f1SMilanka Ringwald btstack_run_loop_set_timer(&connection->press_and_hold_cmd_timer, 2000); // 2 seconds timeout 430be32e7f1SMilanka Ringwald btstack_run_loop_add_timer(&connection->press_and_hold_cmd_timer); 431be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_PRESS_COMMAND; 432be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 433be32e7f1SMilanka Ringwald } 434be32e7f1SMilanka Ringwald 435be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timer_start(avrcp_connection_t * connection){ 436be32e7f1SMilanka Ringwald btstack_run_loop_remove_timer(&connection->press_and_hold_cmd_timer); 437be32e7f1SMilanka Ringwald btstack_run_loop_set_timer_handler(&connection->press_and_hold_cmd_timer, avrcp_press_and_hold_timeout_handler); 438be32e7f1SMilanka Ringwald btstack_run_loop_set_timer_context(&connection->press_and_hold_cmd_timer, connection); 439be32e7f1SMilanka Ringwald btstack_run_loop_set_timer(&connection->press_and_hold_cmd_timer, 2000); // 2 seconds timeout 440be32e7f1SMilanka Ringwald btstack_run_loop_add_timer(&connection->press_and_hold_cmd_timer); 441be32e7f1SMilanka Ringwald } 442be32e7f1SMilanka Ringwald 443be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timer_stop(avrcp_connection_t * connection){ 444be32e7f1SMilanka Ringwald btstack_run_loop_remove_timer(&connection->press_and_hold_cmd_timer); 445be32e7f1SMilanka Ringwald } 446be32e7f1SMilanka Ringwald 447be32e7f1SMilanka Ringwald static void request_pass_through_release_control_cmd(avrcp_connection_t * connection){ 448be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_RELEASE_COMMAND; 449be32e7f1SMilanka Ringwald switch (connection->cmd_operands[0]){ 450be32e7f1SMilanka Ringwald case AVRCP_OPERATION_ID_REWIND: 451be32e7f1SMilanka Ringwald case AVRCP_OPERATION_ID_FAST_FORWARD: 452be32e7f1SMilanka Ringwald avrcp_press_and_hold_timer_stop(connection); 453be32e7f1SMilanka Ringwald break; 454be32e7f1SMilanka Ringwald default: 455be32e7f1SMilanka Ringwald break; 456be32e7f1SMilanka Ringwald } 457be32e7f1SMilanka Ringwald connection->cmd_operands[0] = 0x80 | connection->cmd_operands[0]; 458be32e7f1SMilanka Ringwald connection->transaction_label++; 459be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 460be32e7f1SMilanka Ringwald } 461be32e7f1SMilanka Ringwald 462be32e7f1SMilanka Ringwald static void request_pass_through_press_control_cmd(uint16_t con_handle, avrcp_operation_id_t opid, uint16_t playback_speed){ 463be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 464be32e7f1SMilanka Ringwald if (!connection){ 465be32e7f1SMilanka Ringwald log_error("avrcp: could not find a connection."); 466be32e7f1SMilanka Ringwald return; 467be32e7f1SMilanka Ringwald } 468be32e7f1SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return; 469be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_PRESS_COMMAND; 470be32e7f1SMilanka Ringwald connection->cmd_to_send = AVRCP_CMD_OPCODE_PASS_THROUGH; 471be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_CONTROL; 472be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 473be32e7f1SMilanka Ringwald connection->subunit_id = 0; 474be32e7f1SMilanka Ringwald connection->cmd_operands_lenght = 0; 475be32e7f1SMilanka Ringwald 476be32e7f1SMilanka Ringwald connection->cmd_operands_lenght = 2; 477be32e7f1SMilanka Ringwald connection->cmd_operands[0] = opid; 478be32e7f1SMilanka Ringwald if (playback_speed > 0){ 479be32e7f1SMilanka Ringwald connection->cmd_operands[2] = playback_speed; 480be32e7f1SMilanka Ringwald connection->cmd_operands_lenght++; 481be32e7f1SMilanka Ringwald } 482be32e7f1SMilanka Ringwald connection->cmd_operands[1] = connection->cmd_operands_lenght - 2; 483be32e7f1SMilanka Ringwald 484be32e7f1SMilanka Ringwald switch (connection->cmd_operands[0]){ 485be32e7f1SMilanka Ringwald case AVRCP_OPERATION_ID_REWIND: 486be32e7f1SMilanka Ringwald case AVRCP_OPERATION_ID_FAST_FORWARD: 487be32e7f1SMilanka Ringwald avrcp_press_and_hold_timer_start(connection); 488be32e7f1SMilanka Ringwald break; 489be32e7f1SMilanka Ringwald default: 490be32e7f1SMilanka Ringwald break; 491be32e7f1SMilanka Ringwald } 492be32e7f1SMilanka Ringwald connection->transaction_label++; 493be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 494be32e7f1SMilanka Ringwald } 495be32e7f1SMilanka Ringwald 496be32e7f1SMilanka Ringwald 497be32e7f1SMilanka Ringwald static int avrcp_send_cmd(uint16_t cid, avrcp_connection_t * connection){ 498be32e7f1SMilanka Ringwald uint8_t command[20]; 499be32e7f1SMilanka Ringwald int pos = 0; 500be32e7f1SMilanka Ringwald // transport header 501be32e7f1SMilanka Ringwald // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 502be32e7f1SMilanka Ringwald command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0; 503be32e7f1SMilanka Ringwald // Profile IDentifier (PID) 504be32e7f1SMilanka Ringwald command[pos++] = AV_REMOTE_CONTROL >> 8; 505be32e7f1SMilanka Ringwald command[pos++] = AV_REMOTE_CONTROL & 0x00FF; 506be32e7f1SMilanka Ringwald 507be32e7f1SMilanka Ringwald // command_type 508be32e7f1SMilanka Ringwald command[pos++] = connection->command_type; 509be32e7f1SMilanka Ringwald // subunit_type | subunit ID 510be32e7f1SMilanka Ringwald command[pos++] = (connection->subunit_type << 3) | connection->subunit_id; 511be32e7f1SMilanka Ringwald // opcode 512be32e7f1SMilanka Ringwald command[pos++] = (uint8_t)connection->cmd_to_send; 513be32e7f1SMilanka Ringwald // operands 514be32e7f1SMilanka Ringwald memcpy(command+pos, connection->cmd_operands, connection->cmd_operands_lenght); 515be32e7f1SMilanka Ringwald pos += connection->cmd_operands_lenght; 516be32e7f1SMilanka Ringwald 517be32e7f1SMilanka Ringwald return l2cap_send(cid, command, pos); 518be32e7f1SMilanka Ringwald } 519be32e7f1SMilanka Ringwald 520be32e7f1SMilanka Ringwald static int avrcp_register_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){ 521be32e7f1SMilanka Ringwald if (connection->notifications_to_deregister & (1 << event_id)) return 0; 522be32e7f1SMilanka Ringwald if (connection->notifications_enabled & (1 << event_id)) return 0; 523be32e7f1SMilanka Ringwald if (connection->notifications_to_register & (1 << event_id)) return 0; 524be32e7f1SMilanka Ringwald connection->notifications_to_register |= (1 << event_id); 525be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 526be32e7f1SMilanka Ringwald return 1; 527be32e7f1SMilanka Ringwald } 528be32e7f1SMilanka Ringwald 529be32e7f1SMilanka Ringwald static void avrcp_prepare_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){ 530be32e7f1SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return; 531be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 532be32e7f1SMilanka Ringwald 533be32e7f1SMilanka Ringwald connection->transaction_label++; 534be32e7f1SMilanka Ringwald connection->cmd_to_send = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 535be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_NOTIFY; 536be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 537be32e7f1SMilanka Ringwald connection->subunit_id = 0; 538be32e7f1SMilanka Ringwald int pos = 0; 539be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 540be32e7f1SMilanka Ringwald pos += 3; 541be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_REGISTER_NOTIFICATION; 542be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; // reserved(upper 6) | packet_type -> 0 543be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 5); // parameter length 544be32e7f1SMilanka Ringwald pos += 2; 545be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = event_id; 546be32e7f1SMilanka Ringwald big_endian_store_32(connection->cmd_operands, pos, 0); 547be32e7f1SMilanka Ringwald pos += 4; 548be32e7f1SMilanka Ringwald connection->cmd_operands_lenght = pos; 549be32e7f1SMilanka Ringwald // AVRCP_SPEC_V14.pdf 166 550be32e7f1SMilanka Ringwald // answer page 61 551be32e7f1SMilanka Ringwald } 552be32e7f1SMilanka Ringwald 553be32e7f1SMilanka Ringwald static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){ 554be32e7f1SMilanka Ringwald switch (connection->state){ 555be32e7f1SMilanka Ringwald case AVCTP_W2_RECEIVE_PRESS_RESPONSE: 556be32e7f1SMilanka Ringwald switch (connection->cmd_operands[0]){ 557be32e7f1SMilanka Ringwald case AVRCP_OPERATION_ID_REWIND: 558be32e7f1SMilanka Ringwald case AVRCP_OPERATION_ID_FAST_FORWARD: 559be32e7f1SMilanka Ringwald connection->state = AVCTP_W4_STOP; 560be32e7f1SMilanka Ringwald break; 561be32e7f1SMilanka Ringwald default: 562be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_RELEASE_COMMAND; 563be32e7f1SMilanka Ringwald break; 564be32e7f1SMilanka Ringwald } 565be32e7f1SMilanka Ringwald break; 566be32e7f1SMilanka Ringwald case AVCTP_W2_RECEIVE_RESPONSE: 567be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 568be32e7f1SMilanka Ringwald break; 569be32e7f1SMilanka Ringwald default: 570be32e7f1SMilanka Ringwald // check for notifications? move state transition down 571be32e7f1SMilanka Ringwald break; 572be32e7f1SMilanka Ringwald } 573be32e7f1SMilanka Ringwald 574be32e7f1SMilanka Ringwald avrcp_command_type_t ctype; 575be32e7f1SMilanka Ringwald avrcp_subunit_type_t subunit_type; 576be32e7f1SMilanka Ringwald avrcp_subunit_type_t subunit_id; 577be32e7f1SMilanka Ringwald 578be32e7f1SMilanka Ringwald uint8_t operands[20]; 579be32e7f1SMilanka Ringwald uint8_t opcode; 580be32e7f1SMilanka Ringwald int pos = 0; 581be32e7f1SMilanka Ringwald // uint8_t transport_header = packet[0]; 582be32e7f1SMilanka Ringwald // uint8_t transaction_label = transport_header >> 4; 583be32e7f1SMilanka Ringwald // uint8_t packet_type = (transport_header & 0x0F) >> 2; 584be32e7f1SMilanka Ringwald // uint8_t frame_type = (transport_header & 0x03) >> 1; 585be32e7f1SMilanka Ringwald // uint8_t ipid = transport_header & 0x01; 586be32e7f1SMilanka Ringwald uint8_t byte_value = packet[2]; 587be32e7f1SMilanka Ringwald // uint16_t pid = (byte_value << 8) | packet[2]; 588be32e7f1SMilanka Ringwald pos = 3; 589be32e7f1SMilanka Ringwald 590be32e7f1SMilanka Ringwald // printf(" Transport header 0x%02x (transaction_label %d, packet_type %d, frame_type %d, ipid %d), pid 0x%4x\n", 591be32e7f1SMilanka Ringwald // transport_header, transaction_label, packet_type, frame_type, ipid, pid); 592be32e7f1SMilanka Ringwald // // printf_hexdump(packet+pos, size-pos); 593be32e7f1SMilanka Ringwald 594be32e7f1SMilanka Ringwald switch (connection->cmd_to_send){ 595be32e7f1SMilanka Ringwald case AVRCP_CMD_OPCODE_UNIT_INFO:{ 596be32e7f1SMilanka Ringwald ctype = packet[pos++]; 597be32e7f1SMilanka Ringwald byte_value = packet[pos++]; 598be32e7f1SMilanka Ringwald subunit_type = byte_value >> 3; 599be32e7f1SMilanka Ringwald subunit_id = byte_value & 0x07; 600be32e7f1SMilanka Ringwald opcode = packet[pos++]; 601be32e7f1SMilanka Ringwald 602be32e7f1SMilanka Ringwald // operands: 603be32e7f1SMilanka Ringwald memcpy(operands, packet+pos, 5); 604be32e7f1SMilanka Ringwald uint8_t unit_type = operands[1] >> 3; 605be32e7f1SMilanka Ringwald uint8_t unit = operands[1] & 0x07; 606be32e7f1SMilanka Ringwald uint32_t company_id = operands[2] << 16 | operands[3] << 8 | operands[4]; 607be32e7f1SMilanka 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", 608be32e7f1SMilanka Ringwald ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id ); 609be32e7f1SMilanka Ringwald break; 610be32e7f1SMilanka Ringwald } 611be32e7f1SMilanka Ringwald case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT: 612be32e7f1SMilanka Ringwald ctype = packet[pos++]; 613be32e7f1SMilanka Ringwald byte_value = packet[pos++]; 614be32e7f1SMilanka Ringwald subunit_type = byte_value >> 3; 615be32e7f1SMilanka Ringwald subunit_id = byte_value & 0x07; 616be32e7f1SMilanka Ringwald opcode = packet[pos++]; 617be32e7f1SMilanka Ringwald 618be32e7f1SMilanka Ringwald if (size - pos < 7) { 619be32e7f1SMilanka Ringwald log_error("avrcp: wrong packet size"); 620be32e7f1SMilanka Ringwald return; 621be32e7f1SMilanka Ringwald }; 622be32e7f1SMilanka Ringwald // operands: 623be32e7f1SMilanka Ringwald memcpy(operands, packet+pos, 7); 624be32e7f1SMilanka Ringwald pos += 7; 625be32e7f1SMilanka Ringwald // uint32_t company_id = operands[0] << 16 | operands[1] << 8 | operands[2]; 626be32e7f1SMilanka Ringwald uint8_t pdu_id = operands[3]; 627be32e7f1SMilanka Ringwald // uint8_t unit_type = operands[4] >> 3; 628be32e7f1SMilanka Ringwald // uint8_t unit = operands[4] & 0x07; 629be32e7f1SMilanka Ringwald uint16_t param_length = big_endian_read_16(operands, 5); 630be32e7f1SMilanka Ringwald 631be32e7f1SMilanka 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", 632be32e7f1SMilanka Ringwald // ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id ); 633be32e7f1SMilanka Ringwald 634be32e7f1SMilanka Ringwald // if (ctype == AVRCP_CTYPE_RESPONSE_INTERIM) return; 635be32e7f1SMilanka Ringwald log_info(" VENDOR DEPENDENT response: pdu id 0x%02x, param_length %d, status %s", pdu_id, param_length, avrcp_ctype2str(ctype)); 636be32e7f1SMilanka Ringwald switch (pdu_id){ 637be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue:{ 638be32e7f1SMilanka Ringwald uint8_t num_attributes = packet[pos++]; 639be32e7f1SMilanka Ringwald int i; 640be32e7f1SMilanka Ringwald uint8_t repeat_mode = 0; 641be32e7f1SMilanka Ringwald uint8_t shuffle_mode = 0; 642be32e7f1SMilanka Ringwald for (i = 0; i < num_attributes; i++){ 643be32e7f1SMilanka Ringwald uint8_t attribute_id = packet[pos++]; 644be32e7f1SMilanka Ringwald uint8_t attribute_value = packet[pos++]; 645be32e7f1SMilanka Ringwald switch (attribute_id){ 646be32e7f1SMilanka Ringwald case 0x02: 647be32e7f1SMilanka Ringwald repeat_mode = attribute_value; 648be32e7f1SMilanka Ringwald break; 649be32e7f1SMilanka Ringwald case 0x03: 650be32e7f1SMilanka Ringwald shuffle_mode = attribute_value; 651be32e7f1SMilanka Ringwald break; 652be32e7f1SMilanka Ringwald default: 653be32e7f1SMilanka Ringwald break; 654be32e7f1SMilanka Ringwald } 655be32e7f1SMilanka Ringwald } 656be32e7f1SMilanka Ringwald avrcp_emit_repeat_and_shuffle_mode(avrcp_callback, connection->con_handle, ctype, repeat_mode, shuffle_mode); 657be32e7f1SMilanka Ringwald break; 658be32e7f1SMilanka Ringwald } 659be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_SetPlayerApplicationSettingValue:{ 660be32e7f1SMilanka Ringwald uint8_t event[6]; 661be32e7f1SMilanka Ringwald int offset = 0; 662be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 663be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 664be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE; 665be32e7f1SMilanka Ringwald little_endian_store_16(event, offset, connection->con_handle); 666be32e7f1SMilanka Ringwald offset += 2; 667be32e7f1SMilanka Ringwald event[offset++] = ctype; 668be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 669be32e7f1SMilanka Ringwald break; 670be32e7f1SMilanka Ringwald } 671be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME:{ 672be32e7f1SMilanka Ringwald uint8_t event[7]; 673be32e7f1SMilanka Ringwald int offset = 0; 674be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 675be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 676be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_SET_ABSOLUTE_VOLUME_RESPONSE; 677be32e7f1SMilanka Ringwald little_endian_store_16(event, offset, connection->con_handle); 678be32e7f1SMilanka Ringwald offset += 2; 679be32e7f1SMilanka Ringwald event[offset++] = ctype; 680be32e7f1SMilanka Ringwald event[offset++] = packet[pos++]; 681be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 682be32e7f1SMilanka Ringwald break; 683be32e7f1SMilanka Ringwald } 684be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GET_CAPABILITIES:{ 685be32e7f1SMilanka Ringwald avrcp_capability_id_t capability_id = packet[pos++]; 686be32e7f1SMilanka Ringwald uint8_t capability_count = packet[pos++]; 687be32e7f1SMilanka Ringwald int i; 688be32e7f1SMilanka Ringwald switch (capability_id){ 689be32e7f1SMilanka Ringwald case AVRCP_CAPABILITY_ID_COMPANY: 690be32e7f1SMilanka Ringwald // log_info("Supported companies %d: ", capability_count); 691be32e7f1SMilanka Ringwald for (i = 0; i < capability_count; i++){ 692be32e7f1SMilanka Ringwald uint32_t company_id = big_endian_read_24(packet, pos); 693be32e7f1SMilanka Ringwald pos += 3; 694be32e7f1SMilanka Ringwald log_info(" 0x%06x, ", company_id); 695be32e7f1SMilanka Ringwald } 696be32e7f1SMilanka Ringwald break; 697be32e7f1SMilanka Ringwald case AVRCP_CAPABILITY_ID_EVENT: 698be32e7f1SMilanka Ringwald // log_info("Supported events %d: ", capability_count); 699be32e7f1SMilanka Ringwald for (i = 0; i < capability_count; i++){ 700be32e7f1SMilanka Ringwald uint8_t event_id = packet[pos++]; 701be32e7f1SMilanka Ringwald log_info(" 0x%02x %s", event_id, avrcp_event2str(event_id)); 702be32e7f1SMilanka Ringwald } 703be32e7f1SMilanka Ringwald break; 704be32e7f1SMilanka Ringwald } 705be32e7f1SMilanka Ringwald break; 706be32e7f1SMilanka Ringwald } 707be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GET_PLAY_STATUS:{ 708be32e7f1SMilanka Ringwald uint32_t song_length = big_endian_read_32(packet, pos); 709be32e7f1SMilanka Ringwald pos += 4; 710be32e7f1SMilanka Ringwald uint32_t song_position = big_endian_read_32(packet, pos); 711be32e7f1SMilanka Ringwald pos += 4; 712be32e7f1SMilanka Ringwald uint8_t play_status = packet[pos]; 713be32e7f1SMilanka Ringwald // log_info(" GET_PLAY_STATUS length 0x%04X, position 0x%04X, status %s", song_length, song_position, avrcp_play_status2str(play_status)); 714be32e7f1SMilanka Ringwald 715be32e7f1SMilanka Ringwald uint8_t event[15]; 716be32e7f1SMilanka Ringwald int offset = 0; 717be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 718be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 719be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_PLAY_STATUS; 720be32e7f1SMilanka Ringwald little_endian_store_16(event, offset, connection->con_handle); 721be32e7f1SMilanka Ringwald offset += 2; 722be32e7f1SMilanka Ringwald event[offset++] = ctype; 723be32e7f1SMilanka Ringwald little_endian_store_32(event, offset, song_length); 724be32e7f1SMilanka Ringwald offset += 4; 725be32e7f1SMilanka Ringwald little_endian_store_32(event, offset, song_position); 726be32e7f1SMilanka Ringwald offset += 4; 727be32e7f1SMilanka Ringwald event[offset++] = play_status; 728be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 729be32e7f1SMilanka Ringwald break; 730be32e7f1SMilanka Ringwald } 731be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_REGISTER_NOTIFICATION:{ 732be32e7f1SMilanka Ringwald uint8_t event_id = packet[pos++]; 733be32e7f1SMilanka Ringwald uint16_t event_mask = (1 << event_id); 734be32e7f1SMilanka Ringwald uint16_t reset_event_mask = ~event_mask; 735be32e7f1SMilanka Ringwald switch (ctype){ 736be32e7f1SMilanka Ringwald case AVRCP_CTYPE_RESPONSE_INTERIM: 737be32e7f1SMilanka Ringwald // register as enabled 738be32e7f1SMilanka Ringwald connection->notifications_enabled |= event_mask; 739be32e7f1SMilanka Ringwald // clear registration bit 740be32e7f1SMilanka Ringwald connection->notifications_to_register &= reset_event_mask; 741be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 742be32e7f1SMilanka Ringwald // printf("INTERIM notifications_enabled 0x%2x, notifications_to_register 0x%2x\n", connection->notifications_enabled, connection->notifications_to_register); 743be32e7f1SMilanka Ringwald break; 744be32e7f1SMilanka Ringwald case AVRCP_CTYPE_RESPONSE_CHANGED_STABLE: 745be32e7f1SMilanka Ringwald // received change, event is considered deregistered 746be32e7f1SMilanka Ringwald // we are re-enabling it automatically, if it is not 747be32e7f1SMilanka Ringwald // explicitly disabled 748be32e7f1SMilanka Ringwald connection->notifications_enabled &= reset_event_mask; 749be32e7f1SMilanka Ringwald if (! (connection->notifications_to_deregister & event_mask)){ 750be32e7f1SMilanka Ringwald avrcp_register_notification(connection, event_id); 751be32e7f1SMilanka Ringwald // printf("CHANGED_STABLE notifications_enabled 0x%2x, notifications_to_register 0x%2x\n", connection->notifications_enabled, connection->notifications_to_register); 752be32e7f1SMilanka Ringwald } else { 753be32e7f1SMilanka Ringwald connection->notifications_to_deregister &= reset_event_mask; 754be32e7f1SMilanka Ringwald } 755be32e7f1SMilanka Ringwald break; 756be32e7f1SMilanka Ringwald default: 757be32e7f1SMilanka Ringwald connection->notifications_to_register &= reset_event_mask; 758be32e7f1SMilanka Ringwald connection->notifications_enabled &= reset_event_mask; 759be32e7f1SMilanka Ringwald connection->notifications_to_deregister &= reset_event_mask; 760be32e7f1SMilanka Ringwald break; 761be32e7f1SMilanka Ringwald } 762be32e7f1SMilanka Ringwald 763be32e7f1SMilanka Ringwald switch (event_id){ 764be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED:{ 765be32e7f1SMilanka Ringwald uint8_t event[7]; 766be32e7f1SMilanka Ringwald int offset = 0; 767be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 768be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 769be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED; 770be32e7f1SMilanka Ringwald little_endian_store_16(event, offset, connection->con_handle); 771be32e7f1SMilanka Ringwald offset += 2; 772be32e7f1SMilanka Ringwald event[offset++] = ctype; 773be32e7f1SMilanka Ringwald event[offset++] = packet[pos]; 774be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 775be32e7f1SMilanka Ringwald break; 776be32e7f1SMilanka Ringwald } 777be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED:{ 778be32e7f1SMilanka Ringwald uint8_t event[7]; 779be32e7f1SMilanka Ringwald int offset = 0; 780be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 781be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 782be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED; 783be32e7f1SMilanka Ringwald little_endian_store_16(event, offset, connection->con_handle); 784be32e7f1SMilanka Ringwald offset += 2; 785be32e7f1SMilanka Ringwald event[offset++] = ctype; 786be32e7f1SMilanka Ringwald event[offset++] = packet[pos]; 787be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 788be32e7f1SMilanka Ringwald break; 789be32e7f1SMilanka Ringwald } 790be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED:{ 791be32e7f1SMilanka Ringwald uint8_t event[6]; 792be32e7f1SMilanka Ringwald int offset = 0; 793be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 794be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 795be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED; 796be32e7f1SMilanka Ringwald little_endian_store_16(event, offset, connection->con_handle); 797be32e7f1SMilanka Ringwald offset += 2; 798be32e7f1SMilanka Ringwald event[offset++] = ctype; 799be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 800be32e7f1SMilanka Ringwald break; 801be32e7f1SMilanka Ringwald } 802be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_AVAILABLE_PLAYERS_CHANGED:{ 803be32e7f1SMilanka Ringwald uint8_t event[6]; 804be32e7f1SMilanka Ringwald int offset = 0; 805be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 806be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 807be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED; 808be32e7f1SMilanka Ringwald little_endian_store_16(event, offset, connection->con_handle); 809be32e7f1SMilanka Ringwald offset += 2; 810be32e7f1SMilanka Ringwald event[offset++] = ctype; 811be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 812be32e7f1SMilanka Ringwald break; 813be32e7f1SMilanka Ringwald } 814be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED:{ 815be32e7f1SMilanka Ringwald uint8_t event[7]; 816be32e7f1SMilanka Ringwald int offset = 0; 817be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 818be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 819be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED; 820be32e7f1SMilanka Ringwald little_endian_store_16(event, offset, connection->con_handle); 821be32e7f1SMilanka Ringwald offset += 2; 822be32e7f1SMilanka Ringwald event[offset++] = ctype; 823be32e7f1SMilanka Ringwald event[offset++] = packet[pos++] & 0x7F; 824be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 825be32e7f1SMilanka Ringwald break; 826be32e7f1SMilanka Ringwald } 827be32e7f1SMilanka Ringwald // case AVRCP_NOTIFICATION_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:{ 828be32e7f1SMilanka Ringwald // uint8_t num_PlayerApplicationSettingAttributes = packet[pos++]; 829be32e7f1SMilanka Ringwald // int i; 830be32e7f1SMilanka Ringwald // for (i = 0; i < num_PlayerApplicationSettingAttributes; i++){ 831be32e7f1SMilanka Ringwald // uint8_t PlayerApplicationSetting_AttributeID = packet[pos++]; 832be32e7f1SMilanka Ringwald // uint8_t PlayerApplicationSettingValueID = packet[pos++]; 833be32e7f1SMilanka Ringwald // } 834be32e7f1SMilanka Ringwald // break; 835be32e7f1SMilanka Ringwald // } 836be32e7f1SMilanka Ringwald // case AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED: 837be32e7f1SMilanka Ringwald // uint16_t player_id = big_endian_read_16(packet, pos); 838be32e7f1SMilanka Ringwald // pos += 2; 839be32e7f1SMilanka Ringwald // uint16_t uid_counter = big_endian_read_16(packet, pos); 840be32e7f1SMilanka Ringwald // pos += 2; 841be32e7f1SMilanka Ringwald // break; 842be32e7f1SMilanka Ringwald // case AVRCP_NOTIFICATION_EVENT_UIDS_CHANGED: 843be32e7f1SMilanka Ringwald // uint16_t uid_counter = big_endian_read_16(packet, pos); 844be32e7f1SMilanka Ringwald // pos += 2; 845be32e7f1SMilanka Ringwald // break; 846be32e7f1SMilanka Ringwald default: 847be32e7f1SMilanka Ringwald log_info("avrcp: not implemented"); 848be32e7f1SMilanka Ringwald break; 849be32e7f1SMilanka Ringwald } 850be32e7f1SMilanka Ringwald if (connection->notifications_to_register != 0){ 851be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 852be32e7f1SMilanka Ringwald } 853be32e7f1SMilanka Ringwald break; 854be32e7f1SMilanka Ringwald } 855be32e7f1SMilanka Ringwald 856be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES:{ 857be32e7f1SMilanka Ringwald uint8_t num_attributes = packet[pos++]; 858be32e7f1SMilanka Ringwald int i; 859be32e7f1SMilanka Ringwald struct item { 860be32e7f1SMilanka Ringwald uint16_t len; 861be32e7f1SMilanka Ringwald uint8_t * value; 862be32e7f1SMilanka Ringwald } items[AVRCP_MEDIA_ATTR_COUNT]; 863be32e7f1SMilanka Ringwald memset(items, 0, sizeof(items)); 864be32e7f1SMilanka Ringwald 865be32e7f1SMilanka Ringwald uint16_t string_attributes_len = 0; 866be32e7f1SMilanka Ringwald uint8_t num_string_attributes = 0; 867be32e7f1SMilanka Ringwald uint16_t total_event_payload_for_string_attributes = HCI_EVENT_PAYLOAD_SIZE-2; 868be32e7f1SMilanka Ringwald uint16_t max_string_attribute_value_len = 0; 869be32e7f1SMilanka Ringwald for (i = 0; i < num_attributes; i++){ 870be32e7f1SMilanka Ringwald avrcp_media_attribute_id_t attr_id = big_endian_read_32(packet, pos); 871be32e7f1SMilanka Ringwald pos += 4; 872be32e7f1SMilanka Ringwald // uint16_t character_set = big_endian_read_16(packet, pos); 873be32e7f1SMilanka Ringwald pos += 2; 874be32e7f1SMilanka Ringwald uint16_t attr_value_length = big_endian_read_16(packet, pos); 875be32e7f1SMilanka Ringwald pos += 2; 876be32e7f1SMilanka Ringwald 877be32e7f1SMilanka Ringwald // debug - to remove later 878be32e7f1SMilanka Ringwald uint8_t value[100]; 879be32e7f1SMilanka Ringwald uint16_t value_len = sizeof(value) <= attr_value_length? sizeof(value) - 1 : attr_value_length; 880be32e7f1SMilanka Ringwald memcpy(value, packet+pos, value_len); 881be32e7f1SMilanka Ringwald value[value_len] = 0; 882be32e7f1SMilanka Ringwald // printf("Now Playing Info %s: %s \n", attribute2str(attr_id), value); 883be32e7f1SMilanka Ringwald // end debug 884be32e7f1SMilanka Ringwald 885be32e7f1SMilanka Ringwald if ((attr_id >= 1) || (attr_id <= AVRCP_MEDIA_ATTR_COUNT)) { 886be32e7f1SMilanka Ringwald items[attr_id-1].len = attr_value_length; 887be32e7f1SMilanka Ringwald items[attr_id-1].value = &packet[pos]; 888be32e7f1SMilanka Ringwald switch (attr_id){ 889be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TITLE: 890be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ARTIST: 891be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ALBUM: 892be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_GENRE: 893be32e7f1SMilanka Ringwald num_string_attributes++; 894be32e7f1SMilanka Ringwald string_attributes_len += attr_value_length; 895be32e7f1SMilanka Ringwald if (max_string_attribute_value_len < attr_value_length){ 896be32e7f1SMilanka Ringwald max_string_attribute_value_len = attr_value_length; 897be32e7f1SMilanka Ringwald } 898be32e7f1SMilanka Ringwald break; 899be32e7f1SMilanka Ringwald default: 900be32e7f1SMilanka Ringwald break; 901be32e7f1SMilanka Ringwald } 902be32e7f1SMilanka Ringwald } 903be32e7f1SMilanka Ringwald pos += attr_value_length; 904be32e7f1SMilanka Ringwald } 905be32e7f1SMilanka Ringwald // subtract space for fixed fields 906be32e7f1SMilanka Ringwald total_event_payload_for_string_attributes -= 14 + 4; // 4 for '\0' 907be32e7f1SMilanka Ringwald 908be32e7f1SMilanka Ringwald // @TODO optimize space by repeatedly decreasing max_string_attribute_value_len until it fits into buffer instead of crude divion 909be32e7f1SMilanka 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; 910be32e7f1SMilanka 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); 911be32e7f1SMilanka Ringwald 912be32e7f1SMilanka Ringwald const uint8_t attribute_order[] = { 913be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_TRACK, 914be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_TOTAL_TRACKS, 915be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_SONG_LENGTH, 916be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_TITLE, 917be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_ARTIST, 918be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_ALBUM, 919be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_GENRE 920be32e7f1SMilanka Ringwald }; 921be32e7f1SMilanka Ringwald 922be32e7f1SMilanka Ringwald uint8_t event[HCI_EVENT_BUFFER_SIZE]; 923be32e7f1SMilanka Ringwald event[0] = HCI_EVENT_AVRCP_META; 924be32e7f1SMilanka Ringwald pos = 2; 925be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_NOW_PLAYING_INFO; 926be32e7f1SMilanka Ringwald little_endian_store_16(event, pos, connection->con_handle); 927be32e7f1SMilanka Ringwald pos += 2; 928be32e7f1SMilanka Ringwald event[pos++] = ctype; 929be32e7f1SMilanka Ringwald for (i = 0; i < sizeof(attribute_order); i++){ 930be32e7f1SMilanka Ringwald avrcp_media_attribute_id_t attr_id = attribute_order[i]; 931be32e7f1SMilanka Ringwald uint16_t value_len = 0; 932be32e7f1SMilanka Ringwald switch (attr_id){ 933be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TITLE: 934be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ARTIST: 935be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ALBUM: 936be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_GENRE: 937be32e7f1SMilanka Ringwald if (items[attr_id-1].value){ 938be32e7f1SMilanka Ringwald value_len = items[attr_id-1].len <= max_value_len ? items[attr_id-1].len : max_value_len; 939be32e7f1SMilanka Ringwald } 940be32e7f1SMilanka Ringwald event[pos++] = value_len + 1; 941be32e7f1SMilanka Ringwald if (value_len){ 942be32e7f1SMilanka Ringwald memcpy(event+pos, items[attr_id-1].value, value_len); 943be32e7f1SMilanka Ringwald pos += value_len; 944be32e7f1SMilanka Ringwald } 945be32e7f1SMilanka Ringwald event[pos++] = 0; 946be32e7f1SMilanka Ringwald break; 947be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_SONG_LENGTH: 948be32e7f1SMilanka Ringwald if (items[attr_id-1].value){ 949be32e7f1SMilanka Ringwald little_endian_store_32(event, pos, btstack_atoi((char *)items[attr_id-1].value)); 950be32e7f1SMilanka Ringwald } else { 951be32e7f1SMilanka Ringwald little_endian_store_32(event, pos, 0); 952be32e7f1SMilanka Ringwald } 953be32e7f1SMilanka Ringwald pos += 4; 954be32e7f1SMilanka Ringwald break; 955be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TRACK: 956be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TOTAL_TRACKS: 957be32e7f1SMilanka Ringwald if (items[attr_id-1].value){ 958be32e7f1SMilanka Ringwald event[pos++] = btstack_atoi((char *)items[attr_id-1].value); 959be32e7f1SMilanka Ringwald } else { 960be32e7f1SMilanka Ringwald event[pos++] = 0; 961be32e7f1SMilanka Ringwald } 962be32e7f1SMilanka Ringwald break; 963be32e7f1SMilanka Ringwald } 964be32e7f1SMilanka Ringwald } 965be32e7f1SMilanka Ringwald event[1] = pos - 2; 966be32e7f1SMilanka Ringwald // printf_hexdump(event, pos); 967be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, pos); 968be32e7f1SMilanka Ringwald break; 969be32e7f1SMilanka Ringwald } 970be32e7f1SMilanka Ringwald default: 971be32e7f1SMilanka Ringwald break; 972be32e7f1SMilanka Ringwald } 973be32e7f1SMilanka Ringwald break; 974be32e7f1SMilanka Ringwald case AVRCP_CMD_OPCODE_PASS_THROUGH:{ 975be32e7f1SMilanka Ringwald // 0x80 | connection->cmd_operands[0] 976be32e7f1SMilanka Ringwald ctype = packet[pos++]; 977be32e7f1SMilanka Ringwald byte_value = packet[pos++]; 978be32e7f1SMilanka Ringwald subunit_type = byte_value >> 3; 979be32e7f1SMilanka Ringwald subunit_id = byte_value & 0x07; 980be32e7f1SMilanka Ringwald opcode = packet[pos++]; 981be32e7f1SMilanka Ringwald uint8_t operation_id = packet[pos++]; 982be32e7f1SMilanka Ringwald 983be32e7f1SMilanka Ringwald if (connection->state == AVCTP_W4_STOP){ 984be32e7f1SMilanka Ringwald avrcp_emit_operation_status(avrcp_callback, AVRCP_SUBEVENT_OPERATION_START, connection->con_handle, ctype, operation_id); 985be32e7f1SMilanka Ringwald } 986be32e7f1SMilanka Ringwald if (connection->state == AVCTP_CONNECTION_OPENED) { 987be32e7f1SMilanka Ringwald // RELEASE response 988be32e7f1SMilanka Ringwald operation_id = operation_id & 0x7F; 989be32e7f1SMilanka Ringwald avrcp_emit_operation_status(avrcp_callback, AVRCP_SUBEVENT_OPERATION_COMPLETE, connection->con_handle, ctype, operation_id); 990be32e7f1SMilanka Ringwald } 991be32e7f1SMilanka Ringwald if (connection->state == AVCTP_W2_SEND_RELEASE_COMMAND){ 992be32e7f1SMilanka Ringwald // PRESS response 993be32e7f1SMilanka Ringwald request_pass_through_release_control_cmd(connection); 994be32e7f1SMilanka Ringwald } 995be32e7f1SMilanka Ringwald break; 996be32e7f1SMilanka Ringwald } 997be32e7f1SMilanka Ringwald default: 998be32e7f1SMilanka Ringwald break; 999be32e7f1SMilanka Ringwald } 1000be32e7f1SMilanka Ringwald } 1001be32e7f1SMilanka Ringwald 1002be32e7f1SMilanka Ringwald static void avrcp_handle_can_send_now(avrcp_connection_t * connection){ 1003be32e7f1SMilanka Ringwald int i; 1004be32e7f1SMilanka Ringwald switch (connection->state){ 1005be32e7f1SMilanka Ringwald case AVCTP_W2_SEND_PRESS_COMMAND: 1006be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_RECEIVE_PRESS_RESPONSE; 1007be32e7f1SMilanka Ringwald break; 1008be32e7f1SMilanka Ringwald case AVCTP_W2_SEND_COMMAND: 1009be32e7f1SMilanka Ringwald case AVCTP_W2_SEND_RELEASE_COMMAND: 1010be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_RECEIVE_RESPONSE; 1011be32e7f1SMilanka Ringwald break; 1012be32e7f1SMilanka Ringwald case AVCTP_CONNECTION_OPENED: 1013be32e7f1SMilanka Ringwald if (connection->disconnect){ 1014be32e7f1SMilanka Ringwald connection->wait_to_send = 0; 1015be32e7f1SMilanka Ringwald connection->disconnect = 0; 1016be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_W4_L2CAP_DISCONNECTED; 1017be32e7f1SMilanka Ringwald l2cap_disconnect(connection->l2cap_signaling_cid, 0); 1018be32e7f1SMilanka Ringwald return; 1019be32e7f1SMilanka Ringwald } 1020be32e7f1SMilanka Ringwald if (connection->notifications_to_register != 0){ 1021be32e7f1SMilanka Ringwald for (i = 1; i < 13; i++){ 1022be32e7f1SMilanka Ringwald if (connection->notifications_to_register & (1<<i)){ 1023be32e7f1SMilanka Ringwald avrcp_prepare_notification(connection, i); 1024be32e7f1SMilanka Ringwald avrcp_send_cmd(connection->l2cap_signaling_cid, connection); 1025be32e7f1SMilanka Ringwald return; 1026be32e7f1SMilanka Ringwald } 1027be32e7f1SMilanka Ringwald } 1028be32e7f1SMilanka Ringwald } 1029be32e7f1SMilanka Ringwald return; 1030be32e7f1SMilanka Ringwald default: 1031be32e7f1SMilanka Ringwald return; 1032be32e7f1SMilanka Ringwald } 1033be32e7f1SMilanka Ringwald avrcp_send_cmd(connection->l2cap_signaling_cid, connection); 1034be32e7f1SMilanka Ringwald } 1035be32e7f1SMilanka Ringwald 1036be32e7f1SMilanka Ringwald static avrcp_connection_t * avrcp_create_connection(bd_addr_t remote_addr){ 1037be32e7f1SMilanka Ringwald avrcp_connection_t * connection = btstack_memory_avrcp_connection_get(); 1038be32e7f1SMilanka Ringwald memset(connection, 0, sizeof(avrcp_connection_t)); 1039be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_IDLE; 1040be32e7f1SMilanka Ringwald connection->transaction_label = 0xFF; 1041be32e7f1SMilanka Ringwald memcpy(connection->remote_addr, remote_addr, 6); 1042be32e7f1SMilanka Ringwald btstack_linked_list_add(&avrcp_connections, (btstack_linked_item_t *) connection); 1043be32e7f1SMilanka Ringwald return connection; 1044be32e7f1SMilanka Ringwald } 1045be32e7f1SMilanka Ringwald 1046be32e7f1SMilanka Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 1047be32e7f1SMilanka Ringwald bd_addr_t event_addr; 1048be32e7f1SMilanka Ringwald hci_con_handle_t con_handle; 1049be32e7f1SMilanka Ringwald uint16_t psm; 1050be32e7f1SMilanka Ringwald uint16_t local_cid; 1051be32e7f1SMilanka Ringwald avrcp_connection_t * connection = NULL; 1052be32e7f1SMilanka Ringwald 1053be32e7f1SMilanka Ringwald switch (packet_type) { 1054be32e7f1SMilanka Ringwald case L2CAP_DATA_PACKET: 1055be32e7f1SMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel); 1056be32e7f1SMilanka Ringwald if (!connection) break; 1057be32e7f1SMilanka Ringwald avrcp_handle_l2cap_data_packet_for_signaling_connection(connection, packet, size); 1058be32e7f1SMilanka Ringwald break; 1059be32e7f1SMilanka Ringwald 1060be32e7f1SMilanka Ringwald case HCI_EVENT_PACKET: 1061be32e7f1SMilanka Ringwald switch (hci_event_packet_get_type(packet)) { 1062be32e7f1SMilanka Ringwald case L2CAP_EVENT_INCOMING_CONNECTION: 1063be32e7f1SMilanka Ringwald l2cap_event_incoming_connection_get_address(packet, event_addr); 1064be32e7f1SMilanka Ringwald local_cid = l2cap_event_incoming_connection_get_local_cid(packet); 1065be32e7f1SMilanka Ringwald 1066be32e7f1SMilanka Ringwald connection = get_avrcp_connection_for_bd_addr(event_addr); 1067be32e7f1SMilanka Ringwald if (!connection){ 1068be32e7f1SMilanka Ringwald connection = avrcp_create_connection(event_addr); 1069be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED; 1070be32e7f1SMilanka Ringwald l2cap_accept_connection(local_cid); 1071be32e7f1SMilanka Ringwald break; 1072be32e7f1SMilanka Ringwald } 1073be32e7f1SMilanka Ringwald break; 1074be32e7f1SMilanka Ringwald 1075be32e7f1SMilanka Ringwald case L2CAP_EVENT_CHANNEL_OPENED: 1076be32e7f1SMilanka Ringwald l2cap_event_channel_opened_get_address(packet, event_addr); 1077be32e7f1SMilanka Ringwald 1078be32e7f1SMilanka Ringwald if (l2cap_event_channel_opened_get_status(packet)){ 1079be32e7f1SMilanka Ringwald log_error("L2CAP connection to connection %s failed. status code 0x%02x", 1080be32e7f1SMilanka Ringwald bd_addr_to_str(event_addr), l2cap_event_channel_opened_get_status(packet)); 1081be32e7f1SMilanka Ringwald break; 1082be32e7f1SMilanka Ringwald } 1083be32e7f1SMilanka Ringwald psm = l2cap_event_channel_opened_get_psm(packet); 1084*235946f1SMatthias Ringwald if (psm != BLUETOOTH_PROTOCOL_AVCTP){ 1085be32e7f1SMilanka Ringwald log_error("unexpected PSM - Not implemented yet: L2CAP_EVENT_CHANNEL_OPENED"); 1086be32e7f1SMilanka Ringwald return; 1087be32e7f1SMilanka Ringwald } 1088be32e7f1SMilanka Ringwald 1089be32e7f1SMilanka Ringwald connection = get_avrcp_connection_for_bd_addr(event_addr); 1090be32e7f1SMilanka Ringwald if (!connection) break; 1091be32e7f1SMilanka Ringwald 1092be32e7f1SMilanka Ringwald con_handle = l2cap_event_channel_opened_get_handle(packet); 1093be32e7f1SMilanka Ringwald local_cid = l2cap_event_channel_opened_get_local_cid(packet); 1094be32e7f1SMilanka Ringwald // printf("L2CAP_EVENT_CHANNEL_OPENED: Channel successfully opened: %s, handle 0x%02x, psm 0x%02x, local cid 0x%02x, remote cid 0x%02x\n", 1095be32e7f1SMilanka Ringwald // bd_addr_to_str(event_addr), con_handle, psm, local_cid, l2cap_event_channel_opened_get_remote_cid(packet)); 1096be32e7f1SMilanka Ringwald if (connection->l2cap_signaling_cid == 0) { 1097be32e7f1SMilanka Ringwald connection->l2cap_signaling_cid = local_cid; 1098be32e7f1SMilanka Ringwald connection->con_handle = con_handle; 1099be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 1100be32e7f1SMilanka Ringwald avrcp_emit_connection_established(avrcp_callback, con_handle, 0, local_cid, event_addr); 1101be32e7f1SMilanka Ringwald break; 1102be32e7f1SMilanka Ringwald } 1103be32e7f1SMilanka Ringwald break; 1104be32e7f1SMilanka Ringwald 1105be32e7f1SMilanka Ringwald case L2CAP_EVENT_CAN_SEND_NOW: 1106be32e7f1SMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel); 1107be32e7f1SMilanka Ringwald if (!connection) break; 1108be32e7f1SMilanka Ringwald avrcp_handle_can_send_now(connection); 1109be32e7f1SMilanka Ringwald break; 1110be32e7f1SMilanka Ringwald 1111be32e7f1SMilanka Ringwald case L2CAP_EVENT_CHANNEL_CLOSED: 1112be32e7f1SMilanka Ringwald // data: event (8), len(8), channel (16) 1113be32e7f1SMilanka Ringwald local_cid = l2cap_event_channel_closed_get_local_cid(packet); 1114be32e7f1SMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(local_cid); 1115be32e7f1SMilanka Ringwald if (connection){ 1116be32e7f1SMilanka Ringwald avrcp_emit_connection_closed(avrcp_callback, connection->con_handle); 1117be32e7f1SMilanka Ringwald btstack_linked_list_remove(&avrcp_connections, (btstack_linked_item_t*) connection); 1118be32e7f1SMilanka Ringwald break; 1119be32e7f1SMilanka Ringwald } 1120be32e7f1SMilanka Ringwald break; 1121be32e7f1SMilanka Ringwald default: 1122be32e7f1SMilanka Ringwald break; 1123be32e7f1SMilanka Ringwald } 1124be32e7f1SMilanka Ringwald break; 1125be32e7f1SMilanka Ringwald default: 1126be32e7f1SMilanka Ringwald break; 1127be32e7f1SMilanka Ringwald } 1128be32e7f1SMilanka Ringwald } 1129be32e7f1SMilanka Ringwald 1130be32e7f1SMilanka Ringwald void avrcp_init(void){ 1131be32e7f1SMilanka Ringwald avrcp_connections = NULL; 1132*235946f1SMatthias Ringwald l2cap_register_service(&packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_0); 1133be32e7f1SMilanka Ringwald } 1134be32e7f1SMilanka Ringwald 1135be32e7f1SMilanka Ringwald void avrcp_register_packet_handler(btstack_packet_handler_t callback){ 1136be32e7f1SMilanka Ringwald if (callback == NULL){ 1137be32e7f1SMilanka Ringwald log_error("avrcp_register_packet_handler called with NULL callback"); 1138be32e7f1SMilanka Ringwald return; 1139be32e7f1SMilanka Ringwald } 1140be32e7f1SMilanka Ringwald avrcp_callback = callback; 1141be32e7f1SMilanka Ringwald } 1142be32e7f1SMilanka Ringwald 1143be32e7f1SMilanka Ringwald void avrcp_connect(bd_addr_t bd_addr){ 1144be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_bd_addr(bd_addr); 1145be32e7f1SMilanka Ringwald if (!connection){ 1146be32e7f1SMilanka Ringwald connection = avrcp_create_connection(bd_addr); 1147be32e7f1SMilanka Ringwald } 1148be32e7f1SMilanka Ringwald if (!connection){ 1149be32e7f1SMilanka Ringwald log_error("avrcp: could not find or create a connection."); 1150be32e7f1SMilanka Ringwald return; 1151be32e7f1SMilanka Ringwald } 1152be32e7f1SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_IDLE) return; 1153be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED; 1154*235946f1SMatthias Ringwald l2cap_create_channel(packet_handler, connection->remote_addr, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, NULL); 1155be32e7f1SMilanka Ringwald } 1156be32e7f1SMilanka Ringwald 1157be32e7f1SMilanka Ringwald void avrcp_unit_info(uint16_t con_handle){ 1158be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 1159be32e7f1SMilanka Ringwald if (!connection){ 1160be32e7f1SMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 1161be32e7f1SMilanka Ringwald return; 1162be32e7f1SMilanka Ringwald } 1163be32e7f1SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return; 1164be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1165be32e7f1SMilanka Ringwald 1166be32e7f1SMilanka Ringwald connection->transaction_label++; 1167be32e7f1SMilanka Ringwald connection->cmd_to_send = AVRCP_CMD_OPCODE_UNIT_INFO; 1168be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1169be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique 1170be32e7f1SMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE; 1171be32e7f1SMilanka Ringwald memset(connection->cmd_operands, 0xFF, connection->cmd_operands_lenght); 1172be32e7f1SMilanka Ringwald connection->cmd_operands_lenght = 5; 1173be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 1174be32e7f1SMilanka Ringwald } 1175be32e7f1SMilanka Ringwald 1176be32e7f1SMilanka Ringwald static void avrcp_get_capabilities(uint16_t con_handle, uint8_t capability_id){ 1177be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 1178be32e7f1SMilanka Ringwald if (!connection){ 1179be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 1180be32e7f1SMilanka Ringwald return; 1181be32e7f1SMilanka Ringwald } 1182be32e7f1SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return; 1183be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1184be32e7f1SMilanka Ringwald 1185be32e7f1SMilanka Ringwald connection->transaction_label++; 1186be32e7f1SMilanka Ringwald connection->cmd_to_send = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1187be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1188be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1189be32e7f1SMilanka Ringwald connection->subunit_id = 0; 1190be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); 1191be32e7f1SMilanka Ringwald connection->cmd_operands[3] = AVRCP_PDU_ID_GET_CAPABILITIES; // PDU ID 1192be32e7f1SMilanka Ringwald connection->cmd_operands[4] = 0; 1193be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, 5, 1); // parameter length 1194be32e7f1SMilanka Ringwald connection->cmd_operands[7] = capability_id; // capability ID 1195be32e7f1SMilanka Ringwald connection->cmd_operands_lenght = 8; 1196be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 1197be32e7f1SMilanka Ringwald } 1198be32e7f1SMilanka Ringwald 1199be32e7f1SMilanka Ringwald void avrcp_get_supported_company_ids(uint16_t con_handle){ 1200be32e7f1SMilanka Ringwald avrcp_get_capabilities(con_handle, AVRCP_CAPABILITY_ID_COMPANY); 1201be32e7f1SMilanka Ringwald } 1202be32e7f1SMilanka Ringwald 1203be32e7f1SMilanka Ringwald void avrcp_get_supported_events(uint16_t con_handle){ 1204be32e7f1SMilanka Ringwald avrcp_get_capabilities(con_handle, AVRCP_CAPABILITY_ID_EVENT); 1205be32e7f1SMilanka Ringwald } 1206be32e7f1SMilanka Ringwald 1207be32e7f1SMilanka Ringwald 1208be32e7f1SMilanka Ringwald void avrcp_play(uint16_t con_handle){ 1209be32e7f1SMilanka Ringwald request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_PLAY, 0); 1210be32e7f1SMilanka Ringwald } 1211be32e7f1SMilanka Ringwald 1212be32e7f1SMilanka Ringwald void avrcp_stop(uint16_t con_handle){ 1213be32e7f1SMilanka Ringwald request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_STOP, 0); 1214be32e7f1SMilanka Ringwald } 1215be32e7f1SMilanka Ringwald 1216be32e7f1SMilanka Ringwald void avrcp_pause(uint16_t con_handle){ 1217be32e7f1SMilanka Ringwald request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_PAUSE, 0); 1218be32e7f1SMilanka Ringwald } 1219be32e7f1SMilanka Ringwald 1220be32e7f1SMilanka Ringwald void avrcp_forward(uint16_t con_handle){ 1221be32e7f1SMilanka Ringwald request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_FORWARD, 0); 1222be32e7f1SMilanka Ringwald } 1223be32e7f1SMilanka Ringwald 1224be32e7f1SMilanka Ringwald void avrcp_backward(uint16_t con_handle){ 1225be32e7f1SMilanka Ringwald request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_BACKWARD, 0); 1226be32e7f1SMilanka Ringwald } 1227be32e7f1SMilanka Ringwald 1228be32e7f1SMilanka Ringwald void avrcp_start_rewind(uint16_t con_handle){ 1229be32e7f1SMilanka Ringwald request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_REWIND, 0); 1230be32e7f1SMilanka Ringwald } 1231be32e7f1SMilanka Ringwald 1232be32e7f1SMilanka Ringwald void avrcp_volume_up(uint16_t con_handle){ 1233be32e7f1SMilanka Ringwald request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_VOLUME_UP, 0); 1234be32e7f1SMilanka Ringwald } 1235be32e7f1SMilanka Ringwald 1236be32e7f1SMilanka Ringwald void avrcp_volume_down(uint16_t con_handle){ 1237be32e7f1SMilanka Ringwald request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_VOLUME_DOWN, 0); 1238be32e7f1SMilanka Ringwald } 1239be32e7f1SMilanka Ringwald 1240be32e7f1SMilanka Ringwald void avrcp_mute(uint16_t con_handle){ 1241be32e7f1SMilanka Ringwald request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_MUTE, 0); 1242be32e7f1SMilanka Ringwald } 1243be32e7f1SMilanka Ringwald 1244be32e7f1SMilanka Ringwald void avrcp_skip(uint16_t con_handle){ 1245be32e7f1SMilanka Ringwald request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_SKIP, 0); 1246be32e7f1SMilanka Ringwald } 1247be32e7f1SMilanka Ringwald 1248be32e7f1SMilanka Ringwald void avrcp_stop_rewind(uint16_t con_handle){ 1249be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 1250be32e7f1SMilanka Ringwald if (!connection){ 1251be32e7f1SMilanka Ringwald log_error("avrcp_stop_rewind: could not find a connection."); 1252be32e7f1SMilanka Ringwald return; 1253be32e7f1SMilanka Ringwald } 1254be32e7f1SMilanka Ringwald if (connection->state != AVCTP_W4_STOP) return; 1255be32e7f1SMilanka Ringwald request_pass_through_release_control_cmd(connection); 1256be32e7f1SMilanka Ringwald } 1257be32e7f1SMilanka Ringwald 1258be32e7f1SMilanka Ringwald void avrcp_start_fast_forward(uint16_t con_handle){ 1259be32e7f1SMilanka Ringwald request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_FAST_FORWARD, 0); 1260be32e7f1SMilanka Ringwald } 1261be32e7f1SMilanka Ringwald 1262be32e7f1SMilanka Ringwald void avrcp_stop_fast_forward(uint16_t con_handle){ 1263be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 1264be32e7f1SMilanka Ringwald if (!connection){ 1265be32e7f1SMilanka Ringwald log_error("avrcp_stop_fast_forward: could not find a connection."); 1266be32e7f1SMilanka Ringwald return; 1267be32e7f1SMilanka Ringwald } 1268be32e7f1SMilanka Ringwald if (connection->state != AVCTP_W4_STOP) return; 1269be32e7f1SMilanka Ringwald request_pass_through_release_control_cmd(connection); 1270be32e7f1SMilanka Ringwald } 1271be32e7f1SMilanka Ringwald 1272be32e7f1SMilanka Ringwald void avrcp_get_play_status(uint16_t con_handle){ 1273be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 1274be32e7f1SMilanka Ringwald if (!connection){ 1275be32e7f1SMilanka Ringwald log_error("avrcp_get_play_status: could not find a connection."); 1276be32e7f1SMilanka Ringwald return; 1277be32e7f1SMilanka Ringwald } 1278be32e7f1SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return; 1279be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1280be32e7f1SMilanka Ringwald 1281be32e7f1SMilanka Ringwald connection->transaction_label++; 1282be32e7f1SMilanka Ringwald connection->cmd_to_send = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1283be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1284be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1285be32e7f1SMilanka Ringwald connection->subunit_id = 0; 1286be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); 1287be32e7f1SMilanka Ringwald connection->cmd_operands[3] = AVRCP_PDU_ID_GET_PLAY_STATUS; 1288be32e7f1SMilanka Ringwald connection->cmd_operands[4] = 0; // reserved(upper 6) | packet_type -> 0 1289be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, 5, 0); // parameter length 1290be32e7f1SMilanka Ringwald connection->cmd_operands_lenght = 7; 1291be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 1292be32e7f1SMilanka Ringwald } 1293be32e7f1SMilanka Ringwald 1294be32e7f1SMilanka Ringwald void avrcp_enable_notification(uint16_t con_handle, avrcp_notification_event_id_t event_id){ 1295be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 1296be32e7f1SMilanka Ringwald if (!connection){ 1297be32e7f1SMilanka Ringwald log_error("avrcp_get_play_status: could not find a connection."); 1298be32e7f1SMilanka Ringwald return; 1299be32e7f1SMilanka Ringwald } 1300be32e7f1SMilanka Ringwald avrcp_register_notification(connection, event_id); 1301be32e7f1SMilanka Ringwald } 1302be32e7f1SMilanka Ringwald 1303be32e7f1SMilanka Ringwald void avrcp_disable_notification(uint16_t con_handle, avrcp_notification_event_id_t event_id){ 1304be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 1305be32e7f1SMilanka Ringwald if (!connection){ 1306be32e7f1SMilanka Ringwald log_error("avrcp_get_play_status: could not find a connection."); 1307be32e7f1SMilanka Ringwald return; 1308be32e7f1SMilanka Ringwald } 1309be32e7f1SMilanka Ringwald connection->notifications_to_deregister |= (1 << event_id); 1310be32e7f1SMilanka Ringwald } 1311be32e7f1SMilanka Ringwald 1312be32e7f1SMilanka Ringwald void avrcp_get_now_playing_info(uint16_t con_handle){ 1313be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 1314be32e7f1SMilanka Ringwald if (!connection){ 1315be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 1316be32e7f1SMilanka Ringwald return; 1317be32e7f1SMilanka Ringwald } 1318be32e7f1SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return; 1319be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1320be32e7f1SMilanka Ringwald 1321be32e7f1SMilanka Ringwald connection->transaction_label++; 1322be32e7f1SMilanka Ringwald connection->cmd_to_send = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1323be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1324be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1325be32e7f1SMilanka Ringwald connection->subunit_id = 0; 1326be32e7f1SMilanka Ringwald int pos = 0; 1327be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 1328be32e7f1SMilanka Ringwald pos += 3; 1329be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES; // PDU ID 1330be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; 1331be32e7f1SMilanka Ringwald 1332be32e7f1SMilanka Ringwald // Parameter Length 1333be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 9); 1334be32e7f1SMilanka Ringwald pos += 2; 1335be32e7f1SMilanka Ringwald 1336be32e7f1SMilanka Ringwald // write 8 bytes value 1337be32e7f1SMilanka Ringwald memset(connection->cmd_operands + pos, 0, 8); // identifier: PLAYING 1338be32e7f1SMilanka Ringwald pos += 8; 1339be32e7f1SMilanka Ringwald 1340be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; // attribute count, if 0 get all attributes 1341be32e7f1SMilanka Ringwald // every attribute is 4 bytes long 1342be32e7f1SMilanka Ringwald 1343be32e7f1SMilanka Ringwald connection->cmd_operands_lenght = pos; 1344be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 1345be32e7f1SMilanka Ringwald 1346be32e7f1SMilanka Ringwald } 1347be32e7f1SMilanka Ringwald 1348be32e7f1SMilanka Ringwald void avrcp_set_absolute_volume(uint16_t con_handle, uint8_t volume){ 1349be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 1350be32e7f1SMilanka Ringwald if (!connection){ 1351be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 1352be32e7f1SMilanka Ringwald return; 1353be32e7f1SMilanka Ringwald } 1354be32e7f1SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return; 1355be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1356be32e7f1SMilanka Ringwald 1357be32e7f1SMilanka Ringwald connection->transaction_label++; 1358be32e7f1SMilanka Ringwald connection->cmd_to_send = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1359be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_CONTROL; 1360be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1361be32e7f1SMilanka Ringwald connection->subunit_id = 0; 1362be32e7f1SMilanka Ringwald int pos = 0; 1363be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 1364be32e7f1SMilanka Ringwald pos += 3; 1365be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME; // PDU ID 1366be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; 1367be32e7f1SMilanka Ringwald 1368be32e7f1SMilanka Ringwald // Parameter Length 1369be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 1); 1370be32e7f1SMilanka Ringwald pos += 2; 1371be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = volume; 1372be32e7f1SMilanka Ringwald 1373be32e7f1SMilanka Ringwald connection->cmd_operands_lenght = pos; 1374be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 1375be32e7f1SMilanka Ringwald } 1376be32e7f1SMilanka Ringwald 1377be32e7f1SMilanka Ringwald void avrcp_query_shuffle_and_repeat_modes(uint16_t con_handle){ 1378be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 1379be32e7f1SMilanka Ringwald if (!connection){ 1380be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 1381be32e7f1SMilanka Ringwald return; 1382be32e7f1SMilanka Ringwald } 1383be32e7f1SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return; 1384be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1385be32e7f1SMilanka Ringwald 1386be32e7f1SMilanka Ringwald connection->transaction_label++; 1387be32e7f1SMilanka Ringwald connection->cmd_to_send = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1388be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1389be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1390be32e7f1SMilanka Ringwald connection->subunit_id = 0; 1391be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); 1392be32e7f1SMilanka Ringwald connection->cmd_operands[3] = AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue; // PDU ID 1393be32e7f1SMilanka Ringwald connection->cmd_operands[4] = 0; 1394be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, 5, 5); // parameter length 1395be32e7f1SMilanka Ringwald connection->cmd_operands[7] = 4; // NumPlayerApplicationSettingAttributeID 1396be32e7f1SMilanka Ringwald // PlayerApplicationSettingAttributeID1 AVRCP Spec, Appendix F, 133 1397be32e7f1SMilanka Ringwald connection->cmd_operands[8] = 0x01; // equalizer (1-OFF, 2-ON) 1398be32e7f1SMilanka Ringwald connection->cmd_operands[9] = 0x02; // repeat (1-off, 2-single track, 3-all tracks, 4-group repeat) 1399be32e7f1SMilanka Ringwald connection->cmd_operands[10] = 0x03; // shuffle (1-off, 2-all tracks, 3-group shuffle) 1400be32e7f1SMilanka Ringwald connection->cmd_operands[11] = 0x04; // scan (1-off, 2-all tracks, 3-group scan) 1401be32e7f1SMilanka Ringwald connection->cmd_operands_lenght = 12; 1402be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 1403be32e7f1SMilanka Ringwald } 1404be32e7f1SMilanka Ringwald 1405be32e7f1SMilanka Ringwald 1406be32e7f1SMilanka Ringwald static void avrcp_set_current_player_application_setting_value(uint16_t con_handle, uint8_t attribute_id, uint8_t attribute_value){ 1407be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 1408be32e7f1SMilanka Ringwald if (!connection){ 1409be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 1410be32e7f1SMilanka Ringwald return; 1411be32e7f1SMilanka Ringwald } 1412be32e7f1SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return; 1413be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1414be32e7f1SMilanka Ringwald 1415be32e7f1SMilanka Ringwald connection->transaction_label++; 1416be32e7f1SMilanka Ringwald connection->cmd_to_send = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1417be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_CONTROL; 1418be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1419be32e7f1SMilanka Ringwald connection->subunit_id = 0; 1420be32e7f1SMilanka Ringwald int pos = 0; 1421be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 1422be32e7f1SMilanka Ringwald pos += 3; 1423be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_SetPlayerApplicationSettingValue; // PDU ID 1424be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; 1425be32e7f1SMilanka Ringwald // Parameter Length 1426be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 3); 1427be32e7f1SMilanka Ringwald pos += 2; 1428be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 2; 1429be32e7f1SMilanka Ringwald connection->cmd_operands_lenght = pos; 1430be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = attribute_id; 1431be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = attribute_value; 1432be32e7f1SMilanka Ringwald connection->cmd_operands_lenght = pos; 1433be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 1434be32e7f1SMilanka Ringwald } 1435be32e7f1SMilanka Ringwald 1436be32e7f1SMilanka Ringwald void avrcp_set_shuffle_mode(uint16_t con_handle, avrcp_shuffle_mode_t mode){ 1437be32e7f1SMilanka Ringwald if (mode < AVRCP_SHUFFLE_MODE_OFF || mode > AVRCP_SHUFFLE_MODE_GROUP) return; 1438be32e7f1SMilanka Ringwald avrcp_set_current_player_application_setting_value(con_handle, 0x03, mode); 1439be32e7f1SMilanka Ringwald } 1440be32e7f1SMilanka Ringwald 1441be32e7f1SMilanka Ringwald void avrcp_set_repeat_mode(uint16_t con_handle, avrcp_repeat_mode_t mode){ 1442be32e7f1SMilanka Ringwald if (mode < AVRCP_REPEAT_MODE_OFF || mode > AVRCP_REPEAT_MODE_GROUP) return; 1443be32e7f1SMilanka Ringwald avrcp_set_current_player_application_setting_value(con_handle, 0x02, mode); 1444be32e7f1SMilanka Ringwald } 1445be32e7f1SMilanka Ringwald 1446be32e7f1SMilanka Ringwald void avrcp_disconnect(uint16_t con_handle){ 1447be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); 1448be32e7f1SMilanka Ringwald if (!connection){ 1449be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 1450be32e7f1SMilanka Ringwald return; 1451be32e7f1SMilanka Ringwald } 1452be32e7f1SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return; 1453be32e7f1SMilanka Ringwald if (connection->state == AVCTP_CONNECTION_W4_L2CAP_DISCONNECTED) return; 1454be32e7f1SMilanka Ringwald 1455be32e7f1SMilanka Ringwald connection->disconnect = 1; 1456be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 1457be32e7f1SMilanka Ringwald }