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