diff options
Diffstat (limited to 'src/modules/bluetooth/module-bluetooth-device.c')
-rw-r--r-- | src/modules/bluetooth/module-bluetooth-device.c | 169 |
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); |