1 /* $OpenBSD: video.c,v 1.59 2024/12/16 21:22:51 mvs Exp $ */
2
3 /*
4 * Copyright (c) 2008 Robert Nagy <robert@openbsd.org>
5 * Copyright (c) 2008 Marcus Glocker <mglocker@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/errno.h>
23 #include <sys/event.h>
24 #include <sys/ioctl.h>
25 #include <sys/fcntl.h>
26 #include <sys/device.h>
27 #include <sys/vnode.h>
28 #include <sys/kernel.h>
29 #include <sys/malloc.h>
30 #include <sys/mutex.h>
31 #include <sys/conf.h>
32 #include <sys/proc.h>
33 #include <sys/videoio.h>
34
35 #include <dev/video_if.h>
36
37 #include <uvm/uvm_extern.h>
38
39 /*
40 * Locks used to protect struct members and global data
41 * a atomic
42 * m sc_mtx
43 */
44
45 #ifdef VIDEO_DEBUG
46 int video_debug = 1;
47 #define DPRINTF(l, x...) do { if ((l) <= video_debug) printf(x); } while (0)
48 #else
49 #define DPRINTF(l, x...)
50 #endif
51
52 struct video_softc {
53 struct device dev;
54 void *hw_hdl; /* hardware driver handle */
55 struct device *sc_dev; /* hardware device struct */
56 const struct video_hw_if *hw_if; /* hardware interface */
57 char sc_dying; /* device detached */
58 struct process *sc_owner; /* owner process */
59 uint8_t sc_open; /* device opened */
60
61 struct mutex sc_mtx;
62 int sc_fsize;
63 uint8_t *sc_fbuffer;
64 caddr_t sc_fbuffer_mmap;
65 size_t sc_fbufferlen;
66 int sc_vidmode; /* access mode */
67 #define VIDMODE_NONE 0
68 #define VIDMODE_MMAP 1
69 #define VIDMODE_READ 2
70 int sc_frames_ready; /* [m] */
71
72 struct klist sc_rklist; /* [m] read selector */
73 };
74
75 int videoprobe(struct device *, void *, void *);
76 void videoattach(struct device *, struct device *, void *);
77 int videodetach(struct device *, int);
78 int videoactivate(struct device *, int);
79 int videoprint(void *, const char *);
80
81 void video_intr(void *);
82 int video_stop(struct video_softc *);
83 int video_claim(struct video_softc *, struct process *);
84
85 const struct cfattach video_ca = {
86 sizeof(struct video_softc), videoprobe, videoattach,
87 videodetach, videoactivate
88 };
89
90 struct cfdriver video_cd = {
91 NULL, "video", DV_DULL
92 };
93
94 /*
95 * Global flag to control if video recording is enabled by kern.video.record.
96 */
97 int video_record_enable = 0; /* [a] */
98
99 int
videoprobe(struct device * parent,void * match,void * aux)100 videoprobe(struct device *parent, void *match, void *aux)
101 {
102 return (1);
103 }
104
105 void
videoattach(struct device * parent,struct device * self,void * aux)106 videoattach(struct device *parent, struct device *self, void *aux)
107 {
108 struct video_softc *sc = (void *)self;
109 struct video_attach_args *sa = aux;
110
111 printf("\n");
112 sc->hw_if = sa->hwif;
113 sc->hw_hdl = sa->hdl;
114 sc->sc_dev = parent;
115 sc->sc_fbufferlen = 0;
116 sc->sc_owner = NULL;
117 mtx_init(&sc->sc_mtx, IPL_MPFLOOR);
118 klist_init_mutex(&sc->sc_rklist, &sc->sc_mtx);
119
120 if (sc->hw_if->get_bufsize)
121 sc->sc_fbufferlen = (sc->hw_if->get_bufsize)(sc->hw_hdl);
122 if (sc->sc_fbufferlen == 0) {
123 printf("video: could not request frame buffer size\n");
124 return;
125 }
126
127 sc->sc_fbuffer = malloc(sc->sc_fbufferlen, M_DEVBUF, M_NOWAIT);
128 if (sc->sc_fbuffer == NULL) {
129 printf("video: could not allocate frame buffer\n");
130 return;
131 }
132 }
133
134 int
videoopen(dev_t dev,int flags,int fmt,struct proc * p)135 videoopen(dev_t dev, int flags, int fmt, struct proc *p)
136 {
137 int unit = VIDEOUNIT(dev);
138 struct video_softc *sc;
139 int error = 0;
140
141 KERNEL_ASSERT_LOCKED();
142
143 if (unit >= video_cd.cd_ndevs ||
144 (sc = video_cd.cd_devs[unit]) == NULL ||
145 sc->hw_if == NULL)
146 return (ENXIO);
147
148 if (sc->sc_open) {
149 DPRINTF(1, "%s: device already open\n", __func__);
150 return (0);
151 }
152
153 sc->sc_vidmode = VIDMODE_NONE;
154 sc->sc_frames_ready = 0;
155
156 if (sc->hw_if->open != NULL) {
157 error = sc->hw_if->open(sc->hw_hdl, flags, &sc->sc_fsize,
158 sc->sc_fbuffer, video_intr, sc);
159 }
160 if (error == 0) {
161 sc->sc_open = 1;
162 DPRINTF(1, "%s: set device to open\n", __func__);
163 }
164
165 return (error);
166 }
167
168 int
videoclose(dev_t dev,int flags,int fmt,struct proc * p)169 videoclose(dev_t dev, int flags, int fmt, struct proc *p)
170 {
171 struct video_softc *sc;
172 int error = 0;
173
174 KERNEL_ASSERT_LOCKED();
175
176 DPRINTF(1, "%s: last close\n", __func__);
177
178 sc = video_cd.cd_devs[VIDEOUNIT(dev)];
179
180 error = video_stop(sc);
181 sc->sc_open = 0;
182
183 return (error);
184 }
185
186 int
videoread(dev_t dev,struct uio * uio,int ioflag)187 videoread(dev_t dev, struct uio *uio, int ioflag)
188 {
189 int unit = VIDEOUNIT(dev);
190 struct video_softc *sc;
191 int error;
192 size_t size;
193
194 KERNEL_ASSERT_LOCKED();
195
196 if (unit >= video_cd.cd_ndevs ||
197 (sc = video_cd.cd_devs[unit]) == NULL)
198 return (ENXIO);
199
200 if (sc->sc_dying)
201 return (EIO);
202
203 if (sc->sc_vidmode == VIDMODE_MMAP)
204 return (EBUSY);
205
206 if ((error = video_claim(sc, curproc->p_p)))
207 return (error);
208
209 /* start the stream if not already started */
210 if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) {
211 error = sc->hw_if->start_read(sc->hw_hdl);
212 if (error)
213 return (error);
214 sc->sc_vidmode = VIDMODE_READ;
215 }
216
217 DPRINTF(1, "resid=%zu\n", uio->uio_resid);
218
219 mtx_enter(&sc->sc_mtx);
220
221 if (sc->sc_frames_ready < 1) {
222 /* block userland read until a frame is ready */
223 error = msleep_nsec(sc, &sc->sc_mtx, PWAIT | PCATCH,
224 "vid_rd", INFSLP);
225 if (sc->sc_dying)
226 error = EIO;
227 if (error) {
228 mtx_leave(&sc->sc_mtx);
229 return (error);
230 }
231 }
232 sc->sc_frames_ready--;
233
234 mtx_leave(&sc->sc_mtx);
235
236 /* move no more than 1 frame to userland, as per specification */
237 size = ulmin(uio->uio_resid, sc->sc_fsize);
238 if (!atomic_load_int(&video_record_enable))
239 bzero(sc->sc_fbuffer, size);
240 error = uiomove(sc->sc_fbuffer, size, uio);
241 if (error)
242 return (error);
243
244 DPRINTF(1, "uiomove successfully done (%zu bytes)\n", size);
245
246 return (0);
247 }
248
249 int
videoioctl(dev_t dev,u_long cmd,caddr_t data,int flags,struct proc * p)250 videoioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
251 {
252 int unit = VIDEOUNIT(dev);
253 struct video_softc *sc;
254 struct v4l2_buffer *vb = (struct v4l2_buffer *)data;
255 int error;
256
257 KERNEL_ASSERT_LOCKED();
258
259 if (unit >= video_cd.cd_ndevs ||
260 (sc = video_cd.cd_devs[unit]) == NULL || sc->hw_if == NULL)
261 return (ENXIO);
262
263 DPRINTF(3, "video_ioctl(%zu, '%c', %zu)\n",
264 IOCPARM_LEN(cmd), (int) IOCGROUP(cmd), cmd & 0xff);
265
266 error = EOPNOTSUPP;
267 switch (cmd) {
268 case VIDIOC_G_CTRL:
269 if (sc->hw_if->g_ctrl)
270 error = (sc->hw_if->g_ctrl)(sc->hw_hdl,
271 (struct v4l2_control *)data);
272 break;
273 case VIDIOC_S_CTRL:
274 if (sc->hw_if->s_ctrl)
275 error = (sc->hw_if->s_ctrl)(sc->hw_hdl,
276 (struct v4l2_control *)data);
277 break;
278 default:
279 error = (ENOTTY);
280 }
281 if (error != ENOTTY)
282 return (error);
283
284 if ((error = video_claim(sc, p->p_p)))
285 return (error);
286
287 /*
288 * The following IOCTLs can only be called by the device owner.
289 * For further shared IOCTLs please move it up.
290 */
291 error = EOPNOTSUPP;
292 switch (cmd) {
293 case VIDIOC_QUERYCAP:
294 if (sc->hw_if->querycap)
295 error = (sc->hw_if->querycap)(sc->hw_hdl,
296 (struct v4l2_capability *)data);
297 break;
298 case VIDIOC_ENUM_FMT:
299 if (sc->hw_if->enum_fmt)
300 error = (sc->hw_if->enum_fmt)(sc->hw_hdl,
301 (struct v4l2_fmtdesc *)data);
302 break;
303 case VIDIOC_ENUM_FRAMESIZES:
304 if (sc->hw_if->enum_fsizes)
305 error = (sc->hw_if->enum_fsizes)(sc->hw_hdl,
306 (struct v4l2_frmsizeenum *)data);
307 break;
308 case VIDIOC_ENUM_FRAMEINTERVALS:
309 if (sc->hw_if->enum_fivals)
310 error = (sc->hw_if->enum_fivals)(sc->hw_hdl,
311 (struct v4l2_frmivalenum *)data);
312 break;
313 case VIDIOC_S_FMT:
314 if (!(flags & FWRITE))
315 return (EACCES);
316 if (sc->hw_if->s_fmt)
317 error = (sc->hw_if->s_fmt)(sc->hw_hdl,
318 (struct v4l2_format *)data);
319 break;
320 case VIDIOC_G_FMT:
321 if (sc->hw_if->g_fmt)
322 error = (sc->hw_if->g_fmt)(sc->hw_hdl,
323 (struct v4l2_format *)data);
324 break;
325 case VIDIOC_S_PARM:
326 if (sc->hw_if->s_parm)
327 error = (sc->hw_if->s_parm)(sc->hw_hdl,
328 (struct v4l2_streamparm *)data);
329 break;
330 case VIDIOC_G_PARM:
331 if (sc->hw_if->g_parm)
332 error = (sc->hw_if->g_parm)(sc->hw_hdl,
333 (struct v4l2_streamparm *)data);
334 break;
335 case VIDIOC_ENUMINPUT:
336 if (sc->hw_if->enum_input)
337 error = (sc->hw_if->enum_input)(sc->hw_hdl,
338 (struct v4l2_input *)data);
339 break;
340 case VIDIOC_S_INPUT:
341 if (sc->hw_if->s_input)
342 error = (sc->hw_if->s_input)(sc->hw_hdl,
343 (int)*data);
344 break;
345 case VIDIOC_G_INPUT:
346 if (sc->hw_if->g_input)
347 error = (sc->hw_if->g_input)(sc->hw_hdl,
348 (int *)data);
349 break;
350 case VIDIOC_REQBUFS:
351 if (sc->hw_if->reqbufs)
352 error = (sc->hw_if->reqbufs)(sc->hw_hdl,
353 (struct v4l2_requestbuffers *)data);
354 break;
355 case VIDIOC_QUERYBUF:
356 if (sc->hw_if->querybuf)
357 error = (sc->hw_if->querybuf)(sc->hw_hdl,
358 (struct v4l2_buffer *)data);
359 break;
360 case VIDIOC_QBUF:
361 if (sc->hw_if->qbuf)
362 error = (sc->hw_if->qbuf)(sc->hw_hdl,
363 (struct v4l2_buffer *)data);
364 break;
365 case VIDIOC_DQBUF:
366 if (!sc->hw_if->dqbuf)
367 break;
368 /* should have called mmap() before now */
369 if (sc->sc_vidmode != VIDMODE_MMAP) {
370 error = EINVAL;
371 break;
372 }
373 error = (sc->hw_if->dqbuf)(sc->hw_hdl,
374 (struct v4l2_buffer *)data);
375 if (!atomic_load_int(&video_record_enable))
376 bzero(sc->sc_fbuffer_mmap + vb->m.offset, vb->length);
377 mtx_enter(&sc->sc_mtx);
378 sc->sc_frames_ready--;
379 mtx_leave(&sc->sc_mtx);
380 break;
381 case VIDIOC_STREAMON:
382 if (sc->hw_if->streamon)
383 error = (sc->hw_if->streamon)(sc->hw_hdl,
384 (int)*data);
385 break;
386 case VIDIOC_STREAMOFF:
387 if (sc->hw_if->streamoff)
388 error = (sc->hw_if->streamoff)(sc->hw_hdl,
389 (int)*data);
390 if (!error) {
391 /* Release device ownership and streaming buffers. */
392 error = video_stop(sc);
393 }
394 break;
395 case VIDIOC_TRY_FMT:
396 if (sc->hw_if->try_fmt)
397 error = (sc->hw_if->try_fmt)(sc->hw_hdl,
398 (struct v4l2_format *)data);
399 break;
400 case VIDIOC_QUERYCTRL:
401 if (sc->hw_if->queryctrl)
402 error = (sc->hw_if->queryctrl)(sc->hw_hdl,
403 (struct v4l2_queryctrl *)data);
404 break;
405 default:
406 error = (ENOTTY);
407 }
408
409 return (error);
410 }
411
412 paddr_t
videommap(dev_t dev,off_t off,int prot)413 videommap(dev_t dev, off_t off, int prot)
414 {
415 int unit = VIDEOUNIT(dev);
416 struct video_softc *sc;
417 caddr_t p;
418 paddr_t pa;
419
420 KERNEL_ASSERT_LOCKED();
421
422 DPRINTF(2, "%s: off=%lld, prot=%d\n", __func__, off, prot);
423
424 if (unit >= video_cd.cd_ndevs ||
425 (sc = video_cd.cd_devs[unit]) == NULL)
426 return (-1);
427
428 if (sc->sc_dying)
429 return (-1);
430
431 if (sc->hw_if->mappage == NULL)
432 return (-1);
433
434 p = sc->hw_if->mappage(sc->hw_hdl, off, prot);
435 if (p == NULL)
436 return (-1);
437 if (pmap_extract(pmap_kernel(), (vaddr_t)p, &pa) == FALSE)
438 panic("videommap: invalid page");
439 sc->sc_vidmode = VIDMODE_MMAP;
440
441 /* store frame buffer base address for later blanking */
442 if (off == 0)
443 sc->sc_fbuffer_mmap = p;
444
445 return (pa);
446 }
447
448 void
filt_videodetach(struct knote * kn)449 filt_videodetach(struct knote *kn)
450 {
451 struct video_softc *sc = kn->kn_hook;
452
453 klist_remove(&sc->sc_rklist, kn);
454 }
455
456 int
filt_videoread(struct knote * kn,long hint)457 filt_videoread(struct knote *kn, long hint)
458 {
459 struct video_softc *sc = kn->kn_hook;
460
461 if (sc->sc_frames_ready > 0)
462 return (1);
463
464 return (0);
465 }
466
467 int
filt_videomodify(struct kevent * kev,struct knote * kn)468 filt_videomodify(struct kevent *kev, struct knote *kn)
469 {
470 struct video_softc *sc = kn->kn_hook;
471 int active;
472
473 mtx_enter(&sc->sc_mtx);
474 active = knote_modify(kev, kn);
475 mtx_leave(&sc->sc_mtx);
476
477 return (active);
478 }
479
480 int
filt_videoprocess(struct knote * kn,struct kevent * kev)481 filt_videoprocess(struct knote *kn, struct kevent *kev)
482 {
483 struct video_softc *sc = kn->kn_hook;
484 int active;
485
486 mtx_enter(&sc->sc_mtx);
487 active = knote_process(kn, kev);
488 mtx_leave(&sc->sc_mtx);
489
490 return (active);
491 }
492
493 const struct filterops video_filtops = {
494 .f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
495 .f_attach = NULL,
496 .f_detach = filt_videodetach,
497 .f_event = filt_videoread,
498 .f_modify = filt_videomodify,
499 .f_process = filt_videoprocess,
500 };
501
502 int
videokqfilter(dev_t dev,struct knote * kn)503 videokqfilter(dev_t dev, struct knote *kn)
504 {
505 int unit = VIDEOUNIT(dev);
506 struct video_softc *sc;
507 int error;
508
509 KERNEL_ASSERT_LOCKED();
510
511 if (unit >= video_cd.cd_ndevs ||
512 (sc = video_cd.cd_devs[unit]) == NULL)
513 return (ENXIO);
514
515 if (sc->sc_dying)
516 return (ENXIO);
517
518 switch (kn->kn_filter) {
519 case EVFILT_READ:
520 kn->kn_fop = &video_filtops;
521 kn->kn_hook = sc;
522 break;
523 default:
524 return (EINVAL);
525 }
526
527 if ((error = video_claim(sc, curproc->p_p)))
528 return (error);
529
530 /*
531 * Start the stream in read() mode if not already started. If
532 * the user wanted mmap() mode, he should have called mmap()
533 * before now.
534 */
535 if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) {
536 if (sc->hw_if->start_read(sc->hw_hdl))
537 return (ENXIO);
538 sc->sc_vidmode = VIDMODE_READ;
539 }
540
541 klist_insert(&sc->sc_rklist, kn);
542
543 return (0);
544 }
545
546 int
video_submatch(struct device * parent,void * match,void * aux)547 video_submatch(struct device *parent, void *match, void *aux)
548 {
549 struct cfdata *cf = match;
550
551 return (cf->cf_driver == &video_cd);
552 }
553
554 /*
555 * Called from hardware driver. This is where the MI video driver gets
556 * probed/attached to the hardware driver
557 */
558 struct device *
video_attach_mi(const struct video_hw_if * rhwp,void * hdlp,struct device * dev)559 video_attach_mi(const struct video_hw_if *rhwp, void *hdlp, struct device *dev)
560 {
561 struct video_attach_args arg;
562
563 arg.hwif = rhwp;
564 arg.hdl = hdlp;
565 return (config_found_sm(dev, &arg, videoprint, video_submatch));
566 }
567
568 void
video_intr(void * addr)569 video_intr(void *addr)
570 {
571 struct video_softc *sc = (struct video_softc *)addr;
572
573 DPRINTF(3, "video_intr sc=%p\n", sc);
574 mtx_enter(&sc->sc_mtx);
575 if (sc->sc_vidmode != VIDMODE_NONE)
576 sc->sc_frames_ready++;
577 else
578 printf("%s: interrupt but no streams!\n", __func__);
579 if (sc->sc_vidmode == VIDMODE_READ)
580 wakeup(sc);
581 knote_locked(&sc->sc_rklist, 0);
582 mtx_leave(&sc->sc_mtx);
583 }
584
585 int
video_stop(struct video_softc * sc)586 video_stop(struct video_softc *sc)
587 {
588 int error = 0;
589
590 DPRINTF(1, "%s: stream close\n", __func__);
591
592 if (sc->hw_if->close != NULL)
593 error = sc->hw_if->close(sc->hw_hdl);
594
595 sc->sc_vidmode = VIDMODE_NONE;
596 mtx_enter(&sc->sc_mtx);
597 sc->sc_frames_ready = 0;
598 mtx_leave(&sc->sc_mtx);
599 sc->sc_owner = NULL;
600
601 return (error);
602 }
603
604 int
video_claim(struct video_softc * sc,struct process * pr)605 video_claim(struct video_softc *sc, struct process *pr)
606 {
607 if (sc->sc_owner != NULL && sc->sc_owner != pr) {
608 DPRINTF(1, "%s: already owned=%p\n", __func__, sc->sc_owner);
609 return (EBUSY);
610 }
611
612 if (sc->sc_owner == NULL) {
613 sc->sc_owner = pr;
614 DPRINTF(1, "%s: new owner=%p\n", __func__, sc->sc_owner);
615 }
616
617 return (0);
618 }
619
620 int
videoprint(void * aux,const char * pnp)621 videoprint(void *aux, const char *pnp)
622 {
623 if (pnp != NULL)
624 printf("video at %s", pnp);
625 return (UNCONF);
626 }
627
628 int
videodetach(struct device * self,int flags)629 videodetach(struct device *self, int flags)
630 {
631 struct video_softc *sc = (struct video_softc *)self;
632 int maj, mn;
633
634 /* locate the major number */
635 for (maj = 0; maj < nchrdev; maj++)
636 if (cdevsw[maj].d_open == videoopen)
637 break;
638
639 /* Nuke the vnodes for any open instances (calls close). */
640 mn = self->dv_unit;
641 vdevgone(maj, mn, mn, VCHR);
642
643 klist_invalidate(&sc->sc_rklist);
644 klist_free(&sc->sc_rklist);
645
646 free(sc->sc_fbuffer, M_DEVBUF, sc->sc_fbufferlen);
647
648 return (0);
649 }
650
651 int
videoactivate(struct device * self,int act)652 videoactivate(struct device *self, int act)
653 {
654 struct video_softc *sc = (struct video_softc *)self;
655
656 switch (act) {
657 case DVACT_DEACTIVATE:
658 sc->sc_dying = 1;
659 break;
660 }
661 return (0);
662 }
663