xref: /freebsd/sys/dev/sound/pci/csamidi.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*-
2  * Copyright (c) 1999 Seigo Tanimura
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <dev/sound/midi/midi.h>
30 #include <dev/sound/chip.h>
31 #include <dev/sound/pci/csareg.h>
32 #include <machine/cpufunc.h>
33 
34 #include <pci/pcireg.h>
35 #include <pci/pcivar.h>
36 
37 static devclass_t midi_devclass;
38 
39 #ifndef DDB
40 #undef DDB
41 #define DDB(x)
42 #endif /* DDB */
43 
44 #define CSAMIDI_RESET      0xff
45 #define CSAMIDI_UART       0x3f
46 #define CSAMIDI_ACK        0xfe
47 
48 #define CSAMIDI_STATMASK   0xc0
49 #define CSAMIDI_OUTPUTBUSY 0x40
50 #define CSAMIDI_INPUTBUSY  0x80
51 
52 #define CSAMIDI_TRYDATA 50
53 #define CSAMIDI_DELAY   25000
54 
55 extern synthdev_info midisynth_op_desc;
56 
57 /* These are the synthesizer and the midi interface information. */
58 static struct synth_info csamidi_synthinfo = {
59 	"CS461x MIDI",
60 	0,
61 	SYNTH_TYPE_MIDI,
62 	0,
63 	0,
64 	128,
65 	128,
66 	128,
67 	SYNTH_CAP_INPUT,
68 };
69 
70 static struct midi_info csamidi_midiinfo = {
71 	"CS461x MIDI",
72 	0,
73 	0,
74 	0,
75 };
76 
77 /*
78  * These functions goes into csamidi_op_desc to get called
79  * from sound.c.
80  */
81 
82 static int csamidi_probe(device_t dev);
83 static int csamidi_attach(device_t dev);
84 
85 static d_ioctl_t csamidi_ioctl;
86 static driver_intr_t csamidi_intr;
87 static midi_callback_t csamidi_callback;
88 
89 /* Here is the parameter structure per a device. */
90 struct csamidi_softc {
91 	device_t dev; /* device information */
92 	mididev_info *devinfo; /* midi device information */
93 	struct csa_bridgeinfo *binfo; /* The state of the parent. */
94 
95 	struct mtx mtx; /* Mutex to protect the device. */
96 
97 	struct resource *io; /* Base of io map */
98 	int io_rid; /* Io map resource ID */
99 	struct resource *mem; /* Base of memory map */
100 	int mem_rid; /* Memory map resource ID */
101 	struct resource *irq; /* Irq */
102 	int irq_rid; /* Irq resource ID */
103 	void *ih; /* Interrupt cookie */
104 
105 	int fflags; /* File flags */
106 };
107 
108 typedef struct csamidi_softc *sc_p;
109 
110 /* These functions are local. */
111 static void csamidi_startplay(sc_p scp);
112 static void csamidi_xmit(sc_p scp);
113 static int csamidi_reset(sc_p scp);
114 static int csamidi_status(sc_p scp);
115 static int csamidi_command(sc_p scp, u_int32_t value);
116 static int csamidi_readdata(sc_p scp);
117 static int csamidi_writedata(sc_p scp, u_int32_t value);
118 static u_int32_t csamidi_readio(sc_p scp, u_long offset);
119 static void csamidi_writeio(sc_p scp, u_long offset, u_int32_t data);
120 /* Not used in this file. */
121 #if notdef
122 static u_int32_t csamidi_readmem(sc_p scp, u_long offset);
123 static void csamidi_writemem(sc_p scp, u_long offset, u_int32_t data);
124 #endif /* notdef */
125 static int csamidi_allocres(sc_p scp, device_t dev);
126 static void csamidi_releaseres(sc_p scp, device_t dev);
127 
128 /*
129  * This is the device descriptor for the midi device.
130  */
131 static mididev_info csamidi_op_desc = {
132 	"CS461x midi",
133 
134 	SNDCARD_MPU401,
135 
136 	NULL,
137 	NULL,
138 	csamidi_ioctl,
139 
140 	csamidi_callback,
141 
142 	MIDI_BUFFSIZE, /* Queue Length */
143 
144 	0, /* XXX This is not an *audio* device! */
145 };
146 
147 /*
148  * Here are the main functions to interact to the user process.
149  */
150 
151 static int
152 csamidi_probe(device_t dev)
153 {
154 	char *s;
155 	sc_p scp;
156 	struct sndcard_func *func;
157 
158 	/* The parent device has already been probed. */
159 
160 	func = device_get_ivars(dev);
161 	if (func == NULL || func->func != SCF_MIDI)
162 		return (ENXIO);
163 
164 	s = "CS461x Midi Interface";
165 
166 	scp = device_get_softc(dev);
167 	bzero(scp, sizeof(*scp));
168 	scp->io_rid = PCIR_MAPS;
169 	scp->mem_rid = PCIR_MAPS + 4;
170 	scp->irq_rid = 0;
171 
172 	device_set_desc(dev, s);
173 	return (0);
174 }
175 
176 static int
177 csamidi_attach(device_t dev)
178 {
179 	sc_p scp;
180 	mididev_info *devinfo;
181 	struct sndcard_func *func;
182 
183 	scp = device_get_softc(dev);
184 	func = device_get_ivars(dev);
185 	scp->binfo = func->varinfo;
186 
187 	/* Allocate the resources. */
188 	if (csamidi_allocres(scp, dev)) {
189 		csamidi_releaseres(scp, dev);
190 		return (ENXIO);
191 	}
192 
193 	/* Fill the softc. */
194 	scp->dev = dev;
195 	mtx_init(&scp->mtx, "csamid", MTX_DEF);
196 	scp->devinfo = devinfo = create_mididev_info_unit(MDT_MIDI, &csamidi_op_desc, &midisynth_op_desc);
197 
198 	/* Fill the midi info. */
199 	snprintf(devinfo->midistat, sizeof(devinfo->midistat), "at irq %d",
200 		 (int)rman_get_start(scp->irq));
201 
202 	midiinit(devinfo, dev);
203 
204 	/* Enable interrupt. */
205 	if (bus_setup_intr(dev, scp->irq, INTR_TYPE_AV, csamidi_intr, scp, &scp->ih)) {
206 		csamidi_releaseres(scp, dev);
207 		return (ENXIO);
208 	}
209 
210 	/* Reset the interface. */
211 	csamidi_reset(scp);
212 
213 	return (0);
214 }
215 
216 static int
217 csamidi_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p)
218 {
219 	sc_p scp;
220 	mididev_info *devinfo;
221 	int unit;
222 	struct synth_info *synthinfo;
223 	struct midi_info *midiinfo;
224 
225 	unit = MIDIUNIT(i_dev);
226 
227 	devinfo = get_mididev_info(i_dev, &unit);
228 	if (devinfo == NULL) {
229 		DEB(printf("csamidi_ioctl: unit %d is not configured.\n", unit));
230 		return (ENXIO);
231 	}
232 	scp = devinfo->softc;
233 
234 	switch (cmd) {
235 	case SNDCTL_SYNTH_INFO:
236 		synthinfo = (struct synth_info *)arg;
237 		if (synthinfo->device != unit)
238 			return (ENXIO);
239 		bcopy(&csamidi_synthinfo, synthinfo, sizeof(csamidi_synthinfo));
240 		synthinfo->device = unit;
241 		return (0);
242 		break;
243 	case SNDCTL_MIDI_INFO:
244 		midiinfo = (struct midi_info *)arg;
245 		if (midiinfo->device != unit)
246 			return (ENXIO);
247 		bcopy(&csamidi_midiinfo, midiinfo, sizeof(csamidi_midiinfo));
248 		midiinfo->device = unit;
249 		return (0);
250 		break;
251 	default:
252 		return (ENOSYS);
253 	}
254 	/* NOTREACHED */
255 	return (EINVAL);
256 }
257 
258 static void
259 csamidi_intr(void *arg)
260 {
261 	sc_p scp;
262 	u_char c;
263 	mididev_info *devinfo;
264 
265 	scp = (sc_p)arg;
266 	devinfo = scp->devinfo;
267 
268 	mtx_lock(&devinfo->flagqueue_mtx);
269 	mtx_lock(&scp->mtx);
270 
271 	/* Read the received data. */
272 	while ((csamidi_status(scp) & MIDSR_RBE) == 0) {
273 		/* Receive the data. */
274 		c = (u_char)csamidi_readdata(scp);
275 		mtx_unlock(&scp->mtx);
276 
277 		/* Queue into the passthru buffer and start transmitting if we can. */
278 		if ((devinfo->flags & MIDI_F_PASSTHRU) != 0 && ((devinfo->flags & MIDI_F_BUSY) == 0 || (devinfo->fflags & FWRITE) == 0)) {
279 			midibuf_input_intr(&devinfo->midi_dbuf_passthru, &c, sizeof(c));
280 			devinfo->callback(devinfo, MIDI_CB_START | MIDI_CB_WR);
281 		}
282 		/* Queue if we are reading. Discard an active sensing. */
283 		if ((devinfo->flags & MIDI_F_READING) != 0 && c != 0xfe) {
284 			midibuf_input_intr(&devinfo->midi_dbuf_in, &c, sizeof(c));
285 		}
286 		mtx_lock(&scp->mtx);
287 	}
288 	mtx_unlock(&scp->mtx);
289 
290 	/* Transmit out data. */
291 	if ((devinfo->flags & MIDI_F_WRITING) != 0 && (csamidi_status(scp) & MIDSR_TBF) == 0)
292 		csamidi_xmit(scp);
293 
294 	mtx_unlock(&devinfo->flagqueue_mtx);
295 
296 	/* Invoke the upper layer. */
297 	midi_intr(devinfo);
298 }
299 
300 static int
301 csamidi_callback(mididev_info *d, int reason)
302 {
303 	int unit;
304 	sc_p scp;
305 
306 	mtx_assert(&d->flagqueue_mtx, MA_OWNED);
307 
308 	if (d == NULL) {
309 		DEB(printf("csamidi_callback: device not configured.\n"));
310 		return (ENXIO);
311 	}
312 
313 	unit = d->unit;
314 	scp = d->softc;
315 
316 	switch (reason & MIDI_CB_REASON_MASK) {
317 	case MIDI_CB_START:
318 		if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) == 0)
319 			/* Begin recording. */
320 			d->flags |= MIDI_F_READING;
321 		if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) == 0)
322 			/* Start playing. */
323 			csamidi_startplay(scp);
324 		break;
325 	case MIDI_CB_STOP:
326 	case MIDI_CB_ABORT:
327 		if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) != 0)
328 			/* Stop recording. */
329 			d->flags &= ~MIDI_F_READING;
330 		if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) != 0)
331 			/* Stop Playing. */
332 			d->flags &= ~MIDI_F_WRITING;
333 		break;
334 	}
335 
336 	return (0);
337 }
338 
339 /*
340  * The functions below here are the libraries for the above ones.
341  */
342 
343 /*
344  * Starts to play the data in the output queue.
345  */
346 static void
347 csamidi_startplay(sc_p scp)
348 {
349 	mididev_info *devinfo;
350 
351 	devinfo = scp->devinfo;
352 
353 	mtx_assert(&devinfo->flagqueue_mtx, MA_OWNED);
354 
355 	/* Can we play now? */
356 	if (devinfo->midi_dbuf_out.rl == 0)
357 		return;
358 
359 	devinfo->flags |= MIDI_F_WRITING;
360 	csamidi_xmit(scp);
361 }
362 
363 static void
364 csamidi_xmit(sc_p scp)
365 {
366 	register mididev_info *devinfo;
367 	register midi_dbuf *dbuf;
368 	u_char c;
369 
370 	devinfo = scp->devinfo;
371 
372 	mtx_assert(&devinfo->flagqueue_mtx, MA_OWNED);
373 
374 	/* See which source to use. */
375 	if ((devinfo->flags & MIDI_F_PASSTHRU) == 0 || ((devinfo->flags & MIDI_F_BUSY) != 0 && (devinfo->fflags & FWRITE) != 0))
376 		dbuf = &devinfo->midi_dbuf_out;
377 	else
378 		dbuf = &devinfo->midi_dbuf_passthru;
379 
380 	/* Transmit the data in the queue. */
381 	while ((devinfo->flags & MIDI_F_WRITING) != 0) {
382 		/* Do we have the data to transmit? */
383 		if (dbuf->rl == 0) {
384 			/* Stop playing. */
385 			devinfo->flags &= ~MIDI_F_WRITING;
386 			break;
387 		} else {
388 			mtx_lock(&scp->mtx);
389 			if ((csamidi_status(scp) & MIDSR_TBF) != 0) {
390 				mtx_unlock(&scp->mtx);
391 				break;
392 			}
393 			/* Send the data. */
394 			midibuf_output_intr(dbuf, &c, sizeof(c));
395 			csamidi_writedata(scp, c);
396 			/* We are playing now. */
397 			devinfo->flags |= MIDI_F_WRITING;
398 			mtx_unlock(&scp->mtx);
399 		}
400 	}
401 }
402 
403 /* Reset midi. */
404 static int
405 csamidi_reset(sc_p scp)
406 {
407 	int i, resp;
408 
409 	mtx_lock(&scp->mtx);
410 
411 	/* Reset the midi. */
412 	resp = 0;
413 	for (i = 0 ; i < CSAMIDI_TRYDATA ; i++) {
414 		resp = csamidi_command(scp, MIDCR_MRST);
415 		if (resp == 0)
416 			break;
417 	}
418 	if (resp != 0) {
419 		mtx_unlock(&scp->mtx);
420 		return (1);
421 	}
422 	for (i = 0 ; i < CSAMIDI_TRYDATA ; i++) {
423 		resp = csamidi_command(scp, MIDCR_TXE | MIDCR_RXE | MIDCR_RIE | MIDCR_TIE);
424 		if (resp == 0)
425 			break;
426 	}
427 	if (resp != 0)
428 		return (1);
429 
430 	mtx_unlock(&scp->mtx);
431 
432 	DELAY(CSAMIDI_DELAY);
433 
434 	return (0);
435 }
436 
437 /* Reads the status. */
438 static int
439 csamidi_status(sc_p scp)
440 {
441 	return csamidi_readio(scp, BA0_MIDSR);
442 }
443 
444 /* Writes a command. */
445 static int
446 csamidi_command(sc_p scp, u_int32_t value)
447 {
448 	csamidi_writeio(scp, BA0_MIDCR, value);
449 
450 	return (0);
451 }
452 
453 /* Reads a byte of data. */
454 static int
455 csamidi_readdata(sc_p scp)
456 {
457 	u_int status;
458 
459 	/* Is the interface ready to read? */
460 	status = csamidi_status(scp);
461 	if ((status & MIDSR_RBE) != 0)
462 		/* The interface is busy. */
463 		return (-EAGAIN);
464 
465 	return (int)csamidi_readio(scp, BA0_MIDRP) & 0xff;
466 }
467 
468 /* Writes a byte of data. */
469 static int
470 csamidi_writedata(sc_p scp, u_int32_t value)
471 {
472 	u_int status;
473 
474 	/* Is the interface ready to write? */
475 	status = csamidi_status(scp);
476 	if ((status & MIDSR_TBF) != 0)
477 		/* The interface is busy. */
478 		return (EAGAIN);
479 
480 	csamidi_writeio(scp, BA0_MIDWP, value & 0xff);
481 
482 	return (0);
483 }
484 
485 static u_int32_t
486 csamidi_readio(sc_p scp, u_long offset)
487 {
488 	if (offset < BA0_AC97_RESET)
489 		return bus_space_read_4(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), offset) & 0xffffffff;
490 	else
491 		return (0);
492 }
493 
494 static void
495 csamidi_writeio(sc_p scp, u_long offset, u_int32_t data)
496 {
497 	if (offset < BA0_AC97_RESET)
498 		bus_space_write_4(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), offset, data);
499 }
500 
501 /* Not used in this file. */
502 #if notdef
503 static u_int32_t
504 csamidi_readmem(sc_p scp, u_long offset)
505 {
506 	return bus_space_read_4(rman_get_bustag(scp->mem), rman_get_bushandle(scp->mem), offset) & 0xffffffff;
507 }
508 
509 static void
510 csamidi_writemem(sc_p scp, u_long offset, u_int32_t data)
511 {
512 	bus_space_write_4(rman_get_bustag(scp->mem), rman_get_bushandle(scp->mem), offset, data);
513 }
514 #endif /* notdef */
515 
516 /* Allocates resources. */
517 static int
518 csamidi_allocres(sc_p scp, device_t dev)
519 {
520 	if (scp->io == NULL) {
521 		scp->io = bus_alloc_resource(dev, SYS_RES_MEMORY, &scp->io_rid, 0, ~0, 1, RF_ACTIVE);
522 		if (scp->io == NULL)
523 			return (1);
524 	}
525 	if (scp->mem == NULL) {
526 		scp->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &scp->mem_rid, 0, ~0, 1, RF_ACTIVE);
527 		if (scp->mem == NULL)
528 			return (1);
529 	}
530 	if (scp->irq == NULL) {
531 		scp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &scp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
532 		if (scp->irq == NULL)
533 			return (1);
534 	}
535 
536 	return (0);
537 }
538 
539 /* Releases resources. */
540 static void
541 csamidi_releaseres(sc_p scp, device_t dev)
542 {
543 	if (scp->irq != NULL) {
544 		bus_release_resource(dev, SYS_RES_IRQ, scp->irq_rid, scp->irq);
545 		scp->irq = NULL;
546 	}
547 	if (scp->io != NULL) {
548 		bus_release_resource(dev, SYS_RES_MEMORY, scp->io_rid, scp->io);
549 		scp->io = NULL;
550 	}
551 	if (scp->mem != NULL) {
552 		bus_release_resource(dev, SYS_RES_MEMORY, scp->mem_rid, scp->mem);
553 		scp->mem = NULL;
554 	}
555 }
556 
557 static device_method_t csamidi_methods[] = {
558 	/* Device interface */
559 	DEVMETHOD(device_probe , csamidi_probe ),
560 	DEVMETHOD(device_attach, csamidi_attach),
561 
562 	{ 0, 0 },
563 };
564 
565 static driver_t csamidi_driver = {
566 	"midi",
567 	csamidi_methods,
568 	sizeof(struct csamidi_softc),
569 };
570 
571 DRIVER_MODULE(csamidi, csa, csamidi_driver, midi_devclass, 0, 0);
572