]>
code.delx.au - pulseaudio/blob - src/modules/module-solaris.c
4 This file is part of polypaudio.
6 polypaudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
11 polypaudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
34 #include <sys/ioctl.h>
36 #include <sys/types.h>
41 #include <sys/audio.h>
43 #include <polyp/mainloop-signal.h>
45 #include <polypcore/iochannel.h>
46 #include <polypcore/sink.h>
47 #include <polypcore/source.h>
48 #include <polypcore/module.h>
49 #include <polypcore/sample-util.h>
50 #include <polypcore/util.h>
51 #include <polypcore/modargs.h>
52 #include <polypcore/xmalloc.h>
53 #include <polypcore/log.h>
55 #include "module-solaris-symdef.h"
57 PA_MODULE_AUTHOR ( "Pierre Ossman" )
58 PA_MODULE_DESCRIPTION ( "Solaris Sink/Source" )
59 PA_MODULE_VERSION ( PACKAGE_VERSION
)
60 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> buffer_size=<record buffer size>" )
69 pa_memchunk memchunk
, silence
;
73 unsigned int written_bytes
, read_bytes
;
79 static const char * const valid_modargs
[] = {
92 #define DEFAULT_SINK_NAME "solaris_output"
93 #define DEFAULT_SOURCE_NAME "solaris_input"
94 #define DEFAULT_DEVICE "/dev/audio"
96 #define CHUNK_SIZE 2048
98 static void update_usage ( struct userdata
* u
) {
99 pa_module_set_used ( u
-> module
,
100 ( u
-> sink
? pa_idxset_size ( u
-> sink
-> inputs
) : 0 ) +
101 ( u
-> sink
? pa_idxset_size ( u
-> sink
-> monitor_source
-> outputs
) : 0 ) +
102 ( u
-> source
? pa_idxset_size ( u
-> source
-> outputs
) : 0 ));
105 static void do_write ( struct userdata
* u
) {
108 pa_memchunk
* memchunk
;
114 if (! u
-> sink
|| ! pa_iochannel_is_writable ( u
-> io
))
119 err
= ioctl ( u
-> fd
, AUDIO_GETINFO
, & info
);
123 * Since we cannot modify the size of the output buffer we fake it
124 * by not filling it more than u->buffer_size.
126 len
= u
-> buffer_size
;
127 len
-= u
-> written_bytes
- ( info
. play
. samples
* u
-> sample_size
);
130 * Do not fill more than half the buffer in one chunk since we only
131 * get notifications upon completion of entire chunks.
133 if ( len
> ( u
-> buffer_size
/ 2 ))
134 len
= u
-> buffer_size
/ 2 ;
136 if ( len
< u
-> sample_size
)
139 memchunk
= & u
-> memchunk
;
141 if (! memchunk
-> length
)
142 if ( pa_sink_render ( u
-> sink
, len
, memchunk
) < 0 )
143 memchunk
= & u
-> silence
;
145 assert ( memchunk
-> memblock
);
146 assert ( memchunk
-> memblock
-> data
);
147 assert ( memchunk
-> length
);
149 if ( memchunk
-> length
< len
)
150 len
= memchunk
-> length
;
152 if (( r
= pa_iochannel_write ( u
-> io
, ( uint8_t *) memchunk
-> memblock
-> data
+ memchunk
-> index
, len
)) < 0 ) {
153 pa_log ( __FILE__
": write() failed: %s" , strerror ( errno
));
157 if ( memchunk
== & u
-> silence
)
158 assert ( r
% u
-> sample_size
== 0 );
160 u
-> memchunk
. index
+= r
;
161 u
-> memchunk
. length
-= r
;
163 if ( u
-> memchunk
. length
<= 0 ) {
164 pa_memblock_unref ( u
-> memchunk
. memblock
);
165 u
-> memchunk
. memblock
= NULL
;
169 u
-> written_bytes
+= r
;
172 * Write 0 bytes which will generate a SIGPOLL when "played".
174 if ( write ( u
-> fd
, NULL
, 0 ) < 0 ) {
175 pa_log ( __FILE__
": write() failed: %s" , strerror ( errno
));
180 static void do_read ( struct userdata
* u
) {
181 pa_memchunk memchunk
;
186 if (! u
-> source
|| ! pa_iochannel_is_readable ( u
-> io
))
191 err
= ioctl ( u
-> fd
, I_NREAD
, & l
);
194 memchunk
. memblock
= pa_memblock_new ( l
, u
-> core
-> memblock_stat
);
195 assert ( memchunk
. memblock
);
196 if (( r
= pa_iochannel_read ( u
-> io
, memchunk
. memblock
-> data
, memchunk
. memblock
-> length
)) < 0 ) {
197 pa_memblock_unref ( memchunk
. memblock
);
199 pa_log ( __FILE__
": read() failed: %s" , strerror ( errno
));
203 assert ( r
<= ( ssize_t
) memchunk
. memblock
-> length
);
204 memchunk
. length
= memchunk
. memblock
-> length
= r
;
207 pa_source_post ( u
-> source
, & memchunk
);
208 pa_memblock_unref ( memchunk
. memblock
);
213 static void io_callback ( pa_iochannel
* io
, void * userdata
) {
214 struct userdata
* u
= userdata
;
220 void sig_callback ( pa_mainloop_api
* api
, pa_signal_event
* e
, int sig
, void * userdata
) {
221 struct userdata
* u
= userdata
;
226 static pa_usec_t
sink_get_latency_cb ( pa_sink
* s
) {
230 struct userdata
* u
= s
-> userdata
;
231 assert ( s
&& u
&& u
-> sink
);
233 err
= ioctl ( u
-> fd
, AUDIO_GETINFO
, & info
);
236 r
+= pa_bytes_to_usec ( u
-> written_bytes
, & s
-> sample_spec
);
237 r
-= pa_bytes_to_usec ( info
. play
. samples
* u
-> sample_size
, & s
-> sample_spec
);
239 if ( u
-> memchunk
. memblock
)
240 r
+= pa_bytes_to_usec ( u
-> memchunk
. length
, & s
-> sample_spec
);
245 static pa_usec_t
source_get_latency_cb ( pa_source
* s
) {
247 struct userdata
* u
= s
-> userdata
;
250 assert ( s
&& u
&& u
-> source
);
252 err
= ioctl ( u
-> fd
, AUDIO_GETINFO
, & info
);
255 r
+= pa_bytes_to_usec ( info
. record
. samples
* u
-> sample_size
, & s
-> sample_spec
);
256 r
-= pa_bytes_to_usec ( u
-> read_bytes
, & s
-> sample_spec
);
261 static int sink_get_hw_volume_cb ( pa_sink
* s
) {
262 struct userdata
* u
= s
-> userdata
;
266 err
= ioctl ( u
-> fd
, AUDIO_GETINFO
, & info
);
269 pa_cvolume_set (& s
-> hw_volume
, s
-> hw_volume
. channels
,
270 info
. play
. gain
* PA_VOLUME_NORM
/ AUDIO_MAX_GAIN
);
275 static int sink_set_hw_volume_cb ( pa_sink
* s
) {
276 struct userdata
* u
= s
-> userdata
;
279 AUDIO_INITINFO (& info
);
281 info
. play
. gain
= pa_cvolume_avg (& s
-> hw_volume
) * AUDIO_MAX_GAIN
/ PA_VOLUME_NORM
;
282 assert ( info
. play
. gain
<= AUDIO_MAX_GAIN
);
284 if ( ioctl ( u
-> fd
, AUDIO_SETINFO
, & info
) < 0 ) {
286 pa_log ( __FILE__
": AUDIO_SETINFO: Unsupported volume." );
288 pa_log ( __FILE__
": AUDIO_SETINFO: %s" , strerror ( errno
));
295 static int pa_solaris_auto_format ( int fd
, int mode
, pa_sample_spec
* ss
) {
298 AUDIO_INITINFO (& info
);
300 if ( mode
!= O_RDONLY
) {
301 info
. play
. sample_rate
= ss
-> rate
;
302 info
. play
. channels
= ss
-> channels
;
303 switch ( ss
-> format
) {
305 info
. play
. precision
= 8 ;
306 info
. play
. encoding
= AUDIO_ENCODING_LINEAR
;
309 info
. play
. precision
= 8 ;
310 info
. play
. encoding
= AUDIO_ENCODING_ALAW
;
313 info
. play
. precision
= 8 ;
314 info
. play
. encoding
= AUDIO_ENCODING_ULAW
;
316 case PA_SAMPLE_S16NE
:
317 info
. play
. precision
= 16 ;
318 info
. play
. encoding
= AUDIO_ENCODING_LINEAR
;
325 if ( mode
!= O_WRONLY
) {
326 info
. record
. sample_rate
= ss
-> rate
;
327 info
. record
. channels
= ss
-> channels
;
328 switch ( ss
-> format
) {
330 info
. record
. precision
= 8 ;
331 info
. record
. encoding
= AUDIO_ENCODING_LINEAR
;
334 info
. record
. precision
= 8 ;
335 info
. record
. encoding
= AUDIO_ENCODING_ALAW
;
338 info
. record
. precision
= 8 ;
339 info
. record
. encoding
= AUDIO_ENCODING_ULAW
;
341 case PA_SAMPLE_S16NE
:
342 info
. record
. precision
= 16 ;
343 info
. record
. encoding
= AUDIO_ENCODING_LINEAR
;
350 if ( ioctl ( fd
, AUDIO_SETINFO
, & info
) < 0 ) {
352 pa_log ( __FILE__
": AUDIO_SETINFO: Unsupported sample format." );
354 pa_log ( __FILE__
": AUDIO_SETINFO: %s" , strerror ( errno
));
361 static int pa_solaris_set_buffer ( int fd
, int buffer_size
) {
364 AUDIO_INITINFO (& info
);
366 info
. record
. buffer_size
= buffer_size
;
368 if ( ioctl ( fd
, AUDIO_SETINFO
, & info
) < 0 ) {
370 pa_log ( __FILE__
": AUDIO_SETINFO: Unsupported buffer size." );
372 pa_log ( __FILE__
": AUDIO_SETINFO: %s" , strerror ( errno
));
379 int pa__init ( pa_core
* c
, pa_module
* m
) {
380 struct userdata
* u
= NULL
;
385 int record
= 1 , playback
= 1 ;
387 pa_modargs
* ma
= NULL
;
390 if (!( ma
= pa_modargs_new ( m
-> argument
, valid_modargs
))) {
391 pa_log ( __FILE__
": failed to parse module arguments." );
395 if ( pa_modargs_get_value_boolean ( ma
, "record" , & record
) < 0 || pa_modargs_get_value_boolean ( ma
, "playback" , & playback
) < 0 ) {
396 pa_log ( __FILE__
": record= and playback= expect numeric argument." );
400 if (! playback
&& ! record
) {
401 pa_log ( __FILE__
": neither playback nor record enabled for device." );
405 mode
= ( playback
&& record
) ? O_RDWR
: ( playback
? O_WRONLY
: ( record
? O_RDONLY
: 0 ));
408 if ( pa_modargs_get_value_s32 ( ma
, "buffer_size" , & buffer_size
) < 0 ) {
409 pa_log ( __FILE__
": failed to parse buffer size argument" );
413 ss
= c
-> default_sample_spec
;
414 if ( pa_modargs_get_sample_spec ( ma
, & ss
) < 0 ) {
415 pa_log ( __FILE__
": failed to parse sample specification" );
419 if (( fd
= open ( p
= pa_modargs_get_value ( ma
, "device" , DEFAULT_DEVICE
), mode
| O_NONBLOCK
)) < 0 )
422 pa_log_info ( __FILE__
": device opened in %s mode." , mode
== O_WRONLY
? "O_WRONLY" : ( mode
== O_RDONLY
? "O_RDONLY" : "O_RDWR" ));
424 if ( pa_solaris_auto_format ( fd
, mode
, & ss
) < 0 )
427 if (( mode
!= O_WRONLY
) && ( buffer_size
>= 1 ))
428 if ( pa_solaris_set_buffer ( fd
, buffer_size
) < 0 )
431 u
= pa_xmalloc ( sizeof ( struct userdata
));
434 if ( mode
!= O_WRONLY
) {
435 u
-> source
= pa_source_new ( c
, __FILE__
, pa_modargs_get_value ( ma
, "source_name" , DEFAULT_SOURCE_NAME
), 0 , & ss
, NULL
);
437 u
-> source
-> userdata
= u
;
438 u
-> source
-> get_latency
= source_get_latency_cb
;
439 pa_source_set_owner ( u
-> source
, m
);
440 u
-> source
-> description
= pa_sprintf_malloc ( "Solaris PCM on '%s'" , p
);
444 if ( mode
!= O_RDONLY
) {
445 u
-> sink
= pa_sink_new ( c
, __FILE__
, pa_modargs_get_value ( ma
, "sink_name" , DEFAULT_SINK_NAME
), 0 , & ss
, NULL
);
447 u
-> sink
-> get_latency
= sink_get_latency_cb
;
448 u
-> sink
-> get_hw_volume
= sink_get_hw_volume_cb
;
449 u
-> sink
-> set_hw_volume
= sink_set_hw_volume_cb
;
450 u
-> sink
-> userdata
= u
;
451 pa_sink_set_owner ( u
-> sink
, m
);
452 u
-> sink
-> description
= pa_sprintf_malloc ( "Solaris PCM on '%s'" , p
);
456 assert ( u
-> source
|| u
-> sink
);
458 u
-> io
= pa_iochannel_new ( c
-> mainloop
, u
-> source
? fd
: - 1 , u
-> sink
? fd
: 0 );
460 pa_iochannel_set_callback ( u
-> io
, io_callback
, u
);
463 u
-> memchunk
. memblock
= NULL
;
464 u
-> memchunk
. length
= 0 ;
465 u
-> sample_size
= pa_frame_size (& ss
);
466 u
-> buffer_size
= buffer_size
;
468 u
-> silence
. memblock
= pa_memblock_new ( u
-> silence
. length
= CHUNK_SIZE
, u
-> core
-> memblock_stat
);
469 assert ( u
-> silence
. memblock
);
470 pa_silence_memblock ( u
-> silence
. memblock
, & ss
);
471 u
-> silence
. index
= 0 ;
473 u
-> written_bytes
= 0 ;
479 u
-> sig
= pa_signal_new ( SIGPOLL
, sig_callback
, u
);
481 ioctl ( u
-> fd
, I_SETSIG
, S_MSG
);
497 void pa__done ( pa_core
* c
, pa_module
* m
) {
501 if (!( u
= m
-> userdata
))
504 ioctl ( u
-> fd
, I_SETSIG
, 0 );
505 pa_signal_free ( u
-> sig
);
507 if ( u
-> memchunk
. memblock
)
508 pa_memblock_unref ( u
-> memchunk
. memblock
);
509 if ( u
-> silence
. memblock
)
510 pa_memblock_unref ( u
-> silence
. memblock
);
513 pa_sink_disconnect ( u
-> sink
);
514 pa_sink_unref ( u
-> sink
);
518 pa_source_disconnect ( u
-> source
);
519 pa_source_unref ( u
-> source
);
522 pa_iochannel_free ( u
-> io
);