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