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