2008-07-31 20:16:43 -03:00
/***
2009-01-29 16:27:27 +01:00
This file is part of PulseAudio .
2008-07-31 20:16:43 -03:00
2013-08-13 01:53:51 -03:00
Copyright 2008 - 2013 João Paulo Rechi Vita
Copyright 2011 - 2013 BMW Car IT GmbH .
2008-07-31 20:16:43 -03:00
2009-01-29 16:27:27 +01:00
PulseAudio is free software ; you can redistribute it and / or modify
it under the terms of the GNU Lesser General Public License as
2009-03-03 20:23:02 +00:00
published by the Free Software Foundation ; either version 2.1 of the
2009-01-29 16:27:27 +01:00
License , or ( at your option ) any later version .
2008-07-31 20:16:43 -03:00
2009-01-29 16:27:27 +01:00
PulseAudio is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
2008-07-31 20:16:43 -03:00
2009-01-29 16:27:27 +01:00
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
USA .
2008-07-31 20:16:43 -03:00
* * */
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
2008-08-11 13:27:13 -03:00
# include <string.h>
2008-08-11 18:10:14 -03:00
# include <errno.h>
2012-12-06 10:35:17 +01:00
# include <math.h>
2008-08-25 22:15:25 -03:00
# include <linux/sockios.h>
2008-08-29 11:46:02 -03:00
# include <arpa/inet.h>
2008-08-11 13:27:13 -03:00
2009-04-04 23:19:53 +03:00
# include <pulse/rtclock.h>
# include <pulse/sample.h>
# include <pulse/timeval.h>
# include <pulse/xmalloc.h>
2009-02-20 01:18:37 +01:00
2011-08-10 10:30:15 +02:00
# include <pulsecore/i18n.h>
2008-07-31 20:16:43 -03:00
# include <pulsecore/module.h>
# include <pulsecore/modargs.h>
2009-04-04 23:19:53 +03:00
# include <pulsecore/core-rtclock.h>
2008-08-19 16:06:21 -03:00
# include <pulsecore/core-util.h>
# include <pulsecore/core-error.h>
2011-03-28 15:35:16 +03:00
# include <pulsecore/shared.h>
2008-08-19 16:06:21 -03:00
# include <pulsecore/socket-util.h>
2008-08-11 13:27:13 -03:00
# include <pulsecore/thread.h>
# include <pulsecore/thread-mq.h>
2011-01-04 11:17:53 +01:00
# include <pulsecore/poll.h>
2008-08-11 13:27:13 -03:00
# include <pulsecore/rtpoll.h>
# include <pulsecore/time-smoother.h>
2009-02-03 17:15:41 +02:00
# include <pulsecore/namereg.h>
2009-01-29 16:27:27 +01:00
2012-08-17 17:21:50 +03:00
# include <sbc/sbc.h>
2013-08-13 01:53:52 -03:00
# include "module-bluez4-device-symdef.h"
2011-03-31 00:56:20 +05:30
# include "a2dp-codecs.h"
2008-08-29 20:22:14 -03:00
# include "rtp.h"
2013-08-13 01:53:52 -03:00
# include "bluez4-util.h"
2008-08-11 13:27:13 -03:00
2010-12-23 13:13:44 +02:00
# define BITPOOL_DEC_LIMIT 32
# define BITPOOL_DEC_STEP 5
2013-08-13 01:53:51 -03:00
PA_MODULE_AUTHOR ( " João Paulo Rechi Vita " ) ;
PA_MODULE_DESCRIPTION ( " BlueZ 4 Bluetooth audio sink and source " ) ;
2008-07-31 20:16:43 -03:00
PA_MODULE_VERSION ( PACKAGE_VERSION ) ;
2013-01-11 11:07:46 +01:00
PA_MODULE_LOAD_ONCE ( false ) ;
2008-07-31 20:16:43 -03:00
PA_MODULE_USAGE (
2009-01-29 16:27:27 +01:00
" name=<name for the card/sink/source, to be prefixed> "
" card_name=<name for the card> "
2009-05-28 02:39:22 +02:00
" card_properties=<properties for the card> "
2009-01-29 16:27:27 +01:00
" sink_name=<name for the sink> "
2009-05-28 02:39:22 +02:00
" sink_properties=<properties for the sink> "
2009-01-29 16:27:27 +01:00
" source_name=<name for the source> "
2009-05-28 02:39:22 +02:00
" source_properties=<properties for the source> "
2008-09-29 21:40:52 +02:00
" address=<address of the device> "
2010-01-29 11:01:31 -02:00
" profile=<a2dp|hsp|hfgw> "
2009-01-19 14:53:35 +02:00
" rate=<sample rate> "
" channels=<number of channels> "
2009-10-04 13:00:51 +02:00
" path=<device object path> "
2011-03-28 15:35:14 +03:00
" auto_connect=<automatically connect?> "
2009-02-12 03:57:59 +01:00
" sco_sink=<SCO over PCM sink name> "
2011-03-28 15:35:14 +03:00
" sco_source=<SCO over PCM source name> " ) ;
2008-08-11 13:27:13 -03:00
2009-03-19 17:20:56 +01:00
/* TODO: not close fd when entering suspend mode in a2dp */
2009-01-29 16:27:27 +01:00
static const char * const valid_modargs [ ] = {
" name " ,
" card_name " ,
2009-05-28 02:39:22 +02:00
" card_properties " ,
2009-01-29 16:27:27 +01:00
" sink_name " ,
2009-05-28 02:39:22 +02:00
" sink_properties " ,
2009-01-29 16:27:27 +01:00
" source_name " ,
2009-05-28 02:39:22 +02:00
" source_properties " ,
2009-01-29 16:27:27 +01:00
" address " ,
" profile " ,
" rate " ,
" channels " ,
" path " ,
2009-10-04 13:00:51 +02:00
" auto_connect " ,
2009-02-03 17:15:41 +02:00
" sco_sink " ,
" sco_source " ,
2009-01-29 16:27:27 +01:00
NULL
} ;
2009-02-02 01:58:48 +01:00
struct a2dp_info {
2008-08-11 13:27:13 -03:00
sbc_t sbc ; /* Codec data */
2013-01-11 22:11:04 +02:00
bool sbc_initialized ; /* Keep track if the encoder is initialized */
2009-03-20 18:04:23 +01:00
size_t codesize , frame_length ; /* SBC Codesize, frame_length. We simply cache those values here */
2008-08-11 13:27:13 -03:00
2009-02-02 01:58:48 +01:00
void * buffer ; /* Codec transfer buffer */
size_t buffer_size ; /* Size of the buffer */
2008-08-11 13:27:13 -03:00
uint16_t seq_num ; /* Cumulative packet sequence */
2010-12-23 13:13:44 +02:00
uint8_t min_bitpool ;
uint8_t max_bitpool ;
2008-08-11 13:27:13 -03:00
} ;
2008-07-31 20:16:43 -03:00
2009-02-03 17:15:41 +02:00
struct hsp_info {
pa_sink * sco_sink ;
2011-03-28 15:35:17 +03:00
void ( * sco_sink_set_volume ) ( pa_sink * s ) ;
2009-02-03 17:15:41 +02:00
pa_source * sco_source ;
2011-03-28 15:35:17 +03:00
void ( * sco_source_set_volume ) ( pa_source * s ) ;
2009-02-03 17:15:41 +02:00
} ;
2011-10-04 09:37:24 +02:00
struct bluetooth_msg {
pa_msgobject parent ;
pa_card * card ;
} ;
typedef struct bluetooth_msg bluetooth_msg ;
PA_DEFINE_PRIVATE_CLASS ( bluetooth_msg , pa_msgobject ) ;
# define BLUETOOTH_MSG(o) (bluetooth_msg_cast(o))
2008-07-31 20:16:43 -03:00
struct userdata {
pa_core * core ;
pa_module * module ;
2009-02-02 01:58:48 +01:00
2013-08-13 01:53:54 -03:00
pa_bluez4_device * device ;
2012-10-26 08:23:51 +02:00
pa_hook_slot * uuid_added_slot ;
2009-03-21 01:19:49 +01:00
char * address ;
2009-03-21 02:54:18 +01:00
char * path ;
2013-08-13 01:53:54 -03:00
pa_bluez4_transport * transport ;
2012-12-14 15:14:34 +01:00
bool transport_acquired ;
2012-11-22 15:20:28 +01:00
pa_hook_slot * discovery_slot ;
2012-12-11 14:40:24 +01:00
pa_hook_slot * sink_state_changed_slot ;
pa_hook_slot * source_state_changed_slot ;
2012-12-10 08:30:40 +01:00
pa_hook_slot * transport_state_changed_slot ;
2012-12-10 08:30:41 +01:00
pa_hook_slot * transport_nrec_changed_slot ;
2012-12-14 15:14:30 +01:00
pa_hook_slot * transport_microphone_changed_slot ;
pa_hook_slot * transport_speaker_changed_slot ;
2010-10-07 17:22:41 +03:00
2013-08-13 01:53:54 -03:00
pa_bluez4_discovery * discovery ;
2013-01-11 22:11:04 +02:00
bool auto_connect ;
2009-03-21 02:54:18 +01:00
2013-03-07 11:32:23 +01:00
char * output_port_name ;
char * input_port_name ;
2009-02-02 01:58:48 +01:00
pa_card * card ;
2008-07-31 20:16:43 -03:00
pa_sink * sink ;
2009-01-19 14:53:35 +02:00
pa_source * source ;
2008-07-31 20:16:43 -03:00
2008-08-11 13:27:13 -03:00
pa_thread_mq thread_mq ;
pa_rtpoll * rtpoll ;
pa_rtpoll_item * rtpoll_item ;
pa_thread * thread ;
2011-10-04 09:37:24 +02:00
bluetooth_msg * msg ;
2008-08-11 13:27:13 -03:00
2009-02-02 01:58:48 +01:00
uint64_t read_index , write_index ;
pa_usec_t started_at ;
pa_smoother * read_smoother ;
pa_memchunk write_memchunk ;
2008-08-11 13:27:13 -03:00
2009-02-12 03:47:27 +01:00
pa_sample_spec sample_spec , requested_sample_spec ;
2008-08-11 13:27:13 -03:00
2008-09-29 21:43:28 +02:00
int stream_fd ;
2008-08-11 18:10:14 -03:00
2012-07-27 16:41:21 +02:00
size_t read_link_mtu ;
size_t read_block_size ;
size_t write_link_mtu ;
size_t write_block_size ;
2008-08-11 13:27:13 -03:00
2009-02-02 01:58:48 +01:00
struct a2dp_info a2dp ;
2009-02-03 17:15:41 +02:00
struct hsp_info hsp ;
2008-07-31 20:16:43 -03:00
2013-08-15 16:17:26 -03:00
pa_bluez4_profile_t profile ;
2009-01-29 16:27:27 +01:00
2009-02-02 01:58:48 +01:00
pa_modargs * modargs ;
2009-04-10 01:30:50 +02:00
int stream_write_type ;
2008-07-31 20:16:43 -03:00
} ;
2011-10-04 09:37:24 +02:00
enum {
2012-05-15 15:55:36 +02:00
BLUETOOTH_MESSAGE_IO_THREAD_FAILED ,
2011-10-04 09:37:24 +02:00
BLUETOOTH_MESSAGE_MAX
} ;
2009-04-10 01:30:50 +02:00
# define FIXED_LATENCY_PLAYBACK_A2DP (25*PA_USEC_PER_MSEC)
2009-07-30 03:18:15 -03:00
# define FIXED_LATENCY_RECORD_A2DP (25*PA_USEC_PER_MSEC)
2009-04-10 01:30:50 +02:00
# define FIXED_LATENCY_PLAYBACK_HSP (125*PA_USEC_PER_MSEC)
# define FIXED_LATENCY_RECORD_HSP (25*PA_USEC_PER_MSEC)
2009-05-08 13:25:21 +03:00
# define MAX_PLAYBACK_CATCH_UP_USEC (100*PA_USEC_PER_MSEC)
2013-08-15 16:17:26 -03:00
# define USE_SCO_OVER_PCM(u) (u->profile == PA_BLUEZ4_PROFILE_HSP && (u->hsp.sco_sink && u->hsp.sco_source))
2009-03-06 14:45:06 +02:00
2009-02-03 18:17:20 +02:00
static int init_profile ( struct userdata * u ) ;
2010-12-23 13:13:44 +02:00
/* from IO thread */
2013-06-18 22:24:24 +02:00
static void a2dp_set_bitpool ( struct userdata * u , uint8_t bitpool ) {
2010-12-23 13:13:44 +02:00
struct a2dp_info * a2dp ;
pa_assert ( u ) ;
a2dp = & u - > a2dp ;
if ( a2dp - > sbc . bitpool = = bitpool )
return ;
if ( bitpool > a2dp - > max_bitpool )
bitpool = a2dp - > max_bitpool ;
else if ( bitpool < a2dp - > min_bitpool )
bitpool = a2dp - > min_bitpool ;
a2dp - > sbc . bitpool = bitpool ;
a2dp - > codesize = sbc_get_codesize ( & a2dp - > sbc ) ;
a2dp - > frame_length = sbc_get_frame_length ( & a2dp - > sbc ) ;
pa_log_debug ( " Bitpool has changed to %u " , a2dp - > sbc . bitpool ) ;
2012-07-27 16:41:21 +02:00
u - > read_block_size =
( u - > read_link_mtu - sizeof ( struct rtp_header ) - sizeof ( struct rtp_payload ) )
/ a2dp - > frame_length * a2dp - > codesize ;
u - > write_block_size =
( u - > write_link_mtu - sizeof ( struct rtp_header ) - sizeof ( struct rtp_payload ) )
2012-07-27 16:41:20 +02:00
/ a2dp - > frame_length * a2dp - > codesize ;
2010-12-23 13:13:44 +02:00
2012-07-27 16:41:21 +02:00
pa_sink_set_max_request_within_thread ( u - > sink , u - > write_block_size ) ;
2010-12-23 13:13:44 +02:00
pa_sink_set_fixed_latency_within_thread ( u - > sink ,
2012-07-27 16:41:21 +02:00
FIXED_LATENCY_PLAYBACK_A2DP + pa_bytes_to_usec ( u - > write_block_size , & u - > sample_spec ) ) ;
2010-12-23 13:13:44 +02:00
}
2012-08-31 12:51:03 +02:00
/* from IO thread, except in SCO over PCM */
static void bt_transport_config_mtu ( struct userdata * u ) {
/* Calculate block sizes */
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_HSP | | u - > profile = = PA_BLUEZ4_PROFILE_HFGW ) {
2012-08-31 12:51:03 +02:00
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 - > a2dp . frame_length * u - > a2dp . codesize ;
u - > write_block_size =
( u - > write_link_mtu - sizeof ( struct rtp_header ) - sizeof ( struct rtp_payload ) )
/ u - > a2dp . frame_length * u - > a2dp . codesize ;
}
if ( USE_SCO_OVER_PCM ( u ) )
return ;
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 ,
2013-08-15 16:17:26 -03:00
( u - > profile = = PA_BLUEZ4_PROFILE_A2DP ?
2012-08-31 12:51:03 +02:00
FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_HSP ) +
pa_bytes_to_usec ( u - > write_block_size , & u - > sample_spec ) ) ;
}
if ( u - > source )
pa_source_set_fixed_latency_within_thread ( u - > source ,
2013-08-15 16:17:26 -03:00
( u - > profile = = PA_BLUEZ4_PROFILE_A2DP_SOURCE ?
2012-08-31 12:51:03 +02:00
FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_HSP ) +
pa_bytes_to_usec ( u - > read_block_size , & u - > sample_spec ) ) ;
}
2009-03-06 14:45:06 +02:00
/* from IO thread, except in SCO over PCM */
2010-10-07 17:22:41 +03:00
2012-08-31 12:50:54 +02:00
static void setup_stream ( struct userdata * u ) {
2010-10-07 17:22:41 +03:00
struct pollfd * pollfd ;
int one ;
2012-12-03 11:03:57 +01:00
pa_log_info ( " Transport %s resuming " , u - > transport - > path ) ;
2012-08-31 12:51:03 +02:00
bt_transport_config_mtu ( u ) ;
2010-10-07 17:22:41 +03:00
pa_make_fd_nonblock ( u - > stream_fd ) ;
pa_make_socket_low_delay ( u - > stream_fd ) ;
one = 1 ;
if ( setsockopt ( u - > stream_fd , SOL_SOCKET , SO_TIMESTAMP , & one , sizeof ( one ) ) < 0 )
pa_log_warn ( " Failed to enable SO_TIMESTAMP: %s " , pa_cstrerror ( errno ) ) ;
pa_log_debug ( " Stream properly set up, we're ready to roll! " ) ;
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_A2DP )
2010-12-23 13:13:44 +02:00
a2dp_set_bitpool ( u , u - > a2dp . max_bitpool ) ;
2010-10-07 17:22:41 +03:00
u - > rtpoll_item = pa_rtpoll_item_new ( u - > rtpoll , PA_RTPOLL_NEVER , 1 ) ;
pollfd = pa_rtpoll_item_get_pollfd ( u - > rtpoll_item , NULL ) ;
pollfd - > fd = u - > stream_fd ;
pollfd - > events = pollfd - > revents = 0 ;
u - > read_index = u - > write_index = 0 ;
u - > started_at = 0 ;
if ( u - > source )
u - > read_smoother = pa_smoother_new (
PA_USEC_PER_SEC ,
PA_USEC_PER_SEC * 2 ,
2013-01-11 11:07:46 +01:00
true ,
true ,
2010-10-07 17:22:41 +03:00
10 ,
pa_rtclock_now ( ) ,
2013-01-11 11:07:46 +01:00
true ) ;
2010-10-07 17:22:41 +03:00
}
2012-08-31 12:51:04 +02:00
static void teardown_stream ( struct userdata * u ) {
if ( u - > rtpoll_item ) {
pa_rtpoll_item_free ( u - > rtpoll_item ) ;
u - > rtpoll_item = NULL ;
}
if ( u - > stream_fd > = 0 ) {
pa_close ( u - > stream_fd ) ;
u - > stream_fd = - 1 ;
}
if ( u - > read_smoother ) {
pa_smoother_free ( u - > read_smoother ) ;
u - > read_smoother = NULL ;
}
2013-01-24 10:16:56 +01:00
if ( u - > write_memchunk . memblock ) {
pa_memblock_unref ( u - > write_memchunk . memblock ) ;
pa_memchunk_reset ( & u - > write_memchunk ) ;
}
2012-08-31 12:51:04 +02:00
pa_log_debug ( " Audio stream torn down " ) ;
}
2011-03-02 12:41:26 +01:00
static void bt_transport_release ( struct userdata * u ) {
2012-10-19 10:11:28 +02:00
pa_assert ( u - > transport ) ;
2010-10-07 17:22:41 +03:00
/* Ignore if already released */
2012-12-14 15:14:35 +01:00
if ( ! u - > transport_acquired )
2010-10-07 17:22:41 +03:00
return ;
2012-10-19 10:11:28 +02:00
pa_log_debug ( " Releasing transport %s " , u - > transport - > path ) ;
2010-10-07 17:22:41 +03:00
2013-08-13 01:53:54 -03:00
pa_bluez4_transport_release ( u - > transport ) ;
2010-10-07 17:22:41 +03:00
2012-12-14 15:14:34 +01:00
u - > transport_acquired = false ;
2010-10-07 17:22:41 +03:00
2012-08-31 12:51:04 +02:00
teardown_stream ( u ) ;
2010-10-07 17:22:41 +03:00
}
2013-01-11 22:11:04 +02:00
static int bt_transport_acquire ( struct userdata * u , bool optional ) {
2012-10-19 10:11:28 +02:00
pa_assert ( u - > transport ) ;
2012-08-31 12:50:59 +02:00
2012-12-14 15:14:39 +01:00
if ( u - > transport_acquired )
2010-10-07 17:22:41 +03:00
return 0 ;
2012-10-19 10:11:28 +02:00
pa_log_debug ( " Acquiring transport %s " , u - > transport - > path ) ;
2010-10-07 17:22:41 +03:00
2013-08-13 01:53:54 -03:00
u - > stream_fd = pa_bluez4_transport_acquire ( u - > transport , optional , & u - > read_link_mtu , & u - > write_link_mtu ) ;
2013-08-13 01:53:40 -03:00
if ( u - > stream_fd < 0 ) {
if ( ! optional )
pa_log ( " Failed to acquire transport %s " , u - > transport - > path ) ;
else
pa_log_info ( " Failed optional acquire of transport %s " , u - > transport - > path ) ;
2010-10-07 17:22:41 +03:00
return - 1 ;
2013-08-13 01:53:40 -03:00
}
2010-10-07 17:22:41 +03:00
2012-12-14 15:14:34 +01:00
u - > transport_acquired = true ;
2012-10-19 10:11:28 +02:00
pa_log_info ( " Transport %s acquired: fd %d " , u - > transport - > path , u - > stream_fd ) ;
2010-10-07 17:22:41 +03:00
2012-08-31 12:50:54 +02:00
return 0 ;
2010-10-07 17:22:41 +03:00
}
2009-04-08 04:15:42 +02:00
/* Run from IO thread */
2008-08-11 13:27:13 -03:00
static int sink_process_msg ( pa_msgobject * o , int code , void * data , int64_t offset , pa_memchunk * chunk ) {
struct userdata * u = PA_SINK ( o ) - > userdata ;
2013-01-11 22:11:04 +02:00
bool failed = false ;
2009-02-22 02:00:25 +01:00
int r ;
2009-02-02 01:58:48 +01:00
pa_assert ( u - > sink = = PA_SINK ( o ) ) ;
2012-07-06 11:19:52 +02:00
pa_assert ( u - > transport ) ;
2008-08-11 13:27:13 -03:00
switch ( code ) {
case PA_SINK_MESSAGE_SET_STATE :
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
switch ( ( pa_sink_state_t ) PA_PTR_TO_UINT ( data ) ) {
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
case PA_SINK_SUSPENDED :
2012-08-31 12:51:00 +02:00
/* Ignore if transition is PA_SINK_INIT->PA_SINK_SUSPENDED */
if ( ! PA_SINK_IS_OPENED ( u - > sink - > thread_info . state ) )
break ;
2009-02-17 12:59:34 +02:00
2009-02-22 02:00:25 +01:00
/* Stop the device if the source is suspended as well */
2012-07-06 11:19:52 +02:00
if ( ! u - > source | | u - > source - > state = = PA_SOURCE_SUSPENDED )
2009-02-22 02:00:25 +01:00
/* We deliberately ignore whether stopping
* actually worked . Since the stream_fd is
* closed it doesn ' t really matter */
2012-07-06 11:19:52 +02:00
bt_transport_release ( u ) ;
2009-02-17 12:59:34 +02:00
2008-08-11 13:27:13 -03:00
break ;
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
case PA_SINK_IDLE :
case PA_SINK_RUNNING :
2009-02-22 02:00:25 +01:00
if ( u - > sink - > thread_info . state ! = PA_SINK_SUSPENDED )
2009-02-17 12:59:34 +02:00
break ;
2009-02-22 02:00:25 +01:00
/* Resume the device if the source was suspended as well */
2012-11-28 19:20:58 +01:00
if ( ! u - > source | | ! PA_SOURCE_IS_OPENED ( u - > source - > thread_info . state ) ) {
2012-12-14 15:14:40 +01:00
if ( bt_transport_acquire ( u , false ) < 0 )
2013-01-11 11:07:46 +01:00
failed = true ;
2012-12-14 15:14:39 +01:00
else
setup_stream ( u ) ;
2010-10-07 17:22:41 +03:00
}
2008-08-11 13:27:13 -03:00
break ;
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
case PA_SINK_UNLINKED :
case PA_SINK_INIT :
2009-02-02 01:58:48 +01:00
case PA_SINK_INVALID_STATE :
2008-08-11 13:27:13 -03:00
;
}
break ;
case PA_SINK_MESSAGE_GET_LATENCY : {
2009-04-10 01:30:50 +02:00
if ( u - > read_smoother ) {
pa_usec_t wi , ri ;
2009-04-04 22:56:38 +03:00
ri = pa_smoother_get ( u - > read_smoother , pa_rtclock_now ( ) ) ;
2012-07-27 16:41:21 +02:00
wi = pa_bytes_to_usec ( u - > write_index + u - > write_block_size , & u - > sample_spec ) ;
2009-04-10 01:30:50 +02:00
* ( ( pa_usec_t * ) data ) = wi > ri ? wi - ri : 0 ;
} else {
pa_usec_t ri , wi ;
2009-04-04 22:56:38 +03:00
ri = pa_rtclock_now ( ) - u - > started_at ;
2009-04-10 01:30:50 +02:00
wi = pa_bytes_to_usec ( u - > write_index , & u - > sample_spec ) ;
* ( ( pa_usec_t * ) data ) = wi > ri ? wi - ri : 0 ;
}
2009-08-15 00:48:14 +02:00
* ( ( pa_usec_t * ) data ) + = u - > sink - > thread_info . fixed_latency ;
2008-08-15 16:56:26 -03:00
return 0 ;
2008-08-11 13:27:13 -03:00
}
}
2009-02-22 02:00:25 +01:00
r = pa_sink_process_msg ( o , code , data , offset , chunk ) ;
return ( r < 0 | | ! failed ) ? r : - 1 ;
2008-08-11 13:27:13 -03:00
}
2009-04-08 04:15:42 +02:00
/* Run from IO thread */
2009-02-02 01:58:48 +01:00
static int source_process_msg ( pa_msgobject * o , int code , void * data , int64_t offset , pa_memchunk * chunk ) {
struct userdata * u = PA_SOURCE ( o ) - > userdata ;
2013-01-11 22:11:04 +02:00
bool failed = false ;
2009-02-22 02:00:25 +01:00
int r ;
2009-02-02 01:58:48 +01:00
pa_assert ( u - > source = = PA_SOURCE ( o ) ) ;
2012-07-06 11:19:52 +02:00
pa_assert ( u - > transport ) ;
2009-02-02 01:58:48 +01:00
switch ( code ) {
case PA_SOURCE_MESSAGE_SET_STATE :
switch ( ( pa_source_state_t ) PA_PTR_TO_UINT ( data ) ) {
case PA_SOURCE_SUSPENDED :
2012-08-31 12:51:00 +02:00
/* Ignore if transition is PA_SOURCE_INIT->PA_SOURCE_SUSPENDED */
if ( ! PA_SOURCE_IS_OPENED ( u - > source - > thread_info . state ) )
break ;
2009-02-22 02:00:25 +01:00
/* Stop the device if the sink is suspended as well */
2012-07-06 11:19:52 +02:00
if ( ! u - > sink | | u - > sink - > state = = PA_SINK_SUSPENDED )
bt_transport_release ( u ) ;
2009-02-22 02:00:25 +01:00
2009-04-10 01:30:50 +02:00
if ( u - > read_smoother )
2009-04-04 22:56:38 +03:00
pa_smoother_pause ( u - > read_smoother , pa_rtclock_now ( ) ) ;
2009-02-02 01:58:48 +01:00
break ;
case PA_SOURCE_IDLE :
case PA_SOURCE_RUNNING :
2009-02-22 02:00:25 +01:00
if ( u - > source - > thread_info . state ! = PA_SOURCE_SUSPENDED )
break ;
/* Resume the device if the sink was suspended as well */
2012-11-28 19:20:58 +01:00
if ( ! u - > sink | | ! PA_SINK_IS_OPENED ( u - > sink - > thread_info . state ) ) {
2012-12-14 15:14:40 +01:00
if ( bt_transport_acquire ( u , false ) < 0 )
2013-01-11 11:07:46 +01:00
failed = true ;
2012-12-14 15:14:39 +01:00
else
setup_stream ( u ) ;
2010-10-07 17:22:41 +03:00
}
2009-04-10 01:30:50 +02:00
/* We don't resume the smoother here. Instead we
* wait until the first packet arrives */
2009-02-02 01:58:48 +01:00
break ;
case PA_SOURCE_UNLINKED :
case PA_SOURCE_INIT :
case PA_SOURCE_INVALID_STATE :
;
}
break ;
case PA_SOURCE_MESSAGE_GET_LATENCY : {
2009-04-10 01:30:50 +02:00
pa_usec_t wi , ri ;
2010-01-08 23:44:42 +01:00
if ( u - > read_smoother ) {
wi = pa_smoother_get ( u - > read_smoother , pa_rtclock_now ( ) ) ;
ri = pa_bytes_to_usec ( u - > read_index , & u - > sample_spec ) ;
* ( ( pa_usec_t * ) data ) = ( wi > ri ? wi - ri : 0 ) + u - > source - > thread_info . fixed_latency ;
} else
* ( ( pa_usec_t * ) data ) = 0 ;
2009-04-10 01:30:50 +02:00
2009-02-02 01:58:48 +01:00
return 0 ;
}
}
2009-02-22 02:00:25 +01:00
r = pa_source_process_msg ( o , code , data , offset , chunk ) ;
return ( r < 0 | | ! failed ) ? r : - 1 ;
2009-02-02 01:58:48 +01:00
}
2011-10-04 09:37:24 +02:00
/* Called from main thread context */
static int device_process_msg ( pa_msgobject * obj , int code , void * data , int64_t offset , pa_memchunk * chunk ) {
struct bluetooth_msg * u = BLUETOOTH_MSG ( obj ) ;
switch ( code ) {
2012-05-15 15:55:36 +02:00
case BLUETOOTH_MESSAGE_IO_THREAD_FAILED : {
if ( u - > card - > module - > unload_requested )
break ;
pa_log_debug ( " Switching the profile to off due to IO thread failure. " ) ;
2011-10-04 09:37:24 +02:00
2013-11-20 15:42:26 +02:00
pa_assert_se ( pa_card_set_profile ( u - > card , pa_hashmap_get ( u - > card - > profiles , " off " ) , false ) > = 0 ) ;
2011-10-04 09:37:24 +02:00
break ;
}
}
return 0 ;
}
2009-04-08 04:15:42 +02:00
/* Run from IO thread */
2009-02-02 01:58:48 +01:00
static int hsp_process_render ( struct userdata * u ) {
2008-09-29 21:43:28 +02:00
int ret = 0 ;
2008-08-11 18:10:14 -03:00
2009-02-02 01:58:48 +01:00
pa_assert ( u ) ;
2013-08-15 16:17:26 -03:00
pa_assert ( u - > profile = = PA_BLUEZ4_PROFILE_HSP | | u - > profile = = PA_BLUEZ4_PROFILE_HFGW ) ;
2009-02-02 01:58:48 +01:00
pa_assert ( u - > sink ) ;
2008-08-11 18:10:14 -03:00
2009-03-20 18:04:23 +01:00
/* First, render some data */
if ( ! u - > write_memchunk . memblock )
2012-07-27 16:41:21 +02:00
pa_sink_render_full ( u - > sink , u - > write_block_size , & u - > write_memchunk ) ;
2009-03-20 18:04:23 +01:00
2012-07-27 16:41:21 +02:00
pa_assert ( u - > write_memchunk . length = = u - > write_block_size ) ;
2008-08-11 18:10:14 -03:00
2008-09-29 21:43:28 +02:00
for ( ; ; ) {
ssize_t l ;
2009-02-02 01:58:48 +01:00
const void * p ;
2008-08-11 18:10:14 -03:00
2009-03-20 18:04:23 +01:00
/* 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 . */
2009-02-02 01:58:48 +01:00
2012-08-17 18:09:34 +03:00
p = ( const uint8_t * ) pa_memblock_acquire_chunk ( & u - > write_memchunk ) ;
2009-03-20 18:04:23 +01:00
l = pa_write ( u - > stream_fd , p , u - > write_memchunk . length , & u - > stream_write_type ) ;
pa_memblock_release ( u - > write_memchunk . memblock ) ;
2008-08-11 18:10:14 -03:00
2008-09-29 21:43:28 +02:00
pa_assert ( l ! = 0 ) ;
2008-08-11 18:10:14 -03:00
2009-02-02 01:58:48 +01:00
if ( l < 0 ) {
2009-03-20 18:04:23 +01:00
if ( errno = = EINTR )
/* Retry right away if we got interrupted */
2009-02-02 01:58:48 +01:00
continue ;
2009-03-20 18:04:23 +01:00
else if ( errno = = EAGAIN )
/* Hmm, apparently the socket was not writable, give up for now */
2009-02-02 01:58:48 +01:00
break ;
2009-03-20 18:04:23 +01:00
pa_log_error ( " Failed to write data to SCO socket: %s " , pa_cstrerror ( errno ) ) ;
ret = - 1 ;
break ;
}
2009-02-02 01:58:48 +01:00
2009-03-20 18:04:23 +01:00
pa_assert ( ( size_t ) l < = u - > write_memchunk . length ) ;
2009-02-02 01:58:48 +01:00
2009-03-20 18:04:23 +01:00
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 ) ;
ret = - 1 ;
break ;
2008-08-14 20:59:28 -03:00
}
2009-02-02 01:58:48 +01:00
2009-03-20 18:04:23 +01:00
u - > write_index + = ( uint64_t ) u - > write_memchunk . length ;
pa_memblock_unref ( u - > write_memchunk . memblock ) ;
pa_memchunk_reset ( & u - > write_memchunk ) ;
2009-04-10 01:30:50 +02:00
ret = 1 ;
2009-03-20 18:04:23 +01:00
break ;
}
2009-02-02 01:58:48 +01:00
return ret ;
}
2009-04-08 04:15:42 +02:00
/* Run from IO thread */
2009-02-02 01:58:48 +01:00
static int hsp_process_push ( struct userdata * u ) {
int ret = 0 ;
pa_memchunk memchunk ;
pa_assert ( u ) ;
2013-08-15 16:17:26 -03:00
pa_assert ( u - > profile = = PA_BLUEZ4_PROFILE_HSP | | u - > profile = = PA_BLUEZ4_PROFILE_HFGW ) ;
2009-02-02 01:58:48 +01:00
pa_assert ( u - > source ) ;
2009-04-10 01:30:50 +02:00
pa_assert ( u - > read_smoother ) ;
2009-02-02 01:58:48 +01:00
2012-07-27 16:41:21 +02:00
memchunk . memblock = pa_memblock_new ( u - > core - > mempool , u - > read_block_size ) ;
2009-02-02 01:58:48 +01:00
memchunk . index = memchunk . length = 0 ;
2008-09-29 21:43:28 +02:00
2009-02-02 01:58:48 +01:00
for ( ; ; ) {
ssize_t l ;
void * p ;
2009-04-10 01:30:50 +02:00
struct msghdr m ;
struct cmsghdr * cm ;
uint8_t aux [ 1024 ] ;
struct iovec iov ;
2013-01-11 22:11:04 +02:00
bool found_tstamp = false ;
2009-04-10 01:30:50 +02:00
pa_usec_t tstamp ;
memset ( & m , 0 , sizeof ( m ) ) ;
memset ( & aux , 0 , sizeof ( aux ) ) ;
memset ( & iov , 0 , sizeof ( iov ) ) ;
m . msg_iov = & iov ;
m . msg_iovlen = 1 ;
m . msg_control = aux ;
m . msg_controllen = sizeof ( aux ) ;
2009-02-02 01:58:48 +01:00
p = pa_memblock_acquire ( memchunk . memblock ) ;
2009-04-10 01:30:50 +02:00
iov . iov_base = p ;
iov . iov_len = pa_memblock_get_length ( memchunk . memblock ) ;
l = recvmsg ( u - > stream_fd , & m , 0 ) ;
2009-02-02 01:58:48 +01:00
pa_memblock_release ( memchunk . memblock ) ;
if ( l < = 0 ) {
2009-03-20 18:04:23 +01:00
if ( l < 0 & & errno = = EINTR )
/* Retry right away if we got interrupted */
2009-02-02 01:58:48 +01:00
continue ;
2009-03-20 18:04:23 +01:00
else if ( l < 0 & & errno = = EAGAIN )
/* Hmm, apparently the socket was not readable, give up for now. */
2009-02-02 01:58:48 +01:00
break ;
2009-03-20 18:04:23 +01:00
pa_log_error ( " Failed to read data from SCO socket: %s " , l < 0 ? pa_cstrerror ( errno ) : " EOF " ) ;
ret = - 1 ;
2008-09-29 21:43:28 +02:00
break ;
2008-08-11 18:10:14 -03:00
}
2009-03-20 18:04:23 +01:00
2009-03-20 18:34:16 +01:00
pa_assert ( ( size_t ) l < = pa_memblock_get_length ( memchunk . memblock ) ) ;
2009-03-20 18:04:23 +01:00
2013-01-28 18:25:21 +01:00
/* 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 ) ;
break ;
}
2009-03-20 18:04:23 +01:00
memchunk . length = ( size_t ) l ;
u - > read_index + = ( uint64_t ) l ;
2009-04-10 01:30:50 +02:00
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 ) ;
2013-01-11 11:07:46 +01:00
found_tstamp = true ;
2009-04-10 01:30:50 +02:00
break ;
}
if ( ! found_tstamp ) {
pa_log_warn ( " Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data! " ) ;
2009-04-04 22:56:38 +03:00
tstamp = pa_rtclock_now ( ) ;
2009-04-10 01:30:50 +02:00
}
pa_smoother_put ( u - > read_smoother , tstamp , pa_bytes_to_usec ( u - > read_index , & u - > sample_spec ) ) ;
2013-01-11 11:07:46 +01:00
pa_smoother_resume ( u - > read_smoother , tstamp , true ) ;
2009-04-10 01:30:50 +02:00
2009-03-20 18:04:23 +01:00
pa_source_post ( u - > source , & memchunk ) ;
2009-04-10 01:30:50 +02:00
2012-07-27 16:41:21 +02:00
ret = l ;
2009-03-20 18:04:23 +01:00
break ;
2008-08-11 18:10:14 -03:00
}
2008-09-29 21:43:28 +02:00
pa_memblock_unref ( memchunk . memblock ) ;
return ret ;
2008-07-31 20:16:43 -03:00
}
2009-04-08 04:15:42 +02:00
/* Run from IO thread */
2009-03-20 18:04:23 +01:00
static void a2dp_prepare_buffer ( struct userdata * u ) {
2012-07-27 16:41:21 +02:00
size_t min_buffer_size = PA_MAX ( u - > read_link_mtu , u - > write_link_mtu ) ;
2009-03-20 18:04:23 +01:00
pa_assert ( u ) ;
2012-07-27 16:41:21 +02:00
if ( u - > a2dp . buffer_size > = min_buffer_size )
2009-03-20 18:04:23 +01:00
return ;
2012-07-27 16:41:21 +02:00
u - > a2dp . buffer_size = 2 * min_buffer_size ;
2009-03-20 18:04:23 +01:00
pa_xfree ( u - > a2dp . buffer ) ;
u - > a2dp . buffer = pa_xmalloc ( u - > a2dp . buffer_size ) ;
}
2009-04-08 04:15:42 +02:00
/* Run from IO thread */
2008-08-21 21:31:43 -03:00
static int a2dp_process_render ( struct userdata * u ) {
2009-02-02 01:58:48 +01:00
struct a2dp_info * a2dp ;
struct rtp_header * header ;
struct rtp_payload * payload ;
2009-03-20 18:04:23 +01:00
size_t nbytes ;
2009-02-02 01:58:48 +01:00
void * d ;
const void * p ;
2009-03-20 18:04:23 +01:00
size_t to_write , to_encode ;
2009-02-02 01:58:48 +01:00
unsigned frame_count ;
2009-03-20 18:04:23 +01:00
int ret = 0 ;
2008-08-21 21:31:43 -03:00
pa_assert ( u ) ;
2013-08-15 16:17:26 -03:00
pa_assert ( u - > profile = = PA_BLUEZ4_PROFILE_A2DP ) ;
2009-02-02 01:58:48 +01:00
pa_assert ( u - > sink ) ;
2008-08-21 21:31:43 -03:00
2009-03-20 18:04:23 +01:00
/* First, render some data */
if ( ! u - > write_memchunk . memblock )
2012-07-27 16:41:21 +02:00
pa_sink_render_full ( u - > sink , u - > write_block_size , & u - > write_memchunk ) ;
2008-08-21 21:31:43 -03:00
2012-07-27 16:41:21 +02:00
pa_assert ( u - > write_memchunk . length = = u - > write_block_size ) ;
2009-03-20 18:04:23 +01:00
a2dp_prepare_buffer ( u ) ;
2008-08-21 21:31:43 -03:00
2009-03-20 18:04:23 +01:00
a2dp = & u - > a2dp ;
header = a2dp - > buffer ;
2009-02-02 01:58:48 +01:00
payload = ( struct rtp_payload * ) ( ( uint8_t * ) a2dp - > buffer + sizeof ( * header ) ) ;
2008-08-21 21:31:43 -03:00
2009-02-02 01:58:48 +01:00
frame_count = 0 ;
2008-08-21 21:31:43 -03:00
2009-03-20 18:04:23 +01:00
/* Try to create a packet of the full MTU */
2008-09-29 21:43:28 +02:00
2012-08-17 18:09:34 +03:00
p = ( const uint8_t * ) pa_memblock_acquire_chunk ( & u - > write_memchunk ) ;
2009-03-20 18:04:23 +01:00
to_encode = u - > write_memchunk . length ;
2009-02-02 01:58:48 +01:00
2009-03-20 18:04:23 +01:00
d = ( uint8_t * ) a2dp - > buffer + sizeof ( * header ) + sizeof ( * payload ) ;
to_write = a2dp - > buffer_size - sizeof ( * header ) - sizeof ( * payload ) ;
while ( PA_LIKELY ( to_encode > 0 & & to_write > 0 ) ) {
2011-03-19 16:26:47 +01:00
ssize_t written ;
2009-03-20 18:04:23 +01:00
ssize_t encoded ;
2009-02-02 01:58:48 +01:00
encoded = sbc_encode ( & a2dp - > sbc ,
2009-03-20 18:04:23 +01:00
p , to_encode ,
d , to_write ,
2009-02-02 01:58:48 +01:00
& written ) ;
2009-02-13 15:09:16 +02:00
2009-03-20 18:04:23 +01:00
if ( PA_UNLIKELY ( encoded < = 0 ) ) {
pa_log_error ( " SBC encoding error (%li) " , ( long ) encoded ) ;
pa_memblock_release ( u - > write_memchunk . memblock ) ;
2008-08-21 21:31:43 -03:00
return - 1 ;
}
2009-03-20 18:04:23 +01:00
/* pa_log_debug("SBC: encoded: %lu; written: %lu", (unsigned long) encoded, (unsigned long) written); */
/* pa_log_debug("SBC: codesize: %lu; frame_length: %lu", (unsigned long) a2dp->codesize, (unsigned long) a2dp->frame_length); */
2009-03-19 17:44:42 +01:00
2009-03-20 18:04:23 +01:00
pa_assert_fp ( ( size_t ) encoded < = to_encode ) ;
pa_assert_fp ( ( size_t ) encoded = = a2dp - > codesize ) ;
2009-02-02 01:58:48 +01:00
2009-03-20 18:04:23 +01:00
pa_assert_fp ( ( size_t ) written < = to_write ) ;
pa_assert_fp ( ( size_t ) written = = a2dp - > frame_length ) ;
2009-02-02 01:58:48 +01:00
2009-03-20 18:04:23 +01:00
p = ( const uint8_t * ) p + encoded ;
to_encode - = encoded ;
2009-02-02 01:58:48 +01:00
d = ( uint8_t * ) d + written ;
2009-03-20 18:04:23 +01:00
to_write - = written ;
2009-02-02 01:58:48 +01:00
frame_count + + ;
2009-03-20 18:04:23 +01:00
}
pa_memblock_release ( u - > write_memchunk . memblock ) ;
pa_assert ( to_encode = = 0 ) ;
2009-02-02 01:58:48 +01:00
2009-03-20 18:04:23 +01:00
PA_ONCE_BEGIN {
pa_log_debug ( " Using SBC encoder implementation: %s " , pa_strnull ( sbc_get_implementation_info ( & a2dp - > sbc ) ) ) ;
} PA_ONCE_END ;
2008-08-21 21:31:43 -03:00
/* write it to the fifo */
memset ( a2dp - > buffer , 0 , sizeof ( * header ) + sizeof ( * payload ) ) ;
header - > v = 2 ;
header - > pt = 1 ;
2009-02-02 01:58:48 +01:00
header - > sequence_number = htons ( a2dp - > seq_num + + ) ;
2009-04-10 01:30:50 +02:00
header - > timestamp = htonl ( u - > write_index / pa_frame_size ( & u - > sample_spec ) ) ;
2008-08-21 21:31:43 -03:00
header - > ssrc = htonl ( 1 ) ;
2009-03-20 18:04:23 +01:00
payload - > frame_count = frame_count ;
2008-08-21 21:31:43 -03:00
2009-03-20 18:04:23 +01:00
nbytes = ( uint8_t * ) d - ( uint8_t * ) a2dp - > buffer ;
2009-02-02 01:58:48 +01:00
2008-09-29 21:43:28 +02:00
for ( ; ; ) {
ssize_t l ;
2008-08-21 21:31:43 -03:00
2009-03-20 18:04:23 +01:00
l = pa_write ( u - > stream_fd , a2dp - > buffer , nbytes , & u - > stream_write_type ) ;
2008-08-21 21:31:43 -03:00
2008-09-29 21:43:28 +02:00
pa_assert ( l ! = 0 ) ;
2009-02-02 01:58:48 +01:00
if ( l < 0 ) {
2008-09-29 21:43:28 +02:00
2009-03-20 18:04:23 +01:00
if ( errno = = EINTR )
/* Retry right away if we got interrupted */
continue ;
2009-02-02 01:58:48 +01:00
2009-03-20 18:04:23 +01:00
else if ( errno = = EAGAIN )
/* Hmm, apparently the socket was not writable, give up for now */
2009-02-02 01:58:48 +01:00
break ;
2009-03-20 18:04:23 +01:00
pa_log_error ( " Failed to write data to socket: %s " , pa_cstrerror ( errno ) ) ;
2011-03-12 19:45:02 +01:00
ret = - 1 ;
2009-03-20 18:04:23 +01:00
break ;
}
pa_assert ( ( size_t ) l < = nbytes ) ;
if ( ( size_t ) l ! = nbytes ) {
pa_log_warn ( " Wrote memory block to socket only partially! %llu written, wanted to write %llu. " ,
( unsigned long long ) l ,
( unsigned long long ) nbytes ) ;
ret = - 1 ;
break ;
2008-08-21 21:31:43 -03:00
}
2009-03-20 18:04:23 +01:00
u - > write_index + = ( uint64_t ) u - > write_memchunk . length ;
pa_memblock_unref ( u - > write_memchunk . memblock ) ;
pa_memchunk_reset ( & u - > write_memchunk ) ;
2009-04-10 01:30:50 +02:00
ret = 1 ;
2009-03-20 18:04:23 +01:00
break ;
2008-08-21 21:31:43 -03:00
}
2009-03-20 18:04:23 +01:00
return ret ;
2008-08-21 21:31:43 -03:00
}
2009-07-30 03:18:15 -03:00
static int a2dp_process_push ( struct userdata * u ) {
int ret = 0 ;
pa_memchunk memchunk ;
pa_assert ( u ) ;
2013-08-15 16:17:26 -03:00
pa_assert ( u - > profile = = PA_BLUEZ4_PROFILE_A2DP_SOURCE ) ;
2009-07-30 03:18:15 -03:00
pa_assert ( u - > source ) ;
pa_assert ( u - > read_smoother ) ;
2012-07-27 16:41:21 +02:00
memchunk . memblock = pa_memblock_new ( u - > core - > mempool , u - > read_block_size ) ;
2009-07-30 03:18:15 -03:00
memchunk . index = memchunk . length = 0 ;
for ( ; ; ) {
2013-01-11 22:11:04 +02:00
bool found_tstamp = false ;
2009-07-30 03:18:15 -03:00
pa_usec_t tstamp ;
struct a2dp_info * a2dp ;
struct rtp_header * header ;
struct rtp_payload * payload ;
const void * p ;
void * d ;
ssize_t l ;
size_t to_write , to_decode ;
a2dp_prepare_buffer ( u ) ;
a2dp = & u - > a2dp ;
header = a2dp - > buffer ;
payload = ( struct rtp_payload * ) ( ( uint8_t * ) a2dp - > buffer + sizeof ( * header ) ) ;
l = pa_read ( u - > stream_fd , a2dp - > buffer , a2dp - > buffer_size , & u - > stream_write_type ) ;
if ( l < = 0 ) {
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. */
break ;
pa_log_error ( " Failed to read data from socket: %s " , l < 0 ? pa_cstrerror ( errno ) : " EOF " ) ;
ret = - 1 ;
break ;
}
pa_assert ( ( size_t ) l < = a2dp - > buffer_size ) ;
u - > read_index + = ( uint64_t ) l ;
/* TODO: get timestamp from rtp */
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 ) ) ;
2013-01-11 11:07:46 +01:00
pa_smoother_resume ( u - > read_smoother , tstamp , true ) ;
2009-07-30 03:18:15 -03:00
p = ( uint8_t * ) a2dp - > buffer + sizeof ( * header ) + sizeof ( * payload ) ;
to_decode = l - sizeof ( * header ) - sizeof ( * payload ) ;
d = pa_memblock_acquire ( memchunk . memblock ) ;
to_write = memchunk . length = pa_memblock_get_length ( memchunk . memblock ) ;
2010-12-23 15:24:39 +02:00
while ( PA_LIKELY ( to_decode > 0 ) ) {
2009-07-30 03:18:15 -03:00
size_t written ;
ssize_t decoded ;
decoded = sbc_decode ( & a2dp - > sbc ,
p , to_decode ,
d , to_write ,
& written ) ;
if ( PA_UNLIKELY ( decoded < = 0 ) ) {
pa_log_error ( " SBC decoding error (%li) " , ( long ) decoded ) ;
pa_memblock_release ( memchunk . memblock ) ;
pa_memblock_unref ( memchunk . memblock ) ;
return - 1 ;
}
/* pa_log_debug("SBC: decoded: %lu; written: %lu", (unsigned long) decoded, (unsigned long) written); */
/* pa_log_debug("SBC: frame_length: %lu; codesize: %lu", (unsigned long) a2dp->frame_length, (unsigned long) a2dp->codesize); */
2010-12-23 15:24:39 +02:00
/* Reset frame length, it can be changed due to bitpool change */
a2dp - > frame_length = sbc_get_frame_length ( & a2dp - > sbc ) ;
2009-07-30 03:18:15 -03:00
pa_assert_fp ( ( size_t ) decoded < = to_decode ) ;
pa_assert_fp ( ( size_t ) decoded = = a2dp - > frame_length ) ;
pa_assert_fp ( ( size_t ) written = = a2dp - > codesize ) ;
p = ( const uint8_t * ) p + decoded ;
to_decode - = decoded ;
d = ( uint8_t * ) d + written ;
to_write - = written ;
}
2010-12-23 15:24:39 +02:00
memchunk . length - = to_write ;
2009-07-30 03:18:15 -03:00
pa_memblock_release ( memchunk . memblock ) ;
pa_source_post ( u - > source , & memchunk ) ;
2012-07-27 16:41:21 +02:00
ret = l ;
2009-07-30 03:18:15 -03:00
break ;
}
pa_memblock_unref ( memchunk . memblock ) ;
return ret ;
}
2013-06-18 22:24:24 +02:00
static void a2dp_reduce_bitpool ( struct userdata * u ) {
2010-12-23 13:13:44 +02:00
struct a2dp_info * a2dp ;
uint8_t bitpool ;
pa_assert ( u ) ;
a2dp = & u - > a2dp ;
/* Check if bitpool is already at its limit */
if ( a2dp - > sbc . bitpool < = BITPOOL_DEC_LIMIT )
return ;
bitpool = a2dp - > sbc . bitpool - BITPOOL_DEC_STEP ;
if ( bitpool < BITPOOL_DEC_LIMIT )
bitpool = BITPOOL_DEC_LIMIT ;
a2dp_set_bitpool ( u , bitpool ) ;
}
2008-08-26 09:43:48 -03:00
static void thread_func ( void * userdata ) {
2008-08-17 20:36:33 -03:00
struct userdata * u = userdata ;
2009-04-10 01:30:50 +02:00
unsigned do_write = 0 ;
2012-07-27 16:41:21 +02:00
unsigned pending_read_bytes = 0 ;
2013-01-11 22:11:04 +02:00
bool writable = false ;
2008-08-17 20:36:33 -03:00
pa_assert ( u ) ;
2012-07-06 11:19:52 +02:00
pa_assert ( u - > transport ) ;
2008-08-17 20:36:33 -03:00
2008-08-26 09:43:48 -03:00
pa_log_debug ( " IO Thread starting up " ) ;
2008-08-17 20:36:33 -03:00
2008-09-29 21:43:28 +02:00
if ( u - > core - > realtime_scheduling )
pa_make_realtime ( u - > core - > realtime_priority ) ;
2009-08-16 00:43:22 +02:00
pa_thread_mq_install ( & u - > thread_mq ) ;
2012-08-31 12:51:04 +02:00
/* Setup the stream only if the transport was already acquired */
2012-12-14 15:14:35 +01:00
if ( u - > transport_acquired )
2012-12-03 11:03:57 +01:00
setup_stream ( u ) ;
2009-02-17 12:59:34 +02:00
2008-08-17 20:36:33 -03:00
for ( ; ; ) {
struct pollfd * pollfd ;
2009-02-02 01:58:48 +01:00
int ret ;
2013-01-11 22:11:04 +02:00
bool disable_timer = true ;
2008-08-17 20:36:33 -03:00
2009-02-22 02:00:25 +01:00
pollfd = u - > rtpoll_item ? pa_rtpoll_item_get_pollfd ( u - > rtpoll_item , NULL ) : NULL ;
2008-08-17 20:36:33 -03:00
2009-02-02 01:58:48 +01:00
if ( u - > source & & PA_SOURCE_IS_LINKED ( u - > source - > thread_info . state ) ) {
2009-04-10 01:31:25 +02:00
/* We should send two blocks to the device before we expect
* a response . */
if ( u - > write_index = = 0 & & u - > read_index < = 0 )
do_write = 2 ;
2009-02-22 02:00:25 +01:00
if ( pollfd & & ( pollfd - > revents & POLLIN ) ) {
2009-04-10 01:30:50 +02:00
int n_read ;
2009-02-02 01:58:48 +01:00
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_HSP | | u - > profile = = PA_BLUEZ4_PROFILE_HFGW )
2009-07-30 03:18:15 -03:00
n_read = hsp_process_push ( u ) ;
else
n_read = a2dp_process_push ( u ) ;
if ( n_read < 0 )
2012-08-31 12:51:04 +02:00
goto io_fail ;
2009-02-02 01:58:48 +01:00
/* We just read something, so we are supposed to write something, too */
2012-07-27 16:41:21 +02:00
pending_read_bytes + = n_read ;
do_write + = pending_read_bytes / u - > write_block_size ;
pending_read_bytes = pending_read_bytes % u - > write_block_size ;
2008-08-26 09:43:48 -03:00
}
2008-08-17 20:36:33 -03:00
}
2009-02-02 01:58:48 +01:00
if ( u - > sink & & PA_SINK_IS_LINKED ( u - > sink - > thread_info . state ) ) {
2012-11-16 23:09:15 +05:30
if ( PA_UNLIKELY ( u - > sink - > thread_info . rewind_requested ) )
2009-02-02 01:58:48 +01:00
pa_sink_process_rewind ( u - > sink , 0 ) ;
2009-02-22 02:00:25 +01:00
if ( pollfd ) {
if ( pollfd - > revents & POLLOUT )
2013-01-11 11:07:46 +01:00
writable = true ;
2009-02-02 01:58:48 +01:00
2009-04-10 01:30:50 +02:00
if ( ( ! u - > source | | ! PA_SOURCE_IS_LINKED ( u - > source - > thread_info . state ) ) & & do_write < = 0 & & writable ) {
2009-02-22 02:00:25 +01:00
pa_usec_t time_passed ;
2009-05-08 13:25:21 +03:00
pa_usec_t audio_sent ;
2009-02-02 01:58:48 +01:00
2009-02-22 02:00:25 +01:00
/* Hmm, there is no input stream we could synchronize
* to . So let ' s do things by time */
2009-02-02 01:58:48 +01:00
2009-04-04 22:56:38 +03:00
time_passed = pa_rtclock_now ( ) - u - > started_at ;
2009-05-08 13:25:21 +03:00
audio_sent = pa_bytes_to_usec ( u - > write_index , & u - > sample_spec ) ;
if ( audio_sent < = time_passed ) {
pa_usec_t audio_to_send = time_passed - audio_sent ;
/* Never try to catch up for more than 100ms */
if ( u - > write_index > 0 & & audio_to_send > MAX_PLAYBACK_CATCH_UP_USEC ) {
pa_usec_t skip_usec ;
uint64_t skip_bytes ;
skip_usec = audio_to_send - MAX_PLAYBACK_CATCH_UP_USEC ;
skip_bytes = pa_usec_to_bytes ( skip_usec , & u - > sample_spec ) ;
2009-02-02 01:58:48 +01:00
2009-07-06 17:50:51 +03:00
if ( skip_bytes > 0 ) {
pa_memchunk tmp ;
2009-05-08 13:25:21 +03:00
2009-07-06 17:50:51 +03:00
pa_log_warn ( " Skipping %llu us (= %llu bytes) in audio stream " ,
( unsigned long long ) skip_usec ,
( unsigned long long ) skip_bytes ) ;
pa_sink_render_full ( u - > sink , skip_bytes , & tmp ) ;
pa_memblock_unref ( tmp . memblock ) ;
u - > write_index + = skip_bytes ;
2010-12-23 13:13:44 +02:00
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_A2DP )
2010-12-23 13:13:44 +02:00
a2dp_reduce_bitpool ( u ) ;
2009-07-06 17:50:51 +03:00
}
2009-05-08 13:25:21 +03:00
}
do_write = 1 ;
2012-07-27 16:41:21 +02:00
pending_read_bytes = 0 ;
2009-05-08 13:25:21 +03:00
}
2009-02-22 02:00:25 +01:00
}
2009-02-02 01:58:48 +01:00
2009-04-10 01:30:50 +02:00
if ( writable & & do_write > 0 ) {
int n_written ;
if ( u - > write_index < = 0 )
2009-04-04 22:56:38 +03:00
u - > started_at = pa_rtclock_now ( ) ;
2009-02-02 01:58:48 +01:00
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_A2DP ) {
2009-04-10 01:30:50 +02:00
if ( ( n_written = a2dp_process_render ( u ) ) < 0 )
2012-08-31 12:51:04 +02:00
goto io_fail ;
2009-02-22 02:00:25 +01:00
} else {
2009-04-10 01:30:50 +02:00
if ( ( n_written = hsp_process_render ( u ) ) < 0 )
2012-08-31 12:51:04 +02:00
goto io_fail ;
2009-02-22 02:00:25 +01:00
}
2009-02-02 01:58:48 +01:00
2009-07-01 13:11:33 +03:00
if ( n_written = = 0 )
pa_log ( " Broken kernel: we got EAGAIN on write() after POLLOUT! " ) ;
2009-04-10 01:30:50 +02:00
do_write - = n_written ;
2013-01-11 11:07:46 +01:00
writable = false ;
2009-02-22 02:00:25 +01:00
}
2009-02-02 01:58:48 +01:00
2011-04-04 14:33:35 +03:00
if ( ( ! u - > source | | ! PA_SOURCE_IS_LINKED ( u - > source - > thread_info . state ) ) & & do_write < = 0 ) {
pa_usec_t sleep_for ;
pa_usec_t time_passed , next_write_at ;
if ( writable ) {
/* Hmm, there is no input stream we could synchronize
* to . So let ' s estimate when we need to wake up the latest */
time_passed = pa_rtclock_now ( ) - u - > started_at ;
next_write_at = pa_bytes_to_usec ( u - > write_index , & u - > sample_spec ) ;
sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0 ;
/* pa_log("Sleeping for %lu; time passed %lu, next write at %lu", (unsigned long) sleep_for, (unsigned long) time_passed, (unsigned long)next_write_at); */
} else
/* drop stream every 500 ms */
sleep_for = PA_USEC_PER_MSEC * 500 ;
2009-02-02 01:58:48 +01:00
2009-02-22 02:00:25 +01:00
pa_rtpoll_set_timer_relative ( u - > rtpoll , sleep_for ) ;
2013-01-11 11:07:46 +01:00
disable_timer = false ;
2009-02-22 02:00:25 +01:00
}
2009-02-02 01:58:48 +01:00
}
2009-02-22 02:00:25 +01:00
}
if ( disable_timer )
2009-02-02 01:58:48 +01:00
pa_rtpoll_set_timer_disabled ( u - > rtpoll ) ;
2008-08-17 20:36:33 -03:00
/* Hmm, nothing to do. Let's sleep */
2009-02-22 02:00:25 +01:00
if ( pollfd )
2009-03-05 18:18:07 +02:00
pollfd - > events = ( short ) ( ( ( u - > sink & & PA_SINK_IS_LINKED ( u - > sink - > thread_info . state ) & & ! writable ) ? POLLOUT : 0 ) |
( u - > source & & PA_SOURCE_IS_LINKED ( u - > source - > thread_info . state ) ? POLLIN : 0 ) ) ;
2009-02-02 01:58:48 +01:00
2013-01-11 11:07:46 +01:00
if ( ( ret = pa_rtpoll_run ( u - > rtpoll , true ) ) < 0 ) {
2012-05-22 15:58:33 +03:00
pa_log_debug ( " pa_rtpoll_run failed with: %d " , ret ) ;
2008-08-17 20:36:33 -03:00
goto fail ;
2012-05-22 15:58:33 +03:00
}
if ( ret = = 0 ) {
pa_log_debug ( " IO thread shutdown requested, stopping cleanly " ) ;
2012-07-06 11:19:52 +02:00
bt_transport_release ( u ) ;
2008-08-17 20:36:33 -03:00
goto finish ;
2012-05-22 15:58:33 +03:00
}
2008-08-17 20:36:33 -03:00
2009-02-22 02:00:25 +01:00
pollfd = u - > rtpoll_item ? pa_rtpoll_item_get_pollfd ( u - > rtpoll_item , NULL ) : NULL ;
2009-02-02 01:58:48 +01:00
2009-02-22 02:00:25 +01:00
if ( pollfd & & ( pollfd - > revents & ~ ( POLLOUT | POLLIN ) ) ) {
2009-04-10 01:29:46 +02:00
pa_log_info ( " FD error: %s%s%s%s " ,
pollfd - > revents & POLLERR ? " POLLERR " : " " ,
pollfd - > revents & POLLHUP ? " POLLHUP " : " " ,
pollfd - > revents & POLLPRI ? " POLLPRI " : " " ,
pollfd - > revents & POLLNVAL ? " POLLNVAL " : " " ) ;
2012-08-31 12:51:04 +02:00
goto io_fail ;
2008-08-17 20:36:33 -03:00
}
2012-08-31 12:51:04 +02:00
continue ;
io_fail :
/* In case of HUP, just tear down the streams */
if ( ! pollfd | | ( pollfd - > revents & POLLHUP ) = = 0 )
goto fail ;
do_write = 0 ;
pending_read_bytes = 0 ;
2013-01-11 11:07:46 +01:00
writable = false ;
2012-08-31 12:51:04 +02:00
teardown_stream ( u ) ;
2008-08-17 20:36:33 -03:00
}
fail :
/* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */
2008-08-26 09:43:48 -03:00
pa_log_debug ( " IO thread failed " ) ;
2012-05-15 15:55:36 +02:00
pa_asyncmsgq_post ( pa_thread_mq_get ( ) - > outq , PA_MSGOBJECT ( u - > msg ) , BLUETOOTH_MESSAGE_IO_THREAD_FAILED , NULL , 0 , NULL , NULL ) ;
2008-08-17 20:36:33 -03:00
pa_asyncmsgq_wait_for ( u - > thread_mq . inq , PA_MESSAGE_SHUTDOWN ) ;
finish :
2008-08-26 09:43:48 -03:00
pa_log_debug ( " IO thread shutting down " ) ;
2008-08-17 20:36:33 -03:00
}
2013-08-13 01:53:54 -03:00
static pa_available_t transport_state_to_availability ( pa_bluez4_transport_state_t state ) {
if ( state = = PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED )
2013-02-18 16:13:24 +01:00
return PA_AVAILABLE_NO ;
2013-08-13 01:53:54 -03:00
else if ( state > = PA_BLUEZ4_TRANSPORT_STATE_PLAYING )
2013-02-18 16:13:24 +01:00
return PA_AVAILABLE_YES ;
2012-08-31 12:51:02 +02:00
else
2013-02-18 16:13:24 +01:00
return PA_AVAILABLE_UNKNOWN ;
2012-08-31 12:51:02 +02:00
}
2013-08-15 16:17:26 -03:00
static pa_direction_t get_profile_direction ( pa_bluez4_profile_t p ) {
2013-02-18 09:10:35 +01:00
static const pa_direction_t profile_direction [ ] = {
2013-08-15 16:17:26 -03:00
[ PA_BLUEZ4_PROFILE_A2DP ] = PA_DIRECTION_OUTPUT ,
[ PA_BLUEZ4_PROFILE_A2DP_SOURCE ] = PA_DIRECTION_INPUT ,
[ PA_BLUEZ4_PROFILE_HSP ] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT ,
[ PA_BLUEZ4_PROFILE_HFGW ] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT ,
[ PA_BLUEZ4_PROFILE_OFF ] = 0
2013-02-18 09:10:35 +01:00
} ;
return profile_direction [ p ] ;
}
/* Run from main thread */
static pa_available_t get_port_availability ( struct userdata * u , pa_direction_t direction ) {
pa_available_t result = PA_AVAILABLE_NO ;
unsigned i ;
pa_assert ( u ) ;
pa_assert ( u - > device ) ;
2013-08-13 01:53:54 -03:00
for ( i = 0 ; i < PA_BLUEZ4_PROFILE_COUNT ; i + + ) {
pa_bluez4_transport * transport ;
2013-02-18 09:10:35 +01:00
if ( ! ( get_profile_direction ( i ) & direction ) )
continue ;
if ( ! ( transport = u - > device - > transports [ i ] ) )
continue ;
switch ( transport - > state ) {
2013-08-13 01:53:54 -03:00
case PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED :
2013-02-18 09:10:35 +01:00
continue ;
2013-08-13 01:53:54 -03:00
case PA_BLUEZ4_TRANSPORT_STATE_IDLE :
2013-02-18 09:10:35 +01:00
if ( result = = PA_AVAILABLE_NO )
result = PA_AVAILABLE_UNKNOWN ;
break ;
2013-08-13 01:53:54 -03:00
case PA_BLUEZ4_TRANSPORT_STATE_PLAYING :
2013-02-18 09:10:35 +01:00
return PA_AVAILABLE_YES ;
}
}
return result ;
2012-11-26 18:32:07 +01:00
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2013-08-13 01:53:54 -03:00
static void handle_transport_state_change ( struct userdata * u , struct pa_bluez4_transport * transport ) {
2013-01-11 22:11:04 +02:00
bool acquire = false ;
bool release = false ;
2013-08-15 16:17:26 -03:00
pa_bluez4_profile_t profile ;
2013-02-18 09:10:33 +01:00
pa_card_profile * cp ;
2013-08-13 01:53:54 -03:00
pa_bluez4_transport_state_t state ;
2013-02-18 09:10:35 +01:00
pa_device_port * port ;
2009-02-02 01:58:48 +01:00
2012-12-14 15:14:32 +01:00
pa_assert ( u ) ;
2012-12-14 15:14:33 +01:00
pa_assert ( transport ) ;
profile = transport - > profile ;
state = transport - > state ;
2009-01-19 14:53:35 +02:00
2013-02-18 09:10:33 +01:00
/* Update profile availability */
2013-08-13 01:53:54 -03:00
if ( ! ( cp = pa_hashmap_get ( u - > card - > profiles , pa_bluez4_profile_to_string ( profile ) ) ) )
2012-12-14 15:14:32 +01:00
return ;
2011-10-04 09:37:25 +02:00
2013-02-18 09:10:33 +01:00
pa_card_profile_set_available ( cp , transport_state_to_availability ( state ) ) ;
/* Update port availability */
2013-03-07 11:32:23 +01:00
pa_assert_se ( port = pa_hashmap_get ( u - > card - > ports , u - > output_port_name ) ) ;
2013-02-18 09:10:35 +01:00
pa_device_port_set_available ( port , get_port_availability ( u , PA_DIRECTION_OUTPUT ) ) ;
2012-08-31 12:51:06 +02:00
2013-03-07 11:32:23 +01:00
pa_assert_se ( port = pa_hashmap_get ( u - > card - > ports , u - > input_port_name ) ) ;
2013-02-18 09:10:35 +01:00
pa_device_port_set_available ( port , get_port_availability ( u , PA_DIRECTION_INPUT ) ) ;
2009-01-19 14:53:35 +02:00
2013-02-18 09:10:33 +01:00
/* Acquire or release transport as needed */
2013-08-13 01:53:54 -03:00
acquire = ( state = = PA_BLUEZ4_TRANSPORT_STATE_PLAYING & & u - > profile = = profile ) ;
release = ( state ! = PA_BLUEZ4_TRANSPORT_STATE_PLAYING & & u - > profile = = profile ) ;
2013-02-18 09:10:35 +01:00
2012-08-31 12:51:06 +02:00
if ( acquire )
2012-12-14 15:14:40 +01:00
if ( bt_transport_acquire ( u , true ) > = 0 ) {
2012-11-16 18:24:34 +02:00
if ( u - > source ) {
pa_log_debug ( " Resuming source %s, because the bluetooth audio state changed to 'playing'. " , u - > source - > name ) ;
2013-01-11 11:07:46 +01:00
pa_source_suspend ( u - > source , false , PA_SUSPEND_IDLE | PA_SUSPEND_USER ) ;
2012-11-16 18:24:34 +02:00
}
2012-08-31 12:51:06 +02:00
2012-11-16 18:24:34 +02:00
if ( u - > sink ) {
pa_log_debug ( " Resuming sink %s, because the bluetooth audio state changed to 'playing'. " , u - > sink - > name ) ;
2013-01-11 11:07:46 +01:00
pa_sink_suspend ( u - > sink , false , PA_SUSPEND_IDLE | PA_SUSPEND_USER ) ;
2012-11-16 18:24:34 +02:00
}
2012-08-31 12:51:06 +02:00
}
2012-12-14 15:14:35 +01:00
if ( release & & u - > transport_acquired ) {
2012-08-31 12:51:07 +02:00
/* FIXME: this release is racy, since the audio stream might have
been set up again in the meantime ( but not processed yet by PA ) .
BlueZ should probably release the transport automatically , and
in that case we would just mark the transport as released */
/* Remote side closed the stream so we consider it PA_SUSPEND_USER */
2012-11-16 18:24:34 +02:00
if ( u - > source ) {
pa_log_debug ( " Suspending source %s, because the remote end closed the stream. " , u - > source - > name ) ;
2013-01-11 11:07:46 +01:00
pa_source_suspend ( u - > source , true , PA_SUSPEND_USER ) ;
2012-11-16 18:24:34 +02:00
}
2012-08-31 12:51:07 +02:00
2012-11-16 18:24:34 +02:00
if ( u - > sink ) {
pa_log_debug ( " Suspending sink %s, because the remote end closed the stream. " , u - > sink - > name ) ;
2013-01-11 11:07:46 +01:00
pa_sink_suspend ( u - > sink , true , PA_SUSPEND_USER ) ;
2012-11-16 18:24:34 +02:00
}
2012-08-31 12:51:07 +02:00
}
2012-12-14 15:14:32 +01:00
}
2012-08-31 12:51:07 +02:00
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-03-21 02:54:18 +01:00
static void sink_set_volume_cb ( pa_sink * s ) {
2012-12-14 15:14:38 +01:00
uint16_t gain ;
2011-04-04 15:24:17 +03:00
pa_volume_t volume ;
2011-03-28 15:35:16 +03:00
struct userdata * u ;
char * k ;
2009-01-19 14:53:35 +02:00
2011-03-28 15:35:16 +03:00
pa_assert ( s ) ;
pa_assert ( s - > core ) ;
2009-01-19 14:53:35 +02:00
2011-03-28 15:35:16 +03:00
k = pa_sprintf_malloc ( " bluetooth-device@%p " , ( void * ) s ) ;
u = pa_shared_get ( s - > core , k ) ;
pa_xfree ( k ) ;
pa_assert ( u ) ;
pa_assert ( u - > sink = = s ) ;
2013-08-15 16:17:26 -03:00
pa_assert ( u - > profile = = PA_BLUEZ4_PROFILE_HSP ) ;
2012-12-14 15:14:30 +01:00
pa_assert ( u - > transport ) ;
2009-01-19 14:53:35 +02:00
2012-12-06 10:35:17 +01:00
gain = ( dbus_uint16_t ) round ( ( double ) pa_cvolume_max ( & s - > real_volume ) * HSP_MAX_GAIN / PA_VOLUME_NORM ) ;
volume = ( pa_volume_t ) round ( ( double ) gain * PA_VOLUME_NORM / HSP_MAX_GAIN ) ;
2011-04-04 15:24:17 +03:00
pa_cvolume_set ( & s - > real_volume , u - > sample_spec . channels , volume ) ;
2009-01-19 14:53:35 +02:00
2013-08-13 01:53:54 -03:00
pa_bluez4_transport_set_speaker_gain ( u - > transport , gain ) ;
2009-03-21 02:54:18 +01:00
}
2009-01-19 14:53:35 +02:00
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-03-21 02:54:18 +01:00
static void source_set_volume_cb ( pa_source * s ) {
2012-12-14 15:14:38 +01:00
uint16_t gain ;
2011-04-04 15:24:17 +03:00
pa_volume_t volume ;
2011-03-28 15:35:16 +03:00
struct userdata * u ;
char * k ;
2009-01-19 14:53:35 +02:00
2011-03-28 15:35:16 +03:00
pa_assert ( s ) ;
pa_assert ( s - > core ) ;
2009-01-19 14:53:35 +02:00
2011-03-28 15:35:16 +03:00
k = pa_sprintf_malloc ( " bluetooth-device@%p " , ( void * ) s ) ;
u = pa_shared_get ( s - > core , k ) ;
pa_xfree ( k ) ;
pa_assert ( u ) ;
pa_assert ( u - > source = = s ) ;
2013-08-15 16:17:26 -03:00
pa_assert ( u - > profile = = PA_BLUEZ4_PROFILE_HSP ) ;
2012-12-14 15:14:30 +01:00
pa_assert ( u - > transport ) ;
2009-01-19 14:53:35 +02:00
2012-12-06 10:35:17 +01:00
gain = ( dbus_uint16_t ) round ( ( double ) pa_cvolume_max ( & s - > real_volume ) * HSP_MAX_GAIN / PA_VOLUME_NORM ) ;
volume = ( pa_volume_t ) round ( ( double ) gain * PA_VOLUME_NORM / HSP_MAX_GAIN ) ;
2009-01-19 14:53:35 +02:00
2011-05-17 22:31:10 +01:00
pa_cvolume_set ( & s - > real_volume , u - > sample_spec . channels , volume ) ;
2009-01-19 14:53:35 +02:00
2013-08-13 01:53:54 -03:00
pa_bluez4_transport_set_microphone_gain ( u - > transport , gain ) ;
2009-03-21 02:54:18 +01:00
}
2009-01-19 14:53:35 +02:00
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2013-01-11 22:11:04 +02:00
static char * get_name ( const char * type , pa_modargs * ma , const char * device_id , bool * namereg_fail ) {
2009-01-29 16:27:27 +01:00
char * t ;
const char * n ;
2008-07-31 20:16:43 -03:00
2009-01-29 16:27:27 +01:00
pa_assert ( type ) ;
pa_assert ( ma ) ;
pa_assert ( device_id ) ;
pa_assert ( namereg_fail ) ;
2009-01-19 14:53:35 +02:00
2009-01-29 16:27:27 +01:00
t = pa_sprintf_malloc ( " %s_name " , type ) ;
n = pa_modargs_get_value ( ma , t , NULL ) ;
pa_xfree ( t ) ;
2008-07-31 20:16:43 -03:00
2009-01-29 16:27:27 +01:00
if ( n ) {
2013-01-11 11:07:46 +01:00
* namereg_fail = true ;
2009-01-29 16:27:27 +01:00
return pa_xstrdup ( n ) ;
2008-07-31 20:16:43 -03:00
}
2009-01-29 16:27:27 +01:00
if ( ( n = pa_modargs_get_value ( ma , " name " , NULL ) ) )
2013-01-11 11:07:46 +01:00
* namereg_fail = true ;
2009-01-29 16:27:27 +01:00
else {
n = device_id ;
2013-01-11 11:07:46 +01:00
* namereg_fail = false ;
2008-07-31 20:16:43 -03:00
}
2009-01-29 16:27:27 +01:00
return pa_sprintf_malloc ( " bluez_%s.%s " , type , n ) ;
}
2013-01-11 22:11:04 +02:00
static int sco_over_pcm_state_update ( struct userdata * u , bool changed ) {
2009-02-03 18:17:20 +02:00
pa_assert ( u ) ;
2009-02-12 03:56:01 +01:00
pa_assert ( USE_SCO_OVER_PCM ( u ) ) ;
2009-02-03 18:17:20 +02:00
if ( PA_SINK_IS_OPENED ( pa_sink_get_state ( u - > hsp . sco_sink ) ) | |
PA_SOURCE_IS_OPENED ( pa_source_get_state ( u - > hsp . sco_source ) ) ) {
2012-07-06 11:19:52 +02:00
if ( u - > stream_fd > = 0 )
2011-03-28 15:35:15 +03:00
return 0 ;
2009-02-03 18:17:20 +02:00
pa_log_debug ( " Resuming SCO over PCM " ) ;
2011-03-28 15:35:15 +03:00
if ( init_profile ( u ) < 0 ) {
2009-02-03 18:17:20 +02:00
pa_log ( " Can't resume SCO over PCM " ) ;
2011-03-28 15:35:15 +03:00
return - 1 ;
}
2009-02-03 18:17:20 +02:00
2012-12-14 15:14:40 +01:00
if ( bt_transport_acquire ( u , false ) < 0 )
2012-12-14 15:14:39 +01:00
return - 1 ;
setup_stream ( u ) ;
return 0 ;
2011-05-15 15:05:44 +01:00
}
if ( changed ) {
2012-07-06 11:19:52 +02:00
if ( u - > stream_fd < 0 )
2011-03-28 15:35:15 +03:00
return 0 ;
2009-02-03 18:17:20 +02:00
2011-03-28 15:35:15 +03:00
pa_log_debug ( " Closing SCO over PCM " ) ;
2009-02-03 18:17:20 +02:00
2012-07-06 11:19:52 +02:00
bt_transport_release ( u ) ;
2009-02-03 18:17:20 +02:00
}
2011-05-15 15:05:44 +01:00
return 0 ;
2009-02-03 18:17:20 +02:00
}
static pa_hook_result_t sink_state_changed_cb ( pa_core * c , pa_sink * s , struct userdata * u ) {
pa_assert ( c ) ;
pa_sink_assert_ref ( s ) ;
pa_assert ( u ) ;
2012-12-11 14:40:24 +01:00
if ( ! USE_SCO_OVER_PCM ( u ) | | s ! = u - > hsp . sco_sink )
2009-02-03 18:17:20 +02:00
return PA_HOOK_OK ;
2013-01-11 11:07:46 +01:00
sco_over_pcm_state_update ( u , true ) ;
2009-02-03 18:17:20 +02:00
return PA_HOOK_OK ;
}
static pa_hook_result_t source_state_changed_cb ( pa_core * c , pa_source * s , struct userdata * u ) {
pa_assert ( c ) ;
pa_source_assert_ref ( s ) ;
pa_assert ( u ) ;
2012-12-11 14:40:24 +01:00
if ( ! USE_SCO_OVER_PCM ( u ) | | s ! = u - > hsp . sco_source )
2009-02-03 18:17:20 +02:00
return PA_HOOK_OK ;
2013-01-11 11:07:46 +01:00
sco_over_pcm_state_update ( u , true ) ;
2009-02-03 18:17:20 +02:00
return PA_HOOK_OK ;
}
2013-08-13 01:53:54 -03:00
static pa_hook_result_t transport_nrec_changed_cb ( pa_bluez4_discovery * y , pa_bluez4_transport * t , struct userdata * u ) {
2012-06-12 15:49:50 +02:00
pa_proplist * p ;
pa_assert ( t ) ;
pa_assert ( u ) ;
2012-12-10 08:30:41 +01:00
if ( t ! = u - > transport )
return PA_HOOK_OK ;
2012-06-12 15:49:50 +02:00
p = pa_proplist_new ( ) ;
pa_proplist_sets ( p , " bluetooth.nrec " , t - > nrec ? " 1 " : " 0 " ) ;
pa_source_update_proplist ( u - > source , PA_UPDATE_REPLACE , p ) ;
pa_proplist_free ( p ) ;
return PA_HOOK_OK ;
}
2013-08-13 01:53:54 -03:00
static pa_hook_result_t transport_microphone_gain_changed_cb ( pa_bluez4_discovery * y , pa_bluez4_transport * t ,
2012-12-14 15:14:30 +01:00
struct userdata * u ) {
pa_cvolume v ;
pa_assert ( t ) ;
pa_assert ( u ) ;
if ( t ! = u - > transport )
return PA_HOOK_OK ;
pa_assert ( u - > source ) ;
pa_cvolume_set ( & v , u - > sample_spec . channels ,
( pa_volume_t ) round ( ( double ) t - > microphone_gain * PA_VOLUME_NORM / HSP_MAX_GAIN ) ) ;
pa_source_volume_changed ( u - > source , & v ) ;
return PA_HOOK_OK ;
}
2013-08-13 01:53:54 -03:00
static pa_hook_result_t transport_speaker_gain_changed_cb ( pa_bluez4_discovery * y , pa_bluez4_transport * t ,
2012-12-14 15:14:30 +01:00
struct userdata * u ) {
pa_cvolume v ;
pa_assert ( t ) ;
pa_assert ( u ) ;
if ( t ! = u - > transport )
return PA_HOOK_OK ;
pa_assert ( u - > sink ) ;
pa_cvolume_set ( & v , u - > sample_spec . channels , ( pa_volume_t ) round ( ( double ) t - > speaker_gain * PA_VOLUME_NORM / HSP_MAX_GAIN ) ) ;
pa_sink_volume_changed ( u - > sink , & v ) ;
return PA_HOOK_OK ;
}
2012-06-04 19:35:41 +02:00
static void connect_ports ( struct userdata * u , void * sink_or_source_new_data , pa_direction_t direction ) {
pa_device_port * port ;
2013-02-18 09:10:35 +01:00
if ( direction = = PA_DIRECTION_OUTPUT ) {
pa_sink_new_data * sink_new_data = sink_or_source_new_data ;
2012-06-04 19:35:41 +02:00
2013-03-07 11:32:23 +01:00
pa_assert_se ( port = pa_hashmap_get ( u - > card - > ports , u - > output_port_name ) ) ;
2013-02-18 09:10:35 +01:00
pa_assert_se ( pa_hashmap_put ( sink_new_data - > ports , port - > name , port ) > = 0 ) ;
pa_device_port_ref ( port ) ;
} else {
pa_source_new_data * source_new_data = sink_or_source_new_data ;
2012-06-04 19:35:41 +02:00
2013-03-07 11:32:23 +01:00
pa_assert_se ( port = pa_hashmap_get ( u - > card - > ports , u - > input_port_name ) ) ;
2013-02-18 09:10:35 +01:00
pa_assert_se ( pa_hashmap_put ( source_new_data - > ports , port - > name , port ) > = 0 ) ;
pa_device_port_ref ( port ) ;
2012-06-08 21:49:09 +03:00
}
2012-06-04 19:35:41 +02:00
}
2012-08-31 12:51:01 +02:00
static int sink_set_port_cb ( pa_sink * s , pa_device_port * p ) {
return 0 ;
}
static int source_set_port_cb ( pa_source * s , pa_device_port * p ) {
return 0 ;
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-01-29 16:27:27 +01:00
static int add_sink ( struct userdata * u ) {
2011-03-31 15:00:52 +03:00
char * k ;
2012-10-19 10:11:28 +02:00
pa_assert ( u - > transport ) ;
2009-02-03 17:15:41 +02:00
if ( USE_SCO_OVER_PCM ( u ) ) {
pa_proplist * p ;
2009-01-29 16:27:27 +01:00
2009-02-03 17:15:41 +02:00
u - > sink = u - > hsp . sco_sink ;
p = pa_proplist_new ( ) ;
2013-08-13 01:53:54 -03:00
pa_proplist_sets ( p , " bluetooth.protocol " , pa_bluez4_profile_to_string ( u - > profile ) ) ;
2009-02-03 17:15:41 +02:00
pa_proplist_update ( u - > sink - > proplist , PA_UPDATE_MERGE , p ) ;
pa_proplist_free ( p ) ;
2011-03-28 15:35:14 +03:00
} else {
2009-02-03 17:15:41 +02:00
pa_sink_new_data data ;
2013-01-11 22:11:04 +02:00
bool b ;
2009-02-03 17:15:41 +02:00
pa_sink_new_data_init ( & data ) ;
data . driver = __FILE__ ;
data . module = u - > module ;
pa_sink_new_data_set_sample_spec ( & data , & u - > sample_spec ) ;
2013-08-13 01:53:54 -03:00
pa_proplist_sets ( data . proplist , " bluetooth.protocol " , pa_bluez4_profile_to_string ( u - > profile ) ) ;
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_HSP )
2009-06-08 16:58:45 +02:00
pa_proplist_sets ( data . proplist , PA_PROP_DEVICE_INTENDED_ROLES , " phone " ) ;
2009-02-03 17:15:41 +02:00
data . card = u - > card ;
2009-03-21 01:19:49 +01:00
data . name = get_name ( " sink " , u - > modargs , u - > address , & b ) ;
2009-02-03 17:15:41 +02:00
data . namereg_fail = b ;
2009-05-28 02:39:22 +02:00
if ( pa_modargs_get_proplist ( u - > modargs , " sink_properties " , data . proplist , PA_UPDATE_REPLACE ) < 0 ) {
pa_log ( " Invalid properties " ) ;
pa_sink_new_data_done ( & data ) ;
return - 1 ;
}
2012-06-04 19:35:41 +02:00
connect_ports ( u , & data , PA_DIRECTION_OUTPUT ) ;
2009-05-28 02:39:22 +02:00
2012-12-14 15:14:35 +01:00
if ( ! u - > transport_acquired )
2012-08-31 12:51:12 +02:00
switch ( u - > profile ) {
2013-08-15 16:17:26 -03:00
case PA_BLUEZ4_PROFILE_A2DP :
case PA_BLUEZ4_PROFILE_HSP :
2012-12-03 11:03:59 +01:00
pa_assert_not_reached ( ) ; /* Profile switch should have failed */
2012-08-31 12:51:12 +02:00
break ;
2013-08-15 16:17:26 -03:00
case PA_BLUEZ4_PROFILE_HFGW :
2012-08-31 12:51:12 +02:00
data . suspend_cause = PA_SUSPEND_USER ;
break ;
2013-08-15 16:17:26 -03:00
case PA_BLUEZ4_PROFILE_A2DP_SOURCE :
case PA_BLUEZ4_PROFILE_OFF :
2012-08-31 12:51:12 +02:00
pa_assert_not_reached ( ) ;
}
2012-08-31 12:51:04 +02:00
2011-07-05 23:44:06 +01:00
u - > sink = pa_sink_new ( u - > core , & data , PA_SINK_HARDWARE | PA_SINK_LATENCY ) ;
2009-02-03 17:15:41 +02:00
pa_sink_new_data_done ( & data ) ;
if ( ! u - > sink ) {
pa_log_error ( " Failed to create sink " ) ;
return - 1 ;
}
u - > sink - > userdata = u ;
u - > sink - > parent . process_msg = sink_process_msg ;
2012-08-31 12:51:01 +02:00
u - > sink - > set_port = sink_set_port_cb ;
2008-08-11 13:27:13 -03:00
}
2008-09-29 21:40:52 +02:00
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_HSP ) {
2011-07-17 15:29:29 +01:00
pa_sink_set_set_volume_callback ( u - > sink , sink_set_volume_cb ) ;
2009-03-21 02:54:18 +01:00
u - > sink - > n_volume_steps = 16 ;
2011-03-31 15:00:52 +03:00
k = pa_sprintf_malloc ( " bluetooth-device@%p " , ( void * ) u - > sink ) ;
pa_shared_set ( u - > core , k , u ) ;
pa_xfree ( k ) ;
2009-03-21 02:54:18 +01:00
}
2009-01-29 16:27:27 +01:00
return 0 ;
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-01-29 16:27:27 +01:00
static int add_source ( struct userdata * u ) {
2011-03-31 15:00:52 +03:00
char * k ;
2012-10-19 10:11:28 +02:00
pa_assert ( u - > transport ) ;
2009-02-03 17:15:41 +02:00
if ( USE_SCO_OVER_PCM ( u ) ) {
u - > source = u - > hsp . sco_source ;
2013-08-13 01:53:54 -03:00
pa_proplist_sets ( u - > source - > proplist , " bluetooth.protocol " , pa_bluez4_profile_to_string ( u - > profile ) ) ;
2011-03-28 15:35:14 +03:00
} else {
2009-02-03 17:15:41 +02:00
pa_source_new_data data ;
2013-01-11 22:11:04 +02:00
bool b ;
2009-02-03 17:15:41 +02:00
pa_source_new_data_init ( & data ) ;
data . driver = __FILE__ ;
data . module = u - > module ;
pa_source_new_data_set_sample_spec ( & data , & u - > sample_spec ) ;
2013-08-13 01:53:54 -03:00
pa_proplist_sets ( data . proplist , " bluetooth.protocol " , pa_bluez4_profile_to_string ( u - > profile ) ) ;
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_HSP )
2009-06-08 16:58:45 +02:00
pa_proplist_sets ( data . proplist , PA_PROP_DEVICE_INTENDED_ROLES , " phone " ) ;
2011-01-14 14:18:08 +02:00
2009-02-03 17:15:41 +02:00
data . card = u - > card ;
2009-03-21 01:19:49 +01:00
data . name = get_name ( " source " , u - > modargs , u - > address , & b ) ;
2009-02-03 17:15:41 +02:00
data . namereg_fail = b ;
2009-05-28 02:39:22 +02:00
if ( pa_modargs_get_proplist ( u - > modargs , " source_properties " , data . proplist , PA_UPDATE_REPLACE ) < 0 ) {
pa_log ( " Invalid properties " ) ;
pa_source_new_data_done ( & data ) ;
return - 1 ;
}
2012-06-04 19:35:41 +02:00
connect_ports ( u , & data , PA_DIRECTION_INPUT ) ;
2012-08-31 12:51:04 +02:00
2012-12-14 15:14:35 +01:00
if ( ! u - > transport_acquired )
2012-08-31 12:51:12 +02:00
switch ( u - > profile ) {
2013-08-15 16:17:26 -03:00
case PA_BLUEZ4_PROFILE_HSP :
2012-12-03 11:03:59 +01:00
pa_assert_not_reached ( ) ; /* Profile switch should have failed */
2012-08-31 12:51:12 +02:00
break ;
2013-08-15 16:17:26 -03:00
case PA_BLUEZ4_PROFILE_A2DP_SOURCE :
case PA_BLUEZ4_PROFILE_HFGW :
2012-08-31 12:51:12 +02:00
data . suspend_cause = PA_SUSPEND_USER ;
break ;
2013-08-15 16:17:26 -03:00
case PA_BLUEZ4_PROFILE_A2DP :
case PA_BLUEZ4_PROFILE_OFF :
2012-08-31 12:51:12 +02:00
pa_assert_not_reached ( ) ;
}
2012-08-31 12:51:04 +02:00
2011-07-05 23:44:06 +01:00
u - > source = pa_source_new ( u - > core , & data , PA_SOURCE_HARDWARE | PA_SOURCE_LATENCY ) ;
2009-02-03 17:15:41 +02:00
pa_source_new_data_done ( & data ) ;
if ( ! u - > source ) {
pa_log_error ( " Failed to create source " ) ;
return - 1 ;
}
u - > source - > userdata = u ;
u - > source - > parent . process_msg = source_process_msg ;
2012-08-31 12:51:01 +02:00
u - > source - > set_port = source_set_port_cb ;
2008-08-11 13:27:13 -03:00
}
2009-01-29 16:27:27 +01:00
2013-08-15 16:17:26 -03:00
if ( ( u - > profile = = PA_BLUEZ4_PROFILE_HSP ) | | ( u - > profile = = PA_BLUEZ4_PROFILE_HFGW ) ) {
2013-08-13 01:53:54 -03:00
pa_bluez4_transport * t = u - > transport ;
2012-07-06 11:19:52 +02:00
pa_proplist_sets ( u - > source - > proplist , " bluetooth.nrec " , t - > nrec ? " 1 " : " 0 " ) ;
2011-01-14 14:18:08 +02:00
}
2010-01-29 11:01:31 -02:00
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_HSP ) {
2011-07-17 15:29:29 +01:00
pa_source_set_set_volume_callback ( u - > source , source_set_volume_cb ) ;
2009-03-21 02:54:18 +01:00
u - > source - > n_volume_steps = 16 ;
2011-03-31 15:00:52 +03:00
k = pa_sprintf_malloc ( " bluetooth-device@%p " , ( void * ) u - > source ) ;
pa_shared_set ( u - > core , k , u ) ;
pa_xfree ( k ) ;
2009-03-21 02:54:18 +01:00
}
2009-01-29 16:27:27 +01:00
return 0 ;
}
2012-08-31 12:50:53 +02:00
static void bt_transport_config_a2dp ( struct userdata * u ) {
2013-08-13 01:53:54 -03:00
const pa_bluez4_transport * t ;
2010-10-07 17:22:41 +03:00
struct a2dp_info * a2dp = & u - > a2dp ;
2011-03-31 00:56:20 +05:30
a2dp_sbc_t * config ;
2010-10-07 17:22:41 +03:00
2012-10-19 10:11:28 +02:00
t = u - > transport ;
2010-10-07 17:22:41 +03:00
pa_assert ( t ) ;
2011-03-31 00:56:20 +05:30
config = ( a2dp_sbc_t * ) t - > config ;
2010-10-07 17:22:41 +03:00
2011-04-29 17:48:05 +03:00
u - > sample_spec . format = PA_SAMPLE_S16LE ;
2010-10-07 17:22:41 +03:00
if ( a2dp - > sbc_initialized )
sbc_reinit ( & a2dp - > sbc , 0 ) ;
else
sbc_init ( & a2dp - > sbc , 0 ) ;
2013-01-11 11:07:46 +01:00
a2dp - > sbc_initialized = true ;
2010-10-07 17:22:41 +03:00
switch ( config - > frequency ) {
2012-08-01 14:18:20 +09:00
case SBC_SAMPLING_FREQ_16000 :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . frequency = SBC_FREQ_16000 ;
2011-04-29 17:48:05 +03:00
u - > sample_spec . rate = 16000U ;
2010-10-07 17:22:41 +03:00
break ;
2012-08-01 14:18:20 +09:00
case SBC_SAMPLING_FREQ_32000 :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . frequency = SBC_FREQ_32000 ;
2011-04-29 17:48:05 +03:00
u - > sample_spec . rate = 32000U ;
2010-10-07 17:22:41 +03:00
break ;
2012-08-01 14:18:20 +09:00
case SBC_SAMPLING_FREQ_44100 :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . frequency = SBC_FREQ_44100 ;
2011-04-29 17:48:05 +03:00
u - > sample_spec . rate = 44100U ;
2010-10-07 17:22:41 +03:00
break ;
2012-08-01 14:18:20 +09:00
case SBC_SAMPLING_FREQ_48000 :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . frequency = SBC_FREQ_48000 ;
2011-04-29 17:48:05 +03:00
u - > sample_spec . rate = 48000U ;
2010-10-07 17:22:41 +03:00
break ;
default :
pa_assert_not_reached ( ) ;
}
switch ( config - > channel_mode ) {
2012-08-01 14:18:20 +09:00
case SBC_CHANNEL_MODE_MONO :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . mode = SBC_MODE_MONO ;
2011-04-29 17:48:05 +03:00
u - > sample_spec . channels = 1 ;
2010-10-07 17:22:41 +03:00
break ;
2012-08-01 14:18:20 +09:00
case SBC_CHANNEL_MODE_DUAL_CHANNEL :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . mode = SBC_MODE_DUAL_CHANNEL ;
2011-04-29 17:48:05 +03:00
u - > sample_spec . channels = 2 ;
2010-10-07 17:22:41 +03:00
break ;
2012-08-01 14:18:20 +09:00
case SBC_CHANNEL_MODE_STEREO :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . mode = SBC_MODE_STEREO ;
2011-04-29 17:48:05 +03:00
u - > sample_spec . channels = 2 ;
2010-10-07 17:22:41 +03:00
break ;
2012-08-01 14:18:20 +09:00
case SBC_CHANNEL_MODE_JOINT_STEREO :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . mode = SBC_MODE_JOINT_STEREO ;
2011-04-29 17:48:05 +03:00
u - > sample_spec . channels = 2 ;
2010-10-07 17:22:41 +03:00
break ;
default :
pa_assert_not_reached ( ) ;
}
switch ( config - > allocation_method ) {
2012-08-01 14:18:20 +09:00
case SBC_ALLOCATION_SNR :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . allocation = SBC_AM_SNR ;
break ;
2012-08-01 14:18:20 +09:00
case SBC_ALLOCATION_LOUDNESS :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . allocation = SBC_AM_LOUDNESS ;
break ;
default :
pa_assert_not_reached ( ) ;
}
switch ( config - > subbands ) {
2012-08-01 14:18:20 +09:00
case SBC_SUBBANDS_4 :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . subbands = SBC_SB_4 ;
break ;
2012-08-01 14:18:20 +09:00
case SBC_SUBBANDS_8 :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . subbands = SBC_SB_8 ;
break ;
default :
pa_assert_not_reached ( ) ;
}
switch ( config - > block_length ) {
2012-08-01 14:18:20 +09:00
case SBC_BLOCK_LENGTH_4 :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . blocks = SBC_BLK_4 ;
break ;
2012-08-01 14:18:20 +09:00
case SBC_BLOCK_LENGTH_8 :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . blocks = SBC_BLK_8 ;
break ;
2012-08-01 14:18:20 +09:00
case SBC_BLOCK_LENGTH_12 :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . blocks = SBC_BLK_12 ;
break ;
2012-08-01 14:18:20 +09:00
case SBC_BLOCK_LENGTH_16 :
2010-10-07 17:22:41 +03:00
a2dp - > sbc . blocks = SBC_BLK_16 ;
break ;
default :
pa_assert_not_reached ( ) ;
}
2010-12-23 13:13:44 +02:00
a2dp - > min_bitpool = config - > min_bitpool ;
a2dp - > max_bitpool = config - > max_bitpool ;
2010-12-23 15:24:39 +02:00
/* Set minimum bitpool for source to get the maximum possible block_size */
2013-08-15 16:17:26 -03:00
a2dp - > sbc . bitpool = u - > profile = = PA_BLUEZ4_PROFILE_A2DP ? a2dp - > max_bitpool : a2dp - > min_bitpool ;
2010-10-07 17:22:41 +03:00
a2dp - > codesize = sbc_get_codesize ( & a2dp - > sbc ) ;
a2dp - > frame_length = sbc_get_frame_length ( & a2dp - > sbc ) ;
pa_log_info ( " SBC parameters: \n \t allocation=%u \n \t subbands=%u \n \t blocks=%u \n \t bitpool=%u \n " ,
a2dp - > sbc . allocation , a2dp - > sbc . subbands , a2dp - > sbc . blocks , a2dp - > sbc . bitpool ) ;
}
2012-08-31 12:50:53 +02:00
static void bt_transport_config ( struct userdata * u ) {
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_HSP | | u - > profile = = PA_BLUEZ4_PROFILE_HFGW ) {
2011-04-29 17:48:05 +03:00
u - > sample_spec . format = PA_SAMPLE_S16LE ;
u - > sample_spec . channels = 1 ;
u - > sample_spec . rate = 8000 ;
2012-08-31 12:50:53 +02:00
} else
bt_transport_config_a2dp ( u ) ;
2010-10-07 17:22:41 +03:00
}
2012-10-19 10:11:26 +02:00
/* Run from main thread */
2013-08-13 01:53:54 -03:00
static pa_hook_result_t transport_state_changed_cb ( pa_bluez4_discovery * y , pa_bluez4_transport * t , struct userdata * u ) {
2012-10-19 10:11:26 +02:00
pa_assert ( t ) ;
pa_assert ( u ) ;
2013-08-13 01:53:54 -03:00
if ( t = = u - > transport & & t - > state = = PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED )
2013-11-20 15:42:26 +02:00
pa_assert_se ( pa_card_set_profile ( u - > card , pa_hashmap_get ( u - > card - > profiles , " off " ) , false ) > = 0 ) ;
2012-10-19 10:11:26 +02:00
2012-12-14 15:14:33 +01:00
if ( t - > device = = u - > device )
handle_transport_state_change ( u , t ) ;
2012-10-19 10:11:26 +02:00
return PA_HOOK_OK ;
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2012-09-28 17:45:27 +02:00
static int setup_transport ( struct userdata * u ) {
2013-08-13 01:53:54 -03:00
pa_bluez4_transport * t ;
2010-10-07 17:22:41 +03:00
2009-02-02 01:58:48 +01:00
pa_assert ( u ) ;
2012-10-19 10:11:28 +02:00
pa_assert ( ! u - > transport ) ;
2013-08-15 16:17:26 -03:00
pa_assert ( u - > profile ! = PA_BLUEZ4_PROFILE_OFF ) ;
2009-02-02 01:58:48 +01:00
2010-10-07 17:22:41 +03:00
/* check if profile has a transport */
2012-12-06 10:35:20 +01:00
t = u - > device - > transports [ u - > profile ] ;
2013-08-13 01:53:54 -03:00
if ( ! t | | t - > state = = PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED ) {
2012-07-06 11:19:52 +02:00
pa_log_warn ( " Profile has no transport " ) ;
2009-02-02 01:58:48 +01:00
return - 1 ;
2012-07-06 11:19:52 +02:00
}
2009-02-02 01:58:48 +01:00
2012-10-19 10:11:28 +02:00
u - > transport = t ;
2008-08-11 13:27:13 -03:00
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_A2DP_SOURCE | | u - > profile = = PA_BLUEZ4_PROFILE_HFGW )
2012-12-14 15:14:40 +01:00
bt_transport_acquire ( u , true ) ; /* In case of error, the sink/sources will be created suspended */
else if ( bt_transport_acquire ( u , false ) < 0 )
2012-12-03 11:03:59 +01:00
return - 1 ; /* We need to fail here until the interactions with module-suspend-on-idle and alike get improved */
2009-02-02 01:58:48 +01:00
2012-08-31 12:50:53 +02:00
bt_transport_config ( u ) ;
return 0 ;
2009-01-29 16:27:27 +01:00
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-02-02 01:58:48 +01:00
static int init_profile ( struct userdata * u ) {
int r = 0 ;
2009-01-29 16:27:27 +01:00
pa_assert ( u ) ;
2013-08-15 16:17:26 -03:00
pa_assert ( u - > profile ! = PA_BLUEZ4_PROFILE_OFF ) ;
2009-01-29 16:27:27 +01:00
2012-09-28 17:45:27 +02:00
if ( setup_transport ( u ) < 0 )
2009-01-29 16:27:27 +01:00
return - 1 ;
2009-02-02 01:58:48 +01:00
2012-10-19 10:11:28 +02:00
pa_assert ( u - > transport ) ;
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_A2DP | |
u - > profile = = PA_BLUEZ4_PROFILE_HSP | |
u - > profile = = PA_BLUEZ4_PROFILE_HFGW )
2009-02-02 01:58:48 +01:00
if ( add_sink ( u ) < 0 )
r = - 1 ;
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_HSP | |
u - > profile = = PA_BLUEZ4_PROFILE_A2DP_SOURCE | |
u - > profile = = PA_BLUEZ4_PROFILE_HFGW )
2009-02-02 01:58:48 +01:00
if ( add_source ( u ) < 0 )
r = - 1 ;
return r ;
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-02-02 01:58:48 +01:00
static void stop_thread ( struct userdata * u ) {
2011-03-31 15:00:52 +03:00
char * k ;
2009-02-02 01:58:48 +01:00
pa_assert ( u ) ;
2012-09-28 17:45:31 +02:00
if ( u - > sink & & ! USE_SCO_OVER_PCM ( u ) )
pa_sink_unlink ( u - > sink ) ;
if ( u - > source & & ! USE_SCO_OVER_PCM ( u ) )
pa_source_unlink ( u - > source ) ;
2009-02-02 01:58:48 +01:00
if ( u - > thread ) {
pa_asyncmsgq_send ( u - > thread_mq . inq , NULL , PA_MESSAGE_SHUTDOWN , NULL , 0 , NULL ) ;
pa_thread_free ( u - > thread ) ;
u - > thread = NULL ;
2008-08-11 13:27:13 -03:00
}
2009-02-02 01:58:48 +01:00
if ( u - > rtpoll_item ) {
pa_rtpoll_item_free ( u - > rtpoll_item ) ;
u - > rtpoll_item = NULL ;
}
2013-02-12 21:37:02 +02:00
if ( u - > rtpoll ) {
pa_thread_mq_done ( & u - > thread_mq ) ;
pa_rtpoll_free ( u - > rtpoll ) ;
u - > rtpoll = NULL ;
}
2012-09-28 17:45:30 +02:00
if ( u - > transport ) {
bt_transport_release ( u ) ;
u - > transport = NULL ;
}
2009-02-02 01:58:48 +01:00
if ( u - > sink ) {
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_HSP ) {
2011-03-31 15:00:52 +03:00
k = pa_sprintf_malloc ( " bluetooth-device@%p " , ( void * ) u - > sink ) ;
pa_shared_remove ( u - > core , k ) ;
pa_xfree ( k ) ;
}
2009-02-02 01:58:48 +01:00
pa_sink_unref ( u - > sink ) ;
u - > sink = NULL ;
}
if ( u - > source ) {
2013-08-15 16:17:26 -03:00
if ( u - > profile = = PA_BLUEZ4_PROFILE_HSP ) {
2011-03-31 15:00:52 +03:00
k = pa_sprintf_malloc ( " bluetooth-device@%p " , ( void * ) u - > source ) ;
pa_shared_remove ( u - > core , k ) ;
pa_xfree ( k ) ;
}
2009-02-02 01:58:48 +01:00
pa_source_unref ( u - > source ) ;
u - > source = NULL ;
}
2009-02-12 22:52:02 +01:00
2009-04-10 01:30:50 +02:00
if ( u - > read_smoother ) {
pa_smoother_free ( u - > read_smoother ) ;
u - > read_smoother = NULL ;
}
2009-02-02 01:58:48 +01:00
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-02-02 01:58:48 +01:00
static int start_thread ( struct userdata * u ) {
pa_assert ( u ) ;
pa_assert ( ! u - > thread ) ;
2009-02-12 22:52:02 +01:00
pa_assert ( ! u - > rtpoll ) ;
2009-02-02 01:58:48 +01:00
pa_assert ( ! u - > rtpoll_item ) ;
2009-03-06 14:45:06 +02:00
u - > rtpoll = pa_rtpoll_new ( ) ;
pa_thread_mq_init ( & u - > thread_mq , u - > core - > mainloop , u - > rtpoll ) ;
2009-02-03 17:15:41 +02:00
if ( USE_SCO_OVER_PCM ( u ) ) {
2013-01-11 11:07:46 +01:00
if ( sco_over_pcm_state_update ( u , false ) < 0 ) {
2011-03-31 15:00:52 +03:00
char * k ;
if ( u - > sink ) {
k = pa_sprintf_malloc ( " bluetooth-device@%p " , ( void * ) u - > sink ) ;
pa_shared_remove ( u - > core , k ) ;
pa_xfree ( k ) ;
u - > sink = NULL ;
}
if ( u - > source ) {
k = pa_sprintf_malloc ( " bluetooth-device@%p " , ( void * ) u - > source ) ;
pa_shared_remove ( u - > core , k ) ;
pa_xfree ( k ) ;
u - > source = NULL ;
}
2009-03-06 14:45:06 +02:00
return - 1 ;
2011-03-28 15:35:15 +03:00
}
2009-03-06 14:45:06 +02:00
2009-02-03 17:15:41 +02:00
pa_sink_ref ( u - > sink ) ;
pa_source_ref ( u - > source ) ;
2009-03-06 14:45:06 +02:00
/* FIXME: monitor stream_fd error */
2009-02-03 17:15:41 +02:00
return 0 ;
}
2010-05-03 13:28:15 +02:00
if ( ! ( u - > thread = pa_thread_new ( " bluetooth " , thread_func , u ) ) ) {
2009-02-02 01:58:48 +01:00
pa_log_error ( " Failed to create IO thread " ) ;
2009-01-29 16:27:27 +01:00
return - 1 ;
2008-08-11 13:27:13 -03:00
}
2009-02-02 01:58:48 +01:00
2009-02-13 00:14:48 +02:00
if ( u - > sink ) {
pa_sink_set_asyncmsgq ( u - > sink , u - > thread_mq . inq ) ;
pa_sink_set_rtpoll ( u - > sink , u - > rtpoll ) ;
2009-02-02 01:58:48 +01:00
pa_sink_put ( u - > sink ) ;
2009-03-21 02:54:18 +01:00
if ( u - > sink - > set_volume )
u - > sink - > set_volume ( u - > sink ) ;
2009-02-13 00:14:48 +02:00
}
2009-02-02 01:58:48 +01:00
2009-02-13 00:14:48 +02:00
if ( u - > source ) {
pa_source_set_asyncmsgq ( u - > source , u - > thread_mq . inq ) ;
pa_source_set_rtpoll ( u - > source , u - > rtpoll ) ;
2009-02-02 01:58:48 +01:00
pa_source_put ( u - > source ) ;
2009-03-21 02:54:18 +01:00
if ( u - > source - > set_volume )
u - > source - > set_volume ( u - > source ) ;
2009-02-13 00:14:48 +02:00
}
2009-02-02 01:58:48 +01:00
return 0 ;
2009-01-29 16:27:27 +01:00
}
2008-08-11 13:27:13 -03:00
2011-03-31 15:00:52 +03:00
static void save_sco_volume_callbacks ( struct userdata * u ) {
pa_assert ( u ) ;
pa_assert ( USE_SCO_OVER_PCM ( u ) ) ;
u - > hsp . sco_sink_set_volume = u - > hsp . sco_sink - > set_volume ;
u - > hsp . sco_source_set_volume = u - > hsp . sco_source - > set_volume ;
}
static void restore_sco_volume_callbacks ( struct userdata * u ) {
pa_assert ( u ) ;
pa_assert ( USE_SCO_OVER_PCM ( u ) ) ;
2011-07-17 15:29:29 +01:00
pa_sink_set_set_volume_callback ( u - > hsp . sco_sink , u - > hsp . sco_sink_set_volume ) ;
pa_source_set_set_volume_callback ( u - > hsp . sco_source , u - > hsp . sco_source_set_volume ) ;
2011-03-31 15:00:52 +03:00
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-01-29 16:27:27 +01:00
static int card_set_profile ( pa_card * c , pa_card_profile * new_profile ) {
struct userdata * u ;
2013-08-15 16:17:26 -03:00
pa_bluez4_profile_t * d ;
2009-01-29 16:27:27 +01:00
pa_assert ( c ) ;
pa_assert ( new_profile ) ;
pa_assert_se ( u = c - > userdata ) ;
d = PA_CARD_PROFILE_DATA ( new_profile ) ;
2013-08-15 16:17:26 -03:00
if ( * d ! = PA_BLUEZ4_PROFILE_OFF ) {
2013-08-13 01:53:54 -03:00
const pa_bluez4_device * device = u - > device ;
2012-09-28 17:45:29 +02:00
2013-08-13 01:53:54 -03:00
if ( ! device - > transports [ * d ] | | device - > transports [ * d ] - > state = = PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED ) {
2012-12-06 15:55:29 +01:00
pa_log_warn ( " Profile not connected, refused to switch profile to %s " , new_profile - > name ) ;
2012-09-28 17:45:29 +02:00
return - PA_ERR_IO ;
}
2010-01-29 11:01:31 -02:00
}
2009-03-24 14:38:52 +02:00
2009-02-02 01:58:48 +01:00
stop_thread ( u ) ;
2011-10-04 09:37:23 +02:00
2011-03-31 15:00:52 +03:00
if ( USE_SCO_OVER_PCM ( u ) )
restore_sco_volume_callbacks ( u ) ;
2009-02-02 01:58:48 +01:00
u - > profile = * d ;
2009-02-12 03:47:27 +01:00
u - > sample_spec = u - > requested_sample_spec ;
2009-02-10 23:48:37 +02:00
2011-03-31 15:00:52 +03:00
if ( USE_SCO_OVER_PCM ( u ) )
save_sco_volume_callbacks ( u ) ;
2013-08-15 16:17:26 -03:00
if ( u - > profile ! = PA_BLUEZ4_PROFILE_OFF )
2012-10-19 10:11:22 +02:00
if ( init_profile ( u ) < 0 )
goto off ;
2009-01-29 16:27:27 +01:00
2009-02-02 01:58:48 +01:00
if ( u - > sink | | u - > source )
2012-10-19 10:11:23 +02:00
if ( start_thread ( u ) < 0 )
goto off ;
2009-02-02 01:58:48 +01:00
2009-01-29 16:27:27 +01:00
return 0 ;
2012-10-19 10:11:22 +02:00
off :
stop_thread ( u ) ;
2013-11-20 15:42:26 +02:00
pa_assert_se ( pa_card_set_profile ( u - > card , pa_hashmap_get ( u - > card - > profiles , " off " ) , false ) > = 0 ) ;
2012-10-19 10:11:22 +02:00
return - PA_ERR_IO ;
2009-01-29 16:27:27 +01:00
}
2013-02-18 09:10:35 +01:00
/* Run from main thread */
static void create_card_ports ( struct userdata * u , pa_hashmap * ports ) {
2012-06-04 19:35:41 +02:00
pa_device_port * port ;
2013-03-28 12:05:11 +01:00
pa_device_port_new_data port_data ;
2013-03-07 11:32:23 +01:00
const char * name_prefix = NULL ;
const char * input_description = NULL ;
const char * output_description = NULL ;
2012-06-04 19:35:41 +02:00
2013-02-18 09:10:35 +01:00
pa_assert ( u ) ;
pa_assert ( ports ) ;
2013-03-07 11:32:23 +01:00
pa_assert ( u - > device ) ;
2013-08-13 01:53:54 -03:00
switch ( pa_bluez4_get_form_factor ( u - > device - > class ) ) {
case PA_BLUEZ4_FORM_FACTOR_UNKNOWN :
2013-03-07 11:32:23 +01:00
break ;
2013-08-13 01:53:54 -03:00
case PA_BLUEZ4_FORM_FACTOR_HEADSET :
2013-03-07 11:32:23 +01:00
name_prefix = " headset " ;
input_description = output_description = _ ( " Headset " ) ;
break ;
2013-08-13 01:53:54 -03:00
case PA_BLUEZ4_FORM_FACTOR_HANDSFREE :
2013-03-07 11:32:23 +01:00
name_prefix = " handsfree " ;
input_description = output_description = _ ( " Handsfree " ) ;
break ;
2013-08-13 01:53:54 -03:00
case PA_BLUEZ4_FORM_FACTOR_MICROPHONE :
2013-03-07 11:32:23 +01:00
name_prefix = " microphone " ;
2013-03-08 18:23:41 +01:00
input_description = _ ( " Microphone " ) ;
2013-03-07 11:32:23 +01:00
break ;
2013-08-13 01:53:54 -03:00
case PA_BLUEZ4_FORM_FACTOR_SPEAKER :
2013-03-07 11:32:23 +01:00
name_prefix = " speaker " ;
2013-03-08 18:23:41 +01:00
output_description = _ ( " Speaker " ) ;
2013-03-07 11:32:23 +01:00
break ;
2013-08-13 01:53:54 -03:00
case PA_BLUEZ4_FORM_FACTOR_HEADPHONE :
2013-03-07 11:32:23 +01:00
name_prefix = " headphone " ;
2013-03-08 18:23:41 +01:00
output_description = _ ( " Headphone " ) ;
2013-03-07 11:32:23 +01:00
break ;
2013-02-18 09:10:35 +01:00
2013-08-13 01:53:54 -03:00
case PA_BLUEZ4_FORM_FACTOR_PORTABLE :
2013-03-07 11:32:23 +01:00
name_prefix = " portable " ;
input_description = output_description = _ ( " Portable " ) ;
break ;
2013-08-13 01:53:54 -03:00
case PA_BLUEZ4_FORM_FACTOR_CAR :
2013-03-07 11:32:23 +01:00
name_prefix = " car " ;
input_description = output_description = _ ( " Car " ) ;
break ;
2013-08-13 01:53:54 -03:00
case PA_BLUEZ4_FORM_FACTOR_HIFI :
2013-03-07 11:32:23 +01:00
name_prefix = " hifi " ;
input_description = output_description = _ ( " HiFi " ) ;
break ;
2013-08-13 01:53:54 -03:00
case PA_BLUEZ4_FORM_FACTOR_PHONE :
2013-03-07 11:32:23 +01:00
name_prefix = " phone " ;
input_description = output_description = _ ( " Phone " ) ;
break ;
}
if ( ! name_prefix )
name_prefix = " unknown " ;
if ( ! output_description )
output_description = _ ( " Bluetooth Output " ) ;
if ( ! input_description )
input_description = _ ( " Bluetooth Input " ) ;
u - > output_port_name = pa_sprintf_malloc ( " %s-output " , name_prefix ) ;
u - > input_port_name = pa_sprintf_malloc ( " %s-input " , name_prefix ) ;
2013-03-28 12:05:11 +01:00
pa_device_port_new_data_init ( & port_data ) ;
pa_device_port_new_data_set_name ( & port_data , u - > output_port_name ) ;
pa_device_port_new_data_set_description ( & port_data , output_description ) ;
pa_device_port_new_data_set_direction ( & port_data , PA_DIRECTION_OUTPUT ) ;
pa_device_port_new_data_set_available ( & port_data , get_port_availability ( u , PA_DIRECTION_OUTPUT ) ) ;
pa_assert_se ( port = pa_device_port_new ( u - > core , & port_data , 0 ) ) ;
2013-02-18 09:10:35 +01:00
pa_assert_se ( pa_hashmap_put ( ports , port - > name , port ) > = 0 ) ;
2013-03-28 12:05:11 +01:00
pa_device_port_new_data_done ( & port_data ) ;
pa_device_port_new_data_init ( & port_data ) ;
pa_device_port_new_data_set_name ( & port_data , u - > input_port_name ) ;
2013-05-24 15:17:13 +03:00
pa_device_port_new_data_set_description ( & port_data , input_description ) ;
2013-03-28 12:05:11 +01:00
pa_device_port_new_data_set_direction ( & port_data , PA_DIRECTION_INPUT ) ;
pa_device_port_new_data_set_available ( & port_data , get_port_availability ( u , PA_DIRECTION_INPUT ) ) ;
pa_assert_se ( port = pa_device_port_new ( u - > core , & port_data , 0 ) ) ;
2013-02-18 09:10:35 +01:00
pa_assert_se ( pa_hashmap_put ( ports , port - > name , port ) > = 0 ) ;
2013-03-28 12:05:11 +01:00
pa_device_port_new_data_done ( & port_data ) ;
2012-06-04 19:35:41 +02:00
}
2012-10-22 10:46:41 +02:00
/* Run from main thread */
2013-05-20 11:48:33 +02:00
static pa_card_profile * create_card_profile ( struct userdata * u , const char * uuid , pa_hashmap * ports ) {
pa_device_port * input_port , * output_port ;
2012-10-22 10:46:41 +02:00
pa_card_profile * p = NULL ;
2013-08-15 16:17:26 -03:00
pa_bluez4_profile_t * d ;
2012-10-22 10:46:41 +02:00
2013-05-20 11:48:33 +02:00
pa_assert ( u - > input_port_name ) ;
pa_assert ( u - > output_port_name ) ;
pa_assert_se ( input_port = pa_hashmap_get ( ports , u - > input_port_name ) ) ;
pa_assert_se ( output_port = pa_hashmap_get ( ports , u - > output_port_name ) ) ;
2012-10-22 10:46:41 +02:00
if ( pa_streq ( uuid , A2DP_SINK_UUID ) ) {
2013-08-15 16:17:26 -03:00
p = pa_card_profile_new ( " a2dp " , _ ( " High Fidelity Playback (A2DP) " ) , sizeof ( pa_bluez4_profile_t ) ) ;
2012-10-22 10:46:41 +02:00
p - > priority = 10 ;
p - > n_sinks = 1 ;
p - > n_sources = 0 ;
p - > max_sink_channels = 2 ;
p - > max_source_channels = 0 ;
2013-05-20 11:48:33 +02:00
pa_hashmap_put ( output_port - > profiles , p - > name , p ) ;
2012-10-22 10:46:41 +02:00
d = PA_CARD_PROFILE_DATA ( p ) ;
2013-08-15 16:17:26 -03:00
* d = PA_BLUEZ4_PROFILE_A2DP ;
2012-10-22 10:46:41 +02:00
} else if ( pa_streq ( uuid , A2DP_SOURCE_UUID ) ) {
2013-08-15 16:17:26 -03:00
p = pa_card_profile_new ( " a2dp_source " , _ ( " High Fidelity Capture (A2DP) " ) , sizeof ( pa_bluez4_profile_t ) ) ;
2012-10-22 10:46:41 +02:00
p - > priority = 10 ;
p - > n_sinks = 0 ;
p - > n_sources = 1 ;
p - > max_sink_channels = 0 ;
p - > max_source_channels = 2 ;
2013-05-20 11:48:33 +02:00
pa_hashmap_put ( input_port - > profiles , p - > name , p ) ;
2012-10-22 10:46:41 +02:00
d = PA_CARD_PROFILE_DATA ( p ) ;
2013-08-15 16:17:26 -03:00
* d = PA_BLUEZ4_PROFILE_A2DP_SOURCE ;
2012-10-22 10:46:41 +02:00
} else if ( pa_streq ( uuid , HSP_HS_UUID ) | | pa_streq ( uuid , HFP_HS_UUID ) ) {
2013-08-15 16:17:26 -03:00
p = pa_card_profile_new ( " hsp " , _ ( " Telephony Duplex (HSP/HFP) " ) , sizeof ( pa_bluez4_profile_t ) ) ;
2012-10-22 10:46:41 +02:00
p - > priority = 20 ;
p - > n_sinks = 1 ;
p - > n_sources = 1 ;
p - > max_sink_channels = 1 ;
p - > max_source_channels = 1 ;
2013-05-20 11:48:33 +02:00
pa_hashmap_put ( input_port - > profiles , p - > name , p ) ;
pa_hashmap_put ( output_port - > profiles , p - > name , p ) ;
2012-10-22 10:46:41 +02:00
d = PA_CARD_PROFILE_DATA ( p ) ;
2013-08-15 16:17:26 -03:00
* d = PA_BLUEZ4_PROFILE_HSP ;
2012-10-22 10:46:41 +02:00
} else if ( pa_streq ( uuid , HFP_AG_UUID ) ) {
2013-08-15 16:17:26 -03:00
p = pa_card_profile_new ( " hfgw " , _ ( " Handsfree Gateway " ) , sizeof ( pa_bluez4_profile_t ) ) ;
2012-10-22 10:46:41 +02:00
p - > priority = 20 ;
p - > n_sinks = 1 ;
p - > n_sources = 1 ;
p - > max_sink_channels = 1 ;
p - > max_source_channels = 1 ;
2013-05-20 11:48:33 +02:00
pa_hashmap_put ( input_port - > profiles , p - > name , p ) ;
pa_hashmap_put ( output_port - > profiles , p - > name , p ) ;
2012-10-22 10:46:41 +02:00
d = PA_CARD_PROFILE_DATA ( p ) ;
2013-08-15 16:17:26 -03:00
* d = PA_BLUEZ4_PROFILE_HFGW ;
2012-10-22 10:46:41 +02:00
}
2013-02-18 09:10:33 +01:00
if ( p ) {
2013-08-13 01:53:54 -03:00
pa_bluez4_transport * t ;
2013-02-18 09:10:33 +01:00
if ( ( t = u - > device - > transports [ * d ] ) )
p - > available = transport_state_to_availability ( t - > state ) ;
}
2012-10-22 10:46:41 +02:00
return p ;
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2012-10-22 10:46:39 +02:00
static int add_card ( struct userdata * u ) {
2009-01-29 16:27:27 +01:00
pa_card_new_data data ;
2013-01-11 22:11:04 +02:00
bool b ;
2009-01-29 16:27:27 +01:00
pa_card_profile * p ;
2013-08-15 16:17:26 -03:00
pa_bluez4_profile_t * d ;
2013-08-13 01:53:54 -03:00
pa_bluez4_form_factor_t ff ;
2009-02-02 01:58:48 +01:00
char * n ;
2009-05-28 02:39:22 +02:00
const char * default_profile ;
2013-08-13 01:53:54 -03:00
const pa_bluez4_device * device ;
const pa_bluez4_uuid * uuid ;
2009-05-28 02:39:22 +02:00
pa_assert ( u ) ;
2013-06-05 17:45:06 +03:00
pa_assert ( u - > device ) ;
device = u - > device ;
2009-01-29 16:27:27 +01:00
pa_card_new_data_init ( & data ) ;
2008-08-11 13:27:13 -03:00
data . driver = __FILE__ ;
2009-01-29 16:27:27 +01:00
data . module = u - > module ;
2009-02-02 01:58:48 +01:00
2013-08-13 01:53:54 -03:00
n = pa_bluez4_cleanup_name ( device - > alias ) ;
2009-02-02 01:58:48 +01:00
pa_proplist_sets ( data . proplist , PA_PROP_DEVICE_DESCRIPTION , n ) ;
pa_xfree ( n ) ;
2009-03-21 01:19:49 +01:00
pa_proplist_sets ( data . proplist , PA_PROP_DEVICE_STRING , device - > address ) ;
2009-02-02 01:58:48 +01:00
pa_proplist_sets ( data . proplist , PA_PROP_DEVICE_API , " bluez " ) ;
pa_proplist_sets ( data . proplist , PA_PROP_DEVICE_CLASS , " sound " ) ;
2009-03-01 20:32:32 +01:00
pa_proplist_sets ( data . proplist , PA_PROP_DEVICE_BUS , " bluetooth " ) ;
2013-03-07 11:32:21 +01:00
2013-08-13 01:53:54 -03:00
if ( ( ff = pa_bluez4_get_form_factor ( device - > class ) ) ! = PA_BLUEZ4_FORM_FACTOR_UNKNOWN )
pa_proplist_sets ( data . proplist , PA_PROP_DEVICE_FORM_FACTOR , pa_bluez4_form_factor_to_string ( ff ) ) ;
2013-03-07 11:32:21 +01:00
2009-03-21 01:19:49 +01:00
pa_proplist_sets ( data . proplist , " bluez.path " , device - > path ) ;
pa_proplist_setf ( data . proplist , " bluez.class " , " 0x%06x " , ( unsigned ) device - > class ) ;
2013-04-26 12:30:24 -03:00
pa_proplist_sets ( data . proplist , " bluez.alias " , device - > alias ) ;
2009-03-21 01:19:49 +01:00
data . name = get_name ( " card " , u - > modargs , device - > address , & b ) ;
2009-01-29 16:27:27 +01:00
data . namereg_fail = b ;
2009-05-28 02:39:22 +02:00
if ( pa_modargs_get_proplist ( u - > modargs , " card_properties " , data . proplist , PA_UPDATE_REPLACE ) < 0 ) {
pa_log ( " Invalid properties " ) ;
pa_card_new_data_done ( & data ) ;
return - 1 ;
}
2013-05-20 11:48:32 +02:00
create_card_ports ( u , data . ports ) ;
2012-10-22 10:46:41 +02:00
PA_LLIST_FOREACH ( uuid , device - > uuids ) {
2013-05-20 11:48:33 +02:00
p = create_card_profile ( u , uuid - > uuid , data . ports ) ;
2009-07-30 03:18:15 -03:00
2012-10-22 10:46:41 +02:00
if ( ! p )
continue ;
2009-01-29 16:27:27 +01:00
2012-10-22 10:46:41 +02:00
if ( pa_hashmap_get ( data . profiles , p - > name ) ) {
pa_card_profile_free ( p ) ;
continue ;
}
2009-02-02 01:58:48 +01:00
pa_hashmap_put ( data . profiles , p - > name , p ) ;
2010-01-29 11:01:31 -02:00
}
2009-01-29 16:27:27 +01:00
pa_assert ( ! pa_hashmap_isempty ( data . profiles ) ) ;
2013-08-15 16:17:26 -03:00
p = pa_card_profile_new ( " off " , _ ( " Off " ) , sizeof ( pa_bluez4_profile_t ) ) ;
2013-02-18 09:10:33 +01:00
p - > available = PA_AVAILABLE_YES ;
2009-01-29 16:27:27 +01:00
d = PA_CARD_PROFILE_DATA ( p ) ;
2013-08-15 16:17:26 -03:00
* d = PA_BLUEZ4_PROFILE_OFF ;
2009-02-02 01:58:48 +01:00
pa_hashmap_put ( data . profiles , p - > name , p ) ;
2009-01-29 16:27:27 +01:00
2009-05-28 02:39:22 +02:00
if ( ( default_profile = pa_modargs_get_value ( u - > modargs , " profile " , NULL ) ) ) {
2009-02-02 01:58:48 +01:00
if ( pa_hashmap_get ( data . profiles , default_profile ) )
pa_card_new_data_set_profile ( & data , default_profile ) ;
else
pa_log_warn ( " Profile '%s' not valid or not supported by device. " , default_profile ) ;
}
u - > card = pa_card_new ( u - > core , & data ) ;
2009-01-29 16:27:27 +01:00
pa_card_new_data_done ( & data ) ;
if ( ! u - > card ) {
pa_log ( " Failed to allocate card. " ) ;
return - 1 ;
}
2009-02-02 01:58:48 +01:00
u - > card - > userdata = u ;
u - > card - > set_profile = card_set_profile ;
2009-01-29 16:27:27 +01:00
2009-06-29 17:46:30 +03:00
d = PA_CARD_PROFILE_DATA ( u - > card - > active_profile ) ;
2013-08-15 16:17:26 -03:00
if ( * d ! = PA_BLUEZ4_PROFILE_OFF & & ( ! device - > transports [ * d ] | |
2013-08-13 01:53:54 -03:00
device - > transports [ * d ] - > state = = PA_BLUEZ4_TRANSPORT_STATE_DISCONNECTED ) ) {
2009-06-29 17:46:30 +03:00
pa_log_warn ( " Default profile not connected, selecting off profile " ) ;
u - > card - > active_profile = pa_hashmap_get ( u - > card - > profiles , " off " ) ;
2013-01-11 11:07:46 +01:00
u - > card - > save_profile = false ;
2009-06-29 17:46:30 +03:00
}
2009-01-29 16:27:27 +01:00
d = PA_CARD_PROFILE_DATA ( u - > card - > active_profile ) ;
u - > profile = * d ;
2011-03-31 15:00:52 +03:00
if ( USE_SCO_OVER_PCM ( u ) )
save_sco_volume_callbacks ( u ) ;
2009-01-29 16:27:27 +01:00
return 0 ;
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2013-08-13 01:53:54 -03:00
static pa_bluez4_device * find_device ( struct userdata * u , const char * address , const char * path ) {
pa_bluez4_device * d = NULL ;
2009-02-02 01:58:48 +01:00
pa_assert ( u ) ;
if ( ! address & & ! path ) {
pa_log_error ( " Failed to get device address/path from module arguments. " ) ;
2009-03-21 01:19:49 +01:00
return NULL ;
2009-02-02 01:58:48 +01:00
}
if ( path ) {
2013-08-13 01:53:54 -03:00
if ( ! ( d = pa_bluez4_discovery_get_by_path ( u - > discovery , path ) ) ) {
2009-02-02 01:58:48 +01:00
pa_log_error ( " %s is not a valid BlueZ audio device. " , path ) ;
2009-03-21 01:19:49 +01:00
return NULL ;
2009-02-02 01:58:48 +01:00
}
2009-03-21 01:19:49 +01:00
if ( address & & ! ( pa_streq ( d - > address , address ) ) ) {
2010-10-07 17:22:41 +03:00
pa_log_error ( " Passed path %s address %s != %s don't match. " , path , d - > address , address ) ;
2009-03-21 01:19:49 +01:00
return NULL ;
2009-02-02 01:58:48 +01:00
}
2009-03-21 01:19:49 +01:00
2009-02-02 01:58:48 +01:00
} else {
2013-08-13 01:53:54 -03:00
if ( ! ( d = pa_bluez4_discovery_get_by_address ( u - > discovery , address ) ) ) {
2009-02-02 01:58:48 +01:00
pa_log_error ( " %s is not known. " , address ) ;
2009-03-21 01:19:49 +01:00
return NULL ;
2009-02-02 01:58:48 +01:00
}
}
2009-03-21 02:54:18 +01:00
if ( d ) {
2009-03-21 01:19:49 +01:00
u - > address = pa_xstrdup ( d - > address ) ;
2009-03-21 02:54:18 +01:00
u - > path = pa_xstrdup ( d - > path ) ;
}
2009-03-21 01:19:49 +01:00
return d ;
2009-02-02 01:58:48 +01:00
}
2012-10-26 08:23:51 +02:00
/* Run from main thread */
2013-08-13 01:53:54 -03:00
static pa_hook_result_t uuid_added_cb ( pa_bluez4_discovery * y , const struct pa_bluez4_hook_uuid_data * data ,
2012-12-06 15:55:28 +01:00
struct userdata * u ) {
2012-10-26 08:23:51 +02:00
pa_card_profile * p ;
2012-12-06 15:55:28 +01:00
pa_assert ( data ) ;
pa_assert ( data - > device ) ;
pa_assert ( data - > uuid ) ;
2012-10-26 08:23:51 +02:00
pa_assert ( u ) ;
2012-12-06 15:55:28 +01:00
if ( data - > device ! = u - > device )
return PA_HOOK_OK ;
2013-05-20 11:48:33 +02:00
p = create_card_profile ( u , data - > uuid , u - > card - > ports ) ;
2012-10-26 08:23:51 +02:00
if ( ! p )
return PA_HOOK_OK ;
if ( pa_hashmap_get ( u - > card - > profiles , p - > name ) ) {
pa_card_profile_free ( p ) ;
return PA_HOOK_OK ;
}
pa_card_add_profile ( u - > card , p ) ;
return PA_HOOK_OK ;
}
2012-11-22 15:20:28 +01:00
/* Run from main thread */
2013-08-13 01:53:54 -03:00
static pa_hook_result_t discovery_hook_cb ( pa_bluez4_discovery * y , const pa_bluez4_device * d , struct userdata * u ) {
2012-11-22 15:20:28 +01:00
pa_assert ( u ) ;
pa_assert ( d ) ;
if ( d ! = u - > device )
return PA_HOOK_OK ;
2012-12-06 15:55:26 +01:00
if ( d - > dead )
pa_log_debug ( " Device %s removed: unloading module " , d - > path ) ;
2013-08-13 01:53:54 -03:00
else if ( ! pa_bluez4_device_any_audio_connected ( d ) )
2012-12-06 15:55:26 +01:00
pa_log_debug ( " Unloading module, because device %s doesn't have any audio profiles connected anymore. " , d - > path ) ;
else
2012-11-22 15:20:28 +01:00
return PA_HOOK_OK ;
pa_module_unload ( u - > core , u - > module , true ) ;
return PA_HOOK_OK ;
}
2013-07-12 14:51:24 -03:00
int pa__init ( pa_module * m ) {
2009-01-29 16:27:27 +01:00
pa_modargs * ma ;
uint32_t channels ;
struct userdata * u ;
2009-02-02 01:58:48 +01:00
const char * address , * path ;
2013-08-13 01:53:54 -03:00
pa_bluez4_device * device ;
2009-01-29 16:27:27 +01:00
pa_assert ( m ) ;
if ( ! ( ma = pa_modargs_new ( m - > argument , valid_modargs ) ) ) {
pa_log_error ( " Failed to parse module arguments " ) ;
2008-08-11 13:27:13 -03:00
goto fail ;
}
2009-01-29 16:27:27 +01:00
m - > userdata = u = pa_xnew0 ( struct userdata , 1 ) ;
u - > module = m ;
u - > core = m - > core ;
u - > stream_fd = - 1 ;
2009-02-02 01:58:48 +01:00
u - > sample_spec = m - > core - > default_sample_spec ;
u - > modargs = ma ;
2008-08-15 16:23:54 -03:00
2009-02-03 17:15:41 +02:00
if ( pa_modargs_get_value ( ma , " sco_sink " , NULL ) & &
! ( u - > hsp . sco_sink = pa_namereg_get ( m - > core , pa_modargs_get_value ( ma , " sco_sink " , NULL ) , PA_NAMEREG_SINK ) ) ) {
pa_log ( " SCO sink not found " ) ;
goto fail ;
}
if ( pa_modargs_get_value ( ma , " sco_source " , NULL ) & &
! ( u - > hsp . sco_source = pa_namereg_get ( m - > core , pa_modargs_get_value ( ma , " sco_source " , NULL ) , PA_NAMEREG_SOURCE ) ) ) {
pa_log ( " SCO source not found " ) ;
goto fail ;
}
2013-11-29 15:32:41 +01:00
if ( pa_modargs_get_sample_rate ( ma , & u - > sample_spec . rate ) < 0 ) {
2009-01-29 16:27:27 +01:00
pa_log_error ( " Failed to get rate from module arguments " ) ;
2009-01-19 14:53:35 +02:00
goto fail ;
}
2008-08-11 13:27:13 -03:00
2013-01-11 11:07:46 +01:00
u - > auto_connect = true ;
2009-10-04 13:00:51 +02:00
if ( pa_modargs_get_value_boolean ( ma , " auto_connect " , & u - > auto_connect ) ) {
pa_log ( " Failed to parse auto_connect= argument " ) ;
goto fail ;
}
2009-02-02 01:58:48 +01:00
channels = u - > sample_spec . channels ;
2009-01-29 16:27:27 +01:00
if ( pa_modargs_get_value_u32 ( ma , " channels " , & channels ) < 0 | |
channels < = 0 | | channels > PA_CHANNELS_MAX ) {
pa_log_error ( " Failed to get channels from module arguments " ) ;
2009-01-19 14:53:35 +02:00
goto fail ;
}
2009-02-02 01:58:48 +01:00
u - > sample_spec . channels = ( uint8_t ) channels ;
2009-02-12 03:47:27 +01:00
u - > requested_sample_spec = u - > sample_spec ;
2009-01-29 16:27:27 +01:00
2009-02-02 01:58:48 +01:00
address = pa_modargs_get_value ( ma , " address " , NULL ) ;
path = pa_modargs_get_value ( ma , " path " , NULL ) ;
2009-01-19 14:53:35 +02:00
2013-08-13 01:53:54 -03:00
if ( ! ( u - > discovery = pa_bluez4_discovery_get ( m - > core ) ) )
2009-01-29 16:27:27 +01:00
goto fail ;
2009-03-30 20:57:12 +02:00
if ( ! ( device = find_device ( u , address , path ) ) )
2009-03-21 01:19:49 +01:00
goto fail ;
2009-01-29 16:27:27 +01:00
2012-10-22 10:46:39 +02:00
u - > device = device ;
2012-12-06 15:55:27 +01:00
u - > discovery_slot =
2013-08-13 01:53:54 -03:00
pa_hook_connect ( pa_bluez4_discovery_hook ( u - > discovery , PA_BLUEZ4_HOOK_DEVICE_CONNECTION_CHANGED ) ,
2012-12-06 15:55:27 +01:00
PA_HOOK_NORMAL , ( pa_hook_cb_t ) discovery_hook_cb , u ) ;
2012-12-06 15:55:28 +01:00
u - > uuid_added_slot =
2013-08-13 01:53:54 -03:00
pa_hook_connect ( pa_bluez4_discovery_hook ( u - > discovery , PA_BLUEZ4_HOOK_DEVICE_UUID_ADDED ) ,
2012-12-06 15:55:28 +01:00
PA_HOOK_NORMAL , ( pa_hook_cb_t ) uuid_added_cb , u ) ;
2012-10-26 08:23:51 +02:00
2012-12-11 14:40:24 +01:00
u - > sink_state_changed_slot =
pa_hook_connect ( & u - > core - > hooks [ PA_CORE_HOOK_SINK_STATE_CHANGED ] ,
PA_HOOK_NORMAL , ( pa_hook_cb_t ) sink_state_changed_cb , u ) ;
u - > source_state_changed_slot =
pa_hook_connect ( & u - > core - > hooks [ PA_CORE_HOOK_SOURCE_STATE_CHANGED ] ,
PA_HOOK_NORMAL , ( pa_hook_cb_t ) source_state_changed_cb , u ) ;
2012-12-10 08:30:40 +01:00
u - > transport_state_changed_slot =
2013-08-13 01:53:54 -03:00
pa_hook_connect ( pa_bluez4_discovery_hook ( u - > discovery , PA_BLUEZ4_HOOK_TRANSPORT_STATE_CHANGED ) ,
2012-12-10 08:30:40 +01:00
PA_HOOK_NORMAL , ( pa_hook_cb_t ) transport_state_changed_cb , u ) ;
2012-12-10 08:30:41 +01:00
u - > transport_nrec_changed_slot =
2013-08-13 01:53:54 -03:00
pa_hook_connect ( pa_bluez4_discovery_hook ( u - > discovery , PA_BLUEZ4_HOOK_TRANSPORT_NREC_CHANGED ) ,
2012-12-10 08:30:41 +01:00
PA_HOOK_NORMAL , ( pa_hook_cb_t ) transport_nrec_changed_cb , u ) ;
2012-12-14 15:14:30 +01:00
u - > transport_microphone_changed_slot =
2013-08-13 01:53:54 -03:00
pa_hook_connect ( pa_bluez4_discovery_hook ( u - > discovery , PA_BLUEZ4_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED ) ,
2012-12-14 15:14:30 +01:00
PA_HOOK_NORMAL , ( pa_hook_cb_t ) transport_microphone_gain_changed_cb , u ) ;
u - > transport_speaker_changed_slot =
2013-08-13 01:53:54 -03:00
pa_hook_connect ( pa_bluez4_discovery_hook ( u - > discovery , PA_BLUEZ4_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED ) ,
2012-12-14 15:14:30 +01:00
PA_HOOK_NORMAL , ( pa_hook_cb_t ) transport_speaker_gain_changed_cb , u ) ;
2009-02-02 01:58:48 +01:00
/* Add the card structure. This will also initialize the default profile */
2012-10-22 10:46:39 +02:00
if ( add_card ( u ) < 0 )
2009-02-02 01:58:48 +01:00
goto fail ;
2009-01-29 16:27:27 +01:00
2011-10-04 09:37:24 +02:00
if ( ! ( u - > msg = pa_msgobject_new ( bluetooth_msg ) ) )
goto fail ;
u - > msg - > parent . process_msg = device_process_msg ;
u - > msg - > card = u - > card ;
2013-08-15 16:17:26 -03:00
if ( u - > profile ! = PA_BLUEZ4_PROFILE_OFF )
2009-03-04 16:38:02 +02:00
if ( init_profile ( u ) < 0 )
2012-10-19 10:11:27 +02:00
goto off ;
2009-01-29 16:27:27 +01:00
2009-03-04 16:38:02 +02:00
if ( u - > sink | | u - > source )
if ( start_thread ( u ) < 0 )
2012-10-19 10:11:27 +02:00
goto off ;
return 0 ;
off :
stop_thread ( u ) ;
2013-11-20 15:42:26 +02:00
pa_assert_se ( pa_card_set_profile ( u - > card , pa_hashmap_get ( u - > card - > profiles , " off " ) , false ) > = 0 ) ;
2009-01-29 16:27:27 +01:00
2008-07-31 20:16:43 -03:00
return 0 ;
fail :
2009-03-21 01:19:49 +01:00
2008-07-31 20:16:43 -03:00
pa__done ( m ) ;
2009-03-21 01:19:49 +01:00
2008-07-31 20:16:43 -03:00
return - 1 ;
}
2009-01-29 16:27:27 +01:00
int pa__get_n_used ( pa_module * m ) {
struct userdata * u ;
pa_assert ( m ) ;
pa_assert_se ( u = m - > userdata ) ;
return
( u - > sink ? pa_sink_linked_by ( u - > sink ) : 0 ) +
( u - > source ? pa_source_linked_by ( u - > source ) : 0 ) ;
}
2008-07-31 20:16:43 -03:00
void pa__done ( pa_module * m ) {
2008-08-20 10:52:25 -03:00
struct userdata * u ;
2011-03-28 15:35:16 +03:00
2008-08-20 10:52:25 -03:00
pa_assert ( m ) ;
if ( ! ( u = m - > userdata ) )
return ;
2009-02-02 01:58:48 +01:00
stop_thread ( u ) ;
2009-01-29 16:27:27 +01:00
2012-11-22 15:20:28 +01:00
if ( u - > discovery_slot )
pa_hook_slot_free ( u - > discovery_slot ) ;
2012-12-11 14:40:24 +01:00
if ( u - > uuid_added_slot )
pa_hook_slot_free ( u - > uuid_added_slot ) ;
if ( u - > sink_state_changed_slot )
pa_hook_slot_free ( u - > sink_state_changed_slot ) ;
if ( u - > source_state_changed_slot )
pa_hook_slot_free ( u - > source_state_changed_slot ) ;
2012-12-10 08:30:40 +01:00
if ( u - > transport_state_changed_slot )
pa_hook_slot_free ( u - > transport_state_changed_slot ) ;
2012-12-10 08:30:41 +01:00
if ( u - > transport_nrec_changed_slot )
pa_hook_slot_free ( u - > transport_nrec_changed_slot ) ;
2012-12-14 15:14:30 +01:00
if ( u - > transport_microphone_changed_slot )
pa_hook_slot_free ( u - > transport_microphone_changed_slot ) ;
if ( u - > transport_speaker_changed_slot )
pa_hook_slot_free ( u - > transport_speaker_changed_slot ) ;
2011-03-31 15:00:52 +03:00
if ( USE_SCO_OVER_PCM ( u ) )
restore_sco_volume_callbacks ( u ) ;
2011-10-04 09:37:24 +02:00
if ( u - > msg )
pa_xfree ( u - > msg ) ;
2009-01-29 16:27:27 +01:00
if ( u - > card )
2009-02-02 01:58:48 +01:00
pa_card_free ( u - > card ) ;
2009-01-29 16:27:27 +01:00
2009-02-02 01:58:48 +01:00
if ( u - > a2dp . buffer )
pa_xfree ( u - > a2dp . buffer ) ;
sbc_finish ( & u - > a2dp . sbc ) ;
if ( u - > modargs )
pa_modargs_free ( u - > modargs ) ;
2013-03-07 11:32:23 +01:00
pa_xfree ( u - > output_port_name ) ;
pa_xfree ( u - > input_port_name ) ;
2009-03-21 01:19:49 +01:00
pa_xfree ( u - > address ) ;
2009-03-21 02:54:18 +01:00
pa_xfree ( u - > path ) ;
2009-03-21 01:19:49 +01:00
2009-03-30 20:57:12 +02:00
if ( u - > discovery )
2013-08-13 01:53:54 -03:00
pa_bluez4_discovery_unref ( u - > discovery ) ;
2009-03-30 20:57:12 +02:00
2008-08-20 10:52:25 -03:00
pa_xfree ( u ) ;
}