avrcp: register/deregister notifications

This commit is contained in:
Milanka Ringwald 2017-02-08 17:11:51 +01:00 committed by Matthias Ringwald
parent c6906b0bac
commit c0be161c3a
3 changed files with 135 additions and 26 deletions

View File

@ -145,7 +145,41 @@ static const char * event2str(uint16_t index){
return avrcp_event_name[0];
}
static const char * avrcp_operation_name[] = {
"NOT SUPPORTED", // 0x3B
"SKIP", "NOT SUPPORTED", "NOT SUPPORTED", "NOT SUPPORTED", "NOT SUPPORTED",
"VOLUME_UP", "VOLUME_DOWN", "MUTE", "PLAY", "STOP", "PAUSE", "NOT SUPPORTED",
"REWIND", "FAST_FORWARD", "NOT SUPPORTED", "FORWARD", "BACKWARD" // 0x4C
};
static const char * operation2str(uint8_t index){
if (index >= 0x3B && index <= 0x4C) return avrcp_operation_name[index - 0x3B];
return avrcp_operation_name[0];
}
static const char * avrcp_ctype_name[] = {
"CONTROL",
"STATUS",
"SPECIFIC_INQUIRY",
"NOTIFY",
"GENERAL_INQUIRY",
"RESERVED5",
"RESERVED6",
"RESERVED7",
"NOT_IMPLEMENTED",
"ACCEPTED",
"REJECTED",
"IN_TRANSITION",
"IMPLEMENTED_STABLE",
"CHANGED_STABLE",
"RESERVED",
"INTERIM"
};
static const char * ctype2str(uint8_t index){
return avrcp_ctype_name[index];
}
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void avrcp_register_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id, uint32_t playback_interval_in_seconds);
static 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){
uint8_t* attribute;
@ -446,9 +480,10 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
connection->state = AVCTP_CONNECTION_OPENED;
break;
default:
return;
// check for notifications? move state transition down
break;
}
avrcp_command_type_t ctype;
avrcp_subunit_type_t subunit_type;
avrcp_subunit_type_t subunit_id;
@ -465,9 +500,10 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
uint16_t pid = (byte_value << 8) | packet[2];
pos = 3;
log_info(" Transport header 0x%02x (transaction_label %d, packet_type %d, frame_type %d, ipid %d), pid 0x%4x",
printf(" Transport header 0x%02x (transaction_label %d, packet_type %d, frame_type %d, ipid %d), pid 0x%4x\n",
transport_header, transaction_label, packet_type, frame_type, ipid, pid);
// printf_hexdump(packet+pos, size-pos);
switch (connection->cmd_to_send){
case AVRCP_CMD_OPCODE_UNIT_INFO:{
ctype = packet[pos++];
@ -509,7 +545,7 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
// printf(" VENDOR DEPENDENT response: ctype 0x%02x (0C), subunit_type 0x%02x (1F), subunit_id 0x%02x (07), opcode 0x%02x (30), unit_type 0x%02x, unit %d, company_id 0x%06x\n",
// ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id );
printf(" VENDOR DEPENDENT response: pdu id 0x%02x, param_length %d\n", pdu_id, param_length);
printf(" VENDOR DEPENDENT response: pdu id 0x%02x, param_length %d, status %s\n", pdu_id, param_length, ctype2str(ctype));
switch (pdu_id){
case AVRCP_PDU_ID_GET_CAPABILITIES:{
printf_hexdump(packet+pos,size-pos);
@ -553,6 +589,32 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
}
case AVRCP_PDU_ID_REGISTER_NOTIFICATION:{
uint8_t event_id = packet[pos++];
switch (ctype){
case AVRCP_CTYPE_RESPONSE_INTERIM:
connection->notifications_to_register &= (0 << event_id);
if (connection->notifications_to_deregister & (1 << event_id)){
connection->notifications_enabled &= (0 << event_id);
connection->notifications_to_deregister &= (0 << event_id);
} else {
connection->notifications_enabled |= (1 << event_id);
}
break;
case AVRCP_CTYPE_RESPONSE_CHANGED_STABLE:
if (connection->notifications_to_deregister & (1 << event_id)){
connection->notifications_enabled &= (0 << event_id);
connection->notifications_to_deregister &= (0 << event_id);
} else {
connection->notifications_enabled &= (0 << event_id);
avrcp_register_notification(connection, event_id, 2);
}
break;
default:
connection->notifications_to_register &= (0 << event_id);
connection->notifications_enabled &= (0 << event_id);
connection->notifications_to_deregister &= (0 << event_id);
break;
}
uint8_t status;
printf(" REGISTER_NOTIFICATION: ");
switch (event_id){
@ -595,12 +657,27 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
break;
}
break;
case AVRCP_CMD_OPCODE_PASS_THROUGH:
if (connection->state == AVCTP_W2_SEND_RELEASE_COMMAND){
request_pass_through_release_control_cmd(connection);
break;
case AVRCP_CMD_OPCODE_PASS_THROUGH:{
// 0x80 | connection->cmd_operands[0]
ctype = packet[pos++];
byte_value = packet[pos++];
subunit_type = byte_value >> 3;
subunit_id = byte_value & 0x07;
opcode = packet[pos++];
uint8_t operation_id = packet[pos++];
if (connection->state == AVCTP_CONNECTION_OPENED) {
// RELEASE response
operation_id = operation_id & 0x7F;
}
if (connection->state == AVCTP_W2_SEND_RELEASE_COMMAND){
// PRESS response
request_pass_through_release_control_cmd(connection);
}
printf("Operation %s, status %s\n", operation2str(operation_id), ctype2str(ctype));
break;
}
default:
break;
}
@ -616,6 +693,7 @@ static void avrcp_handle_can_send_now(avrcp_connection_t * connection){
connection->state = AVCTP_W2_RECEIVE_RESPONSE;
break;
default:
return;
}
avrcp_send_cmd(connection->l2cap_signaling_cid, connection);
@ -875,13 +953,15 @@ void avrcp_get_play_status(uint16_t con_handle){
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
}
void avrcp_register_notification(uint16_t con_handle, avrcp_notification_event_id_t event_id, uint32_t playback_interval_in_seconds){
avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle);
if (!connection){
log_error("avrcp_get_play_status: coud not find a connection.");
return;
}
static void avrcp_register_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id, uint32_t playback_interval_in_seconds){
if (connection->state != AVCTP_CONNECTION_OPENED) return;
if (connection->notifications_to_deregister & (1 << event_id)) return;
if (connection->notifications_enabled & (1 << event_id)) return;
if (connection->notifications_to_register & (1 << event_id)) return;
printf("avrcp_register_notification \n");
connection->notifications_to_register |= (1 << event_id);
connection->state = AVCTP_W2_SEND_COMMAND;
connection->transaction_label++;
@ -905,6 +985,26 @@ void avrcp_register_notification(uint16_t con_handle, avrcp_notification_event_i
// answer page 61
}
void avrcp_enable_notification(uint16_t con_handle, avrcp_notification_event_id_t event_id, uint32_t playback_interval_in_seconds){
avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle);
if (!connection){
log_error("avrcp_get_play_status: coud not find a connection.");
return;
}
avrcp_register_notification(connection, event_id, playback_interval_in_seconds);
}
void avrcp_disable_notification(uint16_t con_handle, avrcp_notification_event_id_t event_id){
avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle);
if (!connection){
log_error("avrcp_get_play_status: coud not find a connection.");
return;
}
if (connection->state != AVCTP_CONNECTION_OPENED) return;
if (connection->notifications_to_deregister & (1 << event_id)) return;
connection->notifications_to_deregister |= (1 << event_id);
}
void avrcp_get_now_playing_info(uint16_t con_handle){
avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle);
if (!connection){

View File

@ -195,6 +195,10 @@ typedef struct {
uint8_t cmd_operands[20];
uint8_t cmd_operands_lenght;
btstack_timer_source_t press_and_hold_cmd_timer;
uint16_t notifications_enabled;
uint16_t notifications_to_register;
uint16_t notifications_to_deregister;
} avrcp_connection_t;
typedef enum {
@ -317,7 +321,8 @@ void avrcp_get_play_status(uint16_t con_handle);
* @param con_handle
* @param event_id
*/
void avrcp_register_notification(uint16_t con_handle, avrcp_notification_event_id_t event_id, uint32_t playback_interval_in_seconds);
void avrcp_enable_notification(uint16_t con_handle, avrcp_notification_event_id_t event_id, uint32_t playback_interval_in_seconds);
void avrcp_disable_notification(uint16_t con_handle, avrcp_notification_event_id_t event_id);
/**
* @brief Get info on now playing media.

View File

@ -120,16 +120,17 @@ static void show_usage(void){
printf("C - disconnect\n");
printf("i - get unit info\n");
printf("e - 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_start_rewind\n");
printf("R - avrcp_stop_rewind\n");
printf("f - avrcp_forward\n");
printf("b - avrcp_backward\n");
printf("l - play\n");
printf("s - stop\n");
printf("p - pause\n");
printf("w - fast_forward\n");
printf("r - start_rewind\n");
printf("R - stop_rewind\n");
printf("f - forward\n");
printf("b - backward\n");
printf("S - get play status\n");
printf("N - register notification, AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED\n");
printf("n - enable notification, AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED\n");
printf("N - disable notification, AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED\n");
printf("I - get now playing info\n");
printf("u - volume up\n");
printf("d - volume down\n");
@ -186,8 +187,11 @@ static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callbac
case 'S':
avrcp_get_play_status(con_handle);
break;
case 'n':
avrcp_enable_notification(con_handle, AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED, 2);
break;
case 'N':
avrcp_register_notification(con_handle, AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED, 2);
avrcp_disable_notification(con_handle, AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED);
break;
case 'I':
avrcp_get_now_playing_info(con_handle);