avrcp: use byte parser for segmented packets

This commit is contained in:
Milanka Ringwald 2018-02-20 09:59:16 +01:00
parent 3e06d92f19
commit 14e95846d8
4 changed files with 143 additions and 103 deletions

View File

@ -55,7 +55,7 @@ extern "C" {
#define BT_SIG_COMPANY_ID 0x001958
#define AVRCP_MEDIA_ATTR_COUNT 7
#define AVRCP_MAX_ATTRIBUTTE_SIZE 5000
#define AVRCP_MAX_ATTRIBUTTE_SIZE 100
#define AVRCP_ATTRIBUTE_HEADER_LEN 8
#define AVRCP_MAX_FOLDER_NAME_SIZE 20
@ -287,9 +287,12 @@ typedef struct {
typedef enum {
AVRCP_PARSER_GET_ATTRIBUTE_HEADER = 0, // 8 bytes
AVRCP_PARSER_GET_ATTRIBUTE_VALUE,
AVRCP_PARSER_CONTINUE_GET_ATTRIBUTE_VALUE
AVRCP_PARSER_IGNORE_REST_OF_ATTRIBUTE_VALUE
} avrcp_parser_state_t;
#define AVRCP_BROWSING_ITEM_HEADER_LEN 3
// BROWSING
typedef struct {
uint16_t l2cap_browsing_cid;
@ -308,7 +311,6 @@ typedef struct {
// players
uint8_t set_browsed_player_id;
uint16_t browsed_player_id;
uint16_t browsed_player_uid_counter;
// get folder item
uint8_t get_folder_item;
@ -332,16 +334,17 @@ typedef struct {
uint8_t get_total_nr_items;
avrcp_browsing_scope_t get_total_nr_items_scope;
// fragmentation
uint8_t fragmented;
avrcp_pdu_id_t fragmented_pdu_id;
uint8_t fragmented_browsing_status;
uint16_t fragmented_uid_counter;
avrcp_pdu_id_t pdu_id;
uint8_t browsing_status;
uint16_t num_items;
uint8_t item_type;
uint16_t item_length;
uint16_t fragment_size;
uint8_t fragment[AVRCP_MAX_ATTRIBUTTE_SIZE];
avrcp_parser_state_t parser_state;
uint8_t parser_attribute_header[AVRCP_BROWSING_ITEM_HEADER_LEN];
uint8_t parser_attribute_header_pos;
uint8_t parsed_attribute_value[AVRCP_MAX_ATTRIBUTTE_SIZE];
uint16_t parsed_attribute_value_len;
uint16_t parsed_attribute_value_offset;
uint8_t parsed_num_attributes;
} avrcp_browsing_connection_t;
// BROWSING END

View File

@ -310,7 +310,6 @@ static int avrcp_browsing_controller_send_change_path_cmd(uint16_t cid, avrcp_br
big_endian_store_16(command, pos, 11);
pos += 2;
big_endian_store_16(command, pos, connection->browsed_player_uid_counter);
pos += 2;
command[pos++] = connection->direction;
memcpy(command+pos, connection->folder_uid, 8);
@ -435,10 +434,82 @@ static void avrcp_browsing_controller_emit_done_with_uid_counter(btstack_packet_
(*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
}
static void avrcp_parser_reset(avrcp_browsing_connection_t * connection){
connection->parser_attribute_header_pos = 0;
connection->parsed_attribute_value_offset = 0;
connection->parsed_num_attributes = 0;
connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_HEADER;
}
static void avrcp_browsing_parser_process_byte(uint8_t byte, avrcp_browsing_connection_t * connection){
uint8_t prepended_header_size = 1;
switch(connection->parser_state){
case AVRCP_PARSER_GET_ATTRIBUTE_HEADER:{
if (connection->parser_attribute_header_pos < AVRCP_BROWSING_ITEM_HEADER_LEN) {
connection->parser_attribute_header[connection->parser_attribute_header_pos++] = byte;
break;
}
uint16_t attribute_total_value_len = big_endian_read_16(connection->parser_attribute_header, 1);
connection->parsed_attribute_value[connection->parsed_attribute_value_offset++] = connection->parser_attribute_header[0]; // prepend with item type
connection->parsed_attribute_value_len = btstack_min(attribute_total_value_len, AVRCP_MAX_ATTRIBUTTE_SIZE - prepended_header_size); // reduce AVRCP_MAX_ATTRIBUTTE_SIZE for the size ot item type
connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_VALUE;
break;
}
case AVRCP_PARSER_GET_ATTRIBUTE_VALUE:{
if (connection->parsed_attribute_value_offset < connection->parsed_attribute_value_len + prepended_header_size){
connection->parsed_attribute_value[connection->parsed_attribute_value_offset++] = byte;
break;
}
if (connection->parsed_attribute_value_offset < big_endian_read_16(connection->parser_attribute_header, 1)){
connection->parser_state = AVRCP_PARSER_IGNORE_REST_OF_ATTRIBUTE_VALUE;
break;
}
connection->parsed_num_attributes++;
(*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, connection->l2cap_browsing_cid, &connection->parsed_attribute_value[0], connection->parsed_attribute_value_offset);
if (connection->parsed_num_attributes == connection->num_items){
avrcp_parser_reset(connection);
connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_HEADER;
connection->parser_attribute_header_pos = 0;
break;
}
break;
}
case AVRCP_PARSER_IGNORE_REST_OF_ATTRIBUTE_VALUE:
if (connection->parsed_attribute_value_offset < big_endian_read_16(connection->parser_attribute_header, 1) + prepended_header_size){
connection->parsed_attribute_value_offset++;
break;
}
connection->parsed_num_attributes++;
(*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, connection->l2cap_browsing_cid, &connection->parsed_attribute_value[0], connection->parsed_attribute_value_offset);
if (connection->parsed_num_attributes == connection->num_items){
avrcp_parser_reset(connection);
connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_HEADER;
connection->parser_attribute_header_pos = 0;
break;
}
break;
default:
break;
}
}
static void avrcp_browsing_parse_and_emit_element_attrs(uint8_t * packet, uint16_t num_bytes_to_read, avrcp_browsing_connection_t * connection){
int i;
for (i=0;i<num_bytes_to_read;i++){
avrcp_browsing_parser_process_byte(packet[i], connection);
}
}
static void avrcp_browsing_controller_emit_done(btstack_packet_handler_t callback, uint16_t browsing_cid, uint8_t browsing_status, uint8_t bluetooth_status){
avrcp_browsing_controller_emit_done_with_uid_counter(callback, browsing_cid, 0, browsing_status, bluetooth_status);
}
static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
avrcp_browsing_connection_t * browsing_connection;
@ -451,13 +522,10 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
// 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;
avrcp_pdu_id_t pdu_id;
uint8_t browsing_status;
uint16_t uid_counter;
if (!browsing_connection->fragmented){
browsing_connection->state = AVCTP_CONNECTION_OPENED;
// printf_hexdump(packet, size);
// printf("L2CAP_DATA_PACKET, packet type \n");
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;
pos += 2;
@ -465,29 +533,27 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
if (avctp_packet_type == AVRCP_START_PACKET){
browsing_connection->num_packets = packet[pos++];
}
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;
}
pdu_id = packet[pos++];
browsing_connection->pdu_id = packet[pos++];
// uint16_t length = big_endian_read_16(packet, pos);
pos += 2;
browsing_status = packet[pos++];
if (browsing_status != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_status, ERROR_CODE_SUCCESS);
browsing_connection->browsing_status = packet[pos++];
if (browsing_connection->browsing_status != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_connection->browsing_status, ERROR_CODE_SUCCESS);
return;
}
uid_counter = big_endian_read_16(packet, pos);
browsing_connection->uid_counter = big_endian_read_16(packet, pos);
pos += 2;
} else {
pdu_id = browsing_connection->fragmented_pdu_id;
browsing_status = browsing_connection->fragmented_browsing_status;
uid_counter = browsing_connection->fragmented_uid_counter;
break;
default:
break;
}
uint32_t i;
switch(pdu_id){
switch(browsing_connection->pdu_id){
case AVRCP_PDU_ID_CHANGE_PATH:
printf("AVRCP_PDU_ID_CHANGE_PATH \n");
break;
@ -497,11 +563,10 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
case AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS:{
uint32_t num_items = big_endian_read_32(packet, pos);
pos += 4;
printf("TDO: send event, uid_counter %d, num_items %d\n", uid_counter, num_items);
printf("TDO: send event, uid_counter %d, num_items %d\n", browsing_connection->uid_counter, num_items);
break;
}
case AVRCP_PDU_ID_SET_BROWSED_PLAYER:{
browsing_connection->browsed_player_uid_counter = uid_counter;
// uint32_t num_items = big_endian_read_32(packet, pos);
pos += 4;
// uint16_t charset = big_endian_read_16(packet, pos);
@ -516,52 +581,33 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
(*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos-1, folder_name_length+1);
pos += folder_name_length;
}
if (avctp_packet_type == AVRCP_SINGLE_PACKET || avctp_packet_type == AVRCP_END_PACKET){
avrcp_browsing_controller_emit_done_with_uid_counter(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_connection->browsed_player_uid_counter, browsing_status, ERROR_CODE_SUCCESS);
}
break;
}
case AVRCP_PDU_ID_GET_FOLDER_ITEMS:{
printf("AVRCP_PDU_ID_GET_FOLDER_ITEMS\n");
// send UID counter with done
if (!browsing_connection->fragmented){
browsing_connection->num_items = big_endian_read_16(packet, pos);
switch (avctp_packet_type){
case AVRCP_SINGLE_PACKET:
case AVRCP_START_PACKET:
avrcp_parser_reset(browsing_connection);
browsing_connection->num_items = big_endian_read_16(packet, pos); //num_items
pos += 2;
}
while (pos < size){
uint8_t header_size = 1;
if (browsing_connection->fragment_size == 0){
browsing_connection->item_type = packet[pos++];
browsing_connection->item_length = big_endian_read_16(packet, pos);
pos += 2;
// reuse packet and add data type as a header
packet[pos-1] = browsing_connection->item_type;
printf(" num items %d\n", browsing_connection->num_items);
avrcp_browsing_parse_and_emit_element_attrs(packet+pos, size-pos, browsing_connection);
break;
if (browsing_connection->item_length + pos > size) {
browsing_connection->fragment_size = size - pos;
browsing_connection->fragment[0] = browsing_connection->item_type;
memcpy(&browsing_connection->fragment[header_size], &packet[pos], browsing_connection->fragment_size);
case AVRCP_CONTINUE_PACKET:
avrcp_browsing_parse_and_emit_element_attrs(packet+pos, size-pos, browsing_connection);
break;
}
(*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, &packet[pos-header_size], browsing_connection->item_length+header_size);
browsing_connection->num_items--;
pos += browsing_connection->item_length;
} else {
uint16_t remaining_bytes_to_copy = browsing_connection->item_length - browsing_connection->fragment_size;
if (remaining_bytes_to_copy + pos > size) {
memcpy(&browsing_connection->fragment[browsing_connection->fragment_size+1], &packet[pos], size - pos);
browsing_connection->fragment_size += size - pos;
case AVRCP_END_PACKET:
avrcp_browsing_parse_and_emit_element_attrs(packet+pos, size-pos, browsing_connection);
avrcp_parser_reset(browsing_connection);
break;
}
memcpy(&browsing_connection->fragment[browsing_connection->fragment_size+1], &packet[pos], remaining_bytes_to_copy);
(*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, browsing_connection->fragment, browsing_connection->item_length+1);
browsing_connection->num_items--;
browsing_connection->fragment_size = 0;
pos += remaining_bytes_to_copy;
}
}
break;
}
case AVRCP_PDU_ID_SEARCH:{
uint32_t num_items = big_endian_read_32(packet, pos);
printf("TODO: send as event, search found %d items\n", num_items);
@ -575,18 +621,9 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
case AVRCP_SINGLE_PACKET:
case AVRCP_END_PACKET:
browsing_connection->state = AVCTP_CONNECTION_OPENED;
browsing_connection->fragmented = 0;
browsing_connection->num_items = 0;
browsing_connection->fragment_size = 0;
avrcp_browsing_controller_emit_done_with_uid_counter(avrcp_controller_context.browsing_avrcp_callback, channel, uid_counter, browsing_status, ERROR_CODE_SUCCESS);
avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_connection->browsing_status, ERROR_CODE_SUCCESS);
break;
case AVRCP_START_PACKET:
browsing_connection->fragmented = 1;
browsing_connection->fragmented_pdu_id = pdu_id;
browsing_connection->fragmented_browsing_status = browsing_status;
break;
case AVRCP_CONTINUE_PACKET:
browsing_connection->fragmented = 1;
default:
break;
}
break;

View File

@ -327,7 +327,7 @@ static void avrcp_parser_process_byte(uint8_t byte, avrcp_connection_t * connect
if (connection->attribute_value_offset < big_endian_read_16(connection->parser_attribute_header, 6)){
// printf("parse until end of valuE, and ignore it\n");
connection->parser_state = AVRCP_PARSER_CONTINUE_GET_ATTRIBUTE_VALUE;
connection->parser_state = AVRCP_PARSER_IGNORE_REST_OF_ATTRIBUTE_VALUE;
break;
}
@ -341,7 +341,7 @@ static void avrcp_parser_process_byte(uint8_t byte, avrcp_connection_t * connect
connection->parser_attribute_header_pos = 0;
break;
}
case AVRCP_PARSER_CONTINUE_GET_ATTRIBUTE_VALUE:
case AVRCP_PARSER_IGNORE_REST_OF_ATTRIBUTE_VALUE:
if (connection->attribute_value_offset < big_endian_read_16(connection->parser_attribute_header, 6)){
connection->list_offset++;
connection->attribute_value_offset++;

View File

@ -803,7 +803,7 @@ static void stdin_process(char * cmd, int size){
switch (cmd[1]){
case 'p':
// players[next_media_player_item_index()] = 1;
printf("AVRCP Browsing: get media players. Brosing cid 0x%02X\n", browsing_cid);
printf("AVRCP Browsing: get media players. Browsing cid 0x%02X\n", browsing_cid);
media_player_item_index = -1;
status = avrcp_browsing_controller_get_media_players(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
break;