1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
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 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/conf.h>
36 #include <sys/fcntl.h>
37 #include <sys/kernel.h>
38 #include <sys/kobj.h>
39 #include <sys/lock.h>
40 #include <sys/module.h>
41 #include <sys/mutex.h>
42 #include <sys/poll.h>
43 #include <sys/queue.h>
44 #include <sys/selinfo.h>
45 #include <sys/sx.h>
46 #include <sys/sysctl.h>
47 #include <sys/uio.h>
48
49 #ifdef HAVE_KERNEL_OPTION_HEADERS
50 #include "opt_snd.h"
51 #endif
52
53 #include <dev/sound/midi/midi.h>
54 #include <dev/sound/midi/midiq.h>
55
56 #include "mpu_if.h"
57
58 MALLOC_DEFINE(M_MIDI, "midi buffers", "Midi data allocation area");
59
60 #define MIDI_NAMELEN 16
61 struct snd_midi {
62 KOBJ_FIELDS;
63 struct mtx lock; /* Protects all but queues */
64 void *cookie;
65
66 int unit; /* Should only be used in midistat */
67 int channel; /* Should only be used in midistat */
68
69 int busy;
70 int flags; /* File flags */
71 char name[MIDI_NAMELEN];
72 struct mtx qlock; /* Protects inq, outq and flags */
73 MIDIQ_HEAD(, char) inq, outq;
74 int rchan, wchan;
75 struct selinfo rsel, wsel;
76 int hiwat; /* QLEN(outq)>High-water -> disable
77 * writes from userland */
78 struct cdev *dev;
79 TAILQ_ENTRY(snd_midi) link;
80 };
81
82 TAILQ_HEAD(, snd_midi) midi_devs;
83
84 struct sx mstat_lock;
85
86 static d_open_t midi_open;
87 static d_close_t midi_close;
88 static d_ioctl_t midi_ioctl;
89 static d_read_t midi_read;
90 static d_write_t midi_write;
91 static d_poll_t midi_poll;
92
93 static struct cdevsw midi_cdevsw = {
94 .d_version = D_VERSION,
95 .d_open = midi_open,
96 .d_close = midi_close,
97 .d_read = midi_read,
98 .d_write = midi_write,
99 .d_ioctl = midi_ioctl,
100 .d_poll = midi_poll,
101 .d_name = "rmidi",
102 };
103
104 static int midi_destroy(struct snd_midi *, int);
105 static int midi_load(void);
106 static int midi_unload(void);
107
108 SYSCTL_NODE(_hw, OID_AUTO, midi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
109 "Midi driver");
110
111 int midi_debug;
112 /* XXX: should this be moved into debug.midi? */
113 SYSCTL_INT(_hw_midi, OID_AUTO, debug, CTLFLAG_RW, &midi_debug, 0, "");
114
115 #define MIDI_DEBUG(l,a) if(midi_debug>=l) a
116
117 void
midistat_lock(void)118 midistat_lock(void)
119 {
120 sx_xlock(&mstat_lock);
121 }
122
123 void
midistat_unlock(void)124 midistat_unlock(void)
125 {
126 sx_xunlock(&mstat_lock);
127 }
128
129 void
midistat_lockassert(void)130 midistat_lockassert(void)
131 {
132 sx_assert(&mstat_lock, SA_XLOCKED);
133 }
134
135 /*
136 * Register a new rmidi device. cls midi_if interface unit == 0 means
137 * auto-assign new unit number unit != 0 already assigned a unit number, eg.
138 * not the first channel provided by this device. channel, sub-unit
139 * cookie is passed back on MPU calls Typical device drivers will call with
140 * unit=0, channel=1..(number of channels) and cookie=soft_c and won't care
141 * what unit number is used.
142 *
143 * It is an error to call midi_init with an already used unit/channel combo.
144 */
145 struct snd_midi *
midi_init(kobj_class_t cls,int unit,int channel,void * cookie)146 midi_init(kobj_class_t cls, int unit, int channel, void *cookie)
147 {
148 struct snd_midi *m;
149 int i;
150 int inqsize, outqsize;
151 uint8_t *buf;
152
153 MIDI_DEBUG(1, printf("midiinit: unit %d/%d.\n", unit, channel));
154 midistat_lock();
155 /*
156 * Protect against call with existing unit/channel or auto-allocate a
157 * new unit number.
158 */
159 i = -1;
160 TAILQ_FOREACH(m, &midi_devs, link) {
161 mtx_lock(&m->lock);
162 if (unit != 0) {
163 if (m->unit == unit && m->channel == channel) {
164 mtx_unlock(&m->lock);
165 goto err0;
166 }
167 } else {
168 /*
169 * Find a better unit number
170 */
171 if (m->unit > i)
172 i = m->unit;
173 }
174 mtx_unlock(&m->lock);
175 }
176
177 if (unit == 0)
178 unit = i + 1;
179
180 MIDI_DEBUG(1, printf("midiinit #2: unit %d/%d.\n", unit, channel));
181 m = malloc(sizeof(*m), M_MIDI, M_WAITOK | M_ZERO);
182 kobj_init((kobj_t)m, cls);
183 inqsize = MPU_INQSIZE(m, cookie);
184 outqsize = MPU_OUTQSIZE(m, cookie);
185
186 MIDI_DEBUG(1, printf("midiinit queues %d/%d.\n", inqsize, outqsize));
187 if (!inqsize && !outqsize)
188 goto err1;
189
190 mtx_init(&m->lock, "raw midi", NULL, 0);
191 mtx_init(&m->qlock, "q raw midi", NULL, 0);
192
193 mtx_lock(&m->lock);
194 mtx_lock(&m->qlock);
195
196 if (inqsize)
197 buf = malloc(sizeof(uint8_t) * inqsize, M_MIDI, M_NOWAIT);
198 else
199 buf = NULL;
200
201 MIDIQ_INIT(m->inq, buf, inqsize);
202
203 if (outqsize)
204 buf = malloc(sizeof(uint8_t) * outqsize, M_MIDI, M_NOWAIT);
205 else
206 buf = NULL;
207 m->hiwat = outqsize / 2;
208
209 MIDIQ_INIT(m->outq, buf, outqsize);
210
211 if ((inqsize && !MIDIQ_BUF(m->inq)) ||
212 (outqsize && !MIDIQ_BUF(m->outq)))
213 goto err2;
214
215 m->busy = 0;
216 m->flags = 0;
217 m->unit = unit;
218 m->channel = channel;
219 m->cookie = cookie;
220
221 if (MPU_INIT(m, cookie))
222 goto err2;
223
224 mtx_unlock(&m->lock);
225 mtx_unlock(&m->qlock);
226
227 TAILQ_INSERT_TAIL(&midi_devs, m, link);
228
229 midistat_unlock();
230
231 m->dev = make_dev(&midi_cdevsw, unit, UID_ROOT, GID_WHEEL, 0666,
232 "midi%d.%d", unit, channel);
233 m->dev->si_drv1 = m;
234
235 return m;
236
237 err2:
238 mtx_destroy(&m->qlock);
239 mtx_destroy(&m->lock);
240
241 if (MIDIQ_BUF(m->inq))
242 free(MIDIQ_BUF(m->inq), M_MIDI);
243 if (MIDIQ_BUF(m->outq))
244 free(MIDIQ_BUF(m->outq), M_MIDI);
245 err1:
246 free(m, M_MIDI);
247 err0:
248 midistat_unlock();
249 MIDI_DEBUG(1, printf("midi_init ended in error\n"));
250 return NULL;
251 }
252
253 /*
254 * midi_uninit does not call MIDI_UNINIT, as since this is the implementors
255 * entry point. midi_uninit if fact, does not send any methods. A call to
256 * midi_uninit is a defacto promise that you won't manipulate ch anymore
257 */
258 int
midi_uninit(struct snd_midi * m)259 midi_uninit(struct snd_midi *m)
260 {
261 int err;
262
263 err = EBUSY;
264 midistat_lock();
265 mtx_lock(&m->lock);
266 if (m->busy) {
267 if (!(m->rchan || m->wchan))
268 goto err;
269
270 if (m->rchan) {
271 wakeup(&m->rchan);
272 m->rchan = 0;
273 }
274 if (m->wchan) {
275 wakeup(&m->wchan);
276 m->wchan = 0;
277 }
278 }
279 err = midi_destroy(m, 0);
280 if (!err)
281 goto exit;
282
283 err:
284 mtx_unlock(&m->lock);
285 exit:
286 midistat_unlock();
287 return err;
288 }
289
290 #ifdef notdef
291 static int midi_lengths[] = {2, 2, 2, 2, 1, 1, 2, 0};
292
293 #endif /* notdef */
294 /* Number of bytes in a MIDI command */
295 #define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7])
296 #define MIDI_ACK 0xfe
297 #define MIDI_IS_STATUS(d) ((d) >= 0x80)
298 #define MIDI_IS_COMMON(d) ((d) >= 0xf0)
299
300 #define MIDI_SYSEX_START 0xF0
301 #define MIDI_SYSEX_END 0xF7
302
303 /*
304 * midi_in: process all data until the queue is full, then discards the rest.
305 * Since midi_in is a state machine, data discards can cause it to get out of
306 * whack. Process as much as possible. It calls, wakeup, selnotify and
307 * psignal at most once.
308 */
309 int
midi_in(struct snd_midi * m,uint8_t * buf,int size)310 midi_in(struct snd_midi *m, uint8_t *buf, int size)
311 {
312 int used;
313
314 MIDI_DEBUG(5, printf("midi_in: m=%p size=%d\n", m, size));
315
316 /*
317 * XXX: locking flub
318 */
319 if (!(m->flags & M_RX))
320 return size;
321
322 used = 0;
323
324 mtx_lock(&m->qlock);
325 MIDI_DEBUG(6, printf("midi_in: len %jd avail %jd\n",
326 (intmax_t)MIDIQ_LEN(m->inq),
327 (intmax_t)MIDIQ_AVAIL(m->inq)));
328 if (MIDIQ_AVAIL(m->inq) > size) {
329 used = size;
330 MIDIQ_ENQ(m->inq, buf, size);
331 } else {
332 MIDI_DEBUG(4, printf("midi_in: Discarding data qu\n"));
333 mtx_unlock(&m->qlock);
334 return 0;
335 }
336 if (m->rchan) {
337 wakeup(&m->rchan);
338 m->rchan = 0;
339 }
340 selwakeup(&m->rsel);
341 mtx_unlock(&m->qlock);
342 return used;
343 }
344
345 /*
346 * midi_out: The only clearer of the M_TXEN flag.
347 */
348 int
midi_out(struct snd_midi * m,uint8_t * buf,int size)349 midi_out(struct snd_midi *m, uint8_t *buf, int size)
350 {
351 int used;
352
353 /*
354 * XXX: locking flub
355 */
356 if (!(m->flags & M_TXEN))
357 return 0;
358
359 MIDI_DEBUG(2, printf("midi_out: %p\n", m));
360 mtx_lock(&m->qlock);
361 used = MIN(size, MIDIQ_LEN(m->outq));
362 MIDI_DEBUG(3, printf("midi_out: used %d\n", used));
363 if (used)
364 MIDIQ_DEQ(m->outq, buf, used);
365 if (MIDIQ_EMPTY(m->outq)) {
366 m->flags &= ~M_TXEN;
367 MPU_CALLBACKP(m, m->cookie, m->flags);
368 }
369 if (used && MIDIQ_AVAIL(m->outq) > m->hiwat) {
370 if (m->wchan) {
371 wakeup(&m->wchan);
372 m->wchan = 0;
373 }
374 selwakeup(&m->wsel);
375 }
376 mtx_unlock(&m->qlock);
377 return used;
378 }
379
380 int
midi_open(struct cdev * i_dev,int flags,int mode,struct thread * td)381 midi_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
382 {
383 struct snd_midi *m = i_dev->si_drv1;
384 int retval;
385
386 MIDI_DEBUG(1, printf("midiopen %p %s %s\n", td,
387 flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
388 if (m == NULL)
389 return ENXIO;
390
391 mtx_lock(&m->lock);
392 mtx_lock(&m->qlock);
393
394 retval = 0;
395
396 if (flags & FREAD) {
397 if (MIDIQ_SIZE(m->inq) == 0)
398 retval = ENXIO;
399 else if (m->flags & M_RX)
400 retval = EBUSY;
401 if (retval)
402 goto err;
403 }
404 if (flags & FWRITE) {
405 if (MIDIQ_SIZE(m->outq) == 0)
406 retval = ENXIO;
407 else if (m->flags & M_TX)
408 retval = EBUSY;
409 if (retval)
410 goto err;
411 }
412 m->busy++;
413
414 m->rchan = 0;
415 m->wchan = 0;
416
417 if (flags & FREAD) {
418 m->flags |= M_RX | M_RXEN;
419 /*
420 * Only clear the inq, the outq might still have data to drain
421 * from a previous session
422 */
423 MIDIQ_CLEAR(m->inq);
424 }
425
426 if (flags & FWRITE)
427 m->flags |= M_TX;
428
429 MPU_CALLBACK(m, m->cookie, m->flags);
430
431 MIDI_DEBUG(2, printf("midi_open: opened.\n"));
432
433 err: mtx_unlock(&m->qlock);
434 mtx_unlock(&m->lock);
435 return retval;
436 }
437
438 int
midi_close(struct cdev * i_dev,int flags,int mode,struct thread * td)439 midi_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
440 {
441 struct snd_midi *m = i_dev->si_drv1;
442 int retval;
443 int oldflags;
444
445 MIDI_DEBUG(1, printf("midi_close %p %s %s\n", td,
446 flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
447
448 if (m == NULL)
449 return ENXIO;
450
451 mtx_lock(&m->lock);
452 mtx_lock(&m->qlock);
453
454 if ((flags & FREAD && !(m->flags & M_RX)) ||
455 (flags & FWRITE && !(m->flags & M_TX))) {
456 retval = ENXIO;
457 goto err;
458 }
459 m->busy--;
460
461 oldflags = m->flags;
462
463 if (flags & FREAD)
464 m->flags &= ~(M_RX | M_RXEN);
465 if (flags & FWRITE)
466 m->flags &= ~M_TX;
467
468 if ((m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN)))
469 MPU_CALLBACK(m, m->cookie, m->flags);
470
471 MIDI_DEBUG(1, printf("midi_close: closed, busy = %d.\n", m->busy));
472
473 mtx_unlock(&m->qlock);
474 mtx_unlock(&m->lock);
475 retval = 0;
476 err: return retval;
477 }
478
479 /*
480 * TODO: midi_read, per oss programmer's guide pg. 42 should return as soon
481 * as data is available.
482 */
483 int
midi_read(struct cdev * i_dev,struct uio * uio,int ioflag)484 midi_read(struct cdev *i_dev, struct uio *uio, int ioflag)
485 {
486 #define MIDI_RSIZE 32
487 struct snd_midi *m = i_dev->si_drv1;
488 int retval;
489 int used;
490 char buf[MIDI_RSIZE];
491
492 MIDI_DEBUG(5, printf("midiread: count=%lu\n",
493 (unsigned long)uio->uio_resid));
494
495 retval = EIO;
496
497 if (m == NULL)
498 goto err0;
499
500 mtx_lock(&m->lock);
501 mtx_lock(&m->qlock);
502
503 if (!(m->flags & M_RX))
504 goto err1;
505
506 while (uio->uio_resid > 0) {
507 while (MIDIQ_EMPTY(m->inq)) {
508 retval = EWOULDBLOCK;
509 if (ioflag & O_NONBLOCK)
510 goto err1;
511 mtx_unlock(&m->lock);
512 m->rchan = 1;
513 retval = msleep(&m->rchan, &m->qlock,
514 PCATCH | PDROP, "midi RX", 0);
515 /*
516 * We slept, maybe things have changed since last
517 * dying check
518 */
519 if (retval == EINTR)
520 goto err0;
521 if (m != i_dev->si_drv1)
522 retval = ENXIO;
523 /* if (retval && retval != ERESTART) */
524 if (retval)
525 goto err0;
526 mtx_lock(&m->lock);
527 mtx_lock(&m->qlock);
528 m->rchan = 0;
529 if (!m->busy)
530 goto err1;
531 }
532 MIDI_DEBUG(6, printf("midi_read start\n"));
533 /*
534 * At this point, it is certain that m->inq has data
535 */
536
537 used = MIN(MIDIQ_LEN(m->inq), uio->uio_resid);
538 used = MIN(used, MIDI_RSIZE);
539
540 MIDI_DEBUG(6, printf("midiread: uiomove cc=%d\n", used));
541 MIDIQ_DEQ(m->inq, buf, used);
542 retval = uiomove(buf, used, uio);
543 if (retval)
544 goto err1;
545 }
546
547 /*
548 * If we Made it here then transfer is good
549 */
550 retval = 0;
551 err1: mtx_unlock(&m->qlock);
552 mtx_unlock(&m->lock);
553 err0: MIDI_DEBUG(4, printf("midi_read: ret %d\n", retval));
554 return retval;
555 }
556
557 /*
558 * midi_write: The only setter of M_TXEN
559 */
560
561 int
midi_write(struct cdev * i_dev,struct uio * uio,int ioflag)562 midi_write(struct cdev *i_dev, struct uio *uio, int ioflag)
563 {
564 #define MIDI_WSIZE 32
565 struct snd_midi *m = i_dev->si_drv1;
566 int retval;
567 int used;
568 char buf[MIDI_WSIZE];
569
570 MIDI_DEBUG(4, printf("midi_write\n"));
571 retval = 0;
572 if (m == NULL)
573 goto err0;
574
575 mtx_lock(&m->lock);
576 mtx_lock(&m->qlock);
577
578 if (!(m->flags & M_TX))
579 goto err1;
580
581 while (uio->uio_resid > 0) {
582 while (MIDIQ_AVAIL(m->outq) == 0) {
583 retval = EWOULDBLOCK;
584 if (ioflag & O_NONBLOCK)
585 goto err1;
586 mtx_unlock(&m->lock);
587 m->wchan = 1;
588 MIDI_DEBUG(3, printf("midi_write msleep\n"));
589 retval = msleep(&m->wchan, &m->qlock,
590 PCATCH | PDROP, "midi TX", 0);
591 /*
592 * We slept, maybe things have changed since last
593 * dying check
594 */
595 if (retval == EINTR)
596 goto err0;
597 if (m != i_dev->si_drv1)
598 retval = ENXIO;
599 if (retval)
600 goto err0;
601 mtx_lock(&m->lock);
602 mtx_lock(&m->qlock);
603 m->wchan = 0;
604 if (!m->busy)
605 goto err1;
606 }
607
608 /*
609 * We are certain than data can be placed on the queue
610 */
611
612 used = MIN(MIDIQ_AVAIL(m->outq), uio->uio_resid);
613 used = MIN(used, MIDI_WSIZE);
614 MIDI_DEBUG(5, printf("midiout: resid %zd len %jd avail %jd\n",
615 uio->uio_resid, (intmax_t)MIDIQ_LEN(m->outq),
616 (intmax_t)MIDIQ_AVAIL(m->outq)));
617
618 MIDI_DEBUG(5, printf("midi_write: uiomove cc=%d\n", used));
619 retval = uiomove(buf, used, uio);
620 if (retval)
621 goto err1;
622 MIDIQ_ENQ(m->outq, buf, used);
623 /*
624 * Inform the bottom half that data can be written
625 */
626 if (!(m->flags & M_TXEN)) {
627 m->flags |= M_TXEN;
628 MPU_CALLBACK(m, m->cookie, m->flags);
629 }
630 }
631 /*
632 * If we Made it here then transfer is good
633 */
634 retval = 0;
635 err1: mtx_unlock(&m->qlock);
636 mtx_unlock(&m->lock);
637 err0: return retval;
638 }
639
640 int
midi_ioctl(struct cdev * i_dev,u_long cmd,caddr_t arg,int mode,struct thread * td)641 midi_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
642 struct thread *td)
643 {
644 return ENXIO;
645 }
646
647 int
midi_poll(struct cdev * i_dev,int events,struct thread * td)648 midi_poll(struct cdev *i_dev, int events, struct thread *td)
649 {
650 struct snd_midi *m = i_dev->si_drv1;
651 int revents;
652
653 if (m == NULL)
654 return 0;
655
656 revents = 0;
657
658 mtx_lock(&m->lock);
659 mtx_lock(&m->qlock);
660
661 if (events & (POLLIN | POLLRDNORM))
662 if (!MIDIQ_EMPTY(m->inq))
663 events |= events & (POLLIN | POLLRDNORM);
664
665 if (events & (POLLOUT | POLLWRNORM))
666 if (MIDIQ_AVAIL(m->outq) < m->hiwat)
667 events |= events & (POLLOUT | POLLWRNORM);
668
669 if (revents == 0) {
670 if (events & (POLLIN | POLLRDNORM))
671 selrecord(td, &m->rsel);
672
673 if (events & (POLLOUT | POLLWRNORM))
674 selrecord(td, &m->wsel);
675 }
676 mtx_unlock(&m->lock);
677 mtx_unlock(&m->qlock);
678
679 return (revents);
680 }
681
682 /*
683 * Single point of midi destructions.
684 */
685 static int
midi_destroy(struct snd_midi * m,int midiuninit)686 midi_destroy(struct snd_midi *m, int midiuninit)
687 {
688 midistat_lockassert();
689 mtx_assert(&m->lock, MA_OWNED);
690
691 MIDI_DEBUG(3, printf("midi_destroy\n"));
692 m->dev->si_drv1 = NULL;
693 mtx_unlock(&m->lock); /* XXX */
694 destroy_dev(m->dev);
695 TAILQ_REMOVE(&midi_devs, m, link);
696 if (midiuninit)
697 MPU_UNINIT(m, m->cookie);
698 free(MIDIQ_BUF(m->inq), M_MIDI);
699 free(MIDIQ_BUF(m->outq), M_MIDI);
700 mtx_destroy(&m->qlock);
701 mtx_destroy(&m->lock);
702 free(m, M_MIDI);
703 return 0;
704 }
705
706 static int
midi_load(void)707 midi_load(void)
708 {
709 sx_init(&mstat_lock, "midistat lock");
710 TAILQ_INIT(&midi_devs);
711
712 return 0;
713 }
714
715 static int
midi_unload(void)716 midi_unload(void)
717 {
718 struct snd_midi *m, *tmp;
719 int retval;
720
721 MIDI_DEBUG(1, printf("midi_unload()\n"));
722 retval = EBUSY;
723 midistat_lock();
724 TAILQ_FOREACH_SAFE(m, &midi_devs, link, tmp) {
725 mtx_lock(&m->lock);
726 if (m->busy)
727 retval = EBUSY;
728 else
729 retval = midi_destroy(m, 1);
730 if (retval)
731 goto exit;
732 }
733 midistat_unlock();
734
735 sx_destroy(&mstat_lock);
736 return 0;
737
738 exit:
739 mtx_unlock(&m->lock);
740 midistat_unlock();
741 if (retval)
742 MIDI_DEBUG(2, printf("midi_unload: failed\n"));
743 return retval;
744 }
745
746 static int
midi_modevent(module_t mod,int type,void * data)747 midi_modevent(module_t mod, int type, void *data)
748 {
749 int retval;
750
751 retval = 0;
752
753 switch (type) {
754 case MOD_LOAD:
755 retval = midi_load();
756 break;
757
758 case MOD_UNLOAD:
759 retval = midi_unload();
760 break;
761
762 default:
763 break;
764 }
765
766 return retval;
767 }
768
769 DEV_MODULE(midi, midi_modevent, NULL);
770 MODULE_VERSION(midi, 1);
771