avrcp browsing: AVRCP msg fragmentation

This commit is contained in:
Milanka Ringwald 2018-01-10 16:56:37 +01:00
parent d5e694a307
commit ad75da140d
5 changed files with 305 additions and 222 deletions

View File

@ -286,6 +286,9 @@ typedef struct {
avctp_connection_state_t state;
uint8_t wait_to_send;
uint8_t transaction_label;
// used for AVCTP fragmentation
uint8_t num_packets;
uint16_t bytes_to_send;
uint8_t *ertm_buffer;
uint32_t ertm_buffer_size;

View File

@ -357,19 +357,6 @@ static int avrcp_browsing_controller_send_set_addressed_player_cmd(uint16_t cid,
static void avrcp_browsing_controller_handle_can_send_now(avrcp_browsing_connection_t * connection){
switch (connection->state){
case AVCTP_CONNECTION_OPENED:
if (connection->get_folder_item){
connection->state = AVCTP_W2_RECEIVE_RESPONSE;
connection->get_folder_item = 0;
avrcp_browsing_controller_send_get_folder_items_cmd(connection->l2cap_browsing_cid, connection);
break;
}
if (connection->change_path){
connection->state = AVCTP_W2_RECEIVE_RESPONSE;
connection->change_path = 0;
avrcp_browsing_controller_send_change_path_cmd(connection->l2cap_browsing_cid, connection);
break;
}
if (connection->set_browsed_player_id){
connection->state = AVCTP_W2_RECEIVE_RESPONSE;
connection->set_browsed_player_id = 0;
@ -383,6 +370,20 @@ static void avrcp_browsing_controller_handle_can_send_now(avrcp_browsing_connect
avrcp_browsing_controller_send_set_addressed_player_cmd(connection->l2cap_browsing_cid, connection);
break;
}
if (connection->get_folder_item){
connection->state = AVCTP_W2_RECEIVE_RESPONSE;
connection->get_folder_item = 0;
avrcp_browsing_controller_send_get_folder_items_cmd(connection->l2cap_browsing_cid, connection);
break;
}
if (connection->change_path){
connection->state = AVCTP_W2_RECEIVE_RESPONSE;
connection->change_path = 0;
avrcp_browsing_controller_send_change_path_cmd(connection->l2cap_browsing_cid, connection);
break;
}
default:
return;
}
@ -411,42 +412,71 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
if (!browsing_connection) break;
browsing_connection->state = AVCTP_CONNECTION_OPENED;
int pos = 3;
if (size < pos + 4){
avrcp_browsing_controller_emit_done(avrcp_controller_context.avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS);
break;
// printf_hexdump(packet, size);
int pos = 0;
uint8_t transport_header = packet[pos++];
// Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
// uint8_t transaction_label = transport_header >> 4;
avrcp_packet_type_t avctp_packet_type = (transport_header & 0x0F) >> 2;
// uint8_t frame_type = (transport_header & 0x03) >> 1;
// uint8_t ipid = transport_header & 0x01;
pos += 2;
browsing_connection->num_packets = 1;
switch (avctp_packet_type){
case AVRCP_SINGLE_PACKET:
break;
case AVRCP_START_PACKET:
browsing_connection->num_packets = packet[pos++];
break;
case AVRCP_CONTINUE_PACKET:
case AVRCP_END_PACKET:
// browsing_connection->num_packets = packet[pos++];
break;
}
if (pos + 4 > size){
avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS);
return;
}
avrcp_pdu_id_t pdu_id = packet[pos++];
uint16_t length = big_endian_read_16(packet, pos);
pos += 2;
if (size + pos < length){
avrcp_browsing_controller_emit_done(avrcp_controller_context.avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS);
break;
}
// if (browsing_connection->num_packets > 1)
// if (browsing_connection->num_packets == 1 && (pos + length > size)){
// printf("pos + length > size, %d, %d, %d \n", pos, length, size);
// avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS);
// return;
// }
uint8_t browsing_status = packet[pos++];
if (browsing_status != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
avrcp_browsing_controller_emit_done(avrcp_controller_context.avrcp_callback, channel, browsing_status, ERROR_CODE_SUCCESS);
break;
printf("browsing_status %d\n", browsing_status);
avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_status, ERROR_CODE_SUCCESS);
return;
}
uint32_t i;
switch(pdu_id){
case AVRCP_PDU_ID_CHANGE_PATH:
printf("AVRCP_PDU_ID_CHANGE_PATH \n");
break;
case AVRCP_PDU_ID_SET_ADDRESSED_PLAYER:
printf("AVRCP_PDU_ID_CHANGE_PATH \n");
break;
case AVRCP_PDU_ID_SET_BROWSED_PLAYER:{
browsing_connection->browsed_player_uid_counter = big_endian_read_16(packet, pos);
pos += 2;
uint32_t num_items = big_endian_read_32(packet, pos);
pos += 4;
printf("AVRCP_PDU_ID_SET_BROWSED_PLAYER uuid counter 0x0%2x, num items %d\n", browsing_connection->browsed_player_uid_counter, num_items);
for (i = 0; i < num_items; i++){
uint16_t browsable_item_length = 5 + big_endian_read_16(packet, pos+3);
printf(" pos %d, len %d\n", pos, browsable_item_length);
// reuse byte to put the new type AVRCP_BROWSING_MEDIA_ROOT_FOLDER
packet[pos-1] = AVRCP_BROWSING_MEDIA_ROOT_FOLDER;
(*avrcp_controller_context.avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos, browsable_item_length+1);
(*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos, browsable_item_length+1);
pos += browsable_item_length;
}
break;
@ -456,19 +486,25 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
pos += 2;
uint16_t num_items = big_endian_read_16(packet, pos);
pos += 2;
for (i = 0; i < num_items; i++){
uint16_t browsable_item_length = 3 + big_endian_read_16(packet, pos+1);
(*avrcp_controller_context.avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos, browsable_item_length);
(*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos, browsable_item_length);
pos += browsable_item_length;
}
break;
}
default:
return;
break;
}
avrcp_browsing_controller_emit_done(avrcp_controller_context.avrcp_callback, channel, browsing_status, ERROR_CODE_SUCCESS);
break;
switch (avctp_packet_type){
case AVRCP_SINGLE_PACKET:
case AVRCP_END_PACKET:
avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_status, ERROR_CODE_SUCCESS);
break;
default:
break;
}
break;
}
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)){
@ -628,7 +664,7 @@ uint8_t avrcp_browsing_controller_set_addressed_player(uint16_t avrcp_browsing_c
}
avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
if (connection->state != AVCTP_CONNECTION_OPENED){
if (!connection || connection->state != AVCTP_CONNECTION_OPENED){
log_error("avrcp_browsing_controller_change_path: connection in wrong state.");
return ERROR_CODE_COMMAND_DISALLOWED;
}
@ -652,7 +688,8 @@ uint8_t avrcp_browsing_controller_change_path(uint16_t avrcp_browsing_cid, uint8
}
avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
if (connection->state != AVCTP_CONNECTION_OPENED){
if (!connection || connection->state != AVCTP_CONNECTION_OPENED || !folder_uid){
log_error("avrcp_browsing_controller_change_path: connection in wrong state.");
return ERROR_CODE_COMMAND_DISALLOWED;
}
@ -664,6 +701,7 @@ uint8_t avrcp_browsing_controller_change_path(uint16_t avrcp_browsing_cid, uint8
connection->change_path = 1;
connection->direction = direction;
memcpy(connection->folder_uid, folder_uid, 8);
avrcp_request_can_send_now(avrcp_connection, connection->l2cap_browsing_cid);
return ERROR_CODE_SUCCESS;

View File

@ -435,17 +435,26 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
uint8_t pdu_id;
uint16_t param_length;
switch (avrcp_cmd_opcode(packet,size)){
case AVRCP_CMD_OPCODE_SUBUNIT_INFO:{
if (connection->state != AVCTP_W2_RECEIVE_RESPONSE) return;
connection->state = AVCTP_CONNECTION_OPENED;
// operands:
memcpy(operands, packet+pos, 5);
uint8_t unit_type = operands[1] >> 3;
uint8_t max_subunit_ID = operands[1] & 0x07;
log_info(" SUBUNIT INFO response: ctype 0x%02x (0C), subunit_type 0x%02x (1F), subunit_id 0x%02x (07), opcode 0x%02x (30), unit_type 0x%02x, max_subunit_ID %d", ctype, subunit_type, subunit_id, opcode, unit_type, max_subunit_ID);
break;
}
case AVRCP_CMD_OPCODE_UNIT_INFO:{
if (connection->state != AVCTP_W2_RECEIVE_RESPONSE) return;
connection->state = AVCTP_CONNECTION_OPENED;
// operands:
memcpy(operands, packet+pos, 5);
uint8_t unit_type = operands[1] >> 3;
uint8_t unit = operands[1] & 0x07;
uint32_t company_id = operands[2] << 16 | operands[3] << 8 | operands[4];
log_info(" UNIT INFO response: ctype 0x%02x (0C), subunit_type 0x%02x (1F), subunit_id 0x%02x (07), opcode 0x%02x (30), unit_type 0x%02x, unit %d, company_id 0x%06" PRIx32,
ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id );
ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id);
break;
}
case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT:
@ -856,6 +865,27 @@ uint8_t avrcp_controller_unit_info(uint16_t avrcp_cid){
return ERROR_CODE_SUCCESS;
}
uint8_t avrcp_controller_subunit_info(uint16_t avrcp_cid){
avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
if (!connection){
log_error("avrcp_unit_info: could not find a connection.");
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
}
if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
connection->state = AVCTP_W2_SEND_COMMAND;
connection->transaction_label++;
connection->command_opcode = AVRCP_CMD_OPCODE_SUBUNIT_INFO;
connection->command_type = AVRCP_CTYPE_STATUS;
connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique
connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE;
memset(connection->cmd_operands, 0xFF, connection->cmd_operands_length);
connection->cmd_operands[0] = 7; // page: 0, extention_code: 7
connection->cmd_operands_length = 5;
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
return ERROR_CODE_SUCCESS;
}
static uint8_t avrcp_controller_get_capabilities(uint16_t avrcp_cid, uint8_t capability_id){
avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
if (!connection){

View File

@ -99,6 +99,13 @@ uint8_t avrcp_controller_disconnect(uint16_t avrcp_cid);
*/
uint8_t avrcp_controller_unit_info(uint16_t avrcp_cid);
/**
* @brief Subunit info.
* @param avrcp_cid
* @returns status
*/
uint8_t avrcp_controller_subunit_info(uint16_t avrcp_cid);
/**
* @brief Get capabilities.
* @param avrcp_cid

View File

@ -137,22 +137,21 @@ static int next_player_index(){
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
if (packet_type != HCI_EVENT_PACKET) return;
// if (packet_type != HCI_EVENT_PACKET) return;
UNUSED(channel);
UNUSED(size);
bd_addr_t event_addr;
uint16_t local_cid;
uint8_t status = 0xFF;
int pos;
// printf("packet_handler packet type 0x%02X, HCI_EVENT_AVRCP_META 0x%02X, subevent 0x%02X\n", hci_event_packet_get_type(packet), HCI_EVENT_AVRCP_META, packet[2]);
// printf("packet_handler packet type 0x%02X, HCI_EVENT_AVRCP_META 0x%02X, subevent 0x%02X\n", packet_type, HCI_EVENT_AVRCP_META, packet[2]);
switch (hci_event_packet_get_type(packet)) {
switch (packet_type) {
case AVRCP_BROWSING_DATA_PACKET:
pos = 0;
browsing_query_active = 1;
avrcp_browsing_item_type_t data_type = (avrcp_browsing_item_type_t)packet[pos++];
pos += 2; // length
switch (data_type){
case AVRCP_BROWSING_MEDIA_ROOT_FOLDER:{
avrcp_browsing_root_folder_t root_folder;
@ -161,7 +160,6 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
root_folder.depth = packet[pos++];
root_folder.name_len = big_endian_read_16(packet, pos);
pos += 2;
memset(root_folder.name, 0, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN);
root_folder.name_len = btstack_min(big_endian_read_16(packet, pos), AVRCP_BROWSING_MAX_FOLDER_NAME_LEN - 1);
memcpy(root_folder.name, packet+pos, root_folder.name_len);
@ -240,189 +238,191 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
}
default:
log_error("AVRCP browsing: unknown browsable item type 0%02x", data_type);
printf("AVRCP browsing: unknown browsable item type 0%02x\n", data_type);
break;
}
break;
case HCI_EVENT_AVDTP_META:
switch (packet[2]){
case AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED:
avdtp_cid = avdtp_subevent_signaling_connection_established_get_avdtp_cid(packet);
status = avdtp_subevent_signaling_connection_established_get_status(packet);
if (status != ERROR_CODE_SUCCESS){
printf("AVDTP connection establishment failed: status 0x%02x.\n", status);
break;
case HCI_EVENT_PACKET:
switch (packet[0]){
case HCI_EVENT_AVDTP_META:
switch (packet[2]){
case AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED:
avdtp_cid = avdtp_subevent_signaling_connection_established_get_avdtp_cid(packet);
status = avdtp_subevent_signaling_connection_established_get_status(packet);
if (status != ERROR_CODE_SUCCESS){
printf("AVDTP connection establishment failed: status 0x%02x.\n", status);
break;
}
printf("AVDTP connection established: avdtp_cid 0x%02x.\n", avdtp_cid);
break;
case AVDTP_SUBEVENT_SIGNALING_CONNECTION_RELEASED:
avdtp_cid = avdtp_subevent_signaling_connection_released_get_avdtp_cid(packet);
printf("AVDTP connection released: avdtp_cid 0x%02x.\n", avdtp_cid);
break;
default:
printf("AVDTP event not parsed.\n");
break;
}
printf("AVDTP connection established: avdtp_cid 0x%02x.\n", avdtp_cid);
break;
case AVDTP_SUBEVENT_SIGNALING_CONNECTION_RELEASED:
avdtp_cid = avdtp_subevent_signaling_connection_released_get_avdtp_cid(packet);
printf("AVDTP connection released: avdtp_cid 0x%02x.\n", avdtp_cid);
break;
default:
printf("AVDTP event not parsed.\n");
break;
}
break;
case HCI_EVENT_AVRCP_META:
switch (packet[2]){
case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED: {
local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet);
if (browsing_cid != 0 && browsing_cid != local_cid) {
printf("AVRCP Controller connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
return;
break;
case HCI_EVENT_AVRCP_META:
switch (packet[2]){
case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED: {
local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet);
if (browsing_cid != 0 && browsing_cid != local_cid) {
printf("AVRCP Controller connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
return;
}
status = avrcp_subevent_connection_established_get_status(packet);
if (status != ERROR_CODE_SUCCESS){
printf("AVRCP Controller connection failed: status 0x%02x\n", status);
browsing_cid = 0;
return;
}
avrcp_cid = local_cid;
avrcp_connected = 1;
avrcp_subevent_connection_established_get_bd_addr(packet, event_addr);
printf("AVRCP Controller connected.\n");
return;
}
case AVRCP_SUBEVENT_CONNECTION_RELEASED:
printf("AVRCP Controller Client released.\n");
browsing_cid = 0;
avrcp_browsing_connected = 0;
folder_index = 0;
memset(folders, 0, sizeof(folders));
return;
case AVRCP_SUBEVENT_INCOMING_BROWSING_CONNECTION:
local_cid = avrcp_subevent_incoming_browsing_connection_get_browsing_cid(packet);
if (browsing_cid != 0 && browsing_cid != local_cid) {
printf("AVRCP Browsing Client connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
avrcp_avrcp_browsing_decline_incoming_connection(browsing_cid);
return;
}
browsing_cid = local_cid;
printf("AVRCP Browsing Client configure incoming connection, browsing cid 0x%02x\n", browsing_cid);
avrcp_avrcp_browsing_configure_incoming_connection(browsing_cid, ertm_buffer, sizeof(ertm_buffer), &ertm_config);
break;
case AVRCP_SUBEVENT_BROWSING_CONNECTION_ESTABLISHED: {
local_cid = avrcp_subevent_browsing_connection_established_get_browsing_cid(packet);
if (browsing_cid != 0 && browsing_cid != local_cid) {
printf("AVRCP Browsing Client connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
return;
}
status = avrcp_subevent_browsing_connection_established_get_status(packet);
if (status != ERROR_CODE_SUCCESS){
printf("AVRCP Browsing Client connection failed: status 0x%02x\n", status);
browsing_cid = 0;
return;
}
browsing_cid = local_cid;
avrcp_browsing_connected = 1;
avrcp_subevent_browsing_connection_established_get_bd_addr(packet, event_addr);
printf("AVRCP Browsing Client connected\n");
return;
}
case AVRCP_SUBEVENT_BROWSING_CONNECTION_RELEASED:
printf("AVRCP Browsing Controller released\n");
browsing_cid = 0;
avrcp_browsing_connected = 0;
return;
case AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE:
browsing_query_active = 0;
if (avrcp_subevent_browsing_media_item_done_get_browsing_status(packet) != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
printf("AVRCP Browsing query done with browsing status 0x%02x, bluetooth status 0x%02x.\n",
avrcp_subevent_browsing_media_item_done_get_browsing_status(packet),
avrcp_subevent_browsing_media_item_done_get_bluetooth_status(packet));
break;
}
printf("AVRCP Browsing cmd: DONE.\n");
break;
default:
break;
}
status = avrcp_subevent_connection_established_get_status(packet);
if (status != ERROR_CODE_SUCCESS){
printf("AVRCP Controller connection failed: status 0x%02x\n", status);
browsing_cid = 0;
return;
}
local_cid = little_endian_read_16(packet, 3);
if (local_cid != avrcp_cid) return;
// avoid printing INTERIM status
status = packet[5];
if (status == AVRCP_CTYPE_RESPONSE_INTERIM) return;
printf("AVRCP: command status: %s, ", avrcp_ctype2str(status));
avrcp_cid = local_cid;
avrcp_connected = 1;
avrcp_subevent_connection_established_get_bd_addr(packet, event_addr);
printf("AVRCP Controller connected.\n");
return;
}
case AVRCP_SUBEVENT_CONNECTION_RELEASED:
printf("AVRCP Controller Client released.\n");
browsing_cid = 0;
avrcp_browsing_connected = 0;
folder_index = 0;
memset(folders, 0, sizeof(folders));
return;
case AVRCP_SUBEVENT_INCOMING_BROWSING_CONNECTION:
local_cid = avrcp_subevent_incoming_browsing_connection_get_browsing_cid(packet);
if (browsing_cid != 0 && browsing_cid != local_cid) {
printf("AVRCP Browsing Client connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
avrcp_avrcp_browsing_decline_incoming_connection(browsing_cid);
return;
}
browsing_cid = local_cid;
printf("AVRCP Browsing Client configure incoming connection, browsing cid 0x%02x\n", browsing_cid);
avrcp_avrcp_browsing_configure_incoming_connection(browsing_cid, ertm_buffer, sizeof(ertm_buffer), &ertm_config);
break;
switch (packet[2]){
case AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED:
printf("notification, playback status changed %s\n", avrcp_play_status2str(avrcp_subevent_notification_playback_status_changed_get_play_status(packet)));
return;
case AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED:
printf("notification, playing content changed\n");
return;
case AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED:
printf("notification track changed\n");
return;
case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED:
printf("notification absolute volume changed %d\n", avrcp_subevent_notification_volume_changed_get_absolute_volume(packet));
return;
case AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED:
printf("notification changed\n");
return;
case AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE:{
uint8_t shuffle_mode = avrcp_subevent_shuffle_and_repeat_mode_get_shuffle_mode(packet);
uint8_t repeat_mode = avrcp_subevent_shuffle_and_repeat_mode_get_repeat_mode(packet);
printf("%s, %s\n", avrcp_shuffle2str(shuffle_mode), avrcp_repeat2str(repeat_mode));
break;
}
case AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO:
if (avrcp_subevent_now_playing_title_info_get_value_len(packet) > 0){
memcpy(avrcp_value, avrcp_subevent_now_playing_title_info_get_value(packet), avrcp_subevent_now_playing_title_info_get_value_len(packet));
printf(" Title: %s\n", avrcp_value);
}
break;
case AVRCP_SUBEVENT_BROWSING_CONNECTION_ESTABLISHED: {
local_cid = avrcp_subevent_browsing_connection_established_get_browsing_cid(packet);
if (browsing_cid != 0 && browsing_cid != local_cid) {
printf("AVRCP Browsing Client connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
return;
case AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO:
if (avrcp_subevent_now_playing_artist_info_get_value_len(packet) > 0){
memcpy(avrcp_value, avrcp_subevent_now_playing_artist_info_get_value(packet), avrcp_subevent_now_playing_artist_info_get_value_len(packet));
printf(" Artist: %s\n", avrcp_value);
}
break;
case AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO:
if (avrcp_subevent_now_playing_album_info_get_value_len(packet) > 0){
memcpy(avrcp_value, avrcp_subevent_now_playing_album_info_get_value(packet), avrcp_subevent_now_playing_album_info_get_value_len(packet));
printf(" Album: %s\n", avrcp_value);
}
break;
case AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO:
if (avrcp_subevent_now_playing_genre_info_get_value_len(packet) > 0){
memcpy(avrcp_value, avrcp_subevent_now_playing_genre_info_get_value(packet), avrcp_subevent_now_playing_genre_info_get_value_len(packet));
printf(" Genre: %s\n", avrcp_value);
}
break;
case AVRCP_SUBEVENT_PLAY_STATUS:
printf("song length: %d ms, song position: %d ms, play status: %s\n",
avrcp_subevent_play_status_get_song_length(packet),
avrcp_subevent_play_status_get_song_position(packet),
avrcp_play_status2str(avrcp_subevent_play_status_get_play_status(packet)));
break;
case AVRCP_SUBEVENT_OPERATION_COMPLETE:
printf("operation done %s\n", avrcp_operation2str(avrcp_subevent_operation_complete_get_operation_id(packet)));
break;
case AVRCP_SUBEVENT_OPERATION_START:
printf("operation start %s\n", avrcp_operation2str(avrcp_subevent_operation_complete_get_operation_id(packet)));
break;
case AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE:
// response to set shuffle and repeat mode
printf("\n");
break;
default:
printf("AVRCP event not parsed.\n");
break;
}
status = avrcp_subevent_browsing_connection_established_get_status(packet);
if (status != ERROR_CODE_SUCCESS){
printf("AVRCP Browsing Client connection failed: status 0x%02x\n", status);
browsing_cid = 0;
return;
}
browsing_cid = local_cid;
avrcp_browsing_connected = 1;
avrcp_subevent_browsing_connection_established_get_bd_addr(packet, event_addr);
printf("AVRCP Browsing Client connected\n");
return;
}
case AVRCP_SUBEVENT_BROWSING_CONNECTION_RELEASED:
printf("AVRCP Browsing Controller released\n");
browsing_cid = 0;
avrcp_browsing_connected = 0;
return;
case AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE:
browsing_query_active = 0;
if (avrcp_subevent_browsing_media_item_done_get_browsing_status(packet) != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
printf("AVRCP Browsing query done with browsing status 0x%02x, bluetooth status 0x%02x.\n",
avrcp_subevent_browsing_media_item_done_get_browsing_status(packet),
avrcp_subevent_browsing_media_item_done_get_bluetooth_status(packet));
break;
}
printf("AVRCP Browsing cmd: DONE.\n");
break;
default:
break;
}
local_cid = little_endian_read_16(packet, 3);
if (local_cid != avrcp_cid) return;
// avoid printing INTERIM status
status = packet[5];
if (status == AVRCP_CTYPE_RESPONSE_INTERIM) return;
printf("AVRCP: command status: %s, ", avrcp_ctype2str(status));
switch (packet[2]){
case AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED:
printf("notification, playback status changed %s\n", avrcp_play_status2str(avrcp_subevent_notification_playback_status_changed_get_play_status(packet)));
return;
case AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED:
printf("notification, playing content changed\n");
return;
case AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED:
printf("notification track changed\n");
return;
case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED:
printf("notification absolute volume changed %d\n", avrcp_subevent_notification_volume_changed_get_absolute_volume(packet));
return;
case AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED:
printf("notification changed\n");
return;
case AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE:{
uint8_t shuffle_mode = avrcp_subevent_shuffle_and_repeat_mode_get_shuffle_mode(packet);
uint8_t repeat_mode = avrcp_subevent_shuffle_and_repeat_mode_get_repeat_mode(packet);
printf("%s, %s\n", avrcp_shuffle2str(shuffle_mode), avrcp_repeat2str(repeat_mode));
break;
}
case AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO:
if (avrcp_subevent_now_playing_title_info_get_value_len(packet) > 0){
memcpy(avrcp_value, avrcp_subevent_now_playing_title_info_get_value(packet), avrcp_subevent_now_playing_title_info_get_value_len(packet));
printf(" Title: %s\n", avrcp_value);
}
break;
case AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO:
if (avrcp_subevent_now_playing_artist_info_get_value_len(packet) > 0){
memcpy(avrcp_value, avrcp_subevent_now_playing_artist_info_get_value(packet), avrcp_subevent_now_playing_artist_info_get_value_len(packet));
printf(" Artist: %s\n", avrcp_value);
}
break;
case AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO:
if (avrcp_subevent_now_playing_album_info_get_value_len(packet) > 0){
memcpy(avrcp_value, avrcp_subevent_now_playing_album_info_get_value(packet), avrcp_subevent_now_playing_album_info_get_value_len(packet));
printf(" Album: %s\n", avrcp_value);
}
break;
case AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO:
if (avrcp_subevent_now_playing_genre_info_get_value_len(packet) > 0){
memcpy(avrcp_value, avrcp_subevent_now_playing_genre_info_get_value(packet), avrcp_subevent_now_playing_genre_info_get_value_len(packet));
printf(" Genre: %s\n", avrcp_value);
}
break;
case AVRCP_SUBEVENT_PLAY_STATUS:
printf("song length: %d ms, song position: %d ms, play status: %s\n",
avrcp_subevent_play_status_get_song_length(packet),
avrcp_subevent_play_status_get_song_position(packet),
avrcp_play_status2str(avrcp_subevent_play_status_get_play_status(packet)));
break;
case AVRCP_SUBEVENT_OPERATION_COMPLETE:
printf("operation done %s\n", avrcp_operation2str(avrcp_subevent_operation_complete_get_operation_id(packet)));
break;
case AVRCP_SUBEVENT_OPERATION_START:
printf("operation start %s\n", avrcp_operation2str(avrcp_subevent_operation_complete_get_operation_id(packet)));
break;
case AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE:
// response to set shuffle and repeat mode
printf("\n");
break;
default:
printf("AVRCP event not parsed.\n");
break;
}
break;
default:
break;
}
@ -446,6 +446,7 @@ static void show_usage(void){
printf("\n--- Bluetooth AVRCP Commands %s ---\n", bd_addr_to_str(iut_address));
printf("q - get capabilities: supported events\n");
printf("w - get unit info\n");
printf("* - get subunit info\n");
printf("r - get play status\n");
printf("t - get now playing info\n");
printf("1 - play\n");
@ -479,18 +480,19 @@ static void show_usage(void){
printf("y/Y - register/deregister UIDS_CHANGED\n");
printf("v/V - register/deregister VOLUME_CHANGED\n");
printf("p - Get media players\n");
printf("I - Set first found player as addressed player\n");
printf("O - Set first found player as browsed player\n");
printf("p - Get media players\n");
printf("Q - Browse folders\n");
printf("P - Go up one level\n");
printf("W - Go down one level\n");
printf("T - Browse media items\n");
printf("Q - Browse folders (Get Folder Items with the Virtual File System scope)\n");
printf("P - Browse media items\n");
printf("W - Go up one level\n");
printf("T - Go down one level\n");
printf("Ctrl-c - exit\n");
printf("---\n");
}
#endif
static uint8_t media_sbc_codec_capabilities[] = {
@ -550,6 +552,10 @@ static void stdin_process(char cmd){
printf("AVRCP: get unit info\n");
avrcp_controller_unit_info(avrcp_cid);
break;
case '*':
printf("AVRCP: get subunit info\n");
avrcp_controller_subunit_info(avrcp_cid);
break;
case 'r':
printf("AVRCP: get play status\n");
avrcp_controller_get_play_status(avrcp_cid);
@ -754,11 +760,10 @@ static void stdin_process(char cmd){
status = avrcp_browsing_controller_set_browsed_player(browsing_cid, players[0]);
break;
case 'p':
players[next_player_index()] = 1;
// printf("AVRCP Browsing: get media players. Brosing cid 0x%02X\n", browsing_cid);
// player_index = -1;
// status = avrcp_browsing_controller_get_media_players(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
// players[next_player_index()] = 1;
printf("AVRCP Browsing: get media players. Brosing cid 0x%02X\n", browsing_cid);
player_index = -1;
status = avrcp_browsing_controller_get_media_players(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
break;
case 'Q':
printf("AVRCP Browsing: browse folders\n");