xref: /btstack/example/avrcp_browsing_client.c (revision e2f2541787fb1a2a99cff34b41ad8a2517cbcfaf)
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  *
5069f3ffd7SMilanka Ringwald  * @text To test with a remote device, e.g. a mobile phone,
5169f3ffd7SMilanka Ringwald  * pair from the remote device with the demo, then use the UI for browsing. If HAVE_BTSTACK_STDIN is set,
5269f3ffd7SMilanka Ringwald  * press SPACE on the console to show the available AVDTP and 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 
209*e2f25417SMilanka Ringwald     uint16_t supported_features = AVRCP_FEATURE_MASK_CATEGORY_PLAYER_OR_RECORDER;
2104f0111ebSMilanka Ringwald #ifdef AVRCP_BROWSING_ENABLED
211*e2f25417SMilanka Ringwald     supported_features |= AVRCP_FEATURE_MASK_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:
3728ee7c9daSMilanka 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);
387f4ffe59aSMilanka Ringwald                     printf("AVRCP Browsing: incoming connection cid 0x%02x\n", local_cid);
388143efbe8SMilanka Ringwald                     if (browsing_cid != 0 && browsing_cid != local_cid) {
389f4ffe59aSMilanka Ringwald                         printf("AVRCP Browsing: decline incoming connection, 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;
3948ee7c9daSMilanka 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);
400462aa085SMilanka Ringwald                     if (browsing_cid != 0 && browsing_cid != local_cid) {
4018ee7c9daSMilanka Ringwald                         printf("AVRCP Browsing: connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
402462aa085SMilanka Ringwald                         return;
403462aa085SMilanka Ringwald                     }
404462aa085SMilanka Ringwald 
405462aa085SMilanka Ringwald                     status = avrcp_subevent_browsing_connection_established_get_status(packet);
406462aa085SMilanka Ringwald                     if (status != ERROR_CODE_SUCCESS){
4078ee7c9daSMilanka Ringwald                         printf("AVRCP Browsing: connection failed: status 0x%02x\n", status);
408462aa085SMilanka Ringwald                         browsing_cid = 0;
409462aa085SMilanka Ringwald                         return;
410462aa085SMilanka Ringwald                     }
411462aa085SMilanka Ringwald 
412462aa085SMilanka Ringwald                     browsing_cid = local_cid;
413462aa085SMilanka Ringwald                     avrcp_browsing_connected = 1;
414954cc391SMilanka Ringwald                     avrcp_subevent_browsing_connection_established_get_bd_addr(packet, address);
415f4ffe59aSMilanka Ringwald                     printf("AVRCP Browsing: connected, browsing cid 0x%02x\n", browsing_cid);
416462aa085SMilanka Ringwald                     return;
417462aa085SMilanka Ringwald                 }
418462aa085SMilanka Ringwald                 case AVRCP_SUBEVENT_BROWSING_CONNECTION_RELEASED:
419f4ffe59aSMilanka Ringwald                     printf("AVRCP Browsing: disconnected, browsing cid 0x%02x\n", browsing_cid);
420462aa085SMilanka Ringwald                     browsing_cid = 0;
421462aa085SMilanka Ringwald                     avrcp_browsing_connected = 0;
422462aa085SMilanka Ringwald                     return;
423143efbe8SMilanka Ringwald 
424143efbe8SMilanka Ringwald                 case AVRCP_SUBEVENT_BROWSING_DONE:
425ace5ab2aSMilanka Ringwald                     browsing_query_active = false;
426143efbe8SMilanka Ringwald                     browsing_uid_counter = 0;
427143efbe8SMilanka Ringwald                     if (avrcp_subevent_browsing_done_get_browsing_status(packet) != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
4288ee7c9daSMilanka Ringwald                         printf("AVRCP Browsing: query done with browsing status 0x%02x, bluetooth status 0x%02x\n",
429143efbe8SMilanka Ringwald                             avrcp_subevent_browsing_done_get_browsing_status(packet),
430143efbe8SMilanka Ringwald                             avrcp_subevent_browsing_done_get_bluetooth_status(packet));
431954cc391SMilanka Ringwald                         break;
432db79553aSMilanka Ringwald                     }
433143efbe8SMilanka Ringwald                     browsing_uid_counter = avrcp_subevent_browsing_done_get_uid_counter(packet);
4348ee7c9daSMilanka Ringwald                     printf("AVRCP Browsing: browsing_uid_counter %d\n", browsing_uid_counter);
435db79553aSMilanka Ringwald                     break;
436143efbe8SMilanka Ringwald 
437462aa085SMilanka Ringwald                 default:
438462aa085SMilanka Ringwald                     break;
439462aa085SMilanka Ringwald             }
440954cc391SMilanka Ringwald             break;
441954cc391SMilanka Ringwald 
442954cc391SMilanka Ringwald         default:
443954cc391SMilanka Ringwald             break;
444954cc391SMilanka Ringwald     }
445954cc391SMilanka Ringwald 
446462aa085SMilanka Ringwald }
447462aa085SMilanka Ringwald 
448462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN
449462aa085SMilanka Ringwald static void show_usage(void){
450462aa085SMilanka Ringwald     bd_addr_t      iut_address;
451462aa085SMilanka Ringwald     gap_local_bd_addr(iut_address);
4528ee7c9daSMilanka Ringwald     printf("\n--- Bluetooth AVRCP Browsing Service Test Console %s ---\n", bd_addr_to_str(iut_address));
4538ee7c9daSMilanka Ringwald     printf("c      - AVRCP Service create connection to addr %s\n", bd_addr_to_str(device_addr));
4548ee7c9daSMilanka Ringwald     printf("C      - AVRCP Service disconnect\n");
4558ee7c9daSMilanka Ringwald     printf("e      - AVRCP Browsing Service create connection to addr %s\n", bd_addr_to_str(device_addr));
4568ee7c9daSMilanka Ringwald     printf("E      - AVRCP Browsing Service disconnect\n");
457db79553aSMilanka Ringwald 
458c0a054f6SMilanka Ringwald     printf("I      - Set first found player as addressed player\n");
459c0a054f6SMilanka Ringwald     printf("O      - Set first found player as browsed player\n");
460db79553aSMilanka Ringwald 
461c0a054f6SMilanka Ringwald     printf("p      - Get media players\n");
462c0a054f6SMilanka Ringwald     printf("Q      - Browse folders\n");
463c0a054f6SMilanka Ringwald     printf("P      - Go up one level\n");
464c0a054f6SMilanka Ringwald     printf("W      - Go down one level\n");
465c0a054f6SMilanka Ringwald     printf("T      - Browse media items\n");
466462aa085SMilanka Ringwald     printf("---\n");
467462aa085SMilanka Ringwald }
468462aa085SMilanka Ringwald #endif
469462aa085SMilanka Ringwald 
470ed0df7b2SMilanka Ringwald 
471462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN
472462aa085SMilanka Ringwald static void stdin_process(char cmd){
473462aa085SMilanka Ringwald     uint8_t status = ERROR_CODE_SUCCESS;
474462aa085SMilanka Ringwald 
475954cc391SMilanka Ringwald     if (cmd != 'a' && cmd != 'A' && cmd != 'c' && cmd != 'C'){
476954cc391SMilanka Ringwald         if (browsing_query_active){
477954cc391SMilanka Ringwald             printf("Query active, try later!\n");
478954cc391SMilanka Ringwald             return;
479954cc391SMilanka Ringwald         }
480954cc391SMilanka Ringwald     }
481954cc391SMilanka Ringwald 
482462aa085SMilanka Ringwald     switch (cmd){
483c0a054f6SMilanka Ringwald         case 'c':
4848ee7c9daSMilanka Ringwald             printf(" - Connect to AVRCP Service on addr %s.\n", bd_addr_to_str(device_addr));
485fe10780bSMilanka Ringwald             status = avrcp_connect(device_addr, &avrcp_cid);
486462aa085SMilanka Ringwald             break;
487c0a054f6SMilanka Ringwald         case 'C':
488247956eaSMilanka Ringwald             if (avrcp_connected){
4898ee7c9daSMilanka Ringwald                 printf(" - AVRCP Service disconnect from addr %s.\n", bd_addr_to_str(device_addr));
490fe10780bSMilanka Ringwald                 status = avrcp_disconnect(avrcp_cid);
491247956eaSMilanka Ringwald                 break;
492247956eaSMilanka Ringwald             }
4938ee7c9daSMilanka Ringwald             printf("AVRCP Service already disconnected\n");
494247956eaSMilanka Ringwald             break;
495247956eaSMilanka Ringwald 
496c0a054f6SMilanka Ringwald         case 'e':
497462aa085SMilanka Ringwald             if (!avrcp_connected) {
4988ee7c9daSMilanka Ringwald                 printf(" You must first connect to AVRCP Service on addr %s.\n", bd_addr_to_str(device_addr));
499462aa085SMilanka Ringwald                 break;
500462aa085SMilanka Ringwald             }
5018ee7c9daSMilanka Ringwald             printf(" - Connect to AVRCP Browsing Service at addr %s.\n", bd_addr_to_str(device_addr));
5023121b998SMilanka Ringwald             status = avrcp_browsing_connect(device_addr, ertm_buffer, sizeof(ertm_buffer), &ertm_config, &browsing_cid);
503462aa085SMilanka Ringwald             break;
504c0a054f6SMilanka Ringwald         case 'E':
505247956eaSMilanka Ringwald             if (avrcp_browsing_connected){
5068ee7c9daSMilanka Ringwald                 printf(" - AVRCP Browsing Service disconnect from addr %s.\n", bd_addr_to_str(device_addr));
5078ee7c9daSMilanka Ringwald                 status = avrcp_browsing_disconnect(browsing_cid);
508462aa085SMilanka Ringwald                 break;
509247956eaSMilanka Ringwald             }
5108ee7c9daSMilanka Ringwald             printf("AVRCP Browsing Service already disconnected\n");
511247956eaSMilanka Ringwald             break;
512462aa085SMilanka Ringwald         case '\n':
513462aa085SMilanka Ringwald         case '\r':
514462aa085SMilanka Ringwald             break;
515db79553aSMilanka Ringwald 
516db79553aSMilanka Ringwald         default:
517db79553aSMilanka Ringwald             if (!avrcp_browsing_connected){
518db79553aSMilanka Ringwald                 show_usage();
519db79553aSMilanka Ringwald                 break;
520db79553aSMilanka Ringwald             }
521db79553aSMilanka Ringwald 
522db79553aSMilanka Ringwald             switch (cmd) {
523c0a054f6SMilanka Ringwald                 case 'I':
524db79553aSMilanka Ringwald                     if (player_index < 0) {
5258ee7c9daSMilanka Ringwald                         printf("AVRCP Browsing:Get media players first\n");
526db79553aSMilanka Ringwald                         break;
527db79553aSMilanka Ringwald                     }
5288ee7c9daSMilanka Ringwald                     printf("AVRCP Browsing:Set addressed player\n");
529143efbe8SMilanka Ringwald                     status = avrcp_controller_set_addressed_player(avrcp_cid, players[0]);
530db79553aSMilanka Ringwald                     break;
531c0a054f6SMilanka Ringwald                 case 'O':
532db79553aSMilanka Ringwald                     if (player_index < 0) {
5338ee7c9daSMilanka Ringwald                         printf("AVRCP Browsing:Get media players first\n");
534db79553aSMilanka Ringwald                         break;
535db79553aSMilanka Ringwald                     }
536db79553aSMilanka Ringwald                     printf("Set browsed player\n");
537db79553aSMilanka Ringwald                     status = avrcp_browsing_controller_set_browsed_player(browsing_cid, players[0]);
538db79553aSMilanka Ringwald                     break;
539c0a054f6SMilanka Ringwald                 case 'p':
540db79553aSMilanka Ringwald                     printf("AVRCP Browsing: get media players\n");
541db79553aSMilanka Ringwald                     player_index = -1;
5422f511ffbSMilanka Ringwald                     status = avrcp_browsing_controller_get_media_players(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
543db79553aSMilanka Ringwald                     break;
544c0a054f6SMilanka Ringwald                 case 'Q':
545db79553aSMilanka Ringwald                     printf("AVRCP Browsing: browse folders\n");
546db79553aSMilanka Ringwald                     folder_index = -1;
5472f511ffbSMilanka Ringwald                     status = avrcp_browsing_controller_browse_file_system(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
548db79553aSMilanka Ringwald                     break;
549c0a054f6SMilanka Ringwald                 case 'P':
550db79553aSMilanka Ringwald                     printf("AVRCP Browsing: browse media items\n");
5512f511ffbSMilanka Ringwald                     avrcp_browsing_controller_browse_media(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
552db79553aSMilanka Ringwald                     break;
553c0a054f6SMilanka Ringwald                 case 'W':
554e5b24613SMilanka Ringwald                     printf("AVRCP Browsing: go up one level\n");
555e5b24613SMilanka Ringwald                     status = avrcp_browsing_controller_go_up_one_level(browsing_cid);
556db79553aSMilanka Ringwald                     folder_index = -1;
557db79553aSMilanka Ringwald                     break;
558c0a054f6SMilanka Ringwald                 case 'T':
559db79553aSMilanka Ringwald                     if (folder_index < 0 && !parent_folder_set){
560db79553aSMilanka Ringwald                         printf("AVRCP Browsing: no folders available\n");
561db79553aSMilanka Ringwald                         break;
562db79553aSMilanka Ringwald                     }
563db79553aSMilanka Ringwald                     if (!parent_folder_set){
564db79553aSMilanka Ringwald                         parent_folder_set = 1;
565db79553aSMilanka Ringwald                         memcpy(parent_folder_name, folders[0].name, folders[0].name_len);
566db79553aSMilanka Ringwald                         memcpy(parent_folder_uid, folders[0].uid, 8);
567db79553aSMilanka Ringwald                     }
568db79553aSMilanka Ringwald                     printf("AVRCP Browsing: go down one level of %s\n", (char *)parent_folder_name);
569db79553aSMilanka Ringwald                     status = avrcp_browsing_controller_go_down_one_level(browsing_cid, parent_folder_uid);
570db79553aSMilanka Ringwald                     folder_index = -1;
571db79553aSMilanka Ringwald                     break;
572462aa085SMilanka Ringwald                 default:
573462aa085SMilanka Ringwald                     show_usage();
574db79553aSMilanka Ringwald                     break;
575db79553aSMilanka Ringwald             }
576db79553aSMilanka Ringwald             break;
577462aa085SMilanka Ringwald     }
578ed0df7b2SMilanka Ringwald 
579462aa085SMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
5808ee7c9daSMilanka Ringwald         printf("AVRCP Browsing: Could not perform command, status 0x%2x\n", status);
581462aa085SMilanka Ringwald     }
582462aa085SMilanka Ringwald }
583462aa085SMilanka Ringwald #endif
584462aa085SMilanka Ringwald /* EXAMPLE_END */
585