1 /* $OpenBSD: midi.c,v 1.58 2024/12/30 02:46:00 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 2003, 2004 Alexandre Ratchov 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/fcntl.h> 21 #include <sys/systm.h> 22 #include <sys/ioctl.h> 23 #include <sys/conf.h> 24 #include <sys/kernel.h> 25 #include <sys/timeout.h> 26 #include <sys/vnode.h> 27 #include <sys/signalvar.h> 28 #include <sys/device.h> 29 30 #include <dev/midi_if.h> 31 #include <dev/audio_if.h> 32 #include <dev/midivar.h> 33 34 #define DEVNAME(sc) ((sc)->dev.dv_xname) 35 36 int midiopen(dev_t, int, int, struct proc *); 37 int midiclose(dev_t, int, int, struct proc *); 38 int midiread(dev_t, struct uio *, int); 39 int midiwrite(dev_t, struct uio *, int); 40 int midikqfilter(dev_t, struct knote *); 41 int midiioctl(dev_t, u_long, caddr_t, int, struct proc *); 42 int midiprobe(struct device *, void *, void *); 43 void midiattach(struct device *, struct device *, void *); 44 int mididetach(struct device *, int); 45 int midiprint(void *, const char *); 46 47 void midi_iintr(void *, int); 48 void midi_ointr(void *); 49 void midi_timeout(void *); 50 void midi_out_start(struct midi_softc *); 51 void midi_out_stop(struct midi_softc *); 52 void midi_out_do(struct midi_softc *); 53 54 55 const struct cfattach midi_ca = { 56 sizeof(struct midi_softc), midiprobe, midiattach, mididetach 57 }; 58 59 struct cfdriver midi_cd = { 60 NULL, "midi", DV_DULL 61 }; 62 63 64 void filt_midiwdetach(struct knote *); 65 int filt_midiwrite(struct knote *, long); 66 int filt_midimodify(struct kevent *, struct knote *); 67 int filt_midiprocess(struct knote *, struct kevent *); 68 69 const struct filterops midiwrite_filtops = { 70 .f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE, 71 .f_attach = NULL, 72 .f_detach = filt_midiwdetach, 73 .f_event = filt_midiwrite, 74 .f_modify = filt_midimodify, 75 .f_process = filt_midiprocess, 76 }; 77 78 void filt_midirdetach(struct knote *); 79 int filt_midiread(struct knote *, long); 80 81 const struct filterops midiread_filtops = { 82 .f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE, 83 .f_attach = NULL, 84 .f_detach = filt_midirdetach, 85 .f_event = filt_midiread, 86 .f_modify = filt_midimodify, 87 .f_process = filt_midiprocess, 88 }; 89 90 void 91 midi_buf_wakeup(struct midi_buffer *buf) 92 { 93 if (buf->blocking) { 94 wakeup(&buf->blocking); 95 buf->blocking = 0; 96 } 97 knote_locked(&buf->klist, 0); 98 } 99 100 void 101 midi_iintr(void *addr, int data) 102 { 103 struct midi_softc *sc = (struct midi_softc *)addr; 104 struct midi_buffer *mb = &sc->inbuf; 105 106 MUTEX_ASSERT_LOCKED(&audio_lock); 107 if (!(sc->dev.dv_flags & DVF_ACTIVE) || !(sc->flags & FREAD)) 108 return; 109 110 if (MIDIBUF_ISFULL(mb)) 111 return; /* discard data */ 112 113 MIDIBUF_WRITE(mb, data); 114 115 midi_buf_wakeup(mb); 116 } 117 118 int 119 midiread(dev_t dev, struct uio *uio, int ioflag) 120 { 121 struct midi_softc *sc; 122 struct midi_buffer *mb; 123 size_t count; 124 int error; 125 126 sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev)); 127 if (sc == NULL) 128 return ENXIO; 129 if (!(sc->flags & FREAD)) { 130 error = ENXIO; 131 goto done; 132 } 133 mb = &sc->inbuf; 134 135 /* if there is no data then sleep (unless IO_NDELAY flag is set) */ 136 error = 0; 137 mtx_enter(&audio_lock); 138 while (MIDIBUF_ISEMPTY(mb)) { 139 if (ioflag & IO_NDELAY) { 140 error = EWOULDBLOCK; 141 goto done_mtx; 142 } 143 sc->inbuf.blocking = 1; 144 error = msleep_nsec(&sc->inbuf.blocking, &audio_lock, 145 PWAIT | PCATCH, "mid_rd", INFSLP); 146 if (!(sc->dev.dv_flags & DVF_ACTIVE)) 147 error = EIO; 148 if (error) 149 goto done_mtx; 150 } 151 152 /* at this stage, there is at least 1 byte */ 153 154 while (uio->uio_resid > 0 && mb->used > 0) { 155 count = MIDIBUF_SIZE - mb->start; 156 if (count > mb->used) 157 count = mb->used; 158 if (count > uio->uio_resid) 159 count = uio->uio_resid; 160 mtx_leave(&audio_lock); 161 error = uiomove(mb->data + mb->start, count, uio); 162 if (error) 163 goto done; 164 mtx_enter(&audio_lock); 165 MIDIBUF_REMOVE(mb, count); 166 } 167 168 done_mtx: 169 mtx_leave(&audio_lock); 170 done: 171 device_unref(&sc->dev); 172 return error; 173 } 174 175 void 176 midi_ointr(void *addr) 177 { 178 struct midi_softc *sc = (struct midi_softc *)addr; 179 struct midi_buffer *mb; 180 181 MUTEX_ASSERT_LOCKED(&audio_lock); 182 if (!(sc->dev.dv_flags & DVF_ACTIVE) || !(sc->flags & FWRITE)) 183 return; 184 185 mb = &sc->outbuf; 186 if (mb->used > 0) { 187 #ifdef MIDI_DEBUG 188 if (!sc->isbusy) { 189 printf("midi_ointr: output must be busy\n"); 190 } 191 #endif 192 midi_out_do(sc); 193 } else if (sc->isbusy) 194 midi_out_stop(sc); 195 } 196 197 void 198 midi_timeout(void *addr) 199 { 200 mtx_enter(&audio_lock); 201 midi_ointr(addr); 202 mtx_leave(&audio_lock); 203 } 204 205 void 206 midi_out_start(struct midi_softc *sc) 207 { 208 if (!sc->isbusy) { 209 sc->isbusy = 1; 210 midi_out_do(sc); 211 } 212 } 213 214 void 215 midi_out_stop(struct midi_softc *sc) 216 { 217 sc->isbusy = 0; 218 midi_buf_wakeup(&sc->outbuf); 219 } 220 221 void 222 midi_out_do(struct midi_softc *sc) 223 { 224 struct midi_buffer *mb = &sc->outbuf; 225 226 while (mb->used > 0) { 227 if (!sc->hw_if->output(sc->hw_hdl, mb->data[mb->start])) 228 break; 229 MIDIBUF_REMOVE(mb, 1); 230 if (MIDIBUF_ISEMPTY(mb)) { 231 if (sc->hw_if->flush != NULL) 232 sc->hw_if->flush(sc->hw_hdl); 233 midi_out_stop(sc); 234 return; 235 } 236 } 237 238 if (!(sc->props & MIDI_PROP_OUT_INTR)) { 239 if (MIDIBUF_ISEMPTY(mb)) 240 midi_out_stop(sc); 241 else 242 timeout_add(&sc->timeo, 1); 243 } 244 } 245 246 int 247 midiwrite(dev_t dev, struct uio *uio, int ioflag) 248 { 249 struct midi_softc *sc; 250 struct midi_buffer *mb; 251 size_t count; 252 int error; 253 254 sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev)); 255 if (sc == NULL) 256 return ENXIO; 257 if (!(sc->flags & FWRITE)) { 258 error = ENXIO; 259 goto done; 260 } 261 mb = &sc->outbuf; 262 263 /* 264 * If IO_NDELAY flag is set then check if there is enough room 265 * in the buffer to store at least one byte. If not then dont 266 * start the write process. 267 */ 268 error = 0; 269 mtx_enter(&audio_lock); 270 if ((ioflag & IO_NDELAY) && MIDIBUF_ISFULL(mb) && (uio->uio_resid > 0)) { 271 error = EWOULDBLOCK; 272 goto done_mtx; 273 } 274 275 while (uio->uio_resid > 0) { 276 while (MIDIBUF_ISFULL(mb)) { 277 if (ioflag & IO_NDELAY) { 278 /* 279 * At this stage at least one byte is already 280 * moved so we do not return EWOULDBLOCK 281 */ 282 goto done_mtx; 283 } 284 sc->outbuf.blocking = 1; 285 error = msleep_nsec(&sc->outbuf.blocking, &audio_lock, 286 PWAIT | PCATCH, "mid_wr", INFSLP); 287 if (!(sc->dev.dv_flags & DVF_ACTIVE)) 288 error = EIO; 289 if (error) 290 goto done_mtx; 291 } 292 293 count = MIDIBUF_SIZE - MIDIBUF_END(mb); 294 if (count > MIDIBUF_AVAIL(mb)) 295 count = MIDIBUF_AVAIL(mb); 296 if (count > uio->uio_resid) 297 count = uio->uio_resid; 298 mtx_leave(&audio_lock); 299 error = uiomove(mb->data + MIDIBUF_END(mb), count, uio); 300 if (error) 301 goto done; 302 mtx_enter(&audio_lock); 303 mb->used += count; 304 midi_out_start(sc); 305 } 306 307 done_mtx: 308 mtx_leave(&audio_lock); 309 done: 310 device_unref(&sc->dev); 311 return error; 312 } 313 314 int 315 midikqfilter(dev_t dev, struct knote *kn) 316 { 317 struct midi_softc *sc; 318 struct klist *klist; 319 int error; 320 321 sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev)); 322 if (sc == NULL) 323 return ENXIO; 324 error = 0; 325 switch (kn->kn_filter) { 326 case EVFILT_READ: 327 klist = &sc->inbuf.klist; 328 kn->kn_fop = &midiread_filtops; 329 break; 330 case EVFILT_WRITE: 331 klist = &sc->outbuf.klist; 332 kn->kn_fop = &midiwrite_filtops; 333 break; 334 default: 335 error = EINVAL; 336 goto done; 337 } 338 kn->kn_hook = (void *)sc; 339 340 klist_insert(klist, kn); 341 done: 342 device_unref(&sc->dev); 343 return error; 344 } 345 346 void 347 filt_midirdetach(struct knote *kn) 348 { 349 struct midi_softc *sc = (struct midi_softc *)kn->kn_hook; 350 351 klist_remove(&sc->inbuf.klist, kn); 352 } 353 354 int 355 filt_midiread(struct knote *kn, long hint) 356 { 357 struct midi_softc *sc = (struct midi_softc *)kn->kn_hook; 358 359 return (!MIDIBUF_ISEMPTY(&sc->inbuf)); 360 } 361 362 void 363 filt_midiwdetach(struct knote *kn) 364 { 365 struct midi_softc *sc = (struct midi_softc *)kn->kn_hook; 366 367 klist_remove(&sc->outbuf.klist, kn); 368 } 369 370 int 371 filt_midiwrite(struct knote *kn, long hint) 372 { 373 struct midi_softc *sc = (struct midi_softc *)kn->kn_hook; 374 375 return (!MIDIBUF_ISFULL(&sc->outbuf)); 376 } 377 378 int 379 filt_midimodify(struct kevent *kev, struct knote *kn) 380 { 381 int active; 382 383 mtx_enter(&audio_lock); 384 active = knote_modify(kev, kn); 385 mtx_leave(&audio_lock); 386 387 return active; 388 } 389 390 int 391 filt_midiprocess(struct knote *kn, struct kevent *kev) 392 { 393 int active; 394 395 mtx_enter(&audio_lock); 396 active = knote_process(kn, kev); 397 mtx_leave(&audio_lock); 398 399 return active; 400 } 401 402 int 403 midiioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) 404 { 405 struct midi_softc *sc; 406 int error; 407 408 sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev)); 409 if (sc == NULL) 410 return ENXIO; 411 error = 0; 412 switch(cmd) { 413 default: 414 error = ENOTTY; 415 } 416 device_unref(&sc->dev); 417 return error; 418 } 419 420 int 421 midiopen(dev_t dev, int flags, int mode, struct proc *p) 422 { 423 struct midi_softc *sc; 424 int error; 425 426 sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev)); 427 if (sc == NULL) 428 return ENXIO; 429 error = 0; 430 if (sc->flags) { 431 error = EBUSY; 432 goto done; 433 } 434 MIDIBUF_INIT(&sc->inbuf); 435 MIDIBUF_INIT(&sc->outbuf); 436 sc->isbusy = 0; 437 sc->inbuf.blocking = sc->outbuf.blocking = 0; 438 sc->flags = flags; 439 error = sc->hw_if->open(sc->hw_hdl, flags, midi_iintr, midi_ointr, sc); 440 if (error) 441 sc->flags = 0; 442 done: 443 device_unref(&sc->dev); 444 return error; 445 } 446 447 int 448 midiclose(dev_t dev, int fflag, int devtype, struct proc *p) 449 { 450 struct midi_softc *sc; 451 struct midi_buffer *mb; 452 int error; 453 454 sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev)); 455 if (sc == NULL) 456 return ENXIO; 457 458 /* start draining output buffer */ 459 error = 0; 460 mb = &sc->outbuf; 461 mtx_enter(&audio_lock); 462 if (!MIDIBUF_ISEMPTY(mb)) 463 midi_out_start(sc); 464 while (sc->isbusy) { 465 sc->outbuf.blocking = 1; 466 error = msleep_nsec(&sc->outbuf.blocking, &audio_lock, 467 PWAIT, "mid_dr", SEC_TO_NSEC(5)); 468 if (!(sc->dev.dv_flags & DVF_ACTIVE)) 469 error = EIO; 470 if (error) 471 break; 472 } 473 mtx_leave(&audio_lock); 474 475 /* 476 * some hw_if->close() reset immediately the midi uart 477 * which flushes the internal buffer of the uart device, 478 * so we may lose some (important) data. To avoid this, 479 * sleep 20ms (around 64 bytes) to give the time to the 480 * uart to drain its internal buffers. 481 */ 482 tsleep_nsec(&sc->outbuf.blocking, PWAIT, "mid_cl", MSEC_TO_NSEC(20)); 483 sc->hw_if->close(sc->hw_hdl); 484 sc->flags = 0; 485 device_unref(&sc->dev); 486 return 0; 487 } 488 489 int 490 midiprobe(struct device *parent, void *match, void *aux) 491 { 492 struct audio_attach_args *sa = aux; 493 494 return (sa != NULL && (sa->type == AUDIODEV_TYPE_MIDI) ? 1 : 0); 495 } 496 497 void 498 midiattach(struct device *parent, struct device *self, void *aux) 499 { 500 struct midi_info mi; 501 struct midi_softc *sc = (struct midi_softc *)self; 502 struct audio_attach_args *sa = (struct audio_attach_args *)aux; 503 const struct midi_hw_if *hwif = sa->hwif; 504 void *hdl = sa->hdl; 505 506 #ifdef DIAGNOSTIC 507 if (hwif == 0 || 508 hwif->open == 0 || 509 hwif->close == 0 || 510 hwif->output == 0 || 511 hwif->getinfo == 0) { 512 printf("%s: missing method\n", DEVNAME(sc)); 513 return; 514 } 515 #endif 516 517 klist_init_mutex(&sc->inbuf.klist, &audio_lock); 518 klist_init_mutex(&sc->outbuf.klist, &audio_lock); 519 520 sc->hw_if = hwif; 521 sc->hw_hdl = hdl; 522 sc->hw_if->getinfo(sc->hw_hdl, &mi); 523 sc->props = mi.props; 524 sc->flags = 0; 525 timeout_set(&sc->timeo, midi_timeout, sc); 526 printf(": <%s>\n", mi.name); 527 } 528 529 int 530 mididetach(struct device *self, int flags) 531 { 532 struct midi_softc *sc = (struct midi_softc *)self; 533 int maj, mn; 534 535 /* locate the major number */ 536 for (maj = 0; maj < nchrdev; maj++) { 537 if (cdevsw[maj].d_open == midiopen) { 538 /* Nuke the vnodes for any open instances (calls close). */ 539 mn = self->dv_unit; 540 vdevgone(maj, mn, mn, VCHR); 541 } 542 } 543 544 /* 545 * The close() method did nothing (device_lookup() returns 546 * NULL), so quickly halt transfers (normally parent is already 547 * gone, and code below is no-op), and wake-up user-land blocked 548 * in read/write/ioctl, which return EIO. 549 */ 550 if (sc->flags) { 551 KERNEL_ASSERT_LOCKED(); 552 if (sc->flags & FREAD) 553 wakeup(&sc->inbuf.blocking); 554 if (sc->flags & FWRITE) 555 wakeup(&sc->outbuf.blocking); 556 sc->hw_if->close(sc->hw_hdl); 557 sc->flags = 0; 558 } 559 560 klist_invalidate(&sc->inbuf.klist); 561 klist_invalidate(&sc->outbuf.klist); 562 klist_free(&sc->inbuf.klist); 563 klist_free(&sc->outbuf.klist); 564 565 return 0; 566 } 567 568 int 569 midiprint(void *aux, const char *pnp) 570 { 571 if (pnp) 572 printf("midi at %s", pnp); 573 return (UNCONF); 574 } 575 576 struct device * 577 midi_attach_mi(const struct midi_hw_if *hwif, void *hdl, struct device *dev) 578 { 579 struct audio_attach_args arg; 580 581 arg.type = AUDIODEV_TYPE_MIDI; 582 arg.hwif = hwif; 583 arg.hdl = hdl; 584 return config_found(dev, &arg, midiprint); 585 } 586