qemu/audio/ossaudio.c
<<
>>
Prefs
   1/*
   2 * QEMU OSS audio driver
   3 *
   4 * Copyright (c) 2003-2005 Vassili Karpov (malc)
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24#include "qemu/osdep.h"
  25#include <sys/mman.h>
  26#include <sys/ioctl.h>
  27#include <sys/soundcard.h>
  28#include "qemu-common.h"
  29#include "qemu/main-loop.h"
  30#include "qemu/host-utils.h"
  31#include "audio.h"
  32#include "trace.h"
  33
  34#define AUDIO_CAP "oss"
  35#include "audio_int.h"
  36
  37#if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY
  38#define USE_DSP_POLICY
  39#endif
  40
  41typedef struct OSSConf {
  42    int try_mmap;
  43    int nfrags;
  44    int fragsize;
  45    const char *devpath_out;
  46    const char *devpath_in;
  47    int exclusive;
  48    int policy;
  49} OSSConf;
  50
  51typedef struct OSSVoiceOut {
  52    HWVoiceOut hw;
  53    void *pcm_buf;
  54    int fd;
  55    int wpos;
  56    int nfrags;
  57    int fragsize;
  58    int mmapped;
  59    int pending;
  60    OSSConf *conf;
  61} OSSVoiceOut;
  62
  63typedef struct OSSVoiceIn {
  64    HWVoiceIn hw;
  65    void *pcm_buf;
  66    int fd;
  67    int nfrags;
  68    int fragsize;
  69    OSSConf *conf;
  70} OSSVoiceIn;
  71
  72struct oss_params {
  73    int freq;
  74    audfmt_e fmt;
  75    int nchannels;
  76    int nfrags;
  77    int fragsize;
  78};
  79
  80static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
  81{
  82    va_list ap;
  83
  84    va_start (ap, fmt);
  85    AUD_vlog (AUDIO_CAP, fmt, ap);
  86    va_end (ap);
  87
  88    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
  89}
  90
  91static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
  92    int err,
  93    const char *typ,
  94    const char *fmt,
  95    ...
  96    )
  97{
  98    va_list ap;
  99
 100    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
 101
 102    va_start (ap, fmt);
 103    AUD_vlog (AUDIO_CAP, fmt, ap);
 104    va_end (ap);
 105
 106    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
 107}
 108
 109static void oss_anal_close (int *fdp)
 110{
 111    int err;
 112
 113    qemu_set_fd_handler (*fdp, NULL, NULL, NULL);
 114    err = close (*fdp);
 115    if (err) {
 116        oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
 117    }
 118    *fdp = -1;
 119}
 120
 121static void oss_helper_poll_out (void *opaque)
 122{
 123    (void) opaque;
 124    audio_run ("oss_poll_out");
 125}
 126
 127static void oss_helper_poll_in (void *opaque)
 128{
 129    (void) opaque;
 130    audio_run ("oss_poll_in");
 131}
 132
 133static void oss_poll_out (HWVoiceOut *hw)
 134{
 135    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
 136
 137    qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
 138}
 139
 140static void oss_poll_in (HWVoiceIn *hw)
 141{
 142    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
 143
 144    qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
 145}
 146
 147static int oss_write (SWVoiceOut *sw, void *buf, int len)
 148{
 149    return audio_pcm_sw_write (sw, buf, len);
 150}
 151
 152static int aud_to_ossfmt (audfmt_e fmt, int endianness)
 153{
 154    switch (fmt) {
 155    case AUD_FMT_S8:
 156        return AFMT_S8;
 157
 158    case AUD_FMT_U8:
 159        return AFMT_U8;
 160
 161    case AUD_FMT_S16:
 162        if (endianness) {
 163            return AFMT_S16_BE;
 164        }
 165        else {
 166            return AFMT_S16_LE;
 167        }
 168
 169    case AUD_FMT_U16:
 170        if (endianness) {
 171            return AFMT_U16_BE;
 172        }
 173        else {
 174            return AFMT_U16_LE;
 175        }
 176
 177    default:
 178        dolog ("Internal logic error: Bad audio format %d\n", fmt);
 179#ifdef DEBUG_AUDIO
 180        abort ();
 181#endif
 182        return AFMT_U8;
 183    }
 184}
 185
 186static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
 187{
 188    switch (ossfmt) {
 189    case AFMT_S8:
 190        *endianness = 0;
 191        *fmt = AUD_FMT_S8;
 192        break;
 193
 194    case AFMT_U8:
 195        *endianness = 0;
 196        *fmt = AUD_FMT_U8;
 197        break;
 198
 199    case AFMT_S16_LE:
 200        *endianness = 0;
 201        *fmt = AUD_FMT_S16;
 202        break;
 203
 204    case AFMT_U16_LE:
 205        *endianness = 0;
 206        *fmt = AUD_FMT_U16;
 207        break;
 208
 209    case AFMT_S16_BE:
 210        *endianness = 1;
 211        *fmt = AUD_FMT_S16;
 212        break;
 213
 214    case AFMT_U16_BE:
 215        *endianness = 1;
 216        *fmt = AUD_FMT_U16;
 217        break;
 218
 219    default:
 220        dolog ("Unrecognized audio format %d\n", ossfmt);
 221        return -1;
 222    }
 223
 224    return 0;
 225}
 226
 227#if defined DEBUG_MISMATCHES || defined DEBUG
 228static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
 229{
 230    dolog ("parameter | requested value | obtained value\n");
 231    dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);
 232    dolog ("channels  |      %10d |     %10d\n",
 233           req->nchannels, obt->nchannels);
 234    dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
 235    dolog ("nfrags    |      %10d |     %10d\n", req->nfrags, obt->nfrags);
 236    dolog ("fragsize  |      %10d |     %10d\n",
 237           req->fragsize, obt->fragsize);
 238}
 239#endif
 240
 241#ifdef USE_DSP_POLICY
 242static int oss_get_version (int fd, int *version, const char *typ)
 243{
 244    if (ioctl (fd, OSS_GETVERSION, &version)) {
 245#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 246        /*
 247         * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION
 248         * since 7.x, but currently only on the mixer device (or in
 249         * the Linuxolator), and in the native version that part of
 250         * the code is in fact never reached so the ioctl fails anyway.
 251         * Until this is fixed, just check the errno and if its what
 252         * FreeBSD's sound drivers return atm assume they are new enough.
 253         */
 254        if (errno == EINVAL) {
 255            *version = 0x040000;
 256            return 0;
 257        }
 258#endif
 259        oss_logerr2 (errno, typ, "Failed to get OSS version\n");
 260        return -1;
 261    }
 262    return 0;
 263}
 264#endif
 265
 266static int oss_open (int in, struct oss_params *req,
 267                     struct oss_params *obt, int *pfd, OSSConf* conf)
 268{
 269    int fd;
 270    int oflags = conf->exclusive ? O_EXCL : 0;
 271    audio_buf_info abinfo;
 272    int fmt, freq, nchannels;
 273    int setfragment = 1;
 274    const char *dspname = in ? conf->devpath_in : conf->devpath_out;
 275    const char *typ = in ? "ADC" : "DAC";
 276
 277    /* Kludge needed to have working mmap on Linux */
 278    oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
 279
 280    fd = open (dspname, oflags | O_NONBLOCK);
 281    if (-1 == fd) {
 282        oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
 283        return -1;
 284    }
 285
 286    freq = req->freq;
 287    nchannels = req->nchannels;
 288    fmt = req->fmt;
 289
 290    if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
 291        oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
 292        goto err;
 293    }
 294
 295    if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
 296        oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
 297                     req->nchannels);
 298        goto err;
 299    }
 300
 301    if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
 302        oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
 303        goto err;
 304    }
 305
 306    if (ioctl (fd, SNDCTL_DSP_NONBLOCK, NULL)) {
 307        oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n");
 308        goto err;
 309    }
 310
 311#ifdef USE_DSP_POLICY
 312    if (conf->policy >= 0) {
 313        int version;
 314
 315        if (!oss_get_version (fd, &version, typ)) {
 316            trace_oss_version(version);
 317
 318            if (version >= 0x040000) {
 319                int policy = conf->policy;
 320                if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
 321                    oss_logerr2 (errno, typ,
 322                                 "Failed to set timing policy to %d\n",
 323                                 conf->policy);
 324                    goto err;
 325                }
 326                setfragment = 0;
 327            }
 328        }
 329    }
 330#endif
 331
 332    if (setfragment) {
 333        int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize);
 334        if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
 335            oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
 336                         req->nfrags, req->fragsize);
 337            goto err;
 338        }
 339    }
 340
 341    if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
 342        oss_logerr2 (errno, typ, "Failed to get buffer length\n");
 343        goto err;
 344    }
 345
 346    if (!abinfo.fragstotal || !abinfo.fragsize) {
 347        AUD_log (AUDIO_CAP, "Returned bogus buffer information(%d, %d) for %s\n",
 348                 abinfo.fragstotal, abinfo.fragsize, typ);
 349        goto err;
 350    }
 351
 352    obt->fmt = fmt;
 353    obt->nchannels = nchannels;
 354    obt->freq = freq;
 355    obt->nfrags = abinfo.fragstotal;
 356    obt->fragsize = abinfo.fragsize;
 357    *pfd = fd;
 358
 359#ifdef DEBUG_MISMATCHES
 360    if ((req->fmt != obt->fmt) ||
 361        (req->nchannels != obt->nchannels) ||
 362        (req->freq != obt->freq) ||
 363        (req->fragsize != obt->fragsize) ||
 364        (req->nfrags != obt->nfrags)) {
 365        dolog ("Audio parameters mismatch\n");
 366        oss_dump_info (req, obt);
 367    }
 368#endif
 369
 370#ifdef DEBUG
 371    oss_dump_info (req, obt);
 372#endif
 373    return 0;
 374
 375 err:
 376    oss_anal_close (&fd);
 377    return -1;
 378}
 379
 380static void oss_write_pending (OSSVoiceOut *oss)
 381{
 382    HWVoiceOut *hw = &oss->hw;
 383
 384    if (oss->mmapped) {
 385        return;
 386    }
 387
 388    while (oss->pending) {
 389        int samples_written;
 390        ssize_t bytes_written;
 391        int samples_till_end = hw->samples - oss->wpos;
 392        int samples_to_write = audio_MIN (oss->pending, samples_till_end);
 393        int bytes_to_write = samples_to_write << hw->info.shift;
 394        void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
 395
 396        bytes_written = write (oss->fd, pcm, bytes_to_write);
 397        if (bytes_written < 0) {
 398            if (errno != EAGAIN) {
 399                oss_logerr (errno, "failed to write %d bytes\n",
 400                            bytes_to_write);
 401            }
 402            break;
 403        }
 404
 405        if (bytes_written & hw->info.align) {
 406            dolog ("misaligned write asked for %d, but got %zd\n",
 407                   bytes_to_write, bytes_written);
 408            return;
 409        }
 410
 411        samples_written = bytes_written >> hw->info.shift;
 412        oss->pending -= samples_written;
 413        oss->wpos = (oss->wpos + samples_written) % hw->samples;
 414        if (bytes_written - bytes_to_write) {
 415            break;
 416        }
 417    }
 418}
 419
 420static int oss_run_out (HWVoiceOut *hw, int live)
 421{
 422    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
 423    int err, decr;
 424    struct audio_buf_info abinfo;
 425    struct count_info cntinfo;
 426    int bufsize;
 427
 428    bufsize = hw->samples << hw->info.shift;
 429
 430    if (oss->mmapped) {
 431        int bytes, pos;
 432
 433        err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
 434        if (err < 0) {
 435            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
 436            return 0;
 437        }
 438
 439        pos = hw->rpos << hw->info.shift;
 440        bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
 441        decr = audio_MIN (bytes >> hw->info.shift, live);
 442    }
 443    else {
 444        err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
 445        if (err < 0) {
 446            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
 447            return 0;
 448        }
 449
 450        if (abinfo.bytes > bufsize) {
 451            trace_oss_invalid_available_size(abinfo.bytes, bufsize);
 452            abinfo.bytes = bufsize;
 453        }
 454
 455        if (abinfo.bytes < 0) {
 456            trace_oss_invalid_available_size(abinfo.bytes, bufsize);
 457            return 0;
 458        }
 459
 460        decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
 461        if (!decr) {
 462            return 0;
 463        }
 464    }
 465
 466    decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
 467    oss->pending += decr;
 468    oss_write_pending (oss);
 469
 470    return decr;
 471}
 472
 473static void oss_fini_out (HWVoiceOut *hw)
 474{
 475    int err;
 476    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
 477
 478    ldebug ("oss_fini\n");
 479    oss_anal_close (&oss->fd);
 480
 481    if (oss->pcm_buf) {
 482        if (oss->mmapped) {
 483            err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
 484            if (err) {
 485                oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
 486                            oss->pcm_buf, hw->samples << hw->info.shift);
 487            }
 488        }
 489        else {
 490            g_free (oss->pcm_buf);
 491        }
 492        oss->pcm_buf = NULL;
 493    }
 494}
 495
 496static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
 497                        void *drv_opaque)
 498{
 499    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
 500    struct oss_params req, obt;
 501    int endianness;
 502    int err;
 503    int fd;
 504    audfmt_e effective_fmt;
 505    struct audsettings obt_as;
 506    OSSConf *conf = drv_opaque;
 507
 508    oss->fd = -1;
 509
 510    req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
 511    req.freq = as->freq;
 512    req.nchannels = as->nchannels;
 513    req.fragsize = conf->fragsize;
 514    req.nfrags = conf->nfrags;
 515
 516    if (oss_open (0, &req, &obt, &fd, conf)) {
 517        return -1;
 518    }
 519
 520    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
 521    if (err) {
 522        oss_anal_close (&fd);
 523        return -1;
 524    }
 525
 526    obt_as.freq = obt.freq;
 527    obt_as.nchannels = obt.nchannels;
 528    obt_as.fmt = effective_fmt;
 529    obt_as.endianness = endianness;
 530
 531    audio_pcm_init_info (&hw->info, &obt_as);
 532    oss->nfrags = obt.nfrags;
 533    oss->fragsize = obt.fragsize;
 534
 535    if (obt.nfrags * obt.fragsize & hw->info.align) {
 536        dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
 537               obt.nfrags * obt.fragsize, hw->info.align + 1);
 538    }
 539
 540    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
 541
 542    oss->mmapped = 0;
 543    if (conf->try_mmap) {
 544        oss->pcm_buf = mmap (
 545            NULL,
 546            hw->samples << hw->info.shift,
 547            PROT_READ | PROT_WRITE,
 548            MAP_SHARED,
 549            fd,
 550            0
 551            );
 552        if (oss->pcm_buf == MAP_FAILED) {
 553            oss_logerr (errno, "Failed to map %d bytes of DAC\n",
 554                        hw->samples << hw->info.shift);
 555        }
 556        else {
 557            int err;
 558            int trig = 0;
 559            if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
 560                oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
 561            }
 562            else {
 563                trig = PCM_ENABLE_OUTPUT;
 564                if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
 565                    oss_logerr (
 566                        errno,
 567                        "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
 568                        );
 569                }
 570                else {
 571                    oss->mmapped = 1;
 572                }
 573            }
 574
 575            if (!oss->mmapped) {
 576                err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
 577                if (err) {
 578                    oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
 579                                oss->pcm_buf, hw->samples << hw->info.shift);
 580                }
 581            }
 582        }
 583    }
 584
 585    if (!oss->mmapped) {
 586        oss->pcm_buf = audio_calloc (
 587            AUDIO_FUNC,
 588            hw->samples,
 589            1 << hw->info.shift
 590            );
 591        if (!oss->pcm_buf) {
 592            dolog (
 593                "Could not allocate DAC buffer (%d samples, each %d bytes)\n",
 594                hw->samples,
 595                1 << hw->info.shift
 596                );
 597            oss_anal_close (&fd);
 598            return -1;
 599        }
 600    }
 601
 602    oss->fd = fd;
 603    oss->conf = conf;
 604    return 0;
 605}
 606
 607static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
 608{
 609    int trig;
 610    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
 611
 612    switch (cmd) {
 613    case VOICE_ENABLE:
 614        {
 615            va_list ap;
 616            int poll_mode;
 617
 618            va_start (ap, cmd);
 619            poll_mode = va_arg (ap, int);
 620            va_end (ap);
 621
 622            ldebug ("enabling voice\n");
 623            if (poll_mode) {
 624                oss_poll_out (hw);
 625                poll_mode = 0;
 626            }
 627            hw->poll_mode = poll_mode;
 628
 629            if (!oss->mmapped) {
 630                return 0;
 631            }
 632
 633            audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
 634            trig = PCM_ENABLE_OUTPUT;
 635            if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
 636                oss_logerr (
 637                    errno,
 638                    "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
 639                    );
 640                return -1;
 641            }
 642        }
 643        break;
 644
 645    case VOICE_DISABLE:
 646        if (hw->poll_mode) {
 647            qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
 648            hw->poll_mode = 0;
 649        }
 650
 651        if (!oss->mmapped) {
 652            return 0;
 653        }
 654
 655        ldebug ("disabling voice\n");
 656        trig = 0;
 657        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
 658            oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
 659            return -1;
 660        }
 661        break;
 662    }
 663    return 0;
 664}
 665
 666static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 667{
 668    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
 669    struct oss_params req, obt;
 670    int endianness;
 671    int err;
 672    int fd;
 673    audfmt_e effective_fmt;
 674    struct audsettings obt_as;
 675    OSSConf *conf = drv_opaque;
 676
 677    oss->fd = -1;
 678
 679    req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
 680    req.freq = as->freq;
 681    req.nchannels = as->nchannels;
 682    req.fragsize = conf->fragsize;
 683    req.nfrags = conf->nfrags;
 684    if (oss_open (1, &req, &obt, &fd, conf)) {
 685        return -1;
 686    }
 687
 688    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
 689    if (err) {
 690        oss_anal_close (&fd);
 691        return -1;
 692    }
 693
 694    obt_as.freq = obt.freq;
 695    obt_as.nchannels = obt.nchannels;
 696    obt_as.fmt = effective_fmt;
 697    obt_as.endianness = endianness;
 698
 699    audio_pcm_init_info (&hw->info, &obt_as);
 700    oss->nfrags = obt.nfrags;
 701    oss->fragsize = obt.fragsize;
 702
 703    if (obt.nfrags * obt.fragsize & hw->info.align) {
 704        dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
 705               obt.nfrags * obt.fragsize, hw->info.align + 1);
 706    }
 707
 708    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
 709    oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
 710    if (!oss->pcm_buf) {
 711        dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
 712               hw->samples, 1 << hw->info.shift);
 713        oss_anal_close (&fd);
 714        return -1;
 715    }
 716
 717    oss->fd = fd;
 718    oss->conf = conf;
 719    return 0;
 720}
 721
 722static void oss_fini_in (HWVoiceIn *hw)
 723{
 724    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
 725
 726    oss_anal_close (&oss->fd);
 727
 728    g_free(oss->pcm_buf);
 729    oss->pcm_buf = NULL;
 730}
 731
 732static int oss_run_in (HWVoiceIn *hw)
 733{
 734    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
 735    int hwshift = hw->info.shift;
 736    int i;
 737    int live = audio_pcm_hw_get_live_in (hw);
 738    int dead = hw->samples - live;
 739    size_t read_samples = 0;
 740    struct {
 741        int add;
 742        int len;
 743    } bufs[2] = {
 744        { .add = hw->wpos, .len = 0 },
 745        { .add = 0,        .len = 0 }
 746    };
 747
 748    if (!dead) {
 749        return 0;
 750    }
 751
 752    if (hw->wpos + dead > hw->samples) {
 753        bufs[0].len = (hw->samples - hw->wpos) << hwshift;
 754        bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
 755    }
 756    else {
 757        bufs[0].len = dead << hwshift;
 758    }
 759
 760    for (i = 0; i < 2; ++i) {
 761        ssize_t nread;
 762
 763        if (bufs[i].len) {
 764            void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
 765            nread = read (oss->fd, p, bufs[i].len);
 766
 767            if (nread > 0) {
 768                if (nread & hw->info.align) {
 769                    dolog ("warning: Misaligned read %zd (requested %d), "
 770                           "alignment %d\n", nread, bufs[i].add << hwshift,
 771                           hw->info.align + 1);
 772                }
 773                read_samples += nread >> hwshift;
 774                hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift);
 775            }
 776
 777            if (bufs[i].len - nread) {
 778                if (nread == -1) {
 779                    switch (errno) {
 780                    case EINTR:
 781                    case EAGAIN:
 782                        break;
 783                    default:
 784                        oss_logerr (
 785                            errno,
 786                            "Failed to read %d bytes of audio (to %p)\n",
 787                            bufs[i].len, p
 788                            );
 789                        break;
 790                    }
 791                }
 792                break;
 793            }
 794        }
 795    }
 796
 797    hw->wpos = (hw->wpos + read_samples) % hw->samples;
 798    return read_samples;
 799}
 800
 801static int oss_read (SWVoiceIn *sw, void *buf, int size)
 802{
 803    return audio_pcm_sw_read (sw, buf, size);
 804}
 805
 806static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
 807{
 808    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
 809
 810    switch (cmd) {
 811    case VOICE_ENABLE:
 812        {
 813            va_list ap;
 814            int poll_mode;
 815
 816            va_start (ap, cmd);
 817            poll_mode = va_arg (ap, int);
 818            va_end (ap);
 819
 820            if (poll_mode) {
 821                oss_poll_in (hw);
 822                poll_mode = 0;
 823            }
 824            hw->poll_mode = poll_mode;
 825        }
 826        break;
 827
 828    case VOICE_DISABLE:
 829        if (hw->poll_mode) {
 830            hw->poll_mode = 0;
 831            qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
 832        }
 833        break;
 834    }
 835    return 0;
 836}
 837
 838static OSSConf glob_conf = {
 839    .try_mmap = 0,
 840    .nfrags = 4,
 841    .fragsize = 4096,
 842    .devpath_out = "/dev/dsp",
 843    .devpath_in = "/dev/dsp",
 844    .exclusive = 0,
 845    .policy = 5
 846};
 847
 848static void *oss_audio_init (void)
 849{
 850    OSSConf *conf = g_malloc(sizeof(OSSConf));
 851    *conf = glob_conf;
 852
 853    if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
 854        access(conf->devpath_out, R_OK | W_OK) < 0) {
 855        g_free(conf);
 856        return NULL;
 857    }
 858    return conf;
 859}
 860
 861static void oss_audio_fini (void *opaque)
 862{
 863    g_free(opaque);
 864}
 865
 866static struct audio_option oss_options[] = {
 867    {
 868        .name  = "FRAGSIZE",
 869        .tag   = AUD_OPT_INT,
 870        .valp  = &glob_conf.fragsize,
 871        .descr = "Fragment size in bytes"
 872    },
 873    {
 874        .name  = "NFRAGS",
 875        .tag   = AUD_OPT_INT,
 876        .valp  = &glob_conf.nfrags,
 877        .descr = "Number of fragments"
 878    },
 879    {
 880        .name  = "MMAP",
 881        .tag   = AUD_OPT_BOOL,
 882        .valp  = &glob_conf.try_mmap,
 883        .descr = "Try using memory mapped access"
 884    },
 885    {
 886        .name  = "DAC_DEV",
 887        .tag   = AUD_OPT_STR,
 888        .valp  = &glob_conf.devpath_out,
 889        .descr = "Path to DAC device"
 890    },
 891    {
 892        .name  = "ADC_DEV",
 893        .tag   = AUD_OPT_STR,
 894        .valp  = &glob_conf.devpath_in,
 895        .descr = "Path to ADC device"
 896    },
 897    {
 898        .name  = "EXCLUSIVE",
 899        .tag   = AUD_OPT_BOOL,
 900        .valp  = &glob_conf.exclusive,
 901        .descr = "Open device in exclusive mode (vmix wont work)"
 902    },
 903#ifdef USE_DSP_POLICY
 904    {
 905        .name  = "POLICY",
 906        .tag   = AUD_OPT_INT,
 907        .valp  = &glob_conf.policy,
 908        .descr = "Set the timing policy of the device, -1 to use fragment mode",
 909    },
 910#endif
 911    { /* End of list */ }
 912};
 913
 914static struct audio_pcm_ops oss_pcm_ops = {
 915    .init_out = oss_init_out,
 916    .fini_out = oss_fini_out,
 917    .run_out  = oss_run_out,
 918    .write    = oss_write,
 919    .ctl_out  = oss_ctl_out,
 920
 921    .init_in  = oss_init_in,
 922    .fini_in  = oss_fini_in,
 923    .run_in   = oss_run_in,
 924    .read     = oss_read,
 925    .ctl_in   = oss_ctl_in
 926};
 927
 928struct audio_driver oss_audio_driver = {
 929    .name           = "oss",
 930    .descr          = "OSS http://www.opensound.com",
 931    .options        = oss_options,
 932    .init           = oss_audio_init,
 933    .fini           = oss_audio_fini,
 934    .pcm_ops        = &oss_pcm_ops,
 935    .can_be_default = 1,
 936    .max_voices_out = INT_MAX,
 937    .max_voices_in  = INT_MAX,
 938    .voice_size_out = sizeof (OSSVoiceOut),
 939    .voice_size_in  = sizeof (OSSVoiceIn)
 940};
 941