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