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
2009-01-29 16:27:27 +01:00
Copyright 2008 Joao Paulo Rechi Vita
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>
# include <poll.h>
2008-08-25 22:15:25 -03:00
# include <sys/ioctl.h>
# 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-02-20 01:18:37 +01:00
# include <pulse/i18n.h>
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
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>
# include <pulsecore/socket-util.h>
2008-08-11 13:27:13 -03:00
# include <pulsecore/thread.h>
# include <pulsecore/thread-mq.h>
# include <pulsecore/rtpoll.h>
# include <pulsecore/time-smoother.h>
2009-02-03 17:15:41 +02:00
# include <pulsecore/namereg.h>
2009-03-19 01:35:02 +02:00
# include <pulsecore/dbus-shared.h>
2009-01-29 16:27:27 +01:00
2008-08-29 20:22:14 -03:00
# include "module-bluetooth-device-symdef.h"
# include "ipc.h"
# include "sbc.h"
# include "rtp.h"
2009-01-29 16:27:27 +01:00
# include "bluetooth-util.h"
2008-08-11 13:27:13 -03:00
# define MAX_BITPOOL 64
2008-09-29 21:43:28 +02:00
# define MIN_BITPOOL 2U
2008-07-31 20:16:43 -03:00
PA_MODULE_AUTHOR ( " Joao Paulo Rechi Vita " ) ;
PA_MODULE_DESCRIPTION ( " Bluetooth audio sink and source " ) ;
PA_MODULE_VERSION ( PACKAGE_VERSION ) ;
PA_MODULE_LOAD_ONCE ( FALSE ) ;
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> "
2009-01-19 14:53:35 +02:00
" profile=<a2dp|hsp> "
" rate=<sample rate> "
" channels=<number of channels> "
2009-03-21 01:31:38 +01:00
" path=<device object path> " ) ;
/*
# ifdef NOKIA
2009-02-12 03:57:59 +01:00
" sco_sink=<SCO over PCM sink name> "
2009-03-21 01:31:38 +01:00
" sco_source=<SCO over PCM source name> "
# endif
*/
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-03-21 01:31:38 +01:00
# ifdef NOKIA
2009-02-03 17:15:41 +02:00
" sco_sink " ,
" sco_source " ,
2009-03-21 01:31:38 +01:00
# endif
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_capabilities_t sbc_capabilities ;
sbc_t sbc ; /* Codec data */
2008-08-21 15:12:03 -03:00
pa_bool_t 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 */
} ;
2008-07-31 20:16:43 -03:00
2009-02-03 17:15:41 +02:00
struct hsp_info {
2009-02-03 18:43:54 +02:00
pcm_capabilities_t pcm_capabilities ;
2009-03-21 01:31:38 +01:00
# ifdef NOKIA
2009-02-03 17:15:41 +02:00
pa_sink * sco_sink ;
pa_source * sco_source ;
2009-03-21 01:31:38 +01:00
# endif
2009-02-03 18:17:20 +02:00
pa_hook_slot * sink_state_changed_slot ;
pa_hook_slot * source_state_changed_slot ;
2009-02-03 17:15:41 +02:00
} ;
2009-01-29 16:27:27 +01:00
enum profile {
PROFILE_A2DP ,
2009-02-02 01:58:48 +01:00
PROFILE_HSP ,
2009-01-29 16:27:27 +01:00
PROFILE_OFF
} ;
2008-07-31 20:16:43 -03:00
struct userdata {
pa_core * core ;
pa_module * module ;
2009-02-02 01:58:48 +01:00
2009-03-21 01:19:49 +01:00
char * address ;
2009-03-21 02:54:18 +01:00
char * path ;
2009-03-30 20:57:12 +02:00
pa_bluetooth_discovery * discovery ;
2009-03-21 02:54:18 +01:00
pa_dbus_connection * connection ;
2009-03-21 01:19:49 +01:00
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 ;
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
2009-01-29 16:27:27 +01:00
int service_fd ;
2008-09-29 21:43:28 +02:00
int stream_fd ;
2008-08-11 18:10:14 -03:00
2008-09-29 21:43:28 +02:00
size_t link_mtu ;
2008-08-11 18:10:14 -03:00
size_t 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
2009-01-29 16:27:27 +01:00
enum profile profile ;
2009-02-02 01:58:48 +01:00
pa_modargs * modargs ;
2009-04-10 01:30:50 +02:00
int stream_write_type ;
2009-02-12 21:45:17 +01:00
int service_write_type , service_read_type ;
2008-07-31 20:16:43 -03:00
} ;
2009-04-10 01:30:50 +02:00
# define FIXED_LATENCY_PLAYBACK_A2DP (25*PA_USEC_PER_MSEC)
# 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)
2009-03-21 01:31:38 +01:00
# ifdef NOKIA
2009-03-06 14:45:06 +02:00
# define USE_SCO_OVER_PCM(u) (u->profile == PROFILE_HSP && (u->hsp.sco_sink && u->hsp.sco_source))
2009-03-21 01:31:38 +01:00
# endif
2009-03-06 14:45:06 +02:00
2009-02-03 18:17:20 +02:00
static int init_bt ( struct userdata * u ) ;
static int init_profile ( struct userdata * u ) ;
2009-02-12 21:45:17 +01:00
static int service_send ( struct userdata * u , const bt_audio_msg_header_t * msg ) {
2009-02-02 01:58:48 +01:00
ssize_t r ;
2009-02-12 21:45:17 +01:00
pa_assert ( u ) ;
pa_assert ( u - > service_fd > = 0 ) ;
2009-02-02 01:58:48 +01:00
pa_assert ( msg ) ;
2009-02-12 22:06:15 +01:00
pa_assert ( msg - > length > 0 ) ;
2009-02-02 01:58:48 +01:00
pa_log_debug ( " Sending %s -> %s " ,
pa_strnull ( bt_audio_strtype ( msg - > type ) ) ,
pa_strnull ( bt_audio_strname ( msg - > name ) ) ) ;
2009-02-12 22:06:15 +01:00
if ( ( r = pa_loop_write ( u - > service_fd , msg , msg - > length , & u - > service_write_type ) ) = = ( ssize_t ) msg - > length )
2009-02-02 01:58:48 +01:00
return 0 ;
if ( r < 0 )
pa_log_error ( " Error sending data to audio service: %s " , pa_cstrerror ( errno ) ) ;
else
2009-02-12 21:45:17 +01:00
pa_log_error ( " Short write() " ) ;
2009-02-02 01:58:48 +01:00
return - 1 ;
2008-07-31 20:16:43 -03:00
}
2009-02-12 21:45:17 +01:00
static int service_recv ( struct userdata * u , bt_audio_msg_header_t * msg , size_t room ) {
2009-02-02 01:58:48 +01:00
ssize_t r ;
2009-02-12 21:45:17 +01:00
pa_assert ( u ) ;
pa_assert ( u - > service_fd > = 0 ) ;
2009-02-02 01:58:48 +01:00
pa_assert ( msg ) ;
2009-01-06 11:00:44 -03:00
2009-02-12 21:45:17 +01:00
if ( room < = 0 )
room = BT_SUGGESTED_BUFFER_SIZE ;
2009-02-12 16:51:02 +01:00
2009-02-02 01:58:48 +01:00
pa_log_debug ( " Trying to receive message from audio service... " ) ;
2009-02-12 21:45:17 +01:00
/* First, read the header */
if ( ( r = pa_loop_read ( u - > service_fd , msg , sizeof ( * msg ) , & u - > service_read_type ) ) ! = sizeof ( * msg ) )
goto read_fail ;
2009-02-02 01:58:48 +01:00
2009-02-12 21:45:17 +01:00
if ( msg - > length < sizeof ( * msg ) ) {
pa_log_error ( " Invalid message size. " ) ;
return - 1 ;
}
2009-02-02 01:58:48 +01:00
2009-02-12 22:06:15 +01:00
/* Secondly, read the payload */
2009-02-12 21:45:17 +01:00
if ( msg - > length > sizeof ( * msg ) ) {
2009-02-02 01:58:48 +01:00
2009-02-12 21:45:17 +01:00
size_t remains = msg - > length - sizeof ( * msg ) ;
2009-02-02 01:58:48 +01:00
2009-02-12 21:45:17 +01:00
if ( ( r = pa_loop_read ( u - > service_fd ,
( uint8_t * ) msg + sizeof ( * msg ) ,
remains ,
& u - > service_read_type ) ) ! = ( ssize_t ) remains )
goto read_fail ;
2008-07-31 20:16:43 -03:00
}
2009-02-12 21:45:17 +01:00
pa_log_debug ( " Received %s <- %s " ,
pa_strnull ( bt_audio_strtype ( msg - > type ) ) ,
pa_strnull ( bt_audio_strname ( msg - > name ) ) ) ;
return 0 ;
read_fail :
2009-02-02 01:58:48 +01:00
if ( r < 0 )
pa_log_error ( " Error receiving data from audio service: %s " , pa_cstrerror ( errno ) ) ;
else
2009-02-12 22:06:15 +01:00
pa_log_error ( " Short read() " ) ;
2009-02-02 01:58:48 +01:00
return - 1 ;
2008-07-31 20:16:43 -03:00
}
2009-02-12 21:45:17 +01:00
static ssize_t service_expect ( struct userdata * u , bt_audio_msg_header_t * rsp , size_t room , uint8_t expected_name , size_t expected_size ) {
2009-02-02 01:58:48 +01:00
int r ;
2008-12-17 17:19:22 -03:00
2009-02-12 21:45:17 +01:00
pa_assert ( u ) ;
pa_assert ( u - > service_fd > = 0 ) ;
2009-02-02 01:58:48 +01:00
pa_assert ( rsp ) ;
2009-02-12 21:45:17 +01:00
if ( ( r = service_recv ( u , rsp , room ) ) < 0 )
2009-02-02 01:58:48 +01:00
return r ;
2009-02-12 21:45:17 +01:00
if ( ( rsp - > type ! = BT_INDICATION & & rsp - > type ! = BT_RESPONSE ) | |
rsp - > name ! = expected_name | |
( expected_size > 0 & & rsp - > length ! = expected_size ) ) {
2008-12-17 17:19:22 -03:00
2009-02-12 16:51:02 +01:00
if ( rsp - > type = = BT_ERROR & & rsp - > length = = sizeof ( bt_audio_error_t ) )
pa_log_error ( " Received error condition: %s " , pa_cstrerror ( ( ( bt_audio_error_t * ) rsp ) - > posix_errno ) ) ;
else
pa_log_error ( " Bogus message %s received while %s was expected " ,
pa_strnull ( bt_audio_strname ( rsp - > name ) ) ,
pa_strnull ( bt_audio_strname ( expected_name ) ) ) ;
2009-02-02 01:58:48 +01:00
return - 1 ;
2008-12-17 17:19:22 -03:00
}
2009-02-02 01:58:48 +01:00
return 0 ;
2008-08-11 13:27:13 -03:00
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-03-24 12:04:52 -03:00
static int parse_caps ( struct userdata * u , uint8_t seid , const struct bt_get_capabilities_rsp * rsp ) {
2009-02-02 01:58:48 +01:00
uint16_t bytes_left ;
const codec_capabilities_t * codec ;
pa_assert ( u ) ;
pa_assert ( rsp ) ;
bytes_left = rsp - > h . length - sizeof ( * rsp ) ;
if ( bytes_left < sizeof ( codec_capabilities_t ) ) {
pa_log_error ( " Packet too small to store codec information. " ) ;
return - 1 ;
}
codec = ( codec_capabilities_t * ) rsp - > data ; /** ALIGNMENT? **/
pa_log_debug ( " Payload size is %lu %lu " , ( unsigned long ) bytes_left , ( unsigned long ) sizeof ( * codec ) ) ;
2008-12-17 17:19:22 -03:00
2009-02-02 01:58:48 +01:00
if ( ( u - > profile = = PROFILE_A2DP & & codec - > transport ! = BT_CAPABILITIES_TRANSPORT_A2DP ) | |
( u - > profile = = PROFILE_HSP & & codec - > transport ! = BT_CAPABILITIES_TRANSPORT_SCO ) ) {
pa_log_error ( " Got capabilities for wrong codec. " ) ;
return - 1 ;
}
2008-12-17 17:19:22 -03:00
2009-02-03 18:43:54 +02:00
if ( u - > profile = = PROFILE_HSP ) {
2009-02-12 03:56:01 +01:00
2009-02-03 18:43:54 +02:00
if ( bytes_left < = 0 | | codec - > length ! = sizeof ( u - > hsp . pcm_capabilities ) )
return - 1 ;
2008-12-17 17:19:22 -03:00
2009-02-03 18:43:54 +02:00
pa_assert ( codec - > type = = BT_HFP_CODEC_PCM ) ;
2008-12-17 17:19:22 -03:00
2009-03-24 12:04:52 -03:00
if ( codec - > configured & & seid = = 0 )
return codec - > seid ;
2009-02-03 18:43:54 +02:00
memcpy ( & u - > hsp . pcm_capabilities , codec , sizeof ( u - > hsp . pcm_capabilities ) ) ;
2008-12-17 17:19:22 -03:00
2009-02-12 03:56:01 +01:00
} else if ( u - > profile = = PROFILE_A2DP ) {
2009-02-03 18:43:54 +02:00
while ( bytes_left > 0 ) {
2009-03-25 17:57:19 -03:00
if ( ( codec - > type = = BT_A2DP_SBC_SINK ) & & ! codec - > lock )
2009-02-03 18:43:54 +02:00
break ;
bytes_left - = codec - > length ;
codec = ( const codec_capabilities_t * ) ( ( const uint8_t * ) codec + codec - > length ) ;
}
if ( bytes_left < = 0 | | codec - > length ! = sizeof ( u - > a2dp . sbc_capabilities ) )
return - 1 ;
2008-12-17 17:19:22 -03:00
2009-03-25 17:57:19 -03:00
pa_assert ( codec - > type = = BT_A2DP_SBC_SINK ) ;
2008-12-17 17:19:22 -03:00
2009-03-24 12:04:52 -03:00
if ( codec - > configured & & seid = = 0 )
return codec - > seid ;
2008-12-17 17:19:22 -03:00
2009-02-03 18:43:54 +02:00
memcpy ( & u - > a2dp . sbc_capabilities , codec , sizeof ( u - > a2dp . sbc_capabilities ) ) ;
}
2008-12-17 17:19:22 -03:00
return 0 ;
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-03-24 12:04:52 -03:00
static int get_caps ( struct userdata * u , uint8_t seid ) {
2008-08-25 21:56:31 -03:00
union {
2008-12-17 17:19:22 -03:00
struct bt_get_capabilities_req getcaps_req ;
struct bt_get_capabilities_rsp getcaps_rsp ;
2009-02-12 16:51:02 +01:00
bt_audio_error_t error ;
2008-12-17 17:19:22 -03:00
uint8_t buf [ BT_SUGGESTED_BUFFER_SIZE ] ;
2008-08-25 21:56:31 -03:00
} msg ;
2009-03-24 12:04:52 -03:00
int ret ;
2008-08-25 21:56:31 -03:00
2009-02-02 01:58:48 +01:00
pa_assert ( u ) ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
2008-12-17 17:19:22 -03:00
msg . getcaps_req . h . type = BT_REQUEST ;
msg . getcaps_req . h . name = BT_GET_CAPABILITIES ;
msg . getcaps_req . h . length = sizeof ( msg . getcaps_req ) ;
2009-03-24 12:04:52 -03:00
msg . getcaps_req . seid = seid ;
2008-12-17 17:19:22 -03:00
2009-03-25 17:57:19 -03:00
pa_strlcpy ( msg . getcaps_req . object , u - > path , sizeof ( msg . getcaps_req . object ) ) ;
2009-02-02 01:58:48 +01:00
if ( u - > profile = = PROFILE_A2DP )
2008-08-25 21:56:31 -03:00
msg . getcaps_req . transport = BT_CAPABILITIES_TRANSPORT_A2DP ;
2008-08-11 13:27:13 -03:00
else {
2009-02-02 01:58:48 +01:00
pa_assert ( u - > profile = = PROFILE_HSP ) ;
msg . getcaps_req . transport = BT_CAPABILITIES_TRANSPORT_SCO ;
2008-08-11 13:27:13 -03:00
}
2008-08-25 21:56:31 -03:00
msg . getcaps_req . flags = BT_FLAG_AUTOCONNECT ;
2008-08-11 13:27:13 -03:00
2009-02-12 21:45:17 +01:00
if ( service_send ( u , & msg . getcaps_req . h ) < 0 )
2009-02-02 01:58:48 +01:00
return - 1 ;
2008-08-11 13:27:13 -03:00
2009-02-12 21:45:17 +01:00
if ( service_expect ( u , & msg . getcaps_rsp . h , sizeof ( msg ) , BT_GET_CAPABILITIES , 0 ) < 0 )
2009-02-02 01:58:48 +01:00
return - 1 ;
2008-08-11 13:27:13 -03:00
2009-03-24 12:04:52 -03:00
ret = parse_caps ( u , seid , & msg . getcaps_rsp ) ;
if ( ret < = 0 )
return ret ;
return get_caps ( u , ret ) ;
2008-08-11 13:27:13 -03:00
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-02-02 01:58:48 +01:00
static uint8_t a2dp_default_bitpool ( uint8_t freq , uint8_t mode ) {
2008-08-11 13:27:13 -03:00
switch ( freq ) {
case BT_SBC_SAMPLING_FREQ_16000 :
case BT_SBC_SAMPLING_FREQ_32000 :
return 53 ;
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
case BT_SBC_SAMPLING_FREQ_44100 :
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
switch ( mode ) {
case BT_A2DP_CHANNEL_MODE_MONO :
case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL :
return 31 ;
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
case BT_A2DP_CHANNEL_MODE_STEREO :
case BT_A2DP_CHANNEL_MODE_JOINT_STEREO :
return 53 ;
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
default :
pa_log_warn ( " Invalid channel mode %u " , mode ) ;
return 53 ;
}
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
case BT_SBC_SAMPLING_FREQ_48000 :
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
switch ( mode ) {
case BT_A2DP_CHANNEL_MODE_MONO :
case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL :
return 29 ;
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
case BT_A2DP_CHANNEL_MODE_STEREO :
case BT_A2DP_CHANNEL_MODE_JOINT_STEREO :
return 51 ;
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
default :
pa_log_warn ( " Invalid channel mode %u " , mode ) ;
return 51 ;
}
2009-02-02 01:58:48 +01:00
2008-08-11 13:27:13 -03:00
default :
pa_log_warn ( " Invalid sampling freq %u " , freq ) ;
return 53 ;
}
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-02-02 01:58:48 +01:00
static int setup_a2dp ( struct userdata * u ) {
sbc_capabilities_t * cap ;
int i ;
2008-09-29 21:42:29 +02:00
static const struct {
uint32_t rate ;
uint8_t cap ;
} freq_table [ ] = {
{ 16000U , BT_SBC_SAMPLING_FREQ_16000 } ,
{ 32000U , BT_SBC_SAMPLING_FREQ_32000 } ,
{ 44100U , BT_SBC_SAMPLING_FREQ_44100 } ,
{ 48000U , BT_SBC_SAMPLING_FREQ_48000 }
} ;
2009-02-02 01:58:48 +01:00
pa_assert ( u ) ;
pa_assert ( u - > profile = = PROFILE_A2DP ) ;
cap = & u - > a2dp . sbc_capabilities ;
2008-09-29 21:42:29 +02:00
/* Find the lowest freq that is at least as high as the requested
* sampling rate */
2009-02-02 01:58:48 +01:00
for ( i = 0 ; ( unsigned ) i < PA_ELEMENTSOF ( freq_table ) ; i + + )
if ( freq_table [ i ] . rate > = u - > sample_spec . rate & & ( cap - > frequency & freq_table [ i ] . cap ) ) {
u - > sample_spec . rate = freq_table [ i ] . rate ;
2008-09-29 21:42:29 +02:00
cap - > frequency = freq_table [ i ] . cap ;
2008-08-11 13:27:13 -03:00
break ;
2008-09-29 21:42:29 +02:00
}
2008-08-11 13:27:13 -03:00
2009-03-26 21:31:12 +02:00
if ( ( unsigned ) i = = PA_ELEMENTSOF ( freq_table ) ) {
for ( - - i ; i > = 0 ; i - - ) {
2009-02-02 01:58:48 +01:00
if ( cap - > frequency & freq_table [ i ] . cap ) {
u - > sample_spec . rate = freq_table [ i ] . rate ;
cap - > frequency = freq_table [ i ] . cap ;
break ;
}
}
if ( i < 0 ) {
pa_log ( " Not suitable sample rate " ) ;
return - 1 ;
}
}
2009-03-30 20:57:23 +02:00
pa_assert ( ( unsigned ) i < PA_ELEMENTSOF ( freq_table ) ) ;
2009-03-26 21:31:12 +02:00
2009-03-26 15:38:40 -03:00
if ( cap - > capability . configured )
return 0 ;
2009-02-02 01:58:48 +01:00
if ( u - > sample_spec . channels < = 1 ) {
if ( cap - > channel_mode & BT_A2DP_CHANNEL_MODE_MONO ) {
cap - > channel_mode = BT_A2DP_CHANNEL_MODE_MONO ;
u - > sample_spec . channels = 1 ;
} else
u - > sample_spec . channels = 2 ;
}
if ( u - > sample_spec . channels > = 2 ) {
u - > sample_spec . channels = 2 ;
2008-08-11 13:27:13 -03:00
if ( cap - > channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO )
cap - > channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO ;
else if ( cap - > channel_mode & BT_A2DP_CHANNEL_MODE_STEREO )
cap - > channel_mode = BT_A2DP_CHANNEL_MODE_STEREO ;
else if ( cap - > channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL )
cap - > channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL ;
2009-02-02 01:58:48 +01:00
else if ( cap - > channel_mode & BT_A2DP_CHANNEL_MODE_MONO ) {
2008-08-11 13:27:13 -03:00
cap - > channel_mode = BT_A2DP_CHANNEL_MODE_MONO ;
2009-02-02 01:58:48 +01:00
u - > sample_spec . channels = 1 ;
} else {
pa_log ( " No supported channel modes " ) ;
return - 1 ;
}
2008-08-11 13:27:13 -03:00
}
if ( cap - > block_length & BT_A2DP_BLOCK_LENGTH_16 )
cap - > block_length = BT_A2DP_BLOCK_LENGTH_16 ;
else if ( cap - > block_length & BT_A2DP_BLOCK_LENGTH_12 )
cap - > block_length = BT_A2DP_BLOCK_LENGTH_12 ;
else if ( cap - > block_length & BT_A2DP_BLOCK_LENGTH_8 )
cap - > block_length = BT_A2DP_BLOCK_LENGTH_8 ;
else if ( cap - > block_length & BT_A2DP_BLOCK_LENGTH_4 )
cap - > block_length = BT_A2DP_BLOCK_LENGTH_4 ;
else {
pa_log_error ( " No supported block lengths " ) ;
return - 1 ;
}
if ( cap - > subbands & BT_A2DP_SUBBANDS_8 )
cap - > subbands = BT_A2DP_SUBBANDS_8 ;
else if ( cap - > subbands & BT_A2DP_SUBBANDS_4 )
cap - > subbands = BT_A2DP_SUBBANDS_4 ;
else {
pa_log_error ( " No supported subbands " ) ;
return - 1 ;
}
if ( cap - > allocation_method & BT_A2DP_ALLOCATION_LOUDNESS )
cap - > allocation_method = BT_A2DP_ALLOCATION_LOUDNESS ;
else if ( cap - > allocation_method & BT_A2DP_ALLOCATION_SNR )
cap - > allocation_method = BT_A2DP_ALLOCATION_SNR ;
2009-02-02 01:58:48 +01:00
cap - > min_bitpool = ( uint8_t ) PA_MAX ( MIN_BITPOOL , cap - > min_bitpool ) ;
cap - > max_bitpool = ( uint8_t ) PA_MIN ( a2dp_default_bitpool ( cap - > frequency , cap - > channel_mode ) , cap - > max_bitpool ) ;
2008-08-11 13:27:13 -03:00
return 0 ;
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-02-02 01:58:48 +01:00
static void setup_sbc ( struct a2dp_info * a2dp ) {
sbc_capabilities_t * active_capabilities ;
pa_assert ( a2dp ) ;
active_capabilities = & a2dp - > sbc_capabilities ;
2008-08-11 13:27:13 -03:00
if ( a2dp - > sbc_initialized )
sbc_reinit ( & a2dp - > sbc , 0 ) ;
else
sbc_init ( & a2dp - > sbc , 0 ) ;
2008-08-21 15:12:03 -03:00
a2dp - > sbc_initialized = TRUE ;
2008-08-11 13:27:13 -03:00
2009-02-02 01:58:48 +01:00
switch ( active_capabilities - > frequency ) {
case BT_SBC_SAMPLING_FREQ_16000 :
a2dp - > sbc . frequency = SBC_FREQ_16000 ;
break ;
case BT_SBC_SAMPLING_FREQ_32000 :
a2dp - > sbc . frequency = SBC_FREQ_32000 ;
break ;
case BT_SBC_SAMPLING_FREQ_44100 :
a2dp - > sbc . frequency = SBC_FREQ_44100 ;
break ;
case BT_SBC_SAMPLING_FREQ_48000 :
a2dp - > sbc . frequency = SBC_FREQ_48000 ;
break ;
default :
pa_assert_not_reached ( ) ;
}
2008-08-11 13:27:13 -03:00
2009-02-02 01:58:48 +01:00
switch ( active_capabilities - > channel_mode ) {
case BT_A2DP_CHANNEL_MODE_MONO :
a2dp - > sbc . mode = SBC_MODE_MONO ;
break ;
case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL :
a2dp - > sbc . mode = SBC_MODE_DUAL_CHANNEL ;
break ;
case BT_A2DP_CHANNEL_MODE_STEREO :
a2dp - > sbc . mode = SBC_MODE_STEREO ;
break ;
case BT_A2DP_CHANNEL_MODE_JOINT_STEREO :
a2dp - > sbc . mode = SBC_MODE_JOINT_STEREO ;
break ;
default :
pa_assert_not_reached ( ) ;
}
2008-08-11 13:27:13 -03:00
2009-02-02 01:58:48 +01:00
switch ( active_capabilities - > allocation_method ) {
case BT_A2DP_ALLOCATION_SNR :
a2dp - > sbc . allocation = SBC_AM_SNR ;
break ;
case BT_A2DP_ALLOCATION_LOUDNESS :
a2dp - > sbc . allocation = SBC_AM_LOUDNESS ;
break ;
default :
pa_assert_not_reached ( ) ;
}
2008-08-11 13:27:13 -03:00
2009-02-02 01:58:48 +01:00
switch ( active_capabilities - > subbands ) {
2008-08-11 13:27:13 -03:00
case BT_A2DP_SUBBANDS_4 :
a2dp - > sbc . subbands = SBC_SB_4 ;
break ;
case BT_A2DP_SUBBANDS_8 :
a2dp - > sbc . subbands = SBC_SB_8 ;
break ;
2009-02-02 01:58:48 +01:00
default :
pa_assert_not_reached ( ) ;
2008-08-11 13:27:13 -03:00
}
2009-02-02 01:58:48 +01:00
switch ( active_capabilities - > block_length ) {
2008-08-11 13:27:13 -03:00
case BT_A2DP_BLOCK_LENGTH_4 :
a2dp - > sbc . blocks = SBC_BLK_4 ;
break ;
case BT_A2DP_BLOCK_LENGTH_8 :
a2dp - > sbc . blocks = SBC_BLK_8 ;
break ;
case BT_A2DP_BLOCK_LENGTH_12 :
a2dp - > sbc . blocks = SBC_BLK_12 ;
break ;
case BT_A2DP_BLOCK_LENGTH_16 :
a2dp - > sbc . blocks = SBC_BLK_16 ;
break ;
2009-02-02 01:58:48 +01:00
default :
pa_assert_not_reached ( ) ;
2008-08-11 13:27:13 -03:00
}
2009-02-02 01:58:48 +01:00
a2dp - > sbc . bitpool = active_capabilities - > max_bitpool ;
2009-03-20 18:04:23 +01:00
a2dp - > codesize = sbc_get_codesize ( & a2dp - > sbc ) ;
a2dp - > frame_length = sbc_get_frame_length ( & a2dp - > sbc ) ;
2008-08-11 13:27:13 -03:00
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-02-02 01:58:48 +01:00
static int set_conf ( struct userdata * u ) {
2008-08-25 21:56:31 -03:00
union {
2009-03-25 17:57:19 -03:00
struct bt_open_req open_req ;
struct bt_open_rsp open_rsp ;
2008-12-17 17:19:22 -03:00
struct bt_set_configuration_req setconf_req ;
struct bt_set_configuration_rsp setconf_rsp ;
2009-02-12 16:51:02 +01:00
bt_audio_error_t error ;
2008-12-17 17:19:22 -03:00
uint8_t buf [ BT_SUGGESTED_BUFFER_SIZE ] ;
2008-08-25 21:56:31 -03:00
} msg ;
2008-08-11 13:27:13 -03:00
2009-03-25 17:57:19 -03:00
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . open_req . h . type = BT_REQUEST ;
msg . open_req . h . name = BT_OPEN ;
msg . open_req . h . length = sizeof ( msg . open_req ) ;
pa_strlcpy ( msg . open_req . object , u - > path , sizeof ( msg . open_req . object ) ) ;
msg . open_req . seid = u - > profile = = PROFILE_A2DP ? u - > a2dp . sbc_capabilities . capability . seid : BT_A2DP_SEID_RANGE + 1 ;
msg . open_req . lock = u - > profile = = PROFILE_A2DP ? BT_WRITE_LOCK : BT_READ_LOCK | BT_WRITE_LOCK ;
if ( service_send ( u , & msg . open_req . h ) < 0 )
return - 1 ;
if ( service_expect ( u , & msg . open_rsp . h , sizeof ( msg ) , BT_OPEN , sizeof ( msg . open_rsp ) ) < 0 )
return - 1 ;
2009-02-02 01:58:48 +01:00
if ( u - > profile = = PROFILE_A2DP ) {
u - > sample_spec . format = PA_SAMPLE_S16LE ;
if ( setup_a2dp ( u ) < 0 )
return - 1 ;
} else {
pa_assert ( u - > profile = = PROFILE_HSP ) ;
u - > sample_spec . format = PA_SAMPLE_S16LE ;
u - > sample_spec . channels = 1 ;
u - > sample_spec . rate = 8000 ;
2009-01-19 10:19:53 -03:00
}
2008-08-11 13:27:13 -03:00
2009-02-02 01:58:48 +01:00
memset ( & msg , 0 , sizeof ( msg ) ) ;
2008-12-17 17:19:22 -03:00
msg . setconf_req . h . type = BT_REQUEST ;
msg . setconf_req . h . name = BT_SET_CONFIGURATION ;
msg . setconf_req . h . length = sizeof ( msg . setconf_req ) ;
2009-02-02 01:58:48 +01:00
if ( u - > profile = = PROFILE_A2DP ) {
memcpy ( & msg . setconf_req . codec , & u - > a2dp . sbc_capabilities , sizeof ( u - > a2dp . sbc_capabilities ) ) ;
2009-03-25 17:57:19 -03:00
} else {
msg . setconf_req . codec . transport = BT_CAPABILITIES_TRANSPORT_SCO ;
msg . setconf_req . codec . seid = BT_A2DP_SEID_RANGE + 1 ;
msg . setconf_req . codec . length = sizeof ( pcm_capabilities_t ) ;
2009-01-06 11:02:16 -03:00
}
2009-03-25 17:57:19 -03:00
msg . setconf_req . h . length + = msg . setconf_req . codec . length - sizeof ( msg . setconf_req . codec ) ;
2008-08-11 13:27:13 -03:00
2009-02-12 21:45:17 +01:00
if ( service_send ( u , & msg . setconf_req . h ) < 0 )
2009-02-02 01:58:48 +01:00
return - 1 ;
2009-02-12 21:45:17 +01:00
if ( service_expect ( u , & msg . setconf_rsp . h , sizeof ( msg ) , BT_SET_CONFIGURATION , sizeof ( msg . setconf_rsp ) ) < 0 )
2009-02-02 01:58:48 +01:00
return - 1 ;
2008-08-25 21:56:31 -03:00
u - > link_mtu = msg . setconf_rsp . link_mtu ;
2008-08-11 13:27:13 -03:00
/* setup SBC encoder now we agree on parameters */
2009-02-02 01:58:48 +01:00
if ( u - > profile = = PROFILE_A2DP ) {
setup_sbc ( & u - > a2dp ) ;
2009-03-20 18:04:23 +01:00
u - > block_size =
( ( u - > link_mtu - sizeof ( struct rtp_header ) - sizeof ( struct rtp_payload ) )
/ u - > a2dp . frame_length
* u - > a2dp . codesize ) ;
2009-02-02 01:58:48 +01:00
pa_log_info ( " SBC parameters: \n \t allocation=%u \n \t subbands=%u \n \t blocks=%u \n \t bitpool=%u \n " ,
u - > a2dp . sbc . allocation , u - > a2dp . sbc . subbands , u - > a2dp . sbc . blocks , u - > a2dp . sbc . bitpool ) ;
} else
2008-08-17 01:54:12 -03:00
u - > block_size = u - > link_mtu ;
2008-08-11 13:27:13 -03:00
return 0 ;
}
2009-03-06 14:45:06 +02:00
/* from IO thread, except in SCO over PCM */
2009-02-17 12:59:34 +02:00
static int start_stream_fd ( struct userdata * u ) {
2008-08-25 21:56:31 -03:00
union {
2008-12-17 17:19:22 -03:00
bt_audio_msg_header_t rsp ;
struct bt_start_stream_req start_req ;
struct bt_start_stream_rsp start_rsp ;
struct bt_new_stream_ind streamfd_ind ;
2009-02-12 16:51:02 +01:00
bt_audio_error_t error ;
2008-12-17 17:19:22 -03:00
uint8_t buf [ BT_SUGGESTED_BUFFER_SIZE ] ;
2008-08-25 21:56:31 -03:00
} msg ;
2009-02-17 12:59:34 +02:00
struct pollfd * pollfd ;
2009-04-10 01:30:50 +02:00
int one ;
2008-08-11 13:27:13 -03:00
2009-02-02 01:58:48 +01:00
pa_assert ( u ) ;
2009-02-17 12:59:34 +02:00
pa_assert ( u - > rtpoll ) ;
pa_assert ( ! u - > rtpoll_item ) ;
2009-02-02 01:58:48 +01:00
pa_assert ( u - > stream_fd < 0 ) ;
2008-12-17 17:19:22 -03:00
memset ( msg . buf , 0 , BT_SUGGESTED_BUFFER_SIZE ) ;
msg . start_req . h . type = BT_REQUEST ;
msg . start_req . h . name = BT_START_STREAM ;
msg . start_req . h . length = sizeof ( msg . start_req ) ;
2008-08-11 13:27:13 -03:00
2009-02-12 21:45:17 +01:00
if ( service_send ( u , & msg . start_req . h ) < 0 )
2009-02-02 01:58:48 +01:00
return - 1 ;
2008-08-11 13:27:13 -03:00
2009-02-12 21:45:17 +01:00
if ( service_expect ( u , & msg . rsp , sizeof ( msg ) , BT_START_STREAM , sizeof ( msg . start_rsp ) ) < 0 )
2009-02-02 01:58:48 +01:00
return - 1 ;
2008-08-11 13:27:13 -03:00
2009-02-12 21:45:17 +01:00
if ( service_expect ( u , & msg . rsp , sizeof ( msg ) , BT_NEW_STREAM , sizeof ( msg . streamfd_ind ) ) < 0 )
2009-02-02 01:58:48 +01:00
return - 1 ;
2008-08-11 13:27:13 -03:00
2009-02-02 01:58:48 +01:00
if ( ( u - > stream_fd = bt_audio_service_get_data_fd ( u - > service_fd ) ) < 0 ) {
pa_log ( " Failed to get stream fd from audio service. " ) ;
return - 1 ;
2008-08-11 13:27:13 -03:00
}
2008-08-14 20:57:32 -03:00
pa_make_fd_nonblock ( u - > stream_fd ) ;
2009-02-02 01:58:48 +01:00
pa_make_socket_low_delay ( u - > stream_fd ) ;
2008-08-11 13:27:13 -03:00
2009-04-10 01:30:50 +02:00
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! " ) ;
2009-02-17 12:59:34 +02: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 ;
2009-04-10 01:30:50 +02:00
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 ,
TRUE ,
TRUE ,
10 ,
2009-04-04 22:56:38 +03:00
pa_rtclock_now ( ) ,
2009-04-10 01:30:50 +02:00
TRUE ) ;
2009-03-05 15:47:13 +02:00
2009-02-17 12:59:34 +02:00
return 0 ;
}
/* from IO thread */
static int stop_stream_fd ( struct userdata * u ) {
union {
bt_audio_msg_header_t rsp ;
struct bt_stop_stream_req start_req ;
struct bt_stop_stream_rsp start_rsp ;
bt_audio_error_t error ;
uint8_t buf [ BT_SUGGESTED_BUFFER_SIZE ] ;
} msg ;
2009-02-22 02:00:25 +01:00
int r = 0 ;
2009-02-17 12:59:34 +02:00
pa_assert ( u ) ;
pa_assert ( u - > rtpoll ) ;
pa_assert ( u - > rtpoll_item ) ;
pa_assert ( u - > stream_fd > = 0 ) ;
pa_rtpoll_item_free ( u - > rtpoll_item ) ;
u - > rtpoll_item = NULL ;
memset ( msg . buf , 0 , BT_SUGGESTED_BUFFER_SIZE ) ;
msg . start_req . h . type = BT_REQUEST ;
msg . start_req . h . name = BT_STOP_STREAM ;
msg . start_req . h . length = sizeof ( msg . start_req ) ;
2009-02-22 02:00:25 +01:00
if ( service_send ( u , & msg . start_req . h ) < 0 | |
service_expect ( u , & msg . rsp , sizeof ( msg ) , BT_STOP_STREAM , sizeof ( msg . start_rsp ) ) < 0 )
r = - 1 ;
2009-02-17 12:59:34 +02:00
pa_close ( u - > stream_fd ) ;
u - > stream_fd = - 1 ;
2009-04-10 01:30:50 +02:00
if ( u - > read_smoother ) {
pa_smoother_free ( u - > read_smoother ) ;
u - > read_smoother = NULL ;
}
2009-02-22 02:00:25 +01:00
return r ;
2008-08-11 13:27:13 -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 ;
2009-02-22 02:00:25 +01:00
pa_bool_t failed = FALSE ;
int r ;
2009-02-02 01:58:48 +01:00
pa_assert ( u - > sink = = PA_SINK ( o ) ) ;
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 :
pa_assert ( PA_SINK_IS_OPENED ( u - > sink - > thread_info . state ) ) ;
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 */
if ( ! u - > source | | u - > source - > state = = PA_SOURCE_SUSPENDED )
/* We deliberately ignore whether stopping
* actually worked . Since the stream_fd is
* closed it doesn ' t really matter */
stop_stream_fd ( 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 */
if ( ! u - > source | | u - > source - > state = = PA_SOURCE_SUSPENDED )
if ( start_stream_fd ( u ) < 0 )
failed = TRUE ;
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 ( ) ) ;
2009-04-10 01:30:50 +02:00
wi = pa_bytes_to_usec ( u - > write_index + u - > block_size , & u - > sample_spec ) ;
* ( ( 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 ;
2009-02-22 02:00:25 +01:00
pa_bool_t failed = FALSE ;
int r ;
2009-02-02 01:58:48 +01:00
pa_assert ( u - > source = = PA_SOURCE ( o ) ) ;
switch ( code ) {
case PA_SOURCE_MESSAGE_SET_STATE :
switch ( ( pa_source_state_t ) PA_PTR_TO_UINT ( data ) ) {
case PA_SOURCE_SUSPENDED :
2009-02-22 02:00:25 +01:00
pa_assert ( PA_SOURCE_IS_OPENED ( u - > source - > thread_info . state ) ) ;
/* Stop the device if the sink is suspended as well */
if ( ! u - > sink | | u - > sink - > state = = PA_SINK_SUSPENDED )
stop_stream_fd ( u ) ;
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 */
if ( ! u - > sink | | u - > sink - > thread_info . state = = PA_SINK_SUSPENDED )
if ( start_stream_fd ( u ) < 0 )
failed = TRUE ;
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 ;
2009-04-04 22:56:38 +03:00
wi = pa_smoother_get ( u - > read_smoother , pa_rtclock_now ( ) ) ;
2009-04-10 01:30:50 +02:00
ri = pa_bytes_to_usec ( u - > read_index , & u - > sample_spec ) ;
2009-08-15 00:48:14 +02:00
* ( ( pa_usec_t * ) data ) = ( wi > ri ? wi - ri : 0 ) + u - > source - > thread_info . fixed_latency ;
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
}
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 ) ;
pa_assert ( u - > profile = = PROFILE_HSP ) ;
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 )
pa_sink_render_full ( u - > sink , u - > block_size , & u - > write_memchunk ) ;
pa_assert ( u - > write_memchunk . length = = u - > 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
2009-03-20 18:04:23 +01:00
p = ( const uint8_t * ) pa_memblock_acquire ( u - > write_memchunk . memblock ) + u - > write_memchunk . index ;
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 ) ;
pa_assert ( u - > profile = = PROFILE_HSP ) ;
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
memchunk . memblock = pa_memblock_new ( u - > core - > mempool , u - > block_size ) ;
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 ;
pa_bool_t found_tstamp = FALSE ;
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
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 ) ;
found_tstamp = TRUE ;
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 ) ) ;
pa_smoother_resume ( u - > read_smoother , tstamp , TRUE ) ;
2009-03-20 18:04:23 +01:00
pa_source_post ( u - > source , & memchunk ) ;
2009-04-10 01:30:50 +02:00
ret = 1 ;
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 ) {
pa_assert ( u ) ;
if ( u - > a2dp . buffer_size > = u - > link_mtu )
return ;
u - > a2dp . buffer_size = 2 * u - > link_mtu ;
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 ) ;
2009-02-02 01:58:48 +01:00
pa_assert ( u - > profile = = PROFILE_A2DP ) ;
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 )
pa_sink_render_full ( u - > sink , u - > block_size , & u - > write_memchunk ) ;
2008-08-21 21:31:43 -03:00
2009-03-20 18:04:23 +01:00
pa_assert ( u - > write_memchunk . length = = u - > block_size ) ;
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
2009-03-20 18:04:23 +01:00
p = ( const uint8_t * ) pa_memblock_acquire ( u - > write_memchunk . memblock ) + u - > write_memchunk . index ;
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 ) ) {
size_t written ;
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 ) ) ;
ret = - 1 ;
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
}
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 ;
pa_bool_t writable = FALSE ;
2008-08-17 20:36:33 -03:00
pa_assert ( u ) ;
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 ) ;
2009-02-17 12:59:34 +02:00
if ( start_stream_fd ( u ) < 0 )
goto fail ;
2008-08-17 20:36:33 -03:00
for ( ; ; ) {
struct pollfd * pollfd ;
2009-02-02 01:58:48 +01:00
int ret ;
2009-02-22 02:00:25 +01:00
pa_bool_t 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
2009-04-10 01:30:50 +02:00
if ( ( n_read = hsp_process_push ( u ) ) < 0 )
2008-08-26 09:43:48 -03:00
goto fail ;
2009-02-02 01:58:48 +01:00
/* We just read something, so we are supposed to write something, too */
2009-04-10 01:30:50 +02:00
do_write + = n_read ;
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 ) ) {
if ( u - > sink - > thread_info . rewind_requested )
pa_sink_process_rewind ( u - > sink , 0 ) ;
2009-02-22 02:00:25 +01:00
if ( pollfd ) {
if ( pollfd - > revents & POLLOUT )
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 ;
}
2009-05-08 13:25:21 +03:00
}
do_write = 1 ;
}
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
2009-02-22 02:00:25 +01:00
if ( u - > profile = = PROFILE_A2DP ) {
2009-04-10 01:30:50 +02:00
if ( ( n_written = a2dp_process_render ( u ) ) < 0 )
2009-02-22 02:00:25 +01:00
goto fail ;
} else {
2009-04-10 01:30:50 +02:00
if ( ( n_written = hsp_process_render ( u ) ) < 0 )
2009-02-22 02:00:25 +01:00
goto fail ;
}
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 ;
2009-02-22 02:00:25 +01:00
writable = FALSE ;
}
2009-02-02 01:58:48 +01:00
2009-07-01 02:21:17 +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 , next_write_at , sleep_for ;
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 estimate when we need to wake up the latest */
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-04-10 01:30:50 +02:00
next_write_at = pa_bytes_to_usec ( u - > write_index , & u - > sample_spec ) ;
2009-02-22 02:00:25 +01:00
sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0 ;
2009-02-02 01:58:48 +01:00
/* 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); */
2009-02-22 02:00:25 +01:00
pa_rtpoll_set_timer_relative ( u - > rtpoll , sleep_for ) ;
disable_timer = FALSE ;
}
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
if ( ( ret = pa_rtpoll_run ( u - > rtpoll , TRUE ) ) < 0 )
2008-08-17 20:36:33 -03:00
goto fail ;
2009-02-02 01:58:48 +01:00
if ( ret = = 0 )
2008-08-17 20:36:33 -03:00
goto finish ;
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 " : " " ) ;
2008-08-17 20:36:33 -03:00
goto fail ;
}
}
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 " ) ;
2008-08-17 20:36:33 -03:00
pa_asyncmsgq_post ( u - > thread_mq . outq , PA_MSGOBJECT ( u - > core ) , PA_CORE_MESSAGE_UNLOAD_MODULE , u - > module , 0 , NULL , NULL ) ;
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
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-03-21 02:54:18 +01:00
static DBusHandlerResult filter_cb ( DBusConnection * bus , DBusMessage * m , void * userdata ) {
DBusError err ;
struct userdata * u ;
2009-02-02 01:58:48 +01:00
2009-03-21 02:54:18 +01:00
pa_assert ( bus ) ;
pa_assert ( m ) ;
pa_assert_se ( u = userdata ) ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
dbus_error_init ( & err ) ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
pa_log_debug ( " dbus: interface=%s, path=%s, member=%s \n " ,
dbus_message_get_interface ( m ) ,
dbus_message_get_path ( m ) ,
dbus_message_get_member ( m ) ) ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
if ( ! dbus_message_has_path ( m , u - > path ) )
goto fail ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
if ( dbus_message_is_signal ( m , " org.bluez.Headset " , " SpeakerGainChanged " ) | |
dbus_message_is_signal ( m , " org.bluez.Headset " , " MicrophoneGainChanged " ) ) {
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
dbus_uint16_t gain ;
pa_cvolume v ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
if ( ! dbus_message_get_args ( m , & err , DBUS_TYPE_UINT16 , & gain , DBUS_TYPE_INVALID ) | | gain > 15 ) {
pa_log ( " Failed to parse org.bluez.Headset.{Speaker|Microphone}GainChanged: %s " , err . message ) ;
goto fail ;
}
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
if ( u - > profile = = PROFILE_HSP ) {
if ( u - > sink & & dbus_message_is_signal ( m , " org.bluez.Headset " , " SpeakerGainChanged " ) ) {
2009-01-19 14:53:35 +02:00
2009-04-10 01:30:50 +02:00
pa_cvolume_set ( & v , u - > sample_spec . channels , ( pa_volume_t ) ( gain * PA_VOLUME_NORM / 15 ) ) ;
2009-08-07 23:55:06 +02:00
pa_sink_volume_changed ( u - > sink , & v ) ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
} else if ( u - > source & & dbus_message_is_signal ( m , " org.bluez.Headset " , " MicrophoneGainChanged " ) ) {
2009-01-19 14:53:35 +02:00
2009-04-10 01:30:50 +02:00
pa_cvolume_set ( & v , u - > sample_spec . channels , ( pa_volume_t ) ( gain * PA_VOLUME_NORM / 15 ) ) ;
2009-08-07 23:55:06 +02:00
pa_source_volume_changed ( u - > source , & v ) ;
2009-03-21 02:54:18 +01:00
}
}
}
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
fail :
dbus_error_free ( & err ) ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED ;
}
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 sink_set_volume_cb ( pa_sink * s ) {
struct userdata * u = s - > userdata ;
DBusMessage * m ;
dbus_uint16_t gain ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
pa_assert ( u ) ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
if ( u - > profile ! = PROFILE_HSP )
return ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
gain = ( pa_cvolume_max ( & s - > virtual_volume ) * 15 ) / PA_VOLUME_NORM ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
if ( gain > 15 )
gain = 15 ;
2009-01-19 14:53:35 +02:00
2009-04-10 01:30:50 +02:00
pa_cvolume_set ( & s - > virtual_volume , u - > sample_spec . channels , ( pa_volume_t ) ( gain * PA_VOLUME_NORM / 15 ) ) ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
pa_assert_se ( m = dbus_message_new_method_call ( " org.bluez " , u - > path , " org.bluez.Headset " , " SetSpeakerGain " ) ) ;
pa_assert_se ( dbus_message_append_args ( m , DBUS_TYPE_UINT16 , & gain , DBUS_TYPE_INVALID ) ) ;
pa_assert_se ( dbus_connection_send ( pa_dbus_connection_get ( u - > connection ) , m , NULL ) ) ;
dbus_message_unref ( m ) ;
}
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 ) {
struct userdata * u = s - > userdata ;
DBusMessage * m ;
dbus_uint16_t gain ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
pa_assert ( u ) ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
if ( u - > profile ! = PROFILE_HSP )
return ;
2009-01-19 14:53:35 +02:00
2009-08-19 01:35:43 +02:00
gain = ( pa_cvolume_max ( & s - > volume ) * 15 ) / PA_VOLUME_NORM ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
if ( gain > 15 )
gain = 15 ;
2009-01-19 14:53:35 +02:00
2009-08-19 01:35:43 +02:00
pa_cvolume_set ( & s - > volume , u - > sample_spec . channels , ( pa_volume_t ) ( gain * PA_VOLUME_NORM / 15 ) ) ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
pa_assert_se ( m = dbus_message_new_method_call ( " org.bluez " , u - > path , " org.bluez.Headset " , " SetMicrophoneGain " ) ) ;
pa_assert_se ( dbus_message_append_args ( m , DBUS_TYPE_UINT16 , & gain , DBUS_TYPE_INVALID ) ) ;
pa_assert_se ( dbus_connection_send ( pa_dbus_connection_get ( u - > connection ) , m , NULL ) ) ;
dbus_message_unref ( m ) ;
}
2009-01-19 14:53:35 +02:00
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-01-29 16:27:27 +01:00
static char * get_name ( const char * type , pa_modargs * ma , const char * device_id , pa_bool_t * namereg_fail ) {
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 ) {
* namereg_fail = TRUE ;
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 ) ) )
* namereg_fail = TRUE ;
else {
n = device_id ;
* 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 ) ;
}
2009-03-21 01:31:38 +01:00
# ifdef NOKIA
2009-02-03 18:17:20 +02:00
static void sco_over_pcm_state_update ( struct userdata * u ) {
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 ) ) ) {
2009-02-27 00:13:54 +02:00
if ( u - > service_fd > = 0 )
2009-02-03 18:17:20 +02:00
return ;
pa_log_debug ( " Resuming SCO over PCM " ) ;
if ( ( init_bt ( u ) < 0 ) | | ( init_profile ( u ) < 0 ) )
pa_log ( " Can't resume SCO over PCM " ) ;
2009-03-06 14:45:06 +02:00
start_stream_fd ( u ) ;
2009-02-03 18:17:20 +02:00
} else {
2009-02-27 00:13:54 +02:00
if ( u - > service_fd < 0 )
2009-02-03 18:17:20 +02:00
return ;
2009-03-06 14:45:06 +02:00
stop_stream_fd ( u ) ;
2009-02-03 18:17:20 +02:00
pa_log_debug ( " Closing SCO over PCM " ) ;
pa_close ( u - > service_fd ) ;
2009-02-12 03:56:01 +01:00
u - > service_fd = - 1 ;
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 ) ;
if ( s ! = u - > hsp . sco_sink )
return PA_HOOK_OK ;
sco_over_pcm_state_update ( u ) ;
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 ) ;
if ( s ! = u - > hsp . sco_source )
return PA_HOOK_OK ;
sco_over_pcm_state_update ( u ) ;
return PA_HOOK_OK ;
}
2009-03-21 01:31:38 +01:00
# endif
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 ) {
2009-03-21 01:31:38 +01:00
# ifdef NOKIA
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 ( ) ;
pa_proplist_sets ( p , " bluetooth.protocol " , " sco " ) ;
pa_proplist_update ( u - > sink - > proplist , PA_UPDATE_MERGE , p ) ;
pa_proplist_free ( p ) ;
2009-01-29 16:27:27 +01:00
2009-02-12 03:57:59 +01:00
if ( ! u - > hsp . sink_state_changed_slot )
2009-02-03 18:17:20 +02:00
u - > hsp . 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 ) ;
2009-03-21 01:31:38 +01:00
} else
# endif
{
2009-02-03 17:15:41 +02:00
pa_sink_new_data data ;
pa_bool_t b ;
pa_sink_new_data_init ( & data ) ;
data . driver = __FILE__ ;
data . module = u - > module ;
pa_sink_new_data_set_sample_spec ( & data , & u - > sample_spec ) ;
pa_proplist_sets ( data . proplist , " bluetooth.protocol " , u - > profile = = PROFILE_A2DP ? " a2dp " : " sco " ) ;
2009-06-08 16:58:45 +02:00
if ( u - > profile = = PROFILE_HSP )
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 ;
}
2009-03-21 02:54:18 +01:00
u - > sink = pa_sink_new ( u - > core , & data , PA_SINK_HARDWARE | PA_SINK_LATENCY | ( u - > profile = = PROFILE_HSP ? PA_SINK_HW_VOLUME_CTRL : 0 ) ) ;
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 ;
2009-04-08 04:15:42 +02:00
pa_sink_set_max_request ( u - > sink , u - > block_size ) ;
2009-05-08 01:56:21 +02:00
pa_sink_set_fixed_latency ( u - > sink ,
( u - > profile = = PROFILE_A2DP ? FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_HSP ) +
pa_bytes_to_usec ( u - > block_size , & u - > sample_spec ) ) ;
2008-08-11 13:27:13 -03:00
}
2008-09-29 21:40:52 +02:00
2009-03-21 02:54:18 +01:00
if ( u - > profile = = PROFILE_HSP ) {
u - > sink - > set_volume = sink_set_volume_cb ;
u - > sink - > n_volume_steps = 16 ;
}
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 ) {
2009-03-21 01:31:38 +01:00
# ifdef NOKIA
2009-02-03 17:15:41 +02:00
if ( USE_SCO_OVER_PCM ( u ) ) {
u - > source = u - > hsp . sco_source ;
2009-04-10 01:28:21 +02:00
pa_proplist_sets ( u - > source - > proplist , " bluetooth.protocol " , " hsp " ) ;
2009-01-29 16:27:27 +01:00
2009-02-03 18:17:20 +02:00
if ( ! u - > hsp . source_state_changed_slot )
u - > hsp . 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 ) ;
2009-01-29 16:27:27 +01:00
2009-03-21 01:31:38 +01:00
} else
# endif
{
2009-02-03 17:15:41 +02:00
pa_source_new_data data ;
pa_bool_t b ;
pa_source_new_data_init ( & data ) ;
data . driver = __FILE__ ;
data . module = u - > module ;
pa_source_new_data_set_sample_spec ( & data , & u - > sample_spec ) ;
2009-04-10 01:28:21 +02:00
pa_proplist_sets ( data . proplist , " bluetooth.protocol " , u - > profile = = PROFILE_A2DP ? " a2dp " : " hsp " ) ;
2009-06-08 16:58:45 +02:00
if ( u - > profile = = PROFILE_HSP )
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 ( " 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 ;
}
2009-03-21 02:54:18 +01:00
u - > source = pa_source_new ( u - > core , & data , PA_SOURCE_HARDWARE | PA_SOURCE_LATENCY | ( u - > profile = = PROFILE_HSP ? PA_SOURCE_HW_VOLUME_CTRL : 0 ) ) ;
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 ;
2009-04-10 01:30:50 +02:00
2009-05-08 01:56:21 +02:00
pa_source_set_fixed_latency ( u - > source ,
( /* u->profile == PROFILE_A2DP ? FIXED_LATENCY_RECORD_A2DP : */ FIXED_LATENCY_RECORD_HSP ) +
pa_bytes_to_usec ( u - > block_size , & u - > sample_spec ) ) ;
2008-08-11 13:27:13 -03:00
}
2009-01-29 16:27:27 +01:00
2009-03-21 02:54:18 +01:00
if ( u - > profile = = PROFILE_HSP ) {
pa_proplist_sets ( u - > source - > proplist , " bluetooth.nrec " , ( u - > hsp . pcm_capabilities . flags & BT_PCM_FLAG_NREC ) ? " 1 " : " 0 " ) ;
u - > source - > set_volume = source_set_volume_cb ;
u - > source - > n_volume_steps = 16 ;
}
2009-01-29 16:27:27 +01:00
return 0 ;
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-02-12 22:09:00 +01:00
static void shutdown_bt ( struct userdata * u ) {
2009-01-29 16:27:27 +01:00
pa_assert ( u ) ;
2008-07-31 20:16:43 -03:00
2009-02-09 22:11:46 +02:00
if ( u - > stream_fd > = 0 ) {
pa_close ( u - > stream_fd ) ;
u - > stream_fd = - 1 ;
2009-03-20 18:04:23 +01:00
u - > stream_write_type = 0 ;
2009-02-09 22:11:46 +02:00
}
if ( u - > service_fd > = 0 ) {
pa_close ( u - > service_fd ) ;
u - > service_fd = - 1 ;
2009-07-20 13:44:27 +03:00
u - > service_write_type = 0 ;
u - > service_read_type = 0 ;
2009-02-09 22:11:46 +02:00
}
2009-03-20 18:04:23 +01:00
if ( u - > write_memchunk . memblock ) {
pa_memblock_unref ( u - > write_memchunk . memblock ) ;
pa_memchunk_reset ( & u - > write_memchunk ) ;
}
2009-02-12 22:09:00 +01:00
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-02-12 22:09:00 +01:00
static int init_bt ( struct userdata * u ) {
pa_assert ( u ) ;
shutdown_bt ( u ) ;
2009-02-09 22:11:46 +02:00
2009-04-10 01:30:50 +02:00
u - > stream_write_type = 0 ;
2009-07-20 13:44:27 +03:00
u - > service_write_type = 0 ;
u - > service_read_type = 0 ;
2009-02-09 22:11:46 +02:00
2009-02-02 01:58:48 +01:00
if ( ( u - > service_fd = bt_audio_service_open ( ) ) < 0 ) {
2008-09-29 21:43:28 +02:00
pa_log_error ( " Couldn't connect to bluetooth audio service " ) ;
2009-01-29 16:27:27 +01:00
return - 1 ;
2008-07-31 20:16:43 -03:00
}
2009-02-12 22:09:00 +01:00
2008-09-29 21:43:28 +02:00
pa_log_debug ( " Connected to the bluetooth audio service " ) ;
2008-07-31 20:16:43 -03:00
2009-02-02 01:58:48 +01:00
return 0 ;
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-02-02 01:58:48 +01:00
static int setup_bt ( struct userdata * u ) {
pa_assert ( u ) ;
2009-03-24 12:04:52 -03:00
if ( get_caps ( u , 0 ) < 0 )
2009-02-02 01:58:48 +01:00
return - 1 ;
2008-09-29 21:43:28 +02:00
pa_log_debug ( " Got device capabilities " ) ;
2008-08-11 13:27:13 -03:00
2009-02-02 01:58:48 +01:00
if ( set_conf ( u ) < 0 )
return - 1 ;
pa_log_debug ( " Connection to the device configured " ) ;
2009-03-21 01:31:38 +01:00
# ifdef NOKIA
2009-02-03 17:15:41 +02:00
if ( USE_SCO_OVER_PCM ( u ) ) {
pa_log_debug ( " Configured to use SCO over PCM " ) ;
return 0 ;
}
2009-03-21 01:31:38 +01:00
# endif
2009-02-03 17:15:41 +02:00
2009-02-02 01:58:48 +01:00
pa_log_debug ( " Got the stream socket " ) ;
2009-01-29 16:27:27 +01:00
return 0 ;
}
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 ) ;
2009-03-04 16:38:02 +02:00
pa_assert ( u - > profile ! = PROFILE_OFF ) ;
2009-01-29 16:27:27 +01:00
2009-02-02 01:58:48 +01:00
if ( setup_bt ( u ) < 0 )
2009-01-29 16:27:27 +01:00
return - 1 ;
2009-02-02 01:58:48 +01:00
if ( u - > profile = = PROFILE_A2DP | |
u - > profile = = PROFILE_HSP )
if ( add_sink ( u ) < 0 )
r = - 1 ;
if ( u - > profile = = PROFILE_HSP )
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 ) {
pa_assert ( u ) ;
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 ;
}
2009-02-03 18:17:20 +02:00
if ( u - > hsp . sink_state_changed_slot ) {
pa_hook_slot_free ( u - > hsp . sink_state_changed_slot ) ;
u - > hsp . sink_state_changed_slot = NULL ;
}
if ( u - > hsp . source_state_changed_slot ) {
pa_hook_slot_free ( u - > hsp . source_state_changed_slot ) ;
u - > hsp . source_state_changed_slot = NULL ;
}
2009-02-02 01:58:48 +01:00
if ( u - > sink ) {
pa_sink_unref ( u - > sink ) ;
u - > sink = NULL ;
}
if ( u - > source ) {
pa_source_unref ( u - > source ) ;
u - > source = NULL ;
}
2009-02-12 22:52:02 +01:00
if ( u - > rtpoll ) {
2009-02-13 12:18:14 +02:00
pa_thread_mq_done ( & u - > thread_mq ) ;
2009-02-12 22:52:02 +01:00
pa_rtpoll_free ( u - > rtpoll ) ;
u - > rtpoll = NULL ;
}
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-03-21 01:31:38 +01:00
# ifdef NOKIA
2009-02-03 17:15:41 +02:00
if ( USE_SCO_OVER_PCM ( u ) ) {
2009-03-06 14:45:06 +02:00
if ( start_stream_fd ( u ) < 0 )
return - 1 ;
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 ;
}
2009-03-21 01:31:38 +01:00
# endif
2009-02-03 17:15:41 +02:00
2009-02-02 01:58:48 +01:00
if ( ! ( u - > thread = pa_thread_new ( thread_func , u ) ) ) {
pa_log_error ( " Failed to create IO thread " ) ;
stop_thread ( u ) ;
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
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 ;
enum profile * d ;
pa_queue * inputs = NULL , * outputs = NULL ;
2009-03-30 20:57:12 +02:00
const pa_bluetooth_device * device ;
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 ) ;
2009-03-30 20:57:12 +02:00
if ( ! ( device = pa_bluetooth_discovery_get_by_path ( u - > discovery , u - > path ) ) ) {
pa_log_error ( " Failed to get device object. " ) ;
2009-06-17 04:51:57 +02:00
return - PA_ERR_IO ;
2009-03-30 20:57:12 +02:00
}
2009-04-04 17:56:36 +03:00
/* The state signal is sent by bluez, so it is racy to check
strictly for CONNECTED , we should also accept STREAMING state
as being good enough . However , if the profile is used
concurrently ( which is unlikely ) , ipc will fail later on , and
module will be unloaded . */
if ( device - > headset_state < PA_BT_AUDIO_STATE_CONNECTED & & * d = = PROFILE_HSP ) {
2009-03-24 14:38:52 +02:00
pa_log_warn ( " HSP is not connected, refused to switch profile " ) ;
2009-06-17 04:51:57 +02:00
return - PA_ERR_IO ;
2009-03-24 14:38:52 +02:00
}
2009-04-04 17:56:36 +03:00
else if ( device - > audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED & & * d = = PROFILE_A2DP ) {
2009-03-24 14:38:52 +02:00
pa_log_warn ( " A2DP is not connected, refused to switch profile " ) ;
2009-06-17 04:51:57 +02:00
return - PA_ERR_IO ;
2009-03-24 14:38:52 +02:00
}
2009-01-29 16:27:27 +01:00
if ( u - > sink ) {
2009-06-17 03:45:14 +02:00
inputs = pa_sink_move_all_start ( u - > sink , NULL ) ;
2009-03-21 01:31:38 +01:00
# ifdef NOKIA
2009-02-12 03:57:59 +01:00
if ( ! USE_SCO_OVER_PCM ( u ) )
2009-03-21 01:31:38 +01:00
# endif
2009-03-24 16:28:12 +02:00
pa_sink_unlink ( u - > sink ) ;
2009-01-29 16:27:27 +01:00
}
if ( u - > source ) {
2009-06-17 03:45:14 +02:00
outputs = pa_source_move_all_start ( u - > source , NULL ) ;
2009-03-21 01:31:38 +01:00
# ifdef NOKIA
2009-02-12 03:57:59 +01:00
if ( ! USE_SCO_OVER_PCM ( u ) )
2009-03-21 01:31:38 +01:00
# endif
2009-03-24 16:28:12 +02:00
pa_source_unlink ( u - > source ) ;
2009-01-29 16:27:27 +01:00
}
2009-02-02 01:58:48 +01:00
stop_thread ( u ) ;
2009-02-12 22:09:00 +01:00
shutdown_bt ( 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
2009-02-12 22:09:00 +01:00
init_bt ( u ) ;
2009-03-04 16:38:02 +02:00
if ( u - > profile ! = PROFILE_OFF )
init_profile ( u ) ;
2009-01-29 16:27:27 +01:00
2009-02-02 01:58:48 +01:00
if ( u - > sink | | u - > source )
start_thread ( u ) ;
2009-01-29 16:27:27 +01:00
if ( inputs ) {
if ( u - > sink )
pa_sink_move_all_finish ( u - > sink , inputs , FALSE ) ;
else
pa_sink_move_all_fail ( inputs ) ;
}
if ( outputs ) {
if ( u - > source )
pa_source_move_all_finish ( u - > source , outputs , FALSE ) ;
else
pa_source_move_all_fail ( outputs ) ;
}
return 0 ;
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-05-28 02:39:22 +02:00
static int add_card ( struct userdata * u , const pa_bluetooth_device * device ) {
2009-01-29 16:27:27 +01:00
pa_card_new_data data ;
pa_bool_t b ;
pa_card_profile * p ;
enum profile * d ;
2009-02-02 01:58:48 +01:00
const char * ff ;
char * n ;
2009-05-28 02:39:22 +02:00
const char * default_profile ;
pa_assert ( u ) ;
pa_assert ( 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
2009-03-21 01:19:49 +01:00
n = pa_bluetooth_cleanup_name ( device - > name ) ;
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 " ) ;
2009-03-21 01:19:49 +01:00
if ( ( ff = pa_bluetooth_get_form_factor ( device - > class ) ) )
2009-02-02 01:58:48 +01:00
pa_proplist_sets ( data . proplist , PA_PROP_DEVICE_FORM_FACTOR , ff ) ;
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 ) ;
pa_proplist_sets ( data . proplist , " bluez.name " , device - > name ) ;
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 ;
}
2009-01-29 16:27:27 +01:00
data . profiles = pa_hashmap_new ( pa_idxset_string_hash_func , pa_idxset_string_compare_func ) ;
2009-03-27 21:48:04 +02:00
/* we base hsp/a2dp availability on UUIDs.
Ideally , it would be based on " Connected " state , but
we can ' t afford to wait for this information when
we are loaded with profile = " hsp " , for instance */
if ( pa_bluetooth_uuid_has ( device - > uuids , A2DP_SINK_UUID ) ) {
2009-02-20 01:18:37 +01:00
p = pa_card_profile_new ( " a2dp " , _ ( " High Fidelity Playback (A2DP) " ) , sizeof ( enum profile ) ) ;
2009-01-29 16:27:27 +01:00
p - > priority = 10 ;
p - > n_sinks = 1 ;
p - > n_sources = 0 ;
p - > max_sink_channels = 2 ;
p - > max_source_channels = 0 ;
d = PA_CARD_PROFILE_DATA ( p ) ;
* d = PROFILE_A2DP ;
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-03-27 21:48:04 +02:00
if ( pa_bluetooth_uuid_has ( device - > uuids , HSP_HS_UUID ) | |
2009-04-10 01:28:21 +02:00
pa_bluetooth_uuid_has ( device - > uuids , HFP_HS_UUID ) ) {
2009-02-20 01:18:37 +01:00
p = pa_card_profile_new ( " hsp " , _ ( " Telephony Duplex (HSP/HFP) " ) , sizeof ( enum profile ) ) ;
2009-01-29 16:27:27 +01:00
p - > priority = 20 ;
p - > n_sinks = 1 ;
p - > n_sources = 1 ;
p - > max_sink_channels = 1 ;
p - > max_source_channels = 1 ;
d = PA_CARD_PROFILE_DATA ( p ) ;
2009-02-02 01:58:48 +01:00
* d = PROFILE_HSP ;
pa_hashmap_put ( data . profiles , p - > name , p ) ;
2009-01-29 16:27:27 +01:00
}
pa_assert ( ! pa_hashmap_isempty ( data . profiles ) ) ;
2009-02-20 01:18:37 +01:00
p = pa_card_profile_new ( " off " , _ ( " Off " ) , sizeof ( enum profile ) ) ;
2009-01-29 16:27:27 +01:00
d = PA_CARD_PROFILE_DATA ( p ) ;
* d = 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 ) ;
if ( ( device - > headset_state < PA_BT_AUDIO_STATE_CONNECTED & & * d = = PROFILE_HSP ) | |
( device - > audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED & & * d = = PROFILE_A2DP ) ) {
pa_log_warn ( " Default profile not connected, selecting off profile " ) ;
u - > card - > active_profile = pa_hashmap_get ( u - > card - > profiles , " off " ) ;
u - > card - > save_profile = FALSE ;
}
2009-01-29 16:27:27 +01:00
d = PA_CARD_PROFILE_DATA ( u - > card - > active_profile ) ;
u - > profile = * d ;
return 0 ;
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-03-30 20:57:12 +02:00
static const pa_bluetooth_device * find_device ( struct userdata * u , const char * address , const char * path ) {
2009-03-21 01:19:49 +01:00
const pa_bluetooth_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 ) {
2009-03-30 20:57:12 +02:00
if ( ! ( d = pa_bluetooth_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 ) ) ) {
2009-02-02 01:58:48 +01:00
pa_log_error ( " Passed path %s and address %s don't match. " , path , 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 {
2009-03-30 20:57:12 +02:00
if ( ! ( d = pa_bluetooth_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
}
2009-04-08 04:15:42 +02:00
/* Run from main thread */
2009-03-21 02:54:18 +01:00
static int setup_dbus ( struct userdata * u ) {
DBusError err ;
dbus_error_init ( & err ) ;
u - > connection = pa_dbus_bus_get ( u - > core , DBUS_BUS_SYSTEM , & err ) ;
if ( dbus_error_is_set ( & err ) | | ! u - > connection ) {
pa_log ( " Failed to get D-Bus connection: %s " , err . message ) ;
dbus_error_free ( & err ) ;
return - 1 ;
}
return 0 ;
}
2009-01-29 16:27:27 +01:00
int pa__init ( pa_module * m ) {
pa_modargs * ma ;
uint32_t channels ;
struct userdata * u ;
2009-02-02 01:58:48 +01:00
const char * address , * path ;
2009-03-21 02:54:18 +01:00
DBusError err ;
char * mike , * speaker ;
2009-03-30 20:57:12 +02:00
const pa_bluetooth_device * device ;
2009-01-29 16:27:27 +01:00
pa_assert ( m ) ;
2009-03-21 02:54:18 +01:00
dbus_error_init ( & err ) ;
2009-01-29 16:27:27 +01:00
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 ;
2009-02-02 01:58:48 +01:00
u - > service_fd = - 1 ;
2009-01-29 16:27:27 +01:00
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-03-21 01:31:38 +01:00
# ifdef NOKIA
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 ;
}
2009-03-21 01:31:38 +01:00
# endif
2009-02-03 17:15:41 +02:00
2009-02-02 01:58:48 +01:00
if ( pa_modargs_get_value_u32 ( ma , " rate " , & u - > sample_spec . rate ) < 0 | |
u - > sample_spec . rate < = 0 | | u - > sample_spec . rate > PA_RATE_MAX ) {
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
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
2009-03-21 02:54:18 +01:00
if ( setup_dbus ( u ) < 0 )
goto fail ;
2009-03-30 20:57:12 +02:00
if ( ! ( u - > discovery = pa_bluetooth_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
2009-02-02 01:58:48 +01:00
/* Add the card structure. This will also initialize the default profile */
2009-05-28 02:39:22 +02:00
if ( add_card ( u , device ) < 0 )
2009-02-02 01:58:48 +01:00
goto fail ;
2009-01-29 16:27:27 +01:00
2009-02-02 01:58:48 +01:00
/* Connect to the BT service and query capabilities */
if ( init_bt ( u ) < 0 )
2009-01-29 16:27:27 +01:00
goto fail ;
2009-01-19 14:53:35 +02:00
2009-03-21 02:54:18 +01:00
if ( ! dbus_connection_add_filter ( pa_dbus_connection_get ( u - > connection ) , filter_cb , u , NULL ) ) {
pa_log_error ( " Failed to add filter function " ) ;
goto fail ;
}
speaker = pa_sprintf_malloc ( " type='signal',sender='org.bluez',interface='org.bluez.Headset',member='SpeakerGainChanged',path='%s' " , u - > path ) ;
mike = pa_sprintf_malloc ( " type='signal',sender='org.bluez',interface='org.bluez.Headset',member='MicrophoneGainChanged',path='%s' " , u - > path ) ;
if ( pa_dbus_add_matches (
pa_dbus_connection_get ( u - > connection ) , & err ,
speaker ,
mike ,
NULL ) < 0 ) {
pa_xfree ( speaker ) ;
pa_xfree ( mike ) ;
pa_log ( " Failed to add D-Bus matches: %s " , err . message ) ;
goto fail ;
}
pa_xfree ( speaker ) ;
pa_xfree ( mike ) ;
2009-03-04 16:38:02 +02:00
if ( u - > profile ! = PROFILE_OFF )
if ( init_profile ( u ) < 0 )
goto fail ;
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 )
goto fail ;
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
2009-03-21 02:54:18 +01:00
dbus_error_free ( & err ) ;
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 ;
pa_assert ( m ) ;
if ( ! ( u = m - > userdata ) )
return ;
2009-03-21 01:31:38 +01:00
if ( u - > sink
# ifdef NOKIA
& & ! USE_SCO_OVER_PCM ( u )
# endif
)
2009-02-02 01:58:48 +01:00
pa_sink_unlink ( u - > sink ) ;
2009-01-19 14:53:35 +02:00
2009-03-21 01:31:38 +01:00
if ( u - > source
# ifdef NOKIA
& & ! USE_SCO_OVER_PCM ( u )
# endif
)
2009-02-02 01:58:48 +01:00
pa_source_unlink ( u - > source ) ;
2009-01-29 16:27:27 +01:00
2009-02-02 01:58:48 +01:00
stop_thread ( u ) ;
2009-01-29 16:27:27 +01:00
2009-02-02 01:58:48 +01:00
if ( u - > connection ) {
2009-03-21 02:54:18 +01:00
if ( u - > path ) {
char * speaker , * mike ;
speaker = pa_sprintf_malloc ( " type='signal',sender='org.bluez',interface='org.bluez.Headset',member='SpeakerGainChanged',path='%s' " , u - > path ) ;
mike = pa_sprintf_malloc ( " type='signal',sender='org.bluez',interface='org.bluez.Headset',member='MicrophoneGainChanged',path='%s' " , u - > path ) ;
pa_dbus_remove_matches ( pa_dbus_connection_get ( u - > connection ) ,
speaker ,
mike ,
NULL ) ;
pa_xfree ( speaker ) ;
pa_xfree ( mike ) ;
}
dbus_connection_remove_filter ( pa_dbus_connection_get ( u - > connection ) , filter_cb , u ) ;
2009-02-02 01:58:48 +01:00
pa_dbus_connection_unref ( u - > connection ) ;
2008-08-20 10:52:25 -03:00
}
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 - > read_smoother )
pa_smoother_free ( u - > read_smoother ) ;
2008-08-21 17:06:41 -03:00
2009-02-12 22:09:00 +01:00
shutdown_bt ( u ) ;
2008-08-20 10:52:25 -03: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 ) ;
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 )
pa_bluetooth_discovery_unref ( u - > discovery ) ;
2008-08-20 10:52:25 -03:00
pa_xfree ( u ) ;
}