Annotation of src/usr.bin/sndiod/opt.c, Revision 1.10
1.10 ! ratchov 1: /* $OpenBSD: opt.c,v 1.9 2021/11/01 14:43:25 ratchov Exp $ */
1.1 ratchov 2: /*
3: * Copyright (c) 2008-2011 Alexandre Ratchov <alex@caoua.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: #include <string.h>
18:
19: #include "dev.h"
1.7 ratchov 20: #include "midi.h"
1.1 ratchov 21: #include "opt.h"
1.7 ratchov 22: #include "sysex.h"
1.1 ratchov 23: #include "utils.h"
24:
1.5 ratchov 25: struct opt *opt_list;
26:
1.7 ratchov 27: void opt_midi_imsg(void *, unsigned char *, int);
28: void opt_midi_omsg(void *, unsigned char *, int);
29: void opt_midi_fill(void *, int);
30: void opt_midi_exit(void *);
31:
32: struct midiops opt_midiops = {
33: opt_midi_imsg,
34: opt_midi_omsg,
35: opt_midi_fill,
36: opt_midi_exit
37: };
38:
39: void
40: opt_midi_imsg(void *arg, unsigned char *msg, int len)
41: {
42: #ifdef DEBUG
43: struct opt *o = arg;
44:
45: log_puts(o->name);
46: log_puts(": can't receive midi messages\n");
47: panic();
48: #endif
49: }
50:
51: void
52: opt_midi_omsg(void *arg, unsigned char *msg, int len)
53: {
54: struct opt *o = arg;
55: struct sysex *x;
56: unsigned int fps, chan;
57:
58: if ((msg[0] & MIDI_CMDMASK) == MIDI_CTL && msg[1] == MIDI_CTL_VOL) {
59: chan = msg[0] & MIDI_CHANMASK;
60: if (chan >= DEV_NSLOT)
61: return;
1.9 ratchov 62: if (slot_array[chan].opt != o)
1.7 ratchov 63: return;
64: slot_setvol(slot_array + chan, msg[2]);
65: ctl_onval(CTL_SLOT_LEVEL, slot_array + chan, NULL, msg[2]);
66: return;
67: }
68: x = (struct sysex *)msg;
69: if (x->start != SYSEX_START)
70: return;
71: if (len < SYSEX_SIZE(empty))
72: return;
73: switch (x->type) {
74: case SYSEX_TYPE_RT:
75: if (x->id0 == SYSEX_CONTROL && x->id1 == SYSEX_MASTER) {
76: if (len == SYSEX_SIZE(master)) {
77: dev_master(o->dev, x->u.master.coarse);
78: if (o->dev->master_enabled) {
79: ctl_onval(CTL_DEV_MASTER, o->dev, NULL,
80: x->u.master.coarse);
81: }
82: }
83: return;
84: }
85: if (x->id0 != SYSEX_MMC)
86: return;
87: switch (x->id1) {
88: case SYSEX_MMC_STOP:
89: if (len != SYSEX_SIZE(stop))
90: return;
1.8 ratchov 91: if (o->mtc == NULL)
1.7 ratchov 92: return;
1.9 ratchov 93: mtc_setdev(o->mtc, o->dev);
1.7 ratchov 94: if (log_level >= 2) {
95: log_puts(o->name);
96: log_puts(": mmc stop\n");
97: }
1.8 ratchov 98: mtc_stop(o->mtc);
1.7 ratchov 99: break;
100: case SYSEX_MMC_START:
101: if (len != SYSEX_SIZE(start))
102: return;
1.8 ratchov 103: if (o->mtc == NULL)
1.7 ratchov 104: return;
1.9 ratchov 105: mtc_setdev(o->mtc, o->dev);
1.7 ratchov 106: if (log_level >= 2) {
107: log_puts(o->name);
108: log_puts(": mmc start\n");
109: }
1.8 ratchov 110: mtc_start(o->mtc);
1.7 ratchov 111: break;
112: case SYSEX_MMC_LOC:
113: if (len != SYSEX_SIZE(loc) ||
114: x->u.loc.len != SYSEX_MMC_LOC_LEN ||
115: x->u.loc.cmd != SYSEX_MMC_LOC_CMD)
116: return;
1.8 ratchov 117: if (o->mtc == NULL)
1.7 ratchov 118: return;
1.9 ratchov 119: mtc_setdev(o->mtc, o->dev);
1.7 ratchov 120: switch (x->u.loc.hr >> 5) {
121: case MTC_FPS_24:
122: fps = 24;
123: break;
124: case MTC_FPS_25:
125: fps = 25;
126: break;
127: case MTC_FPS_30:
128: fps = 30;
129: break;
130: default:
1.8 ratchov 131: mtc_stop(o->mtc);
1.7 ratchov 132: return;
133: }
1.8 ratchov 134: mtc_loc(o->mtc,
1.7 ratchov 135: (x->u.loc.hr & 0x1f) * 3600 * MTC_SEC +
136: x->u.loc.min * 60 * MTC_SEC +
137: x->u.loc.sec * MTC_SEC +
138: x->u.loc.fr * (MTC_SEC / fps));
139: break;
140: }
141: break;
142: case SYSEX_TYPE_EDU:
143: if (x->id0 != SYSEX_AUCAT || x->id1 != SYSEX_AUCAT_DUMPREQ)
144: return;
145: if (len != SYSEX_SIZE(dumpreq))
146: return;
147: dev_midi_dump(o->dev);
148: break;
149: }
150: }
151:
152: void
153: opt_midi_fill(void *arg, int count)
154: {
155: /* nothing to do */
156: }
157:
158: void
159: opt_midi_exit(void *arg)
160: {
161: struct opt *o = arg;
162:
163: if (log_level >= 1) {
164: log_puts(o->name);
165: log_puts(": midi end point died\n");
166: panic();
167: }
168: }
169:
1.1 ratchov 170: /*
171: * create a new audio sub-device "configuration"
172: */
173: struct opt *
1.4 ratchov 174: opt_new(struct dev *d, char *name,
1.1 ratchov 175: int pmin, int pmax, int rmin, int rmax,
176: int maxweight, int mmc, int dup, unsigned int mode)
177: {
1.9 ratchov 178: struct dev *a;
1.6 ratchov 179: struct opt *o, **po;
180: unsigned int len, num;
1.1 ratchov 181: char c;
182:
1.9 ratchov 183: if (name == NULL) {
184: name = d->name;
185: len = strlen(name);
186: } else {
187: for (len = 0; name[len] != '\0'; len++) {
188: if (len == OPT_NAMEMAX) {
189: log_puts(name);
190: log_puts(": too long\n");
191: return NULL;
192: }
193: c = name[len];
194: if ((c < 'a' || c > 'z') &&
195: (c < 'A' || c > 'Z')) {
196: log_puts(name);
197: log_puts(": only alphabetic chars allowed\n");
198: return NULL;
199: }
1.1 ratchov 200: }
201: }
1.6 ratchov 202: num = 0;
203: for (po = &opt_list; *po != NULL; po = &(*po)->next)
204: num++;
205: if (num >= OPT_NMAX) {
206: log_puts(name);
207: log_puts(": too many opts\n");
208: return NULL;
209: }
1.9 ratchov 210:
211: if (opt_byname(name)) {
1.6 ratchov 212: log_puts(name);
213: log_puts(": already defined\n");
214: return NULL;
215: }
1.8 ratchov 216:
217: if (mmc) {
218: if (mtc_array[0].dev != NULL && mtc_array[0].dev != d) {
219: log_puts(name);
220: log_puts(": MTC already setup for another device\n");
221: return NULL;
222: }
223: mtc_array[0].dev = d;
224: if (log_level >= 2) {
225: dev_log(d);
226: log_puts(": initial MTC source, controlled by MMC\n");
227: }
228: }
229:
1.9 ratchov 230: if (strcmp(d->name, name) == 0)
231: a = d;
232: else {
233: /* circulate to the first "alternate" device (greatest num) */
234: for (a = d; a->alt_next->num > a->num; a = a->alt_next)
235: ;
236: }
237:
1.1 ratchov 238: o = xmalloc(sizeof(struct opt));
1.6 ratchov 239: o->num = num;
1.9 ratchov 240: o->alt_first = o->dev = a;
241: o->refcnt = 0;
1.7 ratchov 242:
243: /*
244: * XXX: below, we allocate a midi input buffer, since we don't
245: * receive raw midi data, so no need to allocate a input
246: * ibuf. Possibly set imsg & fill callbacks to NULL and
247: * use this to in midi_new() to check if buffers need to be
248: * allocated
249: */
250: o->midi = midi_new(&opt_midiops, o, MODE_MIDIIN | MODE_MIDIOUT);
251: midi_tag(o->midi, o->num);
252:
1.1 ratchov 253: if (mode & MODE_PLAY) {
254: o->pmin = pmin;
255: o->pmax = pmax;
256: }
257: if (mode & MODE_RECMASK) {
258: o->rmin = rmin;
259: o->rmax = rmax;
260: }
261: o->maxweight = maxweight;
1.8 ratchov 262: o->mtc = mmc ? &mtc_array[0] : NULL;
1.1 ratchov 263: o->dup = dup;
264: o->mode = mode;
265: memcpy(o->name, name, len + 1);
1.6 ratchov 266: o->next = *po;
267: *po = o;
1.1 ratchov 268: if (log_level >= 2) {
1.4 ratchov 269: dev_log(d);
1.1 ratchov 270: log_puts(".");
271: log_puts(o->name);
272: log_puts(":");
273: if (o->mode & MODE_REC) {
274: log_puts(" rec=");
275: log_putu(o->rmin);
276: log_puts(":");
277: log_putu(o->rmax);
278: }
279: if (o->mode & MODE_PLAY) {
280: log_puts(" play=");
281: log_putu(o->pmin);
282: log_puts(":");
283: log_putu(o->pmax);
284: log_puts(" vol=");
285: log_putu(o->maxweight);
286: }
287: if (o->mode & MODE_MON) {
288: log_puts(" mon=");
289: log_putu(o->rmin);
290: log_puts(":");
291: log_putu(o->rmax);
292: }
293: if (o->mode & (MODE_RECMASK | MODE_PLAY)) {
1.8 ratchov 294: if (o->mtc)
295: log_puts(" mtc");
1.1 ratchov 296: if (o->dup)
297: log_puts(" dup");
298: }
299: log_puts("\n");
300: }
301: return o;
302: }
303:
304: struct opt *
1.9 ratchov 305: opt_byname(char *name)
1.1 ratchov 306: {
307: struct opt *o;
308:
1.5 ratchov 309: for (o = opt_list; o != NULL; o = o->next) {
1.1 ratchov 310: if (strcmp(name, o->name) == 0)
311: return o;
312: }
313: return NULL;
314: }
315:
1.9 ratchov 316: struct opt *
317: opt_bynum(int num)
318: {
319: struct opt *o;
320:
321: for (o = opt_list; o != NULL; o = o->next) {
322: if (o->num == num)
323: return o;
324: }
325: return NULL;
326: }
327:
1.1 ratchov 328: void
1.5 ratchov 329: opt_del(struct opt *o)
1.1 ratchov 330: {
331: struct opt **po;
332:
1.5 ratchov 333: for (po = &opt_list; *po != o; po = &(*po)->next) {
1.1 ratchov 334: #ifdef DEBUG
335: if (*po == NULL) {
336: log_puts("opt_del: not on list\n");
337: panic();
338: }
339: #endif
340: }
1.7 ratchov 341: midi_del(o->midi);
1.1 ratchov 342: *po = o->next;
343: xfree(o);
1.9 ratchov 344: }
345:
346: void
347: opt_init(struct opt *o)
348: {
349: struct dev *d;
350:
351: if (strcmp(o->name, o->dev->name) != 0) {
352: for (d = dev_list; d != NULL; d = d->next) {
353: ctl_new(CTL_OPT_DEV, o, d,
354: CTL_SEL, o->name, "server", -1, "device",
355: d->name, -1, 1, o->dev == d);
356: }
357: }
358: }
359:
360: void
361: opt_done(struct opt *o)
362: {
363: struct dev *d;
364:
365: if (o->refcnt != 0) {
366: // XXX: all clients are already kicked, so this never happens
367: log_puts(o->name);
368: log_puts(": still has refs\n");
369: }
370: for (d = dev_list; d != NULL; d = d->next)
371: ctl_del(CTL_OPT_DEV, o, d);
372: }
373:
374: /*
375: * Set opt's device, and (if necessary) move clients to
376: * to the new device
377: */
1.10 ! ratchov 378: int
1.9 ratchov 379: opt_setdev(struct opt *o, struct dev *ndev)
380: {
381: struct dev *odev;
382: struct ctl *c;
383: struct ctlslot *p;
384: struct slot *s;
385: int i;
386:
387: if (!dev_ref(ndev))
1.10 ! ratchov 388: return 0;
1.9 ratchov 389:
390: odev = o->dev;
391: if (odev == ndev) {
392: dev_unref(ndev);
1.10 ! ratchov 393: return 1;
1.9 ratchov 394: }
395:
396: /* check if clients can use new device */
397: for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
398: if (s->opt != o)
399: continue;
400: if (s->ops != NULL && !dev_iscompat(odev, ndev)) {
401: dev_unref(ndev);
1.10 ! ratchov 402: return 0;
1.9 ratchov 403: }
404: }
405:
406: /*
407: * if we're using MMC, move all opts to the new device, mtc_setdev()
408: * will call us back
1.10 ! ratchov 409: *
! 410: * XXX: move this to the end to avoid the recursion
1.9 ratchov 411: */
412: if (o->mtc != NULL && o->mtc->dev != ndev) {
413: mtc_setdev(o->mtc, ndev);
414: dev_unref(ndev);
1.10 ! ratchov 415: return 1;
1.9 ratchov 416: }
417:
418: c = ctl_find(CTL_OPT_DEV, o, o->dev);
419: if (c != NULL)
420: c->curval = 0;
421:
422: /* detach clients from old device */
423: for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
424: if (s->opt != o)
425: continue;
426:
427: if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP)
428: slot_detach(s);
429: }
430:
431: o->dev = ndev;
432:
433: if (o->refcnt > 0) {
434: dev_unref(odev);
435: dev_ref(o->dev);
436: }
437:
438: c = ctl_find(CTL_OPT_DEV, o, o->dev);
439: if (c != NULL) {
440: c->curval = 1;
441: c->val_mask = ~0;
442: }
443:
444: /* attach clients to new device */
445: for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
446: if (s->opt != o)
447: continue;
448:
449: if (ndev != odev) {
450: dev_midi_slotdesc(odev, s);
451: dev_midi_slotdesc(ndev, s);
452: dev_midi_vol(ndev, s);
453: }
454:
455: c = ctl_find(CTL_SLOT_LEVEL, s, NULL);
456: ctl_update(c);
457:
458: if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP) {
459: slot_initconv(s);
460: slot_attach(s);
461: }
462: }
463:
464: /* move controlling clients to new device */
465: for (p = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, p++) {
466: if (p->ops == NULL)
467: continue;
468: if (p->opt == o)
469: ctlslot_update(p);
470: }
471:
472: dev_unref(ndev);
1.10 ! ratchov 473: return 1;
1.9 ratchov 474: }
475:
476: /*
477: * Get a reference to opt's device
478: */
479: struct dev *
480: opt_ref(struct opt *o)
481: {
482: struct dev *d;
483:
484: if (o->refcnt == 0) {
485: if (strcmp(o->name, o->dev->name) == 0) {
486: if (!dev_ref(o->dev))
487: return NULL;
488: } else {
489: /* find first working one */
490: d = o->alt_first;
491: while (1) {
492: if (dev_ref(d))
493: break;
494: d = d->alt_next;
495: if (d == o->alt_first)
496: return NULL;
497: }
498:
499: /* if device changed, move everything to the new one */
500: if (d != o->dev)
501: opt_setdev(o, d);
502: }
503: }
504:
505: o->refcnt++;
506: return o->dev;
507: }
508:
509: /*
510: * Release opt's device
511: */
512: void
513: opt_unref(struct opt *o)
514: {
515: o->refcnt--;
516: if (o->refcnt == 0)
517: dev_unref(o->dev);
1.1 ratchov 518: }