cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

capture.c.rst (15011B)


      1.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
      2
      3file: media/v4l/capture.c
      4=========================
      5
      6.. code-block:: c
      7
      8    /*
      9     *  V4L2 video capture example
     10     *
     11     *  This program can be used and distributed without restrictions.
     12     *
     13     *      This program is provided with the V4L2 API
     14     * see https://linuxtv.org/docs.php for more information
     15     */
     16
     17    #include <stdio.h>
     18    #include <stdlib.h>
     19    #include <string.h>
     20    #include <assert.h>
     21
     22    #include <getopt.h>             /* getopt_long() */
     23
     24    #include <fcntl.h>              /* low-level i/o */
     25    #include <unistd.h>
     26    #include <errno.h>
     27    #include <sys/stat.h>
     28    #include <sys/types.h>
     29    #include <sys/time.h>
     30    #include <sys/mman.h>
     31    #include <sys/ioctl.h>
     32
     33    #include <linux/videodev2.h>
     34
     35    #define CLEAR(x) memset(&(x), 0, sizeof(x))
     36
     37    enum io_method {
     38	    IO_METHOD_READ,
     39	    IO_METHOD_MMAP,
     40	    IO_METHOD_USERPTR,
     41    };
     42
     43    struct buffer {
     44	    void   *start;
     45	    size_t  length;
     46    };
     47
     48    static char            *dev_name;
     49    static enum io_method   io = IO_METHOD_MMAP;
     50    static int              fd = -1;
     51    struct buffer          *buffers;
     52    static unsigned int     n_buffers;
     53    static int              out_buf;
     54    static int              force_format;
     55    static int              frame_count = 70;
     56
     57    static void errno_exit(const char *s)
     58    {
     59	    fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
     60	    exit(EXIT_FAILURE);
     61    }
     62
     63    static int xioctl(int fh, int request, void *arg)
     64    {
     65	    int r;
     66
     67	    do {
     68		    r = ioctl(fh, request, arg);
     69	    } while (-1 == r && EINTR == errno);
     70
     71	    return r;
     72    }
     73
     74    static void process_image(const void *p, int size)
     75    {
     76	    if (out_buf)
     77		    fwrite(p, size, 1, stdout);
     78
     79	    fflush(stderr);
     80	    fprintf(stderr, ".");
     81	    fflush(stdout);
     82    }
     83
     84    static int read_frame(void)
     85    {
     86	    struct v4l2_buffer buf;
     87	    unsigned int i;
     88
     89	    switch (io) {
     90	    case IO_METHOD_READ:
     91		    if (-1 == read(fd, buffers[0].start, buffers[0].length)) {
     92			    switch (errno) {
     93			    case EAGAIN:
     94				    return 0;
     95
     96			    case EIO:
     97				    /* Could ignore EIO, see spec. */
     98
     99				    /* fall through */
    100
    101			    default:
    102				    errno_exit("read");
    103			    }
    104		    }
    105
    106		    process_image(buffers[0].start, buffers[0].length);
    107		    break;
    108
    109	    case IO_METHOD_MMAP:
    110		    CLEAR(buf);
    111
    112		    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    113		    buf.memory = V4L2_MEMORY_MMAP;
    114
    115		    if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
    116			    switch (errno) {
    117			    case EAGAIN:
    118				    return 0;
    119
    120			    case EIO:
    121				    /* Could ignore EIO, see spec. */
    122
    123				    /* fall through */
    124
    125			    default:
    126				    errno_exit("VIDIOC_DQBUF");
    127			    }
    128		    }
    129
    130		    assert(buf.index < n_buffers);
    131
    132		    process_image(buffers[buf.index].start, buf.bytesused);
    133
    134		    if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
    135			    errno_exit("VIDIOC_QBUF");
    136		    break;
    137
    138	    case IO_METHOD_USERPTR:
    139		    CLEAR(buf);
    140
    141		    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    142		    buf.memory = V4L2_MEMORY_USERPTR;
    143
    144		    if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
    145			    switch (errno) {
    146			    case EAGAIN:
    147				    return 0;
    148
    149			    case EIO:
    150				    /* Could ignore EIO, see spec. */
    151
    152				    /* fall through */
    153
    154			    default:
    155				    errno_exit("VIDIOC_DQBUF");
    156			    }
    157		    }
    158
    159		    for (i = 0; i < n_buffers; ++i)
    160			    if (buf.m.userptr == (unsigned long)buffers[i].start
    161				&& buf.length == buffers[i].length)
    162				    break;
    163
    164		    assert(i < n_buffers);
    165
    166		    process_image((void *)buf.m.userptr, buf.bytesused);
    167
    168		    if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
    169			    errno_exit("VIDIOC_QBUF");
    170		    break;
    171	    }
    172
    173	    return 1;
    174    }
    175
    176    static void mainloop(void)
    177    {
    178	    unsigned int count;
    179
    180	    count = frame_count;
    181
    182	    while (count-- > 0) {
    183		    for (;;) {
    184			    fd_set fds;
    185			    struct timeval tv;
    186			    int r;
    187
    188			    FD_ZERO(&fds);
    189			    FD_SET(fd, &fds);
    190
    191			    /* Timeout. */
    192			    tv.tv_sec = 2;
    193			    tv.tv_usec = 0;
    194
    195			    r = select(fd + 1, &fds, NULL, NULL, &tv);
    196
    197			    if (-1 == r) {
    198				    if (EINTR == errno)
    199					    continue;
    200				    errno_exit("select");
    201			    }
    202
    203			    if (0 == r) {
    204				    fprintf(stderr, "select timeout\n");
    205				    exit(EXIT_FAILURE);
    206			    }
    207
    208			    if (read_frame())
    209				    break;
    210			    /* EAGAIN - continue select loop. */
    211		    }
    212	    }
    213    }
    214
    215    static void stop_capturing(void)
    216    {
    217	    enum v4l2_buf_type type;
    218
    219	    switch (io) {
    220	    case IO_METHOD_READ:
    221		    /* Nothing to do. */
    222		    break;
    223
    224	    case IO_METHOD_MMAP:
    225	    case IO_METHOD_USERPTR:
    226		    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    227		    if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
    228			    errno_exit("VIDIOC_STREAMOFF");
    229		    break;
    230	    }
    231    }
    232
    233    static void start_capturing(void)
    234    {
    235	    unsigned int i;
    236	    enum v4l2_buf_type type;
    237
    238	    switch (io) {
    239	    case IO_METHOD_READ:
    240		    /* Nothing to do. */
    241		    break;
    242
    243	    case IO_METHOD_MMAP:
    244		    for (i = 0; i < n_buffers; ++i) {
    245			    struct v4l2_buffer buf;
    246
    247			    CLEAR(buf);
    248			    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    249			    buf.memory = V4L2_MEMORY_MMAP;
    250			    buf.index = i;
    251
    252			    if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
    253				    errno_exit("VIDIOC_QBUF");
    254		    }
    255		    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    256		    if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
    257			    errno_exit("VIDIOC_STREAMON");
    258		    break;
    259
    260	    case IO_METHOD_USERPTR:
    261		    for (i = 0; i < n_buffers; ++i) {
    262			    struct v4l2_buffer buf;
    263
    264			    CLEAR(buf);
    265			    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    266			    buf.memory = V4L2_MEMORY_USERPTR;
    267			    buf.index = i;
    268			    buf.m.userptr = (unsigned long)buffers[i].start;
    269			    buf.length = buffers[i].length;
    270
    271			    if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
    272				    errno_exit("VIDIOC_QBUF");
    273		    }
    274		    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    275		    if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
    276			    errno_exit("VIDIOC_STREAMON");
    277		    break;
    278	    }
    279    }
    280
    281    static void uninit_device(void)
    282    {
    283	    unsigned int i;
    284
    285	    switch (io) {
    286	    case IO_METHOD_READ:
    287		    free(buffers[0].start);
    288		    break;
    289
    290	    case IO_METHOD_MMAP:
    291		    for (i = 0; i < n_buffers; ++i)
    292			    if (-1 == munmap(buffers[i].start, buffers[i].length))
    293				    errno_exit("munmap");
    294		    break;
    295
    296	    case IO_METHOD_USERPTR:
    297		    for (i = 0; i < n_buffers; ++i)
    298			    free(buffers[i].start);
    299		    break;
    300	    }
    301
    302	    free(buffers);
    303    }
    304
    305    static void init_read(unsigned int buffer_size)
    306    {
    307	    buffers = calloc(1, sizeof(*buffers));
    308
    309	    if (!buffers) {
    310		    fprintf(stderr, "Out of memory\n");
    311		    exit(EXIT_FAILURE);
    312	    }
    313
    314	    buffers[0].length = buffer_size;
    315	    buffers[0].start = malloc(buffer_size);
    316
    317	    if (!buffers[0].start) {
    318		    fprintf(stderr, "Out of memory\n");
    319		    exit(EXIT_FAILURE);
    320	    }
    321    }
    322
    323    static void init_mmap(void)
    324    {
    325	    struct v4l2_requestbuffers req;
    326
    327	    CLEAR(req);
    328
    329	    req.count = 4;
    330	    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    331	    req.memory = V4L2_MEMORY_MMAP;
    332
    333	    if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
    334		    if (EINVAL == errno) {
    335			    fprintf(stderr, "%s does not support "
    336				     "memory mappingn", dev_name);
    337			    exit(EXIT_FAILURE);
    338		    } else {
    339			    errno_exit("VIDIOC_REQBUFS");
    340		    }
    341	    }
    342
    343	    if (req.count < 2) {
    344		    fprintf(stderr, "Insufficient buffer memory on %s\n",
    345			     dev_name);
    346		    exit(EXIT_FAILURE);
    347	    }
    348
    349	    buffers = calloc(req.count, sizeof(*buffers));
    350
    351	    if (!buffers) {
    352		    fprintf(stderr, "Out of memory\n");
    353		    exit(EXIT_FAILURE);
    354	    }
    355
    356	    for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
    357		    struct v4l2_buffer buf;
    358
    359		    CLEAR(buf);
    360
    361		    buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    362		    buf.memory      = V4L2_MEMORY_MMAP;
    363		    buf.index       = n_buffers;
    364
    365		    if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
    366			    errno_exit("VIDIOC_QUERYBUF");
    367
    368		    buffers[n_buffers].length = buf.length;
    369		    buffers[n_buffers].start =
    370			    mmap(NULL /* start anywhere */,
    371				  buf.length,
    372				  PROT_READ | PROT_WRITE /* required */,
    373				  MAP_SHARED /* recommended */,
    374				  fd, buf.m.offset);
    375
    376		    if (MAP_FAILED == buffers[n_buffers].start)
    377			    errno_exit("mmap");
    378	    }
    379    }
    380
    381    static void init_userp(unsigned int buffer_size)
    382    {
    383	    struct v4l2_requestbuffers req;
    384
    385	    CLEAR(req);
    386
    387	    req.count  = 4;
    388	    req.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    389	    req.memory = V4L2_MEMORY_USERPTR;
    390
    391	    if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
    392		    if (EINVAL == errno) {
    393			    fprintf(stderr, "%s does not support "
    394				     "user pointer i/on", dev_name);
    395			    exit(EXIT_FAILURE);
    396		    } else {
    397			    errno_exit("VIDIOC_REQBUFS");
    398		    }
    399	    }
    400
    401	    buffers = calloc(4, sizeof(*buffers));
    402
    403	    if (!buffers) {
    404		    fprintf(stderr, "Out of memory\n");
    405		    exit(EXIT_FAILURE);
    406	    }
    407
    408	    for (n_buffers = 0; n_buffers < 4; ++n_buffers) {
    409		    buffers[n_buffers].length = buffer_size;
    410		    buffers[n_buffers].start = malloc(buffer_size);
    411
    412		    if (!buffers[n_buffers].start) {
    413			    fprintf(stderr, "Out of memory\n");
    414			    exit(EXIT_FAILURE);
    415		    }
    416	    }
    417    }
    418
    419    static void init_device(void)
    420    {
    421	    struct v4l2_capability cap;
    422	    struct v4l2_cropcap cropcap;
    423	    struct v4l2_crop crop;
    424	    struct v4l2_format fmt;
    425	    unsigned int min;
    426
    427	    if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
    428		    if (EINVAL == errno) {
    429			    fprintf(stderr, "%s is no V4L2 device\n",
    430				     dev_name);
    431			    exit(EXIT_FAILURE);
    432		    } else {
    433			    errno_exit("VIDIOC_QUERYCAP");
    434		    }
    435	    }
    436
    437	    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
    438		    fprintf(stderr, "%s is no video capture device\n",
    439			     dev_name);
    440		    exit(EXIT_FAILURE);
    441	    }
    442
    443	    switch (io) {
    444	    case IO_METHOD_READ:
    445		    if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
    446			    fprintf(stderr, "%s does not support read i/o\n",
    447				     dev_name);
    448			    exit(EXIT_FAILURE);
    449		    }
    450		    break;
    451
    452	    case IO_METHOD_MMAP:
    453	    case IO_METHOD_USERPTR:
    454		    if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
    455			    fprintf(stderr, "%s does not support streaming i/o\n",
    456				     dev_name);
    457			    exit(EXIT_FAILURE);
    458		    }
    459		    break;
    460	    }
    461
    462
    463	    /* Select video input, video standard and tune here. */
    464
    465
    466	    CLEAR(cropcap);
    467
    468	    cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    469
    470	    if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
    471		    crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    472		    crop.c = cropcap.defrect; /* reset to default */
    473
    474		    if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
    475			    switch (errno) {
    476			    case EINVAL:
    477				    /* Cropping not supported. */
    478				    break;
    479			    default:
    480				    /* Errors ignored. */
    481				    break;
    482			    }
    483		    }
    484	    } else {
    485		    /* Errors ignored. */
    486	    }
    487
    488
    489	    CLEAR(fmt);
    490
    491	    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    492	    if (force_format) {
    493		    fmt.fmt.pix.width       = 640;
    494		    fmt.fmt.pix.height      = 480;
    495		    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    496		    fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
    497
    498		    if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
    499			    errno_exit("VIDIOC_S_FMT");
    500
    501		    /* Note VIDIOC_S_FMT may change width and height. */
    502	    } else {
    503		    /* Preserve original settings as set by v4l2-ctl for example */
    504		    if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))
    505			    errno_exit("VIDIOC_G_FMT");
    506	    }
    507
    508	    /* Buggy driver paranoia. */
    509	    min = fmt.fmt.pix.width * 2;
    510	    if (fmt.fmt.pix.bytesperline < min)
    511		    fmt.fmt.pix.bytesperline = min;
    512	    min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
    513	    if (fmt.fmt.pix.sizeimage < min)
    514		    fmt.fmt.pix.sizeimage = min;
    515
    516	    switch (io) {
    517	    case IO_METHOD_READ:
    518		    init_read(fmt.fmt.pix.sizeimage);
    519		    break;
    520
    521	    case IO_METHOD_MMAP:
    522		    init_mmap();
    523		    break;
    524
    525	    case IO_METHOD_USERPTR:
    526		    init_userp(fmt.fmt.pix.sizeimage);
    527		    break;
    528	    }
    529    }
    530
    531    static void close_device(void)
    532    {
    533	    if (-1 == close(fd))
    534		    errno_exit("close");
    535
    536	    fd = -1;
    537    }
    538
    539    static void open_device(void)
    540    {
    541	    struct stat st;
    542
    543	    if (-1 == stat(dev_name, &st)) {
    544		    fprintf(stderr, "Cannot identify '%s': %d, %s\n",
    545			     dev_name, errno, strerror(errno));
    546		    exit(EXIT_FAILURE);
    547	    }
    548
    549	    if (!S_ISCHR(st.st_mode)) {
    550		    fprintf(stderr, "%s is no devicen", dev_name);
    551		    exit(EXIT_FAILURE);
    552	    }
    553
    554	    fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
    555
    556	    if (-1 == fd) {
    557		    fprintf(stderr, "Cannot open '%s': %d, %s\n",
    558			     dev_name, errno, strerror(errno));
    559		    exit(EXIT_FAILURE);
    560	    }
    561    }
    562
    563    static void usage(FILE *fp, int argc, char **argv)
    564    {
    565	    fprintf(fp,
    566		     "Usage: %s [options]\n\n"
    567		     "Version 1.3\n"
    568		     "Options:\n"
    569		     "-d | --device name   Video device name [%s]\n"
    570		     "-h | --help          Print this message\n"
    571		     "-m | --mmap          Use memory mapped buffers [default]\n"
    572		     "-r | --read          Use read() calls\n"
    573		     "-u | --userp         Use application allocated buffers\n"
    574		     "-o | --output        Outputs stream to stdout\n"
    575		     "-f | --format        Force format to 640x480 YUYV\n"
    576		     "-c | --count         Number of frames to grab [%i]\n"
    577		     "",
    578		     argv[0], dev_name, frame_count);
    579    }
    580
    581    static const char short_options[] = "d:hmruofc:";
    582
    583    static const struct option
    584    long_options[] = {
    585	    { "device", required_argument, NULL, 'd' },
    586	    { "help",   no_argument,       NULL, 'h' },
    587	    { "mmap",   no_argument,       NULL, 'm' },
    588	    { "read",   no_argument,       NULL, 'r' },
    589	    { "userp",  no_argument,       NULL, 'u' },
    590	    { "output", no_argument,       NULL, 'o' },
    591	    { "format", no_argument,       NULL, 'f' },
    592	    { "count",  required_argument, NULL, 'c' },
    593	    { 0, 0, 0, 0 }
    594    };
    595
    596    int main(int argc, char **argv)
    597    {
    598	    dev_name = "/dev/video0";
    599
    600	    for (;;) {
    601		    int idx;
    602		    int c;
    603
    604		    c = getopt_long(argc, argv,
    605				    short_options, long_options, &idx);
    606
    607		    if (-1 == c)
    608			    break;
    609
    610		    switch (c) {
    611		    case 0: /* getopt_long() flag */
    612			    break;
    613
    614		    case 'd':
    615			    dev_name = optarg;
    616			    break;
    617
    618		    case 'h':
    619			    usage(stdout, argc, argv);
    620			    exit(EXIT_SUCCESS);
    621
    622		    case 'm':
    623			    io = IO_METHOD_MMAP;
    624			    break;
    625
    626		    case 'r':
    627			    io = IO_METHOD_READ;
    628			    break;
    629
    630		    case 'u':
    631			    io = IO_METHOD_USERPTR;
    632			    break;
    633
    634		    case 'o':
    635			    out_buf++;
    636			    break;
    637
    638		    case 'f':
    639			    force_format++;
    640			    break;
    641
    642		    case 'c':
    643			    errno = 0;
    644			    frame_count = strtol(optarg, NULL, 0);
    645			    if (errno)
    646				    errno_exit(optarg);
    647			    break;
    648
    649		    default:
    650			    usage(stderr, argc, argv);
    651			    exit(EXIT_FAILURE);
    652		    }
    653	    }
    654
    655	    open_device();
    656	    init_device();
    657	    start_capturing();
    658	    mainloop();
    659	    stop_capturing();
    660	    uninit_device();
    661	    close_device();
    662	    fprintf(stderr, "\n");
    663	    return 0;
    664    }