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