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>
57 #include <pulse/xmalloc.h>
58 #include <pulse/util.h>
60 #include <pulsecore/core-error.h>
61 #include <pulsecore/thread.h>
62 #include <pulsecore/sink.h>
63 #include <pulsecore/source.h>
64 #include <pulsecore/module.h>
65 #include <pulsecore/sample-util.h>
66 #include <pulsecore/core-util.h>
67 #include <pulsecore/modargs.h>
68 #include <pulsecore/log.h>
69 #include <pulsecore/macro.h>
70 #include <pulsecore/thread-mq.h>
71 #include <pulsecore/rtpoll.h>
72 #include <pulsecore/poll.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
->real_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
->real_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
->real_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
->real_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
->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
->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
->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
->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
);
896 /* pa_log("loop"); */
898 if (u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
))
899 if (u
->sink
->thread_info
.rewind_requested
)
900 pa_sink_process_rewind(u
->sink
, 0);
902 /* Render some data and write it to the dsp */
904 if (u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
) && ((revents
& POLLOUT
) || u
->use_mmap
|| u
->use_getospace
)) {
908 if ((ret
= mmap_write(u
)) < 0)
918 pa_bool_t loop
= FALSE
, work_done
= FALSE
;
920 l
= (ssize_t
) u
->out_fragment_size
;
922 if (u
->use_getospace
) {
925 if (ioctl(u
->fd
, SNDCTL_DSP_GETOSPACE
, &info
) < 0) {
926 pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno
));
927 u
->use_getospace
= FALSE
;
931 /* We loop only if GETOSPACE worked and we
932 * actually *know* that we can write more than
933 * one fragment at a time */
938 /* Round down to multiples of the fragment size,
939 * because OSS needs that (at least some versions
941 l
= (l
/(ssize_t
) u
->out_fragment_size
) * (ssize_t
) u
->out_fragment_size
;
943 /* Hmm, so poll() signalled us that we can read
944 * something, but GETOSPACE told us there was nothing?
945 * Hmm, make the best of it, try to read some data, to
946 * avoid spinning forever. */
947 if (l
<= 0 && (revents
& POLLOUT
)) {
948 l
= (ssize_t
) u
->out_fragment_size
;
956 if (u
->memchunk
.length
<= 0)
957 pa_sink_render(u
->sink
, (size_t) l
, &u
->memchunk
);
959 pa_assert(u
->memchunk
.length
> 0);
961 p
= pa_memblock_acquire(u
->memchunk
.memblock
);
962 t
= pa_write(u
->fd
, (uint8_t*) p
+ u
->memchunk
.index
, u
->memchunk
.length
, &write_type
);
963 pa_memblock_release(u
->memchunk
.memblock
);
965 /* pa_log("wrote %i bytes of %u", t, l); */
974 else if (errno
== EAGAIN
) {
975 pa_log_debug("EAGAIN");
981 pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno
));
987 u
->memchunk
.index
+= (size_t) t
;
988 u
->memchunk
.length
-= (size_t) t
;
990 if (u
->memchunk
.length
<= 0) {
991 pa_memblock_unref(u
->memchunk
.memblock
);
992 pa_memchunk_reset(&u
->memchunk
);
1010 /* Try to read some data and pass it on to the source driver. */
1012 if (u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
) && ((revents
& POLLIN
) || u
->use_mmap
|| u
->use_getispace
)) {
1016 if ((ret
= mmap_read(u
)) < 0)
1028 pa_memchunk memchunk
;
1029 pa_bool_t loop
= FALSE
, work_done
= FALSE
;
1031 l
= (ssize_t
) u
->in_fragment_size
;
1033 if (u
->use_getispace
) {
1034 audio_buf_info info
;
1036 if (ioctl(u
->fd
, SNDCTL_DSP_GETISPACE
, &info
) < 0) {
1037 pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno
));
1038 u
->use_getispace
= FALSE
;
1045 l
= (l
/(ssize_t
) u
->in_fragment_size
) * (ssize_t
) u
->in_fragment_size
;
1047 if (l
<= 0 && (revents
& POLLIN
)) {
1048 l
= (ssize_t
) u
->in_fragment_size
;
1058 memchunk
.memblock
= pa_memblock_new(u
->core
->mempool
, (size_t) -1);
1060 k
= pa_memblock_get_length(memchunk
.memblock
);
1065 k
= (k
/u
->frame_size
)*u
->frame_size
;
1067 p
= pa_memblock_acquire(memchunk
.memblock
);
1068 t
= pa_read(u
->fd
, p
, k
, &read_type
);
1069 pa_memblock_release(memchunk
.memblock
);
1071 pa_assert(t
!= 0); /* EOF cannot happen */
1073 /* pa_log("read %i bytes of %u", t, l); */
1076 pa_memblock_unref(memchunk
.memblock
);
1081 else if (errno
== EAGAIN
) {
1082 pa_log_debug("EAGAIN");
1088 pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno
));
1094 memchunk
.length
= (size_t) t
;
1096 pa_source_post(u
->source
, &memchunk
);
1097 pa_memblock_unref(memchunk
.memblock
);
1114 /* pa_log("loop2 revents=%i", revents); */
1116 if (u
->rtpoll_item
) {
1117 struct pollfd
*pollfd
;
1119 pa_assert(u
->fd
>= 0);
1121 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
1122 pollfd
->events
= (short)
1123 (((u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
)) ? POLLIN
: 0) |
1124 ((u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
)) ? POLLOUT
: 0));
1127 /* Hmm, nothing to do. Let's sleep */
1128 if ((ret
= pa_rtpoll_run(u
->rtpoll
, TRUE
)) < 0)
1134 if (u
->rtpoll_item
) {
1135 struct pollfd
*pollfd
;
1137 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
1139 if (pollfd
->revents
& ~(POLLOUT
|POLLIN
)) {
1140 pa_log("DSP shutdown.");
1144 revents
= pollfd
->revents
;
1150 /* If this was no regular exit from the loop we have to continue
1151 * processing messages until we received PA_MESSAGE_SHUTDOWN */
1152 pa_asyncmsgq_post(u
->thread_mq
.outq
, PA_MSGOBJECT(u
->core
), PA_CORE_MESSAGE_UNLOAD_MODULE
, u
->module
, 0, NULL
, NULL
);
1153 pa_asyncmsgq_wait_for(u
->thread_mq
.inq
, PA_MESSAGE_SHUTDOWN
);
1156 pa_log_debug("Thread shutting down");
1159 int pa__init(pa_module
*m
) {
1161 struct audio_buf_info info
;
1162 struct userdata
*u
= NULL
;
1165 int nfrags
, orig_frag_size
, frag_size
;
1167 pa_bool_t record
= TRUE
, playback
= TRUE
, use_mmap
= TRUE
;
1170 pa_modargs
*ma
= NULL
;
1173 pa_bool_t namereg_fail
;
1174 pa_sink_new_data sink_new_data
;
1175 pa_source_new_data source_new_data
;
1179 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1180 pa_log("Failed to parse module arguments.");
1184 if (pa_modargs_get_value_boolean(ma
, "record", &record
) < 0 || pa_modargs_get_value_boolean(ma
, "playback", &playback
) < 0) {
1185 pa_log("record= and playback= expect boolean argument.");
1189 if (!playback
&& !record
) {
1190 pa_log("Neither playback nor record enabled for device.");
1194 mode
= (playback
&& record
) ? O_RDWR
: (playback
? O_WRONLY
: (record
? O_RDONLY
: 0));
1196 ss
= m
->core
->default_sample_spec
;
1197 map
= m
->core
->default_channel_map
;
1198 if (pa_modargs_get_sample_spec_and_channel_map(ma
, &ss
, &map
, PA_CHANNEL_MAP_OSS
) < 0) {
1199 pa_log("Failed to parse sample specification or channel map");
1203 nfrags
= (int) m
->core
->default_n_fragments
;
1204 frag_size
= (int) pa_usec_to_bytes(m
->core
->default_fragment_size_msec
*1000, &ss
);
1206 frag_size
= (int) pa_frame_size(&ss
);
1208 if (pa_modargs_get_value_s32(ma
, "fragments", &nfrags
) < 0 || pa_modargs_get_value_s32(ma
, "fragment_size", &frag_size
) < 0) {
1209 pa_log("Failed to parse fragments arguments");
1213 if (pa_modargs_get_value_boolean(ma
, "mmap", &use_mmap
) < 0) {
1214 pa_log("Failed to parse mmap argument.");
1218 if ((fd
= pa_oss_open(dev
= pa_modargs_get_value(ma
, "device", DEFAULT_DEVICE
), &mode
, &caps
)) < 0)
1221 if (use_mmap
&& (!(caps
& DSP_CAP_MMAP
) || !(caps
& DSP_CAP_TRIGGER
))) {
1222 pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode.");
1226 if (use_mmap
&& mode
== O_WRONLY
) {
1227 pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode.");
1231 if (pa_oss_get_hw_description(dev
, hwdesc
, sizeof(hwdesc
)) >= 0)
1232 pa_log_info("Hardware name is '%s'.", hwdesc
);
1236 pa_log_info("Device opened in %s mode.", mode
== O_WRONLY
? "O_WRONLY" : (mode
== O_RDONLY
? "O_RDONLY" : "O_RDWR"));
1238 orig_frag_size
= frag_size
;
1239 if (nfrags
>= 2 && frag_size
>= 1)
1240 if (pa_oss_set_fragments(fd
, nfrags
, frag_size
) < 0)
1243 if (pa_oss_auto_format(fd
, &ss
) < 0)
1246 if (ioctl(fd
, SNDCTL_DSP_GETBLKSIZE
, &frag_size
) < 0) {
1247 pa_log("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno
));
1250 pa_assert(frag_size
> 0);
1252 u
= pa_xnew0(struct userdata
, 1);
1258 u
->mixer_devmask
= 0;
1259 u
->use_getospace
= u
->use_getispace
= TRUE
;
1260 u
->use_getodelay
= TRUE
;
1262 u
->frame_size
= pa_frame_size(&ss
);
1263 u
->device_name
= pa_xstrdup(dev
);
1264 u
->in_nfrags
= u
->out_nfrags
= (uint32_t) (u
->nfrags
= nfrags
);
1265 u
->out_fragment_size
= u
->in_fragment_size
= (uint32_t) (u
->frag_size
= frag_size
);
1266 u
->orig_frag_size
= orig_frag_size
;
1267 u
->use_mmap
= use_mmap
;
1268 u
->rtpoll
= pa_rtpoll_new();
1269 pa_thread_mq_init(&u
->thread_mq
, m
->core
->mainloop
, u
->rtpoll
);
1270 u
->rtpoll_item
= NULL
;
1273 if (ioctl(fd
, SNDCTL_DSP_GETISPACE
, &info
) >= 0) {
1274 pa_log_info("Input -- %u fragments of size %u.", info
.fragstotal
, info
.fragsize
);
1275 u
->in_fragment_size
= (uint32_t) info
.fragsize
;
1276 u
->in_nfrags
= (uint32_t) info
.fragstotal
;
1277 u
->use_getispace
= TRUE
;
1280 if (ioctl(fd
, SNDCTL_DSP_GETOSPACE
, &info
) >= 0) {
1281 pa_log_info("Output -- %u fragments of size %u.", info
.fragstotal
, info
.fragsize
);
1282 u
->out_fragment_size
= (uint32_t) info
.fragsize
;
1283 u
->out_nfrags
= (uint32_t) info
.fragstotal
;
1284 u
->use_getospace
= TRUE
;
1287 u
->in_hwbuf_size
= u
->in_nfrags
* u
->in_fragment_size
;
1288 u
->out_hwbuf_size
= u
->out_nfrags
* u
->out_fragment_size
;
1290 if (mode
!= O_WRONLY
) {
1291 char *name_buf
= NULL
;
1294 if ((u
->in_mmap
= mmap(NULL
, u
->in_hwbuf_size
, PROT_READ
, MAP_SHARED
, fd
, 0)) == MAP_FAILED
) {
1295 pa_log_warn("mmap(PROT_READ) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno
));
1296 use_mmap
= u
->use_mmap
= FALSE
;
1299 pa_log_debug("Successfully mmap()ed input buffer.");
1302 if ((name
= pa_modargs_get_value(ma
, "source_name", NULL
)))
1303 namereg_fail
= TRUE
;
1305 name
= name_buf
= pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev
));
1306 namereg_fail
= FALSE
;
1309 pa_source_new_data_init(&source_new_data
);
1310 source_new_data
.driver
= __FILE__
;
1311 source_new_data
.module
= m
;
1312 pa_source_new_data_set_name(&source_new_data
, name
);
1313 source_new_data
.namereg_fail
= namereg_fail
;
1314 pa_source_new_data_set_sample_spec(&source_new_data
, &ss
);
1315 pa_source_new_data_set_channel_map(&source_new_data
, &map
);
1316 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_STRING
, dev
);
1317 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_API
, "oss");
1318 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, hwdesc
[0] ? hwdesc
: dev
);
1319 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_ACCESS_MODE
, use_mmap
? "mmap" : "serial");
1320 pa_proplist_setf(source_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE
, "%lu", (unsigned long) (u
->in_hwbuf_size
));
1321 pa_proplist_setf(source_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE
, "%lu", (unsigned long) (u
->in_fragment_size
));
1323 if (pa_modargs_get_proplist(ma
, "source_properties", source_new_data
.proplist
, PA_UPDATE_REPLACE
) < 0) {
1324 pa_log("Invalid properties");
1325 pa_source_new_data_done(&source_new_data
);
1329 u
->source
= pa_source_new(m
->core
, &source_new_data
, PA_SOURCE_HARDWARE
|PA_SOURCE_LATENCY
);
1330 pa_source_new_data_done(&source_new_data
);
1334 pa_log("Failed to create source object");
1338 u
->source
->parent
.process_msg
= source_process_msg
;
1339 u
->source
->userdata
= u
;
1341 pa_source_set_asyncmsgq(u
->source
, u
->thread_mq
.inq
);
1342 pa_source_set_rtpoll(u
->source
, u
->rtpoll
);
1343 pa_source_set_fixed_latency(u
->source
, pa_bytes_to_usec(u
->in_hwbuf_size
, &u
->source
->sample_spec
));
1344 u
->source
->refresh_volume
= TRUE
;
1347 u
->in_mmap_memblocks
= pa_xnew0(pa_memblock
*, u
->in_nfrags
);
1350 if (mode
!= O_RDONLY
) {
1351 char *name_buf
= NULL
;
1354 if ((u
->out_mmap
= mmap(NULL
, u
->out_hwbuf_size
, PROT_WRITE
, MAP_SHARED
, fd
, 0)) == MAP_FAILED
) {
1355 if (mode
== O_RDWR
) {
1356 pa_log_debug("mmap() failed for input. Changing to O_WRONLY mode.");
1360 pa_log_warn("mmap(PROT_WRITE) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno
));
1361 u
->use_mmap
= use_mmap
= FALSE
;
1365 pa_log_debug("Successfully mmap()ed output buffer.");
1366 pa_silence_memory(u
->out_mmap
, u
->out_hwbuf_size
, &ss
);
1370 if ((name
= pa_modargs_get_value(ma
, "sink_name", NULL
)))
1371 namereg_fail
= TRUE
;
1373 name
= name_buf
= pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev
));
1374 namereg_fail
= FALSE
;
1377 pa_sink_new_data_init(&sink_new_data
);
1378 sink_new_data
.driver
= __FILE__
;
1379 sink_new_data
.module
= m
;
1380 pa_sink_new_data_set_name(&sink_new_data
, name
);
1381 sink_new_data
.namereg_fail
= namereg_fail
;
1382 pa_sink_new_data_set_sample_spec(&sink_new_data
, &ss
);
1383 pa_sink_new_data_set_channel_map(&sink_new_data
, &map
);
1384 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_STRING
, dev
);
1385 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_API
, "oss");
1386 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, hwdesc
[0] ? hwdesc
: dev
);
1387 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_ACCESS_MODE
, use_mmap
? "mmap" : "serial");
1388 pa_proplist_setf(sink_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE
, "%lu", (unsigned long) (u
->out_hwbuf_size
));
1389 pa_proplist_setf(sink_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE
, "%lu", (unsigned long) (u
->out_fragment_size
));
1391 if (pa_modargs_get_proplist(ma
, "sink_properties", sink_new_data
.proplist
, PA_UPDATE_REPLACE
) < 0) {
1392 pa_log("Invalid properties");
1393 pa_sink_new_data_done(&sink_new_data
);
1397 u
->sink
= pa_sink_new(m
->core
, &sink_new_data
, PA_SINK_HARDWARE
|PA_SINK_LATENCY
);
1398 pa_sink_new_data_done(&sink_new_data
);
1402 pa_log("Failed to create sink object");
1406 u
->sink
->parent
.process_msg
= sink_process_msg
;
1407 u
->sink
->userdata
= u
;
1409 pa_sink_set_asyncmsgq(u
->sink
, u
->thread_mq
.inq
);
1410 pa_sink_set_rtpoll(u
->sink
, u
->rtpoll
);
1411 pa_sink_set_fixed_latency(u
->sink
, pa_bytes_to_usec(u
->out_hwbuf_size
, &u
->sink
->sample_spec
));
1412 u
->sink
->refresh_volume
= TRUE
;
1414 pa_sink_set_max_request(u
->sink
, u
->out_hwbuf_size
);
1417 u
->out_mmap_memblocks
= pa_xnew0(pa_memblock
*, u
->out_nfrags
);
1420 if ((u
->mixer_fd
= pa_oss_open_mixer_for_device(u
->device_name
)) >= 0) {
1421 pa_bool_t do_close
= TRUE
;
1423 if (ioctl(fd
, SOUND_MIXER_READ_DEVMASK
, &u
->mixer_devmask
) < 0)
1424 pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno
));
1427 if (u
->sink
&& (u
->mixer_devmask
& (SOUND_MASK_VOLUME
|SOUND_MASK_PCM
))) {
1428 pa_log_debug("Found hardware mixer track for playback.");
1429 u
->sink
->flags
|= PA_SINK_HW_VOLUME_CTRL
;
1430 u
->sink
->get_volume
= sink_get_volume
;
1431 u
->sink
->set_volume
= sink_set_volume
;
1432 u
->sink
->n_volume_steps
= 101;
1436 if (u
->source
&& (u
->mixer_devmask
& (SOUND_MASK_RECLEV
|SOUND_MASK_IGAIN
))) {
1437 pa_log_debug("Found hardware mixer track for recording.");
1438 u
->source
->flags
|= PA_SOURCE_HW_VOLUME_CTRL
;
1439 u
->source
->get_volume
= source_get_volume
;
1440 u
->source
->set_volume
= source_set_volume
;
1441 u
->source
->n_volume_steps
= 101;
1447 pa_close(u
->mixer_fd
);
1449 u
->mixer_devmask
= 0;
1455 pa_assert(u
->source
|| u
->sink
);
1457 pa_memchunk_reset(&u
->memchunk
);
1459 if (!(u
->thread
= pa_thread_new("oss", thread_func
, u
))) {
1460 pa_log("Failed to create thread.");
1464 /* Read mixer settings */
1466 if (sink_new_data
.volume_is_set
) {
1467 if (u
->sink
->set_volume
)
1468 u
->sink
->set_volume(u
->sink
);
1470 if (u
->sink
->get_volume
)
1471 u
->sink
->get_volume(u
->sink
);
1476 if (source_new_data
.volume_is_set
) {
1477 if (u
->source
->set_volume
)
1478 u
->source
->set_volume(u
->source
);
1480 if (u
->source
->get_volume
)
1481 u
->source
->get_volume(u
->source
);
1486 pa_sink_put(u
->sink
);
1488 pa_source_put(u
->source
);
1490 pa_modargs_free(ma
);
1502 pa_modargs_free(ma
);
1507 void pa__done(pa_module
*m
) {
1512 if (!(u
= m
->userdata
))
1516 pa_sink_unlink(u
->sink
);
1519 pa_source_unlink(u
->source
);
1522 pa_asyncmsgq_send(u
->thread_mq
.inq
, NULL
, PA_MESSAGE_SHUTDOWN
, NULL
, 0, NULL
);
1523 pa_thread_free(u
->thread
);
1526 pa_thread_mq_done(&u
->thread_mq
);
1529 pa_sink_unref(u
->sink
);
1532 pa_source_unref(u
->source
);
1534 if (u
->memchunk
.memblock
)
1535 pa_memblock_unref(u
->memchunk
.memblock
);
1538 pa_rtpoll_item_free(u
->rtpoll_item
);
1541 pa_rtpoll_free(u
->rtpoll
);
1543 if (u
->out_mmap_memblocks
) {
1545 for (i
= 0; i
< u
->out_nfrags
; i
++)
1546 if (u
->out_mmap_memblocks
[i
])
1547 pa_memblock_unref_fixed(u
->out_mmap_memblocks
[i
]);
1548 pa_xfree(u
->out_mmap_memblocks
);
1551 if (u
->in_mmap_memblocks
) {
1553 for (i
= 0; i
< u
->in_nfrags
; i
++)
1554 if (u
->in_mmap_memblocks
[i
])
1555 pa_memblock_unref_fixed(u
->in_mmap_memblocks
[i
]);
1556 pa_xfree(u
->in_mmap_memblocks
);
1559 if (u
->in_mmap
&& u
->in_mmap
!= MAP_FAILED
)
1560 munmap(u
->in_mmap
, u
->in_hwbuf_size
);
1562 if (u
->out_mmap
&& u
->out_mmap
!= MAP_FAILED
)
1563 munmap(u
->out_mmap
, u
->out_hwbuf_size
);
1568 if (u
->mixer_fd
>= 0)
1569 pa_close(u
->mixer_fd
);
1571 pa_xfree(u
->device_name
);