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 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 * 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 * 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 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 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 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 192 mio_rmidi_nfds(struct mio_hdl *sh) 193 { 194 return 1; 195 } 196 197 static int 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 208 mio_rmidi_revents(struct mio_hdl *sh, struct pollfd *pfd) 209 { 210 return pfd->revents; 211 } 212