mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-04-17 11:42:34 +00:00
avrcp: pass through basic controll cmds, not all working
This commit is contained in:
parent
49bbb6f27b
commit
d144ca3efe
@ -288,9 +288,81 @@ static avrcp_connection_t * get_avrcp_connection_for_l2cap_signaling_cid(uint16_
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void avrcp_request_can_send_now(avrcp_connection_t * connection, uint16_t l2cap_cid){
|
||||
connection->wait_to_send = 1;
|
||||
l2cap_request_can_send_now_event(l2cap_cid);
|
||||
}
|
||||
|
||||
static void request_pass_through_release_control_cmd(avrcp_connection_t * connection){
|
||||
connection->state = AVCTP_W2_SEND_RELEASE_COMMAND;
|
||||
connection->transaction_label++;
|
||||
connection->cmd_operands[0] = 0x80 | connection->cmd_operands[0];
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
}
|
||||
|
||||
static void request_pass_through_press_control_cmd(uint16_t con_handle, avrcp_operation_id_t opid, uint16_t playback_speed){
|
||||
avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle);
|
||||
if (!connection){
|
||||
log_error("avrcp: coud not find a connection.");
|
||||
return;
|
||||
}
|
||||
if (connection->state != AVCTP_CONNECTION_OPENED) return;
|
||||
connection->state = AVCTP_W2_SEND_PRESS_COMMAND;
|
||||
|
||||
connection->transaction_label++;
|
||||
connection->cmd_to_send = AVRCP_CMD_OPCODE_PASS_THROUGH;
|
||||
connection->command_type = AVRCP_CTYPE_CONTROL;
|
||||
connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
|
||||
connection->subunit_id = 0;
|
||||
connection->cmd_operands_lenght = 0;
|
||||
|
||||
connection->cmd_operands_lenght = 2;
|
||||
connection->cmd_operands[0] = opid;
|
||||
if (playback_speed > 0){
|
||||
connection->cmd_operands[2] = playback_speed;
|
||||
connection->cmd_operands_lenght++;
|
||||
}
|
||||
connection->cmd_operands[1] = connection->cmd_operands_lenght - 2;
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
}
|
||||
|
||||
|
||||
static int avrcp_send_cmd(uint16_t cid, avrcp_connection_t * connection){
|
||||
uint8_t command[20];
|
||||
int pos = 0;
|
||||
// transport header
|
||||
// Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
|
||||
command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
|
||||
// Profile IDentifier (PID)
|
||||
command[pos++] = AV_REMOTE_CONTROL >> 8;
|
||||
command[pos++] = AV_REMOTE_CONTROL & 0x00FF;
|
||||
|
||||
// command_type
|
||||
command[pos++] = connection->command_type;
|
||||
// subunit_type | subunit ID
|
||||
command[pos++] = (connection->subunit_type << 3) | connection->subunit_id;
|
||||
// opcode
|
||||
command[pos++] = (uint8_t)connection->cmd_to_send;
|
||||
// operands
|
||||
memcpy(command+pos, connection->cmd_operands, connection->cmd_operands_lenght);
|
||||
pos += connection->cmd_operands_lenght;
|
||||
|
||||
return l2cap_send(cid, command, pos);
|
||||
}
|
||||
|
||||
|
||||
static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){
|
||||
if (connection->state != AVCTP_W2_RECEIVE_RESPONSE) return;
|
||||
connection->state = AVCTP_CONNECTION_OPENED;
|
||||
switch (connection->state){
|
||||
case AVCTP_W2_RECEIVE_PRESS_RESPONSE:
|
||||
connection->state = AVCTP_W2_SEND_RELEASE_COMMAND;
|
||||
break;
|
||||
case AVCTP_W2_RECEIVE_RESPONSE:
|
||||
connection->state = AVCTP_CONNECTION_OPENED;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
avrcp_command_type_t ctype;
|
||||
avrcp_subunit_type_t subunit_type;
|
||||
avrcp_subunit_type_t subunit_id;
|
||||
@ -332,37 +404,28 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
|
||||
}
|
||||
case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT:
|
||||
break;
|
||||
case AVRCP_CMD_OPCODE_PASS_THROUGH:
|
||||
if (connection->state == AVCTP_W2_SEND_RELEASE_COMMAND){
|
||||
request_pass_through_release_control_cmd(connection);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int avrcp_send_cmd(uint16_t cid, avrcp_connection_t * connection){
|
||||
uint8_t command[20];
|
||||
int pos = 0;
|
||||
// transport header
|
||||
// Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
|
||||
command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
|
||||
// Profile IDentifier (PID)
|
||||
command[pos++] = AV_REMOTE_CONTROL >> 8;
|
||||
command[pos++] = AV_REMOTE_CONTROL & 0x00FF;
|
||||
|
||||
// command_type
|
||||
command[pos++] = connection->command_type;
|
||||
// subunit_type | subunit ID
|
||||
command[pos++] = (connection->subunit_type << 3) | connection->subunit_id;
|
||||
// opcode
|
||||
command[pos++] = (uint8_t)connection->cmd_to_send;
|
||||
// operands
|
||||
memcpy(command+pos, connection->cmd_operands, connection->cmd_operands_lenght);
|
||||
pos += connection->cmd_operands_lenght;
|
||||
return l2cap_send(cid, command, sizeof(command));
|
||||
}
|
||||
|
||||
static void avrcp_handle_can_send_now(avrcp_connection_t * connection){
|
||||
if (connection->state != AVCTP_W2_SEND_COMMAND) return;
|
||||
connection->state = AVCTP_W2_RECEIVE_RESPONSE;
|
||||
switch (connection->state){
|
||||
case AVCTP_W2_SEND_PRESS_COMMAND:
|
||||
connection->state = AVCTP_W2_RECEIVE_PRESS_RESPONSE;
|
||||
break;
|
||||
case AVCTP_W2_SEND_COMMAND:
|
||||
case AVCTP_W2_SEND_RELEASE_COMMAND:
|
||||
connection->state = AVCTP_W2_RECEIVE_RESPONSE;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
avrcp_send_cmd(connection->l2cap_signaling_cid, connection);
|
||||
}
|
||||
|
||||
@ -483,12 +546,6 @@ void avrcp_connect(bd_addr_t bd_addr){
|
||||
l2cap_create_channel(packet_handler, connection->remote_addr, PSM_AVCTP, 0xffff, NULL);
|
||||
}
|
||||
|
||||
static void avrcp_request_can_send_now(avrcp_connection_t * connection, uint16_t l2cap_cid){
|
||||
connection->wait_to_send = 1;
|
||||
l2cap_request_can_send_now_event(l2cap_cid);
|
||||
}
|
||||
|
||||
|
||||
void avrcp_unit_info(uint16_t con_handle){
|
||||
avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle);
|
||||
if (!connection){
|
||||
@ -498,13 +555,13 @@ void avrcp_unit_info(uint16_t con_handle){
|
||||
if (connection->state != AVCTP_CONNECTION_OPENED) return;
|
||||
connection->state = AVCTP_W2_SEND_COMMAND;
|
||||
|
||||
connection->cmd_to_send = AVRCP_CMD_OPCODE_UNIT_INFO;
|
||||
connection->cmd_operands_lenght = 5;
|
||||
connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT;
|
||||
connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE;
|
||||
connection->command_type = AVRCP_CTYPE_STATUS;
|
||||
connection->transaction_label++;
|
||||
connection->cmd_to_send = AVRCP_CMD_OPCODE_UNIT_INFO;
|
||||
connection->command_type = AVRCP_CTYPE_STATUS;
|
||||
connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique
|
||||
connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE;
|
||||
memset(connection->cmd_operands, 0xFF, connection->cmd_operands_lenght);
|
||||
connection->cmd_operands_lenght = 5;
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
}
|
||||
|
||||
@ -517,11 +574,11 @@ void avrcp_get_capabilities(uint16_t con_handle){
|
||||
if (connection->state != AVCTP_CONNECTION_OPENED) return;
|
||||
connection->state = AVCTP_W2_SEND_COMMAND;
|
||||
|
||||
connection->transaction_label++;
|
||||
connection->cmd_to_send = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
|
||||
connection->command_type = AVRCP_CTYPE_STATUS;
|
||||
connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
|
||||
connection->subunit_id = 0;
|
||||
connection->transaction_label++;
|
||||
big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID);
|
||||
connection->cmd_operands[3] = 0x10;
|
||||
connection->cmd_operands[4] = 0;
|
||||
@ -529,4 +586,33 @@ void avrcp_get_capabilities(uint16_t con_handle){
|
||||
connection->cmd_operands[7] = 0x02;
|
||||
connection->cmd_operands_lenght = 8;
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void avrcp_play(uint16_t con_handle){
|
||||
request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_PLAY, 0x38);
|
||||
}
|
||||
|
||||
void avrcp_stop(uint16_t con_handle){
|
||||
request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_STOP, 0);
|
||||
}
|
||||
|
||||
void avrcp_pause(uint16_t con_handle){
|
||||
request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_PAUSE, 0);
|
||||
}
|
||||
|
||||
void avrcp_rewind(uint16_t con_handle){
|
||||
request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_REWIND, 0);
|
||||
}
|
||||
|
||||
void avrcp_fast_forward(uint16_t con_handle){
|
||||
request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_FAST_FORWARD, 0x3F);
|
||||
}
|
||||
|
||||
void avrcp_forward(uint16_t con_handle){
|
||||
request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_FORWARD, 0x75);
|
||||
}
|
||||
|
||||
void avrcp_backward(uint16_t con_handle){
|
||||
request_pass_through_press_control_cmd(con_handle, AVRCP_OPERATION_ID_BACKWARD, 0x65);
|
||||
}
|
||||
|
@ -65,13 +65,16 @@ typedef enum {
|
||||
AVRCP_CTYPE_RESPONSE_NOT_IMPLEMENTED = 8,
|
||||
AVRCP_CTYPE_RESPONSE_ACCEPTED,
|
||||
AVRCP_CTYPE_RESPONSE_REJECTED,
|
||||
AVRCP_CTYPE_RESPONSE_IN_TRANSITION,
|
||||
AVRCP_CTYPE_RESPONSE_IN_TRANSITION, // target state is in transition. A subsequent STATUS command, may result in the return of a STABLE status
|
||||
AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE,
|
||||
AVRCP_CTYPE_RESPONSE_CHANGED_STABLE,
|
||||
AVRCP_CTYPE_RESPONSE_RESERVED,
|
||||
AVRCP_CTYPE_RESPONSE_INTERIM
|
||||
AVRCP_CTYPE_RESPONSE_INTERIM // target is unable to respond with either ACCEPTED or REJECTED within 100 millisecond
|
||||
} avrcp_command_type_t;
|
||||
|
||||
// control command response: accepted, rejected, interim
|
||||
// status command response: not implemented, rejected, in transiiton, stable
|
||||
// notify command response: not implemented, rejected, changed
|
||||
typedef enum {
|
||||
AVRCP_SUBUNIT_TYPE_MONITOR = 0,
|
||||
AVRCP_SUBUNIT_TYPE_AUDIO = 1,
|
||||
@ -87,7 +90,8 @@ typedef enum {
|
||||
AVRCP_SUBUNIT_TYPE_CAMERA_STORAGE,
|
||||
AVRCP_SUBUNIT_TYPE_VENDOR_UNIQUE = 0x1C,
|
||||
AVRCP_SUBUNIT_TYPE_RESERVED_FOR_ALL_SUBUNIT_TYPES,
|
||||
AVRCP_SUBUNIT_TYPE_EXTENDED_TO_NEXT_BYTE,
|
||||
AVRCP_SUBUNIT_TYPE_EXTENDED_TO_NEXT_BYTE, // The unit_type field may take value 1E16, which means that the field is extended to the following byte. In that case, an additional byte for extended_unit_type will be added immediately following operand[1].
|
||||
// Further extension is possible when the value of extended_unit_type is FF16, in which case another byte will be added.
|
||||
AVRCP_SUBUNIT_TYPE_UNIT = 0x1F
|
||||
} avrcp_subunit_type_t;
|
||||
|
||||
@ -96,20 +100,34 @@ typedef enum {
|
||||
AVRCP_SUBUNIT_ID_IGNORE = 7
|
||||
} avrcp_subunit_id_t;
|
||||
|
||||
typedef enum{
|
||||
typedef enum {
|
||||
AVRCP_CMD_OPCODE_VENDOR_DEPENDENT = 0x00,
|
||||
AVRCP_CMD_OPCODE_RESERVE = 0x01,
|
||||
AVRCP_CMD_OPCODE_UNIT_INFO = 0x30,
|
||||
AVRCP_CMD_OPCODE_SUBUNIT_INFO = 0x31,
|
||||
AVRCP_CMD_OPCODE_PASS_THROUGH = 0x7C,
|
||||
AVRCP_CMD_OPCODE_VERSION = 0xB0,
|
||||
AVRCP_CMD_OPCODE_POWER = 0xB2
|
||||
} avrcp_command_opcode_t;
|
||||
|
||||
typedef enum {
|
||||
AVRCP_OPERATION_ID_PLAY = 0x44,
|
||||
AVRCP_OPERATION_ID_STOP = 0x45,
|
||||
AVRCP_OPERATION_ID_PAUSE = 0x46,
|
||||
AVRCP_OPERATION_ID_REWIND = 0x48,
|
||||
AVRCP_OPERATION_ID_FAST_FORWARD = 0x49,
|
||||
AVRCP_OPERATION_ID_FORWARD = 0x4B,
|
||||
AVRCP_OPERATION_ID_BACKWARD = 0x4C
|
||||
} avrcp_operation_id_t;
|
||||
|
||||
typedef enum {
|
||||
AVCTP_CONNECTION_IDLE,
|
||||
AVCTP_CONNECTION_W4_L2CAP_CONNECTED,
|
||||
AVCTP_CONNECTION_OPENED,
|
||||
AVCTP_W2_SEND_PRESS_COMMAND,
|
||||
AVCTP_W2_SEND_RELEASE_COMMAND,
|
||||
AVCTP_W2_SEND_COMMAND,
|
||||
AVCTP_W2_RECEIVE_PRESS_RESPONSE,
|
||||
AVCTP_W2_RECEIVE_RESPONSE,
|
||||
AVCTP_CONNECTION_W4_L2CAP_DISCONNECTED
|
||||
} avctp_connection_state_t;
|
||||
@ -180,11 +198,71 @@ void avrcp_connect(bd_addr_t bd_addr);
|
||||
void avrcp_unit_info(uint16_t con_handle);
|
||||
|
||||
/**
|
||||
* @brief Unit info.
|
||||
* @brief Get capabilities.
|
||||
* @param con_handle
|
||||
*/
|
||||
void avrcp_get_capabilities(uint16_t con_handle);
|
||||
|
||||
/**
|
||||
* @brief Play.
|
||||
* @param con_handle
|
||||
*/
|
||||
void avrcp_play(uint16_t con_handle);
|
||||
|
||||
/**
|
||||
* @brief Stop.
|
||||
* @param con_handle
|
||||
*/
|
||||
void avrcp_stop(uint16_t con_handle);
|
||||
|
||||
/**
|
||||
* @brief Pause.
|
||||
* @param con_handle
|
||||
*/
|
||||
void avrcp_pause(uint16_t con_handle);
|
||||
|
||||
/**
|
||||
* @brief Fast forward.
|
||||
* @param con_handle
|
||||
*/
|
||||
void avrcp_fast_forward(uint16_t con_handle);
|
||||
|
||||
/**
|
||||
* @brief Rewind.
|
||||
* @param con_handle
|
||||
*/
|
||||
void avrcp_rewind(uint16_t con_handle);
|
||||
|
||||
/**
|
||||
* @brief Forward.
|
||||
* @param con_handle
|
||||
*/
|
||||
void avrcp_forward(uint16_t con_handle);
|
||||
|
||||
/**
|
||||
* @brief Backward.
|
||||
* @param con_handle
|
||||
*/
|
||||
void avrcp_backward(uint16_t con_handle);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Register notification.
|
||||
* - EVENT_PLAYBACK_STATUS_CHANGED
|
||||
* - EVENT_TRACK_CHANGED
|
||||
* - EVENT_NOW_PLAYING_CONTENT_CHANGED
|
||||
* - EVENT_AVAILABLE_PLAYERS_CHANGED
|
||||
* - EVENT_ADDRESSED_PLAYER_CHANGED
|
||||
* - EVENT_VOLUME_CHANGED
|
||||
* @param con_handle
|
||||
*/
|
||||
// void avrcp_register_notification(uint16_t con_handle);
|
||||
|
||||
/**
|
||||
* @brief Get play status.
|
||||
* @param con_handle
|
||||
*/
|
||||
// void avrcp_get_play_status(uint16_t con_handle);
|
||||
|
||||
/* API_END */
|
||||
#if defined __cplusplus
|
||||
|
@ -114,8 +114,14 @@ static void show_usage(void){
|
||||
printf("c - create connection to addr %s\n", bd_addr_to_str(remote));
|
||||
printf("C - disconnect\n");
|
||||
printf("u - get unit info\n");
|
||||
printf("p - get capabilities\n");
|
||||
|
||||
printf("t - get capabilities\n");
|
||||
printf("l - avrcp_play\n");
|
||||
printf("s - avrcp_stop\n");
|
||||
printf("p - avrcp_pause\n");
|
||||
printf("w - avrcp_fast_forward\n");
|
||||
printf("r - avrcp_rewind\n");
|
||||
printf("f - avrcp_forward\n");
|
||||
printf("b - avrcp_backward\n");
|
||||
printf("Ctrl-c - exit\n");
|
||||
printf("---\n");
|
||||
}
|
||||
@ -125,20 +131,39 @@ static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callbac
|
||||
UNUSED(callback_type);
|
||||
|
||||
int cmd = btstack_stdin_read();
|
||||
|
||||
printf("- execute command %c\n", cmd);
|
||||
switch (cmd){
|
||||
case 'c':
|
||||
printf("c - create connection to addr %s\n", bd_addr_to_str(remote));
|
||||
avrcp_connect(remote);
|
||||
break;
|
||||
case 'u':
|
||||
printf("u - get unit info\n");
|
||||
avrcp_unit_info(con_handle);
|
||||
break;
|
||||
case 'p':
|
||||
printf("p - get capabilities\n");
|
||||
case 't':
|
||||
avrcp_get_capabilities(con_handle);
|
||||
break;
|
||||
case 'l':
|
||||
avrcp_play(con_handle);
|
||||
break;
|
||||
case 's':
|
||||
avrcp_stop(con_handle);
|
||||
break;
|
||||
case 'p':
|
||||
avrcp_pause(con_handle);
|
||||
break;
|
||||
case 'w':
|
||||
avrcp_fast_forward(con_handle);
|
||||
break;
|
||||
case 'r':
|
||||
avrcp_rewind(con_handle);
|
||||
break;
|
||||
case 'f':
|
||||
avrcp_forward(con_handle);
|
||||
break;
|
||||
case 'b':
|
||||
avrcp_backward(con_handle);
|
||||
break;
|
||||
|
||||
default:
|
||||
show_usage();
|
||||
break;
|
||||
|
Loading…
x
Reference in New Issue
Block a user