]> code.delx.au - pulseaudio/blob - src/modules/module-solaris.c
Make local function static.
[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_signal_event *sig;
68
69 pa_memchunk memchunk, silence;
70
71 uint32_t sample_size;
72 uint32_t buffer_size;
73 unsigned int written_bytes, read_bytes;
74
75 int fd;
76 pa_module *module;
77 };
78
79 static const char* const valid_modargs[] = {
80 "sink_name",
81 "source_name",
82 "device",
83 "record",
84 "playback",
85 "buffer_size",
86 "format",
87 "rate",
88 "channels",
89 NULL
90 };
91
92 #define DEFAULT_SINK_NAME "solaris_output"
93 #define DEFAULT_SOURCE_NAME "solaris_input"
94 #define DEFAULT_DEVICE "/dev/audio"
95
96 #define CHUNK_SIZE 2048
97
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));
103 }
104
105 static void do_write(struct userdata *u) {
106 audio_info_t info;
107 int err;
108 pa_memchunk *memchunk;
109 size_t len;
110 ssize_t r;
111
112 assert(u);
113
114 if (!u->sink || !pa_iochannel_is_writable(u->io))
115 return;
116
117 update_usage(u);
118
119 err = ioctl(u->fd, AUDIO_GETINFO, &info);
120 assert(err >= 0);
121
122 /*
123 * Since we cannot modify the size of the output buffer we fake it
124 * by not filling it more than u->buffer_size.
125 */
126 len = u->buffer_size;
127 len -= u->written_bytes - (info.play.samples * u->sample_size);
128
129 /*
130 * Do not fill more than half the buffer in one chunk since we only
131 * get notifications upon completion of entire chunks.
132 */
133 if (len > (u->buffer_size / 2))
134 len = u->buffer_size / 2;
135
136 if (len < u->sample_size)
137 return;
138
139 memchunk = &u->memchunk;
140
141 if (!memchunk->length)
142 if (pa_sink_render(u->sink, len, memchunk) < 0)
143 memchunk = &u->silence;
144
145 assert(memchunk->memblock);
146 assert(memchunk->memblock->data);
147 assert(memchunk->length);
148
149 if (memchunk->length < len)
150 len = memchunk->length;
151
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));
154 return;
155 }
156
157 if (memchunk == &u->silence)
158 assert(r % u->sample_size == 0);
159 else {
160 u->memchunk.index += r;
161 u->memchunk.length -= r;
162
163 if (u->memchunk.length <= 0) {
164 pa_memblock_unref(u->memchunk.memblock);
165 u->memchunk.memblock = NULL;
166 }
167 }
168
169 u->written_bytes += r;
170
171 /*
172 * Write 0 bytes which will generate a SIGPOLL when "played".
173 */
174 if (write(u->fd, NULL, 0) < 0) {
175 pa_log(__FILE__": write() failed: %s", strerror(errno));
176 return;
177 }
178 }
179
180 static void do_read(struct userdata *u) {
181 pa_memchunk memchunk;
182 int err, l;
183 ssize_t r;
184 assert(u);
185
186 if (!u->source || !pa_iochannel_is_readable(u->io))
187 return;
188
189 update_usage(u);
190
191 err = ioctl(u->fd, I_NREAD, &l);
192 assert(err >= 0);
193
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);
198 if (errno != EAGAIN)
199 pa_log(__FILE__": read() failed: %s", strerror(errno));
200 return;
201 }
202
203 assert(r <= (ssize_t) memchunk.memblock->length);
204 memchunk.length = memchunk.memblock->length = r;
205 memchunk.index = 0;
206
207 pa_source_post(u->source, &memchunk);
208 pa_memblock_unref(memchunk.memblock);
209
210 u->read_bytes += r;
211 }
212
213 static void io_callback(pa_iochannel *io, void*userdata) {
214 struct userdata *u = userdata;
215 assert(u);
216 do_write(u);
217 do_read(u);
218 }
219
220 static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
221 struct userdata *u = userdata;
222 assert(u);
223 do_write(u);
224 }
225
226 static pa_usec_t sink_get_latency_cb(pa_sink *s) {
227 pa_usec_t r = 0;
228 audio_info_t info;
229 int err;
230 struct userdata *u = s->userdata;
231 assert(s && u && u->sink);
232
233 err = ioctl(u->fd, AUDIO_GETINFO, &info);
234 assert(err >= 0);
235
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);
238
239 if (u->memchunk.memblock)
240 r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
241
242 return r;
243 }
244
245 static pa_usec_t source_get_latency_cb(pa_source *s) {
246 pa_usec_t r = 0;
247 struct userdata *u = s->userdata;
248 audio_info_t info;
249 int err;
250 assert(s && u && u->source);
251
252 err = ioctl(u->fd, AUDIO_GETINFO, &info);
253 assert(err >= 0);
254
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);
257
258 return r;
259 }
260
261 static int sink_get_hw_volume_cb(pa_sink *s) {
262 struct userdata *u = s->userdata;
263 audio_info_t info;
264 int err;
265
266 err = ioctl(u->fd, AUDIO_GETINFO, &info);
267 assert(err >= 0);
268
269 pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
270 info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
271
272 return 0;
273 }
274
275 static int sink_set_hw_volume_cb(pa_sink *s) {
276 struct userdata *u = s->userdata;
277 audio_info_t info;
278
279 AUDIO_INITINFO(&info);
280
281 info.play.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
282 assert(info.play.gain <= AUDIO_MAX_GAIN);
283
284 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
285 if (errno == EINVAL)
286 pa_log(__FILE__": AUDIO_SETINFO: Unsupported volume.");
287 else
288 pa_log(__FILE__": AUDIO_SETINFO: %s", strerror(errno));
289 return -1;
290 }
291
292 return 0;
293 }
294
295 static int sink_get_hw_mute_cb(pa_sink *s) {
296 struct userdata *u = s->userdata;
297 audio_info_t info;
298 int err;
299
300 err = ioctl(u->fd, AUDIO_GETINFO, &info);
301 assert(err >= 0);
302
303 s->hw_muted = !!info.output_muted;
304
305 return 0;
306 }
307
308 static int sink_set_hw_mute_cb(pa_sink *s) {
309 struct userdata *u = s->userdata;
310 audio_info_t info;
311
312 AUDIO_INITINFO(&info);
313
314 info.output_muted = !!s->hw_muted;
315
316 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
317 pa_log(__FILE__": AUDIO_SETINFO: %s", strerror(errno));
318 return -1;
319 }
320
321 return 0;
322 }
323
324 static int source_get_hw_volume_cb(pa_source *s) {
325 struct userdata *u = s->userdata;
326 audio_info_t info;
327 int err;
328
329 err = ioctl(u->fd, AUDIO_GETINFO, &info);
330 assert(err >= 0);
331
332 pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
333 info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
334
335 return 0;
336 }
337
338 static int source_set_hw_volume_cb(pa_source *s) {
339 struct userdata *u = s->userdata;
340 audio_info_t info;
341
342 AUDIO_INITINFO(&info);
343
344 info.record.gain = pa_cvolume_avg(&s->hw_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
345 assert(info.record.gain <= AUDIO_MAX_GAIN);
346
347 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
348 if (errno == EINVAL)
349 pa_log(__FILE__": AUDIO_SETINFO: Unsupported volume.");
350 else
351 pa_log(__FILE__": AUDIO_SETINFO: %s", strerror(errno));
352 return -1;
353 }
354
355 return 0;
356 }
357
358 static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
359 audio_info_t info;
360
361 AUDIO_INITINFO(&info);
362
363 if (mode != O_RDONLY) {
364 info.play.sample_rate = ss->rate;
365 info.play.channels = ss->channels;
366 switch (ss->format) {
367 case PA_SAMPLE_U8:
368 info.play.precision = 8;
369 info.play.encoding = AUDIO_ENCODING_LINEAR;
370 break;
371 case PA_SAMPLE_ALAW:
372 info.play.precision = 8;
373 info.play.encoding = AUDIO_ENCODING_ALAW;
374 break;
375 case PA_SAMPLE_ULAW:
376 info.play.precision = 8;
377 info.play.encoding = AUDIO_ENCODING_ULAW;
378 break;
379 case PA_SAMPLE_S16NE:
380 info.play.precision = 16;
381 info.play.encoding = AUDIO_ENCODING_LINEAR;
382 break;
383 default:
384 return -1;
385 }
386 }
387
388 if (mode != O_WRONLY) {
389 info.record.sample_rate = ss->rate;
390 info.record.channels = ss->channels;
391 switch (ss->format) {
392 case PA_SAMPLE_U8:
393 info.record.precision = 8;
394 info.record.encoding = AUDIO_ENCODING_LINEAR;
395 break;
396 case PA_SAMPLE_ALAW:
397 info.record.precision = 8;
398 info.record.encoding = AUDIO_ENCODING_ALAW;
399 break;
400 case PA_SAMPLE_ULAW:
401 info.record.precision = 8;
402 info.record.encoding = AUDIO_ENCODING_ULAW;
403 break;
404 case PA_SAMPLE_S16NE:
405 info.record.precision = 16;
406 info.record.encoding = AUDIO_ENCODING_LINEAR;
407 break;
408 default:
409 return -1;
410 }
411 }
412
413 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
414 if (errno == EINVAL)
415 pa_log(__FILE__": AUDIO_SETINFO: Unsupported sample format.");
416 else
417 pa_log(__FILE__": AUDIO_SETINFO: %s", strerror(errno));
418 return -1;
419 }
420
421 return 0;
422 }
423
424 static int pa_solaris_set_buffer(int fd, int buffer_size) {
425 audio_info_t info;
426
427 AUDIO_INITINFO(&info);
428
429 info.record.buffer_size = buffer_size;
430
431 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
432 if (errno == EINVAL)
433 pa_log(__FILE__": AUDIO_SETINFO: Unsupported buffer size.");
434 else
435 pa_log(__FILE__": AUDIO_SETINFO: %s", strerror(errno));
436 return -1;
437 }
438
439 return 0;
440 }
441
442 int pa__init(pa_core *c, pa_module*m) {
443 struct userdata *u = NULL;
444 const char *p;
445 int fd = -1;
446 int buffer_size;
447 int mode;
448 int record = 1, playback = 1;
449 pa_sample_spec ss;
450 pa_modargs *ma = NULL;
451 assert(c && m);
452
453 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
454 pa_log(__FILE__": failed to parse module arguments.");
455 goto fail;
456 }
457
458 if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
459 pa_log(__FILE__": record= and playback= expect numeric argument.");
460 goto fail;
461 }
462
463 if (!playback && !record) {
464 pa_log(__FILE__": neither playback nor record enabled for device.");
465 goto fail;
466 }
467
468 mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
469
470 buffer_size = 16384;
471 if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) {
472 pa_log(__FILE__": failed to parse buffer size argument");
473 goto fail;
474 }
475
476 ss = c->default_sample_spec;
477 if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
478 pa_log(__FILE__": failed to parse sample specification");
479 goto fail;
480 }
481
482 if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
483 goto fail;
484
485 pa_log_info(__FILE__": device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
486
487 if (pa_solaris_auto_format(fd, mode, &ss) < 0)
488 goto fail;
489
490 if ((mode != O_WRONLY) && (buffer_size >= 1))
491 if (pa_solaris_set_buffer(fd, buffer_size) < 0)
492 goto fail;
493
494 u = pa_xmalloc(sizeof(struct userdata));
495 u->core = c;
496
497 if (mode != O_WRONLY) {
498 u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL);
499 assert(u->source);
500 u->source->userdata = u;
501 u->source->get_latency = source_get_latency_cb;
502 u->source->get_hw_volume = source_get_hw_volume_cb;
503 u->source->set_hw_volume = source_set_hw_volume_cb;
504 pa_source_set_owner(u->source, m);
505 u->source->description = pa_sprintf_malloc("Solaris PCM on '%s'", p);
506 } else
507 u->source = NULL;
508
509 if (mode != O_RDONLY) {
510 u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL);
511 assert(u->sink);
512 u->sink->get_latency = sink_get_latency_cb;
513 u->sink->get_hw_volume = sink_get_hw_volume_cb;
514 u->sink->set_hw_volume = sink_set_hw_volume_cb;
515 u->sink->get_hw_mute = sink_get_hw_mute_cb;
516 u->sink->set_hw_mute = sink_set_hw_mute_cb;
517 u->sink->userdata = u;
518 pa_sink_set_owner(u->sink, m);
519 u->sink->description = pa_sprintf_malloc("Solaris PCM on '%s'", p);
520 } else
521 u->sink = NULL;
522
523 assert(u->source || u->sink);
524
525 u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
526 assert(u->io);
527 pa_iochannel_set_callback(u->io, io_callback, u);
528 u->fd = fd;
529
530 u->memchunk.memblock = NULL;
531 u->memchunk.length = 0;
532 u->sample_size = pa_frame_size(&ss);
533 u->buffer_size = buffer_size;
534
535 u->silence.memblock = pa_memblock_new(u->silence.length = CHUNK_SIZE, u->core->memblock_stat);
536 assert(u->silence.memblock);
537 pa_silence_memblock(u->silence.memblock, &ss);
538 u->silence.index = 0;
539
540 u->written_bytes = 0;
541 u->read_bytes = 0;
542
543 u->module = m;
544 m->userdata = u;
545
546 u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
547 assert(u->sig);
548 ioctl(u->fd, I_SETSIG, S_MSG);
549
550 pa_modargs_free(ma);
551
552 /* Read mixer settings */
553 if (u->source)
554 sink_get_hw_volume_cb(u->source);
555 if (u->sink) {
556 sink_get_hw_volume_cb(u->sink);
557 sink_get_hw_mute_cb(u->sink);
558 }
559
560 return 0;
561
562 fail:
563 if (fd >= 0)
564 close(fd);
565
566 if (ma)
567 pa_modargs_free(ma);
568
569 return -1;
570 }
571
572 void pa__done(pa_core *c, pa_module*m) {
573 struct userdata *u;
574 assert(c && m);
575
576 if (!(u = m->userdata))
577 return;
578
579 ioctl(u->fd, I_SETSIG, 0);
580 pa_signal_free(u->sig);
581
582 if (u->memchunk.memblock)
583 pa_memblock_unref(u->memchunk.memblock);
584 if (u->silence.memblock)
585 pa_memblock_unref(u->silence.memblock);
586
587 if (u->sink) {
588 pa_sink_disconnect(u->sink);
589 pa_sink_unref(u->sink);
590 }
591
592 if (u->source) {
593 pa_source_disconnect(u->source);
594 pa_source_unref(u->source);
595 }
596
597 pa_iochannel_free(u->io);
598 pa_xfree(u);
599 }