1462aa085SMilanka Ringwald /* 2462aa085SMilanka Ringwald * Copyright (C) 2016 BlueKitchen GmbH 3462aa085SMilanka Ringwald * 4462aa085SMilanka Ringwald * Redistribution and use in source and binary forms, with or without 5462aa085SMilanka Ringwald * modification, are permitted provided that the following conditions 6462aa085SMilanka Ringwald * are met: 7462aa085SMilanka Ringwald * 8462aa085SMilanka Ringwald * 1. Redistributions of source code must retain the above copyright 9462aa085SMilanka Ringwald * notice, this list of conditions and the following disclaimer. 10462aa085SMilanka Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11462aa085SMilanka Ringwald * notice, this list of conditions and the following disclaimer in the 12462aa085SMilanka Ringwald * documentation and/or other materials provided with the distribution. 13462aa085SMilanka Ringwald * 3. Neither the name of the copyright holders nor the names of 14462aa085SMilanka Ringwald * contributors may be used to endorse or promote products derived 15462aa085SMilanka Ringwald * from this software without specific prior written permission. 16462aa085SMilanka Ringwald * 4. Any redistribution, use, or modification is done solely for 17462aa085SMilanka Ringwald * personal benefit and not for any commercial purpose or for 18462aa085SMilanka Ringwald * monetary gain. 19462aa085SMilanka Ringwald * 20462aa085SMilanka Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21462aa085SMilanka Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22462aa085SMilanka Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23462aa085SMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24462aa085SMilanka Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25462aa085SMilanka Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26462aa085SMilanka Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27462aa085SMilanka Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28462aa085SMilanka Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29462aa085SMilanka Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30462aa085SMilanka Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31462aa085SMilanka Ringwald * SUCH DAMAGE. 32462aa085SMilanka Ringwald * 33462aa085SMilanka Ringwald * Please inquire about commercial licensing options at 34462aa085SMilanka Ringwald * [email protected] 35462aa085SMilanka Ringwald * 36462aa085SMilanka Ringwald */ 37462aa085SMilanka Ringwald 38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "avrcp_browsing_client.c" 39462aa085SMilanka Ringwald 40462aa085SMilanka Ringwald /* 41462aa085SMilanka Ringwald * avrcp_browsing_client.c 42462aa085SMilanka Ringwald */ 43462aa085SMilanka Ringwald 44462aa085SMilanka Ringwald // ***************************************************************************** 45462aa085SMilanka Ringwald /* EXAMPLE_START(avrcp_browsing_client): Browse media players and media information on a remote device. 46462aa085SMilanka Ringwald * 47462aa085SMilanka Ringwald * @text This example demonstrates how to use the AVRCP Controller Browsing service to 48462aa085SMilanka Ringwald * browse madia players and media information on a remote AVRCP Source device. 49462aa085SMilanka Ringwald * 50462aa085SMilanka Ringwald * @test To test with a remote device, e.g. a mobile phone, 51462aa085SMilanka Ringwald * pair from the remote device with the demo, then use the UI for browsing (tap 52462aa085SMilanka Ringwald * SPACE on the console to show the available AVRCP commands). 53462aa085SMilanka Ringwald * 54462aa085SMilanka Ringwald */ 55462aa085SMilanka Ringwald // ***************************************************************************** 56462aa085SMilanka Ringwald 57462aa085SMilanka Ringwald #include <stdint.h> 58954cc391SMilanka Ringwald #include <inttypes.h> 59462aa085SMilanka Ringwald #include <stdio.h> 60462aa085SMilanka Ringwald #include <stdlib.h> 61462aa085SMilanka Ringwald #include <string.h> 62462aa085SMilanka Ringwald 63462aa085SMilanka Ringwald #include "btstack.h" 64462aa085SMilanka Ringwald 65462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN 66462aa085SMilanka Ringwald #include "btstack_stdin.h" 67462aa085SMilanka Ringwald #endif 68462aa085SMilanka Ringwald 694f0111ebSMilanka Ringwald #define AVRCP_BROWSING_ENABLED 704f0111ebSMilanka Ringwald 71db79553aSMilanka Ringwald #define AVRCP_BROWSING_MAX_PLAYERS 10 72db79553aSMilanka Ringwald #define AVRCP_BROWSING_MAX_FOLDERS 10 73db79553aSMilanka Ringwald #define AVRCP_BROWSING_MAX_FOLDER_NAME_LEN 30 74db79553aSMilanka Ringwald 75462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN 76462aa085SMilanka Ringwald // mac 2011: static bd_addr_t remote = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3}; 77462aa085SMilanka Ringwald // pts: static bd_addr_t remote = {0x00, 0x1B, 0xDC, 0x08, 0x0A, 0xA5}; 78462aa085SMilanka Ringwald // mac 2013: 79462aa085SMilanka Ringwald // static const char * device_addr_string = "84:38:35:65:d1:15"; 80462aa085SMilanka Ringwald // iPhone 5S: static const char * device_addr_string = "54:E4:3A:26:A2:39"; 81462aa085SMilanka Ringwald // phone 2013: 82143efbe8SMilanka Ringwald // static const char * device_addr_string = "B0:34:95:CB:97:C4"; 8302d2f9caSMilanka Ringwald // iPod 840eebc132SMilanka Ringwald // static const char * device_addr_string = "B0:34:95:CB:97:C4"; 850eebc132SMilanka Ringwald // iPhone 860eebc132SMilanka Ringwald static const char * device_addr_string = "6C:72:E7:10:22:EE"; 87143efbe8SMilanka Ringwald 88462aa085SMilanka Ringwald static bd_addr_t device_addr; 89462aa085SMilanka Ringwald #endif 90462aa085SMilanka Ringwald 91462aa085SMilanka Ringwald static uint16_t avrcp_cid = 0; 92ace5ab2aSMilanka Ringwald static bool avrcp_connected = false; 93462aa085SMilanka Ringwald 94462aa085SMilanka Ringwald static uint16_t browsing_cid = 0; 95462aa085SMilanka Ringwald static uint8_t avrcp_browsing_connected = 0; 96462aa085SMilanka Ringwald static uint8_t sdp_avrcp_browsing_controller_service_buffer[200]; 97462aa085SMilanka Ringwald 98ace5ab2aSMilanka Ringwald static bool browsing_query_active = false; 99954cc391SMilanka Ringwald static avrcp_media_item_context_t media_item_context; 100954cc391SMilanka Ringwald 101db79553aSMilanka Ringwald typedef struct { 102db79553aSMilanka Ringwald uint16_t charset; 103db79553aSMilanka Ringwald uint8_t depth; 104db79553aSMilanka Ringwald uint16_t name_len; 105db79553aSMilanka Ringwald char name[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN]; 106db79553aSMilanka Ringwald } avrcp_browsing_root_folder_t; 107db79553aSMilanka Ringwald 108db79553aSMilanka Ringwald typedef struct { 109db79553aSMilanka Ringwald uint8_t uid[8]; 110db79553aSMilanka Ringwald uint16_t name_len; 111db79553aSMilanka Ringwald char name[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN]; 112db79553aSMilanka Ringwald } avrcp_browsing_folders_t; 113db79553aSMilanka Ringwald 114db79553aSMilanka Ringwald static uint8_t parent_folder_set = 0; 115db79553aSMilanka Ringwald static uint8_t parent_folder_uid[8]; 116db79553aSMilanka Ringwald static char parent_folder_name[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN]; 117db79553aSMilanka Ringwald static avrcp_browsing_folders_t folders[AVRCP_BROWSING_MAX_FOLDERS]; 118db79553aSMilanka Ringwald static int folder_index = -1; 119db79553aSMilanka Ringwald static uint16_t players[AVRCP_BROWSING_MAX_PLAYERS]; 120db79553aSMilanka Ringwald static int player_index = -1; 121143efbe8SMilanka Ringwald static uint16_t browsing_uid_counter = 0; 122db79553aSMilanka Ringwald 123462aa085SMilanka Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration; 124462aa085SMilanka Ringwald 125462aa085SMilanka Ringwald static uint8_t ertm_buffer[10000]; 126462aa085SMilanka Ringwald static l2cap_ertm_config_t ertm_config = { 127462aa085SMilanka Ringwald 1, // ertm mandatory 128462aa085SMilanka Ringwald 2, // max transmit, some tests require > 1 129462aa085SMilanka Ringwald 2000, 130462aa085SMilanka Ringwald 12000, 131462aa085SMilanka Ringwald 144, // l2cap ertm mtu 132462aa085SMilanka Ringwald 4, 133462aa085SMilanka Ringwald 4, 134c425ea4aSMatthias Ringwald 0, // No FCS 135462aa085SMilanka Ringwald }; 136462aa085SMilanka Ringwald 137db79553aSMilanka Ringwald static inline int next_index(int * index, int max_value){ 138db79553aSMilanka Ringwald if ((*index) < max_value){ 139db79553aSMilanka Ringwald (*index)++; 140db79553aSMilanka Ringwald } else { 141db79553aSMilanka Ringwald (*index) = 0; 142db79553aSMilanka Ringwald } 143db79553aSMilanka Ringwald return (*index); 144db79553aSMilanka Ringwald } 145db79553aSMilanka Ringwald 1463392790eSMatthias Ringwald static int next_folder_index(void){ 147db79553aSMilanka Ringwald return next_index(&folder_index, AVRCP_BROWSING_MAX_FOLDERS); 148db79553aSMilanka Ringwald } 149db79553aSMilanka Ringwald 1503392790eSMatthias Ringwald static int next_player_index(void){ 151db79553aSMilanka Ringwald return next_index(&player_index, AVRCP_BROWSING_MAX_PLAYERS); 152db79553aSMilanka Ringwald } 153db79553aSMilanka Ringwald 154db79553aSMilanka Ringwald 155462aa085SMilanka Ringwald /* @section Main Application Setup 156462aa085SMilanka Ringwald * 157462aa085SMilanka Ringwald * @text The Listing MainConfiguration shows how to setup AVRCP Controller Browsing service. 158462aa085SMilanka Ringwald * To announce AVRCP Controller Browsing service, you need to create corresponding 159462aa085SMilanka Ringwald * SDP record and register it with the SDP service. 160462aa085SMilanka Ringwald * You'll also need to register several packet handlers: 161462aa085SMilanka Ringwald * - stdin_process callback - used to trigger AVRCP commands, such are get media players, playlists, albums, etc. Requires HAVE_BTSTACK_STDIN. 1620eebc132SMilanka Ringwald * - avrcp_browsing_packet_handler - used to receive answers for AVRCP commands. 163462aa085SMilanka Ringwald * 164462aa085SMilanka Ringwald */ 165462aa085SMilanka Ringwald 166462aa085SMilanka Ringwald /* LISTING_START(MainConfiguration): Setup Audio Sink and AVRCP Controller services */ 1670eebc132SMilanka Ringwald static void avrcp_browsing_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 16802d2f9caSMilanka Ringwald static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 16902d2f9caSMilanka Ringwald 170462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN 171462aa085SMilanka Ringwald static void stdin_process(char cmd); 172462aa085SMilanka Ringwald #endif 173462aa085SMilanka Ringwald 174462aa085SMilanka Ringwald 175462aa085SMilanka Ringwald int btstack_main(int argc, const char * argv[]); 176462aa085SMilanka Ringwald int btstack_main(int argc, const char * argv[]){ 177462aa085SMilanka Ringwald (void)argc; 178462aa085SMilanka Ringwald (void)argv; 179462aa085SMilanka Ringwald 180462aa085SMilanka Ringwald // Initialize L2CAP. 181462aa085SMilanka Ringwald l2cap_init(); 182462aa085SMilanka Ringwald 18319691e0aSMilanka Ringwald // Initialize AVRCP service. 18419691e0aSMilanka Ringwald avrcp_init(); 18502d2f9caSMilanka Ringwald // Initialize AVRCP Controller & Target Service. 186462aa085SMilanka Ringwald avrcp_controller_init(); 18702d2f9caSMilanka Ringwald avrcp_target_init(); 18802d2f9caSMilanka Ringwald 18902d2f9caSMilanka Ringwald avrcp_register_packet_handler(&avrcp_packet_handler); 19002d2f9caSMilanka Ringwald avrcp_controller_register_packet_handler(&avrcp_packet_handler); 19102d2f9caSMilanka Ringwald avrcp_target_register_packet_handler(&avrcp_packet_handler); 192462aa085SMilanka Ringwald 1930eebc132SMilanka Ringwald // Initialize AVRCP Browsing Service. 1940eebc132SMilanka Ringwald avrcp_browsing_init(); 195462aa085SMilanka Ringwald avrcp_browsing_controller_init(); 1960eebc132SMilanka Ringwald avrcp_browsing_target_init(); 1970eebc132SMilanka Ringwald 1980eebc132SMilanka Ringwald // Register for HCI events. 1990eebc132SMilanka Ringwald avrcp_browsing_controller_register_packet_handler(&avrcp_browsing_packet_handler); 2000eebc132SMilanka Ringwald avrcp_browsing_target_register_packet_handler(&avrcp_browsing_packet_handler); 2010eebc132SMilanka Ringwald avrcp_browsing_register_packet_handler(&avrcp_browsing_packet_handler); 202462aa085SMilanka Ringwald 203462aa085SMilanka Ringwald // Initialize SDP. 204462aa085SMilanka Ringwald sdp_init(); 205462aa085SMilanka Ringwald 206462aa085SMilanka Ringwald // Create AVRCP service record and register it with SDP. 207462aa085SMilanka Ringwald memset(sdp_avrcp_browsing_controller_service_buffer, 0, sizeof(sdp_avrcp_browsing_controller_service_buffer)); 20811014891SMilanka Ringwald 20911014891SMilanka Ringwald uint16_t supported_features = (1 << AVRCP_CONTROLLER_SUPPORTED_FEATURE_CATEGORY_PLAYER_OR_RECORDER); 2104f0111ebSMilanka Ringwald #ifdef AVRCP_BROWSING_ENABLED 21111014891SMilanka Ringwald supported_features |= (1 << AVRCP_CONTROLLER_SUPPORTED_FEATURE_BROWSING); 2124f0111ebSMilanka Ringwald #endif 2134f0111ebSMilanka Ringwald avrcp_controller_create_sdp_record(sdp_avrcp_browsing_controller_service_buffer, 0x10001, supported_features, NULL, NULL); 214462aa085SMilanka Ringwald sdp_register_service(sdp_avrcp_browsing_controller_service_buffer); 215462aa085SMilanka Ringwald 216462aa085SMilanka Ringwald // Set local name with a template Bluetooth address, that will be automatically 217462aa085SMilanka Ringwald // replaced with a actual address once it is available, i.e. when BTstack boots 218462aa085SMilanka Ringwald // up and starts talking to a Bluetooth module. 219462aa085SMilanka Ringwald gap_set_local_name("AVRCP Browsing Client 00:00:00:00:00:00"); 220462aa085SMilanka Ringwald gap_discoverable_control(1); 221462aa085SMilanka Ringwald gap_set_class_of_device(0x200408); 222462aa085SMilanka Ringwald 223a4fe6467SMatthias Ringwald // Register for HCI events. 2240eebc132SMilanka Ringwald hci_event_callback_registration.callback = &avrcp_browsing_packet_handler; 225a4fe6467SMatthias Ringwald hci_add_event_handler(&hci_event_callback_registration); 226a4fe6467SMatthias Ringwald 227a4fe6467SMatthias Ringwald 228462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN 229462aa085SMilanka Ringwald // Parse human readable Bluetooth address. 230462aa085SMilanka Ringwald sscanf_bd_addr(device_addr_string, device_addr); 231462aa085SMilanka Ringwald btstack_stdin_setup(stdin_process); 232462aa085SMilanka Ringwald #endif 233462aa085SMilanka Ringwald printf("Starting BTstack ...\n"); 234462aa085SMilanka Ringwald hci_power_control(HCI_POWER_ON); 235462aa085SMilanka Ringwald return 0; 236462aa085SMilanka Ringwald } 237462aa085SMilanka Ringwald /* LISTING_END */ 238462aa085SMilanka Ringwald 23902d2f9caSMilanka Ringwald static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 24002d2f9caSMilanka Ringwald UNUSED(channel); 24102d2f9caSMilanka Ringwald UNUSED(size); 24202d2f9caSMilanka Ringwald uint16_t local_cid; 24302d2f9caSMilanka Ringwald uint8_t status = 0xFF; 24402d2f9caSMilanka Ringwald bd_addr_t adress; 24502d2f9caSMilanka Ringwald 24602d2f9caSMilanka Ringwald if (packet_type != HCI_EVENT_PACKET) return; 24702d2f9caSMilanka Ringwald if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return; 24802d2f9caSMilanka Ringwald switch (packet[2]){ 24902d2f9caSMilanka Ringwald case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED: { 25002d2f9caSMilanka Ringwald local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet); 25102d2f9caSMilanka Ringwald status = avrcp_subevent_connection_established_get_status(packet); 25202d2f9caSMilanka Ringwald if (status != ERROR_CODE_SUCCESS){ 25302d2f9caSMilanka Ringwald printf("AVRCP: Connection failed: status 0x%02x\n", status); 25402d2f9caSMilanka Ringwald avrcp_cid = 0; 25502d2f9caSMilanka Ringwald return; 25602d2f9caSMilanka Ringwald } 25702d2f9caSMilanka Ringwald 25802d2f9caSMilanka Ringwald avrcp_cid = local_cid; 259ace5ab2aSMilanka Ringwald avrcp_connected = true; 26002d2f9caSMilanka Ringwald avrcp_subevent_connection_established_get_bd_addr(packet, adress); 26102d2f9caSMilanka Ringwald printf("AVRCP: Connected to %s, cid 0x%02x\n", bd_addr_to_str(adress), avrcp_cid); 26202d2f9caSMilanka Ringwald return; 26302d2f9caSMilanka Ringwald } 26402d2f9caSMilanka Ringwald 26502d2f9caSMilanka Ringwald case AVRCP_SUBEVENT_CONNECTION_RELEASED: 26602d2f9caSMilanka Ringwald printf("AVRCP: Channel released: cid 0x%02x\n", avrcp_subevent_connection_released_get_avrcp_cid(packet)); 26702d2f9caSMilanka Ringwald avrcp_cid = 0; 268ace5ab2aSMilanka Ringwald avrcp_connected = false; 26902d2f9caSMilanka Ringwald return; 27002d2f9caSMilanka Ringwald default: 27102d2f9caSMilanka Ringwald break; 27202d2f9caSMilanka Ringwald } 27302d2f9caSMilanka Ringwald } 2740eebc132SMilanka Ringwald static void avrcp_browsing_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 275462aa085SMilanka Ringwald UNUSED(channel); 276954cc391SMilanka Ringwald int pos; 277462aa085SMilanka Ringwald 278954cc391SMilanka Ringwald switch(packet_type){ 279954cc391SMilanka Ringwald case AVRCP_BROWSING_DATA_PACKET: 280954cc391SMilanka Ringwald pos = 0; 281ace5ab2aSMilanka Ringwald browsing_query_active = true; 282954cc391SMilanka Ringwald avrcp_browsing_item_type_t data_type = (avrcp_browsing_item_type_t)packet[pos++]; 283954cc391SMilanka Ringwald pos += 2; // length 284954cc391SMilanka Ringwald 285954cc391SMilanka Ringwald switch (data_type){ 286db79553aSMilanka Ringwald case AVRCP_BROWSING_MEDIA_ROOT_FOLDER:{ 287db79553aSMilanka Ringwald avrcp_browsing_root_folder_t root_folder; 288db79553aSMilanka Ringwald root_folder.charset = big_endian_read_16(packet, pos); 289db79553aSMilanka Ringwald pos += 2; 290db79553aSMilanka Ringwald root_folder.depth = packet[pos++]; 291db79553aSMilanka Ringwald root_folder.name_len = big_endian_read_16(packet, pos); 292db79553aSMilanka Ringwald pos += 2; 2932f511ffbSMilanka Ringwald 294db79553aSMilanka Ringwald memset(root_folder.name, 0, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN); 2952f511ffbSMilanka Ringwald root_folder.name_len = btstack_min(big_endian_read_16(packet, pos), AVRCP_BROWSING_MAX_FOLDER_NAME_LEN - 1); 296db79553aSMilanka Ringwald memcpy(root_folder.name, packet+pos, root_folder.name_len); 297db79553aSMilanka Ringwald printf("Found root folder: name %s, depth %d \n", (char *)root_folder.name, root_folder.depth); 298db79553aSMilanka Ringwald break; 299db79553aSMilanka Ringwald } 300954cc391SMilanka Ringwald case AVRCP_BROWSING_MEDIA_PLAYER_ITEM:{ 301db79553aSMilanka Ringwald printf("Received media player: "); 302954cc391SMilanka Ringwald uint16_t player_id = big_endian_read_16(packet, pos); 303954cc391SMilanka Ringwald pos += 2; 304954cc391SMilanka Ringwald avrcp_browsing_media_player_major_type_t major_type = packet[pos++]; 305954cc391SMilanka Ringwald avrcp_browsing_media_player_subtype_t subtype = big_endian_read_32(packet, pos); 306954cc391SMilanka Ringwald pos += 4; 307954cc391SMilanka Ringwald uint8_t status = packet[pos++]; 308954cc391SMilanka Ringwald uint8_t feature_bitmask[16]; 309954cc391SMilanka Ringwald memcpy(feature_bitmask, packet, 16); 310954cc391SMilanka Ringwald pos += 16; 311db79553aSMilanka Ringwald printf("player ID 0x%04x, major_type %d, subtype %d, status %d\n", player_id, major_type, subtype, status); 312db79553aSMilanka Ringwald players[next_player_index()] = player_id; 313954cc391SMilanka Ringwald break; 314954cc391SMilanka Ringwald } 315954cc391SMilanka Ringwald case AVRCP_BROWSING_FOLDER_ITEM:{ 316db79553aSMilanka Ringwald int index = next_folder_index(); 3172f511ffbSMilanka Ringwald // printf("Found folder [%d]: ", index); 318db79553aSMilanka Ringwald memcpy(folders[index].uid, packet+pos, 8); 319954cc391SMilanka Ringwald uint32_t folder_uid_high = big_endian_read_32(packet, pos); 320954cc391SMilanka Ringwald pos += 4; 321db79553aSMilanka Ringwald uint32_t folder_uid_low = big_endian_read_32(packet, pos); 322954cc391SMilanka Ringwald pos += 4; 323954cc391SMilanka Ringwald avrcp_browsing_folder_type_t folder_type = packet[pos++]; 324954cc391SMilanka Ringwald uint8_t is_playable = packet[pos++]; 325954cc391SMilanka Ringwald uint16_t charset = big_endian_read_16(packet, pos); 326954cc391SMilanka Ringwald pos += 2; 327954cc391SMilanka Ringwald uint16_t displayable_name_length = big_endian_read_16(packet, pos); 328954cc391SMilanka Ringwald pos += 2; 3292f511ffbSMilanka Ringwald 330db79553aSMilanka Ringwald char value[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN]; 3312f511ffbSMilanka Ringwald 332db79553aSMilanka Ringwald memset(value, 0, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN); 3332f511ffbSMilanka Ringwald uint16_t value_len = btstack_min(displayable_name_length, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN - 1); 334954cc391SMilanka Ringwald memcpy(value, packet+pos, value_len); 335db79553aSMilanka Ringwald 3362f511ffbSMilanka Ringwald printf("UID 0x%08" PRIx32 "%08" PRIx32 ", type 0x%02x, is_playable %d, charset 0x%02x, name %s\n", 3372f511ffbSMilanka Ringwald folder_uid_high, folder_uid_low, folder_type, is_playable, charset, value); 338db79553aSMilanka Ringwald memcpy(folders[index].name, value, value_len); 339db79553aSMilanka Ringwald folders[index].name_len = value_len; 340954cc391SMilanka Ringwald break; 341954cc391SMilanka Ringwald } 342954cc391SMilanka Ringwald case AVRCP_BROWSING_MEDIA_ELEMENT_ITEM:{ 343db79553aSMilanka Ringwald printf("Found media: "); 344954cc391SMilanka Ringwald 345954cc391SMilanka Ringwald uint32_t media_uid_high = big_endian_read_32(packet, pos); 346954cc391SMilanka Ringwald pos += 4; 347954cc391SMilanka Ringwald uint32_t media_uid_low = big_endian_read_32(packet, pos+4); 348954cc391SMilanka Ringwald pos += 4; 349954cc391SMilanka Ringwald avrcp_browsing_media_type_t media_type = packet[pos++]; 350954cc391SMilanka Ringwald uint16_t charset = big_endian_read_16(packet, pos); 351954cc391SMilanka Ringwald pos += 2; 352954cc391SMilanka Ringwald uint16_t displayable_name_length = big_endian_read_16(packet, pos); 353954cc391SMilanka Ringwald pos += 2; 354954cc391SMilanka Ringwald pos += displayable_name_length; 355954cc391SMilanka Ringwald printf("Media UID: 0x%08" PRIx32 "%08" PRIx32 ", media_type 0x%02x, charset 0x%02x, displayable_name_length %d\n", media_uid_high, media_uid_low, media_type, charset, displayable_name_length); 356954cc391SMilanka Ringwald 357954cc391SMilanka Ringwald uint8_t num_attributes = packet[pos++]; 358954cc391SMilanka Ringwald printf("Num media attributes %d\n", num_attributes); 359954cc391SMilanka Ringwald 360954cc391SMilanka Ringwald for (avrcp_media_item_iterator_init(&media_item_context, size-pos, packet+pos); avrcp_media_item_iterator_has_more(&media_item_context); avrcp_media_item_iterator_next(&media_item_context)){ 361954cc391SMilanka Ringwald uint32_t attr_id = avrcp_media_item_iterator_get_attr_id(&media_item_context); 362954cc391SMilanka Ringwald uint16_t attr_charset = avrcp_media_item_iterator_get_attr_charset(&media_item_context); 363954cc391SMilanka Ringwald uint16_t attr_value_length = avrcp_media_item_iterator_get_attr_value_len(&media_item_context); 364954cc391SMilanka Ringwald const uint8_t * attr_value = avrcp_media_item_iterator_get_attr_value(&media_item_context); 365954cc391SMilanka Ringwald 366954cc391SMilanka Ringwald printf("Attr ID 0x%08" PRIx32 ", charset %d, attr_value_length %d, value %s", attr_id, attr_charset, attr_value_length, attr_value); 367954cc391SMilanka Ringwald } 368954cc391SMilanka Ringwald break; 369954cc391SMilanka Ringwald } 370954cc391SMilanka Ringwald 371954cc391SMilanka Ringwald default: 372*8ee7c9daSMilanka Ringwald log_error("AVRCP Browsing: unknown browsable item type 0%02x", data_type); 373954cc391SMilanka Ringwald break; 374954cc391SMilanka Ringwald } 375954cc391SMilanka Ringwald break; 376954cc391SMilanka Ringwald case HCI_EVENT_PACKET: 377954cc391SMilanka Ringwald if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return; 378462aa085SMilanka Ringwald uint16_t local_cid; 379462aa085SMilanka Ringwald uint8_t status = 0xFF; 380954cc391SMilanka Ringwald bd_addr_t address; 381462aa085SMilanka Ringwald 382143efbe8SMilanka Ringwald if (packet[0] != HCI_EVENT_AVRCP_META) break; 383462aa085SMilanka Ringwald switch (packet[2]){ 384143efbe8SMilanka Ringwald 385143efbe8SMilanka Ringwald case AVRCP_SUBEVENT_INCOMING_BROWSING_CONNECTION: 386143efbe8SMilanka Ringwald local_cid = avrcp_subevent_incoming_browsing_connection_get_browsing_cid(packet); 387143efbe8SMilanka Ringwald printf("AVRCP_SUBEVENT_INCOMING_BROWSING_CONNECTION cid 0x%02x\n", local_cid); 388143efbe8SMilanka Ringwald if (browsing_cid != 0 && browsing_cid != local_cid) { 389*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing: connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid); 390f9294da5SMilanka Ringwald avrcp_browsing_decline_incoming_connection(browsing_cid); 391143efbe8SMilanka Ringwald return; 392143efbe8SMilanka Ringwald } 393143efbe8SMilanka Ringwald browsing_cid = local_cid; 394*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing: configure incoming connection, browsing cid 0x%02x\n", browsing_cid); 395a6344352SMilanka Ringwald avrcp_browsing_configure_incoming_connection(browsing_cid, ertm_buffer, sizeof(ertm_buffer), &ertm_config); 396143efbe8SMilanka Ringwald break; 397143efbe8SMilanka Ringwald 398462aa085SMilanka Ringwald case AVRCP_SUBEVENT_BROWSING_CONNECTION_ESTABLISHED: { 399462aa085SMilanka Ringwald local_cid = avrcp_subevent_browsing_connection_established_get_browsing_cid(packet); 400143efbe8SMilanka Ringwald printf("AVRCP_SUBEVENT_BROWSING_CONNECTION_ESTABLISHED cid 0x%02x\n", local_cid); 401462aa085SMilanka Ringwald if (browsing_cid != 0 && browsing_cid != local_cid) { 402*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing: connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid); 403462aa085SMilanka Ringwald return; 404462aa085SMilanka Ringwald } 405462aa085SMilanka Ringwald 406462aa085SMilanka Ringwald status = avrcp_subevent_browsing_connection_established_get_status(packet); 407462aa085SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){ 408*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing: connection failed: status 0x%02x\n", status); 409462aa085SMilanka Ringwald browsing_cid = 0; 410462aa085SMilanka Ringwald return; 411462aa085SMilanka Ringwald } 412462aa085SMilanka Ringwald 413462aa085SMilanka Ringwald browsing_cid = local_cid; 414462aa085SMilanka Ringwald avrcp_browsing_connected = 1; 415954cc391SMilanka Ringwald avrcp_subevent_browsing_connection_established_get_bd_addr(packet, address); 416*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing: connected\n"); 417462aa085SMilanka Ringwald return; 418462aa085SMilanka Ringwald } 419462aa085SMilanka Ringwald case AVRCP_SUBEVENT_BROWSING_CONNECTION_RELEASED: 420*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing: disconnected\n"); 421462aa085SMilanka Ringwald browsing_cid = 0; 422462aa085SMilanka Ringwald avrcp_browsing_connected = 0; 423462aa085SMilanka Ringwald return; 424143efbe8SMilanka Ringwald 425143efbe8SMilanka Ringwald case AVRCP_SUBEVENT_BROWSING_DONE: 426ace5ab2aSMilanka Ringwald browsing_query_active = false; 427143efbe8SMilanka Ringwald browsing_uid_counter = 0; 428143efbe8SMilanka Ringwald if (avrcp_subevent_browsing_done_get_browsing_status(packet) != AVRCP_BROWSING_ERROR_CODE_SUCCESS){ 429*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing: query done with browsing status 0x%02x, bluetooth status 0x%02x\n", 430143efbe8SMilanka Ringwald avrcp_subevent_browsing_done_get_browsing_status(packet), 431143efbe8SMilanka Ringwald avrcp_subevent_browsing_done_get_bluetooth_status(packet)); 432954cc391SMilanka Ringwald break; 433db79553aSMilanka Ringwald } 434143efbe8SMilanka Ringwald browsing_uid_counter = avrcp_subevent_browsing_done_get_uid_counter(packet); 435*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing: browsing_uid_counter %d\n", browsing_uid_counter); 436db79553aSMilanka Ringwald break; 437143efbe8SMilanka Ringwald 438462aa085SMilanka Ringwald default: 439462aa085SMilanka Ringwald break; 440462aa085SMilanka Ringwald } 441954cc391SMilanka Ringwald break; 442954cc391SMilanka Ringwald 443954cc391SMilanka Ringwald default: 444954cc391SMilanka Ringwald break; 445954cc391SMilanka Ringwald } 446954cc391SMilanka Ringwald 447462aa085SMilanka Ringwald } 448462aa085SMilanka Ringwald 449462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN 450462aa085SMilanka Ringwald static void show_usage(void){ 451462aa085SMilanka Ringwald bd_addr_t iut_address; 452462aa085SMilanka Ringwald gap_local_bd_addr(iut_address); 453*8ee7c9daSMilanka Ringwald printf("\n--- Bluetooth AVRCP Browsing Service Test Console %s ---\n", bd_addr_to_str(iut_address)); 454*8ee7c9daSMilanka Ringwald printf("c - AVRCP Service create connection to addr %s\n", bd_addr_to_str(device_addr)); 455*8ee7c9daSMilanka Ringwald printf("C - AVRCP Service disconnect\n"); 456*8ee7c9daSMilanka Ringwald printf("e - AVRCP Browsing Service create connection to addr %s\n", bd_addr_to_str(device_addr)); 457*8ee7c9daSMilanka Ringwald printf("E - AVRCP Browsing Service disconnect\n"); 458db79553aSMilanka Ringwald 459c0a054f6SMilanka Ringwald printf("I - Set first found player as addressed player\n"); 460c0a054f6SMilanka Ringwald printf("O - Set first found player as browsed player\n"); 461db79553aSMilanka Ringwald 462c0a054f6SMilanka Ringwald printf("p - Get media players\n"); 463c0a054f6SMilanka Ringwald printf("Q - Browse folders\n"); 464c0a054f6SMilanka Ringwald printf("P - Go up one level\n"); 465c0a054f6SMilanka Ringwald printf("W - Go down one level\n"); 466c0a054f6SMilanka Ringwald printf("T - Browse media items\n"); 467462aa085SMilanka Ringwald printf("---\n"); 468462aa085SMilanka Ringwald } 469462aa085SMilanka Ringwald #endif 470462aa085SMilanka Ringwald 471ed0df7b2SMilanka Ringwald 472462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN 473462aa085SMilanka Ringwald static void stdin_process(char cmd){ 474462aa085SMilanka Ringwald uint8_t status = ERROR_CODE_SUCCESS; 475462aa085SMilanka Ringwald 476954cc391SMilanka Ringwald if (cmd != 'a' && cmd != 'A' && cmd != 'c' && cmd != 'C'){ 477954cc391SMilanka Ringwald if (browsing_query_active){ 478954cc391SMilanka Ringwald printf("Query active, try later!\n"); 479954cc391SMilanka Ringwald return; 480954cc391SMilanka Ringwald } 481954cc391SMilanka Ringwald } 482954cc391SMilanka Ringwald 483462aa085SMilanka Ringwald switch (cmd){ 484c0a054f6SMilanka Ringwald case 'c': 485*8ee7c9daSMilanka Ringwald printf(" - Connect to AVRCP Service on addr %s.\n", bd_addr_to_str(device_addr)); 486fe10780bSMilanka Ringwald status = avrcp_connect(device_addr, &avrcp_cid); 487462aa085SMilanka Ringwald break; 488c0a054f6SMilanka Ringwald case 'C': 489247956eaSMilanka Ringwald if (avrcp_connected){ 490*8ee7c9daSMilanka Ringwald printf(" - AVRCP Service disconnect from addr %s.\n", bd_addr_to_str(device_addr)); 491fe10780bSMilanka Ringwald status = avrcp_disconnect(avrcp_cid); 492247956eaSMilanka Ringwald break; 493247956eaSMilanka Ringwald } 494*8ee7c9daSMilanka Ringwald printf("AVRCP Service already disconnected\n"); 495247956eaSMilanka Ringwald break; 496247956eaSMilanka Ringwald 497c0a054f6SMilanka Ringwald case 'e': 498462aa085SMilanka Ringwald if (!avrcp_connected) { 499*8ee7c9daSMilanka Ringwald printf(" You must first connect to AVRCP Service on addr %s.\n", bd_addr_to_str(device_addr)); 500462aa085SMilanka Ringwald break; 501462aa085SMilanka Ringwald } 502*8ee7c9daSMilanka Ringwald printf(" - Connect to AVRCP Browsing Service at addr %s.\n", bd_addr_to_str(device_addr)); 5033121b998SMilanka Ringwald status = avrcp_browsing_connect(device_addr, ertm_buffer, sizeof(ertm_buffer), &ertm_config, &browsing_cid); 504462aa085SMilanka Ringwald break; 505c0a054f6SMilanka Ringwald case 'E': 506247956eaSMilanka Ringwald if (avrcp_browsing_connected){ 507*8ee7c9daSMilanka Ringwald printf(" - AVRCP Browsing Service disconnect from addr %s.\n", bd_addr_to_str(device_addr)); 508*8ee7c9daSMilanka Ringwald status = avrcp_browsing_disconnect(browsing_cid); 509462aa085SMilanka Ringwald break; 510247956eaSMilanka Ringwald } 511*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing Service already disconnected\n"); 512247956eaSMilanka Ringwald break; 513462aa085SMilanka Ringwald case '\n': 514462aa085SMilanka Ringwald case '\r': 515462aa085SMilanka Ringwald break; 516db79553aSMilanka Ringwald 517db79553aSMilanka Ringwald default: 518db79553aSMilanka Ringwald if (!avrcp_browsing_connected){ 519db79553aSMilanka Ringwald show_usage(); 520db79553aSMilanka Ringwald break; 521db79553aSMilanka Ringwald } 522db79553aSMilanka Ringwald 523db79553aSMilanka Ringwald switch (cmd) { 524c0a054f6SMilanka Ringwald case 'I': 525db79553aSMilanka Ringwald if (player_index < 0) { 526*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing:Get media players first\n"); 527db79553aSMilanka Ringwald break; 528db79553aSMilanka Ringwald } 529*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing:Set addressed player\n"); 530143efbe8SMilanka Ringwald status = avrcp_controller_set_addressed_player(avrcp_cid, players[0]); 531db79553aSMilanka Ringwald break; 532c0a054f6SMilanka Ringwald case 'O': 533db79553aSMilanka Ringwald if (player_index < 0) { 534*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing:Get media players first\n"); 535db79553aSMilanka Ringwald break; 536db79553aSMilanka Ringwald } 537db79553aSMilanka Ringwald printf("Set browsed player\n"); 538db79553aSMilanka Ringwald status = avrcp_browsing_controller_set_browsed_player(browsing_cid, players[0]); 539db79553aSMilanka Ringwald break; 540c0a054f6SMilanka Ringwald case 'p': 541db79553aSMilanka Ringwald printf("AVRCP Browsing: get media players\n"); 542db79553aSMilanka Ringwald player_index = -1; 5432f511ffbSMilanka Ringwald status = avrcp_browsing_controller_get_media_players(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL); 544db79553aSMilanka Ringwald break; 545c0a054f6SMilanka Ringwald case 'Q': 546db79553aSMilanka Ringwald printf("AVRCP Browsing: browse folders\n"); 547db79553aSMilanka Ringwald folder_index = -1; 5482f511ffbSMilanka Ringwald status = avrcp_browsing_controller_browse_file_system(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL); 549db79553aSMilanka Ringwald break; 550c0a054f6SMilanka Ringwald case 'P': 551db79553aSMilanka Ringwald printf("AVRCP Browsing: browse media items\n"); 5522f511ffbSMilanka Ringwald avrcp_browsing_controller_browse_media(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL); 553db79553aSMilanka Ringwald break; 554c0a054f6SMilanka Ringwald case 'W': 555e5b24613SMilanka Ringwald printf("AVRCP Browsing: go up one level\n"); 556e5b24613SMilanka Ringwald status = avrcp_browsing_controller_go_up_one_level(browsing_cid); 557db79553aSMilanka Ringwald folder_index = -1; 558db79553aSMilanka Ringwald break; 559c0a054f6SMilanka Ringwald case 'T': 560db79553aSMilanka Ringwald if (folder_index < 0 && !parent_folder_set){ 561db79553aSMilanka Ringwald printf("AVRCP Browsing: no folders available\n"); 562db79553aSMilanka Ringwald break; 563db79553aSMilanka Ringwald } 564db79553aSMilanka Ringwald if (!parent_folder_set){ 565db79553aSMilanka Ringwald parent_folder_set = 1; 566db79553aSMilanka Ringwald memcpy(parent_folder_name, folders[0].name, folders[0].name_len); 567db79553aSMilanka Ringwald memcpy(parent_folder_uid, folders[0].uid, 8); 568db79553aSMilanka Ringwald } 569db79553aSMilanka Ringwald printf("AVRCP Browsing: go down one level of %s\n", (char *)parent_folder_name); 570db79553aSMilanka Ringwald status = avrcp_browsing_controller_go_down_one_level(browsing_cid, parent_folder_uid); 571db79553aSMilanka Ringwald folder_index = -1; 572db79553aSMilanka Ringwald break; 573462aa085SMilanka Ringwald default: 574462aa085SMilanka Ringwald show_usage(); 575db79553aSMilanka Ringwald break; 576db79553aSMilanka Ringwald } 577db79553aSMilanka Ringwald break; 578462aa085SMilanka Ringwald } 579ed0df7b2SMilanka Ringwald 580462aa085SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){ 581*8ee7c9daSMilanka Ringwald printf("AVRCP Browsing: Could not perform command, status 0x%2x\n", status); 582462aa085SMilanka Ringwald } 583462aa085SMilanka Ringwald } 584462aa085SMilanka Ringwald #endif 585462aa085SMilanka Ringwald /* EXAMPLE_END */ 586