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