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