avrcp_controller: reassemble fragmented AVCTP packets

This commit is contained in:
Milanka Ringwald 2021-05-21 14:05:15 +02:00
parent ce66cc7ade
commit c3b8c0a2ef
4 changed files with 87 additions and 13 deletions

View File

@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- AVRCP: new field `button_pressed` in `AVRCP_SUBEVENT_OPERATION`
- AVRCP: `AVRCP_SUBEVENT_OPERATION` emitted for button release
- AVRCP Controller: avrcp_controller_start_press_and_hold_cmd helps to support device buttons
- AVRCP Controller: reassemble fragmented AVCTP packets
- AVDTP: `avdtp_register_media_config_validator` allows to validate media codec configuration
- A2DP Source: `ENABLE_A2DP_SOURCE_EXPLICIT_CONFIG` disables auto config. Requires call to `a2dp_source_set_config_{CODEC}'

View File

@ -111,6 +111,14 @@ typedef enum {
AVRCP_END_PACKET
} avrcp_packet_type_t;
typedef enum {
AVCTP_SINGLE_PACKET= 0,
AVCTP_START_PACKET ,
AVCTP_CONTINUE_PACKET ,
AVCTP_END_PACKET
} avctp_packet_type_t;
typedef enum {
AVRCP_COMMAND_FRAME = 0,
AVRCP_RESPONSE_FRAME
@ -375,7 +383,7 @@ typedef struct {
avctp_connection_state_t state;
bool wait_to_send;
uint8_t transaction_label;
// used for AVCTP fragmentation
// used for fragmentation
uint8_t num_packets;
uint16_t bytes_to_send;
@ -555,6 +563,12 @@ typedef struct {
uint8_t num_received_fragments;
uint8_t accept_response;
#ifdef ENABLE_AVCTP_FRAGMENTATION
uint16_t avctp_reassembly_size;
uint8_t avctp_reassembly_buffer[200];
#endif
} avrcp_connection_t;
typedef struct {

View File

@ -690,27 +690,86 @@ avrcp_controller_handle_notification(avrcp_connection_t *connection, avrcp_comma
avrcp_controller_emit_notification_for_event_id(connection->avrcp_cid, event_id, ctype, payload + pos, size - pos);
}
#ifdef ENABLE_AVCTP_FRAGMENTATION
static void avctp_reassemble_message(avrcp_connection_t * connection, avctp_packet_type_t packet_type, uint8_t *packet, uint16_t size){
// after header (transaction label and packet type)
uint16_t pos;
uint16_t bytes_to_store;
switch (packet_type){
case AVCTP_START_PACKET:
if (size < 2) return;
// store header
pos = 0;
connection->avctp_reassembly_buffer[pos] = packet[pos];
pos++;
connection->avctp_reassembly_size = pos;
// NOTE: num packets not needed for reassembly, ignoring it does not pose security risk -> no need to store it
pos++;
// PID in reassembled packet is at offset 1, it will be read later after the avctp_reassemble_message with AVCTP_END_PACKET is called
bytes_to_store = btstack_min(size - pos, sizeof(connection->avctp_reassembly_buffer) - connection->avctp_reassembly_size);
memcpy(&connection->avctp_reassembly_buffer[connection->avctp_reassembly_size], &packet[pos], bytes_to_store);
connection->avctp_reassembly_size += bytes_to_store;
break;
case AVCTP_CONTINUE_PACKET:
case AVCTP_END_PACKET:
if (size < 1) return;
// store remaining data, ignore header
pos = 1;
bytes_to_store = btstack_min(size - pos, sizeof(connection->avctp_reassembly_buffer) - connection->avctp_reassembly_size);
memcpy(&connection->avctp_reassembly_buffer[connection->avctp_reassembly_size], &packet[pos], bytes_to_store);
connection->avctp_reassembly_size += bytes_to_store;
break;
default:
return;
}
}
#endif
static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){
if (size < 6u) return;
uint8_t pdu_id;
uint8_t vendor_dependent_packet_type;
avrcp_packet_type_t vendor_dependent_packet_type;
connection->last_confirmed_transaction_id = packet[0] >> 4;
uint16_t pos = 0;
connection->last_confirmed_transaction_id = packet[pos] >> 4;
avrcp_frame_type_t frame_type = (avrcp_frame_type_t)((packet[pos] >> 1) & 0x01);
avctp_packet_type_t packet_type = (avctp_packet_type_t)((packet[pos] >> 2) & 0x03);
pos++;
avrcp_frame_type_t frame_type = (avrcp_frame_type_t)((packet[0] >> 1) & 0x01);
if (frame_type != AVRCP_RESPONSE_FRAME) return;
avrcp_packet_type_t packet_type = (avrcp_packet_type_t)((packet[0] >> 2) & 0x03);
switch (packet_type){
case AVRCP_SINGLE_PACKET:
case AVCTP_SINGLE_PACKET:
break;
#ifdef ENABLE_AVCTP_FRAGMENTATION
case AVCTP_START_PACKET:
case AVCTP_CONTINUE_PACKET:
avctp_reassemble_message(connection, packet_type, packet, size);
return;
case AVCTP_END_PACKET:
avctp_reassemble_message(connection, packet_type, packet, size);
packet = connection->avctp_reassembly_buffer;
size = connection->avctp_reassembly_size;
break;
#endif
default:
log_info("Fragmentation is not supported");
return;
}
uint16_t pos = 3;
pos += 2; // PID
avrcp_command_type_t ctype = (avrcp_command_type_t) packet[pos++];
#ifdef ENABLE_LOG_INFO
@ -763,7 +822,6 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
vendor_dependent_packet_type = (avrcp_packet_type_t)(packet[pos++] & 0x03);
param_length = big_endian_read_16(packet, pos);
pos += 2;
log_info("operands length %d, remaining size %d", param_length, size - pos);
if ((size - pos) < param_length) return;

View File

@ -28,6 +28,7 @@
#define ENABLE_LE_PERIPHERAL
#define ENABLE_LE_SIGNED_WRITE
#define ENABLE_SDP_EXTRA_QUERIES
#define ENABLE_AVCTP_FRAGMENTATION
// BTstack configuration. buffers, sizes, ...
#define HCI_ACL_PAYLOAD_SIZE 1024