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