xref: /freebsd/sys/dev/sound/midi/midi.c (revision 8ccc0d235c226d84112561d453c49904398d085c)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2003 Mathew Kanner
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Lennart Augustsson (augustss@netbsd.org).
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/conf.h>
36 #include <sys/fcntl.h>
37 #include <sys/kernel.h>
38 #include <sys/kobj.h>
39 #include <sys/lock.h>
40 #include <sys/module.h>
41 #include <sys/mutex.h>
42 #include <sys/poll.h>
43 #include <sys/queue.h>
44 #include <sys/selinfo.h>
45 #include <sys/sx.h>
46 #include <sys/sysctl.h>
47 #include <sys/uio.h>
48 
49 #ifdef HAVE_KERNEL_OPTION_HEADERS
50 #include "opt_snd.h"
51 #endif
52 
53 #include <dev/sound/midi/midi.h>
54 #include <dev/sound/midi/midiq.h>
55 
56 #include "mpu_if.h"
57 
58 MALLOC_DEFINE(M_MIDI, "midi buffers", "Midi data allocation area");
59 
60 #define MIDI_NAMELEN   16
61 struct snd_midi {
62 	KOBJ_FIELDS;
63 	struct mtx lock;		/* Protects all but queues */
64 	void   *cookie;
65 
66 	int	unit;			/* Should only be used in midistat */
67 	int	channel;		/* Should only be used in midistat */
68 
69 	int	busy;
70 	int	flags;			/* File flags */
71 	char	name[MIDI_NAMELEN];
72 	struct mtx qlock;		/* Protects inq, outq and flags */
73 	MIDIQ_HEAD(, char) inq, outq;
74 	int	rchan, wchan;
75 	struct selinfo rsel, wsel;
76 	int	hiwat;			/* QLEN(outq)>High-water -> disable
77 					 * writes from userland */
78 	struct cdev *dev;
79 	TAILQ_ENTRY(snd_midi) link;
80 };
81 
82 TAILQ_HEAD(, snd_midi) midi_devs;
83 
84 struct sx mstat_lock;
85 
86 static d_open_t midi_open;
87 static d_close_t midi_close;
88 static d_ioctl_t midi_ioctl;
89 static d_read_t midi_read;
90 static d_write_t midi_write;
91 static d_poll_t midi_poll;
92 
93 static struct cdevsw midi_cdevsw = {
94 	.d_version = D_VERSION,
95 	.d_open = midi_open,
96 	.d_close = midi_close,
97 	.d_read = midi_read,
98 	.d_write = midi_write,
99 	.d_ioctl = midi_ioctl,
100 	.d_poll = midi_poll,
101 	.d_name = "rmidi",
102 };
103 
104 static int      midi_destroy(struct snd_midi *, int);
105 static int      midi_load(void);
106 static int      midi_unload(void);
107 
108 SYSCTL_NODE(_hw, OID_AUTO, midi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
109     "Midi driver");
110 
111 int             midi_debug;
112 /* XXX: should this be moved into debug.midi? */
113 SYSCTL_INT(_hw_midi, OID_AUTO, debug, CTLFLAG_RW, &midi_debug, 0, "");
114 
115 #define MIDI_DEBUG(l,a)	if(midi_debug>=l) a
116 
117 void
118 midistat_lock(void)
119 {
120 	sx_xlock(&mstat_lock);
121 }
122 
123 void
124 midistat_unlock(void)
125 {
126 	sx_xunlock(&mstat_lock);
127 }
128 
129 void
130 midistat_lockassert(void)
131 {
132 	sx_assert(&mstat_lock, SA_XLOCKED);
133 }
134 
135 /*
136  * Register a new rmidi device. cls midi_if interface unit == 0 means
137  * auto-assign new unit number unit != 0 already assigned a unit number, eg.
138  * not the first channel provided by this device. channel,	sub-unit
139  * cookie is passed back on MPU calls Typical device drivers will call with
140  * unit=0, channel=1..(number of channels) and cookie=soft_c and won't care
141  * what unit number is used.
142  *
143  * It is an error to call midi_init with an already used unit/channel combo.
144  */
145 struct snd_midi *
146 midi_init(kobj_class_t cls, int unit, int channel, void *cookie)
147 {
148 	struct snd_midi *m;
149 	int i;
150 	int inqsize, outqsize;
151 	uint8_t *buf;
152 
153 	MIDI_DEBUG(1, printf("midiinit: unit %d/%d.\n", unit, channel));
154 	midistat_lock();
155 	/*
156 	 * Protect against call with existing unit/channel or auto-allocate a
157 	 * new unit number.
158 	 */
159 	i = -1;
160 	TAILQ_FOREACH(m, &midi_devs, link) {
161 		mtx_lock(&m->lock);
162 		if (unit != 0) {
163 			if (m->unit == unit && m->channel == channel) {
164 				mtx_unlock(&m->lock);
165 				goto err0;
166 			}
167 		} else {
168 			/*
169 			 * Find a better unit number
170 			 */
171 			if (m->unit > i)
172 				i = m->unit;
173 		}
174 		mtx_unlock(&m->lock);
175 	}
176 
177 	if (unit == 0)
178 		unit = i + 1;
179 
180 	MIDI_DEBUG(1, printf("midiinit #2: unit %d/%d.\n", unit, channel));
181 	m = malloc(sizeof(*m), M_MIDI, M_WAITOK | M_ZERO);
182 	kobj_init((kobj_t)m, cls);
183 	inqsize = MPU_INQSIZE(m, cookie);
184 	outqsize = MPU_OUTQSIZE(m, cookie);
185 
186 	MIDI_DEBUG(1, printf("midiinit queues %d/%d.\n", inqsize, outqsize));
187 	if (!inqsize && !outqsize)
188 		goto err1;
189 
190 	mtx_init(&m->lock, "raw midi", NULL, 0);
191 	mtx_init(&m->qlock, "q raw midi", NULL, 0);
192 
193 	mtx_lock(&m->lock);
194 	mtx_lock(&m->qlock);
195 
196 	if (inqsize)
197 		buf = malloc(sizeof(uint8_t) * inqsize, M_MIDI, M_NOWAIT);
198 	else
199 		buf = NULL;
200 
201 	MIDIQ_INIT(m->inq, buf, inqsize);
202 
203 	if (outqsize)
204 		buf = malloc(sizeof(uint8_t) * outqsize, M_MIDI, M_NOWAIT);
205 	else
206 		buf = NULL;
207 	m->hiwat = outqsize / 2;
208 
209 	MIDIQ_INIT(m->outq, buf, outqsize);
210 
211 	if ((inqsize && !MIDIQ_BUF(m->inq)) ||
212 	    (outqsize && !MIDIQ_BUF(m->outq)))
213 		goto err2;
214 
215 	m->busy = 0;
216 	m->flags = 0;
217 	m->unit = unit;
218 	m->channel = channel;
219 	m->cookie = cookie;
220 
221 	if (MPU_INIT(m, cookie))
222 		goto err2;
223 
224 	mtx_unlock(&m->lock);
225 	mtx_unlock(&m->qlock);
226 
227 	TAILQ_INSERT_TAIL(&midi_devs, m, link);
228 
229 	midistat_unlock();
230 
231 	m->dev = make_dev(&midi_cdevsw, unit, UID_ROOT, GID_WHEEL, 0666,
232 	    "midi%d.%d", unit, channel);
233 	m->dev->si_drv1 = m;
234 
235 	return m;
236 
237 err2:
238 	mtx_destroy(&m->qlock);
239 	mtx_destroy(&m->lock);
240 
241 	if (MIDIQ_BUF(m->inq))
242 		free(MIDIQ_BUF(m->inq), M_MIDI);
243 	if (MIDIQ_BUF(m->outq))
244 		free(MIDIQ_BUF(m->outq), M_MIDI);
245 err1:
246 	free(m, M_MIDI);
247 err0:
248 	midistat_unlock();
249 	MIDI_DEBUG(1, printf("midi_init ended in error\n"));
250 	return NULL;
251 }
252 
253 /*
254  * midi_uninit does not call MIDI_UNINIT, as since this is the implementors
255  * entry point. midi_uninit if fact, does not send any methods. A call to
256  * midi_uninit is a defacto promise that you won't manipulate ch anymore
257  */
258 int
259 midi_uninit(struct snd_midi *m)
260 {
261 	int err;
262 
263 	err = EBUSY;
264 	midistat_lock();
265 	mtx_lock(&m->lock);
266 	if (m->busy) {
267 		if (!(m->rchan || m->wchan))
268 			goto err;
269 
270 		if (m->rchan) {
271 			wakeup(&m->rchan);
272 			m->rchan = 0;
273 		}
274 		if (m->wchan) {
275 			wakeup(&m->wchan);
276 			m->wchan = 0;
277 		}
278 	}
279 	err = midi_destroy(m, 0);
280 	if (!err)
281 		goto exit;
282 
283 err:
284 	mtx_unlock(&m->lock);
285 exit:
286 	midistat_unlock();
287 	return err;
288 }
289 
290 #ifdef notdef
291 static int midi_lengths[] = {2, 2, 2, 2, 1, 1, 2, 0};
292 
293 #endif					/* notdef */
294 /* Number of bytes in a MIDI command */
295 #define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7])
296 #define MIDI_ACK	0xfe
297 #define MIDI_IS_STATUS(d) ((d) >= 0x80)
298 #define MIDI_IS_COMMON(d) ((d) >= 0xf0)
299 
300 #define MIDI_SYSEX_START	0xF0
301 #define MIDI_SYSEX_END	    0xF7
302 
303 /*
304  * midi_in: process all data until the queue is full, then discards the rest.
305  * Since midi_in is a state machine, data discards can cause it to get out of
306  * whack.  Process as much as possible.  It calls, wakeup, selnotify and
307  * psignal at most once.
308  */
309 int
310 midi_in(struct snd_midi *m, uint8_t *buf, int size)
311 {
312 	int used;
313 
314 	MIDI_DEBUG(5, printf("midi_in: m=%p size=%d\n", m, size));
315 
316 /*
317  * XXX: locking flub
318  */
319 	if (!(m->flags & M_RX))
320 		return size;
321 
322 	used = 0;
323 
324 	mtx_lock(&m->qlock);
325 	MIDI_DEBUG(6, printf("midi_in: len %jd avail %jd\n",
326 	    (intmax_t)MIDIQ_LEN(m->inq),
327 	    (intmax_t)MIDIQ_AVAIL(m->inq)));
328 	if (MIDIQ_AVAIL(m->inq) > size) {
329 		used = size;
330 		MIDIQ_ENQ(m->inq, buf, size);
331 	} else {
332 		MIDI_DEBUG(4, printf("midi_in: Discarding data qu\n"));
333 		mtx_unlock(&m->qlock);
334 		return 0;
335 	}
336 	if (m->rchan) {
337 		wakeup(&m->rchan);
338 		m->rchan = 0;
339 	}
340 	selwakeup(&m->rsel);
341 	mtx_unlock(&m->qlock);
342 	return used;
343 }
344 
345 /*
346  * midi_out: The only clearer of the M_TXEN flag.
347  */
348 int
349 midi_out(struct snd_midi *m, uint8_t *buf, int size)
350 {
351 	int used;
352 
353 /*
354  * XXX: locking flub
355  */
356 	if (!(m->flags & M_TXEN))
357 		return 0;
358 
359 	MIDI_DEBUG(2, printf("midi_out: %p\n", m));
360 	mtx_lock(&m->qlock);
361 	used = MIN(size, MIDIQ_LEN(m->outq));
362 	MIDI_DEBUG(3, printf("midi_out: used %d\n", used));
363 	if (used)
364 		MIDIQ_DEQ(m->outq, buf, used);
365 	if (MIDIQ_EMPTY(m->outq)) {
366 		m->flags &= ~M_TXEN;
367 		MPU_CALLBACKP(m, m->cookie, m->flags);
368 	}
369 	if (used && MIDIQ_AVAIL(m->outq) > m->hiwat) {
370 		if (m->wchan) {
371 			wakeup(&m->wchan);
372 			m->wchan = 0;
373 		}
374 		selwakeup(&m->wsel);
375 	}
376 	mtx_unlock(&m->qlock);
377 	return used;
378 }
379 
380 int
381 midi_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
382 {
383 	struct snd_midi *m = i_dev->si_drv1;
384 	int retval;
385 
386 	MIDI_DEBUG(1, printf("midiopen %p %s %s\n", td,
387 	    flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
388 	if (m == NULL)
389 		return ENXIO;
390 
391 	mtx_lock(&m->lock);
392 	mtx_lock(&m->qlock);
393 
394 	retval = 0;
395 
396 	if (flags & FREAD) {
397 		if (MIDIQ_SIZE(m->inq) == 0)
398 			retval = ENXIO;
399 		else if (m->flags & M_RX)
400 			retval = EBUSY;
401 		if (retval)
402 			goto err;
403 	}
404 	if (flags & FWRITE) {
405 		if (MIDIQ_SIZE(m->outq) == 0)
406 			retval = ENXIO;
407 		else if (m->flags & M_TX)
408 			retval = EBUSY;
409 		if (retval)
410 			goto err;
411 	}
412 	m->busy++;
413 
414 	m->rchan = 0;
415 	m->wchan = 0;
416 
417 	if (flags & FREAD) {
418 		m->flags |= M_RX | M_RXEN;
419 		/*
420 	         * Only clear the inq, the outq might still have data to drain
421 	         * from a previous session
422 	         */
423 		MIDIQ_CLEAR(m->inq);
424 	}
425 
426 	if (flags & FWRITE)
427 		m->flags |= M_TX;
428 
429 	MPU_CALLBACK(m, m->cookie, m->flags);
430 
431 	MIDI_DEBUG(2, printf("midi_open: opened.\n"));
432 
433 err:	mtx_unlock(&m->qlock);
434 	mtx_unlock(&m->lock);
435 	return retval;
436 }
437 
438 int
439 midi_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
440 {
441 	struct snd_midi *m = i_dev->si_drv1;
442 	int retval;
443 	int oldflags;
444 
445 	MIDI_DEBUG(1, printf("midi_close %p %s %s\n", td,
446 	    flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
447 
448 	if (m == NULL)
449 		return ENXIO;
450 
451 	mtx_lock(&m->lock);
452 	mtx_lock(&m->qlock);
453 
454 	if ((flags & FREAD && !(m->flags & M_RX)) ||
455 	    (flags & FWRITE && !(m->flags & M_TX))) {
456 		retval = ENXIO;
457 		goto err;
458 	}
459 	m->busy--;
460 
461 	oldflags = m->flags;
462 
463 	if (flags & FREAD)
464 		m->flags &= ~(M_RX | M_RXEN);
465 	if (flags & FWRITE)
466 		m->flags &= ~M_TX;
467 
468 	if ((m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN)))
469 		MPU_CALLBACK(m, m->cookie, m->flags);
470 
471 	MIDI_DEBUG(1, printf("midi_close: closed, busy = %d.\n", m->busy));
472 
473 	mtx_unlock(&m->qlock);
474 	mtx_unlock(&m->lock);
475 	retval = 0;
476 err:	return retval;
477 }
478 
479 /*
480  * TODO: midi_read, per oss programmer's guide pg. 42 should return as soon
481  * as data is available.
482  */
483 int
484 midi_read(struct cdev *i_dev, struct uio *uio, int ioflag)
485 {
486 #define MIDI_RSIZE 32
487 	struct snd_midi *m = i_dev->si_drv1;
488 	int retval;
489 	int used;
490 	char buf[MIDI_RSIZE];
491 
492 	MIDI_DEBUG(5, printf("midiread: count=%lu\n",
493 	    (unsigned long)uio->uio_resid));
494 
495 	retval = EIO;
496 
497 	if (m == NULL)
498 		goto err0;
499 
500 	mtx_lock(&m->lock);
501 	mtx_lock(&m->qlock);
502 
503 	if (!(m->flags & M_RX))
504 		goto err1;
505 
506 	while (uio->uio_resid > 0) {
507 		while (MIDIQ_EMPTY(m->inq)) {
508 			retval = EWOULDBLOCK;
509 			if (ioflag & O_NONBLOCK)
510 				goto err1;
511 			mtx_unlock(&m->lock);
512 			m->rchan = 1;
513 			retval = msleep(&m->rchan, &m->qlock,
514 			    PCATCH | PDROP, "midi RX", 0);
515 			/*
516 			 * We slept, maybe things have changed since last
517 			 * dying check
518 			 */
519 			if (retval == EINTR)
520 				goto err0;
521 			if (m != i_dev->si_drv1)
522 				retval = ENXIO;
523 			/* if (retval && retval != ERESTART) */
524 			if (retval)
525 				goto err0;
526 			mtx_lock(&m->lock);
527 			mtx_lock(&m->qlock);
528 			m->rchan = 0;
529 			if (!m->busy)
530 				goto err1;
531 		}
532 		MIDI_DEBUG(6, printf("midi_read start\n"));
533 		/*
534 	         * At this point, it is certain that m->inq has data
535 	         */
536 
537 		used = MIN(MIDIQ_LEN(m->inq), uio->uio_resid);
538 		used = MIN(used, MIDI_RSIZE);
539 
540 		MIDI_DEBUG(6, printf("midiread: uiomove cc=%d\n", used));
541 		MIDIQ_DEQ(m->inq, buf, used);
542 		retval = uiomove(buf, used, uio);
543 		if (retval)
544 			goto err1;
545 	}
546 
547 	/*
548 	 * If we Made it here then transfer is good
549 	 */
550 	retval = 0;
551 err1:	mtx_unlock(&m->qlock);
552 	mtx_unlock(&m->lock);
553 err0:	MIDI_DEBUG(4, printf("midi_read: ret %d\n", retval));
554 	return retval;
555 }
556 
557 /*
558  * midi_write: The only setter of M_TXEN
559  */
560 
561 int
562 midi_write(struct cdev *i_dev, struct uio *uio, int ioflag)
563 {
564 #define MIDI_WSIZE 32
565 	struct snd_midi *m = i_dev->si_drv1;
566 	int retval;
567 	int used;
568 	char buf[MIDI_WSIZE];
569 
570 	MIDI_DEBUG(4, printf("midi_write\n"));
571 	retval = 0;
572 	if (m == NULL)
573 		goto err0;
574 
575 	mtx_lock(&m->lock);
576 	mtx_lock(&m->qlock);
577 
578 	if (!(m->flags & M_TX))
579 		goto err1;
580 
581 	while (uio->uio_resid > 0) {
582 		while (MIDIQ_AVAIL(m->outq) == 0) {
583 			retval = EWOULDBLOCK;
584 			if (ioflag & O_NONBLOCK)
585 				goto err1;
586 			mtx_unlock(&m->lock);
587 			m->wchan = 1;
588 			MIDI_DEBUG(3, printf("midi_write msleep\n"));
589 			retval = msleep(&m->wchan, &m->qlock,
590 			    PCATCH | PDROP, "midi TX", 0);
591 			/*
592 			 * We slept, maybe things have changed since last
593 			 * dying check
594 			 */
595 			if (retval == EINTR)
596 				goto err0;
597 			if (m != i_dev->si_drv1)
598 				retval = ENXIO;
599 			if (retval)
600 				goto err0;
601 			mtx_lock(&m->lock);
602 			mtx_lock(&m->qlock);
603 			m->wchan = 0;
604 			if (!m->busy)
605 				goto err1;
606 		}
607 
608 		/*
609 	         * We are certain than data can be placed on the queue
610 	         */
611 
612 		used = MIN(MIDIQ_AVAIL(m->outq), uio->uio_resid);
613 		used = MIN(used, MIDI_WSIZE);
614 		MIDI_DEBUG(5, printf("midiout: resid %zd len %jd avail %jd\n",
615 		    uio->uio_resid, (intmax_t)MIDIQ_LEN(m->outq),
616 		    (intmax_t)MIDIQ_AVAIL(m->outq)));
617 
618 		MIDI_DEBUG(5, printf("midi_write: uiomove cc=%d\n", used));
619 		retval = uiomove(buf, used, uio);
620 		if (retval)
621 			goto err1;
622 		MIDIQ_ENQ(m->outq, buf, used);
623 		/*
624 	         * Inform the bottom half that data can be written
625 	         */
626 		if (!(m->flags & M_TXEN)) {
627 			m->flags |= M_TXEN;
628 			MPU_CALLBACK(m, m->cookie, m->flags);
629 		}
630 	}
631 	/*
632 	 * If we Made it here then transfer is good
633 	 */
634 	retval = 0;
635 err1:	mtx_unlock(&m->qlock);
636 	mtx_unlock(&m->lock);
637 err0:	return retval;
638 }
639 
640 int
641 midi_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
642     struct thread *td)
643 {
644 	return ENXIO;
645 }
646 
647 int
648 midi_poll(struct cdev *i_dev, int events, struct thread *td)
649 {
650 	struct snd_midi *m = i_dev->si_drv1;
651 	int revents;
652 
653 	if (m == NULL)
654 		return 0;
655 
656 	revents = 0;
657 
658 	mtx_lock(&m->lock);
659 	mtx_lock(&m->qlock);
660 
661 	if (events & (POLLIN | POLLRDNORM))
662 		if (!MIDIQ_EMPTY(m->inq))
663 			events |= events & (POLLIN | POLLRDNORM);
664 
665 	if (events & (POLLOUT | POLLWRNORM))
666 		if (MIDIQ_AVAIL(m->outq) < m->hiwat)
667 			events |= events & (POLLOUT | POLLWRNORM);
668 
669 	if (revents == 0) {
670 		if (events & (POLLIN | POLLRDNORM))
671 			selrecord(td, &m->rsel);
672 
673 		if (events & (POLLOUT | POLLWRNORM))
674 			selrecord(td, &m->wsel);
675 	}
676 	mtx_unlock(&m->lock);
677 	mtx_unlock(&m->qlock);
678 
679 	return (revents);
680 }
681 
682 /*
683  * Single point of midi destructions.
684  */
685 static int
686 midi_destroy(struct snd_midi *m, int midiuninit)
687 {
688 	midistat_lockassert();
689 	mtx_assert(&m->lock, MA_OWNED);
690 
691 	MIDI_DEBUG(3, printf("midi_destroy\n"));
692 	m->dev->si_drv1 = NULL;
693 	mtx_unlock(&m->lock);	/* XXX */
694 	destroy_dev(m->dev);
695 	TAILQ_REMOVE(&midi_devs, m, link);
696 	if (midiuninit)
697 		MPU_UNINIT(m, m->cookie);
698 	free(MIDIQ_BUF(m->inq), M_MIDI);
699 	free(MIDIQ_BUF(m->outq), M_MIDI);
700 	mtx_destroy(&m->qlock);
701 	mtx_destroy(&m->lock);
702 	free(m, M_MIDI);
703 	return 0;
704 }
705 
706 static int
707 midi_load(void)
708 {
709 	sx_init(&mstat_lock, "midistat lock");
710 	TAILQ_INIT(&midi_devs);
711 
712 	return 0;
713 }
714 
715 static int
716 midi_unload(void)
717 {
718 	struct snd_midi *m, *tmp;
719 	int retval;
720 
721 	MIDI_DEBUG(1, printf("midi_unload()\n"));
722 	retval = EBUSY;
723 	midistat_lock();
724 	TAILQ_FOREACH_SAFE(m, &midi_devs, link, tmp) {
725 		mtx_lock(&m->lock);
726 		if (m->busy)
727 			retval = EBUSY;
728 		else
729 			retval = midi_destroy(m, 1);
730 		if (retval)
731 			goto exit;
732 	}
733 	midistat_unlock();
734 
735 	sx_destroy(&mstat_lock);
736 	return 0;
737 
738 exit:
739 	mtx_unlock(&m->lock);
740 	midistat_unlock();
741 	if (retval)
742 		MIDI_DEBUG(2, printf("midi_unload: failed\n"));
743 	return retval;
744 }
745 
746 static int
747 midi_modevent(module_t mod, int type, void *data)
748 {
749 	int retval;
750 
751 	retval = 0;
752 
753 	switch (type) {
754 	case MOD_LOAD:
755 		retval = midi_load();
756 		break;
757 
758 	case MOD_UNLOAD:
759 		retval = midi_unload();
760 		break;
761 
762 	default:
763 		break;
764 	}
765 
766 	return retval;
767 }
768 
769 DEV_MODULE(midi, midi_modevent, NULL);
770 MODULE_VERSION(midi, 1);
771