mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-31 22:25:35 -04:00
The simple mixer abstraction - more work
- separated the "base library" from ac97.c (dynamically loaded) - added necessary handling of all private structures - added selector and sid registration functions and handling code - added basic code for hda.c (yeah, my notebook has this hw)
This commit is contained in:
parent
c593c1d260
commit
59a8226184
9 changed files with 990 additions and 483 deletions
|
|
@ -256,6 +256,9 @@ ALSA_1.0.10 {
|
|||
snd_mixer_selem_set_capture_dB_all;
|
||||
snd_mixer_selem_compare;
|
||||
snd_mixer_sbasic_info;
|
||||
snd_mixer_sbasic_get_private;
|
||||
snd_mixer_sbasic_set_private;
|
||||
snd_mixer_sbasic_set_private_free;
|
||||
|
||||
snd_ctl_ext_create;
|
||||
snd_ctl_ext_delete;
|
||||
|
|
|
|||
|
|
@ -6,3 +6,7 @@ ac97 {
|
|||
searchl "AC97a:"
|
||||
lib smixer-ac97.so
|
||||
}
|
||||
hda {
|
||||
searchl "HDA:"
|
||||
lib smixer-hda.so
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,15 @@ pkglibdir = $(libdir)/@PACKAGE@/smixer
|
|||
|
||||
AM_CFLAGS = -g -O2 -W -Wall
|
||||
|
||||
pkglib_LTLIBRARIES = smixer-ac97.la
|
||||
pkglib_LTLIBRARIES = smixer-sbase.la \
|
||||
smixer-ac97.la \
|
||||
smixer-hda.la
|
||||
|
||||
smixer_ac97_la_SOURCES = ac97.c
|
||||
smixer_sbase_la_SOURCES = sbase.c
|
||||
smixer_sbase_la_LDFLAGS = -module
|
||||
|
||||
smixer_ac97_la_SOURCES = ac97.c sbasedl.c
|
||||
smixer_ac97_la_LDFLAGS = -module
|
||||
|
||||
smixer_hda_la_SOURCES = hda.c sbasedl.c
|
||||
smixer_hda_la_LDFLAGS = -module
|
||||
|
|
|
|||
|
|
@ -28,41 +28,21 @@
|
|||
#include <math.h>
|
||||
#include "asoundlib.h"
|
||||
#include "mixer_abst.h"
|
||||
#include "list.h"
|
||||
#include "sbase.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];
|
||||
};
|
||||
static struct sm_elem_ops simple_ac97_ops;
|
||||
|
||||
struct melem_sids sids[] = {
|
||||
{
|
||||
.sid = SID_MASTER,
|
||||
.sname = "Master",
|
||||
.sindex = 0,
|
||||
.weight = 1,
|
||||
.chanmap = { 3, 0 },
|
||||
.sops = &simple_ac97_ops,
|
||||
}
|
||||
};
|
||||
|
||||
#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[] = {
|
||||
|
|
@ -84,463 +64,26 @@ struct helem_selector selectors[] = {
|
|||
}
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
|
||||
return priv->ops.event(class, mask, helem, melem);
|
||||
}
|
||||
|
||||
int alsa_mixer_simple_init(snd_mixer_class_t *class)
|
||||
{
|
||||
struct bclass_base_ops *ops;
|
||||
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;
|
||||
|
||||
err = mixer_simple_basic_dlopen(class, &ops);
|
||||
if (err < 0)
|
||||
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;
|
||||
}
|
||||
}
|
||||
err = ops->selreg(class, selectors, SELECTORS);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = ops->sidreg(class, sids, SELECTORS);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
90
src/mixer/simple/hda.c
Normal file
90
src/mixer/simple/hda.c
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Mixer Interface - HDA simple abstact 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 "asoundlib.h"
|
||||
#include "mixer_abst.h"
|
||||
#include "sbase.h"
|
||||
|
||||
static struct sm_elem_ops simple_hda_ops;
|
||||
|
||||
struct melem_sids sids[] = {
|
||||
{
|
||||
.sid = SID_FRONT,
|
||||
.sname = "Front",
|
||||
.sindex = 0,
|
||||
.weight = 1,
|
||||
.chanmap = { 3, 0 },
|
||||
.sops = &simple_hda_ops,
|
||||
}
|
||||
};
|
||||
|
||||
#define SELECTORS (sizeof(selectors)/sizeof(selectors[0]))
|
||||
|
||||
struct helem_selector selectors[] = {
|
||||
{
|
||||
.iface = SND_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Front Playback Volume",
|
||||
.index = 0,
|
||||
.sid = SID_FRONT,
|
||||
.purpose = PURPOSE_VOLUME,
|
||||
.caps = SM_CAP_PVOLUME,
|
||||
},
|
||||
{
|
||||
.iface = SND_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Front Playback Switch",
|
||||
.index = 0,
|
||||
.sid = SID_FRONT,
|
||||
.purpose = PURPOSE_SWITCH,
|
||||
.caps = SM_CAP_PSWITCH,
|
||||
}
|
||||
};
|
||||
|
||||
int alsa_mixer_simple_event(snd_mixer_class_t *class, unsigned int mask,
|
||||
snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
|
||||
{
|
||||
struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
|
||||
return priv->ops.event(class, mask, helem, melem);
|
||||
}
|
||||
|
||||
int alsa_mixer_simple_init(snd_mixer_class_t *class)
|
||||
{
|
||||
struct bclass_base_ops *ops;
|
||||
int err;
|
||||
|
||||
err = mixer_simple_basic_dlopen(class, &ops);
|
||||
if (err < 0)
|
||||
return 0;
|
||||
err = ops->selreg(class, selectors, SELECTORS);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = ops->sidreg(class, sids, SELECTORS);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
579
src/mixer/simple/sbase.c
Normal file
579
src/mixer/simple/sbase.c
Normal file
|
|
@ -0,0 +1,579 @@
|
|||
/*
|
||||
* Mixer Interface - simple abstact module - base library
|
||||
* 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 "asoundlib.h"
|
||||
#include "mixer_abst.h"
|
||||
#include "sbase.h"
|
||||
|
||||
/*
|
||||
* 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_base *s, int dir, struct helem_base *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_base *s, int dir, struct helem_base *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_base *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_base *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_base, 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_base, 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_base *s = snd_mixer_elem_get_private(elem);
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case SM_OPS_IS_ACTIVE: {
|
||||
struct list_head *pos;
|
||||
struct helem_base *helem;
|
||||
list_for_each(pos, &s->helems) {
|
||||
helem = list_entry(pos, struct helem_base, 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_base *helem;
|
||||
helem = list_entry(s->helems.next, struct helem_base, list);
|
||||
return !!(helem->purpose == PURPOSE_ENUMLIST);
|
||||
}
|
||||
|
||||
case SM_OPS_IS_ENUMCNT: {
|
||||
struct helem_base *helem;
|
||||
helem = list_entry(s->helems.next, struct helem_base, list);
|
||||
return helem->max;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_range_ops(snd_mixer_elem_t *elem, int dir,
|
||||
long *min, long *max)
|
||||
{
|
||||
struct selem_base *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_base *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_base *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_base *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_base *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_base *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_base *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_base *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_base *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_base *simple = snd_mixer_elem_get_private(elem);
|
||||
struct helem_base *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_base, list);
|
||||
free(hsimple);
|
||||
}
|
||||
free(simple);
|
||||
}
|
||||
|
||||
static int simple_event_add1(snd_mixer_class_t *class,
|
||||
snd_hctl_elem_t *helem,
|
||||
struct helem_selector *sel)
|
||||
{
|
||||
struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
|
||||
int count;
|
||||
snd_mixer_elem_t *melem;
|
||||
snd_mixer_selem_id_t *id;
|
||||
snd_ctl_elem_info_t *info;
|
||||
struct selem_base *simple;
|
||||
struct helem_base *hsimple;
|
||||
snd_ctl_elem_type_t ctype;
|
||||
unsigned long values;
|
||||
long min, max;
|
||||
int err, new = 0;
|
||||
struct list_head *pos;
|
||||
struct bclass_sid *bsid;
|
||||
struct melem_sids *sid;
|
||||
unsigned int ui;
|
||||
|
||||
list_for_each(pos, &priv->sids) {
|
||||
bsid = list_entry(pos, struct bclass_sid, list);
|
||||
for (ui = 0; ui < bsid->count; ui++) {
|
||||
if (bsid->sids[ui].sid == sel->sid) {
|
||||
sid = &bsid->sids[ui];
|
||||
goto __sid_ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
__sid_ok:
|
||||
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 (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, snd_hctl_elem_get_name(helem));
|
||||
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, sid->sname);
|
||||
snd_mixer_selem_id_set_index(id, 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,
|
||||
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 |= sid->chanmap[0];
|
||||
simple->dir[1].chanmap |= 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;
|
||||
}
|
||||
|
||||
static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem)
|
||||
{
|
||||
struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
|
||||
struct bclass_selector *sel;
|
||||
struct helem_selector *hsel;
|
||||
struct list_head *pos;
|
||||
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);
|
||||
unsigned int ui;
|
||||
int err;
|
||||
|
||||
list_for_each(pos, &priv->selectors) {
|
||||
sel = list_entry(pos, struct bclass_selector, list);
|
||||
for (ui = 0; ui < sel->count; ui++) {
|
||||
hsel = &sel->selectors[ui];
|
||||
if (hsel->iface == iface && !strcmp(hsel->name, name) && hsel->index == index) {
|
||||
err = simple_event_add1(class, helem, hsel);
|
||||
if (err < 0)
|
||||
return err; /* early exit? */
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int alsa_mixer_sbasic_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 void sbasic_cpriv_free(snd_mixer_class_t *class)
|
||||
{
|
||||
struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
|
||||
struct bclass_selector *sel;
|
||||
struct bclass_sid *sid;
|
||||
struct list_head *pos, *pos1;
|
||||
|
||||
list_for_each_safe(pos, pos1, &priv->selectors) {
|
||||
sel = list_entry(pos, struct bclass_selector, list);
|
||||
free(sel);
|
||||
}
|
||||
list_for_each_safe(pos, pos1, &priv->sids) {
|
||||
sid = list_entry(pos, struct bclass_sid, list);
|
||||
free(sid);
|
||||
}
|
||||
free(priv);
|
||||
}
|
||||
|
||||
void alsa_mixer_sbasic_initpriv(snd_mixer_class_t *class,
|
||||
struct bclass_private *priv)
|
||||
{
|
||||
INIT_LIST_HEAD(&priv->selectors);
|
||||
INIT_LIST_HEAD(&priv->sids);
|
||||
snd_mixer_sbasic_set_private(class, priv);
|
||||
snd_mixer_sbasic_set_private_free(class, sbasic_cpriv_free);
|
||||
}
|
||||
|
||||
int alsa_mixer_sbasic_selreg(snd_mixer_class_t *class,
|
||||
struct helem_selector *selectors,
|
||||
unsigned int count)
|
||||
{
|
||||
struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
|
||||
struct bclass_selector *sel = calloc(1, sizeof(*sel));
|
||||
|
||||
if (sel == NULL)
|
||||
return -ENOMEM;
|
||||
if (priv == NULL) {
|
||||
priv = calloc(1, sizeof(*priv));
|
||||
if (priv == NULL) {
|
||||
free(sel);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
sel->selectors = selectors;
|
||||
sel->count = count;
|
||||
list_add_tail(&sel->list, &priv->selectors);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int alsa_mixer_sbasic_sidreg(snd_mixer_class_t *class,
|
||||
struct melem_sids *sids,
|
||||
unsigned int count)
|
||||
{
|
||||
struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
|
||||
struct bclass_sid *sid = calloc(1, sizeof(*sid));
|
||||
|
||||
if (sid == NULL)
|
||||
return -ENOMEM;
|
||||
if (priv == NULL) {
|
||||
priv = calloc(1, sizeof(*priv));
|
||||
if (priv == NULL) {
|
||||
free(sid);
|
||||
return -ENOMEM;
|
||||
}
|
||||
INIT_LIST_HEAD(&priv->selectors);
|
||||
INIT_LIST_HEAD(&priv->sids);
|
||||
snd_mixer_sbasic_set_private(class, priv);
|
||||
snd_mixer_sbasic_set_private_free(class, sbasic_cpriv_free);
|
||||
}
|
||||
sid->sids = sids;
|
||||
sid->count = count;
|
||||
list_add(&sid->list, &priv->sids);
|
||||
return 0;
|
||||
}
|
||||
111
src/mixer/simple/sbase.h
Normal file
111
src/mixer/simple/sbase.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Mixer Interface - simple abstact module - base library
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SMIXER_BASE_H
|
||||
|
||||
#include "list.h"
|
||||
|
||||
#define MAX_CHANNEL 6
|
||||
|
||||
#define SID_MASTER 0
|
||||
#define SID_HEADPHONE 1
|
||||
#define SID_FRONT 2
|
||||
#define SID_PCM 3
|
||||
#define SID_CD 4
|
||||
|
||||
struct melem_sids {
|
||||
unsigned short sid;
|
||||
const char *sname;
|
||||
unsigned short sindex;
|
||||
unsigned short weight;
|
||||
unsigned int chanmap[2];
|
||||
struct sm_elem_ops *sops;
|
||||
};
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
struct helem_base {
|
||||
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_base {
|
||||
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];
|
||||
};
|
||||
|
||||
struct bclass_selector {
|
||||
struct list_head list;
|
||||
struct helem_selector *selectors;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
struct bclass_sid {
|
||||
struct list_head list;
|
||||
struct melem_sids *sids;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
typedef struct bclass_base_ops {
|
||||
int (*event)(snd_mixer_class_t *class, unsigned int mask,
|
||||
snd_hctl_elem_t *helem, snd_mixer_elem_t *melem);
|
||||
int (*selreg)(snd_mixer_class_t *class,
|
||||
struct helem_selector *selectors,
|
||||
unsigned int count);
|
||||
int (*sidreg)(snd_mixer_class_t *class,
|
||||
struct melem_sids *sids,
|
||||
unsigned int count);
|
||||
} bclass_base_ops_t;
|
||||
|
||||
struct bclass_private {
|
||||
struct list_head selectors;
|
||||
struct list_head sids;
|
||||
void *dl_sbase;
|
||||
bclass_base_ops_t ops;
|
||||
};
|
||||
|
||||
int mixer_simple_basic_dlopen(snd_mixer_class_t *class,
|
||||
bclass_base_ops_t **ops);
|
||||
|
||||
#endif /* __SMIXER_BASE_H */
|
||||
106
src/mixer/simple/sbasedl.c
Normal file
106
src/mixer/simple/sbasedl.c
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Mixer Interface - simple abstact module - base library (dlopen function)
|
||||
* 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 <dlfcn.h>
|
||||
#include "config.h"
|
||||
#include "asoundlib.h"
|
||||
#include "mixer_abst.h"
|
||||
#include "sbase.h"
|
||||
|
||||
#define SO_PATH PKGLIBDIR "/smixer"
|
||||
|
||||
int mixer_simple_basic_dlopen(snd_mixer_class_t *class,
|
||||
bclass_base_ops_t **ops)
|
||||
{
|
||||
struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
|
||||
const char *lib = "smixer-sbase.so";
|
||||
void (*initpriv)(snd_mixer_class_t *class, struct bclass_private *priv);
|
||||
char *xlib, *path;
|
||||
void *h;
|
||||
int initflag = 0;
|
||||
|
||||
if (priv == NULL) {
|
||||
priv = calloc(1, sizeof(*priv));
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
initflag = 1;
|
||||
}
|
||||
path = getenv("ALSA_MIXER_SIMPLE_MODULES");
|
||||
if (!path)
|
||||
path = SO_PATH;
|
||||
xlib = malloc(strlen(lib) + strlen(path) + 1 + 1);
|
||||
if (xlib == NULL) {
|
||||
if (initflag)
|
||||
free(priv);
|
||||
return -ENOMEM;
|
||||
}
|
||||
strcpy(xlib, path);
|
||||
strcat(xlib, "/");
|
||||
strcat(xlib, lib);
|
||||
h = snd_dlopen(xlib, RTLD_NOW);
|
||||
if (h == NULL) {
|
||||
SNDERR("Unable to open library '%s'", xlib);
|
||||
goto __error;
|
||||
}
|
||||
initpriv = dlsym(h, "alsa_mixer_sbasic_initpriv");
|
||||
if (initpriv == NULL) {
|
||||
SNDERR("Symbol 'alsa_mixer_sbasic_initpriv' was not found in '%s'", xlib);
|
||||
goto __error;
|
||||
}
|
||||
priv->ops.event = dlsym(h, "alsa_mixer_sbasic_event");
|
||||
if (priv->ops.event == NULL) {
|
||||
SNDERR("Symbol 'alsa_mixer_sbasic_event' was not found in '%s'", xlib);
|
||||
goto __error;
|
||||
}
|
||||
priv->ops.selreg = dlsym(h, "alsa_mixer_sbasic_selreg");
|
||||
if (priv->ops.selreg == NULL) {
|
||||
SNDERR("Symbol 'alsa_mixer_sbasic_selreg' was not found in '%s'", xlib);
|
||||
goto __error;
|
||||
}
|
||||
priv->ops.sidreg = dlsym(h, "alsa_mixer_sbasic_sidreg");
|
||||
if (priv->ops.sidreg == NULL) {
|
||||
SNDERR("Symbol 'alsa_mixer_sbasic_sidreg' was not found in '%s'", xlib);
|
||||
goto __error;
|
||||
}
|
||||
free(xlib);
|
||||
if (initflag)
|
||||
initpriv(class, priv);
|
||||
priv->dl_sbase = h;
|
||||
if (ops)
|
||||
*ops = &priv->ops;
|
||||
return 1;
|
||||
|
||||
__error:
|
||||
if (initflag)
|
||||
free(priv);
|
||||
if (h == NULL)
|
||||
snd_dlclose(h);
|
||||
free(xlib);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
|
@ -48,14 +48,20 @@ typedef struct _class_priv {
|
|||
int attach_flag;
|
||||
snd_ctl_card_info_t *info;
|
||||
void *dlhandle;
|
||||
void *private_data;
|
||||
void (*private_free)(snd_mixer_class_t *class);
|
||||
} class_priv_t;
|
||||
|
||||
typedef int (*snd_mixer_sbasic_init_t)(snd_mixer_class_t *class);
|
||||
|
||||
static int try_open(snd_mixer_class_t *class, const char *lib)
|
||||
{
|
||||
class_priv_t *priv = snd_mixer_class_get_private(class);
|
||||
snd_mixer_event_t event_func;
|
||||
snd_mixer_sbasic_init_t init_func;
|
||||
char *xlib, *path;
|
||||
void *h;
|
||||
int err;
|
||||
|
||||
path = getenv("ALSA_MIXER_SIMPLE_MODULES");
|
||||
if (!path)
|
||||
|
|
@ -79,7 +85,19 @@ static int try_open(snd_mixer_class_t *class, const char *lib)
|
|||
free(xlib);
|
||||
return -ENXIO;
|
||||
}
|
||||
init_func = dlsym(h, "alsa_mixer_simple_init");
|
||||
if (init_func == NULL) {
|
||||
SNDERR("Symbol 'alsa_mixer_simple_init' was not found in '%s'", xlib);
|
||||
snd_dlclose(h);
|
||||
free(xlib);
|
||||
return -ENXIO;
|
||||
}
|
||||
free(xlib);
|
||||
err = init_func(class);
|
||||
if (err < 0) {
|
||||
snd_dlclose(h);
|
||||
return err;
|
||||
}
|
||||
snd_mixer_class_set_event(class, event_func);
|
||||
priv->dlhandle = h;
|
||||
return 1;
|
||||
|
|
@ -148,6 +166,8 @@ static void private_free(snd_mixer_class_t *class)
|
|||
{
|
||||
class_priv_t *priv = snd_mixer_class_get_private(class);
|
||||
|
||||
if (priv->private_free)
|
||||
priv->private_free(class);
|
||||
if (priv->dlhandle)
|
||||
snd_dlclose(priv->dlhandle);
|
||||
if (priv->info)
|
||||
|
|
@ -256,10 +276,9 @@ int snd_mixer_simple_basic_register(snd_mixer_t *mixer,
|
|||
}
|
||||
|
||||
/**
|
||||
* \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
|
||||
* \brief Basic Mixer Abstraction - Get information about device
|
||||
* \param class Mixer class
|
||||
* \param info Info structure
|
||||
* \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)
|
||||
|
|
@ -274,3 +293,47 @@ int snd_mixer_sbasic_info(const snd_mixer_class_t *class, sm_class_basic_t *info
|
|||
info->info = priv->info;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get private data for basic abstraction
|
||||
* \param class Mixer class
|
||||
* \return private data
|
||||
*/
|
||||
void *snd_mixer_sbasic_get_private(const snd_mixer_class_t *class)
|
||||
{
|
||||
class_priv_t *priv = snd_mixer_class_get_private(class);
|
||||
|
||||
if (class == NULL)
|
||||
return NULL;
|
||||
return priv->private_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set private data for basic abstraction
|
||||
* \param class Mixer class
|
||||
* \param private_data Private data
|
||||
*/
|
||||
void snd_mixer_sbasic_set_private(const snd_mixer_class_t *class, void *private_data)
|
||||
{
|
||||
class_priv_t *priv;
|
||||
|
||||
if (class == NULL)
|
||||
return;
|
||||
priv = snd_mixer_class_get_private(class);
|
||||
priv->private_data = private_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set private data for basic abstraction
|
||||
* \param class Mixer class
|
||||
* \param private_data Private data
|
||||
*/
|
||||
void snd_mixer_sbasic_set_private_free(const snd_mixer_class_t *class, void (*private_free)(snd_mixer_class_t *class))
|
||||
{
|
||||
class_priv_t *priv;
|
||||
|
||||
if (class == NULL)
|
||||
return;
|
||||
priv = snd_mixer_class_get_private(class);
|
||||
priv->private_free = private_free;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue