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