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

paaudio.c (24745B)


      1/* public domain */
      2
      3#include "qemu/osdep.h"
      4#include "qemu/module.h"
      5#include "audio.h"
      6#include "qapi/opts-visitor.h"
      7
      8#include <pulse/pulseaudio.h>
      9
     10#define AUDIO_CAP "pulseaudio"
     11#include "audio_int.h"
     12
     13typedef struct PAConnection {
     14    char *server;
     15    int refcount;
     16    QTAILQ_ENTRY(PAConnection) list;
     17
     18    pa_threaded_mainloop *mainloop;
     19    pa_context *context;
     20} PAConnection;
     21
     22static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns =
     23    QTAILQ_HEAD_INITIALIZER(pa_conns);
     24
     25typedef struct {
     26    Audiodev *dev;
     27    PAConnection *conn;
     28} paaudio;
     29
     30typedef struct {
     31    HWVoiceOut hw;
     32    pa_stream *stream;
     33    paaudio *g;
     34} PAVoiceOut;
     35
     36typedef struct {
     37    HWVoiceIn hw;
     38    pa_stream *stream;
     39    const void *read_data;
     40    size_t read_length;
     41    paaudio *g;
     42} PAVoiceIn;
     43
     44static void qpa_conn_fini(PAConnection *c);
     45
     46static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
     47{
     48    va_list ap;
     49
     50    va_start (ap, fmt);
     51    AUD_vlog (AUDIO_CAP, fmt, ap);
     52    va_end (ap);
     53
     54    AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
     55}
     56
     57#ifndef PA_CONTEXT_IS_GOOD
     58static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
     59{
     60    return
     61        x == PA_CONTEXT_CONNECTING ||
     62        x == PA_CONTEXT_AUTHORIZING ||
     63        x == PA_CONTEXT_SETTING_NAME ||
     64        x == PA_CONTEXT_READY;
     65}
     66#endif
     67
     68#ifndef PA_STREAM_IS_GOOD
     69static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
     70{
     71    return
     72        x == PA_STREAM_CREATING ||
     73        x == PA_STREAM_READY;
     74}
     75#endif
     76
     77#define CHECK_SUCCESS_GOTO(c, expression, label, msg)           \
     78    do {                                                        \
     79        if (!(expression)) {                                    \
     80            qpa_logerr(pa_context_errno((c)->context), msg);    \
     81            goto label;                                         \
     82        }                                                       \
     83    } while (0)
     84
     85#define CHECK_DEAD_GOTO(c, stream, label, msg)                          \
     86    do {                                                                \
     87        if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
     88            !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
     89            if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
     90                ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
     91                qpa_logerr(pa_context_errno((c)->context), msg);        \
     92            } else {                                                    \
     93                qpa_logerr(PA_ERR_BADSTATE, msg);                       \
     94            }                                                           \
     95            goto label;                                                 \
     96        }                                                               \
     97    } while (0)
     98
     99static void *qpa_get_buffer_in(HWVoiceIn *hw, size_t *size)
    100{
    101    PAVoiceIn *p = (PAVoiceIn *) hw;
    102    PAConnection *c = p->g->conn;
    103    int r;
    104
    105    pa_threaded_mainloop_lock(c->mainloop);
    106
    107    CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
    108                    "pa_threaded_mainloop_lock failed\n");
    109
    110    if (!p->read_length) {
    111        r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
    112        CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
    113                           "pa_stream_peek failed\n");
    114    }
    115
    116    *size = MIN(p->read_length, *size);
    117
    118    pa_threaded_mainloop_unlock(c->mainloop);
    119    return (void *) p->read_data;
    120
    121unlock_and_fail:
    122    pa_threaded_mainloop_unlock(c->mainloop);
    123    *size = 0;
    124    return NULL;
    125}
    126
    127static void qpa_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
    128{
    129    PAVoiceIn *p = (PAVoiceIn *) hw;
    130    PAConnection *c = p->g->conn;
    131    int r;
    132
    133    pa_threaded_mainloop_lock(c->mainloop);
    134
    135    CHECK_DEAD_GOTO(c, p->stream, unlock,
    136                    "pa_threaded_mainloop_lock failed\n");
    137
    138    assert(buf == p->read_data && size <= p->read_length);
    139
    140    p->read_data += size;
    141    p->read_length -= size;
    142
    143    if (size && !p->read_length) {
    144        r = pa_stream_drop(p->stream);
    145        CHECK_SUCCESS_GOTO(c, r == 0, unlock, "pa_stream_drop failed\n");
    146    }
    147
    148unlock:
    149    pa_threaded_mainloop_unlock(c->mainloop);
    150}
    151
    152static size_t qpa_read(HWVoiceIn *hw, void *data, size_t length)
    153{
    154    PAVoiceIn *p = (PAVoiceIn *) hw;
    155    PAConnection *c = p->g->conn;
    156    size_t total = 0;
    157
    158    pa_threaded_mainloop_lock(c->mainloop);
    159
    160    CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
    161                    "pa_threaded_mainloop_lock failed\n");
    162    if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
    163        /* wait for stream to become ready */
    164        goto unlock;
    165    }
    166
    167    while (total < length) {
    168        size_t l;
    169        int r;
    170
    171        if (!p->read_length) {
    172            r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
    173            CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
    174                               "pa_stream_peek failed\n");
    175            if (!p->read_length) {
    176                /* buffer is empty */
    177                break;
    178            }
    179        }
    180
    181        l = MIN(p->read_length, length - total);
    182        memcpy((char *)data + total, p->read_data, l);
    183
    184        p->read_data += l;
    185        p->read_length -= l;
    186        total += l;
    187
    188        if (!p->read_length) {
    189            r = pa_stream_drop(p->stream);
    190            CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
    191                               "pa_stream_drop failed\n");
    192        }
    193    }
    194
    195unlock:
    196    pa_threaded_mainloop_unlock(c->mainloop);
    197    return total;
    198
    199unlock_and_fail:
    200    pa_threaded_mainloop_unlock(c->mainloop);
    201    return 0;
    202}
    203
    204static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
    205{
    206    PAVoiceOut *p = (PAVoiceOut *) hw;
    207    PAConnection *c = p->g->conn;
    208    void *ret;
    209    size_t l;
    210    int r;
    211
    212    pa_threaded_mainloop_lock(c->mainloop);
    213
    214    CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
    215                    "pa_threaded_mainloop_lock failed\n");
    216    if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
    217        /* wait for stream to become ready */
    218        l = 0;
    219        ret = NULL;
    220        goto unlock;
    221    }
    222
    223    l = pa_stream_writable_size(p->stream);
    224    CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
    225                       "pa_stream_writable_size failed\n");
    226
    227    *size = -1;
    228    r = pa_stream_begin_write(p->stream, &ret, size);
    229    CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail,
    230                       "pa_stream_begin_write failed\n");
    231
    232unlock:
    233    pa_threaded_mainloop_unlock(c->mainloop);
    234    if (*size > l) {
    235        *size = l;
    236    }
    237    return ret;
    238
    239unlock_and_fail:
    240    pa_threaded_mainloop_unlock(c->mainloop);
    241    *size = 0;
    242    return NULL;
    243}
    244
    245static size_t qpa_put_buffer_out(HWVoiceOut *hw, void *data, size_t length)
    246{
    247    PAVoiceOut *p = (PAVoiceOut *)hw;
    248    PAConnection *c = p->g->conn;
    249    int r;
    250
    251    pa_threaded_mainloop_lock(c->mainloop);
    252
    253    CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
    254                    "pa_threaded_mainloop_lock failed\n");
    255
    256    r = pa_stream_write(p->stream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
    257    CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n");
    258
    259    pa_threaded_mainloop_unlock(c->mainloop);
    260    return length;
    261
    262unlock_and_fail:
    263    pa_threaded_mainloop_unlock(c->mainloop);
    264    return 0;
    265}
    266
    267static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length)
    268{
    269    PAVoiceOut *p = (PAVoiceOut *) hw;
    270    PAConnection *c = p->g->conn;
    271    size_t l;
    272    int r;
    273
    274    pa_threaded_mainloop_lock(c->mainloop);
    275
    276    CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
    277                    "pa_threaded_mainloop_lock failed\n");
    278    if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
    279        /* wait for stream to become ready */
    280        l = 0;
    281        goto unlock;
    282    }
    283
    284    l = pa_stream_writable_size(p->stream);
    285
    286    CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
    287                       "pa_stream_writable_size failed\n");
    288
    289    if (l > length) {
    290        l = length;
    291    }
    292
    293    r = pa_stream_write(p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
    294    CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n");
    295
    296unlock:
    297    pa_threaded_mainloop_unlock(c->mainloop);
    298    return l;
    299
    300unlock_and_fail:
    301    pa_threaded_mainloop_unlock(c->mainloop);
    302    return 0;
    303}
    304
    305static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
    306{
    307    int format;
    308
    309    switch (afmt) {
    310    case AUDIO_FORMAT_S8:
    311    case AUDIO_FORMAT_U8:
    312        format = PA_SAMPLE_U8;
    313        break;
    314    case AUDIO_FORMAT_S16:
    315    case AUDIO_FORMAT_U16:
    316        format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
    317        break;
    318    case AUDIO_FORMAT_S32:
    319    case AUDIO_FORMAT_U32:
    320        format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
    321        break;
    322    case AUDIO_FORMAT_F32:
    323        format = endianness ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE;
    324        break;
    325    default:
    326        dolog ("Internal logic error: Bad audio format %d\n", afmt);
    327        format = PA_SAMPLE_U8;
    328        break;
    329    }
    330    return format;
    331}
    332
    333static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
    334{
    335    switch (fmt) {
    336    case PA_SAMPLE_U8:
    337        return AUDIO_FORMAT_U8;
    338    case PA_SAMPLE_S16BE:
    339        *endianness = 1;
    340        return AUDIO_FORMAT_S16;
    341    case PA_SAMPLE_S16LE:
    342        *endianness = 0;
    343        return AUDIO_FORMAT_S16;
    344    case PA_SAMPLE_S32BE:
    345        *endianness = 1;
    346        return AUDIO_FORMAT_S32;
    347    case PA_SAMPLE_S32LE:
    348        *endianness = 0;
    349        return AUDIO_FORMAT_S32;
    350    case PA_SAMPLE_FLOAT32BE:
    351        *endianness = 1;
    352        return AUDIO_FORMAT_F32;
    353    case PA_SAMPLE_FLOAT32LE:
    354        *endianness = 0;
    355        return AUDIO_FORMAT_F32;
    356    default:
    357        dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
    358        return AUDIO_FORMAT_U8;
    359    }
    360}
    361
    362static void context_state_cb (pa_context *c, void *userdata)
    363{
    364    PAConnection *conn = userdata;
    365
    366    switch (pa_context_get_state(c)) {
    367    case PA_CONTEXT_READY:
    368    case PA_CONTEXT_TERMINATED:
    369    case PA_CONTEXT_FAILED:
    370        pa_threaded_mainloop_signal(conn->mainloop, 0);
    371        break;
    372
    373    case PA_CONTEXT_UNCONNECTED:
    374    case PA_CONTEXT_CONNECTING:
    375    case PA_CONTEXT_AUTHORIZING:
    376    case PA_CONTEXT_SETTING_NAME:
    377        break;
    378    }
    379}
    380
    381static void stream_state_cb (pa_stream *s, void * userdata)
    382{
    383    PAConnection *c = userdata;
    384
    385    switch (pa_stream_get_state (s)) {
    386
    387    case PA_STREAM_READY:
    388    case PA_STREAM_FAILED:
    389    case PA_STREAM_TERMINATED:
    390        pa_threaded_mainloop_signal(c->mainloop, 0);
    391        break;
    392
    393    case PA_STREAM_UNCONNECTED:
    394    case PA_STREAM_CREATING:
    395        break;
    396    }
    397}
    398
    399static pa_stream *qpa_simple_new (
    400        PAConnection *c,
    401        const char *name,
    402        pa_stream_direction_t dir,
    403        const char *dev,
    404        const pa_sample_spec *ss,
    405        const pa_buffer_attr *attr,
    406        int *rerror)
    407{
    408    int r;
    409    pa_stream *stream = NULL;
    410    pa_stream_flags_t flags;
    411    pa_channel_map map;
    412
    413    pa_threaded_mainloop_lock(c->mainloop);
    414
    415    pa_channel_map_init(&map);
    416    map.channels = ss->channels;
    417
    418    /*
    419     * TODO: This currently expects the only frontend supporting more than 2
    420     * channels is the usb-audio.  We will need some means to set channel
    421     * order when a new frontend gains multi-channel support.
    422     */
    423    switch (ss->channels) {
    424    case 1:
    425        map.map[0] = PA_CHANNEL_POSITION_MONO;
    426        break;
    427
    428    case 2:
    429        map.map[0] = PA_CHANNEL_POSITION_LEFT;
    430        map.map[1] = PA_CHANNEL_POSITION_RIGHT;
    431        break;
    432
    433    case 6:
    434        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
    435        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
    436        map.map[2] = PA_CHANNEL_POSITION_CENTER;
    437        map.map[3] = PA_CHANNEL_POSITION_LFE;
    438        map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
    439        map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
    440        break;
    441
    442    case 8:
    443        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
    444        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
    445        map.map[2] = PA_CHANNEL_POSITION_CENTER;
    446        map.map[3] = PA_CHANNEL_POSITION_LFE;
    447        map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
    448        map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
    449        map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
    450        map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
    451        break;
    452
    453    default:
    454        dolog("Internal error: unsupported channel count %d\n", ss->channels);
    455        goto fail;
    456    }
    457
    458    stream = pa_stream_new(c->context, name, ss, &map);
    459    if (!stream) {
    460        goto fail;
    461    }
    462
    463    pa_stream_set_state_callback(stream, stream_state_cb, c);
    464
    465    flags = PA_STREAM_EARLY_REQUESTS;
    466
    467    if (dev) {
    468        /* don't move the stream if the user specified a sink/source */
    469        flags |= PA_STREAM_DONT_MOVE;
    470    }
    471
    472    if (dir == PA_STREAM_PLAYBACK) {
    473        r = pa_stream_connect_playback(stream, dev, attr, flags, NULL, NULL);
    474    } else {
    475        r = pa_stream_connect_record(stream, dev, attr, flags);
    476    }
    477
    478    if (r < 0) {
    479        goto fail;
    480    }
    481
    482    pa_threaded_mainloop_unlock(c->mainloop);
    483
    484    return stream;
    485
    486fail:
    487    pa_threaded_mainloop_unlock(c->mainloop);
    488
    489    if (stream) {
    490        pa_stream_unref (stream);
    491    }
    492
    493    *rerror = pa_context_errno(c->context);
    494
    495    return NULL;
    496}
    497
    498static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
    499                        void *drv_opaque)
    500{
    501    int error;
    502    pa_sample_spec ss;
    503    pa_buffer_attr ba;
    504    struct audsettings obt_as = *as;
    505    PAVoiceOut *pa = (PAVoiceOut *) hw;
    506    paaudio *g = pa->g = drv_opaque;
    507    AudiodevPaOptions *popts = &g->dev->u.pa;
    508    AudiodevPaPerDirectionOptions *ppdo = popts->out;
    509    PAConnection *c = g->conn;
    510
    511    ss.format = audfmt_to_pa (as->fmt, as->endianness);
    512    ss.channels = as->nchannels;
    513    ss.rate = as->freq;
    514
    515    ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss);
    516    ba.minreq = pa_usec_to_bytes(MIN(ppdo->latency >> 2,
    517                                     (g->dev->timer_period >> 2) * 3), &ss);
    518    ba.maxlength = -1;
    519    ba.prebuf = -1;
    520
    521    obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
    522
    523    pa->stream = qpa_simple_new (
    524        c,
    525        ppdo->has_stream_name ? ppdo->stream_name : g->dev->id,
    526        PA_STREAM_PLAYBACK,
    527        ppdo->has_name ? ppdo->name : NULL,
    528        &ss,
    529        &ba,                    /* buffering attributes */
    530        &error
    531        );
    532    if (!pa->stream) {
    533        qpa_logerr (error, "pa_simple_new for playback failed\n");
    534        goto fail1;
    535    }
    536
    537    audio_pcm_init_info (&hw->info, &obt_as);
    538    /*
    539     * This is wrong. hw->samples counts in frames. hw->samples will be
    540     * number of channels times larger than expected.
    541     */
    542    hw->samples = audio_buffer_samples(
    543        qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
    544
    545    return 0;
    546
    547 fail1:
    548    return -1;
    549}
    550
    551static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
    552{
    553    int error;
    554    pa_sample_spec ss;
    555    pa_buffer_attr ba;
    556    struct audsettings obt_as = *as;
    557    PAVoiceIn *pa = (PAVoiceIn *) hw;
    558    paaudio *g = pa->g = drv_opaque;
    559    AudiodevPaOptions *popts = &g->dev->u.pa;
    560    AudiodevPaPerDirectionOptions *ppdo = popts->in;
    561    PAConnection *c = g->conn;
    562
    563    ss.format = audfmt_to_pa (as->fmt, as->endianness);
    564    ss.channels = as->nchannels;
    565    ss.rate = as->freq;
    566
    567    ba.fragsize = pa_usec_to_bytes((g->dev->timer_period >> 1) * 3, &ss);
    568    ba.maxlength = pa_usec_to_bytes(
    569        MAX(ppdo->latency, g->dev->timer_period * 3), &ss);
    570    ba.minreq = -1;
    571    ba.prebuf = -1;
    572
    573    obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
    574
    575    pa->stream = qpa_simple_new (
    576        c,
    577        ppdo->has_stream_name ? ppdo->stream_name : g->dev->id,
    578        PA_STREAM_RECORD,
    579        ppdo->has_name ? ppdo->name : NULL,
    580        &ss,
    581        &ba,                    /* buffering attributes */
    582        &error
    583        );
    584    if (!pa->stream) {
    585        qpa_logerr (error, "pa_simple_new for capture failed\n");
    586        goto fail1;
    587    }
    588
    589    audio_pcm_init_info (&hw->info, &obt_as);
    590    /*
    591     * This is wrong. hw->samples counts in frames. hw->samples will be
    592     * number of channels times larger than expected.
    593     */
    594    hw->samples = audio_buffer_samples(
    595        qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
    596
    597    return 0;
    598
    599 fail1:
    600    return -1;
    601}
    602
    603static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
    604{
    605    int err;
    606
    607    /*
    608     * wait until actually connects. workaround pa bug #247
    609     * https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/247
    610     */
    611    while (pa_stream_get_state(stream) == PA_STREAM_CREATING) {
    612        pa_threaded_mainloop_wait(c->mainloop);
    613    }
    614
    615    err = pa_stream_disconnect(stream);
    616    if (err != 0) {
    617        dolog("Failed to disconnect! err=%d\n", err);
    618    }
    619    pa_stream_unref(stream);
    620}
    621
    622static void qpa_fini_out (HWVoiceOut *hw)
    623{
    624    PAVoiceOut *pa = (PAVoiceOut *) hw;
    625
    626    if (pa->stream) {
    627        PAConnection *c = pa->g->conn;
    628
    629        pa_threaded_mainloop_lock(c->mainloop);
    630        qpa_simple_disconnect(c, pa->stream);
    631        pa->stream = NULL;
    632        pa_threaded_mainloop_unlock(c->mainloop);
    633    }
    634}
    635
    636static void qpa_fini_in (HWVoiceIn *hw)
    637{
    638    PAVoiceIn *pa = (PAVoiceIn *) hw;
    639
    640    if (pa->stream) {
    641        PAConnection *c = pa->g->conn;
    642
    643        pa_threaded_mainloop_lock(c->mainloop);
    644        if (pa->read_length) {
    645            int r = pa_stream_drop(pa->stream);
    646            if (r) {
    647                qpa_logerr(pa_context_errno(c->context),
    648                           "pa_stream_drop failed\n");
    649            }
    650            pa->read_length = 0;
    651        }
    652        qpa_simple_disconnect(c, pa->stream);
    653        pa->stream = NULL;
    654        pa_threaded_mainloop_unlock(c->mainloop);
    655    }
    656}
    657
    658static void qpa_volume_out(HWVoiceOut *hw, Volume *vol)
    659{
    660    PAVoiceOut *pa = (PAVoiceOut *) hw;
    661    pa_operation *op;
    662    pa_cvolume v;
    663    PAConnection *c = pa->g->conn;
    664    int i;
    665
    666#ifdef PA_CHECK_VERSION    /* macro is present in 0.9.16+ */
    667    pa_cvolume_init (&v);  /* function is present in 0.9.13+ */
    668#endif
    669
    670    v.channels = vol->channels;
    671    for (i = 0; i < vol->channels; ++i) {
    672        v.values[i] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->vol[i]) / 255;
    673    }
    674
    675    pa_threaded_mainloop_lock(c->mainloop);
    676
    677    op = pa_context_set_sink_input_volume(c->context,
    678                                          pa_stream_get_index(pa->stream),
    679                                          &v, NULL, NULL);
    680    if (!op) {
    681        qpa_logerr(pa_context_errno(c->context),
    682                   "set_sink_input_volume() failed\n");
    683    } else {
    684        pa_operation_unref(op);
    685    }
    686
    687    op = pa_context_set_sink_input_mute(c->context,
    688                                        pa_stream_get_index(pa->stream),
    689                                        vol->mute, NULL, NULL);
    690    if (!op) {
    691        qpa_logerr(pa_context_errno(c->context),
    692                   "set_sink_input_mute() failed\n");
    693    } else {
    694        pa_operation_unref(op);
    695    }
    696
    697    pa_threaded_mainloop_unlock(c->mainloop);
    698}
    699
    700static void qpa_volume_in(HWVoiceIn *hw, Volume *vol)
    701{
    702    PAVoiceIn *pa = (PAVoiceIn *) hw;
    703    pa_operation *op;
    704    pa_cvolume v;
    705    PAConnection *c = pa->g->conn;
    706    int i;
    707
    708#ifdef PA_CHECK_VERSION
    709    pa_cvolume_init (&v);
    710#endif
    711
    712    v.channels = vol->channels;
    713    for (i = 0; i < vol->channels; ++i) {
    714        v.values[i] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->vol[i]) / 255;
    715    }
    716
    717    pa_threaded_mainloop_lock(c->mainloop);
    718
    719    op = pa_context_set_source_output_volume(c->context,
    720        pa_stream_get_index(pa->stream),
    721        &v, NULL, NULL);
    722    if (!op) {
    723        qpa_logerr(pa_context_errno(c->context),
    724                   "set_source_output_volume() failed\n");
    725    } else {
    726        pa_operation_unref(op);
    727    }
    728
    729    op = pa_context_set_source_output_mute(c->context,
    730        pa_stream_get_index(pa->stream),
    731        vol->mute, NULL, NULL);
    732    if (!op) {
    733        qpa_logerr(pa_context_errno(c->context),
    734                   "set_source_output_mute() failed\n");
    735    } else {
    736        pa_operation_unref(op);
    737    }
    738
    739    pa_threaded_mainloop_unlock(c->mainloop);
    740}
    741
    742static int qpa_validate_per_direction_opts(Audiodev *dev,
    743                                           AudiodevPaPerDirectionOptions *pdo)
    744{
    745    if (!pdo->has_latency) {
    746        pdo->has_latency = true;
    747        pdo->latency = 15000;
    748    }
    749    return 1;
    750}
    751
    752/* common */
    753static void *qpa_conn_init(const char *server)
    754{
    755    PAConnection *c = g_malloc0(sizeof(PAConnection));
    756    QTAILQ_INSERT_TAIL(&pa_conns, c, list);
    757
    758    c->mainloop = pa_threaded_mainloop_new();
    759    if (!c->mainloop) {
    760        goto fail;
    761    }
    762
    763    c->context = pa_context_new(pa_threaded_mainloop_get_api(c->mainloop),
    764                                audio_application_name());
    765    if (!c->context) {
    766        goto fail;
    767    }
    768
    769    pa_context_set_state_callback(c->context, context_state_cb, c);
    770
    771    if (pa_context_connect(c->context, server, 0, NULL) < 0) {
    772        qpa_logerr(pa_context_errno(c->context),
    773                   "pa_context_connect() failed\n");
    774        goto fail;
    775    }
    776
    777    pa_threaded_mainloop_lock(c->mainloop);
    778
    779    if (pa_threaded_mainloop_start(c->mainloop) < 0) {
    780        goto unlock_and_fail;
    781    }
    782
    783    for (;;) {
    784        pa_context_state_t state;
    785
    786        state = pa_context_get_state(c->context);
    787
    788        if (state == PA_CONTEXT_READY) {
    789            break;
    790        }
    791
    792        if (!PA_CONTEXT_IS_GOOD(state)) {
    793            qpa_logerr(pa_context_errno(c->context),
    794                       "Wrong context state\n");
    795            goto unlock_and_fail;
    796        }
    797
    798        /* Wait until the context is ready */
    799        pa_threaded_mainloop_wait(c->mainloop);
    800    }
    801
    802    pa_threaded_mainloop_unlock(c->mainloop);
    803    return c;
    804
    805unlock_and_fail:
    806    pa_threaded_mainloop_unlock(c->mainloop);
    807fail:
    808    AUD_log (AUDIO_CAP, "Failed to initialize PA context");
    809    qpa_conn_fini(c);
    810    return NULL;
    811}
    812
    813static void *qpa_audio_init(Audiodev *dev)
    814{
    815    paaudio *g;
    816    AudiodevPaOptions *popts = &dev->u.pa;
    817    const char *server;
    818    PAConnection *c;
    819
    820    assert(dev->driver == AUDIODEV_DRIVER_PA);
    821
    822    if (!popts->has_server) {
    823        char pidfile[64];
    824        char *runtime;
    825        struct stat st;
    826
    827        runtime = getenv("XDG_RUNTIME_DIR");
    828        if (!runtime) {
    829            return NULL;
    830        }
    831        snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime);
    832        if (stat(pidfile, &st) != 0) {
    833            return NULL;
    834        }
    835    }
    836
    837    if (!qpa_validate_per_direction_opts(dev, popts->in)) {
    838        return NULL;
    839    }
    840    if (!qpa_validate_per_direction_opts(dev, popts->out)) {
    841        return NULL;
    842    }
    843
    844    g = g_malloc0(sizeof(paaudio));
    845    server = popts->has_server ? popts->server : NULL;
    846
    847    g->dev = dev;
    848
    849    QTAILQ_FOREACH(c, &pa_conns, list) {
    850        if (server == NULL || c->server == NULL ?
    851            server == c->server :
    852            strcmp(server, c->server) == 0) {
    853            g->conn = c;
    854            break;
    855        }
    856    }
    857    if (!g->conn) {
    858        g->conn = qpa_conn_init(server);
    859    }
    860    if (!g->conn) {
    861        g_free(g);
    862        return NULL;
    863    }
    864
    865    ++g->conn->refcount;
    866    return g;
    867}
    868
    869static void qpa_conn_fini(PAConnection *c)
    870{
    871    if (c->mainloop) {
    872        pa_threaded_mainloop_stop(c->mainloop);
    873    }
    874
    875    if (c->context) {
    876        pa_context_disconnect(c->context);
    877        pa_context_unref(c->context);
    878    }
    879
    880    if (c->mainloop) {
    881        pa_threaded_mainloop_free(c->mainloop);
    882    }
    883
    884    QTAILQ_REMOVE(&pa_conns, c, list);
    885    g_free(c);
    886}
    887
    888static void qpa_audio_fini (void *opaque)
    889{
    890    paaudio *g = opaque;
    891    PAConnection *c = g->conn;
    892
    893    if (--c->refcount == 0) {
    894        qpa_conn_fini(c);
    895    }
    896
    897    g_free(g);
    898}
    899
    900static struct audio_pcm_ops qpa_pcm_ops = {
    901    .init_out = qpa_init_out,
    902    .fini_out = qpa_fini_out,
    903    .write    = qpa_write,
    904    .get_buffer_out = qpa_get_buffer_out,
    905    .put_buffer_out = qpa_put_buffer_out,
    906    .volume_out = qpa_volume_out,
    907
    908    .init_in  = qpa_init_in,
    909    .fini_in  = qpa_fini_in,
    910    .read     = qpa_read,
    911    .get_buffer_in = qpa_get_buffer_in,
    912    .put_buffer_in = qpa_put_buffer_in,
    913    .volume_in = qpa_volume_in
    914};
    915
    916static struct audio_driver pa_audio_driver = {
    917    .name           = "pa",
    918    .descr          = "http://www.pulseaudio.org/",
    919    .init           = qpa_audio_init,
    920    .fini           = qpa_audio_fini,
    921    .pcm_ops        = &qpa_pcm_ops,
    922    .can_be_default = 1,
    923    .max_voices_out = INT_MAX,
    924    .max_voices_in  = INT_MAX,
    925    .voice_size_out = sizeof (PAVoiceOut),
    926    .voice_size_in  = sizeof (PAVoiceIn),
    927};
    928
    929static void register_audio_pa(void)
    930{
    931    audio_driver_register(&pa_audio_driver);
    932}
    933type_init(register_audio_pa);