Home | History | Annotate | Download | only in audio
      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, NULL)) {
    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     if (!abinfo.fragstotal || !abinfo.fragsize) {
    258         AUD_log (AUDIO_CAP, "Returned bogus buffer information(%d, %d) for %s\n",
    259                  abinfo.fragstotal, abinfo.fragsize, typ);
    260         goto err;
    261     }
    262 
    263     obt->fmt = fmt;
    264     obt->nchannels = nchannels;
    265     obt->freq = freq;
    266     obt->nfrags = abinfo.fragstotal;
    267     obt->fragsize = abinfo.fragsize;
    268     *pfd = fd;
    269 
    270 #ifdef DEBUG_MISMATCHES
    271     if ((req->fmt != obt->fmt) ||
    272         (req->nchannels != obt->nchannels) ||
    273         (req->freq != obt->freq) ||
    274         (req->fragsize != obt->fragsize) ||
    275         (req->nfrags != obt->nfrags)) {
    276         dolog ("Audio parameters mismatch\n");
    277         oss_dump_info (req, obt);
    278     }
    279 #endif
    280 
    281 #ifdef DEBUG
    282     oss_dump_info (req, obt);
    283 #endif
    284     return 0;
    285 
    286  err:
    287     oss_anal_close (&fd);
    288     return -1;
    289 }
    290 
    291 static int oss_run_out (HWVoiceOut *hw)
    292 {
    293     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
    294     int err, rpos, live, decr;
    295     int samples;
    296     uint8_t *dst;
    297     struct st_sample *src;
    298     struct audio_buf_info abinfo;
    299     struct count_info cntinfo;
    300     int bufsize;
    301 
    302     live = audio_pcm_hw_get_live_out (hw);
    303     if (!live) {
    304         return 0;
    305     }
    306 
    307     bufsize = hw->samples << hw->info.shift;
    308 
    309     if (oss->mmapped) {
    310         int bytes;
    311 
    312         err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
    313         if (err < 0) {
    314             oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
    315             return 0;
    316         }
    317 
    318         if (cntinfo.ptr == oss->old_optr) {
    319             if (abs (hw->samples - live) < 64) {
    320                 dolog ("warning: Overrun\n");
    321             }
    322             return 0;
    323         }
    324 
    325         if (cntinfo.ptr > oss->old_optr) {
    326             bytes = cntinfo.ptr - oss->old_optr;
    327         }
    328         else {
    329             bytes = bufsize + cntinfo.ptr - oss->old_optr;
    330         }
    331 
    332         decr = audio_MIN (bytes >> hw->info.shift, live);
    333     }
    334     else {
    335         err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
    336         if (err < 0) {
    337             oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
    338             return 0;
    339         }
    340 
    341         if (abinfo.bytes > bufsize) {
    342             if (conf.debug) {
    343                 dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
    344                        "please report your OS/audio hw to malc (at) pulsesoft.com\n",
    345                        abinfo.bytes, bufsize);
    346             }
    347             abinfo.bytes = bufsize;
    348         }
    349 
    350         if (abinfo.bytes < 0) {
    351             if (conf.debug) {
    352                 dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
    353                        abinfo.bytes, bufsize);
    354             }
    355             return 0;
    356         }
    357 
    358         decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
    359         if (!decr) {
    360             return 0;
    361         }
    362     }
    363 
    364     samples = decr;
    365     rpos = hw->rpos;
    366     while (samples) {
    367         int left_till_end_samples = hw->samples - rpos;
    368         int convert_samples = audio_MIN (samples, left_till_end_samples);
    369 
    370         src = hw->mix_buf + rpos;
    371         dst = advance (oss->pcm_buf, rpos << hw->info.shift);
    372 
    373         hw->clip (dst, src, convert_samples);
    374         if (!oss->mmapped) {
    375             int written;
    376 
    377             written = write (oss->fd, dst, convert_samples << hw->info.shift);
    378             /* XXX: follow errno recommendations ? */
    379             if (written == -1) {
    380                 oss_logerr (
    381                     errno,
    382                     "Failed to write %d bytes of audio data from %p\n",
    383                     convert_samples << hw->info.shift,
    384                     dst
    385                     );
    386                 continue;
    387             }
    388 
    389             if (written != convert_samples << hw->info.shift) {
    390                 int wsamples = written >> hw->info.shift;
    391                 int wbytes = wsamples << hw->info.shift;
    392                 if (wbytes != written) {
    393                     dolog ("warning: Misaligned write %d (requested %d), "
    394                            "alignment %d\n",
    395                            wbytes, written, hw->info.align + 1);
    396                 }
    397                 decr -= wsamples;
    398                 rpos = (rpos + wsamples) % hw->samples;
    399                 break;
    400             }
    401         }
    402 
    403         rpos = (rpos + convert_samples) % hw->samples;
    404         samples -= convert_samples;
    405     }
    406     if (oss->mmapped) {
    407         oss->old_optr = cntinfo.ptr;
    408     }
    409 
    410     hw->rpos = rpos;
    411     return decr;
    412 }
    413 
    414 static void oss_fini_out (HWVoiceOut *hw)
    415 {
    416     int err;
    417     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
    418 
    419     ldebug ("oss_fini\n");
    420     oss_anal_close (&oss->fd);
    421 
    422     if (oss->pcm_buf) {
    423         if (oss->mmapped) {
    424             err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
    425             if (err) {
    426                 oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
    427                             oss->pcm_buf, hw->samples << hw->info.shift);
    428             }
    429         }
    430         else {
    431             qemu_free (oss->pcm_buf);
    432         }
    433         oss->pcm_buf = NULL;
    434     }
    435 }
    436 
    437 static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
    438 {
    439     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
    440     struct oss_params req, obt;
    441     int endianness;
    442     int err;
    443     int fd;
    444     audfmt_e effective_fmt;
    445     struct audsettings obt_as;
    446 
    447     oss->fd = -1;
    448 
    449     req.fmt = aud_to_ossfmt (as->fmt);
    450     req.freq = as->freq;
    451     req.nchannels = as->nchannels;
    452     req.fragsize = conf.fragsize;
    453     req.nfrags = conf.nfrags;
    454 
    455     if (oss_open (0, &req, &obt, &fd)) {
    456         return -1;
    457     }
    458 
    459     err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
    460     if (err) {
    461         oss_anal_close (&fd);
    462         return -1;
    463     }
    464 
    465     obt_as.freq = obt.freq;
    466     obt_as.nchannels = obt.nchannels;
    467     obt_as.fmt = effective_fmt;
    468     obt_as.endianness = endianness;
    469 
    470     audio_pcm_init_info (&hw->info, &obt_as);
    471     oss->nfrags = obt.nfrags;
    472     oss->fragsize = obt.fragsize;
    473 
    474     if (obt.nfrags * obt.fragsize & hw->info.align) {
    475         dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
    476                obt.nfrags * obt.fragsize, hw->info.align + 1);
    477     }
    478 
    479     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
    480 
    481     oss->mmapped = 0;
    482     if (conf.try_mmap) {
    483         oss->pcm_buf = mmap (
    484             0,
    485             hw->samples << hw->info.shift,
    486             PROT_READ | PROT_WRITE,
    487             MAP_SHARED,
    488             fd,
    489             0
    490             );
    491         if (oss->pcm_buf == MAP_FAILED) {
    492             oss_logerr (errno, "Failed to map %d bytes of DAC\n",
    493                         hw->samples << hw->info.shift);
    494         } else {
    495             int err;
    496             int trig = 0;
    497             if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
    498                 oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
    499             }
    500             else {
    501                 trig = PCM_ENABLE_OUTPUT;
    502                 if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
    503                     oss_logerr (
    504                         errno,
    505                         "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
    506                         );
    507                 }
    508                 else {
    509                     oss->mmapped = 1;
    510                 }
    511             }
    512 
    513             if (!oss->mmapped) {
    514                 err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
    515                 if (err) {
    516                     oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
    517                                 oss->pcm_buf, hw->samples << hw->info.shift);
    518                 }
    519             }
    520         }
    521     }
    522 
    523     if (!oss->mmapped) {
    524         oss->pcm_buf = audio_calloc (
    525             AUDIO_FUNC,
    526             hw->samples,
    527             1 << hw->info.shift
    528             );
    529         if (!oss->pcm_buf) {
    530             dolog (
    531                 "Could not allocate DAC buffer (%d samples, each %d bytes)\n",
    532                 hw->samples,
    533                 1 << hw->info.shift
    534                 );
    535             oss_anal_close (&fd);
    536             return -1;
    537         }
    538     }
    539 
    540     oss->fd = fd;
    541     return 0;
    542 }
    543 
    544 static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
    545 {
    546     int trig;
    547     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
    548 
    549     if (!oss->mmapped) {
    550         return 0;
    551     }
    552 
    553     switch (cmd) {
    554     case VOICE_ENABLE:
    555         ldebug ("enabling voice\n");
    556         audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
    557         trig = PCM_ENABLE_OUTPUT;
    558         if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
    559             oss_logerr (
    560                 errno,
    561                 "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
    562                 );
    563             return -1;
    564         }
    565         break;
    566 
    567     case VOICE_DISABLE:
    568         ldebug ("disabling voice\n");
    569         trig = 0;
    570         if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
    571             oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
    572             return -1;
    573         }
    574         break;
    575     }
    576     return 0;
    577 }
    578 
    579 static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
    580 {
    581     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
    582     struct oss_params req, obt;
    583     int endianness;
    584     int err;
    585     int fd;
    586     audfmt_e effective_fmt;
    587     struct audsettings obt_as;
    588 
    589     oss->fd = -1;
    590 
    591     req.fmt = aud_to_ossfmt (as->fmt);
    592     req.freq = as->freq;
    593     req.nchannels = as->nchannels;
    594     req.fragsize = conf.fragsize;
    595     req.nfrags = conf.nfrags;
    596     if (oss_open (1, &req, &obt, &fd)) {
    597         return -1;
    598     }
    599 
    600     err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
    601     if (err) {
    602         oss_anal_close (&fd);
    603         return -1;
    604     }
    605 
    606     obt_as.freq = obt.freq;
    607     obt_as.nchannels = obt.nchannels;
    608     obt_as.fmt = effective_fmt;
    609     obt_as.endianness = endianness;
    610 
    611     audio_pcm_init_info (&hw->info, &obt_as);
    612     oss->nfrags = obt.nfrags;
    613     oss->fragsize = obt.fragsize;
    614 
    615     if (obt.nfrags * obt.fragsize & hw->info.align) {
    616         dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
    617                obt.nfrags * obt.fragsize, hw->info.align + 1);
    618     }
    619 
    620     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
    621     oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
    622     if (!oss->pcm_buf) {
    623         dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
    624                hw->samples, 1 << hw->info.shift);
    625         oss_anal_close (&fd);
    626         return -1;
    627     }
    628 
    629     oss->fd = fd;
    630     return 0;
    631 }
    632 
    633 static void oss_fini_in (HWVoiceIn *hw)
    634 {
    635     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
    636 
    637     oss_anal_close (&oss->fd);
    638 
    639     if (oss->pcm_buf) {
    640         qemu_free (oss->pcm_buf);
    641         oss->pcm_buf = NULL;
    642     }
    643 }
    644 
    645 static int oss_run_in (HWVoiceIn *hw)
    646 {
    647     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
    648     int hwshift = hw->info.shift;
    649     int i;
    650     int live = audio_pcm_hw_get_live_in (hw);
    651     int dead = hw->samples - live;
    652     size_t read_samples = 0;
    653     struct {
    654         int add;
    655         int len;
    656     } bufs[2] = {
    657         { hw->wpos, 0 },
    658         { 0, 0 }
    659     };
    660 
    661     if (!dead) {
    662         return 0;
    663     }
    664 
    665     if (hw->wpos + dead > hw->samples) {
    666         bufs[0].len = (hw->samples - hw->wpos) << hwshift;
    667         bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
    668     }
    669     else {
    670         bufs[0].len = dead << hwshift;
    671     }
    672 
    673 
    674     for (i = 0; i < 2; ++i) {
    675         ssize_t nread;
    676 
    677         if (bufs[i].len) {
    678             void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
    679             nread = read (oss->fd, p, bufs[i].len);
    680 
    681             if (nread > 0) {
    682                 if (nread & hw->info.align) {
    683                     dolog ("warning: Misaligned read %zd (requested %d), "
    684                            "alignment %d\n", nread, bufs[i].add << hwshift,
    685                            hw->info.align + 1);
    686                 }
    687                 read_samples += nread >> hwshift;
    688                 hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
    689                           &nominal_volume);
    690             }
    691 
    692             if (bufs[i].len - nread) {
    693                 if (nread == -1) {
    694                     switch (errno) {
    695                     case EINTR:
    696                     case EAGAIN:
    697                         break;
    698                     default:
    699                         oss_logerr (
    700                             errno,
    701                             "Failed to read %d bytes of audio (to %p)\n",
    702                             bufs[i].len, p
    703                             );
    704                         break;
    705                     }
    706                 }
    707                 break;
    708             }
    709         }
    710     }
    711 
    712     hw->wpos = (hw->wpos + read_samples) % hw->samples;
    713     return read_samples;
    714 }
    715 
    716 static int oss_read (SWVoiceIn *sw, void *buf, int size)
    717 {
    718     return audio_pcm_sw_read (sw, buf, size);
    719 }
    720 
    721 static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
    722 {
    723     (void) hw;
    724     (void) cmd;
    725     return 0;
    726 }
    727 
    728 static void *oss_audio_init (void)
    729 {
    730     return &conf;
    731 }
    732 
    733 static void oss_audio_fini (void *opaque)
    734 {
    735     (void) opaque;
    736 }
    737 
    738 static struct audio_option oss_options[] = {
    739     {"FRAGSIZE", AUD_OPT_INT, &conf.fragsize,
    740      "Fragment size in bytes", NULL, 0},
    741     {"NFRAGS", AUD_OPT_INT, &conf.nfrags,
    742      "Number of fragments", NULL, 0},
    743     {"MMAP", AUD_OPT_BOOL, &conf.try_mmap,
    744      "Try using memory mapped access", NULL, 0},
    745     {"DAC_DEV", AUD_OPT_STR, &conf.devpath_out,
    746      "Path to DAC device", NULL, 0},
    747     {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
    748      "Path to ADC device", NULL, 0},
    749     {"DEBUG", AUD_OPT_BOOL, &conf.debug,
    750      "Turn on some debugging messages", NULL, 0},
    751     {NULL, 0, NULL, NULL, NULL, 0}
    752 };
    753 
    754 static struct audio_pcm_ops oss_pcm_ops = {
    755     oss_init_out,
    756     oss_fini_out,
    757     oss_run_out,
    758     oss_write,
    759     oss_ctl_out,
    760 
    761     oss_init_in,
    762     oss_fini_in,
    763     oss_run_in,
    764     oss_read,
    765     oss_ctl_in
    766 };
    767 
    768 struct audio_driver oss_audio_driver = {
    769     INIT_FIELD (name           = ) "oss",
    770     INIT_FIELD (descr          = ) "OSS audio (www.opensound.com)",
    771     INIT_FIELD (options        = ) oss_options,
    772     INIT_FIELD (init           = ) oss_audio_init,
    773     INIT_FIELD (fini           = ) oss_audio_fini,
    774     INIT_FIELD (pcm_ops        = ) &oss_pcm_ops,
    775     INIT_FIELD (can_be_default = ) 1,
    776     INIT_FIELD (max_voices_out = ) INT_MAX,
    777     INIT_FIELD (max_voices_in  = ) INT_MAX,
    778     INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut),
    779     INIT_FIELD (voice_size_in  = ) sizeof (OSSVoiceIn)
    780 };
    781