1 /* $OpenBSD: midi.c,v 1.6 2001/03/23 00:13:23 mickey Exp $ */ 2 /* $NetBSD: midi.c,v 1.10 1998/12/20 14:26:44 drochner Exp $ */ 3 4 /* 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 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 #include "midi.h" 41 #include "audio.h" 42 #include "sequencer.h" 43 44 #include <sys/param.h> 45 #include <sys/ioctl.h> 46 #include <sys/fcntl.h> 47 #include <sys/vnode.h> 48 #include <sys/select.h> 49 #include <sys/poll.h> 50 #include <sys/malloc.h> 51 #include <sys/proc.h> 52 #include <sys/systm.h> 53 #include <sys/syslog.h> 54 #include <sys/kernel.h> 55 #include <sys/signalvar.h> 56 #include <sys/conf.h> 57 #include <sys/audioio.h> 58 #include <sys/midiio.h> 59 #include <sys/device.h> 60 61 #include <dev/audio_if.h> 62 #include <dev/midi_if.h> 63 #include <dev/midivar.h> 64 65 #if NMIDI > 0 66 67 #ifdef AUDIO_DEBUG 68 #define DPRINTF(x) if (mididebug) printf x 69 #define DPRINTFN(n,x) if (mididebug >= (n)) printf x 70 int mididebug = 0; 71 #else 72 #define DPRINTF(x) 73 #define DPRINTFN(n,x) 74 #endif 75 76 int midi_wait; 77 78 void midi_in __P((void *, int)); 79 void midi_out __P((void *)); 80 int midi_start_output __P((struct midi_softc *, int)); 81 int midi_sleep_timo __P((int *, char *, int)); 82 int midi_sleep __P((int *, char *)); 83 void midi_wakeup __P((int *)); 84 void midi_initbuf __P((struct midi_buffer *)); 85 void midi_timeout __P((void *)); 86 87 #define __BROKEN_INDIRECT_CONFIG /* XXX */ 88 #ifdef __BROKEN_INDIRECT_CONFIG 89 int midiprobe __P((struct device *, void *, void *)); 90 #else 91 int midiprobe __P((struct device *, struct cfdata *, void *)); 92 #endif 93 void midiattach __P((struct device *, struct device *, void *)); 94 95 struct cfattach midi_ca = { 96 sizeof(struct midi_softc), midiprobe, midiattach 97 }; 98 99 struct cfdriver midi_cd = { 100 NULL, "midi", DV_DULL 101 }; 102 103 #ifdef MIDI_SAVE 104 #define MIDI_SAVE_SIZE 100000 105 int midicnt; 106 struct { 107 int cnt; 108 u_char buf[MIDI_SAVE_SIZE]; 109 } midisave; 110 #define MIDI_GETSAVE _IOWR('m', 100, int) 111 112 #endif 113 114 int 115 midiprobe(parent, match, aux) 116 struct device *parent; 117 #ifdef __BROKEN_INDIRECT_CONFIG 118 void *match; 119 #else 120 struct cfdata *match; 121 #endif 122 void *aux; 123 { 124 struct audio_attach_args *sa = aux; 125 126 DPRINTFN(6,("midiprobe: type=%d sa=%p hw=%p\n", 127 sa->type, sa, sa->hwif)); 128 return ((sa->type == AUDIODEV_TYPE_MIDI) ? 1 : 0); 129 } 130 131 void 132 midiattach(parent, self, aux) 133 struct device *parent, *self; 134 void *aux; 135 { 136 struct midi_softc *sc = (void *)self; 137 struct audio_attach_args *sa = aux; 138 struct midi_hw_if *hwp = sa->hwif; 139 void *hdlp = sa->hdl; 140 141 DPRINTFN(6, ("MIDI attach\n")); 142 143 #ifdef DIAGNOSTIC 144 if (hwp == 0 || 145 hwp->open == 0 || 146 hwp->close == 0 || 147 hwp->output == 0 || 148 hwp->getinfo == 0) { 149 printf("midi: missing method\n"); 150 return; 151 } 152 #endif 153 sc->hw_if = hwp; 154 sc->hw_hdl = hdlp; 155 midi_attach(sc, parent); 156 } 157 158 void 159 midi_attach(sc, parent) 160 struct midi_softc *sc; 161 struct device *parent; 162 { 163 struct midi_info mi; 164 165 sc->isopen = 0; 166 167 midi_wait = MIDI_WAIT * hz / 1000000; 168 if (midi_wait == 0) 169 midi_wait = 1; 170 171 sc->sc_dev = parent; 172 sc->hw_if->getinfo(sc->hw_hdl, &mi); 173 sc->props = mi.props; 174 timeout_set(&sc->timeo, midi_timeout, sc); 175 printf(": <%s>\n", mi.name); 176 } 177 178 int 179 midi_unit_count() 180 { 181 return (midi_cd.cd_ndevs); 182 } 183 184 void 185 midi_initbuf(mb) 186 struct midi_buffer *mb; 187 { 188 mb->used = 0; 189 mb->usedhigh = MIDI_BUFSIZE; 190 mb->end = mb->start + mb->usedhigh; 191 mb->inp = mb->outp = mb->start; 192 } 193 194 int 195 midi_sleep_timo(chan, label, timo) 196 int *chan; 197 char *label; 198 int timo; 199 { 200 int st; 201 202 if (!label) 203 label = "midi"; 204 205 DPRINTFN(5, ("midi_sleep_timo: %p %s %d\n", chan, label, timo)); 206 *chan = 1; 207 st = tsleep(chan, PWAIT | PCATCH, label, timo); 208 *chan = 0; 209 #ifdef MIDI_DEBUG 210 if (st != 0) 211 printf("midi_sleep: %d\n", st); 212 #endif 213 return (st); 214 } 215 216 int 217 midi_sleep(chan, label) 218 int *chan; 219 char *label; 220 { 221 return (midi_sleep_timo(chan, label, 0)); 222 } 223 224 void 225 midi_wakeup(chan) 226 int *chan; 227 { 228 if (*chan) { 229 DPRINTFN(5, ("midi_wakeup: %p\n", chan)); 230 wakeup(chan); 231 *chan = 0; 232 } 233 } 234 235 static int midi_lengths[] = { 2,2,2,2,1,1,2,0 }; 236 /* Number of bytes in a MIDI command */ 237 #define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7]) 238 239 void 240 midi_in(addr, data) 241 void *addr; 242 int data; 243 { 244 struct midi_softc *sc = addr; 245 struct midi_buffer *mb = &sc->inbuf; 246 int i; 247 248 if (!sc->isopen) 249 return; 250 if (data == MIDI_ACK) 251 return; 252 DPRINTFN(3, ("midi_in: %p 0x%02x\n", sc, data)); 253 if (!(sc->flags & FREAD)) 254 return; /* discard data if not reading */ 255 256 switch(sc->in_state) { 257 case MIDI_IN_START: 258 if (MIDI_IS_STATUS(data)) { 259 switch(data) { 260 case 0xf0: /* Sysex */ 261 sc->in_state = MIDI_IN_SYSEX; 262 break; 263 case 0xf1: /* MTC quarter frame */ 264 case 0xf3: /* Song select */ 265 sc->in_state = MIDI_IN_DATA; 266 sc->in_msg[0] = data; 267 sc->in_pos = 1; 268 sc->in_left = 1; 269 break; 270 case 0xf2: /* Song position pointer */ 271 sc->in_state = MIDI_IN_DATA; 272 sc->in_msg[0] = data; 273 sc->in_pos = 1; 274 sc->in_left = 2; 275 break; 276 default: 277 if (MIDI_IS_COMMON(data)) { 278 sc->in_msg[0] = data; 279 sc->in_pos = 1; 280 goto deliver; 281 } else { 282 sc->in_state = MIDI_IN_DATA; 283 sc->in_msg[0] = sc->in_status = data; 284 sc->in_pos = 1; 285 sc->in_left = 286 MIDI_LENGTH(sc->in_status); 287 } 288 break; 289 } 290 } else { 291 if (MIDI_IS_STATUS(sc->in_status)) { 292 sc->in_state = MIDI_IN_DATA; 293 sc->in_msg[0] = sc->in_status; 294 sc->in_msg[1] = data; 295 sc->in_pos = 2; 296 sc->in_left = MIDI_LENGTH(sc->in_status) - 1; 297 } 298 } 299 return; 300 case MIDI_IN_DATA: 301 sc->in_msg[sc->in_pos++] = data; 302 if (--sc->in_left <= 0) 303 break; /* deliver data */ 304 return; 305 case MIDI_IN_SYSEX: 306 if (data == MIDI_SYSEX_END) 307 sc->in_state = MIDI_IN_START; 308 return; 309 } 310 deliver: 311 sc->in_state = MIDI_IN_START; 312 #if NSEQUENCER > 0 313 if (sc->seqopen) { 314 extern void midiseq_in __P((struct midi_dev *,u_char *,int)); 315 midiseq_in(sc->seq_md, sc->in_msg, sc->in_pos); 316 return; 317 } 318 #endif 319 320 if (mb->used + sc->in_pos > mb->usedhigh) { 321 DPRINTF(("midi_in: buffer full, discard data=0x%02x\n", 322 sc->in_msg[0])); 323 return; 324 } 325 for (i = 0; i < sc->in_pos; i++) { 326 *mb->inp++ = sc->in_msg[i]; 327 if (mb->inp >= mb->end) 328 mb->inp = mb->start; 329 mb->used++; 330 } 331 midi_wakeup(&sc->rchan); 332 selwakeup(&sc->rsel); 333 if (sc->async) 334 psignal(sc->async, SIGIO); 335 } 336 337 void 338 midi_out(addr) 339 void *addr; 340 { 341 struct midi_softc *sc = addr; 342 343 if (!sc->isopen) 344 return; 345 DPRINTFN(3, ("midi_out: %p\n", sc)); 346 midi_start_output(sc, 1); 347 } 348 349 int 350 midiopen(dev, flags, ifmt, p) 351 dev_t dev; 352 int flags, ifmt; 353 struct proc *p; 354 { 355 int unit = MIDIUNIT(dev); 356 struct midi_softc *sc; 357 struct midi_hw_if *hw; 358 int error; 359 360 if (unit >= midi_cd.cd_ndevs || 361 (sc = midi_cd.cd_devs[unit]) == NULL) 362 return (ENXIO); 363 DPRINTF(("midiopen %p\n", sc)); 364 365 hw = sc->hw_if; 366 if (!hw) 367 return (ENXIO); 368 if (sc->isopen) 369 return (EBUSY); 370 sc->in_state = MIDI_IN_START; 371 sc->in_status = 0; 372 error = hw->open(sc->hw_hdl, flags, midi_in, midi_out, sc); 373 if (error) 374 return (error); 375 sc->isopen++; 376 midi_initbuf(&sc->outbuf); 377 midi_initbuf(&sc->inbuf); 378 sc->flags = flags; 379 sc->rchan = 0; 380 sc->wchan = 0; 381 sc->pbus = 0; 382 sc->async = 0; 383 384 #ifdef MIDI_SAVE 385 if (midicnt != 0) { 386 midisave.cnt = midicnt; 387 midicnt = 0; 388 } 389 #endif 390 391 return (0); 392 } 393 394 int 395 midiclose(dev, flags, ifmt, p) 396 dev_t dev; 397 int flags, ifmt; 398 struct proc *p; 399 { 400 int unit = MIDIUNIT(dev); 401 struct midi_softc *sc = midi_cd.cd_devs[unit]; 402 struct midi_hw_if *hw = sc->hw_if; 403 int s, error; 404 405 DPRINTF(("midiclose %p\n", sc)); 406 407 midi_start_output(sc, 0); 408 error = 0; 409 s = splaudio(); 410 while (sc->outbuf.used > 0 && !error) { 411 DPRINTFN(2,("midiclose sleep used=%d\n", sc->outbuf.used)); 412 error = midi_sleep_timo(&sc->wchan, "mid_dr", 30*hz); 413 } 414 splx(s); 415 sc->isopen = 0; 416 hw->close(sc->hw_hdl); 417 #if NSEQUENCER > 0 418 sc->seqopen = 0; 419 sc->seq_md = 0; 420 #endif 421 return (0); 422 } 423 424 int 425 midiread(dev, uio, ioflag) 426 dev_t dev; 427 struct uio *uio; 428 int ioflag; 429 { 430 int unit = MIDIUNIT(dev); 431 struct midi_softc *sc = midi_cd.cd_devs[unit]; 432 struct midi_buffer *mb = &sc->inbuf; 433 int error; 434 u_char *outp; 435 int used, cc, n, resid; 436 int s; 437 438 DPRINTF(("midiread: %p, count=%d\n", sc, uio->uio_resid)); 439 440 error = 0; 441 resid = uio->uio_resid; 442 while (uio->uio_resid == resid && !error) { 443 s = splaudio(); 444 while (mb->used <= 0) { 445 if (ioflag & IO_NDELAY) { 446 splx(s); 447 return (EWOULDBLOCK); 448 } 449 error = midi_sleep(&sc->rchan, "mid rd"); 450 if (error) { 451 splx(s); 452 return (error); 453 } 454 } 455 used = mb->used; 456 outp = mb->outp; 457 splx(s); 458 cc = used; /* maximum to read */ 459 n = mb->end - outp; 460 if (n < cc) 461 cc = n; /* don't read beyond end of buffer */ 462 if (uio->uio_resid < cc) 463 cc = uio->uio_resid; /* and no more than we want */ 464 DPRINTFN(3, ("midiread: uiomove cc=%d\n", cc)); 465 error = uiomove(outp, cc, uio); 466 if (error) 467 break; 468 used -= cc; 469 outp += cc; 470 if (outp >= mb->end) 471 outp = mb->start; 472 s = splaudio(); 473 mb->outp = outp; 474 mb->used = used; 475 splx(s); 476 } 477 return (error); 478 } 479 480 void 481 midi_timeout(arg) 482 void *arg; 483 { 484 struct midi_softc *sc = arg; 485 486 DPRINTFN(3,("midi_timeout: %p\n", sc)); 487 midi_start_output(sc, 1); 488 } 489 490 int 491 midi_start_output(sc, intr) 492 struct midi_softc *sc; 493 int intr; 494 { 495 struct midi_buffer *mb = &sc->outbuf; 496 u_char *outp; 497 int error; 498 int s; 499 int i, mmax; 500 501 error = 0; 502 mmax = sc->props & MIDI_PROP_OUT_INTR ? 1 : MIDI_MAX_WRITE; 503 s = splaudio(); 504 if (sc->pbus && !intr) { 505 DPRINTFN(4, ("midi_start_output: busy\n")); 506 splx(s); 507 return (0); 508 } 509 sc->pbus = 1; 510 for (i = 0; i < mmax && mb->used > 0 && !error; i++) { 511 outp = mb->outp; 512 splx(s); 513 DPRINTFN(4, ("midi_start_output: %p i=%d, data=0x%02x\n", 514 sc, i, *outp)); 515 #ifdef MIDI_SAVE 516 midisave.buf[midicnt] = *outp; 517 midicnt = (midicnt + 1) % MIDI_SAVE_SIZE; 518 #endif 519 error = sc->hw_if->output(sc->hw_hdl, *outp++); 520 if (outp >= mb->end) 521 outp = mb->start; 522 s = splaudio(); 523 mb->outp = outp; 524 mb->used--; 525 } 526 midi_wakeup(&sc->wchan); 527 selwakeup(&sc->wsel); 528 if (sc->async) 529 psignal(sc->async, SIGIO); 530 if (mb->used > 0) { 531 if (!(sc->props & MIDI_PROP_OUT_INTR)) 532 timeout_add(&sc->timeo, midi_wait); 533 } else 534 sc->pbus = 0; 535 splx(s); 536 return (error); 537 } 538 539 int 540 midiwrite(dev, uio, ioflag) 541 dev_t dev; 542 struct uio *uio; 543 int ioflag; 544 { 545 int unit = MIDIUNIT(dev); 546 struct midi_softc *sc = midi_cd.cd_devs[unit]; 547 struct midi_buffer *mb = &sc->outbuf; 548 int error; 549 u_char *inp; 550 int used, cc, n; 551 int s; 552 553 DPRINTFN(2, ("midiwrite: %p, unit=%d, count=%d\n", sc, unit, 554 uio->uio_resid)); 555 556 error = 0; 557 while (uio->uio_resid > 0 && !error) { 558 s = splaudio(); 559 if (mb->used >= mb->usedhigh) { 560 DPRINTFN(3,("midi_write: sleep used=%d hiwat=%d\n", 561 mb->used, mb->usedhigh)); 562 if (ioflag & IO_NDELAY) { 563 splx(s); 564 return (EWOULDBLOCK); 565 } 566 error = midi_sleep(&sc->wchan, "mid wr"); 567 if (error) { 568 splx(s); 569 return (error); 570 } 571 } 572 used = mb->used; 573 inp = mb->inp; 574 splx(s); 575 cc = mb->usedhigh - used; /* maximum to write */ 576 n = mb->end - inp; 577 if (n < cc) 578 cc = n; /* don't write beyond end of buffer */ 579 if (uio->uio_resid < cc) 580 cc = uio->uio_resid; /* and no more than we have */ 581 error = uiomove(inp, cc, uio); 582 #ifdef MIDI_DEBUG 583 if (error) 584 printf("midi_write:(1) uiomove failed %d; " 585 "cc=%d inp=%p\n", 586 error, cc, inp); 587 #endif 588 if (error) 589 break; 590 inp = mb->inp + cc; 591 if (inp >= mb->end) 592 inp = mb->start; 593 s = splaudio(); 594 mb->inp = inp; 595 mb->used += cc; 596 splx(s); 597 error = midi_start_output(sc, 0); 598 } 599 return (error); 600 } 601 602 /* 603 * This write routine is only called from sequencer code and expects 604 * a write that is smaller than the MIDI buffer. 605 */ 606 int 607 midi_writebytes(unit, buf, cc) 608 int unit; 609 u_char *buf; 610 int cc; 611 { 612 struct midi_softc *sc = midi_cd.cd_devs[unit]; 613 struct midi_buffer *mb = &sc->outbuf; 614 int n, s; 615 616 DPRINTFN(2, ("midi_writebytes: %p, unit=%d, cc=%d\n", sc, unit, cc)); 617 DPRINTFN(3, ("midi_writebytes: %x %x %x\n",buf[0],buf[1],buf[2])); 618 619 s = splaudio(); 620 if (mb->used + cc >= mb->usedhigh) { 621 splx(s); 622 return (EWOULDBLOCK); 623 } 624 n = mb->end - mb->inp; 625 if (cc < n) 626 n = cc; 627 mb->used += cc; 628 bcopy(buf, mb->inp, n); 629 mb->inp += n; 630 if (mb->inp >= mb->end) { 631 mb->inp = mb->start; 632 cc -= n; 633 if (cc > 0) { 634 bcopy(buf + n, mb->inp, cc); 635 mb->inp += cc; 636 } 637 } 638 splx(s); 639 return (midi_start_output(sc, 0)); 640 } 641 642 int 643 midiioctl(dev, cmd, addr, flag, p) 644 dev_t dev; 645 u_long cmd; 646 caddr_t addr; 647 int flag; 648 struct proc *p; 649 { 650 int unit = MIDIUNIT(dev); 651 struct midi_softc *sc = midi_cd.cd_devs[unit]; 652 struct midi_hw_if *hw = sc->hw_if; 653 int error; 654 655 DPRINTF(("midiioctl: %p cmd=0x%08lx\n", sc, cmd)); 656 error = 0; 657 switch (cmd) { 658 case FIONBIO: 659 /* All handled in the upper FS layer. */ 660 break; 661 662 case FIOASYNC: 663 if (*(int *)addr) { 664 if (sc->async) 665 return (EBUSY); 666 sc->async = p; 667 DPRINTF(("midi_ioctl: FIOASYNC %p\n", p)); 668 } else 669 sc->async = 0; 670 break; 671 672 #if 0 673 case MIDI_PRETIME: 674 /* XXX OSS 675 * This should set up a read timeout, but that's 676 * why we have poll(), so there's nothing yet. */ 677 error = EINVAL; 678 break; 679 #endif 680 681 #ifdef MIDI_SAVE 682 case MIDI_GETSAVE: 683 error = copyout(&midisave, *(void **)addr, sizeof midisave); 684 break; 685 #endif 686 687 default: 688 if (hw->ioctl) 689 error = hw->ioctl(sc->hw_hdl, cmd, addr, flag, p); 690 else 691 error = EINVAL; 692 break; 693 } 694 return (error); 695 } 696 697 int 698 midiselect(dev, rw, p) 699 dev_t dev; 700 int rw; 701 struct proc *p; 702 { 703 int unit = MIDIUNIT(dev); 704 struct midi_softc *sc = midi_cd.cd_devs[unit]; 705 int s = splaudio(); 706 707 DPRINTF(("midiselect: %p rw=0x%x\n", sc, rw)); 708 709 switch (rw) { 710 case FREAD: 711 if (sc->inbuf.used > 0) { 712 splx(s); 713 return (1); 714 } 715 selrecord(p, &sc->rsel); 716 break; 717 718 case FWRITE: 719 if (sc->outbuf.used < sc->outbuf.usedhigh) { 720 splx(s); 721 return (1); 722 } 723 selrecord(p, &sc->wsel); 724 break; 725 } 726 727 splx(s); 728 return (0); 729 } 730 731 void 732 midi_getinfo(dev, mi) 733 dev_t dev; 734 struct midi_info *mi; 735 { 736 int unit = MIDIUNIT(dev); 737 struct midi_softc *sc; 738 739 if (unit >= midi_cd.cd_ndevs || 740 (sc = midi_cd.cd_devs[unit]) == NULL) 741 return; 742 sc->hw_if->getinfo(sc->hw_hdl, mi); 743 } 744 745 #endif /* NMIDI > 0 */ 746 747 #if (NMIDI > 0 || NMIDIBUS > 0) && NAUDIO > 0 748 749 int midiprint __P((void *, const char *)); 750 751 struct device * 752 midi_attach_mi(mhwp, hdlp, dev) 753 struct midi_hw_if *mhwp; 754 void *hdlp; 755 struct device *dev; 756 { 757 struct audio_attach_args arg; 758 759 #ifdef DIAGNOSTIC 760 if (mhwp == NULL) { 761 printf("midi_attach_mi: NULL\n"); 762 return 0; 763 } 764 #endif 765 arg.type = AUDIODEV_TYPE_MIDI; 766 arg.hwif = mhwp; 767 arg.hdl = hdlp; 768 return config_found(dev, &arg, midiprint); 769 } 770 771 int 772 midiprint(aux, pnp) 773 void *aux; 774 const char *pnp; 775 { 776 if (pnp) 777 printf("midi at %s", pnp); 778 return (UNCONF); 779 } 780 781 #endif /* NMIDI > 0 || NMIDIBUS > 0 */ 782