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