diff --git a/src/classic/avrcp.c b/src/classic/avrcp.c index 4e90ffd32..6354ad03c 100644 --- a/src/classic/avrcp.c +++ b/src/classic/avrcp.c @@ -61,6 +61,9 @@ static uint8_t attribute_value[1000]; static const unsigned int attribute_value_buffer_size = sizeof(attribute_value); static btstack_linked_list_t connections; +static btstack_packet_handler_t avrcp_controller_packet_handler; +static btstack_packet_handler_t avrcp_target_packet_handler; +// static int l2cap_service_registered = 0; static const char * avrcp_subunit_type_name[] = { "MONITOR", "AUDIO", "PRINTER", "DISC", "TAPE_RECORDER_PLAYER", "TUNER", @@ -591,77 +594,98 @@ void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet avrcp_connection_t * connection = NULL; uint16_t psm; - if (packet_type != HCI_EVENT_PACKET) return; + switch (packet_type) { - switch (hci_event_packet_get_type(packet)) { - case HCI_EVENT_DISCONNECTION_COMPLETE: - avrcp_emit_connection_closed(context->avrcp_callback, 0); +#if 0 + case L2CAP_DATA_PACKET: + // figure out role! + connection = get_avrcp_connection_for_l2cap_signaling_cid(AVRCP_CONTROLLER, channel); + // or? + connection = get_avrcp_connection_for_l2cap_signaling_cid(AVRCP_TARGET, channel); + if (!connection) break; + // call correct controller/target packet hander break; - case L2CAP_EVENT_INCOMING_CONNECTION: - l2cap_event_incoming_connection_get_address(packet, event_addr); - local_cid = l2cap_event_incoming_connection_get_local_cid(packet); - connection = avrcp_create_connection(context->role, event_addr); - if (!connection) { - log_error("Failed to alloc connection structure"); - l2cap_decline_connection(local_cid); - break; - } - - psm = l2cap_event_incoming_connection_get_psm(packet); - if (psm == PSM_AVCTP){ - if (!connection->l2cap_signaling_cid){ - connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED; - connection->l2cap_signaling_cid = local_cid; - log_info("L2CAP_EVENT_INCOMING_CONNECTION avrcp_cid 0x%02x, l2cap_signaling_cid 0x%02x", connection->avrcp_cid, connection->l2cap_signaling_cid); - l2cap_accept_connection(local_cid); +#endif + + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + case HCI_EVENT_DISCONNECTION_COMPLETE: + avrcp_emit_connection_closed(context->avrcp_callback, 0); break; - } - } - log_info("L2CAP_EVENT_INCOMING_CONNECTION local_cid 0x%02x, psm 0x%2x, decline connection", local_cid, psm); - l2cap_decline_connection(local_cid); - break; - - case L2CAP_EVENT_CHANNEL_OPENED: - l2cap_event_channel_opened_get_address(packet, event_addr); - status = l2cap_event_channel_opened_get_status(packet); - local_cid = l2cap_event_channel_opened_get_local_cid(packet); - - connection = get_avrcp_connection_for_bd_addr(context->role, event_addr); - if (!connection){ - // TODO: validate if this cannot happen. If not, drop disconnect call - log_error("AVRCP connection lookup failed"); - l2cap_disconnect(local_cid, 0); // reason isn't used - break; - } - if (status != ERROR_CODE_SUCCESS){ - log_info("L2CAP connection to connection %s failed. status code 0x%02x", bd_addr_to_str(event_addr), status); - avrcp_emit_connection_established(context->avrcp_callback, connection->avrcp_cid, event_addr, status); - avrcp_finalize_connection(connection); - break; - } + case L2CAP_EVENT_INCOMING_CONNECTION: + l2cap_event_incoming_connection_get_address(packet, event_addr); + local_cid = l2cap_event_incoming_connection_get_local_cid(packet); - psm = l2cap_event_channel_opened_get_psm(packet); - if (psm == PSM_AVCTP){ - connection->l2cap_signaling_cid = local_cid; - connection->l2cap_mtu = l2cap_event_channel_opened_get_remote_mtu(packet); - connection->song_length_ms = 0xFFFFFFFF; - connection->song_position_ms = 0xFFFFFFFF; - connection->playback_status = AVRCP_PLAYBACK_STATUS_ERROR; + // create two connection objects (both) + connection = avrcp_create_connection(context->role, event_addr); + if (!connection) { + log_error("Failed to alloc connection structure"); + l2cap_decline_connection(local_cid); + break; + } + + psm = l2cap_event_incoming_connection_get_psm(packet); + if (psm == PSM_AVCTP){ + if (!connection->l2cap_signaling_cid){ + connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED; + connection->l2cap_signaling_cid = local_cid; + log_info("L2CAP_EVENT_INCOMING_CONNECTION avrcp_cid 0x%02x, l2cap_signaling_cid 0x%02x", connection->avrcp_cid, connection->l2cap_signaling_cid); + l2cap_accept_connection(local_cid); + break; + } + } + log_info("L2CAP_EVENT_INCOMING_CONNECTION local_cid 0x%02x, psm 0x%2x, decline connection", local_cid, psm); + l2cap_decline_connection(local_cid); + break; + + case L2CAP_EVENT_CHANNEL_OPENED: + l2cap_event_channel_opened_get_address(packet, event_addr); + status = l2cap_event_channel_opened_get_status(packet); + local_cid = l2cap_event_channel_opened_get_local_cid(packet); + + connection = get_avrcp_connection_for_bd_addr(context->role, event_addr); + if (!connection){ + // TODO: validate if this cannot happen. If not, drop disconnect call + log_error("AVRCP connection lookup failed"); + l2cap_disconnect(local_cid, 0); // reason isn't used + break; + } + if (status != ERROR_CODE_SUCCESS){ + log_info("L2CAP connection to connection %s failed. status code 0x%02x", bd_addr_to_str(event_addr), status); + // emit twice for each role + avrcp_emit_connection_established(context->avrcp_callback, connection->avrcp_cid, event_addr, status); + avrcp_finalize_connection(connection); + break; + } - log_info("L2CAP_EVENT_CHANNEL_OPENED avrcp_cid 0x%02x, l2cap_signaling_cid 0x%02x", connection->avrcp_cid, connection->l2cap_signaling_cid); - connection->state = AVCTP_CONNECTION_OPENED; - avrcp_emit_connection_established(context->avrcp_callback, connection->avrcp_cid, event_addr, ERROR_CODE_SUCCESS); - } - break; - - case L2CAP_EVENT_CHANNEL_CLOSED: - // data: event (8), len(8), channel (16) - local_cid = l2cap_event_channel_closed_get_local_cid(packet); - connection = get_avrcp_connection_for_l2cap_signaling_cid(context->role, local_cid); - if (connection){ - avrcp_emit_connection_closed(context->avrcp_callback, connection->avrcp_cid); - avrcp_finalize_connection(connection); - break; + psm = l2cap_event_channel_opened_get_psm(packet); + if (psm == PSM_AVCTP){ + connection->l2cap_signaling_cid = local_cid; + connection->l2cap_mtu = l2cap_event_channel_opened_get_remote_mtu(packet); + connection->song_length_ms = 0xFFFFFFFF; + connection->song_position_ms = 0xFFFFFFFF; + connection->playback_status = AVRCP_PLAYBACK_STATUS_ERROR; + + log_info("L2CAP_EVENT_CHANNEL_OPENED avrcp_cid 0x%02x, l2cap_signaling_cid 0x%02x", connection->avrcp_cid, connection->l2cap_signaling_cid); + connection->state = AVCTP_CONNECTION_OPENED; + // emit twice for each role + avrcp_emit_connection_established(context->avrcp_callback, connection->avrcp_cid, event_addr, ERROR_CODE_SUCCESS); + } + break; + + case L2CAP_EVENT_CHANNEL_CLOSED: + // data: event (8), len(8), channel (16) + local_cid = l2cap_event_channel_closed_get_local_cid(packet); + connection = get_avrcp_connection_for_l2cap_signaling_cid(context->role, local_cid); + // emit twice for each role + if (connection){ + avrcp_emit_connection_closed(context->avrcp_callback, connection->avrcp_cid); + avrcp_finalize_connection(connection); + break; + } + break; + default: + break; } break; default: @@ -698,4 +722,21 @@ uint8_t avrcp_connect(avrcp_role_t role, bd_addr_t bd_addr, avrcp_context_t * co void avrcp_init(void){ connections = NULL; + +#if 0 + if (l2cap_service_registered) return; + int status = l2cap_register_service(&avrcp_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_2); + if (status != ERROR_CODE_SUCCESS) return; + l2cap_service_registered = 1; +#endif } + +void avrcp_register_controller_packet_handler(btstack_packet_handler_t callback){ + avrcp_controller_packet_handler = callback; +} + + +void avrcp_register_target_packet_handler(btstack_packet_handler_t callback){ + avrcp_target_packet_handler = callback; +} + diff --git a/src/classic/avrcp.h b/src/classic/avrcp.h index 9e73a3e34..c8c48c0eb 100644 --- a/src/classic/avrcp.h +++ b/src/classic/avrcp.h @@ -517,7 +517,7 @@ typedef struct { btstack_packet_handler_t browsing_packet_handler; // SDP query - uint8_t parse_sdp_record; + uint8_t parse_sdp_record; uint32_t record_id; uint16_t avrcp_cid; uint16_t avrcp_l2cap_psm; @@ -536,6 +536,9 @@ const char * avrcp_shuffle2str(uint8_t index); void avrcp_init(void); +void avrcp_register_controller_packet_handler(btstack_packet_handler_t avrcp_controller_packet_handler); +void avrcp_register_target_packet_handler(btstack_packet_handler_t avrcp_target_packet_handler); + void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size, avrcp_context_t * context); void avrcp_create_sdp_record(uint8_t controller, uint8_t * service, uint32_t service_record_handle, uint8_t browsing, uint16_t supported_features, const char * service_name, const char * service_provider_name); diff --git a/src/classic/avrcp_controller.c b/src/classic/avrcp_controller.c index eb1f9f429..60a62d7c6 100644 --- a/src/classic/avrcp_controller.c +++ b/src/classic/avrcp_controller.c @@ -931,6 +931,7 @@ void avrcp_controller_init(void){ avrcp_init(); avrcp_controller_context.role = AVRCP_CONTROLLER; avrcp_controller_context.packet_handler = avrcp_controller_packet_handler; + avrcp_register_controller_packet_handler(&avrcp_controller_packet_handler); l2cap_register_service(&avrcp_controller_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_2); } diff --git a/src/classic/avrcp_target.c b/src/classic/avrcp_target.c index 1af12b6cf..f374d2137 100644 --- a/src/classic/avrcp_target.c +++ b/src/classic/avrcp_target.c @@ -1242,6 +1242,7 @@ void avrcp_target_init(void){ avrcp_init(); avrcp_target_context.role = AVRCP_TARGET; avrcp_target_context.packet_handler = avrcp_target_packet_handler; + avrcp_register_controller_packet_handler(&avrcp_target_packet_handler); l2cap_register_service(&avrcp_target_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_2); }