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 } 617