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