• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * \file control/control.c
3  * \brief CTL interface - parse ASCII identifiers and values
4  * \author Jaroslav Kysela <perex@perex.cz>
5  * \date 2010
6  */
7 /*
8  *  Control Interface - ASCII parser
9  *  Copyright (c) 2010 by Jaroslav Kysela <perex@perex.cz>
10  *
11  *
12  *   This library is free software; you can redistribute it and/or modify
13  *   it under the terms of the GNU Lesser General Public License as
14  *   published by the Free Software Foundation; either version 2.1 of
15  *   the License, or (at your option) any later version.
16  *
17  *   This program is distributed in the hope that it will be useful,
18  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *   GNU Lesser General Public License for more details.
21  *
22  *   You should have received a copy of the GNU Lesser General Public
23  *   License along with this library; if not, write to the Free Software
24  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
25  *
26  */
27 
28 #include <unistd.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <math.h>
32 #include "control_local.h"
33 
34 /* Function to convert from percentage to volume. val = percentage */
35 
convert_prange1(long perc,long min,long max)36 static inline long int convert_prange1(long perc, long min, long max)
37 {
38 	long tmp;
39 
40 #ifdef HAVE_SOFT_FLOAT
41 	tmp = perc * (max - min);
42 	tmp = tmp / 100 + ((tmp % 100) < 50 ? 0 : 1);
43 #else
44 	tmp = rint((double)perc * (double)(max - min) * 0.01);
45 #endif
46 	if (tmp == 0 && perc > 0)
47 		tmp++;
48 	return tmp + min;
49 }
50 
51 #define check_range(val, min, max) \
52 	((val < min) ? (min) : ((val > max) ? (max) : (val)))
53 
get_integer(const char ** ptr,long min,long max)54 static long get_integer(const char **ptr, long min, long max)
55 {
56 	long val = min;
57 	char *p = (char *)*ptr, *s;
58 
59 	if (*p == ':')
60 		p++;
61 	if (*p == '\0' || (!isdigit(*p) && *p != '-'))
62 		goto out;
63 
64 	s = p;
65 	val = strtol(s, &p, 0);
66 	if (*p == '.') {
67 		p++;
68 		(void)strtol(p, &p, 10);
69 	}
70 	if (*p == '%') {
71 		val = (long)convert_prange1(strtod(s, NULL), min, max);
72 		p++;
73 	}
74 	val = check_range(val, min, max);
75 	if (*p == ',')
76 		p++;
77  out:
78 	*ptr = p;
79 	return val;
80 }
81 
get_integer64(const char ** ptr,long long min,long long max)82 static long long get_integer64(const char **ptr, long long min, long long max)
83 {
84 	long long val = min;
85 	char *p = (char *)*ptr, *s;
86 
87 	if (*p == ':')
88 		p++;
89 	if (*p == '\0' || (!isdigit(*p) && *p != '-'))
90 		goto out;
91 
92 	s = p;
93 	val = strtol(s, &p, 0);
94 	if (*p == '.') {
95 		p++;
96 		(void)strtol(p, &p, 10);
97 	}
98 	if (*p == '%') {
99 		val = (long long)convert_prange1(strtod(s, NULL), min, max);
100 		p++;
101 	}
102 	val = check_range(val, min, max);
103 	if (*p == ',')
104 		p++;
105  out:
106 	*ptr = p;
107 	return val;
108 }
109 
110 /**
111  * \brief return ASCII CTL element identifier name
112  * \param id CTL identifier
113  * \return ascii identifier of CTL element
114  *
115  * The string is allocated using strdup().
116  */
snd_ctl_ascii_elem_id_get(snd_ctl_elem_id_t * id)117 char *snd_ctl_ascii_elem_id_get(snd_ctl_elem_id_t *id)
118 {
119 	unsigned int numid, index, device, subdevice;
120 	char buf[256], buf1[32];
121 	const char *iface;
122 
123 	numid = snd_ctl_elem_id_get_numid(id);
124 	iface = snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(id));
125 	if (numid > 0) {
126 		snprintf(buf, sizeof(buf), "numid=%u,iface=%s,name='%s'",
127 				numid, iface, snd_ctl_elem_id_get_name(id));
128 	} else {
129 		snprintf(buf, sizeof(buf), "iface=%s,name='%s'",
130 				iface, snd_ctl_elem_id_get_name(id));
131 	}
132 	buf[sizeof(buf)-1] = '\0';
133 	index = snd_ctl_elem_id_get_index(id);
134 	device = snd_ctl_elem_id_get_device(id);
135 	subdevice = snd_ctl_elem_id_get_subdevice(id);
136 	if (index) {
137 		snprintf(buf1, sizeof(buf1), ",index=%u", index);
138 		if (strlen(buf) + strlen(buf1) < sizeof(buf))
139 			strcat(buf, buf1);
140 	}
141 	if (device) {
142 		snprintf(buf1, sizeof(buf1), ",device=%u", device);
143 		if (strlen(buf) + strlen(buf1) < sizeof(buf))
144 			strcat(buf, buf1);
145 	}
146 	if (subdevice) {
147 		snprintf(buf1, sizeof(buf1), ",subdevice=%u", subdevice);
148 		if (strlen(buf) + strlen(buf1) < sizeof(buf))
149 			strcat(buf, buf1);
150 	}
151 	return strdup(buf);
152 }
153 
154 #ifndef DOC_HIDDEN
155 /* used by UCM parser, too */
__snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t * dst,const char * str,const char ** ret_ptr)156 int __snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str,
157 				  const char **ret_ptr)
158 {
159 	int c, size, numid;
160 	int err = -EINVAL;
161 	char *ptr;
162 
163 	while (isspace(*str))
164 		str++;
165 	if (!(*str))
166 		goto out;
167 	snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_MIXER);	/* default */
168 	while (*str) {
169 		if (!strncasecmp(str, "numid=", 6)) {
170 			str += 6;
171 			numid = atoi(str);
172 			if (numid <= 0) {
173 				fprintf(stderr, "amixer: Invalid numid %d\n", numid);
174 				goto out;
175 			}
176 			snd_ctl_elem_id_set_numid(dst, atoi(str));
177 			while (isdigit(*str))
178 				str++;
179 		} else if (!strncasecmp(str, "iface=", 6)) {
180 			str += 6;
181 			if (!strncasecmp(str, "card", 4)) {
182 				snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_CARD);
183 				str += 4;
184 			} else if (!strncasecmp(str, "mixer", 5)) {
185 				snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_MIXER);
186 				str += 5;
187 			} else if (!strncasecmp(str, "pcm", 3)) {
188 				snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_PCM);
189 				str += 3;
190 			} else if (!strncasecmp(str, "rawmidi", 7)) {
191 				snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_RAWMIDI);
192 				str += 7;
193 			} else if (!strncasecmp(str, "timer", 5)) {
194 				snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_TIMER);
195 				str += 5;
196 			} else if (!strncasecmp(str, "sequencer", 9)) {
197 				snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_SEQUENCER);
198 				str += 9;
199 			} else {
200 				goto out;
201 			}
202 		} else if (!strncasecmp(str, "name=", 5)) {
203 			char buf[64];
204 			str += 5;
205 			ptr = buf;
206 			size = 0;
207 			if (*str == '\'' || *str == '\"') {
208 				c = *str++;
209 				while (*str && *str != c) {
210 					if (size < (int)sizeof(buf)) {
211 						*ptr++ = *str;
212 						size++;
213 					}
214 					str++;
215 				}
216 				if (*str == c)
217 					str++;
218 			} else {
219 				while (*str && *str != ',') {
220 					if (size < (int)sizeof(buf)) {
221 						*ptr++ = *str;
222 						size++;
223 					}
224 					str++;
225 				}
226 			}
227 			*ptr = '\0';
228 			snd_ctl_elem_id_set_name(dst, buf);
229 		} else if (!strncasecmp(str, "index=", 6)) {
230 			str += 6;
231 			snd_ctl_elem_id_set_index(dst, atoi(str));
232 			while (isdigit(*str))
233 				str++;
234 		} else if (!strncasecmp(str, "device=", 7)) {
235 			str += 7;
236 			snd_ctl_elem_id_set_device(dst, atoi(str));
237 			while (isdigit(*str))
238 				str++;
239 		} else if (!strncasecmp(str, "subdevice=", 10)) {
240 			str += 10;
241 			snd_ctl_elem_id_set_subdevice(dst, atoi(str));
242 			while (isdigit(*str))
243 				str++;
244 		}
245 		if (*str == ',') {
246 			str++;
247 		} else {
248 			/* when ret_ptr is given, allow to terminate gracefully
249 			 * at the next space letter
250 			 */
251 			if (ret_ptr && isspace(*str))
252 				break;
253 			if (*str)
254 				goto out;
255 		}
256 	}
257 	err = 0;
258 
259  out:
260 	if (ret_ptr)
261 		*ret_ptr = str;
262 	return err;
263 }
264 #endif
265 
266 /**
267  * \brief parse ASCII string as CTL element identifier
268  * \param dst destination CTL identifier
269  * \param str source ASCII string
270  * \return zero on success, otherwise a negative error code
271  */
snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t * dst,const char * str)272 int snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str)
273 {
274 	return __snd_ctl_ascii_elem_id_parse(dst, str, NULL);
275 }
276 
get_ctl_enum_item_index(snd_ctl_t * handle,snd_ctl_elem_info_t * info,const char ** ptrp)277 static int get_ctl_enum_item_index(snd_ctl_t *handle,
278 				   snd_ctl_elem_info_t *info,
279 				   const char **ptrp)
280 {
281 	char *ptr = (char *)*ptrp;
282 	int items, i, len;
283 	const char *name;
284 	char end;
285 
286 	items = snd_ctl_elem_info_get_items(info);
287 	if (items <= 0)
288 		return -1;
289 
290 	end = *ptr;
291 	if (end == '\'' || end == '"')
292 		ptr++;
293 	else
294 		end = '\0';
295 
296 	for (i = 0; i < items; i++) {
297 		snd_ctl_elem_info_set_item(info, i);
298 		if (snd_ctl_elem_info(handle, info) < 0)
299 			return -1;
300 		name = snd_ctl_elem_info_get_item_name(info);
301 		len = strlen(name);
302 		if (strncmp(name, ptr, len))
303 			continue;
304 		if (end == '\0' && (ptr[len] == '\0' || ptr[len] == ',' || ptr[len] == '\n')) {
305 			*ptrp = ptr + len;
306 			return i;
307 		}
308 		if (end != '\0' && ptr[len] == end) {
309 			*ptrp = ptr + len + 1;
310 			return i;
311 		}
312 	}
313 	return -1;
314 }
315 
get_ctl_type_max_elements(snd_ctl_elem_type_t type)316 static unsigned int get_ctl_type_max_elements(snd_ctl_elem_type_t type)
317 {
318 	struct snd_ctl_elem_value value;
319 
320 	switch (type) {
321 	case SND_CTL_ELEM_TYPE_BOOLEAN:
322 	case SND_CTL_ELEM_TYPE_INTEGER:
323 		return ARRAY_SIZE(value.value.integer.value);
324 	case SND_CTL_ELEM_TYPE_INTEGER64:
325 		return ARRAY_SIZE(value.value.integer64.value);
326 	case SND_CTL_ELEM_TYPE_ENUMERATED:
327 		return ARRAY_SIZE(value.value.enumerated.item);
328 	case SND_CTL_ELEM_TYPE_BYTES:
329 		return ARRAY_SIZE(value.value.bytes.data);
330 	default:
331 		return 0;
332 	}
333 }
334 
335 /**
336  * \brief parse ASCII string as CTL element value
337  * \param handle CTL handle
338  * \param dst destination CTL element value
339  * \param info CTL element info structure
340  * \param value source ASCII string
341  * \return zero on success, otherwise a negative error code
342  *
343  * Note: For toggle command, the dst must contain previous (current)
344  * state (do the #snd_ctl_elem_read call to obtain it).
345  */
snd_ctl_ascii_value_parse(snd_ctl_t * handle,snd_ctl_elem_value_t * dst,snd_ctl_elem_info_t * info,const char * value)346 int snd_ctl_ascii_value_parse(snd_ctl_t *handle,
347 			      snd_ctl_elem_value_t *dst,
348 			      snd_ctl_elem_info_t *info,
349 			      const char *value)
350 {
351 	const char *ptr = value;
352 	snd_ctl_elem_id_t myid = {0};
353 	snd_ctl_elem_type_t type;
354 	unsigned int idx, count;
355 	long tmp;
356 	long long tmp64;
357 
358 	snd_ctl_elem_info_get_id(info, &myid);
359 	type = snd_ctl_elem_info_get_type(info);
360 	count = snd_ctl_elem_info_get_count(info);
361 	snd_ctl_elem_value_set_id(dst, &myid);
362 
363 	if (count > get_ctl_type_max_elements(type))
364 		count = get_ctl_type_max_elements(type);
365 
366 	for (idx = 0; idx < count && ptr && *ptr; idx++) {
367 		if (*ptr == ',')
368 			goto skip;
369 		switch (type) {
370 		case SND_CTL_ELEM_TYPE_BOOLEAN:
371 			tmp = 0;
372 			if (!strncasecmp(ptr, "on", 2) ||
373 			    !strncasecmp(ptr, "up", 2)) {
374 				tmp = 1;
375 				ptr += 2;
376 			} else if (!strncasecmp(ptr, "yes", 3)) {
377 				tmp = 1;
378 				ptr += 3;
379 			} else if (!strncasecmp(ptr, "toggle", 6)) {
380 				tmp = snd_ctl_elem_value_get_boolean(dst, idx);
381 				tmp = tmp > 0 ? 0 : 1;
382 				ptr += 6;
383 			} else if (isdigit(*ptr)) {
384 				tmp = atoi(ptr) > 0 ? 1 : 0;
385 				while (isdigit(*ptr))
386 					ptr++;
387 			} else {
388 				while (*ptr && *ptr != ',')
389 					ptr++;
390 			}
391 			snd_ctl_elem_value_set_boolean(dst, idx, tmp);
392 			break;
393 		case SND_CTL_ELEM_TYPE_INTEGER:
394 			tmp = get_integer(&ptr,
395 					  snd_ctl_elem_info_get_min(info),
396 					  snd_ctl_elem_info_get_max(info));
397 			snd_ctl_elem_value_set_integer(dst, idx, tmp);
398 			break;
399 		case SND_CTL_ELEM_TYPE_INTEGER64:
400 			tmp64 = get_integer64(&ptr,
401 					  snd_ctl_elem_info_get_min64(info),
402 					  snd_ctl_elem_info_get_max64(info));
403 			snd_ctl_elem_value_set_integer64(dst, idx, tmp64);
404 			break;
405 		case SND_CTL_ELEM_TYPE_ENUMERATED:
406 			tmp = get_ctl_enum_item_index(handle, info, &ptr);
407 			if (tmp < 0)
408 				tmp = get_integer(&ptr, 0,
409 					snd_ctl_elem_info_get_items(info) - 1);
410 			snd_ctl_elem_value_set_enumerated(dst, idx, tmp);
411 			break;
412 		case SND_CTL_ELEM_TYPE_BYTES:
413 			tmp = get_integer(&ptr, 0, 255);
414 			snd_ctl_elem_value_set_byte(dst, idx, tmp);
415 			break;
416 		default:
417 			break;
418 		}
419 	skip:
420 		if (!strchr(value, ','))
421 			ptr = value;
422 		else if (*ptr == ',')
423 			ptr++;
424 	}
425 	return 0;
426 }
427