]> code.delx.au - pulseaudio/blob - polyp/module-solaris.c
f85e71dfb20b9a5a9362074f155ad2e351875855
[pulseaudio] / polyp / 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 "iochannel.h"
44 #include "sink.h"
45 #include "source.h"
46 #include "module.h"
47 #include "sample-util.h"
48 #include "util.h"
49 #include "modargs.h"
50 #include "xmalloc.h"
51 #include "log.h"
52 #include "mainloop-signal.h"
53 #include "module-solaris-symdef.h"
54
55 PA_MODULE_AUTHOR("Pierre Ossman")
56 PA_MODULE_DESCRIPTION("Solaris Sink/Source")
57 PA_MODULE_VERSION(PACKAGE_VERSION)
58 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>")
59
60 struct userdata {
61 pa_sink *sink;
62 pa_source *source;
63 pa_iochannel *io;
64 pa_core *core;
65 pa_signal_event *sig;
66
67 pa_memchunk memchunk, silence;
68
69 uint32_t sample_size;
70 uint32_t buffer_size;
71 unsigned int written_bytes, read_bytes;
72
73 int fd;
74 pa_module *module;
75 };
76
77 static const char* const valid_modargs[] = {
78 "sink_name",
79 "source_name",
80 "device",
81 "record",
82 "playback",
83 "buffer_size",
84 "format",
85 "rate",
86 "channels",
87 NULL
88 };
89
90 #define DEFAULT_SINK_NAME "solaris_output"
91 #define DEFAULT_SOURCE_NAME "solaris_input"
92 #define DEFAULT_DEVICE "/dev/audio"
93
94 #define CHUNK_SIZE 2048
95
96 static void update_usage(struct userdata *u) {
97 pa_module_set_used(u->module,
98 (u->sink ? pa_idxset_size(u->sink->inputs) : 0) +
99 (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) +
100 (u->source ? pa_idxset_size(u->source->outputs) : 0));
101 }
102
103 static void do_write(struct userdata *u) {
104 audio_info_t info;
105 int err;
106 pa_memchunk *memchunk;
107 size_t len;
108 ssize_t r;
109
110 assert(u);
111
112 if (!u->sink || !pa_iochannel_is_writable(u->io))
113 return;
114
115 update_usage(u);
116
117 err = ioctl(u->fd, AUDIO_GETINFO, &info);
118 assert(err >= 0);
119
120 /*
121 * Since we cannot modify the size of the output buffer we fake it
122 * by not filling it more than u->buffer_size.
123 */
124 len = u->buffer_size;
125 len -= u->written_bytes - (info.play.samples * u->sample_size);
126
127 /*
128 * Do not fill more than half the buffer in one chunk since we only
129 * get notifications upon completion of entire chunks.
130 */
131 if (len > (u->buffer_size / 2))
132 len = u->buffer_size / 2;
133
134 if (len < u->sample_size)
135 return;
136
137 memchunk = &u->memchunk;
138
139 if (!memchunk->length)
140 if (pa_sink_render(u->sink, len, memchunk) < 0)
141 memchunk = &u->silence;
142
143 assert(memchunk->memblock);
144 assert(memchunk->memblock->data);
145 assert(memchunk->length);
146
147 if (memchunk->length < len)
148 len = memchunk->length;
149
150 if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, len)) < 0) {
151 pa_log(__FILE__": write() failed: %s\n", strerror(errno));
152 return;
153 }
154
155 if (memchunk == &u->silence)
156 assert(r % u->sample_size == 0);
157 else {
158 u->memchunk.index += r;
159 u->memchunk.length -= r;
160
161 if (u->memchunk.length <= 0) {
162 pa_memblock_unref(u->memchunk.memblock);
163 u->memchunk.memblock = NULL;
164 }
165 }
166
167 u->written_bytes += r;
168
169 /*
170 * Write 0 bytes which will generate a SIGPOLL when "played".
171 */
172 if (write(u->fd, NULL, 0) < 0) {
173 pa_log(__FILE__": write() failed: %s\n", strerror(errno));
174 return;
175 }
176 }
177
178 static void do_read(struct userdata *u) {
179 pa_memchunk memchunk;
180 int err, l;
181 ssize_t r;
182 assert(u);
183
184 if (!u->source || !pa_iochannel_is_readable(u->io))
185 return;
186
187 update_usage(u);
188
189 err = ioctl(u->fd, I_NREAD, &l);
190 assert(err >= 0);
191
192 memchunk.memblock = pa_memblock_new(l, u->core->memblock_stat);
193 assert(memchunk.memblock);
194 if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) {
195 pa_memblock_unref(memchunk.memblock);
196 if (errno != EAGAIN)
197 pa_log(__FILE__": read() failed: %s\n", strerror(errno));
198 return;
199 }
200
201 assert(r <= (ssize_t) memchunk.memblock->length);
202 memchunk.length = memchunk.memblock->length = r;
203 memchunk.index = 0;
204
205 pa_source_post(u->source, &memchunk);
206 pa_memblock_unref(memchunk.memblock);
207
208 u->read_bytes += r;
209 }
210
211 static void io_callback(pa_iochannel *io, void*userdata) {
212 struct userdata *u = userdata;
213 assert(u);
214 do_write(u);
215 do_read(u);
216 }
217
218 void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
219 struct userdata *u = userdata;
220 assert(u);
221 do_write(u);
222 }
223
224 static pa_usec_t sink_get_latency_cb(pa_sink *s) {
225 pa_usec_t r = 0;
226 audio_info_t info;
227 int err;
228 struct userdata *u = s->userdata;
229 assert(s && u && u->sink);
230
231 err = ioctl(u->fd, AUDIO_GETINFO, &info);
232 assert(err >= 0);
233
234 r += pa_bytes_to_usec(u->written_bytes, &s->sample_spec);
235 r -= pa_bytes_to_usec(info.play.samples * u->sample_size, &s->sample_spec);
236
237 if (u->memchunk.memblock)
238 r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
239
240 return r;
241 }
242
243 static pa_usec_t source_get_latency_cb(pa_source *s) {
244 pa_usec_t r = 0;
245 struct userdata *u = s->userdata;
246 audio_info_t info;
247 int err;
248 assert(s && u && u->source);
249
250 err = ioctl(u->fd, AUDIO_GETINFO, &info);
251 assert(err >= 0);
252
253 r += pa_bytes_to_usec(info.record.samples * u->sample_size, &s->sample_spec);
254 r -= pa_bytes_to_usec(u->read_bytes, &s->sample_spec);
255
256 return r;
257 }
258
259 static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
260 audio_info_t info;
261
262 AUDIO_INITINFO(&info);
263
264 if (mode != O_RDONLY) {
265 info.play.sample_rate = ss->rate;
266 info.play.channels = ss->channels;
267 switch (ss->format) {
268 case PA_SAMPLE_U8:
269 info.play.precision = 8;
270 info.play.encoding = AUDIO_ENCODING_LINEAR;
271 break;
272 case PA_SAMPLE_ALAW:
273 info.play.precision = 8;
274 info.play.encoding = AUDIO_ENCODING_ALAW;
275 break;
276 case PA_SAMPLE_ULAW:
277 info.play.precision = 8;
278 info.play.encoding = AUDIO_ENCODING_ULAW;
279 break;
280 case PA_SAMPLE_S16NE:
281 info.play.precision = 16;
282 info.play.encoding = AUDIO_ENCODING_LINEAR;
283 break;
284 default:
285 return -1;
286 }
287 }
288
289 if (mode != O_WRONLY) {
290 info.record.sample_rate = ss->rate;
291 info.record.channels = ss->channels;
292 switch (ss->format) {
293 case PA_SAMPLE_U8:
294 info.record.precision = 8;
295 info.record.encoding = AUDIO_ENCODING_LINEAR;
296 break;
297 case PA_SAMPLE_ALAW:
298 info.record.precision = 8;
299 info.record.encoding = AUDIO_ENCODING_ALAW;
300 break;
301 case PA_SAMPLE_ULAW:
302 info.record.precision = 8;
303 info.record.encoding = AUDIO_ENCODING_ULAW;
304 break;
305 case PA_SAMPLE_S16NE:
306 info.record.precision = 16;
307 info.record.encoding = AUDIO_ENCODING_LINEAR;
308 break;
309 default:
310 return -1;
311 }
312 }
313
314 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
315 if (errno == EINVAL)
316 pa_log(__FILE__": AUDIO_SETINFO: Unsupported sample format.\n");
317 else
318 pa_log(__FILE__": AUDIO_SETINFO: %s\n", strerror(errno));
319 return -1;
320 }
321
322 return 0;
323 }
324
325 static int pa_solaris_set_buffer(int fd, int buffer_size) {
326 audio_info_t info;
327
328 AUDIO_INITINFO(&info);
329
330 info.record.buffer_size = buffer_size;
331
332 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
333 if (errno == EINVAL)
334 pa_log(__FILE__": AUDIO_SETINFO: Unsupported buffer size.\n");
335 else
336 pa_log(__FILE__": AUDIO_SETINFO: %s\n", strerror(errno));
337 return -1;
338 }
339
340 return 0;
341 }
342
343 int pa__init(pa_core *c, pa_module*m) {
344 struct userdata *u = NULL;
345 const char *p;
346 int fd = -1;
347 int buffer_size;
348 int mode;
349 int record = 1, playback = 1;
350 pa_sample_spec ss;
351 pa_modargs *ma = NULL;
352 assert(c && m);
353
354 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
355 pa_log(__FILE__": failed to parse module arguments.\n");
356 goto fail;
357 }
358
359 if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
360 pa_log(__FILE__": record= and playback= expect numeric argument.\n");
361 goto fail;
362 }
363
364 if (!playback && !record) {
365 pa_log(__FILE__": neither playback nor record enabled for device.\n");
366 goto fail;
367 }
368
369 mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
370
371 buffer_size = 16384;
372 if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) {
373 pa_log(__FILE__": failed to parse buffer size argument\n");
374 goto fail;
375 }
376
377 ss = c->default_sample_spec;
378 if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
379 pa_log(__FILE__": failed to parse sample specification\n");
380 goto fail;
381 }
382
383 if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
384 goto fail;
385
386 pa_log_info(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
387
388 if (pa_solaris_auto_format(fd, mode, &ss) < 0)
389 goto fail;
390
391 if ((mode != O_WRONLY) && (buffer_size >= 1))
392 if (pa_solaris_set_buffer(fd, buffer_size) < 0)
393 goto fail;
394
395 u = pa_xmalloc(sizeof(struct userdata));
396 u->core = c;
397
398 if (mode != O_WRONLY) {
399 u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL);
400 assert(u->source);
401 u->source->userdata = u;
402 u->source->get_latency = source_get_latency_cb;
403 pa_source_set_owner(u->source, m);
404 u->source->description = pa_sprintf_malloc("Solaris PCM on '%s'", p);
405 } else
406 u->source = NULL;
407
408 if (mode != O_RDONLY) {
409 u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL);
410 assert(u->sink);
411 u->sink->get_latency = sink_get_latency_cb;
412 u->sink->userdata = u;
413 pa_sink_set_owner(u->sink, m);
414 u->sink->description = pa_sprintf_malloc("Solaris PCM on '%s'", p);
415 } else
416 u->sink = NULL;
417
418 assert(u->source || u->sink);
419
420 u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
421 assert(u->io);
422 pa_iochannel_set_callback(u->io, io_callback, u);
423 u->fd = fd;
424
425 u->memchunk.memblock = NULL;
426 u->memchunk.length = 0;
427 u->sample_size = pa_frame_size(&ss);
428 u->buffer_size = buffer_size;
429
430 u->silence.memblock = pa_memblock_new(u->silence.length = CHUNK_SIZE, u->core->memblock_stat);
431 assert(u->silence.memblock);
432 pa_silence_memblock(u->silence.memblock, &ss);
433 u->silence.index = 0;
434
435 u->written_bytes = 0;
436 u->read_bytes = 0;
437
438 u->module = m;
439 m->userdata = u;
440
441 u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
442 assert(u->sig);
443 ioctl(u->fd, I_SETSIG, S_MSG);
444
445 pa_modargs_free(ma);
446
447 return 0;
448
449 fail:
450 if (fd >= 0)
451 close(fd);
452
453 if (ma)
454 pa_modargs_free(ma);
455
456 return -1;
457 }
458
459 void pa__done(pa_core *c, pa_module*m) {
460 struct userdata *u;
461 assert(c && m);
462
463 if (!(u = m->userdata))
464 return;
465
466 ioctl(u->fd, I_SETSIG, 0);
467 pa_signal_free(u->sig);
468
469 if (u->memchunk.memblock)
470 pa_memblock_unref(u->memchunk.memblock);
471 if (u->silence.memblock)
472 pa_memblock_unref(u->silence.memblock);
473
474 if (u->sink) {
475 pa_sink_disconnect(u->sink);
476 pa_sink_unref(u->sink);
477 }
478
479 if (u->source) {
480 pa_source_disconnect(u->source);
481 pa_source_unref(u->source);
482 }
483
484 pa_iochannel_free(u->io);
485 pa_xfree(u);
486 }