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