avrcp: pass through basic controll cmds, not all working

This commit is contained in:
Milanka Ringwald 2017-02-01 13:03:08 +01:00 committed by Matthias Ringwald
parent 49bbb6f27b
commit d144ca3efe
3 changed files with 241 additions and 52 deletions

View File

@ -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);
}

View File

@ -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

View File

@ -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;