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