[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