xref: /btstack/example/avrcp_browsing_client.c (revision 69f3ffd7c7beb6c03f703bd4fb8f53099521e88f)
1 /*
2  * Copyright (C) 2016 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "avrcp_browsing_client.c"
39 
40 /*
41  * avrcp_browsing_client.c
42  */
43 
44 // *****************************************************************************
45 /* EXAMPLE_START(avrcp_browsing_client): Browse media players and media information on a remote device.
46  *
47  * @text This example demonstrates how to use the AVRCP Controller Browsing service to
48  * browse madia players and media information on a remote AVRCP Source device.
49  *
50  * @text To test with a remote device, e.g. a mobile phone,
51  * pair from the remote device with the demo, then use the UI for browsing. If HAVE_BTSTACK_STDIN is set,
52  * press SPACE on the console to show the available AVDTP and AVRCP commands.
53  *
54  */
55 // *****************************************************************************
56 
57 #include <stdint.h>
58 #include <inttypes.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 
63 #include "btstack.h"
64 
65 #ifdef HAVE_BTSTACK_STDIN
66 #include "btstack_stdin.h"
67 #endif
68 
69 #define AVRCP_BROWSING_ENABLED
70 
71 #define AVRCP_BROWSING_MAX_PLAYERS          10
72 #define AVRCP_BROWSING_MAX_FOLDERS          10
73 #define AVRCP_BROWSING_MAX_FOLDER_NAME_LEN  30
74 
75 #ifdef HAVE_BTSTACK_STDIN
76 // mac 2011: static bd_addr_t remote = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3};
77 // pts: static bd_addr_t remote = {0x00, 0x1B, 0xDC, 0x08, 0x0A, 0xA5};
78 // mac 2013:
79 // static const char * device_addr_string = "84:38:35:65:d1:15";
80 // iPhone 5S: static const char * device_addr_string = "54:E4:3A:26:A2:39";
81 // phone 2013:
82 // static const char * device_addr_string = "B0:34:95:CB:97:C4";
83 // iPod
84 // static const char * device_addr_string = "B0:34:95:CB:97:C4";
85 // iPhone
86 static const char * device_addr_string = "6C:72:E7:10:22:EE";
87 
88 static bd_addr_t device_addr;
89 #endif
90 
91 static uint16_t avrcp_cid = 0;
92 static bool     avrcp_connected = false;
93 
94 static uint16_t browsing_cid = 0;
95 static uint8_t  avrcp_browsing_connected = 0;
96 static uint8_t  sdp_avrcp_browsing_controller_service_buffer[200];
97 
98 static bool     browsing_query_active = false;
99 static avrcp_media_item_context_t media_item_context;
100 
101 typedef struct {
102     uint16_t  charset;
103     uint8_t   depth;
104     uint16_t  name_len;
105     char      name[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN];
106 } avrcp_browsing_root_folder_t;
107 
108 typedef struct {
109     uint8_t  uid[8];
110     uint16_t name_len;
111     char     name[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN];
112 } avrcp_browsing_folders_t;
113 
114 static uint8_t  parent_folder_set = 0;
115 static uint8_t  parent_folder_uid[8];
116 static char     parent_folder_name[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN];
117 static avrcp_browsing_folders_t folders[AVRCP_BROWSING_MAX_FOLDERS];
118 static int folder_index = -1;
119 static uint16_t players[AVRCP_BROWSING_MAX_PLAYERS];
120 static int player_index = -1;
121 static uint16_t browsing_uid_counter = 0;
122 
123 static btstack_packet_callback_registration_t hci_event_callback_registration;
124 
125 static uint8_t ertm_buffer[10000];
126 static l2cap_ertm_config_t ertm_config = {
127     1,  // ertm mandatory
128     2,  // max transmit, some tests require > 1
129     2000,
130     12000,
131     144,    // l2cap ertm mtu
132     4,
133     4,
134     0,      // No FCS
135 };
136 
137 static inline int next_index(int * index, int max_value){
138     if ((*index) < max_value){
139         (*index)++;
140     } else {
141         (*index) = 0;
142     }
143     return (*index);
144 }
145 
146 static int next_folder_index(void){
147     return next_index(&folder_index, AVRCP_BROWSING_MAX_FOLDERS);
148 }
149 
150 static int next_player_index(void){
151     return next_index(&player_index, AVRCP_BROWSING_MAX_PLAYERS);
152 }
153 
154 
155 /* @section Main Application Setup
156  *
157  * @text The Listing MainConfiguration shows how to setup AVRCP Controller Browsing service.
158  * To announce AVRCP Controller Browsing service, you need to create corresponding
159  * SDP record and register it with the SDP service.
160  * You'll also need to register several packet handlers:
161  * - stdin_process callback - used to trigger AVRCP commands, such are get media players, playlists, albums, etc. Requires HAVE_BTSTACK_STDIN.
162  * - avrcp_browsing_packet_handler - used to receive answers for AVRCP commands.
163  *
164  */
165 
166 /* LISTING_START(MainConfiguration): Setup Audio Sink and AVRCP Controller services */
167 static void avrcp_browsing_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
168 static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
169 
170 #ifdef HAVE_BTSTACK_STDIN
171 static void stdin_process(char cmd);
172 #endif
173 
174 
175 int btstack_main(int argc, const char * argv[]);
176 int btstack_main(int argc, const char * argv[]){
177     (void)argc;
178     (void)argv;
179 
180     // Initialize L2CAP.
181     l2cap_init();
182 
183     // Initialize AVRCP service.
184     avrcp_init();
185     // Initialize AVRCP Controller & Target Service.
186     avrcp_controller_init();
187     avrcp_target_init();
188 
189     avrcp_register_packet_handler(&avrcp_packet_handler);
190     avrcp_controller_register_packet_handler(&avrcp_packet_handler);
191     avrcp_target_register_packet_handler(&avrcp_packet_handler);
192 
193     // Initialize AVRCP Browsing Service.
194     avrcp_browsing_init();
195     avrcp_browsing_controller_init();
196     avrcp_browsing_target_init();
197 
198     // Register for HCI events.
199     avrcp_browsing_controller_register_packet_handler(&avrcp_browsing_packet_handler);
200     avrcp_browsing_target_register_packet_handler(&avrcp_browsing_packet_handler);
201     avrcp_browsing_register_packet_handler(&avrcp_browsing_packet_handler);
202 
203     // Initialize SDP.
204     sdp_init();
205 
206     // Create AVRCP service record and register it with SDP.
207     memset(sdp_avrcp_browsing_controller_service_buffer, 0, sizeof(sdp_avrcp_browsing_controller_service_buffer));
208 
209     uint16_t supported_features = (1 << AVRCP_CONTROLLER_SUPPORTED_FEATURE_CATEGORY_PLAYER_OR_RECORDER);
210 #ifdef AVRCP_BROWSING_ENABLED
211     supported_features |= (1 << AVRCP_CONTROLLER_SUPPORTED_FEATURE_BROWSING);
212 #endif
213     avrcp_controller_create_sdp_record(sdp_avrcp_browsing_controller_service_buffer, 0x10001, supported_features, NULL, NULL);
214     sdp_register_service(sdp_avrcp_browsing_controller_service_buffer);
215 
216     // Set local name with a template Bluetooth address, that will be automatically
217     // replaced with a actual address once it is available, i.e. when BTstack boots
218     // up and starts talking to a Bluetooth module.
219     gap_set_local_name("AVRCP Browsing Client 00:00:00:00:00:00");
220     gap_discoverable_control(1);
221     gap_set_class_of_device(0x200408);
222 
223     // Register for HCI events.
224     hci_event_callback_registration.callback = &avrcp_browsing_packet_handler;
225     hci_add_event_handler(&hci_event_callback_registration);
226 
227 
228 #ifdef HAVE_BTSTACK_STDIN
229     // Parse human readable Bluetooth address.
230     sscanf_bd_addr(device_addr_string, device_addr);
231     btstack_stdin_setup(stdin_process);
232 #endif
233     printf("Starting BTstack ...\n");
234     hci_power_control(HCI_POWER_ON);
235     return 0;
236 }
237 /* LISTING_END */
238 
239 static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
240     UNUSED(channel);
241     UNUSED(size);
242     uint16_t local_cid;
243     uint8_t  status = 0xFF;
244     bd_addr_t adress;
245 
246     if (packet_type != HCI_EVENT_PACKET) return;
247     if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return;
248     switch (packet[2]){
249         case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED: {
250             local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet);
251             status = avrcp_subevent_connection_established_get_status(packet);
252             if (status != ERROR_CODE_SUCCESS){
253                 printf("AVRCP: Connection failed: status 0x%02x\n", status);
254                 avrcp_cid = 0;
255                 return;
256             }
257 
258             avrcp_cid = local_cid;
259             avrcp_connected = true;
260             avrcp_subevent_connection_established_get_bd_addr(packet, adress);
261             printf("AVRCP: Connected to %s, cid 0x%02x\n", bd_addr_to_str(adress), avrcp_cid);
262             return;
263         }
264 
265         case AVRCP_SUBEVENT_CONNECTION_RELEASED:
266             printf("AVRCP: Channel released: cid 0x%02x\n", avrcp_subevent_connection_released_get_avrcp_cid(packet));
267             avrcp_cid = 0;
268             avrcp_connected = false;
269             return;
270         default:
271             break;
272     }
273 }
274 static void avrcp_browsing_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
275     UNUSED(channel);
276     int pos;
277 
278     switch(packet_type){
279         case AVRCP_BROWSING_DATA_PACKET:
280             pos = 0;
281             browsing_query_active = true;
282             avrcp_browsing_item_type_t data_type = (avrcp_browsing_item_type_t)packet[pos++];
283             pos += 2; // length
284 
285             switch (data_type){
286                 case AVRCP_BROWSING_MEDIA_ROOT_FOLDER:{
287                     avrcp_browsing_root_folder_t root_folder;
288                     root_folder.charset = big_endian_read_16(packet, pos);
289                     pos += 2;
290                     root_folder.depth = packet[pos++];
291                     root_folder.name_len = big_endian_read_16(packet, pos);
292                     pos += 2;
293 
294                     memset(root_folder.name, 0, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN);
295                     root_folder.name_len = btstack_min(big_endian_read_16(packet, pos), AVRCP_BROWSING_MAX_FOLDER_NAME_LEN - 1);
296                     memcpy(root_folder.name, packet+pos, root_folder.name_len);
297                     printf("Found root folder: name %s, depth %d \n", (char *)root_folder.name, root_folder.depth);
298                     break;
299                 }
300                 case AVRCP_BROWSING_MEDIA_PLAYER_ITEM:{
301                     printf("Received media player:  ");
302                     uint16_t player_id = big_endian_read_16(packet, pos);
303                     pos += 2;
304                     avrcp_browsing_media_player_major_type_t major_type = packet[pos++];
305                     avrcp_browsing_media_player_subtype_t subtype = big_endian_read_32(packet, pos);
306                     pos += 4;
307                     uint8_t status = packet[pos++];
308                     uint8_t feature_bitmask[16];
309                     memcpy(feature_bitmask, packet, 16);
310                     pos += 16;
311                     printf("player ID 0x%04x, major_type %d, subtype %d, status %d\n", player_id, major_type, subtype, status);
312                     players[next_player_index()] = player_id;
313                     break;
314                 }
315                 case AVRCP_BROWSING_FOLDER_ITEM:{
316                     int index = next_folder_index();
317                     // printf("Found folder [%d]: ", index);
318                     memcpy(folders[index].uid, packet+pos, 8);
319                     uint32_t folder_uid_high = big_endian_read_32(packet, pos);
320                     pos += 4;
321                     uint32_t folder_uid_low  = big_endian_read_32(packet, pos);
322                     pos += 4;
323                     avrcp_browsing_folder_type_t folder_type = packet[pos++];
324                     uint8_t  is_playable = packet[pos++];
325                     uint16_t charset = big_endian_read_16(packet, pos);
326                     pos += 2;
327                     uint16_t displayable_name_length = big_endian_read_16(packet, pos);
328                     pos += 2;
329 
330                     char value[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN];
331 
332                     memset(value, 0, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN);
333                     uint16_t value_len = btstack_min(displayable_name_length, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN - 1);
334                     memcpy(value, packet+pos, value_len);
335 
336                     printf("UID 0x%08" PRIx32 "%08" PRIx32 ", type 0x%02x, is_playable %d, charset 0x%02x, name %s\n",
337                         folder_uid_high, folder_uid_low, folder_type, is_playable, charset, value);
338                     memcpy(folders[index].name, value, value_len);
339                     folders[index].name_len = value_len;
340                     break;
341                 }
342                 case AVRCP_BROWSING_MEDIA_ELEMENT_ITEM:{
343                     printf("Found media: ");
344 
345                     uint32_t media_uid_high = big_endian_read_32(packet, pos);
346                     pos += 4;
347                     uint32_t media_uid_low  = big_endian_read_32(packet, pos+4);
348                     pos += 4;
349                     avrcp_browsing_media_type_t media_type = packet[pos++];
350                     uint16_t charset = big_endian_read_16(packet, pos);
351                     pos += 2;
352                     uint16_t displayable_name_length = big_endian_read_16(packet, pos);
353                     pos += 2;
354                     pos += displayable_name_length;
355                     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);
356 
357                     uint8_t num_attributes = packet[pos++];
358                     printf("Num media attributes %d\n", num_attributes);
359 
360                     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)){
361                         uint32_t attr_id            = avrcp_media_item_iterator_get_attr_id(&media_item_context);
362                         uint16_t attr_charset       = avrcp_media_item_iterator_get_attr_charset(&media_item_context);
363                         uint16_t attr_value_length  = avrcp_media_item_iterator_get_attr_value_len(&media_item_context);
364                         const uint8_t * attr_value  = avrcp_media_item_iterator_get_attr_value(&media_item_context);
365 
366                         printf("Attr ID 0x%08" PRIx32 ", charset %d, attr_value_length %d, value %s", attr_id, attr_charset, attr_value_length, attr_value);
367                     }
368                     break;
369                 }
370 
371                 default:
372                     log_error("AVRCP Browsing: unknown browsable item type 0%02x", data_type);
373                     break;
374             }
375             break;
376         case HCI_EVENT_PACKET:
377             if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return;
378             uint16_t local_cid;
379             uint8_t  status = 0xFF;
380             bd_addr_t address;
381 
382             if (packet[0] != HCI_EVENT_AVRCP_META) break;
383             switch (packet[2]){
384 
385                 case AVRCP_SUBEVENT_INCOMING_BROWSING_CONNECTION:
386                     local_cid = avrcp_subevent_incoming_browsing_connection_get_browsing_cid(packet);
387                     printf("AVRCP Browsing: incoming connection cid 0x%02x\n", local_cid);
388                     if (browsing_cid != 0 && browsing_cid != local_cid) {
389                         printf("AVRCP Browsing: decline incoming connection, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
390                         avrcp_browsing_decline_incoming_connection(browsing_cid);
391                         return;
392                     }
393                     browsing_cid = local_cid;
394                     printf("AVRCP Browsing: configure incoming connection, browsing cid 0x%02x\n", browsing_cid);
395                     avrcp_browsing_configure_incoming_connection(browsing_cid, ertm_buffer, sizeof(ertm_buffer), &ertm_config);
396                     break;
397 
398                 case AVRCP_SUBEVENT_BROWSING_CONNECTION_ESTABLISHED: {
399                     local_cid = avrcp_subevent_browsing_connection_established_get_browsing_cid(packet);
400                     if (browsing_cid != 0 && browsing_cid != local_cid) {
401                         printf("AVRCP Browsing: connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
402                         return;
403                     }
404 
405                     status = avrcp_subevent_browsing_connection_established_get_status(packet);
406                     if (status != ERROR_CODE_SUCCESS){
407                         printf("AVRCP Browsing: connection failed: status 0x%02x\n", status);
408                         browsing_cid = 0;
409                         return;
410                     }
411 
412                     browsing_cid = local_cid;
413                     avrcp_browsing_connected = 1;
414                     avrcp_subevent_browsing_connection_established_get_bd_addr(packet, address);
415                     printf("AVRCP Browsing: connected, browsing cid 0x%02x\n", browsing_cid);
416                     return;
417                 }
418                 case AVRCP_SUBEVENT_BROWSING_CONNECTION_RELEASED:
419                     printf("AVRCP Browsing: disconnected, browsing cid 0x%02x\n", browsing_cid);
420                     browsing_cid = 0;
421                     avrcp_browsing_connected = 0;
422                     return;
423 
424                 case AVRCP_SUBEVENT_BROWSING_DONE:
425                     browsing_query_active = false;
426                     browsing_uid_counter = 0;
427                     if (avrcp_subevent_browsing_done_get_browsing_status(packet) != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
428                         printf("AVRCP Browsing: query done with browsing status 0x%02x, bluetooth status 0x%02x\n",
429                             avrcp_subevent_browsing_done_get_browsing_status(packet),
430                             avrcp_subevent_browsing_done_get_bluetooth_status(packet));
431                         break;
432                     }
433                     browsing_uid_counter = avrcp_subevent_browsing_done_get_uid_counter(packet);
434                     printf("AVRCP Browsing: browsing_uid_counter %d\n", browsing_uid_counter);
435                     break;
436 
437                 default:
438                     break;
439             }
440             break;
441 
442         default:
443             break;
444     }
445 
446 }
447 
448 #ifdef HAVE_BTSTACK_STDIN
449 static void show_usage(void){
450     bd_addr_t      iut_address;
451     gap_local_bd_addr(iut_address);
452     printf("\n--- Bluetooth AVRCP Browsing Service Test Console %s ---\n", bd_addr_to_str(iut_address));
453     printf("c      - AVRCP Service create connection to addr %s\n", bd_addr_to_str(device_addr));
454     printf("C      - AVRCP Service disconnect\n");
455     printf("e      - AVRCP Browsing Service create connection to addr %s\n", bd_addr_to_str(device_addr));
456     printf("E      - AVRCP Browsing Service disconnect\n");
457 
458     printf("I      - Set first found player as addressed player\n");
459     printf("O      - Set first found player as browsed player\n");
460 
461     printf("p      - Get media players\n");
462     printf("Q      - Browse folders\n");
463     printf("P      - Go up one level\n");
464     printf("W      - Go down one level\n");
465     printf("T      - Browse media items\n");
466     printf("---\n");
467 }
468 #endif
469 
470 
471 #ifdef HAVE_BTSTACK_STDIN
472 static void stdin_process(char cmd){
473     uint8_t status = ERROR_CODE_SUCCESS;
474 
475     if (cmd != 'a' && cmd != 'A' && cmd != 'c' && cmd != 'C'){
476         if (browsing_query_active){
477             printf("Query active, try later!\n");
478             return;
479         }
480     }
481 
482     switch (cmd){
483         case 'c':
484             printf(" - Connect to AVRCP Service on addr %s.\n", bd_addr_to_str(device_addr));
485             status = avrcp_connect(device_addr, &avrcp_cid);
486             break;
487         case 'C':
488             if (avrcp_connected){
489                 printf(" - AVRCP Service disconnect from addr %s.\n", bd_addr_to_str(device_addr));
490                 status = avrcp_disconnect(avrcp_cid);
491                 break;
492             }
493             printf("AVRCP Service already disconnected\n");
494             break;
495 
496         case 'e':
497             if (!avrcp_connected) {
498                 printf(" You must first connect to AVRCP Service on addr %s.\n", bd_addr_to_str(device_addr));
499                 break;
500             }
501             printf(" - Connect to AVRCP Browsing Service at addr %s.\n", bd_addr_to_str(device_addr));
502             status = avrcp_browsing_connect(device_addr, ertm_buffer, sizeof(ertm_buffer), &ertm_config, &browsing_cid);
503             break;
504         case 'E':
505             if (avrcp_browsing_connected){
506                 printf(" - AVRCP Browsing Service disconnect from addr %s.\n", bd_addr_to_str(device_addr));
507                 status = avrcp_browsing_disconnect(browsing_cid);
508                 break;
509             }
510             printf("AVRCP Browsing Service already disconnected\n");
511             break;
512         case '\n':
513         case '\r':
514             break;
515 
516         default:
517             if (!avrcp_browsing_connected){
518                 show_usage();
519                 break;
520             }
521 
522             switch (cmd) {
523                 case 'I':
524                     if (player_index < 0) {
525                         printf("AVRCP Browsing:Get media players first\n");
526                         break;
527                     }
528                     printf("AVRCP Browsing:Set addressed player\n");
529                     status = avrcp_controller_set_addressed_player(avrcp_cid, players[0]);
530                     break;
531                 case 'O':
532                     if (player_index < 0) {
533                         printf("AVRCP Browsing:Get media players first\n");
534                         break;
535                     }
536                     printf("Set browsed player\n");
537                     status = avrcp_browsing_controller_set_browsed_player(browsing_cid, players[0]);
538                     break;
539                 case 'p':
540                     printf("AVRCP Browsing: get media players\n");
541                     player_index = -1;
542                     status = avrcp_browsing_controller_get_media_players(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
543                     break;
544                 case 'Q':
545                     printf("AVRCP Browsing: browse folders\n");
546                     folder_index = -1;
547                     status = avrcp_browsing_controller_browse_file_system(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
548                     break;
549                 case 'P':
550                     printf("AVRCP Browsing: browse media items\n");
551                     avrcp_browsing_controller_browse_media(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
552                     break;
553                 case 'W':
554                     printf("AVRCP Browsing: go up one level\n");
555                     status = avrcp_browsing_controller_go_up_one_level(browsing_cid);
556                     folder_index = -1;
557                     break;
558                 case 'T':
559                     if (folder_index < 0 && !parent_folder_set){
560                         printf("AVRCP Browsing: no folders available\n");
561                         break;
562                     }
563                     if (!parent_folder_set){
564                         parent_folder_set = 1;
565                         memcpy(parent_folder_name, folders[0].name, folders[0].name_len);
566                         memcpy(parent_folder_uid, folders[0].uid, 8);
567                     }
568                     printf("AVRCP Browsing: go down one level of %s\n", (char *)parent_folder_name);
569                     status = avrcp_browsing_controller_go_down_one_level(browsing_cid, parent_folder_uid);
570                     folder_index = -1;
571                     break;
572                 default:
573                     show_usage();
574                     break;
575             }
576             break;
577     }
578 
579     if (status != ERROR_CODE_SUCCESS){
580         printf("AVRCP Browsing: Could not perform command, status 0x%2x\n", status);
581     }
582 }
583 #endif
584 /* EXAMPLE_END */
585