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