]> code.delx.au - pulseaudio/blob - src/modules/module-solaris.c
Sun's documentation about SIGPOLL on EOF:s is wrong, so use a timer based
[pulseaudio] / src / modules / module-solaris.c
1 /* $Id$ */
2
3 /***
4 This file is part of polypaudio.
5
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.
10
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.
15
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
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <assert.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <limits.h>
34 #include <sys/ioctl.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37
38 #include <signal.h>
39 #include <stropts.h>
40 #include <sys/conf.h>
41 #include <sys/audio.h>
42
43 #include <polyp/mainloop-signal.h>
44
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>
54
55 #include "module-solaris-symdef.h"
56
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>")
61
62 struct userdata {
63 pa_sink *sink;
64 pa_source *source;
65 pa_iochannel *io;
66 pa_core *core;
67 pa_time_event *timer;
68 pa_usec_t poll_timeout;
69 pa_signal_event *sig;
70
71 pa_memchunk memchunk, silence;
72
73 uint32_t sample_size;
74 uint32_t buffer_size;
75 unsigned int written_bytes, read_bytes;
76
77 int fd;
78 pa_module *module;
79 };
80
81 static const char* const valid_modargs[] = {
82 "sink_name",
83 "source_name",
84 "device",
85 "record",
86 "playback",
87 "buffer_size",
88 "format",
89 "rate",
90 "channels",
91 NULL
92 };
93
94 #define DEFAULT_SINK_NAME "solaris_output"
95 #define DEFAULT_SOURCE_NAME "solaris_input"
96 #define DEFAULT_DEVICE "/dev/audio"
97
98 #define CHUNK_SIZE 2048
99
100 static void update_usage(struct userdata *u) {
101 pa_module_set_used(u->module,
102 (u->sink ? pa_idxset_size(u->sink->inputs) : 0) +
103 (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) +
104 (u->source ? pa_idxset_size(u->source->outputs) : 0));
105 }
106
107 static void do_write(struct userdata *u) {
108 audio_info_t info;
109 int err;
110 pa_memchunk *memchunk;
111 size_t len;
112 ssize_t r;
113
114 assert(u);
115
116 /* We cannot check pa_iochannel_is_writable() because of our buffer hack */
117 if (!u->sink)
118 return;
119
120 update_usage(u);
121
122 err = ioctl(u->fd, AUDIO_GETINFO, &info);
123 assert(err >= 0);
124
125 /*
126 * Since we cannot modify the size of the output buffer we fake it
127 * by not filling it more than u->buffer_size.
128 */
129 len = u->buffer_size;
130 len -= u->written_bytes - (info.play.samples * u->sample_size);
131
132 if (len == u->buffer_size)
133 pa_log_debug(__FILE__": Solaris buffer underflow!");
134
135 if (len < u->sample_size)
136 return;
137
138 memchunk = &u->memchunk;
139
140 if (!memchunk->length)
141 if (pa_sink_render(u->sink, len, memchunk) < 0)
142 memchunk = &u->silence;
143
144 assert(memchunk->memblock);
145 assert(memchunk->memblock->data);
146 assert(memchunk->length);
147
148 if (memchunk->length < len)
149 len = memchunk->length;
150
151 if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, len)) < 0) {
152 pa_log(__FILE__": write() failed: %s", strerror(errno));
153 return;
154 }
155
156 if (memchunk == &u->silence)
157 assert(r % u->sample_size == 0);
158 else {
159 u->memchunk.index += r;
160 u->memchunk.length -= r;
161
162 if (u->memchunk.length <= 0) {
163 pa_memblock_unref(u->memchunk.memblock);
164 u->memchunk.memblock = NULL;
165 }
166 }
167
168 u->written_bytes += r;
169 }
170
171 static void do_read(struct userdata *u) {
172 pa_memchunk memchunk;
173 int err, l;
174 ssize_t r;
175 assert(u);
176
177 if (!u->source || !pa_iochannel_is_readable(u->io))
178 return;
179
180 update_usage(u);
181
182 err = ioctl(u->fd, I_NREAD, &l);
183 assert(err >= 0);
184
185 memchunk.memblock = pa_memblock_new(l, u->core->memblock_stat);
186 assert(memchunk.memblock);
187 if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) {
188 pa_memblock_unref(memchunk.memblock);
189 if (errno != EAGAIN)
190 pa_log(__FILE__": read() failed: %s", strerror(errno));
191 return;
192 }
193
194 assert(r <= (ssize_t) memchunk.memblock->length);
195 memchunk.length = memchunk.memblock->length = r;
196 memchunk.index = 0;
197
198 pa_source_post(u->source, &memchunk);
199 pa_memblock_unref(memchunk.memblock);
200
201 u->read_bytes += r;
202 }
203
204 static void io_callback(pa_iochannel *io, void*userdata) {
205 struct userdata *u = userdata;
206 assert(u);
207 do_write(u);
208 do_read(u);
209 }
210
211 static void timer_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
212 struct userdata *u = userdata;
213 struct timeval ntv;
214
215 assert(u);
216
217 do_write(u);
218
219 pa_gettimeofday(&ntv);
220 pa_timeval_add(&ntv, u->poll_timeout);
221
222 a->time_restart(e, &ntv);
223 }
224
225 static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
226 struct userdata *u = userdata;
227 pa_cvolume old_vol;
228
229 assert(u);
230
231 if (u->sink) {
232 assert(u->sink->get_hw_volume);
233 memcpy(&old_vol, &u->sink->hw_volume, sizeof(pa_cvolume));
234 if (u->sink->get_hw_volume(u->sink) < 0)
235 return;
236 if (memcmp(&old_vol, &u->sink->hw_volume, sizeof(pa_cvolume)) != 0) {
237 pa_subscription_post(u->sink->core,
238 PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE,
239 u->sink->index);
240 }
241 }
242
243 if (u->source) {
244 assert(u->source->get_hw_volume);
245 memcpy(&old_vol, &u->source->hw_volume, sizeof(pa_cvolume));
246 if (u->source->get_hw_volume(u->source) < 0)
247 return;
248 if (memcmp(&old_vol, &u->source->hw_volume, sizeof(pa_cvolume)) != 0) {
249 pa_subscription_post(u->source->core,
250 PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE,
251 u->source->index);
252 }
253 }
254 }
255
256 static pa_usec_t sink_get_latency_cb(pa_sink *s) {
257 pa_usec_t r = 0;
258 audio_info_t info;
259 int err;
260 struct userdata *u = s->userdata;
261 assert(s && u && u->sink);
262
263 err = ioctl(u->fd, AUDIO_GETINFO, &info);
264 assert(err >= 0);
265
266 r += pa_bytes_to_usec(u->written_bytes, &s->sample_spec);
267 r -= pa_bytes_to_usec(info.play.samples * u->sample_size, &s->sample_spec);
268
269 if (u->memchunk.memblock)
270 r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
271
272 return r;
273 }
274
275 static pa_usec_t source_get_latency_cb(pa_source *s) {
276 pa_usec_t r = 0;
277 struct userdata *u = s->userdata;
278 audio_info_t info;
279 int err;
280 assert(s && u && u->source);
281
282 err = ioctl(u->fd, AUDIO_GETINFO, &info);
283 assert(err >= 0);
284
285 r += pa_bytes_to_usec(info.record.samples * u->sample_size, &s->sample_spec);
286 r -= pa_bytes_to_usec(u->read_bytes, &s->sample_spec);
287
288 return r;
289 }
290
291 static int sink_get_hw_volume_cb(pa_sink *s) {
292 struct userdata *u = s->userdata;
293 audio_info_t info;
294 int err;
295
296 err = ioctl(u->fd, AUDIO_GETINFO, &info);
297 assert(err >= 0);
298
299 pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
300 info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
301
302 return 0;
303 }
304
305 static int sink_set_hw_volume_cb(pa_sink *s) {
306 struct userdata *u = s->userdata;
307 audio_info_t info;
308
309 AUDIO_INITINFO(&info);
310
311 info.play.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
312 assert(info.play.gain <= AUDIO_MAX_GAIN);
313
314 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
315 if (errno == EINVAL)
316 pa_log(__FILE__": AUDIO_SETINFO: Unsupported volume.");
317 else
318 pa_log(__FILE__": AUDIO_SETINFO: %s", strerror(errno));
319 return -1;
320 }
321
322 return 0;
323 }
324
325 static int sink_get_hw_mute_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 s->hw_muted = !!info.output_muted;
334
335 return 0;
336 }
337
338 static int sink_set_hw_mute_cb(pa_sink *s) {
339 struct userdata *u = s->userdata;
340 audio_info_t info;
341
342 AUDIO_INITINFO(&info);
343
344 info.output_muted = !!s->hw_muted;
345
346 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
347 pa_log(__FILE__": AUDIO_SETINFO: %s", strerror(errno));
348 return -1;
349 }
350
351 return 0;
352 }
353
354 static int source_get_hw_volume_cb(pa_source *s) {
355 struct userdata *u = s->userdata;
356 audio_info_t info;
357 int err;
358
359 err = ioctl(u->fd, AUDIO_GETINFO, &info);
360 assert(err >= 0);
361
362 pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
363 info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
364
365 return 0;
366 }
367
368 static int source_set_hw_volume_cb(pa_source *s) {
369 struct userdata *u = s->userdata;
370 audio_info_t info;
371
372 AUDIO_INITINFO(&info);
373
374 info.record.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
375 assert(info.record.gain <= AUDIO_MAX_GAIN);
376
377 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
378 if (errno == EINVAL)
379 pa_log(__FILE__": AUDIO_SETINFO: Unsupported volume.");
380 else
381 pa_log(__FILE__": AUDIO_SETINFO: %s", strerror(errno));
382 return -1;
383 }
384
385 return 0;
386 }
387
388 static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
389 audio_info_t info;
390
391 AUDIO_INITINFO(&info);
392
393 if (mode != O_RDONLY) {
394 info.play.sample_rate = ss->rate;
395 info.play.channels = ss->channels;
396 switch (ss->format) {
397 case PA_SAMPLE_U8:
398 info.play.precision = 8;
399 info.play.encoding = AUDIO_ENCODING_LINEAR;
400 break;
401 case PA_SAMPLE_ALAW:
402 info.play.precision = 8;
403 info.play.encoding = AUDIO_ENCODING_ALAW;
404 break;
405 case PA_SAMPLE_ULAW:
406 info.play.precision = 8;
407 info.play.encoding = AUDIO_ENCODING_ULAW;
408 break;
409 case PA_SAMPLE_S16NE:
410 info.play.precision = 16;
411 info.play.encoding = AUDIO_ENCODING_LINEAR;
412 break;
413 default:
414 return -1;
415 }
416 }
417
418 if (mode != O_WRONLY) {
419 info.record.sample_rate = ss->rate;
420 info.record.channels = ss->channels;
421 switch (ss->format) {
422 case PA_SAMPLE_U8:
423 info.record.precision = 8;
424 info.record.encoding = AUDIO_ENCODING_LINEAR;
425 break;
426 case PA_SAMPLE_ALAW:
427 info.record.precision = 8;
428 info.record.encoding = AUDIO_ENCODING_ALAW;
429 break;
430 case PA_SAMPLE_ULAW:
431 info.record.precision = 8;
432 info.record.encoding = AUDIO_ENCODING_ULAW;
433 break;
434 case PA_SAMPLE_S16NE:
435 info.record.precision = 16;
436 info.record.encoding = AUDIO_ENCODING_LINEAR;
437 break;
438 default:
439 return -1;
440 }
441 }
442
443 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
444 if (errno == EINVAL)
445 pa_log(__FILE__": AUDIO_SETINFO: Unsupported sample format.");
446 else
447 pa_log(__FILE__": AUDIO_SETINFO: %s", strerror(errno));
448 return -1;
449 }
450
451 return 0;
452 }
453
454 static int pa_solaris_set_buffer(int fd, int buffer_size) {
455 audio_info_t info;
456
457 AUDIO_INITINFO(&info);
458
459 info.record.buffer_size = buffer_size;
460
461 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
462 if (errno == EINVAL)
463 pa_log(__FILE__": AUDIO_SETINFO: Unsupported buffer size.");
464 else
465 pa_log(__FILE__": AUDIO_SETINFO: %s", strerror(errno));
466 return -1;
467 }
468
469 return 0;
470 }
471
472 int pa__init(pa_core *c, pa_module*m) {
473 struct userdata *u = NULL;
474 const char *p;
475 int fd = -1;
476 int buffer_size;
477 int mode;
478 int record = 1, playback = 1;
479 pa_sample_spec ss;
480 pa_modargs *ma = NULL;
481 struct timeval tv;
482 assert(c && m);
483
484 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
485 pa_log(__FILE__": failed to parse module arguments.");
486 goto fail;
487 }
488
489 if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
490 pa_log(__FILE__": record= and playback= expect numeric argument.");
491 goto fail;
492 }
493
494 if (!playback && !record) {
495 pa_log(__FILE__": neither playback nor record enabled for device.");
496 goto fail;
497 }
498
499 mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
500
501 buffer_size = 16384;
502 if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) {
503 pa_log(__FILE__": failed to parse buffer size argument");
504 goto fail;
505 }
506
507 ss = c->default_sample_spec;
508 if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
509 pa_log(__FILE__": failed to parse sample specification");
510 goto fail;
511 }
512
513 if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
514 goto fail;
515
516 pa_log_info(__FILE__": device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
517
518 if (pa_solaris_auto_format(fd, mode, &ss) < 0)
519 goto fail;
520
521 if ((mode != O_WRONLY) && (buffer_size >= 1))
522 if (pa_solaris_set_buffer(fd, buffer_size) < 0)
523 goto fail;
524
525 u = pa_xmalloc(sizeof(struct userdata));
526 u->core = c;
527
528 if (mode != O_WRONLY) {
529 u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL);
530 assert(u->source);
531 u->source->userdata = u;
532 u->source->get_latency = source_get_latency_cb;
533 u->source->get_hw_volume = source_get_hw_volume_cb;
534 u->source->set_hw_volume = source_set_hw_volume_cb;
535 pa_source_set_owner(u->source, m);
536 u->source->description = pa_sprintf_malloc("Solaris PCM on '%s'", p);
537 } else
538 u->source = NULL;
539
540 if (mode != O_RDONLY) {
541 u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL);
542 assert(u->sink);
543 u->sink->get_latency = sink_get_latency_cb;
544 u->sink->get_hw_volume = sink_get_hw_volume_cb;
545 u->sink->set_hw_volume = sink_set_hw_volume_cb;
546 u->sink->get_hw_mute = sink_get_hw_mute_cb;
547 u->sink->set_hw_mute = sink_set_hw_mute_cb;
548 u->sink->userdata = u;
549 pa_sink_set_owner(u->sink, m);
550 u->sink->description = pa_sprintf_malloc("Solaris PCM on '%s'", p);
551 } else
552 u->sink = NULL;
553
554 assert(u->source || u->sink);
555
556 u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
557 assert(u->io);
558 pa_iochannel_set_callback(u->io, io_callback, u);
559 u->fd = fd;
560
561 u->memchunk.memblock = NULL;
562 u->memchunk.length = 0;
563 u->sample_size = pa_frame_size(&ss);
564 u->buffer_size = buffer_size;
565
566 u->silence.memblock = pa_memblock_new(u->silence.length = CHUNK_SIZE, u->core->memblock_stat);
567 assert(u->silence.memblock);
568 pa_silence_memblock(u->silence.memblock, &ss);
569 u->silence.index = 0;
570
571 u->written_bytes = 0;
572 u->read_bytes = 0;
573
574 u->module = m;
575 m->userdata = u;
576
577 u->poll_timeout = pa_bytes_to_usec(u->buffer_size / 10, &ss);
578
579 pa_gettimeofday(&tv);
580 pa_timeval_add(&tv, u->poll_timeout);
581
582 u->timer = c->mainloop->time_new(c->mainloop, &tv, timer_cb, u);
583 assert(u->timer);
584
585 u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
586 assert(u->sig);
587 ioctl(u->fd, I_SETSIG, S_MSG);
588
589 pa_modargs_free(ma);
590
591 /* Read mixer settings */
592 if (u->source)
593 source_get_hw_volume_cb(u->source);
594 if (u->sink) {
595 sink_get_hw_volume_cb(u->sink);
596 sink_get_hw_mute_cb(u->sink);
597 }
598
599 return 0;
600
601 fail:
602 if (fd >= 0)
603 close(fd);
604
605 if (ma)
606 pa_modargs_free(ma);
607
608 return -1;
609 }
610
611 void pa__done(pa_core *c, pa_module*m) {
612 struct userdata *u;
613 assert(c && m);
614
615 if (!(u = m->userdata))
616 return;
617
618 if (u->timer)
619 c->mainloop->time_free(u->timer);
620 ioctl(u->fd, I_SETSIG, 0);
621 pa_signal_free(u->sig);
622
623 if (u->memchunk.memblock)
624 pa_memblock_unref(u->memchunk.memblock);
625 if (u->silence.memblock)
626 pa_memblock_unref(u->silence.memblock);
627
628 if (u->sink) {
629 pa_sink_disconnect(u->sink);
630 pa_sink_unref(u->sink);
631 }
632
633 if (u->source) {
634 pa_source_disconnect(u->source);
635 pa_source_unref(u->source);
636 }
637
638 pa_iochannel_free(u->io);
639 pa_xfree(u);
640 }