xref: /openbsd-src/usr.bin/sndiod/midi.c (revision 7b6392009e6e5a7f8e494c162a4d259ea5e13a62)
1*7b639200Sratchov /*	$OpenBSD: midi.c,v 1.32 2024/12/20 07:35:56 ratchov Exp $	*/
287bc9f6aSratchov /*
387bc9f6aSratchov  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
487bc9f6aSratchov  *
587bc9f6aSratchov  * Permission to use, copy, modify, and distribute this software for any
687bc9f6aSratchov  * purpose with or without fee is hereby granted, provided that the above
787bc9f6aSratchov  * copyright notice and this permission notice appear in all copies.
887bc9f6aSratchov  *
987bc9f6aSratchov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1087bc9f6aSratchov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1187bc9f6aSratchov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1287bc9f6aSratchov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1387bc9f6aSratchov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1487bc9f6aSratchov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1587bc9f6aSratchov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1687bc9f6aSratchov  */
1787bc9f6aSratchov #include <stdio.h>
1887bc9f6aSratchov #include <stdlib.h>
1987bc9f6aSratchov #include <string.h>
2087bc9f6aSratchov 
2187bc9f6aSratchov #include "abuf.h"
2287bc9f6aSratchov #include "defs.h"
2387bc9f6aSratchov #include "dev.h"
2487bc9f6aSratchov #include "file.h"
2587bc9f6aSratchov #include "midi.h"
2687bc9f6aSratchov #include "miofile.h"
2787bc9f6aSratchov #include "sysex.h"
2887bc9f6aSratchov #include "utils.h"
2987bc9f6aSratchov 
3087bc9f6aSratchov int  port_open(struct port *);
3187bc9f6aSratchov void port_imsg(void *, unsigned char *, int);
3287bc9f6aSratchov void port_omsg(void *, unsigned char *, int);
3387bc9f6aSratchov void port_fill(void *, int);
3487bc9f6aSratchov void port_exit(void *);
3587bc9f6aSratchov 
3687bc9f6aSratchov struct midiops port_midiops = {
3787bc9f6aSratchov 	port_imsg,
3887bc9f6aSratchov 	port_omsg,
3987bc9f6aSratchov 	port_fill,
4087bc9f6aSratchov 	port_exit
4187bc9f6aSratchov };
4287bc9f6aSratchov 
4387bc9f6aSratchov #define MIDI_NEP 32
4487bc9f6aSratchov struct midi midi_ep[MIDI_NEP];
4587bc9f6aSratchov struct port *port_list = NULL;
4687bc9f6aSratchov unsigned int midi_portnum = 0;
4787bc9f6aSratchov 
4887bc9f6aSratchov struct midithru {
4907826207Sratchov 	unsigned int txmask, rxmask;
5087bc9f6aSratchov #define MIDITHRU_NMAX 32
5187bc9f6aSratchov } midithru[MIDITHRU_NMAX];
5287bc9f6aSratchov 
5387bc9f6aSratchov /*
5487bc9f6aSratchov  * length of voice and common messages (status byte included)
5587bc9f6aSratchov  */
56b020cfe1Snaddy const unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
57b020cfe1Snaddy const unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
5887bc9f6aSratchov 
59*7b639200Sratchov size_t
60*7b639200Sratchov midiev_fmt(char *buf, size_t size, unsigned char *ev, size_t len)
6187bc9f6aSratchov {
62*7b639200Sratchov 	const char *sep = "";
63*7b639200Sratchov 	char *end = buf + size;
64*7b639200Sratchov 	char *p = buf;
65*7b639200Sratchov 	int i;
66*7b639200Sratchov 
67*7b639200Sratchov 	for (i = 0; i < len; i++) {
68*7b639200Sratchov 		if (i == 1)
69*7b639200Sratchov 			sep = " ";
70*7b639200Sratchov 		p += snprintf(p, p < end ? end - p : 0, "%s%02x", sep, ev[i]);
71*7b639200Sratchov 	}
72*7b639200Sratchov 
73*7b639200Sratchov 	return p - buf;
7487bc9f6aSratchov }
7587bc9f6aSratchov 
7687bc9f6aSratchov void
7787bc9f6aSratchov midi_init(void)
7887bc9f6aSratchov {
7987bc9f6aSratchov }
8087bc9f6aSratchov 
8187bc9f6aSratchov void
8287bc9f6aSratchov midi_done(void)
8387bc9f6aSratchov {
8487bc9f6aSratchov }
8587bc9f6aSratchov 
8687bc9f6aSratchov struct midi *
8787bc9f6aSratchov midi_new(struct midiops *ops, void *arg, int mode)
8887bc9f6aSratchov {
8987bc9f6aSratchov 	int i;
9087bc9f6aSratchov 	struct midi *ep;
9187bc9f6aSratchov 
9287bc9f6aSratchov 	for (i = 0, ep = midi_ep;; i++, ep++) {
9387bc9f6aSratchov 		if (i == MIDI_NEP)
9487bc9f6aSratchov 			return NULL;
9587bc9f6aSratchov 		if (ep->ops == NULL)
9687bc9f6aSratchov 			break;
9787bc9f6aSratchov 	}
9887bc9f6aSratchov 	ep->ops = ops;
9987bc9f6aSratchov 	ep->arg = arg;
10087bc9f6aSratchov 	ep->used = 0;
10187bc9f6aSratchov 	ep->len = 0;
10287bc9f6aSratchov 	ep->idx = 0;
10387bc9f6aSratchov 	ep->st = 0;
104c386404fSratchov 	ep->last_st = 0;
10587bc9f6aSratchov 	ep->txmask = 0;
106*7b639200Sratchov 	ep->num = i;
10707826207Sratchov 	ep->self = 1 << i;
10807826207Sratchov 	ep->tickets = 0;
10987bc9f6aSratchov 	ep->mode = mode;
11007826207Sratchov 
11187bc9f6aSratchov 	/*
112da3adc54Sratchov 	 * the output buffer is the client input
11387bc9f6aSratchov 	 */
11407826207Sratchov 	if (ep->mode & MODE_MIDIIN)
11587bc9f6aSratchov 		abuf_init(&ep->obuf, MIDI_BUFSZ);
11607826207Sratchov 	midi_tickets(ep);
11787bc9f6aSratchov 	return ep;
11887bc9f6aSratchov }
11987bc9f6aSratchov 
12087bc9f6aSratchov void
12187bc9f6aSratchov midi_del(struct midi *ep)
12287bc9f6aSratchov {
12387bc9f6aSratchov 	int i;
12407826207Sratchov 	struct midi *peer;
12587bc9f6aSratchov 
12607826207Sratchov 	ep->txmask = 0;
12707826207Sratchov 	for (i = 0; i < MIDI_NEP; i++) {
12807826207Sratchov 		peer = midi_ep + i;
12907826207Sratchov 		if (peer->txmask & ep->self) {
13007826207Sratchov 			peer->txmask &= ~ep->self;
13107826207Sratchov 			midi_tickets(peer);
13287bc9f6aSratchov 		}
13307826207Sratchov 	}
13407826207Sratchov 	for (i = 0; i < MIDITHRU_NMAX; i++) {
13507826207Sratchov 		midithru[i].txmask &= ~ep->self;
13607826207Sratchov 		midithru[i].rxmask &= ~ep->self;
13707826207Sratchov 	}
13807826207Sratchov 	ep->ops = NULL;
13987bc9f6aSratchov 	if (ep->mode & MODE_MIDIIN) {
14087bc9f6aSratchov 		abuf_done(&ep->obuf);
14187bc9f6aSratchov 	}
14287bc9f6aSratchov }
14387bc9f6aSratchov 
14487bc9f6aSratchov /*
14507826207Sratchov  * connect two midi endpoints
14607826207Sratchov  */
14707826207Sratchov void
14807826207Sratchov midi_link(struct midi *ep, struct midi *peer)
14907826207Sratchov {
15007826207Sratchov 	if (ep->mode & MODE_MIDIOUT) {
15107826207Sratchov 		ep->txmask |= peer->self;
15207826207Sratchov 		midi_tickets(ep);
15307826207Sratchov 	}
15407826207Sratchov 	if (ep->mode & MODE_MIDIIN) {
15507826207Sratchov #ifdef DEBUG
15607826207Sratchov 		if (ep->obuf.used > 0) {
157*7b639200Sratchov 			logx(0, "midi%u: linked with non-empty buffer", ep->num);
15807826207Sratchov 			panic();
15907826207Sratchov 		}
16007826207Sratchov #endif
161da3adc54Sratchov 		/* ep has empty buffer, so no need to call midi_tickets() */
16207826207Sratchov 		peer->txmask |= ep->self;
16307826207Sratchov 	}
16407826207Sratchov }
16507826207Sratchov 
16607826207Sratchov /*
1671342ff69Sratchov  * return the list of endpoints the given one receives from
1681342ff69Sratchov  */
1691342ff69Sratchov unsigned int
1701342ff69Sratchov midi_rxmask(struct midi *ep)
1711342ff69Sratchov {
1721342ff69Sratchov 	int i, rxmask;
1731342ff69Sratchov 
1741342ff69Sratchov 	for (rxmask = 0, i = 0; i < MIDI_NEP; i++) {
1751342ff69Sratchov 		if ((midi_ep[i].txmask & ep->self) == 0)
1761342ff69Sratchov 			continue;
1771342ff69Sratchov 		rxmask |= midi_ep[i].self;
1781342ff69Sratchov 	}
1791342ff69Sratchov 
1801342ff69Sratchov 	return rxmask;
1811342ff69Sratchov }
1821342ff69Sratchov 
1831342ff69Sratchov /*
18487bc9f6aSratchov  * add the midi endpoint in the ``tag'' midi thru box
18587bc9f6aSratchov  */
18687bc9f6aSratchov void
18787bc9f6aSratchov midi_tag(struct midi *ep, unsigned int tag)
18887bc9f6aSratchov {
18907826207Sratchov 	struct midi *peer;
19007826207Sratchov 	struct midithru *t = midithru + tag;
19187bc9f6aSratchov 	int i;
19287bc9f6aSratchov 
19307826207Sratchov 	if (ep->mode & MODE_MIDIOUT) {
19407826207Sratchov 		ep->txmask |= t->txmask;
19507826207Sratchov 		midi_tickets(ep);
19607826207Sratchov 	}
19707826207Sratchov 	if (ep->mode & MODE_MIDIIN) {
19807826207Sratchov #ifdef DEBUG
19907826207Sratchov 		if (ep->obuf.used > 0) {
200*7b639200Sratchov 			logx(0, "midi%u: tagged with non-empty buffer", ep->num);
20107826207Sratchov 			panic();
20207826207Sratchov 		}
20307826207Sratchov #endif
20407826207Sratchov 		for (i = 0; i < MIDI_NEP; i++) {
20507826207Sratchov 			if (!(t->rxmask & (1 << i)))
20687bc9f6aSratchov 				continue;
20707826207Sratchov 			peer = midi_ep + i;
20807826207Sratchov 			peer->txmask |= ep->self;
20907826207Sratchov 		}
21007826207Sratchov 	}
21187bc9f6aSratchov 	if (ep->mode & MODE_MIDIOUT)
21207826207Sratchov 		t->rxmask |= ep->self;
21387bc9f6aSratchov 	if (ep->mode & MODE_MIDIIN)
21407826207Sratchov 		t->txmask |= ep->self;
21587bc9f6aSratchov }
21687bc9f6aSratchov 
21787bc9f6aSratchov /*
218326545d4Sratchov  * return the list of tags
219326545d4Sratchov  */
220326545d4Sratchov unsigned int
221326545d4Sratchov midi_tags(struct midi *ep)
222326545d4Sratchov {
223326545d4Sratchov 	int i;
224326545d4Sratchov 	struct midithru *t;
225326545d4Sratchov 	unsigned int tags;
226326545d4Sratchov 
227326545d4Sratchov 	tags = 0;
228326545d4Sratchov 	for (i = 0; i < MIDITHRU_NMAX; i++) {
229326545d4Sratchov 		t = midithru + i;
230326545d4Sratchov 		if ((t->txmask | t->rxmask) & ep->self)
231326545d4Sratchov 			tags |= 1 << i;
232326545d4Sratchov 	}
233326545d4Sratchov 	return tags;
234326545d4Sratchov }
235326545d4Sratchov 
236326545d4Sratchov /*
237b35d71a2Sratchov  * broadcast the given message to other endpoints
23887bc9f6aSratchov  */
23987bc9f6aSratchov void
24087bc9f6aSratchov midi_send(struct midi *iep, unsigned char *msg, int size)
24187bc9f6aSratchov {
242*7b639200Sratchov #ifdef DEBUG
243*7b639200Sratchov 	char str[128];
244*7b639200Sratchov #endif
24587bc9f6aSratchov 	struct midi *oep;
24687bc9f6aSratchov 	int i;
24787bc9f6aSratchov 
24887bc9f6aSratchov #ifdef DEBUG
249*7b639200Sratchov 	logx(4, "midi%u: sending: %s", iep->num,
250*7b639200Sratchov 	    (midiev_fmt(str, sizeof(str), msg, size), str));
25187bc9f6aSratchov #endif
25287bc9f6aSratchov 	for (i = 0; i < MIDI_NEP ; i++) {
25387bc9f6aSratchov 		if ((iep->txmask & (1 << i)) == 0)
25487bc9f6aSratchov 			continue;
25587bc9f6aSratchov 		oep = midi_ep + i;
25687bc9f6aSratchov 		if (msg[0] <= 0x7f) {
25787bc9f6aSratchov 			if (oep->owner != iep)
25887bc9f6aSratchov 				continue;
25987bc9f6aSratchov 		} else if (msg[0] <= 0xf7)
26087bc9f6aSratchov 			oep->owner = iep;
26187bc9f6aSratchov #ifdef DEBUG
262*7b639200Sratchov 		logx(4, "midi%u -> midi%u", iep->num, oep->num);
26387bc9f6aSratchov #endif
26487bc9f6aSratchov 		oep->ops->omsg(oep->arg, msg, size);
26587bc9f6aSratchov 	}
26687bc9f6aSratchov }
26787bc9f6aSratchov 
26807826207Sratchov /*
26907826207Sratchov  * determine if we have gained more input tickets, and if so call the
27007826207Sratchov  * fill() call-back to notify the i/o layer that it can send more data
27107826207Sratchov  */
27287bc9f6aSratchov void
27307826207Sratchov midi_tickets(struct midi *iep)
27487bc9f6aSratchov {
27507826207Sratchov 	int i, tickets, avail, maxavail;
27607826207Sratchov 	struct midi *oep;
27787bc9f6aSratchov 
278ec043492Sratchov 	/*
279ec043492Sratchov 	 * don't request iep->ops->fill() too often as it generates
280ec043492Sratchov 	 * useless network traffic: wait until we reach half of the
281ec043492Sratchov 	 * max tickets count. As in the worst case (see comment below)
282ec043492Sratchov 	 * one ticket may consume two bytes, the max ticket count is
283ec043492Sratchov 	 * BUFSZ / 2 and halt of it is simply BUFSZ / 4.
284ec043492Sratchov 	 */
285ec043492Sratchov 	if (iep->tickets >= MIDI_BUFSZ / 4)
286ec043492Sratchov 		return;
287ec043492Sratchov 
28807826207Sratchov 	maxavail = MIDI_BUFSZ;
28987bc9f6aSratchov 	for (i = 0; i < MIDI_NEP ; i++) {
29007826207Sratchov 		if ((iep->txmask & (1 << i)) == 0)
29187bc9f6aSratchov 			continue;
29207826207Sratchov 		oep = midi_ep + i;
29307826207Sratchov 		avail = oep->obuf.len - oep->obuf.used;
29407826207Sratchov 		if (maxavail > avail)
29507826207Sratchov 			maxavail = avail;
29607826207Sratchov 	}
29707826207Sratchov 
29807826207Sratchov 	/*
29907826207Sratchov 	 * in the worst case output message is twice the
30007826207Sratchov 	 * input message (2-byte messages with running status)
30107826207Sratchov 	 */
30207826207Sratchov 	tickets = maxavail / 2 - iep->tickets;
30307826207Sratchov 	if (tickets > 0) {
30407826207Sratchov 		iep->tickets += tickets;
30507826207Sratchov 		iep->ops->fill(iep->arg, tickets);
30687bc9f6aSratchov 	}
30787bc9f6aSratchov }
30887bc9f6aSratchov 
30987bc9f6aSratchov /*
31007826207Sratchov  * recalculate tickets of endpoints sending data to this one
31187bc9f6aSratchov  */
31287bc9f6aSratchov void
31307826207Sratchov midi_fill(struct midi *oep)
31407826207Sratchov {
31507826207Sratchov 	int i;
31607826207Sratchov 	struct midi *iep;
31707826207Sratchov 
31807826207Sratchov 	for (i = 0; i < MIDI_NEP; i++) {
31907826207Sratchov 		iep = midi_ep + i;
32007826207Sratchov 		if (iep->txmask & oep->self)
32107826207Sratchov 			midi_tickets(iep);
32207826207Sratchov 	}
32307826207Sratchov }
32407826207Sratchov 
32507826207Sratchov /*
32607826207Sratchov  * parse then give data chunk, and calling imsg() for each message
32707826207Sratchov  */
32807826207Sratchov void
32907826207Sratchov midi_in(struct midi *iep, unsigned char *idata, int icount)
33087bc9f6aSratchov {
33187bc9f6aSratchov 	int i;
33287bc9f6aSratchov 	unsigned char c;
33387bc9f6aSratchov 
33487bc9f6aSratchov 	for (i = 0; i < icount; i++) {
33587bc9f6aSratchov 		c = *idata++;
33687bc9f6aSratchov 		if (c >= 0xf8) {
33787bc9f6aSratchov 			if (c != MIDI_ACK)
33887bc9f6aSratchov 				iep->ops->imsg(iep->arg, &c, 1);
33987bc9f6aSratchov 		} else if (c == SYSEX_END) {
34087bc9f6aSratchov 			if (iep->st == SYSEX_START) {
34187bc9f6aSratchov 				iep->msg[iep->idx++] = c;
34287bc9f6aSratchov 				iep->ops->imsg(iep->arg, iep->msg, iep->idx);
34387bc9f6aSratchov 			}
344c386404fSratchov 
345c386404fSratchov 			/*
346c386404fSratchov 			 * There are bogus MIDI sources that keep
347c386404fSratchov 			 * state across sysex; Linux virmidi ports fed
348c386404fSratchov 			 * by the sequencer is an example. We
349c386404fSratchov 			 * workaround this by saving the current
350c386404fSratchov 			 * status and restoring it at the end of the
351c386404fSratchov 			 * sysex.
352c386404fSratchov 			 */
353c386404fSratchov 			iep->st = iep->last_st;
354c386404fSratchov 			if (iep->st)
355c386404fSratchov 				iep->len = voice_len[(iep->st >> 4) & 7];
35687bc9f6aSratchov 			iep->idx = 0;
35787bc9f6aSratchov 		} else if (c >= 0xf0) {
35887bc9f6aSratchov 			iep->msg[0] = c;
359745acca4Sratchov 			iep->len = common_len[c & 7];
36087bc9f6aSratchov 			iep->st = c;
36187bc9f6aSratchov 			iep->idx = 1;
36287bc9f6aSratchov 		} else if (c >= 0x80) {
36387bc9f6aSratchov 			iep->msg[0] = c;
36487bc9f6aSratchov 			iep->len = voice_len[(c >> 4) & 7];
365c386404fSratchov 			iep->last_st = iep->st = c;
36687bc9f6aSratchov 			iep->idx = 1;
36787bc9f6aSratchov 		} else if (iep->st) {
36887bc9f6aSratchov 			if (iep->idx == 0 && iep->st != SYSEX_START)
36987bc9f6aSratchov 				iep->msg[iep->idx++] = iep->st;
37087bc9f6aSratchov 			iep->msg[iep->idx++] = c;
37187bc9f6aSratchov 			if (iep->idx == iep->len) {
37287bc9f6aSratchov 				iep->ops->imsg(iep->arg, iep->msg, iep->idx);
37387bc9f6aSratchov 				if (iep->st >= 0xf0)
37487bc9f6aSratchov 					iep->st = 0;
37587bc9f6aSratchov 				iep->idx = 0;
37687bc9f6aSratchov 			} else if (iep->idx == MIDI_MSGMAX) {
37787bc9f6aSratchov 				/* sysex continued */
37887bc9f6aSratchov 				iep->ops->imsg(iep->arg, iep->msg, iep->idx);
37987bc9f6aSratchov 				iep->idx = 0;
38087bc9f6aSratchov 			}
38187bc9f6aSratchov 		}
38287bc9f6aSratchov 	}
38307826207Sratchov 	iep->tickets -= icount;
38407826207Sratchov 	if (iep->tickets < 0)
38507826207Sratchov 		iep->tickets = 0;
386b096b52eSratchov 	midi_tickets(iep);
38787bc9f6aSratchov }
38887bc9f6aSratchov 
38987bc9f6aSratchov /*
39087bc9f6aSratchov  * store the given message in the output buffer
39187bc9f6aSratchov  */
39287bc9f6aSratchov void
39387bc9f6aSratchov midi_out(struct midi *oep, unsigned char *idata, int icount)
39487bc9f6aSratchov {
395*7b639200Sratchov #ifdef DEBUG
396*7b639200Sratchov 	char str[128];
397*7b639200Sratchov #endif
39887bc9f6aSratchov 	unsigned char *odata;
39987bc9f6aSratchov 	int ocount;
40087bc9f6aSratchov 
40187bc9f6aSratchov 	while (icount > 0) {
40287bc9f6aSratchov 		if (oep->obuf.used == oep->obuf.len) {
40387bc9f6aSratchov #ifdef DEBUG
404*7b639200Sratchov 			logx(2, "midi%u: too slow, discarding %d bytes",
405*7b639200Sratchov 			    oep->num, oep->obuf.used);
40687bc9f6aSratchov #endif
40787bc9f6aSratchov 			abuf_rdiscard(&oep->obuf, oep->obuf.used);
40887bc9f6aSratchov 			oep->owner = NULL;
40987bc9f6aSratchov 			return;
41087bc9f6aSratchov 		}
41187bc9f6aSratchov 		odata = abuf_wgetblk(&oep->obuf, &ocount);
41287bc9f6aSratchov 		if (ocount > icount)
41387bc9f6aSratchov 			ocount = icount;
41487bc9f6aSratchov 		memcpy(odata, idata, ocount);
41587bc9f6aSratchov #ifdef DEBUG
416*7b639200Sratchov 		logx(4, "midi%u: out: %s", oep->num,
417*7b639200Sratchov 		    (midiev_fmt(str, sizeof(str), odata, ocount), str));
41887bc9f6aSratchov #endif
41987bc9f6aSratchov 		abuf_wcommit(&oep->obuf, ocount);
42087bc9f6aSratchov 		icount -= ocount;
42187bc9f6aSratchov 		idata += ocount;
42287bc9f6aSratchov 	}
42387bc9f6aSratchov }
42487bc9f6aSratchov 
425f7f6d88aSratchov /*
426f7f6d88aSratchov  * disconnect clients attached to this end-point
427f7f6d88aSratchov  */
428f7f6d88aSratchov void
429f7f6d88aSratchov midi_abort(struct midi *p)
430f7f6d88aSratchov {
431f7f6d88aSratchov 	int i;
432f7f6d88aSratchov 	struct midi *ep;
433f7f6d88aSratchov 
434f7f6d88aSratchov 	for (i = 0; i < MIDI_NEP; i++) {
435f7f6d88aSratchov 		ep = midi_ep + i;
436f7f6d88aSratchov 		if ((ep->txmask & p->self) || (p->txmask & ep->self))
437f7f6d88aSratchov 			ep->ops->exit(ep->arg);
438f7f6d88aSratchov 	}
439f7f6d88aSratchov }
440f7f6d88aSratchov 
44136355b88Sratchov /*
44236355b88Sratchov  * connect to "nep" all endpoints currently connected to "oep"
44336355b88Sratchov  */
44436355b88Sratchov void
44536355b88Sratchov midi_migrate(struct midi *oep, struct midi *nep)
44636355b88Sratchov {
44736355b88Sratchov 	struct midithru *t;
44836355b88Sratchov 	struct midi *ep;
44936355b88Sratchov 	int i;
45036355b88Sratchov 
45136355b88Sratchov 	for (i = 0; i < MIDITHRU_NMAX; i++) {
45236355b88Sratchov 		t = midithru + i;
45336355b88Sratchov 		if (t->txmask & oep->self) {
45436355b88Sratchov 			t->txmask &= ~oep->self;
45536355b88Sratchov 			t->txmask |= nep->self;
45636355b88Sratchov 		}
45736355b88Sratchov 		if (t->rxmask & oep->self) {
45836355b88Sratchov 			t->rxmask &= ~oep->self;
45936355b88Sratchov 			t->rxmask |= nep->self;
46036355b88Sratchov 		}
46136355b88Sratchov 	}
46236355b88Sratchov 
46336355b88Sratchov 	for (i = 0; i < MIDI_NEP; i++) {
46436355b88Sratchov 		ep = midi_ep + i;
46536355b88Sratchov 		if (ep->txmask & oep->self) {
46636355b88Sratchov 			ep->txmask &= ~oep->self;
46736355b88Sratchov 			ep->txmask |= nep->self;
46836355b88Sratchov 		}
46936355b88Sratchov 	}
47036355b88Sratchov 
47136355b88Sratchov 	for (i = 0; i < MIDI_NEP; i++) {
47236355b88Sratchov 		ep = midi_ep + i;
47336355b88Sratchov 		if (oep->txmask & ep->self) {
47436355b88Sratchov 			oep->txmask &= ~ep->self;
47536355b88Sratchov 			nep->txmask |= ep->self;
47636355b88Sratchov 		}
47736355b88Sratchov 	}
47836355b88Sratchov }
47936355b88Sratchov 
48087bc9f6aSratchov void
48187bc9f6aSratchov port_imsg(void *arg, unsigned char *msg, int size)
48287bc9f6aSratchov {
48387bc9f6aSratchov 	struct port *p = arg;
48487bc9f6aSratchov 
48587bc9f6aSratchov 	midi_send(p->midi, msg, size);
48687bc9f6aSratchov }
48787bc9f6aSratchov 
48887bc9f6aSratchov 
48987bc9f6aSratchov void
49087bc9f6aSratchov port_omsg(void *arg, unsigned char *msg, int size)
49187bc9f6aSratchov {
49287bc9f6aSratchov 	struct port *p = arg;
49387bc9f6aSratchov 
49487bc9f6aSratchov 	midi_out(p->midi, msg, size);
49587bc9f6aSratchov }
49687bc9f6aSratchov 
49787bc9f6aSratchov void
49887bc9f6aSratchov port_fill(void *arg, int count)
49987bc9f6aSratchov {
50087bc9f6aSratchov 	/* no flow control */
50187bc9f6aSratchov }
50287bc9f6aSratchov 
50387bc9f6aSratchov void
50487bc9f6aSratchov port_exit(void *arg)
50587bc9f6aSratchov {
50687bc9f6aSratchov #ifdef DEBUG
50787bc9f6aSratchov 	struct port *p = arg;
50887bc9f6aSratchov 
509*7b639200Sratchov 	logx(0, "midi%u: port exit", p->midi->num);
5109fd7fddfSratchov 	panic();
51187bc9f6aSratchov #endif
51287bc9f6aSratchov }
51387bc9f6aSratchov 
51487bc9f6aSratchov /*
51587bc9f6aSratchov  * create a new midi port
51687bc9f6aSratchov  */
51787bc9f6aSratchov struct port *
5186d7b45dbSratchov port_new(char *path, unsigned int mode, int hold)
51987bc9f6aSratchov {
52036355b88Sratchov 	struct port *c;
52187bc9f6aSratchov 
52287bc9f6aSratchov 	c = xmalloc(sizeof(struct port));
52336355b88Sratchov 	c->path = path;
52487bc9f6aSratchov 	c->state = PORT_CFG;
5256d7b45dbSratchov 	c->hold = hold;
52687bc9f6aSratchov 	c->midi = midi_new(&port_midiops, c, mode);
5274ae5676aSratchov 	c->num = midi_portnum++;
52836355b88Sratchov 	c->alt_next = c;
52936355b88Sratchov 	c->next = port_list;
53036355b88Sratchov 	port_list = c;
53187bc9f6aSratchov 	return c;
53287bc9f6aSratchov }
53387bc9f6aSratchov 
53487bc9f6aSratchov /*
53587bc9f6aSratchov  * destroy the given midi port
53687bc9f6aSratchov  */
53787bc9f6aSratchov void
53887bc9f6aSratchov port_del(struct port *c)
53987bc9f6aSratchov {
54087bc9f6aSratchov 	struct port **p;
54187bc9f6aSratchov 
54287bc9f6aSratchov 	if (c->state != PORT_CFG)
54387bc9f6aSratchov 		port_close(c);
54487bc9f6aSratchov 	midi_del(c->midi);
54587bc9f6aSratchov 	for (p = &port_list; *p != c; p = &(*p)->next) {
54687bc9f6aSratchov #ifdef DEBUG
54787bc9f6aSratchov 		if (*p == NULL) {
548*7b639200Sratchov 			logx(0, "port to delete not on list");
54987bc9f6aSratchov 			panic();
55087bc9f6aSratchov 		}
55187bc9f6aSratchov #endif
55287bc9f6aSratchov 	}
55387bc9f6aSratchov 	*p = c->next;
55487bc9f6aSratchov 	xfree(c);
55587bc9f6aSratchov }
55687bc9f6aSratchov 
5579fd7fddfSratchov int
5589fd7fddfSratchov port_ref(struct port *c)
5599fd7fddfSratchov {
5609fd7fddfSratchov #ifdef DEBUG
561*7b639200Sratchov 	logx(3, "midi%u: port requested", c->midi->num);
5629fd7fddfSratchov #endif
5639fd7fddfSratchov 	if (c->state == PORT_CFG && !port_open(c))
5649fd7fddfSratchov 		return 0;
5659fd7fddfSratchov 	return 1;
5669fd7fddfSratchov }
5679fd7fddfSratchov 
5689fd7fddfSratchov void
5699fd7fddfSratchov port_unref(struct port *c)
5709fd7fddfSratchov {
5719fd7fddfSratchov 	int i, rxmask;
5729fd7fddfSratchov 
5739fd7fddfSratchov #ifdef DEBUG
574*7b639200Sratchov 	logx(3, "midi%u: port released", c->midi->num);
5759fd7fddfSratchov #endif
5769fd7fddfSratchov 	for (rxmask = 0, i = 0; i < MIDI_NEP; i++)
5779fd7fddfSratchov 		rxmask |= midi_ep[i].txmask;
57867db5a8cSratchov 	if ((rxmask & c->midi->self) == 0 && c->midi->txmask == 0 &&
57967db5a8cSratchov 	    c->state == PORT_INIT && !c->hold)
5801e00c562Sratchov 		port_drain(c);
5819fd7fddfSratchov }
5829fd7fddfSratchov 
58387bc9f6aSratchov struct port *
58436355b88Sratchov port_alt_ref(int num)
58536355b88Sratchov {
58636355b88Sratchov 	struct port *a, *p;
58736355b88Sratchov 
58836355b88Sratchov 	a = port_bynum(num);
58936355b88Sratchov 	if (a == NULL)
59036355b88Sratchov 		return NULL;
59136355b88Sratchov 
59236355b88Sratchov 	/* circulate to first alt port */
59336355b88Sratchov 	while (a->alt_next->num > a->num)
59436355b88Sratchov 		a = a->alt_next;
59536355b88Sratchov 
59636355b88Sratchov 	p = a;
59736355b88Sratchov 	while (1) {
59836355b88Sratchov 		if (port_ref(p))
59936355b88Sratchov 			break;
60036355b88Sratchov 		p = p->alt_next;
60136355b88Sratchov 		if (p == a)
60236355b88Sratchov 			return NULL;
60336355b88Sratchov 	}
60436355b88Sratchov 
60536355b88Sratchov 	return p;
60636355b88Sratchov }
60736355b88Sratchov 
60836355b88Sratchov struct port *
60936355b88Sratchov port_migrate(struct port *op)
61036355b88Sratchov {
61136355b88Sratchov 	struct port *np;
61236355b88Sratchov 
61336355b88Sratchov 	/* not opened */
61436355b88Sratchov 	if (op->state == PORT_CFG)
61536355b88Sratchov 		return op;
61636355b88Sratchov 
61736355b88Sratchov 	np = op;
61836355b88Sratchov 	while (1) {
61936355b88Sratchov 		/* try next one, circulating through the list */
62036355b88Sratchov 		np = np->alt_next;
62136355b88Sratchov 		if (np == op) {
622*7b639200Sratchov 			logx(2, "midi%u: no fall-back port found", op->midi->num);
62336355b88Sratchov 			return op;
62436355b88Sratchov 		}
62536355b88Sratchov 
62636355b88Sratchov 		if (port_ref(np))
62736355b88Sratchov 			break;
62836355b88Sratchov 	}
62936355b88Sratchov 
630*7b639200Sratchov 	logx(2, "midi%u: switching to midi%u", op->midi->num, np->midi->num);
63136355b88Sratchov 
63236355b88Sratchov 	midi_migrate(op->midi, np->midi);
63336355b88Sratchov 	return np;
63436355b88Sratchov }
63536355b88Sratchov 
63636355b88Sratchov struct port *
63787bc9f6aSratchov port_bynum(int num)
63887bc9f6aSratchov {
63987bc9f6aSratchov 	struct port *p;
64087bc9f6aSratchov 
64187bc9f6aSratchov 	for (p = port_list; p != NULL; p = p->next) {
642bd193770Sratchov 		if (p->num == num)
64387bc9f6aSratchov 			return p;
64487bc9f6aSratchov 	}
64587bc9f6aSratchov 	return NULL;
64687bc9f6aSratchov }
64787bc9f6aSratchov 
64887bc9f6aSratchov int
64987bc9f6aSratchov port_open(struct port *c)
65087bc9f6aSratchov {
65187bc9f6aSratchov 	if (!port_mio_open(c)) {
652*7b639200Sratchov 		logx(1, "midi%u: failed to open midi port", c->midi->num);
65387bc9f6aSratchov 		return 0;
65487bc9f6aSratchov 	}
65587bc9f6aSratchov 	c->state = PORT_INIT;
65687bc9f6aSratchov 	return 1;
65787bc9f6aSratchov }
65887bc9f6aSratchov 
659804d9feaSratchov int
660804d9feaSratchov port_close(struct port *c)
661804d9feaSratchov {
66287bc9f6aSratchov #ifdef DEBUG
66387bc9f6aSratchov 	if (c->state == PORT_CFG) {
664*7b639200Sratchov 		logx(0, "midi%u: can't close port (not opened)", c->midi->num);
6659fd7fddfSratchov 		panic();
66687bc9f6aSratchov 	}
66787bc9f6aSratchov #endif
668*7b639200Sratchov 	logx(2, "midi%u: closed", c->midi->num);
66987bc9f6aSratchov 	c->state = PORT_CFG;
6709fd7fddfSratchov 	port_mio_close(c);
67187bc9f6aSratchov 	return 1;
67287bc9f6aSratchov }
67387bc9f6aSratchov 
6741e00c562Sratchov void
6751e00c562Sratchov port_drain(struct port *c)
6761e00c562Sratchov {
6771e00c562Sratchov 	struct midi *ep = c->midi;
6781e00c562Sratchov 
6791e00c562Sratchov 	if (!(ep->mode & MODE_MIDIOUT) || ep->obuf.used == 0)
6801e00c562Sratchov 		port_close(c);
6811e00c562Sratchov 	else {
6821e00c562Sratchov 		c->state = PORT_DRAIN;
6831e00c562Sratchov #ifdef DEBUG
684*7b639200Sratchov 		logx(3, "midi%u: draining", c->midi->num);
6851e00c562Sratchov #endif
6861e00c562Sratchov 	}
6871e00c562Sratchov }
6881e00c562Sratchov 
68987bc9f6aSratchov int
69087bc9f6aSratchov port_init(struct port *c)
69187bc9f6aSratchov {
6926d7b45dbSratchov 	if (c->hold)
69387bc9f6aSratchov 		return port_open(c);
6946d7b45dbSratchov 	return 1;
69587bc9f6aSratchov }
69687bc9f6aSratchov 
69787bc9f6aSratchov void
69887bc9f6aSratchov port_done(struct port *c)
69987bc9f6aSratchov {
7001e00c562Sratchov 	if (c->state == PORT_INIT)
7011e00c562Sratchov 		port_drain(c);
70287bc9f6aSratchov }
703