summaryrefslogtreecommitdiffstats
path: root/src/modules/bluetooth/module-bluetooth-device.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/bluetooth/module-bluetooth-device.c')
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c169
1 files changed, 96 insertions, 73 deletions
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index b04834da..ac8344f0 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -33,6 +33,8 @@
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
#include <pulse/sample.h>
+#include <pulse/i18n.h>
+
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
#include <pulsecore/core-util.h>
@@ -154,89 +156,98 @@ struct userdata {
pa_bluetooth_device *device;
- int write_type, read_type;
+ int stream_write_type, stream_read_type;
+ int service_write_type, service_read_type;
};
static int init_bt(struct userdata *u);
static int init_profile(struct userdata *u);
-static int service_send(int fd, const bt_audio_msg_header_t *msg) {
- size_t length;
+static int service_send(struct userdata *u, const bt_audio_msg_header_t *msg) {
ssize_t r;
- pa_assert(fd >= 0);
+ pa_assert(u);
+ pa_assert(u->service_fd >= 0);
pa_assert(msg);
-
- length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE;
+ pa_assert(msg->length > 0);
pa_log_debug("Sending %s -> %s",
pa_strnull(bt_audio_strtype(msg->type)),
pa_strnull(bt_audio_strname(msg->name)));
- if ((r = send(fd, msg, length, 0)) == (ssize_t) length)
+ if ((r = pa_loop_write(u->service_fd, msg, msg->length, &u->service_write_type)) == (ssize_t) msg->length)
return 0;
if (r < 0)
pa_log_error("Error sending data to audio service: %s", pa_cstrerror(errno));
else
- pa_log_error("Short send()");
+ pa_log_error("Short write()");
return -1;
}
-static int service_recv(int fd, bt_audio_msg_header_t *msg, size_t expected_length) {
- size_t length;
+static int service_recv(struct userdata *u, bt_audio_msg_header_t *msg, size_t room) {
ssize_t r;
- pa_assert(fd >= 0);
+ pa_assert(u);
+ pa_assert(u->service_fd >= 0);
pa_assert(msg);
- length = expected_length ? expected_length : BT_SUGGESTED_BUFFER_SIZE;
-
- if (length < sizeof(bt_audio_error_t))
- length = sizeof(bt_audio_error_t);
+ if (room <= 0)
+ room = BT_SUGGESTED_BUFFER_SIZE;
pa_log_debug("Trying to receive message from audio service...");
- r = recv(fd, msg, length, 0);
-
- if (r > 0 && (r == (ssize_t) length || expected_length <= 0)) {
+ /* First, read the header */
+ if ((r = pa_loop_read(u->service_fd, msg, sizeof(*msg), &u->service_read_type)) != sizeof(*msg))
+ goto read_fail;
- if ((size_t) r < sizeof(*msg)) {
- pa_log_error("Packet read too small.");
- return -1;
- }
+ if (msg->length < sizeof(*msg)) {
+ pa_log_error("Invalid message size.");
+ return -1;
+ }
- if (r != msg->length) {
- pa_log_error("Size read doesn't match header size.");
- return -1;
- }
+ /* Secondly, read the payload */
+ if (msg->length > sizeof(*msg)) {
- pa_log_debug("Received %s <- %s",
- pa_strnull(bt_audio_strtype(msg->type)),
- pa_strnull(bt_audio_strname(msg->name)));
+ size_t remains = msg->length - sizeof(*msg);
- return 0;
+ if ((r = pa_loop_read(u->service_fd,
+ (uint8_t*) msg + sizeof(*msg),
+ remains,
+ &u->service_read_type)) != (ssize_t) remains)
+ goto read_fail;
}
+ pa_log_debug("Received %s <- %s",
+ pa_strnull(bt_audio_strtype(msg->type)),
+ pa_strnull(bt_audio_strname(msg->name)));
+
+ return 0;
+
+read_fail:
+
if (r < 0)
pa_log_error("Error receiving data from audio service: %s", pa_cstrerror(errno));
else
- pa_log_error("Short recv()");
+ pa_log_error("Short read()");
return -1;
}
-static ssize_t service_expect(int fd, bt_audio_msg_header_t *rsp, uint8_t expected_name, size_t expected_length) {
+static ssize_t service_expect(struct userdata*u, bt_audio_msg_header_t *rsp, size_t room, uint8_t expected_name, size_t expected_size) {
int r;
- pa_assert(fd >= 0);
+ pa_assert(u);
+ pa_assert(u->service_fd >= 0);
pa_assert(rsp);
- if ((r = service_recv(fd, rsp, expected_length)) < 0)
+ if ((r = service_recv(u, rsp, room)) < 0)
return r;
- if (rsp->type != BT_RESPONSE || rsp->name != expected_name) {
+ if ((rsp->type != BT_INDICATION && rsp->type != BT_RESPONSE) ||
+ rsp->name != expected_name ||
+ (expected_size > 0 && rsp->length != expected_size)) {
if (rsp->type == BT_ERROR && rsp->length == sizeof(bt_audio_error_t))
pa_log_error("Received error condition: %s", pa_cstrerror(((bt_audio_error_t*) rsp)->posix_errno));
@@ -328,10 +339,10 @@ static int get_caps(struct userdata *u) {
}
msg.getcaps_req.flags = BT_FLAG_AUTOCONNECT;
- if (service_send(u->service_fd, &msg.getcaps_req.h) < 0)
+ if (service_send(u, &msg.getcaps_req.h) < 0)
return -1;
- if (service_expect(u->service_fd, &msg.getcaps_rsp.h, BT_GET_CAPABILITIES, 0) < 0)
+ if (service_expect(u, &msg.getcaps_rsp.h, sizeof(msg), BT_GET_CAPABILITIES, 0) < 0)
return -1;
return parse_caps(u, &msg.getcaps_rsp);
@@ -610,10 +621,10 @@ static int set_conf(struct userdata *u) {
msg.setconf_req.h.length += msg.setconf_req.codec.length - sizeof(msg.setconf_req.codec);
}
- if (service_send(u->service_fd, &msg.setconf_req.h) < 0)
+ if (service_send(u, &msg.setconf_req.h) < 0)
return -1;
- if (service_expect(u->service_fd, &msg.setconf_rsp.h, BT_SET_CONFIGURATION, sizeof(msg.setconf_rsp)) < 0)
+ if (service_expect(u, &msg.setconf_rsp.h, sizeof(msg), BT_SET_CONFIGURATION, sizeof(msg.setconf_rsp)) < 0)
return -1;
if ((u->profile == PROFILE_A2DP && msg.setconf_rsp.transport != BT_CAPABILITIES_TRANSPORT_A2DP) ||
@@ -660,13 +671,13 @@ static int setup_stream_fd(struct userdata *u) {
msg.start_req.h.name = BT_START_STREAM;
msg.start_req.h.length = sizeof(msg.start_req);
- if (service_send(u->service_fd, &msg.start_req.h) < 0)
+ if (service_send(u, &msg.start_req.h) < 0)
return -1;
- if (service_expect(u->service_fd, &msg.rsp, BT_START_STREAM, sizeof(msg.start_rsp)) < 0)
+ if (service_expect(u, &msg.rsp, sizeof(msg), BT_START_STREAM, sizeof(msg.start_rsp)) < 0)
return -1;
- if (service_expect(u->service_fd, &msg.rsp, BT_NEW_STREAM, sizeof(msg.streamfd_ind)) < 0)
+ if (service_expect(u, &msg.rsp, sizeof(msg), BT_NEW_STREAM, sizeof(msg.streamfd_ind)) < 0)
return -1;
if ((u->stream_fd = bt_audio_service_get_data_fd(u->service_fd)) < 0) {
@@ -776,7 +787,7 @@ static int hsp_process_render(struct userdata *u) {
const void *p;
p = (const uint8_t*) pa_memblock_acquire(memchunk.memblock) + memchunk.index;
- l = pa_write(u->stream_fd, p, memchunk.length, &u->write_type);
+ l = pa_write(u->stream_fd, p, memchunk.length, &u->stream_write_type);
pa_memblock_release(memchunk.memblock);
pa_log_debug("Memblock written to socket: %lli bytes", (long long) l);
@@ -825,7 +836,7 @@ static int hsp_process_push(struct userdata *u) {
void *p;
p = pa_memblock_acquire(memchunk.memblock);
- l = pa_read(u->stream_fd, p, pa_memblock_get_length(memchunk.memblock), &u->read_type);
+ l = pa_read(u->stream_fd, p, pa_memblock_get_length(memchunk.memblock), &u->stream_read_type);
pa_memblock_release(memchunk.memblock);
if (l <= 0) {
@@ -895,6 +906,11 @@ static int a2dp_process_render(struct userdata *u) {
(void*) p, u->write_memchunk.length,
d, left,
&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) {
@@ -941,7 +957,7 @@ static int a2dp_process_render(struct userdata *u) {
for (;;) {
ssize_t l;
- l = pa_write(u->stream_fd, p, left, &u->write_type);
+ 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); */
pa_assert(l != 0);
@@ -1340,9 +1356,6 @@ static int add_sink(struct userdata *u) {
u->sink->userdata = u;
u->sink->parent.process_msg = sink_process_msg;
-
- pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
- pa_sink_set_rtpoll(u->sink, u->rtpoll);
}
/* u->sink->get_volume = sink_get_volume_cb; */
@@ -1387,9 +1400,6 @@ static int add_source(struct userdata *u) {
u->source->userdata = u;
u->source->parent.process_msg = source_process_msg;
-
- pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
- pa_source_set_rtpoll(u->source, u->rtpoll);
}
/* u->source->get_volume = source_get_volume_cb; */
@@ -1403,10 +1413,9 @@ static int add_source(struct userdata *u) {
return 0;
}
-static int init_bt(struct userdata *u) {
+static void shutdown_bt(struct userdata *u) {
pa_assert(u);
- /* shutdown bt */
if (u->stream_fd >= 0) {
pa_close(u->stream_fd);
u->stream_fd = -1;
@@ -1416,14 +1425,21 @@ static int init_bt(struct userdata *u) {
pa_close(u->service_fd);
u->service_fd = -1;
}
+}
- u->write_type = u->read_type = 0;
+static int init_bt(struct userdata *u) {
+ pa_assert(u);
+
+ shutdown_bt(u);
+
+ u->stream_write_type = u->stream_read_type = 0;
+ u->service_write_type = u->service_write_type = 0;
- /* connect to the bluez audio service */
if ((u->service_fd = bt_audio_service_open()) < 0) {
pa_log_error("Couldn't connect to bluetooth audio service");
return -1;
}
+
pa_log_debug("Connected to the bluetooth audio service");
return 0;
@@ -1507,6 +1523,13 @@ static void stop_thread(struct userdata *u) {
pa_source_unref(u->source);
u->source = NULL;
}
+
+ if (u->rtpoll) {
+ pa_thread_mq_done(&u->thread_mq);
+
+ pa_rtpoll_free(u->rtpoll);
+ u->rtpoll = NULL;
+ }
}
static int start_thread(struct userdata *u) {
@@ -1514,6 +1537,7 @@ static int start_thread(struct userdata *u) {
pa_assert(u);
pa_assert(!u->thread);
+ pa_assert(!u->rtpoll);
pa_assert(!u->rtpoll_item);
if (USE_SCO_OVER_PCM(u)) {
@@ -1522,6 +1546,9 @@ static int start_thread(struct userdata *u) {
return 0;
}
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll);
+
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->fd = u->stream_fd;
@@ -1533,11 +1560,17 @@ static int start_thread(struct userdata *u) {
return -1;
}
- if (u->sink)
+ if (u->sink) {
+ 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->source)
+ 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);
+ }
return 0;
}
@@ -1566,7 +1599,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
}
stop_thread(u);
- init_bt(u);
+ shutdown_bt(u);
if (u->write_memchunk.memblock) {
pa_memblock_unref(u->write_memchunk.memblock);
@@ -1576,6 +1609,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
u->profile = *d;
u->sample_spec = u->requested_sample_spec;
+ init_bt(u);
init_profile(u);
if (u->sink || u->source)
@@ -1628,7 +1662,7 @@ static int add_card(struct userdata *u, const char * default_profile) {
data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
if (u->device->audio_sink_info_valid > 0) {
- p = pa_card_profile_new("a2dp", "A2DP Advanced Audio Distribution Profile", sizeof(enum profile));
+ p = pa_card_profile_new("a2dp", _("High Fidelity Playback (A2DP)"), sizeof(enum profile));
p->priority = 10;
p->n_sinks = 1;
p->n_sources = 0;
@@ -1642,7 +1676,7 @@ static int add_card(struct userdata *u, const char * default_profile) {
}
if (u->device->headset_info_valid > 0) {
- p = pa_card_profile_new("hsp", "HSP/HFP Headset/Hands-Free Profile", sizeof(enum profile));
+ p = pa_card_profile_new("hsp", _("Telephony Duplex (HSP/HFP)"), sizeof(enum profile));
p->priority = 20;
p->n_sinks = 1;
p->n_sources = 1;
@@ -1657,7 +1691,7 @@ static int add_card(struct userdata *u, const char * default_profile) {
pa_assert(!pa_hashmap_isempty(data.profiles));
- p = pa_card_profile_new("off", "Off", sizeof(enum profile));
+ p = pa_card_profile_new("off", _("Off"), sizeof(enum profile));
d = PA_CARD_PROFILE_DATA(p);
*d = PROFILE_OFF;
pa_hashmap_put(data.profiles, p->name, p);
@@ -1748,8 +1782,6 @@ int pa__init(pa_module* m) {
u->service_fd = -1;
u->stream_fd = -1;
u->read_smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
- u->rtpoll = pa_rtpoll_new();
- pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll);
u->sample_spec = m->core->default_sample_spec;
u->modargs = ma;
@@ -1905,19 +1937,10 @@ void pa__done(pa_module *m) {
if (u->card)
pa_card_free(u->card);
- pa_thread_mq_done(&u->thread_mq);
-
- if (u->rtpoll)
- pa_rtpoll_free(u->rtpoll);
-
if (u->read_smoother)
pa_smoother_free(u->read_smoother);
- if (u->stream_fd >= 0)
- pa_close(u->stream_fd);
-
- if (u->service_fd >= 0)
- pa_close(u->service_fd);
+ shutdown_bt(u);
if (u->device)
pa_bluetooth_device_free(u->device);