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