Statistics
| Branch: | Revision:

root / audio / ossaudio.c @ fb065187

History | View | Annotate | Download (12.7 kB)

1
/*
2
 * QEMU OSS audio output driver
3
 * 
4
 * Copyright (c) 2003-2004 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 <sys/mman.h>
25
#include <sys/types.h>
26
#include <sys/ioctl.h>
27
#include <sys/soundcard.h>
28
#include <assert.h>
29
#include "vl.h"
30

    
31
#include "audio/audio_int.h"
32

    
33
typedef struct OSSVoice {
34
    HWVoice hw;
35
    void *pcm_buf;
36
    int fd;
37
    int nfrags;
38
    int fragsize;
39
    int mmapped;
40
    int old_optr;
41
} OSSVoice;
42

    
43

    
44
#define dolog(...) AUD_log ("oss", __VA_ARGS__)
45
#ifdef DEBUG
46
#define ldebug(...) dolog (__VA_ARGS__)
47
#else
48
#define ldebug(...)
49
#endif
50

    
51
#define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE"
52
#define QC_OSS_NFRAGS   "QEMU_OSS_NFRAGS"
53
#define QC_OSS_MMAP     "QEMU_OSS_MMAP"
54
#define QC_OSS_DEV      "QEMU_OSS_DEV"
55

    
56
#define errstr() strerror (errno)
57

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

    
70
struct oss_params {
71
    int freq;
72
    audfmt_e fmt;
73
    int nchannels;
74
    int nfrags;
75
    int fragsize;
76
};
77

    
78
static int oss_hw_write (SWVoice *sw, void *buf, int len)
79
{
80
    return pcm_hw_write (sw, buf, len);
81
}
82

    
83
static int AUD_to_ossfmt (audfmt_e fmt)
84
{
85
    switch (fmt) {
86
    case AUD_FMT_S8: return AFMT_S8;
87
    case AUD_FMT_U8: return AFMT_U8;
88
    case AUD_FMT_S16: return AFMT_S16_LE;
89
    case AUD_FMT_U16: return AFMT_U16_LE;
90
    default:
91
        dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
92
        exit (EXIT_FAILURE);
93
    }
94
}
95

    
96
static int oss_to_audfmt (int fmt)
97
{
98
    switch (fmt) {
99
    case AFMT_S8: return AUD_FMT_S8;
100
    case AFMT_U8: return AUD_FMT_U8;
101
    case AFMT_S16_LE: return AUD_FMT_S16;
102
    case AFMT_U16_LE: return AUD_FMT_U16;
103
    default:
104
        dolog ("Internal logic error: Unrecognized OSS audio format %d\n"
105
               "Aborting\n",
106
               fmt);
107
        exit (EXIT_FAILURE);
108
    }
109
}
110

    
111
#ifdef DEBUG_PCM
112
static void oss_dump_pcm_info (struct oss_params *req, struct oss_params *obt)
113
{
114
    dolog ("parameter | requested value | obtained value\n");
115
    dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);
116
    dolog ("channels  |      %10d |     %10d\n", req->nchannels, obt->nchannels);
117
    dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
118
    dolog ("nfrags    |      %10d |     %10d\n", req->nfrags, obt->nfrags);
119
    dolog ("fragsize  |      %10d |     %10d\n", req->fragsize, obt->fragsize);
120
}
121
#endif
122

    
123
static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd)
124
{
125
    int fd;
126
    int mmmmssss;
127
    audio_buf_info abinfo;
128
    int fmt, freq, nchannels;
129
    const char *dspname = conf.dspname;
130

    
131
    fd = open (dspname, O_RDWR | O_NONBLOCK);
132
    if (-1 == fd) {
133
        dolog ("Could not initialize audio hardware. Failed to open `%s':\n"
134
               "Reason:%s\n",
135
               dspname,
136
               errstr ());
137
        return -1;
138
    }
139

    
140
    freq = req->freq;
141
    nchannels = req->nchannels;
142
    fmt = req->fmt;
143

    
144
    if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
145
        dolog ("Could not initialize audio hardware\n"
146
               "Failed to set sample size\n"
147
               "Reason: %s\n",
148
               errstr ());
149
        goto err;
150
    }
151

    
152
    if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
153
        dolog ("Could not initialize audio hardware\n"
154
               "Failed to set number of channels\n"
155
               "Reason: %s\n",
156
               errstr ());
157
        goto err;
158
    }
159

    
160
    if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
161
        dolog ("Could not initialize audio hardware\n"
162
               "Failed to set frequency\n"
163
               "Reason: %s\n",
164
               errstr ());
165
        goto err;
166
    }
167

    
168
    if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) {
169
        dolog ("Could not initialize audio hardware\n"
170
               "Failed to set non-blocking mode\n"
171
               "Reason: %s\n",
172
               errstr ());
173
        goto err;
174
    }
175

    
176
    mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
177
    if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
178
        dolog ("Could not initialize audio hardware\n"
179
               "Failed to set buffer length (%d, %d)\n"
180
               "Reason:%s\n",
181
               conf.nfrags, conf.fragsize,
182
               errstr ());
183
        goto err;
184
    }
185

    
186
    if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &abinfo)) {
187
        dolog ("Could not initialize audio hardware\n"
188
               "Failed to get buffer length\n"
189
               "Reason:%s\n",
190
               errstr ());
191
        goto err;
192
    }
193

    
194
    obt->fmt = fmt;
195
    obt->nchannels = nchannels;
196
    obt->freq = freq;
197
    obt->nfrags = abinfo.fragstotal;
198
    obt->fragsize = abinfo.fragsize;
199
    *pfd = fd;
200

    
201
    if ((req->fmt != obt->fmt) ||
202
        (req->nchannels != obt->nchannels) ||
203
        (req->freq != obt->freq) ||
204
        (req->fragsize != obt->fragsize) ||
205
        (req->nfrags != obt->nfrags)) {
206
#ifdef DEBUG_PCM
207
        dolog ("Audio parameters mismatch\n");
208
        oss_dump_pcm_info (req, obt);
209
#endif
210
    }
211

    
212
#ifdef DEBUG_PCM
213
    oss_dump_pcm_info (req, obt);
214
#endif
215
    return 0;
216

    
217
err:
218
    close (fd);
219
    return -1;
220
}
221

    
222
static void oss_hw_run (HWVoice *hw)
223
{
224
    OSSVoice *oss = (OSSVoice *) hw;
225
    int err, rpos, live, decr;
226
    int samples;
227
    uint8_t *dst;
228
    st_sample_t *src;
229
    struct audio_buf_info abinfo;
230
    struct count_info cntinfo;
231

    
232
    live = pcm_hw_get_live (hw);
233
    if (live <= 0)
234
        return;
235

    
236
    if (oss->mmapped) {
237
        int bytes;
238

    
239
        err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
240
        if (err < 0) {
241
            dolog ("SNDCTL_DSP_GETOPTR failed\nReason: %s\n", errstr ());
242
            return;
243
        }
244

    
245
        if (cntinfo.ptr == oss->old_optr) {
246
            if (abs (hw->samples - live) < 64)
247
                dolog ("overrun\n");
248
            return;
249
        }
250

    
251
        if (cntinfo.ptr > oss->old_optr) {
252
            bytes = cntinfo.ptr - oss->old_optr;
253
        }
254
        else {
255
            bytes = hw->bufsize + cntinfo.ptr - oss->old_optr;
256
        }
257

    
258
        decr = audio_MIN (bytes >> hw->shift, live);
259
    }
260
    else {
261
        err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
262
        if (err < 0) {
263
            dolog ("SNDCTL_DSP_GETOSPACE failed\nReason: %s\n", errstr ());
264
            return;
265
        }
266

    
267
        decr = audio_MIN (abinfo.bytes >> hw->shift, live);
268
        if (decr <= 0)
269
            return;
270
    }
271

    
272
    samples = decr;
273
    rpos = hw->rpos;
274
    while (samples) {
275
        int left_till_end_samples = hw->samples - rpos;
276
        int convert_samples = audio_MIN (samples, left_till_end_samples);
277

    
278
        src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
279
        dst = advance (oss->pcm_buf, rpos << hw->shift);
280

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

    
285
            written = write (oss->fd, dst, convert_samples << hw->shift);
286
            /* XXX: follow errno recommendations ? */
287
            if (written == -1) {
288
                dolog ("Failed to write audio\nReason: %s\n", errstr ());
289
                continue;
290
            }
291

    
292
            if (written != convert_samples << hw->shift) {
293
                int wsamples = written >> hw->shift;
294
                int wbytes = wsamples << hw->shift;
295
                if (wbytes != written) {
296
                    dolog ("Unaligned write %d, %d\n", wbytes, written);
297
                }
298
                memset (src, 0, wbytes);
299
                decr -= samples;
300
                rpos = (rpos + wsamples) % hw->samples;
301
                break;
302
            }
303
        }
304
        memset (src, 0, convert_samples * sizeof (st_sample_t));
305

    
306
        rpos = (rpos + convert_samples) % hw->samples;
307
        samples -= convert_samples;
308
    }
309
    if (oss->mmapped) {
310
        oss->old_optr = cntinfo.ptr;
311
    }
312

    
313
    pcm_hw_dec_live (hw, decr);
314
    hw->rpos = rpos;
315
}
316

    
317
static void oss_hw_fini (HWVoice *hw)
318
{
319
    int err;
320
    OSSVoice *oss = (OSSVoice *) hw;
321

    
322
    ldebug ("oss_hw_fini\n");
323
    err = close (oss->fd);
324
    if (err) {
325
        dolog ("Failed to close OSS descriptor\nReason: %s\n", errstr ());
326
    }
327
    oss->fd = -1;
328

    
329
    if (oss->pcm_buf) {
330
        if (oss->mmapped) {
331
            err = munmap (oss->pcm_buf, hw->bufsize);
332
            if (err) {
333
                dolog ("Failed to unmap OSS buffer\nReason: %s\n",
334
                       errstr ());
335
            }
336
        }
337
        else {
338
            qemu_free (oss->pcm_buf);
339
        }
340
        oss->pcm_buf = NULL;
341
    }
342
}
343

    
344
static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
345
{
346
    OSSVoice *oss = (OSSVoice *) hw;
347
    struct oss_params req, obt;
348

    
349
    assert (!oss->fd);
350
    req.fmt = AUD_to_ossfmt (fmt);
351
    req.freq = freq;
352
    req.nchannels = nchannels;
353
    req.fragsize = conf.fragsize;
354
    req.nfrags = conf.nfrags;
355

    
356
    if (oss_open (&req, &obt, &oss->fd))
357
        return -1;
358

    
359
    hw->freq = obt.freq;
360
    hw->fmt = oss_to_audfmt (obt.fmt);
361
    hw->nchannels = obt.nchannels;
362

    
363
    oss->nfrags = obt.nfrags;
364
    oss->fragsize = obt.fragsize;
365
    hw->bufsize = obt.nfrags * obt.fragsize;
366

    
367
    oss->mmapped = 0;
368
    if (conf.try_mmap) {
369
        oss->pcm_buf = mmap (0, hw->bufsize, PROT_READ | PROT_WRITE,
370
                             MAP_SHARED, oss->fd, 0);
371
        if (oss->pcm_buf == MAP_FAILED) {
372
            dolog ("Failed to mmap OSS device\nReason: %s\n",
373
                   errstr ());
374
        }
375

    
376
        for (;;) {
377
            int err;
378
            int trig = 0;
379
            if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
380
                dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
381
                       errstr ());
382
                goto fail;
383
            }
384

    
385
            trig = PCM_ENABLE_OUTPUT;
386
            if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
387
                dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
388
                       "Reason: %s\n", errstr ());
389
                goto fail;
390
            }
391
            oss->mmapped = 1;
392
            break;
393

    
394
        fail:
395
            err = munmap (oss->pcm_buf, hw->bufsize);
396
            if (err) {
397
                dolog ("Failed to unmap OSS device\nReason: %s\n",
398
                       errstr ());
399
            }
400
        }
401
    }
402

    
403
    if (!oss->mmapped) {
404
        oss->pcm_buf = qemu_mallocz (hw->bufsize);
405
        if (!oss->pcm_buf) {
406
            close (oss->fd);
407
            oss->fd = -1;
408
            return -1;
409
        }
410
    }
411

    
412
    return 0;
413
}
414

    
415
static int oss_hw_ctl (HWVoice *hw, int cmd, ...)
416
{
417
    int trig;
418
    OSSVoice *oss = (OSSVoice *) hw;
419

    
420
    if (!oss->mmapped)
421
        return 0;
422

    
423
    switch (cmd) {
424
    case VOICE_ENABLE:
425
        ldebug ("enabling voice\n");
426
        pcm_hw_clear (hw, oss->pcm_buf, hw->samples);
427
        trig = PCM_ENABLE_OUTPUT;
428
        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
429
            dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
430
                   "Reason: %s\n", errstr ());
431
            return -1;
432
        }
433
        break;
434

    
435
    case VOICE_DISABLE:
436
        ldebug ("disabling voice\n");
437
        trig = 0;
438
        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
439
            dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
440
                   errstr ());
441
            return -1;
442
        }
443
        break;
444
    }
445
    return 0;
446
}
447

    
448
static void *oss_audio_init (void)
449
{
450
    conf.fragsize = audio_get_conf_int (QC_OSS_FRAGSIZE, conf.fragsize);
451
    conf.nfrags = audio_get_conf_int (QC_OSS_NFRAGS, conf.nfrags);
452
    conf.try_mmap = audio_get_conf_int (QC_OSS_MMAP, conf.try_mmap);
453
    conf.dspname = audio_get_conf_str (QC_OSS_DEV, conf.dspname);
454
    return &conf;
455
}
456

    
457
static void oss_audio_fini (void *opaque)
458
{
459
}
460

    
461
struct pcm_ops oss_pcm_ops = {
462
    oss_hw_init,
463
    oss_hw_fini,
464
    oss_hw_run,
465
    oss_hw_write,
466
    oss_hw_ctl
467
};
468

    
469
struct audio_output_driver oss_output_driver = {
470
    "oss",
471
    oss_audio_init,
472
    oss_audio_fini,
473
    &oss_pcm_ops,
474
    1,
475
    INT_MAX,
476
    sizeof (OSSVoice)
477
};