1be32e7f1SMilanka Ringwald /* 2be32e7f1SMilanka Ringwald * Copyright (C) 2016 BlueKitchen GmbH 3be32e7f1SMilanka Ringwald * 4be32e7f1SMilanka Ringwald * Redistribution and use in source and binary forms, with or without 5be32e7f1SMilanka Ringwald * modification, are permitted provided that the following conditions 6be32e7f1SMilanka Ringwald * are met: 7be32e7f1SMilanka Ringwald * 8be32e7f1SMilanka Ringwald * 1. Redistributions of source code must retain the above copyright 9be32e7f1SMilanka Ringwald * notice, this list of conditions and the following disclaimer. 10be32e7f1SMilanka Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11be32e7f1SMilanka Ringwald * notice, this list of conditions and the following disclaimer in the 12be32e7f1SMilanka Ringwald * documentation and/or other materials provided with the distribution. 13be32e7f1SMilanka Ringwald * 3. Neither the name of the copyright holders nor the names of 14be32e7f1SMilanka Ringwald * contributors may be used to endorse or promote products derived 15be32e7f1SMilanka Ringwald * from this software without specific prior written permission. 16be32e7f1SMilanka Ringwald * 4. Any redistribution, use, or modification is done solely for 17be32e7f1SMilanka Ringwald * personal benefit and not for any commercial purpose or for 18be32e7f1SMilanka Ringwald * monetary gain. 19be32e7f1SMilanka Ringwald * 20be32e7f1SMilanka Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21be32e7f1SMilanka Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22be32e7f1SMilanka Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23be32e7f1SMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24be32e7f1SMilanka Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25be32e7f1SMilanka Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26be32e7f1SMilanka Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27be32e7f1SMilanka Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28be32e7f1SMilanka Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29be32e7f1SMilanka Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30be32e7f1SMilanka Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31be32e7f1SMilanka Ringwald * SUCH DAMAGE. 32be32e7f1SMilanka Ringwald * 33be32e7f1SMilanka Ringwald * Please inquire about commercial licensing options at 34be32e7f1SMilanka Ringwald * [email protected] 35be32e7f1SMilanka Ringwald * 36be32e7f1SMilanka Ringwald */ 37be32e7f1SMilanka Ringwald 38ab2c6ae4SMatthias Ringwald #define __BTSTACK_FILE__ "avrcp.c" 39ab2c6ae4SMatthias Ringwald 40be32e7f1SMilanka Ringwald #include <stdint.h> 41be32e7f1SMilanka Ringwald #include <stdio.h> 42be32e7f1SMilanka Ringwald #include <stdlib.h> 43be32e7f1SMilanka Ringwald #include <string.h> 44be32e7f1SMilanka Ringwald 45be32e7f1SMilanka Ringwald #include "btstack.h" 46be32e7f1SMilanka Ringwald #include "classic/avrcp.h" 47be32e7f1SMilanka Ringwald 48be32e7f1SMilanka Ringwald #define AV_REMOTE_CONTROL_TARGET 0x110C 49be32e7f1SMilanka Ringwald #define AV_REMOTE_CONTROL 0x110E 50be32e7f1SMilanka Ringwald #define AV_REMOTE_CONTROL_CONTROLLER 0x110F 51be32e7f1SMilanka Ringwald 52235946f1SMatthias Ringwald #define PSM_AVCTP BLUETOOTH_PROTOCOL_AVCTP 53be32e7f1SMilanka Ringwald #define PSM_AVCTP_BROWSING 0xFF17 54be32e7f1SMilanka Ringwald 55be32e7f1SMilanka Ringwald /* 56be32e7f1SMilanka Ringwald Category 1: Player/Recorder 57be32e7f1SMilanka Ringwald Category 2: Monitor/Amplifier 58be32e7f1SMilanka Ringwald Category 3: Tuner 59be32e7f1SMilanka Ringwald Category 4: Menu 60be32e7f1SMilanka Ringwald */ 61be32e7f1SMilanka Ringwald 62be32e7f1SMilanka Ringwald /* controller supported features 63be32e7f1SMilanka Ringwald Bit 0 = Category 1 64be32e7f1SMilanka Ringwald Bit 1 = Category 2 65be32e7f1SMilanka Ringwald Bit 2 = Category 3 66be32e7f1SMilanka Ringwald Bit 3 = Category 4 67be32e7f1SMilanka Ringwald Bit 4-5 = RFA 68be32e7f1SMilanka Ringwald Bit 6 = Supports browsing 69be32e7f1SMilanka Ringwald Bit 7-15 = RFA 70be32e7f1SMilanka Ringwald The bits for supported categories are set to 1. Others are set to 0. 71be32e7f1SMilanka Ringwald */ 72be32e7f1SMilanka Ringwald 73be32e7f1SMilanka Ringwald /* target supported features 74be32e7f1SMilanka Ringwald Bit 0 = Category 1 75be32e7f1SMilanka Ringwald Bit 1 = Category 2 76be32e7f1SMilanka Ringwald Bit 2 = Category 3 77be32e7f1SMilanka Ringwald Bit 3 = Category 4 78be32e7f1SMilanka Ringwald Bit 4 = Player Application Settings. Bit 0 should be set for this bit to be set. 79be32e7f1SMilanka Ringwald Bit 5 = Group Navigation. Bit 0 should be set for this bit to be set. 80be32e7f1SMilanka Ringwald Bit 6 = Supports browsing*4 81be32e7f1SMilanka Ringwald Bit 7 = Supports multiple media player applications 82be32e7f1SMilanka Ringwald Bit 8-15 = RFA 83be32e7f1SMilanka Ringwald The bits for supported categories are set to 1. Others are set to 0. 84be32e7f1SMilanka Ringwald */ 85be32e7f1SMilanka Ringwald 86be32e7f1SMilanka Ringwald // TODO: merge with avdtp_packet_type_t 87be32e7f1SMilanka Ringwald typedef enum { 88be32e7f1SMilanka Ringwald AVRCP_SINGLE_PACKET= 0, 89be32e7f1SMilanka Ringwald AVRCP_START_PACKET , 90be32e7f1SMilanka Ringwald AVRCP_CONTINUE_PACKET , 91be32e7f1SMilanka Ringwald AVRCP_END_PACKET 92be32e7f1SMilanka Ringwald } avrcp_packet_type_t; 93be32e7f1SMilanka Ringwald 94be32e7f1SMilanka Ringwald typedef enum { 95be32e7f1SMilanka Ringwald AVRCP_COMMAND_FRAME = 0, 96be32e7f1SMilanka Ringwald AVRCP_RESPONSE_FRAME 97be32e7f1SMilanka Ringwald } avrcp_frame_type_t; 98be32e7f1SMilanka Ringwald 99be32e7f1SMilanka Ringwald static const char * default_avrcp_controller_service_name = "BTstack AVRCP Controller Service"; 100be32e7f1SMilanka Ringwald static const char * default_avrcp_controller_service_provider_name = "BTstack AVRCP Controller Service Provider"; 101be32e7f1SMilanka Ringwald static const char * default_avrcp_target_service_name = "BTstack AVRCP Target Service"; 102be32e7f1SMilanka Ringwald static const char * default_avrcp_target_service_provider_name = "BTstack AVRCP Target Service Provider"; 103be32e7f1SMilanka Ringwald 104be32e7f1SMilanka Ringwald static btstack_linked_list_t avrcp_connections; 105be32e7f1SMilanka Ringwald static btstack_packet_handler_t avrcp_callback; 106be32e7f1SMilanka Ringwald 107be32e7f1SMilanka Ringwald static const char * avrcp_subunit_type_name[] = { 108be32e7f1SMilanka Ringwald "MONITOR", "AUDIO", "PRINTER", "DISC", "TAPE_RECORDER_PLAYER", "TUNER", 109be32e7f1SMilanka Ringwald "CA", "CAMERA", "RESERVED", "PANEL", "BULLETIN_BOARD", "CAMERA_STORAGE", 110be32e7f1SMilanka Ringwald "VENDOR_UNIQUE", "RESERVED_FOR_ALL_SUBUNIT_TYPES", 111be32e7f1SMilanka Ringwald "EXTENDED_TO_NEXT_BYTE", "UNIT", "ERROR" 112be32e7f1SMilanka Ringwald }; 113be32e7f1SMilanka Ringwald const char * avrcp_subunit2str(uint16_t index){ 114be32e7f1SMilanka Ringwald if (index <= 11) return avrcp_subunit_type_name[index]; 115be32e7f1SMilanka Ringwald if (index >= 0x1C && index <= 0x1F) return avrcp_subunit_type_name[index - 0x10]; 116be32e7f1SMilanka Ringwald return avrcp_subunit_type_name[16]; 117be32e7f1SMilanka Ringwald } 118be32e7f1SMilanka Ringwald 119be32e7f1SMilanka Ringwald static const char * avrcp_event_name[] = { 120be32e7f1SMilanka Ringwald "ERROR", "PLAYBACK_STATUS_CHANGED", 121be32e7f1SMilanka Ringwald "TRACK_CHANGED", "TRACK_REACHED_END", "TRACK_REACHED_START", 122be32e7f1SMilanka Ringwald "PLAYBACK_POS_CHANGED", "BATT_STATUS_CHANGED", "SYSTEM_STATUS_CHANGED", 123be32e7f1SMilanka Ringwald "PLAYER_APPLICATION_SETTING_CHANGED", "NOW_PLAYING_CONTENT_CHANGED", 124be32e7f1SMilanka Ringwald "AVAILABLE_PLAYERS_CHANGED", "ADDRESSED_PLAYER_CHANGED", "UIDS_CHANGED", "VOLUME_CHANGED" 125be32e7f1SMilanka Ringwald }; 126be32e7f1SMilanka Ringwald const char * avrcp_event2str(uint16_t index){ 127be32e7f1SMilanka Ringwald if (index <= 0x0d) return avrcp_event_name[index]; 128be32e7f1SMilanka Ringwald return avrcp_event_name[0]; 129be32e7f1SMilanka Ringwald } 130be32e7f1SMilanka Ringwald 131be32e7f1SMilanka Ringwald static const char * avrcp_operation_name[] = { 132be32e7f1SMilanka Ringwald "NOT SUPPORTED", // 0x3B 133be32e7f1SMilanka Ringwald "SKIP", "NOT SUPPORTED", "NOT SUPPORTED", "NOT SUPPORTED", "NOT SUPPORTED", 134be32e7f1SMilanka Ringwald "VOLUME_UP", "VOLUME_DOWN", "MUTE", "PLAY", "STOP", "PAUSE", "NOT SUPPORTED", 135be32e7f1SMilanka Ringwald "REWIND", "FAST_FORWARD", "NOT SUPPORTED", "FORWARD", "BACKWARD" // 0x4C 136be32e7f1SMilanka Ringwald }; 137be32e7f1SMilanka Ringwald const char * avrcp_operation2str(uint8_t index){ 138be32e7f1SMilanka Ringwald if (index >= 0x3B && index <= 0x4C) return avrcp_operation_name[index - 0x3B]; 139be32e7f1SMilanka Ringwald return avrcp_operation_name[0]; 140be32e7f1SMilanka Ringwald } 141be32e7f1SMilanka Ringwald 142be32e7f1SMilanka Ringwald static const char * avrcp_media_attribute_id_name[] = { 143be32e7f1SMilanka Ringwald "NONE", "TITLE", "ARTIST", "ALBUM", "TRACK", "TOTAL TRACKS", "GENRE", "SONG LENGTH" 144be32e7f1SMilanka Ringwald }; 145be32e7f1SMilanka Ringwald const char * avrcp_attribute2str(uint8_t index){ 146be32e7f1SMilanka Ringwald if (index >= 1 && index <= 7) return avrcp_media_attribute_id_name[index]; 147be32e7f1SMilanka Ringwald return avrcp_media_attribute_id_name[0]; 148be32e7f1SMilanka Ringwald } 149be32e7f1SMilanka Ringwald 150be32e7f1SMilanka Ringwald static const char * avrcp_play_status_name[] = { 151be32e7f1SMilanka Ringwald "STOPPED", "PLAYING", "PAUSED", "FORWARD SEEK", "REVERSE SEEK", 152be32e7f1SMilanka Ringwald "ERROR" // 0xFF 153be32e7f1SMilanka Ringwald }; 154be32e7f1SMilanka Ringwald const char * avrcp_play_status2str(uint8_t index){ 155be32e7f1SMilanka Ringwald if (index >= 1 && index <= 4) return avrcp_play_status_name[index]; 156be32e7f1SMilanka Ringwald return avrcp_play_status_name[5]; 157be32e7f1SMilanka Ringwald } 158be32e7f1SMilanka Ringwald 159be32e7f1SMilanka Ringwald static const char * avrcp_ctype_name[] = { 160be32e7f1SMilanka Ringwald "CONTROL", 161be32e7f1SMilanka Ringwald "STATUS", 162be32e7f1SMilanka Ringwald "SPECIFIC_INQUIRY", 163be32e7f1SMilanka Ringwald "NOTIFY", 164be32e7f1SMilanka Ringwald "GENERAL_INQUIRY", 165be32e7f1SMilanka Ringwald "RESERVED5", 166be32e7f1SMilanka Ringwald "RESERVED6", 167be32e7f1SMilanka Ringwald "RESERVED7", 1689cc1f3ceSMilanka Ringwald "NOT IMPLEMENTED IN REMOTE", 1699cc1f3ceSMilanka Ringwald "ACCEPTED BY REMOTE", 1709cc1f3ceSMilanka Ringwald "REJECTED BY REMOTE", 171be32e7f1SMilanka Ringwald "IN_TRANSITION", 172be32e7f1SMilanka Ringwald "IMPLEMENTED_STABLE", 173be32e7f1SMilanka Ringwald "CHANGED_STABLE", 174be32e7f1SMilanka Ringwald "RESERVED", 175be32e7f1SMilanka Ringwald "INTERIM" 176be32e7f1SMilanka Ringwald }; 177be32e7f1SMilanka Ringwald const char * avrcp_ctype2str(uint8_t index){ 1783982eab9SMatthias Ringwald if (index < sizeof(avrcp_ctype_name)){ 179be32e7f1SMilanka Ringwald return avrcp_ctype_name[index]; 180be32e7f1SMilanka Ringwald } 181be32e7f1SMilanka Ringwald return "NONE"; 182be32e7f1SMilanka Ringwald } 183be32e7f1SMilanka Ringwald 184be32e7f1SMilanka Ringwald static const char * avrcp_shuffle_mode_name[] = { 185be32e7f1SMilanka Ringwald "SHUFFLE OFF", 186be32e7f1SMilanka Ringwald "SHUFFLE ALL TRACKS", 187be32e7f1SMilanka Ringwald "SHUFFLE GROUP" 188be32e7f1SMilanka Ringwald }; 189be32e7f1SMilanka Ringwald 190be32e7f1SMilanka Ringwald const char * avrcp_shuffle2str(uint8_t index){ 191be32e7f1SMilanka Ringwald if (index >= 1 && index <= 3) return avrcp_shuffle_mode_name[index-1]; 192be32e7f1SMilanka Ringwald return "NONE"; 193be32e7f1SMilanka Ringwald } 194be32e7f1SMilanka Ringwald 195be32e7f1SMilanka Ringwald static const char * avrcp_repeat_mode_name[] = { 196be32e7f1SMilanka Ringwald "REPEAT OFF", 197be32e7f1SMilanka Ringwald "REPEAT SINGLE TRACK", 198be32e7f1SMilanka Ringwald "REPEAT ALL TRACKS", 199be32e7f1SMilanka Ringwald "REPEAT GROUP" 200be32e7f1SMilanka Ringwald }; 201be32e7f1SMilanka Ringwald 202be32e7f1SMilanka Ringwald const char * avrcp_repeat2str(uint8_t index){ 203be32e7f1SMilanka Ringwald if (index >= 1 && index <= 4) return avrcp_repeat_mode_name[index-1]; 204be32e7f1SMilanka Ringwald return "NONE"; 205be32e7f1SMilanka Ringwald } 206be32e7f1SMilanka Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 207be32e7f1SMilanka Ringwald 208be32e7f1SMilanka 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){ 209be32e7f1SMilanka Ringwald uint8_t* attribute; 210be32e7f1SMilanka Ringwald de_create_sequence(service); 211be32e7f1SMilanka Ringwald 212be32e7f1SMilanka Ringwald // 0x0000 "Service Record Handle" 213235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE); 214be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle); 215be32e7f1SMilanka Ringwald 216be32e7f1SMilanka Ringwald // 0x0001 "Service Class ID List" 217235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST); 218be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 219be32e7f1SMilanka Ringwald { 220be32e7f1SMilanka Ringwald if (controller){ 221be32e7f1SMilanka Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, AV_REMOTE_CONTROL); 222be32e7f1SMilanka Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, AV_REMOTE_CONTROL_CONTROLLER); 223be32e7f1SMilanka Ringwald } else { 224be32e7f1SMilanka Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, AV_REMOTE_CONTROL_TARGET); 225be32e7f1SMilanka Ringwald } 226be32e7f1SMilanka Ringwald } 227be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 228be32e7f1SMilanka Ringwald 229be32e7f1SMilanka Ringwald // 0x0004 "Protocol Descriptor List" 230235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST); 231be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 232be32e7f1SMilanka Ringwald { 233be32e7f1SMilanka Ringwald uint8_t* l2cpProtocol = de_push_sequence(attribute); 234be32e7f1SMilanka Ringwald { 235235946f1SMatthias Ringwald de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP); 236235946f1SMatthias Ringwald de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP); 237be32e7f1SMilanka Ringwald } 238be32e7f1SMilanka Ringwald de_pop_sequence(attribute, l2cpProtocol); 239be32e7f1SMilanka Ringwald 240be32e7f1SMilanka Ringwald uint8_t* avctpProtocol = de_push_sequence(attribute); 241be32e7f1SMilanka Ringwald { 242235946f1SMatthias Ringwald de_add_number(avctpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP); // avctpProtocol_service 243be32e7f1SMilanka Ringwald de_add_number(avctpProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version 244be32e7f1SMilanka Ringwald } 245be32e7f1SMilanka Ringwald de_pop_sequence(attribute, avctpProtocol); 246be32e7f1SMilanka Ringwald 247be32e7f1SMilanka Ringwald if (browsing){ 248235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST); 249be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 250be32e7f1SMilanka Ringwald { 251be32e7f1SMilanka Ringwald uint8_t* browsing_l2cpProtocol = de_push_sequence(attribute); 252be32e7f1SMilanka Ringwald { 253235946f1SMatthias Ringwald de_add_number(browsing_l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP); 254be32e7f1SMilanka Ringwald de_add_number(browsing_l2cpProtocol, DE_UINT, DE_SIZE_16, PSM_AVCTP_BROWSING); 255be32e7f1SMilanka Ringwald } 256be32e7f1SMilanka Ringwald de_pop_sequence(attribute, browsing_l2cpProtocol); 257be32e7f1SMilanka Ringwald 258be32e7f1SMilanka Ringwald uint8_t* browsing_avctpProtocol = de_push_sequence(attribute); 259be32e7f1SMilanka Ringwald { 260235946f1SMatthias Ringwald de_add_number(browsing_avctpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP); // browsing_avctpProtocol_service 261be32e7f1SMilanka Ringwald de_add_number(browsing_avctpProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version 262be32e7f1SMilanka Ringwald } 263be32e7f1SMilanka Ringwald de_pop_sequence(attribute, browsing_avctpProtocol); 264be32e7f1SMilanka Ringwald } 265be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 266be32e7f1SMilanka Ringwald } 267be32e7f1SMilanka Ringwald } 268be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 269be32e7f1SMilanka Ringwald 270be32e7f1SMilanka Ringwald // 0x0005 "Public Browse Group" 271235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BROWSE_GROUP_LIST); // public browse group 272be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 273be32e7f1SMilanka Ringwald { 274235946f1SMatthias Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT); 275be32e7f1SMilanka Ringwald } 276be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 277be32e7f1SMilanka Ringwald 278be32e7f1SMilanka Ringwald // 0x0009 "Bluetooth Profile Descriptor List" 279235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST); 280be32e7f1SMilanka Ringwald attribute = de_push_sequence(service); 281be32e7f1SMilanka Ringwald { 282be32e7f1SMilanka Ringwald uint8_t *avrcProfile = de_push_sequence(attribute); 283be32e7f1SMilanka Ringwald { 284be32e7f1SMilanka Ringwald de_add_number(avrcProfile, DE_UUID, DE_SIZE_16, AV_REMOTE_CONTROL); 285be32e7f1SMilanka Ringwald de_add_number(avrcProfile, DE_UINT, DE_SIZE_16, 0x0105); 286be32e7f1SMilanka Ringwald } 287be32e7f1SMilanka Ringwald de_pop_sequence(attribute, avrcProfile); 288be32e7f1SMilanka Ringwald } 289be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute); 290be32e7f1SMilanka Ringwald 291be32e7f1SMilanka Ringwald 292be32e7f1SMilanka Ringwald // 0x0100 "Service Name" 293be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); 294be32e7f1SMilanka Ringwald if (service_name){ 295be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(service_name), (uint8_t *) service_name); 296be32e7f1SMilanka Ringwald } else { 297be32e7f1SMilanka Ringwald if (controller){ 298be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_controller_service_name), (uint8_t *) default_avrcp_controller_service_name); 299be32e7f1SMilanka Ringwald } else { 300be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_target_service_name), (uint8_t *) default_avrcp_target_service_name); 301be32e7f1SMilanka Ringwald } 302be32e7f1SMilanka Ringwald } 303be32e7f1SMilanka Ringwald 304be32e7f1SMilanka Ringwald // 0x0100 "Provider Name" 305be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102); 306be32e7f1SMilanka Ringwald if (service_provider_name){ 307be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name); 308be32e7f1SMilanka Ringwald } else { 309be32e7f1SMilanka Ringwald if (controller){ 310be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_controller_service_provider_name), (uint8_t *) default_avrcp_controller_service_provider_name); 311be32e7f1SMilanka Ringwald } else { 312be32e7f1SMilanka Ringwald de_add_data(service, DE_STRING, strlen(default_avrcp_target_service_provider_name), (uint8_t *) default_avrcp_target_service_provider_name); 313be32e7f1SMilanka Ringwald } 314be32e7f1SMilanka Ringwald } 315be32e7f1SMilanka Ringwald 316be32e7f1SMilanka Ringwald // 0x0311 "Supported Features" 317be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); 318be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); 319be32e7f1SMilanka Ringwald } 320be32e7f1SMilanka Ringwald 321be32e7f1SMilanka 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){ 322be32e7f1SMilanka Ringwald avrcp_create_sdp_record(1, service, service_record_handle, browsing, supported_features, service_name, service_provider_name); 323be32e7f1SMilanka Ringwald } 324be32e7f1SMilanka Ringwald 325be32e7f1SMilanka 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){ 326be32e7f1SMilanka Ringwald avrcp_create_sdp_record(0, service, service_record_handle, browsing, supported_features, service_name, service_provider_name); 327be32e7f1SMilanka Ringwald } 328be32e7f1SMilanka Ringwald 3296f43fcd7SMatthias Ringwald static void avrcp_emit_connection_established(btstack_packet_handler_t callback, uint8_t status, bd_addr_t addr, uint16_t con_handle, uint16_t avrcp_cid){ 330b193c45eSMilanka Ringwald if (!callback) return; 331b193c45eSMilanka Ringwald uint8_t event[14]; 332b193c45eSMilanka Ringwald int pos = 0; 333b193c45eSMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 334b193c45eSMilanka Ringwald event[pos++] = sizeof(event) - 2; 335b193c45eSMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_CONNECTION_ESTABLISHED; 3366f43fcd7SMatthias Ringwald event[pos++] = status; 337b193c45eSMilanka Ringwald reverse_bd_addr(addr,&event[pos]); 338b193c45eSMilanka Ringwald pos += 6; 339b193c45eSMilanka Ringwald little_endian_store_16(event, pos, con_handle); 340b193c45eSMilanka Ringwald pos += 2; 341b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 342b193c45eSMilanka Ringwald pos += 2; 343b193c45eSMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 344b193c45eSMilanka Ringwald } 345b193c45eSMilanka Ringwald 346d3b6a85eSMatthias Ringwald static void avrcp_emit_repeat_and_shuffle_mode(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t ctype, avrcp_repeat_mode_t repeat_mode, avrcp_shuffle_mode_t shuffle_mode){ 347be32e7f1SMilanka Ringwald if (!callback) return; 348be32e7f1SMilanka Ringwald uint8_t event[8]; 349be32e7f1SMilanka Ringwald int pos = 0; 350be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 351be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 352be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE; 353b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 354be32e7f1SMilanka Ringwald pos += 2; 355d3b6a85eSMatthias Ringwald event[pos++] = ctype; 356be32e7f1SMilanka Ringwald event[pos++] = repeat_mode; 357be32e7f1SMilanka Ringwald event[pos++] = shuffle_mode; 358be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 359be32e7f1SMilanka Ringwald } 360be32e7f1SMilanka Ringwald 361d3b6a85eSMatthias Ringwald static void avrcp_emit_operation_status(btstack_packet_handler_t callback, uint8_t subevent, uint16_t avrcp_cid, uint8_t ctype, uint8_t operation_id){ 362be32e7f1SMilanka Ringwald if (!callback) return; 363be32e7f1SMilanka Ringwald uint8_t event[7]; 364be32e7f1SMilanka Ringwald int pos = 0; 365be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 366be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 367be32e7f1SMilanka Ringwald event[pos++] = subevent; 368b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 369be32e7f1SMilanka Ringwald pos += 2; 370d3b6a85eSMatthias Ringwald event[pos++] = ctype; 371be32e7f1SMilanka Ringwald event[pos++] = operation_id; 372be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 373be32e7f1SMilanka Ringwald } 374be32e7f1SMilanka Ringwald 375b193c45eSMilanka Ringwald static void avrcp_emit_connection_closed(btstack_packet_handler_t callback, uint16_t avrcp_cid){ 376be32e7f1SMilanka Ringwald if (!callback) return; 377be32e7f1SMilanka Ringwald uint8_t event[5]; 378be32e7f1SMilanka Ringwald int pos = 0; 379be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 380be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2; 381be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_CONNECTION_RELEASED; 382b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 383be32e7f1SMilanka Ringwald pos += 2; 384be32e7f1SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 385be32e7f1SMilanka Ringwald } 386be32e7f1SMilanka Ringwald 387be32e7f1SMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_bd_addr(bd_addr_t addr){ 388be32e7f1SMilanka Ringwald btstack_linked_list_iterator_t it; 389be32e7f1SMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_connections); 390be32e7f1SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 391be32e7f1SMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it); 392be32e7f1SMilanka Ringwald if (memcmp(addr, connection->remote_addr, 6) != 0) continue; 393be32e7f1SMilanka Ringwald return connection; 394be32e7f1SMilanka Ringwald } 395be32e7f1SMilanka Ringwald return NULL; 396be32e7f1SMilanka Ringwald } 397be32e7f1SMilanka Ringwald 398be32e7f1SMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_l2cap_signaling_cid(uint16_t l2cap_cid){ 399be32e7f1SMilanka Ringwald btstack_linked_list_iterator_t it; 400be32e7f1SMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_connections); 401be32e7f1SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 402be32e7f1SMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it); 403be32e7f1SMilanka Ringwald if (connection->l2cap_signaling_cid != l2cap_cid) continue; 404be32e7f1SMilanka Ringwald return connection; 405be32e7f1SMilanka Ringwald } 406be32e7f1SMilanka Ringwald return NULL; 407be32e7f1SMilanka Ringwald } 408be32e7f1SMilanka Ringwald 409be32e7f1SMilanka Ringwald static void avrcp_request_can_send_now(avrcp_connection_t * connection, uint16_t l2cap_cid){ 410be32e7f1SMilanka Ringwald connection->wait_to_send = 1; 411be32e7f1SMilanka Ringwald l2cap_request_can_send_now_event(l2cap_cid); 412be32e7f1SMilanka Ringwald } 413be32e7f1SMilanka Ringwald 414be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timeout_handler(btstack_timer_source_t * timer){ 415be32e7f1SMilanka Ringwald UNUSED(timer); 416be32e7f1SMilanka Ringwald avrcp_connection_t * connection = btstack_run_loop_get_timer_context(timer); 417be32e7f1SMilanka Ringwald btstack_run_loop_set_timer(&connection->press_and_hold_cmd_timer, 2000); // 2 seconds timeout 418be32e7f1SMilanka Ringwald btstack_run_loop_add_timer(&connection->press_and_hold_cmd_timer); 419be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_PRESS_COMMAND; 420be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 421be32e7f1SMilanka Ringwald } 422be32e7f1SMilanka Ringwald 423be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timer_start(avrcp_connection_t * connection){ 424be32e7f1SMilanka Ringwald btstack_run_loop_remove_timer(&connection->press_and_hold_cmd_timer); 425be32e7f1SMilanka Ringwald btstack_run_loop_set_timer_handler(&connection->press_and_hold_cmd_timer, avrcp_press_and_hold_timeout_handler); 426be32e7f1SMilanka Ringwald btstack_run_loop_set_timer_context(&connection->press_and_hold_cmd_timer, connection); 427be32e7f1SMilanka Ringwald btstack_run_loop_set_timer(&connection->press_and_hold_cmd_timer, 2000); // 2 seconds timeout 428be32e7f1SMilanka Ringwald btstack_run_loop_add_timer(&connection->press_and_hold_cmd_timer); 429be32e7f1SMilanka Ringwald } 430be32e7f1SMilanka Ringwald 431be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timer_stop(avrcp_connection_t * connection){ 432*d1845da3SMilanka Ringwald connection->continuous_fast_forward_cmd = 0; 433be32e7f1SMilanka Ringwald btstack_run_loop_remove_timer(&connection->press_and_hold_cmd_timer); 434be32e7f1SMilanka Ringwald } 435be32e7f1SMilanka Ringwald 43631a5a745SMatthias Ringwald static uint8_t request_pass_through_release_control_cmd(avrcp_connection_t * connection){ 437be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_RELEASE_COMMAND; 438*d1845da3SMilanka Ringwald if (connection->continuous_fast_forward_cmd){ 439be32e7f1SMilanka Ringwald avrcp_press_and_hold_timer_stop(connection); 440be32e7f1SMilanka Ringwald } 441be32e7f1SMilanka Ringwald connection->cmd_operands[0] = 0x80 | connection->cmd_operands[0]; 442be32e7f1SMilanka Ringwald connection->transaction_label++; 443be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 44431a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 445be32e7f1SMilanka Ringwald } 446be32e7f1SMilanka Ringwald 447*d1845da3SMilanka Ringwald static inline uint8_t request_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed, uint8_t continuous_fast_forward_cmd){ 448b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 449be32e7f1SMilanka Ringwald if (!connection){ 450be32e7f1SMilanka Ringwald log_error("avrcp: could not find a connection."); 45131a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 452be32e7f1SMilanka Ringwald } 45331a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 454be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_PRESS_COMMAND; 4559cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_PASS_THROUGH; 456be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_CONTROL; 457be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 458be32e7f1SMilanka Ringwald connection->subunit_id = 0; 459adcdded9SMilanka Ringwald connection->cmd_operands_length = 0; 460be32e7f1SMilanka Ringwald 461*d1845da3SMilanka Ringwald connection->continuous_fast_forward_cmd = continuous_fast_forward_cmd; 462adcdded9SMilanka Ringwald connection->cmd_operands_length = 2; 463be32e7f1SMilanka Ringwald connection->cmd_operands[0] = opid; 464be32e7f1SMilanka Ringwald if (playback_speed > 0){ 465be32e7f1SMilanka Ringwald connection->cmd_operands[2] = playback_speed; 466adcdded9SMilanka Ringwald connection->cmd_operands_length++; 467be32e7f1SMilanka Ringwald } 468adcdded9SMilanka Ringwald connection->cmd_operands[1] = connection->cmd_operands_length - 2; 469be32e7f1SMilanka Ringwald 470*d1845da3SMilanka Ringwald if (connection->continuous_fast_forward_cmd){ 471be32e7f1SMilanka Ringwald avrcp_press_and_hold_timer_start(connection); 472be32e7f1SMilanka Ringwald } 473*d1845da3SMilanka Ringwald 474be32e7f1SMilanka Ringwald connection->transaction_label++; 475be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 47631a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 477be32e7f1SMilanka Ringwald } 478be32e7f1SMilanka Ringwald 479*d1845da3SMilanka Ringwald static uint8_t request_single_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed){ 480*d1845da3SMilanka Ringwald return request_pass_through_press_control_cmd(avrcp_cid, opid, playback_speed, 0); 481*d1845da3SMilanka Ringwald } 482*d1845da3SMilanka Ringwald 483*d1845da3SMilanka Ringwald static uint8_t request_continuous_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed){ 484*d1845da3SMilanka Ringwald return request_pass_through_press_control_cmd(avrcp_cid, opid, playback_speed, 1); 485*d1845da3SMilanka Ringwald } 486be32e7f1SMilanka Ringwald 487be32e7f1SMilanka Ringwald static int avrcp_send_cmd(uint16_t cid, avrcp_connection_t * connection){ 488adcdded9SMilanka Ringwald uint8_t command[30]; 489be32e7f1SMilanka Ringwald int pos = 0; 490be32e7f1SMilanka Ringwald // transport header 491be32e7f1SMilanka Ringwald // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 492be32e7f1SMilanka Ringwald command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0; 493be32e7f1SMilanka Ringwald // Profile IDentifier (PID) 494be32e7f1SMilanka Ringwald command[pos++] = AV_REMOTE_CONTROL >> 8; 495be32e7f1SMilanka Ringwald command[pos++] = AV_REMOTE_CONTROL & 0x00FF; 496be32e7f1SMilanka Ringwald 497be32e7f1SMilanka Ringwald // command_type 498be32e7f1SMilanka Ringwald command[pos++] = connection->command_type; 499be32e7f1SMilanka Ringwald // subunit_type | subunit ID 500be32e7f1SMilanka Ringwald command[pos++] = (connection->subunit_type << 3) | connection->subunit_id; 501be32e7f1SMilanka Ringwald // opcode 5029cc1f3ceSMilanka Ringwald command[pos++] = (uint8_t)connection->command_opcode; 503be32e7f1SMilanka Ringwald // operands 504adcdded9SMilanka Ringwald memcpy(command+pos, connection->cmd_operands, connection->cmd_operands_length); 505adcdded9SMilanka Ringwald pos += connection->cmd_operands_length; 506be32e7f1SMilanka Ringwald 507be32e7f1SMilanka Ringwald return l2cap_send(cid, command, pos); 508be32e7f1SMilanka Ringwald } 509be32e7f1SMilanka Ringwald 510be32e7f1SMilanka Ringwald static int avrcp_register_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){ 511be32e7f1SMilanka Ringwald if (connection->notifications_to_deregister & (1 << event_id)) return 0; 512be32e7f1SMilanka Ringwald if (connection->notifications_enabled & (1 << event_id)) return 0; 513be32e7f1SMilanka Ringwald if (connection->notifications_to_register & (1 << event_id)) return 0; 514be32e7f1SMilanka Ringwald connection->notifications_to_register |= (1 << event_id); 515*d1845da3SMilanka Ringwald printf("register %d\n", event_id); 516be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 517be32e7f1SMilanka Ringwald return 1; 518be32e7f1SMilanka Ringwald } 519be32e7f1SMilanka Ringwald 520be32e7f1SMilanka Ringwald static void avrcp_prepare_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){ 521be32e7f1SMilanka Ringwald connection->transaction_label++; 5229cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 523be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_NOTIFY; 524be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 525be32e7f1SMilanka Ringwald connection->subunit_id = 0; 526be32e7f1SMilanka Ringwald int pos = 0; 527be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 528be32e7f1SMilanka Ringwald pos += 3; 529be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_REGISTER_NOTIFICATION; 530be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; // reserved(upper 6) | packet_type -> 0 531be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 5); // parameter length 532be32e7f1SMilanka Ringwald pos += 2; 533be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = event_id; 534be32e7f1SMilanka Ringwald big_endian_store_32(connection->cmd_operands, pos, 0); 535be32e7f1SMilanka Ringwald pos += 4; 536adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 537be32e7f1SMilanka Ringwald // AVRCP_SPEC_V14.pdf 166 538be32e7f1SMilanka Ringwald // answer page 61 539be32e7f1SMilanka Ringwald } 540be32e7f1SMilanka Ringwald 5419cc1f3ceSMilanka Ringwald static uint8_t avrcp_cmd_opcode(uint8_t *packet, uint16_t size){ 5429cc1f3ceSMilanka Ringwald uint8_t cmd_opcode_index = 5; 5439cc1f3ceSMilanka Ringwald if (cmd_opcode_index > size) return AVRCP_CMD_OPCODE_UNDEFINED; 5449cc1f3ceSMilanka Ringwald return packet[cmd_opcode_index]; 545be32e7f1SMilanka Ringwald } 546be32e7f1SMilanka Ringwald 5479cc1f3ceSMilanka Ringwald static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){ 548be32e7f1SMilanka Ringwald uint8_t operands[20]; 549be32e7f1SMilanka Ringwald uint8_t opcode; 5508a2c6c7cSMilanka Ringwald int pos = 3; 551be32e7f1SMilanka Ringwald // uint8_t transport_header = packet[0]; 552be32e7f1SMilanka Ringwald // uint8_t transaction_label = transport_header >> 4; 553be32e7f1SMilanka Ringwald // uint8_t packet_type = (transport_header & 0x0F) >> 2; 554be32e7f1SMilanka Ringwald // uint8_t frame_type = (transport_header & 0x03) >> 1; 555be32e7f1SMilanka Ringwald // uint8_t ipid = transport_header & 0x01; 5568a2c6c7cSMilanka Ringwald // uint8_t byte_value = packet[2]; 557be32e7f1SMilanka Ringwald // uint16_t pid = (byte_value << 8) | packet[2]; 5588a2c6c7cSMilanka Ringwald 5598a2c6c7cSMilanka Ringwald avrcp_command_type_t ctype = packet[pos++]; 5608a2c6c7cSMilanka Ringwald uint8_t byte_value = packet[pos++]; 5618a2c6c7cSMilanka Ringwald avrcp_subunit_type_t subunit_type = byte_value >> 3; 5628a2c6c7cSMilanka Ringwald avrcp_subunit_type_t subunit_id = byte_value & 0x07; 5638a2c6c7cSMilanka Ringwald opcode = packet[pos++]; 564be32e7f1SMilanka Ringwald 565be32e7f1SMilanka Ringwald // printf(" Transport header 0x%02x (transaction_label %d, packet_type %d, frame_type %d, ipid %d), pid 0x%4x\n", 566be32e7f1SMilanka Ringwald // transport_header, transaction_label, packet_type, frame_type, ipid, pid); 567be32e7f1SMilanka Ringwald // // printf_hexdump(packet+pos, size-pos); 5689cc1f3ceSMilanka Ringwald switch (avrcp_cmd_opcode(packet,size)){ 569be32e7f1SMilanka Ringwald case AVRCP_CMD_OPCODE_UNIT_INFO:{ 570294335f1SMilanka Ringwald if (connection->state != AVCTP_W2_RECEIVE_RESPONSE) return; 5719cc1f3ceSMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 572294335f1SMilanka Ringwald 573be32e7f1SMilanka Ringwald // operands: 574be32e7f1SMilanka Ringwald memcpy(operands, packet+pos, 5); 575be32e7f1SMilanka Ringwald uint8_t unit_type = operands[1] >> 3; 576be32e7f1SMilanka Ringwald uint8_t unit = operands[1] & 0x07; 577be32e7f1SMilanka Ringwald uint32_t company_id = operands[2] << 16 | operands[3] << 8 | operands[4]; 578be32e7f1SMilanka 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", 579be32e7f1SMilanka Ringwald ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id ); 580be32e7f1SMilanka Ringwald break; 581be32e7f1SMilanka Ringwald } 582be32e7f1SMilanka Ringwald case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT: 583be32e7f1SMilanka Ringwald if (size - pos < 7) { 584be32e7f1SMilanka Ringwald log_error("avrcp: wrong packet size"); 585be32e7f1SMilanka Ringwald return; 586be32e7f1SMilanka Ringwald }; 587be32e7f1SMilanka Ringwald // operands: 588be32e7f1SMilanka Ringwald memcpy(operands, packet+pos, 7); 589be32e7f1SMilanka Ringwald pos += 7; 590be32e7f1SMilanka Ringwald // uint32_t company_id = operands[0] << 16 | operands[1] << 8 | operands[2]; 591be32e7f1SMilanka Ringwald uint8_t pdu_id = operands[3]; 592294335f1SMilanka Ringwald 593294335f1SMilanka Ringwald if (connection->state != AVCTP_W2_RECEIVE_RESPONSE && pdu_id != AVRCP_PDU_ID_REGISTER_NOTIFICATION){ 594ac98fce4SMilanka Ringwald log_info("AVRCP_CMD_OPCODE_VENDOR_DEPENDENT state %d", connection->state); 595294335f1SMilanka Ringwald return; 596294335f1SMilanka Ringwald } 597294335f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 598294335f1SMilanka Ringwald 599294335f1SMilanka Ringwald 600be32e7f1SMilanka Ringwald // uint8_t unit_type = operands[4] >> 3; 601be32e7f1SMilanka Ringwald // uint8_t unit = operands[4] & 0x07; 602be32e7f1SMilanka Ringwald uint16_t param_length = big_endian_read_16(operands, 5); 603be32e7f1SMilanka Ringwald 604be32e7f1SMilanka 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", 605be32e7f1SMilanka Ringwald // ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id ); 606be32e7f1SMilanka Ringwald 607be32e7f1SMilanka Ringwald // if (ctype == AVRCP_CTYPE_RESPONSE_INTERIM) return; 608be32e7f1SMilanka Ringwald log_info(" VENDOR DEPENDENT response: pdu id 0x%02x, param_length %d, status %s", pdu_id, param_length, avrcp_ctype2str(ctype)); 609be32e7f1SMilanka Ringwald switch (pdu_id){ 610be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue:{ 611be32e7f1SMilanka Ringwald uint8_t num_attributes = packet[pos++]; 612be32e7f1SMilanka Ringwald int i; 613be32e7f1SMilanka Ringwald uint8_t repeat_mode = 0; 614be32e7f1SMilanka Ringwald uint8_t shuffle_mode = 0; 615be32e7f1SMilanka Ringwald for (i = 0; i < num_attributes; i++){ 616be32e7f1SMilanka Ringwald uint8_t attribute_id = packet[pos++]; 617be32e7f1SMilanka Ringwald uint8_t attribute_value = packet[pos++]; 618be32e7f1SMilanka Ringwald switch (attribute_id){ 619be32e7f1SMilanka Ringwald case 0x02: 620be32e7f1SMilanka Ringwald repeat_mode = attribute_value; 621be32e7f1SMilanka Ringwald break; 622be32e7f1SMilanka Ringwald case 0x03: 623be32e7f1SMilanka Ringwald shuffle_mode = attribute_value; 624be32e7f1SMilanka Ringwald break; 625be32e7f1SMilanka Ringwald default: 626be32e7f1SMilanka Ringwald break; 627be32e7f1SMilanka Ringwald } 628be32e7f1SMilanka Ringwald } 62947ec446bSMatthias Ringwald avrcp_emit_repeat_and_shuffle_mode(avrcp_callback, connection->l2cap_signaling_cid, ctype, repeat_mode, shuffle_mode); 630be32e7f1SMilanka Ringwald break; 631be32e7f1SMilanka Ringwald } 632be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_SetPlayerApplicationSettingValue:{ 633be32e7f1SMilanka Ringwald uint8_t event[6]; 634be32e7f1SMilanka Ringwald int offset = 0; 635be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 636be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 637be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE; 63847ec446bSMatthias Ringwald little_endian_store_16(event, offset, connection->l2cap_signaling_cid); 639be32e7f1SMilanka Ringwald offset += 2; 640be32e7f1SMilanka Ringwald event[offset++] = ctype; 641be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 642be32e7f1SMilanka Ringwald break; 643be32e7f1SMilanka Ringwald } 644be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME:{ 645be32e7f1SMilanka Ringwald uint8_t event[7]; 646be32e7f1SMilanka Ringwald int offset = 0; 647be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 648be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 649be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_SET_ABSOLUTE_VOLUME_RESPONSE; 65047ec446bSMatthias Ringwald little_endian_store_16(event, offset, connection->l2cap_signaling_cid); 651be32e7f1SMilanka Ringwald offset += 2; 652be32e7f1SMilanka Ringwald event[offset++] = ctype; 653be32e7f1SMilanka Ringwald event[offset++] = packet[pos++]; 654be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 655be32e7f1SMilanka Ringwald break; 656be32e7f1SMilanka Ringwald } 657be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GET_CAPABILITIES:{ 658be32e7f1SMilanka Ringwald avrcp_capability_id_t capability_id = packet[pos++]; 659be32e7f1SMilanka Ringwald uint8_t capability_count = packet[pos++]; 660be32e7f1SMilanka Ringwald int i; 661be32e7f1SMilanka Ringwald switch (capability_id){ 662be32e7f1SMilanka Ringwald case AVRCP_CAPABILITY_ID_COMPANY: 663be32e7f1SMilanka Ringwald // log_info("Supported companies %d: ", capability_count); 664be32e7f1SMilanka Ringwald for (i = 0; i < capability_count; i++){ 665be32e7f1SMilanka Ringwald uint32_t company_id = big_endian_read_24(packet, pos); 666be32e7f1SMilanka Ringwald pos += 3; 667be32e7f1SMilanka Ringwald log_info(" 0x%06x, ", company_id); 668be32e7f1SMilanka Ringwald } 669be32e7f1SMilanka Ringwald break; 670be32e7f1SMilanka Ringwald case AVRCP_CAPABILITY_ID_EVENT: 671be32e7f1SMilanka Ringwald // log_info("Supported events %d: ", capability_count); 672be32e7f1SMilanka Ringwald for (i = 0; i < capability_count; i++){ 673be32e7f1SMilanka Ringwald uint8_t event_id = packet[pos++]; 674be32e7f1SMilanka Ringwald log_info(" 0x%02x %s", event_id, avrcp_event2str(event_id)); 675be32e7f1SMilanka Ringwald } 676be32e7f1SMilanka Ringwald break; 677be32e7f1SMilanka Ringwald } 678be32e7f1SMilanka Ringwald break; 679be32e7f1SMilanka Ringwald } 680be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GET_PLAY_STATUS:{ 681be32e7f1SMilanka Ringwald uint32_t song_length = big_endian_read_32(packet, pos); 682be32e7f1SMilanka Ringwald pos += 4; 683be32e7f1SMilanka Ringwald uint32_t song_position = big_endian_read_32(packet, pos); 684be32e7f1SMilanka Ringwald pos += 4; 685be32e7f1SMilanka Ringwald uint8_t play_status = packet[pos]; 686be32e7f1SMilanka Ringwald // log_info(" GET_PLAY_STATUS length 0x%04X, position 0x%04X, status %s", song_length, song_position, avrcp_play_status2str(play_status)); 687be32e7f1SMilanka Ringwald 688be32e7f1SMilanka Ringwald uint8_t event[15]; 689be32e7f1SMilanka Ringwald int offset = 0; 690be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 691be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 692be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_PLAY_STATUS; 69347ec446bSMatthias Ringwald little_endian_store_16(event, offset, connection->l2cap_signaling_cid); 694be32e7f1SMilanka Ringwald offset += 2; 695be32e7f1SMilanka Ringwald event[offset++] = ctype; 696be32e7f1SMilanka Ringwald little_endian_store_32(event, offset, song_length); 697be32e7f1SMilanka Ringwald offset += 4; 698be32e7f1SMilanka Ringwald little_endian_store_32(event, offset, song_position); 699be32e7f1SMilanka Ringwald offset += 4; 700be32e7f1SMilanka Ringwald event[offset++] = play_status; 701be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 702be32e7f1SMilanka Ringwald break; 703be32e7f1SMilanka Ringwald } 704be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_REGISTER_NOTIFICATION:{ 705be32e7f1SMilanka Ringwald uint8_t event_id = packet[pos++]; 706be32e7f1SMilanka Ringwald uint16_t event_mask = (1 << event_id); 707be32e7f1SMilanka Ringwald uint16_t reset_event_mask = ~event_mask; 708be32e7f1SMilanka Ringwald switch (ctype){ 709be32e7f1SMilanka Ringwald case AVRCP_CTYPE_RESPONSE_INTERIM: 710be32e7f1SMilanka Ringwald // register as enabled 711be32e7f1SMilanka Ringwald connection->notifications_enabled |= event_mask; 712be32e7f1SMilanka Ringwald // printf("INTERIM notifications_enabled 0x%2x, notifications_to_register 0x%2x\n", connection->notifications_enabled, connection->notifications_to_register); 713be32e7f1SMilanka Ringwald break; 714be32e7f1SMilanka Ringwald case AVRCP_CTYPE_RESPONSE_CHANGED_STABLE: 715be32e7f1SMilanka Ringwald // received change, event is considered deregistered 716be32e7f1SMilanka Ringwald // we are re-enabling it automatically, if it is not 717be32e7f1SMilanka Ringwald // explicitly disabled 718be32e7f1SMilanka Ringwald connection->notifications_enabled &= reset_event_mask; 719be32e7f1SMilanka Ringwald if (! (connection->notifications_to_deregister & event_mask)){ 720be32e7f1SMilanka Ringwald avrcp_register_notification(connection, event_id); 721be32e7f1SMilanka Ringwald // printf("CHANGED_STABLE notifications_enabled 0x%2x, notifications_to_register 0x%2x\n", connection->notifications_enabled, connection->notifications_to_register); 722be32e7f1SMilanka Ringwald } else { 723be32e7f1SMilanka Ringwald connection->notifications_to_deregister &= reset_event_mask; 724be32e7f1SMilanka Ringwald } 725be32e7f1SMilanka Ringwald break; 726be32e7f1SMilanka Ringwald default: 727be32e7f1SMilanka Ringwald connection->notifications_to_register &= reset_event_mask; 728be32e7f1SMilanka Ringwald connection->notifications_enabled &= reset_event_mask; 729be32e7f1SMilanka Ringwald connection->notifications_to_deregister &= reset_event_mask; 730be32e7f1SMilanka Ringwald break; 731be32e7f1SMilanka Ringwald } 732be32e7f1SMilanka Ringwald 733be32e7f1SMilanka Ringwald switch (event_id){ 734be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED:{ 735be32e7f1SMilanka Ringwald uint8_t event[7]; 736be32e7f1SMilanka Ringwald int offset = 0; 737be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 738be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 739be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED; 74047ec446bSMatthias Ringwald little_endian_store_16(event, offset, connection->l2cap_signaling_cid); 741be32e7f1SMilanka Ringwald offset += 2; 742be32e7f1SMilanka Ringwald event[offset++] = ctype; 743be32e7f1SMilanka Ringwald event[offset++] = packet[pos]; 744be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 745be32e7f1SMilanka Ringwald break; 746be32e7f1SMilanka Ringwald } 747be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED:{ 748ee6d17f2SMatthias Ringwald uint8_t event[6]; 749be32e7f1SMilanka Ringwald int offset = 0; 750be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 751be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 752be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED; 75347ec446bSMatthias Ringwald little_endian_store_16(event, offset, connection->l2cap_signaling_cid); 754be32e7f1SMilanka Ringwald offset += 2; 755be32e7f1SMilanka Ringwald event[offset++] = ctype; 756be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 757be32e7f1SMilanka Ringwald break; 758be32e7f1SMilanka Ringwald } 759be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED:{ 760be32e7f1SMilanka Ringwald uint8_t event[6]; 761be32e7f1SMilanka Ringwald int offset = 0; 762be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 763be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 764be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED; 76547ec446bSMatthias Ringwald little_endian_store_16(event, offset, connection->l2cap_signaling_cid); 766be32e7f1SMilanka Ringwald offset += 2; 767be32e7f1SMilanka Ringwald event[offset++] = ctype; 768be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 769be32e7f1SMilanka Ringwald break; 770be32e7f1SMilanka Ringwald } 771be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_AVAILABLE_PLAYERS_CHANGED:{ 772be32e7f1SMilanka Ringwald uint8_t event[6]; 773be32e7f1SMilanka Ringwald int offset = 0; 774be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 775be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 776be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED; 77747ec446bSMatthias Ringwald little_endian_store_16(event, offset, connection->l2cap_signaling_cid); 778be32e7f1SMilanka Ringwald offset += 2; 779be32e7f1SMilanka Ringwald event[offset++] = ctype; 780be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 781be32e7f1SMilanka Ringwald break; 782be32e7f1SMilanka Ringwald } 783be32e7f1SMilanka Ringwald case AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED:{ 784be32e7f1SMilanka Ringwald uint8_t event[7]; 785be32e7f1SMilanka Ringwald int offset = 0; 786be32e7f1SMilanka Ringwald event[offset++] = HCI_EVENT_AVRCP_META; 787be32e7f1SMilanka Ringwald event[offset++] = sizeof(event) - 2; 788be32e7f1SMilanka Ringwald event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED; 78947ec446bSMatthias Ringwald little_endian_store_16(event, offset, connection->l2cap_signaling_cid); 790be32e7f1SMilanka Ringwald offset += 2; 791be32e7f1SMilanka Ringwald event[offset++] = ctype; 792be32e7f1SMilanka Ringwald event[offset++] = packet[pos++] & 0x7F; 793be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 794be32e7f1SMilanka Ringwald break; 795be32e7f1SMilanka Ringwald } 796be32e7f1SMilanka Ringwald // case AVRCP_NOTIFICATION_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:{ 797be32e7f1SMilanka Ringwald // uint8_t num_PlayerApplicationSettingAttributes = packet[pos++]; 798be32e7f1SMilanka Ringwald // int i; 799be32e7f1SMilanka Ringwald // for (i = 0; i < num_PlayerApplicationSettingAttributes; i++){ 800be32e7f1SMilanka Ringwald // uint8_t PlayerApplicationSetting_AttributeID = packet[pos++]; 801be32e7f1SMilanka Ringwald // uint8_t PlayerApplicationSettingValueID = packet[pos++]; 802be32e7f1SMilanka Ringwald // } 803be32e7f1SMilanka Ringwald // break; 804be32e7f1SMilanka Ringwald // } 805be32e7f1SMilanka Ringwald // case AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED: 806be32e7f1SMilanka Ringwald // uint16_t player_id = big_endian_read_16(packet, pos); 807be32e7f1SMilanka Ringwald // pos += 2; 808be32e7f1SMilanka Ringwald // uint16_t uid_counter = big_endian_read_16(packet, pos); 809be32e7f1SMilanka Ringwald // pos += 2; 810be32e7f1SMilanka Ringwald // break; 811be32e7f1SMilanka Ringwald // case AVRCP_NOTIFICATION_EVENT_UIDS_CHANGED: 812be32e7f1SMilanka Ringwald // uint16_t uid_counter = big_endian_read_16(packet, pos); 813be32e7f1SMilanka Ringwald // pos += 2; 814be32e7f1SMilanka Ringwald // break; 815be32e7f1SMilanka Ringwald default: 816be32e7f1SMilanka Ringwald log_info("avrcp: not implemented"); 817be32e7f1SMilanka Ringwald break; 818be32e7f1SMilanka Ringwald } 819be32e7f1SMilanka Ringwald if (connection->notifications_to_register != 0){ 820be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 821be32e7f1SMilanka Ringwald } 822be32e7f1SMilanka Ringwald break; 823be32e7f1SMilanka Ringwald } 824be32e7f1SMilanka Ringwald 825be32e7f1SMilanka Ringwald case AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES:{ 826be32e7f1SMilanka Ringwald uint8_t num_attributes = packet[pos++]; 827be32e7f1SMilanka Ringwald int i; 828be32e7f1SMilanka Ringwald struct item { 829be32e7f1SMilanka Ringwald uint16_t len; 830be32e7f1SMilanka Ringwald uint8_t * value; 831be32e7f1SMilanka Ringwald } items[AVRCP_MEDIA_ATTR_COUNT]; 832be32e7f1SMilanka Ringwald memset(items, 0, sizeof(items)); 833be32e7f1SMilanka Ringwald 834be32e7f1SMilanka Ringwald uint16_t string_attributes_len = 0; 835be32e7f1SMilanka Ringwald uint8_t num_string_attributes = 0; 836be32e7f1SMilanka Ringwald uint16_t total_event_payload_for_string_attributes = HCI_EVENT_PAYLOAD_SIZE-2; 837be32e7f1SMilanka Ringwald uint16_t max_string_attribute_value_len = 0; 8384bb88fd7SMatthias Ringwald if (ctype == AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE || ctype == AVRCP_CTYPE_RESPONSE_CHANGED_STABLE){ 839be32e7f1SMilanka Ringwald for (i = 0; i < num_attributes; i++){ 840be32e7f1SMilanka Ringwald avrcp_media_attribute_id_t attr_id = big_endian_read_32(packet, pos); 841be32e7f1SMilanka Ringwald pos += 4; 842be32e7f1SMilanka Ringwald // uint16_t character_set = big_endian_read_16(packet, pos); 843be32e7f1SMilanka Ringwald pos += 2; 844be32e7f1SMilanka Ringwald uint16_t attr_value_length = big_endian_read_16(packet, pos); 845be32e7f1SMilanka Ringwald pos += 2; 846be32e7f1SMilanka Ringwald 847be32e7f1SMilanka Ringwald // debug - to remove later 848be32e7f1SMilanka Ringwald uint8_t value[100]; 849be32e7f1SMilanka Ringwald uint16_t value_len = sizeof(value) <= attr_value_length? sizeof(value) - 1 : attr_value_length; 850be32e7f1SMilanka Ringwald memcpy(value, packet+pos, value_len); 851be32e7f1SMilanka Ringwald value[value_len] = 0; 852be32e7f1SMilanka Ringwald // printf("Now Playing Info %s: %s \n", attribute2str(attr_id), value); 853be32e7f1SMilanka Ringwald // end debug 854be32e7f1SMilanka Ringwald 855be32e7f1SMilanka Ringwald if ((attr_id >= 1) || (attr_id <= AVRCP_MEDIA_ATTR_COUNT)) { 856be32e7f1SMilanka Ringwald items[attr_id-1].len = attr_value_length; 857be32e7f1SMilanka Ringwald items[attr_id-1].value = &packet[pos]; 858be32e7f1SMilanka Ringwald switch (attr_id){ 859be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TITLE: 860be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ARTIST: 861be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ALBUM: 862be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_GENRE: 863be32e7f1SMilanka Ringwald num_string_attributes++; 864be32e7f1SMilanka Ringwald string_attributes_len += attr_value_length; 865be32e7f1SMilanka Ringwald if (max_string_attribute_value_len < attr_value_length){ 866be32e7f1SMilanka Ringwald max_string_attribute_value_len = attr_value_length; 867be32e7f1SMilanka Ringwald } 868be32e7f1SMilanka Ringwald break; 869be32e7f1SMilanka Ringwald default: 870be32e7f1SMilanka Ringwald break; 871be32e7f1SMilanka Ringwald } 872be32e7f1SMilanka Ringwald } 873be32e7f1SMilanka Ringwald pos += attr_value_length; 874be32e7f1SMilanka Ringwald } 8754bb88fd7SMatthias Ringwald } 8764bb88fd7SMatthias Ringwald 877be32e7f1SMilanka Ringwald // subtract space for fixed fields 878be32e7f1SMilanka Ringwald total_event_payload_for_string_attributes -= 14 + 4; // 4 for '\0' 879be32e7f1SMilanka Ringwald 880be32e7f1SMilanka Ringwald // @TODO optimize space by repeatedly decreasing max_string_attribute_value_len until it fits into buffer instead of crude divion 881be32e7f1SMilanka 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; 882be32e7f1SMilanka 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); 883be32e7f1SMilanka Ringwald 884be32e7f1SMilanka Ringwald const uint8_t attribute_order[] = { 885be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_TRACK, 886be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_TOTAL_TRACKS, 887be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_SONG_LENGTH, 888be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_TITLE, 889be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_ARTIST, 890be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_ALBUM, 891be32e7f1SMilanka Ringwald AVRCP_MEDIA_ATTR_GENRE 892be32e7f1SMilanka Ringwald }; 893be32e7f1SMilanka Ringwald 894be32e7f1SMilanka Ringwald uint8_t event[HCI_EVENT_BUFFER_SIZE]; 895be32e7f1SMilanka Ringwald event[0] = HCI_EVENT_AVRCP_META; 896be32e7f1SMilanka Ringwald pos = 2; 897be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_NOW_PLAYING_INFO; 89847ec446bSMatthias Ringwald little_endian_store_16(event, pos, connection->l2cap_signaling_cid); 899be32e7f1SMilanka Ringwald pos += 2; 900be32e7f1SMilanka Ringwald event[pos++] = ctype; 901be32e7f1SMilanka Ringwald for (i = 0; i < sizeof(attribute_order); i++){ 902be32e7f1SMilanka Ringwald avrcp_media_attribute_id_t attr_id = attribute_order[i]; 903be32e7f1SMilanka Ringwald uint16_t value_len = 0; 904be32e7f1SMilanka Ringwald switch (attr_id){ 905be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TITLE: 906be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ARTIST: 907be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_ALBUM: 908be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_GENRE: 909be32e7f1SMilanka Ringwald if (items[attr_id-1].value){ 910be32e7f1SMilanka Ringwald value_len = items[attr_id-1].len <= max_value_len ? items[attr_id-1].len : max_value_len; 911be32e7f1SMilanka Ringwald } 912be32e7f1SMilanka Ringwald event[pos++] = value_len + 1; 913be32e7f1SMilanka Ringwald if (value_len){ 914be32e7f1SMilanka Ringwald memcpy(event+pos, items[attr_id-1].value, value_len); 915be32e7f1SMilanka Ringwald pos += value_len; 916be32e7f1SMilanka Ringwald } 917be32e7f1SMilanka Ringwald event[pos++] = 0; 918be32e7f1SMilanka Ringwald break; 919be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_SONG_LENGTH: 920be32e7f1SMilanka Ringwald if (items[attr_id-1].value){ 921be32e7f1SMilanka Ringwald little_endian_store_32(event, pos, btstack_atoi((char *)items[attr_id-1].value)); 922be32e7f1SMilanka Ringwald } else { 923be32e7f1SMilanka Ringwald little_endian_store_32(event, pos, 0); 924be32e7f1SMilanka Ringwald } 925be32e7f1SMilanka Ringwald pos += 4; 926be32e7f1SMilanka Ringwald break; 927be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TRACK: 928be32e7f1SMilanka Ringwald case AVRCP_MEDIA_ATTR_TOTAL_TRACKS: 929be32e7f1SMilanka Ringwald if (items[attr_id-1].value){ 930be32e7f1SMilanka Ringwald event[pos++] = btstack_atoi((char *)items[attr_id-1].value); 931be32e7f1SMilanka Ringwald } else { 932be32e7f1SMilanka Ringwald event[pos++] = 0; 933be32e7f1SMilanka Ringwald } 934be32e7f1SMilanka Ringwald break; 935be32e7f1SMilanka Ringwald } 936be32e7f1SMilanka Ringwald } 937be32e7f1SMilanka Ringwald event[1] = pos - 2; 938be32e7f1SMilanka Ringwald // printf_hexdump(event, pos); 939be32e7f1SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, pos); 940be32e7f1SMilanka Ringwald break; 941be32e7f1SMilanka Ringwald } 942be32e7f1SMilanka Ringwald default: 943be32e7f1SMilanka Ringwald break; 944be32e7f1SMilanka Ringwald } 945be32e7f1SMilanka Ringwald break; 946be32e7f1SMilanka Ringwald case AVRCP_CMD_OPCODE_PASS_THROUGH:{ 947be32e7f1SMilanka Ringwald // 0x80 | connection->cmd_operands[0] 948be32e7f1SMilanka Ringwald uint8_t operation_id = packet[pos++]; 9499cc1f3ceSMilanka Ringwald switch (connection->state){ 9509cc1f3ceSMilanka Ringwald case AVCTP_W2_RECEIVE_PRESS_RESPONSE: 951*d1845da3SMilanka Ringwald if (connection->continuous_fast_forward_cmd){ 9529cc1f3ceSMilanka Ringwald connection->state = AVCTP_W4_STOP; 953*d1845da3SMilanka Ringwald } else { 9549cc1f3ceSMilanka Ringwald connection->state = AVCTP_W2_SEND_RELEASE_COMMAND; 9559cc1f3ceSMilanka Ringwald } 9569cc1f3ceSMilanka Ringwald break; 9579cc1f3ceSMilanka Ringwald case AVCTP_W2_RECEIVE_RESPONSE: 9589cc1f3ceSMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 9599cc1f3ceSMilanka Ringwald break; 9609cc1f3ceSMilanka Ringwald default: 9619cc1f3ceSMilanka Ringwald // check for notifications? move state transition down 962ac98fce4SMilanka Ringwald // log_info("AVRCP_CMD_OPCODE_PASS_THROUGH state %d\n", connection->state); 9639cc1f3ceSMilanka Ringwald break; 9649cc1f3ceSMilanka Ringwald } 965be32e7f1SMilanka Ringwald if (connection->state == AVCTP_W4_STOP){ 96647ec446bSMatthias Ringwald avrcp_emit_operation_status(avrcp_callback, AVRCP_SUBEVENT_OPERATION_START, connection->l2cap_signaling_cid, ctype, operation_id); 967be32e7f1SMilanka Ringwald } 968be32e7f1SMilanka Ringwald if (connection->state == AVCTP_CONNECTION_OPENED) { 969be32e7f1SMilanka Ringwald // RELEASE response 970be32e7f1SMilanka Ringwald operation_id = operation_id & 0x7F; 97147ec446bSMatthias Ringwald avrcp_emit_operation_status(avrcp_callback, AVRCP_SUBEVENT_OPERATION_COMPLETE, connection->l2cap_signaling_cid, ctype, operation_id); 972be32e7f1SMilanka Ringwald } 973be32e7f1SMilanka Ringwald if (connection->state == AVCTP_W2_SEND_RELEASE_COMMAND){ 974be32e7f1SMilanka Ringwald // PRESS response 975be32e7f1SMilanka Ringwald request_pass_through_release_control_cmd(connection); 976be32e7f1SMilanka Ringwald } 977be32e7f1SMilanka Ringwald break; 978be32e7f1SMilanka Ringwald } 979be32e7f1SMilanka Ringwald default: 980be32e7f1SMilanka Ringwald break; 981be32e7f1SMilanka Ringwald } 982be32e7f1SMilanka Ringwald } 983be32e7f1SMilanka Ringwald 984be32e7f1SMilanka Ringwald static void avrcp_handle_can_send_now(avrcp_connection_t * connection){ 985be32e7f1SMilanka Ringwald int i; 986be32e7f1SMilanka Ringwald switch (connection->state){ 987be32e7f1SMilanka Ringwald case AVCTP_W2_SEND_PRESS_COMMAND: 988be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_RECEIVE_PRESS_RESPONSE; 989b193c45eSMilanka Ringwald avrcp_send_cmd(connection->l2cap_signaling_cid, connection); 990be32e7f1SMilanka Ringwald break; 991be32e7f1SMilanka Ringwald case AVCTP_W2_SEND_COMMAND: 992be32e7f1SMilanka Ringwald case AVCTP_W2_SEND_RELEASE_COMMAND: 993be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_RECEIVE_RESPONSE; 994b193c45eSMilanka Ringwald avrcp_send_cmd(connection->l2cap_signaling_cid, connection); 995be32e7f1SMilanka Ringwald break; 996be32e7f1SMilanka Ringwald case AVCTP_CONNECTION_OPENED: 997be32e7f1SMilanka Ringwald if (connection->notifications_to_register != 0){ 998*d1845da3SMilanka Ringwald for (i = 1; i <= AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED; i++){ 999be32e7f1SMilanka Ringwald if (connection->notifications_to_register & (1<<i)){ 1000*d1845da3SMilanka Ringwald printf("clear registration bit before sending command"); 100173264fe8SMatthias Ringwald connection->notifications_to_register &= ~ (1 << i); 1002be32e7f1SMilanka Ringwald avrcp_prepare_notification(connection, i); 1003294335f1SMilanka Ringwald connection->state = AVCTP_W2_RECEIVE_RESPONSE; 1004be32e7f1SMilanka Ringwald avrcp_send_cmd(connection->l2cap_signaling_cid, connection); 1005be32e7f1SMilanka Ringwald return; 1006be32e7f1SMilanka Ringwald } 1007be32e7f1SMilanka Ringwald } 1008be32e7f1SMilanka Ringwald } 1009be32e7f1SMilanka Ringwald return; 1010be32e7f1SMilanka Ringwald default: 1011be32e7f1SMilanka Ringwald return; 1012be32e7f1SMilanka Ringwald } 1013be32e7f1SMilanka Ringwald } 1014be32e7f1SMilanka Ringwald 1015be32e7f1SMilanka Ringwald static avrcp_connection_t * avrcp_create_connection(bd_addr_t remote_addr){ 1016be32e7f1SMilanka Ringwald avrcp_connection_t * connection = btstack_memory_avrcp_connection_get(); 1017be32e7f1SMilanka Ringwald memset(connection, 0, sizeof(avrcp_connection_t)); 1018be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_IDLE; 1019be32e7f1SMilanka Ringwald connection->transaction_label = 0xFF; 1020be32e7f1SMilanka Ringwald memcpy(connection->remote_addr, remote_addr, 6); 1021be32e7f1SMilanka Ringwald btstack_linked_list_add(&avrcp_connections, (btstack_linked_item_t *) connection); 1022be32e7f1SMilanka Ringwald return connection; 1023be32e7f1SMilanka Ringwald } 1024be32e7f1SMilanka Ringwald 1025be32e7f1SMilanka Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 1026be32e7f1SMilanka Ringwald bd_addr_t event_addr; 1027be32e7f1SMilanka Ringwald uint16_t local_cid; 10280f76c2d7SMatthias Ringwald uint8_t status; 10296f43fcd7SMatthias Ringwald hci_con_handle_t con_handle; 1030be32e7f1SMilanka Ringwald avrcp_connection_t * connection = NULL; 1031be32e7f1SMilanka Ringwald 1032be32e7f1SMilanka Ringwald switch (packet_type) { 1033be32e7f1SMilanka Ringwald case L2CAP_DATA_PACKET: 1034be32e7f1SMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel); 1035be32e7f1SMilanka Ringwald if (!connection) break; 1036be32e7f1SMilanka Ringwald avrcp_handle_l2cap_data_packet_for_signaling_connection(connection, packet, size); 1037be32e7f1SMilanka Ringwald break; 1038be32e7f1SMilanka Ringwald 1039be32e7f1SMilanka Ringwald case HCI_EVENT_PACKET: 1040be32e7f1SMilanka Ringwald switch (hci_event_packet_get_type(packet)) { 1041be32e7f1SMilanka Ringwald case L2CAP_EVENT_INCOMING_CONNECTION: 1042be32e7f1SMilanka Ringwald l2cap_event_incoming_connection_get_address(packet, event_addr); 1043be32e7f1SMilanka Ringwald local_cid = l2cap_event_incoming_connection_get_local_cid(packet); 1044be32e7f1SMilanka Ringwald connection = avrcp_create_connection(event_addr); 10450f76c2d7SMatthias Ringwald if (!connection) { 10460f76c2d7SMatthias Ringwald log_error("Failed to alloc connection structure"); 10470f76c2d7SMatthias Ringwald l2cap_decline_connection(local_cid); 1048be32e7f1SMilanka Ringwald break; 1049be32e7f1SMilanka Ringwald } 10500f76c2d7SMatthias Ringwald connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED; 10510f76c2d7SMatthias Ringwald connection->l2cap_signaling_cid = local_cid; 10520f76c2d7SMatthias Ringwald l2cap_accept_connection(local_cid); 1053be32e7f1SMilanka Ringwald break; 1054be32e7f1SMilanka Ringwald 1055be32e7f1SMilanka Ringwald case L2CAP_EVENT_CHANNEL_OPENED: 1056be32e7f1SMilanka Ringwald l2cap_event_channel_opened_get_address(packet, event_addr); 1057be32e7f1SMilanka Ringwald 10580f76c2d7SMatthias Ringwald connection = get_avrcp_connection_for_bd_addr(event_addr); 10590f76c2d7SMatthias Ringwald status = l2cap_event_channel_opened_get_status(packet); 10600f76c2d7SMatthias Ringwald local_cid = l2cap_event_channel_opened_get_local_cid(packet); 10610f76c2d7SMatthias Ringwald 10620f76c2d7SMatthias Ringwald if (connection){ 10630f76c2d7SMatthias Ringwald // outgoing connection 1064be32e7f1SMilanka Ringwald if (l2cap_event_channel_opened_get_status(packet)){ 1065be32e7f1SMilanka Ringwald log_error("L2CAP connection to connection %s failed. status code 0x%02x", 1066be32e7f1SMilanka Ringwald bd_addr_to_str(event_addr), l2cap_event_channel_opened_get_status(packet)); 10670f76c2d7SMatthias Ringwald if (connection->state == AVCTP_CONNECTION_W4_L2CAP_CONNECTED){ 10680f76c2d7SMatthias Ringwald avrcp_emit_connection_established(avrcp_callback, status, event_addr, HCI_CON_HANDLE_INVALID, local_cid); 10690f76c2d7SMatthias Ringwald } 10700f76c2d7SMatthias Ringwald // free connection 10716f43fcd7SMatthias Ringwald btstack_linked_list_remove(&avrcp_connections, (btstack_linked_item_t*) connection); 10720f76c2d7SMatthias Ringwald btstack_memory_avrcp_connection_free(connection); 1073be32e7f1SMilanka Ringwald break; 1074be32e7f1SMilanka Ringwald } 10750f76c2d7SMatthias Ringwald } else { 10760f76c2d7SMatthias Ringwald // incoming connection 10770f76c2d7SMatthias Ringwald connection = avrcp_create_connection(event_addr); 10780f76c2d7SMatthias Ringwald if (!connection) { 10790f76c2d7SMatthias Ringwald log_error("Failed to alloc connection structure"); 10800f76c2d7SMatthias Ringwald l2cap_disconnect(local_cid, 0); // reason isn't used 1081b193c45eSMilanka Ringwald break; 1082b193c45eSMilanka Ringwald } 10830f76c2d7SMatthias Ringwald } 1084b193c45eSMilanka Ringwald 1085be32e7f1SMilanka Ringwald connection->l2cap_signaling_cid = local_cid; 10866f43fcd7SMatthias Ringwald con_handle = l2cap_event_channel_opened_get_handle(packet); 1087be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 10886f43fcd7SMatthias Ringwald avrcp_emit_connection_established(avrcp_callback, ERROR_CODE_SUCCESS, event_addr, con_handle, local_cid); 1089be32e7f1SMilanka Ringwald break; 1090be32e7f1SMilanka Ringwald 1091be32e7f1SMilanka Ringwald case L2CAP_EVENT_CAN_SEND_NOW: 1092be32e7f1SMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel); 1093be32e7f1SMilanka Ringwald if (!connection) break; 1094be32e7f1SMilanka Ringwald avrcp_handle_can_send_now(connection); 1095be32e7f1SMilanka Ringwald break; 1096be32e7f1SMilanka Ringwald 1097be32e7f1SMilanka Ringwald case L2CAP_EVENT_CHANNEL_CLOSED: 1098be32e7f1SMilanka Ringwald // data: event (8), len(8), channel (16) 1099be32e7f1SMilanka Ringwald local_cid = l2cap_event_channel_closed_get_local_cid(packet); 1100be32e7f1SMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(local_cid); 1101be32e7f1SMilanka Ringwald if (connection){ 110247ec446bSMatthias Ringwald avrcp_emit_connection_closed(avrcp_callback, connection->l2cap_signaling_cid); 11036f43fcd7SMatthias Ringwald // free connection 1104be32e7f1SMilanka Ringwald btstack_linked_list_remove(&avrcp_connections, (btstack_linked_item_t*) connection); 11056f43fcd7SMatthias Ringwald btstack_memory_avrcp_connection_free(connection); 1106be32e7f1SMilanka Ringwald break; 1107be32e7f1SMilanka Ringwald } 1108be32e7f1SMilanka Ringwald break; 1109be32e7f1SMilanka Ringwald default: 1110be32e7f1SMilanka Ringwald break; 1111be32e7f1SMilanka Ringwald } 1112be32e7f1SMilanka Ringwald break; 1113be32e7f1SMilanka Ringwald default: 1114be32e7f1SMilanka Ringwald break; 1115be32e7f1SMilanka Ringwald } 1116be32e7f1SMilanka Ringwald } 1117be32e7f1SMilanka Ringwald 1118be32e7f1SMilanka Ringwald void avrcp_init(void){ 1119be32e7f1SMilanka Ringwald avrcp_connections = NULL; 1120235946f1SMatthias Ringwald l2cap_register_service(&packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_0); 1121be32e7f1SMilanka Ringwald } 1122be32e7f1SMilanka Ringwald 1123be32e7f1SMilanka Ringwald void avrcp_register_packet_handler(btstack_packet_handler_t callback){ 1124be32e7f1SMilanka Ringwald if (callback == NULL){ 1125be32e7f1SMilanka Ringwald log_error("avrcp_register_packet_handler called with NULL callback"); 1126be32e7f1SMilanka Ringwald return; 1127be32e7f1SMilanka Ringwald } 1128be32e7f1SMilanka Ringwald avrcp_callback = callback; 1129be32e7f1SMilanka Ringwald } 1130be32e7f1SMilanka Ringwald 1131b193c45eSMilanka Ringwald uint8_t avrcp_connect(bd_addr_t bd_addr, uint16_t * avrcp_cid){ 1132be32e7f1SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_bd_addr(bd_addr); 11338a2c6c7cSMilanka Ringwald if (connection){ 11348a2c6c7cSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 11358a2c6c7cSMilanka Ringwald } 11368a2c6c7cSMilanka Ringwald 1137be32e7f1SMilanka Ringwald connection = avrcp_create_connection(bd_addr); 1138be32e7f1SMilanka Ringwald if (!connection){ 11398a2c6c7cSMilanka Ringwald log_error("avrcp: could not allocate connection struct."); 11408a2c6c7cSMilanka Ringwald return BTSTACK_MEMORY_ALLOC_FAILED; 1141be32e7f1SMilanka Ringwald } 11428a2c6c7cSMilanka Ringwald 1143be32e7f1SMilanka Ringwald connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED; 1144b193c45eSMilanka Ringwald l2cap_create_channel(packet_handler, connection->remote_addr, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, &connection->l2cap_signaling_cid); 1145b193c45eSMilanka Ringwald if (avrcp_cid) { 1146b193c45eSMilanka Ringwald *avrcp_cid = connection->l2cap_signaling_cid; 1147b193c45eSMilanka Ringwald } 11488a2c6c7cSMilanka Ringwald return ERROR_CODE_SUCCESS; 1149be32e7f1SMilanka Ringwald } 1150be32e7f1SMilanka Ringwald 115131a5a745SMatthias Ringwald uint8_t avrcp_unit_info(uint16_t avrcp_cid){ 1152b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 1153be32e7f1SMilanka Ringwald if (!connection){ 1154be32e7f1SMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 115531a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1156be32e7f1SMilanka Ringwald } 115731a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1158be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1159be32e7f1SMilanka Ringwald 1160be32e7f1SMilanka Ringwald connection->transaction_label++; 11619cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_UNIT_INFO; 1162be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1163be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique 1164be32e7f1SMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE; 1165adcdded9SMilanka Ringwald memset(connection->cmd_operands, 0xFF, connection->cmd_operands_length); 1166adcdded9SMilanka Ringwald connection->cmd_operands_length = 5; 1167be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 116831a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1169be32e7f1SMilanka Ringwald } 1170be32e7f1SMilanka Ringwald 117131a5a745SMatthias Ringwald static uint8_t avrcp_get_capabilities(uint16_t avrcp_cid, uint8_t capability_id){ 1172b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 1173be32e7f1SMilanka Ringwald if (!connection){ 1174be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 117531a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1176be32e7f1SMilanka Ringwald } 117731a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1178be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1179be32e7f1SMilanka Ringwald 1180be32e7f1SMilanka Ringwald connection->transaction_label++; 11819cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1182be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1183be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1184be32e7f1SMilanka Ringwald connection->subunit_id = 0; 1185be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); 1186be32e7f1SMilanka Ringwald connection->cmd_operands[3] = AVRCP_PDU_ID_GET_CAPABILITIES; // PDU ID 1187be32e7f1SMilanka Ringwald connection->cmd_operands[4] = 0; 1188be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, 5, 1); // parameter length 1189be32e7f1SMilanka Ringwald connection->cmd_operands[7] = capability_id; // capability ID 1190adcdded9SMilanka Ringwald connection->cmd_operands_length = 8; 1191be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 119231a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1193be32e7f1SMilanka Ringwald } 1194be32e7f1SMilanka Ringwald 119531a5a745SMatthias Ringwald uint8_t avrcp_get_supported_company_ids(uint16_t avrcp_cid){ 119631a5a745SMatthias Ringwald return avrcp_get_capabilities(avrcp_cid, AVRCP_CAPABILITY_ID_COMPANY); 1197be32e7f1SMilanka Ringwald } 1198be32e7f1SMilanka Ringwald 119931a5a745SMatthias Ringwald uint8_t avrcp_get_supported_events(uint16_t avrcp_cid){ 120031a5a745SMatthias Ringwald return avrcp_get_capabilities(avrcp_cid, AVRCP_CAPABILITY_ID_EVENT); 1201be32e7f1SMilanka Ringwald } 1202be32e7f1SMilanka Ringwald 1203be32e7f1SMilanka Ringwald 120431a5a745SMatthias Ringwald uint8_t avrcp_play(uint16_t avrcp_cid){ 1205*d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_PLAY, 0); 1206be32e7f1SMilanka Ringwald } 1207be32e7f1SMilanka Ringwald 120831a5a745SMatthias Ringwald uint8_t avrcp_stop(uint16_t avrcp_cid){ 1209*d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_STOP, 0); 1210be32e7f1SMilanka Ringwald } 1211be32e7f1SMilanka Ringwald 121231a5a745SMatthias Ringwald uint8_t avrcp_pause(uint16_t avrcp_cid){ 1213*d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_PAUSE, 0); 1214be32e7f1SMilanka Ringwald } 1215be32e7f1SMilanka Ringwald 121631a5a745SMatthias Ringwald uint8_t avrcp_forward(uint16_t avrcp_cid){ 1217*d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FORWARD, 0); 1218be32e7f1SMilanka Ringwald } 1219be32e7f1SMilanka Ringwald 122031a5a745SMatthias Ringwald uint8_t avrcp_backward(uint16_t avrcp_cid){ 1221*d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_BACKWARD, 0); 1222be32e7f1SMilanka Ringwald } 1223be32e7f1SMilanka Ringwald 122431a5a745SMatthias Ringwald uint8_t avrcp_start_rewind(uint16_t avrcp_cid){ 1225*d1845da3SMilanka Ringwald return request_continuous_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_REWIND, 0); 1226be32e7f1SMilanka Ringwald } 1227be32e7f1SMilanka Ringwald 122831a5a745SMatthias Ringwald uint8_t avrcp_volume_up(uint16_t avrcp_cid){ 1229*d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_VOLUME_UP, 0); 1230be32e7f1SMilanka Ringwald } 1231be32e7f1SMilanka Ringwald 123231a5a745SMatthias Ringwald uint8_t avrcp_volume_down(uint16_t avrcp_cid){ 1233*d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_VOLUME_DOWN, 0); 1234be32e7f1SMilanka Ringwald } 1235be32e7f1SMilanka Ringwald 123631a5a745SMatthias Ringwald uint8_t avrcp_mute(uint16_t avrcp_cid){ 1237*d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_MUTE, 0); 1238be32e7f1SMilanka Ringwald } 1239be32e7f1SMilanka Ringwald 124031a5a745SMatthias Ringwald uint8_t avrcp_skip(uint16_t avrcp_cid){ 1241*d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_SKIP, 0); 1242be32e7f1SMilanka Ringwald } 1243be32e7f1SMilanka Ringwald 124431a5a745SMatthias Ringwald uint8_t avrcp_stop_rewind(uint16_t avrcp_cid){ 1245b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 1246be32e7f1SMilanka Ringwald if (!connection){ 1247be32e7f1SMilanka Ringwald log_error("avrcp_stop_rewind: could not find a connection."); 124831a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1249be32e7f1SMilanka Ringwald } 125031a5a745SMatthias Ringwald if (connection->state != AVCTP_W4_STOP) return ERROR_CODE_COMMAND_DISALLOWED; 125131a5a745SMatthias Ringwald return request_pass_through_release_control_cmd(connection); 1252be32e7f1SMilanka Ringwald } 1253be32e7f1SMilanka Ringwald 125431a5a745SMatthias Ringwald uint8_t avrcp_start_fast_forward(uint16_t avrcp_cid){ 1255*d1845da3SMilanka Ringwald return request_continuous_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FAST_FORWARD, 0); 1256be32e7f1SMilanka Ringwald } 1257be32e7f1SMilanka Ringwald 1258*d1845da3SMilanka Ringwald uint8_t avrcp_fast_forward(uint16_t avrcp_cid){ 1259*d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FAST_FORWARD, 0); 1260*d1845da3SMilanka Ringwald } 1261*d1845da3SMilanka Ringwald 1262*d1845da3SMilanka Ringwald uint8_t avrcp_rewind(uint16_t avrcp_cid){ 1263*d1845da3SMilanka Ringwald return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_REWIND, 0); 1264*d1845da3SMilanka Ringwald } 1265*d1845da3SMilanka Ringwald 1266*d1845da3SMilanka Ringwald 126731a5a745SMatthias Ringwald uint8_t avrcp_stop_fast_forward(uint16_t avrcp_cid){ 1268b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 1269be32e7f1SMilanka Ringwald if (!connection){ 1270be32e7f1SMilanka Ringwald log_error("avrcp_stop_fast_forward: could not find a connection."); 127131a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1272be32e7f1SMilanka Ringwald } 127331a5a745SMatthias Ringwald if (connection->state != AVCTP_W4_STOP) return ERROR_CODE_COMMAND_DISALLOWED; 127431a5a745SMatthias Ringwald return request_pass_through_release_control_cmd(connection); 1275be32e7f1SMilanka Ringwald } 1276be32e7f1SMilanka Ringwald 127731a5a745SMatthias Ringwald uint8_t avrcp_get_play_status(uint16_t avrcp_cid){ 1278b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 1279be32e7f1SMilanka Ringwald if (!connection){ 1280be32e7f1SMilanka Ringwald log_error("avrcp_get_play_status: could not find a connection."); 128131a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1282be32e7f1SMilanka Ringwald } 128331a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1284be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1285be32e7f1SMilanka Ringwald 1286be32e7f1SMilanka Ringwald connection->transaction_label++; 12879cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1288be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1289be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1290be32e7f1SMilanka Ringwald connection->subunit_id = 0; 1291be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); 1292be32e7f1SMilanka Ringwald connection->cmd_operands[3] = AVRCP_PDU_ID_GET_PLAY_STATUS; 1293be32e7f1SMilanka Ringwald connection->cmd_operands[4] = 0; // reserved(upper 6) | packet_type -> 0 1294be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, 5, 0); // parameter length 1295adcdded9SMilanka Ringwald connection->cmd_operands_length = 7; 1296be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 129731a5a745SMatthias Ringwald 129831a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1299be32e7f1SMilanka Ringwald } 1300be32e7f1SMilanka Ringwald 130131a5a745SMatthias Ringwald uint8_t avrcp_enable_notification(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id){ 1302b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 1303be32e7f1SMilanka Ringwald if (!connection){ 1304be32e7f1SMilanka Ringwald log_error("avrcp_get_play_status: could not find a connection."); 130531a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1306be32e7f1SMilanka Ringwald } 1307be32e7f1SMilanka Ringwald avrcp_register_notification(connection, event_id); 130831a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1309be32e7f1SMilanka Ringwald } 1310be32e7f1SMilanka Ringwald 131131a5a745SMatthias Ringwald uint8_t avrcp_disable_notification(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id){ 1312b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 1313be32e7f1SMilanka Ringwald if (!connection){ 1314be32e7f1SMilanka Ringwald log_error("avrcp_get_play_status: could not find a connection."); 131531a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1316be32e7f1SMilanka Ringwald } 1317be32e7f1SMilanka Ringwald connection->notifications_to_deregister |= (1 << event_id); 131831a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1319be32e7f1SMilanka Ringwald } 1320be32e7f1SMilanka Ringwald 132131a5a745SMatthias Ringwald uint8_t avrcp_get_now_playing_info(uint16_t avrcp_cid){ 1322b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 1323be32e7f1SMilanka Ringwald if (!connection){ 1324be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 132531a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1326be32e7f1SMilanka Ringwald } 132731a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1328be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1329be32e7f1SMilanka Ringwald 1330be32e7f1SMilanka Ringwald connection->transaction_label++; 13319cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1332be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1333be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1334be32e7f1SMilanka Ringwald connection->subunit_id = 0; 1335be32e7f1SMilanka Ringwald int pos = 0; 1336be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 1337be32e7f1SMilanka Ringwald pos += 3; 1338be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES; // PDU ID 1339be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; 1340be32e7f1SMilanka Ringwald 1341be32e7f1SMilanka Ringwald // Parameter Length 1342be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 9); 1343be32e7f1SMilanka Ringwald pos += 2; 1344be32e7f1SMilanka Ringwald 1345be32e7f1SMilanka Ringwald // write 8 bytes value 1346be32e7f1SMilanka Ringwald memset(connection->cmd_operands + pos, 0, 8); // identifier: PLAYING 1347be32e7f1SMilanka Ringwald pos += 8; 1348be32e7f1SMilanka Ringwald 1349be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; // attribute count, if 0 get all attributes 1350be32e7f1SMilanka Ringwald // every attribute is 4 bytes long 1351be32e7f1SMilanka Ringwald 1352adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 1353be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 135431a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1355be32e7f1SMilanka Ringwald } 1356be32e7f1SMilanka Ringwald 135731a5a745SMatthias Ringwald uint8_t avrcp_set_absolute_volume(uint16_t avrcp_cid, uint8_t volume){ 1358b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 1359be32e7f1SMilanka Ringwald if (!connection){ 1360be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 136131a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1362be32e7f1SMilanka Ringwald } 136331a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1364be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1365be32e7f1SMilanka Ringwald 1366be32e7f1SMilanka Ringwald connection->transaction_label++; 13679cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1368be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_CONTROL; 1369be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1370be32e7f1SMilanka Ringwald connection->subunit_id = 0; 1371be32e7f1SMilanka Ringwald int pos = 0; 1372be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 1373be32e7f1SMilanka Ringwald pos += 3; 1374be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME; // PDU ID 1375be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; 1376be32e7f1SMilanka Ringwald 1377be32e7f1SMilanka Ringwald // Parameter Length 1378be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 1); 1379be32e7f1SMilanka Ringwald pos += 2; 1380be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = volume; 1381be32e7f1SMilanka Ringwald 1382adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 1383be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 138431a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1385be32e7f1SMilanka Ringwald } 1386be32e7f1SMilanka Ringwald 138731a5a745SMatthias Ringwald uint8_t avrcp_query_shuffle_and_repeat_modes(uint16_t avrcp_cid){ 1388b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 1389be32e7f1SMilanka Ringwald if (!connection){ 1390be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 139131a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1392be32e7f1SMilanka Ringwald } 139331a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1394be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1395be32e7f1SMilanka Ringwald 1396be32e7f1SMilanka Ringwald connection->transaction_label++; 13979cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1398be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_STATUS; 1399be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1400be32e7f1SMilanka Ringwald connection->subunit_id = 0; 1401be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); 1402be32e7f1SMilanka Ringwald connection->cmd_operands[3] = AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue; // PDU ID 1403be32e7f1SMilanka Ringwald connection->cmd_operands[4] = 0; 1404be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, 5, 5); // parameter length 1405be32e7f1SMilanka Ringwald connection->cmd_operands[7] = 4; // NumPlayerApplicationSettingAttributeID 1406be32e7f1SMilanka Ringwald // PlayerApplicationSettingAttributeID1 AVRCP Spec, Appendix F, 133 1407be32e7f1SMilanka Ringwald connection->cmd_operands[8] = 0x01; // equalizer (1-OFF, 2-ON) 1408be32e7f1SMilanka Ringwald connection->cmd_operands[9] = 0x02; // repeat (1-off, 2-single track, 3-all tracks, 4-group repeat) 1409be32e7f1SMilanka Ringwald connection->cmd_operands[10] = 0x03; // shuffle (1-off, 2-all tracks, 3-group shuffle) 1410be32e7f1SMilanka Ringwald connection->cmd_operands[11] = 0x04; // scan (1-off, 2-all tracks, 3-group scan) 1411adcdded9SMilanka Ringwald connection->cmd_operands_length = 12; 1412be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 141331a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1414be32e7f1SMilanka Ringwald } 1415be32e7f1SMilanka Ringwald 141631a5a745SMatthias Ringwald static uint8_t avrcp_set_current_player_application_setting_value(uint16_t avrcp_cid, uint8_t attribute_id, uint8_t attribute_value){ 1417b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 1418be32e7f1SMilanka Ringwald if (!connection){ 1419be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 142031a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1421be32e7f1SMilanka Ringwald } 142231a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1423be32e7f1SMilanka Ringwald connection->state = AVCTP_W2_SEND_COMMAND; 1424be32e7f1SMilanka Ringwald 1425be32e7f1SMilanka Ringwald connection->transaction_label++; 14269cc1f3ceSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 1427be32e7f1SMilanka Ringwald connection->command_type = AVRCP_CTYPE_CONTROL; 1428be32e7f1SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 1429be32e7f1SMilanka Ringwald connection->subunit_id = 0; 1430be32e7f1SMilanka Ringwald int pos = 0; 1431be32e7f1SMilanka Ringwald big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); 1432be32e7f1SMilanka Ringwald pos += 3; 1433be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_SetPlayerApplicationSettingValue; // PDU ID 1434be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 0; 1435be32e7f1SMilanka Ringwald // Parameter Length 1436be32e7f1SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 3); 1437be32e7f1SMilanka Ringwald pos += 2; 1438be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = 2; 1439adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 1440be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = attribute_id; 1441be32e7f1SMilanka Ringwald connection->cmd_operands[pos++] = attribute_value; 1442adcdded9SMilanka Ringwald connection->cmd_operands_length = pos; 1443be32e7f1SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 144431a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1445be32e7f1SMilanka Ringwald } 1446be32e7f1SMilanka Ringwald 144731a5a745SMatthias Ringwald uint8_t avrcp_set_shuffle_mode(uint16_t avrcp_cid, avrcp_shuffle_mode_t mode){ 144831a5a745SMatthias Ringwald if (mode < AVRCP_SHUFFLE_MODE_OFF || mode > AVRCP_SHUFFLE_MODE_GROUP) return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 144931a5a745SMatthias Ringwald return avrcp_set_current_player_application_setting_value(avrcp_cid, 0x03, mode); 1450be32e7f1SMilanka Ringwald } 1451be32e7f1SMilanka Ringwald 145231a5a745SMatthias Ringwald uint8_t avrcp_set_repeat_mode(uint16_t avrcp_cid, avrcp_repeat_mode_t mode){ 145331a5a745SMatthias Ringwald if (mode < AVRCP_REPEAT_MODE_OFF || mode > AVRCP_REPEAT_MODE_GROUP) return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 145431a5a745SMatthias Ringwald return avrcp_set_current_player_application_setting_value(avrcp_cid, 0x02, mode); 1455be32e7f1SMilanka Ringwald } 1456be32e7f1SMilanka Ringwald 145731a5a745SMatthias Ringwald uint8_t avrcp_disconnect(uint16_t avrcp_cid){ 1458b193c45eSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_l2cap_signaling_cid(avrcp_cid); 1459be32e7f1SMilanka Ringwald if (!connection){ 1460be32e7f1SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 146131a5a745SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1462be32e7f1SMilanka Ringwald } 146331a5a745SMatthias Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1464294335f1SMilanka Ringwald l2cap_disconnect(connection->l2cap_signaling_cid, 0); 146531a5a745SMatthias Ringwald return ERROR_CODE_SUCCESS; 1466be32e7f1SMilanka Ringwald }