avrctp browsing target: start addr player changed, get folder items

This commit is contained in:
Milanka Ringwald 2018-04-25 23:34:17 +02:00
parent 7f803253e9
commit 6568eb54ed
4 changed files with 266 additions and 5 deletions

View File

@ -51,6 +51,63 @@
static void avrcp_browser_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size, avrcp_context_t * context);
static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void avrcp_browsing_target_request_can_send_now(avrcp_browsing_connection_t * connection, uint16_t l2cap_cid){
connection->wait_to_send = 1;
l2cap_request_can_send_now_event(l2cap_cid);
}
static int avrcp_browsing_target_handle_can_send_now(avrcp_browsing_connection_t * connection){
int pos = 0;
l2cap_reserve_packet_buffer();
uint8_t * packet = l2cap_get_outgoing_buffer();
// transport header
// Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
// TODO: check for fragmentation
connection->packet_type = AVRCP_SINGLE_PACKET;
printf("ttransaction %d \n", connection->transaction_label);
packet[pos++] = (connection->transaction_label << 4) | (connection->packet_type << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0;
// Profile IDentifier (PID)
packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
// command_type
// packet[pos++] = connection->command_type;
// // subunit_type | subunit ID
// packet[pos++] = (connection->subunit_type << 3) | connection->subunit_id;
// // opcode
// packet[pos++] = (uint8_t)connection->command_opcode;
// operands
// company id is 3 bytes long
// big_endian_store_24(packet, pos, BT_SIG_COMPANY_ID);
// pos += 3;
memcpy(packet+pos, connection->cmd_operands, connection->cmd_operands_length);
// printf_hexdump(packet+pos, connection->cmd_operands_length);
pos += connection->cmd_operands_length;
connection->wait_to_send = 0;
printf(" send reject \n");
printf_hexdump(packet, pos);
return l2cap_send_prepared(connection->l2cap_browsing_cid, pos);
}
static uint8_t avrcp_browsing_target_response_reject(avrcp_browsing_connection_t * connection, avrcp_status_code_t status){
// AVRCP_CTYPE_RESPONSE_REJECTED
int pos = 0;
connection->cmd_operands[pos++] = AVRCP_PDU_ID_GENERAL_REJECT;
// connection->cmd_operands[pos++] = 0;
// param length
big_endian_store_16(connection->cmd_operands, pos, 1);
pos += 2;
connection->cmd_operands[pos++] = status;
connection->cmd_operands_length = 4;
connection->state = AVCTP_W2_SEND_RESPONSE;
avrcp_browsing_target_request_can_send_now(connection, connection->l2cap_browsing_cid);
return ERROR_CODE_SUCCESS;
}
static avrcp_connection_t * get_avrcp_connection_for_browsing_cid(uint16_t browsing_cid, avrcp_context_t * context){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &context->connections);
@ -84,6 +141,21 @@ static avrcp_browsing_connection_t * get_avrcp_browsing_connection_for_l2cap_cid
return NULL;
}
static void avrcp_browsing_target_emit_get_folder_items(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
if (!callback) return;
uint8_t event[10];
int pos = 0;
event[pos++] = HCI_EVENT_AVRCP_META;
event[pos++] = sizeof(event) - 2;
event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_FOLDER_ITEMS;
little_endian_store_16(event, pos, browsing_cid);
pos += 2;
event[pos++] = connection->scope;
big_endian_store_32(event, pos, connection->attr_bitmap);
pos += 4;
(*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
}
static void avrcp_emit_browsing_connection_established(btstack_packet_handler_t callback, uint16_t browsing_cid, bd_addr_t addr, uint8_t status){
if (!callback) return;
uint8_t event[12];
@ -249,7 +321,6 @@ static void avrcp_browser_packet_handler(uint8_t packet_type, uint16_t channel,
}
static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
avrcp_browsing_connection_t * browsing_connection;
@ -257,14 +328,84 @@ static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t c
case L2CAP_DATA_PACKET:{
browsing_connection = get_avrcp_browsing_connection_for_l2cap_cid(channel, &avrcp_target_context);
if (!browsing_connection) 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)
browsing_connection->transaction_label = transport_header >> 4;
avrcp_packet_type_t avctp_packet_type = (transport_header & 0x0F) >> 2;
printf("L2CAP_DATA_PACKET, transaction_label %d\n", browsing_connection->transaction_label);
switch (avctp_packet_type){
case AVRCP_SINGLE_PACKET:
case AVRCP_START_PACKET:
// uint8_t frame_type = (transport_header & 0x03) >> 1;
// uint8_t ipid = transport_header & 0x01;
browsing_connection->subunit_type = packet[pos++] >> 2;
browsing_connection->subunit_id = 0;
browsing_connection->command_opcode = packet[pos++];
// printf("subunit_id")
// pos += 2;
browsing_connection->num_packets = 1;
if (avctp_packet_type == AVRCP_START_PACKET){
browsing_connection->num_packets = packet[pos++];
}
browsing_connection->pdu_id = packet[pos++];
// uint16_t length = big_endian_read_16(packet, pos);
// pos += 2;
break;
default:
break;
}
printf("pdu id 0x%2x\n", browsing_connection->pdu_id);
// uint32_t i;
switch(browsing_connection->pdu_id){
case AVRCP_PDU_ID_GET_FOLDER_ITEMS:
printf("\n");
browsing_connection->scope = packet[pos++];
browsing_connection->start_item = big_endian_read_32(packet, pos);
pos += 4;
browsing_connection->end_item = big_endian_read_32(packet, pos);
pos += 4;
uint8_t attr_count = packet[pos++];
while (attr_count){
uint32_t attr_id = big_endian_read_32(packet, pos);
pos += 4;
browsing_connection->attr_bitmap |= (1 << attr_id);
attr_count--;
}
avrcp_browsing_target_emit_get_folder_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
break;
default:
printf(" not parsed pdu ID 0x%02x\n", browsing_connection->pdu_id);
break;
}
switch (avctp_packet_type){
case AVRCP_SINGLE_PACKET:
case AVRCP_END_PACKET:
printf("send avrcp_browsing_target_response_reject\n");
browsing_connection->state = AVCTP_CONNECTION_OPENED;
avrcp_browsing_target_response_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
// avrcp_browsing_target_emit_done_with_uid_counter(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection->uid_counter, browsing_connection->browsing_status, ERROR_CODE_SUCCESS);
break;
default:
break;
}
// printf(" paket done\n");
break;
}
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)){
case L2CAP_EVENT_CAN_SEND_NOW:
browsing_connection = get_avrcp_browsing_connection_for_l2cap_cid(channel, &avrcp_target_context);
if (!browsing_connection) break;
// avrcp_browsing_target_handle_can_send_now(browsing_connection);
if (browsing_connection->state != AVCTP_W2_SEND_RESPONSE) return;
browsing_connection->state = AVCTP_CONNECTION_OPENED;
avrcp_browsing_target_handle_can_send_now(browsing_connection);
break;
default:
avrcp_browser_packet_handler(packet_type, channel, packet, size, &avrcp_target_context);

View File

@ -395,6 +395,32 @@ static uint8_t avrcp_target_response_vendor_dependent_interim(avrcp_connection_t
return ERROR_CODE_SUCCESS;
}
static uint8_t avrcp_target_response_addressed_player_changed_interim(avrcp_connection_t * connection, avrcp_subunit_type_t subunit_type, avrcp_subunit_id_t subunit_id, avrcp_command_opcode_t opcode, avrcp_pdu_id_t pdu_id){
connection->command_type = AVRCP_CTYPE_RESPONSE_INTERIM;
connection->subunit_type = subunit_type;
connection->subunit_id = subunit_id;
connection->command_opcode = opcode;
printf("avrcp_target_response_addressed_player_changed_interim \n");
// company id is 3 bytes long
int pos = connection->cmd_operands_length;
connection->cmd_operands[pos++] = pdu_id;
connection->cmd_operands[pos++] = 0;
// param length
big_endian_store_16(connection->cmd_operands, pos, 5);
pos += 2;
connection->cmd_operands[pos++] = AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED;
big_endian_read_16( &connection->cmd_operands[pos], connection->addressed_player_id);
pos += 2;
big_endian_read_16( &connection->cmd_operands[pos], connection->uid_counter);
pos += 2;
connection->cmd_operands_length = pos;
connection->state = AVCTP_W2_SEND_RESPONSE;
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
return ERROR_CODE_SUCCESS;
}
// static uint8_t avrcp_target_response_vendor_dependent_changed(avrcp_connection_t * connection, avrcp_pdu_id_t pdu_id, uint8_t event_id){
// connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
// connection->command_type = AVRCP_CTYPE_RESPONSE_CHANGED_STABLE;
@ -659,7 +685,7 @@ uint8_t avrcp_target_track_changed(uint16_t avrcp_cid, uint8_t * track_id){
uint8_t avrcp_target_playing_content_changed(uint16_t avrcp_cid){
avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context);
if (!connection){
log_error("avrcp_unit_info: could not find a connection.");
log_error("avrcp_target_playing_content_changed: could not find a connection.");
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
}
if (connection->notifications_enabled & (1 << AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED)) {
@ -669,6 +695,22 @@ uint8_t avrcp_target_playing_content_changed(uint16_t avrcp_cid){
return ERROR_CODE_SUCCESS;
}
uint8_t avrcp_target_addressed_player_changed(uint16_t avrcp_cid, uint16_t player_id, uint16_t uid_counter){
avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context);
if (!connection){
log_error("avrcp_unit_info: could not find a connection.");
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
}
if (connection->notifications_enabled & (1 << AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED)) {
printf("send AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED\n");
// connection->addressed_player_changed = 1;
// connection->uid_counter = uid_counter;
// connection->addressed_player_id = player_id;
// avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
}
return ERROR_CODE_SUCCESS;
}
uint8_t avrcp_target_battery_status_changed(uint16_t avrcp_cid, avrcp_battery_status_t battery_status){
avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context);
if (!connection){
@ -803,10 +845,22 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT:
pdu_id = pdu[0];
// 1 - reserved
// 2-3 param length,
// 2-3 param length,
printf_hexdump(packet, size);
uint16_t length = big_endian_read_16(pdu, 1);
memcpy(connection->cmd_operands, company_id, 3);
connection->cmd_operands_length = 3;
switch (pdu_id){
case AVRCP_PDU_ID_SET_ADDRESSED_PLAYER:{
if (length == 0){
printf(" reject id\n");
avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_PLAYER_ID);
break;
}
avrcp_target_response_accept(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_SUCCESS);
break;
}
case AVRCP_PDU_ID_GET_CAPABILITIES:{
avrcp_capability_id_t capability_id = (avrcp_capability_id_t) pdu[pos];
switch (capability_id){
@ -898,10 +952,14 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
break;
case AVRCP_NOTIFICATION_EVENT_AVAILABLE_PLAYERS_CHANGED:
case AVRCP_NOTIFICATION_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:
case AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED:
case AVRCP_NOTIFICATION_EVENT_UIDS_CHANGED:
avrcp_target_response_not_implemented(connection, subunit_type, subunit_id, opcode, pdu_id, event_id);
return;
case AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED:
connection->notifications_enabled |= event_mask;
printf("respond with interim AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED\n");
avrcp_target_response_addressed_player_changed_interim(connection, subunit_type, subunit_id, opcode, pdu_id);
return;
default:
avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_PARAMETER);
return;
@ -938,6 +996,53 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
}
}
static int avrcp_target_send_addressed_player_changed_notification(uint16_t cid, avrcp_connection_t * connection, uint16_t uid, uint16_t uid_counter){
if (!connection){
log_error("avrcp tartget: could not find a connection.");
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
}
printf("avrcp_target_send_addressed_player_changed_notification \n");
connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
connection->command_type = AVRCP_CTYPE_RESPONSE_CHANGED_STABLE;
connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
connection->subunit_id = AVRCP_SUBUNIT_ID;
uint16_t pos = 0;
l2cap_reserve_packet_buffer();
uint8_t * packet = l2cap_get_outgoing_buffer();
connection->packet_type = AVRCP_SINGLE_PACKET;
packet[pos++] = (connection->transaction_label << 4) | (connection->packet_type << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0;
// Profile IDentifier (PID)
packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
// command_type
packet[pos++] = connection->command_type;
// subunit_type | subunit ID
packet[pos++] = (connection->subunit_type << 3) | connection->subunit_id;
// opcode
packet[pos++] = (uint8_t)connection->command_opcode;
// company id is 3 bytes long
big_endian_store_24(packet, pos, BT_SIG_COMPANY_ID);
pos += 3;
packet[pos++] = AVRCP_PDU_ID_REGISTER_NOTIFICATION;
packet[pos++] = 0;
big_endian_store_16(packet, pos, 5);
pos += 2;
packet[pos++] = AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED;
big_endian_store_16(packet, pos, uid);
pos += 2;
big_endian_store_16(packet, pos, uid_counter);
pos += 2;
connection->wait_to_send = 0;
return l2cap_send_prepared(cid, pos);
}
static int avrcp_target_send_notification(uint16_t cid, avrcp_connection_t * connection, uint8_t notification_id, uint8_t * value, uint16_t value_len){
if (!connection){
log_error("avrcp tartget: could not find a connection.");
@ -990,6 +1095,7 @@ static void avrcp_target_reset_notification(avrcp_connection_t * connection, uin
log_error("avrcp tartget: could not find a connection.");
return;
}
printf("reset notification\n");
connection->notifications_enabled &= ~(1 << notification_id);
connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
@ -1041,6 +1147,14 @@ static void avrcp_target_packet_handler(uint8_t packet_type, uint16_t channel, u
break;
}
// if (connection->addressed_player_changed){
// connection->playback_status_changed = 0;
// avrcp_target_send_addressed_player_changed_notification(connection->l2cap_signaling_cid, connection, connection->addressed_player_id, connection->uid_counter);
// avrcp_target_reset_notification(connection, AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED);
// avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
// break;
// }
if (connection->playing_content_changed){
connection->playing_content_changed = 0;
avrcp_target_send_notification(connection->l2cap_signaling_cid, connection, AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED, NULL, 0);

View File

@ -85,6 +85,8 @@ void avrcp_target_set_unit_info(uint16_t avrcp_cid, avrcp_subunit_type_t unit_ty
void avrcp_target_set_subunit_info(uint16_t avrcp_cid, avrcp_subunit_type_t subunit_type, const uint8_t * subunit_info_data, uint16_t subunit_info_data_size);
uint8_t avrcp_target_playing_content_changed(uint16_t avrcp_cid);
uint8_t avrcp_target_addressed_player_changed(uint16_t avrcp_cid, uint16_t player_id, uint16_t uid_counter);
uint8_t avrcp_target_battery_status_changed(uint16_t avrcp_cid, avrcp_battery_status_t battery_status);
uint8_t avrcp_target_volume_changed(uint16_t avrcp_cid, uint8_t volume_percentage);
uint8_t avrcp_target_track_changed(uint16_t avrcp_cid, uint8_t * trackID);

View File

@ -547,6 +547,10 @@ static void avrcp_target_packet_handler(uint8_t packet_type, uint16_t channel, u
printf("AVRCP Browsing Client connected\n");
return;
}
case AVRCP_SUBEVENT_BROWSING_GET_FOLDER_ITEMS:
printf(" AVRCP_SUBEVENT_BROWSING_GET_FOLDER_ITEMS \n");
break;
case AVRCP_SUBEVENT_BROWSING_CONNECTION_RELEASED:
printf("AVRCP Browsing Controller released\n");
browsing_cid = 0;