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