mirror of https://github.com/ericonr/sndio.git
Move controls list out of the device structure
Use a unique dynamically allocated ctl->addr key and a "scope" enum to identify controls that belong to different devices.
This commit is contained in:
parent
0adedb2a89
commit
bb27629080
441
sndiod/dev.c
441
sndiod/dev.c
|
@ -107,6 +107,7 @@ struct slotops zomb_slotops = {
|
|||
zomb_exit
|
||||
};
|
||||
|
||||
struct ctl *ctl_list = NULL;
|
||||
struct dev *dev_list = NULL;
|
||||
unsigned int dev_sndnum = 0;
|
||||
|
||||
|
@ -371,12 +372,14 @@ dev_midi_master(struct dev *d)
|
|||
master = d->master;
|
||||
else {
|
||||
master = 0;
|
||||
for (c = d->ctl_list; c != NULL; c = c->next) {
|
||||
for (c = ctl_list; c != NULL; c = c->next) {
|
||||
if (c->type != CTL_NUM ||
|
||||
strcmp(c->group, "") != 0 ||
|
||||
strcmp(c->group, d->name) != 0 ||
|
||||
strcmp(c->node0.name, "output") != 0 ||
|
||||
strcmp(c->func, "level") != 0)
|
||||
continue;
|
||||
if (c->u.any.arg0 != d)
|
||||
continue;
|
||||
v = (c->curval * 127 + c->maxval / 2) / c->maxval;
|
||||
if (master < v)
|
||||
master = v;
|
||||
|
@ -466,7 +469,7 @@ dev_midi_omsg(void *arg, unsigned char *msg, int len)
|
|||
slot_array[chan].opt->dev != d)
|
||||
return;
|
||||
slot_setvol(slot_array + chan, msg[2]);
|
||||
dev_onval(d, CTLADDR_SLOT_LEVEL(chan), msg[2]);
|
||||
ctl_onval(CTL_SLOT_LEVEL, slot_array + chan, NULL, msg[2]);
|
||||
return;
|
||||
}
|
||||
x = (struct sysex *)msg;
|
||||
|
@ -480,7 +483,7 @@ dev_midi_omsg(void *arg, unsigned char *msg, int len)
|
|||
if (len == SYSEX_SIZE(master)) {
|
||||
dev_master(d, x->u.master.coarse);
|
||||
if (d->master_enabled) {
|
||||
dev_onval(d, CTLADDR_MASTER,
|
||||
ctl_onval(CTL_DEV_MASTER, d, NULL,
|
||||
x->u.master.coarse);
|
||||
}
|
||||
}
|
||||
|
@ -1006,14 +1009,16 @@ dev_master(struct dev *d, unsigned int master)
|
|||
if (d->mode & MODE_PLAY)
|
||||
dev_mix_adjvol(d);
|
||||
} else {
|
||||
for (c = d->ctl_list; c != NULL; c = c->next) {
|
||||
for (c = ctl_list; c != NULL; c = c->next) {
|
||||
if (c->scope != CTL_HW || c->u.hw.dev != d)
|
||||
continue;
|
||||
if (c->type != CTL_NUM ||
|
||||
strcmp(c->group, "") != 0 ||
|
||||
strcmp(c->group, d->name) != 0 ||
|
||||
strcmp(c->node0.name, "output") != 0 ||
|
||||
strcmp(c->func, "level") != 0)
|
||||
continue;
|
||||
v = (master * c->maxval + 64) / 127;
|
||||
dev_setctl(d, c->addr, v);
|
||||
ctl_setval(c, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1071,7 +1076,7 @@ dev_new(char *path, struct aparams *par,
|
|||
d->master = MIDI_MAXCTL;
|
||||
d->mtc.origin = 0;
|
||||
d->tstate = MMC_STOP;
|
||||
d->ctl_list = NULL;
|
||||
snprintf(d->name, CTL_NAMEMAX, "%u", d->num);
|
||||
d->next = dev_list;
|
||||
dev_list = d;
|
||||
return d;
|
||||
|
@ -1217,10 +1222,8 @@ dev_allocbufs(struct dev *d)
|
|||
int
|
||||
dev_open(struct dev *d)
|
||||
{
|
||||
int i;
|
||||
char name[CTL_NAMEMAX];
|
||||
struct dev_alt *a;
|
||||
struct slot *s;
|
||||
|
||||
d->master_enabled = 0;
|
||||
d->mode = d->reqmode;
|
||||
|
@ -1244,23 +1247,12 @@ dev_open(struct dev *d)
|
|||
if (!dev_allocbufs(d))
|
||||
return 0;
|
||||
|
||||
for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
|
||||
if (s->opt == NULL || s->opt->dev != d || s->name[0] == 0)
|
||||
continue;
|
||||
slot_ctlname(s, name, CTL_NAMEMAX);
|
||||
dev_addctl(d, "app", CTL_NUM,
|
||||
CTLADDR_SLOT_LEVEL(i),
|
||||
name, -1, "level",
|
||||
NULL, -1, 127, s->vol);
|
||||
}
|
||||
|
||||
/* if there are multiple alt devs, add server.device knob */
|
||||
if (d->alt_list->next != NULL) {
|
||||
for (a = d->alt_list; a != NULL; a = a->next) {
|
||||
snprintf(name, sizeof(name), "%d", a->idx);
|
||||
dev_addctl(d, "", CTL_SEL,
|
||||
CTLADDR_ALT_SEL + a->idx,
|
||||
"server", -1, "device",
|
||||
ctl_new(CTL_DEV_ALT, d, &a->idx,
|
||||
CTL_SEL, d->name, "server", -1, "device",
|
||||
name, -1, 1, a->idx == d->alt_num);
|
||||
}
|
||||
}
|
||||
|
@ -1335,17 +1327,19 @@ dev_freebufs(struct dev *d)
|
|||
void
|
||||
dev_close(struct dev *d)
|
||||
{
|
||||
struct ctl *c;
|
||||
struct dev_alt *a;
|
||||
unsigned int idx;
|
||||
|
||||
d->pstate = DEV_CFG;
|
||||
dev_sio_close(d);
|
||||
dev_freebufs(d);
|
||||
|
||||
/* there are no clients, just free remaining local controls */
|
||||
while ((c = d->ctl_list) != NULL) {
|
||||
d->ctl_list = c->next;
|
||||
xfree(c);
|
||||
if (d->master_enabled) {
|
||||
d->master_enabled = 0;
|
||||
ctl_del(CTL_DEV_MASTER, d, NULL);
|
||||
}
|
||||
for (idx = 0, a = d->alt_list; a != NULL; idx++, a = a->next)
|
||||
ctl_del(CTL_DEV_ALT, d, &idx);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2326,8 +2320,11 @@ ctlslot_new(struct opt *o, struct ctlops *ops, void *arg)
|
|||
return NULL;
|
||||
s->ops = ops;
|
||||
s->arg = arg;
|
||||
for (c = o->dev->ctl_list; c != NULL; c = c->next)
|
||||
for (c = ctl_list; c != NULL; c = c->next) {
|
||||
if (!ctlslot_visible(s, c))
|
||||
continue;
|
||||
c->refs_mask |= s->self;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -2339,7 +2336,7 @@ ctlslot_del(struct ctlslot *s)
|
|||
{
|
||||
struct ctl *c, **pc;
|
||||
|
||||
pc = &s->opt->dev->ctl_list;
|
||||
pc = &ctl_list;
|
||||
while ((c = *pc) != NULL) {
|
||||
c->refs_mask &= ~s->self;
|
||||
if (c->refs_mask == 0) {
|
||||
|
@ -2352,6 +2349,41 @@ ctlslot_del(struct ctlslot *s)
|
|||
dev_unref(s->opt->dev);
|
||||
}
|
||||
|
||||
int
|
||||
ctlslot_visible(struct ctlslot *s, struct ctl *c)
|
||||
{
|
||||
if (s->opt == NULL)
|
||||
return 1;
|
||||
switch (c->scope) {
|
||||
case CTL_HW:
|
||||
case CTL_DEV_MASTER:
|
||||
case CTL_DEV_ALT:
|
||||
return (s->opt->dev == c->u.any.arg0);
|
||||
case CTL_SLOT_LEVEL:
|
||||
return (s->opt->dev == c->u.slot_level.slot->opt->dev);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct ctl *
|
||||
ctlslot_lookup(struct ctlslot *s, int addr)
|
||||
{
|
||||
struct ctl *c;
|
||||
|
||||
c = ctl_list;
|
||||
while (1) {
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
if (c->type != CTL_NONE && c->addr == addr)
|
||||
break;
|
||||
c = c->next;
|
||||
}
|
||||
if (!ctlslot_visible(s, c))
|
||||
return NULL;
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
ctl_node_log(struct ctl_node *c)
|
||||
{
|
||||
|
@ -2388,18 +2420,112 @@ ctl_log(struct ctl *c)
|
|||
}
|
||||
log_puts(" at ");
|
||||
log_putu(c->addr);
|
||||
log_puts(" -> ");
|
||||
switch (c->scope) {
|
||||
case CTL_HW:
|
||||
log_puts("hw:");
|
||||
log_puts(c->u.hw.dev->name);
|
||||
log_puts("/");
|
||||
log_putu(c->u.hw.addr);
|
||||
break;
|
||||
case CTL_DEV_MASTER:
|
||||
log_puts("dev_master:");
|
||||
log_puts(c->u.dev_master.dev->name);
|
||||
break;
|
||||
case CTL_DEV_ALT:
|
||||
log_puts("dev_alt:");
|
||||
log_puts(c->u.dev_alt.dev->name);
|
||||
log_putu(c->u.dev_alt.idx);
|
||||
break;
|
||||
case CTL_SLOT_LEVEL:
|
||||
log_puts("slot_level:");
|
||||
log_puts(c->u.slot_level.slot->name);
|
||||
log_putu(c->u.slot_level.slot->unit);
|
||||
break;
|
||||
default:
|
||||
log_puts("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
ctl_setval(struct ctl *c, int val)
|
||||
{
|
||||
if (c->curval == val) {
|
||||
if (log_level >= 3) {
|
||||
ctl_log(c);
|
||||
log_puts(": already set\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (val < 0 || val > c->maxval) {
|
||||
if (log_level >= 3) {
|
||||
log_putu(val);
|
||||
log_puts(": ctl val out of bounds\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (c->scope) {
|
||||
case CTL_HW:
|
||||
if (log_level >= 3) {
|
||||
ctl_log(c);
|
||||
log_puts(": marked as dirty\n");
|
||||
}
|
||||
c->curval = val;
|
||||
c->dirty = 1;
|
||||
return dev_ref(c->u.hw.dev);
|
||||
case CTL_DEV_MASTER:
|
||||
if (!c->u.dev_master.dev->master_enabled)
|
||||
return 1;
|
||||
dev_master(c->u.dev_master.dev, val);
|
||||
dev_midi_master(c->u.dev_master.dev);
|
||||
c->val_mask = ~0U;
|
||||
c->curval = val;
|
||||
return 1;
|
||||
case CTL_DEV_ALT:
|
||||
dev_setalt (c->u.dev_alt.dev, c->u.dev_alt.idx);
|
||||
return 1;
|
||||
case CTL_SLOT_LEVEL:
|
||||
slot_setvol(c->u.slot_level.slot, val);
|
||||
// XXX change dev_midi_vol() into slot_midi_vol()
|
||||
dev_midi_vol(c->u.slot_level.slot->opt->dev, c->u.slot_level.slot);
|
||||
c->val_mask = ~0U;
|
||||
c->curval = val;
|
||||
return 1;
|
||||
default:
|
||||
if (log_level >= 2) {
|
||||
ctl_log(c);
|
||||
log_puts(": not writable\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* add a ctl
|
||||
*/
|
||||
struct ctl *
|
||||
dev_addctl(struct dev *d, char *gstr, int type, int addr,
|
||||
char *str0, int unit0, char *func, char *str1, int unit1, int maxval, int val)
|
||||
ctl_new(int scope, void *arg0, void *arg1,
|
||||
int type, char *gstr,
|
||||
char *str0, int unit0, char *func,
|
||||
char *str1, int unit1, int maxval, int val)
|
||||
{
|
||||
struct ctl *c, **pc;
|
||||
struct ctlslot *s;
|
||||
int addr;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* find the smallest unused addr number and
|
||||
* the last position in the list
|
||||
*/
|
||||
addr = 0;
|
||||
for (pc = &ctl_list; (c = *pc) != NULL; pc = &c->next) {
|
||||
if (c->addr > addr)
|
||||
addr = c->addr;
|
||||
}
|
||||
addr++;
|
||||
|
||||
c = xmalloc(sizeof(struct ctl));
|
||||
c->type = type;
|
||||
strlcpy(c->func, func, CTL_NAMEMAX);
|
||||
|
@ -2411,65 +2537,138 @@ dev_addctl(struct dev *d, char *gstr, int type, int addr,
|
|||
c->node1.unit = unit1;
|
||||
} else
|
||||
memset(&c->node1, 0, sizeof(struct ctl_node));
|
||||
c->scope = scope;
|
||||
c->u.any.arg0 = arg0;
|
||||
switch (scope) {
|
||||
case CTL_HW:
|
||||
c->u.hw.addr = *(unsigned int *)arg1;
|
||||
break;
|
||||
case CTL_DEV_ALT:
|
||||
c->u.dev_alt.idx = *(unsigned int *)arg1;
|
||||
break;
|
||||
default:
|
||||
c->u.any.arg1 = NULL;
|
||||
}
|
||||
c->addr = addr;
|
||||
c->maxval = maxval;
|
||||
c->val_mask = ~0;
|
||||
c->desc_mask = ~0;
|
||||
c->curval = val;
|
||||
c->dirty = 0;
|
||||
c->refs_mask = 0;
|
||||
for (i = 0; i < DEV_NCTLSLOT; i++) {
|
||||
c->refs_mask |= CTL_DEVMASK;
|
||||
if (ctlslot_array[i].ops != NULL)
|
||||
c->refs_mask = CTL_DEVMASK;
|
||||
for (s = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, s++) {
|
||||
if (s->ops == NULL)
|
||||
continue;
|
||||
if (ctlslot_visible(s, c))
|
||||
c->refs_mask |= 1 << i;
|
||||
}
|
||||
for (pc = &d->ctl_list; *pc != NULL; pc = &(*pc)->next)
|
||||
; /* nothing */
|
||||
c->next = NULL;
|
||||
c->next = *pc;
|
||||
*pc = c;
|
||||
#ifdef DEBUG
|
||||
if (log_level >= 3) {
|
||||
dev_log(d);
|
||||
log_puts(": adding ");
|
||||
if (log_level >= 2) {
|
||||
ctl_log(c);
|
||||
log_puts("\n");
|
||||
log_puts(": added\n");
|
||||
}
|
||||
#endif
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
dev_rmctl(struct dev *d, int addr)
|
||||
ctl_update(struct ctl *c)
|
||||
{
|
||||
struct ctlslot *s;
|
||||
unsigned int refs_mask;
|
||||
int i;
|
||||
|
||||
for (s = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, s++) {
|
||||
if (s->ops == NULL)
|
||||
continue;
|
||||
refs_mask = ctlslot_visible(s, c) ? s->self : 0;
|
||||
|
||||
/* nothing to do if no visibility change */
|
||||
if (((c->refs_mask & s->self) ^ refs_mask) == 0)
|
||||
continue;
|
||||
/* if control becomes visble */
|
||||
if (refs_mask)
|
||||
c->refs_mask |= s->self;
|
||||
/* if control is hidden */
|
||||
c->desc_mask |= s->self;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
ctl_match(struct ctl *c, int scope, void *arg0, void *arg1)
|
||||
{
|
||||
if (c->type == CTL_NONE || c->scope != scope || c->u.any.arg0 != arg0)
|
||||
return 0;
|
||||
if (arg0 != NULL && c->u.any.arg0 != arg0)
|
||||
return 0;
|
||||
switch (scope) {
|
||||
case CTL_HW:
|
||||
if (arg1 != NULL && c->u.hw.addr != *(unsigned int *)arg1)
|
||||
return 0;
|
||||
break;
|
||||
case CTL_DEV_ALT:
|
||||
if (arg1 != NULL && c->u.dev_alt.idx != *(unsigned int *)arg1)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ctl *
|
||||
ctl_find(int scope, void *arg0, void *arg1)
|
||||
{
|
||||
struct ctl *c;
|
||||
|
||||
for (c = ctl_list; c != NULL; c = c->next) {
|
||||
if (ctl_match(c, scope, arg0, arg1))
|
||||
return c;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
ctl_onval(int scope, void *arg0, void *arg1, int val)
|
||||
{
|
||||
struct ctl *c;
|
||||
|
||||
c = ctl_find(scope, arg0, arg1);
|
||||
if (c == NULL)
|
||||
return 0;
|
||||
c->curval = val;
|
||||
c->val_mask = ~0U;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
ctl_del(int scope, void *arg0, void *arg1)
|
||||
{
|
||||
struct ctl *c, **pc;
|
||||
|
||||
pc = &d->ctl_list;
|
||||
pc = &ctl_list;
|
||||
for (;;) {
|
||||
c = *pc;
|
||||
if (c == NULL)
|
||||
return;
|
||||
if (c->type != CTL_NONE && c->addr == addr)
|
||||
break;
|
||||
if (ctl_match(c, scope, arg0, arg1)) {
|
||||
#ifdef DEBUG
|
||||
if (log_level >= 2) {
|
||||
ctl_log(c);
|
||||
log_puts(": removed\n");
|
||||
}
|
||||
#endif
|
||||
c->refs_mask &= ~CTL_DEVMASK;
|
||||
if (c->refs_mask == 0) {
|
||||
*pc = c->next;
|
||||
xfree(c);
|
||||
continue;
|
||||
}
|
||||
c->type = CTL_NONE;
|
||||
c->desc_mask = ~0;
|
||||
}
|
||||
pc = &c->next;
|
||||
}
|
||||
c->type = CTL_NONE;
|
||||
#ifdef DEBUG
|
||||
if (log_level >= 3) {
|
||||
dev_log(d);
|
||||
log_puts(": removing ");
|
||||
ctl_log(c);
|
||||
log_puts(", refs_mask = 0x");
|
||||
log_putx(c->refs_mask);
|
||||
log_puts("\n");
|
||||
}
|
||||
#endif
|
||||
c->refs_mask &= ~CTL_DEVMASK;
|
||||
if (c->refs_mask == 0) {
|
||||
*pc = c->next;
|
||||
xfree(c);
|
||||
return;
|
||||
}
|
||||
c->desc_mask = ~0;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2480,10 +2679,11 @@ dev_ctlsync(struct dev *d)
|
|||
int found, i;
|
||||
|
||||
found = 0;
|
||||
for (c = d->ctl_list; c != NULL; c = c->next) {
|
||||
if (c->addr != CTLADDR_MASTER &&
|
||||
for (c = ctl_list; c != NULL; c = c->next) {
|
||||
if (c->scope == CTL_HW &&
|
||||
c->u.hw.dev == d &&
|
||||
c->type == CTL_NUM &&
|
||||
strcmp(c->group, "") == 0 &&
|
||||
strcmp(c->group, d->name) == 0 &&
|
||||
strcmp(c->node0.name, "output") == 0 &&
|
||||
strcmp(c->func, "level") == 0)
|
||||
found = 1;
|
||||
|
@ -2495,15 +2695,16 @@ dev_ctlsync(struct dev *d)
|
|||
log_puts(": software master level control disabled\n");
|
||||
}
|
||||
d->master_enabled = 0;
|
||||
dev_rmctl(d, CTLADDR_MASTER);
|
||||
ctl_del(CTL_DEV_MASTER, d, NULL);
|
||||
} else if (!d->master_enabled && !found) {
|
||||
if (log_level >= 2) {
|
||||
dev_log(d);
|
||||
log_puts(": software master level control enabled\n");
|
||||
}
|
||||
d->master_enabled = 1;
|
||||
dev_addctl(d, "", CTL_NUM, CTLADDR_MASTER,
|
||||
"output", -1, "level", NULL, -1, 127, d->master);
|
||||
ctl_new(CTL_DEV_MASTER, d, NULL,
|
||||
CTL_NUM, d->name, "output", -1, "level",
|
||||
NULL, -1, 127, d->master);
|
||||
}
|
||||
|
||||
for (s = ctlslot_array, i = DEV_NCTLSLOT; i > 0; i--, s++) {
|
||||
|
@ -2512,95 +2713,6 @@ dev_ctlsync(struct dev *d)
|
|||
}
|
||||
}
|
||||
|
||||
int
|
||||
dev_setctl(struct dev *d, int addr, int val)
|
||||
{
|
||||
struct ctl *c;
|
||||
struct slot *s;
|
||||
int num;
|
||||
|
||||
c = d->ctl_list;
|
||||
for (;;) {
|
||||
if (c == NULL) {
|
||||
if (log_level >= 3) {
|
||||
dev_log(d);
|
||||
log_puts(": ");
|
||||
log_putu(addr);
|
||||
log_puts(": no such ctl address\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (c->type != CTL_NONE && c->addr == addr)
|
||||
break;
|
||||
c = c->next;
|
||||
}
|
||||
if (c->curval == val) {
|
||||
if (log_level >= 3) {
|
||||
ctl_log(c);
|
||||
log_puts(": already set\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (val < 0 || val > c->maxval) {
|
||||
if (log_level >= 3) {
|
||||
dev_log(d);
|
||||
log_puts(": ");
|
||||
log_putu(val);
|
||||
log_puts(": ctl val out of bounds\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (addr >= CTLADDR_END) {
|
||||
if (log_level >= 3) {
|
||||
ctl_log(c);
|
||||
log_puts(": marked as dirty\n");
|
||||
}
|
||||
c->dirty = 1;
|
||||
dev_ref(d);
|
||||
} else {
|
||||
if (addr >= CTLADDR_ALT_SEL) {
|
||||
if (val) {
|
||||
num = addr - CTLADDR_ALT_SEL;
|
||||
dev_setalt(d, num);
|
||||
}
|
||||
return 1;
|
||||
} else if (addr == CTLADDR_MASTER) {
|
||||
if (d->master_enabled) {
|
||||
dev_master(d, val);
|
||||
dev_midi_master(d);
|
||||
}
|
||||
} else {
|
||||
num = addr - CTLADDR_SLOT_LEVEL(0);
|
||||
s = slot_array + num;
|
||||
if (s->opt->dev != d)
|
||||
return 1;
|
||||
slot_setvol(s, val);
|
||||
dev_midi_vol(d, s);
|
||||
}
|
||||
c->val_mask = ~0U;
|
||||
}
|
||||
c->curval = val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
dev_onval(struct dev *d, int addr, int val)
|
||||
{
|
||||
struct ctl *c;
|
||||
|
||||
c = d->ctl_list;
|
||||
for (;;) {
|
||||
if (c == NULL)
|
||||
return 0;
|
||||
if (c->type != CTL_NONE && c->addr == addr)
|
||||
break;
|
||||
c = c->next;
|
||||
}
|
||||
c->curval = val;
|
||||
c->val_mask = ~0U;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
dev_label(struct dev *d, int i)
|
||||
{
|
||||
|
@ -2609,16 +2721,15 @@ dev_label(struct dev *d, int i)
|
|||
|
||||
slot_ctlname(&slot_array[i], name, CTL_NAMEMAX);
|
||||
|
||||
c = d->ctl_list;
|
||||
c = ctl_list;
|
||||
for (;;) {
|
||||
if (c == NULL) {
|
||||
dev_addctl(d, "app", CTL_NUM,
|
||||
CTLADDR_SLOT_LEVEL(i),
|
||||
name, -1, "level",
|
||||
ctl_new(CTL_SLOT_LEVEL, slot_array + i, NULL,
|
||||
CTL_NUM, "app", name, -1, "level",
|
||||
NULL, -1, 127, slot_array[i].vol);
|
||||
return;
|
||||
}
|
||||
if (c->addr == CTLADDR_SLOT_LEVEL(i))
|
||||
if (ctl_match(c, CTL_SLOT_LEVEL, slot_array + i, NULL))
|
||||
break;
|
||||
c = c->next;
|
||||
}
|
||||
|
|
66
sndiod/dev.h
66
sndiod/dev.h
|
@ -22,11 +22,6 @@
|
|||
#include "siofile.h"
|
||||
#include "dev_sioctl.h"
|
||||
|
||||
#define CTLADDR_SLOT_LEVEL(n) (n)
|
||||
#define CTLADDR_MASTER (DEV_NSLOT)
|
||||
#define CTLADDR_ALT_SEL (CTLADDR_MASTER + 1)
|
||||
#define CTLADDR_END (CTLADDR_ALT_SEL + DEV_NMAX)
|
||||
|
||||
/*
|
||||
* preallocated audio clients
|
||||
*/
|
||||
|
@ -119,6 +114,7 @@ struct slot {
|
|||
|
||||
struct ctl {
|
||||
struct ctl *next;
|
||||
|
||||
#define CTL_NONE 0 /* deleted */
|
||||
#define CTL_NUM 2 /* number (aka integer value) */
|
||||
#define CTL_SW 3 /* on/off switch, only bit 7 counts */
|
||||
|
@ -126,7 +122,34 @@ struct ctl {
|
|||
#define CTL_LIST 5 /* switch, element of a list */
|
||||
#define CTL_SEL 6 /* element of a selector */
|
||||
unsigned int type; /* one of above */
|
||||
unsigned int addr; /* control address */
|
||||
|
||||
#define CTL_HW 0
|
||||
#define CTL_DEV_MASTER 1
|
||||
#define CTL_DEV_ALT 2
|
||||
#define CTL_SLOT_LEVEL 3
|
||||
unsigned int scope;
|
||||
union {
|
||||
struct {
|
||||
void *arg0;
|
||||
void *arg1;
|
||||
} any;
|
||||
struct {
|
||||
struct dev *dev;
|
||||
unsigned int addr;
|
||||
} hw;
|
||||
struct {
|
||||
struct dev *dev;
|
||||
} dev_master;
|
||||
struct {
|
||||
struct dev *dev;
|
||||
unsigned int idx;
|
||||
} dev_alt;
|
||||
struct {
|
||||
struct slot *slot;
|
||||
} slot_level;
|
||||
} u;
|
||||
|
||||
unsigned int addr; /* slot side control address */
|
||||
#define CTL_NAMEMAX 16 /* max name lenght */
|
||||
char func[CTL_NAMEMAX]; /* parameter function name */
|
||||
char group[CTL_NAMEMAX]; /* group aka namespace */
|
||||
|
@ -160,6 +183,11 @@ struct dev {
|
|||
struct slot *slot_list; /* audio streams attached */
|
||||
struct midi *midi;
|
||||
|
||||
/*
|
||||
* name used for various controls
|
||||
*/
|
||||
char name[CTL_NAMEMAX];
|
||||
|
||||
/*
|
||||
* audio device (while opened)
|
||||
*/
|
||||
|
@ -243,15 +271,10 @@ struct dev {
|
|||
|
||||
unsigned int master; /* software vol. knob */
|
||||
unsigned int master_enabled; /* 1 if h/w has no vo. knob */
|
||||
|
||||
/*
|
||||
* control
|
||||
*/
|
||||
|
||||
struct ctl *ctl_list;
|
||||
};
|
||||
|
||||
extern struct dev *dev_list;
|
||||
extern struct ctl *ctl_list;
|
||||
extern struct slot slot_array[DEV_NSLOT];
|
||||
extern struct ctlslot ctlslot_array[DEV_NCTLSLOT];
|
||||
|
||||
|
@ -307,17 +330,22 @@ void slot_detach(struct slot *);
|
|||
/*
|
||||
* control related functions
|
||||
*/
|
||||
|
||||
struct ctl *ctl_new(int, void *, void *,
|
||||
int, char *, char *, int, char *, char *, int, int, int);
|
||||
void ctl_del(int, void *, void *);
|
||||
void ctl_log(struct ctl *);
|
||||
int ctl_setval(struct ctl *c, int val);
|
||||
int ctl_match(struct ctl *, int, void *, void *);
|
||||
struct ctl *ctl_find(int, void *, void *);
|
||||
void ctl_update(struct ctl *);
|
||||
int ctl_onval(int, void *, void *, int);
|
||||
|
||||
struct ctlslot *ctlslot_new(struct opt *, struct ctlops *, void *);
|
||||
void ctlslot_del(struct ctlslot *);
|
||||
int dev_setctl(struct dev *, int, int);
|
||||
int dev_onval(struct dev *, int, int);
|
||||
int dev_nctl(struct dev *);
|
||||
int ctlslot_visible(struct ctlslot *, struct ctl *);
|
||||
struct ctl *ctlslot_lookup(struct ctlslot *, int);
|
||||
void dev_label(struct dev *, int);
|
||||
struct ctl *dev_addctl(struct dev *, char *, int, int,
|
||||
char *, int, char *, char *, int, int, int);
|
||||
void dev_rmctl(struct dev *, int);
|
||||
int dev_makeunit(struct dev *, char *);
|
||||
void dev_ctlsync(struct dev *);
|
||||
|
||||
#endif /* !defined(DEV_H) */
|
||||
|
|
|
@ -51,34 +51,27 @@ struct fileops dev_sioctl_ops = {
|
|||
void
|
||||
dev_sioctl_ondesc(void *arg, struct sioctl_desc *desc, int val)
|
||||
{
|
||||
#define GROUP_PREFIX "hw"
|
||||
char group_buf[CTL_NAMEMAX], *group;
|
||||
struct dev *d = arg;
|
||||
int addr;
|
||||
char *group, group_buf[CTL_NAMEMAX];
|
||||
|
||||
if (desc == NULL) {
|
||||
dev_ctlsync(d);
|
||||
return;
|
||||
}
|
||||
|
||||
addr = CTLADDR_END + desc->addr;
|
||||
dev_rmctl(d, addr);
|
||||
ctl_del(CTL_HW, d, &desc->addr);
|
||||
|
||||
/*
|
||||
* prefix with "hw/" group names of controls we expose, to
|
||||
* ensure that all controls have unique names when multiple
|
||||
* sndiod's are chained
|
||||
*/
|
||||
if (strcmp(desc->group, "app") == 0 || (desc->group[0] == 0 &&
|
||||
strcmp(desc->node0.name, "server") == 0)) {
|
||||
group = group_buf;
|
||||
if (snprintf(group_buf, CTL_NAMEMAX, GROUP_PREFIX "/%s",
|
||||
desc->group) >= CTL_NAMEMAX)
|
||||
if (desc->group[0] == 0)
|
||||
group = d->name;
|
||||
else {
|
||||
if (snprintf(group_buf, CTL_NAMEMAX, "%s/%s",
|
||||
d->name, desc->group) >= CTL_NAMEMAX)
|
||||
return;
|
||||
} else
|
||||
group = desc->group;
|
||||
group = group_buf;
|
||||
}
|
||||
|
||||
dev_addctl(d, group, desc->type, addr,
|
||||
ctl_new(CTL_HW, d, &desc->addr,
|
||||
desc->type, group,
|
||||
desc->node0.name, desc->node0.unit, desc->func,
|
||||
desc->node1.name, desc->node1.unit, desc->maxval, val);
|
||||
}
|
||||
|
@ -89,8 +82,6 @@ dev_sioctl_onval(void *arg, unsigned int addr, unsigned int val)
|
|||
struct dev *d = arg;
|
||||
struct ctl *c;
|
||||
|
||||
addr += CTLADDR_END;
|
||||
|
||||
dev_log(d);
|
||||
log_puts(": onctl: addr = ");
|
||||
log_putu(addr);
|
||||
|
@ -98,8 +89,8 @@ dev_sioctl_onval(void *arg, unsigned int addr, unsigned int val)
|
|||
log_putu(val);
|
||||
log_puts("\n");
|
||||
|
||||
for (c = d->ctl_list; c != NULL; c = c->next) {
|
||||
if (c->addr != addr)
|
||||
for (c = ctl_list; c != NULL; c = c->next) {
|
||||
if (c->scope != CTL_HW || c->u.hw.addr != addr)
|
||||
continue;
|
||||
ctl_log(c);
|
||||
log_puts(": new value -> ");
|
||||
|
@ -139,9 +130,9 @@ dev_sioctl_close(struct dev *d)
|
|||
struct ctl *c, **pc;
|
||||
|
||||
/* remove controls */
|
||||
pc = &d->ctl_list;
|
||||
pc = &ctl_list;
|
||||
while ((c = *pc) != NULL) {
|
||||
if (c->addr >= CTLADDR_END) {
|
||||
if (c->scope == CTL_HW && c->u.hw.dev == d) {
|
||||
c->refs_mask &= ~CTL_DEVMASK;
|
||||
if (c->refs_mask == 0) {
|
||||
*pc = c->next;
|
||||
|
@ -163,8 +154,8 @@ dev_sioctl_pollfd(void *arg, struct pollfd *pfd)
|
|||
struct ctl *c;
|
||||
int events = 0;
|
||||
|
||||
for (c = d->ctl_list; c != NULL; c = c->next) {
|
||||
if (c->dirty)
|
||||
for (c = ctl_list; c != NULL; c = c->next) {
|
||||
if (c->scope == CTL_HW && c->u.hw.dev == d && c->dirty)
|
||||
events |= POLLOUT;
|
||||
}
|
||||
return sioctl_pollfd(d->sioctl.hdl, pfd, events);
|
||||
|
@ -196,11 +187,10 @@ dev_sioctl_out(void *arg)
|
|||
* we've finished iterating on it.
|
||||
*/
|
||||
cnt = 0;
|
||||
for (c = d->ctl_list; c != NULL; c = c->next) {
|
||||
if (!c->dirty)
|
||||
for (c = ctl_list; c != NULL; c = c->next) {
|
||||
if (c->scope != CTL_HW || c->u.hw.dev != d || !c->dirty)
|
||||
continue;
|
||||
if (!sioctl_setval(d->sioctl.hdl,
|
||||
c->addr - CTLADDR_END, c->curval)) {
|
||||
if (!sioctl_setval(d->sioctl.hdl, c->u.hw.addr, c->curval)) {
|
||||
ctl_log(c);
|
||||
log_puts(": set failed\n");
|
||||
break;
|
||||
|
|
|
@ -157,11 +157,10 @@ dev_sio_openlist(struct dev *d,
|
|||
log_puts("\n");
|
||||
}
|
||||
d->alt_num = n->idx;
|
||||
for (c = d->ctl_list; c != NULL; c = c->next) {
|
||||
if (c->addr < CTLADDR_ALT_SEL ||
|
||||
c->addr >= CTLADDR_ALT_SEL + DEV_NMAX)
|
||||
for (c = ctl_list; c != NULL; c = c->next) {
|
||||
if (!ctl_match(c, CTL_DEV_ALT, d, NULL))
|
||||
continue;
|
||||
val = (c->addr - CTLADDR_ALT_SEL) == n->idx;
|
||||
val = c->u.dev_alt.idx == n->idx;
|
||||
if (c->curval == val)
|
||||
continue;
|
||||
c->curval = val;
|
||||
|
|
|
@ -1229,8 +1229,7 @@ sock_execmsg(struct sock *f)
|
|||
f->lastvol = ctl; /* dont trigger feedback message */
|
||||
slot_setvol(s, ctl);
|
||||
dev_midi_vol(s->opt->dev, s);
|
||||
dev_onval(s->opt->dev,
|
||||
CTLADDR_SLOT_LEVEL(f->slot - slot_array), ctl);
|
||||
ctl_onval(CTL_SLOT_LEVEL, s, NULL, ctl);
|
||||
break;
|
||||
case AMSG_CTLSUB:
|
||||
#ifdef DEBUG
|
||||
|
@ -1256,9 +1255,10 @@ sock_execmsg(struct sock *f)
|
|||
if (m->u.ctlsub.desc) {
|
||||
if (!(f->ctlops & SOCK_CTLDESC)) {
|
||||
ctl = f->ctlslot->self;
|
||||
c = f->ctlslot->opt->dev->ctl_list;
|
||||
c = ctl_list;
|
||||
while (c != NULL) {
|
||||
c->desc_mask |= ctl;
|
||||
if (ctlslot_visible(f->ctlslot, c))
|
||||
c->desc_mask |= ctl;
|
||||
c = c->next;
|
||||
}
|
||||
f->ctlops |= SOCK_CTLDESC;
|
||||
|
@ -1290,13 +1290,23 @@ sock_execmsg(struct sock *f)
|
|||
sock_close(f);
|
||||
return 0;
|
||||
}
|
||||
if (!dev_setctl(f->ctlslot->opt->dev,
|
||||
ntohs(m->u.ctlset.addr),
|
||||
ntohs(m->u.ctlset.val))) {
|
||||
|
||||
c = ctlslot_lookup(f->ctlslot, ntohs(m->u.ctlset.addr));
|
||||
if (c == NULL) {
|
||||
#ifdef DEBUG
|
||||
if (log_level >= 1) {
|
||||
sock_log(f);
|
||||
log_puts(": CTLSET, wrong addr/val\n");
|
||||
log_puts(": CTLSET, wrong addr\n");
|
||||
}
|
||||
#endif
|
||||
sock_close(f);
|
||||
return 0;
|
||||
}
|
||||
if (!ctl_setval(c, ntohs(m->u.ctlset.val))) {
|
||||
#ifdef DEBUG
|
||||
if (log_level >= 1) {
|
||||
sock_log(f);
|
||||
log_puts(": CTLSET, bad value\n");
|
||||
}
|
||||
#endif
|
||||
sock_close(f);
|
||||
|
@ -1392,7 +1402,7 @@ sock_execmsg(struct sock *f)
|
|||
int
|
||||
sock_buildmsg(struct sock *f)
|
||||
{
|
||||
unsigned int size, mask;
|
||||
unsigned int size, type, mask;
|
||||
struct amsg_ctl_desc *desc;
|
||||
struct ctl *c, **pc;
|
||||
|
||||
|
@ -1544,7 +1554,7 @@ sock_buildmsg(struct sock *f)
|
|||
desc = f->ctldesc;
|
||||
mask = f->ctlslot->self;
|
||||
size = 0;
|
||||
pc = &f->ctlslot->opt->dev->ctl_list;
|
||||
pc = &ctl_list;
|
||||
while ((c = *pc) != NULL) {
|
||||
if ((c->desc_mask & mask) == 0 ||
|
||||
(c->refs_mask & mask) == 0) {
|
||||
|
@ -1556,7 +1566,11 @@ sock_buildmsg(struct sock *f)
|
|||
break;
|
||||
c->desc_mask &= ~mask;
|
||||
c->val_mask &= ~mask;
|
||||
strlcpy(desc->group, c->group,
|
||||
type = ctlslot_visible(f->ctlslot, c) ?
|
||||
c->type : CTL_NONE;
|
||||
strlcpy(desc->group, (f->ctlslot->opt == NULL ||
|
||||
strcmp(c->group, f->ctlslot->opt->dev->name) != 0) ?
|
||||
c->group : "",
|
||||
AMSG_CTL_NAMEMAX);
|
||||
strlcpy(desc->node0.name, c->node0.name,
|
||||
AMSG_CTL_NAMEMAX);
|
||||
|
@ -1564,7 +1578,7 @@ sock_buildmsg(struct sock *f)
|
|||
strlcpy(desc->node1.name, c->node1.name,
|
||||
AMSG_CTL_NAMEMAX);
|
||||
desc->node1.unit = ntohs(c->node1.unit);
|
||||
desc->type = c->type;
|
||||
desc->type = type;
|
||||
strlcpy(desc->func, c->func, AMSG_CTL_NAMEMAX);
|
||||
desc->addr = htons(c->addr);
|
||||
desc->maxval = htons(c->maxval);
|
||||
|
@ -1573,7 +1587,7 @@ sock_buildmsg(struct sock *f)
|
|||
desc++;
|
||||
|
||||
/* if this is a deleted entry unref it */
|
||||
if (c->type == CTL_NONE) {
|
||||
if (type == CTL_NONE) {
|
||||
c->refs_mask &= ~mask;
|
||||
if (c->refs_mask == 0) {
|
||||
*pc = c->next;
|
||||
|
@ -1601,7 +1615,9 @@ sock_buildmsg(struct sock *f)
|
|||
}
|
||||
if (f->ctlslot && (f->ctlops & SOCK_CTLVAL)) {
|
||||
mask = f->ctlslot->self;
|
||||
for (c = f->ctlslot->opt->dev->ctl_list; c != NULL; c = c->next) {
|
||||
for (c = ctl_list; c != NULL; c = c->next) {
|
||||
if (!ctlslot_visible(f->ctlslot, c))
|
||||
continue;
|
||||
if ((c->val_mask & mask) == 0)
|
||||
continue;
|
||||
c->val_mask &= ~mask;
|
||||
|
|
Loading…
Reference in New Issue