void (*sco_source_set_volume)(pa_source *s);
pa_hook_slot *sink_state_changed_slot;
pa_hook_slot *source_state_changed_slot;
+ pa_hook_slot *nrec_changed_slot;
};
struct bluetooth_msg {
pollfd->events = (short) (((u->sink && PA_SINK_IS_LINKED(u->sink->thread_info.state) && !writable) ? POLLOUT : 0) |
(u->source && PA_SOURCE_IS_LINKED(u->source->thread_info.state) ? POLLIN : 0));
- if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
+ pa_log_debug("pa_rtpoll_run failed with: %d", ret);
goto fail;
-
- if (ret == 0)
+ }
+ if (ret == 0) {
+ pa_log_debug("IO thread shutdown requested, stopping cleanly");
+ if (u->transport)
+ bt_transport_release(u);
+ else
+ stop_stream_fd(u);
goto finish;
+ }
pollfd = u->rtpoll_item ? pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL) : NULL;
pa_source_volume_changed(u->source, &v);
}
}
- } else if (dbus_message_is_signal(m, "org.bluez.MediaTransport", "PropertyChanged")) {
- DBusMessageIter arg_i;
- pa_bluetooth_transport *t;
- pa_bool_t nrec;
-
- t = (pa_bluetooth_transport *) pa_bluetooth_discovery_get_transport(u->discovery, u->transport);
- pa_assert(t);
-
- if (!dbus_message_iter_init(m, &arg_i)) {
- pa_log("Failed to parse PropertyChanged: %s", err.message);
- goto fail;
- }
-
- nrec = t->nrec;
-
- if (pa_bluetooth_transport_parse_property(t, &arg_i) < 0)
- goto fail;
-
- if (nrec != t->nrec) {
- pa_log_debug("dbus: property 'NREC' changed to value '%s'", t->nrec ? "True" : "False");
- pa_proplist_sets(u->source->proplist, "bluetooth.nrec", t->nrec ? "1" : "0");
- }
} else if (dbus_message_is_signal(m, "org.bluez.HandsfreeGateway", "PropertyChanged")) {
const char *key;
DBusMessageIter iter;
return PA_HOOK_OK;
}
+static pa_hook_result_t nrec_changed_cb(pa_bluetooth_transport *t, void *call_data, struct userdata *u) {
+ pa_proplist *p;
+
+ pa_assert(t);
+ pa_assert(u);
+
+ p = pa_proplist_new();
+ pa_proplist_sets(p, "bluetooth.nrec", t->nrec ? "1" : "0");
+ pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, p);
+ pa_proplist_free(p);
+
+ return PA_HOOK_OK;
+}
+
+static void connect_ports(struct userdata *u, void *sink_or_source_new_data, pa_direction_t direction) {
+ union {
+ pa_sink_new_data *sink_new_data;
+ pa_source_new_data *source_new_data;
+ } data;
+ pa_device_port *port;
+
+ if (direction == PA_DIRECTION_OUTPUT) {
+ data.sink_new_data = sink_or_source_new_data;
+ data.sink_new_data->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ } else {
+ data.source_new_data = sink_or_source_new_data;
+ data.source_new_data->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ }
+
+ switch (u->profile) {
+ case PROFILE_A2DP:
+ pa_assert_se(port = pa_hashmap_get(u->card->ports, "a2dp-output"));
+ pa_assert_se(pa_hashmap_put(data.sink_new_data->ports, port->name, port) >= 0);
+ pa_device_port_ref(port);
+ break;
+
+ case PROFILE_A2DP_SOURCE:
+ pa_assert_se(port = pa_hashmap_get(u->card->ports, "a2dp-input"));
+ pa_assert_se(pa_hashmap_put(data.source_new_data->ports, port->name, port) >= 0);
+ pa_device_port_ref(port);
+ break;
+
+ case PROFILE_HSP:
+ if (direction == PA_DIRECTION_OUTPUT) {
+ pa_assert_se(port = pa_hashmap_get(u->card->ports, "hsp-output"));
+ pa_assert_se(pa_hashmap_put(data.sink_new_data->ports, port->name, port) >= 0);
+ } else {
+ pa_assert_se(port = pa_hashmap_get(u->card->ports, "hsp-input"));
+ pa_assert_se(pa_hashmap_put(data.source_new_data->ports, port->name, port) >= 0);
+ }
+ pa_device_port_ref(port);
+ break;
+
+ case PROFILE_HFGW:
+ if (direction == PA_DIRECTION_OUTPUT) {
+ pa_assert_se(port = pa_hashmap_get(u->card->ports, "hfgw-output"));
+ pa_assert_se(pa_hashmap_put(data.sink_new_data->ports, port->name, port) >= 0);
+ } else {
+ pa_assert_se(port = pa_hashmap_get(u->card->ports, "hfgw-input"));
+ pa_assert_se(pa_hashmap_put(data.source_new_data->ports, port->name, port) >= 0);
+ }
+ pa_device_port_ref(port);
+ break;
+
+ default:
+ pa_assert_not_reached();
+ }
+}
+
/* Run from main thread */
static int add_sink(struct userdata *u) {
char *k;
pa_sink_new_data_done(&data);
return -1;
}
+ connect_ports(u, &data, PA_DIRECTION_OUTPUT);
u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
pa_sink_new_data_done(&data);
return -1;
}
+ connect_ports(u, &data, PA_DIRECTION_INPUT);
u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
pa_source_new_data_done(&data);
if ((u->profile == PROFILE_HSP) || (u->profile == PROFILE_HFGW)) {
if (u->transport) {
- const pa_bluetooth_transport *t;
+ pa_bluetooth_transport *t;
t = pa_bluetooth_discovery_get_transport(u->discovery, u->transport);
pa_assert(t);
pa_proplist_sets(u->source->proplist, "bluetooth.nrec", t->nrec ? "1" : "0");
+
+ if (!u->hsp.nrec_changed_slot)
+ u->hsp.nrec_changed_slot = pa_hook_connect(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) nrec_changed_cb, u);
} else
pa_proplist_sets(u->source->proplist, "bluetooth.nrec", (u->hsp.pcm_capabilities.flags & BT_PCM_FLAG_NREC) ? "1" : "0");
}
u->hsp.source_state_changed_slot = NULL;
}
+ if (u->hsp.nrec_changed_slot) {
+ pa_hook_slot_free(u->hsp.nrec_changed_slot);
+ u->hsp.nrec_changed_slot = NULL;
+ }
+
if (u->sink) {
if (u->profile == PROFILE_HSP) {
k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->sink);
return 0;
}
+static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_new_data, pa_card_profile *profile) {
+ pa_device_port *port;
+ enum profile *d;
+
+ d = PA_CARD_PROFILE_DATA(profile);
+
+ switch (*d) {
+ case PROFILE_A2DP:
+ pa_assert_se(port = pa_device_port_new(u->core, "a2dp-output", _("Bluetooth High Quality (A2DP)"), 0));
+ pa_assert_se(pa_hashmap_put(card_new_data->ports, port->name, port) >= 0);
+ port->is_output = 1;
+ port->is_input = 0;
+ port->priority = profile->priority * 100;
+ pa_hashmap_put(port->profiles, profile->name, profile);
+ break;
+
+ case PROFILE_A2DP_SOURCE:
+ pa_assert_se(port = pa_device_port_new(u->core, "a2dp-input", _("Bluetooth High Quality (A2DP)"), 0));
+ pa_assert_se(pa_hashmap_put(card_new_data->ports, port->name, port) >= 0);
+ port->is_output = 0;
+ port->is_input = 1;
+ port->priority = profile->priority * 100;
+ pa_hashmap_put(port->profiles, profile->name, profile);
+ break;
+
+ case PROFILE_HSP:
+ pa_assert_se(port = pa_device_port_new(u->core, "hsp-output", _("Bluetooth Telephony (HSP/HFP)"), 0));
+ pa_assert_se(pa_hashmap_put(card_new_data->ports, port->name, port) >= 0);
+ port->is_output = 1;
+ port->is_input = 0;
+ port->priority = profile->priority * 100;
+ pa_hashmap_put(port->profiles, profile->name, profile);
+
+ pa_assert_se(port = pa_device_port_new(u->core, "hsp-input", _("Bluetooth Telephony (HSP/HFP)"), 0));
+ pa_assert_se(pa_hashmap_put(card_new_data->ports, port->name, port) >= 0);
+ port->is_output = 0;
+ port->is_input = 1;
+ port->priority = profile->priority * 100;
+ pa_hashmap_put(port->profiles, profile->name, profile);
+ break;
+
+ case PROFILE_HFGW:
+ pa_assert_se(port = pa_device_port_new(u->core, "hfgw-output", _("Bluetooth Handsfree Gateway"), 0));
+ pa_assert_se(pa_hashmap_put(card_new_data->ports, port->name, port) >= 0);
+ port->is_output = 1;
+ port->is_input = 0;
+ port->priority = profile->priority * 100;
+ pa_hashmap_put(port->profiles, profile->name, profile);
+
+ pa_assert_se(port = pa_device_port_new(u->core, "hfgw-input", _("Bluetooth Handsfree Gateway"), 0));
+ pa_assert_se(pa_hashmap_put(card_new_data->ports, port->name, port) >= 0);
+ port->is_output = 0;
+ port->is_input = 1;
+ port->priority = profile->priority * 100;
+ pa_hashmap_put(port->profiles, profile->name, profile);
+ break;
+
+ default:
+ pa_assert_not_reached();
+ }
+
+}
+
/* Run from main thread */
static int add_card(struct userdata *u, const pa_bluetooth_device *device) {
pa_card_new_data data;
d = PA_CARD_PROFILE_DATA(p);
*d = PROFILE_A2DP;
+ create_ports_for_profile(u, &data, p);
pa_hashmap_put(data.profiles, p->name, p);
}
d = PA_CARD_PROFILE_DATA(p);
*d = PROFILE_A2DP_SOURCE;
+ create_ports_for_profile(u, &data, p);
pa_hashmap_put(data.profiles, p->name, p);
}
d = PA_CARD_PROFILE_DATA(p);
*d = PROFILE_HSP;
+ create_ports_for_profile(u, &data, p);
pa_hashmap_put(data.profiles, p->name, p);
}
d = PA_CARD_PROFILE_DATA(p);
*d = PROFILE_HFGW;
+ create_ports_for_profile(u, &data, p);
pa_hashmap_put(data.profiles, p->name, p);
}