The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/video.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.