cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

ossaudio.c (20594B)


      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
     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"
     31#include "audio.h"
     32#include "trace.h"
     33
     34#define AUDIO_CAP "oss"
     35#include "audio_int.h"
     36
     37#if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY
     38#define USE_DSP_POLICY
     39#endif
     40
     41typedef struct OSSVoiceOut {
     42    HWVoiceOut hw;
     43    int fd;
     44    int nfrags;
     45    int fragsize;
     46    int mmapped;
     47    Audiodev *dev;
     48} OSSVoiceOut;
     49
     50typedef struct OSSVoiceIn {
     51    HWVoiceIn hw;
     52    int fd;
     53    int nfrags;
     54    int fragsize;
     55    Audiodev *dev;
     56} OSSVoiceIn;
     57
     58struct oss_params {
     59    int freq;
     60    int fmt;
     61    int nchannels;
     62    int nfrags;
     63    int fragsize;
     64};
     65
     66static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
     67{
     68    va_list ap;
     69
     70    va_start (ap, fmt);
     71    AUD_vlog (AUDIO_CAP, fmt, ap);
     72    va_end (ap);
     73
     74    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
     75}
     76
     77static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
     78    int err,
     79    const char *typ,
     80    const char *fmt,
     81    ...
     82    )
     83{
     84    va_list ap;
     85
     86    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
     87
     88    va_start (ap, fmt);
     89    AUD_vlog (AUDIO_CAP, fmt, ap);
     90    va_end (ap);
     91
     92    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
     93}
     94
     95static void oss_anal_close (int *fdp)
     96{
     97    int err;
     98
     99    qemu_set_fd_handler (*fdp, NULL, NULL, NULL);
    100    err = close (*fdp);
    101    if (err) {
    102        oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
    103    }
    104    *fdp = -1;
    105}
    106
    107static void oss_helper_poll_out (void *opaque)
    108{
    109    AudioState *s = opaque;
    110    audio_run(s, "oss_poll_out");
    111}
    112
    113static void oss_helper_poll_in (void *opaque)
    114{
    115    AudioState *s = opaque;
    116    audio_run(s, "oss_poll_in");
    117}
    118
    119static void oss_poll_out (HWVoiceOut *hw)
    120{
    121    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
    122
    123    qemu_set_fd_handler(oss->fd, NULL, oss_helper_poll_out, hw->s);
    124}
    125
    126static void oss_poll_in (HWVoiceIn *hw)
    127{
    128    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
    129
    130    qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s);
    131}
    132
    133static int aud_to_ossfmt (AudioFormat fmt, int endianness)
    134{
    135    switch (fmt) {
    136    case AUDIO_FORMAT_S8:
    137        return AFMT_S8;
    138
    139    case AUDIO_FORMAT_U8:
    140        return AFMT_U8;
    141
    142    case AUDIO_FORMAT_S16:
    143        if (endianness) {
    144            return AFMT_S16_BE;
    145        } else {
    146            return AFMT_S16_LE;
    147        }
    148
    149    case AUDIO_FORMAT_U16:
    150        if (endianness) {
    151            return AFMT_U16_BE;
    152        } else {
    153            return AFMT_U16_LE;
    154        }
    155
    156    default:
    157        dolog ("Internal logic error: Bad audio format %d\n", fmt);
    158#ifdef DEBUG_AUDIO
    159        abort ();
    160#endif
    161        return AFMT_U8;
    162    }
    163}
    164
    165static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness)
    166{
    167    switch (ossfmt) {
    168    case AFMT_S8:
    169        *endianness = 0;
    170        *fmt = AUDIO_FORMAT_S8;
    171        break;
    172
    173    case AFMT_U8:
    174        *endianness = 0;
    175        *fmt = AUDIO_FORMAT_U8;
    176        break;
    177
    178    case AFMT_S16_LE:
    179        *endianness = 0;
    180        *fmt = AUDIO_FORMAT_S16;
    181        break;
    182
    183    case AFMT_U16_LE:
    184        *endianness = 0;
    185        *fmt = AUDIO_FORMAT_U16;
    186        break;
    187
    188    case AFMT_S16_BE:
    189        *endianness = 1;
    190        *fmt = AUDIO_FORMAT_S16;
    191        break;
    192
    193    case AFMT_U16_BE:
    194        *endianness = 1;
    195        *fmt = AUDIO_FORMAT_U16;
    196        break;
    197
    198    default:
    199        dolog ("Unrecognized audio format %d\n", ossfmt);
    200        return -1;
    201    }
    202
    203    return 0;
    204}
    205
    206#if defined DEBUG_MISMATCHES || defined DEBUG
    207static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
    208{
    209    dolog ("parameter | requested value | obtained value\n");
    210    dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);
    211    dolog ("channels  |      %10d |     %10d\n",
    212           req->nchannels, obt->nchannels);
    213    dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
    214    dolog ("nfrags    |      %10d |     %10d\n", req->nfrags, obt->nfrags);
    215    dolog ("fragsize  |      %10d |     %10d\n",
    216           req->fragsize, obt->fragsize);
    217}
    218#endif
    219
    220#ifdef USE_DSP_POLICY
    221static int oss_get_version (int fd, int *version, const char *typ)
    222{
    223    if (ioctl (fd, OSS_GETVERSION, &version)) {
    224#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
    225        /*
    226         * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION
    227         * since 7.x, but currently only on the mixer device (or in
    228         * the Linuxolator), and in the native version that part of
    229         * the code is in fact never reached so the ioctl fails anyway.
    230         * Until this is fixed, just check the errno and if its what
    231         * FreeBSD's sound drivers return atm assume they are new enough.
    232         */
    233        if (errno == EINVAL) {
    234            *version = 0x040000;
    235            return 0;
    236        }
    237#endif
    238        oss_logerr2 (errno, typ, "Failed to get OSS version\n");
    239        return -1;
    240    }
    241    return 0;
    242}
    243#endif
    244
    245static int oss_open(int in, struct oss_params *req, audsettings *as,
    246                    struct oss_params *obt, int *pfd, Audiodev *dev)
    247{
    248    AudiodevOssOptions *oopts = &dev->u.oss;
    249    AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out;
    250    int fd;
    251    int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0;
    252    audio_buf_info abinfo;
    253    int fmt, freq, nchannels;
    254    int setfragment = 1;
    255    const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp";
    256    const char *typ = in ? "ADC" : "DAC";
    257#ifdef USE_DSP_POLICY
    258    int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
    259#endif
    260
    261    /* Kludge needed to have working mmap on Linux */
    262    oflags |= (oopts->has_try_mmap && oopts->try_mmap) ?
    263        O_RDWR : (in ? O_RDONLY : O_WRONLY);
    264
    265    fd = open (dspname, oflags | O_NONBLOCK);
    266    if (-1 == fd) {
    267        oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
    268        return -1;
    269    }
    270
    271    freq = req->freq;
    272    nchannels = req->nchannels;
    273    fmt = req->fmt;
    274    req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4;
    275    req->fragsize = audio_buffer_bytes(
    276        qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220);
    277
    278    if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
    279        oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
    280        goto err;
    281    }
    282
    283    if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
    284        oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
    285                     req->nchannels);
    286        goto err;
    287    }
    288
    289    if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
    290        oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
    291        goto err;
    292    }
    293
    294    if (ioctl (fd, SNDCTL_DSP_NONBLOCK, NULL)) {
    295        oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n");
    296        goto err;
    297    }
    298
    299#ifdef USE_DSP_POLICY
    300    if (policy >= 0) {
    301        int version;
    302
    303        if (!oss_get_version (fd, &version, typ)) {
    304            trace_oss_version(version);
    305
    306            if (version >= 0x040000) {
    307                int policy2 = policy;
    308                if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) {
    309                    oss_logerr2 (errno, typ,
    310                                 "Failed to set timing policy to %d\n",
    311                                 policy);
    312                    goto err;
    313                }
    314                setfragment = 0;
    315            }
    316        }
    317    }
    318#endif
    319
    320    if (setfragment) {
    321        int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize);
    322        if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
    323            oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
    324                         req->nfrags, req->fragsize);
    325            goto err;
    326        }
    327    }
    328
    329    if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
    330        oss_logerr2 (errno, typ, "Failed to get buffer length\n");
    331        goto err;
    332    }
    333
    334    if (!abinfo.fragstotal || !abinfo.fragsize) {
    335        AUD_log (AUDIO_CAP, "Returned bogus buffer information(%d, %d) for %s\n",
    336                 abinfo.fragstotal, abinfo.fragsize, typ);
    337        goto err;
    338    }
    339
    340    obt->fmt = fmt;
    341    obt->nchannels = nchannels;
    342    obt->freq = freq;
    343    obt->nfrags = abinfo.fragstotal;
    344    obt->fragsize = abinfo.fragsize;
    345    *pfd = fd;
    346
    347#ifdef DEBUG_MISMATCHES
    348    if ((req->fmt != obt->fmt) ||
    349        (req->nchannels != obt->nchannels) ||
    350        (req->freq != obt->freq) ||
    351        (req->fragsize != obt->fragsize) ||
    352        (req->nfrags != obt->nfrags)) {
    353        dolog ("Audio parameters mismatch\n");
    354        oss_dump_info (req, obt);
    355    }
    356#endif
    357
    358#ifdef DEBUG
    359    oss_dump_info (req, obt);
    360#endif
    361    return 0;
    362
    363 err:
    364    oss_anal_close (&fd);
    365    return -1;
    366}
    367
    368static size_t oss_get_available_bytes(OSSVoiceOut *oss)
    369{
    370    int err;
    371    struct count_info cntinfo;
    372    assert(oss->mmapped);
    373
    374    err = ioctl(oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
    375    if (err < 0) {
    376        oss_logerr(errno, "SNDCTL_DSP_GETOPTR failed\n");
    377        return 0;
    378    }
    379
    380    return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul);
    381}
    382
    383static void oss_run_buffer_out(HWVoiceOut *hw)
    384{
    385    OSSVoiceOut *oss = (OSSVoiceOut *)hw;
    386
    387    if (!oss->mmapped) {
    388        audio_generic_run_buffer_out(hw);
    389    }
    390}
    391
    392static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
    393{
    394    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
    395    if (oss->mmapped) {
    396        *size = MIN(oss_get_available_bytes(oss), hw->size_emul - hw->pos_emul);
    397        return hw->buf_emul + hw->pos_emul;
    398    } else {
    399        return audio_generic_get_buffer_out(hw, size);
    400    }
    401}
    402
    403static size_t oss_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
    404{
    405    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
    406    if (oss->mmapped) {
    407        assert(buf == hw->buf_emul + hw->pos_emul && size < hw->size_emul);
    408
    409        hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
    410        return size;
    411    } else {
    412        return audio_generic_put_buffer_out(hw, buf, size);
    413    }
    414}
    415
    416static size_t oss_write(HWVoiceOut *hw, void *buf, size_t len)
    417{
    418    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
    419    size_t pos;
    420
    421    if (oss->mmapped) {
    422        size_t total_len;
    423        len = MIN(len, oss_get_available_bytes(oss));
    424
    425        total_len = len;
    426        while (len) {
    427            size_t to_copy = MIN(len, hw->size_emul - hw->pos_emul);
    428            memcpy(hw->buf_emul + hw->pos_emul, buf, to_copy);
    429
    430            hw->pos_emul = (hw->pos_emul + to_copy) % hw->size_emul;
    431            buf += to_copy;
    432            len -= to_copy;
    433        }
    434        return total_len;
    435    }
    436
    437    pos = 0;
    438    while (len) {
    439        ssize_t bytes_written;
    440        void *pcm = advance(buf, pos);
    441
    442        bytes_written = write(oss->fd, pcm, len);
    443        if (bytes_written < 0) {
    444            if (errno != EAGAIN) {
    445                oss_logerr(errno, "failed to write %zu bytes\n",
    446                           len);
    447            }
    448            return pos;
    449        }
    450
    451        pos += bytes_written;
    452        if (bytes_written < len) {
    453            break;
    454        }
    455        len -= bytes_written;
    456    }
    457    return pos;
    458}
    459
    460static void oss_fini_out (HWVoiceOut *hw)
    461{
    462    int err;
    463    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
    464
    465    ldebug ("oss_fini\n");
    466    oss_anal_close (&oss->fd);
    467
    468    if (oss->mmapped && hw->buf_emul) {
    469        err = munmap(hw->buf_emul, hw->size_emul);
    470        if (err) {
    471            oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n",
    472                       hw->buf_emul, hw->size_emul);
    473        }
    474        hw->buf_emul = NULL;
    475    }
    476}
    477
    478static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
    479                        void *drv_opaque)
    480{
    481    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
    482    struct oss_params req, obt;
    483    int endianness;
    484    int err;
    485    int fd;
    486    AudioFormat effective_fmt;
    487    struct audsettings obt_as;
    488    Audiodev *dev = drv_opaque;
    489    AudiodevOssOptions *oopts = &dev->u.oss;
    490
    491    oss->fd = -1;
    492
    493    req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
    494    req.freq = as->freq;
    495    req.nchannels = as->nchannels;
    496
    497    if (oss_open(0, &req, as, &obt, &fd, dev)) {
    498        return -1;
    499    }
    500
    501    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
    502    if (err) {
    503        oss_anal_close (&fd);
    504        return -1;
    505    }
    506
    507    obt_as.freq = obt.freq;
    508    obt_as.nchannels = obt.nchannels;
    509    obt_as.fmt = effective_fmt;
    510    obt_as.endianness = endianness;
    511
    512    audio_pcm_init_info (&hw->info, &obt_as);
    513    oss->nfrags = obt.nfrags;
    514    oss->fragsize = obt.fragsize;
    515
    516    if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) {
    517        dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
    518               obt.nfrags * obt.fragsize, hw->info.bytes_per_frame);
    519    }
    520
    521    hw->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame;
    522
    523    oss->mmapped = 0;
    524    if (oopts->has_try_mmap && oopts->try_mmap) {
    525        hw->size_emul = hw->samples * hw->info.bytes_per_frame;
    526        hw->buf_emul = mmap(
    527            NULL,
    528            hw->size_emul,
    529            PROT_READ | PROT_WRITE,
    530            MAP_SHARED,
    531            fd,
    532            0
    533            );
    534        if (hw->buf_emul == MAP_FAILED) {
    535            oss_logerr(errno, "Failed to map %zu bytes of DAC\n",
    536                       hw->size_emul);
    537            hw->buf_emul = NULL;
    538        } else {
    539            int err;
    540            int trig = 0;
    541            if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
    542                oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
    543            } else {
    544                trig = PCM_ENABLE_OUTPUT;
    545                if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
    546                    oss_logerr (
    547                        errno,
    548                        "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
    549                        );
    550                } else {
    551                    oss->mmapped = 1;
    552                }
    553            }
    554
    555            if (!oss->mmapped) {
    556                err = munmap(hw->buf_emul, hw->size_emul);
    557                if (err) {
    558                    oss_logerr(errno, "Failed to unmap buffer %p size %zu\n",
    559                               hw->buf_emul, hw->size_emul);
    560                }
    561                hw->buf_emul = NULL;
    562            }
    563        }
    564    }
    565
    566    oss->fd = fd;
    567    oss->dev = dev;
    568    return 0;
    569}
    570
    571static void oss_enable_out(HWVoiceOut *hw, bool enable)
    572{
    573    int trig;
    574    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
    575    AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
    576
    577    if (enable) {
    578        hw->poll_mode = opdo->try_poll;
    579
    580        ldebug("enabling voice\n");
    581        if (hw->poll_mode) {
    582            oss_poll_out(hw);
    583        }
    584
    585        if (!oss->mmapped) {
    586            return;
    587        }
    588
    589        audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->samples);
    590        trig = PCM_ENABLE_OUTPUT;
    591        if (ioctl(oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
    592            oss_logerr(errno,
    593                       "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n");
    594            return;
    595        }
    596    } else {
    597        if (hw->poll_mode) {
    598            qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
    599            hw->poll_mode = 0;
    600        }
    601
    602        if (!oss->mmapped) {
    603            return;
    604        }
    605
    606        ldebug ("disabling voice\n");
    607        trig = 0;
    608        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
    609            oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
    610            return;
    611        }
    612    }
    613}
    614
    615static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
    616{
    617    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
    618    struct oss_params req, obt;
    619    int endianness;
    620    int err;
    621    int fd;
    622    AudioFormat effective_fmt;
    623    struct audsettings obt_as;
    624    Audiodev *dev = drv_opaque;
    625
    626    oss->fd = -1;
    627
    628    req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
    629    req.freq = as->freq;
    630    req.nchannels = as->nchannels;
    631    if (oss_open(1, &req, as, &obt, &fd, dev)) {
    632        return -1;
    633    }
    634
    635    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
    636    if (err) {
    637        oss_anal_close (&fd);
    638        return -1;
    639    }
    640
    641    obt_as.freq = obt.freq;
    642    obt_as.nchannels = obt.nchannels;
    643    obt_as.fmt = effective_fmt;
    644    obt_as.endianness = endianness;
    645
    646    audio_pcm_init_info (&hw->info, &obt_as);
    647    oss->nfrags = obt.nfrags;
    648    oss->fragsize = obt.fragsize;
    649
    650    if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) {
    651        dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
    652               obt.nfrags * obt.fragsize, hw->info.bytes_per_frame);
    653    }
    654
    655    hw->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame;
    656
    657    oss->fd = fd;
    658    oss->dev = dev;
    659    return 0;
    660}
    661
    662static void oss_fini_in (HWVoiceIn *hw)
    663{
    664    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
    665
    666    oss_anal_close (&oss->fd);
    667}
    668
    669static size_t oss_read(HWVoiceIn *hw, void *buf, size_t len)
    670{
    671    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
    672    size_t pos = 0;
    673
    674    while (len) {
    675        ssize_t nread;
    676
    677        void *dst = advance(buf, pos);
    678        nread = read(oss->fd, dst, len);
    679
    680        if (nread == -1) {
    681            switch (errno) {
    682            case EINTR:
    683            case EAGAIN:
    684                break;
    685            default:
    686                oss_logerr(errno, "Failed to read %zu bytes of audio (to %p)\n",
    687                           len, dst);
    688                break;
    689            }
    690            break;
    691        }
    692
    693        pos += nread;
    694        len -= nread;
    695    }
    696
    697    return pos;
    698}
    699
    700static void oss_enable_in(HWVoiceIn *hw, bool enable)
    701{
    702    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
    703    AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
    704
    705    if (enable) {
    706        hw->poll_mode = opdo->try_poll;
    707
    708        if (hw->poll_mode) {
    709            oss_poll_in(hw);
    710        }
    711    } else {
    712        if (hw->poll_mode) {
    713            qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
    714            hw->poll_mode = 0;
    715        }
    716    }
    717}
    718
    719static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo)
    720{
    721    if (!opdo->has_try_poll) {
    722        opdo->try_poll = true;
    723        opdo->has_try_poll = true;
    724    }
    725}
    726
    727static void *oss_audio_init(Audiodev *dev)
    728{
    729    AudiodevOssOptions *oopts;
    730    assert(dev->driver == AUDIODEV_DRIVER_OSS);
    731
    732    oopts = &dev->u.oss;
    733    oss_init_per_direction(oopts->in);
    734    oss_init_per_direction(oopts->out);
    735
    736    if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp",
    737               R_OK | W_OK) < 0 ||
    738        access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp",
    739               R_OK | W_OK) < 0) {
    740        return NULL;
    741    }
    742    return dev;
    743}
    744
    745static void oss_audio_fini (void *opaque)
    746{
    747}
    748
    749static struct audio_pcm_ops oss_pcm_ops = {
    750    .init_out = oss_init_out,
    751    .fini_out = oss_fini_out,
    752    .write    = oss_write,
    753    .run_buffer_out = oss_run_buffer_out,
    754    .get_buffer_out = oss_get_buffer_out,
    755    .put_buffer_out = oss_put_buffer_out,
    756    .enable_out = oss_enable_out,
    757
    758    .init_in  = oss_init_in,
    759    .fini_in  = oss_fini_in,
    760    .read     = oss_read,
    761    .run_buffer_in = audio_generic_run_buffer_in,
    762    .enable_in = oss_enable_in
    763};
    764
    765static struct audio_driver oss_audio_driver = {
    766    .name           = "oss",
    767    .descr          = "OSS http://www.opensound.com",
    768    .init           = oss_audio_init,
    769    .fini           = oss_audio_fini,
    770    .pcm_ops        = &oss_pcm_ops,
    771    .can_be_default = 1,
    772    .max_voices_out = INT_MAX,
    773    .max_voices_in  = INT_MAX,
    774    .voice_size_out = sizeof (OSSVoiceOut),
    775    .voice_size_in  = sizeof (OSSVoiceIn)
    776};
    777
    778static void register_audio_oss(void)
    779{
    780    audio_driver_register(&oss_audio_driver);
    781}
    782type_init(register_audio_oss);