1 /*
2 * GUS midi interface driver.
3 * Based on the newmidi MPU401 driver.
4 *
5 * Copyright (c) 1999 Ville-Pertti Keinonen
6 * Copyright by Hannu Savolainen 1993
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met: 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 2.
12 * Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * Modified: Riccardo Facchetti 24 Mar 1995 - Added the Audio Excel DSP 16
29 * initialization routine.
30 *
31 * Ported to the new Audio Driver by Luigi Rizzo:
32 * (C) 1999 Seigo Tanimura <tanimura@r.dl.itc.u-tokyo.ac.jp>
33 *
34 * $FreeBSD: releng/5.2/sys/dev/sound/isa/gusmidi.c 93818 2002-04-04 21:03:38Z jhb $
35 *
36 */
37
38 #include <dev/sound/midi/midi.h>
39
40 #include <dev/sound/chip.h>
41 #include <machine/cpufunc.h>
42
43 static devclass_t midi_devclass;
44
45 extern synthdev_info midisynth_op_desc;
46
47 /* These are the synthesizer and the midi interface information. */
48 static struct synth_info gusmidi_synthinfo = {
49 "GUS MIDI",
50 0,
51 SYNTH_TYPE_MIDI,
52 0,
53 0,
54 128,
55 128,
56 128,
57 SYNTH_CAP_INPUT,
58 };
59
60 static struct midi_info gusmidi_midiinfo = {
61 "GUS MIDI",
62 0,
63 0,
64 0,
65 };
66
67 #define MIDICTL_MASTER_RESET 0x03
68 #define MIDICTL_TX_IRQ_EN 0x20
69 #define MIDICTL_RX_IRQ_EN 0x80
70
71 #define MIDIST_RXFULL 0x01
72 #define MIDIST_TXDONE 0x02
73 #define MIDIST_ERR_FR 0x10
74 #define MIDIST_ERR_OVR 0x20
75 #define MIDIST_INTR_PEND 0x80
76
77 #define PORT_CTL 0
78 #define PORT_ST 0
79 #define PORT_TX 1
80 #define PORT_RX 1
81
82 /*
83 * These functions goes into gusmidi_op_desc to get called
84 * from sound.c.
85 */
86
87 static int gusmidi_probe(device_t dev);
88 static int gusmidi_attach(device_t dev);
89
90 static d_open_t gusmidi_open;
91 static d_ioctl_t gusmidi_ioctl;
92 driver_intr_t gusmidi_intr;
93 static midi_callback_t gusmidi_callback;
94
95 /* Here is the parameter structure per a device. */
96 struct gusmidi_softc {
97 device_t dev; /* device information */
98 mididev_info *devinfo; /* midi device information */
99
100 struct mtx mtx; /* Mutex to protect the device. */
101
102 struct resource *io; /* Base of io port */
103 int io_rid; /* Io resource ID */
104 struct resource *irq; /* Irq */
105 int irq_rid; /* Irq resource ID */
106 void *ih; /* Interrupt cookie */
107
108 int ctl; /* Control bits. */
109 };
110
111 typedef struct gusmidi_softc *sc_p;
112
113 /* These functions are local. */
114 static int gusmidi_init(device_t dev);
115 static int gusmidi_allocres(sc_p scp, device_t dev);
116 static void gusmidi_releaseres(sc_p scp, device_t dev);
117 static void gusmidi_startplay(sc_p scp);
118 static void gusmidi_xmit(sc_p scp);
119 static u_int gusmidi_readport(sc_p scp, int off);
120 static void gusmidi_writeport(sc_p scp, int off, u_int8_t value);
121
122 /*
123 * This is the device descriptor for the midi device.
124 */
125 static mididev_info gusmidi_op_desc = {
126 "GUS midi",
127
128 SNDCARD_GUS,
129
130 gusmidi_open,
131 NULL,
132 gusmidi_ioctl,
133
134 gusmidi_callback,
135
136 MIDI_BUFFSIZE, /* Queue Length */
137
138 0, /* XXX This is not an *audio* device! */
139 };
140
141 static int
142 gusmidi_probe(device_t dev)
143 {
144 char *s;
145 sc_p scp;
146 struct sndcard_func *func;
147
148 /* The parent device has already been probed. */
149
150 func = device_get_ivars(dev);
151 if (func == NULL || func->func != SCF_MIDI)
152 return (ENXIO);
153
154 s = "GUS Midi Interface";
155
156 scp = device_get_softc(dev);
157 bzero(scp, sizeof(*scp));
158 scp->io_rid = 1;
159 scp->irq_rid = 0;
160 #if notdef
161 ret = mpu_probe2(dev);
162 if (ret != 0)
163 return (ret);
164 #endif /* notdef */
165 device_set_desc(dev, s);
166 return (0);
167 }
168
169 static int
170 gusmidi_attach(device_t dev)
171 {
172 sc_p scp;
173
174 scp = device_get_softc(dev);
175
176 /* Allocate the resources, switch to uart mode. */
177 if (gusmidi_allocres(scp, dev)) {
178 gusmidi_releaseres(scp, dev);
179 return (ENXIO);
180 }
181
182 gusmidi_init(dev);
183
184 return (0);
185 }
186
187 static int
188 gusmidi_init(device_t dev)
189 {
190 sc_p scp;
191 mididev_info *devinfo;
192
193 scp = device_get_softc(dev);
194
195 /* Fill the softc. */
196 scp->dev = dev;
197 mtx_init(&scp->mtx, "gusmid", NULL, MTX_DEF);
198 scp->devinfo = devinfo = create_mididev_info_unit(MDT_MIDI, &gusmidi_op_desc, &midisynth_op_desc);
199
200 /* Fill the midi info. */
201 if (scp->irq != NULL)
202 snprintf(devinfo->midistat, sizeof(devinfo->midistat), "at 0x%x irq %d",
203 (u_int)rman_get_start(scp->io), (int)rman_get_start(scp->irq));
204 else
205 snprintf(devinfo->midistat, sizeof(devinfo->midistat), "at 0x%x",
206 (u_int)rman_get_start(scp->io));
207
208 midiinit(devinfo, dev);
209
210 bus_setup_intr(dev, scp->irq, INTR_TYPE_AV, gusmidi_intr, scp,
211 &scp->ih);
212
213 return (0);
214 }
215
216 static int
217 gusmidi_open(dev_t i_dev, int flags, int mode, struct thread *td)
218 {
219 sc_p scp;
220 mididev_info *devinfo;
221 int unit;
222
223 unit = MIDIUNIT(i_dev);
224
225 devinfo = get_mididev_info(i_dev, &unit);
226 if (devinfo == NULL) {
227 MIDI_DEBUG(printf("gusmidi_open: unit %d is not configured.\n", unit));
228 return (ENXIO);
229 }
230 scp = devinfo->softc;
231
232 mtx_lock(&scp->mtx);
233
234 gusmidi_writeport(scp, PORT_CTL, MIDICTL_MASTER_RESET);
235 DELAY(100);
236
237 gusmidi_writeport(scp, PORT_CTL, scp->ctl);
238
239 mtx_unlock(&scp->mtx);
240
241 return (0);
242 }
243
244 static int
245 gusmidi_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
246 {
247 sc_p scp;
248 mididev_info *devinfo;
249 int unit;
250 struct synth_info *synthinfo;
251 struct midi_info *midiinfo;
252
253 unit = MIDIUNIT(i_dev);
254
255 MIDI_DEBUG(printf("gusmidi_ioctl: unit %d, cmd %s.\n", unit, midi_cmdname(cmd, cmdtab_midiioctl)));
256
257 devinfo = get_mididev_info(i_dev, &unit);
258 if (devinfo == NULL) {
259 MIDI_DEBUG(printf("gusmidi_ioctl: unit %d is not configured.\n", unit));
260 return (ENXIO);
261 }
262 scp = devinfo->softc;
263
264 switch (cmd) {
265 case SNDCTL_SYNTH_INFO:
266 synthinfo = (struct synth_info *)arg;
267 if (synthinfo->device != unit)
268 return (ENXIO);
269 bcopy(&gusmidi_synthinfo, synthinfo, sizeof(gusmidi_synthinfo));
270 synthinfo->device = unit;
271 return (0);
272 break;
273 case SNDCTL_MIDI_INFO:
274 midiinfo = (struct midi_info *)arg;
275 if (midiinfo->device != unit)
276 return (ENXIO);
277 bcopy(&gusmidi_midiinfo, midiinfo, sizeof(gusmidi_midiinfo));
278 midiinfo->device = unit;
279 return (0);
280 break;
281 default:
282 return (ENOSYS);
283 }
284 /* NOTREACHED */
285 return (EINVAL);
286 }
287
288 void
289 gusmidi_intr(void *arg)
290 {
291 sc_p scp;
292 u_char c;
293 mididev_info *devinfo;
294 int stat, did_something, leni;
295
296 scp = (sc_p)arg;
297 devinfo = scp->devinfo;
298
299 /* XXX No framing/overrun checks... */
300 mtx_lock(&devinfo->flagqueue_mtx);
301 mtx_lock(&scp->mtx);
302
303 do {
304 stat = gusmidi_readport(scp, PORT_ST);
305 did_something = 0;
306 if (stat & MIDIST_RXFULL) {
307 c = gusmidi_readport(scp, PORT_RX);
308 mtx_unlock(&scp->mtx);
309 if ((devinfo->flags & MIDI_F_PASSTHRU) &&
310 (!(devinfo->flags & MIDI_F_BUSY) ||
311 !(devinfo->fflags & FWRITE))) {
312 midibuf_input_intr(&devinfo->midi_dbuf_passthru,
313 &c, sizeof c, &leni);
314 devinfo->callback(devinfo,
315 MIDI_CB_START | MIDI_CB_WR);
316 }
317 if ((devinfo->flags & MIDI_F_READING) && c != 0xfe) {
318 midibuf_input_intr(&devinfo->midi_dbuf_in,
319 &c, sizeof c, &leni);
320 }
321 did_something = 1;
322 } else
323 mtx_unlock(&scp->mtx);
324 if (stat & MIDIST_TXDONE) {
325 if (devinfo->flags & MIDI_F_WRITING) {
326 gusmidi_xmit(scp);
327 did_something = 1;
328 mtx_lock(&scp->mtx);
329 } else if (scp->ctl & MIDICTL_TX_IRQ_EN) {
330 /* This shouldn't happen. */
331 mtx_lock(&scp->mtx);
332 scp->ctl &= ~MIDICTL_TX_IRQ_EN;
333 gusmidi_writeport(scp, PORT_CTL, scp->ctl);
334 }
335 } else
336 mtx_lock(&scp->mtx);
337 } while (did_something != 0);
338
339 mtx_unlock(&scp->mtx);
340 mtx_unlock(&devinfo->flagqueue_mtx);
341
342 /* Invoke the upper layer. */
343 midi_intr(devinfo);
344 }
345
346 static int
347 gusmidi_callback(void *di, int reason)
348 {
349 int unit;
350 sc_p scp;
351 mididev_info *d;
352
353 d = (mididev_info *)di;
354
355 mtx_assert(&d->flagqueue_mtx, MA_OWNED);
356
357 if (d == NULL) {
358 MIDI_DEBUG(printf("gusmidi_callback: device not configured.\n"));
359 return (ENXIO);
360 }
361
362 unit = d->unit;
363 scp = d->softc;
364
365 switch (reason & MIDI_CB_REASON_MASK) {
366 case MIDI_CB_START:
367 if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) == 0) {
368 /* Begin recording. */
369 d->flags |= MIDI_F_READING;
370 mtx_lock(&scp->mtx);
371 scp->ctl |= MIDICTL_RX_IRQ_EN;
372 gusmidi_writeport(scp, PORT_CTL, scp->ctl);
373 mtx_unlock(&scp->mtx);
374 }
375 if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) == 0)
376 /* Start playing. */
377 gusmidi_startplay(scp);
378 break;
379 case MIDI_CB_STOP:
380 case MIDI_CB_ABORT:
381 mtx_lock(&scp->mtx);
382 if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) != 0) {
383 /* Stop recording. */
384 d->flags &= ~MIDI_F_READING;
385 scp->ctl &= ~MIDICTL_RX_IRQ_EN;
386 }
387 if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) != 0) {
388 /* Stop Playing. */
389 d->flags &= ~MIDI_F_WRITING;
390 scp->ctl &= ~MIDICTL_TX_IRQ_EN;
391 }
392 gusmidi_writeport(scp, PORT_CTL, scp->ctl);
393 mtx_unlock(&scp->mtx);
394 break;
395 }
396
397 return (0);
398 }
399
400 /*
401 * The functions below here are the libraries for the above ones.
402 */
403
404 /*
405 * Starts to play the data in the output queue.
406 */
407 static void
408 gusmidi_startplay(sc_p scp)
409 {
410 mididev_info *devinfo;
411
412 devinfo = scp->devinfo;
413
414 mtx_assert(&devinfo->flagqueue_mtx, MA_OWNED);
415
416 /* Can we play now? */
417 if (devinfo->midi_dbuf_out.rl == 0)
418 return;
419
420 devinfo->flags |= MIDI_F_WRITING;
421 mtx_lock(&scp->mtx);
422 scp->ctl |= MIDICTL_TX_IRQ_EN;
423 mtx_unlock(&scp->mtx);
424 }
425
426 static void
427 gusmidi_xmit(sc_p scp)
428 {
429 register mididev_info *devinfo;
430 register midi_dbuf *dbuf;
431 u_char c;
432 int leno;
433
434 devinfo = scp->devinfo;
435
436 mtx_assert(&devinfo->flagqueue_mtx, MA_OWNED);
437
438 /* See which source to use. */
439 if ((devinfo->flags & MIDI_F_PASSTHRU) == 0 || ((devinfo->flags & MIDI_F_BUSY) != 0 && (devinfo->fflags & FWRITE) != 0))
440 dbuf = &devinfo->midi_dbuf_out;
441 else
442 dbuf = &devinfo->midi_dbuf_passthru;
443
444 /* Transmit the data in the queue. */
445 while (devinfo->flags & MIDI_F_WRITING) {
446 /* Do we have the data to transmit? */
447 if (dbuf->rl == 0) {
448 /* Stop playing. */
449 devinfo->flags &= ~MIDI_F_WRITING;
450 mtx_lock(&scp->mtx);
451 scp->ctl &= ~MIDICTL_TX_IRQ_EN;
452 gusmidi_writeport(scp, PORT_CTL, scp->ctl);
453 mtx_unlock(&scp->mtx);
454 break;
455 } else {
456 mtx_lock(&scp->mtx);
457 if (gusmidi_readport(scp, PORT_ST) & MIDIST_TXDONE) {
458 /* Send the data. */
459 midibuf_output_intr(dbuf, &c, sizeof(c), &leno);
460 gusmidi_writeport(scp, PORT_TX, c);
461 /* We are playing now. */
462 } else {
463 mtx_unlock(&scp->mtx);
464 break;
465 }
466 mtx_unlock(&scp->mtx);
467 }
468 }
469 }
470
471 /* Reads from a port. */
472 static u_int
473 gusmidi_readport(sc_p scp, int off)
474 {
475 return bus_space_read_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), off) & 0xff;
476 }
477
478 /* Writes to a port. */
479 static void
480 gusmidi_writeport(sc_p scp, int off, u_int8_t value)
481 {
482 bus_space_write_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), off, value);
483 }
484
485 /* Allocates resources. */
486 static int
487 gusmidi_allocres(sc_p scp, device_t dev)
488 {
489 if (scp->io == NULL) {
490 scp->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid, 0, ~0, 2, RF_ACTIVE);
491 if (scp->io == NULL)
492 return (1);
493 }
494 #if notdef
495 if (scp->irq == NULL && !(device_get_flags(dev) & MPU_DF_NO_IRQ)) {
496 #else
497 if (scp->irq == NULL) {
498 #endif /* notdef */
499 scp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &scp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
500 if (scp->irq == NULL)
501 return (1);
502 }
503
504 return (0);
505 }
506
507 /* Releases resources. */
508 static void
509 gusmidi_releaseres(sc_p scp, device_t dev)
510 {
511 if (scp->irq != NULL) {
512 bus_release_resource(dev, SYS_RES_IRQ, scp->irq_rid, scp->irq);
513 scp->irq = NULL;
514 }
515 if (scp->io != NULL) {
516 bus_release_resource(dev, SYS_RES_IOPORT, scp->io_rid, scp->io);
517 scp->io = NULL;
518 }
519 }
520
521 static device_method_t gusmidi_methods[] = {
522 /* Device interface */
523 DEVMETHOD(device_probe , gusmidi_probe ),
524 DEVMETHOD(device_attach, gusmidi_attach),
525
526 { 0, 0 },
527 };
528
529 driver_t gusmidi_driver = {
530 "midi",
531 gusmidi_methods,
532 sizeof(struct gusmidi_softc),
533 };
534
535 DRIVER_MODULE(gusmidi, gusc, gusmidi_driver, midi_devclass, 0, 0);
Cache object: 0caa658987194edaf80f3af97cadcbcc
|