2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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.1 of the License,
10 or (at your option) any later version.
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.
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
23 /* General power management rules:
25 * When SUSPENDED we close the audio device.
27 * We make no difference between IDLE and RUNNING in our handling.
29 * As long as we are in RUNNING/IDLE state we will *always* write data to
30 * the device. If none is available from the inputs, we write silence
33 * If power should be saved on IDLE module-suspend-on-idle should be used.
41 #ifdef HAVE_SYS_MMAN_H
45 #include <sys/soundcard.h>
46 #include <sys/ioctl.h>
53 #include <pulse/xmalloc.h>
54 #include <pulse/util.h>
56 #include <pulsecore/core-error.h>
57 #include <pulsecore/thread.h>
58 #include <pulsecore/sink.h>
59 #include <pulsecore/source.h>
60 #include <pulsecore/module.h>
61 #include <pulsecore/sample-util.h>
62 #include <pulsecore/core-util.h>
63 #include <pulsecore/modargs.h>
64 #include <pulsecore/log.h>
65 #include <pulsecore/macro.h>
66 #include <pulsecore/thread-mq.h>
67 #include <pulsecore/rtpoll.h>
68 #include <pulsecore/poll.h>
70 #if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
71 #include <sys/audioio.h>
72 #include <sys/syscall.h>
76 #include "module-oss-symdef.h"
78 PA_MODULE_AUTHOR("Lennart Poettering");
79 PA_MODULE_DESCRIPTION("OSS Sink/Source");
80 PA_MODULE_VERSION(PACKAGE_VERSION
);
81 PA_MODULE_LOAD_ONCE(false);
83 "sink_name=<name for the sink> "
84 "sink_properties=<properties for the sink> "
85 "source_name=<name for the source> "
86 "source_properties=<properties for the source> "
87 "device=<OSS device> "
88 "record=<enable source?> "
89 "playback=<enable sink?> "
90 "format=<sample format> "
92 "channels=<number of channels> "
93 "channel_map=<channel map> "
94 "fragments=<number of fragments> "
95 "fragment_size=<fragment size> "
96 "mmap=<enable memory mapping?>");
98 PA_MODULE_DEPRECATED("Please use module-alsa-card instead of module-oss!");
101 #define DEFAULT_DEVICE "/dev/dsp"
110 pa_thread_mq thread_mq
;
115 pa_memchunk memchunk
;
118 uint32_t in_fragment_size
, out_fragment_size
, in_nfrags
, out_nfrags
, in_hwbuf_size
, out_hwbuf_size
;
119 bool use_getospace
, use_getispace
;
122 bool sink_suspended
, source_suspended
;
130 int nfrags
, frag_size
, orig_frag_size
;
133 unsigned out_mmap_current
, in_mmap_current
;
134 void *in_mmap
, *out_mmap
;
135 pa_memblock
**in_mmap_memblocks
, **out_mmap_memblocks
;
137 int in_mmap_saved_nfrags
, out_mmap_saved_nfrags
;
139 pa_rtpoll_item
*rtpoll_item
;
142 static const char* const valid_modargs
[] = {
160 static int trigger(struct userdata
*u
, bool quick
) {
161 int enable_bits
= 0, zero
= 0;
168 pa_log_debug("trigger");
170 if (u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
))
171 enable_bits
|= PCM_ENABLE_INPUT
;
173 if (u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
))
174 enable_bits
|= PCM_ENABLE_OUTPUT
;
176 pa_log_debug("trigger: %i", enable_bits
);
181 ioctl(u
->fd
, SNDCTL_DSP_SETTRIGGER
, &zero
);
183 #ifdef SNDCTL_DSP_HALT
184 if (enable_bits
== 0)
185 if (ioctl(u
->fd
, SNDCTL_DSP_HALT
, NULL
) < 0)
186 pa_log_warn("SNDCTL_DSP_HALT: %s", pa_cstrerror(errno
));
189 if (ioctl(u
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable_bits
) < 0)
190 pa_log_warn("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno
));
192 if (u
->sink
&& !(enable_bits
& PCM_ENABLE_OUTPUT
)) {
193 pa_log_debug("clearing playback buffer");
194 pa_silence_memory(u
->out_mmap
, u
->out_hwbuf_size
, &u
->sink
->sample_spec
);
200 if (ioctl(u
->fd
, SNDCTL_DSP_POST
, NULL
) < 0)
201 pa_log_warn("SNDCTL_DSP_POST: %s", pa_cstrerror(errno
));
205 * Some crappy drivers do not start the recording until we
206 * read something. Without this snippet, poll will never
207 * register the fd as ready.
210 if (u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
)) {
211 uint8_t *buf
= pa_xnew(uint8_t, u
->in_fragment_size
);
213 if (pa_read(u
->fd
, buf
, u
->in_fragment_size
, NULL
) < 0) {
214 pa_log("pa_read() failed: %s", pa_cstrerror(errno
));
227 static void mmap_fill_memblocks(struct userdata
*u
, unsigned n
) {
229 pa_assert(u
->out_mmap_memblocks
);
231 /* pa_log("Mmmap writing %u blocks", n); */
236 if (u
->out_mmap_memblocks
[u
->out_mmap_current
])
237 pa_memblock_unref_fixed(u
->out_mmap_memblocks
[u
->out_mmap_current
]);
239 chunk
.memblock
= u
->out_mmap_memblocks
[u
->out_mmap_current
] =
240 pa_memblock_new_fixed(
242 (uint8_t*) u
->out_mmap
+ u
->out_fragment_size
* u
->out_mmap_current
,
243 u
->out_fragment_size
,
246 chunk
.length
= pa_memblock_get_length(chunk
.memblock
);
249 pa_sink_render_into_full(u
->sink
, &chunk
);
251 u
->out_mmap_current
++;
252 while (u
->out_mmap_current
>= u
->out_nfrags
)
253 u
->out_mmap_current
-= u
->out_nfrags
;
259 static int mmap_write(struct userdata
*u
) {
260 struct count_info info
;
265 /* pa_log("Mmmap writing..."); */
267 if (ioctl(u
->fd
, SNDCTL_DSP_GETOPTR
, &info
) < 0) {
268 pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno
));
272 info
.blocks
+= u
->out_mmap_saved_nfrags
;
273 u
->out_mmap_saved_nfrags
= 0;
276 mmap_fill_memblocks(u
, (unsigned) info
.blocks
);
281 static void mmap_post_memblocks(struct userdata
*u
, unsigned n
) {
283 pa_assert(u
->in_mmap_memblocks
);
285 /* pa_log("Mmmap reading %u blocks", n); */
290 if (!u
->in_mmap_memblocks
[u
->in_mmap_current
]) {
292 chunk
.memblock
= u
->in_mmap_memblocks
[u
->in_mmap_current
] =
293 pa_memblock_new_fixed(
295 (uint8_t*) u
->in_mmap
+ u
->in_fragment_size
*u
->in_mmap_current
,
299 chunk
.length
= pa_memblock_get_length(chunk
.memblock
);
302 pa_source_post(u
->source
, &chunk
);
305 u
->in_mmap_current
++;
306 while (u
->in_mmap_current
>= u
->in_nfrags
)
307 u
->in_mmap_current
-= u
->in_nfrags
;
313 static void mmap_clear_memblocks(struct userdata
*u
, unsigned n
) {
314 unsigned i
= u
->in_mmap_current
;
317 pa_assert(u
->in_mmap_memblocks
);
319 if (n
> u
->in_nfrags
)
323 if (u
->in_mmap_memblocks
[i
]) {
324 pa_memblock_unref_fixed(u
->in_mmap_memblocks
[i
]);
325 u
->in_mmap_memblocks
[i
] = NULL
;
329 while (i
>= u
->in_nfrags
)
336 static int mmap_read(struct userdata
*u
) {
337 struct count_info info
;
339 pa_assert(u
->source
);
341 /* pa_log("Mmmap reading..."); */
343 if (ioctl(u
->fd
, SNDCTL_DSP_GETIPTR
, &info
) < 0) {
344 pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno
));
348 /* pa_log("... %i", info.blocks); */
350 info
.blocks
+= u
->in_mmap_saved_nfrags
;
351 u
->in_mmap_saved_nfrags
= 0;
353 if (info
.blocks
> 0) {
354 mmap_post_memblocks(u
, (unsigned) info
.blocks
);
355 mmap_clear_memblocks(u
, u
->in_nfrags
/2);
361 static pa_usec_t
mmap_sink_get_latency(struct userdata
*u
) {
362 struct count_info info
;
367 if (ioctl(u
->fd
, SNDCTL_DSP_GETOPTR
, &info
) < 0) {
368 pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno
));
372 u
->out_mmap_saved_nfrags
+= info
.blocks
;
374 bpos
= ((u
->out_mmap_current
+ (unsigned) u
->out_mmap_saved_nfrags
) * u
->out_fragment_size
) % u
->out_hwbuf_size
;
376 if (bpos
<= (size_t) info
.ptr
)
377 n
= u
->out_hwbuf_size
- ((size_t) info
.ptr
- bpos
);
379 n
= bpos
- (size_t) info
.ptr
;
381 /* pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->out_fragment_size, u->out_fragments); */
383 return pa_bytes_to_usec(n
, &u
->sink
->sample_spec
);
386 static pa_usec_t
mmap_source_get_latency(struct userdata
*u
) {
387 struct count_info info
;
392 if (ioctl(u
->fd
, SNDCTL_DSP_GETIPTR
, &info
) < 0) {
393 pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno
));
397 u
->in_mmap_saved_nfrags
+= info
.blocks
;
398 bpos
= ((u
->in_mmap_current
+ (unsigned) u
->in_mmap_saved_nfrags
) * u
->in_fragment_size
) % u
->in_hwbuf_size
;
400 if (bpos
<= (size_t) info
.ptr
)
401 n
= (size_t) info
.ptr
- bpos
;
403 n
= u
->in_hwbuf_size
- bpos
+ (size_t) info
.ptr
;
405 /* pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->in_fragment_size, u->in_fragments); */
407 return pa_bytes_to_usec(n
, &u
->source
->sample_spec
);
410 static pa_usec_t
io_sink_get_latency(struct userdata
*u
) {
415 if (u
->use_getodelay
) {
417 #if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
418 #if defined(AUDIO_GETBUFINFO)
419 struct audio_info info
;
420 if (syscall(SYS_ioctl
, u
->fd
, AUDIO_GETBUFINFO
, &info
) < 0) {
421 pa_log_info("Device doesn't support AUDIO_GETBUFINFO: %s", pa_cstrerror(errno
));
422 u
->use_getodelay
= 0;
424 arg
= info
.play
.seek
+ info
.blocksize
/ 2;
425 r
= pa_bytes_to_usec((size_t) arg
, &u
->sink
->sample_spec
);
428 pa_log_info("System doesn't support AUDIO_GETBUFINFO");
429 u
->use_getodelay
= 0;
432 if (ioctl(u
->fd
, SNDCTL_DSP_GETODELAY
, &arg
) < 0) {
433 pa_log_info("Device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno
));
434 u
->use_getodelay
= 0;
436 r
= pa_bytes_to_usec((size_t) arg
, &u
->sink
->sample_spec
);
440 if (!u
->use_getodelay
&& u
->use_getospace
) {
441 struct audio_buf_info info
;
443 if (ioctl(u
->fd
, SNDCTL_DSP_GETOSPACE
, &info
) < 0) {
444 pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno
));
445 u
->use_getospace
= 0;
447 r
= pa_bytes_to_usec((size_t) info
.bytes
, &u
->sink
->sample_spec
);
450 if (u
->memchunk
.memblock
)
451 r
+= pa_bytes_to_usec(u
->memchunk
.length
, &u
->sink
->sample_spec
);
456 static pa_usec_t
io_source_get_latency(struct userdata
*u
) {
461 if (u
->use_getispace
) {
462 struct audio_buf_info info
;
464 if (ioctl(u
->fd
, SNDCTL_DSP_GETISPACE
, &info
) < 0) {
465 pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno
));
466 u
->use_getispace
= 0;
468 r
= pa_bytes_to_usec((size_t) info
.bytes
, &u
->source
->sample_spec
);
474 static void build_pollfd(struct userdata
*u
) {
475 struct pollfd
*pollfd
;
478 pa_assert(u
->fd
>= 0);
481 pa_rtpoll_item_free(u
->rtpoll_item
);
483 u
->rtpoll_item
= pa_rtpoll_item_new(u
->rtpoll
, PA_RTPOLL_NEVER
, 1);
484 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
490 /* Called from IO context */
491 static int suspend(struct userdata
*u
) {
493 pa_assert(u
->fd
>= 0);
495 pa_log_info("Suspending...");
497 if (u
->out_mmap_memblocks
) {
499 for (i
= 0; i
< u
->out_nfrags
; i
++)
500 if (u
->out_mmap_memblocks
[i
]) {
501 pa_memblock_unref_fixed(u
->out_mmap_memblocks
[i
]);
502 u
->out_mmap_memblocks
[i
] = NULL
;
506 if (u
->in_mmap_memblocks
) {
508 for (i
= 0; i
< u
->in_nfrags
; i
++)
509 if (u
->in_mmap_memblocks
[i
]) {
510 pa_memblock_unref_fixed(u
->in_mmap_memblocks
[i
]);
511 u
->in_mmap_memblocks
[i
] = NULL
;
515 if (u
->in_mmap
&& u
->in_mmap
!= MAP_FAILED
) {
516 munmap(u
->in_mmap
, u
->in_hwbuf_size
);
520 if (u
->out_mmap
&& u
->out_mmap
!= MAP_FAILED
) {
521 munmap(u
->out_mmap
, u
->out_hwbuf_size
);
526 ioctl(u
->fd
, SNDCTL_DSP_SYNC
, NULL
);
530 if (u
->rtpoll_item
) {
531 pa_rtpoll_item_free(u
->rtpoll_item
);
532 u
->rtpoll_item
= NULL
;
535 pa_log_info("Device suspended...");
540 /* Called from IO context */
541 static int unsuspend(struct userdata
*u
) {
543 pa_sample_spec ss
, *ss_original
;
544 int frag_size
, in_frag_size
, out_frag_size
;
545 int in_nfrags
, out_nfrags
;
546 struct audio_buf_info info
;
549 pa_assert(u
->fd
< 0);
553 pa_log_info("Trying resume...");
555 if ((u
->fd
= pa_oss_open(u
->device_name
, &m
, NULL
)) < 0) {
556 pa_log_warn("Resume failed, device busy (%s)", pa_cstrerror(errno
));
561 pa_log_warn("Resume failed, couldn't open device with original access mode.");
565 if (u
->nfrags
>= 2 && u
->frag_size
>= 1)
566 if (pa_oss_set_fragments(u
->fd
, u
->nfrags
, u
->orig_frag_size
) < 0) {
567 pa_log_warn("Resume failed, couldn't set original fragment settings.");
571 ss
= *(ss_original
= u
->sink
? &u
->sink
->sample_spec
: &u
->source
->sample_spec
);
572 if (pa_oss_auto_format(u
->fd
, &ss
) < 0 || !pa_sample_spec_equal(&ss
, ss_original
)) {
573 pa_log_warn("Resume failed, couldn't set original sample format settings.");
577 if (ioctl(u
->fd
, SNDCTL_DSP_GETBLKSIZE
, &frag_size
) < 0) {
578 pa_log_warn("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno
));
582 in_frag_size
= out_frag_size
= frag_size
;
583 in_nfrags
= out_nfrags
= u
->nfrags
;
585 if (ioctl(u
->fd
, SNDCTL_DSP_GETISPACE
, &info
) >= 0) {
586 in_frag_size
= info
.fragsize
;
587 in_nfrags
= info
.fragstotal
;
590 if (ioctl(u
->fd
, SNDCTL_DSP_GETOSPACE
, &info
) >= 0) {
591 out_frag_size
= info
.fragsize
;
592 out_nfrags
= info
.fragstotal
;
595 if ((u
->source
&& (in_frag_size
!= (int) u
->in_fragment_size
|| in_nfrags
!= (int) u
->in_nfrags
)) ||
596 (u
->sink
&& (out_frag_size
!= (int) u
->out_fragment_size
|| out_nfrags
!= (int) u
->out_nfrags
))) {
597 pa_log_warn("Resume failed, input fragment settings don't match.");
603 if ((u
->in_mmap
= mmap(NULL
, u
->in_hwbuf_size
, PROT_READ
, MAP_SHARED
, u
->fd
, 0)) == MAP_FAILED
) {
604 pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno
));
610 if ((u
->out_mmap
= mmap(NULL
, u
->out_hwbuf_size
, PROT_WRITE
, MAP_SHARED
, u
->fd
, 0)) == MAP_FAILED
) {
611 pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno
));
612 if (u
->in_mmap
&& u
->in_mmap
!= MAP_FAILED
) {
613 munmap(u
->in_mmap
, u
->in_hwbuf_size
);
620 pa_silence_memory(u
->out_mmap
, u
->out_hwbuf_size
, &ss
);
624 u
->out_mmap_current
= u
->in_mmap_current
= 0;
625 u
->out_mmap_saved_nfrags
= u
->in_mmap_saved_nfrags
= 0;
627 pa_assert(!u
->rtpoll_item
);
631 if (u
->sink
&& u
->sink
->get_volume
)
632 u
->sink
->get_volume(u
->sink
);
633 if (u
->source
&& u
->source
->get_volume
)
634 u
->source
->get_volume(u
->source
);
636 pa_log_info("Resumed successfully...");
646 /* Called from IO context */
647 static int sink_process_msg(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
648 struct userdata
*u
= PA_SINK(o
)->userdata
;
650 bool do_trigger
= false, quick
= true;
654 case PA_SINK_MESSAGE_GET_LATENCY
: {
659 r
= mmap_sink_get_latency(u
);
661 r
= io_sink_get_latency(u
);
664 *((pa_usec_t
*) data
) = r
;
669 case PA_SINK_MESSAGE_SET_STATE
:
671 switch ((pa_sink_state_t
) PA_PTR_TO_UINT(data
)) {
673 case PA_SINK_SUSPENDED
:
674 pa_assert(PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
));
676 if (!u
->source
|| u
->source_suspended
) {
683 u
->sink_suspended
= true;
687 case PA_SINK_RUNNING
:
689 if (u
->sink
->thread_info
.state
== PA_SINK_INIT
) {
691 quick
= u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
);
694 if (u
->sink
->thread_info
.state
== PA_SINK_SUSPENDED
) {
696 if (!u
->source
|| u
->source_suspended
) {
697 if (unsuspend(u
) < 0)
704 u
->out_mmap_current
= 0;
705 u
->out_mmap_saved_nfrags
= 0;
707 u
->sink_suspended
= false;
712 case PA_SINK_INVALID_STATE
:
713 case PA_SINK_UNLINKED
:
722 ret
= pa_sink_process_msg(o
, code
, data
, offset
, chunk
);
724 if (ret
>= 0 && do_trigger
) {
725 if (trigger(u
, quick
) < 0)
732 static int source_process_msg(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
733 struct userdata
*u
= PA_SOURCE(o
)->userdata
;
735 int do_trigger
= false, quick
= true;
739 case PA_SOURCE_MESSAGE_GET_LATENCY
: {
744 r
= mmap_source_get_latency(u
);
746 r
= io_source_get_latency(u
);
749 *((pa_usec_t
*) data
) = r
;
753 case PA_SOURCE_MESSAGE_SET_STATE
:
755 switch ((pa_source_state_t
) PA_PTR_TO_UINT(data
)) {
756 case PA_SOURCE_SUSPENDED
:
757 pa_assert(PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
));
759 if (!u
->sink
|| u
->sink_suspended
) {
766 u
->source_suspended
= true;
770 case PA_SOURCE_RUNNING
:
772 if (u
->source
->thread_info
.state
== PA_SOURCE_INIT
) {
774 quick
= u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
);
777 if (u
->source
->thread_info
.state
== PA_SOURCE_SUSPENDED
) {
779 if (!u
->sink
|| u
->sink_suspended
) {
780 if (unsuspend(u
) < 0)
787 u
->in_mmap_current
= 0;
788 u
->in_mmap_saved_nfrags
= 0;
790 u
->source_suspended
= false;
794 case PA_SOURCE_UNLINKED
:
796 case PA_SOURCE_INVALID_STATE
:
804 ret
= pa_source_process_msg(o
, code
, data
, offset
, chunk
);
806 if (ret
>= 0 && do_trigger
) {
807 if (trigger(u
, quick
) < 0)
814 static void sink_get_volume(pa_sink
*s
) {
817 pa_assert_se(u
= s
->userdata
);
819 pa_assert(u
->mixer_devmask
& (SOUND_MASK_VOLUME
|SOUND_MASK_PCM
));
821 if (u
->mixer_devmask
& SOUND_MASK_VOLUME
)
822 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_READ_VOLUME
, &s
->sample_spec
, &s
->real_volume
) >= 0)
825 if (u
->mixer_devmask
& SOUND_MASK_PCM
)
826 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_READ_PCM
, &s
->sample_spec
, &s
->real_volume
) >= 0)
829 pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno
));
832 static void sink_set_volume(pa_sink
*s
) {
835 pa_assert_se(u
= s
->userdata
);
837 pa_assert(u
->mixer_devmask
& (SOUND_MASK_VOLUME
|SOUND_MASK_PCM
));
839 if (u
->mixer_devmask
& SOUND_MASK_VOLUME
)
840 if (pa_oss_set_volume(u
->mixer_fd
, SOUND_MIXER_WRITE_VOLUME
, &s
->sample_spec
, &s
->real_volume
) >= 0)
843 if (u
->mixer_devmask
& SOUND_MASK_PCM
)
844 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_WRITE_PCM
, &s
->sample_spec
, &s
->real_volume
) >= 0)
847 pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno
));
850 static void source_get_volume(pa_source
*s
) {
853 pa_assert_se(u
= s
->userdata
);
855 pa_assert(u
->mixer_devmask
& (SOUND_MASK_IGAIN
|SOUND_MASK_RECLEV
));
857 if (u
->mixer_devmask
& SOUND_MASK_IGAIN
)
858 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_READ_IGAIN
, &s
->sample_spec
, &s
->real_volume
) >= 0)
861 if (u
->mixer_devmask
& SOUND_MASK_RECLEV
)
862 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_READ_RECLEV
, &s
->sample_spec
, &s
->real_volume
) >= 0)
865 pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno
));
868 static void source_set_volume(pa_source
*s
) {
871 pa_assert_se(u
= s
->userdata
);
873 pa_assert(u
->mixer_devmask
& (SOUND_MASK_IGAIN
|SOUND_MASK_RECLEV
));
875 if (u
->mixer_devmask
& SOUND_MASK_IGAIN
)
876 if (pa_oss_set_volume(u
->mixer_fd
, SOUND_MIXER_WRITE_IGAIN
, &s
->sample_spec
, &s
->real_volume
) >= 0)
879 if (u
->mixer_devmask
& SOUND_MASK_RECLEV
)
880 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_WRITE_RECLEV
, &s
->sample_spec
, &s
->real_volume
) >= 0)
883 pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno
));
886 static void thread_func(void *userdata
) {
887 struct userdata
*u
= userdata
;
888 int write_type
= 0, read_type
= 0;
893 pa_log_debug("Thread starting up");
895 if (u
->core
->realtime_scheduling
)
896 pa_make_realtime(u
->core
->realtime_priority
);
898 pa_thread_mq_install(&u
->thread_mq
);
903 /* pa_log("loop"); */
905 if (PA_UNLIKELY(u
->sink
&& u
->sink
->thread_info
.rewind_requested
))
906 pa_sink_process_rewind(u
->sink
, 0);
908 /* Render some data and write it to the dsp */
910 if (u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
) && ((revents
& POLLOUT
) || u
->use_mmap
|| u
->use_getospace
)) {
914 if ((ret
= mmap_write(u
)) < 0)
924 bool loop
= false, work_done
= false;
926 l
= (ssize_t
) u
->out_fragment_size
;
928 if (u
->use_getospace
) {
931 if (ioctl(u
->fd
, SNDCTL_DSP_GETOSPACE
, &info
) < 0) {
932 pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno
));
933 u
->use_getospace
= false;
937 /* We loop only if GETOSPACE worked and we
938 * actually *know* that we can write more than
939 * one fragment at a time */
944 /* Round down to multiples of the fragment size,
945 * because OSS needs that (at least some versions
947 l
= (l
/(ssize_t
) u
->out_fragment_size
) * (ssize_t
) u
->out_fragment_size
;
949 /* Hmm, so poll() signalled us that we can read
950 * something, but GETOSPACE told us there was nothing?
951 * Hmm, make the best of it, try to read some data, to
952 * avoid spinning forever. */
953 if (l
<= 0 && (revents
& POLLOUT
)) {
954 l
= (ssize_t
) u
->out_fragment_size
;
962 if (u
->memchunk
.length
<= 0)
963 pa_sink_render(u
->sink
, (size_t) l
, &u
->memchunk
);
965 pa_assert(u
->memchunk
.length
> 0);
967 p
= pa_memblock_acquire(u
->memchunk
.memblock
);
968 t
= pa_write(u
->fd
, (uint8_t*) p
+ u
->memchunk
.index
, u
->memchunk
.length
, &write_type
);
969 pa_memblock_release(u
->memchunk
.memblock
);
971 /* pa_log("wrote %i bytes of %u", t, l); */
980 else if (errno
== EAGAIN
) {
981 pa_log_debug("EAGAIN");
987 pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno
));
993 u
->memchunk
.index
+= (size_t) t
;
994 u
->memchunk
.length
-= (size_t) t
;
996 if (u
->memchunk
.length
<= 0) {
997 pa_memblock_unref(u
->memchunk
.memblock
);
998 pa_memchunk_reset(&u
->memchunk
);
1003 revents
&= ~POLLOUT
;
1016 /* Try to read some data and pass it on to the source driver. */
1018 if (u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
) && ((revents
& POLLIN
) || u
->use_mmap
|| u
->use_getispace
)) {
1022 if ((ret
= mmap_read(u
)) < 0)
1034 pa_memchunk memchunk
;
1035 bool loop
= false, work_done
= false;
1037 l
= (ssize_t
) u
->in_fragment_size
;
1039 if (u
->use_getispace
) {
1040 audio_buf_info info
;
1042 if (ioctl(u
->fd
, SNDCTL_DSP_GETISPACE
, &info
) < 0) {
1043 pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno
));
1044 u
->use_getispace
= false;
1051 l
= (l
/(ssize_t
) u
->in_fragment_size
) * (ssize_t
) u
->in_fragment_size
;
1053 if (l
<= 0 && (revents
& POLLIN
)) {
1054 l
= (ssize_t
) u
->in_fragment_size
;
1064 memchunk
.memblock
= pa_memblock_new(u
->core
->mempool
, (size_t) -1);
1066 k
= pa_memblock_get_length(memchunk
.memblock
);
1071 k
= (k
/u
->frame_size
)*u
->frame_size
;
1073 p
= pa_memblock_acquire(memchunk
.memblock
);
1074 t
= pa_read(u
->fd
, p
, k
, &read_type
);
1075 pa_memblock_release(memchunk
.memblock
);
1077 pa_assert(t
!= 0); /* EOF cannot happen */
1079 /* pa_log("read %i bytes of %u", t, l); */
1082 pa_memblock_unref(memchunk
.memblock
);
1087 else if (errno
== EAGAIN
) {
1088 pa_log_debug("EAGAIN");
1094 pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno
));
1100 memchunk
.length
= (size_t) t
;
1102 pa_source_post(u
->source
, &memchunk
);
1103 pa_memblock_unref(memchunk
.memblock
);
1120 /* pa_log("loop2 revents=%i", revents); */
1122 if (u
->rtpoll_item
) {
1123 struct pollfd
*pollfd
;
1125 pa_assert(u
->fd
>= 0);
1127 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
1128 pollfd
->events
= (short)
1129 (((u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
)) ? POLLIN
: 0) |
1130 ((u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
)) ? POLLOUT
: 0));
1133 /* Hmm, nothing to do. Let's sleep */
1134 if ((ret
= pa_rtpoll_run(u
->rtpoll
, true)) < 0)
1140 if (u
->rtpoll_item
) {
1141 struct pollfd
*pollfd
;
1143 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
1145 if (pollfd
->revents
& ~(POLLOUT
|POLLIN
)) {
1146 pa_log("DSP shutdown.");
1150 revents
= pollfd
->revents
;
1156 /* If this was no regular exit from the loop we have to continue
1157 * processing messages until we received PA_MESSAGE_SHUTDOWN */
1158 pa_asyncmsgq_post(u
->thread_mq
.outq
, PA_MSGOBJECT(u
->core
), PA_CORE_MESSAGE_UNLOAD_MODULE
, u
->module
, 0, NULL
, NULL
);
1159 pa_asyncmsgq_wait_for(u
->thread_mq
.inq
, PA_MESSAGE_SHUTDOWN
);
1162 pa_log_debug("Thread shutting down");
1165 int pa__init(pa_module
*m
) {
1167 struct audio_buf_info info
;
1168 struct userdata
*u
= NULL
;
1171 int nfrags
, orig_frag_size
, frag_size
;
1173 bool record
= true, playback
= true, use_mmap
= true;
1176 pa_modargs
*ma
= NULL
;
1180 pa_sink_new_data sink_new_data
;
1181 pa_source_new_data source_new_data
;
1185 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1186 pa_log("Failed to parse module arguments.");
1190 if (pa_modargs_get_value_boolean(ma
, "record", &record
) < 0 || pa_modargs_get_value_boolean(ma
, "playback", &playback
) < 0) {
1191 pa_log("record= and playback= expect boolean argument.");
1195 if (!playback
&& !record
) {
1196 pa_log("Neither playback nor record enabled for device.");
1200 mode
= (playback
&& record
) ? O_RDWR
: (playback
? O_WRONLY
: (record
? O_RDONLY
: 0));
1202 ss
= m
->core
->default_sample_spec
;
1203 map
= m
->core
->default_channel_map
;
1204 if (pa_modargs_get_sample_spec_and_channel_map(ma
, &ss
, &map
, PA_CHANNEL_MAP_OSS
) < 0) {
1205 pa_log("Failed to parse sample specification or channel map");
1209 nfrags
= (int) m
->core
->default_n_fragments
;
1210 frag_size
= (int) pa_usec_to_bytes(m
->core
->default_fragment_size_msec
*1000, &ss
);
1212 frag_size
= (int) pa_frame_size(&ss
);
1214 if (pa_modargs_get_value_s32(ma
, "fragments", &nfrags
) < 0 || pa_modargs_get_value_s32(ma
, "fragment_size", &frag_size
) < 0) {
1215 pa_log("Failed to parse fragments arguments");
1219 if (pa_modargs_get_value_boolean(ma
, "mmap", &use_mmap
) < 0) {
1220 pa_log("Failed to parse mmap argument.");
1224 if ((fd
= pa_oss_open(dev
= pa_modargs_get_value(ma
, "device", DEFAULT_DEVICE
), &mode
, &caps
)) < 0)
1227 if (use_mmap
&& (!(caps
& DSP_CAP_MMAP
) || !(caps
& DSP_CAP_TRIGGER
))) {
1228 pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode.");
1232 if (use_mmap
&& mode
== O_WRONLY
) {
1233 pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode.");
1237 if (pa_oss_get_hw_description(dev
, hwdesc
, sizeof(hwdesc
)) >= 0)
1238 pa_log_info("Hardware name is '%s'.", hwdesc
);
1242 pa_log_info("Device opened in %s mode.", mode
== O_WRONLY
? "O_WRONLY" : (mode
== O_RDONLY
? "O_RDONLY" : "O_RDWR"));
1244 orig_frag_size
= frag_size
;
1245 if (nfrags
>= 2 && frag_size
>= 1)
1246 if (pa_oss_set_fragments(fd
, nfrags
, frag_size
) < 0)
1249 if (pa_oss_auto_format(fd
, &ss
) < 0)
1252 if (ioctl(fd
, SNDCTL_DSP_GETBLKSIZE
, &frag_size
) < 0) {
1253 pa_log("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno
));
1256 pa_assert(frag_size
> 0);
1258 u
= pa_xnew0(struct userdata
, 1);
1264 u
->mixer_devmask
= 0;
1265 u
->use_getospace
= u
->use_getispace
= true;
1266 u
->use_getodelay
= true;
1268 u
->frame_size
= pa_frame_size(&ss
);
1269 u
->device_name
= pa_xstrdup(dev
);
1270 u
->in_nfrags
= u
->out_nfrags
= (uint32_t) (u
->nfrags
= nfrags
);
1271 u
->out_fragment_size
= u
->in_fragment_size
= (uint32_t) (u
->frag_size
= frag_size
);
1272 u
->orig_frag_size
= orig_frag_size
;
1273 u
->use_mmap
= use_mmap
;
1274 u
->rtpoll
= pa_rtpoll_new();
1275 pa_thread_mq_init(&u
->thread_mq
, m
->core
->mainloop
, u
->rtpoll
);
1276 u
->rtpoll_item
= NULL
;
1279 if (ioctl(fd
, SNDCTL_DSP_GETISPACE
, &info
) >= 0) {
1280 pa_log_info("Input -- %u fragments of size %u.", info
.fragstotal
, info
.fragsize
);
1281 u
->in_fragment_size
= (uint32_t) info
.fragsize
;
1282 u
->in_nfrags
= (uint32_t) info
.fragstotal
;
1283 u
->use_getispace
= true;
1286 if (ioctl(fd
, SNDCTL_DSP_GETOSPACE
, &info
) >= 0) {
1287 pa_log_info("Output -- %u fragments of size %u.", info
.fragstotal
, info
.fragsize
);
1288 u
->out_fragment_size
= (uint32_t) info
.fragsize
;
1289 u
->out_nfrags
= (uint32_t) info
.fragstotal
;
1290 u
->use_getospace
= true;
1293 u
->in_hwbuf_size
= u
->in_nfrags
* u
->in_fragment_size
;
1294 u
->out_hwbuf_size
= u
->out_nfrags
* u
->out_fragment_size
;
1296 if (mode
!= O_WRONLY
) {
1297 char *name_buf
= NULL
;
1300 if ((u
->in_mmap
= mmap(NULL
, u
->in_hwbuf_size
, PROT_READ
, MAP_SHARED
, fd
, 0)) == MAP_FAILED
) {
1301 pa_log_warn("mmap(PROT_READ) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno
));
1302 use_mmap
= u
->use_mmap
= false;
1305 pa_log_debug("Successfully mmap()ed input buffer.");
1308 if ((name
= pa_modargs_get_value(ma
, "source_name", NULL
)))
1309 namereg_fail
= true;
1311 name
= name_buf
= pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev
));
1312 namereg_fail
= false;
1315 pa_source_new_data_init(&source_new_data
);
1316 source_new_data
.driver
= __FILE__
;
1317 source_new_data
.module
= m
;
1318 pa_source_new_data_set_name(&source_new_data
, name
);
1319 source_new_data
.namereg_fail
= namereg_fail
;
1320 pa_source_new_data_set_sample_spec(&source_new_data
, &ss
);
1321 pa_source_new_data_set_channel_map(&source_new_data
, &map
);
1322 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_STRING
, dev
);
1323 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_API
, "oss");
1324 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, hwdesc
[0] ? hwdesc
: dev
);
1325 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_ACCESS_MODE
, use_mmap
? "mmap" : "serial");
1326 pa_proplist_setf(source_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE
, "%lu", (unsigned long) (u
->in_hwbuf_size
));
1327 pa_proplist_setf(source_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE
, "%lu", (unsigned long) (u
->in_fragment_size
));
1329 if (pa_modargs_get_proplist(ma
, "source_properties", source_new_data
.proplist
, PA_UPDATE_REPLACE
) < 0) {
1330 pa_log("Invalid properties");
1331 pa_source_new_data_done(&source_new_data
);
1336 u
->source
= pa_source_new(m
->core
, &source_new_data
, PA_SOURCE_HARDWARE
|PA_SOURCE_LATENCY
);
1337 pa_source_new_data_done(&source_new_data
);
1341 pa_log("Failed to create source object");
1345 u
->source
->parent
.process_msg
= source_process_msg
;
1346 u
->source
->userdata
= u
;
1348 pa_source_set_asyncmsgq(u
->source
, u
->thread_mq
.inq
);
1349 pa_source_set_rtpoll(u
->source
, u
->rtpoll
);
1350 pa_source_set_fixed_latency(u
->source
, pa_bytes_to_usec(u
->in_hwbuf_size
, &u
->source
->sample_spec
));
1351 u
->source
->refresh_volume
= true;
1354 u
->in_mmap_memblocks
= pa_xnew0(pa_memblock
*, u
->in_nfrags
);
1357 if (mode
!= O_RDONLY
) {
1358 char *name_buf
= NULL
;
1361 if ((u
->out_mmap
= mmap(NULL
, u
->out_hwbuf_size
, PROT_WRITE
, MAP_SHARED
, fd
, 0)) == MAP_FAILED
) {
1362 if (mode
== O_RDWR
) {
1363 pa_log_debug("mmap() failed for input. Changing to O_WRONLY mode.");
1367 pa_log_warn("mmap(PROT_WRITE) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno
));
1368 u
->use_mmap
= use_mmap
= false;
1372 pa_log_debug("Successfully mmap()ed output buffer.");
1373 pa_silence_memory(u
->out_mmap
, u
->out_hwbuf_size
, &ss
);
1377 if ((name
= pa_modargs_get_value(ma
, "sink_name", NULL
)))
1378 namereg_fail
= true;
1380 name
= name_buf
= pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev
));
1381 namereg_fail
= false;
1384 pa_sink_new_data_init(&sink_new_data
);
1385 sink_new_data
.driver
= __FILE__
;
1386 sink_new_data
.module
= m
;
1387 pa_sink_new_data_set_name(&sink_new_data
, name
);
1388 sink_new_data
.namereg_fail
= namereg_fail
;
1389 pa_sink_new_data_set_sample_spec(&sink_new_data
, &ss
);
1390 pa_sink_new_data_set_channel_map(&sink_new_data
, &map
);
1391 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_STRING
, dev
);
1392 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_API
, "oss");
1393 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, hwdesc
[0] ? hwdesc
: dev
);
1394 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_ACCESS_MODE
, use_mmap
? "mmap" : "serial");
1395 pa_proplist_setf(sink_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE
, "%lu", (unsigned long) (u
->out_hwbuf_size
));
1396 pa_proplist_setf(sink_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE
, "%lu", (unsigned long) (u
->out_fragment_size
));
1398 if (pa_modargs_get_proplist(ma
, "sink_properties", sink_new_data
.proplist
, PA_UPDATE_REPLACE
) < 0) {
1399 pa_log("Invalid properties");
1400 pa_sink_new_data_done(&sink_new_data
);
1405 u
->sink
= pa_sink_new(m
->core
, &sink_new_data
, PA_SINK_HARDWARE
|PA_SINK_LATENCY
);
1406 pa_sink_new_data_done(&sink_new_data
);
1410 pa_log("Failed to create sink object");
1414 u
->sink
->parent
.process_msg
= sink_process_msg
;
1415 u
->sink
->userdata
= u
;
1417 pa_sink_set_asyncmsgq(u
->sink
, u
->thread_mq
.inq
);
1418 pa_sink_set_rtpoll(u
->sink
, u
->rtpoll
);
1419 pa_sink_set_fixed_latency(u
->sink
, pa_bytes_to_usec(u
->out_hwbuf_size
, &u
->sink
->sample_spec
));
1420 u
->sink
->refresh_volume
= true;
1422 pa_sink_set_max_request(u
->sink
, u
->out_hwbuf_size
);
1425 u
->out_mmap_memblocks
= pa_xnew0(pa_memblock
*, u
->out_nfrags
);
1428 if ((u
->mixer_fd
= pa_oss_open_mixer_for_device(u
->device_name
)) >= 0) {
1429 bool do_close
= true;
1431 if (ioctl(fd
, SOUND_MIXER_READ_DEVMASK
, &u
->mixer_devmask
) < 0)
1432 pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno
));
1434 if (u
->sink
&& (u
->mixer_devmask
& (SOUND_MASK_VOLUME
|SOUND_MASK_PCM
))) {
1435 pa_log_debug("Found hardware mixer track for playback.");
1436 pa_sink_set_get_volume_callback(u
->sink
, sink_get_volume
);
1437 pa_sink_set_set_volume_callback(u
->sink
, sink_set_volume
);
1438 u
->sink
->n_volume_steps
= 101;
1442 if (u
->source
&& (u
->mixer_devmask
& (SOUND_MASK_RECLEV
|SOUND_MASK_IGAIN
))) {
1443 pa_log_debug("Found hardware mixer track for recording.");
1444 pa_source_set_get_volume_callback(u
->source
, source_get_volume
);
1445 pa_source_set_set_volume_callback(u
->source
, source_set_volume
);
1446 u
->source
->n_volume_steps
= 101;
1452 pa_close(u
->mixer_fd
);
1454 u
->mixer_devmask
= 0;
1460 pa_assert(u
->source
|| u
->sink
);
1462 pa_memchunk_reset(&u
->memchunk
);
1464 if (!(u
->thread
= pa_thread_new("oss", thread_func
, u
))) {
1465 pa_log("Failed to create thread.");
1469 /* Read mixer settings */
1471 if (sink_new_data
.volume_is_set
) {
1472 if (u
->sink
->set_volume
)
1473 u
->sink
->set_volume(u
->sink
);
1475 if (u
->sink
->get_volume
)
1476 u
->sink
->get_volume(u
->sink
);
1481 if (source_new_data
.volume_is_set
) {
1482 if (u
->source
->set_volume
)
1483 u
->source
->set_volume(u
->source
);
1485 if (u
->source
->get_volume
)
1486 u
->source
->get_volume(u
->source
);
1491 pa_sink_put(u
->sink
);
1493 pa_source_put(u
->source
);
1495 pa_modargs_free(ma
);
1507 pa_modargs_free(ma
);
1512 void pa__done(pa_module
*m
) {
1517 if (!(u
= m
->userdata
))
1521 pa_sink_unlink(u
->sink
);
1524 pa_source_unlink(u
->source
);
1527 pa_asyncmsgq_send(u
->thread_mq
.inq
, NULL
, PA_MESSAGE_SHUTDOWN
, NULL
, 0, NULL
);
1528 pa_thread_free(u
->thread
);
1531 pa_thread_mq_done(&u
->thread_mq
);
1534 pa_sink_unref(u
->sink
);
1537 pa_source_unref(u
->source
);
1539 if (u
->memchunk
.memblock
)
1540 pa_memblock_unref(u
->memchunk
.memblock
);
1543 pa_rtpoll_item_free(u
->rtpoll_item
);
1546 pa_rtpoll_free(u
->rtpoll
);
1548 if (u
->out_mmap_memblocks
) {
1550 for (i
= 0; i
< u
->out_nfrags
; i
++)
1551 if (u
->out_mmap_memblocks
[i
])
1552 pa_memblock_unref_fixed(u
->out_mmap_memblocks
[i
]);
1553 pa_xfree(u
->out_mmap_memblocks
);
1556 if (u
->in_mmap_memblocks
) {
1558 for (i
= 0; i
< u
->in_nfrags
; i
++)
1559 if (u
->in_mmap_memblocks
[i
])
1560 pa_memblock_unref_fixed(u
->in_mmap_memblocks
[i
]);
1561 pa_xfree(u
->in_mmap_memblocks
);
1564 if (u
->in_mmap
&& u
->in_mmap
!= MAP_FAILED
)
1565 munmap(u
->in_mmap
, u
->in_hwbuf_size
);
1567 if (u
->out_mmap
&& u
->out_mmap
!= MAP_FAILED
)
1568 munmap(u
->out_mmap
, u
->out_hwbuf_size
);
1573 if (u
->mixer_fd
>= 0)
1574 pa_close(u
->mixer_fd
);
1576 pa_xfree(u
->device_name
);