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