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); | 
