"Fossies" - the Fresh Open Source Software Archive

Member "alsa-oss-1.1.8/alsa/pcm.c" (7 Jan 2019, 43375 Bytes) of package /linux/misc/alsa-oss-1.1.8.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "pcm.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.0.28_vs_1.1.6.

    1 /*
    2  *  OSS -> ALSA compatibility layer
    3  *  Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>,
    4  *           Jaroslav Kysela <perex@perex.cz>
    5  *
    6  *
    7  *   This program is free software; you can redistribute it and/or modify
    8  *   it under the terms of the GNU General Public License as published by
    9  *   the Free Software Foundation; either version 2 of the License, or
   10  *   (at your option) any later version.
   11  *
   12  *   This program is distributed in the hope that it will be useful,
   13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15  *   GNU General Public License for more details.
   16  *
   17  *   You should have received a copy of the GNU General Public License
   18  *   along with this program; if not, write to the Free Software
   19  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   20  *
   21  */
   22 
   23 #define _GNU_SOURCE
   24 
   25 #include <sys/types.h>
   26 #include <sys/time.h>
   27 #include <sys/stat.h>
   28 #include <sys/poll.h>
   29 #include <sys/select.h>
   30 #include <sys/mman.h>
   31 #include <stdarg.h>
   32 #include <unistd.h>
   33 #include <dlfcn.h>
   34 #include <stdio.h>
   35 #include <fcntl.h>
   36 #include <limits.h>
   37 #include <errno.h>
   38 #include <assert.h>
   39 #include <linux/soundcard.h>
   40 #include <alsa/asoundlib.h>
   41 
   42 #include "alsa-local.h"
   43 
   44 int alsa_oss_debug = 0;
   45 snd_output_t *alsa_oss_debug_out = NULL;
   46 
   47 typedef struct {
   48     snd_pcm_t *pcm;
   49     snd_pcm_sw_params_t *sw_params;
   50     size_t frame_bytes;
   51     struct {
   52         snd_pcm_uframes_t period_size;
   53         snd_pcm_uframes_t buffer_size;
   54         snd_pcm_uframes_t boundary;
   55         snd_pcm_uframes_t appl_ptr;
   56         snd_pcm_uframes_t old_hw_ptr;
   57         size_t mmap_buffer_bytes;
   58         size_t mmap_period_bytes;
   59     } alsa;
   60     struct {
   61         snd_pcm_uframes_t period_size;
   62         unsigned int periods;
   63         snd_pcm_uframes_t buffer_size;
   64         size_t bytes;
   65         size_t hw_bytes;
   66         size_t boundary;
   67     } oss;
   68     unsigned int stopped:1;
   69     void *mmap_buffer;
   70     size_t mmap_bytes;
   71     snd_pcm_channel_area_t *mmap_areas;
   72     snd_pcm_uframes_t mmap_advance;
   73 } oss_dsp_stream_t;
   74 
   75 typedef struct {
   76     int hwset;
   77     unsigned int channels;
   78     unsigned int rate;
   79     unsigned int oss_format;
   80     snd_pcm_format_t format;
   81     unsigned int fragshift;
   82     unsigned int maxfrags;
   83     unsigned int subdivision;
   84     oss_dsp_stream_t streams[2];
   85 } oss_dsp_t;
   86 
   87 typedef struct fd {
   88     int fileno;
   89     oss_dsp_t *dsp;
   90     void *mmap_area;
   91     struct fd *next;
   92 } fd_t;
   93 
   94 static fd_t *pcm_fds = NULL;
   95 
   96 
   97 static fd_t *look_for_fd(int fd)
   98 {
   99     fd_t *result = pcm_fds;
  100     while (result) {
  101         if (result->fileno == fd)
  102             return result;
  103         result = result->next;
  104     }
  105     return NULL;
  106 }
  107 
  108 static inline oss_dsp_t *look_for_dsp(int fd)
  109 {
  110     fd_t *xfd = look_for_fd(fd);
  111     return xfd ? xfd->dsp : NULL;
  112 }
  113 
  114 static inline oss_dsp_t *look_for_mmap_addr(void * addr)
  115 {
  116     fd_t *result = pcm_fds;
  117     while (result) {
  118         if (result->mmap_area == addr)
  119             return result->dsp ? result->dsp : NULL;
  120         result = result->next;
  121     }
  122     return NULL;
  123 }
  124 
  125 static void insert_fd(fd_t *xfd)
  126 {
  127     xfd->next = pcm_fds;
  128     pcm_fds = xfd;
  129 }
  130 
  131 static void remove_fd(fd_t *xfd)
  132 {
  133     fd_t *result = pcm_fds, *prev = NULL;
  134     while (result) {
  135         if (result == xfd) {
  136             if (prev == NULL)
  137                 pcm_fds = xfd->next;
  138             else
  139                 prev->next = xfd->next;
  140             return;
  141         }
  142         prev = result;
  143         result = result->next;
  144     }
  145     assert(0);
  146 }
  147 
  148 static unsigned int ld2(u_int32_t v)
  149 {
  150     unsigned r = 0;
  151 
  152     if (v >= 0x10000) {
  153         v >>= 16;
  154         r += 16;
  155     }
  156     if (v >= 0x100) {
  157         v >>= 8;
  158         r += 8;
  159     }
  160     if (v >= 0x10) {
  161         v >>= 4;
  162         r += 4;
  163     }
  164     if (v >= 4) {
  165         v >>= 2;
  166         r += 2;
  167     }
  168     if (v >= 2)
  169         r++;
  170     return r;
  171 }
  172 
  173 static snd_pcm_format_t oss_format_to_alsa(int format)
  174 {
  175     switch (format) {
  176     case AFMT_MU_LAW:   return SND_PCM_FORMAT_MU_LAW;
  177     case AFMT_A_LAW:    return SND_PCM_FORMAT_A_LAW;
  178     case AFMT_IMA_ADPCM:    return SND_PCM_FORMAT_IMA_ADPCM;
  179     case AFMT_U8:       return SND_PCM_FORMAT_U8;
  180     case AFMT_S16_LE:   return SND_PCM_FORMAT_S16_LE;
  181     case AFMT_S16_BE:   return SND_PCM_FORMAT_S16_BE;
  182     case AFMT_S8:       return SND_PCM_FORMAT_S8;
  183     case AFMT_U16_LE:   return SND_PCM_FORMAT_U16_LE;
  184     case AFMT_U16_BE:   return SND_PCM_FORMAT_U16_BE;
  185     case AFMT_MPEG:     return SND_PCM_FORMAT_MPEG;
  186     default:        return SND_PCM_FORMAT_U8;
  187     }
  188 }
  189 
  190 static int alsa_format_to_oss(snd_pcm_format_t format)
  191 {
  192     switch (format) {
  193     case SND_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW;
  194     case SND_PCM_FORMAT_A_LAW:  return AFMT_A_LAW;
  195     case SND_PCM_FORMAT_IMA_ADPCM:  return AFMT_IMA_ADPCM;
  196     case SND_PCM_FORMAT_U8:     return AFMT_U8;
  197     case SND_PCM_FORMAT_S16_LE: return AFMT_S16_LE;
  198     case SND_PCM_FORMAT_S16_BE: return AFMT_S16_BE;
  199     case SND_PCM_FORMAT_S8:     return AFMT_S8;
  200     case SND_PCM_FORMAT_U16_LE: return AFMT_U16_LE;
  201     case SND_PCM_FORMAT_U16_BE: return AFMT_U16_BE;
  202     case SND_PCM_FORMAT_MPEG:   return AFMT_MPEG;
  203     default:            return -EINVAL;
  204     }
  205 }
  206 
  207 static int oss_dsp_hw_params(oss_dsp_t *dsp)
  208 {
  209     int k;
  210     for (k = 1; k >= 0; --k) {
  211         oss_dsp_stream_t *str = &dsp->streams[k];
  212         snd_pcm_t *pcm = str->pcm;
  213         snd_pcm_hw_params_t *hw;
  214         int err;
  215         unsigned int rate, periods_min;
  216         if (!pcm)
  217             continue;
  218         dsp->format = oss_format_to_alsa(dsp->oss_format);
  219         str->frame_bytes = snd_pcm_format_physical_width(dsp->format) * dsp->channels / 8;
  220         snd_pcm_hw_params_alloca(&hw);
  221         snd_pcm_hw_params_any(pcm, hw);
  222 
  223         err = snd_pcm_hw_params_set_format(pcm, hw, dsp->format);
  224         if (err < 0)
  225             return err;
  226         err = snd_pcm_hw_params_set_channels(pcm, hw, dsp->channels);
  227         if (err < 0)
  228             return err;
  229         rate = dsp->rate;
  230         err = snd_pcm_hw_params_set_rate_near(pcm, hw, &rate, 0);
  231         if (err < 0)
  232             return err;
  233 #if 0
  234         err = snd_pcm_hw_params_set_periods_integer(pcm, hw);
  235         if (err < 0)
  236             return err;
  237 #endif
  238 
  239         if (str->mmap_buffer) {
  240             snd_pcm_uframes_t size;
  241             snd_pcm_access_mask_t *mask;
  242             snd_pcm_access_mask_alloca(&mask);
  243             snd_pcm_access_mask_any(mask);
  244             snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
  245             snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
  246             snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
  247             err = snd_pcm_hw_params_set_access_mask(pcm, hw, mask);
  248             if (err < 0)
  249                 return err;
  250             size = str->alsa.mmap_period_bytes / str->frame_bytes;
  251             err = snd_pcm_hw_params_set_period_size_near(pcm, hw, &size, NULL);
  252             if (err < 0)
  253                 return err;
  254             size = str->alsa.mmap_buffer_bytes / str->frame_bytes;
  255             err = snd_pcm_hw_params_set_buffer_size_near(pcm, hw, &size);
  256             if (err < 0)
  257                 return err;
  258             err = snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_MMAP_INTERLEAVED);
  259             if (err < 0)
  260                 return err;
  261         } else {
  262             err = snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED);
  263             if (err < 0)
  264                 return err;
  265             periods_min = 2;
  266             if (!dsp->maxfrags) {
  267                 err = snd_pcm_hw_params_set_periods_min(pcm, hw, &periods_min, 0);
  268                 if (err < 0)
  269                     return err;
  270             } else {
  271                 unsigned int periods_max = periods_min > dsp->maxfrags
  272                     ? periods_min : dsp->maxfrags;
  273                 err = snd_pcm_hw_params_set_periods_max(pcm, hw,
  274                                     &periods_max, 0);
  275                 if (err < 0)
  276                     return err;
  277             }
  278             if (dsp->fragshift > 0) {
  279                 snd_pcm_uframes_t s = (1 << dsp->fragshift) / str->frame_bytes;
  280                 s *= 16;
  281                 while (s >= 1024 && (err = snd_pcm_hw_params_set_buffer_size(pcm, hw, s)) < 0)
  282                     s /= 2;
  283                 s = (1 << dsp->fragshift) / str->frame_bytes;
  284                 while (s >= 256 && (err = snd_pcm_hw_params_set_period_size(pcm, hw, s, 0)) < 0)
  285                     s /= 2;
  286                 if (err < 0) {
  287                     s = (1 << dsp->fragshift) / str->frame_bytes;
  288                     err = snd_pcm_hw_params_set_period_size_near(pcm, hw, &s, 0);
  289                 }
  290             } else {
  291                 snd_pcm_uframes_t s = 16, old_s;
  292                 while (s * 2 < dsp->rate / 2) 
  293                     s *= 2;
  294                 old_s = s = s / 2;
  295                 while (s >= 1024 && (err = snd_pcm_hw_params_set_buffer_size(pcm, hw, s)) < 0)
  296                     s /= 2;
  297                 s = old_s;
  298                 while (s >= 256 && (err = snd_pcm_hw_params_set_period_size(pcm, hw, s, 0)) < 0)
  299                     s /= 2;
  300                 if (err < 0) {
  301                     s = old_s;
  302                     err = snd_pcm_hw_params_set_period_size_near(pcm, hw, &s, 0);
  303                 }
  304             }
  305             if (err < 0)
  306                 return err;
  307         }
  308         err = snd_pcm_hw_params(pcm, hw);
  309         if (err < 0)
  310             return err;
  311 #if 0
  312         if (alsa_oss_debug && alsa_oss_debug_out)
  313             snd_pcm_dump_setup(pcm, alsa_oss_debug_out);
  314 #endif
  315         if (err < 0)
  316             return err;
  317         dsp->oss_format = alsa_format_to_oss(dsp->format);
  318         err = snd_pcm_hw_params_get_period_size(hw, &str->alsa.period_size, 0);
  319         if (err < 0)
  320             return err;
  321         err = snd_pcm_hw_params_get_buffer_size(hw, &str->alsa.buffer_size);
  322         if (err < 0)
  323             return err;
  324         if (str->mmap_buffer == NULL) {
  325             str->oss.buffer_size = 1 << ld2(str->alsa.buffer_size);
  326             if (str->oss.buffer_size < str->alsa.buffer_size)
  327                 str->oss.buffer_size *= 2;
  328             str->oss.period_size = 1 << ld2(str->alsa.period_size);
  329             if (str->oss.period_size < str->alsa.period_size)
  330                 str->oss.period_size *= 2;
  331         } else {
  332             str->oss.buffer_size = str->alsa.mmap_period_bytes / str->frame_bytes;
  333             str->oss.period_size = str->alsa.mmap_buffer_bytes / str->frame_bytes;
  334         }
  335         str->oss.periods = str->oss.buffer_size / str->oss.period_size;
  336         if (str->mmap_areas)
  337             free(str->mmap_areas);
  338         str->mmap_areas = NULL;
  339         if (str->mmap_buffer) {
  340             unsigned int c;
  341             snd_pcm_channel_area_t *a;
  342             unsigned int bits_per_sample, bits_per_frame;
  343             str->mmap_areas = calloc(dsp->channels, sizeof(*str->mmap_areas));
  344             if (!str->mmap_areas)
  345                 return -ENOMEM;
  346             bits_per_sample = snd_pcm_format_physical_width(dsp->format);
  347             bits_per_frame = bits_per_sample * dsp->channels;
  348             a = str->mmap_areas;
  349             for (c = 0; c < dsp->channels; c++, a++) {
  350                 a->addr = str->mmap_buffer;
  351                 a->first = bits_per_sample * c;
  352                 a->step = bits_per_frame;
  353             }
  354         }
  355         str->oss.hw_bytes = 0;
  356         str->oss.boundary = (0x3fffffff / str->oss.buffer_size) * str->oss.buffer_size;
  357         str->alsa.appl_ptr = 0;
  358         str->alsa.old_hw_ptr = 0;
  359         str->mmap_advance = str->oss.period_size;
  360     }
  361     return 0;
  362 }
  363 
  364 static int oss_dsp_sw_params(oss_dsp_t *dsp)
  365 {
  366     int k;
  367     for (k = 1; k >= 0; --k) {
  368         oss_dsp_stream_t *str = &dsp->streams[k];
  369         snd_pcm_t *pcm = str->pcm;
  370         snd_pcm_sw_params_t *sw;
  371         int err;
  372         if (!pcm)
  373             continue;
  374         sw = str->sw_params;
  375         snd_pcm_sw_params_current(pcm, sw);
  376         snd_pcm_sw_params_set_xfer_align(pcm, sw, 1);
  377         snd_pcm_sw_params_set_start_threshold(pcm, sw, 
  378                               str->stopped ? str->alsa.buffer_size + 1 :
  379                               str->alsa.period_size);
  380 #if 1
  381         snd_pcm_sw_params_set_stop_threshold(pcm, sw,
  382                              str->mmap_buffer ? LONG_MAX :
  383                              str->alsa.buffer_size);
  384 #else
  385         snd_pcm_sw_params_set_stop_threshold(pcm, sw,
  386                              LONG_MAX);
  387         snd_pcm_sw_params_set_silence_threshold(pcm, sw,
  388                                str->alsa.period_size);
  389         snd_pcm_sw_params_set_silence_size(pcm, sw,
  390                            str->alsa.period_size);
  391 #endif
  392         err = snd_pcm_sw_params(pcm, sw);
  393         if (err < 0)
  394             return err;
  395         err = snd_pcm_sw_params_current(pcm, sw);
  396         if (err < 0)
  397             return err;
  398         err = snd_pcm_sw_params_get_boundary(sw, &str->alsa.boundary);
  399         if (err < 0)
  400             return err;
  401     }
  402     return 0;
  403 }
  404 
  405 static int oss_dsp_params(oss_dsp_t *dsp)
  406 {
  407     int err;
  408     dsp->hwset = 0;
  409     err = oss_dsp_hw_params(dsp);
  410     if (err < 0) 
  411         return err;
  412     dsp->hwset = 1;
  413     err = oss_dsp_sw_params(dsp);
  414     if (err < 0) 
  415         return err;
  416 #if 0
  417     if (alsa_oss_debug && alsa_oss_debug_out) {
  418         int k;
  419         for (k = 1; k >= 0; --k) {
  420             oss_dsp_stream_t *str = &dsp->streams[k];
  421             if (str->pcm)
  422                 snd_pcm_dump(str->pcm, alsa_oss_debug_out);
  423         }
  424     }
  425 #endif
  426     return 0;
  427 }
  428 
  429 int lib_oss_pcm_close(int fd)
  430 {
  431     int result = 0;
  432     int k;
  433     fd_t *xfd = look_for_fd(fd);
  434     oss_dsp_t *dsp;
  435     
  436     if (xfd == NULL) {
  437         errno = ENOENT;
  438         return -1;
  439     }
  440     dsp = xfd->dsp;
  441     for (k = 0; k < 2; ++k) {
  442         oss_dsp_stream_t *str = &dsp->streams[k];
  443         if (str->sw_params)
  444             snd_pcm_sw_params_free(str->sw_params);
  445     }
  446     for (k = 0; k < 2; ++k) {
  447         int err;
  448         oss_dsp_stream_t *str = &dsp->streams[k];
  449         if (!str->pcm)
  450             continue;
  451         if (k == SND_PCM_STREAM_PLAYBACK) {
  452             if (snd_pcm_state(str->pcm) != SND_PCM_STATE_OPEN)
  453                 snd_pcm_drain(str->pcm);
  454         }
  455         err = snd_pcm_close(str->pcm);
  456         if (err < 0)
  457             result = err;
  458     }
  459     remove_fd(xfd);
  460     free(dsp);
  461     free(xfd);
  462     if (result < 0) {
  463         errno = -result;
  464         result = -1;
  465     }
  466     close(fd);
  467     DEBUG("close(%d) -> %d", fd, result);
  468     if (result < 0)
  469         DEBUG("(errno=%d)\n", errno);
  470     else
  471         DEBUG("\n");
  472     return 0;
  473 }
  474 
  475 static int open_pcm(oss_dsp_t *dsp, const char *name, unsigned int pcm_mode,
  476             unsigned int streams)
  477 {
  478     int k, result;
  479 
  480     result = -ENODEV;
  481     for (k = 0; k < 2; ++k) {
  482         if (!(streams & (1 << k)))
  483             continue;
  484         result = snd_pcm_open(&dsp->streams[k].pcm, name, k, SND_PCM_NONBLOCK);
  485         DEBUG("Opened PCM %s for stream %d (result = %d)\n", name, k, result);
  486         if (result < 0) {
  487             if (k == 1 && dsp->streams[0].pcm != NULL) {
  488                 dsp->streams[1].pcm = NULL;
  489                 streams &= ~(1 << SND_PCM_STREAM_CAPTURE);
  490                 result = 0;
  491             }
  492             break;
  493         } else if (! pcm_mode)
  494             /* reset the blocking mode */
  495             snd_pcm_nonblock(dsp->streams[k].pcm, 0);
  496     }
  497     return result;
  498 }
  499 
  500 static int oss_dsp_open(int card, int device, int oflag, mode_t mode ATTRIBUTE_UNUSED)
  501 {
  502     oss_dsp_t *dsp;
  503     unsigned int pcm_mode = 0;
  504     unsigned int streams, k;
  505     int format = AFMT_MU_LAW;
  506     int fd = -1;
  507     fd_t *xfd;
  508     int result;
  509     char name[64];
  510 
  511     char *s = getenv("ALSA_OSS_DEBUG");
  512     if (s) {
  513         alsa_oss_debug = 1;
  514         if (alsa_oss_debug_out == NULL) {
  515             if (snd_output_stdio_attach(&alsa_oss_debug_out, stderr, 0) < 0)
  516                 alsa_oss_debug_out = NULL;
  517         }
  518     }
  519     switch (device) {
  520     case OSS_DEVICE_DSP:
  521         format = AFMT_U8;
  522         sprintf(name, "dsp%d", card);
  523         break;
  524     case OSS_DEVICE_DSPW:
  525         format = AFMT_S16_LE;
  526         sprintf(name, "dspW%d", card);
  527         break;
  528     case OSS_DEVICE_AUDIO:
  529         sprintf(name, "audio%d", card);
  530         break;
  531     case OSS_DEVICE_ADSP:
  532         sprintf(name, "adsp%d", card);
  533         break;
  534     default:
  535         errno = ENOENT;
  536         return -1;
  537     }
  538     if (oflag & O_NONBLOCK)
  539         pcm_mode = SND_PCM_NONBLOCK;
  540     switch (oflag & O_ACCMODE) {
  541     case O_RDONLY:
  542         streams = 1 << SND_PCM_STREAM_CAPTURE;
  543         break;
  544     case O_WRONLY:
  545         streams = 1 << SND_PCM_STREAM_PLAYBACK;
  546         break;
  547     case O_RDWR:
  548         streams = ((1 << SND_PCM_STREAM_PLAYBACK) | 
  549                (1 << SND_PCM_STREAM_CAPTURE));
  550         break;
  551     default:
  552         errno = EINVAL;
  553         return -1;
  554     }
  555     fd = open("/dev/null", oflag & O_ACCMODE);
  556     if (fd < 0)
  557         return -1;
  558     xfd = calloc(1, sizeof(fd_t));
  559     if (!xfd) {
  560         close(fd);
  561         errno = ENOMEM;
  562         return -1;
  563     }
  564     dsp = calloc(1, sizeof(oss_dsp_t));
  565     if (!dsp) {
  566         result = -ENOMEM;
  567         goto _error;
  568     }
  569     xfd->dsp = dsp;
  570     dsp->channels = 1;
  571     dsp->rate = 8000;
  572     dsp->oss_format = format;
  573     result = -EINVAL;
  574     for (k = 0; k < 2; ++k) {
  575         if (!(streams & (1 << k)))
  576             continue;
  577         result = snd_pcm_sw_params_malloc(&dsp->streams[k].sw_params);
  578         if (result < 0)
  579             goto _error;
  580     }
  581     s = getenv("ALSA_OSS_PCM_DEVICE");
  582     result = -ENODEV;
  583     if (s && *s)
  584         result = open_pcm(dsp, s, pcm_mode, streams);
  585     if (result < 0)
  586         result = open_pcm(dsp, name, pcm_mode, streams);
  587     if (result < 0) {
  588         /* try to open the default pcm as fallback */
  589         if (card == 0 && (device == OSS_DEVICE_DSP || device == OSS_DEVICE_AUDIO))
  590             strcpy(name, "default");
  591         else
  592             sprintf(name, "default:%d", card);
  593         result = open_pcm(dsp, name, pcm_mode, streams);
  594         if (result < 0)
  595             goto _error;
  596     }
  597     result = oss_dsp_params(dsp);
  598     if (result < 0) {
  599         DEBUG("Error setting params\n");
  600         goto _error;
  601     }
  602     xfd->fileno = fd;
  603     insert_fd(xfd);
  604     return fd;
  605 
  606  _error:
  607     for (k = 0; k < 2; ++k) {
  608         if (dsp->streams[k].pcm)
  609             snd_pcm_close(dsp->streams[k].pcm);
  610         if (dsp->streams[k].sw_params)
  611             snd_pcm_sw_params_free(dsp->streams[k].sw_params);
  612     }
  613     close(fd);
  614     if (xfd->dsp)
  615         free(xfd->dsp);
  616     free(xfd);
  617     errno = -result;
  618     return -1;
  619 }
  620 
  621 static int xrun(snd_pcm_t *pcm)
  622 {
  623     switch (snd_pcm_state(pcm)) {
  624     case SND_PCM_STATE_XRUN:
  625         return snd_pcm_prepare(pcm);
  626     case SND_PCM_STATE_DRAINING:
  627         if (snd_pcm_stream(pcm) == SND_PCM_STREAM_CAPTURE)
  628             return snd_pcm_prepare(pcm);
  629         break;
  630     default:
  631         break;
  632     }
  633     return -EIO;
  634 }
  635 
  636 static int resume(snd_pcm_t *pcm)
  637 {
  638     int res;
  639     while ((res = snd_pcm_resume(pcm)) == -EAGAIN)
  640         sleep(1);
  641     if (! res)
  642         return 0;
  643     return snd_pcm_prepare(pcm);
  644 }
  645 
  646 ssize_t lib_oss_pcm_write(int fd, const void *buf, size_t n)
  647 {
  648     ssize_t result;
  649     oss_dsp_t *dsp = look_for_dsp(fd);
  650     oss_dsp_stream_t *str;
  651     snd_pcm_t *pcm;
  652     snd_pcm_uframes_t frames;
  653 
  654     if (dsp == NULL) {
  655         errno = EBADFD;
  656         result = -1;
  657         goto _end;
  658     }
  659     str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
  660     pcm = str->pcm;
  661     if (!pcm) {
  662         errno = EBADFD;
  663         result = -1;
  664         goto _end;
  665     }
  666     frames = n / str->frame_bytes;
  667  _again:
  668     result = snd_pcm_writei(pcm, buf, frames);
  669     if (result == -EPIPE) {
  670         if (! (result = xrun(pcm)))
  671             goto _again;
  672     } else if (result == -ESTRPIPE) {
  673         if (! (result = resume(pcm)))
  674             goto _again;
  675     }
  676     if (result < 0) {
  677         errno = -result;
  678         result = -1;
  679         goto _end;
  680     }
  681     str->alsa.appl_ptr += result;
  682     str->alsa.appl_ptr %= str->alsa.boundary;
  683     result *= str->frame_bytes;
  684     str->oss.bytes += result;
  685  _end:
  686     DEBUG("write(%d, %p, %ld) -> %ld", fd, buf, (long)n, (long)result);
  687     if (result < 0)
  688         DEBUG("(errno=%d)\n", errno);
  689     else
  690         DEBUG("\n");
  691     return result;
  692 }
  693 
  694 ssize_t lib_oss_pcm_read(int fd, void *buf, size_t n)
  695 {
  696     ssize_t result;
  697     oss_dsp_t *dsp = look_for_dsp(fd);
  698     oss_dsp_stream_t *str;
  699     snd_pcm_t *pcm;
  700     snd_pcm_uframes_t frames;
  701 
  702     if (dsp == NULL) {
  703         errno = EBADFD;
  704         result = -1;
  705         goto _end;
  706     }
  707     str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
  708     pcm = str->pcm;
  709     if (!pcm) {
  710         errno = EBADFD;
  711         result = -1;
  712         goto _end;
  713     }
  714     frames = n / str->frame_bytes;
  715  _again:
  716     result = snd_pcm_readi(pcm, buf, frames);
  717     if (result == -EPIPE) {
  718         if (! (result = xrun(pcm)))
  719             goto _again;
  720     } else if (result == -ESTRPIPE) {
  721         if (! (result = resume(pcm)))
  722             goto _again;
  723     }
  724     if (result < 0) {
  725         errno = -result;
  726         result = -1;
  727         goto _end;
  728     }
  729     str->alsa.appl_ptr += result;
  730     str->alsa.appl_ptr %= str->alsa.boundary;
  731     result *= str->frame_bytes;
  732     str->oss.bytes += result;
  733  _end:
  734     DEBUG("read(%d, %p, %ld) -> %ld", fd, buf, (long)n, (long)result);
  735     if (result < 0)
  736         DEBUG("(errno=%d)\n", errno);
  737     else
  738         DEBUG("\n");
  739     return result;
  740 }
  741 
  742 #define USE_REWIND 1
  743 
  744 static void oss_dsp_mmap_update(oss_dsp_t *dsp, snd_pcm_stream_t stream,
  745                 snd_pcm_sframes_t delay)
  746 {
  747     oss_dsp_stream_t *str = &dsp->streams[stream];
  748     snd_pcm_t *pcm = str->pcm;
  749     snd_pcm_sframes_t err;
  750     snd_pcm_uframes_t size;
  751     const snd_pcm_channel_area_t *areas;
  752     switch (stream) {
  753     case SND_PCM_STREAM_PLAYBACK:
  754         if (delay < 0) {
  755             str->mmap_advance -= delay;
  756             if (str->mmap_advance > dsp->rate / 10)
  757                 str->mmap_advance = dsp->rate / 10;
  758             //fprintf(stderr, "mmap_advance=%ld\n", str->mmap_advance);
  759             err = snd_pcm_forward(pcm, -delay);
  760             if (err >= 0) {
  761                 str->alsa.appl_ptr += err;
  762                 str->alsa.appl_ptr %= str->alsa.boundary;
  763             }
  764         }
  765 #if USE_REWIND
  766         err = snd_pcm_rewind(pcm, str->alsa.buffer_size);
  767         if (err < 0) {
  768             /* fallback to not very accurate method */
  769             size = str->mmap_advance - delay;
  770         } else {
  771             str->alsa.appl_ptr -= err;
  772             str->alsa.appl_ptr %= str->alsa.boundary;
  773             size = str->mmap_advance;
  774         }
  775         //fprintf(stderr, "delay=%ld rewind=%ld forward=%ld\n", delay, err, size);
  776 #else
  777         size = str->mmap_advance - delay;
  778 #endif
  779         while (size > 0) {
  780             snd_pcm_uframes_t ofs;
  781             snd_pcm_uframes_t frames = size;
  782             snd_pcm_mmap_begin(pcm, &areas, &ofs, &frames);
  783             if (frames == 0)
  784                 break;
  785 //          fprintf(stderr, "copy %ld %ld %d\n", ofs, frames, dsp->format);
  786             snd_pcm_areas_copy(areas, ofs, str->mmap_areas,
  787                        str->alsa.appl_ptr % str->oss.buffer_size, 
  788                        dsp->channels, frames,
  789                        dsp->format);
  790             err = snd_pcm_mmap_commit(pcm, ofs, frames);
  791             if (err <= 0)
  792                 break;
  793             size -= err;
  794             str->alsa.appl_ptr += err;
  795             str->alsa.appl_ptr %= str->alsa.boundary;
  796         }
  797         break;
  798     case SND_PCM_STREAM_CAPTURE:
  799         if (delay > (snd_pcm_sframes_t)str->alsa.buffer_size) {
  800             err = snd_pcm_forward(pcm, delay - str->alsa.buffer_size);
  801             if (err >= 0) {
  802                 str->alsa.appl_ptr += err;
  803                 str->alsa.appl_ptr %= str->alsa.boundary;
  804                 size = str->alsa.buffer_size;
  805             } else {
  806                 size = delay;
  807             }
  808         } else {
  809             size = delay;
  810         }
  811         while (size > 0) {
  812             snd_pcm_uframes_t ofs;
  813             snd_pcm_uframes_t frames = size;
  814             snd_pcm_mmap_begin(pcm, &areas, &ofs, &frames);
  815             if (frames == 0)
  816                 break;
  817             snd_pcm_areas_copy(str->mmap_areas,
  818                        str->alsa.appl_ptr % str->oss.buffer_size,
  819                        areas, ofs,
  820                        dsp->channels, frames,
  821                        dsp->format);
  822             err = snd_pcm_mmap_commit(pcm, ofs, frames);
  823             if (err < 0)
  824                 break;
  825             size -= err;
  826             str->alsa.appl_ptr += err;
  827             str->alsa.appl_ptr %= str->alsa.boundary;
  828         }
  829         break;
  830     }
  831 }
  832 
  833 int lib_oss_pcm_ioctl(int fd, unsigned long cmd, ...)
  834 {
  835     int result, err = 0;
  836     va_list args;
  837     void *arg;
  838     oss_dsp_t *dsp = look_for_dsp(fd);
  839     oss_dsp_stream_t *str;
  840     snd_pcm_t *pcm;
  841 
  842     if (dsp == NULL) {
  843         errno = EBADFD;
  844         return -1;
  845     }
  846     va_start(args, cmd);
  847     arg = va_arg(args, void *);
  848     va_end(args);
  849     DEBUG("ioctl(%d, ", fd);
  850     switch (cmd) {
  851     case OSS_GETVERSION:
  852         *(int*)arg = SOUND_VERSION;
  853         DEBUG("OSS_GETVERSION, %p) -> [%d]\n", arg, *(int*)arg);
  854         break;
  855     case SNDCTL_DSP_RESET:
  856     {
  857         int k;
  858         DEBUG("SNDCTL_DSP_RESET)\n");
  859         if (!dsp->hwset) {
  860             errno = -EIO;
  861             return -1;
  862         }
  863         result = 0;
  864         for (k = 0; k < 2; ++k) {
  865             str = &dsp->streams[k];
  866             pcm = str->pcm;
  867             if (!pcm)
  868                 continue;
  869             err = snd_pcm_drop(pcm);
  870             if (err >= 0)
  871                 err = snd_pcm_prepare(pcm);
  872             if (err < 0)
  873                 result = err;
  874             str->oss.bytes = 0;
  875             str->oss.hw_bytes = 0;
  876             str->alsa.appl_ptr = 0;
  877             str->alsa.old_hw_ptr = 0;
  878         }
  879         err = result;
  880         break;
  881     }
  882     case SNDCTL_DSP_SYNC:
  883     {
  884         int k;
  885         DEBUG("SNDCTL_DSP_SYNC)\n");
  886         if (!dsp->hwset) {
  887             errno = -EIO;
  888             return -1;
  889         }
  890         result = 0;
  891         for (k = 0; k < 2; ++k) {
  892             str = &dsp->streams[k];
  893             pcm = str->pcm;
  894             if (!pcm)
  895                 continue;
  896             err = snd_pcm_drain(pcm);
  897             if (err >= 0)
  898                 err = snd_pcm_prepare(pcm);
  899             if (err < 0)
  900                 result = err;
  901             str->oss.hw_bytes = 0;
  902             str->alsa.appl_ptr = 0;
  903             str->alsa.old_hw_ptr = 0;
  904         }
  905         err = result;
  906         break;
  907     }
  908     case SNDCTL_DSP_SPEED:
  909         dsp->rate = *(int *)arg;
  910         err = oss_dsp_params(dsp);
  911         DEBUG("SNDCTL_DSP_SPEED, %p[%d]) -> [%d]\n", arg, *(int *)arg, dsp->rate);
  912         *(int *)arg = dsp->rate;
  913         break;
  914     case SNDCTL_DSP_STEREO:
  915         if (*(int *)arg)
  916             dsp->channels = 2;
  917         else
  918             dsp->channels = 1;
  919         err = oss_dsp_params(dsp);
  920         DEBUG("SNDCTL_DSP_STEREO, %p[%d]) -> [%d]\n", arg, *(int *)arg, dsp->channels - 1);
  921         *(int *)arg = dsp->channels - 1;
  922         break;
  923     case SNDCTL_DSP_CHANNELS:
  924         dsp->channels = (*(int *)arg);
  925         err = oss_dsp_params(dsp);
  926         if (err < 0)
  927             break;
  928         DEBUG("SNDCTL_DSP_CHANNELS, %p[%d]) -> [%d]\n", arg, *(int *)arg, dsp->channels);
  929         *(int *)arg = dsp->channels;
  930         break;
  931     case SNDCTL_DSP_SETFMT:
  932         if (*(int *)arg != AFMT_QUERY) {
  933             dsp->oss_format = *(int *)arg;
  934             err = oss_dsp_params(dsp);
  935             if (err < 0)
  936                 break;
  937         }
  938         DEBUG("SNDCTL_DSP_SETFMT, %p[%d]) -> [%d]\n", arg, *(int *)arg, dsp->oss_format);
  939         *(int *) arg = dsp->oss_format;
  940         break;
  941     case SNDCTL_DSP_GETBLKSIZE:
  942         str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
  943         if (!str->pcm)
  944             str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
  945         pcm = str->pcm;
  946         *(int *) arg = str->oss.period_size * str->frame_bytes;
  947         DEBUG("SNDCTL_DSP_GETBLKSIZE, %p) -> [%d]\n", arg, *(int *)arg);
  948         break;
  949     case SNDCTL_DSP_POST:
  950         DEBUG("SNDCTL_DSP_POST)\n");
  951         break;
  952     case SNDCTL_DSP_SUBDIVIDE:
  953         DEBUG("SNDCTL_DSP_SUBDIVIDE, %p[%d])\n", arg, *(int *)arg);
  954         dsp->subdivision = *(int *)arg;
  955         if (dsp->subdivision < 1)
  956             dsp->subdivision = 1;
  957         err = oss_dsp_params(dsp);
  958         break;
  959     case SNDCTL_DSP_SETFRAGMENT:
  960     {
  961         DEBUG("SNDCTL_DSP_SETFRAGMENT, %p[%x])\n", arg, *(int *)arg);
  962         dsp->fragshift = *(int *)arg & 0xffff;
  963         if (dsp->fragshift < 4)
  964             dsp->fragshift = 4;
  965         dsp->maxfrags = ((*(int *)arg) >> 16) & 0xffff;
  966         if (dsp->maxfrags < 2)
  967             dsp->maxfrags = 2;
  968         err = oss_dsp_params(dsp);
  969         break;
  970     }
  971     case SNDCTL_DSP_GETFMTS:
  972     {
  973         *(int *)arg = (AFMT_MU_LAW | AFMT_A_LAW | AFMT_IMA_ADPCM | 
  974                    AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | 
  975                    AFMT_S8 | AFMT_U16_LE | AFMT_U16_BE);
  976         DEBUG("SNDCTL_DSP_GETFMTS, %p) -> [%d]\n", arg, *(int *)arg);
  977         break;
  978     }
  979     case SNDCTL_DSP_NONBLOCK:
  980     {   
  981         DEBUG("SNDCTL_DSP_NONBLOCK)\n");
  982         return lib_oss_pcm_nonblock(fd, 1);
  983     }
  984     case SNDCTL_DSP_GETCAPS:
  985     {
  986         result = DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP;
  987         if (dsp->streams[SND_PCM_STREAM_PLAYBACK].pcm && 
  988             dsp->streams[SND_PCM_STREAM_CAPTURE].pcm)
  989             result |= DSP_CAP_DUPLEX;
  990         *(int*)arg = result;
  991         DEBUG("SNDCTL_DSP_GETCAPS, %p) -> [%d]\n", arg, *(int*)arg);
  992         break;
  993     }
  994     case SNDCTL_DSP_GETTRIGGER:
  995     {
  996         int s = 0;
  997         pcm = dsp->streams[SND_PCM_STREAM_PLAYBACK].pcm;
  998         if (pcm) {
  999             if (snd_pcm_state(pcm) == SND_PCM_STATE_RUNNING)
 1000                 s |= PCM_ENABLE_OUTPUT;
 1001         }
 1002         pcm = dsp->streams[SND_PCM_STREAM_CAPTURE].pcm;
 1003         if (pcm) {
 1004             if (snd_pcm_state(pcm) == SND_PCM_STATE_RUNNING)
 1005                 s |= PCM_ENABLE_INPUT;
 1006         }
 1007         *(int*)arg = s;
 1008         DEBUG("SNDCTL_DSP_GETTRIGGER, %p) -> [%d]\n", arg, *(int*)arg);
 1009         break;
 1010     }       
 1011     case SNDCTL_DSP_SETTRIGGER:
 1012     {
 1013         DEBUG("SNDCTL_DSP_SETTRIGGER, %p[%d])\n", arg, *(int*)arg);
 1014         result = *(int*) arg;
 1015         str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
 1016         pcm = str->pcm;
 1017         if (pcm) {
 1018             if (result & PCM_ENABLE_INPUT) {
 1019                 if (str->stopped) {
 1020                     str->stopped = 0;
 1021                     err = oss_dsp_sw_params(dsp);
 1022                     if (err < 0)
 1023                         break;
 1024                     err = snd_pcm_start(pcm);
 1025                     if (err < 0)
 1026                         break;
 1027                 }
 1028             } else {
 1029                 if (!str->stopped) {
 1030                     str->stopped = 1;
 1031                     err = snd_pcm_drop(pcm);
 1032                     if (err < 0)
 1033                         break;
 1034                     err = oss_dsp_sw_params(dsp);
 1035                     if (err < 0)
 1036                         break;
 1037                     err = snd_pcm_prepare(pcm);
 1038                     if (err < 0)
 1039                         break;
 1040                 }
 1041             }
 1042         }
 1043         str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
 1044         pcm = str->pcm;
 1045         if (pcm) {
 1046             if (result & PCM_ENABLE_OUTPUT) {
 1047                 if (str->stopped) {
 1048                     str->stopped = 0;
 1049                     err = oss_dsp_sw_params(dsp);
 1050                     if (err < 0)
 1051                         break;
 1052                     if (str->mmap_buffer) {
 1053                         const snd_pcm_channel_area_t *areas;
 1054                         snd_pcm_uframes_t offset;
 1055                         snd_pcm_uframes_t size = str->alsa.buffer_size;
 1056                         ssize_t cres;
 1057                         snd_pcm_mmap_begin(pcm, &areas, &offset, &size);
 1058                         snd_pcm_areas_copy(areas, 0, str->mmap_areas, 0,
 1059                                    dsp->channels, size,
 1060                                    dsp->format);
 1061                         cres = snd_pcm_mmap_commit(pcm, offset, size);
 1062                         if (cres > 0) {
 1063                             str->alsa.appl_ptr += cres;
 1064                             str->alsa.appl_ptr %= str->alsa.boundary;
 1065                         }
 1066                     }
 1067                     err = snd_pcm_start(pcm);
 1068                     if (err < 0)
 1069                         break;
 1070                 }
 1071             } else {
 1072                 if (!str->stopped) {
 1073                     str->stopped = 1;
 1074                     err = snd_pcm_drop(pcm);
 1075                     if (err < 0)
 1076                         break;
 1077                     err = oss_dsp_sw_params(dsp);
 1078                     if (err < 0)
 1079                         break;
 1080                     err = snd_pcm_prepare(pcm);
 1081                     if (err < 0)
 1082                         break;
 1083                 }
 1084             }
 1085         }
 1086         break;
 1087     }
 1088     case SNDCTL_DSP_GETISPACE:
 1089     {
 1090         snd_pcm_sframes_t avail, delay;
 1091         snd_pcm_state_t state;
 1092         audio_buf_info *info = arg;
 1093         str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
 1094         pcm = str->pcm;
 1095         if (!pcm) {
 1096             err = -EINVAL;
 1097             break;
 1098         }
 1099         state = snd_pcm_state(pcm);
 1100         if (state == SND_PCM_STATE_XRUN) {
 1101             err = xrun(pcm);
 1102             if (err < 0)
 1103                 break;
 1104             state = snd_pcm_state(pcm);
 1105         }
 1106         if (state == SND_PCM_STATE_SUSPENDED) {
 1107             err = resume(pcm);
 1108             if (err < 0)
 1109                 break;
 1110             state = snd_pcm_state(pcm);
 1111         }
 1112         if (state == SND_PCM_STATE_RUNNING) {
 1113             snd_pcm_delay(pcm, &delay);
 1114             if (str->mmap_buffer)
 1115                 oss_dsp_mmap_update(dsp, SND_PCM_STREAM_CAPTURE, delay);
 1116         }
 1117         avail = snd_pcm_avail_update(pcm);
 1118         if (avail < 0)
 1119             avail = 0;
 1120         if ((snd_pcm_uframes_t)avail > str->oss.buffer_size)
 1121             avail = str->oss.buffer_size;
 1122         info->fragsize = str->oss.period_size * str->frame_bytes;
 1123         info->fragstotal = str->oss.periods;
 1124         info->bytes = avail * str->frame_bytes;
 1125         info->fragments = avail / str->oss.period_size;
 1126         DEBUG("SNDCTL_DSP_GETISPACE, %p) -> {%d, %d, %d, %d}\n", arg,
 1127               info->fragments,
 1128               info->fragstotal,
 1129               info->fragsize,
 1130               info->bytes);
 1131         break;
 1132     }
 1133     case SNDCTL_DSP_GETOSPACE:
 1134     {
 1135         snd_pcm_sframes_t avail, delay;
 1136         snd_pcm_state_t state;
 1137         audio_buf_info *info = arg;
 1138         str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
 1139         pcm = str->pcm;
 1140         if (!pcm) {
 1141             err = -EINVAL;
 1142             break;
 1143         }
 1144         state = snd_pcm_state(pcm);
 1145         if (state == SND_PCM_STATE_XRUN) {
 1146             err = xrun(pcm);
 1147             if (err < 0)
 1148                 break;
 1149             state = snd_pcm_state(pcm);
 1150         }
 1151         if (state == SND_PCM_STATE_SUSPENDED) {
 1152             err = resume(pcm);
 1153             if (err < 0)
 1154                 break;
 1155             state = snd_pcm_state(pcm);
 1156         }
 1157         if (state == SND_PCM_STATE_RUNNING || 
 1158             state == SND_PCM_STATE_DRAINING) {
 1159             snd_pcm_delay(pcm, &delay);
 1160             if (str->mmap_buffer)
 1161                 oss_dsp_mmap_update(dsp, SND_PCM_STREAM_PLAYBACK, delay);
 1162         }
 1163         avail = snd_pcm_avail_update(pcm);
 1164         if (avail < 0 || (snd_pcm_uframes_t)avail > str->oss.buffer_size)
 1165             avail = str->oss.buffer_size;
 1166         info->fragsize = str->oss.period_size * str->frame_bytes;
 1167         info->fragstotal = str->oss.periods;
 1168         info->bytes = avail * str->frame_bytes;
 1169         info->fragments = avail / str->oss.period_size;
 1170         DEBUG("SNDCTL_DSP_GETOSPACE, %p) -> {%d %d %d %d}\n", arg,
 1171               info->fragments,
 1172               info->fragstotal,
 1173               info->fragsize,
 1174               info->bytes);
 1175         break;
 1176     }
 1177     case SNDCTL_DSP_GETIPTR:
 1178     {
 1179         snd_pcm_sframes_t delay = 0, avail, diff;
 1180         snd_pcm_uframes_t hw_ptr;
 1181         snd_pcm_state_t state;
 1182         count_info *info = arg;
 1183         str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
 1184         pcm = str->pcm;
 1185         if (!pcm) {
 1186             err = -EINVAL;
 1187             break;
 1188         }
 1189         state = snd_pcm_state(pcm);
 1190         if (state == SND_PCM_STATE_XRUN) {
 1191             err = xrun(pcm);
 1192             if (err < 0)
 1193                 break;
 1194             state = snd_pcm_state(pcm);
 1195         }
 1196         if (state == SND_PCM_STATE_SUSPENDED) {
 1197             err = resume(pcm);
 1198             if (err < 0)
 1199                 break;
 1200             state = snd_pcm_state(pcm);
 1201         }
 1202         if (state == SND_PCM_STATE_RUNNING) {
 1203             snd_pcm_delay(pcm, &delay);
 1204             if (str->mmap_buffer)
 1205                 oss_dsp_mmap_update(dsp, SND_PCM_STREAM_CAPTURE, delay);
 1206         }
 1207         avail = snd_pcm_avail_update(pcm);
 1208         hw_ptr = (str->alsa.appl_ptr + avail) % str->alsa.boundary;
 1209         diff = hw_ptr - str->alsa.old_hw_ptr;
 1210         if (diff < 0)
 1211             diff += str->alsa.boundary;
 1212         str->oss.hw_bytes += diff;
 1213         str->oss.hw_bytes %= str->oss.boundary;
 1214         info->bytes = (str->oss.hw_bytes * str->frame_bytes) & 0x7fffffff;
 1215         info->ptr = (str->oss.hw_bytes % str->oss.buffer_size) * str->frame_bytes;
 1216         if (str->mmap_buffer) {
 1217             ssize_t n = (hw_ptr / str->oss.period_size) - (str->alsa.old_hw_ptr / str->oss.period_size);
 1218             if (n < 0)
 1219                 n += str->alsa.boundary / str->oss.period_size;
 1220             info->blocks = n;
 1221         } else {
 1222             info->blocks = delay / str->oss.period_size;
 1223         }
 1224         str->alsa.old_hw_ptr = hw_ptr;
 1225         DEBUG("SNDCTL_DSP_GETIPTR, %p) -> {%d %d %d}\n", arg,
 1226               info->bytes,
 1227               info->blocks,
 1228               info->ptr);
 1229         break;
 1230     }
 1231     case SNDCTL_DSP_GETOPTR:
 1232     {
 1233         snd_pcm_sframes_t delay = 0, avail, diff;
 1234         snd_pcm_uframes_t hw_ptr;
 1235         snd_pcm_state_t state;
 1236         count_info *info = arg;
 1237         str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
 1238         pcm = str->pcm;
 1239         if (!pcm) {
 1240             err = -EINVAL;
 1241             break;
 1242         }
 1243         if (state == SND_PCM_STATE_XRUN) {
 1244             err = xrun(pcm);
 1245             if (err < 0)
 1246                 break;
 1247             state = snd_pcm_state(pcm);
 1248         }
 1249         state = snd_pcm_state(pcm);
 1250         if (state == SND_PCM_STATE_SUSPENDED) {
 1251             err = resume(pcm);
 1252             if (err < 0)
 1253                 break;
 1254             state = snd_pcm_state(pcm);
 1255         }
 1256         if (state == SND_PCM_STATE_RUNNING || 
 1257             state == SND_PCM_STATE_DRAINING) {
 1258             snd_pcm_delay(pcm, &delay);
 1259             if (str->mmap_buffer)
 1260                 oss_dsp_mmap_update(dsp, SND_PCM_STREAM_PLAYBACK, delay);
 1261         }
 1262         avail = snd_pcm_avail_update(pcm);
 1263         hw_ptr = (str->alsa.appl_ptr - (str->alsa.buffer_size - avail)) % str->alsa.boundary;
 1264         diff = hw_ptr - str->alsa.old_hw_ptr;
 1265         if (diff < 0)
 1266             diff += str->alsa.boundary;
 1267         str->oss.hw_bytes += diff;
 1268         str->oss.hw_bytes %= str->oss.boundary;
 1269         info->bytes = (str->oss.hw_bytes * str->frame_bytes) & 0x7fffffff;
 1270         info->ptr = (str->oss.hw_bytes % str->oss.buffer_size) * str->frame_bytes;
 1271         if (str->mmap_buffer) {
 1272             ssize_t n = (hw_ptr / str->oss.period_size) - (str->alsa.old_hw_ptr / str->oss.period_size);
 1273             if (n < 0)
 1274                 n += str->alsa.boundary / str->oss.period_size;
 1275             info->blocks = n;
 1276         } else {
 1277             info->blocks = delay / str->oss.period_size;
 1278         }
 1279         str->alsa.old_hw_ptr = hw_ptr;
 1280         DEBUG("SNDCTL_DSP_GETOPTR, %p) -> {%d %d %d}\n", arg,
 1281               info->bytes,
 1282               info->blocks,
 1283               info->ptr);
 1284         break;
 1285     }
 1286     case SNDCTL_DSP_GETODELAY:
 1287     {
 1288         snd_pcm_sframes_t delay = 0;
 1289         snd_pcm_state_t state;
 1290         str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
 1291         pcm = str->pcm;
 1292         if (!pcm) {
 1293             err = -EINVAL;
 1294             break;
 1295         }
 1296         state = snd_pcm_state(pcm);
 1297         if (state == SND_PCM_STATE_SUSPENDED) {
 1298             err = resume(pcm);
 1299             if (err < 0)
 1300                 break;
 1301             state = snd_pcm_state(pcm);
 1302         }
 1303         if (state == SND_PCM_STATE_RUNNING || 
 1304             state == SND_PCM_STATE_DRAINING) {
 1305             snd_pcm_delay(pcm, &delay);
 1306             if (str->mmap_buffer)
 1307                 oss_dsp_mmap_update(dsp, SND_PCM_STREAM_PLAYBACK, delay);
 1308         }
 1309         *(int *)arg = delay * str->frame_bytes;
 1310         DEBUG("SNDCTL_DSP_GETODELAY, %p) -> [%d]\n", arg, *(int*)arg); 
 1311         break;
 1312     }
 1313     case SNDCTL_DSP_SETDUPLEX:
 1314         DEBUG("SNDCTL_DSP_SETDUPLEX)\n"); 
 1315         break;
 1316     case SOUND_PCM_READ_RATE:
 1317     {
 1318         *(int *)arg = dsp->rate;
 1319         DEBUG("SOUND_PCM_READ_RATE, %p) -> [%d]\n", arg, *(int*)arg); 
 1320         break;
 1321     }
 1322     case SOUND_PCM_READ_CHANNELS:
 1323     {
 1324         *(int *)arg = dsp->channels;
 1325         DEBUG("SOUND_PCM_READ_CHANNELS, %p) -> [%d]\n", arg, *(int*)arg); 
 1326         break;
 1327     }
 1328     case SOUND_PCM_READ_BITS:
 1329     {
 1330         *(int *)arg = snd_pcm_format_width(dsp->format);
 1331         DEBUG("SOUND_PCM_READ_BITS, %p) -> [%d]\n", arg, *(int*)arg); 
 1332         break;
 1333     }
 1334     case SNDCTL_DSP_MAPINBUF:
 1335         DEBUG("SNDCTL_DSP_MAPINBUF)\n");
 1336         err = -EINVAL;
 1337         break;
 1338     case SNDCTL_DSP_MAPOUTBUF:
 1339         DEBUG("SNDCTL_DSP_MAPOUTBUF)\n");
 1340         err = -EINVAL;
 1341         break;
 1342     case SNDCTL_DSP_SETSYNCRO:
 1343         DEBUG("SNDCTL_DSP_SETSYNCRO)\n");
 1344         err = -EINVAL;
 1345         break;
 1346     case SOUND_PCM_READ_FILTER:
 1347         DEBUG("SOUND_PCM_READ_FILTER)\n");
 1348         err = -EINVAL;
 1349         break;
 1350     case SOUND_PCM_WRITE_FILTER:
 1351         DEBUG("SOUND_PCM_WRITE_FILTER)\n");
 1352         err = -EINVAL;
 1353         break;
 1354     default:
 1355         DEBUG("%lx, %p)\n", cmd, arg);
 1356         // return oss_mixer_ioctl(...);
 1357         err = -ENXIO;
 1358         break;
 1359     }
 1360     if (err >= 0)
 1361         return 0;
 1362     DEBUG("dsp ioctl error = %d\n", err);
 1363     errno = -err;
 1364     return -1;
 1365 }
 1366 
 1367 int lib_oss_pcm_nonblock(int fd, int nonblock)
 1368 {
 1369     oss_dsp_t *dsp = look_for_dsp(fd);
 1370     int k;
 1371 
 1372     if (dsp == NULL) {
 1373         errno = EBADFD;
 1374         return -1;
 1375     }
 1376     for (k = 0; k < 2; ++k) {
 1377         snd_pcm_t *pcm = dsp->streams[k].pcm;
 1378         int err;
 1379         if (!pcm)
 1380             continue;
 1381         err = snd_pcm_nonblock(pcm, nonblock);
 1382         if (err < 0) {
 1383             errno = -err;
 1384             return -1;
 1385         }
 1386     }
 1387     return 0;
 1388 }
 1389 
 1390 void * lib_oss_pcm_mmap(void *addr ATTRIBUTE_UNUSED, size_t len ATTRIBUTE_UNUSED, int prot, int flags ATTRIBUTE_UNUSED, int fd, off_t offset ATTRIBUTE_UNUSED)
 1391 {
 1392     int err;
 1393     void *result;
 1394     oss_dsp_t *dsp = look_for_dsp(fd);
 1395     oss_dsp_stream_t *str;
 1396 
 1397     if (dsp == NULL) {
 1398         errno = -EBADFD;
 1399         return MAP_FAILED;
 1400     }
 1401     switch (prot & (PROT_READ | PROT_WRITE)) {
 1402     case PROT_READ:
 1403         str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
 1404         break;
 1405     case PROT_WRITE:
 1406         str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
 1407         break;
 1408     case PROT_READ | PROT_WRITE:
 1409         str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
 1410         if (!str->pcm)
 1411             str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
 1412         break;
 1413     default:
 1414         errno = EINVAL;
 1415         result = MAP_FAILED;
 1416         goto _end;
 1417     }
 1418     if (!str->pcm) {
 1419         errno = EBADFD;
 1420         result = MAP_FAILED;
 1421         goto _end;
 1422     }
 1423     assert(!str->mmap_buffer);
 1424     result = malloc(len);
 1425     if (!result) {
 1426         result = MAP_FAILED;
 1427         goto _end;
 1428     }
 1429     str->mmap_buffer = result;
 1430     str->mmap_bytes = len;
 1431     str->alsa.mmap_period_bytes = str->oss.period_size * str->frame_bytes;
 1432     str->alsa.mmap_buffer_bytes = str->oss.buffer_size * str->frame_bytes;
 1433     err = oss_dsp_params(dsp);
 1434     if (err < 0) {
 1435         free(result);
 1436         str->mmap_buffer = NULL;
 1437         str->mmap_bytes = 0;
 1438         errno = -err;
 1439         result = MAP_FAILED;
 1440         goto _end;
 1441     }
 1442  _end:
 1443     DEBUG("mmap(%p, %lu, %d, %d, %d, %ld) -> %p\n", addr, (unsigned long)len, prot, flags, fd, offset, result);
 1444     return result;
 1445 }
 1446 
 1447 int lib_oss_pcm_munmap(void *addr, size_t len)
 1448 {
 1449     int err;
 1450     oss_dsp_t *dsp = look_for_mmap_addr(addr);
 1451     oss_dsp_stream_t *str;
 1452 
 1453     if (dsp == NULL) {
 1454         errno = EBADFD;
 1455         return -1;
 1456     }
 1457     DEBUG("munmap(%p, %lu)\n", addr, (unsigned long)len);
 1458     str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
 1459     if (!str->pcm)
 1460         str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
 1461     assert(str->mmap_buffer);
 1462     free(str->mmap_buffer);
 1463     str->mmap_buffer = 0;
 1464     str->mmap_bytes = 0;
 1465     err = oss_dsp_params(dsp);
 1466     if (err < 0) {
 1467         errno = -err;
 1468         return -1;
 1469     }
 1470     return 0;
 1471 }
 1472 
 1473 static void set_oss_mmap_avail_min(oss_dsp_stream_t *str, int stream ATTRIBUTE_UNUSED, snd_pcm_t *pcm)
 1474 {
 1475     snd_pcm_uframes_t hw_ptr;
 1476     snd_pcm_sframes_t diff;
 1477 
 1478     hw_ptr = str->alsa.old_hw_ptr - 
 1479            (str->alsa.old_hw_ptr % str->oss.period_size) +
 1480            str->oss.period_size;
 1481     diff = hw_ptr - str->alsa.appl_ptr;
 1482     if (diff < 0)
 1483         diff += str->alsa.buffer_size;
 1484     if (diff < 1)
 1485         diff = 1;
 1486     //fprintf(stderr, "avail_min (%i): hw_ptr = %lu, appl_ptr = %lu, diff = %lu\n", stream, hw_ptr, str->alsa.appl_ptr, diff);
 1487     snd_pcm_sw_params_set_avail_min(pcm, str->sw_params, diff);
 1488     snd_pcm_sw_params(pcm, str->sw_params);
 1489 }
 1490 
 1491 int lib_oss_pcm_select_prepare(int fd, int fmode, fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
 1492 {
 1493     oss_dsp_t *dsp = look_for_dsp(fd);
 1494     int k, maxfd = -1;
 1495 
 1496     if (dsp == NULL) {
 1497         errno = EBADFD;
 1498         return -1;
 1499     }
 1500     for (k = 0; k < 2; ++k) {
 1501         oss_dsp_stream_t *str = &dsp->streams[k];
 1502         snd_pcm_t *pcm = str->pcm;
 1503         int err, count;
 1504         if (!pcm)
 1505             continue;
 1506         if ((fmode & O_ACCMODE) == O_RDONLY && snd_pcm_stream(pcm) == SND_PCM_STREAM_PLAYBACK)
 1507             continue;
 1508         if ((fmode & O_ACCMODE) == O_WRONLY && snd_pcm_stream(pcm) == SND_PCM_STREAM_CAPTURE)
 1509             continue;
 1510         if (str->mmap_buffer)
 1511             set_oss_mmap_avail_min(str, k, pcm);
 1512         count = snd_pcm_poll_descriptors_count(pcm);
 1513         if (count < 0) {
 1514             errno = -count;
 1515             return -1;
 1516         }
 1517         {
 1518             struct pollfd ufds[count];
 1519             int j;
 1520             err = snd_pcm_poll_descriptors(pcm, ufds, count);
 1521             if (err < 0) {
 1522                 errno = -err;
 1523                 return -1;
 1524             }
 1525             for (j = 0; j < count; j++) {
 1526                 int fd = ufds[j].fd;
 1527                 unsigned short events = ufds[j].events;
 1528                 if (maxfd < fd)
 1529                     maxfd = fd;
 1530                 if (readfds) {
 1531                     FD_CLR(fd, readfds);
 1532                     if (events & POLLIN)
 1533                         FD_SET(fd, readfds);
 1534                 }
 1535                 if (writefds) {
 1536                     FD_CLR(fd, writefds);
 1537                     if (events & POLLOUT)
 1538                         FD_SET(fd, writefds);
 1539                 }
 1540                 if (exceptfds) {
 1541                     FD_CLR(fd, exceptfds);
 1542                     if (events & (POLLERR|POLLNVAL))
 1543                         FD_SET(fd, exceptfds);
 1544                 }
 1545             }
 1546         }
 1547     }   
 1548     return maxfd;
 1549 }
 1550 
 1551 int lib_oss_pcm_select_result(int fd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
 1552 {
 1553     oss_dsp_t *dsp = look_for_dsp(fd);
 1554     int k, result = 0;
 1555 
 1556     if (dsp == NULL) {
 1557         errno = EBADFD;
 1558         return -1;
 1559     }
 1560     for (k = 0; k < 2; ++k) {
 1561         snd_pcm_t *pcm = dsp->streams[k].pcm;
 1562         int err, count;
 1563         if (!pcm)
 1564             continue;
 1565         count = snd_pcm_poll_descriptors_count(pcm);
 1566         if (count < 0) {
 1567             errno = -count;
 1568             return -1;
 1569         }
 1570         {
 1571             struct pollfd ufds[count];
 1572             int j;
 1573             unsigned short revents;
 1574             err = snd_pcm_poll_descriptors(pcm, ufds, count);
 1575             if (err < 0) {
 1576                 errno = -err;
 1577                 return -1;
 1578             }
 1579             for (j = 0; j < count; j++) {
 1580                 int fd = ufds[j].fd;
 1581                 revents = 0;
 1582                 if (readfds && FD_ISSET(fd, readfds))
 1583                     revents |= POLLIN;
 1584                 if (writefds && FD_ISSET(fd, writefds))
 1585                     revents |= POLLOUT;
 1586                 if (exceptfds && FD_ISSET(fd, exceptfds))
 1587                     revents |= POLLERR;
 1588                 ufds[j].revents = revents;
 1589             }
 1590             err = snd_pcm_poll_descriptors_revents(pcm, ufds, count, &revents);
 1591             if (err < 0) {
 1592                 errno = -err;
 1593                 return -1;
 1594             }
 1595             if (revents & (POLLNVAL|POLLERR))
 1596                 result |= OSS_WAIT_EVENT_ERROR;
 1597             if (revents & POLLIN)
 1598                 result |= OSS_WAIT_EVENT_READ;
 1599             if (revents & POLLOUT)
 1600                 result |= OSS_WAIT_EVENT_WRITE;
 1601         }
 1602     }   
 1603     return result;
 1604 }
 1605 
 1606 extern int lib_oss_pcm_poll_fds(int fd)
 1607 {
 1608     oss_dsp_t *dsp = look_for_dsp(fd);
 1609     int k, result = 0;
 1610 
 1611     if (dsp == NULL) {
 1612         errno = EBADFD;
 1613         return -1;
 1614     }
 1615     for (k = 0; k < 2; ++k) {
 1616         snd_pcm_t *pcm = dsp->streams[k].pcm;
 1617         int err;
 1618         if (!pcm)
 1619             continue;
 1620         err = snd_pcm_poll_descriptors_count(pcm);
 1621         if (err < 0) {
 1622             errno = -err;
 1623             return -1;
 1624         }
 1625         result += err;
 1626     }   
 1627     return result;
 1628 }
 1629 
 1630 int lib_oss_pcm_poll_prepare(int fd, int fmode, struct pollfd *ufds)
 1631 {
 1632     oss_dsp_t *dsp = look_for_dsp(fd);
 1633     int k, result = 0;
 1634 
 1635     if (dsp == NULL) {
 1636         errno = EBADFD;
 1637         return -1;
 1638     }
 1639     for (k = 0; k < 2; ++k) {
 1640         oss_dsp_stream_t *str = &dsp->streams[k];
 1641         snd_pcm_t *pcm = str->pcm;
 1642         int err, count;
 1643         if (!pcm)
 1644             continue;
 1645         if ((fmode & O_ACCMODE) == O_RDONLY && snd_pcm_stream(pcm) == SND_PCM_STREAM_PLAYBACK)
 1646             continue;
 1647         if ((fmode & O_ACCMODE) == O_WRONLY && snd_pcm_stream(pcm) == SND_PCM_STREAM_CAPTURE)
 1648             continue;
 1649         if (str->mmap_buffer)
 1650             set_oss_mmap_avail_min(str, k, pcm);
 1651         count = snd_pcm_poll_descriptors_count(pcm);
 1652         if (count < 0) {
 1653             errno = -count;
 1654             return -1;
 1655         }
 1656         err = snd_pcm_poll_descriptors(pcm, ufds, count);
 1657         if (err < 0) {
 1658             errno = -err;
 1659             return -1;
 1660         }
 1661         ufds += count;
 1662         result += count;
 1663     }   
 1664     return result;
 1665 }
 1666 
 1667 int lib_oss_pcm_poll_result(int fd, struct pollfd *ufds)
 1668 {
 1669     oss_dsp_t *dsp = look_for_dsp(fd);
 1670     int k, result = 0;
 1671 
 1672     if (dsp == NULL) {
 1673         errno = EBADFD;
 1674         return -1;
 1675     }
 1676     for (k = 0; k < 2; ++k) {
 1677         snd_pcm_t *pcm = dsp->streams[k].pcm;
 1678         int err, count;
 1679         unsigned short revents;
 1680         if (!pcm)
 1681             continue;
 1682         count = snd_pcm_poll_descriptors_count(pcm);
 1683         if (count < 0) {
 1684             errno = -count;
 1685             return -1;
 1686         }
 1687         err = snd_pcm_poll_descriptors_revents(pcm, ufds, count, &revents);
 1688         if (err < 0) {
 1689             errno = -err;
 1690             return -1;
 1691         }
 1692         if (revents & (POLLNVAL|POLLERR))
 1693             result |= OSS_WAIT_EVENT_ERROR;
 1694         if (revents & POLLIN)
 1695             result |= OSS_WAIT_EVENT_READ;
 1696         if (revents & POLLOUT)
 1697             result |= OSS_WAIT_EVENT_WRITE;
 1698         ufds += count;
 1699     }   
 1700     return result;
 1701 }
 1702 
 1703 
 1704 static void error_handler(const char *file ATTRIBUTE_UNUSED,
 1705               int line ATTRIBUTE_UNUSED,
 1706               const char *func ATTRIBUTE_UNUSED,
 1707               int err ATTRIBUTE_UNUSED,
 1708               const char *fmt ATTRIBUTE_UNUSED,
 1709               ...)
 1710 {
 1711     /* suppress the error message from alsa-lib */
 1712 }
 1713 
 1714 int lib_oss_pcm_open(const char *file, int oflag, ...)
 1715 {
 1716     int result;
 1717     int minor, card, device;
 1718     struct stat s;
 1719     mode_t mode;
 1720     va_list args;
 1721     va_start(args, oflag);
 1722     mode = va_arg(args, mode_t);
 1723     va_end(args);
 1724     result = stat(file, &s);
 1725     if (result < 0) {
 1726         if (!strncmp(file, "/dev/dsp", 8))
 1727             minor = (atoi(file + 8) << 4) | OSS_DEVICE_DSP;
 1728         else if (!strncmp(file, "/dev/dspW", 9))
 1729             minor = (atoi(file + 9) << 4) | OSS_DEVICE_DSPW;
 1730         else if (!strncmp(file, "/dev/adsp", 9))
 1731             minor = (atoi(file + 9) << 4) | OSS_DEVICE_ADSP;
 1732         else if (!strncmp(file, "/dev/audio", 10))
 1733             minor = (atoi(file + 10) << 4) | OSS_DEVICE_AUDIO;
 1734         else if (!strncmp(file, "/dev/sound/dsp", 14))
 1735             minor = (atoi(file + 14) << 4) | OSS_DEVICE_DSP;
 1736         else if (!strncmp(file, "/dev/sound/dspW", 15))
 1737             minor = (atoi(file + 15) << 4) | OSS_DEVICE_DSPW;
 1738         else if (!strncmp(file, "/dev/sound/adsp", 15))
 1739             minor = (atoi(file + 15) << 4) | OSS_DEVICE_ADSP;
 1740         else if (!strncmp(file, "/dev/sound/audio", 16))
 1741             minor = (atoi(file + 16) << 4) | OSS_DEVICE_AUDIO;
 1742         else {
 1743             errno = ENOENT;
 1744             return -1;
 1745         }
 1746     } else {
 1747         if (!S_ISCHR(s.st_mode) || ((s.st_rdev >> 8) & 0xff) != OSS_MAJOR) {
 1748             errno = ENOENT;
 1749             return -1;
 1750         }
 1751         minor = s.st_rdev & 0xff;
 1752     }
 1753     if (! alsa_oss_debug)
 1754         snd_lib_error_set_handler(error_handler);
 1755     card = minor >> 4;
 1756     device = minor & 0x0f;
 1757     switch (device) {
 1758     case OSS_DEVICE_DSP:
 1759     case OSS_DEVICE_DSPW:
 1760     case OSS_DEVICE_AUDIO:
 1761     case OSS_DEVICE_ADSP:
 1762         result = oss_dsp_open(card, device, oflag, mode);
 1763         DEBUG("open(\"%s\", %d, %d) -> %d\n", file, oflag, mode, result);
 1764         return result;
 1765     default:
 1766         errno = ENOENT;
 1767         return -1;
 1768     }
 1769 }