xref: /openbsd/usr.bin/sndiod/midi.c (revision 63371a7f)
1*63371a7fSjsg /*	$OpenBSD: midi.c,v 1.31 2024/05/19 00:05:43 jsg 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 
5987bc9f6aSratchov void
midi_log(struct midi * ep)6087bc9f6aSratchov midi_log(struct midi *ep)
6187bc9f6aSratchov {
6287bc9f6aSratchov 	log_puts("midi");
6387bc9f6aSratchov 	log_putu(ep - midi_ep);
6487bc9f6aSratchov }
6587bc9f6aSratchov 
6687bc9f6aSratchov void
midi_init(void)6787bc9f6aSratchov midi_init(void)
6887bc9f6aSratchov {
6987bc9f6aSratchov }
7087bc9f6aSratchov 
7187bc9f6aSratchov void
midi_done(void)7287bc9f6aSratchov midi_done(void)
7387bc9f6aSratchov {
7487bc9f6aSratchov }
7587bc9f6aSratchov 
7687bc9f6aSratchov struct midi *
midi_new(struct midiops * ops,void * arg,int mode)7787bc9f6aSratchov midi_new(struct midiops *ops, void *arg, int mode)
7887bc9f6aSratchov {
7987bc9f6aSratchov 	int i;
8087bc9f6aSratchov 	struct midi *ep;
8187bc9f6aSratchov 
8287bc9f6aSratchov 	for (i = 0, ep = midi_ep;; i++, ep++) {
8387bc9f6aSratchov 		if (i == MIDI_NEP)
8487bc9f6aSratchov 			return NULL;
8587bc9f6aSratchov 		if (ep->ops == NULL)
8687bc9f6aSratchov 			break;
8787bc9f6aSratchov 	}
8887bc9f6aSratchov 	ep->ops = ops;
8987bc9f6aSratchov 	ep->arg = arg;
9087bc9f6aSratchov 	ep->used = 0;
9187bc9f6aSratchov 	ep->len = 0;
9287bc9f6aSratchov 	ep->idx = 0;
9387bc9f6aSratchov 	ep->st = 0;
94c386404fSratchov 	ep->last_st = 0;
9587bc9f6aSratchov 	ep->txmask = 0;
9607826207Sratchov 	ep->self = 1 << i;
9707826207Sratchov 	ep->tickets = 0;
9887bc9f6aSratchov 	ep->mode = mode;
9907826207Sratchov 
10087bc9f6aSratchov 	/*
101da3adc54Sratchov 	 * the output buffer is the client input
10287bc9f6aSratchov 	 */
10307826207Sratchov 	if (ep->mode & MODE_MIDIIN)
10487bc9f6aSratchov 		abuf_init(&ep->obuf, MIDI_BUFSZ);
10507826207Sratchov 	midi_tickets(ep);
10687bc9f6aSratchov 	return ep;
10787bc9f6aSratchov }
10887bc9f6aSratchov 
10987bc9f6aSratchov void
midi_del(struct midi * ep)11087bc9f6aSratchov midi_del(struct midi *ep)
11187bc9f6aSratchov {
11287bc9f6aSratchov 	int i;
11307826207Sratchov 	struct midi *peer;
11487bc9f6aSratchov 
11507826207Sratchov 	ep->txmask = 0;
11607826207Sratchov 	for (i = 0; i < MIDI_NEP; i++) {
11707826207Sratchov 		peer = midi_ep + i;
11807826207Sratchov 		if (peer->txmask & ep->self) {
11907826207Sratchov 			peer->txmask &= ~ep->self;
12007826207Sratchov 			midi_tickets(peer);
12187bc9f6aSratchov 		}
12207826207Sratchov 	}
12307826207Sratchov 	for (i = 0; i < MIDITHRU_NMAX; i++) {
12407826207Sratchov 		midithru[i].txmask &= ~ep->self;
12507826207Sratchov 		midithru[i].rxmask &= ~ep->self;
12607826207Sratchov 	}
12707826207Sratchov 	ep->ops = NULL;
12887bc9f6aSratchov 	if (ep->mode & MODE_MIDIIN) {
12987bc9f6aSratchov 		abuf_done(&ep->obuf);
13087bc9f6aSratchov 	}
13187bc9f6aSratchov }
13287bc9f6aSratchov 
13387bc9f6aSratchov /*
13407826207Sratchov  * connect two midi endpoints
13507826207Sratchov  */
13607826207Sratchov void
midi_link(struct midi * ep,struct midi * peer)13707826207Sratchov midi_link(struct midi *ep, struct midi *peer)
13807826207Sratchov {
13907826207Sratchov 	if (ep->mode & MODE_MIDIOUT) {
14007826207Sratchov 		ep->txmask |= peer->self;
14107826207Sratchov 		midi_tickets(ep);
14207826207Sratchov 	}
14307826207Sratchov 	if (ep->mode & MODE_MIDIIN) {
14407826207Sratchov #ifdef DEBUG
14507826207Sratchov 		if (ep->obuf.used > 0) {
14607826207Sratchov 			midi_log(ep);
14707826207Sratchov 			log_puts(": linked with non-empty buffer\n");
14807826207Sratchov 			panic();
14907826207Sratchov 		}
15007826207Sratchov #endif
151da3adc54Sratchov 		/* ep has empty buffer, so no need to call midi_tickets() */
15207826207Sratchov 		peer->txmask |= ep->self;
15307826207Sratchov 	}
15407826207Sratchov }
15507826207Sratchov 
15607826207Sratchov /*
1571342ff69Sratchov  * return the list of endpoints the given one receives from
1581342ff69Sratchov  */
1591342ff69Sratchov unsigned int
midi_rxmask(struct midi * ep)1601342ff69Sratchov midi_rxmask(struct midi *ep)
1611342ff69Sratchov {
1621342ff69Sratchov 	int i, rxmask;
1631342ff69Sratchov 
1641342ff69Sratchov 	for (rxmask = 0, i = 0; i < MIDI_NEP; i++) {
1651342ff69Sratchov 		if ((midi_ep[i].txmask & ep->self) == 0)
1661342ff69Sratchov 			continue;
1671342ff69Sratchov 		rxmask |= midi_ep[i].self;
1681342ff69Sratchov 	}
1691342ff69Sratchov 
1701342ff69Sratchov 	return rxmask;
1711342ff69Sratchov }
1721342ff69Sratchov 
1731342ff69Sratchov /*
17487bc9f6aSratchov  * add the midi endpoint in the ``tag'' midi thru box
17587bc9f6aSratchov  */
17687bc9f6aSratchov void
midi_tag(struct midi * ep,unsigned int tag)17787bc9f6aSratchov midi_tag(struct midi *ep, unsigned int tag)
17887bc9f6aSratchov {
17907826207Sratchov 	struct midi *peer;
18007826207Sratchov 	struct midithru *t = midithru + tag;
18187bc9f6aSratchov 	int i;
18287bc9f6aSratchov 
18307826207Sratchov 	if (ep->mode & MODE_MIDIOUT) {
18407826207Sratchov 		ep->txmask |= t->txmask;
18507826207Sratchov 		midi_tickets(ep);
18607826207Sratchov 	}
18707826207Sratchov 	if (ep->mode & MODE_MIDIIN) {
18807826207Sratchov #ifdef DEBUG
18907826207Sratchov 		if (ep->obuf.used > 0) {
19007826207Sratchov 			midi_log(ep);
19107826207Sratchov 			log_puts(": tagged with non-empty buffer\n");
19207826207Sratchov 			panic();
19307826207Sratchov 		}
19407826207Sratchov #endif
19507826207Sratchov 		for (i = 0; i < MIDI_NEP; i++) {
19607826207Sratchov 			if (!(t->rxmask & (1 << i)))
19787bc9f6aSratchov 				continue;
19807826207Sratchov 			peer = midi_ep + i;
19907826207Sratchov 			peer->txmask |= ep->self;
20007826207Sratchov 		}
20107826207Sratchov 	}
20287bc9f6aSratchov 	if (ep->mode & MODE_MIDIOUT)
20307826207Sratchov 		t->rxmask |= ep->self;
20487bc9f6aSratchov 	if (ep->mode & MODE_MIDIIN)
20507826207Sratchov 		t->txmask |= ep->self;
20687bc9f6aSratchov }
20787bc9f6aSratchov 
20887bc9f6aSratchov /*
209326545d4Sratchov  * return the list of tags
210326545d4Sratchov  */
211326545d4Sratchov unsigned int
midi_tags(struct midi * ep)212326545d4Sratchov midi_tags(struct midi *ep)
213326545d4Sratchov {
214326545d4Sratchov 	int i;
215326545d4Sratchov 	struct midithru *t;
216326545d4Sratchov 	unsigned int tags;
217326545d4Sratchov 
218326545d4Sratchov 	tags = 0;
219326545d4Sratchov 	for (i = 0; i < MIDITHRU_NMAX; i++) {
220326545d4Sratchov 		t = midithru + i;
221326545d4Sratchov 		if ((t->txmask | t->rxmask) & ep->self)
222326545d4Sratchov 			tags |= 1 << i;
223326545d4Sratchov 	}
224326545d4Sratchov 	return tags;
225326545d4Sratchov }
226326545d4Sratchov 
227326545d4Sratchov /*
228b35d71a2Sratchov  * broadcast the given message to other endpoints
22987bc9f6aSratchov  */
23087bc9f6aSratchov void
midi_send(struct midi * iep,unsigned char * msg,int size)23187bc9f6aSratchov midi_send(struct midi *iep, unsigned char *msg, int size)
23287bc9f6aSratchov {
23387bc9f6aSratchov 	struct midi *oep;
23487bc9f6aSratchov 	int i;
23587bc9f6aSratchov 
23687bc9f6aSratchov #ifdef DEBUG
23787bc9f6aSratchov 	if (log_level >= 4) {
23887bc9f6aSratchov 		midi_log(iep);
23987bc9f6aSratchov 		log_puts(": sending:");
24087bc9f6aSratchov 		for (i = 0; i < size; i++) {
24187bc9f6aSratchov 			log_puts(" ");
24287bc9f6aSratchov 			log_putx(msg[i]);
24387bc9f6aSratchov 		}
24487bc9f6aSratchov 		log_puts("\n");
24587bc9f6aSratchov 	}
24687bc9f6aSratchov #endif
24787bc9f6aSratchov 	for (i = 0; i < MIDI_NEP ; i++) {
24887bc9f6aSratchov 		if ((iep->txmask & (1 << i)) == 0)
24987bc9f6aSratchov 			continue;
25087bc9f6aSratchov 		oep = midi_ep + i;
25187bc9f6aSratchov 		if (msg[0] <= 0x7f) {
25287bc9f6aSratchov 			if (oep->owner != iep)
25387bc9f6aSratchov 				continue;
25487bc9f6aSratchov 		} else if (msg[0] <= 0xf7)
25587bc9f6aSratchov 			oep->owner = iep;
25687bc9f6aSratchov #ifdef DEBUG
25787bc9f6aSratchov 		if (log_level >= 4) {
25887bc9f6aSratchov 			midi_log(iep);
25987bc9f6aSratchov 			log_puts(" -> ");
26087bc9f6aSratchov 			midi_log(oep);
26187bc9f6aSratchov 			log_puts("\n");
26287bc9f6aSratchov 		}
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
midi_tickets(struct midi * iep)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
midi_fill(struct midi * oep)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
midi_in(struct midi * iep,unsigned char * idata,int icount)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
midi_out(struct midi * oep,unsigned char * idata,int icount)39387bc9f6aSratchov midi_out(struct midi *oep, unsigned char *idata, int icount)
39487bc9f6aSratchov {
39587bc9f6aSratchov 	unsigned char *odata;
39687bc9f6aSratchov 	int ocount;
39787bc9f6aSratchov #ifdef DEBUG
39887bc9f6aSratchov 	int i;
39987bc9f6aSratchov #endif
40087bc9f6aSratchov 
40187bc9f6aSratchov 	while (icount > 0) {
40287bc9f6aSratchov 		if (oep->obuf.used == oep->obuf.len) {
40387bc9f6aSratchov #ifdef DEBUG
40487bc9f6aSratchov 			if (log_level >= 2) {
40587bc9f6aSratchov 				midi_log(oep);
40607826207Sratchov 				log_puts(": too slow, discarding ");
40787bc9f6aSratchov 				log_putu(oep->obuf.used);
40887bc9f6aSratchov 				log_puts(" bytes\n");
40987bc9f6aSratchov 			}
41087bc9f6aSratchov #endif
41187bc9f6aSratchov 			abuf_rdiscard(&oep->obuf, oep->obuf.used);
41287bc9f6aSratchov 			oep->owner = NULL;
41387bc9f6aSratchov 			return;
41487bc9f6aSratchov 		}
41587bc9f6aSratchov 		odata = abuf_wgetblk(&oep->obuf, &ocount);
41687bc9f6aSratchov 		if (ocount > icount)
41787bc9f6aSratchov 			ocount = icount;
41887bc9f6aSratchov 		memcpy(odata, idata, ocount);
41987bc9f6aSratchov #ifdef DEBUG
42087bc9f6aSratchov 		if (log_level >= 4) {
42187bc9f6aSratchov 			midi_log(oep);
42287bc9f6aSratchov 			log_puts(": out: ");
42387bc9f6aSratchov 			for (i = 0; i < ocount; i++) {
42487bc9f6aSratchov 				log_puts(" ");
42587bc9f6aSratchov 				log_putx(odata[i]);
42687bc9f6aSratchov 			}
42787bc9f6aSratchov 			log_puts("\n");
42887bc9f6aSratchov 		}
42987bc9f6aSratchov #endif
43087bc9f6aSratchov 		abuf_wcommit(&oep->obuf, ocount);
43187bc9f6aSratchov 		icount -= ocount;
43287bc9f6aSratchov 		idata += ocount;
43387bc9f6aSratchov 	}
43487bc9f6aSratchov }
43587bc9f6aSratchov 
436f7f6d88aSratchov /*
437f7f6d88aSratchov  * disconnect clients attached to this end-point
438f7f6d88aSratchov  */
439f7f6d88aSratchov void
midi_abort(struct midi * p)440f7f6d88aSratchov midi_abort(struct midi *p)
441f7f6d88aSratchov {
442f7f6d88aSratchov 	int i;
443f7f6d88aSratchov 	struct midi *ep;
444f7f6d88aSratchov 
445f7f6d88aSratchov 	for (i = 0; i < MIDI_NEP; i++) {
446f7f6d88aSratchov 		ep = midi_ep + i;
447f7f6d88aSratchov 		if ((ep->txmask & p->self) || (p->txmask & ep->self))
448f7f6d88aSratchov 			ep->ops->exit(ep->arg);
449f7f6d88aSratchov 	}
450f7f6d88aSratchov }
451f7f6d88aSratchov 
45236355b88Sratchov /*
45336355b88Sratchov  * connect to "nep" all endpoints currently connected to "oep"
45436355b88Sratchov  */
45536355b88Sratchov void
midi_migrate(struct midi * oep,struct midi * nep)45636355b88Sratchov midi_migrate(struct midi *oep, struct midi *nep)
45736355b88Sratchov {
45836355b88Sratchov 	struct midithru *t;
45936355b88Sratchov 	struct midi *ep;
46036355b88Sratchov 	int i;
46136355b88Sratchov 
46236355b88Sratchov 	for (i = 0; i < MIDITHRU_NMAX; i++) {
46336355b88Sratchov 		t = midithru + i;
46436355b88Sratchov 		if (t->txmask & oep->self) {
46536355b88Sratchov 			t->txmask &= ~oep->self;
46636355b88Sratchov 			t->txmask |= nep->self;
46736355b88Sratchov 		}
46836355b88Sratchov 		if (t->rxmask & oep->self) {
46936355b88Sratchov 			t->rxmask &= ~oep->self;
47036355b88Sratchov 			t->rxmask |= nep->self;
47136355b88Sratchov 		}
47236355b88Sratchov 	}
47336355b88Sratchov 
47436355b88Sratchov 	for (i = 0; i < MIDI_NEP; i++) {
47536355b88Sratchov 		ep = midi_ep + i;
47636355b88Sratchov 		if (ep->txmask & oep->self) {
47736355b88Sratchov 			ep->txmask &= ~oep->self;
47836355b88Sratchov 			ep->txmask |= nep->self;
47936355b88Sratchov 		}
48036355b88Sratchov 	}
48136355b88Sratchov 
48236355b88Sratchov 	for (i = 0; i < MIDI_NEP; i++) {
48336355b88Sratchov 		ep = midi_ep + i;
48436355b88Sratchov 		if (oep->txmask & ep->self) {
48536355b88Sratchov 			oep->txmask &= ~ep->self;
48636355b88Sratchov 			nep->txmask |= ep->self;
48736355b88Sratchov 		}
48836355b88Sratchov 	}
48936355b88Sratchov }
49036355b88Sratchov 
49187bc9f6aSratchov void
port_log(struct port * p)49287bc9f6aSratchov port_log(struct port *p)
49387bc9f6aSratchov {
49487bc9f6aSratchov 	midi_log(p->midi);
49587bc9f6aSratchov }
49687bc9f6aSratchov 
49787bc9f6aSratchov void
port_imsg(void * arg,unsigned char * msg,int size)49887bc9f6aSratchov port_imsg(void *arg, unsigned char *msg, int size)
49987bc9f6aSratchov {
50087bc9f6aSratchov 	struct port *p = arg;
50187bc9f6aSratchov 
50287bc9f6aSratchov 	midi_send(p->midi, msg, size);
50387bc9f6aSratchov }
50487bc9f6aSratchov 
50587bc9f6aSratchov 
50687bc9f6aSratchov void
port_omsg(void * arg,unsigned char * msg,int size)50787bc9f6aSratchov port_omsg(void *arg, unsigned char *msg, int size)
50887bc9f6aSratchov {
50987bc9f6aSratchov 	struct port *p = arg;
51087bc9f6aSratchov 
51187bc9f6aSratchov 	midi_out(p->midi, msg, size);
51287bc9f6aSratchov }
51387bc9f6aSratchov 
51487bc9f6aSratchov void
port_fill(void * arg,int count)51587bc9f6aSratchov port_fill(void *arg, int count)
51687bc9f6aSratchov {
51787bc9f6aSratchov 	/* no flow control */
51887bc9f6aSratchov }
51987bc9f6aSratchov 
52087bc9f6aSratchov void
port_exit(void * arg)52187bc9f6aSratchov port_exit(void *arg)
52287bc9f6aSratchov {
52387bc9f6aSratchov #ifdef DEBUG
52487bc9f6aSratchov 	struct port *p = arg;
52587bc9f6aSratchov 
52687bc9f6aSratchov 	if (log_level >= 3) {
52787bc9f6aSratchov 		port_log(p);
5289fd7fddfSratchov 		log_puts(": port exit\n");
5299fd7fddfSratchov 		panic();
53087bc9f6aSratchov 	}
53187bc9f6aSratchov #endif
53287bc9f6aSratchov }
53387bc9f6aSratchov 
53487bc9f6aSratchov /*
53587bc9f6aSratchov  * create a new midi port
53687bc9f6aSratchov  */
53787bc9f6aSratchov struct port *
port_new(char * path,unsigned int mode,int hold)5386d7b45dbSratchov port_new(char *path, unsigned int mode, int hold)
53987bc9f6aSratchov {
54036355b88Sratchov 	struct port *c;
54187bc9f6aSratchov 
54287bc9f6aSratchov 	c = xmalloc(sizeof(struct port));
54336355b88Sratchov 	c->path = path;
54487bc9f6aSratchov 	c->state = PORT_CFG;
5456d7b45dbSratchov 	c->hold = hold;
54687bc9f6aSratchov 	c->midi = midi_new(&port_midiops, c, mode);
5474ae5676aSratchov 	c->num = midi_portnum++;
54836355b88Sratchov 	c->alt_next = c;
54936355b88Sratchov 	c->next = port_list;
55036355b88Sratchov 	port_list = c;
55187bc9f6aSratchov 	return c;
55287bc9f6aSratchov }
55387bc9f6aSratchov 
55487bc9f6aSratchov /*
55587bc9f6aSratchov  * destroy the given midi port
55687bc9f6aSratchov  */
55787bc9f6aSratchov void
port_del(struct port * c)55887bc9f6aSratchov port_del(struct port *c)
55987bc9f6aSratchov {
56087bc9f6aSratchov 	struct port **p;
56187bc9f6aSratchov 
56287bc9f6aSratchov 	if (c->state != PORT_CFG)
56387bc9f6aSratchov 		port_close(c);
56487bc9f6aSratchov 	midi_del(c->midi);
56587bc9f6aSratchov 	for (p = &port_list; *p != c; p = &(*p)->next) {
56687bc9f6aSratchov #ifdef DEBUG
56787bc9f6aSratchov 		if (*p == NULL) {
56887bc9f6aSratchov 			log_puts("port to delete not on list\n");
56987bc9f6aSratchov 			panic();
57087bc9f6aSratchov 		}
57187bc9f6aSratchov #endif
57287bc9f6aSratchov 	}
57387bc9f6aSratchov 	*p = c->next;
57487bc9f6aSratchov 	xfree(c);
57587bc9f6aSratchov }
57687bc9f6aSratchov 
5779fd7fddfSratchov int
port_ref(struct port * c)5789fd7fddfSratchov port_ref(struct port *c)
5799fd7fddfSratchov {
5809fd7fddfSratchov #ifdef DEBUG
5819fd7fddfSratchov 	if (log_level >= 3) {
5829fd7fddfSratchov 		port_log(c);
5839fd7fddfSratchov 		log_puts(": port requested\n");
5849fd7fddfSratchov 	}
5859fd7fddfSratchov #endif
5869fd7fddfSratchov 	if (c->state == PORT_CFG && !port_open(c))
5879fd7fddfSratchov 		return 0;
5889fd7fddfSratchov 	return 1;
5899fd7fddfSratchov }
5909fd7fddfSratchov 
5919fd7fddfSratchov void
port_unref(struct port * c)5929fd7fddfSratchov port_unref(struct port *c)
5939fd7fddfSratchov {
5949fd7fddfSratchov 	int i, rxmask;
5959fd7fddfSratchov 
5969fd7fddfSratchov #ifdef DEBUG
5979fd7fddfSratchov 	if (log_level >= 3) {
5989fd7fddfSratchov 		port_log(c);
5999fd7fddfSratchov 		log_puts(": port released\n");
6009fd7fddfSratchov 	}
6019fd7fddfSratchov #endif
6029fd7fddfSratchov 	for (rxmask = 0, i = 0; i < MIDI_NEP; i++)
6039fd7fddfSratchov 		rxmask |= midi_ep[i].txmask;
60467db5a8cSratchov 	if ((rxmask & c->midi->self) == 0 && c->midi->txmask == 0 &&
60567db5a8cSratchov 	    c->state == PORT_INIT && !c->hold)
6061e00c562Sratchov 		port_drain(c);
6079fd7fddfSratchov }
6089fd7fddfSratchov 
60987bc9f6aSratchov struct port *
port_alt_ref(int num)61036355b88Sratchov port_alt_ref(int num)
61136355b88Sratchov {
61236355b88Sratchov 	struct port *a, *p;
61336355b88Sratchov 
61436355b88Sratchov 	a = port_bynum(num);
61536355b88Sratchov 	if (a == NULL)
61636355b88Sratchov 		return NULL;
61736355b88Sratchov 
61836355b88Sratchov 	/* circulate to first alt port */
61936355b88Sratchov 	while (a->alt_next->num > a->num)
62036355b88Sratchov 		a = a->alt_next;
62136355b88Sratchov 
62236355b88Sratchov 	p = a;
62336355b88Sratchov 	while (1) {
62436355b88Sratchov 		if (port_ref(p))
62536355b88Sratchov 			break;
62636355b88Sratchov 		p = p->alt_next;
62736355b88Sratchov 		if (p == a)
62836355b88Sratchov 			return NULL;
62936355b88Sratchov 	}
63036355b88Sratchov 
63136355b88Sratchov 	return p;
63236355b88Sratchov }
63336355b88Sratchov 
63436355b88Sratchov struct port *
port_migrate(struct port * op)63536355b88Sratchov port_migrate(struct port *op)
63636355b88Sratchov {
63736355b88Sratchov 	struct port *np;
63836355b88Sratchov 
63936355b88Sratchov 	/* not opened */
64036355b88Sratchov 	if (op->state == PORT_CFG)
64136355b88Sratchov 		return op;
64236355b88Sratchov 
64336355b88Sratchov 	np = op;
64436355b88Sratchov 	while (1) {
64536355b88Sratchov 		/* try next one, circulating through the list */
64636355b88Sratchov 		np = np->alt_next;
64736355b88Sratchov 		if (np == op) {
64836355b88Sratchov 			if (log_level >= 2) {
64936355b88Sratchov 				port_log(op);
65036355b88Sratchov 				log_puts(": no fall-back port found\n");
65136355b88Sratchov 			}
65236355b88Sratchov 			return op;
65336355b88Sratchov 		}
65436355b88Sratchov 
65536355b88Sratchov 		if (port_ref(np))
65636355b88Sratchov 			break;
65736355b88Sratchov 	}
65836355b88Sratchov 
65936355b88Sratchov 	if (log_level >= 2) {
66036355b88Sratchov 		port_log(op);
66136355b88Sratchov 		log_puts(": switching to ");
66236355b88Sratchov 		port_log(np);
66336355b88Sratchov 		log_puts("\n");
66436355b88Sratchov 	}
66536355b88Sratchov 
66636355b88Sratchov 	midi_migrate(op->midi, np->midi);
66736355b88Sratchov 	return np;
66836355b88Sratchov }
66936355b88Sratchov 
67036355b88Sratchov struct port *
port_bynum(int num)67187bc9f6aSratchov port_bynum(int num)
67287bc9f6aSratchov {
67387bc9f6aSratchov 	struct port *p;
67487bc9f6aSratchov 
67587bc9f6aSratchov 	for (p = port_list; p != NULL; p = p->next) {
676bd193770Sratchov 		if (p->num == num)
67787bc9f6aSratchov 			return p;
67887bc9f6aSratchov 	}
67987bc9f6aSratchov 	return NULL;
68087bc9f6aSratchov }
68187bc9f6aSratchov 
68287bc9f6aSratchov int
port_open(struct port * c)68387bc9f6aSratchov port_open(struct port *c)
68487bc9f6aSratchov {
68587bc9f6aSratchov 	if (!port_mio_open(c)) {
68687bc9f6aSratchov 		if (log_level >= 1) {
6870c998ac1Sratchov 			port_log(c);
68887bc9f6aSratchov 			log_puts(": failed to open midi port\n");
68987bc9f6aSratchov 		}
69087bc9f6aSratchov 		return 0;
69187bc9f6aSratchov 	}
69287bc9f6aSratchov 	c->state = PORT_INIT;
69387bc9f6aSratchov 	return 1;
69487bc9f6aSratchov }
69587bc9f6aSratchov 
696804d9feaSratchov int
port_close(struct port * c)697804d9feaSratchov port_close(struct port *c)
698804d9feaSratchov {
69987bc9f6aSratchov #ifdef DEBUG
70087bc9f6aSratchov 	if (c->state == PORT_CFG) {
70187bc9f6aSratchov 		port_log(c);
70287bc9f6aSratchov 		log_puts(": can't close port (not opened)\n");
7039fd7fddfSratchov 		panic();
70487bc9f6aSratchov 	}
70587bc9f6aSratchov #endif
70636355b88Sratchov 	port_log(c);
70736355b88Sratchov 	log_puts(": closed\n");
70887bc9f6aSratchov 	c->state = PORT_CFG;
7099fd7fddfSratchov 	port_mio_close(c);
71087bc9f6aSratchov 	return 1;
71187bc9f6aSratchov }
71287bc9f6aSratchov 
7131e00c562Sratchov void
port_drain(struct port * c)7141e00c562Sratchov port_drain(struct port *c)
7151e00c562Sratchov {
7161e00c562Sratchov 	struct midi *ep = c->midi;
7171e00c562Sratchov 
7181e00c562Sratchov 	if (!(ep->mode & MODE_MIDIOUT) || ep->obuf.used == 0)
7191e00c562Sratchov 		port_close(c);
7201e00c562Sratchov 	else {
7211e00c562Sratchov 		c->state = PORT_DRAIN;
7221e00c562Sratchov #ifdef DEBUG
7231e00c562Sratchov 		if (log_level >= 3) {
7241e00c562Sratchov 			port_log(c);
7251e00c562Sratchov 			log_puts(": draining\n");
7261e00c562Sratchov 		}
7271e00c562Sratchov #endif
7281e00c562Sratchov 	}
7291e00c562Sratchov }
7301e00c562Sratchov 
73187bc9f6aSratchov int
port_init(struct port * c)73287bc9f6aSratchov port_init(struct port *c)
73387bc9f6aSratchov {
7346d7b45dbSratchov 	if (c->hold)
73587bc9f6aSratchov 		return port_open(c);
7366d7b45dbSratchov 	return 1;
73787bc9f6aSratchov }
73887bc9f6aSratchov 
73987bc9f6aSratchov void
port_done(struct port * c)74087bc9f6aSratchov port_done(struct port *c)
74187bc9f6aSratchov {
7421e00c562Sratchov 	if (c->state == PORT_INIT)
7431e00c562Sratchov 		port_drain(c);
74487bc9f6aSratchov }
745