1ac726063SMatthias Ringwald /* 2ac726063SMatthias Ringwald * Copyright (C) 2023 BlueKitchen GmbH 3ac726063SMatthias Ringwald * 4ac726063SMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5ac726063SMatthias Ringwald * modification, are permitted provided that the following conditions 6ac726063SMatthias Ringwald * are met: 7ac726063SMatthias Ringwald * 8ac726063SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9ac726063SMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10ac726063SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11ac726063SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12ac726063SMatthias Ringwald * documentation and/or other materials provided with the distribution. 13ac726063SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14ac726063SMatthias Ringwald * contributors may be used to endorse or promote products derived 15ac726063SMatthias Ringwald * from this software without specific prior written permission. 16ac726063SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17ac726063SMatthias Ringwald * personal benefit and not for any commercial purpose or for 18ac726063SMatthias Ringwald * monetary gain. 19ac726063SMatthias Ringwald * 20ac726063SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21ac726063SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22ac726063SMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23ac726063SMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 24ac726063SMatthias Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25ac726063SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26ac726063SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27ac726063SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28ac726063SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29ac726063SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30ac726063SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31ac726063SMatthias Ringwald * SUCH DAMAGE. 32ac726063SMatthias Ringwald * 33ac726063SMatthias Ringwald * Please inquire about commercial licensing options at 34ac726063SMatthias Ringwald * [email protected] 35ac726063SMatthias Ringwald * 36ac726063SMatthias Ringwald */ 37ac726063SMatthias Ringwald 38ac726063SMatthias Ringwald #define BTSTACK_FILE__ "avrcp_cover_art_client.c" 39ac726063SMatthias Ringwald 40ac726063SMatthias Ringwald #include <string.h> 41ac726063SMatthias Ringwald 42ac726063SMatthias Ringwald #include "btstack_debug.h" 43ac726063SMatthias Ringwald #include "btstack_event.h" 44ac726063SMatthias Ringwald #include "classic/avrcp.h" 45ac726063SMatthias Ringwald #include "classic/avrcp_cover_art_client.h" 46ac726063SMatthias Ringwald #include "classic/obex.h" 47ac726063SMatthias Ringwald #include "hci_event.h" 48ac726063SMatthias Ringwald 49ac726063SMatthias Ringwald #ifdef ENABLE_AVRCP_COVER_ART 50ac726063SMatthias Ringwald 51ac726063SMatthias Ringwald static const hci_event_t avrcp_cover_art_client_connected = { 52ac726063SMatthias Ringwald .event_code = HCI_EVENT_AVRCP_META, 53ac726063SMatthias Ringwald .subevent_code = AVRCP_SUBEVENT_COVER_ART_CONNECTION_ESTABLISHED, 54ac726063SMatthias Ringwald .format = "1B22" 55ac726063SMatthias Ringwald }; 56ac726063SMatthias Ringwald 57ac726063SMatthias Ringwald static const hci_event_t avrcp_cover_art_client_disconnected = { 58ac726063SMatthias Ringwald .event_code = HCI_EVENT_AVRCP_META, 59ac726063SMatthias Ringwald .subevent_code = AVRCP_SUBEVENT_COVER_ART_CONNECTION_RELEASED, 60ac726063SMatthias Ringwald .format = "2" 61ac726063SMatthias Ringwald }; 62ac726063SMatthias Ringwald 63ac726063SMatthias Ringwald static const hci_event_t avrcp_cover_art_client_operation_complete = { 64ac726063SMatthias Ringwald .event_code = HCI_EVENT_AVRCP_META, 65ac726063SMatthias Ringwald .subevent_code = AVRCP_SUBEVENT_COVER_ART_OPERATION_COMPLETE, 66ac726063SMatthias Ringwald .format = "21" 67ac726063SMatthias Ringwald }; 68ac726063SMatthias Ringwald 69ac726063SMatthias Ringwald // 7163DD54-4A7E-11E2-B47C-0050C2490048 70ac726063SMatthias Ringwald static const uint8_t avrcp_cover_art_uuid[] = { 0x71, 0x63, 0xDD, 0x54, 0x4A, 0x7E, 0x11, 0xE2, 0xB4, 0x7C, 0x00, 0x50, 0xC2, 0x49, 0x00, 0x48 }; 71ac726063SMatthias Ringwald 72ac726063SMatthias Ringwald // OBEX types 73ac726063SMatthias Ringwald const char * avrcp_cover_art_image_properties_type = "x-bt/img-properties"; 74ac726063SMatthias Ringwald const char * avrcp_cover_art_image_type = "x-bt/img-img"; 75ac726063SMatthias Ringwald const char * avrcp_cover_art_linked_thumbnail_type = "x-bt/img-thm"; 76ac726063SMatthias Ringwald 77ac726063SMatthias Ringwald static btstack_linked_list_t avrcp_cover_art_client_connections; 78ac726063SMatthias Ringwald 7957ff2ef1SMatthias Ringwald static uint16_t avrcp_cover_art_client_next_cid(void) { 80ac726063SMatthias Ringwald static uint16_t cid = 0; 81ac726063SMatthias Ringwald cid++; 82ac726063SMatthias Ringwald if (cid == 0){ 83ac726063SMatthias Ringwald cid = 1; 84ac726063SMatthias Ringwald } 85ac726063SMatthias Ringwald return cid; 86ac726063SMatthias Ringwald } 87ac726063SMatthias Ringwald 88ac726063SMatthias Ringwald static avrcp_cover_art_client_t * avrcp_cover_art_client_for_goep_cid(uint16_t goep_cid){ 89ac726063SMatthias Ringwald btstack_linked_list_iterator_t it; 90ac726063SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_cover_art_client_connections); 91ac726063SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 92ac726063SMatthias Ringwald avrcp_cover_art_client_t * connection = (avrcp_cover_art_client_t *)btstack_linked_list_iterator_next(&it); 93ac726063SMatthias Ringwald if (connection->goep_cid == goep_cid) { 94ac726063SMatthias Ringwald return connection; 95ac726063SMatthias Ringwald }; 96ac726063SMatthias Ringwald } 97ac726063SMatthias Ringwald return NULL; 98ac726063SMatthias Ringwald } 99ac726063SMatthias Ringwald 100ac726063SMatthias Ringwald static avrcp_cover_art_client_t * avrcp_cover_art_client_for_avrcp_cid(uint16_t avrcp_cid){ 101ac726063SMatthias Ringwald btstack_linked_list_iterator_t it; 102ac726063SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_cover_art_client_connections); 103ac726063SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 104ac726063SMatthias Ringwald avrcp_cover_art_client_t * connection = (avrcp_cover_art_client_t *)btstack_linked_list_iterator_next(&it); 105ac726063SMatthias Ringwald if (connection->avrcp_cid == avrcp_cid) { 106ac726063SMatthias Ringwald return connection; 107ac726063SMatthias Ringwald }; 108ac726063SMatthias Ringwald } 109ac726063SMatthias Ringwald return NULL; 110ac726063SMatthias Ringwald } 111ac726063SMatthias Ringwald 112ac726063SMatthias Ringwald static avrcp_cover_art_client_t * avrcp_cover_art_client_for_cover_art_cid(uint16_t cover_art_cid){ 113ac726063SMatthias Ringwald btstack_linked_list_iterator_t it; 114ac726063SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_cover_art_client_connections); 115ac726063SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 116ac726063SMatthias Ringwald avrcp_cover_art_client_t * connection = (avrcp_cover_art_client_t *)btstack_linked_list_iterator_next(&it); 117ac726063SMatthias Ringwald if (connection->cover_art_cid == cover_art_cid) { 118ac726063SMatthias Ringwald return connection; 119ac726063SMatthias Ringwald }; 120ac726063SMatthias Ringwald } 121ac726063SMatthias Ringwald return NULL; 122ac726063SMatthias Ringwald } 123ac726063SMatthias Ringwald 124ac726063SMatthias Ringwald static void avrcp_cover_art_client_emit_connection_established(btstack_packet_handler_t packet_handler, uint8_t status, 125ac726063SMatthias Ringwald bd_addr_t addr, uint16_t avrcp_cid, 126ac726063SMatthias Ringwald uint16_t cover_art_cid) { 127ac726063SMatthias Ringwald uint8_t buffer[20]; 128ac726063SMatthias Ringwald uint16_t size = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &avrcp_cover_art_client_connected, status, addr, avrcp_cid, cover_art_cid); 129ac726063SMatthias Ringwald (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, size); 130ac726063SMatthias Ringwald } 131ac726063SMatthias Ringwald 132ac726063SMatthias Ringwald static void cover_art_client_emit_operation_complete_event(avrcp_cover_art_client_t * cover_art_client, uint8_t status) { 133ac726063SMatthias Ringwald uint8_t buffer[20]; 134ac726063SMatthias Ringwald uint16_t size = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &avrcp_cover_art_client_operation_complete, cover_art_client->cover_art_cid, status); 135ac726063SMatthias Ringwald (*cover_art_client->packet_handler)(HCI_EVENT_PACKET, 0, buffer, size); 136ac726063SMatthias Ringwald } 137ac726063SMatthias Ringwald 138ac726063SMatthias Ringwald static void avrcp_cover_art_client_emit_connection_released(btstack_packet_handler_t packet_handler, uint16_t cover_art_cid) { 139ac726063SMatthias Ringwald uint8_t buffer[20]; 140ac726063SMatthias Ringwald uint16_t size = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &avrcp_cover_art_client_disconnected, cover_art_cid); 141ac726063SMatthias Ringwald (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, size); 142ac726063SMatthias Ringwald } 143ac726063SMatthias Ringwald 144ac726063SMatthias Ringwald static void avrcp_cover_art_finalize_connection(avrcp_cover_art_client_t *cover_art_client) { 145ac726063SMatthias Ringwald btstack_assert(cover_art_client != NULL); 146ac726063SMatthias Ringwald memset(cover_art_client, 0, sizeof(avrcp_cover_art_client_t)); 147ac726063SMatthias Ringwald btstack_linked_list_remove(&avrcp_cover_art_client_connections, (btstack_linked_item_t *) cover_art_client); 148ac726063SMatthias Ringwald } 149ac726063SMatthias Ringwald 150ac726063SMatthias Ringwald static void avrcp_cover_art_client_parser_callback_connect(void * user_data, uint8_t header_id, uint16_t total_len, uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len){ 151ac726063SMatthias Ringwald avrcp_cover_art_client_t * client = (avrcp_cover_art_client_t *) user_data; 152ac726063SMatthias Ringwald switch (header_id){ 153ac726063SMatthias Ringwald case OBEX_HEADER_CONNECTION_ID: 154ac726063SMatthias Ringwald if (obex_parser_header_store(client->obex_header_buffer, sizeof(client->obex_header_buffer), total_len, data_offset, data_buffer, data_len) == OBEX_PARSER_HEADER_COMPLETE){ 155ac726063SMatthias Ringwald goep_client_set_connection_id(client->goep_cid, big_endian_read_32(client->obex_header_buffer, 0)); 156ac726063SMatthias Ringwald } 157ac726063SMatthias Ringwald break; 158ac726063SMatthias Ringwald default: 159ac726063SMatthias Ringwald break; 160ac726063SMatthias Ringwald } 161ac726063SMatthias Ringwald } 162ac726063SMatthias Ringwald 163ac726063SMatthias Ringwald static void avrcp_cover_art_client_prepare_srm_header(avrcp_cover_art_client_t * cover_art_client){ 164ac726063SMatthias Ringwald if (cover_art_client->flow_control_enabled == false){ 165*0de94e45SMatthias Ringwald obex_srm_client_prepare_header(&cover_art_client->obex_srm, cover_art_client->goep_cid); 166ac726063SMatthias Ringwald } 167ac726063SMatthias Ringwald } 168ac726063SMatthias Ringwald 169ac726063SMatthias Ringwald static void avrcp_cover_art_client_parser_callback_get_operation(void * user_data, uint8_t header_id, uint16_t total_len, uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len){ 170ac726063SMatthias Ringwald avrcp_cover_art_client_t *client = (avrcp_cover_art_client_t *) user_data; 171ac726063SMatthias Ringwald switch (header_id) { 172ac726063SMatthias Ringwald case OBEX_HEADER_SINGLE_RESPONSE_MODE: 173ac726063SMatthias Ringwald obex_parser_header_store(&client->obex_srm.srm_value, 1, total_len, data_offset, data_buffer, data_len); 174ac726063SMatthias Ringwald break; 175ac726063SMatthias Ringwald case OBEX_HEADER_SINGLE_RESPONSE_MODE_PARAMETER: 176ac726063SMatthias Ringwald obex_parser_header_store(&client->obex_srm.srmp_value, 1, total_len, data_offset, data_buffer, data_len); 177ac726063SMatthias Ringwald break; 178ac726063SMatthias Ringwald case OBEX_HEADER_BODY: 179ac726063SMatthias Ringwald case OBEX_HEADER_END_OF_BODY: 180ac726063SMatthias Ringwald switch(client->state){ 181ac726063SMatthias Ringwald case AVRCP_COVER_ART_W4_OBJECT: 182ac726063SMatthias Ringwald client->packet_handler(BIP_DATA_PACKET, client->cover_art_cid, (uint8_t *) data_buffer, data_len); 183ac726063SMatthias Ringwald if (data_offset + data_len == total_len){ 184ac726063SMatthias Ringwald client->flow_wait_for_user = true; 185ac726063SMatthias Ringwald } 186ac726063SMatthias Ringwald break; 187ac726063SMatthias Ringwald default: 188ac726063SMatthias Ringwald btstack_unreachable(); 189ac726063SMatthias Ringwald break; 190ac726063SMatthias Ringwald } 191ac726063SMatthias Ringwald break; 192ac726063SMatthias Ringwald default: 193ac726063SMatthias Ringwald // ignore other headers 194ac726063SMatthias Ringwald break; 195ac726063SMatthias Ringwald } 196ac726063SMatthias Ringwald } 197ac726063SMatthias Ringwald 198ac726063SMatthias Ringwald static void avrcp_cover_art_client_prepare_get_operation(avrcp_cover_art_client_t * cover_art_client){ 199ac726063SMatthias Ringwald obex_parser_init_for_response(&cover_art_client->obex_parser, OBEX_OPCODE_GET, avrcp_cover_art_client_parser_callback_get_operation, cover_art_client); 200*0de94e45SMatthias Ringwald obex_srm_client_init(&cover_art_client->obex_srm); 201ac726063SMatthias Ringwald cover_art_client->obex_parser_waiting_for_response = true; 202ac726063SMatthias Ringwald } 203ac726063SMatthias Ringwald 204ac726063SMatthias Ringwald static void avrcp_cover_art_client_handle_can_send_now(avrcp_cover_art_client_t * cover_art_client){ 205ac726063SMatthias Ringwald switch (cover_art_client->state) { 206ac726063SMatthias Ringwald case AVRCP_COVER_ART_W2_SEND_CONNECT_REQUEST: 207ac726063SMatthias Ringwald // prepare request 208ac726063SMatthias Ringwald goep_client_request_create_connect(cover_art_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT); 209ac726063SMatthias Ringwald goep_client_header_add_target(cover_art_client->goep_cid, avrcp_cover_art_uuid, 16); 210ac726063SMatthias Ringwald // state 211ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W4_CONNECT_RESPONSE; 212ac726063SMatthias Ringwald // prepare response 213ac726063SMatthias Ringwald obex_parser_init_for_response(&cover_art_client->obex_parser, OBEX_OPCODE_CONNECT, 214ac726063SMatthias Ringwald avrcp_cover_art_client_parser_callback_connect, cover_art_client); 215*0de94e45SMatthias Ringwald obex_srm_client_init(&cover_art_client->obex_srm); 216ac726063SMatthias Ringwald cover_art_client->obex_parser_waiting_for_response = true; 217ac726063SMatthias Ringwald // send packet 218ac726063SMatthias Ringwald goep_client_execute(cover_art_client->goep_cid); 219ac726063SMatthias Ringwald break; 220ac726063SMatthias Ringwald case AVRCP_COVER_ART_W2_SEND_GET_OBJECT: 221ac726063SMatthias Ringwald goep_client_request_create_get(cover_art_client->goep_cid); 222ac726063SMatthias Ringwald if (cover_art_client->first_request){ 223ac726063SMatthias Ringwald cover_art_client->first_request = false; 224ac726063SMatthias Ringwald avrcp_cover_art_client_prepare_srm_header(cover_art_client); 225ac726063SMatthias Ringwald goep_client_header_add_type(cover_art_client->goep_cid, cover_art_client->object_type); 226ac726063SMatthias Ringwald if (cover_art_client->image_descriptor != NULL){ 2271a3bc516SMatthias Ringwald goep_client_header_add_variable(cover_art_client->goep_cid, OBEX_HEADER_IMG_DESCRIPTOR, (const uint8_t *) cover_art_client->image_descriptor, (uint16_t) strlen(cover_art_client->image_descriptor)); 228ac726063SMatthias Ringwald } 2291a3bc516SMatthias Ringwald uint8_t image_handle_len = btstack_max(7, (uint16_t) strlen(cover_art_client->image_handle)); 230ac726063SMatthias Ringwald goep_client_header_add_unicode_prefix(cover_art_client->goep_cid, OBEX_HEADER_IMG_HANDLE, cover_art_client->image_handle, image_handle_len); 231ac726063SMatthias Ringwald } 232ac726063SMatthias Ringwald // state 233ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W4_OBJECT; 234ac726063SMatthias Ringwald cover_art_client->flow_next_triggered = 0; 235ac726063SMatthias Ringwald cover_art_client->flow_wait_for_user = 0; 236ac726063SMatthias Ringwald // prepare response 237ac726063SMatthias Ringwald avrcp_cover_art_client_prepare_get_operation(cover_art_client); 238ac726063SMatthias Ringwald // send packet 239ac726063SMatthias Ringwald goep_client_execute(cover_art_client->goep_cid); 240ac726063SMatthias Ringwald break; 241ac726063SMatthias Ringwald case AVRCP_COVER_ART_W2_SEND_DISCONNECT_REQUEST: 242ac726063SMatthias Ringwald // prepare request 243ac726063SMatthias Ringwald goep_client_request_create_disconnect(cover_art_client->goep_cid); 244ac726063SMatthias Ringwald // state 245ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W4_DISCONNECT_RESPONSE; 246ac726063SMatthias Ringwald // prepare response 247ac726063SMatthias Ringwald obex_parser_init_for_response(&cover_art_client->obex_parser, OBEX_OPCODE_DISCONNECT, NULL, cover_art_client); 248ac726063SMatthias Ringwald cover_art_client->obex_parser_waiting_for_response = true; 249ac726063SMatthias Ringwald // send packet 250ac726063SMatthias Ringwald goep_client_execute(cover_art_client->goep_cid); 251ac726063SMatthias Ringwald return; 252ac726063SMatthias Ringwald default: 253ac726063SMatthias Ringwald break; 254ac726063SMatthias Ringwald } 255ac726063SMatthias Ringwald } 256ac726063SMatthias Ringwald 257ac726063SMatthias Ringwald static void avrcp_cover_art_goep_event_handler(const uint8_t *packet, uint16_t size) { 258ac726063SMatthias Ringwald UNUSED(size); 259ac726063SMatthias Ringwald uint8_t status; 260ac726063SMatthias Ringwald avrcp_cover_art_client_t * cover_art_client; 261ac726063SMatthias Ringwald btstack_packet_handler_t packet_handler; 262ac726063SMatthias Ringwald uint16_t cover_art_cid; 263ac726063SMatthias Ringwald 264ac726063SMatthias Ringwald switch (hci_event_packet_get_type(packet)) { 265ac726063SMatthias Ringwald case HCI_EVENT_GOEP_META: 266ac726063SMatthias Ringwald switch (hci_event_goep_meta_get_subevent_code(packet)){ 267ac726063SMatthias Ringwald case GOEP_SUBEVENT_CONNECTION_OPENED: 268ac726063SMatthias Ringwald cover_art_client = avrcp_cover_art_client_for_goep_cid(goep_subevent_connection_opened_get_goep_cid(packet)); 269ac726063SMatthias Ringwald btstack_assert(cover_art_client != NULL); 270ac726063SMatthias Ringwald status = goep_subevent_connection_opened_get_status(packet); 271ac726063SMatthias Ringwald if (status){ 272ac726063SMatthias Ringwald log_info("connection failed %u", status); 273ac726063SMatthias Ringwald avrcp_cover_art_finalize_connection(cover_art_client); 274ac726063SMatthias Ringwald avrcp_cover_art_client_emit_connection_established(cover_art_client->packet_handler, status, 275ac726063SMatthias Ringwald cover_art_client->addr, 276ac726063SMatthias Ringwald cover_art_client->avrcp_cid, 277ac726063SMatthias Ringwald cover_art_client->cover_art_cid); 278ac726063SMatthias Ringwald } else { 279ac726063SMatthias Ringwald log_info("connection established"); 280ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W2_SEND_CONNECT_REQUEST; 281ac726063SMatthias Ringwald goep_client_request_can_send_now(cover_art_client->goep_cid); 282ac726063SMatthias Ringwald } 283ac726063SMatthias Ringwald break; 284ac726063SMatthias Ringwald case GOEP_SUBEVENT_CONNECTION_CLOSED: 285ac726063SMatthias Ringwald cover_art_client = avrcp_cover_art_client_for_goep_cid(goep_subevent_connection_opened_get_goep_cid(packet)); 286ac726063SMatthias Ringwald btstack_assert(cover_art_client != NULL); 287ac726063SMatthias Ringwald if (cover_art_client->state > AVRCP_COVER_ART_CONNECTED){ 288ac726063SMatthias Ringwald cover_art_client_emit_operation_complete_event(cover_art_client, OBEX_DISCONNECTED); 289ac726063SMatthias Ringwald } 290ac726063SMatthias Ringwald cover_art_cid = cover_art_client->cover_art_cid; 291ac726063SMatthias Ringwald packet_handler = cover_art_client->packet_handler; 292ac726063SMatthias Ringwald avrcp_cover_art_finalize_connection(cover_art_client); 293ac726063SMatthias Ringwald avrcp_cover_art_client_emit_connection_released(packet_handler, cover_art_cid); 294ac726063SMatthias Ringwald break; 295ac726063SMatthias Ringwald case GOEP_SUBEVENT_CAN_SEND_NOW: 296ac726063SMatthias Ringwald cover_art_client = avrcp_cover_art_client_for_goep_cid(goep_subevent_can_send_now_get_goep_cid(packet)); 297ac726063SMatthias Ringwald btstack_assert(cover_art_client != NULL); 298ac726063SMatthias Ringwald avrcp_cover_art_client_handle_can_send_now(cover_art_client); 299ac726063SMatthias Ringwald break; 300ac726063SMatthias Ringwald default: 301ac726063SMatthias Ringwald break; 302ac726063SMatthias Ringwald } 303ac726063SMatthias Ringwald break; 304ac726063SMatthias Ringwald default: 305ac726063SMatthias Ringwald break; 306ac726063SMatthias Ringwald } 307ac726063SMatthias Ringwald } 308ac726063SMatthias Ringwald 309ac726063SMatthias Ringwald static void avrcp_cover_art_client_goep_data_handler(avrcp_cover_art_client_t * cover_art_client, uint8_t *packet, uint16_t size){ 310ac726063SMatthias Ringwald btstack_assert(cover_art_client->obex_parser_waiting_for_response); 311ac726063SMatthias Ringwald 312ac726063SMatthias Ringwald obex_parser_object_state_t parser_state; 313ac726063SMatthias Ringwald parser_state = obex_parser_process_data(&cover_art_client->obex_parser, packet, size); 314ac726063SMatthias Ringwald if (parser_state == OBEX_PARSER_OBJECT_STATE_COMPLETE){ 315ac726063SMatthias Ringwald cover_art_client->obex_parser_waiting_for_response = false; 316ac726063SMatthias Ringwald obex_parser_operation_info_t op_info; 317ac726063SMatthias Ringwald obex_parser_get_operation_info(&cover_art_client->obex_parser, &op_info); 318ac726063SMatthias Ringwald switch (cover_art_client->state){ 319ac726063SMatthias Ringwald case AVRCP_COVER_ART_W4_CONNECT_RESPONSE: 320ac726063SMatthias Ringwald switch (op_info.response_code) { 321ac726063SMatthias Ringwald case OBEX_RESP_SUCCESS: 322ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_CONNECTED; 323ac726063SMatthias Ringwald avrcp_cover_art_client_emit_connection_established(cover_art_client->packet_handler, 324ac726063SMatthias Ringwald ERROR_CODE_SUCCESS, 325ac726063SMatthias Ringwald cover_art_client->addr, 326ac726063SMatthias Ringwald cover_art_client->avrcp_cid, 327ac726063SMatthias Ringwald cover_art_client->cover_art_cid); 328ac726063SMatthias Ringwald break; 329ac726063SMatthias Ringwald default: 330ac726063SMatthias Ringwald log_info("pbap: obex connect failed, result 0x%02x", packet[0]); 331ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_INIT; 332ac726063SMatthias Ringwald avrcp_cover_art_client_emit_connection_established(cover_art_client->packet_handler, 333ac726063SMatthias Ringwald OBEX_CONNECT_FAILED, 334ac726063SMatthias Ringwald cover_art_client->addr, 335ac726063SMatthias Ringwald cover_art_client->avrcp_cid, 336ac726063SMatthias Ringwald cover_art_client->cover_art_cid); 337ac726063SMatthias Ringwald break; 338ac726063SMatthias Ringwald } 339ac726063SMatthias Ringwald break; 340ac726063SMatthias Ringwald case AVRCP_COVER_ART_W4_OBJECT: 341ac726063SMatthias Ringwald switch (op_info.response_code) { 342ac726063SMatthias Ringwald case OBEX_RESP_CONTINUE: 343*0de94e45SMatthias Ringwald obex_srm_client_handle_headers(&cover_art_client->obex_srm); 344*0de94e45SMatthias Ringwald if (cover_art_client->obex_srm.srm_state == OBEX_SRM_CLIENT_STATE_ENABLED) { 345ac726063SMatthias Ringwald // prepare response 346ac726063SMatthias Ringwald avrcp_cover_art_client_prepare_get_operation(cover_art_client); 347ac726063SMatthias Ringwald break; 348ac726063SMatthias Ringwald } 349ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W2_SEND_GET_OBJECT; 350ac726063SMatthias Ringwald if (!cover_art_client->flow_control_enabled || !cover_art_client->flow_wait_for_user || 351ac726063SMatthias Ringwald cover_art_client->flow_next_triggered) { 352ac726063SMatthias Ringwald goep_client_request_can_send_now(cover_art_client->goep_cid); 353ac726063SMatthias Ringwald } 354ac726063SMatthias Ringwald break; 355ac726063SMatthias Ringwald case OBEX_RESP_SUCCESS: 356ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_CONNECTED; 357ac726063SMatthias Ringwald cover_art_client_emit_operation_complete_event(cover_art_client, ERROR_CODE_SUCCESS); 358ac726063SMatthias Ringwald break; 359ac726063SMatthias Ringwald default: 360ac726063SMatthias Ringwald log_info("unexpected response 0x%02x", packet[0]); 361ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_CONNECTED; 362ac726063SMatthias Ringwald cover_art_client_emit_operation_complete_event(cover_art_client, OBEX_UNKNOWN_ERROR); 363ac726063SMatthias Ringwald break; 364ac726063SMatthias Ringwald } 365ac726063SMatthias Ringwald break; 366ac726063SMatthias Ringwald case AVRCP_COVER_ART_W4_DISCONNECT_RESPONSE: 367ac726063SMatthias Ringwald goep_client_disconnect(cover_art_client->goep_cid); 368ac726063SMatthias Ringwald break; 369ac726063SMatthias Ringwald default: 370ac726063SMatthias Ringwald btstack_unreachable(); 371ac726063SMatthias Ringwald break; 372ac726063SMatthias Ringwald } 373ac726063SMatthias Ringwald } 374ac726063SMatthias Ringwald } 375ac726063SMatthias Ringwald 376ac726063SMatthias Ringwald static void avrcp_cover_art_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 377ac726063SMatthias Ringwald UNUSED(channel); // ok: there is no channel 378ac726063SMatthias Ringwald UNUSED(size); // ok: handling own goep events 379ac726063SMatthias Ringwald avrcp_cover_art_client_t * cover_art_client; 380ac726063SMatthias Ringwald switch (packet_type){ 381ac726063SMatthias Ringwald case HCI_EVENT_PACKET: 382ac726063SMatthias Ringwald avrcp_cover_art_goep_event_handler(packet, size); 383ac726063SMatthias Ringwald break; 384ac726063SMatthias Ringwald case GOEP_DATA_PACKET: 385ac726063SMatthias Ringwald cover_art_client = avrcp_cover_art_client_for_goep_cid(channel); 386ac726063SMatthias Ringwald btstack_assert(cover_art_client != NULL); 387ac726063SMatthias Ringwald avrcp_cover_art_client_goep_data_handler(cover_art_client, packet, size); 388ac726063SMatthias Ringwald break; 389ac726063SMatthias Ringwald default: 390ac726063SMatthias Ringwald break; 391ac726063SMatthias Ringwald } 392ac726063SMatthias Ringwald } 393ac726063SMatthias Ringwald 394ac726063SMatthias Ringwald static uint8_t avrcp_cover_art_client_setup_connection(avrcp_cover_art_client_t * cover_art_client, uint16_t l2cap_psm){ 395ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W4_GOEP_CONNECTION; 396ac726063SMatthias Ringwald return goep_client_connect_l2cap(&cover_art_client->goep_client, 397ac726063SMatthias Ringwald (l2cap_ertm_config_t *) cover_art_client->ertm_config, 398ac726063SMatthias Ringwald cover_art_client->ertm_buffer, 399ac726063SMatthias Ringwald cover_art_client->ertm_buffer_size, 400ac726063SMatthias Ringwald &avrcp_cover_art_packet_handler, 401ac726063SMatthias Ringwald cover_art_client->addr, 402ac726063SMatthias Ringwald l2cap_psm, 403ac726063SMatthias Ringwald &cover_art_client->goep_cid); 404ac726063SMatthias Ringwald } 405ac726063SMatthias Ringwald 406ac726063SMatthias Ringwald static void avrcp_cover_art_handle_sdp_query_complete(avrcp_connection_t * connection, uint8_t status){ 407ac726063SMatthias Ringwald avrcp_cover_art_client_t * cover_art_client = avrcp_cover_art_client_for_avrcp_cid(connection->avrcp_cid); 408ac726063SMatthias Ringwald 409ac726063SMatthias Ringwald if (cover_art_client == NULL) { 410ac726063SMatthias Ringwald return; 411ac726063SMatthias Ringwald } 412ac726063SMatthias Ringwald if (cover_art_client->state != AVRCP_COVER_ART_W4_SDP_QUERY_COMPLETE){ 413ac726063SMatthias Ringwald return; 414ac726063SMatthias Ringwald } 415ac726063SMatthias Ringwald 416ac726063SMatthias Ringwald // l2cap available? 417ac726063SMatthias Ringwald if (status == ERROR_CODE_SUCCESS){ 418ac726063SMatthias Ringwald if (connection->cover_art_psm == 0){ 419ac726063SMatthias Ringwald status = SDP_SERVICE_NOT_FOUND; 420ac726063SMatthias Ringwald } 421ac726063SMatthias Ringwald } 422ac726063SMatthias Ringwald 423ac726063SMatthias Ringwald if (status == ERROR_CODE_SUCCESS) { 424ac726063SMatthias Ringwald // ready to connect 425ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W2_GOEP_CONNECT; 42665444753SMatthias Ringwald status = avrcp_cover_art_client_setup_connection(cover_art_client, connection->cover_art_psm); 42765444753SMatthias Ringwald } 42865444753SMatthias Ringwald 42965444753SMatthias Ringwald if (status != ERROR_CODE_SUCCESS){ 430ac726063SMatthias Ringwald btstack_packet_handler_t packet_handler = cover_art_client->packet_handler; 431ac726063SMatthias Ringwald uint16_t cover_art_cid = cover_art_client->cover_art_cid; 432ac726063SMatthias Ringwald uint16_t avrcp_cid = cover_art_client->avrcp_cid; 433ac726063SMatthias Ringwald avrcp_cover_art_finalize_connection(cover_art_client); 434ac726063SMatthias Ringwald avrcp_cover_art_client_emit_connection_established(packet_handler, status, connection->remote_addr, 435ac726063SMatthias Ringwald avrcp_cid, cover_art_cid); 436ac726063SMatthias Ringwald } 437ac726063SMatthias Ringwald } 438ac726063SMatthias Ringwald 439ac726063SMatthias Ringwald void avrcp_cover_art_client_init(void){ 440ac726063SMatthias Ringwald avrcp_register_cover_art_sdp_query_complete_handler(&avrcp_cover_art_handle_sdp_query_complete); 441ac726063SMatthias Ringwald } 442ac726063SMatthias Ringwald 443ac726063SMatthias Ringwald uint8_t 444ac726063SMatthias Ringwald avrcp_cover_art_client_connect(avrcp_cover_art_client_t *cover_art_client, btstack_packet_handler_t packet_handler, 445ac726063SMatthias Ringwald bd_addr_t remote_addr, uint8_t *ertm_buffer, uint32_t ertm_buffer_size, 446ac726063SMatthias Ringwald const l2cap_ertm_config_t *ertm_config, uint16_t *avrcp_cover_art_cid) { 447ac726063SMatthias Ringwald 448ac726063SMatthias Ringwald avrcp_connection_t * connection_controller = avrcp_get_connection_for_bd_addr_for_role(AVRCP_CONTROLLER, remote_addr); 449ac726063SMatthias Ringwald avrcp_connection_t * connection_target = avrcp_get_connection_for_bd_addr_for_role(AVRCP_TARGET, remote_addr); 450ac726063SMatthias Ringwald if ((connection_target == NULL) || (connection_controller == NULL)){ 451ac726063SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 452ac726063SMatthias Ringwald } 453ac726063SMatthias Ringwald 454ac726063SMatthias Ringwald cover_art_client->cover_art_cid = avrcp_cover_art_client_next_cid(); 455ac726063SMatthias Ringwald memcpy(cover_art_client->addr, remote_addr, 6); 456ac726063SMatthias Ringwald cover_art_client->avrcp_cid = connection_controller->avrcp_cid; 457ac726063SMatthias Ringwald cover_art_client->packet_handler = packet_handler; 458ac726063SMatthias Ringwald cover_art_client->flow_control_enabled = false; 459ac726063SMatthias Ringwald 460ac726063SMatthias Ringwald // store ERTM config 461ac726063SMatthias Ringwald cover_art_client->ertm_config = ertm_config; 462ac726063SMatthias Ringwald cover_art_client->ertm_buffer = ertm_buffer; 463ac726063SMatthias Ringwald cover_art_client->ertm_buffer_size = ertm_buffer_size; 464ac726063SMatthias Ringwald 465ac726063SMatthias Ringwald if (avrcp_cover_art_cid != NULL){ 466ac726063SMatthias Ringwald *avrcp_cover_art_cid = cover_art_client->cover_art_cid; 467ac726063SMatthias Ringwald } 468ac726063SMatthias Ringwald 469ac726063SMatthias Ringwald btstack_linked_list_add(&avrcp_cover_art_client_connections, (btstack_linked_item_t *) cover_art_client); 470ac726063SMatthias Ringwald 471ac726063SMatthias Ringwald if (connection_controller->cover_art_psm == 0){ 472ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W4_SDP_QUERY_COMPLETE; 473ac726063SMatthias Ringwald avrcp_trigger_sdp_query(connection_controller, connection_controller); 474ac726063SMatthias Ringwald return ERROR_CODE_SUCCESS; 475ac726063SMatthias Ringwald } else { 476ac726063SMatthias Ringwald return avrcp_cover_art_client_setup_connection(cover_art_client, connection_controller->cover_art_psm); 477ac726063SMatthias Ringwald } 478ac726063SMatthias Ringwald } 479ac726063SMatthias Ringwald 480ac726063SMatthias Ringwald static uint8_t avrcp_cover_art_client_get_object(uint16_t avrcp_cover_art_cid, const char * object_type, const char * image_handle, const char * image_descriptor){ 481ac726063SMatthias Ringwald avrcp_cover_art_client_t * cover_art_client = avrcp_cover_art_client_for_cover_art_cid(avrcp_cover_art_cid); 482ac726063SMatthias Ringwald if (cover_art_client == NULL){ 483ac726063SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 484ac726063SMatthias Ringwald } 485ac726063SMatthias Ringwald if (cover_art_client->state != AVRCP_COVER_ART_CONNECTED){ 486ac726063SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 487ac726063SMatthias Ringwald } 488ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W2_SEND_GET_OBJECT; 489ac726063SMatthias Ringwald cover_art_client->first_request = true; 490ac726063SMatthias Ringwald cover_art_client->image_handle = image_handle; 491ac726063SMatthias Ringwald cover_art_client->image_descriptor = image_descriptor; 492ac726063SMatthias Ringwald cover_art_client->object_type = object_type; 493ac726063SMatthias Ringwald goep_client_request_can_send_now(cover_art_client->goep_cid); 494ac726063SMatthias Ringwald return ERROR_CODE_SUCCESS; 495ac726063SMatthias Ringwald } 496ac726063SMatthias Ringwald 497ac726063SMatthias Ringwald uint8_t avrcp_cover_art_client_get_linked_thumbnail(uint16_t avrcp_cover_art_cid, const char * image_handle){ 498ac726063SMatthias Ringwald return avrcp_cover_art_client_get_object(avrcp_cover_art_cid, 499ac726063SMatthias Ringwald avrcp_cover_art_linked_thumbnail_type, 500ac726063SMatthias Ringwald image_handle, 501ac726063SMatthias Ringwald NULL); 502ac726063SMatthias Ringwald } 503ac726063SMatthias Ringwald 504ac726063SMatthias Ringwald uint8_t avrcp_cover_art_client_get_image(uint16_t avrcp_cover_art_cid, const char * image_handle, const char * image_descriptor){ 505ac726063SMatthias Ringwald return avrcp_cover_art_client_get_object(avrcp_cover_art_cid, 506ac726063SMatthias Ringwald avrcp_cover_art_image_type, 507ac726063SMatthias Ringwald image_handle, 508ac726063SMatthias Ringwald image_descriptor); 509ac726063SMatthias Ringwald } 510ac726063SMatthias Ringwald 511ac726063SMatthias Ringwald uint8_t avrcp_cover_art_client_get_image_properties(uint16_t avrcp_cover_art_cid, const char * image_handle){ 512ac726063SMatthias Ringwald return avrcp_cover_art_client_get_object(avrcp_cover_art_cid, 513ac726063SMatthias Ringwald avrcp_cover_art_image_properties_type, 514ac726063SMatthias Ringwald image_handle, 515ac726063SMatthias Ringwald NULL); 516ac726063SMatthias Ringwald } 517ac726063SMatthias Ringwald 518ac726063SMatthias Ringwald uint8_t avrcp_cover_art_client_disconnect(uint16_t avrcp_cover_art_cid){ 519ac726063SMatthias Ringwald avrcp_cover_art_client_t * cover_art_client = avrcp_cover_art_client_for_cover_art_cid(avrcp_cover_art_cid); 520ac726063SMatthias Ringwald if (cover_art_client == NULL){ 521ac726063SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 522ac726063SMatthias Ringwald } 523ac726063SMatthias Ringwald if (cover_art_client->state < AVRCP_COVER_ART_CONNECTED){ 524ac726063SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 525ac726063SMatthias Ringwald } 526ac726063SMatthias Ringwald cover_art_client->state = AVRCP_COVER_ART_W2_SEND_DISCONNECT_REQUEST; 527ac726063SMatthias Ringwald goep_client_request_can_send_now(cover_art_client->goep_cid); 528ac726063SMatthias Ringwald return ERROR_CODE_SUCCESS; 529ac726063SMatthias Ringwald } 530ac726063SMatthias Ringwald 531ac726063SMatthias Ringwald void avrcp_cover_art_client_deinit(void){ 532ac726063SMatthias Ringwald avrcp_cover_art_client_connections = NULL; 533ac726063SMatthias Ringwald } 534ac726063SMatthias Ringwald 535ac726063SMatthias Ringwald #endif /* ENABLE_AVRCP_COVER_ART */ 536