Annotation of qemu/audio/ossaudio.c, revision 1.1.1.8

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.