4 This file is part of PulseAudio.
6 Copyright 2006 Lennart Poettering
7 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
9 PulseAudio is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as published
11 by the Free Software Foundation; either version 2 of the License,
12 or (at your option) any later version.
14 PulseAudio is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with PulseAudio; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
37 #include <sys/ioctl.h>
39 #include <sys/types.h>
44 #include <sys/audio.h>
46 #include <pulse/error.h>
47 #include <pulse/mainloop-signal.h>
48 #include <pulse/xmalloc.h>
49 #include <pulse/timeval.h>
51 #include <pulsecore/iochannel.h>
52 #include <pulsecore/sink.h>
53 #include <pulsecore/source.h>
54 #include <pulsecore/module.h>
55 #include <pulsecore/sample-util.h>
56 #include <pulsecore/core-util.h>
57 #include <pulsecore/modargs.h>
58 #include <pulsecore/log.h>
59 #include <pulsecore/core-error.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_usec_t poll_timeout;
88 unsigned int page_size;
92 unsigned int written_bytes, read_bytes;
99 static const char* const valid_modargs[] = {
113 #define DEFAULT_SINK_NAME "solaris_output"
114 #define DEFAULT_SOURCE_NAME "solaris_input"
115 #define DEFAULT_DEVICE "/dev/audio"
117 #define CHUNK_SIZE 2048
119 static void update_usage(struct userdata *u) {
120 pa_module_set_used(u->module,
121 (u->sink ? pa_sink_used_by(u->sink) : 0) +
122 (u->source ? pa_source_used_by(u->source) : 0));
125 static void do_write(struct userdata *u) {
133 /* We cannot check pa_iochannel_is_writable() because of our buffer hack */
139 err = ioctl(u->fd, AUDIO_GETINFO, &info);
143 * Since we cannot modify the size of the output buffer we fake it
144 * by not filling it more than u->buffer_size.
146 len = u->buffer_size;
147 len -= u->written_bytes - (info.play.samples * u->frame_size);
149 /* The sample counter can sometimes go backwards :( */
150 if (len > u->buffer_size)
153 if (!u->sink_underflow && (len == u->buffer_size))
154 pa_log_debug("Solaris buffer underflow!");
156 len -= len % u->frame_size;
161 if (!u->memchunk.length) {
162 if (pa_sink_render(u->sink, len, &u->memchunk) < 0) {
163 u->sink_underflow = 1;
168 u->sink_underflow = 0;
170 assert(u->memchunk.memblock);
171 assert(u->memchunk.memblock->data);
172 assert(u->memchunk.length);
174 if (u->memchunk.length < len) {
175 len = u->memchunk.length;
176 len -= len % u->frame_size;
180 if ((r = pa_iochannel_write(u->io,
181 (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, len)) < 0) {
182 pa_log("write() failed: %s", pa_cstrerror(errno));
186 assert(r % u->frame_size == 0);
188 u->memchunk.index += r;
189 u->memchunk.length -= r;
191 if (u->memchunk.length <= 0) {
192 pa_memblock_unref(u->memchunk.memblock);
193 u->memchunk.memblock = NULL;
196 u->written_bytes += r;
199 static void do_read(struct userdata *u) {
200 pa_memchunk memchunk;
206 if (!u->source || !pa_iochannel_is_readable(u->io))
211 err = ioctl(u->fd, I_NREAD, &l);
214 /* This is to make sure it fits in the memory pool. Also, a page
215 should be the most efficient transfer size. */
216 if (l > u->page_size)
219 memchunk.memblock = pa_memblock_new(u->core->mempool, l);
220 assert(memchunk.memblock);
221 if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) {
222 pa_memblock_unref(memchunk.memblock);
224 pa_log("read() failed: %s", pa_cstrerror(errno));
228 assert(r <= (ssize_t) memchunk.memblock->length);
229 memchunk.length = memchunk.memblock->length = r;
232 pa_source_post(u->source, &memchunk);
233 pa_memblock_unref(memchunk.memblock);
238 static void io_callback(pa_iochannel *io, void*userdata) {
239 struct userdata *u = userdata;
245 static void timer_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
246 struct userdata *u = userdata;
253 pa_gettimeofday(&ntv);
254 pa_timeval_add(&ntv, u->poll_timeout);
256 a->time_restart(e, &ntv);
259 static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
260 struct userdata *u = userdata;
266 assert(u->sink->get_hw_volume);
267 memcpy(&old_vol, &u->sink->hw_volume, sizeof(pa_cvolume));
268 if (u->sink->get_hw_volume(u->sink) < 0)
270 if (memcmp(&old_vol, &u->sink->hw_volume, sizeof(pa_cvolume)) != 0) {
271 pa_subscription_post(u->sink->core,
272 PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE,
278 assert(u->source->get_hw_volume);
279 memcpy(&old_vol, &u->source->hw_volume, sizeof(pa_cvolume));
280 if (u->source->get_hw_volume(u->source) < 0)
282 if (memcmp(&old_vol, &u->source->hw_volume, sizeof(pa_cvolume)) != 0) {
283 pa_subscription_post(u->source->core,
284 PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE,
290 static pa_usec_t sink_get_latency_cb(pa_sink *s) {
294 struct userdata *u = s->userdata;
295 assert(s && u && u->sink);
297 err = ioctl(u->fd, AUDIO_GETINFO, &info);
300 r += pa_bytes_to_usec(u->written_bytes, &s->sample_spec);
301 r -= pa_bytes_to_usec(info.play.samples * u->frame_size, &s->sample_spec);
303 if (u->memchunk.memblock)
304 r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
309 static pa_usec_t source_get_latency_cb(pa_source *s) {
311 struct userdata *u = s->userdata;
314 assert(s && u && u->source);
316 err = ioctl(u->fd, AUDIO_GETINFO, &info);
319 r += pa_bytes_to_usec(info.record.samples * u->frame_size, &s->sample_spec);
320 r -= pa_bytes_to_usec(u->read_bytes, &s->sample_spec);
325 static int sink_get_hw_volume_cb(pa_sink *s) {
326 struct userdata *u = s->userdata;
330 err = ioctl(u->fd, AUDIO_GETINFO, &info);
333 pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
334 info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
339 static int sink_set_hw_volume_cb(pa_sink *s) {
340 struct userdata *u = s->userdata;
343 AUDIO_INITINFO(&info);
345 info.play.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
346 assert(info.play.gain <= AUDIO_MAX_GAIN);
348 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
350 pa_log("AUDIO_SETINFO: Unsupported volume.");
352 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
359 static int sink_get_hw_mute_cb(pa_sink *s) {
360 struct userdata *u = s->userdata;
364 err = ioctl(u->fd, AUDIO_GETINFO, &info);
367 s->hw_muted = !!info.output_muted;
372 static int sink_set_hw_mute_cb(pa_sink *s) {
373 struct userdata *u = s->userdata;
376 AUDIO_INITINFO(&info);
378 info.output_muted = !!s->hw_muted;
380 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
381 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
388 static int source_get_hw_volume_cb(pa_source *s) {
389 struct userdata *u = s->userdata;
393 err = ioctl(u->fd, AUDIO_GETINFO, &info);
396 pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
397 info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
402 static int source_set_hw_volume_cb(pa_source *s) {
403 struct userdata *u = s->userdata;
406 AUDIO_INITINFO(&info);
408 info.record.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
409 assert(info.record.gain <= AUDIO_MAX_GAIN);
411 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
413 pa_log("AUDIO_SETINFO: Unsupported volume.");
415 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
422 static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
425 AUDIO_INITINFO(&info);
427 if (mode != O_RDONLY) {
428 info.play.sample_rate = ss->rate;
429 info.play.channels = ss->channels;
430 switch (ss->format) {
432 info.play.precision = 8;
433 info.play.encoding = AUDIO_ENCODING_LINEAR;
436 info.play.precision = 8;
437 info.play.encoding = AUDIO_ENCODING_ALAW;
440 info.play.precision = 8;
441 info.play.encoding = AUDIO_ENCODING_ULAW;
443 case PA_SAMPLE_S16NE:
444 info.play.precision = 16;
445 info.play.encoding = AUDIO_ENCODING_LINEAR;
452 if (mode != O_WRONLY) {
453 info.record.sample_rate = ss->rate;
454 info.record.channels = ss->channels;
455 switch (ss->format) {
457 info.record.precision = 8;
458 info.record.encoding = AUDIO_ENCODING_LINEAR;
461 info.record.precision = 8;
462 info.record.encoding = AUDIO_ENCODING_ALAW;
465 info.record.precision = 8;
466 info.record.encoding = AUDIO_ENCODING_ULAW;
468 case PA_SAMPLE_S16NE:
469 info.record.precision = 16;
470 info.record.encoding = AUDIO_ENCODING_LINEAR;
477 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
479 pa_log("AUDIO_SETINFO: Unsupported sample format.");
481 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
488 static int pa_solaris_set_buffer(int fd, int buffer_size) {
491 AUDIO_INITINFO(&info);
493 info.record.buffer_size = buffer_size;
495 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
497 pa_log("AUDIO_SETINFO: Unsupported buffer size.");
499 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
506 int pa__init(pa_core *c, pa_module*m) {
507 struct userdata *u = NULL;
512 int record = 1, playback = 1;
515 pa_modargs *ma = NULL;
520 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
521 pa_log("failed to parse module arguments.");
525 if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
526 pa_log("record= and playback= expect numeric argument.");
530 if (!playback && !record) {
531 pa_log("neither playback nor record enabled for device.");
535 mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
538 if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) {
539 pa_log("failed to parse buffer size argument");
543 ss = c->default_sample_spec;
544 if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
545 pa_log("failed to parse sample specification");
549 if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
552 pa_log_info("device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
554 if (pa_solaris_auto_format(fd, mode, &ss) < 0)
557 if ((mode != O_WRONLY) && (buffer_size >= 1))
558 if (pa_solaris_set_buffer(fd, buffer_size) < 0)
561 u = pa_xmalloc(sizeof(struct userdata));
564 if (mode != O_WRONLY) {
565 u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map);
567 u->source->userdata = u;
568 u->source->get_latency = source_get_latency_cb;
569 u->source->get_hw_volume = source_get_hw_volume_cb;
570 u->source->set_hw_volume = source_set_hw_volume_cb;
571 pa_source_set_owner(u->source, m);
572 pa_source_set_description(u->source, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
574 u->source->is_hardware = 1;
578 if (mode != O_RDONLY) {
579 u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map);
581 u->sink->get_latency = sink_get_latency_cb;
582 u->sink->get_hw_volume = sink_get_hw_volume_cb;
583 u->sink->set_hw_volume = sink_set_hw_volume_cb;
584 u->sink->get_hw_mute = sink_get_hw_mute_cb;
585 u->sink->set_hw_mute = sink_set_hw_mute_cb;
586 u->sink->userdata = u;
587 pa_sink_set_owner(u->sink, m);
588 pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
590 u->sink->is_hardware = 1;
594 assert(u->source || u->sink);
596 u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
598 pa_iochannel_set_callback(u->io, io_callback, u);
601 u->memchunk.memblock = NULL;
602 u->memchunk.length = 0;
604 /* We use this to get a reasonable chunk size */
605 u->page_size = sysconf(_SC_PAGESIZE);
607 u->frame_size = pa_frame_size(&ss);
608 u->buffer_size = buffer_size;
610 u->written_bytes = 0;
613 u->sink_underflow = 1;
618 u->poll_timeout = pa_bytes_to_usec(u->buffer_size / 10, &ss);
620 pa_gettimeofday(&tv);
621 pa_timeval_add(&tv, u->poll_timeout);
623 u->timer = c->mainloop->time_new(c->mainloop, &tv, timer_cb, u);
626 u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
628 ioctl(u->fd, I_SETSIG, S_MSG);
632 /* Read mixer settings */
634 source_get_hw_volume_cb(u->source);
636 sink_get_hw_volume_cb(u->sink);
637 sink_get_hw_mute_cb(u->sink);
652 void pa__done(pa_core *c, pa_module*m) {
656 if (!(u = m->userdata))
660 c->mainloop->time_free(u->timer);
661 ioctl(u->fd, I_SETSIG, 0);
662 pa_signal_free(u->sig);
664 if (u->memchunk.memblock)
665 pa_memblock_unref(u->memchunk.memblock);
668 pa_sink_disconnect(u->sink);
669 pa_sink_unref(u->sink);
673 pa_source_disconnect(u->source);
674 pa_source_unref(u->source);
677 pa_iochannel_free(u->io);