1 /* $OpenBSD: opt.c,v 1.13 2024/12/20 07:35:56 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
opt_midi_imsg(void * arg,unsigned char * msg,int len)40 opt_midi_imsg(void *arg, unsigned char *msg, int len)
41 {
42 #ifdef DEBUG
43 struct opt *o = arg;
44
45 logx(0, "%s: can't receive midi messages", o->name);
46 panic();
47 #endif
48 }
49
50 void
opt_midi_omsg(void * arg,unsigned char * msg,int len)51 opt_midi_omsg(void *arg, unsigned char *msg, int len)
52 {
53 struct opt *o = arg;
54 struct sysex *x;
55 unsigned int fps, chan;
56
57 if ((msg[0] & MIDI_CMDMASK) == MIDI_CTL && msg[1] == MIDI_CTL_VOL) {
58 chan = msg[0] & MIDI_CHANMASK;
59 if (chan >= DEV_NSLOT)
60 return;
61 if (slot_array[chan].opt != o)
62 return;
63 slot_setvol(slot_array + chan, msg[2]);
64 ctl_onval(CTL_SLOT_LEVEL, slot_array + chan, NULL, msg[2]);
65 return;
66 }
67 x = (struct sysex *)msg;
68 if (x->start != SYSEX_START)
69 return;
70 if (len < SYSEX_SIZE(empty))
71 return;
72 switch (x->type) {
73 case SYSEX_TYPE_RT:
74 if (x->id0 == SYSEX_CONTROL && x->id1 == SYSEX_MASTER) {
75 if (len == SYSEX_SIZE(master)) {
76 dev_master(o->dev, x->u.master.coarse);
77 if (o->dev->master_enabled) {
78 ctl_onval(CTL_DEV_MASTER, o->dev, NULL,
79 x->u.master.coarse);
80 }
81 }
82 return;
83 }
84 if (x->id0 != SYSEX_MMC)
85 return;
86 switch (x->id1) {
87 case SYSEX_MMC_STOP:
88 if (len != SYSEX_SIZE(stop))
89 return;
90 if (o->mtc == NULL)
91 return;
92 mtc_setdev(o->mtc, o->dev);
93 logx(2, "%s: mmc stop", o->name);
94 mtc_stop(o->mtc);
95 break;
96 case SYSEX_MMC_START:
97 if (len != SYSEX_SIZE(start))
98 return;
99 if (o->mtc == NULL)
100 return;
101 mtc_setdev(o->mtc, o->dev);
102 logx(2, "%s: mmc start", o->name);
103 mtc_start(o->mtc);
104 break;
105 case SYSEX_MMC_LOC:
106 if (len != SYSEX_SIZE(loc) ||
107 x->u.loc.len != SYSEX_MMC_LOC_LEN ||
108 x->u.loc.cmd != SYSEX_MMC_LOC_CMD)
109 return;
110 if (o->mtc == NULL)
111 return;
112 mtc_setdev(o->mtc, o->dev);
113 switch (x->u.loc.hr >> 5) {
114 case MTC_FPS_24:
115 fps = 24;
116 break;
117 case MTC_FPS_25:
118 fps = 25;
119 break;
120 case MTC_FPS_30:
121 fps = 30;
122 break;
123 default:
124 mtc_stop(o->mtc);
125 return;
126 }
127 mtc_loc(o->mtc,
128 (x->u.loc.hr & 0x1f) * 3600 * MTC_SEC +
129 x->u.loc.min * 60 * MTC_SEC +
130 x->u.loc.sec * MTC_SEC +
131 x->u.loc.fr * (MTC_SEC / fps));
132 break;
133 }
134 break;
135 case SYSEX_TYPE_EDU:
136 if (x->id0 != SYSEX_AUCAT || x->id1 != SYSEX_AUCAT_DUMPREQ)
137 return;
138 if (len != SYSEX_SIZE(dumpreq))
139 return;
140 dev_midi_dump(o->dev);
141 break;
142 }
143 }
144
145 void
opt_midi_fill(void * arg,int count)146 opt_midi_fill(void *arg, int count)
147 {
148 /* nothing to do */
149 }
150
151 void
opt_midi_exit(void * arg)152 opt_midi_exit(void *arg)
153 {
154 struct opt *o = arg;
155
156 logx(1, "%s: midi end point died", o->name);
157 }
158
159 /*
160 * create a new audio sub-device "configuration"
161 */
162 struct opt *
opt_new(struct dev * d,char * name,int pmin,int pmax,int rmin,int rmax,int maxweight,int mmc,int dup,unsigned int mode)163 opt_new(struct dev *d, char *name,
164 int pmin, int pmax, int rmin, int rmax,
165 int maxweight, int mmc, int dup, unsigned int mode)
166 {
167 struct dev *a;
168 struct opt *o, **po;
169 char str[64];
170 unsigned int len, num;
171 char c;
172
173 if (name == NULL) {
174 name = d->name;
175 len = strlen(name);
176 } else {
177 for (len = 0; name[len] != '\0'; len++) {
178 if (len == OPT_NAMEMAX) {
179 logx(0, "%s: too long", name);
180 return NULL;
181 }
182 c = name[len];
183 if ((c < 'a' || c > 'z') &&
184 (c < 'A' || c > 'Z')) {
185 logx(0, "%s: only alphabetic chars allowed", name);
186 return NULL;
187 }
188 }
189 }
190 num = 0;
191 for (po = &opt_list; *po != NULL; po = &(*po)->next)
192 num++;
193 if (num >= OPT_NMAX) {
194 logx(0, "%s: too many opts", name);
195 return NULL;
196 }
197
198 if (opt_byname(name)) {
199 logx(1, "%s: already defined", name);
200 return NULL;
201 }
202
203 if (mmc) {
204 if (mtc_array[0].dev != NULL && mtc_array[0].dev != d) {
205 logx(0, "%s: MTC already setup for another device", name);
206 return NULL;
207 }
208 mtc_array[0].dev = d;
209 logx(2, "%s: initial MTC source, controlled by MMC", d->path);
210 }
211
212 if (strcmp(d->name, name) == 0)
213 a = d;
214 else {
215 /* circulate to the first "alternate" device (greatest num) */
216 for (a = d; a->alt_next->num > a->num; a = a->alt_next)
217 ;
218 }
219
220 o = xmalloc(sizeof(struct opt));
221 o->num = num;
222 o->alt_first = o->dev = a;
223 o->refcnt = 0;
224
225 /*
226 * XXX: below, we allocate a midi input buffer, since we don't
227 * receive raw midi data, so no need to allocate a input
228 * ibuf. Possibly set imsg & fill callbacks to NULL and
229 * use this to in midi_new() to check if buffers need to be
230 * allocated
231 */
232 o->midi = midi_new(&opt_midiops, o, MODE_MIDIIN | MODE_MIDIOUT);
233 midi_tag(o->midi, o->num);
234
235 if (mode & MODE_PLAY) {
236 o->pmin = pmin;
237 o->pmax = pmax;
238 }
239 if (mode & MODE_RECMASK) {
240 o->rmin = rmin;
241 o->rmax = rmax;
242 }
243 o->maxweight = maxweight;
244 o->mtc = mmc ? &mtc_array[0] : NULL;
245 o->dup = dup;
246 o->mode = mode;
247 memcpy(o->name, name, len + 1);
248 o->next = *po;
249 *po = o;
250
251 logx(2, "%s: %s%s, vol = %d", o->name, (chans_fmt(str, sizeof(str),
252 o->mode, o->pmin, o->pmax, o->rmin, o->rmax), str),
253 (o->dup) ? ", dup" : "", o->maxweight);
254
255 return o;
256 }
257
258 struct opt *
opt_byname(char * name)259 opt_byname(char *name)
260 {
261 struct opt *o;
262
263 for (o = opt_list; o != NULL; o = o->next) {
264 if (strcmp(name, o->name) == 0)
265 return o;
266 }
267 return NULL;
268 }
269
270 struct opt *
opt_bynum(int num)271 opt_bynum(int num)
272 {
273 struct opt *o;
274
275 for (o = opt_list; o != NULL; o = o->next) {
276 if (o->num == num)
277 return o;
278 }
279 return NULL;
280 }
281
282 void
opt_del(struct opt * o)283 opt_del(struct opt *o)
284 {
285 struct opt **po;
286
287 for (po = &opt_list; *po != o; po = &(*po)->next) {
288 #ifdef DEBUG
289 if (*po == NULL) {
290 logx(0, "%s: not on list", __func__);
291 panic();
292 }
293 #endif
294 }
295 midi_del(o->midi);
296 *po = o->next;
297 xfree(o);
298 }
299
300 void
opt_init(struct opt * o)301 opt_init(struct opt *o)
302 {
303 }
304
305 void
opt_done(struct opt * o)306 opt_done(struct opt *o)
307 {
308 struct dev *d;
309
310 if (o->refcnt != 0) {
311 // XXX: all clients are already kicked, so this never happens
312 logx(0, "%s: still has refs", o->name);
313 }
314 for (d = dev_list; d != NULL; d = d->next)
315 ctl_del(CTL_OPT_DEV, o, d);
316 }
317
318 /*
319 * Set opt's device, and (if necessary) move clients to
320 * to the new device
321 */
322 int
opt_setdev(struct opt * o,struct dev * ndev)323 opt_setdev(struct opt *o, struct dev *ndev)
324 {
325 struct dev *odev;
326 struct ctl *c;
327 struct ctlslot *p;
328 struct slot *s;
329 int i;
330
331 if (!dev_ref(ndev))
332 return 0;
333
334 odev = o->dev;
335 if (odev == ndev) {
336 dev_unref(ndev);
337 return 1;
338 }
339
340 /* check if clients can use new device */
341 for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
342 if (s->opt != o)
343 continue;
344 if (s->ops != NULL && !dev_iscompat(odev, ndev)) {
345 dev_unref(ndev);
346 return 0;
347 }
348 }
349
350 /*
351 * if we're using MMC, move all opts to the new device, mtc_setdev()
352 * will call us back
353 *
354 * XXX: move this to the end to avoid the recursion
355 */
356 if (o->mtc != NULL && o->mtc->dev != ndev) {
357 mtc_setdev(o->mtc, ndev);
358 dev_unref(ndev);
359 return 1;
360 }
361
362 c = ctl_find(CTL_OPT_DEV, o, o->dev);
363 if (c != NULL)
364 c->curval = 0;
365
366 /* detach clients from old device */
367 for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
368 if (s->opt != o)
369 continue;
370
371 if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP)
372 slot_detach(s);
373 }
374
375 o->dev = ndev;
376
377 if (o->refcnt > 0) {
378 dev_unref(odev);
379 dev_ref(o->dev);
380 }
381
382 c = ctl_find(CTL_OPT_DEV, o, o->dev);
383 if (c != NULL) {
384 c->curval = 1;
385 c->val_mask = ~0;
386 }
387
388 /* attach clients to new device */
389 for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
390 if (s->opt != o)
391 continue;
392
393 if (ndev != odev) {
394 dev_midi_slotdesc(odev, s);
395 dev_midi_slotdesc(ndev, s);
396 dev_midi_vol(ndev, s);
397 }
398
399 c = ctl_find(CTL_SLOT_LEVEL, s, NULL);
400 ctl_update(c);
401
402 if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP) {
403 slot_initconv(s);
404 slot_attach(s);
405 }
406 }
407
408 /* move controlling clients to new device */
409 for (p = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, p++) {
410 if (p->ops == NULL)
411 continue;
412 if (p->opt == o)
413 ctlslot_update(p);
414 }
415
416 dev_unref(ndev);
417 return 1;
418 }
419
420 /*
421 * Get a reference to opt's device
422 */
423 struct dev *
opt_ref(struct opt * o)424 opt_ref(struct opt *o)
425 {
426 struct dev *d;
427
428 if (o->refcnt == 0) {
429 if (strcmp(o->name, o->dev->name) == 0) {
430 if (!dev_ref(o->dev))
431 return NULL;
432 } else {
433 /* find first working one */
434 d = o->alt_first;
435 while (1) {
436 if (dev_ref(d))
437 break;
438 d = d->alt_next;
439 if (d == o->alt_first)
440 return NULL;
441 }
442
443 /* if device changed, move everything to the new one */
444 if (d != o->dev)
445 opt_setdev(o, d);
446
447 /* create server.device control */
448 for (d = dev_list; d != NULL; d = d->next) {
449 d->refcnt++;
450 if (d->pstate == DEV_CFG)
451 dev_open(d);
452 ctl_new(CTL_OPT_DEV, o, d,
453 CTL_SEL, dev_getdisplay(d),
454 o->name, "server", -1, "device",
455 d->name, -1, 1, o->dev == d);
456 }
457 }
458 }
459
460 o->refcnt++;
461 return o->dev;
462 }
463
464 /*
465 * Release opt's device
466 */
467 void
opt_unref(struct opt * o)468 opt_unref(struct opt *o)
469 {
470 struct dev *d;
471
472 o->refcnt--;
473 if (o->refcnt == 0) {
474 /* delete server.device control */
475 for (d = dev_list; d != NULL; d = d->next) {
476 if (ctl_del(CTL_OPT_DEV, o, d))
477 dev_unref(d);
478 }
479 dev_unref(o->dev);
480 }
481 }
482