On Wed, 2014-08-13 at 15:23 +0300, Luiz Augusto von Dentz wrote: > From: Jo?o Paulo Rechi Vita <jprvita at openbossa.org> > > This commit adds basic support for devices implementing HSP Headset > Unit, HSP Audio Gateway, HFP Handsfree Unit, HFP Audio Gateway to the > BlueZ 5 bluetooth audio devices driver module (module-bluez5-device). > --- > src/modules/bluetooth/bluez5-util.c | 4 + > src/modules/bluetooth/bluez5-util.h | 6 + > src/modules/bluetooth/module-bluez5-device.c | 426 ++++++++++++++++++++------- > 3 files changed, 328 insertions(+), 108 deletions(-) > > diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c > index 5b6b372..adb8351 100644 > --- a/src/modules/bluetooth/bluez5-util.c > +++ b/src/modules/bluetooth/bluez5-util.c > @@ -1109,6 +1109,10 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) { > return "a2dp_sink"; > case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: > return "a2dp_source"; > + case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: > + return "headset_head_unit"; > + case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: > + return "headset_audio_gateway"; > case PA_BLUETOOTH_PROFILE_OFF: > return "off"; > } > diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h > index 63bae35..0121733 100644 > --- a/src/modules/bluetooth/bluez5-util.h > +++ b/src/modules/bluetooth/bluez5-util.h > @@ -26,6 +26,10 @@ > > #define PA_BLUETOOTH_UUID_A2DP_SOURCE "0000110a-0000-1000-8000-00805f9b34fb" > #define PA_BLUETOOTH_UUID_A2DP_SINK "0000110b-0000-1000-8000-00805f9b34fb" > +#define PA_BLUETOOTH_UUID_HSP_HS "00001108-0000-1000-8000-00805f9b34fb" > +#define PA_BLUETOOTH_UUID_HSP_AG "00001112-0000-1000-8000-00805f9b34fb" > +#define PA_BLUETOOTH_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb" > +#define PA_BLUETOOTH_UUID_HFP_AG "0000111f-0000-1000-8000-00805f9b34fb" > > typedef struct pa_bluetooth_transport pa_bluetooth_transport; > typedef struct pa_bluetooth_device pa_bluetooth_device; > @@ -41,6 +45,8 @@ typedef enum pa_bluetooth_hook { > typedef enum profile { > PA_BLUETOOTH_PROFILE_A2DP_SINK, > PA_BLUETOOTH_PROFILE_A2DP_SOURCE, > + PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT, > + PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY, > PA_BLUETOOTH_PROFILE_OFF > } pa_bluetooth_profile_t; > #define PA_BLUETOOTH_PROFILE_COUNT PA_BLUETOOTH_PROFILE_OFF > diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c > index 57b2791..92c804a 100644 > --- a/src/modules/bluetooth/module-bluez5-device.c > +++ b/src/modules/bluetooth/module-bluez5-device.c > @@ -33,6 +33,7 @@ > #include <pulse/timeval.h> > > #include <pulsecore/core-error.h> > +#include <pulsecore/core-rtclock.h> > #include <pulsecore/core-util.h> > #include <pulsecore/i18n.h> > #include <pulsecore/module.h> > @@ -59,7 +60,9 @@ PA_MODULE_USAGE("path=<device object path>"); > > #define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC) > #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC) > +#define FIXED_LATENCY_PLAYBACK_SCO (125 * PA_USEC_PER_MSEC) > #define FIXED_LATENCY_RECORD_A2DP (25 * PA_USEC_PER_MSEC) > +#define FIXED_LATENCY_RECORD_SCO (25 * PA_USEC_PER_MSEC) > > #define BITPOOL_DEC_LIMIT 32 > #define BITPOOL_DEC_STEP 5 > @@ -236,6 +239,154 @@ static void connect_ports(struct userdata *u, void *new_data, pa_direction_t dir > } > > /* Run from IO thread */ > +static int sco_process_render(struct userdata *u) { > + ssize_t l; > + > + pa_assert(u); > + pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || > + u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY); > + pa_assert(u->sink); > + > + /* First, render some data */ > + if (!u->write_memchunk.memblock) > + pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk); As I said to Jo?o, I'd prefer using a local memchunk instead of u->write_memchunk, because u->write_memchunk doesn't carry any meaningful state information in the SCO case. > + > + pa_assert(u->write_memchunk.length == u->write_block_size); > + > + for (;;) { > + const void *p; > + > + /* 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. */ > + > + p = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk); > + 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) Tab used for indentation. > + break; > + > + if (errno == EINTR) > + /* Retry right away if we got interrupted */ > + continue; > + else if (errno == EAGAIN) > + /* Hmm, apparently the socket was not writable, give up for now */ > + return 0; > + > + pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(errno)); > + return -1; > + } > + > + pa_assert((size_t) l <= u->write_memchunk.length); > + > + 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); Unaligned indentation. > + return -1; > + } > + > + u->write_index += (uint64_t) u->write_memchunk.length; > + pa_memblock_unref(u->write_memchunk.memblock); > + pa_memchunk_reset(&u->write_memchunk); > + > + return l; The return value on success used to be 1, and that's what the call site expects. > +} > + > +/* Run from IO thread */ > +static int sco_process_push(struct userdata *u) { > + ssize_t l; > + pa_memchunk memchunk; > + struct cmsghdr *cm; > + struct msghdr m; > + bool found_tstamp = false; > + pa_usec_t tstamp = 0; > + > + pa_assert(u); > + pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || > + u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY); > + pa_assert(u->source); > + pa_assert(u->read_smoother); > + > + memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size); > + memchunk.index = memchunk.length = 0; > + > + for (;;) { > + void *p; > + uint8_t aux[1024]; > + struct iovec iov; > + > + pa_zero(m); > + pa_zero(aux); > + pa_zero(iov); > + > + m.msg_iov = &iov; > + m.msg_iovlen = 1; > + m.msg_control = aux; > + m.msg_controllen = sizeof(aux); > + > + p = pa_memblock_acquire(memchunk.memblock); > + iov.iov_base = p; > + iov.iov_len = pa_memblock_get_length(memchunk.memblock); > + l = recvmsg(u->stream_fd, &m, 0); > + pa_memblock_release(memchunk.memblock); > + > + if (l > 0) Tab used for indentation. > + break; > + > + if (l < 0 && errno == EINTR) > + /* Retry right away if we got interrupted */ > + continue; > + else if (l < 0 && errno == EAGAIN) > + /* Hmm, apparently the socket was not readable, give up for now. */ > + return 0; memchunk.memblock needs to be unreffed. > + > + pa_log_error("Failed to read data from SCO socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF"); > + return -1; memchunk.memblock needs to be unreffed. > + } > + > + pa_assert((size_t) l <= pa_memblock_get_length(memchunk.memblock)); > + > + /* In some rare occasions, we might receive packets of a very strange > + * size. This could potentially be possible if the SCO packet was > + * received partially over-the-air, or more probably due to hardware > + * issues in our Bluetooth adapter. In these cases, in order to avoid > + * an assertion failure due to unaligned data, just discard the whole > + * packet */ > + if (!pa_frame_aligned(l, &u->sample_spec)) { > + pa_log_warn("SCO packet received of unaligned size: %zu", l); > + return -1; memchunk.memblock needs to be unreffed. > + } > + > + memchunk.length = (size_t) l; > + u->read_index += (uint64_t) l; > + > + for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) > + if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) { > + struct timeval *tv = (struct timeval*) CMSG_DATA(cm); > + pa_rtclock_from_wallclock(tv); > + tstamp = pa_timeval_load(tv); > + found_tstamp = true; There used to be a "break" here, and I think it should not be removed. -- Tanu