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(©->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 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