|
|
1.1 ! root 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: #define dolog(...) AUD_log ("oss", __VA_ARGS__) ! 44: #ifdef DEBUG ! 45: #define ldebug(...) dolog (__VA_ARGS__) ! 46: #else ! 47: #define ldebug(...) ! 48: #endif ! 49: ! 50: #define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE" ! 51: #define QC_OSS_NFRAGS "QEMU_OSS_NFRAGS" ! 52: #define QC_OSS_MMAP "QEMU_OSS_MMAP" ! 53: #define QC_OSS_DEV "QEMU_OSS_DEV" ! 54: ! 55: #define errstr() strerror (errno) ! 56: ! 57: static struct { ! 58: int try_mmap; ! 59: int nfrags; ! 60: int fragsize; ! 61: const char *dspname; ! 62: } conf = { ! 63: .try_mmap = 0, ! 64: .nfrags = 4, ! 65: .fragsize = 4096, ! 66: .dspname = "/dev/dsp" ! 67: }; ! 68: ! 69: struct oss_params { ! 70: int freq; ! 71: audfmt_e fmt; ! 72: int nchannels; ! 73: int nfrags; ! 74: int fragsize; ! 75: }; ! 76: ! 77: static int oss_hw_write (SWVoice *sw, void *buf, int len) ! 78: { ! 79: return pcm_hw_write (sw, buf, len); ! 80: } ! 81: ! 82: static int AUD_to_ossfmt (audfmt_e fmt) ! 83: { ! 84: switch (fmt) { ! 85: case AUD_FMT_S8: return AFMT_S8; ! 86: case AUD_FMT_U8: return AFMT_U8; ! 87: case AUD_FMT_S16: return AFMT_S16_LE; ! 88: case AUD_FMT_U16: return AFMT_U16_LE; ! 89: default: ! 90: dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); ! 91: exit (EXIT_FAILURE); ! 92: } ! 93: } ! 94: ! 95: static int oss_to_audfmt (int fmt) ! 96: { ! 97: switch (fmt) { ! 98: case AFMT_S8: return AUD_FMT_S8; ! 99: case AFMT_U8: return AUD_FMT_U8; ! 100: case AFMT_S16_LE: return AUD_FMT_S16; ! 101: case AFMT_U16_LE: return AUD_FMT_U16; ! 102: default: ! 103: dolog ("Internal logic error: Unrecognized OSS audio format %d\n" ! 104: "Aborting\n", ! 105: fmt); ! 106: exit (EXIT_FAILURE); ! 107: } ! 108: } ! 109: ! 110: #ifdef DEBUG_PCM ! 111: static void oss_dump_pcm_info (struct oss_params *req, struct oss_params *obt) ! 112: { ! 113: dolog ("parameter | requested value | obtained value\n"); ! 114: dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); ! 115: dolog ("channels | %10d | %10d\n", req->nchannels, obt->nchannels); ! 116: dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); ! 117: dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags); ! 118: dolog ("fragsize | %10d | %10d\n", req->fragsize, obt->fragsize); ! 119: } ! 120: #endif ! 121: ! 122: static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd) ! 123: { ! 124: int fd; ! 125: int mmmmssss; ! 126: audio_buf_info abinfo; ! 127: int fmt, freq, nchannels; ! 128: const char *dspname = conf.dspname; ! 129: ! 130: fd = open (dspname, O_WRONLY | O_NONBLOCK); ! 131: if (-1 == fd) { ! 132: dolog ("Could not initialize audio hardware. Failed to open `%s':\n" ! 133: "Reason:%s\n", ! 134: dspname, ! 135: errstr ()); ! 136: return -1; ! 137: } ! 138: ! 139: freq = req->freq; ! 140: nchannels = req->nchannels; ! 141: fmt = req->fmt; ! 142: ! 143: if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { ! 144: dolog ("Could not initialize audio hardware\n" ! 145: "Failed to set sample size\n" ! 146: "Reason: %s\n", ! 147: errstr ()); ! 148: goto err; ! 149: } ! 150: ! 151: if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) { ! 152: dolog ("Could not initialize audio hardware\n" ! 153: "Failed to set number of channels\n" ! 154: "Reason: %s\n", ! 155: errstr ()); ! 156: goto err; ! 157: } ! 158: ! 159: if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) { ! 160: dolog ("Could not initialize audio hardware\n" ! 161: "Failed to set frequency\n" ! 162: "Reason: %s\n", ! 163: errstr ()); ! 164: goto err; ! 165: } ! 166: ! 167: if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) { ! 168: dolog ("Could not initialize audio hardware\n" ! 169: "Failed to set non-blocking mode\n" ! 170: "Reason: %s\n", ! 171: errstr ()); ! 172: goto err; ! 173: } ! 174: ! 175: mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize); ! 176: if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) { ! 177: dolog ("Could not initialize audio hardware\n" ! 178: "Failed to set buffer length (%d, %d)\n" ! 179: "Reason:%s\n", ! 180: conf.nfrags, conf.fragsize, ! 181: errstr ()); ! 182: goto err; ! 183: } ! 184: ! 185: if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &abinfo)) { ! 186: dolog ("Could not initialize audio hardware\n" ! 187: "Failed to get buffer length\n" ! 188: "Reason:%s\n", ! 189: errstr ()); ! 190: goto err; ! 191: } ! 192: ! 193: obt->fmt = fmt; ! 194: obt->nchannels = nchannels; ! 195: obt->freq = freq; ! 196: obt->nfrags = abinfo.fragstotal; ! 197: obt->fragsize = abinfo.fragsize; ! 198: *pfd = fd; ! 199: ! 200: if ((req->fmt != obt->fmt) || ! 201: (req->nchannels != obt->nchannels) || ! 202: (req->freq != obt->freq) || ! 203: (req->fragsize != obt->fragsize) || ! 204: (req->nfrags != obt->nfrags)) { ! 205: #ifdef DEBUG_PCM ! 206: dolog ("Audio parameters mismatch\n"); ! 207: oss_dump_pcm_info (req, obt); ! 208: #endif ! 209: } ! 210: ! 211: #ifdef DEBUG_PCM ! 212: oss_dump_pcm_info (req, obt); ! 213: #endif ! 214: return 0; ! 215: ! 216: err: ! 217: close (fd); ! 218: return -1; ! 219: } ! 220: ! 221: static void oss_hw_run (HWVoice *hw) ! 222: { ! 223: OSSVoice *oss = (OSSVoice *) hw; ! 224: int err, rpos, live, decr; ! 225: int samples; ! 226: uint8_t *dst; ! 227: st_sample_t *src; ! 228: struct audio_buf_info abinfo; ! 229: struct count_info cntinfo; ! 230: ! 231: live = pcm_hw_get_live (hw); ! 232: if (live <= 0) ! 233: return; ! 234: ! 235: if (oss->mmapped) { ! 236: int bytes; ! 237: ! 238: err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo); ! 239: if (err < 0) { ! 240: dolog ("SNDCTL_DSP_GETOPTR failed\nReason: %s\n", errstr ()); ! 241: return; ! 242: } ! 243: ! 244: if (cntinfo.ptr == oss->old_optr) { ! 245: if (abs (hw->samples - live) < 64) ! 246: dolog ("overrun\n"); ! 247: return; ! 248: } ! 249: ! 250: if (cntinfo.ptr > oss->old_optr) { ! 251: bytes = cntinfo.ptr - oss->old_optr; ! 252: } ! 253: else { ! 254: bytes = hw->bufsize + cntinfo.ptr - oss->old_optr; ! 255: } ! 256: ! 257: decr = audio_MIN (bytes >> hw->shift, live); ! 258: } ! 259: else { ! 260: err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo); ! 261: if (err < 0) { ! 262: dolog ("SNDCTL_DSP_GETOSPACE failed\nReason: %s\n", errstr ()); ! 263: return; ! 264: } ! 265: ! 266: decr = audio_MIN (abinfo.bytes >> hw->shift, live); ! 267: if (decr <= 0) ! 268: return; ! 269: } ! 270: ! 271: samples = decr; ! 272: rpos = hw->rpos; ! 273: while (samples) { ! 274: int left_till_end_samples = hw->samples - rpos; ! 275: int convert_samples = audio_MIN (samples, left_till_end_samples); ! 276: ! 277: src = advance (hw->mix_buf, rpos * sizeof (st_sample_t)); ! 278: dst = advance (oss->pcm_buf, rpos << hw->shift); ! 279: ! 280: hw->clip (dst, src, convert_samples); ! 281: if (!oss->mmapped) { ! 282: int written; ! 283: ! 284: written = write (oss->fd, dst, convert_samples << hw->shift); ! 285: /* XXX: follow errno recommendations ? */ ! 286: if (written == -1) { ! 287: dolog ("Failed to write audio\nReason: %s\n", errstr ()); ! 288: continue; ! 289: } ! 290: ! 291: if (written != convert_samples << hw->shift) { ! 292: int wsamples = written >> hw->shift; ! 293: int wbytes = wsamples << hw->shift; ! 294: if (wbytes != written) { ! 295: dolog ("Unaligned write %d, %d\n", wbytes, written); ! 296: } ! 297: memset (src, 0, wbytes); ! 298: decr -= samples; ! 299: rpos = (rpos + wsamples) % hw->samples; ! 300: break; ! 301: } ! 302: } ! 303: memset (src, 0, convert_samples * sizeof (st_sample_t)); ! 304: ! 305: rpos = (rpos + convert_samples) % hw->samples; ! 306: samples -= convert_samples; ! 307: } ! 308: if (oss->mmapped) { ! 309: oss->old_optr = cntinfo.ptr; ! 310: } ! 311: ! 312: pcm_hw_dec_live (hw, decr); ! 313: hw->rpos = rpos; ! 314: } ! 315: ! 316: static void oss_hw_fini (HWVoice *hw) ! 317: { ! 318: int err; ! 319: OSSVoice *oss = (OSSVoice *) hw; ! 320: ! 321: ldebug ("oss_hw_fini\n"); ! 322: err = close (oss->fd); ! 323: if (err) { ! 324: dolog ("Failed to close OSS descriptor\nReason: %s\n", errstr ()); ! 325: } ! 326: oss->fd = -1; ! 327: ! 328: if (oss->pcm_buf) { ! 329: if (oss->mmapped) { ! 330: err = munmap (oss->pcm_buf, hw->bufsize); ! 331: if (err) { ! 332: dolog ("Failed to unmap OSS buffer\nReason: %s\n", ! 333: errstr ()); ! 334: } ! 335: } ! 336: else { ! 337: qemu_free (oss->pcm_buf); ! 338: } ! 339: oss->pcm_buf = NULL; ! 340: } ! 341: } ! 342: ! 343: static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) ! 344: { ! 345: OSSVoice *oss = (OSSVoice *) hw; ! 346: struct oss_params req, obt; ! 347: ! 348: assert (!oss->fd); ! 349: req.fmt = AUD_to_ossfmt (fmt); ! 350: req.freq = freq; ! 351: req.nchannels = nchannels; ! 352: req.fragsize = conf.fragsize; ! 353: req.nfrags = conf.nfrags; ! 354: ! 355: if (oss_open (&req, &obt, &oss->fd)) ! 356: return -1; ! 357: ! 358: hw->freq = obt.freq; ! 359: hw->fmt = oss_to_audfmt (obt.fmt); ! 360: hw->nchannels = obt.nchannels; ! 361: ! 362: oss->nfrags = obt.nfrags; ! 363: oss->fragsize = obt.fragsize; ! 364: hw->bufsize = obt.nfrags * obt.fragsize; ! 365: ! 366: oss->mmapped = 0; ! 367: if (conf.try_mmap) { ! 368: oss->pcm_buf = mmap (0, hw->bufsize, PROT_READ | PROT_WRITE, ! 369: MAP_SHARED, oss->fd, 0); ! 370: if (oss->pcm_buf == MAP_FAILED) { ! 371: dolog ("Failed to mmap OSS device\nReason: %s\n", ! 372: errstr ()); ! 373: } else { ! 374: int err; ! 375: int trig = 0; ! 376: if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { ! 377: dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n", ! 378: errstr ()); ! 379: } ! 380: else { ! 381: trig = PCM_ENABLE_OUTPUT; ! 382: if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { ! 383: dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" ! 384: "Reason: %s\n", errstr ()); ! 385: } ! 386: else { ! 387: oss->mmapped = 1; ! 388: } ! 389: } ! 390: ! 391: if (!oss->mmapped) { ! 392: err = munmap (oss->pcm_buf, hw->bufsize); ! 393: if (err) { ! 394: dolog ("Failed to unmap OSS device\nReason: %s\n", ! 395: errstr ()); ! 396: } ! 397: } ! 398: } ! 399: } ! 400: ! 401: if (!oss->mmapped) { ! 402: oss->pcm_buf = qemu_mallocz (hw->bufsize); ! 403: if (!oss->pcm_buf) { ! 404: close (oss->fd); ! 405: oss->fd = -1; ! 406: return -1; ! 407: } ! 408: } ! 409: ! 410: return 0; ! 411: } ! 412: ! 413: static int oss_hw_ctl (HWVoice *hw, int cmd, ...) ! 414: { ! 415: int trig; ! 416: OSSVoice *oss = (OSSVoice *) hw; ! 417: ! 418: if (!oss->mmapped) ! 419: return 0; ! 420: ! 421: switch (cmd) { ! 422: case VOICE_ENABLE: ! 423: ldebug ("enabling voice\n"); ! 424: pcm_hw_clear (hw, oss->pcm_buf, hw->samples); ! 425: trig = PCM_ENABLE_OUTPUT; ! 426: if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { ! 427: dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" ! 428: "Reason: %s\n", errstr ()); ! 429: return -1; ! 430: } ! 431: break; ! 432: ! 433: case VOICE_DISABLE: ! 434: ldebug ("disabling voice\n"); ! 435: trig = 0; ! 436: if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { ! 437: dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n", ! 438: errstr ()); ! 439: return -1; ! 440: } ! 441: break; ! 442: } ! 443: return 0; ! 444: } ! 445: ! 446: static void *oss_audio_init (void) ! 447: { ! 448: conf.fragsize = audio_get_conf_int (QC_OSS_FRAGSIZE, conf.fragsize); ! 449: conf.nfrags = audio_get_conf_int (QC_OSS_NFRAGS, conf.nfrags); ! 450: conf.try_mmap = audio_get_conf_int (QC_OSS_MMAP, conf.try_mmap); ! 451: conf.dspname = audio_get_conf_str (QC_OSS_DEV, conf.dspname); ! 452: return &conf; ! 453: } ! 454: ! 455: static void oss_audio_fini (void *opaque) ! 456: { ! 457: } ! 458: ! 459: struct pcm_ops oss_pcm_ops = { ! 460: oss_hw_init, ! 461: oss_hw_fini, ! 462: oss_hw_run, ! 463: oss_hw_write, ! 464: oss_hw_ctl ! 465: }; ! 466: ! 467: struct audio_output_driver oss_output_driver = { ! 468: "oss", ! 469: oss_audio_init, ! 470: oss_audio_fini, ! 471: &oss_pcm_ops, ! 472: 1, ! 473: INT_MAX, ! 474: sizeof (OSSVoice) ! 475: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.