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$");
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/queue.h>
43 #include <sys/kernel.h>
44 #include <sys/lock.h>
45 #include <sys/mutex.h>
46 #include <sys/proc.h>
47 #include <sys/signalvar.h>
48 #include <sys/conf.h>
49 #include <sys/selinfo.h>
50 #include <sys/sysctl.h>
51 #include <sys/malloc.h>
52 #include <sys/sx.h>
53 #include <sys/proc.h>
54 #include <sys/fcntl.h>
55 #include <sys/types.h>
56 #include <sys/uio.h>
57 #include <sys/poll.h>
58 #include <sys/sbuf.h>
59 #include <sys/kobj.h>
60 #include <sys/module.h>
61
62 #ifdef HAVE_KERNEL_OPTION_HEADERS
63 #include "opt_snd.h"
64 #endif
65
66 #include <dev/sound/midi/midi.h>
67 #include "mpu_if.h"
68
69 #include <dev/sound/midi/midiq.h>
70 #include "synth_if.h"
71 MALLOC_DEFINE(M_MIDI, "midi buffers", "Midi data allocation area");
72
73 #ifndef KOBJMETHOD_END
74 #define KOBJMETHOD_END { NULL, NULL }
75 #endif
76
77 #define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
78 #define MIDIMKMINOR(u, d, c) PCMMKMINOR(u, d, c)
79
80 #define MIDI_DEV_RAW 2
81 #define MIDI_DEV_MIDICTL 12
82
83 enum midi_states {
84 MIDI_IN_START, MIDI_IN_SYSEX, MIDI_IN_DATA
85 };
86
87 /*
88 * The MPU interface current has init() uninit() inqsize() outqsize()
89 * callback() : fiddle with the tx|rx status.
90 */
91
92 #include "mpu_if.h"
93
94 /*
95 * /dev/rmidi Structure definitions
96 */
97
98 #define MIDI_NAMELEN 16
99 struct snd_midi {
100 KOBJ_FIELDS;
101 struct mtx lock; /* Protects all but queues */
102 void *cookie;
103
104 int unit; /* Should only be used in midistat */
105 int channel; /* Should only be used in midistat */
106
107 int busy;
108 int flags; /* File flags */
109 char name[MIDI_NAMELEN];
110 struct mtx qlock; /* Protects inq, outq and flags */
111 MIDIQ_HEAD(, char) inq, outq;
112 int rchan, wchan;
113 struct selinfo rsel, wsel;
114 int hiwat; /* QLEN(outq)>High-water -> disable
115 * writes from userland */
116 enum midi_states inq_state;
117 int inq_status, inq_left; /* Variables for the state machine in
118 * Midi_in, this is to provide that
119 * signals only get issued only
120 * complete command packets. */
121 struct proc *async;
122 struct cdev *dev;
123 struct synth_midi *synth;
124 int synth_flags;
125 TAILQ_ENTRY(snd_midi) link;
126 };
127
128 struct synth_midi {
129 KOBJ_FIELDS;
130 struct snd_midi *m;
131 };
132
133 static synth_open_t midisynth_open;
134 static synth_close_t midisynth_close;
135 static synth_writeraw_t midisynth_writeraw;
136 static synth_killnote_t midisynth_killnote;
137 static synth_startnote_t midisynth_startnote;
138 static synth_setinstr_t midisynth_setinstr;
139 static synth_alloc_t midisynth_alloc;
140 static synth_controller_t midisynth_controller;
141 static synth_bender_t midisynth_bender;
142
143
144 static kobj_method_t midisynth_methods[] = {
145 KOBJMETHOD(synth_open, midisynth_open),
146 KOBJMETHOD(synth_close, midisynth_close),
147 KOBJMETHOD(synth_writeraw, midisynth_writeraw),
148 KOBJMETHOD(synth_setinstr, midisynth_setinstr),
149 KOBJMETHOD(synth_startnote, midisynth_startnote),
150 KOBJMETHOD(synth_killnote, midisynth_killnote),
151 KOBJMETHOD(synth_alloc, midisynth_alloc),
152 KOBJMETHOD(synth_controller, midisynth_controller),
153 KOBJMETHOD(synth_bender, midisynth_bender),
154 KOBJMETHOD_END
155 };
156
157 DEFINE_CLASS(midisynth, midisynth_methods, 0);
158
159 /*
160 * Module Exports & Interface
161 *
162 * struct midi_chan *midi_init(MPU_CLASS cls, int unit, int chan,
163 * void *cookie)
164 * int midi_uninit(struct snd_midi *)
165 *
166 * 0 == no error
167 * EBUSY or other error
168 *
169 * int midi_in(struct snd_midi *, char *buf, int count)
170 * int midi_out(struct snd_midi *, char *buf, int count)
171 *
172 * midi_{in,out} return actual size transfered
173 *
174 */
175
176
177 /*
178 * midi_devs tailq, holder of all rmidi instances protected by midistat_lock
179 */
180
181 TAILQ_HEAD(, snd_midi) midi_devs;
182
183 /*
184 * /dev/midistat variables and declarations, protected by midistat_lock
185 */
186
187 static struct sx midistat_lock;
188 static int midistat_isopen = 0;
189 static struct sbuf midistat_sbuf;
190 static struct cdev *midistat_dev;
191
192 /*
193 * /dev/midistat dev_t declarations
194 */
195
196 static d_open_t midistat_open;
197 static d_close_t midistat_close;
198 static d_read_t midistat_read;
199
200 static struct cdevsw midistat_cdevsw = {
201 .d_version = D_VERSION,
202 .d_open = midistat_open,
203 .d_close = midistat_close,
204 .d_read = midistat_read,
205 .d_name = "midistat",
206 };
207
208
209 /*
210 * /dev/rmidi dev_t declarations, struct variable access is protected by
211 * locks contained within the structure.
212 */
213
214 static d_open_t midi_open;
215 static d_close_t midi_close;
216 static d_ioctl_t midi_ioctl;
217 static d_read_t midi_read;
218 static d_write_t midi_write;
219 static d_poll_t midi_poll;
220
221 static struct cdevsw midi_cdevsw = {
222 .d_version = D_VERSION,
223 .d_open = midi_open,
224 .d_close = midi_close,
225 .d_read = midi_read,
226 .d_write = midi_write,
227 .d_ioctl = midi_ioctl,
228 .d_poll = midi_poll,
229 .d_name = "rmidi",
230 };
231
232 /*
233 * Prototypes of library functions
234 */
235
236 static int midi_destroy(struct snd_midi *, int);
237 static int midistat_prepare(struct sbuf * s);
238 static int midi_load(void);
239 static int midi_unload(void);
240
241 /*
242 * Misc declr.
243 */
244 SYSCTL_NODE(_hw, OID_AUTO, midi, CTLFLAG_RD, 0, "Midi driver");
245 static SYSCTL_NODE(_hw_midi, OID_AUTO, stat, CTLFLAG_RD, 0, "Status device");
246
247 int midi_debug;
248 /* XXX: should this be moved into debug.midi? */
249 SYSCTL_INT(_hw_midi, OID_AUTO, debug, CTLFLAG_RW, &midi_debug, 0, "");
250
251 int midi_dumpraw;
252 SYSCTL_INT(_hw_midi, OID_AUTO, dumpraw, CTLFLAG_RW, &midi_dumpraw, 0, "");
253
254 int midi_instroff;
255 SYSCTL_INT(_hw_midi, OID_AUTO, instroff, CTLFLAG_RW, &midi_instroff, 0, "");
256
257 int midistat_verbose;
258 SYSCTL_INT(_hw_midi_stat, OID_AUTO, verbose, CTLFLAG_RW,
259 &midistat_verbose, 0, "");
260
261 #define MIDI_DEBUG(l,a) if(midi_debug>=l) a
262 /*
263 * CODE START
264 */
265
266 /*
267 * Register a new rmidi device. cls midi_if interface unit == 0 means
268 * auto-assign new unit number unit != 0 already assigned a unit number, eg.
269 * not the first channel provided by this device. channel, sub-unit
270 * cookie is passed back on MPU calls Typical device drivers will call with
271 * unit=0, channel=1..(number of channels) and cookie=soft_c and won't care
272 * what unit number is used.
273 *
274 * It is an error to call midi_init with an already used unit/channel combo.
275 *
276 * Returns NULL on error
277 *
278 */
279 struct snd_midi *
280 midi_init(kobj_class_t cls, int unit, int channel, void *cookie)
281 {
282 struct snd_midi *m;
283 int i;
284 int inqsize, outqsize;
285 MIDI_TYPE *buf;
286
287 MIDI_DEBUG(1, printf("midiinit: unit %d/%d.\n", unit, channel));
288 sx_xlock(&midistat_lock);
289 /*
290 * Protect against call with existing unit/channel or auto-allocate a
291 * new unit number.
292 */
293 i = -1;
294 TAILQ_FOREACH(m, &midi_devs, link) {
295 mtx_lock(&m->lock);
296 if (unit != 0) {
297 if (m->unit == unit && m->channel == channel) {
298 mtx_unlock(&m->lock);
299 goto err0;
300 }
301 } else {
302 /*
303 * Find a better unit number
304 */
305 if (m->unit > i)
306 i = m->unit;
307 }
308 mtx_unlock(&m->lock);
309 }
310
311 if (unit == 0)
312 unit = i + 1;
313
314 MIDI_DEBUG(1, printf("midiinit #2: unit %d/%d.\n", unit, channel));
315 m = malloc(sizeof(*m), M_MIDI, M_WAITOK | M_ZERO);
316 m->synth = malloc(sizeof(*m->synth), M_MIDI, M_WAITOK | 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 m->busy = 0;
353 m->flags = 0;
354 m->unit = unit;
355 m->channel = channel;
356 m->cookie = cookie;
357
358 if (MPU_INIT(m, cookie))
359 goto err2;
360
361 mtx_unlock(&m->lock);
362 mtx_unlock(&m->qlock);
363
364 TAILQ_INSERT_TAIL(&midi_devs, m, link);
365
366 sx_xunlock(&midistat_lock);
367
368 m->dev = make_dev(&midi_cdevsw,
369 MIDIMKMINOR(unit, MIDI_DEV_RAW, channel),
370 UID_ROOT, GID_WHEEL, 0666, "midi%d.%d", unit, channel);
371 m->dev->si_drv1 = m;
372
373 return m;
374
375 err2:
376 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);
383 err1:
384 free(m->synth, M_MIDI);
385 free(m, M_MIDI);
386 err0:
387 sx_xunlock(&midistat_lock);
388 MIDI_DEBUG(1, printf("midi_init ended in error\n"));
389 return NULL;
390 }
391
392 /*
393 * midi_uninit does not call MIDI_UNINIT, as since this is the implementors
394 * entry point. midi_uninit if fact, does not send any methods. A call to
395 * midi_uninit is a defacto promise that you won't manipulate ch anymore
396 *
397 */
398
399 int
400 midi_uninit(struct snd_midi *m)
401 {
402 int err;
403
404 err = EBUSY;
405 sx_xlock(&midistat_lock);
406 mtx_lock(&m->lock);
407 if (m->busy) {
408 if (!(m->rchan || m->wchan))
409 goto err;
410
411 if (m->rchan) {
412 wakeup(&m->rchan);
413 m->rchan = 0;
414 }
415 if (m->wchan) {
416 wakeup(&m->wchan);
417 m->wchan = 0;
418 }
419 }
420 err = midi_destroy(m, 0);
421 if (!err)
422 goto exit;
423
424 err:
425 mtx_unlock(&m->lock);
426 exit:
427 sx_xunlock(&midistat_lock);
428 return err;
429 }
430
431 /*
432 * midi_in: process all data until the queue is full, then discards the rest.
433 * Since midi_in is a state machine, data discards can cause it to get out of
434 * whack. Process as much as possible. It calls, wakeup, selnotify and
435 * psignal at most once.
436 */
437
438 #ifdef notdef
439 static int midi_lengths[] = {2, 2, 2, 2, 1, 1, 2, 0};
440
441 #endif /* notdef */
442 /* Number of bytes in a MIDI command */
443 #define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7])
444 #define MIDI_ACK 0xfe
445 #define MIDI_IS_STATUS(d) ((d) >= 0x80)
446 #define MIDI_IS_COMMON(d) ((d) >= 0xf0)
447
448 #define MIDI_SYSEX_START 0xF0
449 #define MIDI_SYSEX_END 0xF7
450
451
452 int
453 midi_in(struct snd_midi *m, MIDI_TYPE *buf, int size)
454 {
455 /* int i, sig, enq; */
456 int used;
457
458 /* MIDI_TYPE data; */
459 MIDI_DEBUG(5, printf("midi_in: m=%p size=%d\n", m, size));
460
461 /*
462 * XXX: locking flub
463 */
464 if (!(m->flags & M_RX))
465 return size;
466
467 used = 0;
468
469 mtx_lock(&m->qlock);
470 #if 0
471 /*
472 * Don't bother queuing if not in read mode. Discard everything and
473 * return size so the caller doesn't freak out.
474 */
475
476 if (!(m->flags & M_RX))
477 return size;
478
479 for (i = sig = 0; i < size; i++) {
480
481 data = buf[i];
482 enq = 0;
483 if (data == MIDI_ACK)
484 continue;
485
486 switch (m->inq_state) {
487 case MIDI_IN_START:
488 if (MIDI_IS_STATUS(data)) {
489 switch (data) {
490 case 0xf0: /* Sysex */
491 m->inq_state = MIDI_IN_SYSEX;
492 break;
493 case 0xf1: /* MTC quarter frame */
494 case 0xf3: /* Song select */
495 m->inq_state = MIDI_IN_DATA;
496 enq = 1;
497 m->inq_left = 1;
498 break;
499 case 0xf2: /* Song position pointer */
500 m->inq_state = MIDI_IN_DATA;
501 enq = 1;
502 m->inq_left = 2;
503 break;
504 default:
505 if (MIDI_IS_COMMON(data)) {
506 enq = 1;
507 sig = 1;
508 } else {
509 m->inq_state = MIDI_IN_DATA;
510 enq = 1;
511 m->inq_status = data;
512 m->inq_left = MIDI_LENGTH(data);
513 }
514 break;
515 }
516 } else if (MIDI_IS_STATUS(m->inq_status)) {
517 m->inq_state = MIDI_IN_DATA;
518 if (!MIDIQ_FULL(m->inq)) {
519 used++;
520 MIDIQ_ENQ(m->inq, &m->inq_status, 1);
521 }
522 enq = 1;
523 m->inq_left = MIDI_LENGTH(m->inq_status) - 1;
524 }
525 break;
526 /*
527 * End of case MIDI_IN_START:
528 */
529
530 case MIDI_IN_DATA:
531 enq = 1;
532 if (--m->inq_left <= 0)
533 sig = 1;/* deliver data */
534 break;
535 case MIDI_IN_SYSEX:
536 if (data == MIDI_SYSEX_END)
537 m->inq_state = MIDI_IN_START;
538 break;
539 }
540
541 if (enq)
542 if (!MIDIQ_FULL(m->inq)) {
543 MIDIQ_ENQ(m->inq, &data, 1);
544 used++;
545 }
546 /*
547 * End of the state machines main "for loop"
548 */
549 }
550 if (sig) {
551 #endif
552 MIDI_DEBUG(6, printf("midi_in: len %jd avail %jd\n",
553 (intmax_t)MIDIQ_LEN(m->inq),
554 (intmax_t)MIDIQ_AVAIL(m->inq)));
555 if (MIDIQ_AVAIL(m->inq) > size) {
556 used = size;
557 MIDIQ_ENQ(m->inq, buf, size);
558 } else {
559 MIDI_DEBUG(4, printf("midi_in: Discarding data qu\n"));
560 mtx_unlock(&m->qlock);
561 return 0;
562 }
563 if (m->rchan) {
564 wakeup(&m->rchan);
565 m->rchan = 0;
566 }
567 selwakeup(&m->rsel);
568 if (m->async) {
569 PROC_LOCK(m->async);
570 kern_psignal(m->async, SIGIO);
571 PROC_UNLOCK(m->async);
572 }
573 #if 0
574 }
575 #endif
576 mtx_unlock(&m->qlock);
577 return used;
578 }
579
580 /*
581 * midi_out: The only clearer of the M_TXEN flag.
582 */
583 int
584 midi_out(struct snd_midi *m, MIDI_TYPE *buf, int size)
585 {
586 int used;
587
588 /*
589 * XXX: locking flub
590 */
591 if (!(m->flags & M_TXEN))
592 return 0;
593
594 MIDI_DEBUG(2, printf("midi_out: %p\n", m));
595 mtx_lock(&m->qlock);
596 used = MIN(size, MIDIQ_LEN(m->outq));
597 MIDI_DEBUG(3, printf("midi_out: used %d\n", used));
598 if (used)
599 MIDIQ_DEQ(m->outq, buf, used);
600 if (MIDIQ_EMPTY(m->outq)) {
601 m->flags &= ~M_TXEN;
602 MPU_CALLBACKP(m, m->cookie, m->flags);
603 }
604 if (used && MIDIQ_AVAIL(m->outq) > m->hiwat) {
605 if (m->wchan) {
606 wakeup(&m->wchan);
607 m->wchan = 0;
608 }
609 selwakeup(&m->wsel);
610 if (m->async) {
611 PROC_LOCK(m->async);
612 kern_psignal(m->async, SIGIO);
613 PROC_UNLOCK(m->async);
614 }
615 }
616 mtx_unlock(&m->qlock);
617 return used;
618 }
619
620
621 /*
622 * /dev/rmidi#.# device access functions
623 */
624 int
625 midi_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
626 {
627 struct snd_midi *m = i_dev->si_drv1;
628 int retval;
629
630 MIDI_DEBUG(1, printf("midiopen %p %s %s\n", td,
631 flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
632 if (m == NULL)
633 return ENXIO;
634
635 mtx_lock(&m->lock);
636 mtx_lock(&m->qlock);
637
638 retval = 0;
639
640 if (flags & FREAD) {
641 if (MIDIQ_SIZE(m->inq) == 0)
642 retval = ENXIO;
643 else if (m->flags & M_RX)
644 retval = EBUSY;
645 if (retval)
646 goto err;
647 }
648 if (flags & FWRITE) {
649 if (MIDIQ_SIZE(m->outq) == 0)
650 retval = ENXIO;
651 else if (m->flags & M_TX)
652 retval = EBUSY;
653 if (retval)
654 goto err;
655 }
656 m->busy++;
657
658 m->rchan = 0;
659 m->wchan = 0;
660 m->async = 0;
661
662 if (flags & FREAD) {
663 m->flags |= M_RX | M_RXEN;
664 /*
665 * Only clear the inq, the outq might still have data to drain
666 * from a previous session
667 */
668 MIDIQ_CLEAR(m->inq);
669 }
670
671 if (flags & FWRITE)
672 m->flags |= M_TX;
673
674 MPU_CALLBACK(m, m->cookie, m->flags);
675
676 MIDI_DEBUG(2, printf("midi_open: opened.\n"));
677
678 err: mtx_unlock(&m->qlock);
679 mtx_unlock(&m->lock);
680 return retval;
681 }
682
683 int
684 midi_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
685 {
686 struct snd_midi *m = i_dev->si_drv1;
687 int retval;
688 int oldflags;
689
690 MIDI_DEBUG(1, printf("midi_close %p %s %s\n", td,
691 flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
692
693 if (m == NULL)
694 return ENXIO;
695
696 mtx_lock(&m->lock);
697 mtx_lock(&m->qlock);
698
699 if ((flags & FREAD && !(m->flags & M_RX)) ||
700 (flags & FWRITE && !(m->flags & M_TX))) {
701 retval = ENXIO;
702 goto err;
703 }
704 m->busy--;
705
706 oldflags = m->flags;
707
708 if (flags & FREAD)
709 m->flags &= ~(M_RX | M_RXEN);
710 if (flags & FWRITE)
711 m->flags &= ~M_TX;
712
713 if ((m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN)))
714 MPU_CALLBACK(m, m->cookie, m->flags);
715
716 MIDI_DEBUG(1, printf("midi_close: closed, busy = %d.\n", m->busy));
717
718 mtx_unlock(&m->qlock);
719 mtx_unlock(&m->lock);
720 retval = 0;
721 err: return retval;
722 }
723
724 /*
725 * TODO: midi_read, per oss programmer's guide pg. 42 should return as soon
726 * as data is available.
727 */
728 int
729 midi_read(struct cdev *i_dev, struct uio *uio, int ioflag)
730 {
731 #define MIDI_RSIZE 32
732 struct snd_midi *m = i_dev->si_drv1;
733 int retval;
734 int used;
735 char buf[MIDI_RSIZE];
736
737 MIDI_DEBUG(5, printf("midiread: count=%lu\n",
738 (unsigned long)uio->uio_resid));
739
740 retval = EIO;
741
742 if (m == NULL)
743 goto err0;
744
745 mtx_lock(&m->lock);
746 mtx_lock(&m->qlock);
747
748 if (!(m->flags & M_RX))
749 goto err1;
750
751 while (uio->uio_resid > 0) {
752 while (MIDIQ_EMPTY(m->inq)) {
753 retval = EWOULDBLOCK;
754 if (ioflag & O_NONBLOCK)
755 goto err1;
756 mtx_unlock(&m->lock);
757 m->rchan = 1;
758 retval = msleep(&m->rchan, &m->qlock,
759 PCATCH | PDROP, "midi RX", 0);
760 /*
761 * We slept, maybe things have changed since last
762 * dying check
763 */
764 if (retval == EINTR)
765 goto err0;
766 if (m != i_dev->si_drv1)
767 retval = ENXIO;
768 /* if (retval && retval != ERESTART) */
769 if (retval)
770 goto err0;
771 mtx_lock(&m->lock);
772 mtx_lock(&m->qlock);
773 m->rchan = 0;
774 if (!m->busy)
775 goto err1;
776 }
777 MIDI_DEBUG(6, printf("midi_read start\n"));
778 /*
779 * At this point, it is certain that m->inq has data
780 */
781
782 used = MIN(MIDIQ_LEN(m->inq), uio->uio_resid);
783 used = MIN(used, MIDI_RSIZE);
784
785 MIDI_DEBUG(6, printf("midiread: uiomove cc=%d\n", used));
786 MIDIQ_DEQ(m->inq, buf, used);
787 retval = uiomove(buf, used, uio);
788 if (retval)
789 goto err1;
790 }
791
792 /*
793 * If we Made it here then transfer is good
794 */
795 retval = 0;
796 err1: mtx_unlock(&m->qlock);
797 mtx_unlock(&m->lock);
798 err0: MIDI_DEBUG(4, printf("midi_read: ret %d\n", retval));
799 return retval;
800 }
801
802 /*
803 * midi_write: The only setter of M_TXEN
804 */
805
806 int
807 midi_write(struct cdev *i_dev, struct uio *uio, int ioflag)
808 {
809 #define MIDI_WSIZE 32
810 struct snd_midi *m = i_dev->si_drv1;
811 int retval;
812 int used;
813 char buf[MIDI_WSIZE];
814
815
816 MIDI_DEBUG(4, printf("midi_write\n"));
817 retval = 0;
818 if (m == NULL)
819 goto err0;
820
821 mtx_lock(&m->lock);
822 mtx_lock(&m->qlock);
823
824 if (!(m->flags & M_TX))
825 goto err1;
826
827 while (uio->uio_resid > 0) {
828 while (MIDIQ_AVAIL(m->outq) == 0) {
829 retval = EWOULDBLOCK;
830 if (ioflag & O_NONBLOCK)
831 goto err1;
832 mtx_unlock(&m->lock);
833 m->wchan = 1;
834 MIDI_DEBUG(3, printf("midi_write msleep\n"));
835 retval = msleep(&m->wchan, &m->qlock,
836 PCATCH | PDROP, "midi TX", 0);
837 /*
838 * We slept, maybe things have changed since last
839 * dying check
840 */
841 if (retval == EINTR)
842 goto err0;
843 if (m != i_dev->si_drv1)
844 retval = ENXIO;
845 if (retval)
846 goto err0;
847 mtx_lock(&m->lock);
848 mtx_lock(&m->qlock);
849 m->wchan = 0;
850 if (!m->busy)
851 goto err1;
852 }
853
854 /*
855 * We are certain than data can be placed on the queue
856 */
857
858 used = MIN(MIDIQ_AVAIL(m->outq), uio->uio_resid);
859 used = MIN(used, MIDI_WSIZE);
860 MIDI_DEBUG(5, printf("midiout: resid %zd len %jd avail %jd\n",
861 uio->uio_resid, (intmax_t)MIDIQ_LEN(m->outq),
862 (intmax_t)MIDIQ_AVAIL(m->outq)));
863
864
865 MIDI_DEBUG(5, printf("midi_write: uiomove cc=%d\n", used));
866 retval = uiomove(buf, used, uio);
867 if (retval)
868 goto err1;
869 MIDIQ_ENQ(m->outq, buf, used);
870 /*
871 * Inform the bottom half that data can be written
872 */
873 if (!(m->flags & M_TXEN)) {
874 m->flags |= M_TXEN;
875 MPU_CALLBACK(m, m->cookie, m->flags);
876 }
877 }
878 /*
879 * If we Made it here then transfer is good
880 */
881 retval = 0;
882 err1: mtx_unlock(&m->qlock);
883 mtx_unlock(&m->lock);
884 err0: return retval;
885 }
886
887 int
888 midi_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
889 struct thread *td)
890 {
891 return ENXIO;
892 }
893
894 int
895 midi_poll(struct cdev *i_dev, int events, struct thread *td)
896 {
897 struct snd_midi *m = i_dev->si_drv1;
898 int revents;
899
900 if (m == NULL)
901 return 0;
902
903 revents = 0;
904
905 mtx_lock(&m->lock);
906 mtx_lock(&m->qlock);
907
908 if (events & (POLLIN | POLLRDNORM))
909 if (!MIDIQ_EMPTY(m->inq))
910 events |= events & (POLLIN | POLLRDNORM);
911
912 if (events & (POLLOUT | POLLWRNORM))
913 if (MIDIQ_AVAIL(m->outq) < m->hiwat)
914 events |= events & (POLLOUT | POLLWRNORM);
915
916 if (revents == 0) {
917 if (events & (POLLIN | POLLRDNORM))
918 selrecord(td, &m->rsel);
919
920 if (events & (POLLOUT | POLLWRNORM))
921 selrecord(td, &m->wsel);
922 }
923 mtx_unlock(&m->lock);
924 mtx_unlock(&m->qlock);
925
926 return (revents);
927 }
928
929 /*
930 * /dev/midistat device functions
931 *
932 */
933 static int
934 midistat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
935 {
936 int error;
937
938 MIDI_DEBUG(1, printf("midistat_open\n"));
939
940 sx_xlock(&midistat_lock);
941 if (midistat_isopen) {
942 sx_xunlock(&midistat_lock);
943 return EBUSY;
944 }
945 midistat_isopen = 1;
946 if (sbuf_new(&midistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) {
947 error = ENXIO;
948 goto out;
949 }
950 error = (midistat_prepare(&midistat_sbuf) > 0) ? 0 : ENOMEM;
951 out:
952 if (error)
953 midistat_isopen = 0;
954 sx_xunlock(&midistat_lock);
955 return error;
956 }
957
958 static int
959 midistat_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
960 {
961 MIDI_DEBUG(1, printf("midistat_close\n"));
962 sx_xlock(&midistat_lock);
963 if (!midistat_isopen) {
964 sx_xunlock(&midistat_lock);
965 return EBADF;
966 }
967 sbuf_delete(&midistat_sbuf);
968 midistat_isopen = 0;
969 sx_xunlock(&midistat_lock);
970 return 0;
971 }
972
973 static int
974 midistat_read(struct cdev *i_dev, struct uio *uio, int flag)
975 {
976 long l;
977 int err;
978
979 MIDI_DEBUG(4, printf("midistat_read\n"));
980 sx_xlock(&midistat_lock);
981 if (!midistat_isopen) {
982 sx_xunlock(&midistat_lock);
983 return EBADF;
984 }
985 if (uio->uio_offset < 0 || uio->uio_offset > sbuf_len(&midistat_sbuf)) {
986 sx_xunlock(&midistat_lock);
987 return EINVAL;
988 }
989 err = 0;
990 l = lmin(uio->uio_resid, sbuf_len(&midistat_sbuf) - uio->uio_offset);
991 if (l > 0) {
992 err = uiomove(sbuf_data(&midistat_sbuf) + uio->uio_offset, l,
993 uio);
994 }
995 sx_xunlock(&midistat_lock);
996 return err;
997 }
998
999 /*
1000 * Module library functions
1001 */
1002
1003 static int
1004 midistat_prepare(struct sbuf *s)
1005 {
1006 struct snd_midi *m;
1007
1008 sx_assert(&midistat_lock, SA_XLOCKED);
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
1036 static char *
1037 midi_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
1074 int
1075 midisynth_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
1132 err: 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
1138 int
1139 midisynth_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;
1177 err: return retval;
1178 }
1179
1180 /*
1181 * Always blocking.
1182 */
1183
1184 int
1185 midisynth_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;
1268 err1: mtx_unlock(&m->qlock);
1269 mtx_unlock(&m->lock);
1270 err0: return retval;
1271 }
1272
1273 static int
1274 midisynth_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
1298 static int
1299 midisynth_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
1312 static int
1313 midisynth_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 }
1329 static int
1330 midisynth_alloc(void *n, uint8_t chan, uint8_t note)
1331 {
1332 return chan;
1333 }
1334
1335 static int
1336 midisynth_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
1349 static int
1350 midisynth_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 */
1368 static int
1369 midi_destroy(struct snd_midi *m, int midiuninit)
1370 {
1371 sx_assert(&midistat_lock, SA_XLOCKED);
1372 mtx_assert(&m->lock, MA_OWNED);
1373
1374 MIDI_DEBUG(3, printf("midi_destroy\n"));
1375 m->dev->si_drv1 = NULL;
1376 mtx_unlock(&m->lock); /* XXX */
1377 destroy_dev(m->dev);
1378 TAILQ_REMOVE(&midi_devs, m, link);
1379 if (midiuninit)
1380 MPU_UNINIT(m, m->cookie);
1381 free(MIDIQ_BUF(m->inq), M_MIDI);
1382 free(MIDIQ_BUF(m->outq), M_MIDI);
1383 mtx_destroy(&m->qlock);
1384 mtx_destroy(&m->lock);
1385 free(m->synth, M_MIDI);
1386 free(m, M_MIDI);
1387 return 0;
1388 }
1389
1390 /*
1391 * Load and unload functions, creates the /dev/midistat device
1392 */
1393
1394 static int
1395 midi_load(void)
1396 {
1397 sx_init(&midistat_lock, "midistat lock");
1398 TAILQ_INIT(&midi_devs);
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
1407 static int
1408 midi_unload(void)
1409 {
1410 struct snd_midi *m, *tmp;
1411 int retval;
1412
1413 MIDI_DEBUG(1, printf("midi_unload()\n"));
1414 retval = EBUSY;
1415 sx_xlock(&midistat_lock);
1416 if (midistat_isopen)
1417 goto exit0;
1418
1419 TAILQ_FOREACH_SAFE(m, &midi_devs, link, tmp) {
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 sx_xunlock(&midistat_lock);
1429 destroy_dev(midistat_dev);
1430
1431 /*
1432 * Made it here then unload is complete
1433 */
1434 sx_destroy(&midistat_lock);
1435 return 0;
1436
1437 exit1:
1438 mtx_unlock(&m->lock);
1439 exit0:
1440 sx_xunlock(&midistat_lock);
1441 if (retval)
1442 MIDI_DEBUG(2, printf("midi_unload: failed\n"));
1443 return retval;
1444 }
1445
1446 extern int seq_modevent(module_t mod, int type, void *data);
1447
1448 static int
1449 midi_modevent(module_t mod, int type, void *data)
1450 {
1451 int retval;
1452
1453 retval = 0;
1454
1455 switch (type) {
1456 case MOD_LOAD:
1457 retval = midi_load();
1458 if (retval == 0)
1459 retval = seq_modevent(mod, type, data);
1460 break;
1461
1462 case MOD_UNLOAD:
1463 retval = midi_unload();
1464 if (retval == 0)
1465 retval = seq_modevent(mod, type, data);
1466 break;
1467
1468 default:
1469 break;
1470 }
1471
1472 return retval;
1473 }
1474
1475 kobj_t
1476 midimapper_addseq(void *arg1, int *unit, void **cookie)
1477 {
1478 unit = NULL;
1479
1480 return (kobj_t)arg1;
1481 }
1482
1483 int
1484 midimapper_open(void *arg1, void **cookie)
1485 {
1486 int retval = 0;
1487 struct snd_midi *m;
1488
1489 sx_xlock(&midistat_lock);
1490 TAILQ_FOREACH(m, &midi_devs, link) {
1491 retval++;
1492 }
1493 sx_xunlock(&midistat_lock);
1494 return retval;
1495 }
1496
1497 int
1498 midimapper_close(void *arg1, void *cookie)
1499 {
1500 return 0;
1501 }
1502
1503 kobj_t
1504 midimapper_fetch_synth(void *arg, void *cookie, int unit)
1505 {
1506 struct snd_midi *m;
1507 int retval = 0;
1508
1509 sx_xlock(&midistat_lock);
1510 TAILQ_FOREACH(m, &midi_devs, link) {
1511 if (unit == retval) {
1512 sx_xunlock(&midistat_lock);
1513 return (kobj_t)m->synth;
1514 }
1515 retval++;
1516 }
1517 sx_xunlock(&midistat_lock);
1518 return NULL;
1519 }
1520
1521 DEV_MODULE(midi, midi_modevent, NULL);
1522 MODULE_VERSION(midi, 1);
Cache object: 8200826bce5591db3bac202adfa03747
|