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