From: Takashi Iwai Date: Wed, 11 Jul 2007 15:44:09 +0000 (+0200) Subject: Split mmap-emulation code from hw layer X-Git-Tag: v1.0.15rc1~16 X-Git-Url: https://git.alsa-project.org/?a=commitdiff_plain;h=4cdb17c6012626580b98859c29a86c9d22e926d0;p=alsa-lib.git Split mmap-emulation code from hw layer Move out mmap-emulation code from hw layer to its own plugin. This cleans up the mess in pcm_hw.c. --- diff --git a/configure.in b/configure.in index 9e0c57e5..21a6dc3d 100644 --- a/configure.in +++ b/configure.in @@ -397,7 +397,7 @@ else pcm_plugins="" fi -PCM_PLUGIN_LIST="copy linear route mulaw alaw adpcm rate plug multi shm file null empty share meter hooks lfloat ladspa dmix dshare dsnoop asym iec958 softvol extplug ioplug" +PCM_PLUGIN_LIST="copy linear route mulaw alaw adpcm rate plug multi shm file null empty share meter hooks lfloat ladspa dmix dshare dsnoop asym iec958 softvol extplug ioplug mmap_emul" build_pcm_plugin="no" for t in $PCM_PLUGIN_LIST; do @@ -466,6 +466,7 @@ AM_CONDITIONAL(BUILD_PCM_PLUGIN_IEC958, test x$build_pcm_iec958 = xyes) AM_CONDITIONAL(BUILD_PCM_PLUGIN_SOFTVOL, test x$build_pcm_softvol = xyes) AM_CONDITIONAL(BUILD_PCM_PLUGIN_EXTPLUG, test x$build_pcm_extplug = xyes) AM_CONDITIONAL(BUILD_PCM_PLUGIN_IOPLUG, test x$build_pcm_ioplug = xyes) +AM_CONDITIONAL(BUILD_PCM_PLUGIN_MMAP_EMUL, test x$build_pcm_mmap_emul = xyes) dnl Defines for plug plugin if test "$build_pcm_rate" = "yes"; then diff --git a/doc/doxygen.cfg b/doc/doxygen.cfg index 2ae56627..ac705752 100644 --- a/doc/doxygen.cfg +++ b/doc/doxygen.cfg @@ -45,6 +45,7 @@ INPUT = index.doxygen \ ../src/pcm/pcm_mmap.c \ ../src/pcm/pcm_plugin.c \ ../src/pcm/pcm_hw.c \ + ../src/pcm/pcm_mmap_emul.c \ ../src/pcm/pcm_shm.c \ ../src/pcm/pcm_null.c \ ../src/pcm/pcm_copy.c \ diff --git a/include/pcm.h b/include/pcm.h index 23b98aac..1c84c9c3 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -362,7 +362,9 @@ enum _snd_pcm_type { SND_PCM_TYPE_IOPLUG, /** External filter plugin */ SND_PCM_TYPE_EXTPLUG, - SND_PCM_TYPE_LAST = SND_PCM_TYPE_EXTPLUG + /** Mmap-emulation plugin */ + SND_PCM_TYPE_MMAP_EMUL, + SND_PCM_TYPE_LAST = SND_PCM_TYPE_MMAP_EMUL }; /** PCM type */ diff --git a/src/pcm/Makefile.am b/src/pcm/Makefile.am index 95e7ad04..28faa542 100644 --- a/src/pcm/Makefile.am +++ b/src/pcm/Makefile.am @@ -99,6 +99,9 @@ endif if BUILD_PCM_PLUGIN_IOPLUG libpcm_la_SOURCES += pcm_ioplug.c endif +if BUILD_PCM_PLUGIN_MMAP_EMUL +libpcm_la_SOURCES += pcm_mmap_emul.c +endif EXTRA_DIST = pcm_dmix_i386.c pcm_dmix_x86_64.c pcm_dmix_generic.c diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 4c72d1c2..e259971a 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -1983,7 +1983,8 @@ snd_pcm_t *snd_async_handler_get_pcm(snd_async_handler_t *handler) static char *build_in_pcms[] = { "adpcm", "alaw", "copy", "dmix", "file", "hooks", "hw", "ladspa", "lfloat", "linear", "meter", "mulaw", "multi", "null", "empty", "plug", "rate", "route", "share", - "shm", "dsnoop", "dshare", "asym", "iec958", "softvol", NULL + "shm", "dsnoop", "dshare", "asym", "iec958", "softvol", "mmap_emul", + NULL }; static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name, diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index e864e930..ccfceadc 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -88,14 +88,10 @@ typedef struct { int version; int fd; int card, device, subdevice; - int mmap_emulation; int sync_ptr_ioctl; volatile struct sndrv_pcm_mmap_status * mmap_status; struct sndrv_pcm_mmap_control *mmap_control; struct sndrv_pcm_sync_ptr *sync_ptr; - int shadow_appl_ptr: 1, - avail_update_flag: 1, - mmap_shm: 1; snd_pcm_uframes_t hw_ptr; snd_pcm_uframes_t appl_ptr; /* restricted parameters */ @@ -109,9 +105,6 @@ typedef struct { #define SNDRV_PCM_VERSION_MAX SNDRV_PROTOCOL_VERSION(2, 0, 5) /* update appl_ptr with driver */ -#define UPDATE_SHADOW_PTR(hw) \ - do { if (hw->shadow_appl_ptr && !hw->avail_update_flag) \ - hw->appl_ptr = hw->mmap_control->appl_ptr; } while (0) #define FAST_PCM_STATE(hw) \ ((enum sndrv_pcm_state) (hw)->mmap_status->state) #define FAST_PCM_TSTAMP(hw) \ @@ -247,73 +240,10 @@ static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) return err; } - if (hw->mmap_emulation) { - int err = 0; - snd_pcm_access_mask_t oldmask = *snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS); - snd_pcm_access_mask_t mask; - const snd_mask_t *pmask; - - snd_mask_empty(&mask); - if (hw_refine_call(hw, params) < 0) - err = -errno; - if (err < 0) { - snd_pcm_hw_params_t new = *params; - - if (!(params->rmask & (1<cmask |= 1<cmask |= 1<cmask |= 1<cmask |= 1<private_data; int err; - if (hw->mmap_emulation) { - snd_pcm_hw_params_t old = *params; - if (hw_params_call(hw, params) < 0) { - snd_pcm_access_t access; - snd_pcm_access_mask_t oldmask; - const snd_mask_t *pmask; - - *params = old; - pmask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS); - oldmask = *(snd_pcm_access_mask_t *)pmask; - if (INTERNAL(snd_pcm_hw_params_get_access)(params, &access) < 0) - goto _err; - switch (access) { - case SND_PCM_ACCESS_MMAP_INTERLEAVED: - snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_INTERLEAVED); - snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_RW_INTERLEAVED); - break; - case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: - snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); - snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_RW_NONINTERLEAVED); - break; - default: - goto _err; - } - if (hw_params_call(hw, params) < 0) - goto _err; - hw->mmap_shm = 1; - *(snd_pcm_access_mask_t *)pmask = oldmask; - } - } else { - if (hw_params_call(hw, params) < 0) { - _err: - err = -errno; - SYSMSG("SNDRV_PCM_IOCTL_HW_PARAMS failed"); - return err; - } + if (hw_params_call(hw, params) < 0) { + err = -errno; + SYSMSG("SNDRV_PCM_IOCTL_HW_PARAMS failed"); + return err; } err = sync_ptr(hw, 0); if (err < 0) return err; if (pcm->stream == SND_PCM_STREAM_CAPTURE) { - if (hw->mmap_shm) { - hw->shadow_appl_ptr = 1; - hw->hw_ptr = 0; - hw->appl_ptr = 0; - snd_pcm_set_hw_ptr(pcm, &hw->hw_ptr, -1, 0); - snd_pcm_set_appl_ptr(pcm, &hw->appl_ptr, -1, 0); - } else { - hw->shadow_appl_ptr = 0; - snd_pcm_set_appl_ptr(pcm, &hw->mmap_control->appl_ptr, hw->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL); - } + snd_pcm_set_appl_ptr(pcm, &hw->mmap_control->appl_ptr, hw->fd, + SNDRV_PCM_MMAP_OFFSET_CONTROL); } return 0; } @@ -434,16 +324,13 @@ static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info return err; } info->channel = i.channel; - if (!hw->mmap_shm) { - info->addr = 0; - info->first = i.first; - info->step = i.step; - info->type = SND_PCM_AREA_MMAP; - info->u.mmap.fd = fd; - info->u.mmap.offset = i.offset; - return 0; - } - return snd_pcm_channel_info_shm(pcm, info, -1); + info->addr = 0; + info->first = i.first; + info->step = i.step; + info->type = SND_PCM_AREA_MMAP; + info->u.mmap.fd = fd; + info->u.mmap.offset = i.offset; + return 0; } static int snd_pcm_hw_status(snd_pcm_t *pcm, snd_pcm_status_t * status) @@ -784,7 +671,6 @@ static snd_pcm_sframes_t snd_pcm_hw_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_ #endif if (err < 0) return snd_pcm_check_error(pcm, err); - UPDATE_SHADOW_PTR(hw); return xferi.result; } @@ -804,7 +690,6 @@ static snd_pcm_sframes_t snd_pcm_hw_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_u #endif if (err < 0) return snd_pcm_check_error(pcm, err); - UPDATE_SHADOW_PTR(hw); return xfern.result; } @@ -928,22 +813,6 @@ static snd_pcm_sframes_t snd_pcm_hw_mmap_commit(snd_pcm_t *pcm, { snd_pcm_hw_t *hw = pcm->private_data; - if (hw->mmap_shm) { - if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - snd_pcm_sframes_t result = 0, res; - - do { - res = snd_pcm_write_mmap(pcm, size); - if (res < 0) - return result > 0 ? result : res; - size -= res; - result += res; - } while (size > 0); - return result; - } else { - assert(hw->shadow_appl_ptr); - } - } snd_pcm_mmap_appl_forward(pcm, size); sync_ptr(hw, 0); #ifdef DEBUG_MMAP @@ -952,92 +821,13 @@ static snd_pcm_sframes_t snd_pcm_hw_mmap_commit(snd_pcm_t *pcm, return size; } -static inline snd_pcm_uframes_t snd_pcm_hw_capture_avail(snd_pcm_t *pcm) -{ - snd_pcm_sframes_t avail; - snd_pcm_hw_t *hw = pcm->private_data; - - avail = hw->mmap_status->hw_ptr - hw->mmap_control->appl_ptr; - if (avail < 0) - avail += pcm->boundary; - return avail; -} - -static snd_pcm_sframes_t snd_pcm_hw_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size) -{ - snd_pcm_uframes_t xfer = 0; - snd_pcm_sframes_t err = 0; - snd_pcm_hw_t *hw = pcm->private_data; - if (! size) - return 0; - while (xfer < size) { - snd_pcm_uframes_t frames = size - xfer; - snd_pcm_uframes_t appl_offset = hw->mmap_control->appl_ptr % pcm->buffer_size; - snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset; - if (cont < frames) - frames = cont; - switch (pcm->access) { - case SND_PCM_ACCESS_MMAP_INTERLEAVED: - { - const snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm); - char *buf = snd_pcm_channel_area_addr(a, appl_offset); - err = _snd_pcm_readi(pcm, buf, frames); - if (err >= 0) - frames = err; - break; - } - case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: - { - snd_pcm_uframes_t channels = pcm->channels; - unsigned int c; - void *bufs[channels]; - const snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm); - for (c = 0; c < channels; ++c) { - const snd_pcm_channel_area_t *a = &areas[c]; - bufs[c] = snd_pcm_channel_area_addr(a, appl_offset); - } - err = _snd_pcm_readn(pcm->fast_op_arg, bufs, frames); - if (err >= 0) - frames = err; - } - default: - SNDMSG("invalid access type %d", pcm->access); - return -EINVAL; - } - if (err < 0) - break; - xfer += frames; - } - if (xfer > 0) - return xfer; - return err; -} - static snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private_data; snd_pcm_uframes_t avail, xfer_avail; sync_ptr(hw, 0); - if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - avail = snd_pcm_mmap_playback_avail(pcm); - } else { - avail = snd_pcm_mmap_capture_avail(pcm); - if (avail < pcm->avail_min && hw->mmap_shm) { - snd_pcm_sframes_t err; - xfer_avail = snd_pcm_hw_capture_avail(pcm); - xfer_avail -= xfer_avail % pcm->xfer_align; - if (xfer_avail > 0) { - hw->avail_update_flag = 1; - err = snd_pcm_hw_read_mmap(pcm, xfer_avail); - hw->avail_update_flag = 0; - if (err < 0) - return err; - hw->hw_ptr += err; - avail = snd_pcm_mmap_capture_avail(pcm); - } - } - } + avail = snd_pcm_mmap_avail(pcm); switch (FAST_PCM_STATE(hw)) { case SNDRV_PCM_STATE_RUNNING: if (avail >= pcm->stop_threshold) { @@ -1124,7 +914,7 @@ static snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = { * \param pcmp Returns created PCM handle * \param name Name of PCM * \param fd File descriptor - * \param mmap_emulation Boolean flag for mmap emulation mode + * \param mmap_emulation Obsoleted parameter * \param sync_ptr_ioctl Boolean flag for sync_ptr ioctl * \retval zero on success otherwise a negative error code * \warning Using of this function might be dangerous in the sense @@ -1132,7 +922,8 @@ static snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = { * changed in future. */ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, - int fd, int mmap_emulation, int sync_ptr_ioctl) + int fd, int mmap_emulation ATTRIBUTE_UNUSED, + int sync_ptr_ioctl) { int ver; long fmode; @@ -1205,7 +996,6 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, hw->device = info.device; hw->subdevice = info.subdevice; hw->fd = fd; - hw->mmap_emulation = mmap_emulation; hw->sync_ptr_ioctl = sync_ptr_ioctl; /* no restriction */ hw->format = SND_PCM_FORMAT_UNKNOWN; @@ -1249,7 +1039,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, * \param subdevice Number of subdevice * \param stream PCM Stream * \param mode PCM Mode - * \param mmap_emulation Emulate mmap access using standard r/w access + * \param mmap_emulation Obsoleted parameter * \param sync_ptr_ioctl Use SYNC_PTR ioctl rather than mmap for control structures * \retval zero on success otherwise a negative error code * \warning Using of this function might be dangerous in the sense @@ -1259,7 +1049,8 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, int card, int device, int subdevice, snd_pcm_stream_t stream, int mode, - int mmap_emulation, int sync_ptr_ioctl) + int mmap_emulation ATTRIBUTE_UNUSED, + int sync_ptr_ioctl) { char filename[sizeof(SNDRV_FILE_PCM_STREAM_PLAYBACK) + 20]; const char *filefmt; @@ -1321,7 +1112,7 @@ int snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, } } snd_ctl_close(ctl); - return snd_pcm_hw_open_fd(pcmp, name, fd, mmap_emulation, sync_ptr_ioctl); + return snd_pcm_hw_open_fd(pcmp, name, fd, 0, sync_ptr_ioctl); _err: snd_ctl_close(ctl); return ret; @@ -1347,7 +1138,6 @@ pcm.name { card INT/STR # Card name (string) or number (integer) [device INT] # Device number (default 0) [subdevice INT] # Subdevice number (default -1: first available) - [mmap_emulation BOOL] # Enable mmap emulation for ro/wo devices [sync_ptr_ioctl BOOL] # Use SYNC_PTR ioctl rather than the direct mmap access for control structures [nonblock BOOL] # Force non-blocking open mode [format STR] # Restrict only to the given format @@ -1384,7 +1174,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, snd_config_iterator_t i, next; long card = -1, device = 0, subdevice = -1; const char *str; - int err, mmap_emulation = 0, sync_ptr_ioctl = 0; + int err, sync_ptr_ioctl = 0; int rate = 0, channels = 0; snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN; snd_config_t *n; @@ -1436,13 +1226,6 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, } continue; } - if (strcmp(id, "mmap_emulation") == 0) { - err = snd_config_get_bool(n); - if (err < 0) - continue; - mmap_emulation = err; - continue; - } if (strcmp(id, "sync_ptr_ioctl") == 0) { err = snd_config_get_bool(n); if (err < 0) @@ -1495,7 +1278,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, } err = snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream, mode | (nonblock ? SND_PCM_NONBLOCK : 0), - mmap_emulation, sync_ptr_ioctl); + 0, sync_ptr_ioctl); if (err < 0) return err; if (nonblock && ! (mode & SND_PCM_NONBLOCK)) { diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index b3e31970..d0c60cd3 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -266,8 +266,10 @@ snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_ snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, snd_pcm_uframes_t size, snd_pcm_xfer_areas_func_t func); -snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size); -snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size); +snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset, + snd_pcm_uframes_t size); +snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset, + snd_pcm_uframes_t size); static inline int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) { return pcm->ops->channel_info(pcm, info); diff --git a/src/pcm/pcm_mmap.c b/src/pcm/pcm_mmap.c index 8a0dc51f..d07a0806 100644 --- a/src/pcm/pcm_mmap.c +++ b/src/pcm/pcm_mmap.c @@ -531,7 +531,8 @@ int snd_pcm_munmap(snd_pcm_t *pcm) return 0; } -snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size) +snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) { snd_pcm_uframes_t xfer = 0; snd_pcm_sframes_t err = 0; @@ -539,7 +540,6 @@ snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size) return 0; while (xfer < size) { snd_pcm_uframes_t frames = size - xfer; - snd_pcm_uframes_t offset = snd_pcm_mmap_hw_offset(pcm); snd_pcm_uframes_t cont = pcm->buffer_size - offset; if (cont < frames) frames = cont; @@ -575,13 +575,15 @@ snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size) if (err < 0) break; xfer += frames; + offset += frames; } if (xfer > 0) return xfer; return err; } -snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size) +snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) { snd_pcm_uframes_t xfer = 0; snd_pcm_sframes_t err = 0; @@ -589,7 +591,6 @@ snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size) return 0; while (xfer < size) { snd_pcm_uframes_t frames = size - xfer; - snd_pcm_uframes_t offset = snd_pcm_mmap_hw_offset(pcm); snd_pcm_uframes_t cont = pcm->buffer_size - offset; if (cont < frames) frames = cont; @@ -624,6 +625,7 @@ snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size) if (err < 0) break; xfer += frames; + offset += frames; } if (xfer > 0) return xfer; diff --git a/src/pcm/pcm_mmap_emul.c b/src/pcm/pcm_mmap_emul.c new file mode 100644 index 00000000..c4a4422e --- /dev/null +++ b/src/pcm/pcm_mmap_emul.c @@ -0,0 +1,485 @@ +/** + * \file pcm/pcm_mmap_emul.c + * \ingroup PCM_Plugins + * \brief PCM Mmap-Emulation Plugin Interface + * \author Takashi Iwai + * \date 2007 + */ +/* + * PCM - Mmap-Emulation + * Copyright (c) 2007 by Takashi Iwai + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm_local.h" +#include "pcm_generic.h" + +#ifndef PIC +/* entry for static linking */ +const char *_snd_module_pcm_mmap_emul = ""; +#endif + +/* + * + */ + +typedef struct { + snd_pcm_generic_t gen; + unsigned int mmap_emul :1; + snd_pcm_uframes_t hw_ptr; + snd_pcm_uframes_t appl_ptr; +} mmap_emul_t; + +/* + * here goes a really tricky part; hw_refine falls back to ACCESS_RW_* type + * when ACCESS_MMAP_* isn't supported by the hardware. + */ +static int snd_pcm_mmap_emul_hw_refine(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params) +{ + mmap_emul_t *map = pcm->private_data; + int err = 0; + snd_pcm_access_mask_t oldmask = + *snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS); + snd_pcm_access_mask_t mask; + const snd_mask_t *pmask; + + snd_mask_none(&mask); + err = snd_pcm_hw_refine(map->gen.slave, params); + if (err < 0) { + /* try to use RW_* */ + snd_pcm_hw_params_t new = *params; + + if (!(params->rmask & (1<gen.slave, &new); + if (err < 0) + return err; + *params = new; + } + + pmask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS); + if (snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_MMAP_INTERLEAVED) || + snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) || + snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_MMAP_COMPLEX)) + return 0; + if (snd_pcm_access_mask_test(&mask, SND_PCM_ACCESS_RW_INTERLEAVED)) { + if (snd_pcm_access_mask_test(pmask, + SND_PCM_ACCESS_RW_INTERLEAVED)) + snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, + SND_PCM_ACCESS_MMAP_INTERLEAVED); + snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, + SND_PCM_ACCESS_RW_INTERLEAVED); + params->cmask |= 1<cmask |= 1<cmask |= 1<cmask |= 1<private_data; + snd_pcm_hw_params_t old = *params; + snd_pcm_access_t access; + snd_pcm_access_mask_t oldmask; + const snd_mask_t *pmask; + int err; + + err = _snd_pcm_hw_params(map->gen.slave, params); + if (err >= 0) { + map->mmap_emul = 0; + return err; + } + + *params = old; + pmask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS); + oldmask = *(snd_pcm_access_mask_t *)pmask; + if (INTERNAL(snd_pcm_hw_params_get_access)(params, &access) < 0) + goto _err; + switch (access) { + case SND_PCM_ACCESS_MMAP_INTERLEAVED: + snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, + SND_PCM_ACCESS_MMAP_INTERLEAVED); + snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, + SND_PCM_ACCESS_RW_INTERLEAVED); + break; + case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: + snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, + SND_PCM_ACCESS_MMAP_NONINTERLEAVED); + snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, + SND_PCM_ACCESS_RW_NONINTERLEAVED); + break; + default: + goto _err; + } + err = _snd_pcm_hw_params(map->gen.slave, params); + if (err < 0) + goto _err; + + /* need to back the access type to relieve apps */ + *(snd_pcm_access_mask_t *)pmask = oldmask; + + /* OK, we do fake */ + map->mmap_emul = 1; + map->appl_ptr = 0; + map->hw_ptr = 0; + snd_pcm_set_hw_ptr(pcm, &map->hw_ptr, -1, 0); + snd_pcm_set_appl_ptr(pcm, &map->appl_ptr, -1, 0); + return 0; + + _err: + err = -errno; + return err; +} + +static int snd_pcm_mmap_emul_prepare(snd_pcm_t *pcm) +{ + mmap_emul_t *map = pcm->private_data; + int err; + + err = snd_pcm_generic_prepare(pcm); + if (err < 0) + return err; + map->hw_ptr = map->appl_ptr = 0; + return err; +} + +static int snd_pcm_mmap_emul_reset(snd_pcm_t *pcm) +{ + mmap_emul_t *map = pcm->private_data; + int err; + + err = snd_pcm_generic_reset(pcm); + if (err < 0) + return err; + map->hw_ptr = map->appl_ptr = 0; + return err; +} + +static snd_pcm_sframes_t +snd_pcm_mmap_emul_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) +{ + frames = snd_pcm_generic_rewind(pcm, frames); + if (frames > 0) + snd_pcm_mmap_appl_backward(pcm, frames); + return frames; +} + +static snd_pcm_sframes_t +snd_pcm_mmap_emul_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) +{ + frames = snd_pcm_generic_forward(pcm, frames); + if (frames > 0) + snd_pcm_mmap_appl_forward(pcm, frames); + return frames; +} + +/* write out the uncommitted chunk on mmap buffer to the slave PCM */ +static snd_pcm_sframes_t +sync_slave_write(snd_pcm_t *pcm) +{ + mmap_emul_t *map = pcm->private_data; + snd_pcm_t *slave = map->gen.slave; + snd_pcm_uframes_t offset; + snd_pcm_sframes_t size; + + size = map->appl_ptr - *slave->appl.ptr; + if (size < 0) + size += pcm->boundary; + size -= size % pcm->xfer_align; + if (!size) + return 0; + offset = *slave->appl.ptr % pcm->buffer_size; + return snd_pcm_write_mmap(pcm, offset, size); +} + +/* read the available chunk on the slave PCM to mmap buffer */ +static snd_pcm_sframes_t +sync_slave_read(snd_pcm_t *pcm) +{ + mmap_emul_t *map = pcm->private_data; + snd_pcm_t *slave = map->gen.slave; + snd_pcm_uframes_t offset; + snd_pcm_sframes_t size; + + size = *slave->hw.ptr - map->hw_ptr; + if (size < 0) + size += pcm->boundary; + size -= size % pcm->xfer_align; + if (!size) + return 0; + offset = map->hw_ptr % pcm->buffer_size; + size = snd_pcm_read_mmap(pcm, offset, size); + if (size > 0) + snd_pcm_mmap_hw_forward(pcm, size); + return 0; +} + +static snd_pcm_sframes_t +snd_pcm_mmap_emul_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) +{ + mmap_emul_t *map = pcm->private_data; + snd_pcm_t *slave = map->gen.slave; + + if (!map->mmap_emul) + return snd_pcm_mmap_commit(slave, offset, size); + snd_pcm_mmap_appl_forward(pcm, size); + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + sync_slave_write(pcm); + return size; +} + +static snd_pcm_sframes_t snd_pcm_mmap_emul_avail_update(snd_pcm_t *pcm) +{ + mmap_emul_t *map = pcm->private_data; + snd_pcm_t *slave = map->gen.slave; + snd_pcm_sframes_t avail, err; + + avail = snd_pcm_avail_update(slave); + if (!map->mmap_emul) + return avail; + + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + map->hw_ptr = *slave->hw.ptr; + else + sync_slave_read(pcm); + return snd_pcm_mmap_avail(pcm); +} + +static void snd_pcm_mmap_emul_dump(snd_pcm_t *pcm, snd_output_t *out) +{ + mmap_emul_t *map = pcm->private_data; + + snd_output_printf(out, "Mmap emulation PCM\n"); + if (pcm->setup) { + snd_output_printf(out, "Its setup is:\n"); + snd_pcm_dump_setup(pcm, out); + } + snd_output_printf(out, "Slave: "); + snd_pcm_dump(map->gen.slave, out); +} + +static snd_pcm_ops_t snd_pcm_mmap_emul_ops = { + .close = snd_pcm_generic_close, + .info = snd_pcm_generic_info, + .hw_refine = snd_pcm_mmap_emul_hw_refine, + .hw_params = snd_pcm_mmap_emul_hw_params, + .hw_free = snd_pcm_generic_hw_free, + .sw_params = snd_pcm_generic_sw_params, + .channel_info = snd_pcm_generic_channel_info, + .dump = snd_pcm_mmap_emul_dump, + .nonblock = snd_pcm_generic_nonblock, + .async = snd_pcm_generic_async, + .mmap = snd_pcm_generic_mmap, + .munmap = snd_pcm_generic_munmap, +}; + +static snd_pcm_fast_ops_t snd_pcm_mmap_emul_fast_ops = { + .status = snd_pcm_generic_status, + .state = snd_pcm_generic_state, + .hwsync = snd_pcm_generic_hwsync, + .delay = snd_pcm_generic_delay, + .prepare = snd_pcm_mmap_emul_prepare, + .reset = snd_pcm_mmap_emul_reset, + .start = snd_pcm_generic_start, + .drop = snd_pcm_generic_drop, + .drain = snd_pcm_generic_drain, + .pause = snd_pcm_generic_pause, + .rewind = snd_pcm_mmap_emul_rewind, + .forward = snd_pcm_mmap_emul_forward, + .resume = snd_pcm_generic_resume, + .link = snd_pcm_generic_link, + .link_slaves = snd_pcm_generic_link_slaves, + .unlink = snd_pcm_generic_unlink, + .writei = snd_pcm_generic_writei, + .writen = snd_pcm_generic_writen, + .readi = snd_pcm_generic_readi, + .readn = snd_pcm_generic_readn, + .avail_update = snd_pcm_mmap_emul_avail_update, + .mmap_commit = snd_pcm_mmap_emul_mmap_commit, + .poll_descriptors = snd_pcm_generic_poll_descriptors, + .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, + .poll_revents = snd_pcm_generic_poll_revents, +}; + +static int snd_pcm_mmap_emul_open(snd_pcm_t **pcmp, const char *name, + snd_pcm_t *slave, int close_slave) +{ + snd_pcm_t *pcm; + mmap_emul_t *map; + int err; + + map = calloc(1, sizeof(*map)); + if (!map) + return -ENOMEM; + map->gen.slave = slave; + map->gen.close_slave = close_slave; + + err = snd_pcm_new(&pcm, SND_PCM_TYPE_MMAP_EMUL, name, + slave->stream, slave->mode); + if (err < 0) { + free(map); + return err; + } + pcm->ops = &snd_pcm_mmap_emul_ops; + pcm->fast_ops = &snd_pcm_mmap_emul_fast_ops; + pcm->private_data = map; + pcm->poll_fd = slave->poll_fd; + pcm->poll_events = slave->poll_events; + snd_pcm_set_hw_ptr(pcm, &map->hw_ptr, -1, 0); + snd_pcm_set_appl_ptr(pcm, &map->appl_ptr, -1, 0); + *pcmp = pcm; + + return 0; +} + +/*! \page pcm_plugins + +\section pcm_plugins_mmap_emul Plugin: mmap_emul + +\code +pcm.name { + type mmap_emul + slave PCM +} +\endcode + +\subsection pcm_plugins_mmap_emul_funcref Function reference + +
    +
  • _snd_pcm_hw_open() +
+ +*/ + +/** + * \brief Creates a new mmap_emul PCM + * \param pcmp Returns created PCM handle + * \param name Name of PCM + * \param root Root configuration node + * \param conf Configuration node with hw PCM description + * \param stream PCM Stream + * \param mode PCM Mode + * \warning Using of this function might be dangerous in the sense + * of compatibility reasons. The prototype might be freely + * changed in future. + */ +int _snd_pcm_mmap_emul_open(snd_pcm_t **pcmp, const char *name, + snd_config_t *root ATTRIBUTE_UNUSED, + snd_config_t *conf, + snd_pcm_stream_t stream, int mode) +{ + snd_config_iterator_t i, next; + int err; + snd_pcm_t *spcm; + snd_config_t *slave = NULL, *sconf; + snd_pcm_format_t sformat; + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + if (snd_pcm_conf_generic_id(id)) + continue; + if (strcmp(id, "slave") == 0) { + slave = n; + continue; + } + SNDERR("Unknown field %s", id); + return -EINVAL; + } + if (!slave) { + SNDERR("slave is not defined"); + return -EINVAL; + } + err = snd_pcm_slave_conf(root, slave, &sconf, 0); + if (err < 0) + return err; + err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf); + snd_config_delete(sconf); + if (err < 0) + return err; + err = snd_pcm_mmap_emul_open(pcmp, name, spcm, 1); + if (err < 0) + snd_pcm_close(spcm); + return err; +} + +#ifndef DOC_HIDDEN +SND_DLSYM_BUILD_VERSION(_snd_pcm_mmap_emul_open, SND_PCM_DLSYM_VERSION); +#endif diff --git a/src/pcm/pcm_symbols.c b/src/pcm/pcm_symbols.c index d7849449..a0cf9e16 100644 --- a/src/pcm/pcm_symbols.c +++ b/src/pcm/pcm_symbols.c @@ -49,6 +49,7 @@ extern const char *_snd_module_pcm_iec958; extern const char *_snd_module_pcm_softvol; extern const char *_snd_module_pcm_extplug; extern const char *_snd_module_pcm_ioplug; +extern const char *_snd_module_pcm_mmap_emul; static const char **snd_pcm_open_objects[] = { &_snd_module_pcm_hw,