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