snd_mixer_elem_t *snd_mixer_last_elem(snd_mixer_t *mixer);
int snd_mixer_handle_events(snd_mixer_t *mixer);
int snd_mixer_attach(snd_mixer_t *mixer, const char *name);
+int snd_mixer_attach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl);
int snd_mixer_detach(snd_mixer_t *mixer, const char *name);
+int snd_mixer_detach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl);
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_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);
int snd_mixer_selem_set_capture_switch_all(snd_mixer_elem_t *elem, int value);
-void snd_mixer_selem_get_playback_volume_range(snd_mixer_elem_t *elem,
- long *min, long *max);
-void snd_mixer_selem_get_playback_dB_range(snd_mixer_elem_t *elem,
- long *min, long *max);
-void snd_mixer_selem_set_playback_volume_range(snd_mixer_elem_t *elem,
- long min, long max);
-void snd_mixer_selem_get_capture_volume_range(snd_mixer_elem_t *elem,
+int snd_mixer_selem_get_playback_volume_range(snd_mixer_elem_t *elem,
long *min, long *max);
-void snd_mixer_selem_get_capture_dB_range(snd_mixer_elem_t *elem,
+int snd_mixer_selem_get_playback_dB_range(snd_mixer_elem_t *elem,
long *min, long *max);
-void snd_mixer_selem_set_capture_volume_range(snd_mixer_elem_t *elem,
+int snd_mixer_selem_set_playback_volume_range(snd_mixer_elem_t *elem,
long min, long max);
+int snd_mixer_selem_get_capture_volume_range(snd_mixer_elem_t *elem,
+ long *min, long *max);
+int snd_mixer_selem_get_capture_dB_range(snd_mixer_elem_t *elem,
+ long *min, long *max);
+int snd_mixer_selem_set_capture_volume_range(snd_mixer_elem_t *elem,
+ long min, long max);
int snd_mixer_selem_is_enumerated(snd_mixer_elem_t *elem);
int snd_mixer_selem_get_enum_items(snd_mixer_elem_t *elem);
unsigned int capture_group;
} sm_selem_t;
+typedef struct _sm_class_basic {
+ char *device;
+ snd_ctl_t *ctl;
+ snd_hctl_t *hctl;
+ snd_ctl_card_info_t *info;
+} sm_class_basic_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 (*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);
+int snd_mixer_sbasic_info(const snd_mixer_class_t *class, sm_class_basic_t *info);
+
/** \} */
#ifdef __cplusplus
snd_mixer_get_hctl;
snd_mixer_elem_get_private;
+ snd_mixer_attach_hctl;
+ snd_mixer_detach_hctl;
snd_mixer_class_register;
snd_mixer_add_elem;
snd_mixer_selem_set_playback_dB_all;
snd_mixer_selem_set_capture_dB_all;
snd_mixer_selem_compare;
+ snd_mixer_sbasic_info;
snd_ctl_ext_create;
snd_ctl_ext_delete;
/**
- * \brief Attach an HCTL to an opened mixer
+ * \brief Attach an HCTL specified with the CTL device name to an opened mixer
* \param mixer Mixer handle
* \param name HCTL name (see #snd_hctl_open)
* \return 0 on success otherwise a negative error code
*/
int snd_mixer_attach(snd_mixer_t *mixer, const char *name)
{
- snd_mixer_slave_t *slave;
snd_hctl_t *hctl;
int err;
- slave = calloc(1, sizeof(*slave));
- if (slave == NULL)
- return -ENOMEM;
+
err = snd_hctl_open(&hctl, name, 0);
+ if (err < 0)
+ return err;
+ err = snd_mixer_attach_hctl(mixer, hctl);
if (err < 0) {
- free(slave);
+ snd_hctl_close(hctl);
return err;
}
+ return 0;
+}
+
+/**
+ * \brief Attach an HCTL to an opened mixer
+ * \param mixer Mixer handle
+ * \param name HCTL name (see #snd_hctl_open)
+ * \return 0 on success otherwise a negative error code
+ */
+int snd_mixer_attach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl)
+{
+ snd_mixer_slave_t *slave;
+ int err;
+
+ assert(hctl);
+ slave = calloc(1, sizeof(*slave));
+ if (slave == NULL)
+ return -ENOMEM;
err = snd_hctl_nonblock(hctl, 1);
if (err < 0) {
snd_hctl_close(hctl);
return -ENOENT;
}
+/**
+ * \brief Detach a previously attached HCTL to an opened mixer freeing all related resources
+ * \param mixer Mixer handle
+ * \param hctl HCTL previously attached
+ * \return 0 on success otherwise a negative error code
+ *
+ * Note: The hctl handle is not closed!
+ */
+int snd_mixer_detach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl)
+{
+ struct list_head *pos;
+ list_for_each(pos, &mixer->slaves) {
+ snd_mixer_slave_t *s;
+ s = list_entry(pos, snd_mixer_slave_t, list);
+ if (hctl == s->hctl) {
+ list_del(pos);
+ free(s);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
/**
* \brief Obtain a HCTL pointer associated to given name
* \param mixer Mixer handle
}
if (options == NULL ||
(options->ver == 1 && options->abstract == SND_MIXER_SABSTRACT_NONE)) {
- return snd_mixer_simple_none_register(mixer, options, classp);
+ int err = snd_mixer_simple_none_register(mixer, options, classp);
+ if (err < 0)
+ return err;
+ if (options != NULL) {
+ err = snd_mixer_attach(mixer, options->device);
+ if (err < 0)
+ return err;
+ }
+ return 0;
} else if (options->ver == 1) {
if (options->abstract == SND_MIXER_SABSTRACT_BASIC)
return snd_mixer_simple_basic_register(mixer, options, classp);
return -ENXIO;
}
+#define CHECK_BASIC(xelem) \
+{ \
+ assert(xelem); \
+ assert((xelem)->type == SND_MIXER_ELEM_SIMPLE); \
+}
+
+#define CHECK_DIR(xelem, xwhat) \
+{ \
+ unsigned int xcaps = ((sm_selem_t *)(elem)->private_data)->caps; \
+ if (! (xcaps & (xwhat))) \
+ return -EINVAL; \
+}
+
+#define CHECK_DIR_CHN(xelem, xwhat, xjoin, xchannel) \
+{ \
+ unsigned int xcaps = ((sm_selem_t *)(elem)->private_data)->caps; \
+ if (! (xcaps & (xwhat))) \
+ return -EINVAL; \
+ if (xcaps & (xjoin)) \
+ xchannel = 0; \
+}
+
+#define CHECK_ENUM(xelem) \
+ if (!((sm_selem_t *)(elem)->private_data)->caps & SM_CAP_ENUM) \
+ return -EINVAL;
+
+#define COND_CAPS(xelem, what) \
+ !!(((sm_selem_t *)(elem)->private_data)->caps & (what))
+
#ifndef DOC_HIDDEN
int snd_mixer_selem_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2)
{
snd_mixer_selem_id_t *id)
{
sm_selem_t *s;
- assert(elem && id);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ assert(id);
+ CHECK_BASIC(elem);
s = elem->private_data;
*id = *s->id;
}
const char *snd_mixer_selem_get_name(snd_mixer_elem_t *elem)
{
sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
s = elem->private_data;
return s->id->name;
}
unsigned int snd_mixer_selem_get_index(snd_mixer_elem_t *elem)
{
sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
s = elem->private_data;
return s->id->index;
}
*/
int snd_mixer_selem_has_common_volume(snd_mixer_elem_t *elem)
{
- sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- s = elem->private_data;
- return !!(s->caps & SM_CAP_GVOLUME);
+ CHECK_BASIC(elem);
+ return COND_CAPS(elem, SM_CAP_GVOLUME);
}
/**
*/
int snd_mixer_selem_has_common_switch(snd_mixer_elem_t *elem)
{
- sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- s = elem->private_data;
- return !!(s->caps & SM_CAP_GSWITCH);
+ CHECK_BASIC(elem);
+ return COND_CAPS(elem, SM_CAP_GSWITCH);
}
/**
*/
int snd_mixer_selem_is_active(snd_mixer_elem_t *elem)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_CHANNEL, (int)channel);
}
* \param min Pointer to returned minimum
* \param max Pointer to returned maximum
*/
-void snd_mixer_selem_get_playback_volume_range(snd_mixer_elem_t *elem,
+int snd_mixer_selem_get_playback_volume_range(snd_mixer_elem_t *elem,
long *min, long *max)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- sm_selem_ops(elem)->get_range(elem, SM_PLAY, min, max);
+ CHECK_BASIC(elem);
+ CHECK_DIR(elem, SM_CAP_PVOLUME);
+ return sm_selem_ops(elem)->get_range(elem, SM_PLAY, min, max);
}
/**
* \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)
+int 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);
+ CHECK_BASIC(elem);
+ CHECK_DIR(elem, SM_CAP_PVOLUME);
+ return sm_selem_ops(elem)->get_dB_range(elem, SM_PLAY, min, max);
}
/**
* \param min minimum volume value
* \param max maximum volume value
*/
-void snd_mixer_selem_set_playback_volume_range(snd_mixer_elem_t *elem,
- long min, long max)
+int snd_mixer_selem_set_playback_volume_range(snd_mixer_elem_t *elem,
+ long min, long max)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
assert(min < max);
- sm_selem_ops(elem)->set_range(elem, SM_PLAY, min, max);
+ CHECK_DIR(elem, SM_CAP_PVOLUME);
+ return sm_selem_ops(elem)->set_range(elem, SM_PLAY, min, max);
}
/**
*/
int snd_mixer_selem_has_playback_volume(snd_mixer_elem_t *elem)
{
- sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- s = elem->private_data;
- return !!(s->caps & SM_CAP_PVOLUME) || !!(s->caps & SM_CAP_GVOLUME);
+ CHECK_BASIC(elem);
+ return COND_CAPS(elem, SM_CAP_PVOLUME);
}
/**
*/
int snd_mixer_selem_has_playback_volume_joined(snd_mixer_elem_t *elem)
{
- sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- s = elem->private_data;
- return !!(s->caps & SM_CAP_PVOLUME_JOIN);
+ CHECK_BASIC(elem);
+ return COND_CAPS(elem, SM_CAP_PVOLUME_JOIN);
}
/**
*/
int snd_mixer_selem_has_playback_switch(snd_mixer_elem_t *elem)
{
- sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- s = elem->private_data;
- return !!(s->caps & SM_CAP_PSWITCH) || !!(s->caps & SM_CAP_GSWITCH);
+ CHECK_BASIC(elem);
+ return COND_CAPS(elem, SM_CAP_PSWITCH);
}
/**
*/
int snd_mixer_selem_has_playback_switch_joined(snd_mixer_elem_t *elem)
{
- sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- s = elem->private_data;
- return !!(s->caps & SM_CAP_PSWITCH_JOIN);
+ CHECK_BASIC(elem);
+ return COND_CAPS(elem, 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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_DIR_CHN(elem, SM_CAP_PVOLUME, SM_CAP_PVOLUME_JOIN, channel);
return sm_selem_ops(elem)->get_volume(elem, SM_PLAY, channel, value);
}
*/
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);
+ unsigned int caps;
+
+ CHECK_BASIC(elem);
+ caps = ((sm_selem_t *)elem->private_data)->caps;
+ if (!(caps & SM_CAP_PVOLUME))
+ return -EINVAL;
+ if (caps & SM_CAP_PVOLUME_JOIN)
+ channel = 0;
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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_DIR_CHN(elem, SM_CAP_PSWITCH, SM_CAP_PSWITCH_JOIN, channel);
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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_DIR_CHN(elem, SM_CAP_PVOLUME, SM_CAP_PVOLUME_JOIN, channel);
return sm_selem_ops(elem)->set_volume(elem, SM_PLAY, channel, value);
}
* \param elem Mixer simple element handle
* \param channel mixer simple element channel identifier
* \param value control value in dB * 100
+ * \param dir select direction (-1 = accurate or first bellow, 0 = accurate, 1 = accurate or first above)
* \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);
+ CHECK_BASIC(elem);
+ CHECK_DIR_CHN(elem, SM_CAP_PVOLUME, SM_CAP_PVOLUME_JOIN, channel);
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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- return sm_selem_ops(elem)->set_volume_all(elem, SM_PLAY, value);
+ snd_mixer_selem_channel_id_t chn;
+ int err;
+
+ for (chn = 0; chn < 32; chn++) {
+ if (!snd_mixer_selem_has_playback_channel(elem, chn))
+ continue;
+ err = snd_mixer_selem_set_playback_volume(elem, chn, value);
+ if (err < 0)
+ return err;
+ if (chn == 0 && snd_mixer_selem_has_playback_volume_joined(elem))
+ return 0;
+ }
+ return 0;
}
/**
* \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
+ * \param dir select direction (-1 = accurate or first bellow, 0 = accurate, 1 = accurate or first above)
* \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);
+ snd_mixer_selem_channel_id_t chn;
+ int err;
+
+ for (chn = 0; chn < 32; chn++) {
+ if (!snd_mixer_selem_has_playback_channel(elem, chn))
+ continue;
+ err = snd_mixer_selem_set_playback_dB(elem, chn, value, dir);
+ if (err < 0)
+ return err;
+ if (chn == 0 && snd_mixer_selem_has_playback_volume_joined(elem))
+ return 0;
+ }
+ return 0;
}
/**
*/
int snd_mixer_selem_set_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int value)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_DIR_CHN(elem, SM_CAP_PSWITCH, SM_CAP_PSWITCH, channel);
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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- return sm_selem_ops(elem)->set_switch_all(elem, SM_PLAY, value);
+ snd_mixer_selem_channel_id_t chn;
+ int err;
+
+ CHECK_BASIC(elem);
+ for (chn = 0; chn < 32; chn++) {
+ if (!snd_mixer_selem_has_playback_channel(elem, chn))
+ continue;
+ err = snd_mixer_selem_set_playback_switch(elem, chn, value);
+ if (err < 0)
+ return err;
+ if (chn == 0 && snd_mixer_selem_has_playback_switch_joined(elem))
+ return 0;
+ }
+ return 0;
}
/**
*/
int snd_mixer_selem_is_capture_mono(snd_mixer_elem_t *elem)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_DIR(elem, SM_CAP_CVOLUME|SM_CAP_CSWITCH);
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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_DIR(elem, SM_CAP_CVOLUME|SM_CAP_CSWITCH);
return sm_selem_ops(elem)->is(elem, SM_CAPT, SM_OPS_IS_CHANNEL, channel);
}
* \param min Pointer to returned minimum
* \param max Pointer to returned maximum
*/
-void snd_mixer_selem_get_capture_volume_range(snd_mixer_elem_t *elem,
- long *min, long *max)
+int snd_mixer_selem_get_capture_volume_range(snd_mixer_elem_t *elem,
+ long *min, long *max)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- sm_selem_ops(elem)->get_range(elem, SM_CAPT, min, max);
+ CHECK_BASIC(elem);
+ CHECK_DIR(elem, SM_CAP_CVOLUME);
+ return sm_selem_ops(elem)->get_range(elem, SM_CAPT, min, max);
}
/**
* \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)
+int 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);
+ CHECK_BASIC(elem);
+ CHECK_DIR(elem, SM_CAP_CVOLUME);
+ return sm_selem_ops(elem)->get_dB_range(elem, SM_CAPT, min, max);
}
/**
* \param min minimum volume value
* \param max maximum volume value
*/
-void snd_mixer_selem_set_capture_volume_range(snd_mixer_elem_t *elem,
- long min, long max)
+int snd_mixer_selem_set_capture_volume_range(snd_mixer_elem_t *elem,
+ long min, long max)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
assert(min < max);
- sm_selem_ops(elem)->set_range(elem, SM_CAPT, min, max);
+ CHECK_DIR(elem, SM_CAP_CVOLUME);
+ return sm_selem_ops(elem)->set_range(elem, SM_CAPT, min, max);
}
/**
*/
int snd_mixer_selem_has_capture_volume(snd_mixer_elem_t *elem)
{
- sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- s = elem->private_data;
- return !!(s->caps & SM_CAP_CVOLUME);
+ CHECK_BASIC(elem);
+ return COND_CAPS(elem, SM_CAP_CVOLUME);
}
/**
*/
int snd_mixer_selem_has_capture_volume_joined(snd_mixer_elem_t *elem)
{
- sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- s = elem->private_data;
- return !!(s->caps & SM_CAP_CVOLUME_JOIN);
+ CHECK_BASIC(elem);
+ return COND_CAPS(elem, SM_CAP_CVOLUME_JOIN);
}
/**
*/
int snd_mixer_selem_has_capture_switch(snd_mixer_elem_t *elem)
{
- sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- s = elem->private_data;
- return !!(s->caps & SM_CAP_CSWITCH);
+ CHECK_BASIC(elem);
+ return COND_CAPS(elem, SM_CAP_CSWITCH);
}
/**
*/
int snd_mixer_selem_has_capture_switch_joined(snd_mixer_elem_t *elem)
{
- sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- s = elem->private_data;
- return !!(s->caps & SM_CAP_CSWITCH_JOIN);
+ CHECK_BASIC(elem);
+ return COND_CAPS(elem, SM_CAP_CSWITCH_JOIN);
}
/**
*/
int snd_mixer_selem_has_capture_switch_exclusive(snd_mixer_elem_t *elem)
{
- sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- s = elem->private_data;
- return !!(s->caps & SM_CAP_CSWITCH_EXCL);
+ CHECK_BASIC(elem);
+ return COND_CAPS(elem, SM_CAP_CSWITCH_EXCL);
}
/**
int snd_mixer_selem_get_capture_group(snd_mixer_elem_t *elem)
{
sm_selem_t *s;
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
s = elem->private_data;
if (! (s->caps & SM_CAP_CSWITCH_EXCL))
return -EINVAL;
*/
int snd_mixer_selem_get_capture_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_DIR_CHN(elem, SM_CAP_CVOLUME, SM_CAP_CVOLUME_JOIN, channel);
return sm_selem_ops(elem)->get_volume(elem, SM_CAPT, channel, value);
}
*/
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);
+ CHECK_BASIC(elem);
+ CHECK_DIR_CHN(elem, SM_CAP_CVOLUME, SM_CAP_CVOLUME_JOIN, channel);
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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_DIR_CHN(elem, SM_CAP_CSWITCH, SM_CAP_CSWITCH_JOIN, channel);
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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_DIR_CHN(elem, SM_CAP_CVOLUME, SM_CAP_CVOLUME_JOIN, channel);
return sm_selem_ops(elem)->set_volume(elem, SM_CAPT, channel, value);
}
* \param elem Mixer simple element handle
* \param channel mixer simple element channel identifier
* \param value control value in dB * 100
+ * \param dir select direction (-1 = accurate or first bellow, 0 = accurate, 1 = accurate or first above)
* \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);
+ CHECK_BASIC(elem);
+ CHECK_DIR_CHN(elem, SM_CAP_CVOLUME, SM_CAP_CVOLUME_JOIN, channel);
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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- return sm_selem_ops(elem)->set_volume_all(elem, SM_CAPT, value);
+ snd_mixer_selem_channel_id_t chn;
+ int err;
+
+ for (chn = 0; chn < 32; chn++) {
+ if (!snd_mixer_selem_has_capture_channel(elem, chn))
+ continue;
+ err = snd_mixer_selem_set_capture_volume(elem, chn, value);
+ if (err < 0)
+ return err;
+ if (chn == 0 && snd_mixer_selem_has_capture_volume_joined(elem))
+ return 0;
+ }
+ return 0;
}
/**
* \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
+ * \param dir select direction (-1 = accurate or first bellow, 0 = accurate, 1 = accurate or first above)
* \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);
+ snd_mixer_selem_channel_id_t chn;
+ int err;
+
+ for (chn = 0; chn < 32; chn++) {
+ if (!snd_mixer_selem_has_capture_channel(elem, chn))
+ continue;
+ err = snd_mixer_selem_set_capture_dB(elem, chn, value, dir);
+ if (err < 0)
+ return err;
+ if (chn == 0 && snd_mixer_selem_has_capture_volume_joined(elem))
+ return 0;
+ }
+ return 0;
}
/**
*/
int snd_mixer_selem_set_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int value)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_DIR_CHN(elem, SM_CAP_CSWITCH, SM_CAP_CSWITCH_JOIN, channel);
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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
- return sm_selem_ops(elem)->set_switch_all(elem, SM_CAPT, value);
+ snd_mixer_selem_channel_id_t chn;
+ int err;
+
+ for (chn = 0; chn < 32; chn++) {
+ if (!snd_mixer_selem_has_capture_channel(elem, chn))
+ continue;
+ err = snd_mixer_selem_set_capture_switch(elem, chn, value);
+ if (err < 0)
+ return err;
+ if (chn == 0 && snd_mixer_selem_has_capture_switch_joined(elem))
+ return 0;
+ }
+ return 0;
}
/**
*/
int snd_mixer_selem_is_enumerated(snd_mixer_elem_t *elem)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_ENUM(elem);
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)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_ENUM(elem);
return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_ENUMCNT, 0);
}
unsigned int item,
size_t maxlen, char *buf)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_ENUM(elem);
return sm_selem_ops(elem)->enum_item_name(elem, item, maxlen, buf);
}
snd_mixer_selem_channel_id_t channel,
unsigned int *itemp)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_ENUM(elem);
return sm_selem_ops(elem)->get_enum_item(elem, channel, itemp);
}
snd_mixer_selem_channel_id_t channel,
unsigned int item)
{
- assert(elem);
- assert(elem->type == SND_MIXER_ELEM_SIMPLE);
+ CHECK_BASIC(elem);
+ CHECK_ENUM(elem);
return sm_selem_ops(elem)->set_enum_item(elem, channel, item);
}
#include <sys/ioctl.h>
#include <math.h>
#include <alsa/asoundlib.h>
+#include <alsa/mixer_abst.h>
+#include <alsa/list.h>
+
+#define MAX_CHANNEL 6
+
+#define SID_MASTER 0
+
+struct melem_sids {
+ const char *sname;
+ unsigned short sindex;
+ unsigned short weight;
+ unsigned int chanmap[2];
+};
+
+struct melem_sids sids[] = {
+ {
+ .sname = "Master",
+ .sindex = 0,
+ .weight = 1,
+ .chanmap = { 3, 0 },
+ }
+};
+
+#define PURPOSE_VOLUME 0
+#define PURPOSE_SWITCH 1
+#define PURPOSE_ENUMLIST 2
+
+struct helem_selector {
+ snd_ctl_elem_iface_t iface;
+ const char *name;
+ unsigned short index;
+ unsigned short sid;
+ unsigned short purpose;
+ unsigned short caps;
+};
+
+#define SELECTORS (sizeof(selectors)/sizeof(selectors[0]))
+
+struct helem_selector selectors[] = {
+ {
+ .iface = SND_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .index = 0,
+ .sid = SID_MASTER,
+ .purpose = PURPOSE_VOLUME,
+ .caps = SM_CAP_PVOLUME,
+ },
+ {
+ .iface = SND_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .index = 0,
+ .sid = SID_MASTER,
+ .purpose = PURPOSE_SWITCH,
+ .caps = SM_CAP_PSWITCH,
+ }
+};
+
+struct helem_ac97 {
+ struct list_head list;
+ snd_hctl_elem_t *helem;
+ unsigned short purpose;
+ unsigned int caps;
+ unsigned int inactive: 1;
+ long min, max;
+ unsigned int count;
+};
+
+struct selem_ac97 {
+ sm_selem_t selem;
+ struct list_head helems;
+ unsigned short sid;
+ struct {
+ unsigned int chanmap;
+ unsigned int forced_range: 1;
+ long min, max;
+ long vol[MAX_CHANNEL];
+ } dir[2];
+};
+
+/*
+ * Prototypes
+ */
+
+static int selem_read(snd_mixer_elem_t *elem);
+
+/*
+ * Helpers
+ */
+
+static unsigned int chanmap_to_channels(unsigned int chanmap)
+{
+ unsigned int i, res;
+
+ for (i = 0, res = 0; i < MAX_CHANNEL; i++)
+ if (chanmap & (1 << i))
+ res++;
+ return res;
+}
+
+static long to_user(struct selem_ac97 *s, int dir, struct helem_ac97 *c, long value)
+{
+ int64_t n;
+ if (c->max == c->min)
+ return s->dir[dir].min;
+ n = (int64_t) (value - c->min) * (s->dir[dir].max - s->dir[dir].min);
+ return s->dir[dir].min + (n + (c->max - c->min) / 2) / (c->max - c->min);
+}
+
+static long from_user(struct selem_ac97 *s, int dir, struct helem_ac97 *c, long value)
+{
+ int64_t n;
+ if (s->dir[dir].max == s->dir[dir].min)
+ return c->min;
+ n = (int64_t) (value - s->dir[dir].min) * (c->max - c->min);
+ return c->min + (n + (s->dir[dir].max - s->dir[dir].min) / 2) / (s->dir[dir].max - s->dir[dir].min);
+}
+
+static void update_ranges(struct selem_ac97 *s)
+{
+ static unsigned int mask[2] = { SM_CAP_PVOLUME, SM_CAP_CVOLUME };
+ static unsigned int gmask[2] = { SM_CAP_GVOLUME, SM_CAP_GVOLUME };
+ unsigned int dir, ok_flag;
+ struct list_head *pos;
+ struct helem_ac97 *helem;
+
+ for (dir = 0; dir < 2; dir++) {
+ s->dir[dir].min = 0;
+ s->dir[dir].max = 0;
+ ok_flag = 0;
+ list_for_each(pos, &s->helems) {
+ helem = list_entry(pos, struct helem_ac97, list);
+ printf("min = %li, max = %li\n", helem->min, helem->max);
+ if (helem->caps & mask[dir]) {
+ s->dir[dir].min = helem->min;
+ s->dir[dir].max = helem->max;
+ ok_flag = 1;
+ break;
+ }
+ }
+ if (ok_flag)
+ continue;
+ list_for_each(pos, &s->helems) {
+ helem = list_entry(pos, struct helem_ac97, list);
+ if (helem->caps & gmask[dir]) {
+ s->dir[dir].min = helem->min;
+ s->dir[dir].max = helem->max;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Simple Mixer Operations
+ */
+
+static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val)
+{
+ struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
+
+ switch (cmd) {
+
+ case SM_OPS_IS_ACTIVE: {
+ struct list_head *pos;
+ struct helem_ac97 *helem;
+ list_for_each(pos, &s->helems) {
+ helem = list_entry(pos, struct helem_ac97, list);
+ if (helem->inactive)
+ return 0;
+ }
+ return 1;
+ }
+
+ case SM_OPS_IS_MONO:
+ return chanmap_to_channels(s->dir[dir].chanmap) == 1;
+
+ case SM_OPS_IS_CHANNEL:
+ if (val > MAX_CHANNEL)
+ return 0;
+ return !!((1 << val) & s->dir[dir].chanmap);
+
+ case SM_OPS_IS_ENUMERATED: {
+ struct helem_ac97 *helem;
+ helem = list_entry(s->helems.next, struct helem_ac97, list);
+ return !!(helem->purpose == PURPOSE_ENUMLIST);
+ }
+
+ case SM_OPS_IS_ENUMCNT: {
+ struct helem_ac97 *helem;
+ helem = list_entry(s->helems.next, struct helem_ac97, list);
+ return helem->max;
+ }
+
+ }
+
+ return 1;
+}
+
+static int get_range_ops(snd_mixer_elem_t *elem, int dir,
+ long *min, long *max)
+{
+ struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
+
+ *min = s->dir[dir].min;
+ *max = s->dir[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)
+{
+ struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
+ int err;
+
+ s->dir[dir].forced_range = 1;
+ s->dir[dir].min = min;
+ s->dir[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)
+{
+ struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
+
+ *value = s->dir[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)
+{
+ struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
+ *value = 0;
+ return 0;
+}
+
+static int set_volume_ops(snd_mixer_elem_t *elem, int dir,
+ snd_mixer_selem_channel_id_t channel, long value)
+{
+ struct selem_ac97 *s = snd_mixer_elem_get_private(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_switch_ops(snd_mixer_elem_t *elem, int dir,
+ snd_mixer_selem_channel_id_t channel, int value)
+{
+ struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
+ int changed;
+ return 0;
+}
+
+static int enum_item_name_ops(snd_mixer_elem_t *elem,
+ unsigned int item,
+ size_t maxlen, char *buf)
+{
+ struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
+ return 0;
+}
+
+static int get_enum_item_ops(snd_mixer_elem_t *elem,
+ snd_mixer_selem_channel_id_t channel,
+ unsigned int *itemp)
+{
+ struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
+ return 0;
+}
+
+static int set_enum_item_ops(snd_mixer_elem_t *elem,
+ snd_mixer_selem_channel_id_t channel,
+ unsigned int item)
+{
+ struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
+ return 0;
+}
+
+static struct sm_elem_ops simple_ac97_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,
+ .get_switch = get_switch_ops,
+ .set_switch = set_switch_ops,
+ .enum_item_name = enum_item_name_ops,
+ .get_enum_item = get_enum_item_ops,
+ .set_enum_item = set_enum_item_ops
+};
+
+/*
+ * event handling
+ */
+
+static int selem_read(snd_mixer_elem_t *elem)
+{
+ printf("elem read: %p\n", elem);
+ return 0;
+}
+
+static int simple_event_remove(snd_hctl_elem_t *helem,
+ snd_mixer_elem_t *melem)
+{
+ printf("event remove: %p\n", helem);
+ return 0;
+}
+
+static void selem_free(snd_mixer_elem_t *elem)
+{
+ struct selem_ac97 *simple = snd_mixer_elem_get_private(elem);
+ struct helem_ac97 *hsimple;
+ struct list_head *pos, *npos;
+
+ if (simple->selem.id)
+ snd_mixer_selem_id_free(simple->selem.id);
+ list_for_each_safe(pos, npos, &simple->helems) {
+ hsimple = list_entry(pos, struct helem_ac97, list);
+ free(hsimple);
+ }
+ free(simple);
+}
+
+static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem)
+{
+ int count;
+ struct helem_selector *sel;
+ snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem);
+ const char *name = snd_hctl_elem_get_name(helem);
+ unsigned int index = snd_hctl_elem_get_index(helem);
+ snd_mixer_elem_t *melem;
+ snd_mixer_selem_id_t *id;
+ struct selem_ac97 *simple;
+ struct helem_ac97 *hsimple;
+ snd_ctl_elem_info_t *info;
+ snd_ctl_elem_type_t ctype;
+ unsigned long values;
+ long min, max;
+ int err, new = 0;
+
+ snd_ctl_elem_info_alloca(&info);
+ for (count = SELECTORS, sel = selectors; count > 0; count--, sel++) {
+ if (sel->iface == iface && !strcmp(sel->name, name) && sel->index == index)
+ break;
+ }
+ if (count == 0)
+ return 0; /* ignore this helem */
+ 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 (ctype) {
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ min = 0;
+ max = snd_ctl_elem_info_get_items(info);
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ min = snd_ctl_elem_info_get_min(info);
+ max = snd_ctl_elem_info_get_max(info);
+ break;
+ default:
+ min = max = 0;
+ break;
+ }
+
+ printf("event add: %p, %p (%s)\n", helem, sel, name);
+ if (snd_mixer_selem_id_malloc(&id))
+ return -ENOMEM;
+ hsimple = calloc(1, sizeof(*hsimple));
+ if (hsimple == NULL) {
+ snd_mixer_selem_id_free(id);
+ return -ENOMEM;
+ }
+ switch (sel->purpose) {
+ case PURPOSE_SWITCH:
+ if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN) {
+ __invalid_type:
+ snd_mixer_selem_id_free(id);
+ return -EINVAL;
+ }
+ break;
+ case PURPOSE_VOLUME:
+ if (ctype != SND_CTL_ELEM_TYPE_INTEGER)
+ goto __invalid_type;
+ break;
+ }
+ hsimple->purpose = sel->purpose;
+ hsimple->caps = sel->caps;
+ hsimple->min = min;
+ hsimple->max = max;
+ snd_mixer_selem_id_set_name(id, sids[sel->sid].sname);
+ snd_mixer_selem_id_set_index(id, sids[sel->sid].sindex);
+ melem = snd_mixer_find_selem(snd_mixer_class_get_mixer(class), id);
+ if (!melem) {
+ simple = calloc(1, sizeof(*simple));
+ if (!simple) {
+ snd_mixer_selem_id_free(id);
+ free(hsimple);
+ return -ENOMEM;
+ }
+ simple->selem.id = id;
+ simple->selem.ops = &simple_ac97_ops;
+ INIT_LIST_HEAD(&simple->helems);
+ simple->sid = sel->sid;
+ err = snd_mixer_elem_new(&melem, SND_MIXER_ELEM_SIMPLE,
+ sids[sel->sid].weight,
+ simple, selem_free);
+ if (err < 0) {
+ snd_mixer_selem_id_free(id);
+ free(hsimple);
+ free(simple);
+ return err;
+ }
+ new = 1;
+ } else {
+ simple = snd_mixer_elem_get_private(melem);
+ snd_mixer_selem_id_free(id);
+ }
+ list_add_tail(&hsimple->list, &simple->helems);
+ hsimple->inactive = snd_ctl_elem_info_is_inactive(info);
+ err = snd_mixer_elem_attach(melem, helem);
+ if (err < 0)
+ goto __error;
+ simple->dir[0].chanmap |= sids[sel->sid].chanmap[0];
+ simple->dir[1].chanmap |= sids[sel->sid].chanmap[1];
+ simple->selem.caps |= hsimple->caps;
+ update_ranges(simple);
+#if 0
+ err = simple_update(melem);
+ if (err < 0) {
+ if (new)
+ goto __error;
+ return err;
+ }
+#endif
+ 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;
+ return 0;
+}
int alsa_mixer_simple_event(snd_mixer_class_t *class, unsigned int mask,
snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
{
- return -EIO;
+ 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;
}
-
\ No newline at end of file
char *device;
snd_ctl_t *ctl;
snd_hctl_t *hctl;
+ int attach_flag;
snd_ctl_card_info_t *info;
void *dlhandle;
} class_priv_t;
free(xlib);
snd_mixer_class_set_event(class, event_func);
priv->dlhandle = h;
- return 0;
+ return 1;
}
static int match(snd_mixer_class_t *class, const char *lib, const char *searchl)
snd_dlclose(priv->dlhandle);
if (priv->info)
snd_ctl_card_info_free(priv->info);
- if (priv->hctl)
+ if (priv->hctl) {
+ if (priv->attach_flag)
+ snd_mixer_detach_hctl(snd_mixer_class_get_mixer(class), priv->hctl);
snd_hctl_close(priv->hctl);
- else if (priv->ctl)
+ } else if (priv->ctl)
snd_ctl_close(priv->ctl);
if (priv->device)
free(priv->device);
snd_config_t *top = NULL;
int err;
- if (options->device == NULL)
- return -EIO;
if (priv == NULL)
return -ENOMEM;
+ if (options->device == NULL) {
+ free(priv);
+ return -EINVAL;
+ }
if (snd_mixer_class_malloc(&class)) {
free(priv);
return -ENOMEM;
goto __error;
}
err = find_module(class, top);
+ snd_config_delete(top);
+ top = NULL;
}
if (err >= 0)
+ err = snd_mixer_attach_hctl(mixer, priv->hctl);
+ if (err >= 0) {
+ priv->attach_flag = 1;
err = snd_mixer_class_register(class, mixer);
+ }
if (err < 0) {
__error:
- if (top)
- snd_config_delete(top);
+ if (top)
+ snd_config_delete(top);
if (class)
snd_mixer_class_free(class);
return err;
*classp = class;
return 0;
}
+
+/**
+ * \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_sbasic_info(const snd_mixer_class_t *class, sm_class_basic_t *info)
+{
+ class_priv_t *priv = snd_mixer_class_get_private(class);
+
+ if (class == NULL || info == NULL)
+ return -EINVAL;
+ info->device = priv->device;
+ info->ctl = priv->ctl;
+ info->hctl = priv->hctl;
+ info->info = priv->info;
+ return 0;
+}
caps |= SM_CAP_PSWITCH;
}
+ if ((caps & SM_CAP_GSWITCH) &&
+ (caps & (SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == 0)
+ caps |= SM_CAP_PSWITCH|SM_CAP_CSWITCH;
+
+ if ((caps & SM_CAP_GVOLUME) &&
+ (caps & (SM_CAP_PVOLUME|SM_CAP_CVOLUME)) == 0)
+ caps |= SM_CAP_PVOLUME|SM_CAP_CVOLUME;
+
simple->selem.caps = caps;
simple->str[SM_PLAY].channels = pchannels;
if (!simple->str[SM_PLAY].range) {
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);
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);
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;
}
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;
}
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;
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)
{
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)
.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
if (!melem) {
simple = calloc(1, sizeof(*simple));
if (!simple) {
- free(id);
+ snd_mixer_selem_id_free(id);
return -ENOMEM;
}
simple->selem.id = id;
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);
+ snd_mixer_selem_id_free(id);
free(simple);
return err;
}
new = 1;
} else {
simple = snd_mixer_elem_get_private(melem);
- free(id);
+ snd_mixer_selem_id_free(id);
}
if (simple->ctls[type].elem) {
SNDERR("helem (%s,'%s',%li,%li,%li) appears twice or more",