| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) STMicroelectronics SA 2013 |
| * Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics. |
| */ |
| |
| #include "delta.h" |
| #include "delta-mjpeg.h" |
| |
| #define MJPEG_SOF_0 0xc0 |
| #define MJPEG_SOF_1 0xc1 |
| #define MJPEG_SOI 0xd8 |
| #define MJPEG_MARKER 0xff |
| |
| static char *header_str(struct mjpeg_header *header, |
| char *str, |
| unsigned int len) |
| { |
| char *cur = str; |
| unsigned int left = len; |
| |
| if (!header) |
| return ""; |
| |
| snprintf(cur, left, "[MJPEG header]\n" |
| "|- length = %d\n" |
| "|- precision = %d\n" |
| "|- width = %d\n" |
| "|- height = %d\n" |
| "|- components = %d\n", |
| header->length, |
| header->sample_precision, |
| header->frame_width, |
| header->frame_height, |
| header->nb_of_components); |
| |
| return str; |
| } |
| |
| static int delta_mjpeg_read_sof(struct delta_ctx *pctx, |
| unsigned char *data, unsigned int size, |
| struct mjpeg_header *header) |
| { |
| struct delta_dev *delta = pctx->dev; |
| unsigned int offset = 0; |
| |
| if (size < 64) |
| goto err_no_more; |
| |
| memset(header, 0, sizeof(*header)); |
| header->length = be16_to_cpu(*(__be16 *)(data + offset)); |
| offset += sizeof(u16); |
| header->sample_precision = *(u8 *)(data + offset); |
| offset += sizeof(u8); |
| header->frame_height = be16_to_cpu(*(__be16 *)(data + offset)); |
| offset += sizeof(u16); |
| header->frame_width = be16_to_cpu(*(__be16 *)(data + offset)); |
| offset += sizeof(u16); |
| header->nb_of_components = *(u8 *)(data + offset); |
| offset += sizeof(u8); |
| |
| if (header->nb_of_components >= MJPEG_MAX_COMPONENTS) { |
| dev_err(delta->dev, |
| "%s unsupported number of components (%d > %d)\n", |
| pctx->name, header->nb_of_components, |
| MJPEG_MAX_COMPONENTS); |
| return -EINVAL; |
| } |
| |
| if ((offset + header->nb_of_components * |
| sizeof(header->components[0])) > size) |
| goto err_no_more; |
| |
| return 0; |
| |
| err_no_more: |
| dev_err(delta->dev, |
| "%s sof: reached end of %d size input stream\n", |
| pctx->name, size); |
| return -ENODATA; |
| } |
| |
| int delta_mjpeg_read_header(struct delta_ctx *pctx, |
| unsigned char *data, unsigned int size, |
| struct mjpeg_header *header, |
| unsigned int *data_offset) |
| { |
| struct delta_dev *delta = pctx->dev; |
| unsigned char str[200]; |
| |
| unsigned int ret = 0; |
| unsigned int offset = 0; |
| unsigned int soi = 0; |
| |
| if (size < 2) |
| goto err_no_more; |
| |
| offset = 0; |
| while (1) { |
| if (data[offset] == MJPEG_MARKER) |
| switch (data[offset + 1]) { |
| case MJPEG_SOI: |
| soi = 1; |
| *data_offset = offset; |
| break; |
| |
| case MJPEG_SOF_0: |
| case MJPEG_SOF_1: |
| if (!soi) { |
| dev_err(delta->dev, |
| "%s wrong sequence, got SOF while SOI not seen\n", |
| pctx->name); |
| return -EINVAL; |
| } |
| |
| ret = delta_mjpeg_read_sof(pctx, |
| &data[offset + 2], |
| size - (offset + 2), |
| header); |
| if (ret) |
| goto err; |
| |
| goto done; |
| |
| default: |
| break; |
| } |
| |
| offset++; |
| if ((offset + 2) >= size) |
| goto err_no_more; |
| } |
| |
| done: |
| dev_dbg(delta->dev, |
| "%s found header @ offset %d:\n%s", pctx->name, |
| *data_offset, |
| header_str(header, str, sizeof(str))); |
| return 0; |
| |
| err_no_more: |
| dev_err(delta->dev, |
| "%s no header found within %d bytes input stream\n", |
| pctx->name, size); |
| return -ENODATA; |
| |
| err: |
| return ret; |
| } |