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