1 /* 2 * Copyright (C) 2016 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 MATTHIAS 24 * RINGWALD 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_browsing_controller.c" 39 40 #include <stdint.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 45 #include "btstack.h" 46 #include "classic/avrcp.h" 47 #include "classic/avrcp_browsing_controller.h" 48 49 static avrcp_context_t avrcp_browsing_controller_context; 50 static void avrcp_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 51 static uint16_t avrcp_cid_counter = 0; 52 53 static avrcp_context_t * sdp_query_context; 54 static uint8_t attribute_value[1000]; 55 static const unsigned int attribute_value_buffer_size = sizeof(attribute_value); 56 57 static void avrcp_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 58 void avrcp_browser_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size, avrcp_context_t * context); 59 60 static uint16_t avrcp_get_next_cid(void){ 61 avrcp_cid_counter++; 62 if (avrcp_cid_counter == 0){ 63 avrcp_cid_counter = 1; 64 } 65 return avrcp_cid_counter; 66 } 67 68 static avrcp_browsing_connection_t * get_avrcp_browsing_connection_for_l2cap_cid(uint16_t l2cap_cid, avrcp_context_t * context){ 69 btstack_linked_list_iterator_t it; 70 btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &context->connections); 71 while (btstack_linked_list_iterator_has_next(&it)){ 72 avrcp_browsing_connection_t * connection = (avrcp_browsing_connection_t *)btstack_linked_list_iterator_next(&it); 73 if (connection->l2cap_browsing_cid != l2cap_cid) continue; 74 return connection; 75 } 76 return NULL; 77 } 78 79 static avrcp_browsing_connection_t * get_avrcp_browsing_connection_for_cid(uint16_t avrcp_cid, avrcp_context_t * context){ 80 btstack_linked_list_iterator_t it; 81 btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &context->connections); 82 while (btstack_linked_list_iterator_has_next(&it)){ 83 avrcp_browsing_connection_t * connection = (avrcp_browsing_connection_t *)btstack_linked_list_iterator_next(&it); 84 if (connection->browsing_cid != avrcp_cid) continue; 85 return connection; 86 } 87 return NULL; 88 } 89 90 static avrcp_browsing_connection_t * get_avrcp_browsing_connection_for_bd_addr(bd_addr_t addr, avrcp_context_t * context){ 91 btstack_linked_list_iterator_t it; 92 btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &context->connections); 93 while (btstack_linked_list_iterator_has_next(&it)){ 94 avrcp_browsing_connection_t * connection = (avrcp_browsing_connection_t *)btstack_linked_list_iterator_next(&it); 95 if (memcmp(addr, connection->remote_addr, 6) != 0) continue; 96 return connection; 97 } 98 return NULL; 99 } 100 101 static void avrcp_emit_browsing_connection_established(btstack_packet_handler_t callback, uint16_t avrcp_cid, bd_addr_t addr, uint8_t status){ 102 if (!callback) return; 103 uint8_t event[12]; 104 int pos = 0; 105 event[pos++] = HCI_EVENT_AVRCP_META; 106 event[pos++] = sizeof(event) - 2; 107 event[pos++] = AVRCP_SUBEVENT_BROWSING_CONNECTION_ESTABLISHED; 108 event[pos++] = status; 109 reverse_bd_addr(addr,&event[pos]); 110 pos += 6; 111 little_endian_store_16(event, pos, avrcp_cid); 112 pos += 2; 113 (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 114 } 115 116 static void avrcp_emit_browsing_connection_closed(btstack_packet_handler_t callback, uint16_t avrcp_cid){ 117 if (!callback) return; 118 uint8_t event[5]; 119 int pos = 0; 120 event[pos++] = HCI_EVENT_AVRCP_META; 121 event[pos++] = sizeof(event) - 2; 122 event[pos++] = AVRCP_SUBEVENT_BROWSING_CONNECTION_RELEASED; 123 little_endian_store_16(event, pos, avrcp_cid); 124 pos += 2; 125 (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 126 } 127 128 static void avrcp_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 129 avrcp_browsing_connection_t * connection = get_avrcp_browsing_connection_for_cid(sdp_query_context->avrcp_cid, sdp_query_context); 130 if (!connection) return; 131 if (connection->state != AVCTP_CONNECTION_W4_SDP_QUERY_COMPLETE) return; 132 UNUSED(packet_type); 133 UNUSED(channel); 134 UNUSED(size); 135 uint8_t status; 136 des_iterator_t des_list_it; 137 des_iterator_t prot_it; 138 // uint32_t avdtp_remote_uuid = 0; 139 140 switch (hci_event_packet_get_type(packet)){ 141 case SDP_EVENT_QUERY_ATTRIBUTE_VALUE: 142 // Handle new SDP record 143 if (sdp_event_query_attribute_byte_get_record_id(packet) != sdp_query_context->record_id) { 144 sdp_query_context->record_id = sdp_event_query_attribute_byte_get_record_id(packet); 145 sdp_query_context->parse_sdp_record = 0; 146 printf("SDP Record: Nr: %d\n", sdp_query_context->record_id); 147 } 148 149 if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= attribute_value_buffer_size) { 150 attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet); 151 152 if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) { 153 switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) { 154 case BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST: 155 if (de_get_element_type(attribute_value) != DE_DES) break; 156 for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) { 157 uint8_t * element = des_iterator_get_element(&des_list_it); 158 if (de_get_element_type(element) != DE_UUID) continue; 159 uint32_t uuid = de_get_uuid32(element); 160 switch (uuid){ 161 case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_TARGET: 162 if (sdp_query_context->role == AVRCP_CONTROLLER) { 163 sdp_query_context->parse_sdp_record = 1; 164 printf(" Controller \n"); 165 break; 166 } 167 break; 168 case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL: 169 case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_CONTROLLER: 170 if (sdp_query_context->role == AVRCP_TARGET) { 171 printf(" Target \n"); 172 sdp_query_context->parse_sdp_record = 1; 173 break; 174 } 175 break; 176 default: 177 printf(" not found\n"); 178 break; 179 } 180 } 181 break; 182 183 case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST: { 184 if (!sdp_query_context->parse_sdp_record) break; 185 // log_info("SDP Attribute: 0x%04x", sdp_event_query_attribute_byte_get_attribute_id(packet)); 186 for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) { 187 uint8_t *des_element; 188 uint8_t *element; 189 uint32_t uuid; 190 191 if (des_iterator_get_type(&des_list_it) != DE_DES) continue; 192 193 des_element = des_iterator_get_element(&des_list_it); 194 des_iterator_init(&prot_it, des_element); 195 element = des_iterator_get_element(&prot_it); 196 197 if (de_get_element_type(element) != DE_UUID) continue; 198 199 uuid = de_get_uuid32(element); 200 switch (uuid){ 201 case BLUETOOTH_PROTOCOL_L2CAP: 202 if (!des_iterator_has_more(&prot_it)) continue; 203 des_iterator_next(&prot_it); 204 de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context->avrcp_l2cap_psm); 205 printf(" found signaling PSM: 0x%02x\n", sdp_query_context->avrcp_l2cap_psm); 206 break; 207 case BLUETOOTH_PROTOCOL_AVCTP: 208 if (!des_iterator_has_more(&prot_it)) continue; 209 des_iterator_next(&prot_it); 210 de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context->avrcp_version); 211 break; 212 default: 213 break; 214 } 215 } 216 } 217 break; 218 case BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: { 219 // log_info("SDP Attribute: 0x%04x", sdp_event_query_attribute_byte_get_attribute_id(packet)); 220 if (!sdp_query_context->parse_sdp_record) break; 221 if (de_get_element_type(attribute_value) != DE_DES) break; 222 223 des_iterator_t des_list_0_it; 224 uint8_t *element_0; 225 226 des_iterator_init(&des_list_0_it, attribute_value); 227 element_0 = des_iterator_get_element(&des_list_0_it); 228 229 for (des_iterator_init(&des_list_it, element_0); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) { 230 uint8_t *des_element; 231 uint8_t *element; 232 uint32_t uuid; 233 234 if (des_iterator_get_type(&des_list_it) != DE_DES) continue; 235 236 des_element = des_iterator_get_element(&des_list_it); 237 des_iterator_init(&prot_it, des_element); 238 element = des_iterator_get_element(&prot_it); 239 240 if (de_get_element_type(element) != DE_UUID) continue; 241 242 uuid = de_get_uuid32(element); 243 switch (uuid){ 244 case BLUETOOTH_PROTOCOL_L2CAP: 245 if (!des_iterator_has_more(&prot_it)) continue; 246 des_iterator_next(&prot_it); 247 de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context->avrcp_browsing_l2cap_psm); 248 printf(" found browsing PSM: 0x%02x\n", sdp_query_context->avrcp_browsing_l2cap_psm); 249 break; 250 case BLUETOOTH_PROTOCOL_AVCTP: 251 if (!des_iterator_has_more(&prot_it)) continue; 252 des_iterator_next(&prot_it); 253 de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context->avrcp_browsing_version); 254 break; 255 default: 256 break; 257 } 258 } 259 } 260 break; 261 default: 262 break; 263 } 264 } 265 } else { 266 log_error("SDP attribute value buffer size exceeded: available %d, required %d", attribute_value_buffer_size, sdp_event_query_attribute_byte_get_attribute_length(packet)); 267 } 268 break; 269 270 case SDP_EVENT_QUERY_COMPLETE: 271 status = sdp_event_query_complete_get_status(packet); 272 if (status != ERROR_CODE_SUCCESS){ 273 avrcp_emit_browsing_connection_established(sdp_query_context->avrcp_callback, connection->browsing_cid, connection->remote_addr, status); 274 btstack_linked_list_remove(&sdp_query_context->connections, (btstack_linked_item_t*) connection); 275 btstack_memory_avrcp_browsing_connection_free(connection); 276 log_info("AVRCP: SDP query failed with status 0x%02x.", status); 277 break; 278 } 279 280 if (!sdp_query_context->parse_sdp_record || !sdp_query_context->avrcp_browsing_l2cap_psm){ 281 connection->state = AVCTP_CONNECTION_IDLE; 282 avrcp_emit_browsing_connection_established(sdp_query_context->avrcp_callback, connection->browsing_cid, connection->remote_addr, SDP_SERVICE_NOT_FOUND); 283 btstack_linked_list_remove(&sdp_query_context->connections, (btstack_linked_item_t*) connection); 284 btstack_memory_avrcp_browsing_connection_free(connection); 285 break; 286 } 287 // log_info("AVRCP Control PSM 0x%02x, Browsing PSM 0x%02x", sdp_query_context->avrcp_l2cap_psm, sdp_query_context->avrcp_browsing_l2cap_psm); 288 connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED; 289 290 l2cap_create_ertm_channel(sdp_query_context->packet_handler, connection->remote_addr, sdp_query_context->avrcp_browsing_l2cap_psm, 291 &connection->ertm_config, connection->ertm_buffer, connection->ertm_buffer_size, NULL); 292 293 // l2cap_create_channel(sdp_query_context->packet_handler, connection->remote_addr, sdp_query_context->avrcp_l2cap_psm, l2cap_max_mtu(), NULL); 294 break; 295 } 296 } 297 298 299 static avrcp_browsing_connection_t * avrcp_browsing_create_connection(bd_addr_t remote_addr, avrcp_context_t * context){ 300 avrcp_browsing_connection_t * connection = btstack_memory_avrcp_browsing_connection_get(); 301 if (!connection){ 302 log_error("avrcp: not enough memory to create connection"); 303 return NULL; 304 } 305 memset(connection, 0, sizeof(avrcp_browsing_connection_t)); 306 connection->state = AVCTP_CONNECTION_IDLE; 307 connection->transaction_label = 0xFF; 308 connection->browsing_cid = avrcp_get_next_cid(); 309 memcpy(connection->remote_addr, remote_addr, 6); 310 btstack_linked_list_add(&context->connections, (btstack_linked_item_t *) connection); 311 return connection; 312 } 313 314 static uint8_t avrcp_browsing_connect(bd_addr_t bd_addr, avrcp_context_t * context, uint8_t * ertm_buffer, uint32_t size, l2cap_ertm_config_t * ertm_config, uint16_t * avrcp_cid){ 315 avrcp_browsing_connection_t * connection = get_avrcp_browsing_connection_for_bd_addr(bd_addr, context); 316 if (connection){ 317 return ERROR_CODE_COMMAND_DISALLOWED; 318 } 319 connection = avrcp_browsing_create_connection(bd_addr, context); 320 if (!connection){ 321 printf("avrcp: could not allocate connection struct."); 322 return BTSTACK_MEMORY_ALLOC_FAILED; 323 } 324 325 // if (!avrcp_cid) return L2CAP_LOCAL_CID_DOES_NOT_EXIST; 326 327 *avrcp_cid = connection->browsing_cid; 328 connection->state = AVCTP_CONNECTION_W4_SDP_QUERY_COMPLETE; 329 connection->ertm_buffer = ertm_buffer; 330 connection->ertm_buffer_size = size; 331 memcpy(&connection->ertm_config, ertm_config, sizeof(l2cap_ertm_config_t)); 332 333 context->parse_sdp_record = 0; 334 context->record_id = 0; 335 context->avrcp_l2cap_psm = 0; 336 context->avrcp_version = 0; 337 context->avrcp_browsing_l2cap_psm = 0; 338 context->avrcp_browsing_version = 0; 339 340 context->avrcp_cid = connection->browsing_cid; 341 sdp_query_context = context; 342 printf(" start SDP query\n"); 343 return sdp_client_query_uuid16(&avrcp_handle_sdp_client_query_result, bd_addr, BLUETOOTH_PROTOCOL_AVCTP); 344 } 345 346 void avrcp_browser_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size, avrcp_context_t * context){ 347 UNUSED(channel); 348 UNUSED(size); 349 bd_addr_t event_addr; 350 uint16_t local_cid; 351 uint8_t status; 352 avrcp_browsing_connection_t * connection = NULL; 353 354 if (packet_type != HCI_EVENT_PACKET) return; 355 356 switch (hci_event_packet_get_type(packet)) { 357 case HCI_EVENT_DISCONNECTION_COMPLETE: 358 avrcp_emit_browsing_connection_closed(context->avrcp_callback, 0); 359 break; 360 case L2CAP_EVENT_INCOMING_CONNECTION: 361 l2cap_event_incoming_connection_get_address(packet, event_addr); 362 local_cid = l2cap_event_incoming_connection_get_local_cid(packet); 363 connection = avrcp_browsing_create_connection(event_addr, context); 364 if (!connection) { 365 log_error("Failed to alloc connection structure"); 366 l2cap_decline_connection(local_cid); 367 break; 368 } 369 connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED; 370 connection->l2cap_browsing_cid = local_cid; 371 log_info("L2CAP_EVENT_INCOMING_CONNECTION avrcp_cid 0x%02x, l2cap_signaling_cid 0x%02x", connection->browsing_cid, connection->l2cap_browsing_cid); 372 // l2cap_accept_connection(local_cid); 373 printf("L2CAP Accepting incoming connection request in ERTM\n"); 374 l2cap_accept_ertm_connection(local_cid, &connection->ertm_config, connection->ertm_buffer, connection->ertm_buffer_size); 375 break; 376 377 case L2CAP_EVENT_CHANNEL_OPENED: 378 l2cap_event_channel_opened_get_address(packet, event_addr); 379 status = l2cap_event_channel_opened_get_status(packet); 380 local_cid = l2cap_event_channel_opened_get_local_cid(packet); 381 382 connection = get_avrcp_browsing_connection_for_bd_addr(event_addr, context); 383 if (!connection){ 384 log_error("Failed to alloc AVRCP connection structure"); 385 avrcp_emit_browsing_connection_established(context->avrcp_callback, connection->browsing_cid, event_addr, BTSTACK_MEMORY_ALLOC_FAILED); 386 l2cap_disconnect(local_cid, 0); // reason isn't used 387 break; 388 } 389 if (status != ERROR_CODE_SUCCESS){ 390 log_info("L2CAP connection to connection %s failed. status code 0x%02x", bd_addr_to_str(event_addr), status); 391 avrcp_emit_browsing_connection_established(context->avrcp_callback, connection->browsing_cid, event_addr, status); 392 btstack_linked_list_remove(&context->connections, (btstack_linked_item_t*) connection); 393 btstack_memory_avrcp_browsing_connection_free(connection); 394 break; 395 } 396 connection->l2cap_browsing_cid = local_cid; 397 398 log_info("L2CAP_EVENT_CHANNEL_OPENED avrcp_cid 0x%02x, l2cap_signaling_cid 0x%02x", connection->browsing_cid, connection->l2cap_browsing_cid); 399 connection->state = AVCTP_CONNECTION_OPENED; 400 avrcp_emit_browsing_connection_established(context->avrcp_callback, connection->browsing_cid, event_addr, ERROR_CODE_SUCCESS); 401 break; 402 403 case L2CAP_EVENT_CHANNEL_CLOSED: 404 // data: event (8), len(8), channel (16) 405 local_cid = l2cap_event_channel_closed_get_local_cid(packet); 406 connection = get_avrcp_browsing_connection_for_l2cap_cid(local_cid, context); 407 if (connection){ 408 avrcp_emit_browsing_connection_closed(context->avrcp_callback, connection->browsing_cid); 409 // free connection 410 btstack_linked_list_remove(&context->connections, (btstack_linked_item_t*) connection); 411 btstack_memory_avrcp_browsing_connection_free(connection); 412 break; 413 } 414 break; 415 default: 416 break; 417 } 418 } 419 420 // static void avrcp_handle_l2cap_data_packet_for_browsing_connection(avrcp_browsing_connection_t * connection, uint8_t *packet, uint16_t size){ 421 422 // } 423 424 // static void avrcp_browsing_controller_handle_can_send_now(avrcp_browsing_connection_t * connection){ 425 // int i; 426 // switch (connection->state){ 427 // case AVCTP_CONNECTION_OPENED: 428 // return; 429 // default: 430 // return; 431 // } 432 // } 433 434 static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 435 avrcp_browsing_connection_t * connection; 436 437 switch (packet_type) { 438 case L2CAP_DATA_PACKET: 439 connection = get_avrcp_browsing_connection_for_l2cap_cid(channel, &avrcp_browsing_controller_context); 440 if (!connection) break; 441 // avrcp_handle_l2cap_data_packet_for_browsing_connection(connection, packet, size); 442 break; 443 case HCI_EVENT_PACKET: 444 switch (hci_event_packet_get_type(packet)){ 445 case L2CAP_EVENT_CAN_SEND_NOW: 446 connection = get_avrcp_browsing_connection_for_l2cap_cid(channel, &avrcp_browsing_controller_context); 447 if (!connection) break; 448 // avrcp_browsing_controller_handle_can_send_now(connection); 449 break; 450 default: 451 avrcp_browser_packet_handler(packet_type, channel, packet, size, &avrcp_browsing_controller_context); 452 break; 453 } 454 default: 455 break; 456 } 457 } 458 459 void avrcp_browsing_controller_init(void){ 460 avrcp_browsing_controller_context.role = AVRCP_CONTROLLER; 461 avrcp_browsing_controller_context.connections = NULL; 462 avrcp_browsing_controller_context.packet_handler = avrcp_browsing_controller_packet_handler; 463 l2cap_register_service(&avrcp_browsing_controller_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_0); 464 } 465 466 void avrcp_browsing_controller_register_packet_handler(btstack_packet_handler_t callback){ 467 if (callback == NULL){ 468 log_error("avrcp_browsing_controller_register_packet_handler called with NULL callback"); 469 return; 470 } 471 avrcp_browsing_controller_context.avrcp_callback = callback; 472 } 473 474 uint8_t avrcp_browsing_controller_connect(bd_addr_t bd_addr, uint8_t * ertm_buffer, uint32_t size, l2cap_ertm_config_t * ertm_config, uint16_t * avrcp_browsing_cid){ 475 return avrcp_browsing_connect(bd_addr, &avrcp_browsing_controller_context, ertm_buffer, size, ertm_config, avrcp_browsing_cid); 476 } 477 478 uint8_t avrcp_browsing_controller_disconnect(uint16_t avrcp_browsing_cid){ 479 avrcp_browsing_connection_t * connection = get_avrcp_browsing_connection_for_cid(avrcp_browsing_cid, &avrcp_browsing_controller_context); 480 if (!connection){ 481 log_error("avrcp_browsing_controller_disconnect: could not find a connection."); 482 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 483 } 484 if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; 485 l2cap_disconnect(connection->l2cap_browsing_cid, 0); 486 return ERROR_CODE_SUCCESS; 487 } 488