1/*
2 * QEMU OSS audio driver
3 *
4 * Copyright (c) 2003-2005 Vassili Karpov (malc)
5 *
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:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
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
22 * THE SOFTWARE.
23 */
24#include <stdlib.h>
25#include <sys/mman.h>
26#include <sys/types.h>
27#include <sys/ioctl.h>
28#ifdef __OpenBSD__
29#include <soundcard.h>
30#else
31#include <sys/soundcard.h>
32#endif
33#include "qemu-common.h"
34#include "host-utils.h"
35#include "qemu-char.h"
36#include "audio.h"
37
38#define AUDIO_CAP "oss"
39#include "audio_int.h"
40
41#if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY
42#define USE_DSP_POLICY
43#endif
44
45typedef struct OSSVoiceOut {
46    HWVoiceOut hw;
47    void *pcm_buf;
48    int fd;
49    int wpos;
50    int nfrags;
51    int fragsize;
52    int mmapped;
53    int pending;
54} OSSVoiceOut;
55
56typedef struct OSSVoiceIn {
57    HWVoiceIn hw;
58    void *pcm_buf;
59    int fd;
60    int nfrags;
61    int fragsize;
62} OSSVoiceIn;
63
64static struct {
65    int try_mmap;
66    int nfrags;
67    int fragsize;
68    const char *devpath_out;
69    const char *devpath_in;
70    int debug;
71    int exclusive;
72    int policy;
73} conf = {
74    .try_mmap = 0,
75    .nfrags = 4,
76    .fragsize = 4096,
77    .devpath_out = "/dev/dsp",
78    .devpath_in = "/dev/dsp",
79    .debug = 0,
80    .exclusive = 0,
81    .policy = 5
82};
83
84struct oss_params {
85    int freq;
86    audfmt_e fmt;
87    int nchannels;
88    int nfrags;
89    int fragsize;
90};
91
92static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
93{
94    va_list ap;
95
96    va_start (ap, fmt);
97    AUD_vlog (AUDIO_CAP, fmt, ap);
98    va_end (ap);
99
100    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
101}
102
103static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
104    int err,
105    const char *typ,
106    const char *fmt,
107    ...
108    )
109{
110    va_list ap;
111
112    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
113
114    va_start (ap, fmt);
115    AUD_vlog (AUDIO_CAP, fmt, ap);
116    va_end (ap);
117
118    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
119}
120
121static void oss_anal_close (int *fdp)
122{
123    int err;
124
125    qemu_set_fd_handler (*fdp, NULL, NULL, NULL);
126    err = close (*fdp);
127    if (err) {
128        oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
129    }
130    *fdp = -1;
131}
132
133static void oss_helper_poll_out (void *opaque)
134{
135    (void) opaque;
136    audio_run ("oss_poll_out");
137}
138
139static void oss_helper_poll_in (void *opaque)
140{
141    (void) opaque;
142    audio_run ("oss_poll_in");
143}
144
145static int oss_poll_out (HWVoiceOut *hw)
146{
147    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
148
149    return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
150}
151
152static int oss_poll_in (HWVoiceIn *hw)
153{
154    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
155
156    return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
157}
158
159static int oss_write (SWVoiceOut *sw, void *buf, int len)
160{
161    return audio_pcm_sw_write (sw, buf, len);
162}
163
164static int aud_to_ossfmt (audfmt_e fmt)
165{
166    switch (fmt) {
167    case AUD_FMT_S8:
168        return AFMT_S8;
169
170    case AUD_FMT_U8:
171        return AFMT_U8;
172
173    case AUD_FMT_S16:
174        return AFMT_S16_LE;
175
176    case AUD_FMT_U16:
177        return AFMT_U16_LE;
178
179    default:
180        dolog ("Internal logic error: Bad audio format %d\n", fmt);
181#ifdef DEBUG_AUDIO
182        abort ();
183#endif
184        return AFMT_U8;
185    }
186}
187
188static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
189{
190    switch (ossfmt) {
191    case AFMT_S8:
192        *endianness = 0;
193        *fmt = AUD_FMT_S8;
194        break;
195
196    case AFMT_U8:
197        *endianness = 0;
198        *fmt = AUD_FMT_U8;
199        break;
200
201    case AFMT_S16_LE:
202        *endianness = 0;
203        *fmt = AUD_FMT_S16;
204        break;
205
206    case AFMT_U16_LE:
207        *endianness = 0;
208        *fmt = AUD_FMT_U16;
209        break;
210
211    case AFMT_S16_BE:
212        *endianness = 1;
213        *fmt = AUD_FMT_S16;
214        break;
215
216    case AFMT_U16_BE:
217        *endianness = 1;
218        *fmt = AUD_FMT_U16;
219        break;
220
221    default:
222        dolog ("Unrecognized audio format %d\n", ossfmt);
223        return -1;
224    }
225
226    return 0;
227}
228
229#if defined DEBUG_MISMATCHES || defined DEBUG
230static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
231{
232    dolog ("parameter | requested value | obtained value\n");
233    dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);
234    dolog ("channels  |      %10d |     %10d\n",
235           req->nchannels, obt->nchannels);
236    dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
237    dolog ("nfrags    |      %10d |     %10d\n", req->nfrags, obt->nfrags);
238    dolog ("fragsize  |      %10d |     %10d\n",
239           req->fragsize, obt->fragsize);
240}
241#endif
242
243#ifdef USE_DSP_POLICY
244static int oss_get_version (int fd, int *version, const char *typ)
245{
246    if (ioctl (fd, OSS_GETVERSION, &version)) {
247#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
248        /*
249         * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION
250         * since 7.x, but currently only on the mixer device (or in
251         * the Linuxolator), and in the native version that part of
252         * the code is in fact never reached so the ioctl fails anyway.
253         * Until this is fixed, just check the errno and if its what
254         * FreeBSD's sound drivers return atm assume they are new enough.
255         */
256        if (errno == EINVAL) {
257            *version = 0x040000;
258            return 0;
259        }
260#endif
261        oss_logerr2 (errno, typ, "Failed to get OSS version\n");
262        return -1;
263    }
264    return 0;
265}
266#endif
267
268static int oss_open (int in, struct oss_params *req,
269                     struct oss_params *obt, int *pfd)
270{
271    int fd;
272    int oflags = conf.exclusive ? O_EXCL : 0;
273    audio_buf_info abinfo;
274    int fmt, freq, nchannels;
275    int setfragment = 1;
276    const char *dspname = in ? conf.devpath_in : conf.devpath_out;
277    const char *typ = in ? "ADC" : "DAC";
278
279    /* Kludge needed to have working mmap on Linux */
280    oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
281
282    fd = open (dspname, oflags | O_NONBLOCK);
283    if (-1 == fd) {
284        oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
285        return -1;
286    }
287
288    freq = req->freq;
289    nchannels = req->nchannels;
290    fmt = req->fmt;
291
292    if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
293        oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
294        goto err;
295    }
296
297    if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
298        oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
299                     req->nchannels);
300        goto err;
301    }
302
303    if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
304        oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
305        goto err;
306    }
307
308    if (ioctl (fd, SNDCTL_DSP_NONBLOCK, NULL)) {
309        oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n");
310        goto err;
311    }
312
313#ifdef USE_DSP_POLICY
314    if (conf.policy >= 0) {
315        int version;
316
317        if (!oss_get_version (fd, &version, typ)) {
318            if (conf.debug) {
319                dolog ("OSS version = %#x\n", version);
320            }
321
322            if (version >= 0x040000) {
323                int policy = conf.policy;
324                if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
325                    oss_logerr2 (errno, typ,
326                                 "Failed to set timing policy to %d\n",
327                                 conf.policy);
328                    goto err;
329                }
330                setfragment = 0;
331            }
332        }
333    }
334#endif
335
336    if (setfragment) {
337        int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize);
338        if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
339            oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
340                         req->nfrags, req->fragsize);
341            goto err;
342        }
343    }
344
345    if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
346        oss_logerr2 (errno, typ, "Failed to get buffer length\n");
347        goto err;
348    }
349
350    if (!abinfo.fragstotal || !abinfo.fragsize) {
351        AUD_log (AUDIO_CAP, "Returned bogus buffer information(%d, %d) for %s\n",
352                 abinfo.fragstotal, abinfo.fragsize, typ);
353        goto err;
354    }
355
356    obt->fmt = fmt;
357    obt->nchannels = nchannels;
358    obt->freq = freq;
359    obt->nfrags = abinfo.fragstotal;
360    obt->fragsize = abinfo.fragsize;
361    *pfd = fd;
362
363#ifdef DEBUG_MISMATCHES
364    if ((req->fmt != obt->fmt) ||
365        (req->nchannels != obt->nchannels) ||
366        (req->freq != obt->freq) ||
367        (req->fragsize != obt->fragsize) ||
368        (req->nfrags != obt->nfrags)) {
369        dolog ("Audio parameters mismatch\n");
370        oss_dump_info (req, obt);
371    }
372#endif
373
374#ifdef DEBUG
375    oss_dump_info (req, obt);
376#endif
377    return 0;
378
379 err:
380    oss_anal_close (&fd);
381    return -1;
382}
383
384static void oss_write_pending (OSSVoiceOut *oss)
385{
386    HWVoiceOut *hw = &oss->hw;
387
388    if (oss->mmapped) {
389        return;
390    }
391
392    while (oss->pending) {
393        int samples_written;
394        ssize_t bytes_written;
395        int samples_till_end = hw->samples - oss->wpos;
396        int samples_to_write = audio_MIN (oss->pending, samples_till_end);
397        int bytes_to_write = samples_to_write << hw->info.shift;
398        void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
399
400        bytes_written = write (oss->fd, pcm, bytes_to_write);
401        if (bytes_written < 0) {
402            if (errno != EAGAIN) {
403                oss_logerr (errno, "failed to write %d bytes\n",
404                            bytes_to_write);
405            }
406            break;
407        }
408
409        if (bytes_written & hw->info.align) {
410            dolog ("misaligned write asked for %d, but got %zd\n",
411                   bytes_to_write, bytes_written);
412            return;
413        }
414
415        samples_written = bytes_written >> hw->info.shift;
416        oss->pending -= samples_written;
417        oss->wpos = (oss->wpos + samples_written) % hw->samples;
418        if (bytes_written - bytes_to_write) {
419            break;
420        }
421    }
422}
423
424static int oss_run_out (HWVoiceOut *hw, int live)
425{
426    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
427    int err, decr;
428    struct audio_buf_info abinfo;
429    struct count_info cntinfo;
430    int bufsize;
431
432    bufsize = hw->samples << hw->info.shift;
433
434    if (oss->mmapped) {
435        int bytes, pos;
436
437        err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
438        if (err < 0) {
439            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
440            return 0;
441        }
442
443        pos = hw->rpos << hw->info.shift;
444        bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
445        decr = audio_MIN (bytes >> hw->info.shift, live);
446    }
447    else {
448        err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
449        if (err < 0) {
450            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
451            return 0;
452        }
453
454        if (abinfo.bytes > bufsize) {
455            if (conf.debug) {
456                dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
457                       "please report your OS/audio hw to av1474@comtv.ru\n",
458                       abinfo.bytes, bufsize);
459            }
460            abinfo.bytes = bufsize;
461        }
462
463        if (abinfo.bytes < 0) {
464            if (conf.debug) {
465                dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
466                       abinfo.bytes, bufsize);
467            }
468            return 0;
469        }
470
471        decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
472        if (!decr) {
473            return 0;
474        }
475    }
476
477    decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
478    oss->pending += decr;
479    oss_write_pending (oss);
480
481    return decr;
482}
483
484static void oss_fini_out (HWVoiceOut *hw)
485{
486    int err;
487    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
488
489    ldebug ("oss_fini\n");
490    oss_anal_close (&oss->fd);
491
492    if (oss->pcm_buf) {
493        if (oss->mmapped) {
494            err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
495            if (err) {
496                oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
497                            oss->pcm_buf, hw->samples << hw->info.shift);
498            }
499        }
500        else {
501            qemu_free (oss->pcm_buf);
502        }
503        oss->pcm_buf = NULL;
504    }
505}
506
507static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
508{
509    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
510    struct oss_params req, obt;
511    int endianness;
512    int err;
513    int fd;
514    audfmt_e effective_fmt;
515    struct audsettings obt_as;
516
517    oss->fd = -1;
518
519    req.fmt = aud_to_ossfmt (as->fmt);
520    req.freq = as->freq;
521    req.nchannels = as->nchannels;
522    req.fragsize = conf.fragsize;
523    req.nfrags = conf.nfrags;
524
525    if (oss_open (0, &req, &obt, &fd)) {
526        return -1;
527    }
528
529    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
530    if (err) {
531        oss_anal_close (&fd);
532        return -1;
533    }
534
535    obt_as.freq = obt.freq;
536    obt_as.nchannels = obt.nchannels;
537    obt_as.fmt = effective_fmt;
538    obt_as.endianness = endianness;
539
540    audio_pcm_init_info (&hw->info, &obt_as);
541    oss->nfrags = obt.nfrags;
542    oss->fragsize = obt.fragsize;
543
544    if (obt.nfrags * obt.fragsize & hw->info.align) {
545        dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
546               obt.nfrags * obt.fragsize, hw->info.align + 1);
547    }
548
549    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
550
551    oss->mmapped = 0;
552    if (conf.try_mmap) {
553        oss->pcm_buf = mmap (
554            NULL,
555            hw->samples << hw->info.shift,
556            PROT_READ | PROT_WRITE,
557            MAP_SHARED,
558            fd,
559            0
560            );
561        if (oss->pcm_buf == MAP_FAILED) {
562            oss_logerr (errno, "Failed to map %d bytes of DAC\n",
563                        hw->samples << hw->info.shift);
564        }
565        else {
566            int err;
567            int trig = 0;
568            if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
569                oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
570            }
571            else {
572                trig = PCM_ENABLE_OUTPUT;
573                if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
574                    oss_logerr (
575                        errno,
576                        "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
577                        );
578                }
579                else {
580                    oss->mmapped = 1;
581                }
582            }
583
584            if (!oss->mmapped) {
585                err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
586                if (err) {
587                    oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
588                                oss->pcm_buf, hw->samples << hw->info.shift);
589                }
590            }
591        }
592    }
593
594    if (!oss->mmapped) {
595        oss->pcm_buf = audio_calloc (
596            AUDIO_FUNC,
597            hw->samples,
598            1 << hw->info.shift
599            );
600        if (!oss->pcm_buf) {
601            dolog (
602                "Could not allocate DAC buffer (%d samples, each %d bytes)\n",
603                hw->samples,
604                1 << hw->info.shift
605                );
606            oss_anal_close (&fd);
607            return -1;
608        }
609    }
610
611    oss->fd = fd;
612    return 0;
613}
614
615static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
616{
617    int trig;
618    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
619
620    switch (cmd) {
621    case VOICE_ENABLE:
622        {
623            va_list ap;
624            int poll_mode;
625
626            va_start (ap, cmd);
627            poll_mode = va_arg (ap, int);
628            va_end (ap);
629
630            ldebug ("enabling voice\n");
631            if (poll_mode && oss_poll_out (hw)) {
632                poll_mode = 0;
633            }
634            hw->poll_mode = poll_mode;
635
636            if (!oss->mmapped) {
637                return 0;
638            }
639
640            audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
641            trig = PCM_ENABLE_OUTPUT;
642            if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
643                oss_logerr (
644                    errno,
645                    "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
646                    );
647                return -1;
648            }
649        }
650        break;
651
652    case VOICE_DISABLE:
653        if (hw->poll_mode) {
654            qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
655            hw->poll_mode = 0;
656        }
657
658        if (!oss->mmapped) {
659            return 0;
660        }
661
662        ldebug ("disabling voice\n");
663        trig = 0;
664        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
665            oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
666            return -1;
667        }
668        break;
669    }
670    return 0;
671}
672
673static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
674{
675    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
676    struct oss_params req, obt;
677    int endianness;
678    int err;
679    int fd;
680    audfmt_e effective_fmt;
681    struct audsettings obt_as;
682
683    oss->fd = -1;
684
685    req.fmt = aud_to_ossfmt (as->fmt);
686    req.freq = as->freq;
687    req.nchannels = as->nchannels;
688    req.fragsize = conf.fragsize;
689    req.nfrags = conf.nfrags;
690    if (oss_open (1, &req, &obt, &fd)) {
691        return -1;
692    }
693
694    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
695    if (err) {
696        oss_anal_close (&fd);
697        return -1;
698    }
699
700    obt_as.freq = obt.freq;
701    obt_as.nchannels = obt.nchannels;
702    obt_as.fmt = effective_fmt;
703    obt_as.endianness = endianness;
704
705    audio_pcm_init_info (&hw->info, &obt_as);
706    oss->nfrags = obt.nfrags;
707    oss->fragsize = obt.fragsize;
708
709    if (obt.nfrags * obt.fragsize & hw->info.align) {
710        dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
711               obt.nfrags * obt.fragsize, hw->info.align + 1);
712    }
713
714    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
715    oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
716    if (!oss->pcm_buf) {
717        dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
718               hw->samples, 1 << hw->info.shift);
719        oss_anal_close (&fd);
720        return -1;
721    }
722
723    oss->fd = fd;
724    return 0;
725}
726
727static void oss_fini_in (HWVoiceIn *hw)
728{
729    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
730
731    oss_anal_close (&oss->fd);
732
733    if (oss->pcm_buf) {
734        qemu_free (oss->pcm_buf);
735        oss->pcm_buf = NULL;
736    }
737}
738
739static int oss_run_in (HWVoiceIn *hw)
740{
741    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
742    int hwshift = hw->info.shift;
743    int i;
744    int live = audio_pcm_hw_get_live_in (hw);
745    int dead = hw->samples - live;
746    size_t read_samples = 0;
747    struct {
748        int add;
749        int len;
750    } bufs[2] = {
751        { .add = hw->wpos, .len = 0 },
752        { .add = 0,        .len = 0 }
753    };
754
755    if (!dead) {
756        return 0;
757    }
758
759    if (hw->wpos + dead > hw->samples) {
760        bufs[0].len = (hw->samples - hw->wpos) << hwshift;
761        bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
762    }
763    else {
764        bufs[0].len = dead << hwshift;
765    }
766
767    for (i = 0; i < 2; ++i) {
768        ssize_t nread;
769
770        if (bufs[i].len) {
771            void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
772            nread = read (oss->fd, p, bufs[i].len);
773
774            if (nread > 0) {
775                if (nread & hw->info.align) {
776                    dolog ("warning: Misaligned read %zd (requested %d), "
777                           "alignment %d\n", nread, bufs[i].add << hwshift,
778                           hw->info.align + 1);
779                }
780                read_samples += nread >> hwshift;
781                hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
782                          &nominal_volume);
783            }
784
785            if (bufs[i].len - nread) {
786                if (nread == -1) {
787                    switch (errno) {
788                    case EINTR:
789                    case EAGAIN:
790                        break;
791                    default:
792                        oss_logerr (
793                            errno,
794                            "Failed to read %d bytes of audio (to %p)\n",
795                            bufs[i].len, p
796                            );
797                        break;
798                    }
799                }
800                break;
801            }
802        }
803    }
804
805    hw->wpos = (hw->wpos + read_samples) % hw->samples;
806    return read_samples;
807}
808
809static int oss_read (SWVoiceIn *sw, void *buf, int size)
810{
811    return audio_pcm_sw_read (sw, buf, size);
812}
813
814static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
815{
816    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
817
818    switch (cmd) {
819    case VOICE_ENABLE:
820        {
821            va_list ap;
822            int poll_mode;
823
824            va_start (ap, cmd);
825            poll_mode = va_arg (ap, int);
826            va_end (ap);
827
828            if (poll_mode && oss_poll_in (hw)) {
829                poll_mode = 0;
830            }
831            hw->poll_mode = poll_mode;
832        }
833        break;
834
835    case VOICE_DISABLE:
836        if (hw->poll_mode) {
837            hw->poll_mode = 0;
838            qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
839        }
840        break;
841    }
842    return 0;
843}
844
845static void *oss_audio_init (void)
846{
847    return &conf;
848}
849
850static void oss_audio_fini (void *opaque)
851{
852    (void) opaque;
853}
854
855static struct audio_option oss_options[] = {
856    {
857        .name  = "FRAGSIZE",
858        .tag   = AUD_OPT_INT,
859        .valp  = &conf.fragsize,
860        .descr = "Fragment size in bytes"
861    },
862    {
863        .name  = "NFRAGS",
864        .tag   = AUD_OPT_INT,
865        .valp  = &conf.nfrags,
866        .descr = "Number of fragments"
867    },
868    {
869        .name  = "MMAP",
870        .tag   = AUD_OPT_BOOL,
871        .valp  = &conf.try_mmap,
872        .descr = "Try using memory mapped access"
873    },
874    {
875        .name  = "DAC_DEV",
876        .tag   = AUD_OPT_STR,
877        .valp  = &conf.devpath_out,
878        .descr = "Path to DAC device"
879    },
880    {
881        .name  = "ADC_DEV",
882        .tag   = AUD_OPT_STR,
883        .valp  = &conf.devpath_in,
884        .descr = "Path to ADC device"
885    },
886    {
887        .name  = "EXCLUSIVE",
888        .tag   = AUD_OPT_BOOL,
889        .valp  = &conf.exclusive,
890        .descr = "Open device in exclusive mode (vmix wont work)"
891    },
892#ifdef USE_DSP_POLICY
893    {
894        .name  = "POLICY",
895        .tag   = AUD_OPT_INT,
896        .valp  = &conf.policy,
897        .descr = "Set the timing policy of the device, -1 to use fragment mode",
898    },
899#endif
900    {
901        .name  = "DEBUG",
902        .tag   = AUD_OPT_BOOL,
903        .valp  = &conf.debug,
904        .descr = "Turn on some debugging messages"
905    },
906    { /* End of list */ }
907};
908
909static struct audio_pcm_ops oss_pcm_ops = {
910    .init_out = oss_init_out,
911    .fini_out = oss_fini_out,
912    .run_out  = oss_run_out,
913    .write    = oss_write,
914    .ctl_out  = oss_ctl_out,
915
916    .init_in  = oss_init_in,
917    .fini_in  = oss_fini_in,
918    .run_in   = oss_run_in,
919    .read     = oss_read,
920    .ctl_in   = oss_ctl_in
921};
922
923struct audio_driver oss_audio_driver = {
924    .name           = "oss",
925    .descr          = "OSS http://www.opensound.com",
926    .options        = oss_options,
927    .init           = oss_audio_init,
928    .fini           = oss_audio_fini,
929    .pcm_ops        = &oss_pcm_ops,
930    .can_be_default = 1,
931    .max_voices_out = INT_MAX,
932    .max_voices_in  = INT_MAX,
933    .voice_size_out = sizeof (OSSVoiceOut),
934    .voice_size_in  = sizeof (OSSVoiceIn)
935};
936