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