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
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 */
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);
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
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
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;
};
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);
--- /dev/null
+/**
+ * \file include/mixer_abst.h
+ * \brief Mixer abstract implementation interface library for the ALSA library
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \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 */
+
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;
--- /dev/null
+usb {
+ searchl "USB"
+ lib smixer_usb.so
+}
+ac97 {
+ searchl "AC97a:"
+ lib smixer_ac97.so
+}
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
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
-#include <pthread.h>
#include "mixer_local.h"
#ifndef DOC_HIDDEN
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)
{
}
/**
+ * \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
* \param class Mixer element class
}
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)
}
/**
+ * \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
* \return 0 on success otherwise a negative error code
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;
/**
* \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)
{
/**
* \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;
+}
#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;
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);
--- /dev/null
+/*
+ * Mixer Simple Interface - local header file
+ * Copyright (c) 2005 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * 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);
* \brief Mixer Simple Element Class Interface
* \author Jaroslav Kysela <perex@suse.cz>
* \author Abramo Bagnara <abramo@alsa-project.org>
- * \date 2001
+ * \date 2001-2004
*
* Mixer simple element class interface.
*/
/*
* Mixer Interface - simple controls
- * Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) 2000,2004 by Jaroslav Kysela <perex@suse.cz>
* Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
*
*
#include <sys/ioctl.h>
#include <math.h>
#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
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;
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;
}
/**
*/
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;
}
/**
*/
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;
}
/**
*/
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);
}
/**
*/
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);
}
/**
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);
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
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);
}
/**
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
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);
}
/**
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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;
}
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
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);
}
/**
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);
}
/**
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);
}
/**
/**
* \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)
{
{
assert(obj);
strncpy(obj->name, val, sizeof(obj->name));
+ obj->name[sizeof(obj->name)-1] = '\0';
}
/**
assert(obj);
obj->index = val;
}
-
--- /dev/null
+/**
+ * \file mixer/simple_abst.c
+ * \brief Mixer Simple Element Class Interface - Module Abstraction
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \date 2005
+ *
+ * Mixer simple element class interface.
+ */
+/*
+ * Mixer Interface - simple controls - abstraction module
+ * Copyright (c) 2005 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <math.h>
+#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;
+}
--- /dev/null
+/**
+ * \file mixer/simple.c
+ * \brief Mixer Simple Element Class Interface
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \author Abramo Bagnara <abramo@alsa-project.org>
+ * \date 2001-2004
+ *
+ * Mixer simple element class interface.
+ */
+/*
+ * Mixer Interface - simple controls
+ * Copyright (c) 2000,2004 by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <math.h>
+#include <alsa/asoundlib.h>
+#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;
+}