Home | History | Annotate | Download | only in pcm
      1 /**
      2  * \file pcm/pcm_copy.c
      3  * \ingroup PCM_Plugins
      4  * \brief PCM Copy Plugin Interface
      5  * \author Abramo Bagnara <abramo (at) alsa-project.org>
      6  * \date 2000-2001
      7  */
      8 /*
      9  *  PCM - Copy conversion
     10  *  Copyright (c) 2000 by Abramo Bagnara <abramo (at) alsa-project.org>
     11  *
     12  *
     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.
     17  *
     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.
     22  *
     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
     26  *
     27  */
     28 
     29 #include <byteswap.h>
     30 #include "pcm_local.h"
     31 #include "pcm_plugin.h"
     32 
     33 #ifndef PIC
     34 /* entry for static linking */
     35 const char *_snd_module_pcm_copy = "";
     36 #endif
     37 
     38 #ifndef DOC_HIDDEN
     39 typedef struct {
     40 	/* This field need to be the first */
     41 	snd_pcm_plugin_t plug;
     42 } snd_pcm_copy_t;
     43 #endif
     44 
     45 static int snd_pcm_copy_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
     46 {
     47 	int err;
     48 	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
     49 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
     50 					 &access_mask);
     51 	if (err < 0)
     52 		return err;
     53 	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
     54 	return 0;
     55 }
     56 
     57 static int snd_pcm_copy_hw_refine_sprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *sparams)
     58 {
     59 	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
     60 	_snd_pcm_hw_params_any(sparams);
     61 	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
     62 				   &saccess_mask);
     63 	return 0;
     64 }
     65 
     66 static int snd_pcm_copy_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
     67 					  snd_pcm_hw_params_t *sparams)
     68 {
     69 	int err;
     70 	unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS;
     71 	err = _snd_pcm_hw_params_refine(sparams, links, params);
     72 	if (err < 0)
     73 		return err;
     74 	return 0;
     75 }
     76 
     77 static int snd_pcm_copy_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
     78 					  snd_pcm_hw_params_t *sparams)
     79 {
     80 	int err;
     81 	unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS;
     82 	err = _snd_pcm_hw_params_refine(params, links, sparams);
     83 	if (err < 0)
     84 		return err;
     85 	return 0;
     86 }
     87 
     88 static int snd_pcm_copy_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
     89 {
     90 	return snd_pcm_hw_refine_slave(pcm, params,
     91 				       snd_pcm_copy_hw_refine_cprepare,
     92 				       snd_pcm_copy_hw_refine_cchange,
     93 				       snd_pcm_copy_hw_refine_sprepare,
     94 				       snd_pcm_copy_hw_refine_schange,
     95 				       snd_pcm_generic_hw_refine);
     96 }
     97 
     98 static int snd_pcm_copy_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
     99 {
    100 	return snd_pcm_hw_params_slave(pcm, params,
    101 				       snd_pcm_copy_hw_refine_cchange,
    102 				       snd_pcm_copy_hw_refine_sprepare,
    103 				       snd_pcm_copy_hw_refine_schange,
    104 				       snd_pcm_generic_hw_params);
    105 }
    106 
    107 static snd_pcm_uframes_t
    108 snd_pcm_copy_write_areas(snd_pcm_t *pcm,
    109 			 const snd_pcm_channel_area_t *areas,
    110 			 snd_pcm_uframes_t offset,
    111 			 snd_pcm_uframes_t size,
    112 			 const snd_pcm_channel_area_t *slave_areas,
    113 			 snd_pcm_uframes_t slave_offset,
    114 			 snd_pcm_uframes_t *slave_sizep)
    115 {
    116 	if (size > *slave_sizep)
    117 		size = *slave_sizep;
    118 	snd_pcm_areas_copy(slave_areas, slave_offset,
    119 			   areas, offset,
    120 			   pcm->channels, size, pcm->format);
    121 	*slave_sizep = size;
    122 	return size;
    123 }
    124 
    125 static snd_pcm_uframes_t
    126 snd_pcm_copy_read_areas(snd_pcm_t *pcm,
    127 			const snd_pcm_channel_area_t *areas,
    128 			snd_pcm_uframes_t offset,
    129 			snd_pcm_uframes_t size,
    130 			const snd_pcm_channel_area_t *slave_areas,
    131 			snd_pcm_uframes_t slave_offset,
    132 			snd_pcm_uframes_t *slave_sizep)
    133 {
    134 	if (size > *slave_sizep)
    135 		size = *slave_sizep;
    136 	snd_pcm_areas_copy(areas, offset,
    137 			   slave_areas, slave_offset,
    138 			   pcm->channels, size, pcm->format);
    139 	*slave_sizep = size;
    140 	return size;
    141 }
    142 
    143 static void snd_pcm_copy_dump(snd_pcm_t *pcm, snd_output_t *out)
    144 {
    145 	snd_pcm_copy_t *copy = pcm->private_data;
    146 	snd_output_printf(out, "Copy conversion PCM\n");
    147 	if (pcm->setup) {
    148 		snd_output_printf(out, "Its setup is:\n");
    149 		snd_pcm_dump_setup(pcm, out);
    150 	}
    151 	snd_output_printf(out, "Slave: ");
    152 	snd_pcm_dump(copy->plug.gen.slave, out);
    153 }
    154 
    155 static const snd_pcm_ops_t snd_pcm_copy_ops = {
    156 	.close = snd_pcm_generic_close,
    157 	.info = snd_pcm_generic_info,
    158 	.hw_refine = snd_pcm_copy_hw_refine,
    159 	.hw_params = snd_pcm_copy_hw_params,
    160 	.hw_free = snd_pcm_generic_hw_free,
    161 	.sw_params = snd_pcm_generic_sw_params,
    162 	.channel_info = snd_pcm_generic_channel_info,
    163 	.dump = snd_pcm_copy_dump,
    164 	.nonblock = snd_pcm_generic_nonblock,
    165 	.async = snd_pcm_generic_async,
    166 	.mmap = snd_pcm_generic_mmap,
    167 	.munmap = snd_pcm_generic_munmap,
    168 };
    169 
    170 /**
    171  * \brief Creates a new copy PCM
    172  * \param pcmp Returns created PCM handle
    173  * \param name Name of PCM
    174  * \param slave Slave PCM handle
    175  * \param close_slave When set, the slave PCM handle is closed with copy PCM
    176  * \retval zero on success otherwise a negative error code
    177  * \warning Using of this function might be dangerous in the sense
    178  *          of compatibility reasons. The prototype might be freely
    179  *          changed in future.
    180  */
    181 int snd_pcm_copy_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t *slave, int close_slave)
    182 {
    183 	snd_pcm_t *pcm;
    184 	snd_pcm_copy_t *copy;
    185 	int err;
    186 	assert(pcmp && slave);
    187 	copy = calloc(1, sizeof(snd_pcm_copy_t));
    188 	if (!copy) {
    189 		return -ENOMEM;
    190 	}
    191 	snd_pcm_plugin_init(&copy->plug);
    192 	copy->plug.read = snd_pcm_copy_read_areas;
    193 	copy->plug.write = snd_pcm_copy_write_areas;
    194 	copy->plug.undo_read = snd_pcm_plugin_undo_read_generic;
    195 	copy->plug.undo_write = snd_pcm_plugin_undo_write_generic;
    196 	copy->plug.gen.slave = slave;
    197 	copy->plug.gen.close_slave = close_slave;
    198 
    199 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_COPY, name, slave->stream, slave->mode);
    200 	if (err < 0) {
    201 		free(copy);
    202 		return err;
    203 	}
    204 	pcm->ops = &snd_pcm_copy_ops;
    205 	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
    206 	pcm->private_data = copy;
    207 	pcm->poll_fd = slave->poll_fd;
    208 	pcm->poll_events = slave->poll_events;
    209 	pcm->monotonic = slave->monotonic;
    210 	snd_pcm_set_hw_ptr(pcm, &copy->plug.hw_ptr, -1, 0);
    211 	snd_pcm_set_appl_ptr(pcm, &copy->plug.appl_ptr, -1, 0);
    212 	*pcmp = pcm;
    213 
    214 	return 0;
    215 }
    216 
    217 /*! \page pcm_plugins
    218 
    219 \section pcm_plugins_copy Plugin: copy
    220 
    221 This plugin copies samples from master copy PCM to given slave PCM.
    222 The channel count, format and rate must match for both of them.
    223 
    224 \code
    225 pcm.name {
    226 	type copy		# Copy PCM
    227 	slave STR		# Slave name
    228 	# or
    229 	slave {			# Slave definition
    230 		pcm STR		# Slave PCM name
    231 		# or
    232 		pcm { }		# Slave PCM definition
    233 	}
    234 }
    235 \endcode
    236 
    237 \subsection pcm_plugins_copy_funcref Function reference
    238 
    239 <UL>
    240   <LI>snd_pcm_copy_open()
    241   <LI>_snd_pcm_copy_open()
    242 </UL>
    243 
    244 */
    245 
    246 /**
    247  * \brief Creates a new copy PCM
    248  * \param pcmp Returns created PCM handle
    249  * \param name Name of PCM
    250  * \param root Root configuration node
    251  * \param conf Configuration node with copy PCM description
    252  * \param stream Stream type
    253  * \param mode Stream mode
    254  * \retval zero on success otherwise a negative error code
    255  * \warning Using of this function might be dangerous in the sense
    256  *          of compatibility reasons. The prototype might be freely
    257  *          changed in future.
    258  */
    259 int _snd_pcm_copy_open(snd_pcm_t **pcmp, const char *name,
    260 		       snd_config_t *root, snd_config_t *conf,
    261 		       snd_pcm_stream_t stream, int mode)
    262 {
    263 	snd_config_iterator_t i, next;
    264 	int err;
    265 	snd_pcm_t *spcm;
    266 	snd_config_t *slave = NULL, *sconf;
    267 	snd_config_for_each(i, next, conf) {
    268 		snd_config_t *n = snd_config_iterator_entry(i);
    269 		const char *id;
    270 		if (snd_config_get_id(n, &id) < 0)
    271 			continue;
    272 		if (snd_pcm_conf_generic_id(id))
    273 			continue;
    274 		if (strcmp(id, "slave") == 0) {
    275 			slave = n;
    276 			continue;
    277 		}
    278 		SNDERR("Unknown field %s", id);
    279 		return -EINVAL;
    280 	}
    281 	if (!slave) {
    282 		SNDERR("slave is not defined");
    283 		return -EINVAL;
    284 	}
    285 	err = snd_pcm_slave_conf(root, slave, &sconf, 0);
    286 	if (err < 0)
    287 		return err;
    288 	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
    289 	snd_config_delete(sconf);
    290 	if (err < 0)
    291 		return err;
    292 	err = snd_pcm_copy_open(pcmp, name, spcm, 1);
    293 	if (err < 0)
    294 		snd_pcm_close(spcm);
    295 	return err;
    296 }
    297 #ifndef DOC_HIDDEN
    298 SND_DLSYM_BUILD_VERSION(_snd_pcm_copy_open, SND_PCM_DLSYM_VERSION);
    299 #endif
    300