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