1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2014 Wim Taymans <wim.taymans at gmail.com> 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation; either version 2.1 of the 9 License, or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <pulse/util.h> 25 26#include <pulsecore/shared.h> 27#include <pulsecore/core-error.h> 28#include <pulsecore/core-util.h> 29#include <pulsecore/dbus-shared.h> 30#include <pulsecore/log.h> 31 32#include <errno.h> 33#include <sys/types.h> 34#include <sys/socket.h> 35 36#include <bluetooth/bluetooth.h> 37#include <bluetooth/sco.h> 38 39#include "bluez5-util.h" 40#include "bt-codec-msbc.h" 41 42struct pa_bluetooth_backend { 43 pa_core *core; 44 pa_dbus_connection *connection; 45 pa_bluetooth_discovery *discovery; 46 pa_hook_slot *adapter_uuids_changed_slot; 47 bool enable_shared_profiles; 48 bool enable_hsp_hs; 49 bool enable_hfp_hf; 50 51 PA_LLIST_HEAD(pa_dbus_pending, pending); 52}; 53 54struct transport_data { 55 int rfcomm_fd; 56 pa_io_event *rfcomm_io; 57 int sco_fd; 58 pa_io_event *sco_io; 59 pa_mainloop_api *mainloop; 60}; 61 62struct hfp_config { 63 uint32_t capabilities; 64 int state; 65 bool support_codec_negotiation; 66 bool support_msbc; 67 bool supports_indicators; 68 int selected_codec; 69}; 70 71/* 72 * the separate hansfree headset (HF) and Audio Gateway (AG) features 73 */ 74enum hfp_hf_features { 75 HFP_HF_EC_NR = 0, 76 HFP_HF_CALL_WAITING = 1, 77 HFP_HF_CLI = 2, 78 HFP_HF_VR = 3, 79 HFP_HF_RVOL = 4, 80 HFP_HF_ESTATUS = 5, 81 HFP_HF_ECALL = 6, 82 HFP_HF_CODECS = 7, 83 HFP_HF_INDICATORS = 8, 84}; 85 86enum hfp_ag_features { 87 HFP_AG_THREE_WAY = 0, 88 HFP_AG_EC_NR = 1, 89 HFP_AG_VR = 2, 90 HFP_AG_RING = 3, 91 HFP_AG_NUM_TAG = 4, 92 HFP_AG_REJECT = 5, 93 HFP_AG_ESTATUS = 6, 94 HFP_AG_ECALL = 7, 95 HFP_AG_EERR = 8, 96 HFP_AG_CODECS = 9, 97 HFP_AG_INDICATORS = 10, 98}; 99 100/* gateway features we support, which is as little as we can get away with */ 101static uint32_t hfp_features = 102 /* HFP 1.6 requires this */ 103 (1 << HFP_AG_ESTATUS ) | (1 << HFP_AG_CODECS) | (1 << HFP_AG_INDICATORS); 104 105#define HSP_AG_PROFILE "/Profile/HSPAGProfile" 106#define HFP_AG_PROFILE "/Profile/HFPAGProfile" 107#define HSP_HS_PROFILE "/Profile/HSPHSProfile" 108 109/* RFCOMM channel for HSP headset role 110 * The choice seems to be a bit arbitrary -- it looks like at least channels 2, 4 and 5 also work*/ 111#define HSP_HS_DEFAULT_CHANNEL 3 112 113/* Total number of trying to reconnect */ 114#define SCO_RECONNECTION_COUNT 3 115 116#define PROFILE_INTROSPECT_XML \ 117 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ 118 "<node>" \ 119 " <interface name=\"" BLUEZ_PROFILE_INTERFACE "\">" \ 120 " <method name=\"Release\">" \ 121 " </method>" \ 122 " <method name=\"RequestDisconnection\">" \ 123 " <arg name=\"device\" direction=\"in\" type=\"o\"/>" \ 124 " </method>" \ 125 " <method name=\"NewConnection\">" \ 126 " <arg name=\"device\" direction=\"in\" type=\"o\"/>" \ 127 " <arg name=\"fd\" direction=\"in\" type=\"h\"/>" \ 128 " <arg name=\"opts\" direction=\"in\" type=\"a{sv}\"/>" \ 129 " </method>" \ 130 " </interface>" \ 131 " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">" \ 132 " <method name=\"Introspect\">" \ 133 " <arg name=\"data\" type=\"s\" direction=\"out\"/>" \ 134 " </method>" \ 135 " </interface>" \ 136 "</node>" 137 138static pa_volume_t hsp_gain_to_volume(uint16_t gain) { 139 pa_volume_t volume = (pa_volume_t) (( 140 gain * PA_VOLUME_NORM 141 /* Round to closest by adding half the denominator */ 142 + HSP_MAX_GAIN / 2 143 ) / HSP_MAX_GAIN); 144 145 if (volume > PA_VOLUME_NORM) 146 volume = PA_VOLUME_NORM; 147 148 return volume; 149} 150 151static uint16_t volume_to_hsp_gain(pa_volume_t volume) { 152 uint16_t gain = volume * HSP_MAX_GAIN / PA_VOLUME_NORM; 153 154 if (gain > HSP_MAX_GAIN) 155 gain = HSP_MAX_GAIN; 156 157 return gain; 158} 159 160static bool is_peer_audio_gateway(pa_bluetooth_profile_t peer_profile) { 161 switch(peer_profile) { 162 case PA_BLUETOOTH_PROFILE_HFP_HF: 163 case PA_BLUETOOTH_PROFILE_HSP_HS: 164 return false; 165 case PA_BLUETOOTH_PROFILE_HFP_AG: 166 case PA_BLUETOOTH_PROFILE_HSP_AG: 167 return true; 168 default: 169 pa_assert_not_reached(); 170 } 171} 172 173static bool is_pulseaudio_audio_gateway(pa_bluetooth_profile_t peer_profile) { 174 return !is_peer_audio_gateway(peer_profile); 175} 176 177static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_backend *backend, DBusMessage *m, 178 DBusPendingCallNotifyFunction func, void *call_data) { 179 180 pa_dbus_pending *p; 181 DBusPendingCall *call; 182 183 pa_assert(backend); 184 pa_assert(m); 185 186 pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(backend->connection), m, &call, -1)); 187 188 p = pa_dbus_pending_new(pa_dbus_connection_get(backend->connection), m, call, backend, call_data); 189 PA_LLIST_PREPEND(pa_dbus_pending, backend->pending, p); 190 dbus_pending_call_set_notify(call, func, p, NULL); 191 192 return p; 193} 194 195static void rfcomm_fmt_write(int fd, const char* fmt_line, const char *fmt_command, va_list ap) 196{ 197 size_t len; 198 char buf[512]; 199 char command[512]; 200 201 pa_vsnprintf(command, sizeof(command), fmt_command, ap); 202 203 pa_log_debug("RFCOMM >> %s", command); 204 205 len = pa_snprintf(buf, sizeof(buf), fmt_line, command); 206 207 /* we ignore any errors, it's not critical and real errors should 208 * be caught with the HANGUP and ERROR events handled above */ 209 210 if ((size_t)write(fd, buf, len) != len) 211 pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno)); 212} 213 214/* The format of COMMAND line sent from HS to AG is COMMAND<cr> */ 215static void rfcomm_write_command(int fd, const char *fmt, ...) 216{ 217 va_list ap; 218 219 va_start(ap, fmt); 220 rfcomm_fmt_write(fd, "%s\r", fmt, ap); 221 va_end(ap); 222} 223 224/* The format of RESPONSE line sent from AG to HS is <cr><lf>RESPONSE<cr><lf> */ 225static void rfcomm_write_response(int fd, const char *fmt, ...) 226{ 227 va_list ap; 228 229 va_start(ap, fmt); 230 rfcomm_fmt_write(fd, "\r\n%s\r\n", fmt, ap); 231 va_end(ap); 232} 233 234static int sco_setsockopt_enable_bt_voice(pa_bluetooth_transport *t, int fd) { 235 /* the mSBC codec requires a special transparent eSCO connection */ 236 struct bt_voice voice; 237 238 memset(&voice, 0, sizeof(voice)); 239 voice.setting = BT_VOICE_TRANSPARENT; 240 if (setsockopt(fd, SOL_BLUETOOTH, BT_VOICE, &voice, sizeof(voice)) < 0) { 241 pa_log_error("sockopt(): %s", pa_cstrerror(errno)); 242 return -1; 243 } 244 pa_log_info("Enabled BT_VOICE_TRANSPARENT connection for mSBC"); 245 return 0; 246} 247 248static int sco_do_connect(pa_bluetooth_transport *t) { 249 pa_bluetooth_device *d = t->device; 250 struct sockaddr_sco addr; 251 socklen_t len; 252 int err, i; 253 int sock; 254 bdaddr_t src; 255 bdaddr_t dst; 256 const char *src_addr, *dst_addr; 257 258 src_addr = d->adapter->address; 259 dst_addr = d->address; 260 261 /* don't use ba2str to avoid -lbluetooth */ 262 for (i = 5; i >= 0; i--, src_addr += 3) 263 src.b[i] = strtol(src_addr, NULL, 16); 264 for (i = 5; i >= 0; i--, dst_addr += 3) 265 dst.b[i] = strtol(dst_addr, NULL, 16); 266 267 sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); 268 if (sock < 0) { 269 pa_log_error("socket(SEQPACKET, SCO) %s", pa_cstrerror(errno)); 270 return -1; 271 } 272 273 len = sizeof(addr); 274 memset(&addr, 0, len); 275 addr.sco_family = AF_BLUETOOTH; 276 bacpy(&addr.sco_bdaddr, &src); 277 278 if (bind(sock, (struct sockaddr *) &addr, len) < 0) { 279 pa_log_error("bind(): %s", pa_cstrerror(errno)); 280 goto fail_close; 281 } 282 283 if (t->setsockopt && t->setsockopt(t, sock) < 0) 284 goto fail_close; 285 286 memset(&addr, 0, len); 287 addr.sco_family = AF_BLUETOOTH; 288 bacpy(&addr.sco_bdaddr, &dst); 289 290 pa_log_info("doing connect"); 291 err = connect(sock, (struct sockaddr *) &addr, len); 292 if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) { 293 pa_log_error("connect(): %s", pa_cstrerror(errno)); 294 goto fail_close; 295 } 296 return sock; 297 298fail_close: 299 close(sock); 300 return -1; 301} 302 303static int sco_do_accept(pa_bluetooth_transport *t) { 304 struct transport_data *trd = t->userdata; 305 struct sockaddr_sco addr; 306 socklen_t optlen; 307 int sock; 308 309 memset(&addr, 0, sizeof(addr)); 310 optlen = sizeof(addr); 311 312 pa_log_info ("doing accept"); 313 sock = accept(trd->sco_fd, (struct sockaddr *) &addr, &optlen); 314 if (sock < 0) { 315 if (errno != EAGAIN) 316 pa_log_error("accept(): %s", pa_cstrerror(errno)); 317 goto fail; 318 } 319 return sock; 320 321fail: 322 return -1; 323} 324 325static int sco_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) { 326 int sock; 327 socklen_t len; 328 int i; 329 330 if (optional) 331 sock = sco_do_accept(t); 332 else { 333 for (i = 0; i < SCO_RECONNECTION_COUNT; i++) { 334 sock = sco_do_connect(t); 335 336 if (sock < 0) { 337 pa_log_debug("err is %s and reconnection count is %d", pa_cstrerror(errno), i); 338 pa_msleep(300); 339 continue; 340 } else 341 break; 342 } 343 } 344 345 if (sock < 0) 346 goto fail; 347 348 /* The correct block size should take into account the SCO MTU from 349 * the Bluetooth adapter and (for adapters in the USB bus) the MxPS 350 * value from the Isoc USB endpoint in use by btusb and should be 351 * made available to userspace by the Bluetooth kernel subsystem. 352 * 353 * Set initial MTU to max known payload length of HCI packet 354 * in USB Alternate Setting 5 (144 bytes) 355 * See also pa_bluetooth_transport::last_read_size handling 356 * and comment about MTU size in bt_prepare_encoder_buffer() 357 */ 358 if (imtu) *imtu = 144; 359 if (omtu) *omtu = 144; 360 361 if (t->device->autodetect_mtu) { 362 struct sco_options sco_opt; 363 364 len = sizeof(sco_opt); 365 memset(&sco_opt, 0, len); 366 367 if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) 368 pa_log_warn("getsockopt(SCO_OPTIONS) failed, loading defaults"); 369 else { 370 pa_log_debug("autodetected imtu = omtu = %u", sco_opt.mtu); 371 if (imtu) *imtu = sco_opt.mtu; 372 if (omtu) *omtu = sco_opt.mtu; 373 } 374 } 375 376 return sock; 377 378fail: 379 return -1; 380} 381 382static void sco_release_cb(pa_bluetooth_transport *t) { 383 pa_log_info("Transport %s released", t->path); 384 /* device will close the SCO socket for us */ 385} 386 387static ssize_t sco_transport_write(pa_bluetooth_transport *t, int fd, const void* buffer, size_t size, size_t write_mtu) { 388 ssize_t l = 0; 389 size_t written = 0; 390 size_t write_size; 391 392 pa_assert(t); 393 394 /* since SCO setup is symmetric, fix write MTU to be size of last read packet */ 395 if (t->last_read_size) 396 write_mtu = PA_MIN(t->last_read_size, write_mtu); 397 398 /* if encoder buffer has less data than required to make complete packet */ 399 if (size < write_mtu) 400 return 0; 401 402 /* write out MTU sized chunks only */ 403 while (written < size) { 404 write_size = PA_MIN(size - written, write_mtu); 405 if (write_size < write_mtu) 406 break; 407 l = pa_write(fd, buffer + written, write_size, &t->stream_write_type); 408 if (l < 0) 409 break; 410 written += l; 411 } 412 413 if (l < 0) { 414 if (errno == EAGAIN) { 415 /* Hmm, apparently the socket was not writable, give up for now */ 416 pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a temporary connection loss."); 417 /* Drain write buffer */ 418 written = size; 419 } else if (errno == EINVAL && t->last_read_size == 0) { 420 /* Likely write_link_mtu is still wrong, retry after next successful read */ 421 pa_log_debug("got write EINVAL, next successful read should fix MTU"); 422 /* Drain write buffer */ 423 written = size; 424 } else { 425 pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno)); 426 /* Report error from write call */ 427 return -1; 428 } 429 } 430 431 /* if too much data left discard it all */ 432 if (size - written >= write_mtu) { 433 pa_log_warn("Wrote memory block to socket only partially! %lu written, discarding pending write size %lu larger than write_mtu %lu", 434 written, size, write_mtu); 435 /* Drain write buffer */ 436 written = size; 437 } 438 439 return written; 440} 441 442static void sco_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { 443 pa_bluetooth_transport *t = userdata; 444 445 pa_assert(io); 446 pa_assert(t); 447 448 if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { 449 pa_log_error("error listening SCO connection: %s", pa_cstrerror(errno)); 450 goto fail; 451 } 452 453 if (t->state != PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) { 454 pa_log_info("SCO incoming connection: changing state to PLAYING"); 455 pa_bluetooth_transport_set_state (t, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING); 456 } 457 458fail: 459 return; 460} 461 462static int sco_listen(pa_bluetooth_transport *t) { 463 struct transport_data *trd = t->userdata; 464 struct sockaddr_sco addr; 465 int sock, i; 466 bdaddr_t src; 467 const char *src_addr; 468 469 sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, BTPROTO_SCO); 470 if (sock < 0) { 471 pa_log_error("socket(SEQPACKET, SCO) %s", pa_cstrerror(errno)); 472 return -1; 473 } 474 475 src_addr = t->device->adapter->address; 476 477 /* don't use ba2str to avoid -lbluetooth */ 478 for (i = 5; i >= 0; i--, src_addr += 3) 479 src.b[i] = strtol(src_addr, NULL, 16); 480 481 /* Bind to local address */ 482 memset(&addr, 0, sizeof(addr)); 483 addr.sco_family = AF_BLUETOOTH; 484 bacpy(&addr.sco_bdaddr, &src); 485 486 if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 487 pa_log_error("bind(): %s", pa_cstrerror(errno)); 488 goto fail_close; 489 } 490 491 pa_log_info ("doing listen"); 492 if (listen(sock, 1) < 0) { 493 pa_log_error("listen(): %s", pa_cstrerror(errno)); 494 goto fail_close; 495 } 496 497 trd->sco_fd = sock; 498 trd->sco_io = trd->mainloop->io_new(trd->mainloop, sock, PA_IO_EVENT_INPUT, 499 sco_io_callback, t); 500 501 return sock; 502 503fail_close: 504 close(sock); 505 return -1; 506} 507 508static void register_profile_reply(DBusPendingCall *pending, void *userdata) { 509 DBusMessage *r; 510 pa_dbus_pending *p; 511 pa_bluetooth_backend *b; 512 pa_bluetooth_profile_t profile; 513 514 pa_assert(pending); 515 pa_assert_se(p = userdata); 516 pa_assert_se(b = p->context_data); 517 pa_assert_se(profile = (pa_bluetooth_profile_t)p->call_data); 518 pa_assert_se(r = dbus_pending_call_steal_reply(pending)); 519 520 if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) { 521 pa_log_info("Couldn't register profile %s because it is disabled in BlueZ", pa_bluetooth_profile_to_string(profile)); 522 profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_ACTIVE); 523 goto finish; 524 } 525 526 if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { 527 pa_log_error(BLUEZ_PROFILE_MANAGER_INTERFACE ".RegisterProfile() failed: %s: %s", dbus_message_get_error_name(r), 528 pa_dbus_get_error_message(r)); 529 profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_ACTIVE); 530 goto finish; 531 } 532 533 profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_REGISTERED); 534 535finish: 536 dbus_message_unref(r); 537 538 PA_LLIST_REMOVE(pa_dbus_pending, b->pending, p); 539 pa_dbus_pending_free(p); 540} 541 542static void register_profile(pa_bluetooth_backend *b, const char *object, const char *uuid, pa_bluetooth_profile_t profile) { 543 DBusMessage *m; 544 DBusMessageIter i, d; 545 dbus_bool_t autoconnect; 546 dbus_uint16_t version, chan; 547 548 pa_assert(profile_status_get(b->discovery, profile) == PA_BLUETOOTH_PROFILE_STATUS_ACTIVE); 549 550 pa_log_debug("Registering Profile %s %s", pa_bluetooth_profile_to_string(profile), uuid); 551 552 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/org/bluez", BLUEZ_PROFILE_MANAGER_INTERFACE, "RegisterProfile")); 553 554 dbus_message_iter_init_append(m, &i); 555 pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &object)); 556 pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &uuid)); 557 dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, 558 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING 559 DBUS_TYPE_STRING_AS_STRING 560 DBUS_TYPE_VARIANT_AS_STRING 561 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, 562 &d); 563 if (pa_bluetooth_uuid_is_hsp_hs(uuid)) { 564 /* In the headset role, the connection will only be initiated from the remote side */ 565 autoconnect = 0; 566 pa_dbus_append_basic_variant_dict_entry(&d, "AutoConnect", DBUS_TYPE_BOOLEAN, &autoconnect); 567 chan = HSP_HS_DEFAULT_CHANNEL; 568 pa_dbus_append_basic_variant_dict_entry(&d, "Channel", DBUS_TYPE_UINT16, &chan); 569 /* HSP version 1.2 */ 570 version = 0x0102; 571 pa_dbus_append_basic_variant_dict_entry(&d, "Version", DBUS_TYPE_UINT16, &version); 572 } 573 dbus_message_iter_close_container(&i, &d); 574 575 profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_REGISTERING); 576 send_and_add_to_pending(b, m, register_profile_reply, (void *)profile); 577} 578 579static void transport_put(pa_bluetooth_transport *t) 580{ 581 pa_bluetooth_transport_put(t); 582 583 pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile)); 584} 585 586static pa_volume_t set_sink_volume(pa_bluetooth_transport *t, pa_volume_t volume); 587static pa_volume_t set_source_volume(pa_bluetooth_transport *t, pa_volume_t volume); 588 589static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf) 590{ 591 struct hfp_config *c = t->config; 592 int indicator, val; 593 char str[5]; 594 const char *r; 595 size_t len; 596 const char *state; 597 598 /* first-time initialize selected codec to CVSD */ 599 if (c->selected_codec == 0) 600 c->selected_codec = 1; 601 602 /* stateful negotiation */ 603 if (c->state == 0 && sscanf(buf, "AT+BRSF=%d", &val) == 1) { 604 c->capabilities = val; 605 pa_log_info("HFP capabilities returns 0x%x", val); 606 rfcomm_write_response(fd, "+BRSF: %d", hfp_features); 607 c->supports_indicators = !!(1 << HFP_HF_INDICATORS); 608 c->state = 1; 609 610 return true; 611 } else if (sscanf(buf, "AT+BAC=%3s", str) == 1) { 612 c->support_msbc = false; 613 614 state = NULL; 615 616 /* check if codec id 2 (mSBC) is in the list of supported codecs */ 617 while ((r = pa_split_in_place(str, ",", &len, &state))) { 618 if (len == 1 && r[0] == '2') { 619 c->support_msbc = true; 620 break; 621 } 622 } 623 624 c->support_codec_negotiation = true; 625 626 if (c->state == 1) { 627 /* initial list of codecs supported by HF */ 628 } else { 629 /* HF sent updated list of codecs */ 630 } 631 632 /* no state change */ 633 634 return true; 635 } else if (c->state == 1 && pa_startswith(buf, "AT+CIND=?")) { 636 /* we declare minimal no indicators */ 637 rfcomm_write_response(fd, "+CIND: " 638 /* many indicators can be supported, only call and 639 * callheld are mandatory, so that's all we reply */ 640 "(\"service\",(0-1))," 641 "(\"call\",(0-1))," 642 "(\"callsetup\",(0-3))," 643 "(\"callheld\",(0-2))"); 644 c->state = 2; 645 646 return true; 647 } else if (c->state == 2 && pa_startswith(buf, "AT+CIND?")) { 648 rfcomm_write_response(fd, "+CIND: 0,0,0,0"); 649 c->state = 3; 650 651 return true; 652 } else if ((c->state == 2 || c->state == 3) && pa_startswith(buf, "AT+CMER=")) { 653 rfcomm_write_response(fd, "OK"); 654 655 if (c->support_codec_negotiation) { 656 if (c->support_msbc && pa_bluetooth_discovery_get_enable_msbc(t->device->discovery)) { 657 rfcomm_write_response(fd, "+BCS:2"); 658 c->state = 4; 659 } else { 660 rfcomm_write_response(fd, "+BCS:1"); 661 c->state = 4; 662 } 663 } else { 664 c->state = 5; 665 pa_bluetooth_transport_reconfigure(t, pa_bluetooth_get_hf_codec("CVSD"), sco_transport_write, NULL); 666 transport_put(t); 667 } 668 669 return false; 670 } else if (sscanf(buf, "AT+BCS=%d", &val)) { 671 if (val == 1) { 672 pa_bluetooth_transport_reconfigure(t, pa_bluetooth_get_hf_codec("CVSD"), sco_transport_write, NULL); 673 } else if (val == 2 && pa_bluetooth_discovery_get_enable_msbc(t->device->discovery)) { 674 pa_bluetooth_transport_reconfigure(t, pa_bluetooth_get_hf_codec("mSBC"), sco_transport_write, sco_setsockopt_enable_bt_voice); 675 } else { 676 pa_assert_fp(val != 1 && val != 2); 677 rfcomm_write_response(fd, "ERROR"); 678 return false; 679 } 680 681 c->selected_codec = val; 682 683 if (c->state == 4) { 684 c->state = 5; 685 pa_log_info("HFP negotiated codec %s", t->bt_codec->name); 686 transport_put(t); 687 } 688 689 return true; 690 } else if (c->supports_indicators && pa_startswith(buf, "AT+BIND=?")) { 691 // Support battery indication 692 rfcomm_write_response(fd, "+BIND: (2)"); 693 return true; 694 } else if (c->supports_indicators && pa_startswith(buf, "AT+BIND?")) { 695 // Battery indication is enabled 696 rfcomm_write_response(fd, "+BIND: 2,1"); 697 return true; 698 } else if (c->supports_indicators && pa_startswith(buf, "AT+BIND=")) { 699 // If this comma-separated list contains `2`, the HF is 700 // able to report values for the battery indicator. 701 return true; 702 } else if (c->supports_indicators && sscanf(buf, "AT+BIEV=%u,%u", &indicator, &val)) { 703 switch (indicator) { 704 case 2: 705 pa_log_notice("Battery Level: %d%%", val); 706 if (val < 0 || val > 100) { 707 pa_log_error("Battery HF indicator %d out of [0, 100] range", val); 708 rfcomm_write_response(fd, "ERROR"); 709 return false; 710 } 711 pa_bluetooth_device_report_battery_level(t->device, val, "HFP 1.7 HF indicator"); 712 break; 713 default: 714 pa_log_error("Unknown HF indicator %u", indicator); 715 rfcomm_write_response(fd, "ERROR"); 716 return false; 717 } 718 return true; 719 } if (c->state == 4) { 720 /* the ack for the codec setting may take a while. we need 721 * to reply OK to everything else until then */ 722 return true; 723 } 724 725 /* if we get here, negotiation should be complete */ 726 if (c->state != 5) { 727 pa_log_error("HFP negotiation failed in state %d with inbound %s\n", 728 c->state, buf); 729 rfcomm_write_response(fd, "ERROR"); 730 return false; 731 } 732 733 /* 734 * once we're fully connected, just reply OK to everything 735 * it will just be the headset sending the occasional status 736 * update, but we process only the ones we care about 737 */ 738 return true; 739} 740 741static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { 742 pa_bluetooth_transport *t = userdata; 743 744 pa_assert(io); 745 pa_assert(t); 746 747 if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { 748 pa_log_info("Lost RFCOMM connection."); 749 // TODO: Keep track of which profile is the current battery provider, 750 // only deregister if it is us currently providing these levels. 751 // (Also helpful to fill the 'Source' property) 752 // We might also move this to Profile1::RequestDisconnection 753 pa_bluetooth_device_deregister_battery(t->device); 754 goto fail; 755 } 756 757 if (events & PA_IO_EVENT_INPUT) { 758 char buf[512]; 759 ssize_t len; 760 int gain, dummy; 761 bool do_reply = false; 762 int vendor, product, version, features; 763 int num; 764 765 len = pa_read(fd, buf, 511, NULL); 766 if (len < 0) { 767 pa_log_error("RFCOMM read error: %s", pa_cstrerror(errno)); 768 goto fail; 769 } 770 buf[len] = 0; 771 pa_log_debug("RFCOMM << %s", buf); 772 773 /* There are only four HSP AT commands: 774 * AT+VGS=value: value between 0 and 15, sent by the HS to AG to set the speaker gain. 775 * +VGS=value is sent by AG to HS as a response to an AT+VGS command or when the gain 776 * is changed on the AG side. 777 * AT+VGM=value: value between 0 and 15, sent by the HS to AG to set the microphone gain. 778 * +VGM=value is sent by AG to HS as a response to an AT+VGM command or when the gain 779 * is changed on the AG side. 780 * AT+CKPD=200: Sent by HS when headset button is pressed. 781 * RING: Sent by AG to HS to notify of an incoming call. It can safely be ignored because 782 * it does not expect a reply. */ 783 if (sscanf(buf, "AT+VGS=%d", &gain) == 1 || sscanf(buf, "\r\n+VGM%*[=:]%d\r\n", &gain) == 1) { 784 if (!t->set_sink_volume) { 785 pa_log_debug("HS/HF peer supports speaker gain control"); 786 t->set_sink_volume = set_sink_volume; 787 } 788 789 t->sink_volume = hsp_gain_to_volume(gain); 790 pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SINK_VOLUME_CHANGED), t); 791 do_reply = true; 792 793 } else if (sscanf(buf, "AT+VGM=%d", &gain) == 1 || sscanf(buf, "\r\n+VGS%*[=:]%d\r\n", &gain) == 1) { 794 if (!t->set_source_volume) { 795 pa_log_debug("HS/HF peer supports microphone gain control"); 796 t->set_source_volume = set_source_volume; 797 } 798 799 t->source_volume = hsp_gain_to_volume(gain); 800 pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED), t); 801 do_reply = true; 802 } else if (sscanf(buf, "AT+CKPD=%d", &dummy) == 1) { 803 do_reply = true; 804 } else if (sscanf(buf, "AT+XAPL=%04x-%04x-%04x,%d", &vendor, &product, &version, &features) == 4) { 805 if (features & 0x2) 806 /* claim, that we support battery status reports */ 807 rfcomm_write_response(fd, "+XAPL=iPhone,6"); 808 do_reply = true; 809 } else if (sscanf(buf, "AT+IPHONEACCEV=%d", &num) == 1) { 810 char *substr = buf, *keystr; 811 int key, val, i; 812 813 do_reply = true; 814 815 for (i = 0; i < num; ++i) { 816 keystr = strchr(substr, ','); 817 if (!keystr) { 818 pa_log_warn("%s misses key for argument #%d", buf, i); 819 do_reply = false; 820 break; 821 } 822 keystr++; 823 substr = strchr(keystr, ','); 824 if (!substr) { 825 pa_log_warn("%s misses value for argument #%d", buf, i); 826 do_reply = false; 827 break; 828 } 829 substr++; 830 831 key = atoi(keystr); 832 val = atoi(substr); 833 834 switch (key) { 835 case 1: 836 pa_log_notice("Battery Level: %d0%%", val + 1); 837 pa_bluetooth_device_report_battery_level(t->device, (val + 1) * 10, "Apple accessory indication"); 838 break; 839 case 2: 840 pa_log_notice("Dock Status: %s", val ? "docked" : "undocked"); 841 break; 842 default: 843 pa_log_debug("Unexpected IPHONEACCEV key %#x", key); 844 break; 845 } 846 } 847 if (!do_reply) 848 rfcomm_write_response(fd, "ERROR"); 849 } else if (t->config) { /* t->config is only non-null for hfp profile */ 850 do_reply = hfp_rfcomm_handle(fd, t, buf); 851 } else { 852 rfcomm_write_response(fd, "ERROR"); 853 do_reply = false; 854 } 855 856 if (do_reply) 857 rfcomm_write_response(fd, "OK"); 858 } 859 860 return; 861 862fail: 863 pa_bluetooth_transport_unlink(t); 864 pa_bluetooth_transport_free(t); 865} 866 867static void transport_destroy(pa_bluetooth_transport *t) { 868 struct transport_data *trd = t->userdata; 869 870 if (trd->sco_io) { 871 trd->mainloop->io_free(trd->sco_io); 872 shutdown(trd->sco_fd, SHUT_RDWR); 873 close (trd->sco_fd); 874 } 875 876 trd->mainloop->io_free(trd->rfcomm_io); 877 shutdown(trd->rfcomm_fd, SHUT_RDWR); 878 close (trd->rfcomm_fd); 879 880 pa_xfree(trd); 881} 882 883static pa_volume_t set_sink_volume(pa_bluetooth_transport *t, pa_volume_t volume) { 884 struct transport_data *trd = t->userdata; 885 uint16_t gain = volume_to_hsp_gain(volume); 886 887 /* Propagate rounding and bound checks */ 888 volume = hsp_gain_to_volume(gain); 889 890 if (t->sink_volume == volume) 891 return volume; 892 893 t->sink_volume = volume; 894 895 /* If we are in the AG role, we send an unsolicited result-code to the headset 896 * to change the speaker gain. In the HS role, source and sink are swapped, 897 * so in this case we notify the AG that the microphone gain has changed 898 * by sending a command. */ 899 if (is_pulseaudio_audio_gateway(t->profile)) { 900 rfcomm_write_response(trd->rfcomm_fd, "+VGS=%d", gain); 901 } else { 902 rfcomm_write_command(trd->rfcomm_fd, "AT+VGM=%d", gain); 903 } 904 905 return volume; 906} 907 908static pa_volume_t set_source_volume(pa_bluetooth_transport *t, pa_volume_t volume) { 909 struct transport_data *trd = t->userdata; 910 uint16_t gain = volume_to_hsp_gain(volume); 911 912 /* Propagate rounding and bound checks */ 913 volume = hsp_gain_to_volume(gain); 914 915 if (t->source_volume == volume) 916 return volume; 917 918 t->source_volume = volume; 919 920 /* If we are in the AG role, we send an unsolicited result-code to the headset 921 * to change the microphone gain. In the HS role, source and sink are swapped, 922 * so in this case we notify the AG that the speaker gain has changed 923 * by sending a command. */ 924 if (is_pulseaudio_audio_gateway(t->profile)) { 925 rfcomm_write_response(trd->rfcomm_fd, "+VGM=%d", gain); 926 } else { 927 rfcomm_write_command(trd->rfcomm_fd, "AT+VGS=%d", gain); 928 } 929 930 return volume; 931} 932 933static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m, void *userdata) { 934 pa_bluetooth_backend *b = userdata; 935 pa_bluetooth_device *d; 936 pa_bluetooth_transport *t; 937 pa_bluetooth_profile_t p; 938 DBusMessage *r; 939 int fd; 940 const char *sender, *path, PA_UNUSED *handler; 941 DBusMessageIter arg_i; 942 char *pathfd; 943 struct transport_data *trd; 944 945 if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oha{sv}")) { 946 pa_log_error("Invalid signature found in NewConnection"); 947 goto fail; 948 } 949 950 handler = dbus_message_get_path(m); 951 if (pa_streq(handler, HSP_AG_PROFILE)) { 952 p = PA_BLUETOOTH_PROFILE_HSP_HS; 953 } else if (pa_streq(handler, HSP_HS_PROFILE)) { 954 p = PA_BLUETOOTH_PROFILE_HSP_AG; 955 } else if (pa_streq(handler, HFP_AG_PROFILE)) { 956 p = PA_BLUETOOTH_PROFILE_HFP_HF; 957 } else { 958 pa_log_error("Invalid handler"); 959 goto fail; 960 } 961 962 pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_OBJECT_PATH); 963 dbus_message_iter_get_basic(&arg_i, &path); 964 965 d = pa_bluetooth_discovery_get_device_by_path(b->discovery, path); 966 if (d == NULL) { 967 pa_log_error("Device doesn't exist for %s", path); 968 goto fail; 969 } 970 971 if (d->enable_hfp_hf) { 972 if (p == PA_BLUETOOTH_PROFILE_HSP_HS && pa_hashmap_get(d->uuids, PA_BLUETOOTH_UUID_HFP_HF)) { 973 /* If peer connecting to HSP Audio Gateway supports HFP HF profile 974 * reject this connection to force it to connect to HSP Audio Gateway instead. 975 */ 976 pa_log_info("HFP HF enabled in native backend and is supported by peer, rejecting HSP HS peer connection"); 977 goto fail; 978 } 979 } 980 981 pa_assert_se(dbus_message_iter_next(&arg_i)); 982 983 pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_UNIX_FD); 984 dbus_message_iter_get_basic(&arg_i, &fd); 985 986 pa_log_debug("dbus: NewConnection path=%s, fd=%d, profile %s", path, fd, 987 pa_bluetooth_profile_to_string(p)); 988 989 sender = dbus_message_get_sender(m); 990 991 pathfd = pa_sprintf_malloc ("%s/fd%d", path, fd); 992 t = pa_bluetooth_transport_new(d, sender, pathfd, p, NULL, 993 p == PA_BLUETOOTH_PROFILE_HFP_HF ? 994 sizeof(struct hfp_config) : 0); 995 pa_xfree(pathfd); 996 997 t->acquire = sco_acquire_cb; 998 t->release = sco_release_cb; 999 t->destroy = transport_destroy; 1000 1001 /* If PA is the HF/HS we are in control of volume attenuation and 1002 * can always send volume commands (notifications) to keep the peer 1003 * updated on actual volume value. 1004 * 1005 * If the peer is the HF/HS it is responsible for attenuation of both 1006 * speaker and microphone gain. 1007 * On HFP speaker/microphone gain support is reported by bit 4 in the 1008 * `AT+BRSF=` command. Since it isn't explicitly documented whether this 1009 * applies to speaker or microphone gain but the peer is required to send 1010 * an initial value with `AT+VG[MS]=` either callback is hooked 1011 * independently as soon as this command is received. 1012 * On HSP this is not specified and is assumed to be dynamic for both 1013 * speaker and microphone. 1014 */ 1015 if (is_peer_audio_gateway(p)) { 1016 t->set_sink_volume = set_sink_volume; 1017 t->set_source_volume = set_source_volume; 1018 } 1019 1020 pa_bluetooth_transport_reconfigure(t, pa_bluetooth_get_hf_codec("CVSD"), sco_transport_write, NULL); 1021 1022 trd = pa_xnew0(struct transport_data, 1); 1023 trd->rfcomm_fd = fd; 1024 trd->mainloop = b->core->mainloop; 1025 trd->rfcomm_io = trd->mainloop->io_new(b->core->mainloop, fd, PA_IO_EVENT_INPUT, 1026 rfcomm_io_callback, t); 1027 t->userdata = trd; 1028 1029 sco_listen(t); 1030 1031 if (p != PA_BLUETOOTH_PROFILE_HFP_HF) 1032 transport_put(t); 1033 1034 pa_assert_se(r = dbus_message_new_method_return(m)); 1035 1036 return r; 1037 1038fail: 1039 pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to handle new connection")); 1040 return r; 1041} 1042 1043static DBusMessage *profile_request_disconnection(DBusConnection *conn, DBusMessage *m, void *userdata) { 1044 DBusMessage *r; 1045 1046 pa_assert_se(r = dbus_message_new_method_return(m)); 1047 1048 return r; 1049} 1050 1051static DBusHandlerResult profile_handler(DBusConnection *c, DBusMessage *m, void *userdata) { 1052 pa_bluetooth_backend *b = userdata; 1053 DBusMessage *r = NULL; 1054 const char *path, *interface, *member; 1055 1056 pa_assert(b); 1057 1058 path = dbus_message_get_path(m); 1059 interface = dbus_message_get_interface(m); 1060 member = dbus_message_get_member(m); 1061 1062 pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member); 1063 1064 if (!pa_streq(path, HSP_AG_PROFILE) && !pa_streq(path, HSP_HS_PROFILE) 1065 && !pa_streq(path, HFP_AG_PROFILE)) 1066 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1067 1068 if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) { 1069 const char *xml = PROFILE_INTROSPECT_XML; 1070 1071 pa_assert_se(r = dbus_message_new_method_return(m)); 1072 pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID)); 1073 1074 } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "Release")) { 1075 pa_log_debug("Release not handled"); 1076 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1077 } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "RequestDisconnection")) { 1078 r = profile_request_disconnection(c, m, userdata); 1079 } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "NewConnection")) 1080 r = profile_new_connection(c, m, userdata); 1081 else 1082 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1083 1084 if (r) { 1085 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(b->connection), r, NULL)); 1086 dbus_message_unref(r); 1087 } 1088 1089 return DBUS_HANDLER_RESULT_HANDLED; 1090} 1091 1092static pa_hook_result_t adapter_uuids_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_adapter *a, pa_bluetooth_backend *b) { 1093 pa_assert(y); 1094 pa_assert(a); 1095 pa_assert(b); 1096 1097 if (profile_status_get(y, PA_BLUETOOTH_PROFILE_HSP_HS) == PA_BLUETOOTH_PROFILE_STATUS_ACTIVE && 1098 !pa_hashmap_get(a->uuids, PA_BLUETOOTH_UUID_HSP_AG)) 1099 register_profile(b, HSP_AG_PROFILE, PA_BLUETOOTH_UUID_HSP_AG, PA_BLUETOOTH_PROFILE_HSP_HS); 1100 1101 if (profile_status_get(y, PA_BLUETOOTH_PROFILE_HSP_AG) == PA_BLUETOOTH_PROFILE_STATUS_ACTIVE && 1102 !pa_hashmap_get(a->uuids, PA_BLUETOOTH_UUID_HSP_HS)) 1103 register_profile(b, HSP_HS_PROFILE, PA_BLUETOOTH_UUID_HSP_HS, PA_BLUETOOTH_PROFILE_HSP_AG); 1104 1105 if (profile_status_get(y, PA_BLUETOOTH_PROFILE_HFP_HF) == PA_BLUETOOTH_PROFILE_STATUS_ACTIVE && 1106 !pa_hashmap_get(a->uuids, PA_BLUETOOTH_UUID_HFP_AG)) 1107 register_profile(b, HFP_AG_PROFILE, PA_BLUETOOTH_UUID_HFP_AG, PA_BLUETOOTH_PROFILE_HFP_HF); 1108 1109 return PA_HOOK_OK; 1110} 1111 1112static void profile_init(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile) { 1113 static const DBusObjectPathVTable vtable_profile = { 1114 .message_function = profile_handler, 1115 }; 1116 const char *object_name; 1117 const char *uuid; 1118 1119 pa_assert(b); 1120 1121 switch (profile) { 1122 case PA_BLUETOOTH_PROFILE_HSP_HS: 1123 object_name = HSP_AG_PROFILE; 1124 uuid = PA_BLUETOOTH_UUID_HSP_AG; 1125 break; 1126 case PA_BLUETOOTH_PROFILE_HSP_AG: 1127 object_name = HSP_HS_PROFILE; 1128 uuid = PA_BLUETOOTH_UUID_HSP_HS; 1129 break; 1130 case PA_BLUETOOTH_PROFILE_HFP_HF: 1131 object_name = HFP_AG_PROFILE; 1132 uuid = PA_BLUETOOTH_UUID_HFP_AG; 1133 break; 1134 default: 1135 pa_assert_not_reached(); 1136 break; 1137 } 1138 1139 pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(b->connection), object_name, &vtable_profile, b)); 1140 1141 profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_ACTIVE); 1142 register_profile(b, object_name, uuid, profile); 1143} 1144 1145static void profile_done(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile) { 1146 pa_assert(b); 1147 1148 profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_INACTIVE); 1149 1150 switch (profile) { 1151 case PA_BLUETOOTH_PROFILE_HSP_HS: 1152 dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_AG_PROFILE); 1153 break; 1154 case PA_BLUETOOTH_PROFILE_HSP_AG: 1155 dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_HS_PROFILE); 1156 break; 1157 case PA_BLUETOOTH_PROFILE_HFP_HF: 1158 dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HFP_AG_PROFILE); 1159 break; 1160 default: 1161 pa_assert_not_reached(); 1162 break; 1163 } 1164} 1165 1166static void native_backend_apply_profile_registration_change(pa_bluetooth_backend *native_backend, bool enable_shared_profiles) { 1167 if (enable_shared_profiles) { 1168 profile_init(native_backend, PA_BLUETOOTH_PROFILE_HSP_AG); 1169 if (native_backend->enable_hfp_hf) 1170 profile_init(native_backend, PA_BLUETOOTH_PROFILE_HFP_HF); 1171 } else { 1172 profile_done(native_backend, PA_BLUETOOTH_PROFILE_HSP_AG); 1173 if (native_backend->enable_hfp_hf) 1174 profile_done(native_backend, PA_BLUETOOTH_PROFILE_HFP_HF); 1175 } 1176} 1177 1178void pa_bluetooth_native_backend_enable_shared_profiles(pa_bluetooth_backend *native_backend, bool enable) { 1179 1180 if (enable == native_backend->enable_shared_profiles) 1181 return; 1182 1183 native_backend_apply_profile_registration_change(native_backend, enable); 1184 1185 native_backend->enable_shared_profiles = enable; 1186} 1187 1188pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_shared_profiles) { 1189 pa_bluetooth_backend *backend; 1190 DBusError err; 1191 1192 pa_log_debug("Bluetooth Headset Backend API support using the native backend"); 1193 1194 backend = pa_xnew0(pa_bluetooth_backend, 1); 1195 backend->core = c; 1196 1197 dbus_error_init(&err); 1198 if (!(backend->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) { 1199 pa_log("Failed to get D-Bus connection: %s", err.message); 1200 dbus_error_free(&err); 1201 pa_xfree(backend); 1202 return NULL; 1203 } 1204 1205 backend->discovery = y; 1206 backend->enable_shared_profiles = enable_shared_profiles; 1207 backend->enable_hfp_hf = pa_bluetooth_discovery_get_enable_native_hfp_hf(y); 1208 backend->enable_hsp_hs = pa_bluetooth_discovery_get_enable_native_hsp_hs(y); 1209 1210 backend->adapter_uuids_changed_slot = 1211 pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_ADAPTER_UUIDS_CHANGED), PA_HOOK_NORMAL, 1212 (pa_hook_cb_t) adapter_uuids_changed_cb, backend); 1213 1214 if (!backend->enable_hsp_hs && !backend->enable_hfp_hf) 1215 pa_log_warn("Both HSP HS and HFP HF bluetooth profiles disabled in native backend. Native backend will not register for headset connections."); 1216 1217 if (backend->enable_hsp_hs) 1218 profile_init(backend, PA_BLUETOOTH_PROFILE_HSP_HS); 1219 1220 if (backend->enable_shared_profiles) 1221 native_backend_apply_profile_registration_change(backend, true); 1222 1223 return backend; 1224} 1225 1226void pa_bluetooth_native_backend_free(pa_bluetooth_backend *backend) { 1227 pa_assert(backend); 1228 1229 pa_dbus_free_pending_list(&backend->pending); 1230 1231 if (backend->adapter_uuids_changed_slot) 1232 pa_hook_slot_free(backend->adapter_uuids_changed_slot); 1233 1234 if (backend->enable_shared_profiles) 1235 native_backend_apply_profile_registration_change(backend, false); 1236 1237 if (backend->enable_hsp_hs) 1238 profile_done(backend, PA_BLUETOOTH_PROFILE_HSP_HS); 1239 1240 pa_dbus_connection_unref(backend->connection); 1241 1242 pa_xfree(backend); 1243} 1244