mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-04-16 08:42:28 +00:00
avrcp target: implement passthrough 3 & 4
This commit is contained in:
parent
7078e43451
commit
12b7c8ec5c
@ -216,6 +216,15 @@ typedef enum {
|
||||
} avrcp_command_opcode_t;
|
||||
|
||||
typedef enum {
|
||||
AVRCP_OPERATION_ID_CHANNEL_UP = 0x30,
|
||||
AVRCP_OPERATION_ID_CHANNEL_DOWN = 0x31,
|
||||
AVRCP_OPERATION_ID_SELECT = 0x00,
|
||||
AVRCP_OPERATION_ID_UP = 0x01,
|
||||
AVRCP_OPERATION_ID_DOWN = 0x02,
|
||||
AVRCP_OPERATION_ID_LEFT = 0x03,
|
||||
AVRCP_OPERATION_ID_RIGHT = 0x04,
|
||||
AVRCP_OPERATION_ID_ROOT_MENU = 0x09,
|
||||
|
||||
AVRCP_OPERATION_ID_SKIP = 0x3C,
|
||||
AVRCP_OPERATION_ID_VOLUME_UP = 0x41,
|
||||
AVRCP_OPERATION_ID_VOLUME_DOWN = 0x42,
|
||||
|
@ -309,6 +309,26 @@ static int avrcp_target_send_response(uint16_t cid, avrcp_connection_t * connect
|
||||
return l2cap_send_prepared(cid, pos);
|
||||
}
|
||||
|
||||
static uint8_t avrcp_target_response_accept(avrcp_connection_t * connection, avrcp_subunit_type_t subunit_type, avrcp_subunit_id_t subunit_id, avrcp_command_opcode_t opcode, avrcp_pdu_id_t pdu_id, avrcp_status_code_t status){
|
||||
// AVRCP_CTYPE_RESPONSE_REJECTED
|
||||
connection->command_type = AVRCP_CTYPE_RESPONSE_ACCEPTED;
|
||||
connection->subunit_type = subunit_type;
|
||||
connection->subunit_id = subunit_id;
|
||||
connection->command_opcode = opcode;
|
||||
// company id is 3 bytes long
|
||||
int pos = connection->cmd_operands_length;
|
||||
connection->cmd_operands[pos++] = pdu_id;
|
||||
connection->cmd_operands[pos++] = 0;
|
||||
// param length
|
||||
big_endian_store_16(connection->cmd_operands, pos, 1);
|
||||
pos += 2;
|
||||
connection->cmd_operands[pos++] = status;
|
||||
connection->cmd_operands_length = pos;
|
||||
connection->state = AVCTP_W2_SEND_RESPONSE;
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t avrcp_target_response_reject(avrcp_connection_t * connection, avrcp_subunit_type_t subunit_type, avrcp_subunit_id_t subunit_id, avrcp_command_opcode_t opcode, avrcp_pdu_id_t pdu_id, avrcp_status_code_t status){
|
||||
// AVRCP_CTYPE_RESPONSE_REJECTED
|
||||
connection->command_type = AVRCP_CTYPE_RESPONSE_REJECTED;
|
||||
@ -620,14 +640,32 @@ void avrcp_target_set_now_playing_info(uint16_t avrcp_cid, const avrcp_track_t *
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t avrcp_target_track_changed(uint16_t avrcp_cid, uint8_t * track_id){
|
||||
avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context);
|
||||
if (!connection){
|
||||
log_error("avrcp_target_track_changed: could not find connection.");
|
||||
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
}
|
||||
if (!track_id) return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
|
||||
if (connection->notifications_enabled & (1 << AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED)) {
|
||||
connection->track_changed = 1;
|
||||
memcpy(connection->track_id, track_id, 8);
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
}
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t avrcp_target_playing_content_changed(uint16_t avrcp_cid){
|
||||
avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context);
|
||||
if (!connection){
|
||||
log_error("avrcp_unit_info: could not find a connection.");
|
||||
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
}
|
||||
if (connection->notifications_enabled & (1 << AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED)) {
|
||||
connection->playing_content_changed = 1;
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
}
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
@ -638,9 +676,11 @@ uint8_t avrcp_target_battery_status_changed(uint16_t avrcp_cid, avrcp_battery_st
|
||||
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
}
|
||||
if (connection->battery_status == battery_status) return ERROR_CODE_SUCCESS;
|
||||
if (connection->notifications_enabled & (1 << AVRCP_NOTIFICATION_EVENT_BATT_STATUS_CHANGED)) {
|
||||
connection->battery_status = battery_status;
|
||||
connection->battery_status_changed = 1;
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
}
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
@ -651,10 +691,11 @@ uint8_t avrcp_target_volume_changed(uint16_t avrcp_cid, uint8_t volume_percentag
|
||||
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
}
|
||||
if (connection->volume_percentage == volume_percentage) return ERROR_CODE_SUCCESS;
|
||||
|
||||
if (connection->notifications_enabled & (1 << AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED )) {
|
||||
connection->volume_percentage = volume_percentage;
|
||||
connection->volume_percentage_changed = 1;
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
}
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
@ -738,6 +779,15 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
|
||||
case AVRCP_OPERATION_ID_BACKWARD:
|
||||
case AVRCP_OPERATION_ID_SKIP:
|
||||
case AVRCP_OPERATION_ID_MUTE:
|
||||
case AVRCP_OPERATION_ID_CHANNEL_UP:
|
||||
case AVRCP_OPERATION_ID_CHANNEL_DOWN:
|
||||
case AVRCP_OPERATION_ID_SELECT:
|
||||
case AVRCP_OPERATION_ID_UP:
|
||||
case AVRCP_OPERATION_ID_DOWN:
|
||||
case AVRCP_OPERATION_ID_LEFT:
|
||||
case AVRCP_OPERATION_ID_RIGHT:
|
||||
case AVRCP_OPERATION_ID_ROOT_MENU:
|
||||
printf("received op 0x%02x\n", operation_id);
|
||||
avrcp_target_operation_accepted(connection->avrcp_cid, packet[6], packet[7], packet[8]);
|
||||
avrcp_target_emit_operation(avrcp_target_context.avrcp_callback, connection->avrcp_cid, operation_id, packet[7], packet[8]);
|
||||
break;
|
||||
@ -859,6 +909,24 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME: {
|
||||
uint16_t param_length = big_endian_read_16(pdu,2);
|
||||
if (param_length != 1){
|
||||
avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_COMMAND);
|
||||
printf("param_length is %d != 1 \n", param_length);
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t absolute_volume = pdu[4];
|
||||
if (absolute_volume > 0x7F){
|
||||
avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_COMMAND);
|
||||
printf("absolute_volume %d > 127 \n", absolute_volume);
|
||||
break;
|
||||
}
|
||||
connection->volume_percentage = absolute_volume;
|
||||
avrcp_target_response_accept(connection, subunit_type, subunit_id, opcode, pdu_id, absolute_volume);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_info("AVRCP target: unhandled pdu id 0x%02x", pdu_id);
|
||||
avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_COMMAND);
|
||||
@ -918,6 +986,16 @@ static int avrcp_target_send_notification(uint16_t cid, avrcp_connection_t * con
|
||||
return l2cap_send_prepared(cid, pos);
|
||||
}
|
||||
|
||||
static void avrcp_target_reset_notification(avrcp_connection_t * connection, uint8_t notification_id){
|
||||
if (!connection){
|
||||
log_error("avrcp tartget: could not find a connection.");
|
||||
return;
|
||||
}
|
||||
connection->notifications_enabled &= ~(1 << notification_id);
|
||||
connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
|
||||
|
||||
}
|
||||
|
||||
static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
avrcp_connection_t * connection;
|
||||
switch (packet_type) {
|
||||
@ -951,6 +1029,7 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
|
||||
if (connection->track_changed){
|
||||
connection->track_changed = 0;
|
||||
avrcp_target_send_notification(connection->l2cap_signaling_cid, connection, AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED, connection->track_id, 8);
|
||||
avrcp_target_reset_notification(connection, AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED);
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
break;
|
||||
}
|
||||
@ -958,6 +1037,7 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
|
||||
if (connection->playback_status_changed){
|
||||
connection->playback_status_changed = 0;
|
||||
avrcp_target_send_notification(connection->l2cap_signaling_cid, connection, AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED, &connection->playback_status_changed, 1);
|
||||
avrcp_target_reset_notification(connection, AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED);
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
break;
|
||||
}
|
||||
@ -965,6 +1045,7 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
|
||||
if (connection->playing_content_changed){
|
||||
connection->playing_content_changed = 0;
|
||||
avrcp_target_send_notification(connection->l2cap_signaling_cid, connection, AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED, NULL, 0);
|
||||
avrcp_target_reset_notification(connection, AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED);
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
break;
|
||||
}
|
||||
@ -972,6 +1053,7 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
|
||||
if (connection->battery_status_changed){
|
||||
connection->battery_status_changed = 0;
|
||||
avrcp_target_send_notification(connection->l2cap_signaling_cid, connection, AVRCP_NOTIFICATION_EVENT_BATT_STATUS_CHANGED, (uint8_t *)&connection->battery_status, 1);
|
||||
avrcp_target_reset_notification(connection, AVRCP_NOTIFICATION_EVENT_BATT_STATUS_CHANGED);
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
break;
|
||||
}
|
||||
@ -979,6 +1061,7 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
|
||||
if (connection->volume_percentage_changed){
|
||||
connection->volume_percentage_changed = 0;
|
||||
avrcp_target_send_notification(connection->l2cap_signaling_cid, connection, AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED, &connection->volume_percentage, 1);
|
||||
avrcp_target_reset_notification(connection, AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED);
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
break;
|
||||
}
|
||||
|
@ -87,6 +87,9 @@ void avrcp_target_set_subunit_info(uint16_t avrcp_cid, avrcp_subunit_type_t subu
|
||||
uint8_t avrcp_target_playing_content_changed(uint16_t avrcp_cid);
|
||||
uint8_t avrcp_target_battery_status_changed(uint16_t avrcp_cid, avrcp_battery_status_t battery_status);
|
||||
uint8_t avrcp_target_volume_changed(uint16_t avrcp_cid, uint8_t volume_percentage);
|
||||
uint8_t avrcp_target_track_changed(uint16_t avrcp_cid, uint8_t * trackID);
|
||||
|
||||
// uint8_t avrcp_target_trigger_notification(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id);
|
||||
|
||||
|
||||
uint8_t avrcp_target_operation_rejected(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint8_t operands_length, uint8_t operand);
|
||||
|
@ -135,14 +135,13 @@ AVDTP_OBJ = $(AVDTP:.c=.o)
|
||||
HXCMOD_PLAYER_OBJ = ${HXCMOD_PLAYER:.c=.o}
|
||||
|
||||
EXAMPLES = iopt ble_peripheral_test ble_central_test l2cap_test classic_test bnep_test hsp_ag_test hsp_hs_test sco_loopback le_data_channel
|
||||
EXAMPLES += avdtp_source_test avdtp_sink_test le_data_channel avrcp_controller_test sm_test
|
||||
# avrcp_target_test
|
||||
EXAMPLES += avdtp_source_test avdtp_sink_test le_data_channel avrcp_controller_test sm_test avrcp_target_test
|
||||
|
||||
all: ${EXAMPLES}
|
||||
|
||||
# compile .gatt descriptions
|
||||
# avrcp_target_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_ENCODER_OBJ} ${SM_OBJ} ${AVDTP_OBJ} ${HXCMOD_PLAYER_OBJ} avrcp.o avrcp_target.o avrcp_target_test.c
|
||||
# ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
|
||||
avrcp_target_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_ENCODER_OBJ} ${SM_OBJ} ${AVDTP_OBJ} ${HXCMOD_PLAYER_OBJ} avrcp_media_item_iterator.o avrcp_controller.o avrcp_browsing_controller.o btstack_stdin_pts.o avrcp.o avrcp_target.o avrcp_target_test.c
|
||||
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
avrcp_controller_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SM_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_OBJ} sdp_client.o avrcp.o avrcp_media_item_iterator.o avrcp_controller.o avrcp_browsing_controller.o btstack_stdin_pts.o avrcp_controller_test.o
|
||||
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
Loading…
x
Reference in New Issue
Block a user