ossaudio.c 19.4 KB
Newer Older
B
bellard 已提交
1
/*
2 3 4 5
 * QEMU OSS audio driver
 *
 * Copyright (c) 2003-2005 Vassili Karpov (malc)
 *
B
bellard 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
B
bellard 已提交
28 29
#include "vl.h"

30 31
#define AUDIO_CAP "oss"
#include "audio_int.h"
B
bellard 已提交
32

33 34
typedef struct OSSVoiceOut {
    HWVoiceOut hw;
B
bellard 已提交
35 36 37 38 39 40
    void *pcm_buf;
    int fd;
    int nfrags;
    int fragsize;
    int mmapped;
    int old_optr;
41
} OSSVoiceOut;
B
bellard 已提交
42

43 44 45 46 47 48 49 50
typedef struct OSSVoiceIn {
    HWVoiceIn hw;
    void *pcm_buf;
    int fd;
    int nfrags;
    int fragsize;
    int old_optr;
} OSSVoiceIn;
B
bellard 已提交
51 52 53 54 55

static struct {
    int try_mmap;
    int nfrags;
    int fragsize;
56 57
    const char *devpath_out;
    const char *devpath_in;
B
bellard 已提交
58 59 60 61
} conf = {
    .try_mmap = 0,
    .nfrags = 4,
    .fragsize = 4096,
62 63
    .devpath_out = "/dev/dsp",
    .devpath_in = "/dev/dsp"
B
bellard 已提交
64 65 66 67 68 69 70 71 72 73
};

struct oss_params {
    int freq;
    audfmt_e fmt;
    int nchannels;
    int nfrags;
    int fragsize;
};

74
static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
B
bellard 已提交
75
{
76 77 78 79 80 81 82
    va_list ap;

    AUD_vlog (AUDIO_CAP, fmt, ap);

    va_start (ap, fmt);
    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
    va_end (ap);
B
bellard 已提交
83 84
}

85 86 87 88 89 90 91 92 93
static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
    int err,
    const char *typ,
    const char *fmt,
    ...
    )
{
    va_list ap;

B
bellard 已提交
94
    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117

    va_start (ap, fmt);
    AUD_vlog (AUDIO_CAP, fmt, ap);
    va_end (ap);

    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
}

static void oss_anal_close (int *fdp)
{
    int err = close (*fdp);
    if (err) {
        oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
    }
    *fdp = -1;
}

static int oss_write (SWVoiceOut *sw, void *buf, int len)
{
    return audio_pcm_sw_write (sw, buf, len);
}

static int aud_to_ossfmt (audfmt_e fmt)
B
bellard 已提交
118 119
{
    switch (fmt) {
120 121 122 123 124 125 126 127 128 129 130 131
    case AUD_FMT_S8:
        return AFMT_S8;

    case AUD_FMT_U8:
        return AFMT_U8;

    case AUD_FMT_S16:
        return AFMT_S16_LE;

    case AUD_FMT_U16:
        return AFMT_U16_LE;

B
bellard 已提交
132
    default:
133 134 135 136 137
        dolog ("Internal logic error: Bad audio format %d\n", fmt);
#ifdef DEBUG_AUDIO
        abort ();
#endif
        return AFMT_U8;
B
bellard 已提交
138 139 140
    }
}

141
static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
B
bellard 已提交
142
{
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
    switch (ossfmt) {
    case AFMT_S8:
        *endianness =0;
        *fmt = AUD_FMT_S8;
        break;

    case AFMT_U8:
        *endianness = 0;
        *fmt = AUD_FMT_U8;
        break;

    case AFMT_S16_LE:
        *endianness = 0;
        *fmt = AUD_FMT_S16;
        break;

    case AFMT_U16_LE:
        *endianness = 0;
        *fmt = AUD_FMT_U16;
        break;

    case AFMT_S16_BE:
        *endianness = 1;
        *fmt = AUD_FMT_S16;
        break;

    case AFMT_U16_BE:
        *endianness = 1;
        *fmt = AUD_FMT_U16;
        break;

B
bellard 已提交
174
    default:
175 176
        dolog ("Unrecognized audio format %d\n", ossfmt);
        return -1;
B
bellard 已提交
177
    }
178 179

    return 0;
B
bellard 已提交
180 181
}

B
bellard 已提交
182
#if defined DEBUG_MISMATCHES || defined DEBUG
183
static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
B
bellard 已提交
184 185 186
{
    dolog ("parameter | requested value | obtained value\n");
    dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);
187 188
    dolog ("channels  |      %10d |     %10d\n",
           req->nchannels, obt->nchannels);
B
bellard 已提交
189 190
    dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
    dolog ("nfrags    |      %10d |     %10d\n", req->nfrags, obt->nfrags);
191 192
    dolog ("fragsize  |      %10d |     %10d\n",
           req->fragsize, obt->fragsize);
B
bellard 已提交
193 194 195
}
#endif

196 197
static int oss_open (int in, struct oss_params *req,
                     struct oss_params *obt, int *pfd)
B
bellard 已提交
198 199 200 201 202
{
    int fd;
    int mmmmssss;
    audio_buf_info abinfo;
    int fmt, freq, nchannels;
203 204
    const char *dspname = in ? conf.devpath_in : conf.devpath_out;
    const char *typ = in ? "ADC" : "DAC";
B
bellard 已提交
205

206
    fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
B
bellard 已提交
207
    if (-1 == fd) {
208
        oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
B
bellard 已提交
209 210 211 212 213 214 215 216
        return -1;
    }

    freq = req->freq;
    nchannels = req->nchannels;
    fmt = req->fmt;

    if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
217
        oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
B
bellard 已提交
218 219 220 221
        goto err;
    }

    if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
222 223
        oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
                     req->nchannels);
B
bellard 已提交
224 225 226 227
        goto err;
    }

    if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
228
        oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
B
bellard 已提交
229 230 231 232
        goto err;
    }

    if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) {
233
        oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n");
B
bellard 已提交
234 235 236 237 238
        goto err;
    }

    mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
    if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
239 240
        oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
                     req->nfrags, req->fragsize);
B
bellard 已提交
241 242 243
        goto err;
    }

244 245
    if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
        oss_logerr2 (errno, typ, "Failed to get buffer length\n");
B
bellard 已提交
246 247 248 249 250 251 252 253 254 255
        goto err;
    }

    obt->fmt = fmt;
    obt->nchannels = nchannels;
    obt->freq = freq;
    obt->nfrags = abinfo.fragstotal;
    obt->fragsize = abinfo.fragsize;
    *pfd = fd;

B
bellard 已提交
256
#ifdef DEBUG_MISMATCHES
B
bellard 已提交
257 258 259 260 261 262
    if ((req->fmt != obt->fmt) ||
        (req->nchannels != obt->nchannels) ||
        (req->freq != obt->freq) ||
        (req->fragsize != obt->fragsize) ||
        (req->nfrags != obt->nfrags)) {
        dolog ("Audio parameters mismatch\n");
263
        oss_dump_info (req, obt);
B
bellard 已提交
264
    }
B
bellard 已提交
265
#endif
B
bellard 已提交
266

267 268
#ifdef DEBUG
    oss_dump_info (req, obt);
B
bellard 已提交
269 270 271
#endif
    return 0;

272 273
 err:
    oss_anal_close (&fd);
B
bellard 已提交
274 275 276
    return -1;
}

277
static int oss_run_out (HWVoiceOut *hw)
B
bellard 已提交
278
{
279
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
B
bellard 已提交
280 281 282 283 284 285
    int err, rpos, live, decr;
    int samples;
    uint8_t *dst;
    st_sample_t *src;
    struct audio_buf_info abinfo;
    struct count_info cntinfo;
B
bellard 已提交
286
    int bufsize;
B
bellard 已提交
287

288 289 290 291
    live = audio_pcm_hw_get_live_out (hw);
    if (!live) {
        return 0;
    }
B
bellard 已提交
292

B
bellard 已提交
293 294
    bufsize = hw->samples << hw->info.shift;

B
bellard 已提交
295 296 297 298 299
    if (oss->mmapped) {
        int bytes;

        err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
        if (err < 0) {
300 301
            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
            return 0;
B
bellard 已提交
302 303 304
        }

        if (cntinfo.ptr == oss->old_optr) {
305
            if (abs (hw->samples - live) < 64) {
B
bellard 已提交
306
                dolog ("warning: Overrun\n");
307 308
            }
            return 0;
B
bellard 已提交
309 310 311 312 313 314
        }

        if (cntinfo.ptr > oss->old_optr) {
            bytes = cntinfo.ptr - oss->old_optr;
        }
        else {
B
bellard 已提交
315
            bytes = bufsize + cntinfo.ptr - oss->old_optr;
B
bellard 已提交
316 317
        }

318
        decr = audio_MIN (bytes >> hw->info.shift, live);
B
bellard 已提交
319 320 321 322
    }
    else {
        err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
        if (err < 0) {
323 324 325 326
            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
            return 0;
        }

B
bellard 已提交
327 328 329
        if (abinfo.bytes < 0 || abinfo.bytes > bufsize) {
            ldebug ("warning: Invalid available size, size=%d bufsize=%d\n",
                    abinfo.bytes, bufsize);
330
            return 0;
B
bellard 已提交
331 332
        }

333 334 335 336
        decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
        if (!decr) {
            return 0;
        }
B
bellard 已提交
337 338 339 340 341 342 343 344
    }

    samples = decr;
    rpos = hw->rpos;
    while (samples) {
        int left_till_end_samples = hw->samples - rpos;
        int convert_samples = audio_MIN (samples, left_till_end_samples);

345 346
        src = hw->mix_buf + rpos;
        dst = advance (oss->pcm_buf, rpos << hw->info.shift);
B
bellard 已提交
347 348 349 350 351

        hw->clip (dst, src, convert_samples);
        if (!oss->mmapped) {
            int written;

352
            written = write (oss->fd, dst, convert_samples << hw->info.shift);
B
bellard 已提交
353 354
            /* XXX: follow errno recommendations ? */
            if (written == -1) {
355 356 357 358 359 360
                oss_logerr (
                    errno,
                    "Failed to write %d bytes of audio data from %p\n",
                    convert_samples << hw->info.shift,
                    dst
                    );
B
bellard 已提交
361 362 363
                continue;
            }

364 365 366
            if (written != convert_samples << hw->info.shift) {
                int wsamples = written >> hw->info.shift;
                int wbytes = wsamples << hw->info.shift;
B
bellard 已提交
367
                if (wbytes != written) {
B
bellard 已提交
368
                    dolog ("warning: Misaligned write %d (requested %d), "
369 370
                           "alignment %d\n",
                           wbytes, written, hw->info.align + 1);
B
bellard 已提交
371
                }
372 373
                mixeng_clear (src, wsamples);
                decr -= wsamples;
B
bellard 已提交
374 375 376 377
                rpos = (rpos + wsamples) % hw->samples;
                break;
            }
        }
378 379

        mixeng_clear (src, convert_samples);
B
bellard 已提交
380 381 382 383 384 385 386 387 388

        rpos = (rpos + convert_samples) % hw->samples;
        samples -= convert_samples;
    }
    if (oss->mmapped) {
        oss->old_optr = cntinfo.ptr;
    }

    hw->rpos = rpos;
389
    return decr;
B
bellard 已提交
390 391
}

392
static void oss_fini_out (HWVoiceOut *hw)
B
bellard 已提交
393 394
{
    int err;
395
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
B
bellard 已提交
396

397 398
    ldebug ("oss_fini\n");
    oss_anal_close (&oss->fd);
B
bellard 已提交
399 400 401

    if (oss->pcm_buf) {
        if (oss->mmapped) {
B
bellard 已提交
402
            err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
B
bellard 已提交
403
            if (err) {
404
                oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
B
bellard 已提交
405
                            oss->pcm_buf, hw->samples << hw->info.shift);
B
bellard 已提交
406 407 408 409 410 411 412 413 414
            }
        }
        else {
            qemu_free (oss->pcm_buf);
        }
        oss->pcm_buf = NULL;
    }
}

B
bellard 已提交
415
static int oss_init_out (HWVoiceOut *hw, audsettings_t *as)
B
bellard 已提交
416
{
417
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
B
bellard 已提交
418
    struct oss_params req, obt;
419 420 421 422
    int endianness;
    int err;
    int fd;
    audfmt_e effective_fmt;
B
bellard 已提交
423
    audsettings_t obt_as;
B
bellard 已提交
424

B
bellard 已提交
425 426 427
    req.fmt = aud_to_ossfmt (as->fmt);
    req.freq = as->freq;
    req.nchannels = as->nchannels;
B
bellard 已提交
428 429 430
    req.fragsize = conf.fragsize;
    req.nfrags = conf.nfrags;

431
    if (oss_open (0, &req, &obt, &fd)) {
B
bellard 已提交
432
        return -1;
433
    }
B
bellard 已提交
434

435 436 437 438 439
    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
    if (err) {
        oss_anal_close (&fd);
        return -1;
    }
B
bellard 已提交
440

B
bellard 已提交
441 442 443 444
    obt_as.freq = obt.freq;
    obt_as.nchannels = obt.nchannels;
    obt_as.fmt = effective_fmt;

445 446
    audio_pcm_init_info (
        &hw->info,
B
bellard 已提交
447
        &obt_as,
448 449
        audio_need_to_swap_endian (endianness)
        );
B
bellard 已提交
450 451
    oss->nfrags = obt.nfrags;
    oss->fragsize = obt.fragsize;
B
bellard 已提交
452 453 454 455 456 457 458

    if (obt.nfrags * obt.fragsize & hw->info.align) {
        dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
               obt.nfrags * obt.fragsize, hw->info.align + 1);
    }

    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
B
bellard 已提交
459 460 461

    oss->mmapped = 0;
    if (conf.try_mmap) {
B
bellard 已提交
462 463 464 465 466 467 468 469
        oss->pcm_buf = mmap (
            0,
            hw->samples << hw->info.shift,
            PROT_READ | PROT_WRITE,
            MAP_SHARED,
            fd,
            0
            );
B
bellard 已提交
470
        if (oss->pcm_buf == MAP_FAILED) {
471
            oss_logerr (errno, "Failed to map %d bytes of DAC\n",
B
bellard 已提交
472
                        hw->samples << hw->info.shift);
B
bellard 已提交
473
        } else {
B
bellard 已提交
474 475
            int err;
            int trig = 0;
476 477
            if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
                oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
B
bellard 已提交
478
            }
B
bellard 已提交
479 480
            else {
                trig = PCM_ENABLE_OUTPUT;
481 482 483 484 485
                if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
                    oss_logerr (
                        errno,
                        "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
                        );
B
bellard 已提交
486 487 488 489
                }
                else {
                    oss->mmapped = 1;
                }
B
bellard 已提交
490 491
            }

B
bellard 已提交
492
            if (!oss->mmapped) {
B
bellard 已提交
493
                err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
B
bellard 已提交
494
                if (err) {
495
                    oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
B
bellard 已提交
496
                                oss->pcm_buf, hw->samples << hw->info.shift);
B
bellard 已提交
497
                }
B
bellard 已提交
498 499 500 501 502
            }
        }
    }

    if (!oss->mmapped) {
B
bellard 已提交
503 504 505 506 507
        oss->pcm_buf = audio_calloc (
            AUDIO_FUNC,
            hw->samples,
            1 << hw->info.shift
            );
B
bellard 已提交
508
        if (!oss->pcm_buf) {
B
bellard 已提交
509 510 511 512 513
            dolog (
                "Could not allocate DAC buffer (%d samples, each %d bytes)\n",
                hw->samples,
                1 << hw->info.shift
                );
514
            oss_anal_close (&fd);
B
bellard 已提交
515 516 517 518
            return -1;
        }
    }

519
    oss->fd = fd;
B
bellard 已提交
520 521 522
    return 0;
}

523
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
B
bellard 已提交
524 525
{
    int trig;
526
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
B
bellard 已提交
527

528
    if (!oss->mmapped) {
B
bellard 已提交
529
        return 0;
530
    }
B
bellard 已提交
531 532 533 534

    switch (cmd) {
    case VOICE_ENABLE:
        ldebug ("enabling voice\n");
535
        audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
B
bellard 已提交
536 537
        trig = PCM_ENABLE_OUTPUT;
        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
538 539 540 541
            oss_logerr (
                errno,
                "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
                );
B
bellard 已提交
542 543 544 545 546 547 548 549
            return -1;
        }
        break;

    case VOICE_DISABLE:
        ldebug ("disabling voice\n");
        trig = 0;
        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
550
            oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
B
bellard 已提交
551 552 553 554 555 556 557
            return -1;
        }
        break;
    }
    return 0;
}

B
bellard 已提交
558
static int oss_init_in (HWVoiceIn *hw, audsettings_t *as)
559 560 561 562 563 564 565
{
    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
    struct oss_params req, obt;
    int endianness;
    int err;
    int fd;
    audfmt_e effective_fmt;
B
bellard 已提交
566
    audsettings_t obt_as;
567

B
bellard 已提交
568 569 570
    req.fmt = aud_to_ossfmt (as->fmt);
    req.freq = as->freq;
    req.nchannels = as->nchannels;
571 572 573 574 575 576 577 578 579 580 581 582
    req.fragsize = conf.fragsize;
    req.nfrags = conf.nfrags;
    if (oss_open (1, &req, &obt, &fd)) {
        return -1;
    }

    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
    if (err) {
        oss_anal_close (&fd);
        return -1;
    }

B
bellard 已提交
583 584 585 586
    obt_as.freq = obt.freq;
    obt_as.nchannels = obt.nchannels;
    obt_as.fmt = effective_fmt;

587 588
    audio_pcm_init_info (
        &hw->info,
B
bellard 已提交
589
        &obt_as,
590 591 592 593
        audio_need_to_swap_endian (endianness)
        );
    oss->nfrags = obt.nfrags;
    oss->fragsize = obt.fragsize;
B
bellard 已提交
594 595 596 597 598 599 600 601

    if (obt.nfrags * obt.fragsize & hw->info.align) {
        dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
               obt.nfrags * obt.fragsize, hw->info.align + 1);
    }

    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
    oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
602
    if (!oss->pcm_buf) {
B
bellard 已提交
603 604
        dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
               hw->samples, 1 << hw->info.shift);
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
        oss_anal_close (&fd);
        return -1;
    }

    oss->fd = fd;
    return 0;
}

static void oss_fini_in (HWVoiceIn *hw)
{
    OSSVoiceIn *oss = (OSSVoiceIn *) hw;

    oss_anal_close (&oss->fd);

    if (oss->pcm_buf) {
        qemu_free (oss->pcm_buf);
        oss->pcm_buf = NULL;
    }
}

static int oss_run_in (HWVoiceIn *hw)
{
    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
    int hwshift = hw->info.shift;
    int i;
    int live = audio_pcm_hw_get_live_in (hw);
    int dead = hw->samples - live;
    size_t read_samples = 0;
    struct {
        int add;
        int len;
    } bufs[2] = {
        { hw->wpos, 0 },
        { 0, 0 }
    };

    if (!dead) {
        return 0;
    }

    if (hw->wpos + dead > hw->samples) {
        bufs[0].len = (hw->samples - hw->wpos) << hwshift;
        bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
    }
    else {
        bufs[0].len = dead << hwshift;
    }


    for (i = 0; i < 2; ++i) {
        ssize_t nread;

        if (bufs[i].len) {
            void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
            nread = read (oss->fd, p, bufs[i].len);

            if (nread > 0) {
                if (nread & hw->info.align) {
B
bellard 已提交
663
                    dolog ("warning: Misaligned read %zd (requested %d), "
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
                           "alignment %d\n", nread, bufs[i].add << hwshift,
                           hw->info.align + 1);
                }
                read_samples += nread >> hwshift;
                hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
                          &nominal_volume);
            }

            if (bufs[i].len - nread) {
                if (nread == -1) {
                    switch (errno) {
                    case EINTR:
                    case EAGAIN:
                        break;
                    default:
                        oss_logerr (
                            errno,
                            "Failed to read %d bytes of audio (to %p)\n",
                            bufs[i].len, p
                            );
                        break;
                    }
                }
                break;
            }
        }
    }

    hw->wpos = (hw->wpos + read_samples) % hw->samples;
    return read_samples;
}

static int oss_read (SWVoiceIn *sw, void *buf, int size)
{
    return audio_pcm_sw_read (sw, buf, size);
}

static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
    (void) hw;
    (void) cmd;
    return 0;
}

B
bellard 已提交
708 709 710 711 712 713 714
static void *oss_audio_init (void)
{
    return &conf;
}

static void oss_audio_fini (void *opaque)
{
715
    (void) opaque;
B
bellard 已提交
716 717
}

718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
static struct audio_option oss_options[] = {
    {"FRAGSIZE", AUD_OPT_INT, &conf.fragsize,
     "Fragment size in bytes", NULL, 0},
    {"NFRAGS", AUD_OPT_INT, &conf.nfrags,
     "Number of fragments", NULL, 0},
    {"MMAP", AUD_OPT_BOOL, &conf.try_mmap,
     "Try using memory mapped access", NULL, 0},
    {"DAC_DEV", AUD_OPT_STR, &conf.devpath_out,
     "Path to DAC device", NULL, 0},
    {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
     "Path to ADC device", NULL, 0},
    {NULL, 0, NULL, NULL, NULL, 0}
};

static struct audio_pcm_ops oss_pcm_ops = {
    oss_init_out,
    oss_fini_out,
    oss_run_out,
    oss_write,
    oss_ctl_out,

    oss_init_in,
    oss_fini_in,
    oss_run_in,
    oss_read,
    oss_ctl_in
B
bellard 已提交
744 745
};

746 747 748 749 750 751 752 753 754 755 756 757
struct audio_driver oss_audio_driver = {
    INIT_FIELD (name           = ) "oss",
    INIT_FIELD (descr          = ) "OSS http://www.opensound.com",
    INIT_FIELD (options        = ) oss_options,
    INIT_FIELD (init           = ) oss_audio_init,
    INIT_FIELD (fini           = ) oss_audio_fini,
    INIT_FIELD (pcm_ops        = ) &oss_pcm_ops,
    INIT_FIELD (can_be_default = ) 1,
    INIT_FIELD (max_voices_out = ) INT_MAX,
    INIT_FIELD (max_voices_in  = ) INT_MAX,
    INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut),
    INIT_FIELD (voice_size_in  = ) sizeof (OSSVoiceIn)
B
bellard 已提交
758
};