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> 44*adaba9f3SMatthias 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 554b338011SMilanka Ringwald static void avrcp_target_emit_respond_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t subeventID){ 564b338011SMilanka Ringwald if (!callback) return; 574b338011SMilanka Ringwald uint8_t event[5]; 584b338011SMilanka Ringwald int pos = 0; 594b338011SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 604b338011SMilanka Ringwald event[pos++] = sizeof(event) - 2; 614b338011SMilanka Ringwald event[pos++] = subeventID; 624b338011SMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 634b338011SMilanka Ringwald pos += 2; 644b338011SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 654b338011SMilanka Ringwald } 664b338011SMilanka Ringwald 67e0bbf3edSMilanka Ringwald static void avrcp_target_emit_respond_subunit_info_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t offset){ 684b338011SMilanka Ringwald if (!callback) return; 694b338011SMilanka Ringwald uint8_t event[6]; 704b338011SMilanka Ringwald int pos = 0; 714b338011SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 724b338011SMilanka Ringwald event[pos++] = sizeof(event) - 2; 734b338011SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_SUBUNIT_INFO_QUERY; 744b338011SMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 754b338011SMilanka Ringwald pos += 2; 76e0bbf3edSMilanka Ringwald event[pos++] = offset; 77e0bbf3edSMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 78e0bbf3edSMilanka Ringwald } 79e0bbf3edSMilanka Ringwald 80c045af99SMilanka Ringwald static void avrcp_target_emit_respond_vendor_dependent_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t subevent_id){ 81e0bbf3edSMilanka Ringwald if (!callback) return; 82c045af99SMilanka Ringwald uint8_t event[5]; 83e0bbf3edSMilanka Ringwald int pos = 0; 84e0bbf3edSMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 85e0bbf3edSMilanka Ringwald event[pos++] = sizeof(event) - 2; 86e0bbf3edSMilanka Ringwald event[pos++] = subevent_id; 87e0bbf3edSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 884b338011SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 894b338011SMilanka Ringwald } 904b338011SMilanka Ringwald 915e3d4d2bSMilanka 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){ 925e3d4d2bSMilanka Ringwald UNUSED(size); 935e3d4d2bSMilanka Ringwald if ((attr_id < 1) || (attr_id > AVRCP_MEDIA_ATTR_COUNT)) return 0; 94*adaba9f3SMatthias Ringwald uint16_t attr_value_length = sprintf((char *)(packet+pos+8), "%0" PRIu32, value); 955e3d4d2bSMilanka Ringwald big_endian_store_32(packet, pos, attr_id); 965e3d4d2bSMilanka Ringwald pos += 4; 975e3d4d2bSMilanka Ringwald big_endian_store_16(packet, pos, UTF8); 985e3d4d2bSMilanka Ringwald pos += 2; 995e3d4d2bSMilanka Ringwald big_endian_store_16(packet, pos, attr_value_length); 1005e3d4d2bSMilanka Ringwald pos += 2; 1015e3d4d2bSMilanka Ringwald return 8 + attr_value_length; 1025e3d4d2bSMilanka Ringwald } 1035e3d4d2bSMilanka Ringwald 1045e3d4d2bSMilanka 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){ 1055e3d4d2bSMilanka Ringwald UNUSED(size); 1065e3d4d2bSMilanka Ringwald if (attr_value_length == 0) return 0; 1075e3d4d2bSMilanka Ringwald if ((attr_id < 1) || (attr_id > AVRCP_MEDIA_ATTR_COUNT)) return 0; 1085e3d4d2bSMilanka Ringwald big_endian_store_32(packet, pos, attr_id); 1095e3d4d2bSMilanka Ringwald pos += 4; 1105e3d4d2bSMilanka Ringwald big_endian_store_16(packet, pos, mib_enumid); 1115e3d4d2bSMilanka Ringwald pos += 2; 1125e3d4d2bSMilanka Ringwald big_endian_store_16(packet, pos, attr_value_length); 1135e3d4d2bSMilanka Ringwald pos += 2; 1145e3d4d2bSMilanka Ringwald memcpy(packet+pos, attr_value, attr_value_length); 1155e3d4d2bSMilanka Ringwald pos += attr_value_length; 1165e3d4d2bSMilanka Ringwald return 8 + attr_value_length; 1175e3d4d2bSMilanka Ringwald } 1185e3d4d2bSMilanka Ringwald 1195e3d4d2bSMilanka Ringwald static int avrcp_target_send_now_playing_info(uint16_t cid, avrcp_connection_t * connection){ 1205e3d4d2bSMilanka Ringwald uint16_t pos = 0; 1215e3d4d2bSMilanka Ringwald l2cap_reserve_packet_buffer(); 1225e3d4d2bSMilanka Ringwald uint8_t * packet = l2cap_get_outgoing_buffer(); 1235e3d4d2bSMilanka Ringwald uint16_t size = l2cap_get_remote_mtu_for_local_cid(connection->l2cap_signaling_cid); 1245e3d4d2bSMilanka Ringwald 1255e3d4d2bSMilanka Ringwald packet[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0; 1264b338011SMilanka Ringwald // Profile IDentifier (PID) 1275e3d4d2bSMilanka Ringwald packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 1285e3d4d2bSMilanka Ringwald packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 1294b338011SMilanka Ringwald 1304b338011SMilanka Ringwald // command_type 1315e3d4d2bSMilanka Ringwald packet[pos++] = connection->command_type; 1324b338011SMilanka Ringwald // subunit_type | subunit ID 1335e3d4d2bSMilanka Ringwald packet[pos++] = (connection->subunit_type << 3) | connection->subunit_id; 1344b338011SMilanka Ringwald // opcode 1355e3d4d2bSMilanka Ringwald packet[pos++] = (uint8_t)connection->command_opcode; 1365e3d4d2bSMilanka Ringwald 1375e3d4d2bSMilanka Ringwald // company id is 3 bytes long 1385e3d4d2bSMilanka Ringwald big_endian_store_24(packet, pos, BT_SIG_COMPANY_ID); 1395e3d4d2bSMilanka Ringwald pos += 3; 1405e3d4d2bSMilanka Ringwald 1415e3d4d2bSMilanka Ringwald packet[pos++] = AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES; 1425e3d4d2bSMilanka Ringwald packet[pos++] = 0; 1435e3d4d2bSMilanka Ringwald 1445e3d4d2bSMilanka Ringwald // num_attrs 1455e3d4d2bSMilanka Ringwald int i; 1465e3d4d2bSMilanka Ringwald 1475e3d4d2bSMilanka Ringwald uint16_t playing_info_buffer_len = 1; 1485e3d4d2bSMilanka Ringwald uint16_t playing_info_buffer_len_position = pos; 1495e3d4d2bSMilanka Ringwald 1505e3d4d2bSMilanka Ringwald uint8_t media_attr_count = 0; 1515e3d4d2bSMilanka Ringwald uint16_t media_attr_count_position = pos + 2; 1525e3d4d2bSMilanka Ringwald pos += 3; 1535e3d4d2bSMilanka Ringwald 1545e3d4d2bSMilanka Ringwald for (i = 0; i < AVRCP_MEDIA_ATTR_COUNT; i++){ 1555e3d4d2bSMilanka Ringwald int attr_id = i+1; 1565e3d4d2bSMilanka Ringwald int attr_len; 1575e3d4d2bSMilanka Ringwald switch (attr_id){ 1585e3d4d2bSMilanka Ringwald case AVRCP_MEDIA_ATTR_TRACK: 1595e3d4d2bSMilanka Ringwald attr_len = avrcp_target_pack_single_element_attribute_number(packet, size, pos, attr_id, connection->track_nr); 1605e3d4d2bSMilanka Ringwald break; 1615e3d4d2bSMilanka Ringwald case AVRCP_MEDIA_ATTR_TOTAL_TRACKS: 1625e3d4d2bSMilanka Ringwald attr_len = avrcp_target_pack_single_element_attribute_number(packet, size, pos, attr_id, connection->total_tracks); 1635e3d4d2bSMilanka Ringwald break; 1645e3d4d2bSMilanka Ringwald case AVRCP_MEDIA_ATTR_SONG_LENGTH: 1655e3d4d2bSMilanka Ringwald attr_len = avrcp_target_pack_single_element_attribute_number(packet, size, pos, attr_id, connection->song_length_ms); 1665e3d4d2bSMilanka Ringwald break; 1675e3d4d2bSMilanka Ringwald default: 1685e3d4d2bSMilanka 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); 1695e3d4d2bSMilanka Ringwald break; 1705e3d4d2bSMilanka Ringwald } 1715e3d4d2bSMilanka Ringwald if (attr_len > 0) { 1725e3d4d2bSMilanka Ringwald pos += attr_len; 1735e3d4d2bSMilanka Ringwald playing_info_buffer_len += attr_len; 1745e3d4d2bSMilanka Ringwald media_attr_count++; 1755e3d4d2bSMilanka Ringwald } 1765e3d4d2bSMilanka Ringwald } 1775e3d4d2bSMilanka Ringwald big_endian_store_16(packet, playing_info_buffer_len_position, playing_info_buffer_len); 1785e3d4d2bSMilanka Ringwald packet[media_attr_count_position] = media_attr_count; 1795e3d4d2bSMilanka Ringwald 1805e3d4d2bSMilanka Ringwald // TODO fragmentation 1815e3d4d2bSMilanka Ringwald if (playing_info_buffer_len + pos > l2cap_get_remote_mtu_for_local_cid(connection->l2cap_signaling_cid)) { 1825e3d4d2bSMilanka Ringwald return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 1835e3d4d2bSMilanka Ringwald } 1845e3d4d2bSMilanka Ringwald 1855e3d4d2bSMilanka Ringwald connection->wait_to_send = 0; 1865e3d4d2bSMilanka Ringwald return l2cap_send_prepared(cid, pos); 1875e3d4d2bSMilanka Ringwald } 1885e3d4d2bSMilanka Ringwald 1895e3d4d2bSMilanka Ringwald static int avrcp_target_send_response(uint16_t cid, avrcp_connection_t * connection){ 1905e3d4d2bSMilanka Ringwald int pos = 0; 1915e3d4d2bSMilanka Ringwald l2cap_reserve_packet_buffer(); 1925e3d4d2bSMilanka Ringwald uint8_t * packet = l2cap_get_outgoing_buffer(); 1935e3d4d2bSMilanka Ringwald 1945e3d4d2bSMilanka Ringwald // transport header 1955e3d4d2bSMilanka Ringwald // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 1965e3d4d2bSMilanka Ringwald packet[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0; 1975e3d4d2bSMilanka Ringwald // Profile IDentifier (PID) 1985e3d4d2bSMilanka Ringwald packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 1995e3d4d2bSMilanka Ringwald packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 2005e3d4d2bSMilanka Ringwald 2015e3d4d2bSMilanka Ringwald // command_type 2025e3d4d2bSMilanka Ringwald packet[pos++] = connection->command_type; 2035e3d4d2bSMilanka Ringwald // subunit_type | subunit ID 2045e3d4d2bSMilanka Ringwald packet[pos++] = (connection->subunit_type << 3) | connection->subunit_id; 2055e3d4d2bSMilanka Ringwald // opcode 2065e3d4d2bSMilanka Ringwald packet[pos++] = (uint8_t)connection->command_opcode; 2074b338011SMilanka Ringwald // operands 2085e3d4d2bSMilanka Ringwald memcpy(packet+pos, connection->cmd_operands, connection->cmd_operands_length); 2094b338011SMilanka Ringwald pos += connection->cmd_operands_length; 210d1386928SMilanka Ringwald connection->wait_to_send = 0; 2115e3d4d2bSMilanka Ringwald return l2cap_send_prepared(cid, pos); 2124b338011SMilanka Ringwald } 2134b338011SMilanka Ringwald 214e0bbf3edSMilanka 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){ 215e0bbf3edSMilanka Ringwald // AVRCP_CTYPE_RESPONSE_REJECTED 216e0bbf3edSMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_REJECTED; 217e0bbf3edSMilanka Ringwald connection->subunit_type = subunit_type; 218e0bbf3edSMilanka Ringwald connection->subunit_id = subunit_id; 219e0bbf3edSMilanka Ringwald connection->command_opcode = opcode; 220e0bbf3edSMilanka Ringwald 221e0bbf3edSMilanka Ringwald // company id is 3 bytes long 222e0bbf3edSMilanka Ringwald int pos = connection->cmd_operands_length; 223e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = pdu_id; 224e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = 0; 225e0bbf3edSMilanka Ringwald // param length 226c045af99SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 1); 227e0bbf3edSMilanka Ringwald pos += 2; 228e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = status; 229e0bbf3edSMilanka Ringwald connection->cmd_operands_length = pos; 230d1386928SMilanka Ringwald 231d1386928SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 232d1386928SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 233d1386928SMilanka Ringwald return ERROR_CODE_SUCCESS; 234e0bbf3edSMilanka Ringwald } 235e0bbf3edSMilanka Ringwald 2364b338011SMilanka Ringwald uint8_t avrcp_target_unit_info(uint16_t avrcp_cid, avrcp_subunit_type_t unit_type, uint32_t company_id){ 2374b338011SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 2384b338011SMilanka Ringwald if (!connection){ 2394b338011SMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 2404b338011SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 2414b338011SMilanka Ringwald } 2424b338011SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 2434b338011SMilanka Ringwald 2444b338011SMilanka Ringwald uint8_t unit = 0; 2454b338011SMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE; 2464b338011SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique 2474b338011SMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE; 248e0bbf3edSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_UNIT_INFO; 2494b338011SMilanka Ringwald 2504b338011SMilanka Ringwald connection->cmd_operands_length = 5; 2514b338011SMilanka Ringwald connection->cmd_operands[0] = 0x07; 2524b338011SMilanka Ringwald connection->cmd_operands[1] = (unit_type << 4) | unit; 2534b338011SMilanka Ringwald // company id is 3 bytes long 254e0bbf3edSMilanka Ringwald big_endian_store_32(connection->cmd_operands, 2, company_id); 255d1386928SMilanka Ringwald 256d1386928SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 257d1386928SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 258d1386928SMilanka Ringwald return ERROR_CODE_SUCCESS; 2594b338011SMilanka Ringwald } 2604b338011SMilanka Ringwald 2614b338011SMilanka 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){ 2624b338011SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 2634b338011SMilanka Ringwald if (!connection){ 2644b338011SMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 2654b338011SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 2664b338011SMilanka Ringwald } 2674b338011SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 2684b338011SMilanka Ringwald 2694b338011SMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_SUBUNIT_INFO; 2704b338011SMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE; 2714b338011SMilanka Ringwald connection->subunit_type = subunit_type; //vendor unique 2724b338011SMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE; 2734b338011SMilanka Ringwald 2744b338011SMilanka Ringwald uint8_t page = offset / 4; 2754b338011SMilanka Ringwald uint8_t extension_code = 7; 2764b338011SMilanka Ringwald connection->cmd_operands_length = 5; 2774b338011SMilanka Ringwald connection->cmd_operands[0] = (page << 4) | extension_code; 2784b338011SMilanka Ringwald memcpy(connection->cmd_operands+1, subunit_info_data + offset, 4); 279d1386928SMilanka Ringwald 280d1386928SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 281d1386928SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 282d1386928SMilanka Ringwald return ERROR_CODE_SUCCESS; 2834b338011SMilanka Ringwald } 2844b338011SMilanka Ringwald 2855e3d4d2bSMilanka 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){ 2865e3d4d2bSMilanka Ringwald *out_connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 2875e3d4d2bSMilanka Ringwald if (!*out_connection){ 2885e3d4d2bSMilanka Ringwald log_error("avrcp tartget: could not find a connection."); 2895e3d4d2bSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 2905e3d4d2bSMilanka Ringwald } 2915e3d4d2bSMilanka Ringwald 2925e3d4d2bSMilanka Ringwald if ((*out_connection)->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 2935e3d4d2bSMilanka Ringwald (*out_connection)->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 2945e3d4d2bSMilanka Ringwald (*out_connection)->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE; 2955e3d4d2bSMilanka Ringwald (*out_connection)->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 2965e3d4d2bSMilanka Ringwald (*out_connection)->subunit_id = AVRCP_SUBUNIT_ID; 2975e3d4d2bSMilanka Ringwald 2985e3d4d2bSMilanka Ringwald (*out_connection)->cmd_operands[(*out_connection)->cmd_operands_length++] = pdu_id; 2995e3d4d2bSMilanka Ringwald // reserved 3005e3d4d2bSMilanka Ringwald (*out_connection)->cmd_operands[(*out_connection)->cmd_operands_length++] = 0; 3015e3d4d2bSMilanka Ringwald // param length 3025e3d4d2bSMilanka Ringwald big_endian_store_16((*out_connection)->cmd_operands, (*out_connection)->cmd_operands_length, param_length); 3035e3d4d2bSMilanka Ringwald (*out_connection)->cmd_operands_length += 2; 3045e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 3055e3d4d2bSMilanka Ringwald } 3065e3d4d2bSMilanka Ringwald 307e0bbf3edSMilanka 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){ 3085e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = NULL; 3095e3d4d2bSMilanka Ringwald uint8_t status = avrcp_prepare_vendor_dependent_response(avrcp_cid, &connection, AVRCP_PDU_ID_GET_CAPABILITIES, 2+size); 3105e3d4d2bSMilanka Ringwald if (status != ERROR_CODE_SUCCESS) return status; 311e0bbf3edSMilanka Ringwald 3125e3d4d2bSMilanka Ringwald connection->cmd_operands[connection->cmd_operands_length++] = capability_id; 3135e3d4d2bSMilanka Ringwald connection->cmd_operands[connection->cmd_operands_length++] = capabilities_num; 3145e3d4d2bSMilanka Ringwald memcpy(connection->cmd_operands+connection->cmd_operands_length, capabilities, size); 3155e3d4d2bSMilanka Ringwald connection->cmd_operands_length += size; 316e0bbf3edSMilanka Ringwald 317d1386928SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 318d1386928SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 319d1386928SMilanka Ringwald return ERROR_CODE_SUCCESS; 320e0bbf3edSMilanka Ringwald } 321e0bbf3edSMilanka Ringwald 322e0bbf3edSMilanka Ringwald uint8_t avrcp_target_supported_events(uint16_t avrcp_cid, uint8_t capabilities_num, uint8_t * capabilities, uint8_t size){ 323e0bbf3edSMilanka Ringwald return avrcp_target_capability(avrcp_cid, AVRCP_CAPABILITY_ID_EVENT, capabilities_num, capabilities, size); 324e0bbf3edSMilanka Ringwald } 325e0bbf3edSMilanka Ringwald 3265e3d4d2bSMilanka Ringwald uint8_t avrcp_target_supported_companies(uint16_t avrcp_cid, uint8_t capabilities_num, uint8_t * capabilities, uint8_t size ){ 3275e3d4d2bSMilanka Ringwald return avrcp_target_capability(avrcp_cid, AVRCP_CAPABILITY_ID_COMPANY, capabilities_num, capabilities, size); 3285e3d4d2bSMilanka Ringwald } 3295e3d4d2bSMilanka Ringwald 3305e3d4d2bSMilanka 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){ 3315e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = NULL; 3325e3d4d2bSMilanka Ringwald uint8_t status = avrcp_prepare_vendor_dependent_response(avrcp_cid, &connection, AVRCP_PDU_ID_GET_PLAY_STATUS, 11); 3335e3d4d2bSMilanka Ringwald if (status != ERROR_CODE_SUCCESS) return status; 3345e3d4d2bSMilanka Ringwald 3355e3d4d2bSMilanka Ringwald big_endian_store_32(connection->cmd_operands, connection->cmd_operands_length, song_length_ms); 3365e3d4d2bSMilanka Ringwald connection->cmd_operands_length += 4; 3375e3d4d2bSMilanka Ringwald big_endian_store_32(connection->cmd_operands, connection->cmd_operands_length, song_position_ms); 3385e3d4d2bSMilanka Ringwald connection->cmd_operands_length += 4; 3395e3d4d2bSMilanka Ringwald connection->cmd_operands[connection->cmd_operands_length++] = play_status; 3405e3d4d2bSMilanka Ringwald 3415e3d4d2bSMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 3425e3d4d2bSMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 3435e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 3445e3d4d2bSMilanka Ringwald } 3455e3d4d2bSMilanka Ringwald 3465e3d4d2bSMilanka Ringwald uint8_t avrcp_target_now_playing_info(uint16_t avrcp_cid){ 3475e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 3485e3d4d2bSMilanka Ringwald if (!connection){ 3495e3d4d2bSMilanka Ringwald log_error("avrcp tartget: could not find a connection."); 3505e3d4d2bSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3515e3d4d2bSMilanka Ringwald } 3525e3d4d2bSMilanka Ringwald 3535e3d4d2bSMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 3545e3d4d2bSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 3555e3d4d2bSMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE; 3565e3d4d2bSMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; 3575e3d4d2bSMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 3585e3d4d2bSMilanka Ringwald 3595e3d4d2bSMilanka Ringwald connection->now_playing_info_response = 1; 3605e3d4d2bSMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 3615e3d4d2bSMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 3625e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 3635e3d4d2bSMilanka Ringwald } 3645e3d4d2bSMilanka Ringwald 3655e3d4d2bSMilanka Ringwald static uint8_t avrcp_target_store_media_attr(uint16_t avrcp_cid, avrcp_media_attribute_id_t attr_id, const char * value){ 3665e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 3675e3d4d2bSMilanka Ringwald if (!connection){ 3685e3d4d2bSMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 3695e3d4d2bSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3705e3d4d2bSMilanka Ringwald } 3715e3d4d2bSMilanka Ringwald 3725e3d4d2bSMilanka Ringwald int index = attr_id - 1; 3735e3d4d2bSMilanka Ringwald connection->now_playing_info[index].value = (uint8_t*)value; 3745e3d4d2bSMilanka Ringwald connection->now_playing_info[index].len = strlen(value); 3755e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 3765e3d4d2bSMilanka Ringwald } 3775e3d4d2bSMilanka Ringwald 3785e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_title(uint16_t avrcp_cid, const char * title){ 3795e3d4d2bSMilanka Ringwald return avrcp_target_store_media_attr(avrcp_cid, AVRCP_MEDIA_ATTR_TITLE, title); 3805e3d4d2bSMilanka Ringwald } 3815e3d4d2bSMilanka Ringwald 3825e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_artist(uint16_t avrcp_cid, const char * artist){ 3835e3d4d2bSMilanka Ringwald return avrcp_target_store_media_attr(avrcp_cid, AVRCP_MEDIA_ATTR_ARTIST, artist); 3845e3d4d2bSMilanka Ringwald } 3855e3d4d2bSMilanka Ringwald 3865e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_album(uint16_t avrcp_cid, const char * album){ 3875e3d4d2bSMilanka Ringwald return avrcp_target_store_media_attr(avrcp_cid, AVRCP_MEDIA_ATTR_ALBUM, album); 3885e3d4d2bSMilanka Ringwald } 3895e3d4d2bSMilanka Ringwald 3905e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_genre(uint16_t avrcp_cid, const char * genre){ 3915e3d4d2bSMilanka Ringwald return avrcp_target_store_media_attr(avrcp_cid, AVRCP_MEDIA_ATTR_GENRE, genre); 3925e3d4d2bSMilanka Ringwald } 3935e3d4d2bSMilanka Ringwald 3945e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_song_length_ms(uint16_t avrcp_cid, const uint32_t song_length_ms){ 3955e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 3965e3d4d2bSMilanka Ringwald if (!connection){ 3975e3d4d2bSMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 3985e3d4d2bSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 3995e3d4d2bSMilanka Ringwald } 4005e3d4d2bSMilanka Ringwald connection->song_length_ms = song_length_ms; 4015e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 4025e3d4d2bSMilanka Ringwald } 4035e3d4d2bSMilanka Ringwald 4045e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_total_tracks(uint16_t avrcp_cid, const int total_tracks){ 4055e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 4065e3d4d2bSMilanka Ringwald if (!connection){ 4075e3d4d2bSMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 4085e3d4d2bSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 4095e3d4d2bSMilanka Ringwald } 4105e3d4d2bSMilanka Ringwald connection->total_tracks = total_tracks; 4115e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 4125e3d4d2bSMilanka Ringwald } 4135e3d4d2bSMilanka Ringwald 4145e3d4d2bSMilanka Ringwald uint8_t avrcp_target_set_now_playing_track_nr(uint16_t avrcp_cid, const int track_nr){ 4155e3d4d2bSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 4165e3d4d2bSMilanka Ringwald if (!connection){ 4175e3d4d2bSMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 4185e3d4d2bSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 4195e3d4d2bSMilanka Ringwald } 4205e3d4d2bSMilanka Ringwald connection->track_nr = track_nr; 4215e3d4d2bSMilanka Ringwald return ERROR_CODE_SUCCESS; 4225e3d4d2bSMilanka Ringwald } 423e0bbf3edSMilanka Ringwald 424e0bbf3edSMilanka Ringwald static uint8_t * avrcp_get_company_id(uint8_t *packet, uint16_t size){ 425e0bbf3edSMilanka Ringwald UNUSED(size); 426e0bbf3edSMilanka Ringwald return packet + 6; 427e0bbf3edSMilanka Ringwald } 428e0bbf3edSMilanka Ringwald 429e0bbf3edSMilanka Ringwald static uint8_t * avrcp_get_pdu(uint8_t *packet, uint16_t size){ 430e0bbf3edSMilanka Ringwald UNUSED(size); 431e0bbf3edSMilanka Ringwald return packet + 9; 432e0bbf3edSMilanka Ringwald } 433e0bbf3edSMilanka Ringwald 43401dc6e35SMilanka Ringwald static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){ 43501dc6e35SMilanka Ringwald UNUSED(connection); 43601dc6e35SMilanka Ringwald UNUSED(packet); 43701dc6e35SMilanka Ringwald UNUSED(size); 4384b338011SMilanka Ringwald 4394b338011SMilanka Ringwald // uint8_t opcode; 440e0bbf3edSMilanka Ringwald 4414b338011SMilanka Ringwald uint8_t transport_header = packet[0]; 4424b338011SMilanka Ringwald connection->transaction_label = transport_header >> 4; 4434b338011SMilanka Ringwald // uint8_t packet_type = (transport_header & 0x0F) >> 2; 4444b338011SMilanka Ringwald // uint8_t frame_type = (transport_header & 0x03) >> 1; 4454b338011SMilanka Ringwald // uint8_t ipid = transport_header & 0x01; 4464b338011SMilanka Ringwald // uint8_t byte_value = packet[2]; 4474b338011SMilanka Ringwald // uint16_t pid = (byte_value << 8) | packet[2]; 4484b338011SMilanka Ringwald 449e0bbf3edSMilanka Ringwald // avrcp_command_type_t ctype = (avrcp_command_type_t) packet[3]; 450e0bbf3edSMilanka Ringwald // uint8_t byte_value = packet[4]; 451e0bbf3edSMilanka Ringwald avrcp_subunit_type_t subunit_type = (avrcp_subunit_type_t) (packet[4] >> 3); 452e0bbf3edSMilanka Ringwald avrcp_subunit_id_t subunit_id = (avrcp_subunit_id_t) (packet[4] & 0x07); 4534b338011SMilanka Ringwald // opcode = packet[pos++]; 4544b338011SMilanka Ringwald 4554b338011SMilanka Ringwald // printf(" Transport header 0x%02x (transaction_label %d, packet_type %d, frame_type %d, ipid %d), pid 0x%4x\n", 4564b338011SMilanka Ringwald // transport_header, transaction_label, packet_type, frame_type, ipid, pid); 4574b338011SMilanka Ringwald // printf_hexdump(packet+pos, size-pos); 4584b338011SMilanka Ringwald 459e0bbf3edSMilanka Ringwald avrcp_command_opcode_t opcode = avrcp_cmd_opcode(packet,size); 460e0bbf3edSMilanka Ringwald uint8_t * company_id = avrcp_get_company_id(packet, size); 461e0bbf3edSMilanka Ringwald uint8_t * pdu = avrcp_get_pdu(packet, size); 4625e3d4d2bSMilanka Ringwald // uint16_t param_length = big_endian_read_16(pdu, 2); 463e0bbf3edSMilanka Ringwald 4645e3d4d2bSMilanka Ringwald int pos = 4; 465c045af99SMilanka Ringwald uint8_t pdu_id; 466e0bbf3edSMilanka Ringwald connection->cmd_operands_length = 0; 467e0bbf3edSMilanka Ringwald 468e0bbf3edSMilanka Ringwald switch (opcode){ 4694b338011SMilanka Ringwald case AVRCP_CMD_OPCODE_UNIT_INFO: 4704b338011SMilanka Ringwald avrcp_target_emit_respond_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_UNIT_INFO_QUERY); 4714b338011SMilanka Ringwald break; 472e0bbf3edSMilanka Ringwald case AVRCP_CMD_OPCODE_SUBUNIT_INFO:{ 4735e3d4d2bSMilanka Ringwald uint8_t offset = 4 * (packet[pos+2]>>4); 474e0bbf3edSMilanka Ringwald avrcp_target_emit_respond_subunit_info_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, offset); 475e0bbf3edSMilanka Ringwald break; 476e0bbf3edSMilanka Ringwald } 477e0bbf3edSMilanka Ringwald case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT: 478e0bbf3edSMilanka Ringwald pdu_id = pdu[0]; 479e0bbf3edSMilanka Ringwald // 1 - reserved 480e0bbf3edSMilanka Ringwald // 2-3 param length, 481c045af99SMilanka Ringwald memcpy(connection->cmd_operands, company_id, 3); 482c045af99SMilanka Ringwald connection->cmd_operands_length = 3; 483e0bbf3edSMilanka Ringwald switch (pdu_id){ 484e0bbf3edSMilanka Ringwald case AVRCP_PDU_ID_GET_CAPABILITIES:{ 4855e3d4d2bSMilanka Ringwald avrcp_capability_id_t capability_id = (avrcp_capability_id_t) pdu[pos]; 486e0bbf3edSMilanka Ringwald switch (capability_id){ 487e0bbf3edSMilanka Ringwald case AVRCP_CAPABILITY_ID_EVENT: 488c045af99SMilanka Ringwald avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_EVENT_IDS_QUERY); 489e0bbf3edSMilanka Ringwald break; 490e0bbf3edSMilanka Ringwald case AVRCP_CAPABILITY_ID_COMPANY: 491c045af99SMilanka Ringwald avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_COMPANY_IDS_QUERY); 492e0bbf3edSMilanka Ringwald break; 493e0bbf3edSMilanka Ringwald default: 494e0bbf3edSMilanka Ringwald avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_PARAMETER); 495e0bbf3edSMilanka Ringwald break; 496e0bbf3edSMilanka Ringwald } 497e0bbf3edSMilanka Ringwald break; 498e0bbf3edSMilanka Ringwald } 499c045af99SMilanka Ringwald case AVRCP_PDU_ID_GET_PLAY_STATUS: 500c045af99SMilanka Ringwald avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_PLAY_STATUS_QUERY); 501c045af99SMilanka Ringwald break; 5025e3d4d2bSMilanka Ringwald case AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES:{ 5035e3d4d2bSMilanka Ringwald uint8_t play_identifier[8]; 5045e3d4d2bSMilanka Ringwald memset(play_identifier, 0, 8); 5055e3d4d2bSMilanka Ringwald if (memcmp(pdu+pos, play_identifier, 8) != 0) { 5065e3d4d2bSMilanka Ringwald avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_PARAMETER); 5075e3d4d2bSMilanka Ringwald return; 5085e3d4d2bSMilanka Ringwald } 5095e3d4d2bSMilanka Ringwald avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_NOW_PLAYING_INFO_QUERY); 5105e3d4d2bSMilanka Ringwald break; 5115e3d4d2bSMilanka Ringwald } 512e0bbf3edSMilanka Ringwald default: 513c045af99SMilanka Ringwald printf("unhandled pdu id 0x%02x\n", pdu_id); 514c045af99SMilanka Ringwald avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_COMMAND); 515e0bbf3edSMilanka Ringwald break; 516e0bbf3edSMilanka Ringwald } 5174b338011SMilanka Ringwald break; 5184b338011SMilanka Ringwald default: 5194b338011SMilanka Ringwald printf("AVRCP source: opcode 0x%02x not implemented\n", avrcp_cmd_opcode(packet,size)); 5204b338011SMilanka Ringwald break; 5214b338011SMilanka Ringwald } 52201dc6e35SMilanka Ringwald } 52301dc6e35SMilanka Ringwald 52401dc6e35SMilanka Ringwald static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 52501dc6e35SMilanka Ringwald avrcp_connection_t * connection; 52601dc6e35SMilanka Ringwald switch (packet_type) { 52701dc6e35SMilanka Ringwald case L2CAP_DATA_PACKET: 52801dc6e35SMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, &avrcp_target_context); 52901dc6e35SMilanka Ringwald if (!connection) break; 53001dc6e35SMilanka Ringwald avrcp_handle_l2cap_data_packet_for_signaling_connection(connection, packet, size); 53101dc6e35SMilanka Ringwald break; 53201dc6e35SMilanka Ringwald case HCI_EVENT_PACKET: 53301dc6e35SMilanka Ringwald switch (hci_event_packet_get_type(packet)){ 53401dc6e35SMilanka Ringwald case L2CAP_EVENT_CAN_SEND_NOW: 53501dc6e35SMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, &avrcp_target_context); 53601dc6e35SMilanka Ringwald if (!connection) break; 537d1386928SMilanka Ringwald switch (connection->state){ 538d1386928SMilanka Ringwald case AVCTP_W2_SEND_RESPONSE: 539d1386928SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 5405e3d4d2bSMilanka Ringwald if (connection->now_playing_info_response){ 5415e3d4d2bSMilanka Ringwald printf("now_playing_info_response \n"); 5425e3d4d2bSMilanka Ringwald connection->now_playing_info_response = 0; 5435e3d4d2bSMilanka Ringwald avrcp_target_send_now_playing_info(connection->l2cap_signaling_cid, connection); 5445e3d4d2bSMilanka Ringwald break; 5455e3d4d2bSMilanka Ringwald } 5465e3d4d2bSMilanka Ringwald avrcp_target_send_response(connection->l2cap_signaling_cid, connection); 547d1386928SMilanka Ringwald break; 548d1386928SMilanka Ringwald default: 549d1386928SMilanka Ringwald return; 550d1386928SMilanka Ringwald } 55101dc6e35SMilanka Ringwald break; 55201dc6e35SMilanka Ringwald default: 55301dc6e35SMilanka Ringwald avrcp_packet_handler(packet_type, channel, packet, size, &avrcp_target_context); 55401dc6e35SMilanka Ringwald break; 55501dc6e35SMilanka Ringwald } 55601dc6e35SMilanka Ringwald default: 55701dc6e35SMilanka Ringwald break; 55801dc6e35SMilanka Ringwald } 55901dc6e35SMilanka Ringwald } 56001dc6e35SMilanka Ringwald 56101dc6e35SMilanka Ringwald void avrcp_target_init(void){ 56201dc6e35SMilanka Ringwald avrcp_target_context.role = AVRCP_TARGET; 56301dc6e35SMilanka Ringwald avrcp_target_context.connections = NULL; 56401dc6e35SMilanka Ringwald avrcp_target_context.packet_handler = avrcp_controller_packet_handler; 56501dc6e35SMilanka Ringwald l2cap_register_service(&avrcp_controller_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_0); 56601dc6e35SMilanka Ringwald } 56701dc6e35SMilanka Ringwald 56801dc6e35SMilanka Ringwald void avrcp_target_register_packet_handler(btstack_packet_handler_t callback){ 56901dc6e35SMilanka Ringwald if (callback == NULL){ 57001dc6e35SMilanka Ringwald log_error("avrcp_register_packet_handler called with NULL callback"); 57101dc6e35SMilanka Ringwald return; 57201dc6e35SMilanka Ringwald } 57301dc6e35SMilanka Ringwald avrcp_target_context.avrcp_callback = callback; 57401dc6e35SMilanka Ringwald } 57501dc6e35SMilanka Ringwald 57601dc6e35SMilanka Ringwald uint8_t avrcp_target_connect(bd_addr_t bd_addr, uint16_t * avrcp_cid){ 57701dc6e35SMilanka Ringwald return avrcp_connect(bd_addr, &avrcp_target_context, avrcp_cid); 57801dc6e35SMilanka Ringwald } 57901dc6e35SMilanka Ringwald 58001dc6e35SMilanka Ringwald uint8_t avrcp_target_disconnect(uint16_t avrcp_cid){ 58101dc6e35SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 58201dc6e35SMilanka Ringwald if (!connection){ 58301dc6e35SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 58401dc6e35SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 58501dc6e35SMilanka Ringwald } 58601dc6e35SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 58701dc6e35SMilanka Ringwald l2cap_disconnect(connection->l2cap_signaling_cid, 0); 58801dc6e35SMilanka Ringwald return ERROR_CODE_SUCCESS; 58901dc6e35SMilanka Ringwald } 59001dc6e35SMilanka Ringwald 591