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