Home | History | Annotate | Download | only in server
      1 /* Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file.
      4  */
      5 
      6 #include <dbus/dbus.h>
      7 #include <errno.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <syslog.h>
     11 
     12 #include "cras_bt_constants.h"
     13 #include "cras_bt_adapter.h"
     14 #include "cras_bt_endpoint.h"
     15 #include "cras_bt_transport.h"
     16 #include "utlist.h"
     17 
     18 /* Defined by doc/media-api.txt in the BlueZ source */
     19 #define ENDPOINT_INTROSPECT_XML						\
     20 	DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE			\
     21 	"<node>\n"							\
     22 	"  <interface name=\"org.bluez.MediaEndpoint\">\n"		\
     23 	"    <method name=\"SetConfiguration\">\n"			\
     24 	"      <arg name=\"transport\" type=\"o\" direction=\"in\"/>\n"	\
     25 	"      <arg name=\"configuration\" type=\"a{sv}\" direction=\"in\"/>\n"\
     26 	"    </method>\n"						\
     27 	"    <method name=\"SelectConfiguration\">\n"			\
     28 	"      <arg name=\"capabilities\" type=\"ay\" direction=\"in\"/>\n"\
     29 	"      <arg name=\"configuration\" type=\"ay\" direction=\"out\"/>\n"\
     30 	"    </method>\n"						\
     31 	"    <method name=\"ClearConfiguration\">\n"			\
     32 	"    </method>\n"						\
     33 	"    <method name=\"Release\">\n"				\
     34 	"    </method>\n"						\
     35 	"  </interface>\n"						\
     36 	"  <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"	\
     37 	"    <method name=\"Introspect\">\n"				\
     38 	"      <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"	\
     39 	"    </method>\n"						\
     40 	"  </interface>\n"						\
     41 	"</node>\n"
     42 
     43 
     44 static void cras_bt_endpoint_suspend(struct cras_bt_endpoint *endpoint)
     45 {
     46 	if (!endpoint->transport)
     47 		return;
     48 
     49 	endpoint->suspend(endpoint, endpoint->transport);
     50 
     51 	cras_bt_transport_set_endpoint(endpoint->transport, NULL);
     52 	endpoint->transport = NULL;
     53 }
     54 
     55 static DBusHandlerResult cras_bt_endpoint_set_configuration(
     56 	DBusConnection *conn,
     57 	DBusMessage *message,
     58 	void *arg)
     59 {
     60 	DBusMessageIter message_iter, properties_array_iter;
     61 	const char *endpoint_path, *transport_path;
     62 	struct cras_bt_endpoint *endpoint;
     63 	struct cras_bt_transport *transport;
     64 	DBusMessage *reply;
     65 
     66 	syslog(LOG_DEBUG, "SetConfiguration: %s",
     67 	       dbus_message_get_path(message));
     68 
     69 	endpoint_path = dbus_message_get_path(message);
     70 	endpoint = cras_bt_endpoint_get(endpoint_path);
     71 	if (!endpoint)
     72 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     73 
     74 	if (!dbus_message_has_signature(message, "oa{sv}")) {
     75 		syslog(LOG_WARNING, "Bad SetConfiguration message received.");
     76 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     77 	}
     78 
     79 	dbus_message_iter_init(message, &message_iter);
     80 
     81 	dbus_message_iter_get_basic(&message_iter, &transport_path);
     82 	dbus_message_iter_next(&message_iter);
     83 
     84 	dbus_message_iter_recurse(&message_iter, &properties_array_iter);
     85 
     86 	transport = cras_bt_transport_get(transport_path);
     87 	if (transport) {
     88 		cras_bt_transport_update_properties(transport,
     89 						    &properties_array_iter,
     90 						    NULL);
     91 	} else {
     92 		transport = cras_bt_transport_create(conn, transport_path);
     93 		if (transport) {
     94 			cras_bt_transport_update_properties(
     95 				transport,
     96 				&properties_array_iter,
     97 				NULL);
     98 			syslog(LOG_INFO, "Bluetooth Transport: %s added",
     99 			       cras_bt_transport_object_path(transport));
    100 		}
    101 	}
    102 
    103 	if (!cras_bt_transport_device(transport)) {
    104 		syslog(LOG_ERR, "Do device found for transport %s",
    105 		       cras_bt_transport_object_path(transport));
    106 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    107 	}
    108 
    109 	cras_bt_transport_set_endpoint(transport, endpoint);
    110 	endpoint->transport = transport;
    111 	endpoint->set_configuration(endpoint, transport);
    112 
    113 	reply = dbus_message_new_method_return(message);
    114 	if (!reply)
    115 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
    116 	if (!dbus_connection_send(conn, reply, NULL))
    117 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
    118 
    119 	dbus_message_unref(reply);
    120 	return DBUS_HANDLER_RESULT_HANDLED;
    121 }
    122 
    123 static DBusHandlerResult cras_bt_endpoint_select_configuration(
    124 	DBusConnection *conn,
    125 	DBusMessage *message,
    126 	void *arg)
    127 {
    128 	DBusError dbus_error;
    129 	const char *endpoint_path;
    130 	struct cras_bt_endpoint *endpoint;
    131 	char buf[4];
    132 	void *capabilities, *configuration = buf;
    133 	int len;
    134 	DBusMessage *reply;
    135 
    136 	syslog(LOG_DEBUG, "SelectConfiguration: %s",
    137 	       dbus_message_get_path(message));
    138 
    139 	endpoint_path = dbus_message_get_path(message);
    140 	endpoint = cras_bt_endpoint_get(endpoint_path);
    141 	if (!endpoint)
    142 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    143 
    144 	dbus_error_init(&dbus_error);
    145 
    146 	if (!dbus_message_get_args(message, &dbus_error,
    147 				   DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
    148 				   &capabilities, &len,
    149 				   DBUS_TYPE_INVALID)) {
    150 		syslog(LOG_WARNING, "Bad SelectConfiguration method call: %s",
    151 		       dbus_error.message);
    152 		dbus_error_free(&dbus_error);
    153 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    154 	}
    155 
    156 	if (len > sizeof(configuration) ||
    157 	    endpoint->select_configuration(endpoint, capabilities, len,
    158 					   configuration) < 0) {
    159 		reply = dbus_message_new_error(
    160 			message,
    161 			"org.chromium.Cras.Error.UnsupportedConfiguration",
    162 			"Unable to select configuration from capabilities");
    163 
    164 		if (!dbus_connection_send(conn, reply, NULL))
    165 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
    166 
    167 		dbus_message_unref(reply);
    168 		return DBUS_HANDLER_RESULT_HANDLED;
    169 	}
    170 
    171 	reply = dbus_message_new_method_return(message);
    172 	if (!reply)
    173 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
    174 	if (!dbus_message_append_args(reply,
    175 				      DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
    176 				      &configuration, len,
    177 				      DBUS_TYPE_INVALID))
    178 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
    179 	if (!dbus_connection_send(conn, reply, NULL))
    180 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
    181 
    182 	dbus_message_unref(reply);
    183 	return DBUS_HANDLER_RESULT_HANDLED;
    184 }
    185 
    186 static DBusHandlerResult cras_bt_endpoint_clear_configuration(
    187 	DBusConnection *conn,
    188 	DBusMessage *message,
    189 	void *arg)
    190 {
    191 	DBusError dbus_error;
    192 	const char *endpoint_path, *transport_path;
    193 	struct cras_bt_endpoint *endpoint;
    194 	struct cras_bt_transport *transport;
    195 	DBusMessage *reply;
    196 
    197 	syslog(LOG_DEBUG, "ClearConfiguration: %s",
    198 	       dbus_message_get_path(message));
    199 
    200 	endpoint_path = dbus_message_get_path(message);
    201 	endpoint = cras_bt_endpoint_get(endpoint_path);
    202 	if (!endpoint)
    203 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    204 
    205 	dbus_error_init(&dbus_error);
    206 
    207 	if (!dbus_message_get_args(message, &dbus_error,
    208 				   DBUS_TYPE_OBJECT_PATH, &transport_path,
    209 				   DBUS_TYPE_INVALID)) {
    210 		syslog(LOG_WARNING, "Bad ClearConfiguration method call: %s",
    211 		       dbus_error.message);
    212 		dbus_error_free(&dbus_error);
    213 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    214 	}
    215 
    216 	transport = cras_bt_transport_get(transport_path);
    217 
    218 	if (transport == endpoint->transport)
    219 		cras_bt_endpoint_suspend(endpoint);
    220 
    221 	reply = dbus_message_new_method_return(message);
    222 	if (!reply)
    223 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
    224 	if (!dbus_connection_send(conn, reply, NULL))
    225 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
    226 
    227 	dbus_message_unref(reply);
    228 	return DBUS_HANDLER_RESULT_HANDLED;
    229 }
    230 
    231 static DBusHandlerResult cras_bt_endpoint_release(DBusConnection *conn,
    232 						  DBusMessage *message,
    233 						  void *arg)
    234 {
    235 	const char *endpoint_path;
    236 	struct cras_bt_endpoint *endpoint;
    237 	DBusMessage *reply;
    238 
    239 	syslog(LOG_DEBUG, "Release: %s",
    240 	       dbus_message_get_path(message));
    241 
    242 	endpoint_path = dbus_message_get_path(message);
    243 	endpoint = cras_bt_endpoint_get(endpoint_path);
    244 	if (!endpoint)
    245 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    246 
    247 	cras_bt_endpoint_suspend(endpoint);
    248 
    249 	reply = dbus_message_new_method_return(message);
    250 	if (!reply)
    251 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
    252 	if (!dbus_connection_send(conn, reply, NULL))
    253 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
    254 
    255 	dbus_message_unref(reply);
    256 	return DBUS_HANDLER_RESULT_HANDLED;
    257 }
    258 
    259 static DBusHandlerResult cras_bt_handle_endpoint_message(DBusConnection *conn,
    260 							 DBusMessage *message,
    261 							 void *arg)
    262 {
    263 	syslog(LOG_DEBUG, "Endpoint message: %s %s %s",
    264 	       dbus_message_get_path(message),
    265 	       dbus_message_get_interface(message),
    266 	       dbus_message_get_member(message));
    267 
    268 	if (dbus_message_is_method_call(message,
    269 					DBUS_INTERFACE_INTROSPECTABLE,
    270 					"Introspect")) {
    271 		DBusMessage *reply;
    272 		const char *xml = ENDPOINT_INTROSPECT_XML;
    273 
    274 		reply = dbus_message_new_method_return(message);
    275 		if (!reply)
    276 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
    277 		if (!dbus_message_append_args(reply,
    278 					      DBUS_TYPE_STRING, &xml,
    279 					      DBUS_TYPE_INVALID))
    280 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
    281 		if (!dbus_connection_send(conn, reply, NULL))
    282 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
    283 
    284 		dbus_message_unref(reply);
    285 		return DBUS_HANDLER_RESULT_HANDLED;
    286 
    287 	} else if (dbus_message_is_method_call(message,
    288 					       BLUEZ_INTERFACE_MEDIA_ENDPOINT,
    289 					       "SetConfiguration")) {
    290 		return cras_bt_endpoint_set_configuration(conn, message, arg);
    291 
    292 	} else if (dbus_message_is_method_call(message,
    293 					       BLUEZ_INTERFACE_MEDIA_ENDPOINT,
    294 					       "SelectConfiguration")) {
    295 		return cras_bt_endpoint_select_configuration(
    296 			conn, message, arg);
    297 
    298 	} else if (dbus_message_is_method_call(message,
    299 					       BLUEZ_INTERFACE_MEDIA_ENDPOINT,
    300 					       "ClearConfiguration")) {
    301 		return cras_bt_endpoint_clear_configuration(conn, message, arg);
    302 
    303 	} else if (dbus_message_is_method_call(message,
    304 					       BLUEZ_INTERFACE_MEDIA_ENDPOINT,
    305 					       "Release")) {
    306 		return cras_bt_endpoint_release(conn, message, arg);
    307 
    308 	} else {
    309 		syslog(LOG_DEBUG, "%s: %s.%s: Unknown MediaEndpoint message",
    310 		       dbus_message_get_path(message),
    311 		       dbus_message_get_interface(message),
    312 		       dbus_message_get_member(message));
    313 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    314 	}
    315 }
    316 
    317 
    318 static void cras_bt_on_register_endpoint(DBusPendingCall *pending_call,
    319 					 void *data)
    320 {
    321 	DBusMessage *reply;
    322 
    323 	reply = dbus_pending_call_steal_reply(pending_call);
    324 	dbus_pending_call_unref(pending_call);
    325 
    326 	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
    327 		syslog(LOG_WARNING, "RegisterEndpoint returned error: %s",
    328 		       dbus_message_get_error_name(reply));
    329 		dbus_message_unref(reply);
    330 		return;
    331 	}
    332 
    333 	dbus_message_unref(reply);
    334 }
    335 
    336 int cras_bt_register_endpoint(DBusConnection *conn,
    337 			      const struct cras_bt_adapter *adapter,
    338 			      struct cras_bt_endpoint *endpoint)
    339 {
    340 	const char *adapter_path, *key;
    341 	DBusMessage *method_call;
    342 	DBusMessageIter message_iter;
    343 	DBusMessageIter properties_array_iter, properties_dict_iter;
    344 	DBusMessageIter variant_iter, bytes_iter;
    345 	DBusPendingCall *pending_call;
    346 	char buf[4];
    347 	void *capabilities = buf;
    348 	int len = sizeof(buf);
    349 	int error;
    350 
    351 	error = endpoint->get_capabilities(endpoint, capabilities, &len);
    352 	if (error < 0)
    353 		return error;
    354 
    355 	adapter_path = cras_bt_adapter_object_path(adapter);
    356 
    357 	method_call = dbus_message_new_method_call(BLUEZ_SERVICE,
    358 						   adapter_path,
    359 						   BLUEZ_INTERFACE_MEDIA,
    360 						   "RegisterEndpoint");
    361 	if (!method_call)
    362 		return -ENOMEM;
    363 
    364 	dbus_message_iter_init_append(method_call, &message_iter);
    365 	dbus_message_iter_append_basic(&message_iter,
    366 				       DBUS_TYPE_OBJECT_PATH,
    367 				       &endpoint->object_path);
    368 
    369 	dbus_message_iter_open_container(&message_iter,
    370 					 DBUS_TYPE_ARRAY,
    371 					 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
    372 					 DBUS_TYPE_STRING_AS_STRING
    373 					 DBUS_TYPE_VARIANT_AS_STRING
    374 					 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
    375 					 &properties_array_iter);
    376 
    377 	key = "UUID";
    378 	dbus_message_iter_open_container(&properties_array_iter,
    379 					 DBUS_TYPE_DICT_ENTRY, NULL,
    380 					 &properties_dict_iter);
    381 	dbus_message_iter_append_basic(&properties_dict_iter,
    382 				       DBUS_TYPE_STRING, &key);
    383 	dbus_message_iter_open_container(&properties_dict_iter,
    384 					 DBUS_TYPE_VARIANT,
    385 					 DBUS_TYPE_STRING_AS_STRING,
    386 					 &variant_iter);
    387 	dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING,
    388 				       &endpoint->uuid);
    389 	dbus_message_iter_close_container(&properties_dict_iter, &variant_iter);
    390 	dbus_message_iter_close_container(&properties_array_iter,
    391 					  &properties_dict_iter);
    392 
    393 	key = "Codec";
    394 	dbus_message_iter_open_container(&properties_array_iter,
    395 					 DBUS_TYPE_DICT_ENTRY, NULL,
    396 					 &properties_dict_iter);
    397 	dbus_message_iter_append_basic(&properties_dict_iter,
    398 				       DBUS_TYPE_STRING, &key);
    399 	dbus_message_iter_open_container(&properties_dict_iter,
    400 					 DBUS_TYPE_VARIANT,
    401 					 DBUS_TYPE_BYTE_AS_STRING,
    402 					 &variant_iter);
    403 	dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_BYTE,
    404 				       &endpoint->codec);
    405 	dbus_message_iter_close_container(&properties_dict_iter, &variant_iter);
    406 	dbus_message_iter_close_container(&properties_array_iter,
    407 					  &properties_dict_iter);
    408 
    409 	key = "Capabilities";
    410 	dbus_message_iter_open_container(&properties_array_iter,
    411 					 DBUS_TYPE_DICT_ENTRY, NULL,
    412 					 &properties_dict_iter);
    413 	dbus_message_iter_append_basic(&properties_dict_iter,
    414 				       DBUS_TYPE_STRING, &key);
    415 	dbus_message_iter_open_container(&properties_dict_iter,
    416 					 DBUS_TYPE_VARIANT,
    417 					 DBUS_TYPE_ARRAY_AS_STRING
    418 					 DBUS_TYPE_BYTE_AS_STRING,
    419 					 &variant_iter);
    420 	dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
    421 					 DBUS_TYPE_BYTE_AS_STRING,
    422 					 &bytes_iter);
    423 	dbus_message_iter_append_fixed_array(&bytes_iter, DBUS_TYPE_BYTE,
    424 					     &capabilities, len);
    425 	dbus_message_iter_close_container(&variant_iter, &bytes_iter);
    426 	dbus_message_iter_close_container(&properties_dict_iter, &variant_iter);
    427 	dbus_message_iter_close_container(&properties_array_iter,
    428 					  &properties_dict_iter);
    429 
    430 	dbus_message_iter_close_container(&message_iter,
    431 					  &properties_array_iter);
    432 
    433 	if (!dbus_connection_send_with_reply(conn, method_call, &pending_call,
    434 					     DBUS_TIMEOUT_USE_DEFAULT)) {
    435 		dbus_message_unref(method_call);
    436 		return -ENOMEM;
    437 	}
    438 
    439 	dbus_message_unref(method_call);
    440 	if (!pending_call)
    441 		return -EIO;
    442 
    443 	if (!dbus_pending_call_set_notify(pending_call,
    444 					  cras_bt_on_register_endpoint,
    445 					  NULL, NULL)) {
    446 		dbus_pending_call_cancel(pending_call);
    447 		dbus_pending_call_unref(pending_call);
    448 		return -ENOMEM;
    449 	}
    450 
    451 	return 0;
    452 }
    453 
    454 static void cras_bt_on_unregister_endpoint(DBusPendingCall *pending_call,
    455 					   void *data)
    456 {
    457 	DBusMessage *reply;
    458 
    459 	reply = dbus_pending_call_steal_reply(pending_call);
    460 	dbus_pending_call_unref(pending_call);
    461 
    462 	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
    463 		syslog(LOG_WARNING, "UnregisterEndpoint returned error: %s",
    464 		       dbus_message_get_error_name(reply));
    465 		dbus_message_unref(reply);
    466 		return;
    467 	}
    468 
    469 	dbus_message_unref(reply);
    470 }
    471 
    472 int cras_bt_unregister_endpoint(DBusConnection *conn,
    473 				const struct cras_bt_adapter *adapter,
    474 				struct cras_bt_endpoint *endpoint)
    475 {
    476 	const char *adapter_path;
    477 	DBusMessage *method_call;
    478 	DBusPendingCall *pending_call;
    479 
    480 	adapter_path = cras_bt_adapter_object_path(adapter);
    481 
    482 	method_call = dbus_message_new_method_call(BLUEZ_SERVICE,
    483 						   adapter_path,
    484 						   BLUEZ_INTERFACE_MEDIA,
    485 						   "UnregisterEndpoint");
    486 	if (!method_call)
    487 		return -ENOMEM;
    488 
    489 	if (!dbus_message_append_args(method_call,
    490 				      DBUS_TYPE_OBJECT_PATH,
    491 				      &endpoint->object_path,
    492 				      DBUS_TYPE_INVALID))
    493 		return -ENOMEM;
    494 
    495 	if (!dbus_connection_send_with_reply(conn, method_call, &pending_call,
    496 					     DBUS_TIMEOUT_USE_DEFAULT)) {
    497 		dbus_message_unref(method_call);
    498 		return -ENOMEM;
    499 	}
    500 
    501 	dbus_message_unref(method_call);
    502 	if (!pending_call)
    503 		return -EIO;
    504 
    505 	if (!dbus_pending_call_set_notify(pending_call,
    506 					  cras_bt_on_unregister_endpoint,
    507 					  NULL, NULL)) {
    508 		dbus_pending_call_cancel(pending_call);
    509 		dbus_pending_call_unref(pending_call);
    510 		return -ENOMEM;
    511 	}
    512 
    513 	return 0;
    514 }
    515 
    516 
    517 /* Available endpoints */
    518 static struct cras_bt_endpoint *endpoints;
    519 
    520 int cras_bt_register_endpoints(DBusConnection *conn,
    521 			       const struct cras_bt_adapter *adapter)
    522 {
    523 	struct cras_bt_endpoint *endpoint;
    524 
    525 	DL_FOREACH(endpoints, endpoint)
    526 		cras_bt_register_endpoint(conn, adapter, endpoint);
    527 
    528 	return 0;
    529 }
    530 
    531 int cras_bt_endpoint_add(DBusConnection *conn,
    532 			 struct cras_bt_endpoint *endpoint)
    533 {
    534 	static const DBusObjectPathVTable endpoint_vtable = {
    535 		.message_function = cras_bt_handle_endpoint_message
    536 	};
    537 
    538 	DBusError dbus_error;
    539 	struct cras_bt_adapter **adapters;
    540 	size_t num_adapters, i;
    541 
    542 	DL_APPEND(endpoints, endpoint);
    543 
    544 	dbus_error_init(&dbus_error);
    545 
    546 	if (!dbus_connection_register_object_path(conn,
    547 						  endpoint->object_path,
    548 						  &endpoint_vtable,
    549 						  &dbus_error)) {
    550 		syslog(LOG_WARNING,
    551 		       "Couldn't register Bluetooth endpoint: %s: %s",
    552 		       endpoint->object_path, dbus_error.message);
    553 		dbus_error_free(&dbus_error);
    554 		return -ENOMEM;
    555 	}
    556 
    557 	num_adapters = cras_bt_adapter_get_list(&adapters);
    558 	for (i = 0; i < num_adapters; ++i)
    559 		cras_bt_register_endpoint(conn, adapters[i], endpoint);
    560 	free(adapters);
    561 
    562 	return 0;
    563 }
    564 
    565 void cras_bt_endpoint_rm(DBusConnection *conn,
    566 			 struct cras_bt_endpoint *endpoint)
    567 {
    568 	struct cras_bt_adapter **adapters;
    569 	size_t num_adapters, i;
    570 
    571 	num_adapters = cras_bt_adapter_get_list(&adapters);
    572 	for (i = 0; i < num_adapters; ++i)
    573 		cras_bt_unregister_endpoint(conn, adapters[i], endpoint);
    574 	free(adapters);
    575 
    576 	dbus_connection_unregister_object_path(conn, endpoint->object_path);
    577 
    578 	DL_DELETE(endpoints, endpoint);
    579 }
    580 
    581 void cras_bt_endpoint_reset()
    582 {
    583 	struct cras_bt_endpoint *endpoint;
    584 
    585 	DL_FOREACH(endpoints, endpoint)
    586 		cras_bt_endpoint_suspend(endpoint);
    587 }
    588 
    589 struct cras_bt_endpoint *cras_bt_endpoint_get(const char *object_path)
    590 {
    591 	struct cras_bt_endpoint *endpoint;
    592 
    593 	DL_FOREACH(endpoints, endpoint) {
    594 		if (strcmp(endpoint->object_path, object_path) == 0)
    595 			return endpoint;
    596 	}
    597 
    598 	return NULL;
    599 }
    600