xref: /btstack/src/classic/avrcp_cover_art_client.c (revision 57ff2ef16c24ff0caf592e9a8647bd13a29aed23)
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 
79*57ff2ef1SMatthias 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){
165ac726063SMatthias Ringwald         goep_client_header_add_srm_enable(cover_art_client->goep_cid);
166ac726063SMatthias Ringwald         cover_art_client->srm_state = SRM_W4_CONFIRM;
167ac726063SMatthias Ringwald     }
168ac726063SMatthias Ringwald }
169ac726063SMatthias Ringwald 
170ac726063SMatthias Ringwald static void obex_srm_init(avrcp_cover_art_obex_srm_t * obex_srm){
171ac726063SMatthias Ringwald     obex_srm->srm_value = OBEX_SRM_DISABLE;
172ac726063SMatthias Ringwald     obex_srm->srmp_value = OBEX_SRMP_NEXT;
173ac726063SMatthias Ringwald }
174ac726063SMatthias Ringwald 
175ac726063SMatthias Ringwald static void avrcp_cover_art_client_handle_srm_headers(avrcp_cover_art_client_t *context) {
176ac726063SMatthias Ringwald     const avrcp_cover_art_obex_srm_t * obex_srm = &context->obex_srm;
177ac726063SMatthias Ringwald     // Update SRM state based on SRM headers
178ac726063SMatthias Ringwald     switch (context->srm_state){
179ac726063SMatthias Ringwald         case SRM_W4_CONFIRM:
180ac726063SMatthias Ringwald             switch (obex_srm->srm_value){
181ac726063SMatthias Ringwald                 case OBEX_SRM_ENABLE:
182ac726063SMatthias Ringwald                     switch (obex_srm->srmp_value){
183ac726063SMatthias Ringwald                         case OBEX_SRMP_WAIT:
184ac726063SMatthias Ringwald                             context->srm_state = SRM_ENABLED_BUT_WAITING;
185ac726063SMatthias Ringwald                             break;
186ac726063SMatthias Ringwald                         default:
187ac726063SMatthias Ringwald                             context->srm_state = SRM_ENABLED;
188ac726063SMatthias Ringwald                             break;
189ac726063SMatthias Ringwald                     }
190ac726063SMatthias Ringwald                     break;
191ac726063SMatthias Ringwald                 default:
192ac726063SMatthias Ringwald                     context->srm_state = SRM_DISABLED;
193ac726063SMatthias Ringwald                     break;
194ac726063SMatthias Ringwald             }
195ac726063SMatthias Ringwald             break;
196ac726063SMatthias Ringwald         case SRM_ENABLED_BUT_WAITING:
197ac726063SMatthias Ringwald             switch (obex_srm->srmp_value){
198ac726063SMatthias Ringwald                 case OBEX_SRMP_WAIT:
199ac726063SMatthias Ringwald                     context->srm_state = SRM_ENABLED_BUT_WAITING;
200ac726063SMatthias Ringwald                     break;
201ac726063SMatthias Ringwald                 default:
202ac726063SMatthias Ringwald                     context->srm_state = SRM_ENABLED;
203ac726063SMatthias Ringwald                     break;
204ac726063SMatthias Ringwald             }
205ac726063SMatthias Ringwald             break;
206ac726063SMatthias Ringwald         default:
207ac726063SMatthias Ringwald             break;
208ac726063SMatthias Ringwald     }
209ac726063SMatthias Ringwald     log_info("SRM state %u", context->srm_state);
210ac726063SMatthias Ringwald }
211ac726063SMatthias Ringwald 
212ac726063SMatthias 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){
213ac726063SMatthias Ringwald     avrcp_cover_art_client_t *client = (avrcp_cover_art_client_t *) user_data;
214ac726063SMatthias Ringwald     switch (header_id) {
215ac726063SMatthias Ringwald         case OBEX_HEADER_SINGLE_RESPONSE_MODE:
216ac726063SMatthias Ringwald             obex_parser_header_store(&client->obex_srm.srm_value, 1, total_len, data_offset, data_buffer, data_len);
217ac726063SMatthias Ringwald             break;
218ac726063SMatthias Ringwald         case OBEX_HEADER_SINGLE_RESPONSE_MODE_PARAMETER:
219ac726063SMatthias Ringwald             obex_parser_header_store(&client->obex_srm.srmp_value, 1, total_len, data_offset, data_buffer, data_len);
220ac726063SMatthias Ringwald             break;
221ac726063SMatthias Ringwald         case OBEX_HEADER_BODY:
222ac726063SMatthias Ringwald         case OBEX_HEADER_END_OF_BODY:
223ac726063SMatthias Ringwald             switch(client->state){
224ac726063SMatthias Ringwald                 case AVRCP_COVER_ART_W4_OBJECT:
225ac726063SMatthias Ringwald                     client->packet_handler(BIP_DATA_PACKET, client->cover_art_cid, (uint8_t *) data_buffer, data_len);
226ac726063SMatthias Ringwald                     if (data_offset + data_len == total_len){
227ac726063SMatthias Ringwald                         client->flow_wait_for_user = true;
228ac726063SMatthias Ringwald                     }
229ac726063SMatthias Ringwald                     break;
230ac726063SMatthias Ringwald                 default:
231ac726063SMatthias Ringwald                     btstack_unreachable();
232ac726063SMatthias Ringwald                     break;
233ac726063SMatthias Ringwald             }
234ac726063SMatthias Ringwald             break;
235ac726063SMatthias Ringwald         default:
236ac726063SMatthias Ringwald             // ignore other headers
237ac726063SMatthias Ringwald             break;
238ac726063SMatthias Ringwald     }
239ac726063SMatthias Ringwald }
240ac726063SMatthias Ringwald 
241ac726063SMatthias Ringwald static void avrcp_cover_art_client_prepare_get_operation(avrcp_cover_art_client_t * cover_art_client){
242ac726063SMatthias 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);
243ac726063SMatthias Ringwald     obex_srm_init(&cover_art_client->obex_srm);
244ac726063SMatthias Ringwald     cover_art_client->obex_parser_waiting_for_response = true;
245ac726063SMatthias Ringwald }
246ac726063SMatthias Ringwald 
247ac726063SMatthias Ringwald static void avrcp_cover_art_client_handle_can_send_now(avrcp_cover_art_client_t * cover_art_client){
248ac726063SMatthias Ringwald     switch (cover_art_client->state) {
249ac726063SMatthias Ringwald         case AVRCP_COVER_ART_W2_SEND_CONNECT_REQUEST:
250ac726063SMatthias Ringwald             // prepare request
251ac726063SMatthias Ringwald             goep_client_request_create_connect(cover_art_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT);
252ac726063SMatthias Ringwald             goep_client_header_add_target(cover_art_client->goep_cid, avrcp_cover_art_uuid, 16);
253ac726063SMatthias Ringwald             // state
254ac726063SMatthias Ringwald             cover_art_client->state = AVRCP_COVER_ART_W4_CONNECT_RESPONSE;
255ac726063SMatthias Ringwald             // prepare response
256ac726063SMatthias Ringwald             obex_parser_init_for_response(&cover_art_client->obex_parser, OBEX_OPCODE_CONNECT,
257ac726063SMatthias Ringwald                                           avrcp_cover_art_client_parser_callback_connect, cover_art_client);
258ac726063SMatthias Ringwald             obex_srm_init(&cover_art_client->obex_srm);
259ac726063SMatthias Ringwald             cover_art_client->obex_parser_waiting_for_response = true;
260ac726063SMatthias Ringwald             // send packet
261ac726063SMatthias Ringwald             goep_client_execute(cover_art_client->goep_cid);
262ac726063SMatthias Ringwald             break;
263ac726063SMatthias Ringwald         case AVRCP_COVER_ART_W2_SEND_GET_OBJECT:
264ac726063SMatthias Ringwald             goep_client_request_create_get(cover_art_client->goep_cid);
265ac726063SMatthias Ringwald             if (cover_art_client->first_request){
266ac726063SMatthias Ringwald                 cover_art_client->first_request = false;
267ac726063SMatthias Ringwald                 avrcp_cover_art_client_prepare_srm_header(cover_art_client);
268ac726063SMatthias Ringwald                 goep_client_header_add_type(cover_art_client->goep_cid, cover_art_client->object_type);
269ac726063SMatthias Ringwald                 if (cover_art_client->image_descriptor != NULL){
270ac726063SMatthias Ringwald                     goep_client_header_add_variable(cover_art_client->goep_cid, OBEX_HEADER_IMG_DESCRIPTOR, (const uint8_t *) cover_art_client->image_descriptor, strlen(cover_art_client->image_descriptor));
271ac726063SMatthias Ringwald                 }
272ac726063SMatthias Ringwald                 uint8_t image_handle_len = btstack_max(7, strlen(cover_art_client->image_handle));
273ac726063SMatthias Ringwald                 goep_client_header_add_unicode_prefix(cover_art_client->goep_cid, OBEX_HEADER_IMG_HANDLE, cover_art_client->image_handle, image_handle_len);
274ac726063SMatthias Ringwald             }
275ac726063SMatthias Ringwald             // state
276ac726063SMatthias Ringwald             cover_art_client->state = AVRCP_COVER_ART_W4_OBJECT;
277ac726063SMatthias Ringwald             cover_art_client->flow_next_triggered = 0;
278ac726063SMatthias Ringwald             cover_art_client->flow_wait_for_user = 0;
279ac726063SMatthias Ringwald             // prepare response
280ac726063SMatthias Ringwald             avrcp_cover_art_client_prepare_get_operation(cover_art_client);
281ac726063SMatthias Ringwald             // send packet
282ac726063SMatthias Ringwald             goep_client_execute(cover_art_client->goep_cid);
283ac726063SMatthias Ringwald             break;
284ac726063SMatthias Ringwald         case AVRCP_COVER_ART_W2_SEND_DISCONNECT_REQUEST:
285ac726063SMatthias Ringwald             // prepare request
286ac726063SMatthias Ringwald             goep_client_request_create_disconnect(cover_art_client->goep_cid);
287ac726063SMatthias Ringwald             // state
288ac726063SMatthias Ringwald             cover_art_client->state = AVRCP_COVER_ART_W4_DISCONNECT_RESPONSE;
289ac726063SMatthias Ringwald             // prepare response
290ac726063SMatthias Ringwald             obex_parser_init_for_response(&cover_art_client->obex_parser, OBEX_OPCODE_DISCONNECT, NULL, cover_art_client);
291ac726063SMatthias Ringwald             cover_art_client->obex_parser_waiting_for_response = true;
292ac726063SMatthias Ringwald             // send packet
293ac726063SMatthias Ringwald             goep_client_execute(cover_art_client->goep_cid);
294ac726063SMatthias Ringwald             return;
295ac726063SMatthias Ringwald         default:
296ac726063SMatthias Ringwald             break;
297ac726063SMatthias Ringwald     }
298ac726063SMatthias Ringwald }
299ac726063SMatthias Ringwald 
300ac726063SMatthias Ringwald static void avrcp_cover_art_goep_event_handler(const uint8_t *packet, uint16_t size) {
301ac726063SMatthias Ringwald     UNUSED(size);
302ac726063SMatthias Ringwald     uint8_t status;
303ac726063SMatthias Ringwald     avrcp_cover_art_client_t * cover_art_client;
304ac726063SMatthias Ringwald     btstack_packet_handler_t packet_handler;
305ac726063SMatthias Ringwald     uint16_t cover_art_cid;
306ac726063SMatthias Ringwald 
307ac726063SMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
308ac726063SMatthias Ringwald         case HCI_EVENT_GOEP_META:
309ac726063SMatthias Ringwald             switch (hci_event_goep_meta_get_subevent_code(packet)){
310ac726063SMatthias Ringwald                 case GOEP_SUBEVENT_CONNECTION_OPENED:
311ac726063SMatthias Ringwald                     cover_art_client = avrcp_cover_art_client_for_goep_cid(goep_subevent_connection_opened_get_goep_cid(packet));
312ac726063SMatthias Ringwald                     btstack_assert(cover_art_client != NULL);
313ac726063SMatthias Ringwald                     status = goep_subevent_connection_opened_get_status(packet);
314ac726063SMatthias Ringwald                     if (status){
315ac726063SMatthias Ringwald                         log_info("connection failed %u", status);
316ac726063SMatthias Ringwald                         avrcp_cover_art_finalize_connection(cover_art_client);
317ac726063SMatthias Ringwald                         avrcp_cover_art_client_emit_connection_established(cover_art_client->packet_handler, status,
318ac726063SMatthias Ringwald                                                                            cover_art_client->addr,
319ac726063SMatthias Ringwald                                                                            cover_art_client->avrcp_cid,
320ac726063SMatthias Ringwald                                                                            cover_art_client->cover_art_cid);
321ac726063SMatthias Ringwald                     } else {
322ac726063SMatthias Ringwald                         log_info("connection established");
323ac726063SMatthias Ringwald                         cover_art_client->state = AVRCP_COVER_ART_W2_SEND_CONNECT_REQUEST;
324ac726063SMatthias Ringwald                         goep_client_request_can_send_now(cover_art_client->goep_cid);
325ac726063SMatthias Ringwald                     }
326ac726063SMatthias Ringwald                     break;
327ac726063SMatthias Ringwald                 case GOEP_SUBEVENT_CONNECTION_CLOSED:
328ac726063SMatthias Ringwald                     cover_art_client = avrcp_cover_art_client_for_goep_cid(goep_subevent_connection_opened_get_goep_cid(packet));
329ac726063SMatthias Ringwald                     btstack_assert(cover_art_client != NULL);
330ac726063SMatthias Ringwald                     if (cover_art_client->state > AVRCP_COVER_ART_CONNECTED){
331ac726063SMatthias Ringwald                         cover_art_client_emit_operation_complete_event(cover_art_client, OBEX_DISCONNECTED);
332ac726063SMatthias Ringwald                     }
333ac726063SMatthias Ringwald                     cover_art_cid = cover_art_client->cover_art_cid;
334ac726063SMatthias Ringwald                     packet_handler = cover_art_client->packet_handler;
335ac726063SMatthias Ringwald                     avrcp_cover_art_finalize_connection(cover_art_client);
336ac726063SMatthias Ringwald                     avrcp_cover_art_client_emit_connection_released(packet_handler, cover_art_cid);
337ac726063SMatthias Ringwald                     break;
338ac726063SMatthias Ringwald                 case GOEP_SUBEVENT_CAN_SEND_NOW:
339ac726063SMatthias Ringwald                     cover_art_client = avrcp_cover_art_client_for_goep_cid(goep_subevent_can_send_now_get_goep_cid(packet));
340ac726063SMatthias Ringwald                     btstack_assert(cover_art_client != NULL);
341ac726063SMatthias Ringwald                     avrcp_cover_art_client_handle_can_send_now(cover_art_client);
342ac726063SMatthias Ringwald                     break;
343ac726063SMatthias Ringwald                 default:
344ac726063SMatthias Ringwald                     break;
345ac726063SMatthias Ringwald             }
346ac726063SMatthias Ringwald             break;
347ac726063SMatthias Ringwald         default:
348ac726063SMatthias Ringwald             break;
349ac726063SMatthias Ringwald     }
350ac726063SMatthias Ringwald }
351ac726063SMatthias Ringwald 
352ac726063SMatthias Ringwald static void avrcp_cover_art_client_goep_data_handler(avrcp_cover_art_client_t * cover_art_client, uint8_t *packet, uint16_t size){
353ac726063SMatthias Ringwald     btstack_assert(cover_art_client->obex_parser_waiting_for_response);
354ac726063SMatthias Ringwald 
355ac726063SMatthias Ringwald     obex_parser_object_state_t parser_state;
356ac726063SMatthias Ringwald     parser_state = obex_parser_process_data(&cover_art_client->obex_parser, packet, size);
357ac726063SMatthias Ringwald     if (parser_state == OBEX_PARSER_OBJECT_STATE_COMPLETE){
358ac726063SMatthias Ringwald         cover_art_client->obex_parser_waiting_for_response = false;
359ac726063SMatthias Ringwald         obex_parser_operation_info_t op_info;
360ac726063SMatthias Ringwald         obex_parser_get_operation_info(&cover_art_client->obex_parser, &op_info);
361ac726063SMatthias Ringwald         switch (cover_art_client->state){
362ac726063SMatthias Ringwald             case AVRCP_COVER_ART_W4_CONNECT_RESPONSE:
363ac726063SMatthias Ringwald                 switch (op_info.response_code) {
364ac726063SMatthias Ringwald                     case OBEX_RESP_SUCCESS:
365ac726063SMatthias Ringwald                         cover_art_client->state = AVRCP_COVER_ART_CONNECTED;
366ac726063SMatthias Ringwald                         avrcp_cover_art_client_emit_connection_established(cover_art_client->packet_handler,
367ac726063SMatthias Ringwald                                                                            ERROR_CODE_SUCCESS,
368ac726063SMatthias Ringwald                                                                            cover_art_client->addr,
369ac726063SMatthias Ringwald                                                                            cover_art_client->avrcp_cid,
370ac726063SMatthias Ringwald                                                                            cover_art_client->cover_art_cid);
371ac726063SMatthias Ringwald                         break;
372ac726063SMatthias Ringwald                     default:
373ac726063SMatthias Ringwald                         log_info("pbap: obex connect failed, result 0x%02x", packet[0]);
374ac726063SMatthias Ringwald                         cover_art_client->state = AVRCP_COVER_ART_INIT;
375ac726063SMatthias Ringwald                         avrcp_cover_art_client_emit_connection_established(cover_art_client->packet_handler,
376ac726063SMatthias Ringwald                                                                            OBEX_CONNECT_FAILED,
377ac726063SMatthias Ringwald                                                                            cover_art_client->addr,
378ac726063SMatthias Ringwald                                                                            cover_art_client->avrcp_cid,
379ac726063SMatthias Ringwald                                                                            cover_art_client->cover_art_cid);
380ac726063SMatthias Ringwald                         break;
381ac726063SMatthias Ringwald                 }
382ac726063SMatthias Ringwald                 break;
383ac726063SMatthias Ringwald             case AVRCP_COVER_ART_W4_OBJECT:
384ac726063SMatthias Ringwald                 switch (op_info.response_code) {
385ac726063SMatthias Ringwald                     case OBEX_RESP_CONTINUE:
386ac726063SMatthias Ringwald                         avrcp_cover_art_client_handle_srm_headers(cover_art_client);
387ac726063SMatthias Ringwald                         if (cover_art_client->srm_state == SRM_ENABLED) {
388ac726063SMatthias Ringwald                             // prepare response
389ac726063SMatthias Ringwald                             avrcp_cover_art_client_prepare_get_operation(cover_art_client);
390ac726063SMatthias Ringwald                             break;
391ac726063SMatthias Ringwald                         }
392ac726063SMatthias Ringwald                         cover_art_client->state = AVRCP_COVER_ART_W2_SEND_GET_OBJECT;
393ac726063SMatthias Ringwald                         if (!cover_art_client->flow_control_enabled || !cover_art_client->flow_wait_for_user ||
394ac726063SMatthias Ringwald                             cover_art_client->flow_next_triggered) {
395ac726063SMatthias Ringwald                             goep_client_request_can_send_now(cover_art_client->goep_cid);
396ac726063SMatthias Ringwald                         }
397ac726063SMatthias Ringwald                         break;
398ac726063SMatthias Ringwald                     case OBEX_RESP_SUCCESS:
399ac726063SMatthias Ringwald                         cover_art_client->state = AVRCP_COVER_ART_CONNECTED;
400ac726063SMatthias Ringwald                         cover_art_client_emit_operation_complete_event(cover_art_client, ERROR_CODE_SUCCESS);
401ac726063SMatthias Ringwald                         break;
402ac726063SMatthias Ringwald                     default:
403ac726063SMatthias Ringwald                         log_info("unexpected response 0x%02x", packet[0]);
404ac726063SMatthias Ringwald                         cover_art_client->state = AVRCP_COVER_ART_CONNECTED;
405ac726063SMatthias Ringwald                         cover_art_client_emit_operation_complete_event(cover_art_client, OBEX_UNKNOWN_ERROR);
406ac726063SMatthias Ringwald                         break;
407ac726063SMatthias Ringwald                 }
408ac726063SMatthias Ringwald                 break;
409ac726063SMatthias Ringwald             case AVRCP_COVER_ART_W4_DISCONNECT_RESPONSE:
410ac726063SMatthias Ringwald                 goep_client_disconnect(cover_art_client->goep_cid);
411ac726063SMatthias Ringwald                 break;
412ac726063SMatthias Ringwald             default:
413ac726063SMatthias Ringwald                 btstack_unreachable();
414ac726063SMatthias Ringwald                 break;
415ac726063SMatthias Ringwald         }
416ac726063SMatthias Ringwald     }
417ac726063SMatthias Ringwald }
418ac726063SMatthias Ringwald 
419ac726063SMatthias Ringwald static void avrcp_cover_art_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
420ac726063SMatthias Ringwald     UNUSED(channel); // ok: there is no channel
421ac726063SMatthias Ringwald     UNUSED(size);    // ok: handling own goep events
422ac726063SMatthias Ringwald     avrcp_cover_art_client_t * cover_art_client;
423ac726063SMatthias Ringwald     switch (packet_type){
424ac726063SMatthias Ringwald         case HCI_EVENT_PACKET:
425ac726063SMatthias Ringwald             avrcp_cover_art_goep_event_handler(packet, size);
426ac726063SMatthias Ringwald             break;
427ac726063SMatthias Ringwald         case GOEP_DATA_PACKET:
428ac726063SMatthias Ringwald             cover_art_client = avrcp_cover_art_client_for_goep_cid(channel);
429ac726063SMatthias Ringwald             btstack_assert(cover_art_client != NULL);
430ac726063SMatthias Ringwald             avrcp_cover_art_client_goep_data_handler(cover_art_client, packet, size);
431ac726063SMatthias Ringwald             break;
432ac726063SMatthias Ringwald         default:
433ac726063SMatthias Ringwald             break;
434ac726063SMatthias Ringwald     }
435ac726063SMatthias Ringwald }
436ac726063SMatthias Ringwald 
437ac726063SMatthias Ringwald static uint8_t avrcp_cover_art_client_setup_connection(avrcp_cover_art_client_t * cover_art_client, uint16_t l2cap_psm){
438ac726063SMatthias Ringwald     cover_art_client->state = AVRCP_COVER_ART_W4_GOEP_CONNECTION;
439ac726063SMatthias Ringwald     return goep_client_connect_l2cap(&cover_art_client->goep_client,
440ac726063SMatthias Ringwald                                      (l2cap_ertm_config_t *) cover_art_client->ertm_config,
441ac726063SMatthias Ringwald                                      cover_art_client->ertm_buffer,
442ac726063SMatthias Ringwald                                      cover_art_client->ertm_buffer_size,
443ac726063SMatthias Ringwald                                      &avrcp_cover_art_packet_handler,
444ac726063SMatthias Ringwald                                      cover_art_client->addr,
445ac726063SMatthias Ringwald                                      l2cap_psm,
446ac726063SMatthias Ringwald                                      &cover_art_client->goep_cid);
447ac726063SMatthias Ringwald }
448ac726063SMatthias Ringwald 
449ac726063SMatthias Ringwald static void avrcp_cover_art_handle_sdp_query_complete(avrcp_connection_t * connection, uint8_t status){
450ac726063SMatthias Ringwald     avrcp_cover_art_client_t * cover_art_client = avrcp_cover_art_client_for_avrcp_cid(connection->avrcp_cid);
451ac726063SMatthias Ringwald 
452ac726063SMatthias Ringwald     if (cover_art_client == NULL) {
453ac726063SMatthias Ringwald         return;
454ac726063SMatthias Ringwald     }
455ac726063SMatthias Ringwald     if (cover_art_client->state != AVRCP_COVER_ART_W4_SDP_QUERY_COMPLETE){
456ac726063SMatthias Ringwald         return;
457ac726063SMatthias Ringwald     }
458ac726063SMatthias Ringwald 
459ac726063SMatthias Ringwald     // l2cap available?
460ac726063SMatthias Ringwald     if (status == ERROR_CODE_SUCCESS){
461ac726063SMatthias Ringwald         if (connection->cover_art_psm == 0){
462ac726063SMatthias Ringwald             status = SDP_SERVICE_NOT_FOUND;
463ac726063SMatthias Ringwald         }
464ac726063SMatthias Ringwald     }
465ac726063SMatthias Ringwald 
466ac726063SMatthias Ringwald     if (status == ERROR_CODE_SUCCESS) {
467ac726063SMatthias Ringwald         // ready to connect
468ac726063SMatthias Ringwald         cover_art_client->state = AVRCP_COVER_ART_W2_GOEP_CONNECT;
469ac726063SMatthias Ringwald         avrcp_cover_art_client_setup_connection(cover_art_client, connection->cover_art_psm);
470ac726063SMatthias Ringwald     } else {
471ac726063SMatthias Ringwald         btstack_packet_handler_t packet_handler = cover_art_client->packet_handler;
472ac726063SMatthias Ringwald         uint16_t cover_art_cid = cover_art_client->cover_art_cid;
473ac726063SMatthias Ringwald         uint16_t avrcp_cid =  cover_art_client->avrcp_cid;
474ac726063SMatthias Ringwald         avrcp_cover_art_finalize_connection(cover_art_client);
475ac726063SMatthias Ringwald         avrcp_cover_art_client_emit_connection_established(packet_handler, status, connection->remote_addr,
476ac726063SMatthias Ringwald                                                            avrcp_cid, cover_art_cid);
477ac726063SMatthias Ringwald     }
478ac726063SMatthias Ringwald }
479ac726063SMatthias Ringwald 
480ac726063SMatthias Ringwald void avrcp_cover_art_client_init(void){
481ac726063SMatthias Ringwald     avrcp_register_cover_art_sdp_query_complete_handler(&avrcp_cover_art_handle_sdp_query_complete);
482ac726063SMatthias Ringwald }
483ac726063SMatthias Ringwald 
484ac726063SMatthias Ringwald uint8_t
485ac726063SMatthias Ringwald avrcp_cover_art_client_connect(avrcp_cover_art_client_t *cover_art_client, btstack_packet_handler_t packet_handler,
486ac726063SMatthias Ringwald                                bd_addr_t remote_addr, uint8_t *ertm_buffer, uint32_t ertm_buffer_size,
487ac726063SMatthias Ringwald                                const l2cap_ertm_config_t *ertm_config, uint16_t *avrcp_cover_art_cid) {
488ac726063SMatthias Ringwald 
489ac726063SMatthias Ringwald     avrcp_connection_t * connection_controller = avrcp_get_connection_for_bd_addr_for_role(AVRCP_CONTROLLER, remote_addr);
490ac726063SMatthias Ringwald     avrcp_connection_t * connection_target = avrcp_get_connection_for_bd_addr_for_role(AVRCP_TARGET, remote_addr);
491ac726063SMatthias Ringwald     if ((connection_target == NULL) || (connection_controller == NULL)){
492ac726063SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
493ac726063SMatthias Ringwald     }
494ac726063SMatthias Ringwald 
495ac726063SMatthias Ringwald     cover_art_client->cover_art_cid = avrcp_cover_art_client_next_cid();
496ac726063SMatthias Ringwald     memcpy(cover_art_client->addr, remote_addr, 6);
497ac726063SMatthias Ringwald     cover_art_client->avrcp_cid = connection_controller->avrcp_cid;
498ac726063SMatthias Ringwald     cover_art_client->packet_handler = packet_handler;
499ac726063SMatthias Ringwald     cover_art_client->flow_control_enabled = false;
500ac726063SMatthias Ringwald 
501ac726063SMatthias Ringwald     // store ERTM config
502ac726063SMatthias Ringwald     cover_art_client->ertm_config = ertm_config;
503ac726063SMatthias Ringwald     cover_art_client->ertm_buffer = ertm_buffer;
504ac726063SMatthias Ringwald     cover_art_client->ertm_buffer_size = ertm_buffer_size;
505ac726063SMatthias Ringwald 
506ac726063SMatthias Ringwald     if (avrcp_cover_art_cid != NULL){
507ac726063SMatthias Ringwald         *avrcp_cover_art_cid = cover_art_client->cover_art_cid;
508ac726063SMatthias Ringwald     }
509ac726063SMatthias Ringwald 
510ac726063SMatthias Ringwald     btstack_linked_list_add(&avrcp_cover_art_client_connections, (btstack_linked_item_t *) cover_art_client);
511ac726063SMatthias Ringwald 
512ac726063SMatthias Ringwald     if (connection_controller->cover_art_psm == 0){
513ac726063SMatthias Ringwald         cover_art_client->state = AVRCP_COVER_ART_W4_SDP_QUERY_COMPLETE;
514ac726063SMatthias Ringwald         avrcp_trigger_sdp_query(connection_controller, connection_controller);
515ac726063SMatthias Ringwald         return ERROR_CODE_SUCCESS;
516ac726063SMatthias Ringwald     } else {
517ac726063SMatthias Ringwald         return avrcp_cover_art_client_setup_connection(cover_art_client, connection_controller->cover_art_psm);
518ac726063SMatthias Ringwald     }
519ac726063SMatthias Ringwald }
520ac726063SMatthias Ringwald 
521ac726063SMatthias 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){
522ac726063SMatthias Ringwald     avrcp_cover_art_client_t * cover_art_client = avrcp_cover_art_client_for_cover_art_cid(avrcp_cover_art_cid);
523ac726063SMatthias Ringwald     if (cover_art_client == NULL){
524ac726063SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
525ac726063SMatthias Ringwald     }
526ac726063SMatthias Ringwald     if (cover_art_client->state != AVRCP_COVER_ART_CONNECTED){
527ac726063SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
528ac726063SMatthias Ringwald     }
529ac726063SMatthias Ringwald     cover_art_client->state = AVRCP_COVER_ART_W2_SEND_GET_OBJECT;
530ac726063SMatthias Ringwald     cover_art_client->first_request = true;
531ac726063SMatthias Ringwald     cover_art_client->image_handle = image_handle;
532ac726063SMatthias Ringwald     cover_art_client->image_descriptor = image_descriptor;
533ac726063SMatthias Ringwald     cover_art_client->object_type = object_type;
534ac726063SMatthias Ringwald     goep_client_request_can_send_now(cover_art_client->goep_cid);
535ac726063SMatthias Ringwald     return ERROR_CODE_SUCCESS;
536ac726063SMatthias Ringwald }
537ac726063SMatthias Ringwald 
538ac726063SMatthias Ringwald uint8_t avrcp_cover_art_client_get_linked_thumbnail(uint16_t avrcp_cover_art_cid, const char * image_handle){
539ac726063SMatthias Ringwald     return avrcp_cover_art_client_get_object(avrcp_cover_art_cid,
540ac726063SMatthias Ringwald                                              avrcp_cover_art_linked_thumbnail_type,
541ac726063SMatthias Ringwald                                              image_handle,
542ac726063SMatthias Ringwald                                              NULL);
543ac726063SMatthias Ringwald }
544ac726063SMatthias Ringwald 
545ac726063SMatthias Ringwald uint8_t avrcp_cover_art_client_get_image(uint16_t avrcp_cover_art_cid, const char * image_handle, const char * image_descriptor){
546ac726063SMatthias Ringwald     return avrcp_cover_art_client_get_object(avrcp_cover_art_cid,
547ac726063SMatthias Ringwald                                              avrcp_cover_art_image_type,
548ac726063SMatthias Ringwald                                              image_handle,
549ac726063SMatthias Ringwald                                              image_descriptor);
550ac726063SMatthias Ringwald }
551ac726063SMatthias Ringwald 
552ac726063SMatthias Ringwald uint8_t avrcp_cover_art_client_get_image_properties(uint16_t avrcp_cover_art_cid, const char * image_handle){
553ac726063SMatthias Ringwald     return avrcp_cover_art_client_get_object(avrcp_cover_art_cid,
554ac726063SMatthias Ringwald                                              avrcp_cover_art_image_properties_type,
555ac726063SMatthias Ringwald                                              image_handle,
556ac726063SMatthias Ringwald                                              NULL);
557ac726063SMatthias Ringwald }
558ac726063SMatthias Ringwald 
559ac726063SMatthias Ringwald uint8_t avrcp_cover_art_client_disconnect(uint16_t avrcp_cover_art_cid){
560ac726063SMatthias Ringwald     avrcp_cover_art_client_t * cover_art_client = avrcp_cover_art_client_for_cover_art_cid(avrcp_cover_art_cid);
561ac726063SMatthias Ringwald     if (cover_art_client == NULL){
562ac726063SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
563ac726063SMatthias Ringwald     }
564ac726063SMatthias Ringwald     if (cover_art_client->state < AVRCP_COVER_ART_CONNECTED){
565ac726063SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
566ac726063SMatthias Ringwald     }
567ac726063SMatthias Ringwald     cover_art_client->state = AVRCP_COVER_ART_W2_SEND_DISCONNECT_REQUEST;
568ac726063SMatthias Ringwald     goep_client_request_can_send_now(cover_art_client->goep_cid);
569ac726063SMatthias Ringwald     return ERROR_CODE_SUCCESS;
570ac726063SMatthias Ringwald }
571ac726063SMatthias Ringwald 
572ac726063SMatthias Ringwald void avrcp_cover_art_client_deinit(void){
573ac726063SMatthias Ringwald     avrcp_cover_art_client_connections = NULL;
574ac726063SMatthias Ringwald }
575ac726063SMatthias Ringwald 
576ac726063SMatthias Ringwald #endif /* ENABLE_AVRCP_COVER_ART */
577