xref: /openbsd-src/sys/dev/midi.c (revision be691f3bb6417f04a68938fadbcaee2d5795e764)
1 /*	$OpenBSD: midi.c,v 1.52 2021/10/30 12:48:11 ratchov 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 #define IPL_SOFTMIDI		IPL_SOFTNET
36 #define DEVNAME(sc)		((sc)->dev.dv_xname)
37 
38 int	midiopen(dev_t, int, int, struct proc *);
39 int	midiclose(dev_t, int, int, struct proc *);
40 int	midiread(dev_t, struct uio *, int);
41 int	midiwrite(dev_t, struct uio *, int);
42 int	midipoll(dev_t, int, struct proc *);
43 int	midikqfilter(dev_t, struct knote *);
44 int	midiioctl(dev_t, u_long, caddr_t, int, struct proc *);
45 int	midiprobe(struct device *, void *, void *);
46 void	midiattach(struct device *, struct device *, void *);
47 int	mididetach(struct device *, int);
48 int	midiprint(void *, const char *);
49 
50 void	midi_iintr(void *, int);
51 void 	midi_ointr(void *);
52 void	midi_timeout(void *);
53 void	midi_out_start(struct midi_softc *);
54 void	midi_out_stop(struct midi_softc *);
55 void	midi_out_do(struct midi_softc *);
56 void	midi_attach(struct midi_softc *, struct device *);
57 
58 
59 struct cfattach midi_ca = {
60 	sizeof(struct midi_softc), midiprobe, midiattach, mididetach
61 };
62 
63 struct cfdriver midi_cd = {
64 	NULL, "midi", DV_DULL
65 };
66 
67 
68 void filt_midiwdetach(struct knote *);
69 int filt_midiwrite(struct knote *, long);
70 
71 const struct filterops midiwrite_filtops = {
72 	.f_flags	= FILTEROP_ISFD,
73 	.f_attach	= NULL,
74 	.f_detach	= filt_midiwdetach,
75 	.f_event	= filt_midiwrite,
76 };
77 
78 void filt_midirdetach(struct knote *);
79 int filt_midiread(struct knote *, long);
80 
81 const struct filterops midiread_filtops = {
82 	.f_flags	= FILTEROP_ISFD,
83 	.f_attach	= NULL,
84 	.f_detach	= filt_midirdetach,
85 	.f_event	= filt_midiread,
86 };
87 
88 void
89 midi_buf_wakeup(void *addr)
90 {
91 	struct midi_buffer *buf = addr;
92 
93 	if (buf->blocking) {
94 		wakeup(&buf->blocking);
95 		buf->blocking = 0;
96 	}
97 	/*
98 	 * As long as selwakeup() grabs the KERNEL_LOCK() make sure it is
99 	 * already held here to avoid lock ordering problems with `audio_lock'
100 	 */
101 	KERNEL_ASSERT_LOCKED();
102 	mtx_enter(&audio_lock);
103 	selwakeup(&buf->sel);
104 	mtx_leave(&audio_lock);
105 }
106 
107 void
108 midi_iintr(void *addr, int data)
109 {
110 	struct midi_softc  *sc = (struct midi_softc *)addr;
111 	struct midi_buffer *mb = &sc->inbuf;
112 
113 	MUTEX_ASSERT_LOCKED(&audio_lock);
114 	if (!(sc->dev.dv_flags & DVF_ACTIVE) || !(sc->flags & FREAD))
115 		return;
116 
117 	if (MIDIBUF_ISFULL(mb))
118 		return; /* discard data */
119 
120 	MIDIBUF_WRITE(mb, data);
121 
122 	/*
123 	 * As long as selwakeup() needs to be protected by the
124 	 * KERNEL_LOCK() we have to delay the wakeup to another
125 	 * context to keep the interrupt context KERNEL_LOCK()
126 	 * free.
127 	 */
128 	softintr_schedule(sc->inbuf.softintr);
129 }
130 
131 int
132 midiread(dev_t dev, struct uio *uio, int ioflag)
133 {
134 	struct midi_softc *sc;
135 	struct midi_buffer *mb;
136 	size_t count;
137 	int error;
138 
139 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
140 	if (sc == NULL)
141 		return ENXIO;
142 	if (!(sc->flags & FREAD)) {
143 		error = ENXIO;
144 		goto done;
145 	}
146 	mb = &sc->inbuf;
147 
148 	/* if there is no data then sleep (unless IO_NDELAY flag is set) */
149 	error = 0;
150 	mtx_enter(&audio_lock);
151 	while (MIDIBUF_ISEMPTY(mb)) {
152 		if (ioflag & IO_NDELAY) {
153 			error = EWOULDBLOCK;
154 			goto done_mtx;
155 		}
156 		sc->inbuf.blocking = 1;
157 		error = msleep_nsec(&sc->inbuf.blocking, &audio_lock,
158 		    PWAIT | PCATCH, "mid_rd", INFSLP);
159 		if (!(sc->dev.dv_flags & DVF_ACTIVE))
160 			error = EIO;
161 		if (error)
162 			goto done_mtx;
163 	}
164 
165 	/* at this stage, there is at least 1 byte */
166 
167 	while (uio->uio_resid > 0 && mb->used > 0) {
168 		count = MIDIBUF_SIZE - mb->start;
169 		if (count > mb->used)
170 			count = mb->used;
171 		if (count > uio->uio_resid)
172 			count = uio->uio_resid;
173 		mtx_leave(&audio_lock);
174 		error = uiomove(mb->data + mb->start, count, uio);
175 		if (error)
176 			goto done;
177 		mtx_enter(&audio_lock);
178 		MIDIBUF_REMOVE(mb, count);
179 	}
180 
181 done_mtx:
182 	mtx_leave(&audio_lock);
183 done:
184 	device_unref(&sc->dev);
185 	return error;
186 }
187 
188 void
189 midi_ointr(void *addr)
190 {
191 	struct midi_softc *sc = (struct midi_softc *)addr;
192 	struct midi_buffer *mb;
193 
194 	MUTEX_ASSERT_LOCKED(&audio_lock);
195 	if (!(sc->dev.dv_flags & DVF_ACTIVE) || !(sc->flags & FWRITE))
196 		return;
197 
198 	mb = &sc->outbuf;
199 	if (mb->used > 0) {
200 #ifdef MIDI_DEBUG
201 		if (!sc->isbusy) {
202 			printf("midi_ointr: output must be busy\n");
203 		}
204 #endif
205 		midi_out_do(sc);
206 	} else if (sc->isbusy)
207 		midi_out_stop(sc);
208 }
209 
210 void
211 midi_timeout(void *addr)
212 {
213 	mtx_enter(&audio_lock);
214 	midi_ointr(addr);
215 	mtx_leave(&audio_lock);
216 }
217 
218 void
219 midi_out_start(struct midi_softc *sc)
220 {
221 	if (!sc->isbusy) {
222 		sc->isbusy = 1;
223 		midi_out_do(sc);
224 	}
225 }
226 
227 void
228 midi_out_stop(struct midi_softc *sc)
229 {
230 	sc->isbusy = 0;
231 
232 	/*
233 	 * As long as selwakeup() needs to be protected by the
234 	 * KERNEL_LOCK() we have to delay the wakeup to another
235 	 * context to keep the interrupt context KERNEL_LOCK()
236 	 * free.
237 	 */
238 	softintr_schedule(sc->outbuf.softintr);
239 }
240 
241 void
242 midi_out_do(struct midi_softc *sc)
243 {
244 	struct midi_buffer *mb = &sc->outbuf;
245 
246 	while (mb->used > 0) {
247 		if (!sc->hw_if->output(sc->hw_hdl, mb->data[mb->start]))
248 			break;
249 		MIDIBUF_REMOVE(mb, 1);
250 		if (MIDIBUF_ISEMPTY(mb)) {
251 			if (sc->hw_if->flush != NULL)
252 				sc->hw_if->flush(sc->hw_hdl);
253 			midi_out_stop(sc);
254 			return;
255 		}
256 	}
257 
258 	if (!(sc->props & MIDI_PROP_OUT_INTR)) {
259 		if (MIDIBUF_ISEMPTY(mb))
260 			midi_out_stop(sc);
261 		else
262 			timeout_add(&sc->timeo, 1);
263 	}
264 }
265 
266 int
267 midiwrite(dev_t dev, struct uio *uio, int ioflag)
268 {
269 	struct midi_softc *sc;
270 	struct midi_buffer *mb;
271 	size_t count;
272 	int error;
273 
274 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
275 	if (sc == NULL)
276 		return ENXIO;
277 	if (!(sc->flags & FWRITE)) {
278 		error = ENXIO;
279 		goto done;
280 	}
281 	mb = &sc->outbuf;
282 
283 	/*
284 	 * If IO_NDELAY flag is set then check if there is enough room
285 	 * in the buffer to store at least one byte. If not then dont
286 	 * start the write process.
287 	 */
288 	error = 0;
289 	mtx_enter(&audio_lock);
290 	if ((ioflag & IO_NDELAY) && MIDIBUF_ISFULL(mb) && (uio->uio_resid > 0)) {
291 		error = EWOULDBLOCK;
292 		goto done_mtx;
293 	}
294 
295 	while (uio->uio_resid > 0) {
296 		while (MIDIBUF_ISFULL(mb)) {
297 			if (ioflag & IO_NDELAY) {
298 				/*
299 				 * At this stage at least one byte is already
300 				 * moved so we do not return EWOULDBLOCK
301 				 */
302 				goto done_mtx;
303 			}
304 			sc->outbuf.blocking = 1;
305 			error = msleep_nsec(&sc->outbuf.blocking, &audio_lock,
306 			    PWAIT | PCATCH, "mid_wr", INFSLP);
307 			if (!(sc->dev.dv_flags & DVF_ACTIVE))
308 				error = EIO;
309 			if (error)
310 				goto done_mtx;
311 		}
312 
313 		count = MIDIBUF_SIZE - MIDIBUF_END(mb);
314 		if (count > MIDIBUF_AVAIL(mb))
315 			count = MIDIBUF_AVAIL(mb);
316 		if (count > uio->uio_resid)
317 			count = uio->uio_resid;
318 		mtx_leave(&audio_lock);
319 		error = uiomove(mb->data + MIDIBUF_END(mb), count, uio);
320 		if (error)
321 			goto done;
322 		mtx_enter(&audio_lock);
323 		mb->used += count;
324 		midi_out_start(sc);
325 	}
326 
327 done_mtx:
328 	mtx_leave(&audio_lock);
329 done:
330 	device_unref(&sc->dev);
331 	return error;
332 }
333 
334 int
335 midipoll(dev_t dev, int events, struct proc *p)
336 {
337 	struct midi_softc *sc;
338 	int revents;
339 
340 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
341 	if (sc == NULL)
342 		return POLLERR;
343 	revents = 0;
344 	mtx_enter(&audio_lock);
345 	if (events & (POLLIN | POLLRDNORM)) {
346 		if (!MIDIBUF_ISEMPTY(&sc->inbuf))
347 			revents |= events & (POLLIN | POLLRDNORM);
348 	}
349 	if (events & (POLLOUT | POLLWRNORM)) {
350 		if (!MIDIBUF_ISFULL(&sc->outbuf))
351 			revents |= events & (POLLOUT | POLLWRNORM);
352 	}
353 	if (revents == 0) {
354 		if (events & (POLLIN | POLLRDNORM))
355 			selrecord(p, &sc->inbuf.sel);
356 		if (events & (POLLOUT | POLLWRNORM))
357 			selrecord(p, &sc->outbuf.sel);
358 	}
359 	mtx_leave(&audio_lock);
360 	device_unref(&sc->dev);
361 	return (revents);
362 }
363 
364 int
365 midikqfilter(dev_t dev, struct knote *kn)
366 {
367 	struct midi_softc *sc;
368 	struct klist 	  *klist;
369 	int error;
370 
371 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
372 	if (sc == NULL)
373 		return ENXIO;
374 	error = 0;
375 	switch (kn->kn_filter) {
376 	case EVFILT_READ:
377 		klist = &sc->inbuf.sel.si_note;
378 		kn->kn_fop = &midiread_filtops;
379 		break;
380 	case EVFILT_WRITE:
381 		klist = &sc->outbuf.sel.si_note;
382 		kn->kn_fop = &midiwrite_filtops;
383 		break;
384 	default:
385 		error = EINVAL;
386 		goto done;
387 	}
388 	kn->kn_hook = (void *)sc;
389 
390 	mtx_enter(&audio_lock);
391 	klist_insert_locked(klist, kn);
392 	mtx_leave(&audio_lock);
393 done:
394 	device_unref(&sc->dev);
395 	return error;
396 }
397 
398 void
399 filt_midirdetach(struct knote *kn)
400 {
401 	struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
402 
403 	mtx_enter(&audio_lock);
404 	klist_remove_locked(&sc->inbuf.sel.si_note, kn);
405 	mtx_leave(&audio_lock);
406 }
407 
408 int
409 filt_midiread(struct knote *kn, long hint)
410 {
411 	struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
412 	int retval;
413 
414 	if ((hint & NOTE_SUBMIT) == 0)
415 		mtx_enter(&audio_lock);
416 	retval = !MIDIBUF_ISEMPTY(&sc->inbuf);
417 	if ((hint & NOTE_SUBMIT) == 0)
418 		mtx_leave(&audio_lock);
419 
420 	return (retval);
421 }
422 
423 void
424 filt_midiwdetach(struct knote *kn)
425 {
426 	struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
427 
428 	mtx_enter(&audio_lock);
429 	klist_remove_locked(&sc->outbuf.sel.si_note, kn);
430 	mtx_leave(&audio_lock);
431 }
432 
433 int
434 filt_midiwrite(struct knote *kn, long hint)
435 {
436 	struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
437 	int		   retval;
438 
439 	if ((hint & NOTE_SUBMIT) == 0)
440 		mtx_enter(&audio_lock);
441 	retval = !MIDIBUF_ISFULL(&sc->outbuf);
442 	if ((hint & NOTE_SUBMIT) == 0)
443 		mtx_leave(&audio_lock);
444 
445 	return (retval);
446 }
447 
448 int
449 midiioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
450 {
451 	struct midi_softc *sc;
452 	int error;
453 
454 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
455 	if (sc == NULL)
456 		return ENXIO;
457 	error = 0;
458 	switch(cmd) {
459 	case FIONBIO:
460 		/* All handled in the upper FS layer */
461 		break;
462 	default:
463 		error = ENOTTY;
464 	}
465 	device_unref(&sc->dev);
466 	return error;
467 }
468 
469 int
470 midiopen(dev_t dev, int flags, int mode, struct proc *p)
471 {
472 	struct midi_softc *sc;
473 	int error;
474 
475 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
476 	if (sc == NULL)
477 		return ENXIO;
478 	error = 0;
479 	if (sc->flags) {
480 		error = EBUSY;
481 		goto done;
482 	}
483 	MIDIBUF_INIT(&sc->inbuf);
484 	MIDIBUF_INIT(&sc->outbuf);
485 	sc->isbusy = 0;
486 	sc->inbuf.blocking = sc->outbuf.blocking = 0;
487 	sc->flags = flags;
488 	error = sc->hw_if->open(sc->hw_hdl, flags, midi_iintr, midi_ointr, sc);
489 	if (error)
490 		sc->flags = 0;
491 done:
492 	device_unref(&sc->dev);
493 	return error;
494 }
495 
496 int
497 midiclose(dev_t dev, int fflag, int devtype, struct proc *p)
498 {
499 	struct midi_softc *sc;
500 	struct midi_buffer *mb;
501 	int error;
502 
503 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
504 	if (sc == NULL)
505 		return ENXIO;
506 
507 	/* start draining output buffer */
508 	error = 0;
509 	mb = &sc->outbuf;
510 	mtx_enter(&audio_lock);
511 	if (!MIDIBUF_ISEMPTY(mb))
512 		midi_out_start(sc);
513 	while (sc->isbusy) {
514 		sc->outbuf.blocking = 1;
515 		error = msleep_nsec(&sc->outbuf.blocking, &audio_lock,
516 		    PWAIT, "mid_dr", SEC_TO_NSEC(5));
517 		if (!(sc->dev.dv_flags & DVF_ACTIVE))
518 			error = EIO;
519 		if (error)
520 			break;
521 	}
522 	mtx_leave(&audio_lock);
523 
524 	/*
525 	 * some hw_if->close() reset immediately the midi uart
526 	 * which flushes the internal buffer of the uart device,
527 	 * so we may lose some (important) data. To avoid this,
528 	 * sleep 20ms (around 64 bytes) to give the time to the
529 	 * uart to drain its internal buffers.
530 	 */
531 	tsleep_nsec(&sc->outbuf.blocking, PWAIT, "mid_cl", MSEC_TO_NSEC(20));
532 	sc->hw_if->close(sc->hw_hdl);
533 	sc->flags = 0;
534 	device_unref(&sc->dev);
535 	return 0;
536 }
537 
538 int
539 midiprobe(struct device *parent, void *match, void *aux)
540 {
541 	struct audio_attach_args *sa = aux;
542 
543 	return (sa != NULL && (sa->type == AUDIODEV_TYPE_MIDI) ? 1 : 0);
544 }
545 
546 void
547 midiattach(struct device *parent, struct device *self, void *aux)
548 {
549 	struct midi_info	  mi;
550 	struct midi_softc        *sc = (struct midi_softc *)self;
551 	struct audio_attach_args *sa = (struct audio_attach_args *)aux;
552 	struct midi_hw_if        *hwif = sa->hwif;
553 	void  			 *hdl = sa->hdl;
554 
555 #ifdef DIAGNOSTIC
556 	if (hwif == 0 ||
557 	    hwif->open == 0 ||
558 	    hwif->close == 0 ||
559 	    hwif->output == 0 ||
560 	    hwif->getinfo == 0) {
561 		printf("%s: missing method\n", DEVNAME(sc));
562 		return;
563 	}
564 #endif
565 
566 	sc->inbuf.softintr = softintr_establish(IPL_SOFTMIDI,
567 	    midi_buf_wakeup, &sc->inbuf);
568 	if (sc->inbuf.softintr == NULL) {
569 		printf("%s: can't establish input softintr\n", DEVNAME(sc));
570 		return;
571 	}
572 
573 	sc->outbuf.softintr = softintr_establish(IPL_SOFTMIDI,
574 	    midi_buf_wakeup, &sc->outbuf);
575 	if (sc->outbuf.softintr == NULL) {
576 		printf("%s: can't establish output softintr\n", DEVNAME(sc));
577 		softintr_disestablish(sc->inbuf.softintr);
578 		return;
579 	}
580 
581 	sc->hw_if = hwif;
582 	sc->hw_hdl = hdl;
583 	sc->hw_if->getinfo(sc->hw_hdl, &mi);
584 	sc->props = mi.props;
585 	sc->flags = 0;
586 	timeout_set(&sc->timeo, midi_timeout, sc);
587 	printf(": <%s>\n", mi.name);
588 }
589 
590 int
591 mididetach(struct device *self, int flags)
592 {
593 	struct midi_softc *sc = (struct midi_softc *)self;
594 	int maj, mn;
595 
596 	/* locate the major number */
597 	for (maj = 0; maj < nchrdev; maj++) {
598 		if (cdevsw[maj].d_open == midiopen) {
599 			/* Nuke the vnodes for any open instances (calls close). */
600 			mn = self->dv_unit;
601 			vdevgone(maj, mn, mn, VCHR);
602 		}
603 	}
604 
605 	/*
606 	 * The close() method did nothing (device_lookup() returns
607 	 * NULL), so quickly halt transfers (normally parent is already
608 	 * gone, and code below is no-op), and wake-up user-land blocked
609 	 * in read/write/ioctl, which return EIO.
610 	 */
611 	if (sc->flags) {
612 		KERNEL_ASSERT_LOCKED();
613 		if (sc->flags & FREAD) {
614 			wakeup(&sc->inbuf.blocking);
615 			mtx_enter(&audio_lock);
616 			selwakeup(&sc->inbuf.sel);
617 			mtx_leave(&audio_lock);
618 		}
619 		if (sc->flags & FWRITE) {
620 			wakeup(&sc->outbuf.blocking);
621 			mtx_enter(&audio_lock);
622 			selwakeup(&sc->outbuf.sel);
623 			mtx_leave(&audio_lock);
624 		}
625 		sc->hw_if->close(sc->hw_hdl);
626 		sc->flags = 0;
627 	}
628 
629 	klist_invalidate(&sc->inbuf.sel.si_note);
630 	klist_invalidate(&sc->outbuf.sel.si_note);
631 
632 	if (sc->inbuf.softintr)
633 		softintr_disestablish(sc->inbuf.softintr);
634 	if (sc->outbuf.softintr)
635 		softintr_disestablish(sc->outbuf.softintr);
636 	return 0;
637 }
638 
639 int
640 midiprint(void *aux, const char *pnp)
641 {
642 	if (pnp)
643 		printf("midi at %s", pnp);
644 	return (UNCONF);
645 }
646 
647 struct device *
648 midi_attach_mi(struct midi_hw_if *hwif, void *hdl, struct device *dev)
649 {
650 	struct audio_attach_args arg;
651 
652 	arg.type = AUDIODEV_TYPE_MIDI;
653 	arg.hwif = hwif;
654 	arg.hdl = hdl;
655 	return config_found(dev, &arg, midiprint);
656 }
657