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