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