|
|
1.1 root 1: /*
1.1.1.2 root 2: * QEMU OSS audio driver
3: *
4: * Copyright (c) 2003-2005 Vassili Karpov (malc)
5: *
1.1 root 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: */
1.1.1.4 root 24: #include <stdlib.h>
1.1 root 25: #include <sys/mman.h>
26: #include <sys/types.h>
27: #include <sys/ioctl.h>
1.1.1.4 root 28: #ifdef __OpenBSD__
29: #include <soundcard.h>
30: #else
1.1 root 31: #include <sys/soundcard.h>
1.1.1.4 root 32: #endif
33: #include "qemu-common.h"
1.1.1.7 ! root 34: #include "host-utils.h"
! 35: #include "qemu-char.h"
1.1.1.4 root 36: #include "audio.h"
1.1 root 37:
1.1.1.2 root 38: #define AUDIO_CAP "oss"
39: #include "audio_int.h"
1.1 root 40:
1.1.1.2 root 41: typedef struct OSSVoiceOut {
42: HWVoiceOut hw;
1.1 root 43: void *pcm_buf;
44: int fd;
1.1.1.7 ! root 45: int wpos;
1.1 root 46: int nfrags;
47: int fragsize;
48: int mmapped;
1.1.1.7 ! root 49: int pending;
1.1.1.2 root 50: } OSSVoiceOut;
1.1 root 51:
1.1.1.2 root 52: typedef struct OSSVoiceIn {
53: HWVoiceIn hw;
54: void *pcm_buf;
55: int fd;
56: int nfrags;
57: int fragsize;
58: } OSSVoiceIn;
1.1 root 59:
60: static struct {
61: int try_mmap;
62: int nfrags;
63: int fragsize;
1.1.1.2 root 64: const char *devpath_out;
65: const char *devpath_in;
1.1.1.3 root 66: int debug;
1.1.1.7 ! root 67: int exclusive;
! 68: int policy;
1.1 root 69: } conf = {
70: .try_mmap = 0,
71: .nfrags = 4,
72: .fragsize = 4096,
1.1.1.2 root 73: .devpath_out = "/dev/dsp",
1.1.1.3 root 74: .devpath_in = "/dev/dsp",
1.1.1.7 ! root 75: .debug = 0,
! 76: .exclusive = 0,
! 77: .policy = 5
1.1 root 78: };
79:
80: struct oss_params {
81: int freq;
82: audfmt_e fmt;
83: int nchannels;
84: int nfrags;
85: int fragsize;
86: };
87:
1.1.1.2 root 88: static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
89: {
90: va_list ap;
91:
92: va_start (ap, fmt);
93: AUD_vlog (AUDIO_CAP, fmt, ap);
94: va_end (ap);
95:
96: AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
97: }
98:
99: static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
100: int err,
101: const char *typ,
102: const char *fmt,
103: ...
104: )
105: {
106: va_list ap;
107:
108: AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
109:
110: va_start (ap, fmt);
111: AUD_vlog (AUDIO_CAP, fmt, ap);
112: va_end (ap);
113:
114: AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
115: }
116:
117: static void oss_anal_close (int *fdp)
118: {
1.1.1.7 ! root 119: int err;
! 120:
! 121: qemu_set_fd_handler (*fdp, NULL, NULL, NULL);
! 122: err = close (*fdp);
1.1.1.2 root 123: if (err) {
124: oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
125: }
126: *fdp = -1;
127: }
128:
1.1.1.7 ! root 129: static void oss_helper_poll_out (void *opaque)
! 130: {
! 131: (void) opaque;
! 132: audio_run ("oss_poll_out");
! 133: }
! 134:
! 135: static void oss_helper_poll_in (void *opaque)
! 136: {
! 137: (void) opaque;
! 138: audio_run ("oss_poll_in");
! 139: }
! 140:
! 141: static int oss_poll_out (HWVoiceOut *hw)
! 142: {
! 143: OSSVoiceOut *oss = (OSSVoiceOut *) hw;
! 144:
! 145: return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
! 146: }
! 147:
! 148: static int oss_poll_in (HWVoiceIn *hw)
! 149: {
! 150: OSSVoiceIn *oss = (OSSVoiceIn *) hw;
! 151:
! 152: return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
! 153: }
! 154:
1.1.1.2 root 155: static int oss_write (SWVoiceOut *sw, void *buf, int len)
1.1 root 156: {
1.1.1.2 root 157: return audio_pcm_sw_write (sw, buf, len);
1.1 root 158: }
159:
1.1.1.2 root 160: static int aud_to_ossfmt (audfmt_e fmt)
1.1 root 161: {
162: switch (fmt) {
1.1.1.2 root 163: case AUD_FMT_S8:
164: return AFMT_S8;
165:
166: case AUD_FMT_U8:
167: return AFMT_U8;
168:
169: case AUD_FMT_S16:
170: return AFMT_S16_LE;
171:
172: case AUD_FMT_U16:
173: return AFMT_U16_LE;
174:
1.1 root 175: default:
1.1.1.2 root 176: dolog ("Internal logic error: Bad audio format %d\n", fmt);
177: #ifdef DEBUG_AUDIO
178: abort ();
179: #endif
180: return AFMT_U8;
1.1 root 181: }
182: }
183:
1.1.1.2 root 184: static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
1.1 root 185: {
1.1.1.2 root 186: switch (ossfmt) {
187: case AFMT_S8:
1.1.1.5 root 188: *endianness = 0;
1.1.1.2 root 189: *fmt = AUD_FMT_S8;
190: break;
191:
192: case AFMT_U8:
193: *endianness = 0;
194: *fmt = AUD_FMT_U8;
195: break;
196:
197: case AFMT_S16_LE:
198: *endianness = 0;
199: *fmt = AUD_FMT_S16;
200: break;
201:
202: case AFMT_U16_LE:
203: *endianness = 0;
204: *fmt = AUD_FMT_U16;
205: break;
206:
207: case AFMT_S16_BE:
208: *endianness = 1;
209: *fmt = AUD_FMT_S16;
210: break;
211:
212: case AFMT_U16_BE:
213: *endianness = 1;
214: *fmt = AUD_FMT_U16;
215: break;
216:
1.1 root 217: default:
1.1.1.2 root 218: dolog ("Unrecognized audio format %d\n", ossfmt);
219: return -1;
1.1 root 220: }
1.1.1.2 root 221:
222: return 0;
1.1 root 223: }
224:
1.1.1.2 root 225: #if defined DEBUG_MISMATCHES || defined DEBUG
226: static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
1.1 root 227: {
228: dolog ("parameter | requested value | obtained value\n");
229: dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
1.1.1.2 root 230: dolog ("channels | %10d | %10d\n",
231: req->nchannels, obt->nchannels);
1.1 root 232: dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
233: dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags);
1.1.1.2 root 234: dolog ("fragsize | %10d | %10d\n",
235: req->fragsize, obt->fragsize);
1.1 root 236: }
237: #endif
238:
1.1.1.2 root 239: static int oss_open (int in, struct oss_params *req,
240: struct oss_params *obt, int *pfd)
1.1 root 241: {
242: int fd;
1.1.1.7 ! root 243: int version;
! 244: int oflags = conf.exclusive ? O_EXCL : 0;
1.1 root 245: audio_buf_info abinfo;
246: int fmt, freq, nchannels;
1.1.1.2 root 247: const char *dspname = in ? conf.devpath_in : conf.devpath_out;
248: const char *typ = in ? "ADC" : "DAC";
1.1 root 249:
1.1.1.7 ! root 250: /* Kludge needed to have working mmap on Linux */
! 251: oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
! 252:
! 253: fd = open (dspname, oflags | O_NONBLOCK);
1.1 root 254: if (-1 == fd) {
1.1.1.2 root 255: oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
1.1 root 256: return -1;
257: }
258:
259: freq = req->freq;
260: nchannels = req->nchannels;
261: fmt = req->fmt;
262:
263: if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
1.1.1.2 root 264: oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
1.1 root 265: goto err;
266: }
267:
268: if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
1.1.1.2 root 269: oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
270: req->nchannels);
1.1 root 271: goto err;
272: }
273:
274: if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
1.1.1.2 root 275: oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
1.1 root 276: goto err;
277: }
278:
1.1.1.5 root 279: if (ioctl (fd, SNDCTL_DSP_NONBLOCK, NULL)) {
1.1.1.2 root 280: oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n");
1.1 root 281: goto err;
282: }
283:
1.1.1.7 ! root 284: if (ioctl (fd, OSS_GETVERSION, &version)) {
! 285: oss_logerr2 (errno, typ, "Failed to get OSS version\n");
! 286: version = 0;
! 287: }
! 288:
! 289: if (conf.debug) {
! 290: dolog ("OSS version = %#x\n", version);
! 291: }
! 292:
! 293: #ifdef SNDCTL_DSP_POLICY
! 294: if (conf.policy >= 0 && version >= 0x040000) {
! 295: int policy = conf.policy;
! 296: if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
! 297: oss_logerr2 (errno, typ, "Failed to set timing policy to %d\n",
! 298: conf.policy);
! 299: goto err;
! 300: }
! 301: }
! 302: else
! 303: #endif
! 304: {
! 305: int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize);
! 306: if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
! 307: oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
! 308: req->nfrags, req->fragsize);
! 309: goto err;
! 310: }
1.1 root 311: }
312:
1.1.1.2 root 313: if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
314: oss_logerr2 (errno, typ, "Failed to get buffer length\n");
1.1 root 315: goto err;
316: }
317:
1.1.1.5 root 318: if (!abinfo.fragstotal || !abinfo.fragsize) {
319: AUD_log (AUDIO_CAP, "Returned bogus buffer information(%d, %d) for %s\n",
320: abinfo.fragstotal, abinfo.fragsize, typ);
321: goto err;
322: }
323:
1.1 root 324: obt->fmt = fmt;
325: obt->nchannels = nchannels;
326: obt->freq = freq;
327: obt->nfrags = abinfo.fragstotal;
328: obt->fragsize = abinfo.fragsize;
329: *pfd = fd;
330:
1.1.1.2 root 331: #ifdef DEBUG_MISMATCHES
1.1 root 332: if ((req->fmt != obt->fmt) ||
333: (req->nchannels != obt->nchannels) ||
334: (req->freq != obt->freq) ||
335: (req->fragsize != obt->fragsize) ||
336: (req->nfrags != obt->nfrags)) {
337: dolog ("Audio parameters mismatch\n");
1.1.1.2 root 338: oss_dump_info (req, obt);
1.1 root 339: }
1.1.1.2 root 340: #endif
1.1 root 341:
1.1.1.2 root 342: #ifdef DEBUG
343: oss_dump_info (req, obt);
1.1 root 344: #endif
345: return 0;
346:
1.1.1.2 root 347: err:
348: oss_anal_close (&fd);
1.1 root 349: return -1;
350: }
351:
1.1.1.7 ! root 352: static void oss_write_pending (OSSVoiceOut *oss)
! 353: {
! 354: HWVoiceOut *hw = &oss->hw;
! 355:
! 356: if (oss->mmapped) {
! 357: return;
! 358: }
! 359:
! 360: while (oss->pending) {
! 361: int samples_written;
! 362: ssize_t bytes_written;
! 363: int samples_till_end = hw->samples - oss->wpos;
! 364: int samples_to_write = audio_MIN (oss->pending, samples_till_end);
! 365: int bytes_to_write = samples_to_write << hw->info.shift;
! 366: void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
! 367:
! 368: bytes_written = write (oss->fd, pcm, bytes_to_write);
! 369: if (bytes_written < 0) {
! 370: if (errno != EAGAIN) {
! 371: oss_logerr (errno, "failed to write %d bytes\n",
! 372: bytes_to_write);
! 373: }
! 374: break;
! 375: }
! 376:
! 377: if (bytes_written & hw->info.align) {
! 378: dolog ("misaligned write asked for %d, but got %zd\n",
! 379: bytes_to_write, bytes_written);
! 380: return;
! 381: }
! 382:
! 383: samples_written = bytes_written >> hw->info.shift;
! 384: oss->pending -= samples_written;
! 385: oss->wpos = (oss->wpos + samples_written) % hw->samples;
! 386: if (bytes_written - bytes_to_write) {
! 387: break;
! 388: }
! 389: }
! 390: }
! 391:
! 392: static int oss_run_out (HWVoiceOut *hw, int live)
1.1 root 393: {
1.1.1.2 root 394: OSSVoiceOut *oss = (OSSVoiceOut *) hw;
1.1.1.7 ! root 395: int err, decr;
1.1 root 396: struct audio_buf_info abinfo;
397: struct count_info cntinfo;
1.1.1.2 root 398: int bufsize;
399:
400: bufsize = hw->samples << hw->info.shift;
1.1 root 401:
402: if (oss->mmapped) {
1.1.1.7 ! root 403: int bytes, pos;
1.1 root 404:
405: err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
406: if (err < 0) {
1.1.1.2 root 407: oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
408: return 0;
1.1 root 409: }
410:
1.1.1.7 ! root 411: pos = hw->rpos << hw->info.shift;
! 412: bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
1.1.1.2 root 413: decr = audio_MIN (bytes >> hw->info.shift, live);
1.1 root 414: }
415: else {
416: err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
417: if (err < 0) {
1.1.1.2 root 418: oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
419: return 0;
420: }
421:
1.1.1.3 root 422: if (abinfo.bytes > bufsize) {
423: if (conf.debug) {
424: dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
1.1.1.7 ! root 425: "please report your OS/audio hw to [email protected]\n",
1.1.1.3 root 426: abinfo.bytes, bufsize);
427: }
428: abinfo.bytes = bufsize;
429: }
430:
431: if (abinfo.bytes < 0) {
432: if (conf.debug) {
433: dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
434: abinfo.bytes, bufsize);
435: }
1.1.1.2 root 436: return 0;
1.1 root 437: }
438:
1.1.1.2 root 439: decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
440: if (!decr) {
441: return 0;
442: }
1.1 root 443: }
444:
1.1.1.7 ! root 445: decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
! 446: oss->pending += decr;
! 447: oss_write_pending (oss);
1.1 root 448:
1.1.1.2 root 449: return decr;
1.1 root 450: }
451:
1.1.1.2 root 452: static void oss_fini_out (HWVoiceOut *hw)
1.1 root 453: {
454: int err;
1.1.1.2 root 455: OSSVoiceOut *oss = (OSSVoiceOut *) hw;
1.1 root 456:
1.1.1.2 root 457: ldebug ("oss_fini\n");
458: oss_anal_close (&oss->fd);
1.1 root 459:
460: if (oss->pcm_buf) {
461: if (oss->mmapped) {
1.1.1.2 root 462: err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
1.1 root 463: if (err) {
1.1.1.2 root 464: oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
465: oss->pcm_buf, hw->samples << hw->info.shift);
1.1 root 466: }
467: }
468: else {
469: qemu_free (oss->pcm_buf);
470: }
471: oss->pcm_buf = NULL;
472: }
473: }
474:
1.1.1.5 root 475: static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
1.1 root 476: {
1.1.1.2 root 477: OSSVoiceOut *oss = (OSSVoiceOut *) hw;
1.1 root 478: struct oss_params req, obt;
1.1.1.2 root 479: int endianness;
480: int err;
481: int fd;
482: audfmt_e effective_fmt;
1.1.1.5 root 483: struct audsettings obt_as;
1.1.1.2 root 484:
485: oss->fd = -1;
1.1 root 486:
1.1.1.2 root 487: req.fmt = aud_to_ossfmt (as->fmt);
488: req.freq = as->freq;
489: req.nchannels = as->nchannels;
1.1 root 490: req.fragsize = conf.fragsize;
491: req.nfrags = conf.nfrags;
492:
1.1.1.2 root 493: if (oss_open (0, &req, &obt, &fd)) {
1.1 root 494: return -1;
1.1.1.2 root 495: }
1.1 root 496:
1.1.1.2 root 497: err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
498: if (err) {
499: oss_anal_close (&fd);
500: return -1;
501: }
1.1 root 502:
1.1.1.2 root 503: obt_as.freq = obt.freq;
504: obt_as.nchannels = obt.nchannels;
505: obt_as.fmt = effective_fmt;
1.1.1.3 root 506: obt_as.endianness = endianness;
1.1.1.2 root 507:
1.1.1.3 root 508: audio_pcm_init_info (&hw->info, &obt_as);
1.1 root 509: oss->nfrags = obt.nfrags;
510: oss->fragsize = obt.fragsize;
1.1.1.2 root 511:
512: if (obt.nfrags * obt.fragsize & hw->info.align) {
513: dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
514: obt.nfrags * obt.fragsize, hw->info.align + 1);
515: }
516:
517: hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
1.1 root 518:
519: oss->mmapped = 0;
520: if (conf.try_mmap) {
1.1.1.2 root 521: oss->pcm_buf = mmap (
1.1.1.6 root 522: NULL,
1.1.1.2 root 523: hw->samples << hw->info.shift,
524: PROT_READ | PROT_WRITE,
525: MAP_SHARED,
526: fd,
527: 0
528: );
1.1 root 529: if (oss->pcm_buf == MAP_FAILED) {
1.1.1.2 root 530: oss_logerr (errno, "Failed to map %d bytes of DAC\n",
531: hw->samples << hw->info.shift);
1.1.1.7 ! root 532: }
! 533: else {
1.1 root 534: int err;
535: int trig = 0;
1.1.1.2 root 536: if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
537: oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
1.1 root 538: }
539: else {
540: trig = PCM_ENABLE_OUTPUT;
1.1.1.2 root 541: if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
542: oss_logerr (
543: errno,
544: "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
545: );
1.1 root 546: }
547: else {
548: oss->mmapped = 1;
549: }
550: }
551:
552: if (!oss->mmapped) {
1.1.1.2 root 553: err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
1.1 root 554: if (err) {
1.1.1.2 root 555: oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
556: oss->pcm_buf, hw->samples << hw->info.shift);
1.1 root 557: }
558: }
559: }
560: }
561:
562: if (!oss->mmapped) {
1.1.1.2 root 563: oss->pcm_buf = audio_calloc (
564: AUDIO_FUNC,
565: hw->samples,
566: 1 << hw->info.shift
567: );
1.1 root 568: if (!oss->pcm_buf) {
1.1.1.2 root 569: dolog (
570: "Could not allocate DAC buffer (%d samples, each %d bytes)\n",
571: hw->samples,
572: 1 << hw->info.shift
573: );
574: oss_anal_close (&fd);
1.1 root 575: return -1;
576: }
577: }
578:
1.1.1.2 root 579: oss->fd = fd;
1.1 root 580: return 0;
581: }
582:
1.1.1.2 root 583: static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
1.1 root 584: {
585: int trig;
1.1.1.2 root 586: OSSVoiceOut *oss = (OSSVoiceOut *) hw;
1.1 root 587:
588: switch (cmd) {
589: case VOICE_ENABLE:
1.1.1.7 ! root 590: {
! 591: va_list ap;
! 592: int poll_mode;
! 593:
! 594: va_start (ap, cmd);
! 595: poll_mode = va_arg (ap, int);
! 596: va_end (ap);
! 597:
! 598: ldebug ("enabling voice\n");
! 599: if (poll_mode && oss_poll_out (hw)) {
! 600: poll_mode = 0;
! 601: }
! 602: hw->poll_mode = poll_mode;
! 603:
! 604: if (!oss->mmapped) {
! 605: return 0;
! 606: }
! 607:
! 608: audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
! 609: trig = PCM_ENABLE_OUTPUT;
! 610: if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
! 611: oss_logerr (
! 612: errno,
! 613: "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
! 614: );
! 615: return -1;
! 616: }
1.1 root 617: }
618: break;
619:
620: case VOICE_DISABLE:
1.1.1.7 ! root 621: if (hw->poll_mode) {
! 622: qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
! 623: hw->poll_mode = 0;
! 624: }
! 625:
! 626: if (!oss->mmapped) {
! 627: return 0;
! 628: }
! 629:
1.1 root 630: ldebug ("disabling voice\n");
631: trig = 0;
632: if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
1.1.1.2 root 633: oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
1.1 root 634: return -1;
635: }
636: break;
637: }
638: return 0;
639: }
640:
1.1.1.5 root 641: static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
1.1.1.2 root 642: {
643: OSSVoiceIn *oss = (OSSVoiceIn *) hw;
644: struct oss_params req, obt;
645: int endianness;
646: int err;
647: int fd;
648: audfmt_e effective_fmt;
1.1.1.5 root 649: struct audsettings obt_as;
1.1.1.2 root 650:
651: oss->fd = -1;
652:
653: req.fmt = aud_to_ossfmt (as->fmt);
654: req.freq = as->freq;
655: req.nchannels = as->nchannels;
656: req.fragsize = conf.fragsize;
657: req.nfrags = conf.nfrags;
658: if (oss_open (1, &req, &obt, &fd)) {
659: return -1;
660: }
661:
662: err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
663: if (err) {
664: oss_anal_close (&fd);
665: return -1;
666: }
667:
668: obt_as.freq = obt.freq;
669: obt_as.nchannels = obt.nchannels;
670: obt_as.fmt = effective_fmt;
1.1.1.3 root 671: obt_as.endianness = endianness;
1.1.1.2 root 672:
1.1.1.3 root 673: audio_pcm_init_info (&hw->info, &obt_as);
1.1.1.2 root 674: oss->nfrags = obt.nfrags;
675: oss->fragsize = obt.fragsize;
676:
677: if (obt.nfrags * obt.fragsize & hw->info.align) {
678: dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
679: obt.nfrags * obt.fragsize, hw->info.align + 1);
680: }
681:
682: hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
683: oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
684: if (!oss->pcm_buf) {
685: dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
686: hw->samples, 1 << hw->info.shift);
687: oss_anal_close (&fd);
688: return -1;
689: }
690:
691: oss->fd = fd;
692: return 0;
693: }
694:
695: static void oss_fini_in (HWVoiceIn *hw)
696: {
697: OSSVoiceIn *oss = (OSSVoiceIn *) hw;
698:
699: oss_anal_close (&oss->fd);
700:
701: if (oss->pcm_buf) {
702: qemu_free (oss->pcm_buf);
703: oss->pcm_buf = NULL;
704: }
705: }
706:
707: static int oss_run_in (HWVoiceIn *hw)
708: {
709: OSSVoiceIn *oss = (OSSVoiceIn *) hw;
710: int hwshift = hw->info.shift;
711: int i;
712: int live = audio_pcm_hw_get_live_in (hw);
713: int dead = hw->samples - live;
714: size_t read_samples = 0;
715: struct {
716: int add;
717: int len;
718: } bufs[2] = {
1.1.1.7 ! root 719: { .add = hw->wpos, .len = 0 },
! 720: { .add = 0, .len = 0 }
1.1.1.2 root 721: };
722:
723: if (!dead) {
724: return 0;
725: }
726:
727: if (hw->wpos + dead > hw->samples) {
728: bufs[0].len = (hw->samples - hw->wpos) << hwshift;
729: bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
730: }
731: else {
732: bufs[0].len = dead << hwshift;
733: }
734:
735: for (i = 0; i < 2; ++i) {
736: ssize_t nread;
737:
738: if (bufs[i].len) {
739: void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
740: nread = read (oss->fd, p, bufs[i].len);
741:
742: if (nread > 0) {
743: if (nread & hw->info.align) {
744: dolog ("warning: Misaligned read %zd (requested %d), "
745: "alignment %d\n", nread, bufs[i].add << hwshift,
746: hw->info.align + 1);
747: }
748: read_samples += nread >> hwshift;
749: hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
750: &nominal_volume);
751: }
752:
753: if (bufs[i].len - nread) {
754: if (nread == -1) {
755: switch (errno) {
756: case EINTR:
757: case EAGAIN:
758: break;
759: default:
760: oss_logerr (
761: errno,
762: "Failed to read %d bytes of audio (to %p)\n",
763: bufs[i].len, p
764: );
765: break;
766: }
767: }
768: break;
769: }
770: }
771: }
772:
773: hw->wpos = (hw->wpos + read_samples) % hw->samples;
774: return read_samples;
775: }
776:
777: static int oss_read (SWVoiceIn *sw, void *buf, int size)
778: {
779: return audio_pcm_sw_read (sw, buf, size);
780: }
781:
782: static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
783: {
1.1.1.7 ! root 784: OSSVoiceIn *oss = (OSSVoiceIn *) hw;
! 785:
! 786: switch (cmd) {
! 787: case VOICE_ENABLE:
! 788: {
! 789: va_list ap;
! 790: int poll_mode;
! 791:
! 792: va_start (ap, cmd);
! 793: poll_mode = va_arg (ap, int);
! 794: va_end (ap);
! 795:
! 796: if (poll_mode && oss_poll_in (hw)) {
! 797: poll_mode = 0;
! 798: }
! 799: hw->poll_mode = poll_mode;
! 800: }
! 801: break;
! 802:
! 803: case VOICE_DISABLE:
! 804: if (hw->poll_mode) {
! 805: hw->poll_mode = 0;
! 806: qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
! 807: }
! 808: break;
! 809: }
1.1.1.2 root 810: return 0;
811: }
812:
1.1 root 813: static void *oss_audio_init (void)
814: {
815: return &conf;
816: }
817:
818: static void oss_audio_fini (void *opaque)
819: {
1.1.1.2 root 820: (void) opaque;
1.1 root 821: }
822:
1.1.1.2 root 823: static struct audio_option oss_options[] = {
1.1.1.7 ! root 824: {
! 825: .name = "FRAGSIZE",
! 826: .tag = AUD_OPT_INT,
! 827: .valp = &conf.fragsize,
! 828: .descr = "Fragment size in bytes"
! 829: },
! 830: {
! 831: .name = "NFRAGS",
! 832: .tag = AUD_OPT_INT,
! 833: .valp = &conf.nfrags,
! 834: .descr = "Number of fragments"
! 835: },
! 836: {
! 837: .name = "MMAP",
! 838: .tag = AUD_OPT_BOOL,
! 839: .valp = &conf.try_mmap,
! 840: .descr = "Try using memory mapped access"
! 841: },
! 842: {
! 843: .name = "DAC_DEV",
! 844: .tag = AUD_OPT_STR,
! 845: .valp = &conf.devpath_out,
! 846: .descr = "Path to DAC device"
! 847: },
! 848: {
! 849: .name = "ADC_DEV",
! 850: .tag = AUD_OPT_STR,
! 851: .valp = &conf.devpath_in,
! 852: .descr = "Path to ADC device"
! 853: },
! 854: {
! 855: .name = "EXCLUSIVE",
! 856: .tag = AUD_OPT_BOOL,
! 857: .valp = &conf.exclusive,
! 858: .descr = "Open device in exclusive mode (vmix wont work)"
! 859: },
! 860: #ifdef SNDCTL_DSP_POLICY
! 861: {
! 862: .name = "POLICY",
! 863: .tag = AUD_OPT_INT,
! 864: .valp = &conf.policy,
! 865: .descr = "Set the timing policy of the device, -1 to use fragment mode",
! 866: },
! 867: #endif
! 868: {
! 869: .name = "DEBUG",
! 870: .tag = AUD_OPT_BOOL,
! 871: .valp = &conf.debug,
! 872: .descr = "Turn on some debugging messages"
! 873: },
! 874: { /* End of list */ }
1.1.1.2 root 875: };
876:
877: static struct audio_pcm_ops oss_pcm_ops = {
1.1.1.7 ! root 878: .init_out = oss_init_out,
! 879: .fini_out = oss_fini_out,
! 880: .run_out = oss_run_out,
! 881: .write = oss_write,
! 882: .ctl_out = oss_ctl_out,
! 883:
! 884: .init_in = oss_init_in,
! 885: .fini_in = oss_fini_in,
! 886: .run_in = oss_run_in,
! 887: .read = oss_read,
! 888: .ctl_in = oss_ctl_in
1.1 root 889: };
890:
1.1.1.2 root 891: struct audio_driver oss_audio_driver = {
1.1.1.7 ! root 892: .name = "oss",
! 893: .descr = "OSS http://www.opensound.com",
! 894: .options = oss_options,
! 895: .init = oss_audio_init,
! 896: .fini = oss_audio_fini,
! 897: .pcm_ops = &oss_pcm_ops,
! 898: .can_be_default = 1,
! 899: .max_voices_out = INT_MAX,
! 900: .max_voices_in = INT_MAX,
! 901: .voice_size_out = sizeof (OSSVoiceOut),
! 902: .voice_size_in = sizeof (OSSVoiceIn)
1.1 root 903: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.