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