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