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

dsoundaudio.c (19231B)


      1/*
      2 * QEMU DirectSound 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/*
     26 * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
     27 */
     28
     29#include "qemu/osdep.h"
     30#include "audio.h"
     31
     32#define AUDIO_CAP "dsound"
     33#include "audio_int.h"
     34#include "qemu/host-utils.h"
     35#include "qemu/module.h"
     36
     37#include <windows.h>
     38#include <mmsystem.h>
     39#include <objbase.h>
     40#include <dsound.h>
     41
     42#include "audio_win_int.h"
     43
     44/* #define DEBUG_DSOUND */
     45
     46typedef struct {
     47    LPDIRECTSOUND dsound;
     48    LPDIRECTSOUNDCAPTURE dsound_capture;
     49    struct audsettings settings;
     50    Audiodev *dev;
     51} dsound;
     52
     53typedef struct {
     54    HWVoiceOut hw;
     55    LPDIRECTSOUNDBUFFER dsound_buffer;
     56    bool first_time;
     57    dsound *s;
     58} DSoundVoiceOut;
     59
     60typedef struct {
     61    HWVoiceIn hw;
     62    LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
     63    bool first_time;
     64    dsound *s;
     65} DSoundVoiceIn;
     66
     67static void dsound_log_hresult (HRESULT hr)
     68{
     69    const char *str = "BUG";
     70
     71    switch (hr) {
     72    case DS_OK:
     73        str = "The method succeeded";
     74        break;
     75#ifdef DS_NO_VIRTUALIZATION
     76    case DS_NO_VIRTUALIZATION:
     77        str = "The buffer was created, but another 3D algorithm was substituted";
     78        break;
     79#endif
     80#ifdef DS_INCOMPLETE
     81    case DS_INCOMPLETE:
     82        str = "The method succeeded, but not all the optional effects were obtained";
     83        break;
     84#endif
     85#ifdef DSERR_ACCESSDENIED
     86    case DSERR_ACCESSDENIED:
     87        str = "The request failed because access was denied";
     88        break;
     89#endif
     90#ifdef DSERR_ALLOCATED
     91    case DSERR_ALLOCATED:
     92        str = "The request failed because resources, "
     93              "such as a priority level, were already in use "
     94              "by another caller";
     95        break;
     96#endif
     97#ifdef DSERR_ALREADYINITIALIZED
     98    case DSERR_ALREADYINITIALIZED:
     99        str = "The object is already initialized";
    100        break;
    101#endif
    102#ifdef DSERR_BADFORMAT
    103    case DSERR_BADFORMAT:
    104        str = "The specified wave format is not supported";
    105        break;
    106#endif
    107#ifdef DSERR_BADSENDBUFFERGUID
    108    case DSERR_BADSENDBUFFERGUID:
    109        str = "The GUID specified in an audiopath file "
    110              "does not match a valid mix-in buffer";
    111        break;
    112#endif
    113#ifdef DSERR_BUFFERLOST
    114    case DSERR_BUFFERLOST:
    115        str = "The buffer memory has been lost and must be restored";
    116        break;
    117#endif
    118#ifdef DSERR_BUFFERTOOSMALL
    119    case DSERR_BUFFERTOOSMALL:
    120        str = "The buffer size is not great enough to "
    121              "enable effects processing";
    122        break;
    123#endif
    124#ifdef DSERR_CONTROLUNAVAIL
    125    case DSERR_CONTROLUNAVAIL:
    126        str = "The buffer control (volume, pan, and so on) "
    127              "requested by the caller is not available. "
    128              "Controls must be specified when the buffer is created, "
    129              "using the dwFlags member of DSBUFFERDESC";
    130        break;
    131#endif
    132#ifdef DSERR_DS8_REQUIRED
    133    case DSERR_DS8_REQUIRED:
    134        str = "A DirectSound object of class CLSID_DirectSound8 or later "
    135              "is required for the requested functionality. "
    136              "For more information, see IDirectSound8 Interface";
    137        break;
    138#endif
    139#ifdef DSERR_FXUNAVAILABLE
    140    case DSERR_FXUNAVAILABLE:
    141        str = "The effects requested could not be found on the system, "
    142              "or they are in the wrong order or in the wrong location; "
    143              "for example, an effect expected in hardware "
    144              "was found in software";
    145        break;
    146#endif
    147#ifdef DSERR_GENERIC
    148    case DSERR_GENERIC:
    149        str = "An undetermined error occurred inside the DirectSound subsystem";
    150        break;
    151#endif
    152#ifdef DSERR_INVALIDCALL
    153    case DSERR_INVALIDCALL:
    154        str = "This function is not valid for the current state of this object";
    155        break;
    156#endif
    157#ifdef DSERR_INVALIDPARAM
    158    case DSERR_INVALIDPARAM:
    159        str = "An invalid parameter was passed to the returning function";
    160        break;
    161#endif
    162#ifdef DSERR_NOAGGREGATION
    163    case DSERR_NOAGGREGATION:
    164        str = "The object does not support aggregation";
    165        break;
    166#endif
    167#ifdef DSERR_NODRIVER
    168    case DSERR_NODRIVER:
    169        str = "No sound driver is available for use, "
    170              "or the given GUID is not a valid DirectSound device ID";
    171        break;
    172#endif
    173#ifdef DSERR_NOINTERFACE
    174    case DSERR_NOINTERFACE:
    175        str = "The requested COM interface is not available";
    176        break;
    177#endif
    178#ifdef DSERR_OBJECTNOTFOUND
    179    case DSERR_OBJECTNOTFOUND:
    180        str = "The requested object was not found";
    181        break;
    182#endif
    183#ifdef DSERR_OTHERAPPHASPRIO
    184    case DSERR_OTHERAPPHASPRIO:
    185        str = "Another application has a higher priority level, "
    186              "preventing this call from succeeding";
    187        break;
    188#endif
    189#ifdef DSERR_OUTOFMEMORY
    190    case DSERR_OUTOFMEMORY:
    191        str = "The DirectSound subsystem could not allocate "
    192               "sufficient memory to complete the caller's request";
    193        break;
    194#endif
    195#ifdef DSERR_PRIOLEVELNEEDED
    196    case DSERR_PRIOLEVELNEEDED:
    197        str = "A cooperative level of DSSCL_PRIORITY or higher is required";
    198        break;
    199#endif
    200#ifdef DSERR_SENDLOOP
    201    case DSERR_SENDLOOP:
    202        str = "A circular loop of send effects was detected";
    203        break;
    204#endif
    205#ifdef DSERR_UNINITIALIZED
    206    case DSERR_UNINITIALIZED:
    207        str = "The Initialize method has not been called "
    208              "or has not been called successfully "
    209              "before other methods were called";
    210        break;
    211#endif
    212#ifdef DSERR_UNSUPPORTED
    213    case DSERR_UNSUPPORTED:
    214        str = "The function called is not supported at this time";
    215        break;
    216#endif
    217    default:
    218        AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT 0x%lx)\n", hr);
    219        return;
    220    }
    221
    222    AUD_log (AUDIO_CAP, "Reason: %s\n", str);
    223}
    224
    225static void GCC_FMT_ATTR (2, 3) dsound_logerr (
    226    HRESULT hr,
    227    const char *fmt,
    228    ...
    229    )
    230{
    231    va_list ap;
    232
    233    va_start (ap, fmt);
    234    AUD_vlog (AUDIO_CAP, fmt, ap);
    235    va_end (ap);
    236
    237    dsound_log_hresult (hr);
    238}
    239
    240static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
    241    HRESULT hr,
    242    const char *typ,
    243    const char *fmt,
    244    ...
    245    )
    246{
    247    va_list ap;
    248
    249    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
    250    va_start (ap, fmt);
    251    AUD_vlog (AUDIO_CAP, fmt, ap);
    252    va_end (ap);
    253
    254    dsound_log_hresult (hr);
    255}
    256
    257#ifdef DEBUG_DSOUND
    258static void print_wave_format (WAVEFORMATEX *wfx)
    259{
    260    dolog ("tag             = %d\n", wfx->wFormatTag);
    261    dolog ("nChannels       = %d\n", wfx->nChannels);
    262    dolog ("nSamplesPerSec  = %ld\n", wfx->nSamplesPerSec);
    263    dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec);
    264    dolog ("nBlockAlign     = %d\n", wfx->nBlockAlign);
    265    dolog ("wBitsPerSample  = %d\n", wfx->wBitsPerSample);
    266    dolog ("cbSize          = %d\n", wfx->cbSize);
    267}
    268#endif
    269
    270static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s)
    271{
    272    HRESULT hr;
    273
    274    hr = IDirectSoundBuffer_Restore (dsb);
    275
    276    if (hr != DS_OK) {
    277        dsound_logerr (hr, "Could not restore playback buffer\n");
    278        return -1;
    279    }
    280    return 0;
    281}
    282
    283#include "dsound_template.h"
    284#define DSBTYPE_IN
    285#include "dsound_template.h"
    286#undef DSBTYPE_IN
    287
    288static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
    289                                  dsound *s)
    290{
    291    HRESULT hr;
    292
    293    hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
    294    if (FAILED (hr)) {
    295        dsound_logerr (hr, "Could not get playback buffer status\n");
    296        return -1;
    297    }
    298
    299    if (*statusp & DSBSTATUS_BUFFERLOST) {
    300        dsound_restore_out(dsb, s);
    301        return -1;
    302    }
    303
    304    return 0;
    305}
    306
    307static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
    308                                 DWORD *statusp)
    309{
    310    HRESULT hr;
    311
    312    hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
    313    if (FAILED (hr)) {
    314        dsound_logerr (hr, "Could not get capture buffer status\n");
    315        return -1;
    316    }
    317
    318    return 0;
    319}
    320
    321static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
    322                                 dsound *s)
    323{
    324    int err;
    325    LPVOID p1, p2;
    326    DWORD blen1, blen2, len1, len2;
    327
    328    err = dsound_lock_out (
    329        dsb,
    330        &hw->info,
    331        0,
    332        hw->size_emul,
    333        &p1, &p2,
    334        &blen1, &blen2,
    335        1,
    336        s
    337        );
    338    if (err) {
    339        return;
    340    }
    341
    342    len1 = blen1 / hw->info.bytes_per_frame;
    343    len2 = blen2 / hw->info.bytes_per_frame;
    344
    345#ifdef DEBUG_DSOUND
    346    dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
    347           p1, blen1, len1,
    348           p2, blen2, len2);
    349#endif
    350
    351    if (p1 && len1) {
    352        audio_pcm_info_clear_buf (&hw->info, p1, len1);
    353    }
    354
    355    if (p2 && len2) {
    356        audio_pcm_info_clear_buf (&hw->info, p2, len2);
    357    }
    358
    359    dsound_unlock_out (dsb, p1, p2, blen1, blen2);
    360}
    361
    362static int dsound_set_cooperative_level(dsound *s)
    363{
    364    HRESULT hr;
    365    HWND hwnd;
    366
    367    hwnd = GetDesktopWindow();
    368    hr = IDirectSound_SetCooperativeLevel (
    369        s->dsound,
    370        hwnd,
    371        DSSCL_PRIORITY
    372        );
    373
    374    if (FAILED (hr)) {
    375        dsound_logerr (hr, "Could not set cooperative level for window %p\n",
    376                       hwnd);
    377        return -1;
    378    }
    379
    380    return 0;
    381}
    382
    383static void dsound_enable_out(HWVoiceOut *hw, bool enable)
    384{
    385    HRESULT hr;
    386    DWORD status;
    387    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
    388    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
    389    dsound *s = ds->s;
    390
    391    if (!dsb) {
    392        dolog ("Attempt to control voice without a buffer\n");
    393        return;
    394    }
    395
    396    if (enable) {
    397        if (dsound_get_status_out (dsb, &status, s)) {
    398            return;
    399        }
    400
    401        if (status & DSBSTATUS_PLAYING) {
    402            dolog ("warning: Voice is already playing\n");
    403            return;
    404        }
    405
    406        dsound_clear_sample (hw, dsb, s);
    407
    408        hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
    409        if (FAILED (hr)) {
    410            dsound_logerr (hr, "Could not start playing buffer\n");
    411            return;
    412        }
    413    } else {
    414        if (dsound_get_status_out (dsb, &status, s)) {
    415            return;
    416        }
    417
    418        if (status & DSBSTATUS_PLAYING) {
    419            hr = IDirectSoundBuffer_Stop (dsb);
    420            if (FAILED (hr)) {
    421                dsound_logerr (hr, "Could not stop playing buffer\n");
    422                return;
    423            }
    424        } else {
    425            dolog ("warning: Voice is not playing\n");
    426        }
    427    }
    428}
    429
    430static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
    431{
    432    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
    433    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
    434    HRESULT hr;
    435    DWORD ppos, wpos, act_size;
    436    size_t req_size;
    437    int err;
    438    void *ret;
    439
    440    hr = IDirectSoundBuffer_GetCurrentPosition(
    441        dsb, &ppos, ds->first_time ? &wpos : NULL);
    442    if (FAILED(hr)) {
    443        dsound_logerr(hr, "Could not get playback buffer position\n");
    444        *size = 0;
    445        return NULL;
    446    }
    447
    448    if (ds->first_time) {
    449        hw->pos_emul = wpos;
    450        ds->first_time = false;
    451    }
    452
    453    req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
    454    req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
    455
    456    if (req_size == 0) {
    457        *size = 0;
    458        return NULL;
    459    }
    460
    461    err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
    462                          &act_size, NULL, false, ds->s);
    463    if (err) {
    464        dolog("Failed to lock buffer\n");
    465        *size = 0;
    466        return NULL;
    467    }
    468
    469    *size = act_size;
    470    return ret;
    471}
    472
    473static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len)
    474{
    475    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
    476    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
    477    int err = dsound_unlock_out(dsb, buf, NULL, len, 0);
    478
    479    if (err) {
    480        dolog("Failed to unlock buffer!!\n");
    481        return 0;
    482    }
    483    hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
    484
    485    return len;
    486}
    487
    488static void dsound_enable_in(HWVoiceIn *hw, bool enable)
    489{
    490    HRESULT hr;
    491    DWORD status;
    492    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
    493    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
    494
    495    if (!dscb) {
    496        dolog ("Attempt to control capture voice without a buffer\n");
    497        return;
    498    }
    499
    500    if (enable) {
    501        if (dsound_get_status_in (dscb, &status)) {
    502            return;
    503        }
    504
    505        if (status & DSCBSTATUS_CAPTURING) {
    506            dolog ("warning: Voice is already capturing\n");
    507            return;
    508        }
    509
    510        /* clear ?? */
    511
    512        hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
    513        if (FAILED (hr)) {
    514            dsound_logerr (hr, "Could not start capturing\n");
    515            return;
    516        }
    517    } else {
    518        if (dsound_get_status_in (dscb, &status)) {
    519            return;
    520        }
    521
    522        if (status & DSCBSTATUS_CAPTURING) {
    523            hr = IDirectSoundCaptureBuffer_Stop (dscb);
    524            if (FAILED (hr)) {
    525                dsound_logerr (hr, "Could not stop capturing\n");
    526                return;
    527            }
    528        } else {
    529            dolog ("warning: Voice is not capturing\n");
    530        }
    531    }
    532}
    533
    534static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
    535{
    536    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
    537    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
    538    HRESULT hr;
    539    DWORD cpos, rpos, act_size;
    540    size_t req_size;
    541    int err;
    542    void *ret;
    543
    544    hr = IDirectSoundCaptureBuffer_GetCurrentPosition(
    545        dscb, &cpos, ds->first_time ? &rpos : NULL);
    546    if (FAILED(hr)) {
    547        dsound_logerr(hr, "Could not get capture buffer position\n");
    548        *size = 0;
    549        return NULL;
    550    }
    551
    552    if (ds->first_time) {
    553        hw->pos_emul = rpos;
    554        ds->first_time = false;
    555    }
    556
    557    req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul);
    558    req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul));
    559
    560    if (req_size == 0) {
    561        *size = 0;
    562        return NULL;
    563    }
    564
    565    err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
    566                         &act_size, NULL, false, ds->s);
    567    if (err) {
    568        dolog("Failed to lock buffer\n");
    569        *size = 0;
    570        return NULL;
    571    }
    572
    573    *size = act_size;
    574    return ret;
    575}
    576
    577static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len)
    578{
    579    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
    580    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
    581    int err = dsound_unlock_in(dscb, buf, NULL, len, 0);
    582
    583    if (err) {
    584        dolog("Failed to unlock buffer!!\n");
    585        return;
    586    }
    587    hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
    588}
    589
    590static void dsound_audio_fini (void *opaque)
    591{
    592    HRESULT hr;
    593    dsound *s = opaque;
    594
    595    if (!s->dsound) {
    596        g_free(s);
    597        return;
    598    }
    599
    600    hr = IDirectSound_Release (s->dsound);
    601    if (FAILED (hr)) {
    602        dsound_logerr (hr, "Could not release DirectSound\n");
    603    }
    604    s->dsound = NULL;
    605
    606    if (!s->dsound_capture) {
    607        g_free(s);
    608        return;
    609    }
    610
    611    hr = IDirectSoundCapture_Release (s->dsound_capture);
    612    if (FAILED (hr)) {
    613        dsound_logerr (hr, "Could not release DirectSoundCapture\n");
    614    }
    615    s->dsound_capture = NULL;
    616
    617    g_free(s);
    618}
    619
    620static void *dsound_audio_init(Audiodev *dev)
    621{
    622    int err;
    623    HRESULT hr;
    624    dsound *s = g_malloc0(sizeof(dsound));
    625    AudiodevDsoundOptions *dso;
    626
    627    assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
    628    s->dev = dev;
    629    dso = &dev->u.dsound;
    630
    631    if (!dso->has_latency) {
    632        dso->has_latency = true;
    633        dso->latency = 10000; /* 10 ms */
    634    }
    635
    636    hr = CoInitialize (NULL);
    637    if (FAILED (hr)) {
    638        dsound_logerr (hr, "Could not initialize COM\n");
    639        g_free(s);
    640        return NULL;
    641    }
    642
    643    hr = CoCreateInstance (
    644        &CLSID_DirectSound,
    645        NULL,
    646        CLSCTX_ALL,
    647        &IID_IDirectSound,
    648        (void **) &s->dsound
    649        );
    650    if (FAILED (hr)) {
    651        dsound_logerr (hr, "Could not create DirectSound instance\n");
    652        g_free(s);
    653        return NULL;
    654    }
    655
    656    hr = IDirectSound_Initialize (s->dsound, NULL);
    657    if (FAILED (hr)) {
    658        dsound_logerr (hr, "Could not initialize DirectSound\n");
    659
    660        hr = IDirectSound_Release (s->dsound);
    661        if (FAILED (hr)) {
    662            dsound_logerr (hr, "Could not release DirectSound\n");
    663        }
    664        g_free(s);
    665        return NULL;
    666    }
    667
    668    hr = CoCreateInstance (
    669        &CLSID_DirectSoundCapture,
    670        NULL,
    671        CLSCTX_ALL,
    672        &IID_IDirectSoundCapture,
    673        (void **) &s->dsound_capture
    674        );
    675    if (FAILED (hr)) {
    676        dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
    677    } else {
    678        hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
    679        if (FAILED (hr)) {
    680            dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
    681
    682            hr = IDirectSoundCapture_Release (s->dsound_capture);
    683            if (FAILED (hr)) {
    684                dsound_logerr (hr, "Could not release DirectSoundCapture\n");
    685            }
    686            s->dsound_capture = NULL;
    687        }
    688    }
    689
    690    err = dsound_set_cooperative_level(s);
    691    if (err) {
    692        dsound_audio_fini (s);
    693        return NULL;
    694    }
    695
    696    return s;
    697}
    698
    699static struct audio_pcm_ops dsound_pcm_ops = {
    700    .init_out = dsound_init_out,
    701    .fini_out = dsound_fini_out,
    702    .write    = audio_generic_write,
    703    .get_buffer_out = dsound_get_buffer_out,
    704    .put_buffer_out = dsound_put_buffer_out,
    705    .enable_out = dsound_enable_out,
    706
    707    .init_in  = dsound_init_in,
    708    .fini_in  = dsound_fini_in,
    709    .read     = audio_generic_read,
    710    .get_buffer_in = dsound_get_buffer_in,
    711    .put_buffer_in = dsound_put_buffer_in,
    712    .enable_in = dsound_enable_in,
    713};
    714
    715static struct audio_driver dsound_audio_driver = {
    716    .name           = "dsound",
    717    .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound",
    718    .init           = dsound_audio_init,
    719    .fini           = dsound_audio_fini,
    720    .pcm_ops        = &dsound_pcm_ops,
    721    .can_be_default = 1,
    722    .max_voices_out = INT_MAX,
    723    .max_voices_in  = 1,
    724    .voice_size_out = sizeof (DSoundVoiceOut),
    725    .voice_size_in  = sizeof (DSoundVoiceIn)
    726};
    727
    728static void register_audio_dsound(void)
    729{
    730    audio_driver_register(&dsound_audio_driver);
    731}
    732type_init(register_audio_dsound);