From: Jaroslav Kysela Date: Fri, 3 Jun 2005 13:33:04 +0000 (+0000) Subject: big simple mixer update X-Git-Tag: v1.0.10rc1~40 X-Git-Url: https://git.alsa-project.org/?a=commitdiff_plain;h=597b4d0942f0618a3b5747f4b623474f57d13b82;p=alsa-lib.git big simple mixer update - exported all necessary functions to create a mixer module outside alsa-lib - separated simple mixer API from the simple mixer implementation (using callbacks as usuall) - src/mixer/simple.c is the core - src/mixer/simple_none.c is the current (no-abstraction) implementation based on control names; note that this module does not depend on internal ALSA structures now - src/mixer/simple_abst.c is the ongoing abstraction which will use external dynamic modules; src/conf/smixer.conf will describe which modules will be used depending on the components from the driver --- diff --git a/include/Makefile.am b/include/Makefile.am index b0ebc8c1..29b041c8 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -6,7 +6,7 @@ alsaincludedir = ${includedir}/alsa alsainclude_HEADERS = asoundlib.h asoundef.h \ version.h global.h input.h output.h error.h \ conf.h pcm.h pcm_old.h pcm_plugin.h rawmidi.h timer.h \ - hwdep.h control.h mixer.h \ + hwdep.h control.h mixer.h mixer_abst.h \ seq_event.h seq.h seqmid.h seq_midi_event.h \ conv.h instr.h iatomic.h \ alisp.h pcm_external.h pcm_ioplug.h pcm_extplug.h diff --git a/include/mixer.h b/include/mixer.h index 0f0db30b..ac2db33d 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -74,6 +74,18 @@ typedef int (*snd_mixer_elem_callback_t)(snd_mixer_elem_t *elem, typedef int (*snd_mixer_compare_t)(const snd_mixer_elem_t *e1, const snd_mixer_elem_t *e2); +/** + * \brief Event callback for the mixer class + * \param class Mixer class + * \param mask Event mask (SND_CTL_EVENT_*) + * \param helem HCTL element which invoked the event + * \param melem Mixer element associated to HCTL element + * \return zero if success, otherwise a negative error value + */ +typedef int (*snd_mixer_event_t)(snd_mixer_class_t *class, unsigned int mask, + snd_hctl_elem_t *helem, snd_mixer_elem_t *melem); + + /** Mixer element type */ typedef enum _snd_mixer_elem_type { /* Simple (legacy) mixer elements */ @@ -88,6 +100,7 @@ snd_mixer_elem_t *snd_mixer_last_elem(snd_mixer_t *mixer); int snd_mixer_handle_events(snd_mixer_t *mixer); int snd_mixer_attach(snd_mixer_t *mixer, const char *name); int snd_mixer_detach(snd_mixer_t *mixer, const char *name); +int snd_mixer_get_hctl(snd_mixer_t *mixer, const char *name, snd_hctl_t **hctl); int snd_mixer_poll_descriptors_count(snd_mixer_t *mixer); int snd_mixer_poll_descriptors(snd_mixer_t *mixer, struct pollfd *pfds, unsigned int space); int snd_mixer_poll_descriptors_revents(snd_mixer_t *mixer, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); @@ -108,6 +121,41 @@ void * snd_mixer_elem_get_callback_private(const snd_mixer_elem_t *obj); void snd_mixer_elem_set_callback_private(snd_mixer_elem_t *obj, void * val); snd_mixer_elem_type_t snd_mixer_elem_get_type(const snd_mixer_elem_t *obj); +int snd_mixer_class_register(snd_mixer_class_t *class, snd_mixer_t *mixer); +int snd_mixer_add_elem(snd_mixer_t *mixer, snd_mixer_elem_t *elem); +int snd_mixer_remove_elem(snd_mixer_t *mixer, snd_mixer_elem_t *elem); +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_mixer_elem_add(snd_mixer_elem_t *elem, snd_mixer_class_t *class); +int snd_mixer_elem_remove(snd_mixer_elem_t *elem); +void snd_mixer_elem_free(snd_mixer_elem_t *elem); +int snd_mixer_elem_info(snd_mixer_elem_t *elem); +int snd_mixer_elem_value(snd_mixer_elem_t *elem); +int snd_mixer_elem_attach(snd_mixer_elem_t *melem, snd_hctl_elem_t *helem); +int snd_mixer_elem_detach(snd_mixer_elem_t *melem, snd_hctl_elem_t *helem); +int snd_mixer_elem_empty(snd_mixer_elem_t *melem); +void *snd_mixer_elem_get_private(const snd_mixer_elem_t *melem); + +size_t snd_mixer_class_sizeof(void); +/** \hideinitializer + * \brief allocate an invalid #snd_mixer_class_t using standard alloca + * \param ptr returned pointer + */ +#define snd_mixer_class_alloca(ptr) do { assert(ptr); *ptr = (snd_mixer_selem_id_t *) alloca(snd_mixer_class_sizeof()); memset(*ptr, 0, snd_mixer_class_sizeof()); } while (0) +int snd_mixer_class_malloc(snd_mixer_class_t **ptr); +void snd_mixer_class_free(snd_mixer_class_t *obj); +void snd_mixer_class_copy(snd_mixer_class_t *dst, const snd_mixer_class_t *src); +snd_mixer_t *snd_mixer_class_get_mixer(const snd_mixer_class_t *class); +snd_mixer_event_t snd_mixer_class_get_event(const snd_mixer_class_t *class); +void *snd_mixer_class_get_private(const snd_mixer_class_t *class); +snd_mixer_compare_t snd_mixer_class_get_compare(const snd_mixer_class_t *class); +int snd_mixer_class_set_event(snd_mixer_class_t *class, snd_mixer_event_t event); +int snd_mixer_class_set_private(snd_mixer_class_t *class, void *private_data); +int snd_mixer_class_set_private_free(snd_mixer_class_t *class, void (*private_free)(snd_mixer_class_t *class)); +int snd_mixer_class_set_compare(snd_mixer_class_t *class, snd_mixer_compare_t compare); /** * \defgroup SimpleMixer Simple Mixer Interface @@ -126,14 +174,20 @@ typedef enum _snd_mixer_selem_channel_id { SND_MIXER_SCHN_FRONT_LEFT = 0, /** Front right */ SND_MIXER_SCHN_FRONT_RIGHT, - /** Front center */ - SND_MIXER_SCHN_FRONT_CENTER, /** Rear left */ SND_MIXER_SCHN_REAR_LEFT, /** Rear right */ SND_MIXER_SCHN_REAR_RIGHT, + /** Front center */ + SND_MIXER_SCHN_FRONT_CENTER, /** Woofer */ SND_MIXER_SCHN_WOOFER, + /** Side Left */ + SND_MIXER_SCHN_SIDE_LEFT, + /** Side Right */ + SND_MIXER_SCHN_SIDE_RIGHT, + /** Rear Center */ + SND_MIXER_SCHN_REAR_CENTER, SND_MIXER_SCHN_LAST = 31, /** Mono (Front left alias) */ SND_MIXER_SCHN_MONO = SND_MIXER_SCHN_FRONT_LEFT @@ -153,9 +207,11 @@ struct snd_mixer_selem_regopt { int ver; /** v1: abstract layer selection */ enum snd_mixer_selem_regopt_abstract abstract; - /** v1: playback PCM connected to mixer device */ + /** v1: device name (must be NULL when playback_pcm or capture_pcm != NULL) */ + const char *device; + /** v1: playback PCM connected to mixer device (NULL == none) */ snd_pcm_t *playback_pcm; - /** v1: playback PCM connected to mixer device */ + /** v1: capture PCM connected to mixer device (NULL == none) */ snd_pcm_t *capture_pcm; }; @@ -204,8 +260,8 @@ int snd_mixer_selem_set_playback_dB(snd_mixer_elem_t *elem, snd_mixer_selem_chan int snd_mixer_selem_set_capture_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value, int dir); int snd_mixer_selem_set_playback_volume_all(snd_mixer_elem_t *elem, long value); int snd_mixer_selem_set_capture_volume_all(snd_mixer_elem_t *elem, long value); -int snd_mixer_selem_set_playback_volume_dB(snd_mixer_elem_t *elem, long value, int dir); -int snd_mixer_selem_set_capture_volume_dB(snd_mixer_elem_t *elem, long value, int dir); +int snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *elem, long value, int dir); +int snd_mixer_selem_set_capture_dB_all(snd_mixer_elem_t *elem, long value, int dir); int snd_mixer_selem_set_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int value); int snd_mixer_selem_set_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int value); int snd_mixer_selem_set_playback_switch_all(snd_mixer_elem_t *elem, int value); diff --git a/include/mixer_abst.h b/include/mixer_abst.h new file mode 100644 index 00000000..90acdb6a --- /dev/null +++ b/include/mixer_abst.h @@ -0,0 +1,100 @@ +/** + * \file include/mixer_abst.h + * \brief Mixer abstract implementation interface library for the ALSA library + * \author Jaroslav Kysela + * \date 2005 + * + * Mixer abstact implementation interface library for the ALSA library + */ +/* + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __ALSA_MIXER_ABST_H +#define __ALSA_MIXER_ABST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup Mixer_Abstract Mixer Abstact Module Interface + * The mixer abstact module interface. + * \{ + */ + +#define SM_PLAY 0 +#define SM_CAPT 1 + +#define SM_CAP_GVOLUME (1<<1) +#define SM_CAP_GSWITCH (1<<2) +#define SM_CAP_PVOLUME (1<<3) +#define SM_CAP_PVOLUME_JOIN (1<<4) +#define SM_CAP_PSWITCH (1<<5) +#define SM_CAP_PSWITCH_JOIN (1<<6) +#define SM_CAP_CVOLUME (1<<7) +#define SM_CAP_CVOLUME_JOIN (1<<8) +#define SM_CAP_CSWITCH (1<<9) +#define SM_CAP_CSWITCH_JOIN (1<<10) +#define SM_CAP_CSWITCH_EXCL (1<<11) +#define SM_CAP_ENUM (1<<12) +/* SM_CAP_* 24-31 => private for module use */ + +#define SM_OPS_IS_ACTIVE 0 +#define SM_OPS_IS_MONO 1 +#define SM_OPS_IS_CHANNEL 2 +#define SM_OPS_IS_ENUMERATED 3 +#define SM_OPS_IS_ENUMCNT 4 + +#define sm_selem(x) ((sm_selem_t *)((x)->private_data)) +#define sm_selem_ops(x) ((sm_selem_t *)((x)->private_data))->ops + +typedef struct _sm_selem { + snd_mixer_selem_id_t *id; + struct sm_elem_ops *ops; + unsigned int caps; + unsigned int capture_group; +} sm_selem_t; + +struct sm_elem_ops { + int (*is)(snd_mixer_elem_t *elem, int dir, int cmd, int val); + int (*get_range)(snd_mixer_elem_t *elem, int dir, long *min, long *max); + int (*set_range)(snd_mixer_elem_t *elem, int dir, long min, long max); + int (*get_dB_range)(snd_mixer_elem_t *elem, int dir, long *min, long *max); + int (*get_volume)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long *value); + int (*get_dB)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long *value); + int (*set_volume)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value); + int (*set_dB)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value, int xdir); + int (*set_volume_all)(snd_mixer_elem_t *elem, int dir, long value); + int (*set_dB_all)(snd_mixer_elem_t *elem, int dir, long value, int xdir); + int (*get_switch)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int *value); + int (*set_switch)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int value); + int (*set_switch_all)(snd_mixer_elem_t *elem, int dir, int value); + int (*enum_item_name)(snd_mixer_elem_t *elem, unsigned int item, size_t maxlen, char *buf); + int (*get_enum_item)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, unsigned int *itemp); + int (*set_enum_item)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, unsigned int item); +}; + +int snd_mixer_selem_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2); + +/** \} */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ALSA_MIXER_ABST_H */ + diff --git a/src/Versions b/src/Versions index 37aecaf9..33bc6c82 100644 --- a/src/Versions +++ b/src/Versions @@ -218,3 +218,39 @@ ALSA_1.0.9 { snd_timer_ginfo_get_clients; } ALSA_1.0.8; + +ALSA_1.0.10 { + global: + + snd_mixer_get_hctl; + snd_mixer_elem_get_private; + + snd_mixer_class_register; + snd_mixer_add_elem; + snd_mixer_remove_elem; + snd_mixer_elem_new; + snd_mixer_elem_add; + snd_mixer_elem_remove; + snd_mixer_elem_free; + snd_mixer_elem_info; + snd_mixer_elem_value; + snd_mixer_elem_attach; + snd_mixer_elem_detach; + snd_mixer_elem_empty; + + snd_mixer_class_malloc; + snd_mixer_class_free; + snd_mixer_class_copy; + snd_mixer_class_get_mixer; + snd_mixer_class_get_event; + snd_mixer_class_get_private; + snd_mixer_class_get_compare; + snd_mixer_class_set_event; + snd_mixer_class_set_private; + snd_mixer_class_set_private_free; + snd_mixer_class_set_compare; + + snd_mixer_selem_set_playback_dB_all; + snd_mixer_selem_set_capture_dB_all; + +} ALSA_1.0.9; diff --git a/src/conf/smixer.conf b/src/conf/smixer.conf new file mode 100644 index 00000000..908de4a4 --- /dev/null +++ b/src/conf/smixer.conf @@ -0,0 +1,8 @@ +usb { + searchl "USB" + lib smixer_usb.so +} +ac97 { + searchl "AC97a:" + lib smixer_ac97.so +} diff --git a/src/mixer/Makefile.am b/src/mixer/Makefile.am index 115c4077..10c03850 100644 --- a/src/mixer/Makefile.am +++ b/src/mixer/Makefile.am @@ -1,6 +1,6 @@ EXTRA_LTLIBRARIES=libmixer.la -libmixer_la_SOURCES = bag.c mixer.c simple.c +libmixer_la_SOURCES = bag.c mixer.c simple.c simple_none.c simple_abst.c noinst_HEADERS = mixer_local.h diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c index ba7c68c7..2a06c757 100644 --- a/src/mixer/mixer.c +++ b/src/mixer/mixer.c @@ -45,7 +45,6 @@ This is an abstraction layer over the hcontrol layer. #include #include #include -#include #include "mixer_local.h" #ifndef DOC_HIDDEN @@ -244,6 +243,27 @@ int snd_mixer_detach(snd_mixer_t *mixer, const char *name) 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) { @@ -284,6 +304,47 @@ static int _snd_mixer_find_elem(snd_mixer_t *mixer, snd_mixer_elem_t *elem, int return idx; } +/** + * \brief Get private data associated to give mixer 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) +{ + return elem->private_data; +} + +/** + * \brief Allocate a new mixer element + * \param elem Returned mixer element + * \param type Mixer element type + * \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)) +{ + snd_mixer_elem_t *melem = calloc(1, sizeof(*melem)); + if (melem == NULL) + return -ENOMEM; + melem->type = type; + melem->compare_weight = compare_weight; + melem->private_data = private_data; + melem->private_free = private_free; + INIT_LIST_HEAD(&melem->helems); + *elem = melem; + return 0; +} + /** * \brief Add an element for a registered mixer element class * \param elem Mixer element @@ -353,9 +414,7 @@ int snd_mixer_elem_remove(snd_mixer_elem_t *elem) } err = snd_mixer_elem_throw_event(elem, SND_CTL_EVENT_MASK_REMOVE); list_del(&elem->list); - if (elem->private_free) - elem->private_free(elem); - free(elem); + snd_mixer_elem_free(elem); mixer->count--; m = mixer->count - idx; if (m > 0) @@ -365,6 +424,20 @@ int snd_mixer_elem_remove(snd_mixer_elem_t *elem) return err; } +/** + * \brief Free a mixer 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) +{ + if (elem->private_free) + elem->private_free(elem); + free(elem); +} + /** * \brief Mixer element informations are changed * \param elem Mixer element @@ -521,26 +594,22 @@ static int snd_mixer_compare_default(const snd_mixer_elem_t *c1, return c1->class->compare(c1, c2); } -static snd_mixer_t *compare_mixer; -static int mixer_compare(const void *a, const void *b) { - return compare_mixer->compare(*(const snd_mixer_elem_t * const *) a, - *(const snd_mixer_elem_t * const *) b); +static int mixer_compare(const void *a, const void *b) +{ + snd_mixer_t *mixer; + + 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); } +typedef int (*qsort_func)(const void *, const void *); static int snd_mixer_sort(snd_mixer_t *mixer) { unsigned int k; - static pthread_mutex_t sync_lock = PTHREAD_MUTEX_INITIALIZER; - assert(mixer); assert(mixer->compare); INIT_LIST_HEAD(&mixer->elems); - - pthread_mutex_lock(&sync_lock); - compare_mixer = mixer; - qsort(mixer->pelems, mixer->count, sizeof(snd_mixer_elem_t*), mixer_compare); - pthread_mutex_unlock(&sync_lock); - + 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); return 0; @@ -744,8 +813,8 @@ int snd_mixer_handle_events(snd_mixer_t *mixer) /** * \brief Set callback function for a mixer - * \param obj mixer handle - * \param val callback function + * \param mixer mixer handle + * \param callback callback function */ void snd_mixer_set_callback(snd_mixer_t *obj, snd_mixer_callback_t val) { @@ -755,78 +824,217 @@ void snd_mixer_set_callback(snd_mixer_t *obj, snd_mixer_callback_t val) /** * \brief Set callback private value for a mixer - * \param obj mixer handle - * \param val callback private value + * \param mixer mixer handle + * \param callback_private callback private value */ -void snd_mixer_set_callback_private(snd_mixer_t *obj, void * val) +void snd_mixer_set_callback_private(snd_mixer_t *mixer, void * val) { - assert(obj); - obj->callback_private = val; + assert(mixer); + mixer->callback_private = val; } /** * \brief Get callback private value for a mixer - * \param obj mixer handle + * \param mixer mixer handle * \return callback private value */ -void * snd_mixer_get_callback_private(const snd_mixer_t *obj) +void * snd_mixer_get_callback_private(const snd_mixer_t *mixer) { - assert(obj); - return obj->callback_private; + assert(mixer); + return mixer->callback_private; } /** * \brief Get elements count for a mixer - * \param obj mixer handle + * \param mixer mixer handle * \return elements count */ -unsigned int snd_mixer_get_count(const snd_mixer_t *obj) +unsigned int snd_mixer_get_count(const snd_mixer_t *mixer) { - assert(obj); - return obj->count; + assert(mixer); + return mixer->count; } /** * \brief Set callback function for a mixer element - * \param obj mixer element + * \param mixer mixer element * \param val callback function */ -void snd_mixer_elem_set_callback(snd_mixer_elem_t *obj, snd_mixer_elem_callback_t val) +void snd_mixer_elem_set_callback(snd_mixer_elem_t *mixer, snd_mixer_elem_callback_t val) { - assert(obj); - obj->callback = val; + assert(mixer); + mixer->callback = val; } /** * \brief Set callback private value for a mixer element - * \param obj mixer element + * \param mixer mixer element * \param val callback private value */ -void snd_mixer_elem_set_callback_private(snd_mixer_elem_t *obj, void * val) +void snd_mixer_elem_set_callback_private(snd_mixer_elem_t *mixer, void * val) { - assert(obj); - obj->callback_private = val; + assert(mixer); + mixer->callback_private = val; } /** * \brief Get callback private value for a mixer element - * \param obj mixer element + * \param mixer mixer element * \return callback private value */ -void * snd_mixer_elem_get_callback_private(const snd_mixer_elem_t *obj) +void * snd_mixer_elem_get_callback_private(const snd_mixer_elem_t *mixer) { - assert(obj); - return obj->callback_private; + assert(mixer); + return mixer->callback_private; } /** * \brief Get type for a mixer element - * \param obj 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 *obj) +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 pointer to object to free + */ +void snd_mixer_class_free(snd_mixer_class_t *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) { assert(obj); - return obj->type; + return obj->mixer; } +/** + * \brief Get mixer event callback associated to given mixer class + * \param obj Mixer simple class identifier + * \return event callback pointer + */ +snd_mixer_event_t snd_mixer_class_get_event(const snd_mixer_class_t *obj) +{ + assert(obj); + return obj->event; +} + +/** + * \brief Get mixer private data associated to given mixer class + * \param obj Mixer simple class identifier + * \return event callback pointer + */ +void *snd_mixer_class_get_private(const snd_mixer_class_t *obj) +{ + assert(obj); + return obj->private_data; +} + + +/** + * \brief Get mixer compare callback associated to given mixer class + * \param obj Mixer simple class identifier + * \return event callback pointer + */ +snd_mixer_compare_t snd_mixer_class_get_compare(const snd_mixer_class_t *obj) +{ + assert(obj); + return obj->compare; +} + +/** + * \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 + */ +int snd_mixer_class_set_event(snd_mixer_class_t *obj, snd_mixer_event_t event) +{ + assert(obj); + obj->event = event; + return 0; +} + +/** + * \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 + */ +int snd_mixer_class_set_private(snd_mixer_class_t *obj, void *private_data) +{ + assert(obj); + obj->private_data = private_data; + return 0; +} + +/** + * \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 + */ +int snd_mixer_class_set_private_free(snd_mixer_class_t *obj, void (*private_free)(snd_mixer_class_t *class)) +{ + assert(obj); + obj->private_free = private_free; + return 0; +} + +/** + * \brief Set mixer compare callback to given mixer class + * \param obj Mixer simple class identifier + * \return zero if success, otherwise a negative error code + */ +int snd_mixer_class_set_compare(snd_mixer_class_t *obj, snd_mixer_compare_t compare) +{ + assert(obj); + obj->compare = compare; + return 0; +} diff --git a/src/mixer/mixer_local.h b/src/mixer/mixer_local.h index 24c1a48a..07460938 100644 --- a/src/mixer/mixer_local.h +++ b/src/mixer/mixer_local.h @@ -42,15 +42,10 @@ typedef struct list_head *bag_iterator_t; #define bag_for_each(pos, bag) list_for_each(pos, bag) #define bag_for_each_safe(pos, next, bag) list_for_each_safe(pos, next, bag) -#define MIXER_COMPARE_WEIGHT_SIMPLE_BASE 0 -#define MIXER_COMPARE_WEIGHT_NEXT_BASE 10000000 -#define MIXER_COMPARE_WEIGHT_NOT_FOUND 1000000000 - struct _snd_mixer_class { struct list_head list; snd_mixer_t *mixer; - int (*event)(snd_mixer_class_t *class, unsigned int mask, - snd_hctl_elem_t *helem, snd_mixer_elem_t *melem); + snd_mixer_event_t event; void *private_data; void (*private_free)(snd_mixer_class_t *class); snd_mixer_compare_t compare; @@ -85,16 +80,3 @@ struct _snd_mixer_selem_id { unsigned char name[60]; unsigned int index; }; - -int snd_mixer_class_register(snd_mixer_class_t *class, snd_mixer_t *mixer); -int snd_mixer_add_elem(snd_mixer_t *mixer, snd_mixer_elem_t *elem); -int snd_mixer_remove_elem(snd_mixer_t *mixer, snd_mixer_elem_t *elem); -int snd_mixer_elem_add(snd_mixer_elem_t *elem, snd_mixer_class_t *class); -int snd_mixer_elem_remove(snd_mixer_elem_t *elem); -int snd_mixer_elem_info(snd_mixer_elem_t *elem); -int snd_mixer_elem_value(snd_mixer_elem_t *elem); -int snd_mixer_elem_attach(snd_mixer_elem_t *melem, - snd_hctl_elem_t *helem); -int snd_mixer_elem_detach(snd_mixer_elem_t *melem, - snd_hctl_elem_t *helem); -int snd_mixer_elem_empty(snd_mixer_elem_t *melem); diff --git a/src/mixer/mixer_simple.h b/src/mixer/mixer_simple.h new file mode 100644 index 00000000..9584bcfd --- /dev/null +++ b/src/mixer/mixer_simple.h @@ -0,0 +1,25 @@ +/* + * Mixer Simple Interface - local header file + * Copyright (c) 2005 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "mixer_abst.h" + +int snd_mixer_simple_none_register(snd_mixer_t *mixer, struct snd_mixer_selem_regopt *options, snd_mixer_class_t **classp); +int snd_mixer_simple_basic_register(snd_mixer_t *mixer, struct snd_mixer_selem_regopt *options, snd_mixer_class_t **classp); diff --git a/src/mixer/simple.c b/src/mixer/simple.c index 8b45ae8a..5041a48b 100644 --- a/src/mixer/simple.c +++ b/src/mixer/simple.c @@ -3,13 +3,13 @@ * \brief Mixer Simple Element Class Interface * \author Jaroslav Kysela * \author Abramo Bagnara - * \date 2001 + * \date 2001-2004 * * Mixer simple element class interface. */ /* * Mixer Interface - simple controls - * Copyright (c) 2000 by Jaroslav Kysela + * Copyright (c) 2000,2004 by Jaroslav Kysela * Copyright (c) 2001 by Abramo Bagnara * * @@ -37,1100 +37,51 @@ #include #include #include "mixer_local.h" - -#ifndef DOC_HIDDEN - -#define CAP_GVOLUME (1<<1) -#define CAP_GSWITCH (1<<2) -#define CAP_PVOLUME (1<<3) -#define CAP_PVOLUME_JOIN (1<<4) -#define CAP_PSWITCH (1<<5) -#define CAP_PSWITCH_JOIN (1<<6) -#define CAP_CVOLUME (1<<7) -#define CAP_CVOLUME_JOIN (1<<8) -#define CAP_CSWITCH (1<<9) -#define CAP_CSWITCH_JOIN (1<<10) -#define CAP_CSWITCH_EXCL (1<<11) -#define CAP_ENUM (1<<12) - -typedef struct _mixer_simple mixer_simple_t; - -#define PLAY 0 -#define CAPT 1 - -typedef enum _selem_ctl_type { - CTL_SINGLE, - CTL_ENUMLIST, - CTL_GLOBAL_SWITCH, - CTL_GLOBAL_VOLUME, - CTL_GLOBAL_ROUTE, - CTL_PLAYBACK_SWITCH, - CTL_PLAYBACK_VOLUME, - CTL_PLAYBACK_ROUTE, - CTL_CAPTURE_SWITCH, - CTL_CAPTURE_VOLUME, - CTL_CAPTURE_ROUTE, - CTL_CAPTURE_SOURCE, - CTL_LAST = CTL_CAPTURE_SOURCE, -} selem_ctl_type_t; - -typedef struct _selem_ctl { - snd_hctl_elem_t *elem; - snd_ctl_elem_type_t type; - unsigned int access; - unsigned int values; - long min, max; -} selem_ctl_t; - -typedef struct _selem { - snd_mixer_selem_id_t id; - selem_ctl_t ctls[CTL_LAST + 1]; - unsigned int capture_item; - unsigned int capture_group; - unsigned int caps; - struct { - unsigned int range: 1; /* Forced range */ - long min, max; - unsigned int channels; - long vol[32]; - unsigned int sw; - } str[2]; -} selem_t; - -static struct mixer_name_table { - const char *longname; - const char *shortname; -} name_table[] = { - {"Tone Control - Switch", "Tone"}, - {"Tone Control - Bass", "Bass"}, - {"Tone Control - Treble", "Treble"}, - {"Synth Tone Control - Switch", "Synth Tone"}, - {"Synth Tone Control - Bass", "Synth Bass"}, - {"Synth Tone Control - Treble", "Synth Treble"}, - {0, 0}, -}; - -#endif /* !DOC_HIDDEN */ - -static const char *get_short_name(const char *lname) -{ - struct mixer_name_table *p; - for (p = name_table; p->longname; p++) { - if (!strcmp(lname, p->longname)) - return p->shortname; - } - return lname; -} - -static int compare_mixer_priority_lookup(const char **name, const char * const *names, int coef) -{ - int res; - - for (res = 0; *names; names++, res += coef) { - if (!strncmp(*name, *names, strlen(*names))) { - *name += strlen(*names); - if (**name == ' ') - (*name)++; - return res+1; - } - } - return MIXER_COMPARE_WEIGHT_NOT_FOUND; -} - -static int get_compare_weight(const char *name, unsigned int idx) -{ - static const char *names[] = { - "Master", - "Headphone", - "Tone", - "Bass", - "Treble", - "3D Control", - "PCM", - "Front", - "Surround", - "Center", - "LFE", - "Side", - "Synth", - "FM", - "Wave", - "Music", - "DSP", - "Line", - "CD", - "Mic", - "Video", - "Zoom Video", - "Phone", - "I2S", - "IEC958", - "PC Speaker", - "Aux", - "Mono", - "Playback", - "Capture", - "Mix", - NULL - }; - static const char *names1[] = { - "-", - NULL, - }; - static const char *names2[] = { - "Mono", - "Digital", - "Switch", - "Depth", - "Wide", - "Space", - "Level", - "Center", - "Output", - "Boost", - "Tone", - "Bass", - "Treble", - NULL, - }; - const char *name1; - int res, res1; - - if ((res = compare_mixer_priority_lookup((const char **)&name, names, 1000)) == MIXER_COMPARE_WEIGHT_NOT_FOUND) - return MIXER_COMPARE_WEIGHT_NOT_FOUND; - if (*name == '\0') - goto __res; - for (name1 = name; *name1 != '\0'; name1++); - for (name1--; name1 != name && *name1 != ' '; name1--); - while (name1 != name && *name1 == ' ') - name1--; - if (name1 != name) { - for (; name1 != name && *name1 != ' '; name1--); - name = name1; - if ((res1 = compare_mixer_priority_lookup((const char **)&name, names1, 200)) == MIXER_COMPARE_WEIGHT_NOT_FOUND) - return res; - res += res1; - } else { - name = name1; - } - if ((res1 = compare_mixer_priority_lookup((const char **)&name, names2, 20)) == MIXER_COMPARE_WEIGHT_NOT_FOUND) - return res; - __res: - return MIXER_COMPARE_WEIGHT_SIMPLE_BASE + res + idx; -} - -static long to_user(selem_t *s, int dir, selem_ctl_t *c, long value) -{ - if (c->max == c->min) { - return s->str[dir].min; - } else { - int64_t n = (int64_t) (value - c->min) * (s->str[dir].max - s->str[dir].min); - return s->str[dir].min + (n + (c->max - c->min) / 2) / (c->max - c->min); - } -} - -static long from_user(selem_t *s, int dir, selem_ctl_t *c, long value) -{ - if (s->str[dir].max == s->str[dir].min) { - return c->min; - } else { - int64_t n = (int64_t) (value - s->str[dir].min) * (c->max - c->min); - return c->min + (n + (s->str[dir].max - s->str[dir].min) / 2) / (s->str[dir].max - s->str[dir].min); - } -} - -static int elem_read_volume(selem_t *s, int dir, selem_ctl_type_t type) -{ - snd_ctl_elem_value_t ctl; - unsigned int idx; - int err; - selem_ctl_t *c = &s->ctls[type]; - memset(&ctl, 0, sizeof(ctl)); - if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0) - return err; - for (idx = 0; idx < s->str[dir].channels; idx++) { - unsigned int idx1 = idx; - if (idx >= c->values) - idx1 = 0; - s->str[dir].vol[idx] = to_user(s, dir, c, ctl.value.integer.value[idx1]); - } - return 0; -} - -static int elem_read_switch(selem_t *s, int dir, selem_ctl_type_t type) -{ - snd_ctl_elem_value_t ctl; - unsigned int idx; - int err; - selem_ctl_t *c = &s->ctls[type]; - memset(&ctl, 0, sizeof(ctl)); - if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0) - return err; - for (idx = 0; idx < s->str[dir].channels; idx++) { - unsigned int idx1 = idx; - if (idx >= c->values) - idx1 = 0; - if (!ctl.value.integer.value[idx1]) - s->str[dir].sw &= ~(1 << idx); - } - return 0; -} - -static int elem_read_route(selem_t *s, int dir, selem_ctl_type_t type) -{ - snd_ctl_elem_value_t ctl; - unsigned int idx; - int err; - selem_ctl_t *c = &s->ctls[type]; - memset(&ctl, 0, sizeof(ctl)); - if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0) - return err; - for (idx = 0; idx < s->str[dir].channels; idx++) { - unsigned int idx1 = idx; - if (idx >= c->values) - idx1 = 0; - if (!ctl.value.integer.value[idx1 * c->values + idx1]) - s->str[dir].sw &= ~(1 << idx); - } - return 0; -} - -static int elem_read_enum(selem_t *s) -{ - snd_ctl_elem_value_t ctl; - unsigned int idx; - int err; - selem_ctl_t *c = &s->ctls[CTL_ENUMLIST]; - memset(&ctl, 0, sizeof(ctl)); - if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0) - return err; - for (idx = 0; idx < s->str[0].channels; idx++) { - unsigned int idx1 = idx; - if (idx >= c->values) - idx1 = 0; - s->str[0].vol[idx] = ctl.value.enumerated.item[idx1]; - } - return 0; -} - -static int selem_read(snd_mixer_elem_t *elem) -{ - selem_t *s; - unsigned int idx; - int err = 0; - long pvol[32], cvol[32]; - unsigned int psw, csw; - - assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - - memcpy(pvol, s->str[PLAY].vol, sizeof(pvol)); - memset(&s->str[PLAY].vol, 0, sizeof(s->str[PLAY].vol)); - psw = s->str[PLAY].sw; - s->str[PLAY].sw = ~0U; - memcpy(cvol, s->str[CAPT].vol, sizeof(cvol)); - memset(&s->str[CAPT].vol, 0, sizeof(s->str[CAPT].vol)); - csw = s->str[CAPT].sw; - s->str[CAPT].sw = ~0U; - - if (s->ctls[CTL_ENUMLIST].elem) { - err = elem_read_enum(s); - if (err < 0) - return err; - goto __skip_cswitch; - } - - if (s->ctls[CTL_PLAYBACK_VOLUME].elem) - err = elem_read_volume(s, PLAY, CTL_PLAYBACK_VOLUME); - else if (s->ctls[CTL_GLOBAL_VOLUME].elem) - err = elem_read_volume(s, PLAY, CTL_GLOBAL_VOLUME); - else if (s->ctls[CTL_SINGLE].elem && - s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER) - err = elem_read_volume(s, PLAY, CTL_SINGLE); - if (err < 0) - return err; - - if ((s->caps & (CAP_GSWITCH|CAP_PSWITCH)) == 0) { - s->str[PLAY].sw = 0; - goto __skip_pswitch; - } - if (s->ctls[CTL_PLAYBACK_SWITCH].elem) { - err = elem_read_switch(s, PLAY, CTL_PLAYBACK_SWITCH); - if (err < 0) - return err; - } - if (s->ctls[CTL_GLOBAL_SWITCH].elem) { - err = elem_read_switch(s, PLAY, CTL_GLOBAL_SWITCH); - if (err < 0) - return err; - } - if (s->ctls[CTL_SINGLE].elem && - s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) { - err = elem_read_switch(s, PLAY, CTL_SINGLE); - if (err < 0) - return err; - } - if (s->ctls[CTL_PLAYBACK_ROUTE].elem) { - err = elem_read_route(s, PLAY, CTL_PLAYBACK_ROUTE); - if (err < 0) - return err; - } - if (s->ctls[CTL_GLOBAL_ROUTE].elem) { - err = elem_read_route(s, PLAY, CTL_GLOBAL_ROUTE); - if (err < 0) - return err; - } - __skip_pswitch: - - if (s->ctls[CTL_CAPTURE_VOLUME].elem) - err = elem_read_volume(s, CAPT, CTL_CAPTURE_VOLUME); - else if (s->ctls[CTL_GLOBAL_VOLUME].elem) - err = elem_read_volume(s, CAPT, CTL_GLOBAL_VOLUME); - else if (s->ctls[CTL_SINGLE].elem && - s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER) - err = elem_read_volume(s, CAPT, CTL_SINGLE); - if (err < 0) - return err; - - if ((s->caps & (CAP_GSWITCH|CAP_CSWITCH)) == 0) { - s->str[CAPT].sw = 0; - goto __skip_cswitch; - } - if (s->ctls[CTL_CAPTURE_SWITCH].elem) { - err = elem_read_switch(s, CAPT, CTL_CAPTURE_SWITCH); - if (err < 0) - return err; - } - if (s->ctls[CTL_GLOBAL_SWITCH].elem) { - err = elem_read_switch(s, CAPT, CTL_GLOBAL_SWITCH); - if (err < 0) - return err; - } - if (s->ctls[CTL_SINGLE].elem && - s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) { - err = elem_read_switch(s, CAPT, CTL_SINGLE); - if (err < 0) - return err; - } - if (s->ctls[CTL_CAPTURE_ROUTE].elem) { - err = elem_read_route(s, CAPT, CTL_CAPTURE_ROUTE); - if (err < 0) - return err; - } - if (s->ctls[CTL_GLOBAL_ROUTE].elem) { - err = elem_read_route(s, CAPT, CTL_GLOBAL_ROUTE); - if (err < 0) - return err; - } - if (s->ctls[CTL_CAPTURE_SOURCE].elem) { - snd_ctl_elem_value_t ctl; - selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE]; - memset(&ctl, 0, sizeof(ctl)); - err = snd_hctl_elem_read(c->elem, &ctl); - if (err < 0) - return err; - for (idx = 0; idx < s->str[CAPT].channels; idx++) { - unsigned int idx1 = idx; - if (idx >= c->values) - idx1 = 0; - if (snd_ctl_elem_value_get_enumerated(&ctl, idx1) != s->capture_item) - s->str[CAPT].sw &= ~(1 << idx); - } - } - __skip_cswitch: - - if (memcmp(pvol, s->str[PLAY].vol, sizeof(pvol)) || - psw != s->str[PLAY].sw || - memcmp(cvol, s->str[CAPT].vol, sizeof(cvol)) || - csw != s->str[CAPT].sw) - return 1; - return 0; -} - -static int elem_write_volume(selem_t *s, int dir, selem_ctl_type_t type) -{ - snd_ctl_elem_value_t ctl; - unsigned int idx; - int err; - selem_ctl_t *c = &s->ctls[type]; - memset(&ctl, 0, sizeof(ctl)); - if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0) - return err; - for (idx = 0; idx < c->values; idx++) - ctl.value.integer.value[idx] = from_user(s, dir, c, s->str[dir].vol[idx]); - if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0) - return err; - return 0; -} - -static int elem_write_switch(selem_t *s, int dir, selem_ctl_type_t type) -{ - snd_ctl_elem_value_t ctl; - unsigned int idx; - int err; - selem_ctl_t *c = &s->ctls[type]; - memset(&ctl, 0, sizeof(ctl)); - if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0) - return err; - for (idx = 0; idx < c->values; idx++) - ctl.value.integer.value[idx] = !!(s->str[dir].sw & (1 << idx)); - if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0) - return err; - return 0; -} - -static int elem_write_switch_constant(selem_t *s, selem_ctl_type_t type, int val) -{ - snd_ctl_elem_value_t ctl; - unsigned int idx; - int err; - selem_ctl_t *c = &s->ctls[type]; - memset(&ctl, 0, sizeof(ctl)); - if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0) - return err; - for (idx = 0; idx < c->values; idx++) - ctl.value.integer.value[idx] = !!val; - if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0) - return err; - return 0; -} - -static int elem_write_route(selem_t *s, int dir, selem_ctl_type_t type) -{ - snd_ctl_elem_value_t ctl; - unsigned int idx; - int err; - selem_ctl_t *c = &s->ctls[type]; - memset(&ctl, 0, sizeof(ctl)); - if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0) - return err; - for (idx = 0; idx < c->values * c->values; idx++) - ctl.value.integer.value[idx] = 0; - for (idx = 0; idx < c->values; idx++) - ctl.value.integer.value[idx * c->values + idx] = !!(s->str[dir].sw & (1 << idx)); - if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0) - return err; - return 0; -} - -static int elem_write_enum(selem_t *s) -{ - snd_ctl_elem_value_t ctl; - unsigned int idx; - int err; - selem_ctl_t *c = &s->ctls[CTL_ENUMLIST]; - memset(&ctl, 0, sizeof(ctl)); - if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0) - return err; - for (idx = 0; idx < c->values; idx++) - ctl.value.enumerated.item[idx] = (unsigned int)s->str[0].vol[idx]; - if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0) - return err; - return 0; -} - -static int selem_write(snd_mixer_elem_t *elem) -{ - selem_t *s; - unsigned int idx; - int err; - - assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - - if (s->ctls[CTL_ENUMLIST].elem) - return elem_write_enum(s); - - if (s->ctls[CTL_SINGLE].elem) { - if (s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER) - err = elem_write_volume(s, PLAY, CTL_SINGLE); - else - err = elem_write_switch(s, PLAY, CTL_SINGLE); - if (err < 0) - return err; - } - if (s->ctls[CTL_GLOBAL_VOLUME].elem) { - err = elem_write_volume(s, PLAY, CTL_GLOBAL_VOLUME); - if (err < 0) - return err; - } - if (s->ctls[CTL_GLOBAL_SWITCH].elem) { - if (s->ctls[CTL_PLAYBACK_SWITCH].elem && s->ctls[CTL_CAPTURE_SWITCH].elem) - err = elem_write_switch_constant(s, CTL_GLOBAL_SWITCH, 1); - else - err = elem_write_switch(s, PLAY, CTL_GLOBAL_SWITCH); - if (err < 0) - return err; - } - if (s->ctls[CTL_PLAYBACK_VOLUME].elem) { - err = elem_write_volume(s, PLAY, CTL_PLAYBACK_VOLUME); - if (err < 0) - return err; - } - if (s->ctls[CTL_PLAYBACK_SWITCH].elem) { - err = elem_write_switch(s, PLAY, CTL_PLAYBACK_SWITCH); - if (err < 0) - return err; - } - if (s->ctls[CTL_PLAYBACK_ROUTE].elem) { - err = elem_write_route(s, PLAY, CTL_PLAYBACK_ROUTE); - if (err < 0) - return err; - } - if (s->ctls[CTL_CAPTURE_VOLUME].elem) { - err = elem_write_volume(s, CAPT, CTL_CAPTURE_VOLUME); - if (err < 0) - return err; - } - if (s->ctls[CTL_CAPTURE_SWITCH].elem) { - err = elem_write_switch(s, CAPT, CTL_CAPTURE_SWITCH); - if (err < 0) - return err; - } - if (s->ctls[CTL_CAPTURE_ROUTE].elem) { - err = elem_write_route(s, CAPT, CTL_CAPTURE_ROUTE); - if (err < 0) - return err; - } - if (s->ctls[CTL_CAPTURE_SOURCE].elem) { - snd_ctl_elem_value_t ctl; - selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE]; - memset(&ctl, 0, sizeof(ctl)); - if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0) - return err; - for (idx = 0; idx < c->values; idx++) { - if (s->str[CAPT].sw & (1 << idx)) - snd_ctl_elem_value_set_enumerated(&ctl, idx, s->capture_item); - } - if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0) - return err; - /* update the element, don't remove */ - err = selem_read(elem); - if (err < 0) - return err; - } - return 0; -} - -static void selem_free(snd_mixer_elem_t *elem) -{ - selem_t *s; - assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - elem->private_data = NULL; - free(s); -} - -static int simple_update(snd_mixer_elem_t *melem) -{ - selem_t *simple; - unsigned int caps, pchannels, cchannels; - long pmin, pmax, cmin, cmax; - selem_ctl_t *ctl; - - caps = 0; - pchannels = 0; - pmin = pmax = 0; - cchannels = 0; - cmin = cmax = 0; - assert(melem->type == SND_MIXER_ELEM_SIMPLE); - simple = melem->private_data; - ctl = &simple->ctls[CTL_SINGLE]; - if (ctl->elem) { - pchannels = cchannels = ctl->values; - if (ctl->type == SNDRV_CTL_ELEM_TYPE_INTEGER) { - caps |= CAP_GVOLUME; - pmin = cmin = ctl->min; - pmax = cmax = ctl->max; - } else - caps |= CAP_GSWITCH; - } - ctl = &simple->ctls[CTL_GLOBAL_SWITCH]; - if (ctl->elem) { - if (pchannels < ctl->values) - pchannels = ctl->values; - if (cchannels < ctl->values) - cchannels = ctl->values; - caps |= CAP_GSWITCH; - } - ctl = &simple->ctls[CTL_GLOBAL_ROUTE]; - if (ctl->elem) { - if (pchannels < ctl->values) - pchannels = ctl->values; - if (cchannels < ctl->values) - cchannels = ctl->values; - caps |= CAP_GSWITCH; - } - ctl = &simple->ctls[CTL_GLOBAL_VOLUME]; - if (ctl->elem) { - if (pchannels < ctl->values) - pchannels = ctl->values; - if (pmin > ctl->min) - pmin = ctl->min; - if (pmax < ctl->max) - pmax = ctl->max; - if (cchannels < ctl->values) - cchannels = ctl->values; - if (cmin > ctl->min) - cmin = ctl->min; - if (cmax < ctl->max) - cmax = ctl->max; - caps |= CAP_GVOLUME; - } - ctl = &simple->ctls[CTL_PLAYBACK_SWITCH]; - if (ctl->elem) { - if (pchannels < ctl->values) - pchannels = ctl->values; - caps |= CAP_PSWITCH; - caps &= ~CAP_GSWITCH; - } - ctl = &simple->ctls[CTL_PLAYBACK_ROUTE]; - if (ctl->elem) { - if (pchannels < ctl->values) - pchannels = ctl->values; - caps |= CAP_PSWITCH; - caps &= ~CAP_GSWITCH; - } - ctl = &simple->ctls[CTL_CAPTURE_SWITCH]; - if (ctl->elem) { - if (cchannels < ctl->values) - cchannels = ctl->values; - caps |= CAP_CSWITCH; - caps &= ~CAP_GSWITCH; - } - ctl = &simple->ctls[CTL_CAPTURE_ROUTE]; - if (ctl->elem) { - if (cchannels < ctl->values) - cchannels = ctl->values; - caps |= CAP_CSWITCH; - caps &= ~CAP_GSWITCH; - } - ctl = &simple->ctls[CTL_PLAYBACK_VOLUME]; - if (ctl->elem) { - if (pchannels < ctl->values) - pchannels = ctl->values; - if (pmin > ctl->min) - pmin = ctl->min; - if (pmax < ctl->max) - pmax = ctl->max; - caps |= CAP_PVOLUME; - caps &= ~CAP_GVOLUME; - } - ctl = &simple->ctls[CTL_CAPTURE_VOLUME]; - if (ctl->elem) { - if (cchannels < ctl->values) - cchannels = ctl->values; - if (cmin > ctl->min) - cmin = ctl->min; - if (cmax < ctl->max) - cmax = ctl->max; - caps |= CAP_CVOLUME; - caps &= ~CAP_GVOLUME; - } - ctl = &simple->ctls[CTL_CAPTURE_SOURCE]; - if (ctl->elem) { - if (cchannels < ctl->values) - cchannels = ctl->values; - caps |= CAP_CSWITCH | CAP_CSWITCH_EXCL; - caps &= ~CAP_GSWITCH; - } - ctl = &simple->ctls[CTL_ENUMLIST]; - if (ctl->elem) { - if (pchannels < ctl->values) - pchannels = ctl->values; - caps |= CAP_ENUM; - } - if (pchannels > 32) - pchannels = 32; - if (cchannels > 32) - cchannels = 32; - if (caps & (CAP_GSWITCH|CAP_PSWITCH)) - caps |= CAP_PSWITCH_JOIN; - if (caps & (CAP_GVOLUME|CAP_PVOLUME)) - caps |= CAP_PVOLUME_JOIN; - if (caps & (CAP_GSWITCH|CAP_CSWITCH)) - caps |= CAP_CSWITCH_JOIN; - if (caps & (CAP_GVOLUME|CAP_CVOLUME)) - caps |= CAP_PVOLUME_JOIN; - if (pchannels > 1 || cchannels > 1) { - if (simple->ctls[CTL_SINGLE].elem && - simple->ctls[CTL_SINGLE].values > 1) { - if (caps & CAP_GSWITCH) - caps &= ~CAP_PSWITCH_JOIN; - else - caps &= ~CAP_PVOLUME_JOIN; - } - if (simple->ctls[CTL_GLOBAL_ROUTE].elem || - (simple->ctls[CTL_GLOBAL_SWITCH].elem && - simple->ctls[CTL_GLOBAL_SWITCH].values > 1)) { - caps &= ~(CAP_PSWITCH_JOIN|CAP_CSWITCH_JOIN); - } - if (simple->ctls[CTL_GLOBAL_VOLUME].elem && - simple->ctls[CTL_GLOBAL_VOLUME].values > 1) { - caps &= ~(CAP_PVOLUME_JOIN|CAP_CVOLUME_JOIN); - } - } - if (pchannels > 1) { - if (simple->ctls[CTL_PLAYBACK_ROUTE].elem || - (simple->ctls[CTL_PLAYBACK_SWITCH].elem && - simple->ctls[CTL_PLAYBACK_SWITCH].values > 1)) { - caps &= ~CAP_PSWITCH_JOIN; - } - if (simple->ctls[CTL_PLAYBACK_VOLUME].elem && - simple->ctls[CTL_PLAYBACK_VOLUME].values > 1) { - caps &= ~CAP_PVOLUME_JOIN; - } - } - if (cchannels > 1) { - if (simple->ctls[CTL_CAPTURE_ROUTE].elem || - (simple->ctls[CTL_CAPTURE_SWITCH].elem && - simple->ctls[CTL_CAPTURE_SWITCH].values > 1)) { - caps &= ~CAP_CSWITCH_JOIN; - } - if (simple->ctls[CTL_CAPTURE_VOLUME].elem && - simple->ctls[CTL_CAPTURE_VOLUME].values > 1) { - caps &= ~CAP_CVOLUME_JOIN; - } - } - - /* exceptions */ - if ((caps & (CAP_GSWITCH|CAP_PSWITCH|CAP_CSWITCH)) && - (caps & (CAP_GSWITCH|CAP_PSWITCH|CAP_CSWITCH)) == (caps & CAP_GSWITCH)) { - caps &= ~(CAP_GSWITCH|CAP_CSWITCH_JOIN|CAP_CSWITCH_EXCL); - caps |= CAP_PSWITCH; - } - - simple->caps = caps; - simple->str[PLAY].channels = pchannels; - if (!simple->str[PLAY].range) { - simple->str[PLAY].min = pmin; - simple->str[PLAY].max = pmax; - } - simple->str[CAPT].channels = cchannels; - if (!simple->str[CAPT].range) { - simple->str[CAPT].min = cmin; - simple->str[CAPT].max = cmax; - } - return 0; -} - -#ifndef DOC_HIDDEN -static struct suf { - const char *suffix; - selem_ctl_type_t type; -} suffixes[] = { - {" Playback Switch", CTL_PLAYBACK_SWITCH}, - {" Playback Route", CTL_PLAYBACK_ROUTE}, - {" Playback Volume", CTL_PLAYBACK_VOLUME}, - {" Capture Switch", CTL_CAPTURE_SWITCH}, - {" Capture Route", CTL_CAPTURE_ROUTE}, - {" Capture Volume", CTL_CAPTURE_VOLUME}, - {" Switch", CTL_GLOBAL_SWITCH}, - {" Route", CTL_GLOBAL_ROUTE}, - {" Volume", CTL_GLOBAL_VOLUME}, - {NULL, 0} -}; -#endif - -/* Return base length or 0 on failure */ -static int base_len(const char *name, selem_ctl_type_t *type) -{ - struct suf *p; - size_t nlen = strlen(name); - p = suffixes; - while (p->suffix) { - size_t slen = strlen(p->suffix); - size_t l; - if (nlen > slen) { - l = nlen - slen; - if (strncmp(name + l, p->suffix, slen) == 0 && - (l < 1 || name[l-1] != '-')) { /* 3D Control - Switch */ - *type = p->type; - return l; - } - } - p++; - } - return 0; -} - -static int simple_add1(snd_mixer_class_t *class, const char *name, - snd_hctl_elem_t *helem, selem_ctl_type_t type, - unsigned int value) -{ - snd_mixer_elem_t *melem; - snd_mixer_selem_id_t id; - int new = 0; - int err; - snd_ctl_elem_info_t info; - selem_t *simple; - const char *name1; - memset(&info, 0, sizeof(info)); - err = snd_hctl_elem_info(helem, &info); - if (err < 0) - return err; - switch (type) { - case CTL_SINGLE: - if (info.type == SND_CTL_ELEM_TYPE_ENUMERATED) - type = CTL_ENUMLIST; - else if (info.type != SND_CTL_ELEM_TYPE_BOOLEAN && - info.type != SND_CTL_ELEM_TYPE_INTEGER) - return 0; - break; - case CTL_GLOBAL_ROUTE: - case CTL_PLAYBACK_ROUTE: - case CTL_CAPTURE_ROUTE: - { - unsigned int n; - if (info.type == SND_CTL_ELEM_TYPE_ENUMERATED) { - type = CTL_ENUMLIST; - break; - } - if (info.type != SND_CTL_ELEM_TYPE_BOOLEAN) - return 0; - n = sqrt((double)info.count); - if (n * n != info.count) - return 0; - info.count = n; - break; - } - case CTL_GLOBAL_SWITCH: - case CTL_PLAYBACK_SWITCH: - case CTL_CAPTURE_SWITCH: - if (info.type == SND_CTL_ELEM_TYPE_ENUMERATED) { - type = CTL_ENUMLIST; - break; - } - if (info.type != SND_CTL_ELEM_TYPE_BOOLEAN) - return 0; - break; - case CTL_GLOBAL_VOLUME: - case CTL_PLAYBACK_VOLUME: - case CTL_CAPTURE_VOLUME: - if (info.type == SND_CTL_ELEM_TYPE_ENUMERATED) { - type = CTL_ENUMLIST; - break; - } - if (info.type != SND_CTL_ELEM_TYPE_INTEGER) - return 0; - break; - case CTL_CAPTURE_SOURCE: - if (info.type != SND_CTL_ELEM_TYPE_ENUMERATED) - return 0; - break; - default: - assert(0); - break; - } - name1 = get_short_name(name); - strncpy(id.name, name1, sizeof(id.name)); - id.index = snd_hctl_elem_get_index(helem); - melem = snd_mixer_find_selem(class->mixer, &id); - if (!melem) { - simple = calloc(1, sizeof(*simple)); - if (!simple) - return -ENOMEM; - melem = calloc(1, sizeof(*melem)); - if (!melem) { - free(simple); - return -ENOMEM; - } - simple->id = id; - melem->type = SND_MIXER_ELEM_SIMPLE; - melem->private_data = simple; - melem->private_free = selem_free; - INIT_LIST_HEAD(&melem->helems); - melem->compare_weight = get_compare_weight(simple->id.name, simple->id.index); - new = 1; - } else { - simple = melem->private_data; - } - if (simple->ctls[type].elem) { - SNDERR("helem (%s,'%s',%li,%li,%li) appears twice or more", - snd_ctl_elem_iface_name(snd_hctl_elem_get_interface(helem)), - snd_hctl_elem_get_name(helem), - snd_hctl_elem_get_index(helem), - snd_hctl_elem_get_device(helem), - snd_hctl_elem_get_subdevice(helem)); - return -EINVAL; - } - simple->ctls[type].elem = helem; - simple->ctls[type].type = info.type; - simple->ctls[type].access = info.access; - simple->ctls[type].values = info.count; - if (type == CTL_ENUMLIST) { - simple->ctls[type].min = 0; - simple->ctls[type].max = info.value.enumerated.items; - } else { - simple->ctls[type].min = info.value.integer.min; - simple->ctls[type].max = info.value.integer.max; - } - switch (type) { - case CTL_CAPTURE_SOURCE: - simple->capture_item = value; - break; - default: - break; - } - err = snd_mixer_elem_attach(melem, helem); - if (err < 0) - return err; - err = simple_update(melem); - if (err < 0) - return err; - if (new) - err = snd_mixer_elem_add(melem, class); - else - err = snd_mixer_elem_info(melem); - if (err < 0) - return err; - err = selem_read(melem); - if (err < 0) - return err; - if (err) - err = snd_mixer_elem_value(melem); - return err; -} - -static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem) -{ - const char *name = snd_hctl_elem_get_name(helem); - size_t len; - selem_ctl_type_t type; - if (snd_hctl_elem_get_interface(helem) != SND_CTL_ELEM_IFACE_MIXER) - return 0; - if (strcmp(name, "Capture Source") == 0) { - snd_ctl_elem_info_t *info; - unsigned int k, items; - int err; - snd_ctl_elem_info_alloca(&info); - err = snd_hctl_elem_info(helem, info); - assert(err >= 0); - if (snd_ctl_elem_info_get_type(info) != SND_CTL_ELEM_TYPE_ENUMERATED) - return 0; - items = snd_ctl_elem_info_get_items(info); - for (k = 0; k < items; ++k) { - const char *n; - snd_ctl_elem_info_set_item(info, k); - err = snd_hctl_elem_info(helem, info); - if (err < 0) - return err; - n = snd_ctl_elem_info_get_item_name(info); - err = simple_add1(class, n, helem, CTL_CAPTURE_SOURCE, k); - if (err < 0) - return err; - } - return 0; - } - len = base_len(name, &type); - if (len == 0) { - return simple_add1(class, name, helem, CTL_SINGLE, 0); - } else { - char ename[128]; - if (len >= sizeof(ename)) - len = sizeof(ename) - 1; - memcpy(ename, name, len); - ename[len] = 0; - /* exception: Capture Volume and Capture Switch */ - if (type == CTL_GLOBAL_VOLUME && !strcmp(ename, "Capture")) - type = CTL_CAPTURE_VOLUME; - else if (type == CTL_GLOBAL_SWITCH && !strcmp(ename, "Capture")) - type = CTL_CAPTURE_SWITCH; - return simple_add1(class, ename, helem, type, 0); - } -} - -static int simple_event_remove(snd_hctl_elem_t *helem, - snd_mixer_elem_t *melem) -{ - selem_t *simple = melem->private_data; - int err; - int k; - for (k = 0; k <= CTL_LAST; k++) { - if (simple->ctls[k].elem == helem) - break; - } - assert(k <= CTL_LAST); - simple->ctls[k].elem = NULL; - err = snd_mixer_elem_detach(melem, helem); - if (err < 0) - return err; - if (snd_mixer_elem_empty(melem)) - return snd_mixer_elem_remove(melem); - err = simple_update(melem); - return snd_mixer_elem_info(melem); -} - -static int simple_event(snd_mixer_class_t *class, unsigned int mask, - snd_hctl_elem_t *helem, snd_mixer_elem_t *melem) -{ - int err; - if (mask == SND_CTL_EVENT_MASK_REMOVE) - return simple_event_remove(helem, melem); - if (mask & SND_CTL_EVENT_MASK_ADD) { - err = simple_event_add(class, helem); - if (err < 0) - return err; - } - if (mask & SND_CTL_EVENT_MASK_INFO) { - err = simple_event_remove(helem, melem); - if (err < 0) - return err; - err = simple_event_add(class, helem); - if (err < 0) - return err; - return 0; - } - if (mask & SND_CTL_EVENT_MASK_VALUE) { - err = selem_read(melem); - if (err < 0) - return err; - if (err) { - err = snd_mixer_elem_value(melem); - if (err < 0) - return err; - } - } - return 0; -} - -static int simple_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2) -{ - selem_t *s1 = c1->private_data; - selem_t *s2 = c2->private_data; - int res = strcmp(s1->id.name, s2->id.name); - if (res) - return res; - return s1->id.index - s2->id.index; -} - +#include "mixer_simple.h" +#include "alisp.h" /** * \brief Register mixer simple element class * \param mixer Mixer handle - * \param options Options container (not used now) + * \param options Options container * \param classp Pointer to returned mixer simple element class handle (or NULL) * \return 0 on success otherwise a negative error code */ -int snd_mixer_selem_register(snd_mixer_t *mixer, struct - snd_mixer_selem_regopt *options ATTRIBUTE_UNUSED, +int snd_mixer_selem_register(snd_mixer_t *mixer, + struct snd_mixer_selem_regopt *options, snd_mixer_class_t **classp) { - snd_mixer_class_t *class = calloc(1, sizeof(*class)); - int err; - if (!class) - return -ENOMEM; - class->event = simple_event; - class->compare = simple_compare; - err = snd_mixer_class_register(class, mixer); - if (err < 0) { - free(class); - return err; + if (options && options->ver == 1) { + if (options->device != NULL && + (options->playback_pcm != NULL || + options->capture_pcm != NULL)) + return -EINVAL; + if (options->device == NULL && + options->playback_pcm == NULL && + options->capture_pcm == NULL) + return -EINVAL; } - if (classp) - *classp = class; - return 0; + if (options == NULL || + (options->ver == 1 && options->abstract == SND_MIXER_SABSTRACT_NONE)) { + return snd_mixer_simple_none_register(mixer, options, classp); + } else if (options->ver == 1) { + if (options->abstract == SND_MIXER_SABSTRACT_BASIC) + return snd_mixer_simple_basic_register(mixer, options, classp); + } + return -ENXIO; } + +#ifndef DOC_HIDDEN +int snd_mixer_selem_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2) +{ + sm_selem_t *s1 = c1->private_data; + sm_selem_t *s2 = c2->private_data; + int res = strcmp(s1->id->name, s2->id->name); + if (res) + return res; + return s1->id->index - s2->id->index; +} +#endif /** * \brief Find a mixer simple element @@ -1142,14 +93,15 @@ snd_mixer_elem_t *snd_mixer_find_selem(snd_mixer_t *mixer, const snd_mixer_selem_id_t *id) { struct list_head *list; + snd_mixer_elem_t *e; + sm_selem_t *s; + list_for_each(list, &mixer->elems) { - snd_mixer_elem_t *e; - selem_t *s; e = list_entry(list, snd_mixer_elem_t, list); if (e->type != SND_MIXER_ELEM_SIMPLE) continue; s = e->private_data; - if (!strcmp(s->id.name, id->name) && s->id.index == id->index) + if (!strcmp(s->id->name, id->name) && s->id->index == id->index) return e; } return NULL; @@ -1163,11 +115,11 @@ snd_mixer_elem_t *snd_mixer_find_selem(snd_mixer_t *mixer, void snd_mixer_selem_get_id(snd_mixer_elem_t *elem, snd_mixer_selem_id_t *id) { - selem_t *s; + sm_selem_t *s; assert(elem && id); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - *id = s->id; + *id = *s->id; } /** @@ -1177,11 +129,11 @@ void snd_mixer_selem_get_id(snd_mixer_elem_t *elem, */ const char *snd_mixer_selem_get_name(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return s->id.name; + return s->id->name; } /** @@ -1191,11 +143,11 @@ const char *snd_mixer_selem_get_name(snd_mixer_elem_t *elem) */ unsigned int snd_mixer_selem_get_index(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return s->id.index; + return s->id->index; } /** @@ -1205,11 +157,11 @@ unsigned int snd_mixer_selem_get_index(snd_mixer_elem_t *elem) */ int snd_mixer_selem_has_common_volume(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return !!(s->caps & CAP_GVOLUME); + return !!(s->caps & SM_CAP_GVOLUME); } /** @@ -1219,83 +171,11 @@ int snd_mixer_selem_has_common_volume(snd_mixer_elem_t *elem) */ int snd_mixer_selem_has_common_switch(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return !!(s->caps & CAP_GSWITCH); -} - -static int _snd_mixer_selem_set_volume(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value) -{ - selem_t *s = elem->private_data; - if ((unsigned int) channel >= s->str[dir].channels) - return 0; - if (value < s->str[dir].min || value > s->str[dir].max) - return 0; - if (s->caps & - (dir == PLAY ? CAP_PVOLUME_JOIN : CAP_CVOLUME_JOIN)) - channel = 0; - if (value != s->str[dir].vol[channel]) { - s->str[dir].vol[channel] = value; - return 1; - } - return 0; -} - -static int _snd_mixer_selem_set_volume_all(snd_mixer_elem_t *elem, int dir, long value) -{ - int changed = 0; - snd_mixer_selem_channel_id_t channel; - selem_t *s = elem->private_data; - if (value < s->str[dir].min || value > s->str[dir].max) - return 0; - for (channel = 0; (unsigned int) channel < s->str[dir].channels; channel++) { - if (value != s->str[dir].vol[channel]) { - s->str[dir].vol[channel] = value; - changed = 1; - } - } - return changed; -} - -static int _snd_mixer_selem_set_switch(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int value) -{ - selem_t *s = elem->private_data; - if ((unsigned int) channel >= s->str[dir].channels) - return 0; - if (s->caps & - (dir == PLAY ? CAP_PSWITCH_JOIN : CAP_CSWITCH_JOIN)) - channel = 0; - if (value) { - if (!(s->str[dir].sw & (1 << channel))) { - s->str[dir].sw |= 1 << channel; - return 1; - } - } else { - if (s->str[dir].sw & (1 << channel)) { - s->str[dir].sw &= ~(1 << channel); - return 1; - } - } - return 0; -} - -static int _snd_mixer_selem_set_switch_all(snd_mixer_elem_t *elem, int dir, int value) -{ - selem_t *s = elem->private_data; - if (value) { - if (s->str[dir].sw != ~0U) { - s->str[dir].sw = ~0U; - return 1; - } - } else { - if (s->str[dir].sw != 0U) { - s->str[dir].sw = 0U; - return 1; - } - } - return 0; + return !!(s->caps & SM_CAP_GSWITCH); } /** @@ -1308,10 +188,13 @@ const char *snd_mixer_selem_channel_name(snd_mixer_selem_channel_id_t channel) static const char *array[SND_MIXER_SCHN_LAST + 1] = { [SND_MIXER_SCHN_FRONT_LEFT] = "Front Left", [SND_MIXER_SCHN_FRONT_RIGHT] = "Front Right", - [SND_MIXER_SCHN_FRONT_CENTER] = "Front Center", [SND_MIXER_SCHN_REAR_LEFT] = "Rear Left", [SND_MIXER_SCHN_REAR_RIGHT] = "Rear Right", - [SND_MIXER_SCHN_WOOFER] = "Woofer" + [SND_MIXER_SCHN_FRONT_CENTER] = "Front Center", + [SND_MIXER_SCHN_WOOFER] = "Woofer", + [SND_MIXER_SCHN_SIDE_LEFT] = "Side Left", + [SND_MIXER_SCHN_SIDE_RIGHT] = "Side Right", + [SND_MIXER_SCHN_REAR_CENTER] = "Rear Center" }; const char *p; assert(channel <= SND_MIXER_SCHN_LAST); @@ -1328,15 +211,9 @@ const char *snd_mixer_selem_channel_name(snd_mixer_selem_channel_id_t channel) */ int snd_mixer_selem_is_active(snd_mixer_elem_t *elem) { - selem_t *s; - selem_ctl_type_t ctl; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - for (ctl = CTL_SINGLE; ctl <= CTL_LAST; ctl++) - if (s->ctls[ctl].elem != NULL && (s->ctls[ctl].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) != 0) - return 0; - return 1; + return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_ACTIVE, 0); } /** @@ -1346,11 +223,9 @@ int snd_mixer_selem_is_active(snd_mixer_elem_t *elem) */ int snd_mixer_selem_is_playback_mono(snd_mixer_elem_t *elem) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - return s->str[PLAY].channels == 1; + return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_MONO, 0); } /** @@ -1361,11 +236,9 @@ int snd_mixer_selem_is_playback_mono(snd_mixer_elem_t *elem) */ int snd_mixer_selem_has_playback_channel(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - return (unsigned int) channel < s->str[PLAY].channels; + return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_CHANNEL, (int)channel); } /** @@ -1377,12 +250,23 @@ int snd_mixer_selem_has_playback_channel(snd_mixer_elem_t *elem, snd_mixer_selem void snd_mixer_selem_get_playback_volume_range(snd_mixer_elem_t *elem, long *min, long *max) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - *min = s->str[PLAY].min; - *max = s->str[PLAY].max; + sm_selem_ops(elem)->get_range(elem, SM_PLAY, min, max); +} + +/** + * \brief Get range in dB for playback volume of a mixer simple element + * \param elem Mixer simple element handle + * \param min Pointer to returned minimum (dB * 100) + * \param max Pointer to returned maximum (dB * 100) + */ +void snd_mixer_selem_get_playback_dB_range(snd_mixer_elem_t *elem, + long *min, long *max) +{ + assert(elem); + assert(elem->type == SND_MIXER_ELEM_SIMPLE); + sm_selem_ops(elem)->get_dB_range(elem, SM_PLAY, min, max); } /** @@ -1394,15 +278,10 @@ void snd_mixer_selem_get_playback_volume_range(snd_mixer_elem_t *elem, void snd_mixer_selem_set_playback_volume_range(snd_mixer_elem_t *elem, long min, long max) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); assert(min < max); - s = elem->private_data; - s->str[PLAY].range = 1; - s->str[PLAY].min = min; - s->str[PLAY].max = max; - selem_read(elem); + sm_selem_ops(elem)->set_range(elem, SM_PLAY, min, max); } /** @@ -1412,11 +291,11 @@ void snd_mixer_selem_set_playback_volume_range(snd_mixer_elem_t *elem, */ int snd_mixer_selem_has_playback_volume(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return !!(s->caps & CAP_PVOLUME) || !!(s->caps & CAP_GVOLUME); + return !!(s->caps & SM_CAP_PVOLUME) || !!(s->caps & SM_CAP_GVOLUME); } /** @@ -1426,11 +305,11 @@ int snd_mixer_selem_has_playback_volume(snd_mixer_elem_t *elem) */ int snd_mixer_selem_has_playback_volume_joined(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return !!(s->caps & CAP_PVOLUME_JOIN); + return !!(s->caps & SM_CAP_PVOLUME_JOIN); } /** @@ -1440,11 +319,11 @@ int snd_mixer_selem_has_playback_volume_joined(snd_mixer_elem_t *elem) */ int snd_mixer_selem_has_playback_switch(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return !!(s->caps & CAP_PSWITCH) || !!(s->caps & CAP_GSWITCH); + return !!(s->caps & SM_CAP_PSWITCH) || !!(s->caps & SM_CAP_GSWITCH); } /** @@ -1454,11 +333,11 @@ int snd_mixer_selem_has_playback_switch(snd_mixer_elem_t *elem) */ int snd_mixer_selem_has_playback_switch_joined(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return !!(s->caps & CAP_PSWITCH_JOIN); + return !!(s->caps & SM_CAP_PSWITCH_JOIN); } /** @@ -1470,18 +349,23 @@ int snd_mixer_selem_has_playback_switch_joined(snd_mixer_elem_t *elem) */ int snd_mixer_selem_get_playback_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if ((unsigned int) channel >= s->str[PLAY].channels) - return -EINVAL; - if (! (s->caps & (CAP_PVOLUME|CAP_GVOLUME))) - return -EINVAL; - if (s->caps & CAP_PVOLUME_JOIN) - channel = 0; - *value = s->str[PLAY].vol[channel]; - return 0; + return sm_selem_ops(elem)->get_volume(elem, SM_PLAY, channel, value); +} + +/** + * \brief Return value of playback volume in dB control of a mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param value pointer to returned value (dB * 100) + * \return 0 on success otherwise a negative error code + */ +int snd_mixer_selem_get_playback_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value) +{ + assert(elem); + assert(elem->type == SND_MIXER_ELEM_SIMPLE); + return sm_selem_ops(elem)->get_dB(elem, SM_PLAY, channel, value); } /** @@ -1493,18 +377,9 @@ int snd_mixer_selem_get_playback_volume(snd_mixer_elem_t *elem, snd_mixer_selem_ */ int snd_mixer_selem_get_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int *value) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if ((unsigned int) channel >= s->str[PLAY].channels) - return -EINVAL; - if (! (s->caps & (CAP_PSWITCH|CAP_GSWITCH))) - return -EINVAL; - if (s->caps & CAP_PSWITCH_JOIN) - channel = 0; - *value = !!(s->str[PLAY].sw & (1 << channel)); - return 0; + return sm_selem_ops(elem)->get_switch(elem, SM_PLAY, channel, value); } /** @@ -1516,19 +391,23 @@ int snd_mixer_selem_get_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_ */ int snd_mixer_selem_set_playback_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value) { - int changed; - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if (! (s->caps & (CAP_GVOLUME|CAP_PVOLUME))) - return -EINVAL; - changed = _snd_mixer_selem_set_volume(elem, PLAY, channel, value); - if (changed < 0) - return changed; - if (changed) - return selem_write(elem); - return 0; + return sm_selem_ops(elem)->set_volume(elem, SM_PLAY, channel, value); +} + +/** + * \brief Set value in dB of playback volume control of a mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param value control value in dB * 100 + * \return 0 on success otherwise a negative error code + */ +int snd_mixer_selem_set_playback_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value, int dir) +{ + assert(elem); + assert(elem->type == SND_MIXER_ELEM_SIMPLE); + return sm_selem_ops(elem)->set_dB(elem, SM_PLAY, channel, value, dir); } /** @@ -1539,19 +418,22 @@ int snd_mixer_selem_set_playback_volume(snd_mixer_elem_t *elem, snd_mixer_selem_ */ int snd_mixer_selem_set_playback_volume_all(snd_mixer_elem_t *elem, long value) { - int changed; - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if (! (s->caps & (CAP_GVOLUME|CAP_PVOLUME))) - return -EINVAL; - changed = _snd_mixer_selem_set_volume_all(elem, PLAY, value); - if (changed < 0) - return changed; - if (changed) - return selem_write(elem); - return 0; + return sm_selem_ops(elem)->set_volume_all(elem, SM_PLAY, value); +} + +/** + * \brief Set value in dB of playback volume control for all channels of a mixer simple element + * \param elem Mixer simple element handle + * \param value control value in dB * 100 + * \return 0 on success otherwise a negative error code + */ +int snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *elem, long value, int dir) +{ + assert(elem); + assert(elem->type == SND_MIXER_ELEM_SIMPLE); + return sm_selem_ops(elem)->set_dB_all(elem, SM_PLAY, value, dir); } /** @@ -1563,19 +445,9 @@ int snd_mixer_selem_set_playback_volume_all(snd_mixer_elem_t *elem, long value) */ int snd_mixer_selem_set_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int value) { - int changed; - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if (! (s->caps & (CAP_GSWITCH|CAP_PSWITCH))) - return -EINVAL; - changed = _snd_mixer_selem_set_switch(elem, PLAY, channel, value); - if (changed < 0) - return changed; - if (changed) - return selem_write(elem); - return 0; + return sm_selem_ops(elem)->set_switch(elem, SM_PLAY, channel, value); } /** @@ -1586,19 +458,9 @@ int snd_mixer_selem_set_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_ */ int snd_mixer_selem_set_playback_switch_all(snd_mixer_elem_t *elem, int value) { - int changed; - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if (! (s->caps & (CAP_GSWITCH|CAP_PSWITCH))) - return -EINVAL; - changed = _snd_mixer_selem_set_switch_all(elem, PLAY, value); - if (changed < 0) - return changed; - if (changed) - return selem_write(elem); - return 0; + return sm_selem_ops(elem)->set_switch_all(elem, SM_PLAY, value); } /** @@ -1608,11 +470,9 @@ int snd_mixer_selem_set_playback_switch_all(snd_mixer_elem_t *elem, int value) */ int snd_mixer_selem_is_capture_mono(snd_mixer_elem_t *elem) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - return s->str[CAPT].channels == 1; + return sm_selem_ops(elem)->is(elem, SM_CAPT, SM_OPS_IS_MONO, 0); } /** @@ -1623,11 +483,9 @@ int snd_mixer_selem_is_capture_mono(snd_mixer_elem_t *elem) */ int snd_mixer_selem_has_capture_channel(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - return (unsigned int) channel < s->str[CAPT].channels; + return sm_selem_ops(elem)->is(elem, SM_CAPT, SM_OPS_IS_CHANNEL, channel); } /** @@ -1639,12 +497,23 @@ int snd_mixer_selem_has_capture_channel(snd_mixer_elem_t *elem, snd_mixer_selem_ void snd_mixer_selem_get_capture_volume_range(snd_mixer_elem_t *elem, long *min, long *max) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - *min = s->str[CAPT].min; - *max = s->str[CAPT].max; + sm_selem_ops(elem)->get_range(elem, SM_CAPT, min, max); +} + +/** + * \brief Get range in dB for capture volume of a mixer simple element + * \param elem Mixer simple element handle + * \param min Pointer to returned minimum (dB * 100) + * \param max Pointer to returned maximum (dB * 100) + */ +void snd_mixer_selem_get_capture_dB_range(snd_mixer_elem_t *elem, + long *min, long *max) +{ + assert(elem); + assert(elem->type == SND_MIXER_ELEM_SIMPLE); + sm_selem_ops(elem)->get_dB_range(elem, SM_CAPT, min, max); } /** @@ -1656,15 +525,10 @@ void snd_mixer_selem_get_capture_volume_range(snd_mixer_elem_t *elem, void snd_mixer_selem_set_capture_volume_range(snd_mixer_elem_t *elem, long min, long max) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); assert(min < max); - s = elem->private_data; - s->str[CAPT].range = 1; - s->str[CAPT].min = min; - s->str[CAPT].max = max; - selem_read(elem); + sm_selem_ops(elem)->set_range(elem, SM_CAPT, min, max); } /** @@ -1674,11 +538,11 @@ void snd_mixer_selem_set_capture_volume_range(snd_mixer_elem_t *elem, */ int snd_mixer_selem_has_capture_volume(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return !!(s->caps & CAP_CVOLUME) /*|| !!(s->caps & CAP_GVOLUME)*/; + return !!(s->caps & SM_CAP_CVOLUME); } /** @@ -1688,11 +552,11 @@ int snd_mixer_selem_has_capture_volume(snd_mixer_elem_t *elem) */ int snd_mixer_selem_has_capture_volume_joined(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return !!(s->caps & CAP_CVOLUME_JOIN); + return !!(s->caps & SM_CAP_CVOLUME_JOIN); } /** @@ -1702,11 +566,11 @@ int snd_mixer_selem_has_capture_volume_joined(snd_mixer_elem_t *elem) */ int snd_mixer_selem_has_capture_switch(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return !!(s->caps & CAP_CSWITCH) /*|| !!(s->caps & CAP_GSWITCH)*/; + return !!(s->caps & SM_CAP_CSWITCH); } /** @@ -1716,11 +580,11 @@ int snd_mixer_selem_has_capture_switch(snd_mixer_elem_t *elem) */ int snd_mixer_selem_has_capture_switch_joined(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return !!(s->caps & CAP_CSWITCH_JOIN); + return !!(s->caps & SM_CAP_CSWITCH_JOIN); } /** @@ -1730,11 +594,11 @@ int snd_mixer_selem_has_capture_switch_joined(snd_mixer_elem_t *elem) */ int snd_mixer_selem_has_capture_switch_exclusive(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - return !!(s->caps & CAP_CSWITCH_EXCL); + return !!(s->caps & SM_CAP_CSWITCH_EXCL); } /** @@ -1744,11 +608,11 @@ int snd_mixer_selem_has_capture_switch_exclusive(snd_mixer_elem_t *elem) */ int snd_mixer_selem_get_capture_group(snd_mixer_elem_t *elem) { - selem_t *s; + sm_selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); s = elem->private_data; - if (! (s->caps & CAP_CSWITCH_EXCL)) + if (! (s->caps & SM_CAP_CSWITCH_EXCL)) return -EINVAL; return s->capture_group; } @@ -1762,18 +626,23 @@ int snd_mixer_selem_get_capture_group(snd_mixer_elem_t *elem) */ int snd_mixer_selem_get_capture_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if ((unsigned int) channel >= s->str[CAPT].channels) - return -EINVAL; - if (! (s->caps & (CAP_GVOLUME|CAP_CVOLUME))) - return -EINVAL; - if (s->caps & CAP_CVOLUME_JOIN) - channel = 0; - *value = s->str[CAPT].vol[channel]; - return 0; + return sm_selem_ops(elem)->get_volume(elem, SM_CAPT, channel, value); +} + +/** + * \brief Return value of capture volume in dB control of a mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param value pointer to returned value (dB * 100) + * \return 0 on success otherwise a negative error code + */ +int snd_mixer_selem_get_capture_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value) +{ + assert(elem); + assert(elem->type == SND_MIXER_ELEM_SIMPLE); + return sm_selem_ops(elem)->get_dB(elem, SM_CAPT, channel, value); } /** @@ -1785,18 +654,9 @@ int snd_mixer_selem_get_capture_volume(snd_mixer_elem_t *elem, snd_mixer_selem_c */ int snd_mixer_selem_get_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int *value) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if ((unsigned int) channel >= s->str[CAPT].channels) - return -EINVAL; - if (! (s->caps & (CAP_GSWITCH | CAP_CSWITCH))) - return -EINVAL; - if (s->caps & CAP_CSWITCH_JOIN) - channel = 0; - *value = !!(s->str[CAPT].sw & (1 << channel)); - return 0; + return sm_selem_ops(elem)->get_switch(elem, SM_CAPT, channel, value); } /** @@ -1808,19 +668,23 @@ int snd_mixer_selem_get_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_c */ int snd_mixer_selem_set_capture_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value) { - int changed; - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if (! (s->caps & (CAP_GVOLUME | CAP_CVOLUME))) - return -EINVAL; - changed = _snd_mixer_selem_set_volume(elem, CAPT, channel, value); - if (changed < 0) - return changed; - if (changed) - return selem_write(elem); - return 0; + return sm_selem_ops(elem)->set_volume(elem, SM_CAPT, channel, value); +} + +/** + * \brief Set value in dB of capture volume control of a mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param value control value in dB * 100 + * \return 0 on success otherwise a negative error code + */ +int snd_mixer_selem_set_capture_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value, int dir) +{ + assert(elem); + assert(elem->type == SND_MIXER_ELEM_SIMPLE); + return sm_selem_ops(elem)->set_dB(elem, SM_CAPT, channel, value, dir); } /** @@ -1831,19 +695,22 @@ int snd_mixer_selem_set_capture_volume(snd_mixer_elem_t *elem, snd_mixer_selem_c */ int snd_mixer_selem_set_capture_volume_all(snd_mixer_elem_t *elem, long value) { - int changed; - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if (! (s->caps & (CAP_GVOLUME | CAP_CVOLUME))) - return -EINVAL; - changed = _snd_mixer_selem_set_volume_all(elem, CAPT, value); - if (changed < 0) - return changed; - if (changed) - return selem_write(elem); - return 0; + return sm_selem_ops(elem)->set_volume_all(elem, SM_CAPT, value); +} + +/** + * \brief Set value in dB of capture volume control for all channels of a mixer simple element + * \param elem Mixer simple element handle + * \param value control value in dB * 100 + * \return 0 on success otherwise a negative error code + */ +int snd_mixer_selem_set_capture_dB_all(snd_mixer_elem_t *elem, long value, int dir) +{ + assert(elem); + assert(elem->type == SND_MIXER_ELEM_SIMPLE); + return sm_selem_ops(elem)->set_dB_all(elem, SM_CAPT, value, dir); } /** @@ -1855,19 +722,9 @@ int snd_mixer_selem_set_capture_volume_all(snd_mixer_elem_t *elem, long value) */ int snd_mixer_selem_set_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int value) { - int changed; - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if (! (s->caps & (CAP_GSWITCH | CAP_CSWITCH))) - return -EINVAL; - changed = _snd_mixer_selem_set_switch(elem, CAPT, channel, value); - if (changed < 0) - return changed; - if (changed) - return selem_write(elem); - return 0; + return sm_selem_ops(elem)->set_switch(elem, SM_CAPT, channel, value); } /** @@ -1878,19 +735,9 @@ int snd_mixer_selem_set_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_c */ int snd_mixer_selem_set_capture_switch_all(snd_mixer_elem_t *elem, int value) { - int changed; - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if (! (s->caps & (CAP_GSWITCH | CAP_CSWITCH))) - return -EINVAL; - changed = _snd_mixer_selem_set_switch_all(elem, CAPT, value); - if (changed < 0) - return changed; - if (changed) - return selem_write(elem); - return 0; + return sm_selem_ops(elem)->set_switch_all(elem, SM_CAPT, value); } /** @@ -1900,11 +747,9 @@ int snd_mixer_selem_set_capture_switch_all(snd_mixer_elem_t *elem, int value) */ int snd_mixer_selem_is_enumerated(snd_mixer_elem_t *elem) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - return s->ctls[CTL_ENUMLIST].elem != 0; + return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_ENUMERATED, 0); } /** @@ -1914,13 +759,9 @@ int snd_mixer_selem_is_enumerated(snd_mixer_elem_t *elem) */ int snd_mixer_selem_get_enum_items(snd_mixer_elem_t *elem) { - selem_t *s; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if (! s->ctls[CTL_ENUMLIST].elem) - return -EINVAL; - return s->ctls[CTL_ENUMLIST].max; + return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_ENUMCNT, 0); } /** @@ -1935,22 +776,9 @@ int snd_mixer_selem_get_enum_item_name(snd_mixer_elem_t *elem, unsigned int item, size_t maxlen, char *buf) { - selem_t *s; - snd_ctl_elem_info_t *info; - snd_hctl_elem_t *helem; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - helem = s->ctls[CTL_ENUMLIST].elem; - assert(helem); - if (item >= (unsigned int)s->ctls[CTL_ENUMLIST].max) - return -EINVAL; - snd_ctl_elem_info_alloca(&info); - snd_hctl_elem_info(helem, info); - snd_ctl_elem_info_set_item(info, item); - snd_hctl_elem_info(helem, info); - strncpy(buf, snd_ctl_elem_info_get_item_name(info), maxlen); - return 0; + return sm_selem_ops(elem)->enum_item_name(elem, item, maxlen, buf); } /** @@ -1964,22 +792,9 @@ int snd_mixer_selem_get_enum_item(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, unsigned int *itemp) { - selem_t *s; - snd_ctl_elem_value_t ctl; - snd_hctl_elem_t *helem; - int err; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if ((unsigned int) channel >= s->str[0].channels) - return -EINVAL; - helem = s->ctls[CTL_ENUMLIST].elem; - assert(helem); - memset(&ctl, 0, sizeof(ctl)); - err = snd_hctl_elem_read(helem, &ctl); - if (! err) - *itemp = ctl.value.enumerated.item[channel]; - return err; + return sm_selem_ops(elem)->get_enum_item(elem, channel, itemp); } /** @@ -1993,23 +808,9 @@ int snd_mixer_selem_set_enum_item(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, unsigned int item) { - selem_t *s; - snd_ctl_elem_value_t ctl; - snd_hctl_elem_t *helem; - int err; assert(elem); assert(elem->type == SND_MIXER_ELEM_SIMPLE); - s = elem->private_data; - if ((unsigned int) channel >= s->str[0].channels) - return -EINVAL; - helem = s->ctls[CTL_ENUMLIST].elem; - assert(helem); - if (item >= (unsigned int)s->ctls[CTL_ENUMLIST].max) - return -EINVAL; - memset(&ctl, 0, sizeof(ctl)); - err = snd_hctl_elem_read(helem, &ctl); - ctl.value.enumerated.item[channel] = item; - return snd_hctl_elem_write(helem, &ctl); + return sm_selem_ops(elem)->set_enum_item(elem, channel, item); } /** @@ -2037,7 +838,7 @@ int snd_mixer_selem_id_malloc(snd_mixer_selem_id_t **ptr) /** * \brief frees a previously allocated #snd_mixer_selem_id_t - * \param obj pointer to object to free + * \param pointer to object to free */ void snd_mixer_selem_id_free(snd_mixer_selem_id_t *obj) { @@ -2086,6 +887,7 @@ void snd_mixer_selem_id_set_name(snd_mixer_selem_id_t *obj, const char *val) { assert(obj); strncpy(obj->name, val, sizeof(obj->name)); + obj->name[sizeof(obj->name)-1] = '\0'; } /** @@ -2098,4 +900,3 @@ void snd_mixer_selem_id_set_index(snd_mixer_selem_id_t *obj, unsigned int val) assert(obj); obj->index = val; } - diff --git a/src/mixer/simple_abst.c b/src/mixer/simple_abst.c new file mode 100644 index 00000000..ab43c4ff --- /dev/null +++ b/src/mixer/simple_abst.c @@ -0,0 +1,77 @@ +/** + * \file mixer/simple_abst.c + * \brief Mixer Simple Element Class Interface - Module Abstraction + * \author Jaroslav Kysela + * \date 2005 + * + * Mixer simple element class interface. + */ +/* + * Mixer Interface - simple controls - abstraction module + * Copyright (c) 2005 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "mixer_local.h" +#include "mixer_simple.h" + +/** + * \brief Register mixer simple element class - basic abstraction + * \param mixer Mixer handle + * \param options Options container + * \param classp Pointer to returned mixer simple element class handle (or NULL + * \return 0 on success otherwise a negative error code + */ +int snd_mixer_simple_basic_register(snd_mixer_t *mixer, + struct snd_mixer_selem_regopt *options, + snd_mixer_class_t **classp) +{ + snd_mixer_class_t *class = calloc(1, sizeof(*class)); + const char *file; + snd_input_t *input; + int err; + + if (snd_mixer_class_malloc(&class)) + return -ENOMEM; + //snd_mixer_class_set_event(class, simple_event); + snd_mixer_class_set_compare(class, snd_mixer_selem_compare); + file = getenv("ALSA_MIXER_SIMPLE"); + if (!file) + file = DATADIR "/alsa/smixer.conf"; + if ((err = snd_input_stdio_open(&input, file, "r")) < 0) { + SNDERR("unable to open simple mixer configuration file '%s'", file); + goto __error; + } + err = snd_mixer_class_register(class, mixer); + if (err < 0) { + __error: + if (class) + snd_mixer_class_free(class); + return err; + } + if (classp) + *classp = class; + return 0; +} diff --git a/src/mixer/simple_none.c b/src/mixer/simple_none.c new file mode 100644 index 00000000..37b6ea57 --- /dev/null +++ b/src/mixer/simple_none.c @@ -0,0 +1,1491 @@ +/** + * \file mixer/simple.c + * \brief Mixer Simple Element Class Interface + * \author Jaroslav Kysela + * \author Abramo Bagnara + * \date 2001-2004 + * + * Mixer simple element class interface. + */ +/* + * Mixer Interface - simple controls + * Copyright (c) 2000,2004 by Jaroslav Kysela + * Copyright (c) 2001 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mixer_simple.h" + +#ifndef DOC_HIDDEN + +#define MIXER_COMPARE_WEIGHT_SIMPLE_BASE 0 +#define MIXER_COMPARE_WEIGHT_NEXT_BASE 10000000 +#define MIXER_COMPARE_WEIGHT_NOT_FOUND 1000000000 + +typedef enum _selem_ctl_type { + CTL_SINGLE, + CTL_ENUMLIST, + CTL_GLOBAL_SWITCH, + CTL_GLOBAL_VOLUME, + CTL_GLOBAL_ROUTE, + CTL_PLAYBACK_SWITCH, + CTL_PLAYBACK_VOLUME, + CTL_PLAYBACK_ROUTE, + CTL_CAPTURE_SWITCH, + CTL_CAPTURE_VOLUME, + CTL_CAPTURE_ROUTE, + CTL_CAPTURE_SOURCE, + CTL_LAST = CTL_CAPTURE_SOURCE, +} selem_ctl_type_t; + +typedef struct _selem_ctl { + snd_hctl_elem_t *elem; + snd_ctl_elem_type_t type; + unsigned int inactive: 1; + unsigned int values; + long min, max; +} selem_ctl_t; + +typedef struct _selem_none { + sm_selem_t selem; + selem_ctl_t ctls[CTL_LAST + 1]; + unsigned int capture_item; + struct { + unsigned int range: 1; /* Forced range */ + long min, max; + unsigned int channels; + long vol[32]; + unsigned int sw; + } str[2]; +} selem_none_t; + +static struct mixer_name_table { + const char *longname; + const char *shortname; +} name_table[] = { + {"Tone Control - Switch", "Tone"}, + {"Tone Control - Bass", "Bass"}, + {"Tone Control - Treble", "Treble"}, + {"Synth Tone Control - Switch", "Synth Tone"}, + {"Synth Tone Control - Bass", "Synth Bass"}, + {"Synth Tone Control - Treble", "Synth Treble"}, + {0, 0}, +}; + +#endif /* !DOC_HIDDEN */ + +static const char *get_short_name(const char *lname) +{ + struct mixer_name_table *p; + for (p = name_table; p->longname; p++) { + if (!strcmp(lname, p->longname)) + return p->shortname; + } + return lname; +} + +static int compare_mixer_priority_lookup(const char **name, const char * const *names, int coef) +{ + int res; + + for (res = 0; *names; names++, res += coef) { + if (!strncmp(*name, *names, strlen(*names))) { + *name += strlen(*names); + if (**name == ' ') + (*name)++; + return res+1; + } + } + return MIXER_COMPARE_WEIGHT_NOT_FOUND; +} + +static int get_compare_weight(const char *name, unsigned int idx) +{ + static const char *names[] = { + "Master", + "Headphone", + "Tone", + "Bass", + "Treble", + "3D Control", + "PCM", + "Front", + "Surround", + "Center", + "LFE", + "Side", + "Synth", + "FM", + "Wave", + "Music", + "DSP", + "Line", + "CD", + "Mic", + "Video", + "Zoom Video", + "Phone", + "I2S", + "IEC958", + "PC Speaker", + "Aux", + "Mono", + "Playback", + "Capture", + "Mix", + NULL + }; + static const char *names1[] = { + "-", + NULL, + }; + static const char *names2[] = { + "Mono", + "Digital", + "Switch", + "Depth", + "Wide", + "Space", + "Level", + "Center", + "Output", + "Boost", + "Tone", + "Bass", + "Treble", + NULL, + }; + const char *name1; + int res, res1; + + if ((res = compare_mixer_priority_lookup((const char **)&name, names, 1000)) == MIXER_COMPARE_WEIGHT_NOT_FOUND) + return MIXER_COMPARE_WEIGHT_NOT_FOUND; + if (*name == '\0') + goto __res; + for (name1 = name; *name1 != '\0'; name1++); + for (name1--; name1 != name && *name1 != ' '; name1--); + while (name1 != name && *name1 == ' ') + name1--; + if (name1 != name) { + for (; name1 != name && *name1 != ' '; name1--); + name = name1; + if ((res1 = compare_mixer_priority_lookup((const char **)&name, names1, 200)) == MIXER_COMPARE_WEIGHT_NOT_FOUND) + return res; + res += res1; + } else { + name = name1; + } + if ((res1 = compare_mixer_priority_lookup((const char **)&name, names2, 20)) == MIXER_COMPARE_WEIGHT_NOT_FOUND) + return res; + __res: + return MIXER_COMPARE_WEIGHT_SIMPLE_BASE + res + idx; +} + +static long to_user(selem_none_t *s, int dir, selem_ctl_t *c, long value) +{ + int64_t n; + if (c->max == c->min) + return s->str[dir].min; + n = (int64_t) (value - c->min) * (s->str[dir].max - s->str[dir].min); + return s->str[dir].min + (n + (c->max - c->min) / 2) / (c->max - c->min); +} + +static long from_user(selem_none_t *s, int dir, selem_ctl_t *c, long value) +{ + int64_t n; + if (s->str[dir].max == s->str[dir].min) + return c->min; + n = (int64_t) (value - s->str[dir].min) * (c->max - c->min); + return c->min + (n + (s->str[dir].max - s->str[dir].min) / 2) / (s->str[dir].max - s->str[dir].min); +} + +static int elem_read_volume(selem_none_t *s, int dir, selem_ctl_type_t type) +{ + snd_ctl_elem_value_t *ctl; + unsigned int idx; + int err; + selem_ctl_t *c = &s->ctls[type]; + snd_ctl_elem_value_alloca(&ctl); + if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) + return err; + for (idx = 0; idx < s->str[dir].channels; idx++) { + unsigned int idx1 = idx; + if (idx >= c->values) + idx1 = 0; + s->str[dir].vol[idx] = to_user(s, dir, c, snd_ctl_elem_value_get_integer(ctl, idx1)); + } + return 0; +} + +static int elem_read_switch(selem_none_t *s, int dir, selem_ctl_type_t type) +{ + snd_ctl_elem_value_t *ctl; + unsigned int idx; + int err; + selem_ctl_t *c = &s->ctls[type]; + snd_ctl_elem_value_alloca(&ctl); + if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) + return err; + for (idx = 0; idx < s->str[dir].channels; idx++) { + unsigned int idx1 = idx; + if (idx >= c->values) + idx1 = 0; + if (!snd_ctl_elem_value_get_integer(ctl, idx1)) + s->str[dir].sw &= ~(1 << idx); + } + return 0; +} + +static int elem_read_route(selem_none_t *s, int dir, selem_ctl_type_t type) +{ + snd_ctl_elem_value_t *ctl; + unsigned int idx; + int err; + selem_ctl_t *c = &s->ctls[type]; + snd_ctl_elem_value_alloca(&ctl); + if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) + return err; + for (idx = 0; idx < s->str[dir].channels; idx++) { + unsigned int idx1 = idx; + if (idx >= c->values) + idx1 = 0; + if (!snd_ctl_elem_value_get_integer(ctl, idx1 * c->values + idx1)) + s->str[dir].sw &= ~(1 << idx); + } + return 0; +} + +static int elem_read_enum(selem_none_t *s) +{ + snd_ctl_elem_value_t *ctl; + unsigned int idx; + int err; + selem_ctl_t *c = &s->ctls[CTL_ENUMLIST]; + snd_ctl_elem_value_alloca(&ctl); + if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) + return err; + for (idx = 0; idx < s->str[0].channels; idx++) { + unsigned int idx1 = idx; + if (idx >= c->values) + idx1 = 0; + s->str[0].vol[idx] = snd_ctl_elem_value_get_enumerated(ctl, idx1); + } + return 0; +} + +static int selem_read(snd_mixer_elem_t *elem) +{ + selem_none_t *s; + unsigned int idx; + int err = 0; + long pvol[32], cvol[32]; + unsigned int psw, csw; + + assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE); + s = snd_mixer_elem_get_private(elem); + + memcpy(pvol, s->str[SM_PLAY].vol, sizeof(pvol)); + memset(&s->str[SM_PLAY].vol, 0, sizeof(s->str[SM_PLAY].vol)); + psw = s->str[SM_PLAY].sw; + s->str[SM_PLAY].sw = ~0U; + memcpy(cvol, s->str[SM_CAPT].vol, sizeof(cvol)); + memset(&s->str[SM_CAPT].vol, 0, sizeof(s->str[SM_CAPT].vol)); + csw = s->str[SM_CAPT].sw; + s->str[SM_CAPT].sw = ~0U; + + if (s->ctls[CTL_ENUMLIST].elem) { + err = elem_read_enum(s); + if (err < 0) + return err; + goto __skip_cswitch; + } + + if (s->ctls[CTL_PLAYBACK_VOLUME].elem) + err = elem_read_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME); + else if (s->ctls[CTL_GLOBAL_VOLUME].elem) + err = elem_read_volume(s, SM_PLAY, CTL_GLOBAL_VOLUME); + else if (s->ctls[CTL_SINGLE].elem && + s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER) + err = elem_read_volume(s, SM_PLAY, CTL_SINGLE); + if (err < 0) + return err; + + if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)) == 0) { + s->str[SM_PLAY].sw = 0; + goto __skip_pswitch; + } + if (s->ctls[CTL_PLAYBACK_SWITCH].elem) { + err = elem_read_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH); + if (err < 0) + return err; + } + if (s->ctls[CTL_GLOBAL_SWITCH].elem) { + err = elem_read_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH); + if (err < 0) + return err; + } + if (s->ctls[CTL_SINGLE].elem && + s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) { + err = elem_read_switch(s, SM_PLAY, CTL_SINGLE); + if (err < 0) + return err; + } + if (s->ctls[CTL_PLAYBACK_ROUTE].elem) { + err = elem_read_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE); + if (err < 0) + return err; + } + if (s->ctls[CTL_GLOBAL_ROUTE].elem) { + err = elem_read_route(s, SM_PLAY, CTL_GLOBAL_ROUTE); + if (err < 0) + return err; + } + __skip_pswitch: + + if (s->ctls[CTL_CAPTURE_VOLUME].elem) + err = elem_read_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME); + else if (s->ctls[CTL_GLOBAL_VOLUME].elem) + err = elem_read_volume(s, SM_CAPT, CTL_GLOBAL_VOLUME); + else if (s->ctls[CTL_SINGLE].elem && + s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER) + err = elem_read_volume(s, SM_CAPT, CTL_SINGLE); + if (err < 0) + return err; + + if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)) == 0) { + s->str[SM_CAPT].sw = 0; + goto __skip_cswitch; + } + if (s->ctls[CTL_CAPTURE_SWITCH].elem) { + err = elem_read_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH); + if (err < 0) + return err; + } + if (s->ctls[CTL_GLOBAL_SWITCH].elem) { + err = elem_read_switch(s, SM_CAPT, CTL_GLOBAL_SWITCH); + if (err < 0) + return err; + } + if (s->ctls[CTL_SINGLE].elem && + s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) { + err = elem_read_switch(s, SM_CAPT, CTL_SINGLE); + if (err < 0) + return err; + } + if (s->ctls[CTL_CAPTURE_ROUTE].elem) { + err = elem_read_route(s, SM_CAPT, CTL_CAPTURE_ROUTE); + if (err < 0) + return err; + } + if (s->ctls[CTL_GLOBAL_ROUTE].elem) { + err = elem_read_route(s, SM_CAPT, CTL_GLOBAL_ROUTE); + if (err < 0) + return err; + } + if (s->ctls[CTL_CAPTURE_SOURCE].elem) { + snd_ctl_elem_value_t *ctl; + selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE]; + snd_ctl_elem_value_alloca(&ctl); + err = snd_hctl_elem_read(c->elem, ctl); + if (err < 0) + return err; + for (idx = 0; idx < s->str[SM_CAPT].channels; idx++) { + unsigned int idx1 = idx; + if (idx >= c->values) + idx1 = 0; + if (snd_ctl_elem_value_get_enumerated(ctl, idx1) != s->capture_item) + s->str[SM_CAPT].sw &= ~(1 << idx); + } + } + __skip_cswitch: + + if (memcmp(pvol, s->str[SM_PLAY].vol, sizeof(pvol)) || + psw != s->str[SM_PLAY].sw || + memcmp(cvol, s->str[SM_CAPT].vol, sizeof(cvol)) || + csw != s->str[SM_CAPT].sw) + return 1; + return 0; +} + +static int elem_write_volume(selem_none_t *s, int dir, selem_ctl_type_t type) +{ + snd_ctl_elem_value_t *ctl; + unsigned int idx; + int err; + selem_ctl_t *c = &s->ctls[type]; + snd_ctl_elem_value_alloca(&ctl); + if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) + return err; + for (idx = 0; idx < c->values; idx++) + snd_ctl_elem_value_set_integer(ctl, idx, from_user(s, dir, c, s->str[dir].vol[idx])); + if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0) + return err; + return 0; +} + +static int elem_write_switch(selem_none_t *s, int dir, selem_ctl_type_t type) +{ + snd_ctl_elem_value_t *ctl; + unsigned int idx; + int err; + selem_ctl_t *c = &s->ctls[type]; + snd_ctl_elem_value_alloca(&ctl); + if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) + return err; + for (idx = 0; idx < c->values; idx++) + snd_ctl_elem_value_set_integer(ctl, idx, !!(s->str[dir].sw & (1 << idx))); + if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0) + return err; + return 0; +} + +static int elem_write_switch_constant(selem_none_t *s, selem_ctl_type_t type, int val) +{ + snd_ctl_elem_value_t *ctl; + unsigned int idx; + int err; + selem_ctl_t *c = &s->ctls[type]; + snd_ctl_elem_value_alloca(&ctl); + if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) + return err; + for (idx = 0; idx < c->values; idx++) + snd_ctl_elem_value_set_integer(ctl, idx, !!val); + if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0) + return err; + return 0; +} + +static int elem_write_route(selem_none_t *s, int dir, selem_ctl_type_t type) +{ + snd_ctl_elem_value_t *ctl; + unsigned int idx; + int err; + selem_ctl_t *c = &s->ctls[type]; + snd_ctl_elem_value_alloca(&ctl); + if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) + return err; + for (idx = 0; idx < c->values * c->values; idx++) + snd_ctl_elem_value_set_integer(ctl, idx, 0); + for (idx = 0; idx < c->values; idx++) + snd_ctl_elem_value_set_integer(ctl, idx * c->values + idx, !!(s->str[dir].sw & (1 << idx))); + if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0) + return err; + return 0; +} + +static int elem_write_enum(selem_none_t *s) +{ + snd_ctl_elem_value_t *ctl; + unsigned int idx; + int err; + selem_ctl_t *c = &s->ctls[CTL_ENUMLIST]; + snd_ctl_elem_value_alloca(&ctl); + if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) + return err; + for (idx = 0; idx < c->values; idx++) + snd_ctl_elem_value_set_enumerated(ctl, idx, (unsigned int)s->str[0].vol[idx]); + if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0) + return err; + return 0; +} + +static int selem_write(snd_mixer_elem_t *elem) +{ + selem_none_t *s; + unsigned int idx; + int err; + + assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE); + s = snd_mixer_elem_get_private(elem); + + if (s->ctls[CTL_ENUMLIST].elem) + return elem_write_enum(s); + + if (s->ctls[CTL_SINGLE].elem) { + if (s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER) + err = elem_write_volume(s, SM_PLAY, CTL_SINGLE); + else + err = elem_write_switch(s, SM_PLAY, CTL_SINGLE); + if (err < 0) + return err; + } + if (s->ctls[CTL_GLOBAL_VOLUME].elem) { + err = elem_write_volume(s, SM_PLAY, CTL_GLOBAL_VOLUME); + if (err < 0) + return err; + } + if (s->ctls[CTL_GLOBAL_SWITCH].elem) { + if (s->ctls[CTL_PLAYBACK_SWITCH].elem && s->ctls[CTL_CAPTURE_SWITCH].elem) + err = elem_write_switch_constant(s, CTL_GLOBAL_SWITCH, 1); + else + err = elem_write_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH); + if (err < 0) + return err; + } + if (s->ctls[CTL_PLAYBACK_VOLUME].elem) { + err = elem_write_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME); + if (err < 0) + return err; + } + if (s->ctls[CTL_PLAYBACK_SWITCH].elem) { + err = elem_write_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH); + if (err < 0) + return err; + } + if (s->ctls[CTL_PLAYBACK_ROUTE].elem) { + err = elem_write_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE); + if (err < 0) + return err; + } + if (s->ctls[CTL_CAPTURE_VOLUME].elem) { + err = elem_write_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME); + if (err < 0) + return err; + } + if (s->ctls[CTL_CAPTURE_SWITCH].elem) { + err = elem_write_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH); + if (err < 0) + return err; + } + if (s->ctls[CTL_CAPTURE_ROUTE].elem) { + err = elem_write_route(s, SM_CAPT, CTL_CAPTURE_ROUTE); + if (err < 0) + return err; + } + if (s->ctls[CTL_CAPTURE_SOURCE].elem) { + snd_ctl_elem_value_t *ctl; + selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE]; + snd_ctl_elem_value_alloca(&ctl); + if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) + return err; + for (idx = 0; idx < c->values; idx++) { + if (s->str[SM_CAPT].sw & (1 << idx)) + snd_ctl_elem_value_set_enumerated(ctl, idx, s->capture_item); + } + if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0) + return err; + /* update the element, don't remove */ + err = selem_read(elem); + if (err < 0) + return err; + } + return 0; +} + +static void selem_free(snd_mixer_elem_t *elem) +{ + assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE); + free(snd_mixer_elem_get_private(elem)); +} + +static int simple_update(snd_mixer_elem_t *melem) +{ + selem_none_t *simple; + unsigned int caps, pchannels, cchannels; + long pmin, pmax, cmin, cmax; + selem_ctl_t *ctl; + + caps = 0; + pchannels = 0; + pmin = pmax = 0; + cchannels = 0; + cmin = cmax = 0; + assert(snd_mixer_elem_get_type(melem) == SND_MIXER_ELEM_SIMPLE); + simple = snd_mixer_elem_get_private(melem); + ctl = &simple->ctls[CTL_SINGLE]; + if (ctl->elem) { + pchannels = cchannels = ctl->values; + if (ctl->type == SND_CTL_ELEM_TYPE_INTEGER) { + caps |= SM_CAP_GVOLUME; + pmin = cmin = ctl->min; + pmax = cmax = ctl->max; + } else + caps |= SM_CAP_GSWITCH; + } + ctl = &simple->ctls[CTL_GLOBAL_SWITCH]; + if (ctl->elem) { + if (pchannels < ctl->values) + pchannels = ctl->values; + if (cchannels < ctl->values) + cchannels = ctl->values; + caps |= SM_CAP_GSWITCH; + } + ctl = &simple->ctls[CTL_GLOBAL_ROUTE]; + if (ctl->elem) { + if (pchannels < ctl->values) + pchannels = ctl->values; + if (cchannels < ctl->values) + cchannels = ctl->values; + caps |= SM_CAP_GSWITCH; + } + ctl = &simple->ctls[CTL_GLOBAL_VOLUME]; + if (ctl->elem) { + if (pchannels < ctl->values) + pchannels = ctl->values; + if (pmin > ctl->min) + pmin = ctl->min; + if (pmax < ctl->max) + pmax = ctl->max; + if (cchannels < ctl->values) + cchannels = ctl->values; + if (cmin > ctl->min) + cmin = ctl->min; + if (cmax < ctl->max) + cmax = ctl->max; + caps |= SM_CAP_GVOLUME; + } + ctl = &simple->ctls[CTL_PLAYBACK_SWITCH]; + if (ctl->elem) { + if (pchannels < ctl->values) + pchannels = ctl->values; + caps |= SM_CAP_PSWITCH; + caps &= ~SM_CAP_GSWITCH; + } + ctl = &simple->ctls[CTL_PLAYBACK_ROUTE]; + if (ctl->elem) { + if (pchannels < ctl->values) + pchannels = ctl->values; + caps |= SM_CAP_PSWITCH; + caps &= ~SM_CAP_GSWITCH; + } + ctl = &simple->ctls[CTL_CAPTURE_SWITCH]; + if (ctl->elem) { + if (cchannels < ctl->values) + cchannels = ctl->values; + caps |= SM_CAP_CSWITCH; + caps &= ~SM_CAP_GSWITCH; + } + ctl = &simple->ctls[CTL_CAPTURE_ROUTE]; + if (ctl->elem) { + if (cchannels < ctl->values) + cchannels = ctl->values; + caps |= SM_CAP_CSWITCH; + caps &= ~SM_CAP_GSWITCH; + } + ctl = &simple->ctls[CTL_PLAYBACK_VOLUME]; + if (ctl->elem) { + if (pchannels < ctl->values) + pchannels = ctl->values; + if (pmin > ctl->min) + pmin = ctl->min; + if (pmax < ctl->max) + pmax = ctl->max; + caps |= SM_CAP_PVOLUME; + caps &= ~SM_CAP_GVOLUME; + } + ctl = &simple->ctls[CTL_CAPTURE_VOLUME]; + if (ctl->elem) { + if (cchannels < ctl->values) + cchannels = ctl->values; + if (cmin > ctl->min) + cmin = ctl->min; + if (cmax < ctl->max) + cmax = ctl->max; + caps |= SM_CAP_CVOLUME; + caps &= ~SM_CAP_GVOLUME; + } + ctl = &simple->ctls[CTL_CAPTURE_SOURCE]; + if (ctl->elem) { + if (cchannels < ctl->values) + cchannels = ctl->values; + caps |= SM_CAP_CSWITCH | SM_CAP_CSWITCH_EXCL; + caps &= ~SM_CAP_GSWITCH; + } + ctl = &simple->ctls[CTL_ENUMLIST]; + if (ctl->elem) { + if (pchannels < ctl->values) + pchannels = ctl->values; + caps |= SM_CAP_ENUM; + } + if (pchannels > 32) + pchannels = 32; + if (cchannels > 32) + cchannels = 32; + if (caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)) + caps |= SM_CAP_PSWITCH_JOIN; + if (caps & (SM_CAP_GVOLUME|SM_CAP_PVOLUME)) + caps |= SM_CAP_PVOLUME_JOIN; + if (caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)) + caps |= SM_CAP_CSWITCH_JOIN; + if (caps & (SM_CAP_GVOLUME|SM_CAP_CVOLUME)) + caps |= SM_CAP_PVOLUME_JOIN; + if (pchannels > 1 || cchannels > 1) { + if (simple->ctls[CTL_SINGLE].elem && + simple->ctls[CTL_SINGLE].values > 1) { + if (caps & SM_CAP_GSWITCH) + caps &= ~SM_CAP_PSWITCH_JOIN; + else + caps &= ~SM_CAP_PVOLUME_JOIN; + } + if (simple->ctls[CTL_GLOBAL_ROUTE].elem || + (simple->ctls[CTL_GLOBAL_SWITCH].elem && + simple->ctls[CTL_GLOBAL_SWITCH].values > 1)) { + caps &= ~(SM_CAP_PSWITCH_JOIN|SM_CAP_CSWITCH_JOIN); + } + if (simple->ctls[CTL_GLOBAL_VOLUME].elem && + simple->ctls[CTL_GLOBAL_VOLUME].values > 1) { + caps &= ~(SM_CAP_PVOLUME_JOIN|SM_CAP_CVOLUME_JOIN); + } + } + if (pchannels > 1) { + if (simple->ctls[CTL_PLAYBACK_ROUTE].elem || + (simple->ctls[CTL_PLAYBACK_SWITCH].elem && + simple->ctls[CTL_PLAYBACK_SWITCH].values > 1)) { + caps &= ~SM_CAP_PSWITCH_JOIN; + } + if (simple->ctls[CTL_PLAYBACK_VOLUME].elem && + simple->ctls[CTL_PLAYBACK_VOLUME].values > 1) { + caps &= ~SM_CAP_PVOLUME_JOIN; + } + } + if (cchannels > 1) { + if (simple->ctls[CTL_CAPTURE_ROUTE].elem || + (simple->ctls[CTL_CAPTURE_SWITCH].elem && + simple->ctls[CTL_CAPTURE_SWITCH].values > 1)) { + caps &= ~SM_CAP_CSWITCH_JOIN; + } + if (simple->ctls[CTL_CAPTURE_VOLUME].elem && + simple->ctls[CTL_CAPTURE_VOLUME].values > 1) { + caps &= ~SM_CAP_CVOLUME_JOIN; + } + } + + /* exceptions */ + if ((caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH|SM_CAP_CSWITCH)) && + (caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == (caps & SM_CAP_GSWITCH)) { + caps &= ~(SM_CAP_GSWITCH|SM_CAP_CSWITCH_JOIN|SM_CAP_CSWITCH_EXCL); + caps |= SM_CAP_PSWITCH; + } + + simple->selem.caps = caps; + simple->str[SM_PLAY].channels = pchannels; + if (!simple->str[SM_PLAY].range) { + simple->str[SM_PLAY].min = pmin; + simple->str[SM_PLAY].max = pmax; + } + simple->str[SM_CAPT].channels = cchannels; + if (!simple->str[SM_CAPT].range) { + simple->str[SM_CAPT].min = cmin; + simple->str[SM_CAPT].max = cmax; + } + return 0; +} + +#ifndef DOC_HIDDEN +static struct suf { + const char *suffix; + selem_ctl_type_t type; +} suffixes[] = { + {" Playback Switch", CTL_PLAYBACK_SWITCH}, + {" Playback Route", CTL_PLAYBACK_ROUTE}, + {" Playback Volume", CTL_PLAYBACK_VOLUME}, + {" Capture Switch", CTL_CAPTURE_SWITCH}, + {" Capture Route", CTL_CAPTURE_ROUTE}, + {" Capture Volume", CTL_CAPTURE_VOLUME}, + {" Switch", CTL_GLOBAL_SWITCH}, + {" Route", CTL_GLOBAL_ROUTE}, + {" Volume", CTL_GLOBAL_VOLUME}, + {NULL, 0} +}; +#endif + +/* Return base length or 0 on failure */ +static int base_len(const char *name, selem_ctl_type_t *type) +{ + struct suf *p; + size_t nlen = strlen(name); + p = suffixes; + while (p->suffix) { + size_t slen = strlen(p->suffix); + size_t l; + if (nlen > slen) { + l = nlen - slen; + if (strncmp(name + l, p->suffix, slen) == 0 && + (l < 1 || name[l-1] != '-')) { /* 3D Control - Switch */ + *type = p->type; + return l; + } + } + p++; + } + return 0; +} + + +/* + * Simple Mixer Operations + */ + +static int _snd_mixer_selem_set_volume(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + if ((unsigned int) channel >= s->str[dir].channels) + return 0; + if (value < s->str[dir].min || value > s->str[dir].max) + return 0; + if (s->selem.caps & + (dir == SM_PLAY ? SM_CAP_PVOLUME_JOIN : SM_CAP_CVOLUME_JOIN)) + channel = 0; + if (value != s->str[dir].vol[channel]) { + s->str[dir].vol[channel] = value; + return 1; + } + return 0; +} + +static int _snd_mixer_selem_set_volume_all(snd_mixer_elem_t *elem, int dir, long value) +{ + int changed = 0; + snd_mixer_selem_channel_id_t channel; + selem_none_t *s = snd_mixer_elem_get_private(elem); + if (value < s->str[dir].min || value > s->str[dir].max) + return 0; + for (channel = 0; (unsigned int) channel < s->str[dir].channels; channel++) { + if (value != s->str[dir].vol[channel]) { + s->str[dir].vol[channel] = value; + changed = 1; + } + } + return changed; +} + +static int _snd_mixer_selem_set_switch(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int value) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + if ((unsigned int) channel >= s->str[dir].channels) + return 0; + if (s->selem.caps & + (dir == SM_PLAY ? SM_CAP_PSWITCH_JOIN : SM_CAP_CSWITCH_JOIN)) + channel = 0; + if (value) { + if (!(s->str[dir].sw & (1 << channel))) { + s->str[dir].sw |= 1 << channel; + return 1; + } + } else { + if (s->str[dir].sw & (1 << channel)) { + s->str[dir].sw &= ~(1 << channel); + return 1; + } + } + return 0; +} + +static int _snd_mixer_selem_set_switch_all(snd_mixer_elem_t *elem, int dir, int value) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + if (value) { + if (s->str[dir].sw != ~0U) { + s->str[dir].sw = ~0U; + return 1; + } + } else { + if (s->str[dir].sw != 0U) { + s->str[dir].sw = 0U; + return 1; + } + } + return 0; +} + +static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + + switch (cmd) { + + case SM_OPS_IS_ACTIVE: { + selem_ctl_type_t ctl; + for (ctl = CTL_SINGLE; ctl <= CTL_LAST; ctl++) + if (s->ctls[ctl].elem != NULL && s->ctls[ctl].inactive) + return 0; + return 1; + } + + case SM_OPS_IS_MONO: + return s->str[dir].channels == 1; + + case SM_OPS_IS_CHANNEL: + return (unsigned int) val < s->str[dir].channels; + + case SM_OPS_IS_ENUMERATED: + return s->ctls[CTL_ENUMLIST].elem != 0; + + case SM_OPS_IS_ENUMCNT: + if (! s->ctls[CTL_ENUMLIST].elem) + return -EINVAL; + return s->ctls[CTL_ENUMLIST].max; + + } + + return 1; +} + +static int get_range_ops(snd_mixer_elem_t *elem, int dir, + long *min, long *max) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + *min = s->str[dir].min; + *max = s->str[dir].max; + return 0; +} + +static int get_dB_range_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED, + int dir ATTRIBUTE_UNUSED, + long *min ATTRIBUTE_UNUSED, + long *max ATTRIBUTE_UNUSED) +{ + return -ENXIO; +} + +static int set_range_ops(snd_mixer_elem_t *elem, int dir, + long min, long max) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + int err; + + s->str[dir].range = 1; + s->str[dir].min = min; + s->str[dir].max = max; + if ((err = selem_read(elem)) < 0) + return err; + return 0; +} + +static int get_volume_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, long *value) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + if ((unsigned int) channel >= s->str[dir].channels) + return -EINVAL; + if (dir == SM_PLAY) { + if (! (s->selem.caps & (SM_CAP_PVOLUME|SM_CAP_GVOLUME))) + return -EINVAL; + if (s->selem.caps & SM_CAP_PVOLUME_JOIN) + channel = 0; + } else { + if (! (s->selem.caps & (SM_CAP_CVOLUME|SM_CAP_GVOLUME))) + return -EINVAL; + if (s->selem.caps & SM_CAP_CVOLUME_JOIN) + channel = 0; + } + *value = s->str[dir].vol[channel]; + return 0; +} + +static int get_dB_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED, + int dir ATTRIBUTE_UNUSED, + snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED, + long *value ATTRIBUTE_UNUSED) +{ + return -ENXIO; +} + +static int get_switch_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, int *value) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + if ((unsigned int) channel >= s->str[dir].channels) + return -EINVAL; + if (dir == SM_PLAY) { + if (! (s->selem.caps & (SM_CAP_PSWITCH|SM_CAP_GSWITCH))) + return -EINVAL; + if (s->selem.caps & SM_CAP_PSWITCH_JOIN) + channel = 0; + } else { + if (! (s->selem.caps & (SM_CAP_CSWITCH|SM_CAP_GSWITCH))) + return -EINVAL; + if (s->selem.caps & SM_CAP_CSWITCH_JOIN) + channel = 0; + } + *value = !!(s->str[dir].sw & (1 << channel)); + return 0; +} + +static int set_volume_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, long value) +{ + int changed; + selem_none_t *s = snd_mixer_elem_get_private(elem); + if (dir == SM_PLAY) { + if (! (s->selem.caps & (SM_CAP_GVOLUME|SM_CAP_PVOLUME))) + return -EINVAL; + } else { + if (! (s->selem.caps & (SM_CAP_GVOLUME|SM_CAP_CVOLUME))) + return -EINVAL; + } + changed = _snd_mixer_selem_set_volume(elem, dir, channel, value); + if (changed < 0) + return changed; + if (changed) + return selem_write(elem); + return 0; +} + +static int set_dB_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED, + int dir ATTRIBUTE_UNUSED, + snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED, + long value ATTRIBUTE_UNUSED, + int xdir ATTRIBUTE_UNUSED) +{ + return -ENXIO; +} + +static int set_volume_all_ops(snd_mixer_elem_t *elem, int dir, long value) +{ + int changed; + selem_none_t *s = snd_mixer_elem_get_private(elem); + if (dir == SM_PLAY) { + if (! (s->selem.caps & (SM_CAP_GVOLUME|SM_CAP_PVOLUME))) + return -EINVAL; + } else { + if (! (s->selem.caps & (SM_CAP_GVOLUME|SM_CAP_CVOLUME))) + return -EINVAL; + } + changed = _snd_mixer_selem_set_volume_all(elem, dir, value); + if (changed < 0) + return changed; + if (changed) + return selem_write(elem); + return 0; +} + +static int set_dB_all_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED, + int dir ATTRIBUTE_UNUSED, + long value ATTRIBUTE_UNUSED, + int xdir ATTRIBUTE_UNUSED) +{ + return -ENXIO; +} + +static int set_switch_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, int value) +{ + int changed; + selem_none_t *s = snd_mixer_elem_get_private(elem); + if (dir == SM_PLAY) { + if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH))) + return -EINVAL; + } else { + if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH))) + return -EINVAL; + } + changed = _snd_mixer_selem_set_switch(elem, dir, channel, value); + if (changed < 0) + return changed; + if (changed) + return selem_write(elem); + return 0; +} + +static int set_switch_all_ops(snd_mixer_elem_t *elem, int dir, int value) +{ + int changed; + selem_none_t *s = snd_mixer_elem_get_private(elem); + if (dir == SM_PLAY) { + if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH))) + return -EINVAL; + } else { + if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH))) + return -EINVAL; + } + changed = _snd_mixer_selem_set_switch_all(elem, dir, value); + if (changed < 0) + return changed; + if (changed) + return selem_write(elem); + return 0; +} + +static int enum_item_name_ops(snd_mixer_elem_t *elem, + unsigned int item, + size_t maxlen, char *buf) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + snd_ctl_elem_info_t *info; + snd_hctl_elem_t *helem; + + helem = s->ctls[CTL_ENUMLIST].elem; + assert(helem); + if (item >= (unsigned int)s->ctls[CTL_ENUMLIST].max) + return -EINVAL; + snd_ctl_elem_info_alloca(&info); + snd_hctl_elem_info(helem, info); + snd_ctl_elem_info_set_item(info, item); + snd_hctl_elem_info(helem, info); + strncpy(buf, snd_ctl_elem_info_get_item_name(info), maxlen); + return 0; +} + +static int get_enum_item_ops(snd_mixer_elem_t *elem, + snd_mixer_selem_channel_id_t channel, + unsigned int *itemp) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + snd_ctl_elem_value_t *ctl; + snd_hctl_elem_t *helem; + int err; + + if ((unsigned int) channel >= s->str[0].channels) + return -EINVAL; + helem = s->ctls[CTL_ENUMLIST].elem; + assert(helem); + snd_ctl_elem_value_alloca(&ctl); + err = snd_hctl_elem_read(helem, ctl); + if (! err) + *itemp = snd_ctl_elem_value_get_enumerated(ctl, channel); + return err; +} + +static int set_enum_item_ops(snd_mixer_elem_t *elem, + snd_mixer_selem_channel_id_t channel, + unsigned int item) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + snd_ctl_elem_value_t *ctl; + snd_hctl_elem_t *helem; + int err; + + if ((unsigned int) channel >= s->str[0].channels) + return -EINVAL; + helem = s->ctls[CTL_ENUMLIST].elem; + assert(helem); + if (item >= (unsigned int)s->ctls[CTL_ENUMLIST].max) + return -EINVAL; + snd_ctl_elem_value_alloca(&ctl); + err = snd_hctl_elem_read(helem, ctl); + if (err < 0) + return err; + snd_ctl_elem_value_set_enumerated(ctl, channel, item); + return snd_hctl_elem_write(helem, ctl); +} + +static struct sm_elem_ops simple_none_ops = { + .is = is_ops, + .get_range = get_range_ops, + .get_dB_range = get_dB_range_ops, + .set_range = set_range_ops, + .get_volume = get_volume_ops, + .get_dB = get_dB_ops, + .set_volume = set_volume_ops, + .set_dB = set_dB_ops, + .set_volume_all = set_volume_all_ops, + .set_dB_all = set_dB_all_ops, + .get_switch = get_switch_ops, + .set_switch = set_switch_ops, + .set_switch_all = set_switch_all_ops, + .enum_item_name = enum_item_name_ops, + .get_enum_item = get_enum_item_ops, + .set_enum_item = set_enum_item_ops +}; + +static int simple_add1(snd_mixer_class_t *class, const char *name, + snd_hctl_elem_t *helem, selem_ctl_type_t type, + unsigned int value) +{ + snd_mixer_elem_t *melem; + snd_mixer_selem_id_t *id; + int new = 0; + int err; + snd_ctl_elem_info_t *info; + selem_none_t *simple; + const char *name1; + snd_ctl_elem_type_t ctype; + unsigned long values; + + snd_ctl_elem_info_alloca(&info); + err = snd_hctl_elem_info(helem, info); + if (err < 0) + return err; + ctype = snd_ctl_elem_info_get_type(info); + values = snd_ctl_elem_info_get_count(info); + switch (type) { + case CTL_SINGLE: + if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) + type = CTL_ENUMLIST; + else if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN && + ctype != SND_CTL_ELEM_TYPE_INTEGER) + return 0; + break; + case CTL_GLOBAL_ROUTE: + case CTL_PLAYBACK_ROUTE: + case CTL_CAPTURE_ROUTE: + { + unsigned int n; + if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) { + type = CTL_ENUMLIST; + break; + } + if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN) + return 0; + n = sqrt((double)values); + if (n * n != values) + return 0; + values = n; + break; + } + case CTL_GLOBAL_SWITCH: + case CTL_PLAYBACK_SWITCH: + case CTL_CAPTURE_SWITCH: + if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) { + type = CTL_ENUMLIST; + break; + } + if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN) + return 0; + break; + case CTL_GLOBAL_VOLUME: + case CTL_PLAYBACK_VOLUME: + case CTL_CAPTURE_VOLUME: + if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) { + type = CTL_ENUMLIST; + break; + } + if (ctype != SND_CTL_ELEM_TYPE_INTEGER) + return 0; + break; + case CTL_CAPTURE_SOURCE: + if (ctype != SND_CTL_ELEM_TYPE_ENUMERATED) + return 0; + break; + default: + assert(0); + break; + } + name1 = get_short_name(name); + if (snd_mixer_selem_id_malloc(&id)) + return -ENOMEM; + snd_mixer_selem_id_set_name(id, name1); + snd_mixer_selem_id_set_index(id, snd_hctl_elem_get_index(helem)); + melem = snd_mixer_find_selem(snd_mixer_class_get_mixer(class), id); + if (!melem) { + simple = calloc(1, sizeof(*simple)); + if (!simple) { + free(id); + return -ENOMEM; + } + simple->selem.id = id; + simple->selem.ops = &simple_none_ops; + err = snd_mixer_elem_new(&melem, SND_MIXER_ELEM_SIMPLE, + get_compare_weight(snd_mixer_selem_id_get_name(simple->selem.id), snd_mixer_selem_id_get_index(simple->selem.id)), + simple, selem_free); + if (err < 0) { + free(id); + free(simple); + return err; + } + new = 1; + } else { + simple = snd_mixer_elem_get_private(melem); + free(id); + } + if (simple->ctls[type].elem) { + SNDERR("helem (%s,'%s',%li,%li,%li) appears twice or more", + snd_ctl_elem_iface_name(snd_hctl_elem_get_interface(helem)), + snd_hctl_elem_get_name(helem), + snd_hctl_elem_get_index(helem), + snd_hctl_elem_get_device(helem), + snd_hctl_elem_get_subdevice(helem)); + err = -EINVAL; + goto __error; + } + simple->ctls[type].elem = helem; + simple->ctls[type].type = snd_ctl_elem_info_get_type(info); + simple->ctls[type].inactive = snd_ctl_elem_info_is_inactive(info); + simple->ctls[type].values = values; + if (type == CTL_ENUMLIST) { + simple->ctls[type].min = 0; + simple->ctls[type].max = snd_ctl_elem_info_get_items(info); + } else { + if (ctype == SND_CTL_ELEM_TYPE_INTEGER) { + simple->ctls[type].min = snd_ctl_elem_info_get_min(info); + simple->ctls[type].max = snd_ctl_elem_info_get_max(info); + } + } + switch (type) { + case CTL_CAPTURE_SOURCE: + simple->capture_item = value; + break; + default: + break; + } + err = snd_mixer_elem_attach(melem, helem); + if (err < 0) + goto __error; + err = simple_update(melem); + if (err < 0) { + if (new) + goto __error; + return err; + } + if (new) + err = snd_mixer_elem_add(melem, class); + else + err = snd_mixer_elem_info(melem); + if (err < 0) + return err; + err = selem_read(melem); + if (err < 0) + return err; + if (err) + err = snd_mixer_elem_value(melem); + return err; + __error: + if (new) + snd_mixer_elem_free(melem); + return -EINVAL; +} + +static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem) +{ + const char *name = snd_hctl_elem_get_name(helem); + size_t len; + selem_ctl_type_t type; + if (snd_hctl_elem_get_interface(helem) != SND_CTL_ELEM_IFACE_MIXER) + return 0; + if (strcmp(name, "Capture Source") == 0) { + snd_ctl_elem_info_t *info; + unsigned int k, items; + int err; + snd_ctl_elem_info_alloca(&info); + err = snd_hctl_elem_info(helem, info); + assert(err >= 0); + if (snd_ctl_elem_info_get_type(info) != SND_CTL_ELEM_TYPE_ENUMERATED) + return 0; + items = snd_ctl_elem_info_get_items(info); + for (k = 0; k < items; ++k) { + const char *n; + snd_ctl_elem_info_set_item(info, k); + err = snd_hctl_elem_info(helem, info); + if (err < 0) + return err; + n = snd_ctl_elem_info_get_item_name(info); + err = simple_add1(class, n, helem, CTL_CAPTURE_SOURCE, k); + if (err < 0) + return err; + } + return 0; + } + len = base_len(name, &type); + if (len == 0) { + return simple_add1(class, name, helem, CTL_SINGLE, 0); + } else { + char ename[128]; + if (len >= sizeof(ename)) + len = sizeof(ename) - 1; + memcpy(ename, name, len); + ename[len] = 0; + /* exception: Capture Volume and Capture Switch */ + if (type == CTL_GLOBAL_VOLUME && !strcmp(ename, "Capture")) + type = CTL_CAPTURE_VOLUME; + else if (type == CTL_GLOBAL_SWITCH && !strcmp(ename, "Capture")) + type = CTL_CAPTURE_SWITCH; + return simple_add1(class, ename, helem, type, 0); + } +} + +static int simple_event_remove(snd_hctl_elem_t *helem, + snd_mixer_elem_t *melem) +{ + selem_none_t *simple = snd_mixer_elem_get_private(melem); + int err; + int k; + for (k = 0; k <= CTL_LAST; k++) { + if (simple->ctls[k].elem == helem) + break; + } + assert(k <= CTL_LAST); + simple->ctls[k].elem = NULL; + err = snd_mixer_elem_detach(melem, helem); + if (err < 0) + return err; + if (snd_mixer_elem_empty(melem)) + return snd_mixer_elem_remove(melem); + err = simple_update(melem); + return snd_mixer_elem_info(melem); +} + +static int simple_event(snd_mixer_class_t *class, unsigned int mask, + snd_hctl_elem_t *helem, snd_mixer_elem_t *melem) +{ + int err; + if (mask == SND_CTL_EVENT_MASK_REMOVE) + return simple_event_remove(helem, melem); + if (mask & SND_CTL_EVENT_MASK_ADD) { + err = simple_event_add(class, helem); + if (err < 0) + return err; + } + if (mask & SND_CTL_EVENT_MASK_INFO) { + err = simple_event_remove(helem, melem); + if (err < 0) + return err; + err = simple_event_add(class, helem); + if (err < 0) + return err; + return 0; + } + if (mask & SND_CTL_EVENT_MASK_VALUE) { + err = selem_read(melem); + if (err < 0) + return err; + if (err) { + err = snd_mixer_elem_value(melem); + if (err < 0) + return err; + } + } + return 0; +} + +/** + * \brief Register mixer simple element class - none abstraction + * \param mixer Mixer handle + * \param options Options container + * \param classp Pointer to returned mixer simple element class handle (or NULL) + * \return 0 on success otherwise a negative error code + */ +int snd_mixer_simple_none_register(snd_mixer_t *mixer, + struct snd_mixer_selem_regopt *options ATTRIBUTE_UNUSED, + snd_mixer_class_t **classp) +{ + snd_mixer_class_t *class; + int err; + + if (snd_mixer_class_malloc(&class)) + return -ENOMEM; + snd_mixer_class_set_event(class, simple_event); + snd_mixer_class_set_compare(class, snd_mixer_selem_compare); + err = snd_mixer_class_register(class, mixer); + if (err < 0) { + if (class) + free(class); + return err; + } + if (classp) + *classp = class; + return 0; +}