[pulseaudio-discuss] bluetooth: Add basic support for HEADSET profiles

Sandeep sandy.8925 at gmail.com
Wed Aug 13 15:03:48 PDT 2014


Pulseaudio user here, whoever wrote these patches, thank you very much!

- Sandeep



>    3. [PATCH 1/4] bluetooth: Add basic support for      HEADSET profiles
>       (Luiz Augusto von Dentz)
>
>
> From: Luiz Augusto von Dentz <luiz.dentz at gmail.com>
> 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);
> +
> +    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)
> +            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);
> +        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;
> +}
> +
> +/* 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)
> +            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;
> +
> +        pa_log_error("Failed to read data from SCO socket: %s", l < 0 ?
> pa_cstrerror(errno) : "EOF");
> +        return -1;
> +    }
> +
> +    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.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;
> +        }
> +
> +    if (!found_tstamp) {
> +        pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary
> recvmsg() data!");
> +        tstamp = pa_rtclock_now();
> +    }
> +
> +    pa_smoother_put(u->read_smoother, tstamp,
> pa_bytes_to_usec(u->read_index, &u->sample_spec));
> +    pa_smoother_resume(u->read_smoother, tstamp, true);
> +
> +    pa_source_post(u->source, &memchunk);
> +    pa_memblock_unref(memchunk.memblock);
> +
> +    return l;
> +}
> +
> +/* Run from IO thread */
>  static void a2dp_prepare_buffer(struct userdata *u) {
>      size_t min_buffer_size = PA_MAX(u->read_link_mtu, u->write_link_mtu);
>
> @@ -611,24 +762,31 @@ static void transport_release(struct userdata *u) {
>
>  /* Run from I/O thread */
>  static void transport_config_mtu(struct userdata *u) {
> -    u->read_block_size =
> -        (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct
> rtp_payload))
> -        / u->sbc_info.frame_length * u->sbc_info.codesize;
> +    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
> u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
> +        u->read_block_size = u->read_link_mtu;
> +        u->write_block_size = u->write_link_mtu;
> +    } else {
> +        u->read_block_size =
> +            (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct
> rtp_payload))
> +            / u->sbc_info.frame_length * u->sbc_info.codesize;
>
> -    u->write_block_size =
> -        (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct
> rtp_payload))
> -        / u->sbc_info.frame_length * u->sbc_info.codesize;
> +        u->write_block_size =
> +            (u->write_link_mtu - sizeof(struct rtp_header) -
> sizeof(struct rtp_payload))
> +            / u->sbc_info.frame_length * u->sbc_info.codesize;
> +    }
>
>      if (u->sink) {
>          pa_sink_set_max_request_within_thread(u->sink,
> u->write_block_size);
>          pa_sink_set_fixed_latency_within_thread(u->sink,
> -
>  FIXED_LATENCY_PLAYBACK_A2DP +
> +                                                (u->profile ==
> PA_BLUETOOTH_PROFILE_A2DP_SINK ?
> +
> FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_SCO) +
>
>  pa_bytes_to_usec(u->write_block_size, &u->sample_spec));
>      }
>
>      if (u->source)
>          pa_source_set_fixed_latency_within_thread(u->source,
> -
>  FIXED_LATENCY_RECORD_A2DP +
> +                                                  (u->profile ==
> PA_BLUETOOTH_PROFILE_A2DP_SOURCE ?
> +
> FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_SCO) +
>
>  pa_bytes_to_usec(u->read_block_size, &u->sample_spec));
>  }
>
> @@ -755,15 +913,19 @@ static int add_source(struct userdata *u) {
>      data.namereg_fail = false;
>      pa_proplist_sets(data.proplist, "bluetooth.protocol",
> pa_bluetooth_profile_to_string(u->profile));
>      pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
> +    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
> +        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES,
> "phone");
>
>      connect_ports(u, &data, PA_DIRECTION_INPUT);
>
>      if (!u->transport_acquired)
>          switch (u->profile) {
>              case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
> +            case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
>                  data.suspend_cause = PA_SUSPEND_USER;
>                  break;
>              case PA_BLUETOOTH_PROFILE_A2DP_SINK:
> +            case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
>              case PA_BLUETOOTH_PROFILE_OFF:
>                  pa_assert_not_reached();
>                  break;
> @@ -870,14 +1032,20 @@ static int add_sink(struct userdata *u) {
>      data.namereg_fail = false;
>      pa_proplist_sets(data.proplist, "bluetooth.protocol",
> pa_bluetooth_profile_to_string(u->profile));
>      pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
> +    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
> +        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES,
> "phone");
>
>      connect_ports(u, &data, PA_DIRECTION_OUTPUT);
>
>      if (!u->transport_acquired)
>          switch (u->profile) {
> +            case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
> +                data.suspend_cause = PA_SUSPEND_USER;
> +                break;
>              case PA_BLUETOOTH_PROFILE_A2DP_SINK:
>                  /* Profile switch should have failed */
>              case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
> +            case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
>              case PA_BLUETOOTH_PROFILE_OFF:
>                  pa_assert_not_reached();
>                  break;
> @@ -898,111 +1066,117 @@ static int add_sink(struct userdata *u) {
>
>  /* Run from main thread */
>  static void transport_config(struct userdata *u) {
> -    sbc_info_t *sbc_info = &u->sbc_info;
> -    a2dp_sbc_t *config;
> +    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
> u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
> +        u->sample_spec.format = PA_SAMPLE_S16LE;
> +        u->sample_spec.channels = 1;
> +        u->sample_spec.rate = 8000;
> +    } else {
> +        sbc_info_t *sbc_info = &u->sbc_info;
> +        a2dp_sbc_t *config;
>
> -    pa_assert(u->transport);
> +        pa_assert(u->transport);
>
> -    u->sample_spec.format = PA_SAMPLE_S16LE;
> -    config = (a2dp_sbc_t *) u->transport->config;
> +        u->sample_spec.format = PA_SAMPLE_S16LE;
> +        config = (a2dp_sbc_t *) u->transport->config;
>
> -    if (sbc_info->sbc_initialized)
> -        sbc_reinit(&sbc_info->sbc, 0);
> -    else
> -        sbc_init(&sbc_info->sbc, 0);
> -    sbc_info->sbc_initialized = true;
> +        if (sbc_info->sbc_initialized)
> +            sbc_reinit(&sbc_info->sbc, 0);
> +        else
> +            sbc_init(&sbc_info->sbc, 0);
> +        sbc_info->sbc_initialized = true;
>
> -    switch (config->frequency) {
> -        case SBC_SAMPLING_FREQ_16000:
> -            sbc_info->sbc.frequency = SBC_FREQ_16000;
> -            u->sample_spec.rate = 16000U;
> -            break;
> -        case SBC_SAMPLING_FREQ_32000:
> -            sbc_info->sbc.frequency = SBC_FREQ_32000;
> -            u->sample_spec.rate = 32000U;
> -            break;
> -        case SBC_SAMPLING_FREQ_44100:
> -            sbc_info->sbc.frequency = SBC_FREQ_44100;
> -            u->sample_spec.rate = 44100U;
> -            break;
> -        case SBC_SAMPLING_FREQ_48000:
> -            sbc_info->sbc.frequency = SBC_FREQ_48000;
> -            u->sample_spec.rate = 48000U;
> -            break;
> -        default:
> -            pa_assert_not_reached();
> -    }
> +        switch (config->frequency) {
> +            case SBC_SAMPLING_FREQ_16000:
> +                sbc_info->sbc.frequency = SBC_FREQ_16000;
> +                u->sample_spec.rate = 16000U;
> +                break;
> +            case SBC_SAMPLING_FREQ_32000:
> +                sbc_info->sbc.frequency = SBC_FREQ_32000;
> +                u->sample_spec.rate = 32000U;
> +                break;
> +            case SBC_SAMPLING_FREQ_44100:
> +                sbc_info->sbc.frequency = SBC_FREQ_44100;
> +                u->sample_spec.rate = 44100U;
> +                break;
> +            case SBC_SAMPLING_FREQ_48000:
> +                sbc_info->sbc.frequency = SBC_FREQ_48000;
> +                u->sample_spec.rate = 48000U;
> +                break;
> +            default:
> +                pa_assert_not_reached();
> +        }
>
> -    switch (config->channel_mode) {
> -        case SBC_CHANNEL_MODE_MONO:
> -            sbc_info->sbc.mode = SBC_MODE_MONO;
> -            u->sample_spec.channels = 1;
> -            break;
> -        case SBC_CHANNEL_MODE_DUAL_CHANNEL:
> -            sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL;
> -            u->sample_spec.channels = 2;
> -            break;
> -        case SBC_CHANNEL_MODE_STEREO:
> -            sbc_info->sbc.mode = SBC_MODE_STEREO;
> -            u->sample_spec.channels = 2;
> -            break;
> -        case SBC_CHANNEL_MODE_JOINT_STEREO:
> -            sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO;
> -            u->sample_spec.channels = 2;
> -            break;
> -        default:
> -            pa_assert_not_reached();
> -    }
> +        switch (config->channel_mode) {
> +            case SBC_CHANNEL_MODE_MONO:
> +                sbc_info->sbc.mode = SBC_MODE_MONO;
> +                u->sample_spec.channels = 1;
> +                break;
> +            case SBC_CHANNEL_MODE_DUAL_CHANNEL:
> +                sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL;
> +                u->sample_spec.channels = 2;
> +                break;
> +            case SBC_CHANNEL_MODE_STEREO:
> +                sbc_info->sbc.mode = SBC_MODE_STEREO;
> +                u->sample_spec.channels = 2;
> +                break;
> +            case SBC_CHANNEL_MODE_JOINT_STEREO:
> +                sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO;
> +                u->sample_spec.channels = 2;
> +                break;
> +            default:
> +                pa_assert_not_reached();
> +        }
>
> -    switch (config->allocation_method) {
> -        case SBC_ALLOCATION_SNR:
> -            sbc_info->sbc.allocation = SBC_AM_SNR;
> -            break;
> -        case SBC_ALLOCATION_LOUDNESS:
> -            sbc_info->sbc.allocation = SBC_AM_LOUDNESS;
> -            break;
> -        default:
> -            pa_assert_not_reached();
> -    }
> +        switch (config->allocation_method) {
> +            case SBC_ALLOCATION_SNR:
> +                sbc_info->sbc.allocation = SBC_AM_SNR;
> +                break;
> +            case SBC_ALLOCATION_LOUDNESS:
> +                sbc_info->sbc.allocation = SBC_AM_LOUDNESS;
> +                break;
> +            default:
> +                pa_assert_not_reached();
> +        }
>
> -    switch (config->subbands) {
> -        case SBC_SUBBANDS_4:
> -            sbc_info->sbc.subbands = SBC_SB_4;
> -            break;
> -        case SBC_SUBBANDS_8:
> -            sbc_info->sbc.subbands = SBC_SB_8;
> -            break;
> -        default:
> -            pa_assert_not_reached();
> -    }
> +        switch (config->subbands) {
> +            case SBC_SUBBANDS_4:
> +                sbc_info->sbc.subbands = SBC_SB_4;
> +                break;
> +            case SBC_SUBBANDS_8:
> +                sbc_info->sbc.subbands = SBC_SB_8;
> +                break;
> +            default:
> +                pa_assert_not_reached();
> +        }
>
> -    switch (config->block_length) {
> -        case SBC_BLOCK_LENGTH_4:
> -            sbc_info->sbc.blocks = SBC_BLK_4;
> -            break;
> -        case SBC_BLOCK_LENGTH_8:
> -            sbc_info->sbc.blocks = SBC_BLK_8;
> -            break;
> -        case SBC_BLOCK_LENGTH_12:
> -            sbc_info->sbc.blocks = SBC_BLK_12;
> -            break;
> -        case SBC_BLOCK_LENGTH_16:
> -            sbc_info->sbc.blocks = SBC_BLK_16;
> -            break;
> -        default:
> -            pa_assert_not_reached();
> -    }
> +        switch (config->block_length) {
> +            case SBC_BLOCK_LENGTH_4:
> +                sbc_info->sbc.blocks = SBC_BLK_4;
> +                break;
> +            case SBC_BLOCK_LENGTH_8:
> +                sbc_info->sbc.blocks = SBC_BLK_8;
> +                break;
> +            case SBC_BLOCK_LENGTH_12:
> +                sbc_info->sbc.blocks = SBC_BLK_12;
> +                break;
> +            case SBC_BLOCK_LENGTH_16:
> +                sbc_info->sbc.blocks = SBC_BLK_16;
> +                break;
> +            default:
> +                pa_assert_not_reached();
> +        }
>
> -    sbc_info->min_bitpool = config->min_bitpool;
> -    sbc_info->max_bitpool = config->max_bitpool;
> +        sbc_info->min_bitpool = config->min_bitpool;
> +        sbc_info->max_bitpool = config->max_bitpool;
>
> -    /* Set minimum bitpool for source to get the maximum possible
> block_size */
> -    sbc_info->sbc.bitpool = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK
> ? sbc_info->max_bitpool : sbc_info->min_bitpool;
> -    sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc);
> -    sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc);
> +        /* Set minimum bitpool for source to get the maximum possible
> block_size */
> +        sbc_info->sbc.bitpool = u->profile ==
> PA_BLUETOOTH_PROFILE_A2DP_SINK ? sbc_info->max_bitpool :
> sbc_info->min_bitpool;
> +        sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc);
> +        sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc);
>
> -    pa_log_info("SBC parameters: allocation=%u, subbands=%u, blocks=%u,
> bitpool=%u",
> -                sbc_info->sbc.allocation, sbc_info->sbc.subbands,
> sbc_info->sbc.blocks, sbc_info->sbc.bitpool);
> +        pa_log_info("SBC parameters: allocation=%u, subbands=%u,
> blocks=%u, bitpool=%u",
> +                    sbc_info->sbc.allocation, sbc_info->sbc.subbands,
> sbc_info->sbc.blocks, sbc_info->sbc.bitpool);
> +    }
>  }
>
>  /* Run from main thread */
> @@ -1022,7 +1196,7 @@ static int setup_transport(struct userdata *u) {
>
>      u->transport = t;
>
> -    if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
> +    if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile ==
> PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
>          transport_acquire(u, true); /* In case of error, the sink/sources
> will be created suspended */
>      else if (transport_acquire(u, false) < 0)
>          return -1; /* We need to fail here until the interactions with
> module-suspend-on-idle and alike get improved */
> @@ -1043,11 +1217,13 @@ static int init_profile(struct userdata *u) {
>
>      pa_assert(u->transport);
>
> -    if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
> +    if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || u->profile ==
> PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
> +        u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
>          if (add_sink(u) < 0)
>              r = -1;
>
> -    if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
> +    if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile ==
> PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
> +        u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
>          if (add_source(u) < 0)
>              r = -1;
>
> @@ -1111,7 +1287,10 @@ static void thread_func(void *userdata) {
>              if (pollfd && (pollfd->revents & POLLIN)) {
>                  int n_read;
>
> -                n_read = a2dp_process_push(u);
> +                if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
> +                    n_read = a2dp_process_push(u);
> +                else
> +                    n_read = sco_process_push(u);
>
>                  if (n_read < 0)
>                      goto fail;
> @@ -1182,8 +1361,13 @@ static void thread_func(void *userdata) {
>                      if (u->write_index <= 0)
>                          u->started_at = pa_rtclock_now();
>
> -                    if ((n_written = a2dp_process_render(u)) < 0)
> -                        goto fail;
> +                    if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
> +                        if ((n_written = a2dp_process_render(u)) < 0)
> +                            goto fail;
> +                    } else {
> +                        if ((n_written = sco_process_render(u)) < 0)
> +                            goto fail;
> +                    }
>
>                      if (n_written == 0)
>                          pa_log("Broken kernel: we got EAGAIN on write()
> after POLLOUT!");
> @@ -1363,6 +1547,8 @@ static pa_direction_t
> get_profile_direction(pa_bluetooth_profile_t p) {
>      static const pa_direction_t profile_direction[] = {
>          [PA_BLUETOOTH_PROFILE_A2DP_SINK] = PA_DIRECTION_OUTPUT,
>          [PA_BLUETOOTH_PROFILE_A2DP_SOURCE] = PA_DIRECTION_INPUT,
> +        [PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT] = PA_DIRECTION_INPUT |
> PA_DIRECTION_OUTPUT,
> +        [PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY] = PA_DIRECTION_INPUT
> | PA_DIRECTION_OUTPUT,
>          [PA_BLUETOOTH_PROFILE_OFF] = 0
>      };
>
> @@ -1540,6 +1726,30 @@ static pa_card_profile *create_card_profile(struct
> userdata *u, const char *uuid
>
>          p = PA_CARD_PROFILE_DATA(cp);
>          *p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
> +    } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS) || pa_streq(uuid,
> PA_BLUETOOTH_UUID_HFP_HF)) {
> +        cp = pa_card_profile_new("headset_head_unit", _("Headset Head
> Unit (HSP/HFP)"), sizeof(enum profile));
> +        cp->priority = 20;
> +        cp->n_sinks = 1;
> +        cp->n_sources = 1;
> +        cp->max_sink_channels = 1;
> +        cp->max_source_channels = 1;
> +        pa_hashmap_put(input_port->profiles, cp->name, cp);
> +        pa_hashmap_put(output_port->profiles, cp->name, cp);
> +
> +        p = PA_CARD_PROFILE_DATA(cp);
> +        *p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT;
> +    } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_AG) || pa_streq(uuid,
> PA_BLUETOOTH_UUID_HFP_AG)) {
> +        cp = pa_card_profile_new("headset_audio_gateway", _("Headset
> Audio Gateway (HSP/HFP)"), sizeof(enum profile));
> +        cp->priority = 20;
> +        cp->n_sinks = 1;
> +        cp->n_sources = 1;
> +        cp->max_sink_channels = 1;
> +        cp->max_source_channels = 1;
> +        pa_hashmap_put(input_port->profiles, cp->name, cp);
> +        pa_hashmap_put(output_port->profiles, cp->name, cp);
> +
> +        p = PA_CARD_PROFILE_DATA(cp);
> +        *p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY;
>      }
>
>      if (cp && u->device->transports[*p])
> --
> 1.9.3
>
>
>
> ------------------------------
>
> Subject: Digest Footer
>
> _______________________________________________
> pulseaudio-discuss mailing list
> pulseaudio-discuss at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
>
>
> ------------------------------
>
> End of pulseaudio-discuss Digest, Vol 40, Issue 34
> **************************************************
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/pulseaudio-discuss/attachments/20140813/3c0837ca/attachment-0001.html>


More information about the pulseaudio-discuss mailing list