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