2004-07-16 19:56:36 +00:00
/* $Id$ */
/***
This file is part of polypaudio .
polypaudio is free software ; you can redistribute it and / or modify
2004-11-14 14:58:54 +00:00
it under the terms of the GNU Lesser General Public License as published
2004-07-16 19:56:36 +00:00
by the Free Software Foundation ; either version 2 of the License ,
or ( at your option ) any later version .
polypaudio 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 .
2004-11-14 14:58:54 +00:00
You should have received a copy of the GNU Lesser General Public License
2004-07-16 19:56:36 +00:00
along with polypaudio ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
USA .
* * */
2004-07-16 19:16:42 +00:00
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
2004-06-16 00:05:30 +00:00
# include <sys/soundcard.h>
# include <sys/ioctl.h>
# include <stdlib.h>
# include <sys/stat.h>
# include <stdio.h>
# include <assert.h>
# include <errno.h>
# include <string.h>
# include <fcntl.h>
# include <unistd.h>
# include <limits.h>
# include <sys/mman.h>
# include "iochannel.h"
# include "sink.h"
# include "source.h"
# include "module.h"
2004-06-23 23:43:42 +00:00
# include "oss-util.h"
2004-06-23 23:17:30 +00:00
# include "sample-util.h"
2004-07-10 20:56:38 +00:00
# include "util.h"
2004-07-11 23:21:32 +00:00
# include "modargs.h"
2004-08-04 16:39:30 +00:00
# include "xmalloc.h"
2004-09-05 00:03:16 +00:00
# include "log.h"
2004-10-30 01:55:16 +00:00
# include "module-oss-mmap-symdef.h"
2004-06-16 00:05:30 +00:00
2004-09-11 23:17:38 +00:00
PA_MODULE_AUTHOR ( " Lennart Poettering " )
PA_MODULE_DESCRIPTION ( " OSS Sink/Source (mmap) " )
PA_MODULE_VERSION ( PACKAGE_VERSION )
2004-09-20 17:19:35 +00:00
PA_MODULE_USAGE ( " sink_name=<name for the sink> source_name=<name for the source> device=<OSS device> record=<enable source?> playback=<enable sink?> format=<sample format> channels=<number of channels> rate=<sample rate> fragments=<number of fragments> fragment_size=<fragment size> " )
2004-09-11 23:17:38 +00:00
2004-06-16 00:05:30 +00:00
struct userdata {
2004-07-03 23:35:12 +00:00
struct pa_sink * sink ;
struct pa_source * source ;
struct pa_core * core ;
2004-06-23 23:17:30 +00:00
struct pa_sample_spec sample_spec ;
2004-06-16 00:05:30 +00:00
2004-06-23 23:17:30 +00:00
size_t in_fragment_size , out_fragment_size , in_fragments , out_fragments , out_fill ;
2004-06-16 00:05:30 +00:00
int fd ;
void * in_mmap , * out_mmap ;
size_t in_mmap_length , out_mmap_length ;
2004-08-05 19:53:57 +00:00
struct pa_io_event * io_event ;
2004-06-16 00:05:30 +00:00
2004-07-03 23:35:12 +00:00
struct pa_memblock * * in_memblocks , * * out_memblocks ;
2004-06-16 00:05:30 +00:00
unsigned out_current , in_current ;
2004-08-04 16:39:30 +00:00
struct pa_module * module ;
2004-06-16 00:05:30 +00:00
} ;
2004-07-11 23:21:32 +00:00
static const char * const valid_modargs [ ] = {
" sink_name " ,
" source_name " ,
" device " ,
" record " ,
" playback " ,
2004-07-16 17:51:53 +00:00
" fragments " ,
" fragment_size " ,
" format " ,
" rate " ,
" channels " ,
2004-07-11 23:21:32 +00:00
NULL
} ;
2004-06-16 00:05:30 +00:00
2004-07-11 23:21:32 +00:00
# define DEFAULT_SINK_NAME "oss_output"
# define DEFAULT_SOURCE_NAME "oss_input"
# define DEFAULT_DEVICE " / dev / dsp"
2004-06-16 00:05:30 +00:00
2004-08-04 16:39:30 +00:00
static void update_usage ( struct userdata * u ) {
pa_module_set_used ( u - > module ,
( u - > sink ? pa_idxset_ncontents ( u - > sink - > inputs ) : 0 ) +
( u - > sink ? pa_idxset_ncontents ( u - > sink - > monitor_source - > outputs ) : 0 ) +
( u - > source ? pa_idxset_ncontents ( u - > source - > outputs ) : 0 ) ) ;
}
2004-06-16 00:05:30 +00:00
static void out_fill_memblocks ( struct userdata * u , unsigned n ) {
assert ( u & & u - > out_memblocks ) ;
while ( n > 0 ) {
2004-07-03 23:35:12 +00:00
struct pa_memchunk chunk ;
2004-06-16 00:05:30 +00:00
2004-07-10 16:50:22 +00:00
if ( u - > out_memblocks [ u - > out_current ] )
pa_memblock_unref_fixed ( u - > out_memblocks [ u - > out_current ] ) ;
2004-09-01 00:46:56 +00:00
chunk . memblock = u - > out_memblocks [ u - > out_current ] = pa_memblock_new_fixed ( ( uint8_t * ) u - > out_mmap + u - > out_fragment_size * u - > out_current , u - > out_fragment_size , u - > core - > memblock_stat ) ;
2004-07-10 16:50:22 +00:00
assert ( chunk . memblock ) ;
chunk . length = chunk . memblock - > length ;
chunk . index = 0 ;
pa_sink_render_into_full ( u - > sink , & chunk ) ;
2004-06-16 00:05:30 +00:00
u - > out_current + + ;
while ( u - > out_current > = u - > out_fragments )
u - > out_current - = u - > out_fragments ;
n - - ;
}
}
static void do_write ( struct userdata * u ) {
struct count_info info ;
assert ( u & & u - > sink ) ;
2004-08-04 16:39:30 +00:00
update_usage ( u ) ;
2004-06-16 00:05:30 +00:00
if ( ioctl ( u - > fd , SNDCTL_DSP_GETOPTR , & info ) < 0 ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : SNDCTL_DSP_GETOPTR: %s \n " , strerror ( errno ) ) ;
2004-06-16 00:05:30 +00:00
return ;
}
u - > out_fill = ( u - > out_fragment_size * u - > out_fragments ) - ( info . ptr % u - > out_fragment_size ) ;
if ( ! info . blocks )
return ;
out_fill_memblocks ( u , info . blocks ) ;
}
static void in_post_memblocks ( struct userdata * u , unsigned n ) {
assert ( u & & u - > in_memblocks ) ;
while ( n > 0 ) {
2004-07-03 23:35:12 +00:00
struct pa_memchunk chunk ;
2004-06-16 00:05:30 +00:00
if ( ! u - > in_memblocks [ u - > in_current ] ) {
2004-09-01 00:46:56 +00:00
chunk . memblock = u - > in_memblocks [ u - > in_current ] = pa_memblock_new_fixed ( ( uint8_t * ) u - > in_mmap + u - > in_fragment_size * u - > in_current , u - > in_fragment_size , u - > core - > memblock_stat ) ;
2004-06-16 00:05:30 +00:00
chunk . length = chunk . memblock - > length ;
chunk . index = 0 ;
2004-07-03 23:35:12 +00:00
pa_source_post ( u - > source , & chunk ) ;
2004-06-16 00:05:30 +00:00
}
u - > in_current + + ;
while ( u - > in_current > = u - > in_fragments )
u - > in_current - = u - > in_fragments ;
n - - ;
}
}
static void in_clear_memblocks ( struct userdata * u , unsigned n ) {
unsigned i = u - > in_current ;
assert ( u & & u - > in_memblocks ) ;
if ( n > u - > in_fragments )
n = u - > in_fragments ;
while ( n > 0 ) {
if ( u - > in_memblocks [ i ] ) {
2004-07-03 23:35:12 +00:00
pa_memblock_unref_fixed ( u - > in_memblocks [ i ] ) ;
2004-06-16 00:05:30 +00:00
u - > in_memblocks [ i ] = NULL ;
}
i + + ;
while ( i > = u - > in_fragments )
i - = u - > in_fragments ;
n - - ;
}
}
static void do_read ( struct userdata * u ) {
struct count_info info ;
assert ( u & & u - > source ) ;
2004-08-04 16:39:30 +00:00
update_usage ( u ) ;
2004-06-16 00:05:30 +00:00
if ( ioctl ( u - > fd , SNDCTL_DSP_GETIPTR , & info ) < 0 ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : SNDCTL_DSP_GETIPTR: %s \n " , strerror ( errno ) ) ;
2004-06-16 00:05:30 +00:00
return ;
}
if ( ! info . blocks )
return ;
in_post_memblocks ( u , info . blocks ) ;
in_clear_memblocks ( u , u - > in_fragments / 2 ) ;
2004-08-19 23:14:59 +00:00
}
2004-06-16 00:05:30 +00:00
2004-08-05 19:53:57 +00:00
static void io_callback ( struct pa_mainloop_api * m , struct pa_io_event * e , int fd , enum pa_io_event_flags f , void * userdata ) {
2004-06-16 00:05:30 +00:00
struct userdata * u = userdata ;
2004-08-05 19:53:57 +00:00
assert ( u & & u - > core - > mainloop = = m & & u - > io_event = = e ) ;
2004-06-16 00:05:30 +00:00
2004-08-05 19:53:57 +00:00
if ( f & PA_IO_EVENT_INPUT )
2004-06-16 00:05:30 +00:00
do_read ( u ) ;
2004-08-05 19:53:57 +00:00
if ( f & PA_IO_EVENT_OUTPUT )
2004-06-16 00:05:30 +00:00
do_write ( u ) ;
}
2004-09-12 13:14:49 +00:00
static pa_usec_t sink_get_latency_cb ( struct pa_sink * s ) {
2004-06-16 00:05:30 +00:00
struct userdata * u = s - > userdata ;
assert ( s & & u ) ;
do_write ( u ) ;
2004-08-03 19:26:56 +00:00
return pa_bytes_to_usec ( u - > out_fill , & s - > sample_spec ) ;
2004-06-16 00:05:30 +00:00
}
2004-09-11 23:17:38 +00:00
int pa__init ( struct pa_core * c , struct pa_module * m ) {
2004-06-16 00:05:30 +00:00
struct audio_buf_info info ;
struct userdata * u = NULL ;
2004-07-11 23:21:32 +00:00
const char * p ;
2004-07-16 17:51:53 +00:00
int nfrags , frag_size ;
2004-07-11 23:21:32 +00:00
int mode , caps ;
2004-06-16 00:05:30 +00:00
int enable_bits = 0 , zero = 0 ;
2004-07-11 23:21:32 +00:00
int playback = 1 , record = 1 ;
struct pa_modargs * ma = NULL ;
2004-06-16 00:05:30 +00:00
assert ( c & & m ) ;
2004-08-04 16:39:30 +00:00
m - > userdata = u = pa_xmalloc0 ( sizeof ( struct userdata ) ) ;
u - > module = m ;
2004-06-16 00:05:30 +00:00
u - > fd = - 1 ;
u - > core = c ;
2004-07-11 23:21:32 +00:00
if ( ! ( ma = pa_modargs_new ( m - > argument , valid_modargs ) ) ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : failed to parse module arguments. \n " ) ;
2004-07-11 23:21:32 +00:00
goto fail ;
2004-06-16 00:05:30 +00:00
}
2004-07-11 23:21:32 +00:00
2004-09-04 00:27:36 +00:00
if ( pa_modargs_get_value_boolean ( ma , " record " , & record ) < 0 | | pa_modargs_get_value_boolean ( ma , " playback " , & playback ) < 0 ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : record= and playback= expect numeric arguments. \n " ) ;
2004-07-11 23:21:32 +00:00
goto fail ;
2004-06-16 00:05:30 +00:00
}
2004-09-04 00:27:36 +00:00
if ( ! playback & & ! record ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : neither playback nor record enabled for device. \n " ) ;
2004-07-11 23:21:32 +00:00
goto fail ;
2004-06-16 00:05:30 +00:00
}
2004-07-11 23:21:32 +00:00
2004-09-04 00:27:36 +00:00
mode = ( playback & & record ) ? O_RDWR : ( playback ? O_WRONLY : ( record ? O_RDONLY : 0 ) ) ;
2004-07-16 17:51:53 +00:00
nfrags = 12 ;
frag_size = 1024 ;
2004-09-07 22:40:43 +00:00
if ( pa_modargs_get_value_s32 ( ma , " fragments " , & nfrags ) < 0 | | pa_modargs_get_value_s32 ( ma , " fragment_size " , & frag_size ) < 0 ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : failed to parse fragments arguments \n " ) ;
2004-07-16 17:51:53 +00:00
goto fail ;
}
u - > sample_spec = c - > default_sample_spec ;
if ( pa_modargs_get_sample_spec ( ma , & u - > sample_spec ) < 0 ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : failed to parse sample specification \n " ) ;
2004-07-16 17:51:53 +00:00
goto fail ;
}
2004-07-11 23:21:32 +00:00
if ( ( u - > fd = pa_oss_open ( p = pa_modargs_get_value ( ma , " device " , DEFAULT_DEVICE ) , & mode , & caps ) ) < 0 )
goto fail ;
2004-06-16 00:05:30 +00:00
if ( ! ( caps & DSP_CAP_MMAP ) | | ! ( caps & DSP_CAP_REALTIME ) | | ! ( caps & DSP_CAP_TRIGGER ) ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : OSS device not mmap capable. \n " ) ;
2004-06-16 00:05:30 +00:00
goto fail ;
}
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : device opened in %s mode. \n " , mode = = O_WRONLY ? " O_WRONLY " : ( mode = = O_RDONLY ? " O_RDONLY " : " O_RDWR " ) ) ;
2004-09-07 22:40:43 +00:00
if ( nfrags > = 2 & & frag_size > = 1 )
if ( pa_oss_set_fragments ( u - > fd , nfrags , frag_size ) < 0 )
goto fail ;
2004-07-16 17:51:53 +00:00
2004-07-03 23:35:12 +00:00
if ( pa_oss_auto_format ( u - > fd , & u - > sample_spec ) < 0 )
2004-06-16 00:05:30 +00:00
goto fail ;
if ( mode ! = O_WRONLY ) {
if ( ioctl ( u - > fd , SNDCTL_DSP_GETISPACE , & info ) < 0 ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : SNDCTL_DSP_GETISPACE: %s \n " , strerror ( errno ) ) ;
2004-06-16 00:05:30 +00:00
goto fail ;
}
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : input -- %u fragments of size %u. \n " , info . fragstotal , info . fragsize ) ;
2004-06-16 00:05:30 +00:00
u - > in_mmap_length = ( u - > in_fragment_size = info . fragsize ) * ( u - > in_fragments = info . fragstotal ) ;
if ( ( u - > in_mmap = mmap ( NULL , u - > in_mmap_length , PROT_READ , MAP_SHARED , u - > fd , 0 ) ) = = MAP_FAILED ) {
if ( mode = = O_RDWR ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : mmap failed for input. Changing to O_WRONLY mode. \n " ) ;
2004-06-16 00:05:30 +00:00
mode = O_WRONLY ;
} else {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : mmap(): %s \n " , strerror ( errno ) ) ;
2004-06-16 00:05:30 +00:00
goto fail ;
}
} else {
2004-07-11 23:21:32 +00:00
u - > source = pa_source_new ( c , pa_modargs_get_value ( ma , " source_name " , DEFAULT_SOURCE_NAME ) , 0 , & u - > sample_spec ) ;
2004-06-16 00:05:30 +00:00
assert ( u - > source ) ;
u - > source - > userdata = u ;
2004-07-10 20:56:38 +00:00
pa_source_set_owner ( u - > source , m ) ;
u - > source - > description = pa_sprintf_malloc ( " Open Sound System PCM/mmap() on '%s' " , p ) ;
2004-08-04 16:39:30 +00:00
u - > in_memblocks = pa_xmalloc0 ( sizeof ( struct pa_memblock * ) * u - > in_fragments ) ;
2004-06-16 00:05:30 +00:00
enable_bits | = PCM_ENABLE_INPUT ;
}
}
2004-07-03 00:32:31 +00:00
if ( mode ! = O_RDONLY ) {
2004-06-16 00:05:30 +00:00
if ( ioctl ( u - > fd , SNDCTL_DSP_GETOSPACE , & info ) < 0 ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : SNDCTL_DSP_GETOSPACE: %s \n " , strerror ( errno ) ) ;
2004-06-16 00:05:30 +00:00
goto fail ;
}
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : output -- %u fragments of size %u. \n " , info . fragstotal , info . fragsize ) ;
2004-06-16 00:05:30 +00:00
u - > out_mmap_length = ( u - > out_fragment_size = info . fragsize ) * ( u - > out_fragments = info . fragstotal ) ;
if ( ( u - > out_mmap = mmap ( NULL , u - > out_mmap_length , PROT_WRITE , MAP_SHARED , u - > fd , 0 ) ) = = MAP_FAILED ) {
if ( mode = = O_RDWR ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : mmap filed for output. Changing to O_RDONLY mode. \n " ) ;
2004-06-16 00:05:30 +00:00
mode = O_RDONLY ;
} else {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : mmap(): %s \n " , strerror ( errno ) ) ;
2004-06-16 00:05:30 +00:00
goto fail ;
}
} else {
2004-07-03 23:35:12 +00:00
pa_silence_memory ( u - > out_mmap , u - > out_mmap_length , & u - > sample_spec ) ;
2004-06-16 00:05:30 +00:00
2004-07-11 23:21:32 +00:00
u - > sink = pa_sink_new ( c , pa_modargs_get_value ( ma , " sink_name " , DEFAULT_SINK_NAME ) , 0 , & u - > sample_spec ) ;
2004-06-16 00:05:30 +00:00
assert ( u - > sink ) ;
u - > sink - > get_latency = sink_get_latency_cb ;
u - > sink - > userdata = u ;
2004-07-10 20:56:38 +00:00
pa_sink_set_owner ( u - > sink , m ) ;
u - > sink - > description = pa_sprintf_malloc ( " Open Sound System PCM/mmap() on '%s' " , p ) ;
2004-06-16 00:05:30 +00:00
2004-08-04 16:39:30 +00:00
u - > out_memblocks = pa_xmalloc0 ( sizeof ( struct memblock * ) * u - > out_fragments ) ;
2004-06-16 00:05:30 +00:00
enable_bits | = PCM_ENABLE_OUTPUT ;
}
}
zero = 0 ;
if ( ioctl ( u - > fd , SNDCTL_DSP_SETTRIGGER , & zero ) < 0 ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : SNDCTL_DSP_SETTRIGGER: %s \n " , strerror ( errno ) ) ;
2004-06-16 00:05:30 +00:00
goto fail ;
}
if ( ioctl ( u - > fd , SNDCTL_DSP_SETTRIGGER , & enable_bits ) < 0 ) {
2004-09-05 00:03:16 +00:00
pa_log ( __FILE__ " : SNDCTL_DSP_SETTRIGGER: %s \n " , strerror ( errno ) ) ;
2004-06-16 00:05:30 +00:00
goto fail ;
}
assert ( u - > source | | u - > sink ) ;
2004-08-05 19:53:57 +00:00
u - > io_event = c - > mainloop - > io_new ( c - > mainloop , u - > fd , ( u - > source ? PA_IO_EVENT_INPUT : 0 ) | ( u - > sink ? PA_IO_EVENT_OUTPUT : 0 ) , io_callback , u ) ;
assert ( u - > io_event ) ;
2004-06-16 00:05:30 +00:00
2004-07-15 17:33:56 +00:00
pa_modargs_free ( ma ) ;
2004-06-16 00:05:30 +00:00
return 0 ;
fail :
2004-09-11 23:17:38 +00:00
pa__done ( c , m ) ;
2004-06-16 00:05:30 +00:00
2004-07-15 17:33:56 +00:00
if ( ma )
pa_modargs_free ( ma ) ;
2004-06-16 00:05:30 +00:00
return - 1 ;
}
2004-09-11 23:17:38 +00:00
void pa__done ( struct pa_core * c , struct pa_module * m ) {
2004-06-16 00:05:30 +00:00
struct userdata * u ;
assert ( c & & m ) ;
2004-08-04 16:39:30 +00:00
if ( ! ( u = m - > userdata ) )
return ;
2004-06-16 00:05:30 +00:00
if ( u - > out_memblocks ) {
2004-07-10 16:50:22 +00:00
unsigned i ;
for ( i = 0 ; i < u - > out_fragments ; i + + )
if ( u - > out_memblocks [ i ] )
pa_memblock_unref_fixed ( u - > out_memblocks [ i ] ) ;
2004-08-04 16:39:30 +00:00
pa_xfree ( u - > out_memblocks ) ;
2004-06-16 00:05:30 +00:00
}
if ( u - > in_memblocks ) {
2004-07-10 16:50:22 +00:00
unsigned i ;
for ( i = 0 ; i < u - > in_fragments ; i + + )
if ( u - > in_memblocks [ i ] )
pa_memblock_unref_fixed ( u - > in_memblocks [ i ] ) ;
2004-08-04 16:39:30 +00:00
pa_xfree ( u - > in_memblocks ) ;
2004-06-16 00:05:30 +00:00
}
if ( u - > in_mmap & & u - > in_mmap ! = MAP_FAILED )
munmap ( u - > in_mmap , u - > in_mmap_length ) ;
if ( u - > out_mmap & & u - > out_mmap ! = MAP_FAILED )
munmap ( u - > out_mmap , u - > out_mmap_length ) ;
2004-09-14 20:53:25 +00:00
if ( u - > sink ) {
pa_sink_disconnect ( u - > sink ) ;
pa_sink_unref ( u - > sink ) ;
}
2004-06-16 00:05:30 +00:00
2004-09-14 20:53:25 +00:00
if ( u - > source ) {
pa_source_disconnect ( u - > source ) ;
pa_source_unref ( u - > source ) ;
}
2004-06-16 00:05:30 +00:00
2004-08-05 19:53:57 +00:00
if ( u - > io_event )
u - > core - > mainloop - > io_free ( u - > io_event ) ;
2004-06-16 00:05:30 +00:00
if ( u - > fd > = 0 )
close ( u - > fd ) ;
2004-08-04 16:39:30 +00:00
pa_xfree ( u ) ;
2004-06-16 00:05:30 +00:00
}