Statistics
| Branch: | Revision:

root / audio / ossaudio.c @ 902e2b51

History | View | Annotate | Download (20.1 kB)

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 <stdlib.h>
25
#include <sys/mman.h>
26
#include <sys/types.h>
27
#include <sys/ioctl.h>
28
#ifdef __OpenBSD__
29
#include <soundcard.h>
30
#else
31
#include <sys/soundcard.h>
32
#endif
33
#include "qemu-common.h"
34
#include "audio.h"
35

    
36
#define AUDIO_CAP "oss"
37
#include "audio_int.h"
38

    
39
typedef struct OSSVoiceOut {
40
    HWVoiceOut hw;
41
    void *pcm_buf;
42
    int fd;
43
    int nfrags;
44
    int fragsize;
45
    int mmapped;
46
    int old_optr;
47
} OSSVoiceOut;
48

    
49
typedef struct OSSVoiceIn {
50
    HWVoiceIn hw;
51
    void *pcm_buf;
52
    int fd;
53
    int nfrags;
54
    int fragsize;
55
    int old_optr;
56
} OSSVoiceIn;
57

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

    
74
struct oss_params {
75
    int freq;
76
    audfmt_e fmt;
77
    int nchannels;
78
    int nfrags;
79
    int fragsize;
80
};
81

    
82
static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
83
{
84
    va_list ap;
85

    
86
    va_start (ap, fmt);
87
    AUD_vlog (AUDIO_CAP, fmt, ap);
88
    va_end (ap);
89

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

    
93
static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
94
    int err,
95
    const char *typ,
96
    const char *fmt,
97
    ...
98
    )
99
{
100
    va_list ap;
101

    
102
    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
103

    
104
    va_start (ap, fmt);
105
    AUD_vlog (AUDIO_CAP, fmt, ap);
106
    va_end (ap);
107

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

    
111
static void oss_anal_close (int *fdp)
112
{
113
    int err = close (*fdp);
114
    if (err) {
115
        oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
116
    }
117
    *fdp = -1;
118
}
119

    
120
static int oss_write (SWVoiceOut *sw, void *buf, int len)
121
{
122
    return audio_pcm_sw_write (sw, buf, len);
123
}
124

    
125
static int aud_to_ossfmt (audfmt_e fmt)
126
{
127
    switch (fmt) {
128
    case AUD_FMT_S8:
129
        return AFMT_S8;
130

    
131
    case AUD_FMT_U8:
132
        return AFMT_U8;
133

    
134
    case AUD_FMT_S16:
135
        return AFMT_S16_LE;
136

    
137
    case AUD_FMT_U16:
138
        return AFMT_U16_LE;
139

    
140
    default:
141
        dolog ("Internal logic error: Bad audio format %d\n", fmt);
142
#ifdef DEBUG_AUDIO
143
        abort ();
144
#endif
145
        return AFMT_U8;
146
    }
147
}
148

    
149
static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
150
{
151
    switch (ossfmt) {
152
    case AFMT_S8:
153
        *endianness = 0;
154
        *fmt = AUD_FMT_S8;
155
        break;
156

    
157
    case AFMT_U8:
158
        *endianness = 0;
159
        *fmt = AUD_FMT_U8;
160
        break;
161

    
162
    case AFMT_S16_LE:
163
        *endianness = 0;
164
        *fmt = AUD_FMT_S16;
165
        break;
166

    
167
    case AFMT_U16_LE:
168
        *endianness = 0;
169
        *fmt = AUD_FMT_U16;
170
        break;
171

    
172
    case AFMT_S16_BE:
173
        *endianness = 1;
174
        *fmt = AUD_FMT_S16;
175
        break;
176

    
177
    case AFMT_U16_BE:
178
        *endianness = 1;
179
        *fmt = AUD_FMT_U16;
180
        break;
181

    
182
    default:
183
        dolog ("Unrecognized audio format %d\n", ossfmt);
184
        return -1;
185
    }
186

    
187
    return 0;
188
}
189

    
190
#if defined DEBUG_MISMATCHES || defined DEBUG
191
static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
192
{
193
    dolog ("parameter | requested value | obtained value\n");
194
    dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);
195
    dolog ("channels  |      %10d |     %10d\n",
196
           req->nchannels, obt->nchannels);
197
    dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
198
    dolog ("nfrags    |      %10d |     %10d\n", req->nfrags, obt->nfrags);
199
    dolog ("fragsize  |      %10d |     %10d\n",
200
           req->fragsize, obt->fragsize);
201
}
202
#endif
203

    
204
static int oss_open (int in, struct oss_params *req,
205
                     struct oss_params *obt, int *pfd)
206
{
207
    int fd;
208
    int mmmmssss;
209
    audio_buf_info abinfo;
210
    int fmt, freq, nchannels;
211
    const char *dspname = in ? conf.devpath_in : conf.devpath_out;
212
    const char *typ = in ? "ADC" : "DAC";
213

    
214
    fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
215
    if (-1 == fd) {
216
        oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
217
        return -1;
218
    }
219

    
220
    freq = req->freq;
221
    nchannels = req->nchannels;
222
    fmt = req->fmt;
223

    
224
    if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
225
        oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
226
        goto err;
227
    }
228

    
229
    if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
230
        oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
231
                     req->nchannels);
232
        goto err;
233
    }
234

    
235
    if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
236
        oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
237
        goto err;
238
    }
239

    
240
    if (ioctl (fd, SNDCTL_DSP_NONBLOCK, NULL)) {
241
        oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n");
242
        goto err;
243
    }
244

    
245
    mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
246
    if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
247
        oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
248
                     req->nfrags, req->fragsize);
249
        goto err;
250
    }
251

    
252
    if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
253
        oss_logerr2 (errno, typ, "Failed to get buffer length\n");
254
        goto err;
255
    }
256

    
257
    if (!abinfo.fragstotal || !abinfo.fragsize) {
258
        AUD_log (AUDIO_CAP, "Returned bogus buffer information(%d, %d) for %s\n",
259
                 abinfo.fragstotal, abinfo.fragsize, typ);
260
        goto err;
261
    }
262

    
263
    obt->fmt = fmt;
264
    obt->nchannels = nchannels;
265
    obt->freq = freq;
266
    obt->nfrags = abinfo.fragstotal;
267
    obt->fragsize = abinfo.fragsize;
268
    *pfd = fd;
269

    
270
#ifdef DEBUG_MISMATCHES
271
    if ((req->fmt != obt->fmt) ||
272
        (req->nchannels != obt->nchannels) ||
273
        (req->freq != obt->freq) ||
274
        (req->fragsize != obt->fragsize) ||
275
        (req->nfrags != obt->nfrags)) {
276
        dolog ("Audio parameters mismatch\n");
277
        oss_dump_info (req, obt);
278
    }
279
#endif
280

    
281
#ifdef DEBUG
282
    oss_dump_info (req, obt);
283
#endif
284
    return 0;
285

    
286
 err:
287
    oss_anal_close (&fd);
288
    return -1;
289
}
290

    
291
static int oss_run_out (HWVoiceOut *hw)
292
{
293
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
294
    int err, rpos, live, decr;
295
    int samples;
296
    uint8_t *dst;
297
    st_sample_t *src;
298
    struct audio_buf_info abinfo;
299
    struct count_info cntinfo;
300
    int bufsize;
301

    
302
    live = audio_pcm_hw_get_live_out (hw);
303
    if (!live) {
304
        return 0;
305
    }
306

    
307
    bufsize = hw->samples << hw->info.shift;
308

    
309
    if (oss->mmapped) {
310
        int bytes;
311

    
312
        err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
313
        if (err < 0) {
314
            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
315
            return 0;
316
        }
317

    
318
        if (cntinfo.ptr == oss->old_optr) {
319
            if (abs (hw->samples - live) < 64) {
320
                dolog ("warning: Overrun\n");
321
            }
322
            return 0;
323
        }
324

    
325
        if (cntinfo.ptr > oss->old_optr) {
326
            bytes = cntinfo.ptr - oss->old_optr;
327
        }
328
        else {
329
            bytes = bufsize + cntinfo.ptr - oss->old_optr;
330
        }
331

    
332
        decr = audio_MIN (bytes >> hw->info.shift, live);
333
    }
334
    else {
335
        err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
336
        if (err < 0) {
337
            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
338
            return 0;
339
        }
340

    
341
        if (abinfo.bytes > bufsize) {
342
            if (conf.debug) {
343
                dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
344
                       "please report your OS/audio hw to malc@pulsesoft.com\n",
345
                       abinfo.bytes, bufsize);
346
            }
347
            abinfo.bytes = bufsize;
348
        }
349

    
350
        if (abinfo.bytes < 0) {
351
            if (conf.debug) {
352
                dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
353
                       abinfo.bytes, bufsize);
354
            }
355
            return 0;
356
        }
357

    
358
        decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
359
        if (!decr) {
360
            return 0;
361
        }
362
    }
363

    
364
    samples = decr;
365
    rpos = hw->rpos;
366
    while (samples) {
367
        int left_till_end_samples = hw->samples - rpos;
368
        int convert_samples = audio_MIN (samples, left_till_end_samples);
369

    
370
        src = hw->mix_buf + rpos;
371
        dst = advance (oss->pcm_buf, rpos << hw->info.shift);
372

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

    
377
            written = write (oss->fd, dst, convert_samples << hw->info.shift);
378
            /* XXX: follow errno recommendations ? */
379
            if (written == -1) {
380
                oss_logerr (
381
                    errno,
382
                    "Failed to write %d bytes of audio data from %p\n",
383
                    convert_samples << hw->info.shift,
384
                    dst
385
                    );
386
                continue;
387
            }
388

    
389
            if (written != convert_samples << hw->info.shift) {
390
                int wsamples = written >> hw->info.shift;
391
                int wbytes = wsamples << hw->info.shift;
392
                if (wbytes != written) {
393
                    dolog ("warning: Misaligned write %d (requested %d), "
394
                           "alignment %d\n",
395
                           wbytes, written, hw->info.align + 1);
396
                }
397
                decr -= wsamples;
398
                rpos = (rpos + wsamples) % hw->samples;
399
                break;
400
            }
401
        }
402

    
403
        rpos = (rpos + convert_samples) % hw->samples;
404
        samples -= convert_samples;
405
    }
406
    if (oss->mmapped) {
407
        oss->old_optr = cntinfo.ptr;
408
    }
409

    
410
    hw->rpos = rpos;
411
    return decr;
412
}
413

    
414
static void oss_fini_out (HWVoiceOut *hw)
415
{
416
    int err;
417
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
418

    
419
    ldebug ("oss_fini\n");
420
    oss_anal_close (&oss->fd);
421

    
422
    if (oss->pcm_buf) {
423
        if (oss->mmapped) {
424
            err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
425
            if (err) {
426
                oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
427
                            oss->pcm_buf, hw->samples << hw->info.shift);
428
            }
429
        }
430
        else {
431
            qemu_free (oss->pcm_buf);
432
        }
433
        oss->pcm_buf = NULL;
434
    }
435
}
436

    
437
static int oss_init_out (HWVoiceOut *hw, audsettings_t *as)
438
{
439
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
440
    struct oss_params req, obt;
441
    int endianness;
442
    int err;
443
    int fd;
444
    audfmt_e effective_fmt;
445
    audsettings_t obt_as;
446

    
447
    oss->fd = -1;
448

    
449
    req.fmt = aud_to_ossfmt (as->fmt);
450
    req.freq = as->freq;
451
    req.nchannels = as->nchannels;
452
    req.fragsize = conf.fragsize;
453
    req.nfrags = conf.nfrags;
454

    
455
    if (oss_open (0, &req, &obt, &fd)) {
456
        return -1;
457
    }
458

    
459
    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
460
    if (err) {
461
        oss_anal_close (&fd);
462
        return -1;
463
    }
464

    
465
    obt_as.freq = obt.freq;
466
    obt_as.nchannels = obt.nchannels;
467
    obt_as.fmt = effective_fmt;
468
    obt_as.endianness = endianness;
469

    
470
    audio_pcm_init_info (&hw->info, &obt_as);
471
    oss->nfrags = obt.nfrags;
472
    oss->fragsize = obt.fragsize;
473

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

    
479
    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
480

    
481
    oss->mmapped = 0;
482
    if (conf.try_mmap) {
483
        oss->pcm_buf = mmap (
484
            0,
485
            hw->samples << hw->info.shift,
486
            PROT_READ | PROT_WRITE,
487
            MAP_SHARED,
488
            fd,
489
            0
490
            );
491
        if (oss->pcm_buf == MAP_FAILED) {
492
            oss_logerr (errno, "Failed to map %d bytes of DAC\n",
493
                        hw->samples << hw->info.shift);
494
        } else {
495
            int err;
496
            int trig = 0;
497
            if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
498
                oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
499
            }
500
            else {
501
                trig = PCM_ENABLE_OUTPUT;
502
                if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
503
                    oss_logerr (
504
                        errno,
505
                        "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
506
                        );
507
                }
508
                else {
509
                    oss->mmapped = 1;
510
                }
511
            }
512

    
513
            if (!oss->mmapped) {
514
                err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
515
                if (err) {
516
                    oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
517
                                oss->pcm_buf, hw->samples << hw->info.shift);
518
                }
519
            }
520
        }
521
    }
522

    
523
    if (!oss->mmapped) {
524
        oss->pcm_buf = audio_calloc (
525
            AUDIO_FUNC,
526
            hw->samples,
527
            1 << hw->info.shift
528
            );
529
        if (!oss->pcm_buf) {
530
            dolog (
531
                "Could not allocate DAC buffer (%d samples, each %d bytes)\n",
532
                hw->samples,
533
                1 << hw->info.shift
534
                );
535
            oss_anal_close (&fd);
536
            return -1;
537
        }
538
    }
539

    
540
    oss->fd = fd;
541
    return 0;
542
}
543

    
544
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
545
{
546
    int trig;
547
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
548

    
549
    if (!oss->mmapped) {
550
        return 0;
551
    }
552

    
553
    switch (cmd) {
554
    case VOICE_ENABLE:
555
        ldebug ("enabling voice\n");
556
        audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
557
        trig = PCM_ENABLE_OUTPUT;
558
        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
559
            oss_logerr (
560
                errno,
561
                "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
562
                );
563
            return -1;
564
        }
565
        break;
566

    
567
    case VOICE_DISABLE:
568
        ldebug ("disabling voice\n");
569
        trig = 0;
570
        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
571
            oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
572
            return -1;
573
        }
574
        break;
575
    }
576
    return 0;
577
}
578

    
579
static int oss_init_in (HWVoiceIn *hw, audsettings_t *as)
580
{
581
    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
582
    struct oss_params req, obt;
583
    int endianness;
584
    int err;
585
    int fd;
586
    audfmt_e effective_fmt;
587
    audsettings_t obt_as;
588

    
589
    oss->fd = -1;
590

    
591
    req.fmt = aud_to_ossfmt (as->fmt);
592
    req.freq = as->freq;
593
    req.nchannels = as->nchannels;
594
    req.fragsize = conf.fragsize;
595
    req.nfrags = conf.nfrags;
596
    if (oss_open (1, &req, &obt, &fd)) {
597
        return -1;
598
    }
599

    
600
    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
601
    if (err) {
602
        oss_anal_close (&fd);
603
        return -1;
604
    }
605

    
606
    obt_as.freq = obt.freq;
607
    obt_as.nchannels = obt.nchannels;
608
    obt_as.fmt = effective_fmt;
609
    obt_as.endianness = endianness;
610

    
611
    audio_pcm_init_info (&hw->info, &obt_as);
612
    oss->nfrags = obt.nfrags;
613
    oss->fragsize = obt.fragsize;
614

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

    
620
    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
621
    oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
622
    if (!oss->pcm_buf) {
623
        dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
624
               hw->samples, 1 << hw->info.shift);
625
        oss_anal_close (&fd);
626
        return -1;
627
    }
628

    
629
    oss->fd = fd;
630
    return 0;
631
}
632

    
633
static void oss_fini_in (HWVoiceIn *hw)
634
{
635
    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
636

    
637
    oss_anal_close (&oss->fd);
638

    
639
    if (oss->pcm_buf) {
640
        qemu_free (oss->pcm_buf);
641
        oss->pcm_buf = NULL;
642
    }
643
}
644

    
645
static int oss_run_in (HWVoiceIn *hw)
646
{
647
    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
648
    int hwshift = hw->info.shift;
649
    int i;
650
    int live = audio_pcm_hw_get_live_in (hw);
651
    int dead = hw->samples - live;
652
    size_t read_samples = 0;
653
    struct {
654
        int add;
655
        int len;
656
    } bufs[2] = {
657
        { hw->wpos, 0 },
658
        { 0, 0 }
659
    };
660

    
661
    if (!dead) {
662
        return 0;
663
    }
664

    
665
    if (hw->wpos + dead > hw->samples) {
666
        bufs[0].len = (hw->samples - hw->wpos) << hwshift;
667
        bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
668
    }
669
    else {
670
        bufs[0].len = dead << hwshift;
671
    }
672

    
673

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

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

    
681
            if (nread > 0) {
682
                if (nread & hw->info.align) {
683
                    dolog ("warning: Misaligned read %zd (requested %d), "
684
                           "alignment %d\n", nread, bufs[i].add << hwshift,
685
                           hw->info.align + 1);
686
                }
687
                read_samples += nread >> hwshift;
688
                hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
689
                          &nominal_volume);
690
            }
691

    
692
            if (bufs[i].len - nread) {
693
                if (nread == -1) {
694
                    switch (errno) {
695
                    case EINTR:
696
                    case EAGAIN:
697
                        break;
698
                    default:
699
                        oss_logerr (
700
                            errno,
701
                            "Failed to read %d bytes of audio (to %p)\n",
702
                            bufs[i].len, p
703
                            );
704
                        break;
705
                    }
706
                }
707
                break;
708
            }
709
        }
710
    }
711

    
712
    hw->wpos = (hw->wpos + read_samples) % hw->samples;
713
    return read_samples;
714
}
715

    
716
static int oss_read (SWVoiceIn *sw, void *buf, int size)
717
{
718
    return audio_pcm_sw_read (sw, buf, size);
719
}
720

    
721
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
722
{
723
    (void) hw;
724
    (void) cmd;
725
    return 0;
726
}
727

    
728
static void *oss_audio_init (void)
729
{
730
    return &conf;
731
}
732

    
733
static void oss_audio_fini (void *opaque)
734
{
735
    (void) opaque;
736
}
737

    
738
static struct audio_option oss_options[] = {
739
    {"FRAGSIZE", AUD_OPT_INT, &conf.fragsize,
740
     "Fragment size in bytes", NULL, 0},
741
    {"NFRAGS", AUD_OPT_INT, &conf.nfrags,
742
     "Number of fragments", NULL, 0},
743
    {"MMAP", AUD_OPT_BOOL, &conf.try_mmap,
744
     "Try using memory mapped access", NULL, 0},
745
    {"DAC_DEV", AUD_OPT_STR, &conf.devpath_out,
746
     "Path to DAC device", NULL, 0},
747
    {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
748
     "Path to ADC device", NULL, 0},
749
    {"DEBUG", AUD_OPT_BOOL, &conf.debug,
750
     "Turn on some debugging messages", NULL, 0},
751
    {NULL, 0, NULL, NULL, NULL, 0}
752
};
753

    
754
static struct audio_pcm_ops oss_pcm_ops = {
755
    oss_init_out,
756
    oss_fini_out,
757
    oss_run_out,
758
    oss_write,
759
    oss_ctl_out,
760

    
761
    oss_init_in,
762
    oss_fini_in,
763
    oss_run_in,
764
    oss_read,
765
    oss_ctl_in
766
};
767

    
768
struct audio_driver oss_audio_driver = {
769
    INIT_FIELD (name           = ) "oss",
770
    INIT_FIELD (descr          = ) "OSS http://www.opensound.com",
771
    INIT_FIELD (options        = ) oss_options,
772
    INIT_FIELD (init           = ) oss_audio_init,
773
    INIT_FIELD (fini           = ) oss_audio_fini,
774
    INIT_FIELD (pcm_ops        = ) &oss_pcm_ops,
775
    INIT_FIELD (can_be_default = ) 1,
776
    INIT_FIELD (max_voices_out = ) INT_MAX,
777
    INIT_FIELD (max_voices_in  = ) INT_MAX,
778
    INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut),
779
    INIT_FIELD (voice_size_in  = ) sizeof (OSSVoiceIn)
780
};