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
8 #include <errno.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <syslog.h>
13 #include <unistd.h>
14
15 #include "cras_bt_device.h"
16 #include "cras_bt_endpoint.h"
17 #include "cras_bt_log.h"
18 #include "cras_bt_transport.h"
19 #include "cras_bt_constants.h"
20 #include "utlist.h"
21
22 struct cras_bt_transport {
23 DBusConnection *conn;
24 char *object_path;
25 struct cras_bt_device *device;
26 enum cras_bt_device_profile profile;
27 int codec;
28 void *configuration;
29 int configuration_len;
30 enum cras_bt_transport_state state;
31 int fd;
32 uint16_t read_mtu;
33 uint16_t write_mtu;
34 int volume;
35 int removed;
36
37 struct cras_bt_endpoint *endpoint;
38 struct cras_bt_transport *prev, *next;
39 };
40
41 static struct cras_bt_transport *transports;
42
cras_bt_transport_create(DBusConnection * conn,const char * object_path)43 struct cras_bt_transport *cras_bt_transport_create(DBusConnection *conn,
44 const char *object_path)
45 {
46 struct cras_bt_transport *transport;
47
48 transport = calloc(1, sizeof(*transport));
49 if (transport == NULL)
50 return NULL;
51
52 transport->object_path = strdup(object_path);
53 if (transport->object_path == NULL) {
54 free(transport);
55 return NULL;
56 }
57
58 transport->conn = conn;
59 dbus_connection_ref(transport->conn);
60
61 transport->fd = -1;
62 transport->volume = -1;
63
64 DL_APPEND(transports, transport);
65
66 return transport;
67 }
68
cras_bt_transport_set_endpoint(struct cras_bt_transport * transport,struct cras_bt_endpoint * endpoint)69 void cras_bt_transport_set_endpoint(struct cras_bt_transport *transport,
70 struct cras_bt_endpoint *endpoint)
71 {
72 transport->endpoint = endpoint;
73 }
74
cras_bt_transport_is_removed(struct cras_bt_transport * transport)75 int cras_bt_transport_is_removed(struct cras_bt_transport *transport)
76 {
77 return transport->removed;
78 }
79
cras_bt_transport_remove(struct cras_bt_transport * transport)80 void cras_bt_transport_remove(struct cras_bt_transport *transport)
81 {
82 /*
83 * If the transport object is still associated with a valid
84 * endpoint. Flag it as removed and wait for the ClearConfiguration
85 * message from BT to actually suspend this A2DP connection and
86 * destroy the transport.
87 */
88 if (transport->endpoint)
89 transport->removed = 1;
90 else
91 cras_bt_transport_destroy(transport);
92 }
93
cras_bt_transport_destroy(struct cras_bt_transport * transport)94 void cras_bt_transport_destroy(struct cras_bt_transport *transport)
95 {
96 DL_DELETE(transports, transport);
97
98 dbus_connection_unref(transport->conn);
99
100 if (transport->fd >= 0)
101 close(transport->fd);
102
103 free(transport->object_path);
104 free(transport->configuration);
105 free(transport);
106 }
107
cras_bt_transport_reset()108 void cras_bt_transport_reset()
109 {
110 while (transports) {
111 syslog(LOG_INFO, "Bluetooth Transport: %s removed",
112 transports->object_path);
113 cras_bt_transport_destroy(transports);
114 }
115 }
116
cras_bt_transport_get(const char * object_path)117 struct cras_bt_transport *cras_bt_transport_get(const char *object_path)
118 {
119 struct cras_bt_transport *transport;
120
121 DL_FOREACH (transports, transport) {
122 if (strcmp(transport->object_path, object_path) == 0)
123 return transport;
124 }
125
126 return NULL;
127 }
128
129 size_t
cras_bt_transport_get_list(struct cras_bt_transport *** transport_list_out)130 cras_bt_transport_get_list(struct cras_bt_transport ***transport_list_out)
131 {
132 struct cras_bt_transport *transport;
133 struct cras_bt_transport **transport_list = NULL;
134 size_t num_transports = 0;
135
136 DL_FOREACH (transports, transport) {
137 struct cras_bt_transport **tmp;
138
139 tmp = realloc(transport_list,
140 sizeof(transport_list[0]) * (num_transports + 1));
141 if (!tmp) {
142 free(transport_list);
143 return -ENOMEM;
144 }
145
146 transport_list = tmp;
147 transport_list[num_transports++] = transport;
148 }
149
150 *transport_list_out = transport_list;
151 return num_transports;
152 }
153
154 const char *
cras_bt_transport_object_path(const struct cras_bt_transport * transport)155 cras_bt_transport_object_path(const struct cras_bt_transport *transport)
156 {
157 return transport->object_path;
158 }
159
160 struct cras_bt_device *
cras_bt_transport_device(const struct cras_bt_transport * transport)161 cras_bt_transport_device(const struct cras_bt_transport *transport)
162 {
163 return transport->device;
164 }
165
166 enum cras_bt_device_profile
cras_bt_transport_profile(const struct cras_bt_transport * transport)167 cras_bt_transport_profile(const struct cras_bt_transport *transport)
168 {
169 return transport->profile;
170 }
171
cras_bt_transport_configuration(const struct cras_bt_transport * transport,void * configuration,int len)172 int cras_bt_transport_configuration(const struct cras_bt_transport *transport,
173 void *configuration, int len)
174 {
175 if (len < transport->configuration_len)
176 return -ENOSPC;
177
178 memcpy(configuration, transport->configuration,
179 transport->configuration_len);
180
181 return 0;
182 }
183
184 enum cras_bt_transport_state
cras_bt_transport_state(const struct cras_bt_transport * transport)185 cras_bt_transport_state(const struct cras_bt_transport *transport)
186 {
187 return transport->state;
188 }
189
cras_bt_transport_fd(const struct cras_bt_transport * transport)190 int cras_bt_transport_fd(const struct cras_bt_transport *transport)
191 {
192 return transport->fd;
193 }
194
cras_bt_transport_write_mtu(const struct cras_bt_transport * transport)195 uint16_t cras_bt_transport_write_mtu(const struct cras_bt_transport *transport)
196 {
197 return transport->write_mtu;
198 }
199
200 static enum cras_bt_transport_state
cras_bt_transport_state_from_string(const char * value)201 cras_bt_transport_state_from_string(const char *value)
202 {
203 if (strcmp("idle", value) == 0)
204 return CRAS_BT_TRANSPORT_STATE_IDLE;
205 else if (strcmp("pending", value) == 0)
206 return CRAS_BT_TRANSPORT_STATE_PENDING;
207 else if (strcmp("active", value) == 0)
208 return CRAS_BT_TRANSPORT_STATE_ACTIVE;
209 else
210 return CRAS_BT_TRANSPORT_STATE_IDLE;
211 }
212
cras_bt_transport_state_changed(struct cras_bt_transport * transport)213 static void cras_bt_transport_state_changed(struct cras_bt_transport *transport)
214 {
215 if (transport->endpoint && transport->endpoint->transport_state_changed)
216 transport->endpoint->transport_state_changed(
217 transport->endpoint, transport);
218 }
219
220 /* Updates bt_device when certain transport property has changed. */
cras_bt_transport_update_device(struct cras_bt_transport * transport)221 static void cras_bt_transport_update_device(struct cras_bt_transport *transport)
222 {
223 if (!transport->device)
224 return;
225
226 /* When the transport has non-negaive volume, it means the remote
227 * BT audio devices supports AVRCP absolute volume. Set the flag in bt
228 * device to use hardware volume. Also map the volume value from 0-127
229 * to 0-100.
230 */
231 if (transport->volume != -1) {
232 cras_bt_device_set_use_hardware_volume(transport->device, 1);
233 cras_bt_device_update_hardware_volume(
234 transport->device, transport->volume * 100 / 127);
235 }
236 }
237
cras_bt_transport_update_properties(struct cras_bt_transport * transport,DBusMessageIter * properties_array_iter,DBusMessageIter * invalidated_array_iter)238 void cras_bt_transport_update_properties(struct cras_bt_transport *transport,
239 DBusMessageIter *properties_array_iter,
240 DBusMessageIter *invalidated_array_iter)
241 {
242 while (dbus_message_iter_get_arg_type(properties_array_iter) !=
243 DBUS_TYPE_INVALID) {
244 DBusMessageIter properties_dict_iter, variant_iter;
245 const char *key;
246 int type;
247
248 dbus_message_iter_recurse(properties_array_iter,
249 &properties_dict_iter);
250
251 dbus_message_iter_get_basic(&properties_dict_iter, &key);
252 dbus_message_iter_next(&properties_dict_iter);
253
254 dbus_message_iter_recurse(&properties_dict_iter, &variant_iter);
255 type = dbus_message_iter_get_arg_type(&variant_iter);
256
257 if (type == DBUS_TYPE_STRING) {
258 const char *value;
259
260 dbus_message_iter_get_basic(&variant_iter, &value);
261
262 if (strcmp(key, "UUID") == 0) {
263 transport->profile =
264 cras_bt_device_profile_from_uuid(value);
265
266 } else if (strcmp(key, "State") == 0) {
267 enum cras_bt_transport_state old_state =
268 transport->state;
269 transport->state =
270 cras_bt_transport_state_from_string(
271 value);
272 if (transport->state != old_state)
273 cras_bt_transport_state_changed(
274 transport);
275 }
276
277 } else if (type == DBUS_TYPE_BYTE) {
278 int value;
279
280 dbus_message_iter_get_basic(&variant_iter, &value);
281
282 if (strcmp(key, "Codec") == 0)
283 transport->codec = value;
284 } else if (type == DBUS_TYPE_OBJECT_PATH) {
285 const char *obj_path;
286
287 /* Property: object Device [readonly] */
288 dbus_message_iter_get_basic(&variant_iter, &obj_path);
289 transport->device = cras_bt_device_get(obj_path);
290 if (!transport->device) {
291 syslog(LOG_ERR,
292 "Device %s not found at update"
293 "transport properties",
294 obj_path);
295 transport->device = cras_bt_device_create(
296 transport->conn, obj_path);
297 cras_bt_transport_update_device(transport);
298 }
299 } else if (strcmp(dbus_message_iter_get_signature(&variant_iter),
300 "ay") == 0 &&
301 strcmp(key, "Configuration") == 0) {
302 DBusMessageIter value_iter;
303 char *value;
304 int len;
305
306 dbus_message_iter_recurse(&variant_iter, &value_iter);
307 dbus_message_iter_get_fixed_array(&value_iter, &value,
308 &len);
309
310 free(transport->configuration);
311 transport->configuration_len = 0;
312
313 transport->configuration = malloc(len);
314 if (transport->configuration) {
315 memcpy(transport->configuration, value, len);
316 transport->configuration_len = len;
317 }
318
319 } else if (strcmp(key, "Volume") == 0) {
320 uint16_t volume;
321
322 dbus_message_iter_get_basic(&variant_iter, &volume);
323 transport->volume = volume;
324 cras_bt_transport_update_device(transport);
325 }
326
327 dbus_message_iter_next(properties_array_iter);
328 }
329
330 while (invalidated_array_iter &&
331 dbus_message_iter_get_arg_type(invalidated_array_iter) !=
332 DBUS_TYPE_INVALID) {
333 const char *key;
334
335 dbus_message_iter_get_basic(invalidated_array_iter, &key);
336
337 if (strcmp(key, "Device") == 0) {
338 transport->device = NULL;
339 } else if (strcmp(key, "UUID") == 0) {
340 transport->profile = 0;
341 } else if (strcmp(key, "State") == 0) {
342 transport->state = CRAS_BT_TRANSPORT_STATE_IDLE;
343 } else if (strcmp(key, "Codec") == 0) {
344 transport->codec = 0;
345 } else if (strcmp(key, "Configuration") == 0) {
346 free(transport->configuration);
347 transport->configuration = NULL;
348 transport->configuration_len = 0;
349 }
350
351 dbus_message_iter_next(invalidated_array_iter);
352 }
353 }
354
on_transport_volume_set(DBusPendingCall * pending_call,void * data)355 static void on_transport_volume_set(DBusPendingCall *pending_call, void *data)
356 {
357 DBusMessage *reply;
358
359 reply = dbus_pending_call_steal_reply(pending_call);
360 dbus_pending_call_unref(pending_call);
361
362 if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
363 syslog(LOG_ERR, "Set absolute volume returned error: %s",
364 dbus_message_get_error_name(reply));
365 dbus_message_unref(reply);
366 }
367
cras_bt_transport_set_volume(struct cras_bt_transport * transport,uint16_t volume)368 int cras_bt_transport_set_volume(struct cras_bt_transport *transport,
369 uint16_t volume)
370 {
371 const char *key = "Volume";
372 const char *interface = BLUEZ_INTERFACE_MEDIA_TRANSPORT;
373 DBusMessage *method_call;
374 DBusMessageIter message_iter, variant;
375 DBusPendingCall *pending_call;
376
377 method_call =
378 dbus_message_new_method_call(BLUEZ_SERVICE,
379 transport->object_path,
380 DBUS_INTERFACE_PROPERTIES, "Set");
381 if (!method_call)
382 return -ENOMEM;
383
384 dbus_message_iter_init_append(method_call, &message_iter);
385
386 dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_STRING,
387 &interface);
388 dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_STRING, &key);
389
390 dbus_message_iter_open_container(&message_iter, DBUS_TYPE_VARIANT,
391 DBUS_TYPE_UINT16_AS_STRING, &variant);
392 dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT16, &volume);
393 dbus_message_iter_close_container(&message_iter, &variant);
394
395 if (!dbus_connection_send_with_reply(transport->conn, method_call,
396 &pending_call,
397 DBUS_TIMEOUT_USE_DEFAULT)) {
398 dbus_message_unref(method_call);
399 return -ENOMEM;
400 }
401
402 dbus_message_unref(method_call);
403 if (!pending_call)
404 return -EIO;
405
406 if (!dbus_pending_call_set_notify(pending_call, on_transport_volume_set,
407 NULL, NULL)) {
408 dbus_pending_call_cancel(pending_call);
409 dbus_pending_call_unref(pending_call);
410 return -ENOMEM;
411 }
412
413 return 0;
414 }
415
cras_bt_transport_acquire(struct cras_bt_transport * transport)416 int cras_bt_transport_acquire(struct cras_bt_transport *transport)
417 {
418 DBusMessage *method_call, *reply;
419 DBusError dbus_error;
420 int rc = 0;
421
422 if (transport->fd >= 0)
423 return 0;
424
425 method_call = dbus_message_new_method_call(
426 BLUEZ_SERVICE, transport->object_path,
427 BLUEZ_INTERFACE_MEDIA_TRANSPORT, "Acquire");
428 if (!method_call)
429 return -ENOMEM;
430
431 dbus_error_init(&dbus_error);
432
433 reply = dbus_connection_send_with_reply_and_block(
434 transport->conn, method_call, DBUS_TIMEOUT_USE_DEFAULT,
435 &dbus_error);
436 if (!reply) {
437 syslog(LOG_ERR, "Failed to acquire transport %s: %s",
438 transport->object_path, dbus_error.message);
439 dbus_error_free(&dbus_error);
440 dbus_message_unref(method_call);
441 rc = -EIO;
442 goto acquire_fail;
443 }
444
445 dbus_message_unref(method_call);
446
447 if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
448 syslog(LOG_ERR, "Acquire returned error: %s",
449 dbus_message_get_error_name(reply));
450 dbus_message_unref(reply);
451 rc = -EIO;
452 goto acquire_fail;
453 }
454
455 if (!dbus_message_get_args(
456 reply, &dbus_error, DBUS_TYPE_UNIX_FD, &(transport->fd),
457 DBUS_TYPE_UINT16, &(transport->read_mtu), DBUS_TYPE_UINT16,
458 &(transport->write_mtu), DBUS_TYPE_INVALID)) {
459 syslog(LOG_ERR, "Bad Acquire reply received: %s",
460 dbus_error.message);
461 dbus_error_free(&dbus_error);
462 dbus_message_unref(reply);
463 rc = -EINVAL;
464 goto acquire_fail;
465 }
466
467 BTLOG(btlog, BT_TRANSPORT_ACQUIRE, 1, transport->fd);
468 dbus_message_unref(reply);
469 return 0;
470
471 acquire_fail:
472 BTLOG(btlog, BT_TRANSPORT_ACQUIRE, 0, 0);
473 return rc;
474 }
475
cras_bt_transport_try_acquire(struct cras_bt_transport * transport)476 int cras_bt_transport_try_acquire(struct cras_bt_transport *transport)
477 {
478 DBusMessage *method_call, *reply;
479 DBusError dbus_error;
480 int fd, read_mtu, write_mtu;
481
482 method_call = dbus_message_new_method_call(
483 BLUEZ_SERVICE, transport->object_path,
484 BLUEZ_INTERFACE_MEDIA_TRANSPORT, "TryAcquire");
485 if (!method_call)
486 return -ENOMEM;
487
488 dbus_error_init(&dbus_error);
489
490 reply = dbus_connection_send_with_reply_and_block(
491 transport->conn, method_call, DBUS_TIMEOUT_USE_DEFAULT,
492 &dbus_error);
493 if (!reply) {
494 syslog(LOG_ERR, "Failed to try acquire transport %s: %s",
495 transport->object_path, dbus_error.message);
496 dbus_error_free(&dbus_error);
497 dbus_message_unref(method_call);
498 return -EIO;
499 }
500
501 dbus_message_unref(method_call);
502
503 if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
504 syslog(LOG_ERR, "TryAcquire returned error: %s",
505 dbus_message_get_error_name(reply));
506 dbus_message_unref(reply);
507 return -EIO;
508 }
509
510 if (!dbus_message_get_args(reply, &dbus_error, DBUS_TYPE_UNIX_FD, &fd,
511 DBUS_TYPE_UINT16, &read_mtu,
512 DBUS_TYPE_UINT16, &write_mtu,
513 DBUS_TYPE_INVALID)) {
514 syslog(LOG_ERR, "Bad TryAcquire reply received: %s",
515 dbus_error.message);
516 dbus_error_free(&dbus_error);
517 dbus_message_unref(reply);
518 return -EINVAL;
519 }
520
521 /* Done TryAcquired the transport so it won't be released in bluez,
522 * no need for the new file descriptor so close it. */
523 if (transport->fd != fd)
524 close(fd);
525
526 dbus_message_unref(reply);
527 return 0;
528 }
529
530 /* Callback to trigger when transport release completed. */
cras_bt_on_transport_release(DBusPendingCall * pending_call,void * data)531 static void cras_bt_on_transport_release(DBusPendingCall *pending_call,
532 void *data)
533 {
534 DBusMessage *reply;
535
536 reply = dbus_pending_call_steal_reply(pending_call);
537 dbus_pending_call_unref(pending_call);
538
539 if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
540 syslog(LOG_WARNING, "Release transport returned error: %s",
541 dbus_message_get_error_name(reply));
542 dbus_message_unref(reply);
543 return;
544 }
545
546 dbus_message_unref(reply);
547 }
548
cras_bt_transport_release(struct cras_bt_transport * transport,unsigned int blocking)549 int cras_bt_transport_release(struct cras_bt_transport *transport,
550 unsigned int blocking)
551 {
552 DBusMessage *method_call, *reply;
553 DBusPendingCall *pending_call;
554 DBusError dbus_error;
555
556 if (transport->fd < 0)
557 return 0;
558
559 BTLOG(btlog, BT_TRANSPORT_RELEASE, transport->fd, 0);
560
561 /* Close the transport on our end no matter whether or not the server
562 * gives us an error.
563 */
564 close(transport->fd);
565 transport->fd = -1;
566
567 method_call = dbus_message_new_method_call(
568 BLUEZ_SERVICE, transport->object_path,
569 BLUEZ_INTERFACE_MEDIA_TRANSPORT, "Release");
570 if (!method_call)
571 return -ENOMEM;
572
573 if (blocking) {
574 dbus_error_init(&dbus_error);
575
576 reply = dbus_connection_send_with_reply_and_block(
577 transport->conn, method_call, DBUS_TIMEOUT_USE_DEFAULT,
578 &dbus_error);
579 if (!reply) {
580 syslog(LOG_ERR, "Failed to release transport %s: %s",
581 transport->object_path, dbus_error.message);
582 dbus_error_free(&dbus_error);
583 dbus_message_unref(method_call);
584 return -EIO;
585 }
586
587 dbus_message_unref(method_call);
588
589 if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
590 syslog(LOG_ERR, "Release returned error: %s",
591 dbus_message_get_error_name(reply));
592 dbus_message_unref(reply);
593 return -EIO;
594 }
595
596 dbus_message_unref(reply);
597 } else {
598 if (!dbus_connection_send_with_reply(
599 transport->conn, method_call, &pending_call,
600 DBUS_TIMEOUT_USE_DEFAULT)) {
601 dbus_message_unref(method_call);
602 return -ENOMEM;
603 }
604
605 dbus_message_unref(method_call);
606 if (!pending_call)
607 return -EIO;
608
609 if (!dbus_pending_call_set_notify(pending_call,
610 cras_bt_on_transport_release,
611 transport, NULL)) {
612 dbus_pending_call_cancel(pending_call);
613 dbus_pending_call_unref(pending_call);
614 return -ENOMEM;
615 }
616 }
617 return 0;
618 }
619