@@ -1,6 +1,6 @@
# SPDX-License-Identifier: CC0-1.0
-SUBDIRS=tests
+SUBDIRS=crc tests
recursive=all clean install
new file mode 100644
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: CC0-1.0
+
+CROSS_COMPILE ?=
+
+CC := $(CROSS_COMPILE)gcc
+CFLAGS ?= -O2 -W -Wall -Wno-unused-parameter -Iinclude
+LDFLAGS ?=
+LIBS :=
+
+OUTPUT := discom-crc
+OBJECTS := main.o
+
+%.o : %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+all: $(OUTPUT)
+
+$(OUTPUT): $(OBJECTS)
+ $(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+crc.c : gen-crc.py
+ ./$< $@
+
+main.o : crc.c
+
+clean:
+ -rm -f *.o
+ -rm -f crc.c
+ -rm -f $(OUTPUT)
+
+install:
+ cp $(OUTPUT) $(INSTALL_DIR)/
new file mode 100755
@@ -0,0 +1,14 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2020 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+
+import crcmod
+import sys
+
+# The initial value is ignored, it must instead be passed to the generated C
+# function.
+crc = crcmod.Crc(0x104c11db7, 0xffffffff, True, 0xffffffff)
+
+f = open(sys.argv[1], 'w')
+crc.generateCode('calculate_crc', f, 'uint8_t', 'uint32_t')
+f.close()
new file mode 100644
@@ -0,0 +1,271 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2020 Laurent Pinchart <laurent.pinchart@ideasonboard.com> */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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] <infile>\n\n", argv0);
+ printf("Calculate the R-Car DISCOM CRC for the image stored in <infile>\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;
+}
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 <laurent.pinchart@ideasonboard.com> --- Makefile | 2 +- crc/Makefile | 32 ++++++ crc/gen-crc.py | 14 +++ crc/main.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 crc/Makefile create mode 100755 crc/gen-crc.py create mode 100644 crc/main.c