From 4701e25071010b1259835f99e27aaa33b9efc5ce Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Sat, 5 Sep 2020 15:48:35 +0200 Subject: [PATCH] avrcp_controller: extract handle notification and send register notification functions --- src/classic/avrcp_controller.c | 133 +++++++++++++++++---------------- 1 file changed, 70 insertions(+), 63 deletions(-) diff --git a/src/classic/avrcp_controller.c b/src/classic/avrcp_controller.c index a5252be39..f0cfc0c79 100644 --- a/src/classic/avrcp_controller.c +++ b/src/classic/avrcp_controller.c @@ -423,8 +423,8 @@ static void avrcp_controller_parse_and_emit_element_attrs(uint8_t * packet, uint static int avrcp_send_cmd(avrcp_connection_t * connection, avrcp_packet_type_t packet_type){ - uint8_t command[AVRCP_CMD_BUFFER_SIZE]; - int pos = 0; + uint8_t command[AVRCP_CMD_BUFFER_SIZE]; + uint16_t pos = 0; // non-fragmented: transport header (1) + PID (2) // fragmented: transport header (1) + num packets (1) + PID (2) @@ -470,6 +470,31 @@ static int avrcp_send_cmd(avrcp_connection_t * connection, avrcp_packet_type_t p return l2cap_send(connection->l2cap_signaling_cid, command, pos); } +static int avrcp_send_register_notification(avrcp_connection_t * connection, uint8_t event_id){ + uint8_t command[18]; + uint16_t pos = 0; + // transport header : transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) + connection->transaction_label++; + command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0; + + command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; + command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; + command[pos++] = AVRCP_CTYPE_NOTIFY; + command[pos++] = (AVRCP_SUBUNIT_TYPE_PANEL << 3) | AVRCP_SUBUNIT_ID; + command[pos++] = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; + + big_endian_store_24(command, pos, BT_SIG_COMPANY_ID); + pos += 3; + command[pos++] = AVRCP_PDU_ID_REGISTER_NOTIFICATION; + command[pos++] = 0; // reserved(upper 6) | packet_type -> 0 + big_endian_store_16(command, pos, 5); // parameter length + pos += 2; + command[pos++] = event_id; + big_endian_store_32(command, pos, 1); // send notification on playback position every second, for other notifications it is ignored + pos += 4; + return l2cap_send(connection->l2cap_signaling_cid, command, pos); +} + static void avrcp_press_and_hold_timeout_handler(btstack_timer_source_t * timer){ UNUSED(timer); avrcp_connection_t * connection = (avrcp_connection_t*) btstack_run_loop_get_timer_context(timer); @@ -558,27 +583,6 @@ static int avrcp_controller_register_notification(avrcp_connection_t * connectio return 1; } -static void avrcp_controller_prepare_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){ - connection->transaction_label++; - connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; - connection->command_type = AVRCP_CTYPE_NOTIFY; - connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; - connection->subunit_id = AVRCP_SUBUNIT_ID; - int pos = 0; - big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); - pos += 3; - connection->cmd_operands[pos++] = AVRCP_PDU_ID_REGISTER_NOTIFICATION; - connection->cmd_operands[pos++] = 0; // reserved(upper 6) | packet_type -> 0 - big_endian_store_16(connection->cmd_operands, pos, 5); // parameter length - pos += 2; - connection->cmd_operands[pos++] = event_id; - big_endian_store_32(connection->cmd_operands, pos, 1); // send notification on playback position every second, for other notifications it is ignored - pos += 4; - connection->cmd_operands_length = pos; - // AVRCP_SPEC_V14.pdf 166 - // answer page 61 -} - static uint8_t avrcp_controller_request_abort_continuation(avrcp_connection_t * connection){ connection->state = AVCTP_W2_SEND_COMMAND; connection->transaction_label++; @@ -622,6 +626,37 @@ static uint8_t avrcp_controller_request_continue_response(avrcp_connection_t * c return ERROR_CODE_SUCCESS; } +static void avrcp_controller_handle_notification(avrcp_connection_t * connection, avrcp_command_type_t ctype, uint8_t * payload){ + uint16_t pos = 0; + avrcp_notification_event_id_t event_id = (avrcp_notification_event_id_t) payload[pos++]; + uint16_t event_mask = (1 << event_id); + uint16_t reset_event_mask = ~event_mask; + switch (ctype){ + case AVRCP_CTYPE_RESPONSE_INTERIM: + // register as enabled + connection->notifications_enabled |= event_mask; + break; + case AVRCP_CTYPE_RESPONSE_CHANGED_STABLE: + // received change, event is considered deregistered + // we are re-enabling it automatically, if it is not + // explicitly disabled + connection->notifications_enabled &= reset_event_mask; + if (! (connection->notifications_to_deregister & event_mask)){ + avrcp_controller_register_notification(connection, event_id); + } else { + connection->notifications_to_deregister &= reset_event_mask; + } + break; + default: + connection->notifications_to_register &= reset_event_mask; + connection->notifications_enabled &= reset_event_mask; + connection->notifications_to_deregister &= reset_event_mask; + break; + } + + avrcp_controller_emit_notification_for_event_id(connection->avrcp_cid, event_id, ctype, payload+pos); +} + static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){ if (size < 6u) return; @@ -684,12 +719,17 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec log_info("operands length %d, remaining size %d", param_length, size - pos); if ((size - pos) < param_length) { - printf_hexdump(packet, size); log_error("Wrong packet size %d < %d", size - pos, param_length); return; }; - if ((connection->state != AVCTP_W2_RECEIVE_RESPONSE) && (pdu_id != AVRCP_PDU_ID_REGISTER_NOTIFICATION)){ + // handle asynchronous notifications, without changing state + if (pdu_id == AVRCP_PDU_ID_REGISTER_NOTIFICATION){ + avrcp_controller_handle_notification(connection, ctype, packet+pos); + break; + } + + if (connection->state != AVCTP_W2_RECEIVE_RESPONSE){ log_info("AVRCP_CMD_OPCODE_VENDOR_DEPENDENT state %d", connection->state); return; } @@ -697,37 +737,6 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec log_info("VENDOR DEPENDENT response: pdu id 0x%02x, param_length %d, status %s", pdu_id, param_length, avrcp_ctype2str(ctype)); switch (pdu_id){ - case AVRCP_PDU_ID_REGISTER_NOTIFICATION:{ - avrcp_notification_event_id_t event_id = (avrcp_notification_event_id_t) packet[pos++]; - uint16_t event_mask = (1 << event_id); - uint16_t reset_event_mask = ~event_mask; - switch (ctype){ - case AVRCP_CTYPE_RESPONSE_INTERIM: - // register as enabled - connection->notifications_enabled |= event_mask; - break; - case AVRCP_CTYPE_RESPONSE_CHANGED_STABLE: - // received change, event is considered deregistered - // we are re-enabling it automatically, if it is not - // explicitly disabled - connection->notifications_enabled &= reset_event_mask; - if (! (connection->notifications_to_deregister & event_mask)){ - avrcp_controller_register_notification(connection, event_id); - } else { - connection->notifications_to_deregister &= reset_event_mask; - } - break; - default: - connection->notifications_to_register &= reset_event_mask; - connection->notifications_enabled &= reset_event_mask; - connection->notifications_to_deregister &= reset_event_mask; - break; - } - - avrcp_controller_emit_notification_for_event_id(connection->avrcp_cid, event_id, ctype, packet+pos); - break; - } - case AVRCP_PDU_ID_GET_CURRENT_PLAYER_APPLICATION_SETTING_VALUE:{ uint8_t num_attributes = packet[pos++]; int i; @@ -948,7 +957,6 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec } static void avrcp_controller_handle_can_send_now(avrcp_connection_t * connection){ - int i; switch (connection->state){ case AVCTP_W2_SEND_PRESS_COMMAND: connection->state = AVCTP_W2_RECEIVE_PRESS_RESPONSE; @@ -978,12 +986,11 @@ static void avrcp_controller_handle_can_send_now(avrcp_connection_t * connection } // send register notification if queued if (connection->notifications_to_register != 0){ - for (i = 1; i <= AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED; i++){ - if (connection->notifications_to_register & (1<notifications_to_register &= ~ (1 << i); - avrcp_controller_prepare_notification(connection, (avrcp_notification_event_id_t) i); - connection->state = AVCTP_W2_RECEIVE_RESPONSE; - avrcp_send_cmd(connection, AVRCP_SINGLE_PACKET); + uint8_t event_id; + for (event_id = 1; event_id <= AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED; event_id++){ + if (connection->notifications_to_register & (1<notifications_to_register &= ~ (1 << event_id); + avrcp_send_register_notification(connection, event_id); return; } }