Statistics
| Branch: | Revision:

root / audio / ossaudio.c @ 87ecb68b

History | View | Annotate | Download (19.8 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)) {
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
    obt->fmt = fmt;
258
    obt->nchannels = nchannels;
259
    obt->freq = freq;
260
    obt->nfrags = abinfo.fragstotal;
261
    obt->fragsize = abinfo.fragsize;
262
    *pfd = fd;
263

    
264
#ifdef DEBUG_MISMATCHES
265
    if ((req->fmt != obt->fmt) ||
266
        (req->nchannels != obt->nchannels) ||
267
        (req->freq != obt->freq) ||
268
        (req->fragsize != obt->fragsize) ||
269
        (req->nfrags != obt->nfrags)) {
270
        dolog ("Audio parameters mismatch\n");
271
        oss_dump_info (req, obt);
272
    }
273
#endif
274

    
275
#ifdef DEBUG
276
    oss_dump_info (req, obt);
277
#endif
278
    return 0;
279

    
280
 err:
281
    oss_anal_close (&fd);
282
    return -1;
283
}
284

    
285
static int oss_run_out (HWVoiceOut *hw)
286
{
287
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
288
    int err, rpos, live, decr;
289
    int samples;
290
    uint8_t *dst;
291
    st_sample_t *src;
292
    struct audio_buf_info abinfo;
293
    struct count_info cntinfo;
294
    int bufsize;
295

    
296
    live = audio_pcm_hw_get_live_out (hw);
297
    if (!live) {
298
        return 0;
299
    }
300

    
301
    bufsize = hw->samples << hw->info.shift;
302

    
303
    if (oss->mmapped) {
304
        int bytes;
305

    
306
        err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
307
        if (err < 0) {
308
            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
309
            return 0;
310
        }
311

    
312
        if (cntinfo.ptr == oss->old_optr) {
313
            if (abs (hw->samples - live) < 64) {
314
                dolog ("warning: Overrun\n");
315
            }
316
            return 0;
317
        }
318

    
319
        if (cntinfo.ptr > oss->old_optr) {
320
            bytes = cntinfo.ptr - oss->old_optr;
321
        }
322
        else {
323
            bytes = bufsize + cntinfo.ptr - oss->old_optr;
324
        }
325

    
326
        decr = audio_MIN (bytes >> hw->info.shift, live);
327
    }
328
    else {
329
        err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
330
        if (err < 0) {
331
            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
332
            return 0;
333
        }
334

    
335
        if (abinfo.bytes > bufsize) {
336
            if (conf.debug) {
337
                dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
338
                       "please report your OS/audio hw to malc@pulsesoft.com\n",
339
                       abinfo.bytes, bufsize);
340
            }
341
            abinfo.bytes = bufsize;
342
        }
343

    
344
        if (abinfo.bytes < 0) {
345
            if (conf.debug) {
346
                dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
347
                       abinfo.bytes, bufsize);
348
            }
349
            return 0;
350
        }
351

    
352
        decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
353
        if (!decr) {
354
            return 0;
355
        }
356
    }
357

    
358
    samples = decr;
359
    rpos = hw->rpos;
360
    while (samples) {
361
        int left_till_end_samples = hw->samples - rpos;
362
        int convert_samples = audio_MIN (samples, left_till_end_samples);
363

    
364
        src = hw->mix_buf + rpos;
365
        dst = advance (oss->pcm_buf, rpos << hw->info.shift);
366

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

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

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

    
397
        rpos = (rpos + convert_samples) % hw->samples;
398
        samples -= convert_samples;
399
    }
400
    if (oss->mmapped) {
401
        oss->old_optr = cntinfo.ptr;
402
    }
403

    
404
    hw->rpos = rpos;
405
    return decr;
406
}
407

    
408
static void oss_fini_out (HWVoiceOut *hw)
409
{
410
    int err;
411
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
412

    
413
    ldebug ("oss_fini\n");
414
    oss_anal_close (&oss->fd);
415

    
416
    if (oss->pcm_buf) {
417
        if (oss->mmapped) {
418
            err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
419
            if (err) {
420
                oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
421
                            oss->pcm_buf, hw->samples << hw->info.shift);
422
            }
423
        }
424
        else {
425
            qemu_free (oss->pcm_buf);
426
        }
427
        oss->pcm_buf = NULL;
428
    }
429
}
430

    
431
static int oss_init_out (HWVoiceOut *hw, audsettings_t *as)
432
{
433
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
434
    struct oss_params req, obt;
435
    int endianness;
436
    int err;
437
    int fd;
438
    audfmt_e effective_fmt;
439
    audsettings_t obt_as;
440

    
441
    oss->fd = -1;
442

    
443
    req.fmt = aud_to_ossfmt (as->fmt);
444
    req.freq = as->freq;
445
    req.nchannels = as->nchannels;
446
    req.fragsize = conf.fragsize;
447
    req.nfrags = conf.nfrags;
448

    
449
    if (oss_open (0, &req, &obt, &fd)) {
450
        return -1;
451
    }
452

    
453
    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
454
    if (err) {
455
        oss_anal_close (&fd);
456
        return -1;
457
    }
458

    
459
    obt_as.freq = obt.freq;
460
    obt_as.nchannels = obt.nchannels;
461
    obt_as.fmt = effective_fmt;
462
    obt_as.endianness = endianness;
463

    
464
    audio_pcm_init_info (&hw->info, &obt_as);
465
    oss->nfrags = obt.nfrags;
466
    oss->fragsize = obt.fragsize;
467

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

    
473
    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
474

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

    
507
            if (!oss->mmapped) {
508
                err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
509
                if (err) {
510
                    oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
511
                                oss->pcm_buf, hw->samples << hw->info.shift);
512
                }
513
            }
514
        }
515
    }
516

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

    
534
    oss->fd = fd;
535
    return 0;
536
}
537

    
538
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
539
{
540
    int trig;
541
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
542

    
543
    if (!oss->mmapped) {
544
        return 0;
545
    }
546

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

    
561
    case VOICE_DISABLE:
562
        ldebug ("disabling voice\n");
563
        trig = 0;
564
        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
565
            oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
566
            return -1;
567
        }
568
        break;
569
    }
570
    return 0;
571
}
572

    
573
static int oss_init_in (HWVoiceIn *hw, audsettings_t *as)
574
{
575
    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
576
    struct oss_params req, obt;
577
    int endianness;
578
    int err;
579
    int fd;
580
    audfmt_e effective_fmt;
581
    audsettings_t obt_as;
582

    
583
    oss->fd = -1;
584

    
585
    req.fmt = aud_to_ossfmt (as->fmt);
586
    req.freq = as->freq;
587
    req.nchannels = as->nchannels;
588
    req.fragsize = conf.fragsize;
589
    req.nfrags = conf.nfrags;
590
    if (oss_open (1, &req, &obt, &fd)) {
591
        return -1;
592
    }
593

    
594
    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
595
    if (err) {
596
        oss_anal_close (&fd);
597
        return -1;
598
    }
599

    
600
    obt_as.freq = obt.freq;
601
    obt_as.nchannels = obt.nchannels;
602
    obt_as.fmt = effective_fmt;
603
    obt_as.endianness = endianness;
604

    
605
    audio_pcm_init_info (&hw->info, &obt_as);
606
    oss->nfrags = obt.nfrags;
607
    oss->fragsize = obt.fragsize;
608

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

    
614
    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
615
    oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
616
    if (!oss->pcm_buf) {
617
        dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
618
               hw->samples, 1 << hw->info.shift);
619
        oss_anal_close (&fd);
620
        return -1;
621
    }
622

    
623
    oss->fd = fd;
624
    return 0;
625
}
626

    
627
static void oss_fini_in (HWVoiceIn *hw)
628
{
629
    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
630

    
631
    oss_anal_close (&oss->fd);
632

    
633
    if (oss->pcm_buf) {
634
        qemu_free (oss->pcm_buf);
635
        oss->pcm_buf = NULL;
636
    }
637
}
638

    
639
static int oss_run_in (HWVoiceIn *hw)
640
{
641
    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
642
    int hwshift = hw->info.shift;
643
    int i;
644
    int live = audio_pcm_hw_get_live_in (hw);
645
    int dead = hw->samples - live;
646
    size_t read_samples = 0;
647
    struct {
648
        int add;
649
        int len;
650
    } bufs[2] = {
651
        { hw->wpos, 0 },
652
        { 0, 0 }
653
    };
654

    
655
    if (!dead) {
656
        return 0;
657
    }
658

    
659
    if (hw->wpos + dead > hw->samples) {
660
        bufs[0].len = (hw->samples - hw->wpos) << hwshift;
661
        bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
662
    }
663
    else {
664
        bufs[0].len = dead << hwshift;
665
    }
666

    
667

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

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

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

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

    
706
    hw->wpos = (hw->wpos + read_samples) % hw->samples;
707
    return read_samples;
708
}
709

    
710
static int oss_read (SWVoiceIn *sw, void *buf, int size)
711
{
712
    return audio_pcm_sw_read (sw, buf, size);
713
}
714

    
715
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
716
{
717
    (void) hw;
718
    (void) cmd;
719
    return 0;
720
}
721

    
722
static void *oss_audio_init (void)
723
{
724
    return &conf;
725
}
726

    
727
static void oss_audio_fini (void *opaque)
728
{
729
    (void) opaque;
730
}
731

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

    
748
static struct audio_pcm_ops oss_pcm_ops = {
749
    oss_init_out,
750
    oss_fini_out,
751
    oss_run_out,
752
    oss_write,
753
    oss_ctl_out,
754

    
755
    oss_init_in,
756
    oss_fini_in,
757
    oss_run_in,
758
    oss_read,
759
    oss_ctl_in
760
};
761

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