Break up vl.h.
[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 "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             /* XXX: follow errno recommendations ? */
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 = conf.fragsize;
589     req.nfrags = conf.nfrags;
590     if (oss_open (1, &req, &obt, &fd)) {
591         return -1;
592     }
593
594     err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
595     if (err) {
596         oss_anal_close (&fd);
597         return -1;
598     }
599
600     obt_as.freq = obt.freq;
601     obt_as.nchannels = obt.nchannels;
602     obt_as.fmt = effective_fmt;
603     obt_as.endianness = endianness;
604
605     audio_pcm_init_info (&hw->info, &obt_as);
606     oss->nfrags = obt.nfrags;
607     oss->fragsize = obt.fragsize;
608
609     if (obt.nfrags * obt.fragsize & hw->info.align) {
610         dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
611                obt.nfrags * obt.fragsize, hw->info.align + 1);
612     }
613
614     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
615     oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
616     if (!oss->pcm_buf) {
617         dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
618                hw->samples, 1 << hw->info.shift);
619         oss_anal_close (&fd);
620         return -1;
621     }
622
623     oss->fd = fd;
624     return 0;
625 }
626
627 static void oss_fini_in (HWVoiceIn *hw)
628 {
629     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
630
631     oss_anal_close (&oss->fd);
632
633     if (oss->pcm_buf) {
634         qemu_free (oss->pcm_buf);
635         oss->pcm_buf = NULL;
636     }
637 }
638
639 static int oss_run_in (HWVoiceIn *hw)
640 {
641     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
642     int hwshift = hw->info.shift;
643     int i;
644     int live = audio_pcm_hw_get_live_in (hw);
645     int dead = hw->samples - live;
646     size_t read_samples = 0;
647     struct {
648         int add;
649         int len;
650     } bufs[2] = {
651         { hw->wpos, 0 },
652         { 0, 0 }
653     };
654
655     if (!dead) {
656         return 0;
657     }
658
659     if (hw->wpos + dead > hw->samples) {
660         bufs[0].len = (hw->samples - hw->wpos) << hwshift;
661         bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
662     }
663     else {
664         bufs[0].len = dead << hwshift;
665     }
666
667
668     for (i = 0; i < 2; ++i) {
669         ssize_t nread;
670
671         if (bufs[i].len) {
672             void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
673             nread = read (oss->fd, p, bufs[i].len);
674
675             if (nread > 0) {
676                 if (nread & hw->info.align) {
677                     dolog ("warning: Misaligned read %zd (requested %d), "
678                            "alignment %d\n", nread, bufs[i].add << hwshift,
679                            hw->info.align + 1);
680                 }
681                 read_samples += nread >> hwshift;
682                 hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
683                           &nominal_volume);
684             }
685
686             if (bufs[i].len - nread) {
687                 if (nread == -1) {
688                     switch (errno) {
689                     case EINTR:
690                     case EAGAIN:
691                         break;
692                     default:
693                         oss_logerr (
694                             errno,
695                             "Failed to read %d bytes of audio (to %p)\n",
696                             bufs[i].len, p
697                             );
698                         break;
699                     }
700                 }
701                 break;
702             }
703         }
704     }
705
706     hw->wpos = (hw->wpos + read_samples) % hw->samples;
707     return read_samples;
708 }
709
710 static int oss_read (SWVoiceIn *sw, void *buf, int size)
711 {
712     return audio_pcm_sw_read (sw, buf, size);
713 }
714
715 static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
716 {
717     (void) hw;
718     (void) cmd;
719     return 0;
720 }
721
722 static void *oss_audio_init (void)
723 {
724     return &conf;
725 }
726
727 static void oss_audio_fini (void *opaque)
728 {
729     (void) opaque;
730 }
731
732 static struct audio_option oss_options[] = {
733     {"FRAGSIZE", AUD_OPT_INT, &conf.fragsize,
734      "Fragment size in bytes", NULL, 0},
735     {"NFRAGS", AUD_OPT_INT, &conf.nfrags,
736      "Number of fragments", NULL, 0},
737     {"MMAP", AUD_OPT_BOOL, &conf.try_mmap,
738      "Try using memory mapped access", NULL, 0},
739     {"DAC_DEV", AUD_OPT_STR, &conf.devpath_out,
740      "Path to DAC device", NULL, 0},
741     {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
742      "Path to ADC device", NULL, 0},
743     {"DEBUG", AUD_OPT_BOOL, &conf.debug,
744      "Turn on some debugging messages", NULL, 0},
745     {NULL, 0, NULL, NULL, NULL, 0}
746 };
747
748 static struct audio_pcm_ops oss_pcm_ops = {
749     oss_init_out,
750     oss_fini_out,
751     oss_run_out,
752     oss_write,
753     oss_ctl_out,
754
755     oss_init_in,
756     oss_fini_in,
757     oss_run_in,
758     oss_read,
759     oss_ctl_in
760 };
761
762 struct audio_driver oss_audio_driver = {
763     INIT_FIELD (name           = ) "oss",
764     INIT_FIELD (descr          = ) "OSS http://www.opensound.com",
765     INIT_FIELD (options        = ) oss_options,
766     INIT_FIELD (init           = ) oss_audio_init,
767     INIT_FIELD (fini           = ) oss_audio_fini,
768     INIT_FIELD (pcm_ops        = ) &oss_pcm_ops,
769     INIT_FIELD (can_be_default = ) 1,
770     INIT_FIELD (max_voices_out = ) INT_MAX,
771     INIT_FIELD (max_voices_in  = ) INT_MAX,
772     INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut),
773     INIT_FIELD (voice_size_in  = ) sizeof (OSSVoiceIn)
774 };