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