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