2 This file is part of PulseAudio.
4 Copyright 2006 Lennart Poettering
5 Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 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 <pulse/error.h>
44 #include <pulse/mainloop-signal.h>
45 #include <pulse/xmalloc.h>
46 #include <pulse/timeval.h>
48 #include <pulsecore/iochannel.h>
49 #include <pulsecore/sink.h>
50 #include <pulsecore/source.h>
51 #include <pulsecore/module.h>
52 #include <pulsecore/sample-util.h>
53 #include <pulsecore/core-util.h>
54 #include <pulsecore/modargs.h>
55 #include <pulsecore/log.h>
56 #include <pulsecore/core-error.h>
57 #include <pulsecore/thread-mq.h>
58 #include <pulsecore/rtpoll.h>
59 #include <pulsecore/thread.h>
61 #include "module-solaris-symdef.h"
63 PA_MODULE_AUTHOR("Pierre Ossman")
64 PA_MODULE_DESCRIPTION("Solaris Sink/Source")
65 PA_MODULE_VERSION(PACKAGE_VERSION
)
67 "sink_name=<name for the sink> "
68 "source_name=<name for the source> "
69 "device=<OSS device> record=<enable source?> "
70 "playback=<enable sink?> "
71 "format=<sample format> "
72 "channels=<number of channels> "
74 "buffer_size=<record buffer size> "
75 "channel_map=<channel map>")
83 pa_thread_mq thread_mq
;
90 unsigned int page_size
;
94 unsigned int written_bytes
, read_bytes
;
97 pa_rtpoll_item
*rtpoll_item
;
101 static const char* const valid_modargs
[] = {
115 #define DEFAULT_SINK_NAME "solaris_output"
116 #define DEFAULT_SOURCE_NAME "solaris_input"
117 #define DEFAULT_DEVICE "/dev/audio"
119 static int sink_process_msg(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
120 struct userdata
*u
= PA_SINK(o
)->userdata
;
125 case PA_SINK_MESSAGE_GET_LATENCY
: {
130 err
= ioctl(u
->fd
, AUDIO_GETINFO
, &info
);
133 r
+= pa_bytes_to_usec(u
->written_bytes
, &PA_SINK(o
)->sample_spec
);
134 r
-= pa_bytes_to_usec(info
.play
.samples
* u
->frame_size
, &PA_SINK(o
)->sample_spec
);
136 if (u
->memchunk
.memblock
)
137 r
+= pa_bytes_to_usec(u
->memchunk
.length
, &PA_SINK(o
)->sample_spec
);
140 *((pa_usec_t
*) data
) = r
;
145 case PA_SINK_MESSAGE_SET_VOLUME
:
147 AUDIO_INITINFO(&info
);
149 info
.play
.gain
= pa_cvolume_avg((pa_cvolume
*)data
) * AUDIO_MAX_GAIN
/ PA_VOLUME_NORM
;
150 assert(info
.play
.gain
<= AUDIO_MAX_GAIN
);
152 if (ioctl(u
->fd
, AUDIO_SETINFO
, &info
) < 0) {
154 pa_log("AUDIO_SETINFO: Unsupported volume.");
156 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno
));
163 case PA_SINK_MESSAGE_GET_VOLUME
:
165 err
= ioctl(u
->fd
, AUDIO_GETINFO
, &info
);
168 pa_cvolume_set((pa_cvolume
*) data
, ((pa_cvolume
*) data
)->channels
,
169 info
.play
.gain
* PA_VOLUME_NORM
/ AUDIO_MAX_GAIN
);
175 case PA_SINK_MESSAGE_SET_MUTE
:
177 AUDIO_INITINFO(&info
);
179 info
.output_muted
= !!PA_PTR_TO_UINT(data
);
181 if (ioctl(u
->fd
, AUDIO_SETINFO
, &info
) < 0)
182 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno
));
188 case PA_SINK_MESSAGE_GET_MUTE
:
190 err
= ioctl(u
->fd
, AUDIO_GETINFO
, &info
);
193 *(int*)data
= !!info
.output_muted
;
200 return pa_sink_process_msg(o
, code
, data
, offset
, chunk
);
203 static int source_process_msg(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
204 struct userdata
*u
= PA_SOURCE(o
)->userdata
;
209 case PA_SOURCE_MESSAGE_GET_LATENCY
: {
213 err
= ioctl(u
->fd
, AUDIO_GETINFO
, &info
);
216 r
+= pa_bytes_to_usec(info
.record
.samples
* u
->frame_size
, &PA_SOURCE(o
)->sample_spec
);
217 r
-= pa_bytes_to_usec(u
->read_bytes
, &PA_SOURCE(o
)->sample_spec
);
220 *((pa_usec_t
*) data
) = r
;
225 case PA_SOURCE_MESSAGE_SET_VOLUME
:
227 AUDIO_INITINFO(&info
);
229 info
.record
.gain
= pa_cvolume_avg((pa_cvolume
*) data
) * AUDIO_MAX_GAIN
/ PA_VOLUME_NORM
;
230 assert(info
.record
.gain
<= AUDIO_MAX_GAIN
);
232 if (ioctl(u
->fd
, AUDIO_SETINFO
, &info
) < 0) {
234 pa_log("AUDIO_SETINFO: Unsupported volume.");
236 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno
));
243 case PA_SOURCE_MESSAGE_GET_VOLUME
:
245 err
= ioctl(u
->fd
, AUDIO_GETINFO
, &info
);
248 pa_cvolume_set((pa_cvolume
*) data
, ((pa_cvolume
*) data
)->channels
,
249 info
.record
.gain
* PA_VOLUME_NORM
/ AUDIO_MAX_GAIN
);
256 return pa_source_process_msg(o
, code
, data
, offset
, chunk
);
259 static void clear_underflow(struct userdata
*u
)
263 AUDIO_INITINFO(&info
);
267 if (ioctl(u
->fd
, AUDIO_SETINFO
, &info
) < 0)
268 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno
));
271 static void clear_overflow(struct userdata
*u
)
275 AUDIO_INITINFO(&info
);
277 info
.record
.error
= 0;
279 if (ioctl(u
->fd
, AUDIO_SETINFO
, &info
) < 0)
280 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno
));
283 static void thread_func(void *userdata
) {
284 struct userdata
*u
= userdata
;
285 unsigned short revents
= 0;
290 pa_log_debug("Thread starting up");
292 if (u
->core
->high_priority
)
295 pa_thread_mq_install(&u
->thread_mq
);
296 pa_rtpoll_install(u
->rtpoll
);
299 /* Render some data and write it to the dsp */
301 if (u
->sink
&& PA_SINK_OPENED(u
->sink
->thread_info
.state
)) {
306 err
= ioctl(u
->fd
, AUDIO_GETINFO
, &info
);
310 * Since we cannot modify the size of the output buffer we fake it
311 * by not filling it more than u->buffer_size.
313 len
= u
->buffer_size
;
314 len
-= u
->written_bytes
- (info
.play
.samples
* u
->frame_size
);
316 /* The sample counter can sometimes go backwards :( */
317 if (len
> u
->buffer_size
)
320 if (info
.play
.error
) {
321 pa_log_debug("Solaris buffer underflow!");
325 len
-= len
% u
->frame_size
;
331 if (!u
->memchunk
.length
)
332 pa_sink_render(u
->sink
, len
, &u
->memchunk
);
334 pa_assert(u
->memchunk
.length
);
336 p
= pa_memblock_acquire(u
->memchunk
.memblock
);
337 r
= pa_write(u
->fd
, (uint8_t*) p
+ u
->memchunk
.index
, u
->memchunk
.length
, NULL
);
338 pa_memblock_release(u
->memchunk
.memblock
);
343 else if (errno
!= EAGAIN
) {
344 pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno
));
348 pa_assert(r
% u
->frame_size
== 0);
350 u
->memchunk
.index
+= r
;
351 u
->memchunk
.length
-= r
;
353 if (u
->memchunk
.length
<= 0) {
354 pa_memblock_unref(u
->memchunk
.memblock
);
355 pa_memchunk_reset(&u
->memchunk
);
359 u
->written_bytes
+= r
;
364 /* Try to read some data and pass it on to the source driver */
366 if (u
->source
&& PA_SOURCE_OPENED(u
->source
->thread_info
.state
) && ((revents
& POLLIN
))) {
367 pa_memchunk memchunk
;
374 err
= ioctl(u
->fd
, AUDIO_GETINFO
, &info
);
377 if (info
.record
.error
) {
378 pa_log_debug("Solaris buffer overflow!");
382 err
= ioctl(u
->fd
, I_NREAD
, &l
);
386 /* This is to make sure it fits in the memory pool. Also, a page
387 should be the most efficient transfer size. */
388 if (l
> u
->page_size
)
391 memchunk
.memblock
= pa_memblock_new(u
->core
->mempool
, l
);
392 pa_assert(memchunk
.memblock
);
394 p
= pa_memblock_acquire(memchunk
.memblock
);
395 r
= pa_read(u
->fd
, p
, l
, NULL
);
396 pa_memblock_release(memchunk
.memblock
);
399 pa_memblock_unref(memchunk
.memblock
);
400 if (errno
!= EAGAIN
) {
401 pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno
));
408 pa_source_post(u
->source
, &memchunk
);
409 pa_memblock_unref(memchunk
.memblock
);
419 struct pollfd
*pollfd
;
421 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
423 ((u
->source
&& PA_SOURCE_OPENED(u
->source
->thread_info
.state
)) ? POLLIN
: 0);
426 /* Hmm, nothing to do. Let's sleep */
427 if ((ret
= pa_rtpoll_run(u
->rtpoll
, 1)) < 0)
434 struct pollfd
*pollfd
;
436 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
438 if (pollfd
->revents
& ~(POLLOUT
|POLLIN
)) {
439 pa_log("DSP shutdown.");
443 revents
= pollfd
->revents
;
449 /* We have to continue processing messages until we receive the
450 * SHUTDOWN message */
451 pa_asyncmsgq_post(u
->thread_mq
.outq
, PA_MSGOBJECT(u
->core
), PA_CORE_MESSAGE_UNLOAD_MODULE
, u
->module
, 0, NULL
, NULL
);
452 pa_asyncmsgq_wait_for(u
->thread_mq
.inq
, PA_MESSAGE_SHUTDOWN
);
455 pa_log_debug("Thread shutting down");
458 static void sig_callback(pa_mainloop_api
*api
, pa_signal_event
*e
, int sig
, void *userdata
) {
459 struct userdata
*u
= userdata
;
464 pa_sink_get_volume(u
->sink
);
465 pa_sink_get_mute(u
->sink
);
469 pa_source_get_volume(u
->source
);
472 static int pa_solaris_auto_format(int fd
, int mode
, pa_sample_spec
*ss
) {
475 AUDIO_INITINFO(&info
);
477 if (mode
!= O_RDONLY
) {
478 info
.play
.sample_rate
= ss
->rate
;
479 info
.play
.channels
= ss
->channels
;
480 switch (ss
->format
) {
482 info
.play
.precision
= 8;
483 info
.play
.encoding
= AUDIO_ENCODING_LINEAR
;
486 info
.play
.precision
= 8;
487 info
.play
.encoding
= AUDIO_ENCODING_ALAW
;
490 info
.play
.precision
= 8;
491 info
.play
.encoding
= AUDIO_ENCODING_ULAW
;
493 case PA_SAMPLE_S16NE
:
494 info
.play
.precision
= 16;
495 info
.play
.encoding
= AUDIO_ENCODING_LINEAR
;
502 if (mode
!= O_WRONLY
) {
503 info
.record
.sample_rate
= ss
->rate
;
504 info
.record
.channels
= ss
->channels
;
505 switch (ss
->format
) {
507 info
.record
.precision
= 8;
508 info
.record
.encoding
= AUDIO_ENCODING_LINEAR
;
511 info
.record
.precision
= 8;
512 info
.record
.encoding
= AUDIO_ENCODING_ALAW
;
515 info
.record
.precision
= 8;
516 info
.record
.encoding
= AUDIO_ENCODING_ULAW
;
518 case PA_SAMPLE_S16NE
:
519 info
.record
.precision
= 16;
520 info
.record
.encoding
= AUDIO_ENCODING_LINEAR
;
527 if (ioctl(fd
, AUDIO_SETINFO
, &info
) < 0) {
529 pa_log("AUDIO_SETINFO: Unsupported sample format.");
531 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno
));
538 static int pa_solaris_set_buffer(int fd
, int buffer_size
) {
541 AUDIO_INITINFO(&info
);
543 info
.play
.buffer_size
= buffer_size
;
544 info
.record
.buffer_size
= buffer_size
;
546 if (ioctl(fd
, AUDIO_SETINFO
, &info
) < 0) {
548 pa_log("AUDIO_SETINFO: Unsupported buffer size.");
550 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno
));
557 int pa__init(pa_module
*m
) {
558 struct userdata
*u
= NULL
;
563 int record
= 1, playback
= 1;
566 pa_modargs
*ma
= NULL
;
568 struct pollfd
*pollfd
;
572 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
573 pa_log("failed to parse module arguments.");
577 if (pa_modargs_get_value_boolean(ma
, "record", &record
) < 0 || pa_modargs_get_value_boolean(ma
, "playback", &playback
) < 0) {
578 pa_log("record= and playback= expect numeric argument.");
582 if (!playback
&& !record
) {
583 pa_log("neither playback nor record enabled for device.");
587 mode
= (playback
&&record
) ? O_RDWR
: (playback
? O_WRONLY
: (record
? O_RDONLY
: 0));
590 if (pa_modargs_get_value_s32(ma
, "buffer_size", &buffer_size
) < 0) {
591 pa_log("failed to parse buffer size argument");
595 ss
= m
->core
->default_sample_spec
;
596 if (pa_modargs_get_sample_spec_and_channel_map(ma
, &ss
, &map
, PA_CHANNEL_MAP_DEFAULT
) < 0) {
597 pa_log("failed to parse sample specification");
601 if ((fd
= open(p
= pa_modargs_get_value(ma
, "device", DEFAULT_DEVICE
), mode
| O_NONBLOCK
)) < 0)
604 pa_log_info("device opened in %s mode.", mode
== O_WRONLY
? "O_WRONLY" : (mode
== O_RDONLY
? "O_RDONLY" : "O_RDWR"));
606 if (pa_solaris_auto_format(fd
, mode
, &ss
) < 0)
609 if (pa_solaris_set_buffer(fd
, buffer_size
) < 0)
612 u
= pa_xmalloc(sizeof(struct userdata
));
617 pa_memchunk_reset(&u
->memchunk
);
619 /* We use this to get a reasonable chunk size */
620 u
->page_size
= PA_PAGE_SIZE
;
622 u
->frame_size
= pa_frame_size(&ss
);
623 u
->buffer_size
= buffer_size
;
625 u
->written_bytes
= 0;
631 pa_thread_mq_init(&u
->thread_mq
, m
->core
->mainloop
);
633 u
->rtpoll
= pa_rtpoll_new();
634 pa_rtpoll_item_new_asyncmsgq(u
->rtpoll
, PA_RTPOLL_EARLY
, u
->thread_mq
.inq
);
636 pa_rtpoll_set_timer_periodic(u
->rtpoll
, pa_bytes_to_usec(u
->buffer_size
/ 10, &ss
));
638 u
->rtpoll_item
= pa_rtpoll_item_new(u
->rtpoll
, PA_RTPOLL_NEVER
, 1);
639 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
644 if (mode
!= O_WRONLY
) {
645 u
->source
= pa_source_new(m
->core
, __FILE__
, pa_modargs_get_value(ma
, "source_name", DEFAULT_SOURCE_NAME
), 0, &ss
, &map
);
646 pa_assert(u
->source
);
648 u
->source
->userdata
= u
;
649 u
->source
->parent
.process_msg
= source_process_msg
;
651 pa_source_set_module(u
->source
, m
);
652 pa_source_set_description(u
->source
, t
= pa_sprintf_malloc("Solaris PCM on '%s'", p
));
654 pa_source_set_asyncmsgq(u
->source
, u
->thread_mq
.inq
);
655 pa_source_set_rtpoll(u
->source
, u
->rtpoll
);
657 u
->source
->flags
= PA_SOURCE_HARDWARE
|PA_SOURCE_LATENCY
|PA_SOURCE_HW_VOLUME_CTRL
;
658 u
->source
->refresh_volume
= 1;
662 if (mode
!= O_RDONLY
) {
663 u
->sink
= pa_sink_new(m
->core
, __FILE__
, pa_modargs_get_value(ma
, "sink_name", DEFAULT_SINK_NAME
), 0, &ss
, &map
);
666 u
->sink
->userdata
= u
;
667 u
->sink
->parent
.process_msg
= sink_process_msg
;
669 pa_sink_set_module(u
->sink
, m
);
670 pa_sink_set_description(u
->sink
, t
= pa_sprintf_malloc("Solaris PCM on '%s'", p
));
672 pa_sink_set_asyncmsgq(u
->sink
, u
->thread_mq
.inq
);
673 pa_sink_set_rtpoll(u
->sink
, u
->rtpoll
);
675 u
->sink
->flags
= PA_SINK_HARDWARE
|PA_SINK_LATENCY
|PA_SINK_HW_VOLUME_CTRL
;
676 u
->sink
->refresh_volume
= 1;
677 u
->sink
->refresh_mute
= 1;
681 pa_assert(u
->source
|| u
->sink
);
683 u
->sig
= pa_signal_new(SIGPOLL
, sig_callback
, u
);
685 ioctl(u
->fd
, I_SETSIG
, S_MSG
);
687 if (!(u
->thread
= pa_thread_new(thread_func
, u
))) {
688 pa_log("Failed to create thread.");
692 /* Read mixer settings */
694 pa_asyncmsgq_send(u
->thread_mq
.inq
, PA_MSGOBJECT(u
->source
), PA_SOURCE_MESSAGE_GET_VOLUME
, &u
->source
->volume
, 0, NULL
);
696 pa_asyncmsgq_send(u
->thread_mq
.inq
, PA_MSGOBJECT(u
->sink
), PA_SINK_MESSAGE_GET_VOLUME
, &u
->sink
->volume
, 0, NULL
);
697 pa_asyncmsgq_send(u
->thread_mq
.inq
, PA_MSGOBJECT(u
->sink
), PA_SINK_MESSAGE_GET_MUTE
, &u
->sink
->muted
, 0, NULL
);
701 pa_sink_put(u
->sink
);
703 pa_source_put(u
->source
);
721 void pa__done(pa_module
*m
) {
726 if (!(u
= m
->userdata
))
729 ioctl(u
->fd
, I_SETSIG
, 0);
730 pa_signal_free(u
->sig
);
733 pa_sink_unlink(u
->sink
);
736 pa_source_unlink(u
->source
);
739 pa_asyncmsgq_send(u
->thread_mq
.inq
, NULL
, PA_MESSAGE_SHUTDOWN
, NULL
, 0, NULL
);
740 pa_thread_free(u
->thread
);
743 pa_thread_mq_done(&u
->thread_mq
);
746 pa_sink_unref(u
->sink
);
749 pa_source_unref(u
->source
);
751 if (u
->memchunk
.memblock
)
752 pa_memblock_unref(u
->memchunk
.memblock
);
755 pa_rtpoll_item_free(u
->rtpoll_item
);
758 pa_rtpoll_free(u
->rtpoll
);