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