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