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