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