<div dir="ltr"><div>Pulseaudio user here, whoever wrote these patches, thank you very much!</div><div><br></div><div>- Sandeep</div><br><div class="gmail_extra"><div class="gmail_quote"><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
3. [PATCH 1/4] bluetooth: Add basic support for HEADSET profiles<br>
(Luiz Augusto von Dentz)<br>
<br>
<br>From: Luiz Augusto von Dentz <<a href="mailto:luiz.dentz@gmail.com">luiz.dentz@gmail.com</a>><br>
From: Jo?o Paulo Rechi Vita <<a href="mailto:jprvita@openbossa.org">jprvita@openbossa.org</a>><br>
<br>
This commit adds basic support for devices implementing HSP Headset<br>
Unit, HSP Audio Gateway, HFP Handsfree Unit, HFP Audio Gateway to the<br>
BlueZ 5 bluetooth audio devices driver module (module-bluez5-device).<br>
---<br>
src/modules/bluetooth/bluez5-util.c | 4 +<br>
src/modules/bluetooth/bluez5-util.h | 6 +<br>
src/modules/bluetooth/module-bluez5-device.c | 426 ++++++++++++++++++++-------<br>
3 files changed, 328 insertions(+), 108 deletions(-)<br>
<br>
diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c<br>
index 5b6b372..adb8351 100644<br>
--- a/src/modules/bluetooth/bluez5-util.c<br>
+++ b/src/modules/bluetooth/bluez5-util.c<br>
@@ -1109,6 +1109,10 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {<br>
return "a2dp_sink";<br>
case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:<br>
return "a2dp_source";<br>
+ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:<br>
+ return "headset_head_unit";<br>
+ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:<br>
+ return "headset_audio_gateway";<br>
case PA_BLUETOOTH_PROFILE_OFF:<br>
return "off";<br>
}<br>
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h<br>
index 63bae35..0121733 100644<br>
--- a/src/modules/bluetooth/bluez5-util.h<br>
+++ b/src/modules/bluetooth/bluez5-util.h<br>
@@ -26,6 +26,10 @@<br>
<br>
#define PA_BLUETOOTH_UUID_A2DP_SOURCE "0000110a-0000-1000-8000-00805f9b34fb"<br>
#define PA_BLUETOOTH_UUID_A2DP_SINK "0000110b-0000-1000-8000-00805f9b34fb"<br>
+#define PA_BLUETOOTH_UUID_HSP_HS "00001108-0000-1000-8000-00805f9b34fb"<br>
+#define PA_BLUETOOTH_UUID_HSP_AG "00001112-0000-1000-8000-00805f9b34fb"<br>
+#define PA_BLUETOOTH_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb"<br>
+#define PA_BLUETOOTH_UUID_HFP_AG "0000111f-0000-1000-8000-00805f9b34fb"<br>
<br>
typedef struct pa_bluetooth_transport pa_bluetooth_transport;<br>
typedef struct pa_bluetooth_device pa_bluetooth_device;<br>
@@ -41,6 +45,8 @@ typedef enum pa_bluetooth_hook {<br>
typedef enum profile {<br>
PA_BLUETOOTH_PROFILE_A2DP_SINK,<br>
PA_BLUETOOTH_PROFILE_A2DP_SOURCE,<br>
+ PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT,<br>
+ PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY,<br>
PA_BLUETOOTH_PROFILE_OFF<br>
} pa_bluetooth_profile_t;<br>
#define PA_BLUETOOTH_PROFILE_COUNT PA_BLUETOOTH_PROFILE_OFF<br>
diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c<br>
index 57b2791..92c804a 100644<br>
--- a/src/modules/bluetooth/module-bluez5-device.c<br>
+++ b/src/modules/bluetooth/module-bluez5-device.c<br>
@@ -33,6 +33,7 @@<br>
#include <pulse/timeval.h><br>
<br>
#include <pulsecore/core-error.h><br>
+#include <pulsecore/core-rtclock.h><br>
#include <pulsecore/core-util.h><br>
#include <pulsecore/i18n.h><br>
#include <pulsecore/module.h><br>
@@ -59,7 +60,9 @@ PA_MODULE_USAGE("path=<device object path>");<br>
<br>
#define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC)<br>
#define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)<br>
+#define FIXED_LATENCY_PLAYBACK_SCO (125 * PA_USEC_PER_MSEC)<br>
#define FIXED_LATENCY_RECORD_A2DP (25 * PA_USEC_PER_MSEC)<br>
+#define FIXED_LATENCY_RECORD_SCO (25 * PA_USEC_PER_MSEC)<br>
<br>
#define BITPOOL_DEC_LIMIT 32<br>
#define BITPOOL_DEC_STEP 5<br>
@@ -236,6 +239,154 @@ static void connect_ports(struct userdata *u, void *new_data, pa_direction_t dir<br>
}<br>
<br>
/* Run from IO thread */<br>
+static int sco_process_render(struct userdata *u) {<br>
+ ssize_t l;<br>
+<br>
+ pa_assert(u);<br>
+ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||<br>
+ u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);<br>
+ pa_assert(u->sink);<br>
+<br>
+ /* First, render some data */<br>
+ if (!u->write_memchunk.memblock)<br>
+ pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk);<br>
+<br>
+ pa_assert(u->write_memchunk.length == u->write_block_size);<br>
+<br>
+ for (;;) {<br>
+ const void *p;<br>
+<br>
+ /* Now write that data to the socket. The socket is of type<br>
+ * SEQPACKET, and we generated the data of the MTU size, so this<br>
+ * should just work. */<br>
+<br>
+ p = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk);<br>
+ l = pa_write(u->stream_fd, p, u->write_memchunk.length, &u->stream_write_type);<br>
+ pa_memblock_release(u->write_memchunk.memblock);<br>
+<br>
+ pa_assert(l != 0);<br>
+<br>
+ if (l > 0)<br>
+ break;<br>
+<br>
+ if (errno == EINTR)<br>
+ /* Retry right away if we got interrupted */<br>
+ continue;<br>
+ else if (errno == EAGAIN)<br>
+ /* Hmm, apparently the socket was not writable, give up for now */<br>
+ return 0;<br>
+<br>
+ pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(errno));<br>
+ return -1;<br>
+ }<br>
+<br>
+ pa_assert((size_t) l <= u->write_memchunk.length);<br>
+<br>
+ if ((size_t) l != u->write_memchunk.length) {<br>
+ pa_log_error("Wrote memory block to socket only partially! %llu written, wanted to write %llu.",<br>
+ (unsigned long long) l,<br>
+ (unsigned long long) u->write_memchunk.length);<br>
+ return -1;<br>
+ }<br>
+<br>
+ u->write_index += (uint64_t) u->write_memchunk.length;<br>
+ pa_memblock_unref(u->write_memchunk.memblock);<br>
+ pa_memchunk_reset(&u->write_memchunk);<br>
+<br>
+ return l;<br>
+}<br>
+<br>
+/* Run from IO thread */<br>
+static int sco_process_push(struct userdata *u) {<br>
+ ssize_t l;<br>
+ pa_memchunk memchunk;<br>
+ struct cmsghdr *cm;<br>
+ struct msghdr m;<br>
+ bool found_tstamp = false;<br>
+ pa_usec_t tstamp = 0;<br>
+<br>
+ pa_assert(u);<br>
+ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||<br>
+ u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);<br>
+ pa_assert(u->source);<br>
+ pa_assert(u->read_smoother);<br>
+<br>
+ memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);<br>
+ memchunk.index = memchunk.length = 0;<br>
+<br>
+ for (;;) {<br>
+ void *p;<br>
+ uint8_t aux[1024];<br>
+ struct iovec iov;<br>
+<br>
+ pa_zero(m);<br>
+ pa_zero(aux);<br>
+ pa_zero(iov);<br>
+<br>
+ m.msg_iov = &iov;<br>
+ m.msg_iovlen = 1;<br>
+ m.msg_control = aux;<br>
+ m.msg_controllen = sizeof(aux);<br>
+<br>
+ p = pa_memblock_acquire(memchunk.memblock);<br>
+ iov.iov_base = p;<br>
+ iov.iov_len = pa_memblock_get_length(memchunk.memblock);<br>
+ l = recvmsg(u->stream_fd, &m, 0);<br>
+ pa_memblock_release(memchunk.memblock);<br>
+<br>
+ if (l > 0)<br>
+ break;<br>
+<br>
+ if (l < 0 && errno == EINTR)<br>
+ /* Retry right away if we got interrupted */<br>
+ continue;<br>
+ else if (l < 0 && errno == EAGAIN)<br>
+ /* Hmm, apparently the socket was not readable, give up for now. */<br>
+ return 0;<br>
+<br>
+ pa_log_error("Failed to read data from SCO socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF");<br>
+ return -1;<br>
+ }<br>
+<br>
+ pa_assert((size_t) l <= pa_memblock_get_length(memchunk.memblock));<br>
+<br>
+ /* In some rare occasions, we might receive packets of a very strange<br>
+ * size. This could potentially be possible if the SCO packet was<br>
+ * received partially over-the-air, or more probably due to hardware<br>
+ * issues in our Bluetooth adapter. In these cases, in order to avoid<br>
+ * an assertion failure due to unaligned data, just discard the whole<br>
+ * packet */<br>
+ if (!pa_frame_aligned(l, &u->sample_spec)) {<br>
+ pa_log_warn("SCO packet received of unaligned size: %zu", l);<br>
+ return -1;<br>
+ }<br>
+<br>
+ memchunk.length = (size_t) l;<br>
+ u->read_index += (uint64_t) l;<br>
+<br>
+ for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm))<br>
+ if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) {<br>
+ struct timeval *tv = (struct timeval*) CMSG_DATA(cm);<br>
+ pa_rtclock_from_wallclock(tv);<br>
+ tstamp = pa_timeval_load(tv);<br>
+ found_tstamp = true;<br>
+ }<br>
+<br>
+ if (!found_tstamp) {<br>
+ pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!");<br>
+ tstamp = pa_rtclock_now();<br>
+ }<br>
+<br>
+ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));<br>
+ pa_smoother_resume(u->read_smoother, tstamp, true);<br>
+<br>
+ pa_source_post(u->source, &memchunk);<br>
+ pa_memblock_unref(memchunk.memblock);<br>
+<br>
+ return l;<br>
+}<br>
+<br>
+/* Run from IO thread */<br>
static void a2dp_prepare_buffer(struct userdata *u) {<br>
size_t min_buffer_size = PA_MAX(u->read_link_mtu, u->write_link_mtu);<br>
<br>
@@ -611,24 +762,31 @@ static void transport_release(struct userdata *u) {<br>
<br>
/* Run from I/O thread */<br>
static void transport_config_mtu(struct userdata *u) {<br>
- u->read_block_size =<br>
- (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))<br>
- / u->sbc_info.frame_length * u->sbc_info.codesize;<br>
+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {<br>
+ u->read_block_size = u->read_link_mtu;<br>
+ u->write_block_size = u->write_link_mtu;<br>
+ } else {<br>
+ u->read_block_size =<br>
+ (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))<br>
+ / u->sbc_info.frame_length * u->sbc_info.codesize;<br>
<br>
- u->write_block_size =<br>
- (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))<br>
- / u->sbc_info.frame_length * u->sbc_info.codesize;<br>
+ u->write_block_size =<br>
+ (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))<br>
+ / u->sbc_info.frame_length * u->sbc_info.codesize;<br>
+ }<br>
<br>
if (u->sink) {<br>
pa_sink_set_max_request_within_thread(u->sink, u->write_block_size);<br>
pa_sink_set_fixed_latency_within_thread(u->sink,<br>
- FIXED_LATENCY_PLAYBACK_A2DP +<br>
+ (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ?<br>
+ FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_SCO) +<br>
pa_bytes_to_usec(u->write_block_size, &u->sample_spec));<br>
}<br>
<br>
if (u->source)<br>
pa_source_set_fixed_latency_within_thread(u->source,<br>
- FIXED_LATENCY_RECORD_A2DP +<br>
+ (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE ?<br>
+ FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_SCO) +<br>
pa_bytes_to_usec(u->read_block_size, &u->sample_spec));<br>
}<br>
<br>
@@ -755,15 +913,19 @@ static int add_source(struct userdata *u) {<br>
data.namereg_fail = false;<br>
pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));<br>
pa_source_new_data_set_sample_spec(&data, &u->sample_spec);<br>
+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)<br>
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");<br>
<br>
connect_ports(u, &data, PA_DIRECTION_INPUT);<br>
<br>
if (!u->transport_acquired)<br>
switch (u->profile) {<br>
case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:<br>
+ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:<br>
data.suspend_cause = PA_SUSPEND_USER;<br>
break;<br>
case PA_BLUETOOTH_PROFILE_A2DP_SINK:<br>
+ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:<br>
case PA_BLUETOOTH_PROFILE_OFF:<br>
pa_assert_not_reached();<br>
break;<br>
@@ -870,14 +1032,20 @@ static int add_sink(struct userdata *u) {<br>
data.namereg_fail = false;<br>
pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));<br>
pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);<br>
+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)<br>
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");<br>
<br>
connect_ports(u, &data, PA_DIRECTION_OUTPUT);<br>
<br>
if (!u->transport_acquired)<br>
switch (u->profile) {<br>
+ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:<br>
+ data.suspend_cause = PA_SUSPEND_USER;<br>
+ break;<br>
case PA_BLUETOOTH_PROFILE_A2DP_SINK:<br>
/* Profile switch should have failed */<br>
case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:<br>
+ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:<br>
case PA_BLUETOOTH_PROFILE_OFF:<br>
pa_assert_not_reached();<br>
break;<br>
@@ -898,111 +1066,117 @@ static int add_sink(struct userdata *u) {<br>
<br>
/* Run from main thread */<br>
static void transport_config(struct userdata *u) {<br>
- sbc_info_t *sbc_info = &u->sbc_info;<br>
- a2dp_sbc_t *config;<br>
+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {<br>
+ u->sample_spec.format = PA_SAMPLE_S16LE;<br>
+ u->sample_spec.channels = 1;<br>
+ u->sample_spec.rate = 8000;<br>
+ } else {<br>
+ sbc_info_t *sbc_info = &u->sbc_info;<br>
+ a2dp_sbc_t *config;<br>
<br>
- pa_assert(u->transport);<br>
+ pa_assert(u->transport);<br>
<br>
- u->sample_spec.format = PA_SAMPLE_S16LE;<br>
- config = (a2dp_sbc_t *) u->transport->config;<br>
+ u->sample_spec.format = PA_SAMPLE_S16LE;<br>
+ config = (a2dp_sbc_t *) u->transport->config;<br>
<br>
- if (sbc_info->sbc_initialized)<br>
- sbc_reinit(&sbc_info->sbc, 0);<br>
- else<br>
- sbc_init(&sbc_info->sbc, 0);<br>
- sbc_info->sbc_initialized = true;<br>
+ if (sbc_info->sbc_initialized)<br>
+ sbc_reinit(&sbc_info->sbc, 0);<br>
+ else<br>
+ sbc_init(&sbc_info->sbc, 0);<br>
+ sbc_info->sbc_initialized = true;<br>
<br>
- switch (config->frequency) {<br>
- case SBC_SAMPLING_FREQ_16000:<br>
- sbc_info->sbc.frequency = SBC_FREQ_16000;<br>
- u->sample_spec.rate = 16000U;<br>
- break;<br>
- case SBC_SAMPLING_FREQ_32000:<br>
- sbc_info->sbc.frequency = SBC_FREQ_32000;<br>
- u->sample_spec.rate = 32000U;<br>
- break;<br>
- case SBC_SAMPLING_FREQ_44100:<br>
- sbc_info->sbc.frequency = SBC_FREQ_44100;<br>
- u->sample_spec.rate = 44100U;<br>
- break;<br>
- case SBC_SAMPLING_FREQ_48000:<br>
- sbc_info->sbc.frequency = SBC_FREQ_48000;<br>
- u->sample_spec.rate = 48000U;<br>
- break;<br>
- default:<br>
- pa_assert_not_reached();<br>
- }<br>
+ switch (config->frequency) {<br>
+ case SBC_SAMPLING_FREQ_16000:<br>
+ sbc_info->sbc.frequency = SBC_FREQ_16000;<br>
+ u->sample_spec.rate = 16000U;<br>
+ break;<br>
+ case SBC_SAMPLING_FREQ_32000:<br>
+ sbc_info->sbc.frequency = SBC_FREQ_32000;<br>
+ u->sample_spec.rate = 32000U;<br>
+ break;<br>
+ case SBC_SAMPLING_FREQ_44100:<br>
+ sbc_info->sbc.frequency = SBC_FREQ_44100;<br>
+ u->sample_spec.rate = 44100U;<br>
+ break;<br>
+ case SBC_SAMPLING_FREQ_48000:<br>
+ sbc_info->sbc.frequency = SBC_FREQ_48000;<br>
+ u->sample_spec.rate = 48000U;<br>
+ break;<br>
+ default:<br>
+ pa_assert_not_reached();<br>
+ }<br>
<br>
- switch (config->channel_mode) {<br>
- case SBC_CHANNEL_MODE_MONO:<br>
- sbc_info->sbc.mode = SBC_MODE_MONO;<br>
- u->sample_spec.channels = 1;<br>
- break;<br>
- case SBC_CHANNEL_MODE_DUAL_CHANNEL:<br>
- sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL;<br>
- u->sample_spec.channels = 2;<br>
- break;<br>
- case SBC_CHANNEL_MODE_STEREO:<br>
- sbc_info->sbc.mode = SBC_MODE_STEREO;<br>
- u->sample_spec.channels = 2;<br>
- break;<br>
- case SBC_CHANNEL_MODE_JOINT_STEREO:<br>
- sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO;<br>
- u->sample_spec.channels = 2;<br>
- break;<br>
- default:<br>
- pa_assert_not_reached();<br>
- }<br>
+ switch (config->channel_mode) {<br>
+ case SBC_CHANNEL_MODE_MONO:<br>
+ sbc_info->sbc.mode = SBC_MODE_MONO;<br>
+ u->sample_spec.channels = 1;<br>
+ break;<br>
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:<br>
+ sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL;<br>
+ u->sample_spec.channels = 2;<br>
+ break;<br>
+ case SBC_CHANNEL_MODE_STEREO:<br>
+ sbc_info->sbc.mode = SBC_MODE_STEREO;<br>
+ u->sample_spec.channels = 2;<br>
+ break;<br>
+ case SBC_CHANNEL_MODE_JOINT_STEREO:<br>
+ sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO;<br>
+ u->sample_spec.channels = 2;<br>
+ break;<br>
+ default:<br>
+ pa_assert_not_reached();<br>
+ }<br>
<br>
- switch (config->allocation_method) {<br>
- case SBC_ALLOCATION_SNR:<br>
- sbc_info->sbc.allocation = SBC_AM_SNR;<br>
- break;<br>
- case SBC_ALLOCATION_LOUDNESS:<br>
- sbc_info->sbc.allocation = SBC_AM_LOUDNESS;<br>
- break;<br>
- default:<br>
- pa_assert_not_reached();<br>
- }<br>
+ switch (config->allocation_method) {<br>
+ case SBC_ALLOCATION_SNR:<br>
+ sbc_info->sbc.allocation = SBC_AM_SNR;<br>
+ break;<br>
+ case SBC_ALLOCATION_LOUDNESS:<br>
+ sbc_info->sbc.allocation = SBC_AM_LOUDNESS;<br>
+ break;<br>
+ default:<br>
+ pa_assert_not_reached();<br>
+ }<br>
<br>
- switch (config->subbands) {<br>
- case SBC_SUBBANDS_4:<br>
- sbc_info->sbc.subbands = SBC_SB_4;<br>
- break;<br>
- case SBC_SUBBANDS_8:<br>
- sbc_info->sbc.subbands = SBC_SB_8;<br>
- break;<br>
- default:<br>
- pa_assert_not_reached();<br>
- }<br>
+ switch (config->subbands) {<br>
+ case SBC_SUBBANDS_4:<br>
+ sbc_info->sbc.subbands = SBC_SB_4;<br>
+ break;<br>
+ case SBC_SUBBANDS_8:<br>
+ sbc_info->sbc.subbands = SBC_SB_8;<br>
+ break;<br>
+ default:<br>
+ pa_assert_not_reached();<br>
+ }<br>
<br>
- switch (config->block_length) {<br>
- case SBC_BLOCK_LENGTH_4:<br>
- sbc_info->sbc.blocks = SBC_BLK_4;<br>
- break;<br>
- case SBC_BLOCK_LENGTH_8:<br>
- sbc_info->sbc.blocks = SBC_BLK_8;<br>
- break;<br>
- case SBC_BLOCK_LENGTH_12:<br>
- sbc_info->sbc.blocks = SBC_BLK_12;<br>
- break;<br>
- case SBC_BLOCK_LENGTH_16:<br>
- sbc_info->sbc.blocks = SBC_BLK_16;<br>
- break;<br>
- default:<br>
- pa_assert_not_reached();<br>
- }<br>
+ switch (config->block_length) {<br>
+ case SBC_BLOCK_LENGTH_4:<br>
+ sbc_info->sbc.blocks = SBC_BLK_4;<br>
+ break;<br>
+ case SBC_BLOCK_LENGTH_8:<br>
+ sbc_info->sbc.blocks = SBC_BLK_8;<br>
+ break;<br>
+ case SBC_BLOCK_LENGTH_12:<br>
+ sbc_info->sbc.blocks = SBC_BLK_12;<br>
+ break;<br>
+ case SBC_BLOCK_LENGTH_16:<br>
+ sbc_info->sbc.blocks = SBC_BLK_16;<br>
+ break;<br>
+ default:<br>
+ pa_assert_not_reached();<br>
+ }<br>
<br>
- sbc_info->min_bitpool = config->min_bitpool;<br>
- sbc_info->max_bitpool = config->max_bitpool;<br>
+ sbc_info->min_bitpool = config->min_bitpool;<br>
+ sbc_info->max_bitpool = config->max_bitpool;<br>
<br>
- /* Set minimum bitpool for source to get the maximum possible block_size */<br>
- sbc_info->sbc.bitpool = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? sbc_info->max_bitpool : sbc_info->min_bitpool;<br>
- sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc);<br>
- sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc);<br>
+ /* Set minimum bitpool for source to get the maximum possible block_size */<br>
+ sbc_info->sbc.bitpool = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? sbc_info->max_bitpool : sbc_info->min_bitpool;<br>
+ sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc);<br>
+ sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc);<br>
<br>
- pa_log_info("SBC parameters: allocation=%u, subbands=%u, blocks=%u, bitpool=%u",<br>
- sbc_info->sbc.allocation, sbc_info->sbc.subbands, sbc_info->sbc.blocks, sbc_info->sbc.bitpool);<br>
+ pa_log_info("SBC parameters: allocation=%u, subbands=%u, blocks=%u, bitpool=%u",<br>
+ sbc_info->sbc.allocation, sbc_info->sbc.subbands, sbc_info->sbc.blocks, sbc_info->sbc.bitpool);<br>
+ }<br>
}<br>
<br>
/* Run from main thread */<br>
@@ -1022,7 +1196,7 @@ static int setup_transport(struct userdata *u) {<br>
<br>
u->transport = t;<br>
<br>
- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)<br>
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)<br>
transport_acquire(u, true); /* In case of error, the sink/sources will be created suspended */<br>
else if (transport_acquire(u, false) < 0)<br>
return -1; /* We need to fail here until the interactions with module-suspend-on-idle and alike get improved */<br>
@@ -1043,11 +1217,13 @@ static int init_profile(struct userdata *u) {<br>
<br>
pa_assert(u->transport);<br>
<br>
- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)<br>
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||<br>
+ u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)<br>
if (add_sink(u) < 0)<br>
r = -1;<br>
<br>
- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)<br>
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||<br>
+ u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)<br>
if (add_source(u) < 0)<br>
r = -1;<br>
<br>
@@ -1111,7 +1287,10 @@ static void thread_func(void *userdata) {<br>
if (pollfd && (pollfd->revents & POLLIN)) {<br>
int n_read;<br>
<br>
- n_read = a2dp_process_push(u);<br>
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)<br>
+ n_read = a2dp_process_push(u);<br>
+ else<br>
+ n_read = sco_process_push(u);<br>
<br>
if (n_read < 0)<br>
goto fail;<br>
@@ -1182,8 +1361,13 @@ static void thread_func(void *userdata) {<br>
if (u->write_index <= 0)<br>
u->started_at = pa_rtclock_now();<br>
<br>
- if ((n_written = a2dp_process_render(u)) < 0)<br>
- goto fail;<br>
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {<br>
+ if ((n_written = a2dp_process_render(u)) < 0)<br>
+ goto fail;<br>
+ } else {<br>
+ if ((n_written = sco_process_render(u)) < 0)<br>
+ goto fail;<br>
+ }<br>
<br>
if (n_written == 0)<br>
pa_log("Broken kernel: we got EAGAIN on write() after POLLOUT!");<br>
@@ -1363,6 +1547,8 @@ static pa_direction_t get_profile_direction(pa_bluetooth_profile_t p) {<br>
static const pa_direction_t profile_direction[] = {<br>
[PA_BLUETOOTH_PROFILE_A2DP_SINK] = PA_DIRECTION_OUTPUT,<br>
[PA_BLUETOOTH_PROFILE_A2DP_SOURCE] = PA_DIRECTION_INPUT,<br>
+ [PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,<br>
+ [PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,<br>
[PA_BLUETOOTH_PROFILE_OFF] = 0<br>
};<br>
<br>
@@ -1540,6 +1726,30 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid<br>
<br>
p = PA_CARD_PROFILE_DATA(cp);<br>
*p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;<br>
+ } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_HF)) {<br>
+ cp = pa_card_profile_new("headset_head_unit", _("Headset Head Unit (HSP/HFP)"), sizeof(enum profile));<br>
+ cp->priority = 20;<br>
+ cp->n_sinks = 1;<br>
+ cp->n_sources = 1;<br>
+ cp->max_sink_channels = 1;<br>
+ cp->max_source_channels = 1;<br>
+ pa_hashmap_put(input_port->profiles, cp->name, cp);<br>
+ pa_hashmap_put(output_port->profiles, cp->name, cp);<br>
+<br>
+ p = PA_CARD_PROFILE_DATA(cp);<br>
+ *p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT;<br>
+ } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_AG) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_AG)) {<br>
+ cp = pa_card_profile_new("headset_audio_gateway", _("Headset Audio Gateway (HSP/HFP)"), sizeof(enum profile));<br>
+ cp->priority = 20;<br>
+ cp->n_sinks = 1;<br>
+ cp->n_sources = 1;<br>
+ cp->max_sink_channels = 1;<br>
+ cp->max_source_channels = 1;<br>
+ pa_hashmap_put(input_port->profiles, cp->name, cp);<br>
+ pa_hashmap_put(output_port->profiles, cp->name, cp);<br>
+<br>
+ p = PA_CARD_PROFILE_DATA(cp);<br>
+ *p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY;<br>
}<br>
<br>
if (cp && u->device->transports[*p])<br>
--<br>
1.9.3<br>
<br>
<br>
<br>
------------------------------<br>
<br>
Subject: Digest Footer<br>
<br>
_______________________________________________<br>
pulseaudio-discuss mailing list<br>
<a href="mailto:pulseaudio-discuss@lists.freedesktop.org">pulseaudio-discuss@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss" target="_blank">http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss</a><br>
<br>
<br>
------------------------------<br>
<br>
End of pulseaudio-discuss Digest, Vol 40, Issue 34<br>
**************************************************<br>
</blockquote></div><br></div></div>