"Fossies" - the Fresh Open Source Software Archive

Member "alsa-lib-1.1.9/src/pcm/pcm_mmap.c" (10 May 2019, 18182 Bytes) of package /linux/misc/alsa-lib-1.1.9.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_mmap.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.1.5_vs_1.1.6.

    1 /*
    2  *  PCM Interface - mmap
    3  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
    4  *
    5  *   This library is free software; you can redistribute it and/or modify
    6  *   it under the terms of the GNU Lesser General Public License as
    7  *   published by the Free Software Foundation; either version 2.1 of
    8  *   the License, or (at your option) any later version.
    9  *
   10  *   This program is distributed in the hope that it will be useful,
   11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13  *   GNU Lesser General Public License for more details.
   14  *
   15  *   You should have received a copy of the GNU Lesser General Public
   16  *   License along with this library; if not, write to the Free Software
   17  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   18  *
   19  */
   20 
   21 #include "config.h"
   22 #include <stdio.h>
   23 #include <malloc.h>
   24 #include <string.h>
   25 #include <poll.h>
   26 #include <sys/mman.h>
   27 #ifdef HAVE_SYS_SHM_H
   28 #include <sys/shm.h>
   29 #endif
   30 #include "pcm_local.h"
   31 
   32 void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
   33 {
   34     snd_pcm_sframes_t appl_ptr = *pcm->appl.ptr;
   35     appl_ptr -= frames;
   36     if (appl_ptr < 0)
   37         appl_ptr += pcm->boundary;
   38     *pcm->appl.ptr = appl_ptr;
   39 }
   40 
   41 void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
   42 {
   43     snd_pcm_uframes_t appl_ptr = *pcm->appl.ptr;
   44     appl_ptr += frames;
   45     if (appl_ptr >= pcm->boundary)
   46         appl_ptr -= pcm->boundary;
   47     *pcm->appl.ptr = appl_ptr;
   48 }
   49 
   50 void snd_pcm_mmap_hw_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
   51 {
   52     snd_pcm_sframes_t hw_ptr = *pcm->hw.ptr;
   53     hw_ptr -= frames;
   54     if (hw_ptr < 0)
   55         hw_ptr += pcm->boundary;
   56     *pcm->hw.ptr = hw_ptr;
   57 }
   58 
   59 void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
   60 {
   61     snd_pcm_uframes_t hw_ptr = *pcm->hw.ptr;
   62     hw_ptr += frames;
   63     if (hw_ptr >= pcm->boundary)
   64         hw_ptr -= pcm->boundary;
   65     *pcm->hw.ptr = hw_ptr;
   66 }
   67 
   68 static snd_pcm_sframes_t snd_pcm_mmap_write_areas(snd_pcm_t *pcm,
   69                           const snd_pcm_channel_area_t *areas,
   70                           snd_pcm_uframes_t offset,
   71                           snd_pcm_uframes_t size)
   72 {
   73     snd_pcm_uframes_t xfer = 0;
   74 
   75     if (snd_pcm_mmap_playback_avail(pcm) < size) {
   76         SNDMSG("too short avail %ld to size %ld", snd_pcm_mmap_playback_avail(pcm), size);
   77         return -EPIPE;
   78     }
   79     while (size > 0) {
   80         const snd_pcm_channel_area_t *pcm_areas;
   81         snd_pcm_uframes_t pcm_offset;
   82         snd_pcm_uframes_t frames = size;
   83         snd_pcm_sframes_t result;
   84 
   85         __snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
   86         snd_pcm_areas_copy(pcm_areas, pcm_offset,
   87                    areas, offset, 
   88                    pcm->channels, 
   89                    frames, pcm->format);
   90         result = __snd_pcm_mmap_commit(pcm, pcm_offset, frames);
   91         if (result < 0)
   92             return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
   93         offset += result;
   94         xfer += result;
   95         size -= result;
   96     }
   97     return (snd_pcm_sframes_t)xfer;
   98 }
   99 
  100 static snd_pcm_sframes_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm,
  101                          const snd_pcm_channel_area_t *areas,
  102                          snd_pcm_uframes_t offset,
  103                          snd_pcm_uframes_t size)
  104 {
  105     snd_pcm_uframes_t xfer = 0;
  106 
  107     if (snd_pcm_mmap_capture_avail(pcm) < size) {
  108         SNDMSG("too short avail %ld to size %ld", snd_pcm_mmap_capture_avail(pcm), size);
  109         return -EPIPE;
  110     }
  111     while (size > 0) {
  112         const snd_pcm_channel_area_t *pcm_areas;
  113         snd_pcm_uframes_t pcm_offset;
  114         snd_pcm_uframes_t frames = size;
  115         snd_pcm_sframes_t result;
  116         
  117         __snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
  118         snd_pcm_areas_copy(areas, offset,
  119                    pcm_areas, pcm_offset,
  120                    pcm->channels, 
  121                    frames, pcm->format);
  122         result = __snd_pcm_mmap_commit(pcm, pcm_offset, frames);
  123         if (result < 0)
  124             return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
  125         offset += result;
  126         xfer += result;
  127         size -= result;
  128     }
  129     return (snd_pcm_sframes_t)xfer;
  130 }
  131 
  132 /**
  133  * \brief Write interleaved frames to a PCM using direct buffer (mmap)
  134  * \param pcm PCM handle
  135  * \param buffer frames containing buffer
  136  * \param size frames to be written
  137  * \return a positive number of frames actually written otherwise a
  138  * negative error code
  139  * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING)
  140  * \retval -EPIPE an underrun occurred
  141  * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
  142  *
  143  * If the blocking behaviour is selected, then routine waits until
  144  * all requested bytes are played or put to the playback ring buffer.
  145  * The count of bytes can be less only if a signal or underrun occurred.
  146  *
  147  * If the non-blocking behaviour is selected, then routine doesn't wait at all.
  148  */
  149 snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
  150 {
  151     snd_pcm_channel_area_t areas[pcm->channels];
  152     snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
  153     return snd_pcm_write_areas(pcm, areas, 0, size,
  154                    snd_pcm_mmap_write_areas);
  155 }
  156 
  157 /**
  158  * \brief Write non interleaved frames to a PCM using direct buffer (mmap)
  159  * \param pcm PCM handle
  160  * \param bufs frames containing buffers (one for each channel)
  161  * \param size frames to be written
  162  * \return a positive number of frames actually written otherwise a
  163  * negative error code
  164  * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING)
  165  * \retval -EPIPE an underrun occurred
  166  * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
  167  *
  168  * If the blocking behaviour is selected, then routine waits until
  169  * all requested bytes are played or put to the playback ring buffer.
  170  * The count of bytes can be less only if a signal or underrun occurred.
  171  *
  172  * If the non-blocking behaviour is selected, then routine doesn't wait at all.
  173  */
  174 snd_pcm_sframes_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
  175 {
  176     snd_pcm_channel_area_t areas[pcm->channels];
  177     snd_pcm_areas_from_bufs(pcm, areas, bufs);
  178     return snd_pcm_write_areas(pcm, areas, 0, size,
  179                    snd_pcm_mmap_write_areas);
  180 }
  181 
  182 /**
  183  * \brief Read interleaved frames from a PCM using direct buffer (mmap)
  184  * \param pcm PCM handle
  185  * \param buffer frames containing buffer
  186  * \param size frames to be written
  187  * \return a positive number of frames actually read otherwise a
  188  * negative error code
  189  * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING)
  190  * \retval -EPIPE an overrun occurred
  191  * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
  192  *
  193  * If the blocking behaviour was selected, then routine waits until
  194  * all requested bytes are filled. The count of bytes can be less only
  195  * if a signal or underrun occurred.
  196  *
  197  * If the non-blocking behaviour is selected, then routine doesn't wait at all.
  198  */
  199 snd_pcm_sframes_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
  200 {
  201     snd_pcm_channel_area_t areas[pcm->channels];
  202     snd_pcm_areas_from_buf(pcm, areas, buffer);
  203     return snd_pcm_read_areas(pcm, areas, 0, size,
  204                   snd_pcm_mmap_read_areas);
  205 }
  206 
  207 /**
  208  * \brief Read non interleaved frames to a PCM using direct buffer (mmap)
  209  * \param pcm PCM handle
  210  * \param bufs frames containing buffers (one for each channel)
  211  * \param size frames to be written
  212  * \return a positive number of frames actually read otherwise a
  213  * negative error code
  214  * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING)
  215  * \retval -EPIPE an overrun occurred
  216  * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
  217  *
  218  * If the blocking behaviour was selected, then routine waits until
  219  * all requested bytes are filled. The count of bytes can be less only
  220  * if a signal or underrun occurred.
  221  *
  222  * If the non-blocking behaviour is selected, then routine doesn't wait at all.
  223  */
  224 snd_pcm_sframes_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
  225 {
  226     snd_pcm_channel_area_t areas[pcm->channels];
  227     snd_pcm_areas_from_bufs(pcm, areas, bufs);
  228     return snd_pcm_read_areas(pcm, areas, 0, size,
  229                   snd_pcm_mmap_read_areas);
  230 }
  231 
  232 int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info, int shmid)
  233 {
  234     switch (pcm->access) {
  235     case SND_PCM_ACCESS_MMAP_INTERLEAVED:
  236     case SND_PCM_ACCESS_RW_INTERLEAVED:
  237         info->first = info->channel * pcm->sample_bits;
  238         info->step = pcm->frame_bits;
  239         break;
  240     case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
  241     case SND_PCM_ACCESS_RW_NONINTERLEAVED:
  242         info->first = 0;
  243         info->step = pcm->sample_bits;
  244         break;
  245     default:
  246         SNDMSG("invalid access type %d", pcm->access);
  247         return -EINVAL;
  248     }
  249     info->addr = 0;
  250     if (pcm->hw_flags & SND_PCM_HW_PARAMS_EXPORT_BUFFER) {
  251         info->type = SND_PCM_AREA_SHM;
  252         info->u.shm.shmid = shmid;
  253         info->u.shm.area = NULL;
  254     } else
  255         info->type = SND_PCM_AREA_LOCAL;
  256     return 0;
  257 }   
  258 
  259 int snd_pcm_mmap(snd_pcm_t *pcm)
  260 {
  261     int err;
  262     unsigned int c;
  263     assert(pcm);
  264     if (CHECK_SANITY(! pcm->setup)) {
  265         SNDMSG("PCM not set up");
  266         return -EIO;
  267     }
  268     if (CHECK_SANITY(pcm->mmap_channels || pcm->running_areas)) {
  269         SNDMSG("Already mmapped");
  270         return -EBUSY;
  271     }
  272     err = pcm->ops->mmap(pcm);
  273     if (err < 0)
  274         return err;
  275     if (pcm->mmap_shadow)
  276         return 0;
  277     pcm->mmap_channels = calloc(pcm->channels, sizeof(pcm->mmap_channels[0]));
  278     if (!pcm->mmap_channels)
  279         return -ENOMEM;
  280     pcm->running_areas = calloc(pcm->channels, sizeof(pcm->running_areas[0]));
  281     if (!pcm->running_areas) {
  282         free(pcm->mmap_channels);
  283         pcm->mmap_channels = NULL;
  284         return -ENOMEM;
  285     }
  286     for (c = 0; c < pcm->channels; ++c) {
  287         snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
  288         i->channel = c;
  289         err = snd_pcm_channel_info(pcm, i);
  290         if (err < 0) {
  291             free(pcm->mmap_channels);
  292             free(pcm->running_areas);
  293             pcm->mmap_channels = NULL;
  294             pcm->running_areas = NULL;
  295             return err;
  296         }
  297     }
  298     for (c = 0; c < pcm->channels; ++c) {
  299         snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
  300         snd_pcm_channel_area_t *a = &pcm->running_areas[c];
  301         char *ptr;
  302         size_t size;
  303         unsigned int c1;
  304         if (i->addr) {
  305                 a->addr = i->addr;
  306                 a->first = i->first;
  307                 a->step = i->step;
  308                 continue;
  309                 }
  310                 size = i->first + i->step * (pcm->buffer_size - 1) + pcm->sample_bits;
  311         for (c1 = c + 1; c1 < pcm->channels; ++c1) {
  312             snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
  313             size_t s;
  314             if (i1->type != i->type)
  315                 continue;
  316             switch (i1->type) {
  317             case SND_PCM_AREA_MMAP:
  318                 if (i1->u.mmap.fd != i->u.mmap.fd ||
  319                     i1->u.mmap.offset != i->u.mmap.offset)
  320                     continue;
  321                 break;
  322             case SND_PCM_AREA_SHM:
  323                 if (i1->u.shm.shmid != i->u.shm.shmid)
  324                     continue;
  325                 break;
  326             case SND_PCM_AREA_LOCAL:
  327                 break;
  328             default:
  329                 assert(0);
  330             }
  331             s = i1->first + i1->step * (pcm->buffer_size - 1) + pcm->sample_bits;
  332             if (s > size)
  333                 size = s;
  334         }
  335         size = (size + 7) / 8;
  336         size = page_align(size);
  337         switch (i->type) {
  338         case SND_PCM_AREA_MMAP:
  339             ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, i->u.mmap.fd, i->u.mmap.offset);
  340             if (ptr == MAP_FAILED) {
  341                 SYSERR("mmap failed");
  342                 return -errno;
  343             }
  344             i->addr = ptr;
  345             break;
  346         case SND_PCM_AREA_SHM:
  347 #ifdef HAVE_SYS_SHM_H
  348             if (i->u.shm.shmid < 0) {
  349                 int id;
  350                 /* FIXME: safer permission? */
  351                 id = shmget(IPC_PRIVATE, size, 0666);
  352                 if (id < 0) {
  353                     SYSERR("shmget failed");
  354                     return -errno;
  355                 }
  356                 i->u.shm.shmid = id;
  357                 ptr = shmat(i->u.shm.shmid, 0, 0);
  358                 if (ptr == (void *) -1) {
  359                     SYSERR("shmat failed");
  360                     return -errno;
  361                 }
  362                 /* automatically remove segment if not used */
  363                 if (shmctl(id, IPC_RMID, NULL) < 0){
  364                     SYSERR("shmctl mark remove failed");
  365                     return -errno;
  366                 }
  367                 i->u.shm.area = snd_shm_area_create(id, ptr);
  368                 if (i->u.shm.area == NULL) {
  369                     SYSERR("snd_shm_area_create failed");
  370                     return -ENOMEM;
  371                 }
  372                 if (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
  373                     pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
  374                     unsigned int c1;
  375                     for (c1 = c + 1; c1 < pcm->channels; c1++) {
  376                         snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
  377                         if (i1->u.shm.shmid < 0) {
  378                             i1->u.shm.shmid = id;
  379                             i1->u.shm.area = snd_shm_area_share(i->u.shm.area);
  380                         }
  381                     }
  382                 }
  383             } else {
  384                 ptr = shmat(i->u.shm.shmid, 0, 0);
  385                 if (ptr == (void*) -1) {
  386                     SYSERR("shmat failed");
  387                     return -errno;
  388                 }
  389             }
  390             i->addr = ptr;
  391             break;
  392 #else
  393             SYSERR("shm support not available");
  394             return -ENOSYS;
  395 #endif
  396         case SND_PCM_AREA_LOCAL:
  397             ptr = malloc(size);
  398             if (ptr == NULL) {
  399                 SYSERR("malloc failed");
  400                 return -errno;
  401             }
  402             i->addr = ptr;
  403             break;
  404         default:
  405             assert(0);
  406         }
  407         for (c1 = c + 1; c1 < pcm->channels; ++c1) {
  408             snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
  409             if (i1->type != i->type)
  410                 continue;
  411             switch (i1->type) {
  412             case SND_PCM_AREA_MMAP:
  413                 if (i1->u.mmap.fd != i->u.mmap.fd ||
  414                                     i1->u.mmap.offset != i->u.mmap.offset)
  415                     continue;
  416                 break;
  417             case SND_PCM_AREA_SHM:
  418                 if (i1->u.shm.shmid != i->u.shm.shmid)
  419                     continue;
  420                 /* fall through */
  421             case SND_PCM_AREA_LOCAL:
  422                 if (pcm->access != SND_PCM_ACCESS_MMAP_INTERLEAVED &&
  423                     pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED)
  424                         continue;
  425                 break;
  426             default:
  427                 assert(0);
  428             }
  429             i1->addr = i->addr;
  430         }
  431         a->addr = i->addr;
  432         a->first = i->first;
  433         a->step = i->step;
  434     }
  435     return 0;
  436 }
  437 
  438 int snd_pcm_munmap(snd_pcm_t *pcm)
  439 {
  440     int err;
  441     unsigned int c;
  442     assert(pcm);
  443     if (CHECK_SANITY(! pcm->mmap_channels)) {
  444         SNDMSG("Not mmapped");
  445         return -ENXIO;
  446     }
  447     if (pcm->mmap_shadow)
  448         return pcm->ops->munmap(pcm);
  449     for (c = 0; c < pcm->channels; ++c) {
  450         snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
  451         unsigned int c1;
  452         size_t size = i->first + i->step * (pcm->buffer_size - 1) + pcm->sample_bits;
  453         if (!i->addr)
  454             continue;
  455         for (c1 = c + 1; c1 < pcm->channels; ++c1) {
  456             snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
  457             size_t s;
  458             if (i1->addr != i->addr)
  459                 continue;
  460             i1->addr = NULL;
  461             s = i1->first + i1->step * (pcm->buffer_size - 1) + pcm->sample_bits;
  462             if (s > size)
  463                 size = s;
  464         }
  465         size = (size + 7) / 8;
  466         size = page_align(size);
  467         switch (i->type) {
  468         case SND_PCM_AREA_MMAP:
  469             err = munmap(i->addr, size);
  470             if (err < 0) {
  471                 SYSERR("mmap failed");
  472                 return -errno;
  473             }
  474             errno = 0;
  475             break;
  476         case SND_PCM_AREA_SHM:
  477 #ifdef HAVE_SYS_SHM_H
  478             if (i->u.shm.area) {
  479                 snd_shm_area_destroy(i->u.shm.area);
  480                 i->u.shm.area = NULL;
  481                 if (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
  482                     pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
  483                     unsigned int c1;
  484                     for (c1 = c + 1; c1 < pcm->channels; c1++) {
  485                         snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
  486                         if (i1->u.shm.area) {
  487                             snd_shm_area_destroy(i1->u.shm.area);
  488                             i1->u.shm.area = NULL;
  489                         }
  490                     }
  491                 }
  492             }
  493             break;
  494 #else
  495             SYSERR("shm support not available");
  496             return -ENOSYS;
  497 #endif
  498         case SND_PCM_AREA_LOCAL:
  499             free(i->addr);
  500             break;
  501         default:
  502             assert(0);
  503         }
  504         i->addr = NULL;
  505     }
  506     err = pcm->ops->munmap(pcm);
  507     if (err < 0)
  508         return err;
  509     free(pcm->mmap_channels);
  510     free(pcm->running_areas);
  511     pcm->mmap_channels = NULL;
  512     pcm->running_areas = NULL;
  513     return 0;
  514 }
  515 
  516 /* called in pcm lock */
  517 snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset,
  518                      snd_pcm_uframes_t size)
  519 {
  520     snd_pcm_uframes_t xfer = 0;
  521     snd_pcm_sframes_t err = 0;
  522     if (! size)
  523         return 0;
  524     while (xfer < size) {
  525         snd_pcm_uframes_t frames = size - xfer;
  526         snd_pcm_uframes_t cont = pcm->buffer_size - offset;
  527         if (cont < frames)
  528             frames = cont;
  529         switch (pcm->access) {
  530         case SND_PCM_ACCESS_MMAP_INTERLEAVED:
  531         {
  532             const snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm);
  533             const char *buf = snd_pcm_channel_area_addr(a, offset);
  534             snd_pcm_unlock(pcm); /* to avoid deadlock */
  535             err = _snd_pcm_writei(pcm, buf, frames);
  536             snd_pcm_lock(pcm);
  537             if (err >= 0)
  538                 frames = err;
  539             break;
  540         }
  541         case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
  542         {
  543             unsigned int channels = pcm->channels;
  544             unsigned int c;
  545             void *bufs[channels];
  546             const snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm);
  547             for (c = 0; c < channels; ++c) {
  548                 const snd_pcm_channel_area_t *a = &areas[c];
  549                 bufs[c] = snd_pcm_channel_area_addr(a, offset);
  550             }
  551             snd_pcm_unlock(pcm); /* to avoid deadlock */
  552             err = _snd_pcm_writen(pcm, bufs, frames);
  553             snd_pcm_lock(pcm);
  554             if (err >= 0)
  555                 frames = err;
  556             break;
  557         }
  558         default:
  559             SNDMSG("invalid access type %d", pcm->access);
  560             return -EINVAL;
  561         }
  562         if (err < 0)
  563             break;
  564         xfer += frames;
  565         offset = (offset + frames) % pcm->buffer_size;
  566     }
  567     if (xfer > 0)
  568         return xfer;
  569     return err;
  570 }
  571 
  572 /* called in pcm lock */
  573 snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset,
  574                     snd_pcm_uframes_t size)
  575 {
  576     snd_pcm_uframes_t xfer = 0;
  577     snd_pcm_sframes_t err = 0;
  578     if (! size)
  579         return 0;
  580     while (xfer < size) {
  581         snd_pcm_uframes_t frames = size - xfer;
  582         snd_pcm_uframes_t cont = pcm->buffer_size - offset;
  583         if (cont < frames)
  584             frames = cont;
  585         switch (pcm->access) {
  586         case SND_PCM_ACCESS_MMAP_INTERLEAVED:
  587         {
  588             const snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm);
  589             char *buf = snd_pcm_channel_area_addr(a, offset);
  590             snd_pcm_unlock(pcm); /* to avoid deadlock */
  591             err = _snd_pcm_readi(pcm, buf, frames);
  592             snd_pcm_lock(pcm);
  593             if (err >= 0)
  594                 frames = err;
  595             break;
  596         }
  597         case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
  598         {
  599             snd_pcm_uframes_t channels = pcm->channels;
  600             unsigned int c;
  601             void *bufs[channels];
  602             const snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm);
  603             for (c = 0; c < channels; ++c) {
  604                 const snd_pcm_channel_area_t *a = &areas[c];
  605                 bufs[c] = snd_pcm_channel_area_addr(a, offset);
  606             }
  607             snd_pcm_unlock(pcm); /* to avoid deadlock */
  608             err = _snd_pcm_readn(pcm->fast_op_arg, bufs, frames);
  609             snd_pcm_lock(pcm);
  610             if (err >= 0)
  611                 frames = err;
  612             break;
  613         }
  614         default:
  615             SNDMSG("invalid access type %d", pcm->access);
  616             return -EINVAL;
  617         }
  618         if (err < 0)
  619             break;
  620         xfer += frames;
  621         offset = (offset + frames) % pcm->buffer_size;
  622     }
  623     if (xfer > 0)
  624         return xfer;
  625     return err;
  626 }