]> code.delx.au - pulseaudio/blob - src/modules/module-solaris.c
Merge dead branch 'lennart'
[pulseaudio] / src / modules / module-solaris.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006 Lennart Poettering
5 Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <stdio.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 <pulse/error.h>
44 #include <pulse/mainloop-signal.h>
45 #include <pulse/xmalloc.h>
46 #include <pulse/timeval.h>
47
48 #include <pulsecore/iochannel.h>
49 #include <pulsecore/sink.h>
50 #include <pulsecore/source.h>
51 #include <pulsecore/module.h>
52 #include <pulsecore/sample-util.h>
53 #include <pulsecore/core-util.h>
54 #include <pulsecore/modargs.h>
55 #include <pulsecore/log.h>
56 #include <pulsecore/core-error.h>
57 #include <pulsecore/thread-mq.h>
58 #include <pulsecore/rtpoll.h>
59 #include <pulsecore/thread.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_core *core;
79 pa_sink *sink;
80 pa_source *source;
81
82 pa_thread *thread;
83 pa_thread_mq thread_mq;
84 pa_rtpoll *rtpoll;
85
86 pa_signal_event *sig;
87
88 pa_memchunk memchunk;
89
90 unsigned int page_size;
91
92 uint32_t frame_size;
93 uint32_t buffer_size;
94 unsigned int written_bytes, read_bytes;
95
96 int fd;
97 pa_rtpoll_item *rtpoll_item;
98 pa_module *module;
99 };
100
101 static const char* const valid_modargs[] = {
102 "sink_name",
103 "source_name",
104 "device",
105 "record",
106 "playback",
107 "buffer_size",
108 "format",
109 "rate",
110 "channels",
111 "channel_map",
112 NULL
113 };
114
115 #define DEFAULT_SINK_NAME "solaris_output"
116 #define DEFAULT_SOURCE_NAME "solaris_input"
117 #define DEFAULT_DEVICE "/dev/audio"
118
119 static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
120 struct userdata *u = PA_SINK(o)->userdata;
121 int err;
122 audio_info_t info;
123
124 switch (code) {
125 case PA_SINK_MESSAGE_GET_LATENCY: {
126 pa_usec_t r = 0;
127
128 if (u->fd >= 0) {
129
130 err = ioctl(u->fd, AUDIO_GETINFO, &info);
131 pa_assert(err >= 0);
132
133 r += pa_bytes_to_usec(u->written_bytes, &PA_SINK(o)->sample_spec);
134 r -= pa_bytes_to_usec(info.play.samples * u->frame_size, &PA_SINK(o)->sample_spec);
135
136 if (u->memchunk.memblock)
137 r += pa_bytes_to_usec(u->memchunk.length, &PA_SINK(o)->sample_spec);
138 }
139
140 *((pa_usec_t*) data) = r;
141
142 return 0;
143 }
144
145 case PA_SINK_MESSAGE_SET_VOLUME:
146 if (u->fd >= 0) {
147 AUDIO_INITINFO(&info);
148
149 info.play.gain = pa_cvolume_avg((pa_cvolume*)data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
150 assert(info.play.gain <= AUDIO_MAX_GAIN);
151
152 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
153 if (errno == EINVAL)
154 pa_log("AUDIO_SETINFO: Unsupported volume.");
155 else
156 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
157 } else {
158 return 0;
159 }
160 }
161 break;
162
163 case PA_SINK_MESSAGE_GET_VOLUME:
164 if (u->fd >= 0) {
165 err = ioctl(u->fd, AUDIO_GETINFO, &info);
166 assert(err >= 0);
167
168 pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels,
169 info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
170
171 return 0;
172 }
173 break;
174
175 case PA_SINK_MESSAGE_SET_MUTE:
176 if (u->fd >= 0) {
177 AUDIO_INITINFO(&info);
178
179 info.output_muted = !!PA_PTR_TO_UINT(data);
180
181 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
182 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
183 else
184 return 0;
185 }
186 break;
187
188 case PA_SINK_MESSAGE_GET_MUTE:
189 if (u->fd >= 0) {
190 err = ioctl(u->fd, AUDIO_GETINFO, &info);
191 pa_assert(err >= 0);
192
193 *(int*)data = !!info.output_muted;
194
195 return 0;
196 }
197 break;
198 }
199
200 return pa_sink_process_msg(o, code, data, offset, chunk);
201 }
202
203 static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
204 struct userdata *u = PA_SOURCE(o)->userdata;
205 int err;
206 audio_info_t info;
207
208 switch (code) {
209 case PA_SOURCE_MESSAGE_GET_LATENCY: {
210 pa_usec_t r = 0;
211
212 if (u->fd) {
213 err = ioctl(u->fd, AUDIO_GETINFO, &info);
214 pa_assert(err >= 0);
215
216 r += pa_bytes_to_usec(info.record.samples * u->frame_size, &PA_SOURCE(o)->sample_spec);
217 r -= pa_bytes_to_usec(u->read_bytes, &PA_SOURCE(o)->sample_spec);
218 }
219
220 *((pa_usec_t*) data) = r;
221
222 return 0;
223 }
224
225 case PA_SOURCE_MESSAGE_SET_VOLUME:
226 if (u->fd >= 0) {
227 AUDIO_INITINFO(&info);
228
229 info.record.gain = pa_cvolume_avg((pa_cvolume*) data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
230 assert(info.record.gain <= AUDIO_MAX_GAIN);
231
232 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
233 if (errno == EINVAL)
234 pa_log("AUDIO_SETINFO: Unsupported volume.");
235 else
236 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
237 } else {
238 return 0;
239 }
240 }
241 break;
242
243 case PA_SOURCE_MESSAGE_GET_VOLUME:
244 if (u->fd >= 0) {
245 err = ioctl(u->fd, AUDIO_GETINFO, &info);
246 pa_assert(err >= 0);
247
248 pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels,
249 info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
250
251 return 0;
252 }
253 break;
254 }
255
256 return pa_source_process_msg(o, code, data, offset, chunk);
257 }
258
259 static void clear_underflow(struct userdata *u)
260 {
261 audio_info_t info;
262
263 AUDIO_INITINFO(&info);
264
265 info.play.error = 0;
266
267 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
268 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
269 }
270
271 static void clear_overflow(struct userdata *u)
272 {
273 audio_info_t info;
274
275 AUDIO_INITINFO(&info);
276
277 info.record.error = 0;
278
279 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
280 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
281 }
282
283 static void thread_func(void *userdata) {
284 struct userdata *u = userdata;
285 unsigned short revents = 0;
286 int ret;
287
288 pa_assert(u);
289
290 pa_log_debug("Thread starting up");
291
292 if (u->core->high_priority)
293 pa_make_realtime();
294
295 pa_thread_mq_install(&u->thread_mq);
296 pa_rtpoll_install(u->rtpoll);
297
298 for (;;) {
299 /* Render some data and write it to the dsp */
300
301 if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) {
302 audio_info_t info;
303 int err;
304 size_t len;
305
306 err = ioctl(u->fd, AUDIO_GETINFO, &info);
307 pa_assert(err >= 0);
308
309 /*
310 * Since we cannot modify the size of the output buffer we fake it
311 * by not filling it more than u->buffer_size.
312 */
313 len = u->buffer_size;
314 len -= u->written_bytes - (info.play.samples * u->frame_size);
315
316 /* The sample counter can sometimes go backwards :( */
317 if (len > u->buffer_size)
318 len = 0;
319
320 if (info.play.error) {
321 pa_log_debug("Solaris buffer underflow!");
322 clear_underflow(u);
323 }
324
325 len -= len % u->frame_size;
326
327 while (len) {
328 void *p;
329 ssize_t r;
330
331 if (!u->memchunk.length)
332 pa_sink_render(u->sink, len, &u->memchunk);
333
334 pa_assert(u->memchunk.length);
335
336 p = pa_memblock_acquire(u->memchunk.memblock);
337 r = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, NULL);
338 pa_memblock_release(u->memchunk.memblock);
339
340 if (r < 0) {
341 if (errno == EINTR)
342 continue;
343 else if (errno != EAGAIN) {
344 pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
345 goto fail;
346 }
347 } else {
348 pa_assert(r % u->frame_size == 0);
349
350 u->memchunk.index += r;
351 u->memchunk.length -= r;
352
353 if (u->memchunk.length <= 0) {
354 pa_memblock_unref(u->memchunk.memblock);
355 pa_memchunk_reset(&u->memchunk);
356 }
357
358 len -= r;
359 u->written_bytes += r;
360 }
361 }
362 }
363
364 /* Try to read some data and pass it on to the source driver */
365
366 if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN))) {
367 pa_memchunk memchunk;
368 int err;
369 size_t l;
370 void *p;
371 ssize_t r;
372 audio_info_t info;
373
374 err = ioctl(u->fd, AUDIO_GETINFO, &info);
375 pa_assert(err >= 0);
376
377 if (info.record.error) {
378 pa_log_debug("Solaris buffer overflow!");
379 clear_overflow(u);
380 }
381
382 err = ioctl(u->fd, I_NREAD, &l);
383 pa_assert(err >= 0);
384
385 if (l > 0) {
386 /* This is to make sure it fits in the memory pool. Also, a page
387 should be the most efficient transfer size. */
388 if (l > u->page_size)
389 l = u->page_size;
390
391 memchunk.memblock = pa_memblock_new(u->core->mempool, l);
392 pa_assert(memchunk.memblock);
393
394 p = pa_memblock_acquire(memchunk.memblock);
395 r = pa_read(u->fd, p, l, NULL);
396 pa_memblock_release(memchunk.memblock);
397
398 if (r < 0) {
399 pa_memblock_unref(memchunk.memblock);
400 if (errno != EAGAIN) {
401 pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
402 goto fail;
403 }
404 } else {
405 memchunk.index = 0;
406 memchunk.length = r;
407
408 pa_source_post(u->source, &memchunk);
409 pa_memblock_unref(memchunk.memblock);
410
411 u->read_bytes += r;
412
413 revents &= ~POLLIN;
414 }
415 }
416 }
417
418 if (u->fd >= 0) {
419 struct pollfd *pollfd;
420
421 pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
422 pollfd->events =
423 ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0);
424 }
425
426 /* Hmm, nothing to do. Let's sleep */
427 if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
428 goto fail;
429
430 if (ret == 0)
431 goto finish;
432
433 if (u->fd >= 0) {
434 struct pollfd *pollfd;
435
436 pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
437
438 if (pollfd->revents & ~(POLLOUT|POLLIN)) {
439 pa_log("DSP shutdown.");
440 goto fail;
441 }
442
443 revents = pollfd->revents;
444 } else
445 revents = 0;
446 }
447
448 fail:
449 /* We have to continue processing messages until we receive the
450 * SHUTDOWN message */
451 pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
452 pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
453
454 finish:
455 pa_log_debug("Thread shutting down");
456 }
457
458 static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
459 struct userdata *u = userdata;
460
461 assert(u);
462
463 if (u->sink) {
464 pa_sink_get_volume(u->sink);
465 pa_sink_get_mute(u->sink);
466 }
467
468 if (u->source)
469 pa_source_get_volume(u->source);
470 }
471
472 static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
473 audio_info_t info;
474
475 AUDIO_INITINFO(&info);
476
477 if (mode != O_RDONLY) {
478 info.play.sample_rate = ss->rate;
479 info.play.channels = ss->channels;
480 switch (ss->format) {
481 case PA_SAMPLE_U8:
482 info.play.precision = 8;
483 info.play.encoding = AUDIO_ENCODING_LINEAR;
484 break;
485 case PA_SAMPLE_ALAW:
486 info.play.precision = 8;
487 info.play.encoding = AUDIO_ENCODING_ALAW;
488 break;
489 case PA_SAMPLE_ULAW:
490 info.play.precision = 8;
491 info.play.encoding = AUDIO_ENCODING_ULAW;
492 break;
493 case PA_SAMPLE_S16NE:
494 info.play.precision = 16;
495 info.play.encoding = AUDIO_ENCODING_LINEAR;
496 break;
497 default:
498 return -1;
499 }
500 }
501
502 if (mode != O_WRONLY) {
503 info.record.sample_rate = ss->rate;
504 info.record.channels = ss->channels;
505 switch (ss->format) {
506 case PA_SAMPLE_U8:
507 info.record.precision = 8;
508 info.record.encoding = AUDIO_ENCODING_LINEAR;
509 break;
510 case PA_SAMPLE_ALAW:
511 info.record.precision = 8;
512 info.record.encoding = AUDIO_ENCODING_ALAW;
513 break;
514 case PA_SAMPLE_ULAW:
515 info.record.precision = 8;
516 info.record.encoding = AUDIO_ENCODING_ULAW;
517 break;
518 case PA_SAMPLE_S16NE:
519 info.record.precision = 16;
520 info.record.encoding = AUDIO_ENCODING_LINEAR;
521 break;
522 default:
523 return -1;
524 }
525 }
526
527 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
528 if (errno == EINVAL)
529 pa_log("AUDIO_SETINFO: Unsupported sample format.");
530 else
531 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
532 return -1;
533 }
534
535 return 0;
536 }
537
538 static int pa_solaris_set_buffer(int fd, int buffer_size) {
539 audio_info_t info;
540
541 AUDIO_INITINFO(&info);
542
543 info.play.buffer_size = buffer_size;
544 info.record.buffer_size = buffer_size;
545
546 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
547 if (errno == EINVAL)
548 pa_log("AUDIO_SETINFO: Unsupported buffer size.");
549 else
550 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
551 return -1;
552 }
553
554 return 0;
555 }
556
557 int pa__init(pa_module *m) {
558 struct userdata *u = NULL;
559 const char *p;
560 int fd = -1;
561 int buffer_size;
562 int mode;
563 int record = 1, playback = 1;
564 pa_sample_spec ss;
565 pa_channel_map map;
566 pa_modargs *ma = NULL;
567 char *t;
568 struct pollfd *pollfd;
569
570 pa_assert(m);
571
572 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
573 pa_log("failed to parse module arguments.");
574 goto fail;
575 }
576
577 if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
578 pa_log("record= and playback= expect numeric argument.");
579 goto fail;
580 }
581
582 if (!playback && !record) {
583 pa_log("neither playback nor record enabled for device.");
584 goto fail;
585 }
586
587 mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
588
589 buffer_size = 16384;
590 if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) {
591 pa_log("failed to parse buffer size argument");
592 goto fail;
593 }
594
595 ss = m->core->default_sample_spec;
596 if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
597 pa_log("failed to parse sample specification");
598 goto fail;
599 }
600
601 if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
602 goto fail;
603
604 pa_log_info("device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
605
606 if (pa_solaris_auto_format(fd, mode, &ss) < 0)
607 goto fail;
608
609 if (pa_solaris_set_buffer(fd, buffer_size) < 0)
610 goto fail;
611
612 u = pa_xmalloc(sizeof(struct userdata));
613 u->core = m->core;
614
615 u->fd = fd;
616
617 pa_memchunk_reset(&u->memchunk);
618
619 /* We use this to get a reasonable chunk size */
620 u->page_size = PA_PAGE_SIZE;
621
622 u->frame_size = pa_frame_size(&ss);
623 u->buffer_size = buffer_size;
624
625 u->written_bytes = 0;
626 u->read_bytes = 0;
627
628 u->module = m;
629 m->userdata = u;
630
631 pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
632
633 u->rtpoll = pa_rtpoll_new();
634 pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
635
636 pa_rtpoll_set_timer_periodic(u->rtpoll, pa_bytes_to_usec(u->buffer_size / 10, &ss));
637
638 u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
639 pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
640 pollfd->fd = fd;
641 pollfd->events = 0;
642 pollfd->revents = 0;
643
644 if (mode != O_WRONLY) {
645 u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map);
646 pa_assert(u->source);
647
648 u->source->userdata = u;
649 u->source->parent.process_msg = source_process_msg;
650
651 pa_source_set_module(u->source, m);
652 pa_source_set_description(u->source, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
653 pa_xfree(t);
654 pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
655 pa_source_set_rtpoll(u->source, u->rtpoll);
656
657 u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL;
658 u->source->refresh_volume = 1;
659 } else
660 u->source = NULL;
661
662 if (mode != O_RDONLY) {
663 u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map);
664 pa_assert(u->sink);
665
666 u->sink->userdata = u;
667 u->sink->parent.process_msg = sink_process_msg;
668
669 pa_sink_set_module(u->sink, m);
670 pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
671 pa_xfree(t);
672 pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
673 pa_sink_set_rtpoll(u->sink, u->rtpoll);
674
675 u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL;
676 u->sink->refresh_volume = 1;
677 u->sink->refresh_mute = 1;
678 } else
679 u->sink = NULL;
680
681 pa_assert(u->source || u->sink);
682
683 u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
684 pa_assert(u->sig);
685 ioctl(u->fd, I_SETSIG, S_MSG);
686
687 if (!(u->thread = pa_thread_new(thread_func, u))) {
688 pa_log("Failed to create thread.");
689 goto fail;
690 }
691
692 /* Read mixer settings */
693 if (u->source)
694 pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_GET_VOLUME, &u->source->volume, 0, NULL);
695 if (u->sink) {
696 pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_VOLUME, &u->sink->volume, 0, NULL);
697 pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_MUTE, &u->sink->muted, 0, NULL);
698 }
699
700 if (u->sink)
701 pa_sink_put(u->sink);
702 if (u->source)
703 pa_source_put(u->source);
704
705 pa_modargs_free(ma);
706
707 return 0;
708
709 fail:
710 if (u)
711 pa__done(m);
712 else if (fd >= 0)
713 close(fd);
714
715 if (ma)
716 pa_modargs_free(ma);
717
718 return -1;
719 }
720
721 void pa__done(pa_module *m) {
722 struct userdata *u;
723
724 pa_assert(m);
725
726 if (!(u = m->userdata))
727 return;
728
729 ioctl(u->fd, I_SETSIG, 0);
730 pa_signal_free(u->sig);
731
732 if (u->sink)
733 pa_sink_unlink(u->sink);
734
735 if (u->source)
736 pa_source_unlink(u->source);
737
738 if (u->thread) {
739 pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
740 pa_thread_free(u->thread);
741 }
742
743 pa_thread_mq_done(&u->thread_mq);
744
745 if (u->sink)
746 pa_sink_unref(u->sink);
747
748 if (u->source)
749 pa_source_unref(u->source);
750
751 if (u->memchunk.memblock)
752 pa_memblock_unref(u->memchunk.memblock);
753
754 if (u->rtpoll_item)
755 pa_rtpoll_item_free(u->rtpoll_item);
756
757 if (u->rtpoll)
758 pa_rtpoll_free(u->rtpoll);
759
760 if (u->fd >= 0)
761 close(u->fd);
762
763 pa_xfree(u);
764 }