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_get_buffer_out(HWVoiceOut
*hw
, size_t *size
)
387 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
389 *size
= MIN(oss_get_available_bytes(oss
), hw
->size_emul
- hw
->pos_emul
);
390 return hw
->buf_emul
+ hw
->pos_emul
;
392 return audio_generic_get_buffer_out(hw
, size
);
396 static size_t oss_put_buffer_out(HWVoiceOut
*hw
, void *buf
, size_t size
)
398 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
400 assert(buf
== hw
->buf_emul
+ hw
->pos_emul
&& size
< hw
->size_emul
);
402 hw
->pos_emul
= (hw
->pos_emul
+ size
) % hw
->size_emul
;
405 return audio_generic_put_buffer_out(hw
, buf
, size
);
409 static size_t oss_write(HWVoiceOut
*hw
, void *buf
, size_t len
)
411 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
416 len
= MIN(len
, oss_get_available_bytes(oss
));
420 size_t to_copy
= MIN(len
, hw
->size_emul
- hw
->pos_emul
);
421 memcpy(hw
->buf_emul
+ hw
->pos_emul
, buf
, to_copy
);
423 hw
->pos_emul
= (hw
->pos_emul
+ to_copy
) % hw
->pos_emul
;
432 ssize_t bytes_written
;
433 void *pcm
= advance(buf
, pos
);
435 bytes_written
= write(oss
->fd
, pcm
, len
);
436 if (bytes_written
< 0) {
437 if (errno
!= EAGAIN
) {
438 oss_logerr(errno
, "failed to write %zu bytes\n",
444 pos
+= bytes_written
;
445 if (bytes_written
< len
) {
448 len
-= bytes_written
;
453 static void oss_fini_out (HWVoiceOut
*hw
)
456 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
458 ldebug ("oss_fini\n");
459 oss_anal_close (&oss
->fd
);
461 if (oss
->mmapped
&& hw
->buf_emul
) {
462 err
= munmap(hw
->buf_emul
, hw
->size_emul
);
464 oss_logerr(errno
, "Failed to unmap buffer %p, size %zu\n",
465 hw
->buf_emul
, hw
->size_emul
);
471 static int oss_init_out(HWVoiceOut
*hw
, struct audsettings
*as
,
474 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
475 struct oss_params req
, obt
;
479 AudioFormat effective_fmt
;
480 struct audsettings obt_as
;
481 Audiodev
*dev
= drv_opaque
;
482 AudiodevOssOptions
*oopts
= &dev
->u
.oss
;
486 req
.fmt
= aud_to_ossfmt (as
->fmt
, as
->endianness
);
488 req
.nchannels
= as
->nchannels
;
490 if (oss_open(0, &req
, as
, &obt
, &fd
, dev
)) {
494 err
= oss_to_audfmt (obt
.fmt
, &effective_fmt
, &endianness
);
496 oss_anal_close (&fd
);
500 obt_as
.freq
= obt
.freq
;
501 obt_as
.nchannels
= obt
.nchannels
;
502 obt_as
.fmt
= effective_fmt
;
503 obt_as
.endianness
= endianness
;
505 audio_pcm_init_info (&hw
->info
, &obt_as
);
506 oss
->nfrags
= obt
.nfrags
;
507 oss
->fragsize
= obt
.fragsize
;
509 if (obt
.nfrags
* obt
.fragsize
& hw
->info
.align
) {
510 dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
511 obt
.nfrags
* obt
.fragsize
, hw
->info
.align
+ 1);
514 hw
->samples
= (obt
.nfrags
* obt
.fragsize
) >> hw
->info
.shift
;
517 if (oopts
->has_try_mmap
&& oopts
->try_mmap
) {
518 hw
->size_emul
= hw
->samples
<< hw
->info
.shift
;
522 PROT_READ
| PROT_WRITE
,
527 if (hw
->buf_emul
== MAP_FAILED
) {
528 oss_logerr(errno
, "Failed to map %zu bytes of DAC\n",
534 if (ioctl (fd
, SNDCTL_DSP_SETTRIGGER
, &trig
) < 0) {
535 oss_logerr (errno
, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
538 trig
= PCM_ENABLE_OUTPUT
;
539 if (ioctl (fd
, SNDCTL_DSP_SETTRIGGER
, &trig
) < 0) {
542 "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
551 err
= munmap(hw
->buf_emul
, hw
->size_emul
);
553 oss_logerr(errno
, "Failed to unmap buffer %p size %zu\n",
554 hw
->buf_emul
, hw
->size_emul
);
566 static void oss_enable_out(HWVoiceOut
*hw
, bool enable
)
569 OSSVoiceOut
*oss
= (OSSVoiceOut
*) hw
;
570 AudiodevOssPerDirectionOptions
*opdo
= oss
->dev
->u
.oss
.out
;
573 bool poll_mode
= opdo
->try_poll
;
575 ldebug("enabling voice\n");
580 hw
->poll_mode
= poll_mode
;
586 audio_pcm_info_clear_buf(&hw
->info
, hw
->buf_emul
, hw
->mix_buf
->size
);
587 trig
= PCM_ENABLE_OUTPUT
;
588 if (ioctl(oss
->fd
, SNDCTL_DSP_SETTRIGGER
, &trig
) < 0) {
590 "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n");
595 qemu_set_fd_handler (oss
->fd
, NULL
, NULL
, NULL
);
603 ldebug ("disabling voice\n");
605 if (ioctl (oss
->fd
, SNDCTL_DSP_SETTRIGGER
, &trig
) < 0) {
606 oss_logerr (errno
, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
612 static int oss_init_in(HWVoiceIn
*hw
, struct audsettings
*as
, void *drv_opaque
)
614 OSSVoiceIn
*oss
= (OSSVoiceIn
*) hw
;
615 struct oss_params req
, obt
;
619 AudioFormat effective_fmt
;
620 struct audsettings obt_as
;
621 Audiodev
*dev
= drv_opaque
;
625 req
.fmt
= aud_to_ossfmt (as
->fmt
, as
->endianness
);
627 req
.nchannels
= as
->nchannels
;
628 if (oss_open(1, &req
, as
, &obt
, &fd
, dev
)) {
632 err
= oss_to_audfmt (obt
.fmt
, &effective_fmt
, &endianness
);
634 oss_anal_close (&fd
);
638 obt_as
.freq
= obt
.freq
;
639 obt_as
.nchannels
= obt
.nchannels
;
640 obt_as
.fmt
= effective_fmt
;
641 obt_as
.endianness
= endianness
;
643 audio_pcm_init_info (&hw
->info
, &obt_as
);
644 oss
->nfrags
= obt
.nfrags
;
645 oss
->fragsize
= obt
.fragsize
;
647 if (obt
.nfrags
* obt
.fragsize
& hw
->info
.align
) {
648 dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
649 obt
.nfrags
* obt
.fragsize
, hw
->info
.align
+ 1);
652 hw
->samples
= (obt
.nfrags
* obt
.fragsize
) >> hw
->info
.shift
;
659 static void oss_fini_in (HWVoiceIn
*hw
)
661 OSSVoiceIn
*oss
= (OSSVoiceIn
*) hw
;
663 oss_anal_close (&oss
->fd
);
666 static size_t oss_read(HWVoiceIn
*hw
, void *buf
, size_t len
)
668 OSSVoiceIn
*oss
= (OSSVoiceIn
*) hw
;
674 void *dst
= advance(buf
, pos
);
675 nread
= read(oss
->fd
, dst
, len
);
683 oss_logerr(errno
, "Failed to read %zu bytes of audio (to %p)\n",
696 static void oss_enable_in(HWVoiceIn
*hw
, bool enable
)
698 OSSVoiceIn
*oss
= (OSSVoiceIn
*) hw
;
699 AudiodevOssPerDirectionOptions
*opdo
= oss
->dev
->u
.oss
.out
;
702 bool poll_mode
= opdo
->try_poll
;
708 hw
->poll_mode
= poll_mode
;
712 qemu_set_fd_handler (oss
->fd
, NULL
, NULL
, NULL
);
717 static void oss_init_per_direction(AudiodevOssPerDirectionOptions
*opdo
)
719 if (!opdo
->has_try_poll
) {
720 opdo
->try_poll
= true;
721 opdo
->has_try_poll
= true;
725 static void *oss_audio_init(Audiodev
*dev
)
727 AudiodevOssOptions
*oopts
;
728 assert(dev
->driver
== AUDIODEV_DRIVER_OSS
);
731 oss_init_per_direction(oopts
->in
);
732 oss_init_per_direction(oopts
->out
);
734 if (access(oopts
->in
->has_dev
? oopts
->in
->dev
: "/dev/dsp",
736 access(oopts
->out
->has_dev
? oopts
->out
->dev
: "/dev/dsp",
743 static void oss_audio_fini (void *opaque
)
747 static struct audio_pcm_ops oss_pcm_ops
= {
748 .init_out
= oss_init_out
,
749 .fini_out
= oss_fini_out
,
751 .get_buffer_out
= oss_get_buffer_out
,
752 .put_buffer_out
= oss_put_buffer_out
,
753 .enable_out
= oss_enable_out
,
755 .init_in
= oss_init_in
,
756 .fini_in
= oss_fini_in
,
758 .enable_in
= oss_enable_in
761 static struct audio_driver oss_audio_driver
= {
763 .descr
= "OSS http://www.opensound.com",
764 .init
= oss_audio_init
,
765 .fini
= oss_audio_fini
,
766 .pcm_ops
= &oss_pcm_ops
,
768 .max_voices_out
= INT_MAX
,
769 .max_voices_in
= INT_MAX
,
770 .voice_size_out
= sizeof (OSSVoiceOut
),
771 .voice_size_in
= sizeof (OSSVoiceIn
)
774 static void register_audio_oss(void)
776 audio_driver_register(&oss_audio_driver
);
778 type_init(register_audio_oss
);