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 
cras_bt_endpoint_suspend(struct cras_bt_endpoint * endpoint)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 
cras_bt_endpoint_set_configuration(DBusConnection * conn,DBusMessage * message,void * arg)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 
cras_bt_endpoint_select_configuration(DBusConnection * conn,DBusMessage * message,void * arg)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 
cras_bt_endpoint_clear_configuration(DBusConnection * conn,DBusMessage * message,void * arg)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 
cras_bt_endpoint_release(DBusConnection * conn,DBusMessage * message,void * arg)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 
cras_bt_handle_endpoint_message(DBusConnection * conn,DBusMessage * message,void * arg)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 
cras_bt_on_register_endpoint(DBusPendingCall * pending_call,void * data)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 
cras_bt_register_endpoint(DBusConnection * conn,const struct cras_bt_adapter * adapter,struct cras_bt_endpoint * endpoint)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 
cras_bt_on_unregister_endpoint(DBusPendingCall * pending_call,void * data)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 
cras_bt_unregister_endpoint(DBusConnection * conn,const struct cras_bt_adapter * adapter,struct cras_bt_endpoint * endpoint)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 
cras_bt_register_endpoints(DBusConnection * conn,const struct cras_bt_adapter * adapter)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 
cras_bt_endpoint_add(DBusConnection * conn,struct cras_bt_endpoint * endpoint)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 
cras_bt_endpoint_rm(DBusConnection * conn,struct cras_bt_endpoint * endpoint)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 
cras_bt_endpoint_reset()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 
cras_bt_endpoint_get(const char * object_path)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