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