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