1*098ff4acSmvs /* $OpenBSD: video.c,v 1.59 2024/12/16 21:22:51 mvs Exp $ */ 24bb7a4f6Smpi 3cf110a4aSrobert /* 4cf110a4aSrobert * Copyright (c) 2008 Robert Nagy <robert@openbsd.org> 57dbc509fSmglocker * Copyright (c) 2008 Marcus Glocker <mglocker@openbsd.org> 6cf110a4aSrobert * 7cf110a4aSrobert * Permission to use, copy, modify, and distribute this software for any 8cf110a4aSrobert * purpose with or without fee is hereby granted, provided that the above 9cf110a4aSrobert * copyright notice and this permission notice appear in all copies. 10cf110a4aSrobert * 11cf110a4aSrobert * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12cf110a4aSrobert * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13cf110a4aSrobert * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14cf110a4aSrobert * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15cf110a4aSrobert * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16cf110a4aSrobert * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17cf110a4aSrobert * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18cf110a4aSrobert */ 19cf110a4aSrobert 20cf110a4aSrobert #include <sys/param.h> 21cf110a4aSrobert #include <sys/systm.h> 22cf110a4aSrobert #include <sys/errno.h> 2382fe8fc4Smvs #include <sys/event.h> 24cf110a4aSrobert #include <sys/ioctl.h> 25cf110a4aSrobert #include <sys/fcntl.h> 26cf110a4aSrobert #include <sys/device.h> 27cf110a4aSrobert #include <sys/vnode.h> 28dabf5a5aSmglocker #include <sys/kernel.h> 29dabf5a5aSmglocker #include <sys/malloc.h> 3082fe8fc4Smvs #include <sys/mutex.h> 31cf110a4aSrobert #include <sys/conf.h> 327eef5d8eSjca #include <sys/proc.h> 33cf110a4aSrobert #include <sys/videoio.h> 344bb7a4f6Smpi 35cf110a4aSrobert #include <dev/video_if.h> 36cf110a4aSrobert 37fde894e5Stedu #include <uvm/uvm_extern.h> 38fde894e5Stedu 3982fe8fc4Smvs /* 4082fe8fc4Smvs * Locks used to protect struct members and global data 41*098ff4acSmvs * a atomic 4282fe8fc4Smvs * m sc_mtx 4382fe8fc4Smvs */ 4482fe8fc4Smvs 45b121e78aSmglocker #ifdef VIDEO_DEBUG 461e03e03bSmglocker int video_debug = 1; 471e03e03bSmglocker #define DPRINTF(l, x...) do { if ((l) <= video_debug) printf(x); } while (0) 48b121e78aSmglocker #else 491e03e03bSmglocker #define DPRINTF(l, x...) 50b121e78aSmglocker #endif 51cf110a4aSrobert 5248fb1d35Smglocker struct video_softc { 5348fb1d35Smglocker struct device dev; 5448fb1d35Smglocker void *hw_hdl; /* hardware driver handle */ 5548fb1d35Smglocker struct device *sc_dev; /* hardware device struct */ 560d6a2fdeSmiod const struct video_hw_if *hw_if; /* hardware interface */ 5748fb1d35Smglocker char sc_dying; /* device detached */ 587eef5d8eSjca struct process *sc_owner; /* owner process */ 59d13871b7Smglocker uint8_t sc_open; /* device opened */ 6048fb1d35Smglocker 6182fe8fc4Smvs struct mutex sc_mtx; 6248fb1d35Smglocker int sc_fsize; 6348fb1d35Smglocker uint8_t *sc_fbuffer; 643f9e8a25Smglocker caddr_t sc_fbuffer_mmap; 6548fb1d35Smglocker size_t sc_fbufferlen; 6648fb1d35Smglocker int sc_vidmode; /* access mode */ 6748fb1d35Smglocker #define VIDMODE_NONE 0 6848fb1d35Smglocker #define VIDMODE_MMAP 1 6948fb1d35Smglocker #define VIDMODE_READ 2 7082fe8fc4Smvs int sc_frames_ready; /* [m] */ 7148fb1d35Smglocker 7282fe8fc4Smvs struct klist sc_rklist; /* [m] read selector */ 7348fb1d35Smglocker }; 7448fb1d35Smglocker 75cf110a4aSrobert int videoprobe(struct device *, void *, void *); 76cf110a4aSrobert void videoattach(struct device *, struct device *, void *); 77cf110a4aSrobert int videodetach(struct device *, int); 78e78728c7Spirofti int videoactivate(struct device *, int); 79cf110a4aSrobert int videoprint(void *, const char *); 80cf110a4aSrobert 81dabf5a5aSmglocker void video_intr(void *); 82d13871b7Smglocker int video_stop(struct video_softc *); 83d13871b7Smglocker int video_claim(struct video_softc *, struct process *); 84dabf5a5aSmglocker 85471aeecfSnaddy const struct cfattach video_ca = { 86cf110a4aSrobert sizeof(struct video_softc), videoprobe, videoattach, 87cf110a4aSrobert videodetach, videoactivate 88cf110a4aSrobert }; 89cf110a4aSrobert 90cf110a4aSrobert struct cfdriver video_cd = { 91cf110a4aSrobert NULL, "video", DV_DULL 92cf110a4aSrobert }; 93cf110a4aSrobert 943f9e8a25Smglocker /* 953f9e8a25Smglocker * Global flag to control if video recording is enabled by kern.video.record. 963f9e8a25Smglocker */ 97*098ff4acSmvs int video_record_enable = 0; /* [a] */ 983f9e8a25Smglocker 99cf110a4aSrobert int 100cf110a4aSrobert videoprobe(struct device *parent, void *match, void *aux) 101cf110a4aSrobert { 102cf110a4aSrobert return (1); 103cf110a4aSrobert } 104cf110a4aSrobert 105cf110a4aSrobert void 106cf110a4aSrobert videoattach(struct device *parent, struct device *self, void *aux) 107cf110a4aSrobert { 108cf110a4aSrobert struct video_softc *sc = (void *)self; 109cf110a4aSrobert struct video_attach_args *sa = aux; 110cf110a4aSrobert 111cf110a4aSrobert printf("\n"); 112cf110a4aSrobert sc->hw_if = sa->hwif; 113cf110a4aSrobert sc->hw_hdl = sa->hdl; 114cf110a4aSrobert sc->sc_dev = parent; 1151e44166bSderaadt sc->sc_fbufferlen = 0; 1167eef5d8eSjca sc->sc_owner = NULL; 11782fe8fc4Smvs mtx_init(&sc->sc_mtx, IPL_MPFLOOR); 11882fe8fc4Smvs klist_init_mutex(&sc->sc_rklist, &sc->sc_mtx); 119fcb11141Smglocker 1200e1aed5fSrobert if (sc->hw_if->get_bufsize) 1211e44166bSderaadt sc->sc_fbufferlen = (sc->hw_if->get_bufsize)(sc->hw_hdl); 1221e44166bSderaadt if (sc->sc_fbufferlen == 0) { 12370fff4d6Smglocker printf("video: could not request frame buffer size\n"); 12470fff4d6Smglocker return; 12570fff4d6Smglocker } 1260e1aed5fSrobert 1271e44166bSderaadt sc->sc_fbuffer = malloc(sc->sc_fbufferlen, M_DEVBUF, M_NOWAIT); 128fcb11141Smglocker if (sc->sc_fbuffer == NULL) { 129fcb11141Smglocker printf("video: could not allocate frame buffer\n"); 130fcb11141Smglocker return; 131fcb11141Smglocker } 132cf110a4aSrobert } 133cf110a4aSrobert 134cf110a4aSrobert int 135cf110a4aSrobert videoopen(dev_t dev, int flags, int fmt, struct proc *p) 136cf110a4aSrobert { 13705824dabSmglocker int unit = VIDEOUNIT(dev); 138cf110a4aSrobert struct video_softc *sc; 139d13871b7Smglocker int error = 0; 140d13871b7Smglocker 141d13871b7Smglocker KERNEL_ASSERT_LOCKED(); 142cf110a4aSrobert 143cf110a4aSrobert if (unit >= video_cd.cd_ndevs || 144cf110a4aSrobert (sc = video_cd.cd_devs[unit]) == NULL || 145cf110a4aSrobert sc->hw_if == NULL) 146cf110a4aSrobert return (ENXIO); 147cf110a4aSrobert 148d13871b7Smglocker if (sc->sc_open) { 1491e03e03bSmglocker DPRINTF(1, "%s: device already open\n", __func__); 1507eef5d8eSjca return (0); 151d13871b7Smglocker } 1522f789105Smglocker 153a256aad2Sjakemsr sc->sc_vidmode = VIDMODE_NONE; 154a256aad2Sjakemsr sc->sc_frames_ready = 0; 155321912b9Smglocker 1564770bd48Smglocker if (sc->hw_if->open != NULL) { 157d13871b7Smglocker error = sc->hw_if->open(sc->hw_hdl, flags, &sc->sc_fsize, 158d13871b7Smglocker sc->sc_fbuffer, video_intr, sc); 1594770bd48Smglocker } 1604770bd48Smglocker if (error == 0) { 1614770bd48Smglocker sc->sc_open = 1; 1624770bd48Smglocker DPRINTF(1, "%s: set device to open\n", __func__); 1634770bd48Smglocker } 164d13871b7Smglocker 165d13871b7Smglocker return (error); 166cf110a4aSrobert } 167cf110a4aSrobert 168cf110a4aSrobert int 169cf110a4aSrobert videoclose(dev_t dev, int flags, int fmt, struct proc *p) 170cf110a4aSrobert { 171cf110a4aSrobert struct video_softc *sc; 172d13871b7Smglocker int error = 0; 173d13871b7Smglocker 174d13871b7Smglocker KERNEL_ASSERT_LOCKED(); 175d13871b7Smglocker 1761e03e03bSmglocker DPRINTF(1, "%s: last close\n", __func__); 177cf110a4aSrobert 178cf110a4aSrobert sc = video_cd.cd_devs[VIDEOUNIT(dev)]; 179cf110a4aSrobert 180d13871b7Smglocker error = video_stop(sc); 181d13871b7Smglocker sc->sc_open = 0; 1822f789105Smglocker 183d13871b7Smglocker return (error); 184cf110a4aSrobert } 185cf110a4aSrobert 186cf110a4aSrobert int 187dabf5a5aSmglocker videoread(dev_t dev, struct uio *uio, int ioflag) 188dabf5a5aSmglocker { 18905824dabSmglocker int unit = VIDEOUNIT(dev); 190dabf5a5aSmglocker struct video_softc *sc; 19105824dabSmglocker int error; 192aba17a88Sstefan size_t size; 193dabf5a5aSmglocker 194d13871b7Smglocker KERNEL_ASSERT_LOCKED(); 195d13871b7Smglocker 196dabf5a5aSmglocker if (unit >= video_cd.cd_ndevs || 197dabf5a5aSmglocker (sc = video_cd.cd_devs[unit]) == NULL) 198dabf5a5aSmglocker return (ENXIO); 199dabf5a5aSmglocker 200dabf5a5aSmglocker if (sc->sc_dying) 201dabf5a5aSmglocker return (EIO); 202dabf5a5aSmglocker 203a256aad2Sjakemsr if (sc->sc_vidmode == VIDMODE_MMAP) 204a256aad2Sjakemsr return (EBUSY); 205a256aad2Sjakemsr 206d13871b7Smglocker if ((error = video_claim(sc, curproc->p_p))) 207d13871b7Smglocker return (error); 208d13871b7Smglocker 209a256aad2Sjakemsr /* start the stream if not already started */ 210a256aad2Sjakemsr if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) { 2116e7dc0a3Smglocker error = sc->hw_if->start_read(sc->hw_hdl); 2126e7dc0a3Smglocker if (error) 2136e7dc0a3Smglocker return (error); 214a256aad2Sjakemsr sc->sc_vidmode = VIDMODE_READ; 215321912b9Smglocker } 216321912b9Smglocker 2171e03e03bSmglocker DPRINTF(1, "resid=%zu\n", uio->uio_resid); 218dabf5a5aSmglocker 21982fe8fc4Smvs mtx_enter(&sc->sc_mtx); 22082fe8fc4Smvs 221a256aad2Sjakemsr if (sc->sc_frames_ready < 1) { 222dabf5a5aSmglocker /* block userland read until a frame is ready */ 22382fe8fc4Smvs error = msleep_nsec(sc, &sc->sc_mtx, PWAIT | PCATCH, 22482fe8fc4Smvs "vid_rd", INFSLP); 225a256aad2Sjakemsr if (sc->sc_dying) 226a256aad2Sjakemsr error = EIO; 22782fe8fc4Smvs if (error) { 22882fe8fc4Smvs mtx_leave(&sc->sc_mtx); 229dabf5a5aSmglocker return (error); 230a256aad2Sjakemsr } 23182fe8fc4Smvs } 23282fe8fc4Smvs sc->sc_frames_ready--; 23382fe8fc4Smvs 23482fe8fc4Smvs mtx_leave(&sc->sc_mtx); 235dabf5a5aSmglocker 236a256aad2Sjakemsr /* move no more than 1 frame to userland, as per specification */ 237aba17a88Sstefan size = ulmin(uio->uio_resid, sc->sc_fsize); 238*098ff4acSmvs if (!atomic_load_int(&video_record_enable)) 2393f9e8a25Smglocker bzero(sc->sc_fbuffer, size); 240aba17a88Sstefan error = uiomove(sc->sc_fbuffer, size, uio); 241dabf5a5aSmglocker if (error) 242dabf5a5aSmglocker return (error); 243dabf5a5aSmglocker 2441e03e03bSmglocker DPRINTF(1, "uiomove successfully done (%zu bytes)\n", size); 245dabf5a5aSmglocker 246dabf5a5aSmglocker return (0); 247dabf5a5aSmglocker } 248dabf5a5aSmglocker 249dabf5a5aSmglocker int 250cf110a4aSrobert videoioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) 251cf110a4aSrobert { 25205824dabSmglocker int unit = VIDEOUNIT(dev); 253cf110a4aSrobert struct video_softc *sc; 2543f9e8a25Smglocker struct v4l2_buffer *vb = (struct v4l2_buffer *)data; 25505824dabSmglocker int error; 256cf110a4aSrobert 257d13871b7Smglocker KERNEL_ASSERT_LOCKED(); 258d13871b7Smglocker 259cf110a4aSrobert if (unit >= video_cd.cd_ndevs || 260cf110a4aSrobert (sc = video_cd.cd_devs[unit]) == NULL || sc->hw_if == NULL) 261cf110a4aSrobert return (ENXIO); 262cf110a4aSrobert 2631e03e03bSmglocker DPRINTF(3, "video_ioctl(%zu, '%c', %zu)\n", 2641e03e03bSmglocker IOCPARM_LEN(cmd), (int) IOCGROUP(cmd), cmd & 0xff); 265cf110a4aSrobert 266cf110a4aSrobert error = EOPNOTSUPP; 267cf110a4aSrobert switch (cmd) { 268d13871b7Smglocker case VIDIOC_G_CTRL: 269d13871b7Smglocker if (sc->hw_if->g_ctrl) 270d13871b7Smglocker error = (sc->hw_if->g_ctrl)(sc->hw_hdl, 271d13871b7Smglocker (struct v4l2_control *)data); 272d13871b7Smglocker break; 273d13871b7Smglocker case VIDIOC_S_CTRL: 274d13871b7Smglocker if (sc->hw_if->s_ctrl) 275d13871b7Smglocker error = (sc->hw_if->s_ctrl)(sc->hw_hdl, 276d13871b7Smglocker (struct v4l2_control *)data); 277d13871b7Smglocker break; 278d13871b7Smglocker default: 279d13871b7Smglocker error = (ENOTTY); 280d13871b7Smglocker } 281d13871b7Smglocker if (error != ENOTTY) 282d13871b7Smglocker return (error); 283d13871b7Smglocker 284d13871b7Smglocker if ((error = video_claim(sc, p->p_p))) 285d13871b7Smglocker return (error); 286d13871b7Smglocker 287d13871b7Smglocker /* 288d13871b7Smglocker * The following IOCTLs can only be called by the device owner. 289d13871b7Smglocker * For further shared IOCTLs please move it up. 290d13871b7Smglocker */ 291d13871b7Smglocker error = EOPNOTSUPP; 292d13871b7Smglocker switch (cmd) { 293cf110a4aSrobert case VIDIOC_QUERYCAP: 294cf110a4aSrobert if (sc->hw_if->querycap) 295cf110a4aSrobert error = (sc->hw_if->querycap)(sc->hw_hdl, 296cf110a4aSrobert (struct v4l2_capability *)data); 297cf110a4aSrobert break; 2980aa3ede6Smglocker case VIDIOC_ENUM_FMT: 2990aa3ede6Smglocker if (sc->hw_if->enum_fmt) 3000aa3ede6Smglocker error = (sc->hw_if->enum_fmt)(sc->hw_hdl, 3010aa3ede6Smglocker (struct v4l2_fmtdesc *)data); 3020aa3ede6Smglocker break; 30312262cecSmglocker case VIDIOC_ENUM_FRAMESIZES: 30412262cecSmglocker if (sc->hw_if->enum_fsizes) 30512262cecSmglocker error = (sc->hw_if->enum_fsizes)(sc->hw_hdl, 30612262cecSmglocker (struct v4l2_frmsizeenum *)data); 30712262cecSmglocker break; 30812262cecSmglocker case VIDIOC_ENUM_FRAMEINTERVALS: 30912262cecSmglocker if (sc->hw_if->enum_fivals) 31012262cecSmglocker error = (sc->hw_if->enum_fivals)(sc->hw_hdl, 31112262cecSmglocker (struct v4l2_frmivalenum *)data); 31212262cecSmglocker break; 313cf110a4aSrobert case VIDIOC_S_FMT: 314cf110a4aSrobert if (!(flags & FWRITE)) 315cf110a4aSrobert return (EACCES); 316cf110a4aSrobert if (sc->hw_if->s_fmt) 317cf110a4aSrobert error = (sc->hw_if->s_fmt)(sc->hw_hdl, 318cf110a4aSrobert (struct v4l2_format *)data); 319cf110a4aSrobert break; 320cf110a4aSrobert case VIDIOC_G_FMT: 321cf110a4aSrobert if (sc->hw_if->g_fmt) 322cf110a4aSrobert error = (sc->hw_if->g_fmt)(sc->hw_hdl, 323cf110a4aSrobert (struct v4l2_format *)data); 324cf110a4aSrobert break; 325c9ba893eSjakemsr case VIDIOC_S_PARM: 326c9ba893eSjakemsr if (sc->hw_if->s_parm) 327c9ba893eSjakemsr error = (sc->hw_if->s_parm)(sc->hw_hdl, 328c9ba893eSjakemsr (struct v4l2_streamparm *)data); 329c9ba893eSjakemsr break; 330c9ba893eSjakemsr case VIDIOC_G_PARM: 331c9ba893eSjakemsr if (sc->hw_if->g_parm) 332c9ba893eSjakemsr error = (sc->hw_if->g_parm)(sc->hw_hdl, 333c9ba893eSjakemsr (struct v4l2_streamparm *)data); 334c9ba893eSjakemsr break; 3350aa3ede6Smglocker case VIDIOC_ENUMINPUT: 3360aa3ede6Smglocker if (sc->hw_if->enum_input) 3370aa3ede6Smglocker error = (sc->hw_if->enum_input)(sc->hw_hdl, 3380aa3ede6Smglocker (struct v4l2_input *)data); 3390aa3ede6Smglocker break; 3400aa3ede6Smglocker case VIDIOC_S_INPUT: 3410aa3ede6Smglocker if (sc->hw_if->s_input) 3420aa3ede6Smglocker error = (sc->hw_if->s_input)(sc->hw_hdl, 3430aa3ede6Smglocker (int)*data); 3440aa3ede6Smglocker break; 3459c924229Sarmani case VIDIOC_G_INPUT: 3469c924229Sarmani if (sc->hw_if->g_input) 3479c924229Sarmani error = (sc->hw_if->g_input)(sc->hw_hdl, 3489c924229Sarmani (int *)data); 3499c924229Sarmani break; 350f7821c14Smglocker case VIDIOC_REQBUFS: 351f7821c14Smglocker if (sc->hw_if->reqbufs) 352f7821c14Smglocker error = (sc->hw_if->reqbufs)(sc->hw_hdl, 353f7821c14Smglocker (struct v4l2_requestbuffers *)data); 3540aa3ede6Smglocker break; 355f7821c14Smglocker case VIDIOC_QUERYBUF: 356f7821c14Smglocker if (sc->hw_if->querybuf) 357f7821c14Smglocker error = (sc->hw_if->querybuf)(sc->hw_hdl, 358f7821c14Smglocker (struct v4l2_buffer *)data); 359f7821c14Smglocker break; 3601dc1fa5aSmglocker case VIDIOC_QBUF: 3611dc1fa5aSmglocker if (sc->hw_if->qbuf) 3621dc1fa5aSmglocker error = (sc->hw_if->qbuf)(sc->hw_hdl, 3631dc1fa5aSmglocker (struct v4l2_buffer *)data); 3641dc1fa5aSmglocker break; 365f7821c14Smglocker case VIDIOC_DQBUF: 366a256aad2Sjakemsr if (!sc->hw_if->dqbuf) 367a256aad2Sjakemsr break; 368a256aad2Sjakemsr /* should have called mmap() before now */ 369a256aad2Sjakemsr if (sc->sc_vidmode != VIDMODE_MMAP) { 370a256aad2Sjakemsr error = EINVAL; 371a256aad2Sjakemsr break; 372a256aad2Sjakemsr } 3731dc1fa5aSmglocker error = (sc->hw_if->dqbuf)(sc->hw_hdl, 3741dc1fa5aSmglocker (struct v4l2_buffer *)data); 375*098ff4acSmvs if (!atomic_load_int(&video_record_enable)) 3763f9e8a25Smglocker bzero(sc->sc_fbuffer_mmap + vb->m.offset, vb->length); 37782fe8fc4Smvs mtx_enter(&sc->sc_mtx); 378a256aad2Sjakemsr sc->sc_frames_ready--; 37982fe8fc4Smvs mtx_leave(&sc->sc_mtx); 3801dc1fa5aSmglocker break; 3811dc1fa5aSmglocker case VIDIOC_STREAMON: 3821dc1fa5aSmglocker if (sc->hw_if->streamon) 3831dc1fa5aSmglocker error = (sc->hw_if->streamon)(sc->hw_hdl, 3841dc1fa5aSmglocker (int)*data); 3850aa3ede6Smglocker break; 386952b8b3eSrobert case VIDIOC_STREAMOFF: 387952b8b3eSrobert if (sc->hw_if->streamoff) 388952b8b3eSrobert error = (sc->hw_if->streamoff)(sc->hw_hdl, 389952b8b3eSrobert (int)*data); 390d13871b7Smglocker if (!error) { 391d13871b7Smglocker /* Release device ownership and streaming buffers. */ 3929d17d797Smglocker error = video_stop(sc); 393d13871b7Smglocker } 394952b8b3eSrobert break; 3950aa3ede6Smglocker case VIDIOC_TRY_FMT: 3960aa3ede6Smglocker if (sc->hw_if->try_fmt) 3970aa3ede6Smglocker error = (sc->hw_if->try_fmt)(sc->hw_hdl, 3980aa3ede6Smglocker (struct v4l2_format *)data); 3990aa3ede6Smglocker break; 4006a4f627aSrobert case VIDIOC_QUERYCTRL: 4016a4f627aSrobert if (sc->hw_if->queryctrl) 4026a4f627aSrobert error = (sc->hw_if->queryctrl)(sc->hw_hdl, 4036a4f627aSrobert (struct v4l2_queryctrl *)data); 4046a4f627aSrobert break; 405cf110a4aSrobert default: 406cf110a4aSrobert error = (ENOTTY); 407cf110a4aSrobert } 408cf110a4aSrobert 409cf110a4aSrobert return (error); 410cf110a4aSrobert } 411cf110a4aSrobert 412dabf5a5aSmglocker paddr_t 413dabf5a5aSmglocker videommap(dev_t dev, off_t off, int prot) 414dabf5a5aSmglocker { 41505824dabSmglocker int unit = VIDEOUNIT(dev); 416dabf5a5aSmglocker struct video_softc *sc; 417f7821c14Smglocker caddr_t p; 418f7821c14Smglocker paddr_t pa; 419f7821c14Smglocker 420d13871b7Smglocker KERNEL_ASSERT_LOCKED(); 421d13871b7Smglocker 4221e03e03bSmglocker DPRINTF(2, "%s: off=%lld, prot=%d\n", __func__, off, prot); 423dabf5a5aSmglocker 424dabf5a5aSmglocker if (unit >= video_cd.cd_ndevs || 425dabf5a5aSmglocker (sc = video_cd.cd_devs[unit]) == NULL) 426f7821c14Smglocker return (-1); 427dabf5a5aSmglocker 428dabf5a5aSmglocker if (sc->sc_dying) 429f7821c14Smglocker return (-1); 430dabf5a5aSmglocker 431f7821c14Smglocker if (sc->hw_if->mappage == NULL) 432f7821c14Smglocker return (-1); 433dabf5a5aSmglocker 434f7821c14Smglocker p = sc->hw_if->mappage(sc->hw_hdl, off, prot); 435f7821c14Smglocker if (p == NULL) 436f7821c14Smglocker return (-1); 437f7821c14Smglocker if (pmap_extract(pmap_kernel(), (vaddr_t)p, &pa) == FALSE) 438f7821c14Smglocker panic("videommap: invalid page"); 439a256aad2Sjakemsr sc->sc_vidmode = VIDMODE_MMAP; 440f7821c14Smglocker 4413f9e8a25Smglocker /* store frame buffer base address for later blanking */ 4423f9e8a25Smglocker if (off == 0) 4433f9e8a25Smglocker sc->sc_fbuffer_mmap = p; 4443f9e8a25Smglocker 445f7821c14Smglocker return (pa); 446dabf5a5aSmglocker } 447dabf5a5aSmglocker 448092fd1acSmpi void 449092fd1acSmpi filt_videodetach(struct knote *kn) 450092fd1acSmpi { 451092fd1acSmpi struct video_softc *sc = kn->kn_hook; 452092fd1acSmpi 45382fe8fc4Smvs klist_remove(&sc->sc_rklist, kn); 454092fd1acSmpi } 455092fd1acSmpi 456092fd1acSmpi int 457092fd1acSmpi filt_videoread(struct knote *kn, long hint) 458092fd1acSmpi { 459092fd1acSmpi struct video_softc *sc = kn->kn_hook; 460092fd1acSmpi 461092fd1acSmpi if (sc->sc_frames_ready > 0) 462092fd1acSmpi return (1); 463092fd1acSmpi 464092fd1acSmpi return (0); 465092fd1acSmpi } 466092fd1acSmpi 46782fe8fc4Smvs int 46882fe8fc4Smvs filt_videomodify(struct kevent *kev, struct knote *kn) 46982fe8fc4Smvs { 47082fe8fc4Smvs struct video_softc *sc = kn->kn_hook; 47182fe8fc4Smvs int active; 47282fe8fc4Smvs 47382fe8fc4Smvs mtx_enter(&sc->sc_mtx); 47482fe8fc4Smvs active = knote_modify(kev, kn); 47582fe8fc4Smvs mtx_leave(&sc->sc_mtx); 47682fe8fc4Smvs 47782fe8fc4Smvs return (active); 47882fe8fc4Smvs } 47982fe8fc4Smvs 48082fe8fc4Smvs int 48182fe8fc4Smvs filt_videoprocess(struct knote *kn, struct kevent *kev) 48282fe8fc4Smvs { 48382fe8fc4Smvs struct video_softc *sc = kn->kn_hook; 48482fe8fc4Smvs int active; 48582fe8fc4Smvs 48682fe8fc4Smvs mtx_enter(&sc->sc_mtx); 48782fe8fc4Smvs active = knote_process(kn, kev); 48882fe8fc4Smvs mtx_leave(&sc->sc_mtx); 48982fe8fc4Smvs 49082fe8fc4Smvs return (active); 49182fe8fc4Smvs } 49282fe8fc4Smvs 493092fd1acSmpi const struct filterops video_filtops = { 49482fe8fc4Smvs .f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE, 495092fd1acSmpi .f_attach = NULL, 496092fd1acSmpi .f_detach = filt_videodetach, 497092fd1acSmpi .f_event = filt_videoread, 49882fe8fc4Smvs .f_modify = filt_videomodify, 49982fe8fc4Smvs .f_process = filt_videoprocess, 500092fd1acSmpi }; 501092fd1acSmpi 502092fd1acSmpi int 503092fd1acSmpi videokqfilter(dev_t dev, struct knote *kn) 504092fd1acSmpi { 505092fd1acSmpi int unit = VIDEOUNIT(dev); 506092fd1acSmpi struct video_softc *sc; 50782fe8fc4Smvs int error; 508d13871b7Smglocker 509d13871b7Smglocker KERNEL_ASSERT_LOCKED(); 510092fd1acSmpi 511092fd1acSmpi if (unit >= video_cd.cd_ndevs || 512092fd1acSmpi (sc = video_cd.cd_devs[unit]) == NULL) 513092fd1acSmpi return (ENXIO); 514092fd1acSmpi 515092fd1acSmpi if (sc->sc_dying) 516092fd1acSmpi return (ENXIO); 517092fd1acSmpi 518092fd1acSmpi switch (kn->kn_filter) { 519092fd1acSmpi case EVFILT_READ: 520092fd1acSmpi kn->kn_fop = &video_filtops; 521092fd1acSmpi kn->kn_hook = sc; 522092fd1acSmpi break; 523092fd1acSmpi default: 524092fd1acSmpi return (EINVAL); 525092fd1acSmpi } 526092fd1acSmpi 527d13871b7Smglocker if ((error = video_claim(sc, curproc->p_p))) 528d13871b7Smglocker return (error); 529d13871b7Smglocker 530092fd1acSmpi /* 531092fd1acSmpi * Start the stream in read() mode if not already started. If 532092fd1acSmpi * the user wanted mmap() mode, he should have called mmap() 533092fd1acSmpi * before now. 534092fd1acSmpi */ 535092fd1acSmpi if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) { 536092fd1acSmpi if (sc->hw_if->start_read(sc->hw_hdl)) 537092fd1acSmpi return (ENXIO); 538092fd1acSmpi sc->sc_vidmode = VIDMODE_READ; 539092fd1acSmpi } 540092fd1acSmpi 54182fe8fc4Smvs klist_insert(&sc->sc_rklist, kn); 542092fd1acSmpi 543092fd1acSmpi return (0); 544092fd1acSmpi } 545092fd1acSmpi 546181d57e0Smglocker int 547181d57e0Smglocker video_submatch(struct device *parent, void *match, void *aux) 548181d57e0Smglocker { 549181d57e0Smglocker struct cfdata *cf = match; 550181d57e0Smglocker 551181d57e0Smglocker return (cf->cf_driver == &video_cd); 552181d57e0Smglocker } 553181d57e0Smglocker 554cf110a4aSrobert /* 555cf110a4aSrobert * Called from hardware driver. This is where the MI video driver gets 556cf110a4aSrobert * probed/attached to the hardware driver 557cf110a4aSrobert */ 558cf110a4aSrobert struct device * 5590d6a2fdeSmiod video_attach_mi(const struct video_hw_if *rhwp, void *hdlp, struct device *dev) 560cf110a4aSrobert { 561cf110a4aSrobert struct video_attach_args arg; 562cf110a4aSrobert 563cf110a4aSrobert arg.hwif = rhwp; 564cf110a4aSrobert arg.hdl = hdlp; 565181d57e0Smglocker return (config_found_sm(dev, &arg, videoprint, video_submatch)); 566cf110a4aSrobert } 567cf110a4aSrobert 568dabf5a5aSmglocker void 569dabf5a5aSmglocker video_intr(void *addr) 570dabf5a5aSmglocker { 571dabf5a5aSmglocker struct video_softc *sc = (struct video_softc *)addr; 572dabf5a5aSmglocker 5731e03e03bSmglocker DPRINTF(3, "video_intr sc=%p\n", sc); 57482fe8fc4Smvs mtx_enter(&sc->sc_mtx); 575a256aad2Sjakemsr if (sc->sc_vidmode != VIDMODE_NONE) 576a256aad2Sjakemsr sc->sc_frames_ready++; 577a256aad2Sjakemsr else 578a256aad2Sjakemsr printf("%s: interrupt but no streams!\n", __func__); 579a256aad2Sjakemsr if (sc->sc_vidmode == VIDMODE_READ) 580dabf5a5aSmglocker wakeup(sc); 58182fe8fc4Smvs knote_locked(&sc->sc_rklist, 0); 58282fe8fc4Smvs mtx_leave(&sc->sc_mtx); 583dabf5a5aSmglocker } 584dabf5a5aSmglocker 585cf110a4aSrobert int 586d13871b7Smglocker video_stop(struct video_softc *sc) 587d13871b7Smglocker { 588d13871b7Smglocker int error = 0; 589d13871b7Smglocker 5901e03e03bSmglocker DPRINTF(1, "%s: stream close\n", __func__); 591d13871b7Smglocker 592d13871b7Smglocker if (sc->hw_if->close != NULL) 593d13871b7Smglocker error = sc->hw_if->close(sc->hw_hdl); 594d13871b7Smglocker 595d13871b7Smglocker sc->sc_vidmode = VIDMODE_NONE; 59682fe8fc4Smvs mtx_enter(&sc->sc_mtx); 597d13871b7Smglocker sc->sc_frames_ready = 0; 59882fe8fc4Smvs mtx_leave(&sc->sc_mtx); 599d13871b7Smglocker sc->sc_owner = NULL; 600d13871b7Smglocker 601d13871b7Smglocker return (error); 602d13871b7Smglocker } 603d13871b7Smglocker 604d13871b7Smglocker int 605759f3dcfSmglocker video_claim(struct video_softc *sc, struct process *pr) 606d13871b7Smglocker { 607759f3dcfSmglocker if (sc->sc_owner != NULL && sc->sc_owner != pr) { 6081e03e03bSmglocker DPRINTF(1, "%s: already owned=%p\n", __func__, sc->sc_owner); 609d13871b7Smglocker return (EBUSY); 610d13871b7Smglocker } 611d13871b7Smglocker 612d13871b7Smglocker if (sc->sc_owner == NULL) { 613759f3dcfSmglocker sc->sc_owner = pr; 6141e03e03bSmglocker DPRINTF(1, "%s: new owner=%p\n", __func__, sc->sc_owner); 615d13871b7Smglocker } 616d13871b7Smglocker 617d13871b7Smglocker return (0); 618d13871b7Smglocker } 619d13871b7Smglocker 620d13871b7Smglocker int 621cf110a4aSrobert videoprint(void *aux, const char *pnp) 622cf110a4aSrobert { 623cf110a4aSrobert if (pnp != NULL) 624cf110a4aSrobert printf("video at %s", pnp); 625cf110a4aSrobert return (UNCONF); 626cf110a4aSrobert } 627cf110a4aSrobert 628cf110a4aSrobert int 629cf110a4aSrobert videodetach(struct device *self, int flags) 630cf110a4aSrobert { 631fcb11141Smglocker struct video_softc *sc = (struct video_softc *)self; 63282fe8fc4Smvs int maj, mn; 633cf110a4aSrobert 634cf110a4aSrobert /* locate the major number */ 635cf110a4aSrobert for (maj = 0; maj < nchrdev; maj++) 636cf110a4aSrobert if (cdevsw[maj].d_open == videoopen) 637cf110a4aSrobert break; 638cf110a4aSrobert 639cf110a4aSrobert /* Nuke the vnodes for any open instances (calls close). */ 640cf110a4aSrobert mn = self->dv_unit; 641cf110a4aSrobert vdevgone(maj, mn, mn, VCHR); 642cf110a4aSrobert 64382fe8fc4Smvs klist_invalidate(&sc->sc_rklist); 64482fe8fc4Smvs klist_free(&sc->sc_rklist); 645092fd1acSmpi 6464a480bf8Smpi free(sc->sc_fbuffer, M_DEVBUF, sc->sc_fbufferlen); 6474a480bf8Smpi 648cf110a4aSrobert return (0); 649cf110a4aSrobert } 650cf110a4aSrobert 651cf110a4aSrobert int 652e78728c7Spirofti videoactivate(struct device *self, int act) 653cf110a4aSrobert { 654cf110a4aSrobert struct video_softc *sc = (struct video_softc *)self; 655cf110a4aSrobert 656cf110a4aSrobert switch (act) { 657cf110a4aSrobert case DVACT_DEACTIVATE: 658cf110a4aSrobert sc->sc_dying = 1; 659cf110a4aSrobert break; 660cf110a4aSrobert } 661cf110a4aSrobert return (0); 662cf110a4aSrobert } 663