xref: /openbsd/usr.bin/sndiod/midi.c (revision 9fd7fddf)
1*9fd7fddfSratchov /*	$OpenBSD: midi.c,v 1.3 2012/11/30 20:48:00 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 /*
1887bc9f6aSratchov  * TODO
1987bc9f6aSratchov  *
2087bc9f6aSratchov  * use shadow variables (to save NRPNs, LSB of controller)
2187bc9f6aSratchov  * in the midi merger
2287bc9f6aSratchov  */
2387bc9f6aSratchov #include <stdio.h>
2487bc9f6aSratchov #include <stdlib.h>
2587bc9f6aSratchov #include <string.h>
2687bc9f6aSratchov 
2787bc9f6aSratchov #include "abuf.h"
2887bc9f6aSratchov #include "defs.h"
2987bc9f6aSratchov #include "dev.h"
3087bc9f6aSratchov #include "file.h"
3187bc9f6aSratchov #include "midi.h"
3287bc9f6aSratchov #include "miofile.h"
3387bc9f6aSratchov #include "sysex.h"
3487bc9f6aSratchov #include "utils.h"
3587bc9f6aSratchov 
3687bc9f6aSratchov /*
3787bc9f6aSratchov  * input data rate is XFER / TIMO (in bytes per microsecond),
3887bc9f6aSratchov  * it must be slightly larger than the MIDI standard 3125 bytes/s
3987bc9f6aSratchov  */
4087bc9f6aSratchov #define MIDI_XFER 1
4187bc9f6aSratchov #define MIDI_TIMO 100000
4287bc9f6aSratchov 
4387bc9f6aSratchov int  port_open(struct port *);
4487bc9f6aSratchov void port_imsg(void *, unsigned char *, int);
4587bc9f6aSratchov void port_omsg(void *, unsigned char *, int);
4687bc9f6aSratchov void port_fill(void *, int);
4787bc9f6aSratchov void port_exit(void *);
4887bc9f6aSratchov 
4987bc9f6aSratchov struct midiops port_midiops = {
5087bc9f6aSratchov 	port_imsg,
5187bc9f6aSratchov 	port_omsg,
5287bc9f6aSratchov 	port_fill,
5387bc9f6aSratchov 	port_exit
5487bc9f6aSratchov };
5587bc9f6aSratchov 
5687bc9f6aSratchov #define MIDI_NEP 32
5787bc9f6aSratchov struct midi midi_ep[MIDI_NEP];
5887bc9f6aSratchov struct timo midi_timo;
5987bc9f6aSratchov struct port *port_list = NULL;
6087bc9f6aSratchov unsigned int midi_portnum = 0;
6187bc9f6aSratchov 
6287bc9f6aSratchov struct midithru {
6307826207Sratchov 	unsigned int txmask, rxmask;
6487bc9f6aSratchov #define MIDITHRU_NMAX 32
6587bc9f6aSratchov } midithru[MIDITHRU_NMAX];
6687bc9f6aSratchov 
6787bc9f6aSratchov /*
6887bc9f6aSratchov  * length of voice and common messages (status byte included)
6987bc9f6aSratchov  */
7087bc9f6aSratchov unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
7187bc9f6aSratchov unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
7287bc9f6aSratchov 
7387bc9f6aSratchov void
7487bc9f6aSratchov midi_log(struct midi *ep)
7587bc9f6aSratchov {
7687bc9f6aSratchov 	log_puts("midi");
7787bc9f6aSratchov 	log_putu(ep - midi_ep);
7887bc9f6aSratchov }
7987bc9f6aSratchov 
8087bc9f6aSratchov void
8187bc9f6aSratchov midi_ontimo(void *arg)
8287bc9f6aSratchov {
8387bc9f6aSratchov 	int i;
8487bc9f6aSratchov 	struct midi *ep;
8587bc9f6aSratchov 
8687bc9f6aSratchov 	for (i = MIDI_NEP, ep = midi_ep; i > 0; i--, ep++) {
8787bc9f6aSratchov 	}
8887bc9f6aSratchov 	timo_add(&midi_timo, MIDI_TIMO);
8987bc9f6aSratchov }
9087bc9f6aSratchov 
9187bc9f6aSratchov void
9287bc9f6aSratchov midi_init(void)
9387bc9f6aSratchov {
9487bc9f6aSratchov 	timo_set(&midi_timo, midi_ontimo, NULL);
9587bc9f6aSratchov 	timo_add(&midi_timo, MIDI_TIMO);
9687bc9f6aSratchov }
9787bc9f6aSratchov 
9887bc9f6aSratchov void
9987bc9f6aSratchov midi_done(void)
10087bc9f6aSratchov {
10187bc9f6aSratchov 	timo_del(&midi_timo);
10287bc9f6aSratchov }
10387bc9f6aSratchov 
10487bc9f6aSratchov struct midi *
10587bc9f6aSratchov midi_new(struct midiops *ops, void *arg, int mode)
10687bc9f6aSratchov {
10787bc9f6aSratchov 	int i;
10887bc9f6aSratchov 	struct midi *ep;
10987bc9f6aSratchov 
11087bc9f6aSratchov 	for (i = 0, ep = midi_ep;; i++, ep++) {
11187bc9f6aSratchov 		if (i == MIDI_NEP)
11287bc9f6aSratchov 			return NULL;
11387bc9f6aSratchov 		if (ep->ops == NULL)
11487bc9f6aSratchov 			break;
11587bc9f6aSratchov 	}
11687bc9f6aSratchov 	ep->ops = ops;
11787bc9f6aSratchov 	ep->arg = arg;
11887bc9f6aSratchov 	ep->used = 0;
11987bc9f6aSratchov 	ep->len = 0;
12087bc9f6aSratchov 	ep->idx = 0;
12187bc9f6aSratchov 	ep->st = 0;
12287bc9f6aSratchov 	ep->txmask = 0;
12307826207Sratchov 	ep->self = 1 << i;
12407826207Sratchov 	ep->tickets = 0;
12587bc9f6aSratchov 	ep->mode = mode;
12607826207Sratchov 
12787bc9f6aSratchov 	/*
12807826207Sratchov 	 * the output buffer is the client intput
12987bc9f6aSratchov 	 */
13007826207Sratchov 	if (ep->mode & MODE_MIDIIN)
13187bc9f6aSratchov 		abuf_init(&ep->obuf, MIDI_BUFSZ);
13207826207Sratchov 	midi_tickets(ep);
13387bc9f6aSratchov 	return ep;
13487bc9f6aSratchov }
13587bc9f6aSratchov 
13687bc9f6aSratchov void
13787bc9f6aSratchov midi_del(struct midi *ep)
13887bc9f6aSratchov {
13987bc9f6aSratchov 	int i;
14007826207Sratchov 	struct midi *peer;
14187bc9f6aSratchov 
14207826207Sratchov 	ep->txmask = 0;
14307826207Sratchov 	for (i = 0; i < MIDI_NEP; i++) {
14407826207Sratchov 		peer = midi_ep + i;
14507826207Sratchov 		if (peer->txmask & ep->self) {
14607826207Sratchov 			peer->txmask &= ~ep->self;
14707826207Sratchov 			midi_tickets(peer);
14887bc9f6aSratchov 		}
14907826207Sratchov 	}
15007826207Sratchov 	for (i = 0; i < MIDITHRU_NMAX; i++) {
15107826207Sratchov 		midithru[i].txmask &= ~ep->self;
15207826207Sratchov 		midithru[i].rxmask &= ~ep->self;
15307826207Sratchov 	}
15407826207Sratchov 	ep->ops = NULL;
15587bc9f6aSratchov 	if (ep->mode & MODE_MIDIIN) {
15687bc9f6aSratchov 		abuf_done(&ep->obuf);
15787bc9f6aSratchov 	}
15887bc9f6aSratchov }
15987bc9f6aSratchov 
16087bc9f6aSratchov /*
16107826207Sratchov  * connect two midi endpoints
16207826207Sratchov  */
16307826207Sratchov void
16407826207Sratchov midi_link(struct midi *ep, struct midi *peer)
16507826207Sratchov {
16607826207Sratchov 	if (ep->mode & MODE_MIDIOUT) {
16707826207Sratchov 		ep->txmask |= peer->self;
16807826207Sratchov 		midi_tickets(ep);
16907826207Sratchov 	}
17007826207Sratchov 	if (ep->mode & MODE_MIDIIN) {
17107826207Sratchov #ifdef DEBUG
17207826207Sratchov 		if (ep->obuf.used > 0) {
17307826207Sratchov 			midi_log(ep);
17407826207Sratchov 			log_puts(": linked with non-empty buffer\n");
17507826207Sratchov 			panic();
17607826207Sratchov 		}
17707826207Sratchov #endif
17807826207Sratchov 		/* ep has empry buffer, so no need to call midi_tickets() */
17907826207Sratchov 		peer->txmask |= ep->self;
18007826207Sratchov 	}
18107826207Sratchov }
18207826207Sratchov 
18307826207Sratchov /*
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) {
20007826207Sratchov 			midi_log(ep);
20107826207Sratchov 			log_puts(": tagged with non-empty buffer\n");
20207826207Sratchov 			panic();
20307826207Sratchov 		}
20407826207Sratchov #endif
20507826207Sratchov 		for (i = 0; i < MIDI_NEP; i++) {
20607826207Sratchov 			if (!(t->rxmask & (1 << i)))
20787bc9f6aSratchov 				continue;
20807826207Sratchov 			peer = midi_ep + i;
20907826207Sratchov 			peer->txmask |= ep->self;
21007826207Sratchov 		}
21107826207Sratchov 	}
21287bc9f6aSratchov 	if (ep->mode & MODE_MIDIOUT)
21307826207Sratchov 		t->rxmask |= ep->self;
21487bc9f6aSratchov 	if (ep->mode & MODE_MIDIIN)
21507826207Sratchov 		t->txmask |= ep->self;
21687bc9f6aSratchov }
21787bc9f6aSratchov 
21887bc9f6aSratchov /*
21987bc9f6aSratchov  * broadcast the given message to other members of the thru box
22087bc9f6aSratchov  */
22187bc9f6aSratchov void
22287bc9f6aSratchov midi_send(struct midi *iep, unsigned char *msg, int size)
22387bc9f6aSratchov {
22487bc9f6aSratchov 	struct midi *oep;
22587bc9f6aSratchov 	int i;
22687bc9f6aSratchov 
22787bc9f6aSratchov #ifdef DEBUG
22887bc9f6aSratchov 	if (log_level >= 4) {
22987bc9f6aSratchov 		midi_log(iep);
23087bc9f6aSratchov 		log_puts(": sending:");
23187bc9f6aSratchov 		for (i = 0; i < size; i++) {
23287bc9f6aSratchov 			log_puts(" ");
23387bc9f6aSratchov 			log_putx(msg[i]);
23487bc9f6aSratchov 		}
23587bc9f6aSratchov 		log_puts("\n");
23687bc9f6aSratchov 	}
23787bc9f6aSratchov #endif
23887bc9f6aSratchov 	for (i = 0; i < MIDI_NEP ; i++) {
23987bc9f6aSratchov 		if ((iep->txmask & (1 << i)) == 0)
24087bc9f6aSratchov 			continue;
24187bc9f6aSratchov 		oep = midi_ep + i;
24287bc9f6aSratchov 		if (msg[0] <= 0x7f) {
24387bc9f6aSratchov 			if (oep->owner != iep)
24487bc9f6aSratchov 				continue;
24587bc9f6aSratchov 		} else if (msg[0] <= 0xf7)
24687bc9f6aSratchov 			oep->owner = iep;
24787bc9f6aSratchov #ifdef DEBUG
24887bc9f6aSratchov 		if (log_level >= 4) {
24987bc9f6aSratchov 			midi_log(iep);
25087bc9f6aSratchov 			log_puts(" -> ");
25187bc9f6aSratchov 			midi_log(oep);
25287bc9f6aSratchov 			log_puts("\n");
25387bc9f6aSratchov 		}
25487bc9f6aSratchov #endif
25587bc9f6aSratchov 		oep->ops->omsg(oep->arg, msg, size);
25687bc9f6aSratchov 	}
25787bc9f6aSratchov }
25887bc9f6aSratchov 
25987bc9f6aSratchov 
26007826207Sratchov /*
26107826207Sratchov  * determine if we have gained more input tickets, and if so call the
26207826207Sratchov  * fill() call-back to notify the i/o layer that it can send more data
26307826207Sratchov  */
26487bc9f6aSratchov void
26507826207Sratchov midi_tickets(struct midi *iep)
26687bc9f6aSratchov {
26707826207Sratchov 	int i, tickets, avail, maxavail;
26807826207Sratchov 	struct midi *oep;
26987bc9f6aSratchov 
27007826207Sratchov 	maxavail = MIDI_BUFSZ;
27187bc9f6aSratchov 	for (i = 0; i < MIDI_NEP ; i++) {
27207826207Sratchov 		if ((iep->txmask & (1 << i)) == 0)
27387bc9f6aSratchov 			continue;
27407826207Sratchov 		oep = midi_ep + i;
27507826207Sratchov 		avail = oep->obuf.len - oep->obuf.used;
27607826207Sratchov 		if (maxavail > avail)
27707826207Sratchov 			maxavail = avail;
27807826207Sratchov 	}
27907826207Sratchov 
28007826207Sratchov 	/*
28107826207Sratchov 	 * in the worst case output message is twice the
28207826207Sratchov 	 * input message (2-byte messages with running status)
28307826207Sratchov 	 */
28407826207Sratchov 	tickets = maxavail / 2 - iep->tickets;
28507826207Sratchov 	if (tickets > 0) {
28607826207Sratchov 		iep->tickets += tickets;
28707826207Sratchov 		iep->ops->fill(iep->arg, tickets);
28887bc9f6aSratchov 	}
28987bc9f6aSratchov }
29087bc9f6aSratchov 
29187bc9f6aSratchov /*
29207826207Sratchov  * recalculate tickets of endpoints sending data to this one
29387bc9f6aSratchov  */
29487bc9f6aSratchov void
29507826207Sratchov midi_fill(struct midi *oep)
29607826207Sratchov {
29707826207Sratchov 	int i;
29807826207Sratchov 	struct midi *iep;
29907826207Sratchov 
30007826207Sratchov 	for (i = 0; i < MIDI_NEP; i++) {
30107826207Sratchov 		iep = midi_ep + i;
30207826207Sratchov 		if (iep->txmask & oep->self)
30307826207Sratchov 			midi_tickets(iep);
30407826207Sratchov 	}
30507826207Sratchov }
30607826207Sratchov 
30707826207Sratchov /*
30807826207Sratchov  * parse then give data chunk, and calling imsg() for each message
30907826207Sratchov  */
31007826207Sratchov void
31107826207Sratchov midi_in(struct midi *iep, unsigned char *idata, int icount)
31287bc9f6aSratchov {
31387bc9f6aSratchov 	int i;
31487bc9f6aSratchov 	unsigned char c;
31587bc9f6aSratchov 
31687bc9f6aSratchov 	for (i = 0; i < icount; i++) {
31787bc9f6aSratchov 		c = *idata++;
31887bc9f6aSratchov 		if (c >= 0xf8) {
31987bc9f6aSratchov 			if (c != MIDI_ACK)
32087bc9f6aSratchov 				iep->ops->imsg(iep->arg, &c, 1);
32187bc9f6aSratchov 		} else if (c == SYSEX_END) {
32287bc9f6aSratchov 			if (iep->st == SYSEX_START) {
32387bc9f6aSratchov 				iep->msg[iep->idx++] = c;
32487bc9f6aSratchov 				iep->ops->imsg(iep->arg, iep->msg, iep->idx);
32587bc9f6aSratchov 			}
32687bc9f6aSratchov 			iep->st = 0;
32787bc9f6aSratchov 			iep->idx = 0;
32887bc9f6aSratchov 		} else if (c >= 0xf0) {
32987bc9f6aSratchov 			iep->msg[0] = c;
33087bc9f6aSratchov 			iep->len = common_len[c & 7];
33187bc9f6aSratchov 			iep->st = c;
33287bc9f6aSratchov 			iep->idx = 1;
33387bc9f6aSratchov 		} else if (c >= 0x80) {
33487bc9f6aSratchov 			iep->msg[0] = c;
33587bc9f6aSratchov 			iep->len = voice_len[(c >> 4) & 7];
33687bc9f6aSratchov 			iep->st = c;
33787bc9f6aSratchov 			iep->idx = 1;
33887bc9f6aSratchov 		} else if (iep->st) {
33987bc9f6aSratchov 			if (iep->idx == 0 && iep->st != SYSEX_START)
34087bc9f6aSratchov 				iep->msg[iep->idx++] = iep->st;
34187bc9f6aSratchov 			iep->msg[iep->idx++] = c;
34287bc9f6aSratchov 			if (iep->idx == iep->len) {
34387bc9f6aSratchov 				iep->ops->imsg(iep->arg, iep->msg, iep->idx);
34487bc9f6aSratchov 				if (iep->st >= 0xf0)
34587bc9f6aSratchov 					iep->st = 0;
34687bc9f6aSratchov 				iep->idx = 0;
34787bc9f6aSratchov 			} else if (iep->idx == MIDI_MSGMAX) {
34887bc9f6aSratchov 				/* sysex continued */
34987bc9f6aSratchov 				iep->ops->imsg(iep->arg, iep->msg, iep->idx);
35087bc9f6aSratchov 				iep->idx = 0;
35187bc9f6aSratchov 			}
35287bc9f6aSratchov 		}
35387bc9f6aSratchov 	}
35407826207Sratchov 	iep->tickets -= icount;
35507826207Sratchov 	if (iep->tickets < 0)
35607826207Sratchov 		iep->tickets = 0;
35787bc9f6aSratchov }
35887bc9f6aSratchov 
35987bc9f6aSratchov /*
36087bc9f6aSratchov  * store the given message in the output buffer
36187bc9f6aSratchov  */
36287bc9f6aSratchov void
36387bc9f6aSratchov midi_out(struct midi *oep, unsigned char *idata, int icount)
36487bc9f6aSratchov {
36587bc9f6aSratchov 	unsigned char *odata;
36687bc9f6aSratchov 	int ocount;
36787bc9f6aSratchov #ifdef DEBUG
36887bc9f6aSratchov 	int i;
36987bc9f6aSratchov #endif
37087bc9f6aSratchov 
37187bc9f6aSratchov 	while (icount > 0) {
37287bc9f6aSratchov 		if (oep->obuf.used == oep->obuf.len) {
37387bc9f6aSratchov #ifdef DEBUG
37487bc9f6aSratchov 			if (log_level >= 2) {
37587bc9f6aSratchov 				midi_log(oep);
37607826207Sratchov 				log_puts(": too slow, discarding ");
37787bc9f6aSratchov 				log_putu(oep->obuf.used);
37887bc9f6aSratchov 				log_puts(" bytes\n");
37987bc9f6aSratchov 			}
38087bc9f6aSratchov #endif
38187bc9f6aSratchov 			abuf_rdiscard(&oep->obuf, oep->obuf.used);
38287bc9f6aSratchov 			oep->owner = NULL;
38387bc9f6aSratchov 			return;
38487bc9f6aSratchov 		}
38587bc9f6aSratchov 		odata = abuf_wgetblk(&oep->obuf, &ocount);
38687bc9f6aSratchov 		if (ocount > icount)
38787bc9f6aSratchov 			ocount = icount;
38887bc9f6aSratchov 		memcpy(odata, idata, ocount);
38987bc9f6aSratchov #ifdef DEBUG
39087bc9f6aSratchov 		if (log_level >= 4) {
39187bc9f6aSratchov 			midi_log(oep);
39287bc9f6aSratchov 			log_puts(": out: ");
39387bc9f6aSratchov 			for (i = 0; i < ocount; i++) {
39487bc9f6aSratchov 				log_puts(" ");
39587bc9f6aSratchov 				log_putx(odata[i]);
39687bc9f6aSratchov 			}
39787bc9f6aSratchov 			log_puts("\n");
39887bc9f6aSratchov 		}
39987bc9f6aSratchov #endif
40087bc9f6aSratchov 		abuf_wcommit(&oep->obuf, ocount);
40187bc9f6aSratchov 		icount -= ocount;
40287bc9f6aSratchov 		idata += ocount;
40387bc9f6aSratchov 	}
40487bc9f6aSratchov }
40587bc9f6aSratchov 
40687bc9f6aSratchov #ifdef DEBUG
40787bc9f6aSratchov void
40887bc9f6aSratchov port_log(struct port *p)
40987bc9f6aSratchov {
41087bc9f6aSratchov 	midi_log(p->midi);
41187bc9f6aSratchov }
41287bc9f6aSratchov #endif
41387bc9f6aSratchov 
41487bc9f6aSratchov void
41587bc9f6aSratchov port_imsg(void *arg, unsigned char *msg, int size)
41687bc9f6aSratchov {
41787bc9f6aSratchov 	struct port *p = arg;
41887bc9f6aSratchov 
41987bc9f6aSratchov 	midi_send(p->midi, msg, size);
42087bc9f6aSratchov }
42187bc9f6aSratchov 
42287bc9f6aSratchov 
42387bc9f6aSratchov void
42487bc9f6aSratchov port_omsg(void *arg, unsigned char *msg, int size)
42587bc9f6aSratchov {
42687bc9f6aSratchov 	struct port *p = arg;
42787bc9f6aSratchov 
42887bc9f6aSratchov 	midi_out(p->midi, msg, size);
42987bc9f6aSratchov }
43087bc9f6aSratchov 
43187bc9f6aSratchov void
43287bc9f6aSratchov port_fill(void *arg, int count)
43387bc9f6aSratchov {
43487bc9f6aSratchov 	/* no flow control */
43587bc9f6aSratchov }
43687bc9f6aSratchov 
43787bc9f6aSratchov void
43887bc9f6aSratchov port_exit(void *arg)
43987bc9f6aSratchov {
44087bc9f6aSratchov #ifdef DEBUG
44187bc9f6aSratchov 	struct port *p = arg;
44287bc9f6aSratchov 
44387bc9f6aSratchov 	if (log_level >= 3) {
44487bc9f6aSratchov 		port_log(p);
445*9fd7fddfSratchov 		log_puts(": port exit\n");
446*9fd7fddfSratchov 		panic();
44787bc9f6aSratchov 	}
44887bc9f6aSratchov #endif
44987bc9f6aSratchov }
45087bc9f6aSratchov 
45187bc9f6aSratchov /*
45287bc9f6aSratchov  * create a new midi port
45387bc9f6aSratchov  */
45487bc9f6aSratchov struct port *
45587bc9f6aSratchov port_new(char *path, unsigned int mode)
45687bc9f6aSratchov {
45787bc9f6aSratchov 	struct port *c;
45887bc9f6aSratchov 
45987bc9f6aSratchov 	c = xmalloc(sizeof(struct port));
46087bc9f6aSratchov 	c->path = path;
46187bc9f6aSratchov 	c->state = PORT_CFG;
46287bc9f6aSratchov 	c->midi = midi_new(&port_midiops, c, mode);
46387bc9f6aSratchov 	midi_portnum++;
46487bc9f6aSratchov 	c->next = port_list;
46587bc9f6aSratchov 	port_list = c;
46687bc9f6aSratchov 	return c;
46787bc9f6aSratchov }
46887bc9f6aSratchov 
46987bc9f6aSratchov /*
47087bc9f6aSratchov  * destroy the given midi port
47187bc9f6aSratchov  */
47287bc9f6aSratchov void
47387bc9f6aSratchov port_del(struct port *c)
47487bc9f6aSratchov {
47587bc9f6aSratchov 	struct port **p;
47687bc9f6aSratchov 
47787bc9f6aSratchov 	if (c->state != PORT_CFG)
47887bc9f6aSratchov 		port_close(c);
47987bc9f6aSratchov 	midi_del(c->midi);
48087bc9f6aSratchov 	for (p = &port_list; *p != c; p = &(*p)->next) {
48187bc9f6aSratchov #ifdef DEBUG
48287bc9f6aSratchov 		if (*p == NULL) {
48387bc9f6aSratchov 			log_puts("port to delete not on list\n");
48487bc9f6aSratchov 			panic();
48587bc9f6aSratchov 		}
48687bc9f6aSratchov #endif
48787bc9f6aSratchov 	}
48887bc9f6aSratchov 	*p = c->next;
48987bc9f6aSratchov 	xfree(c);
49087bc9f6aSratchov }
49187bc9f6aSratchov 
492*9fd7fddfSratchov int
493*9fd7fddfSratchov port_ref(struct port *c)
494*9fd7fddfSratchov {
495*9fd7fddfSratchov #ifdef DEBUG
496*9fd7fddfSratchov 	if (log_level >= 3) {
497*9fd7fddfSratchov 		port_log(c);
498*9fd7fddfSratchov 		log_puts(": port requested\n");
499*9fd7fddfSratchov 	}
500*9fd7fddfSratchov #endif
501*9fd7fddfSratchov 	if (c->state == PORT_CFG && !port_open(c))
502*9fd7fddfSratchov 		return 0;
503*9fd7fddfSratchov 	return 1;
504*9fd7fddfSratchov }
505*9fd7fddfSratchov 
506*9fd7fddfSratchov void
507*9fd7fddfSratchov port_unref(struct port *c)
508*9fd7fddfSratchov {
509*9fd7fddfSratchov 	int i, rxmask;
510*9fd7fddfSratchov 
511*9fd7fddfSratchov #ifdef DEBUG
512*9fd7fddfSratchov 	if (log_level >= 3) {
513*9fd7fddfSratchov 		port_log(c);
514*9fd7fddfSratchov 		log_puts(": port released\n");
515*9fd7fddfSratchov 	}
516*9fd7fddfSratchov #endif
517*9fd7fddfSratchov 	for (rxmask = 0, i = 0; i < MIDI_NEP; i++)
518*9fd7fddfSratchov 		rxmask |= midi_ep[i].txmask;
519*9fd7fddfSratchov 	if ((rxmask & c->midi->self) == 0 && c->state == PORT_INIT)
520*9fd7fddfSratchov 		port_close(c);
521*9fd7fddfSratchov }
522*9fd7fddfSratchov 
52387bc9f6aSratchov struct port *
52487bc9f6aSratchov port_bynum(int num)
52587bc9f6aSratchov {
52687bc9f6aSratchov 	struct port *p;
52787bc9f6aSratchov 
52887bc9f6aSratchov 	for (p = port_list; p != NULL; p = p->next) {
52987bc9f6aSratchov 		if (num-- == 0)
53087bc9f6aSratchov 			return p;
53187bc9f6aSratchov 	}
53287bc9f6aSratchov 	return NULL;
53387bc9f6aSratchov }
53487bc9f6aSratchov 
53587bc9f6aSratchov int
53687bc9f6aSratchov port_open(struct port *c)
53787bc9f6aSratchov {
53887bc9f6aSratchov 	if (!port_mio_open(c)) {
53987bc9f6aSratchov 		if (log_level >= 1) {
54087bc9f6aSratchov 			log_puts(c->path);
54187bc9f6aSratchov 			log_puts(": failed to open midi port\n");
54287bc9f6aSratchov 		}
54387bc9f6aSratchov 		return 0;
54487bc9f6aSratchov 	}
54587bc9f6aSratchov 	c->state = PORT_INIT;
54687bc9f6aSratchov 	return 1;
54787bc9f6aSratchov }
54887bc9f6aSratchov 
54987bc9f6aSratchov int
55087bc9f6aSratchov port_close(struct port *c)
55187bc9f6aSratchov {
552*9fd7fddfSratchov 	int i;
553*9fd7fddfSratchov 	struct midi *ep;
55487bc9f6aSratchov #ifdef DEBUG
55587bc9f6aSratchov 	if (c->state == PORT_CFG) {
55687bc9f6aSratchov 		port_log(c);
55787bc9f6aSratchov 		log_puts(": can't close port (not opened)\n");
558*9fd7fddfSratchov 		panic();
55987bc9f6aSratchov 	}
56087bc9f6aSratchov #endif
56187bc9f6aSratchov 	c->state = PORT_CFG;
562*9fd7fddfSratchov 	port_mio_close(c);
563*9fd7fddfSratchov 
564*9fd7fddfSratchov 	for (i = 0; i < MIDI_NEP; i++) {
565*9fd7fddfSratchov 		ep = midi_ep + i;
566*9fd7fddfSratchov 		if ((ep->txmask & c->midi->self) ||
567*9fd7fddfSratchov 		    (c->midi->txmask & ep->self))
568*9fd7fddfSratchov 			ep->ops->exit(ep->arg);
569*9fd7fddfSratchov 	}
57087bc9f6aSratchov 	return 1;
57187bc9f6aSratchov }
57287bc9f6aSratchov 
57387bc9f6aSratchov int
57487bc9f6aSratchov port_init(struct port *c)
57587bc9f6aSratchov {
57687bc9f6aSratchov 	return port_open(c);
57787bc9f6aSratchov }
57887bc9f6aSratchov 
57987bc9f6aSratchov void
58087bc9f6aSratchov port_done(struct port *c)
58187bc9f6aSratchov {
58287bc9f6aSratchov 	/* XXX: drain? */
58387bc9f6aSratchov 	if (c->state != PORT_CFG)
58487bc9f6aSratchov 		port_close(c);
58587bc9f6aSratchov }
586