[pulseaudio-discuss] [PATCH 1/4] bluetooth: Add basic support for HEADSET profiles

Tanu Kaskinen tanu.kaskinen at linux.intel.com
Sun Aug 17 02:22:40 PDT 2014


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



More information about the pulseaudio-discuss mailing list