Initial Import
[profile/ivi/alsa-lib.git] / src / pcm / pcm_copy.c
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 */
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