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
midi_buf_wakeup(struct midi_buffer * buf)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
midi_iintr(void * addr,int data)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
midiread(dev_t dev,struct uio * uio,int ioflag)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
midi_ointr(void * addr)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
midi_timeout(void * addr)198 midi_timeout(void *addr)
199 {
200 mtx_enter(&audio_lock);
201 midi_ointr(addr);
202 mtx_leave(&audio_lock);
203 }
204
205 void
midi_out_start(struct midi_softc * sc)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
midi_out_stop(struct midi_softc * sc)215 midi_out_stop(struct midi_softc *sc)
216 {
217 sc->isbusy = 0;
218 midi_buf_wakeup(&sc->outbuf);
219 }
220
221 void
midi_out_do(struct midi_softc * sc)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
midiwrite(dev_t dev,struct uio * uio,int ioflag)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
midikqfilter(dev_t dev,struct knote * kn)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
filt_midirdetach(struct knote * kn)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
filt_midiread(struct knote * kn,long hint)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
filt_midiwdetach(struct knote * kn)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
filt_midiwrite(struct knote * kn,long hint)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
filt_midimodify(struct kevent * kev,struct knote * kn)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
filt_midiprocess(struct knote * kn,struct kevent * kev)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
midiioctl(dev_t dev,u_long cmd,caddr_t addr,int flag,struct proc * p)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
midiopen(dev_t dev,int flags,int mode,struct proc * p)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
midiclose(dev_t dev,int fflag,int devtype,struct proc * p)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
midiprobe(struct device * parent,void * match,void * aux)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
midiattach(struct device * parent,struct device * self,void * aux)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
mididetach(struct device * self,int flags)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
midiprint(void * aux,const char * pnp)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 *
midi_attach_mi(const struct midi_hw_if * hwif,void * hdl,struct device * dev)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