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