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 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 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 146 opt_midi_fill(void *arg, int count) 147 { 148 /* nothing to do */ 149 } 150 151 void 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 * 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 * 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 * 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 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 301 opt_init(struct opt *o) 302 { 303 } 304 305 void 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 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 * 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 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