1 /**
2  * \file pcm/pcm_alaw.c
3  * \ingroup PCM_Plugins
4  * \brief PCM A-Law Conversion Plugin Interface
5  * \author Abramo Bagnara <abramo@alsa-project.org>
6  * \date 2000-2001
7  */
8 /*
9  *  PCM - A-Law 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_alaw = "";
38 #endif
39 
40 #ifndef DOC_HIDDEN
41 
42 typedef void (*alaw_f)(const snd_pcm_channel_area_t *dst_areas,
43 		       snd_pcm_uframes_t dst_offset,
44 		       const snd_pcm_channel_area_t *src_areas,
45 		       snd_pcm_uframes_t src_offset,
46 		       unsigned int channels, snd_pcm_uframes_t frames,
47 		       unsigned int getputidx);
48 
49 typedef struct {
50 	/* This field need to be the first */
51 	snd_pcm_plugin_t plug;
52 	unsigned int getput_idx;
53 	alaw_f func;
54 	snd_pcm_format_t sformat;
55 } snd_pcm_alaw_t;
56 
57 #endif
58 
val_seg(int val)59 static inline int val_seg(int val)
60 {
61 	int r = 1;
62 	val >>= 8;
63 	if (val & 0xf0) {
64 		val >>= 4;
65 		r += 4;
66 	}
67 	if (val & 0x0c) {
68 		val >>= 2;
69 		r += 2;
70 	}
71 	if (val & 0x02)
72 		r += 1;
73 	return r;
74 }
75 
76 /*
77  * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
78  *
79  * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
80  *
81  *		Linear Input Code	Compressed Code
82  *	------------------------	---------------
83  *	0000000wxyza			000wxyz
84  *	0000001wxyza			001wxyz
85  *	000001wxyzab			010wxyz
86  *	00001wxyzabc			011wxyz
87  *	0001wxyzabcd			100wxyz
88  *	001wxyzabcde			101wxyz
89  *	01wxyzabcdef			110wxyz
90  *	1wxyzabcdefg			111wxyz
91  *
92  * For further information see John C. Bellamy's Digital Telephony, 1982,
93  * John Wiley & Sons, pps 98-111 and 472-476.
94  */
95 
s16_to_alaw(int pcm_val)96 static unsigned char s16_to_alaw(int pcm_val)
97 {
98 	int		mask;
99 	int		seg;
100 	unsigned char	aval;
101 
102 	if (pcm_val >= 0) {
103 		mask = 0xD5;
104 	} else {
105 		mask = 0x55;
106 		pcm_val = -pcm_val;
107 		if (pcm_val > 0x7fff)
108 			pcm_val = 0x7fff;
109 	}
110 
111 	if (pcm_val < 256)
112 		aval = pcm_val >> 4;
113 	else {
114 		/* Convert the scaled magnitude to segment number. */
115 		seg = val_seg(pcm_val);
116 		aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
117 	}
118 	return aval ^ mask;
119 }
120 
121 /*
122  * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
123  *
124  */
alaw_to_s16(unsigned char a_val)125 static int alaw_to_s16(unsigned char a_val)
126 {
127 	int		t;
128 	int		seg;
129 
130 	a_val ^= 0x55;
131 	t = a_val & 0x7f;
132 	if (t < 16)
133 		t = (t << 4) + 8;
134 	else {
135 		seg = (t >> 4) & 0x07;
136 		t = ((t & 0x0f) << 4) + 0x108;
137 		t <<= seg -1;
138 	}
139 	return ((a_val & 0x80) ? t : -t);
140 }
141 
142 #ifndef DOC_HIDDEN
143 
snd_pcm_alaw_decode(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 putidx)144 void snd_pcm_alaw_decode(const snd_pcm_channel_area_t *dst_areas,
145 			 snd_pcm_uframes_t dst_offset,
146 			 const snd_pcm_channel_area_t *src_areas,
147 			 snd_pcm_uframes_t src_offset,
148 			 unsigned int channels, snd_pcm_uframes_t frames,
149 			 unsigned int putidx)
150 {
151 #define PUT16_LABELS
152 #include "plugin_ops.h"
153 #undef PUT16_LABELS
154 	void *put = put16_labels[putidx];
155 	unsigned int channel;
156 	for (channel = 0; channel < channels; ++channel) {
157 		const unsigned 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 			int16_t sample = alaw_to_s16(*src);
170 			goto *put;
171 #define PUT16_END after
172 #include "plugin_ops.h"
173 #undef PUT16_END
174 		after:
175 			src += src_step;
176 			dst += dst_step;
177 		}
178 	}
179 }
180 
snd_pcm_alaw_encode(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 getidx)181 void snd_pcm_alaw_encode(const snd_pcm_channel_area_t *dst_areas,
182 			 snd_pcm_uframes_t dst_offset,
183 			 const snd_pcm_channel_area_t *src_areas,
184 			 snd_pcm_uframes_t src_offset,
185 			 unsigned int channels, snd_pcm_uframes_t frames,
186 			 unsigned int getidx)
187 {
188 #define GET16_LABELS
189 #include "plugin_ops.h"
190 #undef GET16_LABELS
191 	void *get = get16_labels[getidx];
192 	unsigned int channel;
193 	int16_t sample = 0;
194 	for (channel = 0; channel < channels; ++channel) {
195 		const char *src;
196 		char *dst;
197 		int src_step, dst_step;
198 		snd_pcm_uframes_t frames1;
199 		const snd_pcm_channel_area_t *src_area = &src_areas[channel];
200 		const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
201 		src = snd_pcm_channel_area_addr(src_area, src_offset);
202 		dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
203 		src_step = snd_pcm_channel_area_step(src_area);
204 		dst_step = snd_pcm_channel_area_step(dst_area);
205 		frames1 = frames;
206 		while (frames1-- > 0) {
207 			goto *get;
208 #define GET16_END after
209 #include "plugin_ops.h"
210 #undef GET16_END
211 		after:
212 			*dst = s16_to_alaw(sample);
213 			src += src_step;
214 			dst += dst_step;
215 		}
216 	}
217 }
218 
219 #endif /* DOC_HIDDEN */
220 
snd_pcm_alaw_hw_refine_cprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)221 static int snd_pcm_alaw_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
222 {
223 	snd_pcm_alaw_t *alaw = pcm->private_data;
224 	int err;
225 	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
226 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
227 					 &access_mask);
228 	if (err < 0)
229 		return err;
230 	if (alaw->sformat == SND_PCM_FORMAT_A_LAW) {
231 		snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
232 		err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
233 						 &format_mask);
234 	} else {
235 		err = _snd_pcm_hw_params_set_format(params,
236 						   SND_PCM_FORMAT_A_LAW);
237 	}
238 	if (err < 0)
239 		return err;
240 	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
241 	if (err < 0)
242 		return err;
243 	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
244 	return 0;
245 }
246 
snd_pcm_alaw_hw_refine_sprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * sparams)247 static int snd_pcm_alaw_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
248 {
249 	snd_pcm_alaw_t *alaw = pcm->private_data;
250 	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
251 	_snd_pcm_hw_params_any(sparams);
252 	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
253 				   &saccess_mask);
254 	_snd_pcm_hw_params_set_format(sparams, alaw->sformat);
255 	_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
256 	return 0;
257 }
258 
snd_pcm_alaw_hw_refine_schange(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)259 static int snd_pcm_alaw_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
260 					    snd_pcm_hw_params_t *sparams)
261 {
262 	int err;
263 	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
264 			      SND_PCM_HW_PARBIT_RATE |
265 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
266 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
267 			      SND_PCM_HW_PARBIT_PERIODS |
268 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
269 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
270 			      SND_PCM_HW_PARBIT_TICK_TIME);
271 	err = _snd_pcm_hw_params_refine(sparams, links, params);
272 	if (err < 0)
273 		return err;
274 	return 0;
275 }
276 
snd_pcm_alaw_hw_refine_cchange(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)277 static int snd_pcm_alaw_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
278 					    snd_pcm_hw_params_t *sparams)
279 {
280 	int err;
281 	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
282 			      SND_PCM_HW_PARBIT_RATE |
283 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
284 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
285 			      SND_PCM_HW_PARBIT_PERIODS |
286 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
287 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
288 			      SND_PCM_HW_PARBIT_TICK_TIME);
289 	err = _snd_pcm_hw_params_refine(params, links, sparams);
290 	if (err < 0)
291 		return err;
292 	return 0;
293 }
294 
snd_pcm_alaw_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)295 static int snd_pcm_alaw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
296 {
297 	return snd_pcm_hw_refine_slave(pcm, params,
298 				       snd_pcm_alaw_hw_refine_cprepare,
299 				       snd_pcm_alaw_hw_refine_cchange,
300 				       snd_pcm_alaw_hw_refine_sprepare,
301 				       snd_pcm_alaw_hw_refine_schange,
302 				       snd_pcm_generic_hw_refine);
303 }
304 
snd_pcm_alaw_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)305 static int snd_pcm_alaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
306 {
307 	snd_pcm_alaw_t *alaw = pcm->private_data;
308 	snd_pcm_format_t format;
309 	int err = snd_pcm_hw_params_slave(pcm, params,
310 					  snd_pcm_alaw_hw_refine_cchange,
311 					  snd_pcm_alaw_hw_refine_sprepare,
312 					  snd_pcm_alaw_hw_refine_schange,
313 					  snd_pcm_generic_hw_params);
314 	if (err < 0)
315 		return err;
316 
317 	err = INTERNAL(snd_pcm_hw_params_get_format)(params, &format);
318 	if (err < 0)
319 		return err;
320 
321 	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
322 		if (alaw->sformat == SND_PCM_FORMAT_A_LAW) {
323 			alaw->getput_idx = snd_pcm_linear_get_index(format, SND_PCM_FORMAT_S16);
324 			alaw->func = snd_pcm_alaw_encode;
325 		} else {
326 			alaw->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, alaw->sformat);
327 			alaw->func = snd_pcm_alaw_decode;
328 		}
329 	} else {
330 		if (alaw->sformat == SND_PCM_FORMAT_A_LAW) {
331 			alaw->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, format);
332 			alaw->func = snd_pcm_alaw_decode;
333 		} else {
334 			alaw->getput_idx = snd_pcm_linear_get_index(alaw->sformat, SND_PCM_FORMAT_S16);
335 			alaw->func = snd_pcm_alaw_encode;
336 		}
337 	}
338 	return 0;
339 }
340 
341 static snd_pcm_uframes_t
snd_pcm_alaw_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)342 snd_pcm_alaw_write_areas(snd_pcm_t *pcm,
343 			 const snd_pcm_channel_area_t *areas,
344 			 snd_pcm_uframes_t offset,
345 			 snd_pcm_uframes_t size,
346 			 const snd_pcm_channel_area_t *slave_areas,
347 			 snd_pcm_uframes_t slave_offset,
348 			 snd_pcm_uframes_t *slave_sizep)
349 {
350 	snd_pcm_alaw_t *alaw = pcm->private_data;
351 	if (size > *slave_sizep)
352 		size = *slave_sizep;
353 	alaw->func(slave_areas, slave_offset,
354 		   areas, offset,
355 		   pcm->channels, size,
356 		   alaw->getput_idx);
357 	*slave_sizep = size;
358 	return size;
359 }
360 
361 static snd_pcm_uframes_t
snd_pcm_alaw_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)362 snd_pcm_alaw_read_areas(snd_pcm_t *pcm,
363 			const snd_pcm_channel_area_t *areas,
364 			snd_pcm_uframes_t offset,
365 			snd_pcm_uframes_t size,
366 			const snd_pcm_channel_area_t *slave_areas,
367 			snd_pcm_uframes_t slave_offset,
368 			snd_pcm_uframes_t *slave_sizep)
369 {
370 	snd_pcm_alaw_t *alaw = pcm->private_data;
371 	if (size > *slave_sizep)
372 		size = *slave_sizep;
373 	alaw->func(areas, offset,
374 		   slave_areas, slave_offset,
375 		   pcm->channels, size,
376 		   alaw->getput_idx);
377 	*slave_sizep = size;
378 	return size;
379 }
380 
snd_pcm_alaw_dump(snd_pcm_t * pcm,snd_output_t * out)381 static void snd_pcm_alaw_dump(snd_pcm_t *pcm, snd_output_t *out)
382 {
383 	snd_pcm_alaw_t *alaw = pcm->private_data;
384 	snd_output_printf(out, "A-Law conversion PCM (%s)\n",
385 		snd_pcm_format_name(alaw->sformat));
386 	if (pcm->setup) {
387 		snd_output_printf(out, "Its setup is:\n");
388 		snd_pcm_dump_setup(pcm, out);
389 	}
390 	snd_output_printf(out, "Slave: ");
391 	snd_pcm_dump(alaw->plug.gen.slave, out);
392 }
393 
394 static const snd_pcm_ops_t snd_pcm_alaw_ops = {
395 	.close = snd_pcm_generic_close,
396 	.info = snd_pcm_generic_info,
397 	.hw_refine = snd_pcm_alaw_hw_refine,
398 	.hw_params = snd_pcm_alaw_hw_params,
399 	.hw_free = snd_pcm_generic_hw_free,
400 	.sw_params = snd_pcm_generic_sw_params,
401 	.channel_info = snd_pcm_generic_channel_info,
402 	.dump = snd_pcm_alaw_dump,
403 	.nonblock = snd_pcm_generic_nonblock,
404 	.async = snd_pcm_generic_async,
405 	.mmap = snd_pcm_generic_mmap,
406 	.munmap = snd_pcm_generic_munmap,
407 	.query_chmaps = snd_pcm_generic_query_chmaps,
408 	.get_chmap = snd_pcm_generic_get_chmap,
409 	.set_chmap = snd_pcm_generic_set_chmap,
410 };
411 
412 /**
413  * \brief Creates a new A-Law conversion PCM
414  * \param pcmp Returns created PCM handle
415  * \param name Name of PCM
416  * \param sformat Slave (destination) format
417  * \param slave Slave PCM handle
418  * \param close_slave When set, the slave PCM handle is closed with copy PCM
419  * \retval zero on success otherwise a negative error code
420  * \warning Using of this function might be dangerous in the sense
421  *          of compatibility reasons. The prototype might be freely
422  *          changed in future.
423  */
snd_pcm_alaw_open(snd_pcm_t ** pcmp,const char * name,snd_pcm_format_t sformat,snd_pcm_t * slave,int close_slave)424 int snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
425 {
426 	snd_pcm_t *pcm;
427 	snd_pcm_alaw_t *alaw;
428 	int err;
429 	assert(pcmp && slave);
430 	if (snd_pcm_format_linear(sformat) != 1 &&
431 	    sformat != SND_PCM_FORMAT_A_LAW)
432 		return -EINVAL;
433 	alaw = calloc(1, sizeof(snd_pcm_alaw_t));
434 	if (!alaw) {
435 		return -ENOMEM;
436 	}
437 	snd_pcm_plugin_init(&alaw->plug);
438 	alaw->sformat = sformat;
439 	alaw->plug.read = snd_pcm_alaw_read_areas;
440 	alaw->plug.write = snd_pcm_alaw_write_areas;
441 	alaw->plug.undo_read = snd_pcm_plugin_undo_read_generic;
442 	alaw->plug.undo_write = snd_pcm_plugin_undo_write_generic;
443 	alaw->plug.gen.slave = slave;
444 	alaw->plug.gen.close_slave = close_slave;
445 
446 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_ALAW, name, slave->stream, slave->mode);
447 	if (err < 0) {
448 		free(alaw);
449 		return err;
450 	}
451 	pcm->ops = &snd_pcm_alaw_ops;
452 	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
453 	pcm->private_data = alaw;
454 	pcm->poll_fd = slave->poll_fd;
455 	pcm->poll_events = slave->poll_events;
456 	pcm->tstamp_type = slave->tstamp_type;
457 	snd_pcm_set_hw_ptr(pcm, &alaw->plug.hw_ptr, -1, 0);
458 	snd_pcm_set_appl_ptr(pcm, &alaw->plug.appl_ptr, -1, 0);
459 	*pcmp = pcm;
460 
461 	return 0;
462 }
463 
464 /*! \page pcm_plugins
465 
466 \section pcm_plugins_alaw Plugin: A-Law
467 
468 This plugin converts A-Law samples to linear or linear to A-Law samples
469 from master A-Law conversion PCM to given slave PCM. The channel count,
470 format and rate must match for both of them.
471 
472 \code
473 pcm.name {
474         type alaw               # A-Law conversion PCM
475         slave STR               # Slave name
476         # or
477         slave {                 # Slave definition
478                 pcm STR         # Slave PCM name
479                 # or
480                 pcm { }         # Slave PCM definition
481                 format STR      # Slave format
482         }
483 }
484 \endcode
485 
486 \subsection pcm_plugins_alaw_funcref Function reference
487 
488 <UL>
489   <LI>snd_pcm_alaw_open()
490   <LI>_snd_pcm_alaw_open()
491 </UL>
492 
493 */
494 
495 /**
496  * \brief Creates a new A-Law conversion PCM
497  * \param pcmp Returns created PCM handle
498  * \param name Name of PCM
499  * \param root Root configuration node
500  * \param conf Configuration node with copy PCM description
501  * \param stream Stream type
502  * \param mode Stream mode
503  * \retval zero on success otherwise a negative error code
504  * \warning Using of this function might be dangerous in the sense
505  *          of compatibility reasons. The prototype might be freely
506  *          changed in future.
507  */
_snd_pcm_alaw_open(snd_pcm_t ** pcmp,const char * name,snd_config_t * root,snd_config_t * conf,snd_pcm_stream_t stream,int mode)508 int _snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name,
509 		       snd_config_t *root, snd_config_t *conf,
510 		       snd_pcm_stream_t stream, int mode)
511 {
512 	snd_config_iterator_t i, next;
513 	int err;
514 	snd_pcm_t *spcm;
515 	snd_config_t *slave = NULL, *sconf;
516 	snd_pcm_format_t sformat;
517 	snd_config_for_each(i, next, conf) {
518 		snd_config_t *n = snd_config_iterator_entry(i);
519 		const char *id;
520 		if (snd_config_get_id(n, &id) < 0)
521 			continue;
522 		if (snd_pcm_conf_generic_id(id))
523 			continue;
524 		if (strcmp(id, "slave") == 0) {
525 			slave = n;
526 			continue;
527 		}
528 		SNDERR("Unknown field %s", id);
529 		return -EINVAL;
530 	}
531 	if (!slave) {
532 		SNDERR("slave is not defined");
533 		return -EINVAL;
534 	}
535 	err = snd_pcm_slave_conf(root, slave, &sconf, 1,
536 				 SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
537 	if (err < 0)
538 		return err;
539 	if (snd_pcm_format_linear(sformat) != 1 &&
540 	    sformat != SND_PCM_FORMAT_A_LAW) {
541 	    	snd_config_delete(sconf);
542 		SNDERR("invalid slave format");
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_alaw_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_alaw_open, SND_PCM_DLSYM_VERSION);
556 #endif
557