1 /* $OpenBSD$ */
2 /*
3 * Copyright (c) 2008-2011 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 #ifdef USE_ALSA
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 #include <alsa/asoundlib.h>
30
31 #include "debug.h"
32 #include "mio_priv.h"
33
34 #define DEVNAME_PREFIX "hw:"
35
36 #ifdef DEBUG
37 static snd_output_t *output = NULL;
38 #define DALSA(str, err) fprintf(stderr, "%s: %s\n", str, snd_strerror(err))
39 #else
40 #define DALSA(str, err) do {} while (0)
41 #endif
42
43 struct mio_alsa_hdl {
44 struct mio_hdl mio;
45 char *devname;
46 snd_rawmidi_t *in, *out;
47 int infds, onfds, nfds, events;
48 };
49
50 static void mio_alsa_close(struct mio_hdl *);
51 static size_t mio_alsa_read(struct mio_hdl *, void *, size_t);
52 static size_t mio_alsa_write(struct mio_hdl *, const void *, size_t);
53 static int mio_alsa_nfds(struct mio_hdl *);
54 static int mio_alsa_pollfd(struct mio_hdl *, struct pollfd *, int);
55 static int mio_alsa_revents(struct mio_hdl *, struct pollfd *);
56
57 static struct mio_ops mio_alsa_ops = {
58 mio_alsa_close,
59 mio_alsa_write,
60 mio_alsa_read,
61 mio_alsa_nfds,
62 mio_alsa_pollfd,
63 mio_alsa_revents
64 };
65
66 struct mio_hdl *
_mio_alsa_open(const char * _str,unsigned int mode,int nbio)67 _mio_alsa_open(const char *_str, unsigned int mode, int nbio)
68 {
69 const char *p;
70 struct mio_alsa_hdl *hdl;
71 size_t len;
72 int rc;
73
74 p = _sndio_parsetype(_str, "rmidi");
75 if (p == NULL) {
76 DPRINTF("_mio_alsa_open: %s: \"rsnd\" expected\n", _str);
77 return NULL;
78 }
79 switch (*p) {
80 case '/':
81 p++;
82 break;
83 default:
84 DPRINTF("_mio_alsa_open: %s: '/' expected\n", _str);
85 return NULL;
86 }
87 hdl = malloc(sizeof(struct mio_alsa_hdl));
88 if (hdl == NULL)
89 return NULL;
90 _mio_create(&hdl->mio, &mio_alsa_ops, mode, nbio);
91 #ifdef DEBUG
92 rc = snd_output_stdio_attach(&output, stderr, 0);
93 if (rc < 0)
94 DALSA("couldn't attach to stderr", rc);
95 #endif
96 len = strlen(p);
97 hdl->devname = malloc(len + sizeof(DEVNAME_PREFIX));
98 if (hdl->devname == NULL) {
99 free(hdl);
100 return NULL;
101 }
102 memcpy(hdl->devname, DEVNAME_PREFIX, sizeof(DEVNAME_PREFIX) - 1);
103 memcpy(hdl->devname + sizeof(DEVNAME_PREFIX) - 1, p, len + 1);
104 hdl->in = hdl->out = NULL;
105 rc = snd_rawmidi_open(&hdl->in, &hdl->out,
106 hdl->devname, SND_RAWMIDI_NONBLOCK);
107 if (rc) {
108 DALSA("could't open port", rc);
109 free(hdl->devname);
110 free(hdl);
111 return NULL;
112 }
113 hdl->nfds = 0;
114 if (hdl->in)
115 hdl->nfds += snd_rawmidi_poll_descriptors_count(hdl->in);
116 if (hdl->out)
117 hdl->nfds += snd_rawmidi_poll_descriptors_count(hdl->out);
118 return (struct mio_hdl *)hdl;
119 }
120
121 static void
mio_alsa_close(struct mio_hdl * sh)122 mio_alsa_close(struct mio_hdl *sh)
123 {
124 struct mio_alsa_hdl *hdl = (struct mio_alsa_hdl *)sh;
125
126 if (hdl->in)
127 snd_rawmidi_close(hdl->in);
128 if (hdl->out) {
129 snd_rawmidi_drain(hdl->out);
130 snd_rawmidi_close(hdl->out);
131 }
132 free(hdl->devname);
133 free(hdl);
134 }
135
136 static size_t
mio_alsa_read(struct mio_hdl * sh,void * buf,size_t len)137 mio_alsa_read(struct mio_hdl *sh, void *buf, size_t len)
138 {
139 struct mio_alsa_hdl *hdl = (struct mio_alsa_hdl *)sh;
140 ssize_t n;
141
142 for (;;) {
143 n = snd_rawmidi_read(hdl->in, buf, len);
144 if (n > 0)
145 return n;
146 if (n == -EINTR)
147 continue;
148 if (n == -EAGAIN)
149 return 0;
150 if (n == 0)
151 DPRINTF("mio_alsa_read: eof\n");
152 if (n < 0)
153 DALSA("mio_alsa_read", n);
154 hdl->mio.eof = 1;
155 return 0;
156 }
157 }
158
159 static size_t
mio_alsa_write(struct mio_hdl * sh,const void * buf,size_t len)160 mio_alsa_write(struct mio_hdl *sh, const void *buf, size_t len)
161 {
162 struct mio_alsa_hdl *hdl = (struct mio_alsa_hdl *)sh;
163 ssize_t n;
164
165 for (;;) {
166 n = snd_rawmidi_write(hdl->out, buf, len);
167 if (n > 0)
168 return n;
169 if (n == -EINTR)
170 continue;
171 if (n == -EAGAIN)
172 return 0;
173 if (n == 0)
174 DPRINTF("mio_alsa_write: eof\n");
175 if (n < 0)
176 DALSA("mio_alsa_write", n);
177 hdl->mio.eof = 1;
178 return 0;
179 }
180 }
181
182 static int
mio_alsa_nfds(struct mio_hdl * sh)183 mio_alsa_nfds(struct mio_hdl *sh)
184 {
185 struct mio_alsa_hdl *hdl = (struct mio_alsa_hdl *)sh;
186
187 return hdl->nfds;
188 }
189
190 static int
mio_alsa_pollfd(struct mio_hdl * sh,struct pollfd * pfd,int events)191 mio_alsa_pollfd(struct mio_hdl *sh, struct pollfd *pfd, int events)
192 {
193 struct mio_alsa_hdl *hdl = (struct mio_alsa_hdl *)sh;
194
195 if (!hdl->in)
196 events &= ~POLLIN;
197 if (!hdl->out)
198 events &= ~POLLOUT;
199 hdl->events = events;
200 if (events & POLLIN) {
201 hdl->infds = snd_rawmidi_poll_descriptors(hdl->in,
202 pfd, hdl->nfds);
203 } else
204 hdl->infds = 0;
205 if (events & POLLOUT) {
206 hdl->onfds += snd_rawmidi_poll_descriptors(hdl->out,
207 pfd + hdl->infds, hdl->nfds - hdl->infds);
208 } else
209 hdl->onfds = 0;
210 return hdl->infds + hdl->onfds;
211 }
212
213 static int
mio_alsa_revents(struct mio_hdl * sh,struct pollfd * pfd)214 mio_alsa_revents(struct mio_hdl *sh, struct pollfd *pfd)
215 {
216 struct mio_alsa_hdl *hdl = (struct mio_alsa_hdl *)sh;
217 unsigned short r;
218 int revents = 0, rc;
219
220 if (hdl->events & POLLIN) {
221 rc = snd_rawmidi_poll_descriptors_revents(hdl->in,
222 pfd, hdl->infds, &r);
223 if (rc < 0) {
224 DALSA("snd_rawmidi_poll_descriptors_revents", rc);
225 hdl->mio.eof = 1;
226 return POLLHUP;
227 }
228 revents |= r & (POLLIN | POLLHUP);
229 }
230 if (hdl->events & POLLOUT) {
231 rc = snd_rawmidi_poll_descriptors_revents(hdl->in,
232 pfd + hdl->infds, hdl->onfds, &r);
233 if (rc < 0) {
234 DALSA("snd_rawmidi_poll_descriptors_revents", rc);
235 hdl->mio.eof = 1;
236 return POLLHUP;
237 }
238 revents |= r & (POLLOUT | POLLHUP);
239 }
240 return revents;
241 }
242 #endif /* defined USE_ALSA */
243