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