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 BT_SIG_COMPANY_ID 0x001958
#define AVRCP_MEDIA_ATTR_COUNT 7 #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_ATTRIBUTE_HEADER_LEN 8
#define AVRCP_MAX_FOLDER_NAME_SIZE 20 #define AVRCP_MAX_FOLDER_NAME_SIZE 20
@ -287,9 +287,12 @@ typedef struct {
typedef enum { typedef enum {
AVRCP_PARSER_GET_ATTRIBUTE_HEADER = 0, // 8 bytes AVRCP_PARSER_GET_ATTRIBUTE_HEADER = 0, // 8 bytes
AVRCP_PARSER_GET_ATTRIBUTE_VALUE, AVRCP_PARSER_GET_ATTRIBUTE_VALUE,
AVRCP_PARSER_CONTINUE_GET_ATTRIBUTE_VALUE AVRCP_PARSER_IGNORE_REST_OF_ATTRIBUTE_VALUE
} avrcp_parser_state_t; } avrcp_parser_state_t;
#define AVRCP_BROWSING_ITEM_HEADER_LEN 3
// BROWSING // BROWSING
typedef struct { typedef struct {
uint16_t l2cap_browsing_cid; uint16_t l2cap_browsing_cid;
@ -308,8 +311,7 @@ typedef struct {
// players // players
uint8_t set_browsed_player_id; uint8_t set_browsed_player_id;
uint16_t browsed_player_id; uint16_t browsed_player_id;
uint16_t browsed_player_uid_counter;
// get folder item // get folder item
uint8_t get_folder_item; uint8_t get_folder_item;
avrcp_browsing_scope_t scope; avrcp_browsing_scope_t scope;
@ -332,16 +334,17 @@ typedef struct {
uint8_t get_total_nr_items; uint8_t get_total_nr_items;
avrcp_browsing_scope_t get_total_nr_items_scope; avrcp_browsing_scope_t get_total_nr_items_scope;
// fragmentation avrcp_pdu_id_t pdu_id;
uint8_t fragmented; uint8_t browsing_status;
avrcp_pdu_id_t fragmented_pdu_id;
uint8_t fragmented_browsing_status;
uint16_t fragmented_uid_counter;
uint16_t num_items; uint16_t num_items;
uint8_t item_type;
uint16_t item_length; avrcp_parser_state_t parser_state;
uint16_t fragment_size; uint8_t parser_attribute_header[AVRCP_BROWSING_ITEM_HEADER_LEN];
uint8_t fragment[AVRCP_MAX_ATTRIBUTTE_SIZE]; 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; } avrcp_browsing_connection_t;
// BROWSING END // 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); big_endian_store_16(command, pos, 11);
pos += 2; pos += 2;
big_endian_store_16(command, pos, connection->browsed_player_uid_counter);
pos += 2; pos += 2;
command[pos++] = connection->direction; command[pos++] = connection->direction;
memcpy(command+pos, connection->folder_uid, 8); 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)); (*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){ 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); 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){ 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; avrcp_browsing_connection_t * browsing_connection;
@ -451,43 +522,38 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
// Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
// uint8_t transaction_label = transport_header >> 4; // uint8_t transaction_label = transport_header >> 4;
avrcp_packet_type_t avctp_packet_type = (transport_header & 0x0F) >> 2; avrcp_packet_type_t avctp_packet_type = (transport_header & 0x0F) >> 2;
// printf("L2CAP_DATA_PACKET, packet type \n");
avrcp_pdu_id_t pdu_id; switch (avctp_packet_type){
uint8_t browsing_status; case AVRCP_SINGLE_PACKET:
uint16_t uid_counter; case AVRCP_START_PACKET:
if (!browsing_connection->fragmented){ // uint8_t frame_type = (transport_header & 0x03) >> 1;
browsing_connection->state = AVCTP_CONNECTION_OPENED; // uint8_t ipid = transport_header & 0x01;
// printf_hexdump(packet, size); pos += 2;
// uint8_t frame_type = (transport_header & 0x03) >> 1; browsing_connection->num_packets = 1;
// uint8_t ipid = transport_header & 0x01; if (avctp_packet_type == AVRCP_START_PACKET){
pos += 2; browsing_connection->num_packets = packet[pos++];
browsing_connection->num_packets = 1; }
if (avctp_packet_type == AVRCP_START_PACKET){ if (pos + 4 > size){
browsing_connection->num_packets = packet[pos++]; avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS);
} return;
}
if (pos + 4 > size){ browsing_connection->pdu_id = packet[pos++];
avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS); // uint16_t length = big_endian_read_16(packet, pos);
return; pos += 2;
} browsing_connection->browsing_status = packet[pos++];
pdu_id = packet[pos++]; if (browsing_connection->browsing_status != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
// uint16_t length = big_endian_read_16(packet, pos); avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_connection->browsing_status, ERROR_CODE_SUCCESS);
pos += 2; return;
browsing_status = packet[pos++]; }
if (browsing_status != AVRCP_BROWSING_ERROR_CODE_SUCCESS){ browsing_connection->uid_counter = big_endian_read_16(packet, pos);
avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_status, ERROR_CODE_SUCCESS); pos += 2;
return; break;
} default:
uid_counter = big_endian_read_16(packet, pos); break;
pos += 2;
} else {
pdu_id = browsing_connection->fragmented_pdu_id;
browsing_status = browsing_connection->fragmented_browsing_status;
uid_counter = browsing_connection->fragmented_uid_counter;
} }
uint32_t i; uint32_t i;
switch(pdu_id){ switch(browsing_connection->pdu_id){
case AVRCP_PDU_ID_CHANGE_PATH: case AVRCP_PDU_ID_CHANGE_PATH:
printf("AVRCP_PDU_ID_CHANGE_PATH \n"); printf("AVRCP_PDU_ID_CHANGE_PATH \n");
break; 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:{ case AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS:{
uint32_t num_items = big_endian_read_32(packet, pos); uint32_t num_items = big_endian_read_32(packet, pos);
pos += 4; 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; break;
} }
case AVRCP_PDU_ID_SET_BROWSED_PLAYER:{ 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); // uint32_t num_items = big_endian_read_32(packet, pos);
pos += 4; pos += 4;
// uint16_t charset = big_endian_read_16(packet, pos); // 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); (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos-1, folder_name_length+1);
pos += folder_name_length; 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; break;
} }
case AVRCP_PDU_ID_GET_FOLDER_ITEMS:{ case AVRCP_PDU_ID_GET_FOLDER_ITEMS:{
printf("AVRCP_PDU_ID_GET_FOLDER_ITEMS\n"); printf("AVRCP_PDU_ID_GET_FOLDER_ITEMS\n");
// send UID counter with done switch (avctp_packet_type){
if (!browsing_connection->fragmented){ case AVRCP_SINGLE_PACKET:
browsing_connection->num_items = big_endian_read_16(packet, pos); case AVRCP_START_PACKET:
pos += 2; avrcp_parser_reset(browsing_connection);
} browsing_connection->num_items = big_endian_read_16(packet, pos); //num_items
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; pos += 2;
// reuse packet and add data type as a header printf(" num items %d\n", browsing_connection->num_items);
packet[pos-1] = browsing_connection->item_type; 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; case AVRCP_CONTINUE_PACKET:
browsing_connection->fragment[0] = browsing_connection->item_type; avrcp_browsing_parse_and_emit_element_attrs(packet+pos, size-pos, browsing_connection);
memcpy(&browsing_connection->fragment[header_size], &packet[pos], browsing_connection->fragment_size); break;
break;
} case AVRCP_END_PACKET:
(*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, &packet[pos-header_size], browsing_connection->item_length+header_size); avrcp_browsing_parse_and_emit_element_attrs(packet+pos, size-pos, browsing_connection);
browsing_connection->num_items--; avrcp_parser_reset(browsing_connection);
pos += browsing_connection->item_length; break;
} 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;
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; break;
} }
case AVRCP_PDU_ID_SEARCH:{ case AVRCP_PDU_ID_SEARCH:{
uint32_t num_items = big_endian_read_32(packet, pos); uint32_t num_items = big_endian_read_32(packet, pos);
printf("TODO: send as event, search found %d items\n", num_items); 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_SINGLE_PACKET:
case AVRCP_END_PACKET: case AVRCP_END_PACKET:
browsing_connection->state = AVCTP_CONNECTION_OPENED; browsing_connection->state = AVCTP_CONNECTION_OPENED;
browsing_connection->fragmented = 0; avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_connection->browsing_status, ERROR_CODE_SUCCESS);
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);
break; break;
case AVRCP_START_PACKET: default:
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;
break; break;
} }
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)){ if (connection->attribute_value_offset < big_endian_read_16(connection->parser_attribute_header, 6)){
// printf("parse until end of valuE, and ignore it\n"); // 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; break;
} }
@ -341,7 +341,7 @@ static void avrcp_parser_process_byte(uint8_t byte, avrcp_connection_t * connect
connection->parser_attribute_header_pos = 0; connection->parser_attribute_header_pos = 0;
break; 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)){ if (connection->attribute_value_offset < big_endian_read_16(connection->parser_attribute_header, 6)){
connection->list_offset++; connection->list_offset++;
connection->attribute_value_offset++; connection->attribute_value_offset++;

View File

@ -803,7 +803,7 @@ static void stdin_process(char * cmd, int size){
switch (cmd[1]){ switch (cmd[1]){
case 'p': case 'p':
// players[next_media_player_item_index()] = 1; // 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; media_player_item_index = -1;
status = avrcp_browsing_controller_get_media_players(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL); status = avrcp_browsing_controller_get_media_players(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
break; break;