1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
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:
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