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