00001
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <unistd.h>
00033 #include <string.h>
00034 #include <fcntl.h>
00035 #include <sys/ioctl.h>
00036 #include <math.h>
00037 #include <dlfcn.h>
00038 #include "config.h"
00039 #include "asoundlib.h"
00040 #include "mixer_simple.h"
00041
00042 #ifndef DOC_HIDDEN
00043
00044 #define SO_PATH PKGLIBDIR "/smixer"
00045
00046 typedef struct _class_priv {
00047 char *device;
00048 snd_ctl_t *ctl;
00049 snd_hctl_t *hctl;
00050 int attach_flag;
00051 snd_ctl_card_info_t *info;
00052 void *dlhandle;
00053 void *private_data;
00054 void (*private_free)(snd_mixer_class_t *class);
00055 } class_priv_t;
00056
00057 typedef int (*snd_mixer_sbasic_init_t)(snd_mixer_class_t *class);
00058
00059 #endif
00060
00061 static int try_open(snd_mixer_class_t *class, const char *lib)
00062 {
00063 class_priv_t *priv = snd_mixer_class_get_private(class);
00064 snd_mixer_event_t event_func;
00065 snd_mixer_sbasic_init_t init_func;
00066 char *xlib, *path;
00067 void *h;
00068 int err;
00069
00070 path = getenv("ALSA_MIXER_SIMPLE_MODULES");
00071 if (!path)
00072 path = SO_PATH;
00073 xlib = malloc(strlen(lib) + strlen(path) + 1 + 1);
00074 if (xlib == NULL)
00075 return -ENOMEM;
00076 strcpy(xlib, path);
00077 strcat(xlib, "/");
00078 strcat(xlib, lib);
00079 h = snd_dlopen(xlib, RTLD_NOW);
00080 if (h == NULL) {
00081 SNDERR("Unable to open library '%s'", xlib);
00082 free(xlib);
00083 return -ENXIO;
00084 }
00085 event_func = dlsym(h, "alsa_mixer_simple_event");
00086 if (event_func == NULL) {
00087 SNDERR("Symbol 'alsa_mixer_simple_event' was not found in '%s'", xlib);
00088 snd_dlclose(h);
00089 free(xlib);
00090 return -ENXIO;
00091 }
00092 init_func = dlsym(h, "alsa_mixer_simple_init");
00093 if (init_func == NULL) {
00094 SNDERR("Symbol 'alsa_mixer_simple_init' was not found in '%s'", xlib);
00095 snd_dlclose(h);
00096 free(xlib);
00097 return -ENXIO;
00098 }
00099 free(xlib);
00100 err = init_func(class);
00101 if (err < 0) {
00102 snd_dlclose(h);
00103 return err;
00104 }
00105 snd_mixer_class_set_event(class, event_func);
00106 priv->dlhandle = h;
00107 return 1;
00108 }
00109
00110 static int match(snd_mixer_class_t *class, const char *lib, const char *searchl)
00111 {
00112 class_priv_t *priv = snd_mixer_class_get_private(class);
00113 const char *components;
00114
00115 if (searchl == NULL)
00116 return try_open(class, lib);
00117 components = snd_ctl_card_info_get_components(priv->info);
00118 while (*components != '\0') {
00119 if (!strncmp(components, searchl, strlen(searchl)))
00120 return try_open(class, lib);
00121 while (*components != ' ' && *components != '\0')
00122 components++;
00123 while (*components == ' ' && *components != '\0')
00124 components++;
00125 }
00126 return 0;
00127 }
00128
00129 static int find_module(snd_mixer_class_t *class, snd_config_t *top)
00130 {
00131 snd_config_iterator_t i, next;
00132 snd_config_iterator_t j, jnext;
00133 char *lib, *searchl;
00134 const char *id;
00135 int err;
00136
00137 snd_config_for_each(i, next, top) {
00138 snd_config_t *n = snd_config_iterator_entry(i);
00139 if (snd_config_get_id(n, &id) < 0)
00140 continue;
00141 searchl = NULL;
00142 lib = NULL;
00143 snd_config_for_each(j, jnext, n) {
00144 snd_config_t *m = snd_config_iterator_entry(j);
00145 if (snd_config_get_id(m, &id) < 0)
00146 continue;
00147 if (!strcmp(id, "searchl")) {
00148 err = snd_config_get_string(m, (const char **)&searchl);
00149 if (err < 0)
00150 return err;
00151 continue;
00152 }
00153 if (!strcmp(id, "lib")) {
00154 err = snd_config_get_string(m, (const char **)&lib);
00155 if (err < 0)
00156 return err;
00157 continue;
00158 }
00159 }
00160 err = match(class, lib, searchl);
00161 if (err == 1)
00162 return 0;
00163 if (err < 0)
00164 return err;
00165 }
00166 return -ENOENT;
00167 }
00168
00169 static void private_free(snd_mixer_class_t *class)
00170 {
00171 class_priv_t *priv = snd_mixer_class_get_private(class);
00172
00173 if (priv->private_free)
00174 priv->private_free(class);
00175 if (priv->dlhandle)
00176 snd_dlclose(priv->dlhandle);
00177 if (priv->info)
00178 snd_ctl_card_info_free(priv->info);
00179 if (priv->hctl) {
00180 if (priv->attach_flag)
00181 snd_mixer_detach_hctl(snd_mixer_class_get_mixer(class), priv->hctl);
00182 snd_hctl_close(priv->hctl);
00183 } else if (priv->ctl)
00184 snd_ctl_close(priv->ctl);
00185 free(priv->device);
00186 free(priv);
00187 }
00188
00196 int snd_mixer_simple_basic_register(snd_mixer_t *mixer,
00197 struct snd_mixer_selem_regopt *options,
00198 snd_mixer_class_t **classp)
00199 {
00200 snd_mixer_class_t *class;
00201 class_priv_t *priv = calloc(1, sizeof(*priv));
00202 const char *file;
00203 snd_input_t *input;
00204 snd_config_t *top = NULL;
00205 int err;
00206
00207 if (priv == NULL)
00208 return -ENOMEM;
00209 if (options->device == NULL) {
00210 free(priv);
00211 return -EINVAL;
00212 }
00213 if (snd_mixer_class_malloc(&class)) {
00214 free(priv);
00215 return -ENOMEM;
00216 }
00217 priv->device = strdup(options->device);
00218 if (priv->device == NULL) {
00219 free(priv);
00220 snd_mixer_class_free(class);
00221 return -ENOMEM;
00222 }
00223 snd_mixer_class_set_compare(class, snd_mixer_selem_compare);
00224 snd_mixer_class_set_private(class, priv);
00225 snd_mixer_class_set_private_free(class, private_free);
00226 err = snd_ctl_open(&priv->ctl, priv->device, 0);
00227 if (err < 0) {
00228 SNDERR("unable to open control device '%s': %s", priv->device, snd_strerror(err));
00229 goto __error;
00230 }
00231 err = snd_hctl_open_ctl(&priv->hctl, priv->ctl);
00232 if (err < 0)
00233 goto __error;
00234 err = snd_ctl_card_info_malloc(&priv->info);
00235 if (err < 0)
00236 goto __error;
00237 err = snd_ctl_card_info(priv->ctl, priv->info);
00238 if (err < 0)
00239 goto __error;
00240 file = getenv("ALSA_MIXER_SIMPLE");
00241 if (!file)
00242 file = "/share" "/alsa/smixer.conf";
00243 err = snd_config_top(&top);
00244 if (err >= 0) {
00245 err = snd_input_stdio_open(&input, file, "r");
00246 if (err < 0) {
00247 SNDERR("unable to open simple mixer configuration file '%s'", file);
00248 goto __error;
00249 }
00250 err = snd_config_load(top, input);
00251 snd_input_close(input);
00252 if (err < 0) {
00253 SNDERR("%s may be old or corrupted: consider to remove or fix it", file);
00254 goto __error;
00255 }
00256 err = find_module(class, top);
00257 snd_config_delete(top);
00258 top = NULL;
00259 }
00260 if (err >= 0)
00261 err = snd_mixer_attach_hctl(mixer, priv->hctl);
00262 if (err >= 0) {
00263 priv->attach_flag = 1;
00264 err = snd_mixer_class_register(class, mixer);
00265 }
00266 if (err < 0) {
00267 __error:
00268 if (top)
00269 snd_config_delete(top);
00270 if (class)
00271 snd_mixer_class_free(class);
00272 return err;
00273 }
00274 if (top)
00275 snd_config_delete(top);
00276 if (classp)
00277 *classp = class;
00278 return 0;
00279 }
00280
00287 int snd_mixer_sbasic_info(const snd_mixer_class_t *class, sm_class_basic_t *info)
00288 {
00289 class_priv_t *priv = snd_mixer_class_get_private(class);
00290
00291 if (class == NULL || info == NULL)
00292 return -EINVAL;
00293 info->device = priv->device;
00294 info->ctl = priv->ctl;
00295 info->hctl = priv->hctl;
00296 info->info = priv->info;
00297 return 0;
00298 }
00299
00305 void *snd_mixer_sbasic_get_private(const snd_mixer_class_t *class)
00306 {
00307 class_priv_t *priv = snd_mixer_class_get_private(class);
00308
00309 if (class == NULL)
00310 return NULL;
00311 return priv->private_data;
00312 }
00313
00319 void snd_mixer_sbasic_set_private(const snd_mixer_class_t *class, void *private_data)
00320 {
00321 class_priv_t *priv;
00322
00323 if (class == NULL)
00324 return;
00325 priv = snd_mixer_class_get_private(class);
00326 priv->private_data = private_data;
00327 }
00328
00334 void snd_mixer_sbasic_set_private_free(const snd_mixer_class_t *class, void (*private_free)(snd_mixer_class_t *class))
00335 {
00336 class_priv_t *priv;
00337
00338 if (class == NULL)
00339 return;
00340 priv = snd_mixer_class_get_private(class);
00341 priv->private_free = private_free;
00342 }