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