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 BLUEKITCHEN
24 * GMBH 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): AVRCP Browsing - Browse Media Players and Media Information
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 #ifndef ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
70 #error "AVRCP Browsing requires L2CAP ERTM, please add ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE to your btstack_config.h"
71 #endif
72
73 #define AVRCP_BROWSING_ENABLED
74
75 #define AVRCP_BROWSING_MAX_PLAYERS 10
76 #define AVRCP_BROWSING_MAX_FOLDERS 10
77 #define AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN 30
78 #define AVRCP_BROWSING_MAX_MEDIA_ITEMS 10
79
80 #ifdef HAVE_BTSTACK_STDIN
81 // mac 2011: static bd_addr_t remote = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3};
82 // pts: static bd_addr_t remote = {0x00, 0x1B, 0xDC, 0x08, 0x0A, 0xA5};
83 // mac 2013:
84 // static const char * device_addr_string = "84:38:35:65:d1:15";
85 // iPhone 5S: static const char * device_addr_string = "54:E4:3A:26:A2:39";
86 // phone 2013:
87 // static const char * device_addr_string = "B0:34:95:CB:97:C4";
88 // iPod
89 // static const char * device_addr_string = "B0:34:95:CB:97:C4";
90 // iPhone
91 static const char * device_addr_string = "6C:72:E7:10:22:EE";
92
93 static bd_addr_t device_addr;
94 #endif
95
96 typedef enum {
97 AVRCP_BROWSING_STATE_IDLE,
98 AVRCP_BROWSING_STATE_W4_GET_PLAYERS,
99 AVRCP_BROWSING_STATE_W4_SET_PLAYER,
100 AVRCP_BROWSING_STATE_READY
101 } avrcp_browsing_state_t;
102
103 typedef struct {
104 uint16_t charset;
105 uint8_t depth;
106 uint16_t name_len;
107 char name[AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN];
108 } avrcp_browsing_root_folder_t;
109
110 typedef struct {
111 uint8_t uid[8];
112 uint16_t name_len;
113 char name[AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN];
114 } avrcp_browsable_item_t;
115
116 typedef struct {
117 uint16_t player_id;
118 uint8_t major_player_type;
119 uint32_t player_sub_type;
120 uint8_t play_status;
121 uint8_t feature_bitmask[16];
122 uint16_t charset;
123 uint16_t name_len;
124 char name[AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN];
125 } avrcp_browsable_media_player_item_t;
126
127 typedef struct {
128 uint32_t id;
129 uint16_t charset;
130 uint16_t value_len;
131 char value[AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN];
132 } avrcp_browsable_media_element_item_attribute_t;
133
134 typedef struct {
135 uint8_t uid[8];
136 uint8_t media_type;
137 uint16_t charset;
138 uint16_t name_len;
139 char name[AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN];
140 uint8_t num_attributes;
141 } avrcp_browsable_media_element_item_t;
142
143 static avrcp_browsing_state_t browsing_state = AVRCP_BROWSING_STATE_IDLE;
144 static uint16_t avrcp_cid = 0;
145 static bool avrcp_connected = false;
146
147 static uint16_t browsing_cid = 0;
148 static bool browsing_connected = false;
149
150 static uint8_t sdp_avrcp_browsing_controller_service_buffer[200];
151 static uint8_t sdp_avdtp_sink_service_buffer[150];
152
153 static uint16_t a2dp_cid = 0;
154 static uint8_t a2dp_local_seid = 0;
155
156 static uint8_t media_sbc_codec_capabilities[] = {
157 0xFF,//(AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO,
158 0xFF,//(AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS,
159 2, 53
160 };
161
162 static uint8_t media_sbc_codec_configuration[] = {
163 (AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO,
164 (AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS,
165 2, 53
166 };
167
168 static bool browsing_query_active = false;
169 // static avrcp_media_item_context_t media_item_context;
170
171 static int playable_folder_index = 0;
172
173
174 static uint16_t browsing_uid_counter = 0;
175
176 static uint8_t parent_folder_set = 0;
177 static uint8_t parent_folder_uid[8];
178 static char parent_folder_name[AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN];
179
180 static avrcp_browsable_item_t folders[AVRCP_BROWSING_MAX_FOLDERS];
181 static int folder_index = -1;
182
183 static avrcp_browsable_media_element_item_t media_element_items[AVRCP_BROWSING_MAX_MEDIA_ITEMS];
184 static int media_element_item_index = -1;
185
186 static avrcp_browsable_media_player_item_t media_player_items[AVRCP_BROWSING_MAX_MEDIA_ITEMS];
187 static int media_player_item_index = -1;
188
189 static btstack_packet_callback_registration_t hci_event_callback_registration;
190
191 static uint8_t ertm_buffer[10000];
192 static l2cap_ertm_config_t ertm_config = {
193 1, // ertm mandatory
194 2, // max transmit, some tests require > 1
195 2000,
196 12000,
197 144, // l2cap ertm mtu
198 4,
199 4,
200 1, // 16-bit FCS
201 };
202
203
next_index(int * index,int max_value)204 static inline int next_index(int * index, int max_value){
205 if ((*index) < max_value){
206 (*index)++;
207 } else {
208 (*index) = 0;
209 }
210 return (*index);
211 }
212
next_folder_index(void)213 static int next_folder_index(void){
214 return next_index(&folder_index, AVRCP_BROWSING_MAX_FOLDERS);
215 }
216
next_media_element_item_index(void)217 static int next_media_element_item_index(void){
218 return next_index(&media_element_item_index, AVRCP_BROWSING_MAX_MEDIA_ITEMS);
219 }
next_media_player_item_index(void)220 static int next_media_player_item_index(void){
221 return next_index(&media_player_item_index, AVRCP_BROWSING_MAX_MEDIA_ITEMS);
222 }
223
224 /* @section Main Application Setup
225 *
226 * @text The Listing MainConfiguration shows how to setup AVRCP Controller Browsing service.
227 * To announce AVRCP Controller Browsing service, you need to create corresponding
228 * SDP record and register it with the SDP service.
229 * You'll also need to register several packet handlers:
230 * - stdin_process callback - used to trigger AVRCP commands, such are get media players, playlists, albums, etc. Requires HAVE_BTSTACK_STDIN.
231 * - avrcp_browsing_controller_packet_handler - used to receive answers for AVRCP commands.
232 *
233 */
234
235 /* LISTING_START(MainConfiguration): Setup Audio Sink and AVRCP Controller services */
236 static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
237 static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
238 static void a2dp_sink_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
239
240 #ifdef HAVE_BTSTACK_STDIN
241 static void stdin_process(char cmd);
242 #endif
243
244
245 int btstack_main(int argc, const char * argv[]);
btstack_main(int argc,const char * argv[])246 int btstack_main(int argc, const char * argv[]){
247 (void)argc;
248 (void)argv;
249
250 // Initialize L2CAP.
251 l2cap_init();
252
253 #ifdef ENABLE_BLE
254 // Initialize LE Security Manager. Needed for cross-transport key derivation
255 sm_init();
256 #endif
257
258 a2dp_sink_init();
259 a2dp_sink_register_packet_handler(&a2dp_sink_packet_handler);
260
261 avdtp_stream_endpoint_t * local_stream_endpoint = a2dp_sink_create_stream_endpoint(AVDTP_AUDIO,
262 AVDTP_CODEC_SBC, media_sbc_codec_capabilities, sizeof(media_sbc_codec_capabilities),
263 media_sbc_codec_configuration, sizeof(media_sbc_codec_configuration));
264 if (!local_stream_endpoint){
265 printf("A2DP Sink: not enough memory to create local stream endpoint\n");
266 return 1;
267 }
268 a2dp_local_seid = avdtp_local_seid(local_stream_endpoint);
269
270 // Initialize AVRCP service.
271 avrcp_init();
272 // Initialize AVRCP Controller & Target Service.
273 avrcp_controller_init();
274 avrcp_target_init();
275
276 avrcp_register_packet_handler(&avrcp_packet_handler);
277 avrcp_controller_register_packet_handler(&avrcp_packet_handler);
278 avrcp_target_register_packet_handler(&avrcp_packet_handler);
279
280 // Initialize AVRCP Browsing Service.
281 avrcp_browsing_init();
282 avrcp_browsing_controller_init();
283 avrcp_browsing_target_init();
284
285 // Register for HCI events.
286 avrcp_browsing_controller_register_packet_handler(&avrcp_browsing_controller_packet_handler);
287 avrcp_browsing_target_register_packet_handler(&avrcp_browsing_controller_packet_handler);
288 avrcp_browsing_register_packet_handler(&avrcp_browsing_controller_packet_handler);
289
290 // Initialize SDP.
291 sdp_init();
292 // setup AVDTP sink
293 memset(sdp_avdtp_sink_service_buffer, 0, sizeof(sdp_avdtp_sink_service_buffer));
294 a2dp_sink_create_sdp_record(sdp_avdtp_sink_service_buffer, sdp_create_service_record_handle(), AVDTP_SINK_FEATURE_MASK_HEADPHONE, NULL, NULL);
295 btstack_assert(de_get_len( sdp_avdtp_sink_service_buffer) <= sizeof(sdp_avdtp_sink_service_buffer));
296 sdp_register_service(sdp_avdtp_sink_service_buffer);
297
298 // Create AVRCP service record and register it with SDP.
299 memset(sdp_avrcp_browsing_controller_service_buffer, 0, sizeof(sdp_avrcp_browsing_controller_service_buffer));
300
301 uint16_t supported_features = AVRCP_FEATURE_MASK_CATEGORY_PLAYER_OR_RECORDER;
302 #ifdef AVRCP_BROWSING_ENABLED
303 supported_features |= AVRCP_FEATURE_MASK_BROWSING;
304 #endif
305 avrcp_controller_create_sdp_record(sdp_avrcp_browsing_controller_service_buffer, sdp_create_service_record_handle(), supported_features, NULL, NULL);
306 btstack_assert(de_get_len( sdp_avrcp_browsing_controller_service_buffer) <= sizeof(sdp_avrcp_browsing_controller_service_buffer));
307 sdp_register_service(sdp_avrcp_browsing_controller_service_buffer);
308
309 // Set local name with a template Bluetooth address, that will be automatically
310 // replaced with a actual address once it is available, i.e. when BTstack boots
311 // up and starts talking to a Bluetooth module.
312 gap_set_local_name("AVRCP Browsing Client 00:00:00:00:00:00");
313 gap_discoverable_control(1);
314 gap_set_class_of_device(0x200408);
315
316 // Register for HCI events.
317 hci_event_callback_registration.callback = &avrcp_browsing_controller_packet_handler;
318 hci_add_event_handler(&hci_event_callback_registration);
319
320
321 #ifdef HAVE_BTSTACK_STDIN
322 // Parse human readable Bluetooth address.
323 sscanf_bd_addr(device_addr_string, device_addr);
324 btstack_stdin_setup(stdin_process);
325 #endif
326 printf("Starting BTstack ...\n");
327 hci_power_control(HCI_POWER_ON);
328 return 0;
329 }
330 /* LISTING_END */
331
avrcp_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)332 static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
333 UNUSED(channel);
334 UNUSED(size);
335 uint16_t local_cid;
336 uint8_t status = 0xFF;
337 bd_addr_t adress;
338
339 if (packet_type != HCI_EVENT_PACKET) return;
340 if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return;
341 switch (packet[2]){
342 case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED: {
343 local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet);
344 status = avrcp_subevent_connection_established_get_status(packet);
345 if (status != ERROR_CODE_SUCCESS){
346 printf("AVRCP: Connection failed, status 0x%02x\n", status);
347 avrcp_cid = 0;
348 return;
349 }
350
351 avrcp_cid = local_cid;
352 avrcp_connected = true;
353 avrcp_subevent_connection_established_get_bd_addr(packet, adress);
354 printf("AVRCP: Connected to %s, cid 0x%02x\n", bd_addr_to_str(adress), avrcp_cid);
355 return;
356 }
357
358 case AVRCP_SUBEVENT_CONNECTION_RELEASED:
359 printf("AVRCP: Channel released: cid 0x%02x\n", avrcp_subevent_connection_released_get_avrcp_cid(packet));
360 avrcp_cid = 0;
361 avrcp_connected = false;
362 return;
363 default:
364 break;
365 }
366 }
367
a2dp_sink_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)368 static void a2dp_sink_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
369 UNUSED(channel);
370 UNUSED(size);
371 bd_addr_t address;
372 uint8_t status;
373
374 if (packet_type != HCI_EVENT_PACKET) return;
375 if (hci_event_packet_get_type(packet) != HCI_EVENT_A2DP_META) return;
376
377 switch (packet[2]){
378 case A2DP_SUBEVENT_STREAM_ESTABLISHED:
379 a2dp_subevent_stream_established_get_bd_addr(packet, address);
380 status = a2dp_subevent_stream_established_get_status(packet);
381
382 if (status != ERROR_CODE_SUCCESS){
383 printf("A2DP Sink : Streaming connection failed, status 0x%02x\n", status);
384 break;
385 }
386
387 a2dp_cid = a2dp_subevent_stream_established_get_a2dp_cid(packet);
388 memcpy(device_addr, address, 6);
389 printf("A2DP Sink: Connection established, address %s, a2dp_cid 0x%02x\n", bd_addr_to_str(address), a2dp_cid);
390 break;
391
392 case A2DP_SUBEVENT_SIGNALING_CONNECTION_RELEASED:
393 printf("A2DP Sink: Connection released\n");
394 break;
395
396 default:
397 break;
398 }
399 }
400
401
avrcp_browsing_controller_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)402 static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
403 UNUSED(channel);
404 UNUSED(size);
405 bd_addr_t address;
406 uint8_t status;
407 uint16_t pos = 0;
408 uint16_t local_cid;
409
410 // printf("avrcp_browsing_controller_packet_handler packet type 0x%02X, subevent 0x%02X\n", packet_type, packet[2]);
411 switch (packet_type) {
412 case HCI_EVENT_PACKET:
413 switch (packet[0]){
414 case HCI_EVENT_AVRCP_META:
415 switch (packet[2]){
416 case AVRCP_SUBEVENT_BROWSING_CONNECTION_ESTABLISHED: {
417 local_cid = avrcp_subevent_browsing_connection_established_get_browsing_cid(packet);
418 status = avrcp_subevent_browsing_connection_established_get_status(packet);
419 if (status != ERROR_CODE_SUCCESS){
420 printf("AVRCP: Connection failed, status 0x%02x\n", status);
421 browsing_cid = 0;
422 return;
423 }
424 avrcp_subevent_browsing_connection_established_get_bd_addr(packet, address);
425 browsing_cid = local_cid;
426 browsing_connected = true;
427 printf("AVRCP Browsing: Connection established, address %s, browsing_cid 0x%02x\n", bd_addr_to_str(address), browsing_cid);
428 return;
429 }
430
431 case AVRCP_SUBEVENT_BROWSING_CONNECTION_RELEASED:
432 printf("AVRCP: Connection released, cid 0x%02x\n", avrcp_subevent_browsing_connection_released_get_browsing_cid(packet));
433 browsing_cid = 0;
434 browsing_connected = false;
435 return;
436 case AVRCP_SUBEVENT_BROWSING_DONE:
437
438 browsing_query_active = 0;
439 browsing_uid_counter = 0;
440 if (avrcp_subevent_browsing_done_get_browsing_status(packet) != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
441 printf("AVRCP Browsing query done with browsing status 0x%02x, bluetooth status 0x%02x.\n",
442 avrcp_subevent_browsing_done_get_browsing_status(packet),
443 avrcp_subevent_browsing_done_get_bluetooth_status(packet));
444 return;
445 }
446 browsing_uid_counter = avrcp_subevent_browsing_done_get_uid_counter(packet);
447 printf("DONE, browsing_uid_counter %d.\n", browsing_uid_counter);
448
449 switch (browsing_state){
450 case AVRCP_BROWSING_STATE_W4_GET_PLAYERS:
451 if (media_player_item_index < 0) {
452 printf("Get media players first\n");
453 break;
454 }
455 printf("Set browsed player\n");
456 browsing_state = AVRCP_BROWSING_STATE_W4_SET_PLAYER;
457 status = avrcp_browsing_controller_set_browsed_player(browsing_cid, media_player_items[0].player_id);
458 if (status != ERROR_CODE_SUCCESS){
459 printf("Could not set player, status 0x%02x\n", status);
460 status = AVRCP_BROWSING_STATE_W4_GET_PLAYERS;
461 break;
462 }
463 break;
464 case AVRCP_BROWSING_STATE_W4_SET_PLAYER:
465 browsing_state = AVRCP_BROWSING_STATE_READY;
466 break;
467 default:
468 break;
469 }
470 break;
471 default:
472 break;
473 }
474
475 default:
476 break;
477 }
478 break;
479
480 case AVRCP_BROWSING_DATA_PACKET:
481 browsing_query_active = 1;
482 avrcp_browsing_item_type_t data_type = (avrcp_browsing_item_type_t)packet[pos++];
483
484 switch (data_type){
485 case AVRCP_BROWSING_MEDIA_ROOT_FOLDER:{
486 uint16_t folder_name_length = size - pos;
487 char folder_name[AVRCP_MAX_FOLDER_NAME_SIZE];
488 memcpy(folder_name, &packet[pos], folder_name_length);
489 folder_name[folder_name_length] = 0;
490 printf("Found root folder: name %s \n", folder_name);
491 break;
492 }
493
494 case AVRCP_BROWSING_FOLDER_ITEM:{
495 int index = next_folder_index();
496 memcpy(folders[index].uid, packet+pos, 8);
497
498 uint32_t folder_uid_high = big_endian_read_32(packet, pos);
499 pos += 4;
500 uint32_t folder_uid_low = big_endian_read_32(packet, pos);
501 pos += 4;
502 avrcp_browsing_folder_type_t folder_type = packet[pos++];
503 uint8_t is_playable = packet[pos++];
504 uint16_t charset = big_endian_read_16(packet, pos);
505 pos += 2;
506 uint16_t displayable_name_length = big_endian_read_16(packet, pos);
507 pos += 2;
508
509 char value[AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN];
510 memset(value, 0, AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN);
511 uint16_t value_len = btstack_min(displayable_name_length, AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN - 1);
512 memcpy(value, packet+pos, value_len);
513
514 printf("Folder UID 0x%08" PRIx32 "%08" PRIx32 ", type 0x%02x, is_playable %d, charset 0x%02x, name %s\n",
515 folder_uid_high, folder_uid_low, folder_type, is_playable, charset, value);
516 if (is_playable){
517 playable_folder_index = index;
518 }
519 memcpy(folders[index].name, value, value_len);
520 folders[index].name_len = value_len;
521 break;
522 }
523
524 case AVRCP_BROWSING_MEDIA_PLAYER_ITEM:{
525 printf("Received media player item: ");
526 uint16_t player_id = big_endian_read_16(packet, pos);
527 pos += 2;
528 avrcp_browsing_media_player_major_type_t major_type = packet[pos++];
529 avrcp_browsing_media_player_subtype_t subtype = big_endian_read_32(packet, pos);
530 pos += 4;
531 status = packet[pos++];
532 uint8_t feature_bitmask[16];
533 memcpy(feature_bitmask, packet, 16);
534 pos += 16;
535 printf("player ID 0x%04x, major_type %d, subtype %d, status 0x%02x\n", player_id, major_type, subtype, status);
536 media_player_items[next_media_player_item_index()].player_id = player_id;
537 break;
538 }
539
540 case AVRCP_BROWSING_MEDIA_ELEMENT_ITEM:{
541 int index = next_media_element_item_index();
542 memcpy(media_element_items[index].uid, packet+pos, 8);
543 printf("Received media element item UID (index %d): ", index);
544
545 // uint32_t media_uid_high = big_endian_read_32(packet, pos);
546 pos += 4;
547 // uint32_t media_uid_low = big_endian_read_32(packet, pos+4);
548 pos += 4;
549
550 avrcp_browsing_media_type_t media_type = packet[pos++];
551 uint16_t charset = big_endian_read_16(packet, pos);
552 pos += 2;
553 uint16_t displayable_name_length = big_endian_read_16(packet, pos);
554 pos += 2;
555
556 char value[AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN];
557 memset(value, 0, AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN);
558 uint16_t value_len = btstack_min(displayable_name_length, AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN - 1);
559 memcpy(value, packet+pos, value_len);
560 memcpy(media_element_items[index].name, value, value_len);
561
562 pos += displayable_name_length;
563 // printf("Media UID: 0x%08" PRIx32 "%08" PRIx32 ", media_type 0x%02x, charset 0x%02x, actual len %d, name %s\n", media_uid_high, media_uid_low, media_type, charset, value_len, value);
564
565 printf_hexdump(media_element_items[index].uid, 8);
566 uint8_t num_attributes = packet[pos++];
567 printf(" Media type 0x%02x, charset 0x%02x, actual len %d, name %s, num attributes %d:\n", media_type, charset, value_len, value, num_attributes);
568
569 avrcp_media_item_context_t media_item_context;
570 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)){
571 uint32_t attr_id = avrcp_media_item_iterator_get_attr_id(&media_item_context);
572 uint16_t attr_charset = avrcp_media_item_iterator_get_attr_charset(&media_item_context);
573 uint16_t attr_value_length = avrcp_media_item_iterator_get_attr_value_len(&media_item_context);
574 const uint8_t * attr_value = avrcp_media_item_iterator_get_attr_value(&media_item_context);
575
576 memset(value, 0, AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN);
577 value_len = btstack_min(attr_value_length, AVRCP_BROWSING_MAX_BROWSABLE_ITEM_NAME_LEN - 1);
578 memcpy(value, attr_value, value_len);
579
580 printf(" - attr ID 0x%08" PRIx32 ", charset 0x%02x, actual len %d, name %s\n", attr_id, attr_charset, value_len, value);
581 }
582 break;
583 }
584
585 case AVRCP_BROWSING_MEDIA_ELEMENT_ITEM_ATTRIBUTE:{
586 uint8_t num_attributes = packet[pos++];
587 printf("Num media attributes %d:\n", num_attributes);
588 avrcp_media_item_context_t media_item_context;
589 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)){
590 uint32_t attr_id = avrcp_media_item_iterator_get_attr_id(&media_item_context);
591 uint16_t attr_charset = avrcp_media_item_iterator_get_attr_charset(&media_item_context);
592 uint16_t attr_value_length = avrcp_media_item_iterator_get_attr_value_len(&media_item_context);
593 const uint8_t * attr_value = avrcp_media_item_iterator_get_attr_value(&media_item_context);
594 printf(" - attr ID 0x%08" PRIx32 ", charset 0x%02x, actual len %d, name %s\n", attr_id, attr_charset, attr_value_length, attr_value);
595 }
596 break;
597 }
598 default:
599 printf("AVRCP browsing: unknown browsable item type 0%02x\n", data_type);
600 break;
601 }
602 break;
603 default:
604 break;
605 }
606 }
607
608 #ifdef HAVE_BTSTACK_STDIN
show_usage(void)609 static void show_usage(void){
610 bd_addr_t iut_address;
611 gap_local_bd_addr(iut_address);
612 printf("\n--- Bluetooth AVRCP Browsing Service Test Console %s ---\n", bd_addr_to_str(iut_address));
613 printf("c - AVRCP Service create connection to addr %s\n", bd_addr_to_str(device_addr));
614 printf("C - AVRCP Service disconnect\n");
615 printf("e - AVRCP Browsing Service create connection to addr %s\n", bd_addr_to_str(device_addr));
616 printf("E - AVRCP Browsing Service disconnect\n");
617
618 printf("I - Set first found player as addressed player\n");
619 printf("O - Set first found player as browsed player\n");
620
621 printf("p - Get media players\n");
622 printf("Q - Browse folders\n");
623 printf("P - Go up one level\n");
624 printf("W - Go down one level\n");
625 printf("T - Browse media items\n");
626 printf("---\n");
627 }
628 #endif
629
630
631 #ifdef HAVE_BTSTACK_STDIN
stdin_process(char cmd)632 static void stdin_process(char cmd){
633 uint8_t status = ERROR_CODE_SUCCESS;
634
635 if (cmd != 'a' && cmd != 'A' && cmd != 'c' && cmd != 'C'){
636 if (browsing_query_active){
637 printf("Query active, try later!\n");
638 return;
639 }
640 }
641
642 switch (cmd){
643 case 'b':
644 status = a2dp_sink_establish_stream(device_addr, &a2dp_cid);
645 printf(" - Create AVDTP connection to addr %s, and local seid %d, expected cid 0x%02x.\n", bd_addr_to_str(device_addr), a2dp_local_seid, a2dp_cid);
646 break;
647 case 'B':
648 printf(" - AVDTP disconnect from addr %s.\n", bd_addr_to_str(device_addr));
649 a2dp_sink_disconnect(a2dp_cid);
650 break;
651
652 case 'c':
653 printf(" - Connect to AVRCP Service on addr %s.\n", bd_addr_to_str(device_addr));
654 status = avrcp_connect(device_addr, &avrcp_cid);
655 break;
656 case 'C':
657 if (avrcp_connected){
658 printf(" - AVRCP Service disconnect from addr %s.\n", bd_addr_to_str(device_addr));
659 status = avrcp_disconnect(avrcp_cid);
660 break;
661 }
662 printf("AVRCP Service already disconnected\n");
663 break;
664
665 case 'e':
666 if (!avrcp_connected) {
667 printf(" You must first connect to AVRCP Service on addr %s.\n", bd_addr_to_str(device_addr));
668 break;
669 }
670 printf(" - Connect to AVRCP Browsing Service at addr %s.\n", bd_addr_to_str(device_addr));
671 status = avrcp_browsing_connect(device_addr, ertm_buffer, sizeof(ertm_buffer), &ertm_config, &browsing_cid);
672 break;
673 case 'E':
674 if (browsing_connected){
675 printf(" - AVRCP Browsing Service disconnect from addr %s.\n", bd_addr_to_str(device_addr));
676 status = avrcp_browsing_disconnect(browsing_cid);
677 break;
678 }
679 printf("AVRCP Browsing Service already disconnected\n");
680 break;
681 case '\n':
682 case '\r':
683 break;
684
685 default:
686 if (!browsing_connected){
687 show_usage();
688 break;
689 }
690
691 switch (cmd) {
692 case 'I':
693 if (media_player_item_index < 0) {
694 printf("AVRCP Browsing:Get media players first\n");
695 break;
696 }
697 printf("AVRCP Browsing:Set addressed player\n");
698 status = avrcp_controller_set_addressed_player(avrcp_cid, media_player_items[0].player_id);
699 break;
700 case 'O':
701 if (media_player_item_index < 0) {
702 printf("AVRCP Browsing:Get media players first\n");
703 break;
704 }
705 printf("Set browsed player\n");
706 status = avrcp_browsing_controller_set_browsed_player(browsing_cid, media_player_items[0].player_id);
707 break;
708 case 'p':
709 printf("AVRCP Browsing: get media players\n");
710 media_player_item_index = -1;
711 status = avrcp_browsing_controller_get_media_players(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
712 break;
713 case 'Q':
714 printf("AVRCP Browsing: browse folders\n");
715 folder_index = -1;
716 status = avrcp_browsing_controller_browse_file_system(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
717 break;
718 case 'P':
719 printf("AVRCP Browsing: browse media items\n");
720 avrcp_browsing_controller_browse_media(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
721 break;
722 case 'W':
723 printf("AVRCP Browsing: go up one level\n");
724 status = avrcp_browsing_controller_go_up_one_level(browsing_cid);
725 folder_index = -1;
726 break;
727 case 'T':
728 if (folder_index < 0 && !parent_folder_set){
729 printf("AVRCP Browsing: no folders available\n");
730 break;
731 }
732 if (!parent_folder_set){
733 parent_folder_set = 1;
734 memcpy(parent_folder_name, folders[0].name, folders[0].name_len);
735 memcpy(parent_folder_uid, folders[0].uid, 8);
736 }
737 printf("AVRCP Browsing: go down one level of %s\n", (char *)parent_folder_name);
738 status = avrcp_browsing_controller_go_down_one_level(browsing_cid, parent_folder_uid);
739 folder_index = -1;
740 break;
741 default:
742 show_usage();
743 break;
744 }
745 break;
746 }
747
748 if (status != ERROR_CODE_SUCCESS){
749 printf("AVRCP Browsing: Could not perform command, status 0x%2x\n", status);
750 }
751 }
752 #endif
753 /* EXAMPLE_END */
754