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