1 /**
2 * \file pcm/pcm_linear.c
3 * \ingroup PCM_Plugins
4 * \brief PCM Linear Conversion Plugin Interface
5 * \author Abramo Bagnara <abramo@alsa-project.org>
6 * \date 2000-2001
7 */
8 /*
9 * PCM - Linear 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 #include "plugin_ops.h"
34
35 #ifndef PIC
36 /* entry for static linking */
37 const char *_snd_module_pcm_linear = "";
38 #endif
39
40 #ifndef DOC_HIDDEN
41 typedef struct {
42 /* This field need to be the first */
43 snd_pcm_plugin_t plug;
44 unsigned int use_getput;
45 unsigned int conv_idx;
46 unsigned int get_idx, put_idx;
47 snd_pcm_format_t sformat;
48 } snd_pcm_linear_t;
49 #endif
50
51 #ifndef DOC_HIDDEN
52
snd_pcm_linear_convert_index(snd_pcm_format_t src_format,snd_pcm_format_t dst_format)53 int snd_pcm_linear_convert_index(snd_pcm_format_t src_format,
54 snd_pcm_format_t dst_format)
55 {
56 int src_endian, dst_endian, sign, src_width, dst_width;
57
58 sign = (snd_pcm_format_signed(src_format) !=
59 snd_pcm_format_signed(dst_format));
60 #ifdef SND_LITTLE_ENDIAN
61 src_endian = snd_pcm_format_big_endian(src_format);
62 dst_endian = snd_pcm_format_big_endian(dst_format);
63 #else
64 src_endian = snd_pcm_format_little_endian(src_format);
65 dst_endian = snd_pcm_format_little_endian(dst_format);
66 #endif
67
68 if (src_endian < 0)
69 src_endian = 0;
70 if (dst_endian < 0)
71 dst_endian = 0;
72
73 src_width = snd_pcm_format_width(src_format) / 8 - 1;
74 dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
75
76 return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
77 }
78
snd_pcm_linear_get_index(snd_pcm_format_t src_format,snd_pcm_format_t dst_format)79 int snd_pcm_linear_get_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
80 {
81 int sign, width, pwidth, endian;
82 sign = (snd_pcm_format_signed(src_format) !=
83 snd_pcm_format_signed(dst_format));
84 #ifdef SND_LITTLE_ENDIAN
85 endian = snd_pcm_format_big_endian(src_format);
86 #else
87 endian = snd_pcm_format_little_endian(src_format);
88 #endif
89 if (endian < 0)
90 endian = 0;
91 pwidth = snd_pcm_format_physical_width(src_format);
92 width = snd_pcm_format_width(src_format);
93 if (pwidth == 24) {
94 switch (width) {
95 case 24:
96 width = 0; break;
97 case 20:
98 width = 1; break;
99 case 18:
100 default:
101 width = 2; break;
102 }
103 return width * 4 + endian * 2 + sign + 20;
104 } else {
105 if (width == 20)
106 width = 40;
107
108 width = width / 8 - 1;
109 return width * 4 + endian * 2 + sign;
110 }
111 }
112
snd_pcm_linear_put_index(snd_pcm_format_t src_format,snd_pcm_format_t dst_format)113 int snd_pcm_linear_put_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
114 {
115 int sign, width, pwidth, endian;
116 sign = (snd_pcm_format_signed(src_format) !=
117 snd_pcm_format_signed(dst_format));
118 #ifdef SND_LITTLE_ENDIAN
119 endian = snd_pcm_format_big_endian(dst_format);
120 #else
121 endian = snd_pcm_format_little_endian(dst_format);
122 #endif
123 if (endian < 0)
124 endian = 0;
125 pwidth = snd_pcm_format_physical_width(dst_format);
126 width = snd_pcm_format_width(dst_format);
127 if (pwidth == 24) {
128 switch (width) {
129 case 24:
130 width = 0; break;
131 case 20:
132 width = 1; break;
133 case 18:
134 default:
135 width = 2; break;
136 }
137 return width * 4 + endian * 2 + sign + 20;
138 } else {
139 if (width == 20)
140 width = 40;
141
142 width = width / 8 - 1;
143 return width * 4 + endian * 2 + sign;
144 }
145 }
146
snd_pcm_linear_convert(const snd_pcm_channel_area_t * dst_areas,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int channels,snd_pcm_uframes_t frames,unsigned int convidx)147 void snd_pcm_linear_convert(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
148 const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
149 unsigned int channels, snd_pcm_uframes_t frames,
150 unsigned int convidx)
151 {
152 #define CONV_LABELS
153 #include "plugin_ops.h"
154 #undef CONV_LABELS
155 void *conv = conv_labels[convidx];
156 unsigned int channel;
157 for (channel = 0; channel < channels; ++channel) {
158 const char *src;
159 char *dst;
160 int src_step, dst_step;
161 snd_pcm_uframes_t frames1;
162 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
163 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
164 src = snd_pcm_channel_area_addr(src_area, src_offset);
165 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
166 src_step = snd_pcm_channel_area_step(src_area);
167 dst_step = snd_pcm_channel_area_step(dst_area);
168 frames1 = frames;
169 while (frames1-- > 0) {
170 goto *conv;
171 #define CONV_END after
172 #include "plugin_ops.h"
173 #undef CONV_END
174 after:
175 src += src_step;
176 dst += dst_step;
177 }
178 }
179 }
180
snd_pcm_linear_getput(const snd_pcm_channel_area_t * dst_areas,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int channels,snd_pcm_uframes_t frames,unsigned int get_idx,unsigned int put_idx)181 void snd_pcm_linear_getput(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
182 const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
183 unsigned int channels, snd_pcm_uframes_t frames,
184 unsigned int get_idx, unsigned int put_idx)
185 {
186 #define CONV24_LABELS
187 #include "plugin_ops.h"
188 #undef CONV24_LABELS
189 void *get = get32_labels[get_idx];
190 void *put = put32_labels[put_idx];
191 unsigned int channel;
192 uint32_t sample = 0;
193 for (channel = 0; channel < channels; ++channel) {
194 const char *src;
195 char *dst;
196 int src_step, dst_step;
197 snd_pcm_uframes_t frames1;
198 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
199 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
200 src = snd_pcm_channel_area_addr(src_area, src_offset);
201 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
202 src_step = snd_pcm_channel_area_step(src_area);
203 dst_step = snd_pcm_channel_area_step(dst_area);
204 frames1 = frames;
205 while (frames1-- > 0) {
206 goto *get;
207 #define CONV24_END after
208 #include "plugin_ops.h"
209 #undef CONV24_END
210 after:
211 src += src_step;
212 dst += dst_step;
213 }
214 }
215 }
216
217 #endif /* DOC_HIDDEN */
218
snd_pcm_linear_hw_refine_cprepare(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params)219 static int snd_pcm_linear_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
220 {
221 int err;
222 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
223 snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
224 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
225 &access_mask);
226 if (err < 0)
227 return err;
228 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
229 &format_mask);
230 if (err < 0)
231 return err;
232 err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
233 if (err < 0)
234 return err;
235 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
236 return 0;
237 }
238
snd_pcm_linear_hw_refine_sprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * sparams)239 static int snd_pcm_linear_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
240 {
241 snd_pcm_linear_t *linear = pcm->private_data;
242 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
243 _snd_pcm_hw_params_any(sparams);
244 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
245 &saccess_mask);
246 _snd_pcm_hw_params_set_format(sparams, linear->sformat);
247 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
248 return 0;
249 }
250
snd_pcm_linear_hw_refine_schange(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)251 static int snd_pcm_linear_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
252 snd_pcm_hw_params_t *sparams)
253 {
254 int err;
255 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
256 SND_PCM_HW_PARBIT_RATE |
257 SND_PCM_HW_PARBIT_PERIOD_SIZE |
258 SND_PCM_HW_PARBIT_BUFFER_SIZE |
259 SND_PCM_HW_PARBIT_PERIODS |
260 SND_PCM_HW_PARBIT_PERIOD_TIME |
261 SND_PCM_HW_PARBIT_BUFFER_TIME |
262 SND_PCM_HW_PARBIT_TICK_TIME);
263 err = _snd_pcm_hw_params_refine(sparams, links, params);
264 if (err < 0)
265 return err;
266 return 0;
267 }
268
snd_pcm_linear_hw_refine_cchange(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)269 static int snd_pcm_linear_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
270 snd_pcm_hw_params_t *sparams)
271 {
272 int err;
273 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
274 SND_PCM_HW_PARBIT_RATE |
275 SND_PCM_HW_PARBIT_PERIOD_SIZE |
276 SND_PCM_HW_PARBIT_BUFFER_SIZE |
277 SND_PCM_HW_PARBIT_PERIODS |
278 SND_PCM_HW_PARBIT_PERIOD_TIME |
279 SND_PCM_HW_PARBIT_BUFFER_TIME |
280 SND_PCM_HW_PARBIT_TICK_TIME);
281 err = _snd_pcm_hw_params_refine(params, links, sparams);
282 if (err < 0)
283 return err;
284 return 0;
285 }
286
snd_pcm_linear_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)287 static int snd_pcm_linear_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
288 {
289 return snd_pcm_hw_refine_slave(pcm, params,
290 snd_pcm_linear_hw_refine_cprepare,
291 snd_pcm_linear_hw_refine_cchange,
292 snd_pcm_linear_hw_refine_sprepare,
293 snd_pcm_linear_hw_refine_schange,
294 snd_pcm_generic_hw_refine);
295 }
296
snd_pcm_linear_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)297 static int snd_pcm_linear_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
298 {
299 snd_pcm_linear_t *linear = pcm->private_data;
300 snd_pcm_format_t format;
301 int err = snd_pcm_hw_params_slave(pcm, params,
302 snd_pcm_linear_hw_refine_cchange,
303 snd_pcm_linear_hw_refine_sprepare,
304 snd_pcm_linear_hw_refine_schange,
305 snd_pcm_generic_hw_params);
306 if (err < 0)
307 return err;
308 err = INTERNAL(snd_pcm_hw_params_get_format)(params, &format);
309 if (err < 0)
310 return err;
311 linear->use_getput = (snd_pcm_format_physical_width(format) == 24 ||
312 snd_pcm_format_physical_width(linear->sformat) == 24 ||
313 snd_pcm_format_width(format) == 20 ||
314 snd_pcm_format_width(linear->sformat) == 20);
315 if (linear->use_getput) {
316 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
317 linear->get_idx = snd_pcm_linear_get_index(format, SND_PCM_FORMAT_S32);
318 linear->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, linear->sformat);
319 } else {
320 linear->get_idx = snd_pcm_linear_get_index(linear->sformat, SND_PCM_FORMAT_S32);
321 linear->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, format);
322 }
323 } else {
324 if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
325 linear->conv_idx = snd_pcm_linear_convert_index(format,
326 linear->sformat);
327 else
328 linear->conv_idx = snd_pcm_linear_convert_index(linear->sformat,
329 format);
330 }
331 return 0;
332 }
333
334 static snd_pcm_uframes_t
snd_pcm_linear_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)335 snd_pcm_linear_write_areas(snd_pcm_t *pcm,
336 const snd_pcm_channel_area_t *areas,
337 snd_pcm_uframes_t offset,
338 snd_pcm_uframes_t size,
339 const snd_pcm_channel_area_t *slave_areas,
340 snd_pcm_uframes_t slave_offset,
341 snd_pcm_uframes_t *slave_sizep)
342 {
343 snd_pcm_linear_t *linear = pcm->private_data;
344 if (size > *slave_sizep)
345 size = *slave_sizep;
346 if (linear->use_getput)
347 snd_pcm_linear_getput(slave_areas, slave_offset,
348 areas, offset,
349 pcm->channels, size,
350 linear->get_idx, linear->put_idx);
351 else
352 snd_pcm_linear_convert(slave_areas, slave_offset,
353 areas, offset,
354 pcm->channels, size, linear->conv_idx);
355 *slave_sizep = size;
356 return size;
357 }
358
359 static snd_pcm_uframes_t
snd_pcm_linear_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)360 snd_pcm_linear_read_areas(snd_pcm_t *pcm,
361 const snd_pcm_channel_area_t *areas,
362 snd_pcm_uframes_t offset,
363 snd_pcm_uframes_t size,
364 const snd_pcm_channel_area_t *slave_areas,
365 snd_pcm_uframes_t slave_offset,
366 snd_pcm_uframes_t *slave_sizep)
367 {
368 snd_pcm_linear_t *linear = pcm->private_data;
369 if (size > *slave_sizep)
370 size = *slave_sizep;
371 if (linear->use_getput)
372 snd_pcm_linear_getput(areas, offset,
373 slave_areas, slave_offset,
374 pcm->channels, size,
375 linear->get_idx, linear->put_idx);
376 else
377 snd_pcm_linear_convert(areas, offset,
378 slave_areas, slave_offset,
379 pcm->channels, size, linear->conv_idx);
380 *slave_sizep = size;
381 return size;
382 }
383
snd_pcm_linear_dump(snd_pcm_t * pcm,snd_output_t * out)384 static void snd_pcm_linear_dump(snd_pcm_t *pcm, snd_output_t *out)
385 {
386 snd_pcm_linear_t *linear = pcm->private_data;
387 snd_output_printf(out, "Linear conversion PCM (%s)\n",
388 snd_pcm_format_name(linear->sformat));
389 if (pcm->setup) {
390 snd_output_printf(out, "Its setup is:\n");
391 snd_pcm_dump_setup(pcm, out);
392 }
393 snd_output_printf(out, "Slave: ");
394 snd_pcm_dump(linear->plug.gen.slave, out);
395 }
396
397 static const snd_pcm_ops_t snd_pcm_linear_ops = {
398 .close = snd_pcm_generic_close,
399 .info = snd_pcm_generic_info,
400 .hw_refine = snd_pcm_linear_hw_refine,
401 .hw_params = snd_pcm_linear_hw_params,
402 .hw_free = snd_pcm_generic_hw_free,
403 .sw_params = snd_pcm_generic_sw_params,
404 .channel_info = snd_pcm_generic_channel_info,
405 .dump = snd_pcm_linear_dump,
406 .nonblock = snd_pcm_generic_nonblock,
407 .async = snd_pcm_generic_async,
408 .mmap = snd_pcm_generic_mmap,
409 .munmap = snd_pcm_generic_munmap,
410 .query_chmaps = snd_pcm_generic_query_chmaps,
411 .get_chmap = snd_pcm_generic_get_chmap,
412 .set_chmap = snd_pcm_generic_set_chmap,
413 };
414
415
416 /**
417 * \brief Creates a new linear conversion PCM
418 * \param pcmp Returns created PCM handle
419 * \param name Name of PCM
420 * \param sformat Slave (destination) format
421 * \param slave Slave PCM handle
422 * \param close_slave When set, the slave PCM handle is closed with copy PCM
423 * \retval zero on success otherwise a negative error code
424 * \warning Using of this function might be dangerous in the sense
425 * of compatibility reasons. The prototype might be freely
426 * changed in future.
427 */
snd_pcm_linear_open(snd_pcm_t ** pcmp,const char * name,snd_pcm_format_t sformat,snd_pcm_t * slave,int close_slave)428 int snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
429 {
430 snd_pcm_t *pcm;
431 snd_pcm_linear_t *linear;
432 int err;
433 assert(pcmp && slave);
434 if (snd_pcm_format_linear(sformat) != 1)
435 return -EINVAL;
436 linear = calloc(1, sizeof(snd_pcm_linear_t));
437 if (!linear) {
438 return -ENOMEM;
439 }
440 snd_pcm_plugin_init(&linear->plug);
441 linear->sformat = sformat;
442 linear->plug.read = snd_pcm_linear_read_areas;
443 linear->plug.write = snd_pcm_linear_write_areas;
444 linear->plug.undo_read = snd_pcm_plugin_undo_read_generic;
445 linear->plug.undo_write = snd_pcm_plugin_undo_write_generic;
446 linear->plug.gen.slave = slave;
447 linear->plug.gen.close_slave = close_slave;
448
449 err = snd_pcm_new(&pcm, SND_PCM_TYPE_LINEAR, name, slave->stream, slave->mode);
450 if (err < 0) {
451 free(linear);
452 return err;
453 }
454 pcm->ops = &snd_pcm_linear_ops;
455 pcm->fast_ops = &snd_pcm_plugin_fast_ops;
456 pcm->private_data = linear;
457 pcm->poll_fd = slave->poll_fd;
458 pcm->poll_events = slave->poll_events;
459 pcm->tstamp_type = slave->tstamp_type;
460 snd_pcm_set_hw_ptr(pcm, &linear->plug.hw_ptr, -1, 0);
461 snd_pcm_set_appl_ptr(pcm, &linear->plug.appl_ptr, -1, 0);
462 *pcmp = pcm;
463
464 return 0;
465 }
466
467 /*! \page pcm_plugins
468
469 \section pcm_plugins_linear Plugin: linear
470
471 This plugin converts linear samples from master linear conversion PCM to given
472 slave PCM. The channel count, format and rate must match for both of them.
473
474 \code
475 pcm.name {
476 type linear # Linear conversion PCM
477 slave STR # Slave name
478 # or
479 slave { # Slave definition
480 pcm STR # Slave PCM name
481 # or
482 pcm { } # Slave PCM definition
483 format STR # Slave format
484 }
485 }
486 \endcode
487
488 \subsection pcm_plugins_linear_funcref Function reference
489
490 <UL>
491 <LI>snd_pcm_linear_open()
492 <LI>_snd_pcm_linear_open()
493 </UL>
494
495 */
496
497 /**
498 * \brief Creates a new linear conversion PCM
499 * \param pcmp Returns created PCM handle
500 * \param name Name of PCM
501 * \param root Root configuration node
502 * \param conf Configuration node with copy PCM description
503 * \param stream Stream type
504 * \param mode Stream mode
505 * \retval zero on success otherwise a negative error code
506 * \warning Using of this function might be dangerous in the sense
507 * of compatibility reasons. The prototype might be freely
508 * changed in future.
509 */
_snd_pcm_linear_open(snd_pcm_t ** pcmp,const char * name,snd_config_t * root,snd_config_t * conf,snd_pcm_stream_t stream,int mode)510 int _snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name,
511 snd_config_t *root, snd_config_t *conf,
512 snd_pcm_stream_t stream, int mode)
513 {
514 snd_config_iterator_t i, next;
515 int err;
516 snd_pcm_t *spcm;
517 snd_config_t *slave = NULL, *sconf;
518 snd_pcm_format_t sformat;
519 snd_config_for_each(i, next, conf) {
520 snd_config_t *n = snd_config_iterator_entry(i);
521 const char *id;
522 if (snd_config_get_id(n, &id) < 0)
523 continue;
524 if (snd_pcm_conf_generic_id(id))
525 continue;
526 if (strcmp(id, "slave") == 0) {
527 slave = n;
528 continue;
529 }
530 SNDERR("Unknown field %s", id);
531 return -EINVAL;
532 }
533 if (!slave) {
534 SNDERR("slave is not defined");
535 return -EINVAL;
536 }
537 err = snd_pcm_slave_conf(root, slave, &sconf, 1,
538 SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
539 if (err < 0)
540 return err;
541 if (snd_pcm_format_linear(sformat) != 1) {
542 snd_config_delete(sconf);
543 SNDERR("slave format is not linear");
544 return -EINVAL;
545 }
546 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
547 snd_config_delete(sconf);
548 if (err < 0)
549 return err;
550 err = snd_pcm_linear_open(pcmp, name, sformat, spcm, 1);
551 if (err < 0)
552 snd_pcm_close(spcm);
553 return err;
554 }
555 #ifndef DOC_HIDDEN
556 SND_DLSYM_BUILD_VERSION(_snd_pcm_linear_open, SND_PCM_DLSYM_VERSION);
557 #endif
558