diff options
-rw-r--r-- | src/daemon/main.c | 8 | ||||
-rw-r--r-- | src/modules/alsa/alsa-util.c | 6 | ||||
-rw-r--r-- | src/modules/alsa/module-alsa-card.c | 3 | ||||
-rw-r--r-- | src/modules/bluetooth/bluetooth-util.c | 260 | ||||
-rw-r--r-- | src/modules/bluetooth/bluetooth-util.h | 19 | ||||
-rw-r--r-- | src/modules/bluetooth/module-bluetooth-device.c | 747 | ||||
-rw-r--r-- | src/modules/bluetooth/module-bluetooth-discover.c | 72 | ||||
-rw-r--r-- | src/modules/bluetooth/sbc.c | 10 | ||||
-rw-r--r-- | src/modules/bluetooth/sbc.h | 10 | ||||
-rw-r--r-- | src/modules/dbus-util.c | 16 | ||||
-rw-r--r-- | src/modules/dbus-util.h | 3 | ||||
-rw-r--r-- | src/modules/module-stream-restore.c | 22 | ||||
-rw-r--r-- | src/pulsecore/hook-list.c | 6 | ||||
-rw-r--r-- | src/pulsecore/hook-list.h | 2 | ||||
-rw-r--r-- | src/pulsecore/macro.h | 20 | ||||
-rw-r--r-- | src/pulsecore/protocol-native.c | 2 | ||||
-rw-r--r-- | src/pulsecore/sink-input.c | 22 | ||||
-rw-r--r-- | src/pulsecore/sink-input.h | 1 | ||||
-rw-r--r-- | src/pulsecore/sink.c | 26 | ||||
-rw-r--r-- | src/pulsecore/sink.h | 2 | ||||
-rw-r--r-- | src/pulsecore/source.c | 27 | ||||
-rw-r--r-- | src/pulsecore/source.h | 2 |
22 files changed, 712 insertions, 574 deletions
diff --git a/src/daemon/main.c b/src/daemon/main.c index 0048e7b7..47ca2673 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -851,6 +851,14 @@ int main(int argc, char *argv[]) { pa_log_debug(_("Optimized build: no")); #endif +#ifdef NDEBUG + pa_log_debug(_("NDEBUG defined, all asserts disabled.")); +#elif defined(FASTPATH) + pa_log_debug(_("FASTPATH defined, only fast path asserts disabled.")); +#else + pa_log_debug(_("All asserts enabled.")); +#endif + if (!(s = pa_machine_id())) { pa_log(_("Failed to get machine ID")); goto finish; diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 454cfd4e..3f26aebe 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -587,9 +587,9 @@ static const struct pa_alsa_profile_info device_table[] = { "analog-surround-51", 8 }, - {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_CENTER, - PA_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, - PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE}}, + {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE}}, "a52", N_("Digital Surround 5.1 (IEC958/AC3)"), "iec958-ac3-surround-51", diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index d5e2cdc2..fdc952dd 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -351,7 +351,8 @@ int pa__init(pa_module *m) { init_profile(u); - pa_reserve_wrapper_unref(reserve); + if (reserve) + pa_reserve_wrapper_unref(reserve); return 0; diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c index ccc8bee3..3dfc65e7 100644 --- a/src/modules/bluetooth/bluetooth-util.c +++ b/src/modules/bluetooth/bluetooth-util.c @@ -24,31 +24,19 @@ #endif #include <pulsecore/core-util.h> +#include <pulsecore/shared.h> #include <modules/dbus-util.h> #include "bluetooth-util.h" -enum mode { - MODE_FIND, - MODE_GET, - MODE_DISCOVER -}; - struct pa_bluetooth_discovery { - DBusConnection *connection; - PA_LLIST_HEAD(pa_dbus_pending, pending); + PA_REFCNT_DECLARE; - enum mode mode; - - /* If mode == MODE_FIND look for a specific device by its address. - If mode == MODE_GET look for a specific device by its path. */ - const char *looking_for; - pa_bluetooth_device *found_device; - - /* If looking_for is NULL we do long-time discovery */ + pa_core *core; + pa_dbus_connection *connection; + PA_LLIST_HEAD(pa_dbus_pending, pending); pa_hashmap *devices; - pa_bluetooth_device_callback_t callback; - struct userdata *userdata; + pa_hook hook; }; static pa_bluetooth_uuid *uuid_new(const char *uuid) { @@ -73,9 +61,9 @@ static pa_bluetooth_device* device_new(const char *path) { d = pa_xnew(pa_bluetooth_device, 1); - d->device_info_valid = d->audio_sink_info_valid = d->headset_info_valid = 0; + d->dead = FALSE; - d->data = NULL; + d->device_info_valid = d->audio_sink_info_valid = d->headset_info_valid = 0; d->name = NULL; d->path = pa_xstrdup(path); @@ -94,7 +82,7 @@ static pa_bluetooth_device* device_new(const char *path) { return d; } -void pa_bluetooth_device_free(pa_bluetooth_device *d) { +static void device_free(pa_bluetooth_device *d) { pa_bluetooth_uuid *u; pa_assert(d); @@ -114,20 +102,21 @@ void pa_bluetooth_device_free(pa_bluetooth_device *d) { static pa_bool_t device_is_loaded(pa_bluetooth_device *d) { pa_assert(d); - /* FIXME: e83621724d7939b97b4f01f0d7e965d61ef8e55e, f1daa282f030e4e2381341e0f65faca47c4b891b is borked, probably needs to be reversed */ - - return d->device_info_valid && (d->audio_sink_info_valid || d->headset_info_valid); + return + d->device_info_valid && + d->audio_sink_info_valid && + d->headset_info_valid; } static pa_bool_t device_is_audio(pa_bluetooth_device *d) { pa_assert(d); pa_assert(d->device_info_valid); - pa_assert(d->audio_sink_info_valid || d->headset_info_valid); + pa_assert(d->audio_sink_info_valid); + pa_assert(d->headset_info_valid); - /* FIXME: e83621724d7939b97b4f01f0d7e965d61ef8e55e, f1daa282f030e4e2381341e0f65faca47c4b891b is borked, probably needs to be reversed */ - - return d->device_info_valid > 0 && + return + d->device_info_valid > 0 && (d->audio_sink_info_valid > 0 || d->headset_info_valid > 0); } @@ -289,21 +278,18 @@ static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusM return 0; } -static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_bool_t good) { +static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_bool_t dead) { pa_assert(y); pa_assert(d); - if (y->mode != MODE_DISCOVER) - return; - if (!device_is_loaded(d)) return; if (!device_is_audio(d)) return; - y->callback(y->userdata, d, good); - + d->dead = dead; + pa_hook_fire(&y->hook, d); } static void get_properties_reply(DBusPendingCall *pending, void *userdata) { @@ -378,7 +364,7 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) { } finish: - run_callback(y, d, TRUE); + run_callback(y, d, FALSE); dbus_message_unref(r); @@ -393,9 +379,9 @@ static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, pa_bl pa_assert(y); pa_assert(m); - pa_assert_se(dbus_connection_send_with_reply(y->connection, m, &call, -1)); + pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(y->connection), m, &call, -1)); - p = pa_dbus_pending_new(m, call, y, d); + p = pa_dbus_pending_new(pa_dbus_connection_get(y->connection), m, call, y, d); PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p); dbus_pending_call_set_notify(call, func, p, NULL); @@ -411,13 +397,7 @@ static void found_device(pa_bluetooth_discovery *y, const char* path) { d = device_new(path); - if (y->mode == MODE_DISCOVER) { - pa_assert(y->devices); - pa_hashmap_put(y->devices, d->path, d); - } else { - pa_assert(!y->found_device); - y->found_device = d; - } + pa_hashmap_put(y->devices, d->path, d); pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Device", "GetProperties")); send_and_add_to_pending(y, d, m, get_properties_reply); @@ -470,57 +450,11 @@ end: pa_dbus_pending_free(p); } -static void find_device_reply(DBusPendingCall *pending, void *userdata) { - DBusError e; - DBusMessage *r; - char *path = NULL; - pa_dbus_pending *p; - pa_bluetooth_discovery *y; - - pa_assert(pending); - - dbus_error_init(&e); - - pa_assert_se(p = userdata); - pa_assert_se(y = p->context_data); - pa_assert_se(r = dbus_pending_call_steal_reply(pending)); - - if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { - pa_log("Error from FindDevice reply: %s", dbus_message_get_error_name(r)); - goto end; - } - - if (!dbus_message_get_args(r, &e, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { - pa_log("org.bluez.Adapter.FindDevice returned an error: '%s'\n", e.message); - dbus_error_free(&e); - } else - found_device(y, path); - -end: - dbus_message_unref(r); - - PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p); - pa_dbus_pending_free(p); -} - static void found_adapter(pa_bluetooth_discovery *y, const char *path) { DBusMessage *m; - if (y->mode == MODE_FIND) { - pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "FindDevice")); - - pa_assert_se(dbus_message_append_args(m, - DBUS_TYPE_STRING, &y->looking_for, - DBUS_TYPE_INVALID)); - - send_and_add_to_pending(y, NULL, m, find_device_reply); - - } else { - pa_assert(y->mode == MODE_DISCOVER); - - pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "ListDevices")); - send_and_add_to_pending(y, NULL, m, list_devices_reply); - } + pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "ListDevices")); + send_and_add_to_pending(y, NULL, m, list_devices_reply); } static void list_adapters_reply(DBusPendingCall *pending, void *userdata) { @@ -600,11 +534,8 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us pa_log_debug("Device %s removed", path); if ((d = pa_hashmap_remove(y->devices, path))) { - - pa_assert_se(y->mode == MODE_DISCOVER); - run_callback(y, d, FALSE); - - pa_bluetooth_device_free(d); + run_callback(y, d, TRUE); + device_free(d); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -656,16 +587,13 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us } else if (dbus_message_has_interface(m, "org.bluez.Headset")) { if (parse_audio_property(y, &d->headset_connected, &arg_i) < 0) goto fail; - d->headset_info_valid = 1; } else if (dbus_message_has_interface(m, "org.bluez.AudioSink")) { if (parse_audio_property(y, &d->audio_sink_connected, &arg_i) < 0) goto fail; - d->audio_sink_info_valid = 1; } - pa_assert_se(y->mode == MODE_DISCOVER); - run_callback(y, d, TRUE); + run_callback(y, d, FALSE); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -677,82 +605,81 @@ fail: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -pa_bluetooth_device* pa_bluetooth_find_device(DBusConnection *c, const char* address) { - pa_bluetooth_discovery y; - - memset(&y, 0, sizeof(y)); - y.mode = MODE_FIND; - y.looking_for = address; - y.connection = c; - PA_LLIST_HEAD_INIT(pa_dbus_pending, y.pending); +const pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discovery *y, const char* address) { + pa_bluetooth_device *d; + void *state = NULL; - list_adapters(&y); + pa_assert(y); + pa_assert(PA_REFCNT_VALUE(y) > 0); + pa_assert(address); - pa_dbus_sync_pending_list(&y.pending); - pa_assert(!y.pending); + if (!pa_hook_is_firing(&y->hook)) + pa_bluetooth_discovery_sync(y); - if (y.found_device) { - pa_assert(device_is_loaded(y.found_device)); + while ((d = pa_hashmap_iterate(y->devices, &state, NULL))) + if (pa_streq(d->address, address)) + return d; - if (!device_is_audio(y.found_device)) { - pa_bluetooth_device_free(y.found_device); - return NULL; - } - } - - return y.found_device; + return NULL; } -pa_bluetooth_device* pa_bluetooth_get_device(DBusConnection *c, const char* path) { - pa_bluetooth_discovery y; +const pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *y, const char* path) { + pa_assert(y); + pa_assert(PA_REFCNT_VALUE(y) > 0); + pa_assert(path); - memset(&y, 0, sizeof(y)); - y.mode = MODE_GET; - y.connection = c; - PA_LLIST_HEAD_INIT(pa_dbus_pending, y.pending); + if (!pa_hook_is_firing(&y->hook)) + pa_bluetooth_discovery_sync(y); - found_device(&y, path); + return pa_hashmap_get(y->devices, path); +} - pa_dbus_sync_pending_list(&y.pending); - pa_assert(!y.pending); +static int setup_dbus(pa_bluetooth_discovery *y) { + DBusError err; - if (y.found_device) { - pa_assert(device_is_loaded(y.found_device)); + dbus_error_init(&err); - if (!device_is_audio(y.found_device)) { - pa_bluetooth_device_free(y.found_device); - return NULL; - } + y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err); + + if (dbus_error_is_set(&err) || !y->connection) { + pa_log("Failed to get D-Bus connection: %s", err.message); + dbus_error_free(&err); + return -1; } - return y.found_device; + return 0; } -pa_bluetooth_discovery* pa_bluetooth_discovery_new(DBusConnection *c, pa_bluetooth_device_callback_t cb, struct userdata *u) { +pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { DBusError err; pa_bluetooth_discovery *y; pa_assert(c); - pa_assert(cb); dbus_error_init(&err); + if ((y = pa_shared_get(c, "bluetooth-discovery"))) + return pa_bluetooth_discovery_ref(y); + y = pa_xnew0(pa_bluetooth_discovery, 1); - y->mode = MODE_DISCOVER; - y->connection = c; - y->callback = cb; - y->userdata = u; + PA_REFCNT_INIT(y); + y->core = c; y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending); + pa_hook_init(&y->hook, y); + pa_shared_set(c, "bluetooth-discovery", y); + + if (setup_dbus(y) < 0) + goto fail; /* dynamic detection of bluetooth audio devices */ - if (!dbus_connection_add_filter(c, filter_cb, y, NULL)) { + if (!dbus_connection_add_filter(pa_dbus_connection_get(y->connection), filter_cb, y, NULL)) { pa_log_error("Failed to add filter function"); goto fail; } if (pa_dbus_add_matches( - c, &err, + pa_dbus_connection_get(y->connection), &err, "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'", @@ -768,28 +695,46 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_new(DBusConnection *c, pa_bluetoo return y; fail: + + if (y) + pa_bluetooth_discovery_unref(y); + dbus_error_free(&err); + return NULL; } -void pa_bluetooth_discovery_free(pa_bluetooth_discovery *y) { +pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) { + pa_assert(y); + pa_assert(PA_REFCNT_VALUE(y) > 0); + + PA_REFCNT_INC(y); + + return y; +} + +void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { pa_bluetooth_device *d; pa_assert(y); + pa_assert(PA_REFCNT_VALUE(y) > 0); + + if (PA_REFCNT_DEC(y) > 0) + return; pa_dbus_free_pending_list(&y->pending); if (y->devices) { while ((d = pa_hashmap_steal_first(y->devices))) { - run_callback(y, d, FALSE); - pa_bluetooth_device_free(d); + run_callback(y, d, TRUE); + device_free(d); } pa_hashmap_free(y->devices, NULL, NULL); } if (y->connection) { - pa_dbus_remove_matches(y->connection, + pa_dbus_remove_matches(pa_dbus_connection_get(y->connection), "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'", "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", @@ -798,16 +743,31 @@ void pa_bluetooth_discovery_free(pa_bluetooth_discovery *y) { "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL); - dbus_connection_remove_filter(y->connection, filter_cb, y); + dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y); + + pa_dbus_connection_unref(y->connection); } + + pa_hook_done(&y->hook); + + if (y->core) + pa_shared_remove(y->core, "bluetooth-discovery"); } void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *y) { pa_assert(y); + pa_assert(PA_REFCNT_VALUE(y) > 0); pa_dbus_sync_pending_list(&y->pending); } +pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y) { + pa_assert(y); + pa_assert(PA_REFCNT_VALUE(y) > 0); + + return &y->hook; +} + const char*pa_bluetooth_get_form_factor(uint32_t class) { unsigned i; const char *r; diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h index 0364c972..1d05e63c 100644 --- a/src/modules/bluetooth/bluetooth-util.h +++ b/src/modules/bluetooth/bluetooth-util.h @@ -40,7 +40,7 @@ struct pa_bluetooth_uuid { }; struct pa_bluetooth_device { - void *data; /* arbitrary information for the one owning the discovery object */ + pa_bool_t dead; int device_info_valid; /* 0: no results yet; 1: good results; -1: bad results ... */ int audio_sink_info_valid; /* ... same here ... */ @@ -64,17 +64,18 @@ struct pa_bluetooth_device { int headset_connected; }; -void pa_bluetooth_device_free(pa_bluetooth_device *d); +pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core); +pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y); +void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *d); -pa_bluetooth_device* pa_bluetooth_get_device(DBusConnection *c, const char* path); -pa_bluetooth_device* pa_bluetooth_find_device(DBusConnection *c, const char* address); - -typedef void (*pa_bluetooth_device_callback_t)(struct userdata *u, pa_bluetooth_device *d, pa_bool_t good); -pa_bluetooth_discovery* pa_bluetooth_discovery_new(DBusConnection *c, pa_bluetooth_device_callback_t cb, struct userdata *u); -void pa_bluetooth_discovery_free(pa_bluetooth_discovery *d); void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *d); -const char*pa_bluetooth_get_form_factor(uint32_t class); +const pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *d, const char* path); +const pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discovery *d, const char* address); + +pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *d); + +const char* pa_bluetooth_get_form_factor(uint32_t class); char *pa_bluetooth_cleanup_name(const char *name); diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 8d066a95..dbc7ab17 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -71,14 +71,17 @@ PA_MODULE_USAGE( "profile=<a2dp|hsp> " "rate=<sample rate> " "channels=<number of channels> " - "path=<device object path> " + "path=<device object path>"); + +/* +#ifdef NOKIA "sco_sink=<SCO over PCM sink name> " - "sco_source=<SCO over PCM source name>"); + "sco_source=<SCO over PCM source name>" +#endif +*/ /* TODO: not close fd when entering suspend mode in a2dp */ -/* TODO: BT_PCM_FLAG_NREC */ - static const char* const valid_modargs[] = { "name", "card_name", @@ -89,8 +92,10 @@ static const char* const valid_modargs[] = { "rate", "channels", "path", +#ifdef NOKIA "sco_sink", "sco_source", +#endif NULL }; @@ -98,7 +103,7 @@ struct a2dp_info { sbc_capabilities_t sbc_capabilities; sbc_t sbc; /* Codec data */ pa_bool_t sbc_initialized; /* Keep track if the encoder is initialized */ - size_t codesize; /* SBC codesize */ + size_t codesize, frame_length; /* SBC Codesize, frame_length. We simply cache those values here */ void* buffer; /* Codec transfer buffer */ size_t buffer_size; /* Size of the buffer */ @@ -108,8 +113,10 @@ struct a2dp_info { struct hsp_info { pcm_capabilities_t pcm_capabilities; +#ifdef NOKIA pa_sink *sco_sink; pa_source *sco_source; +#endif pa_hook_slot *sink_state_changed_slot; pa_hook_slot *source_state_changed_slot; }; @@ -124,6 +131,11 @@ struct userdata { pa_core *core; pa_module *module; + char *address; + char *path; + + pa_dbus_connection *connection; + pa_card *card; pa_sink *sink; pa_source *source; @@ -149,19 +161,18 @@ struct userdata { struct a2dp_info a2dp; struct hsp_info hsp; - pa_dbus_connection *connection; enum profile profile; pa_modargs *modargs; - pa_bluetooth_device *device; - int stream_write_type, stream_read_type; int service_write_type, service_read_type; }; +#ifdef NOKIA #define USE_SCO_OVER_PCM(u) (u->profile == PROFILE_HSP && (u->hsp.sco_sink && u->hsp.sco_source)) +#endif static int init_bt(struct userdata *u); static int init_profile(struct userdata *u); @@ -333,7 +344,7 @@ static int get_caps(struct userdata *u) { msg.getcaps_req.h.name = BT_GET_CAPABILITIES; msg.getcaps_req.h.length = sizeof(msg.getcaps_req); - pa_strlcpy(msg.getcaps_req.device, u->device->address, sizeof(msg.getcaps_req.device)); + pa_strlcpy(msg.getcaps_req.device, u->address, sizeof(msg.getcaps_req.device)); if (u->profile == PROFILE_A2DP) msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP; else { @@ -585,7 +596,8 @@ static void setup_sbc(struct a2dp_info *a2dp) { } a2dp->sbc.bitpool = active_capabilities->max_bitpool; - a2dp->codesize = (uint16_t) sbc_get_codesize(&a2dp->sbc); + a2dp->codesize = sbc_get_codesize(&a2dp->sbc); + a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc); } static int set_conf(struct userdata *u) { @@ -614,7 +626,7 @@ static int set_conf(struct userdata *u) { msg.setconf_req.h.name = BT_SET_CONFIGURATION; msg.setconf_req.h.length = sizeof(msg.setconf_req); - pa_strlcpy(msg.setconf_req.device, u->device->address, sizeof(msg.setconf_req.device)); + pa_strlcpy(msg.setconf_req.device, u->address, sizeof(msg.setconf_req.device)); msg.setconf_req.access_mode = u->profile == PROFILE_A2DP ? BT_CAPABILITIES_ACCESS_MODE_WRITE : BT_CAPABILITIES_ACCESS_MODE_READWRITE; msg.setconf_req.codec.transport = u->profile == PROFILE_A2DP ? BT_CAPABILITIES_TRANSPORT_A2DP : BT_CAPABILITIES_TRANSPORT_SCO; @@ -647,7 +659,12 @@ static int set_conf(struct userdata *u) { /* setup SBC encoder now we agree on parameters */ if (u->profile == PROFILE_A2DP) { setup_sbc(&u->a2dp); - u->block_size = u->a2dp.codesize; + + u->block_size = + ((u->link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) + / u->a2dp.frame_length + * u->a2dp.codesize); + pa_log_info("SBC parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", u->a2dp.sbc.allocation, u->a2dp.sbc.subbands, u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool); } else @@ -855,48 +872,62 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off static int hsp_process_render(struct userdata *u) { int ret = 0; - pa_memchunk memchunk; pa_assert(u); pa_assert(u->profile == PROFILE_HSP); pa_assert(u->sink); - pa_sink_render_full(u->sink, u->block_size, &memchunk); + /* First, render some data */ + if (!u->write_memchunk.memblock) + pa_sink_render_full(u->sink, u->block_size, &u->write_memchunk); + + pa_assert(u->write_memchunk.length == u->block_size); for (;;) { ssize_t l; const void *p; - p = (const uint8_t*) pa_memblock_acquire(memchunk.memblock) + memchunk.index; - l = pa_write(u->stream_fd, p, memchunk.length, &u->stream_write_type); - pa_memblock_release(memchunk.memblock); + /* Now write that data to the socket. The socket is of type + * SEQPACKET, and we generated the data of the MTU size, so this + * should just work. */ - pa_log_debug("Memblock written to socket: %lli bytes", (long long) l); + p = (const uint8_t*) pa_memblock_acquire(u->write_memchunk.memblock) + u->write_memchunk.index; + l = pa_write(u->stream_fd, p, u->write_memchunk.length, &u->stream_write_type); + pa_memblock_release(u->write_memchunk.memblock); pa_assert(l != 0); if (l < 0) { - if (errno == EINTR || errno == EAGAIN) /*** FIXME: EAGAIN handling borked ***/ + + if (errno == EINTR) + /* Retry right away if we got interrupted */ continue; - else { - pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(errno)); - ret = -1; + + else if (errno == EAGAIN) + /* Hmm, apparently the socket was not writable, give up for now */ break; - } - } else { - pa_assert((size_t) l <= memchunk.length); - memchunk.index += (size_t) l; - memchunk.length -= (size_t) l; + pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(errno)); + ret = -1; + break; + } - u->write_index += (uint64_t) l; + pa_assert((size_t) l <= u->write_memchunk.length); - if (memchunk.length <= 0) - break; + if ((size_t) l != u->write_memchunk.length) { + pa_log_error("Wrote memory block to socket only partially! %llu written, wanted to write %llu.", + (unsigned long long) l, + (unsigned long long) u->write_memchunk.length); + ret = -1; + break; } - } - pa_memblock_unref(memchunk.memblock); + u->write_index += (uint64_t) u->write_memchunk.length; + pa_memblock_unref(u->write_memchunk.memblock); + pa_memchunk_reset(&u->write_memchunk); + + break; + } return ret; } @@ -921,20 +952,27 @@ static int hsp_process_push(struct userdata *u) { pa_memblock_release(memchunk.memblock); if (l <= 0) { - if (l < 0 && (errno == EINTR || errno == EAGAIN)) /*** FIXME: EAGAIN handling borked ***/ + + if (l < 0 && errno == EINTR) + /* Retry right away if we got interrupted */ continue; - else { - pa_log_error("Failed to read data from SCO socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF"); - ret = -1; + + else if (l < 0 && errno == EAGAIN) + /* Hmm, apparently the socket was not readable, give up for now. */ break; - } - } else { - memchunk.length = (size_t) l; - u->read_index += (uint64_t) l; - pa_source_post(u->source, &memchunk); + pa_log_error("Failed to read data from SCO socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF"); + ret = -1; break; } + + pa_assert((size_t) l <= pa_memblock_get_length(memchunk.memblock)); + + memchunk.length = (size_t) l; + u->read_index += (uint64_t) l; + + pa_source_post(u->source, &memchunk); + break; } pa_memblock_unref(memchunk.memblock); @@ -942,127 +980,146 @@ static int hsp_process_push(struct userdata *u) { return ret; } +static void a2dp_prepare_buffer(struct userdata *u) { + pa_assert(u); + + if (u->a2dp.buffer_size >= u->link_mtu) + return; + + u->a2dp.buffer_size = 2 * u->link_mtu; + pa_xfree(u->a2dp.buffer); + u->a2dp.buffer = pa_xmalloc(u->a2dp.buffer_size); +} + static int a2dp_process_render(struct userdata *u) { - size_t frame_size; struct a2dp_info *a2dp; struct rtp_header *header; struct rtp_payload *payload; - size_t left; + size_t nbytes; void *d; const void *p; + size_t to_write, to_encode; unsigned frame_count; - size_t written; - uint64_t writing_at; + int ret = 0; pa_assert(u); pa_assert(u->profile == PROFILE_A2DP); pa_assert(u->sink); - a2dp = &u->a2dp; + /* First, render some data */ + if (!u->write_memchunk.memblock) + pa_sink_render_full(u->sink, u->block_size, &u->write_memchunk); - if (a2dp->buffer_size < u->link_mtu) { - a2dp->buffer_size = 2*u->link_mtu; - pa_xfree(a2dp->buffer); - a2dp->buffer = pa_xmalloc(a2dp->buffer_size); - } + pa_assert(u->write_memchunk.length == u->block_size); + + a2dp_prepare_buffer(u); - header = (struct rtp_header*) a2dp->buffer; + a2dp = &u->a2dp; + header = a2dp->buffer; payload = (struct rtp_payload*) ((uint8_t*) a2dp->buffer + sizeof(*header)); - d = (uint8_t*) a2dp->buffer + sizeof(*header) + sizeof(*payload); - left = a2dp->buffer_size - sizeof(*header) - sizeof(*payload); - frame_size = sbc_get_frame_length(&a2dp->sbc); frame_count = 0; - writing_at = u->write_index; + /* Try to create a packet of the full MTU */ - do { - ssize_t encoded; + p = (const uint8_t*) pa_memblock_acquire(u->write_memchunk.memblock) + u->write_memchunk.index; + to_encode = u->write_memchunk.length; - if (!u->write_memchunk.memblock) - pa_sink_render_full(u->sink, u->block_size, &u->write_memchunk); + d = (uint8_t*) a2dp->buffer + sizeof(*header) + sizeof(*payload); + to_write = a2dp->buffer_size - sizeof(*header) - sizeof(*payload); + + while (PA_LIKELY(to_encode > 0 && to_write > 0)) { + size_t written; + ssize_t encoded; - p = (const uint8_t*) pa_memblock_acquire(u->write_memchunk.memblock) + u->write_memchunk.index; encoded = sbc_encode(&a2dp->sbc, - p, u->write_memchunk.length, - d, left, + p, to_encode, + d, to_write, &written); - PA_ONCE_BEGIN { - pa_log_debug("Using SBC encoder implementation: %s", pa_strnull(sbc_get_implementation_info(&a2dp->sbc))); - } PA_ONCE_END; - - pa_memblock_release(u->write_memchunk.memblock); - - if (encoded <= 0) { - pa_log_error("SBC encoding error (%d)", encoded); + if (PA_UNLIKELY(encoded <= 0)) { + pa_log_error("SBC encoding error (%li)", (long) encoded); + pa_memblock_release(u->write_memchunk.memblock); return -1; } - pa_assert((size_t) encoded <= u->write_memchunk.length); - pa_assert((size_t) encoded == sbc_get_codesize(&a2dp->sbc)); - - pa_assert((size_t) written <= left); - pa_assert((size_t) written == sbc_get_frame_length(&a2dp->sbc)); +/* pa_log_debug("SBC: encoded: %lu; written: %lu", (unsigned long) encoded, (unsigned long) written); */ +/* pa_log_debug("SBC: codesize: %lu; frame_length: %lu", (unsigned long) a2dp->codesize, (unsigned long) a2dp->frame_length); */ -/* pa_log_debug("SBC: encoded: %d; written: %d", encoded, written); */ + pa_assert_fp((size_t) encoded <= to_encode); + pa_assert_fp((size_t) encoded == a2dp->codesize); - u->write_memchunk.index += encoded; - u->write_memchunk.length -= encoded; + pa_assert_fp((size_t) written <= to_write); + pa_assert_fp((size_t) written == a2dp->frame_length); - if (u->write_memchunk.length <= 0) { - pa_memblock_unref(u->write_memchunk.memblock); - pa_memchunk_reset(&u->write_memchunk); - } - - u->write_index += encoded; + p = (const uint8_t*) p + encoded; + to_encode -= encoded; d = (uint8_t*) d + written; - left -= written; + to_write -= written; frame_count++; + } - } while (((uint8_t*) d - ((uint8_t*) a2dp->buffer + sbc_get_frame_length(&a2dp->sbc))) < (ptrdiff_t) u->link_mtu); + pa_memblock_release(u->write_memchunk.memblock); + + pa_assert(to_encode == 0); + + PA_ONCE_BEGIN { + pa_log_debug("Using SBC encoder implementation: %s", pa_strnull(sbc_get_implementation_info(&a2dp->sbc))); + } PA_ONCE_END; /* write it to the fifo */ memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload)); - payload->frame_count = frame_count; header->v = 2; header->pt = 1; header->sequence_number = htons(a2dp->seq_num++); - header->timestamp = htonl(writing_at / frame_size); + header->timestamp = htonl(u->write_index / pa_frame_size(&u->sink->sample_spec)); header->ssrc = htonl(1); + payload->frame_count = frame_count; - p = a2dp->buffer; - left = (uint8_t*) d - (uint8_t*) a2dp->buffer; + nbytes = (uint8_t*) d - (uint8_t*) a2dp->buffer; for (;;) { ssize_t l; - l = pa_write(u->stream_fd, p, left, &u->stream_write_type); -/* pa_log_debug("write: requested %lu bytes; written %li bytes; mtu=%li", (unsigned long) left, (long) l, (unsigned long) u->link_mtu); */ + l = pa_write(u->stream_fd, a2dp->buffer, nbytes, &u->stream_write_type); pa_assert(l != 0); if (l < 0) { - if (errno == EINTR || errno == EAGAIN) /*** FIXME: EAGAIN handling borked ***/ - continue; - else { - pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno)); - return -1; - } - } else { - pa_assert((size_t) l <= left); - d = (uint8_t*) d + l; - left -= l; + if (errno == EINTR) + /* Retry right away if we got interrupted */ + continue; - if (left <= 0) + else if (errno == EAGAIN) + /* Hmm, apparently the socket was not writable, give up for now */ break; + + pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno)); + ret = -1; + break; } + + pa_assert((size_t) l <= nbytes); + + if ((size_t) l != nbytes) { + pa_log_warn("Wrote memory block to socket only partially! %llu written, wanted to write %llu.", + (unsigned long long) l, + (unsigned long long) nbytes); + ret = -1; + break; + } + + u->write_index += (uint64_t) u->write_memchunk.length; + pa_memblock_unref(u->write_memchunk.memblock); + pa_memchunk_reset(&u->write_memchunk); + + break; } - return 0; + return ret; } static void thread_func(void *userdata) { @@ -1192,145 +1249,100 @@ finish: pa_log_debug("IO thread shutting down"); } -/* static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) { */ -/* DBusMessageIter arg_i; */ -/* DBusError err; */ -/* const char *value; */ -/* struct userdata *u; */ - -/* pa_assert(bus); */ -/* pa_assert(msg); */ -/* pa_assert(userdata); */ -/* u = userdata; */ - -/* pa_log_debug("dbus: interface=%s, path=%s, member=%s\n", */ -/* dbus_message_get_interface(msg), */ -/* dbus_message_get_path(msg), */ -/* dbus_message_get_member(msg)); */ - -/* dbus_error_init(&err); */ - -/* if (!dbus_message_has_path(msg, u->path)) */ -/* goto done; */ - -/* if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged") || */ -/* dbus_message_is_signal(msg, "org.bluez.AudioSink", "PropertyChanged")) { */ - -/* struct device *d; */ -/* const char *profile; */ -/* DBusMessageIter variant_i; */ -/* dbus_uint16_t gain; */ - -/* if (!dbus_message_iter_init(msg, &arg_i)) { */ -/* pa_log("dbus: message has no parameters"); */ -/* goto done; */ -/* } */ +static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) { + DBusError err; + struct userdata *u; -/* if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_STRING) { */ -/* pa_log("Property name not a string."); */ -/* goto done; */ -/* } */ + pa_assert(bus); + pa_assert(m); + pa_assert_se(u = userdata); -/* dbus_message_iter_get_basic(&arg_i, &value); */ + dbus_error_init(&err); -/* if (!dbus_message_iter_next(&arg_i)) { */ -/* pa_log("Property value missing"); */ -/* goto done; */ -/* } */ + pa_log_debug("dbus: interface=%s, path=%s, member=%s\n", + dbus_message_get_interface(m), + dbus_message_get_path(m), + dbus_message_get_member(m)); -/* if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_VARIANT) { */ -/* pa_log("Property value not a variant."); */ -/* goto done; */ -/* } */ + if (!dbus_message_has_path(m, u->path)) + goto fail; -/* dbus_message_iter_recurse(&arg_i, &variant_i); */ + if (dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged") || + dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) { -/* if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_UINT16) { */ -/* dbus_message_iter_get_basic(&variant_i, &gain); */ + dbus_uint16_t gain; + pa_cvolume v; -/* if (pa_streq(value, "SpeakerGain")) { */ -/* pa_log("spk gain: %d", gain); */ -/* pa_cvolume_set(&u->sink->virtual_volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); */ -/* pa_subscription_post(u->sink->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index); */ -/* } else { */ -/* pa_log("mic gain: %d", gain); */ -/* if (!u->source) */ -/* goto done; */ + if (!dbus_message_get_args(m, &err, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID) || gain > 15) { + pa_log("Failed to parse org.bluez.Headset.{Speaker|Microphone}GainChanged: %s", err.message); + goto fail; + } -/* pa_cvolume_set(&u->source->virtual_volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); */ -/* pa_subscription_post(u->source->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, u->source->index); */ -/* } */ -/* } */ -/* } */ + if (u->profile == PROFILE_HSP) { + if (u->sink && dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged")) { -/* done: */ -/* dbus_error_free(&err); */ -/* return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; */ -/* } */ + pa_cvolume_set(&v, u->sink->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); + pa_sink_volume_changed(u->sink, &v); -/* static int sink_get_volume_cb(pa_sink *s) { */ -/* struct userdata *u = s->userdata; */ -/* pa_assert(u); */ + } else if (u->source && dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) { -/* /\* refresh? *\/ */ + pa_cvolume_set(&v, u->sink->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); + pa_source_volume_changed(u->source, &v); + } + } + } -/* return 0; */ -/* } */ +fail: + dbus_error_free(&err); -/* static int source_get_volume_cb(pa_source *s) { */ -/* struct userdata *u = s->userdata; */ -/* pa_assert(u); */ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} -/* /\* refresh? *\/ */ +static void sink_set_volume_cb(pa_sink *s) { + struct userdata *u = s->userdata; + DBusMessage *m; + dbus_uint16_t gain; -/* return 0; */ -/* } */ + pa_assert(u); -/* static int sink_set_volume_cb(pa_sink *s) { */ -/* DBusError e; */ -/* DBusMessage *m, *r; */ -/* DBusMessageIter it, itvar; */ -/* dbus_uint16_t vol; */ -/* const char *spkgain = "SpeakerGain"; */ -/* struct userdata *u = s->userdata; */ -/* pa_assert(u); */ + if (u->profile != PROFILE_HSP) + return; -/* dbus_error_init(&e); */ + gain = (pa_cvolume_max(&s->virtual_volume) * 15) / PA_VOLUME_NORM; -/* vol = ((float) pa_cvolume_max(&s->virtual_volume) / PA_VOLUME_NORM) * 15; */ -/* pa_log_debug("set headset volume: %d", vol); */ + if (gain > 15) + gain = 15; -/* pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetProperty")); */ -/* dbus_message_iter_init_append(m, &it); */ -/* dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &spkgain); */ -/* dbus_message_iter_open_container(&it, DBUS_TYPE_VARIANT, DBUS_TYPE_UINT16_AS_STRING, &itvar); */ -/* dbus_message_iter_append_basic(&itvar, DBUS_TYPE_UINT16, &vol); */ -/* dbus_message_iter_close_container(&it, &itvar); */ + pa_cvolume_set(&s->virtual_volume, u->sink->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); -/* r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e); */ + pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetSpeakerGain")); + pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID)); + pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->connection), m, NULL)); + dbus_message_unref(m); +} -/* finish: */ -/* if (m) */ -/* dbus_message_unref(m); */ -/* if (r) */ -/* dbus_message_unref(r); */ +static void source_set_volume_cb(pa_source *s) { + struct userdata *u = s->userdata; + DBusMessage *m; + dbus_uint16_t gain; -/* dbus_error_free(&e); */ + pa_assert(u); -/* return 0; */ -/* } */ + if (u->profile != PROFILE_HSP) + return; -/* static int source_set_volume_cb(pa_source *s) { */ -/* dbus_uint16_t vol; */ -/* struct userdata *u = s->userdata; */ -/* pa_assert(u); */ + gain = (pa_cvolume_max(&s->virtual_volume) * 15) / PA_VOLUME_NORM; -/* vol = ((float)pa_cvolume_max(&s->virtual_volume) / PA_VOLUME_NORM) * 15; */ + if (gain > 15) + gain = 15; -/* pa_log_debug("set headset mic volume: %d (not implemented yet)", vol); */ + pa_cvolume_set(&s->virtual_volume, u->source->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); -/* return 0; */ -/* } */ + pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetMicrophoneGain")); + pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID)); + pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->connection), m, NULL)); + dbus_message_unref(m); +} static char *get_name(const char *type, pa_modargs *ma, const char *device_id, pa_bool_t *namereg_fail) { char *t; @@ -1360,6 +1372,8 @@ static char *get_name(const char *type, pa_modargs *ma, const char *device_id, p return pa_sprintf_malloc("bluez_%s.%s", type, n); } +#ifdef NOKIA + static void sco_over_pcm_state_update(struct userdata *u) { pa_assert(u); pa_assert(USE_SCO_OVER_PCM(u)); @@ -1414,8 +1428,11 @@ static pa_hook_result_t source_state_changed_cb(pa_core *c, pa_source *s, struct return PA_HOOK_OK; } +#endif + static int add_sink(struct userdata *u) { +#ifdef NOKIA if (USE_SCO_OVER_PCM(u)) { pa_proplist *p; @@ -1428,7 +1445,10 @@ static int add_sink(struct userdata *u) { if (!u->hsp.sink_state_changed_slot) u->hsp.sink_state_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_cb, u); - } else { + } else +#endif + + { pa_sink_new_data data; pa_bool_t b; @@ -1438,10 +1458,10 @@ static int add_sink(struct userdata *u) { pa_sink_new_data_set_sample_spec(&data, &u->sample_spec); pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco"); data.card = u->card; - data.name = get_name("sink", u->modargs, u->device->address, &b); + data.name = get_name("sink", u->modargs, u->address, &b); data.namereg_fail = b; - u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); + u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY | (u->profile == PROFILE_HSP ? PA_SINK_HW_VOLUME_CTRL : 0)); pa_sink_new_data_done(&data); if (!u->sink) { @@ -1453,26 +1473,28 @@ static int add_sink(struct userdata *u) { u->sink->parent.process_msg = sink_process_msg; } -/* u->sink->get_volume = sink_get_volume_cb; */ -/* u->sink->set_volume = sink_set_volume_cb; */ + if (u->profile == PROFILE_HSP) { + u->sink->set_volume = sink_set_volume_cb; + u->sink->n_volume_steps = 16; + } return 0; } static int add_source(struct userdata *u) { - pa_proplist *p; +#ifdef NOKIA if (USE_SCO_OVER_PCM(u)) { u->source = u->hsp.sco_source; - p = pa_proplist_new(); - pa_proplist_sets(p, "bluetooth.protocol", "sco"); - pa_proplist_update(u->source->proplist, PA_UPDATE_MERGE, p); - pa_proplist_free(p); + pa_proplist_sets(u->source->proplist, "bluetooth.protocol", "sco"); if (!u->hsp.source_state_changed_slot) u->hsp.source_state_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_state_changed_cb, u); - } else { + } else +#endif + + { pa_source_new_data data; pa_bool_t b; @@ -1482,10 +1504,10 @@ static int add_source(struct userdata *u) { pa_source_new_data_set_sample_spec(&data, &u->sample_spec); pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco"); data.card = u->card; - data.name = get_name("source", u->modargs, u->device->address, &b); + data.name = get_name("source", u->modargs, u->address, &b); data.namereg_fail = b; - u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); + u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY | (u->profile == PROFILE_HSP ? PA_SOURCE_HW_VOLUME_CTRL : 0)); pa_source_new_data_done(&data); if (!u->source) { @@ -1497,13 +1519,11 @@ static int add_source(struct userdata *u) { u->source->parent.process_msg = source_process_msg; } -/* u->source->get_volume = source_get_volume_cb; */ -/* u->source->set_volume = source_set_volume_cb; */ - - p = pa_proplist_new(); - pa_proplist_sets(p, "bluetooth.nrec", pa_yes_no(u->hsp.pcm_capabilities.flags & BT_PCM_FLAG_NREC)); - pa_proplist_update(u->source->proplist, PA_UPDATE_MERGE, p); - pa_proplist_free(p); + if (u->profile == PROFILE_HSP) { + pa_proplist_sets(u->source->proplist, "bluetooth.nrec", (u->hsp.pcm_capabilities.flags & BT_PCM_FLAG_NREC) ? "1" : "0"); + u->source->set_volume = source_set_volume_cb; + u->source->n_volume_steps = 16; + } return 0; } @@ -1514,12 +1534,20 @@ static void shutdown_bt(struct userdata *u) { if (u->stream_fd >= 0) { pa_close(u->stream_fd); u->stream_fd = -1; + + u->stream_write_type = 0; + u->stream_read_type = 0; } if (u->service_fd >= 0) { pa_close(u->service_fd); u->service_fd = -1; } + + if (u->write_memchunk.memblock) { + pa_memblock_unref(u->write_memchunk.memblock); + pa_memchunk_reset(&u->write_memchunk); + } } static int init_bt(struct userdata *u) { @@ -1553,10 +1581,12 @@ static int setup_bt(struct userdata *u) { pa_log_debug("Connection to the device configured"); +#ifdef NOKIA if (USE_SCO_OVER_PCM(u)) { pa_log_debug("Configured to use SCO over PCM"); return 0; } +#endif pa_log_debug("Got the stream socket"); @@ -1634,6 +1664,7 @@ static int start_thread(struct userdata *u) { u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll); +#ifdef NOKIA if (USE_SCO_OVER_PCM(u)) { if (start_stream_fd(u) < 0) return -1; @@ -1643,6 +1674,7 @@ static int start_thread(struct userdata *u) { /* FIXME: monitor stream_fd error */ return 0; } +#endif if (!(u->thread = pa_thread_new(thread_func, u))) { pa_log_error("Failed to create IO thread"); @@ -1654,12 +1686,18 @@ static int start_thread(struct userdata *u) { pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); pa_sink_put(u->sink); + + if (u->sink->set_volume) + u->sink->set_volume(u->sink); } if (u->source) { pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); pa_source_put(u->source); + + if (u->source->set_volume) + u->source->set_volume(u->source); } return 0; @@ -1678,24 +1716,23 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { if (u->sink) { inputs = pa_sink_move_all_start(u->sink); +#ifdef NOKIA if (!USE_SCO_OVER_PCM(u)) pa_sink_unlink(u->sink); +#endif } if (u->source) { outputs = pa_source_move_all_start(u->source); +#ifdef NOKIA if (!USE_SCO_OVER_PCM(u)) pa_source_unlink(u->source); +#endif } stop_thread(u); shutdown_bt(u); - if (u->write_memchunk.memblock) { - pa_memblock_unref(u->write_memchunk.memblock); - pa_memchunk_reset(&u->write_memchunk); - } - u->profile = *d; u->sample_spec = u->requested_sample_spec; @@ -1724,7 +1761,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { return 0; } -static int add_card(struct userdata *u, const char * default_profile) { +static int add_card(struct userdata *u, const char *default_profile, const pa_bluetooth_device *device) { pa_card_new_data data; pa_bool_t b; pa_card_profile *p; @@ -1736,24 +1773,24 @@ static int add_card(struct userdata *u, const char * default_profile) { data.driver = __FILE__; data.module = u->module; - n = pa_bluetooth_cleanup_name(u->device->name); + n = pa_bluetooth_cleanup_name(device->name); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, n); pa_xfree(n); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device->address); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, device->address); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "bluez"); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound"); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_BUS, "bluetooth"); - if ((ff = pa_bluetooth_get_form_factor(u->device->class))) + if ((ff = pa_bluetooth_get_form_factor(device->class))) pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, ff); - pa_proplist_sets(data.proplist, "bluez.path", u->device->path); - pa_proplist_setf(data.proplist, "bluez.class", "0x%06x", (unsigned) u->device->class); - pa_proplist_sets(data.proplist, "bluez.name", u->device->name); - data.name = get_name("card", u->modargs, u->device->address, &b); + pa_proplist_sets(data.proplist, "bluez.path", device->path); + pa_proplist_setf(data.proplist, "bluez.class", "0x%06x", (unsigned) device->class); + pa_proplist_sets(data.proplist, "bluez.name", device->name); + data.name = get_name("card", u->modargs, device->address, &b); data.namereg_fail = b; data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - if (u->device->audio_sink_info_valid > 0) { + if (device->audio_sink_info_valid > 0) { p = pa_card_profile_new("a2dp", _("High Fidelity Playback (A2DP)"), sizeof(enum profile)); p->priority = 10; p->n_sinks = 1; @@ -1767,7 +1804,7 @@ static int add_card(struct userdata *u, const char * default_profile) { pa_hashmap_put(data.profiles, p->name, p); } - if (u->device->headset_info_valid > 0) { + if (device->headset_info_valid > 0) { p = pa_card_profile_new("hsp", _("Telephony Duplex (HSP/HFP)"), sizeof(enum profile)); p->priority = 20; p->n_sinks = 1; @@ -1812,46 +1849,56 @@ static int add_card(struct userdata *u, const char * default_profile) { return 0; } -static int setup_dbus(struct userdata *u) { - DBusError error; - - dbus_error_init(&error); - - u->connection = pa_dbus_bus_get(u->core, DBUS_BUS_SYSTEM, &error); - if (dbus_error_is_set(&error) || (!u->connection)) { - pa_log("Failed to get D-Bus connection: %s", error.message); - dbus_error_free(&error); - return -1; - } - - return 0; -} +static const pa_bluetooth_device* find_device(struct userdata *u, pa_bluetooth_discovery *y, const char *address, const char *path) { + const pa_bluetooth_device *d = NULL; -static int find_device(struct userdata *u, const char *address, const char *path) { pa_assert(u); + pa_assert(y); if (!address && !path) { pa_log_error("Failed to get device address/path from module arguments."); - return -1; + return NULL; } if (path) { - if (!(u->device = pa_bluetooth_get_device(pa_dbus_connection_get(u->connection), path))) { + if (!(d = pa_bluetooth_discovery_get_by_path(y, path))) { pa_log_error("%s is not a valid BlueZ audio device.", path); - return -1; + return NULL; } - if (address && !(pa_streq(u->device->address, address))) { + if (address && !(pa_streq(d->address, address))) { pa_log_error("Passed path %s and address %s don't match.", path, address); - return -1; + return NULL; } + } else { - if (!(u->device = pa_bluetooth_find_device(pa_dbus_connection_get(u->connection), address))) { + if (!(d = pa_bluetooth_discovery_get_by_address(y, address))) { pa_log_error("%s is not known.", address); - return -1; + return NULL; } } + if (d) { + u->address = pa_xstrdup(d->address); + u->path = pa_xstrdup(d->path); + } + + return d; +} + +static int setup_dbus(struct userdata *u) { + DBusError err; + + dbus_error_init(&err); + + u->connection = pa_dbus_bus_get(u->core, DBUS_BUS_SYSTEM, &err); + + if (dbus_error_is_set(&err) || !u->connection) { + pa_log("Failed to get D-Bus connection: %s", err.message); + dbus_error_free(&err); + return -1; + } + return 0; } @@ -1860,9 +1907,15 @@ int pa__init(pa_module* m) { uint32_t channels; struct userdata *u; const char *address, *path; + const pa_bluetooth_device *d; + pa_bluetooth_discovery *y = NULL; + DBusError err; + char *mike, *speaker; pa_assert(m); + dbus_error_init(&err); + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log_error("Failed to parse module arguments"); goto fail; @@ -1877,6 +1930,7 @@ int pa__init(pa_module* m) { u->sample_spec = m->core->default_sample_spec; u->modargs = ma; +#ifdef NOKIA if (pa_modargs_get_value(ma, "sco_sink", NULL) && !(u->hsp.sco_sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_sink", NULL), PA_NAMEREG_SINK))) { pa_log("SCO sink not found"); @@ -1888,6 +1942,7 @@ int pa__init(pa_module* m) { pa_log("SCO source not found"); goto fail; } +#endif if (pa_modargs_get_value_u32(ma, "rate", &u->sample_spec.rate) < 0 || u->sample_spec.rate <= 0 || u->sample_spec.rate > PA_RATE_MAX) { @@ -1904,65 +1959,57 @@ int pa__init(pa_module* m) { u->sample_spec.channels = (uint8_t) channels; u->requested_sample_spec = u->sample_spec; - if (setup_dbus(u) < 0) - goto fail; - address = pa_modargs_get_value(ma, "address", NULL); path = pa_modargs_get_value(ma, "path", NULL); - if (find_device(u, address, path) < 0) + if (setup_dbus(u) < 0) + goto fail; + + if (!(y = pa_bluetooth_discovery_get(m->core))) goto fail; - pa_assert(u->device); + if (!(d = find_device(u, y, address, path))) + goto fail; /* Add the card structure. This will also initialize the default profile */ - if (add_card(u, pa_modargs_get_value(ma, "profile", NULL)) < 0) + if (add_card(u, pa_modargs_get_value(ma, "profile", NULL), d) < 0) goto fail; + pa_bluetooth_discovery_unref(y); + y = NULL; + /* Connect to the BT service and query capabilities */ if (init_bt(u) < 0) goto fail; + if (!dbus_connection_add_filter(pa_dbus_connection_get(u->connection), filter_cb, u, NULL)) { + pa_log_error("Failed to add filter function"); + goto fail; + } + + speaker = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='SpeakerGainChanged',path='%s'", u->path); + mike = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='MicrophoneGainChanged',path='%s'", u->path); + + if (pa_dbus_add_matches( + pa_dbus_connection_get(u->connection), &err, + speaker, + mike, + NULL) < 0) { + + pa_xfree(speaker); + pa_xfree(mike); + + pa_log("Failed to add D-Bus matches: %s", err.message); + goto fail; + } + + pa_xfree(speaker); + pa_xfree(mike); + if (u->profile != PROFILE_OFF) if (init_profile(u) < 0) goto fail; -/* if (u->path) { */ -/* DBusError err; */ -/* dbus_error_init(&err); */ -/* char *t; */ - - -/* if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) { */ -/* pa_log_error("Failed to add filter function"); */ -/* goto fail; */ -/* } */ - -/* if (u->transport == BT_CAPABILITIES_TRANSPORT_SCO || */ -/* u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */ -/* t = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path); */ -/* dbus_bus_add_match(pa_dbus_connection_get(u->conn), t, &err); */ -/* pa_xfree(t); */ - -/* if (dbus_error_is_set(&err)) { */ -/* pa_log_error("Unable to subscribe to org.bluez.Headset signals: %s: %s", err.name, err.message); */ -/* goto fail; */ -/* } */ -/* } */ - -/* if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP || */ -/* u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */ -/* t = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path); */ -/* dbus_bus_add_match(pa_dbus_connection_get(u->conn), t, &err); */ -/* pa_xfree(t); */ - -/* if (dbus_error_is_set(&err)) { */ -/* pa_log_error("Unable to subscribe to org.bluez.AudioSink signals: %s: %s", err.name, err.message); */ -/* goto fail; */ -/* } */ -/* } */ -/* } */ - if (u->sink || u->source) if (start_thread(u) < 0) goto fail; @@ -1970,7 +2017,14 @@ int pa__init(pa_module* m) { return 0; fail: + + if (y) + pa_bluetooth_discovery_unref(y); + pa__done(m); + + dbus_error_free(&err); + return -1; } @@ -1992,39 +2046,39 @@ void pa__done(pa_module *m) { if (!(u = m->userdata)) return; - if (u->sink && !USE_SCO_OVER_PCM(u)) + if (u->sink +#ifdef NOKIA + && !USE_SCO_OVER_PCM(u) +#endif + ) pa_sink_unlink(u->sink); - if (u->source && !USE_SCO_OVER_PCM(u)) + if (u->source +#ifdef NOKIA + && !USE_SCO_OVER_PCM(u) +#endif + ) pa_source_unlink(u->source); stop_thread(u); if (u->connection) { -/* DBusError error; */ -/* char *t; */ - -/* if (u->transport == BT_CAPABILITIES_TRANSPORT_SCO || */ -/* u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */ - -/* t = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path); */ -/* dbus_error_init(&error); */ -/* dbus_bus_remove_match(pa_dbus_connection_get(u->conn), t, &error); */ -/* dbus_error_free(&error); */ -/* pa_xfree(t); */ -/* } */ - -/* if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP || */ -/* u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */ - -/* t = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path); */ -/* dbus_error_init(&error); */ -/* dbus_bus_remove_match(pa_dbus_connection_get(u->conn), t, &error); */ -/* dbus_error_free(&error); */ -/* pa_xfree(t); */ -/* } */ - -/* dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), filter_cb, u); */ + + if (u->path) { + char *speaker, *mike; + speaker = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='SpeakerGainChanged',path='%s'", u->path); + mike = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='MicrophoneGainChanged',path='%s'", u->path); + + pa_dbus_remove_matches(pa_dbus_connection_get(u->connection), + speaker, + mike, + NULL); + + pa_xfree(speaker); + pa_xfree(mike); + } + + dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), filter_cb, u); pa_dbus_connection_unref(u->connection); } @@ -2036,12 +2090,6 @@ void pa__done(pa_module *m) { shutdown_bt(u); - if (u->device) - pa_bluetooth_device_free(u->device); - - if (u->write_memchunk.memblock) - pa_memblock_unref(u->write_memchunk.memblock); - if (u->a2dp.buffer) pa_xfree(u->a2dp.buffer); @@ -2050,5 +2098,8 @@ void pa__done(pa_module *m) { if (u->modargs) pa_modargs_free(u->modargs); + pa_xfree(u->address); + pa_xfree(u->path); + pa_xfree(u); } diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c index 4586d8ca..4805e5f5 100644 --- a/src/modules/bluetooth/module-bluetooth-discover.c +++ b/src/modules/bluetooth/module-bluetooth-discover.c @@ -42,13 +42,20 @@ PA_MODULE_AUTHOR("Joao Paulo Rechi Vita"); PA_MODULE_DESCRIPTION("Detect available bluetooth audio devices and load bluetooth audio drivers"); PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_USAGE("sco_sink=<name of sink> " - "sco_source=<name of source>" - "async=<Asynchronous initialization?>"); +PA_MODULE_USAGE("async=<Asynchronous initialization?>"); + +/* +#ifdef NOKIA + "sco_sink=<name of sink> " + "sco_source=<name of source>" +#endif +*/ static const char* const valid_modargs[] = { +#ifdef NOKIA "sco_sink", "sco_source", +#endif "async", NULL }; @@ -57,19 +64,28 @@ struct userdata { pa_module *module; pa_modargs *modargs; pa_core *core; - pa_dbus_connection *connection; pa_bluetooth_discovery *discovery; + pa_hook_slot *slot; + pa_hashmap *hashmap; }; -static void load_module_for_device(struct userdata *u, pa_bluetooth_device *d, pa_bool_t good) { +static pa_hook_result_t load_module_for_device(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) { + uint32_t midx; + pa_assert(u); pa_assert(d); - if (good && + + if (!(midx = PA_PTR_TO_UINT(pa_hashmap_get(u->hashmap, d->path)))) + midx = PA_INVALID_INDEX; + else + midx--; + + if (!d->dead && d->device_connected > 0 && (d->audio_sink_connected > 0 || d->headset_connected > 0)) { - if (((uint32_t) PA_PTR_TO_UINT(d->data))-1 == PA_INVALID_INDEX) { + if (midx == PA_INVALID_INDEX) { pa_module *m = NULL; char *args; @@ -77,6 +93,7 @@ static void load_module_for_device(struct userdata *u, pa_bluetooth_device *d, p args = pa_sprintf_malloc("address=\"%s\" path=\"%s\" profile=\"%s\"", d->address, d->path, d->headset_connected ? "hsp" : "a2dp"); +#ifdef NOKIA if (pa_modargs_get_value(u->modargs, "sco_sink", NULL) && pa_modargs_get_value(u->modargs, "sco_source", NULL)) { char *tmp; @@ -87,44 +104,32 @@ static void load_module_for_device(struct userdata *u, pa_bluetooth_device *d, p pa_xfree(args); args = tmp; } +#endif pa_log_debug("Loading module-bluetooth-device %s", args); m = pa_module_load(u->module->core, "module-bluetooth-device", args); pa_xfree(args); if (m) - d->data = PA_UINT_TO_PTR((uint32_t) (m->index+1)); + pa_hashmap_put(u->hashmap, d->path, PA_UINT_TO_PTR((uint32_t) (m->index+1))); else pa_log_debug("Failed to load module for device %s", d->path); } } else { - if (((uint32_t) PA_PTR_TO_UINT(d->data))-1 != PA_INVALID_INDEX) { + if (midx != PA_INVALID_INDEX) { /* Hmm, disconnection? Then let's unload our module */ pa_log_debug("Unloading module for %s", d->path); - pa_module_unload_request_by_index(u->core, (uint32_t) (PA_PTR_TO_UINT(d->data))-1, TRUE); - d->data = NULL; - } - } -} - -static int setup_dbus(struct userdata *u) { - DBusError err; - - dbus_error_init(&err); - - u->connection = pa_dbus_bus_get(u->core, DBUS_BUS_SYSTEM, &err); + pa_module_unload_request_by_index(u->core, midx, TRUE); - if (dbus_error_is_set(&err) || !u->connection) { - pa_log("Failed to get D-Bus connection: %s", err.message); - dbus_error_free(&err); - return -1; + pa_hashmap_remove(u->hashmap, d->path); + } } - return 0; + return PA_HOOK_OK; } int pa__init(pa_module* m) { @@ -149,12 +154,12 @@ int pa__init(pa_module* m) { u->core = m->core; u->modargs = ma; ma = NULL; + u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - if (setup_dbus(u) < 0) + if (!(u->discovery = pa_bluetooth_discovery_get(u->core))) goto fail; - if (!(u->discovery = pa_bluetooth_discovery_new(pa_dbus_connection_get(u->connection), load_module_for_device, u))) - goto fail; + u->slot = pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery), PA_HOOK_NORMAL, (pa_hook_cb_t) load_module_for_device, u); if (!async) pa_bluetooth_discovery_sync(u->discovery); @@ -178,11 +183,14 @@ void pa__done(pa_module* m) { if (!(u = m->userdata)) return; + if (u->slot) + pa_hook_slot_free(u->slot); + if (u->discovery) - pa_bluetooth_discovery_free(u->discovery); + pa_bluetooth_discovery_unref(u->discovery); - if (u->connection) - pa_dbus_connection_unref(u->connection); + if (u->hashmap) + pa_hashmap_free(u->hashmap, NULL, NULL); if (u->modargs) pa_modargs_free(u->modargs); diff --git a/src/modules/bluetooth/sbc.c b/src/modules/bluetooth/sbc.c index 6fa54796..42bae91a 100644 --- a/src/modules/bluetooth/sbc.c +++ b/src/modules/bluetooth/sbc.c @@ -973,13 +973,15 @@ int sbc_init(sbc_t *sbc, unsigned long flags) return 0; } -int sbc_parse(sbc_t *sbc, void *input, int input_len) +ssize_t sbc_parse(sbc_t *sbc, const void *input, size_t input_len) { return sbc_decode(sbc, input, input_len, NULL, 0, NULL); } -int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output, - int output_len, int *written) +ssize_t sbc_decode(sbc_t *sbc, + const void *input, size_t input_len, + void *output, size_t output_len, + size_t *written) { struct sbc_priv *priv; char *ptr; @@ -1020,7 +1022,7 @@ int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output, ptr = output; - if (output_len < samples * priv->frame.channels * 2) + if (output_len < (size_t) (samples * priv->frame.channels * 2)) samples = output_len / (priv->frame.channels * 2); for (i = 0; i < samples; i++) { diff --git a/src/modules/bluetooth/sbc.h b/src/modules/bluetooth/sbc.h index 25a12885..9a7b4cec 100644 --- a/src/modules/bluetooth/sbc.h +++ b/src/modules/bluetooth/sbc.h @@ -82,9 +82,13 @@ typedef struct sbc_struct sbc_t; int sbc_init(sbc_t *sbc, unsigned long flags); int sbc_reinit(sbc_t *sbc, unsigned long flags); -int sbc_parse(sbc_t *sbc, void *input, int input_len); -int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output, - int output_len, int *len); + +ssize_t sbc_parse(sbc_t *sbc, const void *input, size_t input_len); + +ssize_t sbc_decode(sbc_t *sbc, + const void *input, size_t input_len, + void *output, size_t output_len, + size_t *written); /* Encodes ONE input block into ONE output block */ ssize_t sbc_encode(sbc_t *sbc, diff --git a/src/modules/dbus-util.c b/src/modules/dbus-util.c index d51befb9..e2d45803 100644 --- a/src/modules/dbus-util.c +++ b/src/modules/dbus-util.c @@ -383,12 +383,19 @@ void pa_dbus_remove_matches(DBusConnection *c, ...) { va_end(ap); } -pa_dbus_pending *pa_dbus_pending_new(DBusMessage *m, DBusPendingCall *pending, void *context_data, void *call_data) { +pa_dbus_pending *pa_dbus_pending_new( + DBusConnection *c, + DBusMessage *m, + DBusPendingCall *pending, + void *context_data, + void *call_data) { + pa_dbus_pending *p; pa_assert(pending); p = pa_xnew(pa_dbus_pending, 1); + p->connection = c; p->message = m; p->pending = pending; p->context_data = context_data; @@ -402,9 +409,8 @@ pa_dbus_pending *pa_dbus_pending_new(DBusMessage *m, DBusPendingCall *pending, v void pa_dbus_pending_free(pa_dbus_pending *p) { pa_assert(p); - if (p->pending) { + if (p->pending) dbus_pending_call_cancel(p->pending); /* p->pending is freed by cancel() */ - } if (p->message) dbus_message_unref(p->message); @@ -415,8 +421,8 @@ void pa_dbus_pending_free(pa_dbus_pending *p) { void pa_dbus_sync_pending_list(pa_dbus_pending **p) { pa_assert(p); - while (*p) - dbus_pending_call_block((*p)->pending); + while (*p && dbus_connection_read_write_dispatch((*p)->connection, -1)) + ; } void pa_dbus_free_pending_list(pa_dbus_pending **p) { diff --git a/src/modules/dbus-util.h b/src/modules/dbus-util.h index 90abbc7b..554f41a4 100644 --- a/src/modules/dbus-util.h +++ b/src/modules/dbus-util.h @@ -46,6 +46,7 @@ typedef struct pa_dbus_pending pa_dbus_pending; struct userdata; /* We leave the actual definition to the caller */ struct pa_dbus_pending { + DBusConnection *connection; DBusMessage *message; DBusPendingCall *pending; @@ -55,7 +56,7 @@ struct pa_dbus_pending { PA_LLIST_FIELDS(pa_dbus_pending); }; -pa_dbus_pending *pa_dbus_pending_new(DBusMessage *m, DBusPendingCall *pending, void *context_data, void *call_data); +pa_dbus_pending *pa_dbus_pending_new(DBusConnection *c, DBusMessage *m, DBusPendingCall *pending, void *context_data, void *call_data); void pa_dbus_pending_free(pa_dbus_pending *p); /* Sync up a list of pa_dbus_pending_call objects */ diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 723b5d73..2c90e726 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -192,7 +192,7 @@ static struct entry* read_entry(struct userdata *u, const char *name) { goto fail; } - if ((e->relative_volume_valid || e->absolute_volume_valid) && !(pa_channel_map_valid(&e->channel_map))) { + if ((e->relative_volume_valid || e->absolute_volume_valid) && !pa_channel_map_valid(&e->channel_map)) { pa_log_warn("Invalid channel map stored in database for stream %s", name); goto fail; } @@ -293,16 +293,14 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 entry.channel_map = sink_input->channel_map; + pa_sink_input_get_relative_volume(sink_input, &entry.relative_volume); + entry.relative_volume_valid = sink_input->save_volume; + if (sink_input->sink->flags & PA_SINK_FLAT_VOLUME) { entry.absolute_volume = *pa_sink_input_get_volume(sink_input); entry.absolute_volume_valid = sink_input->save_volume; - - pa_sw_cvolume_divide(&entry.relative_volume, &entry.absolute_volume, pa_sink_get_volume(sink_input->sink, FALSE)); - entry.relative_volume_valid = sink_input->save_volume; - } else { - entry.relative_volume = *pa_sink_input_get_volume(sink_input); - entry.relative_volume_valid = sink_input->save_volume; - } + } else + entry.absolute_volume_valid = FALSE; entry.muted = pa_sink_input_get_mute(sink_input); entry.muted_valid = sink_input->save_muted; @@ -528,11 +526,11 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { if (!(n = get_name(si->proplist, "sink-input"))) continue; - if (strcmp(name, n)) { + if (!pa_streq(name, n)) { pa_xfree(n); continue; } - pa_xfree(n); + pa_xfree(n); if (u->restore_volume) { pa_cvolume v; @@ -578,11 +576,11 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { if (!(n = get_name(so->proplist, "source-output"))) continue; - if (strcmp(name, n)) { + if (!pa_streq(name, n)) { pa_xfree(n); continue; } - pa_xfree(n); + pa_xfree(n); if (u->restore_device && e->device_valid && diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c index 5f7a8665..a00116d1 100644 --- a/src/pulsecore/hook-list.c +++ b/src/pulsecore/hook-list.c @@ -121,3 +121,9 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) { return result; } + +pa_bool_t pa_hook_is_firing(pa_hook *hook) { + pa_assert(hook); + + return hook->n_firing > 0; +} diff --git a/src/pulsecore/hook-list.h b/src/pulsecore/hook-list.h index 8514cced..86ce9d25 100644 --- a/src/pulsecore/hook-list.h +++ b/src/pulsecore/hook-list.h @@ -71,4 +71,6 @@ void pa_hook_slot_free(pa_hook_slot *slot); pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data); +pa_bool_t pa_hook_is_firing(pa_hook *hook); + #endif diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h index 20015bf5..309f1a0d 100644 --- a/src/pulsecore/macro.h +++ b/src/pulsecore/macro.h @@ -164,8 +164,8 @@ typedef int pa_bool_t; #define pa_return_null_if_fail(expr) pa_return_val_if_fail(expr, NULL) -/* An assert which guarantees side effects of x, i.e. is never - * optimized away */ +/* pa_assert_se() is an assert which guarantees side effects of x, + * i.e. is never optimized away, regardless of NDEBUG or FASTPATH. */ #define pa_assert_se(expr) \ do { \ if (PA_UNLIKELY(!(expr))) { \ @@ -174,11 +174,23 @@ typedef int pa_bool_t; } \ } while (FALSE) -/* An assert that may be optimized away by defining NDEBUG */ +/* Does exactly nothing */ +#define pa_nop() do {} while (FALSE) + +/* pa_assert() is an assert that may be optimized away by defining + * NDEBUG. pa_assert_fp() is an assert that may be optimized away by + * defining FASTPATH. It is supposed to be used in inner loops. It's + * there for extra paranoia checking and should probably be removed in + * production builds. */ #ifdef NDEBUG -#define pa_assert(expr) do {} while (FALSE) +#define pa_assert(expr) pa_nop() +#define pa_assert_fp(expr) pa_nop() +#elif defined (FASTPATH) +#define pa_assert(expr) pa_assert_se(expr) +#define pa_assert_fp(expr) pa_nop() #else #define pa_assert(expr) pa_assert_se(expr) +#define pa_assert_fp(expr) pa_assert_se(expr) #endif #define pa_assert_not_reached() \ diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 50a9191b..4860860b 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -775,7 +775,7 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, if (s->connection->version >= 13) { pa_tagstruct *t; - /* Notify the user we're overflowed*/ + /* Notify the user we started playback */ t = pa_tagstruct_new(NULL, 0); pa_tagstruct_putu32(t, PA_COMMAND_STARTED); pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 53e727bb..ae2c6f54 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -924,6 +924,28 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) { } /* Called from main context */ +pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v) { + pa_sink_input_assert_ref(i); + pa_assert(v); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + + *v = i->virtual_volume; + + /* This always returns a relative volume, even in flat volume mode */ + + if (i->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume sv; + + sv = *pa_sink_get_volume(i->sink, FALSE); + + pa_sw_cvolume_divide(v, v, + pa_cvolume_remap(&sv, &i->sink->channel_map, &i->channel_map)); + } + + return v; +} + +/* Called from main context */ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) { pa_assert(i); pa_sink_input_assert_ref(i); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index e3801687..0bcb9d56 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -302,6 +302,7 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency); void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save); const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i); +pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v); void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save); pa_bool_t pa_sink_input_get_mute(pa_sink_input *i); void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index c725595f..147926a0 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1135,6 +1135,19 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) { } /* Called from main thread */ +void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) { + pa_sink_assert_ref(s); + + /* The sink implementor may call this if the volume changed to make sure everyone is notified */ + + if (pa_cvolume_equal(&s->virtual_volume, new_volume)) + return; + + s->virtual_volume = *new_volume; + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} + +/* Called from main thread */ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { pa_bool_t old_muted; @@ -1174,6 +1187,19 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) { } /* Called from main thread */ +void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted) { + pa_sink_assert_ref(s); + + /* The sink implementor may call this if the volume changed to make sure everyone is notified */ + + if (s->muted == new_muted) + return; + + s->muted = new_muted; + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} + +/* Called from main thread */ pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) { pa_sink_assert_ref(s); diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 0d33679f..448f2805 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -224,6 +224,8 @@ void pa_sink_detach(pa_sink *s); void pa_sink_attach(pa_sink *s); void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume); +void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume); +void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted); pa_bool_t pa_device_init_description(pa_proplist *p); pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink); diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index cc6dfc40..ac1ef1e7 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -675,6 +675,19 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) { } /* Called from main thread */ +void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume) { + pa_source_assert_ref(s); + + /* The source implementor may call this if the volume changed to make sure everyone is notified */ + + if (pa_cvolume_equal(&s->virtual_volume, new_volume)) + return; + + s->virtual_volume = *new_volume; + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} + +/* Called from main thread */ void pa_source_set_mute(pa_source *s, pa_bool_t mute) { pa_bool_t old_muted; @@ -695,7 +708,6 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) { /* Called from main thread */ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) { - pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); @@ -715,6 +727,19 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) { } /* Called from main thread */ +void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted) { + pa_source_assert_ref(s); + + /* The source implementor may call this if the mute state changed to make sure everyone is notified */ + + if (s->muted == new_muted) + return; + + s->muted = new_muted; + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} + +/* Called from main thread */ pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p) { pa_source_assert_ref(s); pa_assert(p); diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index 26471de0..68bf2f06 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -211,6 +211,8 @@ void pa_source_detach(pa_source *s); void pa_source_attach(pa_source *s); void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume); +void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume); +void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted); int pa_source_sync_suspend(pa_source *s); |