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

coreaudio.c (22250B)


      1/*
      2 * QEMU OS X CoreAudio audio driver
      3 *
      4 * Copyright (c) 2005 Mike Kronenberg
      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 <CoreAudio/CoreAudio.h>
     27#include <pthread.h>            /* pthread_X */
     28
     29#include "qemu/main-loop.h"
     30#include "qemu/module.h"
     31#include "audio.h"
     32
     33#define AUDIO_CAP "coreaudio"
     34#include "audio_int.h"
     35
     36typedef struct coreaudioVoiceOut {
     37    HWVoiceOut hw;
     38    pthread_mutex_t buf_mutex;
     39    AudioDeviceID outputDeviceID;
     40    int frameSizeSetting;
     41    uint32_t bufferCount;
     42    UInt32 audioDevicePropertyBufferFrameSize;
     43    AudioDeviceIOProcID ioprocid;
     44    bool enabled;
     45} coreaudioVoiceOut;
     46
     47static const AudioObjectPropertyAddress voice_addr = {
     48    kAudioHardwarePropertyDefaultOutputDevice,
     49    kAudioObjectPropertyScopeGlobal,
     50    kAudioObjectPropertyElementMaster
     51};
     52
     53static OSStatus coreaudio_get_voice(AudioDeviceID *id)
     54{
     55    UInt32 size = sizeof(*id);
     56
     57    return AudioObjectGetPropertyData(kAudioObjectSystemObject,
     58                                      &voice_addr,
     59                                      0,
     60                                      NULL,
     61                                      &size,
     62                                      id);
     63}
     64
     65static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
     66                                             AudioValueRange *framerange)
     67{
     68    UInt32 size = sizeof(*framerange);
     69    AudioObjectPropertyAddress addr = {
     70        kAudioDevicePropertyBufferFrameSizeRange,
     71        kAudioDevicePropertyScopeOutput,
     72        kAudioObjectPropertyElementMaster
     73    };
     74
     75    return AudioObjectGetPropertyData(id,
     76                                      &addr,
     77                                      0,
     78                                      NULL,
     79                                      &size,
     80                                      framerange);
     81}
     82
     83static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
     84{
     85    UInt32 size = sizeof(*framesize);
     86    AudioObjectPropertyAddress addr = {
     87        kAudioDevicePropertyBufferFrameSize,
     88        kAudioDevicePropertyScopeOutput,
     89        kAudioObjectPropertyElementMaster
     90    };
     91
     92    return AudioObjectGetPropertyData(id,
     93                                      &addr,
     94                                      0,
     95                                      NULL,
     96                                      &size,
     97                                      framesize);
     98}
     99
    100static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
    101{
    102    UInt32 size = sizeof(*framesize);
    103    AudioObjectPropertyAddress addr = {
    104        kAudioDevicePropertyBufferFrameSize,
    105        kAudioDevicePropertyScopeOutput,
    106        kAudioObjectPropertyElementMaster
    107    };
    108
    109    return AudioObjectSetPropertyData(id,
    110                                      &addr,
    111                                      0,
    112                                      NULL,
    113                                      size,
    114                                      framesize);
    115}
    116
    117static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
    118                                           AudioStreamBasicDescription *d)
    119{
    120    UInt32 size = sizeof(*d);
    121    AudioObjectPropertyAddress addr = {
    122        kAudioDevicePropertyStreamFormat,
    123        kAudioDevicePropertyScopeOutput,
    124        kAudioObjectPropertyElementMaster
    125    };
    126
    127    return AudioObjectSetPropertyData(id,
    128                                      &addr,
    129                                      0,
    130                                      NULL,
    131                                      size,
    132                                      d);
    133}
    134
    135static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
    136{
    137    UInt32 size = sizeof(*result);
    138    AudioObjectPropertyAddress addr = {
    139        kAudioDevicePropertyDeviceIsRunning,
    140        kAudioDevicePropertyScopeOutput,
    141        kAudioObjectPropertyElementMaster
    142    };
    143
    144    return AudioObjectGetPropertyData(id,
    145                                      &addr,
    146                                      0,
    147                                      NULL,
    148                                      &size,
    149                                      result);
    150}
    151
    152static void coreaudio_logstatus (OSStatus status)
    153{
    154    const char *str = "BUG";
    155
    156    switch (status) {
    157    case kAudioHardwareNoError:
    158        str = "kAudioHardwareNoError";
    159        break;
    160
    161    case kAudioHardwareNotRunningError:
    162        str = "kAudioHardwareNotRunningError";
    163        break;
    164
    165    case kAudioHardwareUnspecifiedError:
    166        str = "kAudioHardwareUnspecifiedError";
    167        break;
    168
    169    case kAudioHardwareUnknownPropertyError:
    170        str = "kAudioHardwareUnknownPropertyError";
    171        break;
    172
    173    case kAudioHardwareBadPropertySizeError:
    174        str = "kAudioHardwareBadPropertySizeError";
    175        break;
    176
    177    case kAudioHardwareIllegalOperationError:
    178        str = "kAudioHardwareIllegalOperationError";
    179        break;
    180
    181    case kAudioHardwareBadDeviceError:
    182        str = "kAudioHardwareBadDeviceError";
    183        break;
    184
    185    case kAudioHardwareBadStreamError:
    186        str = "kAudioHardwareBadStreamError";
    187        break;
    188
    189    case kAudioHardwareUnsupportedOperationError:
    190        str = "kAudioHardwareUnsupportedOperationError";
    191        break;
    192
    193    case kAudioDeviceUnsupportedFormatError:
    194        str = "kAudioDeviceUnsupportedFormatError";
    195        break;
    196
    197    case kAudioDevicePermissionsError:
    198        str = "kAudioDevicePermissionsError";
    199        break;
    200
    201    default:
    202        AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
    203        return;
    204    }
    205
    206    AUD_log (AUDIO_CAP, "Reason: %s\n", str);
    207}
    208
    209static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
    210    OSStatus status,
    211    const char *fmt,
    212    ...
    213    )
    214{
    215    va_list ap;
    216
    217    va_start (ap, fmt);
    218    AUD_log (AUDIO_CAP, fmt, ap);
    219    va_end (ap);
    220
    221    coreaudio_logstatus (status);
    222}
    223
    224static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
    225    OSStatus status,
    226    const char *typ,
    227    const char *fmt,
    228    ...
    229    )
    230{
    231    va_list ap;
    232
    233    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
    234
    235    va_start (ap, fmt);
    236    AUD_vlog (AUDIO_CAP, fmt, ap);
    237    va_end (ap);
    238
    239    coreaudio_logstatus (status);
    240}
    241
    242#define coreaudio_playback_logerr(status, ...) \
    243    coreaudio_logerr2(status, "playback", __VA_ARGS__)
    244
    245static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name)
    246{
    247    int err;
    248
    249    err = pthread_mutex_lock (&core->buf_mutex);
    250    if (err) {
    251        dolog ("Could not lock voice for %s\nReason: %s\n",
    252               fn_name, strerror (err));
    253        return -1;
    254    }
    255    return 0;
    256}
    257
    258static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
    259{
    260    int err;
    261
    262    err = pthread_mutex_unlock (&core->buf_mutex);
    263    if (err) {
    264        dolog ("Could not unlock voice for %s\nReason: %s\n",
    265               fn_name, strerror (err));
    266        return -1;
    267    }
    268    return 0;
    269}
    270
    271#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
    272    static ret_type glue(coreaudio_, name)args_decl             \
    273    {                                                           \
    274        coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;     \
    275        ret_type ret;                                           \
    276                                                                \
    277        if (coreaudio_buf_lock(core, "coreaudio_" #name)) {         \
    278            return 0;                                           \
    279        }                                                       \
    280                                                                \
    281        ret = glue(audio_generic_, name)args;                   \
    282                                                                \
    283        coreaudio_buf_unlock(core, "coreaudio_" #name);             \
    284        return ret;                                             \
    285    }
    286COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
    287                       (hw, size))
    288COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
    289                       (HWVoiceOut *hw, void *buf, size_t size),
    290                       (hw, buf, size))
    291COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
    292                       (hw, buf, size))
    293#undef COREAUDIO_WRAPPER_FUNC
    294
    295/*
    296 * callback to feed audiooutput buffer. called without iothread lock.
    297 * allowed to lock "buf_mutex", but disallowed to have any other locks.
    298 */
    299static OSStatus audioDeviceIOProc(
    300    AudioDeviceID inDevice,
    301    const AudioTimeStamp *inNow,
    302    const AudioBufferList *inInputData,
    303    const AudioTimeStamp *inInputTime,
    304    AudioBufferList *outOutputData,
    305    const AudioTimeStamp *inOutputTime,
    306    void *hwptr)
    307{
    308    UInt32 frameCount, pending_frames;
    309    void *out = outOutputData->mBuffers[0].mData;
    310    HWVoiceOut *hw = hwptr;
    311    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
    312    size_t len;
    313
    314    if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
    315        inInputTime = 0;
    316        return 0;
    317    }
    318
    319    if (inDevice != core->outputDeviceID) {
    320        coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
    321        return 0;
    322    }
    323
    324    frameCount = core->audioDevicePropertyBufferFrameSize;
    325    pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
    326
    327    /* if there are not enough samples, set signal and return */
    328    if (pending_frames < frameCount) {
    329        inInputTime = 0;
    330        coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)");
    331        return 0;
    332    }
    333
    334    len = frameCount * hw->info.bytes_per_frame;
    335    while (len) {
    336        size_t write_len;
    337        ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
    338        if (start < 0) {
    339            start += hw->size_emul;
    340        }
    341        assert(start >= 0 && start < hw->size_emul);
    342
    343        write_len = MIN(MIN(hw->pending_emul, len),
    344                        hw->size_emul - start);
    345
    346        memcpy(out, hw->buf_emul + start, write_len);
    347        hw->pending_emul -= write_len;
    348        len -= write_len;
    349        out += write_len;
    350    }
    351
    352    coreaudio_buf_unlock (core, "audioDeviceIOProc");
    353    return 0;
    354}
    355
    356static OSStatus init_out_device(coreaudioVoiceOut *core)
    357{
    358    OSStatus status;
    359    AudioValueRange frameRange;
    360
    361    AudioStreamBasicDescription streamBasicDescription = {
    362        .mBitsPerChannel = core->hw.info.bits,
    363        .mBytesPerFrame = core->hw.info.bytes_per_frame,
    364        .mBytesPerPacket = core->hw.info.bytes_per_frame,
    365        .mChannelsPerFrame = core->hw.info.nchannels,
    366        .mFormatFlags = kLinearPCMFormatFlagIsFloat,
    367        .mFormatID = kAudioFormatLinearPCM,
    368        .mFramesPerPacket = 1,
    369        .mSampleRate = core->hw.info.freq
    370    };
    371
    372    status = coreaudio_get_voice(&core->outputDeviceID);
    373    if (status != kAudioHardwareNoError) {
    374        coreaudio_playback_logerr (status,
    375                                   "Could not get default output Device\n");
    376        return status;
    377    }
    378    if (core->outputDeviceID == kAudioDeviceUnknown) {
    379        dolog ("Could not initialize playback - Unknown Audiodevice\n");
    380        return status;
    381    }
    382
    383    /* get minimum and maximum buffer frame sizes */
    384    status = coreaudio_get_framesizerange(core->outputDeviceID,
    385                                          &frameRange);
    386    if (status == kAudioHardwareBadObjectError) {
    387        return 0;
    388    }
    389    if (status != kAudioHardwareNoError) {
    390        coreaudio_playback_logerr (status,
    391                                    "Could not get device buffer frame range\n");
    392        return status;
    393    }
    394
    395    if (frameRange.mMinimum > core->frameSizeSetting) {
    396        core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
    397        dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
    398    } else if (frameRange.mMaximum < core->frameSizeSetting) {
    399        core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
    400        dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
    401    } else {
    402        core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting;
    403    }
    404
    405    /* set Buffer Frame Size */
    406    status = coreaudio_set_framesize(core->outputDeviceID,
    407                                     &core->audioDevicePropertyBufferFrameSize);
    408    if (status == kAudioHardwareBadObjectError) {
    409        return 0;
    410    }
    411    if (status != kAudioHardwareNoError) {
    412        coreaudio_playback_logerr (status,
    413                                    "Could not set device buffer frame size %" PRIu32 "\n",
    414                                    (uint32_t)core->audioDevicePropertyBufferFrameSize);
    415        return status;
    416    }
    417
    418    /* get Buffer Frame Size */
    419    status = coreaudio_get_framesize(core->outputDeviceID,
    420                                     &core->audioDevicePropertyBufferFrameSize);
    421    if (status == kAudioHardwareBadObjectError) {
    422        return 0;
    423    }
    424    if (status != kAudioHardwareNoError) {
    425        coreaudio_playback_logerr (status,
    426                                    "Could not get device buffer frame size\n");
    427        return status;
    428    }
    429    core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
    430
    431    /* set Samplerate */
    432    status = coreaudio_set_streamformat(core->outputDeviceID,
    433                                        &streamBasicDescription);
    434    if (status == kAudioHardwareBadObjectError) {
    435        return 0;
    436    }
    437    if (status != kAudioHardwareNoError) {
    438        coreaudio_playback_logerr (status,
    439                                   "Could not set samplerate %lf\n",
    440                                   streamBasicDescription.mSampleRate);
    441        core->outputDeviceID = kAudioDeviceUnknown;
    442        return status;
    443    }
    444
    445    /*
    446     * set Callback.
    447     *
    448     * On macOS 11.3.1, Core Audio calls AudioDeviceIOProc after calling an
    449     * internal function named HALB_Mutex::Lock(), which locks a mutex in
    450     * HALB_IOThread::Entry(void*). HALB_Mutex::Lock() is also called in
    451     * AudioObjectGetPropertyData, which is called by coreaudio driver.
    452     * Therefore, the specified callback must be designed to avoid a deadlock
    453     * with the callers of AudioObjectGetPropertyData.
    454     */
    455    core->ioprocid = NULL;
    456    status = AudioDeviceCreateIOProcID(core->outputDeviceID,
    457                                       audioDeviceIOProc,
    458                                       &core->hw,
    459                                       &core->ioprocid);
    460    if (status == kAudioHardwareBadDeviceError) {
    461        return 0;
    462    }
    463    if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
    464        coreaudio_playback_logerr (status, "Could not set IOProc\n");
    465        core->outputDeviceID = kAudioDeviceUnknown;
    466        return status;
    467    }
    468
    469    return 0;
    470}
    471
    472static void fini_out_device(coreaudioVoiceOut *core)
    473{
    474    OSStatus status;
    475    UInt32 isrunning;
    476
    477    /* stop playback */
    478    status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
    479    if (status != kAudioHardwareBadObjectError) {
    480        if (status != kAudioHardwareNoError) {
    481            coreaudio_logerr(status,
    482                             "Could not determine whether Device is playing\n");
    483        }
    484
    485        if (isrunning) {
    486            status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
    487            if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
    488                coreaudio_logerr(status, "Could not stop playback\n");
    489            }
    490        }
    491    }
    492
    493    /* remove callback */
    494    status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
    495                                        core->ioprocid);
    496    if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
    497        coreaudio_logerr(status, "Could not remove IOProc\n");
    498    }
    499    core->outputDeviceID = kAudioDeviceUnknown;
    500}
    501
    502static void update_device_playback_state(coreaudioVoiceOut *core)
    503{
    504    OSStatus status;
    505    UInt32 isrunning;
    506
    507    status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
    508    if (status != kAudioHardwareNoError) {
    509        if (status != kAudioHardwareBadObjectError) {
    510            coreaudio_logerr(status,
    511                             "Could not determine whether Device is playing\n");
    512        }
    513
    514        return;
    515    }
    516
    517    if (core->enabled) {
    518        /* start playback */
    519        if (!isrunning) {
    520            status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
    521            if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
    522                coreaudio_logerr (status, "Could not resume playback\n");
    523            }
    524        }
    525    } else {
    526        /* stop playback */
    527        if (isrunning) {
    528            status = AudioDeviceStop(core->outputDeviceID,
    529                                     core->ioprocid);
    530            if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
    531                coreaudio_logerr(status, "Could not pause playback\n");
    532            }
    533        }
    534    }
    535}
    536
    537/* called without iothread lock. */
    538static OSStatus handle_voice_change(
    539    AudioObjectID in_object_id,
    540    UInt32 in_number_addresses,
    541    const AudioObjectPropertyAddress *in_addresses,
    542    void *in_client_data)
    543{
    544    OSStatus status;
    545    coreaudioVoiceOut *core = in_client_data;
    546
    547    qemu_mutex_lock_iothread();
    548
    549    if (core->outputDeviceID) {
    550        fini_out_device(core);
    551    }
    552
    553    status = init_out_device(core);
    554    if (!status) {
    555        update_device_playback_state(core);
    556    }
    557
    558    qemu_mutex_unlock_iothread();
    559    return status;
    560}
    561
    562static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
    563                              void *drv_opaque)
    564{
    565    OSStatus status;
    566    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
    567    int err;
    568    Audiodev *dev = drv_opaque;
    569    AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
    570    struct audsettings obt_as;
    571
    572    /* create mutex */
    573    err = pthread_mutex_init(&core->buf_mutex, NULL);
    574    if (err) {
    575        dolog("Could not create mutex\nReason: %s\n", strerror (err));
    576        return -1;
    577    }
    578
    579    obt_as = *as;
    580    as = &obt_as;
    581    as->fmt = AUDIO_FORMAT_F32;
    582    audio_pcm_init_info (&hw->info, as);
    583
    584    core->frameSizeSetting = audio_buffer_frames(
    585        qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
    586
    587    core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
    588
    589    status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
    590                                            &voice_addr, handle_voice_change,
    591                                            core);
    592    if (status != kAudioHardwareNoError) {
    593        coreaudio_playback_logerr (status,
    594                                   "Could not listen to voice property change\n");
    595        return -1;
    596    }
    597
    598    if (init_out_device(core)) {
    599        status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
    600                                                   &voice_addr,
    601                                                   handle_voice_change,
    602                                                   core);
    603        if (status != kAudioHardwareNoError) {
    604            coreaudio_playback_logerr(status,
    605                                      "Could not remove voice property change listener\n");
    606        }
    607    }
    608
    609    return 0;
    610}
    611
    612static void coreaudio_fini_out (HWVoiceOut *hw)
    613{
    614    OSStatus status;
    615    int err;
    616    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
    617
    618    status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
    619                                               &voice_addr,
    620                                               handle_voice_change,
    621                                               core);
    622    if (status != kAudioHardwareNoError) {
    623        coreaudio_logerr(status, "Could not remove voice property change listener\n");
    624    }
    625
    626    fini_out_device(core);
    627
    628    /* destroy mutex */
    629    err = pthread_mutex_destroy(&core->buf_mutex);
    630    if (err) {
    631        dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
    632    }
    633}
    634
    635static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
    636{
    637    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
    638
    639    core->enabled = enable;
    640    update_device_playback_state(core);
    641}
    642
    643static void *coreaudio_audio_init(Audiodev *dev)
    644{
    645    return dev;
    646}
    647
    648static void coreaudio_audio_fini (void *opaque)
    649{
    650}
    651
    652static struct audio_pcm_ops coreaudio_pcm_ops = {
    653    .init_out = coreaudio_init_out,
    654    .fini_out = coreaudio_fini_out,
    655  /* wrapper for audio_generic_write */
    656    .write    = coreaudio_write,
    657  /* wrapper for audio_generic_get_buffer_out */
    658    .get_buffer_out = coreaudio_get_buffer_out,
    659  /* wrapper for audio_generic_put_buffer_out */
    660    .put_buffer_out = coreaudio_put_buffer_out,
    661    .enable_out = coreaudio_enable_out
    662};
    663
    664static struct audio_driver coreaudio_audio_driver = {
    665    .name           = "coreaudio",
    666    .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
    667    .init           = coreaudio_audio_init,
    668    .fini           = coreaudio_audio_fini,
    669    .pcm_ops        = &coreaudio_pcm_ops,
    670    .can_be_default = 1,
    671    .max_voices_out = 1,
    672    .max_voices_in  = 0,
    673    .voice_size_out = sizeof (coreaudioVoiceOut),
    674    .voice_size_in  = 0
    675};
    676
    677static void register_audio_coreaudio(void)
    678{
    679    audio_driver_register(&coreaudio_audio_driver);
    680}
    681type_init(register_audio_coreaudio);