]> code.delx.au - pulseaudio/blob - src/modules/module-solaris.c
a50f1ecf8889183b6970b0eff5d87501735011a2
[pulseaudio] / src / modules / module-solaris.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 Copyright 2006 Lennart Poettering
7 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
8
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.
13
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.
18
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
22 USA.
23 ***/
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <assert.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <limits.h>
37 #include <sys/ioctl.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40
41 #include <signal.h>
42 #include <stropts.h>
43 #include <sys/conf.h>
44 #include <sys/audio.h>
45
46 #include <pulse/error.h>
47 #include <pulse/mainloop-signal.h>
48 #include <pulse/xmalloc.h>
49 #include <pulse/timeval.h>
50
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>
60
61 #include "module-solaris-symdef.h"
62
63 PA_MODULE_AUTHOR("Pierre Ossman")
64 PA_MODULE_DESCRIPTION("Solaris Sink/Source")
65 PA_MODULE_VERSION(PACKAGE_VERSION)
66 PA_MODULE_USAGE(
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> "
73 "rate=<sample rate> "
74 "buffer_size=<record buffer size> "
75 "channel_map=<channel map>")
76
77 struct userdata {
78 pa_sink *sink;
79 pa_source *source;
80 pa_iochannel *io;
81 pa_core *core;
82 pa_time_event *timer;
83 pa_usec_t poll_timeout;
84 pa_signal_event *sig;
85
86 pa_memchunk memchunk;
87
88 unsigned int page_size;
89
90 uint32_t frame_size;
91 uint32_t buffer_size;
92 unsigned int written_bytes, read_bytes;
93 int sink_underflow;
94
95 int fd;
96 pa_module *module;
97 };
98
99 static const char* const valid_modargs[] = {
100 "sink_name",
101 "source_name",
102 "device",
103 "record",
104 "playback",
105 "buffer_size",
106 "format",
107 "rate",
108 "channels",
109 "channel_map",
110 NULL
111 };
112
113 #define DEFAULT_SINK_NAME "solaris_output"
114 #define DEFAULT_SOURCE_NAME "solaris_input"
115 #define DEFAULT_DEVICE "/dev/audio"
116
117 #define CHUNK_SIZE 2048
118
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));
123 }
124
125 static void do_write(struct userdata *u) {
126 audio_info_t info;
127 int err;
128 size_t len;
129 ssize_t r;
130
131 assert(u);
132
133 /* We cannot check pa_iochannel_is_writable() because of our buffer hack */
134 if (!u->sink)
135 return;
136
137 update_usage(u);
138
139 err = ioctl(u->fd, AUDIO_GETINFO, &info);
140 assert(err >= 0);
141
142 /*
143 * Since we cannot modify the size of the output buffer we fake it
144 * by not filling it more than u->buffer_size.
145 */
146 len = u->buffer_size;
147 len -= u->written_bytes - (info.play.samples * u->frame_size);
148
149 /* The sample counter can sometimes go backwards :( */
150 if (len > u->buffer_size)
151 len = 0;
152
153 if (!u->sink_underflow && (len == u->buffer_size))
154 pa_log_debug("Solaris buffer underflow!");
155
156 len -= len % u->frame_size;
157
158 if (len == 0)
159 return;
160
161 if (!u->memchunk.length) {
162 if (pa_sink_render(u->sink, len, &u->memchunk) < 0) {
163 u->sink_underflow = 1;
164 return;
165 }
166 }
167
168 u->sink_underflow = 0;
169
170 assert(u->memchunk.memblock);
171 assert(u->memchunk.memblock->data);
172 assert(u->memchunk.length);
173
174 if (u->memchunk.length < len) {
175 len = u->memchunk.length;
176 len -= len % u->frame_size;
177 assert(len);
178 }
179
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));
183 return;
184 }
185
186 assert(r % u->frame_size == 0);
187
188 u->memchunk.index += r;
189 u->memchunk.length -= r;
190
191 if (u->memchunk.length <= 0) {
192 pa_memblock_unref(u->memchunk.memblock);
193 u->memchunk.memblock = NULL;
194 }
195
196 u->written_bytes += r;
197 }
198
199 static void do_read(struct userdata *u) {
200 pa_memchunk memchunk;
201 int err;
202 size_t l;
203 ssize_t r;
204 assert(u);
205
206 if (!u->source || !pa_iochannel_is_readable(u->io))
207 return;
208
209 update_usage(u);
210
211 err = ioctl(u->fd, I_NREAD, &l);
212 assert(err >= 0);
213
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)
217 l = u->page_size;
218
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);
223 if (errno != EAGAIN)
224 pa_log("read() failed: %s", pa_cstrerror(errno));
225 return;
226 }
227
228 assert(r <= (ssize_t) memchunk.memblock->length);
229 memchunk.length = memchunk.memblock->length = r;
230 memchunk.index = 0;
231
232 pa_source_post(u->source, &memchunk);
233 pa_memblock_unref(memchunk.memblock);
234
235 u->read_bytes += r;
236 }
237
238 static void io_callback(pa_iochannel *io, void*userdata) {
239 struct userdata *u = userdata;
240 assert(u);
241 do_write(u);
242 do_read(u);
243 }
244
245 static void timer_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
246 struct userdata *u = userdata;
247 struct timeval ntv;
248
249 assert(u);
250
251 do_write(u);
252
253 pa_gettimeofday(&ntv);
254 pa_timeval_add(&ntv, u->poll_timeout);
255
256 a->time_restart(e, &ntv);
257 }
258
259 static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
260 struct userdata *u = userdata;
261 pa_cvolume old_vol;
262
263 assert(u);
264
265 if (u->sink) {
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)
269 return;
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,
273 u->sink->index);
274 }
275 }
276
277 if (u->source) {
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)
281 return;
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,
285 u->source->index);
286 }
287 }
288 }
289
290 static pa_usec_t sink_get_latency_cb(pa_sink *s) {
291 pa_usec_t r = 0;
292 audio_info_t info;
293 int err;
294 struct userdata *u = s->userdata;
295 assert(s && u && u->sink);
296
297 err = ioctl(u->fd, AUDIO_GETINFO, &info);
298 assert(err >= 0);
299
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);
302
303 if (u->memchunk.memblock)
304 r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
305
306 return r;
307 }
308
309 static pa_usec_t source_get_latency_cb(pa_source *s) {
310 pa_usec_t r = 0;
311 struct userdata *u = s->userdata;
312 audio_info_t info;
313 int err;
314 assert(s && u && u->source);
315
316 err = ioctl(u->fd, AUDIO_GETINFO, &info);
317 assert(err >= 0);
318
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);
321
322 return r;
323 }
324
325 static int sink_get_hw_volume_cb(pa_sink *s) {
326 struct userdata *u = s->userdata;
327 audio_info_t info;
328 int err;
329
330 err = ioctl(u->fd, AUDIO_GETINFO, &info);
331 assert(err >= 0);
332
333 pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
334 info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
335
336 return 0;
337 }
338
339 static int sink_set_hw_volume_cb(pa_sink *s) {
340 struct userdata *u = s->userdata;
341 audio_info_t info;
342
343 AUDIO_INITINFO(&info);
344
345 info.play.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
346 assert(info.play.gain <= AUDIO_MAX_GAIN);
347
348 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
349 if (errno == EINVAL)
350 pa_log("AUDIO_SETINFO: Unsupported volume.");
351 else
352 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
353 return -1;
354 }
355
356 return 0;
357 }
358
359 static int sink_get_hw_mute_cb(pa_sink *s) {
360 struct userdata *u = s->userdata;
361 audio_info_t info;
362 int err;
363
364 err = ioctl(u->fd, AUDIO_GETINFO, &info);
365 assert(err >= 0);
366
367 s->hw_muted = !!info.output_muted;
368
369 return 0;
370 }
371
372 static int sink_set_hw_mute_cb(pa_sink *s) {
373 struct userdata *u = s->userdata;
374 audio_info_t info;
375
376 AUDIO_INITINFO(&info);
377
378 info.output_muted = !!s->hw_muted;
379
380 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
381 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
382 return -1;
383 }
384
385 return 0;
386 }
387
388 static int source_get_hw_volume_cb(pa_source *s) {
389 struct userdata *u = s->userdata;
390 audio_info_t info;
391 int err;
392
393 err = ioctl(u->fd, AUDIO_GETINFO, &info);
394 assert(err >= 0);
395
396 pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
397 info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
398
399 return 0;
400 }
401
402 static int source_set_hw_volume_cb(pa_source *s) {
403 struct userdata *u = s->userdata;
404 audio_info_t info;
405
406 AUDIO_INITINFO(&info);
407
408 info.record.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
409 assert(info.record.gain <= AUDIO_MAX_GAIN);
410
411 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
412 if (errno == EINVAL)
413 pa_log("AUDIO_SETINFO: Unsupported volume.");
414 else
415 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
416 return -1;
417 }
418
419 return 0;
420 }
421
422 static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
423 audio_info_t info;
424
425 AUDIO_INITINFO(&info);
426
427 if (mode != O_RDONLY) {
428 info.play.sample_rate = ss->rate;
429 info.play.channels = ss->channels;
430 switch (ss->format) {
431 case PA_SAMPLE_U8:
432 info.play.precision = 8;
433 info.play.encoding = AUDIO_ENCODING_LINEAR;
434 break;
435 case PA_SAMPLE_ALAW:
436 info.play.precision = 8;
437 info.play.encoding = AUDIO_ENCODING_ALAW;
438 break;
439 case PA_SAMPLE_ULAW:
440 info.play.precision = 8;
441 info.play.encoding = AUDIO_ENCODING_ULAW;
442 break;
443 case PA_SAMPLE_S16NE:
444 info.play.precision = 16;
445 info.play.encoding = AUDIO_ENCODING_LINEAR;
446 break;
447 default:
448 return -1;
449 }
450 }
451
452 if (mode != O_WRONLY) {
453 info.record.sample_rate = ss->rate;
454 info.record.channels = ss->channels;
455 switch (ss->format) {
456 case PA_SAMPLE_U8:
457 info.record.precision = 8;
458 info.record.encoding = AUDIO_ENCODING_LINEAR;
459 break;
460 case PA_SAMPLE_ALAW:
461 info.record.precision = 8;
462 info.record.encoding = AUDIO_ENCODING_ALAW;
463 break;
464 case PA_SAMPLE_ULAW:
465 info.record.precision = 8;
466 info.record.encoding = AUDIO_ENCODING_ULAW;
467 break;
468 case PA_SAMPLE_S16NE:
469 info.record.precision = 16;
470 info.record.encoding = AUDIO_ENCODING_LINEAR;
471 break;
472 default:
473 return -1;
474 }
475 }
476
477 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
478 if (errno == EINVAL)
479 pa_log("AUDIO_SETINFO: Unsupported sample format.");
480 else
481 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
482 return -1;
483 }
484
485 return 0;
486 }
487
488 static int pa_solaris_set_buffer(int fd, int buffer_size) {
489 audio_info_t info;
490
491 AUDIO_INITINFO(&info);
492
493 info.record.buffer_size = buffer_size;
494
495 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
496 if (errno == EINVAL)
497 pa_log("AUDIO_SETINFO: Unsupported buffer size.");
498 else
499 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
500 return -1;
501 }
502
503 return 0;
504 }
505
506 int pa__init(pa_core *c, pa_module*m) {
507 struct userdata *u = NULL;
508 const char *p;
509 int fd = -1;
510 int buffer_size;
511 int mode;
512 int record = 1, playback = 1;
513 pa_sample_spec ss;
514 pa_channel_map map;
515 pa_modargs *ma = NULL;
516 struct timeval tv;
517 char *t;
518 assert(c && m);
519
520 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
521 pa_log("failed to parse module arguments.");
522 goto fail;
523 }
524
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.");
527 goto fail;
528 }
529
530 if (!playback && !record) {
531 pa_log("neither playback nor record enabled for device.");
532 goto fail;
533 }
534
535 mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
536
537 buffer_size = 16384;
538 if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) {
539 pa_log("failed to parse buffer size argument");
540 goto fail;
541 }
542
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");
546 goto fail;
547 }
548
549 if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
550 goto fail;
551
552 pa_log_info("device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
553
554 if (pa_solaris_auto_format(fd, mode, &ss) < 0)
555 goto fail;
556
557 if ((mode != O_WRONLY) && (buffer_size >= 1))
558 if (pa_solaris_set_buffer(fd, buffer_size) < 0)
559 goto fail;
560
561 u = pa_xmalloc(sizeof(struct userdata));
562 u->core = c;
563
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);
566 assert(u->source);
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));
573 pa_xfree(t);
574 u->source->is_hardware = 1;
575 } else
576 u->source = NULL;
577
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);
580 assert(u->sink);
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));
589 pa_xfree(t);
590 u->sink->is_hardware = 1;
591 } else
592 u->sink = NULL;
593
594 assert(u->source || u->sink);
595
596 u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
597 assert(u->io);
598 pa_iochannel_set_callback(u->io, io_callback, u);
599 u->fd = fd;
600
601 u->memchunk.memblock = NULL;
602 u->memchunk.length = 0;
603
604 /* We use this to get a reasonable chunk size */
605 u->page_size = sysconf(_SC_PAGESIZE);
606
607 u->frame_size = pa_frame_size(&ss);
608 u->buffer_size = buffer_size;
609
610 u->written_bytes = 0;
611 u->read_bytes = 0;
612
613 u->sink_underflow = 1;
614
615 u->module = m;
616 m->userdata = u;
617
618 u->poll_timeout = pa_bytes_to_usec(u->buffer_size / 10, &ss);
619
620 pa_gettimeofday(&tv);
621 pa_timeval_add(&tv, u->poll_timeout);
622
623 u->timer = c->mainloop->time_new(c->mainloop, &tv, timer_cb, u);
624 assert(u->timer);
625
626 u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
627 assert(u->sig);
628 ioctl(u->fd, I_SETSIG, S_MSG);
629
630 pa_modargs_free(ma);
631
632 /* Read mixer settings */
633 if (u->source)
634 source_get_hw_volume_cb(u->source);
635 if (u->sink) {
636 sink_get_hw_volume_cb(u->sink);
637 sink_get_hw_mute_cb(u->sink);
638 }
639
640 return 0;
641
642 fail:
643 if (fd >= 0)
644 close(fd);
645
646 if (ma)
647 pa_modargs_free(ma);
648
649 return -1;
650 }
651
652 void pa__done(pa_core *c, pa_module*m) {
653 struct userdata *u;
654 assert(c && m);
655
656 if (!(u = m->userdata))
657 return;
658
659 if (u->timer)
660 c->mainloop->time_free(u->timer);
661 ioctl(u->fd, I_SETSIG, 0);
662 pa_signal_free(u->sig);
663
664 if (u->memchunk.memblock)
665 pa_memblock_unref(u->memchunk.memblock);
666
667 if (u->sink) {
668 pa_sink_disconnect(u->sink);
669 pa_sink_unref(u->sink);
670 }
671
672 if (u->source) {
673 pa_source_disconnect(u->source);
674 pa_source_unref(u->source);
675 }
676
677 pa_iochannel_free(u->io);
678 pa_xfree(u);
679 }