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., 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 */
35const char *_snd_module_pcm_copy = "";
36#endif
37
38#ifndef DOC_HIDDEN
39typedef struct {
40	/* This field need to be the first */
41	snd_pcm_plugin_t plug;
42} snd_pcm_copy_t;
43#endif
44
45static 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
57static 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
66static 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
77static 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
88static 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
98static 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
107static snd_pcm_uframes_t
108snd_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
125static snd_pcm_uframes_t
126snd_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
143static 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
155static 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 */
181int 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
221This plugin copies samples from master copy PCM to given slave PCM.
222The channel count, format and rate must match for both of them.
223
224\code
225pcm.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 */
259int _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
298SND_DLSYM_BUILD_VERSION(_snd_pcm_copy_open, SND_PCM_DLSYM_VERSION);
299#endif
300