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>
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 pa_bool_t use_getospace
, use_getispace
;
120 pa_bool_t use_getodelay
;
122 pa_bool_t 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 void trigger(struct userdata
*u
, pa_bool_t 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
);
182 ioctl(u
->fd
, SNDCTL_DSP_SETTRIGGER
, &zero
);
184 #ifdef SNDCTL_DSP_HALT
185 if (enable_bits
== 0)
186 if (ioctl(u
->fd
, SNDCTL_DSP_HALT
, NULL
) < 0)
187 pa_log_warn("SNDCTL_DSP_HALT: %s", pa_cstrerror(errno
));
190 if (ioctl(u
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable_bits
) < 0)
191 pa_log_warn("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno
));
193 if (u
->sink
&& !(enable_bits
& PCM_ENABLE_OUTPUT
)) {
194 pa_log_debug("clearing playback buffer");
195 pa_silence_memory(u
->out_mmap
, u
->out_hwbuf_size
, &u
->sink
->sample_spec
);
201 if (ioctl(u
->fd
, SNDCTL_DSP_POST
, NULL
) < 0)
202 pa_log_warn("SNDCTL_DSP_POST: %s", pa_cstrerror(errno
));
206 * Some crappy drivers do not start the recording until we
207 * read something. Without this snippet, poll will never
208 * register the fd as ready.
211 if (u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
)) {
212 uint8_t *buf
= pa_xnew(uint8_t, u
->in_fragment_size
);
213 pa_read(u
->fd
, buf
, u
->in_fragment_size
, NULL
);
220 static void mmap_fill_memblocks(struct userdata
*u
, unsigned n
) {
222 pa_assert(u
->out_mmap_memblocks
);
224 /* pa_log("Mmmap writing %u blocks", n); */
229 if (u
->out_mmap_memblocks
[u
->out_mmap_current
])
230 pa_memblock_unref_fixed(u
->out_mmap_memblocks
[u
->out_mmap_current
]);
232 chunk
.memblock
= u
->out_mmap_memblocks
[u
->out_mmap_current
] =
233 pa_memblock_new_fixed(
235 (uint8_t*) u
->out_mmap
+ u
->out_fragment_size
* u
->out_mmap_current
,
236 u
->out_fragment_size
,
239 chunk
.length
= pa_memblock_get_length(chunk
.memblock
);
242 pa_sink_render_into_full(u
->sink
, &chunk
);
244 u
->out_mmap_current
++;
245 while (u
->out_mmap_current
>= u
->out_nfrags
)
246 u
->out_mmap_current
-= u
->out_nfrags
;
252 static int mmap_write(struct userdata
*u
) {
253 struct count_info info
;
258 /* pa_log("Mmmap writing..."); */
260 if (ioctl(u
->fd
, SNDCTL_DSP_GETOPTR
, &info
) < 0) {
261 pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno
));
265 info
.blocks
+= u
->out_mmap_saved_nfrags
;
266 u
->out_mmap_saved_nfrags
= 0;
269 mmap_fill_memblocks(u
, (unsigned) info
.blocks
);
274 static void mmap_post_memblocks(struct userdata
*u
, unsigned n
) {
276 pa_assert(u
->in_mmap_memblocks
);
278 /* pa_log("Mmmap reading %u blocks", n); */
283 if (!u
->in_mmap_memblocks
[u
->in_mmap_current
]) {
285 chunk
.memblock
= u
->in_mmap_memblocks
[u
->in_mmap_current
] =
286 pa_memblock_new_fixed(
288 (uint8_t*) u
->in_mmap
+ u
->in_fragment_size
*u
->in_mmap_current
,
292 chunk
.length
= pa_memblock_get_length(chunk
.memblock
);
295 pa_source_post(u
->source
, &chunk
);
298 u
->in_mmap_current
++;
299 while (u
->in_mmap_current
>= u
->in_nfrags
)
300 u
->in_mmap_current
-= u
->in_nfrags
;
306 static void mmap_clear_memblocks(struct userdata
*u
, unsigned n
) {
307 unsigned i
= u
->in_mmap_current
;
310 pa_assert(u
->in_mmap_memblocks
);
312 if (n
> u
->in_nfrags
)
316 if (u
->in_mmap_memblocks
[i
]) {
317 pa_memblock_unref_fixed(u
->in_mmap_memblocks
[i
]);
318 u
->in_mmap_memblocks
[i
] = NULL
;
322 while (i
>= u
->in_nfrags
)
329 static int mmap_read(struct userdata
*u
) {
330 struct count_info info
;
332 pa_assert(u
->source
);
334 /* pa_log("Mmmap reading..."); */
336 if (ioctl(u
->fd
, SNDCTL_DSP_GETIPTR
, &info
) < 0) {
337 pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno
));
341 /* pa_log("... %i", info.blocks); */
343 info
.blocks
+= u
->in_mmap_saved_nfrags
;
344 u
->in_mmap_saved_nfrags
= 0;
346 if (info
.blocks
> 0) {
347 mmap_post_memblocks(u
, (unsigned) info
.blocks
);
348 mmap_clear_memblocks(u
, u
->in_nfrags
/2);
354 static pa_usec_t
mmap_sink_get_latency(struct userdata
*u
) {
355 struct count_info info
;
360 if (ioctl(u
->fd
, SNDCTL_DSP_GETOPTR
, &info
) < 0) {
361 pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno
));
365 u
->out_mmap_saved_nfrags
+= info
.blocks
;
367 bpos
= ((u
->out_mmap_current
+ (unsigned) u
->out_mmap_saved_nfrags
) * u
->out_fragment_size
) % u
->out_hwbuf_size
;
369 if (bpos
<= (size_t) info
.ptr
)
370 n
= u
->out_hwbuf_size
- ((size_t) info
.ptr
- bpos
);
372 n
= bpos
- (size_t) info
.ptr
;
374 /* 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); */
376 return pa_bytes_to_usec(n
, &u
->sink
->sample_spec
);
379 static pa_usec_t
mmap_source_get_latency(struct userdata
*u
) {
380 struct count_info info
;
385 if (ioctl(u
->fd
, SNDCTL_DSP_GETIPTR
, &info
) < 0) {
386 pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno
));
390 u
->in_mmap_saved_nfrags
+= info
.blocks
;
391 bpos
= ((u
->in_mmap_current
+ (unsigned) u
->in_mmap_saved_nfrags
) * u
->in_fragment_size
) % u
->in_hwbuf_size
;
393 if (bpos
<= (size_t) info
.ptr
)
394 n
= (size_t) info
.ptr
- bpos
;
396 n
= u
->in_hwbuf_size
- bpos
+ (size_t) info
.ptr
;
398 /* 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); */
400 return pa_bytes_to_usec(n
, &u
->source
->sample_spec
);
403 static pa_usec_t
io_sink_get_latency(struct userdata
*u
) {
408 if (u
->use_getodelay
) {
410 #if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
411 #if defined(AUDIO_GETBUFINFO)
412 struct audio_info info
;
413 if (syscall(SYS_ioctl
, u
->fd
, AUDIO_GETBUFINFO
, &info
) < 0) {
414 pa_log_info("Device doesn't support AUDIO_GETBUFINFO: %s", pa_cstrerror(errno
));
415 u
->use_getodelay
= 0;
417 arg
= info
.play
.seek
+ info
.blocksize
/ 2;
418 r
= pa_bytes_to_usec((size_t) arg
, &u
->sink
->sample_spec
);
421 pa_log_info("System doesn't support AUDIO_GETBUFINFO");
422 u
->use_getodelay
= 0;
425 if (ioctl(u
->fd
, SNDCTL_DSP_GETODELAY
, &arg
) < 0) {
426 pa_log_info("Device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno
));
427 u
->use_getodelay
= 0;
429 r
= pa_bytes_to_usec((size_t) arg
, &u
->sink
->sample_spec
);
433 if (!u
->use_getodelay
&& u
->use_getospace
) {
434 struct audio_buf_info info
;
436 if (ioctl(u
->fd
, SNDCTL_DSP_GETOSPACE
, &info
) < 0) {
437 pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno
));
438 u
->use_getospace
= 0;
440 r
= pa_bytes_to_usec((size_t) info
.bytes
, &u
->sink
->sample_spec
);
443 if (u
->memchunk
.memblock
)
444 r
+= pa_bytes_to_usec(u
->memchunk
.length
, &u
->sink
->sample_spec
);
449 static pa_usec_t
io_source_get_latency(struct userdata
*u
) {
454 if (u
->use_getispace
) {
455 struct audio_buf_info info
;
457 if (ioctl(u
->fd
, SNDCTL_DSP_GETISPACE
, &info
) < 0) {
458 pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno
));
459 u
->use_getispace
= 0;
461 r
= pa_bytes_to_usec((size_t) info
.bytes
, &u
->source
->sample_spec
);
467 static void build_pollfd(struct userdata
*u
) {
468 struct pollfd
*pollfd
;
471 pa_assert(u
->fd
>= 0);
474 pa_rtpoll_item_free(u
->rtpoll_item
);
476 u
->rtpoll_item
= pa_rtpoll_item_new(u
->rtpoll
, PA_RTPOLL_NEVER
, 1);
477 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
483 /* Called from IO context */
484 static int suspend(struct userdata
*u
) {
486 pa_assert(u
->fd
>= 0);
488 pa_log_info("Suspending...");
490 if (u
->out_mmap_memblocks
) {
492 for (i
= 0; i
< u
->out_nfrags
; i
++)
493 if (u
->out_mmap_memblocks
[i
]) {
494 pa_memblock_unref_fixed(u
->out_mmap_memblocks
[i
]);
495 u
->out_mmap_memblocks
[i
] = NULL
;
499 if (u
->in_mmap_memblocks
) {
501 for (i
= 0; i
< u
->in_nfrags
; i
++)
502 if (u
->in_mmap_memblocks
[i
]) {
503 pa_memblock_unref_fixed(u
->in_mmap_memblocks
[i
]);
504 u
->in_mmap_memblocks
[i
] = NULL
;
508 if (u
->in_mmap
&& u
->in_mmap
!= MAP_FAILED
) {
509 munmap(u
->in_mmap
, u
->in_hwbuf_size
);
513 if (u
->out_mmap
&& u
->out_mmap
!= MAP_FAILED
) {
514 munmap(u
->out_mmap
, u
->out_hwbuf_size
);
519 ioctl(u
->fd
, SNDCTL_DSP_SYNC
, NULL
);
523 if (u
->rtpoll_item
) {
524 pa_rtpoll_item_free(u
->rtpoll_item
);
525 u
->rtpoll_item
= NULL
;
528 pa_log_info("Device suspended...");
533 /* Called from IO context */
534 static int unsuspend(struct userdata
*u
) {
536 pa_sample_spec ss
, *ss_original
;
537 int frag_size
, in_frag_size
, out_frag_size
;
538 int in_nfrags
, out_nfrags
;
539 struct audio_buf_info info
;
542 pa_assert(u
->fd
< 0);
546 pa_log_info("Trying resume...");
548 if ((u
->fd
= pa_oss_open(u
->device_name
, &m
, NULL
)) < 0) {
549 pa_log_warn("Resume failed, device busy (%s)", pa_cstrerror(errno
));
554 pa_log_warn("Resume failed, couldn't open device with original access mode.");
558 if (u
->nfrags
>= 2 && u
->frag_size
>= 1)
559 if (pa_oss_set_fragments(u
->fd
, u
->nfrags
, u
->orig_frag_size
) < 0) {
560 pa_log_warn("Resume failed, couldn't set original fragment settings.");
564 ss
= *(ss_original
= u
->sink
? &u
->sink
->sample_spec
: &u
->source
->sample_spec
);
565 if (pa_oss_auto_format(u
->fd
, &ss
) < 0 || !pa_sample_spec_equal(&ss
, ss_original
)) {
566 pa_log_warn("Resume failed, couldn't set original sample format settings.");
570 if (ioctl(u
->fd
, SNDCTL_DSP_GETBLKSIZE
, &frag_size
) < 0) {
571 pa_log_warn("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno
));
575 in_frag_size
= out_frag_size
= frag_size
;
576 in_nfrags
= out_nfrags
= u
->nfrags
;
578 if (ioctl(u
->fd
, SNDCTL_DSP_GETISPACE
, &info
) >= 0) {
579 in_frag_size
= info
.fragsize
;
580 in_nfrags
= info
.fragstotal
;
583 if (ioctl(u
->fd
, SNDCTL_DSP_GETOSPACE
, &info
) >= 0) {
584 out_frag_size
= info
.fragsize
;
585 out_nfrags
= info
.fragstotal
;
588 if ((u
->source
&& (in_frag_size
!= (int) u
->in_fragment_size
|| in_nfrags
!= (int) u
->in_nfrags
)) ||
589 (u
->sink
&& (out_frag_size
!= (int) u
->out_fragment_size
|| out_nfrags
!= (int) u
->out_nfrags
))) {
590 pa_log_warn("Resume failed, input fragment settings don't match.");
596 if ((u
->in_mmap
= mmap(NULL
, u
->in_hwbuf_size
, PROT_READ
, MAP_SHARED
, u
->fd
, 0)) == MAP_FAILED
) {
597 pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno
));
603 if ((u
->out_mmap
= mmap(NULL
, u
->out_hwbuf_size
, PROT_WRITE
, MAP_SHARED
, u
->fd
, 0)) == MAP_FAILED
) {
604 pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno
));
605 if (u
->in_mmap
&& u
->in_mmap
!= MAP_FAILED
) {
606 munmap(u
->in_mmap
, u
->in_hwbuf_size
);
613 pa_silence_memory(u
->out_mmap
, u
->out_hwbuf_size
, &ss
);
617 u
->out_mmap_current
= u
->in_mmap_current
= 0;
618 u
->out_mmap_saved_nfrags
= u
->in_mmap_saved_nfrags
= 0;
620 pa_assert(!u
->rtpoll_item
);
624 if (u
->sink
&& u
->sink
->get_volume
)
625 u
->sink
->get_volume(u
->sink
);
626 if (u
->source
&& u
->source
->get_volume
)
627 u
->source
->get_volume(u
->source
);
629 pa_log_info("Resumed successfully...");
639 /* Called from IO context */
640 static int sink_process_msg(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
641 struct userdata
*u
= PA_SINK(o
)->userdata
;
643 pa_bool_t do_trigger
= FALSE
, quick
= TRUE
;
647 case PA_SINK_MESSAGE_GET_LATENCY
: {
652 r
= mmap_sink_get_latency(u
);
654 r
= io_sink_get_latency(u
);
657 *((pa_usec_t
*) data
) = r
;
662 case PA_SINK_MESSAGE_SET_STATE
:
664 switch ((pa_sink_state_t
) PA_PTR_TO_UINT(data
)) {
666 case PA_SINK_SUSPENDED
:
667 pa_assert(PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
));
669 if (!u
->source
|| u
->source_suspended
) {
676 u
->sink_suspended
= TRUE
;
680 case PA_SINK_RUNNING
:
682 if (u
->sink
->thread_info
.state
== PA_SINK_INIT
) {
684 quick
= u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
);
687 if (u
->sink
->thread_info
.state
== PA_SINK_SUSPENDED
) {
689 if (!u
->source
|| u
->source_suspended
) {
690 if (unsuspend(u
) < 0)
697 u
->out_mmap_current
= 0;
698 u
->out_mmap_saved_nfrags
= 0;
700 u
->sink_suspended
= FALSE
;
705 case PA_SINK_INVALID_STATE
:
706 case PA_SINK_UNLINKED
:
715 ret
= pa_sink_process_msg(o
, code
, data
, offset
, chunk
);
723 static int source_process_msg(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
724 struct userdata
*u
= PA_SOURCE(o
)->userdata
;
726 int do_trigger
= FALSE
, quick
= TRUE
;
730 case PA_SOURCE_MESSAGE_GET_LATENCY
: {
735 r
= mmap_source_get_latency(u
);
737 r
= io_source_get_latency(u
);
740 *((pa_usec_t
*) data
) = r
;
744 case PA_SOURCE_MESSAGE_SET_STATE
:
746 switch ((pa_source_state_t
) PA_PTR_TO_UINT(data
)) {
747 case PA_SOURCE_SUSPENDED
:
748 pa_assert(PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
));
750 if (!u
->sink
|| u
->sink_suspended
) {
757 u
->source_suspended
= TRUE
;
761 case PA_SOURCE_RUNNING
:
763 if (u
->source
->thread_info
.state
== PA_SOURCE_INIT
) {
765 quick
= u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
);
768 if (u
->source
->thread_info
.state
== PA_SOURCE_SUSPENDED
) {
770 if (!u
->sink
|| u
->sink_suspended
) {
771 if (unsuspend(u
) < 0)
778 u
->in_mmap_current
= 0;
779 u
->in_mmap_saved_nfrags
= 0;
781 u
->source_suspended
= FALSE
;
785 case PA_SOURCE_UNLINKED
:
787 case PA_SOURCE_INVALID_STATE
:
795 ret
= pa_source_process_msg(o
, code
, data
, offset
, chunk
);
803 static void sink_get_volume(pa_sink
*s
) {
806 pa_assert_se(u
= s
->userdata
);
808 pa_assert(u
->mixer_devmask
& (SOUND_MASK_VOLUME
|SOUND_MASK_PCM
));
810 if (u
->mixer_devmask
& SOUND_MASK_VOLUME
)
811 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_READ_VOLUME
, &s
->sample_spec
, &s
->real_volume
) >= 0)
814 if (u
->mixer_devmask
& SOUND_MASK_PCM
)
815 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_READ_PCM
, &s
->sample_spec
, &s
->real_volume
) >= 0)
818 pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno
));
821 static void sink_set_volume(pa_sink
*s
) {
824 pa_assert_se(u
= s
->userdata
);
826 pa_assert(u
->mixer_devmask
& (SOUND_MASK_VOLUME
|SOUND_MASK_PCM
));
828 if (u
->mixer_devmask
& SOUND_MASK_VOLUME
)
829 if (pa_oss_set_volume(u
->mixer_fd
, SOUND_MIXER_WRITE_VOLUME
, &s
->sample_spec
, &s
->real_volume
) >= 0)
832 if (u
->mixer_devmask
& SOUND_MASK_PCM
)
833 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_WRITE_PCM
, &s
->sample_spec
, &s
->real_volume
) >= 0)
836 pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno
));
839 static void source_get_volume(pa_source
*s
) {
842 pa_assert_se(u
= s
->userdata
);
844 pa_assert(u
->mixer_devmask
& (SOUND_MASK_IGAIN
|SOUND_MASK_RECLEV
));
846 if (u
->mixer_devmask
& SOUND_MASK_IGAIN
)
847 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_READ_IGAIN
, &s
->sample_spec
, &s
->real_volume
) >= 0)
850 if (u
->mixer_devmask
& SOUND_MASK_RECLEV
)
851 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_READ_RECLEV
, &s
->sample_spec
, &s
->real_volume
) >= 0)
854 pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno
));
857 static void source_set_volume(pa_source
*s
) {
860 pa_assert_se(u
= s
->userdata
);
862 pa_assert(u
->mixer_devmask
& (SOUND_MASK_IGAIN
|SOUND_MASK_RECLEV
));
864 if (u
->mixer_devmask
& SOUND_MASK_IGAIN
)
865 if (pa_oss_set_volume(u
->mixer_fd
, SOUND_MIXER_WRITE_IGAIN
, &s
->sample_spec
, &s
->real_volume
) >= 0)
868 if (u
->mixer_devmask
& SOUND_MASK_RECLEV
)
869 if (pa_oss_get_volume(u
->mixer_fd
, SOUND_MIXER_WRITE_RECLEV
, &s
->sample_spec
, &s
->real_volume
) >= 0)
872 pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno
));
875 static void thread_func(void *userdata
) {
876 struct userdata
*u
= userdata
;
877 int write_type
= 0, read_type
= 0;
882 pa_log_debug("Thread starting up");
884 if (u
->core
->realtime_scheduling
)
885 pa_make_realtime(u
->core
->realtime_priority
);
887 pa_thread_mq_install(&u
->thread_mq
);
892 /* pa_log("loop"); */
894 if (u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
))
895 if (u
->sink
->thread_info
.rewind_requested
)
896 pa_sink_process_rewind(u
->sink
, 0);
898 /* Render some data and write it to the dsp */
900 if (u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
) && ((revents
& POLLOUT
) || u
->use_mmap
|| u
->use_getospace
)) {
904 if ((ret
= mmap_write(u
)) < 0)
914 pa_bool_t loop
= FALSE
, work_done
= FALSE
;
916 l
= (ssize_t
) u
->out_fragment_size
;
918 if (u
->use_getospace
) {
921 if (ioctl(u
->fd
, SNDCTL_DSP_GETOSPACE
, &info
) < 0) {
922 pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno
));
923 u
->use_getospace
= FALSE
;
927 /* We loop only if GETOSPACE worked and we
928 * actually *know* that we can write more than
929 * one fragment at a time */
934 /* Round down to multiples of the fragment size,
935 * because OSS needs that (at least some versions
937 l
= (l
/(ssize_t
) u
->out_fragment_size
) * (ssize_t
) u
->out_fragment_size
;
939 /* Hmm, so poll() signalled us that we can read
940 * something, but GETOSPACE told us there was nothing?
941 * Hmm, make the best of it, try to read some data, to
942 * avoid spinning forever. */
943 if (l
<= 0 && (revents
& POLLOUT
)) {
944 l
= (ssize_t
) u
->out_fragment_size
;
952 if (u
->memchunk
.length
<= 0)
953 pa_sink_render(u
->sink
, (size_t) l
, &u
->memchunk
);
955 pa_assert(u
->memchunk
.length
> 0);
957 p
= pa_memblock_acquire(u
->memchunk
.memblock
);
958 t
= pa_write(u
->fd
, (uint8_t*) p
+ u
->memchunk
.index
, u
->memchunk
.length
, &write_type
);
959 pa_memblock_release(u
->memchunk
.memblock
);
961 /* pa_log("wrote %i bytes of %u", t, l); */
970 else if (errno
== EAGAIN
) {
971 pa_log_debug("EAGAIN");
977 pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno
));
983 u
->memchunk
.index
+= (size_t) t
;
984 u
->memchunk
.length
-= (size_t) t
;
986 if (u
->memchunk
.length
<= 0) {
987 pa_memblock_unref(u
->memchunk
.memblock
);
988 pa_memchunk_reset(&u
->memchunk
);
1006 /* Try to read some data and pass it on to the source driver. */
1008 if (u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
) && ((revents
& POLLIN
) || u
->use_mmap
|| u
->use_getispace
)) {
1012 if ((ret
= mmap_read(u
)) < 0)
1024 pa_memchunk memchunk
;
1025 pa_bool_t loop
= FALSE
, work_done
= FALSE
;
1027 l
= (ssize_t
) u
->in_fragment_size
;
1029 if (u
->use_getispace
) {
1030 audio_buf_info info
;
1032 if (ioctl(u
->fd
, SNDCTL_DSP_GETISPACE
, &info
) < 0) {
1033 pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno
));
1034 u
->use_getispace
= FALSE
;
1041 l
= (l
/(ssize_t
) u
->in_fragment_size
) * (ssize_t
) u
->in_fragment_size
;
1043 if (l
<= 0 && (revents
& POLLIN
)) {
1044 l
= (ssize_t
) u
->in_fragment_size
;
1054 memchunk
.memblock
= pa_memblock_new(u
->core
->mempool
, (size_t) -1);
1056 k
= pa_memblock_get_length(memchunk
.memblock
);
1061 k
= (k
/u
->frame_size
)*u
->frame_size
;
1063 p
= pa_memblock_acquire(memchunk
.memblock
);
1064 t
= pa_read(u
->fd
, p
, k
, &read_type
);
1065 pa_memblock_release(memchunk
.memblock
);
1067 pa_assert(t
!= 0); /* EOF cannot happen */
1069 /* pa_log("read %i bytes of %u", t, l); */
1072 pa_memblock_unref(memchunk
.memblock
);
1077 else if (errno
== EAGAIN
) {
1078 pa_log_debug("EAGAIN");
1084 pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno
));
1090 memchunk
.length
= (size_t) t
;
1092 pa_source_post(u
->source
, &memchunk
);
1093 pa_memblock_unref(memchunk
.memblock
);
1110 /* pa_log("loop2 revents=%i", revents); */
1112 if (u
->rtpoll_item
) {
1113 struct pollfd
*pollfd
;
1115 pa_assert(u
->fd
>= 0);
1117 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
1118 pollfd
->events
= (short)
1119 (((u
->source
&& PA_SOURCE_IS_OPENED(u
->source
->thread_info
.state
)) ? POLLIN
: 0) |
1120 ((u
->sink
&& PA_SINK_IS_OPENED(u
->sink
->thread_info
.state
)) ? POLLOUT
: 0));
1123 /* Hmm, nothing to do. Let's sleep */
1124 if ((ret
= pa_rtpoll_run(u
->rtpoll
, TRUE
)) < 0)
1130 if (u
->rtpoll_item
) {
1131 struct pollfd
*pollfd
;
1133 pollfd
= pa_rtpoll_item_get_pollfd(u
->rtpoll_item
, NULL
);
1135 if (pollfd
->revents
& ~(POLLOUT
|POLLIN
)) {
1136 pa_log("DSP shutdown.");
1140 revents
= pollfd
->revents
;
1146 /* If this was no regular exit from the loop we have to continue
1147 * processing messages until we received PA_MESSAGE_SHUTDOWN */
1148 pa_asyncmsgq_post(u
->thread_mq
.outq
, PA_MSGOBJECT(u
->core
), PA_CORE_MESSAGE_UNLOAD_MODULE
, u
->module
, 0, NULL
, NULL
);
1149 pa_asyncmsgq_wait_for(u
->thread_mq
.inq
, PA_MESSAGE_SHUTDOWN
);
1152 pa_log_debug("Thread shutting down");
1155 int pa__init(pa_module
*m
) {
1157 struct audio_buf_info info
;
1158 struct userdata
*u
= NULL
;
1161 int nfrags
, orig_frag_size
, frag_size
;
1163 pa_bool_t record
= TRUE
, playback
= TRUE
, use_mmap
= TRUE
;
1166 pa_modargs
*ma
= NULL
;
1169 pa_bool_t namereg_fail
;
1170 pa_sink_new_data sink_new_data
;
1171 pa_source_new_data source_new_data
;
1175 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1176 pa_log("Failed to parse module arguments.");
1180 if (pa_modargs_get_value_boolean(ma
, "record", &record
) < 0 || pa_modargs_get_value_boolean(ma
, "playback", &playback
) < 0) {
1181 pa_log("record= and playback= expect boolean argument.");
1185 if (!playback
&& !record
) {
1186 pa_log("Neither playback nor record enabled for device.");
1190 mode
= (playback
&& record
) ? O_RDWR
: (playback
? O_WRONLY
: (record
? O_RDONLY
: 0));
1192 ss
= m
->core
->default_sample_spec
;
1193 map
= m
->core
->default_channel_map
;
1194 if (pa_modargs_get_sample_spec_and_channel_map(ma
, &ss
, &map
, PA_CHANNEL_MAP_OSS
) < 0) {
1195 pa_log("Failed to parse sample specification or channel map");
1199 nfrags
= (int) m
->core
->default_n_fragments
;
1200 frag_size
= (int) pa_usec_to_bytes(m
->core
->default_fragment_size_msec
*1000, &ss
);
1202 frag_size
= (int) pa_frame_size(&ss
);
1204 if (pa_modargs_get_value_s32(ma
, "fragments", &nfrags
) < 0 || pa_modargs_get_value_s32(ma
, "fragment_size", &frag_size
) < 0) {
1205 pa_log("Failed to parse fragments arguments");
1209 if (pa_modargs_get_value_boolean(ma
, "mmap", &use_mmap
) < 0) {
1210 pa_log("Failed to parse mmap argument.");
1214 if ((fd
= pa_oss_open(dev
= pa_modargs_get_value(ma
, "device", DEFAULT_DEVICE
), &mode
, &caps
)) < 0)
1217 if (use_mmap
&& (!(caps
& DSP_CAP_MMAP
) || !(caps
& DSP_CAP_TRIGGER
))) {
1218 pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode.");
1222 if (use_mmap
&& mode
== O_WRONLY
) {
1223 pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode.");
1227 if (pa_oss_get_hw_description(dev
, hwdesc
, sizeof(hwdesc
)) >= 0)
1228 pa_log_info("Hardware name is '%s'.", hwdesc
);
1232 pa_log_info("Device opened in %s mode.", mode
== O_WRONLY
? "O_WRONLY" : (mode
== O_RDONLY
? "O_RDONLY" : "O_RDWR"));
1234 orig_frag_size
= frag_size
;
1235 if (nfrags
>= 2 && frag_size
>= 1)
1236 if (pa_oss_set_fragments(fd
, nfrags
, frag_size
) < 0)
1239 if (pa_oss_auto_format(fd
, &ss
) < 0)
1242 if (ioctl(fd
, SNDCTL_DSP_GETBLKSIZE
, &frag_size
) < 0) {
1243 pa_log("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno
));
1246 pa_assert(frag_size
> 0);
1248 u
= pa_xnew0(struct userdata
, 1);
1254 u
->mixer_devmask
= 0;
1255 u
->use_getospace
= u
->use_getispace
= TRUE
;
1256 u
->use_getodelay
= TRUE
;
1258 u
->frame_size
= pa_frame_size(&ss
);
1259 u
->device_name
= pa_xstrdup(dev
);
1260 u
->in_nfrags
= u
->out_nfrags
= (uint32_t) (u
->nfrags
= nfrags
);
1261 u
->out_fragment_size
= u
->in_fragment_size
= (uint32_t) (u
->frag_size
= frag_size
);
1262 u
->orig_frag_size
= orig_frag_size
;
1263 u
->use_mmap
= use_mmap
;
1264 u
->rtpoll
= pa_rtpoll_new();
1265 pa_thread_mq_init(&u
->thread_mq
, m
->core
->mainloop
, u
->rtpoll
);
1266 u
->rtpoll_item
= NULL
;
1269 if (ioctl(fd
, SNDCTL_DSP_GETISPACE
, &info
) >= 0) {
1270 pa_log_info("Input -- %u fragments of size %u.", info
.fragstotal
, info
.fragsize
);
1271 u
->in_fragment_size
= (uint32_t) info
.fragsize
;
1272 u
->in_nfrags
= (uint32_t) info
.fragstotal
;
1273 u
->use_getispace
= TRUE
;
1276 if (ioctl(fd
, SNDCTL_DSP_GETOSPACE
, &info
) >= 0) {
1277 pa_log_info("Output -- %u fragments of size %u.", info
.fragstotal
, info
.fragsize
);
1278 u
->out_fragment_size
= (uint32_t) info
.fragsize
;
1279 u
->out_nfrags
= (uint32_t) info
.fragstotal
;
1280 u
->use_getospace
= TRUE
;
1283 u
->in_hwbuf_size
= u
->in_nfrags
* u
->in_fragment_size
;
1284 u
->out_hwbuf_size
= u
->out_nfrags
* u
->out_fragment_size
;
1286 if (mode
!= O_WRONLY
) {
1287 char *name_buf
= NULL
;
1290 if ((u
->in_mmap
= mmap(NULL
, u
->in_hwbuf_size
, PROT_READ
, MAP_SHARED
, fd
, 0)) == MAP_FAILED
) {
1291 pa_log_warn("mmap(PROT_READ) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno
));
1292 use_mmap
= u
->use_mmap
= FALSE
;
1295 pa_log_debug("Successfully mmap()ed input buffer.");
1298 if ((name
= pa_modargs_get_value(ma
, "source_name", NULL
)))
1299 namereg_fail
= TRUE
;
1301 name
= name_buf
= pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev
));
1302 namereg_fail
= FALSE
;
1305 pa_source_new_data_init(&source_new_data
);
1306 source_new_data
.driver
= __FILE__
;
1307 source_new_data
.module
= m
;
1308 pa_source_new_data_set_name(&source_new_data
, name
);
1309 source_new_data
.namereg_fail
= namereg_fail
;
1310 pa_source_new_data_set_sample_spec(&source_new_data
, &ss
);
1311 pa_source_new_data_set_channel_map(&source_new_data
, &map
);
1312 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_STRING
, dev
);
1313 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_API
, "oss");
1314 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, hwdesc
[0] ? hwdesc
: dev
);
1315 pa_proplist_sets(source_new_data
.proplist
, PA_PROP_DEVICE_ACCESS_MODE
, use_mmap
? "mmap" : "serial");
1316 pa_proplist_setf(source_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE
, "%lu", (unsigned long) (u
->in_hwbuf_size
));
1317 pa_proplist_setf(source_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE
, "%lu", (unsigned long) (u
->in_fragment_size
));
1319 if (pa_modargs_get_proplist(ma
, "source_properties", source_new_data
.proplist
, PA_UPDATE_REPLACE
) < 0) {
1320 pa_log("Invalid properties");
1321 pa_source_new_data_done(&source_new_data
);
1325 u
->source
= pa_source_new(m
->core
, &source_new_data
, PA_SOURCE_HARDWARE
|PA_SOURCE_LATENCY
);
1326 pa_source_new_data_done(&source_new_data
);
1330 pa_log("Failed to create source object");
1334 u
->source
->parent
.process_msg
= source_process_msg
;
1335 u
->source
->userdata
= u
;
1337 pa_source_set_asyncmsgq(u
->source
, u
->thread_mq
.inq
);
1338 pa_source_set_rtpoll(u
->source
, u
->rtpoll
);
1339 pa_source_set_fixed_latency(u
->source
, pa_bytes_to_usec(u
->in_hwbuf_size
, &u
->source
->sample_spec
));
1340 u
->source
->refresh_volume
= TRUE
;
1343 u
->in_mmap_memblocks
= pa_xnew0(pa_memblock
*, u
->in_nfrags
);
1346 if (mode
!= O_RDONLY
) {
1347 char *name_buf
= NULL
;
1350 if ((u
->out_mmap
= mmap(NULL
, u
->out_hwbuf_size
, PROT_WRITE
, MAP_SHARED
, fd
, 0)) == MAP_FAILED
) {
1351 if (mode
== O_RDWR
) {
1352 pa_log_debug("mmap() failed for input. Changing to O_WRONLY mode.");
1356 pa_log_warn("mmap(PROT_WRITE) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno
));
1357 u
->use_mmap
= use_mmap
= FALSE
;
1361 pa_log_debug("Successfully mmap()ed output buffer.");
1362 pa_silence_memory(u
->out_mmap
, u
->out_hwbuf_size
, &ss
);
1366 if ((name
= pa_modargs_get_value(ma
, "sink_name", NULL
)))
1367 namereg_fail
= TRUE
;
1369 name
= name_buf
= pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev
));
1370 namereg_fail
= FALSE
;
1373 pa_sink_new_data_init(&sink_new_data
);
1374 sink_new_data
.driver
= __FILE__
;
1375 sink_new_data
.module
= m
;
1376 pa_sink_new_data_set_name(&sink_new_data
, name
);
1377 sink_new_data
.namereg_fail
= namereg_fail
;
1378 pa_sink_new_data_set_sample_spec(&sink_new_data
, &ss
);
1379 pa_sink_new_data_set_channel_map(&sink_new_data
, &map
);
1380 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_STRING
, dev
);
1381 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_API
, "oss");
1382 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, hwdesc
[0] ? hwdesc
: dev
);
1383 pa_proplist_sets(sink_new_data
.proplist
, PA_PROP_DEVICE_ACCESS_MODE
, use_mmap
? "mmap" : "serial");
1384 pa_proplist_setf(sink_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE
, "%lu", (unsigned long) (u
->out_hwbuf_size
));
1385 pa_proplist_setf(sink_new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE
, "%lu", (unsigned long) (u
->out_fragment_size
));
1387 if (pa_modargs_get_proplist(ma
, "sink_properties", sink_new_data
.proplist
, PA_UPDATE_REPLACE
) < 0) {
1388 pa_log("Invalid properties");
1389 pa_sink_new_data_done(&sink_new_data
);
1393 u
->sink
= pa_sink_new(m
->core
, &sink_new_data
, PA_SINK_HARDWARE
|PA_SINK_LATENCY
);
1394 pa_sink_new_data_done(&sink_new_data
);
1398 pa_log("Failed to create sink object");
1402 u
->sink
->parent
.process_msg
= sink_process_msg
;
1403 u
->sink
->userdata
= u
;
1405 pa_sink_set_asyncmsgq(u
->sink
, u
->thread_mq
.inq
);
1406 pa_sink_set_rtpoll(u
->sink
, u
->rtpoll
);
1407 pa_sink_set_fixed_latency(u
->sink
, pa_bytes_to_usec(u
->out_hwbuf_size
, &u
->sink
->sample_spec
));
1408 u
->sink
->refresh_volume
= TRUE
;
1410 pa_sink_set_max_request(u
->sink
, u
->out_hwbuf_size
);
1413 u
->out_mmap_memblocks
= pa_xnew0(pa_memblock
*, u
->out_nfrags
);
1416 if ((u
->mixer_fd
= pa_oss_open_mixer_for_device(u
->device_name
)) >= 0) {
1417 pa_bool_t do_close
= TRUE
;
1419 if (ioctl(fd
, SOUND_MIXER_READ_DEVMASK
, &u
->mixer_devmask
) < 0)
1420 pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno
));
1423 if (u
->sink
&& (u
->mixer_devmask
& (SOUND_MASK_VOLUME
|SOUND_MASK_PCM
))) {
1424 pa_log_debug("Found hardware mixer track for playback.");
1425 u
->sink
->flags
|= PA_SINK_HW_VOLUME_CTRL
;
1426 pa_sink_set_get_volume_callback(u
->sink
, sink_get_volume
);
1427 pa_sink_set_set_volume_callback(u
->sink
, sink_set_volume
);
1428 u
->sink
->n_volume_steps
= 101;
1432 if (u
->source
&& (u
->mixer_devmask
& (SOUND_MASK_RECLEV
|SOUND_MASK_IGAIN
))) {
1433 pa_log_debug("Found hardware mixer track for recording.");
1434 u
->source
->flags
|= PA_SOURCE_HW_VOLUME_CTRL
;
1435 pa_source_set_get_volume_callback(u
->source
, source_get_volume
);
1436 pa_source_set_set_volume_callback(u
->source
, source_set_volume
);
1437 u
->source
->n_volume_steps
= 101;
1443 pa_close(u
->mixer_fd
);
1445 u
->mixer_devmask
= 0;
1451 pa_assert(u
->source
|| u
->sink
);
1453 pa_memchunk_reset(&u
->memchunk
);
1455 if (!(u
->thread
= pa_thread_new("oss", thread_func
, u
))) {
1456 pa_log("Failed to create thread.");
1460 /* Read mixer settings */
1462 if (sink_new_data
.volume_is_set
) {
1463 if (u
->sink
->set_volume
)
1464 u
->sink
->set_volume(u
->sink
);
1466 if (u
->sink
->get_volume
)
1467 u
->sink
->get_volume(u
->sink
);
1472 if (source_new_data
.volume_is_set
) {
1473 if (u
->source
->set_volume
)
1474 u
->source
->set_volume(u
->source
);
1476 if (u
->source
->get_volume
)
1477 u
->source
->get_volume(u
->source
);
1482 pa_sink_put(u
->sink
);
1484 pa_source_put(u
->source
);
1486 pa_modargs_free(ma
);
1498 pa_modargs_free(ma
);
1503 void pa__done(pa_module
*m
) {
1508 if (!(u
= m
->userdata
))
1512 pa_sink_unlink(u
->sink
);
1515 pa_source_unlink(u
->source
);
1518 pa_asyncmsgq_send(u
->thread_mq
.inq
, NULL
, PA_MESSAGE_SHUTDOWN
, NULL
, 0, NULL
);
1519 pa_thread_free(u
->thread
);
1522 pa_thread_mq_done(&u
->thread_mq
);
1525 pa_sink_unref(u
->sink
);
1528 pa_source_unref(u
->source
);
1530 if (u
->memchunk
.memblock
)
1531 pa_memblock_unref(u
->memchunk
.memblock
);
1534 pa_rtpoll_item_free(u
->rtpoll_item
);
1537 pa_rtpoll_free(u
->rtpoll
);
1539 if (u
->out_mmap_memblocks
) {
1541 for (i
= 0; i
< u
->out_nfrags
; i
++)
1542 if (u
->out_mmap_memblocks
[i
])
1543 pa_memblock_unref_fixed(u
->out_mmap_memblocks
[i
]);
1544 pa_xfree(u
->out_mmap_memblocks
);
1547 if (u
->in_mmap_memblocks
) {
1549 for (i
= 0; i
< u
->in_nfrags
; i
++)
1550 if (u
->in_mmap_memblocks
[i
])
1551 pa_memblock_unref_fixed(u
->in_mmap_memblocks
[i
]);
1552 pa_xfree(u
->in_mmap_memblocks
);
1555 if (u
->in_mmap
&& u
->in_mmap
!= MAP_FAILED
)
1556 munmap(u
->in_mmap
, u
->in_hwbuf_size
);
1558 if (u
->out_mmap
&& u
->out_mmap
!= MAP_FAILED
)
1559 munmap(u
->out_mmap
, u
->out_hwbuf_size
);
1564 if (u
->mixer_fd
>= 0)
1565 pa_close(u
->mixer_fd
);
1567 pa_xfree(u
->device_name
);