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 avilable 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>
58 #include <pulse/xmalloc.h>
59 #include <pulse/util.h>
61 #include <pulsecore/core-error.h>
62 #include <pulsecore/thread.h>
63 #include <pulsecore/sink.h>
64 #include <pulsecore/source.h>
65 #include <pulsecore/module.h>
66 #include <pulsecore/sample-util.h>
67 #include <pulsecore/core-util.h>
68 #include <pulsecore/modargs.h>
69 #include <pulsecore/log.h>
70 #include <pulsecore/macro.h>
71 #include <pulsecore/thread-mq.h>
72 #include <pulsecore/rtpoll.h>
74 #if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
75 #include <sys/audioio.h>
76 #include <sys/syscall.h>
80 #include "module-oss-symdef.h"
82 PA_MODULE_AUTHOR("Lennart Poettering");
83 PA_MODULE_DESCRIPTION("OSS Sink/Source");
84 PA_MODULE_VERSION(PACKAGE_VERSION
);
85 PA_MODULE_LOAD_ONCE(FALSE
);
87 "sink_name=<name for the sink> "
88 "sink_properties=<properties for the sink> "
89 "source_name=<name for the source> "
90 "source_properties=<properties for the source> "
91 "device=<OSS device> "
92 "record=<enable source?> "
93 "playback=<enable sink?> "
94 "format=<sample format> "
96 "channels=<number of channels> "
97 "channel_map=<channel map> "
98 "fragments=<number of fragments> "
99 "fragment_size=<fragment size> "
100 "mmap=<enable memory mapping?>");
102 PA_MODULE_DEPRECATED("Please use module-alsa-card instead of module-oss!");
105 #define DEFAULT_DEVICE "/dev/dsp"
114 pa_thread_mq thread_mq
;
119 pa_memchunk memchunk
;
122 uint32_t in_fragment_size
, out_fragment_size
, in_nfrags
, out_nfrags
, in_hwbuf_size
, out_hwbuf_size
;
123 pa_bool_t use_getospace
, use_getispace
;
124 pa_bool_t use_getodelay
;
126 pa_bool_t sink_suspended
, source_suspended
;
134 int nfrags
, frag_size
, orig_frag_size
;
137 unsigned out_mmap_current
, in_mmap_current
;
138 void *in_mmap
, *out_mmap
;
139 pa_memblock
**in_mmap_memblocks
, **out_mmap_memblocks
;
141 int in_mmap_saved_nfrags
, out_mmap_saved_nfrags
;
143 pa_rtpoll_item
*rtpoll_item
;
146 static const char* const valid_modargs
[] = {
164 static void trigger(struct userdata
*u
, pa_bool_t quick
) {
165 int enable_bits
= 0, zero
= 0;
172 pa_log_debug("trigger");
174 if (u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
))
175 enable_bits
|= PCM_ENABLE_INPUT
;
177 if (u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
))
178 enable_bits
|= PCM_ENABLE_OUTPUT
;
180 pa_log_debug("trigger: %i", enable_bits
);
186 ioctl(u
->fd
, SNDCTL_DSP_SETTRIGGER
, &zero
);
188 #ifdef SNDCTL_DSP_HALT
189 if (enable_bits
== 0)
190 if (ioctl(u
->fd
, SNDCTL_DSP_HALT
, NULL
) < 0)
191 pa_log_warn("SNDCTL_DSP_HALT: %s", pa_cstrerror(errno
));
194 if (ioctl(u
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable_bits
) < 0)
195 pa_log_warn("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno
));
197 if (u
->sink
&& !(enable_bits
& PCM_ENABLE_OUTPUT
)) {
198 pa_log_debug("clearing playback buffer");
199 pa_silence_memory(u
->out_mmap
, u
->out_hwbuf_size
, &u
->sink
->sample_spec
);
205 if (ioctl(u
->fd
, SNDCTL_DSP_POST
, NULL
) < 0)
206 pa_log_warn("SNDCTL_DSP_POST: %s", pa_cstrerror(errno
));
210 * Some crappy drivers do not start the recording until we
211 * read something. Without this snippet, poll will never
212 * register the fd as ready.
215 if (u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
)) {
216 uint8_t *buf
= pa_xnew(uint8_t, u
->in_fragment_size
);
217 pa_read(u
->fd
, buf
, u
->in_fragment_size
, NULL
);
224 static void mmap_fill_memblocks(struct userdata
*u
, unsigned n
) {
226 pa_assert(u
->out_mmap_memblocks
);
228 /* pa_log("Mmmap writing %u blocks", n); */
233 if (u
->out_mmap_memblocks
[u
->out_mmap_current
])
234 pa_memblock_unref_fixed(u
->out_mmap_memblocks
[u
->out_mmap_current
]);
236 chunk
.memblock
= u
->out_mmap_memblocks
[u
->out_mmap_current
] =
237 pa_memblock_new_fixed(
239 (uint8_t*) u
->out_mmap
+ u
->out_fragment_size
* u
->out_mmap_current
,
240 u
->out_fragment_size
,
243 chunk
.length
= pa_memblock_get_length(chunk
.memblock
);
246 pa_sink_render_into_full(u
->sink
, &chunk
);
248 u
->out_mmap_current
++;
249 while (u
->out_mmap_current
>= u
->out_nfrags
)
250 u
->out_mmap_current
-= u
->out_nfrags
;
256 static int mmap_write(struct userdata
*u
) {
257 struct count_info info
;
262 /* pa_log("Mmmap writing..."); */
264 if (ioctl(u
->fd
, SNDCTL_DSP_GETOPTR
, &info
) < 0) {
265 pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno
));
269 info
.blocks
+= u
->out_mmap_saved_nfrags
;
270 u
->out_mmap_saved_nfrags
= 0;
273 mmap_fill_memblocks(u
, (unsigned) info
.blocks
);
278 static void mmap_post_memblocks(struct userdata
*u
, unsigned n
) {
280 pa_assert(u
->in_mmap_memblocks
);
282 /* pa_log("Mmmap reading %u blocks", n); */
287 if (!u
->in_mmap_memblocks
[u
->in_mmap_current
]) {
289 chunk
.memblock
= u
->in_mmap_memblocks
[u
->in_mmap_current
] =
290 pa_memblock_new_fixed(
292 (uint8_t*) u
->in_mmap
+ u
->in_fragment_size
*u
->in_mmap_current
,
296 chunk
.length
= pa_memblock_get_length(chunk
.memblock
);
299 pa_source_post(u
->source
, &chunk
);
302 u
->in_mmap_current
++;
303 while (u
->in_mmap_current
>= u
->in_nfrags
)
304 u
->in_mmap_current
-= u
->in_nfrags
;
310 static void mmap_clear_memblocks(struct userdata
*u
, unsigned n
) {
311 unsigned i
= u
->in_mmap_current
;
314 pa_assert(u
->in_mmap_memblocks
);
316 if (n
> u
->in_nfrags
)
320 if (u
->in_mmap_memblocks
[i
]) {
321 pa_memblock_unref_fixed(u
->in_mmap_memblocks
[i
]);
322 u
->in_mmap_memblocks
[i
] = NULL
;
326 while (i
>= u
->in_nfrags
)
333 static int mmap_read(struct userdata
*u
) {
334 struct count_info info
;
336 pa_assert(u
->source
);
338 /* pa_log("Mmmap reading..."); */
340 if (ioctl(u
->fd
, SNDCTL_DSP_GETIPTR
, &info
) < 0) {
341 pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno
));
345 /* pa_log("... %i", info.blocks); */
347 info
.blocks
+= u
->in_mmap_saved_nfrags
;
348 u
->in_mmap_saved_nfrags
= 0;
350 if (info
.blocks
> 0) {
351 mmap_post_memblocks(u
, (unsigned) info
.blocks
);
352 mmap_clear_memblocks(u
, u
->in_nfrags
/2);
358 static pa_usec_t
mmap_sink_get_latency(struct userdata
*u
) {
359 struct count_info info
;
364 if (ioctl(u
->fd
, SNDCTL_DSP_GETOPTR
, &info
) < 0) {
365 pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno
));
369 u
->out_mmap_saved_nfrags
+= info
.blocks
;
371 bpos
= ((u
->out_mmap_current
+ (unsigned) u
->out_mmap_saved_nfrags
) * u
->out_fragment_size
) % u
->out_hwbuf_size
;
373 if (bpos
<= (size_t) info
.ptr
)
374 n
= u
->out_hwbuf_size
- ((size_t) info
.ptr
- bpos
);
376 n
= bpos
- (size_t) info
.ptr
;
378 /* 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); */
380 return pa_bytes_to_usec(n
, &u
->sink
->sample_spec
);
383 static pa_usec_t
mmap_source_get_latency(struct userdata
*u
) {
384 struct count_info info
;
389 if (ioctl(u
->fd
, SNDCTL_DSP_GETIPTR
, &info
) < 0) {
390 pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno
));
394 u
->in_mmap_saved_nfrags
+= info
.blocks
;
395 bpos
= ((u
->in_mmap_current
+ (unsigned) u
->in_mmap_saved_nfrags
) * u
->in_fragment_size
) % u
->in_hwbuf_size
;
397 if (bpos
<= (size_t) info
.ptr
)
398 n
= (size_t) info
.ptr
- bpos
;
400 n
= u
->in_hwbuf_size
- bpos
+ (size_t) info
.ptr
;
402 /* 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); */
404 return pa_bytes_to_usec(n
, &u
->source
->sample_spec
);
407 static pa_usec_t
io_sink_get_latency(struct userdata
*u
) {
412 if (u
->use_getodelay
) {
414 #if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
415 #if defined(AUDIO_GETBUFINFO)
416 struct audio_info info
;
417 if (syscall(SYS_ioctl
, u
->fd
, AUDIO_GETBUFINFO
, &info
) < 0) {
418 pa_log_info("Device doesn't support AUDIO_GETBUFINFO: %s", pa_cstrerror(errno
));
419 u
->use_getodelay
= 0;
421 arg
= info
.play
.seek
+ info
.blocksize
/ 2;
422 r
= pa_bytes_to_usec((size_t) arg
, &u
->sink
->sample_spec
);
425 pa_log_info("System doesn't support AUDIO_GETBUFINFO");
426 u
->use_getodelay
= 0;
429 if (ioctl(u
->fd
, SNDCTL_DSP_GETODELAY
, &arg
) < 0) {
430 pa_log_info("Device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno
));
431 u
->use_getodelay
= 0;
433 r
= pa_bytes_to_usec((size_t) arg
, &u
->sink
->sample_spec
);
437 if (!u
->use_getodelay
&& u
->use_getospace
) {
438 struct audio_buf_info info
;
440 if (ioctl(u
->fd
, SNDCTL_DSP_GETOSPACE
, &info
) < 0) {
441 pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno
));
442 u
->use_getospace
= 0;
444 r
= pa_bytes_to_usec((size_t) info
.bytes
, &u
->sink
->sample_spec
);
447 if (u
->memchunk
.memblock
)
448 r
+= pa_bytes_to_usec(u
->memchunk
.length
, &u
->sink
->sample_spec
);
453 static pa_usec_t
io_source_get_latency(struct userdata
*u
) {
458 if (u
->use_getispace
) {
459 struct audio_buf_info info
;
461 if (ioctl(u
->fd
, SNDCTL_DSP_GETISPACE
, &info
) < 0) {
462 pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno
));
463 u
->use_getispace
= 0;
465 r
= pa_bytes_to_usec((size_t) info
.bytes
, &u
->source
->sample_spec
);
471 static void build_pollfd(struct userdata
*u
) {
472 struct pollfd
*pollfd
;
475 pa_assert(u
->fd
>= 0);
478 pa_rtpoll_item_free(u
->rtpoll_item
);
480 u
->rtpoll_item
= pa_rtpoll_item_new(u
->rtpoll
, PA_RTPOLL_NEVER
, 1);
481 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
487 /* Called from IO context */
488 static int suspend(struct userdata
*u
) {
490 pa_assert(u
->fd
>= 0);
492 pa_log_info("Suspending...");
494 if (u
->out_mmap_memblocks
) {
496 for (i
= 0; i
< u
->out_nfrags
; i
++)
497 if (u
->out_mmap_memblocks
[i
]) {
498 pa_memblock_unref_fixed(u
->out_mmap_memblocks
[i
]);
499 u
->out_mmap_memblocks
[i
] = NULL
;
503 if (u
->in_mmap_memblocks
) {
505 for (i
= 0; i
< u
->in_nfrags
; i
++)
506 if (u
->in_mmap_memblocks
[i
]) {
507 pa_memblock_unref_fixed(u
->in_mmap_memblocks
[i
]);
508 u
->in_mmap_memblocks
[i
] = NULL
;
512 if (u
->in_mmap
&& u
->in_mmap
!= MAP_FAILED
) {
513 munmap(u
->in_mmap
, u
->in_hwbuf_size
);
517 if (u
->out_mmap
&& u
->out_mmap
!= MAP_FAILED
) {
518 munmap(u
->out_mmap
, u
->out_hwbuf_size
);
523 ioctl(u
->fd
, SNDCTL_DSP_SYNC
, NULL
);
527 if (u
->rtpoll_item
) {
528 pa_rtpoll_item_free(u
->rtpoll_item
);
529 u
->rtpoll_item
= NULL
;
532 pa_log_info("Device suspended...");
537 /* Called from IO context */
538 static int unsuspend(struct userdata
*u
) {
540 pa_sample_spec ss
, *ss_original
;
541 int frag_size
, in_frag_size
, out_frag_size
;
542 int in_nfrags
, out_nfrags
;
543 struct audio_buf_info info
;
546 pa_assert(u
->fd
< 0);
550 pa_log_info("Trying resume...");
552 if ((u
->fd
= pa_oss_open(u
->device_name
, &m
, NULL
)) < 0) {
553 pa_log_warn("Resume failed, device busy (%s)", pa_cstrerror(errno
));
558 pa_log_warn("Resume failed, couldn't open device with original access mode.");
562 if (u
->nfrags
>= 2 && u
->frag_size
>= 1)
563 if (pa_oss_set_fragments(u
->fd
, u
->nfrags
, u
->orig_frag_size
) < 0) {
564 pa_log_warn("Resume failed, couldn't set original fragment settings.");
568 ss
= *(ss_original
= u
->sink
? &u
->sink
->sample_spec
: &u
->source
->sample_spec
);
569 if (pa_oss_auto_format(u
->fd
, &ss
) < 0 || !pa_sample_spec_equal(&ss
, ss_original
)) {
570 pa_log_warn("Resume failed, couldn't set original sample format settings.");
574 if (ioctl(u
->fd
, SNDCTL_DSP_GETBLKSIZE
, &frag_size
) < 0) {
575 pa_log_warn("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno
));
579 in_frag_size
= out_frag_size
= frag_size
;
580 in_nfrags
= out_nfrags
= u
->nfrags
;
582 if (ioctl(u
->fd
, SNDCTL_DSP_GETISPACE
, &info
) >= 0) {
583 in_frag_size
= info
.fragsize
;
584 in_nfrags
= info
.fragstotal
;
587 if (ioctl(u
->fd
, SNDCTL_DSP_GETOSPACE
, &info
) >= 0) {
588 out_frag_size
= info
.fragsize
;
589 out_nfrags
= info
.fragstotal
;
592 if ((u
->source
&& (in_frag_size
!= (int) u
->in_fragment_size
|| in_nfrags
!= (int) u
->in_nfrags
)) ||
593 (u
->sink
&& (out_frag_size
!= (int) u
->out_fragment_size
|| out_nfrags
!= (int) u
->out_nfrags
))) {
594 pa_log_warn("Resume failed, input fragment settings don't match.");
600 if ((u
->in_mmap
= mmap(NULL
, u
->in_hwbuf_size
, PROT_READ
, MAP_SHARED
, u
->fd
, 0)) == MAP_FAILED
) {
601 pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno
));
607 if ((u
->out_mmap
= mmap(NULL
, u
->out_hwbuf_size
, PROT_WRITE
, MAP_SHARED
, u
->fd
, 0)) == MAP_FAILED
) {
608 pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno
));
609 if (u
->in_mmap
&& u
->in_mmap
!= MAP_FAILED
) {
610 munmap(u
->in_mmap
, u
->in_hwbuf_size
);
617 pa_silence_memory(u
->out_mmap
, u
->out_hwbuf_size
, &ss
);
621 u
->out_mmap_current
= u
->in_mmap_current
= 0;
622 u
->out_mmap_saved_nfrags
= u
->in_mmap_saved_nfrags
= 0;
624 pa_assert(!u
->rtpoll_item
);
628 if (u
->sink
&& u
->sink
->get_volume
)
629 u
->sink
->get_volume(u
->sink
);
630 if (u
->source
&& u
->source
->get_volume
)
631 u
->source
->get_volume(u
->source
);
633 pa_log_info("Resumed successfully...");
643 /* Called from IO context */
644 static int sink_process_msg(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
645 struct userdata
*u
= PA_SINK(o
)->userdata
;
647 pa_bool_t do_trigger
= FALSE
, quick
= TRUE
;
651 case PA_SINK_MESSAGE_GET_LATENCY
: {
656 r
= mmap_sink_get_latency(u
);
658 r
= io_sink_get_latency(u
);
661 *((pa_usec_t
*) data
) = r
;
666 case PA_SINK_MESSAGE_SET_STATE
:
668 switch ((pa_sink_state_t
) PA_PTR_TO_UINT(data
)) {
670 case PA_SINK_SUSPENDED
:
671 pa_assert(PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
));
673 if (!u
->source
|| u
->source_suspended
) {
680 u
->sink_suspended
= TRUE
;
684 case PA_SINK_RUNNING
:
686 if (u
->sink
->thread_info
.state
== PA_SINK_INIT
) {
688 quick
= u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
);
691 if (u
->sink
->thread_info
.state
== PA_SINK_SUSPENDED
) {
693 if (!u
->source
|| u
->source_suspended
) {
694 if (unsuspend(u
) < 0)
701 u
->out_mmap_current
= 0;
702 u
->out_mmap_saved_nfrags
= 0;
704 u
->sink_suspended
= FALSE
;
709 case PA_SINK_INVALID_STATE
:
710 case PA_SINK_UNLINKED
:
719 ret
= pa_sink_process_msg(o
, code
, data
, offset
, chunk
);
727 static int source_process_msg(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
728 struct userdata
*u
= PA_SOURCE(o
)->userdata
;
730 int do_trigger
= FALSE
, quick
= TRUE
;
734 case PA_SOURCE_MESSAGE_GET_LATENCY
: {
739 r
= mmap_source_get_latency(u
);
741 r
= io_source_get_latency(u
);
744 *((pa_usec_t
*) data
) = r
;
748 case PA_SOURCE_MESSAGE_SET_STATE
:
750 switch ((pa_source_state_t
) PA_PTR_TO_UINT(data
)) {
751 case PA_SOURCE_SUSPENDED
:
752 pa_assert(PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
));
754 if (!u
->sink
|| u
->sink_suspended
) {
761 u
->source_suspended
= TRUE
;
765 case PA_SOURCE_RUNNING
:
767 if (u
->source
->thread_info
.state
== PA_SOURCE_INIT
) {
769 quick
= u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
);
772 if (u
->source
->thread_info
.state
== PA_SOURCE_SUSPENDED
) {
774 if (!u
->sink
|| u
->sink_suspended
) {
775 if (unsuspend(u
) < 0)
782 u
->in_mmap_current
= 0;
783 u
->in_mmap_saved_nfrags
= 0;
785 u
->source_suspended
= FALSE
;
789 case PA_SOURCE_UNLINKED
:
791 case PA_SOURCE_INVALID_STATE
:
799 ret
= pa_source_process_msg(o
, code
, data
, offset
, chunk
);
807 static void sink_get_volume(pa_sink
*s
) {
810 pa_assert_se(u
= s
->userdata
);
812 pa_assert(u
->mixer_devmask
& (SOUND_MASK_VOLUME
|SOUND_MASK_PCM
));
814 if (u
->mixer_devmask
& SOUND_MASK_VOLUME
)
815 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_READ_VOLUME
, &s
->sample_spec
, &s
->virtual_volume
) >= 0)
818 if (u
->mixer_devmask
& SOUND_MASK_PCM
)
819 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_READ_PCM
, &s
->sample_spec
, &s
->virtual_volume
) >= 0)
822 pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno
));
825 static void sink_set_volume(pa_sink
*s
) {
828 pa_assert_se(u
= s
->userdata
);
830 pa_assert(u
->mixer_devmask
& (SOUND_MASK_VOLUME
|SOUND_MASK_PCM
));
832 if (u
->mixer_devmask
& SOUND_MASK_VOLUME
)
833 if (pa_oss_set_volume(u
->mixer_fd
, SOUND_MIXER_WRITE_VOLUME
, &s
->sample_spec
, &s
->virtual_volume
) >= 0)
836 if (u
->mixer_devmask
& SOUND_MASK_PCM
)
837 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_WRITE_PCM
, &s
->sample_spec
, &s
->virtual_volume
) >= 0)
840 pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno
));
843 static void source_get_volume(pa_source
*s
) {
846 pa_assert_se(u
= s
->userdata
);
848 pa_assert(u
->mixer_devmask
& (SOUND_MASK_IGAIN
|SOUND_MASK_RECLEV
));
850 if (u
->mixer_devmask
& SOUND_MASK_IGAIN
)
851 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_READ_IGAIN
, &s
->sample_spec
, &s
->virtual_volume
) >= 0)
854 if (u
->mixer_devmask
& SOUND_MASK_RECLEV
)
855 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_READ_RECLEV
, &s
->sample_spec
, &s
->virtual_volume
) >= 0)
858 pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno
));
861 static void source_set_volume(pa_source
*s
) {
864 pa_assert_se(u
= s
->userdata
);
866 pa_assert(u
->mixer_devmask
& (SOUND_MASK_IGAIN
|SOUND_MASK_RECLEV
));
868 if (u
->mixer_devmask
& SOUND_MASK_IGAIN
)
869 if (pa_oss_set_volume(u
->mixer_fd
, SOUND_MIXER_WRITE_IGAIN
, &s
->sample_spec
, &s
->virtual_volume
) >= 0)
872 if (u
->mixer_devmask
& SOUND_MASK_RECLEV
)
873 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_WRITE_RECLEV
, &s
->sample_spec
, &s
->virtual_volume
) >= 0)
876 pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno
));
879 static void thread_func(void *userdata
) {
880 struct userdata
*u
= userdata
;
881 int write_type
= 0, read_type
= 0;
886 pa_log_debug("Thread starting up");
888 if (u
->core
->realtime_scheduling
)
889 pa_make_realtime(u
->core
->realtime_priority
);
891 pa_thread_mq_install(&u
->thread_mq
);
892 pa_rtpoll_install(u
->rtpoll
);
897 /* pa_log("loop"); */
899 if (u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
))
900 if (u
->sink
->thread_info
.rewind_requested
)
901 pa_sink_process_rewind(u
->sink
, 0);
903 /* Render some data and write it to the dsp */
905 if (u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
) && ((revents
& POLLOUT
) || u
->use_mmap
|| u
->use_getospace
)) {
909 if ((ret
= mmap_write(u
)) < 0)
919 pa_bool_t loop
= FALSE
, work_done
= FALSE
;
921 l
= (ssize_t
) u
->out_fragment_size
;
923 if (u
->use_getospace
) {
926 if (ioctl(u
->fd
, SNDCTL_DSP_GETOSPACE
, &info
) < 0) {
927 pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno
));
928 u
->use_getospace
= FALSE
;
932 /* We loop only if GETOSPACE worked and we
933 * actually *know* that we can write more than
934 * one fragment at a time */
939 /* Round down to multiples of the fragment size,
940 * because OSS needs that (at least some versions
942 l
= (l
/(ssize_t
) u
->out_fragment_size
) * (ssize_t
) u
->out_fragment_size
;
944 /* Hmm, so poll() signalled us that we can read
945 * something, but GETOSPACE told us there was nothing?
946 * Hmm, make the best of it, try to read some data, to
947 * avoid spinning forever. */
948 if (l
<= 0 && (revents
& POLLOUT
)) {
949 l
= (ssize_t
) u
->out_fragment_size
;
957 if (u
->memchunk
.length
<= 0)
958 pa_sink_render(u
->sink
, (size_t) l
, &u
->memchunk
);
960 pa_assert(u
->memchunk
.length
> 0);
962 p
= pa_memblock_acquire(u
->memchunk
.memblock
);
963 t
= pa_write(u
->fd
, (uint8_t*) p
+ u
->memchunk
.index
, u
->memchunk
.length
, &write_type
);
964 pa_memblock_release(u
->memchunk
.memblock
);
966 /* pa_log("wrote %i bytes of %u", t, l); */
975 else if (errno
== EAGAIN
) {
976 pa_log_debug("EAGAIN");
982 pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno
));
988 u
->memchunk
.index
+= (size_t) t
;
989 u
->memchunk
.length
-= (size_t) t
;
991 if (u
->memchunk
.length
<= 0) {
992 pa_memblock_unref(u
->memchunk
.memblock
);
993 pa_memchunk_reset(&u
->memchunk
);
1011 /* Try to read some data and pass it on to the source driver. */
1013 if (u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
) && ((revents
& POLLIN
) || u
->use_mmap
|| u
->use_getispace
)) {
1017 if ((ret
= mmap_read(u
)) < 0)
1029 pa_memchunk memchunk
;
1030 pa_bool_t loop
= FALSE
, work_done
= FALSE
;
1032 l
= (ssize_t
) u
->in_fragment_size
;
1034 if (u
->use_getispace
) {
1035 audio_buf_info info
;
1037 if (ioctl(u
->fd
, SNDCTL_DSP_GETISPACE
, &info
) < 0) {
1038 pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno
));
1039 u
->use_getispace
= FALSE
;
1046 l
= (l
/(ssize_t
) u
->in_fragment_size
) * (ssize_t
) u
->in_fragment_size
;
1048 if (l
<= 0 && (revents
& POLLIN
)) {
1049 l
= (ssize_t
) u
->in_fragment_size
;
1059 memchunk
.memblock
= pa_memblock_new(u
->core
->mempool
, (size_t) -1);
1061 k
= pa_memblock_get_length(memchunk
.memblock
);
1066 k
= (k
/u
->frame_size
)*u
->frame_size
;
1068 p
= pa_memblock_acquire(memchunk
.memblock
);
1069 t
= pa_read(u
->fd
, p
, k
, &read_type
);
1070 pa_memblock_release(memchunk
.memblock
);
1072 pa_assert(t
!= 0); /* EOF cannot happen */
1074 /* pa_log("read %i bytes of %u", t, l); */
1077 pa_memblock_unref(memchunk
.memblock
);
1082 else if (errno
== EAGAIN
) {
1083 pa_log_debug("EAGAIN");
1089 pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno
));
1095 memchunk
.length
= (size_t) t
;
1097 pa_source_post(u
->source
, &memchunk
);
1098 pa_memblock_unref(memchunk
.memblock
);
1115 /* pa_log("loop2 revents=%i", revents); */
1117 if (u
->rtpoll_item
) {
1118 struct pollfd
*pollfd
;
1120 pa_assert(u
->fd
>= 0);
1122 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
1123 pollfd
->events
= (short)
1124 (((u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
)) ? POLLIN
: 0) |
1125 ((u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
)) ? POLLOUT
: 0));
1128 /* Hmm, nothing to do. Let's sleep */
1129 if ((ret
= pa_rtpoll_run(u
->rtpoll
, TRUE
)) < 0)
1135 if (u
->rtpoll_item
) {
1136 struct pollfd
*pollfd
;
1138 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
1140 if (pollfd
->revents
& ~(POLLOUT
|POLLIN
)) {
1141 pa_log("DSP shutdown.");
1145 revents
= pollfd
->revents
;
1151 /* If this was no regular exit from the loop we have to continue
1152 * processing messages until we received PA_MESSAGE_SHUTDOWN */
1153 pa_asyncmsgq_post(u
->thread_mq
.outq
, PA_MSGOBJECT(u
->core
), PA_CORE_MESSAGE_UNLOAD_MODULE
, u
->module
, 0, NULL
, NULL
);
1154 pa_asyncmsgq_wait_for(u
->thread_mq
.inq
, PA_MESSAGE_SHUTDOWN
);
1157 pa_log_debug("Thread shutting down");
1160 int pa__init(pa_module
*m
) {
1162 struct audio_buf_info info
;
1163 struct userdata
*u
= NULL
;
1166 int nfrags
, orig_frag_size
, frag_size
;
1168 pa_bool_t record
= TRUE
, playback
= TRUE
, use_mmap
= TRUE
;
1171 pa_modargs
*ma
= NULL
;
1174 pa_bool_t namereg_fail
;
1175 pa_sink_new_data sink_new_data
;
1176 pa_source_new_data source_new_data
;
1180 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1181 pa_log("Failed to parse module arguments.");
1185 if (pa_modargs_get_value_boolean(ma
, "record", &record
) < 0 || pa_modargs_get_value_boolean(ma
, "playback", &playback
) < 0) {
1186 pa_log("record= and playback= expect boolean argument.");
1190 if (!playback
&& !record
) {
1191 pa_log("Neither playback nor record enabled for device.");
1195 mode
= (playback
&& record
) ? O_RDWR
: (playback
? O_WRONLY
: (record
? O_RDONLY
: 0));
1197 ss
= m
->core
->default_sample_spec
;
1198 map
= m
->core
->default_channel_map
;
1199 if (pa_modargs_get_sample_spec_and_channel_map(ma
, &ss
, &map
, PA_CHANNEL_MAP_OSS
) < 0) {
1200 pa_log("Failed to parse sample specification or channel map");
1204 nfrags
= (int) m
->core
->default_n_fragments
;
1205 frag_size
= (int) pa_usec_to_bytes(m
->core
->default_fragment_size_msec
*1000, &ss
);
1207 frag_size
= (int) pa_frame_size(&ss
);
1209 if (pa_modargs_get_value_s32(ma
, "fragments", &nfrags
) < 0 || pa_modargs_get_value_s32(ma
, "fragment_size", &frag_size
) < 0) {
1210 pa_log("Failed to parse fragments arguments");
1214 if (pa_modargs_get_value_boolean(ma
, "mmap", &use_mmap
) < 0) {
1215 pa_log("Failed to parse mmap argument.");
1219 if ((fd
= pa_oss_open(dev
= pa_modargs_get_value(ma
, "device", DEFAULT_DEVICE
), &mode
, &caps
)) < 0)
1222 if (use_mmap
&& (!(caps
& DSP_CAP_MMAP
) || !(caps
& DSP_CAP_TRIGGER
))) {
1223 pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode.");
1227 if (use_mmap
&& mode
== O_WRONLY
) {
1228 pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode.");
1232 if (pa_oss_get_hw_description(dev
, hwdesc
, sizeof(hwdesc
)) >= 0)
1233 pa_log_info("Hardware name is '%s'.", hwdesc
);
1237 pa_log_info("Device opened in %s mode.", mode
== O_WRONLY
? "O_WRONLY" : (mode
== O_RDONLY
? "O_RDONLY" : "O_RDWR"));
1239 orig_frag_size
= frag_size
;
1240 if (nfrags
>= 2 && frag_size
>= 1)
1241 if (pa_oss_set_fragments(fd
, nfrags
, frag_size
) < 0)
1244 if (pa_oss_auto_format(fd
, &ss
) < 0)
1247 if (ioctl(fd
, SNDCTL_DSP_GETBLKSIZE
, &frag_size
) < 0) {
1248 pa_log("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno
));
1251 pa_assert(frag_size
> 0);
1253 u
= pa_xnew0(struct userdata
, 1);
1259 u
->mixer_devmask
= 0;
1260 u
->use_getospace
= u
->use_getispace
= TRUE
;
1261 u
->use_getodelay
= TRUE
;
1263 u
->frame_size
= pa_frame_size(&ss
);
1264 u
->device_name
= pa_xstrdup(dev
);
1265 u
->in_nfrags
= u
->out_nfrags
= (uint32_t) (u
->nfrags
= nfrags
);
1266 u
->out_fragment_size
= u
->in_fragment_size
= (uint32_t) (u
->frag_size
= frag_size
);
1267 u
->orig_frag_size
= orig_frag_size
;
1268 u
->use_mmap
= use_mmap
;
1269 u
->rtpoll
= pa_rtpoll_new();
1270 pa_thread_mq_init(&u
->thread_mq
, m
->core
->mainloop
, u
->rtpoll
);
1271 u
->rtpoll_item
= NULL
;
1274 if (ioctl(fd
, SNDCTL_DSP_GETISPACE
, &info
) >= 0) {
1275 pa_log_info("Input -- %u fragments of size %u.", info
.fragstotal
, info
.fragsize
);
1276 u
->in_fragment_size
= (uint32_t) info
.fragsize
;
1277 u
->in_nfrags
= (uint32_t) info
.fragstotal
;
1278 u
->use_getispace
= TRUE
;
1281 if (ioctl(fd
, SNDCTL_DSP_GETOSPACE
, &info
) >= 0) {
1282 pa_log_info("Output -- %u fragments of size %u.", info
.fragstotal
, info
.fragsize
);
1283 u
->out_fragment_size
= (uint32_t) info
.fragsize
;
1284 u
->out_nfrags
= (uint32_t) info
.fragstotal
;
1285 u
->use_getospace
= TRUE
;
1288 u
->in_hwbuf_size
= u
->in_nfrags
* u
->in_fragment_size
;
1289 u
->out_hwbuf_size
= u
->out_nfrags
* u
->out_fragment_size
;
1291 if (mode
!= O_WRONLY
) {
1292 char *name_buf
= NULL
;
1295 if ((u
->in_mmap
= mmap(NULL
, u
->in_hwbuf_size
, PROT_READ
, MAP_SHARED
, fd
, 0)) == MAP_FAILED
) {
1296 pa_log_warn("mmap(PROT_READ) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno
));
1297 use_mmap
= u
->use_mmap
= FALSE
;
1300 pa_log_debug("Successfully mmap()ed input buffer.");
1303 if ((name
= pa_modargs_get_value(ma
, "source_name", NULL
)))
1304 namereg_fail
= TRUE
;
1306 name
= name_buf
= pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev
));
1307 namereg_fail
= FALSE
;
1310 pa_source_new_data_init(&source_new_data
);
1311 source_new_data
.driver
= __FILE__
;
1312 source_new_data
.module
= m
;
1313 pa_source_new_data_set_name(&source_new_data
, name
);
1314 source_new_data
.namereg_fail
= namereg_fail
;
1315 pa_source_new_data_set_sample_spec(&source_new_data
, &ss
);
1316 pa_source_new_data_set_channel_map(&source_new_data
, &map
);
1317 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_STRING
, dev
);
1318 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_API
, "oss");
1319 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, hwdesc
[0] ? hwdesc
: dev
);
1320 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_ACCESS_MODE
, use_mmap
? "mmap" : "serial");
1321 pa_proplist_setf(source_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE
, "%lu", (unsigned long) (u
->in_hwbuf_size
));
1322 pa_proplist_setf(source_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE
, "%lu", (unsigned long) (u
->in_fragment_size
));
1324 if (pa_modargs_get_proplist(ma
, "source_properties", source_new_data
.proplist
, PA_UPDATE_REPLACE
) < 0) {
1325 pa_log("Invalid properties");
1326 pa_source_new_data_done(&source_new_data
);
1330 u
->source
= pa_source_new(m
->core
, &source_new_data
, PA_SOURCE_HARDWARE
|PA_SOURCE_LATENCY
);
1331 pa_source_new_data_done(&source_new_data
);
1335 pa_log("Failed to create source object");
1339 u
->source
->parent
.process_msg
= source_process_msg
;
1340 u
->source
->userdata
= u
;
1342 pa_source_set_asyncmsgq(u
->source
, u
->thread_mq
.inq
);
1343 pa_source_set_rtpoll(u
->source
, u
->rtpoll
);
1344 pa_source_set_fixed_latency(u
->source
, pa_bytes_to_usec(u
->in_hwbuf_size
, &u
->source
->sample_spec
));
1345 u
->source
->refresh_volume
= TRUE
;
1348 u
->in_mmap_memblocks
= pa_xnew0(pa_memblock
*, u
->in_nfrags
);
1351 if (mode
!= O_RDONLY
) {
1352 char *name_buf
= NULL
;
1355 if ((u
->out_mmap
= mmap(NULL
, u
->out_hwbuf_size
, PROT_WRITE
, MAP_SHARED
, fd
, 0)) == MAP_FAILED
) {
1356 if (mode
== O_RDWR
) {
1357 pa_log_debug("mmap() failed for input. Changing to O_WRONLY mode.");
1361 pa_log_warn("mmap(PROT_WRITE) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno
));
1362 u
->use_mmap
= use_mmap
= FALSE
;
1366 pa_log_debug("Successfully mmap()ed output buffer.");
1367 pa_silence_memory(u
->out_mmap
, u
->out_hwbuf_size
, &ss
);
1371 if ((name
= pa_modargs_get_value(ma
, "sink_name", NULL
)))
1372 namereg_fail
= TRUE
;
1374 name
= name_buf
= pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev
));
1375 namereg_fail
= FALSE
;
1378 pa_sink_new_data_init(&sink_new_data
);
1379 sink_new_data
.driver
= __FILE__
;
1380 sink_new_data
.module
= m
;
1381 pa_sink_new_data_set_name(&sink_new_data
, name
);
1382 sink_new_data
.namereg_fail
= namereg_fail
;
1383 pa_sink_new_data_set_sample_spec(&sink_new_data
, &ss
);
1384 pa_sink_new_data_set_channel_map(&sink_new_data
, &map
);
1385 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_STRING
, dev
);
1386 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_API
, "oss");
1387 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, hwdesc
[0] ? hwdesc
: dev
);
1388 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_ACCESS_MODE
, use_mmap
? "mmap" : "serial");
1389 pa_proplist_setf(sink_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE
, "%lu", (unsigned long) (u
->out_hwbuf_size
));
1390 pa_proplist_setf(sink_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE
, "%lu", (unsigned long) (u
->out_fragment_size
));
1392 if (pa_modargs_get_proplist(ma
, "sink_properties", sink_new_data
.proplist
, PA_UPDATE_REPLACE
) < 0) {
1393 pa_log("Invalid properties");
1394 pa_sink_new_data_done(&sink_new_data
);
1398 u
->sink
= pa_sink_new(m
->core
, &sink_new_data
, PA_SINK_HARDWARE
|PA_SINK_LATENCY
);
1399 pa_sink_new_data_done(&sink_new_data
);
1403 pa_log("Failed to create sink object");
1407 u
->sink
->parent
.process_msg
= sink_process_msg
;
1408 u
->sink
->userdata
= u
;
1410 pa_sink_set_asyncmsgq(u
->sink
, u
->thread_mq
.inq
);
1411 pa_sink_set_rtpoll(u
->sink
, u
->rtpoll
);
1412 pa_sink_set_fixed_latency(u
->sink
, pa_bytes_to_usec(u
->out_hwbuf_size
, &u
->sink
->sample_spec
));
1413 u
->sink
->refresh_volume
= TRUE
;
1415 pa_sink_set_max_request(u
->sink
, u
->out_hwbuf_size
);
1418 u
->out_mmap_memblocks
= pa_xnew0(pa_memblock
*, u
->out_nfrags
);
1421 if ((u
->mixer_fd
= pa_oss_open_mixer_for_device(u
->device_name
)) >= 0) {
1422 pa_bool_t do_close
= TRUE
;
1424 if (ioctl(fd
, SOUND_MIXER_READ_DEVMASK
, &u
->mixer_devmask
) < 0)
1425 pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno
));
1428 if (u
->sink
&& (u
->mixer_devmask
& (SOUND_MASK_VOLUME
|SOUND_MASK_PCM
))) {
1429 pa_log_debug("Found hardware mixer track for playback.");
1430 u
->sink
->flags
|= PA_SINK_HW_VOLUME_CTRL
;
1431 u
->sink
->get_volume
= sink_get_volume
;
1432 u
->sink
->set_volume
= sink_set_volume
;
1433 u
->sink
->n_volume_steps
= 101;
1437 if (u
->source
&& (u
->mixer_devmask
& (SOUND_MASK_RECLEV
|SOUND_MASK_IGAIN
))) {
1438 pa_log_debug("Found hardware mixer track for recording.");
1439 u
->source
->flags
|= PA_SOURCE_HW_VOLUME_CTRL
;
1440 u
->source
->get_volume
= source_get_volume
;
1441 u
->source
->set_volume
= source_set_volume
;
1442 u
->source
->n_volume_steps
= 101;
1448 pa_close(u
->mixer_fd
);
1450 u
->mixer_devmask
= 0;
1456 pa_assert(u
->source
|| u
->sink
);
1458 pa_memchunk_reset(&u
->memchunk
);
1460 if (!(u
->thread
= pa_thread_new(thread_func
, u
))) {
1461 pa_log("Failed to create thread.");
1465 /* Read mixer settings */
1467 if (sink_new_data
.volume_is_set
) {
1468 if (u
->sink
->set_volume
)
1469 u
->sink
->set_volume(u
->sink
);
1471 if (u
->sink
->get_volume
)
1472 u
->sink
->get_volume(u
->sink
);
1477 if (source_new_data
.volume_is_set
) {
1478 if (u
->source
->set_volume
)
1479 u
->source
->set_volume(u
->source
);
1481 if (u
->source
->get_volume
)
1482 u
->source
->get_volume(u
->source
);
1487 pa_sink_put(u
->sink
);
1489 pa_source_put(u
->source
);
1491 pa_modargs_free(ma
);
1503 pa_modargs_free(ma
);
1508 void pa__done(pa_module
*m
) {
1513 if (!(u
= m
->userdata
))
1517 pa_sink_unlink(u
->sink
);
1520 pa_source_unlink(u
->source
);
1523 pa_asyncmsgq_send(u
->thread_mq
.inq
, NULL
, PA_MESSAGE_SHUTDOWN
, NULL
, 0, NULL
);
1524 pa_thread_free(u
->thread
);
1527 pa_thread_mq_done(&u
->thread_mq
);
1530 pa_sink_unref(u
->sink
);
1533 pa_source_unref(u
->source
);
1535 if (u
->memchunk
.memblock
)
1536 pa_memblock_unref(u
->memchunk
.memblock
);
1539 pa_rtpoll_item_free(u
->rtpoll_item
);
1542 pa_rtpoll_free(u
->rtpoll
);
1544 if (u
->out_mmap_memblocks
) {
1546 for (i
= 0; i
< u
->out_nfrags
; i
++)
1547 if (u
->out_mmap_memblocks
[i
])
1548 pa_memblock_unref_fixed(u
->out_mmap_memblocks
[i
]);
1549 pa_xfree(u
->out_mmap_memblocks
);
1552 if (u
->in_mmap_memblocks
) {
1554 for (i
= 0; i
< u
->in_nfrags
; i
++)
1555 if (u
->in_mmap_memblocks
[i
])
1556 pa_memblock_unref_fixed(u
->in_mmap_memblocks
[i
]);
1557 pa_xfree(u
->in_mmap_memblocks
);
1560 if (u
->in_mmap
&& u
->in_mmap
!= MAP_FAILED
)
1561 munmap(u
->in_mmap
, u
->in_hwbuf_size
);
1563 if (u
->out_mmap
&& u
->out_mmap
!= MAP_FAILED
)
1564 munmap(u
->out_mmap
, u
->out_hwbuf_size
);
1569 if (u
->mixer_fd
>= 0)
1570 pa_close(u
->mixer_fd
);
1572 pa_xfree(u
->device_name
);