1 /* $OpenBSD: video.c,v 1.23 2008/11/11 12:37:07 mglocker 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/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/videoio.h> 31 #include <uvm/uvm.h> 32 #include <uvm/uvm_pmap.h> 33 34 #include <dev/video_if.h> 35 #include <dev/videovar.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 int videoprobe(struct device *, void *, void *); 44 void videoattach(struct device *, struct device *, void *); 45 int videodetach(struct device *, int); 46 int videoactivate(struct device *, enum devact); 47 int videoprint(void *, const char *); 48 49 void video_intr(void *); 50 51 struct cfattach video_ca = { 52 sizeof(struct video_softc), videoprobe, videoattach, 53 videodetach, videoactivate 54 }; 55 56 struct cfdriver video_cd = { 57 NULL, "video", DV_DULL 58 }; 59 60 int 61 videoprobe(struct device *parent, void *match, void *aux) 62 { 63 return (1); 64 } 65 66 void 67 videoattach(struct device *parent, struct device *self, void *aux) 68 { 69 struct video_softc *sc = (void *)self; 70 struct video_attach_args *sa = aux; 71 int video_buf_size = 0; 72 73 printf("\n"); 74 sc->hw_if = sa->hwif; 75 sc->hw_hdl = sa->hdl; 76 sc->sc_dev = parent; 77 78 if (sc->hw_if->get_bufsize) 79 video_buf_size = (sc->hw_if->get_bufsize)(sc->hw_hdl); 80 if (video_buf_size == EINVAL) { 81 printf("video: could not request frame buffer size\n"); 82 return; 83 } 84 85 sc->sc_fbuffer = malloc(video_buf_size, M_DEVBUF, M_NOWAIT); 86 if (sc->sc_fbuffer == NULL) { 87 printf("video: could not allocate frame buffer\n"); 88 return; 89 } 90 } 91 92 int 93 videoopen(dev_t dev, int flags, int fmt, struct proc *p) 94 { 95 int unit; 96 struct video_softc *sc; 97 98 unit = VIDEOUNIT(dev); 99 if (unit >= video_cd.cd_ndevs || 100 (sc = video_cd.cd_devs[unit]) == NULL || 101 sc->hw_if == NULL) 102 return (ENXIO); 103 104 if (sc->sc_open & VIDEO_OPEN) 105 return (EBUSY); 106 sc->sc_open |= VIDEO_OPEN; 107 108 sc->sc_start_read = 0; 109 110 if (sc->hw_if->open != NULL) 111 return (sc->hw_if->open(sc->hw_hdl, flags, &sc->sc_fsize, 112 sc->sc_fbuffer, video_intr, sc)); 113 else 114 return (0); 115 } 116 117 int 118 videoclose(dev_t dev, int flags, int fmt, struct proc *p) 119 { 120 struct video_softc *sc; 121 int r = 0; 122 123 sc = video_cd.cd_devs[VIDEOUNIT(dev)]; 124 125 if (sc->hw_if->close != NULL) 126 r = sc->hw_if->close(sc->hw_hdl); 127 128 sc->sc_open &= ~VIDEO_OPEN; 129 130 return (r); 131 } 132 133 int 134 videoread(dev_t dev, struct uio *uio, int ioflag) 135 { 136 struct video_softc *sc; 137 int unit, error, size; 138 139 unit = VIDEOUNIT(dev); 140 if (unit >= video_cd.cd_ndevs || 141 (sc = video_cd.cd_devs[unit]) == NULL) 142 return (ENXIO); 143 144 if (sc->sc_dying) 145 return (EIO); 146 147 /* start the stream */ 148 if (sc->hw_if->start_read && !sc->sc_start_read) { 149 error = sc->hw_if->start_read(sc->hw_hdl); 150 if (error) 151 return (error); 152 sc->sc_start_read = 1; 153 } 154 155 DPRINTF(("resid=%d\n", uio->uio_resid)); 156 157 /* block userland read until a frame is ready */ 158 error = tsleep(sc, PWAIT | PCATCH, "vid_rd", 0); 159 if (error) 160 return (error); 161 162 /* move the frame to userland */ 163 if (sc->sc_fsize < uio->uio_resid) 164 size = sc->sc_fsize; 165 else 166 size = uio->uio_resid; 167 error = uiomove(sc->sc_fbuffer, size, uio); 168 if (error) 169 return (error); 170 171 DPRINTF(("uiomove successfully done (%d bytes)\n", size)); 172 173 return (0); 174 } 175 176 int 177 videoioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) 178 { 179 struct video_softc *sc; 180 int unit, error; 181 182 unit = VIDEOUNIT(dev); 183 if (unit >= video_cd.cd_ndevs || 184 (sc = video_cd.cd_devs[unit]) == NULL || sc->hw_if == NULL) 185 return (ENXIO); 186 187 DPRINTF(("video_ioctl(%d, '%c', %d)\n", 188 IOCPARM_LEN(cmd), IOCGROUP(cmd), cmd & 0xff)); 189 190 error = EOPNOTSUPP; 191 switch (cmd) { 192 case VIDIOC_QUERYCAP: 193 if (sc->hw_if->querycap) 194 error = (sc->hw_if->querycap)(sc->hw_hdl, 195 (struct v4l2_capability *)data); 196 break; 197 case VIDIOC_ENUM_FMT: 198 if (sc->hw_if->enum_fmt) 199 error = (sc->hw_if->enum_fmt)(sc->hw_hdl, 200 (struct v4l2_fmtdesc *)data); 201 break; 202 case VIDIOC_ENUM_FRAMESIZES: 203 if (sc->hw_if->enum_fsizes) 204 error = (sc->hw_if->enum_fsizes)(sc->hw_hdl, 205 (struct v4l2_frmsizeenum *)data); 206 break; 207 case VIDIOC_ENUM_FRAMEINTERVALS: 208 if (sc->hw_if->enum_fivals) 209 error = (sc->hw_if->enum_fivals)(sc->hw_hdl, 210 (struct v4l2_frmivalenum *)data); 211 break; 212 case VIDIOC_S_FMT: 213 if (!(flags & FWRITE)) 214 return (EACCES); 215 if (sc->hw_if->s_fmt) 216 error = (sc->hw_if->s_fmt)(sc->hw_hdl, 217 (struct v4l2_format *)data); 218 break; 219 case VIDIOC_G_FMT: 220 if (sc->hw_if->g_fmt) 221 error = (sc->hw_if->g_fmt)(sc->hw_hdl, 222 (struct v4l2_format *)data); 223 break; 224 case VIDIOC_ENUMINPUT: 225 if (sc->hw_if->enum_input) 226 error = (sc->hw_if->enum_input)(sc->hw_hdl, 227 (struct v4l2_input *)data); 228 break; 229 case VIDIOC_S_INPUT: 230 if (sc->hw_if->s_input) 231 error = (sc->hw_if->s_input)(sc->hw_hdl, 232 (int)*data); 233 break; 234 case VIDIOC_REQBUFS: 235 if (sc->hw_if->reqbufs) 236 error = (sc->hw_if->reqbufs)(sc->hw_hdl, 237 (struct v4l2_requestbuffers *)data); 238 break; 239 case VIDIOC_QUERYBUF: 240 if (sc->hw_if->querybuf) 241 error = (sc->hw_if->querybuf)(sc->hw_hdl, 242 (struct v4l2_buffer *)data); 243 break; 244 case VIDIOC_QBUF: 245 if (sc->hw_if->qbuf) 246 error = (sc->hw_if->qbuf)(sc->hw_hdl, 247 (struct v4l2_buffer *)data); 248 break; 249 case VIDIOC_DQBUF: 250 if (sc->hw_if->dqbuf) 251 error = (sc->hw_if->dqbuf)(sc->hw_hdl, 252 (struct v4l2_buffer *)data); 253 break; 254 case VIDIOC_STREAMON: 255 if (sc->hw_if->streamon) 256 error = (sc->hw_if->streamon)(sc->hw_hdl, 257 (int)*data); 258 break; 259 case VIDIOC_STREAMOFF: 260 if (sc->hw_if->streamoff) 261 error = (sc->hw_if->streamoff)(sc->hw_hdl, 262 (int)*data); 263 break; 264 case VIDIOC_TRY_FMT: 265 if (sc->hw_if->try_fmt) 266 error = (sc->hw_if->try_fmt)(sc->hw_hdl, 267 (struct v4l2_format *)data); 268 break; 269 case VIDIOC_QUERYCTRL: 270 if (sc->hw_if->queryctrl) 271 error = (sc->hw_if->queryctrl)(sc->hw_hdl, 272 (struct v4l2_queryctrl *)data); 273 break; 274 case VIDIOC_G_CTRL: 275 if (sc->hw_if->g_ctrl) 276 error = (sc->hw_if->g_ctrl)(sc->hw_hdl, 277 (struct v4l2_control *)data); 278 break; 279 case VIDIOC_S_CTRL: 280 if (sc->hw_if->s_ctrl) 281 error = (sc->hw_if->s_ctrl)(sc->hw_hdl, 282 (struct v4l2_control *)data); 283 break; 284 default: 285 error = (ENOTTY); 286 } 287 288 return (error); 289 } 290 291 paddr_t 292 videommap(dev_t dev, off_t off, int prot) 293 { 294 struct video_softc *sc; 295 int unit; 296 caddr_t p; 297 paddr_t pa; 298 299 DPRINTF(("%s: off=%d, prot=%d\n", __func__, off, prot)); 300 301 unit = VIDEOUNIT(dev); 302 if (unit >= video_cd.cd_ndevs || 303 (sc = video_cd.cd_devs[unit]) == NULL) 304 return (-1); 305 306 if (sc->sc_dying) 307 return (-1); 308 309 if (sc->hw_if->mappage == NULL) 310 return (-1); 311 312 p = sc->hw_if->mappage(sc->hw_hdl, off, prot); 313 if (p == NULL) 314 return (-1); 315 if (pmap_extract(pmap_kernel(), (vaddr_t)p, &pa) == FALSE) 316 panic("videommap: invalid page"); 317 318 #if defined(__powerpc__) || defined(__sparc64__) 319 return (pa); 320 #else 321 return (atop(pa)); 322 #endif 323 } 324 325 /* 326 * Called from hardware driver. This is where the MI video driver gets 327 * probed/attached to the hardware driver 328 */ 329 struct device * 330 video_attach_mi(struct video_hw_if *rhwp, void *hdlp, struct device *dev) 331 { 332 struct video_attach_args arg; 333 334 arg.hwif = rhwp; 335 arg.hdl = hdlp; 336 return (config_found(dev, &arg, videoprint)); 337 } 338 339 void 340 video_intr(void *addr) 341 { 342 struct video_softc *sc = (struct video_softc *)addr; 343 344 DPRINTF(("video_intr sc=%p\n", sc)); 345 wakeup(sc); 346 } 347 348 int 349 videoprint(void *aux, const char *pnp) 350 { 351 if (pnp != NULL) 352 printf("video at %s", pnp); 353 return (UNCONF); 354 } 355 356 int 357 videodetach(struct device *self, int flags) 358 { 359 struct video_softc *sc = (struct video_softc *)self; 360 int maj, mn; 361 362 if (sc->sc_fbuffer != NULL) 363 free(sc->sc_fbuffer, M_DEVBUF); 364 365 /* locate the major number */ 366 for (maj = 0; maj < nchrdev; maj++) 367 if (cdevsw[maj].d_open == videoopen) 368 break; 369 370 /* Nuke the vnodes for any open instances (calls close). */ 371 mn = self->dv_unit; 372 vdevgone(maj, mn, mn, VCHR); 373 374 return (0); 375 } 376 377 int 378 videoactivate(struct device *self, enum devact act) 379 { 380 struct video_softc *sc = (struct video_softc *)self; 381 382 switch (act) { 383 case DVACT_ACTIVATE: 384 break; 385 386 case DVACT_DEACTIVATE: 387 sc->sc_dying = 1; 388 break; 389 } 390 return (0); 391 } 392