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> 4401dc6e35SMilanka Ringwald 4501dc6e35SMilanka Ringwald #include "btstack.h" 4601dc6e35SMilanka Ringwald #include "classic/avrcp.h" 4701dc6e35SMilanka Ringwald 4801dc6e35SMilanka Ringwald static avrcp_context_t avrcp_target_context; 4901dc6e35SMilanka Ringwald 5001dc6e35SMilanka 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){ 5101dc6e35SMilanka Ringwald avrcp_create_sdp_record(0, service, service_record_handle, browsing, supported_features, service_name, service_provider_name); 5201dc6e35SMilanka Ringwald } 5301dc6e35SMilanka Ringwald 544b338011SMilanka Ringwald static void avrcp_target_emit_respond_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t subeventID){ 554b338011SMilanka Ringwald if (!callback) return; 564b338011SMilanka Ringwald uint8_t event[5]; 574b338011SMilanka Ringwald int pos = 0; 584b338011SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 594b338011SMilanka Ringwald event[pos++] = sizeof(event) - 2; 604b338011SMilanka Ringwald event[pos++] = subeventID; 614b338011SMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 624b338011SMilanka Ringwald pos += 2; 634b338011SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 644b338011SMilanka Ringwald } 654b338011SMilanka Ringwald 66e0bbf3edSMilanka Ringwald static void avrcp_target_emit_respond_subunit_info_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t offset){ 674b338011SMilanka Ringwald if (!callback) return; 684b338011SMilanka Ringwald uint8_t event[6]; 694b338011SMilanka Ringwald int pos = 0; 704b338011SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 714b338011SMilanka Ringwald event[pos++] = sizeof(event) - 2; 724b338011SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_SUBUNIT_INFO_QUERY; 734b338011SMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 744b338011SMilanka Ringwald pos += 2; 75e0bbf3edSMilanka Ringwald event[pos++] = offset; 76e0bbf3edSMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 77e0bbf3edSMilanka Ringwald } 78e0bbf3edSMilanka Ringwald 79*c045af99SMilanka Ringwald static void avrcp_target_emit_respond_vendor_dependent_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t subevent_id){ 80e0bbf3edSMilanka Ringwald if (!callback) return; 81*c045af99SMilanka Ringwald uint8_t event[5]; 82e0bbf3edSMilanka Ringwald int pos = 0; 83e0bbf3edSMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META; 84e0bbf3edSMilanka Ringwald event[pos++] = sizeof(event) - 2; 85e0bbf3edSMilanka Ringwald event[pos++] = subevent_id; 86e0bbf3edSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid); 874b338011SMilanka Ringwald (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 884b338011SMilanka Ringwald } 894b338011SMilanka Ringwald 904b338011SMilanka Ringwald static int avrcp_send_response(uint16_t cid, avrcp_connection_t * connection){ 914b338011SMilanka Ringwald uint8_t command[30]; 924b338011SMilanka Ringwald int pos = 0; 934b338011SMilanka Ringwald // transport header 944b338011SMilanka Ringwald // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 954b338011SMilanka Ringwald command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0; 964b338011SMilanka Ringwald // Profile IDentifier (PID) 974b338011SMilanka Ringwald command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 984b338011SMilanka Ringwald command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 994b338011SMilanka Ringwald 1004b338011SMilanka Ringwald // command_type 1014b338011SMilanka Ringwald command[pos++] = connection->command_type; 1024b338011SMilanka Ringwald // subunit_type | subunit ID 1034b338011SMilanka Ringwald command[pos++] = (connection->subunit_type << 3) | connection->subunit_id; 1044b338011SMilanka Ringwald // opcode 1054b338011SMilanka Ringwald command[pos++] = (uint8_t)connection->command_opcode; 1064b338011SMilanka Ringwald // operands 1074b338011SMilanka Ringwald memcpy(command+pos, connection->cmd_operands, connection->cmd_operands_length); 1084b338011SMilanka Ringwald pos += connection->cmd_operands_length; 109d1386928SMilanka Ringwald connection->wait_to_send = 0; 1104b338011SMilanka Ringwald return l2cap_send(cid, command, pos); 1114b338011SMilanka Ringwald } 1124b338011SMilanka Ringwald 113e0bbf3edSMilanka 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){ 114e0bbf3edSMilanka Ringwald // AVRCP_CTYPE_RESPONSE_REJECTED 115e0bbf3edSMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_REJECTED; 116e0bbf3edSMilanka Ringwald connection->subunit_type = subunit_type; 117e0bbf3edSMilanka Ringwald connection->subunit_id = subunit_id; 118e0bbf3edSMilanka Ringwald connection->command_opcode = opcode; 119e0bbf3edSMilanka Ringwald 120e0bbf3edSMilanka Ringwald // company id is 3 bytes long 121e0bbf3edSMilanka Ringwald int pos = connection->cmd_operands_length; 122e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = pdu_id; 123e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = 0; 124e0bbf3edSMilanka Ringwald // param length 125*c045af99SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 1); 126e0bbf3edSMilanka Ringwald pos += 2; 127e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = status; 128e0bbf3edSMilanka Ringwald connection->cmd_operands_length = pos; 129d1386928SMilanka Ringwald 130d1386928SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 131d1386928SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 132d1386928SMilanka Ringwald return ERROR_CODE_SUCCESS; 133e0bbf3edSMilanka Ringwald } 134e0bbf3edSMilanka Ringwald 1354b338011SMilanka Ringwald uint8_t avrcp_target_unit_info(uint16_t avrcp_cid, avrcp_subunit_type_t unit_type, uint32_t company_id){ 1364b338011SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 1374b338011SMilanka Ringwald if (!connection){ 1384b338011SMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 1394b338011SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1404b338011SMilanka Ringwald } 1414b338011SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1424b338011SMilanka Ringwald 1434b338011SMilanka Ringwald uint8_t unit = 0; 1444b338011SMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE; 1454b338011SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique 1464b338011SMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE; 147e0bbf3edSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_UNIT_INFO; 1484b338011SMilanka Ringwald 1494b338011SMilanka Ringwald connection->cmd_operands_length = 5; 1504b338011SMilanka Ringwald connection->cmd_operands[0] = 0x07; 1514b338011SMilanka Ringwald connection->cmd_operands[1] = (unit_type << 4) | unit; 1524b338011SMilanka Ringwald // company id is 3 bytes long 153e0bbf3edSMilanka Ringwald big_endian_store_32(connection->cmd_operands, 2, company_id); 154d1386928SMilanka Ringwald 155d1386928SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 156d1386928SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 157d1386928SMilanka Ringwald return ERROR_CODE_SUCCESS; 1584b338011SMilanka Ringwald } 1594b338011SMilanka Ringwald 1604b338011SMilanka 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){ 1614b338011SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 1624b338011SMilanka Ringwald if (!connection){ 1634b338011SMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 1644b338011SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1654b338011SMilanka Ringwald } 1664b338011SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 1674b338011SMilanka Ringwald 1684b338011SMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_SUBUNIT_INFO; 1694b338011SMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE; 1704b338011SMilanka Ringwald connection->subunit_type = subunit_type; //vendor unique 1714b338011SMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE; 1724b338011SMilanka Ringwald 1734b338011SMilanka Ringwald uint8_t page = offset / 4; 1744b338011SMilanka Ringwald uint8_t extension_code = 7; 1754b338011SMilanka Ringwald connection->cmd_operands_length = 5; 1764b338011SMilanka Ringwald connection->cmd_operands[0] = (page << 4) | extension_code; 1774b338011SMilanka Ringwald memcpy(connection->cmd_operands+1, subunit_info_data + offset, 4); 178d1386928SMilanka Ringwald 179d1386928SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 180d1386928SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 181d1386928SMilanka Ringwald return ERROR_CODE_SUCCESS; 1824b338011SMilanka Ringwald } 1834b338011SMilanka Ringwald 184e0bbf3edSMilanka 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){ 185e0bbf3edSMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 186e0bbf3edSMilanka Ringwald if (!connection){ 187e0bbf3edSMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 188e0bbf3edSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 189e0bbf3edSMilanka Ringwald } 190e0bbf3edSMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 191e0bbf3edSMilanka Ringwald 192e0bbf3edSMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 193e0bbf3edSMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE; 194e0bbf3edSMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; //AVRCP_SUBUNIT_TYPE_UNIT; 195e0bbf3edSMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 196e0bbf3edSMilanka Ringwald 197e0bbf3edSMilanka Ringwald int pos = connection->cmd_operands_length; 198e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_CAPABILITIES; 199e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = 0; 200e0bbf3edSMilanka Ringwald // param length 201e0bbf3edSMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 2+size); 202e0bbf3edSMilanka Ringwald pos += 2; 203e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = capability_id; 204e0bbf3edSMilanka Ringwald connection->cmd_operands[pos++] = capabilities_num; 205e0bbf3edSMilanka Ringwald memcpy(connection->cmd_operands+pos, capabilities, size); 206e0bbf3edSMilanka Ringwald pos += size; 207e0bbf3edSMilanka Ringwald connection->cmd_operands_length = pos; 208d1386928SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 209d1386928SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 210d1386928SMilanka Ringwald return ERROR_CODE_SUCCESS; 211e0bbf3edSMilanka Ringwald } 212e0bbf3edSMilanka Ringwald 213*c045af99SMilanka 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 status){ 214*c045af99SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 215*c045af99SMilanka Ringwald if (!connection){ 216*c045af99SMilanka Ringwald log_error("avrcp_unit_info: could not find a connection."); 217*c045af99SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 218*c045af99SMilanka Ringwald } 219*c045af99SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 220*c045af99SMilanka Ringwald 221*c045af99SMilanka Ringwald connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; 222*c045af99SMilanka Ringwald connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE; 223*c045af99SMilanka Ringwald connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; //AVRCP_SUBUNIT_TYPE_UNIT; 224*c045af99SMilanka Ringwald connection->subunit_id = AVRCP_SUBUNIT_ID; 225*c045af99SMilanka Ringwald 226*c045af99SMilanka Ringwald int pos = connection->cmd_operands_length; 227*c045af99SMilanka Ringwald connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_PLAY_STATUS; 228*c045af99SMilanka Ringwald connection->cmd_operands[pos++] = 0; 229*c045af99SMilanka Ringwald // param length 230*c045af99SMilanka Ringwald big_endian_store_16(connection->cmd_operands, pos, 2+4+4+1); 231*c045af99SMilanka Ringwald pos += 2; 232*c045af99SMilanka Ringwald big_endian_store_32(connection->cmd_operands, pos, song_length_ms); 233*c045af99SMilanka Ringwald pos += 4; 234*c045af99SMilanka Ringwald big_endian_store_32(connection->cmd_operands, pos, song_position_ms); 235*c045af99SMilanka Ringwald pos += 4; 236*c045af99SMilanka Ringwald connection->cmd_operands[pos++] = status; 237*c045af99SMilanka Ringwald connection->cmd_operands_length = pos; 238*c045af99SMilanka Ringwald connection->state = AVCTP_W2_SEND_RESPONSE; 239*c045af99SMilanka Ringwald avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); 240*c045af99SMilanka Ringwald return ERROR_CODE_SUCCESS; 241*c045af99SMilanka Ringwald } 242*c045af99SMilanka Ringwald 243e0bbf3edSMilanka Ringwald 244e0bbf3edSMilanka Ringwald uint8_t avrcp_target_supported_companies(uint16_t avrcp_cid, uint8_t capabilities_num, uint8_t * capabilities, uint8_t size ){ 245e0bbf3edSMilanka Ringwald return avrcp_target_capability(avrcp_cid, AVRCP_CAPABILITY_ID_COMPANY, capabilities_num, capabilities, size); 246e0bbf3edSMilanka Ringwald } 247e0bbf3edSMilanka Ringwald 248e0bbf3edSMilanka Ringwald uint8_t avrcp_target_supported_events(uint16_t avrcp_cid, uint8_t capabilities_num, uint8_t * capabilities, uint8_t size){ 249e0bbf3edSMilanka Ringwald return avrcp_target_capability(avrcp_cid, AVRCP_CAPABILITY_ID_EVENT, capabilities_num, capabilities, size); 250e0bbf3edSMilanka Ringwald } 251e0bbf3edSMilanka Ringwald 252e0bbf3edSMilanka Ringwald 253e0bbf3edSMilanka Ringwald static uint8_t * avrcp_get_company_id(uint8_t *packet, uint16_t size){ 254e0bbf3edSMilanka Ringwald UNUSED(size); 255e0bbf3edSMilanka Ringwald return packet + 6; 256e0bbf3edSMilanka Ringwald } 257e0bbf3edSMilanka Ringwald 258e0bbf3edSMilanka Ringwald static uint8_t * avrcp_get_pdu(uint8_t *packet, uint16_t size){ 259e0bbf3edSMilanka Ringwald UNUSED(size); 260e0bbf3edSMilanka Ringwald return packet + 9; 261e0bbf3edSMilanka Ringwald } 262e0bbf3edSMilanka Ringwald 26301dc6e35SMilanka Ringwald static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){ 26401dc6e35SMilanka Ringwald UNUSED(connection); 26501dc6e35SMilanka Ringwald UNUSED(packet); 26601dc6e35SMilanka Ringwald UNUSED(size); 2674b338011SMilanka Ringwald 2684b338011SMilanka Ringwald // uint8_t opcode; 269e0bbf3edSMilanka Ringwald 2704b338011SMilanka Ringwald uint8_t transport_header = packet[0]; 2714b338011SMilanka Ringwald connection->transaction_label = transport_header >> 4; 2724b338011SMilanka Ringwald // uint8_t packet_type = (transport_header & 0x0F) >> 2; 2734b338011SMilanka Ringwald // uint8_t frame_type = (transport_header & 0x03) >> 1; 2744b338011SMilanka Ringwald // uint8_t ipid = transport_header & 0x01; 2754b338011SMilanka Ringwald // uint8_t byte_value = packet[2]; 2764b338011SMilanka Ringwald // uint16_t pid = (byte_value << 8) | packet[2]; 2774b338011SMilanka Ringwald 278e0bbf3edSMilanka Ringwald // avrcp_command_type_t ctype = (avrcp_command_type_t) packet[3]; 279e0bbf3edSMilanka Ringwald // uint8_t byte_value = packet[4]; 280e0bbf3edSMilanka Ringwald avrcp_subunit_type_t subunit_type = (avrcp_subunit_type_t) (packet[4] >> 3); 281e0bbf3edSMilanka Ringwald avrcp_subunit_id_t subunit_id = (avrcp_subunit_id_t) (packet[4] & 0x07); 2824b338011SMilanka Ringwald // opcode = packet[pos++]; 2834b338011SMilanka Ringwald 2844b338011SMilanka Ringwald // printf(" Transport header 0x%02x (transaction_label %d, packet_type %d, frame_type %d, ipid %d), pid 0x%4x\n", 2854b338011SMilanka Ringwald // transport_header, transaction_label, packet_type, frame_type, ipid, pid); 2864b338011SMilanka Ringwald // printf_hexdump(packet+pos, size-pos); 2874b338011SMilanka Ringwald 288e0bbf3edSMilanka Ringwald avrcp_command_opcode_t opcode = avrcp_cmd_opcode(packet,size); 289e0bbf3edSMilanka Ringwald // uint16_t param_length = big_endian_read_16(operands, 5); 290e0bbf3edSMilanka Ringwald uint8_t * company_id = avrcp_get_company_id(packet, size); 291e0bbf3edSMilanka Ringwald uint8_t * pdu = avrcp_get_pdu(packet, size); 292e0bbf3edSMilanka Ringwald 293*c045af99SMilanka Ringwald uint8_t pdu_id; 294*c045af99SMilanka Ringwald 295e0bbf3edSMilanka Ringwald connection->cmd_operands_length = 0; 296e0bbf3edSMilanka Ringwald 297e0bbf3edSMilanka Ringwald switch (opcode){ 2984b338011SMilanka Ringwald case AVRCP_CMD_OPCODE_UNIT_INFO: 2994b338011SMilanka Ringwald avrcp_target_emit_respond_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_UNIT_INFO_QUERY); 3004b338011SMilanka Ringwald break; 301e0bbf3edSMilanka Ringwald case AVRCP_CMD_OPCODE_SUBUNIT_INFO:{ 302e0bbf3edSMilanka Ringwald uint8_t offset = 4 * (packet[6]>>4); 303e0bbf3edSMilanka Ringwald avrcp_target_emit_respond_subunit_info_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, offset); 304e0bbf3edSMilanka Ringwald break; 305e0bbf3edSMilanka Ringwald } 306e0bbf3edSMilanka Ringwald case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT: 307e0bbf3edSMilanka Ringwald pdu_id = pdu[0]; 308e0bbf3edSMilanka Ringwald // 1 - reserved 309e0bbf3edSMilanka Ringwald // 2-3 param length, 310*c045af99SMilanka Ringwald memcpy(connection->cmd_operands, company_id, 3); 311*c045af99SMilanka Ringwald connection->cmd_operands_length = 3; 312e0bbf3edSMilanka Ringwald switch (pdu_id){ 313e0bbf3edSMilanka Ringwald case AVRCP_PDU_ID_GET_CAPABILITIES:{ 314e0bbf3edSMilanka Ringwald avrcp_capability_id_t capability_id = (avrcp_capability_id_t) pdu[4]; 315e0bbf3edSMilanka Ringwald switch (capability_id){ 316e0bbf3edSMilanka Ringwald case AVRCP_CAPABILITY_ID_EVENT: 317*c045af99SMilanka Ringwald avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_EVENT_IDS_QUERY); 318e0bbf3edSMilanka Ringwald break; 319e0bbf3edSMilanka Ringwald case AVRCP_CAPABILITY_ID_COMPANY: 320*c045af99SMilanka Ringwald avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_COMPANY_IDS_QUERY); 321e0bbf3edSMilanka Ringwald break; 322e0bbf3edSMilanka Ringwald default: 323e0bbf3edSMilanka Ringwald avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_PARAMETER); 324e0bbf3edSMilanka Ringwald break; 325e0bbf3edSMilanka Ringwald } 326e0bbf3edSMilanka Ringwald break; 327e0bbf3edSMilanka Ringwald } 328*c045af99SMilanka Ringwald case AVRCP_PDU_ID_GET_PLAY_STATUS: 329*c045af99SMilanka Ringwald avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_PLAY_STATUS_QUERY); 330*c045af99SMilanka Ringwald break; 331e0bbf3edSMilanka Ringwald default: 332*c045af99SMilanka Ringwald printf("unhandled pdu id 0x%02x\n", pdu_id); 333*c045af99SMilanka Ringwald avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_COMMAND); 334e0bbf3edSMilanka Ringwald break; 335e0bbf3edSMilanka Ringwald } 3364b338011SMilanka Ringwald break; 3374b338011SMilanka Ringwald default: 3384b338011SMilanka Ringwald printf("AVRCP source: opcode 0x%02x not implemented\n", avrcp_cmd_opcode(packet,size)); 3394b338011SMilanka Ringwald break; 3404b338011SMilanka Ringwald } 34101dc6e35SMilanka Ringwald } 34201dc6e35SMilanka Ringwald 34301dc6e35SMilanka Ringwald static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 34401dc6e35SMilanka Ringwald avrcp_connection_t * connection; 34501dc6e35SMilanka Ringwald switch (packet_type) { 34601dc6e35SMilanka Ringwald case L2CAP_DATA_PACKET: 34701dc6e35SMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, &avrcp_target_context); 34801dc6e35SMilanka Ringwald if (!connection) break; 34901dc6e35SMilanka Ringwald avrcp_handle_l2cap_data_packet_for_signaling_connection(connection, packet, size); 35001dc6e35SMilanka Ringwald break; 35101dc6e35SMilanka Ringwald case HCI_EVENT_PACKET: 35201dc6e35SMilanka Ringwald switch (hci_event_packet_get_type(packet)){ 35301dc6e35SMilanka Ringwald case L2CAP_EVENT_CAN_SEND_NOW: 35401dc6e35SMilanka Ringwald connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, &avrcp_target_context); 35501dc6e35SMilanka Ringwald if (!connection) break; 356d1386928SMilanka Ringwald switch (connection->state){ 357d1386928SMilanka Ringwald case AVCTP_W2_SEND_RESPONSE: 358d1386928SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED; 359d1386928SMilanka Ringwald avrcp_send_response(connection->l2cap_signaling_cid, connection); 360d1386928SMilanka Ringwald break; 361d1386928SMilanka Ringwald default: 362d1386928SMilanka Ringwald return; 363d1386928SMilanka Ringwald } 36401dc6e35SMilanka Ringwald break; 36501dc6e35SMilanka Ringwald default: 36601dc6e35SMilanka Ringwald avrcp_packet_handler(packet_type, channel, packet, size, &avrcp_target_context); 36701dc6e35SMilanka Ringwald break; 36801dc6e35SMilanka Ringwald } 36901dc6e35SMilanka Ringwald default: 37001dc6e35SMilanka Ringwald break; 37101dc6e35SMilanka Ringwald } 37201dc6e35SMilanka Ringwald } 37301dc6e35SMilanka Ringwald 37401dc6e35SMilanka Ringwald void avrcp_target_init(void){ 37501dc6e35SMilanka Ringwald avrcp_target_context.role = AVRCP_TARGET; 37601dc6e35SMilanka Ringwald avrcp_target_context.connections = NULL; 37701dc6e35SMilanka Ringwald avrcp_target_context.packet_handler = avrcp_controller_packet_handler; 37801dc6e35SMilanka Ringwald l2cap_register_service(&avrcp_controller_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_0); 37901dc6e35SMilanka Ringwald } 38001dc6e35SMilanka Ringwald 38101dc6e35SMilanka Ringwald void avrcp_target_register_packet_handler(btstack_packet_handler_t callback){ 38201dc6e35SMilanka Ringwald if (callback == NULL){ 38301dc6e35SMilanka Ringwald log_error("avrcp_register_packet_handler called with NULL callback"); 38401dc6e35SMilanka Ringwald return; 38501dc6e35SMilanka Ringwald } 38601dc6e35SMilanka Ringwald avrcp_target_context.avrcp_callback = callback; 38701dc6e35SMilanka Ringwald } 38801dc6e35SMilanka Ringwald 38901dc6e35SMilanka Ringwald uint8_t avrcp_target_connect(bd_addr_t bd_addr, uint16_t * avrcp_cid){ 39001dc6e35SMilanka Ringwald return avrcp_connect(bd_addr, &avrcp_target_context, avrcp_cid); 39101dc6e35SMilanka Ringwald } 39201dc6e35SMilanka Ringwald 39301dc6e35SMilanka Ringwald uint8_t avrcp_target_disconnect(uint16_t avrcp_cid){ 39401dc6e35SMilanka Ringwald avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context); 39501dc6e35SMilanka Ringwald if (!connection){ 39601dc6e35SMilanka Ringwald log_error("avrcp_get_capabilities: could not find a connection."); 39701dc6e35SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 39801dc6e35SMilanka Ringwald } 39901dc6e35SMilanka Ringwald if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 40001dc6e35SMilanka Ringwald l2cap_disconnect(connection->l2cap_signaling_cid, 0); 40101dc6e35SMilanka Ringwald return ERROR_CODE_SUCCESS; 40201dc6e35SMilanka Ringwald } 40301dc6e35SMilanka Ringwald 404