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