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