]> code.delx.au - pulseaudio/blob - src/modules/module-solaris.c
Reorganised the source tree. We now have src/ with a couple of subdirs:
[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\n", 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\n", 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\n", 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 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 pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
262 audio_info_t info;
263
264 AUDIO_INITINFO(&info);
265
266 if (mode != O_RDONLY) {
267 info.play.sample_rate = ss->rate;
268 info.play.channels = ss->channels;
269 switch (ss->format) {
270 case PA_SAMPLE_U8:
271 info.play.precision = 8;
272 info.play.encoding = AUDIO_ENCODING_LINEAR;
273 break;
274 case PA_SAMPLE_ALAW:
275 info.play.precision = 8;
276 info.play.encoding = AUDIO_ENCODING_ALAW;
277 break;
278 case PA_SAMPLE_ULAW:
279 info.play.precision = 8;
280 info.play.encoding = AUDIO_ENCODING_ULAW;
281 break;
282 case PA_SAMPLE_S16NE:
283 info.play.precision = 16;
284 info.play.encoding = AUDIO_ENCODING_LINEAR;
285 break;
286 default:
287 return -1;
288 }
289 }
290
291 if (mode != O_WRONLY) {
292 info.record.sample_rate = ss->rate;
293 info.record.channels = ss->channels;
294 switch (ss->format) {
295 case PA_SAMPLE_U8:
296 info.record.precision = 8;
297 info.record.encoding = AUDIO_ENCODING_LINEAR;
298 break;
299 case PA_SAMPLE_ALAW:
300 info.record.precision = 8;
301 info.record.encoding = AUDIO_ENCODING_ALAW;
302 break;
303 case PA_SAMPLE_ULAW:
304 info.record.precision = 8;
305 info.record.encoding = AUDIO_ENCODING_ULAW;
306 break;
307 case PA_SAMPLE_S16NE:
308 info.record.precision = 16;
309 info.record.encoding = AUDIO_ENCODING_LINEAR;
310 break;
311 default:
312 return -1;
313 }
314 }
315
316 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
317 if (errno == EINVAL)
318 pa_log(__FILE__": AUDIO_SETINFO: Unsupported sample format.\n");
319 else
320 pa_log(__FILE__": AUDIO_SETINFO: %s\n", strerror(errno));
321 return -1;
322 }
323
324 return 0;
325 }
326
327 static int pa_solaris_set_buffer(int fd, int buffer_size) {
328 audio_info_t info;
329
330 AUDIO_INITINFO(&info);
331
332 info.record.buffer_size = buffer_size;
333
334 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
335 if (errno == EINVAL)
336 pa_log(__FILE__": AUDIO_SETINFO: Unsupported buffer size.\n");
337 else
338 pa_log(__FILE__": AUDIO_SETINFO: %s\n", strerror(errno));
339 return -1;
340 }
341
342 return 0;
343 }
344
345 int pa__init(pa_core *c, pa_module*m) {
346 struct userdata *u = NULL;
347 const char *p;
348 int fd = -1;
349 int buffer_size;
350 int mode;
351 int record = 1, playback = 1;
352 pa_sample_spec ss;
353 pa_modargs *ma = NULL;
354 assert(c && m);
355
356 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
357 pa_log(__FILE__": failed to parse module arguments.\n");
358 goto fail;
359 }
360
361 if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
362 pa_log(__FILE__": record= and playback= expect numeric argument.\n");
363 goto fail;
364 }
365
366 if (!playback && !record) {
367 pa_log(__FILE__": neither playback nor record enabled for device.\n");
368 goto fail;
369 }
370
371 mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
372
373 buffer_size = 16384;
374 if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) {
375 pa_log(__FILE__": failed to parse buffer size argument\n");
376 goto fail;
377 }
378
379 ss = c->default_sample_spec;
380 if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
381 pa_log(__FILE__": failed to parse sample specification\n");
382 goto fail;
383 }
384
385 if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
386 goto fail;
387
388 pa_log_info(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
389
390 if (pa_solaris_auto_format(fd, mode, &ss) < 0)
391 goto fail;
392
393 if ((mode != O_WRONLY) && (buffer_size >= 1))
394 if (pa_solaris_set_buffer(fd, buffer_size) < 0)
395 goto fail;
396
397 u = pa_xmalloc(sizeof(struct userdata));
398 u->core = c;
399
400 if (mode != O_WRONLY) {
401 u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL);
402 assert(u->source);
403 u->source->userdata = u;
404 u->source->get_latency = source_get_latency_cb;
405 pa_source_set_owner(u->source, m);
406 u->source->description = pa_sprintf_malloc("Solaris PCM on '%s'", p);
407 } else
408 u->source = NULL;
409
410 if (mode != O_RDONLY) {
411 u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL);
412 assert(u->sink);
413 u->sink->get_latency = sink_get_latency_cb;
414 u->sink->userdata = u;
415 pa_sink_set_owner(u->sink, m);
416 u->sink->description = pa_sprintf_malloc("Solaris PCM on '%s'", p);
417 } else
418 u->sink = NULL;
419
420 assert(u->source || u->sink);
421
422 u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
423 assert(u->io);
424 pa_iochannel_set_callback(u->io, io_callback, u);
425 u->fd = fd;
426
427 u->memchunk.memblock = NULL;
428 u->memchunk.length = 0;
429 u->sample_size = pa_frame_size(&ss);
430 u->buffer_size = buffer_size;
431
432 u->silence.memblock = pa_memblock_new(u->silence.length = CHUNK_SIZE, u->core->memblock_stat);
433 assert(u->silence.memblock);
434 pa_silence_memblock(u->silence.memblock, &ss);
435 u->silence.index = 0;
436
437 u->written_bytes = 0;
438 u->read_bytes = 0;
439
440 u->module = m;
441 m->userdata = u;
442
443 u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
444 assert(u->sig);
445 ioctl(u->fd, I_SETSIG, S_MSG);
446
447 pa_modargs_free(ma);
448
449 return 0;
450
451 fail:
452 if (fd >= 0)
453 close(fd);
454
455 if (ma)
456 pa_modargs_free(ma);
457
458 return -1;
459 }
460
461 void pa__done(pa_core *c, pa_module*m) {
462 struct userdata *u;
463 assert(c && m);
464
465 if (!(u = m->userdata))
466 return;
467
468 ioctl(u->fd, I_SETSIG, 0);
469 pa_signal_free(u->sig);
470
471 if (u->memchunk.memblock)
472 pa_memblock_unref(u->memchunk.memblock);
473 if (u->silence.memblock)
474 pa_memblock_unref(u->silence.memblock);
475
476 if (u->sink) {
477 pa_sink_disconnect(u->sink);
478 pa_sink_unref(u->sink);
479 }
480
481 if (u->source) {
482 pa_source_disconnect(u->source);
483 pa_source_unref(u->source);
484 }
485
486 pa_iochannel_free(u->io);
487 pa_xfree(u);
488 }