18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * V4L2 JPEG header parser helpers.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Pengutronix, Philipp Zabel <kernel@pengutronix.de>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * For reference, see JPEG ITU-T.81 (ISO/IEC 10918-1) [1]
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * [1] https://www.w3.org/Graphics/JPEG/itu-t81.pdf
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/types.h>
178c2ecf20Sopenharmony_ci#include <media/v4l2-jpeg.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("V4L2 JPEG header parser helpers");
208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Philipp Zabel <kernel@pengutronix.de>");
218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* Table B.1 - Marker code assignments */
248c2ecf20Sopenharmony_ci#define SOF0	0xffc0	/* start of frame */
258c2ecf20Sopenharmony_ci#define SOF1	0xffc1
268c2ecf20Sopenharmony_ci#define SOF2	0xffc2
278c2ecf20Sopenharmony_ci#define SOF3	0xffc3
288c2ecf20Sopenharmony_ci#define SOF5	0xffc5
298c2ecf20Sopenharmony_ci#define SOF7	0xffc7
308c2ecf20Sopenharmony_ci#define JPG	0xffc8	/* extensions */
318c2ecf20Sopenharmony_ci#define SOF9	0xffc9
328c2ecf20Sopenharmony_ci#define SOF11	0xffcb
338c2ecf20Sopenharmony_ci#define SOF13	0xffcd
348c2ecf20Sopenharmony_ci#define SOF15	0xffcf
358c2ecf20Sopenharmony_ci#define DHT	0xffc4	/* huffman table */
368c2ecf20Sopenharmony_ci#define DAC	0xffcc	/* arithmetic coding conditioning */
378c2ecf20Sopenharmony_ci#define RST0	0xffd0	/* restart */
388c2ecf20Sopenharmony_ci#define RST7	0xffd7
398c2ecf20Sopenharmony_ci#define SOI	0xffd8	/* start of image */
408c2ecf20Sopenharmony_ci#define EOI	0xffd9	/* end of image */
418c2ecf20Sopenharmony_ci#define SOS	0xffda	/* start of stream */
428c2ecf20Sopenharmony_ci#define DQT	0xffdb	/* quantization table */
438c2ecf20Sopenharmony_ci#define DNL	0xffdc	/* number of lines */
448c2ecf20Sopenharmony_ci#define DRI	0xffdd	/* restart interval */
458c2ecf20Sopenharmony_ci#define DHP	0xffde	/* hierarchical progression */
468c2ecf20Sopenharmony_ci#define EXP	0xffdf	/* expand reference */
478c2ecf20Sopenharmony_ci#define APP0	0xffe0	/* application data */
488c2ecf20Sopenharmony_ci#define APP15	0xffef
498c2ecf20Sopenharmony_ci#define JPG0	0xfff0	/* extensions */
508c2ecf20Sopenharmony_ci#define JPG13	0xfffd
518c2ecf20Sopenharmony_ci#define COM	0xfffe	/* comment */
528c2ecf20Sopenharmony_ci#define TEM	0xff01	/* temporary */
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/**
558c2ecf20Sopenharmony_ci * struct jpeg_stream - JPEG byte stream
568c2ecf20Sopenharmony_ci * @curr: current position in stream
578c2ecf20Sopenharmony_ci * @end: end position, after last byte
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_cistruct jpeg_stream {
608c2ecf20Sopenharmony_ci	u8 *curr;
618c2ecf20Sopenharmony_ci	u8 *end;
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* returns a value that fits into u8, or negative error */
658c2ecf20Sopenharmony_cistatic int jpeg_get_byte(struct jpeg_stream *stream)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	if (stream->curr >= stream->end)
688c2ecf20Sopenharmony_ci		return -EINVAL;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return *stream->curr++;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/* returns a value that fits into u16, or negative error */
748c2ecf20Sopenharmony_cistatic int jpeg_get_word_be(struct jpeg_stream *stream)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	u16 word;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (stream->curr + sizeof(__be16) > stream->end)
798c2ecf20Sopenharmony_ci		return -EINVAL;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	word = get_unaligned_be16(stream->curr);
828c2ecf20Sopenharmony_ci	stream->curr += sizeof(__be16);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return word;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int jpeg_skip(struct jpeg_stream *stream, size_t len)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	if (stream->curr + len > stream->end)
908c2ecf20Sopenharmony_ci		return -EINVAL;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	stream->curr += len;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	return 0;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int jpeg_next_marker(struct jpeg_stream *stream)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	int byte;
1008c2ecf20Sopenharmony_ci	u16 marker = 0;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	while ((byte = jpeg_get_byte(stream)) >= 0) {
1038c2ecf20Sopenharmony_ci		marker = (marker << 8) | byte;
1048c2ecf20Sopenharmony_ci		/* skip stuffing bytes and REServed markers */
1058c2ecf20Sopenharmony_ci		if (marker == TEM || (marker > 0xffbf && marker < 0xffff))
1068c2ecf20Sopenharmony_ci			return marker;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return byte;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/* this does not advance the current position in the stream */
1138c2ecf20Sopenharmony_cistatic int jpeg_reference_segment(struct jpeg_stream *stream,
1148c2ecf20Sopenharmony_ci				  struct v4l2_jpeg_reference *segment)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	u16 len;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (stream->curr + sizeof(__be16) > stream->end)
1198c2ecf20Sopenharmony_ci		return -EINVAL;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	len = get_unaligned_be16(stream->curr);
1228c2ecf20Sopenharmony_ci	if (stream->curr + len > stream->end)
1238c2ecf20Sopenharmony_ci		return -EINVAL;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	segment->start = stream->curr;
1268c2ecf20Sopenharmony_ci	segment->length = len;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return 0;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int v4l2_jpeg_decode_subsampling(u8 nf, u8 h_v)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	if (nf == 1)
1348c2ecf20Sopenharmony_ci		return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* no chroma subsampling for 4-component images */
1378c2ecf20Sopenharmony_ci	if (nf == 4 && h_v != 0x11)
1388c2ecf20Sopenharmony_ci		return -EINVAL;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	switch (h_v) {
1418c2ecf20Sopenharmony_ci	case 0x11:
1428c2ecf20Sopenharmony_ci		return V4L2_JPEG_CHROMA_SUBSAMPLING_444;
1438c2ecf20Sopenharmony_ci	case 0x21:
1448c2ecf20Sopenharmony_ci		return V4L2_JPEG_CHROMA_SUBSAMPLING_422;
1458c2ecf20Sopenharmony_ci	case 0x22:
1468c2ecf20Sopenharmony_ci		return V4L2_JPEG_CHROMA_SUBSAMPLING_420;
1478c2ecf20Sopenharmony_ci	case 0x41:
1488c2ecf20Sopenharmony_ci		return V4L2_JPEG_CHROMA_SUBSAMPLING_411;
1498c2ecf20Sopenharmony_ci	default:
1508c2ecf20Sopenharmony_ci		return -EINVAL;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int jpeg_parse_frame_header(struct jpeg_stream *stream, u16 sof_marker,
1558c2ecf20Sopenharmony_ci				   struct v4l2_jpeg_frame_header *frame_header)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	int len = jpeg_get_word_be(stream);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (len < 0)
1608c2ecf20Sopenharmony_ci		return len;
1618c2ecf20Sopenharmony_ci	/* Lf = 8 + 3 * Nf, Nf >= 1 */
1628c2ecf20Sopenharmony_ci	if (len < 8 + 3)
1638c2ecf20Sopenharmony_ci		return -EINVAL;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (frame_header) {
1668c2ecf20Sopenharmony_ci		/* Table B.2 - Frame header parameter sizes and values */
1678c2ecf20Sopenharmony_ci		int p, y, x, nf;
1688c2ecf20Sopenharmony_ci		int i;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		p = jpeg_get_byte(stream);
1718c2ecf20Sopenharmony_ci		if (p < 0)
1728c2ecf20Sopenharmony_ci			return p;
1738c2ecf20Sopenharmony_ci		/*
1748c2ecf20Sopenharmony_ci		 * Baseline DCT only supports 8-bit precision.
1758c2ecf20Sopenharmony_ci		 * Extended sequential DCT also supports 12-bit precision.
1768c2ecf20Sopenharmony_ci		 */
1778c2ecf20Sopenharmony_ci		if (p != 8 && (p != 12 || sof_marker != SOF1))
1788c2ecf20Sopenharmony_ci			return -EINVAL;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci		y = jpeg_get_word_be(stream);
1818c2ecf20Sopenharmony_ci		if (y < 0)
1828c2ecf20Sopenharmony_ci			return y;
1838c2ecf20Sopenharmony_ci		if (y == 0)
1848c2ecf20Sopenharmony_ci			return -EINVAL;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		x = jpeg_get_word_be(stream);
1878c2ecf20Sopenharmony_ci		if (x < 0)
1888c2ecf20Sopenharmony_ci			return x;
1898c2ecf20Sopenharmony_ci		if (x == 0)
1908c2ecf20Sopenharmony_ci			return -EINVAL;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		nf = jpeg_get_byte(stream);
1938c2ecf20Sopenharmony_ci		if (nf < 0)
1948c2ecf20Sopenharmony_ci			return nf;
1958c2ecf20Sopenharmony_ci		/*
1968c2ecf20Sopenharmony_ci		 * The spec allows 1 <= Nf <= 255, but we only support up to 4
1978c2ecf20Sopenharmony_ci		 * components.
1988c2ecf20Sopenharmony_ci		 */
1998c2ecf20Sopenharmony_ci		if (nf < 1 || nf > V4L2_JPEG_MAX_COMPONENTS)
2008c2ecf20Sopenharmony_ci			return -EINVAL;
2018c2ecf20Sopenharmony_ci		if (len != 8 + 3 * nf)
2028c2ecf20Sopenharmony_ci			return -EINVAL;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		frame_header->precision = p;
2058c2ecf20Sopenharmony_ci		frame_header->height = y;
2068c2ecf20Sopenharmony_ci		frame_header->width = x;
2078c2ecf20Sopenharmony_ci		frame_header->num_components = nf;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		for (i = 0; i < nf; i++) {
2108c2ecf20Sopenharmony_ci			struct v4l2_jpeg_frame_component_spec *component;
2118c2ecf20Sopenharmony_ci			int c, h_v, tq;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci			c = jpeg_get_byte(stream);
2148c2ecf20Sopenharmony_ci			if (c < 0)
2158c2ecf20Sopenharmony_ci				return c;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci			h_v = jpeg_get_byte(stream);
2188c2ecf20Sopenharmony_ci			if (h_v < 0)
2198c2ecf20Sopenharmony_ci				return h_v;
2208c2ecf20Sopenharmony_ci			if (i == 0) {
2218c2ecf20Sopenharmony_ci				int subs;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci				subs = v4l2_jpeg_decode_subsampling(nf, h_v);
2248c2ecf20Sopenharmony_ci				if (subs < 0)
2258c2ecf20Sopenharmony_ci					return subs;
2268c2ecf20Sopenharmony_ci				frame_header->subsampling = subs;
2278c2ecf20Sopenharmony_ci			} else if (h_v != 0x11) {
2288c2ecf20Sopenharmony_ci				/* all chroma sampling factors must be 1 */
2298c2ecf20Sopenharmony_ci				return -EINVAL;
2308c2ecf20Sopenharmony_ci			}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci			tq = jpeg_get_byte(stream);
2338c2ecf20Sopenharmony_ci			if (tq < 0)
2348c2ecf20Sopenharmony_ci				return tq;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci			component = &frame_header->component[i];
2378c2ecf20Sopenharmony_ci			component->component_identifier = c;
2388c2ecf20Sopenharmony_ci			component->horizontal_sampling_factor =
2398c2ecf20Sopenharmony_ci				(h_v >> 4) & 0xf;
2408c2ecf20Sopenharmony_ci			component->vertical_sampling_factor = h_v & 0xf;
2418c2ecf20Sopenharmony_ci			component->quantization_table_selector = tq;
2428c2ecf20Sopenharmony_ci		}
2438c2ecf20Sopenharmony_ci	} else {
2448c2ecf20Sopenharmony_ci		return jpeg_skip(stream, len - 2);
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int jpeg_parse_scan_header(struct jpeg_stream *stream,
2518c2ecf20Sopenharmony_ci				  struct v4l2_jpeg_scan_header *scan_header)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	size_t skip;
2548c2ecf20Sopenharmony_ci	int len = jpeg_get_word_be(stream);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (len < 0)
2578c2ecf20Sopenharmony_ci		return len;
2588c2ecf20Sopenharmony_ci	/* Ls = 8 + 3 * Ns, Ns >= 1 */
2598c2ecf20Sopenharmony_ci	if (len < 6 + 2)
2608c2ecf20Sopenharmony_ci		return -EINVAL;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (scan_header) {
2638c2ecf20Sopenharmony_ci		int ns;
2648c2ecf20Sopenharmony_ci		int i;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		ns = jpeg_get_byte(stream);
2678c2ecf20Sopenharmony_ci		if (ns < 0)
2688c2ecf20Sopenharmony_ci			return ns;
2698c2ecf20Sopenharmony_ci		if (ns < 1 || ns > 4 || len != 6 + 2 * ns)
2708c2ecf20Sopenharmony_ci			return -EINVAL;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		scan_header->num_components = ns;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		for (i = 0; i < ns; i++) {
2758c2ecf20Sopenharmony_ci			struct v4l2_jpeg_scan_component_spec *component;
2768c2ecf20Sopenharmony_ci			int cs, td_ta;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci			cs = jpeg_get_byte(stream);
2798c2ecf20Sopenharmony_ci			if (cs < 0)
2808c2ecf20Sopenharmony_ci				return cs;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci			td_ta = jpeg_get_byte(stream);
2838c2ecf20Sopenharmony_ci			if (td_ta < 0)
2848c2ecf20Sopenharmony_ci				return td_ta;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci			component = &scan_header->component[i];
2878c2ecf20Sopenharmony_ci			component->component_selector = cs;
2888c2ecf20Sopenharmony_ci			component->dc_entropy_coding_table_selector =
2898c2ecf20Sopenharmony_ci				(td_ta >> 4) & 0xf;
2908c2ecf20Sopenharmony_ci			component->ac_entropy_coding_table_selector =
2918c2ecf20Sopenharmony_ci				td_ta & 0xf;
2928c2ecf20Sopenharmony_ci		}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		skip = 3; /* skip Ss, Se, Ah, and Al */
2958c2ecf20Sopenharmony_ci	} else {
2968c2ecf20Sopenharmony_ci		skip = len - 2;
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return jpeg_skip(stream, skip);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/* B.2.4.1 Quantization table-specification syntax */
3038c2ecf20Sopenharmony_cistatic int jpeg_parse_quantization_tables(struct jpeg_stream *stream,
3048c2ecf20Sopenharmony_ci					  u8 precision,
3058c2ecf20Sopenharmony_ci					  struct v4l2_jpeg_reference *tables)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	int len = jpeg_get_word_be(stream);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (len < 0)
3108c2ecf20Sopenharmony_ci		return len;
3118c2ecf20Sopenharmony_ci	/* Lq = 2 + n * 65 (for baseline DCT), n >= 1 */
3128c2ecf20Sopenharmony_ci	if (len < 2 + 65)
3138c2ecf20Sopenharmony_ci		return -EINVAL;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	len -= 2;
3168c2ecf20Sopenharmony_ci	while (len >= 65) {
3178c2ecf20Sopenharmony_ci		u8 pq, tq, *qk;
3188c2ecf20Sopenharmony_ci		int ret;
3198c2ecf20Sopenharmony_ci		int pq_tq = jpeg_get_byte(stream);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci		if (pq_tq < 0)
3228c2ecf20Sopenharmony_ci			return pq_tq;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		/* quantization table element precision */
3258c2ecf20Sopenharmony_ci		pq = (pq_tq >> 4) & 0xf;
3268c2ecf20Sopenharmony_ci		/*
3278c2ecf20Sopenharmony_ci		 * Only 8-bit Qk values for 8-bit sample precision. Extended
3288c2ecf20Sopenharmony_ci		 * sequential DCT with 12-bit sample precision also supports
3298c2ecf20Sopenharmony_ci		 * 16-bit Qk values.
3308c2ecf20Sopenharmony_ci		 */
3318c2ecf20Sopenharmony_ci		if (pq != 0 && (pq != 1 || precision != 12))
3328c2ecf20Sopenharmony_ci			return -EINVAL;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci		/* quantization table destination identifier */
3358c2ecf20Sopenharmony_ci		tq = pq_tq & 0xf;
3368c2ecf20Sopenharmony_ci		if (tq > 3)
3378c2ecf20Sopenharmony_ci			return -EINVAL;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		/* quantization table element */
3408c2ecf20Sopenharmony_ci		qk = stream->curr;
3418c2ecf20Sopenharmony_ci		ret = jpeg_skip(stream, pq ? 128 : 64);
3428c2ecf20Sopenharmony_ci		if (ret < 0)
3438c2ecf20Sopenharmony_ci			return -EINVAL;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci		if (tables) {
3468c2ecf20Sopenharmony_ci			tables[tq].start = qk;
3478c2ecf20Sopenharmony_ci			tables[tq].length = pq ? 128 : 64;
3488c2ecf20Sopenharmony_ci		}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		len -= pq ? 129 : 65;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	return 0;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci/* B.2.4.2 Huffman table-specification syntax */
3578c2ecf20Sopenharmony_cistatic int jpeg_parse_huffman_tables(struct jpeg_stream *stream,
3588c2ecf20Sopenharmony_ci				     struct v4l2_jpeg_reference *tables)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	int mt;
3618c2ecf20Sopenharmony_ci	int len = jpeg_get_word_be(stream);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	if (len < 0)
3648c2ecf20Sopenharmony_ci		return len;
3658c2ecf20Sopenharmony_ci	/* Table B.5 - Huffman table specification parameter sizes and values */
3668c2ecf20Sopenharmony_ci	if (len < 2 + 17)
3678c2ecf20Sopenharmony_ci		return -EINVAL;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	for (len -= 2; len >= 17; len -= 17 + mt) {
3708c2ecf20Sopenharmony_ci		u8 tc, th, *table;
3718c2ecf20Sopenharmony_ci		int tc_th = jpeg_get_byte(stream);
3728c2ecf20Sopenharmony_ci		int i, ret;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci		if (tc_th < 0)
3758c2ecf20Sopenharmony_ci			return tc_th;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		/* table class - 0 = DC, 1 = AC */
3788c2ecf20Sopenharmony_ci		tc = (tc_th >> 4) & 0xf;
3798c2ecf20Sopenharmony_ci		if (tc > 1)
3808c2ecf20Sopenharmony_ci			return -EINVAL;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci		/* huffman table destination identifier */
3838c2ecf20Sopenharmony_ci		th = tc_th & 0xf;
3848c2ecf20Sopenharmony_ci		/* only two Huffman tables for baseline DCT */
3858c2ecf20Sopenharmony_ci		if (th > 1)
3868c2ecf20Sopenharmony_ci			return -EINVAL;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		/* BITS - number of Huffman codes with length i */
3898c2ecf20Sopenharmony_ci		table = stream->curr;
3908c2ecf20Sopenharmony_ci		mt = 0;
3918c2ecf20Sopenharmony_ci		for (i = 0; i < 16; i++) {
3928c2ecf20Sopenharmony_ci			int li;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci			li = jpeg_get_byte(stream);
3958c2ecf20Sopenharmony_ci			if (li < 0)
3968c2ecf20Sopenharmony_ci				return li;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci			mt += li;
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci		/* HUFFVAL - values associated with each Huffman code */
4018c2ecf20Sopenharmony_ci		ret = jpeg_skip(stream, mt);
4028c2ecf20Sopenharmony_ci		if (ret < 0)
4038c2ecf20Sopenharmony_ci			return ret;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		if (tables) {
4068c2ecf20Sopenharmony_ci			tables[(tc << 1) | th].start = table;
4078c2ecf20Sopenharmony_ci			tables[(tc << 1) | th].length = stream->curr - table;
4088c2ecf20Sopenharmony_ci		}
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return jpeg_skip(stream, len - 2);
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci/* B.2.4.4 Restart interval definition syntax */
4158c2ecf20Sopenharmony_cistatic int jpeg_parse_restart_interval(struct jpeg_stream *stream,
4168c2ecf20Sopenharmony_ci				       u16 *restart_interval)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	int len = jpeg_get_word_be(stream);
4198c2ecf20Sopenharmony_ci	int ri;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	if (len < 0)
4228c2ecf20Sopenharmony_ci		return len;
4238c2ecf20Sopenharmony_ci	if (len != 4)
4248c2ecf20Sopenharmony_ci		return -EINVAL;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	ri = jpeg_get_word_be(stream);
4278c2ecf20Sopenharmony_ci	if (ri < 0)
4288c2ecf20Sopenharmony_ci		return ri;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	*restart_interval = ri;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return 0;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic int jpeg_skip_segment(struct jpeg_stream *stream)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	int len = jpeg_get_word_be(stream);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (len < 0)
4408c2ecf20Sopenharmony_ci		return len;
4418c2ecf20Sopenharmony_ci	if (len < 2)
4428c2ecf20Sopenharmony_ci		return -EINVAL;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	return jpeg_skip(stream, len - 2);
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci/**
4488c2ecf20Sopenharmony_ci * jpeg_parse_header - locate marker segments and optionally parse headers
4498c2ecf20Sopenharmony_ci * @buf: address of the JPEG buffer, should start with a SOI marker
4508c2ecf20Sopenharmony_ci * @len: length of the JPEG buffer
4518c2ecf20Sopenharmony_ci * @out: returns marker segment positions and optionally parsed headers
4528c2ecf20Sopenharmony_ci *
4538c2ecf20Sopenharmony_ci * The out->scan_header pointer must be initialized to NULL or point to a valid
4548c2ecf20Sopenharmony_ci * v4l2_jpeg_scan_header structure. The out->huffman_tables and
4558c2ecf20Sopenharmony_ci * out->quantization_tables pointers must be initialized to NULL or point to a
4568c2ecf20Sopenharmony_ci * valid array of 4 v4l2_jpeg_reference structures each.
4578c2ecf20Sopenharmony_ci *
4588c2ecf20Sopenharmony_ci * Returns 0 or negative error if parsing failed.
4598c2ecf20Sopenharmony_ci */
4608c2ecf20Sopenharmony_ciint v4l2_jpeg_parse_header(void *buf, size_t len, struct v4l2_jpeg_header *out)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	struct jpeg_stream stream;
4638c2ecf20Sopenharmony_ci	int marker;
4648c2ecf20Sopenharmony_ci	int ret = 0;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	stream.curr = buf;
4678c2ecf20Sopenharmony_ci	stream.end = stream.curr + len;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	out->num_dht = 0;
4708c2ecf20Sopenharmony_ci	out->num_dqt = 0;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	/* the first marker must be SOI */
4738c2ecf20Sopenharmony_ci	marker = jpeg_next_marker(&stream);
4748c2ecf20Sopenharmony_ci	if (marker < 0)
4758c2ecf20Sopenharmony_ci		return marker;
4768c2ecf20Sopenharmony_ci	if (marker != SOI)
4778c2ecf20Sopenharmony_ci		return -EINVAL;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	/* loop through marker segments */
4808c2ecf20Sopenharmony_ci	while ((marker = jpeg_next_marker(&stream)) >= 0) {
4818c2ecf20Sopenharmony_ci		switch (marker) {
4828c2ecf20Sopenharmony_ci		/* baseline DCT, extended sequential DCT */
4838c2ecf20Sopenharmony_ci		case SOF0 ... SOF1:
4848c2ecf20Sopenharmony_ci			ret = jpeg_reference_segment(&stream, &out->sof);
4858c2ecf20Sopenharmony_ci			if (ret < 0)
4868c2ecf20Sopenharmony_ci				return ret;
4878c2ecf20Sopenharmony_ci			ret = jpeg_parse_frame_header(&stream, marker,
4888c2ecf20Sopenharmony_ci						      &out->frame);
4898c2ecf20Sopenharmony_ci			break;
4908c2ecf20Sopenharmony_ci		/* progressive, lossless */
4918c2ecf20Sopenharmony_ci		case SOF2 ... SOF3:
4928c2ecf20Sopenharmony_ci		/* differential coding */
4938c2ecf20Sopenharmony_ci		case SOF5 ... SOF7:
4948c2ecf20Sopenharmony_ci		/* arithmetic coding */
4958c2ecf20Sopenharmony_ci		case SOF9 ... SOF11:
4968c2ecf20Sopenharmony_ci		case SOF13 ... SOF15:
4978c2ecf20Sopenharmony_ci		case DAC:
4988c2ecf20Sopenharmony_ci		case TEM:
4998c2ecf20Sopenharmony_ci			return -EINVAL;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		case DHT:
5028c2ecf20Sopenharmony_ci			ret = jpeg_reference_segment(&stream,
5038c2ecf20Sopenharmony_ci					&out->dht[out->num_dht++ % 4]);
5048c2ecf20Sopenharmony_ci			if (ret < 0)
5058c2ecf20Sopenharmony_ci				return ret;
5068c2ecf20Sopenharmony_ci			ret = jpeg_parse_huffman_tables(&stream,
5078c2ecf20Sopenharmony_ci							out->huffman_tables);
5088c2ecf20Sopenharmony_ci			break;
5098c2ecf20Sopenharmony_ci		case DQT:
5108c2ecf20Sopenharmony_ci			ret = jpeg_reference_segment(&stream,
5118c2ecf20Sopenharmony_ci					&out->dqt[out->num_dqt++ % 4]);
5128c2ecf20Sopenharmony_ci			if (ret < 0)
5138c2ecf20Sopenharmony_ci				return ret;
5148c2ecf20Sopenharmony_ci			ret = jpeg_parse_quantization_tables(&stream,
5158c2ecf20Sopenharmony_ci					out->frame.precision,
5168c2ecf20Sopenharmony_ci					out->quantization_tables);
5178c2ecf20Sopenharmony_ci			break;
5188c2ecf20Sopenharmony_ci		case DRI:
5198c2ecf20Sopenharmony_ci			ret = jpeg_parse_restart_interval(&stream,
5208c2ecf20Sopenharmony_ci							&out->restart_interval);
5218c2ecf20Sopenharmony_ci			break;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci		case SOS:
5248c2ecf20Sopenharmony_ci			ret = jpeg_reference_segment(&stream, &out->sos);
5258c2ecf20Sopenharmony_ci			if (ret < 0)
5268c2ecf20Sopenharmony_ci				return ret;
5278c2ecf20Sopenharmony_ci			ret = jpeg_parse_scan_header(&stream, out->scan);
5288c2ecf20Sopenharmony_ci			/*
5298c2ecf20Sopenharmony_ci			 * stop parsing, the scan header marks the beginning of
5308c2ecf20Sopenharmony_ci			 * the entropy coded segment
5318c2ecf20Sopenharmony_ci			 */
5328c2ecf20Sopenharmony_ci			out->ecs_offset = stream.curr - (u8 *)buf;
5338c2ecf20Sopenharmony_ci			return ret;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci		/* markers without parameters */
5368c2ecf20Sopenharmony_ci		case RST0 ... RST7: /* restart */
5378c2ecf20Sopenharmony_ci		case SOI: /* start of image */
5388c2ecf20Sopenharmony_ci		case EOI: /* end of image */
5398c2ecf20Sopenharmony_ci			break;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci		/* skip unknown or unsupported marker segments */
5428c2ecf20Sopenharmony_ci		default:
5438c2ecf20Sopenharmony_ci			ret = jpeg_skip_segment(&stream);
5448c2ecf20Sopenharmony_ci			break;
5458c2ecf20Sopenharmony_ci		}
5468c2ecf20Sopenharmony_ci		if (ret < 0)
5478c2ecf20Sopenharmony_ci			return ret;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	return marker;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_jpeg_parse_header);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci/**
5558c2ecf20Sopenharmony_ci * v4l2_jpeg_parse_frame_header - parse frame header
5568c2ecf20Sopenharmony_ci * @buf: address of the frame header, after the SOF0 marker
5578c2ecf20Sopenharmony_ci * @len: length of the frame header
5588c2ecf20Sopenharmony_ci * @frame_header: returns the parsed frame header
5598c2ecf20Sopenharmony_ci *
5608c2ecf20Sopenharmony_ci * Returns 0 or negative error if parsing failed.
5618c2ecf20Sopenharmony_ci */
5628c2ecf20Sopenharmony_ciint v4l2_jpeg_parse_frame_header(void *buf, size_t len,
5638c2ecf20Sopenharmony_ci				 struct v4l2_jpeg_frame_header *frame_header)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	struct jpeg_stream stream;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	stream.curr = buf;
5688c2ecf20Sopenharmony_ci	stream.end = stream.curr + len;
5698c2ecf20Sopenharmony_ci	return jpeg_parse_frame_header(&stream, SOF0, frame_header);
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_jpeg_parse_frame_header);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci/**
5748c2ecf20Sopenharmony_ci * v4l2_jpeg_parse_scan_header - parse scan header
5758c2ecf20Sopenharmony_ci * @buf: address of the scan header, after the SOS marker
5768c2ecf20Sopenharmony_ci * @len: length of the scan header
5778c2ecf20Sopenharmony_ci * @scan_header: returns the parsed scan header
5788c2ecf20Sopenharmony_ci *
5798c2ecf20Sopenharmony_ci * Returns 0 or negative error if parsing failed.
5808c2ecf20Sopenharmony_ci */
5818c2ecf20Sopenharmony_ciint v4l2_jpeg_parse_scan_header(void *buf, size_t len,
5828c2ecf20Sopenharmony_ci				struct v4l2_jpeg_scan_header *scan_header)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	struct jpeg_stream stream;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	stream.curr = buf;
5878c2ecf20Sopenharmony_ci	stream.end = stream.curr + len;
5888c2ecf20Sopenharmony_ci	return jpeg_parse_scan_header(&stream, scan_header);
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_jpeg_parse_scan_header);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci/**
5938c2ecf20Sopenharmony_ci * v4l2_jpeg_parse_quantization_tables - parse quantization tables segment
5948c2ecf20Sopenharmony_ci * @buf: address of the quantization table segment, after the DQT marker
5958c2ecf20Sopenharmony_ci * @len: length of the quantization table segment
5968c2ecf20Sopenharmony_ci * @precision: sample precision (P) in bits per component
5978c2ecf20Sopenharmony_ci * @q_tables: returns four references into the buffer for the
5988c2ecf20Sopenharmony_ci *            four possible quantization table destinations
5998c2ecf20Sopenharmony_ci *
6008c2ecf20Sopenharmony_ci * Returns 0 or negative error if parsing failed.
6018c2ecf20Sopenharmony_ci */
6028c2ecf20Sopenharmony_ciint v4l2_jpeg_parse_quantization_tables(void *buf, size_t len, u8 precision,
6038c2ecf20Sopenharmony_ci					struct v4l2_jpeg_reference *q_tables)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	struct jpeg_stream stream;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	stream.curr = buf;
6088c2ecf20Sopenharmony_ci	stream.end = stream.curr + len;
6098c2ecf20Sopenharmony_ci	return jpeg_parse_quantization_tables(&stream, precision, q_tables);
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_jpeg_parse_quantization_tables);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci/**
6148c2ecf20Sopenharmony_ci * v4l2_jpeg_parse_huffman_tables - parse huffman tables segment
6158c2ecf20Sopenharmony_ci * @buf: address of the Huffman table segment, after the DHT marker
6168c2ecf20Sopenharmony_ci * @len: length of the Huffman table segment
6178c2ecf20Sopenharmony_ci * @huffman_tables: returns four references into the buffer for the
6188c2ecf20Sopenharmony_ci *                  four possible Huffman table destinations, in
6198c2ecf20Sopenharmony_ci *                  the order DC0, DC1, AC0, AC1
6208c2ecf20Sopenharmony_ci *
6218c2ecf20Sopenharmony_ci * Returns 0 or negative error if parsing failed.
6228c2ecf20Sopenharmony_ci */
6238c2ecf20Sopenharmony_ciint v4l2_jpeg_parse_huffman_tables(void *buf, size_t len,
6248c2ecf20Sopenharmony_ci				   struct v4l2_jpeg_reference *huffman_tables)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	struct jpeg_stream stream;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	stream.curr = buf;
6298c2ecf20Sopenharmony_ci	stream.end = stream.curr + len;
6308c2ecf20Sopenharmony_ci	return jpeg_parse_huffman_tables(&stream, huffman_tables);
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_jpeg_parse_huffman_tables);
633