1 /* $OpenBSD: miofile.c,v 1.8 2021/01/28 11:17:58 ratchov Exp $ */ 2 /* 3 * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/time.h> 20 21 #include <poll.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <sndio.h> 26 #include "defs.h" 27 #include "fdpass.h" 28 #include "file.h" 29 #include "midi.h" 30 #include "miofile.h" 31 #include "utils.h" 32 33 int port_mio_pollfd(void *, struct pollfd *); 34 int port_mio_revents(void *, struct pollfd *); 35 void port_mio_in(void *); 36 void port_mio_out(void *); 37 void port_mio_hup(void *); 38 39 struct fileops port_mio_ops = { 40 "mio", 41 port_mio_pollfd, 42 port_mio_revents, 43 port_mio_in, 44 port_mio_out, 45 port_mio_hup 46 }; 47 48 /* 49 * open the port using one of the provided paths 50 */ 51 static struct mio_hdl * 52 port_mio_openlist(struct port *c, unsigned int mode) 53 { 54 struct mio_hdl *hdl; 55 struct name *n; 56 int idx; 57 58 idx = 0; 59 n = c->path_list; 60 while (1) { 61 if (n == NULL) 62 break; 63 hdl = fdpass_mio_open(c->num, idx, mode); 64 if (hdl != NULL) { 65 if (log_level >= 2) { 66 port_log(c); 67 log_puts(": using "); 68 log_puts(n->str); 69 log_puts("\n"); 70 } 71 return hdl; 72 } 73 n = n->next; 74 idx++; 75 } 76 return NULL; 77 } 78 79 int 80 port_mio_open(struct port *p) 81 { 82 p->mio.hdl = port_mio_openlist(p, p->midi->mode); 83 if (p->mio.hdl == NULL) 84 return 0; 85 p->mio.file = file_new(&port_mio_ops, p, "port", mio_nfds(p->mio.hdl)); 86 return 1; 87 } 88 89 /* 90 * Open an alternate port. Upon success, close the old port 91 * and continue using the new one. 92 */ 93 int 94 port_mio_reopen(struct port *p) 95 { 96 struct mio_hdl *hdl; 97 98 hdl = port_mio_openlist(p, p->midi->mode); 99 if (hdl == NULL) { 100 if (log_level >= 1) { 101 port_log(p); 102 log_puts(": couldn't open an alternate port\n"); 103 } 104 return 0; 105 } 106 107 /* close unused device */ 108 file_del(p->mio.file); 109 mio_close(p->mio.hdl); 110 111 p->mio.hdl = hdl; 112 p->mio.file = file_new(&port_mio_ops, p, "port", mio_nfds(hdl)); 113 return 1; 114 } 115 116 void 117 port_mio_close(struct port *p) 118 { 119 file_del(p->mio.file); 120 mio_close(p->mio.hdl); 121 } 122 123 int 124 port_mio_pollfd(void *addr, struct pollfd *pfd) 125 { 126 struct port *p = addr; 127 struct midi *ep = p->midi; 128 int events = 0; 129 130 if (ep->mode & MODE_MIDIIN) 131 events |= POLLIN; 132 if ((ep->mode & MODE_MIDIOUT) && ep->obuf.used > 0) 133 events |= POLLOUT; 134 return mio_pollfd(p->mio.hdl, pfd, events); 135 } 136 137 int 138 port_mio_revents(void *addr, struct pollfd *pfd) 139 { 140 struct port *p = addr; 141 142 return mio_revents(p->mio.hdl, pfd); 143 } 144 145 void 146 port_mio_in(void *arg) 147 { 148 unsigned char data[MIDI_BUFSZ]; 149 struct port *p = arg; 150 struct midi *ep = p->midi; 151 int n; 152 153 for (;;) { 154 n = mio_read(p->mio.hdl, data, MIDI_BUFSZ); 155 if (n == 0) 156 break; 157 midi_in(ep, data, n); 158 } 159 } 160 161 void 162 port_mio_out(void *arg) 163 { 164 struct port *p = arg; 165 struct midi *ep = p->midi; 166 unsigned char *data; 167 int n, count; 168 169 for (;;) { 170 data = abuf_rgetblk(&ep->obuf, &count); 171 if (count == 0) 172 break; 173 n = mio_write(p->mio.hdl, data, count); 174 if (n == 0) 175 break; 176 abuf_rdiscard(&ep->obuf, n); 177 if (n < count) 178 break; 179 } 180 if (p->state == PORT_DRAIN && ep->obuf.used == 0) 181 port_close(p); 182 midi_fill(ep); 183 } 184 185 void 186 port_mio_hup(void *arg) 187 { 188 struct port *p = arg; 189 190 if (!port_reopen(p)) { 191 midi_abort(p->midi); 192 if (p->state != PORT_CFG) 193 port_close(p); 194 } 195 } 196