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