xref: /openbsd-src/sys/dev/midi.c (revision b9ae17a00bed12afbf856d60e03f648694a9de20)
1*b9ae17a0Sguenther /*	$OpenBSD: midi.c,v 1.58 2024/12/30 02:46:00 guenther Exp $	*/
2d1b5c3f8Sbrad 
3081bdb75Sderaadt /*
4081bdb75Sderaadt  * Copyright (c) 2003, 2004 Alexandre Ratchov
5081bdb75Sderaadt  *
6081bdb75Sderaadt  * Permission to use, copy, modify, and distribute this software for any
7081bdb75Sderaadt  * purpose with or without fee is hereby granted, provided that the above
8081bdb75Sderaadt  * copyright notice and this permission notice appear in all copies.
9081bdb75Sderaadt  *
10081bdb75Sderaadt  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11081bdb75Sderaadt  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12081bdb75Sderaadt  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13081bdb75Sderaadt  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14081bdb75Sderaadt  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15081bdb75Sderaadt  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16081bdb75Sderaadt  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17081bdb75Sderaadt  */
185daee67fSniklas 
19081bdb75Sderaadt #include <sys/param.h>
20081bdb75Sderaadt #include <sys/fcntl.h>
21081bdb75Sderaadt #include <sys/systm.h>
22081bdb75Sderaadt #include <sys/ioctl.h>
23081bdb75Sderaadt #include <sys/conf.h>
24081bdb75Sderaadt #include <sys/kernel.h>
25081bdb75Sderaadt #include <sys/timeout.h>
26081bdb75Sderaadt #include <sys/vnode.h>
27081bdb75Sderaadt #include <sys/signalvar.h>
28081bdb75Sderaadt #include <sys/device.h>
295daee67fSniklas 
30081bdb75Sderaadt #include <dev/midi_if.h>
31081bdb75Sderaadt #include <dev/audio_if.h>
32081bdb75Sderaadt #include <dev/midivar.h>
335daee67fSniklas 
3402e0ccc4Sratchov #define DEVNAME(sc)		((sc)->dev.dv_xname)
356df90749Sjsg 
36081bdb75Sderaadt int	midiopen(dev_t, int, int, struct proc *);
37081bdb75Sderaadt int	midiclose(dev_t, int, int, struct proc *);
38081bdb75Sderaadt int	midiread(dev_t, struct uio *, int);
39081bdb75Sderaadt int	midiwrite(dev_t, struct uio *, int);
4095f33846Snicm int	midikqfilter(dev_t, struct knote *);
41081bdb75Sderaadt int	midiioctl(dev_t, u_long, caddr_t, int, struct proc *);
42c4071fd1Smillert int	midiprobe(struct device *, void *, void *);
43c4071fd1Smillert void	midiattach(struct device *, struct device *, void *);
44081bdb75Sderaadt int	mididetach(struct device *, int);
45081bdb75Sderaadt int	midiprint(void *, const char *);
46081bdb75Sderaadt 
47081bdb75Sderaadt void	midi_iintr(void *, int);
48081bdb75Sderaadt void 	midi_ointr(void *);
49886882aaSratchov void	midi_timeout(void *);
50081bdb75Sderaadt void	midi_out_start(struct midi_softc *);
51081bdb75Sderaadt void	midi_out_stop(struct midi_softc *);
52081bdb75Sderaadt void	midi_out_do(struct midi_softc *);
53081bdb75Sderaadt 
54081bdb75Sderaadt 
55471aeecfSnaddy const struct cfattach midi_ca = {
56081bdb75Sderaadt 	sizeof(struct midi_softc), midiprobe, midiattach, mididetach
575daee67fSniklas };
585daee67fSniklas 
595daee67fSniklas struct cfdriver midi_cd = {
605daee67fSniklas 	NULL, "midi", DV_DULL
615daee67fSniklas };
625daee67fSniklas 
635daee67fSniklas 
6495f33846Snicm void filt_midiwdetach(struct knote *);
6595f33846Snicm int filt_midiwrite(struct knote *, long);
66cedac2f9Smvs int filt_midimodify(struct kevent *, struct knote *);
67cedac2f9Smvs int filt_midiprocess(struct knote *, struct kevent *);
6895f33846Snicm 
6994321eb4Svisa const struct filterops midiwrite_filtops = {
70cedac2f9Smvs 	.f_flags	= FILTEROP_ISFD | FILTEROP_MPSAFE,
7194321eb4Svisa 	.f_attach	= NULL,
7294321eb4Svisa 	.f_detach	= filt_midiwdetach,
7394321eb4Svisa 	.f_event	= filt_midiwrite,
74cedac2f9Smvs 	.f_modify	= filt_midimodify,
75cedac2f9Smvs 	.f_process	= filt_midiprocess,
7695f33846Snicm };
7795f33846Snicm 
7895f33846Snicm void filt_midirdetach(struct knote *);
7995f33846Snicm int filt_midiread(struct knote *, long);
8095f33846Snicm 
8194321eb4Svisa const struct filterops midiread_filtops = {
82cedac2f9Smvs 	.f_flags	= FILTEROP_ISFD | FILTEROP_MPSAFE,
8394321eb4Svisa 	.f_attach	= NULL,
8494321eb4Svisa 	.f_detach	= filt_midirdetach,
8594321eb4Svisa 	.f_event	= filt_midiread,
86cedac2f9Smvs 	.f_modify	= filt_midimodify,
87cedac2f9Smvs 	.f_process	= filt_midiprocess,
8895f33846Snicm };
8995f33846Snicm 
905daee67fSniklas void
91cedac2f9Smvs midi_buf_wakeup(struct midi_buffer *buf)
9202e0ccc4Sratchov {
9302e0ccc4Sratchov 	if (buf->blocking) {
9402e0ccc4Sratchov 		wakeup(&buf->blocking);
9502e0ccc4Sratchov 		buf->blocking = 0;
9602e0ccc4Sratchov 	}
97cedac2f9Smvs 	knote_locked(&buf->klist, 0);
9802e0ccc4Sratchov }
9902e0ccc4Sratchov 
10002e0ccc4Sratchov void
101081bdb75Sderaadt midi_iintr(void *addr, int data)
1025daee67fSniklas {
103081bdb75Sderaadt 	struct midi_softc  *sc = (struct midi_softc *)addr;
1045daee67fSniklas 	struct midi_buffer *mb = &sc->inbuf;
1055daee67fSniklas 
106d0b9e3b5Sratchov 	MUTEX_ASSERT_LOCKED(&audio_lock);
107d0b9e3b5Sratchov 	if (!(sc->dev.dv_flags & DVF_ACTIVE) || !(sc->flags & FREAD))
108066545b4Sratchov 		return;
1095daee67fSniklas 
110081bdb75Sderaadt 	if (MIDIBUF_ISFULL(mb))
111081bdb75Sderaadt 		return; /* discard data */
11295f33846Snicm 
11395f33846Snicm 	MIDIBUF_WRITE(mb, data);
11402e0ccc4Sratchov 
115cedac2f9Smvs 	midi_buf_wakeup(mb);
1165c8953a5Sjsg }
1175daee67fSniklas 
1185daee67fSniklas int
119081bdb75Sderaadt midiread(dev_t dev, struct uio *uio, int ioflag)
1205daee67fSniklas {
121d0b9e3b5Sratchov 	struct midi_softc *sc;
122f14e0e78Sjsg 	struct midi_buffer *mb;
12311c52440Sratchov 	size_t count;
124886882aaSratchov 	int error;
1255daee67fSniklas 
126d0b9e3b5Sratchov 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
127d0b9e3b5Sratchov 	if (sc == NULL)
128081bdb75Sderaadt 		return ENXIO;
129d0b9e3b5Sratchov 	if (!(sc->flags & FREAD)) {
130d0b9e3b5Sratchov 		error = ENXIO;
131d0b9e3b5Sratchov 		goto done;
132d0b9e3b5Sratchov 	}
133f14e0e78Sjsg 	mb = &sc->inbuf;
1345daee67fSniklas 
135081bdb75Sderaadt 	/* if there is no data then sleep (unless IO_NDELAY flag is set) */
136d0b9e3b5Sratchov 	error = 0;
137886882aaSratchov 	mtx_enter(&audio_lock);
138081bdb75Sderaadt 	while (MIDIBUF_ISEMPTY(mb)) {
1395daee67fSniklas 		if (ioflag & IO_NDELAY) {
140d0b9e3b5Sratchov 			error = EWOULDBLOCK;
141e6c232e8Sratchov 			goto done_mtx;
1425daee67fSniklas 		}
14302e0ccc4Sratchov 		sc->inbuf.blocking = 1;
14402e0ccc4Sratchov 		error = msleep_nsec(&sc->inbuf.blocking, &audio_lock,
14502e0ccc4Sratchov 		    PWAIT | PCATCH, "mid_rd", INFSLP);
146d0b9e3b5Sratchov 		if (!(sc->dev.dv_flags & DVF_ACTIVE))
147d0b9e3b5Sratchov 			error = EIO;
148e6c232e8Sratchov 		if (error)
149e6c232e8Sratchov 			goto done_mtx;
1505daee67fSniklas 	}
151081bdb75Sderaadt 
152081bdb75Sderaadt 	/* at this stage, there is at least 1 byte */
153081bdb75Sderaadt 
154081bdb75Sderaadt 	while (uio->uio_resid > 0 && mb->used > 0) {
155081bdb75Sderaadt 		count = MIDIBUF_SIZE - mb->start;
156081bdb75Sderaadt 		if (count > mb->used)
157081bdb75Sderaadt 			count = mb->used;
158081bdb75Sderaadt 		if (count > uio->uio_resid)
159081bdb75Sderaadt 			count = uio->uio_resid;
160886882aaSratchov 		mtx_leave(&audio_lock);
16111c52440Sratchov 		error = uiomove(mb->data + mb->start, count, uio);
1624ac461caSratchov 		if (error)
163d0b9e3b5Sratchov 			goto done;
1644ac461caSratchov 		mtx_enter(&audio_lock);
165081bdb75Sderaadt 		MIDIBUF_REMOVE(mb, count);
166081bdb75Sderaadt 	}
167e6c232e8Sratchov 
168e6c232e8Sratchov done_mtx:
169886882aaSratchov 	mtx_leave(&audio_lock);
170d0b9e3b5Sratchov done:
171d0b9e3b5Sratchov 	device_unref(&sc->dev);
172d0b9e3b5Sratchov 	return error;
173081bdb75Sderaadt }
174081bdb75Sderaadt 
175081bdb75Sderaadt void
176081bdb75Sderaadt midi_ointr(void *addr)
177081bdb75Sderaadt {
178081bdb75Sderaadt 	struct midi_softc *sc = (struct midi_softc *)addr;
179081bdb75Sderaadt 	struct midi_buffer *mb;
180081bdb75Sderaadt 
181886882aaSratchov 	MUTEX_ASSERT_LOCKED(&audio_lock);
182d0b9e3b5Sratchov 	if (!(sc->dev.dv_flags & DVF_ACTIVE) || !(sc->flags & FWRITE))
183d0b9e3b5Sratchov 		return;
184d0b9e3b5Sratchov 
185081bdb75Sderaadt 	mb = &sc->outbuf;
186913ced02Sratchov 	if (mb->used > 0) {
187913ced02Sratchov #ifdef MIDI_DEBUG
188913ced02Sratchov 		if (!sc->isbusy) {
189913ced02Sratchov 			printf("midi_ointr: output must be busy\n");
190913ced02Sratchov 		}
191913ced02Sratchov #endif
192913ced02Sratchov 		midi_out_do(sc);
193913ced02Sratchov 	} else if (sc->isbusy)
194081bdb75Sderaadt 		midi_out_stop(sc);
1955daee67fSniklas }
196081bdb75Sderaadt 
197886882aaSratchov void
198886882aaSratchov midi_timeout(void *addr)
199886882aaSratchov {
200886882aaSratchov 	mtx_enter(&audio_lock);
201886882aaSratchov 	midi_ointr(addr);
202886882aaSratchov 	mtx_leave(&audio_lock);
203886882aaSratchov }
204081bdb75Sderaadt 
205081bdb75Sderaadt void
206081bdb75Sderaadt midi_out_start(struct midi_softc *sc)
207081bdb75Sderaadt {
208081bdb75Sderaadt 	if (!sc->isbusy) {
209081bdb75Sderaadt 		sc->isbusy = 1;
210081bdb75Sderaadt 		midi_out_do(sc);
211081bdb75Sderaadt 	}
2125daee67fSniklas }
2135daee67fSniklas 
2145daee67fSniklas void
215081bdb75Sderaadt midi_out_stop(struct midi_softc *sc)
2165daee67fSniklas {
217081bdb75Sderaadt 	sc->isbusy = 0;
218cedac2f9Smvs 	midi_buf_wakeup(&sc->outbuf);
2195daee67fSniklas }
2205daee67fSniklas 
221081bdb75Sderaadt void
222081bdb75Sderaadt midi_out_do(struct midi_softc *sc)
2235daee67fSniklas {
2245daee67fSniklas 	struct midi_buffer *mb = &sc->outbuf;
2255daee67fSniklas 
226f3db5e0dSratchov 	while (mb->used > 0) {
227f3db5e0dSratchov 		if (!sc->hw_if->output(sc->hw_hdl, mb->data[mb->start]))
2285daee67fSniklas 			break;
2296df90749Sjsg 		MIDIBUF_REMOVE(mb, 1);
230081bdb75Sderaadt 		if (MIDIBUF_ISEMPTY(mb)) {
2316df90749Sjsg 			if (sc->hw_if->flush != NULL)
2326df90749Sjsg 				sc->hw_if->flush(sc->hw_hdl);
233081bdb75Sderaadt 			midi_out_stop(sc);
234081bdb75Sderaadt 			return;
235081bdb75Sderaadt 		}
2365daee67fSniklas 	}
237081bdb75Sderaadt 
238081bdb75Sderaadt 	if (!(sc->props & MIDI_PROP_OUT_INTR)) {
239081bdb75Sderaadt 		if (MIDIBUF_ISEMPTY(mb))
240081bdb75Sderaadt 			midi_out_stop(sc);
241588907faSderaadt 		else
242f3db5e0dSratchov 			timeout_add(&sc->timeo, 1);
2435daee67fSniklas 	}
244081bdb75Sderaadt }
245081bdb75Sderaadt 
2465daee67fSniklas int
247081bdb75Sderaadt midiwrite(dev_t dev, struct uio *uio, int ioflag)
2485daee67fSniklas {
249d0b9e3b5Sratchov 	struct midi_softc *sc;
250f14e0e78Sjsg 	struct midi_buffer *mb;
25111c52440Sratchov 	size_t count;
252886882aaSratchov 	int error;
2535daee67fSniklas 
254d0b9e3b5Sratchov 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
255d0b9e3b5Sratchov 	if (sc == NULL)
256081bdb75Sderaadt 		return ENXIO;
257d0b9e3b5Sratchov 	if (!(sc->flags & FWRITE)) {
258d0b9e3b5Sratchov 		error = ENXIO;
259d0b9e3b5Sratchov 		goto done;
260d0b9e3b5Sratchov 	}
261f14e0e78Sjsg 	mb = &sc->outbuf;
2625daee67fSniklas 
263081bdb75Sderaadt 	/*
264081bdb75Sderaadt 	 * If IO_NDELAY flag is set then check if there is enough room
265081bdb75Sderaadt 	 * in the buffer to store at least one byte. If not then dont
266081bdb75Sderaadt 	 * start the write process.
267081bdb75Sderaadt 	 */
268d0b9e3b5Sratchov 	error = 0;
2694ac461caSratchov 	mtx_enter(&audio_lock);
2704ac461caSratchov 	if ((ioflag & IO_NDELAY) && MIDIBUF_ISFULL(mb) && (uio->uio_resid > 0)) {
271d0b9e3b5Sratchov 		error = EWOULDBLOCK;
272e6c232e8Sratchov 		goto done_mtx;
2734ac461caSratchov 	}
274081bdb75Sderaadt 
275081bdb75Sderaadt 	while (uio->uio_resid > 0) {
276081bdb75Sderaadt 		while (MIDIBUF_ISFULL(mb)) {
277081bdb75Sderaadt 			if (ioflag & IO_NDELAY) {
278081bdb75Sderaadt 				/*
279081bdb75Sderaadt 				 * At this stage at least one byte is already
280081bdb75Sderaadt 				 * moved so we do not return EWOULDBLOCK
281081bdb75Sderaadt 				 */
282e6c232e8Sratchov 				goto done_mtx;
283081bdb75Sderaadt 			}
28402e0ccc4Sratchov 			sc->outbuf.blocking = 1;
28502e0ccc4Sratchov 			error = msleep_nsec(&sc->outbuf.blocking, &audio_lock,
286a3a67300Scheloha 			    PWAIT | PCATCH, "mid_wr", INFSLP);
287d0b9e3b5Sratchov 			if (!(sc->dev.dv_flags & DVF_ACTIVE))
288d0b9e3b5Sratchov 				error = EIO;
289e6c232e8Sratchov 			if (error)
290e6c232e8Sratchov 				goto done_mtx;
291081bdb75Sderaadt 		}
292081bdb75Sderaadt 
293081bdb75Sderaadt 		count = MIDIBUF_SIZE - MIDIBUF_END(mb);
294081bdb75Sderaadt 		if (count > MIDIBUF_AVAIL(mb))
295081bdb75Sderaadt 			count = MIDIBUF_AVAIL(mb);
296081bdb75Sderaadt 		if (count > uio->uio_resid)
297081bdb75Sderaadt 			count = uio->uio_resid;
298886882aaSratchov 		mtx_leave(&audio_lock);
29911c52440Sratchov 		error = uiomove(mb->data + MIDIBUF_END(mb), count, uio);
3004ac461caSratchov 		if (error)
301d0b9e3b5Sratchov 			goto done;
3024ac461caSratchov 		mtx_enter(&audio_lock);
303081bdb75Sderaadt 		mb->used += count;
304081bdb75Sderaadt 		midi_out_start(sc);
305081bdb75Sderaadt 	}
306e6c232e8Sratchov 
307e6c232e8Sratchov done_mtx:
3084ac461caSratchov 	mtx_leave(&audio_lock);
309d0b9e3b5Sratchov done:
310d0b9e3b5Sratchov 	device_unref(&sc->dev);
311d0b9e3b5Sratchov 	return error;
312081bdb75Sderaadt }
313081bdb75Sderaadt 
314081bdb75Sderaadt int
31595f33846Snicm midikqfilter(dev_t dev, struct knote *kn)
31695f33846Snicm {
317d0b9e3b5Sratchov 	struct midi_softc *sc;
31895f33846Snicm 	struct klist 	  *klist;
319d0b9e3b5Sratchov 	int error;
32095f33846Snicm 
321d0b9e3b5Sratchov 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
322d0b9e3b5Sratchov 	if (sc == NULL)
323d0b9e3b5Sratchov 		return ENXIO;
324d0b9e3b5Sratchov 	error = 0;
32595f33846Snicm 	switch (kn->kn_filter) {
32695f33846Snicm 	case EVFILT_READ:
327cedac2f9Smvs 		klist = &sc->inbuf.klist;
32895f33846Snicm 		kn->kn_fop = &midiread_filtops;
32995f33846Snicm 		break;
33095f33846Snicm 	case EVFILT_WRITE:
331cedac2f9Smvs 		klist = &sc->outbuf.klist;
33295f33846Snicm 		kn->kn_fop = &midiwrite_filtops;
33395f33846Snicm 		break;
33495f33846Snicm 	default:
335d0b9e3b5Sratchov 		error = EINVAL;
336d0b9e3b5Sratchov 		goto done;
33795f33846Snicm 	}
33895f33846Snicm 	kn->kn_hook = (void *)sc;
33995f33846Snicm 
340cedac2f9Smvs 	klist_insert(klist, kn);
341d0b9e3b5Sratchov done:
342d0b9e3b5Sratchov 	device_unref(&sc->dev);
343d0b9e3b5Sratchov 	return error;
34495f33846Snicm }
34595f33846Snicm 
34695f33846Snicm void
34795f33846Snicm filt_midirdetach(struct knote *kn)
34895f33846Snicm {
34995f33846Snicm 	struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
35095f33846Snicm 
351cedac2f9Smvs 	klist_remove(&sc->inbuf.klist, kn);
35295f33846Snicm }
35395f33846Snicm 
35495f33846Snicm int
35595f33846Snicm filt_midiread(struct knote *kn, long hint)
35695f33846Snicm {
35795f33846Snicm 	struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
35895f33846Snicm 
359cedac2f9Smvs 	return (!MIDIBUF_ISEMPTY(&sc->inbuf));
36095f33846Snicm }
36195f33846Snicm 
36295f33846Snicm void
36395f33846Snicm filt_midiwdetach(struct knote *kn)
36495f33846Snicm {
36595f33846Snicm 	struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
36695f33846Snicm 
367cedac2f9Smvs 	klist_remove(&sc->outbuf.klist, kn);
36895f33846Snicm }
36995f33846Snicm 
37095f33846Snicm int
37195f33846Snicm filt_midiwrite(struct knote *kn, long hint)
37295f33846Snicm {
37395f33846Snicm 	struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
37495f33846Snicm 
375cedac2f9Smvs 	return (!MIDIBUF_ISFULL(&sc->outbuf));
376cedac2f9Smvs }
377cedac2f9Smvs 
378cedac2f9Smvs int
379cedac2f9Smvs filt_midimodify(struct kevent *kev, struct knote *kn)
380cedac2f9Smvs {
381cedac2f9Smvs 	int active;
382cedac2f9Smvs 
383886882aaSratchov 	mtx_enter(&audio_lock);
384cedac2f9Smvs 	active = knote_modify(kev, kn);
385886882aaSratchov 	mtx_leave(&audio_lock);
38695f33846Snicm 
387cedac2f9Smvs 	return active;
388cedac2f9Smvs }
389cedac2f9Smvs 
390cedac2f9Smvs int
391cedac2f9Smvs filt_midiprocess(struct knote *kn, struct kevent *kev)
392cedac2f9Smvs {
393cedac2f9Smvs 	int active;
394cedac2f9Smvs 
395cedac2f9Smvs 	mtx_enter(&audio_lock);
396cedac2f9Smvs 	active = knote_process(kn, kev);
397cedac2f9Smvs 	mtx_leave(&audio_lock);
398cedac2f9Smvs 
399cedac2f9Smvs 	return active;
40095f33846Snicm }
40195f33846Snicm 
40295f33846Snicm int
403081bdb75Sderaadt midiioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
404081bdb75Sderaadt {
405d0b9e3b5Sratchov 	struct midi_softc *sc;
406d0b9e3b5Sratchov 	int error;
407081bdb75Sderaadt 
408d0b9e3b5Sratchov 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
409d0b9e3b5Sratchov 	if (sc == NULL)
410d0b9e3b5Sratchov 		return ENXIO;
411d0b9e3b5Sratchov 	error = 0;
412081bdb75Sderaadt 	switch(cmd) {
413081bdb75Sderaadt 	default:
414d0b9e3b5Sratchov 		error = ENOTTY;
415081bdb75Sderaadt 	}
416d0b9e3b5Sratchov 	device_unref(&sc->dev);
417d0b9e3b5Sratchov 	return error;
418081bdb75Sderaadt }
419081bdb75Sderaadt 
420081bdb75Sderaadt int
421081bdb75Sderaadt midiopen(dev_t dev, int flags, int mode, struct proc *p)
422081bdb75Sderaadt {
423081bdb75Sderaadt 	struct midi_softc *sc;
424d0b9e3b5Sratchov 	int error;
425081bdb75Sderaadt 
426d0b9e3b5Sratchov 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
427d0b9e3b5Sratchov 	if (sc == NULL)
428081bdb75Sderaadt 		return ENXIO;
429d0b9e3b5Sratchov 	error = 0;
430d0b9e3b5Sratchov 	if (sc->flags) {
431d0b9e3b5Sratchov 		error = EBUSY;
432d0b9e3b5Sratchov 		goto done;
433d0b9e3b5Sratchov 	}
434081bdb75Sderaadt 	MIDIBUF_INIT(&sc->inbuf);
435081bdb75Sderaadt 	MIDIBUF_INIT(&sc->outbuf);
436081bdb75Sderaadt 	sc->isbusy = 0;
43702e0ccc4Sratchov 	sc->inbuf.blocking = sc->outbuf.blocking = 0;
438081bdb75Sderaadt 	sc->flags = flags;
439d0b9e3b5Sratchov 	error = sc->hw_if->open(sc->hw_hdl, flags, midi_iintr, midi_ointr, sc);
440d0b9e3b5Sratchov 	if (error)
441c8d03e3aSratchov 		sc->flags = 0;
442d0b9e3b5Sratchov done:
443d0b9e3b5Sratchov 	device_unref(&sc->dev);
444d0b9e3b5Sratchov 	return error;
445081bdb75Sderaadt }
446081bdb75Sderaadt 
447081bdb75Sderaadt int
448081bdb75Sderaadt midiclose(dev_t dev, int fflag, int devtype, struct proc *p)
449081bdb75Sderaadt {
450d0b9e3b5Sratchov 	struct midi_softc *sc;
451081bdb75Sderaadt 	struct midi_buffer *mb;
452081bdb75Sderaadt 	int error;
453081bdb75Sderaadt 
454d0b9e3b5Sratchov 	sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
455d0b9e3b5Sratchov 	if (sc == NULL)
456d0b9e3b5Sratchov 		return ENXIO;
457d0b9e3b5Sratchov 
458081bdb75Sderaadt 	/* start draining output buffer */
459d0b9e3b5Sratchov 	error = 0;
460d0b9e3b5Sratchov 	mb = &sc->outbuf;
461886882aaSratchov 	mtx_enter(&audio_lock);
462081bdb75Sderaadt 	if (!MIDIBUF_ISEMPTY(mb))
463081bdb75Sderaadt 		midi_out_start(sc);
464081bdb75Sderaadt 	while (sc->isbusy) {
46502e0ccc4Sratchov 		sc->outbuf.blocking = 1;
46602e0ccc4Sratchov 		error = msleep_nsec(&sc->outbuf.blocking, &audio_lock,
467a3a67300Scheloha 		    PWAIT, "mid_dr", SEC_TO_NSEC(5));
468d0b9e3b5Sratchov 		if (!(sc->dev.dv_flags & DVF_ACTIVE))
469d0b9e3b5Sratchov 			error = EIO;
470d0b9e3b5Sratchov 		if (error)
471081bdb75Sderaadt 			break;
472081bdb75Sderaadt 	}
473886882aaSratchov 	mtx_leave(&audio_lock);
474081bdb75Sderaadt 
475081bdb75Sderaadt 	/*
476081bdb75Sderaadt 	 * some hw_if->close() reset immediately the midi uart
477081bdb75Sderaadt 	 * which flushes the internal buffer of the uart device,
478f3db5e0dSratchov 	 * so we may lose some (important) data. To avoid this,
479f3db5e0dSratchov 	 * sleep 20ms (around 64 bytes) to give the time to the
480f3db5e0dSratchov 	 * uart to drain its internal buffers.
481081bdb75Sderaadt 	 */
48202e0ccc4Sratchov 	tsleep_nsec(&sc->outbuf.blocking, PWAIT, "mid_cl", MSEC_TO_NSEC(20));
483081bdb75Sderaadt 	sc->hw_if->close(sc->hw_hdl);
484c8d03e3aSratchov 	sc->flags = 0;
485d0b9e3b5Sratchov 	device_unref(&sc->dev);
486081bdb75Sderaadt 	return 0;
487081bdb75Sderaadt }
488081bdb75Sderaadt 
489081bdb75Sderaadt int
490081bdb75Sderaadt midiprobe(struct device *parent, void *match, void *aux)
491081bdb75Sderaadt {
492081bdb75Sderaadt 	struct audio_attach_args *sa = aux;
493066545b4Sratchov 
494081bdb75Sderaadt 	return (sa != NULL && (sa->type == AUDIODEV_TYPE_MIDI) ? 1 : 0);
495081bdb75Sderaadt }
496081bdb75Sderaadt 
497081bdb75Sderaadt void
498081bdb75Sderaadt midiattach(struct device *parent, struct device *self, void *aux)
499081bdb75Sderaadt {
50079f4179eSratchov 	struct midi_info	  mi;
501081bdb75Sderaadt 	struct midi_softc        *sc = (struct midi_softc *)self;
502081bdb75Sderaadt 	struct audio_attach_args *sa = (struct audio_attach_args *)aux;
5030d6a2fdeSmiod 	const struct midi_hw_if  *hwif = sa->hwif;
504081bdb75Sderaadt 	void  			 *hdl = sa->hdl;
505081bdb75Sderaadt 
506081bdb75Sderaadt #ifdef DIAGNOSTIC
507081bdb75Sderaadt 	if (hwif == 0 ||
508081bdb75Sderaadt 	    hwif->open == 0 ||
509081bdb75Sderaadt 	    hwif->close == 0 ||
510081bdb75Sderaadt 	    hwif->output == 0 ||
511081bdb75Sderaadt 	    hwif->getinfo == 0) {
51202e0ccc4Sratchov 		printf("%s: missing method\n", DEVNAME(sc));
513081bdb75Sderaadt 		return;
514081bdb75Sderaadt 	}
515081bdb75Sderaadt #endif
51602e0ccc4Sratchov 
517cedac2f9Smvs 	klist_init_mutex(&sc->inbuf.klist, &audio_lock);
518cedac2f9Smvs 	klist_init_mutex(&sc->outbuf.klist, &audio_lock);
51902e0ccc4Sratchov 
520081bdb75Sderaadt 	sc->hw_if = hwif;
521081bdb75Sderaadt 	sc->hw_hdl = hdl;
52279f4179eSratchov 	sc->hw_if->getinfo(sc->hw_hdl, &mi);
52379f4179eSratchov 	sc->props = mi.props;
524c8d03e3aSratchov 	sc->flags = 0;
52579f4179eSratchov 	timeout_set(&sc->timeo, midi_timeout, sc);
52679f4179eSratchov 	printf(": <%s>\n", mi.name);
527081bdb75Sderaadt }
528081bdb75Sderaadt 
529081bdb75Sderaadt int
530081bdb75Sderaadt mididetach(struct device *self, int flags)
531081bdb75Sderaadt {
532081bdb75Sderaadt 	struct midi_softc *sc = (struct midi_softc *)self;
533081bdb75Sderaadt 	int maj, mn;
534081bdb75Sderaadt 
535081bdb75Sderaadt 	/* locate the major number */
536d3538cb0Sratchov 	for (maj = 0; maj < nchrdev; maj++) {
537d3538cb0Sratchov 		if (cdevsw[maj].d_open == midiopen) {
538081bdb75Sderaadt 			/* Nuke the vnodes for any open instances (calls close). */
539081bdb75Sderaadt 			mn = self->dv_unit;
540081bdb75Sderaadt 			vdevgone(maj, mn, mn, VCHR);
541d3538cb0Sratchov 		}
542d3538cb0Sratchov 	}
543d0b9e3b5Sratchov 
544d0b9e3b5Sratchov 	/*
545d0b9e3b5Sratchov 	 * The close() method did nothing (device_lookup() returns
546d0b9e3b5Sratchov 	 * NULL), so quickly halt transfers (normally parent is already
547d0b9e3b5Sratchov 	 * gone, and code below is no-op), and wake-up user-land blocked
548d0b9e3b5Sratchov 	 * in read/write/ioctl, which return EIO.
549d0b9e3b5Sratchov 	 */
550d0b9e3b5Sratchov 	if (sc->flags) {
551501ade62Sratchov 		KERNEL_ASSERT_LOCKED();
552cedac2f9Smvs 		if (sc->flags & FREAD)
553501ade62Sratchov 			wakeup(&sc->inbuf.blocking);
554cedac2f9Smvs 		if (sc->flags & FWRITE)
555501ade62Sratchov 			wakeup(&sc->outbuf.blocking);
556d0b9e3b5Sratchov 		sc->hw_if->close(sc->hw_hdl);
557d0b9e3b5Sratchov 		sc->flags = 0;
558d0b9e3b5Sratchov 	}
559501ade62Sratchov 
560cedac2f9Smvs 	klist_invalidate(&sc->inbuf.klist);
561cedac2f9Smvs 	klist_invalidate(&sc->outbuf.klist);
562cedac2f9Smvs 	klist_free(&sc->inbuf.klist);
563cedac2f9Smvs 	klist_free(&sc->outbuf.klist);
564501ade62Sratchov 
565081bdb75Sderaadt 	return 0;
566081bdb75Sderaadt }
567081bdb75Sderaadt 
568081bdb75Sderaadt int
569081bdb75Sderaadt midiprint(void *aux, const char *pnp)
5702e92e48dSfgsch {
5712e92e48dSfgsch 	if (pnp)
5722e92e48dSfgsch 		printf("midi at %s", pnp);
5732e92e48dSfgsch 	return (UNCONF);
5745daee67fSniklas }
5755daee67fSniklas 
576081bdb75Sderaadt struct device *
5770d6a2fdeSmiod midi_attach_mi(const struct midi_hw_if *hwif, void *hdl, struct device *dev)
578081bdb75Sderaadt {
579081bdb75Sderaadt 	struct audio_attach_args arg;
580081bdb75Sderaadt 
581081bdb75Sderaadt 	arg.type = AUDIODEV_TYPE_MIDI;
582081bdb75Sderaadt 	arg.hwif = hwif;
583081bdb75Sderaadt 	arg.hdl = hdl;
584081bdb75Sderaadt 	return config_found(dev, &arg, midiprint);
585081bdb75Sderaadt }
586