diff mbox series

[kms-tests,6/6] crc: Add a utility to compute the CRC of a frame

Message ID 20200807232119.28854-7-laurent.pinchart@ideasonboard.com (mailing list archive)
State New
Delegated to: Kieran Bingham
Headers show
Series Improve CRC (DISCOM) test | expand

Commit Message

Laurent Pinchart Aug. 7, 2020, 11:21 p.m. UTC
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
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index 1f0da15546b8..e9c0edb785e7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@ 
 # SPDX-License-Identifier: CC0-1.0
 
-SUBDIRS=tests
+SUBDIRS=crc tests
 
 recursive=all clean install
 
diff --git a/crc/Makefile b/crc/Makefile
new file mode 100644
index 000000000000..0922163da31b
--- /dev/null
+++ b/crc/Makefile
@@ -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)/
diff --git a/crc/gen-crc.py b/crc/gen-crc.py
new file mode 100755
index 000000000000..10c5776d071d
--- /dev/null
+++ b/crc/gen-crc.py
@@ -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()
diff --git a/crc/main.c b/crc/main.c
new file mode 100644
index 000000000000..b63421902f18
--- /dev/null
+++ b/crc/main.c
@@ -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;
+}