xref: /btstack/src/classic/avrcp_target.c (revision adaba9f34040c696f6e8e6f1096f218bf6ceb78e)
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