xref: /openbsd-src/sys/dev/video.c (revision 098ff4accc6eb5eaf1cfee7dc19234ea8c0bd383)
1*098ff4acSmvs /*	$OpenBSD: video.c,v 1.59 2024/12/16 21:22:51 mvs Exp $	*/
24bb7a4f6Smpi 
3cf110a4aSrobert /*
4cf110a4aSrobert  * Copyright (c) 2008 Robert Nagy <robert@openbsd.org>
57dbc509fSmglocker  * Copyright (c) 2008 Marcus Glocker <mglocker@openbsd.org>
6cf110a4aSrobert  *
7cf110a4aSrobert  * Permission to use, copy, modify, and distribute this software for any
8cf110a4aSrobert  * purpose with or without fee is hereby granted, provided that the above
9cf110a4aSrobert  * copyright notice and this permission notice appear in all copies.
10cf110a4aSrobert  *
11cf110a4aSrobert  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12cf110a4aSrobert  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13cf110a4aSrobert  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14cf110a4aSrobert  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15cf110a4aSrobert  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16cf110a4aSrobert  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17cf110a4aSrobert  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18cf110a4aSrobert  */
19cf110a4aSrobert 
20cf110a4aSrobert #include <sys/param.h>
21cf110a4aSrobert #include <sys/systm.h>
22cf110a4aSrobert #include <sys/errno.h>
2382fe8fc4Smvs #include <sys/event.h>
24cf110a4aSrobert #include <sys/ioctl.h>
25cf110a4aSrobert #include <sys/fcntl.h>
26cf110a4aSrobert #include <sys/device.h>
27cf110a4aSrobert #include <sys/vnode.h>
28dabf5a5aSmglocker #include <sys/kernel.h>
29dabf5a5aSmglocker #include <sys/malloc.h>
3082fe8fc4Smvs #include <sys/mutex.h>
31cf110a4aSrobert #include <sys/conf.h>
327eef5d8eSjca #include <sys/proc.h>
33cf110a4aSrobert #include <sys/videoio.h>
344bb7a4f6Smpi 
35cf110a4aSrobert #include <dev/video_if.h>
36cf110a4aSrobert 
37fde894e5Stedu #include <uvm/uvm_extern.h>
38fde894e5Stedu 
3982fe8fc4Smvs /*
4082fe8fc4Smvs  * Locks used to protect struct members and global data
41*098ff4acSmvs  *	a	atomic
4282fe8fc4Smvs  *	m	sc_mtx
4382fe8fc4Smvs  */
4482fe8fc4Smvs 
45b121e78aSmglocker #ifdef VIDEO_DEBUG
461e03e03bSmglocker int video_debug = 1;
471e03e03bSmglocker #define DPRINTF(l, x...) do { if ((l) <= video_debug) printf(x); } while (0)
48b121e78aSmglocker #else
491e03e03bSmglocker #define DPRINTF(l, x...)
50b121e78aSmglocker #endif
51cf110a4aSrobert 
5248fb1d35Smglocker struct video_softc {
5348fb1d35Smglocker 	struct device		 dev;
5448fb1d35Smglocker 	void			*hw_hdl;	/* hardware driver handle */
5548fb1d35Smglocker 	struct device		*sc_dev;	/* hardware device struct */
560d6a2fdeSmiod 	const struct video_hw_if *hw_if;	/* hardware interface */
5748fb1d35Smglocker 	char			 sc_dying;	/* device detached */
587eef5d8eSjca 	struct process		*sc_owner;	/* owner process */
59d13871b7Smglocker 	uint8_t			 sc_open;	/* device opened */
6048fb1d35Smglocker 
6182fe8fc4Smvs 	struct mutex		 sc_mtx;
6248fb1d35Smglocker 	int			 sc_fsize;
6348fb1d35Smglocker 	uint8_t			*sc_fbuffer;
643f9e8a25Smglocker 	caddr_t			 sc_fbuffer_mmap;
6548fb1d35Smglocker 	size_t			 sc_fbufferlen;
6648fb1d35Smglocker 	int			 sc_vidmode;	/* access mode */
6748fb1d35Smglocker #define		VIDMODE_NONE	0
6848fb1d35Smglocker #define		VIDMODE_MMAP	1
6948fb1d35Smglocker #define		VIDMODE_READ	2
7082fe8fc4Smvs 	int			 sc_frames_ready;	/* [m] */
7148fb1d35Smglocker 
7282fe8fc4Smvs 	struct klist		 sc_rklist;		/* [m] read selector */
7348fb1d35Smglocker };
7448fb1d35Smglocker 
75cf110a4aSrobert int	videoprobe(struct device *, void *, void *);
76cf110a4aSrobert void	videoattach(struct device *, struct device *, void *);
77cf110a4aSrobert int	videodetach(struct device *, int);
78e78728c7Spirofti int	videoactivate(struct device *, int);
79cf110a4aSrobert int	videoprint(void *, const char *);
80cf110a4aSrobert 
81dabf5a5aSmglocker void	video_intr(void *);
82d13871b7Smglocker int	video_stop(struct video_softc *);
83d13871b7Smglocker int	video_claim(struct video_softc *, struct process *);
84dabf5a5aSmglocker 
85471aeecfSnaddy const struct cfattach video_ca = {
86cf110a4aSrobert 	sizeof(struct video_softc), videoprobe, videoattach,
87cf110a4aSrobert 	videodetach, videoactivate
88cf110a4aSrobert };
89cf110a4aSrobert 
90cf110a4aSrobert struct cfdriver video_cd = {
91cf110a4aSrobert 	NULL, "video", DV_DULL
92cf110a4aSrobert };
93cf110a4aSrobert 
943f9e8a25Smglocker /*
953f9e8a25Smglocker  * Global flag to control if video recording is enabled by kern.video.record.
963f9e8a25Smglocker  */
97*098ff4acSmvs int video_record_enable = 0;	/* [a] */
983f9e8a25Smglocker 
99cf110a4aSrobert int
100cf110a4aSrobert videoprobe(struct device *parent, void *match, void *aux)
101cf110a4aSrobert {
102cf110a4aSrobert 	return (1);
103cf110a4aSrobert }
104cf110a4aSrobert 
105cf110a4aSrobert void
106cf110a4aSrobert videoattach(struct device *parent, struct device *self, void *aux)
107cf110a4aSrobert {
108cf110a4aSrobert 	struct video_softc *sc = (void *)self;
109cf110a4aSrobert 	struct video_attach_args *sa = aux;
110cf110a4aSrobert 
111cf110a4aSrobert 	printf("\n");
112cf110a4aSrobert 	sc->hw_if = sa->hwif;
113cf110a4aSrobert 	sc->hw_hdl = sa->hdl;
114cf110a4aSrobert 	sc->sc_dev = parent;
1151e44166bSderaadt 	sc->sc_fbufferlen = 0;
1167eef5d8eSjca 	sc->sc_owner = NULL;
11782fe8fc4Smvs 	mtx_init(&sc->sc_mtx, IPL_MPFLOOR);
11882fe8fc4Smvs 	klist_init_mutex(&sc->sc_rklist, &sc->sc_mtx);
119fcb11141Smglocker 
1200e1aed5fSrobert 	if (sc->hw_if->get_bufsize)
1211e44166bSderaadt 		sc->sc_fbufferlen = (sc->hw_if->get_bufsize)(sc->hw_hdl);
1221e44166bSderaadt 	if (sc->sc_fbufferlen == 0) {
12370fff4d6Smglocker 		printf("video: could not request frame buffer size\n");
12470fff4d6Smglocker 		return;
12570fff4d6Smglocker 	}
1260e1aed5fSrobert 
1271e44166bSderaadt 	sc->sc_fbuffer = malloc(sc->sc_fbufferlen, M_DEVBUF, M_NOWAIT);
128fcb11141Smglocker 	if (sc->sc_fbuffer == NULL) {
129fcb11141Smglocker 		printf("video: could not allocate frame buffer\n");
130fcb11141Smglocker 		return;
131fcb11141Smglocker 	}
132cf110a4aSrobert }
133cf110a4aSrobert 
134cf110a4aSrobert int
135cf110a4aSrobert videoopen(dev_t dev, int flags, int fmt, struct proc *p)
136cf110a4aSrobert {
13705824dabSmglocker 	int unit = VIDEOUNIT(dev);
138cf110a4aSrobert 	struct video_softc *sc;
139d13871b7Smglocker 	int error = 0;
140d13871b7Smglocker 
141d13871b7Smglocker 	KERNEL_ASSERT_LOCKED();
142cf110a4aSrobert 
143cf110a4aSrobert 	if (unit >= video_cd.cd_ndevs ||
144cf110a4aSrobert 	    (sc = video_cd.cd_devs[unit]) == NULL ||
145cf110a4aSrobert 	     sc->hw_if == NULL)
146cf110a4aSrobert 		return (ENXIO);
147cf110a4aSrobert 
148d13871b7Smglocker 	if (sc->sc_open) {
1491e03e03bSmglocker 		DPRINTF(1, "%s: device already open\n", __func__);
1507eef5d8eSjca 		return (0);
151d13871b7Smglocker 	}
1522f789105Smglocker 
153a256aad2Sjakemsr 	sc->sc_vidmode = VIDMODE_NONE;
154a256aad2Sjakemsr 	sc->sc_frames_ready = 0;
155321912b9Smglocker 
1564770bd48Smglocker 	if (sc->hw_if->open != NULL) {
157d13871b7Smglocker 		error = sc->hw_if->open(sc->hw_hdl, flags, &sc->sc_fsize,
158d13871b7Smglocker 		    sc->sc_fbuffer, video_intr, sc);
1594770bd48Smglocker 	}
1604770bd48Smglocker 	if (error == 0) {
1614770bd48Smglocker 		sc->sc_open = 1;
1624770bd48Smglocker 		DPRINTF(1, "%s: set device to open\n", __func__);
1634770bd48Smglocker 	}
164d13871b7Smglocker 
165d13871b7Smglocker 	return (error);
166cf110a4aSrobert }
167cf110a4aSrobert 
168cf110a4aSrobert int
169cf110a4aSrobert videoclose(dev_t dev, int flags, int fmt, struct proc *p)
170cf110a4aSrobert {
171cf110a4aSrobert 	struct video_softc *sc;
172d13871b7Smglocker 	int error = 0;
173d13871b7Smglocker 
174d13871b7Smglocker 	KERNEL_ASSERT_LOCKED();
175d13871b7Smglocker 
1761e03e03bSmglocker 	DPRINTF(1, "%s: last close\n", __func__);
177cf110a4aSrobert 
178cf110a4aSrobert 	sc = video_cd.cd_devs[VIDEOUNIT(dev)];
179cf110a4aSrobert 
180d13871b7Smglocker 	error = video_stop(sc);
181d13871b7Smglocker 	sc->sc_open = 0;
1822f789105Smglocker 
183d13871b7Smglocker 	return (error);
184cf110a4aSrobert }
185cf110a4aSrobert 
186cf110a4aSrobert int
187dabf5a5aSmglocker videoread(dev_t dev, struct uio *uio, int ioflag)
188dabf5a5aSmglocker {
18905824dabSmglocker 	int unit = VIDEOUNIT(dev);
190dabf5a5aSmglocker 	struct video_softc *sc;
19105824dabSmglocker 	int error;
192aba17a88Sstefan 	size_t size;
193dabf5a5aSmglocker 
194d13871b7Smglocker 	KERNEL_ASSERT_LOCKED();
195d13871b7Smglocker 
196dabf5a5aSmglocker 	if (unit >= video_cd.cd_ndevs ||
197dabf5a5aSmglocker 	    (sc = video_cd.cd_devs[unit]) == NULL)
198dabf5a5aSmglocker 		return (ENXIO);
199dabf5a5aSmglocker 
200dabf5a5aSmglocker 	if (sc->sc_dying)
201dabf5a5aSmglocker 		return (EIO);
202dabf5a5aSmglocker 
203a256aad2Sjakemsr 	if (sc->sc_vidmode == VIDMODE_MMAP)
204a256aad2Sjakemsr 		return (EBUSY);
205a256aad2Sjakemsr 
206d13871b7Smglocker 	if ((error = video_claim(sc, curproc->p_p)))
207d13871b7Smglocker 		return (error);
208d13871b7Smglocker 
209a256aad2Sjakemsr 	/* start the stream if not already started */
210a256aad2Sjakemsr 	if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) {
2116e7dc0a3Smglocker  		error = sc->hw_if->start_read(sc->hw_hdl);
2126e7dc0a3Smglocker  		if (error)
2136e7dc0a3Smglocker  			return (error);
214a256aad2Sjakemsr 		sc->sc_vidmode = VIDMODE_READ;
215321912b9Smglocker  	}
216321912b9Smglocker 
2171e03e03bSmglocker 	DPRINTF(1, "resid=%zu\n", uio->uio_resid);
218dabf5a5aSmglocker 
21982fe8fc4Smvs 	mtx_enter(&sc->sc_mtx);
22082fe8fc4Smvs 
221a256aad2Sjakemsr 	if (sc->sc_frames_ready < 1) {
222dabf5a5aSmglocker 		/* block userland read until a frame is ready */
22382fe8fc4Smvs 		error = msleep_nsec(sc, &sc->sc_mtx, PWAIT | PCATCH,
22482fe8fc4Smvs 		    "vid_rd", INFSLP);
225a256aad2Sjakemsr 		if (sc->sc_dying)
226a256aad2Sjakemsr 			error = EIO;
22782fe8fc4Smvs 		if (error) {
22882fe8fc4Smvs 			mtx_leave(&sc->sc_mtx);
229dabf5a5aSmglocker 			return (error);
230a256aad2Sjakemsr 		}
23182fe8fc4Smvs 	}
23282fe8fc4Smvs 	sc->sc_frames_ready--;
23382fe8fc4Smvs 
23482fe8fc4Smvs 	mtx_leave(&sc->sc_mtx);
235dabf5a5aSmglocker 
236a256aad2Sjakemsr 	/* move no more than 1 frame to userland, as per specification */
237aba17a88Sstefan 	size = ulmin(uio->uio_resid, sc->sc_fsize);
238*098ff4acSmvs 	if (!atomic_load_int(&video_record_enable))
2393f9e8a25Smglocker 		bzero(sc->sc_fbuffer, size);
240aba17a88Sstefan 	error = uiomove(sc->sc_fbuffer, size, uio);
241dabf5a5aSmglocker 	if (error)
242dabf5a5aSmglocker 		return (error);
243dabf5a5aSmglocker 
2441e03e03bSmglocker 	DPRINTF(1, "uiomove successfully done (%zu bytes)\n", size);
245dabf5a5aSmglocker 
246dabf5a5aSmglocker 	return (0);
247dabf5a5aSmglocker }
248dabf5a5aSmglocker 
249dabf5a5aSmglocker int
250cf110a4aSrobert videoioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
251cf110a4aSrobert {
25205824dabSmglocker 	int unit = VIDEOUNIT(dev);
253cf110a4aSrobert 	struct video_softc *sc;
2543f9e8a25Smglocker 	struct v4l2_buffer *vb = (struct v4l2_buffer *)data;
25505824dabSmglocker 	int error;
256cf110a4aSrobert 
257d13871b7Smglocker 	KERNEL_ASSERT_LOCKED();
258d13871b7Smglocker 
259cf110a4aSrobert 	if (unit >= video_cd.cd_ndevs ||
260cf110a4aSrobert 	    (sc = video_cd.cd_devs[unit]) == NULL || sc->hw_if == NULL)
261cf110a4aSrobert 		return (ENXIO);
262cf110a4aSrobert 
2631e03e03bSmglocker 	DPRINTF(3, "video_ioctl(%zu, '%c', %zu)\n",
2641e03e03bSmglocker 	    IOCPARM_LEN(cmd), (int) IOCGROUP(cmd), cmd & 0xff);
265cf110a4aSrobert 
266cf110a4aSrobert 	error = EOPNOTSUPP;
267cf110a4aSrobert 	switch (cmd) {
268d13871b7Smglocker 	case VIDIOC_G_CTRL:
269d13871b7Smglocker 		if (sc->hw_if->g_ctrl)
270d13871b7Smglocker 			error = (sc->hw_if->g_ctrl)(sc->hw_hdl,
271d13871b7Smglocker 			    (struct v4l2_control *)data);
272d13871b7Smglocker 		break;
273d13871b7Smglocker 	case VIDIOC_S_CTRL:
274d13871b7Smglocker 		if (sc->hw_if->s_ctrl)
275d13871b7Smglocker 			error = (sc->hw_if->s_ctrl)(sc->hw_hdl,
276d13871b7Smglocker 			    (struct v4l2_control *)data);
277d13871b7Smglocker 		break;
278d13871b7Smglocker 	default:
279d13871b7Smglocker 		error = (ENOTTY);
280d13871b7Smglocker 	}
281d13871b7Smglocker 	if (error != ENOTTY)
282d13871b7Smglocker 		return (error);
283d13871b7Smglocker 
284d13871b7Smglocker 	if ((error = video_claim(sc, p->p_p)))
285d13871b7Smglocker 		return (error);
286d13871b7Smglocker 
287d13871b7Smglocker 	/*
288d13871b7Smglocker 	 * The following IOCTLs can only be called by the device owner.
289d13871b7Smglocker 	 * For further shared IOCTLs please move it up.
290d13871b7Smglocker 	 */
291d13871b7Smglocker 	error = EOPNOTSUPP;
292d13871b7Smglocker 	switch (cmd) {
293cf110a4aSrobert 	case VIDIOC_QUERYCAP:
294cf110a4aSrobert 		if (sc->hw_if->querycap)
295cf110a4aSrobert 			error = (sc->hw_if->querycap)(sc->hw_hdl,
296cf110a4aSrobert 			    (struct v4l2_capability *)data);
297cf110a4aSrobert 		break;
2980aa3ede6Smglocker 	case VIDIOC_ENUM_FMT:
2990aa3ede6Smglocker 		if (sc->hw_if->enum_fmt)
3000aa3ede6Smglocker 			error = (sc->hw_if->enum_fmt)(sc->hw_hdl,
3010aa3ede6Smglocker 			    (struct v4l2_fmtdesc *)data);
3020aa3ede6Smglocker 		break;
30312262cecSmglocker 	case VIDIOC_ENUM_FRAMESIZES:
30412262cecSmglocker 		if (sc->hw_if->enum_fsizes)
30512262cecSmglocker 			error = (sc->hw_if->enum_fsizes)(sc->hw_hdl,
30612262cecSmglocker 			    (struct v4l2_frmsizeenum *)data);
30712262cecSmglocker 		break;
30812262cecSmglocker 	case VIDIOC_ENUM_FRAMEINTERVALS:
30912262cecSmglocker 		if (sc->hw_if->enum_fivals)
31012262cecSmglocker 			error = (sc->hw_if->enum_fivals)(sc->hw_hdl,
31112262cecSmglocker 			    (struct v4l2_frmivalenum *)data);
31212262cecSmglocker 		break;
313cf110a4aSrobert 	case VIDIOC_S_FMT:
314cf110a4aSrobert 		if (!(flags & FWRITE))
315cf110a4aSrobert 			return (EACCES);
316cf110a4aSrobert 		if (sc->hw_if->s_fmt)
317cf110a4aSrobert 			error = (sc->hw_if->s_fmt)(sc->hw_hdl,
318cf110a4aSrobert 			    (struct v4l2_format *)data);
319cf110a4aSrobert 		break;
320cf110a4aSrobert 	case VIDIOC_G_FMT:
321cf110a4aSrobert 		if (sc->hw_if->g_fmt)
322cf110a4aSrobert 			error = (sc->hw_if->g_fmt)(sc->hw_hdl,
323cf110a4aSrobert 			    (struct v4l2_format *)data);
324cf110a4aSrobert 		break;
325c9ba893eSjakemsr 	case VIDIOC_S_PARM:
326c9ba893eSjakemsr 		if (sc->hw_if->s_parm)
327c9ba893eSjakemsr 			error = (sc->hw_if->s_parm)(sc->hw_hdl,
328c9ba893eSjakemsr 			    (struct v4l2_streamparm *)data);
329c9ba893eSjakemsr 		break;
330c9ba893eSjakemsr 	case VIDIOC_G_PARM:
331c9ba893eSjakemsr 		if (sc->hw_if->g_parm)
332c9ba893eSjakemsr 			error = (sc->hw_if->g_parm)(sc->hw_hdl,
333c9ba893eSjakemsr 			    (struct v4l2_streamparm *)data);
334c9ba893eSjakemsr 		break;
3350aa3ede6Smglocker 	case VIDIOC_ENUMINPUT:
3360aa3ede6Smglocker 		if (sc->hw_if->enum_input)
3370aa3ede6Smglocker 			error = (sc->hw_if->enum_input)(sc->hw_hdl,
3380aa3ede6Smglocker 			    (struct v4l2_input *)data);
3390aa3ede6Smglocker 		break;
3400aa3ede6Smglocker 	case VIDIOC_S_INPUT:
3410aa3ede6Smglocker 		if (sc->hw_if->s_input)
3420aa3ede6Smglocker 			error = (sc->hw_if->s_input)(sc->hw_hdl,
3430aa3ede6Smglocker 			    (int)*data);
3440aa3ede6Smglocker 		break;
3459c924229Sarmani 	case VIDIOC_G_INPUT:
3469c924229Sarmani 		if (sc->hw_if->g_input)
3479c924229Sarmani 			error = (sc->hw_if->g_input)(sc->hw_hdl,
3489c924229Sarmani 			    (int *)data);
3499c924229Sarmani 		break;
350f7821c14Smglocker 	case VIDIOC_REQBUFS:
351f7821c14Smglocker 		if (sc->hw_if->reqbufs)
352f7821c14Smglocker 			error = (sc->hw_if->reqbufs)(sc->hw_hdl,
353f7821c14Smglocker 			    (struct v4l2_requestbuffers *)data);
3540aa3ede6Smglocker 		break;
355f7821c14Smglocker 	case VIDIOC_QUERYBUF:
356f7821c14Smglocker 		if (sc->hw_if->querybuf)
357f7821c14Smglocker 			error = (sc->hw_if->querybuf)(sc->hw_hdl,
358f7821c14Smglocker 			    (struct v4l2_buffer *)data);
359f7821c14Smglocker 		break;
3601dc1fa5aSmglocker 	case VIDIOC_QBUF:
3611dc1fa5aSmglocker 		if (sc->hw_if->qbuf)
3621dc1fa5aSmglocker 			error = (sc->hw_if->qbuf)(sc->hw_hdl,
3631dc1fa5aSmglocker 			    (struct v4l2_buffer *)data);
3641dc1fa5aSmglocker 		break;
365f7821c14Smglocker 	case VIDIOC_DQBUF:
366a256aad2Sjakemsr 		if (!sc->hw_if->dqbuf)
367a256aad2Sjakemsr 			break;
368a256aad2Sjakemsr 		/* should have called mmap() before now */
369a256aad2Sjakemsr 		if (sc->sc_vidmode != VIDMODE_MMAP) {
370a256aad2Sjakemsr 			error = EINVAL;
371a256aad2Sjakemsr 			break;
372a256aad2Sjakemsr 		}
3731dc1fa5aSmglocker 		error = (sc->hw_if->dqbuf)(sc->hw_hdl,
3741dc1fa5aSmglocker 		    (struct v4l2_buffer *)data);
375*098ff4acSmvs 		if (!atomic_load_int(&video_record_enable))
3763f9e8a25Smglocker 			bzero(sc->sc_fbuffer_mmap + vb->m.offset, vb->length);
37782fe8fc4Smvs 		mtx_enter(&sc->sc_mtx);
378a256aad2Sjakemsr 		sc->sc_frames_ready--;
37982fe8fc4Smvs 		mtx_leave(&sc->sc_mtx);
3801dc1fa5aSmglocker 		break;
3811dc1fa5aSmglocker 	case VIDIOC_STREAMON:
3821dc1fa5aSmglocker 		if (sc->hw_if->streamon)
3831dc1fa5aSmglocker 			error = (sc->hw_if->streamon)(sc->hw_hdl,
3841dc1fa5aSmglocker 			    (int)*data);
3850aa3ede6Smglocker 		break;
386952b8b3eSrobert 	case VIDIOC_STREAMOFF:
387952b8b3eSrobert 		if (sc->hw_if->streamoff)
388952b8b3eSrobert 			error = (sc->hw_if->streamoff)(sc->hw_hdl,
389952b8b3eSrobert 			    (int)*data);
390d13871b7Smglocker 		if (!error) {
391d13871b7Smglocker 			/* Release device ownership and streaming buffers. */
3929d17d797Smglocker 			error = video_stop(sc);
393d13871b7Smglocker 		}
394952b8b3eSrobert 		break;
3950aa3ede6Smglocker 	case VIDIOC_TRY_FMT:
3960aa3ede6Smglocker 		if (sc->hw_if->try_fmt)
3970aa3ede6Smglocker 			error = (sc->hw_if->try_fmt)(sc->hw_hdl,
3980aa3ede6Smglocker 			    (struct v4l2_format *)data);
3990aa3ede6Smglocker 		break;
4006a4f627aSrobert 	case VIDIOC_QUERYCTRL:
4016a4f627aSrobert 		if (sc->hw_if->queryctrl)
4026a4f627aSrobert 			error = (sc->hw_if->queryctrl)(sc->hw_hdl,
4036a4f627aSrobert 			    (struct v4l2_queryctrl *)data);
4046a4f627aSrobert 		break;
405cf110a4aSrobert 	default:
406cf110a4aSrobert 		error = (ENOTTY);
407cf110a4aSrobert 	}
408cf110a4aSrobert 
409cf110a4aSrobert 	return (error);
410cf110a4aSrobert }
411cf110a4aSrobert 
412dabf5a5aSmglocker paddr_t
413dabf5a5aSmglocker videommap(dev_t dev, off_t off, int prot)
414dabf5a5aSmglocker {
41505824dabSmglocker 	int unit = VIDEOUNIT(dev);
416dabf5a5aSmglocker 	struct video_softc *sc;
417f7821c14Smglocker 	caddr_t p;
418f7821c14Smglocker 	paddr_t pa;
419f7821c14Smglocker 
420d13871b7Smglocker 	KERNEL_ASSERT_LOCKED();
421d13871b7Smglocker 
4221e03e03bSmglocker 	DPRINTF(2, "%s: off=%lld, prot=%d\n", __func__, off, prot);
423dabf5a5aSmglocker 
424dabf5a5aSmglocker 	if (unit >= video_cd.cd_ndevs ||
425dabf5a5aSmglocker 	    (sc = video_cd.cd_devs[unit]) == NULL)
426f7821c14Smglocker 		return (-1);
427dabf5a5aSmglocker 
428dabf5a5aSmglocker 	if (sc->sc_dying)
429f7821c14Smglocker 		return (-1);
430dabf5a5aSmglocker 
431f7821c14Smglocker 	if (sc->hw_if->mappage == NULL)
432f7821c14Smglocker 		return (-1);
433dabf5a5aSmglocker 
434f7821c14Smglocker 	p = sc->hw_if->mappage(sc->hw_hdl, off, prot);
435f7821c14Smglocker 	if (p == NULL)
436f7821c14Smglocker 		return (-1);
437f7821c14Smglocker 	if (pmap_extract(pmap_kernel(), (vaddr_t)p, &pa) == FALSE)
438f7821c14Smglocker 		panic("videommap: invalid page");
439a256aad2Sjakemsr 	sc->sc_vidmode = VIDMODE_MMAP;
440f7821c14Smglocker 
4413f9e8a25Smglocker 	/* store frame buffer base address for later blanking */
4423f9e8a25Smglocker 	if (off == 0)
4433f9e8a25Smglocker 		sc->sc_fbuffer_mmap = p;
4443f9e8a25Smglocker 
445f7821c14Smglocker 	return (pa);
446dabf5a5aSmglocker }
447dabf5a5aSmglocker 
448092fd1acSmpi void
449092fd1acSmpi filt_videodetach(struct knote *kn)
450092fd1acSmpi {
451092fd1acSmpi 	struct video_softc *sc = kn->kn_hook;
452092fd1acSmpi 
45382fe8fc4Smvs 	klist_remove(&sc->sc_rklist, kn);
454092fd1acSmpi }
455092fd1acSmpi 
456092fd1acSmpi int
457092fd1acSmpi filt_videoread(struct knote *kn, long hint)
458092fd1acSmpi {
459092fd1acSmpi 	struct video_softc *sc = kn->kn_hook;
460092fd1acSmpi 
461092fd1acSmpi 	if (sc->sc_frames_ready > 0)
462092fd1acSmpi 		return (1);
463092fd1acSmpi 
464092fd1acSmpi 	return (0);
465092fd1acSmpi }
466092fd1acSmpi 
46782fe8fc4Smvs int
46882fe8fc4Smvs filt_videomodify(struct kevent *kev, struct knote *kn)
46982fe8fc4Smvs {
47082fe8fc4Smvs 	struct video_softc *sc = kn->kn_hook;
47182fe8fc4Smvs 	int active;
47282fe8fc4Smvs 
47382fe8fc4Smvs 	mtx_enter(&sc->sc_mtx);
47482fe8fc4Smvs 	active = knote_modify(kev, kn);
47582fe8fc4Smvs 	mtx_leave(&sc->sc_mtx);
47682fe8fc4Smvs 
47782fe8fc4Smvs 	return (active);
47882fe8fc4Smvs }
47982fe8fc4Smvs 
48082fe8fc4Smvs int
48182fe8fc4Smvs filt_videoprocess(struct knote *kn, struct kevent *kev)
48282fe8fc4Smvs {
48382fe8fc4Smvs 	struct video_softc *sc = kn->kn_hook;
48482fe8fc4Smvs 	int active;
48582fe8fc4Smvs 
48682fe8fc4Smvs 	mtx_enter(&sc->sc_mtx);
48782fe8fc4Smvs 	active = knote_process(kn, kev);
48882fe8fc4Smvs 	mtx_leave(&sc->sc_mtx);
48982fe8fc4Smvs 
49082fe8fc4Smvs 	return (active);
49182fe8fc4Smvs }
49282fe8fc4Smvs 
493092fd1acSmpi const struct filterops video_filtops = {
49482fe8fc4Smvs 	.f_flags	= FILTEROP_ISFD | FILTEROP_MPSAFE,
495092fd1acSmpi 	.f_attach	= NULL,
496092fd1acSmpi 	.f_detach	= filt_videodetach,
497092fd1acSmpi 	.f_event	= filt_videoread,
49882fe8fc4Smvs 	.f_modify	= filt_videomodify,
49982fe8fc4Smvs 	.f_process	= filt_videoprocess,
500092fd1acSmpi };
501092fd1acSmpi 
502092fd1acSmpi int
503092fd1acSmpi videokqfilter(dev_t dev, struct knote *kn)
504092fd1acSmpi {
505092fd1acSmpi 	int unit = VIDEOUNIT(dev);
506092fd1acSmpi 	struct video_softc *sc;
50782fe8fc4Smvs 	int error;
508d13871b7Smglocker 
509d13871b7Smglocker 	KERNEL_ASSERT_LOCKED();
510092fd1acSmpi 
511092fd1acSmpi 	if (unit >= video_cd.cd_ndevs ||
512092fd1acSmpi 	    (sc = video_cd.cd_devs[unit]) == NULL)
513092fd1acSmpi 		return (ENXIO);
514092fd1acSmpi 
515092fd1acSmpi 	if (sc->sc_dying)
516092fd1acSmpi 		return (ENXIO);
517092fd1acSmpi 
518092fd1acSmpi 	switch (kn->kn_filter) {
519092fd1acSmpi 	case EVFILT_READ:
520092fd1acSmpi 		kn->kn_fop = &video_filtops;
521092fd1acSmpi 		kn->kn_hook = sc;
522092fd1acSmpi 		break;
523092fd1acSmpi 	default:
524092fd1acSmpi 		return (EINVAL);
525092fd1acSmpi 	}
526092fd1acSmpi 
527d13871b7Smglocker 	if ((error = video_claim(sc, curproc->p_p)))
528d13871b7Smglocker 		return (error);
529d13871b7Smglocker 
530092fd1acSmpi 	/*
531092fd1acSmpi 	 * Start the stream in read() mode if not already started.  If
532092fd1acSmpi 	 * the user wanted mmap() mode, he should have called mmap()
533092fd1acSmpi 	 * before now.
534092fd1acSmpi 	 */
535092fd1acSmpi 	if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) {
536092fd1acSmpi 		if (sc->hw_if->start_read(sc->hw_hdl))
537092fd1acSmpi 			return (ENXIO);
538092fd1acSmpi 		sc->sc_vidmode = VIDMODE_READ;
539092fd1acSmpi 	}
540092fd1acSmpi 
54182fe8fc4Smvs 	klist_insert(&sc->sc_rklist, kn);
542092fd1acSmpi 
543092fd1acSmpi 	return (0);
544092fd1acSmpi }
545092fd1acSmpi 
546181d57e0Smglocker int
547181d57e0Smglocker video_submatch(struct device *parent, void *match, void *aux)
548181d57e0Smglocker {
549181d57e0Smglocker         struct cfdata *cf = match;
550181d57e0Smglocker 
551181d57e0Smglocker 	return (cf->cf_driver == &video_cd);
552181d57e0Smglocker }
553181d57e0Smglocker 
554cf110a4aSrobert /*
555cf110a4aSrobert  * Called from hardware driver. This is where the MI video driver gets
556cf110a4aSrobert  * probed/attached to the hardware driver
557cf110a4aSrobert  */
558cf110a4aSrobert struct device *
5590d6a2fdeSmiod video_attach_mi(const struct video_hw_if *rhwp, void *hdlp, struct device *dev)
560cf110a4aSrobert {
561cf110a4aSrobert 	struct video_attach_args arg;
562cf110a4aSrobert 
563cf110a4aSrobert 	arg.hwif = rhwp;
564cf110a4aSrobert 	arg.hdl = hdlp;
565181d57e0Smglocker 	return (config_found_sm(dev, &arg, videoprint, video_submatch));
566cf110a4aSrobert }
567cf110a4aSrobert 
568dabf5a5aSmglocker void
569dabf5a5aSmglocker video_intr(void *addr)
570dabf5a5aSmglocker {
571dabf5a5aSmglocker 	struct video_softc *sc = (struct video_softc *)addr;
572dabf5a5aSmglocker 
5731e03e03bSmglocker 	DPRINTF(3, "video_intr sc=%p\n", sc);
57482fe8fc4Smvs 	mtx_enter(&sc->sc_mtx);
575a256aad2Sjakemsr 	if (sc->sc_vidmode != VIDMODE_NONE)
576a256aad2Sjakemsr 		sc->sc_frames_ready++;
577a256aad2Sjakemsr 	else
578a256aad2Sjakemsr 		printf("%s: interrupt but no streams!\n", __func__);
579a256aad2Sjakemsr 	if (sc->sc_vidmode == VIDMODE_READ)
580dabf5a5aSmglocker 		wakeup(sc);
58182fe8fc4Smvs 	knote_locked(&sc->sc_rklist, 0);
58282fe8fc4Smvs 	mtx_leave(&sc->sc_mtx);
583dabf5a5aSmglocker }
584dabf5a5aSmglocker 
585cf110a4aSrobert int
586d13871b7Smglocker video_stop(struct video_softc *sc)
587d13871b7Smglocker {
588d13871b7Smglocker 	int error = 0;
589d13871b7Smglocker 
5901e03e03bSmglocker 	DPRINTF(1, "%s: stream close\n", __func__);
591d13871b7Smglocker 
592d13871b7Smglocker 	if (sc->hw_if->close != NULL)
593d13871b7Smglocker 		error = sc->hw_if->close(sc->hw_hdl);
594d13871b7Smglocker 
595d13871b7Smglocker 	sc->sc_vidmode = VIDMODE_NONE;
59682fe8fc4Smvs 	mtx_enter(&sc->sc_mtx);
597d13871b7Smglocker 	sc->sc_frames_ready = 0;
59882fe8fc4Smvs 	mtx_leave(&sc->sc_mtx);
599d13871b7Smglocker 	sc->sc_owner = NULL;
600d13871b7Smglocker 
601d13871b7Smglocker 	return (error);
602d13871b7Smglocker }
603d13871b7Smglocker 
604d13871b7Smglocker int
605759f3dcfSmglocker video_claim(struct video_softc *sc, struct process *pr)
606d13871b7Smglocker {
607759f3dcfSmglocker 	if (sc->sc_owner != NULL && sc->sc_owner != pr) {
6081e03e03bSmglocker 		DPRINTF(1, "%s: already owned=%p\n", __func__, sc->sc_owner);
609d13871b7Smglocker 		return (EBUSY);
610d13871b7Smglocker 	}
611d13871b7Smglocker 
612d13871b7Smglocker 	if (sc->sc_owner == NULL) {
613759f3dcfSmglocker 		sc->sc_owner = pr;
6141e03e03bSmglocker 		DPRINTF(1, "%s: new owner=%p\n", __func__, sc->sc_owner);
615d13871b7Smglocker 	}
616d13871b7Smglocker 
617d13871b7Smglocker 	return (0);
618d13871b7Smglocker }
619d13871b7Smglocker 
620d13871b7Smglocker int
621cf110a4aSrobert videoprint(void *aux, const char *pnp)
622cf110a4aSrobert {
623cf110a4aSrobert 	if (pnp != NULL)
624cf110a4aSrobert 		printf("video at %s", pnp);
625cf110a4aSrobert 	return (UNCONF);
626cf110a4aSrobert }
627cf110a4aSrobert 
628cf110a4aSrobert int
629cf110a4aSrobert videodetach(struct device *self, int flags)
630cf110a4aSrobert {
631fcb11141Smglocker 	struct video_softc *sc = (struct video_softc *)self;
63282fe8fc4Smvs 	int maj, mn;
633cf110a4aSrobert 
634cf110a4aSrobert 	/* locate the major number */
635cf110a4aSrobert 	for (maj = 0; maj < nchrdev; maj++)
636cf110a4aSrobert 		if (cdevsw[maj].d_open == videoopen)
637cf110a4aSrobert 			break;
638cf110a4aSrobert 
639cf110a4aSrobert 	/* Nuke the vnodes for any open instances (calls close). */
640cf110a4aSrobert 	mn = self->dv_unit;
641cf110a4aSrobert 	vdevgone(maj, mn, mn, VCHR);
642cf110a4aSrobert 
64382fe8fc4Smvs 	klist_invalidate(&sc->sc_rklist);
64482fe8fc4Smvs 	klist_free(&sc->sc_rklist);
645092fd1acSmpi 
6464a480bf8Smpi 	free(sc->sc_fbuffer, M_DEVBUF, sc->sc_fbufferlen);
6474a480bf8Smpi 
648cf110a4aSrobert 	return (0);
649cf110a4aSrobert }
650cf110a4aSrobert 
651cf110a4aSrobert int
652e78728c7Spirofti videoactivate(struct device *self, int act)
653cf110a4aSrobert {
654cf110a4aSrobert 	struct video_softc *sc = (struct video_softc *)self;
655cf110a4aSrobert 
656cf110a4aSrobert 	switch (act) {
657cf110a4aSrobert 	case DVACT_DEACTIVATE:
658cf110a4aSrobert 		sc->sc_dying = 1;
659cf110a4aSrobert 		break;
660cf110a4aSrobert 	}
661cf110a4aSrobert 	return (0);
662cf110a4aSrobert }
663