2 * QEMU OSS audio driver
4 * Copyright (c) 2003-2005 Vassili Karpov (malc)
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 #include "qemu/osdep.h"
26 #include <sys/ioctl.h>
27 #include <sys/soundcard.h>
28 #include "qemu/main-loop.h"
29 #include "qemu/module.h"
30 #include "qemu/host-utils.h"
34 #define AUDIO_CAP "oss"
35 #include "audio_int.h"
37 #if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY
38 #define USE_DSP_POLICY
41 typedef struct OSSVoiceOut
{
50 typedef struct OSSVoiceIn
{
66 static void GCC_FMT_ATTR (2, 3) oss_logerr (int err
, const char *fmt
, ...)
71 AUD_vlog (AUDIO_CAP
, fmt
, ap
);
74 AUD_log (AUDIO_CAP
, "Reason: %s\n", strerror (err
));
77 static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
86 AUD_log (AUDIO_CAP
, "Could not initialize %s\n", typ
);
89 AUD_vlog (AUDIO_CAP
, fmt
, ap
);
92 AUD_log (AUDIO_CAP
, "Reason: %s\n", strerror (err
));
95 static void oss_anal_close (int *fdp
)
99 qemu_set_fd_handler (*fdp
, NULL
, NULL
, NULL
);
102 oss_logerr (errno
, "Failed to close file(fd=%d)\n", *fdp
);
107 static void oss_helper_poll_out (void *opaque
)
109 AudioState
*s
= opaque
;
110 audio_run(s
, "oss_poll_out");
113 static void oss_helper_poll_in (void *opaque
)
115 AudioState
*s
= opaque
;
116 audio_run(s
, "oss_poll_in");
119 static void oss_poll_out (HWVoiceOut
*hw
)
121 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
123 qemu_set_fd_handler(oss
->fd
, NULL
, oss_helper_poll_out
, hw
->s
);
126 static void oss_poll_in (HWVoiceIn
*hw
)
128 OSSVoiceIn
*oss
= (OSSVoiceIn
*) hw
;
130 qemu_set_fd_handler(oss
->fd
, oss_helper_poll_in
, NULL
, hw
->s
);
133 static int aud_to_ossfmt (AudioFormat fmt
, int endianness
)
136 case AUDIO_FORMAT_S8
:
139 case AUDIO_FORMAT_U8
:
142 case AUDIO_FORMAT_S16
:
150 case AUDIO_FORMAT_U16
:
159 dolog ("Internal logic error: Bad audio format %d\n", fmt
);
167 static int oss_to_audfmt (int ossfmt
, AudioFormat
*fmt
, int *endianness
)
172 *fmt
= AUDIO_FORMAT_S8
;
177 *fmt
= AUDIO_FORMAT_U8
;
182 *fmt
= AUDIO_FORMAT_S16
;
187 *fmt
= AUDIO_FORMAT_U16
;
192 *fmt
= AUDIO_FORMAT_S16
;
197 *fmt
= AUDIO_FORMAT_U16
;
201 dolog ("Unrecognized audio format %d\n", ossfmt
);
208 #if defined DEBUG_MISMATCHES || defined DEBUG
209 static void oss_dump_info (struct oss_params
*req
, struct oss_params
*obt
)
211 dolog ("parameter | requested value | obtained value\n");
212 dolog ("format | %10d | %10d\n", req
->fmt
, obt
->fmt
);
213 dolog ("channels | %10d | %10d\n",
214 req
->nchannels
, obt
->nchannels
);
215 dolog ("frequency | %10d | %10d\n", req
->freq
, obt
->freq
);
216 dolog ("nfrags | %10d | %10d\n", req
->nfrags
, obt
->nfrags
);
217 dolog ("fragsize | %10d | %10d\n",
218 req
->fragsize
, obt
->fragsize
);
222 #ifdef USE_DSP_POLICY
223 static int oss_get_version (int fd
, int *version
, const char *typ
)
225 if (ioctl (fd
, OSS_GETVERSION
, &version
)) {
226 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
228 * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION
229 * since 7.x, but currently only on the mixer device (or in
230 * the Linuxolator), and in the native version that part of
231 * the code is in fact never reached so the ioctl fails anyway.
232 * Until this is fixed, just check the errno and if its what
233 * FreeBSD's sound drivers return atm assume they are new enough.
235 if (errno
== EINVAL
) {
240 oss_logerr2 (errno
, typ
, "Failed to get OSS version\n");
247 static int oss_open(int in
, struct oss_params
*req
, audsettings
*as
,
248 struct oss_params
*obt
, int *pfd
, Audiodev
*dev
)
250 AudiodevOssOptions
*oopts
= &dev
->u
.oss
;
251 AudiodevOssPerDirectionOptions
*opdo
= in
? oopts
->in
: oopts
->out
;
253 int oflags
= (oopts
->has_exclusive
&& oopts
->exclusive
) ? O_EXCL
: 0;
254 audio_buf_info abinfo
;
255 int fmt
, freq
, nchannels
;
257 const char *dspname
= opdo
->has_dev
? opdo
->dev
: "/dev/dsp";
258 const char *typ
= in
? "ADC" : "DAC";
259 #ifdef USE_DSP_POLICY
260 int policy
= oopts
->has_dsp_policy
? oopts
->dsp_policy
: 5;
263 /* Kludge needed to have working mmap on Linux */
264 oflags
|= (oopts
->has_try_mmap
&& oopts
->try_mmap
) ?
265 O_RDWR
: (in
? O_RDONLY
: O_WRONLY
);
267 fd
= open (dspname
, oflags
| O_NONBLOCK
);
269 oss_logerr2 (errno
, typ
, "Failed to open `%s'\n", dspname
);
274 nchannels
= req
->nchannels
;
276 req
->nfrags
= opdo
->has_buffer_count
? opdo
->buffer_count
: 4;
277 req
->fragsize
= audio_buffer_bytes(
278 qapi_AudiodevOssPerDirectionOptions_base(opdo
), as
, 23220);
280 if (ioctl (fd
, SNDCTL_DSP_SAMPLESIZE
, &fmt
)) {
281 oss_logerr2 (errno
, typ
, "Failed to set sample size %d\n", req
->fmt
);
285 if (ioctl (fd
, SNDCTL_DSP_CHANNELS
, &nchannels
)) {
286 oss_logerr2 (errno
, typ
, "Failed to set number of channels %d\n",
291 if (ioctl (fd
, SNDCTL_DSP_SPEED
, &freq
)) {
292 oss_logerr2 (errno
, typ
, "Failed to set frequency %d\n", req
->freq
);
296 if (ioctl (fd
, SNDCTL_DSP_NONBLOCK
, NULL
)) {
297 oss_logerr2 (errno
, typ
, "Failed to set non-blocking mode\n");
301 #ifdef USE_DSP_POLICY
305 if (!oss_get_version (fd
, &version
, typ
)) {
306 trace_oss_version(version
);
308 if (version
>= 0x040000) {
309 int policy2
= policy
;
310 if (ioctl(fd
, SNDCTL_DSP_POLICY
, &policy2
)) {
311 oss_logerr2 (errno
, typ
,
312 "Failed to set timing policy to %d\n",
323 int mmmmssss
= (req
->nfrags
<< 16) | ctz32 (req
->fragsize
);
324 if (ioctl (fd
, SNDCTL_DSP_SETFRAGMENT
, &mmmmssss
)) {
325 oss_logerr2 (errno
, typ
, "Failed to set buffer length (%d, %d)\n",
326 req
->nfrags
, req
->fragsize
);
331 if (ioctl (fd
, in
? SNDCTL_DSP_GETISPACE
: SNDCTL_DSP_GETOSPACE
, &abinfo
)) {
332 oss_logerr2 (errno
, typ
, "Failed to get buffer length\n");
336 if (!abinfo
.fragstotal
|| !abinfo
.fragsize
) {
337 AUD_log (AUDIO_CAP
, "Returned bogus buffer information(%d, %d) for %s\n",
338 abinfo
.fragstotal
, abinfo
.fragsize
, typ
);
343 obt
->nchannels
= nchannels
;
345 obt
->nfrags
= abinfo
.fragstotal
;
346 obt
->fragsize
= abinfo
.fragsize
;
349 #ifdef DEBUG_MISMATCHES
350 if ((req
->fmt
!= obt
->fmt
) ||
351 (req
->nchannels
!= obt
->nchannels
) ||
352 (req
->freq
!= obt
->freq
) ||
353 (req
->fragsize
!= obt
->fragsize
) ||
354 (req
->nfrags
!= obt
->nfrags
)) {
355 dolog ("Audio parameters mismatch\n");
356 oss_dump_info (req
, obt
);
361 oss_dump_info (req
, obt
);
366 oss_anal_close (&fd
);
370 static size_t oss_get_available_bytes(OSSVoiceOut
*oss
)
373 struct count_info cntinfo
;
374 assert(oss
->mmapped
);
376 err
= ioctl(oss
->fd
, SNDCTL_DSP_GETOPTR
, &cntinfo
);
378 oss_logerr(errno
, "SNDCTL_DSP_GETOPTR failed\n");
382 return audio_ring_dist(cntinfo
.ptr
, oss
->hw
.pos_emul
, oss
->hw
.size_emul
);
385 static void oss_run_buffer_out(HWVoiceOut
*hw
)
387 OSSVoiceOut
*oss
= (OSSVoiceOut
*)hw
;
390 audio_generic_run_buffer_out(hw
);
394 static void *oss_get_buffer_out(HWVoiceOut
*hw
, size_t *size
)
396 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
398 *size
= MIN(oss_get_available_bytes(oss
), hw
->size_emul
- hw
->pos_emul
);
399 return hw
->buf_emul
+ hw
->pos_emul
;
401 return audio_generic_get_buffer_out(hw
, size
);
405 static size_t oss_put_buffer_out(HWVoiceOut
*hw
, void *buf
, size_t size
)
407 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
409 assert(buf
== hw
->buf_emul
+ hw
->pos_emul
&& size
< hw
->size_emul
);
411 hw
->pos_emul
= (hw
->pos_emul
+ size
) % hw
->size_emul
;
414 return audio_generic_put_buffer_out(hw
, buf
, size
);
418 static size_t oss_write(HWVoiceOut
*hw
, void *buf
, size_t len
)
420 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
425 len
= MIN(len
, oss_get_available_bytes(oss
));
429 size_t to_copy
= MIN(len
, hw
->size_emul
- hw
->pos_emul
);
430 memcpy(hw
->buf_emul
+ hw
->pos_emul
, buf
, to_copy
);
432 hw
->pos_emul
= (hw
->pos_emul
+ to_copy
) % hw
->size_emul
;
441 ssize_t bytes_written
;
442 void *pcm
= advance(buf
, pos
);
444 bytes_written
= write(oss
->fd
, pcm
, len
);
445 if (bytes_written
< 0) {
446 if (errno
!= EAGAIN
) {
447 oss_logerr(errno
, "failed to write %zu bytes\n",
453 pos
+= bytes_written
;
454 if (bytes_written
< len
) {
457 len
-= bytes_written
;
462 static void oss_fini_out (HWVoiceOut
*hw
)
465 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
467 ldebug ("oss_fini\n");
468 oss_anal_close (&oss
->fd
);
470 if (oss
->mmapped
&& hw
->buf_emul
) {
471 err
= munmap(hw
->buf_emul
, hw
->size_emul
);
473 oss_logerr(errno
, "Failed to unmap buffer %p, size %zu\n",
474 hw
->buf_emul
, hw
->size_emul
);
480 static int oss_init_out(HWVoiceOut
*hw
, struct audsettings
*as
,
483 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
484 struct oss_params req
, obt
;
488 AudioFormat effective_fmt
;
489 struct audsettings obt_as
;
490 Audiodev
*dev
= drv_opaque
;
491 AudiodevOssOptions
*oopts
= &dev
->u
.oss
;
495 req
.fmt
= aud_to_ossfmt (as
->fmt
, as
->endianness
);
497 req
.nchannels
= as
->nchannels
;
499 if (oss_open(0, &req
, as
, &obt
, &fd
, dev
)) {
503 err
= oss_to_audfmt (obt
.fmt
, &effective_fmt
, &endianness
);
505 oss_anal_close (&fd
);
509 obt_as
.freq
= obt
.freq
;
510 obt_as
.nchannels
= obt
.nchannels
;
511 obt_as
.fmt
= effective_fmt
;
512 obt_as
.endianness
= endianness
;
514 audio_pcm_init_info (&hw
->info
, &obt_as
);
515 oss
->nfrags
= obt
.nfrags
;
516 oss
->fragsize
= obt
.fragsize
;
518 if (obt
.nfrags
* obt
.fragsize
% hw
->info
.bytes_per_frame
) {
519 dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
520 obt
.nfrags
* obt
.fragsize
, hw
->info
.bytes_per_frame
);
523 hw
->samples
= (obt
.nfrags
* obt
.fragsize
) / hw
->info
.bytes_per_frame
;
526 if (oopts
->has_try_mmap
&& oopts
->try_mmap
) {
527 hw
->size_emul
= hw
->samples
* hw
->info
.bytes_per_frame
;
531 PROT_READ
| PROT_WRITE
,
536 if (hw
->buf_emul
== MAP_FAILED
) {
537 oss_logerr(errno
, "Failed to map %zu bytes of DAC\n",
543 if (ioctl (fd
, SNDCTL_DSP_SETTRIGGER
, &trig
) < 0) {
544 oss_logerr (errno
, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
547 trig
= PCM_ENABLE_OUTPUT
;
548 if (ioctl (fd
, SNDCTL_DSP_SETTRIGGER
, &trig
) < 0) {
551 "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
560 err
= munmap(hw
->buf_emul
, hw
->size_emul
);
562 oss_logerr(errno
, "Failed to unmap buffer %p size %zu\n",
563 hw
->buf_emul
, hw
->size_emul
);
575 static void oss_enable_out(HWVoiceOut
*hw
, bool enable
)
578 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
579 AudiodevOssPerDirectionOptions
*opdo
= oss
->dev
->u
.oss
.out
;
582 hw
->poll_mode
= opdo
->try_poll
;
584 ldebug("enabling voice\n");
593 audio_pcm_info_clear_buf(&hw
->info
, hw
->buf_emul
, hw
->samples
);
594 trig
= PCM_ENABLE_OUTPUT
;
595 if (ioctl(oss
->fd
, SNDCTL_DSP_SETTRIGGER
, &trig
) < 0) {
597 "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n");
602 qemu_set_fd_handler (oss
->fd
, NULL
, NULL
, NULL
);
610 ldebug ("disabling voice\n");
612 if (ioctl (oss
->fd
, SNDCTL_DSP_SETTRIGGER
, &trig
) < 0) {
613 oss_logerr (errno
, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
619 static int oss_init_in(HWVoiceIn
*hw
, struct audsettings
*as
, void *drv_opaque
)
621 OSSVoiceIn
*oss
= (OSSVoiceIn
*) hw
;
622 struct oss_params req
, obt
;
626 AudioFormat effective_fmt
;
627 struct audsettings obt_as
;
628 Audiodev
*dev
= drv_opaque
;
632 req
.fmt
= aud_to_ossfmt (as
->fmt
, as
->endianness
);
634 req
.nchannels
= as
->nchannels
;
635 if (oss_open(1, &req
, as
, &obt
, &fd
, dev
)) {
639 err
= oss_to_audfmt (obt
.fmt
, &effective_fmt
, &endianness
);
641 oss_anal_close (&fd
);
645 obt_as
.freq
= obt
.freq
;
646 obt_as
.nchannels
= obt
.nchannels
;
647 obt_as
.fmt
= effective_fmt
;
648 obt_as
.endianness
= endianness
;
650 audio_pcm_init_info (&hw
->info
, &obt_as
);
651 oss
->nfrags
= obt
.nfrags
;
652 oss
->fragsize
= obt
.fragsize
;
654 if (obt
.nfrags
* obt
.fragsize
% hw
->info
.bytes_per_frame
) {
655 dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
656 obt
.nfrags
* obt
.fragsize
, hw
->info
.bytes_per_frame
);
659 hw
->samples
= (obt
.nfrags
* obt
.fragsize
) / hw
->info
.bytes_per_frame
;
666 static void oss_fini_in (HWVoiceIn
*hw
)
668 OSSVoiceIn
*oss
= (OSSVoiceIn
*) hw
;
670 oss_anal_close (&oss
->fd
);
673 static size_t oss_read(HWVoiceIn
*hw
, void *buf
, size_t len
)
675 OSSVoiceIn
*oss
= (OSSVoiceIn
*) hw
;
681 void *dst
= advance(buf
, pos
);
682 nread
= read(oss
->fd
, dst
, len
);
690 oss_logerr(errno
, "Failed to read %zu bytes of audio (to %p)\n",
703 static void oss_enable_in(HWVoiceIn
*hw
, bool enable
)
705 OSSVoiceIn
*oss
= (OSSVoiceIn
*) hw
;
706 AudiodevOssPerDirectionOptions
*opdo
= oss
->dev
->u
.oss
.out
;
709 hw
->poll_mode
= opdo
->try_poll
;
716 qemu_set_fd_handler (oss
->fd
, NULL
, NULL
, NULL
);
722 static void oss_init_per_direction(AudiodevOssPerDirectionOptions
*opdo
)
724 if (!opdo
->has_try_poll
) {
725 opdo
->try_poll
= true;
726 opdo
->has_try_poll
= true;
730 static void *oss_audio_init(Audiodev
*dev
)
732 AudiodevOssOptions
*oopts
;
733 assert(dev
->driver
== AUDIODEV_DRIVER_OSS
);
736 oss_init_per_direction(oopts
->in
);
737 oss_init_per_direction(oopts
->out
);
739 if (access(oopts
->in
->has_dev
? oopts
->in
->dev
: "/dev/dsp",
741 access(oopts
->out
->has_dev
? oopts
->out
->dev
: "/dev/dsp",
748 static void oss_audio_fini (void *opaque
)
752 static struct audio_pcm_ops oss_pcm_ops
= {
753 .init_out
= oss_init_out
,
754 .fini_out
= oss_fini_out
,
756 .run_buffer_out
= oss_run_buffer_out
,
757 .get_buffer_out
= oss_get_buffer_out
,
758 .put_buffer_out
= oss_put_buffer_out
,
759 .enable_out
= oss_enable_out
,
761 .init_in
= oss_init_in
,
762 .fini_in
= oss_fini_in
,
764 .enable_in
= oss_enable_in
767 static struct audio_driver oss_audio_driver
= {
769 .descr
= "OSS http://www.opensound.com",
770 .init
= oss_audio_init
,
771 .fini
= oss_audio_fini
,
772 .pcm_ops
= &oss_pcm_ops
,
774 .max_voices_out
= INT_MAX
,
775 .max_voices_in
= INT_MAX
,
776 .voice_size_out
= sizeof (OSSVoiceOut
),
777 .voice_size_in
= sizeof (OSSVoiceIn
)
780 static void register_audio_oss(void)
782 audio_driver_register(&oss_audio_driver
);
784 type_init(register_audio_oss
);