xref: /openbsd/usr.bin/sndiod/miofile.c (revision 097a140d)
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