midi.c revision 359161
1/*-
2 * Copyright (c) 2003 Mathew Kanner
3 * Copyright (c) 1998 The NetBSD Foundation, Inc.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Lennart Augustsson (augustss@netbsd.org).
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /*
32  * Parts of this file started out as NetBSD: midi.c 1.31
33  * They are mostly gone.  Still the most obvious will be the state
34  * machine midi_in
35  */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: stable/10/sys/dev/sound/midi/midi.c 359161 2020-03-20 14:26:08Z avatar $");
39
40#include <sys/param.h>
41#include <sys/queue.h>
42#include <sys/kernel.h>
43#include <sys/lock.h>
44#include <sys/mutex.h>
45#include <sys/proc.h>
46#include <sys/signalvar.h>
47#include <sys/conf.h>
48#include <sys/selinfo.h>
49#include <sys/sysctl.h>
50#include <sys/types.h>
51#include <sys/malloc.h>
52#include <sys/param.h>
53#include <sys/systm.h>
54#include <sys/proc.h>
55#include <sys/fcntl.h>
56#include <sys/types.h>
57#include <sys/uio.h>
58#include <sys/poll.h>
59#include <sys/sbuf.h>
60#include <sys/kobj.h>
61#include <sys/module.h>
62
63#ifdef HAVE_KERNEL_OPTION_HEADERS
64#include "opt_snd.h"
65#endif
66
67#include <dev/sound/midi/midi.h>
68#include "mpu_if.h"
69
70#include <dev/sound/midi/midiq.h>
71#include "synth_if.h"
72MALLOC_DEFINE(M_MIDI, "midi buffers", "Midi data allocation area");
73
74#ifndef KOBJMETHOD_END
75#define KOBJMETHOD_END	{ NULL, NULL }
76#endif
77
78#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
79#define MIDIMKMINOR(u, d, c) PCMMKMINOR(u, d, c)
80
81#define MIDI_DEV_RAW	2
82#define MIDI_DEV_MIDICTL 12
83
84enum midi_states {
85	MIDI_IN_START, MIDI_IN_SYSEX, MIDI_IN_DATA
86};
87
88/*
89 * The MPU interface current has init() uninit() inqsize() outqsize()
90 * callback() : fiddle with the tx|rx status.
91 */
92
93#include "mpu_if.h"
94
95/*
96 * /dev/rmidi	Structure definitions
97 */
98
99#define MIDI_NAMELEN   16
100struct snd_midi {
101	KOBJ_FIELDS;
102	struct mtx lock;		/* Protects all but queues */
103	void   *cookie;
104
105	int	unit;			/* Should only be used in midistat */
106	int	channel;		/* Should only be used in midistat */
107
108	int	busy;
109	int	flags;			/* File flags */
110	char	name[MIDI_NAMELEN];
111	struct mtx qlock;		/* Protects inq, outq and flags */
112	MIDIQ_HEAD(, char) inq, outq;
113	int	rchan, wchan;
114	struct selinfo rsel, wsel;
115	int	hiwat;			/* QLEN(outq)>High-water -> disable
116					 * writes from userland */
117	enum midi_states inq_state;
118	int	inq_status, inq_left;	/* Variables for the state machine in
119					 * Midi_in, this is to provide that
120					 * signals only get issued only
121					 * complete command packets. */
122	struct proc *async;
123	struct cdev *dev;
124	struct synth_midi *synth;
125	int	synth_flags;
126	TAILQ_ENTRY(snd_midi) link;
127};
128
129struct synth_midi {
130	KOBJ_FIELDS;
131	struct snd_midi *m;
132};
133
134static synth_open_t midisynth_open;
135static synth_close_t midisynth_close;
136static synth_writeraw_t midisynth_writeraw;
137static synth_killnote_t midisynth_killnote;
138static synth_startnote_t midisynth_startnote;
139static synth_setinstr_t midisynth_setinstr;
140static synth_alloc_t midisynth_alloc;
141static synth_controller_t midisynth_controller;
142static synth_bender_t midisynth_bender;
143
144
145static kobj_method_t midisynth_methods[] = {
146	KOBJMETHOD(synth_open, midisynth_open),
147	KOBJMETHOD(synth_close, midisynth_close),
148	KOBJMETHOD(synth_writeraw, midisynth_writeraw),
149	KOBJMETHOD(synth_setinstr, midisynth_setinstr),
150	KOBJMETHOD(synth_startnote, midisynth_startnote),
151	KOBJMETHOD(synth_killnote, midisynth_killnote),
152	KOBJMETHOD(synth_alloc, midisynth_alloc),
153	KOBJMETHOD(synth_controller, midisynth_controller),
154	KOBJMETHOD(synth_bender, midisynth_bender),
155	KOBJMETHOD_END
156};
157
158DEFINE_CLASS(midisynth, midisynth_methods, 0);
159
160/*
161 * Module Exports & Interface
162 *
163 * struct midi_chan *midi_init(MPU_CLASS cls, int unit, int chan,
164 *     void *cookie)
165 * int midi_uninit(struct snd_midi *)
166 *
167 * 0 == no error
168 * EBUSY or other error
169 *
170 * int midi_in(struct snd_midi *, char *buf, int count)
171 * int midi_out(struct snd_midi *, char *buf, int count)
172 *
173 * midi_{in,out} return actual size transfered
174 *
175 */
176
177
178/*
179 * midi_devs tailq, holder of all rmidi instances protected by midistat_lock
180 */
181
182TAILQ_HEAD(, snd_midi) midi_devs;
183
184/*
185 * /dev/midistat variables and declarations, protected by midistat_lock
186 */
187
188static struct mtx midistat_lock;
189static int      midistat_isopen = 0;
190static struct sbuf midistat_sbuf;
191static int      midistat_bufptr;
192static struct cdev *midistat_dev;
193
194/*
195 * /dev/midistat	dev_t declarations
196 */
197
198static d_open_t midistat_open;
199static d_close_t midistat_close;
200static d_read_t midistat_read;
201
202static struct cdevsw midistat_cdevsw = {
203	.d_version = D_VERSION,
204	.d_open = midistat_open,
205	.d_close = midistat_close,
206	.d_read = midistat_read,
207	.d_name = "midistat",
208};
209
210
211/*
212 * /dev/rmidi dev_t declarations, struct variable access is protected by
213 * locks contained within the structure.
214 */
215
216static d_open_t midi_open;
217static d_close_t midi_close;
218static d_ioctl_t midi_ioctl;
219static d_read_t midi_read;
220static d_write_t midi_write;
221static d_poll_t midi_poll;
222
223static struct cdevsw midi_cdevsw = {
224	.d_version = D_VERSION,
225	.d_open = midi_open,
226	.d_close = midi_close,
227	.d_read = midi_read,
228	.d_write = midi_write,
229	.d_ioctl = midi_ioctl,
230	.d_poll = midi_poll,
231	.d_name = "rmidi",
232};
233
234/*
235 * Prototypes of library functions
236 */
237
238static int      midi_destroy(struct snd_midi *, int);
239static int      midistat_prepare(struct sbuf * s);
240static int      midi_load(void);
241static int      midi_unload(void);
242
243/*
244 * Misc declr.
245 */
246SYSCTL_NODE(_hw, OID_AUTO, midi, CTLFLAG_RD, 0, "Midi driver");
247static SYSCTL_NODE(_hw_midi, OID_AUTO, stat, CTLFLAG_RD, 0, "Status device");
248
249int             midi_debug;
250/* XXX: should this be moved into debug.midi? */
251SYSCTL_INT(_hw_midi, OID_AUTO, debug, CTLFLAG_RW, &midi_debug, 0, "");
252
253int             midi_dumpraw;
254SYSCTL_INT(_hw_midi, OID_AUTO, dumpraw, CTLFLAG_RW, &midi_dumpraw, 0, "");
255
256int             midi_instroff;
257SYSCTL_INT(_hw_midi, OID_AUTO, instroff, CTLFLAG_RW, &midi_instroff, 0, "");
258
259int             midistat_verbose;
260SYSCTL_INT(_hw_midi_stat, OID_AUTO, verbose, CTLFLAG_RW,
261	&midistat_verbose, 0, "");
262
263#define MIDI_DEBUG(l,a)	if(midi_debug>=l) a
264/*
265 * CODE START
266 */
267
268/*
269 * Register a new rmidi device. cls midi_if interface unit == 0 means
270 * auto-assign new unit number unit != 0 already assigned a unit number, eg.
271 * not the first channel provided by this device. channel,	sub-unit
272 * cookie is passed back on MPU calls Typical device drivers will call with
273 * unit=0, channel=1..(number of channels) and cookie=soft_c and won't care
274 * what unit number is used.
275 *
276 * It is an error to call midi_init with an already used unit/channel combo.
277 *
278 * Returns NULL on error
279 *
280 */
281struct snd_midi *
282midi_init(kobj_class_t cls, int unit, int channel, void *cookie)
283{
284	struct snd_midi *m;
285	int i;
286	int inqsize, outqsize;
287	MIDI_TYPE *buf;
288
289	MIDI_DEBUG(1, printf("midiinit: unit %d/%d.\n", unit, channel));
290	mtx_lock(&midistat_lock);
291	/*
292	 * Protect against call with existing unit/channel or auto-allocate a
293	 * new unit number.
294	 */
295	i = -1;
296	TAILQ_FOREACH(m, &midi_devs, link) {
297		mtx_lock(&m->lock);
298		if (unit != 0) {
299			if (m->unit == unit && m->channel == channel) {
300				mtx_unlock(&m->lock);
301				goto err0;
302			}
303		} else {
304			/*
305			 * Find a better unit number
306			 */
307			if (m->unit > i)
308				i = m->unit;
309		}
310		mtx_unlock(&m->lock);
311	}
312
313	if (unit == 0)
314		unit = i + 1;
315
316	MIDI_DEBUG(1, printf("midiinit #2: unit %d/%d.\n", unit, channel));
317	m = malloc(sizeof(*m), M_MIDI, M_NOWAIT | M_ZERO);
318	if (m == NULL)
319		goto err0;
320
321	m->synth = malloc(sizeof(*m->synth), M_MIDI, M_NOWAIT | M_ZERO);
322	if (m->synth == NULL)
323		goto err1;
324	kobj_init((kobj_t)m->synth, &midisynth_class);
325	m->synth->m = m;
326	kobj_init((kobj_t)m, cls);
327	inqsize = MPU_INQSIZE(m, cookie);
328	outqsize = MPU_OUTQSIZE(m, cookie);
329
330	MIDI_DEBUG(1, printf("midiinit queues %d/%d.\n", inqsize, outqsize));
331	if (!inqsize && !outqsize)
332		goto err2;
333
334	mtx_init(&m->lock, "raw midi", NULL, 0);
335	mtx_init(&m->qlock, "q raw midi", NULL, 0);
336
337	mtx_lock(&m->lock);
338	mtx_lock(&m->qlock);
339
340	if (inqsize)
341		buf = malloc(sizeof(MIDI_TYPE) * inqsize, M_MIDI, M_NOWAIT);
342	else
343		buf = NULL;
344
345	MIDIQ_INIT(m->inq, buf, inqsize);
346
347	if (outqsize)
348		buf = malloc(sizeof(MIDI_TYPE) * outqsize, M_MIDI, M_NOWAIT);
349	else
350		buf = NULL;
351	m->hiwat = outqsize / 2;
352
353	MIDIQ_INIT(m->outq, buf, outqsize);
354
355	if ((inqsize && !MIDIQ_BUF(m->inq)) ||
356	    (outqsize && !MIDIQ_BUF(m->outq)))
357		goto err3;
358
359
360	m->busy = 0;
361	m->flags = 0;
362	m->unit = unit;
363	m->channel = channel;
364	m->cookie = cookie;
365
366	if (MPU_INIT(m, cookie))
367		goto err3;
368
369	mtx_unlock(&m->lock);
370	mtx_unlock(&m->qlock);
371
372	TAILQ_INSERT_TAIL(&midi_devs, m, link);
373
374	mtx_unlock(&midistat_lock);
375
376	m->dev = make_dev(&midi_cdevsw,
377	    MIDIMKMINOR(unit, MIDI_DEV_RAW, channel),
378	    UID_ROOT, GID_WHEEL, 0666, "midi%d.%d", unit, channel);
379	m->dev->si_drv1 = m;
380
381	return m;
382
383err3:	mtx_destroy(&m->qlock);
384	mtx_destroy(&m->lock);
385
386	if (MIDIQ_BUF(m->inq))
387		free(MIDIQ_BUF(m->inq), M_MIDI);
388	if (MIDIQ_BUF(m->outq))
389		free(MIDIQ_BUF(m->outq), M_MIDI);
390err2:	free(m->synth, M_MIDI);
391err1:	free(m, M_MIDI);
392err0:	mtx_unlock(&midistat_lock);
393	MIDI_DEBUG(1, printf("midi_init ended in error\n"));
394	return NULL;
395}
396
397/*
398 * midi_uninit does not call MIDI_UNINIT, as since this is the implementors
399 * entry point. midi_uninit if fact, does not send any methods. A call to
400 * midi_uninit is a defacto promise that you won't manipulate ch anymore
401 *
402 */
403
404int
405midi_uninit(struct snd_midi *m)
406{
407	int err;
408
409	err = EBUSY;
410	mtx_lock(&midistat_lock);
411	mtx_lock(&m->lock);
412	if (m->busy) {
413		if (!(m->rchan || m->wchan))
414			goto err;
415
416		if (m->rchan) {
417			wakeup(&m->rchan);
418			m->rchan = 0;
419		}
420		if (m->wchan) {
421			wakeup(&m->wchan);
422			m->wchan = 0;
423		}
424	}
425	err = midi_destroy(m, 0);
426	if (!err)
427		goto exit;
428
429err:	mtx_unlock(&m->lock);
430exit:	mtx_unlock(&midistat_lock);
431	return err;
432}
433
434/*
435 * midi_in: process all data until the queue is full, then discards the rest.
436 * Since midi_in is a state machine, data discards can cause it to get out of
437 * whack.  Process as much as possible.  It calls, wakeup, selnotify and
438 * psignal at most once.
439 */
440
441#ifdef notdef
442static int midi_lengths[] = {2, 2, 2, 2, 1, 1, 2, 0};
443
444#endif					/* notdef */
445/* Number of bytes in a MIDI command */
446#define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7])
447#define MIDI_ACK	0xfe
448#define MIDI_IS_STATUS(d) ((d) >= 0x80)
449#define MIDI_IS_COMMON(d) ((d) >= 0xf0)
450
451#define MIDI_SYSEX_START	0xF0
452#define MIDI_SYSEX_END	    0xF7
453
454
455int
456midi_in(struct snd_midi *m, MIDI_TYPE *buf, int size)
457{
458	/* int             i, sig, enq; */
459	int used;
460
461	/* MIDI_TYPE       data; */
462	MIDI_DEBUG(5, printf("midi_in: m=%p size=%d\n", m, size));
463
464/*
465 * XXX: locking flub
466 */
467	if (!(m->flags & M_RX))
468		return size;
469
470	used = 0;
471
472	mtx_lock(&m->qlock);
473#if 0
474	/*
475	 * Don't bother queuing if not in read mode.  Discard everything and
476	 * return size so the caller doesn't freak out.
477	 */
478
479	if (!(m->flags & M_RX))
480		return size;
481
482	for (i = sig = 0; i < size; i++) {
483
484		data = buf[i];
485		enq = 0;
486		if (data == MIDI_ACK)
487			continue;
488
489		switch (m->inq_state) {
490		case MIDI_IN_START:
491			if (MIDI_IS_STATUS(data)) {
492				switch (data) {
493				case 0xf0:	/* Sysex */
494					m->inq_state = MIDI_IN_SYSEX;
495					break;
496				case 0xf1:	/* MTC quarter frame */
497				case 0xf3:	/* Song select */
498					m->inq_state = MIDI_IN_DATA;
499					enq = 1;
500					m->inq_left = 1;
501					break;
502				case 0xf2:	/* Song position pointer */
503					m->inq_state = MIDI_IN_DATA;
504					enq = 1;
505					m->inq_left = 2;
506					break;
507				default:
508					if (MIDI_IS_COMMON(data)) {
509						enq = 1;
510						sig = 1;
511					} else {
512						m->inq_state = MIDI_IN_DATA;
513						enq = 1;
514						m->inq_status = data;
515						m->inq_left = MIDI_LENGTH(data);
516					}
517					break;
518				}
519			} else if (MIDI_IS_STATUS(m->inq_status)) {
520				m->inq_state = MIDI_IN_DATA;
521				if (!MIDIQ_FULL(m->inq)) {
522					used++;
523					MIDIQ_ENQ(m->inq, &m->inq_status, 1);
524				}
525				enq = 1;
526				m->inq_left = MIDI_LENGTH(m->inq_status) - 1;
527			}
528			break;
529			/*
530			 * End of case MIDI_IN_START:
531			 */
532
533		case MIDI_IN_DATA:
534			enq = 1;
535			if (--m->inq_left <= 0)
536				sig = 1;/* deliver data */
537			break;
538		case MIDI_IN_SYSEX:
539			if (data == MIDI_SYSEX_END)
540				m->inq_state = MIDI_IN_START;
541			break;
542		}
543
544		if (enq)
545			if (!MIDIQ_FULL(m->inq)) {
546				MIDIQ_ENQ(m->inq, &data, 1);
547				used++;
548			}
549		/*
550	         * End of the state machines main "for loop"
551	         */
552	}
553	if (sig) {
554#endif
555		MIDI_DEBUG(6, printf("midi_in: len %jd avail %jd\n",
556		    (intmax_t)MIDIQ_LEN(m->inq),
557		    (intmax_t)MIDIQ_AVAIL(m->inq)));
558		if (MIDIQ_AVAIL(m->inq) > size) {
559			used = size;
560			MIDIQ_ENQ(m->inq, buf, size);
561		} else {
562			MIDI_DEBUG(4, printf("midi_in: Discarding data qu\n"));
563			mtx_unlock(&m->qlock);
564			return 0;
565		}
566		if (m->rchan) {
567			wakeup(&m->rchan);
568			m->rchan = 0;
569		}
570		selwakeup(&m->rsel);
571		if (m->async) {
572			PROC_LOCK(m->async);
573			kern_psignal(m->async, SIGIO);
574			PROC_UNLOCK(m->async);
575		}
576#if 0
577	}
578#endif
579	mtx_unlock(&m->qlock);
580	return used;
581}
582
583/*
584 * midi_out: The only clearer of the M_TXEN flag.
585 */
586int
587midi_out(struct snd_midi *m, MIDI_TYPE *buf, int size)
588{
589	int used;
590
591/*
592 * XXX: locking flub
593 */
594	if (!(m->flags & M_TXEN))
595		return 0;
596
597	MIDI_DEBUG(2, printf("midi_out: %p\n", m));
598	mtx_lock(&m->qlock);
599	used = MIN(size, MIDIQ_LEN(m->outq));
600	MIDI_DEBUG(3, printf("midi_out: used %d\n", used));
601	if (used)
602		MIDIQ_DEQ(m->outq, buf, used);
603	if (MIDIQ_EMPTY(m->outq)) {
604		m->flags &= ~M_TXEN;
605		MPU_CALLBACKP(m, m->cookie, m->flags);
606	}
607	if (used && MIDIQ_AVAIL(m->outq) > m->hiwat) {
608		if (m->wchan) {
609			wakeup(&m->wchan);
610			m->wchan = 0;
611		}
612		selwakeup(&m->wsel);
613		if (m->async) {
614			PROC_LOCK(m->async);
615			kern_psignal(m->async, SIGIO);
616			PROC_UNLOCK(m->async);
617		}
618	}
619	mtx_unlock(&m->qlock);
620	return used;
621}
622
623
624/*
625 * /dev/rmidi#.#	device access functions
626 */
627int
628midi_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
629{
630	struct snd_midi *m = i_dev->si_drv1;
631	int retval;
632
633	MIDI_DEBUG(1, printf("midiopen %p %s %s\n", td,
634	    flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
635	if (m == NULL)
636		return ENXIO;
637
638	mtx_lock(&m->lock);
639	mtx_lock(&m->qlock);
640
641	retval = 0;
642
643	if (flags & FREAD) {
644		if (MIDIQ_SIZE(m->inq) == 0)
645			retval = ENXIO;
646		else if (m->flags & M_RX)
647			retval = EBUSY;
648		if (retval)
649			goto err;
650	}
651	if (flags & FWRITE) {
652		if (MIDIQ_SIZE(m->outq) == 0)
653			retval = ENXIO;
654		else if (m->flags & M_TX)
655			retval = EBUSY;
656		if (retval)
657			goto err;
658	}
659	m->busy++;
660
661	m->rchan = 0;
662	m->wchan = 0;
663	m->async = 0;
664
665	if (flags & FREAD) {
666		m->flags |= M_RX | M_RXEN;
667		/*
668	         * Only clear the inq, the outq might still have data to drain
669	         * from a previous session
670	         */
671		MIDIQ_CLEAR(m->inq);
672	};
673
674	if (flags & FWRITE)
675		m->flags |= M_TX;
676
677	MPU_CALLBACK(m, m->cookie, m->flags);
678
679	MIDI_DEBUG(2, printf("midi_open: opened.\n"));
680
681err:	mtx_unlock(&m->qlock);
682	mtx_unlock(&m->lock);
683	return retval;
684}
685
686int
687midi_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
688{
689	struct snd_midi *m = i_dev->si_drv1;
690	int retval;
691	int oldflags;
692
693	MIDI_DEBUG(1, printf("midi_close %p %s %s\n", td,
694	    flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
695
696	if (m == NULL)
697		return ENXIO;
698
699	mtx_lock(&m->lock);
700	mtx_lock(&m->qlock);
701
702	if ((flags & FREAD && !(m->flags & M_RX)) ||
703	    (flags & FWRITE && !(m->flags & M_TX))) {
704		retval = ENXIO;
705		goto err;
706	}
707	m->busy--;
708
709	oldflags = m->flags;
710
711	if (flags & FREAD)
712		m->flags &= ~(M_RX | M_RXEN);
713	if (flags & FWRITE)
714		m->flags &= ~M_TX;
715
716	if ((m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN)))
717		MPU_CALLBACK(m, m->cookie, m->flags);
718
719	MIDI_DEBUG(1, printf("midi_close: closed, busy = %d.\n", m->busy));
720
721	mtx_unlock(&m->qlock);
722	mtx_unlock(&m->lock);
723	retval = 0;
724err:	return retval;
725}
726
727/*
728 * TODO: midi_read, per oss programmer's guide pg. 42 should return as soon
729 * as data is available.
730 */
731int
732midi_read(struct cdev *i_dev, struct uio *uio, int ioflag)
733{
734#define MIDI_RSIZE 32
735	struct snd_midi *m = i_dev->si_drv1;
736	int retval;
737	int used;
738	char buf[MIDI_RSIZE];
739
740	MIDI_DEBUG(5, printf("midiread: count=%lu\n",
741	    (unsigned long)uio->uio_resid));
742
743	retval = EIO;
744
745	if (m == NULL)
746		goto err0;
747
748	mtx_lock(&m->lock);
749	mtx_lock(&m->qlock);
750
751	if (!(m->flags & M_RX))
752		goto err1;
753
754	while (uio->uio_resid > 0) {
755		while (MIDIQ_EMPTY(m->inq)) {
756			retval = EWOULDBLOCK;
757			if (ioflag & O_NONBLOCK)
758				goto err1;
759			mtx_unlock(&m->lock);
760			m->rchan = 1;
761			retval = msleep(&m->rchan, &m->qlock,
762			    PCATCH | PDROP, "midi RX", 0);
763			/*
764			 * We slept, maybe things have changed since last
765			 * dying check
766			 */
767			if (retval == EINTR)
768				goto err0;
769			if (m != i_dev->si_drv1)
770				retval = ENXIO;
771			/* if (retval && retval != ERESTART) */
772			if (retval)
773				goto err0;
774			mtx_lock(&m->lock);
775			mtx_lock(&m->qlock);
776			m->rchan = 0;
777			if (!m->busy)
778				goto err1;
779		}
780		MIDI_DEBUG(6, printf("midi_read start\n"));
781		/*
782	         * At this point, it is certain that m->inq has data
783	         */
784
785		used = MIN(MIDIQ_LEN(m->inq), uio->uio_resid);
786		used = MIN(used, MIDI_RSIZE);
787
788		MIDI_DEBUG(6, printf("midiread: uiomove cc=%d\n", used));
789		MIDIQ_DEQ(m->inq, buf, used);
790		retval = uiomove(buf, used, uio);
791		if (retval)
792			goto err1;
793	}
794
795	/*
796	 * If we Made it here then transfer is good
797	 */
798	retval = 0;
799err1:	mtx_unlock(&m->qlock);
800	mtx_unlock(&m->lock);
801err0:	MIDI_DEBUG(4, printf("midi_read: ret %d\n", retval));
802	return retval;
803}
804
805/*
806 * midi_write: The only setter of M_TXEN
807 */
808
809int
810midi_write(struct cdev *i_dev, struct uio *uio, int ioflag)
811{
812#define MIDI_WSIZE 32
813	struct snd_midi *m = i_dev->si_drv1;
814	int retval;
815	int used;
816	char buf[MIDI_WSIZE];
817
818
819	MIDI_DEBUG(4, printf("midi_write\n"));
820	retval = 0;
821	if (m == NULL)
822		goto err0;
823
824	mtx_lock(&m->lock);
825	mtx_lock(&m->qlock);
826
827	if (!(m->flags & M_TX))
828		goto err1;
829
830	while (uio->uio_resid > 0) {
831		while (MIDIQ_AVAIL(m->outq) == 0) {
832			retval = EWOULDBLOCK;
833			if (ioflag & O_NONBLOCK)
834				goto err1;
835			mtx_unlock(&m->lock);
836			m->wchan = 1;
837			MIDI_DEBUG(3, printf("midi_write msleep\n"));
838			retval = msleep(&m->wchan, &m->qlock,
839			    PCATCH | PDROP, "midi TX", 0);
840			/*
841			 * We slept, maybe things have changed since last
842			 * dying check
843			 */
844			if (retval == EINTR)
845				goto err0;
846			if (m != i_dev->si_drv1)
847				retval = ENXIO;
848			if (retval)
849				goto err0;
850			mtx_lock(&m->lock);
851			mtx_lock(&m->qlock);
852			m->wchan = 0;
853			if (!m->busy)
854				goto err1;
855		}
856
857		/*
858	         * We are certain than data can be placed on the queue
859	         */
860
861		used = MIN(MIDIQ_AVAIL(m->outq), uio->uio_resid);
862		used = MIN(used, MIDI_WSIZE);
863		MIDI_DEBUG(5, printf("midiout: resid %zd len %jd avail %jd\n",
864		    uio->uio_resid, (intmax_t)MIDIQ_LEN(m->outq),
865		    (intmax_t)MIDIQ_AVAIL(m->outq)));
866
867
868		MIDI_DEBUG(5, printf("midi_write: uiomove cc=%d\n", used));
869		retval = uiomove(buf, used, uio);
870		if (retval)
871			goto err1;
872		MIDIQ_ENQ(m->outq, buf, used);
873		/*
874	         * Inform the bottom half that data can be written
875	         */
876		if (!(m->flags & M_TXEN)) {
877			m->flags |= M_TXEN;
878			MPU_CALLBACK(m, m->cookie, m->flags);
879		}
880	}
881	/*
882	 * If we Made it here then transfer is good
883	 */
884	retval = 0;
885err1:	mtx_unlock(&m->qlock);
886	mtx_unlock(&m->lock);
887err0:	return retval;
888}
889
890int
891midi_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
892    struct thread *td)
893{
894	return ENXIO;
895}
896
897int
898midi_poll(struct cdev *i_dev, int events, struct thread *td)
899{
900	struct snd_midi *m = i_dev->si_drv1;
901	int revents;
902
903	if (m == NULL)
904		return 0;
905
906	revents = 0;
907
908	mtx_lock(&m->lock);
909	mtx_lock(&m->qlock);
910
911	if (events & (POLLIN | POLLRDNORM))
912		if (!MIDIQ_EMPTY(m->inq))
913			events |= events & (POLLIN | POLLRDNORM);
914
915	if (events & (POLLOUT | POLLWRNORM))
916		if (MIDIQ_AVAIL(m->outq) < m->hiwat)
917			events |= events & (POLLOUT | POLLWRNORM);
918
919	if (revents == 0) {
920		if (events & (POLLIN | POLLRDNORM))
921			selrecord(td, &m->rsel);
922
923		if (events & (POLLOUT | POLLWRNORM))
924			selrecord(td, &m->wsel);
925	}
926	mtx_unlock(&m->lock);
927	mtx_unlock(&m->qlock);
928
929	return (revents);
930}
931
932/*
933 * /dev/midistat device functions
934 *
935 */
936static int
937midistat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
938{
939	int error;
940
941	MIDI_DEBUG(1, printf("midistat_open\n"));
942	mtx_lock(&midistat_lock);
943
944	if (midistat_isopen) {
945		mtx_unlock(&midistat_lock);
946		return EBUSY;
947	}
948	midistat_isopen = 1;
949	mtx_unlock(&midistat_lock);
950
951	if (sbuf_new(&midistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) {
952		error = ENXIO;
953		mtx_lock(&midistat_lock);
954		goto out;
955	}
956	mtx_lock(&midistat_lock);
957	midistat_bufptr = 0;
958	error = (midistat_prepare(&midistat_sbuf) > 0) ? 0 : ENOMEM;
959
960out:	if (error)
961		midistat_isopen = 0;
962	mtx_unlock(&midistat_lock);
963	return error;
964}
965
966static int
967midistat_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
968{
969	MIDI_DEBUG(1, printf("midistat_close\n"));
970	mtx_lock(&midistat_lock);
971	if (!midistat_isopen) {
972		mtx_unlock(&midistat_lock);
973		return EBADF;
974	}
975	sbuf_delete(&midistat_sbuf);
976	midistat_isopen = 0;
977
978	mtx_unlock(&midistat_lock);
979	return 0;
980}
981
982static int
983midistat_read(struct cdev *i_dev, struct uio *buf, int flag)
984{
985	int l, err;
986
987	MIDI_DEBUG(4, printf("midistat_read\n"));
988	mtx_lock(&midistat_lock);
989	if (!midistat_isopen) {
990		mtx_unlock(&midistat_lock);
991		return EBADF;
992	}
993	l = min(buf->uio_resid, sbuf_len(&midistat_sbuf) - midistat_bufptr);
994	err = 0;
995	if (l > 0) {
996		mtx_unlock(&midistat_lock);
997		err = uiomove(sbuf_data(&midistat_sbuf) + midistat_bufptr, l,
998		    buf);
999		mtx_lock(&midistat_lock);
1000	} else
1001		l = 0;
1002	midistat_bufptr += l;
1003	mtx_unlock(&midistat_lock);
1004	return err;
1005}
1006
1007/*
1008 * Module library functions
1009 */
1010
1011static int
1012midistat_prepare(struct sbuf *s)
1013{
1014	struct snd_midi *m;
1015
1016	mtx_assert(&midistat_lock, MA_OWNED);
1017
1018	sbuf_printf(s, "FreeBSD Midi Driver (midi2)\n");
1019	if (TAILQ_EMPTY(&midi_devs)) {
1020		sbuf_printf(s, "No devices installed.\n");
1021		sbuf_finish(s);
1022		return sbuf_len(s);
1023	}
1024	sbuf_printf(s, "Installed devices:\n");
1025
1026	TAILQ_FOREACH(m, &midi_devs, link) {
1027		mtx_lock(&m->lock);
1028		sbuf_printf(s, "%s [%d/%d:%s]", m->name, m->unit, m->channel,
1029		    MPU_PROVIDER(m, m->cookie));
1030		sbuf_printf(s, "%s", MPU_DESCR(m, m->cookie, midistat_verbose));
1031		sbuf_printf(s, "\n");
1032		mtx_unlock(&m->lock);
1033	}
1034
1035	sbuf_finish(s);
1036	return sbuf_len(s);
1037}
1038
1039#ifdef notdef
1040/*
1041 * Convert IOCTL command to string for debugging
1042 */
1043
1044static char *
1045midi_cmdname(int cmd)
1046{
1047	static struct {
1048		int	cmd;
1049		char   *name;
1050	}     *tab, cmdtab_midiioctl[] = {
1051#define A(x)	{x, ## x}
1052		/*
1053	         * Once we have some real IOCTLs define, the following will
1054	         * be relavant.
1055	         *
1056	         * A(SNDCTL_MIDI_PRETIME), A(SNDCTL_MIDI_MPUMODE),
1057	         * A(SNDCTL_MIDI_MPUCMD), A(SNDCTL_SYNTH_INFO),
1058	         * A(SNDCTL_MIDI_INFO), A(SNDCTL_SYNTH_MEMAVL),
1059	         * A(SNDCTL_FM_LOAD_INSTR), A(SNDCTL_FM_4OP_ENABLE),
1060	         * A(MIOSPASSTHRU), A(MIOGPASSTHRU), A(AIONWRITE),
1061	         * A(AIOGSIZE), A(AIOSSIZE), A(AIOGFMT), A(AIOSFMT),
1062	         * A(AIOGMIX), A(AIOSMIX), A(AIOSTOP), A(AIOSYNC),
1063	         * A(AIOGCAP),
1064	         */
1065#undef A
1066		{
1067			-1, "unknown"
1068		},
1069	};
1070
1071	for (tab = cmdtab_midiioctl; tab->cmd != cmd && tab->cmd != -1; tab++);
1072	return tab->name;
1073}
1074
1075#endif					/* notdef */
1076
1077/*
1078 * midisynth
1079 */
1080
1081
1082int
1083midisynth_open(void *n, void *arg, int flags)
1084{
1085	struct snd_midi *m = ((struct synth_midi *)n)->m;
1086	int retval;
1087
1088	MIDI_DEBUG(1, printf("midisynth_open %s %s\n",
1089	    flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
1090
1091	if (m == NULL)
1092		return ENXIO;
1093
1094	mtx_lock(&m->lock);
1095	mtx_lock(&m->qlock);
1096
1097	retval = 0;
1098
1099	if (flags & FREAD) {
1100		if (MIDIQ_SIZE(m->inq) == 0)
1101			retval = ENXIO;
1102		else if (m->flags & M_RX)
1103			retval = EBUSY;
1104		if (retval)
1105			goto err;
1106	}
1107	if (flags & FWRITE) {
1108		if (MIDIQ_SIZE(m->outq) == 0)
1109			retval = ENXIO;
1110		else if (m->flags & M_TX)
1111			retval = EBUSY;
1112		if (retval)
1113			goto err;
1114	}
1115	m->busy++;
1116
1117	/*
1118	 * TODO: Consider m->async = 0;
1119	 */
1120
1121	if (flags & FREAD) {
1122		m->flags |= M_RX | M_RXEN;
1123		/*
1124	         * Only clear the inq, the outq might still have data to drain
1125	         * from a previous session
1126	         */
1127		MIDIQ_CLEAR(m->inq);
1128		m->rchan = 0;
1129	};
1130
1131	if (flags & FWRITE) {
1132		m->flags |= M_TX;
1133		m->wchan = 0;
1134	}
1135	m->synth_flags = flags & (FREAD | FWRITE);
1136
1137	MPU_CALLBACK(m, m->cookie, m->flags);
1138
1139
1140err:	mtx_unlock(&m->qlock);
1141	mtx_unlock(&m->lock);
1142	MIDI_DEBUG(2, printf("midisynth_open: return %d.\n", retval));
1143	return retval;
1144}
1145
1146int
1147midisynth_close(void *n)
1148{
1149	struct snd_midi *m = ((struct synth_midi *)n)->m;
1150	int retval;
1151	int oldflags;
1152
1153	MIDI_DEBUG(1, printf("midisynth_close %s %s\n",
1154	    m->synth_flags & FREAD ? "M_RX" : "",
1155	    m->synth_flags & FWRITE ? "M_TX" : ""));
1156
1157	if (m == NULL)
1158		return ENXIO;
1159
1160	mtx_lock(&m->lock);
1161	mtx_lock(&m->qlock);
1162
1163	if ((m->synth_flags & FREAD && !(m->flags & M_RX)) ||
1164	    (m->synth_flags & FWRITE && !(m->flags & M_TX))) {
1165		retval = ENXIO;
1166		goto err;
1167	}
1168	m->busy--;
1169
1170	oldflags = m->flags;
1171
1172	if (m->synth_flags & FREAD)
1173		m->flags &= ~(M_RX | M_RXEN);
1174	if (m->synth_flags & FWRITE)
1175		m->flags &= ~M_TX;
1176
1177	if ((m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN)))
1178		MPU_CALLBACK(m, m->cookie, m->flags);
1179
1180	MIDI_DEBUG(1, printf("midi_close: closed, busy = %d.\n", m->busy));
1181
1182	mtx_unlock(&m->qlock);
1183	mtx_unlock(&m->lock);
1184	retval = 0;
1185err:	return retval;
1186}
1187
1188/*
1189 * Always blocking.
1190 */
1191
1192int
1193midisynth_writeraw(void *n, uint8_t *buf, size_t len)
1194{
1195	struct snd_midi *m = ((struct synth_midi *)n)->m;
1196	int retval;
1197	int used;
1198	int i;
1199
1200	MIDI_DEBUG(4, printf("midisynth_writeraw\n"));
1201
1202	retval = 0;
1203
1204	if (m == NULL)
1205		return ENXIO;
1206
1207	mtx_lock(&m->lock);
1208	mtx_lock(&m->qlock);
1209
1210	if (!(m->flags & M_TX))
1211		goto err1;
1212
1213	if (midi_dumpraw)
1214		printf("midi dump: ");
1215
1216	while (len > 0) {
1217		while (MIDIQ_AVAIL(m->outq) == 0) {
1218			if (!(m->flags & M_TXEN)) {
1219				m->flags |= M_TXEN;
1220				MPU_CALLBACK(m, m->cookie, m->flags);
1221			}
1222			mtx_unlock(&m->lock);
1223			m->wchan = 1;
1224			MIDI_DEBUG(3, printf("midisynth_writeraw msleep\n"));
1225			retval = msleep(&m->wchan, &m->qlock,
1226			    PCATCH | PDROP, "midi TX", 0);
1227			/*
1228			 * We slept, maybe things have changed since last
1229			 * dying check
1230			 */
1231			if (retval == EINTR)
1232				goto err0;
1233
1234			if (retval)
1235				goto err0;
1236			mtx_lock(&m->lock);
1237			mtx_lock(&m->qlock);
1238			m->wchan = 0;
1239			if (!m->busy)
1240				goto err1;
1241		}
1242
1243		/*
1244	         * We are certain than data can be placed on the queue
1245	         */
1246
1247		used = MIN(MIDIQ_AVAIL(m->outq), len);
1248		used = MIN(used, MIDI_WSIZE);
1249		MIDI_DEBUG(5,
1250		    printf("midi_synth: resid %zu len %jd avail %jd\n",
1251		    len, (intmax_t)MIDIQ_LEN(m->outq),
1252		    (intmax_t)MIDIQ_AVAIL(m->outq)));
1253
1254		if (midi_dumpraw)
1255			for (i = 0; i < used; i++)
1256				printf("%x ", buf[i]);
1257
1258		MIDIQ_ENQ(m->outq, buf, used);
1259		len -= used;
1260
1261		/*
1262	         * Inform the bottom half that data can be written
1263	         */
1264		if (!(m->flags & M_TXEN)) {
1265			m->flags |= M_TXEN;
1266			MPU_CALLBACK(m, m->cookie, m->flags);
1267		}
1268	}
1269	/*
1270	 * If we Made it here then transfer is good
1271	 */
1272	if (midi_dumpraw)
1273		printf("\n");
1274
1275	retval = 0;
1276err1:	mtx_unlock(&m->qlock);
1277	mtx_unlock(&m->lock);
1278err0:	return retval;
1279}
1280
1281static int
1282midisynth_killnote(void *n, uint8_t chn, uint8_t note, uint8_t vel)
1283{
1284	u_char c[3];
1285
1286
1287	if (note > 127 || chn > 15)
1288		return (EINVAL);
1289
1290	if (vel > 127)
1291		vel = 127;
1292
1293	if (vel == 64) {
1294		c[0] = 0x90 | (chn & 0x0f);	/* Note on. */
1295		c[1] = (u_char)note;
1296		c[2] = 0;
1297	} else {
1298		c[0] = 0x80 | (chn & 0x0f);	/* Note off. */
1299		c[1] = (u_char)note;
1300		c[2] = (u_char)vel;
1301	}
1302
1303	return midisynth_writeraw(n, c, 3);
1304}
1305
1306static int
1307midisynth_setinstr(void *n, uint8_t chn, uint16_t instr)
1308{
1309	u_char c[2];
1310
1311	if (instr > 127 || chn > 15)
1312		return EINVAL;
1313
1314	c[0] = 0xc0 | (chn & 0x0f);	/* Progamme change. */
1315	c[1] = instr + midi_instroff;
1316
1317	return midisynth_writeraw(n, c, 2);
1318}
1319
1320static int
1321midisynth_startnote(void *n, uint8_t chn, uint8_t note, uint8_t vel)
1322{
1323	u_char c[3];
1324
1325	if (note > 127 || chn > 15)
1326		return EINVAL;
1327
1328	if (vel > 127)
1329		vel = 127;
1330
1331	c[0] = 0x90 | (chn & 0x0f);	/* Note on. */
1332	c[1] = (u_char)note;
1333	c[2] = (u_char)vel;
1334
1335	return midisynth_writeraw(n, c, 3);
1336}
1337static int
1338midisynth_alloc(void *n, uint8_t chan, uint8_t note)
1339{
1340	return chan;
1341}
1342
1343static int
1344midisynth_controller(void *n, uint8_t chn, uint8_t ctrlnum, uint16_t val)
1345{
1346	u_char c[3];
1347
1348	if (ctrlnum > 127 || chn > 15)
1349		return EINVAL;
1350
1351	c[0] = 0xb0 | (chn & 0x0f);	/* Control Message. */
1352	c[1] = ctrlnum;
1353	c[2] = val;
1354	return midisynth_writeraw(n, c, 3);
1355}
1356
1357static int
1358midisynth_bender(void *n, uint8_t chn, uint16_t val)
1359{
1360	u_char c[3];
1361
1362
1363	if (val > 16383 || chn > 15)
1364		return EINVAL;
1365
1366	c[0] = 0xe0 | (chn & 0x0f);	/* Pitch bend. */
1367	c[1] = (u_char)val & 0x7f;
1368	c[2] = (u_char)(val >> 7) & 0x7f;
1369
1370	return midisynth_writeraw(n, c, 3);
1371}
1372
1373/*
1374 * Single point of midi destructions.
1375 */
1376static int
1377midi_destroy(struct snd_midi *m, int midiuninit)
1378{
1379
1380	mtx_assert(&midistat_lock, MA_OWNED);
1381	mtx_assert(&m->lock, MA_OWNED);
1382
1383	MIDI_DEBUG(3, printf("midi_destroy\n"));
1384	m->dev->si_drv1 = NULL;
1385	mtx_unlock(&m->lock);	/* XXX */
1386	destroy_dev(m->dev);
1387	TAILQ_REMOVE(&midi_devs, m, link);
1388	if (midiuninit)
1389		MPU_UNINIT(m, m->cookie);
1390	free(MIDIQ_BUF(m->inq), M_MIDI);
1391	free(MIDIQ_BUF(m->outq), M_MIDI);
1392	mtx_destroy(&m->qlock);
1393	mtx_destroy(&m->lock);
1394	free(m->synth, M_MIDI);
1395	free(m, M_MIDI);
1396	return 0;
1397}
1398
1399/*
1400 * Load and unload functions, creates the /dev/midistat device
1401 */
1402
1403static int
1404midi_load(void)
1405{
1406	mtx_init(&midistat_lock, "midistat lock", NULL, 0);
1407	TAILQ_INIT(&midi_devs);		/* Initialize the queue. */
1408
1409	midistat_dev = make_dev(&midistat_cdevsw,
1410	    MIDIMKMINOR(0, MIDI_DEV_MIDICTL, 0),
1411	    UID_ROOT, GID_WHEEL, 0666, "midistat");
1412
1413	return 0;
1414}
1415
1416static int
1417midi_unload(void)
1418{
1419	struct snd_midi *m, *tmp;
1420	int retval;
1421
1422	MIDI_DEBUG(1, printf("midi_unload()\n"));
1423	retval = EBUSY;
1424	mtx_lock(&midistat_lock);
1425	if (midistat_isopen)
1426		goto exit0;
1427
1428	TAILQ_FOREACH_SAFE(m, &midi_devs, link, tmp) {
1429		mtx_lock(&m->lock);
1430		if (m->busy)
1431			retval = EBUSY;
1432		else
1433			retval = midi_destroy(m, 1);
1434		if (retval)
1435			goto exit1;
1436	}
1437
1438	mtx_unlock(&midistat_lock);	/* XXX */
1439
1440	destroy_dev(midistat_dev);
1441	/*
1442	 * Made it here then unload is complete
1443	 */
1444	mtx_destroy(&midistat_lock);
1445	return 0;
1446
1447exit1:
1448	mtx_unlock(&m->lock);
1449exit0:
1450	mtx_unlock(&midistat_lock);
1451	if (retval)
1452		MIDI_DEBUG(2, printf("midi_unload: failed\n"));
1453	return retval;
1454}
1455
1456extern int seq_modevent(module_t mod, int type, void *data);
1457
1458static int
1459midi_modevent(module_t mod, int type, void *data)
1460{
1461	int retval;
1462
1463	retval = 0;
1464
1465	switch (type) {
1466	case MOD_LOAD:
1467		retval = midi_load();
1468		if (retval == 0)
1469			retval = seq_modevent(mod, type, data);
1470		break;
1471
1472	case MOD_UNLOAD:
1473		retval = midi_unload();
1474		if (retval == 0)
1475			retval = seq_modevent(mod, type, data);
1476		break;
1477
1478	default:
1479		break;
1480	}
1481
1482	return retval;
1483}
1484
1485kobj_t
1486midimapper_addseq(void *arg1, int *unit, void **cookie)
1487{
1488	unit = 0;
1489
1490	return (kobj_t)arg1;
1491}
1492
1493int
1494midimapper_open(void *arg1, void **cookie)
1495{
1496	int retval = 0;
1497	struct snd_midi *m;
1498
1499	mtx_lock(&midistat_lock);
1500
1501	TAILQ_FOREACH(m, &midi_devs, link) {
1502		retval++;
1503	}
1504
1505	mtx_unlock(&midistat_lock);
1506	return retval;
1507}
1508
1509int
1510midimapper_close(void *arg1, void *cookie)
1511{
1512	return 0;
1513}
1514
1515kobj_t
1516midimapper_fetch_synth(void *arg, void *cookie, int unit)
1517{
1518	struct snd_midi *m;
1519	int retval = 0;
1520
1521	mtx_lock(&midistat_lock);
1522
1523	TAILQ_FOREACH(m, &midi_devs, link) {
1524		if (unit == retval) {
1525			mtx_unlock(&midistat_lock);
1526			return (kobj_t)m->synth;
1527		}
1528		retval++;
1529	}
1530
1531	mtx_unlock(&midistat_lock);
1532	return NULL;
1533}
1534
1535DEV_MODULE(midi, midi_modevent, NULL);
1536MODULE_VERSION(midi, 1);
1537