1 /* 2 * 3 * BlueZ - Bluetooth protocol stack for Linux 4 * 5 * Copyright (C) 2006-2007 Nokia Corporation 6 * Copyright (C) 2004-2009 Marcel Holtmann <marcel (at) holtmann.org> 7 * Copyright (C) 2009-2010 Motorola Inc. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 */ 24 25 #ifdef HAVE_CONFIG_H 26 #include <config.h> 27 #endif 28 29 #include <stdint.h> 30 #include <errno.h> 31 32 #include <bluetooth/bluetooth.h> 33 #include <bluetooth/sdp.h> 34 35 #include <glib.h> 36 #include <dbus/dbus.h> 37 #include <gdbus.h> 38 39 #include "log.h" 40 41 #include "device.h" 42 #include "avdtp.h" 43 #include "media.h" 44 #include "a2dp.h" 45 #include "error.h" 46 #include "sink.h" 47 #include "dbus-common.h" 48 #include "../src/adapter.h" 49 #include "../src/device.h" 50 51 #define STREAM_SETUP_RETRY_TIMER 2 52 53 struct pending_request { 54 DBusConnection *conn; 55 DBusMessage *msg; 56 unsigned int id; 57 }; 58 59 struct sink { 60 struct audio_device *dev; 61 struct avdtp *session; 62 struct avdtp_stream *stream; 63 unsigned int cb_id; 64 guint retry_id; 65 avdtp_session_state_t session_state; 66 avdtp_state_t stream_state; 67 sink_state_t state; 68 struct pending_request *connect; 69 struct pending_request *disconnect; 70 DBusConnection *conn; 71 }; 72 73 struct sink_state_callback { 74 sink_state_cb cb; 75 void *user_data; 76 unsigned int id; 77 }; 78 79 static GSList *sink_callbacks = NULL; 80 81 static unsigned int avdtp_callback_id = 0; 82 83 static char *str_state[] = { 84 "SINK_STATE_DISCONNECTED", 85 "SINK_STATE_CONNECTING", 86 "SINK_STATE_CONNECTED", 87 "SINK_STATE_PLAYING", 88 }; 89 90 static const char *state2str(sink_state_t state) 91 { 92 switch (state) { 93 case SINK_STATE_DISCONNECTED: 94 return "disconnected"; 95 case SINK_STATE_CONNECTING: 96 return "connecting"; 97 case SINK_STATE_CONNECTED: 98 return "connected"; 99 case SINK_STATE_PLAYING: 100 return "playing"; 101 default: 102 error("Invalid sink state %d", state); 103 return NULL; 104 } 105 } 106 107 static void sink_set_state(struct audio_device *dev, sink_state_t new_state) 108 { 109 struct sink *sink = dev->sink; 110 const char *state_str; 111 sink_state_t old_state = sink->state; 112 GSList *l; 113 114 sink->state = new_state; 115 116 state_str = state2str(new_state); 117 if (state_str) 118 emit_property_changed(dev->conn, dev->path, 119 AUDIO_SINK_INTERFACE, "State", 120 DBUS_TYPE_STRING, &state_str); 121 122 DBG("State changed %s: %s -> %s", dev->path, str_state[old_state], 123 str_state[new_state]); 124 125 for (l = sink_callbacks; l != NULL; l = l->next) { 126 struct sink_state_callback *cb = l->data; 127 cb->cb(dev, old_state, new_state, cb->user_data); 128 } 129 } 130 131 static void avdtp_state_callback(struct audio_device *dev, 132 struct avdtp *session, 133 avdtp_session_state_t old_state, 134 avdtp_session_state_t new_state, 135 void *user_data) 136 { 137 struct sink *sink = dev->sink; 138 139 if (sink == NULL) 140 return; 141 142 switch (new_state) { 143 case AVDTP_SESSION_STATE_DISCONNECTED: 144 if (sink->state != SINK_STATE_CONNECTING) { 145 gboolean value = FALSE; 146 g_dbus_emit_signal(dev->conn, dev->path, 147 AUDIO_SINK_INTERFACE, "Disconnected", 148 DBUS_TYPE_INVALID); 149 emit_property_changed(dev->conn, dev->path, 150 AUDIO_SINK_INTERFACE, "Connected", 151 DBUS_TYPE_BOOLEAN, &value); 152 } 153 sink_set_state(dev, SINK_STATE_DISCONNECTED); 154 break; 155 case AVDTP_SESSION_STATE_CONNECTING: 156 sink_set_state(dev, SINK_STATE_CONNECTING); 157 break; 158 case AVDTP_SESSION_STATE_CONNECTED: 159 break; 160 } 161 162 sink->session_state = new_state; 163 } 164 165 static void pending_request_free(struct audio_device *dev, 166 struct pending_request *pending) 167 { 168 if (pending->conn) 169 dbus_connection_unref(pending->conn); 170 if (pending->msg) 171 dbus_message_unref(pending->msg); 172 if (pending->id) 173 a2dp_cancel(dev, pending->id); 174 175 g_free(pending); 176 } 177 178 static void stream_state_changed(struct avdtp_stream *stream, 179 avdtp_state_t old_state, 180 avdtp_state_t new_state, 181 struct avdtp_error *err, 182 void *user_data) 183 { 184 struct audio_device *dev = user_data; 185 struct sink *sink = dev->sink; 186 gboolean value; 187 188 if (err) 189 return; 190 191 switch (new_state) { 192 case AVDTP_STATE_IDLE: 193 if (sink->disconnect) { 194 DBusMessage *reply; 195 struct pending_request *p; 196 197 p = sink->disconnect; 198 sink->disconnect = NULL; 199 200 reply = dbus_message_new_method_return(p->msg); 201 g_dbus_send_message(p->conn, reply); 202 pending_request_free(dev, p); 203 } 204 205 if (sink->session) { 206 avdtp_unref(sink->session); 207 sink->session = NULL; 208 } 209 sink->stream = NULL; 210 sink->cb_id = 0; 211 break; 212 case AVDTP_STATE_OPEN: 213 if (old_state == AVDTP_STATE_CONFIGURED && 214 sink->state == SINK_STATE_CONNECTING) { 215 value = TRUE; 216 g_dbus_emit_signal(dev->conn, dev->path, 217 AUDIO_SINK_INTERFACE, 218 "Connected", 219 DBUS_TYPE_INVALID); 220 emit_property_changed(dev->conn, dev->path, 221 AUDIO_SINK_INTERFACE, 222 "Connected", 223 DBUS_TYPE_BOOLEAN, &value); 224 } else if (old_state == AVDTP_STATE_STREAMING) { 225 value = FALSE; 226 g_dbus_emit_signal(dev->conn, dev->path, 227 AUDIO_SINK_INTERFACE, 228 "Stopped", 229 DBUS_TYPE_INVALID); 230 emit_property_changed(dev->conn, dev->path, 231 AUDIO_SINK_INTERFACE, 232 "Playing", 233 DBUS_TYPE_BOOLEAN, &value); 234 } 235 sink_set_state(dev, SINK_STATE_CONNECTED); 236 break; 237 case AVDTP_STATE_STREAMING: 238 value = TRUE; 239 g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, 240 "Playing", DBUS_TYPE_INVALID); 241 emit_property_changed(dev->conn, dev->path, 242 AUDIO_SINK_INTERFACE, "Playing", 243 DBUS_TYPE_BOOLEAN, &value); 244 sink_set_state(dev, SINK_STATE_PLAYING); 245 break; 246 case AVDTP_STATE_CONFIGURED: 247 case AVDTP_STATE_CLOSING: 248 case AVDTP_STATE_ABORTING: 249 default: 250 break; 251 } 252 253 sink->stream_state = new_state; 254 } 255 256 static void error_failed(DBusConnection *conn, DBusMessage *msg, 257 const char *desc) 258 { 259 DBusMessage *reply = btd_error_failed(msg, desc); 260 g_dbus_send_message(conn, reply); 261 } 262 263 static gboolean stream_setup_retry(gpointer user_data) 264 { 265 struct sink *sink = user_data; 266 struct pending_request *pending = sink->connect; 267 268 sink->retry_id = 0; 269 270 if (sink->stream_state >= AVDTP_STATE_OPEN) { 271 DBG("Stream successfully created, after XCASE connect:connect"); 272 if (pending->msg) { 273 DBusMessage *reply; 274 reply = dbus_message_new_method_return(pending->msg); 275 g_dbus_send_message(pending->conn, reply); 276 } 277 } else { 278 DBG("Stream setup failed, after XCASE connect:connect"); 279 if (pending->msg) 280 error_failed(pending->conn, pending->msg, "Stream setup failed"); 281 } 282 283 sink->connect = NULL; 284 pending_request_free(sink->dev, pending); 285 286 return FALSE; 287 } 288 289 static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, 290 struct avdtp_stream *stream, 291 struct avdtp_error *err, void *user_data) 292 { 293 struct sink *sink = user_data; 294 struct pending_request *pending; 295 296 pending = sink->connect; 297 298 pending->id = 0; 299 300 if (stream) { 301 DBG("Stream successfully created"); 302 303 if (pending->msg) { 304 DBusMessage *reply; 305 reply = dbus_message_new_method_return(pending->msg); 306 g_dbus_send_message(pending->conn, reply); 307 } 308 309 sink->connect = NULL; 310 pending_request_free(sink->dev, pending); 311 312 return; 313 } 314 315 avdtp_unref(sink->session); 316 sink->session = NULL; 317 if (avdtp_error_category(err) == AVDTP_ERRNO 318 && avdtp_error_posix_errno(err) != EHOSTDOWN) { 319 DBG("connect:connect XCASE detected"); 320 sink->retry_id = g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER, 321 stream_setup_retry, 322 sink); 323 } else { 324 if (pending->msg) 325 error_failed(pending->conn, pending->msg, "Stream setup failed"); 326 sink->connect = NULL; 327 pending_request_free(sink->dev, pending); 328 DBG("Stream setup failed : %s", avdtp_strerror(err)); 329 } 330 } 331 332 static void select_complete(struct avdtp *session, struct a2dp_sep *sep, 333 GSList *caps, void *user_data) 334 { 335 struct sink *sink = user_data; 336 struct pending_request *pending; 337 int id; 338 339 pending = sink->connect; 340 pending->id = 0; 341 342 id = a2dp_config(session, sep, stream_setup_complete, caps, sink); 343 if (id == 0) 344 goto failed; 345 346 pending->id = id; 347 return; 348 349 failed: 350 if (pending->msg) 351 error_failed(pending->conn, pending->msg, "Stream setup failed"); 352 pending_request_free(sink->dev, pending); 353 sink->connect = NULL; 354 avdtp_unref(sink->session); 355 sink->session = NULL; 356 } 357 358 static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err, 359 void *user_data) 360 { 361 struct sink *sink = user_data; 362 struct pending_request *pending; 363 int id; 364 365 if (!sink->connect) { 366 avdtp_unref(sink->session); 367 sink->session = NULL; 368 return; 369 } 370 371 pending = sink->connect; 372 373 if (err) { 374 avdtp_unref(sink->session); 375 sink->session = NULL; 376 if (avdtp_error_category(err) == AVDTP_ERRNO 377 && avdtp_error_posix_errno(err) != EHOSTDOWN) { 378 DBG("connect:connect XCASE detected"); 379 sink->retry_id = 380 g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER, 381 stream_setup_retry, 382 sink); 383 } else 384 goto failed; 385 return; 386 } 387 388 DBG("Discovery complete"); 389 390 id = a2dp_select_capabilities(sink->session, AVDTP_SEP_TYPE_SINK, NULL, 391 select_complete, sink); 392 if (id == 0) 393 goto failed; 394 395 pending->id = id; 396 return; 397 398 failed: 399 if (pending->msg) 400 error_failed(pending->conn, pending->msg, "Stream setup failed"); 401 pending_request_free(sink->dev, pending); 402 sink->connect = NULL; 403 avdtp_unref(sink->session); 404 sink->session = NULL; 405 } 406 407 gboolean sink_setup_stream(struct sink *sink, struct avdtp *session) 408 { 409 if (sink->connect || sink->disconnect) 410 return FALSE; 411 412 if (session && !sink->session) 413 sink->session = avdtp_ref(session); 414 415 if (!sink->session) 416 return FALSE; 417 418 avdtp_set_auto_disconnect(sink->session, FALSE); 419 420 if (avdtp_discover(sink->session, discovery_complete, sink) < 0) 421 return FALSE; 422 423 sink->connect = g_new0(struct pending_request, 1); 424 425 return TRUE; 426 } 427 428 static DBusMessage *sink_connect(DBusConnection *conn, 429 DBusMessage *msg, void *data) 430 { 431 struct audio_device *dev = data; 432 struct sink *sink = dev->sink; 433 struct pending_request *pending; 434 435 if (!sink->session) 436 sink->session = avdtp_get(&dev->src, &dev->dst); 437 438 if (!sink->session) 439 return btd_error_failed(msg, "Unable to get a session"); 440 441 if (sink->connect || sink->disconnect) 442 return btd_error_busy(msg); 443 444 if (sink->stream_state >= AVDTP_STATE_OPEN) 445 return btd_error_already_connected(msg); 446 447 if (!sink_setup_stream(sink, NULL)) 448 return btd_error_failed(msg, "Failed to create a stream"); 449 450 dev->auto_connect = FALSE; 451 452 pending = sink->connect; 453 454 pending->conn = dbus_connection_ref(conn); 455 pending->msg = dbus_message_ref(msg); 456 457 DBG("stream creation in progress"); 458 459 return NULL; 460 } 461 462 static DBusMessage *sink_disconnect(DBusConnection *conn, 463 DBusMessage *msg, void *data) 464 { 465 struct audio_device *device = data; 466 struct sink *sink = device->sink; 467 struct pending_request *pending; 468 int err; 469 470 if (!sink->session) 471 return btd_error_not_connected(msg); 472 473 if (sink->connect || sink->disconnect) 474 return btd_error_busy(msg); 475 476 if (sink->stream_state < AVDTP_STATE_OPEN) { 477 DBusMessage *reply = dbus_message_new_method_return(msg); 478 if (!reply) 479 return NULL; 480 avdtp_unref(sink->session); 481 sink->session = NULL; 482 return reply; 483 } 484 485 err = avdtp_close(sink->session, sink->stream, FALSE); 486 if (err < 0) 487 return btd_error_failed(msg, strerror(-err)); 488 489 pending = g_new0(struct pending_request, 1); 490 pending->conn = dbus_connection_ref(conn); 491 pending->msg = dbus_message_ref(msg); 492 sink->disconnect = pending; 493 494 return NULL; 495 } 496 497 static DBusMessage *sink_suspend(DBusConnection *conn, 498 DBusMessage *msg, void *data) 499 { 500 struct audio_device *device = data; 501 struct sink *sink = device->sink; 502 struct pending_request *pending; 503 int err; 504 505 if (!sink->session) 506 return g_dbus_create_error(msg, ERROR_INTERFACE 507 ".NotConnected", 508 "Device not Connected"); 509 510 if (sink->connect || sink->disconnect) 511 return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", 512 "%s", strerror(EBUSY)); 513 514 if (sink->state < AVDTP_STATE_OPEN) { 515 DBusMessage *reply = dbus_message_new_method_return(msg); 516 if (!reply) 517 return NULL; 518 avdtp_unref(sink->session); 519 sink->session = NULL; 520 return reply; 521 } 522 523 err = avdtp_suspend(sink->session, sink->stream); 524 if (err < 0) 525 return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", 526 "%s", strerror(-err)); 527 528 return NULL; 529 } 530 531 static DBusMessage *sink_resume(DBusConnection *conn, 532 DBusMessage *msg, void *data) 533 { 534 struct audio_device *device = data; 535 struct sink *sink = device->sink; 536 struct pending_request *pending; 537 int err; 538 539 if (!sink->session) 540 return g_dbus_create_error(msg, ERROR_INTERFACE 541 ".NotConnected", 542 "Device not Connected"); 543 544 if (sink->connect || sink->disconnect) 545 return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", 546 "%s", strerror(EBUSY)); 547 548 if (sink->state < AVDTP_STATE_OPEN) { 549 DBusMessage *reply = dbus_message_new_method_return(msg); 550 if (!reply) 551 return NULL; 552 avdtp_unref(sink->session); 553 sink->session = NULL; 554 return reply; 555 } 556 557 err = avdtp_start(sink->session, sink->stream); 558 if (err < 0) 559 return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", 560 "%s", strerror(-err)); 561 562 return NULL; 563 } 564 565 static DBusMessage *sink_is_connected(DBusConnection *conn, 566 DBusMessage *msg, 567 void *data) 568 { 569 struct audio_device *device = data; 570 struct sink *sink = device->sink; 571 DBusMessage *reply; 572 dbus_bool_t connected; 573 574 reply = dbus_message_new_method_return(msg); 575 if (!reply) 576 return NULL; 577 578 connected = (sink->stream_state >= AVDTP_STATE_CONFIGURED); 579 580 dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, 581 DBUS_TYPE_INVALID); 582 583 return reply; 584 } 585 586 static DBusMessage *sink_get_properties(DBusConnection *conn, 587 DBusMessage *msg, void *data) 588 { 589 struct audio_device *device = data; 590 struct sink *sink = device->sink; 591 DBusMessage *reply; 592 DBusMessageIter iter; 593 DBusMessageIter dict; 594 const char *state; 595 gboolean value; 596 597 reply = dbus_message_new_method_return(msg); 598 if (!reply) 599 return NULL; 600 601 dbus_message_iter_init_append(reply, &iter); 602 603 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, 604 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING 605 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING 606 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); 607 608 /* Playing */ 609 value = (sink->stream_state == AVDTP_STATE_STREAMING); 610 dict_append_entry(&dict, "Playing", DBUS_TYPE_BOOLEAN, &value); 611 612 /* Connected */ 613 value = (sink->stream_state >= AVDTP_STATE_CONFIGURED); 614 dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value); 615 616 /* State */ 617 state = state2str(sink->state); 618 if (state) 619 dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state); 620 621 dbus_message_iter_close_container(&iter, &dict); 622 623 return reply; 624 } 625 626 static GDBusMethodTable sink_methods[] = { 627 { "Connect", "", "", sink_connect, 628 G_DBUS_METHOD_FLAG_ASYNC }, 629 { "Disconnect", "", "", sink_disconnect, 630 G_DBUS_METHOD_FLAG_ASYNC }, 631 { "Suspend", "", "", sink_suspend, 632 G_DBUS_METHOD_FLAG_ASYNC }, 633 { "Resume", "", "", sink_resume, 634 G_DBUS_METHOD_FLAG_ASYNC }, 635 { "IsConnected", "", "b", sink_is_connected, 636 G_DBUS_METHOD_FLAG_DEPRECATED }, 637 { "GetProperties", "", "a{sv}",sink_get_properties }, 638 { NULL, NULL, NULL, NULL } 639 }; 640 641 static GDBusSignalTable sink_signals[] = { 642 { "Connected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED }, 643 { "Disconnected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED }, 644 { "Playing", "", G_DBUS_SIGNAL_FLAG_DEPRECATED }, 645 { "Stopped", "", G_DBUS_SIGNAL_FLAG_DEPRECATED }, 646 { "PropertyChanged", "sv" }, 647 { NULL, NULL } 648 }; 649 650 static void sink_free(struct audio_device *dev) 651 { 652 struct sink *sink = dev->sink; 653 654 if (sink->cb_id) 655 avdtp_stream_remove_cb(sink->session, sink->stream, 656 sink->cb_id); 657 658 if (sink->session) 659 avdtp_unref(sink->session); 660 661 if (sink->connect) 662 pending_request_free(dev, sink->connect); 663 664 if (sink->disconnect) 665 pending_request_free(dev, sink->disconnect); 666 667 if (sink->retry_id) 668 g_source_remove(sink->retry_id); 669 670 g_free(sink); 671 dev->sink = NULL; 672 } 673 674 static void path_unregister(void *data) 675 { 676 struct audio_device *dev = data; 677 678 DBG("Unregistered interface %s on path %s", 679 AUDIO_SINK_INTERFACE, dev->path); 680 681 sink_free(dev); 682 } 683 684 void sink_unregister(struct audio_device *dev) 685 { 686 g_dbus_unregister_interface(dev->conn, dev->path, 687 AUDIO_SINK_INTERFACE); 688 } 689 690 struct sink *sink_init(struct audio_device *dev) 691 { 692 struct sink *sink; 693 694 if (!g_dbus_register_interface(dev->conn, dev->path, 695 AUDIO_SINK_INTERFACE, 696 sink_methods, sink_signals, NULL, 697 dev, path_unregister)) 698 return NULL; 699 700 DBG("Registered interface %s on path %s", 701 AUDIO_SINK_INTERFACE, dev->path); 702 703 if (avdtp_callback_id == 0) 704 avdtp_callback_id = avdtp_add_state_cb(avdtp_state_callback, 705 NULL); 706 707 sink = g_new0(struct sink, 1); 708 709 sink->dev = dev; 710 711 return sink; 712 } 713 714 gboolean sink_is_active(struct audio_device *dev) 715 { 716 struct sink *sink = dev->sink; 717 718 if (sink->session) 719 return TRUE; 720 721 return FALSE; 722 } 723 724 avdtp_state_t sink_get_state(struct audio_device *dev) 725 { 726 struct sink *sink = dev->sink; 727 728 return sink->stream_state; 729 } 730 731 gboolean sink_new_stream(struct audio_device *dev, struct avdtp *session, 732 struct avdtp_stream *stream) 733 { 734 struct sink *sink = dev->sink; 735 736 if (sink->stream) 737 return FALSE; 738 739 if (!sink->session) 740 sink->session = avdtp_ref(session); 741 742 sink->stream = stream; 743 744 sink->cb_id = avdtp_stream_add_cb(session, stream, 745 stream_state_changed, dev); 746 747 return TRUE; 748 } 749 750 gboolean sink_shutdown(struct sink *sink) 751 { 752 if (!sink->session) 753 return FALSE; 754 755 avdtp_set_device_disconnect(sink->session, TRUE); 756 757 /* cancel pending connect */ 758 if (sink->connect) { 759 struct pending_request *pending = sink->connect; 760 761 if (pending->msg) 762 error_failed(pending->conn, pending->msg, 763 "Stream setup failed"); 764 pending_request_free(sink->dev, pending); 765 sink->connect = NULL; 766 767 avdtp_unref(sink->session); 768 sink->session = NULL; 769 770 return TRUE; 771 } 772 773 /* disconnect already ongoing */ 774 if (sink->disconnect) 775 return TRUE; 776 777 if (!sink->stream) 778 return FALSE; 779 780 if (avdtp_close(sink->session, sink->stream, FALSE) < 0) 781 return FALSE; 782 783 return TRUE; 784 } 785 786 unsigned int sink_add_state_cb(sink_state_cb cb, void *user_data) 787 { 788 struct sink_state_callback *state_cb; 789 static unsigned int id = 0; 790 791 state_cb = g_new(struct sink_state_callback, 1); 792 state_cb->cb = cb; 793 state_cb->user_data = user_data; 794 state_cb->id = ++id; 795 796 sink_callbacks = g_slist_append(sink_callbacks, state_cb); 797 798 return state_cb->id; 799 } 800 801 gboolean sink_remove_state_cb(unsigned int id) 802 { 803 GSList *l; 804 805 for (l = sink_callbacks; l != NULL; l = l->next) { 806 struct sink_state_callback *cb = l->data; 807 if (cb && cb->id == id) { 808 sink_callbacks = g_slist_remove(sink_callbacks, cb); 809 g_free(cb); 810 return TRUE; 811 } 812 } 813 814 return FALSE; 815 } 816