Line data Source code
1 : /* $OpenBSD: midi.c,v 1.43 2017/07/19 22:23:54 kettenis Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2003, 2004 Alexandre Ratchov
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : #include <sys/param.h>
20 : #include <sys/fcntl.h>
21 : #include <sys/systm.h>
22 : #include <sys/ioctl.h>
23 : #include <sys/conf.h>
24 : #include <sys/poll.h>
25 : #include <sys/kernel.h>
26 : #include <sys/timeout.h>
27 : #include <sys/vnode.h>
28 : #include <sys/signalvar.h>
29 : #include <sys/device.h>
30 :
31 : #include <dev/midi_if.h>
32 : #include <dev/audio_if.h>
33 : #include <dev/midivar.h>
34 :
35 :
36 : int midiopen(dev_t, int, int, struct proc *);
37 : int midiclose(dev_t, int, int, struct proc *);
38 : int midiread(dev_t, struct uio *, int);
39 : int midiwrite(dev_t, struct uio *, int);
40 : int midipoll(dev_t, int, struct proc *);
41 : int midikqfilter(dev_t, struct knote *);
42 : int midiioctl(dev_t, u_long, caddr_t, int, struct proc *);
43 : int midiprobe(struct device *, void *, void *);
44 : void midiattach(struct device *, struct device *, void *);
45 : int mididetach(struct device *, int);
46 : int midiprint(void *, const char *);
47 :
48 : void midi_iintr(void *, int);
49 : void midi_ointr(void *);
50 : void midi_timeout(void *);
51 : void midi_out_start(struct midi_softc *);
52 : void midi_out_stop(struct midi_softc *);
53 : void midi_out_do(struct midi_softc *);
54 : void midi_attach(struct midi_softc *, struct device *);
55 :
56 :
57 : struct cfattach midi_ca = {
58 : sizeof(struct midi_softc), midiprobe, midiattach, mididetach
59 : };
60 :
61 : struct cfdriver midi_cd = {
62 : NULL, "midi", DV_DULL
63 : };
64 :
65 :
66 : void filt_midiwdetach(struct knote *);
67 : int filt_midiwrite(struct knote *, long);
68 :
69 : struct filterops midiwrite_filtops = {
70 : 1, NULL, filt_midiwdetach, filt_midiwrite
71 : };
72 :
73 : void filt_midirdetach(struct knote *);
74 : int filt_midiread(struct knote *, long);
75 :
76 : struct filterops midiread_filtops = {
77 : 1, NULL, filt_midirdetach, filt_midiread
78 : };
79 :
80 : void
81 0 : midi_iintr(void *addr, int data)
82 : {
83 0 : struct midi_softc *sc = (struct midi_softc *)addr;
84 0 : struct midi_buffer *mb = &sc->inbuf;
85 :
86 0 : MUTEX_ASSERT_LOCKED(&audio_lock);
87 0 : if (!(sc->dev.dv_flags & DVF_ACTIVE) || !(sc->flags & FREAD))
88 0 : return;
89 :
90 0 : if (MIDIBUF_ISFULL(mb))
91 0 : return; /* discard data */
92 :
93 0 : MIDIBUF_WRITE(mb, data);
94 0 : if (mb->used == 1) {
95 0 : if (sc->rchan) {
96 0 : sc->rchan = 0;
97 0 : wakeup(&sc->rchan);
98 0 : }
99 0 : selwakeup(&sc->rsel);
100 0 : }
101 0 : }
102 :
103 : int
104 0 : midiread(dev_t dev, struct uio *uio, int ioflag)
105 : {
106 : struct midi_softc *sc;
107 : struct midi_buffer *mb;
108 : size_t count;
109 : int error;
110 :
111 0 : sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
112 0 : if (sc == NULL)
113 0 : return ENXIO;
114 0 : if (!(sc->flags & FREAD)) {
115 : error = ENXIO;
116 0 : goto done;
117 : }
118 0 : mb = &sc->inbuf;
119 :
120 : /* if there is no data then sleep (unless IO_NDELAY flag is set) */
121 : error = 0;
122 0 : mtx_enter(&audio_lock);
123 0 : while (MIDIBUF_ISEMPTY(mb)) {
124 0 : if (ioflag & IO_NDELAY) {
125 : error = EWOULDBLOCK;
126 0 : goto done_mtx;
127 : }
128 0 : sc->rchan = 1;
129 0 : error = msleep(&sc->rchan, &audio_lock, PWAIT | PCATCH, "mid_rd", 0);
130 0 : if (!(sc->dev.dv_flags & DVF_ACTIVE))
131 : error = EIO;
132 0 : if (error)
133 : goto done_mtx;
134 : }
135 :
136 : /* at this stage, there is at least 1 byte */
137 :
138 0 : while (uio->uio_resid > 0 && mb->used > 0) {
139 0 : count = MIDIBUF_SIZE - mb->start;
140 0 : if (count > mb->used)
141 0 : count = mb->used;
142 0 : if (count > uio->uio_resid)
143 0 : count = uio->uio_resid;
144 0 : mtx_leave(&audio_lock);
145 0 : error = uiomove(mb->data + mb->start, count, uio);
146 0 : if (error)
147 : goto done;
148 0 : mtx_enter(&audio_lock);
149 0 : MIDIBUF_REMOVE(mb, count);
150 : }
151 :
152 : done_mtx:
153 0 : mtx_leave(&audio_lock);
154 : done:
155 0 : device_unref(&sc->dev);
156 0 : return error;
157 0 : }
158 :
159 : void
160 0 : midi_ointr(void *addr)
161 : {
162 0 : struct midi_softc *sc = (struct midi_softc *)addr;
163 : struct midi_buffer *mb;
164 :
165 0 : MUTEX_ASSERT_LOCKED(&audio_lock);
166 0 : if (!(sc->dev.dv_flags & DVF_ACTIVE) || !(sc->flags & FWRITE))
167 0 : return;
168 :
169 0 : mb = &sc->outbuf;
170 0 : if (mb->used > 0) {
171 : #ifdef MIDI_DEBUG
172 : if (!sc->isbusy) {
173 : printf("midi_ointr: output must be busy\n");
174 : }
175 : #endif
176 0 : midi_out_do(sc);
177 0 : } else if (sc->isbusy)
178 0 : midi_out_stop(sc);
179 0 : }
180 :
181 : void
182 0 : midi_timeout(void *addr)
183 : {
184 0 : mtx_enter(&audio_lock);
185 0 : midi_ointr(addr);
186 0 : mtx_leave(&audio_lock);
187 0 : }
188 :
189 : void
190 0 : midi_out_start(struct midi_softc *sc)
191 : {
192 0 : if (!sc->isbusy) {
193 0 : sc->isbusy = 1;
194 0 : midi_out_do(sc);
195 0 : }
196 0 : }
197 :
198 : void
199 0 : midi_out_stop(struct midi_softc *sc)
200 : {
201 0 : sc->isbusy = 0;
202 0 : if (sc->wchan) {
203 0 : sc->wchan = 0;
204 0 : wakeup(&sc->wchan);
205 0 : }
206 0 : selwakeup(&sc->wsel);
207 0 : }
208 :
209 : void
210 0 : midi_out_do(struct midi_softc *sc)
211 : {
212 0 : struct midi_buffer *mb = &sc->outbuf;
213 :
214 0 : while (mb->used > 0) {
215 0 : if (!sc->hw_if->output(sc->hw_hdl, mb->data[mb->start]))
216 : break;
217 0 : MIDIBUF_REMOVE(mb, 1);
218 0 : if (MIDIBUF_ISEMPTY(mb)) {
219 0 : if (sc->hw_if->flush != NULL)
220 0 : sc->hw_if->flush(sc->hw_hdl);
221 0 : midi_out_stop(sc);
222 0 : return;
223 : }
224 : }
225 :
226 0 : if (!(sc->props & MIDI_PROP_OUT_INTR)) {
227 0 : if (MIDIBUF_ISEMPTY(mb))
228 0 : midi_out_stop(sc);
229 : else
230 0 : timeout_add(&sc->timeo, 1);
231 : }
232 0 : }
233 :
234 : int
235 0 : midiwrite(dev_t dev, struct uio *uio, int ioflag)
236 : {
237 : struct midi_softc *sc;
238 : struct midi_buffer *mb;
239 : size_t count;
240 : int error;
241 :
242 0 : sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
243 0 : if (sc == NULL)
244 0 : return ENXIO;
245 0 : if (!(sc->flags & FWRITE)) {
246 : error = ENXIO;
247 0 : goto done;
248 : }
249 0 : mb = &sc->outbuf;
250 :
251 : /*
252 : * If IO_NDELAY flag is set then check if there is enough room
253 : * in the buffer to store at least one byte. If not then dont
254 : * start the write process.
255 : */
256 : error = 0;
257 0 : mtx_enter(&audio_lock);
258 0 : if ((ioflag & IO_NDELAY) && MIDIBUF_ISFULL(mb) && (uio->uio_resid > 0)) {
259 : error = EWOULDBLOCK;
260 0 : goto done_mtx;
261 : }
262 :
263 0 : while (uio->uio_resid > 0) {
264 0 : while (MIDIBUF_ISFULL(mb)) {
265 0 : if (ioflag & IO_NDELAY) {
266 : /*
267 : * At this stage at least one byte is already
268 : * moved so we do not return EWOULDBLOCK
269 : */
270 : goto done_mtx;
271 : }
272 0 : sc->wchan = 1;
273 0 : error = msleep(&sc->wchan, &audio_lock,
274 : PWAIT | PCATCH, "mid_wr", 0);
275 0 : if (!(sc->dev.dv_flags & DVF_ACTIVE))
276 : error = EIO;
277 0 : if (error)
278 : goto done_mtx;
279 : }
280 :
281 0 : count = MIDIBUF_SIZE - MIDIBUF_END(mb);
282 0 : if (count > MIDIBUF_AVAIL(mb))
283 0 : count = MIDIBUF_AVAIL(mb);
284 0 : if (count > uio->uio_resid)
285 0 : count = uio->uio_resid;
286 0 : mtx_leave(&audio_lock);
287 0 : error = uiomove(mb->data + MIDIBUF_END(mb), count, uio);
288 0 : if (error)
289 : goto done;
290 0 : mtx_enter(&audio_lock);
291 0 : mb->used += count;
292 0 : midi_out_start(sc);
293 : }
294 :
295 : done_mtx:
296 0 : mtx_leave(&audio_lock);
297 : done:
298 0 : device_unref(&sc->dev);
299 0 : return error;
300 0 : }
301 :
302 : int
303 0 : midipoll(dev_t dev, int events, struct proc *p)
304 : {
305 : struct midi_softc *sc;
306 : int revents;
307 :
308 0 : sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
309 0 : if (sc == NULL)
310 0 : return POLLERR;
311 : revents = 0;
312 0 : mtx_enter(&audio_lock);
313 0 : if (events & (POLLIN | POLLRDNORM)) {
314 0 : if (!MIDIBUF_ISEMPTY(&sc->inbuf))
315 0 : revents |= events & (POLLIN | POLLRDNORM);
316 : }
317 0 : if (events & (POLLOUT | POLLWRNORM)) {
318 0 : if (!MIDIBUF_ISFULL(&sc->outbuf))
319 0 : revents |= events & (POLLOUT | POLLWRNORM);
320 : }
321 0 : if (revents == 0) {
322 0 : if (events & (POLLIN | POLLRDNORM))
323 0 : selrecord(p, &sc->rsel);
324 0 : if (events & (POLLOUT | POLLWRNORM))
325 0 : selrecord(p, &sc->wsel);
326 : }
327 0 : mtx_leave(&audio_lock);
328 0 : device_unref(&sc->dev);
329 0 : return (revents);
330 0 : }
331 :
332 : int
333 0 : midikqfilter(dev_t dev, struct knote *kn)
334 : {
335 : struct midi_softc *sc;
336 : struct klist *klist;
337 : int error;
338 :
339 0 : sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
340 0 : if (sc == NULL)
341 0 : return ENXIO;
342 : error = 0;
343 0 : switch (kn->kn_filter) {
344 : case EVFILT_READ:
345 0 : klist = &sc->rsel.si_note;
346 0 : kn->kn_fop = &midiread_filtops;
347 0 : break;
348 : case EVFILT_WRITE:
349 0 : klist = &sc->wsel.si_note;
350 0 : kn->kn_fop = &midiwrite_filtops;
351 0 : break;
352 : default:
353 : error = EINVAL;
354 0 : goto done;
355 : }
356 0 : kn->kn_hook = (void *)sc;
357 :
358 0 : mtx_enter(&audio_lock);
359 0 : SLIST_INSERT_HEAD(klist, kn, kn_selnext);
360 0 : mtx_leave(&audio_lock);
361 : done:
362 0 : device_unref(&sc->dev);
363 0 : return error;
364 0 : }
365 :
366 : void
367 0 : filt_midirdetach(struct knote *kn)
368 : {
369 0 : struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
370 :
371 0 : mtx_enter(&audio_lock);
372 0 : SLIST_REMOVE(&sc->rsel.si_note, kn, knote, kn_selnext);
373 0 : mtx_leave(&audio_lock);
374 0 : }
375 :
376 : int
377 0 : filt_midiread(struct knote *kn, long hint)
378 : {
379 0 : struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
380 : int retval;
381 :
382 0 : mtx_enter(&audio_lock);
383 0 : retval = !MIDIBUF_ISEMPTY(&sc->inbuf);
384 0 : mtx_leave(&audio_lock);
385 :
386 0 : return (retval);
387 : }
388 :
389 : void
390 0 : filt_midiwdetach(struct knote *kn)
391 : {
392 0 : struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
393 :
394 0 : mtx_enter(&audio_lock);
395 0 : SLIST_REMOVE(&sc->wsel.si_note, kn, knote, kn_selnext);
396 0 : mtx_leave(&audio_lock);
397 0 : }
398 :
399 : int
400 0 : filt_midiwrite(struct knote *kn, long hint)
401 : {
402 0 : struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
403 : int retval;
404 :
405 0 : mtx_enter(&audio_lock);
406 0 : retval = !MIDIBUF_ISFULL(&sc->outbuf);
407 0 : mtx_leave(&audio_lock);
408 :
409 0 : return (retval);
410 : }
411 :
412 : int
413 0 : midiioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
414 : {
415 : struct midi_softc *sc;
416 : int error;
417 :
418 0 : sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
419 0 : if (sc == NULL)
420 0 : return ENXIO;
421 : error = 0;
422 0 : switch(cmd) {
423 : case FIONBIO:
424 : /* All handled in the upper FS layer */
425 : break;
426 : default:
427 : error = ENOTTY;
428 : }
429 0 : device_unref(&sc->dev);
430 0 : return error;
431 0 : }
432 :
433 : int
434 0 : midiopen(dev_t dev, int flags, int mode, struct proc *p)
435 : {
436 : struct midi_softc *sc;
437 : int error;
438 :
439 0 : sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
440 0 : if (sc == NULL)
441 0 : return ENXIO;
442 : error = 0;
443 0 : if (sc->flags) {
444 : error = EBUSY;
445 0 : goto done;
446 : }
447 0 : MIDIBUF_INIT(&sc->inbuf);
448 0 : MIDIBUF_INIT(&sc->outbuf);
449 0 : sc->isbusy = 0;
450 0 : sc->rchan = sc->wchan = 0;
451 0 : sc->flags = flags;
452 0 : error = sc->hw_if->open(sc->hw_hdl, flags, midi_iintr, midi_ointr, sc);
453 0 : if (error)
454 0 : sc->flags = 0;
455 : done:
456 0 : device_unref(&sc->dev);
457 0 : return error;
458 0 : }
459 :
460 : int
461 0 : midiclose(dev_t dev, int fflag, int devtype, struct proc *p)
462 : {
463 : struct midi_softc *sc;
464 : struct midi_buffer *mb;
465 : int error;
466 :
467 0 : sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
468 0 : if (sc == NULL)
469 0 : return ENXIO;
470 :
471 : /* start draining output buffer */
472 : error = 0;
473 0 : mb = &sc->outbuf;
474 0 : mtx_enter(&audio_lock);
475 0 : if (!MIDIBUF_ISEMPTY(mb))
476 0 : midi_out_start(sc);
477 0 : while (sc->isbusy) {
478 0 : sc->wchan = 1;
479 0 : error = msleep(&sc->wchan, &audio_lock,
480 0 : PWAIT, "mid_dr", 5 * hz);
481 0 : if (!(sc->dev.dv_flags & DVF_ACTIVE))
482 : error = EIO;
483 0 : if (error)
484 : break;
485 : }
486 0 : mtx_leave(&audio_lock);
487 :
488 : /*
489 : * some hw_if->close() reset immediately the midi uart
490 : * which flushes the internal buffer of the uart device,
491 : * so we may lose some (important) data. To avoid this,
492 : * sleep 20ms (around 64 bytes) to give the time to the
493 : * uart to drain its internal buffers.
494 : */
495 0 : tsleep(&sc->wchan, PWAIT, "mid_cl", hz * MIDI_MAXWRITE / MIDI_RATE);
496 0 : sc->hw_if->close(sc->hw_hdl);
497 0 : sc->flags = 0;
498 0 : device_unref(&sc->dev);
499 0 : return 0;
500 0 : }
501 :
502 : int
503 0 : midiprobe(struct device *parent, void *match, void *aux)
504 : {
505 0 : struct audio_attach_args *sa = aux;
506 :
507 0 : return (sa != NULL && (sa->type == AUDIODEV_TYPE_MIDI) ? 1 : 0);
508 : }
509 :
510 : void
511 0 : midiattach(struct device *parent, struct device *self, void *aux)
512 : {
513 0 : struct midi_info mi;
514 0 : struct midi_softc *sc = (struct midi_softc *)self;
515 0 : struct audio_attach_args *sa = (struct audio_attach_args *)aux;
516 0 : struct midi_hw_if *hwif = sa->hwif;
517 0 : void *hdl = sa->hdl;
518 :
519 : #ifdef DIAGNOSTIC
520 0 : if (hwif == 0 ||
521 0 : hwif->open == 0 ||
522 0 : hwif->close == 0 ||
523 0 : hwif->output == 0 ||
524 0 : hwif->getinfo == 0) {
525 0 : printf("midi: missing method\n");
526 0 : return;
527 : }
528 : #endif
529 0 : sc->hw_if = hwif;
530 0 : sc->hw_hdl = hdl;
531 0 : sc->hw_if->getinfo(sc->hw_hdl, &mi);
532 0 : sc->props = mi.props;
533 0 : sc->flags = 0;
534 0 : timeout_set(&sc->timeo, midi_timeout, sc);
535 0 : printf(": <%s>\n", mi.name);
536 0 : }
537 :
538 : int
539 0 : mididetach(struct device *self, int flags)
540 : {
541 0 : struct midi_softc *sc = (struct midi_softc *)self;
542 : int maj, mn;
543 :
544 : /* locate the major number */
545 0 : for (maj = 0; maj < nchrdev; maj++) {
546 0 : if (cdevsw[maj].d_open == midiopen) {
547 : /* Nuke the vnodes for any open instances (calls close). */
548 0 : mn = self->dv_unit;
549 0 : vdevgone(maj, mn, mn, VCHR);
550 0 : }
551 : }
552 :
553 : /*
554 : * The close() method did nothing (device_lookup() returns
555 : * NULL), so quickly halt transfers (normally parent is already
556 : * gone, and code below is no-op), and wake-up user-land blocked
557 : * in read/write/ioctl, which return EIO.
558 : */
559 0 : if (sc->flags) {
560 0 : if (sc->flags & FREAD) {
561 0 : sc->rchan = 0;
562 0 : wakeup(&sc->rchan);
563 0 : selwakeup(&sc->rsel);
564 0 : }
565 0 : if (sc->flags & FWRITE) {
566 0 : sc->wchan = 0;
567 0 : wakeup(&sc->wchan);
568 0 : selwakeup(&sc->wsel);
569 0 : }
570 0 : sc->hw_if->close(sc->hw_hdl);
571 0 : sc->flags = 0;
572 0 : }
573 0 : return 0;
574 : }
575 :
576 : int
577 0 : midiprint(void *aux, const char *pnp)
578 : {
579 0 : if (pnp)
580 0 : printf("midi at %s", pnp);
581 0 : return (UNCONF);
582 : }
583 :
584 : struct device *
585 0 : midi_attach_mi(struct midi_hw_if *hwif, void *hdl, struct device *dev)
586 : {
587 0 : struct audio_attach_args arg;
588 :
589 0 : arg.type = AUDIODEV_TYPE_MIDI;
590 0 : arg.hwif = hwif;
591 0 : arg.hdl = hdl;
592 0 : return config_found(dev, &arg, midiprint);
593 0 : }
|