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