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