xref: /openbsd-src/usr.bin/sndiod/opt.c (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1 /*	$OpenBSD: opt.c,v 1.9 2021/11/01 14:43:25 ratchov Exp $	*/
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"
20 #include "midi.h"
21 #include "opt.h"
22 #include "sysex.h"
23 #include "utils.h"
24 
25 struct opt *opt_list;
26 
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;
62 		if (slot_array[chan].opt != o)
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;
91 			if (o->mtc == NULL)
92 				return;
93 			mtc_setdev(o->mtc, o->dev);
94 			if (log_level >= 2) {
95 				log_puts(o->name);
96 				log_puts(": mmc stop\n");
97 			}
98 			mtc_stop(o->mtc);
99 			break;
100 		case SYSEX_MMC_START:
101 			if (len != SYSEX_SIZE(start))
102 				return;
103 			if (o->mtc == NULL)
104 				return;
105 			mtc_setdev(o->mtc, o->dev);
106 			if (log_level >= 2) {
107 				log_puts(o->name);
108 				log_puts(": mmc start\n");
109 			}
110 			mtc_start(o->mtc);
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;
117 			if (o->mtc == NULL)
118 				return;
119 			mtc_setdev(o->mtc, o->dev);
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:
131 				mtc_stop(o->mtc);
132 				return;
133 			}
134 			mtc_loc(o->mtc,
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 
170 /*
171  * create a new audio sub-device "configuration"
172  */
173 struct opt *
174 opt_new(struct dev *d, char *name,
175     int pmin, int pmax, int rmin, int rmax,
176     int maxweight, int mmc, int dup, unsigned int mode)
177 {
178 	struct dev *a;
179 	struct opt *o, **po;
180 	unsigned int len, num;
181 	char c;
182 
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 			}
200 		}
201 	}
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 	}
210 
211 	if (opt_byname(name)) {
212 		log_puts(name);
213 		log_puts(": already defined\n");
214 		return NULL;
215 	}
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 
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 
238 	o = xmalloc(sizeof(struct opt));
239 	o->num = num;
240 	o->alt_first = o->dev = a;
241 	o->refcnt = 0;
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 
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;
262 	o->mtc = mmc ? &mtc_array[0] : NULL;
263 	o->dup = dup;
264 	o->mode = mode;
265 	memcpy(o->name, name, len + 1);
266 	o->next = *po;
267 	*po = o;
268 	if (log_level >= 2) {
269 		dev_log(d);
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)) {
294 			if (o->mtc)
295 				log_puts(" mtc");
296 			if (o->dup)
297 				log_puts(" dup");
298 		}
299 		log_puts("\n");
300 	}
301 	return o;
302 }
303 
304 struct opt *
305 opt_byname(char *name)
306 {
307 	struct opt *o;
308 
309 	for (o = opt_list; o != NULL; o = o->next) {
310 		if (strcmp(name, o->name) == 0)
311 			return o;
312 	}
313 	return NULL;
314 }
315 
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 
328 void
329 opt_del(struct opt *o)
330 {
331 	struct opt **po;
332 
333 	for (po = &opt_list; *po != o; po = &(*po)->next) {
334 #ifdef DEBUG
335 		if (*po == NULL) {
336 			log_puts("opt_del: not on list\n");
337 			panic();
338 		}
339 #endif
340 	}
341 	midi_del(o->midi);
342 	*po = o->next;
343 	xfree(o);
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  */
378 void
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))
388 		return;
389 
390 	odev = o->dev;
391 	if (odev == ndev) {
392 		dev_unref(ndev);
393 		return;
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);
402 			return;
403 		}
404 	}
405 
406 	/*
407 	 * if we're using MMC, move all opts to the new device, mtc_setdev()
408 	 * will call us back
409 	 */
410 	if (o->mtc != NULL && o->mtc->dev != ndev) {
411 		mtc_setdev(o->mtc, ndev);
412 		dev_unref(ndev);
413 		return;
414 	}
415 
416 	c = ctl_find(CTL_OPT_DEV, o, o->dev);
417 	if (c != NULL)
418 		c->curval = 0;
419 
420 	/* detach clients from old device */
421 	for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
422 		if (s->opt != o)
423 			continue;
424 
425 		if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP)
426 			slot_detach(s);
427 	}
428 
429 	o->dev = ndev;
430 
431 	if (o->refcnt > 0) {
432 		dev_unref(odev);
433 		dev_ref(o->dev);
434 	}
435 
436 	c = ctl_find(CTL_OPT_DEV, o, o->dev);
437 	if (c != NULL) {
438 		c->curval = 1;
439 		c->val_mask = ~0;
440 	}
441 
442 	/* attach clients to new device */
443 	for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
444 		if (s->opt != o)
445 			continue;
446 
447 		if (ndev != odev) {
448 			dev_midi_slotdesc(odev, s);
449 			dev_midi_slotdesc(ndev, s);
450 			dev_midi_vol(ndev, s);
451 		}
452 
453 		c = ctl_find(CTL_SLOT_LEVEL, s, NULL);
454 		ctl_update(c);
455 
456 		if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP) {
457 			slot_initconv(s);
458 			slot_attach(s);
459 		}
460 	}
461 
462 	/* move controlling clients to new device */
463 	for (p = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, p++) {
464 		if (p->ops == NULL)
465 			continue;
466 		if (p->opt == o)
467 			ctlslot_update(p);
468 	}
469 
470 	dev_unref(ndev);
471 }
472 
473 /*
474  * Get a reference to opt's device
475  */
476 struct dev *
477 opt_ref(struct opt *o)
478 {
479 	struct dev *d;
480 
481 	if (o->refcnt == 0) {
482 		if (strcmp(o->name, o->dev->name) == 0) {
483 			if (!dev_ref(o->dev))
484 				return NULL;
485 		} else {
486 			/* find first working one */
487 			d = o->alt_first;
488 			while (1) {
489 				if (dev_ref(d))
490 					break;
491 				d = d->alt_next;
492 				if (d == o->alt_first)
493 					return NULL;
494 			}
495 
496 			/* if device changed, move everything to the new one */
497 			if (d != o->dev)
498 				opt_setdev(o, d);
499 		}
500 	}
501 
502 	o->refcnt++;
503 	return o->dev;
504 }
505 
506 /*
507  * Release opt's device
508  */
509 void
510 opt_unref(struct opt *o)
511 {
512 	o->refcnt--;
513 	if (o->refcnt == 0)
514 		dev_unref(o->dev);
515 }
516