101dc6e35SMilanka Ringwald /* 201dc6e35SMilanka Ringwald * Copyright (C) 2016 BlueKitchen GmbH 301dc6e35SMilanka Ringwald * 401dc6e35SMilanka Ringwald * Redistribution and use in source and binary forms, with or without 501dc6e35SMilanka Ringwald * modification, are permitted provided that the following conditions 601dc6e35SMilanka Ringwald * are met: 701dc6e35SMilanka Ringwald * 801dc6e35SMilanka Ringwald * 1. Redistributions of source code must retain the above copyright 901dc6e35SMilanka Ringwald * notice, this list of conditions and the following disclaimer. 1001dc6e35SMilanka Ringwald * 2. Redistributions in binary form must reproduce the above copyright 1101dc6e35SMilanka Ringwald * notice, this list of conditions and the following disclaimer in the 1201dc6e35SMilanka Ringwald * documentation and/or other materials provided with the distribution. 1301dc6e35SMilanka Ringwald * 3. Neither the name of the copyright holders nor the names of 1401dc6e35SMilanka Ringwald * contributors may be used to endorse or promote products derived 1501dc6e35SMilanka Ringwald * from this software without specific prior written permission. 1601dc6e35SMilanka Ringwald * 4. Any redistribution, use, or modification is done solely for 1701dc6e35SMilanka Ringwald * personal benefit and not for any commercial purpose or for 1801dc6e35SMilanka Ringwald * monetary gain. 1901dc6e35SMilanka Ringwald * 2001dc6e35SMilanka Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 2101dc6e35SMilanka Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2201dc6e35SMilanka Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2301dc6e35SMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 2401dc6e35SMilanka Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2501dc6e35SMilanka Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2601dc6e35SMilanka Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 2701dc6e35SMilanka Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2801dc6e35SMilanka Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2901dc6e35SMilanka Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 3001dc6e35SMilanka Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3101dc6e35SMilanka Ringwald * SUCH DAMAGE. 3201dc6e35SMilanka Ringwald * 3301dc6e35SMilanka Ringwald * Please inquire about commercial licensing options at 3401dc6e35SMilanka Ringwald * [email protected] 3501dc6e35SMilanka Ringwald * 3601dc6e35SMilanka Ringwald */ 3701dc6e35SMilanka Ringwald 3801dc6e35SMilanka Ringwald #define __BTSTACK_FILE__ "avrcp_target.c" 3901dc6e35SMilanka Ringwald 4001dc6e35SMilanka Ringwald #include <stdint.h> 4101dc6e35SMilanka Ringwald #include <stdio.h> 4201dc6e35SMilanka Ringwald #include <stdlib.h> 4301dc6e35SMilanka Ringwald #include <string.h> 44adaba9f3SMatthias Ringwald #include <inttypes.h> 4501dc6e35SMilanka Ringwald 4601dc6e35SMilanka Ringwald #include "btstack.h" 4701dc6e35SMilanka Ringwald #include "classic/avrcp.h" 4801dc6e35SMilanka Ringwald 4901dc6e35SMilanka Ringwald static avrcp_context_t avrcp_target_context; 5001dc6e35SMilanka Ringwald 5101dc6e35SMilanka Ringwald void avrcp_target_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint8_t browsing, uint16_t supported_features, const char * service_name, const char * service_provider_name){ 5201dc6e35SMilanka Ringwald avrcp_create_sdp_record(0, service, service_record_handle, browsing, supported_features, service_name, service_provider_name); 5301dc6e35SMilanka Ringwald } 5401dc6e35SMilanka Ringwald 55*831d3fd5SMilanka Ringwald static void avrcp_target_emit_operation(btstack_packet_handler_t callback, uint16_t avrcp_cid, avrcp_operation_id_t operation_id, uint8_t operands_length, uint8_t operand){ 56*831d3fd5SMilanka Ringwald if (!callback) return; 57*831d3fd5SMilanka Ringwald uint8_t event[8]; 58*831d3fd5SMilanka Ringwald int pos = 0; 59*831d3fd5SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 60*831d3fd5SMilanka Ringwald event[pos++] = sizeof(event) - 2; 61*831d3fd5SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_OPERATION; 62*831d3fd5SMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 63*831d3fd5SMilanka Ringwald pos += 2; 64*831d3fd5SMilanka Ringwald event[pos++] = operation_id; 65*831d3fd5SMilanka Ringwald event[pos++] = operands_length; 66*831d3fd5SMilanka Ringwald event[pos++] = operand; 67*831d3fd5SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 68*831d3fd5SMilanka Ringwald } 69*831d3fd5SMilanka Ringwald 704b338011SMilanka Ringwald static void avrcp_target_emit_respond_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t subeventID){ 714b338011SMilanka Ringwald if (!callback) return; 724b338011SMilanka Ringwald uint8_t event[5]; 734b338011SMilanka Ringwald int pos = 0; 744b338011SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 754b338011SMilanka Ringwald event[pos++] = sizeof(event) - 2; 764b338011SMilanka Ringwald event[pos++] = subeventID; 774b338011SMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 784b338011SMilanka Ringwald pos += 2; 794b338011SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 804b338011SMilanka Ringwald } 814b338011SMilanka Ringwald 82e0bbf3edSMilanka Ringwald static void avrcp_target_emit_respond_subunit_info_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t offset){ 834b338011SMilanka Ringwald if (!callback) return; 844b338011SMilanka Ringwald uint8_t event[6]; 854b338011SMilanka Ringwald int pos = 0; 864b338011SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 874b338011SMilanka Ringwald event[pos++] = sizeof(event) - 2; 884b338011SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_SUBUNIT_INFO_QUERY; 894b338011SMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 904b338011SMilanka Ringwald pos += 2; 91e0bbf3edSMilanka Ringwald event[pos++] = offset; 92e0bbf3edSMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 93e0bbf3edSMilanka Ringwald } 94e0bbf3edSMilanka Ringwald 95c045af99SMilanka Ringwald static void avrcp_target_emit_respond_vendor_dependent_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t subevent_id){ 96e0bbf3edSMilanka Ringwald if (!callback) return; 97c045af99SMilanka Ringwald uint8_t event[5]; 98e0bbf3edSMilanka Ringwald int pos = 0; 99e0bbf3edSMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 100e0bbf3edSMilanka Ringwald event[pos++] = sizeof(event) - 2; 101e0bbf3edSMilanka Ringwald event[pos++] = subevent_id; 102e0bbf3edSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 1034b338011SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 1044b338011SMilanka Ringwald } 1054b338011SMilanka Ringwald 1065e3d4d2bSMilanka Ringwald static uint16_t avrcp_target_pack_single_element_attribute_number(uint8_t * packet, uint16_t size, uint16_t pos, avrcp_media_attribute_id_t attr_id, uint32_t value){ 1075e3d4d2bSMilanka Ringwald UNUSED(size); 1085e3d4d2bSMilanka Ringwald if ((attr_id < 1) || (attr_id > AVRCP_MEDIA_ATTR_COUNT)) return 0; 109adaba9f3SMatthias Ringwald uint16_t attr_value_length = sprintf((char *)(packet+pos+8), "%0" PRIu32, value); 1105e3d4d2bSMilanka Ringwald big_endian_store_32(packet, pos, attr_id); 1115e3d4d2bSMilanka Ringwald pos += 4; 1125e3d4d2bSMilanka Ringwald big_endian_store_16(packet, pos, UTF8); 1135e3d4d2bSMilanka Ringwald pos += 2; 1145e3d4d2bSMilanka Ringwald big_endian_store_16(packet, pos, attr_value_length); 1155e3d4d2bSMilanka Ringwald pos += 2; 1165e3d4d2bSMilanka Ringwald return 8 + attr_value_length; 1175e3d4d2bSMilanka Ringwald } 1185e3d4d2bSMilanka Ringwald 1195e3d4d2bSMilanka Ringwald static uint16_t avrcp_target_pack_single_element_attribute_string(uint8_t * packet, uint16_t size, uint16_t pos, rfc2978_charset_mib_enumid_t mib_enumid, avrcp_media_attribute_id_t attr_id, uint8_t * attr_value, uint16_t attr_value_length){ 1205e3d4d2bSMilanka Ringwald UNUSED(size); 1215e3d4d2bSMilanka Ringwald if (attr_value_length == 0) return 0; 1225e3d4d2bSMilanka Ringwald if ((attr_id < 1) || (attr_id > AVRCP_MEDIA_ATTR_COUNT)) return 0; 1235e3d4d2bSMilanka Ringwald big_endian_store_32(packet, pos, attr_id); 1245e3d4d2bSMilanka Ringwald pos += 4; 1255e3d4d2bSMilanka Ringwald big_endian_store_16(packet, pos, mib_enumid); 1265e3d4d2bSMilanka Ringwald pos += 2; 1275e3d4d2bSMilanka Ringwald big_endian_store_16(packet, pos, attr_value_length); 1285e3d4d2bSMilanka Ringwald pos += 2; 1295e3d4d2bSMilanka Ringwald memcpy(packet+pos, attr_value, attr_value_length); 1305e3d4d2bSMilanka Ringwald pos += attr_value_length; 1315e3d4d2bSMilanka Ringwald return 8 + attr_value_length; 1325e3d4d2bSMilanka Ringwald } 1335e3d4d2bSMilanka Ringwald 1345e3d4d2bSMilanka Ringwald static int avrcp_target_send_now_playing_info(uint16_t cid, avrcp_connection_t * connection){ 1355e3d4d2bSMilanka Ringwald uint16_t pos = 0; 1365e3d4d2bSMilanka Ringwald l2cap_reserve_packet_buffer(); 1375e3d4d2bSMilanka Ringwald uint8_t * packet = l2cap_get_outgoing_buffer(); 1385e3d4d2bSMilanka Ringwald uint16_t size = l2cap_get_remote_mtu_for_local_cid(connection->l2cap_signaling_cid); 1395e3d4d2bSMilanka Ringwald 1405e3d4d2bSMilanka Ringwald packet[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0; 1414b338011SMilanka Ringwald // Profile IDentifier (PID) 1425e3d4d2bSMilanka Ringwald packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 1435e3d4d2bSMilanka Ringwald packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 1444b338011SMilanka Ringwald 1454b338011SMilanka Ringwald // command_type 1465e3d4d2bSMilanka Ringwald packet[pos++] = connection->command_type; 1474b338011SMilanka Ringwald // subunit_type | subunit ID 1485e3d4d2bSMilanka Ringwald packet[pos++] = (connection->subunit_type << 3) | connection->subunit_id; 1494b338011SMilanka Ringwald // opcode 1505e3d4d2bSMilanka Ringwald packet[pos++] = (uint8_t)connection->command_opcode; 1515e3d4d2bSMilanka Ringwald 1525e3d4d2bSMilanka Ringwald // company id is 3 bytes long 1535e3d4d2bSMilanka Ringwald big_endian_store_24(packet, pos, BT_SIG_COMPANY_ID); 1545e3d4d2bSMilanka Ringwald pos += 3; 1555e3d4d2bSMilanka Ringwald 1565e3d4d2bSMilanka Ringwald packet[pos++] = AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES; 1575e3d4d2bSMilanka Ringwald packet[pos++] = 0; 1585e3d4d2bSMilanka Ringwald 1595e3d4d2bSMilanka Ringwald // num_attrs 1605e3d4d2bSMilanka Ringwald int i; 1615e3d4d2bSMilanka Ringwald 1625e3d4d2bSMilanka Ringwald uint16_t playing_info_buffer_len = 1; 1635e3d4d2bSMilanka Ringwald uint16_t playing_info_buffer_len_position = pos; 1645e3d4d2bSMilanka Ringwald 1655e3d4d2bSMilanka Ringwald uint8_t media_attr_count = 0; 1665e3d4d2bSMilanka Ringwald uint16_t media_attr_count_position = pos + 2; 1675e3d4d2bSMilanka Ringwald pos += 3; 1685e3d4d2bSMilanka Ringwald 1695e3d4d2bSMilanka Ringwald for (i = 0; i < AVRCP_MEDIA_ATTR_COUNT; i++){ 1705e3d4d2bSMilanka Ringwald int attr_id = i+1; 1715e3d4d2bSMilanka Ringwald int attr_len; 1725e3d4d2bSMilanka Ringwald switch (attr_id){ 1735e3d4d2bSMilanka Ringwald case AVRCP_MEDIA_ATTR_TRACK: 1745e3d4d2bSMilanka Ringwald attr_len = avrcp_target_pack_single_element_attribute_number(packet, size, pos, attr_id, connection->track_nr); 1755e3d4d2bSMilanka Ringwald break; 1765e3d4d2bSMilanka Ringwald case AVRCP_MEDIA_ATTR_TOTAL_TRACKS: 1775e3d4d2bSMilanka Ringwald attr_len = avrcp_target_pack_single_element_attribute_number(packet, size, pos, attr_id, connection->total_tracks); 1785e3d4d2bSMilanka Ringwald break; 1795e3d4d2bSMilanka Ringwald case AVRCP_MEDIA_ATTR_SONG_LENGTH: 1805e3d4d2bSMilanka Ringwald attr_len = avrcp_target_pack_single_element_attribute_number(packet, size, pos, attr_id, connection->song_length_ms); 1815e3d4d2bSMilanka Ringwald break; 1825e3d4d2bSMilanka Ringwald default: 1835e3d4d2bSMilanka Ringwald attr_len = avrcp_target_pack_single_element_attribute_string(packet, size, pos, UTF8, attr_id, connection->now_playing_info[i].value, connection->now_playing_info[i].len); 1845e3d4d2bSMilanka Ringwald break; 1855e3d4d2bSMilanka Ringwald } 1865e3d4d2bSMilanka Ringwald if (attr_len > 0) { 1875e3d4d2bSMilanka Ringwald pos += attr_len; 1885e3d4d2bSMilanka Ringwald playing_info_buffer_len += attr_len; 1895e3d4d2bSMilanka Ringwald media_attr_count++; 1905e3d4d2bSMilanka Ringwald } 1915e3d4d2bSMilanka Ringwald } 1925e3d4d2bSMilanka Ringwald big_endian_store_16(packet, playing_info_buffer_len_position, playing_info_buffer_len); 1935e3d4d2bSMilanka Ringwald packet[media_attr_count_position] = media_attr_count; 1945e3d4d2bSMilanka Ringwald 1955e3d4d2bSMilanka Ringwald // TODO fragmentation 1965e3d4d2bSMilanka Ringwald if (playing_info_buffer_len + pos > l2cap_get_remote_mtu_for_local_cid(connection->l2cap_signaling_cid)) { 1975e3d4d2bSMilanka Ringwald return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 1985e3d4d2bSMilanka Ringwald } 1995e3d4d2bSMilanka Ringwald 2005e3d4d2bSMilanka Ringwald connection->wait_to_send = 0; 2015e3d4d2bSMilanka Ringwald return l2cap_send_prepared(cid, pos); 2025e3d4d2bSMilanka Ringwald } 2035e3d4d2bSMilanka Ringwald 2045e3d4d2bSMilanka Ringwald static int avrcp_target_send_response(uint16_t cid, avrcp_connection_t * connection){ 2055e3d4d2bSMilanka Ringwald int pos = 0; 2065e3d4d2bSMilanka Ringwald l2cap_reserve_packet_buffer(); 2075e3d4d2bSMilanka Ringwald uint8_t * packet = l2cap_get_outgoing_buffer(); 2085e3d4d2bSMilanka Ringwald 2095e3d4d2bSMilanka Ringwald // transport header 2105e3d4d2bSMilanka Ringwald // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 2115e3d4d2bSMilanka Ringwald packet[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0; 2125e3d4d2bSMilanka Ringwald // Profile IDentifier (PID) 2135e3d4d2bSMilanka Ringwald packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 2145e3d4d2bSMilanka Ringwald packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 2155e3d4d2bSMilanka Ringwald 2165e3d4d2bSMilanka Ringwald // command_type 2175e3d4d2bSMilanka Ringwald packet[pos++] = connection->command_type; 2185e3d4d2bSMilanka Ringwald // subunit_type | subunit ID 2195e3d4d2bSMilanka Ringwald packet[pos++] = (connection->subunit_type << 3) | connection->subunit_id; 2205e3d4d2bSMilanka Ringwald // opcode 2215e3d4d2bSMilanka Ringwald packet[pos++] = (uint8_t)connection->command_opcode; 2224b338011SMilanka Ringwald // operands 2235e3d4d2bSMilanka Ringwald memcpy(packet+pos, connection->cmd_operands, connection->cmd_operands_length); 224*831d3fd5SMilanka Ringwald // printf_hexdump(packet+pos, connection->cmd_operands_length); 225*831d3fd5SMilanka Ringwald 2264b338011SMilanka Ringwald pos += connection->cmd_operands_length; 227d1386928SMilanka Ringwald connection->wait_to_send = 0; 2285e3d4d2bSMilanka Ringwald return l2cap_send_prepared(cid, pos); 2294b338011SMilanka Ringwald } 2304b338011SMilanka Ringwald 231e0bbf3edSMilanka Ringwald 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){ 232e0bbf3edSMilanka Ringwald // AVRCP_CTYPE_RESPONSE_REJECTED 233e0bbf3edSMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_REJECTED; 234e0bbf3edSMilanka Ringwald connection->subunit_type = subunit_type; 235e0bbf3edSMilanka Ringwald connection->subunit_id = subunit_id; 236e0bbf3edSMilanka Ringwald connection->command_opcode = opcode; 237e0bbf3edSMilanka Ringwald 238e0bbf3edSMilanka Ringwald // company id is 3 bytes long 239e0bbf3edSMilanka Ringwald int pos = connection->cmd_operands_length; 240e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = pdu_id; 241e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = 0; 242e0bbf3edSMilanka Ringwald // param length 243c045af99SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 1); 244e0bbf3edSMilanka Ringwald pos += 2; 245e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = status; 246e0bbf3edSMilanka Ringwald connection->cmd_operands_length = pos; 247d1386928SMilanka Ringwald 248d1386928SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 249d1386928SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 250d1386928SMilanka Ringwald return ERROR_CODE_SUCCESS; 251e0bbf3edSMilanka Ringwald } 252e0bbf3edSMilanka Ringwald 253*831d3fd5SMilanka Ringwald static uint8_t avrcp_target_pass_through_response(uint16_t avrcp_cid, avrcp_command_type_t cmd_type, avrcp_operation_id_t opid, uint8_t operands_length, uint8_t operand){ 254*831d3fd5SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 255*831d3fd5SMilanka Ringwald if (!connection){ 256*831d3fd5SMilanka Ringwald log_error("avrcp_target_operation_reject: could not find a connection."); 257*831d3fd5SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 258*831d3fd5SMilanka Ringwald } 259*831d3fd5SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 260*831d3fd5SMilanka Ringwald printf("avrcp_target_pass_through_response: operation 0x%02x, operands length %d\n", opid, operands_length); 261*831d3fd5SMilanka Ringwald 262*831d3fd5SMilanka Ringwald connection->command_type = cmd_type; 263*831d3fd5SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 264*831d3fd5SMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 265*831d3fd5SMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_PASS_THROUGH; 266*831d3fd5SMilanka Ringwald 267*831d3fd5SMilanka Ringwald int pos = 0; 268*831d3fd5SMilanka Ringwald connection->cmd_operands[pos++] = opid; 269*831d3fd5SMilanka Ringwald connection->cmd_operands[pos++] = operands_length; 270*831d3fd5SMilanka Ringwald if (operands_length == 1){ 271*831d3fd5SMilanka Ringwald connection->cmd_operands[pos++] = operand; 272*831d3fd5SMilanka Ringwald } 273*831d3fd5SMilanka Ringwald connection->cmd_operands_length = pos; 274*831d3fd5SMilanka Ringwald 275*831d3fd5SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 276*831d3fd5SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 277*831d3fd5SMilanka Ringwald return ERROR_CODE_SUCCESS; 278*831d3fd5SMilanka Ringwald } 279*831d3fd5SMilanka Ringwald 280*831d3fd5SMilanka Ringwald uint8_t avrcp_target_operation_rejected(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint8_t operands_length, uint8_t operand){ 281*831d3fd5SMilanka Ringwald return avrcp_target_pass_through_response(avrcp_cid, AVRCP_CTYPE_RESPONSE_REJECTED, opid, operands_length, operand); 282*831d3fd5SMilanka Ringwald } 283*831d3fd5SMilanka Ringwald 284*831d3fd5SMilanka Ringwald uint8_t avrcp_target_operation_accepted(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint8_t operands_length, uint8_t operand){ 285*831d3fd5SMilanka Ringwald return avrcp_target_pass_through_response(avrcp_cid, AVRCP_CTYPE_RESPONSE_ACCEPTED, opid, operands_length, operand); 286*831d3fd5SMilanka Ringwald } 287*831d3fd5SMilanka Ringwald 288*831d3fd5SMilanka Ringwald uint8_t avrcp_target_operation_not_implemented(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint8_t operands_length, uint8_t operand){ 289*831d3fd5SMilanka Ringwald return avrcp_target_pass_through_response(avrcp_cid, AVRCP_CTYPE_RESPONSE_ACCEPTED, opid, operands_length, operand); 290*831d3fd5SMilanka Ringwald } 291*831d3fd5SMilanka Ringwald 2924b338011SMilanka Ringwald uint8_t avrcp_target_unit_info(uint16_t avrcp_cid, avrcp_subunit_type_t unit_type, uint32_t company_id){ 2934b338011SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 2944b338011SMilanka Ringwald if (!connection){ 2954b338011SMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 2964b338011SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 2974b338011SMilanka Ringwald } 2984b338011SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 2994b338011SMilanka Ringwald 3004b338011SMilanka Ringwald uint8_t unit = 0; 3014b338011SMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE; 3024b338011SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique 3034b338011SMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE; 304e0bbf3edSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_UNIT_INFO; 3054b338011SMilanka Ringwald 3064b338011SMilanka Ringwald connection->cmd_operands_length = 5; 3074b338011SMilanka Ringwald connection->cmd_operands[0] = 0x07; 3084b338011SMilanka Ringwald connection->cmd_operands[1] = (unit_type << 4) | unit; 3094b338011SMilanka Ringwald // company id is 3 bytes long 310e0bbf3edSMilanka Ringwald big_endian_store_32(connection->cmd_operands, 2, company_id); 311d1386928SMilanka Ringwald 312d1386928SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 313d1386928SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 314d1386928SMilanka Ringwald return ERROR_CODE_SUCCESS; 3154b338011SMilanka Ringwald } 3164b338011SMilanka Ringwald 3174b338011SMilanka Ringwald uint8_t avrcp_target_subunit_info(uint16_t avrcp_cid, avrcp_subunit_type_t subunit_type, uint8_t offset, uint8_t * subunit_info_data){ 3184b338011SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 3194b338011SMilanka Ringwald if (!connection){ 3204b338011SMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 3214b338011SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3224b338011SMilanka Ringwald } 3234b338011SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 3244b338011SMilanka Ringwald 3254b338011SMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_SUBUNIT_INFO; 3264b338011SMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE; 3274b338011SMilanka Ringwald connection->subunit_type = subunit_type; //vendor unique 3284b338011SMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE; 3294b338011SMilanka Ringwald 3304b338011SMilanka Ringwald uint8_t page = offset / 4; 3314b338011SMilanka Ringwald uint8_t extension_code = 7; 3324b338011SMilanka Ringwald connection->cmd_operands_length = 5; 3334b338011SMilanka Ringwald connection->cmd_operands[0] = (page << 4) | extension_code; 3344b338011SMilanka Ringwald memcpy(connection->cmd_operands+1, subunit_info_data + offset, 4); 335d1386928SMilanka Ringwald 336d1386928SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 337d1386928SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 338d1386928SMilanka Ringwald return ERROR_CODE_SUCCESS; 3394b338011SMilanka Ringwald } 3404b338011SMilanka Ringwald 3415e3d4d2bSMilanka Ringwald static inline uint8_t avrcp_prepare_vendor_dependent_response(uint16_t avrcp_cid, avrcp_connection_t ** out_connection, avrcp_pdu_id_t pdu_id, uint16_t param_length){ 3425e3d4d2bSMilanka Ringwald *out_connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 3435e3d4d2bSMilanka Ringwald if (!*out_connection){ 3445e3d4d2bSMilanka Ringwald log_error("avrcp tartget: could not find a connection."); 3455e3d4d2bSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3465e3d4d2bSMilanka Ringwald } 3475e3d4d2bSMilanka Ringwald 3485e3d4d2bSMilanka Ringwald if ((*out_connection)->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 3495e3d4d2bSMilanka Ringwald (*out_connection)->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 3505e3d4d2bSMilanka Ringwald (*out_connection)->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE; 3515e3d4d2bSMilanka Ringwald (*out_connection)->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 3525e3d4d2bSMilanka Ringwald (*out_connection)->subunit_id = AVRCP_SUBUNIT_ID; 3535e3d4d2bSMilanka Ringwald 3545e3d4d2bSMilanka Ringwald (*out_connection)->cmd_operands[(*out_connection)->cmd_operands_length++] = pdu_id; 3555e3d4d2bSMilanka Ringwald // reserved 3565e3d4d2bSMilanka Ringwald (*out_connection)->cmd_operands[(*out_connection)->cmd_operands_length++] = 0; 3575e3d4d2bSMilanka Ringwald // param length 3585e3d4d2bSMilanka Ringwald big_endian_store_16((*out_connection)->cmd_operands, (*out_connection)->cmd_operands_length, param_length); 3595e3d4d2bSMilanka Ringwald (*out_connection)->cmd_operands_length += 2; 3605e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 3615e3d4d2bSMilanka Ringwald } 3625e3d4d2bSMilanka Ringwald 363e0bbf3edSMilanka Ringwald static uint8_t avrcp_target_capability(uint16_t avrcp_cid, avrcp_capability_id_t capability_id, uint8_t capabilities_num, uint8_t * capabilities, uint8_t size){ 3645e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = NULL; 3655e3d4d2bSMilanka Ringwald uint8_t status = avrcp_prepare_vendor_dependent_response(avrcp_cid, &connection, AVRCP_PDU_ID_GET_CAPABILITIES, 2+size); 3665e3d4d2bSMilanka Ringwald if (status != ERROR_CODE_SUCCESS) return status; 367e0bbf3edSMilanka Ringwald 3685e3d4d2bSMilanka Ringwald connection->cmd_operands[connection->cmd_operands_length++] = capability_id; 3695e3d4d2bSMilanka Ringwald connection->cmd_operands[connection->cmd_operands_length++] = capabilities_num; 3705e3d4d2bSMilanka Ringwald memcpy(connection->cmd_operands+connection->cmd_operands_length, capabilities, size); 3715e3d4d2bSMilanka Ringwald connection->cmd_operands_length += size; 372e0bbf3edSMilanka Ringwald 373d1386928SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 374d1386928SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 375d1386928SMilanka Ringwald return ERROR_CODE_SUCCESS; 376e0bbf3edSMilanka Ringwald } 377e0bbf3edSMilanka Ringwald 378e0bbf3edSMilanka Ringwald uint8_t avrcp_target_supported_events(uint16_t avrcp_cid, uint8_t capabilities_num, uint8_t * capabilities, uint8_t size){ 379e0bbf3edSMilanka Ringwald return avrcp_target_capability(avrcp_cid, AVRCP_CAPABILITY_ID_EVENT, capabilities_num, capabilities, size); 380e0bbf3edSMilanka Ringwald } 381e0bbf3edSMilanka Ringwald 3825e3d4d2bSMilanka Ringwald uint8_t avrcp_target_supported_companies(uint16_t avrcp_cid, uint8_t capabilities_num, uint8_t * capabilities, uint8_t size ){ 3835e3d4d2bSMilanka Ringwald return avrcp_target_capability(avrcp_cid, AVRCP_CAPABILITY_ID_COMPANY, capabilities_num, capabilities, size); 3845e3d4d2bSMilanka Ringwald } 3855e3d4d2bSMilanka Ringwald 3865e3d4d2bSMilanka Ringwald uint8_t avrcp_target_play_status(uint16_t avrcp_cid, uint32_t song_length_ms, uint32_t song_position_ms, avrcp_play_status_t play_status){ 3875e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = NULL; 3885e3d4d2bSMilanka Ringwald uint8_t status = avrcp_prepare_vendor_dependent_response(avrcp_cid, &connection, AVRCP_PDU_ID_GET_PLAY_STATUS, 11); 3895e3d4d2bSMilanka Ringwald if (status != ERROR_CODE_SUCCESS) return status; 3905e3d4d2bSMilanka Ringwald 3915e3d4d2bSMilanka Ringwald big_endian_store_32(connection->cmd_operands, connection->cmd_operands_length, song_length_ms); 3925e3d4d2bSMilanka Ringwald connection->cmd_operands_length += 4; 3935e3d4d2bSMilanka Ringwald big_endian_store_32(connection->cmd_operands, connection->cmd_operands_length, song_position_ms); 3945e3d4d2bSMilanka Ringwald connection->cmd_operands_length += 4; 3955e3d4d2bSMilanka Ringwald connection->cmd_operands[connection->cmd_operands_length++] = play_status; 3965e3d4d2bSMilanka Ringwald 3975e3d4d2bSMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 3985e3d4d2bSMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 3995e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 4005e3d4d2bSMilanka Ringwald } 4015e3d4d2bSMilanka Ringwald 4025e3d4d2bSMilanka Ringwald uint8_t avrcp_target_now_playing_info(uint16_t avrcp_cid){ 4035e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 4045e3d4d2bSMilanka Ringwald if (!connection){ 4055e3d4d2bSMilanka Ringwald log_error("avrcp tartget: could not find a connection."); 4065e3d4d2bSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 4075e3d4d2bSMilanka Ringwald } 4085e3d4d2bSMilanka Ringwald 4095e3d4d2bSMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 4105e3d4d2bSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 4115e3d4d2bSMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE; 4125e3d4d2bSMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 4135e3d4d2bSMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 4145e3d4d2bSMilanka Ringwald 4155e3d4d2bSMilanka Ringwald connection->now_playing_info_response = 1; 4165e3d4d2bSMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 4175e3d4d2bSMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 4185e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 4195e3d4d2bSMilanka Ringwald } 4205e3d4d2bSMilanka Ringwald 4215e3d4d2bSMilanka Ringwald static uint8_t avrcp_target_store_media_attr(uint16_t avrcp_cid, avrcp_media_attribute_id_t attr_id, const char * value){ 4225e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 4235e3d4d2bSMilanka Ringwald if (!connection){ 4245e3d4d2bSMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 4255e3d4d2bSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 4265e3d4d2bSMilanka Ringwald } 4275e3d4d2bSMilanka Ringwald 4285e3d4d2bSMilanka Ringwald int index = attr_id - 1; 4295e3d4d2bSMilanka Ringwald connection->now_playing_info[index].value = (uint8_t*)value; 4305e3d4d2bSMilanka Ringwald connection->now_playing_info[index].len = strlen(value); 4315e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 4325e3d4d2bSMilanka Ringwald } 4335e3d4d2bSMilanka Ringwald 4345e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_title(uint16_t avrcp_cid, const char * title){ 4355e3d4d2bSMilanka Ringwald return avrcp_target_store_media_attr(avrcp_cid, AVRCP_MEDIA_ATTR_TITLE, title); 4365e3d4d2bSMilanka Ringwald } 4375e3d4d2bSMilanka Ringwald 4385e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_artist(uint16_t avrcp_cid, const char * artist){ 4395e3d4d2bSMilanka Ringwald return avrcp_target_store_media_attr(avrcp_cid, AVRCP_MEDIA_ATTR_ARTIST, artist); 4405e3d4d2bSMilanka Ringwald } 4415e3d4d2bSMilanka Ringwald 4425e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_album(uint16_t avrcp_cid, const char * album){ 4435e3d4d2bSMilanka Ringwald return avrcp_target_store_media_attr(avrcp_cid, AVRCP_MEDIA_ATTR_ALBUM, album); 4445e3d4d2bSMilanka Ringwald } 4455e3d4d2bSMilanka Ringwald 4465e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_genre(uint16_t avrcp_cid, const char * genre){ 4475e3d4d2bSMilanka Ringwald return avrcp_target_store_media_attr(avrcp_cid, AVRCP_MEDIA_ATTR_GENRE, genre); 4485e3d4d2bSMilanka Ringwald } 4495e3d4d2bSMilanka Ringwald 4505e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_song_length_ms(uint16_t avrcp_cid, const uint32_t song_length_ms){ 4515e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 4525e3d4d2bSMilanka Ringwald if (!connection){ 4535e3d4d2bSMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 4545e3d4d2bSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 4555e3d4d2bSMilanka Ringwald } 4565e3d4d2bSMilanka Ringwald connection->song_length_ms = song_length_ms; 4575e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 4585e3d4d2bSMilanka Ringwald } 4595e3d4d2bSMilanka Ringwald 4605e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_total_tracks(uint16_t avrcp_cid, const int total_tracks){ 4615e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 4625e3d4d2bSMilanka Ringwald if (!connection){ 4635e3d4d2bSMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 4645e3d4d2bSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 4655e3d4d2bSMilanka Ringwald } 4665e3d4d2bSMilanka Ringwald connection->total_tracks = total_tracks; 4675e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 4685e3d4d2bSMilanka Ringwald } 4695e3d4d2bSMilanka Ringwald 4705e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_track_nr(uint16_t avrcp_cid, const int track_nr){ 4715e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 4725e3d4d2bSMilanka Ringwald if (!connection){ 4735e3d4d2bSMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 4745e3d4d2bSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 4755e3d4d2bSMilanka Ringwald } 4765e3d4d2bSMilanka Ringwald connection->track_nr = track_nr; 4775e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 4785e3d4d2bSMilanka Ringwald } 479e0bbf3edSMilanka Ringwald 480e0bbf3edSMilanka Ringwald static uint8_t * avrcp_get_company_id(uint8_t *packet, uint16_t size){ 481e0bbf3edSMilanka Ringwald UNUSED(size); 482e0bbf3edSMilanka Ringwald return packet + 6; 483e0bbf3edSMilanka Ringwald } 484e0bbf3edSMilanka Ringwald 485e0bbf3edSMilanka Ringwald static uint8_t * avrcp_get_pdu(uint8_t *packet, uint16_t size){ 486e0bbf3edSMilanka Ringwald UNUSED(size); 487e0bbf3edSMilanka Ringwald return packet + 9; 488e0bbf3edSMilanka Ringwald } 489e0bbf3edSMilanka Ringwald 49001dc6e35SMilanka Ringwald static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){ 49101dc6e35SMilanka Ringwald UNUSED(connection); 49201dc6e35SMilanka Ringwald UNUSED(packet); 49301dc6e35SMilanka Ringwald UNUSED(size); 4944b338011SMilanka Ringwald 4954b338011SMilanka Ringwald // uint8_t opcode; 496e0bbf3edSMilanka Ringwald 4974b338011SMilanka Ringwald uint8_t transport_header = packet[0]; 4984b338011SMilanka Ringwald connection->transaction_label = transport_header >> 4; 4994b338011SMilanka Ringwald // uint8_t packet_type = (transport_header & 0x0F) >> 2; 5004b338011SMilanka Ringwald // uint8_t frame_type = (transport_header & 0x03) >> 1; 5014b338011SMilanka Ringwald // uint8_t ipid = transport_header & 0x01; 5024b338011SMilanka Ringwald // uint8_t byte_value = packet[2]; 5034b338011SMilanka Ringwald // uint16_t pid = (byte_value << 8) | packet[2]; 5044b338011SMilanka Ringwald 505e0bbf3edSMilanka Ringwald // avrcp_command_type_t ctype = (avrcp_command_type_t) packet[3]; 506e0bbf3edSMilanka Ringwald // uint8_t byte_value = packet[4]; 507e0bbf3edSMilanka Ringwald avrcp_subunit_type_t subunit_type = (avrcp_subunit_type_t) (packet[4] >> 3); 508e0bbf3edSMilanka Ringwald avrcp_subunit_id_t subunit_id = (avrcp_subunit_id_t) (packet[4] & 0x07); 5094b338011SMilanka Ringwald // opcode = packet[pos++]; 5104b338011SMilanka Ringwald 5114b338011SMilanka Ringwald // printf(" Transport header 0x%02x (transaction_label %d, packet_type %d, frame_type %d, ipid %d), pid 0x%4x\n", 5124b338011SMilanka Ringwald // transport_header, transaction_label, packet_type, frame_type, ipid, pid); 5134b338011SMilanka Ringwald // printf_hexdump(packet+pos, size-pos); 5144b338011SMilanka Ringwald 515e0bbf3edSMilanka Ringwald avrcp_command_opcode_t opcode = avrcp_cmd_opcode(packet,size); 516e0bbf3edSMilanka Ringwald uint8_t * company_id = avrcp_get_company_id(packet, size); 517e0bbf3edSMilanka Ringwald uint8_t * pdu = avrcp_get_pdu(packet, size); 5185e3d4d2bSMilanka Ringwald // uint16_t param_length = big_endian_read_16(pdu, 2); 519e0bbf3edSMilanka Ringwald 5205e3d4d2bSMilanka Ringwald int pos = 4; 521c045af99SMilanka Ringwald uint8_t pdu_id; 522e0bbf3edSMilanka Ringwald connection->cmd_operands_length = 0; 523e0bbf3edSMilanka Ringwald 524e0bbf3edSMilanka Ringwald switch (opcode){ 5254b338011SMilanka Ringwald case AVRCP_CMD_OPCODE_UNIT_INFO: 5264b338011SMilanka Ringwald avrcp_target_emit_respond_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_UNIT_INFO_QUERY); 5274b338011SMilanka Ringwald break; 528e0bbf3edSMilanka Ringwald case AVRCP_CMD_OPCODE_SUBUNIT_INFO:{ 5295e3d4d2bSMilanka Ringwald uint8_t offset = 4 * (packet[pos+2]>>4); 530e0bbf3edSMilanka Ringwald avrcp_target_emit_respond_subunit_info_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, offset); 531e0bbf3edSMilanka Ringwald break; 532e0bbf3edSMilanka Ringwald } 533*831d3fd5SMilanka Ringwald case AVRCP_CMD_OPCODE_PASS_THROUGH: 534*831d3fd5SMilanka Ringwald log_info("AVRCP_OPERATION_ID 0x%02x, operands length %d, operand %d", packet[6], packet[7], packet[8]); 535*831d3fd5SMilanka Ringwald avrcp_target_emit_operation(avrcp_target_context.avrcp_callback, connection->avrcp_cid, packet[6], packet[7], packet[8]); 536*831d3fd5SMilanka Ringwald break; 537e0bbf3edSMilanka Ringwald case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT: 538e0bbf3edSMilanka Ringwald pdu_id = pdu[0]; 539e0bbf3edSMilanka Ringwald // 1 - reserved 540e0bbf3edSMilanka Ringwald // 2-3 param length, 541c045af99SMilanka Ringwald memcpy(connection->cmd_operands, company_id, 3); 542c045af99SMilanka Ringwald connection->cmd_operands_length = 3; 543e0bbf3edSMilanka Ringwald switch (pdu_id){ 544e0bbf3edSMilanka Ringwald case AVRCP_PDU_ID_GET_CAPABILITIES:{ 5455e3d4d2bSMilanka Ringwald avrcp_capability_id_t capability_id = (avrcp_capability_id_t) pdu[pos]; 546e0bbf3edSMilanka Ringwald switch (capability_id){ 547e0bbf3edSMilanka Ringwald case AVRCP_CAPABILITY_ID_EVENT: 548c045af99SMilanka Ringwald avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_EVENT_IDS_QUERY); 549e0bbf3edSMilanka Ringwald break; 550e0bbf3edSMilanka Ringwald case AVRCP_CAPABILITY_ID_COMPANY: 551c045af99SMilanka Ringwald avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_COMPANY_IDS_QUERY); 552e0bbf3edSMilanka Ringwald break; 553e0bbf3edSMilanka Ringwald default: 554e0bbf3edSMilanka Ringwald avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_PARAMETER); 555e0bbf3edSMilanka Ringwald break; 556e0bbf3edSMilanka Ringwald } 557e0bbf3edSMilanka Ringwald break; 558e0bbf3edSMilanka Ringwald } 559c045af99SMilanka Ringwald case AVRCP_PDU_ID_GET_PLAY_STATUS: 560c045af99SMilanka Ringwald avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_PLAY_STATUS_QUERY); 561c045af99SMilanka Ringwald break; 5625e3d4d2bSMilanka Ringwald case AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES:{ 5635e3d4d2bSMilanka Ringwald uint8_t play_identifier[8]; 5645e3d4d2bSMilanka Ringwald memset(play_identifier, 0, 8); 5655e3d4d2bSMilanka Ringwald if (memcmp(pdu+pos, play_identifier, 8) != 0) { 5665e3d4d2bSMilanka Ringwald avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_PARAMETER); 5675e3d4d2bSMilanka Ringwald return; 5685e3d4d2bSMilanka Ringwald } 5695e3d4d2bSMilanka Ringwald avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_NOW_PLAYING_INFO_QUERY); 5705e3d4d2bSMilanka Ringwald break; 5715e3d4d2bSMilanka Ringwald } 572e0bbf3edSMilanka Ringwald default: 573c045af99SMilanka Ringwald printf("unhandled pdu id 0x%02x\n", pdu_id); 574c045af99SMilanka Ringwald avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_COMMAND); 575e0bbf3edSMilanka Ringwald break; 576e0bbf3edSMilanka Ringwald } 5774b338011SMilanka Ringwald break; 5784b338011SMilanka Ringwald default: 579*831d3fd5SMilanka Ringwald printf("AVRCP target: opcode 0x%02x not implemented\n", avrcp_cmd_opcode(packet,size)); 5804b338011SMilanka Ringwald break; 5814b338011SMilanka Ringwald } 58201dc6e35SMilanka Ringwald } 58301dc6e35SMilanka Ringwald 58401dc6e35SMilanka Ringwald static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 58501dc6e35SMilanka Ringwald avrcp_connection_t * connection; 58601dc6e35SMilanka Ringwald switch (packet_type) { 58701dc6e35SMilanka Ringwald case L2CAP_DATA_PACKET: 58801dc6e35SMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, &avrcp_target_context); 58901dc6e35SMilanka Ringwald if (!connection) break; 59001dc6e35SMilanka Ringwald avrcp_handle_l2cap_data_packet_for_signaling_connection(connection, packet, size); 59101dc6e35SMilanka Ringwald break; 59201dc6e35SMilanka Ringwald case HCI_EVENT_PACKET: 59301dc6e35SMilanka Ringwald switch (hci_event_packet_get_type(packet)){ 59401dc6e35SMilanka Ringwald case L2CAP_EVENT_CAN_SEND_NOW: 59501dc6e35SMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, &avrcp_target_context); 59601dc6e35SMilanka Ringwald if (!connection) break; 597d1386928SMilanka Ringwald switch (connection->state){ 598d1386928SMilanka Ringwald case AVCTP_W2_SEND_RESPONSE: 599d1386928SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 6005e3d4d2bSMilanka Ringwald if (connection->now_playing_info_response){ 6015e3d4d2bSMilanka Ringwald printf("now_playing_info_response \n"); 6025e3d4d2bSMilanka Ringwald connection->now_playing_info_response = 0; 6035e3d4d2bSMilanka Ringwald avrcp_target_send_now_playing_info(connection->l2cap_signaling_cid, connection); 6045e3d4d2bSMilanka Ringwald break; 6055e3d4d2bSMilanka Ringwald } 6065e3d4d2bSMilanka Ringwald avrcp_target_send_response(connection->l2cap_signaling_cid, connection); 607d1386928SMilanka Ringwald break; 608d1386928SMilanka Ringwald default: 609d1386928SMilanka Ringwald return; 610d1386928SMilanka Ringwald } 61101dc6e35SMilanka Ringwald break; 61201dc6e35SMilanka Ringwald default: 61301dc6e35SMilanka Ringwald avrcp_packet_handler(packet_type, channel, packet, size, &avrcp_target_context); 61401dc6e35SMilanka Ringwald break; 61501dc6e35SMilanka Ringwald } 61601dc6e35SMilanka Ringwald default: 61701dc6e35SMilanka Ringwald break; 61801dc6e35SMilanka Ringwald } 61901dc6e35SMilanka Ringwald } 62001dc6e35SMilanka Ringwald 62101dc6e35SMilanka Ringwald void avrcp_target_init(void){ 62201dc6e35SMilanka Ringwald avrcp_target_context.role = AVRCP_TARGET; 62301dc6e35SMilanka Ringwald avrcp_target_context.connections = NULL; 62401dc6e35SMilanka Ringwald avrcp_target_context.packet_handler = avrcp_controller_packet_handler; 62501dc6e35SMilanka Ringwald l2cap_register_service(&avrcp_controller_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_0); 62601dc6e35SMilanka Ringwald } 62701dc6e35SMilanka Ringwald 62801dc6e35SMilanka Ringwald void avrcp_target_register_packet_handler(btstack_packet_handler_t callback){ 62901dc6e35SMilanka Ringwald if (callback == NULL){ 63001dc6e35SMilanka Ringwald log_error("avrcp_register_packet_handler called with NULL callback"); 63101dc6e35SMilanka Ringwald return; 63201dc6e35SMilanka Ringwald } 63301dc6e35SMilanka Ringwald avrcp_target_context.avrcp_callback = callback; 63401dc6e35SMilanka Ringwald } 63501dc6e35SMilanka Ringwald 63601dc6e35SMilanka Ringwald uint8_t avrcp_target_connect(bd_addr_t bd_addr, uint16_t * avrcp_cid){ 63701dc6e35SMilanka Ringwald return avrcp_connect(bd_addr, &avrcp_target_context, avrcp_cid); 63801dc6e35SMilanka Ringwald } 63901dc6e35SMilanka Ringwald 64001dc6e35SMilanka Ringwald uint8_t avrcp_target_disconnect(uint16_t avrcp_cid){ 64101dc6e35SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 64201dc6e35SMilanka Ringwald if (!connection){ 64301dc6e35SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 64401dc6e35SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 64501dc6e35SMilanka Ringwald } 64601dc6e35SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 64701dc6e35SMilanka Ringwald l2cap_disconnect(connection->l2cap_signaling_cid, 0); 64801dc6e35SMilanka Ringwald return ERROR_CODE_SUCCESS; 64901dc6e35SMilanka Ringwald } 65001dc6e35SMilanka Ringwald 651