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