1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * V4L2 JPEG header parser helpers. |
4 | * |
5 | * Copyright (C) 2019 Pengutronix, Philipp Zabel <kernel@pengutronix.de> |
6 | * |
7 | * For reference, see JPEG ITU-T.81 (ISO/IEC 10918-1) [1] |
8 | * |
9 | * [1] https://www.w3.org/Graphics/JPEG/itu-t81.pdf |
10 | */ |
11 | |
12 | #include <asm/unaligned.h> |
13 | #include <linux/errno.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | #include <linux/types.h> |
17 | #include <media/v4l2-jpeg.h> |
18 | |
19 | MODULE_DESCRIPTION("V4L2 JPEG header parser helpers" ); |
20 | MODULE_AUTHOR("Philipp Zabel <kernel@pengutronix.de>" ); |
21 | MODULE_LICENSE("GPL" ); |
22 | |
23 | /* Table B.1 - Marker code assignments */ |
24 | #define SOF0 0xffc0 /* start of frame */ |
25 | #define SOF1 0xffc1 |
26 | #define SOF2 0xffc2 |
27 | #define SOF3 0xffc3 |
28 | #define SOF5 0xffc5 |
29 | #define SOF7 0xffc7 |
30 | #define JPG 0xffc8 /* extensions */ |
31 | #define SOF9 0xffc9 |
32 | #define SOF11 0xffcb |
33 | #define SOF13 0xffcd |
34 | #define SOF15 0xffcf |
35 | #define DHT 0xffc4 /* huffman table */ |
36 | #define DAC 0xffcc /* arithmetic coding conditioning */ |
37 | #define RST0 0xffd0 /* restart */ |
38 | #define RST7 0xffd7 |
39 | #define SOI 0xffd8 /* start of image */ |
40 | #define EOI 0xffd9 /* end of image */ |
41 | #define SOS 0xffda /* start of stream */ |
42 | #define DQT 0xffdb /* quantization table */ |
43 | #define DNL 0xffdc /* number of lines */ |
44 | #define DRI 0xffdd /* restart interval */ |
45 | #define DHP 0xffde /* hierarchical progression */ |
46 | #define EXP 0xffdf /* expand reference */ |
47 | #define APP0 0xffe0 /* application data */ |
48 | #define APP14 0xffee /* application data for colour encoding */ |
49 | #define APP15 0xffef |
50 | #define JPG0 0xfff0 /* extensions */ |
51 | #define JPG13 0xfffd |
52 | #define COM 0xfffe /* comment */ |
53 | #define TEM 0xff01 /* temporary */ |
54 | |
55 | /** |
56 | * struct jpeg_stream - JPEG byte stream |
57 | * @curr: current position in stream |
58 | * @end: end position, after last byte |
59 | */ |
60 | struct jpeg_stream { |
61 | u8 *curr; |
62 | u8 *end; |
63 | }; |
64 | |
65 | /* returns a value that fits into u8, or negative error */ |
66 | static int jpeg_get_byte(struct jpeg_stream *stream) |
67 | { |
68 | if (stream->curr >= stream->end) |
69 | return -EINVAL; |
70 | |
71 | return *stream->curr++; |
72 | } |
73 | |
74 | /* returns a value that fits into u16, or negative error */ |
75 | static int jpeg_get_word_be(struct jpeg_stream *stream) |
76 | { |
77 | u16 word; |
78 | |
79 | if (stream->curr + sizeof(__be16) > stream->end) |
80 | return -EINVAL; |
81 | |
82 | word = get_unaligned_be16(p: stream->curr); |
83 | stream->curr += sizeof(__be16); |
84 | |
85 | return word; |
86 | } |
87 | |
88 | static int jpeg_skip(struct jpeg_stream *stream, size_t len) |
89 | { |
90 | if (stream->curr + len > stream->end) |
91 | return -EINVAL; |
92 | |
93 | stream->curr += len; |
94 | |
95 | return 0; |
96 | } |
97 | |
98 | static int jpeg_next_marker(struct jpeg_stream *stream) |
99 | { |
100 | int byte; |
101 | u16 marker = 0; |
102 | |
103 | while ((byte = jpeg_get_byte(stream)) >= 0) { |
104 | marker = (marker << 8) | byte; |
105 | /* skip stuffing bytes and REServed markers */ |
106 | if (marker == TEM || (marker > 0xffbf && marker < 0xffff)) |
107 | return marker; |
108 | } |
109 | |
110 | return byte; |
111 | } |
112 | |
113 | /* this does not advance the current position in the stream */ |
114 | static int jpeg_reference_segment(struct jpeg_stream *stream, |
115 | struct v4l2_jpeg_reference *segment) |
116 | { |
117 | u16 len; |
118 | |
119 | if (stream->curr + sizeof(__be16) > stream->end) |
120 | return -EINVAL; |
121 | |
122 | len = get_unaligned_be16(p: stream->curr); |
123 | if (stream->curr + len > stream->end) |
124 | return -EINVAL; |
125 | |
126 | segment->start = stream->curr; |
127 | segment->length = len; |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | static int v4l2_jpeg_decode_subsampling(u8 nf, u8 h_v) |
133 | { |
134 | if (nf == 1) |
135 | return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; |
136 | |
137 | /* no chroma subsampling for 4-component images */ |
138 | if (nf == 4 && h_v != 0x11) |
139 | return -EINVAL; |
140 | |
141 | switch (h_v) { |
142 | case 0x11: |
143 | return V4L2_JPEG_CHROMA_SUBSAMPLING_444; |
144 | case 0x21: |
145 | return V4L2_JPEG_CHROMA_SUBSAMPLING_422; |
146 | case 0x22: |
147 | return V4L2_JPEG_CHROMA_SUBSAMPLING_420; |
148 | case 0x41: |
149 | return V4L2_JPEG_CHROMA_SUBSAMPLING_411; |
150 | default: |
151 | return -EINVAL; |
152 | } |
153 | } |
154 | |
155 | static int (struct jpeg_stream *stream, u16 sof_marker, |
156 | struct v4l2_jpeg_frame_header *) |
157 | { |
158 | int len = jpeg_get_word_be(stream); |
159 | |
160 | if (len < 0) |
161 | return len; |
162 | /* Lf = 8 + 3 * Nf, Nf >= 1 */ |
163 | if (len < 8 + 3) |
164 | return -EINVAL; |
165 | |
166 | if (frame_header) { |
167 | /* Table B.2 - Frame header parameter sizes and values */ |
168 | int p, y, x, nf; |
169 | int i; |
170 | |
171 | p = jpeg_get_byte(stream); |
172 | if (p < 0) |
173 | return p; |
174 | /* |
175 | * Baseline DCT only supports 8-bit precision. |
176 | * Extended sequential DCT also supports 12-bit precision. |
177 | */ |
178 | if (p != 8 && (p != 12 || sof_marker != SOF1)) |
179 | return -EINVAL; |
180 | |
181 | y = jpeg_get_word_be(stream); |
182 | if (y < 0) |
183 | return y; |
184 | if (y == 0) |
185 | return -EINVAL; |
186 | |
187 | x = jpeg_get_word_be(stream); |
188 | if (x < 0) |
189 | return x; |
190 | if (x == 0) |
191 | return -EINVAL; |
192 | |
193 | nf = jpeg_get_byte(stream); |
194 | if (nf < 0) |
195 | return nf; |
196 | /* |
197 | * The spec allows 1 <= Nf <= 255, but we only support up to 4 |
198 | * components. |
199 | */ |
200 | if (nf < 1 || nf > V4L2_JPEG_MAX_COMPONENTS) |
201 | return -EINVAL; |
202 | if (len != 8 + 3 * nf) |
203 | return -EINVAL; |
204 | |
205 | frame_header->precision = p; |
206 | frame_header->height = y; |
207 | frame_header->width = x; |
208 | frame_header->num_components = nf; |
209 | |
210 | for (i = 0; i < nf; i++) { |
211 | struct v4l2_jpeg_frame_component_spec *component; |
212 | int c, h_v, tq; |
213 | |
214 | c = jpeg_get_byte(stream); |
215 | if (c < 0) |
216 | return c; |
217 | |
218 | h_v = jpeg_get_byte(stream); |
219 | if (h_v < 0) |
220 | return h_v; |
221 | if (i == 0) { |
222 | int subs; |
223 | |
224 | subs = v4l2_jpeg_decode_subsampling(nf, h_v); |
225 | if (subs < 0) |
226 | return subs; |
227 | frame_header->subsampling = subs; |
228 | } else if (h_v != 0x11) { |
229 | /* all chroma sampling factors must be 1 */ |
230 | return -EINVAL; |
231 | } |
232 | |
233 | tq = jpeg_get_byte(stream); |
234 | if (tq < 0) |
235 | return tq; |
236 | |
237 | component = &frame_header->component[i]; |
238 | component->component_identifier = c; |
239 | component->horizontal_sampling_factor = |
240 | (h_v >> 4) & 0xf; |
241 | component->vertical_sampling_factor = h_v & 0xf; |
242 | component->quantization_table_selector = tq; |
243 | } |
244 | } else { |
245 | return jpeg_skip(stream, len: len - 2); |
246 | } |
247 | |
248 | return 0; |
249 | } |
250 | |
251 | static int (struct jpeg_stream *stream, |
252 | struct v4l2_jpeg_scan_header *) |
253 | { |
254 | size_t skip; |
255 | int len = jpeg_get_word_be(stream); |
256 | |
257 | if (len < 0) |
258 | return len; |
259 | /* Ls = 8 + 3 * Ns, Ns >= 1 */ |
260 | if (len < 6 + 2) |
261 | return -EINVAL; |
262 | |
263 | if (scan_header) { |
264 | int ns; |
265 | int i; |
266 | |
267 | ns = jpeg_get_byte(stream); |
268 | if (ns < 0) |
269 | return ns; |
270 | if (ns < 1 || ns > 4 || len != 6 + 2 * ns) |
271 | return -EINVAL; |
272 | |
273 | scan_header->num_components = ns; |
274 | |
275 | for (i = 0; i < ns; i++) { |
276 | struct v4l2_jpeg_scan_component_spec *component; |
277 | int cs, td_ta; |
278 | |
279 | cs = jpeg_get_byte(stream); |
280 | if (cs < 0) |
281 | return cs; |
282 | |
283 | td_ta = jpeg_get_byte(stream); |
284 | if (td_ta < 0) |
285 | return td_ta; |
286 | |
287 | component = &scan_header->component[i]; |
288 | component->component_selector = cs; |
289 | component->dc_entropy_coding_table_selector = |
290 | (td_ta >> 4) & 0xf; |
291 | component->ac_entropy_coding_table_selector = |
292 | td_ta & 0xf; |
293 | } |
294 | |
295 | skip = 3; /* skip Ss, Se, Ah, and Al */ |
296 | } else { |
297 | skip = len - 2; |
298 | } |
299 | |
300 | return jpeg_skip(stream, len: skip); |
301 | } |
302 | |
303 | /* B.2.4.1 Quantization table-specification syntax */ |
304 | static int jpeg_parse_quantization_tables(struct jpeg_stream *stream, |
305 | u8 precision, |
306 | struct v4l2_jpeg_reference *tables) |
307 | { |
308 | int len = jpeg_get_word_be(stream); |
309 | |
310 | if (len < 0) |
311 | return len; |
312 | /* Lq = 2 + n * 65 (for baseline DCT), n >= 1 */ |
313 | if (len < 2 + 65) |
314 | return -EINVAL; |
315 | |
316 | len -= 2; |
317 | while (len >= 65) { |
318 | u8 pq, tq, *qk; |
319 | int ret; |
320 | int pq_tq = jpeg_get_byte(stream); |
321 | |
322 | if (pq_tq < 0) |
323 | return pq_tq; |
324 | |
325 | /* quantization table element precision */ |
326 | pq = (pq_tq >> 4) & 0xf; |
327 | /* |
328 | * Only 8-bit Qk values for 8-bit sample precision. Extended |
329 | * sequential DCT with 12-bit sample precision also supports |
330 | * 16-bit Qk values. |
331 | */ |
332 | if (pq != 0 && (pq != 1 || precision != 12)) |
333 | return -EINVAL; |
334 | |
335 | /* quantization table destination identifier */ |
336 | tq = pq_tq & 0xf; |
337 | if (tq > 3) |
338 | return -EINVAL; |
339 | |
340 | /* quantization table element */ |
341 | qk = stream->curr; |
342 | ret = jpeg_skip(stream, len: pq ? 128 : 64); |
343 | if (ret < 0) |
344 | return -EINVAL; |
345 | |
346 | if (tables) { |
347 | tables[tq].start = qk; |
348 | tables[tq].length = pq ? 128 : 64; |
349 | } |
350 | |
351 | len -= pq ? 129 : 65; |
352 | } |
353 | |
354 | return 0; |
355 | } |
356 | |
357 | /* B.2.4.2 Huffman table-specification syntax */ |
358 | static int jpeg_parse_huffman_tables(struct jpeg_stream *stream, |
359 | struct v4l2_jpeg_reference *tables) |
360 | { |
361 | int mt; |
362 | int len = jpeg_get_word_be(stream); |
363 | |
364 | if (len < 0) |
365 | return len; |
366 | /* Table B.5 - Huffman table specification parameter sizes and values */ |
367 | if (len < 2 + 17) |
368 | return -EINVAL; |
369 | |
370 | for (len -= 2; len >= 17; len -= 17 + mt) { |
371 | u8 tc, th, *table; |
372 | int tc_th = jpeg_get_byte(stream); |
373 | int i, ret; |
374 | |
375 | if (tc_th < 0) |
376 | return tc_th; |
377 | |
378 | /* table class - 0 = DC, 1 = AC */ |
379 | tc = (tc_th >> 4) & 0xf; |
380 | if (tc > 1) |
381 | return -EINVAL; |
382 | |
383 | /* huffman table destination identifier */ |
384 | th = tc_th & 0xf; |
385 | /* only two Huffman tables for baseline DCT */ |
386 | if (th > 1) |
387 | return -EINVAL; |
388 | |
389 | /* BITS - number of Huffman codes with length i */ |
390 | table = stream->curr; |
391 | mt = 0; |
392 | for (i = 0; i < 16; i++) { |
393 | int li; |
394 | |
395 | li = jpeg_get_byte(stream); |
396 | if (li < 0) |
397 | return li; |
398 | |
399 | mt += li; |
400 | } |
401 | /* HUFFVAL - values associated with each Huffman code */ |
402 | ret = jpeg_skip(stream, len: mt); |
403 | if (ret < 0) |
404 | return ret; |
405 | |
406 | if (tables) { |
407 | tables[(tc << 1) | th].start = table; |
408 | tables[(tc << 1) | th].length = stream->curr - table; |
409 | } |
410 | } |
411 | |
412 | return jpeg_skip(stream, len: len - 2); |
413 | } |
414 | |
415 | /* B.2.4.4 Restart interval definition syntax */ |
416 | static int jpeg_parse_restart_interval(struct jpeg_stream *stream, |
417 | u16 *restart_interval) |
418 | { |
419 | int len = jpeg_get_word_be(stream); |
420 | int ri; |
421 | |
422 | if (len < 0) |
423 | return len; |
424 | if (len != 4) |
425 | return -EINVAL; |
426 | |
427 | ri = jpeg_get_word_be(stream); |
428 | if (ri < 0) |
429 | return ri; |
430 | |
431 | *restart_interval = ri; |
432 | |
433 | return 0; |
434 | } |
435 | |
436 | static int jpeg_skip_segment(struct jpeg_stream *stream) |
437 | { |
438 | int len = jpeg_get_word_be(stream); |
439 | |
440 | if (len < 0) |
441 | return len; |
442 | if (len < 2) |
443 | return -EINVAL; |
444 | |
445 | return jpeg_skip(stream, len: len - 2); |
446 | } |
447 | |
448 | /* Rec. ITU-T T.872 (06/2012) 6.5.3 */ |
449 | static int jpeg_parse_app14_data(struct jpeg_stream *stream, |
450 | enum v4l2_jpeg_app14_tf *tf) |
451 | { |
452 | int ret; |
453 | int lp; |
454 | int skip; |
455 | |
456 | lp = jpeg_get_word_be(stream); |
457 | if (lp < 0) |
458 | return lp; |
459 | |
460 | /* Check for "Adobe\0" in Ap1..6 */ |
461 | if (stream->curr + 6 > stream->end || |
462 | strncmp(stream->curr, "Adobe\0" , 6)) |
463 | return jpeg_skip(stream, len: lp - 2); |
464 | |
465 | /* get to Ap12 */ |
466 | ret = jpeg_skip(stream, len: 11); |
467 | if (ret < 0) |
468 | return ret; |
469 | |
470 | ret = jpeg_get_byte(stream); |
471 | if (ret < 0) |
472 | return ret; |
473 | |
474 | *tf = ret; |
475 | |
476 | /* skip the rest of the segment, this ensures at least it is complete */ |
477 | skip = lp - 2 - 11 - 1; |
478 | return jpeg_skip(stream, len: skip); |
479 | } |
480 | |
481 | /** |
482 | * v4l2_jpeg_parse_header - locate marker segments and optionally parse headers |
483 | * @buf: address of the JPEG buffer, should start with a SOI marker |
484 | * @len: length of the JPEG buffer |
485 | * @out: returns marker segment positions and optionally parsed headers |
486 | * |
487 | * The out->scan_header pointer must be initialized to NULL or point to a valid |
488 | * v4l2_jpeg_scan_header structure. The out->huffman_tables and |
489 | * out->quantization_tables pointers must be initialized to NULL or point to a |
490 | * valid array of 4 v4l2_jpeg_reference structures each. |
491 | * |
492 | * Returns 0 or negative error if parsing failed. |
493 | */ |
494 | int (void *buf, size_t len, struct v4l2_jpeg_header *out) |
495 | { |
496 | struct jpeg_stream stream; |
497 | int marker; |
498 | int ret = 0; |
499 | |
500 | stream.curr = buf; |
501 | stream.end = stream.curr + len; |
502 | |
503 | out->num_dht = 0; |
504 | out->num_dqt = 0; |
505 | |
506 | /* the first bytes must be SOI, B.2.1 High-level syntax */ |
507 | if (jpeg_get_word_be(stream: &stream) != SOI) |
508 | return -EINVAL; |
509 | |
510 | /* init value to signal if this marker is not present */ |
511 | out->app14_tf = V4L2_JPEG_APP14_TF_UNKNOWN; |
512 | |
513 | /* loop through marker segments */ |
514 | while ((marker = jpeg_next_marker(stream: &stream)) >= 0) { |
515 | switch (marker) { |
516 | /* baseline DCT, extended sequential DCT */ |
517 | case SOF0 ... SOF1: |
518 | ret = jpeg_reference_segment(stream: &stream, segment: &out->sof); |
519 | if (ret < 0) |
520 | return ret; |
521 | ret = jpeg_parse_frame_header(stream: &stream, sof_marker: marker, |
522 | frame_header: &out->frame); |
523 | break; |
524 | /* progressive, lossless */ |
525 | case SOF2 ... SOF3: |
526 | /* differential coding */ |
527 | case SOF5 ... SOF7: |
528 | /* arithmetic coding */ |
529 | case SOF9 ... SOF11: |
530 | case SOF13 ... SOF15: |
531 | case DAC: |
532 | case TEM: |
533 | return -EINVAL; |
534 | |
535 | case DHT: |
536 | ret = jpeg_reference_segment(stream: &stream, |
537 | segment: &out->dht[out->num_dht++ % 4]); |
538 | if (ret < 0) |
539 | return ret; |
540 | if (!out->huffman_tables) { |
541 | ret = jpeg_skip_segment(stream: &stream); |
542 | break; |
543 | } |
544 | ret = jpeg_parse_huffman_tables(stream: &stream, |
545 | tables: out->huffman_tables); |
546 | break; |
547 | case DQT: |
548 | ret = jpeg_reference_segment(stream: &stream, |
549 | segment: &out->dqt[out->num_dqt++ % 4]); |
550 | if (ret < 0) |
551 | return ret; |
552 | if (!out->quantization_tables) { |
553 | ret = jpeg_skip_segment(stream: &stream); |
554 | break; |
555 | } |
556 | ret = jpeg_parse_quantization_tables(stream: &stream, |
557 | precision: out->frame.precision, |
558 | tables: out->quantization_tables); |
559 | break; |
560 | case DRI: |
561 | ret = jpeg_parse_restart_interval(stream: &stream, |
562 | restart_interval: &out->restart_interval); |
563 | break; |
564 | case APP14: |
565 | ret = jpeg_parse_app14_data(stream: &stream, |
566 | tf: &out->app14_tf); |
567 | break; |
568 | case SOS: |
569 | ret = jpeg_reference_segment(stream: &stream, segment: &out->sos); |
570 | if (ret < 0) |
571 | return ret; |
572 | ret = jpeg_parse_scan_header(stream: &stream, scan_header: out->scan); |
573 | /* |
574 | * stop parsing, the scan header marks the beginning of |
575 | * the entropy coded segment |
576 | */ |
577 | out->ecs_offset = stream.curr - (u8 *)buf; |
578 | return ret; |
579 | |
580 | /* markers without parameters */ |
581 | case RST0 ... RST7: /* restart */ |
582 | case SOI: /* start of image */ |
583 | case EOI: /* end of image */ |
584 | break; |
585 | |
586 | /* skip unknown or unsupported marker segments */ |
587 | default: |
588 | ret = jpeg_skip_segment(stream: &stream); |
589 | break; |
590 | } |
591 | if (ret < 0) |
592 | return ret; |
593 | } |
594 | |
595 | return marker; |
596 | } |
597 | EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_header); |
598 | |
599 | /** |
600 | * v4l2_jpeg_parse_frame_header - parse frame header |
601 | * @buf: address of the frame header, after the SOF0 marker |
602 | * @len: length of the frame header |
603 | * @frame_header: returns the parsed frame header |
604 | * |
605 | * Returns 0 or negative error if parsing failed. |
606 | */ |
607 | int (void *buf, size_t len, |
608 | struct v4l2_jpeg_frame_header *) |
609 | { |
610 | struct jpeg_stream stream; |
611 | |
612 | stream.curr = buf; |
613 | stream.end = stream.curr + len; |
614 | return jpeg_parse_frame_header(stream: &stream, SOF0, frame_header); |
615 | } |
616 | EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_frame_header); |
617 | |
618 | /** |
619 | * v4l2_jpeg_parse_scan_header - parse scan header |
620 | * @buf: address of the scan header, after the SOS marker |
621 | * @len: length of the scan header |
622 | * @scan_header: returns the parsed scan header |
623 | * |
624 | * Returns 0 or negative error if parsing failed. |
625 | */ |
626 | int (void *buf, size_t len, |
627 | struct v4l2_jpeg_scan_header *) |
628 | { |
629 | struct jpeg_stream stream; |
630 | |
631 | stream.curr = buf; |
632 | stream.end = stream.curr + len; |
633 | return jpeg_parse_scan_header(stream: &stream, scan_header); |
634 | } |
635 | EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_scan_header); |
636 | |
637 | /** |
638 | * v4l2_jpeg_parse_quantization_tables - parse quantization tables segment |
639 | * @buf: address of the quantization table segment, after the DQT marker |
640 | * @len: length of the quantization table segment |
641 | * @precision: sample precision (P) in bits per component |
642 | * @q_tables: returns four references into the buffer for the |
643 | * four possible quantization table destinations |
644 | * |
645 | * Returns 0 or negative error if parsing failed. |
646 | */ |
647 | int v4l2_jpeg_parse_quantization_tables(void *buf, size_t len, u8 precision, |
648 | struct v4l2_jpeg_reference *q_tables) |
649 | { |
650 | struct jpeg_stream stream; |
651 | |
652 | stream.curr = buf; |
653 | stream.end = stream.curr + len; |
654 | return jpeg_parse_quantization_tables(stream: &stream, precision, tables: q_tables); |
655 | } |
656 | EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_quantization_tables); |
657 | |
658 | /** |
659 | * v4l2_jpeg_parse_huffman_tables - parse huffman tables segment |
660 | * @buf: address of the Huffman table segment, after the DHT marker |
661 | * @len: length of the Huffman table segment |
662 | * @huffman_tables: returns four references into the buffer for the |
663 | * four possible Huffman table destinations, in |
664 | * the order DC0, DC1, AC0, AC1 |
665 | * |
666 | * Returns 0 or negative error if parsing failed. |
667 | */ |
668 | int v4l2_jpeg_parse_huffman_tables(void *buf, size_t len, |
669 | struct v4l2_jpeg_reference *huffman_tables) |
670 | { |
671 | struct jpeg_stream stream; |
672 | |
673 | stream.curr = buf; |
674 | stream.end = stream.curr + len; |
675 | return jpeg_parse_huffman_tables(stream: &stream, tables: huffman_tables); |
676 | } |
677 | EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_huffman_tables); |
678 | |