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