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