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 118 midistat_lock(void) 119 { 120 sx_xlock(&mstat_lock); 121 } 122 123 void 124 midistat_unlock(void) 125 { 126 sx_xunlock(&mstat_lock); 127 } 128 129 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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