add new function pa_yes_no()
[pulseaudio.git] / src / modules / module-solaris.c
blob4a5c88e484f86823e5dea74e72c016541168fb0b
1 /* $Id$ */
3 /***
4 This file is part of PulseAudio.
6 Copyright 2006 Lennart Poettering
7 Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
9 PulseAudio is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as published
11 by the Free Software Foundation; either version 2 of the License,
12 or (at your option) any later version.
14 PulseAudio is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with PulseAudio; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 USA.
23 ***/
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <limits.h>
36 #include <sys/ioctl.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
40 #include <signal.h>
41 #include <stropts.h>
42 #include <sys/conf.h>
43 #include <sys/audio.h>
45 #include <pulse/error.h>
46 #include <pulse/mainloop-signal.h>
47 #include <pulse/xmalloc.h>
48 #include <pulse/timeval.h>
50 #include <pulsecore/iochannel.h>
51 #include <pulsecore/sink.h>
52 #include <pulsecore/source.h>
53 #include <pulsecore/module.h>
54 #include <pulsecore/sample-util.h>
55 #include <pulsecore/core-util.h>
56 #include <pulsecore/modargs.h>
57 #include <pulsecore/log.h>
58 #include <pulsecore/core-error.h>
59 #include <pulsecore/thread-mq.h>
60 #include <pulsecore/rtpoll.h>
61 #include <pulsecore/thread.h>
63 #include "module-solaris-symdef.h"
65 PA_MODULE_AUTHOR("Pierre Ossman")
66 PA_MODULE_DESCRIPTION("Solaris Sink/Source")
67 PA_MODULE_VERSION(PACKAGE_VERSION)
68 PA_MODULE_USAGE(
69 "sink_name=<name for the sink> "
70 "source_name=<name for the source> "
71 "device=<OSS device> record=<enable source?> "
72 "playback=<enable sink?> "
73 "format=<sample format> "
74 "channels=<number of channels> "
75 "rate=<sample rate> "
76 "buffer_size=<record buffer size> "
77 "channel_map=<channel map>")
79 struct userdata {
80 pa_core *core;
81 pa_sink *sink;
82 pa_source *source;
84 pa_thread *thread;
85 pa_thread_mq thread_mq;
86 pa_rtpoll *rtpoll;
88 pa_signal_event *sig;
90 pa_memchunk memchunk;
92 unsigned int page_size;
94 uint32_t frame_size;
95 uint32_t buffer_size;
96 unsigned int written_bytes, read_bytes;
98 int fd;
99 pa_rtpoll_item *rtpoll_item;
100 pa_module *module;
103 static const char* const valid_modargs[] = {
104 "sink_name",
105 "source_name",
106 "device",
107 "record",
108 "playback",
109 "buffer_size",
110 "format",
111 "rate",
112 "channels",
113 "channel_map",
114 NULL
117 #define DEFAULT_SINK_NAME "solaris_output"
118 #define DEFAULT_SOURCE_NAME "solaris_input"
119 #define DEFAULT_DEVICE "/dev/audio"
121 static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
122 struct userdata *u = PA_SINK(o)->userdata;
123 int err;
124 audio_info_t info;
126 switch (code) {
127 case PA_SINK_MESSAGE_GET_LATENCY: {
128 pa_usec_t r = 0;
130 if (u->fd >= 0) {
132 err = ioctl(u->fd, AUDIO_GETINFO, &info);
133 pa_assert(err >= 0);
135 r += pa_bytes_to_usec(u->written_bytes, &PA_SINK(o)->sample_spec);
136 r -= pa_bytes_to_usec(info.play.samples * u->frame_size, &PA_SINK(o)->sample_spec);
138 if (u->memchunk.memblock)
139 r += pa_bytes_to_usec(u->memchunk.length, &PA_SINK(o)->sample_spec);
142 *((pa_usec_t*) data) = r;
144 return 0;
147 case PA_SINK_MESSAGE_SET_VOLUME:
148 if (u->fd >= 0) {
149 AUDIO_INITINFO(&info);
151 info.play.gain = pa_cvolume_avg((pa_cvolume*)data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
152 assert(info.play.gain <= AUDIO_MAX_GAIN);
154 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
155 if (errno == EINVAL)
156 pa_log("AUDIO_SETINFO: Unsupported volume.");
157 else
158 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
159 } else {
160 return 0;
163 break;
165 case PA_SINK_MESSAGE_GET_VOLUME:
166 if (u->fd >= 0) {
167 err = ioctl(u->fd, AUDIO_GETINFO, &info);
168 assert(err >= 0);
170 pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels,
171 info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
173 return 0;
175 break;
177 case PA_SINK_MESSAGE_SET_MUTE:
178 if (u->fd >= 0) {
179 AUDIO_INITINFO(&info);
181 info.output_muted = !!PA_PTR_TO_UINT(data);
183 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
184 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
185 else
186 return 0;
188 break;
190 case PA_SINK_MESSAGE_GET_MUTE:
191 if (u->fd >= 0) {
192 err = ioctl(u->fd, AUDIO_GETINFO, &info);
193 pa_assert(err >= 0);
195 *(int*)data = !!info.output_muted;
197 return 0;
199 break;
202 return pa_sink_process_msg(o, code, data, offset, chunk);
205 static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
206 struct userdata *u = PA_SOURCE(o)->userdata;
207 int err;
208 audio_info_t info;
210 switch (code) {
211 case PA_SOURCE_MESSAGE_GET_LATENCY: {
212 pa_usec_t r = 0;
214 if (u->fd) {
215 err = ioctl(u->fd, AUDIO_GETINFO, &info);
216 pa_assert(err >= 0);
218 r += pa_bytes_to_usec(info.record.samples * u->frame_size, &PA_SOURCE(o)->sample_spec);
219 r -= pa_bytes_to_usec(u->read_bytes, &PA_SOURCE(o)->sample_spec);
222 *((pa_usec_t*) data) = r;
224 return 0;
227 case PA_SOURCE_MESSAGE_SET_VOLUME:
228 if (u->fd >= 0) {
229 AUDIO_INITINFO(&info);
231 info.record.gain = pa_cvolume_avg((pa_cvolume*) data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
232 assert(info.record.gain <= AUDIO_MAX_GAIN);
234 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
235 if (errno == EINVAL)
236 pa_log("AUDIO_SETINFO: Unsupported volume.");
237 else
238 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
239 } else {
240 return 0;
243 break;
245 case PA_SOURCE_MESSAGE_GET_VOLUME:
246 if (u->fd >= 0) {
247 err = ioctl(u->fd, AUDIO_GETINFO, &info);
248 pa_assert(err >= 0);
250 pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels,
251 info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
253 return 0;
255 break;
258 return pa_source_process_msg(o, code, data, offset, chunk);
261 static void clear_underflow(struct userdata *u)
263 audio_info_t info;
265 AUDIO_INITINFO(&info);
267 info.play.error = 0;
269 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
270 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
273 static void clear_overflow(struct userdata *u)
275 audio_info_t info;
277 AUDIO_INITINFO(&info);
279 info.record.error = 0;
281 if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
282 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
285 static void thread_func(void *userdata) {
286 struct userdata *u = userdata;
287 unsigned short revents = 0;
288 int ret;
290 pa_assert(u);
292 pa_log_debug("Thread starting up");
294 if (u->core->high_priority)
295 pa_make_realtime();
297 pa_thread_mq_install(&u->thread_mq);
298 pa_rtpoll_install(u->rtpoll);
300 for (;;) {
301 /* Render some data and write it to the dsp */
303 if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) {
304 audio_info_t info;
305 int err;
306 size_t len;
308 err = ioctl(u->fd, AUDIO_GETINFO, &info);
309 pa_assert(err >= 0);
312 * Since we cannot modify the size of the output buffer we fake it
313 * by not filling it more than u->buffer_size.
315 len = u->buffer_size;
316 len -= u->written_bytes - (info.play.samples * u->frame_size);
318 /* The sample counter can sometimes go backwards :( */
319 if (len > u->buffer_size)
320 len = 0;
322 if (info.play.error) {
323 pa_log_debug("Solaris buffer underflow!");
324 clear_underflow(u);
327 len -= len % u->frame_size;
329 while (len) {
330 void *p;
331 ssize_t r;
333 if (!u->memchunk.length)
334 pa_sink_render(u->sink, len, &u->memchunk);
336 pa_assert(u->memchunk.length);
338 p = pa_memblock_acquire(u->memchunk.memblock);
339 r = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, NULL);
340 pa_memblock_release(u->memchunk.memblock);
342 if (r < 0) {
343 if (errno == EINTR)
344 continue;
345 else if (errno != EAGAIN) {
346 pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
347 goto fail;
349 } else {
350 pa_assert(r % u->frame_size == 0);
352 u->memchunk.index += r;
353 u->memchunk.length -= r;
355 if (u->memchunk.length <= 0) {
356 pa_memblock_unref(u->memchunk.memblock);
357 pa_memchunk_reset(&u->memchunk);
360 len -= r;
361 u->written_bytes += r;
366 /* Try to read some data and pass it on to the source driver */
368 if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN))) {
369 pa_memchunk memchunk;
370 int err;
371 size_t l;
372 void *p;
373 ssize_t r;
374 audio_info_t info;
376 err = ioctl(u->fd, AUDIO_GETINFO, &info);
377 pa_assert(err >= 0);
379 if (info.record.error) {
380 pa_log_debug("Solaris buffer overflow!");
381 clear_overflow(u);
384 err = ioctl(u->fd, I_NREAD, &l);
385 pa_assert(err >= 0);
387 if (l > 0) {
388 /* This is to make sure it fits in the memory pool. Also, a page
389 should be the most efficient transfer size. */
390 if (l > u->page_size)
391 l = u->page_size;
393 memchunk.memblock = pa_memblock_new(u->core->mempool, l);
394 pa_assert(memchunk.memblock);
396 p = pa_memblock_acquire(memchunk.memblock);
397 r = pa_read(u->fd, p, l, NULL);
398 pa_memblock_release(memchunk.memblock);
400 if (r < 0) {
401 pa_memblock_unref(memchunk.memblock);
402 if (errno != EAGAIN) {
403 pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
404 goto fail;
406 } else {
407 memchunk.index = 0;
408 memchunk.length = r;
410 pa_source_post(u->source, &memchunk);
411 pa_memblock_unref(memchunk.memblock);
413 u->read_bytes += r;
415 revents &= ~POLLIN;
420 if (u->fd >= 0) {
421 struct pollfd *pollfd;
423 pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
424 pollfd->events =
425 ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0);
428 /* Hmm, nothing to do. Let's sleep */
429 if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
430 goto fail;
432 if (ret == 0)
433 goto finish;
435 if (u->fd >= 0) {
436 struct pollfd *pollfd;
438 pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
440 if (pollfd->revents & ~(POLLOUT|POLLIN)) {
441 pa_log("DSP shutdown.");
442 goto fail;
445 revents = pollfd->revents;
446 } else
447 revents = 0;
450 fail:
451 /* We have to continue processing messages until we receive the
452 * SHUTDOWN message */
453 pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
454 pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
456 finish:
457 pa_log_debug("Thread shutting down");
460 static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
461 struct userdata *u = userdata;
463 assert(u);
465 if (u->sink) {
466 pa_sink_get_volume(u->sink);
467 pa_sink_get_mute(u->sink);
470 if (u->source)
471 pa_source_get_volume(u->source);
474 static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
475 audio_info_t info;
477 AUDIO_INITINFO(&info);
479 if (mode != O_RDONLY) {
480 info.play.sample_rate = ss->rate;
481 info.play.channels = ss->channels;
482 switch (ss->format) {
483 case PA_SAMPLE_U8:
484 info.play.precision = 8;
485 info.play.encoding = AUDIO_ENCODING_LINEAR;
486 break;
487 case PA_SAMPLE_ALAW:
488 info.play.precision = 8;
489 info.play.encoding = AUDIO_ENCODING_ALAW;
490 break;
491 case PA_SAMPLE_ULAW:
492 info.play.precision = 8;
493 info.play.encoding = AUDIO_ENCODING_ULAW;
494 break;
495 case PA_SAMPLE_S16NE:
496 info.play.precision = 16;
497 info.play.encoding = AUDIO_ENCODING_LINEAR;
498 break;
499 default:
500 return -1;
504 if (mode != O_WRONLY) {
505 info.record.sample_rate = ss->rate;
506 info.record.channels = ss->channels;
507 switch (ss->format) {
508 case PA_SAMPLE_U8:
509 info.record.precision = 8;
510 info.record.encoding = AUDIO_ENCODING_LINEAR;
511 break;
512 case PA_SAMPLE_ALAW:
513 info.record.precision = 8;
514 info.record.encoding = AUDIO_ENCODING_ALAW;
515 break;
516 case PA_SAMPLE_ULAW:
517 info.record.precision = 8;
518 info.record.encoding = AUDIO_ENCODING_ULAW;
519 break;
520 case PA_SAMPLE_S16NE:
521 info.record.precision = 16;
522 info.record.encoding = AUDIO_ENCODING_LINEAR;
523 break;
524 default:
525 return -1;
529 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
530 if (errno == EINVAL)
531 pa_log("AUDIO_SETINFO: Unsupported sample format.");
532 else
533 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
534 return -1;
537 return 0;
540 static int pa_solaris_set_buffer(int fd, int buffer_size) {
541 audio_info_t info;
543 AUDIO_INITINFO(&info);
545 info.play.buffer_size = buffer_size;
546 info.record.buffer_size = buffer_size;
548 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
549 if (errno == EINVAL)
550 pa_log("AUDIO_SETINFO: Unsupported buffer size.");
551 else
552 pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
553 return -1;
556 return 0;
559 int pa__init(pa_module *m) {
560 struct userdata *u = NULL;
561 const char *p;
562 int fd = -1;
563 int buffer_size;
564 int mode;
565 int record = 1, playback = 1;
566 pa_sample_spec ss;
567 pa_channel_map map;
568 pa_modargs *ma = NULL;
569 char *t;
570 struct pollfd *pollfd;
572 pa_assert(m);
574 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
575 pa_log("failed to parse module arguments.");
576 goto fail;
579 if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
580 pa_log("record= and playback= expect numeric argument.");
581 goto fail;
584 if (!playback && !record) {
585 pa_log("neither playback nor record enabled for device.");
586 goto fail;
589 mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
591 buffer_size = 16384;
592 if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) {
593 pa_log("failed to parse buffer size argument");
594 goto fail;
597 ss = m->core->default_sample_spec;
598 if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
599 pa_log("failed to parse sample specification");
600 goto fail;
603 if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
604 goto fail;
606 pa_log_info("device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
608 if (pa_solaris_auto_format(fd, mode, &ss) < 0)
609 goto fail;
611 if (pa_solaris_set_buffer(fd, buffer_size) < 0)
612 goto fail;
614 u = pa_xmalloc(sizeof(struct userdata));
615 u->core = m->core;
617 u->fd = fd;
619 pa_memchunk_reset(&u->memchunk);
621 /* We use this to get a reasonable chunk size */
622 u->page_size = PA_PAGE_SIZE;
624 u->frame_size = pa_frame_size(&ss);
625 u->buffer_size = buffer_size;
627 u->written_bytes = 0;
628 u->read_bytes = 0;
630 u->module = m;
631 m->userdata = u;
633 pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
635 u->rtpoll = pa_rtpoll_new();
636 pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
638 pa_rtpoll_set_timer_periodic(u->rtpoll, pa_bytes_to_usec(u->buffer_size / 10, &ss));
640 u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
641 pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
642 pollfd->fd = fd;
643 pollfd->events = 0;
644 pollfd->revents = 0;
646 if (mode != O_WRONLY) {
647 u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map);
648 pa_assert(u->source);
650 u->source->userdata = u;
651 u->source->parent.process_msg = source_process_msg;
653 pa_source_set_module(u->source, m);
654 pa_source_set_description(u->source, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
655 pa_xfree(t);
656 pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
657 pa_source_set_rtpoll(u->source, u->rtpoll);
659 u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL;
660 u->source->refresh_volume = 1;
661 } else
662 u->source = NULL;
664 if (mode != O_RDONLY) {
665 u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map);
666 pa_assert(u->sink);
668 u->sink->userdata = u;
669 u->sink->parent.process_msg = sink_process_msg;
671 pa_sink_set_module(u->sink, m);
672 pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
673 pa_xfree(t);
674 pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
675 pa_sink_set_rtpoll(u->sink, u->rtpoll);
677 u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL;
678 u->sink->refresh_volume = 1;
679 u->sink->refresh_mute = 1;
680 } else
681 u->sink = NULL;
683 pa_assert(u->source || u->sink);
685 u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
686 pa_assert(u->sig);
687 ioctl(u->fd, I_SETSIG, S_MSG);
689 if (!(u->thread = pa_thread_new(thread_func, u))) {
690 pa_log("Failed to create thread.");
691 goto fail;
694 /* Read mixer settings */
695 if (u->source)
696 pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_GET_VOLUME, &u->source->volume, 0, NULL);
697 if (u->sink) {
698 pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_VOLUME, &u->sink->volume, 0, NULL);
699 pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_MUTE, &u->sink->muted, 0, NULL);
702 if (u->sink)
703 pa_sink_put(u->sink);
704 if (u->source)
705 pa_source_put(u->source);
707 pa_modargs_free(ma);
709 return 0;
711 fail:
712 if (u)
713 pa__done(m);
714 else if (fd >= 0)
715 close(fd);
717 if (ma)
718 pa_modargs_free(ma);
720 return -1;
723 void pa__done(pa_module *m) {
724 struct userdata *u;
726 pa_assert(m);
728 if (!(u = m->userdata))
729 return;
731 ioctl(u->fd, I_SETSIG, 0);
732 pa_signal_free(u->sig);
734 if (u->sink)
735 pa_sink_unlink(u->sink);
737 if (u->source)
738 pa_source_unlink(u->source);
740 if (u->thread) {
741 pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
742 pa_thread_free(u->thread);
745 pa_thread_mq_done(&u->thread_mq);
747 if (u->sink)
748 pa_sink_unref(u->sink);
750 if (u->source)
751 pa_source_unref(u->source);
753 if (u->memchunk.memblock)
754 pa_memblock_unref(u->memchunk.memblock);
756 if (u->rtpoll_item)
757 pa_rtpoll_item_free(u->rtpoll_item);
759 if (u->rtpoll)
760 pa_rtpoll_free(u->rtpoll);
762 if (u->fd >= 0)
763 close(u->fd);
765 pa_xfree(u);