xref: /openbsd/lib/libsndio/mio_rmidi.c (revision 95d14907)
1 /*	$OpenBSD: mio_rmidi.c,v 1.28 2019/06/29 06:05:26 ratchov Exp $	*/
2 /*
3  * Copyright (c) 2008 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 
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <limits.h>
23 #include <poll.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 
29 #include "debug.h"
30 #include "mio_priv.h"
31 
32 #define DEVPATH_PREFIX	"/dev/rmidi"
33 #define DEVPATH_MAX 	(1 +		\
34 	sizeof(DEVPATH_PREFIX) - 1 +	\
35 	sizeof(int) * 3)
36 
37 struct mio_rmidi_hdl {
38 	struct mio_hdl mio;
39 	int fd;
40 };
41 
42 static void mio_rmidi_close(struct mio_hdl *);
43 static size_t mio_rmidi_read(struct mio_hdl *, void *, size_t);
44 static size_t mio_rmidi_write(struct mio_hdl *, const void *, size_t);
45 static int mio_rmidi_nfds(struct mio_hdl *);
46 static int mio_rmidi_pollfd(struct mio_hdl *, struct pollfd *, int);
47 static int mio_rmidi_revents(struct mio_hdl *, struct pollfd *);
48 
49 static struct mio_ops mio_rmidi_ops = {
50 	mio_rmidi_close,
51 	mio_rmidi_write,
52 	mio_rmidi_read,
53 	mio_rmidi_nfds,
54 	mio_rmidi_pollfd,
55 	mio_rmidi_revents
56 };
57 
58 int
mio_rmidi_getfd(const char * str,unsigned int mode,int nbio)59 mio_rmidi_getfd(const char *str, unsigned int mode, int nbio)
60 {
61 	const char *p;
62 	char path[DEVPATH_MAX];
63 	unsigned int devnum;
64 	int fd, flags;
65 
66 #ifdef DEBUG
67 	_sndio_debug_init();
68 #endif
69 	p = _sndio_parsetype(str, "rmidi");
70 	if (p == NULL) {
71 		DPRINTF("mio_rmidi_getfd: %s: \"rmidi\" expected\n", str);
72 		return -1;
73 	}
74 	switch (*p) {
75 	case '/':
76 		p++;
77 		break;
78 	default:
79 		DPRINTF("mio_rmidi_getfd: %s: '/' expected\n", str);
80 		return -1;
81 	}
82 	p = _sndio_parsenum(p, &devnum, 255);
83 	if (p == NULL) {
84 		DPRINTF("mio_rmidi_getfd: %s: number expected after '/'\n", str);
85 		return -1;
86 	}
87 	if (*p != '\0') {
88 		DPRINTF("mio_rmidi_getfd: junk at end of string: %s\n", p);
89 		return -1;
90 	}
91 	snprintf(path, sizeof(path), DEVPATH_PREFIX "%u", devnum);
92 	if (mode == (MIO_IN | MIO_OUT))
93 		flags = O_RDWR;
94 	else
95 		flags = (mode & MIO_OUT) ? O_WRONLY : O_RDONLY;
96 	while ((fd = open(path, flags | O_NONBLOCK | O_CLOEXEC)) == -1) {
97 		if (errno == EINTR)
98 			continue;
99 		DPERROR(path);
100 		return -1;
101 	}
102 	return fd;
103 }
104 
105 struct mio_hdl *
mio_rmidi_fdopen(int fd,unsigned int mode,int nbio)106 mio_rmidi_fdopen(int fd, unsigned int mode, int nbio)
107 {
108 	struct mio_rmidi_hdl *hdl;
109 
110 #ifdef DEBUG
111 	_sndio_debug_init();
112 #endif
113 	hdl = malloc(sizeof(struct mio_rmidi_hdl));
114 	if (hdl == NULL)
115 		return NULL;
116 	_mio_create(&hdl->mio, &mio_rmidi_ops, mode, nbio);
117 	hdl->fd = fd;
118 	return (struct mio_hdl *)hdl;
119 }
120 
121 struct mio_hdl *
_mio_rmidi_open(const char * str,unsigned int mode,int nbio)122 _mio_rmidi_open(const char *str, unsigned int mode, int nbio)
123 {
124 	struct mio_hdl *hdl;
125 	int fd;
126 
127 	fd = mio_rmidi_getfd(str, mode, nbio);
128 	if (fd == -1)
129 		return NULL;
130 	hdl = mio_rmidi_fdopen(fd, mode, nbio);
131 	if (hdl != NULL)
132 		return hdl;
133 	while (close(fd) == -1 && errno == EINTR)
134 		; /* retry */
135 	return NULL;
136 }
137 
138 static void
mio_rmidi_close(struct mio_hdl * sh)139 mio_rmidi_close(struct mio_hdl *sh)
140 {
141 	struct mio_rmidi_hdl *hdl = (struct mio_rmidi_hdl *)sh;
142 	int rc;
143 
144 	do {
145 		rc = close(hdl->fd);
146 	} while (rc == -1 && errno == EINTR);
147 	free(hdl);
148 }
149 
150 static size_t
mio_rmidi_read(struct mio_hdl * sh,void * buf,size_t len)151 mio_rmidi_read(struct mio_hdl *sh, void *buf, size_t len)
152 {
153 	struct mio_rmidi_hdl *hdl = (struct mio_rmidi_hdl *)sh;
154 	ssize_t n;
155 
156 	while ((n = read(hdl->fd, buf, len)) == -1) {
157 		if (errno == EINTR)
158 			continue;
159 		if (errno != EAGAIN) {
160 			DPERROR("mio_rmidi_read: read");
161 			hdl->mio.eof = 1;
162 		}
163 		return 0;
164 	}
165 	if (n == 0) {
166 		DPRINTF("mio_rmidi_read: eof\n");
167 		hdl->mio.eof = 1;
168 		return 0;
169 	}
170 	return n;
171 }
172 
173 static size_t
mio_rmidi_write(struct mio_hdl * sh,const void * buf,size_t len)174 mio_rmidi_write(struct mio_hdl *sh, const void *buf, size_t len)
175 {
176 	struct mio_rmidi_hdl *hdl = (struct mio_rmidi_hdl *)sh;
177 	ssize_t n;
178 
179 	while ((n = write(hdl->fd, buf, len)) == -1) {
180 		if (errno == EINTR)
181 			continue;
182 		if (errno != EAGAIN) {
183 			DPERROR("mio_rmidi_write: write");
184 			hdl->mio.eof = 1;
185 		}
186 		return 0;
187 	}
188 	return n;
189 }
190 
191 static int
mio_rmidi_nfds(struct mio_hdl * sh)192 mio_rmidi_nfds(struct mio_hdl *sh)
193 {
194 	return 1;
195 }
196 
197 static int
mio_rmidi_pollfd(struct mio_hdl * sh,struct pollfd * pfd,int events)198 mio_rmidi_pollfd(struct mio_hdl *sh, struct pollfd *pfd, int events)
199 {
200 	struct mio_rmidi_hdl *hdl = (struct mio_rmidi_hdl *)sh;
201 
202 	pfd->fd = hdl->fd;
203 	pfd->events = events;
204 	return 1;
205 }
206 
207 static int
mio_rmidi_revents(struct mio_hdl * sh,struct pollfd * pfd)208 mio_rmidi_revents(struct mio_hdl *sh, struct pollfd *pfd)
209 {
210 	return pfd->revents;
211 }
212