1d5ac70f0Sopenharmony_ci/**
2d5ac70f0Sopenharmony_ci * \file mixer/simple_abst.c
3d5ac70f0Sopenharmony_ci * \brief Mixer Simple Element Class Interface - Module Abstraction
4d5ac70f0Sopenharmony_ci * \author Jaroslav Kysela <perex@perex.cz>
5d5ac70f0Sopenharmony_ci * \date 2005
6d5ac70f0Sopenharmony_ci *
7d5ac70f0Sopenharmony_ci * Mixer simple element class interface.
8d5ac70f0Sopenharmony_ci */
9d5ac70f0Sopenharmony_ci/*
10d5ac70f0Sopenharmony_ci *  Mixer Interface - simple controls - abstraction module
11d5ac70f0Sopenharmony_ci *  Copyright (c) 2005 by Jaroslav Kysela <perex@perex.cz>
12d5ac70f0Sopenharmony_ci *
13d5ac70f0Sopenharmony_ci *
14d5ac70f0Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
15d5ac70f0Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as
16d5ac70f0Sopenharmony_ci *   published by the Free Software Foundation; either version 2.1 of
17d5ac70f0Sopenharmony_ci *   the License, or (at your option) any later version.
18d5ac70f0Sopenharmony_ci *
19d5ac70f0Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
20d5ac70f0Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
21d5ac70f0Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22d5ac70f0Sopenharmony_ci *   GNU Lesser General Public License for more details.
23d5ac70f0Sopenharmony_ci *
24d5ac70f0Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public
25d5ac70f0Sopenharmony_ci *   License along with this library; if not, write to the Free Software
26d5ac70f0Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
27d5ac70f0Sopenharmony_ci *
28d5ac70f0Sopenharmony_ci */
29d5ac70f0Sopenharmony_ci
30d5ac70f0Sopenharmony_ci#include "mixer_local.h"
31d5ac70f0Sopenharmony_ci#include "mixer_simple.h"
32d5ac70f0Sopenharmony_ci#include <stdio.h>
33d5ac70f0Sopenharmony_ci#include <stdlib.h>
34d5ac70f0Sopenharmony_ci#include <unistd.h>
35d5ac70f0Sopenharmony_ci#include <string.h>
36d5ac70f0Sopenharmony_ci#include <fcntl.h>
37d5ac70f0Sopenharmony_ci#include <sys/ioctl.h>
38d5ac70f0Sopenharmony_ci#include <math.h>
39d5ac70f0Sopenharmony_ci#include <dlfcn.h>
40d5ac70f0Sopenharmony_ci
41d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
42d5ac70f0Sopenharmony_ci
43d5ac70f0Sopenharmony_ci#define SO_PATH "smixer"
44d5ac70f0Sopenharmony_ci
45d5ac70f0Sopenharmony_citypedef struct _class_priv {
46d5ac70f0Sopenharmony_ci	char *device;
47d5ac70f0Sopenharmony_ci	snd_ctl_t *ctl;
48d5ac70f0Sopenharmony_ci	snd_hctl_t *hctl;
49d5ac70f0Sopenharmony_ci	int attach_flag;
50d5ac70f0Sopenharmony_ci	snd_ctl_card_info_t *info;
51d5ac70f0Sopenharmony_ci	void *dlhandle;
52d5ac70f0Sopenharmony_ci	void *private_data;
53d5ac70f0Sopenharmony_ci	void (*private_free)(snd_mixer_class_t *class);
54d5ac70f0Sopenharmony_ci} class_priv_t;
55d5ac70f0Sopenharmony_ci
56d5ac70f0Sopenharmony_citypedef int (*snd_mixer_sbasic_init_t)(snd_mixer_class_t *class);
57d5ac70f0Sopenharmony_citypedef int (*snd_mixer_sfbasic_init_t)(snd_mixer_class_t *class,
58d5ac70f0Sopenharmony_ci					snd_mixer_t *mixer,
59d5ac70f0Sopenharmony_ci					const char *device);
60d5ac70f0Sopenharmony_ci
61d5ac70f0Sopenharmony_ci#endif /* !DOC_HIDDEN */
62d5ac70f0Sopenharmony_ci
63d5ac70f0Sopenharmony_cistatic int try_open(snd_mixer_class_t *class, const char *lib)
64d5ac70f0Sopenharmony_ci{
65d5ac70f0Sopenharmony_ci	class_priv_t *priv = snd_mixer_class_get_private(class);
66d5ac70f0Sopenharmony_ci	snd_mixer_event_t event_func;
67d5ac70f0Sopenharmony_ci	snd_mixer_sbasic_init_t init_func = NULL;
68d5ac70f0Sopenharmony_ci	char *xlib, *path, errbuf[256];
69d5ac70f0Sopenharmony_ci	void *h;
70d5ac70f0Sopenharmony_ci	int err = 0;
71d5ac70f0Sopenharmony_ci
72d5ac70f0Sopenharmony_ci	if (!lib)
73d5ac70f0Sopenharmony_ci		return -ENXIO;
74d5ac70f0Sopenharmony_ci	path = getenv("ALSA_MIXER_SIMPLE_MODULES");
75d5ac70f0Sopenharmony_ci	if (!path)
76d5ac70f0Sopenharmony_ci		path = SO_PATH;
77d5ac70f0Sopenharmony_ci	xlib = malloc(strlen(lib) + strlen(path) + 1 + 1);
78d5ac70f0Sopenharmony_ci	if (xlib == NULL)
79d5ac70f0Sopenharmony_ci		return -ENOMEM;
80d5ac70f0Sopenharmony_ci	strcpy(xlib, path);
81d5ac70f0Sopenharmony_ci	strcat(xlib, "/");
82d5ac70f0Sopenharmony_ci	strcat(xlib, lib);
83d5ac70f0Sopenharmony_ci	h = INTERNAL(snd_dlopen)(xlib, RTLD_NOW, errbuf, sizeof(errbuf));
84d5ac70f0Sopenharmony_ci	if (h == NULL) {
85d5ac70f0Sopenharmony_ci		SNDERR("Unable to open library '%s' (%s)", xlib, errbuf);
86d5ac70f0Sopenharmony_ci		free(xlib);
87d5ac70f0Sopenharmony_ci		return -ENXIO;
88d5ac70f0Sopenharmony_ci	}
89d5ac70f0Sopenharmony_ci	priv->dlhandle = h;
90d5ac70f0Sopenharmony_ci	event_func = snd_dlsym(h, "alsa_mixer_simple_event", NULL);
91d5ac70f0Sopenharmony_ci	if (event_func == NULL) {
92d5ac70f0Sopenharmony_ci		SNDERR("Symbol 'alsa_mixer_simple_event' was not found in '%s'", xlib);
93d5ac70f0Sopenharmony_ci		err = -ENXIO;
94d5ac70f0Sopenharmony_ci	}
95d5ac70f0Sopenharmony_ci	if (err == 0) {
96d5ac70f0Sopenharmony_ci		init_func = snd_dlsym(h, "alsa_mixer_simple_init", NULL);
97d5ac70f0Sopenharmony_ci		if (init_func == NULL) {
98d5ac70f0Sopenharmony_ci			SNDERR("Symbol 'alsa_mixer_simple_init' was not found in '%s'", xlib);
99d5ac70f0Sopenharmony_ci			err = -ENXIO;
100d5ac70f0Sopenharmony_ci		}
101d5ac70f0Sopenharmony_ci	}
102d5ac70f0Sopenharmony_ci	free(xlib);
103d5ac70f0Sopenharmony_ci	err = err == 0 ? init_func(class) : err;
104d5ac70f0Sopenharmony_ci	if (err < 0)
105d5ac70f0Sopenharmony_ci		return err;
106d5ac70f0Sopenharmony_ci	snd_mixer_class_set_event(class, event_func);
107d5ac70f0Sopenharmony_ci	return 1;
108d5ac70f0Sopenharmony_ci}
109d5ac70f0Sopenharmony_ci
110d5ac70f0Sopenharmony_cistatic int try_open_full(snd_mixer_class_t *class, snd_mixer_t *mixer,
111d5ac70f0Sopenharmony_ci			 const char *lib, const char *device)
112d5ac70f0Sopenharmony_ci{
113d5ac70f0Sopenharmony_ci	class_priv_t *priv = snd_mixer_class_get_private(class);
114d5ac70f0Sopenharmony_ci	snd_mixer_event_t event_func;
115d5ac70f0Sopenharmony_ci	snd_mixer_sfbasic_init_t init_func = NULL;
116d5ac70f0Sopenharmony_ci	char *xlib, *path, errbuf[256];
117d5ac70f0Sopenharmony_ci	void *h;
118d5ac70f0Sopenharmony_ci	int err = 0;
119d5ac70f0Sopenharmony_ci
120d5ac70f0Sopenharmony_ci	path = getenv("ALSA_MIXER_SIMPLE_MODULES");
121d5ac70f0Sopenharmony_ci	if (!path)
122d5ac70f0Sopenharmony_ci		path = SO_PATH;
123d5ac70f0Sopenharmony_ci	xlib = malloc(strlen(lib) + strlen(path) + 1 + 1);
124d5ac70f0Sopenharmony_ci	if (xlib == NULL)
125d5ac70f0Sopenharmony_ci		return -ENOMEM;
126d5ac70f0Sopenharmony_ci	strcpy(xlib, path);
127d5ac70f0Sopenharmony_ci	strcat(xlib, "/");
128d5ac70f0Sopenharmony_ci	strcat(xlib, lib);
129d5ac70f0Sopenharmony_ci	/* note python modules requires RTLD_GLOBAL */
130d5ac70f0Sopenharmony_ci	h = INTERNAL(snd_dlopen)(xlib, RTLD_NOW|RTLD_GLOBAL, errbuf, sizeof(errbuf));
131d5ac70f0Sopenharmony_ci	if (h == NULL) {
132d5ac70f0Sopenharmony_ci		SNDERR("Unable to open library '%s'", xlib);
133d5ac70f0Sopenharmony_ci		free(xlib);
134d5ac70f0Sopenharmony_ci		return -ENXIO;
135d5ac70f0Sopenharmony_ci	}
136d5ac70f0Sopenharmony_ci	priv->dlhandle = h;
137d5ac70f0Sopenharmony_ci	event_func = snd_dlsym(h, "alsa_mixer_simple_event", NULL);
138d5ac70f0Sopenharmony_ci	if (event_func == NULL) {
139d5ac70f0Sopenharmony_ci		SNDERR("Symbol 'alsa_mixer_simple_event' was not found in '%s'", xlib);
140d5ac70f0Sopenharmony_ci		err = -ENXIO;
141d5ac70f0Sopenharmony_ci	}
142d5ac70f0Sopenharmony_ci	if (err == 0) {
143d5ac70f0Sopenharmony_ci		init_func = snd_dlsym(h, "alsa_mixer_simple_finit", NULL);
144d5ac70f0Sopenharmony_ci		if (init_func == NULL) {
145d5ac70f0Sopenharmony_ci			SNDERR("Symbol 'alsa_mixer_simple_finit' was not found in '%s'", xlib);
146d5ac70f0Sopenharmony_ci			err = -ENXIO;
147d5ac70f0Sopenharmony_ci		}
148d5ac70f0Sopenharmony_ci	}
149d5ac70f0Sopenharmony_ci	free(xlib);
150d5ac70f0Sopenharmony_ci	err = err == 0 ? init_func(class, mixer, device) : err;
151d5ac70f0Sopenharmony_ci	if (err < 0)
152d5ac70f0Sopenharmony_ci		return err;
153d5ac70f0Sopenharmony_ci	snd_mixer_class_set_event(class, event_func);
154d5ac70f0Sopenharmony_ci	return 1;
155d5ac70f0Sopenharmony_ci}
156d5ac70f0Sopenharmony_ci
157d5ac70f0Sopenharmony_cistatic int match(snd_mixer_class_t *class, const char *lib, const char *searchl)
158d5ac70f0Sopenharmony_ci{
159d5ac70f0Sopenharmony_ci	class_priv_t *priv = snd_mixer_class_get_private(class);
160d5ac70f0Sopenharmony_ci	const char *components;
161d5ac70f0Sopenharmony_ci
162d5ac70f0Sopenharmony_ci	if (searchl == NULL)
163d5ac70f0Sopenharmony_ci		return try_open(class, lib);
164d5ac70f0Sopenharmony_ci	components = snd_ctl_card_info_get_components(priv->info);
165d5ac70f0Sopenharmony_ci	while (*components != '\0') {
166d5ac70f0Sopenharmony_ci		if (!strncmp(components, searchl, strlen(searchl)))
167d5ac70f0Sopenharmony_ci			return try_open(class, lib);
168d5ac70f0Sopenharmony_ci		while (*components != ' ' && *components != '\0')
169d5ac70f0Sopenharmony_ci			components++;
170d5ac70f0Sopenharmony_ci		while (*components == ' ' && *components != '\0')
171d5ac70f0Sopenharmony_ci			components++;
172d5ac70f0Sopenharmony_ci	}
173d5ac70f0Sopenharmony_ci	return 0;
174d5ac70f0Sopenharmony_ci}
175d5ac70f0Sopenharmony_ci
176d5ac70f0Sopenharmony_cistatic int find_full(snd_mixer_class_t *class, snd_mixer_t *mixer,
177d5ac70f0Sopenharmony_ci		     snd_config_t *top, const char *device)
178d5ac70f0Sopenharmony_ci{
179d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
180d5ac70f0Sopenharmony_ci	char *lib;
181d5ac70f0Sopenharmony_ci	const char *id;
182d5ac70f0Sopenharmony_ci	int err;
183d5ac70f0Sopenharmony_ci
184d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, top) {
185d5ac70f0Sopenharmony_ci		snd_config_t *n = snd_config_iterator_entry(i);
186d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
187d5ac70f0Sopenharmony_ci			continue;
188d5ac70f0Sopenharmony_ci		if (strcmp(id, "_full"))
189d5ac70f0Sopenharmony_ci			continue;
190d5ac70f0Sopenharmony_ci		err = snd_config_get_string(n, (const char **)&lib);
191d5ac70f0Sopenharmony_ci		if (err < 0)
192d5ac70f0Sopenharmony_ci			return err;
193d5ac70f0Sopenharmony_ci		err = try_open_full(class, mixer, lib, device);
194d5ac70f0Sopenharmony_ci		if (err < 0)
195d5ac70f0Sopenharmony_ci			return err;
196d5ac70f0Sopenharmony_ci		return 0;
197d5ac70f0Sopenharmony_ci	}
198d5ac70f0Sopenharmony_ci	return -ENOENT;
199d5ac70f0Sopenharmony_ci}
200d5ac70f0Sopenharmony_ci
201d5ac70f0Sopenharmony_cistatic int find_module(snd_mixer_class_t *class, snd_config_t *top)
202d5ac70f0Sopenharmony_ci{
203d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
204d5ac70f0Sopenharmony_ci	snd_config_iterator_t j, jnext;
205d5ac70f0Sopenharmony_ci	char *lib, *searchl;
206d5ac70f0Sopenharmony_ci	const char *id;
207d5ac70f0Sopenharmony_ci	int err;
208d5ac70f0Sopenharmony_ci
209d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, top) {
210d5ac70f0Sopenharmony_ci		snd_config_t *n = snd_config_iterator_entry(i);
211d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
212d5ac70f0Sopenharmony_ci			continue;
213d5ac70f0Sopenharmony_ci		if (*id == '_')
214d5ac70f0Sopenharmony_ci			continue;
215d5ac70f0Sopenharmony_ci		searchl = NULL;
216d5ac70f0Sopenharmony_ci		lib = NULL;
217d5ac70f0Sopenharmony_ci		snd_config_for_each(j, jnext, n) {
218d5ac70f0Sopenharmony_ci			snd_config_t *m = snd_config_iterator_entry(j);
219d5ac70f0Sopenharmony_ci			if (snd_config_get_id(m, &id) < 0)
220d5ac70f0Sopenharmony_ci				continue;
221d5ac70f0Sopenharmony_ci			if (!strcmp(id, "searchl")) {
222d5ac70f0Sopenharmony_ci				err = snd_config_get_string(m, (const char **)&searchl);
223d5ac70f0Sopenharmony_ci				if (err < 0)
224d5ac70f0Sopenharmony_ci					return err;
225d5ac70f0Sopenharmony_ci				continue;
226d5ac70f0Sopenharmony_ci			}
227d5ac70f0Sopenharmony_ci			if (!strcmp(id, "lib")) {
228d5ac70f0Sopenharmony_ci				err = snd_config_get_string(m, (const char **)&lib);
229d5ac70f0Sopenharmony_ci				if (err < 0)
230d5ac70f0Sopenharmony_ci					return err;
231d5ac70f0Sopenharmony_ci				continue;
232d5ac70f0Sopenharmony_ci			}
233d5ac70f0Sopenharmony_ci		}
234d5ac70f0Sopenharmony_ci		err = match(class, lib, searchl);
235d5ac70f0Sopenharmony_ci		if (err == 1)
236d5ac70f0Sopenharmony_ci			return 0;
237d5ac70f0Sopenharmony_ci		if (err < 0)
238d5ac70f0Sopenharmony_ci			return err;
239d5ac70f0Sopenharmony_ci	}
240d5ac70f0Sopenharmony_ci	return -ENOENT;
241d5ac70f0Sopenharmony_ci}
242d5ac70f0Sopenharmony_ci
243d5ac70f0Sopenharmony_cistatic void private_free(snd_mixer_class_t *class)
244d5ac70f0Sopenharmony_ci{
245d5ac70f0Sopenharmony_ci	class_priv_t *priv = snd_mixer_class_get_private(class);
246d5ac70f0Sopenharmony_ci
247d5ac70f0Sopenharmony_ci	if (priv->private_free)
248d5ac70f0Sopenharmony_ci		priv->private_free(class);
249d5ac70f0Sopenharmony_ci	if (priv->dlhandle)
250d5ac70f0Sopenharmony_ci		snd_dlclose(priv->dlhandle);
251d5ac70f0Sopenharmony_ci	if (priv->info)
252d5ac70f0Sopenharmony_ci		snd_ctl_card_info_free(priv->info);
253d5ac70f0Sopenharmony_ci	if (priv->hctl) {
254d5ac70f0Sopenharmony_ci		if (priv->attach_flag)
255d5ac70f0Sopenharmony_ci			snd_mixer_detach_hctl(snd_mixer_class_get_mixer(class), priv->hctl);
256d5ac70f0Sopenharmony_ci		snd_hctl_close(priv->hctl);
257d5ac70f0Sopenharmony_ci	} else if (priv->ctl)
258d5ac70f0Sopenharmony_ci		snd_ctl_close(priv->ctl);
259d5ac70f0Sopenharmony_ci	free(priv->device);
260d5ac70f0Sopenharmony_ci	free(priv);
261d5ac70f0Sopenharmony_ci}
262d5ac70f0Sopenharmony_ci
263d5ac70f0Sopenharmony_ci/**
264d5ac70f0Sopenharmony_ci * \brief Register mixer simple element class - basic abstraction
265d5ac70f0Sopenharmony_ci * \param mixer Mixer handle
266d5ac70f0Sopenharmony_ci * \param options Options container
267d5ac70f0Sopenharmony_ci * \param classp Pointer to returned mixer simple element class handle (or NULL
268d5ac70f0Sopenharmony_ci * \return 0 on success otherwise a negative error code
269d5ac70f0Sopenharmony_ci */
270d5ac70f0Sopenharmony_ciint snd_mixer_simple_basic_register(snd_mixer_t *mixer,
271d5ac70f0Sopenharmony_ci				    struct snd_mixer_selem_regopt *options,
272d5ac70f0Sopenharmony_ci				    snd_mixer_class_t **classp)
273d5ac70f0Sopenharmony_ci{
274d5ac70f0Sopenharmony_ci	snd_mixer_class_t *class;
275d5ac70f0Sopenharmony_ci	class_priv_t *priv = calloc(1, sizeof(*priv));
276d5ac70f0Sopenharmony_ci	const char *file;
277d5ac70f0Sopenharmony_ci	snd_input_t *input;
278d5ac70f0Sopenharmony_ci	snd_config_t *top = NULL;
279d5ac70f0Sopenharmony_ci	int err;
280d5ac70f0Sopenharmony_ci
281d5ac70f0Sopenharmony_ci	if (priv == NULL)
282d5ac70f0Sopenharmony_ci		return -ENOMEM;
283d5ac70f0Sopenharmony_ci	if (options->device == NULL) {
284d5ac70f0Sopenharmony_ci		free(priv);
285d5ac70f0Sopenharmony_ci		return -EINVAL;
286d5ac70f0Sopenharmony_ci	}
287d5ac70f0Sopenharmony_ci	if (snd_mixer_class_malloc(&class)) {
288d5ac70f0Sopenharmony_ci		free(priv);
289d5ac70f0Sopenharmony_ci		return -ENOMEM;
290d5ac70f0Sopenharmony_ci	}
291d5ac70f0Sopenharmony_ci	priv->device = strdup(options->device);
292d5ac70f0Sopenharmony_ci	if (priv->device == NULL) {
293d5ac70f0Sopenharmony_ci		free(priv);
294d5ac70f0Sopenharmony_ci		snd_mixer_class_free(class);
295d5ac70f0Sopenharmony_ci		return -ENOMEM;
296d5ac70f0Sopenharmony_ci	}
297d5ac70f0Sopenharmony_ci	snd_mixer_class_set_compare(class, snd_mixer_selem_compare);
298d5ac70f0Sopenharmony_ci	snd_mixer_class_set_private(class, priv);
299d5ac70f0Sopenharmony_ci	snd_mixer_class_set_private_free(class, private_free);
300d5ac70f0Sopenharmony_ci	file = getenv("ALSA_MIXER_SIMPLE");
301d5ac70f0Sopenharmony_ci	if (!file) {
302d5ac70f0Sopenharmony_ci		const char *topdir = snd_config_topdir();
303d5ac70f0Sopenharmony_ci		char *s = alloca(strlen(topdir) + strlen("smixer.conf") + 2);
304d5ac70f0Sopenharmony_ci		sprintf(s, "%s/smixer.conf", topdir);
305d5ac70f0Sopenharmony_ci		file = s;
306d5ac70f0Sopenharmony_ci	}
307d5ac70f0Sopenharmony_ci	err = snd_config_top(&top);
308d5ac70f0Sopenharmony_ci	if (err >= 0) {
309d5ac70f0Sopenharmony_ci		err = snd_input_stdio_open(&input, file, "r");
310d5ac70f0Sopenharmony_ci		if (err < 0) {
311d5ac70f0Sopenharmony_ci			SNDERR("unable to open simple mixer configuration file '%s'", file);
312d5ac70f0Sopenharmony_ci			goto __error;
313d5ac70f0Sopenharmony_ci		}
314d5ac70f0Sopenharmony_ci		err = snd_config_load(top, input);
315d5ac70f0Sopenharmony_ci		snd_input_close(input);
316d5ac70f0Sopenharmony_ci		if (err < 0) {
317d5ac70f0Sopenharmony_ci			SNDERR("%s may be old or corrupted: consider to remove or fix it", file);
318d5ac70f0Sopenharmony_ci			goto __error;
319d5ac70f0Sopenharmony_ci		}
320d5ac70f0Sopenharmony_ci		err = find_full(class, mixer, top, priv->device);
321d5ac70f0Sopenharmony_ci		if (err >= 0)
322d5ac70f0Sopenharmony_ci			goto __full;
323d5ac70f0Sopenharmony_ci	}
324d5ac70f0Sopenharmony_ci	if (err >= 0) {
325d5ac70f0Sopenharmony_ci		err = snd_ctl_open(&priv->ctl, priv->device, 0);
326d5ac70f0Sopenharmony_ci		if (err < 0) {
327d5ac70f0Sopenharmony_ci			SNDERR("unable to open control device '%s': %s", priv->device, snd_strerror(err));
328d5ac70f0Sopenharmony_ci			goto __error;
329d5ac70f0Sopenharmony_ci		}
330d5ac70f0Sopenharmony_ci		err = snd_hctl_open_ctl(&priv->hctl, priv->ctl);
331d5ac70f0Sopenharmony_ci		if (err < 0)
332d5ac70f0Sopenharmony_ci			goto __error;
333d5ac70f0Sopenharmony_ci		err = snd_ctl_card_info_malloc(&priv->info);
334d5ac70f0Sopenharmony_ci		if (err < 0)
335d5ac70f0Sopenharmony_ci			goto __error;
336d5ac70f0Sopenharmony_ci		err = snd_ctl_card_info(priv->ctl, priv->info);
337d5ac70f0Sopenharmony_ci		if (err < 0)
338d5ac70f0Sopenharmony_ci			goto __error;
339d5ac70f0Sopenharmony_ci	}
340d5ac70f0Sopenharmony_ci	if (err >= 0)
341d5ac70f0Sopenharmony_ci		err = find_module(class, top);
342d5ac70f0Sopenharmony_ci	if (err >= 0)
343d5ac70f0Sopenharmony_ci		err = snd_mixer_attach_hctl(mixer, priv->hctl);
344d5ac70f0Sopenharmony_ci	if (err >= 0) {
345d5ac70f0Sopenharmony_ci		priv->attach_flag = 1;
346d5ac70f0Sopenharmony_ci		err = snd_mixer_class_register(class, mixer);
347d5ac70f0Sopenharmony_ci	}
348d5ac70f0Sopenharmony_ci      __full:
349d5ac70f0Sopenharmony_ci	if (err < 0) {
350d5ac70f0Sopenharmony_ci	      __error:
351d5ac70f0Sopenharmony_ci		if (top)
352d5ac70f0Sopenharmony_ci			snd_config_delete(top);
353d5ac70f0Sopenharmony_ci	      	if (class)
354d5ac70f0Sopenharmony_ci			snd_mixer_class_free(class);
355d5ac70f0Sopenharmony_ci		return err;
356d5ac70f0Sopenharmony_ci	}
357d5ac70f0Sopenharmony_ci	if (top)
358d5ac70f0Sopenharmony_ci		snd_config_delete(top);
359d5ac70f0Sopenharmony_ci	if (classp)
360d5ac70f0Sopenharmony_ci		*classp = class;
361d5ac70f0Sopenharmony_ci	return 0;
362d5ac70f0Sopenharmony_ci}
363d5ac70f0Sopenharmony_ci
364d5ac70f0Sopenharmony_ci/**
365d5ac70f0Sopenharmony_ci * \brief Basic Mixer Abstraction - Get information about device
366d5ac70f0Sopenharmony_ci * \param class Mixer class
367d5ac70f0Sopenharmony_ci * \param info Info structure
368d5ac70f0Sopenharmony_ci * \return 0 on success otherwise a negative error code
369d5ac70f0Sopenharmony_ci */
370d5ac70f0Sopenharmony_ciint snd_mixer_sbasic_info(const snd_mixer_class_t *class, sm_class_basic_t *info)
371d5ac70f0Sopenharmony_ci{
372d5ac70f0Sopenharmony_ci	class_priv_t *priv = snd_mixer_class_get_private(class);
373d5ac70f0Sopenharmony_ci
374d5ac70f0Sopenharmony_ci	if (class == NULL || info == NULL)
375d5ac70f0Sopenharmony_ci		return -EINVAL;
376d5ac70f0Sopenharmony_ci	info->device = priv->device;
377d5ac70f0Sopenharmony_ci	info->ctl = priv->ctl;
378d5ac70f0Sopenharmony_ci	info->hctl = priv->hctl;
379d5ac70f0Sopenharmony_ci	info->info = priv->info;
380d5ac70f0Sopenharmony_ci	return 0;
381d5ac70f0Sopenharmony_ci}
382d5ac70f0Sopenharmony_ci
383d5ac70f0Sopenharmony_ci/**
384d5ac70f0Sopenharmony_ci * \brief Get private data for basic abstraction
385d5ac70f0Sopenharmony_ci * \param class Mixer class
386d5ac70f0Sopenharmony_ci * \return private data
387d5ac70f0Sopenharmony_ci */
388d5ac70f0Sopenharmony_civoid *snd_mixer_sbasic_get_private(const snd_mixer_class_t *class)
389d5ac70f0Sopenharmony_ci{
390d5ac70f0Sopenharmony_ci	class_priv_t *priv = snd_mixer_class_get_private(class);
391d5ac70f0Sopenharmony_ci
392d5ac70f0Sopenharmony_ci	if (class == NULL)
393d5ac70f0Sopenharmony_ci		return NULL;
394d5ac70f0Sopenharmony_ci	return priv->private_data;
395d5ac70f0Sopenharmony_ci}
396d5ac70f0Sopenharmony_ci
397d5ac70f0Sopenharmony_ci/**
398d5ac70f0Sopenharmony_ci * \brief Set private data for basic abstraction
399d5ac70f0Sopenharmony_ci * \param class Mixer class
400d5ac70f0Sopenharmony_ci * \param private_data Private data
401d5ac70f0Sopenharmony_ci */
402d5ac70f0Sopenharmony_civoid snd_mixer_sbasic_set_private(const snd_mixer_class_t *class, void *private_data)
403d5ac70f0Sopenharmony_ci{
404d5ac70f0Sopenharmony_ci	class_priv_t *priv;
405d5ac70f0Sopenharmony_ci
406d5ac70f0Sopenharmony_ci	if (class == NULL)
407d5ac70f0Sopenharmony_ci		return;
408d5ac70f0Sopenharmony_ci	priv = snd_mixer_class_get_private(class);
409d5ac70f0Sopenharmony_ci	priv->private_data = private_data;
410d5ac70f0Sopenharmony_ci}
411d5ac70f0Sopenharmony_ci
412d5ac70f0Sopenharmony_ci/**
413d5ac70f0Sopenharmony_ci * \brief Set private data free callback for basic abstraction
414d5ac70f0Sopenharmony_ci * \param class Mixer class
415d5ac70f0Sopenharmony_ci * \param private_free free callback for private data
416d5ac70f0Sopenharmony_ci */
417d5ac70f0Sopenharmony_civoid snd_mixer_sbasic_set_private_free(const snd_mixer_class_t *class, void (*private_free)(snd_mixer_class_t *class))
418d5ac70f0Sopenharmony_ci{
419d5ac70f0Sopenharmony_ci	class_priv_t *priv;
420d5ac70f0Sopenharmony_ci
421d5ac70f0Sopenharmony_ci	if (class == NULL)
422d5ac70f0Sopenharmony_ci		return;
423d5ac70f0Sopenharmony_ci	priv = snd_mixer_class_get_private(class);
424d5ac70f0Sopenharmony_ci	priv->private_free = private_free;
425d5ac70f0Sopenharmony_ci}
426