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