1/* $OpenBSD: opt.c,v 1.12 2024/05/24 15:21:35 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 25struct opt *opt_list; 26 27void opt_midi_imsg(void *, unsigned char *, int); 28void opt_midi_omsg(void *, unsigned char *, int); 29void opt_midi_fill(void *, int); 30void opt_midi_exit(void *); 31 32struct midiops opt_midiops = { 33 opt_midi_imsg, 34 opt_midi_omsg, 35 opt_midi_fill, 36 opt_midi_exit 37}; 38 39void 40opt_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 51void 52opt_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 152void 153opt_midi_fill(void *arg, int count) 154{ 155 /* nothing to do */ 156} 157 158void 159opt_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 */ 173struct opt * 174opt_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 304struct opt * 305opt_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 316struct opt * 317opt_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 328void 329opt_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 346void 347opt_init(struct opt *o) 348{ 349} 350 351void 352opt_done(struct opt *o) 353{ 354 struct dev *d; 355 356 if (o->refcnt != 0) { 357 // XXX: all clients are already kicked, so this never happens 358 log_puts(o->name); 359 log_puts(": still has refs\n"); 360 } 361 for (d = dev_list; d != NULL; d = d->next) 362 ctl_del(CTL_OPT_DEV, o, d); 363} 364 365/* 366 * Set opt's device, and (if necessary) move clients to 367 * to the new device 368 */ 369int 370opt_setdev(struct opt *o, struct dev *ndev) 371{ 372 struct dev *odev; 373 struct ctl *c; 374 struct ctlslot *p; 375 struct slot *s; 376 int i; 377 378 if (!dev_ref(ndev)) 379 return 0; 380 381 odev = o->dev; 382 if (odev == ndev) { 383 dev_unref(ndev); 384 return 1; 385 } 386 387 /* check if clients can use new device */ 388 for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { 389 if (s->opt != o) 390 continue; 391 if (s->ops != NULL && !dev_iscompat(odev, ndev)) { 392 dev_unref(ndev); 393 return 0; 394 } 395 } 396 397 /* 398 * if we're using MMC, move all opts to the new device, mtc_setdev() 399 * will call us back 400 * 401 * XXX: move this to the end to avoid the recursion 402 */ 403 if (o->mtc != NULL && o->mtc->dev != ndev) { 404 mtc_setdev(o->mtc, ndev); 405 dev_unref(ndev); 406 return 1; 407 } 408 409 c = ctl_find(CTL_OPT_DEV, o, o->dev); 410 if (c != NULL) 411 c->curval = 0; 412 413 /* detach clients from old device */ 414 for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { 415 if (s->opt != o) 416 continue; 417 418 if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP) 419 slot_detach(s); 420 } 421 422 o->dev = ndev; 423 424 if (o->refcnt > 0) { 425 dev_unref(odev); 426 dev_ref(o->dev); 427 } 428 429 c = ctl_find(CTL_OPT_DEV, o, o->dev); 430 if (c != NULL) { 431 c->curval = 1; 432 c->val_mask = ~0; 433 } 434 435 /* attach clients to new device */ 436 for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { 437 if (s->opt != o) 438 continue; 439 440 if (ndev != odev) { 441 dev_midi_slotdesc(odev, s); 442 dev_midi_slotdesc(ndev, s); 443 dev_midi_vol(ndev, s); 444 } 445 446 c = ctl_find(CTL_SLOT_LEVEL, s, NULL); 447 ctl_update(c); 448 449 if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP) { 450 slot_initconv(s); 451 slot_attach(s); 452 } 453 } 454 455 /* move controlling clients to new device */ 456 for (p = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, p++) { 457 if (p->ops == NULL) 458 continue; 459 if (p->opt == o) 460 ctlslot_update(p); 461 } 462 463 dev_unref(ndev); 464 return 1; 465} 466 467/* 468 * Get a reference to opt's device 469 */ 470struct dev * 471opt_ref(struct opt *o) 472{ 473 struct dev *d; 474 475 if (o->refcnt == 0) { 476 if (strcmp(o->name, o->dev->name) == 0) { 477 if (!dev_ref(o->dev)) 478 return NULL; 479 } else { 480 /* find first working one */ 481 d = o->alt_first; 482 while (1) { 483 if (dev_ref(d)) 484 break; 485 d = d->alt_next; 486 if (d == o->alt_first) 487 return NULL; 488 } 489 490 /* if device changed, move everything to the new one */ 491 if (d != o->dev) 492 opt_setdev(o, d); 493 494 /* create server.device control */ 495 for (d = dev_list; d != NULL; d = d->next) { 496 d->refcnt++; 497 if (d->pstate == DEV_CFG) 498 dev_open(d); 499 ctl_new(CTL_OPT_DEV, o, d, 500 CTL_SEL, dev_getdisplay(d), 501 o->name, "server", -1, "device", 502 d->name, -1, 1, o->dev == d); 503 } 504 } 505 } 506 507 o->refcnt++; 508 return o->dev; 509} 510 511/* 512 * Release opt's device 513 */ 514void 515opt_unref(struct opt *o) 516{ 517 struct dev *d; 518 519 o->refcnt--; 520 if (o->refcnt == 0) { 521 /* delete server.device control */ 522 for (d = dev_list; d != NULL; d = d->next) { 523 if (ctl_del(CTL_OPT_DEV, o, d)) 524 dev_unref(d); 525 } 526 dev_unref(o->dev); 527 } 528} 529