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