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