• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * \file control/hcontrol.c
3  * \brief HCTL Interface - High Level CTL
4  * \author Jaroslav Kysela <perex@perex.cz>
5  * \author Abramo Bagnara <abramo@alsa-project.org>
6  * \date 2000
7  *
8  * HCTL interface is designed to access preloaded and sorted primitive controls.
9  * Callbacks may be used for event handling.
10  * See \ref hcontrol page for more details.
11  */
12 /*
13  *  Control Interface - high level API
14  *  Copyright (c) 2000 by Jaroslav Kysela <perex@perex.cz>
15  *  Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
16  *
17  *
18  *   This library is free software; you can redistribute it and/or modify
19  *   it under the terms of the GNU Lesser General Public License as
20  *   published by the Free Software Foundation; either version 2.1 of
21  *   the License, or (at your option) any later version.
22  *
23  *   This program is distributed in the hope that it will be useful,
24  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
25  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  *   GNU Lesser General Public License for more details.
27  *
28  *   You should have received a copy of the GNU Lesser General Public
29  *   License along with this library; if not, write to the Free Software
30  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
31  *
32  */
33 
34 /*! \page hcontrol High level control interface
35 
36 <P> High level control interface is designed to access preloaded and sorted primitive controls.
37 
38 \section hcontrol_general_overview General overview
39 
40 <P> High level control interface caches the accesses to primitive controls
41 to reduce overhead accessing the real controls in kernel drivers.
42 
43 */
44 
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <fcntl.h>
50 #include <sys/ioctl.h>
51 #include "control_local.h"
52 #ifdef HAVE_LIBPTHREAD
53 #include <pthread.h>
54 #endif
55 
56 #ifndef DOC_HIDDEN
57 #define NOT_FOUND 1000000000
58 #endif
59 
60 static int snd_hctl_compare_default(const snd_hctl_elem_t *c1,
61 				    const snd_hctl_elem_t *c2);
62 
63 /**
64  * \brief Opens an HCTL
65  * \param hctlp Returned HCTL handle
66  * \param name ASCII identifier of the underlying CTL handle
67  * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)
68  * \return 0 on success otherwise a negative error code
69  */
snd_hctl_open(snd_hctl_t ** hctlp,const char * name,int mode)70 int snd_hctl_open(snd_hctl_t **hctlp, const char *name, int mode)
71 {
72 	snd_ctl_t *ctl;
73 	int err;
74 
75 	if ((err = snd_ctl_open(&ctl, name, mode)) < 0)
76 		return err;
77 	err = snd_hctl_open_ctl(hctlp, ctl);
78 	if (err < 0)
79 		snd_ctl_close(ctl);
80 	return err;
81 }
82 
83 /**
84  * \brief Opens an HCTL
85  * \param hctlp Returned HCTL handle
86  * \param ctl underlying CTL handle
87  * \return 0 on success otherwise a negative error code
88  */
snd_hctl_open_ctl(snd_hctl_t ** hctlp,snd_ctl_t * ctl)89 int snd_hctl_open_ctl(snd_hctl_t **hctlp, snd_ctl_t *ctl)
90 {
91 	snd_hctl_t *hctl;
92 
93 	assert(hctlp);
94 	*hctlp = NULL;
95 	if ((hctl = (snd_hctl_t *)calloc(1, sizeof(snd_hctl_t))) == NULL)
96 		return -ENOMEM;
97 	INIT_LIST_HEAD(&hctl->elems);
98 	hctl->ctl = ctl;
99 	*hctlp = hctl;
100 	return 0;
101 }
102 
103 /**
104  * \brief close HCTL handle
105  * \param hctl HCTL handle
106  * \return 0 on success otherwise a negative error code
107  *
108  * Closes the specified HCTL handle and frees all associated
109  * resources.
110  */
snd_hctl_close(snd_hctl_t * hctl)111 int snd_hctl_close(snd_hctl_t *hctl)
112 {
113 	int err;
114 
115 	assert(hctl);
116 	err = snd_ctl_close(hctl->ctl);
117 	snd_hctl_free(hctl);
118 	free(hctl);
119 	return err;
120 }
121 
122 /**
123  * \brief get identifier of HCTL handle
124  * \param hctl HCTL handle
125  * \return ascii identifier of HCTL handle
126  *
127  * Returns the ASCII identifier of given HCTL handle. It's the same
128  * identifier specified in snd_hctl_open().
129  */
snd_hctl_name(snd_hctl_t * hctl)130 const char *snd_hctl_name(snd_hctl_t *hctl)
131 {
132 	assert(hctl);
133 	return snd_ctl_name(hctl->ctl);
134 }
135 
136 /**
137  * \brief set nonblock mode
138  * \param hctl HCTL handle
139  * \param nonblock 0 = block, 1 = nonblock mode
140  * \return 0 on success otherwise a negative error code
141  */
snd_hctl_nonblock(snd_hctl_t * hctl,int nonblock)142 int snd_hctl_nonblock(snd_hctl_t *hctl, int nonblock)
143 {
144 	assert(hctl);
145 	return snd_ctl_nonblock(hctl->ctl, nonblock);
146 }
147 
148 /**
149  * \brief set async mode
150  * \param hctl HCTL handle
151  * \param sig Signal to raise: < 0 disable, 0 default (SIGIO)
152  * \param pid Process ID to signal: 0 current
153  * \return 0 on success otherwise a negative error code
154  *
155  * A signal is raised when a change happens.
156  */
snd_hctl_async(snd_hctl_t * hctl,int sig,pid_t pid)157 int snd_hctl_async(snd_hctl_t *hctl, int sig, pid_t pid)
158 {
159 	assert(hctl);
160 	return snd_ctl_async(hctl->ctl, sig, pid);
161 }
162 
163 /**
164  * \brief get count of poll descriptors for HCTL handle
165  * \param hctl HCTL handle
166  * \return count of poll descriptors
167  */
snd_hctl_poll_descriptors_count(snd_hctl_t * hctl)168 int snd_hctl_poll_descriptors_count(snd_hctl_t *hctl)
169 {
170 	assert(hctl);
171 	return snd_ctl_poll_descriptors_count(hctl->ctl);
172 }
173 
174 /**
175  * \brief get poll descriptors
176  * \param hctl HCTL handle
177  * \param pfds array of poll descriptors
178  * \param space space in the poll descriptor array
179  * \return count of filled descriptors
180  */
snd_hctl_poll_descriptors(snd_hctl_t * hctl,struct pollfd * pfds,unsigned int space)181 int snd_hctl_poll_descriptors(snd_hctl_t *hctl, struct pollfd *pfds, unsigned int space)
182 {
183 	assert(hctl);
184 	return snd_ctl_poll_descriptors(hctl->ctl, pfds, space);
185 }
186 
187 /**
188  * \brief get returned events from poll descriptors
189  * \param hctl HCTL handle
190  * \param pfds array of poll descriptors
191  * \param nfds count of poll descriptors
192  * \param revents returned events
193  * \return zero if success, otherwise a negative error code
194  */
snd_hctl_poll_descriptors_revents(snd_hctl_t * hctl,struct pollfd * pfds,unsigned int nfds,unsigned short * revents)195 int snd_hctl_poll_descriptors_revents(snd_hctl_t *hctl, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
196 {
197 	assert(hctl);
198 	return snd_ctl_poll_descriptors_revents(hctl->ctl, pfds, nfds, revents);
199 }
200 
snd_hctl_throw_event(snd_hctl_t * hctl,unsigned int mask,snd_hctl_elem_t * elem)201 static int snd_hctl_throw_event(snd_hctl_t *hctl, unsigned int mask,
202 			 snd_hctl_elem_t *elem)
203 {
204 	if (hctl->callback)
205 		return hctl->callback(hctl, mask, elem);
206 	return 0;
207 }
208 
snd_hctl_elem_throw_event(snd_hctl_elem_t * elem,unsigned int mask)209 static int snd_hctl_elem_throw_event(snd_hctl_elem_t *elem,
210 			      unsigned int mask)
211 {
212 	if (elem->callback)
213 		return elem->callback(elem, mask);
214 	return 0;
215 }
216 
snd_hctl_compare_mixer_priority_lookup(const char ** name,const char * const * names,int coef)217 static int snd_hctl_compare_mixer_priority_lookup(const char **name, const char * const *names, int coef)
218 {
219 	int res;
220 
221 	for (res = 0; *names; names++, res += coef) {
222 		if (!strncmp(*name, *names, strlen(*names))) {
223 			*name += strlen(*names);
224 			if (**name == ' ')
225 				(*name)++;
226 			return res+1;
227 		}
228 	}
229 	return NOT_FOUND;
230 }
231 
get_compare_weight(const snd_ctl_elem_id_t * id)232 static int get_compare_weight(const snd_ctl_elem_id_t *id)
233 {
234 	static const char *const names[] = {
235 		"Master",
236 		"Hardware Master",
237 		"Headphone",
238 		"Tone Control",
239 		"3D Control",
240 		"PCM",
241 		"Front",
242 		"Surround",
243 		"Center",
244 		"LFE",
245 		"Synth",
246 		"FM",
247 		"Wave",
248 		"Music",
249 		"DSP",
250 		"Line",
251 		"CD",
252 		"Mic",
253 		"Phone",
254 		"Video",
255 		"Zoom Video",
256 		"PC Speaker",
257 		"Aux",
258 		"Mono",
259 		"ADC",
260 		"Capture Source",
261 		"Capture",
262 		"Playback",
263 		"Loopback",
264 		"Analog Loopback",
265 		"Digital Loopback",
266 		"I2S",
267 		"IEC958",
268 		NULL
269 	};
270 	static const char *const names1[] = {
271 		"Switch",
272 		"Volume",
273 		"Playback",
274 		"Capture",
275 		"Bypass",
276 		"Mono",
277 		"Front",
278 		"Rear",
279 		"Pan",
280 		"Output",
281 		"-",
282 		NULL
283 	};
284 	static const char *const names2[] = {
285 		"Switch",
286 		"Volume",
287 		"Bypass",
288 		"Depth",
289 		"Wide",
290 		"Space",
291 		"Level",
292 		"Center",
293 		NULL
294 	};
295 	const char *name = (char *)id->name, *name1;
296 	int res, res1;
297 
298 	if ((res = snd_hctl_compare_mixer_priority_lookup((const char **)&name, names, 1000000)) == NOT_FOUND)
299 		return NOT_FOUND;
300 	if (*name == '\0')
301 		return res;
302 	for (name1 = name; *name1 != '\0'; name1++);
303 	for (name1--; name1 != name && *name1 != ' '; name1--);
304 	while (name1 != name && *name1 == ' ')
305 		name1--;
306 	if (name1 != name) {
307 		for (; name1 != name && *name1 != ' '; name1--);
308 		name = name1;
309 		if ((res1 = snd_hctl_compare_mixer_priority_lookup((const char **)&name, names1, 1000)) == NOT_FOUND)
310 			return res;
311 		res += res1;
312 	} else {
313 		name = name1;
314 	}
315 	if ((res1 = snd_hctl_compare_mixer_priority_lookup((const char **)&name, names2, 1)) == NOT_FOUND)
316 		return res;
317 	return res + res1;
318 }
319 
_snd_hctl_find_elem(snd_hctl_t * hctl,const snd_ctl_elem_id_t * id,int * dir)320 static int _snd_hctl_find_elem(snd_hctl_t *hctl, const snd_ctl_elem_id_t *id, int *dir)
321 {
322 	unsigned int l, u;
323 	snd_hctl_elem_t el;
324 	int c = 0;
325 	int idx = -1;
326 	assert(hctl && id);
327 	assert(hctl->compare);
328 	el.id = *id;
329 	el.compare_weight = get_compare_weight(id);
330 	l = 0;
331 	u = hctl->count;
332 	while (l < u) {
333 		idx = (l + u) / 2;
334 		c = hctl->compare(&el, hctl->pelems[idx]);
335 		if (c < 0)
336 			u = idx;
337 		else if (c > 0)
338 			l = idx + 1;
339 		else
340 			break;
341 	}
342 	*dir = c;
343 	return idx;
344 }
345 
snd_hctl_elem_add(snd_hctl_t * hctl,snd_hctl_elem_t * elem)346 static int snd_hctl_elem_add(snd_hctl_t *hctl, snd_hctl_elem_t *elem)
347 {
348 	int dir;
349 	int idx;
350 	elem->compare_weight = get_compare_weight(&elem->id);
351 	if (hctl->count == hctl->alloc) {
352 		snd_hctl_elem_t **h;
353 		hctl->alloc += 32;
354 		h = realloc(hctl->pelems, sizeof(*h) * hctl->alloc);
355 		if (!h) {
356 			hctl->alloc -= 32;
357 			return -ENOMEM;
358 		}
359 		hctl->pelems = h;
360 	}
361 	if (hctl->count == 0) {
362 		list_add_tail(&elem->list, &hctl->elems);
363 		hctl->pelems[0] = elem;
364 	} else {
365 		idx = _snd_hctl_find_elem(hctl, &elem->id, &dir);
366 		assert(dir != 0);
367 		if (dir > 0) {
368 			list_add(&elem->list, &hctl->pelems[idx]->list);
369 			idx++;
370 		} else {
371 			list_add_tail(&elem->list, &hctl->pelems[idx]->list);
372 		}
373 		memmove(hctl->pelems + idx + 1,
374 			hctl->pelems + idx,
375 			(hctl->count - idx) * sizeof(snd_hctl_elem_t *));
376 		hctl->pelems[idx] = elem;
377 	}
378 	hctl->count++;
379 	return snd_hctl_throw_event(hctl, SNDRV_CTL_EVENT_MASK_ADD, elem);
380 }
381 
snd_hctl_elem_remove(snd_hctl_t * hctl,unsigned int idx)382 static void snd_hctl_elem_remove(snd_hctl_t *hctl, unsigned int idx)
383 {
384 	snd_hctl_elem_t *elem = hctl->pelems[idx];
385 	unsigned int m;
386 	snd_hctl_elem_throw_event(elem, SNDRV_CTL_EVENT_MASK_REMOVE);
387 	list_del(&elem->list);
388 	free(elem);
389 	hctl->count--;
390 	m = hctl->count - idx;
391 	if (m > 0)
392 		memmove(hctl->pelems + idx,
393 			hctl->pelems + idx + 1,
394 			m * sizeof(snd_hctl_elem_t *));
395 }
396 
397 /**
398  * \brief free HCTL loaded elements
399  * \param hctl HCTL handle
400  * \return 0 on success otherwise a negative error code
401  */
snd_hctl_free(snd_hctl_t * hctl)402 int snd_hctl_free(snd_hctl_t *hctl)
403 {
404 	while (hctl->count > 0)
405 		snd_hctl_elem_remove(hctl, hctl->count - 1);
406 	free(hctl->pelems);
407 	hctl->pelems = 0;
408 	hctl->alloc = 0;
409 	INIT_LIST_HEAD(&hctl->elems);
410 	return 0;
411 }
412 
413 static snd_hctl_t *compare_hctl;
hctl_compare(const void * a,const void * b)414 static int hctl_compare(const void *a, const void *b) {
415 	return compare_hctl->compare(*(const snd_hctl_elem_t * const *) a,
416 			     *(const snd_hctl_elem_t * const *) b);
417 }
418 
snd_hctl_sort(snd_hctl_t * hctl)419 static void snd_hctl_sort(snd_hctl_t *hctl)
420 {
421 	unsigned int k;
422 #ifdef HAVE_LIBPTHREAD
423 	static pthread_mutex_t sync_lock = PTHREAD_MUTEX_INITIALIZER;
424 #endif
425 
426 	assert(hctl);
427 	assert(hctl->compare);
428 	INIT_LIST_HEAD(&hctl->elems);
429 
430 #ifdef HAVE_LIBPTHREAD
431 	pthread_mutex_lock(&sync_lock);
432 #endif
433 	compare_hctl = hctl;
434 	qsort(hctl->pelems, hctl->count, sizeof(*hctl->pelems), hctl_compare);
435 #ifdef HAVE_LIBPTHREAD
436 	pthread_mutex_unlock(&sync_lock);
437 #endif
438 	for (k = 0; k < hctl->count; k++)
439 		list_add_tail(&hctl->pelems[k]->list, &hctl->elems);
440 }
441 
442 /**
443  * \brief Change HCTL compare function and reorder elements
444  * \param hctl HCTL handle
445  * \param compare Element compare function
446  * \return 0 on success otherwise a negative error code
447  */
snd_hctl_set_compare(snd_hctl_t * hctl,snd_hctl_compare_t compare)448 int snd_hctl_set_compare(snd_hctl_t *hctl, snd_hctl_compare_t compare)
449 {
450 	assert(hctl);
451 	hctl->compare = compare == NULL ? snd_hctl_compare_default : compare;
452 	snd_hctl_sort(hctl);
453 	return 0;
454 }
455 
456 /**
457  * \brief A "don't care" fast compare functions that may be used with #snd_hctl_set_compare
458  * \param c1 First HCTL element
459  * \param c2 Second HCTL element
460  * \return -1 if c1 < c2, 0 if c1 == c2, 1 if c1 > c2
461  */
snd_hctl_compare_fast(const snd_hctl_elem_t * c1,const snd_hctl_elem_t * c2)462 int snd_hctl_compare_fast(const snd_hctl_elem_t *c1,
463 			  const snd_hctl_elem_t *c2)
464 {
465 	return c1->id.numid - c2->id.numid;
466 }
467 
snd_hctl_compare_default(const snd_hctl_elem_t * c1,const snd_hctl_elem_t * c2)468 static int snd_hctl_compare_default(const snd_hctl_elem_t *c1,
469 				    const snd_hctl_elem_t *c2)
470 {
471 	int res, d;
472 
473 	d = c1->id.iface - c2->id.iface;
474 	if (d != 0)
475 		return d;
476 	if (c1->id.iface == SNDRV_CTL_ELEM_IFACE_MIXER) {
477 		d = c1->compare_weight - c2->compare_weight;
478 		if (d != 0)
479 			return d;
480 	}
481 	d = c1->id.device - c2->id.device;
482 	if (d != 0)
483 		return d;
484 	d = c1->id.subdevice - c2->id.subdevice;
485 	if (d != 0)
486 		return d;
487 	res = strcmp((const char *)c1->id.name, (const char *)c2->id.name);
488 	if (res != 0)
489 		return res;
490 	return c1->id.index - c2->id.index;
491 }
492 
493 /**
494  * \brief get first element for an HCTL
495  * \param hctl HCTL handle
496  * \return pointer to first element
497  */
snd_hctl_first_elem(snd_hctl_t * hctl)498 snd_hctl_elem_t *snd_hctl_first_elem(snd_hctl_t *hctl)
499 {
500 	assert(hctl);
501 	if (list_empty(&hctl->elems))
502 		return NULL;
503 	return list_entry(hctl->elems.next, snd_hctl_elem_t, list);
504 }
505 
506 /**
507  * \brief get last element for an HCTL
508  * \param hctl HCTL handle
509  * \return pointer to last element
510  */
snd_hctl_last_elem(snd_hctl_t * hctl)511 snd_hctl_elem_t *snd_hctl_last_elem(snd_hctl_t *hctl)
512 {
513 	assert(hctl);
514 	if (list_empty(&hctl->elems))
515 		return NULL;
516 	return list_entry(hctl->elems.prev, snd_hctl_elem_t, list);
517 }
518 
519 /**
520  * \brief get next HCTL element
521  * \param elem HCTL element
522  * \return pointer to next element
523  */
snd_hctl_elem_next(snd_hctl_elem_t * elem)524 snd_hctl_elem_t *snd_hctl_elem_next(snd_hctl_elem_t *elem)
525 {
526 	assert(elem);
527 	if (elem->list.next == &elem->hctl->elems)
528 		return NULL;
529 	return list_entry(elem->list.next, snd_hctl_elem_t, list);
530 }
531 
532 /**
533  * \brief get previous HCTL element
534  * \param elem HCTL element
535  * \return pointer to previous element
536  */
snd_hctl_elem_prev(snd_hctl_elem_t * elem)537 snd_hctl_elem_t *snd_hctl_elem_prev(snd_hctl_elem_t *elem)
538 {
539 	assert(elem);
540 	if (elem->list.prev == &elem->hctl->elems)
541 		return NULL;
542 	return list_entry(elem->list.prev, snd_hctl_elem_t, list);
543 }
544 
545 /**
546  * \brief Search an HCTL element
547  * \param hctl HCTL handle
548  * \param id Element identifier
549  * \return pointer to found HCTL element or NULL if it does not exists
550  */
snd_hctl_find_elem(snd_hctl_t * hctl,const snd_ctl_elem_id_t * id)551 snd_hctl_elem_t *snd_hctl_find_elem(snd_hctl_t *hctl, const snd_ctl_elem_id_t *id)
552 {
553 	int dir;
554 	int res = _snd_hctl_find_elem(hctl, id, &dir);
555 	if (res < 0 || dir != 0)
556 		return NULL;
557 	return hctl->pelems[res];
558 }
559 
560 /**
561  * \brief Load an HCTL with all elements and sort them
562  * \param hctl HCTL handle
563  * \return 0 on success otherwise a negative error code
564  */
snd_hctl_load(snd_hctl_t * hctl)565 int snd_hctl_load(snd_hctl_t *hctl)
566 {
567 	snd_ctl_elem_list_t list;
568 	int err = 0;
569 	unsigned int idx;
570 
571 	assert(hctl);
572 	assert(hctl->ctl);
573 	assert(hctl->count == 0);
574 	assert(list_empty(&hctl->elems));
575 	memset(&list, 0, sizeof(list));
576 	if ((err = snd_ctl_elem_list(hctl->ctl, &list)) < 0)
577 		goto _end;
578 	while (list.count != list.used) {
579 		err = snd_ctl_elem_list_alloc_space(&list, list.count);
580 		if (err < 0)
581 			goto _end;
582 		if ((err = snd_ctl_elem_list(hctl->ctl, &list)) < 0)
583 			goto _end;
584 	}
585 	if (hctl->alloc < list.count) {
586 		hctl->alloc = list.count;
587 		free(hctl->pelems);
588 		hctl->pelems = malloc(hctl->alloc * sizeof(*hctl->pelems));
589 		if (!hctl->pelems) {
590 			err = -ENOMEM;
591 			goto _end;
592 		}
593 	}
594 	for (idx = 0; idx < list.count; idx++) {
595 		snd_hctl_elem_t *elem;
596 		elem = calloc(1, sizeof(snd_hctl_elem_t));
597 		if (elem == NULL) {
598 			snd_hctl_free(hctl);
599 			err = -ENOMEM;
600 			goto _end;
601 		}
602 		elem->id = list.pids[idx];
603 		elem->hctl = hctl;
604 		elem->compare_weight = get_compare_weight(&elem->id);
605 		hctl->pelems[idx] = elem;
606 		list_add_tail(&elem->list, &hctl->elems);
607 		hctl->count++;
608 	}
609 	if (!hctl->compare)
610 		hctl->compare = snd_hctl_compare_default;
611 	snd_hctl_sort(hctl);
612 	for (idx = 0; idx < hctl->count; idx++) {
613 		int res = snd_hctl_throw_event(hctl, SNDRV_CTL_EVENT_MASK_ADD,
614 					       hctl->pelems[idx]);
615 		if (res < 0)
616 			return res;
617 	}
618 	err = snd_ctl_subscribe_events(hctl->ctl, 1);
619  _end:
620 	free(list.pids);
621 	return err;
622 }
623 
624 /**
625  * \brief Set callback function for an HCTL
626  * \param hctl HCTL handle
627  * \param callback callback function
628  */
snd_hctl_set_callback(snd_hctl_t * hctl,snd_hctl_callback_t callback)629 void snd_hctl_set_callback(snd_hctl_t *hctl, snd_hctl_callback_t callback)
630 {
631 	assert(hctl);
632 	hctl->callback = callback;
633 }
634 
635 /**
636  * \brief Set callback private value for an HCTL
637  * \param hctl HCTL handle
638  * \param callback_private callback private value
639  */
snd_hctl_set_callback_private(snd_hctl_t * hctl,void * callback_private)640 void snd_hctl_set_callback_private(snd_hctl_t *hctl, void *callback_private)
641 {
642 	assert(hctl);
643 	hctl->callback_private = callback_private;
644 }
645 
646 /**
647  * \brief Get callback private value for an HCTL
648  * \param hctl HCTL handle
649  * \return callback private value
650  */
snd_hctl_get_callback_private(snd_hctl_t * hctl)651 void *snd_hctl_get_callback_private(snd_hctl_t *hctl)
652 {
653 	assert(hctl);
654 	return hctl->callback_private;
655 }
656 
657 /**
658  * \brief Get number of loaded elements for an HCTL
659  * \param hctl HCTL handle
660  * \return elements count
661  */
snd_hctl_get_count(snd_hctl_t * hctl)662 unsigned int snd_hctl_get_count(snd_hctl_t *hctl)
663 {
664 	return hctl->count;
665 }
666 
667 /**
668  * \brief Wait for a HCTL to become ready (i.e. at least one event pending)
669  * \param hctl HCTL handle
670  * \param timeout maximum time in milliseconds to wait
671  * \return a positive value on success otherwise a negative error code
672  * \retval 0 timeout occurred
673  * \retval 1 an event is pending
674  */
snd_hctl_wait(snd_hctl_t * hctl,int timeout)675 int snd_hctl_wait(snd_hctl_t *hctl, int timeout)
676 {
677 	struct pollfd *pfd;
678 	unsigned short *revents;
679 	int i, npfds, pollio, err, err_poll;
680 
681 	npfds = snd_hctl_poll_descriptors_count(hctl);
682 	if (npfds <= 0 || npfds >= 16) {
683 		SNDERR("Invalid poll_fds %d\n", npfds);
684 		return -EIO;
685 	}
686 	pfd = alloca(sizeof(*pfd) * npfds);
687 	revents = alloca(sizeof(*revents) * npfds);
688 	err = snd_hctl_poll_descriptors(hctl, pfd, npfds);
689 	if (err < 0)
690 		return err;
691 	if (err != npfds) {
692 		SNDMSG("invalid poll descriptors %d\n", err);
693 		return -EIO;
694 	}
695 	do {
696 		pollio = 0;
697 		err_poll = poll(pfd, npfds, timeout);
698 		if (err_poll < 0) {
699 			if (errno == EINTR && !CTLINABORT(hctl->ctl))
700 				continue;
701 			return -errno;
702 		}
703 		if (! err_poll)
704 			break;
705 		err = snd_hctl_poll_descriptors_revents(hctl, pfd, npfds, revents);
706 		if (err < 0)
707 			return err;
708 		for (i = 0; i < npfds; i++) {
709 			if (revents[i] & (POLLERR | POLLNVAL))
710 				return -EIO;
711 			if ((revents[i] & (POLLIN | POLLOUT)) == 0)
712 				continue;
713 			pollio++;
714 		}
715 	} while (! pollio);
716 	return err_poll > 0 ? 1 : 0;
717 }
718 
719 /**
720  * \brief Get a ctl handle associated to the given hctl handle
721  * \param hctl HCTL handle
722  * \return a ctl handle otherwise NULL
723  */
snd_hctl_ctl(snd_hctl_t * hctl)724 snd_ctl_t *snd_hctl_ctl(snd_hctl_t *hctl)
725 {
726 	return hctl->ctl;
727 }
728 
snd_hctl_handle_event(snd_hctl_t * hctl,snd_ctl_event_t * event)729 static int snd_hctl_handle_event(snd_hctl_t *hctl, snd_ctl_event_t *event)
730 {
731 	snd_hctl_elem_t *elem;
732 	int res;
733 
734 	assert(hctl);
735 	assert(hctl->ctl);
736 	switch (event->type) {
737 	case SND_CTL_EVENT_ELEM:
738 		break;
739 	default:
740 		return 0;
741 	}
742 	if (event->data.elem.mask == SNDRV_CTL_EVENT_MASK_REMOVE) {
743 		int dir;
744 		res = _snd_hctl_find_elem(hctl, &event->data.elem.id, &dir);
745 		if (res < 0 || dir != 0)
746 			return -ENOENT;
747 		snd_hctl_elem_remove(hctl, (unsigned int) res);
748 		return 0;
749 	}
750 	if (event->data.elem.mask & SNDRV_CTL_EVENT_MASK_ADD) {
751 		elem = calloc(1, sizeof(snd_hctl_elem_t));
752 		if (elem == NULL)
753 			return -ENOMEM;
754 		elem->id = event->data.elem.id;
755 		elem->hctl = hctl;
756 		res = snd_hctl_elem_add(hctl, elem);
757 		if (res < 0)
758 			return res;
759 	}
760 	if (event->data.elem.mask & (SNDRV_CTL_EVENT_MASK_VALUE |
761 				     SNDRV_CTL_EVENT_MASK_INFO)) {
762 		elem = snd_hctl_find_elem(hctl, &event->data.elem.id);
763 		if (!elem)
764 			return -ENOENT;
765 		res = snd_hctl_elem_throw_event(elem, event->data.elem.mask &
766 						(SNDRV_CTL_EVENT_MASK_VALUE |
767 						 SNDRV_CTL_EVENT_MASK_INFO));
768 		if (res < 0)
769 			return res;
770 	}
771 	return 0;
772 }
773 
774 /**
775  * \brief Handle pending HCTL events invoking callbacks
776  * \param hctl HCTL handle
777  * \return 0 otherwise a negative error code on failure
778  */
snd_hctl_handle_events(snd_hctl_t * hctl)779 int snd_hctl_handle_events(snd_hctl_t *hctl)
780 {
781 	snd_ctl_event_t event;
782 	int res;
783 	unsigned int count = 0;
784 
785 	assert(hctl);
786 	assert(hctl->ctl);
787 	while ((res = snd_ctl_read(hctl->ctl, &event)) != 0 &&
788 	       res != -EAGAIN) {
789 		if (res < 0)
790 			return res;
791 		res = snd_hctl_handle_event(hctl, &event);
792 		if (res < 0)
793 			return res;
794 		count++;
795 	}
796 	return count;
797 }
798 
799 /**
800  * \brief Get information for an HCTL element
801  * \param elem HCTL element
802  * \param info HCTL element information
803  * \return 0 otherwise a negative error code on failure
804  */
snd_hctl_elem_info(snd_hctl_elem_t * elem,snd_ctl_elem_info_t * info)805 int snd_hctl_elem_info(snd_hctl_elem_t *elem, snd_ctl_elem_info_t *info)
806 {
807 	assert(elem);
808 	assert(elem->hctl);
809 	assert(info);
810 	info->id = elem->id;
811 	return snd_ctl_elem_info(elem->hctl->ctl, info);
812 }
813 
814 /**
815  * \brief Get value for an HCTL element
816  * \param elem HCTL element
817  * \param value HCTL element value
818  * \return 0 otherwise a negative error code on failure
819  */
snd_hctl_elem_read(snd_hctl_elem_t * elem,snd_ctl_elem_value_t * value)820 int snd_hctl_elem_read(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value)
821 {
822 	assert(elem);
823 	assert(elem->hctl);
824 	assert(value);
825 	value->id = elem->id;
826 	return snd_ctl_elem_read(elem->hctl->ctl, value);
827 }
828 
829 /**
830  * \brief Set value for an HCTL element
831  * \param elem HCTL element
832  * \param value HCTL element value
833  * \retval 0 on success
834  * \retval >1 on success when value was changed
835  * \retval <0 a negative error code on failure
836  */
snd_hctl_elem_write(snd_hctl_elem_t * elem,snd_ctl_elem_value_t * value)837 int snd_hctl_elem_write(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value)
838 {
839 	assert(elem);
840 	assert(elem->hctl);
841 	assert(value);
842 	value->id = elem->id;
843 	return snd_ctl_elem_write(elem->hctl->ctl, value);
844 }
845 
846 /**
847  * \brief Get TLV value for an HCTL element
848  * \param elem HCTL element
849  * \param tlv TLV array for value
850  * \param tlv_size size of TLV array in bytes
851  * \return 0 otherwise a negative error code on failure
852  */
snd_hctl_elem_tlv_read(snd_hctl_elem_t * elem,unsigned int * tlv,unsigned int tlv_size)853 int snd_hctl_elem_tlv_read(snd_hctl_elem_t *elem, unsigned int *tlv, unsigned int tlv_size)
854 {
855 	assert(elem);
856 	assert(tlv);
857 	assert(tlv_size >= 12);
858 	return snd_ctl_elem_tlv_read(elem->hctl->ctl, &elem->id, tlv, tlv_size);
859 }
860 
861 /**
862  * \brief Set TLV value for an HCTL element
863  * \param elem HCTL element
864  * \param tlv TLV array for value
865  * \retval 0 on success
866  * \retval >1 on success when value was changed
867  * \retval <0 a negative error code on failure
868  */
snd_hctl_elem_tlv_write(snd_hctl_elem_t * elem,const unsigned int * tlv)869 int snd_hctl_elem_tlv_write(snd_hctl_elem_t *elem, const unsigned int *tlv)
870 {
871 	assert(elem);
872 	assert(tlv);
873 	assert(tlv[SNDRV_CTL_TLVO_LEN] >= 4);
874 	return snd_ctl_elem_tlv_write(elem->hctl->ctl, &elem->id, tlv);
875 }
876 
877 /**
878  * \brief Set TLV value for an HCTL element
879  * \param elem HCTL element
880  * \param tlv TLV array for value
881  * \retval 0 on success
882  * \retval >1 on success when value was changed
883  * \retval <0 a negative error code on failure
884  */
snd_hctl_elem_tlv_command(snd_hctl_elem_t * elem,const unsigned int * tlv)885 int snd_hctl_elem_tlv_command(snd_hctl_elem_t *elem, const unsigned int *tlv)
886 {
887 	assert(elem);
888 	assert(tlv);
889 	assert(tlv[SNDRV_CTL_TLVO_LEN] >= 4);
890 	return snd_ctl_elem_tlv_command(elem->hctl->ctl, &elem->id, tlv);
891 }
892 
893 /**
894  * \brief Get HCTL handle for an HCTL element
895  * \param elem HCTL element
896  * \return HCTL handle
897  */
snd_hctl_elem_get_hctl(snd_hctl_elem_t * elem)898 snd_hctl_t *snd_hctl_elem_get_hctl(snd_hctl_elem_t *elem)
899 {
900 	assert(elem);
901 	return elem->hctl;
902 }
903 
904 /**
905  * \brief Get CTL element identifier of a CTL element id/value
906  * \param obj CTL element id/value
907  * \param ptr Pointer to returned CTL element identifier
908  */
snd_hctl_elem_get_id(const snd_hctl_elem_t * obj,snd_ctl_elem_id_t * ptr)909 void snd_hctl_elem_get_id(const snd_hctl_elem_t *obj, snd_ctl_elem_id_t *ptr)
910 {
911 	assert(obj && ptr);
912 	*ptr = obj->id;
913 }
914 
915 /**
916  * \brief Get element numeric identifier of a CTL element id/value
917  * \param obj CTL element id/value
918  * \return element numeric identifier
919  */
snd_hctl_elem_get_numid(const snd_hctl_elem_t * obj)920 unsigned int snd_hctl_elem_get_numid(const snd_hctl_elem_t *obj)
921 {
922 	assert(obj);
923 	return obj->id.numid;
924 }
925 
926 /**
927  * \brief Get interface part of CTL element identifier of a CTL element id/value
928  * \param obj CTL element id/value
929  * \return interface part of element identifier
930  */
snd_hctl_elem_get_interface(const snd_hctl_elem_t * obj)931 snd_ctl_elem_iface_t snd_hctl_elem_get_interface(const snd_hctl_elem_t *obj)
932 {
933 	assert(obj);
934 	return obj->id.iface;
935 }
936 
937 /**
938  * \brief Get device part of CTL element identifier of a CTL element id/value
939  * \param obj CTL element id/value
940  * \return device part of element identifier
941  */
snd_hctl_elem_get_device(const snd_hctl_elem_t * obj)942 unsigned int snd_hctl_elem_get_device(const snd_hctl_elem_t *obj)
943 {
944 	assert(obj);
945 	return obj->id.device;
946 }
947 
948 /**
949  * \brief Get subdevice part of CTL element identifier of a CTL element id/value
950  * \param obj CTL element id/value
951  * \return subdevice part of element identifier
952  */
snd_hctl_elem_get_subdevice(const snd_hctl_elem_t * obj)953 unsigned int snd_hctl_elem_get_subdevice(const snd_hctl_elem_t *obj)
954 {
955 	assert(obj);
956 	return obj->id.subdevice;
957 }
958 
959 /**
960  * \brief Get name part of CTL element identifier of a CTL element id/value
961  * \param obj CTL element id/value
962  * \return name part of element identifier
963  */
snd_hctl_elem_get_name(const snd_hctl_elem_t * obj)964 const char *snd_hctl_elem_get_name(const snd_hctl_elem_t *obj)
965 {
966 	assert(obj);
967 	return (const char *)obj->id.name;
968 }
969 
970 /**
971  * \brief Get index part of CTL element identifier of a CTL element id/value
972  * \param obj CTL element id/value
973  * \return index part of element identifier
974  */
snd_hctl_elem_get_index(const snd_hctl_elem_t * obj)975 unsigned int snd_hctl_elem_get_index(const snd_hctl_elem_t *obj)
976 {
977 	assert(obj);
978 	return obj->id.index;
979 }
980 
981 /**
982  * \brief Set callback function for an HCTL element
983  * \param obj HCTL element
984  * \param val callback function
985  */
snd_hctl_elem_set_callback(snd_hctl_elem_t * obj,snd_hctl_elem_callback_t val)986 void snd_hctl_elem_set_callback(snd_hctl_elem_t *obj, snd_hctl_elem_callback_t val)
987 {
988 	assert(obj);
989 	obj->callback = val;
990 }
991 
992 /**
993  * \brief Set callback private value for an HCTL element
994  * \param obj HCTL element
995  * \param val callback private value
996  */
snd_hctl_elem_set_callback_private(snd_hctl_elem_t * obj,void * val)997 void snd_hctl_elem_set_callback_private(snd_hctl_elem_t *obj, void * val)
998 {
999 	assert(obj);
1000 	obj->callback_private = val;
1001 }
1002 
1003 /**
1004  * \brief Get callback private value for an HCTL element
1005  * \param obj HCTL element
1006  * \return callback private value
1007  */
snd_hctl_elem_get_callback_private(const snd_hctl_elem_t * obj)1008 void * snd_hctl_elem_get_callback_private(const snd_hctl_elem_t *obj)
1009 {
1010 	assert(obj);
1011 	return obj->callback_private;
1012 }
1013 
1014