diff options
Diffstat (limited to 'src/mixer/mixer.c')
-rw-r--r-- | src/mixer/mixer.c | 1215 |
1 files changed, 562 insertions, 653 deletions
diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c index 85d843fc..cece6541 100644 --- a/src/mixer/mixer.c +++ b/src/mixer/mixer.c @@ -3,7 +3,7 @@ * \brief Mixer Interface * \author Jaroslav Kysela <perex@perex.cz> * \author Abramo Bagnara <abramo@alsa-project.org> - * \date 2001 + * \date 2001-2009 * * Mixer interface is designed to access mixer elements. * Callbacks may be used for event handling. @@ -30,12 +30,49 @@ * */ -/*! \page mixer Mixer interface +/*! \page amixer Mixer interface -<P>Mixer interface is designed to access the abstracted mixer controls. -This is an abstraction layer over the hcontrol layer. +<P>Mixer interface is designed to access the abstracted amixer controls. -\section mixer_general_overview General overview +\section amixer_general_overview General overview + +\section amixer_global Global mixer + +The global mixer exposes basic or all (#SND_AMIXER_ALL) mixer related +controls to application. + +\par Master +This control element defines playback master volume control for +whole card. + +\section amixer_pcm PCM related mixer + +This mixer works with PCM related controls with predefined abstractions. + +\subsection amixer_pcm_playback Playback direction + +Bellow mixer controls are available for playback PCM. + +\par Master +Playback master volume control. + +\par PCM +Playback PCM stream related volume control. + +\subsection amixer_pcm_capture Capture direction + +Note that none or any combination of controls might be present, but +at least Capture control should be implemented in alsa-lib. + +\par Capture +Capture PCM stream related volume control. + +\par Source +Capture Source (enum like Mic,CD,Line etc.). + +\par [other] - like CD, Aux, Front Line etc. +These sources are mixed to PCM input. Both volume and switch might be +available. */ @@ -46,103 +83,342 @@ This is an abstraction layer over the hcontrol layer. #include <fcntl.h> #include <sys/ioctl.h> #include "mixer_local.h" - -#ifndef DOC_HIDDEN -typedef struct _snd_mixer_slave { - snd_hctl_t *hctl; - struct list_head list; -} snd_mixer_slave_t; - +#include "mixer_abst.h" + +static int ctl_event_handler(snd_ctl_t *ctl, + unsigned int mask, + snd_ctl_elem_t *elem); + +static const char *const build_in_mixers[] = { + "none", NULL +}; + +static int snd_amixer_open_conf(snd_amixer_t **amixerp, + const char *name, + snd_config_t *mixer_root, + snd_config_t *mixer_conf, + snd_pcm_t *pcm_playback, + snd_pcm_t *pcm_capture, + int mode) +{ + const char *str; + char *buf = NULL, *buf1 = NULL; + int err, idx; + snd_config_t *conf, *type_conf = NULL; + snd_config_iterator_t i, next; + const char *id; + const char *lib = NULL, *open_name = NULL; + int (*open_func)(snd_amixer_t *amixer, + snd_config_t *root, + snd_config_t *conf, + struct sm_open *sm_open) = NULL; +#ifndef PIC + extern void *snd_amixer_open_symbols(void); +#endif + void *h = NULL; + snd_amixer_t *amixer; + if (snd_config_get_type(mixer_conf) != SND_CONFIG_TYPE_COMPOUND) { + char *val; + id = NULL; + snd_config_get_id(mixer_conf, &id); + val = NULL; + snd_config_get_ascii(mixer_conf, &val); + SNDERR("Invalid type for mixer %s%sdefinition (id: %s, value: %s)", name ? name : "", name ? " " : "", id, val); + free(val); + return -EINVAL; + } + err = snd_config_search(mixer_conf, "type", &conf); + if (err < 0) { + SNDERR("type is not defined"); + return err; + } + err = snd_config_get_id(conf, &id); + if (err < 0) { + SNDERR("unable to get id"); + return err; + } + err = snd_config_get_string(conf, &str); + if (err < 0) { + SNDERR("Invalid type for %s", id); + return err; + } + err = snd_config_search_definition(mixer_root, "amixer_type", str, &type_conf); + if (err >= 0) { + if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("Invalid type for amixer type %s definition", str); + goto _err; + } + snd_config_for_each(i, next, type_conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + if (strcmp(id, "comment") == 0) + continue; + if (strcmp(id, "lib") == 0) { + err = snd_config_get_string(n, &lib); + if (err < 0) { + SNDERR("Invalid type for %s", id); + goto _err; + } + continue; + } + if (strcmp(id, "open") == 0) { + err = snd_config_get_string(n, &open_name); + if (err < 0) { + SNDERR("Invalid type for %s", id); + goto _err; + } + continue; + } + SNDERR("Unknown field %s", id); + err = -EINVAL; + goto _err; + } + } + if (!open_name) { + buf = malloc(strlen(str) + 32); + if (buf == NULL) { + err = -ENOMEM; + goto _err; + } + open_name = buf; + sprintf(buf, "_snd_amixer_%s_open", str); + } + if (!lib) { + const char *const *build_in = build_in_mixers; + while (*build_in) { + if (!strcmp(*build_in, str)) + break; + build_in++; + } + if (*build_in == NULL) { + buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32); + if (buf1 == NULL) { + err = -ENOMEM; + goto _err; + } + lib = buf1; + sprintf(buf1, "%s/libasound_module_mixer_%s.so", ALSA_PLUGIN_DIR, str); + } + } +#ifndef PIC + snd_mixer_open_symbols(); /* this call is for static linking only */ #endif + open_func = snd_dlobj_cache_lookup(open_name); + if (open_func) { + err = 0; + goto _err; + } + h = snd_dlopen(lib, RTLD_NOW); + if (h) + open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_AMIXER_DLSYM_VERSION)); + err = 0; + if (!h) { + SNDERR("Cannot open shared library %s", + lib ? lib : "[builtin]"); + err = -ENOENT; + } else if (!open_func) { + SNDERR("symbol %s is not defined inside %s", open_name, + lib ? lib : "[builtin]"); + snd_dlclose(h); + err = -ENXIO; + } + _err: + if (err >= 0) { + amixer = calloc(1, sizeof(*amixer)); + if (amixer == NULL) { + err = -ENOMEM; + goto _err1; + } + INIT_LIST_HEAD(&amixer->elems); + amixer->compare = snd_amixer_compare_default; + amixer->sm_open.name = name; + amixer->sm_open.pcm_playback = pcm_playback; + amixer->sm_open.pcm_capture = pcm_capture; + amixer->sm_open.mode = mode; + err = open_func(amixer, mixer_root, mixer_conf, &amixer->sm_open); + if (err < 0) { + snd_amixer_close(amixer); + goto _err1; + } + for (idx = 0; idx < SM_CTL_COUNT; idx++) { + snd_ctl_t *ctl = amixer->sm_open.ctl[idx]; + if (ctl == NULL) + continue; + snd_ctl_set_callback(ctl, ctl_event_handler); + snd_ctl_set_callback_private(ctl, amixer); + err = snd_hctl_nonblock(ctl, 1); + if (err >= 0) + err = snd_ctl_subscribe_events(ctl, 1); + if (err < 0) { + snd_amixer_close(amixer); + goto _err1; + } + } + *amixerp = amixer; + _err1: + if (err >= 0) { + if (h /*&& (mode & SND_PCM_KEEP_ALIVE)*/) { + snd_dlobj_cache_add(open_name, h, open_func); + h = NULL; + } + amixer->dl_handle = h; + err = 0; + } else { + if (h) + snd_dlclose(h); + } + } + if (type_conf) + snd_config_delete(type_conf); + free(buf); + free(buf1); + return err; +} + +static int snd_amixer_open_noupdate(snd_amixer_t **amixerp, + snd_config_t *root, + const char *name, + snd_pcm_t *pcm_playback, + snd_pcm_t *pcm_capture, + int mode, + int hop) +{ + int err, pcm = pcm_playback || pcm_capture; + snd_config_t *mixer_conf; + const char *str; -static int snd_mixer_compare_default(const snd_mixer_elem_t *c1, - const snd_mixer_elem_t *c2); + err = snd_config_search_definition(root, pcm ? "amixer_pcm" : "amixer", name, &mixer_conf); + if (err < 0) { + SNDERR("Uknown amixer %s", name); + return err; + } + if (snd_config_get_string(mixer_conf, &str) >= 0) { + err = snd_amixer_open_noupdate(amixerp, root, name, pcm_playback, pcm_capture, mode, hop); + } else { + snd_config_set_hop(mixer_conf, hop); + err = snd_amixer_open_conf(amixerp, name, root, mixer_conf, pcm_playback, pcm_capture, mode); + } + snd_config_delete(mixer_conf); + return err; +} +/** + * \brief Opens the global or PCM related mixer + * \param amixerp Returned amixer handle + * \param name ASCII identifier of the mixer handle + * \param pcm_playback Playback PCM + * \param pcm_capture Capture PCM + * \param mode Open mode + * \return 0 on success otherwise a negative error code + * + * If both pcm_playback and pcm_capture parameters are NULL, the global + * mixer is opened. + */ +int snd_amixer_open(snd_amixer_t **amixerp, + const char *name, + snd_pcm_t *pcm_playback, + snd_pcm_t *pcm_capture, + int mode) +{ + int err; + assert(amixerp); + assert(name); + err = snd_config_update(); + if (err < 0) + return err; + return snd_amixer_open_noupdate(amixerp, snd_config, name, pcm_playback, pcm_capture, mode, 0); +} /** - * \brief Opens an empty mixer - * \param mixerp Returned mixer handle + * \brief Opens the global or PCM related mixer using local configuration + * \param amixerp Returned amixer handle + * \param name ASCII identifier of the mixer handle + * \param pcm_playback Playback PCM + * \param pcm_capture Capture PCM * \param mode Open mode + * \param lconf Local configuration * \return 0 on success otherwise a negative error code + * + * If both pcm_playback and pcm_capture parameters are NULL, the global + * mixer is opened. */ -int snd_mixer_open(snd_mixer_t **mixerp, int mode ATTRIBUTE_UNUSED) +int snd_amixer_open_lconf(snd_amixer_t **amixerp, + const char *name, + snd_pcm_t *pcm_playback, + snd_pcm_t *pcm_capture, + int mode, + snd_config_t *lconf) { - snd_mixer_t *mixer; - assert(mixerp); - mixer = calloc(1, sizeof(*mixer)); - if (mixer == NULL) - return -ENOMEM; - INIT_LIST_HEAD(&mixer->slaves); - INIT_LIST_HEAD(&mixer->classes); - INIT_LIST_HEAD(&mixer->elems); - mixer->compare = snd_mixer_compare_default; - *mixerp = mixer; - return 0; + assert(amixerp); + assert(name); + assert(lconf); + return snd_amixer_open_noupdate(amixerp, lconf, name, pcm_playback, pcm_capture, mode, 0); } /** - * \brief Attach an HCTL element to a mixer element + * \brief Attach an CTL element to a amixer element * \param melem Mixer element - * \param helem HCTL element + * \param elem CTL element * \return 0 on success otherwise a negative error code * - * For use by mixer element class specific code. + * For use by amixer element class specific code. */ -int snd_mixer_elem_attach(snd_mixer_elem_t *melem, - snd_hctl_elem_t *helem) +int snd_amixer_elem_attach(snd_amixer_elem_t *melem, + snd_ctl_elem_t *elem) { - bag_t *bag = snd_hctl_elem_get_callback_private(helem); + bag_t *bag = snd_ctl_elem_get_callback_private(elem); int err; err = bag_add(bag, melem); if (err < 0) return err; - return bag_add(&melem->helems, helem); + return bag_add(&melem->helems, elem); } /** - * \brief Detach an HCTL element from a mixer element + * \brief Detach an CTL element from a amixer element * \param melem Mixer element - * \param helem HCTL element + * \param elem CTL element * \return 0 on success otherwise a negative error code * - * For use by mixer element class specific code. + * For use by amixer element class specific code. */ -int snd_mixer_elem_detach(snd_mixer_elem_t *melem, - snd_hctl_elem_t *helem) +int snd_amixer_elem_detach(snd_amixer_elem_t *melem, + snd_ctl_elem_t *elem) { - bag_t *bag = snd_hctl_elem_get_callback_private(helem); + bag_t *bag = snd_ctl_elem_get_callback_private(elem); int err; err = bag_del(bag, melem); assert(err >= 0); - err = bag_del(&melem->helems, helem); + err = bag_del(&melem->helems, elem); assert(err >= 0); return 0; } /** - * \brief Return true if a mixer element does not contain any HCTL elements + * \brief Return true if a amixer element does not contain any CTL elements * \param melem Mixer element * \return 0 if not empty, 1 if empty * - * For use by mixer element class specific code. + * For use by amixer element class specific code. */ -int snd_mixer_elem_empty(snd_mixer_elem_t *melem) +int snd_amixer_elem_is_empty(snd_amixer_elem_t *melem) { return bag_empty(&melem->helems); } -static int hctl_elem_event_handler(snd_hctl_elem_t *helem, - unsigned int mask) +static int ctl_elem_event_handler(snd_ctl_elem_t *elem, + unsigned int mask) { - bag_t *bag = snd_hctl_elem_get_callback_private(helem); + bag_t *bag = snd_ctl_elem_get_callback_private(elem); if (mask == SND_CTL_EVENT_MASK_REMOVE) { int res = 0; int err; bag_iterator_t i, n; bag_for_each_safe(i, n, bag) { - snd_mixer_elem_t *melem = bag_iterator_entry(i); - snd_mixer_class_t *class = melem->class; - err = class->event(class, mask, helem, melem); + snd_amixer_elem_t *melem = bag_iterator_entry(i); + err = melem->amixer->event(melem->amixer, mask, elem, melem); if (err < 0) res = err; } @@ -154,9 +430,8 @@ static int hctl_elem_event_handler(snd_hctl_elem_t *helem, int err = 0; bag_iterator_t i, n; bag_for_each_safe(i, n, bag) { - snd_mixer_elem_t *melem = bag_iterator_entry(i); - snd_mixer_class_t *class = melem->class; - err = class->event(class, mask, helem, melem); + snd_amixer_elem_t *melem = bag_iterator_entry(i); + err = melem->amixer->event(melem->amixer, mask, elem, melem); if (err < 0) return err; } @@ -164,176 +439,57 @@ static int hctl_elem_event_handler(snd_hctl_elem_t *helem, return 0; } -static int hctl_event_handler(snd_hctl_t *hctl, unsigned int mask, - snd_hctl_elem_t *elem) +static int ctl_event_handler(snd_ctl_t *ctl, + unsigned int mask, + snd_ctl_elem_t *elem) { - snd_mixer_t *mixer = snd_hctl_get_callback_private(hctl); + snd_amixer_t *amixer = snd_ctl_get_callback_private(ctl); int res = 0; if (mask & SND_CTL_EVENT_MASK_ADD) { - struct list_head *pos; bag_t *bag; int err = bag_new(&bag); if (err < 0) return err; - snd_hctl_elem_set_callback(elem, hctl_elem_event_handler); - snd_hctl_elem_set_callback_private(elem, bag); - list_for_each(pos, &mixer->classes) { - snd_mixer_class_t *c; - c = list_entry(pos, snd_mixer_class_t, list); - err = c->event(c, mask, elem, NULL); - if (err < 0) - res = err; - } + snd_ctl_elem_set_callback(elem, ctl_elem_event_handler); + snd_ctl_elem_set_callback_private(elem, bag); + err = amixer->event(amixer, mask, elem, NULL); + if (err < 0) + return err; } return res; } - -/** - * \brief Attach an HCTL specified with the CTL device name to an opened mixer - * \param mixer Mixer handle - * \param name HCTL name (see #snd_hctl_open) - * \return 0 on success otherwise a negative error code - */ -int snd_mixer_attach(snd_mixer_t *mixer, const char *name) -{ - snd_hctl_t *hctl; - int err; - - err = snd_hctl_open(&hctl, name, 0); - if (err < 0) - return err; - err = snd_mixer_attach_hctl(mixer, hctl); - if (err < 0) { - snd_hctl_close(hctl); - return err; - } - return 0; -} - -/** - * \brief Attach an HCTL to an opened mixer - * \param mixer Mixer handle - * \param hctl the HCTL to be attached - * \return 0 on success otherwise a negative error code - */ -int snd_mixer_attach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl) -{ - snd_mixer_slave_t *slave; - int err; - - assert(hctl); - slave = calloc(1, sizeof(*slave)); - if (slave == NULL) - return -ENOMEM; - err = snd_hctl_nonblock(hctl, 1); - if (err < 0) { - snd_hctl_close(hctl); - free(slave); - return err; - } - snd_hctl_set_callback(hctl, hctl_event_handler); - snd_hctl_set_callback_private(hctl, mixer); - slave->hctl = hctl; - list_add_tail(&slave->list, &mixer->slaves); - return 0; -} - -/** - * \brief Detach a previously attached HCTL to an opened mixer freeing all related resources - * \param mixer Mixer handle - * \param name HCTL previously attached - * \return 0 on success otherwise a negative error code - */ -int snd_mixer_detach(snd_mixer_t *mixer, const char *name) -{ - struct list_head *pos; - list_for_each(pos, &mixer->slaves) { - snd_mixer_slave_t *s; - s = list_entry(pos, snd_mixer_slave_t, list); - if (strcmp(name, snd_hctl_name(s->hctl)) == 0) { - snd_hctl_close(s->hctl); - list_del(pos); - free(s); - return 0; - } - } - return -ENOENT; -} - -/** - * \brief Detach a previously attached HCTL to an opened mixer freeing all related resources - * \param mixer Mixer handle - * \param hctl HCTL previously attached - * \return 0 on success otherwise a negative error code - * - * Note: The hctl handle is not closed! - */ -int snd_mixer_detach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl) -{ - struct list_head *pos; - list_for_each(pos, &mixer->slaves) { - snd_mixer_slave_t *s; - s = list_entry(pos, snd_mixer_slave_t, list); - if (hctl == s->hctl) { - list_del(pos); - free(s); - return 0; - } - } - return -ENOENT; -} - -/** - * \brief Obtain a HCTL pointer associated to given name - * \param mixer Mixer handle - * \param name HCTL previously attached - * \param hctl HCTL pointer - * \return 0 on success otherwise a negative error code - */ -int snd_mixer_get_hctl(snd_mixer_t *mixer, const char *name, snd_hctl_t **hctl) -{ - struct list_head *pos; - list_for_each(pos, &mixer->slaves) { - snd_mixer_slave_t *s; - s = list_entry(pos, snd_mixer_slave_t, list); - if (strcmp(name, snd_hctl_name(s->hctl)) == 0) { - *hctl = s->hctl; - return 0; - } - } - return -ENOENT; -} - -static int snd_mixer_throw_event(snd_mixer_t *mixer, unsigned int mask, - snd_mixer_elem_t *elem) +static int snd_amixer_throw_event(snd_amixer_t *amixer, + unsigned int mask, + snd_amixer_elem_t *elem) { - mixer->events++; - if (mixer->callback) - return mixer->callback(mixer, mask, elem); + amixer->events++; + if (amixer->callback) + return amixer->callback(amixer, mask, elem); return 0; } -static int snd_mixer_elem_throw_event(snd_mixer_elem_t *elem, unsigned int mask) +static int snd_amixer_elem_throw_event(snd_amixer_elem_t *elem, + unsigned int mask) { - elem->class->mixer->events++; + elem->amixer->events++; if (elem->callback) return elem->callback(elem, mask); return 0; } -static int _snd_mixer_find_elem(snd_mixer_t *mixer, snd_mixer_elem_t *elem, int *dir) +static int _snd_amixer_find_elem(snd_amixer_t *amixer, snd_amixer_elem_t *elem, int *dir) { unsigned int l, u; int c = 0; int idx = -1; - assert(mixer && elem); - assert(mixer->compare); + assert(amixer && elem); + assert(amixer->compare); l = 0; - u = mixer->count; + u = amixer->count; while (l < u) { idx = (l + u) / 2; - c = mixer->compare(elem, mixer->pelems[idx]); + c = amixer->compare(elem, amixer->pelems[idx]); if (c < 0) u = idx; else if (c > 0) @@ -346,38 +502,37 @@ static int _snd_mixer_find_elem(snd_mixer_t *mixer, snd_mixer_elem_t *elem, int } /** - * \brief Get private data associated to give mixer element + * \brief Get private data associated to give amixer element * \param elem Mixer element * \return private data - * - * For use by mixer element class specific code. */ -void *snd_mixer_elem_get_private(const snd_mixer_elem_t *elem) +void *snd_amixer_elem_get_private(const snd_amixer_elem_t *elem) { return elem->private_data; } /** - * \brief Allocate a new mixer element - * \param elem Returned mixer element - * \param type Mixer element type + * \brief Allocate a new amixer element + * \param amixer Mixer handle + * \param elem Returned amixer element + * \param id Element identificator * \param compare_weight Mixer element compare weight * \param private_data Private data * \param private_free Private data free callback * \return 0 on success otherwise a negative error code - * - * For use by mixer element class specific code. */ -int snd_mixer_elem_new(snd_mixer_elem_t **elem, - snd_mixer_elem_type_t type, - int compare_weight, - void *private_data, - void (*private_free)(snd_mixer_elem_t *elem)) +int snd_amixer_elem_new(snd_amixer_t *amixer, + snd_amixer_elem_t **elem, + snd_amixer_elem_id_t *id, + int compare_weight, + void *private_data, + void (*private_free)(snd_amixer_elem_t *elem)) { - snd_mixer_elem_t *melem = calloc(1, sizeof(*melem)); + snd_amixer_elem_t *melem = calloc(1, sizeof(*melem)); if (melem == NULL) return -ENOMEM; - melem->type = type; + melem->amixer = amixer; + melem->sm.id = *id; melem->compare_weight = compare_weight; melem->private_data = private_data; melem->private_free = private_free; @@ -387,92 +542,84 @@ int snd_mixer_elem_new(snd_mixer_elem_t **elem, } /** - * \brief Add an element for a registered mixer element class + * \brief Add an element for an amixer handle + * \param amixer Mixer handle * \param elem Mixer element - * \param class Mixer element class * \return 0 on success otherwise a negative error code - * - * For use by mixer element class specific code. */ -int snd_mixer_elem_add(snd_mixer_elem_t *elem, snd_mixer_class_t *class) +int snd_amixer_elem_add(snd_amixer_t *amixer, snd_amixer_elem_t *elem) { int dir, idx; - snd_mixer_t *mixer = class->mixer; - elem->class = class; - if (mixer->count == mixer->alloc) { - snd_mixer_elem_t **m; - mixer->alloc += 32; - m = realloc(mixer->pelems, sizeof(*m) * mixer->alloc); + if (amixer->count == amixer->alloc) { + snd_amixer_elem_t **m; + amixer->alloc += 32; + m = realloc(amixer->pelems, sizeof(*m) * amixer->alloc); if (!m) { - mixer->alloc -= 32; + amixer->alloc -= 32; return -ENOMEM; } - mixer->pelems = m; + amixer->pelems = m; } - if (mixer->count == 0) { - list_add_tail(&elem->list, &mixer->elems); - mixer->pelems[0] = elem; + if (amixer->count == 0) { + list_add_tail(&elem->list, &amixer->elems); + amixer->pelems[0] = elem; } else { - idx = _snd_mixer_find_elem(mixer, elem, &dir); + idx = _snd_amixer_find_elem(amixer, elem, &dir); assert(dir != 0); if (dir > 0) { - list_add(&elem->list, &mixer->pelems[idx]->list); + list_add(&elem->list, &amixer->pelems[idx]->list); idx++; } else { - list_add_tail(&elem->list, &mixer->pelems[idx]->list); + list_add_tail(&elem->list, &amixer->pelems[idx]->list); } - memmove(mixer->pelems + idx + 1, - mixer->pelems + idx, - (mixer->count - idx) * sizeof(snd_mixer_elem_t *)); - mixer->pelems[idx] = elem; + memmove(amixer->pelems + idx + 1, + amixer->pelems + idx, + (amixer->count - idx) * sizeof(snd_amixer_elem_t *)); + amixer->pelems[idx] = elem; } - mixer->count++; - return snd_mixer_throw_event(mixer, SND_CTL_EVENT_MASK_ADD, elem); + amixer->count++; + return snd_amixer_throw_event(amixer, SND_CTL_EVENT_MASK_ADD, elem); } /** - * \brief Remove a mixer element + * \brief Remove a amixer element * \param elem Mixer element * \return 0 on success otherwise a negative error code - * - * For use by mixer element class specific code. */ -int snd_mixer_elem_remove(snd_mixer_elem_t *elem) +int snd_amixer_elem_remove(snd_amixer_elem_t *elem) { - snd_mixer_t *mixer = elem->class->mixer; + snd_amixer_t *amixer = elem->amixer; bag_iterator_t i, n; int err, idx, dir; unsigned int m; assert(elem); - assert(mixer->count); - idx = _snd_mixer_find_elem(mixer, elem, &dir); + assert(amixer->count); + idx = _snd_amixer_find_elem(amixer, elem, &dir); if (dir != 0) return -EINVAL; bag_for_each_safe(i, n, &elem->helems) { - snd_hctl_elem_t *helem = bag_iterator_entry(i); - snd_mixer_elem_detach(elem, helem); + snd_ctl_elem_t *helem = bag_iterator_entry(i); + snd_amixer_elem_detach(elem, helem); } - err = snd_mixer_elem_throw_event(elem, SND_CTL_EVENT_MASK_REMOVE); + err = snd_amixer_elem_throw_event(elem, SND_CTL_EVENT_MASK_REMOVE); list_del(&elem->list); - snd_mixer_elem_free(elem); - mixer->count--; - m = mixer->count - idx; + snd_amixer_elem_free(elem); + amixer->count--; + m = amixer->count - idx; if (m > 0) - memmove(mixer->pelems + idx, - mixer->pelems + idx + 1, - m * sizeof(snd_mixer_elem_t *)); + memmove(amixer->pelems + idx, + amixer->pelems + idx + 1, + m * sizeof(snd_amixer_elem_t *)); return err; } /** - * \brief Free a mixer element + * \brief Free a amixer element * \param elem Mixer element * \return 0 on success otherwise a negative error code - * - * For use by mixer element class specific code. */ -void snd_mixer_elem_free(snd_mixer_elem_t *elem) +void snd_amixer_elem_free(snd_amixer_elem_t *elem) { if (elem->private_free) elem->private_free(elem); @@ -483,214 +630,100 @@ void snd_mixer_elem_free(snd_mixer_elem_t *elem) * \brief Mixer element informations are changed * \param elem Mixer element * \return 0 on success otherwise a negative error code - * - * For use by mixer element class specific code. */ -int snd_mixer_elem_info(snd_mixer_elem_t *elem) +int snd_amixer_elem_info(snd_amixer_elem_t *elem) { - return snd_mixer_elem_throw_event(elem, SND_CTL_EVENT_MASK_INFO); + return snd_amixer_elem_throw_event(elem, SND_CTL_EVENT_MASK_INFO); } /** * \brief Mixer element values is changed * \param elem Mixer element * \return 0 on success otherwise a negative error code - * - * For use by mixer element class specific code. - */ -int snd_mixer_elem_value(snd_mixer_elem_t *elem) -{ - return snd_mixer_elem_throw_event(elem, SND_CTL_EVENT_MASK_VALUE); -} - -/** - * \brief Register mixer element class - * \param class Mixer element class - * \param mixer Mixer handle - * \return 0 on success otherwise a negative error code - * - * For use by mixer element class specific code. - */ -int snd_mixer_class_register(snd_mixer_class_t *class, snd_mixer_t *mixer) -{ - struct list_head *pos; - class->mixer = mixer; - list_add_tail(&class->list, &mixer->classes); - if (!class->event) - return 0; - list_for_each(pos, &mixer->slaves) { - int err; - snd_mixer_slave_t *slave; - snd_hctl_elem_t *elem; - slave = list_entry(pos, snd_mixer_slave_t, list); - elem = snd_hctl_first_elem(slave->hctl); - while (elem) { - err = class->event(class, SND_CTL_EVENT_MASK_ADD, elem, NULL); - if (err < 0) - return err; - elem = snd_hctl_elem_next(elem); - } - } - return 0; -} - -/** - * \brief Unregister mixer element class and remove all its elements - * \param class Mixer element class - * \return 0 on success otherwise a negative error code - * - * Note that the class structure is also deallocated! */ -int snd_mixer_class_unregister(snd_mixer_class_t *class) +int snd_amixer_elem_value(snd_amixer_elem_t *elem) { - unsigned int k; - snd_mixer_elem_t *e; - snd_mixer_t *mixer = class->mixer; - for (k = mixer->count; k > 0; k--) { - e = mixer->pelems[k-1]; - if (e->class == class) - snd_mixer_elem_remove(e); - } - if (class->private_free) - class->private_free(class); - list_del(&class->list); - free(class); - return 0; + return snd_amixer_elem_throw_event(elem, SND_CTL_EVENT_MASK_VALUE); } /** - * \brief Load a mixer elements - * \param mixer Mixer handle + * \brief Close a amixer and free all related resources + * \param amixer Mixer handle * \return 0 on success otherwise a negative error code */ -int snd_mixer_load(snd_mixer_t *mixer) +int snd_amixer_close(snd_amixer_t *amixer) { - struct list_head *pos; - list_for_each(pos, &mixer->slaves) { - int err; - snd_mixer_slave_t *s; - s = list_entry(pos, snd_mixer_slave_t, list); - err = snd_hctl_load(s->hctl); - if (err < 0) - return err; - } - return 0; -} + int res = 0, i; -/** - * \brief Unload all mixer elements and free all related resources - * \param mixer Mixer handle - */ -void snd_mixer_free(snd_mixer_t *mixer) -{ - struct list_head *pos; - list_for_each(pos, &mixer->slaves) { - snd_mixer_slave_t *s; - s = list_entry(pos, snd_mixer_slave_t, list); - snd_hctl_free(s->hctl); - } -} - -/** - * \brief Close a mixer and free all related resources - * \param mixer Mixer handle - * \return 0 on success otherwise a negative error code - */ -int snd_mixer_close(snd_mixer_t *mixer) -{ - int res = 0; - assert(mixer); - while (!list_empty(&mixer->classes)) { - snd_mixer_class_t *c; - c = list_entry(mixer->classes.next, snd_mixer_class_t, list); - snd_mixer_class_unregister(c); + assert(amixer); + for (i = 0; i < SM_CTL_COUNT; i++) { + if (amixer->sm_open.ctl[i]) + res = snd_ctl_close(amixer->sm_open.ctl[i]); + amixer->sm_open.ctl[i] = NULL; } - assert(list_empty(&mixer->elems)); - assert(mixer->count == 0); - free(mixer->pelems); - mixer->pelems = NULL; - while (!list_empty(&mixer->slaves)) { - int err; - snd_mixer_slave_t *s; - s = list_entry(mixer->slaves.next, snd_mixer_slave_t, list); - err = snd_hctl_close(s->hctl); - if (err < 0) - res = err; - list_del(&s->list); - free(s); - } - free(mixer); + assert(list_empty(&amixer->elems)); + if (amixer->pelems) + free(amixer->pelems); + if (amixer->dl_handle) + snd_dlclose(amixer->dl_handle); + free(amixer); return res; } -static int snd_mixer_compare_default(const snd_mixer_elem_t *c1, - const snd_mixer_elem_t *c2) -{ - int d = c1->compare_weight - c2->compare_weight; - if (d) - return d; - assert(c1->class && c1->class->compare); - assert(c2->class && c2->class->compare); - assert(c1->class == c2->class); - return c1->class->compare(c1, c2); -} - -static int mixer_compare(const void *a, const void *b) +static int amixer_compare(const void *a, const void *b) { - snd_mixer_t *mixer; + snd_amixer_t *amixer; - mixer = (*((const snd_mixer_elem_t * const *)a))->class->mixer; - return mixer->compare(*(const snd_mixer_elem_t * const *)a, *(const snd_mixer_elem_t * const *)b); + amixer = (*((const snd_amixer_elem_t * const *)a))->amixer; + return amixer->compare(*(const snd_amixer_elem_t * const *)a, *(const snd_amixer_elem_t * const *)b); } -static int snd_mixer_sort(snd_mixer_t *mixer) +static int snd_amixer_sort(snd_amixer_t *amixer) { unsigned int k; - assert(mixer); - assert(mixer->compare); - INIT_LIST_HEAD(&mixer->elems); - qsort(mixer->pelems, mixer->count, sizeof(snd_mixer_elem_t *), mixer_compare); - for (k = 0; k < mixer->count; k++) - list_add_tail(&mixer->pelems[k]->list, &mixer->elems); + assert(amixer); + assert(amixer->compare); + INIT_LIST_HEAD(&amixer->elems); + qsort(amixer->pelems, amixer->count, sizeof(snd_amixer_elem_t *), amixer_compare); + for (k = 0; k < amixer->count; k++) + list_add_tail(&amixer->pelems[k]->list, &amixer->elems); return 0; } /** - * \brief Change mixer compare function and reorder elements - * \param mixer Mixer handle + * \brief Change amixer compare function and reorder elements + * \param amixer Mixer handle * \param compare Element compare function * \return 0 on success otherwise a negative error code */ -int snd_mixer_set_compare(snd_mixer_t *mixer, snd_mixer_compare_t compare) +int snd_amixer_set_compare(snd_amixer_t *amixer, snd_amixer_compare_t compare) { - snd_mixer_compare_t compare_old; + snd_amixer_compare_t compare_old; int err; - assert(mixer); - compare_old = mixer->compare; - mixer->compare = compare == NULL ? snd_mixer_compare_default : compare; - if ((err = snd_mixer_sort(mixer)) < 0) { - mixer->compare = compare_old; + assert(amixer); + compare_old = amixer->compare; + amixer->compare = compare == NULL ? snd_amixer_compare_default : compare; + if ((err = snd_amixer_sort(amixer)) < 0) { + amixer->compare = compare_old; return err; } return 0; } /** - * \brief get count of poll descriptors for mixer handle - * \param mixer Mixer handle + * \brief get count of poll descriptors for amixer handle + * \param amixer Mixer handle * \return count of poll descriptors */ -int snd_mixer_poll_descriptors_count(snd_mixer_t *mixer) +int snd_amixer_poll_descriptors_count(snd_amixer_t *amixer) { - struct list_head *pos; - unsigned int c = 0; - assert(mixer); - list_for_each(pos, &mixer->slaves) { - snd_mixer_slave_t *s; - int n; - s = list_entry(pos, snd_mixer_slave_t, list); - n = snd_hctl_poll_descriptors_count(s->hctl); + assert(amixer); + int i, c = 0, n; + for (i = 0; i < SM_CTL_COUNT; i++) { + if (amixer->sm_open.ctl[i] == NULL) + continue; + n = snd_ctl_poll_descriptors_count(amixer->sm_open.ctl[i]); if (n < 0) return n; c += n; @@ -700,46 +733,46 @@ int snd_mixer_poll_descriptors_count(snd_mixer_t *mixer) /** * \brief get poll descriptors - * \param mixer Mixer handle + * \param amixer Mixer handle * \param pfds array of poll descriptors * \param space space in the poll descriptor array * \return count of filled descriptors */ -int snd_mixer_poll_descriptors(snd_mixer_t *mixer, struct pollfd *pfds, unsigned int space) +int snd_amixer_poll_descriptors(snd_amixer_t *amixer, struct pollfd *pfds, unsigned int space) { - struct list_head *pos; - unsigned int count = 0; - assert(mixer); - list_for_each(pos, &mixer->slaves) { - snd_mixer_slave_t *s; - int n; - s = list_entry(pos, snd_mixer_slave_t, list); - n = snd_hctl_poll_descriptors(s->hctl, pfds, space); + int i, count = 0, n; + + assert(amixer); + for (i = 0; i < SM_CTL_COUNT; i++) { + if (amixer->sm_open.ctl[i] == NULL) + continue; + n = snd_ctl_poll_descriptors(amixer->sm_open.ctl[i], pfds, space); if (n < 0) return n; - if (space >= (unsigned int) n) { + if (space >= (unsigned int)n) { count += n; space -= n; pfds += n; - } else + } else { space = 0; + } } return count; } /** * \brief get returned events from poll descriptors - * \param mixer Mixer handle + * \param amixer Mixer handle * \param pfds array of poll descriptors * \param nfds count of poll descriptors * \param revents returned events * \return zero if success, otherwise a negative error code */ -int snd_mixer_poll_descriptors_revents(snd_mixer_t *mixer, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) +int snd_amixer_poll_descriptors_revents(snd_amixer_t *amixer, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) { unsigned int idx; unsigned short res; - assert(mixer && pfds && revents); + assert(amixer && pfds && revents); if (nfds == 0) return -EINVAL; res = 0; @@ -750,25 +783,25 @@ int snd_mixer_poll_descriptors_revents(snd_mixer_t *mixer, struct pollfd *pfds, } /** - * \brief Wait for a mixer to become ready (i.e. at least one event pending) - * \param mixer Mixer handle + * \brief Wait for a amixer to become ready (i.e. at least one event pending) + * \param amixer Mixer handle * \param timeout maximum time in milliseconds to wait * \return 0 otherwise a negative error code on failure */ -int snd_mixer_wait(snd_mixer_t *mixer, int timeout) +int snd_amixer_wait(snd_amixer_t *amixer, int timeout) { struct pollfd spfds[16]; struct pollfd *pfds = spfds; int err; int count; - count = snd_mixer_poll_descriptors(mixer, pfds, sizeof(spfds) / sizeof(spfds[0])); + count = snd_amixer_poll_descriptors(amixer, pfds, sizeof(spfds) / sizeof(spfds[0])); if (count < 0) return count; if ((unsigned int) count > sizeof(spfds) / sizeof(spfds[0])) { pfds = malloc(count * sizeof(*pfds)); if (!pfds) return -ENOMEM; - err = snd_mixer_poll_descriptors(mixer, pfds, + err = snd_amixer_poll_descriptors(amixer, pfds, (unsigned int) count); assert(err == count); } @@ -779,305 +812,181 @@ int snd_mixer_wait(snd_mixer_t *mixer, int timeout) } /** - * \brief get first element for a mixer - * \param mixer Mixer handle + * \brief get first element for a amixer + * \param amixer Mixer handle * \return pointer to first element */ -snd_mixer_elem_t *snd_mixer_first_elem(snd_mixer_t *mixer) +snd_amixer_elem_t *snd_amixer_first_elem(snd_amixer_t *amixer) { - assert(mixer); - if (list_empty(&mixer->elems)) + assert(amixer); + if (list_empty(&amixer->elems)) return NULL; - return list_entry(mixer->elems.next, snd_mixer_elem_t, list); + return list_entry(amixer->elems.next, snd_amixer_elem_t, list); } /** - * \brief get last element for a mixer - * \param mixer Mixer handle + * \brief get last element for a amixer + * \param amixer Mixer handle * \return pointer to last element */ -snd_mixer_elem_t *snd_mixer_last_elem(snd_mixer_t *mixer) +snd_amixer_elem_t *snd_amixer_last_elem(snd_amixer_t *amixer) { - assert(mixer); - if (list_empty(&mixer->elems)) + assert(amixer); + if (list_empty(&amixer->elems)) return NULL; - return list_entry(mixer->elems.prev, snd_mixer_elem_t, list); + return list_entry(amixer->elems.prev, snd_amixer_elem_t, list); } /** - * \brief get next mixer element - * \param elem mixer element + * \brief get next amixer element + * \param elem amixer element * \return pointer to next element */ -snd_mixer_elem_t *snd_mixer_elem_next(snd_mixer_elem_t *elem) +snd_amixer_elem_t *snd_amixer_elem_next(snd_amixer_elem_t *elem) { assert(elem); - if (elem->list.next == &elem->class->mixer->elems) + if (elem->list.next == &elem->amixer->elems) return NULL; - return list_entry(elem->list.next, snd_mixer_elem_t, list); + return list_entry(elem->list.next, snd_amixer_elem_t, list); } /** - * \brief get previous mixer element - * \param elem mixer element + * \brief get previous amixer element + * \param elem amixer element * \return pointer to previous element */ -snd_mixer_elem_t *snd_mixer_elem_prev(snd_mixer_elem_t *elem) +snd_amixer_elem_t *snd_amixer_elem_prev(snd_amixer_elem_t *elem) { assert(elem); - if (elem->list.prev == &elem->class->mixer->elems) + if (elem->list.prev == &elem->amixer->elems) return NULL; - return list_entry(elem->list.prev, snd_mixer_elem_t, list); + return list_entry(elem->list.prev, snd_amixer_elem_t, list); } /** - * \brief Handle pending mixer events invoking callbacks - * \param mixer Mixer handle + * \brief Handle pending amixer events invoking callbacks + * \param amixer Mixer handle * \return Number of events that occured on success, otherwise a negative error code on failure */ -int snd_mixer_handle_events(snd_mixer_t *mixer) +int snd_amixer_handle_events(snd_amixer_t *amixer) { - struct list_head *pos; - assert(mixer); - mixer->events = 0; - list_for_each(pos, &mixer->slaves) { - int err; - snd_mixer_slave_t *s; - s = list_entry(pos, snd_mixer_slave_t, list); - err = snd_hctl_handle_events(s->hctl); + int err, i; + + assert(amixer); + amixer->events = 0; + for (i = 0; i < SM_CTL_COUNT; i++) { + if (amixer->sm_open.ctl[i] == NULL) + continue; + err = snd_ctl_handle_events(amixer->sm_open.ctl[i]); if (err < 0) return err; } - return mixer->events; + return amixer->events; } /** - * \brief Set callback function for a mixer - * \param obj mixer handle + * \brief Set event callback function for a amixer + * \param obj amixer handle * \param val callback function + * + * This function is used in the mixer implementation. Use callback + * functions to watch events. */ -void snd_mixer_set_callback(snd_mixer_t *obj, snd_mixer_callback_t val) +void snd_amixer_set_event(snd_amixer_t *obj, snd_amixer_event_t val) { assert(obj); - obj->callback = val; -} - -/** - * \brief Set callback private value for a mixer - * \param mixer mixer handle - * \param val callback private value - */ -void snd_mixer_set_callback_private(snd_mixer_t *mixer, void * val) -{ - assert(mixer); - mixer->callback_private = val; -} - -/** - * \brief Get callback private value for a mixer - * \param mixer mixer handle - * \return callback private value - */ -void * snd_mixer_get_callback_private(const snd_mixer_t *mixer) -{ - assert(mixer); - return mixer->callback_private; -} - -/** - * \brief Get elements count for a mixer - * \param mixer mixer handle - * \return elements count - */ -unsigned int snd_mixer_get_count(const snd_mixer_t *mixer) -{ - assert(mixer); - return mixer->count; + obj->event = val; } /** - * \brief Set callback function for a mixer element - * \param mixer mixer element + * \brief Set callback function for a amixer + * \param obj amixer handle * \param val callback function */ -void snd_mixer_elem_set_callback(snd_mixer_elem_t *mixer, snd_mixer_elem_callback_t val) -{ - assert(mixer); - mixer->callback = val; -} - -/** - * \brief Set callback private value for a mixer element - * \param mixer mixer element - * \param val callback private value - */ -void snd_mixer_elem_set_callback_private(snd_mixer_elem_t *mixer, void * val) -{ - assert(mixer); - mixer->callback_private = val; -} - -/** - * \brief Get callback private value for a mixer element - * \param mixer mixer element - * \return callback private value - */ -void * snd_mixer_elem_get_callback_private(const snd_mixer_elem_t *mixer) -{ - assert(mixer); - return mixer->callback_private; -} - -/** - * \brief Get type for a mixer element - * \param mixer mixer element - * \return mixer element type - */ -snd_mixer_elem_type_t snd_mixer_elem_get_type(const snd_mixer_elem_t *mixer) -{ - assert(mixer); - return mixer->type; -} - - -/** - * \brief get size of #snd_mixer_class_t - * \return size in bytes - */ -size_t snd_mixer_class_sizeof() -{ - return sizeof(snd_mixer_class_t); -} - -/** - * \brief allocate an invalid #snd_mixer_class_t using standard malloc - * \param ptr returned pointer - * \return 0 on success otherwise negative error code - */ -int snd_mixer_class_malloc(snd_mixer_class_t **ptr) -{ - assert(ptr); - *ptr = calloc(1, sizeof(snd_mixer_class_t)); - if (!*ptr) - return -ENOMEM; - return 0; -} - -/** - * \brief frees a previously allocated #snd_mixer_class_t - * \param obj pointer to object to free - */ -void snd_mixer_class_free(snd_mixer_class_t *obj) -{ - if (obj->private_free) - obj->private_free(obj); - free(obj); -} - -/** - * \brief copy one #snd_mixer_class_t to another - * \param dst pointer to destination - * \param src pointer to source - */ -void snd_mixer_class_copy(snd_mixer_class_t *dst, const snd_mixer_class_t *src) -{ - assert(dst && src); - *dst = *src; -} - -/** - * \brief Get a mixer associated to given mixer class - * \param obj Mixer simple class identifier - * \return mixer pointer - */ -snd_mixer_t *snd_mixer_class_get_mixer(const snd_mixer_class_t *obj) +void snd_amixer_set_callback(snd_amixer_t *obj, snd_amixer_callback_t val) { assert(obj); - return obj->mixer; + obj->callback = val; } /** - * \brief Get mixer event callback associated to given mixer class - * \param obj Mixer simple class identifier - * \return event callback pointer + * \brief Set callback private value for a amixer + * \param obj amixer handle + * \param val callback private value */ -snd_mixer_event_t snd_mixer_class_get_event(const snd_mixer_class_t *obj) +void snd_amixer_set_callback_private(snd_amixer_t *obj, void * val) { assert(obj); - return obj->event; + obj->callback_private = val; } /** - * \brief Get mixer private data associated to given mixer class - * \param obj Mixer simple class identifier - * \return event callback pointer + * \brief Get callback private value for a amixer + * \param amixer amixer handle + * \return callback private value */ -void *snd_mixer_class_get_private(const snd_mixer_class_t *obj) +void * snd_amixer_get_callback_private(const snd_amixer_t *amixer) { - assert(obj); - return obj->private_data; + assert(amixer); + return amixer->callback_private; } - /** - * \brief Get mixer compare callback associated to given mixer class - * \param obj Mixer simple class identifier - * \return event callback pointer + * \brief Get elements count for a amixer + * \param amixer amixer handle + * \return elements count */ -snd_mixer_compare_t snd_mixer_class_get_compare(const snd_mixer_class_t *obj) +unsigned int snd_amixer_get_count(const snd_amixer_t *amixer) { - assert(obj); - return obj->compare; + assert(amixer); + return amixer->count; } /** - * \brief Set mixer event callback to given mixer class - * \param obj Mixer simple class identifier - * \param event Event callback - * \return zero if success, otherwise a negative error code + * \brief Set callback function for a amixer element + * \param amixer amixer element + * \param val callback function */ -int snd_mixer_class_set_event(snd_mixer_class_t *obj, snd_mixer_event_t event) +void snd_amixer_elem_set_callback(snd_amixer_elem_t *amixer, snd_amixer_elem_callback_t val) { - assert(obj); - obj->event = event; - return 0; + assert(amixer); + amixer->callback = val; } /** - * \brief Set mixer private data to given mixer class - * \param obj Mixer simple class identifier - * \param private_data class private data - * \return zero if success, otherwise a negative error code + * \brief Set callback private value for a amixer element + * \param amixer amixer element + * \param val callback private value */ -int snd_mixer_class_set_private(snd_mixer_class_t *obj, void *private_data) +void snd_amixer_elem_set_callback_private(snd_amixer_elem_t *amixer, void * val) { - assert(obj); - obj->private_data = private_data; - return 0; + assert(amixer); + amixer->callback_private = val; } /** - * \brief Set mixer private data free callback to given mixer class - * \param obj Mixer simple class identifier - * \param private_free Mixer class private data free callback - * \return zero if success, otherwise a negative error code + * \brief Get callback private value for a amixer element + * \param amixer amixer element + * \return callback private value */ -int snd_mixer_class_set_private_free(snd_mixer_class_t *obj, void (*private_free)(snd_mixer_class_t *class)) +void * snd_amixer_elem_get_callback_private(const snd_amixer_elem_t *amixer) { - assert(obj); - obj->private_free = private_free; - return 0; + assert(amixer); + return amixer->callback_private; } /** - * \brief Set mixer compare callback to given mixer class - * \param obj Mixer simple class identifier - * \param compare the compare callback to be used - * \return zero if success, otherwise a negative error code + * \brief Check if ID is generic + * \param id string + * \return 1 if success, otherwise 0 */ -int snd_mixer_class_set_compare(snd_mixer_class_t *obj, snd_mixer_compare_t compare) +int snd_amixer_conf_generic_id(const char *id) { - assert(obj); - obj->compare = compare; + static const char ids[3][8] = { "comment", "type", "hint" }; + unsigned int k; + for (k = 0; k < sizeof(ids) / sizeof(ids[0]); ++k) { + if (strcmp(id, ids[k]) == 0) + return 1; + } return 0; } |