2 * \file pcm/pcm_mmap_emul.c
4 * \brief PCM Mmap-Emulation Plugin Interface
5 * \author Takashi Iwai <tiwai@suse.de>
10 * Copyright (c) 2007 by Takashi Iwai <tiwai@suse.de>
13 * This library is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser General Public License as
15 * published by the Free Software Foundation; either version 2.1 of
16 * the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "pcm_local.h"
30 #include "pcm_generic.h"
33 /* entry for static linking */
34 const char *_snd_module_pcm_mmap_emul = "";
43 snd_pcm_generic_t gen;
44 unsigned int mmap_emul :1;
45 snd_pcm_uframes_t hw_ptr;
46 snd_pcm_uframes_t appl_ptr;
47 snd_pcm_uframes_t start_threshold;
52 * here goes a really tricky part; hw_refine falls back to ACCESS_RW_* type
53 * when ACCESS_MMAP_* isn't supported by the hardware.
55 static int snd_pcm_mmap_emul_hw_refine(snd_pcm_t *pcm,
56 snd_pcm_hw_params_t *params)
58 mmap_emul_t *map = pcm->private_data;
60 snd_pcm_access_mask_t oldmask =
61 *snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
62 snd_pcm_access_mask_t mask;
63 const snd_mask_t *pmask;
66 err = snd_pcm_hw_refine(map->gen.slave, params);
68 snd_pcm_hw_params_t new = *params;
71 if (snd_pcm_access_mask_test(&oldmask,
72 SND_PCM_ACCESS_MMAP_INTERLEAVED) &&
73 !snd_pcm_access_mask_test(&oldmask,
74 SND_PCM_ACCESS_RW_INTERLEAVED))
75 snd_pcm_access_mask_set(&mask,
76 SND_PCM_ACCESS_RW_INTERLEAVED);
77 if (snd_pcm_access_mask_test(&oldmask,
78 SND_PCM_ACCESS_MMAP_NONINTERLEAVED) &&
79 !snd_pcm_access_mask_test(&oldmask,
80 SND_PCM_ACCESS_RW_NONINTERLEAVED))
81 snd_pcm_access_mask_set(&mask,
82 SND_PCM_ACCESS_RW_NONINTERLEAVED);
83 if (snd_pcm_access_mask_empty(&mask))
85 pmask = snd_pcm_hw_param_get_mask(&new,
86 SND_PCM_HW_PARAM_ACCESS);
87 *(snd_mask_t *)pmask = mask;
88 err = snd_pcm_hw_refine(map->gen.slave, &new);
94 pmask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
95 if (snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
96 snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) ||
97 snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_MMAP_COMPLEX))
99 if (snd_pcm_access_mask_test(&mask, SND_PCM_ACCESS_RW_INTERLEAVED)) {
100 if (snd_pcm_access_mask_test(pmask,
101 SND_PCM_ACCESS_RW_INTERLEAVED))
102 snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask,
103 SND_PCM_ACCESS_MMAP_INTERLEAVED);
104 snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask,
105 SND_PCM_ACCESS_RW_INTERLEAVED);
106 params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
108 if (snd_pcm_access_mask_test(&mask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
109 if (snd_pcm_access_mask_test(pmask,
110 SND_PCM_ACCESS_RW_NONINTERLEAVED))
111 snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask,
112 SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
113 snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask,
114 SND_PCM_ACCESS_RW_NONINTERLEAVED);
115 params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
117 if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
118 if (snd_pcm_access_mask_test(&oldmask,
119 SND_PCM_ACCESS_RW_INTERLEAVED)) {
120 if (snd_pcm_access_mask_test(pmask,
121 SND_PCM_ACCESS_RW_INTERLEAVED)) {
122 snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask,
123 SND_PCM_ACCESS_MMAP_INTERLEAVED);
124 params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
128 if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
129 if (snd_pcm_access_mask_test(&oldmask,
130 SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
131 if (snd_pcm_access_mask_test(pmask,
132 SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
133 snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask,
134 SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
135 params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
143 * hw_params needs a similar hack like hw_refine, but it's much simpler
144 * because now snd_pcm_hw_params_t takes only one choice for each item.
146 * Here, when the normal hw_params call fails, it turns on the mmap_emul
147 * flag and tries to use ACCESS_RW_* mode.
149 * In mmap_emul mode, the appl_ptr and hw_ptr are handled individually
150 * from the layering slave PCM, and they are sync'ed appropriately in
151 * each read/write or avail_update/commit call.
153 static int snd_pcm_mmap_emul_hw_params(snd_pcm_t *pcm,
154 snd_pcm_hw_params_t *params)
156 mmap_emul_t *map = pcm->private_data;
157 snd_pcm_hw_params_t old = *params;
158 snd_pcm_access_t access;
159 snd_pcm_access_mask_t oldmask;
160 snd_pcm_access_mask_t *pmask;
163 err = _snd_pcm_hw_params(map->gen.slave, params);
170 pmask = (snd_pcm_access_mask_t *)snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
172 if (INTERNAL(snd_pcm_hw_params_get_access)(params, &access) < 0)
175 case SND_PCM_ACCESS_MMAP_INTERLEAVED:
176 snd_pcm_access_mask_reset(pmask,
177 SND_PCM_ACCESS_MMAP_INTERLEAVED);
178 snd_pcm_access_mask_set(pmask, SND_PCM_ACCESS_RW_INTERLEAVED);
180 case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
181 snd_pcm_access_mask_reset(pmask,
182 SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
183 snd_pcm_access_mask_set(pmask,
184 SND_PCM_ACCESS_RW_NONINTERLEAVED);
189 err = _snd_pcm_hw_params(map->gen.slave, params);
193 /* need to back the access type to relieve apps */
200 snd_pcm_set_hw_ptr(pcm, &map->hw_ptr, -1, 0);
201 snd_pcm_set_appl_ptr(pcm, &map->appl_ptr, -1, 0);
209 static int snd_pcm_mmap_emul_sw_params(snd_pcm_t *pcm,
210 snd_pcm_sw_params_t *params)
212 mmap_emul_t *map = pcm->private_data;
216 return snd_pcm_generic_sw_params(pcm, params);
218 map->start_threshold = params->start_threshold;
220 /* HACK: don't auto-start in the slave PCM */
221 params->start_threshold = pcm->boundary;
222 err = snd_pcm_generic_sw_params(pcm, params);
225 /* restore the value for this PCM */
226 params->start_threshold = map->start_threshold;
230 static int snd_pcm_mmap_emul_prepare(snd_pcm_t *pcm)
232 mmap_emul_t *map = pcm->private_data;
235 err = snd_pcm_generic_prepare(pcm);
238 map->hw_ptr = map->appl_ptr = 0;
242 static int snd_pcm_mmap_emul_reset(snd_pcm_t *pcm)
244 mmap_emul_t *map = pcm->private_data;
247 err = snd_pcm_generic_reset(pcm);
250 map->hw_ptr = map->appl_ptr = 0;
254 static snd_pcm_sframes_t
255 snd_pcm_mmap_emul_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
257 frames = snd_pcm_generic_rewind(pcm, frames);
259 snd_pcm_mmap_appl_backward(pcm, frames);
263 static snd_pcm_sframes_t
264 snd_pcm_mmap_emul_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
266 frames = snd_pcm_generic_forward(pcm, frames);
268 snd_pcm_mmap_appl_forward(pcm, frames);
272 /* write out the uncommitted chunk on mmap buffer to the slave PCM */
273 static snd_pcm_sframes_t
274 sync_slave_write(snd_pcm_t *pcm)
276 mmap_emul_t *map = pcm->private_data;
277 snd_pcm_t *slave = map->gen.slave;
278 snd_pcm_uframes_t offset;
279 snd_pcm_sframes_t size;
281 /* HACK: don't start stream automatically at commit in mmap mode */
282 pcm->start_threshold = pcm->boundary;
284 size = map->appl_ptr - *slave->appl.ptr;
286 size += pcm->boundary;
288 offset = *slave->appl.ptr % pcm->buffer_size;
289 size = snd_pcm_write_mmap(pcm, offset, size);
291 pcm->start_threshold = map->start_threshold; /* restore */
295 /* read the available chunk on the slave PCM to mmap buffer */
296 static snd_pcm_sframes_t
297 sync_slave_read(snd_pcm_t *pcm)
299 mmap_emul_t *map = pcm->private_data;
300 snd_pcm_t *slave = map->gen.slave;
301 snd_pcm_uframes_t offset;
302 snd_pcm_sframes_t size;
304 size = *slave->hw.ptr - map->hw_ptr;
306 size += pcm->boundary;
309 offset = map->hw_ptr % pcm->buffer_size;
310 size = snd_pcm_read_mmap(pcm, offset, size);
312 snd_pcm_mmap_hw_forward(pcm, size);
316 static snd_pcm_sframes_t
317 snd_pcm_mmap_emul_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset,
318 snd_pcm_uframes_t size)
320 mmap_emul_t *map = pcm->private_data;
321 snd_pcm_t *slave = map->gen.slave;
323 snd_pcm_mmap_appl_forward(pcm, size);
325 return snd_pcm_mmap_commit(slave, offset, size);
326 if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
327 sync_slave_write(pcm);
331 static snd_pcm_sframes_t snd_pcm_mmap_emul_avail_update(snd_pcm_t *pcm)
333 mmap_emul_t *map = pcm->private_data;
334 snd_pcm_t *slave = map->gen.slave;
335 snd_pcm_sframes_t avail;
337 avail = snd_pcm_avail_update(slave);
338 if (!map->mmap_emul || pcm->stream == SND_PCM_STREAM_PLAYBACK)
339 map->hw_ptr = *slave->hw.ptr;
341 sync_slave_read(pcm);
342 return snd_pcm_mmap_avail(pcm);
345 static void snd_pcm_mmap_emul_dump(snd_pcm_t *pcm, snd_output_t *out)
347 mmap_emul_t *map = pcm->private_data;
349 snd_output_printf(out, "Mmap emulation PCM\n");
351 snd_output_printf(out, "Its setup is:\n");
352 snd_pcm_dump_setup(pcm, out);
354 snd_output_printf(out, "Slave: ");
355 snd_pcm_dump(map->gen.slave, out);
358 static const snd_pcm_ops_t snd_pcm_mmap_emul_ops = {
359 .close = snd_pcm_generic_close,
360 .info = snd_pcm_generic_info,
361 .hw_refine = snd_pcm_mmap_emul_hw_refine,
362 .hw_params = snd_pcm_mmap_emul_hw_params,
363 .hw_free = snd_pcm_generic_hw_free,
364 .sw_params = snd_pcm_mmap_emul_sw_params,
365 .channel_info = snd_pcm_generic_channel_info,
366 .dump = snd_pcm_mmap_emul_dump,
367 .nonblock = snd_pcm_generic_nonblock,
368 .async = snd_pcm_generic_async,
369 .mmap = snd_pcm_generic_mmap,
370 .munmap = snd_pcm_generic_munmap,
373 static const snd_pcm_fast_ops_t snd_pcm_mmap_emul_fast_ops = {
374 .status = snd_pcm_generic_status,
375 .state = snd_pcm_generic_state,
376 .hwsync = snd_pcm_generic_hwsync,
377 .delay = snd_pcm_generic_delay,
378 .prepare = snd_pcm_mmap_emul_prepare,
379 .reset = snd_pcm_mmap_emul_reset,
380 .start = snd_pcm_generic_start,
381 .drop = snd_pcm_generic_drop,
382 .drain = snd_pcm_generic_drain,
383 .pause = snd_pcm_generic_pause,
384 .rewindable = snd_pcm_generic_rewindable,
385 .rewind = snd_pcm_mmap_emul_rewind,
386 .forwardable = snd_pcm_generic_forwardable,
387 .forward = snd_pcm_mmap_emul_forward,
388 .resume = snd_pcm_generic_resume,
389 .link = snd_pcm_generic_link,
390 .link_slaves = snd_pcm_generic_link_slaves,
391 .unlink = snd_pcm_generic_unlink,
392 .writei = snd_pcm_generic_writei,
393 .writen = snd_pcm_generic_writen,
394 .readi = snd_pcm_generic_readi,
395 .readn = snd_pcm_generic_readn,
396 .avail_update = snd_pcm_mmap_emul_avail_update,
397 .mmap_commit = snd_pcm_mmap_emul_mmap_commit,
398 .htimestamp = snd_pcm_generic_htimestamp,
399 .poll_descriptors = snd_pcm_generic_poll_descriptors,
400 .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
401 .poll_revents = snd_pcm_generic_poll_revents,
405 int __snd_pcm_mmap_emul_open(snd_pcm_t **pcmp, const char *name,
406 snd_pcm_t *slave, int close_slave)
412 map = calloc(1, sizeof(*map));
415 map->gen.slave = slave;
416 map->gen.close_slave = close_slave;
418 err = snd_pcm_new(&pcm, SND_PCM_TYPE_MMAP_EMUL, name,
419 slave->stream, slave->mode);
424 pcm->ops = &snd_pcm_mmap_emul_ops;
425 pcm->fast_ops = &snd_pcm_mmap_emul_fast_ops;
426 pcm->private_data = map;
427 pcm->poll_fd = slave->poll_fd;
428 pcm->poll_events = slave->poll_events;
429 pcm->monotonic = slave->monotonic;
430 snd_pcm_set_hw_ptr(pcm, &map->hw_ptr, -1, 0);
431 snd_pcm_set_appl_ptr(pcm, &map->appl_ptr, -1, 0);
438 /*! \page pcm_plugins
440 \section pcm_plugins_mmap_emul Plugin: mmap_emul
449 \subsection pcm_plugins_mmap_emul_funcref Function reference
452 <LI>_snd_pcm_hw_open()
458 * \brief Creates a new mmap_emul PCM
459 * \param pcmp Returns created PCM handle
460 * \param name Name of PCM
461 * \param root Root configuration node
462 * \param conf Configuration node with hw PCM description
463 * \param stream PCM Stream
464 * \param mode PCM Mode
465 * \warning Using of this function might be dangerous in the sense
466 * of compatibility reasons. The prototype might be freely
469 int _snd_pcm_mmap_emul_open(snd_pcm_t **pcmp, const char *name,
470 snd_config_t *root ATTRIBUTE_UNUSED,
472 snd_pcm_stream_t stream, int mode)
474 snd_config_iterator_t i, next;
477 snd_config_t *slave = NULL, *sconf;
479 snd_config_for_each(i, next, conf) {
480 snd_config_t *n = snd_config_iterator_entry(i);
482 if (snd_config_get_id(n, &id) < 0)
484 if (snd_pcm_conf_generic_id(id))
486 if (strcmp(id, "slave") == 0) {
490 SNDERR("Unknown field %s", id);
494 SNDERR("slave is not defined");
497 err = snd_pcm_slave_conf(root, slave, &sconf, 0);
500 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
501 snd_config_delete(sconf);
504 err = __snd_pcm_mmap_emul_open(pcmp, name, spcm, 1);
511 SND_DLSYM_BUILD_VERSION(_snd_pcm_mmap_emul_open, SND_PCM_DLSYM_VERSION);