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