From 322821a1381f81c4dd480d065bec13803c7e69dc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 8 Aug 2020 01:43:12 +0300 Subject: crc: Add a utility to compute the CRC of a frame The discom-crc utility computes the CRC of a frame using the same algorithm as the DISCOM hardware block. This is useful to precompute CRC values and then compare them with the hardware-generated values. The utility computes the CRC on data stored in a file passed as a command line argument. It supports two optional arguments, --crop and --size, to specify the crop rectangle and the image size. The size only needs to be specified if a crop rectangle is set, it is deduced from the file size otherwise. Signed-off-by: Laurent Pinchart --- crc/main.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 crc/main.c (limited to 'crc/main.c') diff --git a/crc/main.c b/crc/main.c new file mode 100644 index 0000000..b634219 --- /dev/null +++ b/crc/main.c @@ -0,0 +1,271 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2020 Laurent Pinchart */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crc.c" + +struct image_rect { + int left; + int top; + unsigned int width; + unsigned int height; +}; + +struct image_size { + unsigned int width; + unsigned int height; +}; + +struct options { + const char *filename; + struct image_rect crop; + struct image_size size; +}; + +/* ----------------------------------------------------------------------------- + * Miscellaneous helpers + */ + +static bool rect_is_empty(const struct image_rect *rect) +{ + return !rect->width || !rect->height; +} + +static bool rect_is_null(const struct image_rect *rect) +{ + return !rect->left && !rect->top && !rect->width && !rect->height; +} + +static bool size_is_null(const struct image_size *size) +{ + return !size->width && !size->height; +} + +static int readall(int fd, void *buf, size_t count) +{ + int ret; + + do { + ret = read(fd, buf, count); + if (ret == -1) + return -errno; + if (ret == 0) + return -ENODATA; + + count -= ret; + buf += ret; + } while (count); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Usage and argument parsing + */ + +static void usage(const char *argv0) +{ + printf("Usage: %s [options] \n\n", argv0); + printf("Calculate the R-Car DISCOM CRC for the image stored in \n\n"); + printf("Supported options:\n"); + printf("-c, --crop (X,Y)/WxH Crop the input image (needs --size)\n"); + printf("-s, --size WxH Input image size\n"); +} + +static struct option opts[] = { + {"crop", 1, 0, 'c'}, + {"size", 1, 0, 's'}, + {0, 0, 0, 0} +}; + +static int parse_args(struct options *options, int argc, char *argv[]) +{ + int ret; + int c; + + if (argc < 2) { + usage(argv[0]); + return 1; + } + + memset(options, 0, sizeof(*options)); + + opterr = 0; + while ((c = getopt_long(argc, argv, "c:s:", opts, NULL)) != -1) { + int count; + + switch (c) { + case 'c': + ret = sscanf(optarg, "(%d,%d)/%ux%u%n", + &options->crop.left, &options->crop.top, + &options->crop.width, &options->crop.height, + &count); + if (ret != 4 || count != (int)strlen(optarg)) { + printf("Invalid crop value '%s'\n", optarg); + return 1; + } + break; + + case 's': + ret = sscanf(optarg, "%ux%u%n", + &options->size.width, &options->size.height, + &count); + if (ret != 2 || count != (int)strlen(optarg)) { + printf("Invalid size value '%s'\n", optarg); + return 1; + } + break; + + default: + printf("Invalid option -%c\n", c); + printf("Run %s -h for help.\n", argv[0]); + return 1; + } + } + + if (optind != argc - 1) { + usage(argv[0]); + return 1; + } + + options->filename = argv[optind]; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Main + */ + +int main(int argc, char *argv[]) +{ + struct options options; + void *image = NULL; + uint32_t crc; + off_t offset; + off_t size; + int fd = -1; + int ret; + + /* Parse and validate options. */ + ret = parse_args(&options, argc, argv); + if (ret) + return ret; + + if (!rect_is_null(&options.crop)) { + if (size_is_null(&options.size)) { + printf("--crop requires --size\n"); + goto error; + } + + if (rect_is_empty(&options.crop)) { + printf("Crop rectangle is empty\n"); + goto error; + } + + if (options.crop.left < 0 || options.crop.top < 0 || + options.crop.left + options.crop.width > options.size.width || + options.crop.top + options.crop.height > options.size.height) { + printf("Crop rectangle out of image bounds\n"); + goto error; + } + } + + /* Open the file and determine its size. */ + fd = open(options.filename, O_RDONLY); + if (fd == -1) { + printf("Failed to open '%s': %s\n", options.filename, + strerror(errno)); + goto error; + } + + size = lseek(fd, 0, SEEK_END); + if (size == -1) { + printf("Failed to determine file size: %s\n", strerror(errno)); + goto error; + } + + if (!size_is_null(&options.size) && + options.size.width * options.size.height * 4 != size) { + printf("Image size %ux%u doesn't match file size %jd\n", + options.size.width, options.size.height, (intmax_t)size); + goto error; + } + + /* Read the image data. */ + if (!rect_is_null(&options.crop)) + size = options.crop.width * options.crop.height * 4; + + image = malloc(size); + if (!image) { + printf("Unable to allocate memory for image data\n"); + goto error; + } + + offset = (options.crop.top * options.size.width + options.crop.left) * 4; + lseek(fd, offset, SEEK_SET); + + if (!options.crop.width || options.crop.width == options.size.width) { + /* + * When the crop rectangle width spans the whole image, read it + * in one go. + */ + ret = readall(fd, image, size); + if (ret < 0) { + printf("Unable to read image: %s\n", strerror(errno)); + goto error; + } + } else { + /* Otherwise, read line by line. */ + void *line = image; + unsigned int y; + + offset = (options.size.width - options.crop.width) * 4; + + for (y = 0; y < options.crop.height; ++y) { + ret = readall(fd, line, options.crop.width * 4); + if (ret < 0) { + printf("Unable to read line %u: %s\n", y, + strerror(errno)); + goto error; + } + + lseek(fd, offset, SEEK_CUR); + line += options.crop.width * 4; + } + } + + close(fd); + fd = -1; + + /* + * Compute the CRC. The generate CRC code XORs the initial value with + * the final XOR value, so we need to pass 0 to get the desired + * 0xffffffff initial value. + */ + crc = calculate_crc(image, size, 0); + + free(image); + image = NULL; + + printf("0x%08x\n", crc); + + return 0; + +error: + free(image); + if (fd != -1) + close(fd); + return 1; +} -- cgit v1.2.3