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 sbuf_new(&midistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND); 954 error = (midistat_prepare(&midistat_sbuf) > 0) ? 0 : ENOMEM; 955 if (error) 956 midistat_isopen = 0; 957 midistat_unlock(); 958 return error; 959 } 960 961 static int 962 midistat_close(struct cdev *i_dev, int flags, int mode, struct thread *td) 963 { 964 MIDI_DEBUG(1, printf("midistat_close\n")); 965 midistat_lock(); 966 if (!midistat_isopen) { 967 midistat_unlock(); 968 return EBADF; 969 } 970 sbuf_delete(&midistat_sbuf); 971 midistat_isopen = 0; 972 midistat_unlock(); 973 return 0; 974 } 975 976 static int 977 midistat_read(struct cdev *i_dev, struct uio *uio, int flag) 978 { 979 long l; 980 int err; 981 982 MIDI_DEBUG(4, printf("midistat_read\n")); 983 midistat_lock(); 984 if (!midistat_isopen) { 985 midistat_unlock(); 986 return EBADF; 987 } 988 if (uio->uio_offset < 0 || uio->uio_offset > sbuf_len(&midistat_sbuf)) { 989 midistat_unlock(); 990 return EINVAL; 991 } 992 err = 0; 993 l = lmin(uio->uio_resid, sbuf_len(&midistat_sbuf) - uio->uio_offset); 994 if (l > 0) { 995 err = uiomove(sbuf_data(&midistat_sbuf) + uio->uio_offset, l, 996 uio); 997 } 998 midistat_unlock(); 999 return err; 1000 } 1001 1002 /* 1003 * Module library functions 1004 */ 1005 1006 static int 1007 midistat_prepare(struct sbuf *s) 1008 { 1009 struct snd_midi *m; 1010 1011 midistat_lockassert(); 1012 1013 sbuf_printf(s, "FreeBSD Midi Driver (midi2)\n"); 1014 if (TAILQ_EMPTY(&midi_devs)) { 1015 sbuf_printf(s, "No devices installed.\n"); 1016 sbuf_finish(s); 1017 return sbuf_len(s); 1018 } 1019 sbuf_printf(s, "Installed devices:\n"); 1020 1021 TAILQ_FOREACH(m, &midi_devs, link) { 1022 mtx_lock(&m->lock); 1023 sbuf_printf(s, "%s [%d/%d:%s]", m->name, m->unit, m->channel, 1024 MPU_PROVIDER(m, m->cookie)); 1025 sbuf_printf(s, "%s", MPU_DESCR(m, m->cookie, midistat_verbose)); 1026 sbuf_printf(s, "\n"); 1027 mtx_unlock(&m->lock); 1028 } 1029 1030 sbuf_finish(s); 1031 return sbuf_len(s); 1032 } 1033 1034 #ifdef notdef 1035 /* 1036 * Convert IOCTL command to string for debugging 1037 */ 1038 1039 static char * 1040 midi_cmdname(int cmd) 1041 { 1042 static struct { 1043 int cmd; 1044 char *name; 1045 } *tab, cmdtab_midiioctl[] = { 1046 #define A(x) {x, ## x} 1047 /* 1048 * Once we have some real IOCTLs define, the following will 1049 * be relavant. 1050 * 1051 * A(SNDCTL_MIDI_PRETIME), A(SNDCTL_MIDI_MPUMODE), 1052 * A(SNDCTL_MIDI_MPUCMD), A(SNDCTL_SYNTH_INFO), 1053 * A(SNDCTL_MIDI_INFO), A(SNDCTL_SYNTH_MEMAVL), 1054 * A(SNDCTL_FM_LOAD_INSTR), A(SNDCTL_FM_4OP_ENABLE), 1055 * A(MIOSPASSTHRU), A(MIOGPASSTHRU), A(AIONWRITE), 1056 * A(AIOGSIZE), A(AIOSSIZE), A(AIOGFMT), A(AIOSFMT), 1057 * A(AIOGMIX), A(AIOSMIX), A(AIOSTOP), A(AIOSYNC), 1058 * A(AIOGCAP), 1059 */ 1060 #undef A 1061 { 1062 -1, "unknown" 1063 }, 1064 }; 1065 1066 for (tab = cmdtab_midiioctl; tab->cmd != cmd && tab->cmd != -1; tab++); 1067 return tab->name; 1068 } 1069 1070 #endif /* notdef */ 1071 1072 /* 1073 * midisynth 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 err: mtx_unlock(&m->qlock); 1134 mtx_unlock(&m->lock); 1135 MIDI_DEBUG(2, printf("midisynth_open: return %d.\n", retval)); 1136 return retval; 1137 } 1138 1139 int 1140 midisynth_close(void *n) 1141 { 1142 struct snd_midi *m = ((struct synth_midi *)n)->m; 1143 int retval; 1144 int oldflags; 1145 1146 MIDI_DEBUG(1, printf("midisynth_close %s %s\n", 1147 m->synth_flags & FREAD ? "M_RX" : "", 1148 m->synth_flags & FWRITE ? "M_TX" : "")); 1149 1150 if (m == NULL) 1151 return ENXIO; 1152 1153 mtx_lock(&m->lock); 1154 mtx_lock(&m->qlock); 1155 1156 if ((m->synth_flags & FREAD && !(m->flags & M_RX)) || 1157 (m->synth_flags & FWRITE && !(m->flags & M_TX))) { 1158 retval = ENXIO; 1159 goto err; 1160 } 1161 m->busy--; 1162 1163 oldflags = m->flags; 1164 1165 if (m->synth_flags & FREAD) 1166 m->flags &= ~(M_RX | M_RXEN); 1167 if (m->synth_flags & FWRITE) 1168 m->flags &= ~M_TX; 1169 1170 if ((m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN))) 1171 MPU_CALLBACK(m, m->cookie, m->flags); 1172 1173 MIDI_DEBUG(1, printf("midi_close: closed, busy = %d.\n", m->busy)); 1174 1175 mtx_unlock(&m->qlock); 1176 mtx_unlock(&m->lock); 1177 retval = 0; 1178 err: return retval; 1179 } 1180 1181 /* 1182 * Always blocking. 1183 */ 1184 1185 int 1186 midisynth_writeraw(void *n, uint8_t *buf, size_t len) 1187 { 1188 struct snd_midi *m = ((struct synth_midi *)n)->m; 1189 int retval; 1190 int used; 1191 int i; 1192 1193 MIDI_DEBUG(4, printf("midisynth_writeraw\n")); 1194 1195 retval = 0; 1196 1197 if (m == NULL) 1198 return ENXIO; 1199 1200 mtx_lock(&m->lock); 1201 mtx_lock(&m->qlock); 1202 1203 if (!(m->flags & M_TX)) 1204 goto err1; 1205 1206 if (midi_dumpraw) 1207 printf("midi dump: "); 1208 1209 while (len > 0) { 1210 while (MIDIQ_AVAIL(m->outq) == 0) { 1211 if (!(m->flags & M_TXEN)) { 1212 m->flags |= M_TXEN; 1213 MPU_CALLBACK(m, m->cookie, m->flags); 1214 } 1215 mtx_unlock(&m->lock); 1216 m->wchan = 1; 1217 MIDI_DEBUG(3, printf("midisynth_writeraw msleep\n")); 1218 retval = msleep(&m->wchan, &m->qlock, 1219 PCATCH | PDROP, "midi TX", 0); 1220 /* 1221 * We slept, maybe things have changed since last 1222 * dying check 1223 */ 1224 if (retval == EINTR) 1225 goto err0; 1226 1227 if (retval) 1228 goto err0; 1229 mtx_lock(&m->lock); 1230 mtx_lock(&m->qlock); 1231 m->wchan = 0; 1232 if (!m->busy) 1233 goto err1; 1234 } 1235 1236 /* 1237 * We are certain than data can be placed on the queue 1238 */ 1239 1240 used = MIN(MIDIQ_AVAIL(m->outq), len); 1241 used = MIN(used, MIDI_WSIZE); 1242 MIDI_DEBUG(5, 1243 printf("midi_synth: resid %zu len %jd avail %jd\n", 1244 len, (intmax_t)MIDIQ_LEN(m->outq), 1245 (intmax_t)MIDIQ_AVAIL(m->outq))); 1246 1247 if (midi_dumpraw) 1248 for (i = 0; i < used; i++) 1249 printf("%x ", buf[i]); 1250 1251 MIDIQ_ENQ(m->outq, buf, used); 1252 len -= used; 1253 1254 /* 1255 * Inform the bottom half that data can be written 1256 */ 1257 if (!(m->flags & M_TXEN)) { 1258 m->flags |= M_TXEN; 1259 MPU_CALLBACK(m, m->cookie, m->flags); 1260 } 1261 } 1262 /* 1263 * If we Made it here then transfer is good 1264 */ 1265 if (midi_dumpraw) 1266 printf("\n"); 1267 1268 retval = 0; 1269 err1: mtx_unlock(&m->qlock); 1270 mtx_unlock(&m->lock); 1271 err0: return retval; 1272 } 1273 1274 static int 1275 midisynth_killnote(void *n, uint8_t chn, uint8_t note, uint8_t vel) 1276 { 1277 u_char c[3]; 1278 1279 if (note > 127 || chn > 15) 1280 return (EINVAL); 1281 1282 if (vel > 127) 1283 vel = 127; 1284 1285 if (vel == 64) { 1286 c[0] = 0x90 | (chn & 0x0f); /* Note on. */ 1287 c[1] = (u_char)note; 1288 c[2] = 0; 1289 } else { 1290 c[0] = 0x80 | (chn & 0x0f); /* Note off. */ 1291 c[1] = (u_char)note; 1292 c[2] = (u_char)vel; 1293 } 1294 1295 return midisynth_writeraw(n, c, 3); 1296 } 1297 1298 static int 1299 midisynth_setinstr(void *n, uint8_t chn, uint16_t instr) 1300 { 1301 u_char c[2]; 1302 1303 if (instr > 127 || chn > 15) 1304 return EINVAL; 1305 1306 c[0] = 0xc0 | (chn & 0x0f); /* Progamme change. */ 1307 c[1] = instr + midi_instroff; 1308 1309 return midisynth_writeraw(n, c, 2); 1310 } 1311 1312 static int 1313 midisynth_startnote(void *n, uint8_t chn, uint8_t note, uint8_t vel) 1314 { 1315 u_char c[3]; 1316 1317 if (note > 127 || chn > 15) 1318 return EINVAL; 1319 1320 if (vel > 127) 1321 vel = 127; 1322 1323 c[0] = 0x90 | (chn & 0x0f); /* Note on. */ 1324 c[1] = (u_char)note; 1325 c[2] = (u_char)vel; 1326 1327 return midisynth_writeraw(n, c, 3); 1328 } 1329 static int 1330 midisynth_alloc(void *n, uint8_t chan, uint8_t note) 1331 { 1332 return chan; 1333 } 1334 1335 static int 1336 midisynth_controller(void *n, uint8_t chn, uint8_t ctrlnum, uint16_t val) 1337 { 1338 u_char c[3]; 1339 1340 if (ctrlnum > 127 || chn > 15) 1341 return EINVAL; 1342 1343 c[0] = 0xb0 | (chn & 0x0f); /* Control Message. */ 1344 c[1] = ctrlnum; 1345 c[2] = val; 1346 return midisynth_writeraw(n, c, 3); 1347 } 1348 1349 static int 1350 midisynth_bender(void *n, uint8_t chn, uint16_t val) 1351 { 1352 u_char c[3]; 1353 1354 if (val > 16383 || chn > 15) 1355 return EINVAL; 1356 1357 c[0] = 0xe0 | (chn & 0x0f); /* Pitch bend. */ 1358 c[1] = (u_char)val & 0x7f; 1359 c[2] = (u_char)(val >> 7) & 0x7f; 1360 1361 return midisynth_writeraw(n, c, 3); 1362 } 1363 1364 /* 1365 * Single point of midi destructions. 1366 */ 1367 static int 1368 midi_destroy(struct snd_midi *m, int midiuninit) 1369 { 1370 midistat_lockassert(); 1371 mtx_assert(&m->lock, MA_OWNED); 1372 1373 MIDI_DEBUG(3, printf("midi_destroy\n")); 1374 m->dev->si_drv1 = NULL; 1375 mtx_unlock(&m->lock); /* XXX */ 1376 destroy_dev(m->dev); 1377 TAILQ_REMOVE(&midi_devs, m, link); 1378 if (midiuninit) 1379 MPU_UNINIT(m, m->cookie); 1380 free(MIDIQ_BUF(m->inq), M_MIDI); 1381 free(MIDIQ_BUF(m->outq), M_MIDI); 1382 mtx_destroy(&m->qlock); 1383 mtx_destroy(&m->lock); 1384 free(m->synth, M_MIDI); 1385 free(m, M_MIDI); 1386 return 0; 1387 } 1388 1389 /* 1390 * Load and unload functions, creates the /dev/midistat device 1391 */ 1392 1393 static int 1394 midi_load(void) 1395 { 1396 sx_init(&mstat_lock, "midistat lock"); 1397 TAILQ_INIT(&midi_devs); 1398 1399 midistat_dev = make_dev(&midistat_cdevsw, MIDI_DEV_MIDICTL, UID_ROOT, 1400 GID_WHEEL, 0666, "midistat"); 1401 1402 return 0; 1403 } 1404 1405 static int 1406 midi_unload(void) 1407 { 1408 struct snd_midi *m, *tmp; 1409 int retval; 1410 1411 MIDI_DEBUG(1, printf("midi_unload()\n")); 1412 retval = EBUSY; 1413 midistat_lock(); 1414 if (midistat_isopen) 1415 goto exit0; 1416 1417 TAILQ_FOREACH_SAFE(m, &midi_devs, link, tmp) { 1418 mtx_lock(&m->lock); 1419 if (m->busy) 1420 retval = EBUSY; 1421 else 1422 retval = midi_destroy(m, 1); 1423 if (retval) 1424 goto exit1; 1425 } 1426 midistat_unlock(); 1427 destroy_dev(midistat_dev); 1428 1429 /* 1430 * Made it here then unload is complete 1431 */ 1432 sx_destroy(&mstat_lock); 1433 return 0; 1434 1435 exit1: 1436 mtx_unlock(&m->lock); 1437 exit0: 1438 midistat_unlock(); 1439 if (retval) 1440 MIDI_DEBUG(2, printf("midi_unload: failed\n")); 1441 return retval; 1442 } 1443 1444 extern int seq_modevent(module_t mod, int type, void *data); 1445 1446 static int 1447 midi_modevent(module_t mod, int type, void *data) 1448 { 1449 int retval; 1450 1451 retval = 0; 1452 1453 switch (type) { 1454 case MOD_LOAD: 1455 retval = midi_load(); 1456 if (retval == 0) 1457 retval = seq_modevent(mod, type, data); 1458 break; 1459 1460 case MOD_UNLOAD: 1461 retval = midi_unload(); 1462 if (retval == 0) 1463 retval = seq_modevent(mod, type, data); 1464 break; 1465 1466 default: 1467 break; 1468 } 1469 1470 return retval; 1471 } 1472 1473 kobj_t 1474 midimapper_addseq(void *arg1, int *unit, void **cookie) 1475 { 1476 unit = NULL; 1477 1478 return (kobj_t)arg1; 1479 } 1480 1481 int 1482 midimapper_open_locked(void *arg1, void **cookie) 1483 { 1484 int retval = 0; 1485 struct snd_midi *m; 1486 1487 midistat_lockassert(); 1488 TAILQ_FOREACH(m, &midi_devs, link) { 1489 retval++; 1490 } 1491 1492 return retval; 1493 } 1494 1495 int 1496 midimapper_open(void *arg1, void **cookie) 1497 { 1498 int retval; 1499 1500 midistat_lock(); 1501 retval = midimapper_open_locked(arg1, cookie); 1502 midistat_unlock(); 1503 1504 return retval; 1505 } 1506 1507 int 1508 midimapper_close(void *arg1, void *cookie) 1509 { 1510 return 0; 1511 } 1512 1513 kobj_t 1514 midimapper_fetch_synth_locked(void *arg, void *cookie, int unit) 1515 { 1516 struct snd_midi *m; 1517 int retval = 0; 1518 1519 midistat_lockassert(); 1520 TAILQ_FOREACH(m, &midi_devs, link) { 1521 if (unit == retval) 1522 return (kobj_t)m->synth; 1523 retval++; 1524 } 1525 1526 return NULL; 1527 } 1528 1529 kobj_t 1530 midimapper_fetch_synth(void *arg, void *cookie, int unit) 1531 { 1532 kobj_t synth; 1533 1534 midistat_lock(); 1535 synth = midimapper_fetch_synth_locked(arg, cookie, unit); 1536 midistat_unlock(); 1537 1538 return synth; 1539 } 1540 1541 DEV_MODULE(midi, midi_modevent, NULL); 1542 MODULE_VERSION(midi, 1); 1543