xref: /freebsd/sys/dev/sound/pci/csamidi.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
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 <dev/pci/pcireg.h>
35 #include <dev/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, u_int8_t *value);
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_BAR(0);
169 	scp->mem_rid = PCIR_BAR(1);
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", NULL, 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 thread *td)
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 	MIDI_DEBUG(printf("csamidi_ioctl: unit %d, cmd %s.\n", unit, midi_cmdname(cmd, cmdtab_midiioctl)));
228 
229 	devinfo = get_mididev_info(i_dev, &unit);
230 	if (devinfo == NULL) {
231 		MIDI_DEBUG(printf("csamidi_ioctl: unit %d is not configured.\n", unit));
232 		return (ENXIO);
233 	}
234 	scp = devinfo->softc;
235 
236 	switch (cmd) {
237 	case SNDCTL_SYNTH_INFO:
238 		synthinfo = (struct synth_info *)arg;
239 		if (synthinfo->device != unit)
240 			return (ENXIO);
241 		bcopy(&csamidi_synthinfo, synthinfo, sizeof(csamidi_synthinfo));
242 		synthinfo->device = unit;
243 		return (0);
244 		break;
245 	case SNDCTL_MIDI_INFO:
246 		midiinfo = (struct midi_info *)arg;
247 		if (midiinfo->device != unit)
248 			return (ENXIO);
249 		bcopy(&csamidi_midiinfo, midiinfo, sizeof(csamidi_midiinfo));
250 		midiinfo->device = unit;
251 		return (0);
252 		break;
253 	default:
254 		return (ENOSYS);
255 	}
256 	/* NOTREACHED */
257 	return (EINVAL);
258 }
259 
260 static void
261 csamidi_intr(void *arg)
262 {
263 	sc_p scp;
264 	u_char c;
265 	mididev_info *devinfo;
266 	int leni;
267 
268 	scp = (sc_p)arg;
269 	devinfo = scp->devinfo;
270 
271 	mtx_lock(&devinfo->flagqueue_mtx);
272 	mtx_lock(&scp->mtx);
273 
274 	/* Read the received data. */
275 	while ((csamidi_status(scp) & MIDSR_RBE) == 0) {
276 		/* Receive the data. */
277 		csamidi_readdata(scp, &c);
278 		mtx_unlock(&scp->mtx);
279 
280 		/* Queue into the passthru buffer and start transmitting if we can. */
281 		if ((devinfo->flags & MIDI_F_PASSTHRU) != 0 && ((devinfo->flags & MIDI_F_BUSY) == 0 || (devinfo->fflags & FWRITE) == 0)) {
282 			midibuf_input_intr(&devinfo->midi_dbuf_passthru, &c, sizeof(c), &leni);
283 			devinfo->callback(devinfo, MIDI_CB_START | MIDI_CB_WR);
284 		}
285 		/* Queue if we are reading. Discard an active sensing. */
286 		if ((devinfo->flags & MIDI_F_READING) != 0 && c != 0xfe) {
287 			midibuf_input_intr(&devinfo->midi_dbuf_in, &c, sizeof(c), &leni);
288 		}
289 		mtx_lock(&scp->mtx);
290 	}
291 	mtx_unlock(&scp->mtx);
292 
293 	/* Transmit out data. */
294 	if ((devinfo->flags & MIDI_F_WRITING) != 0 && (csamidi_status(scp) & MIDSR_TBF) == 0)
295 		csamidi_xmit(scp);
296 
297 	mtx_unlock(&devinfo->flagqueue_mtx);
298 
299 	/* Invoke the upper layer. */
300 	midi_intr(devinfo);
301 }
302 
303 static int
304 csamidi_callback(void *di, int reason)
305 {
306 	int unit;
307 	sc_p scp;
308 	mididev_info *d;
309 
310 	d = (mididev_info *)di;
311 
312 	mtx_assert(&d->flagqueue_mtx, MA_OWNED);
313 
314 	if (d == NULL) {
315 		MIDI_DEBUG(printf("csamidi_callback: device not configured.\n"));
316 		return (ENXIO);
317 	}
318 
319 	unit = d->unit;
320 	scp = d->softc;
321 
322 	switch (reason & MIDI_CB_REASON_MASK) {
323 	case MIDI_CB_START:
324 		if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) == 0)
325 			/* Begin recording. */
326 			d->flags |= MIDI_F_READING;
327 		if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) == 0)
328 			/* Start playing. */
329 			csamidi_startplay(scp);
330 		break;
331 	case MIDI_CB_STOP:
332 	case MIDI_CB_ABORT:
333 		if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) != 0)
334 			/* Stop recording. */
335 			d->flags &= ~MIDI_F_READING;
336 		if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) != 0)
337 			/* Stop Playing. */
338 			d->flags &= ~MIDI_F_WRITING;
339 		break;
340 	}
341 
342 	return (0);
343 }
344 
345 /*
346  * The functions below here are the libraries for the above ones.
347  */
348 
349 /*
350  * Starts to play the data in the output queue.
351  */
352 static void
353 csamidi_startplay(sc_p scp)
354 {
355 	mididev_info *devinfo;
356 
357 	devinfo = scp->devinfo;
358 
359 	mtx_assert(&devinfo->flagqueue_mtx, MA_OWNED);
360 
361 	/* Can we play now? */
362 	if (devinfo->midi_dbuf_out.rl == 0)
363 		return;
364 
365 	devinfo->flags |= MIDI_F_WRITING;
366 	csamidi_xmit(scp);
367 }
368 
369 static void
370 csamidi_xmit(sc_p scp)
371 {
372 	register mididev_info *devinfo;
373 	register midi_dbuf *dbuf;
374 	u_char c;
375 	int leno;
376 
377 	devinfo = scp->devinfo;
378 
379 	mtx_assert(&devinfo->flagqueue_mtx, MA_OWNED);
380 
381 	/* See which source to use. */
382 	if ((devinfo->flags & MIDI_F_PASSTHRU) == 0 || ((devinfo->flags & MIDI_F_BUSY) != 0 && (devinfo->fflags & FWRITE) != 0))
383 		dbuf = &devinfo->midi_dbuf_out;
384 	else
385 		dbuf = &devinfo->midi_dbuf_passthru;
386 
387 	/* Transmit the data in the queue. */
388 	while ((devinfo->flags & MIDI_F_WRITING) != 0) {
389 		/* Do we have the data to transmit? */
390 		if (dbuf->rl == 0) {
391 			/* Stop playing. */
392 			devinfo->flags &= ~MIDI_F_WRITING;
393 			break;
394 		} else {
395 			mtx_lock(&scp->mtx);
396 			if ((csamidi_status(scp) & MIDSR_TBF) != 0) {
397 				mtx_unlock(&scp->mtx);
398 				break;
399 			}
400 			/* Send the data. */
401 			midibuf_output_intr(dbuf, &c, sizeof(c), &leno);
402 			csamidi_writedata(scp, c);
403 			/* We are playing now. */
404 			devinfo->flags |= MIDI_F_WRITING;
405 			mtx_unlock(&scp->mtx);
406 		}
407 	}
408 }
409 
410 /* Reset midi. */
411 static int
412 csamidi_reset(sc_p scp)
413 {
414 	int i, resp;
415 
416 	mtx_lock(&scp->mtx);
417 
418 	/* Reset the midi. */
419 	resp = 0;
420 	for (i = 0 ; i < CSAMIDI_TRYDATA ; i++) {
421 		resp = csamidi_command(scp, MIDCR_MRST);
422 		if (resp == 0)
423 			break;
424 	}
425 	if (resp != 0) {
426 		mtx_unlock(&scp->mtx);
427 		return (1);
428 	}
429 	for (i = 0 ; i < CSAMIDI_TRYDATA ; i++) {
430 		resp = csamidi_command(scp, MIDCR_TXE | MIDCR_RXE | MIDCR_RIE | MIDCR_TIE);
431 		if (resp == 0)
432 			break;
433 	}
434 	if (resp != 0)
435 		return (1);
436 
437 	mtx_unlock(&scp->mtx);
438 
439 	DELAY(CSAMIDI_DELAY);
440 
441 	return (0);
442 }
443 
444 /* Reads the status. */
445 static int
446 csamidi_status(sc_p scp)
447 {
448 	return csamidi_readio(scp, BA0_MIDSR);
449 }
450 
451 /* Writes a command. */
452 static int
453 csamidi_command(sc_p scp, u_int32_t value)
454 {
455 	csamidi_writeio(scp, BA0_MIDCR, value);
456 
457 	return (0);
458 }
459 
460 /* Reads a byte of data. */
461 static int
462 csamidi_readdata(sc_p scp, u_int8_t *value)
463 {
464 	u_int status;
465 
466 	if (value == NULL)
467 		return (EINVAL);
468 
469 	/* Is the interface ready to read? */
470 	status = csamidi_status(scp);
471 	if ((status & MIDSR_RBE) != 0)
472 		/* The interface is busy. */
473 		return (EAGAIN);
474 
475 	*value = (u_int8_t)(csamidi_readio(scp, BA0_MIDRP) & 0xff);
476 
477 	return (0);
478 }
479 
480 /* Writes a byte of data. */
481 static int
482 csamidi_writedata(sc_p scp, u_int32_t value)
483 {
484 	u_int status;
485 
486 	/* Is the interface ready to write? */
487 	status = csamidi_status(scp);
488 	if ((status & MIDSR_TBF) != 0)
489 		/* The interface is busy. */
490 		return (EAGAIN);
491 
492 	csamidi_writeio(scp, BA0_MIDWP, value & 0xff);
493 
494 	return (0);
495 }
496 
497 static u_int32_t
498 csamidi_readio(sc_p scp, u_long offset)
499 {
500 	if (offset < BA0_AC97_RESET)
501 		return bus_space_read_4(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), offset) & 0xffffffff;
502 	else
503 		return (0);
504 }
505 
506 static void
507 csamidi_writeio(sc_p scp, u_long offset, u_int32_t data)
508 {
509 	if (offset < BA0_AC97_RESET)
510 		bus_space_write_4(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), offset, data);
511 }
512 
513 /* Not used in this file. */
514 #if notdef
515 static u_int32_t
516 csamidi_readmem(sc_p scp, u_long offset)
517 {
518 	return bus_space_read_4(rman_get_bustag(scp->mem), rman_get_bushandle(scp->mem), offset) & 0xffffffff;
519 }
520 
521 static void
522 csamidi_writemem(sc_p scp, u_long offset, u_int32_t data)
523 {
524 	bus_space_write_4(rman_get_bustag(scp->mem), rman_get_bushandle(scp->mem), offset, data);
525 }
526 #endif /* notdef */
527 
528 /* Allocates resources. */
529 static int
530 csamidi_allocres(sc_p scp, device_t dev)
531 {
532 	if (scp->io == NULL) {
533 		scp->io = bus_alloc_resource(dev, SYS_RES_MEMORY, &scp->io_rid, 0, ~0, 1, RF_ACTIVE);
534 		if (scp->io == NULL)
535 			return (1);
536 	}
537 	if (scp->mem == NULL) {
538 		scp->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &scp->mem_rid, 0, ~0, 1, RF_ACTIVE);
539 		if (scp->mem == NULL)
540 			return (1);
541 	}
542 	if (scp->irq == NULL) {
543 		scp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &scp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
544 		if (scp->irq == NULL)
545 			return (1);
546 	}
547 
548 	return (0);
549 }
550 
551 /* Releases resources. */
552 static void
553 csamidi_releaseres(sc_p scp, device_t dev)
554 {
555 	if (scp->irq != NULL) {
556 		bus_release_resource(dev, SYS_RES_IRQ, scp->irq_rid, scp->irq);
557 		scp->irq = NULL;
558 	}
559 	if (scp->io != NULL) {
560 		bus_release_resource(dev, SYS_RES_MEMORY, scp->io_rid, scp->io);
561 		scp->io = NULL;
562 	}
563 	if (scp->mem != NULL) {
564 		bus_release_resource(dev, SYS_RES_MEMORY, scp->mem_rid, scp->mem);
565 		scp->mem = NULL;
566 	}
567 }
568 
569 static device_method_t csamidi_methods[] = {
570 	/* Device interface */
571 	DEVMETHOD(device_probe , csamidi_probe ),
572 	DEVMETHOD(device_attach, csamidi_attach),
573 
574 	{ 0, 0 },
575 };
576 
577 static driver_t csamidi_driver = {
578 	"midi",
579 	csamidi_methods,
580 	sizeof(struct csamidi_softc),
581 };
582 
583 DRIVER_MODULE(csamidi, csa, csamidi_driver, midi_devclass, 0, 0);
584