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 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 68 void cras_bt_transport_set_endpoint(struct cras_bt_transport *transport, 69 struct cras_bt_endpoint *endpoint) { 70 transport->endpoint = endpoint; 71 } 72 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 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 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 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 134 const char *cras_bt_transport_object_path( 135 const struct cras_bt_transport *transport) 136 { 137 return transport->object_path; 138 } 139 140 struct cras_bt_device *cras_bt_transport_device( 141 const struct cras_bt_transport *transport) 142 { 143 return transport->device; 144 } 145 146 enum cras_bt_device_profile cras_bt_transport_profile( 147 const struct cras_bt_transport *transport) 148 { 149 return transport->profile; 150 } 151 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 164 enum cras_bt_transport_state cras_bt_transport_state( 165 const struct cras_bt_transport *transport) 166 { 167 return transport->state; 168 } 169 170 int cras_bt_transport_fd(const struct cras_bt_transport *transport) 171 { 172 return transport->fd; 173 } 174 175 uint16_t cras_bt_transport_write_mtu(const struct cras_bt_transport *transport) 176 { 177 return transport->write_mtu; 178 } 179 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 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. */ 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 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 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 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 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 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. */ 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 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