xref: /btstack/example/avrcp_browsing_client.c (revision db79553a49f499c029750f66c79b114af5410306)
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 
38462aa085SMilanka 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 
69*db79553aSMilanka Ringwald #define AVRCP_BROWSING_MAX_PLAYERS          10
70*db79553aSMilanka Ringwald #define AVRCP_BROWSING_MAX_FOLDERS          10
71*db79553aSMilanka Ringwald #define AVRCP_BROWSING_MAX_FOLDER_NAME_LEN  30
72*db79553aSMilanka Ringwald 
73462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN
74462aa085SMilanka Ringwald // mac 2011: static bd_addr_t remote = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3};
75462aa085SMilanka Ringwald // pts: static bd_addr_t remote = {0x00, 0x1B, 0xDC, 0x08, 0x0A, 0xA5};
76462aa085SMilanka Ringwald // mac 2013:
77462aa085SMilanka Ringwald // static const char * device_addr_string = "84:38:35:65:d1:15";
78462aa085SMilanka Ringwald // iPhone 5S: static const char * device_addr_string = "54:E4:3A:26:A2:39";
79462aa085SMilanka Ringwald // phone 2013:
80462aa085SMilanka Ringwald static const char * device_addr_string = "D8:BB:2C:DF:F1:08";
81462aa085SMilanka Ringwald static bd_addr_t device_addr;
82462aa085SMilanka Ringwald #endif
83462aa085SMilanka Ringwald 
84462aa085SMilanka Ringwald static uint16_t avrcp_cid = 0;
85462aa085SMilanka Ringwald static uint8_t  avrcp_connected = 0;
86462aa085SMilanka Ringwald 
87462aa085SMilanka Ringwald static uint16_t browsing_cid = 0;
88462aa085SMilanka Ringwald static uint8_t  avrcp_browsing_connected = 0;
89462aa085SMilanka Ringwald static uint8_t  sdp_avrcp_browsing_controller_service_buffer[200];
90462aa085SMilanka Ringwald 
91954cc391SMilanka Ringwald static uint8_t browsing_query_active = 0;
92954cc391SMilanka Ringwald static avrcp_media_item_context_t media_item_context;
93954cc391SMilanka Ringwald 
94*db79553aSMilanka Ringwald typedef struct {
95*db79553aSMilanka Ringwald     uint16_t  charset;
96*db79553aSMilanka Ringwald     uint8_t   depth;
97*db79553aSMilanka Ringwald     uint16_t  name_len;
98*db79553aSMilanka Ringwald     char      name[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN];
99*db79553aSMilanka Ringwald } avrcp_browsing_root_folder_t;
100*db79553aSMilanka Ringwald 
101*db79553aSMilanka Ringwald typedef struct {
102*db79553aSMilanka Ringwald     uint8_t  uid[8];
103*db79553aSMilanka Ringwald     uint16_t name_len;
104*db79553aSMilanka Ringwald     char     name[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN];
105*db79553aSMilanka Ringwald } avrcp_browsing_folders_t;
106*db79553aSMilanka Ringwald 
107*db79553aSMilanka Ringwald static uint8_t  parent_folder_set = 0;
108*db79553aSMilanka Ringwald static uint8_t  parent_folder_uid[8];
109*db79553aSMilanka Ringwald static char     parent_folder_name[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN];
110*db79553aSMilanka Ringwald static avrcp_browsing_folders_t folders[AVRCP_BROWSING_MAX_FOLDERS];
111*db79553aSMilanka Ringwald static int folder_index = -1;
112*db79553aSMilanka Ringwald static uint16_t players[AVRCP_BROWSING_MAX_PLAYERS];
113*db79553aSMilanka Ringwald static int player_index = -1;
114*db79553aSMilanka Ringwald 
115462aa085SMilanka Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration;
116462aa085SMilanka Ringwald 
117462aa085SMilanka Ringwald static uint8_t ertm_buffer[10000];
118462aa085SMilanka Ringwald static l2cap_ertm_config_t ertm_config = {
119462aa085SMilanka Ringwald     1,  // ertm mandatory
120462aa085SMilanka Ringwald     2,  // max transmit, some tests require > 1
121462aa085SMilanka Ringwald     2000,
122462aa085SMilanka Ringwald     12000,
123462aa085SMilanka Ringwald     144,    // l2cap ertm mtu
124462aa085SMilanka Ringwald     4,
125462aa085SMilanka Ringwald     4,
126462aa085SMilanka Ringwald };
127462aa085SMilanka Ringwald 
128462aa085SMilanka Ringwald 
129*db79553aSMilanka Ringwald static inline int next_index(int * index, int max_value){
130*db79553aSMilanka Ringwald     if ((*index) < max_value){
131*db79553aSMilanka Ringwald         (*index)++;
132*db79553aSMilanka Ringwald     } else {
133*db79553aSMilanka Ringwald         (*index) = 0;
134*db79553aSMilanka Ringwald     }
135*db79553aSMilanka Ringwald     return (*index);
136*db79553aSMilanka Ringwald }
137*db79553aSMilanka Ringwald 
138*db79553aSMilanka Ringwald static int next_folder_index(){
139*db79553aSMilanka Ringwald     return next_index(&folder_index, AVRCP_BROWSING_MAX_FOLDERS);
140*db79553aSMilanka Ringwald }
141*db79553aSMilanka Ringwald 
142*db79553aSMilanka Ringwald static int next_player_index(){
143*db79553aSMilanka Ringwald     return next_index(&player_index, AVRCP_BROWSING_MAX_PLAYERS);
144*db79553aSMilanka Ringwald }
145*db79553aSMilanka Ringwald 
146*db79553aSMilanka Ringwald 
147462aa085SMilanka Ringwald /* @section Main Application Setup
148462aa085SMilanka Ringwald  *
149462aa085SMilanka Ringwald  * @text The Listing MainConfiguration shows how to setup AVRCP Controller Browsing service.
150462aa085SMilanka Ringwald  * To announce AVRCP Controller Browsing service, you need to create corresponding
151462aa085SMilanka Ringwald  * SDP record and register it with the SDP service.
152462aa085SMilanka Ringwald  * You'll also need to register several packet handlers:
153462aa085SMilanka Ringwald  * - stdin_process callback - used to trigger AVRCP commands, such are get media players, playlists, albums, etc. Requires HAVE_BTSTACK_STDIN.
154462aa085SMilanka Ringwald  * - avrcp_browsing_controller_packet_handler - used to receive answers for AVRCP commands.
155462aa085SMilanka Ringwald  *
156462aa085SMilanka Ringwald  */
157462aa085SMilanka Ringwald 
158462aa085SMilanka Ringwald /* LISTING_START(MainConfiguration): Setup Audio Sink and AVRCP Controller services */
159462aa085SMilanka Ringwald static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
160462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN
161462aa085SMilanka Ringwald static void stdin_process(char cmd);
162462aa085SMilanka Ringwald #endif
163462aa085SMilanka Ringwald 
164462aa085SMilanka Ringwald #define BROWSING_ENABLED 1
165462aa085SMilanka Ringwald 
166462aa085SMilanka Ringwald int btstack_main(int argc, const char * argv[]);
167462aa085SMilanka Ringwald int btstack_main(int argc, const char * argv[]){
168462aa085SMilanka Ringwald     (void)argc;
169462aa085SMilanka Ringwald     (void)argv;
170462aa085SMilanka Ringwald 
171462aa085SMilanka Ringwald     // Register for HCI events.
172462aa085SMilanka Ringwald     hci_event_callback_registration.callback = &avrcp_browsing_controller_packet_handler;
173462aa085SMilanka Ringwald     hci_add_event_handler(&hci_event_callback_registration);
174462aa085SMilanka Ringwald 
175462aa085SMilanka Ringwald     // Initialize L2CAP.
176462aa085SMilanka Ringwald     l2cap_init();
177462aa085SMilanka Ringwald 
178462aa085SMilanka Ringwald     // Initialize AVRCP Controller.
179462aa085SMilanka Ringwald     avrcp_controller_init();
180462aa085SMilanka Ringwald     // Register AVRCP for HCI events.
181462aa085SMilanka Ringwald     avrcp_controller_register_packet_handler(&avrcp_browsing_controller_packet_handler);
182462aa085SMilanka Ringwald 
183247956eaSMilanka Ringwald     // Initialize AVRCP Browsing Controller, HCI events will be sent to the AVRCP Controller callback.
184462aa085SMilanka Ringwald     avrcp_browsing_controller_init();
185e30788a3SMilanka Ringwald     // // Register AVRCP for HCI events.
186e30788a3SMilanka Ringwald     // avrcp_browsing_controller_register_packet_handler(&avrcp_browsing_controller_packet_handler);
187462aa085SMilanka Ringwald 
188462aa085SMilanka Ringwald     // Initialize SDP.
189462aa085SMilanka Ringwald     sdp_init();
190462aa085SMilanka Ringwald 
191462aa085SMilanka Ringwald     // Create AVRCP service record and register it with SDP.
192462aa085SMilanka Ringwald     memset(sdp_avrcp_browsing_controller_service_buffer, 0, sizeof(sdp_avrcp_browsing_controller_service_buffer));
193462aa085SMilanka Ringwald     avrcp_controller_create_sdp_record(sdp_avrcp_browsing_controller_service_buffer, 0x10001, BROWSING_ENABLED, 1, NULL, NULL);
194462aa085SMilanka Ringwald     sdp_register_service(sdp_avrcp_browsing_controller_service_buffer);
195462aa085SMilanka Ringwald 
196462aa085SMilanka Ringwald     // Set local name with a template Bluetooth address, that will be automatically
197462aa085SMilanka Ringwald     // replaced with a actual address once it is available, i.e. when BTstack boots
198462aa085SMilanka Ringwald     // up and starts talking to a Bluetooth module.
199462aa085SMilanka Ringwald     gap_set_local_name("AVRCP Browsing Client 00:00:00:00:00:00");
200462aa085SMilanka Ringwald     gap_discoverable_control(1);
201462aa085SMilanka Ringwald     gap_set_class_of_device(0x200408);
202462aa085SMilanka Ringwald 
203462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN
204462aa085SMilanka Ringwald     // Parse human readable Bluetooth address.
205462aa085SMilanka Ringwald     sscanf_bd_addr(device_addr_string, device_addr);
206462aa085SMilanka Ringwald     btstack_stdin_setup(stdin_process);
207462aa085SMilanka Ringwald #endif
208462aa085SMilanka Ringwald     printf("Starting BTstack ...\n");
209462aa085SMilanka Ringwald     hci_power_control(HCI_POWER_ON);
210462aa085SMilanka Ringwald     return 0;
211462aa085SMilanka Ringwald }
212462aa085SMilanka Ringwald /* LISTING_END */
213462aa085SMilanka Ringwald 
214462aa085SMilanka Ringwald static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
215462aa085SMilanka Ringwald     UNUSED(channel);
216954cc391SMilanka Ringwald     int pos;
217462aa085SMilanka Ringwald 
218954cc391SMilanka Ringwald     switch(packet_type){
219954cc391SMilanka Ringwald         case AVRCP_BROWSING_DATA_PACKET:
220954cc391SMilanka Ringwald             pos = 0;
221954cc391SMilanka Ringwald             browsing_query_active = 1;
222954cc391SMilanka Ringwald             avrcp_browsing_item_type_t data_type = (avrcp_browsing_item_type_t)packet[pos++];
223954cc391SMilanka Ringwald             pos += 2; // length
224954cc391SMilanka Ringwald 
225954cc391SMilanka Ringwald             switch (data_type){
226*db79553aSMilanka Ringwald                 case AVRCP_BROWSING_MEDIA_ROOT_FOLDER:{
227*db79553aSMilanka Ringwald                     avrcp_browsing_root_folder_t root_folder;
228*db79553aSMilanka Ringwald                     root_folder.charset = big_endian_read_16(packet, pos);
229*db79553aSMilanka Ringwald                     pos += 2;
230*db79553aSMilanka Ringwald                     root_folder.depth = packet[pos++];
231*db79553aSMilanka Ringwald                     root_folder.name_len = big_endian_read_16(packet, pos);
232*db79553aSMilanka Ringwald                     pos += 2;
233*db79553aSMilanka Ringwald                     root_folder.name_len = btstack_max(big_endian_read_16(packet, pos), AVRCP_BROWSING_MAX_FOLDER_NAME_LEN - 1);
234*db79553aSMilanka Ringwald                     memset(root_folder.name, 0, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN);
235*db79553aSMilanka Ringwald                     memcpy(root_folder.name, packet+pos, root_folder.name_len);
236*db79553aSMilanka Ringwald                     printf("Found root folder: name %s, depth %d \n", (char *)root_folder.name, root_folder.depth);
237*db79553aSMilanka Ringwald                     break;
238*db79553aSMilanka Ringwald                 }
239954cc391SMilanka Ringwald                 case AVRCP_BROWSING_MEDIA_PLAYER_ITEM:{
240*db79553aSMilanka Ringwald                     printf("Received media player:  ");
241954cc391SMilanka Ringwald                     uint16_t player_id = big_endian_read_16(packet, pos);
242954cc391SMilanka Ringwald                     pos += 2;
243954cc391SMilanka Ringwald                     avrcp_browsing_media_player_major_type_t major_type = packet[pos++];
244954cc391SMilanka Ringwald                     avrcp_browsing_media_player_subtype_t subtype = big_endian_read_32(packet, pos);
245954cc391SMilanka Ringwald                     pos += 4;
246954cc391SMilanka Ringwald                     uint8_t status = packet[pos++];
247954cc391SMilanka Ringwald                     uint8_t feature_bitmask[16];
248954cc391SMilanka Ringwald                     memcpy(feature_bitmask, packet, 16);
249954cc391SMilanka Ringwald                     pos += 16;
250*db79553aSMilanka Ringwald                     printf("player ID 0x%04x, major_type %d, subtype %d, status %d\n", player_id, major_type, subtype, status);
251*db79553aSMilanka Ringwald                     players[next_player_index()] = player_id;
252954cc391SMilanka Ringwald                     break;
253954cc391SMilanka Ringwald                 }
254954cc391SMilanka Ringwald                 case AVRCP_BROWSING_FOLDER_ITEM:{
255*db79553aSMilanka Ringwald                     int index = next_folder_index();
256*db79553aSMilanka Ringwald                     printf("Found folder [%d]: ", index);
257*db79553aSMilanka Ringwald                     memcpy(folders[index].uid, packet+pos, 8);
258954cc391SMilanka Ringwald                     uint32_t folder_uid_high = big_endian_read_32(packet, pos);
259954cc391SMilanka Ringwald                     pos += 4;
260*db79553aSMilanka Ringwald                     uint32_t folder_uid_low  = big_endian_read_32(packet, pos);
261954cc391SMilanka Ringwald                     pos += 4;
262954cc391SMilanka Ringwald                     avrcp_browsing_folder_type_t folder_type = packet[pos++];
263954cc391SMilanka Ringwald                     uint8_t  is_playable = packet[pos++];
264954cc391SMilanka Ringwald                     uint16_t charset = big_endian_read_16(packet, pos);
265954cc391SMilanka Ringwald                     pos += 2;
266954cc391SMilanka Ringwald                     uint16_t displayable_name_length = big_endian_read_16(packet, pos);
267954cc391SMilanka Ringwald                     pos += 2;
268*db79553aSMilanka Ringwald                     char value[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN];
269*db79553aSMilanka Ringwald                     memset(value, 0, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN);
270*db79553aSMilanka Ringwald                     uint16_t value_len = btstack_max(displayable_name_length, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN - 1);
271954cc391SMilanka Ringwald                     memcpy(value, packet+pos, value_len);
272*db79553aSMilanka Ringwald 
273954cc391SMilanka Ringwald                     printf("Folder UID: 0x%08" PRIx32 "%08" PRIx32 ", folder_type 0x%02x, is_playable %d, charset 0x%02x, displayable_name_length %d, value %s\n",
274954cc391SMilanka Ringwald                         folder_uid_high, folder_uid_low, folder_type, is_playable, charset, displayable_name_length, value);
275*db79553aSMilanka Ringwald                     memcpy(folders[index].name, value, value_len);
276*db79553aSMilanka Ringwald                     folders[index].name_len = value_len;
277954cc391SMilanka Ringwald                     break;
278954cc391SMilanka Ringwald                 }
279954cc391SMilanka Ringwald                 case AVRCP_BROWSING_MEDIA_ELEMENT_ITEM:{
280*db79553aSMilanka Ringwald                     printf("Found media: ");
281954cc391SMilanka Ringwald 
282954cc391SMilanka Ringwald                     uint32_t media_uid_high = big_endian_read_32(packet, pos);
283954cc391SMilanka Ringwald                     pos += 4;
284954cc391SMilanka Ringwald                     uint32_t media_uid_low  = big_endian_read_32(packet, pos+4);
285954cc391SMilanka Ringwald                     pos += 4;
286954cc391SMilanka Ringwald                     avrcp_browsing_media_type_t media_type = packet[pos++];
287954cc391SMilanka Ringwald                     uint16_t charset = big_endian_read_16(packet, pos);
288954cc391SMilanka Ringwald                     pos += 2;
289954cc391SMilanka Ringwald                     uint16_t displayable_name_length = big_endian_read_16(packet, pos);
290954cc391SMilanka Ringwald                     pos += 2;
291954cc391SMilanka Ringwald                     pos += displayable_name_length;
292954cc391SMilanka 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);
293954cc391SMilanka Ringwald 
294954cc391SMilanka Ringwald                     uint8_t num_attributes = packet[pos++];
295954cc391SMilanka Ringwald                     printf("Num media attributes %d\n", num_attributes);
296954cc391SMilanka Ringwald 
297954cc391SMilanka 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)){
298954cc391SMilanka Ringwald                         uint32_t attr_id            = avrcp_media_item_iterator_get_attr_id(&media_item_context);
299954cc391SMilanka Ringwald                         uint16_t attr_charset       = avrcp_media_item_iterator_get_attr_charset(&media_item_context);
300954cc391SMilanka Ringwald                         uint16_t attr_value_length  = avrcp_media_item_iterator_get_attr_value_len(&media_item_context);
301954cc391SMilanka Ringwald                         const uint8_t * attr_value  = avrcp_media_item_iterator_get_attr_value(&media_item_context);
302954cc391SMilanka Ringwald 
303954cc391SMilanka Ringwald                         printf("Attr ID 0x%08" PRIx32 ", charset %d, attr_value_length %d, value %s", attr_id, attr_charset, attr_value_length, attr_value);
304954cc391SMilanka Ringwald                     }
305954cc391SMilanka Ringwald                     break;
306954cc391SMilanka Ringwald                 }
307954cc391SMilanka Ringwald 
308954cc391SMilanka Ringwald                 default:
309954cc391SMilanka Ringwald                     log_error("AVRCP browsing: unknown browsable item type 0%02x", data_type);
310954cc391SMilanka Ringwald                     break;
311954cc391SMilanka Ringwald             }
312954cc391SMilanka Ringwald             break;
313954cc391SMilanka Ringwald         case HCI_EVENT_PACKET:
314954cc391SMilanka Ringwald             if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return;
315462aa085SMilanka Ringwald             uint16_t local_cid;
316462aa085SMilanka Ringwald             uint8_t  status = 0xFF;
317954cc391SMilanka Ringwald             bd_addr_t address;
318462aa085SMilanka Ringwald 
319462aa085SMilanka Ringwald             switch (packet[2]){
320462aa085SMilanka Ringwald                 case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED: {
321462aa085SMilanka Ringwald                     local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet);
322462aa085SMilanka Ringwald                     if (browsing_cid != 0 && browsing_cid != local_cid) {
323*db79553aSMilanka Ringwald                         printf("AVRCP Controller connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
324462aa085SMilanka Ringwald                         return;
325462aa085SMilanka Ringwald                     }
326462aa085SMilanka Ringwald 
327462aa085SMilanka Ringwald                     status = avrcp_subevent_connection_established_get_status(packet);
328462aa085SMilanka Ringwald                     if (status != ERROR_CODE_SUCCESS){
329*db79553aSMilanka Ringwald                         printf("AVRCP Controller connection failed: status 0x%02x\n", status);
330462aa085SMilanka Ringwald                         browsing_cid = 0;
331462aa085SMilanka Ringwald                         return;
332462aa085SMilanka Ringwald                     }
333462aa085SMilanka Ringwald 
334462aa085SMilanka Ringwald                     avrcp_cid = local_cid;
335462aa085SMilanka Ringwald                     avrcp_connected = 1;
336954cc391SMilanka Ringwald                     avrcp_subevent_connection_established_get_bd_addr(packet, address);
337*db79553aSMilanka Ringwald                     printf("AVRCP Controller connected.\n");
338462aa085SMilanka Ringwald                     return;
339462aa085SMilanka Ringwald                 }
340462aa085SMilanka Ringwald                 case AVRCP_SUBEVENT_CONNECTION_RELEASED:
341*db79553aSMilanka Ringwald                     printf("AVRCP Browsing Client released.\n");
342462aa085SMilanka Ringwald                     browsing_cid = 0;
343462aa085SMilanka Ringwald                     avrcp_browsing_connected = 0;
344*db79553aSMilanka Ringwald                     folder_index = 0;
345*db79553aSMilanka Ringwald                     memset(folders, 0, sizeof(folders));
346462aa085SMilanka Ringwald                     return;
347462aa085SMilanka Ringwald                 case AVRCP_SUBEVENT_BROWSING_CONNECTION_ESTABLISHED: {
348462aa085SMilanka Ringwald                     local_cid = avrcp_subevent_browsing_connection_established_get_browsing_cid(packet);
349462aa085SMilanka Ringwald                     if (browsing_cid != 0 && browsing_cid != local_cid) {
350*db79553aSMilanka Ringwald                         printf("AVRCP Browsing Client connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
351462aa085SMilanka Ringwald                         return;
352462aa085SMilanka Ringwald                     }
353462aa085SMilanka Ringwald 
354462aa085SMilanka Ringwald                     status = avrcp_subevent_browsing_connection_established_get_status(packet);
355462aa085SMilanka Ringwald                     if (status != ERROR_CODE_SUCCESS){
356*db79553aSMilanka Ringwald                         printf("AVRCP Browsing Client connection failed: status 0x%02x\n", status);
357462aa085SMilanka Ringwald                         browsing_cid = 0;
358462aa085SMilanka Ringwald                         return;
359462aa085SMilanka Ringwald                     }
360462aa085SMilanka Ringwald 
361462aa085SMilanka Ringwald                     browsing_cid = local_cid;
362462aa085SMilanka Ringwald                     avrcp_browsing_connected = 1;
363954cc391SMilanka Ringwald                     avrcp_subevent_browsing_connection_established_get_bd_addr(packet, address);
364*db79553aSMilanka Ringwald                     printf("AVRCP Browsing Client connected\n");
365462aa085SMilanka Ringwald                     return;
366462aa085SMilanka Ringwald                 }
367462aa085SMilanka Ringwald                 case AVRCP_SUBEVENT_BROWSING_CONNECTION_RELEASED:
368*db79553aSMilanka Ringwald                     printf("AVRCP Browsing Client released\n");
369462aa085SMilanka Ringwald                     browsing_cid = 0;
370462aa085SMilanka Ringwald                     avrcp_browsing_connected = 0;
371462aa085SMilanka Ringwald                     return;
372954cc391SMilanka Ringwald                 case AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE:
373*db79553aSMilanka Ringwald                     browsing_query_active = 0;
374*db79553aSMilanka Ringwald                     if (avrcp_subevent_browsing_media_item_done_get_browsing_status(packet) != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
375*db79553aSMilanka Ringwald                         printf("AVRCP Browsing query done with browsing status 0x%02x, bluetooth status 0x%02x.\n",
376954cc391SMilanka Ringwald                             avrcp_subevent_browsing_media_item_done_get_browsing_status(packet),
377954cc391SMilanka Ringwald                             avrcp_subevent_browsing_media_item_done_get_bluetooth_status(packet));
378954cc391SMilanka Ringwald                         break;
379*db79553aSMilanka Ringwald                     }
380*db79553aSMilanka Ringwald                     printf("AVRCP Browsing cmd: DONE.\n");
381*db79553aSMilanka Ringwald                     break;
382462aa085SMilanka Ringwald                 default:
383462aa085SMilanka Ringwald                     printf("AVRCP Browsing Client: event is not parsed\n");
384462aa085SMilanka Ringwald                     break;
385462aa085SMilanka Ringwald             }
386954cc391SMilanka Ringwald             break;
387954cc391SMilanka Ringwald 
388954cc391SMilanka Ringwald         default:
389954cc391SMilanka Ringwald             break;
390954cc391SMilanka Ringwald     }
391954cc391SMilanka Ringwald 
392462aa085SMilanka Ringwald }
393462aa085SMilanka Ringwald 
394462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN
395462aa085SMilanka Ringwald static void show_usage(void){
396462aa085SMilanka Ringwald     bd_addr_t      iut_address;
397462aa085SMilanka Ringwald     gap_local_bd_addr(iut_address);
398462aa085SMilanka Ringwald     printf("\n--- Bluetooth AVRCP Controller Connection Test Console %s ---\n", bd_addr_to_str(iut_address));
399*db79553aSMilanka Ringwald     printf("e      - AVRCP Controller create connection to addr %s\n", bd_addr_to_str(device_addr));
400247956eaSMilanka Ringwald     printf("c      - AVRCP Browsing Controller create connection to addr %s\n", bd_addr_to_str(device_addr));
401247956eaSMilanka Ringwald     printf("C      - AVRCP Browsing Controller disconnect\n");
402*db79553aSMilanka Ringwald     printf("E      - AVRCP Controller disconnect\n");
403*db79553aSMilanka Ringwald 
404*db79553aSMilanka Ringwald     printf("a      - Set first found player as addressed player\n");
405*db79553aSMilanka Ringwald     printf("b      - Set first found player as browsed player\n");
406*db79553aSMilanka Ringwald 
407*db79553aSMilanka Ringwald     printf("m      - Get media players\n");
408*db79553aSMilanka Ringwald     printf("f      - Browse folders\n");
409*db79553aSMilanka Ringwald     printf("u      - Go up one level\n");
410*db79553aSMilanka Ringwald     printf("d      - Go down one level\n");
411*db79553aSMilanka Ringwald     printf("i      - Browse media items\n");
412462aa085SMilanka Ringwald     printf("---\n");
413462aa085SMilanka Ringwald }
414462aa085SMilanka Ringwald #endif
415462aa085SMilanka Ringwald 
416ed0df7b2SMilanka Ringwald 
417462aa085SMilanka Ringwald #ifdef HAVE_BTSTACK_STDIN
418462aa085SMilanka Ringwald static void stdin_process(char cmd){
419462aa085SMilanka Ringwald     uint8_t status = ERROR_CODE_SUCCESS;
420462aa085SMilanka Ringwald 
421954cc391SMilanka Ringwald     if (cmd != 'a' && cmd != 'A' && cmd != 'c' && cmd != 'C'){
422954cc391SMilanka Ringwald         if (browsing_query_active){
423954cc391SMilanka Ringwald             printf("Query active, try later!\n");
424954cc391SMilanka Ringwald             return;
425954cc391SMilanka Ringwald         }
426954cc391SMilanka Ringwald     }
427954cc391SMilanka Ringwald 
428462aa085SMilanka Ringwald     switch (cmd){
429*db79553aSMilanka Ringwald         case 'e':
430247956eaSMilanka Ringwald             printf(" - Create AVRCP connection for control to addr %s.\n", bd_addr_to_str(device_addr));
431462aa085SMilanka Ringwald             status = avrcp_controller_connect(device_addr, &avrcp_cid);
432462aa085SMilanka Ringwald             break;
433*db79553aSMilanka Ringwald         case 'E':
434247956eaSMilanka Ringwald             if (avrcp_connected){
435247956eaSMilanka Ringwald                 printf(" - AVRCP Controller disconnect from addr %s.\n", bd_addr_to_str(device_addr));
436247956eaSMilanka Ringwald                 status = avrcp_controller_disconnect(avrcp_cid);
437247956eaSMilanka Ringwald                 break;
438247956eaSMilanka Ringwald             }
439247956eaSMilanka Ringwald             printf("AVRCP Controller already disconnected\n");
440247956eaSMilanka Ringwald             break;
441247956eaSMilanka Ringwald 
442462aa085SMilanka Ringwald         case 'c':
443462aa085SMilanka Ringwald             if (!avrcp_connected) {
444247956eaSMilanka Ringwald                 printf(" You must first create AVRCP connection for control to addr %s.\n", bd_addr_to_str(device_addr));
445462aa085SMilanka Ringwald                 break;
446462aa085SMilanka Ringwald             }
447462aa085SMilanka Ringwald             printf(" - Create AVRCP connection for browsing to addr %s.\n", bd_addr_to_str(device_addr));
448462aa085SMilanka Ringwald             status = avrcp_browsing_controller_connect(device_addr, ertm_buffer, sizeof(ertm_buffer), &ertm_config, &browsing_cid);
449462aa085SMilanka Ringwald             break;
450462aa085SMilanka Ringwald         case 'C':
451247956eaSMilanka Ringwald             if (avrcp_browsing_connected){
452247956eaSMilanka Ringwald                 printf(" - AVRCP Browsing Controller disconnect from addr %s.\n", bd_addr_to_str(device_addr));
453462aa085SMilanka Ringwald                 status = avrcp_browsing_controller_disconnect(browsing_cid);
454462aa085SMilanka Ringwald                 break;
455247956eaSMilanka Ringwald             }
456247956eaSMilanka Ringwald             printf("AVRCP Browsing Controller already disconnected\n");
457247956eaSMilanka Ringwald             break;
458462aa085SMilanka Ringwald         case '\n':
459462aa085SMilanka Ringwald         case '\r':
460462aa085SMilanka Ringwald             break;
461*db79553aSMilanka Ringwald 
462*db79553aSMilanka Ringwald         default:
463*db79553aSMilanka Ringwald             if (!avrcp_browsing_connected){
464*db79553aSMilanka Ringwald                 show_usage();
465*db79553aSMilanka Ringwald                 printf("Please connect the AVRCP Browsing client\n");
466*db79553aSMilanka Ringwald                 break;
467*db79553aSMilanka Ringwald             }
468*db79553aSMilanka Ringwald 
469*db79553aSMilanka Ringwald             switch (cmd) {
470*db79553aSMilanka Ringwald                 case 'a':
471*db79553aSMilanka Ringwald                     if (player_index < 0) {
472*db79553aSMilanka Ringwald                         printf("Get media players first\n");
473*db79553aSMilanka Ringwald                         break;
474*db79553aSMilanka Ringwald                     }
475*db79553aSMilanka Ringwald                     printf("Set addressed player\n");
476*db79553aSMilanka Ringwald                     status = avrcp_browsing_controller_set_addressed_player(browsing_cid, players[0]);
477*db79553aSMilanka Ringwald                     break;
478*db79553aSMilanka Ringwald                 case 'b':
479*db79553aSMilanka Ringwald                     if (player_index < 0) {
480*db79553aSMilanka Ringwald                         printf("Get media players first\n");
481*db79553aSMilanka Ringwald                         break;
482*db79553aSMilanka Ringwald                     }
483*db79553aSMilanka Ringwald                     printf("Set browsed player\n");
484*db79553aSMilanka Ringwald                     status = avrcp_browsing_controller_set_browsed_player(browsing_cid, players[0]);
485*db79553aSMilanka Ringwald                     break;
486*db79553aSMilanka Ringwald                 case 'm':
487*db79553aSMilanka Ringwald                     printf("AVRCP Browsing: get media players\n");
488*db79553aSMilanka Ringwald                     player_index = -1;
489*db79553aSMilanka Ringwald                     status = avrcp_browsing_controller_get_media_players(browsing_cid);
490*db79553aSMilanka Ringwald                     break;
491*db79553aSMilanka Ringwald                 case 'f':
492*db79553aSMilanka Ringwald                     printf("AVRCP Browsing: browse folders\n");
493*db79553aSMilanka Ringwald                     folder_index = -1;
494*db79553aSMilanka Ringwald                     status = avrcp_browsing_controller_browse_file_system(browsing_cid);
495*db79553aSMilanka Ringwald                     break;
496*db79553aSMilanka Ringwald                 case 'i':
497*db79553aSMilanka Ringwald                     printf("AVRCP Browsing: browse media items\n");
498*db79553aSMilanka Ringwald                     avrcp_browsing_controller_browse_media(browsing_cid);
499*db79553aSMilanka Ringwald                     break;
500*db79553aSMilanka Ringwald                 case 'u':
501*db79553aSMilanka Ringwald                     if (folder_index < 0 && !parent_folder_set){
502*db79553aSMilanka Ringwald                         printf("AVRCP Browsing: no folders available\n");
503*db79553aSMilanka Ringwald                         break;
504*db79553aSMilanka Ringwald                     }
505*db79553aSMilanka Ringwald                     if (!parent_folder_set){
506*db79553aSMilanka Ringwald                         parent_folder_set = 1;
507*db79553aSMilanka Ringwald                         memcpy(parent_folder_name, folders[0].name, folders[0].name_len);
508*db79553aSMilanka Ringwald                         memcpy(parent_folder_uid, folders[0].uid, 8);
509*db79553aSMilanka Ringwald                     }
510*db79553aSMilanka Ringwald                     printf("AVRCP Browsing: go up one level of %s\n", (char *)parent_folder_name);
511*db79553aSMilanka Ringwald                     status = avrcp_browsing_controller_go_up_one_level(browsing_cid, parent_folder_uid);
512*db79553aSMilanka Ringwald                     folder_index = -1;
513*db79553aSMilanka Ringwald                     break;
514*db79553aSMilanka Ringwald                 case 'd':
515*db79553aSMilanka Ringwald                     if (folder_index < 0 && !parent_folder_set){
516*db79553aSMilanka Ringwald                         printf("AVRCP Browsing: no folders available\n");
517*db79553aSMilanka Ringwald                         break;
518*db79553aSMilanka Ringwald                     }
519*db79553aSMilanka Ringwald                     if (!parent_folder_set){
520*db79553aSMilanka Ringwald                         parent_folder_set = 1;
521*db79553aSMilanka Ringwald                         memcpy(parent_folder_name, folders[0].name, folders[0].name_len);
522*db79553aSMilanka Ringwald                         memcpy(parent_folder_uid, folders[0].uid, 8);
523*db79553aSMilanka Ringwald                     }
524*db79553aSMilanka Ringwald                     printf("AVRCP Browsing: go down one level of %s\n", (char *)parent_folder_name);
525*db79553aSMilanka Ringwald                     status = avrcp_browsing_controller_go_down_one_level(browsing_cid, parent_folder_uid);
526*db79553aSMilanka Ringwald                     folder_index = -1;
527*db79553aSMilanka Ringwald                     break;
528462aa085SMilanka Ringwald                 default:
529462aa085SMilanka Ringwald                     show_usage();
530*db79553aSMilanka Ringwald                     break;
531*db79553aSMilanka Ringwald             }
532*db79553aSMilanka Ringwald             break;
533462aa085SMilanka Ringwald     }
534ed0df7b2SMilanka Ringwald 
535462aa085SMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
536462aa085SMilanka Ringwald         printf("Could not perform command, status 0x%2x\n", status);
537462aa085SMilanka Ringwald     }
538462aa085SMilanka Ringwald }
539462aa085SMilanka Ringwald #endif
540462aa085SMilanka Ringwald /* EXAMPLE_END */
541