• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * \file pcm/pcm_copy.c
3  * \ingroup PCM_Plugins
4  * \brief PCM Copy Plugin Interface
5  * \author Abramo Bagnara <abramo@alsa-project.org>
6  * \date 2000-2001
7  */
8 /*
9  *  PCM - Copy conversion
10  *  Copyright (c) 2000 by Abramo Bagnara <abramo@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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26  *
27  */
28 
29 #include "bswap.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 
snd_pcm_copy_hw_refine_cprepare(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params)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 
snd_pcm_copy_hw_refine_sprepare(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * sparams)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 
snd_pcm_copy_hw_refine_schange(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)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 
snd_pcm_copy_hw_refine_cchange(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)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 
snd_pcm_copy_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)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 
snd_pcm_copy_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)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
snd_pcm_copy_write_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)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
snd_pcm_copy_read_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)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 
snd_pcm_copy_dump(snd_pcm_t * pcm,snd_output_t * out)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 	.query_chmaps = snd_pcm_generic_query_chmaps,
169 	.get_chmap = snd_pcm_generic_get_chmap,
170 	.set_chmap = snd_pcm_generic_set_chmap,
171 };
172 
173 /**
174  * \brief Creates a new copy PCM
175  * \param pcmp Returns created PCM handle
176  * \param name Name of PCM
177  * \param slave Slave PCM handle
178  * \param close_slave When set, the slave PCM handle is closed with copy PCM
179  * \retval zero on success otherwise a negative error code
180  * \warning Using of this function might be dangerous in the sense
181  *          of compatibility reasons. The prototype might be freely
182  *          changed in future.
183  */
snd_pcm_copy_open(snd_pcm_t ** pcmp,const char * name,snd_pcm_t * slave,int close_slave)184 int snd_pcm_copy_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t *slave, int close_slave)
185 {
186 	snd_pcm_t *pcm;
187 	snd_pcm_copy_t *copy;
188 	int err;
189 	assert(pcmp && slave);
190 	copy = calloc(1, sizeof(snd_pcm_copy_t));
191 	if (!copy) {
192 		return -ENOMEM;
193 	}
194 	snd_pcm_plugin_init(&copy->plug);
195 	copy->plug.read = snd_pcm_copy_read_areas;
196 	copy->plug.write = snd_pcm_copy_write_areas;
197 	copy->plug.undo_read = snd_pcm_plugin_undo_read_generic;
198 	copy->plug.undo_write = snd_pcm_plugin_undo_write_generic;
199 	copy->plug.gen.slave = slave;
200 	copy->plug.gen.close_slave = close_slave;
201 
202 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_COPY, name, slave->stream, slave->mode);
203 	if (err < 0) {
204 		free(copy);
205 		return err;
206 	}
207 	pcm->ops = &snd_pcm_copy_ops;
208 	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
209 	pcm->private_data = copy;
210 	pcm->poll_fd = slave->poll_fd;
211 	pcm->poll_events = slave->poll_events;
212 	pcm->tstamp_type = slave->tstamp_type;
213 	snd_pcm_set_hw_ptr(pcm, &copy->plug.hw_ptr, -1, 0);
214 	snd_pcm_set_appl_ptr(pcm, &copy->plug.appl_ptr, -1, 0);
215 	*pcmp = pcm;
216 
217 	return 0;
218 }
219 
220 /*! \page pcm_plugins
221 
222 \section pcm_plugins_copy Plugin: copy
223 
224 This plugin copies samples from master copy PCM to given slave PCM.
225 The channel count, format and rate must match for both of them.
226 
227 \code
228 pcm.name {
229 	type copy		# Copy PCM
230 	slave STR		# Slave name
231 	# or
232 	slave {			# Slave definition
233 		pcm STR		# Slave PCM name
234 		# or
235 		pcm { }		# Slave PCM definition
236 	}
237 }
238 \endcode
239 
240 \subsection pcm_plugins_copy_funcref Function reference
241 
242 <UL>
243   <LI>snd_pcm_copy_open()
244   <LI>_snd_pcm_copy_open()
245 </UL>
246 
247 */
248 
249 /**
250  * \brief Creates a new copy PCM
251  * \param pcmp Returns created PCM handle
252  * \param name Name of PCM
253  * \param root Root configuration node
254  * \param conf Configuration node with copy PCM description
255  * \param stream Stream type
256  * \param mode Stream mode
257  * \retval zero on success otherwise a negative error code
258  * \warning Using of this function might be dangerous in the sense
259  *          of compatibility reasons. The prototype might be freely
260  *          changed in future.
261  */
_snd_pcm_copy_open(snd_pcm_t ** pcmp,const char * name,snd_config_t * root,snd_config_t * conf,snd_pcm_stream_t stream,int mode)262 int _snd_pcm_copy_open(snd_pcm_t **pcmp, const char *name,
263 		       snd_config_t *root, snd_config_t *conf,
264 		       snd_pcm_stream_t stream, int mode)
265 {
266 	snd_config_iterator_t i, next;
267 	int err;
268 	snd_pcm_t *spcm;
269 	snd_config_t *slave = NULL, *sconf;
270 	snd_config_for_each(i, next, conf) {
271 		snd_config_t *n = snd_config_iterator_entry(i);
272 		const char *id;
273 		if (snd_config_get_id(n, &id) < 0)
274 			continue;
275 		if (snd_pcm_conf_generic_id(id))
276 			continue;
277 		if (strcmp(id, "slave") == 0) {
278 			slave = n;
279 			continue;
280 		}
281 		SNDERR("Unknown field %s", id);
282 		return -EINVAL;
283 	}
284 	if (!slave) {
285 		SNDERR("slave is not defined");
286 		return -EINVAL;
287 	}
288 	err = snd_pcm_slave_conf(root, slave, &sconf, 0);
289 	if (err < 0)
290 		return err;
291 	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
292 	snd_config_delete(sconf);
293 	if (err < 0)
294 		return err;
295 	err = snd_pcm_copy_open(pcmp, name, spcm, 1);
296 	if (err < 0)
297 		snd_pcm_close(spcm);
298 	return err;
299 }
300 #ifndef DOC_HIDDEN
301 SND_DLSYM_BUILD_VERSION(_snd_pcm_copy_open, SND_PCM_DLSYM_VERSION);
302 #endif
303