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(©->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, ©->plug.hw_ptr, -1, 0);
214 snd_pcm_set_appl_ptr(pcm, ©->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