xref: /btstack/src/classic/avrcp_cover_art_client.c (revision 0de94e451fd1a95bcfd60064ee88fab821b78fa4)
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