summaryrefslogtreecommitdiff
path: root/drivers/media/v4l2-core/v4l2-isp.c
blob: 29831f7032e9428a5f4659580b07c042dbfcef27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Video4Linux2 generic ISP parameters and statistics support
 *
 * Copyright (C) 2025 Ideas On Board Oy
 * Author: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
 */

#include <media/v4l2-isp.h>

#include <linux/bitops.h>
#include <linux/device.h>

#include <media/videobuf2-core.h>

int v4l2_isp_params_validate_buffer_size(struct device *dev,
					 struct vb2_buffer *vb,
					 size_t max_size)
{
	size_t header_size = offsetof(struct v4l2_isp_params_buffer, data);
	size_t payload_size = vb2_get_plane_payload(vb, 0);

	/* Payload size can't be greater than the destination buffer size */
	if (payload_size > max_size) {
		dev_dbg(dev, "Payload size is too large: %zu\n", payload_size);
		return -EINVAL;
	}

	/* Payload size can't be smaller than the header size */
	if (payload_size < header_size) {
		dev_dbg(dev, "Payload size is too small: %zu\n", payload_size);
		return -EINVAL;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(v4l2_isp_params_validate_buffer_size);

int v4l2_isp_params_validate_buffer(struct device *dev, struct vb2_buffer *vb,
				    const struct v4l2_isp_params_buffer *buffer,
				    const struct v4l2_isp_params_block_type_info *type_info,
				    size_t num_block_types)
{
	size_t header_size = offsetof(struct v4l2_isp_params_buffer, data);
	size_t payload_size = vb2_get_plane_payload(vb, 0);
	size_t block_offset = 0;
	size_t buffer_size;

	/*
	 * Currently only the first version of the V4L2 ISP parameters format is
	 * supported. We accept both V0 and V1 to support existing drivers
	 * compatible with V4L2 ISP that use either 0 or 1 as their "first
	 * version" identifiers.
	 */
	if (buffer->version != V4L2_ISP_PARAMS_VERSION_V0 &&
	    buffer->version != V4L2_ISP_PARAMS_VERSION_V1) {
		dev_dbg(dev,
			"Unsupported V4L2 ISP parameters format version: %u\n",
			buffer->version);
		return -EINVAL;
	}

	/* Validate the size reported in the header */
	buffer_size = header_size + buffer->data_size;
	if (buffer_size != payload_size) {
		dev_dbg(dev, "Data size %zu and payload size %zu are different\n",
			buffer_size, payload_size);
		return -EINVAL;
	}

	/* Walk the list of ISP configuration blocks and validate them. */
	buffer_size = buffer->data_size;
	while (buffer_size >= sizeof(struct v4l2_isp_params_block_header)) {
		const struct v4l2_isp_params_block_type_info *info;
		const struct v4l2_isp_params_block_header *block;

		block = (const struct v4l2_isp_params_block_header *)
			(buffer->data + block_offset);

		if (block->type >= num_block_types) {
			dev_dbg(dev,
				"Invalid block type %u at offset %zu\n",
				block->type, block_offset);
			return -EINVAL;
		}

		if (block->size > buffer_size) {
			dev_dbg(dev, "Premature end of parameters data\n");
			return -EINVAL;
		}

		/* It's invalid to specify both ENABLE and DISABLE. */
		if ((block->flags & (V4L2_ISP_PARAMS_FL_BLOCK_ENABLE |
				     V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)) ==
		     (V4L2_ISP_PARAMS_FL_BLOCK_ENABLE |
		     V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)) {
			dev_dbg(dev, "Invalid block flags %x at offset %zu\n",
				block->flags, block_offset);
			return -EINVAL;
		}

		/*
		 * Match the block reported size against the type info provided
		 * one, but allow the block to only contain the header in
		 * case it is going to be disabled.
		 */
		info = &type_info[block->type];
		if (block->size != info->size &&
		    (!(block->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) ||
		    block->size != sizeof(*block))) {
			dev_dbg(dev,
				"Invalid block size %u (expected %zu) at offset %zu\n",
				block->size, info->size, block_offset);
			return -EINVAL;
		}

		block_offset += block->size;
		buffer_size -= block->size;
	}

	if (buffer_size) {
		dev_dbg(dev, "Unexpected data after the parameters buffer end\n");
		return -EINVAL;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(v4l2_isp_params_validate_buffer);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jacopo Mondi <jacopo.mondi@ideasonboard.com");
MODULE_DESCRIPTION("V4L2 generic ISP parameters and statistics helpers");