Annotation of src/usr.bin/aucat/midi.c, Revision 1.35
1.35 ! ratchov 1: /* $OpenBSD: midi.c,v 1.34 2011/06/02 18:50:39 ratchov Exp $ */
1.1 ratchov 2: /*
3: * Copyright (c) 2008 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: /*
18: * TODO
19: *
1.7 ratchov 20: * use shadow variables (to save NRPNs, LSB of controller)
21: * in the midi merger
1.1 ratchov 22: *
23: * make output and input identical when only one
24: * input is used (fix running status)
25: */
26: #include <stdio.h>
27: #include <stdlib.h>
28: #include <string.h>
29:
30: #include "abuf.h"
31: #include "aproc.h"
1.3 ratchov 32: #include "conf.h"
33: #include "dev.h"
1.1 ratchov 34: #include "midi.h"
1.33 ratchov 35: #include "sysex.h"
1.14 ratchov 36: #ifdef DEBUG
37: #include "dbg.h"
38: #endif
1.1 ratchov 39:
40: /*
41: * input data rate is XFER / TIMO (in bytes per microsecond),
42: * it must be slightly larger than the MIDI standard 3125 bytes/s
43: */
44: #define MIDITHRU_XFER 340
45: #define MIDITHRU_TIMO 100000
46:
1.3 ratchov 47: /*
48: * masks to extract command and channel of status byte
49: */
50: #define MIDI_CMDMASK 0xf0
51: #define MIDI_CHANMASK 0x0f
52:
53: /*
54: * MIDI status bytes of voice messages
55: */
56: #define MIDI_NOFF 0x80 /* note off */
57: #define MIDI_NON 0x90 /* note on */
58: #define MIDI_KAT 0xa0 /* key after touch */
59: #define MIDI_CTL 0xb0 /* controller */
60: #define MIDI_PC 0xc0 /* program change */
61: #define MIDI_CAT 0xd0 /* channel after touch */
62: #define MIDI_BEND 0xe0 /* pitch bend */
1.16 ratchov 63: #define MIDI_ACK 0xfe /* active sensing message */
1.3 ratchov 64:
65: /*
66: * MIDI controller numbers
67: */
68: #define MIDI_CTLVOL 7 /* volume */
69: #define MIDI_CTLPAN 11 /* pan */
70:
71: /*
72: * length of voice and common messages (status byte included)
73: */
1.1 ratchov 74: unsigned voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
75: unsigned common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
76:
1.7 ratchov 77: /*
1.10 ratchov 78: * send the message stored in of ibuf->r.midi.msg to obuf
1.7 ratchov 79: */
1.1 ratchov 80: void
81: thru_flush(struct aproc *p, struct abuf *ibuf, struct abuf *obuf)
82: {
83: unsigned ocount, itodo;
84: unsigned char *odata, *idata;
85:
1.10 ratchov 86: itodo = ibuf->r.midi.used;
87: idata = ibuf->r.midi.msg;
1.14 ratchov 88: #ifdef DEBUG
89: if (debug_level >= 4) {
90: aproc_dbg(p);
91: dbg_puts(": flushing ");
92: dbg_putu(itodo);
93: dbg_puts(" byte message\n");
94: }
95: #endif
1.1 ratchov 96: while (itodo > 0) {
97: if (!ABUF_WOK(obuf)) {
1.14 ratchov 98: #ifdef DEBUG
1.28 ratchov 99: if (debug_level >= 3) {
1.14 ratchov 100: aproc_dbg(p);
101: dbg_puts(": overrun, discarding ");
102: dbg_putu(obuf->used);
103: dbg_puts(" bytes\n");
104: }
105: #endif
1.1 ratchov 106: abuf_rdiscard(obuf, obuf->used);
107: if (p->u.thru.owner == ibuf)
108: p->u.thru.owner = NULL;
109: return;
110: }
111: odata = abuf_wgetblk(obuf, &ocount, 0);
112: if (ocount > itodo)
113: ocount = itodo;
114: memcpy(odata, idata, ocount);
115: abuf_wcommit(obuf, ocount);
116: itodo -= ocount;
117: idata += ocount;
118: }
1.10 ratchov 119: ibuf->r.midi.used = 0;
1.1 ratchov 120: p->u.thru.owner = ibuf;
121: }
122:
1.7 ratchov 123: /*
124: * send the real-time message (one byte) to obuf, similar to thrui_flush()
125: */
1.1 ratchov 126: void
127: thru_rt(struct aproc *p, struct abuf *ibuf, struct abuf *obuf, unsigned c)
128: {
129: unsigned ocount;
130: unsigned char *odata;
131:
1.14 ratchov 132: #ifdef DEBUG
133: if (debug_level >= 4) {
134: aproc_dbg(p);
135: dbg_puts(": ");
1.19 ratchov 136: dbg_putx(c);
1.14 ratchov 137: dbg_puts(": flushing realtime message\n");
138: }
139: #endif
1.16 ratchov 140: if (c == MIDI_ACK)
141: return;
1.1 ratchov 142: if (!ABUF_WOK(obuf)) {
1.14 ratchov 143: #ifdef DEBUG
1.28 ratchov 144: if (debug_level >= 3) {
1.14 ratchov 145: aproc_dbg(p);
146: dbg_puts(": overrun, discarding ");
147: dbg_putu(obuf->used);
148: dbg_puts(" bytes\n");
149: }
150: #endif
1.1 ratchov 151: abuf_rdiscard(obuf, obuf->used);
152: if (p->u.thru.owner == ibuf)
153: p->u.thru.owner = NULL;
154: }
155: odata = abuf_wgetblk(obuf, &ocount, 0);
156: odata[0] = c;
157: abuf_wcommit(obuf, 1);
158: }
159:
1.7 ratchov 160: /*
161: * parse ibuf contents and store each message into obuf,
162: * use at most ``todo'' bytes (for throttling)
163: */
1.1 ratchov 164: void
165: thru_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf, unsigned todo)
166: {
167: unsigned char *idata;
168: unsigned c, icount, ioffs;
169:
170: idata = NULL;
171: icount = ioffs = 0;
172: for (;;) {
173: if (icount == 0) {
174: if (todo == 0)
175: break;
176: idata = abuf_rgetblk(ibuf, &icount, ioffs);
177: if (icount > todo)
178: icount = todo;
179: if (icount == 0)
180: break;
181: todo -= icount;
182: ioffs += icount;
183: }
184: c = *idata++;
185: icount--;
186: if (c < 0x80) {
1.10 ratchov 187: if (ibuf->r.midi.idx == 0 && ibuf->r.midi.st) {
188: ibuf->r.midi.msg[ibuf->r.midi.used++] = ibuf->r.midi.st;
189: ibuf->r.midi.idx++;
1.1 ratchov 190: }
1.10 ratchov 191: ibuf->r.midi.msg[ibuf->r.midi.used++] = c;
192: ibuf->r.midi.idx++;
193: if (ibuf->r.midi.idx == ibuf->r.midi.len) {
1.1 ratchov 194: thru_flush(p, ibuf, obuf);
1.10 ratchov 195: if (ibuf->r.midi.st >= 0xf0)
196: ibuf->r.midi.st = 0;
197: ibuf->r.midi.idx = 0;
1.1 ratchov 198: }
1.10 ratchov 199: if (ibuf->r.midi.used == MIDI_MSGMAX) {
200: if (ibuf->r.midi.used == ibuf->r.midi.idx ||
1.1 ratchov 201: p->u.thru.owner == ibuf)
202: thru_flush(p, ibuf, obuf);
203: else
1.10 ratchov 204: ibuf->r.midi.used = 0;
1.1 ratchov 205: }
206: } else if (c < 0xf8) {
1.10 ratchov 207: if (ibuf->r.midi.used == ibuf->r.midi.idx ||
1.1 ratchov 208: p->u.thru.owner == ibuf) {
209: thru_flush(p, ibuf, obuf);
210: } else
1.10 ratchov 211: ibuf->r.midi.used = 0;
212: ibuf->r.midi.msg[0] = c;
213: ibuf->r.midi.used = 1;
214: ibuf->r.midi.len = (c >= 0xf0) ?
1.1 ratchov 215: common_len[c & 7] :
216: voice_len[(c >> 4) & 7];
1.10 ratchov 217: if (ibuf->r.midi.len == 1) {
1.1 ratchov 218: thru_flush(p, ibuf, obuf);
1.10 ratchov 219: ibuf->r.midi.idx = 0;
220: ibuf->r.midi.st = 0;
221: ibuf->r.midi.len = 0;
1.1 ratchov 222: } else {
1.10 ratchov 223: ibuf->r.midi.st = c;
224: ibuf->r.midi.idx = 1;
1.1 ratchov 225: }
226: } else {
227: thru_rt(p, ibuf, obuf, c);
228: }
229: }
230: }
231:
232: int
233: thru_in(struct aproc *p, struct abuf *ibuf)
234: {
235: struct abuf *i, *inext;
236: unsigned todo;
237:
238: if (!ABUF_ROK(ibuf))
239: return 0;
1.10 ratchov 240: if (ibuf->tickets == 0) {
1.14 ratchov 241: #ifdef DEBUG
242: if (debug_level >= 4) {
243: abuf_dbg(ibuf);
244: dbg_puts(": out of tickets, blocking\n");
245: }
246: #endif
1.1 ratchov 247: return 0;
248: }
249: todo = ibuf->used;
1.10 ratchov 250: if (todo > ibuf->tickets)
251: todo = ibuf->tickets;
252: ibuf->tickets -= todo;
1.20 ratchov 253: for (i = LIST_FIRST(&p->outs); i != NULL; i = inext) {
1.1 ratchov 254: inext = LIST_NEXT(i, oent);
255: if (ibuf->duplex == i)
256: continue;
257: thru_bcopy(p, ibuf, i, todo);
258: (void)abuf_flush(i);
259: }
260: abuf_rdiscard(ibuf, todo);
261: return 1;
262: }
263:
264: int
265: thru_out(struct aproc *p, struct abuf *obuf)
266: {
267: return 0;
268: }
269:
270: void
271: thru_eof(struct aproc *p, struct abuf *ibuf)
272: {
1.13 ratchov 273: if (!(p->flags & APROC_QUIT))
1.11 ratchov 274: return;
1.34 ratchov 275: if (LIST_EMPTY(&p->ins))
1.11 ratchov 276: aproc_del(p);
1.1 ratchov 277: }
278:
279: void
280: thru_hup(struct aproc *p, struct abuf *obuf)
281: {
1.31 ratchov 282: if (!(p->flags & APROC_QUIT))
283: return;
1.34 ratchov 284: if (LIST_EMPTY(&p->ins))
1.31 ratchov 285: aproc_del(p);
1.1 ratchov 286: }
287:
288: void
289: thru_newin(struct aproc *p, struct abuf *ibuf)
290: {
1.10 ratchov 291: ibuf->r.midi.used = 0;
292: ibuf->r.midi.len = 0;
293: ibuf->r.midi.idx = 0;
294: ibuf->r.midi.st = 0;
295: ibuf->tickets = MIDITHRU_XFER;
1.1 ratchov 296: }
297:
298: void
299: thru_done(struct aproc *p)
300: {
301: timo_del(&p->u.thru.timo);
302: }
303:
304: struct aproc_ops thru_ops = {
305: "thru",
306: thru_in,
307: thru_out,
308: thru_eof,
309: thru_hup,
310: thru_newin,
311: NULL, /* newout */
312: NULL, /* ipos */
313: NULL, /* opos */
314: thru_done
315: };
316:
1.7 ratchov 317: /*
318: * call-back invoked periodically to implement throttling at each invocation
319: * gain more ``tickets'' for processing. If one of the buffer was blocked by
320: * the throttelling mechanism, then run it
321: */
1.1 ratchov 322: void
323: thru_cb(void *addr)
324: {
325: struct aproc *p = (struct aproc *)addr;
326: struct abuf *i, *inext;
327: unsigned tickets;
328:
329: timo_add(&p->u.thru.timo, MIDITHRU_TIMO);
330:
1.20 ratchov 331: for (i = LIST_FIRST(&p->ins); i != NULL; i = inext) {
1.1 ratchov 332: inext = LIST_NEXT(i, ient);
1.10 ratchov 333: tickets = i->tickets;
334: i->tickets = MIDITHRU_XFER;
1.1 ratchov 335: if (tickets == 0)
336: abuf_run(i);
337: }
338: }
339:
340: struct aproc *
341: thru_new(char *name)
342: {
343: struct aproc *p;
344:
345: p = aproc_new(&thru_ops, name);
346: p->u.thru.owner = NULL;
347: timo_set(&p->u.thru.timo, thru_cb, p);
348: timo_add(&p->u.thru.timo, MIDITHRU_TIMO);
1.3 ratchov 349: return p;
350: }
351:
1.14 ratchov 352: #ifdef DEBUG
353: void
354: ctl_slotdbg(struct aproc *p, int slot)
355: {
356: struct ctl_slot *s;
357:
358: if (slot < 0) {
1.19 ratchov 359: dbg_puts("none");
1.14 ratchov 360: } else {
361: s = p->u.ctl.slot + slot;
362: dbg_puts(s->name);
363: dbg_putu(s->unit);
1.19 ratchov 364: dbg_puts("(");
1.14 ratchov 365: dbg_putu(s->vol);
1.19 ratchov 366: dbg_puts(")/");
1.14 ratchov 367: switch (s->tstate) {
368: case CTL_OFF:
369: dbg_puts("off");
370: break;
371: case CTL_RUN:
372: dbg_puts("run");
373: break;
374: case CTL_START:
375: dbg_puts("sta");
376: break;
377: case CTL_STOP:
378: dbg_puts("stp");
379: break;
380: default:
381: dbg_puts("unk");
382: break;
383: }
384: }
385: }
386: #endif
1.12 ratchov 387:
1.7 ratchov 388: /*
1.33 ratchov 389: * send a message to the given output
390: */
391: void
392: ctl_copymsg(struct abuf *obuf, unsigned char *msg, unsigned len)
393: {
394: unsigned ocount, itodo;
395: unsigned char *odata, *idata;
396:
397: itodo = len;
398: idata = msg;
399: while (itodo > 0) {
400: if (!ABUF_WOK(obuf)) {
401: #ifdef DEBUG
402: if (debug_level >= 3) {
403: abuf_dbg(obuf);
404: dbg_puts(": overrun, discarding ");
405: dbg_putu(obuf->used);
406: dbg_puts(" bytes\n");
407: }
408: #endif
409: abuf_rdiscard(obuf, obuf->used);
410: }
411: odata = abuf_wgetblk(obuf, &ocount, 0);
412: if (ocount > itodo)
413: ocount = itodo;
414: #ifdef DEBUG
415: if (debug_level >= 4) {
416: abuf_dbg(obuf);
417: dbg_puts(": stored ");
418: dbg_putu(ocount);
419: dbg_puts(" bytes\n");
420: }
421: #endif
422: memcpy(odata, idata, ocount);
423: abuf_wcommit(obuf, ocount);
424: itodo -= ocount;
425: idata += ocount;
426: }
427: }
428:
429: /*
1.7 ratchov 430: * broadcast a message to all output buffers on the behalf of ibuf.
431: * ie. don't sent back the message to the sender
432: */
1.3 ratchov 433: void
434: ctl_sendmsg(struct aproc *p, struct abuf *ibuf, unsigned char *msg, unsigned len)
435: {
436: struct abuf *i, *inext;
437:
1.20 ratchov 438: for (i = LIST_FIRST(&p->outs); i != NULL; i = inext) {
1.3 ratchov 439: inext = LIST_NEXT(i, oent);
1.19 ratchov 440: if (i->duplex && i->duplex == ibuf)
1.3 ratchov 441: continue;
1.33 ratchov 442: ctl_copymsg(i, msg, len);
1.3 ratchov 443: (void)abuf_flush(i);
444: }
445: }
446:
1.7 ratchov 447: /*
1.13 ratchov 448: * send a quarter frame MTC message
449: */
450: void
451: ctl_qfr(struct aproc *p)
452: {
453: unsigned char buf[2];
454: unsigned data;
455:
456: switch (p->u.ctl.qfr) {
457: case 0:
458: data = p->u.ctl.fr & 0xf;
459: break;
460: case 1:
461: data = p->u.ctl.fr >> 4;
462: break;
463: case 2:
464: data = p->u.ctl.sec & 0xf;
465: break;
466: case 3:
467: data = p->u.ctl.sec >> 4;
468: break;
469: case 4:
470: data = p->u.ctl.min & 0xf;
471: break;
472: case 5:
473: data = p->u.ctl.min >> 4;
474: break;
475: case 6:
476: data = p->u.ctl.hr & 0xf;
477: break;
478: case 7:
479: data = (p->u.ctl.hr >> 4) | (p->u.ctl.fps_id << 1);
480: /*
481: * tick messages are sent 2 frames ahead
482: */
483: p->u.ctl.fr += 2;
484: if (p->u.ctl.fr < p->u.ctl.fps)
485: break;
486: p->u.ctl.fr -= p->u.ctl.fps;
487: p->u.ctl.sec++;
488: if (p->u.ctl.sec < 60)
1.32 deraadt 489: break;
1.13 ratchov 490: p->u.ctl.sec = 0;
491: p->u.ctl.min++;
492: if (p->u.ctl.min < 60)
493: break;
494: p->u.ctl.min = 0;
495: p->u.ctl.hr++;
496: if (p->u.ctl.hr < 24)
497: break;
498: p->u.ctl.hr = 0;
499: break;
500: default:
501: /* NOTREACHED */
502: data = 0;
503: }
504: buf[0] = 0xf1;
505: buf[1] = (p->u.ctl.qfr << 4) | data;
506: p->u.ctl.qfr++;
507: p->u.ctl.qfr &= 7;
508: ctl_sendmsg(p, NULL, buf, 2);
509: }
510:
511: /*
512: * send a full frame MTC message
513: */
514: void
515: ctl_full(struct aproc *p)
516: {
517: unsigned char buf[10];
518: unsigned origin = p->u.ctl.origin;
519: unsigned fps = p->u.ctl.fps;
520:
521: p->u.ctl.hr = (origin / (3600 * MTC_SEC)) % 24;
522: p->u.ctl.min = (origin / (60 * MTC_SEC)) % 60;
523: p->u.ctl.sec = (origin / MTC_SEC) % 60;
524: p->u.ctl.fr = (origin / (MTC_SEC / fps)) % fps;
525:
526: buf[0] = 0xf0;
527: buf[1] = 0x7f;
528: buf[2] = 0x7f;
529: buf[3] = 0x01;
530: buf[4] = 0x01;
531: buf[5] = p->u.ctl.hr | (p->u.ctl.fps_id << 5);
532: buf[6] = p->u.ctl.min;
533: buf[7] = p->u.ctl.sec;
534: buf[8] = p->u.ctl.fr;
535: buf[9] = 0xf7;
536: p->u.ctl.qfr = 0;
537: ctl_sendmsg(p, NULL, buf, 10);
538: }
539:
1.33 ratchov 540: void
1.35 ! ratchov 541: ctl_msg_info(struct aproc *p, int slot, char *msg)
! 542: {
! 543: struct ctl_slot *s;
! 544: struct sysex *x = (struct sysex *)msg;
! 545:
! 546: s = p->u.ctl.slot + slot;
! 547: memset(x, 0, sizeof(struct sysex));
! 548: x->start = SYSEX_START;
! 549: x->type = SYSEX_TYPE_EDU;
! 550: x->id0 = SYSEX_AUCAT;
! 551: x->id1 = SYSEX_AUCAT_MIXINFO;
! 552: if (*s->name != '\0') {
! 553: snprintf(x->u.mixinfo.name,
! 554: SYSEX_NAMELEN, "%s%u", s->name, s->unit);
! 555: }
! 556: x->u.mixinfo.chan = slot;
! 557: x->u.mixinfo.end = SYSEX_END;
! 558: }
! 559:
! 560: void
1.33 ratchov 561: ctl_msg_vol(struct aproc *p, int slot, char *msg)
562: {
563: struct ctl_slot *s;
564:
565: s = p->u.ctl.slot + slot;
566: msg[0] = MIDI_CTL | slot;
567: msg[1] = MIDI_CTLVOL;
568: msg[2] = s->vol;
569: }
570:
1.35 ! ratchov 571: void
! 572: ctl_dump(struct aproc *p, struct abuf *obuf)
! 573: {
! 574: unsigned i;
! 575: unsigned char msg[sizeof(struct sysex)];
! 576: struct ctl_slot *s;
! 577:
! 578: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
! 579: ctl_msg_info(p, i, msg);
! 580: ctl_copymsg(obuf, msg, SYSEX_SIZE(mixinfo));
! 581: ctl_msg_vol(p, i, msg);
! 582: ctl_copymsg(obuf, msg, 3);
! 583: }
! 584: msg[0] = SYSEX_START;
! 585: msg[1] = SYSEX_TYPE_EDU;
! 586: msg[2] = 0;
! 587: msg[3] = SYSEX_AUCAT;
! 588: msg[4] = SYSEX_AUCAT_DUMPEND;
! 589: msg[5] = SYSEX_END;
! 590: ctl_copymsg(obuf, msg, 6);
! 591: dbg_puts("end dump\n");
! 592: abuf_flush(obuf);
! 593: }
! 594:
1.13 ratchov 595: /*
1.12 ratchov 596: * find the best matching free slot index (ie midi channel).
597: * return -1, if there are no free slots anymore
1.7 ratchov 598: */
1.3 ratchov 599: int
1.12 ratchov 600: ctl_getidx(struct aproc *p, char *who)
1.3 ratchov 601: {
602: char *s;
603: struct ctl_slot *slot;
1.4 ratchov 604: char name[CTL_NAMEMAX];
605: unsigned i, unit, umap = 0;
1.6 ratchov 606: unsigned ser, bestser, bestidx;
1.3 ratchov 607:
1.4 ratchov 608: /*
609: * create a ``valid'' control name (lowcase, remove [^a-z], trucate)
610: */
1.5 ratchov 611: for (i = 0, s = who; ; s++) {
1.4 ratchov 612: if (i == CTL_NAMEMAX - 1 || *s == '\0') {
613: name[i] = '\0';
614: break;
615: } else if (*s >= 'A' && *s <= 'Z') {
616: name[i++] = *s + 'a' - 'A';
617: } else if (*s >= 'a' && *s <= 'z')
618: name[i++] = *s;
619: }
620: if (i == 0)
621: strlcpy(name, "noname", CTL_NAMEMAX);
622:
623: /*
624: * find the instance number of the control name
625: */
626: for (i = 0, slot = p->u.ctl.slot; i < CTL_NSLOT; i++, slot++) {
1.12 ratchov 627: if (slot->ops == NULL)
1.4 ratchov 628: continue;
629: if (strcmp(slot->name, name) == 0)
630: umap |= (1 << i);
631: }
1.12 ratchov 632: for (unit = 0; ; unit++) {
1.29 ratchov 633: if (unit == CTL_NSLOT) {
634: #ifdef DEBUG
635: if (debug_level >= 1) {
636: dbg_puts(name);
1.30 ratchov 637: dbg_puts(": too many instances\n");
1.29 ratchov 638: }
639: #endif
1.3 ratchov 640: return -1;
1.29 ratchov 641: }
1.12 ratchov 642: if ((umap & (1 << unit)) == 0)
1.3 ratchov 643: break;
644: }
1.29 ratchov 645:
1.4 ratchov 646: /*
647: * find a free controller slot with the same name/unit
648: */
649: for (i = 0, slot = p->u.ctl.slot; i < CTL_NSLOT; i++, slot++) {
1.12 ratchov 650: if (slot->ops == NULL &&
1.4 ratchov 651: strcmp(slot->name, name) == 0 &&
652: slot->unit == unit) {
1.14 ratchov 653: #ifdef DEBUG
654: if (debug_level >= 3) {
1.29 ratchov 655: dbg_puts(name);
656: dbg_putu(unit);
1.14 ratchov 657: dbg_puts(": found slot ");
658: dbg_putu(i);
659: dbg_puts("\n");
660: }
661: #endif
1.4 ratchov 662: return i;
663: }
664: }
665:
666: /*
1.6 ratchov 667: * couldn't find a matching slot, pick oldest free slot
1.12 ratchov 668: * and set its name/unit
1.4 ratchov 669: */
1.6 ratchov 670: bestser = 0;
671: bestidx = CTL_NSLOT;
672: for (i = 0, slot = p->u.ctl.slot; i < CTL_NSLOT; i++, slot++) {
1.12 ratchov 673: if (slot->ops != NULL)
1.6 ratchov 674: continue;
675: ser = p->u.ctl.serial - slot->serial;
676: if (ser > bestser) {
677: bestser = ser;
678: bestidx = i;
679: }
1.3 ratchov 680: }
1.29 ratchov 681: if (bestidx == CTL_NSLOT) {
682: #ifdef DEBUG
683: if (debug_level >= 1) {
684: dbg_puts(name);
685: dbg_putu(unit);
686: dbg_puts(": out of mixer slots\n");
687: }
688: #endif
1.6 ratchov 689: return -1;
1.29 ratchov 690: }
1.6 ratchov 691: slot = p->u.ctl.slot + bestidx;
1.22 ratchov 692: if (slot->name[0] != '\0')
693: slot->vol = MIDI_MAXCTL;
1.4 ratchov 694: strlcpy(slot->name, name, CTL_NAMEMAX);
1.6 ratchov 695: slot->serial = p->u.ctl.serial++;
1.4 ratchov 696: slot->unit = unit;
1.14 ratchov 697: #ifdef DEBUG
698: if (debug_level >= 3) {
1.29 ratchov 699: dbg_puts(name);
700: dbg_putu(unit);
1.14 ratchov 701: dbg_puts(": overwritten slot ");
702: dbg_putu(bestidx);
703: dbg_puts("\n");
704: }
705: #endif
1.6 ratchov 706: return bestidx;
1.3 ratchov 707: }
708:
1.7 ratchov 709: /*
1.13 ratchov 710: * check that all clients controlled by MMC are ready to start,
711: * if so, start them all but the caller
712: */
713: int
714: ctl_trystart(struct aproc *p, int caller)
715: {
716: unsigned i;
717: struct ctl_slot *s;
718:
719: if (p->u.ctl.tstate != CTL_START) {
1.14 ratchov 720: #ifdef DEBUG
1.19 ratchov 721: if (debug_level >= 3) {
722: ctl_slotdbg(p, caller);
723: dbg_puts(": server not started, delayd\n");
724: }
1.14 ratchov 725: #endif
1.13 ratchov 726: return 0;
727: }
728: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
729: if (!s->ops || i == caller)
730: continue;
731: if (s->tstate != CTL_OFF && s->tstate != CTL_START) {
1.14 ratchov 732: #ifdef DEBUG
1.19 ratchov 733: if (debug_level >= 3) {
734: ctl_slotdbg(p, i);
735: dbg_puts(": not ready, server delayed\n");
736: }
1.14 ratchov 737: #endif
1.13 ratchov 738: return 0;
739: }
740: }
741: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
742: if (!s->ops || i == caller)
743: continue;
744: if (s->tstate == CTL_START) {
1.14 ratchov 745: #ifdef DEBUG
1.19 ratchov 746: if (debug_level >= 3) {
747: ctl_slotdbg(p, i);
748: dbg_puts(": started\n");
749: }
1.14 ratchov 750: #endif
1.13 ratchov 751: s->tstate = CTL_RUN;
752: s->ops->start(s->arg);
753: }
754: }
755: if (caller >= 0)
756: p->u.ctl.slot[caller].tstate = CTL_RUN;
757: p->u.ctl.tstate = CTL_RUN;
1.25 ratchov 758: p->u.ctl.delta = MTC_SEC * dev_getpos(p->u.ctl.dev);
759: if (p->u.ctl.dev->rate % (30 * 4 * p->u.ctl.dev->round) == 0) {
1.13 ratchov 760: p->u.ctl.fps_id = MTC_FPS_30;
761: p->u.ctl.fps = 30;
1.25 ratchov 762: } else if (p->u.ctl.dev->rate % (25 * 4 * p->u.ctl.dev->round) == 0) {
1.13 ratchov 763: p->u.ctl.fps_id = MTC_FPS_25;
764: p->u.ctl.fps = 25;
765: } else {
766: p->u.ctl.fps_id = MTC_FPS_24;
767: p->u.ctl.fps = 24;
768: }
1.14 ratchov 769: #ifdef DEBUG
1.19 ratchov 770: if (debug_level >= 3) {
771: ctl_slotdbg(p, caller);
772: dbg_puts(": started server at ");
773: dbg_puti(p->u.ctl.delta);
774: dbg_puts(", ");
775: dbg_puti(p->u.ctl.fps);
776: dbg_puts(" mtc fps\n");
777: }
1.14 ratchov 778: #endif
1.25 ratchov 779: dev_wakeup(p->u.ctl.dev);
1.13 ratchov 780: ctl_full(p);
781: return 1;
782: }
783:
784: /*
1.12 ratchov 785: * allocate a new slot and register the given call-backs
786: */
787: int
1.13 ratchov 788: ctl_slotnew(struct aproc *p, char *who, struct ctl_ops *ops, void *arg, int tr)
1.12 ratchov 789: {
790: int idx;
791: struct ctl_slot *s;
1.33 ratchov 792: unsigned char msg[sizeof(struct sysex)];
1.12 ratchov 793:
1.19 ratchov 794: if (!APROC_OK(p)) {
795: #ifdef DEBUG
796: if (debug_level >= 1) {
797: dbg_puts(who);
798: dbg_puts(": MIDI control not available\n");
799: }
800: #endif
1.13 ratchov 801: return -1;
1.19 ratchov 802: }
1.12 ratchov 803: idx = ctl_getidx(p, who);
804: if (idx < 0)
805: return -1;
806:
807: s = p->u.ctl.slot + idx;
808: s->ops = ops;
809: s->arg = arg;
1.13 ratchov 810: s->tstate = tr ? CTL_STOP : CTL_OFF;
1.12 ratchov 811: s->ops->vol(s->arg, s->vol);
1.35 ! ratchov 812: ctl_msg_info(p, idx, msg);
! 813: ctl_sendmsg(p, NULL, msg, SYSEX_SIZE(mixinfo));
1.33 ratchov 814: ctl_msg_vol(p, idx, msg);
815: ctl_sendmsg(p, NULL, msg, 3);
1.12 ratchov 816: return idx;
817: }
818:
819: /*
1.7 ratchov 820: * release the given slot
821: */
1.3 ratchov 822: void
823: ctl_slotdel(struct aproc *p, int index)
824: {
1.13 ratchov 825: unsigned i;
826: struct ctl_slot *s;
827:
1.19 ratchov 828: if (!APROC_OK(p))
1.13 ratchov 829: return;
1.12 ratchov 830: p->u.ctl.slot[index].ops = NULL;
1.13 ratchov 831: if (!(p->flags & APROC_QUIT))
832: return;
833: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
834: if (s->ops)
835: return;
836: }
1.34 ratchov 837: if (LIST_EMPTY(&p->ins))
1.13 ratchov 838: aproc_del(p);
839: }
840:
841: /*
842: * called at every clock tick by the mixer, delta is positive, unless
843: * there's an overrun/underrun
844: */
845: void
846: ctl_ontick(struct aproc *p, int delta)
847: {
848: int qfrlen;
849:
850: /*
851: * don't send ticks before the start signal
852: */
853: if (p->u.ctl.tstate != CTL_RUN)
854: return;
855:
856: p->u.ctl.delta += delta * MTC_SEC;
857:
858: /*
859: * don't send ticks during the count-down
860: */
861: if (p->u.ctl.delta < 0)
862: return;
863:
1.25 ratchov 864: qfrlen = p->u.ctl.dev->rate * (MTC_SEC / (4 * p->u.ctl.fps));
1.13 ratchov 865: while (p->u.ctl.delta >= qfrlen) {
866: ctl_qfr(p);
867: p->u.ctl.delta -= qfrlen;
868: }
1.3 ratchov 869: }
870:
1.7 ratchov 871: /*
872: * notifty the mixer that volume changed, called by whom allocad the slot using
873: * ctl_slotnew(). Note: it doesn't make sens to call this from within the
874: * call-back.
875: */
1.3 ratchov 876: void
877: ctl_slotvol(struct aproc *p, int slot, unsigned vol)
878: {
879: unsigned char msg[3];
880:
1.19 ratchov 881: if (!APROC_OK(p))
1.13 ratchov 882: return;
1.14 ratchov 883: #ifdef DEBUG
884: if (debug_level >= 3) {
885: ctl_slotdbg(p, slot);
886: dbg_puts(": changing volume to ");
887: dbg_putu(vol);
888: dbg_puts("\n");
889: }
890: #endif
1.7 ratchov 891: p->u.ctl.slot[slot].vol = vol;
1.33 ratchov 892: ctl_msg_vol(p, slot, msg);
1.3 ratchov 893: ctl_sendmsg(p, NULL, msg, 3);
894: }
895:
1.7 ratchov 896: /*
1.13 ratchov 897: * notify the MMC layer that the stream is attempting
898: * to start. If other streams are not ready, 0 is returned meaning
899: * that the stream should wait. If other streams are ready, they
900: * are started, and the caller should start immediately.
901: */
902: int
903: ctl_slotstart(struct aproc *p, int slot)
904: {
905: struct ctl_slot *s = p->u.ctl.slot + slot;
906:
1.19 ratchov 907: if (!APROC_OK(p))
1.13 ratchov 908: return 1;
909: if (s->tstate == CTL_OFF || p->u.ctl.tstate == CTL_OFF)
910: return 1;
911:
912: /*
913: * if the server already started (the client missed the
914: * start rendez-vous) or the server is stopped, then
915: * tag the client as ``wanting to start''
916: */
917: s->tstate = CTL_START;
918: return ctl_trystart(p, slot);
919: }
920:
921: /*
922: * notify the MMC layer that the stream no longer is trying to
923: * start (or that it just stopped), meaning that its ``start'' call-back
924: * shouldn't be called anymore
925: */
926: void
927: ctl_slotstop(struct aproc *p, int slot)
928: {
929: struct ctl_slot *s = p->u.ctl.slot + slot;
930:
1.19 ratchov 931: if (!APROC_OK(p))
1.13 ratchov 932: return;
933: /*
934: * tag the stream as not trying to start,
935: * unless MMC is turned off
936: */
937: if (s->tstate != CTL_OFF)
938: s->tstate = CTL_STOP;
939: }
940:
941: /*
1.19 ratchov 942: * start all slots simultaneously
943: */
944: void
945: ctl_start(struct aproc *p)
946: {
947: if (!APROC_OK(p))
948: return;
949: if (p->u.ctl.tstate == CTL_STOP) {
950: p->u.ctl.tstate = CTL_START;
951: (void)ctl_trystart(p, -1);
952: #ifdef DEBUG
953: } else {
954: if (debug_level >= 3) {
955: aproc_dbg(p);
956: dbg_puts(": ignoring mmc start\n");
957: }
958: #endif
959: }
960: }
961:
962: /*
963: * stop all slots simultaneously
964: */
965: void
966: ctl_stop(struct aproc *p)
967: {
968: unsigned i;
969: struct ctl_slot *s;
970:
971: if (!APROC_OK(p))
972: return;
973: switch (p->u.ctl.tstate) {
974: case CTL_START:
975: p->u.ctl.tstate = CTL_STOP;
976: return;
977: case CTL_RUN:
978: p->u.ctl.tstate = CTL_STOP;
979: break;
980: default:
981: #ifdef DEBUG
982: if (debug_level >= 3) {
983: aproc_dbg(p);
984: dbg_puts(": ignored mmc stop\n");
985: }
986: #endif
987: return;
988: }
989: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
990: if (!s->ops)
991: continue;
992: if (s->tstate == CTL_RUN) {
993: #ifdef DEBUG
994: if (debug_level >= 3) {
995: ctl_slotdbg(p, i);
996: dbg_puts(": requested to stop\n");
997: }
998: #endif
999: s->ops->stop(s->arg);
1000: }
1001: }
1002: }
1003:
1004: /*
1005: * relocate all slots simultaneously
1006: */
1007: void
1008: ctl_loc(struct aproc *p, unsigned origin)
1009: {
1010: unsigned i, tstate;
1011: struct ctl_slot *s;
1012:
1013: if (!APROC_OK(p))
1014: return;
1015: #ifdef DEBUG
1016: if (debug_level >= 2) {
1017: dbg_puts("server relocated to ");
1018: dbg_putu(origin);
1019: dbg_puts("\n");
1020: }
1021: #endif
1022: tstate = p->u.ctl.tstate;
1023: if (tstate == CTL_RUN)
1024: ctl_stop(p);
1025: p->u.ctl.origin = origin;
1026: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
1027: if (!s->ops)
1028: continue;
1029: s->ops->loc(s->arg, p->u.ctl.origin);
1030: }
1031: if (tstate == CTL_RUN)
1032: ctl_start(p);
1033: }
1034:
1035: /*
1036: * check if there are controlled streams
1037: */
1038: int
1039: ctl_idle(struct aproc *p)
1040: {
1041: unsigned i;
1042: struct ctl_slot *s;
1043:
1044: if (!APROC_OK(p))
1045: return 1;
1046: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
1047: if (s->ops)
1048: return 0;
1049: }
1050: return 1;
1051: }
1052:
1053: /*
1.7 ratchov 1054: * handle a MIDI event received from ibuf
1055: */
1.3 ratchov 1056: void
1057: ctl_ev(struct aproc *p, struct abuf *ibuf)
1058: {
1059: unsigned chan;
1.5 ratchov 1060: struct ctl_slot *slot;
1.33 ratchov 1061: struct sysex *x;
1062: unsigned fps, len;
1.14 ratchov 1063: #ifdef DEBUG
1064: unsigned i;
1065:
1066: if (debug_level >= 3) {
1067: abuf_dbg(ibuf);
1068: dbg_puts(": got event:");
1069: for (i = 0; i < ibuf->r.midi.idx; i++) {
1070: dbg_puts(" ");
1071: dbg_putx(ibuf->r.midi.msg[i]);
1072: }
1073: dbg_puts("\n");
1074: }
1075: #endif
1.10 ratchov 1076: if ((ibuf->r.midi.msg[0] & MIDI_CMDMASK) == MIDI_CTL &&
1.33 ratchov 1077: (ibuf->r.midi.msg[1] == MIDI_CTLVOL)) {
1.10 ratchov 1078: chan = ibuf->r.midi.msg[0] & MIDI_CHANMASK;
1.3 ratchov 1079: if (chan >= CTL_NSLOT)
1080: return;
1.5 ratchov 1081: slot = p->u.ctl.slot + chan;
1.22 ratchov 1082: slot->vol = ibuf->r.midi.msg[2];
1.12 ratchov 1083: if (slot->ops == NULL)
1.3 ratchov 1084: return;
1.12 ratchov 1085: slot->ops->vol(slot->arg, slot->vol);
1.10 ratchov 1086: ctl_sendmsg(p, ibuf, ibuf->r.midi.msg, ibuf->r.midi.len);
1.3 ratchov 1087: }
1.33 ratchov 1088: x = (struct sysex *)ibuf->r.midi.msg;
1089: len = ibuf->r.midi.idx;
1090: if (x->start != SYSEX_START)
1091: return;
1092: if (len < SYSEX_SIZE(empty))
1093: return;
1094: switch (x->type) {
1095: case SYSEX_TYPE_RT:
1096: if (x->id0 != SYSEX_MMC)
1097: return;
1098: switch (x->id1) {
1099: case SYSEX_MMC_STOP:
1100: if (len != SYSEX_SIZE(stop))
1101: return;
1.14 ratchov 1102: #ifdef DEBUG
1.19 ratchov 1103: if (debug_level >= 3) {
1.14 ratchov 1104: abuf_dbg(ibuf);
1105: dbg_puts(": mmc stop\n");
1106: }
1107: #endif
1.19 ratchov 1108: ctl_stop(p);
1.13 ratchov 1109: break;
1.33 ratchov 1110: case SYSEX_MMC_START:
1111: if (len != SYSEX_SIZE(start))
1112: return;
1.14 ratchov 1113: #ifdef DEBUG
1.19 ratchov 1114: if (debug_level >= 3) {
1.14 ratchov 1115: abuf_dbg(ibuf);
1116: dbg_puts(": mmc start\n");
1117: }
1118: #endif
1.19 ratchov 1119: ctl_start(p);
1.13 ratchov 1120: break;
1.33 ratchov 1121: case SYSEX_MMC_LOC:
1122: if (len != SYSEX_SIZE(loc) ||
1123: x->u.loc.len != SYSEX_MMC_LOC_LEN ||
1124: x->u.loc.cmd != SYSEX_MMC_LOC_CMD)
1125: return;
1126: switch (x->u.loc.hr >> 5) {
1127: case MTC_FPS_24:
1128: fps = 24;
1129: break;
1130: case MTC_FPS_25:
1131: fps = 25;
1132: break;
1133: case MTC_FPS_30:
1134: fps = 30;
1135: break;
1136: default:
1137: p->u.ctl.origin = 0;
1138: return;
1139: }
1140: ctl_loc(p,
1141: (x->u.loc.hr & 0x1f) * 3600 * MTC_SEC +
1142: x->u.loc.min * 60 * MTC_SEC +
1143: x->u.loc.sec * MTC_SEC +
1144: x->u.loc.fr * (MTC_SEC / fps) +
1145: x->u.loc.cent * (MTC_SEC / 100 / fps));
1.13 ratchov 1146: break;
1147: }
1.35 ! ratchov 1148: break;
! 1149: case SYSEX_TYPE_EDU:
! 1150: if (x->id0 != SYSEX_AUCAT || x->id1 != SYSEX_AUCAT_DUMPREQ)
! 1151: return;
! 1152: if (len != SYSEX_SIZE(dumpreq))
! 1153: return;
! 1154: dbg_puts("dump request\n");
! 1155: if (ibuf->duplex)
! 1156: ctl_dump(p, ibuf->duplex);
1.33 ratchov 1157: break;
1.13 ratchov 1158: }
1.3 ratchov 1159: }
1160:
1161: int
1162: ctl_in(struct aproc *p, struct abuf *ibuf)
1163: {
1164: unsigned char *idata;
1165: unsigned c, i, icount;
1166:
1167: if (!ABUF_ROK(ibuf))
1168: return 0;
1169: idata = abuf_rgetblk(ibuf, &icount, 0);
1170: for (i = 0; i < icount; i++) {
1171: c = *idata++;
1.8 ratchov 1172: if (c >= 0xf8) {
1173: /* clock events not used yet */
1174: } else if (c >= 0xf0) {
1.10 ratchov 1175: if (ibuf->r.midi.st == 0xf0 && c == 0xf7 &&
1176: ibuf->r.midi.idx < MIDI_MSGMAX) {
1177: ibuf->r.midi.msg[ibuf->r.midi.idx++] = c;
1.8 ratchov 1178: ctl_ev(p, ibuf);
1179: continue;
1180: }
1.10 ratchov 1181: ibuf->r.midi.msg[0] = c;
1182: ibuf->r.midi.len = common_len[c & 7];
1183: ibuf->r.midi.st = c;
1184: ibuf->r.midi.idx = 1;
1.3 ratchov 1185: } else if (c >= 0x80) {
1.10 ratchov 1186: ibuf->r.midi.msg[0] = c;
1187: ibuf->r.midi.len = voice_len[(c >> 4) & 7];
1188: ibuf->r.midi.st = c;
1189: ibuf->r.midi.idx = 1;
1190: } else if (ibuf->r.midi.st) {
1191: if (ibuf->r.midi.idx == MIDI_MSGMAX)
1.8 ratchov 1192: continue;
1.10 ratchov 1193: if (ibuf->r.midi.idx == 0)
1194: ibuf->r.midi.msg[ibuf->r.midi.idx++] = ibuf->r.midi.st;
1195: ibuf->r.midi.msg[ibuf->r.midi.idx++] = c;
1196: if (ibuf->r.midi.idx == ibuf->r.midi.len) {
1.3 ratchov 1197: ctl_ev(p, ibuf);
1.10 ratchov 1198: ibuf->r.midi.idx = 0;
1.3 ratchov 1199: }
1200: }
1201: }
1202: abuf_rdiscard(ibuf, icount);
1203: return 1;
1204: }
1205:
1206: int
1207: ctl_out(struct aproc *p, struct abuf *obuf)
1208: {
1209: return 0;
1210: }
1211:
1212: void
1213: ctl_eof(struct aproc *p, struct abuf *ibuf)
1214: {
1.13 ratchov 1215: unsigned i;
1216: struct ctl_slot *s;
1217:
1218: if (!(p->flags & APROC_QUIT))
1219: return;
1220: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
1.27 ratchov 1221: if (s->ops != NULL)
1222: s->ops->quit(s->arg);
1.13 ratchov 1223: }
1.34 ratchov 1224: if (LIST_EMPTY(&p->ins))
1.13 ratchov 1225: aproc_del(p);
1.3 ratchov 1226: }
1227:
1228: void
1229: ctl_hup(struct aproc *p, struct abuf *obuf)
1230: {
1.13 ratchov 1231: unsigned i;
1232: struct ctl_slot *s;
1233:
1234: if (!(p->flags & APROC_QUIT))
1235: return;
1236: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
1237: if (s->ops)
1238: return;
1239: }
1.34 ratchov 1240: if (LIST_EMPTY(&p->ins))
1.13 ratchov 1241: aproc_del(p);
1.3 ratchov 1242: }
1243:
1244: void
1245: ctl_newin(struct aproc *p, struct abuf *ibuf)
1246: {
1.10 ratchov 1247: ibuf->r.midi.used = 0;
1248: ibuf->r.midi.len = 0;
1249: ibuf->r.midi.idx = 0;
1250: ibuf->r.midi.st = 0;
1.3 ratchov 1251: }
1252:
1253: void
1254: ctl_done(struct aproc *p)
1255: {
1.14 ratchov 1256: unsigned i;
1257: struct ctl_slot *s;
1258:
1259: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
1.25 ratchov 1260: if (s->ops != NULL)
1261: s->ops->quit(s->arg);
1.14 ratchov 1262: }
1.3 ratchov 1263: }
1264:
1265: struct aproc_ops ctl_ops = {
1266: "ctl",
1267: ctl_in,
1268: ctl_out,
1269: ctl_eof,
1270: ctl_hup,
1271: ctl_newin,
1272: NULL, /* newout */
1273: NULL, /* ipos */
1274: NULL, /* opos */
1275: ctl_done
1276: };
1277:
1278: struct aproc *
1.25 ratchov 1279: ctl_new(char *name, struct dev *dev)
1.3 ratchov 1280: {
1281: struct aproc *p;
1.5 ratchov 1282: struct ctl_slot *s;
1.3 ratchov 1283: unsigned i;
1284:
1285: p = aproc_new(&ctl_ops, name);
1.25 ratchov 1286: p->u.ctl.dev = dev;
1.6 ratchov 1287: p->u.ctl.serial = 0;
1.13 ratchov 1288: p->u.ctl.tstate = CTL_STOP;
1.5 ratchov 1289: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
1.3 ratchov 1290: p->u.ctl.slot[i].unit = i;
1.12 ratchov 1291: p->u.ctl.slot[i].ops = NULL;
1.7 ratchov 1292: p->u.ctl.slot[i].vol = MIDI_MAXCTL;
1.13 ratchov 1293: p->u.ctl.slot[i].tstate = CTL_OFF;
1.6 ratchov 1294: p->u.ctl.slot[i].serial = p->u.ctl.serial++;
1.22 ratchov 1295: p->u.ctl.slot[i].name[0] = '\0';
1.3 ratchov 1296: }
1.1 ratchov 1297: return p;
1298: }