• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
snd_pcm_mmap_appl_backward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)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 
snd_pcm_mmap_appl_forward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)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 
snd_pcm_mmap_hw_backward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)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 
snd_pcm_mmap_hw_forward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)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 
snd_pcm_mmap_write_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)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 
snd_pcm_mmap_read_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)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  */
snd_pcm_mmap_writei(snd_pcm_t * pcm,const void * buffer,snd_pcm_uframes_t size)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  */
snd_pcm_mmap_writen(snd_pcm_t * pcm,void ** bufs,snd_pcm_uframes_t size)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 read
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  */
snd_pcm_mmap_readi(snd_pcm_t * pcm,void * buffer,snd_pcm_uframes_t size)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  */
snd_pcm_mmap_readn(snd_pcm_t * pcm,void ** bufs,snd_pcm_uframes_t size)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 
snd_pcm_channel_info_shm(snd_pcm_t * pcm,snd_pcm_channel_info_t * info,int shmid)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 
snd_pcm_mmap(snd_pcm_t * pcm)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 	if (pcm->ops->mmap)
273 		err = pcm->ops->mmap(pcm);
274 	else
275 		err = -ENOSYS;
276 	if (err < 0)
277 		return err;
278 	if (pcm->mmap_shadow)
279 		return 0;
280 	pcm->mmap_channels = calloc(pcm->channels, sizeof(pcm->mmap_channels[0]));
281 	if (!pcm->mmap_channels)
282 		return -ENOMEM;
283 	pcm->running_areas = calloc(pcm->channels, sizeof(pcm->running_areas[0]));
284 	if (!pcm->running_areas) {
285 		free(pcm->mmap_channels);
286 		pcm->mmap_channels = NULL;
287 		return -ENOMEM;
288 	}
289 	for (c = 0; c < pcm->channels; ++c) {
290 		snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
291 		i->channel = c;
292 		err = snd_pcm_channel_info(pcm, i);
293 		if (err < 0) {
294 			free(pcm->mmap_channels);
295 			free(pcm->running_areas);
296 			pcm->mmap_channels = NULL;
297 			pcm->running_areas = NULL;
298 			return err;
299 		}
300 	}
301 	for (c = 0; c < pcm->channels; ++c) {
302 		snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
303 		snd_pcm_channel_area_t *a = &pcm->running_areas[c];
304 		char *ptr;
305 		size_t size;
306 		unsigned int c1;
307 		if (i->addr) {
308         		a->addr = i->addr;
309         		a->first = i->first;
310         		a->step = i->step;
311 		        continue;
312                 }
313                 size = i->first + i->step * (pcm->buffer_size - 1) + pcm->sample_bits;
314 		for (c1 = c + 1; c1 < pcm->channels; ++c1) {
315 			snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
316 			size_t s;
317 			if (i1->type != i->type)
318 				continue;
319 			switch (i1->type) {
320 			case SND_PCM_AREA_MMAP:
321 				if (i1->u.mmap.fd != i->u.mmap.fd ||
322 				    i1->u.mmap.offset != i->u.mmap.offset)
323 					continue;
324 				break;
325 			case SND_PCM_AREA_SHM:
326 				if (i1->u.shm.shmid != i->u.shm.shmid)
327 					continue;
328 				break;
329 			case SND_PCM_AREA_LOCAL:
330 				break;
331 			default:
332 				assert(0);
333 			}
334 			s = i1->first + i1->step * (pcm->buffer_size - 1) + pcm->sample_bits;
335 			if (s > size)
336 				size = s;
337 		}
338 		size = (size + 7) / 8;
339 		size = page_align(size);
340 		switch (i->type) {
341 		case SND_PCM_AREA_MMAP:
342 			ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, i->u.mmap.fd, i->u.mmap.offset);
343 			if (ptr == MAP_FAILED) {
344 				SYSERR("mmap failed");
345 				return -errno;
346 			}
347 			i->addr = ptr;
348 			break;
349 		case SND_PCM_AREA_SHM:
350 #ifdef HAVE_SYS_SHM_H
351 			if (i->u.shm.shmid < 0) {
352 				int id;
353 				/* FIXME: safer permission? */
354 				id = shmget(IPC_PRIVATE, size, 0666);
355 				if (id < 0) {
356 					SYSERR("shmget failed");
357 					return -errno;
358 				}
359 				i->u.shm.shmid = id;
360 				ptr = shmat(i->u.shm.shmid, 0, 0);
361 				if (ptr == (void *) -1) {
362 					SYSERR("shmat failed");
363 					return -errno;
364 				}
365 				/* automatically remove segment if not used */
366 				if (shmctl(id, IPC_RMID, NULL) < 0){
367 					SYSERR("shmctl mark remove failed");
368 					return -errno;
369 				}
370 				i->u.shm.area = snd_shm_area_create(id, ptr);
371 				if (i->u.shm.area == NULL) {
372 					SYSERR("snd_shm_area_create failed");
373 					return -ENOMEM;
374 				}
375 				if (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
376 				    pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
377 					unsigned int c1;
378 					for (c1 = c + 1; c1 < pcm->channels; c1++) {
379 						snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
380 						if (i1->u.shm.shmid < 0) {
381 							i1->u.shm.shmid = id;
382 							i1->u.shm.area = snd_shm_area_share(i->u.shm.area);
383 						}
384 					}
385 				}
386 			} else {
387 				ptr = shmat(i->u.shm.shmid, 0, 0);
388 				if (ptr == (void*) -1) {
389 					SYSERR("shmat failed");
390 					return -errno;
391 				}
392 			}
393 			i->addr = ptr;
394 			break;
395 #else
396 			SYSERR("shm support not available");
397 			return -ENOSYS;
398 #endif
399 		case SND_PCM_AREA_LOCAL:
400 			ptr = malloc(size);
401 			if (ptr == NULL) {
402 				SYSERR("malloc failed");
403 				return -errno;
404 			}
405 			i->addr = ptr;
406 			break;
407 		default:
408 			assert(0);
409 		}
410 		for (c1 = c + 1; c1 < pcm->channels; ++c1) {
411 			snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
412 			if (i1->type != i->type)
413 				continue;
414 			switch (i1->type) {
415 			case SND_PCM_AREA_MMAP:
416 				if (i1->u.mmap.fd != i->u.mmap.fd ||
417                                     i1->u.mmap.offset != i->u.mmap.offset)
418 					continue;
419 				break;
420 			case SND_PCM_AREA_SHM:
421 				if (i1->u.shm.shmid != i->u.shm.shmid)
422 					continue;
423 				/* fall through */
424 			case SND_PCM_AREA_LOCAL:
425 				if (pcm->access != SND_PCM_ACCESS_MMAP_INTERLEAVED &&
426 				    pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED)
427 				        continue;
428 				break;
429 			default:
430 				assert(0);
431 			}
432 			i1->addr = i->addr;
433 		}
434 		a->addr = i->addr;
435 		a->first = i->first;
436 		a->step = i->step;
437 	}
438 	return 0;
439 }
440 
snd_pcm_munmap(snd_pcm_t * pcm)441 int snd_pcm_munmap(snd_pcm_t *pcm)
442 {
443 	int err;
444 	unsigned int c;
445 	assert(pcm);
446 	if (CHECK_SANITY(! pcm->mmap_channels)) {
447 		SNDMSG("Not mmapped");
448 		return -ENXIO;
449 	}
450 	if (pcm->mmap_shadow) {
451 		if (pcm->ops->munmap)
452 			return pcm->ops->munmap(pcm);
453 		else
454 			return -ENOSYS;
455 	}
456 	for (c = 0; c < pcm->channels; ++c) {
457 		snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
458 		unsigned int c1;
459 		size_t size = i->first + i->step * (pcm->buffer_size - 1) + pcm->sample_bits;
460 		if (!i->addr)
461 			continue;
462 		for (c1 = c + 1; c1 < pcm->channels; ++c1) {
463 			snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
464 			size_t s;
465 			if (i1->addr != i->addr)
466 				continue;
467 			i1->addr = NULL;
468 			s = i1->first + i1->step * (pcm->buffer_size - 1) + pcm->sample_bits;
469 			if (s > size)
470 				size = s;
471 		}
472 		size = (size + 7) / 8;
473 		size = page_align(size);
474 		switch (i->type) {
475 		case SND_PCM_AREA_MMAP:
476 			err = munmap(i->addr, size);
477 			if (err < 0) {
478 				SYSERR("mmap failed");
479 				return -errno;
480 			}
481 			errno = 0;
482 			break;
483 		case SND_PCM_AREA_SHM:
484 #ifdef HAVE_SYS_SHM_H
485 			if (i->u.shm.area) {
486 				snd_shm_area_destroy(i->u.shm.area);
487 				i->u.shm.area = NULL;
488 				if (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
489 				    pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
490 					unsigned int c1;
491 					for (c1 = c + 1; c1 < pcm->channels; c1++) {
492 						snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
493 						if (i1->u.shm.area) {
494 							snd_shm_area_destroy(i1->u.shm.area);
495 							i1->u.shm.area = NULL;
496 						}
497 					}
498 				}
499 			}
500 			break;
501 #else
502 			SYSERR("shm support not available");
503 			return -ENOSYS;
504 #endif
505 		case SND_PCM_AREA_LOCAL:
506 			free(i->addr);
507 			break;
508 		default:
509 			assert(0);
510 		}
511 		i->addr = NULL;
512 	}
513 	if (pcm->ops->munmap)
514 		err = pcm->ops->munmap(pcm);
515 	else
516 		err = -ENOSYS;
517 	if (err < 0)
518 		return err;
519 	free(pcm->mmap_channels);
520 	free(pcm->running_areas);
521 	pcm->mmap_channels = NULL;
522 	pcm->running_areas = NULL;
523 	return 0;
524 }
525 
526 /* called in pcm lock */
snd_pcm_write_mmap(snd_pcm_t * pcm,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)527 snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset,
528 				     snd_pcm_uframes_t size)
529 {
530 	snd_pcm_uframes_t xfer = 0;
531 	snd_pcm_sframes_t err = 0;
532 	if (! size)
533 		return 0;
534 	while (xfer < size) {
535 		snd_pcm_uframes_t frames = size - xfer;
536 		snd_pcm_uframes_t cont = pcm->buffer_size - offset;
537 		if (cont < frames)
538 			frames = cont;
539 		switch (pcm->access) {
540 		case SND_PCM_ACCESS_MMAP_INTERLEAVED:
541 		{
542 			const snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm);
543 			const char *buf = snd_pcm_channel_area_addr(a, offset);
544 			snd_pcm_unlock(pcm); /* to avoid deadlock */
545 			err = _snd_pcm_writei(pcm, buf, frames);
546 			snd_pcm_lock(pcm);
547 			if (err >= 0)
548 				frames = err;
549 			break;
550 		}
551 		case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
552 		{
553 			unsigned int channels = pcm->channels;
554 			unsigned int c;
555 			void *bufs[channels];
556 			const snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm);
557 			for (c = 0; c < channels; ++c) {
558 				const snd_pcm_channel_area_t *a = &areas[c];
559 				bufs[c] = snd_pcm_channel_area_addr(a, offset);
560 			}
561 			snd_pcm_unlock(pcm); /* to avoid deadlock */
562 			err = _snd_pcm_writen(pcm, bufs, frames);
563 			snd_pcm_lock(pcm);
564 			if (err >= 0)
565 				frames = err;
566 			break;
567 		}
568 		default:
569 			SNDMSG("invalid access type %d", pcm->access);
570 			return -EINVAL;
571 		}
572 		if (err < 0)
573 			break;
574 		xfer += frames;
575 		offset = (offset + frames) % pcm->buffer_size;
576 	}
577 	if (xfer > 0)
578 		return xfer;
579 	return err;
580 }
581 
582 /* called in pcm lock */
snd_pcm_read_mmap(snd_pcm_t * pcm,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)583 snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset,
584 				    snd_pcm_uframes_t size)
585 {
586 	snd_pcm_uframes_t xfer = 0;
587 	snd_pcm_sframes_t err = 0;
588 	if (! size)
589 		return 0;
590 	while (xfer < size) {
591 		snd_pcm_uframes_t frames = size - xfer;
592 		snd_pcm_uframes_t cont = pcm->buffer_size - offset;
593 		if (cont < frames)
594 			frames = cont;
595 		switch (pcm->access) {
596 		case SND_PCM_ACCESS_MMAP_INTERLEAVED:
597 		{
598 			const snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm);
599 			char *buf = snd_pcm_channel_area_addr(a, offset);
600 			snd_pcm_unlock(pcm); /* to avoid deadlock */
601 			err = _snd_pcm_readi(pcm, buf, frames);
602 			snd_pcm_lock(pcm);
603 			if (err >= 0)
604 				frames = err;
605 			break;
606 		}
607 		case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
608 		{
609 			snd_pcm_uframes_t channels = pcm->channels;
610 			unsigned int c;
611 			void *bufs[channels];
612 			const snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm);
613 			for (c = 0; c < channels; ++c) {
614 				const snd_pcm_channel_area_t *a = &areas[c];
615 				bufs[c] = snd_pcm_channel_area_addr(a, offset);
616 			}
617 			snd_pcm_unlock(pcm); /* to avoid deadlock */
618 			err = _snd_pcm_readn(pcm->fast_op_arg, bufs, frames);
619 			snd_pcm_lock(pcm);
620 			if (err >= 0)
621 				frames = err;
622 			break;
623 		}
624 		default:
625 			SNDMSG("invalid access type %d", pcm->access);
626 			return -EINVAL;
627 		}
628 		if (err < 0)
629 			break;
630 		xfer += frames;
631 		offset = (offset + frames) % pcm->buffer_size;
632 	}
633 	if (xfer > 0)
634 		return xfer;
635 	return err;
636 }
637