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