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

alsaaudio.c (24871B)


      1/*
      2 * QEMU ALSA audio driver
      3 *
      4 * Copyright (c) 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 <alsa/asoundlib.h>
     27#include "qemu/main-loop.h"
     28#include "qemu/module.h"
     29#include "audio.h"
     30#include "trace.h"
     31
     32#pragma GCC diagnostic ignored "-Waddress"
     33
     34#define AUDIO_CAP "alsa"
     35#include "audio_int.h"
     36
     37#define DEBUG_ALSA 0
     38
     39struct pollhlp {
     40    snd_pcm_t *handle;
     41    struct pollfd *pfds;
     42    int count;
     43    int mask;
     44    AudioState *s;
     45};
     46
     47typedef struct ALSAVoiceOut {
     48    HWVoiceOut hw;
     49    snd_pcm_t *handle;
     50    struct pollhlp pollhlp;
     51    Audiodev *dev;
     52} ALSAVoiceOut;
     53
     54typedef struct ALSAVoiceIn {
     55    HWVoiceIn hw;
     56    snd_pcm_t *handle;
     57    struct pollhlp pollhlp;
     58    Audiodev *dev;
     59} ALSAVoiceIn;
     60
     61struct alsa_params_req {
     62    int freq;
     63    snd_pcm_format_t fmt;
     64    int nchannels;
     65};
     66
     67struct alsa_params_obt {
     68    int freq;
     69    AudioFormat fmt;
     70    int endianness;
     71    int nchannels;
     72    snd_pcm_uframes_t samples;
     73};
     74
     75static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
     76{
     77    va_list ap;
     78
     79    va_start (ap, fmt);
     80    AUD_vlog (AUDIO_CAP, fmt, ap);
     81    va_end (ap);
     82
     83    AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
     84}
     85
     86static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
     87    int err,
     88    const char *typ,
     89    const char *fmt,
     90    ...
     91    )
     92{
     93    va_list ap;
     94
     95    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
     96
     97    va_start (ap, fmt);
     98    AUD_vlog (AUDIO_CAP, fmt, ap);
     99    va_end (ap);
    100
    101    AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
    102}
    103
    104static void alsa_fini_poll (struct pollhlp *hlp)
    105{
    106    int i;
    107    struct pollfd *pfds = hlp->pfds;
    108
    109    if (pfds) {
    110        for (i = 0; i < hlp->count; ++i) {
    111            qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL);
    112        }
    113        g_free (pfds);
    114    }
    115    hlp->pfds = NULL;
    116    hlp->count = 0;
    117    hlp->handle = NULL;
    118}
    119
    120static void alsa_anal_close1 (snd_pcm_t **handlep)
    121{
    122    int err = snd_pcm_close (*handlep);
    123    if (err) {
    124        alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep);
    125    }
    126    *handlep = NULL;
    127}
    128
    129static void alsa_anal_close (snd_pcm_t **handlep, struct pollhlp *hlp)
    130{
    131    alsa_fini_poll (hlp);
    132    alsa_anal_close1 (handlep);
    133}
    134
    135static int alsa_recover (snd_pcm_t *handle)
    136{
    137    int err = snd_pcm_prepare (handle);
    138    if (err < 0) {
    139        alsa_logerr (err, "Failed to prepare handle %p\n", handle);
    140        return -1;
    141    }
    142    return 0;
    143}
    144
    145static int alsa_resume (snd_pcm_t *handle)
    146{
    147    int err = snd_pcm_resume (handle);
    148    if (err < 0) {
    149        alsa_logerr (err, "Failed to resume handle %p\n", handle);
    150        return -1;
    151    }
    152    return 0;
    153}
    154
    155static void alsa_poll_handler (void *opaque)
    156{
    157    int err, count;
    158    snd_pcm_state_t state;
    159    struct pollhlp *hlp = opaque;
    160    unsigned short revents;
    161
    162    count = poll (hlp->pfds, hlp->count, 0);
    163    if (count < 0) {
    164        dolog ("alsa_poll_handler: poll %s\n", strerror (errno));
    165        return;
    166    }
    167
    168    if (!count) {
    169        return;
    170    }
    171
    172    /* XXX: ALSA example uses initial count, not the one returned by
    173       poll, correct? */
    174    err = snd_pcm_poll_descriptors_revents (hlp->handle, hlp->pfds,
    175                                            hlp->count, &revents);
    176    if (err < 0) {
    177        alsa_logerr (err, "snd_pcm_poll_descriptors_revents");
    178        return;
    179    }
    180
    181    if (!(revents & hlp->mask)) {
    182        trace_alsa_revents(revents);
    183        return;
    184    }
    185
    186    state = snd_pcm_state (hlp->handle);
    187    switch (state) {
    188    case SND_PCM_STATE_SETUP:
    189        alsa_recover (hlp->handle);
    190        break;
    191
    192    case SND_PCM_STATE_XRUN:
    193        alsa_recover (hlp->handle);
    194        break;
    195
    196    case SND_PCM_STATE_SUSPENDED:
    197        alsa_resume (hlp->handle);
    198        break;
    199
    200    case SND_PCM_STATE_PREPARED:
    201        audio_run(hlp->s, "alsa run (prepared)");
    202        break;
    203
    204    case SND_PCM_STATE_RUNNING:
    205        audio_run(hlp->s, "alsa run (running)");
    206        break;
    207
    208    default:
    209        dolog ("Unexpected state %d\n", state);
    210    }
    211}
    212
    213static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask)
    214{
    215    int i, count, err;
    216    struct pollfd *pfds;
    217
    218    count = snd_pcm_poll_descriptors_count (handle);
    219    if (count <= 0) {
    220        dolog ("Could not initialize poll mode\n"
    221               "Invalid number of poll descriptors %d\n", count);
    222        return -1;
    223    }
    224
    225    pfds = audio_calloc ("alsa_poll_helper", count, sizeof (*pfds));
    226    if (!pfds) {
    227        dolog ("Could not initialize poll mode\n");
    228        return -1;
    229    }
    230
    231    err = snd_pcm_poll_descriptors (handle, pfds, count);
    232    if (err < 0) {
    233        alsa_logerr (err, "Could not initialize poll mode\n"
    234                     "Could not obtain poll descriptors\n");
    235        g_free (pfds);
    236        return -1;
    237    }
    238
    239    for (i = 0; i < count; ++i) {
    240        if (pfds[i].events & POLLIN) {
    241            qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, NULL, hlp);
    242        }
    243        if (pfds[i].events & POLLOUT) {
    244            trace_alsa_pollout(i, pfds[i].fd);
    245            qemu_set_fd_handler (pfds[i].fd, NULL, alsa_poll_handler, hlp);
    246        }
    247        trace_alsa_set_handler(pfds[i].events, i, pfds[i].fd, err);
    248
    249    }
    250    hlp->pfds = pfds;
    251    hlp->count = count;
    252    hlp->handle = handle;
    253    hlp->mask = mask;
    254    return 0;
    255}
    256
    257static int alsa_poll_out (HWVoiceOut *hw)
    258{
    259    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
    260
    261    return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLOUT);
    262}
    263
    264static int alsa_poll_in (HWVoiceIn *hw)
    265{
    266    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
    267
    268    return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN);
    269}
    270
    271static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
    272{
    273    switch (fmt) {
    274    case AUDIO_FORMAT_S8:
    275        return SND_PCM_FORMAT_S8;
    276
    277    case AUDIO_FORMAT_U8:
    278        return SND_PCM_FORMAT_U8;
    279
    280    case AUDIO_FORMAT_S16:
    281        if (endianness) {
    282            return SND_PCM_FORMAT_S16_BE;
    283        } else {
    284            return SND_PCM_FORMAT_S16_LE;
    285        }
    286
    287    case AUDIO_FORMAT_U16:
    288        if (endianness) {
    289            return SND_PCM_FORMAT_U16_BE;
    290        } else {
    291            return SND_PCM_FORMAT_U16_LE;
    292        }
    293
    294    case AUDIO_FORMAT_S32:
    295        if (endianness) {
    296            return SND_PCM_FORMAT_S32_BE;
    297        } else {
    298            return SND_PCM_FORMAT_S32_LE;
    299        }
    300
    301    case AUDIO_FORMAT_U32:
    302        if (endianness) {
    303            return SND_PCM_FORMAT_U32_BE;
    304        } else {
    305            return SND_PCM_FORMAT_U32_LE;
    306        }
    307
    308    case AUDIO_FORMAT_F32:
    309        if (endianness) {
    310            return SND_PCM_FORMAT_FLOAT_BE;
    311        } else {
    312            return SND_PCM_FORMAT_FLOAT_LE;
    313        }
    314
    315    default:
    316        dolog ("Internal logic error: Bad audio format %d\n", fmt);
    317#ifdef DEBUG_AUDIO
    318        abort ();
    319#endif
    320        return SND_PCM_FORMAT_U8;
    321    }
    322}
    323
    324static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
    325                           int *endianness)
    326{
    327    switch (alsafmt) {
    328    case SND_PCM_FORMAT_S8:
    329        *endianness = 0;
    330        *fmt = AUDIO_FORMAT_S8;
    331        break;
    332
    333    case SND_PCM_FORMAT_U8:
    334        *endianness = 0;
    335        *fmt = AUDIO_FORMAT_U8;
    336        break;
    337
    338    case SND_PCM_FORMAT_S16_LE:
    339        *endianness = 0;
    340        *fmt = AUDIO_FORMAT_S16;
    341        break;
    342
    343    case SND_PCM_FORMAT_U16_LE:
    344        *endianness = 0;
    345        *fmt = AUDIO_FORMAT_U16;
    346        break;
    347
    348    case SND_PCM_FORMAT_S16_BE:
    349        *endianness = 1;
    350        *fmt = AUDIO_FORMAT_S16;
    351        break;
    352
    353    case SND_PCM_FORMAT_U16_BE:
    354        *endianness = 1;
    355        *fmt = AUDIO_FORMAT_U16;
    356        break;
    357
    358    case SND_PCM_FORMAT_S32_LE:
    359        *endianness = 0;
    360        *fmt = AUDIO_FORMAT_S32;
    361        break;
    362
    363    case SND_PCM_FORMAT_U32_LE:
    364        *endianness = 0;
    365        *fmt = AUDIO_FORMAT_U32;
    366        break;
    367
    368    case SND_PCM_FORMAT_S32_BE:
    369        *endianness = 1;
    370        *fmt = AUDIO_FORMAT_S32;
    371        break;
    372
    373    case SND_PCM_FORMAT_U32_BE:
    374        *endianness = 1;
    375        *fmt = AUDIO_FORMAT_U32;
    376        break;
    377
    378    case SND_PCM_FORMAT_FLOAT_LE:
    379        *endianness = 0;
    380        *fmt = AUDIO_FORMAT_F32;
    381        break;
    382
    383    case SND_PCM_FORMAT_FLOAT_BE:
    384        *endianness = 1;
    385        *fmt = AUDIO_FORMAT_F32;
    386        break;
    387
    388    default:
    389        dolog ("Unrecognized audio format %d\n", alsafmt);
    390        return -1;
    391    }
    392
    393    return 0;
    394}
    395
    396static void alsa_dump_info (struct alsa_params_req *req,
    397                            struct alsa_params_obt *obt,
    398                            snd_pcm_format_t obtfmt,
    399                            AudiodevAlsaPerDirectionOptions *apdo)
    400{
    401    dolog("parameter | requested value | obtained value\n");
    402    dolog("format    |      %10d |     %10d\n", req->fmt, obtfmt);
    403    dolog("channels  |      %10d |     %10d\n",
    404          req->nchannels, obt->nchannels);
    405    dolog("frequency |      %10d |     %10d\n", req->freq, obt->freq);
    406    dolog("============================================\n");
    407    dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n",
    408          apdo->buffer_length, apdo->period_length);
    409    dolog("obtained: samples %ld\n", obt->samples);
    410}
    411
    412static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
    413{
    414    int err;
    415    snd_pcm_sw_params_t *sw_params;
    416
    417    snd_pcm_sw_params_alloca (&sw_params);
    418
    419    err = snd_pcm_sw_params_current (handle, sw_params);
    420    if (err < 0) {
    421        dolog ("Could not fully initialize DAC\n");
    422        alsa_logerr (err, "Failed to get current software parameters\n");
    423        return;
    424    }
    425
    426    err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);
    427    if (err < 0) {
    428        dolog ("Could not fully initialize DAC\n");
    429        alsa_logerr (err, "Failed to set software threshold to %ld\n",
    430                     threshold);
    431        return;
    432    }
    433
    434    err = snd_pcm_sw_params (handle, sw_params);
    435    if (err < 0) {
    436        dolog ("Could not fully initialize DAC\n");
    437        alsa_logerr (err, "Failed to set software parameters\n");
    438        return;
    439    }
    440}
    441
    442static int alsa_open(bool in, struct alsa_params_req *req,
    443                     struct alsa_params_obt *obt, snd_pcm_t **handlep,
    444                     Audiodev *dev)
    445{
    446    AudiodevAlsaOptions *aopts = &dev->u.alsa;
    447    AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->out;
    448    snd_pcm_t *handle;
    449    snd_pcm_hw_params_t *hw_params;
    450    int err;
    451    unsigned int freq, nchannels;
    452    const char *pcm_name = apdo->has_dev ? apdo->dev : "default";
    453    snd_pcm_uframes_t obt_buffer_size;
    454    const char *typ = in ? "ADC" : "DAC";
    455    snd_pcm_format_t obtfmt;
    456
    457    freq = req->freq;
    458    nchannels = req->nchannels;
    459
    460    snd_pcm_hw_params_alloca (&hw_params);
    461
    462    err = snd_pcm_open (
    463        &handle,
    464        pcm_name,
    465        in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
    466        SND_PCM_NONBLOCK
    467        );
    468    if (err < 0) {
    469        alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name);
    470        return -1;
    471    }
    472
    473    err = snd_pcm_hw_params_any (handle, hw_params);
    474    if (err < 0) {
    475        alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
    476        goto err;
    477    }
    478
    479    err = snd_pcm_hw_params_set_access (
    480        handle,
    481        hw_params,
    482        SND_PCM_ACCESS_RW_INTERLEAVED
    483        );
    484    if (err < 0) {
    485        alsa_logerr2 (err, typ, "Failed to set access type\n");
    486        goto err;
    487    }
    488
    489    err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
    490    if (err < 0) {
    491        alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
    492    }
    493
    494    err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
    495    if (err < 0) {
    496        alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);
    497        goto err;
    498    }
    499
    500    err = snd_pcm_hw_params_set_channels_near (
    501        handle,
    502        hw_params,
    503        &nchannels
    504        );
    505    if (err < 0) {
    506        alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",
    507                      req->nchannels);
    508        goto err;
    509    }
    510
    511    if (apdo->buffer_length) {
    512        int dir = 0;
    513        unsigned int btime = apdo->buffer_length;
    514
    515        err = snd_pcm_hw_params_set_buffer_time_near(
    516            handle, hw_params, &btime, &dir);
    517
    518        if (err < 0) {
    519            alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n",
    520                         apdo->buffer_length);
    521            goto err;
    522        }
    523
    524        if (apdo->has_buffer_length && btime != apdo->buffer_length) {
    525            dolog("Requested buffer time %" PRId32
    526                  " was rejected, using %u\n", apdo->buffer_length, btime);
    527        }
    528    }
    529
    530    if (apdo->period_length) {
    531        int dir = 0;
    532        unsigned int ptime = apdo->period_length;
    533
    534        err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime,
    535                                                     &dir);
    536
    537        if (err < 0) {
    538            alsa_logerr2(err, typ, "Failed to set period time to %" PRId32 "\n",
    539                         apdo->period_length);
    540            goto err;
    541        }
    542
    543        if (apdo->has_period_length && ptime != apdo->period_length) {
    544            dolog("Requested period time %" PRId32 " was rejected, using %d\n",
    545                  apdo->period_length, ptime);
    546        }
    547    }
    548
    549    err = snd_pcm_hw_params (handle, hw_params);
    550    if (err < 0) {
    551        alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");
    552        goto err;
    553    }
    554
    555    err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size);
    556    if (err < 0) {
    557        alsa_logerr2 (err, typ, "Failed to get buffer size\n");
    558        goto err;
    559    }
    560
    561    err = snd_pcm_hw_params_get_format (hw_params, &obtfmt);
    562    if (err < 0) {
    563        alsa_logerr2 (err, typ, "Failed to get format\n");
    564        goto err;
    565    }
    566
    567    if (alsa_to_audfmt (obtfmt, &obt->fmt, &obt->endianness)) {
    568        dolog ("Invalid format was returned %d\n", obtfmt);
    569        goto err;
    570    }
    571
    572    err = snd_pcm_prepare (handle);
    573    if (err < 0) {
    574        alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
    575        goto err;
    576    }
    577
    578    if (!in && aopts->has_threshold && aopts->threshold) {
    579        struct audsettings as = { .freq = freq };
    580        alsa_set_threshold(
    581            handle,
    582            audio_buffer_frames(qapi_AudiodevAlsaPerDirectionOptions_base(apdo),
    583                                &as, aopts->threshold));
    584    }
    585
    586    obt->nchannels = nchannels;
    587    obt->freq = freq;
    588    obt->samples = obt_buffer_size;
    589
    590    *handlep = handle;
    591
    592    if (DEBUG_ALSA || obtfmt != req->fmt ||
    593        obt->nchannels != req->nchannels || obt->freq != req->freq) {
    594        dolog ("Audio parameters for %s\n", typ);
    595        alsa_dump_info(req, obt, obtfmt, apdo);
    596    }
    597
    598    return 0;
    599
    600 err:
    601    alsa_anal_close1 (&handle);
    602    return -1;
    603}
    604
    605static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len)
    606{
    607    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
    608    size_t pos = 0;
    609    size_t len_frames = len / hw->info.bytes_per_frame;
    610
    611    while (len_frames) {
    612        char *src = advance(buf, pos);
    613        snd_pcm_sframes_t written;
    614
    615        written = snd_pcm_writei(alsa->handle, src, len_frames);
    616
    617        if (written <= 0) {
    618            switch (written) {
    619            case 0:
    620                trace_alsa_wrote_zero(len_frames);
    621                return pos;
    622
    623            case -EPIPE:
    624                if (alsa_recover(alsa->handle)) {
    625                    alsa_logerr(written, "Failed to write %zu frames\n",
    626                                len_frames);
    627                    return pos;
    628                }
    629                trace_alsa_xrun_out();
    630                continue;
    631
    632            case -ESTRPIPE:
    633                /*
    634                 * stream is suspended and waiting for an application
    635                 * recovery
    636                 */
    637                if (alsa_resume(alsa->handle)) {
    638                    alsa_logerr(written, "Failed to write %zu frames\n",
    639                                len_frames);
    640                    return pos;
    641                }
    642                trace_alsa_resume_out();
    643                continue;
    644
    645            case -EAGAIN:
    646                return pos;
    647
    648            default:
    649                alsa_logerr(written, "Failed to write %zu frames from %p\n",
    650                            len, src);
    651                return pos;
    652            }
    653        }
    654
    655        pos += written * hw->info.bytes_per_frame;
    656        if (written < len_frames) {
    657            break;
    658        }
    659        len_frames -= written;
    660    }
    661
    662    return pos;
    663}
    664
    665static void alsa_fini_out (HWVoiceOut *hw)
    666{
    667    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
    668
    669    ldebug ("alsa_fini\n");
    670    alsa_anal_close (&alsa->handle, &alsa->pollhlp);
    671}
    672
    673static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
    674                         void *drv_opaque)
    675{
    676    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
    677    struct alsa_params_req req;
    678    struct alsa_params_obt obt;
    679    snd_pcm_t *handle;
    680    struct audsettings obt_as;
    681    Audiodev *dev = drv_opaque;
    682
    683    req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
    684    req.freq = as->freq;
    685    req.nchannels = as->nchannels;
    686
    687    if (alsa_open(0, &req, &obt, &handle, dev)) {
    688        return -1;
    689    }
    690
    691    obt_as.freq = obt.freq;
    692    obt_as.nchannels = obt.nchannels;
    693    obt_as.fmt = obt.fmt;
    694    obt_as.endianness = obt.endianness;
    695
    696    audio_pcm_init_info (&hw->info, &obt_as);
    697    hw->samples = obt.samples;
    698
    699    alsa->pollhlp.s = hw->s;
    700    alsa->handle = handle;
    701    alsa->dev = dev;
    702    return 0;
    703}
    704
    705#define VOICE_CTL_PAUSE 0
    706#define VOICE_CTL_PREPARE 1
    707#define VOICE_CTL_START 2
    708
    709static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
    710{
    711    int err;
    712
    713    if (ctl == VOICE_CTL_PAUSE) {
    714        err = snd_pcm_drop (handle);
    715        if (err < 0) {
    716            alsa_logerr (err, "Could not stop %s\n", typ);
    717            return -1;
    718        }
    719    } else {
    720        err = snd_pcm_prepare (handle);
    721        if (err < 0) {
    722            alsa_logerr (err, "Could not prepare handle for %s\n", typ);
    723            return -1;
    724        }
    725        if (ctl == VOICE_CTL_START) {
    726            err = snd_pcm_start(handle);
    727            if (err < 0) {
    728                alsa_logerr (err, "Could not start handle for %s\n", typ);
    729                return -1;
    730            }
    731        }
    732    }
    733
    734    return 0;
    735}
    736
    737static void alsa_enable_out(HWVoiceOut *hw, bool enable)
    738{
    739    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
    740    AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
    741
    742    if (enable) {
    743        bool poll_mode = apdo->try_poll;
    744
    745        ldebug("enabling voice\n");
    746        if (poll_mode && alsa_poll_out(hw)) {
    747            poll_mode = 0;
    748        }
    749        hw->poll_mode = poll_mode;
    750        alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PREPARE);
    751    } else {
    752        ldebug("disabling voice\n");
    753        if (hw->poll_mode) {
    754            hw->poll_mode = 0;
    755            alsa_fini_poll(&alsa->pollhlp);
    756        }
    757        alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PAUSE);
    758    }
    759}
    760
    761static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
    762{
    763    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
    764    struct alsa_params_req req;
    765    struct alsa_params_obt obt;
    766    snd_pcm_t *handle;
    767    struct audsettings obt_as;
    768    Audiodev *dev = drv_opaque;
    769
    770    req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
    771    req.freq = as->freq;
    772    req.nchannels = as->nchannels;
    773
    774    if (alsa_open(1, &req, &obt, &handle, dev)) {
    775        return -1;
    776    }
    777
    778    obt_as.freq = obt.freq;
    779    obt_as.nchannels = obt.nchannels;
    780    obt_as.fmt = obt.fmt;
    781    obt_as.endianness = obt.endianness;
    782
    783    audio_pcm_init_info (&hw->info, &obt_as);
    784    hw->samples = obt.samples;
    785
    786    alsa->pollhlp.s = hw->s;
    787    alsa->handle = handle;
    788    alsa->dev = dev;
    789    return 0;
    790}
    791
    792static void alsa_fini_in (HWVoiceIn *hw)
    793{
    794    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
    795
    796    alsa_anal_close (&alsa->handle, &alsa->pollhlp);
    797}
    798
    799static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len)
    800{
    801    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
    802    size_t pos = 0;
    803
    804    while (len) {
    805        void *dst = advance(buf, pos);
    806        snd_pcm_sframes_t nread;
    807
    808        nread = snd_pcm_readi(
    809            alsa->handle, dst, len / hw->info.bytes_per_frame);
    810
    811        if (nread <= 0) {
    812            switch (nread) {
    813            case 0:
    814                trace_alsa_read_zero(len);
    815                return pos;
    816
    817            case -EPIPE:
    818                if (alsa_recover(alsa->handle)) {
    819                    alsa_logerr(nread, "Failed to read %zu frames\n", len);
    820                    return pos;
    821                }
    822                trace_alsa_xrun_in();
    823                continue;
    824
    825            case -EAGAIN:
    826                return pos;
    827
    828            default:
    829                alsa_logerr(nread, "Failed to read %zu frames to %p\n",
    830                            len, dst);
    831                return pos;
    832            }
    833        }
    834
    835        pos += nread * hw->info.bytes_per_frame;
    836        len -= nread * hw->info.bytes_per_frame;
    837    }
    838
    839    return pos;
    840}
    841
    842static void alsa_enable_in(HWVoiceIn *hw, bool enable)
    843{
    844    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
    845    AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
    846
    847    if (enable) {
    848        bool poll_mode = apdo->try_poll;
    849
    850        ldebug("enabling voice\n");
    851        if (poll_mode && alsa_poll_in(hw)) {
    852            poll_mode = 0;
    853        }
    854        hw->poll_mode = poll_mode;
    855
    856        alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_START);
    857    } else {
    858        ldebug ("disabling voice\n");
    859        if (hw->poll_mode) {
    860            hw->poll_mode = 0;
    861            alsa_fini_poll(&alsa->pollhlp);
    862        }
    863        alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_PAUSE);
    864    }
    865}
    866
    867static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
    868{
    869    if (!apdo->has_try_poll) {
    870        apdo->try_poll = true;
    871        apdo->has_try_poll = true;
    872    }
    873}
    874
    875static void *alsa_audio_init(Audiodev *dev)
    876{
    877    AudiodevAlsaOptions *aopts;
    878    assert(dev->driver == AUDIODEV_DRIVER_ALSA);
    879
    880    aopts = &dev->u.alsa;
    881    alsa_init_per_direction(aopts->in);
    882    alsa_init_per_direction(aopts->out);
    883
    884    /*
    885     * need to define them, as otherwise alsa produces no sound
    886     * doesn't set has_* so alsa_open can identify it wasn't set by the user
    887     */
    888    if (!dev->u.alsa.out->has_period_length) {
    889        /* 1024 frames assuming 44100Hz */
    890        dev->u.alsa.out->period_length = 1024 * 1000000 / 44100;
    891    }
    892    if (!dev->u.alsa.out->has_buffer_length) {
    893        /* 4096 frames assuming 44100Hz */
    894        dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100;
    895    }
    896
    897    /*
    898     * OptsVisitor sets unspecified optional fields to zero, but do not depend
    899     * on it...
    900     */
    901    if (!dev->u.alsa.in->has_period_length) {
    902        dev->u.alsa.in->period_length = 0;
    903    }
    904    if (!dev->u.alsa.in->has_buffer_length) {
    905        dev->u.alsa.in->buffer_length = 0;
    906    }
    907
    908    return dev;
    909}
    910
    911static void alsa_audio_fini (void *opaque)
    912{
    913}
    914
    915static struct audio_pcm_ops alsa_pcm_ops = {
    916    .init_out = alsa_init_out,
    917    .fini_out = alsa_fini_out,
    918    .write    = alsa_write,
    919    .run_buffer_out = audio_generic_run_buffer_out,
    920    .enable_out = alsa_enable_out,
    921
    922    .init_in  = alsa_init_in,
    923    .fini_in  = alsa_fini_in,
    924    .read     = alsa_read,
    925    .run_buffer_in = audio_generic_run_buffer_in,
    926    .enable_in = alsa_enable_in,
    927};
    928
    929static struct audio_driver alsa_audio_driver = {
    930    .name           = "alsa",
    931    .descr          = "ALSA http://www.alsa-project.org",
    932    .init           = alsa_audio_init,
    933    .fini           = alsa_audio_fini,
    934    .pcm_ops        = &alsa_pcm_ops,
    935    .can_be_default = 1,
    936    .max_voices_out = INT_MAX,
    937    .max_voices_in  = INT_MAX,
    938    .voice_size_out = sizeof (ALSAVoiceOut),
    939    .voice_size_in  = sizeof (ALSAVoiceIn)
    940};
    941
    942static void register_audio_alsa(void)
    943{
    944    audio_driver_register(&alsa_audio_driver);
    945}
    946type_init(register_audio_alsa);