FreeBSD/Linux Kernel Cross Reference
sys/dev/video.c
1 /* $OpenBSD: video.c,v 1.57 2022/07/02 08:50:41 visa 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/device.h>
26 #include <sys/vnode.h>
27 #include <sys/kernel.h>
28 #include <sys/malloc.h>
29 #include <sys/conf.h>
30 #include <sys/proc.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 int video_debug = 1;
39 #define DPRINTF(l, x...) do { if ((l) <= video_debug) printf(x); } while (0)
40 #else
41 #define DPRINTF(l, x...)
42 #endif
43
44 struct video_softc {
45 struct device dev;
46 void *hw_hdl; /* hardware driver handle */
47 struct device *sc_dev; /* hardware device struct */
48 const struct video_hw_if *hw_if; /* hardware interface */
49 char sc_dying; /* device detached */
50 struct process *sc_owner; /* owner process */
51 uint8_t sc_open; /* device opened */
52
53 int sc_fsize;
54 uint8_t *sc_fbuffer;
55 caddr_t sc_fbuffer_mmap;
56 size_t sc_fbufferlen;
57 int sc_vidmode; /* access mode */
58 #define VIDMODE_NONE 0
59 #define VIDMODE_MMAP 1
60 #define VIDMODE_READ 2
61 int sc_frames_ready;
62
63 struct selinfo sc_rsel; /* read selector */
64 };
65
66 int videoprobe(struct device *, void *, void *);
67 void videoattach(struct device *, struct device *, void *);
68 int videodetach(struct device *, int);
69 int videoactivate(struct device *, int);
70 int videoprint(void *, const char *);
71
72 void video_intr(void *);
73 int video_stop(struct video_softc *);
74 int video_claim(struct video_softc *, struct process *);
75
76 const struct cfattach video_ca = {
77 sizeof(struct video_softc), videoprobe, videoattach,
78 videodetach, videoactivate
79 };
80
81 struct cfdriver video_cd = {
82 NULL, "video", DV_DULL
83 };
84
85 /*
86 * Global flag to control if video recording is enabled by kern.video.record.
87 */
88 int video_record_enable = 0;
89
90 int
91 videoprobe(struct device *parent, void *match, void *aux)
92 {
93 return (1);
94 }
95
96 void
97 videoattach(struct device *parent, struct device *self, void *aux)
98 {
99 struct video_softc *sc = (void *)self;
100 struct video_attach_args *sa = aux;
101
102 printf("\n");
103 sc->hw_if = sa->hwif;
104 sc->hw_hdl = sa->hdl;
105 sc->sc_dev = parent;
106 sc->sc_fbufferlen = 0;
107 sc->sc_owner = NULL;
108
109 if (sc->hw_if->get_bufsize)
110 sc->sc_fbufferlen = (sc->hw_if->get_bufsize)(sc->hw_hdl);
111 if (sc->sc_fbufferlen == 0) {
112 printf("video: could not request frame buffer size\n");
113 return;
114 }
115
116 sc->sc_fbuffer = malloc(sc->sc_fbufferlen, M_DEVBUF, M_NOWAIT);
117 if (sc->sc_fbuffer == NULL) {
118 printf("video: could not allocate frame buffer\n");
119 return;
120 }
121 }
122
123 int
124 videoopen(dev_t dev, int flags, int fmt, struct proc *p)
125 {
126 int unit = VIDEOUNIT(dev);
127 struct video_softc *sc;
128 int error = 0;
129
130 KERNEL_ASSERT_LOCKED();
131
132 if (unit >= video_cd.cd_ndevs ||
133 (sc = video_cd.cd_devs[unit]) == NULL ||
134 sc->hw_if == NULL)
135 return (ENXIO);
136
137 if (sc->sc_open) {
138 DPRINTF(1, "%s: device already open\n", __func__);
139 return (0);
140 }
141
142 sc->sc_vidmode = VIDMODE_NONE;
143 sc->sc_frames_ready = 0;
144
145 if (sc->hw_if->open != NULL) {
146 error = sc->hw_if->open(sc->hw_hdl, flags, &sc->sc_fsize,
147 sc->sc_fbuffer, video_intr, sc);
148 }
149 if (error == 0) {
150 sc->sc_open = 1;
151 DPRINTF(1, "%s: set device to open\n", __func__);
152 }
153
154 return (error);
155 }
156
157 int
158 videoclose(dev_t dev, int flags, int fmt, struct proc *p)
159 {
160 struct video_softc *sc;
161 int error = 0;
162
163 KERNEL_ASSERT_LOCKED();
164
165 DPRINTF(1, "%s: last close\n", __func__);
166
167 sc = video_cd.cd_devs[VIDEOUNIT(dev)];
168
169 error = video_stop(sc);
170 sc->sc_open = 0;
171
172 return (error);
173 }
174
175 int
176 videoread(dev_t dev, struct uio *uio, int ioflag)
177 {
178 int unit = VIDEOUNIT(dev);
179 struct video_softc *sc;
180 int error;
181 size_t size;
182
183 KERNEL_ASSERT_LOCKED();
184
185 if (unit >= video_cd.cd_ndevs ||
186 (sc = video_cd.cd_devs[unit]) == NULL)
187 return (ENXIO);
188
189 if (sc->sc_dying)
190 return (EIO);
191
192 if (sc->sc_vidmode == VIDMODE_MMAP)
193 return (EBUSY);
194
195 if ((error = video_claim(sc, curproc->p_p)))
196 return (error);
197
198 /* start the stream if not already started */
199 if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) {
200 error = sc->hw_if->start_read(sc->hw_hdl);
201 if (error)
202 return (error);
203 sc->sc_vidmode = VIDMODE_READ;
204 }
205
206 DPRINTF(1, "resid=%zu\n", uio->uio_resid);
207
208 if (sc->sc_frames_ready < 1) {
209 /* block userland read until a frame is ready */
210 error = tsleep_nsec(sc, PWAIT | PCATCH, "vid_rd", INFSLP);
211 if (sc->sc_dying)
212 error = EIO;
213 if (error)
214 return (error);
215 }
216
217 /* move no more than 1 frame to userland, as per specification */
218 size = ulmin(uio->uio_resid, sc->sc_fsize);
219 if (!video_record_enable)
220 bzero(sc->sc_fbuffer, size);
221 error = uiomove(sc->sc_fbuffer, size, uio);
222 sc->sc_frames_ready--;
223 if (error)
224 return (error);
225
226 DPRINTF(1, "uiomove successfully done (%zu bytes)\n", size);
227
228 return (0);
229 }
230
231 int
232 videoioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
233 {
234 int unit = VIDEOUNIT(dev);
235 struct video_softc *sc;
236 struct v4l2_buffer *vb = (struct v4l2_buffer *)data;
237 int error;
238
239 KERNEL_ASSERT_LOCKED();
240
241 if (unit >= video_cd.cd_ndevs ||
242 (sc = video_cd.cd_devs[unit]) == NULL || sc->hw_if == NULL)
243 return (ENXIO);
244
245 DPRINTF(3, "video_ioctl(%zu, '%c', %zu)\n",
246 IOCPARM_LEN(cmd), (int) IOCGROUP(cmd), cmd & 0xff);
247
248 error = EOPNOTSUPP;
249 switch (cmd) {
250 case VIDIOC_G_CTRL:
251 if (sc->hw_if->g_ctrl)
252 error = (sc->hw_if->g_ctrl)(sc->hw_hdl,
253 (struct v4l2_control *)data);
254 break;
255 case VIDIOC_S_CTRL:
256 if (sc->hw_if->s_ctrl)
257 error = (sc->hw_if->s_ctrl)(sc->hw_hdl,
258 (struct v4l2_control *)data);
259 break;
260 default:
261 error = (ENOTTY);
262 }
263 if (error != ENOTTY)
264 return (error);
265
266 if ((error = video_claim(sc, p->p_p)))
267 return (error);
268
269 /*
270 * The following IOCTLs can only be called by the device owner.
271 * For further shared IOCTLs please move it up.
272 */
273 error = EOPNOTSUPP;
274 switch (cmd) {
275 case VIDIOC_QUERYCAP:
276 if (sc->hw_if->querycap)
277 error = (sc->hw_if->querycap)(sc->hw_hdl,
278 (struct v4l2_capability *)data);
279 break;
280 case VIDIOC_ENUM_FMT:
281 if (sc->hw_if->enum_fmt)
282 error = (sc->hw_if->enum_fmt)(sc->hw_hdl,
283 (struct v4l2_fmtdesc *)data);
284 break;
285 case VIDIOC_ENUM_FRAMESIZES:
286 if (sc->hw_if->enum_fsizes)
287 error = (sc->hw_if->enum_fsizes)(sc->hw_hdl,
288 (struct v4l2_frmsizeenum *)data);
289 break;
290 case VIDIOC_ENUM_FRAMEINTERVALS:
291 if (sc->hw_if->enum_fivals)
292 error = (sc->hw_if->enum_fivals)(sc->hw_hdl,
293 (struct v4l2_frmivalenum *)data);
294 break;
295 case VIDIOC_S_FMT:
296 if (!(flags & FWRITE))
297 return (EACCES);
298 if (sc->hw_if->s_fmt)
299 error = (sc->hw_if->s_fmt)(sc->hw_hdl,
300 (struct v4l2_format *)data);
301 break;
302 case VIDIOC_G_FMT:
303 if (sc->hw_if->g_fmt)
304 error = (sc->hw_if->g_fmt)(sc->hw_hdl,
305 (struct v4l2_format *)data);
306 break;
307 case VIDIOC_S_PARM:
308 if (sc->hw_if->s_parm)
309 error = (sc->hw_if->s_parm)(sc->hw_hdl,
310 (struct v4l2_streamparm *)data);
311 break;
312 case VIDIOC_G_PARM:
313 if (sc->hw_if->g_parm)
314 error = (sc->hw_if->g_parm)(sc->hw_hdl,
315 (struct v4l2_streamparm *)data);
316 break;
317 case VIDIOC_ENUMINPUT:
318 if (sc->hw_if->enum_input)
319 error = (sc->hw_if->enum_input)(sc->hw_hdl,
320 (struct v4l2_input *)data);
321 break;
322 case VIDIOC_S_INPUT:
323 if (sc->hw_if->s_input)
324 error = (sc->hw_if->s_input)(sc->hw_hdl,
325 (int)*data);
326 break;
327 case VIDIOC_G_INPUT:
328 if (sc->hw_if->g_input)
329 error = (sc->hw_if->g_input)(sc->hw_hdl,
330 (int *)data);
331 break;
332 case VIDIOC_REQBUFS:
333 if (sc->hw_if->reqbufs)
334 error = (sc->hw_if->reqbufs)(sc->hw_hdl,
335 (struct v4l2_requestbuffers *)data);
336 break;
337 case VIDIOC_QUERYBUF:
338 if (sc->hw_if->querybuf)
339 error = (sc->hw_if->querybuf)(sc->hw_hdl,
340 (struct v4l2_buffer *)data);
341 break;
342 case VIDIOC_QBUF:
343 if (sc->hw_if->qbuf)
344 error = (sc->hw_if->qbuf)(sc->hw_hdl,
345 (struct v4l2_buffer *)data);
346 break;
347 case VIDIOC_DQBUF:
348 if (!sc->hw_if->dqbuf)
349 break;
350 /* should have called mmap() before now */
351 if (sc->sc_vidmode != VIDMODE_MMAP) {
352 error = EINVAL;
353 break;
354 }
355 error = (sc->hw_if->dqbuf)(sc->hw_hdl,
356 (struct v4l2_buffer *)data);
357 if (!video_record_enable)
358 bzero(sc->sc_fbuffer_mmap + vb->m.offset, vb->length);
359 sc->sc_frames_ready--;
360 break;
361 case VIDIOC_STREAMON:
362 if (sc->hw_if->streamon)
363 error = (sc->hw_if->streamon)(sc->hw_hdl,
364 (int)*data);
365 break;
366 case VIDIOC_STREAMOFF:
367 if (sc->hw_if->streamoff)
368 error = (sc->hw_if->streamoff)(sc->hw_hdl,
369 (int)*data);
370 if (!error) {
371 /* Release device ownership and streaming buffers. */
372 error = video_stop(sc);
373 }
374 break;
375 case VIDIOC_TRY_FMT:
376 if (sc->hw_if->try_fmt)
377 error = (sc->hw_if->try_fmt)(sc->hw_hdl,
378 (struct v4l2_format *)data);
379 break;
380 case VIDIOC_QUERYCTRL:
381 if (sc->hw_if->queryctrl)
382 error = (sc->hw_if->queryctrl)(sc->hw_hdl,
383 (struct v4l2_queryctrl *)data);
384 break;
385 default:
386 error = (ENOTTY);
387 }
388
389 return (error);
390 }
391
392 paddr_t
393 videommap(dev_t dev, off_t off, int prot)
394 {
395 int unit = VIDEOUNIT(dev);
396 struct video_softc *sc;
397 caddr_t p;
398 paddr_t pa;
399
400 KERNEL_ASSERT_LOCKED();
401
402 DPRINTF(2, "%s: off=%lld, prot=%d\n", __func__, off, prot);
403
404 if (unit >= video_cd.cd_ndevs ||
405 (sc = video_cd.cd_devs[unit]) == NULL)
406 return (-1);
407
408 if (sc->sc_dying)
409 return (-1);
410
411 if (sc->hw_if->mappage == NULL)
412 return (-1);
413
414 p = sc->hw_if->mappage(sc->hw_hdl, off, prot);
415 if (p == NULL)
416 return (-1);
417 if (pmap_extract(pmap_kernel(), (vaddr_t)p, &pa) == FALSE)
418 panic("videommap: invalid page");
419 sc->sc_vidmode = VIDMODE_MMAP;
420
421 /* store frame buffer base address for later blanking */
422 if (off == 0)
423 sc->sc_fbuffer_mmap = p;
424
425 return (pa);
426 }
427
428 void
429 filt_videodetach(struct knote *kn)
430 {
431 struct video_softc *sc = kn->kn_hook;
432 int s;
433
434 s = splhigh();
435 klist_remove_locked(&sc->sc_rsel.si_note, kn);
436 splx(s);
437 }
438
439 int
440 filt_videoread(struct knote *kn, long hint)
441 {
442 struct video_softc *sc = kn->kn_hook;
443
444 if (sc->sc_frames_ready > 0)
445 return (1);
446
447 return (0);
448 }
449
450 const struct filterops video_filtops = {
451 .f_flags = FILTEROP_ISFD,
452 .f_attach = NULL,
453 .f_detach = filt_videodetach,
454 .f_event = filt_videoread,
455 };
456
457 int
458 videokqfilter(dev_t dev, struct knote *kn)
459 {
460 int unit = VIDEOUNIT(dev);
461 struct video_softc *sc;
462 int s, error;
463
464 KERNEL_ASSERT_LOCKED();
465
466 if (unit >= video_cd.cd_ndevs ||
467 (sc = video_cd.cd_devs[unit]) == NULL)
468 return (ENXIO);
469
470 if (sc->sc_dying)
471 return (ENXIO);
472
473 switch (kn->kn_filter) {
474 case EVFILT_READ:
475 kn->kn_fop = &video_filtops;
476 kn->kn_hook = sc;
477 break;
478 default:
479 return (EINVAL);
480 }
481
482 if ((error = video_claim(sc, curproc->p_p)))
483 return (error);
484
485 /*
486 * Start the stream in read() mode if not already started. If
487 * the user wanted mmap() mode, he should have called mmap()
488 * before now.
489 */
490 if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) {
491 if (sc->hw_if->start_read(sc->hw_hdl))
492 return (ENXIO);
493 sc->sc_vidmode = VIDMODE_READ;
494 }
495
496 s = splhigh();
497 klist_insert_locked(&sc->sc_rsel.si_note, kn);
498 splx(s);
499
500 return (0);
501 }
502
503 int
504 video_submatch(struct device *parent, void *match, void *aux)
505 {
506 struct cfdata *cf = match;
507
508 return (cf->cf_driver == &video_cd);
509 }
510
511 /*
512 * Called from hardware driver. This is where the MI video driver gets
513 * probed/attached to the hardware driver
514 */
515 struct device *
516 video_attach_mi(const struct video_hw_if *rhwp, void *hdlp, struct device *dev)
517 {
518 struct video_attach_args arg;
519
520 arg.hwif = rhwp;
521 arg.hdl = hdlp;
522 return (config_found_sm(dev, &arg, videoprint, video_submatch));
523 }
524
525 void
526 video_intr(void *addr)
527 {
528 struct video_softc *sc = (struct video_softc *)addr;
529
530 DPRINTF(3, "video_intr sc=%p\n", sc);
531 if (sc->sc_vidmode != VIDMODE_NONE)
532 sc->sc_frames_ready++;
533 else
534 printf("%s: interrupt but no streams!\n", __func__);
535 if (sc->sc_vidmode == VIDMODE_READ)
536 wakeup(sc);
537 selwakeup(&sc->sc_rsel);
538 }
539
540 int
541 video_stop(struct video_softc *sc)
542 {
543 int error = 0;
544
545 DPRINTF(1, "%s: stream close\n", __func__);
546
547 if (sc->hw_if->close != NULL)
548 error = sc->hw_if->close(sc->hw_hdl);
549
550 sc->sc_vidmode = VIDMODE_NONE;
551 sc->sc_frames_ready = 0;
552 sc->sc_owner = NULL;
553
554 return (error);
555 }
556
557 int
558 video_claim(struct video_softc *sc, struct process *pr)
559 {
560 if (sc->sc_owner != NULL && sc->sc_owner != pr) {
561 DPRINTF(1, "%s: already owned=%p\n", __func__, sc->sc_owner);
562 return (EBUSY);
563 }
564
565 if (sc->sc_owner == NULL) {
566 sc->sc_owner = pr;
567 DPRINTF(1, "%s: new owner=%p\n", __func__, sc->sc_owner);
568 }
569
570 return (0);
571 }
572
573 int
574 videoprint(void *aux, const char *pnp)
575 {
576 if (pnp != NULL)
577 printf("video at %s", pnp);
578 return (UNCONF);
579 }
580
581 int
582 videodetach(struct device *self, int flags)
583 {
584 struct video_softc *sc = (struct video_softc *)self;
585 int s, maj, mn;
586
587 /* locate the major number */
588 for (maj = 0; maj < nchrdev; maj++)
589 if (cdevsw[maj].d_open == videoopen)
590 break;
591
592 /* Nuke the vnodes for any open instances (calls close). */
593 mn = self->dv_unit;
594 vdevgone(maj, mn, mn, VCHR);
595
596 s = splhigh();
597 klist_invalidate(&sc->sc_rsel.si_note);
598 splx(s);
599
600 free(sc->sc_fbuffer, M_DEVBUF, sc->sc_fbufferlen);
601
602 return (0);
603 }
604
605 int
606 videoactivate(struct device *self, int act)
607 {
608 struct video_softc *sc = (struct video_softc *)self;
609
610 switch (act) {
611 case DVACT_DEACTIVATE:
612 sc->sc_dying = 1;
613 break;
614 }
615 return (0);
616 }
Cache object: 291059ae4ae674237bd30b1fbd4cf151
|