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