Moving to a global server-wide controls list is necessary to expose
controls that are not associated to a particular device (ex. a device
selector).

The current hack to use the device-side sioctl_desc->addr variable as
client-side key can't work anymore. So, we use a unique dynamically
allocated ctl->addr key; this is which much cleaner. A new "scope"
enum (with two "void *" arguments) is used to determine what the
control does control. This adds a lot of flexibility and allows to
easily add new control types that are not associated to devices.

The diff touches many parts of the code and is quite big. Please test
and report regressions.

Besides that no behavior change.

OK?

Index: dev.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dev.c,v
retrieving revision 1.89
diff -u -p -u -p -r1.89 dev.c
--- dev.c       29 Jan 2021 11:38:23 -0000      1.89
+++ dev.c       29 Jan 2021 11:39:08 -0000
@@ -106,6 +106,7 @@ struct slotops zomb_slotops = {
        zomb_exit
 };
 
+struct ctl *ctl_list = NULL;
 struct dev *dev_list = NULL;
 unsigned int dev_sndnum = 0;
 
@@ -370,12 +371,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;
@@ -465,7 +468,7 @@ dev_midi_omsg(void *arg, unsigned char *
                    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;
@@ -479,7 +482,7 @@ dev_midi_omsg(void *arg, unsigned char *
                        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);
                                }
                        }
@@ -1005,14 +1008,16 @@ dev_master(struct dev *d, unsigned int m
                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);
                }
        }
 }
@@ -1070,7 +1075,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;
@@ -1208,10 +1213,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;
@@ -1235,23 +1238,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);
                }
        }
@@ -1326,17 +1318,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);
 }
 
 /*
@@ -2325,8 +2319,11 @@ ctlslot_new(struct opt *o, struct ctlops
                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;
 }
 
@@ -2338,7 +2335,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) {
@@ -2351,6 +2348,37 @@ 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;
+
+       for (c = ctl_list; c != NULL; c = c->next) {
+               if (c->type != CTL_NONE && c->addr == addr)
+                       break;
+       }
+       if (!ctlslot_visible(s, c))
+               return NULL;
+       return c;
+}
+
 void
 ctl_node_log(struct ctl_node *c)
 {
@@ -2387,18 +2415,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);
@@ -2410,65 +2532,138 @@ dev_addctl(struct dev *d, char *gstr, in
                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;
-               pc = &c->next;
-       }
-       c->type = CTL_NONE;
+               if (ctl_match(c, scope, arg0, arg1)) {
 #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");
-       }
+                       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);
-               return;
+                       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->desc_mask = ~0;
 }
 
 void
@@ -2479,10 +2674,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;
@@ -2494,15 +2690,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++) {
@@ -2511,95 +2708,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)
 {
@@ -2608,16 +2716,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;
        }
Index: dev.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dev.h,v
retrieving revision 1.37
diff -u -p -u -p -r1.37 dev.h
--- dev.h       29 Jan 2021 11:38:23 -0000      1.37
+++ dev.h       29 Jan 2021 11:39:08 -0000
@@ -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 */
@@ -161,6 +184,11 @@ struct dev {
        struct midi *midi;
 
        /*
+        * name used for various controls
+        */
+       char name[CTL_NAMEMAX];
+
+       /*
         * audio device (while opened)
         */
        struct dev_sio sio;
@@ -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) */
Index: dev_sioctl.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dev_sioctl.c,v
retrieving revision 1.6
diff -u -p -u -p -r1.6 dev_sioctl.c
--- dev_sioctl.c        28 Jun 2020 05:21:39 -0000      1.6
+++ dev_sioctl.c        29 Jan 2021 11:39:09 -0000
@@ -50,34 +50,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);
 }
@@ -88,8 +81,6 @@ dev_sioctl_onval(void *arg, unsigned int
        struct dev *d = arg;
        struct ctl *c;
 
-       addr += CTLADDR_END;
-
        dev_log(d);
        log_puts(": onctl: addr = ");
        log_putu(addr);
@@ -97,8 +88,8 @@ dev_sioctl_onval(void *arg, unsigned int
        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 -> ");
@@ -138,9 +129,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;
@@ -162,8 +153,8 @@ dev_sioctl_pollfd(void *arg, struct poll
        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);
@@ -195,11 +186,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;
Index: siofile.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/siofile.c,v
retrieving revision 1.22
diff -u -p -u -p -r1.22 siofile.c
--- siofile.c   28 Jun 2020 05:21:39 -0000      1.22
+++ siofile.c   29 Jan 2021 11:39:09 -0000
@@ -119,11 +119,10 @@ dev_sio_openlist(struct dev *d, unsigned
                                }
                        }
                        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;
Index: sock.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/sock.c,v
retrieving revision 1.41
diff -u -p -u -p -r1.41 sock.c
--- sock.c      29 Jan 2021 11:38:23 -0000      1.41
+++ sock.c      29 Jan 2021 11:39:09 -0000
@@ -1240,8 +1240,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
@@ -1267,9 +1266,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;
@@ -1301,13 +1301,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\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, wrong addr/val\n");
+                               log_puts(": CTLSET, bad value\n");
                        }
 #endif
                        sock_close(f);
@@ -1403,7 +1413,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;
 
@@ -1555,7 +1565,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) {
@@ -1567,7 +1577,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);
@@ -1575,7 +1589,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);
@@ -1584,7 +1598,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;
@@ -1612,7 +1626,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;

Reply via email to