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 "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